API Docs for: 1.0.1

File: js\grape\game\game.js

define(['../class', '../env', '../etc/event-emitter', './game-loop', './input', './scene'], function (Class, Env, EventEmitter, GameLoop, Input, Scene) {
    /**
     * A class that represents a game. You can create and run multiple games in a single page.
     *
     * @class Grape.Game
     * @uses Grape.EventEmitter
     * @constructor
     * @param {Object} opts Initial properties
     */
    return Class('Game', EventEmitter, {
        init: function (opts) {
            opts = opts || {};
            /**
             *  The scene which starts when (re)starting the game.
             * It should be a constructor or a function returning with the scene, not an instantiated scene.
             *
             * @property initialScene
             * @type {Function}
             * @default Grape.Scene
             */
            this.initialScene = opts.initialScene || Scene;
            /* istanbul ignore else */
            if (opts.hasOwnProperty('container')) { //null and undefined too
                /**
                 * The dom element which serves as the screen of the game. The size of the container is not manipulated
                 * by the engine, therefore you should set the size of it. The engine handles when this "screen size"
                 * changes and updates the displayed views. The container can be an id of a html element or a html
                 * element itself.
                 *
                 * @property container
                 * @type {String|HTMLElement}
                 * @default document.body
                 */
                this.container = opts.container;
            } else if (Env.browser) {
                this.container = document.body;
            }
            this.gameLoop = this.createGameLoop();
            /* istanbul ignore else */
            if (Env.browser) {
                this.input = new Input({reservedKeys: opts.reservedKeys});
            }
        },
        /**
         * This method is called once when the game is created. If you want to use a custom game loop for your game, you
         * can override this method.
         *
         * @method createGameLoop
         * @return {Grape.GameLoop} The game loop
         */
        createGameLoop: function () {
            return new GameLoop(this);
        },
        /**
         * Starts the game. Initializes the game screen, the input system, and the game loop.
         *
         * @method start
         * @param {Grape.Scene} [scene] Initial scene, which overrides the initialScene property.
         */
        'final start': function (scene) {
            if (this.gameLoop.isRunning()) {
                throw new Error('already running');
            }

            //initialize screen
            /* istanbul ignore else */
            if (Env.browser) {
                if (typeof this.container === 'string') {
                    this.container = document.getElementById(this.container);
                }
                if (!this.container) {
                    throw new Error('Container does not exists!');
                }
                this.container.innerHTML = ''; //todov2 test restart
                this._screen = document.createElement('div');
                this._screen.style.position = 'relative';
                this._screen.style.float = 'left';
                this._screen.style.width = '100%';
                this._screen.style.height = '100%';
                this._screen.style.overflow = 'hidden';
                this.container.appendChild(this._screen);
            }

            this._starting = true; //startScene can run now
            /* istanbul ignore else */
            if (this.input) {
                this.input._start(this._screen);
            }
            scene = scene || this.initialScene;
            this.startScene(typeof scene === 'function' ? new scene() : scene);
            this._starting = false;
            this.gameLoop.start();
        },
        /**
         * Stops the game.
         *
         * @method stop
         */
        'final stop': function () { //TODOv2 remove screen
            this.gameLoop.stop();
            /* istanbul ignore else */
            if (this.input) {
                this.input._stop();
            }
            /**
             * Fired when the stop() method is called.
             *
             * @event stop
             */
            this.emit('stop');
        },
        /**
         * Starts a new scene in the game.
         *
         * @method startScene
         * @param {Scene} scene The scene
         */
        startScene: function (scene) {
            if (!this._starting && !this.gameLoop.isRunning()) {
                throw new Error('Game is not running! You can game.start(scene).');
            }

            if (scene._game) {
                throw new Error('Scene already started!');
            }
            if (this.scene) {
                /**
                 * Fired to the scene when the scene is stopped.
                 *
                 * @event stop (scene)
                 */
                this.scene.emit('stop');
                this.scene._game = null;
            }

            scene._game = this;
            this.scene = scene;
            /**
             * Emitted to the scene when started. The parameter is the game.
             *
             * @event start (scene)
             */
            scene.emit('start', this);
        },
        /**
         * Called by the game loop in each frame.
         *
         * @method frame
         */
        frame: function () {
            /**
             * Fired in each frame
             *
             * @event frame
             */
            this.emit('frame');
            /**
             * Fired to the actual scene in each frame
             *
             * @event frame (scene)
             */
            this.scene.emit('frame');
            /* istanbul ignore else */
            if (this.input) {
                this.input._emitEvents(this.scene); //TODOv2 is it wrong? ie. keyPress.none?
            }
        },
        /**
         * Called by the game loop when at least one frame was executed since the last screen update.
         *
         * @method render
         */
        render: function () {
            /* istanbul ignore else */
            if (Env.browser) {
                /**
                 * Fired to the current scene in each render frame
                 *
                 * @event renderLayer (scene)
                 */
                this.scene.emit('renderLayer');
            }
        },
        /**
         * Returns the game screen which is appended to the container.
         *
         * @method getScreen
         * @return {HTMLElement} Screen element
         */
        getScreen: function () {
            return this._screen;
        },

        /**
         * Returns the width of the screen.
         *
         * @method getScreenWidth
         * @return {Number} Width
         */
        getScreenWidth: function () {
            /* istanbul ignore next */
            return this._screen ? this._screen.offsetWidth : 1;
        },

        /**
         * Returns the height of the screen.
         *
         * @method getScreenHeight
         * @return {Number} Height
         */
        getScreenHeight: function () {
            /* istanbul ignore next */
            return this._screen ? this._screen.offsetHeight : 1;
        },

        /**
         * Sets the cursor style for the screen.
         *
         * @method setCursor
         * @param {String} cursor new cursor style
         */
        setCursor: /* istanbul ignore next */ function (cursor) {
            if (!this._screen) {
                return;
            }
            this._screen.style.cursor = cursor;
        },
        /**
         * Returns the current scene.
         *
         * @method getScene
         * @return {Grape.Scene}
         */
        getScene: function () {
            return this.scene; //todov2 rename to _scene
        },

        /**
         * This method is called by the game loop to determine what is the required FPS (Frames Per Second) for the
         * game. By default, this is decided by the "fps" property of the current scene.
         *
         * @method getRequiredFps
         * @return {Number} FPS
         */
        getRequiredFps: function () {
            return this.scene.fps;
        }
    });
});