import {compose, EventEmitter} from '@avocado/core'; import {QuadTree, Rectangle, Vector} from '@avocado/math'; import {EntityCreatePacket} from '../packets/entity-create.packet'; import {EntityRemovePacket} from '../packets/entity-remove.packet'; import {Entity} from '../entity.synchronized'; const decorate = compose( EventEmitter, ); export class EntityList extends decorate(class {}) { constructor() { super(); this._afterDestructionTickers = []; this._entities = {}; this._entitiesJustAdded = []; this._entitiesJustRemoved = []; this._entityTickers = [] this._flatEntities = []; this._quadTree = new QuadTree(); } *[Symbol.iterator]() { for (const uuid in this._entities) { const entity = this._entities[uuid]; yield entity; } } acceptPacket(packet) { } addEntity(entity) { const uuid = entity.instanceUuid; // Already exists? if (this._entities[uuid]) { return; } this._entities[uuid] = entity; this._flatEntities.push(entity); this._entityTickers.push(entity.tick); if (AVOCADO_SERVER) { this._entitiesJustAdded.push(entity); } entity.setIntoList(this); entity.once('destroy', () => { this.removeEntity(entity); // In the process of destroying, allow entities to specify tickers that // must live on past destruction. const tickers = entity.invokeHookFlat('afterDestructionTickers'); for (let i = 0; i < tickers.length; i++) { const ticker = tickers[i]; this._afterDestructionTickers.push(ticker); } }); this.emit('entityAdded', entity); } destroy() { for (let i = 0; i < this._flatEntities.length; i++) { this._flatEntities[i].destroy(); } } findEntity(uuid) { if (uuid in this._entities) { return this._entities[uuid]; } } fromJSON(json) { for (let i = 0; i < json.length; i++) { const entityJSON = json[i]; if (entityJSON.uri) { Entity.read(entityJSON.uri).then((readJSON) => { this.addEntity(new Entity(readJSON, entityJSON)); }); } else { this.addEntity(new Entity(json[i])); } } } packets(informed) { const packets = []; return packets; } get quadTree() { return this._quadTree; } removeEntity(entity) { const uuid = entity.instanceUuid; if (!(uuid in this._entities)) { return; } if (AVOCADO_SERVER) { this._entitiesJustRemoved.push(entity); } entity.removeFromList(); delete this._entities[uuid]; this._flatEntities.splice(this._flatEntities.indexOf(entity), 1); this._entityTickers.splice(this._entityTickers.indexOf(entity.tick), 1); this.emit('entityRemoved', entity); } tick(elapsed) { // Run after destruction tickers. if (this._afterDestructionTickers.length > 0) { this.tickAfterDestructionTickers(elapsed); } // Run normal tickers. this.tickEntities(elapsed) } tickAfterDestructionTickers(elapsed) { const finishedTickers = []; for (let i = 0; i < this._afterDestructionTickers.length; ++i) { const ticker = this._afterDestructionTickers[i]; if (ticker(elapsed)) { finishedTickers.push(ticker); } } for (let i = 0; i < finishedTickers.length; ++i) { const ticker = finishedTickers[i]; const index = this._afterDestructionTickers.indexOf(ticker); this._afterDestructionTickers.splice(index, 1); } } tickEntities(elapsed) { for (let i = 0; i < this._entityTickers.length; i++) { this._entityTickers[i](elapsed); } } toJSON() { const json = []; for (let i = 0; i < this._flatEntities.length; i++) { json.push(this._flatEntities[i].mergeDiff()); } return json; } visibleEntities(query) { const entities = []; const entitiesChecked = []; const quadTree = this._quadTree; const nodes = quadTree.search(query); // Check all nodes. for (let i = 0; i < nodes.length; ++i) { const node = nodes[i]; const entity = node.data[2]; const aabb = node.data[3]; if (-1 === entitiesChecked.indexOf(entity)) { entitiesChecked.push(entity); // Make sure the AABB is actually in the query due to expansion. if (entity.isVisible && Rectangle.intersects(query, aabb)) { entities.push(entity); } } } return entities; } visibleEntitiesWithUri(query, uri) { return this.visibleEntities(query).filter((entity) => { return entity.uri === uri; }); } } export {EntityListView} from './view';