/** * GameLib.EntityManager * @constructor */ GameLib.EntityManager = function() { this.id = GameLib.Utils.RandomId(); this.name = 'Entity Manager (' + this.id + ')'; this.entities = []; GameLib.Component.call( this, GameLib.Component.COMPONENT_ENTITY_MANAGER, { 'entities' : [GameLib.Entity] }, null ); this.loaded = []; this.dependencies = {}; this.subscriptions = []; this.registerCallbacks(); }; GameLib.EntityManager.prototype = Object.create(GameLib.Component.prototype); GameLib.EntityManager.prototype.constructor = GameLib.EntityManager; GameLib.EntityManager.prototype.createInstance = function() { return GameLib.EntityManager.Instance; }; /** * Creates an GameLib.Entity and adds it to entities array * @returns {*} */ GameLib.EntityManager.prototype.createEntity = function(name) { var apiEntity = new GameLib.API.Entity( null, name, null, null, this ); var entity = new GameLib.Entity( this.graphics, apiEntity ); this.entities.push(entity); GameLib.Event.Emit( GameLib.Event.NEW_ENTITY, { entity : entity } ); return entity; }; /** * Returns an entity by ID or null * @param id * @returns {*} */ GameLib.EntityManager.prototype.findEntityById = function(id) { return this.entities.reduce( function(result, entity){ if (entity.id === id) { result = entity; } return result; }, null ); }; GameLib.EntityManager.prototype.findHelperByObject = function(object) { return this.entities.reduce( function(result, entity) { var helpers = entity.getComponents(GameLib.D3.Helper); var helper = helpers.reduce( function(helperResult, tmpHelper) { if (tmpHelper.object === object) { helperResult = tmpHelper; } return helperResult; }, null ); if (helper) { result = helper; } return result; }, null ); }; /** * Adds an entity to this manager * @param entity GameLib.Entity */ GameLib.EntityManager.prototype.addEntity = function(entity) { entity.parentEntityManager = this; this.entities.push(entity); }; // /** // * Adds a system to this manager // * @param system GameLib.System // */ // GameLib.EntityManager.prototype.addSystem = function(system) { // this.systems.push(system); // }; /** * Returns entity by name * @param name * @returns {*} */ GameLib.EntityManager.prototype.queryByName = function(name) { return this.entities.reduce( function(result, entity){ if (entity.name === name) { result = entity; } return result; }, null ) }; /** * Removes an entity - do we remove all its components as well? * @param entity GameLib.D3.Entity * @returns boolean true if successful */ GameLib.EntityManager.prototype.removeEntity = function(entity) { var index = this.entities.indexOf(entity); if (index === -1) { console.log('failed to remove entity : ', entity); return false; } entity.parentEntityManager = null; this.entities.splice(index, 1); return true; }; /** * Returns all the entities with the following components * @param components GameLib.Component[] */ GameLib.EntityManager.prototype.query = function(components) { var entities = this.entities.reduce( function(result, entity) { var hasAllComponents = components.reduce( function(componentResult, component) { if (!entity.hasComponent(component)) { componentResult = false; } return componentResult; }, true ); if (hasAllComponents) { result.push(entity); } return result; }, [] ); return entities; }; /** * Returns all actual components of all entities that contain this component * @param constructor */ GameLib.EntityManager.prototype.queryComponents = function(constructor) { var entities = this.query([constructor]); var components = entities.reduce( function(result, entity){ var ecs = entity.getComponents(constructor); ecs.map(function(ec){ result.push(ec); }); return result; }, [] ); return components; }; /** * Converts a GameLib.Entity to GameLib.API.Entity * @returns {GameLib.API.EntityManager} */ GameLib.EntityManager.prototype.toApiObject = function() { var apiEntities = this.entities.map( function (entity) { return entity.toApiObject(); } ); // var apiSystems = this.systems.map( // function (system) { // return system.toApiObject(); // } // ); var apiEntityManager = new GameLib.API.EntityManager( this.id, this.name, apiEntities, // apiSystems, this.parentEntity ); return apiEntityManager; }; /** * Returns an EntityManager from an Object entity manager * @param graphics * @param objectEntityManager Object * @constructor */ GameLib.EntityManager.FromObject = function(objectEntityManager) { var apiEntityManager = GameLib.API.EntityManager.FromObject(objectEntityManager); var entityManager = new GameLib.EntityManager(apiEntityManager); return entityManager; }; /** * Defines what should happen when a parent scene changes * @param data */ GameLib.EntityManager.prototype.onParentSceneChange = function(data) { if ( data.object instanceof GameLib.D3.Mesh || data.object instanceof GameLib.D3.Light ) { /** * We remove the helper (if any) from the old scene and add it to the new scene */ var helper = this.findHelperByObject(data.object); if (helper) { if (data.originalScene && data.originalScene.instance) { data.originalScene.instance.remove(helper.instance); } data.newScene.instance.add(helper.instance); } /** * We remove the mesh from the old scene and add it to the new scene */ if (data.originalScene && data.originalScene.removeObject) { data.originalScene.removeObject(data.object); } data.newScene.addObject(data.object); /** * We inherit the parent entity of this new scene */ var originalEntity = null; var newEntity = null; if (data.object.hasOwnProperty('parentEntity')) { originalEntity = data.object.parentEntity } if (data.newScene.hasOwnProperty('parentEntity')) { newEntity = data.newScene.parentEntity; } var gui = null; if (originalEntity) { if (originalEntity.removeComponent) { if (helper) { originalEntity.removeComponent(helper); } originalEntity.removeComponent(data.object); } if (originalEntity.getFirstComponent) { gui = originalEntity.getFirstComponent(GameLib.GUI); if (gui) { gui.removeObject(data.object); gui.build(this); } } } if (newEntity) { if (newEntity.addComponent) { if (helper) { newEntity.addComponent(helper); } newEntity.addComponent(data.object); } if (newEntity.getFirstComponent) { gui = newEntity.getFirstComponent(GameLib.GUI); if (gui) { gui.addObject(data.object); gui.build(this); } } } } }; /** * Change parent entity * TODO: also change parent entity of children objects * @param data */ GameLib.EntityManager.prototype.onParentEntityChange = function(data) { if (data.originalEntity) { data.originalEntity.removeComponent(data.object); } data.newEntity.addComponent(data.object); // - ok not so cool - we may have parent entities of entities - // - so not all children should inherit the parent entity // data.object.buildIdToObject(); // // for (var property in data.object.idToObject) { // if (data.object.idToObject.hasOwnProperty(property)) { // if (data.object.idToObject[property].hasOwnProperty('parentEntity')) { // data.object.idToObject[property].parentEntity = data.newEntity; // } // } // } }; GameLib.EntityManager.prototype.componentCreated = function(data) { console.log('component created : ' + data.component.name); /** * If we notify ourselves - ignore it */ if (data.component === this) { return; } /** * Store this component into our 'loaded' list */ this.loaded.push(data.component); /** * Store the dependencies too */ data.component.dependencies.map(function(id) { if (GameLib.Utils.UndefinedOrNull(this.dependencies[id])) { this.dependencies[id] = []; } this.dependencies[id].push(data.component); }.bind(this)); /** * Now find all the dependencies of this component */ var dependencies = this.dependencies[data.component.id]; if (GameLib.Utils.UndefinedOrNull(dependencies)) { /** * We have no dependencies, so mark our component as loaded and create an instance */ data.component.loaded = true; data.component.instance = data.component.createInstance(); /** * If we have none, we are done and can exit */ return; } /** * Otherwise, now - for each dependency - update 'idToObject' and check if its loaded */ dependencies.map(function(component){ component.idToObject[data.component.id] = data.component; var loaded = true; for (var property in component.idToObject) { if ( component.idToObject.hasOwnProperty(property) && component.idToObject[property] === null ) { loaded = false } } component.loaded = loaded; if (component.loaded) { /** * Our component is fully loaded, time to create an instance of it */ component.instance = component.createInstance(); } }) }; /** * */ GameLib.EntityManager.prototype.registerCallbacks = function() { this.subscriptions.push( this.subscribe( GameLib.Event.PARENT_SCENE_CHANGE, this.onParentSceneChange ) ); this.subscriptions.push( this.subscribe( GameLib.Event.PARENT_ENTITY_CHANGE, this.onParentEntityChange ) ); this.subscriptions.push( this.subscribe( GameLib.Event.COMPONENT_CREATED, this.componentCreated ) ); }; /** * Links object Ids to actual objects * @param idToObject */ // GameLib.EntityManager.prototype.linkObjects = function(idToObject) { // // this.entities.map( // function(entity) { // entity.components.map( // function (componentId, index, array) { // if (componentId instanceof GameLib.Component) { // array[index] = componentId; // } else { // array[index] = idToObject[componentId]; // } // // Object.keys(array[index].linkedObjects).map( // function (propertyName) { // array[index][propertyName] = idToObject[array[index][propertyName]]; // } // ); // // array[index].loaded = true; // } // ) // } // ); // // };