UNPKG

ecsy

Version:
255 lines (221 loc) 7.66 kB
import Query from "./Query.js"; import { componentRegistered } from "./Utils.js"; export class System { canExecute() { if (this._mandatoryQueries.length === 0) return true; for (let i = 0; i < this._mandatoryQueries.length; i++) { var query = this._mandatoryQueries[i]; if (query.entities.length === 0) { return false; } } return true; } getName() { return this.constructor.getName(); } constructor(world, attributes) { this.world = world; this.enabled = true; // @todo Better naming :) this._queries = {}; this.queries = {}; this.priority = 0; // Used for stats this.executeTime = 0; if (attributes && attributes.priority) { this.priority = attributes.priority; } this._mandatoryQueries = []; this.initialized = true; if (this.constructor.queries) { for (var queryName in this.constructor.queries) { var queryConfig = this.constructor.queries[queryName]; var Components = queryConfig.components; if (!Components || Components.length === 0) { throw new Error("'components' attribute can't be empty in a query"); } // Detect if the components have already been registered let unregisteredComponents = Components.filter( (Component) => !componentRegistered(Component) ); if (unregisteredComponents.length > 0) { throw new Error( `Tried to create a query '${ this.constructor.name }.${queryName}' with unregistered components: [${unregisteredComponents .map((c) => c.getName()) .join(", ")}]` ); } var query = this.world.entityManager.queryComponents(Components); this._queries[queryName] = query; if (queryConfig.mandatory === true) { this._mandatoryQueries.push(query); } this.queries[queryName] = { results: query.entities, }; // Reactive configuration added/removed/changed var validEvents = ["added", "removed", "changed"]; const eventMapping = { added: Query.prototype.ENTITY_ADDED, removed: Query.prototype.ENTITY_REMOVED, changed: Query.prototype.COMPONENT_CHANGED, // Query.prototype.ENTITY_CHANGED }; if (queryConfig.listen) { validEvents.forEach((eventName) => { if (!this.execute) { console.warn( `System '${this.getName()}' has defined listen events (${validEvents.join( ", " )}) for query '${queryName}' but it does not implement the 'execute' method.` ); } // Is the event enabled on this system's query? if (queryConfig.listen[eventName]) { let event = queryConfig.listen[eventName]; if (eventName === "changed") { query.reactive = true; if (event === true) { // Any change on the entity from the components in the query let eventList = (this.queries[queryName][eventName] = []); query.eventDispatcher.addEventListener( Query.prototype.COMPONENT_CHANGED, (entity) => { // Avoid duplicates if (eventList.indexOf(entity) === -1) { eventList.push(entity); } } ); } else if (Array.isArray(event)) { let eventList = (this.queries[queryName][eventName] = []); query.eventDispatcher.addEventListener( Query.prototype.COMPONENT_CHANGED, (entity, changedComponent) => { // Avoid duplicates if ( event.indexOf(changedComponent.constructor) !== -1 && eventList.indexOf(entity) === -1 ) { eventList.push(entity); } } ); } else { /* // Checking just specific components let changedList = (this.queries[queryName][eventName] = {}); event.forEach(component => { let eventList = (changedList[ componentPropertyName(component) ] = []); query.eventDispatcher.addEventListener( Query.prototype.COMPONENT_CHANGED, (entity, changedComponent) => { if ( changedComponent.constructor === component && eventList.indexOf(entity) === -1 ) { eventList.push(entity); } } ); }); */ } } else { let eventList = (this.queries[queryName][eventName] = []); query.eventDispatcher.addEventListener( eventMapping[eventName], (entity) => { // @fixme overhead? if (eventList.indexOf(entity) === -1) eventList.push(entity); } ); } } }); } } } } stop() { this.executeTime = 0; this.enabled = false; } play() { this.enabled = true; } // @question rename to clear queues? clearEvents() { for (let queryName in this.queries) { var query = this.queries[queryName]; if (query.added) { query.added.length = 0; } if (query.removed) { query.removed.length = 0; } if (query.changed) { if (Array.isArray(query.changed)) { query.changed.length = 0; } else { for (let name in query.changed) { query.changed[name].length = 0; } } } } } toJSON() { var json = { name: this.getName(), enabled: this.enabled, executeTime: this.executeTime, priority: this.priority, queries: {}, }; if (this.constructor.queries) { var queries = this.constructor.queries; for (let queryName in queries) { let query = this.queries[queryName]; let queryDefinition = queries[queryName]; let jsonQuery = (json.queries[queryName] = { key: this._queries[queryName].key, }); jsonQuery.mandatory = queryDefinition.mandatory === true; jsonQuery.reactive = queryDefinition.listen && (queryDefinition.listen.added === true || queryDefinition.listen.removed === true || queryDefinition.listen.changed === true || Array.isArray(queryDefinition.listen.changed)); if (jsonQuery.reactive) { jsonQuery.listen = {}; const methods = ["added", "removed", "changed"]; methods.forEach((method) => { if (query[method]) { jsonQuery.listen[method] = { entities: query[method].length, }; } }); } } } return json; } } System.isSystem = true; System.getName = function () { return this.displayName || this.name; }; export function Not(Component) { return { operator: "not", Component: Component, }; }