API Docs for:
Show:

File: src/object-manager.js

(function(root) {
    'use strict';

    /**
    * Manages a list of all objects and their tile positions.
    * Handles adding, removing, moving objects within the game.
    * @class ObjectManager
    * @constructor
    * @param {Game} game - Game instance this `ObjectManager` is attached to.
    * @param {Object} ObjectConstructor - Object constructor used to create new objects with `this.add()`.
    * @param {Number} [width] - Width of current map in tiles.
    * @param {Number} [height] - Height of current map in tiles.
    */
    var ObjectManager = function ObjectManager(game, ObjectConstructor, width, height) {
        this.game = game;
        this.ObjectConstructor = ObjectConstructor;
        this.objects = [];
        this.map = new RL.Array2d(width, height);
    };

    ObjectManager.prototype = {
        constructor: ObjectManager,

        /**
        * Game instance this obj is attached to.
        * @property game
        * @type {Game}
        */
        game: null,

        /**
        * Array containing all objects.
        * @property objects
        * @type {Array}
        */
        objects: null,

        /**
        * Array2d containing all objects by their current map tile coord
        * @property map
        * @type {Array2d}
        */
        map: null,

        /**
        * Retrieves an entity by map tile coords.
        * @method get
        * @param {Number} x - The tile map coord x.
        * @param {Number} y - The tile map coord y.
        * @return {Object|false}
        */
        get: function(x, y) {
            return this.map.get(x, y);
        },

        /**
        * Adds an object to the manager at given map tile coord.
        * If an object is already at this map tile coord it is removed from the manager completely.
        * @method add
        * @param {Number} x - The tile map coord x.
        * @param {Number} y - The tile map coord y.
        * @param {Object|String} obj - The Object being set at given coords. If obj is a string a new Object will be created using `this.makeNewObjectFromType(obj)`.
        * @return {Object} The added object.
        */
        add: function(x, y, obj) {
            if(typeof obj === 'string'){
                obj = this.makeNewObjectFromType(obj);
            }
            var existing = this.get(x, y);
            if(existing){
                this.remove(existing);
            }
            obj.game = this.game;
            obj.x = x;
            obj.y = y;
            this.objects.push(obj);
            this.map.set(x, y, obj);
            if(obj.onAdd){
                obj.onAdd();
            }
            return obj;
        },

        /**
        * Removes an object from the manager.
        * @method remove
        * @param {Object} object - The objectity to be removed.
        */
        remove: function(object) {
            this.map.remove(object.x, object.y);
            var index = this.objects.indexOf(object);
            this.objects.splice(index, 1);
            if(object.onRemove){
                object.onRemove();
            }
        },

        /**
        * Changes the position of an object already added to this objectManager.
        * @method move
        * @param {Number} x - The destination tile map coord x.
        * @param {Number} y - The destination tile map coord y.
        * @param {Obj} object - The objectity to be removed.
        */
        move: function(x, y, object) {
            var existing = this.get(object.x, object.y);
            if(existing !== object){
                throw new Error({error: 'Attempting to move object not in correct position in Object manager', x: x, y: y, object: object});
            }

            if(this.objects.indexOf(object) === -1){
                throw new Error({error: 'Attempting to move object not in Object manager', x: x, y: y, object: object});
            }
            this.map.remove(object.x, object.y);
            this.map.set(x, y, object);
            object.x = x;
            object.y = y;
        },

        /**
        * Resets this entityManager.
        * @method reset
        */
        reset: function() {
            this.objects = [];
            this.map.reset();
        },

        /**
        * Sets the size of the map to manage objects within.
        * @method setSize
        * @param {Number} width - Width of current map in tiles.
        * @param {Number} height - Height of current map in tiles.
        */
        setSize: function(width, height){
            this.map.setSize(width, height);
        },

        /**
        * Loads object data from an array of strings.
        * @method loadEntitiesFromArrayString
        * @param {Array} mapData - The array of strings to load.
        * @param {Object} charToType - An object mapping string characters to Object types see `this.makeNewObjectFromType()`. Characters in mapData not in charToType are ignored.
        * @param {String} [defaultType] - If set, all characters in `mapData` not found in `charToType` will be replaced by an object with `defaultType`.
        * @param {Bool} [replaceCurrentObjects=false] - If true current objects at positons of objects being added will be removed. Otherwise new objects at occupied positions will not be added.
        * @example

            // 'P' will be ignored and a floor tile will be placed at that position
            var mapData = [
                '####',
                '#..#',
                '#.Z#',
                '####',
            ],
            charToType = {
                'Z': 'zombie'
            };

            entityManager.loadTilesFromArrayString(mapData, charToType);
        *
        */
        loadFromArrayString: function(mapData, charToType, defaultType, replaceCurrentObjects){
            var _this = this,
                width = mapData[0].length,
                height = mapData.length;

            if(width !== this.map.width || height !== this.map.height){
                this.map.setSize(width, height);
            }

            // loop over each coord in the Array2d (val will be undefined)
            this.map.each(function(val, x, y){
                var currentObj = val;
                if(currentObj){
                    if(replaceCurrentObjects){
                        this.remove(currentObj);
                    } else {
                        return;
                    }
                }
                var char = mapData[y][x],
                    type = charToType[char];
                if(type === void 0 && defaultType){
                    type = defaultType;
                }
                if(type !== void 0){
                    var entity = _this.makeNewObjectFromType(type);
                    _this.add(x, y, entity);
                }
            });
        },

        /**
         * Creates a new object instance of given type.
         * @method makeNewObjectFromType
         * @param {String} type - The type to make the object
         * @return {Object}
         */
        makeNewObjectFromType: function(type){
            return new this.ObjectConstructor(this.game, type);
        },

        /**
        * Calls the `object.update()` method on all objects. Removes `object.dead == true` objects.
        * Typically called after a player has resolved their actions.
        * Not all object managers need to upade the objects they manage.
        * @param {Object} [excludeObject] - excludeObject will be skipped if found in `this.objects`. Typically used to skip the player object.
        * @method update
        */
        update: function(excludeObject) {
            // count down for performance and so we can remove things as we go
            for (var i = this.objects.length - 1; i >= 0; i--) {
                var obj = this.objects[i];
                if(excludeObject === obj){
                    continue;
                }
                if (obj.dead) {
                    this.remove(obj);
                    continue;
                }
                if(obj.update){
                    obj.update();
                }
            }
        },
    };

    root.RL.ObjectManager = ObjectManager;

}(this));