/** * GameLib.EntityManager * @constructor */ GameLib.EntityManager = function() { this.id = GameLib.Utils.RandomId(); this.name = 'Entity Manager (' + this.id + ')'; this.entities = []; this.loading = []; this.dependencies = {}; this.subscriptions = []; this.register = {}; this.checkRegister = []; this.registerCallbacks(); GameLib.Component.call( this, GameLib.Component.COMPONENT_ENTITY_MANAGER, { 'entities' : [GameLib.Entity] }, null ); }; 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 constructors (array of constructor or just a single constructor) */ GameLib.EntityManager.prototype.queryComponents = function(constructors) { return this.checkRegister.reduce( function(result, object) { if (constructors instanceof Array) { constructors.map( function(constructor) { if (object instanceof constructor) { result.push(object); } } ); } else { if (object instanceof constructors) { result.push(object); } } return result; }, [] ); }; /** * 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.link = function(component, data) { for (var property in component.linkedObjects) { if (component.linkedObjects.hasOwnProperty(property)) { if (component.linkedObjects[property] instanceof Array) { component[property] = component[property].map(function (entry) { if (entry === data.component.id) { return data.component; } else { return entry; } }); } else { if (component[property] && component[property] === data.component.id) { component[property] = data.component; } } } } }; GameLib.EntityManager.prototype.componentCreated = function(data) { //console.log('component created : ' + data.component.name); /** * Register this component immediately */ this.checkRegister.push(data.component); /** * If we notify ourselves - ignore it */ if (data.component === this) { return; } /** * Store this component into our 'loaded' list */ this.loading.push(data.component); /** * Store the dependencies too */ data.component.dependencies.map(function(id) { /** * Check if we already processed a component on which this component is dependent */ if (this.register.hasOwnProperty(id)) { console.log('found a component here'); /** * First add this to the 'idToObject' */ data.component.idToObject[id] = this.register[id]; /** * Remove this dependency from the dependency list */ var index = data.component.dependencies.indexOf(id); if (index === -1) { console.log('failed to locate dependency which should exist'); } data.component.dependencies.splice(index, 1); /** * Now link the component */ this.link(data.component, {component:this.register[id]}); } else { if (GameLib.Utils.UndefinedOrNull(this.dependencies[id])) { this.dependencies[id] = []; } /** * Don't store duplicate dependencies */ if (this.dependencies[id].indexOf(data.component === -1)) { this.dependencies[id].push(data.component); } } }.bind(this)); /** * Now find all the components which depend on this component */ if (GameLib.Utils.UndefinedOrNull(this.dependencies[data.component.id])) { /** * We don't know about any dependencies on this object - but maybe a component still * has to load which has dependencies to this object */ this.register[data.component.id] = data.component; } else { /** * Otherwise, now - for each dependency - update 'idToObject' and check if its loaded */ this.dependencies[data.component.id] = this.dependencies[data.component.id].reduce( function (result, component) { /** * Link the object to the component */ component.idToObject[data.component.id] = data.component; /** * Remove the actual dependency */ var index = component.dependencies.indexOf(data.component.id); if (index === -1) { console.warn('dependency mismatch'); } else { component.dependencies.splice(index, 1); } /** * Find the actual place where this object should be linked - and link them */ this.link(component, data); /** * If we now managed to link the objects, and this object has no more dependencies */ if (component.dependencies.length === 0) { component.loaded = true; } /** * Also remove this from the current dependency list */ return result; }.bind(this), [] ); delete this.dependencies[data.component.id]; } /** * Now if this new component has no dependencies, load it */ if (data.component.dependencies.length === 0) { data.component.loaded = true; } /** * Now check if all components are loaded, i.e., no more dependencies - if so - create their instance objects */ var loaded = true; for (var i = 0; i < this.loading.length; i++) { if (!this.loading[i].loaded) { loaded = false; break } } /** * All components loaded */ if (loaded) { this.loading.map(function(component){ component.instance = component.createInstance(); }); this.loading = []; } }; /** * */ 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; // } // ) // } // ); // // };