>18]+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[l>>
9 | 12&63]+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[l>>6&63]+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[l&63]);d-=f;return g.slice(0,g.length-d)+"==".slice(0,d)};
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | var gulp = require('gulp'),
2 | zip = require('gulp-zip'),
3 | watch = require('gulp-watch'),
4 | clean = require('gulp-clean'),
5 | jshint = require('gulp-jshint'),
6 | concat = require('gulp-concat'),
7 | uglify = require('gulp-uglify'),
8 | cssmin = require('gulp-cssmin'),
9 | rename = require('gulp-rename'),
10 | htmlmin = require('gulp-htmlmin'),
11 | replace = require('gulp-replace-path');
12 |
13 | var APP_NAME = 'save_the_forest';
14 | var bases = {
15 | app: 'src/',
16 | dist: 'dist/',
17 | };
18 |
19 | var paths = {
20 | scripts: [
21 | 'src/utils.js',
22 | 'src/jsfxr.js',
23 | 'src/sound.js',
24 | 'src/flame.js',
25 | 'src/menu.js',
26 | 'src/weather.js',
27 | 'src/particles.js',
28 | 'src/background.js',
29 | 'src/player.js',
30 | 'src/tree.js',
31 | 'src/game.js',
32 | 'src/main.js'
33 | ],
34 | html: [ 'src/index.html' ],
35 | css: [ 'src/css/*.css' ],
36 | images: [ 'src/images/*.*' ]
37 | };
38 |
39 | // Delete the dist directory
40 | gulp.task('clean', function() {
41 | return gulp.src(bases.dist, {read: false})
42 | .pipe(clean());
43 | });
44 |
45 | // Process scripts and concatenate them into one output file
46 | gulp.task('scripts', ['clean'], function() {
47 | return gulp.src(paths.scripts)
48 | // .pipe(jshint())
49 | // .pipe(jshint.reporter('default'))
50 | .pipe(concat('game.js'))
51 | .pipe(gulp.dest(bases.dist)) // for dev mode
52 | .pipe(uglify())
53 | .pipe(rename({suffix: '.min'}))
54 | .pipe(gulp.dest(bases.dist)) // for prod
55 | });
56 |
57 | // Copy all other files to dist directly
58 | gulp.task('copy', ['clean'], function() {
59 | // Copy images, maintaining the original directory structure
60 | return gulp.src(paths.images, {cwd: bases.app})
61 | .pipe(gulp.dest(bases.dist));
62 | });
63 |
64 | gulp.task('uglify-html', ['clean'], function() {
65 | return gulp.src('src/*.html')
66 | .pipe(htmlmin({collapseWhitespace: true}))
67 | .pipe(replace(/..\/dist\//g, ''))
68 | .pipe(replace(/game.css/g, 'game.min.css'))
69 | .pipe(replace(/game.js/g, 'game.min.js'))
70 | .pipe(gulp.dest(bases.dist));
71 | });
72 |
73 | gulp.task('uglify-css', ['clean'], function () {
74 | return gulp.src('css/*.css', {cwd: bases.app})
75 | .pipe(gulp.dest(bases.dist)) // for dev mode
76 | .pipe(cssmin())
77 | .pipe(rename({suffix: '.min'}))
78 | .pipe(gulp.dest(bases.dist)); // for prod
79 | });
80 |
81 | // Delete the dist directory
82 | gulp.task('zip', ['default'], function() {
83 | return gulp.src([ 'dist/**/*.min.*', 'dist/index.html' ])
84 | .pipe(zip(APP_NAME + '.zip'))
85 | .pipe(gulp.dest('.'));
86 | });
87 |
88 | gulp.task('watch' , function () {
89 | return gulp.watch([
90 | paths.scripts,
91 | paths.html,
92 | paths.images,
93 | paths.css
94 | ], ['default']);
95 | });
96 |
97 | // Define the default task as a sequence of the above tasks
98 | gulp.task('default', [
99 | 'clean',
100 | 'copy',
101 | 'scripts',
102 | 'uglify-html',
103 | 'uglify-css'
104 | ]);
105 |
--------------------------------------------------------------------------------
/src/tree.js:
--------------------------------------------------------------------------------
1 | var T, CC;
2 | var blw = 200, bw = 0;
3 |
4 | function Tree(config) {
5 | T = this;
6 | config = config || {};
7 | // T.lw = T.w = 0;
8 | T.minW = 10;
9 | T.maxW = 80;
10 | T.minH = P.fireOffset;
11 | T.maxH = G.isMobile() ? 300 : 400;
12 | T.minDist = 50;
13 | T.maxDist = G.isMobile() ? 100 : 200;
14 | // T.branchThickness = 3;
15 |
16 | CC.w = utils.pI(G.can.width);
17 | CC.h = utils.pI(G.can.height);
18 |
19 | T.color = '#a77b44';
20 | this.add();
21 | if (!config.isNoFlame) {
22 | if (G.isMobile()) {
23 | this.flame = true;
24 | } else {
25 | this.flame = smoky;
26 | this.flame.addEntity(Flame);
27 | }
28 | }
29 | return T;
30 | }
31 |
32 | Tree.prototype = {
33 | /*drawFractalTree: function (x, y, width, height) {
34 | T.drawTree(x, y, width, height, -90, T.branchThickness);
35 | },
36 | drawTree: function (x1, y1, width, height, angle, depth){
37 | T.brLength = T.brLength || T.random(T.minW, T.maxW);
38 | T.angle = T.angle || T.random(15, 20);
39 | T.bb = (T.cos(angle) * depth * T.brLength);
40 | T.vv = (T.sin(angle) * depth * T.brLength);
41 | if (depth != 0){
42 | var x2 = x1 + T.bb;
43 | var y2 = y1 - T.vv;
44 |
45 | T.drawLine(x1, y1, x2, y2, depth);
46 |
47 | T.drawTree(x2, y2, width, height, angle - T.angle, depth - 1);
48 | T.drawTree(x2, y2, width, height, angle + T.angle, depth - 1);
49 | // T.drawLine(x1, y1, x2, y2, depth);
50 | }
51 | },
52 | random: function (min, max){
53 | return min + Math.floor(Math.random()*(max+1-min));
54 | },
55 | drawLine: function (x1, y1, x2, y2, thickness){
56 | ctx.fillStyle = '#000';
57 | if(thickness > 2)
58 | ctx.strokeStyle = 'rgb(139,126, 102)'; //Brown
59 | else
60 | ctx.strokeStyle = 'rgb(34,139,34)'; //Green
61 | ctx.lineWidth = thickness * 1.5;
62 | bp();
63 | mt(x1, y1);
64 | lt(x2, y2);
65 | cp();
66 | st();
67 |
68 | },
69 | cos: function (angle) {
70 | return M.cos(T.deg_to_rad(angle));
71 | },
72 | sin: function (angle) {
73 | return M.sin(T.deg_to_rad(angle));
74 | },
75 | deg_to_rad: function (angle){
76 | return angle*(M.PI/180.0);
77 | },*/
78 | getWidth: function (val) {
79 | if (val !== undefined) {
80 | return val;
81 | }
82 | return utils.getRandomInt(T.minW, T.maxW);
83 | },
84 | getHeight: function (val) {
85 | if (val !== undefined) {
86 | return val;
87 | }
88 | return utils.getRandomInt(T.minH, T.maxH);
89 | },
90 | add: function (val) {
91 | T.preCompute();
92 | T.x = blw + bw;
93 | T.y = CC.h - T.h - (P.fireOffset * 0.6),
94 | T.width = bw,
95 | T.height = T.h;
96 | // T.drawFractalTree(T.x, T.y, T.width, T.height)
97 |
98 | //T.update(T);
99 | return T;
100 | },
101 | update: function (treeInstance) {
102 | var x = treeInstance.x,
103 | y = treeInstance.y,
104 | width = treeInstance.width,
105 | height = treeInstance.height;
106 |
107 | sv();
108 | fs(T.color);
109 |
110 | bp();
111 | mt(x, y);
112 | // left side
113 | bct(x , y + height, x - 25, y + height, x - 25, y + height);
114 |
115 | // left bottom curve
116 | bct(x, y + height, x + (width / 2), y + height / 1.2, x + (width / 2), y + (height / 1.2))
117 | // right bottom curve
118 | bct(x + (width / 2), y + (height / 1.2), x + (width / 2), y + height / 1.2, x + width + 25, y + height);
119 |
120 | // right side
121 | bct(x + width, y + height, x + width, y, x + width, y);
122 |
123 | ctx.shadowColor = '#6b4e2a';
124 | ctx.shadowOffsetX = -3;
125 | ctx.shadowOffsetY = 3;
126 | ctx.shadowBlur = 10;
127 | ctx.strokeStyle = '#6b4e2a';
128 | ctx.lineWidth = 1;
129 | st();
130 | cp();
131 | fl();
132 | rs();
133 |
134 | fs('#444')
135 | el(ctx, x, y - 4, width, 10, '#6b4e2a');
136 |
137 | if (treeInstance.flame) {
138 | if (G.isMobile()) {
139 | T.addCircle(x, y, width);
140 | } else {
141 | treeInstance.flame.update(x, y, width);
142 | }
143 | }
144 | },
145 | addCircle: function (x, y, width) {
146 | bp();
147 | ar(x + (width/2), y, width/2, 0, Math.PI*2, false);
148 | fs('rgba(255, 0, 0, 0.4)');
149 | fl();
150 |
151 | bp();
152 | ar(x + (width/2), y, width/3, 0, Math.PI*2, false);
153 | fs('rgba(255, 165, 0, 0.4)');
154 | fl();
155 |
156 | bp();
157 | ar(x + (width/2), y, width/6, 0, Math.PI*2, false);
158 | fs('rgba(255, 255, 0, ' + utils.getRandomInt(0.3, 0.5)/10 + ')');
159 | fl();
160 | },
161 | preCompute: function () {
162 | T.lw = blw + bw + (bw === 0 ? 0 : utils.getRandomInt(T.minDist, T.maxDist));
163 | blw = T.lw;
164 | T.w = utils.getRandomInt(T.minW, T.maxW);
165 | bw = T.w;
166 | T.h = utils.getRandomInt(T.minH, T.maxH);
167 | // console.log(blw, bw)
168 | // T.rw = CC.w - T.lw - T.w;
169 | },
170 | removeFlame: function (that) {
171 | that.flame = undefined;
172 | }
173 | };
174 |
175 |
--------------------------------------------------------------------------------
/src/flame.js:
--------------------------------------------------------------------------------
1 | var SF, Flame, H, PI_2, Smoke, Trail, W, SmokyFlame, drawCircle, rand, w, slice = [].slice;
2 |
3 | PI_2 = 2 * Math.PI;
4 | rand = function (a, b) {
5 | return (b - a) * Math.random() + a;
6 | };
7 | drawCircle = function (x, y, r, style) {
8 | bp();
9 | ar(x, y, r, 0, PI_2, false);
10 | ctx.fillStyle = style;
11 | return fl();
12 | };
13 |
14 | Smoke = function () {
15 | function Smoke(x, y) {
16 | this.opacity = 0.8;
17 | this.x = x;
18 | this.y = y;
19 | this.r = 2.0;
20 | }
21 | Smoke.prototype.step = function (x, y, w) {
22 | y -= utils.getRandomInt(rand(60,70), rand(200,350))
23 | // y -= rand(0, 3);
24 | x += rand(-2, 2);
25 | this.opacity -= 0.04;
26 | if (this.opacity <= 0) {
27 | return this.destroyed = true;
28 | }
29 | };
30 | Smoke.prototype.draw = function (x, y, w) {
31 | y -= utils.getRandomInt(60, 150)
32 | x += utils.getRandomInt(rand(-w+w/2, 0), rand(0,w-w/2))
33 | if (this.opacity <= 0) {
34 | return;
35 | }
36 | return drawCircle(x, y, this.r, 'rgba(60,60,60,' + this.opacity + ')');
37 | };
38 | return Smoke;
39 | }();
40 | Trail = function () {
41 | function Trail(x, y) {
42 | this.opacity = 1;
43 | this.x = x;
44 | this.y = y;
45 | this.r = 12;
46 | }
47 | Trail.prototype.step = function (x, y, w) {
48 | this.r = w / 5;
49 | y -= rand(0, 8);
50 | x -= rand(-3, 3);
51 | this.opacity -= 0.03;
52 | if (this.opacity <= 0) {
53 | this.destroyed = true;
54 | if (rand(0, 1) < 0.5) {
55 | return SF.addEntity(Smoke, x, y - this.r);
56 | }
57 | }
58 | };
59 | Trail.prototype.draw = function (x, y, w) {
60 | this.r = w / 6;
61 | y -= rand(rand(-45, 5), rand(25, 75));
62 | x -= rand(-w/2 - 20, w/2 + 20);
63 | var color, color2, g, rg;
64 | if (this.opacity <= 0) {
65 | return;
66 | }
67 | color = 'rgba(255,' + ~~(240 * this.opacity) + ',0,' + this.opacity + ')';
68 | color2 = 'rgba(255,' + ~~(240 * this.opacity) + ',0,0)';
69 | rg = this.r * 1.5 + rand(0, 2);
70 | g = ctx.createRadialGradient(x, y, 0, x, y, rg);
71 | g.addColorStop(0, color);
72 | g.addColorStop(1, color2);
73 | drawCircle(x, y, this.r, g);
74 | return drawCircle(x, y, this.r * this.opacity, color);
75 | };
76 | return Trail;
77 | }();
78 | Flame = function () {
79 | function Flame() {
80 | this.x = G.can.width / 2;
81 | this.y = G.can.height / 2 + 90;
82 | this.r = 24;
83 | this.rg = 22;
84 | }
85 | Flame.prototype.step = function (x, y, w) {
86 | return false;
87 | };
88 | Flame.prototype.draw = function (x, y, w) {
89 | this.g = ctx.createRadialGradient(x, y, 0, x, y, w * 1.2);
90 | this.g.addColorStop(0, 'rgba(255,255,255,1)');
91 | this.g.addColorStop(1, 'rgba(255,120,0,0)');
92 |
93 |
94 | var g, i, j;
95 | //for (i = j = 1; j <= 1; i = ++j) {
96 | SF.addEntity(Trail, x, y - this.r / 3);
97 | //}
98 | g = ctx.createRadialGradient(x, y, 0, x, y, this.rg);
99 | g.addColorStop(0, 'rgba(255,180,0,' + rand(0.2, 0.9) + ')');
100 | g.addColorStop(1, 'rgba(255,180,0,0)');
101 | drawCircle(x, y, this.rg, g);
102 | return drawCircle(x + rand(-1.5, 1.5), y + rand(-1.5, 1.5), w, this.g );
103 | };
104 | return Flame;
105 | }();
106 |
107 | SmokyFlame = function () {
108 | function SmokyFlame() {
109 | SF = this;
110 | this.entities = {};
111 | this.i = 0;
112 | this.ii = 0;
113 | // this.update();
114 | }
115 | SmokyFlame.prototype.addEntity = function () {
116 | var args, klass;
117 | klass = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
118 | this.entities[this.i] = function (func, args, ctor) {
119 | ctor.prototype = func.prototype;
120 | var child = new ctor(), result = func.apply(child, args);
121 | return Object(result) === result ? result : child;
122 | }(klass, args, function () {
123 | });
124 | return this.i += 1;
125 | };
126 | SmokyFlame.prototype.update = function (x, y, w) {
127 | var entity, k, ref;
128 | // ctx.clearRect(0, 0, W, H);
129 | ref = this.entities;
130 | for (k in ref) {
131 | entity = ref[k];
132 | if (entity.destroyed === true) {
133 | delete this.entities[k];
134 | continue;
135 | }
136 |
137 | if (this.ii % 5 === 0) {
138 | entity.step(x + (w / 2), y - 10, w);
139 | entity.draw(x + (w / 2), y - 10, w);
140 | this.ii = 0;
141 | }
142 | this.ii++;
143 | }
144 | };
145 | return SmokyFlame;
146 | }();
--------------------------------------------------------------------------------
/src/player.js:
--------------------------------------------------------------------------------
1 | var Pl;
2 | function Player() {
3 | Pl = this;
4 | Pl.liesOn = 0;
5 | Pl.maxH = 66;
6 | Pl.bounceHeight = 5;
7 | Pl.w = 20;
8 | Pl.h = Pl.maxH;
9 | Pl.x = G.trees[0].x;
10 | Pl.y = G.trees[0].y - Pl.h;
11 | Pl.vel = 0;
12 | Pl.isJet = false;
13 | Pl.isRest = true;
14 | Pl.t = new Date();
15 | Pl.update();
16 | return Pl;
17 | }
18 |
19 | Player.prototype = {
20 | tears: function (x, y) {
21 | if (Pl.died) return;
22 | bp();
23 | ctx.fillStyle = '#36b1f7';
24 | mt(Pl.x + Pl.w + x - 3, Pl.y + y);
25 | lt(Pl.x + Pl.w + x, Pl.y + y - 4);
26 | lt(Pl.x + Pl.w + x + 3, Pl.y + y);
27 | ar(Pl.x + Pl.w + x, Pl.y + y, 3, 0, Math.PI);
28 | cp();
29 | fl();
30 | },
31 | body: function () {
32 | sv();
33 | ctx.shadowColor = '#000';
34 | ctx.shadowOffsetX = -2;
35 | ctx.shadowOffsetY = 2;
36 | ctx.shadowBlur = 10;
37 | if (Pl.died) {
38 | fr(Pl.x - 2 * (Pl.h / 3), Pl.y, (2 * Pl.h) / 3, Pl.w);
39 | rs();
40 | } else {
41 | fr(Pl.x, Pl.y, Pl.w, (2 * Pl.h) / 3);
42 | rs();
43 | // shade
44 | fs('#777')
45 | fr(Pl.x + 3, Pl.y + 15, 3, 2 * (Pl.h / 3) - 30);
46 | }
47 | rs();
48 | },
49 | eyes: function () {
50 | if (!Pl.w && !Pl.h) return;
51 |
52 | var ct = new Date(), clr;
53 | // 0 means NO, 1 means starts blinking, 2 means blinked
54 | Pl.isBlink = ct - Pl.t > 2500 ? ++Pl.isBlink : 0;
55 | clr = Pl.isBlink > 5 ? '#000' : '#fff';
56 |
57 | if (Pl.isBlink >= 8) {
58 | Pl.t = ct; Pl.isBlink = 0;
59 | }
60 | //fs(clr);
61 | bp(clr);
62 |
63 | if (Pl.died) {
64 | ar(Pl.x - 2 * (Pl.h / 3) + 6, Pl.y + 4, 2, 0, M.PI * 2, true);
65 | ar(Pl.x - 2 * (Pl.h / 3) + 6, Pl.y + 10, 2, 0, M.PI * 2, true);
66 | } else {
67 | ar(Pl.x + 2 * (Pl.w / 3) - 2, Pl.y + 5, 2, 0, M.PI * 2, true);
68 | ar(Pl.x + (Pl.w - 3), Pl.y + 5, 2, 0, M.PI * 2, true);
69 | }
70 | ctx.fillStyle = clr;//'#8effb6';
71 | fl();
72 | },
73 | legs: function () {
74 | var lw = 3; // leg width
75 | fs('#000');
76 | if (Pl.died) {
77 | // left leg
78 | fr(Pl.x, Pl.y + (Pl.w / 3), Pl.h / 3, lw);
79 | // right leg
80 | fr(Pl.x, Pl.y + 2 * (Pl.w / 3), Pl.h / 3, lw);
81 | } else {
82 | // left leg
83 | fr(Pl.x + (Pl.w / 3) - lw / 2, Pl.y + 2 * (Pl.h / 3), lw, Pl.h / 3);
84 | // right leg
85 | fr(Pl.x + 2 * (Pl.w / 3) - lw / 2, Pl.y + 2 * (Pl.h / 3), lw, Pl.h / 3);
86 | }
87 | },
88 | fe: function () { // Fire Extinguisher
89 | fs('#eaeaea');
90 | bp();
91 | el(ctx, Pl.x - 30, Pl.y + Pl.h, 80, 10, '#000')
92 | cp();
93 | fs('#EAEAEA');
94 | fl();
95 | ctx.lineWidth = 1;
96 | sts('#dedede');
97 | st();
98 | fs('#8ED6FF');
99 | for (var i = -30; i < Pl.w + 30; i+=6) {
100 | bp();
101 | mt(Pl.x + i, Pl.y + Pl.h);
102 | lt(Pl.x + i - 1, Pl.y + Pl.h + utils.getRandomInt(10, G.can.height/2));
103 | lt(Pl.x + i + 3, Pl.y + Pl.h + utils.getRandomInt(10, G.can.height/2));
104 | lt(Pl.x + i + 1, Pl.y + Pl.h);
105 | cp();
106 | fl();
107 | }
108 |
109 | },
110 | burst: function () {
111 | // Particle effects
112 | if (Pl.w && Pl.h) {
113 | this.dieParticles = new Particles(Pl.x, Pl.y);
114 | } else if (PS.finished) {
115 | G.stopCycle();
116 | PS.finished = false;
117 | }
118 |
119 | if (!Pl.w && !Pl.h) {
120 | this.dieParticles.draw();
121 | }
122 |
123 | Pl.w = 0;
124 | Pl.h = 0;
125 | },
126 | update: function () {
127 | Pl.x -= G.speed;
128 | if (Pl.isInAir && Pl.bounceFactor > 0) {
129 | Pl.y -= Pl.bounceHeight;
130 | Pl.x += 1.5 * G.speed;
131 | Pl.bounceFactor -= 2;
132 | } else if (Pl.isInAir && !Pl.bounceFactor) { // smooth curve parabola jump
133 | Pl.y -= Pl.bounceHeight / 2;
134 | Pl.x += 1.5 * G.speed;
135 | Pl.bounceFactor -= 2;
136 | } else if (Pl.isInAir && Pl.bounceFactor === -2) { // smooth curve parabola jump
137 | Pl.y -= 0;
138 | Pl.x += 1.5 * G.speed;
139 | Pl.bounceFactor -= 2;
140 | } else if (Pl.isInAir && Pl.bounceFactor === -4) { // smooth curve parabola jump
141 | Pl.y -= Pl.bounceHeight / 2;
142 | Pl.x += 1.5 * G.speed;
143 | Pl.bounceFactor -= 2;
144 | } else if (Pl.isInAir && Pl.bounceFactor === -6) { // revert back force rewind
145 | Pl.y += Pl.bounceHeight;
146 | Pl.x += 1.5 * G.speed;
147 | Pl.bounceFactor = -6;
148 | }
149 |
150 | if (Pl.died && !Pl.busted) {
151 | Pl.y += 10;
152 | if (Pl.isCornerStrike) {
153 | if (Pl.y > CC.h - 3*P.fireOffset) {
154 | if (!Pl.busted) {
155 | Pl.busted = true;
156 | }
157 | }
158 | } else if (!Pl.busted) {
159 | Pl.busted = true;
160 | }
161 | }
162 |
163 | fs('#000');
164 | Pl.body();
165 | Pl.legs();
166 | Pl.eyes();
167 | Pl.tears(2, 6);
168 | Pl.tears(5, 15);
169 | if (Pl.isInAir && !Pl.busted) Pl.fe();
170 | if (Pl.busted) {
171 | Pl.burst();
172 | }
173 | Pl.checkCollision();
174 | },
175 | keyDown: function (key) {
176 | if (Pl.busted) { return; }
177 |
178 | if (key === 32) { // 32 is space,38 is UP, 40 is DOWN
179 | Pl.irj = true; // isReadyToJump
180 | if (Pl.h < 50) { return; }
181 | Pl.h -= 2;
182 | Pl.y += 2;
183 | } else if (key === 39) {
184 | Pl.x += G.speed;
185 | }
186 |
187 | if (Pl.irj) {
188 | Pl.irj = false;
189 | Pl.isInAir = true;
190 | Pl.isKarmaLocked = false;
191 | Pl.bounceFactor = Pl.maxH - Pl.h;
192 | Pl.bounceFactor *= 4;
193 | Pl.h = Pl.maxH;
194 | }
195 | },
196 | keyUp: function () {
197 | /*if (Pl.irj) {
198 | Pl.irj = false;
199 | Pl.isInAir = true;
200 | SU.play('moveAhead');
201 | Pl.bounceFactor = Pl.maxH - Pl.h;
202 | Pl.bounceFactor *= 4;
203 | Pl.h = Pl.maxH;
204 | }*/
205 | },
206 | checkCollision: function () {
207 | if (Pl.x <= 0) { // leftmost collision
208 | Pl.died = true;
209 | Pl.isCornerStrike = true;
210 | return;
211 | } else if (Pl.y > CC.h - P.fireOffset) { // bottom fire collision
212 | Pl.died = true;
213 | Pl.isCornerStrike = true;
214 | return;
215 | } else if (Pl.x + Pl.w > CC.w) { // rightmost collision
216 | Pl.died = true;
217 | Pl.isCornerStrike = true;
218 | return;
219 | } else if (Pl.y < 0 ) { // topmost collision
220 | Pl.died = true;
221 | Pl.isCornerStrike = true;
222 | return;
223 | }
224 |
225 | var i, tree;
226 | for (i = 0; i < G.trees.length; i++) { // M.min(Pl.liesOn + 10, )
227 | tree = G.trees[i];
228 |
229 | var playerEnd = Pl.x + Pl.w - 4; // 4 bcz legs are placed (x coord)technically before body
230 | if ((playerEnd >= tree.x && playerEnd < (tree.x + tree.width + 4) && Pl.y + Pl.w + Pl.h >= tree.x)
231 | ) {
232 | for (var j = 0; j < Pl.bounceHeight; j++) {
233 | if (Pl.y + Pl.h + j >= tree.y) {
234 | G.trees[i].flame = null;
235 | Pl.isInAir = false;
236 | Pl.liesOn = i;
237 | if (!Pl.isKarmaLocked && Pl.liesOn !== Pl.lastLiesOn) {
238 | G.karma && SU.play('moveAhead');
239 | G.karma += 1;
240 | }
241 | Pl.isKarmaLocked = true;;
242 | Pl.lastLiesOn = Pl.liesOn;
243 | // Pl.y += tree.y - (Pl.y + Pl.h) - 2;
244 | break;
245 | }
246 | }
247 | if (Pl.y >= tree.y && Pl.y < tree.y + tree.height) {
248 | Pl.died = true;
249 | break;
250 | }
251 |
252 | }
253 | }
254 | }
255 | };
--------------------------------------------------------------------------------
/src/game.js:
--------------------------------------------------------------------------------
1 | var G, ctx, CC, background, player, weather, smoky;
2 | function Game() {
3 | G = this;
4 | G.isInProgress = true;
5 | G.canSpeedBeIncreased = G.canExplode = true;
6 | G.backgroundColor = '#fff';
7 |
8 | G.karma = 0;
9 |
10 | G.highscore = utils.getLocalStorageData() || 0;
11 | G.isSound = utils.getLocalStorageData(true);
12 | if (G.isSound !== 0) {
13 | G.isSound = 1;
14 | }
15 |
16 | G.resolution = 1;
17 | G.curPos = [];
18 |
19 | G.can = document.querySelector('canvas');
20 | G.can.width = P.w;
21 | G.can.height = P.h;
22 |
23 | ctx = G.ctx = window.c = G.can.getContext('2d');
24 |
25 | G.trees = [];
26 |
27 | // Resizing
28 | G.resize();
29 | addEventListener('resize', G.resize, false);
30 |
31 | CC = document.getElementById('canvascontainer').style;
32 |
33 | document.body.addEventListener('touchstart', G.touchStart.bind(G), false);
34 | document.body.addEventListener('touchmove', G.touchMove.bind(G), false);
35 | document.body.addEventListener('touchend', G.touchEnd.bind(G), false);
36 | document.body.addEventListener('mousedown', G.mouseDown.bind(G), false);
37 | document.body.addEventListener('mousemove', G.mouseMove.bind(G), false);
38 | document.body.addEventListener('mouseup', G.mouseUp.bind(G), false);
39 |
40 | document.body.addEventListener('keydown', G.keyDown.bind(G), false);
41 | document.body.addEventListener('keyup', G.keyUp.bind(G), false);
42 |
43 | // Loop
44 | G.frameCount = 0;
45 | G.lastFrame = G.frameCountStart = Date.now();
46 |
47 | var displayablePixels = _.innerWidth * _.innerHeight * _.devicePixelRatio,
48 | gamePixels = P.w * P.h,
49 | ratio = displayablePixels / gamePixels;
50 |
51 | if (ratio < 0.5){
52 | G.setResolution(ratio * 2);
53 | }
54 |
55 | G.speed = 1;
56 |
57 | // background animation
58 | // background = new Background();
59 | flameBack.canvas = G.can;
60 | //flameBack.init();
61 |
62 | G.menu = true;
63 | }
64 |
65 | var tree, time;
66 | Game.prototype = {
67 | restart: function () {
68 | G.isGameOver = false;
69 | G.isInProgress = true;
70 | G.karma = 0;
71 | G.speed = 1;
72 | G.gameStartTime = new Date().getTime();
73 |
74 | smoky = new SmokyFlame();
75 |
76 | blw = 200, bw =0;
77 | G.addInitialtrees();
78 |
79 | player = new Player();
80 | Pl.x = G.trees[0].x;
81 |
82 | flameBack.init();
83 | weather = new Weather();
84 | G.raf = raf(function(){
85 | if (G.raf) {
86 | G.cycle();
87 | raf(arguments.callee);
88 | }
89 | });
90 | },
91 | stopCycle: function () {
92 | G.isGameOver = true;
93 | G.isInProgress = false;
94 |
95 | flameBack.update();
96 | canvasToImage(); // get image before spash screen
97 |
98 | // console.log('Boom! DIE!');
99 | // update high score
100 | if (G.karma > G.highscore) {
101 | SU.play('highestScore');
102 | G.highscore = G.karma;
103 | utils.setLocalStorageData(G.karma);
104 | }
105 |
106 | SU.play('gameOver');
107 |
108 | G.menu = new Menu();
109 | },
110 | cycle: function () {
111 | var now = new Date().getTime();
112 | dt = now - time;
113 |
114 | if (dt < (1000 / fps))
115 | return; // skip a frame
116 |
117 | //SU.play('game');
118 | time = now;
119 | if (G.menu) {
120 | G.menu.update && G.menu.update();
121 | return;
122 | }
123 |
124 | if (G.canExplode && M.ceil((now - G.gameStartTime) / 1000) % 6 === 0) {
125 | G.mildExplosion ? SU.play('explosion2') : SU.play('explosion1');
126 | G.mildExplosion = !G.mildExplosion;
127 | G.canExplode = false;
128 | } else if (M.ceil((now - G.gameStartTime) / 1000) % 7 === 0) {
129 | G.canExplode = true;
130 | }
131 |
132 | if (G.canSpeedBeIncreased && M.ceil((now - G.gameStartTime) / 1000) % 10 === 0) {
133 | G.speed += G.isMobile() ? 0.1 : 0.2;
134 | WD.speed = utils.getRandomInt(1, 30);
135 | G.canSpeedBeIncreased = false;
136 | } else if (M.ceil((now - G.gameStartTime) / 1000) % 11 === 0) {
137 | // G.speed += 0.1;
138 | G.canSpeedBeIncreased = true;
139 | }
140 |
141 | fs(G.backgroundColor);
142 | fr(0, 0, CC.w, CC.h);
143 |
144 | var speedIncFactor = G.isMobile() ? 1.1 : 1.6;
145 | if (G.speed >= speedIncFactor &&
146 | utils.getRandomInt(0, 10) === 10
147 | ) {
148 | G.showNoisyScreen();
149 | utils.getRandomInt(0, 10) === 4 && SU.play('glitch');
150 | }
151 |
152 | //background.burnBurnBurn();
153 | weather.update();
154 |
155 | ctx.font = '15px Comic Sans';
156 | ctx.fillStyle = thisWeather.hexToRgb(thisWeather.getColor(true), 1.0);
157 | ctx.fillText('KARMA: ' + G.karma, 25, 25);
158 | ctx.fillText('SPEED: ' + G.speed.toFixed(1) + ' mph', G.can.width - 130, 25);
159 | ctx.fillText('WIND: ' + WD.speed.toFixed(1) + ' mph W', G.can.width - 130, 45);
160 | ctx.lineWidth = 3;
161 |
162 | if (G.trees.length) {
163 | for (var i = 0; i < G.trees.length; i++) {
164 | G.trees[i].x -= G.speed;
165 | G.trees[i].update(G.trees[i]);
166 |
167 | if (G.trees[i].x < 0 - G.trees[i].width) {
168 | G.trees[i] = new Tree();
169 | }
170 | }
171 | player.update();
172 | }
173 | flameBack.update();
174 | },
175 | showNoisyScreen: function () {
176 | var w = G.can.width,
177 | h = G.can.height,
178 | idata = ctx.createImageData(w, h),
179 | buffer32 = new Uint32Array(idata.data.buffer),
180 | len = buffer32.length,
181 | i = 0;
182 |
183 | for (; i < len;) {
184 | buffer32[i++] = ((255 * Math.random())|0) << 24;
185 | }
186 |
187 | ctx.putImageData(idata, 0, 0);
188 | },
189 | addInitialtrees: function () {
190 | G.trees = [];
191 | G.trees.push(new Tree({isNoFlame: true}))
192 | for (var i = 0; i < 5; i++) {
193 | G.trees.push(new Tree())
194 | }
195 | },
196 | resize: function() {
197 | setTimeout(function(){
198 | var maxWidth = innerWidth,
199 | maxHeight = innerHeight,
200 |
201 | availableRatio = maxWidth / maxHeight,
202 | baseRatio = P.w / P.h,
203 | ratioDifference = abs(availableRatio - baseRatio),
204 | width,
205 | height,
206 | s = document.getElementById('canvascontainer').style;
207 |
208 | if (availableRatio <= baseRatio){
209 | width = maxWidth;
210 | height = maxHeight;//width / baseRatio;
211 | } else{
212 | height = maxHeight;
213 | width = height * baseRatio;
214 | }
215 |
216 | s.width = width + 'px';
217 | s.height = height + 'px';
218 |
219 | ctx.globalCompositeOperation="lighter";
220 |
221 | G.can.width = width;
222 | G.can.height = height;
223 |
224 |
225 | if (G.menu) {
226 | G.menu = new Menu();
227 | G.raf = raf(function(){
228 | if (G.raf) {
229 | G.cycle();
230 | raf(arguments.callee);
231 | }
232 | });
233 | return;
234 | }
235 |
236 | G.restart();
237 |
238 | },100);
239 | },
240 | isMobile: function () {
241 | if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i
242 | .test(navigator.userAgent)) {
243 | return true;
244 | }
245 | return false;
246 | },
247 | pos : function(e){
248 | var rect = G.can.getBoundingClientRect(),
249 | pos = [];
250 |
251 | e = e.touches || [e];
252 |
253 | for(var i = 0 ; i < e.length ; i++){
254 | pos.push({
255 | x : (e[i].clientX),// - rect.left) / (rect.width / P.w),
256 | y : (e[i].clientY)// - rect.top) / (rect.height / P.h)
257 | })
258 | }
259 |
260 | return pos;
261 | },
262 | touchStart : function(e,m) {
263 | e.preventDefault();
264 | G.touch = G.touch || !m;
265 | var p = G.pos(e);
266 | G.curPos = p;
267 |
268 | scrollTo(0, 1);
269 |
270 | if (G.menu) {
271 | var x = G.curPos[0].x - G.can.offsetLeft,
272 | y = G.curPos[0].y - G.can.offsetTop;
273 |
274 | G.menu.mouseDown && G.menu.mouseDown(e, x, y);
275 | } else {
276 | // G.isTouching = true;
277 | G.keyDown({keyCode: 32});
278 | }
279 |
280 | if(!G.isInProgress) return;
281 |
282 | //G.world.touchStart();
283 | },
284 | touchMove : function(e) {
285 | e.preventDefault();
286 | if (G.curPos){
287 | G.curPos = G.pos(e);
288 |
289 | if(!G.isInProgress) return;
290 | //G.world.touchMove();
291 | }
292 | },
293 | touchEnd : function(e) {
294 | e.preventDefault();
295 |
296 | var p = G.curPos[0];
297 | G.curPos = G.pos(e);
298 |
299 | if (!G.isInProgress) {
300 | //!G.isInProgress.click(p.x, p.y);
301 | } else {
302 | //G.world.touchEnd();
303 | }
304 | },
305 | keyDown: function(e) {
306 | // 13 is enter
307 | if ((e.keyCode === 13 || e.keyCode === 32) && G.menu) {
308 | G.menu = null;
309 | G.restart();
310 | SU.play('playGame');
311 | return;
312 | }
313 | if (!G.isInProgress) {
314 | return;
315 | }
316 |
317 | // 39 is right, 40 is down, 38 is up
318 | if (e.keyCode === 39 || e.keyCode === 38 || e.keyCode === 32) {
319 | player && player.keyDown(e.keyCode);
320 | }
321 | },
322 | keyUp: function(e) {
323 | if(!G.isInProgress) return;
324 | player && player.keyUp(e.keyCode);
325 | },
326 | mouseDown: function(e) {
327 | /*if(!G.touch){
328 | G.touchStart(e, true);
329 | }*/
330 | if (G.menu) {
331 | var x = e.pageX - G.can.offsetLeft,
332 | y = e.pageY - G.can.offsetTop;
333 |
334 | G.menu.mouseDown && G.menu.mouseDown(e, x, y);
335 | }
336 | },
337 | mouseMove: function(e) {
338 | /*if(!G.touch){
339 | G.touchMove(e);
340 | }*/
341 | },
342 | mouseUp: function(e) {
343 | /*if(!G.touch){
344 | G.touchEnd(e);
345 | }*/
346 | },
347 | setResolution: function(r) {
348 | G.can.width = P.w * r;
349 | G.can.height = P.h * r;
350 |
351 | G.resolution = r;
352 | }
353 | }
--------------------------------------------------------------------------------
/src/background.js:
--------------------------------------------------------------------------------
1 | /*var BG;
2 | function Background() {
3 | BG = this;
4 | BG.animate();
5 | }
6 |
7 | Background.prototype = {
8 | burnBurnBurn: function() {
9 | var x, y, bottomLine = BG.canvasWidth * (BG.canvasHeight - 1);
10 |
11 | // draw random pixels at the bottom line
12 | for (x = 0; x < BG.canvasWidth; x++) {
13 | var value = 0;
14 |
15 | if (Math.random() > BG.threshold)
16 | value = 255;
17 |
18 | BG.fire[bottomLine + x] = value;
19 | }
20 |
21 | // move flip upwards, start at bottom
22 | var value = 0;
23 |
24 | for (y = 0; y < BG.canvasHeight; ++y) {
25 | for (var x = 0; x < BG.canvasWidth; ++x) {
26 | if (x == 0) {
27 | value = BG.fire[bottomLine];
28 | value += BG.fire[bottomLine];
29 | value += BG.fire[bottomLine - BG.canvasWidth];
30 | value /= 3;
31 | } else if (x == BG.canvasWidth -1) {
32 | value = BG.fire[bottomLine + x];
33 | value += BG.fire[bottomLine - BG.canvasWidth + x];
34 | value += BG.fire[bottomLine + x - 1];
35 | value /= 3;
36 | } else {
37 | value = BG.fire[bottomLine + x];
38 | value += BG.fire[bottomLine + x + 1];
39 | value += BG.fire[bottomLine + x - 1];
40 | value += BG.fire[bottomLine - BG.canvasWidth + x];
41 | value /= 4;
42 | }
43 |
44 | if (value > 1)
45 | value -= 1;
46 |
47 | value = Math.floor(value);
48 | var index = bottomLine - BG.canvasWidth + x;
49 | BG.fire[index] = value;
50 | }
51 |
52 | bottomLine -= BG.canvasWidth;
53 | }
54 |
55 | var skipRows = 1; // skip the bottom 2 rows
56 |
57 | // render the flames using our color table
58 | for (var y = skipRows; y < BG.canvasHeight; ++y) {
59 | for (var x = 0; x < BG.canvasWidth; ++x) {
60 | var index = y * BG.canvasWidth * 4 + x * 4;
61 | var value = BG.fire[(y - skipRows) * BG.canvasWidth + x];
62 |
63 | BG.data[index] = BG.colors[value][0];
64 | BG.data[++index] = BG.colors[value][1];
65 | BG.data[++index] = BG.colors[value][2];
66 | BG.data[++index] = 255;
67 | }
68 | }
69 |
70 | // sometimes change BG.fire intensity
71 | if (BG.intensity == null) {
72 | if (Math.random() > 0.95) {
73 | BG.randomizeThreshold();
74 | }
75 | }
76 |
77 | BG.ctx.putImageData(BG.imageData, 0, BG.CC.height - BG.canvasHeight);
78 |
79 | },
80 | randomizeThreshold: function() {
81 | BG.threshold += Math.random() * 0.2 - 0.1;
82 | BG.threshold = Math.min(Math.max(BG.threshold, 0.5), 0.8);
83 | },
84 | animate: function () {
85 | BG.intensity = null;
86 | BG.threshold = 0.5;
87 | BG.CC = document.querySelector('canvas');
88 | BG.ctx = BG.CC.getContext('2d');
89 | BG.canvasWidth = BG.CC.width;
90 | BG.canvasHeight = 50 || P.fireOffset;
91 | BG.imageData = BG.ctx.getImageData(0, BG.CC.height - BG.canvasHeight, BG.canvasWidth, BG.canvasHeight);
92 | BG.data = BG.imageData.data;
93 | //BG.numPixels = BG.data.length / 4;
94 | BG.colors = [];
95 |
96 | for (var i = 0; i < 256; i++) {
97 | var color = [];
98 | color[0] = color[1] = color[2] = 75;
99 | BG.colors[i] = color;
100 | }
101 |
102 | for (var i = 0; i < 32; ++i) {
103 | BG.colors[i][2] = i << 1;
104 | BG.colors[i + 32][0] = i << 3;
105 | BG.colors[i + 32][2] = 64 - (i << 1);
106 | BG.colors[i + 64][0] = 255;
107 | BG.colors[i + 64][1] = i << 3;
108 | BG.colors[i + 96][0] = 255;
109 | BG.colors[i + 96][1] = 255;
110 | BG.colors[i + 96][2] = i << 2;
111 | BG.colors[i + 128][0] = 255;
112 | BG.colors[i + 128][1] = 255;
113 | BG.colors[i + 128][2] = 64 + (i << 2);
114 | BG.colors[i + 160][0] = 255;
115 | BG.colors[i + 160][1] = 255;
116 | BG.colors[i + 160][2] = 128 + (i << 2);
117 | BG.colors[i + 192][0] = 255;
118 | BG.colors[i + 192][1] = 255;
119 | BG.colors[i + 192][2] = 192 + i;
120 | BG.colors[i + 224][0] = 255;
121 | BG.colors[i + 224][1] = 255;
122 | BG.colors[i + 224][2] = 224 + i;
123 | }
124 |
125 | BG.fire = [];
126 | // init BG.fire array
127 | for (var i = 0; i < BG.canvasWidth * BG.canvasHeight; i++) {
128 | BG.fire[i] = 75;
129 | }
130 |
131 | BG.burnBurnBurn();
132 |
133 | // intercept key up event to change intensity on BG.fire effect
134 | document.body.onkeyup = function(event) {
135 | if (event.keyCode >= 97 && event.keyCode <= 105) {
136 | BG.intensity = (event.keyCode - 97);
137 | BG.intensity = BG.intensity / 8;
138 | BG.intensity = BG.intensity * 0.4;
139 | BG.intensity = BG.intensity + 0.2;
140 | BG.threshold = 1 - BG.intensity;
141 | } else if (event.keyCode == 96) { // 0 ==> randomize
142 | BG.intensity = 0;
143 | BG.randomizeThreshold();
144 | }
145 | };
146 |
147 | }
148 | };*/
149 |
150 | var flameBack = new function() {
151 | var context;
152 | var buffer;
153 | var bufferContext;
154 | var imageData;
155 | var palette;
156 | var colorMap;
157 | var width;
158 | var height;
159 | var scale = 2;
160 | var fan = 2.5;
161 | var slack = 5;
162 | this.time = new Date();
163 |
164 | this.canvas = undefined;
165 |
166 | this.init = function() {
167 | context = this.canvas.getContext('2d');
168 |
169 | width = (this.canvas.width + 30) / scale;
170 | height = P.fireOffset / scale;
171 |
172 | width = Math.ceil(width);
173 | height = Math.ceil(height);
174 |
175 | colorMap = Array(width * height);
176 |
177 | for(var i = 0; i < colorMap.length; i++)
178 | colorMap[i] = 255;
179 |
180 | initPalette();
181 | initBuffer();
182 |
183 | this.update();
184 | };
185 |
186 | // init palette from warm to white hot colors
187 | var initPalette = function() {
188 | palette = Array(256);
189 |
190 | for(var i = 0; i < 64; i++) {
191 | palette[i] = [(i << 2), 0, 0];
192 | palette[i + 64] = [255, (i << 2), 0];
193 | palette[i + 128] = [255, 255, (i << 2)];
194 | palette[i + 192] = [255, 255, 255];
195 | }
196 | };
197 |
198 | // offscreen buffer for rendering and scaling
199 | var initBuffer = function() {
200 | buffer = document.createElement('canvas');
201 | buffer.width = width;
202 | buffer.height = height;
203 | buffer.style.visibility = 'hidden';
204 |
205 | bufferContext = buffer.getContext('2d');
206 | imageData = bufferContext.createImageData(width, height);
207 | };
208 |
209 | // main render loop
210 | this.update = function() {
211 | if (!G.isMobile()) {
212 | smooth();
213 | draw();
214 | fan = utils.getRandomInt(0, 6);
215 | } else {
216 | var grd = ctx.createLinearGradient(0, CC.h - P.fireOffset , 0, G.can.height);
217 | grd.addColorStop(0, 'rgba(255, 0, 0, ' + utils.getRandomInt(8, 10)/10 + ')');
218 | grd.addColorStop(0.7, 'rgba(255, 165, 0, ' + utils.getRandomInt(8, 10)/10 + ')');
219 | grd.addColorStop(0.9, 'rgba(255, 255, 0, ' + utils.getRandomInt(8, 10)/10 + ')');
220 | sv();
221 | fs(grd);
222 | fr(0, CC.h - P.fireOffset, G.can.width, P.fireOffset)
223 | rs();
224 | }
225 | };
226 |
227 | var smooth = function() {
228 | for(var x = width - 1; x >= 1; x--) {
229 | for(var y = height; y--;) {
230 | var p = ((
231 | colorMap[toIndex(x - 1, y - 1)] +
232 | colorMap[toIndex(x, y - 1)] +
233 | colorMap[toIndex(x + 1, y - 1)] +
234 | colorMap[toIndex(x - 1, y)] +
235 | colorMap[toIndex(x + 1, y)] +
236 | colorMap[toIndex(x - 1, y + 1)] +
237 | colorMap[toIndex(x, y + 1)] +
238 | colorMap[toIndex(x + 1, y + 1)]) >> 3);
239 |
240 | p = Math.max(0, p - randomValue(fan));
241 |
242 | colorMap[toIndex(x, y - 1)] = p;
243 |
244 | if (y < height - slack) { // don't draw random noise in bottom rows
245 | if (y < height - 2) {
246 | // set two lines of random palette noise at bottom of
247 | // colorMap
248 | colorMap[toIndex(x, height)] =
249 | randomValue(palette.length);
250 | colorMap[toIndex(x, height - 1)] =
251 | randomValue(palette.length);
252 | }
253 |
254 | drawPixel(x, y, palette[colorMap[toIndex(x, y)]]);
255 | }
256 | }
257 | }
258 | };
259 |
260 | // draw colormap->palette values to screen
261 | var draw = function() {
262 | // render the image data to the offscreen buffer...
263 | bufferContext.putImageData(imageData, 0, 0);
264 | // ...then draw it to scale to the onscreen canvas
265 | context.drawImage(buffer, -20, CC.h - (height * scale), width * scale, height * scale + 10);
266 | };
267 |
268 | // set pixels in imageData
269 | var drawPixel = function(x, y, color) {
270 | var offset = (x + y * imageData.width) * 4;
271 | imageData.data[offset] = color[0];
272 | imageData.data[offset + 1] = color[1];
273 | imageData.data[offset + 2] = color[2];
274 | imageData.data[offset + 3] = 255;
275 | };
276 |
277 | var randomValue = function(max) {
278 | // protip: a double bitwise not (~~) is much faster than
279 | // Math.floor() for truncating floating point values into "ints"
280 | return ~~(Math.random() * max);
281 | };
282 |
283 | // because "two-dimensional" arrays in JavaScript suck
284 | var toIndex = function(x, y) {
285 | return (y * width + x);
286 | };
287 |
288 | // draw a bunch of random embers onscreen
289 | this.drawEmbers = function() {
290 | for(var x = 1; x < width - 1; x++) {
291 | for(var y = 1; y < height; y++) {
292 | if(Math.random() < 0.11)
293 | colorMap[toIndex(x, y)] = randomValue(palette.length);
294 | }
295 | }
296 | };
297 | };
298 |
--------------------------------------------------------------------------------
/src/weather.js:
--------------------------------------------------------------------------------
1 | var CC, RN, WD, SM, cloud, sunMoon, wind, rain;
2 | var diffInWeatherTime = 5;
3 | function Cloud() {
4 | this.color = 'blue';
5 | this.x = G.can.width || P.w;
6 | this.y = 100;
7 | this.speed = 7;
8 | this.update();
9 | }
10 | Cloud.prototype = {
11 | drawArcs: function (x, y, sx, sy) {
12 | bp();
13 | mt(x/sx + 150, y - 15); // 188, 50
14 | qct(
15 | x/sx + 150 + 50,
16 | y - 15 + 0,
17 | x/sx + 150 + 40,
18 | y - 15 + 40
19 | );
20 | ctx.lineWidth = 4;
21 | sts(thisWeather.hexToRgb(thisWeather.getColor(), 0.8))
22 | st();
23 |
24 | bp();
25 | mt(x/sx + 20 + 30, y + 10); // 188, 50
26 | qct(
27 | x/sx + 30,
28 | y + 35 + 10,
29 | x/sx + 30 + 60,
30 | y + 35 + 15
31 | );
32 | st();
33 | },
34 | draw: function (x, y, sx, sy) {
35 | var cloudColor = thisWeather.hexToRgb(thisWeather.getColor(), 0.5);
36 |
37 | ctx.scale(sx, sy);
38 | bp();
39 | fs(cloudColor)
40 | mt(x/sx, y);
41 | bct(x/sx - 40, y + 20, x/sx - 40, y + 70, x/sx + 60, y + 70);
42 | bct(x/sx + 80, y + 100, x/sx + 150, y + 100, x/sx + 170, y + 70);
43 | bct(x/sx + 250, y + 70, x/sx + 250, y + 40, x/sx + 220, y + 20);
44 | bct(x/sx + 260, y - 40, x/sx + 200, y - 50, x/sx + 170, y - 30);
45 | bct(x/sx + 150, y - 75, x/sx + 80, y - 60, x/sx + 80, y - 30);
46 | bct(x/sx + 30, y - 75, x/sx - 20, y - 60, x/sx, y);
47 | cp();
48 | ctx.shadowColor = thisWeather.hexToRgb(thisWeather.getColor(), 0.8);
49 | ctx.shadowOffsetX = -3;
50 | ctx.shadowOffsetY = 3;
51 | ctx.shadowBlur = 10;
52 | ctx.lineWidth = 3;
53 | sts(thisWeather.hexToRgb(thisWeather.getColor(), 0.8))
54 | st();
55 | fl();
56 |
57 | this.drawArcs(x, y, sx, sy);
58 | },
59 | update: function () {
60 | this.x -= this.speed;
61 | if (this.x + 250 < 0) {
62 | this.x = CC.w + 250;
63 | this.y = this.y + utils.getRandomInt(-10, 10);
64 | }
65 |
66 | var x = this.x, y = this.y;
67 | sv();
68 | this.draw(x, y, 0.8, 0.7);
69 | this.draw(x, y, 0.7, 0.6);
70 | this.draw(x, y, 0.6, 0.6);
71 | this.draw(x, y, 0.5, 0.7);
72 | rs();
73 | }
74 | }
75 |
76 | function SunMoon() {
77 | SM = this;
78 | this.isSun = true; // will act as moon too
79 | this.r = 20;
80 | this.x = 0;
81 | this.y = 100;
82 | this.speed = 1;
83 | G.period = 'morning';
84 | this.update();
85 | }
86 | SunMoon.prototype = {
87 | getColor: function () {
88 | var color;
89 | switch (G.period) {
90 | case 'morning':
91 | color = this.isSun ? '#ffff9e' : '#fff';
92 | break;
93 | case 'afternoon':
94 | color = this.isSun ? 'yellow' : '#fff';
95 | break;
96 | case 'evening':
97 | color = this.isSun ? '#e28800' : '#fff';
98 | break;
99 | case 'night':
100 | color = this.isSun ? '#fff' : '#fff';
101 | break;
102 | }
103 | return color;
104 | },
105 | resetPos: function () {
106 | this.x = 0;
107 | this.y = 100;
108 | G.period = 'morning';
109 | thisWeather.step = 0;
110 | },
111 | update: function () {
112 | // lets assume 30 secs is 1 day, so 15-15 secs day-night
113 | if (Weather.dt / 1000 % (5*diffInWeatherTime) > 5*diffInWeatherTime ||
114 | Weather.dt / 1000 % (5*diffInWeatherTime) > 4*diffInWeatherTime ||
115 | Weather.dt / 1000 % (5*diffInWeatherTime) > 3*diffInWeatherTime
116 | ) {
117 | G.period = 'night';
118 | } else if (Weather.dt / 1000 % (5*diffInWeatherTime) > 2*diffInWeatherTime) {
119 | G.period = 'evening';
120 | } else if (Weather.dt / 1000 % (5*diffInWeatherTime) > 1*diffInWeatherTime) {
121 | G.period = 'afternoon';
122 | } else {
123 | G.period = 'morning';
124 | }
125 |
126 | this.x += ((G.can.width / (2 * diffInWeatherTime)) / fps); // this.speed;
127 | if (this.x > G.can.width) {
128 | this.resetPos();
129 | this.isSun = !this.isSun;
130 | return;
131 | }
132 |
133 | this.y -= 0.1;
134 | sv();
135 | ctx.shadowColor = this.getColor();
136 | ctx.shadowOffsetX = -3;
137 | ctx.shadowOffsetY = 3;
138 | ctx.shadowBlur = 10;
139 | bp();
140 | ar(this.x, this.y, this.r, 0, Math.PI * 2, true);
141 | cp();
142 | ctx.fillStyle = this.getColor();
143 | fl();
144 | rs()
145 |
146 | sv();
147 | bp();
148 | ctx.fillStyle = thisWeather.hexToRgb('#444', 0.5);
149 | // moon curvature
150 | if (!this.isSun) {
151 | ar(this.x + 5, this.y - 5, 20, 0, Math.PI * 2, true);
152 | }
153 | cp();
154 | fl();
155 | rs();
156 |
157 | thisWeather.updateGradient();
158 | }
159 | }
160 |
161 | function WindParticle(i) {
162 | this.x = G.can.width + utils.getRandomInt(0, G.can.width);
163 | this.y = (i+1) * WD.pDist;
164 | this.color = '#d1e5ff';
165 | this.speed = utils.getRandomInt(1, WD.speed);
166 | }
167 | function Wind() {
168 | WD = this;
169 | this.speed = 1;
170 | this.particlesCount = 15;
171 | this.particles = [];
172 | this.pDist = 10;
173 | this.create();
174 | }
175 | Wind.prototype = {
176 | create: function () {
177 | for (var i = 0; i < WD.particlesCount; i++) {
178 | WD.particles.push(new WindParticle(i));
179 | }
180 | },
181 | update: function () {
182 | for (var i = 0; i < WD.particles.length; i++) {
183 | var wParticle = WD.particles[i];
184 | // wParticle.y += wParticle.speed;
185 | wParticle.x -= M.max(wParticle.speed);
186 | wParticle.color = thisWeather.getColor();
187 | fs(wParticle.color);
188 | var wParticleW = utils.getRandomInt(10, 50)
189 | bp();
190 | mt(wParticle.x, wParticle.y);
191 | lt(wParticle.x - wParticleW, wParticle.y);
192 | cp();
193 | fl();
194 | ctx.lineWidth = 2;
195 | sts(wParticle.color);
196 | st();
197 |
198 | if (wParticle.x < 0) {
199 | // reinitialize a new wParticle
200 | WD.particles[i] = new WindParticle(i);
201 | }
202 | }
203 | }
204 | }
205 |
206 | function Droplet(i) {
207 | this.x = RN.topDropletsDist * i;
208 | this.y = 0;
209 | this.color = '#d1e5ff';
210 | this.speed = utils.getRandomInt(10, 30);
211 | }
212 | function Rain() {
213 | RN = this;
214 | this.particles = [];
215 | this.particlesCount = 33;
216 | this.topDroplets = this.particlesCount * 1.5; // 66
217 | this.rightDroplets = this.particlesCount / 3; // 33
218 | this.topDropletsDist = (G.can.width / this.topDroplets) * 2;
219 | this.rightDropletsDist = (G.can.width / this.rightDroplets) * 2;
220 | this.create();
221 | }
222 | Rain.prototype = {
223 | create: function () {
224 | for (var i = 0; i < RN.particlesCount; i++) {
225 | RN.particles.push(new Droplet(i));
226 | }
227 | },
228 | update: function () {
229 | for (var i = 0; i < RN.particles.length; i++) {
230 | var droplet = RN.particles[i];
231 | droplet.y += droplet.speed;
232 | droplet.x -= M.max(G.speed, wind.speed);
233 | droplet.color = thisWeather.getColor();
234 | fs(droplet.color);
235 | var dropletH = utils.getRandomInt(6, 20)
236 | bp();
237 | mt(droplet.x, droplet.y);
238 | lt(droplet.x - 2, droplet.y + dropletH);
239 | cp();
240 | fl();
241 | ctx.lineWidth = 2;
242 | sts(droplet.color);
243 | st();
244 |
245 | if (droplet.y > CC.h) {
246 | // reinitialize a new droplet
247 | RN.particles[i] = new Droplet(i);
248 | }
249 | }
250 | }
251 | };
252 |
253 | function Weather() {
254 | this.colors = [
255 | [255,255,255],
256 | [142,214,255],
257 | [255, 254, 210],
258 | [153,153,153],
259 | [20,20,20],
260 | [20,20,20]
261 | ];
262 |
263 | this.step = 0;
264 | this.i = 0;
265 | this.colorIndices = [0, 1 ,2, 3];
266 |
267 | thisWeather = this;
268 | CC = document.getElementById('canvascontainer').style;
269 | this.init();
270 | }
271 |
272 | Weather.prototype = {
273 | updateGradient: function () {
274 | var c0_0 = thisWeather.colors[thisWeather.colorIndices[0]],
275 | c0_1 = thisWeather.colors[thisWeather.colorIndices[1]],
276 | c1_0 = thisWeather.colors[thisWeather.colorIndices[2]],
277 | c1_1 = thisWeather.colors[thisWeather.colorIndices[3]],
278 |
279 | istep = 1 - thisWeather.step,
280 | r1 = Math.round(istep * c0_0[0] + thisWeather.step * c0_1[0]),
281 | g1 = Math.round(istep * c0_0[1] + thisWeather.step * c0_1[1]),
282 | b1 = Math.round(istep * c0_0[2] + thisWeather.step * c0_1[2]),
283 | color1 = 'rgb(' + r1 + ',' + g1 + ',' + b1 + ')',
284 |
285 | r2 = Math.round(istep * 255+ thisWeather.step * 255),
286 | g2 = Math.round(istep * 255+ thisWeather.step * 255),
287 | b2 = Math.round(istep * 255+ thisWeather.step * 255),
288 | color2 = 'rgb(' + r2 + ',' + g2 + ',' + b2 + ')';
289 |
290 | var grd = ctx.createLinearGradient(0, 0, 0, G.can.height);
291 | grd.addColorStop(0, color1);
292 | grd.addColorStop(0.9, color2);
293 | G.backgroundColor = grd;
294 |
295 | thisWeather.step += SM.isSun ? 0.0076 : 0.0076/2.22; // 1 / (diffInWeatherTime * fps);
296 | if (thisWeather.step >= 1) {
297 | thisWeather.step = 0;
298 | for (var j = 0; j < thisWeather.colorIndices.length; j++) {
299 | thisWeather.colorIndices[j] = (thisWeather.i + 1) % thisWeather.colors.length;
300 | }
301 | thisWeather.i += 1;
302 | }
303 | },
304 | hexToRgb: function (hexColor, alpha) {
305 | if (!hexColor) { return; }
306 |
307 | alpha = alpha || 1.0;
308 | // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
309 | var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
310 | hexColor = hexColor.replace(shorthandRegex, function(m, r, g, b) {
311 | return r + r + g + g + b + b;
312 | });
313 |
314 | var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hexColor);
315 | return result ? 'rgba(' +
316 | parseInt(result[1], 16) + ',' +
317 | parseInt(result[2], 16) + ',' +
318 | parseInt(result[3], 16) + ',' +
319 | alpha + ')' : 'rgba(255,255,255,0)';
320 | },
321 | getColor: function (isKarmaText) {
322 | var color;
323 | switch (G.period) {
324 | case 'morning':
325 | color = isKarmaText ? SM.isSun ? '#444' : '#fff' : SM.isSun ? '#8ED6FF' : '#444';
326 | break;
327 | case 'afternoon':
328 | color = isKarmaText ? SM.isSun ? '#444' : '#fff' : SM.isSun ? '#56baf3' : '#444';
329 | break;
330 | case 'evening':
331 | color = isKarmaText ? SM.isSun ? '#444' : '#fff' : SM.isSun ? '#999' : '#444';
332 | break;
333 | case 'night':
334 | color = isKarmaText ? SM.isSun ? '#444' : '#fff' : SM.isSun ? '#444' : '#444';
335 | break;
336 | }
337 | return color;
338 | },
339 | update: function () {
340 | var now = new Date().getTime();
341 | Weather.dt = now - G.gameStartTime;
342 |
343 | sunMoon.update();
344 | if (!G.isMobile()) {
345 | cloud.update();
346 |
347 | // console.log(M.ceil(Weather.dt / 1000))
348 | if (!this.canRain && M.ceil(Weather.dt / 1000) % 16 === 0) {
349 | this.canRain = true;
350 | this.isRaining = true;
351 | } else if (M.ceil(Weather.dt / 1000) % 33 === 0) {
352 | this.canRain = false;
353 | this.isRaining = false;
354 | }
355 |
356 | if (this.canRain && this.isRaining) {
357 | rain.update();
358 | }
359 |
360 | wind.update();
361 | }
362 | },
363 | init: function () {
364 | cloud = new Cloud();
365 | sunMoon = new SunMoon();
366 | rain = new Rain();
367 | wind = new Wind();
368 | this.update();
369 | }
370 | };
371 |
--------------------------------------------------------------------------------
/src/menu.js:
--------------------------------------------------------------------------------
1 | var MN;
2 | function Menu() {
3 | MN = this;
4 | this.y = 0;
5 | this.font = '50px Helvetica';
6 | this.fireColor = 'rgb(255, 56, 8)';
7 |
8 | ctx.fillStyle = '#fff'
9 | ctx.fillRect(0, 0, G.can.width, G.can.height);
10 |
11 | this.heat = MN.getHeatMap();
12 | this.noise = null;
13 | this.noise = MN.getNoise(G.can.width, G.can.height*8);
14 | ctx.drawImage(this.heat, 0, 0);
15 | this.update();
16 | }
17 | Menu.prototype = {
18 | getNoise: function () {
19 | var canvas = document.createElement('canvas');
20 | canvas.width = G.can.width;
21 | canvas.height = G.can.height;
22 | var ctx = canvas.getContext('2d');
23 |
24 | var w = canvas.width, h = canvas.height,
25 | img = ctx.createImageData(w, h),
26 | n = w * h * 4;
27 |
28 | for(var i = 0; i < n; i+=4) {
29 | img.data[i] = 15;
30 | img.data[i+1] = 3;
31 | img.data[i+2] = 1;
32 | img.data[i+3] = Math.floor(Math.random() * 128);
33 | }
34 | sv();
35 | ctx.putImageData(img, 0, 0);
36 | ctx.drawImage(canvas, 0, 0, w * 64, h * 64);
37 | ctx.globalAlpha = 0.5;
38 | ctx.drawImage(canvas, 0, 0, w * 16, h * 16);
39 | var img = ctx.getImageData(0, 0, w, h);
40 | // increase contrast a bit by clamping values
41 | for (var i = 3; i < w * h * 4; i += 4){
42 | if (img.data[i] > 220){
43 | img.data[i] = 255;
44 | }
45 | if (img.data[i] < 40){
46 | img.data[i] = 0;
47 | }
48 | }
49 | ctx.putImageData(img, 0, 0);
50 | rs();
51 | return canvas;
52 | },
53 | getHeatMap: function () {
54 | var canvas = document.createElement('canvas');
55 | canvas.width = G.can.width;
56 | canvas.height = G.can.height;
57 |
58 | var ctx = canvas.getContext('2d');
59 | sv();
60 | var w = G.can.width,
61 | h = G.can.height,
62 | color = MN.fireColor,
63 | firstText = G.isGameOver ? 'GAME' : 'SAVE',
64 | secondText = G.isGameOver ? 'OVER' : 'THE';
65 | thirdText = G.isGameOver ? '' : 'FOREST';
66 |
67 | if (G.isMobile()) {
68 | firstText = firstText.split('').join(' ');
69 | secondText = secondText.split('').join(' ');
70 | thirdText = thirdText.split('').join(' ');
71 | } else {
72 | firstText = firstText.split('').join(' ');
73 | secondText = secondText.split('').join(' ');
74 | thirdText = thirdText.split('').join(' ');
75 | }
76 |
77 | ctx.fillStyle = color;
78 | ctx.strokeStyle = color;
79 | ctx.font = MN.font;
80 |
81 | var m1 = ctx.measureText(firstText);
82 | var m2 = ctx.measureText(secondText);
83 | var m3 = ctx.measureText(thirdText);
84 | ctx.fillText(firstText, (w - m1.width) / 2, h / 6);
85 | ctx.fillText(secondText, (w - m2.width) / 2, h / 4);
86 | ctx.fillText(thirdText, (w - m3.width) / 2, h / 3);
87 | ctx.lineWidth = 10;
88 |
89 | if (!G.isInfoMenu) {
90 | var highestScoreText = 'BEST: ' + G.highscore;
91 | if (G.isMobile()) {
92 | highestScoreText = highestScoreText.split('').join(' ');
93 | } else {
94 | highestScoreText = highestScoreText.split('').join(' ');
95 | }
96 | ctx.fillStyle = '#fff';
97 | ctx.font = '35px Helvetica';
98 | ctx.fillText(highestScoreText, (w - ctx.measureText(highestScoreText).width) / 2, h / 2.1);
99 |
100 | // Sound circle
101 | ctx.beginPath();
102 | ctx.arc(w*(1/4), h/1.2, 30, 0, 2 * Math.PI, false);
103 | ctx.fillStyle = '#555';
104 | ctx.closePath();
105 | ctx.fill();
106 |
107 | // Rules / Instructions circle
108 | ctx.beginPath();
109 | ctx.arc(w*(3/4), h/1.2, 30, 0, 2 * Math.PI, false);
110 | ctx.fillStyle = '#555';
111 | ctx.closePath();
112 | ctx.fill();
113 |
114 | // sound icon
115 | ctx.beginPath();
116 | ctx.moveTo(w*(1/4) - 20, h/1.2 - 10);
117 | ctx.lineTo(w*(1/4) - 20, h/1.2 + 5);
118 | ctx.lineTo(w*(1/4) - 10, h/1.2 + 5);
119 | ctx.lineTo(w*(1/4) + 5, h/1.2 + 15);
120 | ctx.lineTo(w*(1/4) + 5, h/1.2 - 20);
121 | ctx.lineTo(w*(1/4) - 10, h/1.2 - 10);
122 | ctx.fillStyle = '#222';
123 | ctx.closePath();
124 | if (G.isSound) {
125 | ctx.fillRect(w*(1/4) + 10, h/1.2 - 5, 3, 10);
126 | ctx.fillRect(w*(1/4) + 15, h/1.2 - 7, 3, 15);
127 | ctx.fillRect(w*(1/4) + 20, h/1.2 - 10, 3, 20);
128 | }
129 | ctx.fill();
130 |
131 | // if no sound, show / on icon
132 | if (!G.isSound) {
133 | ctx.save();
134 | ctx.beginPath();
135 | ctx.moveTo(w*(1/4) + 10, h/1.2 - 22);
136 | ctx.lineTo(w*(1/4) - 10, h/1.2 + 22);
137 | ctx.closePath();
138 | ctx.fill();
139 | ctx.lineWidth = 5;
140 | ctx.strokeStyle = '#000';
141 | ctx.stroke();
142 | ctx.restore();
143 | }
144 |
145 | // instructions icon
146 | ctx.fillRect(w*(3/4) - 2, h/1.2, 5, 15);
147 | ctx.beginPath();
148 | ctx.arc(w*(3/4), h/1.2 - 10, 5, 0, 2 * Math.PI, false);
149 | ctx.closePath();
150 | ctx.fillStyle = '#222';
151 | ctx.fill();
152 |
153 | if (G.isGameOver) {
154 | ctx.fillStyle = '#fff';
155 | ctx.font = '35px Helvetica';
156 | var karmaText = 'KARMA: ' + G.karma;
157 |
158 | if (G.isMobile()) {
159 | karmaText = karmaText.split('').join(' ');
160 | } else {
161 | karmaText = karmaText.split('').join(' ');
162 | }
163 |
164 | ctx.fillText(karmaText, (w - ctx.measureText(karmaText).width) / 2, h / 2.5);
165 | ctx.lineWidth = 10;
166 |
167 | ctx.beginPath();
168 | ctx.arc(w*(2/4), h/1.2, 30, 0, 2 * Math.PI, false);
169 | ctx.fillStyle = '#555';
170 | ctx.closePath();
171 | ctx.fill();
172 |
173 | // download icon
174 | ctx.beginPath();
175 | ctx.moveTo(w*(2/4) - 10, h/1.2 - 15);
176 | ctx.lineTo(w*(2/4) - 10, h/1.2 - 15 + 15);
177 | ctx.lineTo(w*(2/4) - 20, h/1.2 - 15 + 15);
178 |
179 | ctx.lineTo(w*(2/4), h/1.2 - 15 + 35);
180 | ctx.lineTo(w*(2/4) + 20, h/1.2 - 15 + 15);
181 |
182 | ctx.lineTo(w*(2/4) + 10, h/1.2 - 15 + 15);
183 | ctx.lineTo(w*(2/4) + 10, h/1.2 - 15);
184 |
185 | ctx.fillStyle = '#222';
186 | ctx.closePath();
187 | ctx.fill();
188 | }
189 |
190 | // Play button
191 | ctx.beginPath();
192 | ctx.arc(w/2, h/1.6, 50, 0, 2 * Math.PI, false);
193 | ctx.fillStyle = '#793f02';
194 | ctx.closePath();
195 | ctx.fill();
196 |
197 | var tw = 20, th = h/1.6 - tw;
198 | ctx.beginPath();
199 | ctx.moveTo(w/2 - tw/2, th);
200 | ctx.lineTo(w/2 + tw, th + 20);
201 | ctx.lineTo(w/2 - tw/2, th + 40);
202 | ctx.fillStyle = '#fff';
203 | ctx.closePath();
204 | ctx.fill();
205 | } else {
206 | // back button
207 | var hFactor = G.isMobile() ? 10 : 4.4;
208 |
209 | ctx.beginPath();
210 | ctx.arc(w/10, h/hFactor, 30, 0, 2 * Math.PI, false);
211 | ctx.fillStyle = '#555';
212 | ctx.closePath();
213 | ctx.fill();
214 |
215 | ctx.beginPath();
216 | ctx.moveTo(w/10, h/hFactor-5);
217 | ctx.lineTo(w/10, h/hFactor-5 - 10);
218 | ctx.lineTo(w/10 - 20, h/hFactor-5 + 5);
219 | ctx.lineTo(w/10, h/hFactor-5 + 20);
220 | ctx.lineTo(w/10, h/hFactor-5 + 10);
221 | ctx.lineTo(w/10 + 20, h/hFactor-5 + 10);
222 | ctx.lineTo(w/10 + 20, h/hFactor-5);
223 | ctx.closePath();
224 | ctx.fillStyle = '#000';
225 | ctx.fill();
226 |
227 | // show info
228 | var instructionLines = [
229 | 'Save our planet Earth!',
230 | 'Protect Forest! Don\'t burn them!',
231 | 'Abrupt climatic changes. Time to worry!',
232 | 'Extinguish fire on trees.',
233 | 'Hit spacebar or tap to jump player.',
234 | 'Earn Karma! Nature will show her love!',
235 | 'JS13KGames 16 - hidden glitches',
236 | 'Climate Abnormalities, Player Loves Trees',
237 | '(Player struggles to jump off tree)',
238 | 'More hinderances once speed > 1.6 mph'
239 | ];
240 | ctx.font = G.isMobile() ? '15px Helvetica' : '20px Helvetica';
241 | ctx.fillStyle = '#fff';
242 | for (var l = 0; l < instructionLines.length; l++) {
243 | var line = instructionLines[l];
244 | var hOffset = G.isMobile() ? l*40 : l*45;
245 | if (l === 0 || l === 2 || l === 4 || l === 6) {
246 | ctx.beginPath();
247 | ctx.arc(w / 10, h/2.6 + hOffset, 10, 0, 2*Math.PI, false);
248 | ctx.fill();
249 | ctx.closePath();
250 | }
251 | ctx.fillText(line, w/10 + (G.isMobile() ? 25: 50), h/2.6 + hOffset);
252 | }
253 | }
254 | rs();
255 | return canvas;
256 | },
257 | process: function () {
258 | sv();
259 | // cooldown factor
260 | ctx.globalAlpha = 0.35;
261 | ctx.globalCompositeOperation = 'source-over';
262 | // movement speed of cooldown map
263 | MN.y = (MN.y + 3) % MN.noise.height;
264 | // flickering of cooldown map
265 | x = Math.round(Math.random() * 5) * 0;
266 | ctx.drawImage(MN.noise, x, MN.y);
267 | ctx.drawImage(MN.noise, x, MN.y - MN.noise.height);
268 |
269 | // spread of the flame
270 | ctx.globalAlpha = 1.0;
271 | // whind
272 | x = 1 - Math.random() * 2;
273 | // move flame up
274 | ctx.drawImage(G.can, x, -1);
275 | ctx.globalAlpha = 0.13;
276 | ctx.globalCompositeOperation = 'lighter';
277 | ctx.drawImage(G.can, x, -1);
278 |
279 | // heat it up
280 | ctx.globalAlpha = 0.22;
281 | ctx.drawImage(MN.heat, 0, 0);
282 | fs(MN.fireColor);
283 | bp();
284 | ctx.globalAlpha = 0.52;
285 | cp();
286 | fl();
287 | rs();
288 | },
289 | mouseDown: function (e, x, y) {
290 | var w = G.can.width,
291 | h = G.can.height,
292 | ctx = MN.heat.getContext('2d');
293 |
294 | var hFactor = G.isMobile() ? 10 : 4.4;
295 |
296 | if (x >= w/2 - 50 && x <= w/2 + 50 &&
297 | y >= h/1.6 - 50 && y <= h/1.6 + 50) {
298 | // play btn clicked
299 | G.menu = null;
300 | G.restart();
301 | SU.play('playGame');
302 | } else if (x >= w*(2/4) - 30 && x <= w*(2/4) + 30 &&
303 | y >= h/1.2 - 30 && y <= h/1.2 + 30) {
304 | // download clicked
305 | downloadCanvas();
306 | SU.play('download');
307 | } else if (x >= w*(1/4) - 30 && x <= w*(1/4) + 30 &&
308 | y >= h/1.2 - 30 && y <= h/1.2 + 30) {
309 | // sound clicked
310 | G.isSound = +(!G.isSound);
311 | G.isSound && SU.play('soundOn');
312 | utils.setLocalStorageData(G.isSound, true);
313 | MN.heat = MN.getHeatMap();
314 | } else if (x >= w*(3/4) - 30 && x <= w*(3/4) + 30 &&
315 | y >= h/1.2 - 30 && y <= h/1.2 + 30) {
316 | // info clicked
317 | G.isInfoMenu = true;
318 | MN.heat = MN.getHeatMap();
319 | SU.play('info');
320 | } else if (x >= w*(1/10) - 30 && x <= w*(1/10) + 30 &&
321 | y >= h/hFactor - 30 && y <= h/hFactor + 30) {
322 | // back btn clicked
323 | G.isInfoMenu = false;
324 | MN.heat = MN.getHeatMap();
325 | SU.play('info');
326 | }
327 | },
328 | update: function () {
329 | // this.noise = MN.getNoise(G.can.width, G.can.height * 8);
330 | MN.process();
331 | }
332 | };
333 |
--------------------------------------------------------------------------------
/dist/game.min.js:
--------------------------------------------------------------------------------
1 | function J(){this.B=function(t){for(var e=0;24>e;e++)this[String.fromCharCode(97+e)]=t[e]||0;.01>this.c&&(this.c=.01),t=this.b+this.c+this.e,.18>t&&(t=.18/t,this.b*=t,this.c*=t,this.e*=t)}}function Menu(){MN=this,this.y=0,this.font="50px Helvetica",this.fireColor="rgb(255, 56, 8)",ctx.fillStyle="#fff",ctx.fillRect(0,0,G.can.width,G.can.height),this.heat=MN.getHeatMap(),this.noise=null,this.noise=MN.getNoise(G.can.width,8*G.can.height),ctx.drawImage(this.heat,0,0),this.update()}function Cloud(){this.color="blue",this.x=G.can.width||P.w,this.y=100,this.speed=7,this.update()}function SunMoon(){SM=this,this.isSun=!0,this.r=20,this.x=0,this.y=100,this.speed=1,G.period="morning",this.update()}function WindParticle(t){this.x=G.can.width+utils.getRandomInt(0,G.can.width),this.y=(t+1)*WD.pDist,this.color="#d1e5ff",this.speed=utils.getRandomInt(1,WD.speed)}function Wind(){WD=this,this.speed=1,this.particlesCount=15,this.particles=[],this.pDist=10,this.create()}function Droplet(t){this.x=RN.topDropletsDist*t,this.y=0,this.color="#d1e5ff",this.speed=utils.getRandomInt(10,30)}function Rain(){RN=this,this.particles=[],this.particlesCount=33,this.topDroplets=1.5*this.particlesCount,this.rightDroplets=this.particlesCount/3,this.topDropletsDist=G.can.width/this.topDroplets*2,this.rightDropletsDist=G.can.width/this.rightDroplets*2,this.create()}function Weather(){this.colors=[[255,255,255],[142,214,255],[255,254,210],[153,153,153],[20,20,20],[20,20,20]],this.step=0,this.i=0,this.colorIndices=[0,1,2,3],thisWeather=this,CC=document.getElementById("canvascontainer").style,this.init()}function Particles(t,e){PS=this,this.x=t-10,this.y=e,this.vyL1=3,this.vyL2=2,this.vyL3=1,this.finished=!1,this.particles=[],this.diff=0,this.draw()}function Player(){return Pl=this,Pl.liesOn=0,Pl.maxH=66,Pl.bounceHeight=5,Pl.w=20,Pl.h=Pl.maxH,Pl.x=G.trees[0].x,Pl.y=G.trees[0].y-Pl.h,Pl.vel=0,Pl.isJet=!1,Pl.isRest=!0,Pl.t=new Date,Pl.update(),Pl}function Tree(t){return T=this,t=t||{},T.minW=10,T.maxW=80,T.minH=P.fireOffset,T.maxH=G.isMobile()?300:400,T.minDist=50,T.maxDist=G.isMobile()?100:200,CC.w=utils.pI(G.can.width),CC.h=utils.pI(G.can.height),T.color="#a77b44",this.add(),t.isNoFlame||(G.isMobile()?this.flame=!0:(this.flame=smoky,this.flame.addEntity(Flame))),T}function Game(){G=this,G.isInProgress=!0,G.canSpeedBeIncreased=G.canExplode=!0,G.backgroundColor="#fff",G.karma=0,G.highscore=utils.getLocalStorageData()||0,G.isSound=utils.getLocalStorageData(!0),0!==G.isSound&&(G.isSound=1),G.resolution=1,G.curPos=[],G.can=document.querySelector("canvas"),G.can.width=P.w,G.can.height=P.h,ctx=G.ctx=window.c=G.can.getContext("2d"),G.trees=[],G.resize(),addEventListener("resize",G.resize,!1),CC=document.getElementById("canvascontainer").style,document.body.addEventListener("touchstart",G.touchStart.bind(G),!1),document.body.addEventListener("touchmove",G.touchMove.bind(G),!1),document.body.addEventListener("touchend",G.touchEnd.bind(G),!1),document.body.addEventListener("mousedown",G.mouseDown.bind(G),!1),document.body.addEventListener("mousemove",G.mouseMove.bind(G),!1),document.body.addEventListener("mouseup",G.mouseUp.bind(G),!1),document.body.addEventListener("keydown",G.keyDown.bind(G),!1),document.body.addEventListener("keyup",G.keyUp.bind(G),!1),G.frameCount=0,G.lastFrame=G.frameCountStart=Date.now();var t=_.innerWidth*_.innerHeight*_.devicePixelRatio,e=P.w*P.h,i=t/e;i<.5&&G.setResolution(2*i),G.speed=1,flameBack.canvas=G.can,G.menu=!0}function canvasToImage(){G.dataURL=document.getElementById("game-canvas").toDataURL("image/png")}function downloadCanvas(){var t=_.open();t?t.document.write('
'):alert("Your browser prevented the window from opening. Please allow to view game screenshot.")}navigator.vibrate=function(){return navigator.vibrate||navigator.mozVibrate||navigator.webkitVibrate||noop}();var utils={getRandomInt:function(t,e){return Math.floor(Math.random()*(e-t+1))+t},pI:function(t){return parseInt(t,10)},clamp:function(t,e,i){return"number"!=typeof e&&(e=-(1/0)),"number"!=typeof i&&(i=1/0),Math.max(e,Math.min(i,t))},getLocalStorageData:function(t){return t?utils.pI(atob(localStorage.getItem("__js13k_game_sound"))):utils.pI(atob(localStorage.getItem("__js13k_game_karma")))||0},setLocalStorageData:function(t,e){e?localStorage.setItem("__js13k_game_sound",btoa(t)):localStorage.setItem("__js13k_game_karma",btoa(t))}},W=new function(){this.A=new J;var t,e,i,a,n,r,o,s,l,h,c,d;this.reset=function(){var t=this.A;a=100/(t.f*t.f+.001),n=100/(t.g*t.g+.001),r=1-.01*t.h*t.h*t.h,o=1e-6*-t.i*t.i*t.i,t.a||(c=.5-t.n/2,d=5e-5*-t.o),s=0p.q?-1020:1020),C=p.p?(2e4*(1-p.p)*(1-p.p)|0)+32:0,T=p.d,I=p.j/2,W=.01*p.k*p.k,k=p.a,R=t,D=1/t,E=1/e,A=1/i,p=5/(1+20*p.u*p.u)*(.01+G);.8=C&&(J=0,this.reset()),h&&++l>=h&&(h=0,a*=s),r+=o,a*=r,a>n&&(a=n,0U&&(U=8),k||(c+=d,0>c?c=0:.5R)switch(_=0,++B){case 1:R=e;break;case 2:R=i}switch(B){case 0:j=_*D;break;case 1:j=1+2*(1-_*E)*T;break;case 2:j=1-_*A;break;case 3:j=0,H=!0}b&&(M+=S,N=0|M,0>N?N=-N:1023m?m=1e-5:.1=U&&(Y%=U,3==k))for(O=Z.length;O--;)Z[O]=2*Math.random()-1;switch(k){case 0:F=Y/UF?1.27323954*F+.405284735*F*F:1.27323954*F-.405284735*F*F,F=0>F?.225*(F*-F-F)+F:.225*(F*F-F)+F;break;case 3:F=Z[Math.abs(32*Y/U|0)]}P&&(O=K,G*=y,0>G?G=0:.1=L?-32768:32767*L|0}return u}};window.jsfxr=function(t){W.A.B(t);var e=W.D();t=new Uint8Array(4*((e+1)/2|0)+44);var e=2*W.C(new Uint16Array(t.buffer,44),e),i=new Uint32Array(t.buffer,0,44);i[0]=1179011410,i[1]=e+36,i[2]=1163280727,i[3]=544501094,i[4]=16,i[5]=65537,i[6]=44100,i[7]=88200,i[8]=1048578,i[9]=1635017060,i[10]=e;for(var e=e+44,i=0,a="data:audio/wav;base64,";i>18]+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[n>>12&63]+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[n>>6&63]+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[63&n]);return i-=e,a.slice(0,a.length-i)+"==".slice(0,i)};var soundUtils=SU={rd:function(t,e){return e||(e=t,t=0),Math.random()*(e-t)+t},rp:function(t){return t[~~this.rd(t.length)]},soundEffect:function(t,e){SU[t]=[],e.forEach(function(e){var i=new Audio;i.src=jsfxr(e),SU[t].push(i)})},play:function(t){G.isSound&&SU[t]&&SU.rp(SU[t]).play()}};SU.soundEffect("gameOver",[[2,.2,.01,,.83,.24,,,,.62,.6,,,.1248,.4522,,,,.4,,,,,.6]]),SU.soundEffect("moveAhead",[[2,,.2047,,.3986,.5855,.2236,-.1697,,,,,,.7882,-.2576,,,,1,,,,,.43]]),SU.soundEffect("highestScore",[[0,,.016,.4953,.3278,.6502,,,,,,.4439,.6322,,,,,,1,,,,,1]]),SU.soundEffect("explosion1",[[3,,.3729,.6547,.4138,.0496,,,,,,,,,,,,,1,,,,,.4]]),SU.soundEffect("explosion2",[[3,.43,.61,.3794,.86,.17,.17,.1399,.1,.07,.06,.04,.1,,,.96,.26,-.16,1,,,,,.15]]),SU.soundEffect("info",[[2,,.1889,,.111,.2004,,,,,,,,.1157,,,,,1,,,.1,,1]]),SU.soundEffect("soundOn",[[2,,.2,,.1753,.64,,-.5261,,,,,,.5522,-.564,,,,1,,,,,.5]]),SU.soundEffect("playGame",[[2,,.261,.2142,.2005,.4618,.0137,-.3602,,,,,,.2249,.0858,,,,1,,,1e-4,,.44]]),SU.soundEffect("glitch",[[3,,.0272,.5654,.1785,.7424,,,,,,.2984,.5495,,,,,,1,,,,,.43]]);var SF,Flame,H,PI_2,Smoke,Trail,W,SmokyFlame,drawCircle,rand,w,slice=[].slice;PI_2=2*Math.PI,rand=function(t,e){return(e-t)*Math.random()+t},drawCircle=function(t,e,i,a){return bp(),ar(t,e,i,0,PI_2,!1),ctx.fillStyle=a,fl()},Smoke=function(){function t(t,e){this.opacity=.8,this.x=t,this.y=e,this.r=2}return t.prototype.step=function(t,e,i){if(e-=utils.getRandomInt(rand(60,70),rand(200,350)),t+=rand(-2,2),this.opacity-=.04,this.opacity<=0)return this.destroyed=!0},t.prototype.draw=function(t,e,i){if(e-=utils.getRandomInt(60,150),t+=utils.getRandomInt(rand(-i+i/2,0),rand(0,i-i/2)),!(this.opacity<=0))return drawCircle(t,e,this.r,"rgba(60,60,60,"+this.opacity+")")},t}(),Trail=function(){function t(t,e){this.opacity=1,this.x=t,this.y=e,this.r=12}return t.prototype.step=function(t,e,i){if(this.r=i/5,e-=rand(0,8),t-=rand(-3,3),this.opacity-=.03,this.opacity<=0&&(this.destroyed=!0,rand(0,1)<.5))return SF.addEntity(Smoke,t,e-this.r)},t.prototype.draw=function(t,e,i){this.r=i/6,e-=rand(rand(-45,5),rand(25,75)),t-=rand(-i/2-20,i/2+20);var a,n,r,o;if(!(this.opacity<=0))return a="rgba(255,"+~~(240*this.opacity)+",0,"+this.opacity+")",n="rgba(255,"+~~(240*this.opacity)+",0,0)",o=1.5*this.r+rand(0,2),r=ctx.createRadialGradient(t,e,0,t,e,o),r.addColorStop(0,a),r.addColorStop(1,n),drawCircle(t,e,this.r,r),drawCircle(t,e,this.r*this.opacity,a)},t}(),Flame=function(){function t(){this.x=G.can.width/2,this.y=G.can.height/2+90,this.r=24,this.rg=22}return t.prototype.step=function(t,e,i){return!1},t.prototype.draw=function(t,e,i){this.g=ctx.createRadialGradient(t,e,0,t,e,1.2*i),this.g.addColorStop(0,"rgba(255,255,255,1)"),this.g.addColorStop(1,"rgba(255,120,0,0)");var a;return SF.addEntity(Trail,t,e-this.r/3),a=ctx.createRadialGradient(t,e,0,t,e,this.rg),a.addColorStop(0,"rgba(255,180,0,"+rand(.2,.9)+")"),a.addColorStop(1,"rgba(255,180,0,0)"),drawCircle(t,e,this.rg,a),drawCircle(t+rand(-1.5,1.5),e+rand(-1.5,1.5),i,this.g)},t}(),SmokyFlame=function(){function t(){SF=this,this.entities={},this.i=0,this.ii=0}return t.prototype.addEntity=function(){var t,e;return e=arguments[0],t=2<=arguments.length?slice.call(arguments,1):[],this.entities[this.i]=function(t,e,i){i.prototype=t.prototype;var a=new i,n=t.apply(a,e);return Object(n)===n?n:a}(e,t,function(){}),this.i+=1},t.prototype.update=function(t,e,i){var a,n,r;r=this.entities;for(n in r)a=r[n],a.destroyed!==!0?(this.ii%5===0&&(a.step(t+i/2,e-10,i),a.draw(t+i/2,e-10,i),this.ii=0),this.ii++):delete this.entities[n]},t}();var MN;Menu.prototype={getNoise:function(){var t=document.createElement("canvas");t.width=G.can.width,t.height=G.can.height;for(var e=t.getContext("2d"),i=t.width,a=t.height,n=e.createImageData(i,a),r=i*a*4,o=0;o220&&(n.data[o]=255),n.data[o]<40&&(n.data[o]=0);return e.putImageData(n,0,0),rs(),t},getHeatMap:function(){var t=document.createElement("canvas");t.width=G.can.width,t.height=G.can.height;var e=t.getContext("2d");sv();var i=G.can.width,a=G.can.height,n=MN.fireColor,r=G.isGameOver?"GAME":"SAVE",o=G.isGameOver?"OVER":"THE";thirdText=G.isGameOver?"":"FOREST",G.isMobile()?(r=r.split("").join(" "),o=o.split("").join(" "),thirdText=thirdText.split("").join(" ")):(r=r.split("").join(" "),o=o.split("").join(" "),thirdText=thirdText.split("").join(" ")),e.fillStyle=n,e.strokeStyle=n,e.font=MN.font;var s=e.measureText(r),l=e.measureText(o),h=e.measureText(thirdText);if(e.fillText(r,(i-s.width)/2,a/6),e.fillText(o,(i-l.width)/2,a/4),e.fillText(thirdText,(i-h.width)/2,a/3),e.lineWidth=10,G.isInfoMenu){var c=G.isMobile()?10:4.4;e.beginPath(),e.arc(i/10,a/c,30,0,2*Math.PI,!1),e.fillStyle="#555",e.closePath(),e.fill(),e.beginPath(),e.moveTo(i/10,a/c-5),e.lineTo(i/10,a/c-5-10),e.lineTo(i/10-20,a/c-5+5),e.lineTo(i/10,a/c-5+20),e.lineTo(i/10,a/c-5+10),e.lineTo(i/10+20,a/c-5+10),e.lineTo(i/10+20,a/c-5),e.closePath(),e.fillStyle="#000",e.fill();var d=["Save our planet Earth!","Protect Forest! Don't burn them!","Abrupt climatic changes. Time to worry!","Extinguish fire on trees.","Hit spacebar or tap to jump player.","Earn Karma! Nature will show her love!","JS13KGames 16 - hidden glitches","Climate Abnormalities, Player Loves Trees","(Player struggles to jump off tree)","More hinderances once speed > 1.6 mph"];e.font=G.isMobile()?"15px Helvetica":"20px Helvetica",e.fillStyle="#fff";for(var f=0;f=a/2-50&&e<=a/2+50&&i>=n/1.6-50&&i<=n/1.6+50?(G.menu=null,G.restart(),SU.play("playGame")):e>=.5*a-30&&e<=.5*a+30&&i>=n/1.2-30&&i<=n/1.2+30?(downloadCanvas(),SU.play("download")):e>=.25*a-30&&e<=.25*a+30&&i>=n/1.2-30&&i<=n/1.2+30?(G.isSound=+!G.isSound,G.isSound&&SU.play("soundOn"),utils.setLocalStorageData(G.isSound,!0),MN.heat=MN.getHeatMap()):e>=.75*a-30&&e<=.75*a+30&&i>=n/1.2-30&&i<=n/1.2+30?(G.isInfoMenu=!0,MN.heat=MN.getHeatMap(),SU.play("info")):e>=.1*a-30&&e<=.1*a+30&&i>=n/r-30&&i<=n/r+30&&(G.isInfoMenu=!1,MN.heat=MN.getHeatMap(),SU.play("info"))},update:function(){MN.process()}};var CC,RN,WD,SM,cloud,sunMoon,wind,rain,diffInWeatherTime=5;Cloud.prototype={drawArcs:function(t,e,i,a){bp(),mt(t/i+150,e-15),qct(t/i+150+50,e-15+0,t/i+150+40,e-15+40),ctx.lineWidth=4,sts(thisWeather.hexToRgb(thisWeather.getColor(),.8)),st(),bp(),mt(t/i+20+30,e+10),qct(t/i+30,e+35+10,t/i+30+60,e+35+15),st()},draw:function(t,e,i,a){var n=thisWeather.hexToRgb(thisWeather.getColor(),.5);ctx.scale(i,a),bp(),fs(n),mt(t/i,e),bct(t/i-40,e+20,t/i-40,e+70,t/i+60,e+70),bct(t/i+80,e+100,t/i+150,e+100,t/i+170,e+70),bct(t/i+250,e+70,t/i+250,e+40,t/i+220,e+20),bct(t/i+260,e-40,t/i+200,e-50,t/i+170,e-30),bct(t/i+150,e-75,t/i+80,e-60,t/i+80,e-30),bct(t/i+30,e-75,t/i-20,e-60,t/i,e),cp(),ctx.shadowColor=thisWeather.hexToRgb(thisWeather.getColor(),.8),ctx.shadowOffsetX=-3,ctx.shadowOffsetY=3,ctx.shadowBlur=10,ctx.lineWidth=3,sts(thisWeather.hexToRgb(thisWeather.getColor(),.8)),st(),fl(),this.drawArcs(t,e,i,a)},update:function(){this.x-=this.speed,this.x+250<0&&(this.x=CC.w+250,this.y=this.y+utils.getRandomInt(-10,10));var t=this.x,e=this.y;sv(),this.draw(t,e,.8,.7),this.draw(t,e,.7,.6),this.draw(t,e,.6,.6),this.draw(t,e,.5,.7),rs()}},SunMoon.prototype={getColor:function(){var t;switch(G.period){case"morning":t=this.isSun?"#ffff9e":"#fff";break;case"afternoon":t=this.isSun?"yellow":"#fff";break;case"evening":t=this.isSun?"#e28800":"#fff";break;case"night":this.isSun,t="#fff"}return t},resetPos:function(){this.x=0,this.y=100,G.period="morning",thisWeather.step=0},update:function(){return Weather.dt/1e3%(5*diffInWeatherTime)>5*diffInWeatherTime||Weather.dt/1e3%(5*diffInWeatherTime)>4*diffInWeatherTime||Weather.dt/1e3%(5*diffInWeatherTime)>3*diffInWeatherTime?G.period="night":Weather.dt/1e3%(5*diffInWeatherTime)>2*diffInWeatherTime?G.period="evening":Weather.dt/1e3%(5*diffInWeatherTime)>1*diffInWeatherTime?G.period="afternoon":G.period="morning",this.x+=G.can.width/(2*diffInWeatherTime)/fps,this.x>G.can.width?(this.resetPos(),void(this.isSun=!this.isSun)):(this.y-=.1,sv(),ctx.shadowColor=this.getColor(),ctx.shadowOffsetX=-3,ctx.shadowOffsetY=3,ctx.shadowBlur=10,bp(),ar(this.x,this.y,this.r,0,2*Math.PI,!0),cp(),ctx.fillStyle=this.getColor(),fl(),rs(),sv(),bp(),ctx.fillStyle=thisWeather.hexToRgb("#444",.5),this.isSun||ar(this.x+5,this.y-5,20,0,2*Math.PI,!0),cp(),fl(),rs(),void thisWeather.updateGradient())}},Wind.prototype={create:function(){for(var t=0;tCC.h&&(RN.particles[t]=new Droplet(t))}}},Weather.prototype={updateGradient:function(){var t=thisWeather.colors[thisWeather.colorIndices[0]],e=thisWeather.colors[thisWeather.colorIndices[1]],i=(thisWeather.colors[thisWeather.colorIndices[2]],thisWeather.colors[thisWeather.colorIndices[3]],1-thisWeather.step),a=Math.round(i*t[0]+thisWeather.step*e[0]),n=Math.round(i*t[1]+thisWeather.step*e[1]),r=Math.round(i*t[2]+thisWeather.step*e[2]),o="rgb("+a+","+n+","+r+")",s=Math.round(255*i+255*thisWeather.step),l=Math.round(255*i+255*thisWeather.step),h=Math.round(255*i+255*thisWeather.step),c="rgb("+s+","+l+","+h+")",d=ctx.createLinearGradient(0,0,0,G.can.height);if(d.addColorStop(0,o),d.addColorStop(.9,c),G.backgroundColor=d,thisWeather.step+=SM.isSun?.0076:.0076/2.22,thisWeather.step>=1){thisWeather.step=0;for(var f=0;fCC.h-P.fireOffset&&(PS.finished=!0),PS.x-=G.speed}};var flameBack=new function(){var t,e,i,a,n,r,o,s,l=2,h=2.5,c=5;this.time=new Date,this.canvas=void 0,this.init=function(){t=this.canvas.getContext("2d"),o=(this.canvas.width+30)/l,s=P.fireOffset/l,o=Math.ceil(o),s=Math.ceil(s),r=Array(o*s);for(var e=0;e=1;t--)for(var e=s;e--;){var i=r[y(t-1,e-1)]+r[y(t,e-1)]+r[y(t+1,e-1)]+r[y(t-1,e)]+r[y(t+1,e)]+r[y(t-1,e+1)]+r[y(t,e+1)]+r[y(t+1,e+1)]>>3;i=Math.max(0,i-g(h)),r[y(t,e-1)]=i,e2500?++Pl.isBlink:0,t=Pl.isBlink>5?"#000":"#fff",Pl.isBlink>=8&&(Pl.t=e,Pl.isBlink=0),bp(t),Pl.died?(ar(Pl.x-2*(Pl.h/3)+6,Pl.y+4,2,0,2*M.PI,!0),ar(Pl.x-2*(Pl.h/3)+6,Pl.y+10,2,0,2*M.PI,!0)):(ar(Pl.x+2*(Pl.w/3)-2,Pl.y+5,2,0,2*M.PI,!0),ar(Pl.x+(Pl.w-3),Pl.y+5,2,0,2*M.PI,!0)),ctx.fillStyle=t,fl()}},legs:function(){var t=3;fs("#000"),Pl.died?(fr(Pl.x,Pl.y+Pl.w/3,Pl.h/3,t),fr(Pl.x,Pl.y+2*(Pl.w/3),Pl.h/3,t)):(fr(Pl.x+Pl.w/3-t/2,Pl.y+2*(Pl.h/3),t,Pl.h/3),fr(Pl.x+2*(Pl.w/3)-t/2,Pl.y+2*(Pl.h/3),t,Pl.h/3))},fe:function(){fs("#eaeaea"),bp(),el(ctx,Pl.x-30,Pl.y+Pl.h,80,10,"#000"),cp(),fs("#EAEAEA"),fl(),ctx.lineWidth=1,sts("#dedede"),st(),fs("#8ED6FF");for(var t=-30;t0?(Pl.y-=Pl.bounceHeight,Pl.x+=1.5*G.speed,Pl.bounceFactor-=2):Pl.isInAir&&!Pl.bounceFactor?(Pl.y-=Pl.bounceHeight/2,Pl.x+=1.5*G.speed,Pl.bounceFactor-=2):Pl.isInAir&&Pl.bounceFactor===-2?(Pl.y-=0,Pl.x+=1.5*G.speed,Pl.bounceFactor-=2):Pl.isInAir&&Pl.bounceFactor===-4?(Pl.y-=Pl.bounceHeight/2,Pl.x+=1.5*G.speed,Pl.bounceFactor-=2):Pl.isInAir&&Pl.bounceFactor===-6&&(Pl.y+=Pl.bounceHeight,Pl.x+=1.5*G.speed,Pl.bounceFactor=-6),Pl.died&&!Pl.busted&&(Pl.y+=10,Pl.isCornerStrike?Pl.y>CC.h-3*P.fireOffset&&(Pl.busted||(Pl.busted=!0)):Pl.busted||(Pl.busted=!0)),fs("#000"),Pl.body(),Pl.legs(),Pl.eyes(),Pl.tears(2,6),Pl.tears(5,15),Pl.isInAir&&!Pl.busted&&Pl.fe(),Pl.busted&&Pl.burst(),Pl.checkCollision()},keyDown:function(t){if(!Pl.busted){if(32===t){if(Pl.irj=!0,Pl.h<50)return;Pl.h-=2,Pl.y+=2}else 39===t&&(Pl.x+=G.speed);Pl.irj&&(Pl.irj=!1,Pl.isInAir=!0,Pl.isKarmaLocked=!1,Pl.bounceFactor=Pl.maxH-Pl.h,Pl.bounceFactor*=4,Pl.h=Pl.maxH)}},keyUp:function(){},checkCollision:function(){if(Pl.x<=0)return Pl.died=!0,void(Pl.isCornerStrike=!0);if(Pl.y>CC.h-P.fireOffset)return Pl.died=!0,void(Pl.isCornerStrike=!0);if(Pl.x+Pl.w>CC.w)return Pl.died=!0,void(Pl.isCornerStrike=!0);if(Pl.y<0)return Pl.died=!0,void(Pl.isCornerStrike=!0);var t,e;for(t=0;t=e.x&&i=e.x){for(var a=0;a=e.y){G.trees[t].flame=null,Pl.isInAir=!1,Pl.liesOn=t,Pl.isKarmaLocked||Pl.liesOn===Pl.lastLiesOn||(G.karma&&SU.play("moveAhead"),G.karma+=1),Pl.isKarmaLocked=!0,Pl.lastLiesOn=Pl.liesOn;break}if(Pl.y>=e.y&&Pl.yG.highscore&&(SU.play("highestScore"),G.highscore=G.karma,utils.setLocalStorageData(G.karma)),SU.play("gameOver"),G.menu=new Menu},cycle:function(){var t=(new Date).getTime();if(dt=t-time,!(dt<1e3/fps)){if(time=t,G.menu)return void(G.menu.update&&G.menu.update());G.canExplode&&M.ceil((t-G.gameStartTime)/1e3)%6===0?(G.mildExplosion?SU.play("explosion2"):SU.play("explosion1"),G.mildExplosion=!G.mildExplosion,G.canExplode=!1):M.ceil((t-G.gameStartTime)/1e3)%7===0&&(G.canExplode=!0),G.canSpeedBeIncreased&&M.ceil((t-G.gameStartTime)/1e3)%10===0?(G.speed+=G.isMobile()?.1:.2,WD.speed=utils.getRandomInt(1,30),G.canSpeedBeIncreased=!1):M.ceil((t-G.gameStartTime)/1e3)%11===0&&(G.canSpeedBeIncreased=!0),fs(G.backgroundColor),fr(0,0,CC.w,CC.h);var e=G.isMobile()?1.1:1.6;if(G.speed>=e&&10===utils.getRandomInt(0,10)&&(G.showNoisyScreen(),4===utils.getRandomInt(0,10)&&SU.play("glitch")),weather.update(),ctx.font="15px Comic Sans",ctx.fillStyle=thisWeather.hexToRgb(thisWeather.getColor(!0),1),ctx.fillText("KARMA: "+G.karma,25,25),ctx.fillText("SPEED: "+G.speed.toFixed(1)+" mph",G.can.width-130,25),ctx.fillText("WIND: "+WD.speed.toFixed(1)+" mph W",G.can.width-130,45),ctx.lineWidth=3,G.trees.length){for(var i=0;if;f++)this[String.fromCharCode(97+f)]=e[f]||0;0.01>this.c&&(this.c=0.01);e=this.b+this.c+this.e;0.18>e&&(e=0.18/e,this.b*=e,this.c*=e,this.e*=e)}}
68 | var W=new function(){this.A=new J;var e,f,d,g,l,z,K,L,M,A,m,N;this.reset=function(){var c=this.A;g=100/(c.f*c.f+0.001);l=100/(c.g*c.g+0.001);z=1-0.01*c.h*c.h*c.h;K=1E-6*-c.i*c.i*c.i;c.a||(m=0.5-c.n/2,N=5E-5*-c.o);L=0a.q?-1020:1020),S=a.p?(2E4*(1-a.p)*(1-a.p)|0)+32:0,ba=a.d,T=a.j/2,ca=0.01*a.k*a.k,E=a.a,F=e,da=1/e,ea=1/f,fa=1/d,a=5/(1+20*a.u*a.u)*(0.01+n);0.8=S&&(V=0,this.reset());A&&++M>=A&&(A=0,g*=L);z+=K;g*=z;g>l&&(g=l,0<$&&(G=!0));h=g;0<
70 | T&&(I+=ca,h*=1+Math.sin(I)*T);h|=0;8>h&&(h=8);E||(m+=N,0>m?m=0:0.5F)switch(v=0,++U){case 1:F=f;break;case 2:F=d}switch(U){case 0:w=v*da;break;case 1:w=1+2*(1-v*ea)*ba;break;case 2:w=1-v*fa;break;case 3:w=0,G=!0}R&&(D+=aa,s=D|0,0>s?s=-s:1023r?r=1E-5:0.1=h&&(p%=h,3==E))for(x=y.length;x--;)y[x]=2*Math.random()-1;switch(E){case 0:b=p/hb?1.27323954*b+0.405284735*b*b:1.27323954*b-0.405284735*b*b;b=0>b?0.225*(b*-b-b)+b:0.225*(b*b-b)+b;break;case 3:b=y[Math.abs(32*p/h|0)]}P&&(x=u,n*=X,0>n?n=0:0.1=q?-32768:32767*q|0}return O}};
72 | window.jsfxr=function(e){W.A.B(e);var f=W.D();e=new Uint8Array(4*((f+1)/2|0)+44);var f=2*W.C(new Uint16Array(e.buffer,44),f),d=new Uint32Array(e.buffer,0,44);d[0]=1179011410;d[1]=f+36;d[2]=1163280727;d[3]=544501094;d[4]=16;d[5]=65537;d[6]=44100;d[7]=88200;d[8]=1048578;d[9]=1635017060;d[10]=f;for(var f=f+44,d=0,g="data:audio/wav;base64,";d>18]+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[l>>
73 | 12&63]+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[l>>6&63]+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[l&63]);d-=f;return g.slice(0,g.length-d)+"==".slice(0,d)};
74 | // For sound Effects
75 | var soundUtils = SU = {
76 | rd: function (a, b){
77 | if(!b){
78 | b = a;
79 | a = 0;
80 | }
81 | return Math.random() * (b - a) + a;
82 | },
83 | rp: function (a){
84 | return a[~~this.rd(a.length)];
85 | },
86 | soundEffect: function(sid, settings){
87 | SU[sid] = [];
88 |
89 | settings.forEach(function(sound){
90 | var audio = new Audio();
91 | audio.src = jsfxr(sound);
92 |
93 | SU[sid].push(audio);
94 | });
95 | },
96 | play: function(sid) {
97 | if (!G.isSound) {
98 | return;
99 | }
100 | SU[sid] && SU.rp(SU[sid]).play();
101 | }
102 | };
103 |
104 |
105 | SU.soundEffect('gameOver', [
106 | [2,0.2,0.01,,0.83,0.24,,,,0.62,0.6,,,0.1248,0.4522,,,,0.4,,,,,0.6]
107 | ]);
108 | SU.soundEffect('moveAhead', [
109 | [2,,0.2047,,0.3986,0.5855,0.2236,-0.1697,,,,,,0.7882,-0.2576,,,,1,,,,,0.43]
110 | ]);
111 | SU.soundEffect('highestScore', [
112 | [0,,0.016,0.4953,0.3278,0.6502,,,,,,0.4439,0.6322,,,,,,1,,,,,1]
113 | ]);
114 | SU.soundEffect('explosion1', [
115 | [3,,0.3729,0.6547,0.4138,0.0496,,,,,,,,,,,,,1,,,,,0.4]
116 | ]);
117 | SU.soundEffect('explosion2', [
118 | [3,0.43,0.61,0.3794,0.86,0.17,0.17,0.1399,0.1,0.07,0.06,0.04,0.1,,,0.96,0.26,-0.16,1,,,,,0.15]
119 | ]);
120 | SU.soundEffect('info', [
121 | [2,,0.1889,,0.111,0.2004,,,,,,,,0.1157,,,,,1,,,0.1,,1]
122 | ]);
123 | SU.soundEffect('soundOn', [
124 | [2,,0.2,,0.1753,0.64,,-0.5261,,,,,,0.5522,-0.564,,,,1,,,,,0.5]
125 | ]);
126 | SU.soundEffect('playGame', [
127 | [2,,0.261,0.2142,0.2005,0.4618,0.0137,-0.3602,,,,,,0.2249,0.0858,,,,1,,,0.0001,,0.44]
128 | ]);
129 | SU.soundEffect('glitch', [
130 | [3,,0.0272,0.5654,0.1785,0.7424,,,,,,0.2984,0.5495,,,,,,1,,,,,0.43]
131 | ])
132 | var SF, Flame, H, PI_2, Smoke, Trail, W, SmokyFlame, drawCircle, rand, w, slice = [].slice;
133 |
134 | PI_2 = 2 * Math.PI;
135 | rand = function (a, b) {
136 | return (b - a) * Math.random() + a;
137 | };
138 | drawCircle = function (x, y, r, style) {
139 | bp();
140 | ar(x, y, r, 0, PI_2, false);
141 | ctx.fillStyle = style;
142 | return fl();
143 | };
144 |
145 | Smoke = function () {
146 | function Smoke(x, y) {
147 | this.opacity = 0.8;
148 | this.x = x;
149 | this.y = y;
150 | this.r = 2.0;
151 | }
152 | Smoke.prototype.step = function (x, y, w) {
153 | y -= utils.getRandomInt(rand(60,70), rand(200,350))
154 | // y -= rand(0, 3);
155 | x += rand(-2, 2);
156 | this.opacity -= 0.04;
157 | if (this.opacity <= 0) {
158 | return this.destroyed = true;
159 | }
160 | };
161 | Smoke.prototype.draw = function (x, y, w) {
162 | y -= utils.getRandomInt(60, 150)
163 | x += utils.getRandomInt(rand(-w+w/2, 0), rand(0,w-w/2))
164 | if (this.opacity <= 0) {
165 | return;
166 | }
167 | return drawCircle(x, y, this.r, 'rgba(60,60,60,' + this.opacity + ')');
168 | };
169 | return Smoke;
170 | }();
171 | Trail = function () {
172 | function Trail(x, y) {
173 | this.opacity = 1;
174 | this.x = x;
175 | this.y = y;
176 | this.r = 12;
177 | }
178 | Trail.prototype.step = function (x, y, w) {
179 | this.r = w / 5;
180 | y -= rand(0, 8);
181 | x -= rand(-3, 3);
182 | this.opacity -= 0.03;
183 | if (this.opacity <= 0) {
184 | this.destroyed = true;
185 | if (rand(0, 1) < 0.5) {
186 | return SF.addEntity(Smoke, x, y - this.r);
187 | }
188 | }
189 | };
190 | Trail.prototype.draw = function (x, y, w) {
191 | this.r = w / 6;
192 | y -= rand(rand(-45, 5), rand(25, 75));
193 | x -= rand(-w/2 - 20, w/2 + 20);
194 | var color, color2, g, rg;
195 | if (this.opacity <= 0) {
196 | return;
197 | }
198 | color = 'rgba(255,' + ~~(240 * this.opacity) + ',0,' + this.opacity + ')';
199 | color2 = 'rgba(255,' + ~~(240 * this.opacity) + ',0,0)';
200 | rg = this.r * 1.5 + rand(0, 2);
201 | g = ctx.createRadialGradient(x, y, 0, x, y, rg);
202 | g.addColorStop(0, color);
203 | g.addColorStop(1, color2);
204 | drawCircle(x, y, this.r, g);
205 | return drawCircle(x, y, this.r * this.opacity, color);
206 | };
207 | return Trail;
208 | }();
209 | Flame = function () {
210 | function Flame() {
211 | this.x = G.can.width / 2;
212 | this.y = G.can.height / 2 + 90;
213 | this.r = 24;
214 | this.rg = 22;
215 | }
216 | Flame.prototype.step = function (x, y, w) {
217 | return false;
218 | };
219 | Flame.prototype.draw = function (x, y, w) {
220 | this.g = ctx.createRadialGradient(x, y, 0, x, y, w * 1.2);
221 | this.g.addColorStop(0, 'rgba(255,255,255,1)');
222 | this.g.addColorStop(1, 'rgba(255,120,0,0)');
223 |
224 |
225 | var g, i, j;
226 | //for (i = j = 1; j <= 1; i = ++j) {
227 | SF.addEntity(Trail, x, y - this.r / 3);
228 | //}
229 | g = ctx.createRadialGradient(x, y, 0, x, y, this.rg);
230 | g.addColorStop(0, 'rgba(255,180,0,' + rand(0.2, 0.9) + ')');
231 | g.addColorStop(1, 'rgba(255,180,0,0)');
232 | drawCircle(x, y, this.rg, g);
233 | return drawCircle(x + rand(-1.5, 1.5), y + rand(-1.5, 1.5), w, this.g );
234 | };
235 | return Flame;
236 | }();
237 |
238 | SmokyFlame = function () {
239 | function SmokyFlame() {
240 | SF = this;
241 | this.entities = {};
242 | this.i = 0;
243 | this.ii = 0;
244 | // this.update();
245 | }
246 | SmokyFlame.prototype.addEntity = function () {
247 | var args, klass;
248 | klass = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
249 | this.entities[this.i] = function (func, args, ctor) {
250 | ctor.prototype = func.prototype;
251 | var child = new ctor(), result = func.apply(child, args);
252 | return Object(result) === result ? result : child;
253 | }(klass, args, function () {
254 | });
255 | return this.i += 1;
256 | };
257 | SmokyFlame.prototype.update = function (x, y, w) {
258 | var entity, k, ref;
259 | // ctx.clearRect(0, 0, W, H);
260 | ref = this.entities;
261 | for (k in ref) {
262 | entity = ref[k];
263 | if (entity.destroyed === true) {
264 | delete this.entities[k];
265 | continue;
266 | }
267 |
268 | if (this.ii % 5 === 0) {
269 | entity.step(x + (w / 2), y - 10, w);
270 | entity.draw(x + (w / 2), y - 10, w);
271 | this.ii = 0;
272 | }
273 | this.ii++;
274 | }
275 | };
276 | return SmokyFlame;
277 | }();
278 | var MN;
279 | function Menu() {
280 | MN = this;
281 | this.y = 0;
282 | this.font = '50px Helvetica';
283 | this.fireColor = 'rgb(255, 56, 8)';
284 |
285 | ctx.fillStyle = '#fff'
286 | ctx.fillRect(0, 0, G.can.width, G.can.height);
287 |
288 | this.heat = MN.getHeatMap();
289 | this.noise = null;
290 | this.noise = MN.getNoise(G.can.width, G.can.height*8);
291 | ctx.drawImage(this.heat, 0, 0);
292 | this.update();
293 | }
294 | Menu.prototype = {
295 | getNoise: function () {
296 | var canvas = document.createElement('canvas');
297 | canvas.width = G.can.width;
298 | canvas.height = G.can.height;
299 | var ctx = canvas.getContext('2d');
300 |
301 | var w = canvas.width, h = canvas.height,
302 | img = ctx.createImageData(w, h),
303 | n = w * h * 4;
304 |
305 | for(var i = 0; i < n; i+=4) {
306 | img.data[i] = 15;
307 | img.data[i+1] = 3;
308 | img.data[i+2] = 1;
309 | img.data[i+3] = Math.floor(Math.random() * 128);
310 | }
311 | sv();
312 | ctx.putImageData(img, 0, 0);
313 | ctx.drawImage(canvas, 0, 0, w * 64, h * 64);
314 | ctx.globalAlpha = 0.5;
315 | ctx.drawImage(canvas, 0, 0, w * 16, h * 16);
316 | var img = ctx.getImageData(0, 0, w, h);
317 | // increase contrast a bit by clamping values
318 | for (var i = 3; i < w * h * 4; i += 4){
319 | if (img.data[i] > 220){
320 | img.data[i] = 255;
321 | }
322 | if (img.data[i] < 40){
323 | img.data[i] = 0;
324 | }
325 | }
326 | ctx.putImageData(img, 0, 0);
327 | rs();
328 | return canvas;
329 | },
330 | getHeatMap: function () {
331 | var canvas = document.createElement('canvas');
332 | canvas.width = G.can.width;
333 | canvas.height = G.can.height;
334 |
335 | var ctx = canvas.getContext('2d');
336 | sv();
337 | var w = G.can.width,
338 | h = G.can.height,
339 | color = MN.fireColor,
340 | firstText = G.isGameOver ? 'GAME' : 'SAVE',
341 | secondText = G.isGameOver ? 'OVER' : 'THE';
342 | thirdText = G.isGameOver ? '' : 'FOREST';
343 |
344 | if (G.isMobile()) {
345 | firstText = firstText.split('').join(' ');
346 | secondText = secondText.split('').join(' ');
347 | thirdText = thirdText.split('').join(' ');
348 | } else {
349 | firstText = firstText.split('').join(' ');
350 | secondText = secondText.split('').join(' ');
351 | thirdText = thirdText.split('').join(' ');
352 | }
353 |
354 | ctx.fillStyle = color;
355 | ctx.strokeStyle = color;
356 | ctx.font = MN.font;
357 |
358 | var m1 = ctx.measureText(firstText);
359 | var m2 = ctx.measureText(secondText);
360 | var m3 = ctx.measureText(thirdText);
361 | ctx.fillText(firstText, (w - m1.width) / 2, h / 6);
362 | ctx.fillText(secondText, (w - m2.width) / 2, h / 4);
363 | ctx.fillText(thirdText, (w - m3.width) / 2, h / 3);
364 | ctx.lineWidth = 10;
365 |
366 | if (!G.isInfoMenu) {
367 | var highestScoreText = 'BEST: ' + G.highscore;
368 | if (G.isMobile()) {
369 | highestScoreText = highestScoreText.split('').join(' ');
370 | } else {
371 | highestScoreText = highestScoreText.split('').join(' ');
372 | }
373 | ctx.fillStyle = '#fff';
374 | ctx.font = '35px Helvetica';
375 | ctx.fillText(highestScoreText, (w - ctx.measureText(highestScoreText).width) / 2, h / 2.1);
376 |
377 | // Sound circle
378 | ctx.beginPath();
379 | ctx.arc(w*(1/4), h/1.2, 30, 0, 2 * Math.PI, false);
380 | ctx.fillStyle = '#555';
381 | ctx.closePath();
382 | ctx.fill();
383 |
384 | // Rules / Instructions circle
385 | ctx.beginPath();
386 | ctx.arc(w*(3/4), h/1.2, 30, 0, 2 * Math.PI, false);
387 | ctx.fillStyle = '#555';
388 | ctx.closePath();
389 | ctx.fill();
390 |
391 | // sound icon
392 | ctx.beginPath();
393 | ctx.moveTo(w*(1/4) - 20, h/1.2 - 10);
394 | ctx.lineTo(w*(1/4) - 20, h/1.2 + 5);
395 | ctx.lineTo(w*(1/4) - 10, h/1.2 + 5);
396 | ctx.lineTo(w*(1/4) + 5, h/1.2 + 15);
397 | ctx.lineTo(w*(1/4) + 5, h/1.2 - 20);
398 | ctx.lineTo(w*(1/4) - 10, h/1.2 - 10);
399 | ctx.fillStyle = '#222';
400 | ctx.closePath();
401 | if (G.isSound) {
402 | ctx.fillRect(w*(1/4) + 10, h/1.2 - 5, 3, 10);
403 | ctx.fillRect(w*(1/4) + 15, h/1.2 - 7, 3, 15);
404 | ctx.fillRect(w*(1/4) + 20, h/1.2 - 10, 3, 20);
405 | }
406 | ctx.fill();
407 |
408 | // if no sound, show / on icon
409 | if (!G.isSound) {
410 | ctx.save();
411 | ctx.beginPath();
412 | ctx.moveTo(w*(1/4) + 10, h/1.2 - 22);
413 | ctx.lineTo(w*(1/4) - 10, h/1.2 + 22);
414 | ctx.closePath();
415 | ctx.fill();
416 | ctx.lineWidth = 5;
417 | ctx.strokeStyle = '#000';
418 | ctx.stroke();
419 | ctx.restore();
420 | }
421 |
422 | // instructions icon
423 | ctx.fillRect(w*(3/4) - 2, h/1.2, 5, 15);
424 | ctx.beginPath();
425 | ctx.arc(w*(3/4), h/1.2 - 10, 5, 0, 2 * Math.PI, false);
426 | ctx.closePath();
427 | ctx.fillStyle = '#222';
428 | ctx.fill();
429 |
430 | if (G.isGameOver) {
431 | ctx.fillStyle = '#fff';
432 | ctx.font = '35px Helvetica';
433 | var karmaText = 'KARMA: ' + G.karma;
434 |
435 | if (G.isMobile()) {
436 | karmaText = karmaText.split('').join(' ');
437 | } else {
438 | karmaText = karmaText.split('').join(' ');
439 | }
440 |
441 | ctx.fillText(karmaText, (w - ctx.measureText(karmaText).width) / 2, h / 2.5);
442 | ctx.lineWidth = 10;
443 |
444 | ctx.beginPath();
445 | ctx.arc(w*(2/4), h/1.2, 30, 0, 2 * Math.PI, false);
446 | ctx.fillStyle = '#555';
447 | ctx.closePath();
448 | ctx.fill();
449 |
450 | // download icon
451 | ctx.beginPath();
452 | ctx.moveTo(w*(2/4) - 10, h/1.2 - 15);
453 | ctx.lineTo(w*(2/4) - 10, h/1.2 - 15 + 15);
454 | ctx.lineTo(w*(2/4) - 20, h/1.2 - 15 + 15);
455 |
456 | ctx.lineTo(w*(2/4), h/1.2 - 15 + 35);
457 | ctx.lineTo(w*(2/4) + 20, h/1.2 - 15 + 15);
458 |
459 | ctx.lineTo(w*(2/4) + 10, h/1.2 - 15 + 15);
460 | ctx.lineTo(w*(2/4) + 10, h/1.2 - 15);
461 |
462 | ctx.fillStyle = '#222';
463 | ctx.closePath();
464 | ctx.fill();
465 | }
466 |
467 | // Play button
468 | ctx.beginPath();
469 | ctx.arc(w/2, h/1.6, 50, 0, 2 * Math.PI, false);
470 | ctx.fillStyle = '#793f02';
471 | ctx.closePath();
472 | ctx.fill();
473 |
474 | var tw = 20, th = h/1.6 - tw;
475 | ctx.beginPath();
476 | ctx.moveTo(w/2 - tw/2, th);
477 | ctx.lineTo(w/2 + tw, th + 20);
478 | ctx.lineTo(w/2 - tw/2, th + 40);
479 | ctx.fillStyle = '#fff';
480 | ctx.closePath();
481 | ctx.fill();
482 | } else {
483 | // back button
484 | var hFactor = G.isMobile() ? 10 : 4.4;
485 |
486 | ctx.beginPath();
487 | ctx.arc(w/10, h/hFactor, 30, 0, 2 * Math.PI, false);
488 | ctx.fillStyle = '#555';
489 | ctx.closePath();
490 | ctx.fill();
491 |
492 | ctx.beginPath();
493 | ctx.moveTo(w/10, h/hFactor-5);
494 | ctx.lineTo(w/10, h/hFactor-5 - 10);
495 | ctx.lineTo(w/10 - 20, h/hFactor-5 + 5);
496 | ctx.lineTo(w/10, h/hFactor-5 + 20);
497 | ctx.lineTo(w/10, h/hFactor-5 + 10);
498 | ctx.lineTo(w/10 + 20, h/hFactor-5 + 10);
499 | ctx.lineTo(w/10 + 20, h/hFactor-5);
500 | ctx.closePath();
501 | ctx.fillStyle = '#000';
502 | ctx.fill();
503 |
504 | // show info
505 | var instructionLines = [
506 | 'Save our planet Earth!',
507 | 'Protect Forest! Don\'t burn them!',
508 | 'Abrupt climatic changes. Time to worry!',
509 | 'Extinguish fire on trees.',
510 | 'Hit spacebar or tap to jump player.',
511 | 'Earn Karma! Nature will show her love!',
512 | 'JS13KGames 16 - hidden glitches',
513 | 'Climate Abnormalities, Player Loves Trees',
514 | '(Player struggles to jump off tree)',
515 | 'More hinderances once speed > 1.6 mph'
516 | ];
517 | ctx.font = G.isMobile() ? '15px Helvetica' : '20px Helvetica';
518 | ctx.fillStyle = '#fff';
519 | for (var l = 0; l < instructionLines.length; l++) {
520 | var line = instructionLines[l];
521 | var hOffset = G.isMobile() ? l*40 : l*45;
522 | if (l === 0 || l === 2 || l === 4 || l === 6) {
523 | ctx.beginPath();
524 | ctx.arc(w / 10, h/2.6 + hOffset, 10, 0, 2*Math.PI, false);
525 | ctx.fill();
526 | ctx.closePath();
527 | }
528 | ctx.fillText(line, w/10 + (G.isMobile() ? 25: 50), h/2.6 + hOffset);
529 | }
530 | }
531 | rs();
532 | return canvas;
533 | },
534 | process: function () {
535 | sv();
536 | // cooldown factor
537 | ctx.globalAlpha = 0.35;
538 | ctx.globalCompositeOperation = 'source-over';
539 | // movement speed of cooldown map
540 | MN.y = (MN.y + 3) % MN.noise.height;
541 | // flickering of cooldown map
542 | x = Math.round(Math.random() * 5) * 0;
543 | ctx.drawImage(MN.noise, x, MN.y);
544 | ctx.drawImage(MN.noise, x, MN.y - MN.noise.height);
545 |
546 | // spread of the flame
547 | ctx.globalAlpha = 1.0;
548 | // whind
549 | x = 1 - Math.random() * 2;
550 | // move flame up
551 | ctx.drawImage(G.can, x, -1);
552 | ctx.globalAlpha = 0.13;
553 | ctx.globalCompositeOperation = 'lighter';
554 | ctx.drawImage(G.can, x, -1);
555 |
556 | // heat it up
557 | ctx.globalAlpha = 0.22;
558 | ctx.drawImage(MN.heat, 0, 0);
559 | fs(MN.fireColor);
560 | bp();
561 | ctx.globalAlpha = 0.52;
562 | cp();
563 | fl();
564 | rs();
565 | },
566 | mouseDown: function (e, x, y) {
567 | var w = G.can.width,
568 | h = G.can.height,
569 | ctx = MN.heat.getContext('2d');
570 |
571 | var hFactor = G.isMobile() ? 10 : 4.4;
572 |
573 | if (x >= w/2 - 50 && x <= w/2 + 50 &&
574 | y >= h/1.6 - 50 && y <= h/1.6 + 50) {
575 | // play btn clicked
576 | G.menu = null;
577 | G.restart();
578 | SU.play('playGame');
579 | } else if (x >= w*(2/4) - 30 && x <= w*(2/4) + 30 &&
580 | y >= h/1.2 - 30 && y <= h/1.2 + 30) {
581 | // download clicked
582 | downloadCanvas();
583 | SU.play('download');
584 | } else if (x >= w*(1/4) - 30 && x <= w*(1/4) + 30 &&
585 | y >= h/1.2 - 30 && y <= h/1.2 + 30) {
586 | // sound clicked
587 | G.isSound = +(!G.isSound);
588 | G.isSound && SU.play('soundOn');
589 | utils.setLocalStorageData(G.isSound, true);
590 | MN.heat = MN.getHeatMap();
591 | } else if (x >= w*(3/4) - 30 && x <= w*(3/4) + 30 &&
592 | y >= h/1.2 - 30 && y <= h/1.2 + 30) {
593 | // info clicked
594 | G.isInfoMenu = true;
595 | MN.heat = MN.getHeatMap();
596 | SU.play('info');
597 | } else if (x >= w*(1/10) - 30 && x <= w*(1/10) + 30 &&
598 | y >= h/hFactor - 30 && y <= h/hFactor + 30) {
599 | // back btn clicked
600 | G.isInfoMenu = false;
601 | MN.heat = MN.getHeatMap();
602 | SU.play('info');
603 | }
604 | },
605 | update: function () {
606 | // this.noise = MN.getNoise(G.can.width, G.can.height * 8);
607 | MN.process();
608 | }
609 | };
610 |
611 | var CC, RN, WD, SM, cloud, sunMoon, wind, rain;
612 | var diffInWeatherTime = 5;
613 | function Cloud() {
614 | this.color = 'blue';
615 | this.x = G.can.width || P.w;
616 | this.y = 100;
617 | this.speed = 7;
618 | this.update();
619 | }
620 | Cloud.prototype = {
621 | drawArcs: function (x, y, sx, sy) {
622 | bp();
623 | mt(x/sx + 150, y - 15); // 188, 50
624 | qct(
625 | x/sx + 150 + 50,
626 | y - 15 + 0,
627 | x/sx + 150 + 40,
628 | y - 15 + 40
629 | );
630 | ctx.lineWidth = 4;
631 | sts(thisWeather.hexToRgb(thisWeather.getColor(), 0.8))
632 | st();
633 |
634 | bp();
635 | mt(x/sx + 20 + 30, y + 10); // 188, 50
636 | qct(
637 | x/sx + 30,
638 | y + 35 + 10,
639 | x/sx + 30 + 60,
640 | y + 35 + 15
641 | );
642 | st();
643 | },
644 | draw: function (x, y, sx, sy) {
645 | var cloudColor = thisWeather.hexToRgb(thisWeather.getColor(), 0.5);
646 |
647 | ctx.scale(sx, sy);
648 | bp();
649 | fs(cloudColor)
650 | mt(x/sx, y);
651 | bct(x/sx - 40, y + 20, x/sx - 40, y + 70, x/sx + 60, y + 70);
652 | bct(x/sx + 80, y + 100, x/sx + 150, y + 100, x/sx + 170, y + 70);
653 | bct(x/sx + 250, y + 70, x/sx + 250, y + 40, x/sx + 220, y + 20);
654 | bct(x/sx + 260, y - 40, x/sx + 200, y - 50, x/sx + 170, y - 30);
655 | bct(x/sx + 150, y - 75, x/sx + 80, y - 60, x/sx + 80, y - 30);
656 | bct(x/sx + 30, y - 75, x/sx - 20, y - 60, x/sx, y);
657 | cp();
658 | ctx.shadowColor = thisWeather.hexToRgb(thisWeather.getColor(), 0.8);
659 | ctx.shadowOffsetX = -3;
660 | ctx.shadowOffsetY = 3;
661 | ctx.shadowBlur = 10;
662 | ctx.lineWidth = 3;
663 | sts(thisWeather.hexToRgb(thisWeather.getColor(), 0.8))
664 | st();
665 | fl();
666 |
667 | this.drawArcs(x, y, sx, sy);
668 | },
669 | update: function () {
670 | this.x -= this.speed;
671 | if (this.x + 250 < 0) {
672 | this.x = CC.w + 250;
673 | this.y = this.y + utils.getRandomInt(-10, 10);
674 | }
675 |
676 | var x = this.x, y = this.y;
677 | sv();
678 | this.draw(x, y, 0.8, 0.7);
679 | this.draw(x, y, 0.7, 0.6);
680 | this.draw(x, y, 0.6, 0.6);
681 | this.draw(x, y, 0.5, 0.7);
682 | rs();
683 | }
684 | }
685 |
686 | function SunMoon() {
687 | SM = this;
688 | this.isSun = true; // will act as moon too
689 | this.r = 20;
690 | this.x = 0;
691 | this.y = 100;
692 | this.speed = 1;
693 | G.period = 'morning';
694 | this.update();
695 | }
696 | SunMoon.prototype = {
697 | getColor: function () {
698 | var color;
699 | switch (G.period) {
700 | case 'morning':
701 | color = this.isSun ? '#ffff9e' : '#fff';
702 | break;
703 | case 'afternoon':
704 | color = this.isSun ? 'yellow' : '#fff';
705 | break;
706 | case 'evening':
707 | color = this.isSun ? '#e28800' : '#fff';
708 | break;
709 | case 'night':
710 | color = this.isSun ? '#fff' : '#fff';
711 | break;
712 | }
713 | return color;
714 | },
715 | resetPos: function () {
716 | this.x = 0;
717 | this.y = 100;
718 | G.period = 'morning';
719 | thisWeather.step = 0;
720 | },
721 | update: function () {
722 | // lets assume 30 secs is 1 day, so 15-15 secs day-night
723 | if (Weather.dt / 1000 % (5*diffInWeatherTime) > 5*diffInWeatherTime ||
724 | Weather.dt / 1000 % (5*diffInWeatherTime) > 4*diffInWeatherTime ||
725 | Weather.dt / 1000 % (5*diffInWeatherTime) > 3*diffInWeatherTime
726 | ) {
727 | G.period = 'night';
728 | } else if (Weather.dt / 1000 % (5*diffInWeatherTime) > 2*diffInWeatherTime) {
729 | G.period = 'evening';
730 | } else if (Weather.dt / 1000 % (5*diffInWeatherTime) > 1*diffInWeatherTime) {
731 | G.period = 'afternoon';
732 | } else {
733 | G.period = 'morning';
734 | }
735 |
736 | this.x += ((G.can.width / (2 * diffInWeatherTime)) / fps); // this.speed;
737 | if (this.x > G.can.width) {
738 | this.resetPos();
739 | this.isSun = !this.isSun;
740 | return;
741 | }
742 |
743 | this.y -= 0.1;
744 | sv();
745 | ctx.shadowColor = this.getColor();
746 | ctx.shadowOffsetX = -3;
747 | ctx.shadowOffsetY = 3;
748 | ctx.shadowBlur = 10;
749 | bp();
750 | ar(this.x, this.y, this.r, 0, Math.PI * 2, true);
751 | cp();
752 | ctx.fillStyle = this.getColor();
753 | fl();
754 | rs()
755 |
756 | sv();
757 | bp();
758 | ctx.fillStyle = thisWeather.hexToRgb('#444', 0.5);
759 | // moon curvature
760 | if (!this.isSun) {
761 | ar(this.x + 5, this.y - 5, 20, 0, Math.PI * 2, true);
762 | }
763 | cp();
764 | fl();
765 | rs();
766 |
767 | thisWeather.updateGradient();
768 | }
769 | }
770 |
771 | function WindParticle(i) {
772 | this.x = G.can.width + utils.getRandomInt(0, G.can.width);
773 | this.y = (i+1) * WD.pDist;
774 | this.color = '#d1e5ff';
775 | this.speed = utils.getRandomInt(1, WD.speed);
776 | }
777 | function Wind() {
778 | WD = this;
779 | this.speed = 1;
780 | this.particlesCount = 15;
781 | this.particles = [];
782 | this.pDist = 10;
783 | this.create();
784 | }
785 | Wind.prototype = {
786 | create: function () {
787 | for (var i = 0; i < WD.particlesCount; i++) {
788 | WD.particles.push(new WindParticle(i));
789 | }
790 | },
791 | update: function () {
792 | for (var i = 0; i < WD.particles.length; i++) {
793 | var wParticle = WD.particles[i];
794 | // wParticle.y += wParticle.speed;
795 | wParticle.x -= M.max(wParticle.speed);
796 | wParticle.color = thisWeather.getColor();
797 | fs(wParticle.color);
798 | var wParticleW = utils.getRandomInt(10, 50)
799 | bp();
800 | mt(wParticle.x, wParticle.y);
801 | lt(wParticle.x - wParticleW, wParticle.y);
802 | cp();
803 | fl();
804 | ctx.lineWidth = 2;
805 | sts(wParticle.color);
806 | st();
807 |
808 | if (wParticle.x < 0) {
809 | // reinitialize a new wParticle
810 | WD.particles[i] = new WindParticle(i);
811 | }
812 | }
813 | }
814 | }
815 |
816 | function Droplet(i) {
817 | this.x = RN.topDropletsDist * i;
818 | this.y = 0;
819 | this.color = '#d1e5ff';
820 | this.speed = utils.getRandomInt(10, 30);
821 | }
822 | function Rain() {
823 | RN = this;
824 | this.particles = [];
825 | this.particlesCount = 33;
826 | this.topDroplets = this.particlesCount * 1.5; // 66
827 | this.rightDroplets = this.particlesCount / 3; // 33
828 | this.topDropletsDist = (G.can.width / this.topDroplets) * 2;
829 | this.rightDropletsDist = (G.can.width / this.rightDroplets) * 2;
830 | this.create();
831 | }
832 | Rain.prototype = {
833 | create: function () {
834 | for (var i = 0; i < RN.particlesCount; i++) {
835 | RN.particles.push(new Droplet(i));
836 | }
837 | },
838 | update: function () {
839 | for (var i = 0; i < RN.particles.length; i++) {
840 | var droplet = RN.particles[i];
841 | droplet.y += droplet.speed;
842 | droplet.x -= M.max(G.speed, wind.speed);
843 | droplet.color = thisWeather.getColor();
844 | fs(droplet.color);
845 | var dropletH = utils.getRandomInt(6, 20)
846 | bp();
847 | mt(droplet.x, droplet.y);
848 | lt(droplet.x - 2, droplet.y + dropletH);
849 | cp();
850 | fl();
851 | ctx.lineWidth = 2;
852 | sts(droplet.color);
853 | st();
854 |
855 | if (droplet.y > CC.h) {
856 | // reinitialize a new droplet
857 | RN.particles[i] = new Droplet(i);
858 | }
859 | }
860 | }
861 | };
862 |
863 | function Weather() {
864 | this.colors = [
865 | [255,255,255],
866 | [142,214,255],
867 | [255, 254, 210],
868 | [153,153,153],
869 | [20,20,20],
870 | [20,20,20]
871 | ];
872 |
873 | this.step = 0;
874 | this.i = 0;
875 | this.colorIndices = [0, 1 ,2, 3];
876 |
877 | thisWeather = this;
878 | CC = document.getElementById('canvascontainer').style;
879 | this.init();
880 | }
881 |
882 | Weather.prototype = {
883 | updateGradient: function () {
884 | var c0_0 = thisWeather.colors[thisWeather.colorIndices[0]],
885 | c0_1 = thisWeather.colors[thisWeather.colorIndices[1]],
886 | c1_0 = thisWeather.colors[thisWeather.colorIndices[2]],
887 | c1_1 = thisWeather.colors[thisWeather.colorIndices[3]],
888 |
889 | istep = 1 - thisWeather.step,
890 | r1 = Math.round(istep * c0_0[0] + thisWeather.step * c0_1[0]),
891 | g1 = Math.round(istep * c0_0[1] + thisWeather.step * c0_1[1]),
892 | b1 = Math.round(istep * c0_0[2] + thisWeather.step * c0_1[2]),
893 | color1 = 'rgb(' + r1 + ',' + g1 + ',' + b1 + ')',
894 |
895 | r2 = Math.round(istep * 255+ thisWeather.step * 255),
896 | g2 = Math.round(istep * 255+ thisWeather.step * 255),
897 | b2 = Math.round(istep * 255+ thisWeather.step * 255),
898 | color2 = 'rgb(' + r2 + ',' + g2 + ',' + b2 + ')';
899 |
900 | var grd = ctx.createLinearGradient(0, 0, 0, G.can.height);
901 | grd.addColorStop(0, color1);
902 | grd.addColorStop(0.9, color2);
903 | G.backgroundColor = grd;
904 |
905 | thisWeather.step += SM.isSun ? 0.0076 : 0.0076/2.22; // 1 / (diffInWeatherTime * fps);
906 | if (thisWeather.step >= 1) {
907 | thisWeather.step = 0;
908 | for (var j = 0; j < thisWeather.colorIndices.length; j++) {
909 | thisWeather.colorIndices[j] = (thisWeather.i + 1) % thisWeather.colors.length;
910 | }
911 | thisWeather.i += 1;
912 | }
913 | },
914 | hexToRgb: function (hexColor, alpha) {
915 | if (!hexColor) { return; }
916 |
917 | alpha = alpha || 1.0;
918 | // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
919 | var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
920 | hexColor = hexColor.replace(shorthandRegex, function(m, r, g, b) {
921 | return r + r + g + g + b + b;
922 | });
923 |
924 | var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hexColor);
925 | return result ? 'rgba(' +
926 | parseInt(result[1], 16) + ',' +
927 | parseInt(result[2], 16) + ',' +
928 | parseInt(result[3], 16) + ',' +
929 | alpha + ')' : 'rgba(255,255,255,0)';
930 | },
931 | getColor: function (isKarmaText) {
932 | var color;
933 | switch (G.period) {
934 | case 'morning':
935 | color = isKarmaText ? SM.isSun ? '#444' : '#fff' : SM.isSun ? '#8ED6FF' : '#444';
936 | break;
937 | case 'afternoon':
938 | color = isKarmaText ? SM.isSun ? '#444' : '#fff' : SM.isSun ? '#56baf3' : '#444';
939 | break;
940 | case 'evening':
941 | color = isKarmaText ? SM.isSun ? '#444' : '#fff' : SM.isSun ? '#999' : '#444';
942 | break;
943 | case 'night':
944 | color = isKarmaText ? SM.isSun ? '#444' : '#fff' : SM.isSun ? '#444' : '#444';
945 | break;
946 | }
947 | return color;
948 | },
949 | update: function () {
950 | var now = new Date().getTime();
951 | Weather.dt = now - G.gameStartTime;
952 |
953 | sunMoon.update();
954 | if (!G.isMobile()) {
955 | cloud.update();
956 |
957 | // console.log(M.ceil(Weather.dt / 1000))
958 | if (!this.canRain && M.ceil(Weather.dt / 1000) % 16 === 0) {
959 | this.canRain = true;
960 | this.isRaining = true;
961 | } else if (M.ceil(Weather.dt / 1000) % 33 === 0) {
962 | this.canRain = false;
963 | this.isRaining = false;
964 | }
965 |
966 | if (this.canRain && this.isRaining) {
967 | rain.update();
968 | }
969 |
970 | wind.update();
971 | }
972 | },
973 | init: function () {
974 | cloud = new Cloud();
975 | sunMoon = new SunMoon();
976 | rain = new Rain();
977 | wind = new Wind();
978 | this.update();
979 | }
980 | };
981 |
982 | var PS;
983 | function Particles(x, y) {
984 | PS = this;
985 | this.x = x - 10,
986 | this.y = y;
987 | this.vyL1 = 3;
988 | this.vyL2 = 2;
989 | this.vyL3 = 1;
990 | this.finished = false;
991 | this.particles = [];
992 | this.diff = 0;
993 | this.draw();
994 | // setTimeout(function () {
995 | // PS.finished = true;
996 | // }, 1000);
997 | }
998 |
999 | Particles.prototype = {
1000 | draw: function () {
1001 | // ctx.globalCompositeOperation = 'source-over';
1002 | fs('red');
1003 | for (var i = 0; i < 10; i+= 2) {
1004 | fr(PS.x + 4*i, M.min(PS.y + this.vyL1, CC.h - 50), utils.getRandomInt(4, 6), utils.getRandomInt(4, 6));
1005 | }
1006 | for (var i = 1; i < 10; i+= 2) {
1007 | fr(PS.x + 4*i, M.min(PS.y + 7 + this.vyL2, CC.h - 50), utils.getRandomInt(4, 6), utils.getRandomInt(4, 6));
1008 | }
1009 | for (var i = 0; i < 10; i+= 2) {
1010 | fr(PS.x + 4*i, M.min(PS.y + 15 + this.vyL3, CC.h - 50), utils.getRandomInt(4, 6), utils.getRandomInt(4, 6));
1011 | }
1012 |
1013 | this.diff = CC.h - PS.y;
1014 | PS.y += this.diff * (10 / 15) * (fps / 1000);
1015 |
1016 | if (PS.y > CC.h - P.fireOffset) {
1017 | PS.finished = true;
1018 | }
1019 | PS.x -= G.speed;
1020 | }
1021 | }
1022 | /*var BG;
1023 | function Background() {
1024 | BG = this;
1025 | BG.animate();
1026 | }
1027 |
1028 | Background.prototype = {
1029 | burnBurnBurn: function() {
1030 | var x, y, bottomLine = BG.canvasWidth * (BG.canvasHeight - 1);
1031 |
1032 | // draw random pixels at the bottom line
1033 | for (x = 0; x < BG.canvasWidth; x++) {
1034 | var value = 0;
1035 |
1036 | if (Math.random() > BG.threshold)
1037 | value = 255;
1038 |
1039 | BG.fire[bottomLine + x] = value;
1040 | }
1041 |
1042 | // move flip upwards, start at bottom
1043 | var value = 0;
1044 |
1045 | for (y = 0; y < BG.canvasHeight; ++y) {
1046 | for (var x = 0; x < BG.canvasWidth; ++x) {
1047 | if (x == 0) {
1048 | value = BG.fire[bottomLine];
1049 | value += BG.fire[bottomLine];
1050 | value += BG.fire[bottomLine - BG.canvasWidth];
1051 | value /= 3;
1052 | } else if (x == BG.canvasWidth -1) {
1053 | value = BG.fire[bottomLine + x];
1054 | value += BG.fire[bottomLine - BG.canvasWidth + x];
1055 | value += BG.fire[bottomLine + x - 1];
1056 | value /= 3;
1057 | } else {
1058 | value = BG.fire[bottomLine + x];
1059 | value += BG.fire[bottomLine + x + 1];
1060 | value += BG.fire[bottomLine + x - 1];
1061 | value += BG.fire[bottomLine - BG.canvasWidth + x];
1062 | value /= 4;
1063 | }
1064 |
1065 | if (value > 1)
1066 | value -= 1;
1067 |
1068 | value = Math.floor(value);
1069 | var index = bottomLine - BG.canvasWidth + x;
1070 | BG.fire[index] = value;
1071 | }
1072 |
1073 | bottomLine -= BG.canvasWidth;
1074 | }
1075 |
1076 | var skipRows = 1; // skip the bottom 2 rows
1077 |
1078 | // render the flames using our color table
1079 | for (var y = skipRows; y < BG.canvasHeight; ++y) {
1080 | for (var x = 0; x < BG.canvasWidth; ++x) {
1081 | var index = y * BG.canvasWidth * 4 + x * 4;
1082 | var value = BG.fire[(y - skipRows) * BG.canvasWidth + x];
1083 |
1084 | BG.data[index] = BG.colors[value][0];
1085 | BG.data[++index] = BG.colors[value][1];
1086 | BG.data[++index] = BG.colors[value][2];
1087 | BG.data[++index] = 255;
1088 | }
1089 | }
1090 |
1091 | // sometimes change BG.fire intensity
1092 | if (BG.intensity == null) {
1093 | if (Math.random() > 0.95) {
1094 | BG.randomizeThreshold();
1095 | }
1096 | }
1097 |
1098 | BG.ctx.putImageData(BG.imageData, 0, BG.CC.height - BG.canvasHeight);
1099 |
1100 | },
1101 | randomizeThreshold: function() {
1102 | BG.threshold += Math.random() * 0.2 - 0.1;
1103 | BG.threshold = Math.min(Math.max(BG.threshold, 0.5), 0.8);
1104 | },
1105 | animate: function () {
1106 | BG.intensity = null;
1107 | BG.threshold = 0.5;
1108 | BG.CC = document.querySelector('canvas');
1109 | BG.ctx = BG.CC.getContext('2d');
1110 | BG.canvasWidth = BG.CC.width;
1111 | BG.canvasHeight = 50 || P.fireOffset;
1112 | BG.imageData = BG.ctx.getImageData(0, BG.CC.height - BG.canvasHeight, BG.canvasWidth, BG.canvasHeight);
1113 | BG.data = BG.imageData.data;
1114 | //BG.numPixels = BG.data.length / 4;
1115 | BG.colors = [];
1116 |
1117 | for (var i = 0; i < 256; i++) {
1118 | var color = [];
1119 | color[0] = color[1] = color[2] = 75;
1120 | BG.colors[i] = color;
1121 | }
1122 |
1123 | for (var i = 0; i < 32; ++i) {
1124 | BG.colors[i][2] = i << 1;
1125 | BG.colors[i + 32][0] = i << 3;
1126 | BG.colors[i + 32][2] = 64 - (i << 1);
1127 | BG.colors[i + 64][0] = 255;
1128 | BG.colors[i + 64][1] = i << 3;
1129 | BG.colors[i + 96][0] = 255;
1130 | BG.colors[i + 96][1] = 255;
1131 | BG.colors[i + 96][2] = i << 2;
1132 | BG.colors[i + 128][0] = 255;
1133 | BG.colors[i + 128][1] = 255;
1134 | BG.colors[i + 128][2] = 64 + (i << 2);
1135 | BG.colors[i + 160][0] = 255;
1136 | BG.colors[i + 160][1] = 255;
1137 | BG.colors[i + 160][2] = 128 + (i << 2);
1138 | BG.colors[i + 192][0] = 255;
1139 | BG.colors[i + 192][1] = 255;
1140 | BG.colors[i + 192][2] = 192 + i;
1141 | BG.colors[i + 224][0] = 255;
1142 | BG.colors[i + 224][1] = 255;
1143 | BG.colors[i + 224][2] = 224 + i;
1144 | }
1145 |
1146 | BG.fire = [];
1147 | // init BG.fire array
1148 | for (var i = 0; i < BG.canvasWidth * BG.canvasHeight; i++) {
1149 | BG.fire[i] = 75;
1150 | }
1151 |
1152 | BG.burnBurnBurn();
1153 |
1154 | // intercept key up event to change intensity on BG.fire effect
1155 | document.body.onkeyup = function(event) {
1156 | if (event.keyCode >= 97 && event.keyCode <= 105) {
1157 | BG.intensity = (event.keyCode - 97);
1158 | BG.intensity = BG.intensity / 8;
1159 | BG.intensity = BG.intensity * 0.4;
1160 | BG.intensity = BG.intensity + 0.2;
1161 | BG.threshold = 1 - BG.intensity;
1162 | } else if (event.keyCode == 96) { // 0 ==> randomize
1163 | BG.intensity = 0;
1164 | BG.randomizeThreshold();
1165 | }
1166 | };
1167 |
1168 | }
1169 | };*/
1170 |
1171 | var flameBack = new function() {
1172 | var context;
1173 | var buffer;
1174 | var bufferContext;
1175 | var imageData;
1176 | var palette;
1177 | var colorMap;
1178 | var width;
1179 | var height;
1180 | var scale = 2;
1181 | var fan = 2.5;
1182 | var slack = 5;
1183 | this.time = new Date();
1184 |
1185 | this.canvas = undefined;
1186 |
1187 | this.init = function() {
1188 | context = this.canvas.getContext('2d');
1189 |
1190 | width = (this.canvas.width + 30) / scale;
1191 | height = P.fireOffset / scale;
1192 |
1193 | width = Math.ceil(width);
1194 | height = Math.ceil(height);
1195 |
1196 | colorMap = Array(width * height);
1197 |
1198 | for(var i = 0; i < colorMap.length; i++)
1199 | colorMap[i] = 255;
1200 |
1201 | initPalette();
1202 | initBuffer();
1203 |
1204 | this.update();
1205 | };
1206 |
1207 | // init palette from warm to white hot colors
1208 | var initPalette = function() {
1209 | palette = Array(256);
1210 |
1211 | for(var i = 0; i < 64; i++) {
1212 | palette[i] = [(i << 2), 0, 0];
1213 | palette[i + 64] = [255, (i << 2), 0];
1214 | palette[i + 128] = [255, 255, (i << 2)];
1215 | palette[i + 192] = [255, 255, 255];
1216 | }
1217 | };
1218 |
1219 | // offscreen buffer for rendering and scaling
1220 | var initBuffer = function() {
1221 | buffer = document.createElement('canvas');
1222 | buffer.width = width;
1223 | buffer.height = height;
1224 | buffer.style.visibility = 'hidden';
1225 |
1226 | bufferContext = buffer.getContext('2d');
1227 | imageData = bufferContext.createImageData(width, height);
1228 | };
1229 |
1230 | // main render loop
1231 | this.update = function() {
1232 | if (!G.isMobile()) {
1233 | smooth();
1234 | draw();
1235 | fan = utils.getRandomInt(0, 6);
1236 | } else {
1237 | var grd = ctx.createLinearGradient(0, CC.h - P.fireOffset , 0, G.can.height);
1238 | grd.addColorStop(0, 'rgba(255, 0, 0, ' + utils.getRandomInt(8, 10)/10 + ')');
1239 | grd.addColorStop(0.7, 'rgba(255, 165, 0, ' + utils.getRandomInt(8, 10)/10 + ')');
1240 | grd.addColorStop(0.9, 'rgba(255, 255, 0, ' + utils.getRandomInt(8, 10)/10 + ')');
1241 | sv();
1242 | fs(grd);
1243 | fr(0, CC.h - P.fireOffset, G.can.width, P.fireOffset)
1244 | rs();
1245 | }
1246 | };
1247 |
1248 | var smooth = function() {
1249 | for(var x = width - 1; x >= 1; x--) {
1250 | for(var y = height; y--;) {
1251 | var p = ((
1252 | colorMap[toIndex(x - 1, y - 1)] +
1253 | colorMap[toIndex(x, y - 1)] +
1254 | colorMap[toIndex(x + 1, y - 1)] +
1255 | colorMap[toIndex(x - 1, y)] +
1256 | colorMap[toIndex(x + 1, y)] +
1257 | colorMap[toIndex(x - 1, y + 1)] +
1258 | colorMap[toIndex(x, y + 1)] +
1259 | colorMap[toIndex(x + 1, y + 1)]) >> 3);
1260 |
1261 | p = Math.max(0, p - randomValue(fan));
1262 |
1263 | colorMap[toIndex(x, y - 1)] = p;
1264 |
1265 | if (y < height - slack) { // don't draw random noise in bottom rows
1266 | if (y < height - 2) {
1267 | // set two lines of random palette noise at bottom of
1268 | // colorMap
1269 | colorMap[toIndex(x, height)] =
1270 | randomValue(palette.length);
1271 | colorMap[toIndex(x, height - 1)] =
1272 | randomValue(palette.length);
1273 | }
1274 |
1275 | drawPixel(x, y, palette[colorMap[toIndex(x, y)]]);
1276 | }
1277 | }
1278 | }
1279 | };
1280 |
1281 | // draw colormap->palette values to screen
1282 | var draw = function() {
1283 | // render the image data to the offscreen buffer...
1284 | bufferContext.putImageData(imageData, 0, 0);
1285 | // ...then draw it to scale to the onscreen canvas
1286 | context.drawImage(buffer, -20, CC.h - (height * scale), width * scale, height * scale + 10);
1287 | };
1288 |
1289 | // set pixels in imageData
1290 | var drawPixel = function(x, y, color) {
1291 | var offset = (x + y * imageData.width) * 4;
1292 | imageData.data[offset] = color[0];
1293 | imageData.data[offset + 1] = color[1];
1294 | imageData.data[offset + 2] = color[2];
1295 | imageData.data[offset + 3] = 255;
1296 | };
1297 |
1298 | var randomValue = function(max) {
1299 | // protip: a double bitwise not (~~) is much faster than
1300 | // Math.floor() for truncating floating point values into "ints"
1301 | return ~~(Math.random() * max);
1302 | };
1303 |
1304 | // because "two-dimensional" arrays in JavaScript suck
1305 | var toIndex = function(x, y) {
1306 | return (y * width + x);
1307 | };
1308 |
1309 | // draw a bunch of random embers onscreen
1310 | this.drawEmbers = function() {
1311 | for(var x = 1; x < width - 1; x++) {
1312 | for(var y = 1; y < height; y++) {
1313 | if(Math.random() < 0.11)
1314 | colorMap[toIndex(x, y)] = randomValue(palette.length);
1315 | }
1316 | }
1317 | };
1318 | };
1319 |
1320 | var Pl;
1321 | function Player() {
1322 | Pl = this;
1323 | Pl.liesOn = 0;
1324 | Pl.maxH = 66;
1325 | Pl.bounceHeight = 5;
1326 | Pl.w = 20;
1327 | Pl.h = Pl.maxH;
1328 | Pl.x = G.trees[0].x;
1329 | Pl.y = G.trees[0].y - Pl.h;
1330 | Pl.vel = 0;
1331 | Pl.isJet = false;
1332 | Pl.isRest = true;
1333 | Pl.t = new Date();
1334 | Pl.update();
1335 | return Pl;
1336 | }
1337 |
1338 | Player.prototype = {
1339 | tears: function (x, y) {
1340 | if (Pl.died) return;
1341 | bp();
1342 | ctx.fillStyle = '#36b1f7';
1343 | mt(Pl.x + Pl.w + x - 3, Pl.y + y);
1344 | lt(Pl.x + Pl.w + x, Pl.y + y - 4);
1345 | lt(Pl.x + Pl.w + x + 3, Pl.y + y);
1346 | ar(Pl.x + Pl.w + x, Pl.y + y, 3, 0, Math.PI);
1347 | cp();
1348 | fl();
1349 | },
1350 | body: function () {
1351 | sv();
1352 | ctx.shadowColor = '#000';
1353 | ctx.shadowOffsetX = -2;
1354 | ctx.shadowOffsetY = 2;
1355 | ctx.shadowBlur = 10;
1356 | if (Pl.died) {
1357 | fr(Pl.x - 2 * (Pl.h / 3), Pl.y, (2 * Pl.h) / 3, Pl.w);
1358 | rs();
1359 | } else {
1360 | fr(Pl.x, Pl.y, Pl.w, (2 * Pl.h) / 3);
1361 | rs();
1362 | // shade
1363 | fs('#777')
1364 | fr(Pl.x + 3, Pl.y + 15, 3, 2 * (Pl.h / 3) - 30);
1365 | }
1366 | rs();
1367 | },
1368 | eyes: function () {
1369 | if (!Pl.w && !Pl.h) return;
1370 |
1371 | var ct = new Date(), clr;
1372 | // 0 means NO, 1 means starts blinking, 2 means blinked
1373 | Pl.isBlink = ct - Pl.t > 2500 ? ++Pl.isBlink : 0;
1374 | clr = Pl.isBlink > 5 ? '#000' : '#fff';
1375 |
1376 | if (Pl.isBlink >= 8) {
1377 | Pl.t = ct; Pl.isBlink = 0;
1378 | }
1379 | //fs(clr);
1380 | bp(clr);
1381 |
1382 | if (Pl.died) {
1383 | ar(Pl.x - 2 * (Pl.h / 3) + 6, Pl.y + 4, 2, 0, M.PI * 2, true);
1384 | ar(Pl.x - 2 * (Pl.h / 3) + 6, Pl.y + 10, 2, 0, M.PI * 2, true);
1385 | } else {
1386 | ar(Pl.x + 2 * (Pl.w / 3) - 2, Pl.y + 5, 2, 0, M.PI * 2, true);
1387 | ar(Pl.x + (Pl.w - 3), Pl.y + 5, 2, 0, M.PI * 2, true);
1388 | }
1389 | ctx.fillStyle = clr;//'#8effb6';
1390 | fl();
1391 | },
1392 | legs: function () {
1393 | var lw = 3; // leg width
1394 | fs('#000');
1395 | if (Pl.died) {
1396 | // left leg
1397 | fr(Pl.x, Pl.y + (Pl.w / 3), Pl.h / 3, lw);
1398 | // right leg
1399 | fr(Pl.x, Pl.y + 2 * (Pl.w / 3), Pl.h / 3, lw);
1400 | } else {
1401 | // left leg
1402 | fr(Pl.x + (Pl.w / 3) - lw / 2, Pl.y + 2 * (Pl.h / 3), lw, Pl.h / 3);
1403 | // right leg
1404 | fr(Pl.x + 2 * (Pl.w / 3) - lw / 2, Pl.y + 2 * (Pl.h / 3), lw, Pl.h / 3);
1405 | }
1406 | },
1407 | fe: function () { // Fire Extinguisher
1408 | fs('#eaeaea');
1409 | bp();
1410 | el(ctx, Pl.x - 30, Pl.y + Pl.h, 80, 10, '#000')
1411 | cp();
1412 | fs('#EAEAEA');
1413 | fl();
1414 | ctx.lineWidth = 1;
1415 | sts('#dedede');
1416 | st();
1417 | fs('#8ED6FF');
1418 | for (var i = -30; i < Pl.w + 30; i+=6) {
1419 | bp();
1420 | mt(Pl.x + i, Pl.y + Pl.h);
1421 | lt(Pl.x + i - 1, Pl.y + Pl.h + utils.getRandomInt(10, G.can.height/2));
1422 | lt(Pl.x + i + 3, Pl.y + Pl.h + utils.getRandomInt(10, G.can.height/2));
1423 | lt(Pl.x + i + 1, Pl.y + Pl.h);
1424 | cp();
1425 | fl();
1426 | }
1427 |
1428 | },
1429 | burst: function () {
1430 | // Particle effects
1431 | if (Pl.w && Pl.h) {
1432 | this.dieParticles = new Particles(Pl.x, Pl.y);
1433 | } else if (PS.finished) {
1434 | G.stopCycle();
1435 | PS.finished = false;
1436 | }
1437 |
1438 | if (!Pl.w && !Pl.h) {
1439 | this.dieParticles.draw();
1440 | }
1441 |
1442 | Pl.w = 0;
1443 | Pl.h = 0;
1444 | },
1445 | update: function () {
1446 | Pl.x -= G.speed;
1447 | if (Pl.isInAir && Pl.bounceFactor > 0) {
1448 | Pl.y -= Pl.bounceHeight;
1449 | Pl.x += 1.5 * G.speed;
1450 | Pl.bounceFactor -= 2;
1451 | } else if (Pl.isInAir && !Pl.bounceFactor) { // smooth curve parabola jump
1452 | Pl.y -= Pl.bounceHeight / 2;
1453 | Pl.x += 1.5 * G.speed;
1454 | Pl.bounceFactor -= 2;
1455 | } else if (Pl.isInAir && Pl.bounceFactor === -2) { // smooth curve parabola jump
1456 | Pl.y -= 0;
1457 | Pl.x += 1.5 * G.speed;
1458 | Pl.bounceFactor -= 2;
1459 | } else if (Pl.isInAir && Pl.bounceFactor === -4) { // smooth curve parabola jump
1460 | Pl.y -= Pl.bounceHeight / 2;
1461 | Pl.x += 1.5 * G.speed;
1462 | Pl.bounceFactor -= 2;
1463 | } else if (Pl.isInAir && Pl.bounceFactor === -6) { // revert back force rewind
1464 | Pl.y += Pl.bounceHeight;
1465 | Pl.x += 1.5 * G.speed;
1466 | Pl.bounceFactor = -6;
1467 | }
1468 |
1469 | if (Pl.died && !Pl.busted) {
1470 | Pl.y += 10;
1471 | if (Pl.isCornerStrike) {
1472 | if (Pl.y > CC.h - 3*P.fireOffset) {
1473 | if (!Pl.busted) {
1474 | Pl.busted = true;
1475 | }
1476 | }
1477 | } else if (!Pl.busted) {
1478 | Pl.busted = true;
1479 | }
1480 | }
1481 |
1482 | fs('#000');
1483 | Pl.body();
1484 | Pl.legs();
1485 | Pl.eyes();
1486 | Pl.tears(2, 6);
1487 | Pl.tears(5, 15);
1488 | if (Pl.isInAir && !Pl.busted) Pl.fe();
1489 | if (Pl.busted) {
1490 | Pl.burst();
1491 | }
1492 | Pl.checkCollision();
1493 | },
1494 | keyDown: function (key) {
1495 | if (Pl.busted) { return; }
1496 |
1497 | if (key === 32) { // 32 is space,38 is UP, 40 is DOWN
1498 | Pl.irj = true; // isReadyToJump
1499 | if (Pl.h < 50) { return; }
1500 | Pl.h -= 2;
1501 | Pl.y += 2;
1502 | } else if (key === 39) {
1503 | Pl.x += G.speed;
1504 | }
1505 |
1506 | if (Pl.irj) {
1507 | Pl.irj = false;
1508 | Pl.isInAir = true;
1509 | Pl.isKarmaLocked = false;
1510 | Pl.bounceFactor = Pl.maxH - Pl.h;
1511 | Pl.bounceFactor *= 4;
1512 | Pl.h = Pl.maxH;
1513 | }
1514 | },
1515 | keyUp: function () {
1516 | /*if (Pl.irj) {
1517 | Pl.irj = false;
1518 | Pl.isInAir = true;
1519 | SU.play('moveAhead');
1520 | Pl.bounceFactor = Pl.maxH - Pl.h;
1521 | Pl.bounceFactor *= 4;
1522 | Pl.h = Pl.maxH;
1523 | }*/
1524 | },
1525 | checkCollision: function () {
1526 | if (Pl.x <= 0) { // leftmost collision
1527 | Pl.died = true;
1528 | Pl.isCornerStrike = true;
1529 | return;
1530 | } else if (Pl.y > CC.h - P.fireOffset) { // bottom fire collision
1531 | Pl.died = true;
1532 | Pl.isCornerStrike = true;
1533 | return;
1534 | } else if (Pl.x + Pl.w > CC.w) { // rightmost collision
1535 | Pl.died = true;
1536 | Pl.isCornerStrike = true;
1537 | return;
1538 | } else if (Pl.y < 0 ) { // topmost collision
1539 | Pl.died = true;
1540 | Pl.isCornerStrike = true;
1541 | return;
1542 | }
1543 |
1544 | var i, tree;
1545 | for (i = 0; i < G.trees.length; i++) { // M.min(Pl.liesOn + 10, )
1546 | tree = G.trees[i];
1547 |
1548 | var playerEnd = Pl.x + Pl.w - 4; // 4 bcz legs are placed (x coord)technically before body
1549 | if ((playerEnd >= tree.x && playerEnd < (tree.x + tree.width + 4) && Pl.y + Pl.w + Pl.h >= tree.x)
1550 | ) {
1551 | for (var j = 0; j < Pl.bounceHeight; j++) {
1552 | if (Pl.y + Pl.h + j >= tree.y) {
1553 | G.trees[i].flame = null;
1554 | Pl.isInAir = false;
1555 | Pl.liesOn = i;
1556 | if (!Pl.isKarmaLocked && Pl.liesOn !== Pl.lastLiesOn) {
1557 | G.karma && SU.play('moveAhead');
1558 | G.karma += 1;
1559 | }
1560 | Pl.isKarmaLocked = true;;
1561 | Pl.lastLiesOn = Pl.liesOn;
1562 | // Pl.y += tree.y - (Pl.y + Pl.h) - 2;
1563 | break;
1564 | }
1565 | }
1566 | if (Pl.y >= tree.y && Pl.y < tree.y + tree.height) {
1567 | Pl.died = true;
1568 | break;
1569 | }
1570 |
1571 | }
1572 | }
1573 | }
1574 | };
1575 | var T, CC;
1576 | var blw = 200, bw = 0;
1577 |
1578 | function Tree(config) {
1579 | T = this;
1580 | config = config || {};
1581 | // T.lw = T.w = 0;
1582 | T.minW = 10;
1583 | T.maxW = 80;
1584 | T.minH = P.fireOffset;
1585 | T.maxH = G.isMobile() ? 300 : 400;
1586 | T.minDist = 50;
1587 | T.maxDist = G.isMobile() ? 100 : 200;
1588 | // T.branchThickness = 3;
1589 |
1590 | CC.w = utils.pI(G.can.width);
1591 | CC.h = utils.pI(G.can.height);
1592 |
1593 | T.color = '#a77b44';
1594 | this.add();
1595 | if (!config.isNoFlame) {
1596 | if (G.isMobile()) {
1597 | this.flame = true;
1598 | } else {
1599 | this.flame = smoky;
1600 | this.flame.addEntity(Flame);
1601 | }
1602 | }
1603 | return T;
1604 | }
1605 |
1606 | Tree.prototype = {
1607 | /*drawFractalTree: function (x, y, width, height) {
1608 | T.drawTree(x, y, width, height, -90, T.branchThickness);
1609 | },
1610 | drawTree: function (x1, y1, width, height, angle, depth){
1611 | T.brLength = T.brLength || T.random(T.minW, T.maxW);
1612 | T.angle = T.angle || T.random(15, 20);
1613 | T.bb = (T.cos(angle) * depth * T.brLength);
1614 | T.vv = (T.sin(angle) * depth * T.brLength);
1615 | if (depth != 0){
1616 | var x2 = x1 + T.bb;
1617 | var y2 = y1 - T.vv;
1618 |
1619 | T.drawLine(x1, y1, x2, y2, depth);
1620 |
1621 | T.drawTree(x2, y2, width, height, angle - T.angle, depth - 1);
1622 | T.drawTree(x2, y2, width, height, angle + T.angle, depth - 1);
1623 | // T.drawLine(x1, y1, x2, y2, depth);
1624 | }
1625 | },
1626 | random: function (min, max){
1627 | return min + Math.floor(Math.random()*(max+1-min));
1628 | },
1629 | drawLine: function (x1, y1, x2, y2, thickness){
1630 | ctx.fillStyle = '#000';
1631 | if(thickness > 2)
1632 | ctx.strokeStyle = 'rgb(139,126, 102)'; //Brown
1633 | else
1634 | ctx.strokeStyle = 'rgb(34,139,34)'; //Green
1635 | ctx.lineWidth = thickness * 1.5;
1636 | bp();
1637 | mt(x1, y1);
1638 | lt(x2, y2);
1639 | cp();
1640 | st();
1641 |
1642 | },
1643 | cos: function (angle) {
1644 | return M.cos(T.deg_to_rad(angle));
1645 | },
1646 | sin: function (angle) {
1647 | return M.sin(T.deg_to_rad(angle));
1648 | },
1649 | deg_to_rad: function (angle){
1650 | return angle*(M.PI/180.0);
1651 | },*/
1652 | getWidth: function (val) {
1653 | if (val !== undefined) {
1654 | return val;
1655 | }
1656 | return utils.getRandomInt(T.minW, T.maxW);
1657 | },
1658 | getHeight: function (val) {
1659 | if (val !== undefined) {
1660 | return val;
1661 | }
1662 | return utils.getRandomInt(T.minH, T.maxH);
1663 | },
1664 | add: function (val) {
1665 | T.preCompute();
1666 | T.x = blw + bw;
1667 | T.y = CC.h - T.h - (P.fireOffset * 0.6),
1668 | T.width = bw,
1669 | T.height = T.h;
1670 | // T.drawFractalTree(T.x, T.y, T.width, T.height)
1671 |
1672 | //T.update(T);
1673 | return T;
1674 | },
1675 | update: function (treeInstance) {
1676 | var x = treeInstance.x,
1677 | y = treeInstance.y,
1678 | width = treeInstance.width,
1679 | height = treeInstance.height;
1680 |
1681 | sv();
1682 | fs(T.color);
1683 |
1684 | bp();
1685 | mt(x, y);
1686 | // left side
1687 | bct(x , y + height, x - 25, y + height, x - 25, y + height);
1688 |
1689 | // left bottom curve
1690 | bct(x, y + height, x + (width / 2), y + height / 1.2, x + (width / 2), y + (height / 1.2))
1691 | // right bottom curve
1692 | bct(x + (width / 2), y + (height / 1.2), x + (width / 2), y + height / 1.2, x + width + 25, y + height);
1693 |
1694 | // right side
1695 | bct(x + width, y + height, x + width, y, x + width, y);
1696 |
1697 | ctx.shadowColor = '#6b4e2a';
1698 | ctx.shadowOffsetX = -3;
1699 | ctx.shadowOffsetY = 3;
1700 | ctx.shadowBlur = 10;
1701 | ctx.strokeStyle = '#6b4e2a';
1702 | ctx.lineWidth = 1;
1703 | st();
1704 | cp();
1705 | fl();
1706 | rs();
1707 |
1708 | fs('#444')
1709 | el(ctx, x, y - 4, width, 10, '#6b4e2a');
1710 |
1711 | if (treeInstance.flame) {
1712 | if (G.isMobile()) {
1713 | T.addCircle(x, y, width);
1714 | } else {
1715 | treeInstance.flame.update(x, y, width);
1716 | }
1717 | }
1718 | },
1719 | addCircle: function (x, y, width) {
1720 | bp();
1721 | ar(x + (width/2), y, width/2, 0, Math.PI*2, false);
1722 | fs('rgba(255, 0, 0, 0.4)');
1723 | fl();
1724 |
1725 | bp();
1726 | ar(x + (width/2), y, width/3, 0, Math.PI*2, false);
1727 | fs('rgba(255, 165, 0, 0.4)');
1728 | fl();
1729 |
1730 | bp();
1731 | ar(x + (width/2), y, width/6, 0, Math.PI*2, false);
1732 | fs('rgba(255, 255, 0, ' + utils.getRandomInt(0.3, 0.5)/10 + ')');
1733 | fl();
1734 | },
1735 | preCompute: function () {
1736 | T.lw = blw + bw + (bw === 0 ? 0 : utils.getRandomInt(T.minDist, T.maxDist));
1737 | blw = T.lw;
1738 | T.w = utils.getRandomInt(T.minW, T.maxW);
1739 | bw = T.w;
1740 | T.h = utils.getRandomInt(T.minH, T.maxH);
1741 | // console.log(blw, bw)
1742 | // T.rw = CC.w - T.lw - T.w;
1743 | },
1744 | removeFlame: function (that) {
1745 | that.flame = undefined;
1746 | }
1747 | };
1748 |
1749 |
1750 | var G, ctx, CC, background, player, weather, smoky;
1751 | function Game() {
1752 | G = this;
1753 | G.isInProgress = true;
1754 | G.canSpeedBeIncreased = G.canExplode = true;
1755 | G.backgroundColor = '#fff';
1756 |
1757 | G.karma = 0;
1758 |
1759 | G.highscore = utils.getLocalStorageData() || 0;
1760 | G.isSound = utils.getLocalStorageData(true);
1761 | if (G.isSound !== 0) {
1762 | G.isSound = 1;
1763 | }
1764 |
1765 | G.resolution = 1;
1766 | G.curPos = [];
1767 |
1768 | G.can = document.querySelector('canvas');
1769 | G.can.width = P.w;
1770 | G.can.height = P.h;
1771 |
1772 | ctx = G.ctx = window.c = G.can.getContext('2d');
1773 |
1774 | G.trees = [];
1775 |
1776 | // Resizing
1777 | G.resize();
1778 | addEventListener('resize', G.resize, false);
1779 |
1780 | CC = document.getElementById('canvascontainer').style;
1781 |
1782 | document.body.addEventListener('touchstart', G.touchStart.bind(G), false);
1783 | document.body.addEventListener('touchmove', G.touchMove.bind(G), false);
1784 | document.body.addEventListener('touchend', G.touchEnd.bind(G), false);
1785 | document.body.addEventListener('mousedown', G.mouseDown.bind(G), false);
1786 | document.body.addEventListener('mousemove', G.mouseMove.bind(G), false);
1787 | document.body.addEventListener('mouseup', G.mouseUp.bind(G), false);
1788 |
1789 | document.body.addEventListener('keydown', G.keyDown.bind(G), false);
1790 | document.body.addEventListener('keyup', G.keyUp.bind(G), false);
1791 |
1792 | // Loop
1793 | G.frameCount = 0;
1794 | G.lastFrame = G.frameCountStart = Date.now();
1795 |
1796 | var displayablePixels = _.innerWidth * _.innerHeight * _.devicePixelRatio,
1797 | gamePixels = P.w * P.h,
1798 | ratio = displayablePixels / gamePixels;
1799 |
1800 | if (ratio < 0.5){
1801 | G.setResolution(ratio * 2);
1802 | }
1803 |
1804 | G.speed = 1;
1805 |
1806 | // background animation
1807 | // background = new Background();
1808 | flameBack.canvas = G.can;
1809 | //flameBack.init();
1810 |
1811 | G.menu = true;
1812 | }
1813 |
1814 | var tree, time;
1815 | Game.prototype = {
1816 | restart: function () {
1817 | G.isGameOver = false;
1818 | G.isInProgress = true;
1819 | G.karma = 0;
1820 | G.speed = 1;
1821 | G.gameStartTime = new Date().getTime();
1822 |
1823 | smoky = new SmokyFlame();
1824 |
1825 | blw = 200, bw =0;
1826 | G.addInitialtrees();
1827 |
1828 | player = new Player();
1829 | Pl.x = G.trees[0].x;
1830 |
1831 | flameBack.init();
1832 | weather = new Weather();
1833 | G.raf = raf(function(){
1834 | if (G.raf) {
1835 | G.cycle();
1836 | raf(arguments.callee);
1837 | }
1838 | });
1839 | },
1840 | stopCycle: function () {
1841 | G.isGameOver = true;
1842 | G.isInProgress = false;
1843 |
1844 | flameBack.update();
1845 | canvasToImage(); // get image before spash screen
1846 |
1847 | // console.log('Boom! DIE!');
1848 | // update high score
1849 | if (G.karma > G.highscore) {
1850 | SU.play('highestScore');
1851 | G.highscore = G.karma;
1852 | utils.setLocalStorageData(G.karma);
1853 | }
1854 |
1855 | SU.play('gameOver');
1856 |
1857 | G.menu = new Menu();
1858 | },
1859 | cycle: function () {
1860 | var now = new Date().getTime();
1861 | dt = now - time;
1862 |
1863 | if (dt < (1000 / fps))
1864 | return; // skip a frame
1865 |
1866 | //SU.play('game');
1867 | time = now;
1868 | if (G.menu) {
1869 | G.menu.update && G.menu.update();
1870 | return;
1871 | }
1872 |
1873 | if (G.canExplode && M.ceil((now - G.gameStartTime) / 1000) % 6 === 0) {
1874 | G.mildExplosion ? SU.play('explosion2') : SU.play('explosion1');
1875 | G.mildExplosion = !G.mildExplosion;
1876 | G.canExplode = false;
1877 | } else if (M.ceil((now - G.gameStartTime) / 1000) % 7 === 0) {
1878 | G.canExplode = true;
1879 | }
1880 |
1881 | if (G.canSpeedBeIncreased && M.ceil((now - G.gameStartTime) / 1000) % 10 === 0) {
1882 | G.speed += G.isMobile() ? 0.1 : 0.2;
1883 | WD.speed = utils.getRandomInt(1, 30);
1884 | G.canSpeedBeIncreased = false;
1885 | } else if (M.ceil((now - G.gameStartTime) / 1000) % 11 === 0) {
1886 | // G.speed += 0.1;
1887 | G.canSpeedBeIncreased = true;
1888 | }
1889 |
1890 | fs(G.backgroundColor);
1891 | fr(0, 0, CC.w, CC.h);
1892 |
1893 | var speedIncFactor = G.isMobile() ? 1.1 : 1.6;
1894 | if (G.speed >= speedIncFactor &&
1895 | utils.getRandomInt(0, 10) === 10
1896 | ) {
1897 | G.showNoisyScreen();
1898 | utils.getRandomInt(0, 10) === 4 && SU.play('glitch');
1899 | }
1900 |
1901 | //background.burnBurnBurn();
1902 | weather.update();
1903 |
1904 | ctx.font = '15px Comic Sans';
1905 | ctx.fillStyle = thisWeather.hexToRgb(thisWeather.getColor(true), 1.0);
1906 | ctx.fillText('KARMA: ' + G.karma, 25, 25);
1907 | ctx.fillText('SPEED: ' + G.speed.toFixed(1) + ' mph', G.can.width - 130, 25);
1908 | ctx.fillText('WIND: ' + WD.speed.toFixed(1) + ' mph W', G.can.width - 130, 45);
1909 | ctx.lineWidth = 3;
1910 |
1911 | if (G.trees.length) {
1912 | for (var i = 0; i < G.trees.length; i++) {
1913 | G.trees[i].x -= G.speed;
1914 | G.trees[i].update(G.trees[i]);
1915 |
1916 | if (G.trees[i].x < 0 - G.trees[i].width) {
1917 | G.trees[i] = new Tree();
1918 | }
1919 | }
1920 | player.update();
1921 | }
1922 | flameBack.update();
1923 | },
1924 | showNoisyScreen: function () {
1925 | var w = G.can.width,
1926 | h = G.can.height,
1927 | idata = ctx.createImageData(w, h),
1928 | buffer32 = new Uint32Array(idata.data.buffer),
1929 | len = buffer32.length,
1930 | i = 0;
1931 |
1932 | for (; i < len;) {
1933 | buffer32[i++] = ((255 * Math.random())|0) << 24;
1934 | }
1935 |
1936 | ctx.putImageData(idata, 0, 0);
1937 | },
1938 | addInitialtrees: function () {
1939 | G.trees = [];
1940 | G.trees.push(new Tree({isNoFlame: true}))
1941 | for (var i = 0; i < 5; i++) {
1942 | G.trees.push(new Tree())
1943 | }
1944 | },
1945 | resize: function() {
1946 | setTimeout(function(){
1947 | var maxWidth = innerWidth,
1948 | maxHeight = innerHeight,
1949 |
1950 | availableRatio = maxWidth / maxHeight,
1951 | baseRatio = P.w / P.h,
1952 | ratioDifference = abs(availableRatio - baseRatio),
1953 | width,
1954 | height,
1955 | s = document.getElementById('canvascontainer').style;
1956 |
1957 | if (availableRatio <= baseRatio){
1958 | width = maxWidth;
1959 | height = maxHeight;//width / baseRatio;
1960 | } else{
1961 | height = maxHeight;
1962 | width = height * baseRatio;
1963 | }
1964 |
1965 | s.width = width + 'px';
1966 | s.height = height + 'px';
1967 |
1968 | ctx.globalCompositeOperation="lighter";
1969 |
1970 | G.can.width = width;
1971 | G.can.height = height;
1972 |
1973 |
1974 | if (G.menu) {
1975 | G.menu = new Menu();
1976 | G.raf = raf(function(){
1977 | if (G.raf) {
1978 | G.cycle();
1979 | raf(arguments.callee);
1980 | }
1981 | });
1982 | return;
1983 | }
1984 |
1985 | G.restart();
1986 |
1987 | },100);
1988 | },
1989 | isMobile: function () {
1990 | if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i
1991 | .test(navigator.userAgent)) {
1992 | return true;
1993 | }
1994 | return false;
1995 | },
1996 | pos : function(e){
1997 | var rect = G.can.getBoundingClientRect(),
1998 | pos = [];
1999 |
2000 | e = e.touches || [e];
2001 |
2002 | for(var i = 0 ; i < e.length ; i++){
2003 | pos.push({
2004 | x : (e[i].clientX),// - rect.left) / (rect.width / P.w),
2005 | y : (e[i].clientY)// - rect.top) / (rect.height / P.h)
2006 | })
2007 | }
2008 |
2009 | return pos;
2010 | },
2011 | touchStart : function(e,m) {
2012 | e.preventDefault();
2013 | G.touch = G.touch || !m;
2014 | var p = G.pos(e);
2015 | G.curPos = p;
2016 |
2017 | scrollTo(0, 1);
2018 |
2019 | if (G.menu) {
2020 | var x = G.curPos[0].x - G.can.offsetLeft,
2021 | y = G.curPos[0].y - G.can.offsetTop;
2022 |
2023 | G.menu.mouseDown && G.menu.mouseDown(e, x, y);
2024 | } else {
2025 | // G.isTouching = true;
2026 | G.keyDown({keyCode: 32});
2027 | }
2028 |
2029 | if(!G.isInProgress) return;
2030 |
2031 | //G.world.touchStart();
2032 | },
2033 | touchMove : function(e) {
2034 | e.preventDefault();
2035 | if (G.curPos){
2036 | G.curPos = G.pos(e);
2037 |
2038 | if(!G.isInProgress) return;
2039 | //G.world.touchMove();
2040 | }
2041 | },
2042 | touchEnd : function(e) {
2043 | e.preventDefault();
2044 |
2045 | var p = G.curPos[0];
2046 | G.curPos = G.pos(e);
2047 |
2048 | if (!G.isInProgress) {
2049 | //!G.isInProgress.click(p.x, p.y);
2050 | } else {
2051 | //G.world.touchEnd();
2052 | }
2053 | },
2054 | keyDown: function(e) {
2055 | // 13 is enter
2056 | if ((e.keyCode === 13 || e.keyCode === 32) && G.menu) {
2057 | G.menu = null;
2058 | G.restart();
2059 | SU.play('playGame');
2060 | return;
2061 | }
2062 | if (!G.isInProgress) {
2063 | return;
2064 | }
2065 |
2066 | // 39 is right, 40 is down, 38 is up
2067 | if (e.keyCode === 39 || e.keyCode === 38 || e.keyCode === 32) {
2068 | player && player.keyDown(e.keyCode);
2069 | }
2070 | },
2071 | keyUp: function(e) {
2072 | if(!G.isInProgress) return;
2073 | player && player.keyUp(e.keyCode);
2074 | },
2075 | mouseDown: function(e) {
2076 | /*if(!G.touch){
2077 | G.touchStart(e, true);
2078 | }*/
2079 | if (G.menu) {
2080 | var x = e.pageX - G.can.offsetLeft,
2081 | y = e.pageY - G.can.offsetTop;
2082 |
2083 | G.menu.mouseDown && G.menu.mouseDown(e, x, y);
2084 | }
2085 | },
2086 | mouseMove: function(e) {
2087 | /*if(!G.touch){
2088 | G.touchMove(e);
2089 | }*/
2090 | },
2091 | mouseUp: function(e) {
2092 | /*if(!G.touch){
2093 | G.touchEnd(e);
2094 | }*/
2095 | },
2096 | setResolution: function(r) {
2097 | G.can.width = P.w * r;
2098 | G.can.height = P.h * r;
2099 |
2100 | G.resolution = r;
2101 | }
2102 | }
2103 | var _ = window,
2104 | raf = (function() {
2105 | return _.requestAnimationFrame ||
2106 | _.webkitRequestAnimationFrame ||
2107 | _.mozRequestAnimationFrame ||
2108 |
2109 | function(c){
2110 | setTimeout(c, 1000 / 60);
2111 | };
2112 | })(),
2113 | M = Math,
2114 | abs = M.abs,
2115 | min = M.min,
2116 | max = M.max,
2117 | to = setTimeout,
2118 | fps = 60;
2119 |
2120 | // Shortcuts
2121 | var p = CanvasRenderingContext2D.prototype;
2122 | p.fr = p.fillRect;
2123 | p.sv = p.save;
2124 | p.rs = p.restore;
2125 | p.lt = p.lineTo;
2126 | p.mt = p.moveTo;
2127 | p.sc = p.scale;
2128 | p.bp = p.beginPath;
2129 | p.cp = p.closePath;
2130 | p.rt = p.rotate;
2131 | p.ft = p.fillText;
2132 | p.bct = p.bezierCurveTo;
2133 | p.qct = p.quadraticCurveTo;
2134 | p.st = p.stroke;
2135 | p.ar = p.arc;
2136 | p.fl = p.fill;
2137 |
2138 | // ctx.ellipsis wont work in firefox
2139 | p.el = function drawEllipseWithQuatraticCurve(ctx, x, y, w, h, style) {
2140 | var kappa = .5522848,
2141 | ox = (w / 2) * kappa, // control point offset horizontal
2142 | oy = (h / 2) * kappa, // control point offset vertical
2143 | xe = x + w, // x-end
2144 | ye = y + h, // y-end
2145 | xm = x + w / 2, // x-middle
2146 | ym = y + h / 2; // y-middle
2147 |
2148 | sv();
2149 | bp();
2150 | mt(x, ym);
2151 | qct(x,y,xm,y);
2152 | qct(xe,y,xe,ym);
2153 | qct(xe,ye,xm,ye);
2154 | qct(x,ye,x,ym);
2155 | ctx.strokeStyle = style ? style : '#000';
2156 | ctx.lineWidth = 2;
2157 | st();
2158 | rs();
2159 | fl();
2160 | }
2161 |
2162 | p.fs = function(p){
2163 | this.fillStyle = P.inverted ? invert(p) : p;
2164 | };
2165 | p.sts = function(p){
2166 | this.strokeStyle = P.inverted ? invert(p) : p;
2167 | };
2168 |
2169 | // Adding all these functions to the global scope
2170 | for(var i in p){
2171 | _[i] = (function(f){
2172 | return function(){
2173 | c[f].apply(c, arguments);
2174 | }
2175 | })(i);
2176 | }
2177 |
2178 | var P = {
2179 | w: 640,
2180 | h: 760,
2181 | g: 800,
2182 | fireOffset: 70,
2183 | spikesOffset: 50,
2184 | tbOffset: 20
2185 | };
2186 |
2187 | function canvasToImage() {
2188 | G.dataURL = document.getElementById('game-canvas').toDataURL('image/png');
2189 | }
2190 |
2191 | function downloadCanvas() {
2192 | var windowRef = _.open();
2193 | if (windowRef) {
2194 | windowRef.document.write('
');
2195 | } else {
2196 | alert('Your browser prevented the window from opening. Please allow to view game screenshot.')
2197 | }
2198 | }
2199 |
2200 | addEventListener('DOMContentLoaded',function(){
2201 | _._can = document.querySelector('canvas');
2202 | new Game();
2203 | });
2204 |
2205 |
--------------------------------------------------------------------------------