<commit_before>Molecule.module('Molecule.Game', function (require, p) {

    var MapFile = require('Molecule.MapFile'),
        Camera = require('Molecule.Camera'),
        Scene = require('Molecule.Scene'),
        Map = require('Molecule.Map'),
        ImageFile = require('Molecule.ImageFile'),
        AudioFile = require('Molecule.AudioFile'),
        Input = require('Molecule.Input'),
        Text = require('Molecule.Text'),
        physics = require('Molecule.Physics'),
        move = require('Molecule.Move'),
        calculateSpriteCollisions = require('Molecule.SpriteCollisions'),
        calculateMapCollisions = require('Molecule.MapCollisions'),
        Sprite = require('Molecule.Sprite'),
        MObject = require('Molecule.MObject')

    p.init = null;

    p.run = function () {};

    p.update = function (_exit, game) {
        var sprite,
            object;
        for (var i = 0; i < game.scene.objects.length; i++) {
            object = game.scene.objects[i];
            if (object.update) object.update();
        }
        for (var i = 0; i < game.scene.sprites.length; i++) {
            sprite = game.scene.sprites[i];
            sprite.update();
            sprite.flipUpdate();
            if (sprite.animation !== null && _exit)
                sprite.animation.nextFrame();
        }

        if (game.map) {
            game.map.update();
        }

    };

    p.loadResources = function (_interval, game) {
        var total = game.sprite.data.length + game.tilemap.map.length + game.audio.data.length;
        var total_loaded = game.sprite.counter + game.tilemap.getCounter() + game.audio.counter;
        if (game.imageFile.isLoaded() && game.mapFile.isLoaded() && game.audioFile.isLoaded()) {
            clearInterval(_interval);
            for (var i = 0; i < game.scene.sprites.length; i++) {
                game.scene.sprites[i].getAnimation();
            }
            p.init();
            p.loop(game);
        }
        game.context.save();
        game.context.fillStyle='#f8f8f8';
        game.context.fillRect(30, Math.round(game.height / 1.25), (game.width - (30 * 2)), 16);
        game.context.fillStyle='#ea863a';
        game.context.fillRect(30, Math.round(game.height / 1.25), (game.width - (30 * 2)) * (total_loaded / total), 16);
        game.context.restore();
    };

    p.removeSprites = function (sprites) {
        for (var i = sprites.length - 1; i >= 0; i--) {
            if (sprites[i].kill) {
                sprites.splice(i, 1);
            }
        }
    };

    p.resetCollisionState = function (sprites) {
        var sprite;
        for (var i = 0; i < sprites.length; i++) {
            sprite = sprites[i];
            sprite.collision.sprite.id = null;
            sprite.collision.sprite.left = false;
            sprite.collision.sprite.right = false;
            sprite.collision.sprite.up = false;
            sprite.collision.sprite.down = false;

            sprite.collision.map.tile = null;
            sprite.collision.map.left = false;
            sprite.collision.map.right = false;
            sprite.collision.map.up = false;
            sprite.collision.map.down = false;

            sprite.collision.boundaries.id = null;
            sprite.collision.boundaries.left = false;
            sprite.collision.boundaries.right = false;
            sprite.collision.boundaries.up = false;
            sprite.collision.boundaries.down = false;
        }
    };

    p.loop = function (game) {

        p.requestAnimFrame(function () {
            p.loop(game);
        });
        p.removeSprites(game.scene.sprites);
        p.update(null, game);
        if (game.status == 1) {
            var exit = false;
            physics(game);
            p.resetCollisionState(game.scene.sprites);
            while (!exit) {
                exit = move(game.scene.sprites);
                calculateMapCollisions(game);
                calculateSpriteCollisions(game);
                p.updateSpriteCollisionCheck(game.scene.sprites);
                if (game.camera.type === 1) {
                    game.camera.update(game.scene.sprites);
                }
                p.update(exit, game);
                p.checkBoundaries(game);
                game.resetMove();
            }
        }
        p.draw(game);
        p.run();
    };

    p.updateSpriteCollisionCheck = function (sprites) {
        var sprite;
        for (var i = 0; i < sprites.length; i++) {
            sprite = sprites[i];
            if (sprite.speed.check.x && sprite.speed.check.y) {
                sprite.resetMove();
            }
        }
    };

    p.checkBoundaries = function (game) {
        var sprite;
        for (var i = 0; i < game.scene.sprites.length; i++) {
            sprite = game.scene.sprites[i];
            if (game.boundaries.x !== null && sprite.collides.boundaries) {
                if (sprite.position.x - sprite.anchor.x + sprite.frame.offset.x < game.boundaries.x) {
                    sprite.position.x = game.boundaries.x + sprite.anchor.x - sprite.frame.offset.x;
                    sprite.collision.boundaries.left = true;
                    sprite.collision.boundaries.id = 0;
                    sprite.move.x = 0;
                    sprite.speed.x = 0;
                    sprite.speed.t.x = 0;
                    if(game.physics.gravity.x < 0) {
                        sprite.speed.gravity.x = 0;
                    }
                }
                if (sprite.position.x + sprite.frame.width - sprite.anchor.x - sprite.frame.offset.x > game.boundaries.x + game.boundaries.width) {
                    sprite.position.x = game.boundaries.x + game.boundaries.width - sprite.frame.width + sprite.anchor.x + sprite.frame.offset.x;
                    sprite.collision.boundaries.right = true;
                    sprite.collision.boundaries.id = 1;
                    sprite.move.x = 0;
                    sprite.speed.x = 0;
                    sprite.speed.t.x = 0;
                    if(game.physics.gravity.x > 0) {
                        sprite.speed.gravity.x = 0;
                    }
                }
            }
            if (game.boundaries.y !== null && sprite.collides.boundaries) {
                if (sprite.position.y - sprite.anchor.y + sprite.frame.offset.y < game.boundaries.y) {
                    sprite.position.y = game.boundaries.y + sprite.anchor.y - sprite.frame.offset.y;
                    sprite.collision.boundaries.up = true;
                    sprite.collision.boundaries.id = 2;
                    sprite.move.y = 0;
                    sprite.speed.y = 0;
                    sprite.speed.t.y = 0;
                    if(game.physics.gravity.y < 0) {
                        sprite.speed.gravity.y = 0;
                    }
                }
                if (sprite.position.y + sprite.frame.height - sprite.anchor.y - sprite.frame.offset.y > game.boundaries.y + game.boundaries.height) {
                    sprite.position.y = game.boundaries.y + game.boundaries.height - sprite.frame.height + sprite.anchor.y + sprite.frame.offset.y;
                    sprite.collision.boundaries.down = true;
                    sprite.collision.boundaries.id = 3;
                    sprite.move.y = 0;
                    sprite.speed.y = 0;
                    sprite.speed.t.y = 0;
                    if(game.physics.gravity.y > 0) {
                        sprite.speed.gravity.y = 0;
                    }
                }
            }
        }
    };

    p.draw = function (game) {
        game.context.clearRect(0, 0, game.canvas.width, game.canvas.height);
        if (game.map && game.map.visible) {
            game.map.draw(false);
        }
        for (var i = 0; i < game.scene.sprites.length; i++) {
            if (game.scene.sprites[i].visible) {
                game.scene.sprites[i].draw(false);
            }
        }
        for (var i = 0; i < game.scene.sprites.length; i++) {
            if (game.scene.sprites[i].visible) {
                game.scene.sprites[i].draw(true);
            }
        }
        if (game.map && game.map.visible) {
            game.map.draw(true);
        }
        for (var i = 0; i < game.scene.text.length; i++) {
            if (game.scene.text[i].visible) {
                game.scene.text[i].draw();
            }
        }
    };

    p.requestAnimFrame = (function () {
        var requestAnimFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (callback) {
            window.setTimeout(callback, 1000 / 60)
        };
        return requestAnimFrame.bind(window);
    })();

    p.start = function (game) {
        var interval = setInterval(function () {
            p.loadResources(interval, game);
        }, 100);
    };

    var Game = function (options) {

        // PROPERTIES
        this.canvas = null;
        this.context = null;
        this.next = {scene: null, fade: null};
        this.status = 1;
        this.timer = {loop: 60 / 1000, previus: null, now: null, fps: 60, frame: 0};
        this.sounds = {};
        this.sprites = {};
        this.tilemaps = {};

        // OPTIONS
        this.scale = options.scale || 1;
        this.width = options.width;
        this.height = options.height;

        // CANVAS
        this.canvas = document.createElement('canvas');
        this.canvas.setAttribute('id', 'canvas');
        this.canvas.width = options.width;
        this.canvas.height = options.height;
        this.canvas.style.width = options.width * this.scale + "px";
        this.canvas.style.height = options.height * this.scale + "px";
        this.context = this.canvas.getContext('2d');

        // GAME COMPONENTS
        this.camera = new Camera(this);
        this.scene = new Scene(this);
        this.map = new Map(this);
        this.input = new Input(this);

        // ASSET LOADING
        this.imageFile = new ImageFile(this);
        this.audioFile = new AudioFile(this);
        this.mapFile = new MapFile(this);

        // GAME SETTINGS
        this.physics = {gravity: {x: 0, y: 0}, friction: {x: 0, y: 0}};
        this.boundaries = {x: null, y: null, width: null, height: null};

        document.body.appendChild(this.canvas);

    };

    // TODO: Should clone the sprite instead
    // Will now load the sprite to the scene, but should maybe do that with a game object instead?
    Game.prototype.sprite = function (_id) {
        var loadedSprite = this.sprites[_id],
            s = new Sprite(loadedSprite.name, loadedSprite.frame.width, loadedSprite.frame.height);
        s.game = this;
        s.image = loadedSprite.image;
        s.id = _id;
        s.getAnimation();
        return s;

    };

    Game.prototype.sound = function (_id) {

        return this.sounds[_id];

    };

    Game.prototype.tilemap = function (_id) {

        return this.tilemaps[_id];

    };

    // TODO: Should not be able to add objects more than once
    Game.prototype.add = function (object, options) {
        if (typeof object === 'string') {
            var Obj = require(object),
                obj = new Obj(options);
            this.scene.objects.push(obj);
            if (obj.sprite) {
                obj.sprite.getAnimation();
                this.scene.sprites.push(obj.sprite);
            } else if (obj.sprites) {
                for (var sprite in obj.sprites) {
                    if (obj.sprites.hasOwnProperty(sprite)) {
                        obj.sprites[sprite].getAnimation();
                        this.scene.sprites.push(obj.sprites[sprite]);
                    }
                }
            }
            obj.init(this);

        } else if (object instanceof Array) {
            // Loop objects to add
        } else if (typeof object === 'function') {

            // Adds a game object to the game
            // Also adds the objects sprites
            var obj = new object(options);
            this.scene.objects.push(obj);

            if (obj.sprite) {
                this.scene.sprites.push(obj.sprite);
            } else if (obj.sprites) {
                for (var sprite in obj.sprites) {
                    if (obj.sprites.hasOwnProperty(sprite)) {
                        this.scene.sprites.push(obj.sprites[sprite]);
                    }
                }
            }

            obj.init(this);

        } else if (object instanceof Sprite) {
            // Adds a sprite directly to the game as an object and as sprite
            this.scene.objects.push(object);
            this.scene.sprites.push(object);

        } else if (object instanceof Map) {
            this.mapFile.set(object);
            // Make a more sensible method for doing this
        }

    };

    Game.prototype.get = function (id) {
        for (var x = 0; x < this.scene.objects.length; x++) {
            if (id === this.scene.objects[x].id) {
                return this.scene.objects[x];
            }
        }
    };

    Game.prototype.remove = function (obj) {

        if (!obj) {
            return;
        }

        if (obj instanceof Sprite) {
            return this.scene.sprites.splice(this.scene.sprites.indexOf(obj), 1);
        }

        if (obj instanceof Map) {
            return this.map = null;
        }

        this.scene.objects.splice(this.scene.objects.indexOf(obj), 1);
        if (obj.sprite) {
            this.scene.sprites.splice(this.scene.sprites.indexOf(obj.sprite), 1);
        } else if (obj.sprites) {
            for (var sprite in obj.sprites) {
                if (obj.sprites.hasOwnProperty(sprite)) {
                    this.scene.sprites.splice(this.scene.sprites.indexOf(obj.sprites[sprite]), 1);
                }
            }
        }

    };

    Game.prototype.text = function (_font, _x, _y, _title) {
        var t = new Text(_font, _x, _y, _title, this);
        this.scene.text.push(t);
        return t;
    };

    // Not in use, remove?
    Game.prototype.updateTimer = function () {
        this.timer.frame++;
        this.timer.now = new Date().getTime();
        if (this.timer.previus !== null)
            this.timer.loop = (this.timer.now - this.timer.previus) / 1000;
        if (this.timer.now - this.timer.previus >= 1000) {
            this.timer.previus = this.timer.now;
            this.timer.fps = this.timer.frame;
            this.timer.frame = 0;
        }
    };

    Game.prototype.play = function () {
        this.status = 1;
    };

    Game.prototype.stop = function () {
        this.status = 0;
    };

    Game.prototype.resetMove = function () {

        for (var i = 0; i < this.scene.sprites.length; i++) {
            this.scene.sprites[i].resetMove();
        }
        if (this.map) {
            this.map.resetScroll();
        }

        p.update(null, this);

    };

    Game.prototype.cameraUpdate = function (_exit) {
        for (var i = 0; i < this.scene.sprites.length; i++) {
            this.scene.sprites[i].update();
            this.scene.sprites[i].flipUpdate();
            if (this.scene.sprites[i].animation !== null && _exit)
                this.scene.sprites[i].animation.nextFrame();
        }
        if (this.map !== null)
            this.map.update();
    };

    Game.prototype.run = function () {
        p.run();
    };

    Game.prototype.start = function () {
        p.start(this);
    };

    Game.prototype.init = function (initializeModules, callback) {
        var self = this;
        p.init = function () {
            initializeModules();
            callback.call(self, self, require);
        }
    };

    Game.prototype.update = function (callback) {
        p.run = callback.bind(this, this, require);
    };

    Game.prototype.Object = MObject;

//    Game.prototype.cancelRequestAnimFrame = (function () {
//        return window.cancelAnimationFrame || window.webkitCancelRequestAnimationFrame || window.mozCancelRequestAnimationFrame || window.oCancelRequestAnimationFrame || window.msCancelRequestAnimationFrame || clearTimeout
//    })();

    return Game;

});<commit_msg>Fixed loading bar<commit_after>Molecule.module('Molecule.Game', function (require, p) {

    var MapFile = require('Molecule.MapFile'),
        Camera = require('Molecule.Camera'),
        Scene = require('Molecule.Scene'),
        Map = require('Molecule.Map'),
        ImageFile = require('Molecule.ImageFile'),
        AudioFile = require('Molecule.AudioFile'),
        Input = require('Molecule.Input'),
        Text = require('Molecule.Text'),
        physics = require('Molecule.Physics'),
        move = require('Molecule.Move'),
        calculateSpriteCollisions = require('Molecule.SpriteCollisions'),
        calculateMapCollisions = require('Molecule.MapCollisions'),
        Sprite = require('Molecule.Sprite'),
        MObject = require('Molecule.MObject')

    p.init = null;

    p.run = function () {};

    p.update = function (_exit, game) {
        var sprite,
            object;
        for (var i = 0; i < game.scene.objects.length; i++) {
            object = game.scene.objects[i];
            if (object.update) object.update();
        }
        for (var i = 0; i < game.scene.sprites.length; i++) {
            sprite = game.scene.sprites[i];
            sprite.update();
            sprite.flipUpdate();
            if (sprite.animation !== null && _exit)
                sprite.animation.nextFrame();
        }

        if (game.map) {
            game.map.update();
        }

    };

    p.loadResources = function (_interval, game) {
        var total = game.imageFile.data.length + game.mapFile.map.length + game.audioFile.data.length;
        var total_loaded = game.imageFile.counter + game.mapFile.getCounter() + game.audioFile.counter;
        if (game.imageFile.isLoaded() && game.mapFile.isLoaded() && game.audioFile.isLoaded()) {
            clearInterval(_interval);
            for (var i = 0; i < game.scene.sprites.length; i++) {
                game.scene.sprites[i].getAnimation();
            }
            p.init();
            p.loop(game);
        }
        game.context.save();
        game.context.fillStyle='#f8f8f8';
        game.context.fillRect(30, Math.round(game.height / 1.25), (game.width - (30 * 2)), 16);
        game.context.fillStyle='#ea863a';
        game.context.fillRect(30, Math.round(game.height / 1.25), (game.width - (30 * 2)) * (total_loaded / total), 16);
        game.context.restore();
    };

    p.removeSprites = function (sprites) {
        for (var i = sprites.length - 1; i >= 0; i--) {
            if (sprites[i].kill) {
                sprites.splice(i, 1);
            }
        }
    };

    p.resetCollisionState = function (sprites) {
        var sprite;
        for (var i = 0; i < sprites.length; i++) {
            sprite = sprites[i];
            sprite.collision.sprite.id = null;
            sprite.collision.sprite.left = false;
            sprite.collision.sprite.right = false;
            sprite.collision.sprite.up = false;
            sprite.collision.sprite.down = false;

            sprite.collision.map.tile = null;
            sprite.collision.map.left = false;
            sprite.collision.map.right = false;
            sprite.collision.map.up = false;
            sprite.collision.map.down = false;

            sprite.collision.boundaries.id = null;
            sprite.collision.boundaries.left = false;
            sprite.collision.boundaries.right = false;
            sprite.collision.boundaries.up = false;
            sprite.collision.boundaries.down = false;
        }
    };

    p.loop = function (game) {

        p.requestAnimFrame(function () {
            p.loop(game);
        });
        p.removeSprites(game.scene.sprites);
        p.update(null, game);
        if (game.status == 1) {
            var exit = false;
            physics(game);
            p.resetCollisionState(game.scene.sprites);
            while (!exit) {
                exit = move(game.scene.sprites);
                calculateMapCollisions(game);
                calculateSpriteCollisions(game);
                p.updateSpriteCollisionCheck(game.scene.sprites);
                if (game.camera.type === 1) {
                    game.camera.update(game.scene.sprites);
                }
                p.update(exit, game);
                p.checkBoundaries(game);
                game.resetMove();
            }
        }
        p.draw(game);
        p.run();
    };

    p.updateSpriteCollisionCheck = function (sprites) {
        var sprite;
        for (var i = 0; i < sprites.length; i++) {
            sprite = sprites[i];
            if (sprite.speed.check.x && sprite.speed.check.y) {
                sprite.resetMove();
            }
        }
    };

    p.checkBoundaries = function (game) {
        var sprite;
        for (var i = 0; i < game.scene.sprites.length; i++) {
            sprite = game.scene.sprites[i];
            if (game.boundaries.x !== null && sprite.collides.boundaries) {
                if (sprite.position.x - sprite.anchor.x + sprite.frame.offset.x < game.boundaries.x) {
                    sprite.position.x = game.boundaries.x + sprite.anchor.x - sprite.frame.offset.x;
                    sprite.collision.boundaries.left = true;
                    sprite.collision.boundaries.id = 0;
                    sprite.move.x = 0;
                    sprite.speed.x = 0;
                    sprite.speed.t.x = 0;
                    if(game.physics.gravity.x < 0) {
                        sprite.speed.gravity.x = 0;
                    }
                }
                if (sprite.position.x + sprite.frame.width - sprite.anchor.x - sprite.frame.offset.x > game.boundaries.x + game.boundaries.width) {
                    sprite.position.x = game.boundaries.x + game.boundaries.width - sprite.frame.width + sprite.anchor.x + sprite.frame.offset.x;
                    sprite.collision.boundaries.right = true;
                    sprite.collision.boundaries.id = 1;
                    sprite.move.x = 0;
                    sprite.speed.x = 0;
                    sprite.speed.t.x = 0;
                    if(game.physics.gravity.x > 0) {
                        sprite.speed.gravity.x = 0;
                    }
                }
            }
            if (game.boundaries.y !== null && sprite.collides.boundaries) {
                if (sprite.position.y - sprite.anchor.y + sprite.frame.offset.y < game.boundaries.y) {
                    sprite.position.y = game.boundaries.y + sprite.anchor.y - sprite.frame.offset.y;
                    sprite.collision.boundaries.up = true;
                    sprite.collision.boundaries.id = 2;
                    sprite.move.y = 0;
                    sprite.speed.y = 0;
                    sprite.speed.t.y = 0;
                    if(game.physics.gravity.y < 0) {
                        sprite.speed.gravity.y = 0;
                    }
                }
                if (sprite.position.y + sprite.frame.height - sprite.anchor.y - sprite.frame.offset.y > game.boundaries.y + game.boundaries.height) {
                    sprite.position.y = game.boundaries.y + game.boundaries.height - sprite.frame.height + sprite.anchor.y + sprite.frame.offset.y;
                    sprite.collision.boundaries.down = true;
                    sprite.collision.boundaries.id = 3;
                    sprite.move.y = 0;
                    sprite.speed.y = 0;
                    sprite.speed.t.y = 0;
                    if(game.physics.gravity.y > 0) {
                        sprite.speed.gravity.y = 0;
                    }
                }
            }
        }
    };

    p.draw = function (game) {
        game.context.clearRect(0, 0, game.canvas.width, game.canvas.height);
        if (game.map && game.map.visible) {
            game.map.draw(false);
        }
        for (var i = 0; i < game.scene.sprites.length; i++) {
            if (game.scene.sprites[i].visible) {
                game.scene.sprites[i].draw(false);
            }
        }
        for (var i = 0; i < game.scene.sprites.length; i++) {
            if (game.scene.sprites[i].visible) {
                game.scene.sprites[i].draw(true);
            }
        }
        if (game.map && game.map.visible) {
            game.map.draw(true);
        }
        for (var i = 0; i < game.scene.text.length; i++) {
            if (game.scene.text[i].visible) {
                game.scene.text[i].draw();
            }
        }
    };

    p.requestAnimFrame = (function () {
        var requestAnimFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (callback) {
            window.setTimeout(callback, 1000 / 60)
        };
        return requestAnimFrame.bind(window);
    })();

    p.start = function (game) {
        var interval = setInterval(function () {
            p.loadResources(interval, game);
        }, 100);
    };

    var Game = function (options) {

        // PROPERTIES
        this.canvas = null;
        this.context = null;
        this.next = {scene: null, fade: null};
        this.status = 1;
        this.timer = {loop: 60 / 1000, previus: null, now: null, fps: 60, frame: 0};
        this.sounds = {};
        this.sprites = {};
        this.tilemaps = {};

        // OPTIONS
        this.scale = options.scale || 1;
        this.width = options.width;
        this.height = options.height;

        // CANVAS
        this.canvas = document.createElement('canvas');
        this.canvas.setAttribute('id', 'canvas');
        this.canvas.width = options.width;
        this.canvas.height = options.height;
        this.canvas.style.width = options.width * this.scale + "px";
        this.canvas.style.height = options.height * this.scale + "px";
        this.context = this.canvas.getContext('2d');

        // GAME COMPONENTS
        this.camera = new Camera(this);
        this.scene = new Scene(this);
        this.map = new Map(this);
        this.input = new Input(this);

        // ASSET LOADING
        this.imageFile = new ImageFile(this);
        this.audioFile = new AudioFile(this);
        this.mapFile = new MapFile(this);

        // GAME SETTINGS
        this.physics = {gravity: {x: 0, y: 0}, friction: {x: 0, y: 0}};
        this.boundaries = {x: null, y: null, width: null, height: null};

        document.body.appendChild(this.canvas);

    };

    // TODO: Should clone the sprite instead
    // Will now load the sprite to the scene, but should maybe do that with a game object instead?
    Game.prototype.sprite = function (_id) {
        var loadedSprite = this.sprites[_id],
            s = new Sprite(loadedSprite.name, loadedSprite.frame.width, loadedSprite.frame.height);
        s.game = this;
        s.image = loadedSprite.image;
        s.id = _id;
        s.getAnimation();
        return s;

    };

    Game.prototype.sound = function (_id) {

        return this.sounds[_id];

    };

    Game.prototype.tilemap = function (_id) {

        return this.tilemaps[_id];

    };

    // TODO: Should not be able to add objects more than once
    Game.prototype.add = function (object, options) {
        if (typeof object === 'string') {
            var Obj = require(object),
                obj = new Obj(options);
            this.scene.objects.push(obj);
            if (obj.sprite) {
                obj.sprite.getAnimation();
                this.scene.sprites.push(obj.sprite);
            } else if (obj.sprites) {
                for (var sprite in obj.sprites) {
                    if (obj.sprites.hasOwnProperty(sprite)) {
                        obj.sprites[sprite].getAnimation();
                        this.scene.sprites.push(obj.sprites[sprite]);
                    }
                }
            }
            obj.init(this);

        } else if (object instanceof Array) {
            // Loop objects to add
        } else if (typeof object === 'function') {

            // Adds a game object to the game
            // Also adds the objects sprites
            var obj = new object(options);
            this.scene.objects.push(obj);

            if (obj.sprite) {
                this.scene.sprites.push(obj.sprite);
            } else if (obj.sprites) {
                for (var sprite in obj.sprites) {
                    if (obj.sprites.hasOwnProperty(sprite)) {
                        this.scene.sprites.push(obj.sprites[sprite]);
                    }
                }
            }

            obj.init(this);

        } else if (object instanceof Sprite) {
            // Adds a sprite directly to the game as an object and as sprite
            this.scene.objects.push(object);
            this.scene.sprites.push(object);

        } else if (object instanceof Map) {
            this.mapFile.set(object);
            // Make a more sensible method for doing this
        }

    };

    Game.prototype.get = function (id) {
        for (var x = 0; x < this.scene.objects.length; x++) {
            if (id === this.scene.objects[x].id) {
                return this.scene.objects[x];
            }
        }
    };

    Game.prototype.remove = function (obj) {

        if (!obj) {
            return;
        }

        if (obj instanceof Sprite) {
            return this.scene.sprites.splice(this.scene.sprites.indexOf(obj), 1);
        }

        if (obj instanceof Map) {
            return this.map = null;
        }

        this.scene.objects.splice(this.scene.objects.indexOf(obj), 1);
        if (obj.sprite) {
            this.scene.sprites.splice(this.scene.sprites.indexOf(obj.sprite), 1);
        } else if (obj.sprites) {
            for (var sprite in obj.sprites) {
                if (obj.sprites.hasOwnProperty(sprite)) {
                    this.scene.sprites.splice(this.scene.sprites.indexOf(obj.sprites[sprite]), 1);
                }
            }
        }

    };

    Game.prototype.text = function (_font, _x, _y, _title) {
        var t = new Text(_font, _x, _y, _title, this);
        this.scene.text.push(t);
        return t;
    };

    // Not in use, remove?
    Game.prototype.updateTimer = function () {
        this.timer.frame++;
        this.timer.now = new Date().getTime();
        if (this.timer.previus !== null)
            this.timer.loop = (this.timer.now - this.timer.previus) / 1000;
        if (this.timer.now - this.timer.previus >= 1000) {
            this.timer.previus = this.timer.now;
            this.timer.fps = this.timer.frame;
            this.timer.frame = 0;
        }
    };

    Game.prototype.play = function () {
        this.status = 1;
    };

    Game.prototype.stop = function () {
        this.status = 0;
    };

    Game.prototype.resetMove = function () {

        for (var i = 0; i < this.scene.sprites.length; i++) {
            this.scene.sprites[i].resetMove();
        }
        if (this.map) {
            this.map.resetScroll();
        }

        p.update(null, this);

    };

    Game.prototype.cameraUpdate = function (_exit) {
        for (var i = 0; i < this.scene.sprites.length; i++) {
            this.scene.sprites[i].update();
            this.scene.sprites[i].flipUpdate();
            if (this.scene.sprites[i].animation !== null && _exit)
                this.scene.sprites[i].animation.nextFrame();
        }
        if (this.map !== null)
            this.map.update();
    };

    Game.prototype.run = function () {
        p.run();
    };

    Game.prototype.start = function () {
        p.start(this);
    };

    Game.prototype.init = function (initializeModules, callback) {
        var self = this;
        p.init = function () {
            initializeModules();
            callback.call(self, self, require);
        }
    };

    Game.prototype.update = function (callback) {
        p.run = callback.bind(this, this, require);
    };

    Game.prototype.Object = MObject;

//    Game.prototype.cancelRequestAnimFrame = (function () {
//        return window.cancelAnimationFrame || window.webkitCancelRequestAnimationFrame || window.mozCancelRequestAnimationFrame || window.oCancelRequestAnimationFrame || window.msCancelRequestAnimationFrame || clearTimeout
//    })();

    return Game;

});<|endoftext|>


<commit_before>(function() {

  // Set up State enum
  var states = [
    'Uninitialized',
    'PlayerSetup',
    'ShowScenarioChoices',
    'PreTurn',
    'MidTurn',
    'PostTurn',
    'OpinionPhase',
    'VotingPhase',
    'FinalChoice',
    'GameEnd'
  ];

  var State = {};
  for (var i = 0; i < states.length; i++) {
    var stateName = states[ i ];
    State[ stateName ] = stateName;
  }

  var Game = Backbone.Model.extend({
    type: 'Game',

    defaults: {
      players: [],
      numberOfTurns: 0,
      currentTurn: -1,
      scenarioId: -1,
      scenario: undefined,
      cardsOnBoard: [],
      state: State.Uninitialized,
      shouldShowPostTurn: true,
      boardPercentage: 0.65,
      finalChoice: 0
    },

    constructor: function() {
      Backbone.Model.apply(this, arguments);

      this.loaded = new $.Deferred();

      var self = this;
      $.getJSON('data/scenarios.json', function(data) {
        self.scenarioDB = data;
        self.loaded.resolve(self);
      });
    },

    // Accessors
    currentPlayer: function() {
      var turn = this.get('currentTurn');
      var players = this.get('players');
      var currentPlayerIndex = turn % players.length;
      return players[ currentPlayerIndex ];
    },

    currentSentence: function() {
      var cards = this.get('cardsOnBoard');
      return cards
        .map(function(card) {
          return card.get('text');
        })
        .join(' ');
    },

    numberOfTurnsLeft: function() {
      return this.get('numberOfTurns') - this.get('currentTurn');
    },

    numberOfRounds: function() {
      var numberOfPlayers = this.get('players').length;
      if (numberOfPlayers > 0) {
        return this.get('numberOfTurns') / numberOfPlayers;
      }
      return 0;
    },

    currentRound: function() {
      var numberOfPlayers = this.get('players').length;
      if (numberOfPlayers > 0) {
        return ((this.get('currentTurn') / numberOfPlayers) | 0) + 1;
      }
      return 0;
    },

    // State changes
    beginPlayerSetup: function() {
      this.set('state', Game.State.PlayerSetup);
    },

    finishPlayerSetup: function(numberOfPlayers) {
      var scenarioId = this.get('scenarioId');
      if (scenarioId === -1) {
        scenarioId = (Math.random() * this.scenarioDB.length) | 0;
      }

      var p = this.get('boardPercentage');
      var scenario = new Scenario(this.scenarioDB[scenarioId]);
      var numCards = scenario.get('cards').length;
      var numberOfRounds = Math.max((numCards / numberOfPlayers * p) | 0, 1);
      var numberOfTurns = numberOfRounds * numberOfPlayers;
      var cardGroups = scenario.generateCardGroups(numberOfPlayers);

      var players = [];
      for (var i = 0; i < numberOfPlayers; i++) {
        var player = new Player({
          identifier: i
        });
        player.addCardsToHand(cardGroups[i]);
        players.push(player);
      }

      this.set({
        numberOfTurns: numberOfTurns,
        players: players,
        scenario: scenario
      });

      this.set('state', Game.State.ShowScenarioChoices);
    },

    beginNextTurn: function() {
      var turn = this.get('currentTurn') + 1;
      this.set('currentTurn', turn);

      if (!turn) {
        this.beginPreTurn();
      } else if (turn < this.get('numberOfTurns')) {
        if (this.get('shouldShowPostTurn')) {
          this.beginPostTurn();
        } else {
          this.beginPreTurn();
        }
      } else {
        // this.beginOpinionPhase();
        this.beginFinalChoice();
      }
    },

    beginPreTurn: function() {
      this.set('state', Game.State.PreTurn);
    },

    beginMidTurn: function() {
      this.midTurnTimeLimitMs = 15000;
      // Not sure that we need anything else here
      this.set('state', Game.State.MidTurn);
    },

    beginPostTurn: function() {
      this.set('state', Game.State.PostTurn);
    },

    finalizeTurn: function(sharedCard, cardsOnBoard) {
      var player = this.currentPlayer();
      player.playCardFromHand(sharedCard);
      this.set('cardsOnBoard', cardsOnBoard);
      this.beginNextTurn();
    },

    beginOpinionPhase: function() {
      this.set('state', Game.State.OpinionPhase);
    },

    beginVotingPhase: function() {
      this.set('state', Game.State.VotingPhase);
    },

    beginFinalChoice: function() {
      this.set('state', Game.State.FinalChoice);
    },

    lockInDecision: function(choice) {
      this.set('finalChoice', choice);
      this.set('state', Game.State.GameEnd);
    }
  });

  Game.State = State;
  window.Game = Game;
}());
<commit_msg>Increase time limit over course of game<commit_after>(function() {

  // Set up State enum
  var states = [
    'Uninitialized',
    'PlayerSetup',
    'ShowScenarioChoices',
    'PreTurn',
    'MidTurn',
    'PostTurn',
    'OpinionPhase',
    'VotingPhase',
    'FinalChoice',
    'GameEnd'
  ];

  var State = {};
  for (var i = 0; i < states.length; i++) {
    var stateName = states[ i ];
    State[ stateName ] = stateName;
  }

  var Game = Backbone.Model.extend({
    type: 'Game',

    defaults: {
      players: [],
      numberOfTurns: 0,
      currentTurn: -1,
      scenarioId: -1,
      scenario: undefined,
      cardsOnBoard: [],
      state: State.Uninitialized,
      shouldShowPostTurn: true,
      boardPercentage: 0.65,
      finalChoice: 0
    },

    constructor: function() {
      Backbone.Model.apply(this, arguments);

      this.loaded = new $.Deferred();

      var self = this;
      $.getJSON('data/scenarios.json', function(data) {
        self.scenarioDB = data;
        self.loaded.resolve(self);
      });
    },

    // Accessors
    currentPlayer: function() {
      var turn = this.get('currentTurn');
      var players = this.get('players');
      var currentPlayerIndex = turn % players.length;
      return players[ currentPlayerIndex ];
    },

    currentSentence: function() {
      var cards = this.get('cardsOnBoard');
      return cards
        .map(function(card) {
          return card.get('text');
        })
        .join(' ');
    },

    numberOfTurnsLeft: function() {
      return this.get('numberOfTurns') - this.get('currentTurn');
    },

    numberOfRounds: function() {
      var numberOfPlayers = this.get('players').length;
      if (numberOfPlayers > 0) {
        return this.get('numberOfTurns') / numberOfPlayers;
      }
      return 0;
    },

    currentRound: function() {
      var numberOfPlayers = this.get('players').length;
      if (numberOfPlayers > 0) {
        return ((this.get('currentTurn') / numberOfPlayers) | 0) + 1;
      }
      return 0;
    },

    // State changes
    beginPlayerSetup: function() {
      this.set('state', Game.State.PlayerSetup);
    },

    finishPlayerSetup: function(numberOfPlayers) {
      var scenarioId = this.get('scenarioId');
      if (scenarioId === -1) {
        scenarioId = (Math.random() * this.scenarioDB.length) | 0;
      }

      var p = this.get('boardPercentage');
      var scenario = new Scenario(this.scenarioDB[scenarioId]);
      var numCards = scenario.get('cards').length;
      var numberOfRounds = Math.max((numCards / numberOfPlayers * p) | 0, 1);
      var numberOfTurns = numberOfRounds * numberOfPlayers;
      var cardGroups = scenario.generateCardGroups(numberOfPlayers);

      var players = [];
      for (var i = 0; i < numberOfPlayers; i++) {
        var player = new Player({
          identifier: i
        });
        player.addCardsToHand(cardGroups[i]);
        players.push(player);
      }

      this.set({
        numberOfTurns: numberOfTurns,
        players: players,
        scenario: scenario
      });

      this.set('state', Game.State.ShowScenarioChoices);
    },

    beginNextTurn: function() {
      var turn = this.get('currentTurn') + 1;
      this.set('currentTurn', turn);

      if (!turn) {
        this.beginPreTurn();
      } else if (turn < this.get('numberOfTurns')) {
        if (this.get('shouldShowPostTurn')) {
          this.beginPostTurn();
        } else {
          this.beginPreTurn();
        }
      } else {
        // this.beginOpinionPhase();
        this.beginFinalChoice();
      }
    },

    beginPreTurn: function() {
      this.set('state', Game.State.PreTurn);
    },

    beginMidTurn: function() {
      var numberOfCards = this.get('cardsOnBoard').length;
      this.midTurnTimeLimitMs = 10000 + 1000 * numberOfCards;
      // Not sure that we need anything else here
      this.set('state', Game.State.MidTurn);
    },

    beginPostTurn: function() {
      this.set('state', Game.State.PostTurn);
    },

    finalizeTurn: function(sharedCard, cardsOnBoard) {
      var player = this.currentPlayer();
      player.playCardFromHand(sharedCard);
      this.set('cardsOnBoard', cardsOnBoard);
      this.beginNextTurn();
    },

    beginOpinionPhase: function() {
      this.set('state', Game.State.OpinionPhase);
    },

    beginVotingPhase: function() {
      this.set('state', Game.State.VotingPhase);
    },

    beginFinalChoice: function() {
      this.set('state', Game.State.FinalChoice);
    },

    lockInDecision: function(choice) {
      this.set('finalChoice', choice);
      this.set('state', Game.State.GameEnd);
    }
  });

  Game.State = State;
  window.Game = Game;
}());
<|endoftext|>


<commit_before>/* globals requestAnimationFrame, io */
const kbd = require('@dasilvacontin/keyboard')
const deepEqual = require('deep-equal')

const socket = io()

let myPlayerId = null
const myInputs = {
  LEFT_ARROW: false,
  RIGHT_ARROW: false,
  UP_ARROW: false,
  DOWN_ARROW: false
}

const ACCEL = 1 / 500

class GameClient {
  constructor () {
    this.players = {}
  }

  onWorldInit (serverPlayers) {
    this.players = serverPlayers
  }

  onPlayerMoved (player) {
    console.log(player)
    this.players[player.id] = player

    const delta = (Date.now() + clockDiff) - player.timestamp

        // increment position due to current velocity
        // and update our velocity accordingly
    player.x += player.vx * delta
    player.y += player.vy * delta

    const { inputs } = player
    if (inputs.LEFT_ARROW && !inputs.RIGHT_ARROW) {
      player.x -= ACCEL * Math.pow(delta, 2) / 2
      player.vx -= ACCEL * delta
    } else if (!inputs.LEFT_ARROW && inputs.RIGHT_ARROW) {
      player.x += ACCEL * Math.pow(delta, 2) / 2
      player.vx += ACCEL * delta
    }
    if (inputs.UP_ARROW && !inputs.DOWN_ARROW) {
      player.y -= ACCEL * Math.pow(delta, 2) / 2
      player.vy -= ACCEL * delta
    } else if (!inputs.UP_ARROW && inputs.DOWN_ARROW) {
      player.y += ACCEL * Math.pow(delta, 2) / 2
      player.vy += ACCEL * delta
    }
  }

  onPlayerDisconnected (playerId) {
    delete this.players[playerId]
  }

  logic (delta) {
    const vInc = ACCEL * delta
    for (let playerId in this.players) {
      const player = this.players[playerId]
      const { inputs } = player
      if (inputs.LEFT_ARROW) player.vx -= vInc
      if (inputs.RIGHT_ARROW) player.vx += vInc
      if (inputs.UP_ARROW) player.vy -= vInc
      if (inputs.DOWN_ARROW) player.vy += vInc

      player.x += player.vx * delta
      player.y += player.vy * delta
    }
  }
}
const game = new GameClient()

function updateInputs () {
  const oldInputs = Object.assign({}, myInputs)

  for (let key in myInputs) {
    myInputs[key] = kbd.isKeyDown(kbd[key])
  }

  if (!deepEqual(myInputs, oldInputs)) {
    socket.emit('move', myInputs)

    // update our local player' inputs so that we see instant change
    // (inputs get taken into account in logic simulation)
    const myPlayer = game.players[myPlayerId]
    myPlayer.inputs = Object.assign({}, myInputs)
  }
}

const canvas = document.createElement('canvas')
canvas.width = window.innerWidth
canvas.height = window.innerHeight
document.body.appendChild(canvas)

const ctx = canvas.getContext('2d')

function gameRenderer (game) {
  ctx.fillStyle = 'white'
  ctx.fillRect(0, 0, window.innerWidth, window.innerHeight)

  for (let playerId in game.players) {
    const { color, x, y } = game.players[playerId]
    ctx.fillStyle = color
    ctx.fillRect(x, y, 50, 50)
    if (playerId === myPlayerId) {
      ctx.strokeRect(x, y, 50, 50)
    }
  }
}

let past = Date.now()
function gameloop () {
  requestAnimationFrame(gameloop)

  const now = Date.now()
  const delta = now - past
  past = now

  updateInputs()
  game.logic(delta)
  gameRenderer(game)
}

let lastPingTimestamp
let clockDiff = 0 // how many ms the server is ahead from us
let ping = Infinity

function startPingHandshake () {
  lastPingTimestamp = Date.now()
  socket.emit('game:ping')
}
setInterval(startPingHandshake, 250)

socket.on('connect', function () {
  socket.on('world:init', function (serverPlayers, myId) {
    game.onWorldInit(serverPlayers)
    myPlayerId = myId
  })
  socket.on('playerMoved', game.onPlayerMoved.bind(game))
  socket.on('playerDisconnected', game.onPlayerDisconnected.bind(game))

  socket.on('game:pong', (serverNow) => {
    ping = (Date.now() - lastPingTimestamp) / 2
    clockDiff = (serverNow + ping) - Date.now()
    console.log({ ping, clockDiff })
  })
})

requestAnimationFrame(gameloop)
<commit_msg>Apply input approximately when the server does so<commit_after>/* globals requestAnimationFrame, io */
const kbd = require('@dasilvacontin/keyboard')
const deepEqual = require('deep-equal')

const socket = io()

let myPlayerId = null
const myInputs = {
  LEFT_ARROW: false,
  RIGHT_ARROW: false,
  UP_ARROW: false,
  DOWN_ARROW: false
}

const ACCEL = 1 / 500

class GameClient {
  constructor () {
    this.players = {}
  }

  onWorldInit (serverPlayers) {
    this.players = serverPlayers
  }

  onPlayerMoved (player) {
    console.log(player)
    this.players[player.id] = player

    const delta = (Date.now() + clockDiff) - player.timestamp

        // increment position due to current velocity
        // and update our velocity accordingly
    player.x += player.vx * delta
    player.y += player.vy * delta

    const { inputs } = player
    if (inputs.LEFT_ARROW && !inputs.RIGHT_ARROW) {
      player.x -= ACCEL * Math.pow(delta, 2) / 2
      player.vx -= ACCEL * delta
    } else if (!inputs.LEFT_ARROW && inputs.RIGHT_ARROW) {
      player.x += ACCEL * Math.pow(delta, 2) / 2
      player.vx += ACCEL * delta
    }
    if (inputs.UP_ARROW && !inputs.DOWN_ARROW) {
      player.y -= ACCEL * Math.pow(delta, 2) / 2
      player.vy -= ACCEL * delta
    } else if (!inputs.UP_ARROW && inputs.DOWN_ARROW) {
      player.y += ACCEL * Math.pow(delta, 2) / 2
      player.vy += ACCEL * delta
    }
  }

  onPlayerDisconnected (playerId) {
    delete this.players[playerId]
  }

  logic (delta) {
    const vInc = ACCEL * delta
    for (let playerId in this.players) {
      const player = this.players[playerId]
      const { inputs } = player
      if (inputs.LEFT_ARROW) player.vx -= vInc
      if (inputs.RIGHT_ARROW) player.vx += vInc
      if (inputs.UP_ARROW) player.vy -= vInc
      if (inputs.DOWN_ARROW) player.vy += vInc

      player.x += player.vx * delta
      player.y += player.vy * delta
    }
  }
}
const game = new GameClient()

function updateInputs () {
  const oldInputs = Object.assign({}, myInputs)

  for (let key in myInputs) {
    myInputs[key] = kbd.isKeyDown(kbd[key])
  }

  if (!deepEqual(myInputs, oldInputs)) {
    socket.emit('move', myInputs)

    // update our local player' inputs aproximately when the server
    // takes them into account
    const frozenInputs = Object.assign({}, myInputs)
    setTimeout(function () {
      const myPlayer = game.players[myPlayerId]
      myPlayer.inputs = frozenInputs
    }, ping)
  }
}

const canvas = document.createElement('canvas')
canvas.width = window.innerWidth
canvas.height = window.innerHeight
document.body.appendChild(canvas)

const ctx = canvas.getContext('2d')

function gameRenderer (game) {
  ctx.fillStyle = 'white'
  ctx.fillRect(0, 0, window.innerWidth, window.innerHeight)

  for (let playerId in game.players) {
    const { color, x, y } = game.players[playerId]
    ctx.fillStyle = color
    ctx.fillRect(x, y, 50, 50)
    if (playerId === myPlayerId) {
      ctx.strokeRect(x, y, 50, 50)
    }
  }
}

let past = Date.now()
function gameloop () {
  requestAnimationFrame(gameloop)

  const now = Date.now()
  const delta = now - past
  past = now

  updateInputs()
  game.logic(delta)
  gameRenderer(game)
}

let lastPingTimestamp
let clockDiff = 0 // how many ms the server is ahead from us
let ping = Infinity

function startPingHandshake () {
  lastPingTimestamp = Date.now()
  socket.emit('game:ping')
}
setInterval(startPingHandshake, 250)

socket.on('connect', function () {
  socket.on('world:init', function (serverPlayers, myId) {
    game.onWorldInit(serverPlayers)
    myPlayerId = myId
  })
  socket.on('playerMoved', game.onPlayerMoved.bind(game))
  socket.on('playerDisconnected', game.onPlayerDisconnected.bind(game))

  socket.on('game:pong', (serverNow) => {
    ping = (Date.now() - lastPingTimestamp) / 2
    clockDiff = (serverNow + ping) - Date.now()
    console.log({ ping, clockDiff })
  })
})

requestAnimationFrame(gameloop)
<|endoftext|>


<commit_before>class Game {
    constructor(canvas) {
        this.canvas = canvas;
        this.canvas.width = 640;
        this.canvas.height = 480;

        this.input = new Input(canvas);
        this.projectiles = new Pool();
        this.player = new Player(0, 0, this.input);
        this.player.currentWeapon = new Bow(this.projectiles, this);
        this.enemies = [];
        this.enemies.push(new Enemy(200, 200, this));
        this.oldTime = null;
        this.score = 0;
    }
    run() {
        requestAnimationFrame(this.frame.bind(this));
    }
    frame(newTime) {
        if (!this.oldTime) this.oldTime = newTime;
        var elapsedTime = (newTime - this.oldTime) / 1000;
        this.oldTime = newTime;
        this.update(elapsedTime);
        this.render();
        requestAnimationFrame(this.frame.bind(this));
    }
    update(elapsedTime) {
        this.player.update(elapsedTime);
        for (var i = 0; i < this.enemies.length; i++) {
            if (this.enemies[i].alive) {
                this.enemies[i].update(elapsedTime);
            }
        }
        for (var i = 0; i < this.projectiles.length; i++) {
            if (this.projectiles.get(i).alive) {
                this.projectiles.get(i).update(elapsedTime);
            }
        }
    }
    enemyKilled(enemy) {
        this.addScore(1);
    }
    findClosestEnemy(source) {
        var dist, min = Number.POSITIVE_INFINITY, closest;
        for (var i = 0; i < this.enemies.length; i++) {
            if (this.enemies[i].alive) {
                dist = Geometry.getDistance(source, this.enemies[i].position);
                if (dist < min) {
                    min = dist;
                    closest = this.enemies[i];
                }
            }
        }

        return closest;
    }
    isColliding(source, target) {
        return source.position.x + source.w > target.position.x && source.position.x < target.position.x + target.w
            && source.position.y + source.h > target.position.y && source.position.y < target.position.y + target.h;
    }
    addScore(value) {
        this.score += value;
    }
    render() {
        var context = this.canvas.getContext('2d');
        context.clearRect(0, 0, this.canvas.width, this.canvas.height);
        this.player.render(context);
        for (var i = 0; i < this.enemies.length; i++) {
            if (this.enemies[i].alive) {
                this.enemies[i].render(context);
            }
        }
        for (var i = 0; i < this.projectiles.length; i++) {
            if (this.projectiles.get(i).alive) {
                this.projectiles.get(i).render(context);
            }
        }
    }
}
<commit_msg>use pool for enemies<commit_after>class Game {
    constructor(canvas) {
        this.canvas = canvas;
        this.canvas.width = 640;
        this.canvas.height = 480;

        this.input = new Input(canvas);
        this.projectiles = new Pool();
        this.player = new Player(0, 0, this.input);
        this.enemies = new Pool();
        this.oldTime = null;
        this.score = 0;
    }
    run() {
        this.player.currentWeapon = new Bow(this.projectiles, this);
        this.enemies.push(new Enemy(200, 200, this));
        requestAnimationFrame(this.frame.bind(this));
    }
    frame(newTime) {
        if (!this.oldTime) this.oldTime = newTime;
        var elapsedTime = (newTime - this.oldTime) / 1000;
        this.oldTime = newTime;
        this.update(elapsedTime);
        this.render();
        requestAnimationFrame(this.frame.bind(this));
    }
    update(elapsedTime) {
        this.player.update(elapsedTime);
        for (var i = 0; i < this.enemies.length; i++) {
            if (this.enemies.get(i).alive) {
                this.enemies.get(i).update(elapsedTime);
            }
        }
        for (var i = 0; i < this.projectiles.length; i++) {
            if (this.projectiles.get(i).alive) {
                this.projectiles.get(i).update(elapsedTime);
            }
        }
    }
    enemyKilled(enemy) {
        this.addScore(1);
    }
    findClosestEnemy(source) {
        var dist, min = Number.POSITIVE_INFINITY, closest;
        for (var i = 0; i < this.enemies.length; i++) {
            if (this.enemies.get(i).alive) {
                dist = Geometry.getDistance(source, this.enemies.get(i).position);
                if (dist < min) {
                    min = dist;
                    closest = this.enemies.get(i);
                }
            }
        }

        return closest;
    }
    isColliding(source, target) {
        return source.position.x + source.w > target.position.x && source.position.x < target.position.x + target.w
            && source.position.y + source.h > target.position.y && source.position.y < target.position.y + target.h;
    }
    addScore(value) {
        this.score += value;
    }
    render() {
        var context = this.canvas.getContext('2d');
        context.clearRect(0, 0, this.canvas.width, this.canvas.height);
        this.player.render(context);
        for (var i = 0; i < this.enemies.length; i++) {
            if (this.enemies.get(i).alive) {
                this.enemies.get(i).render(context);
            }
        }
        for (var i = 0; i < this.projectiles.length; i++) {
            if (this.projectiles.get(i).alive) {
                this.projectiles.get(i).render(context);
            }
        }
    }
}
<|endoftext|>


<commit_before>var _ = require('lodash');
var urbanDictionary = require('./urbanDictionary');

var defaultData = {
  scores: {},
  activePlayers: [],
  blackCard: null,
  czar: null,
  hands: {},
  modes: {},
  decks: {},
  answers: [],
  handsize: 7
};

var Game = function (robot) {
  this.robot = robot;
  this.db = {};
};
module.exports = Game;

var hasProp = {}.hasOwnProperty;
var indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };

var blackBlank = "_____";
var blackCards = require('./blackcards.coffee');
var whiteCards = require('./whitecards.coffee');

Game.prototype.init = function (data) {
  this.initialized = true;
  data.cah = data.cah || _.cloneDeep(defaultData);
  this.db = _.defaults(data.cah, defaultData);

  this.db.decks.ud = [];

  this.shuffle();
};

Game.prototype.toggleMode = function (mode) {
  this.db.modes = this.db.modes || {};
  this.db.modes[mode] = !this.db.modes[mode];
  return this.db.modes[mode];
}

Game.prototype.message = function (message) {
  if (this.db.room) {
    this.robot.messageRoom('#' + this.db.room, message);
  }
};

Game.prototype.shuffle = function (color) {
  this.db.decks = this.db.decks || {};
  if (!color || color === 'black') {
    this.db.decks.black = _.shuffle(blackCards);
  }
  if (!color || color === 'white') {
    this.db.decks.white = _.shuffle(whiteCards);
  }
};

Game.prototype.random_black_card = function () {
  return this.db.decks.black[_.random(0, this.db.decks.black.length)];
};

Game.prototype.random_white_card = function () {
  return this.db.decks.white[_.random(0, this.db.decks.white.length)];
};

Game.prototype.checkMode = function (mode) {
  return this.db.modes && this.db.modes[mode];
};

Game.prototype.add_urban_dictionary_card = function () {
  var self = this;
  this.db.decks.ud = this.db.decks.ud || [];

  urbanDictionary.getRandomEntry(function (err, entry) {
    if (!err && entry && entry.term) {
      self.db.decks.ud.push(entry.term + '.');
      self.db.decks.ud = _.uniq(self.db.decks.ud);
    }
  });
};

Game.prototype.deal_card = function (color) {
  var next;

  if (color === 'white' && this.checkMode('urban-dictionary')) {
    var shouldPick = (_.random(0, this.db.handsize) <= 1);
    if (shouldPick && Array.isArray(this.db.decks.ud)) {
      this.add_urban_dictionary_card();
      next = this.db.decks.ud.shift();
    }
  }

  if (!next) {
    next = this.db.decks[color].shift();
  }

  if (!next) {
    this.message("Out of " + color + " cards, re-shuffling the deck");
    this.shuffle(color);
    return this.deal_card(color);
  }

  return next;
};

Game.prototype.fix_hands = function () {
  var cardArray, name, newCard, newHands, ref;
  newHands = {};
  ref = this.db.hands;
  for (name in ref) {
    if (!hasProp.call(ref, name)) continue;
    cardArray = ref[name];
    if (indexOf.call(this.db.activePlayers, name) >= 0) {
      while (cardArray.length < this.db.handsize) {
        cardArray.push(this.deal_card('white'));
      }
      newHands[name] = cardArray;
    }
  }
  return this.db.hands = newHands;
};

Game.prototype.set_room = function (res) {
  return this.db.room = this.db.room || res.message.room;
};

Game.prototype.add_player = function (playerName) {
  var cards, newCard;
  if (this.db.activePlayers && indexOf.call(this.db.activePlayers, playerName) < 0) {
    this.db.activePlayers.push(playerName);
  }
  if (this.db.scores[playerName] == null) {
    this.db.scores[playerName] = 0;
  }
  cards = [];
  while (cards.length < this.db.handsize) {
    cards.push(this.deal_card('white'));
  }
  this.db.hands[playerName] = cards;
  if (this.db.activePlayers.length === 1) {
    this.db.czar = playerName;
    return this.db.blackCard = this.deal_card('black');
  }
};

Game.prototype.removeAnswer = function (player) {
  _.remove(this.db.answers, function (answer) {
    return answer[0] === player;
  });
};

Game.prototype.remove_player = function (playerName) {
  var i = this.db.activePlayers.indexOf(playerName);
  if (i >= 0) {
    this.db.activePlayers.splice(i, 1);
  }
  if (this.db.hands[playerName] != null) {
    delete this.db.hands[playerName];
  }
  if (this.db.czar === playerName) {
    if (this.db.activePlayers.length === 0) {
      this.db.czar = null;
      return;
    }
    this.db.czar = _.sample(this.db.activePlayers);
    this.removeAnswer(this.db.czar);
  }
  this.removeAnswer(playerName);
};

Game.prototype.show_answers = function (res, force) {
  var answers, answers_n, cards, i, j, ref, responseString, status, submitters_n;
  answers = this.db.answers;
  answers_n = answers.length;
  submitters_n = this.db.activePlayers.length - 1;
  if (force || (answers_n >= submitters_n)) {
    responseString = "White card submissions:";
    for (i = j = 0, ref = answers_n; j < ref; i = j += 1) {
      cards = answers[i][1];
      responseString += "\n" + (i + 1) + ": " + (this.generate_phrase(this.db.blackCard, cards));
    }
    if (force) {
      return this.robot.messageRoom(this.sender(res), responseString);
    } else {
      return this.message(responseString + "\n\n*Time to choose, " + this.db.czar + "!*");
    }
  } else {
    return res.reply("NOPE, not everyone has responded yet!");
  }
};

Game.prototype.generate_phrase = function (blackCard, whiteCards) {
  var bi, blackBits, phrase, wi;
  phrase = "";
  blackBits = blackCard.split(blackBlank);
  if (blackBits.length === 1) {
    phrase += blackCard + " *" + whiteCards[0] + "*";
  } else {
    wi = 0;
    bi = 0;
    if (blackCard.substring(0, blackBlank.length) === blackBlank) {
      phrase += "*" + (whiteCards[0].match(/(.*[a-zA-Z0-9])[^a-zA-Z0-9]*$/i)[0]) + "*";
      wi = 1;
    }
    while (wi < whiteCards.length || bi < blackBits.length) {
      if (bi < blackBits.length) {
        phrase += blackBits[bi];
        bi++;
      }
      if (wi < whiteCards.length) {
        phrase += "*" + (whiteCards[wi].match(/(.*[a-zA-Z0-9])[^a-zA-Z0-9]*$/i)[0]) + "*";
        wi++;
      }
    }
  }
  return phrase;
};

Game.prototype.submit_answer = function (res, handIndices) {
  var card, cards, i, j, k, len, len1, playerHand;
  var playerName = this.sender(res);
  playerHand = this.db.hands[playerName];
  cards = [];
  for (j = 0, len = handIndices.length; j < len; j++) {
    i = handIndices[j];
    cards.push(playerHand[i]);
  }
  for (k = 0, len1 = cards.length; k < len1; k++) {
    card = cards[k];
    i = playerHand.indexOf(card);
    playerHand.splice(i, 1);
  }
  this.db.answers.push([playerName, cards]);
  if (this.db.answers.length === (this.db.activePlayers.length - 1)) {
    this.show_answers(res);
  }
};

Game.prototype.submit = function(res) {
  var expectedCount, i, j, k, l, m, numString, nums, ref, ref1, ref2, ref3;
  if (this.sender(res) === this.db.czar) {
    res.reply("You are currently the Card Czar!");
    return;
  }
  if (this.db.hands[this.sender(res)].length < this.db.handsize) {
    res.reply("You have already submitted cards for this round.");
    return;
  }
  numString = res.match[0].replace(/^[^\d]+/, '');
  nums = numString.split(" ");
  expectedCount = this.db.blackCard.split(blackBlank).length - 1;
  if (expectedCount === 0) {
    expectedCount = 1;
  }
  if (nums.length !== expectedCount) {
    return res.reply("You submitted " + nums.length + " cards, " + expectedCount + " expected.");
  } else {
    for (i = k = 0, ref = nums.length; k < ref; i = k += 1) {
      nums[i] = parseInt(nums[i], 10) - 1;
      if (nums[i] >= this.db.hands[this.sender(res)].length) {
        res.reply(nums[i] + " is not a valid card number.");
        return;
      }
    }
    for (i = l = 0, ref1 = nums.length; l < ref1; i = l += 1) {
      for (j = m = ref2 = i + 1, ref3 = nums.length; m < ref3; j = m += 1) {
        if (nums[i] === nums[j]) {
          res.reply("You cannot submit a single card more than once.");
          return;
        }
      }
    }
    res.reply("Submission accepted.");
    this.submit_answer(res, nums);
  }
};

Game.prototype.czar_choose_winner = function (answerIndex) {
  var answer, cards, czarIndex, j, len, ref, responseString, winner, winningPhrase;
  if (0 <= answerIndex && answerIndex < this.db.answers.length) {
    responseString = "Current round:";
    ref = this.db.answers;
    for (j = 0, len = ref.length; j < len; j++) {
      answer = ref[j];
      responseString += "\n" + answer[0] + ": " + (this.generate_phrase(this.db.blackCard, answer[1]));
    }
    winner = this.db.answers[answerIndex][0];
    cards = this.db.answers[answerIndex][1];
    winningPhrase = this.generate_phrase(this.db.blackCard, cards);
    responseString += "\n\n" + winner + " earns a point for\n*" + winningPhrase + "*";
    this.db.scores[winner] = Number(this.db.scores[winner]) + 1;

    if (this.db.scores[winner] >= 7) {
      // announce winner, then reset
      this.message("🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉\n\nTA DA! You've all lost to " + winner + "! I hope you're all ashamed! HAHAHAHA!\n\n🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉")
      this.reset({ czar: winner });
      return;
    }
  }
  this.db.answers = [];
  this.fix_hands();
  this.db.blackCard = this.deal_card('black');
  if (this.db.activePlayers.length === 0) {
    this.db.czar = null;
  } else if (this.db.czar == null) {
    this.db.czar = this.db.activePlayers[0];
  } else {
    czarIndex = this.db.activePlayers.indexOf(this.db.czar);
    if (czarIndex < 0 || czarIndex === this.db.activePlayers.length - 1) {
      this.db.czar = this.db.activePlayers[0];
    } else {
      this.db.czar = this.db.activePlayers[czarIndex + 1];
    }
  }
  return responseString + "\n\nNext round:\n" + this.game_state_string();
};

Game.prototype.reset = function (db) {
  var modes = this.db.modes;
  var players = this.db.activePlayers;
  this.db = _.cloneDeep(defaultData);
  this.db.modes = modes;
  this.db.activePlayers = players;
  this.db = _.defaults(db, this.db);
  if (!this.db.czar) {
    this.db.czar = _.sample(this.db.activePlayers.length);
  }
  this.shuffle();
  this.fix_hands();
  this.message('Starting a new game... now!');
  this.message(this.game_state_string());
};

Game.prototype.who_hasnt_answered = function () {
  var self = this;
  var responders = this.db.activePlayers.filter(function (p) {
    return p !== self.db.czar;
  });
  var answered = this.db.answers.map(function (a) {
    return a[0];
  });
  return _.difference(responders, answered);
};

Game.prototype.game_state_string = function () {
  if (this.db.czar == null) {
    return "Waiting for players.";
  } else {
    var remaining = this.who_hasnt_answered();
    var message = "*" + this.db.blackCard + "* [czar: " + this.db.czar + "]";

    message += "\n[" + this.db.answers.length + "/" + (this.db.activePlayers.length - 1) + "] _Waiting on: " + (remaining ? remaining.join(', ') : "nobodyz") + "_";

    return message;
  }
};

Game.prototype.sender = function (res) {
  return res.message.user.name;
};
<commit_msg>Shuffle answers. Might work.<commit_after>var _ = require('lodash');
var urbanDictionary = require('./urbanDictionary');

var defaultData = {
  scores: {},
  activePlayers: [],
  blackCard: null,
  czar: null,
  hands: {},
  modes: {},
  decks: {},
  answers: [],
  handsize: 7
};

var Game = function (robot) {
  this.robot = robot;
  this.db = {};
};
module.exports = Game;

var hasProp = {}.hasOwnProperty;
var indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };

var blackBlank = "_____";
var blackCards = require('./blackcards.coffee');
var whiteCards = require('./whitecards.coffee');

Game.prototype.init = function (data) {
  this.initialized = true;
  data.cah = data.cah || _.cloneDeep(defaultData);
  this.db = _.defaults(data.cah, defaultData);

  this.db.decks.ud = [];

  this.shuffle();
};

Game.prototype.toggleMode = function (mode) {
  this.db.modes = this.db.modes || {};
  this.db.modes[mode] = !this.db.modes[mode];
  return this.db.modes[mode];
}

Game.prototype.message = function (message) {
  if (this.db.room) {
    this.robot.messageRoom('#' + this.db.room, message);
  }
};

Game.prototype.shuffle = function (color) {
  this.db.decks = this.db.decks || {};
  if (!color || color === 'black') {
    this.db.decks.black = _.shuffle(blackCards);
  }
  if (!color || color === 'white') {
    this.db.decks.white = _.shuffle(whiteCards);
  }
};

Game.prototype.random_black_card = function () {
  return this.db.decks.black[_.random(0, this.db.decks.black.length)];
};

Game.prototype.random_white_card = function () {
  return this.db.decks.white[_.random(0, this.db.decks.white.length)];
};

Game.prototype.checkMode = function (mode) {
  return this.db.modes && this.db.modes[mode];
};

Game.prototype.add_urban_dictionary_card = function () {
  var self = this;
  this.db.decks.ud = this.db.decks.ud || [];

  urbanDictionary.getRandomEntry(function (err, entry) {
    if (!err && entry && entry.term) {
      self.db.decks.ud.push(entry.term + '.');
      self.db.decks.ud = _.uniq(self.db.decks.ud);
    }
  });
};

Game.prototype.deal_card = function (color) {
  var next;

  if (color === 'white' && this.checkMode('urban-dictionary')) {
    var shouldPick = (_.random(0, this.db.handsize) <= 1);
    if (shouldPick && Array.isArray(this.db.decks.ud)) {
      this.add_urban_dictionary_card();
      next = this.db.decks.ud.shift();
    }
  }

  if (!next) {
    next = this.db.decks[color].shift();
  }

  if (!next) {
    this.message("Out of " + color + " cards, re-shuffling the deck");
    this.shuffle(color);
    return this.deal_card(color);
  }

  return next;
};

Game.prototype.fix_hands = function () {
  var cardArray, name, newCard, newHands, ref;
  newHands = {};
  ref = this.db.hands;
  for (name in ref) {
    if (!hasProp.call(ref, name)) continue;
    cardArray = ref[name];
    if (indexOf.call(this.db.activePlayers, name) >= 0) {
      while (cardArray.length < this.db.handsize) {
        cardArray.push(this.deal_card('white'));
      }
      newHands[name] = cardArray;
    }
  }
  return this.db.hands = newHands;
};

Game.prototype.set_room = function (res) {
  return this.db.room = this.db.room || res.message.room;
};

Game.prototype.add_player = function (playerName) {
  var cards, newCard;
  if (this.db.activePlayers && indexOf.call(this.db.activePlayers, playerName) < 0) {
    this.db.activePlayers.push(playerName);
  }
  if (this.db.scores[playerName] == null) {
    this.db.scores[playerName] = 0;
  }
  cards = [];
  while (cards.length < this.db.handsize) {
    cards.push(this.deal_card('white'));
  }
  this.db.hands[playerName] = cards;
  if (this.db.activePlayers.length === 1) {
    this.db.czar = playerName;
    return this.db.blackCard = this.deal_card('black');
  }
};

Game.prototype.removeAnswer = function (player) {
  _.remove(this.db.answers, function (answer) {
    return answer[0] === player;
  });
};

Game.prototype.remove_player = function (playerName) {
  var i = this.db.activePlayers.indexOf(playerName);
  if (i >= 0) {
    this.db.activePlayers.splice(i, 1);
  }
  if (this.db.hands[playerName] != null) {
    delete this.db.hands[playerName];
  }
  if (this.db.czar === playerName) {
    if (this.db.activePlayers.length === 0) {
      this.db.czar = null;
      return;
    }
    this.db.czar = _.sample(this.db.activePlayers);
    this.removeAnswer(this.db.czar);
  }
  this.removeAnswer(playerName);
};

Game.prototype.show_answers = function (res, force) {
  var answers, answers_n, cards, i, j, ref, responseString, status, submitters_n;
  answers = _.shuffle(this.db.answers);
  answers_n = answers.length;
  submitters_n = this.db.activePlayers.length - 1;
  if (force || (answers_n >= submitters_n)) {
    responseString = "White card submissions:";
    for (i = j = 0, ref = answers_n; j < ref; i = j += 1) {
      cards = answers[i][1];
      responseString += "\n" + (i + 1) + ": " + (this.generate_phrase(this.db.blackCard, cards));
    }
    if (force) {
      return this.robot.messageRoom(this.sender(res), responseString);
    } else {
      return this.message(responseString + "\n\n*Time to choose, " + this.db.czar + "!*");
    }
  } else {
    return res.reply("NOPE, not everyone has responded yet!");
  }
};

Game.prototype.generate_phrase = function (blackCard, whiteCards) {
  var bi, blackBits, phrase, wi;
  phrase = "";
  blackBits = blackCard.split(blackBlank);
  if (blackBits.length === 1) {
    phrase += blackCard + " *" + whiteCards[0] + "*";
  } else {
    wi = 0;
    bi = 0;
    if (blackCard.substring(0, blackBlank.length) === blackBlank) {
      phrase += "*" + (whiteCards[0].match(/(.*[a-zA-Z0-9])[^a-zA-Z0-9]*$/i)[0]) + "*";
      wi = 1;
    }
    while (wi < whiteCards.length || bi < blackBits.length) {
      if (bi < blackBits.length) {
        phrase += blackBits[bi];
        bi++;
      }
      if (wi < whiteCards.length) {
        phrase += "*" + (whiteCards[wi].match(/(.*[a-zA-Z0-9])[^a-zA-Z0-9]*$/i)[0]) + "*";
        wi++;
      }
    }
  }
  return phrase;
};

Game.prototype.submit_answer = function (res, handIndices) {
  var card, cards, i, j, k, len, len1, playerHand;
  var playerName = this.sender(res);
  playerHand = this.db.hands[playerName];
  cards = [];
  for (j = 0, len = handIndices.length; j < len; j++) {
    i = handIndices[j];
    cards.push(playerHand[i]);
  }
  for (k = 0, len1 = cards.length; k < len1; k++) {
    card = cards[k];
    i = playerHand.indexOf(card);
    playerHand.splice(i, 1);
  }
  this.db.answers.push([playerName, cards]);
  if (this.db.answers.length === (this.db.activePlayers.length - 1)) {
    this.show_answers(res);
  }
};

Game.prototype.submit = function(res) {
  var expectedCount, i, j, k, l, m, numString, nums, ref, ref1, ref2, ref3;
  if (this.sender(res) === this.db.czar) {
    res.reply("You are currently the Card Czar!");
    return;
  }
  if (this.db.hands[this.sender(res)].length < this.db.handsize) {
    res.reply("You have already submitted cards for this round.");
    return;
  }
  numString = res.match[0].replace(/^[^\d]+/, '');
  nums = numString.split(" ");
  expectedCount = this.db.blackCard.split(blackBlank).length - 1;
  if (expectedCount === 0) {
    expectedCount = 1;
  }
  if (nums.length !== expectedCount) {
    return res.reply("You submitted " + nums.length + " cards, " + expectedCount + " expected.");
  } else {
    for (i = k = 0, ref = nums.length; k < ref; i = k += 1) {
      nums[i] = parseInt(nums[i], 10) - 1;
      if (nums[i] >= this.db.hands[this.sender(res)].length) {
        res.reply(nums[i] + " is not a valid card number.");
        return;
      }
    }
    for (i = l = 0, ref1 = nums.length; l < ref1; i = l += 1) {
      for (j = m = ref2 = i + 1, ref3 = nums.length; m < ref3; j = m += 1) {
        if (nums[i] === nums[j]) {
          res.reply("You cannot submit a single card more than once.");
          return;
        }
      }
    }
    res.reply("Submission accepted.");
    this.submit_answer(res, nums);
  }
};

Game.prototype.czar_choose_winner = function (answerIndex) {
  var answer, cards, czarIndex, j, len, ref, responseString, winner, winningPhrase;
  if (0 <= answerIndex && answerIndex < this.db.answers.length) {
    responseString = "Current round:";
    ref = this.db.answers;
    for (j = 0, len = ref.length; j < len; j++) {
      answer = ref[j];
      responseString += "\n" + answer[0] + ": " + (this.generate_phrase(this.db.blackCard, answer[1]));
    }
    winner = this.db.answers[answerIndex][0];
    cards = this.db.answers[answerIndex][1];
    winningPhrase = this.generate_phrase(this.db.blackCard, cards);
    responseString += "\n\n" + winner + " earns a point for\n*" + winningPhrase + "*";
    this.db.scores[winner] = Number(this.db.scores[winner]) + 1;

    if (this.db.scores[winner] >= 7) {
      // announce winner, then reset
      this.message("🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉\n\nTA DA! You've all lost to " + winner + "! I hope you're all ashamed! HAHAHAHA!\n\n🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉")
      this.reset({ czar: winner });
      return;
    }
  }
  this.db.answers = [];
  this.fix_hands();
  this.db.blackCard = this.deal_card('black');
  if (this.db.activePlayers.length === 0) {
    this.db.czar = null;
  } else if (this.db.czar == null) {
    this.db.czar = this.db.activePlayers[0];
  } else {
    czarIndex = this.db.activePlayers.indexOf(this.db.czar);
    if (czarIndex < 0 || czarIndex === this.db.activePlayers.length - 1) {
      this.db.czar = this.db.activePlayers[0];
    } else {
      this.db.czar = this.db.activePlayers[czarIndex + 1];
    }
  }
  return responseString + "\n\nNext round:\n" + this.game_state_string();
};

Game.prototype.reset = function (db) {
  var modes = this.db.modes;
  var players = this.db.activePlayers;
  this.db = _.cloneDeep(defaultData);
  this.db.modes = modes;
  this.db.activePlayers = players;
  this.db = _.defaults(db, this.db);
  if (!this.db.czar) {
    this.db.czar = _.sample(this.db.activePlayers.length);
  }
  this.shuffle();
  this.fix_hands();
  this.message('Starting a new game... now!');
  this.message(this.game_state_string());
};

Game.prototype.who_hasnt_answered = function () {
  var self = this;
  var responders = this.db.activePlayers.filter(function (p) {
    return p !== self.db.czar;
  });
  var answered = this.db.answers.map(function (a) {
    return a[0];
  });
  return _.difference(responders, answered);
};

Game.prototype.game_state_string = function () {
  if (this.db.czar == null) {
    return "Waiting for players.";
  } else {
    var remaining = this.who_hasnt_answered();
    var message = "*" + this.db.blackCard + "* [czar: " + this.db.czar + "]";

    message += "\n[" + this.db.answers.length + "/" + (this.db.activePlayers.length - 1) + "] _Waiting on: " + (remaining ? remaining.join(', ') : "nobodyz") + "_";

    return message;
  }
};

Game.prototype.sender = function (res) {
  return res.message.user.name;
};
<|endoftext|>