import * as I from 'immutable'; import mapValues from 'lodash.mapvalues'; import {compose} from '@avocado/core'; import {QuadTree} from '@avocado/math'; import {EventEmitter} from '@avocado/mixins'; import {create} from './index'; const decorate = compose( EventEmitter, ); class EntityListBase { constructor() { this.entities_PRIVATE = {}; this.quadTree_PRIVATE = new QuadTree(); this.quadTreeData_PRIVATE = {}; 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; } } acceptStateChange(change) { for (const uuid in change) { const localUuid = this.uuidMap_PRIVATE[uuid]; const entity = this.entities_PRIVATE[localUuid]; if (entity) { if (false === change[uuid]) { // Entity removed. this.removeEntity(entity); } else { entity.acceptStateChange(change[uuid]); this.state_PRIVATE = this.state_PRIVATE.set(localUuid, entity.state()); } } else { // New entity. Create with change as traits. const newEntity = create().fromJSON({ traits: change[uuid], }); this.addEntity(newEntity); this.uuidMap_PRIVATE[uuid] = newEntity.instanceUuid; } } } addEntity(entity) { const uuid = entity.instanceUuid; this.entities_PRIVATE[uuid] = entity; this.state_PRIVATE = this.state_PRIVATE.set(uuid, entity.state()); entity.addTrait('listed', { params: { list: this, }, }); entity.on('destroyed', () => { this.removeEntity(entity); }); entity.on('positionChanged', () => { this.quadTree_PRIVATE.remove(this.quadTreePoint(entity)); this.quadTree_PRIVATE.add(this.recomputeQuadTreePoint(entity)); }); this.emit('entityAdded', entity); this.quadTree_PRIVATE.add(this.recomputeQuadTreePoint(entity)); } entity(uuid) { return this.entities_PRIVATE[uuid]; } mappedUuid(uuid) { return this.uuidMap_PRIVATE[uuid]; } quadTreePoint(entity) { return this.quadTreeData_PRIVATE[entity.instanceUuid]; } recomputeQuadTreePoint(entity) { const point = this.quadTreeData_PRIVATE[entity.instanceUuid] = [ entity.x, entity.y, entity, ]; return point; } 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.hasTrait('listed')) { entity.removeTrait('listed'); } this.quadTree_PRIVATE.remove(this.quadTreePoint(entity)); } 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(); } within(rectangle) { return this.quadTree_PRIVATE.search(rectangle).map((node) => { return node.data[2]; }); } } export class EntityList extends decorate(EntityListBase) {}