diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..32df1bd --- /dev/null +++ b/.editorconfig @@ -0,0 +1,16 @@ +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = crlf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[{package.json}] +indent_style = space +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000..e4b8122 --- /dev/null +++ b/.jshintrc @@ -0,0 +1,93 @@ +{ + // JSHint Default Configuration File (as on JSHint website) + // See http://jshint.com/docs/ for more details + + "maxerr" : 50, // {int} Maximum error before stopping + + // Enforcing + "bitwise" : false, // true: Prohibit bitwise operators (&, |, ^, etc.) + "camelcase" : false, // true: Identifiers must be in camelCase + "curly" : true, // true: Require {} for every new block or scope + "eqeqeq" : true, // true: Require triple equals (===) for comparison + "forin" : true, // true: Require filtering for..in loops with obj.hasOwnProperty() + "freeze" : true, // true: prohibits overwriting prototypes of native objects such as Array, Date etc. + "immed" : true, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());` + "indent" : 4, // {int} Number of spaces to use for indentation + "latedef" : true, // true: Require variables/functions to be defined before being used + "newcap" : true, // true: Require capitalization of all constructor functions e.g. `new F()` + "noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee` + "noempty" : true, // true: Prohibit use of empty blocks + "nonbsp" : true, // true: Prohibit "non-breaking whitespace" characters. + "nonew" : false, // true: Prohibit use of constructors for side-effects (without assignment) + "plusplus" : false, // true: Prohibit use of `++` & `--` + "quotmark" : false, // Quotation mark consistency: + // false : do nothing (default) + // true : ensure whatever is used is consistent + // "single" : require single quotes + // "double" : require double quotes + "undef" : true, // true: Require all non-global variables to be declared (prevents global leaks) + "unused" : true, // true: Require all defined variables be used + "strict" : false, // true: Requires all functions run in ES5 Strict Mode + "maxparams" : false, // {int} Max number of formal params allowed per function + "maxdepth" : false, // {int} Max depth of nested blocks (within functions) + "maxstatements" : false, // {int} Max number statements per function + "maxcomplexity" : false, // {int} Max cyclomatic complexity per function + "maxlen" : false, // {int} Max number of characters per line + + // Relaxing + "asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons) + "boss" : false, // true: Tolerate assignments where comparisons would be expected + "debug" : false, // true: Allow debugger statements e.g. browser breakpoints. + "eqnull" : false, // true: Tolerate use of `== null` + "es5" : true, // true: Allow ES5 syntax (ex: getters and setters) + "esnext" : true, // true: Allow ES.next (ES6) syntax (ex: `const`) + "elision" : true, + "moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features) + // (ex: `for each`, multiple try/catch, function expression…) + "evil" : false, // true: Tolerate use of `eval` and `new Function()` + "expr" : false, // true: Tolerate `ExpressionStatement` as Programs + "funcscope" : false, // true: Tolerate defining variables inside control statements + "globalstrict" : false, // true: Allow global "use strict" (also enables 'strict') + "iterator" : false, // true: Tolerate using the `__iterator__` property + "lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block + "laxbreak" : false, // true: Tolerate possibly unsafe line breakings + "laxcomma" : false, // true: Tolerate comma-first style coding + "loopfunc" : false, // true: Tolerate functions being defined in loops + "multistr" : false, // true: Tolerate multi-line strings + "noyield" : false, // true: Tolerate generator functions with no yield statement in them. + "notypeof" : false, // true: Tolerate invalid typeof operator values + "proto" : false, // true: Tolerate using the `__proto__` property + "scripturl" : false, // true: Tolerate script-targeted URLs + "shadow" : false, // true: Allows re-define variables later in code e.g. `var x=1; x=2;` + "sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation + "supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;` + "validthis" : false, // true: Tolerate using this in a non-constructor function + + // Environments + "browser" : true, // Web Browser (window, document, etc) + "browserify" : false, // Browserify (node.js code in the browser) + "couch" : false, // CouchDB + "devel" : true, // Development/debugging (alert, confirm, etc) + "dojo" : false, // Dojo Toolkit + "jasmine" : false, // Jasmine + "jquery" : false, // jQuery + "mocha" : false, // Mocha + "mootools" : false, // MooTools + "node" : true, // Node.js + "nonstandard" : false, // Widely adopted globals (escape, unescape, etc) + "prototypejs" : false, // Prototype and Scriptaculous + "qunit" : false, // QUnit + "rhino" : false, // Rhino + "shelljs" : false, // ShellJS + "worker" : false, // Web Workers + "wsh" : false, // Windows Scripting Host + "yui" : false, // Yahoo User Interface + + // Custom Globals + // additional predefined global variables + "globals" : { + "game": true, + "jsfxr": true, + "Stats": true + } +} diff --git a/app/index.html b/app/index.html index 61a92f5..55a1d29 100644 --- a/app/index.html +++ b/app/index.html @@ -10,61 +10,76 @@ - -
-
-
-
-
-
-
- LEVEL: - SCORE: -
- -
-
-

Controls

-

Swap/Move elements: Click highlighted column to select an element and click to drop to another column

-

Pause: P, Mute: M,
Stop bg anim: B
(because of performance issues on some devices)

-
-
-

Credits

-

Created by: Zoltan Pasztor & Csaba Tuncsik

-

Audio Processing: [JSFXR](https://github.com/mneubrand/jsfxr) by [@markusneubrand](https://twitter.com/markusneubrand)

-

Game sound inspired by: [@jackrugile](https://twitter.com/jackrugile) (http://codepen.io/jackrugile/blog/arcade-audio-for-js13k-games)

-

Game random number utility inspired by: # JS13KGames Boilerplate (https://github.com/ooflorent/js13k-boilerplate/blob/master/src/rng.js)

-
-
-

FEWA

-
    -
  • RESUME
  • -
  • NEW GAME
  • -
  • HELP
  • -
  • CREDITS
  • -
-
+ +
+
+
+
+
+
+
+ LEVEL: + SCORE: +
+ +
+
+

Controls

+ +

Swap/Move elements: Click highlighted column to select an element and click to drop to another + column

+ +

Pause: P, Mute: M,
Stop bg anim: B
(because + of performance issues on some devices)

+
+
+

Credits

+ +

Created by: Zoltan Pasztor & Csaba Tuncsik

+ +

Audio Processing: [JSFXR](https://github.com/mneubrand/jsfxr) + by [@markusneubrand](https://twitter.com/markusneubrand) +

+ +

Game sound inspired by: [@jackrugile](https://twitter.com/jackrugile) + (http://codepen.io/jackrugile/blog/arcade-audio-for-js13k-games) +

+ +

Game random number utility inspired by: # JS13KGames Boilerplate (https://github.com/ooflorent/js13k-boilerplate/blob/master/src/rng.js) +

+
+
+

FEWA

+
    +
  • RESUME
  • +
  • NEW GAME
  • +
  • HELP
  • +
  • CREDITS
  • +
- - - - - - - - - - - - - - - - - - - +
+ + + + + + + + + + + + + + + + + + + + diff --git a/app/scripts/audio.js b/app/scripts/audio.js index de49bcd..d066b1f 100644 --- a/app/scripts/audio.js +++ b/app/scripts/audio.js @@ -1,103 +1,107 @@ -game.fx = { - 'click': { - count: 4, - params: [ - [2, , 0.06, , 0.61, 0.58, , 0.0092, -0.0248, 0.0059, 0.9077, -1, -0.855, , -1, , -0.24, -0.4599, 0.9991, -0.647, -0.5371, , -1, 0.33] - ] - }, - 'hover': { - count: 4, - params: [ - [2, 0.46, 0.01, 0.0058, , 0.29, , -0.06, -0.02, , , -0.7, 0.5, , -1, , -0.5125, -0.2453, 0.4399, -0.7, , 0.0058, -1, 0.51] - ] - }, - 'select': { - count: 4, - params: [ - [2, , 0.0138, , 0.86, 0.52, , 0.04, -0.04, , , -1, , , -1, , -1, -0.56, 0.63, -0.4, 0.35, , -1, 0.16] - ] - }, - 'move': { - count: 4, - params: [ - [2, 0.18, 0.01, , 0.3702, 0.14, , -0.0799, 0.26, 0.41, 0.67, -1, , , -1, , -0.1999, -0.3199, 0.46, -0.0799, , , -1, 0.4] - ] - }, - 'hit': { - count: 18, - params: [ - [2, 0.16, 0.18, , , 0.4, , -0.3199, -0.4399, , , -1, , , -1, , -1, -1, 0.74, -1, , 0.54, -1, 0.55] - ] - }, - 'explosion': { - count: 4, - params: [ - [3, , 0.11, 0.81, 0.4543, 0.11, , -1, -1, , , 0.36, 0.21, 0.45, -0.6599, , -0.04, 0.72, 0.26, -0.4399, , , -0.64, 0.34] +(function () { + "use strict"; - ] - }, - 'explosionBig': { - count: 4, - params: [ - [3, , 0.87, , , 0.22, , , -0.14, , , -1, , , -1, , 0.08, -0.28, 0.44, -0.3399, , , -1, 0.17] - ] - }, - 'levelup': { - count: 1, - params: [ - [3, 0.0061, 0.1462, 0.0048, 0.9138, 0.5027, , 0.065, , , 0.6737, 0.1527, , , -0.9567, , 0.081, -0.0307, 0.071, 0.0945, , 0.0002, , 0.26] - ] - }, - 'death': { - count: 1, - params: [ - [0, 0.0001, 0.7618, 0.1376, 0.81, 0.1, , 0.3358, 0.0003, , , 0.3006, , , 0.048, 0.6803, -1, -0.76, 0.9985, 0.6463, 0.3453, , -1, 0.27] - ] - } -}; + game.fx = { + 'click': { + count: 4, + params: [ + [2, , 0.06, , 0.61, 0.58, , 0.0092, -0.0248, 0.0059, 0.9077, -1, -0.855, , -1, , -0.24, -0.4599, 0.9991, -0.647, -0.5371, , -1, 0.33] + ] + }, + 'hover': { + count: 4, + params: [ + [2, 0.46, 0.01, 0.0058, , 0.29, , -0.06, -0.02, , , -0.7, 0.5, , -1, , -0.5125, -0.2453, 0.4399, -0.7, , 0.0058, -1, 0.51] + ] + }, + 'select': { + count: 4, + params: [ + [2, , 0.0138, , 0.86, 0.52, , 0.04, -0.04, , , -1, , , -1, , -1, -0.56, 0.63, -0.4, 0.35, , -1, 0.16] + ] + }, + 'move': { + count: 4, + params: [ + [2, 0.18, 0.01, , 0.3702, 0.14, , -0.0799, 0.26, 0.41, 0.67, -1, , , -1, , -0.1999, -0.3199, 0.46, -0.0799, , , -1, 0.4] + ] + }, + 'hit': { + count: 18, + params: [ + [2, 0.16, 0.18, , , 0.4, , -0.3199, -0.4399, , , -1, , , -1, , -1, -1, 0.74, -1, , 0.54, -1, 0.55] + ] + }, + 'explosion': { + count: 4, + params: [ + [3, , 0.11, 0.81, 0.4543, 0.11, , -1, -1, , , 0.36, 0.21, 0.45, -0.6599, , -0.04, 0.72, 0.26, -0.4399, , , -0.64, 0.34] -game.audio = { - sounds: {}, - references: [], - mute: false, - play: function(sound) { - if (!game.audio.mute) { - var audio = game.audio.sounds[sound]; - if (audio.length > 1) { - audio = game.audio.sounds[sound][game.rand.range(0, audio.length)]; - } else { - audio = game.audio.sounds[sound][0]; - } - audio.pool[audio.tick].play(); - if (audio.tick < audio.count - 1) { - audio.tick++; - } else { - audio.tick = 0; + ] + }, + 'explosionBig': { + count: 4, + params: [ + [3, , 0.87, , , 0.22, , , -0.14, , , -1, , , -1, , 0.08, -0.28, 0.44, -0.3399, , , -1, 0.17] + ] + }, + 'levelup': { + count: 1, + params: [ + [3, 0.0061, 0.1462, 0.0048, 0.9138, 0.5027, , 0.065, , , 0.6737, 0.1527, , , -0.9567, , 0.081, -0.0307, 0.071, 0.0945, , 0.0002, , 0.26] + ] + }, + 'death': { + count: 1, + params: [ + [0, 0.0001, 0.7618, 0.1376, 0.81, 0.1, , 0.3358, 0.0003, , , 0.3006, , , 0.048, 0.6803, -1, -0.76, 0.9985, 0.6463, 0.3453, , -1, 0.27] + ] + } + }; + + game.audio = { + sounds: {}, + references: [], + mute: false, + play: function (sound) { + if (!game.audio.mute) { + var audio = game.audio.sounds[sound]; + if (audio.length > 1) { + audio = game.audio.sounds[sound][game.rand.range(0, audio.length)]; + } else { + audio = game.audio.sounds[sound][0]; + } + audio.pool[audio.tick].play(); + if (audio.tick < audio.count - 1) { + audio.tick++; + } else { + audio.tick = 0; + } } } - } -}; + }; -for (var k in game.fx) { - if (game.fx.hasOwnProperty(k)) { - game.audio.sounds[k] = []; + for (var k in game.fx) { + if (game.fx.hasOwnProperty(k)) { + game.audio.sounds[k] = []; - game.fx[k].params.forEach(function(elem, index) { - game.audio.sounds[k].push({ - tick: 0, - count: game.fx[k].count, - pool: [] - }); + game.fx[k].params.forEach(function (elem, index) { + game.audio.sounds[k].push({ + tick: 0, + count: game.fx[k].count, + pool: [] + }); - for (var i = 0; i < game.fx[k].count; i++) { - var audio = new Audio(); - if (typeof jsfxr !== "undefined") { - audio.src = jsfxr(elem); + for (var i = 0; i < game.fx[k].count; i++) { + var audio = new Audio(); + if (typeof jsfxr !== "undefined") { + audio.src = jsfxr(elem); + } + game.audio.references.push(audio); + game.audio.sounds[k][index].pool.push(audio); } - game.audio.references.push(audio); - game.audio.sounds[k][index].pool.push(audio); - } - }); + }); + } } -} +})(); diff --git a/app/scripts/brick.js b/app/scripts/brick.js index 474938b..97c846c 100644 --- a/app/scripts/brick.js +++ b/app/scripts/brick.js @@ -1,146 +1,135 @@ -game.brick = function(p) { - this.column = p.column; - this.type = p.type; - this.width = game.brickWidth; - this.height = game.brickHeight; - this.x = this.column * this.width + game.gap * (this.column + 1); - this.y = -this.height; - this.hw = this.width / 2; - this.hh = this.height / 2; - this.rad = Math.round(this.width / 10); - this.removed = false; - this.col = game.elements[this.type].color; - this.add = 0; - this.explode = game.noop; - this.sprite = game.sprite.get(this.type + '-BRICK-' + p.item); - this.val = this.energy = game.elements[this.type].bricks[p.item].energy; +(function () { + "use strict"; + game.brick = function (p) { + this.column = p.column; + this.type = p.type; + this.width = game.brickWidth; + this.height = game.brickHeight; + this.x = this.column * this.width + game.gap * (this.column + 1); + this.y = -this.height; + this.hw = this.width / 2; + this.hh = this.height / 2; + this.rad = Math.round(this.width / 10); + this.removed = false; + this.col = game.elements[this.type].color; + this.add = 0; + this.explode = game.noop; + this.sprite = game.sprite.get(this.type + '-BRICK-' + p.item); + this.val = this.energy = game.elements[this.type].bricks[p.item].energy; - if (p.item === 'special') { - switch (p.type) { - case 'FIRE': - this.explode = function() { - game.rumble.level = 10; - var b = game.bricks[this.column], - n = b.length; - while (n--) { - if (b[n] && !b[n].removed) { - b[n].hitType = b[n].type; - b[n].destroy(); - } - } - game.audio.play('explosionBig'); - }; - break; - case 'EARTH': - this.explode = function() { - game.rumble.level = 10; - var n = game.columns, - r, b; - while (n--) { - r = game.bricks[n].length; - while (r--) { - b = game.bricks[n][r]; - if (b && !b.removed && b.row >= this.row - 1 && b.row <= this.row + 1 && b.column >= this.column - 1 && b.column <= this.column + 1) { - b.hitType = b.type; - b.destroy(); + if (p.item === 'special') { + switch (p.type) { + case 'FIRE': + this.explode = function () { + game.rumble.level = 10; + game.arr.loop(game.bricks[this.column], function (brick, idx) { + if (brick && !brick.removed) { + brick.hitType = brick.type; + brick.destroy(); } - } - } - game.audio.play('explosionBig'); - }; - break; - case 'WATER': - this.explode = function() { - game.rumble.level = 10; - var n = game.columns, - r; - while (n--) { - if (n !== this.column) { - r = game.bricks[n].length; - while (r--) { - if (game.bricks[n][r] && !game.bricks[n][r].removed && game.bricks[n][r].row === this.row) { - game.bricks[n][r].hitType = game.bricks[n][r].type; - game.bricks[n][r].destroy(); + }); + game.audio.play('explosionBig'); + }; + break; + case 'EARTH': + this.explode = function () { + game.rumble.level = 10; + for (var i = 0, l = game.columns; i < l; i += 1) { + game.arr.loop(game.bricks[i], function (brick, idx) { + if (brick && !brick.removed && brick.row >= this.row - 1 && brick.row <= this.row + 1 && brick.column >= this.column - 1 && brick.column <= this.column + 1) { + brick.hitType = brick.type; + brick.destroy(); } - } + }, this); } - } - game.audio.play('explosionBig'); - }; - break; - case 'AIR': - this.explode = function() { - game.rumble.level = 10; - var n = game.columns, - r, b; - while (n--) { - r = game.bricks[n].length; - while (r--) { - b = game.bricks[n][r]; - if (b && !b.removed && b.type === this.type) { - b.hitType = b.type; - b.destroy(); + game.audio.play('explosionBig'); + }; + break; + case 'WATER': + this.explode = function () { + game.rumble.level = 10; + for (var i = 0, l = game.columns; i < l; i += 1) { + if (i !== this.column) { + game.arr.loop(game.bricks[i], function (brick, idx) { + if (brick && !brick.removed && brick.row === this.row) { + brick.hitType = brick.type; + brick.destroy(); + } + }, this); } } - } - game.audio.play('explosionBig'); - }; - break; + game.audio.play('explosionBig'); + }; + break; + case 'AIR': + this.explode = function () { + game.rumble.level = 10; + for (var i = 0, l = game.columns; i < l; i += 1) { + game.arr.loop(game.bricks[i], function (brick, idx) { + if (brick && !brick.removed && brick.type === this.type) { + brick.hitType = brick.type; + brick.destroy(); + } + }, this); + } + game.audio.play('explosionBig'); + }; + break; + } } - } -}; + }; -game.brick.prototype.destroy = function(noscore) { - this.removed = true; - if (this.type === this.hitType) { - this.explode(); - game.flash = 8; - } - game.rumble.level = 5; - var sz = 12, - pcs = 10; - while (pcs--) { - game.particles.push(new game.particle({ - name: 'particle' + this.col, - x: this.x + this.width / 2, - y: this.y + this.height, - w: this.width / sz, - h: this.height / (sz / 2), - col: this.col, - speed: game.rand.range(1, 5), - dist: game.rand.range(3, 5) * 100 - })); - } - if (!noscore) { - game.countScore(this.val); - } - game.audio.play('explosion'); -}; + game.brick.prototype.destroy = function (noscore) { + this.removed = true; + if (this.type === this.hitType) { + this.explode(); + game.flash = 8; + } + game.rumble.level = 5; + var size = 12; + for (var i = 0, l = 10; i < l; i+=1) { + game.particles.push(new game.particle({ + name: 'particle' + this.col, + x: this.x + this.width / 2, + y: this.y + this.height, + w: this.width / size, + h: this.height / (size / 2), + col: this.col, + speed: game.rand.range(1, 5), + dist: game.rand.range(3, 5) * 100 + })); + } + if (!noscore) { + game.countScore(this.val); + } + game.audio.play('explosion'); + }; -game.brick.prototype.hit = function(p, t) { - this.hitType = t; - this.energy -= p; - if (this.energy <= 0 && !this.removed) { - this.destroy(); - } - game.audio.play('hit'); -}; + game.brick.prototype.hit = function (p, t) { + this.hitType = t; + this.energy -= p; + if (this.energy <= 0 && !this.removed) { + this.destroy(); + } + game.audio.play('hit'); + }; -game.brick.prototype.update = function() { - this.y += game.timer.move + this.add; - if (this.add === 0) { - if (game.s !== 0 && this.y > game.stage.height - this.height - game.brickWidth) { - game.over(); + game.brick.prototype.update = function () { + this.y += game.timer.move + this.add; + if (this.add === 0) { + if (game.s !== 0 && this.y > game.stage.height - this.height - game.brickWidth) { + game.over(); + } + } else { + this.add -= game.gravity; } - } else { - this.add -= game.gravity; - } -}; + }; -game.brick.prototype.draw = function() { - game.ctx.save(); - game.ctx.translate(this.x, this.y); - game.ctx.drawImage(this.sprite, 0, 0); - game.ctx.restore(); -}; + game.brick.prototype.draw = function () { + game.ctx.save(); + game.ctx.translate(this.x, this.y); + game.ctx.drawImage(this.sprite, 0, 0); + game.ctx.restore(); + }; +})(); diff --git a/app/scripts/bullet.js b/app/scripts/bullet.js index fb30f3f..bea4da5 100644 --- a/app/scripts/bullet.js +++ b/app/scripts/bullet.js @@ -1,75 +1,78 @@ -game.bullet = function(p) { - this.column = p.column; - this.type = p.type; - this.speed = 5; - this.width = game.brickWidth; - this.height = game.brickHeight; - this.x = this.column * this.width + game.gap * (this.column + 1); - this.y = game.stage.height - game.brickWidth; - this.hw = this.width / 2; - this.hh = this.height / 2; - this.rad = 1; - this.removed = false; - this.power = 100; - this.sprite = game.sprite.get(this.type + '-SHOT'); -}; +(function () { + "use strict"; -game.bullet.prototype.destroy = function() { - game.explosions.push(new game.explosion({ - name: 'explosion#ffffff', - x: this.x + this.hw, - y: this.y, - w: this.power / 4, - h: this.power / 4, - col: '#ffffff', - delta: 2, - rot: 0 - })); - game.explosions.push(new game.explosion({ - name: 'explosion#ed8500', - x: this.x + this.hw, - y: this.y, - w: this.power / 7, - h: this.power / 7, - col: '#ed8500', - delta: 1.5, - rot: -5 - })); - game.explosions.push(new game.explosion({ - name: 'explosion#ffff00', - x: this.x + this.hw, - y: this.y, - w: this.power / 8, - h: this.power / 8, - col: '#ffff00', - delta: 1, - rot: 4 - })); - this.removed = true; -}; + game.bullet = function (p) { + this.column = p.column; + this.type = p.type; + this.speed = 5; + this.width = game.brickWidth; + this.height = game.brickHeight; + this.x = this.column * this.width + game.gap * (this.column + 1); + this.y = game.stage.height - game.brickWidth; + this.hw = this.width / 2; + this.hh = this.height / 2; + this.rad = 1; + this.removed = false; + this.power = 100; + this.sprite = game.sprite.get(this.type + '-SHOT'); + }; -game.bullet.prototype.update = function() { - this.y -= this.speed * game.timer.delta; + game.bullet.prototype.destroy = function () { + [].push.apply(game.explosions, + [ + new game.explosion({ + name: 'explosion#ffffff', + x: this.x + this.hw, + y: this.y, + w: this.power / 4, + h: this.power / 4, + col: '#ffffff', + delta: 2, + rot: 0 + }), + new game.explosion({ + name: 'explosion#ed8500', + x: this.x + this.hw, + y: this.y, + w: this.power / 7, + h: this.power / 7, + col: '#ed8500', + delta: 1.5, + rot: -5 + }), + new game.explosion({ + name: 'explosion#ffff00', + x: this.x + this.hw, + y: this.y, + w: this.power / 8, + h: this.power / 8, + col: '#ffff00', + delta: 1, + rot: 4 + }) + ] + ); + this.removed = true; + }; - var b = game.bricks[this.column], - n = b.length; - - while (n--) { - if (b[n] !== null && b[n].y > this.y - game.brickHeight) { - this.power = game.elements[this.type].against[b[n].type]; - if (this.type === b[n].type){ - game.rumble.level = 1; + game.bullet.prototype.update = function () { + this.y -= this.speed * game.timer.delta; + game.arr.loop(game.bricks[this.column], function (brick, idx) { + if (brick !== null && brick.y > this.y - game.brickHeight) { + this.power = game.elements[this.type].against[brick.type]; + if (this.type === brick.type) { + game.rumble.level = 1; + } + brick.hit(this.power, this.type); + this.destroy(); } - this.destroy(); - b[n].hit(this.power, this.type); - } - } - -}; + }, this); + }; -game.bullet.prototype.draw = function() { - game.ctx.save(); - game.ctx.translate(this.x, this.y); - game.ctx.drawImage(this.sprite, 0, 0); - game.ctx.restore(); -}; + game.bullet.prototype.draw = function () { + game.ctx.save(); + game.ctx.translate(this.x, this.y); + game.ctx.drawImage(this.sprite, 0, 0); + game.ctx.restore(); + }; +})(); diff --git a/app/scripts/config.js b/app/scripts/config.js index c27d83e..d9def50 100644 --- a/app/scripts/config.js +++ b/app/scripts/config.js @@ -1,113 +1,113 @@ -game.obj.extend(true, game, { - ww: window.innerWidth, - wh: window.innerHeight, - columns: 8, - gap: 2, - score: 0, - speed: 0.1, - gravity: 0.3, - paused: false, - moveBg: true, - rumble: { - x: 0, - y: 0, - level: 0, - decay: 0.4 - }, - starAmount: Math.floor((window.innerWidth / window.innerHeight) * 100), - starColors: ["#ffffff", "#ffe9c4", "#d4fbff"], - elements: { - FIRE: { - color: '#ff3824', - latency: 50, - against: { - FIRE: 200, - EARTH: 20, - WATER: 10, - AIR: 50 - }, - bricks: { - empty: { - energy: 100 +(function () { + "use strict"; + + game.obj.extend(true, game, { + columns: 8, + gap: 2, + speed: 0.1, + gravity: 0.3, + moveBg: true, + rumble: { + x: 0, + y: 0, + level: 0, + decay: 0.4 + }, + starAmount: Math.floor((window.innerWidth / window.innerHeight) * 100), + starColors: ["#ffffff", "#ffe9c4", "#d4fbff"], + elements: { + FIRE: { + color: '#ff3824', + latency: 50, + against: { + FIRE: 200, + EARTH: 20, + WATER: 10, + AIR: 50 }, - filled: { - energy: 250 + bricks: { + empty: { + energy: 100 + }, + filled: { + energy: 250 + }, + special: { + energy: 400 + } }, - special: { - energy: 400 - } + desc: "The Fire element, most effective against fire bricks, but least effective against water bricks.", + spec: "Fire special brick explodes the whole column of bricks." }, - desc: "The Fire element, most effective against fire bricks, but least effective against water bricks.", - spec: "Fire special brick explodes the whole column of bricks." - }, - EARTH: { - color: '#44db5e', - latency: 80, - against: { - FIRE: 50, - EARTH: 200, - WATER: 20, - AIR: 10 - }, - bricks: { - empty: { - energy: 100 + EARTH: { + color: '#44db5e', + latency: 80, + against: { + FIRE: 50, + EARTH: 200, + WATER: 20, + AIR: 10 }, - filled: { - energy: 250 + bricks: { + empty: { + energy: 100 + }, + filled: { + energy: 250 + }, + special: { + energy: 400 + } }, - special: { - energy: 400 - } + desc: "The Earth element, most effective against earth bricks, but least effective against air bricks.", + spec: "Earth special brick explodes surrounding bricks." }, - desc: "The Earth element, most effective against earth bricks, but least effective against air bricks.", - spec: "Earth special brick explodes surrounding bricks." - }, - WATER: { - color: '#54c7fc', - latency: 70, - against: { - FIRE: 10, - EARTH: 20, - WATER: 200, - AIR: 50 - }, - bricks: { - empty: { - energy: 100 + WATER: { + color: '#54c7fc', + latency: 70, + against: { + FIRE: 10, + EARTH: 20, + WATER: 200, + AIR: 50 }, - filled: { - energy: 250 + bricks: { + empty: { + energy: 100 + }, + filled: { + energy: 250 + }, + special: { + energy: 400 + } }, - special: { - energy: 400 - } - }, - desc: "The Water element, most effective against water bricks, but least effective against fire bricks.", - spec: "Water special brick explodes the whole row of bricks." - }, - AIR: { - color: '#ffcd00', - latency: 60, - against: { - FIRE: 50, - EARTH: 10, - WATER: 20, - AIR: 200 + desc: "The Water element, most effective against water bricks, but least effective against fire bricks.", + spec: "Water special brick explodes the whole row of bricks." }, - bricks: { - empty: { - energy: 100 + AIR: { + color: '#ffcd00', + latency: 60, + against: { + FIRE: 50, + EARTH: 10, + WATER: 20, + AIR: 200 }, - filled: { - energy: 250 + bricks: { + empty: { + energy: 100 + }, + filled: { + energy: 250 + }, + special: { + energy: 400 + } }, - special: { - energy: 400 - } - }, - desc: "The Air element, most effective against air bricks, but least effective against earth bricks.", - spec: "Air special brick explodes all the weak(stroked) air bricks." + desc: "The Air element, most effective against air bricks, but least effective against earth bricks.", + spec: "Air special brick explodes all the weak(stroked) air bricks." + } } - } -}); \ No newline at end of file + }); +})(); diff --git a/app/scripts/core.js b/app/scripts/core.js index 6851005..1766b85 100644 --- a/app/scripts/core.js +++ b/app/scripts/core.js @@ -1,570 +1,508 @@ -game.obj.extend(true, game, { - timer: { - date: new Date(), - curr: null, - timestamp: Date.now(), - delta: 1, - msec: 0, - move: 0, - t: 1E3 / 60, - tick: function() { - game.timer.curr = Date.now(); - game.timer.d = game.timer.curr - game.timer.timestamp; - game.timer.delta = game.timer.d / game.timer.t; - game.timer.delta = (game.timer.delta < 0) ? 0.001 : game.timer.delta; - game.timer.delta = (game.timer.delta > 10) ? 10 : game.timer.delta; - game.timer.msec += game.timer.delta; - game.timer.timestamp = game.timer.curr; - game.timer.move = game.s * game.timer.delta; - } - }, - emitter: { - sum: 0, - row: 0, - emit: function() { - if (game.emitter.sum > game.brickHeight + game.gap || game.emitter.sum === 0) { - game.emitter.sum = 0; - var c = game.columns, - b, randType, randItem; - while (c--) { - randType = game.rand.select(game.elementTypes); - randItem = game.rand.select(Object.keys(game.elements[randType].bricks)); - b = new game.brick({ - column: c, - type: randType, - item: randItem - }); - b.row = game.emitter.row; - game.bricks[c].unshift(b); - } - game.emitter.row++; - game.s += 0.001; - } - game.emitter.sum += game.timer.move; - } - }, - init: function() { - game.flash = 0; - game.elementTypes = Object.keys(game.elements); - game.transformName = game.vendor.get('transform'); - game.brickWidth = (game.stage.width / game.columns - game.gap) - (game.gap / game.columns); - game.brickHeight = game.brickWidth / 2 + game.gap; - game.prevLevel = game.level; - game.sprite.factory(); - game.initBackground(); - game.controls(); - game.stats(); - game.menu(); - }, - message: function(cont, delay, back) { - var ms = game.msg.style, - b; - game.mnu.style.display = 'none'; - if (cont) { - if (typeof cont === 'object') { - game.msg.innerHTML = ''; - cont.style.display = 'block'; - game.msg.appendChild(cont); - } else { - game.msg.innerHTML = cont; - } - if (back) { - b = game.dom.create('em'); - b.innerHTML = back; - game.msg.appendChild(b); +(function () { + "use strict"; + + game.obj.extend(true, game, { + timer: { + date: new Date(), + curr: null, + timestamp: Date.now(), + delta: 1, + msec: 0, + move: 0, + t: 1E3 / 60, + tick: function () { + game.timer.curr = Date.now(); + game.timer.d = game.timer.curr - game.timer.timestamp; + game.timer.delta = game.timer.d / game.timer.t; + game.timer.delta = (game.timer.delta < 0) ? 0.001 : game.timer.delta; + game.timer.delta = (game.timer.delta > 10) ? 10 : game.timer.delta; + game.timer.msec += game.timer.delta; + game.timer.timestamp = game.timer.curr; + game.timer.move = game.s * game.timer.delta; } - ms.opacity = 0; - ms.display = 'block'; - ms.marginTop = -game.msg.offsetHeight / 2 + 'px'; - ms.opacity = 1; - } else { - ms.display = 'none'; - } - if (delay) { - setTimeout(function() { - ms.display = 'none'; - }, delay); - } - }, - newgame: function() { - game.bricks = game.arr.create(game.columns); - game.towers = []; - game.bullets = []; - game.particles = []; - game.explosions = []; - game.activeColumn = null; - game.hoverColumn = null; - - var c, arr = (game.elementTypes.concat(game.arr.create(game.columns - 4, 0))).sort(function() { - return 0.5 - Math.random(); - }); - - for (c = 0; c < arr.length; c++) { - game.towers[c] = arr[c] ? new game.tower({ - column: c, - type: arr[c] - }) : {}; - } - game.score = 0; - game.s = game.speed; - game.countScore(0); - game.paused = false; - game.msg.style.display = 'none'; - }, - over: function() { - var t = 600; - game.s = 0; - game.message('GAME OVER', 0, 'MAIN MENU'); - game.audio.play('death'); - game.towers.forEach(function(tw, n) { - setTimeout(function() { - if (tw && tw.x) { - tw.destroy(); + }, + emitter: { + sum: 0, + row: 0, + emit: function () { + if (game.emitter.sum > game.brickHeight + game.gap || game.emitter.sum === 0) { + game.emitter.sum = 0; + var brick, randType, randItem; + for (var i = 0, l = game.columns; i < l; i += 1) { + randType = game.rand.select(game.elementTypes); + randItem = game.rand.select(Object.keys(game.elements[randType].bricks)); + brick = new game.brick({ + column: i, + type: randType, + item: randItem + }); + brick.row = game.emitter.row; + game.bricks[i].unshift(brick); + } + game.emitter.row++; + game.s += 0.001; } - }, n * 100); - }); + game.emitter.sum += game.timer.move; + } + }, + initBackground: function () { + var star = null; + for (var i = 0, l = game.bgs.length; i < l; i += 1) { + game.bgCtx.clearRect(0, 0, game.bgCtx.canvas.width, game.bgCtx.canvas.height); + for (var ii = 0, ll = game.starAmount; ii < ll; ii += 1) { + star = new game.star(game.bgs.length - i + 1); + star.draw(); - game.bricks.forEach(function(column) { - setTimeout(function() { - column.forEach(function(brick) { - if (brick && !brick.removed) { - brick.add = -0.001; - } - }); - }, t); - t += 100; - }); - }, - pause: function() { - if (game.paused) { - game.paused = false; - game.message(); - } else { - game.paused = true; - game.message('PAUSE'); - } - }, - mute: function() { - game.audio.mute = !game.audio.mute; - }, - noBg: function() { - game.moveBg = !game.moveBg; - }, - menu: function(resume) { - game.paused = true; - if (resume && game.bricks) { - game.re.style.display = 'block'; - } - game.mnu.style.display = 'block'; - game.msg.style.display = 'none'; - }, - help: function() { - if (!game.buildHelp) { - for (var element in game.elements) { - if (game.elements.hasOwnProperty(element)) { - var k = element, - v = game.elements[element], - el = game.dom.create('div'), - ttl = game.dom.create('h2'), - desc = game.dom.create('p'), - spec = game.dom.create('p'); - - ttl.innerHTML = k + '
'; - desc.innerHTML = v.desc; - spec.innerHTML = v.spec; - el.appendChild(ttl); - desc.insertBefore(game.sprite.get(k), desc.firstChild); - el.appendChild(desc); - spec.insertBefore(game.sprite.get(k + '-BRICK-special'), spec.firstChild); - el.appendChild(spec); - game.hlp.appendChild(el); } + game.bgs[i].b.style.backgroundImage = 'url(' + game.bg.toDataURL() + ')'; } - game.buildHelp = true; - } - game.message(game.hlp, 0, 'MAIN MENU'); - }, - credits: function() { - game.message(game.crd, 0, 'MAIN MENU'); - }, - countScore: function(v) { - game.score += v; - game.level = Math.ceil(game.s / 0.12); - game.hud.score.innerHTML = game.score; - game.hud.level.innerHTML = game.level; - if (game.level > game.prevLevel) { - game.prevLevel = game.level; - game.message('LEVEL UP', 2000); - game.audio.play('levelup'); - } - }, - stats: function() { - var stats; - if (typeof Stats === 'function') { - stats = new Stats(); - stats.setMode(0); - stats.domElement.style.position = 'absolute'; - stats.domElement.style.left = '0px'; - stats.domElement.style.top = '0px'; - document.body.appendChild(stats.domElement); - game.stats = stats; - } else { - game.stats = { - begin: game.noop, - end: game.noop - }; - } - }, - rumble: { - update: function() { - if (game.rumble.level > 0) { - game.rumble.level -= game.rumble.decay; - game.rumble.level = (game.rumble.level < 0) ? 0 : game.rumble.level; - game.rumble.x = game.rand.rangef(-game.rumble.level, game.rumble.level); - game.rumble.y = game.rand.rangef(-game.rumble.level, game.rumble.level); - } else { - game.rumble.x = 0; - game.rumble.y = 0; + }, + noBg: function () { + game.moveBg = !game.moveBg; + }, + mute: function () { + game.audio.mute = !game.audio.mute; + }, + menu: function (resume) { + game.paused = true; + if (resume && game.bricks) { + game.re.style.display = 'block'; } + game.mnu.style.display = 'block'; + game.msg.style.display = 'none'; + }, + newgame: function () { + game.bricks = game.arr.create(game.columns); + game.towers = []; + game.bullets = []; + game.particles = []; + game.explosions = []; + game.activeColumn = null; + game.hoverColumn = null; + + var c, arr = (game.elementTypes.concat(game.arr.create(game.columns - 4, 0))).sort(function () { + return 0.5 - Math.random(); + }); + + game.arr.loop(arr, function (obj, idx) { + game.towers[idx] = arr[idx] ? new game.tower({ + column: idx, + type: arr[idx] + }) : false; + }); + + game.score = 0; + game.s = game.speed; + game.countScore(0); + game.paused = false; + game.msg.style.display = 'none'; + }, + over: function () { + var t = 600; + game.s = 0; + game.message('GAME OVER', 0, 'MAIN MENU'); + game.audio.play('death'); + game.towers.forEach(function (tw, n) { + setTimeout(function () { + if (tw && tw.x) { + tw.destroy(); + } + }, n * 100); + }); + + game.bricks.forEach(function (column) { + setTimeout(function () { + column.forEach(function (brick) { + if (brick && !brick.removed) { + brick.add = -0.001; + } + }); + }, t); + t += 100; + }); }, - draw: function() { - if (game.rumble.x !== 0 || game.rumble.y !== 0) { - game.stage.style[game.transformName] = "translate(" + game.rumble.x + "px," + game.rumble.y + "px)"; + pause: function () { + if (game.paused) { + game.paused = false; + game.message(); + } else { + game.paused = true; + game.message('PAUSE'); } - } - }, - update: function() { - - var c = game.bricks.length, - n; - - while (c--) { - n = game.bricks[c].length; - while (n--) { - if (game.bricks[c][n]) { - if (game.bricks[c][n].removed) { - game.bricks[c].splice(n, 1); - } else { - game.bricks[c][n].update(); + }, + help: function () { + if (!game.buildHelp) { + for (var element in game.elements) { + if (game.elements.hasOwnProperty(element)) { + var k = element, + v = game.elements[element], + el = game.dom.create('div'), + ttl = game.dom.create('h2'), + desc = game.dom.create('p'), + spec = game.dom.create('p'); + + ttl.innerHTML = k + '
'; + desc.innerHTML = v.desc; + spec.innerHTML = v.spec; + el.appendChild(ttl); + desc.insertBefore(game.sprite.get(k), desc.firstChild); + el.appendChild(desc); + spec.insertBefore(game.sprite.get(k + '-BRICK-special'), spec.firstChild); + el.appendChild(spec); + game.hlp.appendChild(el); } } + game.buildHelp = true; } - } - - n = game.towers.length; - while (n--) { - if (game.towers[n].width) { - if (game.towers[n].removed) { - game.towers.splice(n, 1); + game.message(game.hlp, 0, 'MAIN MENU'); + }, + message: function (cont, delay, back) { + var ms = game.msg.style, + b; + game.mnu.style.display = 'none'; + if (cont) { + if (typeof cont === 'object') { + game.msg.innerHTML = ''; + cont.style.display = 'block'; + game.msg.appendChild(cont); } else { - game.towers[n].update(); + game.msg.innerHTML = cont; } + if (back) { + b = game.dom.create('em'); + b.innerHTML = back; + game.msg.appendChild(b); + } + ms.opacity = 0; + ms.display = 'block'; + ms.marginTop = -game.msg.offsetHeight / 2 + 'px'; + ms.opacity = 1; + } else { + ms.display = 'none'; } - } + if (delay) { + setTimeout(function () { + ms.display = 'none'; + }, delay); + } + }, + credits: function () { + game.message(game.crd, 0, 'MAIN MENU'); + }, + controls: function () { + + window.addEventListener('keyup', function (e) { + switch (e.keyCode) { + case 27: + game.menu(true); + break; + case 80: + game.pause(); + break; + case 77: + game.mute(); + break; + case 66: + game.noBg(); + break; + } + }, false); + game.ng.addEventListener('click', function () { + game.newgame(); + game.audio.play('click'); + game.mnu.style.display = 'none'; + }, false); - n = game.bullets.length; - while (n--) { - if (game.bullets[n]) { - if (game.bullets[n].removed) { - game.bullets.splice(n, 1); + game.hl.addEventListener('click', function () { + game.help(); + game.audio.play('click'); + }, false); - } else { - game.bullets[n].update(); - } - } - } + game.re.addEventListener('click', function () { + game.pause(); + }, false); + + ['hlp', 'msg', 'crd'].forEach(function (el) { + game[el].addEventListener('click', function () { + game.menu(); + }, false); + }); + + game.cr.addEventListener('click', function () { + game.credits(); + game.audio.play('click'); + }, false); + + ['ng', 'hl', 'cr', 're'].forEach(function (el) { + game[el].addEventListener('mouseover', function () { + game.audio.play('hover'); + }, false); + }); - n = game.particles.length; - while (n--) { - if (game.particles[n]) { - if (game.particles[n].removed) { - game.particles.splice(n, 1); + game.cnt.addEventListener('mousemove', function (e) { + if (!game.paused && game.s > 0) { + var x = e.pageX - this.offsetLeft, + col = Math.floor(x / (game.brickWidth + game.gap + game.gap / game.columns)); + + game.hoverColumn = col; } else { - game.particles[n].update(); + game.hoverColumn = null; } - } - } + }, false); - n = game.explosions.length; - while (n--) { - if (game.explosions[n]) { - if (game.explosions[n].removed) { - game.explosions.splice(n, 1); + game.cnt.addEventListener('click', function (e) { + if (!game.paused && game.s > 0) { + var x = e.pageX - this.offsetLeft, + col = Math.floor(x / (game.brickWidth + game.gap + game.gap / game.columns)); + if (game.towers[col] && !game.towers[col].removed) { + // clicked to tower + if (game.activeColumn === null) { + // selection (first) click + game.activeColumn = col; + game.audio.play('select'); + } else { + // switch towers + game.towers[game.activeColumn].setColumn(col); + game.towers[col].setColumn(game.activeColumn); + game.arr.move(game.towers, game.activeColumn, col); + game.activeColumn = null; + game.audio.play('move'); + } + } else { + if (game.activeColumn !== null) { + game.towers[game.activeColumn].setColumn(col); + game.arr.move(game.towers, game.activeColumn, col); + game.audio.play('move'); + } + game.activeColumn = null; + } } else { - game.explosions[n].update(); + game.activeColumn = null; } + }, false); + }, + stats: function () { + var stats; + if (typeof Stats === 'function') { + stats = new Stats(); + stats.setMode(0); + stats.domElement.style.position = 'absolute'; + stats.domElement.style.left = '0px'; + stats.domElement.style.top = '0px'; + document.body.appendChild(stats.domElement); + game.stats = stats; + } else { + game.stats = { + begin: game.noop, + end: game.noop + }; } - } - - game.rumble.update(); - }, - draw: function() { - game.ctx.save(); - game.ctx.clearRect(0, 0, game.ctx.canvas.width, game.ctx.canvas.height); - var c = game.bricks.length, - n; - - game.drawFlash(); - - while (c--) { - n = game.bricks[c].length; - while (n--) { - if (game.bricks[c][n] !== null) { - game.bricks[c][n].draw(); - } + }, + countScore: function (v) { + game.score += v; + game.level = Math.ceil(game.s / 0.12); + game.hud.score.innerHTML = game.score; + game.hud.level.innerHTML = game.level; + if (game.level > game.prevLevel) { + game.prevLevel = game.level; + game.message('LEVEL UP', 2000); + game.audio.play('levelup'); } - } + }, + update: function () { + + game.arr.loop(game.bricks, function (bricks, row) { + game.arr.loop(bricks, function (brick, col) { + if (brick) { + if (brick.removed) { + bricks.splice(col, 1); + } else { + brick.update(); + } + } + }); + }); + + game.arr.loop([game.towers, game.bullets, game.particles, game.explosions], function (objects) { + game.arr.loop(objects, function (obj, idx) { + if (obj) { + if (obj.removed) { + objects.splice(idx, 1); + } else { + obj.update(); + } + } + }); + }); - n = game.towers.length; - while (n--) { - if (game.towers[n].width) { - game.towers[n].draw(); - } - } + game.rumble.update(); + }, + updateBackground: function () { + if (game.moveBg) { + for (var i = 0, l = game.bgs.length; i < l; i += 1) { + game.bgs[i].c += (i + 1); + game.bgs[i].b.style.backgroundPosition = '0px ' + (game.bgs[i].c / 3) + 'px'; - n = game.bullets.length; - while (n--) { - if (game.bullets[n].width) { - game.bullets[n].draw(); + } } - } - - - n = game.particles.length; - while (n--) { - if (game.particles[n] !== null) { - game.particles[n].draw(); + }, + rumble: { + update: function () { + if (game.rumble.level > 0) { + game.rumble.level -= game.rumble.decay; + game.rumble.level = (game.rumble.level < 0) ? 0 : game.rumble.level; + game.rumble.x = game.rand.rangef(-game.rumble.level, game.rumble.level); + game.rumble.y = game.rand.rangef(-game.rumble.level, game.rumble.level); + } else { + game.rumble.x = 0; + game.rumble.y = 0; + } + }, + draw: function () { + if (game.rumble.x !== 0 || game.rumble.y !== 0) { + game.stage.style[game.transformName] = "translate(" + game.rumble.x + "px," + game.rumble.y + "px)"; + } } - } + }, + draw: function () { + game.ctx.save(); + game.ctx.clearRect(0, 0, game.ctx.canvas.width, game.ctx.canvas.height); - n = game.explosions.length; - while (n--) { - if (game.explosions[n] !== null) { - game.explosions[n].draw(); - } - } + game.arr.loop(game.bricks, function (bricks) { + game.arr.loop(bricks, function (brick) { + if (brick && !brick.removed) { + brick.draw(); + } + }); + }); - game.rumble.draw(); - game.drawSelection(); - - }, - initBackground: function() { - var star = null, - s, - b = game.bgs.length; - - while (b--) { - game.bgCtx.clearRect(0, 0, game.bgCtx.canvas.width, game.bgCtx.canvas.height); - s = game.starAmount; - while (s--) { - star = new game.star(game.bgs.length - b + 1); - star.draw(); - } + game.arr.loop([game.towers, game.bullets, game.particles, game.explosions], function (objects) { + game.arr.loop(objects, function (obj) { + if (obj && !obj.removed) { + obj.draw(); + } + }); + }); - game.bgs[b].b.style.backgroundImage = 'url(' + game.bg.toDataURL() + ')'; - } - }, - updateBackground: function() { - if (game.moveBg) { - var b = game.bgs.length; - while (b--) { - game.bgs[b].c += (b + 1); - game.bgs[b].b.style.backgroundPosition = '0px ' + (game.bgs[b].c / 3) + 'px'; - } + game.drawFlash(); + game.rumble.draw(); + game.drawSelection(); - } - }, - drawSelection: function() { - var ac, hc; - if (game.activeColumn !== null) { - ac = game.towers[game.activeColumn]; - if (ac && ac.x) { + }, + drawSelection: function () { + var ac, hc; + if (game.activeColumn !== null) { + ac = game.towers[game.activeColumn]; + if (ac && ac.x) { + game.ctx.save(); + game.ctx.translate(ac.x, 0); + game.ctx.lineWidth = 2; + game.ctx.fillStyle = game.elements[ac.type].color; + game.ctx.globalAlpha = 0.2; + game.ctx.fillRect(0, 0, game.brickWidth, game.stage.height); + game.ctx.restore(); + } + } + if (game.hoverColumn !== null) { + ac = game.towers[game.activeColumn]; + hc = game.towers[game.hoverColumn]; + var col = '#ffffff'; + if (hc && hc.x) { + col = game.elements[hc.type].color; + } + if (ac && ac.x) { + col = game.elements[ac.type].color; + } game.ctx.save(); - game.ctx.translate(ac.x, 0); + game.ctx.translate(game.gap + game.hoverColumn * (game.brickWidth + game.gap), 0); game.ctx.lineWidth = 2; - game.ctx.fillStyle = game.elements[ac.type].color; + game.ctx.fillStyle = col; game.ctx.globalAlpha = 0.2; game.ctx.fillRect(0, 0, game.brickWidth, game.stage.height); game.ctx.restore(); } - } - if (game.hoverColumn !== null) { - ac = game.towers[game.activeColumn]; - hc = game.towers[game.hoverColumn]; - var col = '#ffffff'; - if (hc && hc.x) { - col = game.elements[hc.type].color; - } - if (ac && ac.x) { - col = game.elements[ac.type].color; - } - game.ctx.save(); - game.ctx.translate(game.gap + game.hoverColumn * (game.brickWidth + game.gap), 0); - game.ctx.lineWidth = 2; - game.ctx.fillStyle = col; - game.ctx.globalAlpha = 0.2; - game.ctx.fillRect(0, 0, game.brickWidth, game.stage.height); - game.ctx.restore(); - } - }, - drawFlash: function() { - if (game.flash > 0) { - game.flash--; - f.style.opacity = game.flash / 10; - } - }, - controls: function() { - - window.addEventListener('keyup', function(e) { - switch (e.keyCode) { - case 27: - game.menu(true); - break; - case 80: - game.pause(); - break; - case 77: - game.mute(); - break; - case 66: - game.noBg(); - break; - } - }, false); - - game.ng.addEventListener('click', function() { - game.newgame(); - game.audio.play('click'); - game.mnu.style.display = 'none'; - }, false); - - game.hl.addEventListener('click', function() { - game.help(); - game.audio.play('click'); - }, false); - - game.re.addEventListener('click', function() { - game.pause(); - }, false); - - ['hlp', 'msg', 'crd'].forEach(function(el) { - game[el].addEventListener('click', function() { - game.menu(); - }, false); - }); - - game.cr.addEventListener('click', function() { - game.credits(); - game.audio.play('click'); - }, false); - - ['ng', 'hl', 'cr', 're'].forEach(function(el) { - game[el].addEventListener('mouseover', function() { - game.audio.play('hover'); - }, false); - }); - - game.cnt.addEventListener('mousemove', function(e) { - if (!game.paused && game.s > 0) { - var x = e.pageX - this.offsetLeft, - col = Math.floor(x / (game.brickWidth + game.gap + game.gap / game.columns)); - - game.hoverColumn = col; - } else { - game.hoverColumn = null; - } - }, false); - - game.cnt.addEventListener('click', function(e) { - if (!game.paused && game.s > 0) { - var x = e.pageX - this.offsetLeft, - col = Math.floor(x / (game.brickWidth + game.gap + game.gap / game.columns)); - if (game.towers[col] && game.towers[col].width) { - // clicked to tower - if (game.activeColumn === null) { - // selection (first) click - game.activeColumn = col; - game.audio.play('select'); - } else { - // switch towers - game.towers[game.activeColumn].setColumn(col); - game.towers[col].setColumn(game.activeColumn); - game.arr.move(game.towers, game.activeColumn, col); - game.activeColumn = null; - game.audio.play('move'); - } - } else { - if (game.activeColumn !== null) { - game.towers[game.activeColumn].setColumn(col); - game.arr.move(game.towers, game.activeColumn, col); - game.audio.play('move'); - } - game.activeColumn = null; - } - } else { - game.activeColumn = null; - } - }, false); - } -}); - -window.onload = function() { - - game.obj.extend(true, game, { - rumble: { - body: game.dom.get("r") }, - hud: { - level: game.dom.get("lv"), - score: game.dom.get('sc') + drawFlash: function () { + if (game.flash > 0) { + game.flash--; + game.f.style.opacity = game.flash / 10; + } }, - cnt: game.dom.get("cnt"), - msg: game.dom.get("msg"), - mnu: game.dom.get("mnu"), - hlp: game.dom.get("hlp"), - crd: game.dom.get("crd"), - re: game.dom.get("re"), - ng: game.dom.get("ng"), - hl: game.dom.get("hl"), - cr: game.dom.get("cr"), - f: game.dom.get("f"), - stage: game.dom.get("c"), - bg: game.dom.get('b'), - bgs: [{ - b: game.dom.get("b1"), - c: 0 - }, { - b: game.dom.get("b2"), - c: 0 - }, { - b: game.dom.get("b3"), - c: 0 - }] - }); - - game.stage.width = game.wh / 2; - game.stage.height = game.wh - 30; - game.cnt.style.width = game.stage.width + 'px'; - game.cnt.style.height = game.wh + 'px'; - game.bg.width = game.ww; - game.bg.height = game.wh; + init: function () { + + game.obj.extend(true, game, { + ww: window.innerWidth, + wh: window.innerHeight, + rumble: { + body: game.dom.get("r") + }, + hud: { + level: game.dom.get("lv"), + score: game.dom.get('sc') + }, + cnt: game.dom.get("cnt"), + msg: game.dom.get("msg"), + mnu: game.dom.get("mnu"), + hlp: game.dom.get("hlp"), + crd: game.dom.get("crd"), + re: game.dom.get("re"), + ng: game.dom.get("ng"), + hl: game.dom.get("hl"), + cr: game.dom.get("cr"), + f: game.dom.get("f"), + stage: game.dom.get("c"), + bg: game.dom.get('b'), + bgs: [{ + b: game.dom.get("b1"), + c: 0 + }, { + b: game.dom.get("b2"), + c: 0 + }, { + b: game.dom.get("b3"), + c: 0 + }] + }); + + game.stage.width = game.wh / 2; + game.stage.height = game.wh - 30; + game.cnt.style.width = game.stage.width + 'px'; + game.cnt.style.height = game.wh + 'px'; + game.bg.width = game.ww; + game.bg.height = game.wh; + game.brickWidth = (game.stage.width / game.columns - game.gap) - (game.gap / game.columns); + game.brickHeight = game.brickWidth / 2 + game.gap; + + game.obj.extend(true, game, { + ctx: game.stage.getContext('2d'), + bgCtx: game.bg.getContext('2d'), + elementTypes: Object.keys(game.elements), + transformName: game.vendor.get('transform'), + prevLevel: 0, + score: 0, + flash: 0, + paused: true + }); + + for (var i = 0; i < 3; i++) { + game.bgs[i].width = game.ww; + game.bgs[i].height = game.wh; + } - game.obj.extend(true, game, { - ctx: game.stage.getContext('2d'), - bgCtx: game.bg.getContext('2d') + game.sprite.factory(); + game.initBackground(); + game.controls(); + game.stats(); + game.menu(); + } }); - for (var i = 0; i < 3; i++) { - game.bgs[i].width = game.ww; - game.bgs[i].height = game.wh; - } - game.init(); (function loop() { + requestAnimationFrame(loop, game.stage.canvas); game.timer.tick(); - window.requestAnimationFrame(loop, game.stage.canvas); game.stats.begin(); game.updateBackground(); if (!game.paused) { @@ -574,4 +512,5 @@ window.onload = function() { } game.stats.end(); })(); -}; + +})(); diff --git a/app/scripts/entity.js b/app/scripts/entity.js index 9861f83..5af4474 100644 --- a/app/scripts/entity.js +++ b/app/scripts/entity.js @@ -1,30 +1,30 @@ -game.entity = function(p) { - if (p) { - this.x = p.x; - this.y = p.y; - this.width = p.w; - this.height = p.h; - this.col = p.col; +(function () { + "use strict"; + + game.entity = function (p) { + this.x = p.x || 0; + this.y = p.y || 0; + this.w = p.w || 0; + this.h = p.h || 0; + this.col = p.col || 0; + this.name = p.name || ''; this.removed = false; - this.sprite = game.sprite.get(p.name); - if (!this.sprite) { - game.sprite.create(p.name, this.width, this.height, function(c) { - c.fillStyle = p.col; - c.fillRect(0, 0, p.w, p.h); - }); - } - this.sprite = game.sprite.get(p.name); - } -}; + this.sprite = game.sprite.create(this.name, this.w, this.h, function (ctx) { + ctx.fillStyle = this.col; + ctx.fillRect(0, 0, this.w, this.h); + }.bind(this)); -game.entity.prototype.destroy = function() { - this.removed = true; -}; + }; -game.entity.prototype.draw = function() { - game.ctx.translate(this.x, this.y); - if (this.rotate) { - game.ctx.rotate(this.rotate * game.RAD); - } - game.ctx.drawImage(this.sprite, -this.width / 2, -this.height / 2, this.width, this.height); -}; + game.entity.prototype.destroy = function () { + this.removed = true; + }; + + game.entity.prototype.draw = function () { + game.ctx.translate(this.x, this.y); + if (this.rotate) { + game.ctx.rotate(this.rotate * game.RAD); + } + game.ctx.drawImage(this.sprite, -this.w / 2, -this.h / 2, this.w, this.h); + }; +})(); diff --git a/app/scripts/explosion.js b/app/scripts/explosion.js index 16d7934..4b3594b 100644 --- a/app/scripts/explosion.js +++ b/app/scripts/explosion.js @@ -1,29 +1,33 @@ -game.explosion = function(p) { - game.entity.apply(this, arguments); - this.distance = 0; - this.rotate = game.rand.range(0, 360); - this.rot = p.rot || game.rand.range(-5, 5); - this.delta = p.delta || 0.1; - this.maxDistance = game.rand.range(this.delta * 8, this.delta * 15); -}; +(function () { + "use strict"; -game.explosion.prototype = new game.entity(); -game.explosion.prototype.constructor = game.explosion; + game.explosion = function (p) { + game.entity.apply(this, arguments); + this.distance = 0; + this.rotate = game.rand.range(0, 360); + this.rot = p.rot || game.rand.range(-5, 5); + this.delta = p.delta || 0.1; + this.maxDistance = game.rand.range(this.delta * 8, this.delta * 15); + }; -game.explosion.prototype.update = function() { - this.distance += this.delta; - this.width += this.delta; - this.height += this.delta; - this.rotate += this.rot; - if (this.distance > this.maxDistance) { - this.distance = this.maxDistance; - this.removed = true; - } -}; + game.explosion.prototype = Object.create(game.entity.prototype); + game.explosion.prototype.constructor = game.explosion; -game.explosion.prototype.draw = function() { - game.ctx.save(); - game.ctx.globalAlpha = 1 - this.distance / this.maxDistance; - game.entity.prototype.draw.call(this); - game.ctx.restore(); -}; + game.explosion.prototype.update = function () { + this.distance += this.delta; + this.width += this.delta; + this.height += this.delta; + this.rotate += this.rot; + if (this.distance > this.maxDistance) { + this.distance = this.maxDistance; + this.destroy(); + } + }; + + game.explosion.prototype.draw = function () { + game.ctx.save(); + game.ctx.globalAlpha = 1 - this.distance / this.maxDistance; + game.entity.prototype.draw.call(this); + game.ctx.restore(); + }; +})(); diff --git a/app/scripts/game.js b/app/scripts/game.js new file mode 100644 index 0000000..286be48 --- /dev/null +++ b/app/scripts/game.js @@ -0,0 +1 @@ +var game = {}; diff --git a/app/scripts/jsfxr.js b/app/scripts/jsfxr.js index 5e4e255..70141da 100644 --- a/app/scripts/jsfxr.js +++ b/app/scripts/jsfxr.js @@ -1,482 +1,483 @@ -/** - * SfxrParams - * - * Copyright 2010 Thomas Vian - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * @author Thomas Vian - */ -/** @constructor */ -function SfxrParams() { - //-------------------------------------------------------------------------- - // - // Settings String Methods - // - //-------------------------------------------------------------------------- - - /** - * Parses a settings array into the parameters - * @param array Array of the settings values, where elements 0 - 23 are - * a: waveType - * b: attackTime - * c: sustainTime - * d: sustainPunch - * e: decayTime - * f: startFrequency - * g: minFrequency - * h: slide - * i: deltaSlide - * j: vibratoDepth - * k: vibratoSpeed - * l: changeAmount - * m: changeSpeed - * n: squareDuty - * o: dutySweep - * p: repeatSpeed - * q: phaserOffset - * r: phaserSweep - * s: lpFilterCutoff - * t: lpFilterCutoffSweep - * u: lpFilterResonance - * v: hpFilterCutoff - * w: hpFilterCutoffSweep - * x: masterVolume - * @return If the string successfully parsed - */ - this.setSettings = function(values) - { - for ( var i = 0; i < 24; i++ ) - { - this[String.fromCharCode( 97 + i )] = values[i] || 0; - } - - // I moved this here from the reset(true) function - if (this['c'] < .01) { - this['c'] = .01; - } +(function () { + "use strict"; + + /** + * SfxrParams + * + * Copyright 2010 Thomas Vian + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @author Thomas Vian + */ + /** @constructor */ + function SfxrParams() { + //-------------------------------------------------------------------------- + // + // Settings String Methods + // + //-------------------------------------------------------------------------- + + /** + * Parses a settings array into the parameters + * @param array Array of the settings values, where elements 0 - 23 are + * a: waveType + * b: attackTime + * c: sustainTime + * d: sustainPunch + * e: decayTime + * f: startFrequency + * g: minFrequency + * h: slide + * i: deltaSlide + * j: vibratoDepth + * k: vibratoSpeed + * l: changeAmount + * m: changeSpeed + * n: squareDuty + * o: dutySweep + * p: repeatSpeed + * q: phaserOffset + * r: phaserSweep + * s: lpFilterCutoff + * t: lpFilterCutoffSweep + * u: lpFilterResonance + * v: hpFilterCutoff + * w: hpFilterCutoffSweep + * x: masterVolume + * @return If the string successfully parsed + */ + this.setSettings = function (values) { + for (var i = 0; i < 24; i++) { + this[String.fromCharCode(97 + i)] = values[i] || 0; + } - var totalTime = this['b'] + this['c'] + this['e']; - if (totalTime < .18) { - var multiplier = .18 / totalTime; - this['b'] *= multiplier; - this['c'] *= multiplier; - this['e'] *= multiplier; - } - } -} - -/** - * SfxrSynth - * - * Copyright 2010 Thomas Vian - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * @author Thomas Vian - */ -/** @constructor */ -function SfxrSynth() { - // All variables are kept alive through function closures - - //-------------------------------------------------------------------------- - // - // Sound Parameters - // - //-------------------------------------------------------------------------- - - this._params = new SfxrParams(); // Params instance - - //-------------------------------------------------------------------------- - // - // Synth Variables - // - //-------------------------------------------------------------------------- - - var _envelopeLength0, // Length of the attack stage - _envelopeLength1, // Length of the sustain stage - _envelopeLength2, // Length of the decay stage - - _period, // Period of the wave - _maxPeriod, // Maximum period before sound stops (from minFrequency) - - _slide, // Note slide - _deltaSlide, // Change in slide - - _changeAmount, // Amount to change the note by - _changeTime, // Counter for the note change - _changeLimit, // Once the time reaches this limit, the note changes - - _squareDuty, // Offset of center switching point in the square wave - _dutySweep; // Amount to change the duty by - - //-------------------------------------------------------------------------- - // - // Synth Methods - // - //-------------------------------------------------------------------------- - - /** - * Resets the runing variables from the params - * Used once at the start (total reset) and for the repeat effect (partial reset) - */ - this.reset = function() { - // Shorter reference - var p = this._params; - - _period = 100 / (p['f'] * p['f'] + .001); - _maxPeriod = 100 / (p['g'] * p['g'] + .001); - - _slide = 1 - p['h'] * p['h'] * p['h'] * .01; - _deltaSlide = -p['i'] * p['i'] * p['i'] * .000001; - - if (!p['a']) { - _squareDuty = .5 - p['n'] / 2; - _dutySweep = -p['o'] * .00005; - } + // I moved this here from the reset(true) function + if (this['c'] < .01) { + this['c'] = .01; + } - _changeAmount = 1 + p['l'] * p['l'] * (p['l'] > 0 ? -.9 : 10); - _changeTime = 0; - _changeLimit = p['m'] == 1 ? 0 : (1 - p['m']) * (1 - p['m']) * 20000 + 32; - } - - // I split the reset() function into two functions for better readability - this.totalReset = function() { - this.reset(); - - // Shorter reference - var p = this._params; - - // Calculating the length is all that remained here, everything else moved somewhere - _envelopeLength0 = p['b'] * p['b'] * 100000; - _envelopeLength1 = p['c'] * p['c'] * 100000; - _envelopeLength2 = p['e'] * p['e'] * 100000 + 12; - // Full length of the volume envelop (and therefore sound) - // Make sure the length can be divided by 3 so we will not need the padding "==" after base64 encode - return ((_envelopeLength0 + _envelopeLength1 + _envelopeLength2) / 3 | 0) * 3; - } - - /** - * Writes the wave to the supplied buffer ByteArray - * @param buffer A ByteArray to write the wave to - * @return If the wave is finished - */ - this.synthWave = function(buffer, length) { - // Shorter reference - var p = this._params; - - // If the filters are active - var _filters = p['s'] != 1 || p['v'], - // Cutoff multiplier which adjusts the amount the wave position can move - _hpFilterCutoff = p['v'] * p['v'] * .1, - // Speed of the high-pass cutoff multiplier - _hpFilterDeltaCutoff = 1 + p['w'] * .0003, - // Cutoff multiplier which adjusts the amount the wave position can move - _lpFilterCutoff = p['s'] * p['s'] * p['s'] * .1, - // Speed of the low-pass cutoff multiplier - _lpFilterDeltaCutoff = 1 + p['t'] * .0001, - // If the low pass filter is active - _lpFilterOn = p['s'] != 1, - // masterVolume * masterVolume (for quick calculations) - _masterVolume = p['x'] * p['x'], - // Minimum frequency before stopping - _minFreqency = p['g'], - // If the phaser is active - _phaser = p['q'] || p['r'], - // Change in phase offset - _phaserDeltaOffset = p['r'] * p['r'] * p['r'] * .2, - // Phase offset for phaser effect - _phaserOffset = p['q'] * p['q'] * (p['q'] < 0 ? -1020 : 1020), - // Once the time reaches this limit, some of the iables are reset - _repeatLimit = p['p'] ? ((1 - p['p']) * (1 - p['p']) * 20000 | 0) + 32 : 0, - // The punch factor (louder at begining of sustain) - _sustainPunch = p['d'], - // Amount to change the period of the wave by at the peak of the vibrato wave - _vibratoAmplitude = p['j'] / 2, - // Speed at which the vibrato phase moves - _vibratoSpeed = p['k'] * p['k'] * .01, - // The type of wave to generate - _waveType = p['a']; - - var _envelopeLength = _envelopeLength0, // Length of the current envelope stage - _envelopeOverLength0 = 1 / _envelopeLength0, // (for quick calculations) - _envelopeOverLength1 = 1 / _envelopeLength1, // (for quick calculations) - _envelopeOverLength2 = 1 / _envelopeLength2; // (for quick calculations) - - // Damping muliplier which restricts how fast the wave position can move - var _lpFilterDamping = 5 / (1 + p['u'] * p['u'] * 20) * (.01 + _lpFilterCutoff); - if (_lpFilterDamping > .8) { - _lpFilterDamping = .8; - } - _lpFilterDamping = 1 - _lpFilterDamping; - - var _finished = false, // If the sound has finished - _envelopeStage = 0, // Current stage of the envelope (attack, sustain, decay, end) - _envelopeTime = 0, // Current time through current enelope stage - _envelopeVolume = 0, // Current volume of the envelope - _hpFilterPos = 0, // Adjusted wave position after high-pass filter - _lpFilterDeltaPos = 0, // Change in low-pass wave position, as allowed by the cutoff and damping - _lpFilterOldPos, // Previous low-pass wave position - _lpFilterPos = 0, // Adjusted wave position after low-pass filter - _periodTemp, // Period modified by vibrato - _phase = 0, // Phase through the wave - _phaserInt, // Integer phaser offset, for bit maths - _phaserPos = 0, // Position through the phaser buffer - _pos, // Phase expresed as a Number from 0-1, used for fast sin approx - _repeatTime = 0, // Counter for the repeats - _sample, // Sub-sample calculated 8 times per actual sample, averaged out to get the super sample - _superSample, // Actual sample writen to the wave - _vibratoPhase = 0; // Phase through the vibrato sine wave - - // Buffer of wave values used to create the out of phase second wave - var _phaserBuffer = new Array(1024), - // Buffer of random values used to generate noise - _noiseBuffer = new Array(32); - for (var i = _phaserBuffer.length; i--; ) { - _phaserBuffer[i] = 0; - } - for (var i = _noiseBuffer.length; i--; ) { - _noiseBuffer[i] = Math.random() * 2 - 1; + var totalTime = this['b'] + this['c'] + this['e']; + if (totalTime < .18) { + var multiplier = .18 / totalTime; + this['b'] *= multiplier; + this['c'] *= multiplier; + this['e'] *= multiplier; + } + } } - for (var i = 0; i < length; i++) { - if (_finished) { - return i; - } + /** + * SfxrSynth + * + * Copyright 2010 Thomas Vian + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @author Thomas Vian + */ + /** @constructor */ + function SfxrSynth() { + // All variables are kept alive through function closures + + //-------------------------------------------------------------------------- + // + // Sound Parameters + // + //-------------------------------------------------------------------------- + + this._params = new SfxrParams(); // Params instance + + //-------------------------------------------------------------------------- + // + // Synth Variables + // + //-------------------------------------------------------------------------- + + var _envelopeLength0, // Length of the attack stage + _envelopeLength1, // Length of the sustain stage + _envelopeLength2, // Length of the decay stage + + _period, // Period of the wave + _maxPeriod, // Maximum period before sound stops (from minFrequency) + + _slide, // Note slide + _deltaSlide, // Change in slide + + _changeAmount, // Amount to change the note by + _changeTime, // Counter for the note change + _changeLimit, // Once the time reaches this limit, the note changes + + _squareDuty, // Offset of center switching point in the square wave + _dutySweep; // Amount to change the duty by + + //-------------------------------------------------------------------------- + // + // Synth Methods + // + //-------------------------------------------------------------------------- + + /** + * Resets the runing variables from the params + * Used once at the start (total reset) and for the repeat effect (partial reset) + */ + this.reset = function () { + // Shorter reference + var p = this._params; + + _period = 100 / (p['f'] * p['f'] + .001); + _maxPeriod = 100 / (p['g'] * p['g'] + .001); + + _slide = 1 - p['h'] * p['h'] * p['h'] * .01; + _deltaSlide = -p['i'] * p['i'] * p['i'] * .000001; + + if (!p['a']) { + _squareDuty = .5 - p['n'] / 2; + _dutySweep = -p['o'] * .00005; + } - // Repeats every _repeatLimit times, partially resetting the sound parameters - if (_repeatLimit) { - if (++_repeatTime >= _repeatLimit) { - _repeatTime = 0; - this.reset(); + _changeAmount = 1 + p['l'] * p['l'] * (p['l'] > 0 ? -.9 : 10); + _changeTime = 0; + _changeLimit = p['m'] == 1 ? 0 : (1 - p['m']) * (1 - p['m']) * 20000 + 32; } - } - // If _changeLimit is reached, shifts the pitch - if (_changeLimit) { - if (++_changeTime >= _changeLimit) { - _changeLimit = 0; - _period *= _changeAmount; - } - } + // I split the reset() function into two functions for better readability + this.totalReset = function () { + this.reset(); - // Acccelerate and apply slide - _slide += _deltaSlide; - _period *= _slide; + // Shorter reference + var p = this._params; - // Checks for frequency getting too low, and stops the sound if a minFrequency was set - if (_period > _maxPeriod) { - _period = _maxPeriod; - if (_minFreqency > 0) { - _finished = true; - } - } - - _periodTemp = _period; - - // Applies the vibrato effect - if (_vibratoAmplitude > 0) { - _vibratoPhase += _vibratoSpeed; - _periodTemp *= 1 + Math.sin(_vibratoPhase) * _vibratoAmplitude; - } - - _periodTemp |= 0; - if (_periodTemp < 8) { - _periodTemp = 8; - } - - // Sweeps the square duty - if (!_waveType) { - _squareDuty += _dutySweep; - if (_squareDuty < 0) { - _squareDuty = 0; - } else if (_squareDuty > .5) { - _squareDuty = .5; - } - } - - // Moves through the different stages of the volume envelope - if (++_envelopeTime > _envelopeLength) { - _envelopeTime = 0; - - switch (++_envelopeStage) { - case 1: - _envelopeLength = _envelopeLength1; - break; - case 2: - _envelopeLength = _envelopeLength2; - } - } - - // Sets the volume based on the position in the envelope - switch (_envelopeStage) { - case 0: - _envelopeVolume = _envelopeTime * _envelopeOverLength0; - break; - case 1: - _envelopeVolume = 1 + (1 - _envelopeTime * _envelopeOverLength1) * 2 * _sustainPunch; - break; - case 2: - _envelopeVolume = 1 - _envelopeTime * _envelopeOverLength2; - break; - case 3: - _envelopeVolume = 0; - _finished = true; - } - - // Moves the phaser offset - if (_phaser) { - _phaserOffset += _phaserDeltaOffset; - _phaserInt = _phaserOffset | 0; - if (_phaserInt < 0) { - _phaserInt = -_phaserInt; - } else if (_phaserInt > 1023) { - _phaserInt = 1023; - } - } - - // Moves the high-pass filter cutoff - if (_filters && _hpFilterDeltaCutoff) { - _hpFilterCutoff *= _hpFilterDeltaCutoff; - if (_hpFilterCutoff < .00001) { - _hpFilterCutoff = .00001; - } else if (_hpFilterCutoff > .1) { - _hpFilterCutoff = .1; - } - } - - _superSample = 0; - for (var j = 8; j--; ) { - // Cycles through the period - _phase++; - if (_phase >= _periodTemp) { - _phase %= _periodTemp; - - // Generates new random noise for this period - if (_waveType == 3) { - for (var n = _noiseBuffer.length; n--; ) { - _noiseBuffer[n] = Math.random() * 2 - 1; - } - } + // Calculating the length is all that remained here, everything else moved somewhere + _envelopeLength0 = p['b'] * p['b'] * 100000; + _envelopeLength1 = p['c'] * p['c'] * 100000; + _envelopeLength2 = p['e'] * p['e'] * 100000 + 12; + // Full length of the volume envelop (and therefore sound) + // Make sure the length can be divided by 3 so we will not need the padding "==" after base64 encode + return ((_envelopeLength0 + _envelopeLength1 + _envelopeLength2) / 3 | 0) * 3; } - // Gets the sample from the oscillator - switch (_waveType) { - case 0: // Square wave - _sample = ((_phase / _periodTemp) < _squareDuty) ? .5 : -.5; - break; - case 1: // Saw wave - _sample = 1 - _phase / _periodTemp * 2; - break; - case 2: // Sine wave (fast and accurate approx) - _pos = _phase / _periodTemp; - _pos = (_pos > .5 ? _pos - 1 : _pos) * 6.28318531; - _sample = 1.27323954 * _pos + .405284735 * _pos * _pos * (_pos < 0 ? 1 : -1); - _sample = .225 * ((_sample < 0 ? -1 : 1) * _sample * _sample - _sample) + _sample; - break; - case 3: // Noise - _sample = _noiseBuffer[Math.abs(_phase * 32 / _periodTemp | 0)]; - } + /** + * Writes the wave to the supplied buffer ByteArray + * @param buffer A ByteArray to write the wave to + * @return If the wave is finished + */ + this.synthWave = function (buffer, length) { + // Shorter reference + var p = this._params; + + // If the filters are active + var _filters = p['s'] != 1 || p['v'], + // Cutoff multiplier which adjusts the amount the wave position can move + _hpFilterCutoff = p['v'] * p['v'] * .1, + // Speed of the high-pass cutoff multiplier + _hpFilterDeltaCutoff = 1 + p['w'] * .0003, + // Cutoff multiplier which adjusts the amount the wave position can move + _lpFilterCutoff = p['s'] * p['s'] * p['s'] * .1, + // Speed of the low-pass cutoff multiplier + _lpFilterDeltaCutoff = 1 + p['t'] * .0001, + // If the low pass filter is active + _lpFilterOn = p['s'] != 1, + // masterVolume * masterVolume (for quick calculations) + _masterVolume = p['x'] * p['x'], + // Minimum frequency before stopping + _minFreqency = p['g'], + // If the phaser is active + _phaser = p['q'] || p['r'], + // Change in phase offset + _phaserDeltaOffset = p['r'] * p['r'] * p['r'] * .2, + // Phase offset for phaser effect + _phaserOffset = p['q'] * p['q'] * (p['q'] < 0 ? -1020 : 1020), + // Once the time reaches this limit, some of the iables are reset + _repeatLimit = p['p'] ? ((1 - p['p']) * (1 - p['p']) * 20000 | 0) + 32 : 0, + // The punch factor (louder at begining of sustain) + _sustainPunch = p['d'], + // Amount to change the period of the wave by at the peak of the vibrato wave + _vibratoAmplitude = p['j'] / 2, + // Speed at which the vibrato phase moves + _vibratoSpeed = p['k'] * p['k'] * .01, + // The type of wave to generate + _waveType = p['a']; + + var _envelopeLength = _envelopeLength0, // Length of the current envelope stage + _envelopeOverLength0 = 1 / _envelopeLength0, // (for quick calculations) + _envelopeOverLength1 = 1 / _envelopeLength1, // (for quick calculations) + _envelopeOverLength2 = 1 / _envelopeLength2; // (for quick calculations) + + // Damping muliplier which restricts how fast the wave position can move + var _lpFilterDamping = 5 / (1 + p['u'] * p['u'] * 20) * (.01 + _lpFilterCutoff); + if (_lpFilterDamping > .8) { + _lpFilterDamping = .8; + } + _lpFilterDamping = 1 - _lpFilterDamping; + + var _finished = false, // If the sound has finished + _envelopeStage = 0, // Current stage of the envelope (attack, sustain, decay, end) + _envelopeTime = 0, // Current time through current enelope stage + _envelopeVolume = 0, // Current volume of the envelope + _hpFilterPos = 0, // Adjusted wave position after high-pass filter + _lpFilterDeltaPos = 0, // Change in low-pass wave position, as allowed by the cutoff and damping + _lpFilterOldPos, // Previous low-pass wave position + _lpFilterPos = 0, // Adjusted wave position after low-pass filter + _periodTemp, // Period modified by vibrato + _phase = 0, // Phase through the wave + _phaserInt, // Integer phaser offset, for bit maths + _phaserPos = 0, // Position through the phaser buffer + _pos, // Phase expresed as a Number from 0-1, used for fast sin approx + _repeatTime = 0, // Counter for the repeats + _sample, // Sub-sample calculated 8 times per actual sample, averaged out to get the super sample + _superSample, // Actual sample writen to the wave + _vibratoPhase = 0; // Phase through the vibrato sine wave + + // Buffer of wave values used to create the out of phase second wave + var _phaserBuffer = new Array(1024), + // Buffer of random values used to generate noise + _noiseBuffer = new Array(32); + for (var i = _phaserBuffer.length; i--;) { + _phaserBuffer[i] = 0; + } + for (var i = _noiseBuffer.length; i--;) { + _noiseBuffer[i] = Math.random() * 2 - 1; + } - // Applies the low and high pass filters - if (_filters) { - _lpFilterOldPos = _lpFilterPos; - _lpFilterCutoff *= _lpFilterDeltaCutoff; - if (_lpFilterCutoff < 0) { - _lpFilterCutoff = 0; - } else if (_lpFilterCutoff > .1) { - _lpFilterCutoff = .1; - } - - if (_lpFilterOn) { - _lpFilterDeltaPos += (_sample - _lpFilterPos) * _lpFilterCutoff; - _lpFilterDeltaPos *= _lpFilterDamping; - } else { - _lpFilterPos = _sample; - _lpFilterDeltaPos = 0; - } - - _lpFilterPos += _lpFilterDeltaPos; - - _hpFilterPos += _lpFilterPos - _lpFilterOldPos; - _hpFilterPos *= 1 - _hpFilterCutoff; - _sample = _hpFilterPos; - } + for (var i = 0; i < length; i++) { + if (_finished) { + return i; + } + + // Repeats every _repeatLimit times, partially resetting the sound parameters + if (_repeatLimit) { + if (++_repeatTime >= _repeatLimit) { + _repeatTime = 0; + this.reset(); + } + } + + // If _changeLimit is reached, shifts the pitch + if (_changeLimit) { + if (++_changeTime >= _changeLimit) { + _changeLimit = 0; + _period *= _changeAmount; + } + } + + // Acccelerate and apply slide + _slide += _deltaSlide; + _period *= _slide; + + // Checks for frequency getting too low, and stops the sound if a minFrequency was set + if (_period > _maxPeriod) { + _period = _maxPeriod; + if (_minFreqency > 0) { + _finished = true; + } + } + + _periodTemp = _period; + + // Applies the vibrato effect + if (_vibratoAmplitude > 0) { + _vibratoPhase += _vibratoSpeed; + _periodTemp *= 1 + Math.sin(_vibratoPhase) * _vibratoAmplitude; + } + + _periodTemp |= 0; + if (_periodTemp < 8) { + _periodTemp = 8; + } + + // Sweeps the square duty + if (!_waveType) { + _squareDuty += _dutySweep; + if (_squareDuty < 0) { + _squareDuty = 0; + } else if (_squareDuty > .5) { + _squareDuty = .5; + } + } + + // Moves through the different stages of the volume envelope + if (++_envelopeTime > _envelopeLength) { + _envelopeTime = 0; + + switch (++_envelopeStage) { + case 1: + _envelopeLength = _envelopeLength1; + break; + case 2: + _envelopeLength = _envelopeLength2; + } + } + + // Sets the volume based on the position in the envelope + switch (_envelopeStage) { + case 0: + _envelopeVolume = _envelopeTime * _envelopeOverLength0; + break; + case 1: + _envelopeVolume = 1 + (1 - _envelopeTime * _envelopeOverLength1) * 2 * _sustainPunch; + break; + case 2: + _envelopeVolume = 1 - _envelopeTime * _envelopeOverLength2; + break; + case 3: + _envelopeVolume = 0; + _finished = true; + } + + // Moves the phaser offset + if (_phaser) { + _phaserOffset += _phaserDeltaOffset; + _phaserInt = _phaserOffset | 0; + if (_phaserInt < 0) { + _phaserInt = -_phaserInt; + } else if (_phaserInt > 1023) { + _phaserInt = 1023; + } + } + + // Moves the high-pass filter cutoff + if (_filters && _hpFilterDeltaCutoff) { + _hpFilterCutoff *= _hpFilterDeltaCutoff; + if (_hpFilterCutoff < .00001) { + _hpFilterCutoff = .00001; + } else if (_hpFilterCutoff > .1) { + _hpFilterCutoff = .1; + } + } + + _superSample = 0; + for (var j = 8; j--;) { + // Cycles through the period + _phase++; + if (_phase >= _periodTemp) { + _phase %= _periodTemp; + + // Generates new random noise for this period + if (_waveType == 3) { + for (var n = _noiseBuffer.length; n--;) { + _noiseBuffer[n] = Math.random() * 2 - 1; + } + } + } + + // Gets the sample from the oscillator + switch (_waveType) { + case 0: // Square wave + _sample = ((_phase / _periodTemp) < _squareDuty) ? .5 : -.5; + break; + case 1: // Saw wave + _sample = 1 - _phase / _periodTemp * 2; + break; + case 2: // Sine wave (fast and accurate approx) + _pos = _phase / _periodTemp; + _pos = (_pos > .5 ? _pos - 1 : _pos) * 6.28318531; + _sample = 1.27323954 * _pos + .405284735 * _pos * _pos * (_pos < 0 ? 1 : -1); + _sample = .225 * ((_sample < 0 ? -1 : 1) * _sample * _sample - _sample) + _sample; + break; + case 3: // Noise + _sample = _noiseBuffer[Math.abs(_phase * 32 / _periodTemp | 0)]; + } + + // Applies the low and high pass filters + if (_filters) { + _lpFilterOldPos = _lpFilterPos; + _lpFilterCutoff *= _lpFilterDeltaCutoff; + if (_lpFilterCutoff < 0) { + _lpFilterCutoff = 0; + } else if (_lpFilterCutoff > .1) { + _lpFilterCutoff = .1; + } + + if (_lpFilterOn) { + _lpFilterDeltaPos += (_sample - _lpFilterPos) * _lpFilterCutoff; + _lpFilterDeltaPos *= _lpFilterDamping; + } else { + _lpFilterPos = _sample; + _lpFilterDeltaPos = 0; + } + + _lpFilterPos += _lpFilterDeltaPos; + + _hpFilterPos += _lpFilterPos - _lpFilterOldPos; + _hpFilterPos *= 1 - _hpFilterCutoff; + _sample = _hpFilterPos; + } + + // Applies the phaser effect + if (_phaser) { + _phaserBuffer[_phaserPos % 1024] = _sample; + _sample += _phaserBuffer[(_phaserPos - _phaserInt + 1024) % 1024]; + _phaserPos++; + } + + _superSample += _sample; + } + + // Averages out the super samples and applies volumes + _superSample *= .125 * _envelopeVolume * _masterVolume; + + // Clipping if too loud + buffer[i] = _superSample >= 1 ? 32767 : _superSample <= -1 ? -32768 : _superSample * 32767 | 0; + } - // Applies the phaser effect - if (_phaser) { - _phaserBuffer[_phaserPos % 1024] = _sample; - _sample += _phaserBuffer[(_phaserPos - _phaserInt + 1024) % 1024]; - _phaserPos++; + return length; } - - _superSample += _sample; - } - - // Averages out the super samples and applies volumes - _superSample *= .125 * _envelopeVolume * _masterVolume; - - // Clipping if too loud - buffer[i] = _superSample >= 1 ? 32767 : _superSample <= -1 ? -32768 : _superSample * 32767 | 0; } - return length; - } -} - // Adapted from http://codebase.es/riffwave/ -var synth = new SfxrSynth(); + var synth = new SfxrSynth(); // Export for the Closure Compiler -window['jsfxr'] = function(settings) { - // Initialize SfxrParams - synth._params.setSettings(settings); - // Synthesize Wave - var envelopeFullLength = synth.totalReset(); - var data = new Uint8Array(((envelopeFullLength + 1) / 2 | 0) * 4 + 44); - var used = synth.synthWave(new Uint16Array(data.buffer, 44), envelopeFullLength) * 2; - var dv = new Uint32Array(data.buffer, 0, 44); - // Initialize header - dv[0] = 0x46464952; // "RIFF" - dv[1] = used + 36; // put total size here - dv[2] = 0x45564157; // "WAVE" - dv[3] = 0x20746D66; // "fmt " - dv[4] = 0x00000010; // size of the following - dv[5] = 0x00010001; // Mono: 1 channel, PCM format - dv[6] = 0x0000AC44; // 44,100 samples per second - dv[7] = 0x00015888; // byte rate: two bytes per sample - dv[8] = 0x00100002; // 16 bits per sample, aligned on every two bytes - dv[9] = 0x61746164; // "data" - dv[10] = used; // put number of samples here - - // Base64 encoding written by me, @maettig - used += 44; - var i = 0, - base64Characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/', - output = 'data:audio/wav;base64,'; - for (; i < used; i += 3) - { - var a = data[i] << 16 | data[i + 1] << 8 | data[i + 2]; - output += base64Characters[a >> 18] + base64Characters[a >> 12 & 63] + base64Characters[a >> 6 & 63] + base64Characters[a & 63]; - } - return output; -} \ No newline at end of file + window['jsfxr'] = function (settings) { + // Initialize SfxrParams + synth._params.setSettings(settings); + // Synthesize Wave + var envelopeFullLength = synth.totalReset(); + var data = new Uint8Array(((envelopeFullLength + 1) / 2 | 0) * 4 + 44); + var used = synth.synthWave(new Uint16Array(data.buffer, 44), envelopeFullLength) * 2; + var dv = new Uint32Array(data.buffer, 0, 44); + // Initialize header + dv[0] = 0x46464952; // "RIFF" + dv[1] = used + 36; // put total size here + dv[2] = 0x45564157; // "WAVE" + dv[3] = 0x20746D66; // "fmt " + dv[4] = 0x00000010; // size of the following + dv[5] = 0x00010001; // Mono: 1 channel, PCM format + dv[6] = 0x0000AC44; // 44,100 samples per second + dv[7] = 0x00015888; // byte rate: two bytes per sample + dv[8] = 0x00100002; // 16 bits per sample, aligned on every two bytes + dv[9] = 0x61746164; // "data" + dv[10] = used; // put number of samples here + + // Base64 encoding written by me, @maettig + used += 44; + var i = 0, + base64Characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/', + output = 'data:audio/wav;base64,'; + for (; i < used; i += 3) { + var a = data[i] << 16 | data[i + 1] << 8 | data[i + 2]; + output += base64Characters[a >> 18] + base64Characters[a >> 12 & 63] + base64Characters[a >> 6 & 63] + base64Characters[a & 63]; + } + return output; + }; +})(); diff --git a/app/scripts/particle.js b/app/scripts/particle.js index f9aded3..dc626ee 100644 --- a/app/scripts/particle.js +++ b/app/scripts/particle.js @@ -1,36 +1,40 @@ -game.particle = function(p) { - game.entity.apply(this, arguments); - this.dir = game.rand.bool() ? -1 : 1; - this.vx = Math.random() * 5 - 2; - this.vy = Math.random() * -10 - 1; - this.move = p.speed + game.rand.rangef(-0.5, 0.5); - this.distance = 0; - this.maxDistance = (p.dist || 200) + game.rand.range(-30, 30); - this.delta = this.width / this.maxDistance; -}; +(function () { + "use strict"; -game.particle.prototype = new game.entity(); -game.particle.prototype.constructor = game.particle; + game.particle = function (p) { + game.entity.apply(this, arguments); + this.dir = game.rand.bool() ? -1 : 1; + this.vx = Math.random() * 5 - 2; + this.vy = Math.random() * -10 - 1; + this.move = p.speed + game.rand.rangef(-0.5, 0.5); + this.distance = 0; + this.maxDistance = (p.dist || 200) + game.rand.range(-30, 30); + this.delta = this.width / this.maxDistance; + }; -game.particle.prototype.update = function() { + game.particle.prototype = Object.create(game.entity.prototype); + game.particle.prototype.constructor = game.particle; - this.width -= this.delta; - this.height -= this.delta; + game.particle.prototype.update = function () { - this.vy += game.gravity; - this.y += this.vy; - this.x += this.vx; + this.width -= this.delta; + this.height -= this.delta; - this.distance += Math.sqrt(Math.pow(this.vx, 2) + Math.pow(this.vy, 2)); - if (this.distance > this.maxDistance) { - this.distance = this.maxDistance; - this.destroy(); - } -}; + this.vy += game.gravity; + this.y += this.vy; + this.x += this.vx; -game.particle.prototype.draw = function() { - game.ctx.save(); - game.ctx.globalAlpha = 1 - this.distance / this.maxDistance; - game.entity.prototype.draw.call(this); - game.ctx.restore(); -}; + this.distance += Math.sqrt(Math.pow(this.vx, 2) + Math.pow(this.vy, 2)); + if (this.distance > this.maxDistance) { + this.distance = this.maxDistance; + this.destroy(); + } + }; + + game.particle.prototype.draw = function () { + game.ctx.save(); + game.ctx.globalAlpha = 1 - this.distance / this.maxDistance; + game.entity.prototype.draw.call(this); + game.ctx.restore(); + }; +})(); diff --git a/app/scripts/raf.js b/app/scripts/raf.js index 222a8ed..a5963ef 100644 --- a/app/scripts/raf.js +++ b/app/scripts/raf.js @@ -1,18 +1,20 @@ -(function() { +(function () { + "use strict"; + var lastTime = 0, vendors = ['webkit', 'moz', 'o', 'ms']; - for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { - window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame']; + for (var i = 0; i < vendors.length && !window.requestAnimationFrame; i+=1) { + window.requestAnimationFrame = window[vendors[i] + 'RequestAnimationFrame']; } if (!window.requestAnimationFrame) { - window.requestAnimationFrame = function(callback, element) { + window.requestAnimationFrame = function (callback, element) { var currTime = new Date().getTime(), timeToCall = Math.max(0, 16 - (currTime - lastTime)), - id = window.setTimeout(function() { + id = window.setTimeout(function () { callback(currTime + timeToCall); }, timeToCall); lastTime = currTime + timeToCall; return id; }; } -})(); \ No newline at end of file +})(); diff --git a/app/scripts/sprites.js b/app/scripts/sprites.js index cc9dc86..f7288f1 100644 --- a/app/scripts/sprites.js +++ b/app/scripts/sprites.js @@ -1,188 +1,168 @@ -game.sprite = { - items: {}, - create: function(name, w, h, fn) { - if (!this.get(name)) { - var c = document.createElement('canvas'); - c.width = w; - c.height = h; - - var octx = c.getContext('2d'); - octx.save(); - fn(octx); - octx.restore(); - this.set(name, c); - } - return this.get(name); - }, - set: function(name, val) { - this.items[name] = val; - }, - get: function(name) { - return this.items[name]; - }, - show: function() { - var cnt = 0; - for (var i in this.items) { - game.ctx.save(); - game.ctx.translate(cnt * game.brickWidth, 100); - game.ctx.drawImage(this.items[i], 0, 0); - game.ctx.restore(); - cnt += 1.1; - } - }, - factory: function() { - - // AIR SPRITES - var w10 = game.brickWidth / 10, - w2 = game.brickWidth / 2, - h2 = game.brickHeight / 2, - linewidth = game.brickWidth / 10, - radius = w10, - glow = 'rgba(255,255,255,0.8)', - glow2 = 'rgba(255,255,0,0.5)'; - - this.create('AIR', game.brickWidth, game.brickWidth, function(c) { - var w = (game.brickWidth - linewidth * 2) / 4; - - game.rect(c, 0, 0, game.brickWidth, game.brickWidth, radius, game.elements.AIR.color, linewidth, null, glow); - c.lineWidth = linewidth; - c.strokeStyle = game.elements.AIR.color; - c.translate(linewidth, 0); - for (var i = 0; i < 3; i++) { - c.beginPath(); - c.moveTo(w, linewidth); - c.lineTo(w, game.brickWidth - linewidth); - c.stroke(); - c.translate(w, 0); - } - }); - - this.create('AIR-SHOT', game.brickWidth, game.brickHeight, function(c) { - game.rect(c, w2 - w10, 0, w10 * 2, game.brickHeight, 2, game.elements.AIR.color, linewidth, game.elements.AIR.color, glow2); - }); - - this.create('AIR-BRICK-empty', game.brickWidth, game.brickHeight, function(c) { - game.rect(c, 0, 0, game.brickWidth, game.brickHeight, radius, game.elements.AIR.color, linewidth, null, glow); - }); - - this.create('AIR-BRICK-filled', game.brickWidth, game.brickHeight, function(c) { - game.rect(c, 0, 0, game.brickWidth, game.brickHeight, radius, game.elements.AIR.color, linewidth, game.elements.AIR.color, glow); - }); - - this.create('AIR-BRICK-special', game.brickWidth, game.brickHeight, function(c) { - //game.rect(c, linewidth * 2, linewidth * 2, game.brickWidth - linewidth * 2, game.brickHeight - linewidth * 2, radius, game.elements.AIR.color, linewidth, game.elements.AIR.color); - c.fillStyle = game.elements.AIR.color; - c.fillRect(w2 - h2 / 2, linewidth, h2, game.brickHeight - linewidth * 2); - game.rect(c, 0, 0, game.brickWidth, game.brickHeight, radius, game.elements.AIR.color, linewidth, null, glow); - }); - - // EARTH SPRITES - this.create('EARTH', game.brickWidth, game.brickWidth, function(c) { - var h = (game.brickWidth - linewidth * 2) / 4; - - game.rect(c, 0, 0, game.brickWidth, game.brickWidth, radius, game.elements.EARTH.color, linewidth, null, glow); - c.lineWidth = linewidth; - c.strokeStyle = game.elements.EARTH.color; - c.translate(0, linewidth); - for (var i = 0; i < 3; i++) { - c.beginPath(); - c.moveTo(linewidth, h); - c.lineTo(game.brickWidth - linewidth, h); - c.stroke(); - c.translate(0, h); - } - }); - - this.create('EARTH-SHOT', game.brickWidth, game.brickHeight, function(c) { - //c.fillStyle = game.elements.EARTH.color; - //c.fillRect(w2 - w10, 0, w10 * 2, game.brickHeight); - game.rect(c, w2 - w10, 0, w10 * 2, game.brickHeight, 2, game.elements.EARTH.color, linewidth, game.elements.EARTH.color, glow2); - }); - - this.create('EARTH-BRICK-empty', game.brickWidth, game.brickHeight, function(c) { - game.rect(c, 0, 0, game.brickWidth, game.brickHeight, radius, game.elements.EARTH.color, linewidth, null, glow); - }); - - this.create('EARTH-BRICK-filled', game.brickWidth, game.brickHeight, function(c) { - game.rect(c, 0, 0, game.brickWidth, game.brickHeight, radius, game.elements.EARTH.color, linewidth, game.elements.EARTH.color, glow); - }); - - this.create('EARTH-BRICK-special', game.brickWidth, game.brickHeight, function(c) { - c.fillStyle = game.elements.EARTH.color; - c.fillRect(w2 - h2 / 2, linewidth, h2, game.brickHeight - linewidth * 2); - game.rect(c, 0, 0, game.brickWidth, game.brickHeight, radius, game.elements.EARTH.color, linewidth, null, glow); - }); - - - // WATER SPRITES - this.create('WATER', game.brickWidth, game.brickWidth, function(c) { - var h = (game.brickWidth - linewidth * 2) / 4; - - game.rect(c, 0, 0, game.brickWidth, game.brickWidth, radius, game.elements.WATER.color, linewidth, null, glow); - c.lineWidth = linewidth; - c.strokeStyle = game.elements.WATER.color; - c.translate(0, linewidth); - for (var i = 0; i < 3; i++) { - game.sine(c, linewidth, h, game.brickWidth - linewidth, h); - c.translate(0, h); +(function () { + "use strict"; + + game.sprite = { + items: {}, + create: function (name, w, h, fn) { + if (!this.get(name)) { + var c = document.createElement('canvas'); + c.width = w; + c.height = h; + + var octx = c.getContext('2d'); + octx.save(); + fn(octx); + octx.restore(); + this.set(name, c); } - }); - - this.create('WATER-SHOT', game.brickWidth, game.brickHeight, function(c) { - //c.fillStyle = game.elements.WATER.color; - //c.fillRect(w2 - w10, 0, w10 * 2, game.brickHeight); - game.rect(c, w2 - w10, 0, w10 * 2, game.brickHeight, 2, game.elements.WATER.color, linewidth, game.elements.WATER.color, glow2); - }); - - this.create('WATER-BRICK-empty', game.brickWidth, game.brickHeight, function(c) { - game.rect(c, 0, 0, game.brickWidth, game.brickHeight, radius, game.elements.WATER.color, linewidth, null, glow); - }); - - this.create('WATER-BRICK-filled', game.brickWidth, game.brickHeight, function(c) { - game.rect(c, 0, 0, game.brickWidth, game.brickHeight, radius, game.elements.WATER.color, linewidth, game.elements.WATER.color, glow); - }); - - this.create('WATER-BRICK-special', game.brickWidth, game.brickHeight, function(c) { - game.line(c, linewidth * 2, h2, game.brickWidth - linewidth * 2, h2, game.elements.WATER.color, linewidth); - game.rect(c, 0, 0, game.brickWidth, game.brickHeight, radius, game.elements.WATER.color, linewidth, null, glow); - }); - - - // FIRE SPRITES - this.create('FIRE', game.brickWidth, game.brickWidth, function(c) { - var h = (game.brickWidth - linewidth * 2) / 4; - game.rect(c, 0, 0, game.brickWidth, game.brickWidth, radius, game.elements.FIRE.color, linewidth, null, glow); - c.lineWidth = linewidth; - c.strokeStyle = game.elements.FIRE.color; - c.translate(-linewidth, 0); - c.translate(game.brickWidth / 2, game.brickWidth / 2); - c.rotate(90 * game.RAD); - c.translate(-game.brickWidth / 2, -game.brickWidth / 2); - for (var i = 0; i < 3; i++) { - game.sine(c, linewidth, h, game.brickWidth - linewidth, h); - c.translate(0, h); - } - }); - - this.create('FIRE-SHOT', game.brickWidth, game.brickHeight, function(c) { - //c.fillStyle = game.elements.FIRE.color; - //c.fillRect(w2 - w10, 0, w10 * 2, game.brickHeight); - game.rect(c, w2 - w10, 0, w10 * 2, game.brickHeight, 2, game.elements.FIRE.color, linewidth, game.elements.FIRE.color, glow2); - }); - - this.create('FIRE-BRICK-empty', game.brickWidth, game.brickHeight, function(c) { - game.rect(c, 0, 0, game.brickWidth, game.brickHeight, radius, game.elements.FIRE.color, linewidth, null, glow); - }); - - this.create('FIRE-BRICK-filled', game.brickWidth, game.brickHeight, function(c) { - game.rect(c, 0, 0, game.brickWidth, game.brickHeight, radius, game.elements.FIRE.color, linewidth, game.elements.FIRE.color, glow); - }); - - this.create('FIRE-BRICK-special', game.brickWidth, game.brickHeight, function(c) { - game.line(c, w2, linewidth * 2, w2, game.brickHeight - linewidth * 2, game.elements.FIRE.color, linewidth); - game.rect(c, 0, 0, game.brickWidth, game.brickHeight, radius, game.elements.FIRE.color, linewidth, null, glow); - }); - - //this.show(); - } -}; + return this.get(name); + }, + set: function (name, val) { + this.items[name] = val; + }, + get: function (name) { + return this.items[name]; + }, + factory: function () { + + var w10 = game.brickWidth / 10, + w2 = game.brickWidth / 2, + h2 = game.brickHeight / 2, + linewidth = game.brickWidth / 10, + radius = w10, + glow = 'rgba(255,255,255,0.8)', + glow2 = 'rgba(255,255,0,0.5)'; + + this.create('AIR', game.brickWidth, game.brickWidth, function (c) { + var w = (game.brickWidth - linewidth * 2) / 4; + + game.rect(c, 0, 0, game.brickWidth, game.brickWidth, radius, game.elements.AIR.color, linewidth, null, glow); + c.lineWidth = linewidth; + c.strokeStyle = game.elements.AIR.color; + c.translate(linewidth, 0); + for (var i = 0; i < 3; i++) { + c.beginPath(); + c.moveTo(w, linewidth); + c.lineTo(w, game.brickWidth - linewidth); + c.stroke(); + c.translate(w, 0); + } + }); + + this.create('AIR-SHOT', game.brickWidth, game.brickHeight, function (c) { + game.rect(c, w2 - w10, 0, w10 * 2, game.brickHeight, 2, game.elements.AIR.color, linewidth, game.elements.AIR.color, glow2); + }); + + this.create('AIR-BRICK-empty', game.brickWidth, game.brickHeight, function (c) { + game.rect(c, 0, 0, game.brickWidth, game.brickHeight, radius, game.elements.AIR.color, linewidth, null, glow); + }); + + this.create('AIR-BRICK-filled', game.brickWidth, game.brickHeight, function (c) { + game.rect(c, 0, 0, game.brickWidth, game.brickHeight, radius, game.elements.AIR.color, linewidth, game.elements.AIR.color, glow); + }); + + this.create('AIR-BRICK-special', game.brickWidth, game.brickHeight, function (c) { + c.fillStyle = game.elements.AIR.color; + c.fillRect(w2 - h2 / 2, linewidth, h2, game.brickHeight - linewidth * 2); + game.rect(c, 0, 0, game.brickWidth, game.brickHeight, radius, game.elements.AIR.color, linewidth, null, glow); + }); + + this.create('EARTH', game.brickWidth, game.brickWidth, function (c) { + var h = (game.brickWidth - linewidth * 2) / 4; + + game.rect(c, 0, 0, game.brickWidth, game.brickWidth, radius, game.elements.EARTH.color, linewidth, null, glow); + c.lineWidth = linewidth; + c.strokeStyle = game.elements.EARTH.color; + c.translate(0, linewidth); + for (var i = 0; i < 3; i++) { + c.beginPath(); + c.moveTo(linewidth, h); + c.lineTo(game.brickWidth - linewidth, h); + c.stroke(); + c.translate(0, h); + } + }); + + this.create('EARTH-SHOT', game.brickWidth, game.brickHeight, function (c) { + game.rect(c, w2 - w10, 0, w10 * 2, game.brickHeight, 2, game.elements.EARTH.color, linewidth, game.elements.EARTH.color, glow2); + }); + + this.create('EARTH-BRICK-empty', game.brickWidth, game.brickHeight, function (c) { + game.rect(c, 0, 0, game.brickWidth, game.brickHeight, radius, game.elements.EARTH.color, linewidth, null, glow); + }); + + this.create('EARTH-BRICK-filled', game.brickWidth, game.brickHeight, function (c) { + game.rect(c, 0, 0, game.brickWidth, game.brickHeight, radius, game.elements.EARTH.color, linewidth, game.elements.EARTH.color, glow); + }); + + this.create('EARTH-BRICK-special', game.brickWidth, game.brickHeight, function (c) { + c.fillStyle = game.elements.EARTH.color; + c.fillRect(w2 - h2 / 2, linewidth, h2, game.brickHeight - linewidth * 2); + game.rect(c, 0, 0, game.brickWidth, game.brickHeight, radius, game.elements.EARTH.color, linewidth, null, glow); + }); + + this.create('WATER', game.brickWidth, game.brickWidth, function (c) { + var h = (game.brickWidth - linewidth * 2) / 4; + + game.rect(c, 0, 0, game.brickWidth, game.brickWidth, radius, game.elements.WATER.color, linewidth, null, glow); + c.lineWidth = linewidth; + c.strokeStyle = game.elements.WATER.color; + c.translate(0, linewidth); + for (var i = 0; i < 3; i++) { + game.sine(c, linewidth, h, game.brickWidth - linewidth, h); + c.translate(0, h); + } + }); + + this.create('WATER-SHOT', game.brickWidth, game.brickHeight, function (c) { + game.rect(c, w2 - w10, 0, w10 * 2, game.brickHeight, 2, game.elements.WATER.color, linewidth, game.elements.WATER.color, glow2); + }); + + this.create('WATER-BRICK-empty', game.brickWidth, game.brickHeight, function (c) { + game.rect(c, 0, 0, game.brickWidth, game.brickHeight, radius, game.elements.WATER.color, linewidth, null, glow); + }); + + this.create('WATER-BRICK-filled', game.brickWidth, game.brickHeight, function (c) { + game.rect(c, 0, 0, game.brickWidth, game.brickHeight, radius, game.elements.WATER.color, linewidth, game.elements.WATER.color, glow); + }); + + this.create('WATER-BRICK-special', game.brickWidth, game.brickHeight, function (c) { + game.line(c, linewidth * 2, h2, game.brickWidth - linewidth * 2, h2, game.elements.WATER.color, linewidth); + game.rect(c, 0, 0, game.brickWidth, game.brickHeight, radius, game.elements.WATER.color, linewidth, null, glow); + }); + + this.create('FIRE', game.brickWidth, game.brickWidth, function (c) { + var h = (game.brickWidth - linewidth * 2) / 4; + game.rect(c, 0, 0, game.brickWidth, game.brickWidth, radius, game.elements.FIRE.color, linewidth, null, glow); + c.lineWidth = linewidth; + c.strokeStyle = game.elements.FIRE.color; + c.translate(-linewidth, 0); + c.translate(game.brickWidth / 2, game.brickWidth / 2); + c.rotate(90 * game.RAD); + c.translate(-game.brickWidth / 2, -game.brickWidth / 2); + for (var i = 0; i < 3; i++) { + game.sine(c, linewidth, h, game.brickWidth - linewidth, h); + c.translate(0, h); + } + }); + + this.create('FIRE-SHOT', game.brickWidth, game.brickHeight, function (c) { + game.rect(c, w2 - w10, 0, w10 * 2, game.brickHeight, 2, game.elements.FIRE.color, linewidth, game.elements.FIRE.color, glow2); + }); + + this.create('FIRE-BRICK-empty', game.brickWidth, game.brickHeight, function (c) { + game.rect(c, 0, 0, game.brickWidth, game.brickHeight, radius, game.elements.FIRE.color, linewidth, null, glow); + }); + + this.create('FIRE-BRICK-filled', game.brickWidth, game.brickHeight, function (c) { + game.rect(c, 0, 0, game.brickWidth, game.brickHeight, radius, game.elements.FIRE.color, linewidth, game.elements.FIRE.color, glow); + }); + + this.create('FIRE-BRICK-special', game.brickWidth, game.brickHeight, function (c) { + game.line(c, w2, linewidth * 2, w2, game.brickHeight - linewidth * 2, game.elements.FIRE.color, linewidth); + game.rect(c, 0, 0, game.brickWidth, game.brickHeight, radius, game.elements.FIRE.color, linewidth, null, glow); + }); + + } + }; +})(); diff --git a/app/scripts/stars.js b/app/scripts/stars.js index 264171b..f8164dc 100644 --- a/app/scripts/stars.js +++ b/app/scripts/stars.js @@ -1,23 +1,23 @@ -game.star = function(l) { - this.x = game.rand.flot() * game.bgCtx.canvas.width; - this.y = game.rand.flot() * game.bgCtx.canvas.height; - this.brightness = l * 15 + game.rand.range(l*14, l*18) / 100; - this.radius = game.rand.flot() / l * 4; - this.color = game.starColors[game.rand.range(0, game.starColors.length)]; - this.draw(); -}; +(function () { + "use strict"; -game.star.prototype.update = function() { + game.star = function (l) { + this.x = game.rand.flot() * game.bgCtx.canvas.width; + this.y = game.rand.flot() * game.bgCtx.canvas.height; + this.brightness = l * 15 + game.rand.range(l * 14, l * 18) / 100; + this.radius = game.rand.flot() / l * 4; + this.color = game.starColors[game.rand.range(0, game.starColors.length)]; + this.draw(); + }; -}; - -game.star.prototype.draw = function() { - game.bgCtx.save(); - game.bgCtx.beginPath(); - game.bgCtx.globalAlpha = this.brightness; - game.bgCtx.fillStyle = this.color; - game.bgCtx.arc(this.x, this.y, this.radius, 0, game.PI2); - game.bgCtx.fill(); - game.bgCtx.closePath(); - game.bgCtx.restore(); -}; \ No newline at end of file + game.star.prototype.draw = function () { + game.bgCtx.save(); + game.bgCtx.beginPath(); + game.bgCtx.globalAlpha = this.brightness; + game.bgCtx.fillStyle = this.color; + game.bgCtx.arc(this.x, this.y, this.radius, 0, game.PI2); + game.bgCtx.fill(); + game.bgCtx.closePath(); + game.bgCtx.restore(); + }; +})(); diff --git a/app/scripts/stats.js b/app/scripts/stats.js index 90b2a27..3345f42 100644 --- a/app/scripts/stats.js +++ b/app/scripts/stats.js @@ -1,149 +1,150 @@ -/** - * @author mrdoob / http://mrdoob.com/ - */ +(function () { + "use strict"; -var Stats = function () { + /** + * @author mrdoob / http://mrdoob.com/ + */ - var startTime = Date.now(), prevTime = startTime; - var ms = 0, msMin = Infinity, msMax = 0; - var fps = 0, fpsMin = Infinity, fpsMax = 0; - var frames = 0, mode = 0; + window.Stats = function () { - var container = document.createElement( 'div' ); - container.id = 'stats'; - container.addEventListener( 'mousedown', function ( event ) { event.preventDefault(); setMode( ++ mode % 2 ) }, false ); - container.style.cssText = 'width:80px;opacity:0.9;cursor:pointer'; + var startTime = Date.now(), prevTime = startTime; + var ms = 0, msMin = Infinity, msMax = 0; + var fps = 0, fpsMin = Infinity, fpsMax = 0; + var frames = 0, mode = 0; - var fpsDiv = document.createElement( 'div' ); - fpsDiv.id = 'fps'; - fpsDiv.style.cssText = 'padding:0 0 3px 3px;text-align:left;background-color:#002'; - container.appendChild( fpsDiv ); + var container = document.createElement('div'); + container.id = 'stats'; + container.addEventListener('mousedown', function (event) { + event.preventDefault(); + setMode(++mode % 2) + }, false); + container.style.cssText = 'width:80px;opacity:0.9;cursor:pointer'; - var fpsText = document.createElement( 'div' ); - fpsText.id = 'fpsText'; - fpsText.style.cssText = 'color:#0ff;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px'; - fpsText.innerHTML = 'FPS'; - fpsDiv.appendChild( fpsText ); + var fpsDiv = document.createElement('div'); + fpsDiv.id = 'fps'; + fpsDiv.style.cssText = 'padding:0 0 3px 3px;text-align:left;background-color:#002'; + container.appendChild(fpsDiv); - var fpsGraph = document.createElement( 'div' ); - fpsGraph.id = 'fpsGraph'; - fpsGraph.style.cssText = 'position:relative;width:74px;height:30px;background-color:#0ff'; - fpsDiv.appendChild( fpsGraph ); + var fpsText = document.createElement('div'); + fpsText.id = 'fpsText'; + fpsText.style.cssText = 'color:#0ff;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px'; + fpsText.innerHTML = 'FPS'; + fpsDiv.appendChild(fpsText); - while ( fpsGraph.children.length < 74 ) { + var fpsGraph = document.createElement('div'); + fpsGraph.id = 'fpsGraph'; + fpsGraph.style.cssText = 'position:relative;width:74px;height:30px;background-color:#0ff'; + fpsDiv.appendChild(fpsGraph); - var bar = document.createElement( 'span' ); - bar.style.cssText = 'width:1px;height:30px;float:left;background-color:#113'; - fpsGraph.appendChild( bar ); + while (fpsGraph.children.length < 74) { - } + var bar = document.createElement('span'); + bar.style.cssText = 'width:1px;height:30px;float:left;background-color:#113'; + fpsGraph.appendChild(bar); - var msDiv = document.createElement( 'div' ); - msDiv.id = 'ms'; - msDiv.style.cssText = 'padding:0 0 3px 3px;text-align:left;background-color:#020;display:none'; - container.appendChild( msDiv ); + } - var msText = document.createElement( 'div' ); - msText.id = 'msText'; - msText.style.cssText = 'color:#0f0;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px'; - msText.innerHTML = 'MS'; - msDiv.appendChild( msText ); + var msDiv = document.createElement('div'); + msDiv.id = 'ms'; + msDiv.style.cssText = 'padding:0 0 3px 3px;text-align:left;background-color:#020;display:none'; + container.appendChild(msDiv); - var msGraph = document.createElement( 'div' ); - msGraph.id = 'msGraph'; - msGraph.style.cssText = 'position:relative;width:74px;height:30px;background-color:#0f0'; - msDiv.appendChild( msGraph ); + var msText = document.createElement('div'); + msText.id = 'msText'; + msText.style.cssText = 'color:#0f0;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px'; + msText.innerHTML = 'MS'; + msDiv.appendChild(msText); - while ( msGraph.children.length < 74 ) { + var msGraph = document.createElement('div'); + msGraph.id = 'msGraph'; + msGraph.style.cssText = 'position:relative;width:74px;height:30px;background-color:#0f0'; + msDiv.appendChild(msGraph); - var bar = document.createElement( 'span' ); - bar.style.cssText = 'width:1px;height:30px;float:left;background-color:#131'; - msGraph.appendChild( bar ); + while (msGraph.children.length < 74) { - } + var bar = document.createElement('span'); + bar.style.cssText = 'width:1px;height:30px;float:left;background-color:#131'; + msGraph.appendChild(bar); - var setMode = function ( value ) { + } - mode = value; + var setMode = function (value) { - switch ( mode ) { + mode = value; - case 0: - fpsDiv.style.display = 'block'; - msDiv.style.display = 'none'; - break; - case 1: - fpsDiv.style.display = 'none'; - msDiv.style.display = 'block'; - break; - } + switch (mode) { - }; + case 0: + fpsDiv.style.display = 'block'; + msDiv.style.display = 'none'; + break; + case 1: + fpsDiv.style.display = 'none'; + msDiv.style.display = 'block'; + break; + } - var updateGraph = function ( dom, value ) { + }; - var child = dom.appendChild( dom.firstChild ); - child.style.height = value + 'px'; + var updateGraph = function (dom, value) { - }; + var child = dom.appendChild(dom.firstChild); + child.style.height = value + 'px'; - return { + }; - REVISION: 12, + return { - domElement: container, + REVISION: 12, - setMode: setMode, + domElement: container, - begin: function () { + setMode: setMode, - startTime = Date.now(); + begin: function () { - }, + startTime = Date.now(); - end: function () { + }, - var time = Date.now(); + end: function () { - ms = time - startTime; - msMin = Math.min( msMin, ms ); - msMax = Math.max( msMax, ms ); + var time = Date.now(); - msText.textContent = ms + ' MS (' + msMin + '-' + msMax + ')'; - updateGraph( msGraph, Math.min( 30, 30 - ( ms / 200 ) * 30 ) ); + ms = time - startTime; + msMin = Math.min(msMin, ms); + msMax = Math.max(msMax, ms); - frames ++; + msText.textContent = ms + ' MS (' + msMin + '-' + msMax + ')'; + updateGraph(msGraph, Math.min(30, 30 - ( ms / 200 ) * 30)); - if ( time > prevTime + 1000 ) { + frames++; - fps = Math.round( ( frames * 1000 ) / ( time - prevTime ) ); - fpsMin = Math.min( fpsMin, fps ); - fpsMax = Math.max( fpsMax, fps ); + if (time > prevTime + 1000) { - fpsText.textContent = fps + ' FPS (' + fpsMin + '-' + fpsMax + ')'; - updateGraph( fpsGraph, Math.min( 30, 30 - ( fps / 100 ) * 30 ) ); + fps = Math.round(( frames * 1000 ) / ( time - prevTime )); + fpsMin = Math.min(fpsMin, fps); + fpsMax = Math.max(fpsMax, fps); - prevTime = time; - frames = 0; + fpsText.textContent = fps + ' FPS (' + fpsMin + '-' + fpsMax + ')'; + updateGraph(fpsGraph, Math.min(30, 30 - ( fps / 100 ) * 30)); - } + prevTime = time; + frames = 0; - return time; + } - }, + return time; - update: function () { + }, - startTime = this.end(); + update: function () { - } + startTime = this.end(); - } + } -}; + } -if ( typeof module === 'object' ) { - - module.exports = Stats; - -} \ No newline at end of file + }; +})(); diff --git a/app/scripts/tower.js b/app/scripts/tower.js index ed80bf4..56c0310 100644 --- a/app/scripts/tower.js +++ b/app/scripts/tower.js @@ -1,67 +1,71 @@ -game.tower = function(p) { - this.type = p.type; - this.width = game.brickWidth; - this.height = game.brickWidth; - this.setColumn(p.column); - this.y = game.stage.height - this.height; - this.col = game.elements[this.type].color; - this.hw = this.width / 2; - this.hh = this.height / 2; - this.rad = Math.round(this.width / 10); - this.removed = false; - this.sprite = game.sprite.get(this.type); - this.shootDelay = this.latency = game.elements[this.type].latency; -}; +(function () { + "use strict"; -game.tower.prototype.setColumn = function(c) { - this.column = c; - this.x = this.column * this.width + game.gap * (this.column + 1); -}; + game.tower = function (p) { + this.type = p.type; + this.width = game.brickWidth; + this.height = game.brickWidth; + this.setColumn(p.column); + this.y = game.stage.height - this.height; + this.col = game.elements[this.type].color; + this.hw = this.width / 2; + this.hh = this.height / 2; + this.rad = Math.round(this.width / 10); + this.removed = false; + this.sprite = game.sprite.get(this.type); + this.shootDelay = this.latency = game.elements[this.type].latency; + }; -game.tower.prototype.destroy = function() { - this.removed = true; - var px = this.width / 6, - py = this.height / 6, - j, k; + game.tower.prototype.setColumn = function (c) { + this.column = c; + this.x = this.column * this.width + game.gap * (this.column + 1); + }; - game.rumble.level = 10; - for (j = 1; j < 6; j++) { - for (k = 1; k < 6; k++) { - game.particles.push(new game.particle({ - name: 'particle' + this.col, - x: this.x + j * px, - y: this.y + k * py, - w: px, - h: py, - col: this.col, - speed: game.rand.range(1, 5), - dist: game.rand.range(3, 5) * 100 - })); + game.tower.prototype.destroy = function () { + this.removed = true; + var px = this.width / 6, + py = this.height / 6, + j, k; + + game.rumble.level = 10; + for (j = 1; j < 6; j++) { + for (k = 1; k < 6; k++) { + game.particles.push(new game.particle({ + name: 'particle' + this.col, + x: this.x + j * px, + y: this.y + k * py, + w: px, + h: py, + col: this.col, + speed: game.rand.range(1, 5), + dist: game.rand.range(3, 5) * 100 + })); + } } - } - game.audio.play('explosion'); -}; + game.audio.play('explosion'); + }; -game.tower.prototype.shoot = function() { - if (game.s !== 0) { - game.bullets.push(new game.bullet({ - column: this.column, - type: this.type - })); - } -}; + game.tower.prototype.shoot = function () { + if (game.s !== 0) { + game.bullets.push(new game.bullet({ + column: this.column, + type: this.type + })); + } + }; -game.tower.prototype.update = function() { - this.shootDelay -= game.timer.delta; - if (this.shootDelay < 0) { - this.shoot(); - this.shootDelay = this.latency; - } -}; + game.tower.prototype.update = function () { + this.shootDelay -= game.timer.delta; + if (this.shootDelay < 0) { + this.shoot(); + this.shootDelay = this.latency; + } + }; -game.tower.prototype.draw = function() { - game.ctx.save(); - game.ctx.translate(this.x, this.y); - game.ctx.drawImage(this.sprite, 0, 0); - game.ctx.restore(); -}; + game.tower.prototype.draw = function () { + game.ctx.save(); + game.ctx.translate(this.x, this.y); + game.ctx.drawImage(this.sprite, 0, 0); + game.ctx.restore(); + }; +})(); diff --git a/app/scripts/utils.js b/app/scripts/utils.js index a13b099..ab6b79e 100644 --- a/app/scripts/utils.js +++ b/app/scripts/utils.js @@ -1,212 +1,213 @@ -// Create the global game namespace -var game = {}; - -// Object utilities -game.obj = { - extend: (function() { - 'use strict'; - var hasOwn = Object.prototype.hasOwnProperty, - toStr = Object.prototype.toString, - isPlainObject = function(obj) { - - if (!obj || toStr.call(obj) !== '[object Object]') { - return false; - } +(function () { + "use strict"; - var has_own_constructor = hasOwn.call(obj, 'constructor'), - has_is_property_of_method = obj.constructor && obj.constructor.prototype && hasOwn.call(obj.constructor.prototype, 'isPrototypeOf'); + game.obj = { + extend: (function () { + var hasOwn = Object.prototype.hasOwnProperty, + toStr = Object.prototype.toString, + isPlainObject = function (obj) { - // Not own constructor property must be Object - if (obj.constructor && !has_own_constructor && !has_is_property_of_method) { - return false; - } + if (!obj || toStr.call(obj) !== '[object Object]') { + return false; + } - // Own properties are enumerated firstly, so to speed up, - // if last one is own, then all properties are own. - var key; - for (key in obj) {} + var has_own_constructor = hasOwn.call(obj, 'constructor'), + has_is_property_of_method = obj.constructor && obj.constructor.prototype && hasOwn.call(obj.constructor.prototype, 'isPrototypeOf'); - return key === undefined || hasOwn.call(obj, key); - }; + // Not own constructor property must be Object + if (obj.constructor && !has_own_constructor && !has_is_property_of_method) { + return false; + } - return function extend() { - - var options, name, src, copy, copyIsArray, clone, - target = arguments[0], - i = 1, - length = arguments.length, - deep = false; - - // Handle a deep copy situation - if (typeof target === 'boolean') { - deep = target; - target = arguments[1] || {}; - // skip the boolean and the target - i = 2; - } else if ((typeof target !== 'object' && typeof target !== 'function') || target === null) { - target = {}; - } + // Own properties are enumerated firstly, so to speed up, + // if last one is own, then all properties are own. + var key; + for (key in obj) { + } - for (; i < length; ++i) { - options = arguments[i]; - // Only deal with non-null/undefined values - if (options !== null) { - // Extend the base object - for (name in options) { - src = target[name]; - copy = options[name]; - - // Prevent never-ending loop - if (target === copy) { - continue; - } + return key === undefined || hasOwn.call(obj, key); + }; + + return function extend() { + + var options, name, src, copy, copyIsArray, clone, + target = arguments[0], + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if (typeof target === 'boolean') { + deep = target; + target = arguments[1] || {}; + // skip the boolean and the target + i = 2; + } else if ((typeof target !== 'object' && typeof target !== 'function') || target === null) { + target = {}; + } - // Recurse if we're merging plain objects or arrays - if (deep && copy && (isPlainObject(copy) || (copyIsArray = Array.isArray(copy)))) { - if (copyIsArray) { - copyIsArray = false; - clone = src && Array.isArray(src) ? src : []; - } else { - clone = src && isPlainObject(src) ? src : {}; + for (; i < length; ++i) { + options = arguments[i]; + // Only deal with non-null/undefined values + if (options !== null) { + // Extend the base object + for (name in options) { + src = target[name]; + copy = options[name]; + + // Prevent never-ending loop + if (target === copy) { + continue; } - // Never move original objects, clone them - target[name] = extend(deep, clone, copy); - - // Don't bring in undefined values - } else if (copy !== undefined) { - target[name] = copy; + // Recurse if we're merging plain objects or arrays + if (deep && copy && (isPlainObject(copy) || (copyIsArray = Array.isArray(copy)))) { + if (copyIsArray) { + copyIsArray = false; + clone = src && Array.isArray(src) ? src : []; + } else { + clone = src && isPlainObject(src) ? src : {}; + } + + // Never move original objects, clone them + target[name] = extend(deep, clone, copy); + + // Don't bring in undefined values + } else if (copy !== undefined) { + target[name] = copy; + } } } } - } - // Return the modified object - return target; - }; - })() -}; + // Return the modified object + return target; + }; + })() + }; + + game.obj.extend(true, game, { + + vendor: { + prefixes: ['', 'ms', 'webkit', 'moz', 'o'], + get: function (prop) { + var name, i; + for (i = 0; i < this.prefixes.length; i++) { + name = this.prefixes[i] + (this.prefixes[i] === '' ? prop : prop.charAt(0).toUpperCase() + prop.slice(1)); + if (typeof document.body.style[name] !== 'undefined') { + return name; + } + } + return null; + } + }, + dom: { + get: function (id) { + return document.getElementById(id); + }, + create: function (n) { + return document.createElement(n); + } + }, -game.obj.extend(true, game, { + arr: { + create: function (n, v) { + var arr = [], + val; + for (var i = 0; i < n; i += 1) { + val = typeof v === 'undefined' ? [] : v; + arr.push(val); - // Vendor prefixes utility - vendor: { - prefixes: ['', 'ms', 'webkit', 'moz', 'o'], - get: function(prop) { - var name, i; - for (i = 0; i < this.prefixes.length; i++) { - name = this.prefixes[i] + (this.prefixes[i] === '' ? prop : prop.charAt(0).toUpperCase() + prop.slice(1)); - if (typeof document.body.style[name] !== 'undefined') { - return name; + } + return arr; + }, + move: function (arr, from, to) { + arr[from] = arr.splice(to, 1, arr[from])[0]; + }, + loop: function (arr, cb, context) { + var len = arr.length, + i = 0; + for (; i < len; i += 1) { + cb.call(context || null, arr[i], i); } } - return null; - } - }, - - // Dom utilities - dom: { - get: function(id) { - return document.getElementById(id); }, - create: function(n) { - return document.createElement(n); - } - }, - - // Array utilities - arr: { - create: function(n, v) { - var arr = [], - val; - while (n--) { - val = typeof v === 'undefined' ? [] : v; - arr.push(val); + + rand: { + intg: function (max) { + return Math.random() * (max || 0xfffffff) | 0; + }, + flot: function () { + return Math.random(); + }, + bool: function () { + return Math.random() > 0.5; + }, + range: function (min, max) { + return game.rand.intg(max - min) + min; + }, + rangef: function (min, max) { + return game.rand.flot() * (max - min) + min; + }, + select: function (source) { + return source[game.rand.range(0, source.length)]; } - return arr; }, - move: function(arr, from, to) { - arr[from] = arr.splice(to, 1, arr[from])[0]; - } - }, - // Random number utilities - rand: { - intg: function(max) { - return Math.random() * (max || 0xfffffff) | 0; - }, - flot: function() { - return Math.random(); - }, - bool: function() { - return Math.random() > 0.5; + PI2: Math.PI * 2, + RAD: Math.PI / 180, + DEG: 180 / Math.PI, + + noop: function () { }, - range: function(min, max) { - return game.rand.intg(max - min) + min; + + line: function (c, x1, y1, x2, y2, col, lw) { + c.strokeStyle = col; + c.lineWidth = lw; + c.beginPath(); + c.moveTo(x1, y1); + c.lineTo(x2, y2); + c.stroke(); }, - rangef: function(min, max) { - return game.rand.flot() * (max - min) + min; + rect: function (c, x, y, w, h, r, col, lw, fillcolor, glow) { + x += lw; + w -= lw * 2; + y += lw; + h -= lw * 2; + c.beginPath(); + c.strokeStyle = col; + c.lineWidth = lw; + c.moveTo(x + r, y); + c.lineTo(x + w - r, y); + c.quadraticCurveTo(x + w, y, x + w, y + r); + c.lineTo(x + w, y + h - r); + c.quadraticCurveTo(x + w, y + h, x + w - r, y + h); + c.lineTo(x + r, y + h); + c.quadraticCurveTo(x, y + h, x, y + h - r); + c.lineTo(x, y + r); + c.quadraticCurveTo(x, y, x + r, y); + c.closePath(); + c.stroke(); + if (fillcolor) { + c.fillStyle = fillcolor; + c.fill(); + } + if (glow) { + game.rect(c, x - lw, y - lw, w + lw * 2, h + lw * 2, r * 1.2, glow, lw / 2); + } }, - select: function(source) { - return source[game.rand.range(0, source.length)]; - } - }, - - // Some math constants - PI2: Math.PI * 2, - RAD: Math.PI / 180, - DEG: 180 / Math.PI, - - // Empty function - noop: function() {}, - - // Drawing utilities - line: function(c, x1, y1, x2, y2, col, lw) { - c.strokeStyle = col; - c.lineWidth = lw; - c.beginPath(); - c.moveTo(x1, y1); - c.lineTo(x2, y2); - c.stroke(); - }, - rect: function(c, x, y, w, h, r, col, lw, fillcolor, glow) { - x += lw; - w -= lw * 2; - y += lw; - h -= lw * 2; - c.beginPath(); - c.strokeStyle = col; - c.lineWidth = lw; - c.moveTo(x + r, y); - c.lineTo(x + w - r, y); - c.quadraticCurveTo(x + w, y, x + w, y + r); - c.lineTo(x + w, y + h - r); - c.quadraticCurveTo(x + w, y + h, x + w - r, y + h); - c.lineTo(x + r, y + h); - c.quadraticCurveTo(x, y + h, x, y + h - r); - c.lineTo(x, y + r); - c.quadraticCurveTo(x, y, x + r, y); - c.closePath(); - c.stroke(); - if (fillcolor) { - c.fillStyle = fillcolor; - c.fill(); - } - if (glow) { - game.rect(c, x - lw, y - lw, w + lw * 2, h + lw * 2, r * 1.2, glow, lw / 2); + sine: function (c, x1, y1, x2, y2, col, lw) { + var xc = (x1 + x2) / 2, + yc = (y1 + y2) / 2; + + c.strokeStyle = col; + c.lineWidth = lw; + c.beginPath(); + c.moveTo(x1, y1); + c.bezierCurveTo(x1, y1, xc / 2, yc * 1.5, xc, yc); + c.bezierCurveTo(xc * 1.5, yc / 2, x2, y2, x2, y2); + c.stroke(); } - }, - sine: function(c, x1, y1, x2, y2, col, lw) { - var xc = (x1 + x2) / 2, - yc = (y1 + y2) / 2; - - c.strokeStyle = col; - c.lineWidth = lw; - c.beginPath(); - c.moveTo(x1, y1); - c.bezierCurveTo(x1, y1, xc / 2, yc * 1.5, xc, yc); - c.bezierCurveTo(xc * 1.5, yc / 2, x2, y2, x2, y2); - c.stroke(); - } -}); + }); +})(); diff --git a/app/styles/common.less b/app/styles/common.less index d6f9b39..c4ed123 100644 --- a/app/styles/common.less +++ b/app/styles/common.less @@ -8,6 +8,7 @@ body { overflow: hidden; font-family: Arial; } + h1 { font-size: 70px; line-height: 70px; @@ -22,6 +23,7 @@ h1 { } } } + h2 { font-size: 14px; line-height: 17px; @@ -29,13 +31,15 @@ h2 { margin: 0; padding: 0 0 20px; } + a { color: #888; &:hover { color: #a00; } } - p { + +p { font-size: 12px; line-height: 15px; font-weight: normal; @@ -43,11 +47,13 @@ a { margin: 0; padding: 0 0 10px; } + ul, li { margin: 0; padding: 0; } + em, li { font-size: 30px; @@ -60,25 +66,30 @@ li { color: #888; } } -em{ + +em { display: block; font-size: 18px; line-height: 21px; } + #cnt { display: inline-block; position: relative; } + #c { position: relative; z-index: 2; background: rgba(255, 255, 255, 0.04); } + #b { display: none; margin-top: -10000px; z-index: 0 } + #b1, #b2, #b3 { @@ -91,6 +102,7 @@ em{ bottom: 0; right: 0 } + #hud { color: #fff; font-size: 16px; @@ -106,6 +118,7 @@ em{ } } } + #msg, #mnu { display: none; @@ -123,16 +136,20 @@ em{ text-align: center; padding: 20px; } + #mnu { height: 330px; margin-top: -165px; } + #crd { display: none; } + #re { display: none; } + #hlp { div { margin-bottom: 10px; @@ -144,6 +161,7 @@ em{ margin: 0 5px 5px 0; } } + #f { position: absolute; left: 0; diff --git a/gulpfile.js b/gulpfile.js index 2ba51cc..7b6f4e1 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -32,7 +32,7 @@ var browserSync = require('browser-sync'), dest: { root: 'dist', html: 'dist/*.html', - js: 'dist', + js: 'dist/*.js', css: 'dist' }, tmp: { @@ -80,7 +80,7 @@ gulp.task('html', function() { .pipe(plumber()) .pipe(usemin({ css: [csso(), rev()], - js: [uglify(), rev()], + js: [rev()], html: [minifyHtml({ comments: true, conditionals: true, @@ -95,7 +95,7 @@ gulp.task('html', function() { gulp.task('browser-sync', function() { browserSync({ server: { - baseDir: ['dist', 'app', 'tmp', './'] + baseDir: ['dist', 'app', 'tmp'] } }); }); @@ -122,11 +122,32 @@ gulp.task('inline', function() { .pipe(gulp.dest(paths.tmp.root)); }); +gulp.task('use-strict:remove', function() { + return gulp.src(paths.src.js) + .pipe(plumber()) + .pipe(replace(/^(\(function[\s\S]*strict";)([\s\S]*)(\}\)\(\);\r\n)$/, '$2')) + .pipe(gulp.dest(paths.tmp.root + '/scripts')); +}); + +gulp.task('use-strict:add', function() { + return gulp.src(paths.dest.js) + .pipe(plumber()) + .pipe(replace(/([\s\S]*)/, '(function () {"use strict";$1})();')) + .pipe(gulp.dest(paths.dest.root)); +}); + +gulp.task('uglify', function() { + return gulp.src(paths.dest.js) + .pipe(plumber()) + .pipe(uglify()) + .pipe(gulp.dest(paths.dest.root)); +}); + gulp.task('zip', function () { - return gulp.src(paths.dest.root + '/*') + return gulp.src(paths.tmp.root + '/index.html') .pipe(plumber()) - .pipe(zip('dist.zip')) - .pipe(gulp.dest(paths.tmp.root)); + .pipe(zip('inlined-compressed.zip')) + .pipe(gulp.dest(paths.dest.root)); }); gulp.task('build', function() { @@ -134,7 +155,10 @@ gulp.task('build', function() { 'lint', 'less' ], + 'use-strict:remove', 'html', + 'use-strict:add', + 'uglify', 'inline', 'browser-sync', 'zip'); @@ -147,4 +171,4 @@ gulp.task('default', function() { 'watch' ], 'browser-sync'); -}); \ No newline at end of file +}); diff --git a/package.json b/package.json index 4dbda8e..d154507 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "FEWA", - "version": "1.0.1", + "version": "1.1.1", "description": "Shooter game for the js13k contest", "authors": [ "Csaba ", @@ -9,27 +9,27 @@ "license": "free", "repository": "cstuncsik/fewa.git", "devDependencies": { - "browser-sync": "^1.6.5", - "gulp": "^3.8.10", - "gulp-autoprefixer": "0.0.8", - "gulp-cached": "^1.0.1", - "gulp-changed": "^1.0.0", + "browser-sync": "^2.7.6", + "gulp": "^3.9.0", + "gulp-autoprefixer": "2.3.0", + "gulp-cached": "^1.1.0", + "gulp-changed": "^1.2.1", "gulp-clean": "^0.3.1", - "gulp-csso": "^0.2.9", - "gulp-jshint": "^1.9.0", - "gulp-less": "^1.3.5", - "gulp-minify-html": "^0.1.4", - "gulp-plumber": "^0.6.6", - "gulp-replace": "^0.4.0", - "gulp-rev": "^1.1.0", - "gulp-size": "^1.0.0", - "gulp-uglify": "^0.3.2", - "gulp-usemin": "^0.3.8", + "gulp-csso": "^1.0.0", + "gulp-jshint": "^1.11.0", + "gulp-less": "^3.0.3", + "gulp-minify-html": "^1.0.3", + "gulp-plumber": "^1.0.1", + "gulp-replace": "^0.5.3", + "gulp-rev": "^4.0.0", + "gulp-size": "^1.2.1", + "gulp-uglify": "^1.2.0", + "gulp-usemin": "^0.3.11", "gulp-using": "0.0.1", - "gulp-util": "^3.0.0", - "gulp-zip": "^2.0.1", - "jshint-stylish": "^0.4.0", - "run-sequence": "^0.3.7" + "gulp-util": "^3.0.5", + "gulp-zip": "^3.0.2", + "jshint-stylish": "^2.0.0", + "run-sequence": "^1.1.0" }, "engines": { "node": ">= 0.10.0",