r3-legacy/src/game-lib-entity-manager.js

624 lines
14 KiB
JavaScript

/**
* 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;
// }
// )
// }
// );
//
// };