Source: scene/Scenes.js

//------------------------------------------------------------------------------
// Constructor scope
//------------------------------------------------------------------------------

/** 
 * Creates a new instance of the class.
 * 
 * @constructor
 * 
 * @class
 * @classdesc
 * 
 * Represents a scene manager. Multiple scenes can exist simultaneously, but 
 * only one scene can be selected at a time. Rendering is limited to the 
 * selected scene.
 */
rune.scene.Scenes = function() {
    
    //--------------------------------------------------------------------------
    // Private properties
    //--------------------------------------------------------------------------

    /**
     * Current batch.
     *
     * @type {Array.<rune.scene.Scene>}
     * @private
     */
    this.m_cb = [];
    
    /**
     * Requested batch.
     *
     * @type {Array.<rune.scene.Scene>}
     * @private
     */
    this.m_rb = null;
    
    /**
     * Selected scene index.
     *
     * @type {number}
     * @private
     */
    this.m_cs = 0;
    
    /**
     * Requested scene index.
     *
     * @type {number}
     * @private
     */
    this.m_rs = 0;
};

//------------------------------------------------------------------------------
// Public prototype getter and setter methods
//------------------------------------------------------------------------------

/**
 * The number of scenes that exist within the current batch, i.e. how many 
 * Scene objects currently exist in memory. Note that a batch can contain 
 * multiple scenes, but only one scene can be selected at a time.
 *
 * @member {number} batchSize
 * @memberof rune.scene.Scenes
 * @instance
 * @readonly
 */
Object.defineProperty(rune.scene.Scenes.prototype, "batchSize", {
    /**
     * @this rune.scene.Scenes
     * @ignore
     */
    get : function() {
        return this.m_cb.length;
    },
});

/**
 * The currently selected scene.
 *
 * @member {rune.scene.Scene} selected
 * @memberof rune.scene.Scenes
 * @instance
 * @readonly
 */
Object.defineProperty(rune.scene.Scenes.prototype, "selected", {
    /**
     * @this rune.scene.Scenes
     * @ignore
     */
    get : function() {
        return this.m_cb[this.m_cs];
    },
});

//------------------------------------------------------------------------------
// Public prototype methods (API)
//------------------------------------------------------------------------------

/**
 * Searches for a specific scene in the current batch of scene objects. The 
 * search is based on scene name.
 *
 * @param {string} name The name of the searched scene.
 *
 * @returns {rune.scene.Scene}
 */
rune.scene.Scenes.prototype.find = function(name) {
    for (var i = 0; i < this.m_cb.length; i++) {
        if (this.m_cb[i]['name'] === name) {
            return this.m_cb[i];
        }
    }
    
    return null;
};

/**
 * Searching for a specific scene. The scene is automatically activated 
 * if it is found.
 *
 * @param {string} name The name of the searched scene.
 *
 * @returns {boolean}
 */
rune.scene.Scenes.prototype.findAndSelect = function(name) {
    for (var i = 0; i < this.m_cb.length; i++) {
        if (this.m_cb[i]['name'] === name) {
            this.select(i);
            return true;
        }
    }
    
    return false;
};

/**
 * Activates a batch of scenes. This process removes any previous scenes and 
 * frees up allocated memory, hence the process can not be undone.
 *
 * @param {Array.<rune.scene.Scene>} batch Batch to be loaded.
 *
 * @throws {Error} If the specified batch is invalid.
 *
 * @returns {undefined}
 */
rune.scene.Scenes.prototype.load = function(batch) {
    if (Array.isArray(batch) === true && batch.length > 0) {
        this.m_rb = batch;
    } else throw new Error("Invalid scene batch");
};

/**
 * Select a scene from the current batch. Note that only the selected scene is 
 * rendered. Inactive scenes remain unchanged during their time as inactive.
 *
 * @param {number} index List index of requested scene.
 *
 * @throws {RangeError} On invalid list index.
 *
 * @returns {undefined}
 */
rune.scene.Scenes.prototype.select = function(index) {
    var batch = (this.m_rb) ? this.m_rb : this.m_cb;
    if (index > -1 && index < batch.length) {
        if (this.m_cs != index) {
            this.m_rs  = index;
        }
    } else throw new RangeError("Invalid scene index");
};

//------------------------------------------------------------------------------
// Public prototype methods (ENGINE)
//------------------------------------------------------------------------------

/**
 * Updates current scenes.
 *
 * @param {number} step Current time step.
 *
 * @returns {undefined}
 * @ignore
 */
rune.scene.Scenes.prototype.update = function(step) {
    this.m_updateBatch(step);
    this.m_updateSelection(step);
    this.m_updateScenes(step);
};

/**
 * Renders the selected scene.
 *
 * @returns {undefined}
 * @ignore
 */
rune.scene.Scenes.prototype.render = function() {
    this.m_renderScenes();
};

/**
 * Deletes current scenes and frees up allocated memory.
 *
 * @returns {undefined}
 * @ignore
 */
rune.scene.Scenes.prototype.dispose = function() {
    this.m_disposeScenes();
};

//------------------------------------------------------------------------------
// Private prototype methods
//------------------------------------------------------------------------------

/**
 * Replaces the current batch with a new one.
 *
 * @throws {Error} Unless there is a new batch of scenes.
 *
 * @returns {undefined}
 * @private
 */
rune.scene.Scenes.prototype.m_iniScenes = function() {
    if (this.m_rb != null && this.m_rb.length > 0) {  
        
        this.m_disposeScenes();
        
        this.m_cb = this.m_rb;
        
        for (var i = 0; i < this.m_cb.length; i++) {
            this.m_cb[i].init();
        }
        
        this.m_rb = null;
        this.m_cb[0].onSelect(null);
    } else throw new Error();
};

/**
 * If there is a requested batch, it should replace the existing one.
 *
 * @param {number} step Current time step.
 *
 * @returns {undefined}
 * @private
 */
rune.scene.Scenes.prototype.m_updateBatch = function(step) {
    if (this.m_rb != null) {
        this.m_iniScenes();
    }
};

/**
 * Updates scene selection. Multiple scenes can be initiated simultaneously, 
 * but only one can be selected.
 *
 * @param {number} step Current time step.
 *
 * @returns {undefined}
 * @private
 */
rune.scene.Scenes.prototype.m_updateSelection = function(step) {
    if (this.m_cs != this.m_rs) {
        
        var o = this.m_cb[this.m_cs];
        var n = this.m_cb[this.m_rs];
        
        this.m_cs = this.m_rs;
        
        o['onDeselect'](n);
        n['onSelect'](o);
    }
};

/**
 * Updates active scenes.
 *
 * @param {number} step Current time step.
 *
 * @returns {undefined}
 * @private
 */
rune.scene.Scenes.prototype.m_updateScenes = function(step) {
    if (this.m_cb != null) {
        var l = this.m_cb.length;
        for (var i = 0; i < l; i++) {
            if (i == this.m_cs || this.m_cb[i].persistent == true) {
                this.m_cb[i].update(step);
            }
        }
    }
};

/**
 * Renders the selected scene.
 *
 * @returns {undefined}
 * @private
 */
rune.scene.Scenes.prototype.m_renderScenes = function() {
    if (this.m_cb != null && this.m_cb.length > this.m_cs) {
        this.m_cb[this.m_cs].render();
    }
};

/**
 * Removes allocated scenes.
 *
 * @returns {undefined}
 * @private
 */
rune.scene.Scenes.prototype.m_disposeScenes = function() {
    if (this.m_cb != null) {
        rune.system.Application['instance']['sounds'].clear();
        var i = this.m_cb.length;
        while (i--) {
            this.m_cb[i].dispose();
            this.m_cb[i] = null;
        }
        
        this.m = null;
        this.m_cs = 0;
    }
};