import * as I from 'immutable'; import mapValues from 'lodash.mapvalues'; import {arrayUnique, compose} from '@avocado/core'; import {QuadTree, Rectangle, Vector} from '@avocado/math'; import {EventEmitter} from '@avocado/mixins'; import {create} from './index'; const decorate = compose( EventEmitter, ); export class EntityList extends decorate(class {}) { constructor() { super(); this.entities_PRIVATE = {}; this._quadTree = new QuadTree(); this.state_PRIVATE = I.Map(); this.uuidMap_PRIVATE = {}; } *[Symbol.iterator]() { for (const uuid in this.entities_PRIVATE) { const entity = this.entities_PRIVATE[uuid]; yield entity; } } addEntity(entity) { const uuid = entity.instanceUuid; this.entities_PRIVATE[uuid] = entity; this.state_PRIVATE = this.state_PRIVATE.set(uuid, entity.state); entity.addTrait('listed'); entity.list = this; entity.once('destroyed', () => { this.removeEntity(entity); }); this.emit('entityAdded', entity); } destroy() { for (const entity of this) { entity.destroy(); } } findEntity(uuid) { if (this.uuidMap_PRIVATE[uuid]) { return this.entities_PRIVATE[this.uuidMap_PRIVATE[uuid]]; } if (this.entities_PRIVATE[uuid]) { return this.entities_PRIVATE[uuid]; } } patchState(patch) { for (const uuid in patch) { const localUuid = this.uuidMap_PRIVATE[uuid]; const entity = this.entities_PRIVATE[localUuid]; if (entity) { if (false === patch[uuid]) { // Entity removed. this.removeEntity(entity); } else { entity.patchState(patch[uuid]); this.state_PRIVATE = this.state_PRIVATE.set(localUuid, entity.state); } } else { // New entity. Create with patch as traits. const newEntity = create().fromJSON({ traits: patch[uuid], }); this.uuidMap_PRIVATE[uuid] = newEntity.instanceUuid; this.addEntity(newEntity); } } } get quadTree() { return this._quadTree; } recomputeState() { for (const uuid in this.entities_PRIVATE) { const entity = this.entities_PRIVATE[uuid]; this.state_PRIVATE = this.state_PRIVATE.set(uuid, entity.state); } } removeEntity(entity) { const uuid = entity.instanceUuid; delete this.entities_PRIVATE[uuid]; this.state_PRIVATE = this.state_PRIVATE.delete(uuid); this.emit('entityRemoved', entity); if (entity.is('listed')) { entity.removeTrait('listed'); } } get state() { return this.state_PRIVATE; } tick(elapsed) { for (const uuid in this.entities_PRIVATE) { const entity = this.entities_PRIVATE[uuid]; if ('tick' in entity) { entity.tick(elapsed); } } this.recomputeState(); } visibleEntities(query) { const quadTree = this._quadTree; const entities = quadTree.search(query).map((node) => { return node.data[2]; }); // Hitting multiple points for each entity can return duplicates. return arrayUnique(entities); } }