├── js ├── game │ ├── ents │ │ ├── bg3.js │ │ ├── bgtitle.js │ │ ├── battery.js │ │ ├── star.js │ │ ├── msg.js │ │ ├── blocker.js │ │ ├── portal.js │ │ ├── fader.js │ │ ├── explosion.js │ │ ├── bg1.js │ │ ├── bg2.js │ │ ├── bullet.js │ │ ├── spark.js │ │ ├── crate.js │ │ ├── drone.js │ │ ├── floater.js │ │ ├── bg.js │ │ └── robo.js │ ├── states │ │ ├── splash.js │ │ ├── gameover.js │ │ ├── tutorial.js │ │ ├── title.js │ │ └── play.js │ ├── levels.js │ └── data.js ├── engine │ ├── base.js │ ├── shake.js │ ├── emitter.js │ ├── particle.js │ ├── audio.js │ ├── state.js │ ├── tile.js │ ├── input.js │ ├── load.js │ ├── inherit.js │ ├── draw.js │ ├── sprite.js │ ├── helpers.js │ └── game.js └── lib │ ├── stats.js │ ├── jsfxr.min.js │ └── jsfxr.js ├── game.zip ├── play.png ├── .gitignore ├── play2.png ├── splash.png ├── icon64x64.png ├── screenshot160x160.png ├── screenshot400x250.png ├── encode.js ├── img.php ├── package.json ├── README.md ├── dev.html ├── gulpfile.js ├── g.js ├── index.html └── g.min.js /js/game/ents/bg3.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /game.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eoinmcg/roboflip/HEAD/game.zip -------------------------------------------------------------------------------- /play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eoinmcg/roboflip/HEAD/play.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /a 2 | /node_modules 3 | /screenshots 4 | todo.md 5 | sync 6 | -------------------------------------------------------------------------------- /play2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eoinmcg/roboflip/HEAD/play2.png -------------------------------------------------------------------------------- /splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eoinmcg/roboflip/HEAD/splash.png -------------------------------------------------------------------------------- /icon64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eoinmcg/roboflip/HEAD/icon64x64.png -------------------------------------------------------------------------------- /screenshot160x160.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eoinmcg/roboflip/HEAD/screenshot160x160.png -------------------------------------------------------------------------------- /screenshot400x250.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eoinmcg/roboflip/HEAD/screenshot400x250.png -------------------------------------------------------------------------------- /encode.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var img = process.argv.slice(2)[0]; 3 | 4 | fs.readFile(img, function(err, original_data){ 5 | var base64Image = original_data.toString('base64'); 6 | var append = 'data:image/gif;base64,'; 7 | console.log(append+base64Image); 8 | }); 9 | -------------------------------------------------------------------------------- /js/game/ents/bgtitle.js: -------------------------------------------------------------------------------- 1 | $.BgTitle = $.Bg.extend({ 2 | 3 | init: function(g, o){ 4 | 5 | this._super(g, o); 6 | 7 | this.tiles = []; 8 | 9 | this.tiles.push(new $.Tile(this.g, { 10 | ctx: this.ctx, x: 0, y: -5, i: g.draw.scale(g.imgs.window, 10), speed: -( o.speed / 2 ) 11 | 12 | })); 13 | 14 | } 15 | 16 | 17 | 18 | }); 19 | 20 | -------------------------------------------------------------------------------- /js/engine/base.js: -------------------------------------------------------------------------------- 1 | var $ = { 2 | v: 0.1, 3 | // http://androidarts.com/palette/16pal.htm 4 | cols : { 5 | black: '#000', 6 | ash: '#9d9d9d', 7 | blind: '#fff', 8 | bloodred: '#be2633', 9 | pigmeat: '#e06f8b', 10 | oldpoop: '#493C2B', 11 | newpoop: '#a46422', 12 | blaze: '#eb8931', 13 | zornskin: '#f7e26b', 14 | shadegreen: '#2f484e', 15 | leafgreen: '#44891A', 16 | slimegreen: '#A3CE27', 17 | nightblue: '#1B2632', 18 | seablue: '#005784', 19 | skyblue: '#31A2F2', 20 | cloudblue: '#B2DCEF' 21 | } 22 | }; 23 | 24 | 25 | -------------------------------------------------------------------------------- /img.php: -------------------------------------------------------------------------------- 1 | 0) { 30 | c.style.marginTop = m + 'px'; 31 | c.style.marginLeft = (m + this.l) + 'px'; 32 | } 33 | 34 | 35 | }; 36 | 37 | }; 38 | -------------------------------------------------------------------------------- /js/game/ents/battery.js: -------------------------------------------------------------------------------- 1 | $.Battery = $.Sprite.extend({ 2 | init: function(g, o) { 3 | this._super(g, o); 4 | this.scale = 6; 5 | this.group = 'battery'; 6 | this.speed = g.bg.speed * -1; 7 | this.frames = 1; 8 | this.mkImg('battery'); 9 | 10 | this.vx = ( g.bg.speed / 1000 ) * -1; 11 | this.x = 480; 12 | this.y = 120; 13 | 14 | }, 15 | 16 | update: function() { 17 | 18 | this._super(); 19 | this.hitGroup('player'); 20 | 21 | }, 22 | 23 | 24 | doDamage: function(o) { 25 | var g = this.g; 26 | if (o.group === 'player') { 27 | this.remove = true; 28 | g.ents.push(new $.Msg(g, { 29 | text: g.p1.doPowerup(), 30 | col: 'p', 31 | x: this.x, y: this.y 32 | })); 33 | } 34 | } 35 | 36 | 37 | 38 | 39 | }); 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /js/game/ents/star.js: -------------------------------------------------------------------------------- 1 | $.Star = $.Sprite.extend({ 2 | init: function(g, o) { 3 | this._super(g, o); 4 | this.scale = 6; 5 | this.group = 'star'; 6 | this.speed = g.bg.speed * -1; 7 | this.frames = 1; 8 | this.mkImg('star'); 9 | 10 | this.vx = ( g.bg.speed / 1000 ) * -1; 11 | this.vy = 0.05 * (this.x > (g.w / 2) ? 1 : -1); 12 | 13 | }, 14 | 15 | update: function() { 16 | 17 | this._super(); 18 | this.hitGroup('player'); 19 | 20 | }, 21 | 22 | 23 | doDamage: function(o) { 24 | var g = this.g; 25 | if (o.group === 'player') { 26 | this.remove = true; 27 | g.audio.play('powerup'); 28 | g.ents.push(new $.Msg(g, { 29 | text: this.val, 30 | col: 'p', 31 | x: this.x, y: this.y 32 | })); 33 | } 34 | } 35 | 36 | 37 | 38 | 39 | }); 40 | 41 | 42 | -------------------------------------------------------------------------------- /js/game/ents/msg.js: -------------------------------------------------------------------------------- 1 | 2 | $.Msg = $.Sprite.extend({ 3 | 4 | init: function(g, o) { 5 | this._super(g, o); 6 | this.vy = -1; 7 | this.lifespan = 2; 8 | this.ttl = this.lifespan; 9 | this.w = 10; 10 | this.h = 10; 11 | this.col = o.col || 'w'; 12 | this.f = g.mkFont(this.col, 3); 13 | this.vx = -0.09; 14 | this.vy = -0.05; 15 | }, 16 | 17 | update: function() { 18 | var g = this.g; 19 | this._super(); 20 | 21 | this.x += this.vx * g.dt; 22 | this.y += this.vy * g.dt; 23 | 24 | this.ttl -= g.dt / 1000; 25 | if (this.ttl < 0) { 26 | this.kill(); 27 | } 28 | }, 29 | 30 | render: function() { 31 | var g = this.g; 32 | 33 | g.ctx.globalAlpha = (this.ttl / this.lifespan); 34 | g.draw.text(this.text, this.f, this.x, this.y); 35 | g.ctx.globalAlpha = 1; 36 | } 37 | 38 | }); 39 | -------------------------------------------------------------------------------- /js/engine/emitter.js: -------------------------------------------------------------------------------- 1 | $.Emitter = function(g) { 2 | 3 | this.g = g; 4 | 5 | 6 | this.particle = function(p, x, y) { 7 | 8 | var g = this.g, i; 9 | 10 | if (g.ios) { return; } 11 | 12 | for (i = 0; i < p; i+= 1) { 13 | g.ents.push(new $.Particle(g, { 14 | x: x, y: y } 15 | )); 16 | } 17 | 18 | 19 | }; 20 | 21 | this.explosion = function(num, x, y, particles, magnitude) { 22 | var g = this.g, 23 | r = $.H.rnd; 24 | 25 | // if (g.ios) { return; } 26 | 27 | while (num--) { 28 | window.setTimeout(function() { 29 | g.ents.push(new $.Explosion(g, { 30 | x: x + r(-10, 10), y: y + r(-10, 10), 31 | magnitude: magnitude, 32 | particles: particles 33 | })); 34 | }, num * 150); 35 | } 36 | }; 37 | 38 | 39 | 40 | }; 41 | -------------------------------------------------------------------------------- /js/game/ents/blocker.js: -------------------------------------------------------------------------------- 1 | $.Blocker = $.Sprite.extend({ 2 | 3 | 4 | init: function(g, o) { 5 | 6 | this._super(g,o); 7 | this.scale = 5; 8 | this.mkImg('blocker'); 9 | this.group = 'baddies'; 10 | this.health = 10; 11 | 12 | 13 | }, 14 | 15 | 16 | update: function() { 17 | 18 | this._super(); 19 | 20 | var g = this.g, 21 | p1 = g.p1; 22 | 23 | if (!p1.dead) { 24 | this.vx = -(this.speed); 25 | this.vy = 0; 26 | } else { 27 | this.vx = 0; 28 | this.vy = 0; 29 | } 30 | 31 | this.hitGroup('player'); 32 | 33 | }, 34 | 35 | receiveDamage: function() { 36 | this.g.emitter.particle(2, this.x, this.y); 37 | this.x += 2; 38 | this.health -= 2; 39 | // if (this.health > 0) { 40 | // this.kill(); 41 | // } 42 | }, 43 | 44 | 45 | kill: function() { 46 | // return; 47 | } 48 | 49 | 50 | 51 | }); 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /js/game/ents/portal.js: -------------------------------------------------------------------------------- 1 | $.Portal = $.Sprite.extend({ 2 | init: function(g, o) { 3 | this._super(g, o); 4 | this.scale = 4; 5 | this.health = this.scale * 10; 6 | this.group = 'portal'; 7 | this.speed = 0; 8 | 9 | this.h = 50; 10 | this.w = 20; 11 | 12 | this.hide(); 13 | 14 | 15 | }, 16 | 17 | update: function() { 18 | 19 | this._super(); 20 | this.hitGroup('player'); 21 | 22 | }, 23 | 24 | 25 | show: function() { 26 | var g = this.g; 27 | this.x = g.w - this.w; 28 | this.y = g.h /2 - (this.h / 2); 29 | this.col = $.cols.slimegreen; 30 | }, 31 | 32 | hide: function() { 33 | var g = this.g; 34 | this.x = g.w * -2; 35 | this.active = false; 36 | }, 37 | 38 | doDamage: function(o) { 39 | var g = this.g; 40 | if (o.group === 'player') { 41 | this.active = true; 42 | g.bonus = { 43 | x: this.x, y: this.y 44 | }; 45 | 46 | } 47 | } 48 | 49 | 50 | 51 | }); 52 | 53 | 54 | -------------------------------------------------------------------------------- /js/game/ents/fader.js: -------------------------------------------------------------------------------- 1 | $.Fader = $.Sprite.extend({ 2 | init: function(g, o) { 3 | 4 | this._super(g, o); 5 | this.w = 0; 6 | this.h = g.h; 7 | this.remove = false; 8 | this.col = o.col; 9 | this.cb = o.cb; 10 | this.dir = o.dir || 1; 11 | 12 | 13 | 14 | }, 15 | 16 | update: function() { 17 | 18 | var g = this.g; 19 | 20 | this.tick = g.dt / 1.5; 21 | this.w += this.tick; 22 | 23 | if (this.w > g.w) { 24 | if (this.cb) { 25 | this.cb(); 26 | } 27 | this.remove= true; 28 | } 29 | 30 | }, 31 | 32 | render: function() { 33 | 34 | var g = this.g; 35 | 36 | if (this.dir === 1) { 37 | g.draw.rect(0, 0, this.w, this.h, this.col); 38 | g.draw.rect(g.w, 0, -this.w, this.h, this.col); 39 | } else if (this.dir === -1) { 40 | g.draw.rect(0, 0, g.w - this.w, this.h, this.col); 41 | g.draw.rect(g.w, 0, -(g.w - this.w), this.h, this.col); 42 | } 43 | 44 | 45 | 46 | } 47 | 48 | 49 | }); 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FLIPBOT 2 | 3 | ## Entry for the 2015 js13kgames.com competition 4 | 5 | By [@eoinmcg](https://twitter.com/eoinmcg) 6 | 7 | ![Screenshot](https://github.com/eoinmcg/roboflip/raw/master/splash.png) 8 | 9 | ### Background: 10 | 11 | Intergalactic space pirates have invaded over your spaceship. 12 | All crew members are in deep sleep. Which means that you a humble, FlipBot 2000, 13 | must save the day! Are you up to the challenge? 14 | 15 | ### How to play: 16 | 17 | - Tap left / press [SPACEBAR] to jump 18 | - Tap right / press [SHIFT] to shoot 19 | - Double jump reverses gravity 20 | - Collect batteries to powerup your gun 21 | - Jump into the portal at the end of each level for bonus points 22 | 23 | ### Tested on: 24 | 25 | - Firefox (Desktop & Android) 26 | - Chrome (Desltop & Android) 27 | - iOS 28 | - Chrome 29 | - Firefox 30 | 31 | ### Features: 32 | 33 | - 16 colors! 34 | - Blocky graphics! 35 | - 3 levels of parallax scrolling 36 | - Explosions! 37 | - Particles! 38 | - Mayhem! 39 | 40 | 41 | ![Screenshot](https://github.com/eoinmcg/roboflip/raw/master/play.png) 42 | ![Screenshot](https://github.com/eoinmcg/roboflip/raw/master/play2.png) 43 | 44 | -------------------------------------------------------------------------------- /js/engine/particle.js: -------------------------------------------------------------------------------- 1 | $.Particle = $.Sprite.extend({ 2 | 3 | init: function(g, o){ 4 | 5 | var cols = ['blaze'], 6 | i; 7 | 8 | this._super(g, o); 9 | 10 | this.name = 'particle'; 11 | this.scale = 1; 12 | this.group = 'na'; 13 | 14 | this.w = 4; 15 | this.h = 4; 16 | 17 | this.v = (Math.random() * 5) + 5; 18 | this.lifespan = $.H.rnd(20,50); 19 | this.ttl = this.lifespan; 20 | this.alpha = 1; 21 | 22 | 23 | this.vx = ( $.H.rnd(0, 600) - 300 ) / 1000; 24 | this.vy = ( $.H.rnd(0, 600) - 300 ) / 1000; 25 | 26 | }, 27 | 28 | 29 | update: function() { 30 | this._super(); 31 | 32 | // var g = this.g; 33 | // this.vx += 0.0095 * g.dt; 34 | // this.vy += 0.0095 * g.dt; 35 | 36 | this.ttl -= 1; 37 | if (this.ttl < 0) { 38 | this.remove = true; 39 | } 40 | 41 | }, 42 | 43 | 44 | render: function() { 45 | 46 | var g = this.g; 47 | 48 | g.ctx.globalAlpha = (this.ttl / this.lifespan); 49 | // this._super(); 50 | g.draw.rect(this.x, this.y, 5, 5, $.cols.zornskin); 51 | g.ctx.globalAlpha = 1; 52 | 53 | } 54 | 55 | 56 | }); 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /js/engine/audio.js: -------------------------------------------------------------------------------- 1 | $.Audio = function(sounds) { 2 | 3 | this.sounds = sounds; 4 | this.sfx = {}; 5 | 6 | this.init = function() { 7 | 8 | var n; 9 | 10 | for (n in this.sounds) { 11 | this.add(n, 10, $.data.sfx[n]); 12 | } 13 | }; 14 | 15 | 16 | this.add = function(key, n, data) { 17 | 18 | // inspired by 19 | // http://codepen.io/jackrugile/pen/crxws 20 | var i; 21 | 22 | this.sfx[key] = { 23 | tick: 0, 24 | pool: [] 25 | }; 26 | 27 | for( i = 0; i < n; i++ ) { 28 | var a = new Audio(); 29 | a.src = jsfxr( data ); 30 | this.sfx[key].pool.push( a ); 31 | } 32 | }; 33 | 34 | 35 | this.play = function(key) { 36 | 37 | var sfx = this.sfx[key]; 38 | 39 | sfx.pool[sfx.tick].play(); 40 | sfx.tick = ( sfx.tick < sfx.pool.length - 1 ) ? 41 | sfx.tick += 1 : sfx.tick = 0; 42 | }; 43 | 44 | 45 | this.say = function(text, rate) { 46 | if (typeof SpeechSynthesisUtterance !== 'undefined') { 47 | var msg = new SpeechSynthesisUtterance(text); 48 | msg.pitch = 0.1; 49 | msg.rate = rate || 0.7; 50 | speechSynthesis.speak(msg); 51 | } 52 | }; 53 | 54 | }; 55 | -------------------------------------------------------------------------------- /js/game/ents/explosion.js: -------------------------------------------------------------------------------- 1 | $.Explosion = $.Sprite.extend({ 2 | 3 | init: function(g, o){ 4 | 5 | var i; 6 | 7 | this._super(g, o); 8 | 9 | this.name = 'explosion'; 10 | this.scale = 1; 11 | this.group = 'na'; 12 | this.startX = o.x; 13 | this.startY = o.y; 14 | this.particles = o.particles || 3; 15 | this.magnitude = o.magnitude || 9; 16 | this.factor = 1; 17 | this.mkImg('circle'); 18 | 19 | g.emitter.particle(this.particles, this.x, this.y); 20 | 21 | g.audio.play('explode'); 22 | this.angle = 0; 23 | this.grow = 1; 24 | 25 | }, 26 | 27 | 28 | update: function() { 29 | 30 | var g = this.g; 31 | 32 | this._super(); 33 | 34 | if (this.scale <= this.magnitude) { 35 | this.scale += this.factor; 36 | } 37 | if (this.scale === this.magnitude) { 38 | this.factor *= -1; 39 | } 40 | if (this.scale <= 1) { 41 | this.remove = true; 42 | } 43 | 44 | this.mkImg('circle'); 45 | 46 | }, 47 | 48 | 49 | render: function() { 50 | 51 | var x = this.startX - (this.w /2), 52 | y = this.startY - (this.h / 2), 53 | g = this.g, 54 | i = this.i; 55 | // i = g.draw.rotate(this.i, this.angle); 56 | g.ctx.drawImage(i, x, y); 57 | 58 | } 59 | 60 | 61 | }); 62 | 63 | 64 | -------------------------------------------------------------------------------- /js/game/ents/bg1.js: -------------------------------------------------------------------------------- 1 | $.Bg1 = $.Bg.extend({ 2 | 3 | init: function(g, o){ 4 | 5 | this._super(g, o); 6 | 7 | var n = 20, 8 | i = g.imgs; 9 | sc = g.draw.scale; 10 | 11 | this.g = g; 12 | this.c = $.H.mkCanvas(g.w, g.h); 13 | this.ctx = this.c.getContext('2d'); 14 | this.draw = new $.Draw(this.ctx, g.w, g.h); 15 | 16 | this.ground = 40; 17 | this.speed = o.speed; 18 | 19 | this.tiles = []; 20 | 21 | var floor = this.makeFloor(sc(i.ground, 4)); 22 | 23 | this.tiles.push(new $.Tile(this.g, { 24 | ctx: this.ctx, x: 0, y: 280, i: floor, speed: -o.speed 25 | })); 26 | 27 | this.tiles.push(new $.Tile(this.g, { 28 | ctx: this.ctx, x: 0, y: 0, i: g.draw.flip( floor, 0, 1 ), speed: -o.speed 29 | })); 30 | 31 | this.tiles.push(new $.Tile(this.g, { 32 | ctx: this.ctx, x: 0, y: 230, i: g.draw.scale(g.imgs.bg1, 10), speed: -( o.speed / 1.5 ) 33 | 34 | })); 35 | 36 | this.tiles.push(new $.Tile(this.g, { 37 | ctx: this.ctx, x: 0, y: 41, i: g.draw.scale(g.imgs.window, 8), speed: -( o.speed / 2 ) 38 | 39 | })); 40 | 41 | this.stars = []; 42 | while (n--) { 43 | this.stars.push({ 44 | x: $.H.rnd(0, g.w), 45 | y: $.H.rnd(0, g.h)}); 46 | } 47 | 48 | 49 | 50 | } 51 | 52 | 53 | }); 54 | 55 | -------------------------------------------------------------------------------- /js/game/states/splash.js: -------------------------------------------------------------------------------- 1 | $.Splash = $.State.extend({ 2 | 3 | init: function(g) { 4 | 5 | this._super(g); 6 | 7 | this.h1 = g.mkFont('g', 6); 8 | this.p = g.mkFont('w', 2); 9 | 10 | this.fade = 0; 11 | this.skull = g.draw.scale(g.imgs.skull_w, 6); 12 | 13 | $.fullScreen = function() { 14 | $.H.fullScreen(g.c); 15 | }; 16 | 17 | window.addEventListener('click', $.fullScreen); 18 | window.addEventListener('keydown', $.fullScreen); 19 | window.addEventListener('touchstart', $.fullScreen); 20 | 21 | }, 22 | 23 | 24 | update: function() { 25 | 26 | var g = this.g; 27 | 28 | this._super(); 29 | 30 | this.fade += ( 0.01 * ( g.dt * 5 ) ) / 100; 31 | 32 | if (this.fade > 2) { 33 | g.audio.play('powerup'); 34 | g.changeState('Title'); 35 | } 36 | 37 | }, 38 | 39 | 40 | render: function() { 41 | 42 | var g = this.g, 43 | s = this, 44 | c = $.cols, n; 45 | 46 | g.draw.rect(0, 0, g.w, g.h, $.cols.black); 47 | 48 | g.ctx.globalAlpha = this.fade; 49 | g.draw.text('EOINMCG PRESENTS', this.p, false, 100); 50 | 51 | g.ctx.globalAlpha = this.fade / 10; 52 | g.ctx.drawImage(this.skull, 220, 150); 53 | g.ctx.globalAlpha = 1; 54 | 55 | s._super(); 56 | 57 | } 58 | 59 | 60 | 61 | }); 62 | 63 | 64 | -------------------------------------------------------------------------------- /js/game/states/gameover.js: -------------------------------------------------------------------------------- 1 | $.Gameover = $.State.extend({ 2 | 3 | init: function(g) { 4 | 5 | this._super(g); 6 | 7 | this.h1 = g.mkFont('b', 7); 8 | this.h2 = g.mkFont('g', 5); 9 | this.p = g.mkFont('w', 2); 10 | 11 | if (g.newHi) { 12 | this.skull = g.draw.scale(g.imgs.star_w, 27); 13 | } else { 14 | this.skull = g.draw.scale(g.imgs.skull_w, 27); 15 | } 16 | 17 | g.audio.play('die'); 18 | }, 19 | 20 | update: function() { 21 | 22 | var g = this.g; 23 | 24 | this._super(); 25 | 26 | if ( g.doJump && g.tick > 1) { 27 | g.ents.push(new $.Fader(g, { 28 | col: $.cols.pigmeat, 29 | dir: -1, 30 | cb: function() { 31 | g.changeState('Title'); 32 | } 33 | })); 34 | } 35 | }, 36 | 37 | render: function() { 38 | 39 | var g = this.g; 40 | 41 | this._super(); 42 | 43 | g.draw.rect(0, 0, g.w, g.h, $.cols.bloodred); 44 | 45 | g.draw.text('GAME OVER', this.h1, false, 70); 46 | g.ctx.globalAlpha = 0.05; 47 | g.ctx.drawImage(this.skull, 220, 100 + (this.fader * 20)); 48 | g.ctx.globalAlpha = 1; 49 | 50 | if (g.newHi) { 51 | g.ctx.globalAlpha = this.fader; 52 | g.draw.text('NEW HISCORE', this.h2, false, 170); 53 | g.ctx.globalAlpha = 1; 54 | } 55 | 56 | } 57 | 58 | 59 | 60 | 61 | }); 62 | 63 | 64 | -------------------------------------------------------------------------------- /js/engine/state.js: -------------------------------------------------------------------------------- 1 | $.State = Class.extend({ 2 | 3 | init: function(g){ 4 | this.g = g; 5 | this.fader = 0; 6 | 7 | g.jump = 0; 8 | g.lastJump = 0; 9 | g.doJump = false; 10 | 11 | 12 | }, 13 | 14 | 15 | update: function() { 16 | 17 | var g = this.g, 18 | i = g.ents.length, 19 | e; 20 | 21 | g.lastJump = g.jump; 22 | g.jump = ( g.mobile ) ? g.input.touchLeft : g.input.k[32]; 23 | g.doJump = (g.lastJump === 0 & g.jump === 1) ? 24 | true : false; 25 | 26 | while(i--) { 27 | if (g.ents[i] && !g.ents[i].remove) { 28 | g.ents[i].update(); 29 | } 30 | } 31 | 32 | i = g.ents.length; 33 | while (i--) { 34 | if (g.ents[i].remove) { 35 | g.ents.splice(i, 1); 36 | } 37 | } 38 | 39 | 40 | i = g.events.length; 41 | while(i--) { 42 | e = g.events[i]; 43 | if (!e) { 44 | break; 45 | } 46 | e.time -= ( g.dt / 1000 ); 47 | if (e.time < 0) { 48 | e.cb.call(this); 49 | g.events.splice(i, 1); 50 | } 51 | } 52 | 53 | 54 | this.fader = Math.sin(g.tick * 3) + 1; 55 | 56 | }, 57 | 58 | 59 | render: function() { 60 | 61 | var g = this.g, 62 | i, x, y; 63 | 64 | for (i = 0; i < g.ents.length; i += 1) { 65 | // g.ents[i].render(); 66 | if (!g.ents[i].remove) { 67 | g.ents[i].render(); 68 | } 69 | } 70 | 71 | } 72 | 73 | }); 74 | 75 | -------------------------------------------------------------------------------- /js/game/ents/bg2.js: -------------------------------------------------------------------------------- 1 | $.Bg2 = $.Bg.extend({ 2 | 3 | init: function(g, o){ 4 | 5 | this._super(g, o); 6 | 7 | var n = 40, 8 | i = g.imgs; 9 | sc = g.draw.scale; 10 | 11 | this.g = g; 12 | this.c = $.H.mkCanvas(g.w, g.h); 13 | this.ctx = this.c.getContext('2d'); 14 | this.draw = new $.Draw(this.ctx, g.w, g.h); 15 | 16 | this.ground = 40; 17 | this.speed = o.speed; 18 | 19 | this.tiles = []; 20 | 21 | var floor = this.makeFloor(sc(i.ground1, 4)); 22 | 23 | this.tiles.push(new $.Tile(this.g, { 24 | ctx: this.ctx, x: 0, y: 280, i: floor, speed: -o.speed 25 | })); 26 | 27 | this.tiles.push(new $.Tile(this.g, { 28 | ctx: this.ctx, x: 0, y: 0, i: g.draw.flip( floor, 0, 1 ), speed: -o.speed 29 | })); 30 | 31 | this.tiles.push(new $.Tile(this.g, { 32 | ctx: this.ctx, x: 0, y: 230, i: g.draw.scale(g.imgs.bg2, 10), speed: -( o.speed / 1.5 ) 33 | 34 | })); 35 | 36 | 37 | this.tiles.push(new $.Tile(this.g, { 38 | ctx: this.ctx, x: 0, y: 0, i: g.draw.scale(g.imgs.window, 5), speed: -( o.speed / 2 ) 39 | })); 40 | this.tiles.push(new $.Tile(this.g, { 41 | ctx: this.ctx, x: 0, y: 160, i: g.draw.scale(g.imgs.window, 5), speed: -( o.speed / 2 ) 42 | })); 43 | 44 | this.stars = []; 45 | while (n--) { 46 | this.stars.push({ 47 | x: $.H.rnd(0, g.w), 48 | y: $.H.rnd(0, g.h)}); 49 | } 50 | 51 | 52 | 53 | } 54 | 55 | 56 | }); 57 | 58 | -------------------------------------------------------------------------------- /js/game/ents/bullet.js: -------------------------------------------------------------------------------- 1 | $.Bullet = $.Sprite.extend({ 2 | 3 | init: function(g, o){ 4 | 5 | this._super(g, o); 6 | this.w = o.size || 8; 7 | this.h = o.size || 8; 8 | this.name = 'bullet'; 9 | this.speed = 6 / 10; 10 | this.ttl = 3000; 11 | this.group = 'bullets'; 12 | this.scale = 2; 13 | // this.mkImg('circle'); 14 | 15 | 16 | this.vx = this.speed * Math.cos(this.angle); 17 | this.vy = this.speed * Math.sin(this.angle); 18 | 19 | this.col = $.cols.zornskin; 20 | 21 | g.audio.play('shoot'); 22 | 23 | }, 24 | 25 | 26 | update: function() { 27 | 28 | this._super(); 29 | this.hitGroup('baddies'); 30 | if (this.offscreen) { 31 | this.kill(); 32 | } 33 | this.ttl -= this.tick; 34 | if (this.ttl < 0) { 35 | this.remove = true; 36 | } 37 | 38 | }, 39 | 40 | render: function() { 41 | 42 | var g = this.g, 43 | i = g.draw.scale(g.imgs.circle_w, 2); 44 | 45 | this._super(); 46 | 47 | if (this.tick < 0.3) { 48 | g.ctx.drawImage(i, ~~this.x, ~~this.y); 49 | } 50 | 51 | 52 | }, 53 | 54 | 55 | keepOnScreen: function() { 56 | 57 | var g = this.g; 58 | if (this.x < 0) { 59 | this.vx *= -1; 60 | } else if (this.x > g.w - this.w) { 61 | this.vx *= -1; 62 | } 63 | 64 | if (this.y < 0) { 65 | this.vy *= -1; 66 | } else if(this.y > g.h - this.h) { 67 | this.vy *= -1; 68 | } 69 | 70 | } 71 | 72 | 73 | }); 74 | 75 | 76 | -------------------------------------------------------------------------------- /js/engine/tile.js: -------------------------------------------------------------------------------- 1 | $.Tile = Class.extend({ 2 | 3 | init: function(g, o) { 4 | this.g = g; 5 | this.i = o.i; 6 | this.x = o.x; 7 | this.y = o.y; 8 | this.speed = ( o.speed / 1000 ); 9 | this.w = this.i.width; 10 | this.h = this.i.height; 11 | this.ctx = o.ctx; 12 | 13 | this.numTiles = Math.ceil(g.w / this.w) + 2; 14 | 15 | this.tiles = []; 16 | for (i = 0; i < this.numTiles; i += 1) { 17 | this.tiles.push({ 18 | x: i * this.w, 19 | y: this.y 20 | }); 21 | } 22 | }, 23 | 24 | update: function() { 25 | var g = this.g, 26 | i, t, newX; 27 | 28 | 29 | for (i = 0; i < this.numTiles; i +=1 ) { 30 | t = this.tiles[i]; 31 | newX = this.speed * g.dt; 32 | if (t.x < -this.w) { 33 | // t.x = ( ( this.numTiles - 1 ) * this.w ) + (this.speed * 2); 34 | // t.x = ( ( this.numTiles - 1 ) * this.w ) - (newX * 1); 35 | t.x = this.findLastTile() + this.w; 36 | } 37 | t.x += newX; 38 | } 39 | }, 40 | 41 | findLastTile: function() { 42 | var tile, last = 0, i = this.tiles.length; 43 | 44 | while (i--) { 45 | last = ( this.tiles[i].x > last ) ? this.tiles[i].x : last; 46 | } 47 | 48 | return last; 49 | 50 | }, 51 | 52 | render: function() { 53 | 54 | var i, t; 55 | 56 | for (i = 0; i < this.numTiles; i +=1 ) { 57 | t = this.tiles[i]; 58 | this.ctx.drawImage(this.i, ~~t.x, ~~t.y); 59 | } 60 | 61 | } 62 | 63 | }); 64 | 65 | -------------------------------------------------------------------------------- /js/engine/input.js: -------------------------------------------------------------------------------- 1 | $.Input = function() { 2 | 3 | this.init = function(g) { 4 | var s = this, 5 | l = window.addEventListener; 6 | 7 | this.g = g; 8 | 9 | s.k = []; 10 | s.k[32] = 0; 11 | s.k[16] = 0; 12 | s.touching = 0; 13 | s.touchLeft = 0; 14 | s.touchRight = 0; 15 | 16 | 17 | 18 | l('touchstart', function(e) { 19 | s.touching = 1; 20 | s.trackTouch(e.touches); 21 | }); 22 | 23 | l('touchmove', function(e) { 24 | e.preventDefault(); 25 | s.trackTouch(e.touches); 26 | }); 27 | 28 | l('touchend', function(e) { 29 | e.preventDefault(); 30 | s.trackTouch(e.touches); 31 | s.touching = 0; 32 | }); 33 | 34 | 35 | l('keydown', function(e) { 36 | s.k[e.keyCode] = 1; 37 | 38 | }, false); 39 | l('keyup', function(e) { 40 | s.k[e.keyCode] = 0; 41 | }, false); 42 | }; 43 | 44 | this.trackTouch = function(touches) { 45 | 46 | var s = this, 47 | c = this.g.c, 48 | offsetY = c.offsetTop, 49 | offsetX = c.offsetLeft, 50 | scale = parseInt(c.style.width, 10) / c.width, 51 | x, y, i; 52 | 53 | s.touchLeft = 0; 54 | s.touchRight = 0; 55 | 56 | for (i = 0; i < touches.length; i += 1) { 57 | if (i > 1) { break; } 58 | x = ~~((touches[i].pageX - offsetX) / scale); 59 | y = ~~((touches[i].pageY - offsetY) / scale); 60 | if (x < c.width / 2) { 61 | this.touchLeft = 1; 62 | } else { 63 | this.touchRight = 1; 64 | } 65 | } 66 | 67 | }; 68 | 69 | 70 | }; 71 | -------------------------------------------------------------------------------- /js/game/states/tutorial.js: -------------------------------------------------------------------------------- 1 | $.Tutorial = $.State.extend({ 2 | 3 | init: function(g) { 4 | 5 | this._super(g); 6 | 7 | this.h1 = g.mkFont('g', 5); 8 | this.p = g.mkFont('w', 3); 9 | this.p2 = g.mkFont('p', 3); 10 | 11 | this.shootKey = g.mobile ? 'TAP RIGHT' : 'SHIFT KEY'; 12 | this.jumpKey = g.mobile ? 'TAP LEFT' : 'SPACE BAR'; 13 | 14 | this.bg = new $.Bg1(g, {speed: 200}); 15 | g.bg = this.bg; 16 | 17 | this.hideText = false; 18 | 19 | }, 20 | 21 | update: function() { 22 | 23 | var g = this.g; 24 | 25 | this.bg.update(); 26 | this._super(); 27 | 28 | if ( g.doJump && g.tick > 1 || g.tick > 5) { 29 | this.hideText = true; 30 | g.ents.push(new $.Fader(g, { 31 | col: $.cols.nightblue, 32 | cb: function() { 33 | g.changeState('Play'); 34 | } 35 | })); 36 | } 37 | }, 38 | 39 | render: function() { 40 | 41 | var g = this.g; 42 | 43 | this.bg.render(); 44 | this._super(); 45 | 46 | if (!this.hideText) { 47 | g.draw.text('HOW TO PLAY', this.h1, false, 50); 48 | 49 | g.draw.text('SHOOT', this.p, 130, 130); 50 | g.draw.text(this.shootKey, this.p2, 230, 130); 51 | 52 | g.draw.text('JUMP', this.p, 130, 160); 53 | g.draw.text(this.jumpKey, this.p2, 230, 160); 54 | 55 | g.ctx.globalAlpha = this.fader; 56 | g.draw.text('DOUBLE JUMP REVERSES GRAVITY', this.p2, false, 210); 57 | g.ctx.globalAlpha = 1; 58 | } 59 | 60 | } 61 | 62 | 63 | 64 | 65 | }); 66 | 67 | -------------------------------------------------------------------------------- /js/game/states/title.js: -------------------------------------------------------------------------------- 1 | $.Title = $.State.extend({ 2 | 3 | init: function(g) { 4 | 5 | var i; 6 | 7 | this._super(g); 8 | this.startText = (g.mobile) ? 9 | 'TAP LEFT TO START' : 'PRESS SPACE'; 10 | 11 | 12 | this.h1 = g.mkFont('g', 6); 13 | this.p = g.mkFont('w', 2); 14 | 15 | 16 | if (g.plays === 0) { 17 | g.audio.say($.data.title); 18 | } 19 | 20 | this.bg = new $.BgTitle(g, { speed: 500, numStars: 0 }); 21 | 22 | this.hideText = false; 23 | 24 | 25 | }, 26 | 27 | 28 | update: function() { 29 | 30 | var g = this.g; 31 | 32 | this._super(); 33 | 34 | this.bg.update(); 35 | 36 | if ( g.doJump && g.tick > 0.4) { 37 | this.hideText = true; 38 | g.ents.push(new $.Fader(g, { 39 | col: $.cols.black, 40 | cb: function() { 41 | g.changeState(g.plays < 1 ? 'Tutorial' : 'Play'); 42 | } 43 | })); 44 | 45 | } 46 | 47 | 48 | }, 49 | 50 | 51 | render: function() { 52 | 53 | var g = this.g, 54 | s = this, 55 | c = $.cols, n; 56 | 57 | this.bg.render(); 58 | s._super(); 59 | 60 | if (!this.hideText) { 61 | 62 | 63 | g.draw.text('HI', this.p, 40, 40); 64 | g.draw.text(g.hiScore, this.p, 60, 40); 65 | 66 | g.ctx.globalAlpha = this.fader; 67 | g.draw.text(this.startText, this.p, false, 250); 68 | g.ctx.globalAlpha = 1; 69 | 70 | g.draw.text($.data.title, this.h1, 150, 85); 71 | } 72 | 73 | 74 | } 75 | 76 | 77 | 78 | }); 79 | 80 | 81 | -------------------------------------------------------------------------------- /js/game/levels.js: -------------------------------------------------------------------------------- 1 | $.L = [ 2 | { 3 | distance: 10000, 4 | bg: 'Bg1', 5 | bgSettings: {speed: 400}, 6 | baddies: [], 7 | waveSize: 3, 8 | interval: 3, 9 | init: [ 10 | ['Crate', {}], 11 | ['Crate', {y: 40}] 12 | ] 13 | }, 14 | { 15 | distance: 10000, 16 | bg: 'Bg1', 17 | bgSettings: {speed: 400}, 18 | baddies: ['straightBot'], 19 | waveSize: 3, 20 | interval: 3, 21 | init: [ 22 | ['Crate', {}], 23 | ['Crate', {y: 40}] 24 | ] 25 | }, 26 | { 27 | distance: 10000, 28 | bg: 'Bg1', 29 | powerup: 1, 30 | bgSettings: {speed: 400}, 31 | baddies: ['sway', 'straightTop'], 32 | waveSize: 4, 33 | interval: 3, 34 | init: [ 35 | ['Crate', {}] 36 | ] 37 | }, 38 | { 39 | distance: 10000, 40 | bg: 'Bg1', 41 | powerup: 3, 42 | bgSettings: {speed: 400}, 43 | baddies: ['topbot', 'bottop'], 44 | waveSize: 5, 45 | interval: 3, 46 | init: [ 47 | ['Spark', {y: 150}] 48 | ] 49 | }, 50 | { 51 | distance: 20000, 52 | bg: 'Bg2', 53 | powerup: 4, 54 | bgSettings: {speed: 400}, 55 | baddies: ['topbot', 'bottop', 'circle'], 56 | waveSize: 5, 57 | interval: 2, 58 | init: [ 59 | ['Crate', {}], 60 | ['Crate', {y: 40}] 61 | ] 62 | }, 63 | { 64 | distance: 20000, 65 | bg: 'Bg2', 66 | powerup: 3, 67 | bgSettings: {speed: 600}, 68 | baddies: ['all'], 69 | waveSize: 5, 70 | interval: 2, 71 | init: [ 72 | ['Crate', {}], 73 | ['Crate', {y: 40}] 74 | ] 75 | } 76 | ]; 77 | -------------------------------------------------------------------------------- /js/game/ents/spark.js: -------------------------------------------------------------------------------- 1 | $.Spark = $.Sprite.extend({ 2 | init: function(g, o) { 3 | this._super(g, o); 4 | this.scale = 4; 5 | this.health = this.scale * 10; 6 | this.group = 'baddies'; 7 | this.speed = g.bg.speed * -1; 8 | this.frames = 2; 9 | this.mkImg('spark'); 10 | this.iHurt = g.draw.scale(g.imgs.spark_w, this.scale); 11 | this.hurt = 0; 12 | this.hurtTime = 200; 13 | this.frameRate = 100; 14 | 15 | this.x = 500; 16 | // this.y = g.bg.ground + this.h; 17 | this.vx = ( g.bg.speed / 1000 ) * -1; 18 | 19 | }, 20 | 21 | update: function() { 22 | 23 | var g = this.g; 24 | 25 | this._super(); 26 | 27 | if (this.x < -this.w) { 28 | this.x = g.w * 2; 29 | } 30 | 31 | 32 | }, 33 | 34 | render: function() { 35 | 36 | // this._super(); 37 | var g = this.g, 38 | i= this.i; 39 | 40 | if (this.hurt > 0) { 41 | i = this.iHurt; 42 | } 43 | 44 | 45 | g.ctx.drawImage(i, 46 | ( this.frame * this.w ) - this.w, 0, 47 | this.w, this.h, 48 | ~~this.x, ~~this.y, 49 | this.w, this.h 50 | ); 51 | 52 | }, 53 | 54 | 55 | receiveDamage: function() { 56 | var g = this.g; 57 | // this.hurt = this.hurtTime; 58 | g.emitter.particle(2, this.x, this.y); 59 | this.health -= 0; 60 | if (this.health < 0) { 61 | 62 | g.shake.start(this.scale * 5, this.scale * 5); 63 | g.ents.push(new $.Explosion(g, { 64 | x: this.x, y: this.y 65 | })); 66 | this.remove = true; 67 | } 68 | }, 69 | 70 | kill: function() { 71 | return; 72 | } 73 | 74 | 75 | 76 | 77 | }); 78 | 79 | -------------------------------------------------------------------------------- /js/engine/load.js: -------------------------------------------------------------------------------- 1 | $.Load = function(g) { 2 | 3 | this.g = g; 4 | 5 | this.imgsLoaded = 0; 6 | this.imgsTotal = Object.keys($.data.i).length; 7 | 8 | this.init = function() { 9 | 10 | var g = this.g, 11 | append = 'data:image/gif;base64,', 12 | i = $.data.i, n; 13 | 14 | // g.draw.rect(0, 0, g.w, g.h, $.cols.nightblue); 15 | 16 | for (n in i) { 17 | if (i.hasOwnProperty(n)) { 18 | g.imgs[n] = new Image(); 19 | g.imgs[n].onload = this.checkLoaded(); 20 | g.imgs[n].src = append + i[n]; 21 | } 22 | } 23 | }; 24 | 25 | 26 | this.checkLoaded = function() { 27 | 28 | var g = this.g, 29 | s = this, 30 | p; 31 | this.imgsLoaded += 1; 32 | 33 | // p = (s.imgsLoaded / s.imgsTotal) * g.w; 34 | 35 | // g.draw.rect(0, 150, p, 50, $.cols.slimegreen); 36 | 37 | if (s.imgsLoaded === s.imgsTotal) { 38 | window.setTimeout(function() { 39 | s.mkFonts(); 40 | }, 10); 41 | } 42 | 43 | }; 44 | 45 | 46 | 47 | 48 | 49 | this.mkFonts = function() { 50 | var g = this.g, 51 | fonts = { 52 | b: [0,0,0], 53 | w: [255,255,255], 54 | g: [163,206,39], 55 | p: [224,111,139] 56 | }, 57 | i = g.imgs, 58 | n; 59 | 60 | for (n in fonts) { 61 | g.fonts[n] = $.H.resize(g.imgs.font, 1, fonts[n]); 62 | } 63 | 64 | for (n in i) { 65 | i[n + '_w'] = $.H.resize(i[n], 1, [255,255,255]); 66 | } 67 | 68 | window.setTimeout(function() { 69 | g.init(); 70 | }, 10); 71 | 72 | }; 73 | 74 | this.init(); 75 | 76 | }; 77 | -------------------------------------------------------------------------------- /js/lib/stats.js: -------------------------------------------------------------------------------- 1 | // stats.js - http://github.com/mrdoob/stats.js 2 | var Stats=function(){function f(a,e,b){a=document.createElement(a);a.id=e;a.style.cssText=b;return a}function l(a,e,b){var c=f("div",a,"padding:0 0 3px 3px;text-align:left;background:"+b),d=f("div",a+"Text","font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px;color:"+e);d.innerHTML=a.toUpperCase();c.appendChild(d);a=f("div",a+"Graph","width:74px;height:30px;background:"+e);c.appendChild(a);for(e=0;74>e;e++)a.appendChild(f("span","","width:1px;height:30px;float:left;opacity:0.9;background:"+ 3 | b));return c}function m(a){for(var b=c.children,d=0;dr+1E3&&(d=Math.round(1E3* 5 | t/(a-r)),u=Math.min(u,d),v=Math.max(v,d),A.textContent=d+" FPS ("+u+"-"+v+")",p(B,d/100),r=a,t=0,void 0!==h)){var b=performance.memory.usedJSHeapSize,c=performance.memory.jsHeapSizeLimit;h=Math.round(9.54E-7*b);y=Math.min(y,h);z=Math.max(z,h);E.textContent=h+" MB ("+y+"-"+z+")";p(F,b/c)}return a},update:function(){k=this.end()}}};"object"===typeof module&&(module.exports=Stats); 6 | -------------------------------------------------------------------------------- /js/game/ents/crate.js: -------------------------------------------------------------------------------- 1 | $.Crate = $.Sprite.extend({ 2 | init: function(g, o) { 3 | this._super(g, o); 4 | this.scale = 4; 5 | this.health = this.scale * 10; 6 | this.group = 'baddies'; 7 | this.speed = g.bg.speed * -1; 8 | this.frames = 1; 9 | this.mkImg('crate'); 10 | this.iHurt = g.draw.scale(g.imgs.crate_w, this.scale); 11 | this.frameRate = 120; 12 | this.hurt = 0; 13 | this.hurtTime = 200; 14 | 15 | this.x = 500; 16 | this.y = o.y || ( g.h - g.bg.ground ) - this.h; 17 | // this.vx = ( $.H.rnd(0, 200) - 100 ) / 1000; 18 | // this.vx = ( ( $.H.rnd(0, 200) ) / 1000 ) * -1; 19 | this.vx = ( g.bg.speed / 1000 ) * -1; 20 | 21 | }, 22 | 23 | update: function() { 24 | 25 | var g = this.g; 26 | this.lastX = this.x; 27 | this.lastY = this.y; 28 | 29 | 30 | if (g.bg.speed !== 0) { 31 | this.x += this.vx * g.dt; 32 | } 33 | 34 | if (this.x < -this.w) { 35 | this.x = g.w * 2 + $.H.rnd(0, g.w); 36 | } 37 | 38 | this.frameNext -= g.dt; 39 | this.hurt -= g.dt; 40 | 41 | }, 42 | 43 | render: function() { 44 | 45 | // this._super(); 46 | var g = this.g, 47 | i= this.i; 48 | 49 | if (this.hurt > 0) { 50 | i = this.iHurt; 51 | } 52 | 53 | 54 | g.ctx.drawImage(i, 55 | ( this.frame * this.w ) - this.w, 0, 56 | this.w, this.h, 57 | ~~this.x, ~~this.y, 58 | this.w, this.h 59 | ); 60 | 61 | }, 62 | 63 | 64 | receiveDamage: function() { 65 | var g = this.g; 66 | this.hurt = this.hurtTime; 67 | g.emitter.particle(2, this.x, this.y); 68 | this.health -= 0; 69 | if (this.health < 0) { 70 | 71 | g.shake.start(this.scale * 5, this.scale * 5); 72 | g.ents.push(new $.Explosion(g, { 73 | x: this.x, y: this.y 74 | })); 75 | this.remove = true; 76 | } 77 | }, 78 | 79 | kill: function() { 80 | return; 81 | } 82 | 83 | 84 | 85 | 86 | }); 87 | 88 | -------------------------------------------------------------------------------- /js/game/ents/drone.js: -------------------------------------------------------------------------------- 1 | $.Drone = $.Sprite.extend({ 2 | init: function(g, o) { 3 | this._super(g, o); 4 | this.scale = 4; 5 | this.health = 5; 6 | this.group = 'baddies'; 7 | this.speed = ( g.bg.speed / 4 )* -1; 8 | this.frames = 2; 9 | 10 | this.t = 0; 11 | 12 | this.m = $.data.moves[o.m]; 13 | 14 | this.scale = this.m.scale || 4; 15 | this.flipped = this.m.flipped || 0; 16 | this.mkImg(this.m.img || 'drone'); 17 | 18 | this.x = o.x || this.m.sx; 19 | this.y = o.y || this.m.sy; 20 | 21 | this.t = 0; 22 | 23 | }, 24 | 25 | update: function() { 26 | 27 | var g = this.g, 28 | m = this.m; 29 | 30 | this.t += ( g.dt / 1000 ); 31 | 32 | 33 | this.lastX = this.x; 34 | this.lastY = this.y; 35 | 36 | this.vx = m.A + m.B * Math.sin(m.C * this.t + m.D); 37 | this.vy = m.E + m.F * Math.sin(m.G * this.t + m.H); 38 | 39 | this.x += this.vx * ( g.dt / 1000 ); 40 | this.y += this.vy * ( g.dt / 1000 ); 41 | 42 | if (this.x < -50) { 43 | this.remove = true; 44 | } 45 | 46 | if (this.frameNext < 0) { 47 | this.frameNext = this.frameRate; 48 | this.frame = (this.frame === this.frames) ? 1 : this.frame += 1; 49 | } 50 | 51 | this.frameNext -= g.dt; 52 | this.hurt -= g.dt; 53 | 54 | }, 55 | 56 | 57 | receiveDamage: function() { 58 | var g = this.g; 59 | this.hurt = this.hurtTime; 60 | g.emitter.particle(2, this.x, this.y); 61 | this.health -= 10; 62 | if (this.health < 0) { 63 | 64 | this.remove = true; 65 | g.score += this.scale * 10; 66 | g.ents.push(new $.Explosion(g, { 67 | x: this.x, y: this.y 68 | })); 69 | g.ents.push(new $.Msg(g, { 70 | text: this.scale * 10, 71 | x: this.x, y: this.y 72 | })); 73 | g.waves[this.waveId] -= 1; 74 | if (!g.ios) { 75 | 76 | g.shake.start(this.scale * 5, this.scale * 5); 77 | } 78 | 79 | if (g.waves[this.waveId] === 0) { 80 | g.score += this.scale * 30; 81 | g.ents.push(new $.Star(g, { 82 | val: this.scale * 30, 83 | x: this.x, y: this.y 84 | })); 85 | 86 | 87 | } 88 | 89 | } 90 | }, 91 | 92 | 93 | kill: function() { 94 | return; 95 | } 96 | 97 | 98 | 99 | 100 | }); 101 | 102 | 103 | -------------------------------------------------------------------------------- /dev.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Loading 6 | 7 | 8 | 9 | 10 | 11 | 12 |

Loading

13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /js/engine/inherit.js: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // Class object based on John Resigs code; inspired by base2 and Prototype 3 | // http://ejohn.org/blog/simple-javascript-inheritance/ 4 | 5 | var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/; 6 | var lastClassId = 0; 7 | 8 | Class = function(){}; 9 | var inject = function(prop) { 10 | var proto = this.prototype; 11 | var _super = {}; 12 | for( var name in prop ) { 13 | if( 14 | typeof(prop[name]) == "function" && 15 | typeof(proto[name]) == "function" && 16 | fnTest.test(prop[name]) 17 | ) { 18 | _super[name] = proto[name]; // save original function 19 | proto[name] = (function(name, fn){ 20 | return function() { 21 | var tmp = this._super; 22 | this._super = _super[name]; 23 | var ret = fn.apply(this, arguments); 24 | this._super = tmp; 25 | return ret; 26 | }; 27 | })( name, prop[name] ); 28 | } 29 | else { 30 | proto[name] = prop[name]; 31 | } 32 | } 33 | }; 34 | 35 | Class.extend = function(prop) { 36 | var _super = this.prototype; 37 | 38 | initializing = true; 39 | var prototype = new this(); 40 | initializing = false; 41 | 42 | for( var name in prop ) { 43 | if( 44 | typeof(prop[name]) == "function" && 45 | typeof(_super[name]) == "function" && 46 | fnTest.test(prop[name]) 47 | ) { 48 | prototype[name] = (function(name, fn){ 49 | return function() { 50 | var tmp = this._super; 51 | this._super = _super[name]; 52 | var ret = fn.apply(this, arguments); 53 | this._super = tmp; 54 | return ret; 55 | }; 56 | })( name, prop[name] ); 57 | } 58 | else { 59 | prototype[name] = prop[name]; 60 | } 61 | } 62 | 63 | function Class() { 64 | if( !initializing ) { 65 | 66 | // If this class has a staticInstantiate method, invoke it 67 | // and check if we got something back. If not, the normal 68 | // constructor (init) is called. 69 | if( this.staticInstantiate ) { 70 | var obj = this.staticInstantiate.apply(this, arguments); 71 | if( obj ) { 72 | return obj; 73 | } 74 | } 75 | for( var p in this ) { 76 | if( typeof(this[p]) == 'object' ) { 77 | this[p] = ig.copy(this[p]); // deep copy! 78 | } 79 | } 80 | if( this.init ) { 81 | this.init.apply(this, arguments); 82 | } 83 | } 84 | return this; 85 | } 86 | 87 | Class.prototype = prototype; 88 | Class.prototype.constructor = Class; 89 | Class.extend = window.Class.extend; 90 | Class.inject = inject; 91 | Class.classId = prototype.classId = ++lastClassId; 92 | 93 | return Class; 94 | }; 95 | -------------------------------------------------------------------------------- /js/lib/jsfxr.min.js: -------------------------------------------------------------------------------- 1 | 2 | function SfxrParams(){this.setSettings=function(e){for(var f=0;24>f;f++)this[String.fromCharCode(97+f)]=e[f]||0;.01>this.c&&(this.c=.01);e=this.b+this.c+this.e;.18>e&&(e=.18/e,this.b*=e,this.c*=e,this.e*=e)}} 3 | function SfxrSynth(){this._params=new SfxrParams;var e,f,d,h,l,A,K,L,M,B,m,N;this.reset=function(){var b=this._params;h=100/(b.f*b.f+.001);l=100/(b.g*b.g+.001);A=1-b.h*b.h*b.h*.01;K=-b.i*b.i*b.i*1E-6;b.a||(m=.5-b.n/2,N=5E-5*-b.o);L=1+b.l*b.l*(0a.q?-1020:1020),S=a.p?((1-a.p)*(1-a.p)*2E4|0)+32:0,ba=a.d,T=a.j/2,ca=a.k*a.k*.01,F=a.a,G=e,da=1/e,ea=1/f,fa=1/d,a=5/(1+a.u*a.u*20)*(.01+n);.8=S&&(V=0,this.reset());B&&++M>=B&&(B=0,h*= 5 | L);A+=K;h*=A;h>l&&(h=l,0g&&(g=8);F||(m+=N,0>m?m=0:.5G)switch(w=0,++U){case 1:G=f;break;case 2:G=d}switch(U){case 0:x=w*da;break;case 1:x=1+2*(1-w*ea)*ba;break;case 2:x=1-w*fa;break;case 3:x=0,H=!0}R&&(E+=aa,t=E|0,0>t?t=-t:1023r?r=1E-5:.1=g&&(p%=g,3==F))for(y=z.length;y--;)z[y]=2*Math.random()-1;switch(F){case 0:c=p/gc?1:-1);c=.225*((0>c?-1:1)*c*c-c)+c;break;case 3:c=z[Math.abs(32*p/g|0)]}P&&(y=v,n*=W,0>n?n=0:.1=q?-32768:32767*q|0}return O}}var synth=new SfxrSynth; 7 | window.jsfxr=function(e){synth._params.setSettings(e);var f=synth.totalReset();e=new Uint8Array(4*((f+1)/2|0)+44);var f=2*synth.synthWave(new Uint16Array(e.buffer,44),f),d=new Uint32Array(e.buffer,0,44);d[0]=1179011410;d[1]=f+36;d[2]=1163280727;d[3]=544501094;d[4]=16;d[5]=65537;d[6]=44100;d[7]=88200;d[8]=1048578;d[9]=1635017060;d[10]=f;for(var f=f+44,d=0,h="data:audio/wav;base64,";d>18]+ 8 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[l>>12&63]+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[l>>6&63]+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[l&63]);return h}; 9 | -------------------------------------------------------------------------------- /js/game/ents/floater.js: -------------------------------------------------------------------------------- 1 | $.Floater = $.Sprite.extend({ 2 | init: function(g, o) { 3 | this._super(g, o); 4 | this.scale = $.H.rnd(2,5); 5 | this.health = this.scale * 10; 6 | this.group = 'baddies'; 7 | this.speed = 1; 8 | this.frames = 2; 9 | this.frameChange = 10; 10 | this.mkImg('floater'); 11 | this.frameRate = 120; 12 | 13 | this.x = $.H.rnd(( g.w / 2 ) - ( this.w / 2 )); 14 | this.y = $.H.rnd(( g.h / 2 ) - ( this.h / 2 )); 15 | 16 | this.setDir(); 17 | }, 18 | 19 | update: function() { 20 | // this._super(); 21 | 22 | var g = this.g; 23 | this.lastX = this.x; 24 | this.lastY = this.y; 25 | 26 | if (this.gravity) { 27 | this.vy += this.gravity; 28 | } 29 | 30 | this.x += this.vx * g.dt; 31 | this.y += this.vy * g.dt; 32 | 33 | this.atEdget = this.keepOnScreen(); 34 | if (this.atEdget) { 35 | this.setDir(); 36 | } 37 | 38 | this.cx = this.x + (this.w / 2); 39 | this.cy = this.y + (this.y / 2); 40 | 41 | this.tick += 1; 42 | 43 | if (this.frameNext < 0) { 44 | this.frameNext = this.frameRate; 45 | this.frame = (this.frame === this.frames) ? 1 : this.frame += 1; 46 | } 47 | 48 | this.frameNext -= g.dt; 49 | this.hurt -= g.dt; 50 | 51 | }, 52 | 53 | render: function() { 54 | 55 | // this._super(); 56 | var g = this.g, 57 | i= this.i; 58 | 59 | if (this.hurt > 0) { 60 | i = this.iHurt; 61 | } 62 | 63 | 64 | if (this.flipX) { 65 | i = g.draw.flip(i, 1, 0); 66 | } 67 | 68 | 69 | g.ctx.drawImage(i, 70 | ( this.frame * this.w ) - this.w, 0, 71 | this.w, this.h, 72 | ~~this.x, ~~this.y, 73 | this.w, this.h 74 | ); 75 | 76 | }, 77 | 78 | 79 | receiveDamage: function() { 80 | var g = this.g; 81 | this.hurt = this.hurtTime; 82 | g.emitter.particle(2, this.x, this.y); 83 | this.health -= 10; 84 | if (this.health < 0) { 85 | 86 | g.ents.push(new $.Msg(g, { 87 | text: this.scale * 10, 88 | x: this.x, y: this.y 89 | })); 90 | 91 | if (!g.ios) { 92 | g.shake.start(this.scale * 5, this.scale * 5); 93 | g.ents.push(new $.Explosion(g, { 94 | x: this.x, y: this.y 95 | })); 96 | } 97 | this.remove = true; 98 | } 99 | }, 100 | 101 | kill: function() { 102 | return; 103 | }, 104 | 105 | setDir: function() { 106 | this.vx = ( $.H.rnd(0, 200) - 100 ) / 1000; 107 | this.vy = ( $.H.rnd(0, 200) - 100 ) / 1000; 108 | this.flipX = this.vx > 0 ? true : false; 109 | } 110 | 111 | 112 | 113 | }); 114 | 115 | -------------------------------------------------------------------------------- /js/game/ents/bg.js: -------------------------------------------------------------------------------- 1 | $.Bg = Class.extend({ 2 | 3 | init: function(g, o){ 4 | 5 | o = o || {}; 6 | var numStars = o.numStars || 20, 7 | i = g.imgs; 8 | sc = g.draw.scale; 9 | 10 | this.g = g; 11 | this.c = $.H.mkCanvas(g.w, g.h); 12 | this.ctx = this.c.getContext('2d'); 13 | this.draw = new $.Draw(this.ctx, g.w, g.h); 14 | 15 | this.ground = o.ground || 40; 16 | this.groundTileImg = o.groundTile || i.ground; 17 | this.groundTileScale = o.groundTile || i.ground; 18 | this.speed = o.speed; 19 | 20 | this.tiles = []; 21 | 22 | // var floor = this.makeFloor(sc(this.groundTileImg, this.groundTileScale)); 23 | var floor = sc(i.ground, 4); 24 | 25 | this.tiles.push(new $.Tile(this.g, { 26 | ctx: this.ctx, x: 0, y: 280, i: floor, speed: -o.speed 27 | })); 28 | 29 | this.tiles.push(new $.Tile(this.g, { 30 | ctx: this.ctx, x: 0, y: 0, i: g.draw.flip( floor, 0, 1 ), speed: -o.speed 31 | })); 32 | 33 | this.tiles.push(new $.Tile(this.g, { 34 | ctx: this.ctx, x: 0, y: 230, i: g.draw.scale(g.imgs.bg1, 10), speed: -( o.speed / 1.5 ) 35 | 36 | })); 37 | 38 | this.tiles.push(new $.Tile(this.g, { 39 | ctx: this.ctx, x: 0, y: 41, i: g.draw.scale(g.imgs.window, 8), speed: -( o.speed / 2 ) 40 | 41 | })); 42 | 43 | this.stars = []; 44 | while (numStars--) { 45 | this.stars.push({ 46 | x: $.H.rnd(0, g.w), 47 | y: $.H.rnd(0, g.h)}); 48 | } 49 | 50 | 51 | 52 | }, 53 | 54 | 55 | update: function() { 56 | 57 | var g = this.g, 58 | i = this.tiles.length; 59 | 60 | while (i--) { 61 | this.tiles[i].update(); 62 | } 63 | 64 | 65 | }, 66 | 67 | render: function() { 68 | 69 | var g = this.g, 70 | i = this.stars.length, s; 71 | 72 | this.ctx.fillStyle = $.cols.black; 73 | this.ctx.fillRect(0, 0, g.w, g.h); 74 | 75 | 76 | while (i--) { 77 | s = this.stars[i]; 78 | this.draw.circle(s.x, s.y, 1, $.cols.blind); 79 | } 80 | 81 | i = this.tiles.length; 82 | while (i--) { 83 | this.tiles[i].render(); 84 | } 85 | 86 | g.ctx.drawImage(this.c, 0, 0); 87 | 88 | }, 89 | 90 | stop: function() { 91 | var t = this.tiles.length; 92 | 93 | this.speed = 0; 94 | while(t--) { 95 | this.tiles[t].speed = 0; 96 | } 97 | }, 98 | 99 | makeFloor: function(i) { 100 | 101 | var parts = 10, 102 | c = $.H.mkCanvas(i.width * 10, i.height), 103 | ctx = c.getContext('2d'), 104 | n; 105 | 106 | for (n = 0; n < parts; n += 1) { 107 | ctx.drawImage(i, i.width * n, 0); 108 | } 109 | 110 | return c; 111 | } 112 | 113 | }); 114 | 115 | 116 | -------------------------------------------------------------------------------- /js/game/ents/robo.js: -------------------------------------------------------------------------------- 1 | $.Robo = $.Sprite.extend({ 2 | 3 | init: function(g, o) { 4 | 5 | this._super(g,o); 6 | 7 | this.speed = 1; 8 | this.group = 'player'; 9 | 10 | this.frames = 2; 11 | this.scale = 4; 12 | this.mkImg('p1'); 13 | this.angle = 0; 14 | 15 | this.speed = 5; 16 | this.vy = this.speed; 17 | 18 | this.gravity = 0.001; 19 | this.jump = -0.4; 20 | this.jumping = false; 21 | this.flipping = false; 22 | this.flipped = false; 23 | this.jumpCount = 0; 24 | 25 | this.jumps = 0; 26 | this.flips = 0; 27 | this.pause = false; 28 | 29 | this.powerup = 0; 30 | 31 | this.gun = 1; 32 | 33 | }, 34 | 35 | update: function() { 36 | 37 | var g = this.g, 38 | k = g.input.k; 39 | 40 | if (this.pause) { 41 | return; 42 | } 43 | 44 | this.hitGroup('baddies'); 45 | 46 | if (g.doJump && this.jumpCount < 2 && this.tick > 2) { 47 | 48 | this.jumpCount += 1; 49 | 50 | if (!this.jumping) { 51 | g.audio.play('jump'); 52 | this.jumps += 1; 53 | this.jumping = true; 54 | this.vy = this.jump; 55 | 56 | } else if (this.jumping) { 57 | g.audio.play('flip'); 58 | this.flips += 1; 59 | this.flipping = true; 60 | this.gravity *= -1; 61 | this.jump *= -1; 62 | this.flipped = !this.flipped; 63 | } 64 | 65 | } 66 | 67 | this._super(); 68 | 69 | this.keepOnScreen(); 70 | 71 | if (this.x > 450) { 72 | this.frame = 1; 73 | } 74 | 75 | }, 76 | 77 | 78 | render: function() { 79 | 80 | if (this.pause) { 81 | return; 82 | } 83 | this._super(); 84 | }, 85 | 86 | doDamage: function(o) { 87 | if (o.group === 'baddies') { 88 | this.kill(); 89 | } 90 | }, 91 | 92 | 93 | receiveDamage: function(o) { 94 | if (o.group === 'baddies') { 95 | this.kill(); 96 | } 97 | }, 98 | 99 | kill: function() { 100 | 101 | var g = this.g, 102 | self = this, 103 | r = $.H.rnd, 104 | num = 3; 105 | 106 | this.dead = true; 107 | this.remove = true; 108 | g.bg.stop(); 109 | 110 | g.ents.push(new $.Explosion(g, { 111 | x: this.x, y: this.y 112 | })); 113 | g.shake.start(50, 50); 114 | 115 | }, 116 | 117 | doPowerup: function() { 118 | 119 | var g = this.g; 120 | 121 | this.powerup += 1; 122 | 123 | if (this.powerup > 3) { 124 | g.score += 100; 125 | return 100; 126 | } 127 | 128 | if (this.powerup === 1) { 129 | g.bulletInterval = 100; 130 | return 'RAPID FIRE'; 131 | } else if (this.powerup === 2) { 132 | return 'DOUBLE SHOT'; 133 | } else if (this.powerup === 3) { 134 | return 'TRIPLE SHOT'; 135 | } 136 | 137 | } 138 | 139 | 140 | 141 | 142 | }); 143 | -------------------------------------------------------------------------------- /js/engine/draw.js: -------------------------------------------------------------------------------- 1 | $.Draw = function(ctx, w, h) { 2 | 3 | this.ctx = ctx; 4 | this.w = w; 5 | this.h = h; 6 | 7 | this.clear = function() { 8 | 9 | this.ctx.clearRect(0, 0, this.w, this.h); 10 | 11 | }; 12 | 13 | 14 | this.rect = function(x, y, w, h, col) { 15 | 16 | this.ctx.fillStyle = col; 17 | this.ctx.fillRect(~~x, ~~y, w, h); 18 | 19 | }; 20 | 21 | 22 | this.circle = function(x, y, r, col) { 23 | 24 | var ctx = this.ctx; 25 | 26 | x = x + (r / 2); 27 | y = y + (r / 2); 28 | 29 | ctx.beginPath(); 30 | ctx.arc(x, y, r, 0, Math.PI*2, true); 31 | ctx.closePath(); 32 | ctx.fillStyle = col; 33 | ctx.fill(); 34 | 35 | }; 36 | 37 | 38 | this.rotate = function(i, a) { 39 | var c = document.createElement('canvas'), 40 | ctx = c.getContext('2d'), 41 | size = Math.max(i.width, i.height) + 6, 42 | deg = a * (180 / Math.PI); 43 | 44 | c.width = size; 45 | c.height = size; 46 | 47 | ctx.translate(size/2, size/2); 48 | ctx.rotate(a + Math.PI/2); 49 | ctx.drawImage(i, -(i.width/2), -(i.height/2)); 50 | 51 | return c; 52 | 53 | }; 54 | 55 | 56 | this.scale = function(i, scale, n) { 57 | 58 | var c = $.H.mkCanvas(i.width * scale, i.height * scale), 59 | ctx = c.getContext('2d'); 60 | 61 | if (c.width) { 62 | ctx.save(); 63 | ctx.scale(scale, scale); 64 | ctx.drawImage(i, 0, 0); 65 | ctx.restore(); 66 | } 67 | 68 | 69 | return c; 70 | }; 71 | 72 | 73 | this.flip = function(i, flipH, flipV) { 74 | 75 | var c = $.H.mkCanvas(i.width, i.height), 76 | ctx = c.getContext('2d'), 77 | scaleH = flipH ? -1 : 1, 78 | scaleV = flipV ? -1 : 1, 79 | posX = flipH ? i.width * -1 : 0, 80 | posY = flipV ? i.height * -1 : 0; 81 | 82 | c.width = i.width; 83 | c.height = i.height; 84 | 85 | ctx.save(); 86 | ctx.scale(scaleH, scaleV); 87 | ctx.drawImage(i, posX, posY, i.width, i.height); 88 | ctx.restore(); 89 | 90 | return c; 91 | 92 | }; 93 | 94 | 95 | this.text = function(s,f,x,y) { 96 | 97 | var i = 0, 98 | ctx = this.ctx, 99 | firstChar = 65, 100 | offset = 0, 101 | w = 3 * f.scale, 102 | h = 5 * f.scale, 103 | spacing = 1 * f.scale, 104 | sW = $.H.textWidth(s, f), 105 | charPos = 0; 106 | 107 | if (typeof(s) === 'number' || s[0] === '0') { 108 | s += ''; 109 | offset = 43; 110 | } 111 | 112 | x = x || (this.w - sW) / 2; 113 | 114 | 115 | for (i = 0; i < s.length; i += 1) { 116 | charPos = ( ( s.charCodeAt(i) - firstChar ) + offset ) * (w + spacing); 117 | if (charPos > -1) { 118 | ctx.drawImage(f, 119 | charPos, 0, 120 | w, h, 121 | ~~x, ~~y, 122 | w, h); 123 | } 124 | x += w + spacing; 125 | } 126 | }; 127 | }; 128 | 129 | -------------------------------------------------------------------------------- /js/game/data.js: -------------------------------------------------------------------------------- 1 | $.data = { 2 | 3 | title: 'ROBO FLIP', 4 | 5 | i: { 6 | 7 | circle: 'R0lGODlhBgAGAKEDAL4mM+uJMffia////yH5BAEKAAMALAAAAAAGAAYAAAIN3AB2EQkhRHuxLWuQKQA7', 8 | 9 | floater: 'R0lGODlhDgAIAKEDAL4mMzGi8vfia////yH+EUNyZWF0ZWQgd2l0aCBHSU1QACH5BAEKAAMALAAAAAAOAAgAAAIdXIZnuOEPIQBxVirxE4MLJ4ShwyRHhHiGUppPyxQAOw==', 10 | 11 | spark: 'R0lGODlhDgAHAKECAOuJMffia////////yH+EUNyZWF0ZWQgd2l0aCBHSU1QACH5BAEKAAMALAAAAAAOAAcAAAIYxGZ4u+acWohoHgCCmE7b+4HRQ43XyRwFADs=', 12 | 13 | p1: 'R0lGODlhDAAIAMIFAAUDC74mMzWADJrKH/3//P///////////yH5BAEKAAcALAAAAAAMAAgAAAMfeLonMkM5KF9sVgUS9tkdgVFj5GRlRU4RsLiHCzNKAgA7', 14 | 15 | crawler: 'R0lGODlhEAAIAKECAL4mM/fia////////yH5BAEKAAIALAAAAAAQAAgAAAIZlI8Skba4WIoIAHnsc/TE/WHhFJYmZSpjAQA7', 16 | 17 | drone: 'R0lGODlhEAAIAKEDAL4mM+Bvi/fia////yH5BAEKAAMALAAAAAAQAAgAAAIenBOmu4j8VBAuHSernbXhoSXASJYmMJyGurYKmhoFADs=', 18 | 19 | star: 'R0lGODlhBQAFAIABAPfia////yH5BAEKAAEALAAAAAAFAAUAAAIITGCGB43OWAEAOw==', 20 | 21 | battery: 'R0lGODlhAwAFAKECAESJGqPOJ////////yH5BAEKAAIALAAAAAADAAUAAAIGFAwBuZoFADs=', 22 | 23 | font: 'R0lGODlhjwAFAIABAAAAAMwAACH5BAEKAAEALAAAAACPAAUAAAJ0DGKHcLzOFDRJ0UbXzdJ2lFQbRo5ipJ1TA7XsW2KanNWyZXpuzuNSz5txQDZTChSrsI6kHQpVu/wer9GvWuw5ssMp1LmbuZKeDdN4NVqT1BAydWvHi14ityTUSZHLE3El0uWHN/Vg9WYoOPe01YEl9VgVUQAAOw==', 24 | 25 | window: 'R0lGODlhIAAiAKECABsmMi9ITv///////yH5BAEKAAMALAAAAAAgACIAAAJZDI6py2gNo5O0PTCy3rzviXnimB0GiXpmmLbD6rpwnM40ad9irnd8/wGcgCohixgcIpPH5cvoZEY1P2SVeAVme1td9/alhWNjGXT6VEZXTSy7An/HK5c5pQAAOw==', 26 | 27 | ground: 'R0lGODlhCgAKAKECAABXhDGi8v///////yH5BAEKAAIALAAAAAAKAAoAAAIQjI+gyxztoIRvWlBlVqiHAgA7', 28 | 29 | ground1: 'R0lGODlhCgAKAKEBABsmMi9ITi9ITi9ITiH5BAEKAAIALAAAAAAKAAoAAAIPhI8RoMoNo5y02vucQ7wAADs=', 30 | 31 | bg1: 'R0lGODlhFAAFAIABAC9ITv///yH5BAEKAAEALAAAAAAUAAUAAAIRjI+pG+CMXnNSPTpxzZzeWgAAOw==', 32 | 33 | bg2: 'R0lGODlhFAAFAIAAAC9ITi9ITiH5BAEKAAEALAAAAAAUAAUAAAIPjI+pBr0fmoRpTnpAxqYAADs=', 34 | 35 | skull: 'R0lGODlhCAAIAIABAAAAAP///yH5BAEKAAEALAAAAAAIAAgAAAIOTIBoyc27nDuKJnMyMAUAOw==', 36 | 37 | crate: 'R0lGODlhCgAKAKECAC9ITp2dnf///////yH5BAEKAAMALAAAAAAKAAoAAAIZXI5nAd0JEkMRiTotxuHSLoSc80hmsJhDAQA7' 38 | 39 | 40 | }, 41 | 42 | 43 | 44 | sfx: { 45 | jump: [0,,0.2432,,0.1709,0.3046,,0.1919,,,,,,0.5923,,,,,1,,,,,0.5], 46 | flip: [0,0.001,0.2379,0.1592,0.0225,0.85,,0.0659,0.0917,,-0.6595,-0.2759,0.7809,0.0597,0.0205,0.3604,-0.0083,-0.5261,0.3385,-0.0003,0.0833,,0.6489,0.5], 47 | powerup: [0,,0.0129,0.5211,0.4714,0.4234,,,,,,0.4355,0.5108,,,,,,1,,,,,0.5], 48 | shoot: [2,,0.1128,,0.178,0.7748,0.0046,-0.4528,,,,,,0.185,0.0994,,,,1,,,,,0.5], 49 | explode: [3,,0.3708,0.5822,0.3851,0.0584,,-0.0268,,,,-0.0749,0.7624,,,,,,1,,,,,0.5], 50 | levelup: [1,0.115,0.2886,0.4061,0.6535,0.0666,,0.3295,0.0262,-0.0114,,0.2484,0.4319,0.7129,-0.7396,,,-0.906,0.9658,0.1462,0.6577,0.0129,0.0448,0.56], 51 | die: [2,0.1987,0.388,0.4366,0.0335,0.5072,,0.1128,-0.1656,0.1987,,-0.376,0.2686,-0.684,0.1392,-0.6819,-0.8117,-0.1072,0.9846,0.057,,0.004,-0.0045,0.56], 52 | alarm: [1,0.0241,0.9846,0.6067,0.3041,0.1838,,0.0565,0.1439,-0.3068,0.1402,0.0867,0.7339,0.1332,-0.3119,-0.3257,0.2875,-0.0014,0.5866,0.0086,-0.9675,0.3643,,0.5] 53 | }, 54 | 55 | moves: { 56 | straightTop: { 57 | sx: 500, sy: 42, scale: 4, img: 'crawler', flipped: 1, 58 | A: -200, B: 0, C: 0, D: 0, E: 0, F: 0, G: 0, H: 0 59 | }, 60 | straightBot: { 61 | sx: 500, sy: 247, scale: 4, img: 'crawler', 62 | A: -200, B: 0, C: 0, D: 0, E: 0, F: 0, G: 0, H: 0 63 | }, 64 | topbot: { 65 | sx: 500, sy: 50, scale: 3, img: 'floater', 66 | A: -200, B: 0, C: 0, D: 0, E: 80, F: 0, G: 0, H: 0 67 | }, 68 | bottop: { 69 | sx: 500, sy: 230, scale: 3, img: 'floater', 70 | A: -200, B: 0, C: 0, D: 0, E: -80, F: 0, G: 0, H: 0 71 | }, 72 | sway: { 73 | sx: 500, sy: 60, scale: 4, img: 'drone', 74 | A: -100, B: 0, C: 0, D: 0, E: 0, F: 360, G: 4, H: 0 75 | }, 76 | circle: { 77 | sx: 500, sy: 160, scale: 5, img: 'drone', 78 | A: -150, B: -100, C: 5, D: 0, E: 50, F: 200, G: 10, H: Math.PI/2 79 | } 80 | } 81 | 82 | }; 83 | 84 | 85 | window.addEventListener('load', function() { 86 | var g = new $.Game(); 87 | g.boot('Splash'); 88 | 89 | }, false); 90 | 91 | -------------------------------------------------------------------------------- /js/engine/sprite.js: -------------------------------------------------------------------------------- 1 | $.Sprite = Class.extend({ 2 | 3 | init: function(g, o){ 4 | 5 | var n; 6 | this.g = g; 7 | this.angle = 0; 8 | this.id = Date.now(); 9 | this.offscreen = false; 10 | this.remove = false; 11 | this.dead = false; 12 | this.tick = 0; 13 | this.vx = 0; 14 | this.vy = 0; 15 | this.scale = 1; 16 | this.alpha = 1; 17 | this.col = $.cols.slimegreen; 18 | this.frames = 1; 19 | this.frame = 1; 20 | this.gravity = 0; 21 | this.frameRate = 80; 22 | this.frameNext = 0; 23 | this.hurt = 0; 24 | this.hurtTime = 200; 25 | 26 | for (n in o) { 27 | this[n] = o[n]; 28 | } 29 | 30 | }, 31 | 32 | 33 | update: function() { 34 | 35 | var g = this.g; 36 | 37 | this.tick += ( g.dt / 100 ); 38 | 39 | this.lastX = this.x; 40 | this.lastY = this.y; 41 | 42 | this.x += this.vx * g.dt; 43 | this.y += this.vy * g.dt; 44 | 45 | if (this.gravity) { 46 | this.vy += this.gravity * g.dt; 47 | } 48 | 49 | this.offscreen = this.checkOffScreen(); 50 | 51 | this.cx = this.x + (this.w / 2); 52 | this.cy = this.y + (this.y / 2); 53 | 54 | if (this.frameNext < 0) { 55 | this.frameNext = this.frameRate; 56 | this.frame = (this.frame === this.frames) ? 1 : this.frame += 1; 57 | } 58 | 59 | this.frameNext -= g.dt; 60 | 61 | }, 62 | 63 | render: function() { 64 | 65 | var g = this.g, 66 | i = this.i; 67 | 68 | if (i) { 69 | 70 | if (this.flipped) { 71 | i = g.draw.flip(i, 0, 1); 72 | } 73 | if (this.flipX) { 74 | i = g.draw.flip(i, 1, 0); 75 | } 76 | 77 | g.ctx.drawImage(i, 78 | ( this.frame * this.w ) - this.w, 0, 79 | this.w, this.h, 80 | ~~this.x, ~~this.y, 81 | this.w, this.h 82 | ); 83 | } else { 84 | this.g.draw.rect(~~this.x, ~~this.y, this.w, this.h, this.col); 85 | } 86 | 87 | }, 88 | 89 | 90 | keepOnScreen: function() { 91 | 92 | var g = this.g, 93 | yBound = g.bg.ground || 0, 94 | atEdge = false; 95 | 96 | 97 | 98 | if (this.x < 0) { 99 | this.x = 0; 100 | atEdge = true; 101 | } else if (this.x > ( g.w - this.w )) { 102 | this.x = g.w - this.w; 103 | atEdge = true; 104 | } 105 | 106 | if (this.y < yBound) { 107 | this.y = yBound; 108 | this.jumping = false; 109 | this.flipping = false; 110 | this.jumpCount = 0; 111 | atEdge = true; 112 | } else if(( this.y ) > ( g.h - yBound ) - this.h) { 113 | this.y = g.h - this.h - yBound; 114 | this.jumping = false; 115 | this.flipping = false; 116 | this.jumpCount = 0; 117 | atEdge = true; 118 | } 119 | 120 | return atEdge; 121 | 122 | }, 123 | 124 | 125 | checkOffScreen: function() { 126 | 127 | var g = this.g; 128 | return (this.x < 0 || 129 | this.x > (g.w - this.w) || 130 | this.y < 0 || 131 | this.y > (g.h - this.h) 132 | ); 133 | 134 | }, 135 | 136 | 137 | doDamage: function(o) { 138 | this.remove = true; 139 | }, 140 | 141 | 142 | kill: function() { 143 | 144 | this.dead = true; 145 | this.remove = true; 146 | 147 | }, 148 | 149 | receiveDamage: function(o) { 150 | this.kill(); 151 | }, 152 | 153 | hitGroup: function(group) { 154 | 155 | var g = this.g, 156 | i = g.ents.length; 157 | 158 | while (i--) { 159 | if (g.ents[i] && g.ents[i].group === group && 160 | g.ents[i].id !== this.id && 161 | this.hit(g.ents[i])) { 162 | this.doDamage(g.ents[i]); 163 | g.ents[i].receiveDamage(this); 164 | } 165 | } 166 | 167 | }, 168 | 169 | 170 | hit: function(o) { 171 | 172 | return !((o.y+o.h-1this.y+this.h-1) || 173 | (o.x+o.w-1this.x+this.w-1)); 174 | 175 | }, 176 | 177 | 178 | mkImg: function(name) { 179 | 180 | var g = this.g; 181 | 182 | this.i = g.draw.scale(g.imgs[name], this.scale); 183 | 184 | this.w = ( this.i.width / this.frames); 185 | this.h = this.i.height; 186 | this.iHurt = g.draw.scale(g.imgs[name + '_w'], this.scale); 187 | 188 | 189 | } 190 | 191 | 192 | 193 | }); 194 | -------------------------------------------------------------------------------- /js/engine/helpers.js: -------------------------------------------------------------------------------- 1 | $.H = { 2 | listen: function(event, callback) { 3 | window.addEventListener(event, callback, false); 4 | }, 5 | 6 | el: function(id) { 7 | return document.getElementById(id); 8 | }, 9 | 10 | rnd: function (min, max) { 11 | return ~~(Math.random() * max) + min; 12 | }, 13 | 14 | rndArray: function(a) { 15 | return a[~~(Math.random() * a.length)]; 16 | }, 17 | 18 | textWidth: function(s, f) { 19 | 20 | return ( s.length * (3 * f.scale) ) + 21 | ( s.length * (1 * f.scale) ); 22 | 23 | }, 24 | 25 | 26 | // tween: function (t, b, c, d) { 27 | // return c*t/d + b; 28 | // }, 29 | 30 | // getDist: function(v1, v2) { 31 | // var dx = v1.x - v2.x, 32 | // dy = v1.y - v2.y; 33 | 34 | // return Math.sqrt((dx * dx) + (dy * dy)); 35 | // }, 36 | 37 | 38 | // getAngle: function(v1, v2) { 39 | // var dx = v1.x - v2.x, 40 | // dy = v1.y - v2.y; 41 | 42 | // return (Math.atan2(dy, dx)); 43 | // }, 44 | 45 | 46 | fullScreen: function(el) { 47 | if(el.requestFullscreen) { 48 | el.requestFullscreen(); 49 | } else if(el.mozRequestFullScreen) { 50 | el.mozRequestFullScreen(); 51 | } else if(el.webkitRequestFullscreen) { 52 | el.webkitRequestFullscreen(); 53 | } else if(el.msRequestFullscreen) { 54 | el.msRequestFullscreen(); 55 | } 56 | 57 | window.removeEventListener('click', $.fullScreen); 58 | window.removeEventListener('keydown', $.fullScreen); 59 | window.removeEventListener('touchstart', $.fullScreen); 60 | 61 | }, 62 | 63 | 64 | mkCanvas: function(w, h) { 65 | 66 | var c = document.createElement('canvas'), 67 | ctx = c.getContext('2d'); 68 | 69 | c.width = w; 70 | c.height = h; 71 | 72 | ctx.mozImageSmoothingEnabled = false; 73 | ctx.webkitImageSmoothingEnabled = false; 74 | ctx.msImageSmoothingEnabled = false; 75 | ctx.imageSmoothingEnabled = false; 76 | 77 | return c; 78 | }, 79 | 80 | resize: function(img, scale, col) { 81 | 82 | scale = scale || 1; 83 | col = col || false; 84 | 85 | var widthScaled = img.width * scale; 86 | var heightScaled = img.height * scale; 87 | 88 | var orig = document.createElement('canvas'); 89 | orig.width = img.width; 90 | orig.height = img.height; 91 | var origCtx = orig.getContext('2d'); 92 | origCtx.drawImage(img, 0, 0); 93 | 94 | 95 | var origPixels = origCtx.getImageData(0, 0, img.width, img.height); 96 | 97 | var scaled = document.createElement('canvas'); 98 | scaled.width = widthScaled; 99 | scaled.height = heightScaled; 100 | var scaledCtx = scaled.getContext('2d'); 101 | var scaledPixels = scaledCtx.getImageData( 0, 0, widthScaled, heightScaled ); 102 | var y, x; 103 | 104 | for( y = 0; y < heightScaled; y++ ) { 105 | for( x = 0; x < widthScaled; x++ ) { 106 | var index = (Math.floor(y / scale) * img.width + Math.floor(x / scale)) * 4; 107 | var indexScaled = (y * widthScaled + x) * 4; 108 | scaledPixels.data[ indexScaled ] = origPixels.data[ index ]; 109 | scaledPixels.data[ indexScaled+1 ] = origPixels.data[ index+1 ]; 110 | scaledPixels.data[ indexScaled+2 ] = origPixels.data[ index+2 ]; 111 | scaledPixels.data[ indexScaled+3 ] = origPixels.data[ index+3 ]; 112 | if (origPixels.data[index+3] === 0) { 113 | scaledPixels.data[ indexScaled ] = 0; 114 | scaledPixels.data[ indexScaled+1 ] = 0; 115 | scaledPixels.data[ indexScaled+2 ] = 0; 116 | scaledPixels.data[ indexScaled+3 ] = 0; 117 | } else if (col) { 118 | scaledPixels.data[ indexScaled ] = col[0]; 119 | scaledPixels.data[ indexScaled+1 ] = col[1]; 120 | scaledPixels.data[ indexScaled+2 ] = col[2]; 121 | scaledPixels.data[ indexScaled+3 ] = 255; 122 | } 123 | } 124 | } 125 | 126 | scaledCtx.putImageData( scaledPixels, 0, 0 ); 127 | var image = new Image(); 128 | image.src = scaled.toDataURL('image/png'); 129 | return image; 130 | 131 | } 132 | }; 133 | 134 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'), 2 | cheerio = require('cheerio'), 3 | gulp = require('gulp'), 4 | concat = require('gulp-concat'), 5 | htmlmin = require('gulp-htmlmin'), 6 | rimraf = require('gulp-rimraf'), 7 | rename = require('gulp-rename'), 8 | replace = require('gulp-replace'), 9 | webserver = require('gulp-webserver'), 10 | uglify = require('gulp-uglify'), 11 | unzip = require('gulp-unzip'), 12 | zip = require('gulp-zip'), 13 | exclude_min = ['js/lib/jsfxr.min.js'], 14 | config = { js: [] }; 15 | 16 | 17 | gulp.task('build', ['initbuild', 'jsmin', 'addjs', 'zip', 'unzip', 'clean', 'report']); 18 | 19 | 20 | gulp.task('serve', function() { 21 | gulp.src('.') 22 | .pipe(webserver({ 23 | livereload: false, 24 | host: '0.0.0.0', 25 | port: 8013, 26 | open: true 27 | })); 28 | }); 29 | 30 | 31 | gulp.task('initbuild', function() { 32 | 33 | var stream, html, $, src, js = []; 34 | 35 | // delete prev files 36 | stream = gulp.src('game.zip') 37 | .pipe(rimraf()); 38 | 39 | stream = gulp.src('g.js') 40 | .pipe(rimraf()); 41 | 42 | stream = gulp.src('index.html') 43 | .pipe(rimraf()); 44 | 45 | 46 | // get a list of all js scripts from our dev file 47 | html = fs.readFileSync('dev.html', 'utf-8', function(e, data) { 48 | return data; 49 | }); 50 | 51 | $ = cheerio.load(html); 52 | 53 | $('script').each(function() { 54 | src = $(this).attr('src'); 55 | if (exclude_min.indexOf(src) === -1) { 56 | js.push(src); 57 | } 58 | }); 59 | 60 | config.js = js; 61 | 62 | }); 63 | 64 | gulp.task('jsmin', ['initbuild'], function() { 65 | 66 | var stream = gulp.src(config.js) 67 | .pipe(concat('g.js')) 68 | .pipe(uglify()) 69 | .pipe(gulp.dest('.')); 70 | 71 | return stream; 72 | 73 | }); 74 | 75 | gulp.task('addjs', ['jsmin'], function() { 76 | 77 | var js = fs.readFileSync('g.js', 'utf-8', function(e, data) { 78 | return data; 79 | }); 80 | 81 | var i, tmp, extra_js = ''; 82 | 83 | for (i = 0; i < exclude_min.length; i += 1) { 84 | console.log(exclude_min[i]) 85 | extra_js += fs.readFileSync(exclude_min[i], 'utf-8', function(e, data) { 86 | return data; 87 | }); 88 | } 89 | console.log(extra_js.length, 'OK', exclude_min); 90 | 91 | var stream = gulp.src('dev.html') 92 | .pipe(replace(/<.*?script.*?>.*?<\/.*?script.*?>/igm, '')) 93 | .pipe(replace(/<\/body>/igm, '')) 94 | .pipe(htmlmin({collapseWhitespace: true})) 95 | .pipe(rename('index.html')) 96 | .pipe(gulp.dest('./tmp')); 97 | 98 | return stream; 99 | 100 | }); 101 | 102 | gulp.task('zip', ['addjs'], function() { 103 | var stream = gulp.src('tmp/index.html') 104 | .pipe(zip('game.zip')) 105 | .pipe(gulp.dest('.')); 106 | 107 | return stream; 108 | }); 109 | 110 | 111 | gulp.task('unzip', ['zip'], function() { 112 | var stream = gulp.src('game.zip') 113 | .pipe(unzip()) 114 | .pipe(gulp.dest('.')); 115 | 116 | return stream; 117 | }); 118 | 119 | 120 | gulp.task('clean', ['unzip'], function() { 121 | var stream = gulp.src('tmp/') 122 | .pipe(rimraf()); 123 | 124 | 125 | return stream; 126 | }); 127 | 128 | gulp.task('report', ['clean'], function() { 129 | var stat = fs.statSync('game.zip'), 130 | limit = 1024 * 13, 131 | size = stat.size, 132 | remaining = limit - size, 133 | percentage = (remaining / limit) * 100; 134 | 135 | percentage = Math.round(percentage * 100) / 100 136 | 137 | console.log('\n\n-------------'); 138 | console.log('BYTES USED: ' + stat.size); 139 | console.log('BYTES REMAINING: ' + remaining); 140 | console.log(percentage +'%'); 141 | console.log('-------------\n\n'); 142 | }); 143 | 144 | 145 | gulp.task('encode', function() { 146 | var files = fs.readdirSync('./a'), 147 | gifs = [], 148 | n, parts, base64; 149 | 150 | for ( n in files) { 151 | if (files[n].indexOf('.gif') !== -1) { 152 | gifs.push(files[n]); 153 | } 154 | } 155 | 156 | for (n = 0; n < gifs.length; n += 1) { 157 | 158 | fs.readFileSync('.a/'+gifs[n], function(err, data) { 159 | console.log(err, data); 160 | }); 161 | parts = gifs[n].split('.'); 162 | console.log(parts[0], gifs[n], base64); 163 | } 164 | 165 | }); 166 | -------------------------------------------------------------------------------- /js/engine/game.js: -------------------------------------------------------------------------------- 1 | $.Game = function() { 2 | 3 | this.w = 480; 4 | this.h = 320; 5 | this.bgs = {}; 6 | this.imgs = {}; 7 | this.fonts = {}; 8 | this.ents = []; 9 | this.events = []; 10 | this.sfx = {}; 11 | this.plays = 0; 12 | this.time = 0; 13 | this.dt = 0; 14 | this.fps = 0; 15 | this.hiScore = parseInt( localStorage.getItem('hiScore'), 10 ) || 100; 16 | 17 | this.boot = function(state) { 18 | 19 | var g = this; 20 | 21 | this.startState = state || 'Title'; 22 | this.c = document.getElementsByTagName('canvas')[0]; 23 | this.ctx = this.c.getContext('2d'); 24 | this.c.style.width = this.w+'px'; 25 | this.c.style.height = this.h+'px'; 26 | 27 | this.draw = new $.Draw(this.ctx, this.w, this.h); 28 | 29 | this.load = new $.Load(this); 30 | 31 | document.title = $.data.title; 32 | 33 | }; 34 | 35 | this.init = function() { 36 | 37 | var g = this, 38 | ua = navigator.userAgent.toLowerCase(); 39 | 40 | 41 | g.mobile = 'createTouch' in document || false; 42 | g.mobile = 'createTouch' in document || false; 43 | g.android = ua.indexOf('android') > -1; 44 | g.ios = /ipad|iphone|ipod/.test(ua); 45 | g.firefox = ua.indexOf('firefox') > -1; 46 | 47 | 48 | g.input = new $.Input(); 49 | g.input.init(g); 50 | 51 | g.emitter = new $.Emitter(g); 52 | 53 | // slows down ios loading time for order of magnitude 54 | if (this.ios) { 55 | this.audio = { play: function() {}, say: function() {} }; 56 | } else { 57 | this.audio = new $.Audio($.data.sfx); 58 | this.audio.init(); 59 | } 60 | 61 | 62 | $.H.el('b').style.display = 'block'; 63 | $.H.el('l').style.display = 'none'; 64 | 65 | g.resize(); 66 | $.H.listen('resize', function() { 67 | g.resize(); 68 | }); 69 | $.H.listen('orientationchange', function() { 70 | g.resize(); 71 | }); 72 | 73 | 74 | g.shake = new $.Shake(g); 75 | g.changeState(g.startState); 76 | 77 | function gameLoop() { 78 | g.loop(); 79 | requestAnimationFrame(gameLoop, g.c); 80 | } 81 | gameLoop(); 82 | 83 | }; 84 | 85 | 86 | this.changeState = function(state) { 87 | var g = this; 88 | g.ents = []; 89 | g.events = []; 90 | g.tick = 0; 91 | g.state = new $[state](g); 92 | }; 93 | 94 | 95 | this.addEvent = function(e) { 96 | this.events.push(e); 97 | }; 98 | 99 | 100 | 101 | this.resize = function() { 102 | 103 | var winH = window.innerHeight, 104 | winW = window.innerWidth, 105 | ratio = this.w / this.h, 106 | w2 = winH * ratio, 107 | left = ~~( winW - w2 ) / 2; 108 | scale = w2 / this.w; 109 | 110 | if (winW < winH) { 111 | $.H.el('l').style.display = 'block'; 112 | $.H.el('h').innerHTML = 'Rotate Device'; 113 | $.H.el('b').style.display = 'none'; 114 | } else { 115 | $.H.el('l').style.display = 'none'; 116 | $.H.el('b').style.display = 'block'; 117 | } 118 | 119 | this.c.width = this.w; 120 | this.c.height = this.h; 121 | 122 | this.w2 = this.w / 2; 123 | this.h2 = this.h / 2; 124 | 125 | this.c.style.width = ~~(w2)+ 'px'; 126 | this.c.style.height = ~~(winH) + 'px'; 127 | this.c.style.marginLeft = ~~( (winW - w2) / 2 )+'px'; 128 | }; 129 | 130 | 131 | this.loop = function(t) { 132 | 133 | var g = this, 134 | now = new Date().getTime(); 135 | 136 | this.dt = now - (this.time || now); 137 | this.fps = ~~( 1000 / this.dt ); 138 | 139 | this.time = now; 140 | 141 | g.shake.update(); 142 | g.state.update(); 143 | g.state.render(); 144 | g.tick += ( g.dt / 1000); 145 | 146 | }; 147 | 148 | this.countGroup = function(name) { 149 | var e = this.ents, i = e.length, 150 | num = 0; 151 | 152 | while(i--) { 153 | if (e[i].group === name) { 154 | num += 1; 155 | } 156 | } 157 | 158 | return num; 159 | }; 160 | 161 | this.mkFont = function(col, scale) { 162 | var g = this, 163 | f = g.draw.scale(g.fonts[col], scale); 164 | 165 | f.scale = scale; 166 | return f; 167 | }; 168 | 169 | 170 | this.findClosest = function(e, group) { 171 | 172 | var g = this, 173 | i = g.ents.length, 174 | angle = -1.5, 175 | closest = g.w * 2, 176 | selected, 177 | tmp, 178 | dist; 179 | 180 | while (i--) { 181 | tmp = g.ents[i]; 182 | if (tmp.group === group) { 183 | 184 | dist = $.H.getDist(e, tmp); 185 | if (dist < closest) { 186 | closest = dist; 187 | selected = tmp; 188 | } 189 | 190 | } 191 | } 192 | 193 | if (selected) { 194 | angle = $.H.getAngle(selected, e); 195 | } 196 | 197 | return { 198 | dist: closest, 199 | angle: angle, 200 | e: selected 201 | }; 202 | 203 | }; 204 | 205 | 206 | }; 207 | 208 | 209 | -------------------------------------------------------------------------------- /js/game/states/play.js: -------------------------------------------------------------------------------- 1 | $.Play = $.State.extend({ 2 | 3 | init: function(g) { 4 | 5 | this._super(g); 6 | 7 | this.h1 = g.mkFont('g', 5); 8 | this.p = g.mkFont('w', 3); 9 | g.newHi = false; 10 | 11 | g.plays += 1; 12 | g.score = 0; 13 | 14 | this.levelNum = 0; 15 | g.waves = []; 16 | 17 | this.p1 = new $.Robo(g, { 18 | x: 60, y: 200 19 | }); 20 | g.p1 = this.p1; 21 | g.ents.push(this.p1); 22 | 23 | this.portal = new $.Portal(g, {}); 24 | g.ents.push(this.portal); 25 | 26 | this.gameover = false; 27 | g.state = this; 28 | 29 | this.bulletDelay = 0; 30 | g.bulletInterval = 200; 31 | 32 | g.audio.play('alarm'); 33 | this.nextLevel(); 34 | }, 35 | 36 | 37 | update: function() { 38 | 39 | var g = this.g, 40 | s = this, 41 | i = g.input, 42 | k = i.k; 43 | 44 | this._super(); 45 | 46 | this.bg.update(); 47 | 48 | if (( i.touchRight || k[16] ) && 49 | this.bulletDelay < 0 && !s.p1.dead && !s.p1.pause) { 50 | 51 | this.bulletDelay = g.bulletInterval; 52 | g.ents.push(new $.Bullet(g, { 53 | x: this.p1.x + ( this.p1.w / 2 ), y: this.p1.y + (this.p1.h / 3), 54 | angle: 0 55 | })); 56 | if (g.p1.powerup > 1) { 57 | g.ents.push(new $.Bullet(g, { 58 | x: this.p1.x + ( this.p1.w / 2 ), y: this.p1.y + (this.p1.h / 3), 59 | angle: -0.3 60 | })); 61 | } 62 | if (g.p1.powerup > 2) { 63 | g.ents.push(new $.Bullet(g, { 64 | x: this.p1.x + ( this.p1.w / 2 ), y: this.p1.y + (this.p1.h / 3), 65 | angle: 0.3 66 | })); 67 | } 68 | } 69 | 70 | this.bulletDelay -= g.dt; 71 | 72 | if (g.p1.dead && !this.gameover) { 73 | this.gameover = true; 74 | if (g.score > g.hiScore) { 75 | g.newHi = true; 76 | g.hiScore = g.score; 77 | try { 78 | localStorage.setItem('hiScore', g.score); 79 | } catch(e) {} 80 | } 81 | g.ents.push(new $.Fader(g, {col: $.cols.bloodred, cb: function() { 82 | g.changeState('Gameover'); 83 | }})); 84 | } 85 | 86 | if (this.gameover && g.doJump) { 87 | g.changeState('Title'); 88 | } 89 | 90 | // if (g.bg.speed !== 0 && !g.p1.dead) { 91 | g.distance -= g.dt; 92 | // } 93 | 94 | if (g.distance < 0) { 95 | g.p1.vx = 0.5; 96 | this.portal.show(); 97 | g.bg.stop(); 98 | } 99 | 100 | if (g.p1.x > 450) { 101 | this.portal.active = true; 102 | } 103 | if (g.distance < -2000 && !this.gameover) { 104 | this.portal.active = true; 105 | } 106 | 107 | if (this.portal.active) { 108 | this.levelUp(); 109 | } 110 | 111 | }, 112 | 113 | 114 | render: function() { 115 | 116 | var g = this.g, 117 | s = this, 118 | numBaddies = g.ents.length, 119 | c = $.cols, n; 120 | 121 | 122 | this.bg.render(); 123 | 124 | s._super(); 125 | 126 | 127 | 128 | if (!this.gameover) { 129 | // g.draw.text(g.fps, this.p, 400, 40); 130 | // g.draw.text(numBaddies, this.p, 40, 40); 131 | g.draw.text(g.score, this.p, 40, 40); 132 | } 133 | 134 | 135 | }, 136 | 137 | 138 | levelUp: function() { 139 | 140 | var g = this.g, 141 | s = this, 142 | i = g.ents.length; 143 | 144 | if (g.p1.pause) { 145 | return; 146 | } 147 | 148 | g.audio.play('levelup'); 149 | g.p1.pause = true; 150 | while (i--) { 151 | if (g.ents[i].group === 'baddies') { 152 | g.ents[i].remove = true; 153 | } 154 | } 155 | i = g.ents.length; 156 | while (i--) { 157 | if (g.ents[i].remove) { 158 | g.ents.splice(i, 1); 159 | } 160 | } 161 | 162 | g.events = []; 163 | 164 | s.p1.x = g.w / 3; 165 | s.portal.hide(); 166 | 167 | g.ents.push(new $.Fader(g, {col: $.cols.nightblue, cb: function() { 168 | g.ents.push(new $.Fader(g, {col: $.cols.nightblue, cb: function() { 169 | g.state.nextLevel(); 170 | g.p1.pause = false; 171 | }, dir: -1 })); 172 | }})); 173 | 174 | if (g.bonus) { 175 | g.score += 100; 176 | g.ents.push(new $.Msg(g, { 177 | text: 'BONUS', 178 | x: g.bonus.x, y: g.bonus.y 179 | })); 180 | g.bonus = false; 181 | } 182 | 183 | }, 184 | 185 | 186 | nextLevel: function() { 187 | 188 | var g = this.g, 189 | s = this, 190 | l, i; 191 | 192 | 193 | if (this.levelNum > ( $.L.length - 1 )) { 194 | this.levelNum = 0; 195 | } 196 | 197 | this.level = $.L[this.levelNum]; 198 | l = this.level; 199 | if (l.baddies[0] === 'all') { 200 | l.baddies = Object.keys($.data.moves); 201 | } 202 | this.bg = new $[l.bg](g, l.bgSettings); 203 | 204 | 205 | if (l.powerup) { 206 | g.addEvent({ 207 | time: l.powerup, 208 | cb: function() { 209 | g.ents.push(new $.Battery(g, {})); 210 | } 211 | }); 212 | } 213 | 214 | g.addEvent({ 215 | time: 2, 216 | cb: function() { 217 | s.spawnWave(); 218 | for (i = 0; i < l.init.length; i += 1) { 219 | g.ents.push(new $[ l.init[i][0] ](g, l.init[i][1])); 220 | } 221 | } 222 | }); 223 | 224 | 225 | 226 | g.bg = this.bg; 227 | g.level = this.level; 228 | g.distance = g.level.distance; 229 | g.p1.x = 60; 230 | g.p1.vx = 0; 231 | s.portal.hide(); 232 | g.p1.pause = false; 233 | 234 | this.levelNum += 1; 235 | 236 | }, 237 | 238 | spawnWave: function() { 239 | 240 | var g = this.g, 241 | s = this, 242 | l = this.level, 243 | size = l.waveSize, 244 | waveId = new Date().getTime(), 245 | m = $.H.rndArray(l.baddies), 246 | i; 247 | 248 | 249 | if (g.distance < 100 || g.p1.dead || l.baddies.length === 0) { 250 | return; 251 | } 252 | 253 | g.waves[waveId] = size; 254 | for (i = 0; i < l.waveSize; i += 1) { 255 | g.addEvent({ 256 | time: 0.2 * i, 257 | cb: function() { 258 | g.ents.push(new $.Drone(g, { 259 | m: m, 260 | waveId: waveId, 261 | x:500 262 | })); 263 | } 264 | }); 265 | } 266 | 267 | g.addEvent({ 268 | time: l.interval, 269 | cb: function() { 270 | s.spawnWave(); 271 | } 272 | }); 273 | 274 | } 275 | 276 | 277 | 278 | 279 | }); 280 | 281 | 282 | 283 | -------------------------------------------------------------------------------- /js/lib/jsfxr.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SfxrParams 3 | * 4 | * Copyright 2010 Thomas Vian 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * @author Thomas Vian 19 | */ 20 | /** @constructor */ 21 | function SfxrParams() { 22 | //-------------------------------------------------------------------------- 23 | // 24 | // Settings String Methods 25 | // 26 | //-------------------------------------------------------------------------- 27 | 28 | /** 29 | * Parses a settings array into the parameters 30 | * @param array Array of the settings values, where elements 0 - 23 are 31 | * a: waveType 32 | * b: attackTime 33 | * c: sustainTime 34 | * d: sustainPunch 35 | * e: decayTime 36 | * f: startFrequency 37 | * g: minFrequency 38 | * h: slide 39 | * i: deltaSlide 40 | * j: vibratoDepth 41 | * k: vibratoSpeed 42 | * l: changeAmount 43 | * m: changeSpeed 44 | * n: squareDuty 45 | * o: dutySweep 46 | * p: repeatSpeed 47 | * q: phaserOffset 48 | * r: phaserSweep 49 | * s: lpFilterCutoff 50 | * t: lpFilterCutoffSweep 51 | * u: lpFilterResonance 52 | * v: hpFilterCutoff 53 | * w: hpFilterCutoffSweep 54 | * x: masterVolume 55 | * @return If the string successfully parsed 56 | */ 57 | this.setSettings = function(values) 58 | { 59 | for ( var i = 0; i < 24; i++ ) 60 | { 61 | this[String.fromCharCode( 97 + i )] = values[i] || 0; 62 | } 63 | 64 | // I moved this here from the reset(true) function 65 | if (this['c'] < .01) { 66 | this['c'] = .01; 67 | } 68 | 69 | var totalTime = this['b'] + this['c'] + this['e']; 70 | if (totalTime < .18) { 71 | var multiplier = .18 / totalTime; 72 | this['b'] *= multiplier; 73 | this['c'] *= multiplier; 74 | this['e'] *= multiplier; 75 | } 76 | } 77 | } 78 | 79 | /** 80 | * SfxrSynth 81 | * 82 | * Copyright 2010 Thomas Vian 83 | * 84 | * Licensed under the Apache License, Version 2.0 (the "License"); 85 | * you may not use this file except in compliance with the License. 86 | * You may obtain a copy of the License at 87 | * 88 | * http://www.apache.org/licenses/LICENSE-2.0 89 | * 90 | * Unless required by applicable law or agreed to in writing, software 91 | * distributed under the License is distributed on an "AS IS" BASIS, 92 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 93 | * See the License for the specific language governing permissions and 94 | * limitations under the License. 95 | * 96 | * @author Thomas Vian 97 | */ 98 | /** @constructor */ 99 | function SfxrSynth() { 100 | // All variables are kept alive through function closures 101 | 102 | //-------------------------------------------------------------------------- 103 | // 104 | // Sound Parameters 105 | // 106 | //-------------------------------------------------------------------------- 107 | 108 | this._params = new SfxrParams(); // Params instance 109 | 110 | //-------------------------------------------------------------------------- 111 | // 112 | // Synth Variables 113 | // 114 | //-------------------------------------------------------------------------- 115 | 116 | var _envelopeLength0, // Length of the attack stage 117 | _envelopeLength1, // Length of the sustain stage 118 | _envelopeLength2, // Length of the decay stage 119 | 120 | _period, // Period of the wave 121 | _maxPeriod, // Maximum period before sound stops (from minFrequency) 122 | 123 | _slide, // Note slide 124 | _deltaSlide, // Change in slide 125 | 126 | _changeAmount, // Amount to change the note by 127 | _changeTime, // Counter for the note change 128 | _changeLimit, // Once the time reaches this limit, the note changes 129 | 130 | _squareDuty, // Offset of center switching point in the square wave 131 | _dutySweep; // Amount to change the duty by 132 | 133 | //-------------------------------------------------------------------------- 134 | // 135 | // Synth Methods 136 | // 137 | //-------------------------------------------------------------------------- 138 | 139 | /** 140 | * Resets the runing variables from the params 141 | * Used once at the start (total reset) and for the repeat effect (partial reset) 142 | */ 143 | this.reset = function() { 144 | // Shorter reference 145 | var p = this._params; 146 | 147 | _period = 100 / (p['f'] * p['f'] + .001); 148 | _maxPeriod = 100 / (p['g'] * p['g'] + .001); 149 | 150 | _slide = 1 - p['h'] * p['h'] * p['h'] * .01; 151 | _deltaSlide = -p['i'] * p['i'] * p['i'] * .000001; 152 | 153 | if (!p['a']) { 154 | _squareDuty = .5 - p['n'] / 2; 155 | _dutySweep = -p['o'] * .00005; 156 | } 157 | 158 | _changeAmount = 1 + p['l'] * p['l'] * (p['l'] > 0 ? -.9 : 10); 159 | _changeTime = 0; 160 | _changeLimit = p['m'] == 1 ? 0 : (1 - p['m']) * (1 - p['m']) * 20000 + 32; 161 | } 162 | 163 | // I split the reset() function into two functions for better readability 164 | this.totalReset = function() { 165 | this.reset(); 166 | 167 | // Shorter reference 168 | var p = this._params; 169 | 170 | // Calculating the length is all that remained here, everything else moved somewhere 171 | _envelopeLength0 = p['b'] * p['b'] * 100000; 172 | _envelopeLength1 = p['c'] * p['c'] * 100000; 173 | _envelopeLength2 = p['e'] * p['e'] * 100000 + 12; 174 | // Full length of the volume envelop (and therefore sound) 175 | // Make sure the length can be divided by 3 so we will not need the padding "==" after base64 encode 176 | return ((_envelopeLength0 + _envelopeLength1 + _envelopeLength2) / 3 | 0) * 3; 177 | } 178 | 179 | /** 180 | * Writes the wave to the supplied buffer ByteArray 181 | * @param buffer A ByteArray to write the wave to 182 | * @return If the wave is finished 183 | */ 184 | this.synthWave = function(buffer, length) { 185 | // Shorter reference 186 | var p = this._params; 187 | 188 | // If the filters are active 189 | var _filters = p['s'] != 1 || p['v'], 190 | // Cutoff multiplier which adjusts the amount the wave position can move 191 | _hpFilterCutoff = p['v'] * p['v'] * .1, 192 | // Speed of the high-pass cutoff multiplier 193 | _hpFilterDeltaCutoff = 1 + p['w'] * .0003, 194 | // Cutoff multiplier which adjusts the amount the wave position can move 195 | _lpFilterCutoff = p['s'] * p['s'] * p['s'] * .1, 196 | // Speed of the low-pass cutoff multiplier 197 | _lpFilterDeltaCutoff = 1 + p['t'] * .0001, 198 | // If the low pass filter is active 199 | _lpFilterOn = p['s'] != 1, 200 | // masterVolume * masterVolume (for quick calculations) 201 | _masterVolume = p['x'] * p['x'], 202 | // Minimum frequency before stopping 203 | _minFreqency = p['g'], 204 | // If the phaser is active 205 | _phaser = p['q'] || p['r'], 206 | // Change in phase offset 207 | _phaserDeltaOffset = p['r'] * p['r'] * p['r'] * .2, 208 | // Phase offset for phaser effect 209 | _phaserOffset = p['q'] * p['q'] * (p['q'] < 0 ? -1020 : 1020), 210 | // Once the time reaches this limit, some of the iables are reset 211 | _repeatLimit = p['p'] ? ((1 - p['p']) * (1 - p['p']) * 20000 | 0) + 32 : 0, 212 | // The punch factor (louder at begining of sustain) 213 | _sustainPunch = p['d'], 214 | // Amount to change the period of the wave by at the peak of the vibrato wave 215 | _vibratoAmplitude = p['j'] / 2, 216 | // Speed at which the vibrato phase moves 217 | _vibratoSpeed = p['k'] * p['k'] * .01, 218 | // The type of wave to generate 219 | _waveType = p['a']; 220 | 221 | var _envelopeLength = _envelopeLength0, // Length of the current envelope stage 222 | _envelopeOverLength0 = 1 / _envelopeLength0, // (for quick calculations) 223 | _envelopeOverLength1 = 1 / _envelopeLength1, // (for quick calculations) 224 | _envelopeOverLength2 = 1 / _envelopeLength2; // (for quick calculations) 225 | 226 | // Damping muliplier which restricts how fast the wave position can move 227 | var _lpFilterDamping = 5 / (1 + p['u'] * p['u'] * 20) * (.01 + _lpFilterCutoff); 228 | if (_lpFilterDamping > .8) { 229 | _lpFilterDamping = .8; 230 | } 231 | _lpFilterDamping = 1 - _lpFilterDamping; 232 | 233 | var _finished = false, // If the sound has finished 234 | _envelopeStage = 0, // Current stage of the envelope (attack, sustain, decay, end) 235 | _envelopeTime = 0, // Current time through current enelope stage 236 | _envelopeVolume = 0, // Current volume of the envelope 237 | _hpFilterPos = 0, // Adjusted wave position after high-pass filter 238 | _lpFilterDeltaPos = 0, // Change in low-pass wave position, as allowed by the cutoff and damping 239 | _lpFilterOldPos, // Previous low-pass wave position 240 | _lpFilterPos = 0, // Adjusted wave position after low-pass filter 241 | _periodTemp, // Period modified by vibrato 242 | _phase = 0, // Phase through the wave 243 | _phaserInt, // Integer phaser offset, for bit maths 244 | _phaserPos = 0, // Position through the phaser buffer 245 | _pos, // Phase expresed as a Number from 0-1, used for fast sin approx 246 | _repeatTime = 0, // Counter for the repeats 247 | _sample, // Sub-sample calculated 8 times per actual sample, averaged out to get the super sample 248 | _superSample, // Actual sample writen to the wave 249 | _vibratoPhase = 0; // Phase through the vibrato sine wave 250 | 251 | // Buffer of wave values used to create the out of phase second wave 252 | var _phaserBuffer = new Array(1024), 253 | // Buffer of random values used to generate noise 254 | _noiseBuffer = new Array(32); 255 | for (var i = _phaserBuffer.length; i--; ) { 256 | _phaserBuffer[i] = 0; 257 | } 258 | for (var i = _noiseBuffer.length; i--; ) { 259 | _noiseBuffer[i] = Math.random() * 2 - 1; 260 | } 261 | 262 | for (var i = 0; i < length; i++) { 263 | if (_finished) { 264 | return i; 265 | } 266 | 267 | // Repeats every _repeatLimit times, partially resetting the sound parameters 268 | if (_repeatLimit) { 269 | if (++_repeatTime >= _repeatLimit) { 270 | _repeatTime = 0; 271 | this.reset(); 272 | } 273 | } 274 | 275 | // If _changeLimit is reached, shifts the pitch 276 | if (_changeLimit) { 277 | if (++_changeTime >= _changeLimit) { 278 | _changeLimit = 0; 279 | _period *= _changeAmount; 280 | } 281 | } 282 | 283 | // Acccelerate and apply slide 284 | _slide += _deltaSlide; 285 | _period *= _slide; 286 | 287 | // Checks for frequency getting too low, and stops the sound if a minFrequency was set 288 | if (_period > _maxPeriod) { 289 | _period = _maxPeriod; 290 | if (_minFreqency > 0) { 291 | _finished = true; 292 | } 293 | } 294 | 295 | _periodTemp = _period; 296 | 297 | // Applies the vibrato effect 298 | if (_vibratoAmplitude > 0) { 299 | _vibratoPhase += _vibratoSpeed; 300 | _periodTemp *= 1 + Math.sin(_vibratoPhase) * _vibratoAmplitude; 301 | } 302 | 303 | _periodTemp |= 0; 304 | if (_periodTemp < 8) { 305 | _periodTemp = 8; 306 | } 307 | 308 | // Sweeps the square duty 309 | if (!_waveType) { 310 | _squareDuty += _dutySweep; 311 | if (_squareDuty < 0) { 312 | _squareDuty = 0; 313 | } else if (_squareDuty > .5) { 314 | _squareDuty = .5; 315 | } 316 | } 317 | 318 | // Moves through the different stages of the volume envelope 319 | if (++_envelopeTime > _envelopeLength) { 320 | _envelopeTime = 0; 321 | 322 | switch (++_envelopeStage) { 323 | case 1: 324 | _envelopeLength = _envelopeLength1; 325 | break; 326 | case 2: 327 | _envelopeLength = _envelopeLength2; 328 | } 329 | } 330 | 331 | // Sets the volume based on the position in the envelope 332 | switch (_envelopeStage) { 333 | case 0: 334 | _envelopeVolume = _envelopeTime * _envelopeOverLength0; 335 | break; 336 | case 1: 337 | _envelopeVolume = 1 + (1 - _envelopeTime * _envelopeOverLength1) * 2 * _sustainPunch; 338 | break; 339 | case 2: 340 | _envelopeVolume = 1 - _envelopeTime * _envelopeOverLength2; 341 | break; 342 | case 3: 343 | _envelopeVolume = 0; 344 | _finished = true; 345 | } 346 | 347 | // Moves the phaser offset 348 | if (_phaser) { 349 | _phaserOffset += _phaserDeltaOffset; 350 | _phaserInt = _phaserOffset | 0; 351 | if (_phaserInt < 0) { 352 | _phaserInt = -_phaserInt; 353 | } else if (_phaserInt > 1023) { 354 | _phaserInt = 1023; 355 | } 356 | } 357 | 358 | // Moves the high-pass filter cutoff 359 | if (_filters && _hpFilterDeltaCutoff) { 360 | _hpFilterCutoff *= _hpFilterDeltaCutoff; 361 | if (_hpFilterCutoff < .00001) { 362 | _hpFilterCutoff = .00001; 363 | } else if (_hpFilterCutoff > .1) { 364 | _hpFilterCutoff = .1; 365 | } 366 | } 367 | 368 | _superSample = 0; 369 | for (var j = 8; j--; ) { 370 | // Cycles through the period 371 | _phase++; 372 | if (_phase >= _periodTemp) { 373 | _phase %= _periodTemp; 374 | 375 | // Generates new random noise for this period 376 | if (_waveType == 3) { 377 | for (var n = _noiseBuffer.length; n--; ) { 378 | _noiseBuffer[n] = Math.random() * 2 - 1; 379 | } 380 | } 381 | } 382 | 383 | // Gets the sample from the oscillator 384 | switch (_waveType) { 385 | case 0: // Square wave 386 | _sample = ((_phase / _periodTemp) < _squareDuty) ? .5 : -.5; 387 | break; 388 | case 1: // Saw wave 389 | _sample = 1 - _phase / _periodTemp * 2; 390 | break; 391 | case 2: // Sine wave (fast and accurate approx) 392 | _pos = _phase / _periodTemp; 393 | _pos = (_pos > .5 ? _pos - 1 : _pos) * 6.28318531; 394 | _sample = 1.27323954 * _pos + .405284735 * _pos * _pos * (_pos < 0 ? 1 : -1); 395 | _sample = .225 * ((_sample < 0 ? -1 : 1) * _sample * _sample - _sample) + _sample; 396 | break; 397 | case 3: // Noise 398 | _sample = _noiseBuffer[Math.abs(_phase * 32 / _periodTemp | 0)]; 399 | } 400 | 401 | // Applies the low and high pass filters 402 | if (_filters) { 403 | _lpFilterOldPos = _lpFilterPos; 404 | _lpFilterCutoff *= _lpFilterDeltaCutoff; 405 | if (_lpFilterCutoff < 0) { 406 | _lpFilterCutoff = 0; 407 | } else if (_lpFilterCutoff > .1) { 408 | _lpFilterCutoff = .1; 409 | } 410 | 411 | if (_lpFilterOn) { 412 | _lpFilterDeltaPos += (_sample - _lpFilterPos) * _lpFilterCutoff; 413 | _lpFilterDeltaPos *= _lpFilterDamping; 414 | } else { 415 | _lpFilterPos = _sample; 416 | _lpFilterDeltaPos = 0; 417 | } 418 | 419 | _lpFilterPos += _lpFilterDeltaPos; 420 | 421 | _hpFilterPos += _lpFilterPos - _lpFilterOldPos; 422 | _hpFilterPos *= 1 - _hpFilterCutoff; 423 | _sample = _hpFilterPos; 424 | } 425 | 426 | // Applies the phaser effect 427 | if (_phaser) { 428 | _phaserBuffer[_phaserPos % 1024] = _sample; 429 | _sample += _phaserBuffer[(_phaserPos - _phaserInt + 1024) % 1024]; 430 | _phaserPos++; 431 | } 432 | 433 | _superSample += _sample; 434 | } 435 | 436 | // Averages out the super samples and applies volumes 437 | _superSample *= .125 * _envelopeVolume * _masterVolume; 438 | 439 | // Clipping if too loud 440 | buffer[i] = _superSample >= 1 ? 32767 : _superSample <= -1 ? -32768 : _superSample * 32767 | 0; 441 | } 442 | 443 | return length; 444 | } 445 | } 446 | 447 | // Adapted from http://codebase.es/riffwave/ 448 | var synth = new SfxrSynth(); 449 | // Export for the Closure Compiler 450 | window['jsfxr'] = function(settings) { 451 | // Initialize SfxrParams 452 | synth._params.setSettings(settings); 453 | // Synthesize Wave 454 | var envelopeFullLength = synth.totalReset(); 455 | var data = new Uint8Array(((envelopeFullLength + 1) / 2 | 0) * 4 + 44); 456 | var used = synth.synthWave(new Uint16Array(data.buffer, 44), envelopeFullLength) * 2; 457 | var dv = new Uint32Array(data.buffer, 0, 44); 458 | // Initialize header 459 | dv[0] = 0x46464952; // "RIFF" 460 | dv[1] = used + 36; // put total size here 461 | dv[2] = 0x45564157; // "WAVE" 462 | dv[3] = 0x20746D66; // "fmt " 463 | dv[4] = 0x00000010; // size of the following 464 | dv[5] = 0x00010001; // Mono: 1 channel, PCM format 465 | dv[6] = 0x0000AC44; // 44,100 samples per second 466 | dv[7] = 0x00015888; // byte rate: two bytes per sample 467 | dv[8] = 0x00100002; // 16 bits per sample, aligned on every two bytes 468 | dv[9] = 0x61746164; // "data" 469 | dv[10] = used; // put number of samples here 470 | 471 | // Base64 encoding written by me, @maettig 472 | used += 44; 473 | var i = 0, 474 | base64Characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/', 475 | output = 'data:audio/wav;base64,'; 476 | for (; i < used; i += 3) 477 | { 478 | var a = data[i] << 16 | data[i + 1] << 8 | data[i + 2]; 479 | output += base64Characters[a >> 18] + base64Characters[a >> 12 & 63] + base64Characters[a >> 6 & 63] + base64Characters[a & 63]; 480 | } 481 | return output; 482 | } 483 | 484 | -------------------------------------------------------------------------------- /g.js: -------------------------------------------------------------------------------- 1 | var $={v:.1,cols:{black:"#000",ash:"#9d9d9d",blind:"#fff",bloodred:"#be2633",pigmeat:"#e06f8b",oldpoop:"#493C2B",newpoop:"#a46422",blaze:"#eb8931",zornskin:"#f7e26b",shadegreen:"#2f484e",leafgreen:"#44891A",slimegreen:"#A3CE27",nightblue:"#1B2632",seablue:"#005784",skyblue:"#31A2F2",cloudblue:"#B2DCEF"}},initializing=!1,fnTest=/xyz/.test(function(){xyz})?/\b_super\b/:/.*/,lastClassId=0;Class=function(){};var inject=function(t){var i=this.prototype,s={};for(var e in t)"function"==typeof t[e]&&"function"==typeof i[e]&&fnTest.test(t[e])?(s[e]=i[e],i[e]=function(t,i){return function(){var e=this._super;this._super=s[t];var h=i.apply(this,arguments);return this._super=e,h}}(e,t[e])):i[e]=t[e]};Class.extend=function(t){function i(){if(!initializing){if(this.staticInstantiate){var t=this.staticInstantiate.apply(this,arguments);if(t)return t}for(var i in this)"object"==typeof this[i]&&(this[i]=ig.copy(this[i]));this.init&&this.init.apply(this,arguments)}return this}var s=this.prototype;initializing=!0;var e=new this;initializing=!1;for(var h in t)e[h]="function"==typeof t[h]&&"function"==typeof s[h]&&fnTest.test(t[h])?function(t,i){return function(){var e=this._super;this._super=s[t];var h=i.apply(this,arguments);return this._super=e,h}}(h,t[h]):t[h];return i.prototype=e,i.prototype.constructor=i,i.extend=window.Class.extend,i.inject=inject,i.classId=e.classId=++lastClassId,i},$.Load=function(t){this.g=t,this.imgsLoaded=0,this.imgsTotal=Object.keys($.data.i).length,this.init=function(){var t,i=this.g,s="data:image/gif;base64,",e=$.data.i;for(t in e)e.hasOwnProperty(t)&&(i.imgs[t]=new Image,i.imgs[t].onload=this.checkLoaded(),i.imgs[t].src=s+e[t])},this.checkLoaded=function(){var t=(this.g,this);this.imgsLoaded+=1,t.imgsLoaded===t.imgsTotal&&window.setTimeout(function(){t.mkFonts()},10)},this.mkFonts=function(){var t,i=this.g,s={b:[0,0,0],w:[255,255,255],g:[163,206,39],p:[224,111,139]},e=i.imgs;for(t in s)i.fonts[t]=$.H.resize(i.imgs.font,1,s[t]);for(t in e)e[t+"_w"]=$.H.resize(e[t],1,[255,255,255]);window.setTimeout(function(){i.init()},10)},this.init()},$.Game=function(){this.w=480,this.h=320,this.bgs={},this.imgs={},this.fonts={},this.ents=[],this.events=[],this.sfx={},this.plays=0,this.time=0,this.dt=0,this.fps=0,this.hiScore=parseInt(localStorage.getItem("hiScore"),10)||100,this.boot=function(t){this.startState=t||"Title",this.c=document.getElementsByTagName("canvas")[0],this.ctx=this.c.getContext("2d"),this.c.style.width=this.w+"px",this.c.style.height=this.h+"px",this.draw=new $.Draw(this.ctx,this.w,this.h),this.load=new $.Load(this),document.title=$.data.title},this.init=function(){function t(){i.loop(),requestAnimationFrame(t,i.c)}var i=this,s=navigator.userAgent.toLowerCase();i.mobile="createTouch"in document||!1,i.mobile="createTouch"in document||!1,i.android=s.indexOf("android")>-1,i.ios=/ipad|iphone|ipod/.test(s),i.firefox=s.indexOf("firefox")>-1,i.input=new $.Input,i.input.init(i),i.emitter=new $.Emitter(i),this.ios?this.audio={play:function(){},say:function(){}}:(this.audio=new $.Audio($.data.sfx),this.audio.init()),$.H.el("b").style.display="block",$.H.el("l").style.display="none",i.resize(),$.H.listen("resize",function(){i.resize()}),$.H.listen("orientationchange",function(){i.resize()}),i.shake=new $.Shake(i),i.changeState(i.startState),t()},this.changeState=function(t){var i=this;i.ents=[],i.events=[],i.tick=0,i.state=new $[t](i)},this.addEvent=function(t){this.events.push(t)},this.resize=function(){var t=window.innerHeight,i=window.innerWidth,s=this.w/this.h,e=t*s;scale=e/this.w,t>i?($.H.el("l").style.display="block",$.H.el("h").innerHTML="Rotate Device",$.H.el("b").style.display="none"):($.H.el("l").style.display="none",$.H.el("b").style.display="block"),this.c.width=this.w,this.c.height=this.h,this.w2=this.w/2,this.h2=this.h/2,this.c.style.width=~~e+"px",this.c.style.height=~~t+"px",this.c.style.marginLeft=~~((i-e)/2)+"px"},this.loop=function(t){var i=this,s=(new Date).getTime();this.dt=s-(this.time||s),this.fps=~~(1e3/this.dt),this.time=s,i.shake.update(),i.state.update(),i.state.render(),i.tick+=i.dt/1e3},this.countGroup=function(t){for(var i=this.ents,s=i.length,e=0;s--;)i[s].group===t&&(e+=1);return e},this.mkFont=function(t,i){var s=this,e=s.draw.scale(s.fonts[t],i);return e.scale=i,e},this.findClosest=function(t,i){for(var s,e,h,n=this,a=n.ents.length,r=-1.5,o=2*n.w;a--;)e=n.ents[a],e.group===i&&(h=$.H.getDist(t,e),o>h&&(o=h,s=e));return s&&(r=$.H.getAngle(s,t)),{dist:o,angle:r,e:s}}},$.H={listen:function(t,i){window.addEventListener(t,i,!1)},el:function(t){return document.getElementById(t)},rnd:function(t,i){return~~(Math.random()*i)+t},rndArray:function(t){return t[~~(Math.random()*t.length)]},textWidth:function(t,i){return 3*t.length*i.scale+1*t.length*i.scale},fullScreen:function(t){t.requestFullscreen?t.requestFullscreen():t.mozRequestFullScreen?t.mozRequestFullScreen():t.webkitRequestFullscreen?t.webkitRequestFullscreen():t.msRequestFullscreen&&t.msRequestFullscreen(),window.removeEventListener("click",$.fullScreen),window.removeEventListener("keydown",$.fullScreen),window.removeEventListener("touchstart",$.fullScreen)},mkCanvas:function(t,i){var s=document.createElement("canvas"),e=s.getContext("2d");return s.width=t,s.height=i,e.mozImageSmoothingEnabled=!1,e.webkitImageSmoothingEnabled=!1,e.msImageSmoothingEnabled=!1,e.imageSmoothingEnabled=!1,s},resize:function(t,i,s){i=i||1,s=s||!1;var e=t.width*i,h=t.height*i,n=document.createElement("canvas");n.width=t.width,n.height=t.height;var a=n.getContext("2d");a.drawImage(t,0,0);var r=a.getImageData(0,0,t.width,t.height),o=document.createElement("canvas");o.width=e,o.height=h;var l,c,d=o.getContext("2d"),u=d.getImageData(0,0,e,h);for(l=0;h>l;l++)for(c=0;e>c;c++){var p=4*(Math.floor(l/i)*t.width+Math.floor(c/i)),g=4*(l*e+c);u.data[g]=r.data[p],u.data[g+1]=r.data[p+1],u.data[g+2]=r.data[p+2],u.data[g+3]=r.data[p+3],0===r.data[p+3]?(u.data[g]=0,u.data[g+1]=0,u.data[g+2]=0,u.data[g+3]=0):s&&(u.data[g]=s[0],u.data[g+1]=s[1],u.data[g+2]=s[2],u.data[g+3]=255)}d.putImageData(u,0,0);var f=new Image;return f.src=o.toDataURL("image/png"),f}},$.Draw=function(t,i,s){this.ctx=t,this.w=i,this.h=s,this.clear=function(){this.ctx.clearRect(0,0,this.w,this.h)},this.rect=function(t,i,s,e,h){this.ctx.fillStyle=h,this.ctx.fillRect(~~t,~~i,s,e)},this.circle=function(t,i,s,e){var h=this.ctx;t+=s/2,i+=s/2,h.beginPath(),h.arc(t,i,s,0,2*Math.PI,!0),h.closePath(),h.fillStyle=e,h.fill()},this.rotate=function(t,i){{var s=document.createElement("canvas"),e=s.getContext("2d"),h=Math.max(t.width,t.height)+6;i*(180/Math.PI)}return s.width=h,s.height=h,e.translate(h/2,h/2),e.rotate(i+Math.PI/2),e.drawImage(t,-(t.width/2),-(t.height/2)),s},this.scale=function(t,i,s){var e=$.H.mkCanvas(t.width*i,t.height*i),h=e.getContext("2d");return e.width&&(h.save(),h.scale(i,i),h.drawImage(t,0,0),h.restore()),e},this.flip=function(t,i,s){var e=$.H.mkCanvas(t.width,t.height),h=e.getContext("2d"),n=i?-1:1,a=s?-1:1,r=i?-1*t.width:0,o=s?-1*t.height:0;return e.width=t.width,e.height=t.height,h.save(),h.scale(n,a),h.drawImage(t,r,o,t.width,t.height),h.restore(),e},this.text=function(t,i,s,e){var h=0,n=this.ctx,a=65,r=0,o=3*i.scale,l=5*i.scale,c=1*i.scale,d=$.H.textWidth(t,i),u=0;for(("number"==typeof t||"0"===t[0])&&(t+="",r=43),s=s||(this.w-d)/2,h=0;h-1&&n.drawImage(i,u,0,o,l,~~s,~~e,o,l),s+=o+c}},$.Audio=function(t){this.sounds=t,this.sfx={},this.init=function(){var t;for(t in this.sounds)this.add(t,10,$.data.sfx[t])},this.add=function(t,i,s){var e;for(this.sfx[t]={tick:0,pool:[]},e=0;i>e;e++){var h=new Audio;h.src=jsfxr(s),this.sfx[t].pool.push(h)}},this.play=function(t){var i=this.sfx[t];i.pool[i.tick].play(),i.tick=i.tick1);e+=1)i=~~((t[e].pageX-r)/o),s=~~((t[e].pageY-a)/o),it.w-this.w&&(this.x=t.w-this.w,s=!0),this.yt.h-i-this.h&&(this.y=t.h-this.h-i,this.jumping=!1,this.flipping=!1,this.jumpCount=0,s=!0),s},checkOffScreen:function(){var t=this.g;return this.x<0||this.x>t.w-this.w||this.y<0||this.y>t.h-this.h},doDamage:function(t){this.remove=!0},kill:function(){this.dead=!0,this.remove=!0},receiveDamage:function(t){this.kill()},hitGroup:function(t){for(var i=this.g,s=i.ents.length;s--;)i.ents[s]&&i.ents[s].group===t&&i.ents[s].id!==this.id&&this.hit(i.ents[s])&&(this.doDamage(i.ents[s]),i.ents[s].receiveDamage(this))},hit:function(t){return!(t.y+t.h-1this.y+this.h-1||t.x+t.w-1this.x+this.w-1)},mkImg:function(t){var i=this.g;this.i=i.draw.scale(i.imgs[t],this.scale),this.w=this.i.width/this.frames,this.h=this.i.height,this.iHurt=i.draw.scale(i.imgs[t+"_w"],this.scale)}}),$.State=Class.extend({init:function(t){this.g=t,this.fader=0,t.jump=0,t.lastJump=0,t.doJump=!1},update:function(){var t,i=this.g,s=i.ents.length;for(i.lastJump=i.jump,i.jump=i.mobile?i.input.touchLeft:i.input.k[32],i.doJump=0===i.lastJump&1===i.jump?!0:!1;s--;)i.ents[s]&&!i.ents[s].remove&&i.ents[s].update();for(s=i.ents.length;s--;)i.ents[s].remove&&i.ents.splice(s,1);for(s=i.events.length;s--&&(t=i.events[s]);)t.time-=i.dt/1e3,t.time<0&&(t.cb.call(this),i.events.splice(s,1));this.fader=Math.sin(3*i.tick)+1},render:function(){var t,i=this.g;for(t=0;te;e+=1)h.ents.push(new $.Particle(h,{x:i,y:s}))},this.explosion=function(t,i,s,e,h){for(var n=this.g,a=$.H.rnd;t--;)window.setTimeout(function(){n.ents.push(new $.Explosion(n,{x:i+a(-10,10),y:s+a(-10,10),magnitude:h,particles:e}))},150*t)}},$.Particle=$.Sprite.extend({init:function(t,i){this._super(t,i),this.name="particle",this.scale=1,this.group="na",this.w=4,this.h=4,this.v=5*Math.random()+5,this.lifespan=$.H.rnd(20,50),this.ttl=this.lifespan,this.alpha=1,this.vx=($.H.rnd(0,600)-300)/1e3,this.vy=($.H.rnd(0,600)-300)/1e3},update:function(){this._super(),this.ttl-=1,this.ttl<0&&(this.remove=!0)},render:function(){var t=this.g;t.ctx.globalAlpha=this.ttl/this.lifespan,t.draw.rect(this.x,this.y,5,5,$.cols.zornskin),t.ctx.globalAlpha=1}}),$.Tile=Class.extend({init:function(t,s){for(this.g=t,this.i=s.i,this.x=s.x,this.y=s.y,this.speed=s.speed/1e3,this.w=this.i.width,this.h=this.i.height,this.ctx=s.ctx,this.numTiles=Math.ceil(t.w/this.w)+2,this.tiles=[],i=0;it?this.tiles[i].x:t;return t},render:function(){var t,i;for(t=0;t0&&(i.style.marginTop=s+"px",i.style.marginLeft=s+this.l+"px"))}},$.Bg=Class.extend({init:function(t,i){i=i||{};var s=i.numStars||20,e=t.imgs;sc=t.draw.scale,this.g=t,this.c=$.H.mkCanvas(t.w,t.h),this.ctx=this.c.getContext("2d"),this.draw=new $.Draw(this.ctx,t.w,t.h),this.ground=i.ground||40,this.groundTileImg=i.groundTile||e.ground,this.groundTileScale=i.groundTile||e.ground,this.speed=i.speed,this.tiles=[];var h=sc(e.ground,4);for(this.tiles.push(new $.Tile(this.g,{ctx:this.ctx,x:0,y:280,i:h,speed:-i.speed})),this.tiles.push(new $.Tile(this.g,{ctx:this.ctx,x:0,y:0,i:t.draw.flip(h,0,1),speed:-i.speed})),this.tiles.push(new $.Tile(this.g,{ctx:this.ctx,x:0,y:230,i:t.draw.scale(t.imgs.bg1,10),speed:-(i.speed/1.5)})),this.tiles.push(new $.Tile(this.g,{ctx:this.ctx,x:0,y:41,i:t.draw.scale(t.imgs.window,8),speed:-(i.speed/2)})),this.stars=[];s--;)this.stars.push({x:$.H.rnd(0,t.w),y:$.H.rnd(0,t.h)})},update:function(){for(var t=(this.g,this.tiles.length);t--;)this.tiles[t].update()},render:function(){var t,i=this.g,s=this.stars.length;for(this.ctx.fillStyle=$.cols.black,this.ctx.fillRect(0,0,i.w,i.h);s--;)t=this.stars[s],this.draw.circle(t.x,t.y,1,$.cols.blind);for(s=this.tiles.length;s--;)this.tiles[s].render();i.ctx.drawImage(this.c,0,0)},stop:function(){var t=this.tiles.length;for(this.speed=0;t--;)this.tiles[t].speed=0},makeFloor:function(t){var i,s=10,e=$.H.mkCanvas(10*t.width,t.height),h=e.getContext("2d");for(i=0;s>i;i+=1)h.drawImage(t,t.width*i,0);return e}}),$.Bg1=$.Bg.extend({init:function(t,i){this._super(t,i);var s=20,e=t.imgs;sc=t.draw.scale,this.g=t,this.c=$.H.mkCanvas(t.w,t.h),this.ctx=this.c.getContext("2d"),this.draw=new $.Draw(this.ctx,t.w,t.h),this.ground=40,this.speed=i.speed,this.tiles=[];var h=this.makeFloor(sc(e.ground,4));for(this.tiles.push(new $.Tile(this.g,{ctx:this.ctx,x:0,y:280,i:h,speed:-i.speed})),this.tiles.push(new $.Tile(this.g,{ctx:this.ctx,x:0,y:0,i:t.draw.flip(h,0,1),speed:-i.speed})),this.tiles.push(new $.Tile(this.g,{ctx:this.ctx,x:0,y:230,i:t.draw.scale(t.imgs.bg1,10),speed:-(i.speed/1.5)})),this.tiles.push(new $.Tile(this.g,{ctx:this.ctx,x:0,y:41,i:t.draw.scale(t.imgs.window,8),speed:-(i.speed/2)})),this.stars=[];s--;)this.stars.push({x:$.H.rnd(0,t.w),y:$.H.rnd(0,t.h)})}}),$.Bg2=$.Bg.extend({init:function(t,i){this._super(t,i);var s=40,e=t.imgs;sc=t.draw.scale,this.g=t,this.c=$.H.mkCanvas(t.w,t.h),this.ctx=this.c.getContext("2d"),this.draw=new $.Draw(this.ctx,t.w,t.h),this.ground=40,this.speed=i.speed,this.tiles=[];var h=this.makeFloor(sc(e.ground1,4));for(this.tiles.push(new $.Tile(this.g,{ctx:this.ctx,x:0,y:280,i:h,speed:-i.speed})),this.tiles.push(new $.Tile(this.g,{ctx:this.ctx,x:0,y:0,i:t.draw.flip(h,0,1),speed:-i.speed})),this.tiles.push(new $.Tile(this.g,{ctx:this.ctx,x:0,y:230,i:t.draw.scale(t.imgs.bg2,10),speed:-(i.speed/1.5)})),this.tiles.push(new $.Tile(this.g,{ctx:this.ctx,x:0,y:0,i:t.draw.scale(t.imgs.window,5),speed:-(i.speed/2)})),this.tiles.push(new $.Tile(this.g,{ctx:this.ctx,x:0,y:160,i:t.draw.scale(t.imgs.window,5),speed:-(i.speed/2)})),this.stars=[];s--;)this.stars.push({x:$.H.rnd(0,t.w),y:$.H.rnd(0,t.h)})}}),$.BgTitle=$.Bg.extend({init:function(t,i){this._super(t,i),this.tiles=[],this.tiles.push(new $.Tile(this.g,{ctx:this.ctx,x:0,y:-5,i:t.draw.scale(t.imgs.window,10),speed:-(i.speed/2)}))}}),$.Star=$.Sprite.extend({init:function(t,i){this._super(t,i),this.scale=6,this.group="star",this.speed=-1*t.bg.speed,this.frames=1,this.mkImg("star"),this.vx=t.bg.speed/1e3*-1,this.vy=.05*(this.x>t.w/2?1:-1)},update:function(){this._super(),this.hitGroup("player")},doDamage:function(t){var i=this.g;"player"===t.group&&(this.remove=!0,i.audio.play("powerup"),i.ents.push(new $.Msg(i,{text:this.val,col:"p",x:this.x,y:this.y})))}}),$.Battery=$.Sprite.extend({init:function(t,i){this._super(t,i),this.scale=6,this.group="battery",this.speed=-1*t.bg.speed,this.frames=1,this.mkImg("battery"),this.vx=t.bg.speed/1e3*-1,this.x=480,this.y=120},update:function(){this._super(),this.hitGroup("player")},doDamage:function(t){var i=this.g;"player"===t.group&&(this.remove=!0,i.ents.push(new $.Msg(i,{text:i.p1.doPowerup(),col:"p",x:this.x,y:this.y})))}}),$.Fader=$.Sprite.extend({init:function(t,i){this._super(t,i),this.w=0,this.h=t.h,this.remove=!1,this.col=i.col,this.cb=i.cb,this.dir=i.dir||1},update:function(){var t=this.g;this.tick=t.dt/1.5,this.w+=this.tick,this.w>t.w&&(this.cb&&this.cb(),this.remove=!0)},render:function(){var t=this.g;1===this.dir?(t.draw.rect(0,0,this.w,this.h,this.col),t.draw.rect(t.w,0,-this.w,this.h,this.col)):-1===this.dir&&(t.draw.rect(0,0,t.w-this.w,this.h,this.col),t.draw.rect(t.w,0,-(t.w-this.w),this.h,this.col))}}),$.Explosion=$.Sprite.extend({init:function(t,i){this._super(t,i),this.name="explosion",this.scale=1,this.group="na",this.startX=i.x,this.startY=i.y,this.particles=i.particles||3,this.magnitude=i.magnitude||9,this.factor=1,this.mkImg("circle"),t.emitter.particle(this.particles,this.x,this.y),t.audio.play("explode"),this.angle=0,this.grow=1},update:function(){this.g;this._super(),this.scale<=this.magnitude&&(this.scale+=this.factor),this.scale===this.magnitude&&(this.factor*=-1),this.scale<=1&&(this.remove=!0),this.mkImg("circle")},render:function(){var t=this.startX-this.w/2,i=this.startY-this.h/2,s=this.g,e=this.i;s.ctx.drawImage(e,t,i)}}),$.Msg=$.Sprite.extend({init:function(t,i){this._super(t,i),this.vy=-1,this.lifespan=2,this.ttl=this.lifespan,this.w=10,this.h=10,this.col=i.col||"w",this.f=t.mkFont(this.col,3),this.vx=-.09,this.vy=-.05},update:function(){var t=this.g;this._super(),this.x+=this.vx*t.dt,this.y+=this.vy*t.dt,this.ttl-=t.dt/1e3,this.ttl<0&&this.kill()},render:function(){var t=this.g;t.ctx.globalAlpha=this.ttl/this.lifespan,t.draw.text(this.text,this.f,this.x,this.y),t.ctx.globalAlpha=1}}),$.Robo=$.Sprite.extend({init:function(t,i){this._super(t,i),this.speed=1,this.group="player",this.frames=2,this.scale=4,this.mkImg("p1"),this.angle=0,this.speed=5,this.vy=this.speed,this.gravity=.001,this.jump=-.4,this.jumping=!1,this.flipping=!1,this.flipped=!1,this.jumpCount=0,this.jumps=0,this.flips=0,this.pause=!1,this.powerup=0,this.gun=1},update:function(){{var t=this.g;t.input.k}this.pause||(this.hitGroup("baddies"),t.doJump&&this.jumpCount<2&&this.tick>2&&(this.jumpCount+=1,this.jumping?this.jumping&&(t.audio.play("flip"),this.flips+=1,this.flipping=!0,this.gravity*=-1,this.jump*=-1,this.flipped=!this.flipped):(t.audio.play("jump"),this.jumps+=1,this.jumping=!0,this.vy=this.jump)),this._super(),this.keepOnScreen(),this.x>450&&(this.frame=1))},render:function(){this.pause||this._super()},doDamage:function(t){"baddies"===t.group&&this.kill()},receiveDamage:function(t){"baddies"===t.group&&this.kill()},kill:function(){{var t=this.g;$.H.rnd}this.dead=!0,this.remove=!0,t.bg.stop(),t.ents.push(new $.Explosion(t,{x:this.x,y:this.y})),t.shake.start(50,50)},doPowerup:function(){var t=this.g;return this.powerup+=1,this.powerup>3?(t.score+=100,100):1===this.powerup?(t.bulletInterval=100,"RAPID FIRE"):2===this.powerup?"DOUBLE SHOT":3===this.powerup?"TRIPLE SHOT":void 0}}),$.Floater=$.Sprite.extend({init:function(t,i){this._super(t,i),this.scale=$.H.rnd(2,5),this.health=10*this.scale,this.group="baddies",this.speed=1,this.frames=2,this.frameChange=10,this.mkImg("floater"),this.frameRate=120,this.x=$.H.rnd(t.w/2-this.w/2),this.y=$.H.rnd(t.h/2-this.h/2),this.setDir()},update:function(){var t=this.g;this.lastX=this.x,this.lastY=this.y,this.gravity&&(this.vy+=this.gravity),this.x+=this.vx*t.dt,this.y+=this.vy*t.dt,this.atEdget=this.keepOnScreen(),this.atEdget&&this.setDir(),this.cx=this.x+this.w/2,this.cy=this.y+this.y/2,this.tick+=1,this.frameNext<0&&(this.frameNext=this.frameRate,this.frame=this.frame===this.frames?1:this.frame+=1),this.frameNext-=t.dt,this.hurt-=t.dt},render:function(){var t=this.g,i=this.i;this.hurt>0&&(i=this.iHurt),this.flipX&&(i=t.draw.flip(i,1,0)),t.ctx.drawImage(i,this.frame*this.w-this.w,0,this.w,this.h,~~this.x,~~this.y,this.w,this.h)},receiveDamage:function(){var t=this.g;this.hurt=this.hurtTime,t.emitter.particle(2,this.x,this.y),this.health-=10,this.health<0&&(t.ents.push(new $.Msg(t,{text:10*this.scale,x:this.x,y:this.y})),t.ios||(t.shake.start(5*this.scale,5*this.scale),t.ents.push(new $.Explosion(t,{x:this.x,y:this.y}))),this.remove=!0)},kill:function(){},setDir:function(){this.vx=($.H.rnd(0,200)-100)/1e3,this.vy=($.H.rnd(0,200)-100)/1e3,this.flipX=this.vx>0?!0:!1}}),$.Crate=$.Sprite.extend({init:function(t,i){this._super(t,i),this.scale=4,this.health=10*this.scale,this.group="baddies",this.speed=-1*t.bg.speed,this.frames=1,this.mkImg("crate"),this.iHurt=t.draw.scale(t.imgs.crate_w,this.scale),this.frameRate=120,this.hurt=0,this.hurtTime=200,this.x=500,this.y=i.y||t.h-t.bg.ground-this.h,this.vx=t.bg.speed/1e3*-1},update:function(){var t=this.g;this.lastX=this.x,this.lastY=this.y,0!==t.bg.speed&&(this.x+=this.vx*t.dt),this.x<-this.w&&(this.x=2*t.w+$.H.rnd(0,t.w)),this.frameNext-=t.dt,this.hurt-=t.dt},render:function(){var t=this.g,i=this.i;this.hurt>0&&(i=this.iHurt),t.ctx.drawImage(i,this.frame*this.w-this.w,0,this.w,this.h,~~this.x,~~this.y,this.w,this.h)},receiveDamage:function(){var t=this.g;this.hurt=this.hurtTime,t.emitter.particle(2,this.x,this.y),this.health-=0,this.health<0&&(t.shake.start(5*this.scale,5*this.scale),t.ents.push(new $.Explosion(t,{x:this.x,y:this.y})),this.remove=!0)},kill:function(){}}),$.Spark=$.Sprite.extend({init:function(t,i){this._super(t,i),this.scale=4,this.health=10*this.scale,this.group="baddies",this.speed=-1*t.bg.speed,this.frames=2,this.mkImg("spark"),this.iHurt=t.draw.scale(t.imgs.spark_w,this.scale),this.hurt=0,this.hurtTime=200,this.frameRate=100,this.x=500,this.vx=t.bg.speed/1e3*-1},update:function(){var t=this.g;this._super(),this.x<-this.w&&(this.x=2*t.w)},render:function(){var t=this.g,i=this.i;this.hurt>0&&(i=this.iHurt),t.ctx.drawImage(i,this.frame*this.w-this.w,0,this.w,this.h,~~this.x,~~this.y,this.w,this.h)},receiveDamage:function(){var t=this.g;t.emitter.particle(2,this.x,this.y),this.health-=0,this.health<0&&(t.shake.start(5*this.scale,5*this.scale),t.ents.push(new $.Explosion(t,{x:this.x,y:this.y})),this.remove=!0)},kill:function(){}}),$.Drone=$.Sprite.extend({init:function(t,i){this._super(t,i),this.scale=4,this.health=5,this.group="baddies",this.speed=t.bg.speed/4*-1,this.frames=2,this.t=0,this.m=$.data.moves[i.m],this.scale=this.m.scale||4,this.flipped=this.m.flipped||0,this.mkImg(this.m.img||"drone"),this.x=i.x||this.m.sx,this.y=i.y||this.m.sy,this.t=0},update:function(){var t=this.g,i=this.m;this.t+=t.dt/1e3,this.lastX=this.x,this.lastY=this.y,this.vx=i.A+i.B*Math.sin(i.C*this.t+i.D),this.vy=i.E+i.F*Math.sin(i.G*this.t+i.H),this.x+=this.vx*(t.dt/1e3),this.y+=this.vy*(t.dt/1e3),this.x<-50&&(this.remove=!0),this.frameNext<0&&(this.frameNext=this.frameRate,this.frame=this.frame===this.frames?1:this.frame+=1),this.frameNext-=t.dt,this.hurt-=t.dt},receiveDamage:function(){var t=this.g;this.hurt=this.hurtTime,t.emitter.particle(2,this.x,this.y),this.health-=10,this.health<0&&(this.remove=!0,t.score+=10*this.scale,t.ents.push(new $.Explosion(t,{x:this.x,y:this.y})),t.ents.push(new $.Msg(t,{text:10*this.scale,x:this.x,y:this.y})),t.waves[this.waveId]-=1,t.ios||t.shake.start(5*this.scale,5*this.scale),0===t.waves[this.waveId]&&(t.score+=30*this.scale,t.ents.push(new $.Star(t,{val:30*this.scale,x:this.x,y:this.y}))))},kill:function(){}}),$.Bullet=$.Sprite.extend({init:function(t,i){this._super(t,i),this.w=i.size||8,this.h=i.size||8,this.name="bullet",this.speed=.6,this.ttl=3e3,this.group="bullets",this.scale=2,this.vx=this.speed*Math.cos(this.angle),this.vy=this.speed*Math.sin(this.angle),this.col=$.cols.zornskin,t.audio.play("shoot")},update:function(){this._super(),this.hitGroup("baddies"),this.offscreen&&this.kill(),this.ttl-=this.tick,this.ttl<0&&(this.remove=!0)},render:function(){var t=this.g,i=t.draw.scale(t.imgs.circle_w,2);this._super(),this.tick<.3&&t.ctx.drawImage(i,~~this.x,~~this.y)},keepOnScreen:function(){var t=this.g;this.x<0?this.vx*=-1:this.x>t.w-this.w&&(this.vx*=-1),this.y<0?this.vy*=-1:this.y>t.h-this.h&&(this.vy*=-1)}}),$.Portal=$.Sprite.extend({init:function(t,i){this._super(t,i),this.scale=4,this.health=10*this.scale,this.group="portal",this.speed=0,this.h=50,this.w=20,this.hide()},update:function(){this._super(),this.hitGroup("player")},show:function(){var t=this.g;this.x=t.w-this.w,this.y=t.h/2-this.h/2,this.col=$.cols.slimegreen},hide:function(){var t=this.g;this.x=-2*t.w,this.active=!1},doDamage:function(t){var i=this.g,s=this;"player"===t.group&&(this.active=!0,this.col=$.cols.pigmeat,i.addEvent({time:.001,cb:function(){i.score+=100,i.ents.push(new $.Msg(i,{text:"BONUS",x:s.x,y:s.y}))}}))}}),$.Splash=$.State.extend({init:function(t){this._super(t),this.h1=t.mkFont("g",6),this.p=t.mkFont("w",2),this.fade=0,this.skull=t.draw.scale(t.imgs.skull_w,6),$.fullScreen=function(){$.H.fullScreen(t.c)},window.addEventListener("click",$.fullScreen),window.addEventListener("keydown",$.fullScreen),window.addEventListener("touchstart",$.fullScreen)},update:function(){var t=this.g;this._super(),this.fade+=.05*t.dt/100,this.fade>2&&(t.audio.play("powerup"),t.changeState("Title"))},render:function(){{var t=this.g,i=this;$.cols}t.draw.rect(0,0,t.w,t.h,$.cols.black),t.ctx.globalAlpha=this.fade,t.draw.text("EOINMCG PRESENTS",this.p,!1,100),t.ctx.globalAlpha=this.fade/10,t.ctx.drawImage(this.skull,220,150),t.ctx.globalAlpha=1,i._super()}}),$.Title=$.State.extend({init:function(t){this._super(t),this.startText=t.mobile?"TAP LEFT TO START":"PRESS SPACE",this.h1=t.mkFont("g",6),this.p=t.mkFont("w",2),0===t.plays&&t.audio.say($.data.title),this.bg=new $.BgTitle(t,{speed:500,numStars:0}),this.hideText=!1},update:function(){var t=this.g;this._super(),this.bg.update(),t.doJump&&t.tick>.4&&(this.hideText=!0,t.ents.push(new $.Fader(t,{col:$.cols.black,cb:function(){t.changeState(t.plays<1?"Tutorial":"Play")}})))},render:function(){{var t=this.g,i=this;$.cols}this.bg.render(),i._super(),this.hideText||(t.draw.text("HI",this.p,40,40),t.draw.text(t.hiScore,this.p,60,40),t.ctx.globalAlpha=this.fader,t.draw.text(this.startText,this.p,!1,250),t.ctx.globalAlpha=1,t.draw.text($.data.title,this.h1,150,85))}}),$.Play=$.State.extend({init:function(t){this._super(t),this.h1=t.mkFont("g",5),this.p=t.mkFont("w",3),t.newHi=!1,t.plays+=1,t.score=0,this.levelNum=0,t.waves=[],this.p1=new $.Robo(t,{x:60,y:200}),t.p1=this.p1,t.ents.push(this.p1),this.portal=new $.Portal(t,{}),t.ents.push(this.portal),this.gameover=!1,t.state=this,this.bulletDelay=0,t.bulletInterval=200,t.audio.play("alarm"),this.nextLevel()},update:function(){var t=this.g,i=this,s=t.input,e=s.k;if(this._super(),this.bg.update(),(s.touchRight||e[16])&&this.bulletDelay<0&&!i.p1.dead&&!i.p1.pause&&(this.bulletDelay=t.bulletInterval,t.ents.push(new $.Bullet(t,{x:this.p1.x+this.p1.w/2,y:this.p1.y+this.p1.h/3,angle:0})),t.p1.powerup>1&&t.ents.push(new $.Bullet(t,{x:this.p1.x+this.p1.w/2,y:this.p1.y+this.p1.h/3,angle:-.3})),t.p1.powerup>2&&t.ents.push(new $.Bullet(t,{x:this.p1.x+this.p1.w/2,y:this.p1.y+this.p1.h/3,angle:.3}))),this.bulletDelay-=t.dt,t.p1.dead&&!this.gameover){if(this.gameover=!0,t.score>t.hiScore){t.newHi=!0,t.hiScore=t.score;try{localStorage.setItem("hiScore",t.score)}catch(h){}}t.ents.push(new $.Fader(t,{col:$.cols.bloodred,cb:function(){t.changeState("Gameover")}}))}this.gameover&&t.doJump&&t.changeState("Title"),t.distance-=t.dt,t.distance<0&&(t.p1.vx=.5,this.portal.show(),t.bg.stop()),t.p1.x>450&&(this.portal.active=!0),t.distance<-2e3&&!this.gameover&&(this.portal.active=!0),this.portal.active&&this.levelUp()},render:function(){{var t=this.g,i=this;t.ents.length,$.cols}this.bg.render(),i._super(),this.gameover||t.draw.text(t.score,this.p,40,40)},levelUp:function(){var t=this.g,i=this,s=t.ents.length;if(!t.p1.pause){for(t.audio.play("levelup"),t.p1.pause=!0;s--;)"baddies"===t.ents[s].group&&(t.ents[s].remove=!0);for(s=t.ents.length;s--;)t.ents[s].remove&&t.ents.splice(s,1);t.events=[],i.p1.x=t.w/3,i.portal.hide(),t.ents.push(new $.Fader(t,{col:$.cols.nightblue,cb:function(){t.ents.push(new $.Fader(t,{col:$.cols.nightblue,cb:function(){t.state.nextLevel(),t.p1.pause=!1},dir:-1}))}}))}},nextLevel:function(){var t,i,s=this.g,e=this;this.levelNum>$.L.length-1&&(this.levelNum=0),this.level=$.L[this.levelNum],t=this.level,"all"===t.baddies[0]&&(t.baddies=Object.keys($.data.moves)),this.bg=new $[t.bg](s,t.bgSettings),t.powerup&&s.addEvent({time:t.powerup,cb:function(){s.ents.push(new $.Battery(s,{}))}}),s.addEvent({time:2,cb:function(){for(e.spawnWave(),i=0;i1||t.tick>5)&&(this.hideText=!0,t.ents.push(new $.Fader(t,{col:$.cols.nightblue,cb:function(){t.changeState("Play")}})))},render:function(){var t=this.g;this.bg.render(),this._super(),this.hideText||(t.draw.text("HOW TO PLAY",this.h1,!1,50),t.draw.text("SHOOT",this.p,130,130),t.draw.text(this.shootKey,this.p2,230,130),t.draw.text("JUMP",this.p,130,160),t.draw.text(this.jumpKey,this.p2,230,160),t.ctx.globalAlpha=this.fader,t.draw.text("DOUBLE JUMP REVERSES GRAVITY",this.p2,!1,210),t.ctx.globalAlpha=1)}}),$.Gameover=$.State.extend({init:function(t){this._super(t),this.h1=t.mkFont("b",7),this.h2=t.mkFont("g",5),this.p=t.mkFont("w",2),this.skull=t.newHi?t.draw.scale(t.imgs.star_w,27):t.draw.scale(t.imgs.skull_w,27),t.audio.play("die")},update:function(){var t=this.g;this._super(),t.doJump&&t.tick>1&&t.ents.push(new $.Fader(t,{col:$.cols.pigmeat,dir:-1,cb:function(){t.changeState("Title")}}))},render:function(){var t=this.g;this._super(),t.draw.rect(0,0,t.w,t.h,$.cols.bloodred),t.draw.text("GAME OVER",this.h1,!1,70),t.ctx.globalAlpha=.05,t.ctx.drawImage(this.skull,220,100+20*this.fader),t.ctx.globalAlpha=1,t.newHi&&(t.ctx.globalAlpha=this.fader,t.draw.text("NEW HISCORE",this.h2,!1,170),t.ctx.globalAlpha=1)}}),$.L=[{distance:1e4,bg:"Bg1",bgSettings:{speed:400},baddies:[],waveSize:3,interval:3,init:[["Crate",{}],["Crate",{y:40}]]},{distance:1e4,bg:"Bg1", 2 | bgSettings:{speed:400},baddies:["straightBot"],waveSize:3,interval:3,init:[["Crate",{}],["Crate",{y:40}]]},{distance:1e4,bg:"Bg1",powerup:1,bgSettings:{speed:400},baddies:["sway","straightTop"],waveSize:4,interval:3,init:[["Crate",{}]]},{distance:1e4,bg:"Bg1",powerup:3,bgSettings:{speed:400},baddies:["topbot","bottop"],waveSize:5,interval:3,init:[["Spark",{y:150}]]},{distance:2e4,bg:"Bg2",powerup:4,bgSettings:{speed:400},baddies:["topbot","bottop","circle"],waveSize:5,interval:2,init:[["Crate",{}],["Crate",{y:40}]]},{distance:2e4,bg:"Bg2",powerup:3,bgSettings:{speed:600},baddies:["all"],waveSize:5,interval:2,init:[["Crate",{}],["Crate",{y:40}]]}],$.data={title:"ROBO FLIP",i:{circle:"R0lGODlhBgAGAKEDAL4mM+uJMffia////yH5BAEKAAMALAAAAAAGAAYAAAIN3AB2EQkhRHuxLWuQKQA7",floater:"R0lGODlhDgAIAKEDAL4mMzGi8vfia////yH+EUNyZWF0ZWQgd2l0aCBHSU1QACH5BAEKAAMALAAAAAAOAAgAAAIdXIZnuOEPIQBxVirxE4MLJ4ShwyRHhHiGUppPyxQAOw==",spark:"R0lGODlhDgAHAKECAOuJMffia////////yH+EUNyZWF0ZWQgd2l0aCBHSU1QACH5BAEKAAMALAAAAAAOAAcAAAIYxGZ4u+acWohoHgCCmE7b+4HRQ43XyRwFADs=",p1:"R0lGODlhDAAIAMIFAAUDC74mMzWADJrKH/3//P///////////yH5BAEKAAcALAAAAAAMAAgAAAMfeLonMkM5KF9sVgUS9tkdgVFj5GRlRU4RsLiHCzNKAgA7",crawler:"R0lGODlhEAAIAKECAL4mM/fia////////yH5BAEKAAIALAAAAAAQAAgAAAIZlI8Skba4WIoIAHnsc/TE/WHhFJYmZSpjAQA7",drone:"R0lGODlhEAAIAKEDAL4mM+Bvi/fia////yH5BAEKAAMALAAAAAAQAAgAAAIenBOmu4j8VBAuHSernbXhoSXASJYmMJyGurYKmhoFADs=",star:"R0lGODlhBQAFAIABAPfia////yH5BAEKAAEALAAAAAAFAAUAAAIITGCGB43OWAEAOw==",battery:"R0lGODlhAwAFAKECAESJGqPOJ////////yH5BAEKAAIALAAAAAADAAUAAAIGFAwBuZoFADs=",font:"R0lGODlhjwAFAIABAAAAAMwAACH5BAEKAAEALAAAAACPAAUAAAJ0DGKHcLzOFDRJ0UbXzdJ2lFQbRo5ipJ1TA7XsW2KanNWyZXpuzuNSz5txQDZTChSrsI6kHQpVu/wer9GvWuw5ssMp1LmbuZKeDdN4NVqT1BAydWvHi14ityTUSZHLE3El0uWHN/Vg9WYoOPe01YEl9VgVUQAAOw==",window:"R0lGODlhIAAiAKECABsmMi9ITv///////yH5BAEKAAMALAAAAAAgACIAAAJZDI6py2gNo5O0PTCy3rzviXnimB0GiXpmmLbD6rpwnM40ad9irnd8/wGcgCohixgcIpPH5cvoZEY1P2SVeAVme1td9/alhWNjGXT6VEZXTSy7An/HK5c5pQAAOw==",ground:"R0lGODlhCgAKAKECAABXhDGi8v///////yH5BAEKAAIALAAAAAAKAAoAAAIQjI+gyxztoIRvWlBlVqiHAgA7",ground1:"R0lGODlhCgAKAKEBABsmMi9ITi9ITi9ITiH5BAEKAAIALAAAAAAKAAoAAAIPhI8RoMoNo5y02vucQ7wAADs=",bg1:"R0lGODlhFAAFAIABAC9ITv///yH5BAEKAAEALAAAAAAUAAUAAAIRjI+pG+CMXnNSPTpxzZzeWgAAOw==",bg2:"R0lGODlhFAAFAIAAAC9ITi9ITiH5BAEKAAEALAAAAAAUAAUAAAIPjI+pBr0fmoRpTnpAxqYAADs=",skull:"R0lGODlhCAAIAIABAAAAAP///yH5BAEKAAEALAAAAAAIAAgAAAIOTIBoyc27nDuKJnMyMAUAOw==",crate:"R0lGODlhCgAKAKECAC9ITp2dnf///////yH5BAEKAAMALAAAAAAKAAoAAAIZXI5nAd0JEkMRiTotxuHSLoSc80hmsJhDAQA7"},sfx:{jump:[0,,.2432,,.1709,.3046,,.1919,,,,,,.5923,,,,,1,,,,,.5],flip:[0,.001,.2379,.1592,.0225,.85,,.0659,.0917,,-.6595,-.2759,.7809,.0597,.0205,.3604,-.0083,-.5261,.3385,-3e-4,.0833,,.6489,.5],powerup:[0,,.0129,.5211,.4714,.4234,,,,,,.4355,.5108,,,,,,1,,,,,.5],shoot:[2,,.1128,,.178,.7748,.0046,-.4528,,,,,,.185,.0994,,,,1,,,,,.5],explode:[3,,.3708,.5822,.3851,.0584,,-.0268,,,,-.0749,.7624,,,,,,1,,,,,.5],levelup:[1,.115,.2886,.4061,.6535,.0666,,.3295,.0262,-.0114,,.2484,.4319,.7129,-.7396,,,-.906,.9658,.1462,.6577,.0129,.0448,.56],die:[2,.1987,.388,.4366,.0335,.5072,,.1128,-.1656,.1987,,-.376,.2686,-.684,.1392,-.6819,-.8117,-.1072,.9846,.057,,.004,-.0045,.56],alarm:[1,.0241,.9846,.6067,.3041,.1838,,.0565,.1439,-.3068,.1402,.0867,.7339,.1332,-.3119,-.3257,.2875,-.0014,.5866,.0086,-.9675,.3643,,.5]},moves:{straightTop:{sx:500,sy:42,scale:4,img:"crawler",flipped:1,A:-200,B:0,C:0,D:0,E:0,F:0,G:0,H:0},straightBot:{sx:500,sy:247,scale:4,img:"crawler",A:-200,B:0,C:0,D:0,E:0,F:0,G:0,H:0},topbot:{sx:500,sy:50,scale:3,img:"floater",A:-200,B:0,C:0,D:0,E:80,F:0,G:0,H:0},bottop:{sx:500,sy:230,scale:3,img:"floater",A:-200,B:0,C:0,D:0,E:-80,F:0,G:0,H:0},sway:{sx:500,sy:60,scale:4,img:"drone",A:-100,B:0,C:0,D:0,E:0,F:360,G:4,H:0},circle:{sx:500,sy:160,scale:5,img:"drone",A:-150,B:-100,C:5,D:0,E:50,F:200,G:10,H:Math.PI/2}}},window.addEventListener("load",function(){var t=new $.Game;t.boot("Splash")},!1); -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | Loading

Loading

-------------------------------------------------------------------------------- /g.min.js: -------------------------------------------------------------------------------- 1 | function SfxrParams(){this.setSettings=function(a){for(var b=0;b<24;b++){this[String.fromCharCode(97+b)]=a[b]||0}if(this["c"]<0.01){this["c"]=0.01}var c=this["b"]+this["c"]+this["e"];if(c<0.18){var d=0.18/c;this["b"]*=d;this["c"]*=d;this["e"]*=d}}}function SfxrSynth(){this._params=new SfxrParams();var k,j,h,b,e,f,d,g,c,m,a,l;this.reset=function(){var n=this._params;b=100/(n.f*n.f+0.001);e=100/(n.g*n.g+0.001);f=1-n.h*n.h*n.h*0.01;d=-n.i*n.i*n.i*0.000001;if(!n.a){a=0.5-n.n/2;l=-n.o*0.00005}g=1+n.l*n.l*(n.l>0?-0.9:10);c=0;m=n.m==1?0:(1-n.m)*(1-n.m)*20000+32};this.totalReset=function(){this.reset();var n=this._params;k=n.b*n.b*100000;j=n.c*n.c*100000;h=n.e*n.e*100000+12;return((k+j+h)/3|0)*3};this.synthWave=function(E,B){var W=this._params;var X=W.s!=1||W.v,o=W.v*W.v*0.1,C=1+W.w*0.0003,ah=W.s*W.s*W.s*0.1,T=1+W.t*0.0001,S=W.s!=1,z=W.x*W.x,M=W.g,ag=W.q||W.r,K=W.r*W.r*W.r*0.2,ae=W.q*W.q*(W.q<0?-1020:1020),Z=W.p?((1-W.p)*(1-W.p)*20000|0)+32:0,N=W.d,U=W.j/2,R=W.k*W.k*0.01,t=W.a;var s=k,I=1/k,G=1/j,F=1/h;var r=5/(1+W.u*W.u*20)*(0.01+ah);if(r>0.8){r=0.8}r=1-r;var ac=false,ai=0,u=0,D=0,H=0,ad=0,L,x=0,af,y=0,V,Q=0,O,w=0,J,A,q=0;var P=new Array(1024),v=new Array(32);for(var ab=P.length;ab--;){P[ab]=0}for(var ab=v.length;ab--;){v[ab]=Math.random()*2-1}for(var ab=0;ab=Z){w=0;this.reset()}}if(m){if(++c>=m){m=0;b*=g}}f+=d;b*=f;if(b>e){b=e;if(M>0){ac=true}}af=b;if(U>0){q+=R;af*=1+Math.sin(q)*U}af|=0;if(af<8){af=8}if(!t){a+=l;if(a<0){a=0}else{if(a>0.5){a=0.5}}}if(++u>s){u=0;switch(++ai){case 1:s=j;break;case 2:s=h}}switch(ai){case 0:D=u*I;break;case 1:D=1+(1-u*G)*2*N;break;case 2:D=1-u*F;break;case 3:D=0;ac=true}if(ag){ae+=K;V=ae|0;if(V<0){V=-V}else{if(V>1023){V=1023}}}if(X&&C){o*=C;if(o<0.00001){o=0.00001}else{if(o>0.1){o=0.1}}}A=0;for(var aa=8;aa--;){y++;if(y>=af){y%=af;if(t==3){for(var Y=v.length;Y--;){v[Y]=Math.random()*2-1}}}switch(t){case 0:J=((y/af)0.5?O-1:O)*6.28318531;J=1.27323954*O+0.405284735*O*O*(O<0?1:-1);J=0.225*((J<0?-1:1)*J*J-J)+J;break;case 3:J=v[Math.abs(y*32/af|0)]}if(X){L=x;ah*=T;if(ah<0){ah=0}else{if(ah>0.1){ah=0.1}}if(S){ad+=(J-x)*ah;ad*=r}else{x=J;ad=0}x+=ad;H+=x-L;H*=1-o;J=H}if(ag){P[Q%1024]=J;J+=P[(Q-V+1024)%1024];Q++}A+=J}A*=0.125*D*z;E[ab]=A>=1?32767:A<=-1?-32768:A*32767|0}return B}}var synth=new SfxrSynth();window.jsfxr=function(d){synth._params.setSettings(d);var h=synth.totalReset();var f=new Uint8Array(((h+1)/2|0)*4+44);var k=synth.synthWave(new Uint16Array(f.buffer,44),h)*2;var b=new Uint32Array(f.buffer,0,44);b[0]=1179011410;b[1]=k+36;b[2]=1163280727;b[3]=544501094;b[4]=16;b[5]=65537;b[6]=44100;b[7]=88200;b[8]=1048578;b[9]=1635017060;b[10]=k;k+=44;var g=0,e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",c="data:audio/wav;base64,";for(;g>18]+e[j>>12&63]+e[j>>6&63]+e[j&63]}return c};var $={v:0.1,cols:{black:"#000",ash:"#9d9d9d",blind:"#fff",bloodred:"#be2633",pigmeat:"#e06f8b",oldpoop:"#493C2B",newpoop:"#a46422",blaze:"#eb8931",zornskin:"#f7e26b",shadegreen:"#2f484e",leafgreen:"#44891A",slimegreen:"#A3CE27",nightblue:"#1B2632",seablue:"#005784",skyblue:"#31A2F2",cloudblue:"#B2DCEF"}};var initializing=false,fnTest=/xyz/.test(function(){xyz})?/\b_super\b/:/.*/;var lastClassId=0;Class=function(){};var inject=function(d){var c=this.prototype;var b={};for(var a in d){if(typeof(d[a])=="function"&&typeof(c[a])=="function"&&fnTest.test(d[a])){b[a]=c[a];c[a]=(function(e,f){return function(){var h=this._super;this._super=b[e];var g=f.apply(this,arguments);this._super=h;return g}})(a,d[a])}else{c[a]=d[a]}}};Class.extend=function(e){var d=this.prototype;initializing=true;var c=new this();initializing=false;for(var b in e){if(typeof(e[b])=="function"&&typeof(d[b])=="function"&&fnTest.test(e[b])){c[b]=(function(f,g){return function(){var j=this._super;this._super=d[f];var h=g.apply(this,arguments);this._super=j;return h}})(b,e[b])}else{c[b]=e[b]}}function a(){if(!initializing){if(this.staticInstantiate){var g=this.staticInstantiate.apply(this,arguments);if(g){return g}}for(var f in this){if(typeof(this[f])=="object"){this[f]=ig.copy(this[f])}}if(this.init){this.init.apply(this,arguments)}}return this}a.prototype=c;a.prototype.constructor=a;a.extend=window.Class.extend;a.inject=inject;a.classId=c.classId=++lastClassId;return a};$.Load=function(a){this.g=a;this.imgsLoaded=0;this.imgsTotal=Object.keys($.data.i).length;this.init=function(){var d=this.g,b="data:image/gif;base64,",c=$.data.i,e;for(e in c){if(c.hasOwnProperty(e)){d.imgs[e]=new Image();d.imgs[e].onload=this.checkLoaded();d.imgs[e].src=b+c[e]}}};this.checkLoaded=function(){var c=this.g,b=this,d;this.imgsLoaded+=1;if(b.imgsLoaded===b.imgsTotal){window.setTimeout(function(){b.mkFonts()},10)}};this.mkFonts=function(){var c=this.g,e={b:[0,0,0],w:[255,255,255],g:[163,206,39],p:[224,111,139]},b=c.imgs,d;for(d in e){c.fonts[d]=$.H.resize(c.imgs.font,1,e[d])}for(d in b){b[d+"_w"]=$.H.resize(b[d],1,[255,255,255])}window.setTimeout(function(){c.init()},10)};this.init()};$.Game=function(){this.w=480;this.h=320;this.bgs={};this.imgs={};this.fonts={};this.ents=[];this.events=[];this.sfx={};this.plays=0;this.time=0;this.dt=0;this.fps=0;this.hiScore=parseInt(localStorage.getItem("hiScore"),10)||100;this.boot=function(b){var a=this;this.startState=b||"Title";this.c=document.getElementsByTagName("canvas")[0];this.ctx=this.c.getContext("2d");this.c.style.width=this.w+"px";this.c.style.height=this.h+"px";this.draw=new $.Draw(this.ctx,this.w,this.h);this.load=new $.Load(this);document.title=$.data.title};this.init=function(){var b=this,a=navigator.userAgent.toLowerCase();b.mobile="createTouch" in document||false;b.mobile="createTouch" in document||false;b.android=a.indexOf("android")>-1;b.ios=/ipad|iphone|ipod/.test(a);b.firefox=a.indexOf("firefox")>-1;b.input=new $.Input();b.input.init(b);b.emitter=new $.Emitter(b);if(this.ios){this.audio={play:function(){},say:function(){}}}else{this.audio=new $.Audio($.data.sfx);this.audio.init()}$.H.el("b").style.display="block";$.H.el("l").style.display="none";b.resize();$.H.listen("resize",function(){b.resize()});$.H.listen("orientationchange",function(){b.resize()});b.shake=new $.Shake(b);b.changeState(b.startState);function c(){b.loop();requestAnimationFrame(c,b.c)}c()};this.changeState=function(b){var a=this;a.ents=[];a.events=[];a.tick=0;a.state=new $[b](a)};this.addEvent=function(a){this.events.push(a)};this.resize=function(){var e=window.innerHeight,b=window.innerWidth,c=this.w/this.h,a=e*c,d=~~(b-a)/2;scale=a/this.w;if(b-1){r.drawImage(l,e,0,p,k,~~o,~~n,p,k)}o+=p+m}}};$.Audio=function(a){this.sounds=a;this.sfx={};this.init=function(){var b;for(b in this.sounds){this.add(b,10,$.data.sfx[b])}};this.add=function(d,f,e){var c;this.sfx[d]={tick:0,pool:[]};for(c=0;c1){break}j=~~((e[b].pageX-f)/a);h=~~((e[b].pageY-d)/a);if(j(b.w-this.w)){this.x=b.w-this.w;c=true}}if(this.y(b.h-a)-this.h){this.y=b.h-this.h-a;this.jumping=false;this.flipping=false;this.jumpCount=0;c=true}}return c},checkOffScreen:function(){var a=this.g;return(this.x<0||this.x>(a.w-this.w)||this.y<0||this.y>(a.h-this.h))},doDamage:function(a){this.remove=true},kill:function(){this.dead=true;this.remove=true},receiveDamage:function(a){this.kill()},hitGroup:function(c){var b=this.g,a=b.ents.length;while(a--){if(b.ents[a]&&b.ents[a].group===c&&b.ents[a].id!==this.id&&this.hit(b.ents[a])){this.doDamage(b.ents[a]);b.ents[a].receiveDamage(this)}}},hit:function(a){return !((a.y+a.h-1this.y+this.h-1)||(a.x+a.w-1this.x+this.w-1))},mkImg:function(a){var b=this.g;this.i=b.draw.scale(b.imgs[a],this.scale);this.w=(this.i.width/this.frames);this.h=this.i.height;this.iHurt=b.draw.scale(b.imgs[a+"_w"],this.scale)}});$.State=Class.extend({init:function(a){this.g=a;this.fader=0;a.jump=0;a.lastJump=0;a.doJump=false},update:function(){var b=this.g,a=b.ents.length,c;b.lastJump=b.jump;b.jump=(b.mobile)?b.input.touchLeft:b.input.k[32];b.doJump=(b.lastJump===0&b.jump===1)?true:false;while(a--){if(b.ents[a]&&!b.ents[a].remove){b.ents[a].update()}}a=b.ents.length;while(a--){if(b.ents[a].remove){b.ents.splice(a,1)}}a=b.events.length;while(a--){c=b.events[a];if(!c){break}c.time-=(b.dt/1000);if(c.time<0){c.cb.call(this);b.events.splice(a,1)}}this.fader=Math.sin(b.tick*3)+1},render:function(){var c=this.g,b,a,d;for(b=0;bb)?this.tiles[a].x:b}return b},render:function(){var b,a;for(b=0;b0){e.style.marginTop=b+"px";e.style.marginLeft=(b+this.l)+"px"}}}};$.Bg=Class.extend({init:function(d,e){e=e||{};var a=e.numStars||20,b=d.imgs;sc=d.draw.scale;this.g=d;this.c=$.H.mkCanvas(d.w,d.h);this.ctx=this.c.getContext("2d");this.draw=new $.Draw(this.ctx,d.w,d.h);this.ground=e.ground||40;this.groundTileImg=e.groundTile||b.ground;this.groundTileScale=e.groundTile||b.ground;this.speed=e.speed;this.tiles=[];var c=sc(b.ground,4);this.tiles.push(new $.Tile(this.g,{ctx:this.ctx,x:0,y:280,i:c,speed:-e.speed}));this.tiles.push(new $.Tile(this.g,{ctx:this.ctx,x:0,y:0,i:d.draw.flip(c,0,1),speed:-e.speed}));this.tiles.push(new $.Tile(this.g,{ctx:this.ctx,x:0,y:230,i:d.draw.scale(d.imgs.bg1,10),speed:-(e.speed/1.5)}));this.tiles.push(new $.Tile(this.g,{ctx:this.ctx,x:0,y:41,i:d.draw.scale(d.imgs.window,8),speed:-(e.speed/2)}));this.stars=[];while(a--){this.stars.push({x:$.H.rnd(0,d.w),y:$.H.rnd(0,d.h)})}},update:function(){var b=this.g,a=this.tiles.length;while(a--){this.tiles[a].update()}},render:function(){var c=this.g,a=this.stars.length,b;this.ctx.fillStyle=$.cols.black;this.ctx.fillRect(0,0,c.w,c.h);while(a--){b=this.stars[a];this.draw.circle(b.x,b.y,1,$.cols.blind)}a=this.tiles.length;while(a--){this.tiles[a].render()}c.ctx.drawImage(this.c,0,0)},stop:function(){var a=this.tiles.length;this.speed=0;while(a--){this.tiles[a].speed=0}},makeFloor:function(b){var d=10,f=$.H.mkCanvas(b.width*10,b.height),a=f.getContext("2d"),e;for(e=0;e(a.w/2)?1:-1)},update:function(){this._super();this.hitGroup("player")},doDamage:function(b){var a=this.g;if(b.group==="player"){this.remove=true;a.audio.play("powerup");a.ents.push(new $.Msg(a,{text:this.val,col:"p",x:this.x,y:this.y}))}}});$.Battery=$.Sprite.extend({init:function(a,b){this._super(a,b);this.scale=6;this.group="battery";this.speed=a.bg.speed*-1;this.frames=1;this.mkImg("battery");this.vx=(a.bg.speed/1000)*-1;this.x=480;this.y=120},update:function(){this._super();this.hitGroup("player")},doDamage:function(b){var a=this.g;if(b.group==="player"){this.remove=true;a.ents.push(new $.Msg(a,{text:a.p1.doPowerup(),col:"p",x:this.x,y:this.y}))}}});$.Fader=$.Sprite.extend({init:function(a,b){this._super(a,b);this.w=0;this.h=a.h;this.remove=false;this.col=b.col;this.cb=b.cb;this.dir=b.dir||1},update:function(){var a=this.g;this.tick=a.dt/1.5;this.w+=this.tick;if(this.w>a.w){if(this.cb){this.cb()}this.remove=true}},render:function(){var a=this.g;if(this.dir===1){a.draw.rect(0,0,this.w,this.h,this.col);a.draw.rect(a.w,0,-this.w,this.h,this.col)}else{if(this.dir===-1){a.draw.rect(0,0,a.w-this.w,this.h,this.col);a.draw.rect(a.w,0,-(a.w-this.w),this.h,this.col)}}}});$.Explosion=$.Sprite.extend({init:function(b,c){var a;this._super(b,c);this.name="explosion";this.scale=1;this.group="na";this.startX=c.x;this.startY=c.y;this.particles=c.particles||3;this.magnitude=c.magnitude||9;this.factor=1;this.mkImg("circle");b.emitter.particle(this.particles,this.x,this.y);b.audio.play("explode");this.angle=0;this.grow=1},update:function(){var a=this.g;this._super();if(this.scale<=this.magnitude){this.scale+=this.factor}if(this.scale===this.magnitude){this.factor*=-1}if(this.scale<=1){this.remove=true}this.mkImg("circle")},render:function(){var a=this.startX-(this.w/2),d=this.startY-(this.h/2),c=this.g,b=this.i;c.ctx.drawImage(b,a,d)}});$.Msg=$.Sprite.extend({init:function(a,b){this._super(a,b);this.vy=-1;this.lifespan=2;this.ttl=this.lifespan;this.w=10;this.h=10;this.col=b.col||"w";this.f=a.mkFont(this.col,3);this.vx=-0.09;this.vy=-0.05},update:function(){var a=this.g;this._super();this.x+=this.vx*a.dt;this.y+=this.vy*a.dt;this.ttl-=a.dt/1000;if(this.ttl<0){this.kill()}},render:function(){var a=this.g;a.ctx.globalAlpha=(this.ttl/this.lifespan);a.draw.text(this.text,this.f,this.x,this.y);a.ctx.globalAlpha=1}});$.Robo=$.Sprite.extend({init:function(a,b){this._super(a,b);this.speed=1;this.group="player";this.frames=2;this.scale=4;this.mkImg("p1");this.angle=0;this.speed=5;this.vy=this.speed;this.gravity=0.001;this.jump=-0.4;this.jumping=false;this.flipping=false;this.flipped=false;this.jumpCount=0;this.jumps=0;this.flips=0;this.pause=false;this.powerup=0;this.gun=1},update:function(){var b=this.g,a=b.input.k;if(this.pause){return}this.hitGroup("baddies");if(b.doJump&&this.jumpCount<2&&this.tick>2){this.jumpCount+=1;if(!this.jumping){b.audio.play("jump");this.jumps+=1;this.jumping=true;this.vy=this.jump}else{if(this.jumping){b.audio.play("flip");this.flips+=1;this.flipping=true;this.gravity*=-1;this.jump*=-1;this.flipped=!this.flipped}}}this._super();this.keepOnScreen();if(this.x>450){this.frame=1}},render:function(){if(this.pause){return}this._super()},doDamage:function(a){if(a.group==="baddies"){this.kill()}},receiveDamage:function(a){if(a.group==="baddies"){this.kill()}},kill:function(){var d=this.g,a=this,c=$.H.rnd,b=3;this.dead=true;this.remove=true;d.bg.stop();d.ents.push(new $.Explosion(d,{x:this.x,y:this.y}));d.shake.start(50,50)},doPowerup:function(){var a=this.g;this.powerup+=1;if(this.powerup>3){a.score+=100;return 100}if(this.powerup===1){a.bulletInterval=100;return"RAPID FIRE"}else{if(this.powerup===2){return"DOUBLE SHOT"}else{if(this.powerup===3){return"TRIPLE SHOT"}}}}});$.Floater=$.Sprite.extend({init:function(a,b){this._super(a,b);this.scale=$.H.rnd(2,5);this.health=this.scale*10;this.group="baddies";this.speed=1;this.frames=2;this.frameChange=10;this.mkImg("floater");this.frameRate=120;this.x=$.H.rnd((a.w/2)-(this.w/2));this.y=$.H.rnd((a.h/2)-(this.h/2));this.setDir()},update:function(){var a=this.g;this.lastX=this.x;this.lastY=this.y;if(this.gravity){this.vy+=this.gravity}this.x+=this.vx*a.dt;this.y+=this.vy*a.dt;this.atEdget=this.keepOnScreen();if(this.atEdget){this.setDir()}this.cx=this.x+(this.w/2);this.cy=this.y+(this.y/2);this.tick+=1;if(this.frameNext<0){this.frameNext=this.frameRate;this.frame=(this.frame===this.frames)?1:this.frame+=1}this.frameNext-=a.dt;this.hurt-=a.dt},render:function(){var b=this.g,a=this.i;if(this.hurt>0){a=this.iHurt}if(this.flipX){a=b.draw.flip(a,1,0)}b.ctx.drawImage(a,(this.frame*this.w)-this.w,0,this.w,this.h,~~this.x,~~this.y,this.w,this.h)},receiveDamage:function(){var a=this.g;this.hurt=this.hurtTime;a.emitter.particle(2,this.x,this.y);this.health-=10;if(this.health<0){a.ents.push(new $.Msg(a,{text:this.scale*10,x:this.x,y:this.y}));if(!a.ios){a.shake.start(this.scale*5,this.scale*5);a.ents.push(new $.Explosion(a,{x:this.x,y:this.y}))}this.remove=true}},kill:function(){return},setDir:function(){this.vx=($.H.rnd(0,200)-100)/1000;this.vy=($.H.rnd(0,200)-100)/1000;this.flipX=this.vx>0?true:false}});$.Crate=$.Sprite.extend({init:function(a,b){this._super(a,b);this.scale=4;this.health=this.scale*10;this.group="baddies";this.speed=a.bg.speed*-1;this.frames=1;this.mkImg("crate");this.iHurt=a.draw.scale(a.imgs.crate_w,this.scale);this.frameRate=120;this.hurt=0;this.hurtTime=200;this.x=500;this.y=b.y||(a.h-a.bg.ground)-this.h;this.vx=(a.bg.speed/1000)*-1},update:function(){var a=this.g;this.lastX=this.x;this.lastY=this.y;if(a.bg.speed!==0){this.x+=this.vx*a.dt}if(this.x<-this.w){this.x=a.w*2+$.H.rnd(0,a.w)}this.frameNext-=a.dt;this.hurt-=a.dt},render:function(){var b=this.g,a=this.i;if(this.hurt>0){a=this.iHurt}b.ctx.drawImage(a,(this.frame*this.w)-this.w,0,this.w,this.h,~~this.x,~~this.y,this.w,this.h)},receiveDamage:function(){var a=this.g;this.hurt=this.hurtTime;a.emitter.particle(2,this.x,this.y);this.health-=0;if(this.health<0){a.shake.start(this.scale*5,this.scale*5);a.ents.push(new $.Explosion(a,{x:this.x,y:this.y}));this.remove=true}},kill:function(){return}});$.Spark=$.Sprite.extend({init:function(a,b){this._super(a,b);this.scale=4;this.health=this.scale*10;this.group="baddies";this.speed=a.bg.speed*-1;this.frames=2;this.mkImg("spark");this.iHurt=a.draw.scale(a.imgs.spark_w,this.scale);this.hurt=0;this.hurtTime=200;this.frameRate=100;this.x=500;this.vx=(a.bg.speed/1000)*-1},update:function(){var a=this.g;this._super();if(this.x<-this.w){this.x=a.w*2}},render:function(){var b=this.g,a=this.i;if(this.hurt>0){a=this.iHurt}b.ctx.drawImage(a,(this.frame*this.w)-this.w,0,this.w,this.h,~~this.x,~~this.y,this.w,this.h)},receiveDamage:function(){var a=this.g;a.emitter.particle(2,this.x,this.y);this.health-=0;if(this.health<0){a.shake.start(this.scale*5,this.scale*5);a.ents.push(new $.Explosion(a,{x:this.x,y:this.y}));this.remove=true}},kill:function(){return}});$.Drone=$.Sprite.extend({init:function(a,b){this._super(a,b);this.scale=4;this.health=5;this.group="baddies";this.speed=(a.bg.speed/4)*-1;this.frames=2;this.t=0;this.m=$.data.moves[b.m];this.scale=this.m.scale||4;this.flipped=this.m.flipped||0;this.mkImg(this.m.img||"drone");this.x=b.x||this.m.sx;this.y=b.y||this.m.sy;this.t=0},update:function(){var b=this.g,a=this.m;this.t+=(b.dt/1000);this.lastX=this.x;this.lastY=this.y;this.vx=a.A+a.B*Math.sin(a.C*this.t+a.D);this.vy=a.E+a.F*Math.sin(a.G*this.t+a.H);this.x+=this.vx*(b.dt/1000);this.y+=this.vy*(b.dt/1000);if(this.x<-50){this.remove=true}if(this.frameNext<0){this.frameNext=this.frameRate;this.frame=(this.frame===this.frames)?1:this.frame+=1}this.frameNext-=b.dt;this.hurt-=b.dt},receiveDamage:function(){var a=this.g;this.hurt=this.hurtTime;a.emitter.particle(2,this.x,this.y);this.health-=10;if(this.health<0){this.remove=true;a.score+=this.scale*10;a.ents.push(new $.Explosion(a,{x:this.x,y:this.y}));a.ents.push(new $.Msg(a,{text:this.scale*10,x:this.x,y:this.y}));a.waves[this.waveId]-=1;if(!a.ios){a.shake.start(this.scale*5,this.scale*5)}if(a.waves[this.waveId]===0){a.score+=this.scale*30;a.ents.push(new $.Star(a,{val:this.scale*30,x:this.x,y:this.y}))}}},kill:function(){return}});$.Bullet=$.Sprite.extend({init:function(a,b){this._super(a,b);this.w=b.size||8;this.h=b.size||8;this.name="bullet";this.speed=6/10;this.ttl=3000;this.group="bullets";this.scale=2;this.vx=this.speed*Math.cos(this.angle);this.vy=this.speed*Math.sin(this.angle);this.col=$.cols.zornskin;a.audio.play("shoot")},update:function(){this._super();this.hitGroup("baddies");if(this.offscreen){this.kill()}this.ttl-=this.tick;if(this.ttl<0){this.remove=true}},render:function(){var b=this.g,a=b.draw.scale(b.imgs.circle_w,2);this._super();if(this.tick<0.3){b.ctx.drawImage(a,~~this.x,~~this.y)}},keepOnScreen:function(){var a=this.g;if(this.x<0){this.vx*=-1}else{if(this.x>a.w-this.w){this.vx*=-1}}if(this.y<0){this.vy*=-1}else{if(this.y>a.h-this.h){this.vy*=-1}}}});$.Portal=$.Sprite.extend({init:function(a,b){this._super(a,b);this.scale=4;this.health=this.scale*10;this.group="portal";this.speed=0;this.h=50;this.w=20;this.hide()},update:function(){this._super();this.hitGroup("player")},show:function(){var a=this.g;this.x=a.w-this.w;this.y=a.h/2-(this.h/2);this.col=$.cols.slimegreen},hide:function(){var a=this.g;this.x=a.w*-2;this.active=false},doDamage:function(b){var a=this.g;if(b.group==="player"){this.active=true;this.col=$.cols.pigmeat;a.score+=100;a.ents.push(new $.Msg(a,{text:"BONUS",x:this.x,y:this.y}))}}});$.Splash=$.State.extend({init:function(a){this._super(a);this.h1=a.mkFont("g",6);this.p=a.mkFont("w",2);this.fade=0;this.skull=a.draw.scale(a.imgs.skull_w,6)},update:function(){var a=this.g;this._super();this.fade+=(0.01*(a.dt*5))/100;if(this.fade>2){a.audio.play("powerup");a.changeState("Title")}},render:function(){var b=this.g,a=this,e=$.cols,d;b.draw.rect(0,0,b.w,b.h,$.cols.black);b.ctx.globalAlpha=this.fade;b.draw.text("EOINMCG PRESENTS",this.p,false,100);b.ctx.globalAlpha=this.fade/10;b.ctx.drawImage(this.skull,220,150);b.ctx.globalAlpha=1;a._super()}});$.Title=$.State.extend({init:function(b){var a;this._super(b);this.startText=(b.mobile)?"TAP LEFT TO START":"PRESS SPACE";this.h1=b.mkFont("g",6);this.p=b.mkFont("w",2);if(b.plays===0){b.audio.say($.data.title)}this.bg=new $.BgTitle(b,{speed:500,numStars:0});this.hideText=false},update:function(){var a=this.g;this._super();this.bg.update();if(a.doJump&&a.tick>0.4){this.hideText=true;a.ents.push(new $.Fader(a,{col:$.cols.black,cb:function(){a.changeState(a.plays<1?"Tutorial":"Play")}}))}},render:function(){var b=this.g,a=this,e=$.cols,d;this.bg.render();a._super();if(!this.hideText){b.draw.text("HI",this.p,40,40);b.draw.text(b.hiScore,this.p,60,40);b.ctx.globalAlpha=this.fader;b.draw.text(this.startText,this.p,false,250);b.ctx.globalAlpha=1;b.draw.text($.data.title,this.h1,150,85)}}});$.Play=$.State.extend({init:function(a){this._super(a);this.h1=a.mkFont("g",5);this.p=a.mkFont("w",3);a.newHi=false;a.plays+=1;a.score=0;this.levelNum=0;a.waves=[];this.p1=new $.Robo(a,{x:60,y:200});a.p1=this.p1;a.ents.push(this.p1);this.portal=new $.Portal(a,{});a.ents.push(this.portal);this.gameover=false;a.state=this;this.bulletDelay=0;a.bulletInterval=200;a.audio.play("alarm");this.nextLevel()},update:function(){var d=this.g,c=this,b=d.input,a=b.k;this._super();this.bg.update();if((b.touchRight||a[16])&&this.bulletDelay<0&&!c.p1.dead&&!c.p1.pause){this.bulletDelay=d.bulletInterval;d.ents.push(new $.Bullet(d,{x:this.p1.x+(this.p1.w/2),y:this.p1.y+(this.p1.h/3),angle:0}));if(d.p1.powerup>1){d.ents.push(new $.Bullet(d,{x:this.p1.x+(this.p1.w/2),y:this.p1.y+(this.p1.h/3),angle:-0.3}))}if(d.p1.powerup>2){d.ents.push(new $.Bullet(d,{x:this.p1.x+(this.p1.w/2),y:this.p1.y+(this.p1.h/3),angle:0.3}))}}this.bulletDelay-=d.dt;if(d.p1.dead&&!this.gameover){this.gameover=true;if(d.score>d.hiScore){d.newHi=true;d.hiScore=d.score;try{localStorage.setItem("hiScore",d.score)}catch(f){}}d.ents.push(new $.Fader(d,{col:$.cols.bloodred,cb:function(){d.changeState("Gameover")}}))}if(this.gameover&&d.doJump){d.changeState("Title")}d.distance-=d.dt;if(d.distance<0){d.p1.vx=0.5;this.portal.show();d.bg.stop()}if(d.p1.x>450){this.portal.active=true}if(d.distance<-2000&&!this.gameover){this.portal.active=true}if(this.portal.active){this.levelUp()}},render:function(){var d=this.g,b=this,a=d.ents.length,f=$.cols,e;this.bg.render();b._super();if(!this.gameover){d.draw.text(d.score,this.p,40,40)}},levelUp:function(){var c=this.g,b=this,a=c.ents.length;if(c.p1.pause){return}c.audio.play("levelup");c.p1.pause=true;while(a--){if(c.ents[a].group==="baddies"){c.ents[a].remove=true}}a=c.ents.length;while(a--){if(c.ents[a].remove){c.ents.splice(a,1)}}c.events=[];b.p1.x=c.w/3;b.portal.hide();c.ents.push(new $.Fader(c,{col:$.cols.nightblue,cb:function(){c.ents.push(new $.Fader(c,{col:$.cols.nightblue,cb:function(){c.state.nextLevel();c.p1.pause=false},dir:-1}))}}))},nextLevel:function(){var d=this.g,c=this,a,b;if(this.levelNum>($.L.length-1)){this.levelNum=0}this.level=$.L[this.levelNum];a=this.level;if(a.baddies[0]==="all"){a.baddies=Object.keys($.data.moves)}this.bg=new $[a.bg](d,a.bgSettings);if(a.powerup){d.addEvent({time:a.powerup,cb:function(){d.ents.push(new $.Battery(d,{}))}})}d.addEvent({time:2,cb:function(){c.spawnWave();for(b=0;b1||a.tick>5){this.hideText=true;a.ents.push(new $.Fader(a,{col:$.cols.nightblue,cb:function(){a.changeState("Play")}}))}},render:function(){var a=this.g;this.bg.render();this._super();if(!this.hideText){a.draw.text("HOW TO PLAY",this.h1,false,50);a.draw.text("SHOOT",this.p,130,130);a.draw.text(this.shootKey,this.p2,230,130);a.draw.text("JUMP",this.p,130,160);a.draw.text(this.jumpKey,this.p2,230,160);a.ctx.globalAlpha=this.fader;a.draw.text("DOUBLE JUMP REVERSES GRAVITY",this.p2,false,210);a.ctx.globalAlpha=1}}});$.Gameover=$.State.extend({init:function(a){this._super(a);this.h1=a.mkFont("b",7);this.h2=a.mkFont("g",5);this.p=a.mkFont("w",2);if(a.newHi){this.skull=a.draw.scale(a.imgs.star_w,27)}else{this.skull=a.draw.scale(a.imgs.skull_w,27)}a.audio.play("die")},update:function(){var a=this.g;this._super();if(a.doJump&&a.tick>1){a.ents.push(new $.Fader(a,{col:$.cols.pigmeat,dir:-1,cb:function(){a.changeState("Title")}}))}},render:function(){var a=this.g;this._super();a.draw.rect(0,0,a.w,a.h,$.cols.bloodred);a.draw.text("GAME OVER",this.h1,false,70);a.ctx.globalAlpha=0.05;a.ctx.drawImage(this.skull,220,100+(this.fader*20));a.ctx.globalAlpha=1;if(a.newHi){a.ctx.globalAlpha=this.fader;a.draw.text("NEW HISCORE",this.h2,false,170);a.ctx.globalAlpha=1}}});$.L=[{distance:10000,bg:"Bg1",bgSettings:{speed:400},baddies:[],waveSize:3,interval:3,init:[["Crate",{}],["Crate",{y:40}]]},{distance:10000,bg:"Bg1",bgSettings:{speed:400},baddies:["straightBot"],waveSize:3,interval:3,init:[["Crate",{}],["Crate",{y:40}]]},{distance:10000,bg:"Bg1",powerup:1,bgSettings:{speed:400},baddies:["sway","straightTop"],waveSize:4,interval:3,init:[["Crate",{}]]},{distance:10000,bg:"Bg1",powerup:3,bgSettings:{speed:400},baddies:["topbot","bottop"],waveSize:5,interval:3,init:[["Spark",{y:150}]]},{distance:20000,bg:"Bg2",powerup:4,bgSettings:{speed:400},baddies:["topbot","bottop","circle"],waveSize:5,interval:2,init:[["Crate",{}],["Crate",{y:40}]]},{distance:20000,bg:"Bg2",powerup:3,bgSettings:{speed:600},baddies:["all"],waveSize:5,interval:2,init:[["Crate",{}],["Crate",{y:40}]]}];$.data={title:"ROBO FLIP",i:{circle:"R0lGODlhBgAGAKEDAL4mM+uJMffia////yH5BAEKAAMALAAAAAAGAAYAAAIN3AB2EQkhRHuxLWuQKQA7",floater:"R0lGODlhDgAIAKEDAL4mMzGi8vfia////yH+EUNyZWF0ZWQgd2l0aCBHSU1QACH5BAEKAAMALAAAAAAOAAgAAAIdXIZnuOEPIQBxVirxE4MLJ4ShwyRHhHiGUppPyxQAOw==",spark:"R0lGODlhDgAHAKECAOuJMffia////////yH+EUNyZWF0ZWQgd2l0aCBHSU1QACH5BAEKAAMALAAAAAAOAAcAAAIYxGZ4u+acWohoHgCCmE7b+4HRQ43XyRwFADs=",p1:"R0lGODlhDAAIAMIFAAUDC74mMzWADJrKH/3//P///////////yH5BAEKAAcALAAAAAAMAAgAAAMfeLonMkM5KF9sVgUS9tkdgVFj5GRlRU4RsLiHCzNKAgA7",crawler:"R0lGODlhEAAIAKECAL4mM/fia////////yH5BAEKAAIALAAAAAAQAAgAAAIZlI8Skba4WIoIAHnsc/TE/WHhFJYmZSpjAQA7",drone:"R0lGODlhEAAIAKEDAL4mM+Bvi/fia////yH5BAEKAAMALAAAAAAQAAgAAAIenBOmu4j8VBAuHSernbXhoSXASJYmMJyGurYKmhoFADs=",star:"R0lGODlhBQAFAIABAPfia////yH5BAEKAAEALAAAAAAFAAUAAAIITGCGB43OWAEAOw==",battery:"R0lGODlhAwAFAKECAESJGqPOJ////////yH5BAEKAAIALAAAAAADAAUAAAIGFAwBuZoFADs=",font:"R0lGODlhjwAFAIABAAAAAMwAACH5BAEKAAEALAAAAACPAAUAAAJ0DGKHcLzOFDRJ0UbXzdJ2lFQbRo5ipJ1TA7XsW2KanNWyZXpuzuNSz5txQDZTChSrsI6kHQpVu/wer9GvWuw5ssMp1LmbuZKeDdN4NVqT1BAydWvHi14ityTUSZHLE3El0uWHN/Vg9WYoOPe01YEl9VgVUQAAOw==",window:"R0lGODlhIAAiAKECABsmMi9ITv///////yH5BAEKAAMALAAAAAAgACIAAAJZDI6py2gNo5O0PTCy3rzviXnimB0GiXpmmLbD6rpwnM40ad9irnd8/wGcgCohixgcIpPH5cvoZEY1P2SVeAVme1td9/alhWNjGXT6VEZXTSy7An/HK5c5pQAAOw==",ground:"R0lGODlhCgAKAKECAABXhDGi8v///////yH5BAEKAAIALAAAAAAKAAoAAAIQjI+gyxztoIRvWlBlVqiHAgA7",ground1:"R0lGODlhCgAKAKEBABsmMi9ITi9ITi9ITiH5BAEKAAIALAAAAAAKAAoAAAIPhI8RoMoNo5y02vucQ7wAADs=",bg1:"R0lGODlhFAAFAIABAC9ITv///yH5BAEKAAEALAAAAAAUAAUAAAIRjI+pG+CMXnNSPTpxzZzeWgAAOw==",bg2:"R0lGODlhFAAFAIAAAC9ITi9ITiH5BAEKAAEALAAAAAAUAAUAAAIPjI+pBr0fmoRpTnpAxqYAADs=",skull:"R0lGODlhCAAIAIABAAAAAP///yH5BAEKAAEALAAAAAAIAAgAAAIOTIBoyc27nDuKJnMyMAUAOw==",crate:"R0lGODlhCgAKAKECAC9ITp2dnf///////yH5BAEKAAMALAAAAAAKAAoAAAIZXI5nAd0JEkMRiTotxuHSLoSc80hmsJhDAQA7"},sfx:{jump:[0,,0.2432,,0.1709,0.3046,,0.1919,,,,,,0.5923,,,,,1,,,,,0.5],flip:[0,0.001,0.2379,0.1592,0.0225,0.85,,0.0659,0.0917,,-0.6595,-0.2759,0.7809,0.0597,0.0205,0.3604,-0.0083,-0.5261,0.3385,-0.0003,0.0833,,0.6489,0.5],powerup:[0,,0.0129,0.5211,0.4714,0.4234,,,,,,0.4355,0.5108,,,,,,1,,,,,0.5],shoot:[2,,0.1128,,0.178,0.7748,0.0046,-0.4528,,,,,,0.185,0.0994,,,,1,,,,,0.5],explode:[3,,0.3708,0.5822,0.3851,0.0584,,-0.0268,,,,-0.0749,0.7624,,,,,,1,,,,,0.5],levelup:[1,0.115,0.2886,0.4061,0.6535,0.0666,,0.3295,0.0262,-0.0114,,0.2484,0.4319,0.7129,-0.7396,,,-0.906,0.9658,0.1462,0.6577,0.0129,0.0448,0.56],die:[2,0.1987,0.388,0.4366,0.0335,0.5072,,0.1128,-0.1656,0.1987,,-0.376,0.2686,-0.684,0.1392,-0.6819,-0.8117,-0.1072,0.9846,0.057,,0.004,-0.0045,0.56],alarm:[1,0.0241,0.9846,0.6067,0.3041,0.1838,,0.0565,0.1439,-0.3068,0.1402,0.0867,0.7339,0.1332,-0.3119,-0.3257,0.2875,-0.0014,0.5866,0.0086,-0.9675,0.3643,,0.5]},moves:{straightTop:{sx:500,sy:42,scale:4,img:"crawler",flipped:1,A:-200,B:0,C:0,D:0,E:0,F:0,G:0,H:0},straightBot:{sx:500,sy:247,scale:4,img:"crawler",A:-200,B:0,C:0,D:0,E:0,F:0,G:0,H:0},topbot:{sx:500,sy:50,scale:3,img:"floater",A:-200,B:0,C:0,D:0,E:80,F:0,G:0,H:0},bottop:{sx:500,sy:230,scale:3,img:"floater",A:-200,B:0,C:0,D:0,E:-80,F:0,G:0,H:0},sway:{sx:500,sy:60,scale:4,img:"drone",A:-100,B:0,C:0,D:0,E:0,F:360,G:4,H:0},circle:{sx:500,sy:160,scale:5,img:"drone",A:-150,B:-100,C:5,D:0,E:50,F:200,G:10,H:Math.PI/2}}};window.addEventListener("load",function(){var a=new $.Game();a.boot("Splash")},false); --------------------------------------------------------------------------------