ok

Mini Shell

Direktori : /proc/self/root/home/importfo/public_html/inddigmedia.in/jss/
Upload File :
Current File : //proc/self/root/home/importfo/public_html/inddigmedia.in/jss/game.js

/*==============================================================================
Init
==============================================================================*/
$.init = function() {
    $.setupStorage();
    $.wrap = document.getElementById('wrap');
    $.wrapInner = document.getElementById('wrap-inner');
    $.cbg1 = document.getElementById('cbg1');
    $.cbg2 = document.getElementById('cbg2');
    $.cbg3 = document.getElementById('cbg3');
    $.cbg4 = document.getElementById('cbg4');
    $.cmg = document.getElementById('cmg');
    $.cfg = document.getElementById('cfg');
    $.ctxbg1 = $.cbg1.getContext('2d');
    $.ctxbg2 = $.cbg2.getContext('2d');
    $.ctxbg3 = $.cbg3.getContext('2d');
    $.ctxbg4 = $.cbg4.getContext('2d');
    $.ctxmg = $.cmg.getContext('2d');
    $.ctxfg = $.cfg.getContext('2d');
    $.cw = $.cmg.width = $.cfg.width = 800;
    $.ch = $.cmg.height = $.cfg.height = 600;
    $.wrap.style.width = $.wrapInner.style.width = $.cw + 'px';
    $.wrap.style.height = $.wrapInner.style.height = $.ch + 'px';
    $.wrap.style.marginLeft = (-$.cw / 2) - 10 + 'px';
    $.wrap.style.marginTop = (-$.ch / 2) - 10 + 'px';
    $.ww = Math.floor($.cw * 2);
    $.wh = Math.floor($.ch * 2);
    $.cbg1.width = Math.floor($.cw * 1.1);
    $.cbg1.height = Math.floor($.ch * 1.1);
    $.cbg2.width = Math.floor($.cw * 1.15);
    $.cbg2.height = Math.floor($.ch * 1.15);
    $.cbg3.width = Math.floor($.cw * 1.2);
    $.cbg3.height = Math.floor($.ch * 1.2);
    $.cbg4.width = Math.floor($.cw * 1.25);
    $.cbg4.height = Math.floor($.ch * 1.25);

    $.screen = {
        x: ($.ww - $.cw) / -2,
        y: ($.wh - $.ch) / -2
    };

    $.mute = $.storage['mute'];
    $.autofire = $.storage['autofire'];
    $.slowEnemyDivider = 3;

    $.keys = {
        state: {
            up: 0,
            down: 0,
            left: 0,
            right: 0,
            f: 0,
            m: 0,
            p: 0
        },
        pressed: {
            up: 0,
            down: 0,
            left: 0,
            right: 0,
            f: 0,
            m: 0,
            p: 0
        }
    };
    $.okeys = {};
    $.mouse = {
        x: $.ww / 2,
        y: $.wh / 2,
        sx: 0,
        sy: 0,
        ax: window.innerWidth / 2,
        ay: 0,
        down: 0
    };
    $.buttons = [];

    $.minimap = {
            x: 20,
            y: $.ch - Math.floor($.ch * 0.1) - 20,
            width: Math.floor($.cw * 0.1),
            height: Math.floor($.ch * 0.1),
            scale: Math.floor($.cw * 0.1) / $.ww,
            color: 'hsla(0, 0%, 0%, 0.85)',
            strokeColor: '#3a3a3a'
        },
        $.cOffset = {
            left: 0,
            top: 0
        };

    $.levelCount = $.definitions.levels.length;
    $.states = {};
    $.state = '';
    $.enemies = [];
    $.bullets = [];
    $.explosions = [];
    $.powerups = [];
    $.particleEmitters = [];
    $.textPops = [];
    $.levelPops = [];
    $.powerupTimers = [];

    $.resizecb();
    $.bindEvents();
    $.setupStates();
    $.renderBackground1();
    $.renderBackground2();
    $.renderBackground3();
    $.renderBackground4();
    $.renderForeground();
    $.renderFavicon();
    $.setState('menu');
    $.loop();
};

/*==============================================================================
Reset
==============================================================================*/
$.reset = function() {
    $.indexGlobal = 0;
    $.dt = 1;
    $.lt = 0;
    $.elapsed = 0;
    $.tick = 0;

    $.gameoverTick = 0;
    $.gameoverTickMax = 200;
    $.gameoverExplosion = 0;

    $.instructionTick = 0;
    $.instructionTickMax = 400;

    $.levelDiffOffset = 0;
    $.enemyOffsetMod = 0;
    $.slow = 0;

    $.screen = {
        x: ($.ww - $.cw) / -2,
        y: ($.wh - $.ch) / -2
    };
    $.rumble = {
        x: 0,
        y: 0,
        level: 0,
        decay: 0.4
    };

    $.mouse.down = 0;

    $.level = {
        current: 0,
        kills: 0,
        killsToLevel: $.definitions.levels[0].killsToLevel,
        distribution: $.definitions.levels[0].distribution,
        distributionCount: $.definitions.levels[0].distribution.length
    };

    $.enemies.length = 0;
    $.bullets.length = 0;
    $.explosions.length = 0;
    $.powerups.length = 0;
    $.particleEmitters.length = 0;
    $.textPops.length = 0;
    $.levelPops.length = 0;
    $.powerupTimers.length = 0;

    for (var i = 0; i < $.definitions.powerups.length; i++) {
        $.powerupTimers.push(0);
    }

    $.kills = 0;
    $.bulletsFired = 0;
    $.powerupsCollected = 0;
    $.score = 0;

    $.hero = new $.Hero();

    $.levelPops.push(new $.LevelPop({
        level: 1
    }));
};

/*==============================================================================
Create Favicon
==============================================================================*/
$.renderFavicon = function() {
    var favicon = document.getElementById('favicon'),
        favc = document.createElement('canvas'),
        favctx = favc.getContext('2d'),
        faviconGrid = [
            [1, 1, 1, 1, 1, , , 1, 1, 1, 1, 1, 1, 1, 1, 1],
            [1, , , , , , , , , , , , , , , 1],
            [1, , , , , , , , , , , , , , , 1],
            [1, , , , , 1, 1, , , 1, 1, 1, 1, 1, , 0],
            [1, , , , , 1, 1, , , 1, 1, 1, 1, 1, , 0],
            [1, , , , , 1, 1, , , 1, 1, , , , , 1],
            [1, , , , , 1, 1, , , 1, 1, , , , , 1],
            [1, , , , , 1, 1, , , 1, 1, , , , , 1],
            [1, , , , , 1, 1, , , 1, 1, , , , , 1],
            [1, , , , , 1, 1, , , 1, 1, , , , , 1],
            [1, , , , , 1, 1, , , 1, 1, , , , , 1],
            [, , 1, 1, 1, 1, 1, , , 1, 1, , , , , 1],
            [, , 1, 1, 1, 1, 1, , , 1, 1, , , , , 1],
            [1, , , , , , , , , , , , , , , 1],
            [1, , , , , , , , , , , , , , , 1],
            [1, 1, 1, 1, 1, 1, 1, 1, 1, , , 1, 1, 1, 1, 1]
        ];
    favc.width = favc.height = 16;
    favctx.beginPath();
    for (var y = 0; y < 16; y++) {
        for (var x = 0; x < 16; x++) {
            if (faviconGrid[y][x] === 1) {
                favctx.rect(x, y, 1, 1);
            }
        }
    }
    favctx.fill();
    favicon.href = favc.toDataURL();
};

/*==============================================================================
Render Backgrounds
==============================================================================*/
$.renderBackground1 = function() {
    var gradient = $.ctxbg1.createRadialGradient($.cbg1.width / 2, $.cbg1.height / 2, 0, $.cbg1.width / 2, $.cbg1.height / 2, $.cbg1.height);
    gradient.addColorStop(0, 'hsla(0, 0%, 100%, 0.1)');
    gradient.addColorStop(0.65, 'hsla(0, 0%, 100%, 0)');
    $.ctxbg1.fillStyle = gradient;
    $.ctxbg1.fillRect(0, 0, $.cbg1.width, $.cbg1.height);

    var i = 2000;
    while (i--) {
        $.util.fillCircle($.ctxbg1, $.util.rand(0, $.cbg1.width), $.util.rand(0, $.cbg1.height), $.util.rand(0.2, 0.5), 'hsla(0, 0%, 100%, ' + $.util.rand(0.05, 0.2) + ')');
    }

    var i = 800;
    while (i--) {
        $.util.fillCircle($.ctxbg1, $.util.rand(0, $.cbg1.width), $.util.rand(0, $.cbg1.height), $.util.rand(0.1, 0.8), 'hsla(0, 0%, 100%, ' + $.util.rand(0.05, 0.5) + ')');
    }
}

$.renderBackground2 = function() {
    var i = 80;
    while (i--) {
        $.util.fillCircle($.ctxbg2, $.util.rand(0, $.cbg2.width), $.util.rand(0, $.cbg2.height), $.util.rand(1, 2), 'hsla(0, 0%, 100%, ' + $.util.rand(0.05, 0.15) + ')');
    }
}

$.renderBackground3 = function() {
    var i = 40;
    while (i--) {
        $.util.fillCircle($.ctxbg3, $.util.rand(0, $.cbg3.width), $.util.rand(0, $.cbg3.height), $.util.rand(1, 2.5), 'hsla(0, 0%, 100%, ' + $.util.rand(0.05, 0.1) + ')');
    }
}

$.renderBackground4 = function() {
    var size = 50;
    $.ctxbg4.fillStyle = 'hsla(0, 0%, 50%, 0.05)';
    var i = Math.round($.cbg4.height / size);
    while (i--) {
        $.ctxbg4.fillRect(0, i * size + 25, $.cbg4.width, 1);
    }
    i = Math.round($.cbg4.width / size);
    while (i--) {
        $.ctxbg4.fillRect(i * size, 0, 1, $.cbg4.height);
    }
}

/*==============================================================================
Render Foreground
==============================================================================*/
$.renderForeground = function() {
    var gradient = $.ctxfg.createRadialGradient($.cw / 2, $.ch / 2, $.ch / 3, $.cw / 2, $.ch / 2, $.ch);
    gradient.addColorStop(0, 'hsla(0, 0%, 0%, 0)');
    gradient.addColorStop(1, 'hsla(0, 0%, 0%, 0.5)');
    $.ctxfg.fillStyle = gradient;
    $.ctxfg.fillRect(0, 0, $.cw, $.ch);

    $.ctxfg.fillStyle = 'hsla(0, 0%, 50%, 0.1)';
    var i = Math.round($.ch / 2);
    while (i--) {
        $.ctxfg.fillRect(0, i * 2, $.cw, 1);
    }

    var gradient2 = $.ctxfg.createLinearGradient($.cw, 0, 0, $.ch);
    gradient2.addColorStop(0, 'hsla(0, 0%, 100%, 0.04)');
    gradient2.addColorStop(0.75, 'hsla(0, 0%, 100%, 0)');
    $.ctxfg.beginPath();
    $.ctxfg.moveTo(0, 0);
    $.ctxfg.lineTo($.cw, 0);
    $.ctxfg.lineTo(0, $.ch);
    $.ctxfg.closePath();
    $.ctxfg.fillStyle = gradient2;
    $.ctxfg.fill();
}

/*==============================================================================
User Interface / UI / GUI / Minimap
==============================================================================*/

$.renderInterface = function() {
    /*==============================================================================
    Powerup Timers
    ==============================================================================*/
    for (var i = 0; i < $.definitions.powerups.length; i++) {
        var powerup = $.definitions.powerups[i],
            powerupOn = ($.powerupTimers[i] > 0);
        $.ctxmg.beginPath();
        var powerupText = $.text({
            ctx: $.ctxmg,
            x: $.minimap.x + $.minimap.width + 90,
            y: $.minimap.y + 4 + (i * 12),
            text: powerup.title,
            hspacing: 1,
            vspacing: 1,
            halign: 'right',
            valign: 'top',
            scale: 1,
            snap: 1,
            render: 1
        });
        if (powerupOn) {
            $.ctxmg.fillStyle = 'hsla(0, 0%, 100%, ' + (0.25 + (($.powerupTimers[i] / 300) * 0.75)) + ')';
        } else {
            $.ctxmg.fillStyle = 'hsla(0, 0%, 100%, 0.25)';
        }
        $.ctxmg.fill();
        if (powerupOn) {
            var powerupBar = {
                x: powerupText.ex + 5,
                y: powerupText.sy,
                width: 110,
                height: 5
            };
            $.ctxmg.fillStyle = 'hsl(' + powerup.hue + ', ' + powerup.saturation + '%, ' + powerup.lightness + '%)';
            $.ctxmg.fillRect(powerupBar.x, powerupBar.y, ($.powerupTimers[i] / 300) * powerupBar.width, powerupBar.height);
        }
    }

    /*==============================================================================
    Instructions
    ==============================================================================*/
    if ($.instructionTick < $.instructionTickMax) {
        $.instructionTick += $.dt;
        $.ctxmg.beginPath();
        $.text({
            ctx: $.ctxmg,
            x: $.cw / 2 - 10,
            y: $.ch - 20,
            text: 'MOVE\nAIM/FIRE\nAUTOFIRE\nPAUSE\nMUTE',
            hspacing: 1,
            vspacing: 17,
            halign: 'right',
            valign: 'bottom',
            scale: 2,
            snap: 1,
            render: 1
        });
        if ($.instructionTick < $.instructionTickMax * 0.25) {
            var alpha = ($.instructionTick / ($.instructionTickMax * 0.25)) * 0.5;
        } else if ($.instructionTick > $.instructionTickMax - $.instructionTickMax * 0.25) {
            var alpha = (($.instructionTickMax - $.instructionTick) / ($.instructionTickMax * 0.25)) * 0.5;
        } else {
            var alpha = 0.5;
        }
        alpha = Math.min(1, Math.max(0, alpha));

        $.ctxmg.fillStyle = 'hsla(0, 0%, 100%, ' + alpha + ')';
        $.ctxmg.fill();

        $.ctxmg.beginPath();
        $.text({
            ctx: $.ctxmg,
            x: $.cw / 2 + 10,
            y: $.ch - 20,
            text: 'WASD/ARROWS\nMOUSE\nF\nP\nM',
            hspacing: 1,
            vspacing: 17,
            halign: 'left',
            valign: 'bottom',
            scale: 2,
            snap: 1,
            render: 1
        });
        if ($.instructionTick < $.instructionTickMax * 0.25) {
            var alpha = ($.instructionTick / ($.instructionTickMax * 0.25)) * 1;
        } else if ($.instructionTick > $.instructionTickMax - $.instructionTickMax * 0.25) {
            var alpha = (($.instructionTickMax - $.instructionTick) / ($.instructionTickMax * 0.25)) * 1;
        } else {
            var alpha = 1;
        }
        alpha = Math.min(1, Math.max(0, alpha));

        $.ctxmg.fillStyle = 'hsla(0, 0%, 100%, ' + alpha + ')';
        $.ctxmg.fill();
    }

    /*==============================================================================
    Slow Enemies Screen Cover
    ==============================================================================*/
    if ($.powerupTimers[1] > 0) {
        $.ctxmg.fillStyle = 'hsla(200, 100%, 20%, 0.05)';
        $.ctxmg.fillRect(0, 0, $.cw, $.ch);
    }

    /*==============================================================================
    Health
    ==============================================================================*/
    $.ctxmg.beginPath();
    var healthText = $.text({
        ctx: $.ctxmg,
        x: 20,
        y: 20,
        text: 'HEALTH',
        hspacing: 1,
        vspacing: 1,
        halign: 'top',
        valign: 'left',
        scale: 2,
        snap: 1,
        render: 1
    });
    $.ctxmg.fillStyle = 'hsla(0, 0%, 100%, 0.5)';
    $.ctxmg.fill();
    var healthBar = {
        x: healthText.ex + 10,
        y: healthText.sy,
        width: 110,
        height: 10
    };
    $.ctxmg.fillStyle = 'hsla(0, 0%, 20%, 1)';
    $.ctxmg.fillRect(healthBar.x, healthBar.y, healthBar.width, healthBar.height);
    $.ctxmg.fillStyle = 'hsla(0, 0%, 100%, 0.25)';
    $.ctxmg.fillRect(healthBar.x, healthBar.y, healthBar.width, healthBar.height / 2);
    $.ctxmg.fillStyle = 'hsla(' + $.hero.life * 120 + ', 100%, 40%, 1)';
    $.ctxmg.fillRect(healthBar.x, healthBar.y, $.hero.life * healthBar.width, healthBar.height);
    $.ctxmg.fillStyle = 'hsla(' + $.hero.life * 120 + ', 100%, 75%, 1)';
    $.ctxmg.fillRect(healthBar.x, healthBar.y, $.hero.life * healthBar.width, healthBar.height / 2);

    if ($.hero.takingDamage && $.hero.life > 0.01) {
        $.particleEmitters.push(new $.ParticleEmitter({
            x: -$.screen.x + healthBar.x + $.hero.life * healthBar.width,
            y: -$.screen.y + healthBar.y + healthBar.height / 2,
            count: 1,
            spawnRange: 2,
            friction: 0.85,
            minSpeed: 2,
            maxSpeed: 20,
            minDirection: $.pi / 2 - 0.2,
            maxDirection: $.pi / 2 + 0.2,
            hue: $.hero.life * 120,
            saturation: 100
        }));
    }

    /*==============================================================================
    Progress
    ==============================================================================*/
    $.ctxmg.beginPath();
    var progressText = $.text({
        ctx: $.ctxmg,
        x: healthBar.x + healthBar.width + 40,
        y: 20,
        text: 'PROGRESS',
        hspacing: 1,
        vspacing: 1,
        halign: 'top',
        valign: 'left',
        scale: 2,
        snap: 1,
        render: 1
    });
    $.ctxmg.fillStyle = 'hsla(0, 0%, 100%, 0.5)';
    $.ctxmg.fill();
    var progressBar = {
        x: progressText.ex + 10,
        y: progressText.sy,
        width: healthBar.width,
        height: healthBar.height
    };
    $.ctxmg.fillStyle = 'hsla(0, 0%, 20%, 1)';
    $.ctxmg.fillRect(progressBar.x, progressBar.y, progressBar.width, progressBar.height);
    $.ctxmg.fillStyle = 'hsla(0, 0%, 100%, 0.25)';
    $.ctxmg.fillRect(progressBar.x, progressBar.y, progressBar.width, progressBar.height / 2);
    $.ctxmg.fillStyle = 'hsla(0, 0%, 50%, 1)';
    $.ctxmg.fillRect(progressBar.x, progressBar.y, ($.level.kills / $.level.killsToLevel) * progressBar.width, progressBar.height);
    $.ctxmg.fillStyle = 'hsla(0, 0%, 100%, 1)';
    $.ctxmg.fillRect(progressBar.x, progressBar.y, ($.level.kills / $.level.killsToLevel) * progressBar.width, progressBar.height / 2);

    if ($.level.kills == $.level.killsToLevel) {
        $.particleEmitters.push(new $.ParticleEmitter({
            x: -$.screen.x + progressBar.x + progressBar.width,
            y: -$.screen.y + progressBar.y + progressBar.height / 2,
            count: 30,
            spawnRange: 5,
            friction: 0.95,
            minSpeed: 2,
            maxSpeed: 25,
            minDirection: 0,
            minDirection: $.pi / 2 - $.pi / 4,
            maxDirection: $.pi / 2 + $.pi / 4,
            hue: 0,
            saturation: 0
        }));
    }

    /*==============================================================================
    Score
    ==============================================================================*/
    $.ctxmg.beginPath();
    var scoreLabel = $.text({
        ctx: $.ctxmg,
        x: progressBar.x + progressBar.width + 40,
        y: 20,
        text: 'SCORE',
        hspacing: 1,
        vspacing: 1,
        halign: 'top',
        valign: 'left',
        scale: 2,
        snap: 1,
        render: 1
    });
    $.ctxmg.fillStyle = 'hsla(0, 0%, 100%, 0.5)';
    $.ctxmg.fill();

    $.ctxmg.beginPath();
    var scoreText = $.text({
        ctx: $.ctxmg,
        x: scoreLabel.ex + 10,
        y: 20,
        text: $.util.pad($.score, 6),
        hspacing: 1,
        vspacing: 1,
        halign: 'top',
        valign: 'left',
        scale: 2,
        snap: 1,
        render: 1
    });
    $.ctxmg.fillStyle = 'hsla(0, 0%, 100%, 1)';
    $.ctxmg.fill();

    $.ctxmg.beginPath();
    var bestLabel = $.text({
        ctx: $.ctxmg,
        x: scoreText.ex + 40,
        y: 20,
        text: 'BEST',
        hspacing: 1,
        vspacing: 1,
        halign: 'top',
        valign: 'left',
        scale: 2,
        snap: 1,
        render: 1
    });
    $.ctxmg.fillStyle = 'hsla(0, 0%, 100%, 0.5)';
    $.ctxmg.fill();

    $.ctxmg.beginPath();
    var bestText = $.text({
        ctx: $.ctxmg,
        x: bestLabel.ex + 10,
        y: 20,
        text: $.util.pad(Math.max($.storage['score'], $.score), 6),
        hspacing: 1,
        vspacing: 1,
        halign: 'top',
        valign: 'left',
        scale: 2,
        snap: 1,
        render: 1
    });
    $.ctxmg.fillStyle = 'hsla(0, 0%, 100%, 1)';
    $.ctxmg.fill();
};

$.renderMinimap = function() {
    $.ctxmg.fillStyle = $.minimap.color;
    $.ctxmg.fillRect($.minimap.x, $.minimap.y, $.minimap.width, $.minimap.height);

    $.ctxmg.fillStyle = 'hsla(0, 0%, 100%, 0.1)';
    $.ctxmg.fillRect(
        Math.floor($.minimap.x + -$.screen.x * $.minimap.scale),
        Math.floor($.minimap.y + -$.screen.y * $.minimap.scale),
        Math.floor($.cw * $.minimap.scale),
        Math.floor($.ch * $.minimap.scale)
    );

    //$.ctxmg.beginPath();
    for (var i = 0; i < $.enemies.length; i++) {
        var enemy = $.enemies[i],
            x = $.minimap.x + Math.floor(enemy.x * $.minimap.scale),
            y = $.minimap.y + Math.floor(enemy.y * $.minimap.scale);
        if ($.util.pointInRect(x + 1, y + 1, $.minimap.x, $.minimap.y, $.minimap.width, $.minimap.height)) {
            //$.ctxmg.rect( x, y, 2, 2 );
            $.ctxmg.fillStyle = 'hsl(' + enemy.hue + ', ' + enemy.saturation + '%, 50%)';
            $.ctxmg.fillRect(x, y, 2, 2);
        }
    }
    //$.ctxmg.fillStyle = '#f00';
    //$.ctxmg.fill();

    $.ctxmg.beginPath();
    for (var i = 0; i < $.bullets.length; i++) {
        var bullet = $.bullets[i],
            x = $.minimap.x + Math.floor(bullet.x * $.minimap.scale),
            y = $.minimap.y + Math.floor(bullet.y * $.minimap.scale);
        if ($.util.pointInRect(x, y, $.minimap.x, $.minimap.y, $.minimap.width, $.minimap.height)) {
            $.ctxmg.rect(x, y, 1, 1);
        }
    }
    $.ctxmg.fillStyle = '#fff';
    $.ctxmg.fill();

    $.ctxmg.fillStyle = $.hero.fillStyle;
    $.ctxmg.fillRect($.minimap.x + Math.floor($.hero.x * $.minimap.scale), $.minimap.y + Math.floor($.hero.y * $.minimap.scale), 2, 2);

    $.ctxmg.strokeStyle = $.minimap.strokeColor;
    $.ctxmg.strokeRect($.minimap.x - 0.5, $.minimap.y - 0.5, $.minimap.width + 1, $.minimap.height + 1);
};

/*==============================================================================
Enemy Spawning
==============================================================================*/
$.getSpawnCoordinates = function(radius) {
    var quadrant = Math.floor($.util.rand(0, 4)),
        x,
        y,
        start;

    if (quadrant === 0) {
        x = $.util.rand(0, $.ww);
        y = -radius;
        start = 'top';
    } else if (quadrant === 1) {
        x = $.ww + radius;
        y = $.util.rand(0, $.wh);
        start = 'right';
    } else if (quadrant === 2) {
        x = $.util.rand(0, $.ww);
        y = $.wh + radius;
        start = 'bottom';
    } else {
        x = -radius;
        y = $.util.rand(0, $.wh);
        start = 'left';
    }

    return {
        x: x,
        y: y,
        start: start
    };
};

$.spawnEnemy = function(type) {
    var params = $.definitions.enemies[type],
        coordinates = $.getSpawnCoordinates(params.radius);
    params.x = coordinates.x;
    params.y = coordinates.y;
    params.start = coordinates.start;
    params.type = type;
    return new $.Enemy(params);
};

$.spawnEnemies = function() {
    var floorTick = Math.floor($.tick);
    for (var i = 0; i < $.level.distributionCount; i++) {
        var timeCheck = $.level.distribution[i];
        if ($.levelDiffOffset > 0) {
            timeCheck = Math.max(1, timeCheck - ($.levelDiffOffset * 2));
        }
        if (floorTick % timeCheck === 0) {
            $.enemies.push($.spawnEnemy(i));
        }
    }
};

/*==============================================================================
Events
==============================================================================*/
$.mousemovecb = function(e) {
    e.preventDefault();
    $.mouse.ax = e.pageX;
    $.mouse.ay = e.pageY;
    $.mousescreen();
};

$.mousescreen = function() {
    $.mouse.sx = $.mouse.ax - $.cOffset.left;
    $.mouse.sy = $.mouse.ay - $.cOffset.top;
    $.mouse.x = $.mouse.sx - $.screen.x;
    $.mouse.y = $.mouse.sy - $.screen.y;
};

$.mousedowncb = function(e) {
    e.preventDefault();
    $.mouse.down = 1;
};

$.mouseupcb = function(e) {
    e.preventDefault();
    $.mouse.down = 0;
};

$.keydowncb = function(e) {
    var e = (e.keyCode ? e.keyCode : e.which);
    if (e === 38 || e === 87) {
        $.keys.state.up = 1;
    }
    if (e === 39 || e === 68) {
        $.keys.state.right = 1;
    }
    if (e === 40 || e === 83) {
        $.keys.state.down = 1;
    }
    if (e === 37 || e === 65) {
        $.keys.state.left = 1;
    }
    if (e === 70) {
        $.keys.state.f = 1;
    }
    if (e === 77) {
        $.keys.state.m = 1;
    }
    if (e === 80) {
        $.keys.state.p = 1;
    }
}

$.keyupcb = function(e) {
    var e = (e.keyCode ? e.keyCode : e.which);
    if (e === 38 || e === 87) {
        $.keys.state.up = 0;
    }
    if (e === 39 || e === 68) {
        $.keys.state.right = 0;
    }
    if (e === 40 || e === 83) {
        $.keys.state.down = 0;
    }
    if (e === 37 || e === 65) {
        $.keys.state.left = 0;
    }
    if (e === 70) {
        $.keys.state.f = 0;
    }
    if (e === 77) {
        $.keys.state.m = 0;
    }
    if (e === 80) {
        $.keys.state.p = 0;
    }
}

$.resizecb = function(e) {
    var rect = $.cmg.getBoundingClientRect();
    $.cOffset = {
        left: rect.left,
        top: rect.top
    }
}

$.blurcb = function() {
    if ($.state == 'play') {
        $.setState('pause');
    }
}

$.bindEvents = function() {
    window.addEventListener('mousemove', $.mousemovecb);
    window.addEventListener('mousedown', $.mousedowncb);
    window.addEventListener('mouseup', $.mouseupcb);
    window.addEventListener('keydown', $.keydowncb);
    window.addEventListener('keyup', $.keyupcb);
    window.addEventListener('resize', $.resizecb);
    window.addEventListener('blur', $.blurcb);
};

/*==============================================================================
Miscellaneous
==============================================================================*/
$.clearScreen = function() {
    $.ctxmg.clearRect(0, 0, $.cw, $.ch);
};

$.updateDelta = function() {
    var now = Date.now();
    $.dt = (now - $.lt) / (1000 / 60);
    $.dt = ($.dt < 0) ? 0.001 : $.dt;
    $.dt = ($.dt > 10) ? 10 : $.dt;
    $.lt = now;
    $.elapsed += $.dt;
};

$.updateScreen = function() {
    var xSnap,
        xModify,
        ySnap,
        yModify;

    if ($.hero.x < $.cw / 2) {
        xModify = $.hero.x / $.cw;
    } else if ($.hero.x > $.ww - $.cw / 2) {
        xModify = 1 - ($.ww - $.hero.x) / $.cw;
    } else {
        xModify = 0.5;
    }

    if ($.hero.y < $.ch / 2) {
        yModify = $.hero.y / $.ch;
    } else if ($.hero.y > $.wh - $.ch / 2) {
        yModify = 1 - ($.wh - $.hero.y) / $.ch;
    } else {
        yModify = 0.5;
    }

    xSnap = (($.cw * xModify - $.hero.x) - $.screen.x) / 30;
    ySnap = (($.ch * yModify - $.hero.y) - $.screen.y) / 30;

    // ease to new coordinates
    $.screen.x += xSnap * $.dt;
    $.screen.y += ySnap * $.dt;

    // update rumble levels, keep X and Y changes consistent, apply rumble
    if ($.rumble.level > 0) {
        $.rumble.level -= $.rumble.decay;
        $.rumble.level = ($.rumble.level < 0) ? 0 : $.rumble.level;
        $.rumble.x = $.util.rand(-$.rumble.level, $.rumble.level);
        $.rumble.y = $.util.rand(-$.rumble.level, $.rumble.level);
    } else {
        $.rumble.x = 0;
        $.rumble.y = 0;
    }

    //$.screen.x -= $.rumble.x;
    //$.screen.y -= $.rumble.y;

    // animate background canvas
    $.cbg1.style.marginLeft = -(($.cbg1.width - $.cw) / 2) // half the difference from bg to viewport
        -
        (($.cbg1.width - $.cw) / 2) // half the diff again, modified by a percentage below
        *
        ((-$.screen.x - ($.ww - $.cw) / 2) / (($.ww - $.cw) / 2)) // viewport offset applied to bg
        -
        $.rumble.x + 'px';
    $.cbg1.style.marginTop = -(($.cbg1.height - $.ch) / 2) -
        (($.cbg1.height - $.ch) / 2) *
        ((-$.screen.y - ($.wh - $.ch) / 2) / (($.wh - $.ch) / 2)) -
        $.rumble.y + 'px';
    $.cbg2.style.marginLeft = -(($.cbg2.width - $.cw) / 2) // half the difference from bg to viewport
        -
        (($.cbg2.width - $.cw) / 2) // half the diff again, modified by a percentage below
        *
        ((-$.screen.x - ($.ww - $.cw) / 2) / (($.ww - $.cw) / 2)) // viewport offset applied to bg
        -
        $.rumble.x + 'px';
    $.cbg2.style.marginTop = -(($.cbg2.height - $.ch) / 2) -
        (($.cbg2.height - $.ch) / 2) *
        ((-$.screen.y - ($.wh - $.ch) / 2) / (($.wh - $.ch) / 2)) -
        $.rumble.y + 'px';
    $.cbg3.style.marginLeft = -(($.cbg3.width - $.cw) / 2) // half the difference from bg to viewport
        -
        (($.cbg3.width - $.cw) / 2) // half the diff again, modified by a percentage below
        *
        ((-$.screen.x - ($.ww - $.cw) / 2) / (($.ww - $.cw) / 2)) // viewport offset applied to bg
        -
        $.rumble.x + 'px';
    $.cbg3.style.marginTop = -(($.cbg3.height - $.ch) / 2) -
        (($.cbg3.height - $.ch) / 2) *
        ((-$.screen.y - ($.wh - $.ch) / 2) / (($.wh - $.ch) / 2)) -
        $.rumble.y + 'px';
    $.cbg4.style.marginLeft = -(($.cbg4.width - $.cw) / 2) // half the difference from bg to viewport
        -
        (($.cbg4.width - $.cw) / 2) // half the diff again, modified by a percentage below
        *
        ((-$.screen.x - ($.ww - $.cw) / 2) / (($.ww - $.cw) / 2)) // viewport offset applied to bg
        -
        $.rumble.x + 'px';
    $.cbg4.style.marginTop = -(($.cbg4.height - $.ch) / 2) -
        (($.cbg4.height - $.ch) / 2) *
        ((-$.screen.y - ($.wh - $.ch) / 2) / (($.wh - $.ch) / 2)) -
        $.rumble.y + 'px';

    $.mousescreen();
};

$.updateLevel = function() {
    if ($.level.kills >= $.level.killsToLevel) {
        if ($.level.current + 1 < $.levelCount) {
            $.level.current++;
            $.level.kills = 0;
            $.level.killsToLevel = $.definitions.levels[$.level.current].killsToLevel;
            $.level.distribution = $.definitions.levels[$.level.current].distribution;
            $.level.distributionCount = $.level.distribution.length;
        } else {
            $.level.current++;
            $.level.kills = 0;
            // no more level definitions, so take the last level and increase the spawn rate slightly
            //for( var i = 0; i < $.level.distributionCount; i++ ) {
            //$.level.distribution[ i ] = Math.max( 1, $.level.distribution[ i ] - 5 );
            //}
        }
        $.levelDiffOffset = $.level.current + 1 - $.levelCount;
        $.levelPops.push(new $.LevelPop({
            level: $.level.current + 1
        }));
    }
};

$.updatePowerupTimers = function() {
    // HEALTH
    if ($.powerupTimers[0] > 0) {
        if ($.hero.life < 1) {
            $.hero.life += 0.001;
        }
        if ($.hero.life > 1) {
            $.hero.life = 1;
        }
        $.powerupTimers[0] -= $.dt;
    }

    // SLOW ENEMIES
    if ($.powerupTimers[1] > 0) {
        $.slow = 1;
        $.powerupTimers[1] -= $.dt;
    } else {
        $.slow = 0;
    }

    // FAST SHOT
    if ($.powerupTimers[2] > 0) {
        $.hero.weapon.fireRate = 2;
        $.hero.weapon.bullet.speed = 14;
        $.powerupTimers[2] -= $.dt;
    } else {
        $.hero.weapon.fireRate = 5;
        $.hero.weapon.bullet.speed = 10;
    }

    // TRIPLE SHOT
    if ($.powerupTimers[3] > 0) {
        $.hero.weapon.count = 3;
        $.powerupTimers[3] -= $.dt;
    } else {
        $.hero.weapon.count = 1;
    }

    // PIERCE SHOT
    if ($.powerupTimers[4] > 0) {
        $.hero.weapon.bullet.piercing = 1;
        $.powerupTimers[4] -= $.dt;
    } else {
        $.hero.weapon.bullet.piercing = 0;
    }
};

$.spawnPowerup = function(x, y) {
    if (Math.random() < 0.1) {
        var min = ($.hero.life < 0.9) ? 0 : 1,
            type = Math.floor($.util.rand(min, $.definitions.powerups.length)),
            params = $.definitions.powerups[type];
        params.type = type;
        params.x = x;
        params.y = y;
        $.powerups.push(new $.Powerup(params));
    }
};

/*==============================================================================
States
==============================================================================*/
$.setState = function(state) {
    // handle clean up between states
    $.buttons.length = 0;

    if (state == 'menu') {
        $.mouse.down = 0;
        $.mouse.ax = 0;
        $.mouse.ay = 0;

        $.reset();

        var playButton = new $.Button({
            x: $.cw / 2 + 1,
            y: $.ch / 2 - 24,
            lockedWidth: 299,
            lockedHeight: 49,
            scale: 3,
            title: 'PLAY',
            action: function() {
                $.reset();
                $.audio.play('levelup');
                $.setState('play');
            }
        });
        $.buttons.push(playButton);

        var statsButton = new $.Button({
            x: $.cw / 2 + 1,
            y: playButton.ey + 25,
            lockedWidth: 299,
            lockedHeight: 49,
            scale: 3,
            title: 'STATS',
            action: function() {
                $.setState('stats');
            }
        });
        $.buttons.push(statsButton);

        var creditsButton = new $.Button({
            x: $.cw / 2 + 1,
            y: statsButton.ey + 26,
            lockedWidth: 299,
            lockedHeight: 49,
            scale: 3,
            title: 'CREDITS',
            action: function() {
                $.setState('credits');
            }
        });
        $.buttons.push(creditsButton);
    }

    if (state == 'stats') {
        $.mouse.down = 0;

        var clearButton = new $.Button({
            x: $.cw / 2 + 1,
            y: 426,
            lockedWidth: 299,
            lockedHeight: 49,
            scale: 3,
            title: 'CLEAR DATA',
            action: function() {
                $.mouse.down = 0;
                if (window.confirm('Are you sure you want to clear all locally stored game data? This cannot be undone.')) {
                    $.clearStorage();
                    $.mouse.down = 0;
                }
            }
        });
        $.buttons.push(clearButton);

        var menuButton = new $.Button({
            x: $.cw / 2 + 1,
            y: clearButton.ey + 25,
            lockedWidth: 299,
            lockedHeight: 49,
            scale: 3,
            title: 'MENU',
            action: function() {
                $.setState('menu');
            }
        });
        $.buttons.push(menuButton);
    }

    if (state == 'credits') {
        $.mouse.down = 0;

        var js13kButton = new $.Button({
            x: $.cw / 2 + 1,
            y: 476,
            lockedWidth: 299,
            lockedHeight: 49,
            scale: 3,
            title: 'INDDIG MEDIA',
            action: function() {
                location.href = '#';
                $.mouse.down = 0;
            }
        });
        $.buttons.push(js13kButton);

        var menuButton = new $.Button({
            x: $.cw / 2 + 1,
            y: js13kButton.ey + 25,
            lockedWidth: 299,
            lockedHeight: 49,
            scale: 3,
            title: 'MENU',
            action: function() {
                $.setState('menu');
            }
        });
        $.buttons.push(menuButton);
    }

    if (state == 'pause') {
        $.mouse.down = 0;
        $.screenshot = $.ctxmg.getImageData(0, 0, $.cw, $.ch);
        var resumeButton = new $.Button({
            x: $.cw / 2 + 1,
            y: $.ch / 2 + 26,
            lockedWidth: 299,
            lockedHeight: 49,
            scale: 3,
            title: 'RESUME',
            action: function() {
                $.lt = Date.now() + 1000;
                $.setState('play');
            }
        });
        $.buttons.push(resumeButton);

        var menuButton = new $.Button({
            x: $.cw / 2 + 1,
            y: resumeButton.ey + 25,
            lockedWidth: 299,
            lockedHeight: 49,
            scale: 3,
            title: 'MENU',
            action: function() {
                $.mouse.down = 0;
                if (window.confirm('Are you sure you want to end this game and return to the menu?')) {
                    $.mousescreen();
                    $.setState('menu');
                }
            }
        });
        $.buttons.push(menuButton);
    }

    if (state == 'gameover') {
        $.mouse.down = 0;

        $.screenshot = $.ctxmg.getImageData(0, 0, $.cw, $.ch);
        var resumeButton = new $.Button({
            x: $.cw / 2 + 1,
            y: 426,
            lockedWidth: 299,
            lockedHeight: 49,
            scale: 3,
            title: 'PLAY AGAIN',
            action: function() {
                $.reset();
                $.audio.play('levelup');
                $.setState('play');
            }
        });
        $.buttons.push(resumeButton);

        var menuButton = new $.Button({
            x: $.cw / 2 + 1,
            y: resumeButton.ey + 25,
            lockedWidth: 299,
            lockedHeight: 49,
            scale: 3,
            title: 'MENU',
            action: function() {
                $.setState('menu');
            }
        });
        $.buttons.push(menuButton);

        $.storage['score'] = Math.max($.storage['score'], $.score);
        $.storage['level'] = Math.max($.storage['level'], $.level.current);
        $.storage['rounds'] += 1;
        $.storage['kills'] += $.kills;
        $.storage['bullets'] += $.bulletsFired;
        $.storage['powerups'] += $.powerupsCollected;
        $.storage['time'] += Math.floor($.elapsed);
        $.updateStorage();
    }

    // set state
    $.state = state;
};

$.setupStates = function() {
    $.states['menu'] = function() {
        $.clearScreen();
        $.updateScreen();

        var i = $.buttons.length;
        while (i--) {
            $.buttons[i].update(i)
        }
        i = $.buttons.length;
        while (i--) {
            $.buttons[i].render(i)
        }

        $.ctxmg.beginPath();
        var title = $.text({
            ctx: $.ctxmg,
            x: $.cw / 2,
            y: $.ch / 2 - 100,
            text: 'INDDIG MEDIA',
            hspacing: 2,
            vspacing: 1,
            halign: 'center',
            valign: 'bottom',
            scale: 6,
            snap: 1,
            render: 1
        });
        gradient = $.ctxmg.createLinearGradient(title.sx, title.sy, title.sx, title.ey);
        gradient.addColorStop(0, '#fff');
        gradient.addColorStop(1, '#999');
        $.ctxmg.fillStyle = gradient;
        $.ctxmg.fill();

        $.ctxmg.beginPath();
        var bottomInfo = $.text({
            ctx: $.ctxmg,
            x: $.cw / 2,
            y: $.ch - 172,
            text: 'CREATED BY INDDIG MEDIA',
            hspacing: 1,
            vspacing: 1,
            halign: 'center',
            valign: 'bottom',
            scale: 1,
            snap: 1,
            render: 1
        });
        $.ctxmg.fillStyle = '#666';
        $.ctxmg.fill();

    };

    $.states['stats'] = function() {
        $.clearScreen();

        $.ctxmg.beginPath();
        var statsTitle = $.text({
            ctx: $.ctxmg,
            x: $.cw / 2,
            y: 150,
            text: 'STATS',
            hspacing: 3,
            vspacing: 1,
            halign: 'center',
            valign: 'bottom',
            scale: 6,
            snap: 1,
            render: 1
        });
        var gradient = $.ctxmg.createLinearGradient(statsTitle.sx, statsTitle.sy, statsTitle.sx, statsTitle.ey);
        gradient.addColorStop(0, '#fff');
        gradient.addColorStop(1, '#999');
        $.ctxmg.fillStyle = gradient;
        $.ctxmg.fill();

        $.ctxmg.beginPath();
        var statKeys = $.text({
            ctx: $.ctxmg,
            x: $.cw / 2 - 10,
            y: statsTitle.ey + 39,
            text: 'BEST SCORE\nBEST LEVEL\nROUNDS PLAYED\nENEMIES KILLED\nBULLETS FIRED\nPOWERUPS COLLECTED\nTIME ELAPSED',
            hspacing: 1,
            vspacing: 17,
            halign: 'right',
            valign: 'top',
            scale: 2,
            snap: 1,
            render: 1
        });
        $.ctxmg.fillStyle = 'hsla(0, 0%, 100%, 0.5)';
        $.ctxmg.fill();

        $.ctxmg.beginPath();
        var statsValues = $.text({
            ctx: $.ctxmg,
            x: $.cw / 2 + 10,
            y: statsTitle.ey + 39,
            text: $.util.commas($.storage['score']) + '\n' +
                ($.storage['level'] + 1) + '\n' +
                $.util.commas($.storage['rounds']) + '\n' +
                $.util.commas($.storage['kills']) + '\n' +
                $.util.commas($.storage['bullets']) + '\n' +
                $.util.commas($.storage['powerups']) + '\n' +
                $.util.convertTime(($.storage['time'] * (1000 / 60)) / 1000),
            hspacing: 1,
            vspacing: 17,
            halign: 'left',
            valign: 'top',
            scale: 2,
            snap: 1,
            render: 1
        });
        $.ctxmg.fillStyle = '#fff';
        $.ctxmg.fill();

        var i = $.buttons.length;
        while (i--) {
            $.buttons[i].render(i)
        }
        i = $.buttons.length;
        while (i--) {
            $.buttons[i].update(i)
        }
    };

    $.states['credits'] = function() {
        $.clearScreen();

        $.ctxmg.beginPath();
        var creditsTitle = $.text({
            ctx: $.ctxmg,
            x: $.cw / 2,
            y: 100,
            text: 'CREDITS',
            hspacing: 3,
            vspacing: 1,
            halign: 'center',
            valign: 'bottom',
            scale: 10,
            snap: 1,
            render: 1
        });
        var gradient = $.ctxmg.createLinearGradient(creditsTitle.sx, creditsTitle.sy, creditsTitle.sx, creditsTitle.ey);
        gradient.addColorStop(0, '#fff');
        gradient.addColorStop(1, '#999');
        $.ctxmg.fillStyle = gradient;
        $.ctxmg.fill();

        $.ctxmg.beginPath();
        var creditKeys = $.text({
            ctx: $.ctxmg,
            x: $.cw / 2 - 10,
            y: creditsTitle.ey + 49,
            text: 'INDDIG MEDIA',
            hspacing: 1,
            vspacing: 17,
            halign: 'right',
            valign: 'top',
            scale: 2,
            snap: 1,
            render: 1
        });
        $.ctxmg.fillStyle = 'hsla(0, 0%, 100%, 0.5)';
        $.ctxmg.fill();

        $.ctxmg.beginPath();
        var creditValues = $.text({
            ctx: $.ctxmg,
            x: $.cw / 2 + 10,
            y: creditsTitle.ey + 49,
            text: 'INDDIG MEDIA',
            hspacing: 1,
            vspacing: 17,
            halign: 'left',
            valign: 'top',
            scale: 2,
            snap: 1,
            render: 1
        });
        $.ctxmg.fillStyle = '#fff';
        $.ctxmg.fill();

        var i = $.buttons.length;
        while (i--) {
            $.buttons[i].render(i)
        }
        i = $.buttons.length;
        while (i--) {
            $.buttons[i].update(i)
        }
    };

    $.states['play'] = function() {
        $.updateDelta();
        $.updateScreen();
        $.updateLevel();
        $.updatePowerupTimers();
        $.spawnEnemies();
        $.enemyOffsetMod += ($.slow) ? $.dt / 3 : $.dt;

        // update entities	
        var i = $.enemies.length;
        while (i--) {
            $.enemies[i].update(i)
        }
        i = $.explosions.length;
        while (i--) {
            $.explosions[i].update(i)
        }
        i = $.powerups.length;
        while (i--) {
            $.powerups[i].update(i)
        }
        i = $.particleEmitters.length;
        while (i--) {
            $.particleEmitters[i].update(i)
        }
        i = $.textPops.length;
        while (i--) {
            $.textPops[i].update(i)
        }
        i = $.levelPops.length;
        while (i--) {
            $.levelPops[i].update(i)
        }
        i = $.bullets.length;
        while (i--) {
            $.bullets[i].update(i)
        }
        $.hero.update();

        // render entities
        $.clearScreen();
        $.ctxmg.save();
        $.ctxmg.translate($.screen.x - $.rumble.x, $.screen.y - $.rumble.y);
        i = $.enemies.length;
        while (i--) {
            $.enemies[i].render(i)
        }
        i = $.explosions.length;
        while (i--) {
            $.explosions[i].render(i)
        }
        i = $.powerups.length;
        while (i--) {
            $.powerups[i].render(i)
        }
        i = $.particleEmitters.length;
        while (i--) {
            $.particleEmitters[i].render(i)
        }
        i = $.textPops.length;
        while (i--) {
            $.textPops[i].render(i)
        }
        i = $.bullets.length;
        while (i--) {
            $.bullets[i].render(i)
        }
        $.hero.render();
        $.ctxmg.restore();
        i = $.levelPops.length;
        while (i--) {
            $.levelPops[i].render(i)
        }
        $.renderInterface();
        $.renderMinimap();

        // handle gameover
        if ($.hero.life <= 0) {
            var alpha = (($.gameoverTick / $.gameoverTickMax) * 0.8);
            alpha = Math.min(1, Math.max(0, alpha));
            $.ctxmg.fillStyle = 'hsla(0, 100%, 0%, ' + alpha + ')';
            $.ctxmg.fillRect(0, 0, $.cw, $.ch);
            if ($.gameoverTick < $.gameoverTickMax) {
                $.gameoverTick += $.dt;
            } else {
                $.setState('gameover');
            }

            if (!$.gameoverExplosion) {
                $.audio.play('death');
                $.rumble.level = 25;
                $.explosions.push(new $.Explosion({
                    x: $.hero.x + $.util.rand(-10, 10),
                    y: $.hero.y + $.util.rand(-10, 10),
                    radius: 50,
                    hue: 0,
                    saturation: 0
                }));
                $.particleEmitters.push(new $.ParticleEmitter({
                    x: $.hero.x,
                    y: $.hero.y,
                    count: 45,
                    spawnRange: 10,
                    friction: 0.95,
                    minSpeed: 2,
                    maxSpeed: 20,
                    minDirection: 0,
                    maxDirection: $.twopi,
                    hue: 0,
                    saturation: 0
                }));
                for (var i = 0; i < $.powerupTimers.length; i++) {
                    $.powerupTimers[i] = 0;
                }
                $.gameoverExplosion = 1;
            }
        }

        // update tick	
        $.tick += $.dt;

        // listen for pause
        if ($.keys.pressed.p) {
            $.setState('pause');
        }

        // always listen for autofire toggle
        if ($.keys.pressed.f) {
            $.autofire = ~~!$.autofire;
            $.storage['autofire'] = $.autofire;
            $.updateStorage();
        }
    };

    $.states['pause'] = function() {
        $.clearScreen();
        $.ctxmg.putImageData($.screenshot, 0, 0);

        $.ctxmg.fillStyle = 'hsla(0, 0%, 0%, 0.4)';
        $.ctxmg.fillRect(0, 0, $.cw, $.ch);

        $.ctxmg.beginPath();
        var pauseText = $.text({
            ctx: $.ctxmg,
            x: $.cw / 2,
            y: $.ch / 2 - 50,
            text: 'PAUSED',
            hspacing: 3,
            vspacing: 1,
            halign: 'center',
            valign: 'bottom',
            scale: 8,
            snap: 1,
            render: 1
        });
        var gradient = $.ctxmg.createLinearGradient(pauseText.sx, pauseText.sy, pauseText.sx, pauseText.ey);
        gradient.addColorStop(0, '#fff');
        gradient.addColorStop(1, '#999');
        $.ctxmg.fillStyle = gradient;
        $.ctxmg.fill();

        var i = $.buttons.length;
        while (i--) {
            $.buttons[i].render(i)
        }
        i = $.buttons.length;
        while (i--) {
            $.buttons[i].update(i)
        }

        if ($.keys.pressed.p) {
            $.setState('play');
        }
    };

    $.states['gameover'] = function() {
        $.clearScreen();
        $.ctxmg.putImageData($.screenshot, 0, 0);

        var i = $.buttons.length;
        while (i--) {
            $.buttons[i].update(i)
        }
        i = $.buttons.length;
        while (i--) {
            $.buttons[i].render(i)
        }

        $.ctxmg.beginPath();
        var gameoverTitle = $.text({
            ctx: $.ctxmg,
            x: $.cw / 2,
            y: 150,
            text: 'GAME OVER',
            hspacing: 3,
            vspacing: 1,
            halign: 'center',
            valign: 'bottom',
            scale: 8,
            snap: 1,
            render: 1
        });
        var gradient = $.ctxmg.createLinearGradient(gameoverTitle.sx, gameoverTitle.sy, gameoverTitle.sx, gameoverTitle.ey);
        gradient.addColorStop(0, '#f22');
        gradient.addColorStop(1, '#b00');
        $.ctxmg.fillStyle = gradient;
        $.ctxmg.fill();

        $.ctxmg.beginPath();
        var gameoverStatsKeys = $.text({
            ctx: $.ctxmg,
            x: $.cw / 2 - 10,
            y: gameoverTitle.ey + 51,
            text: 'SCORE\nLEVEL\nKILLS\nBULLETS\nPOWERUPS\nTIME',
            hspacing: 1,
            vspacing: 17,
            halign: 'right',
            valign: 'top',
            scale: 2,
            snap: 1,
            render: 1
        });
        $.ctxmg.fillStyle = 'hsla(0, 0%, 100%, 0.5)';
        $.ctxmg.fill();

        $.ctxmg.beginPath();
        var gameoverStatsValues = $.text({
            ctx: $.ctxmg,
            x: $.cw / 2 + 10,
            y: gameoverTitle.ey + 51,
            text: $.util.commas($.score) + '\n' +
                ($.level.current + 1) + '\n' +
                $.util.commas($.kills) + '\n' +
                $.util.commas($.bulletsFired) + '\n' +
                $.util.commas($.powerupsCollected) + '\n' +
                $.util.convertTime(($.elapsed * (1000 / 60)) / 1000),
            hspacing: 1,
            vspacing: 17,
            halign: 'left',
            valign: 'top',
            scale: 2,
            snap: 1,
            render: 1
        });
        $.ctxmg.fillStyle = '#fff';
        $.ctxmg.fill();
    };
}

/*==============================================================================
Loop
==============================================================================*/
$.loop = function() {
    requestAnimFrame($.loop);

    // setup the pressed state for all keys
    for (var k in $.keys.state) {
        if ($.keys.state[k] && !$.okeys[k]) {
            $.keys.pressed[k] = 1;
        } else {
            $.keys.pressed[k] = 0;
        }
    }

    // run the current state
    $.states[$.state]();

    // always listen for mute toggle
    if ($.keys.pressed.m) {
        $.mute = ~~!$.mute;
        var i = $.audio.references.length;
        while (i--) {
            $.audio.references[i].volume = ~~!$.mute;
        }
        $.storage['mute'] = $.mute;
        $.updateStorage();
    }

    // move current keys into old keys
    $.okeys = {};
    for (var k in $.keys.state) {
        $.okeys[k] = $.keys.state[k];
    }
};

/*==============================================================================
Start Game on Load
==============================================================================*/
window.addEventListener('load', function() {
    document.documentElement.className += ' loaded';
    $.init();
});;

Zerion Mini Shell 1.0