2019-03-17 23:45:48 -05:00
|
|
|
import * as I from 'immutable';
|
|
|
|
import mapValues from 'lodash.mapvalues';
|
|
|
|
|
2019-04-28 23:45:03 -05:00
|
|
|
import {arrayUnique, compose, EventEmitter} from '@avocado/core';
|
2019-03-27 01:47:38 -05:00
|
|
|
import {QuadTree, Rectangle, Vector} from '@avocado/math';
|
2019-04-07 11:43:50 -05:00
|
|
|
import {Synchronized} from '@avocado/state';
|
2019-03-18 19:58:22 -05:00
|
|
|
|
2019-04-16 16:40:20 -05:00
|
|
|
import {Entity} from '../index';
|
2019-03-17 23:45:48 -05:00
|
|
|
|
2019-03-18 19:58:22 -05:00
|
|
|
const decorate = compose(
|
|
|
|
EventEmitter,
|
2019-04-16 17:52:56 -05:00
|
|
|
Synchronized,
|
2019-03-18 19:58:22 -05:00
|
|
|
);
|
|
|
|
|
2019-04-16 17:52:56 -05:00
|
|
|
export class EntityList extends decorate(class {}) {
|
2019-03-17 23:45:48 -05:00
|
|
|
|
|
|
|
constructor() {
|
2019-03-27 16:18:27 -05:00
|
|
|
super();
|
2019-04-19 14:46:00 -05:00
|
|
|
this._afterDestructionTickers = [];
|
2019-04-07 12:00:11 -05:00
|
|
|
this._entities = {};
|
2019-03-27 01:47:38 -05:00
|
|
|
this._quadTree = new QuadTree();
|
2019-03-17 23:45:48 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
*[Symbol.iterator]() {
|
2019-04-07 12:00:11 -05:00
|
|
|
for (const uuid in this._entities) {
|
|
|
|
const entity = this._entities[uuid];
|
2019-03-17 23:45:48 -05:00
|
|
|
yield entity;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
addEntity(entity) {
|
|
|
|
const uuid = entity.instanceUuid;
|
2019-04-07 12:00:11 -05:00
|
|
|
this._entities[uuid] = entity;
|
2019-04-07 11:43:50 -05:00
|
|
|
this.state = this.state.set(uuid, entity.state);
|
2019-03-21 00:36:06 -05:00
|
|
|
entity.addTrait('listed');
|
|
|
|
entity.list = this;
|
2019-04-19 14:42:08 -05:00
|
|
|
entity.once('destroy', () => {
|
2019-03-21 00:09:17 -05:00
|
|
|
this.removeEntity(entity);
|
2019-04-19 14:46:00 -05:00
|
|
|
// In the process of destroying, allow entities to specify tickers that
|
|
|
|
// must live on past destruction.
|
|
|
|
const tickers = entity.invokeHookFlat('afterDestructionTickers');
|
|
|
|
this._afterDestructionTickers.push(...tickers);
|
2019-03-20 18:35:19 -05:00
|
|
|
});
|
2019-03-18 19:58:22 -05:00
|
|
|
this.emit('entityAdded', entity);
|
2019-03-17 23:45:48 -05:00
|
|
|
}
|
|
|
|
|
2019-03-21 01:32:49 -05:00
|
|
|
destroy() {
|
|
|
|
for (const entity of this) {
|
|
|
|
entity.destroy();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-27 17:36:57 -05:00
|
|
|
findEntity(uuid) {
|
2019-04-07 12:00:11 -05:00
|
|
|
if (this._entities[uuid]) {
|
|
|
|
return this._entities[uuid];
|
2019-03-27 17:36:57 -05:00
|
|
|
}
|
2019-03-20 21:07:57 -05:00
|
|
|
}
|
|
|
|
|
2019-04-07 11:43:50 -05:00
|
|
|
patchStateStep(uuid, step) {
|
2019-04-28 08:57:46 -05:00
|
|
|
const entity = this._entities[uuid];
|
2019-04-28 22:30:22 -05:00
|
|
|
if ('/' === step.path) {
|
|
|
|
switch (step.op) {
|
|
|
|
case 'add':
|
|
|
|
// New entity. Create with patch as traits.
|
|
|
|
const newEntity = (new Entity()).fromJSON({
|
|
|
|
traits: step.value,
|
|
|
|
});
|
|
|
|
newEntity.instanceUuid = uuid;
|
|
|
|
this.addEntity(newEntity);
|
|
|
|
break;
|
|
|
|
case 'remove':
|
|
|
|
// Maybe already destroyed on the client?
|
|
|
|
if (entity && entity.is('existent')) {
|
|
|
|
entity.destroy();
|
|
|
|
}
|
|
|
|
break;
|
2019-04-06 23:19:32 -05:00
|
|
|
}
|
2019-04-28 22:30:22 -05:00
|
|
|
return;
|
2019-04-06 23:19:32 -05:00
|
|
|
}
|
2019-04-28 22:30:22 -05:00
|
|
|
if ('replace' === step.op && entity) {
|
2019-04-19 14:46:00 -05:00
|
|
|
// Exists; patch.
|
|
|
|
entity.patchState([step]);
|
|
|
|
}
|
2019-04-05 15:16:55 -05:00
|
|
|
}
|
|
|
|
|
2019-03-27 01:47:38 -05:00
|
|
|
get quadTree() {
|
|
|
|
return this._quadTree;
|
2019-03-20 18:35:19 -05:00
|
|
|
}
|
|
|
|
|
2019-03-17 23:45:48 -05:00
|
|
|
removeEntity(entity) {
|
|
|
|
const uuid = entity.instanceUuid;
|
2019-04-07 12:00:11 -05:00
|
|
|
delete this._entities[uuid];
|
2019-04-07 11:43:50 -05:00
|
|
|
this.state = this.state.delete(uuid);
|
2019-03-18 19:58:22 -05:00
|
|
|
this.emit('entityRemoved', entity);
|
2019-03-23 18:49:19 -05:00
|
|
|
if (entity.is('listed')) {
|
2019-03-20 23:23:34 -05:00
|
|
|
entity.removeTrait('listed');
|
|
|
|
}
|
2019-03-17 23:45:48 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
tick(elapsed) {
|
2019-04-19 14:46:00 -05:00
|
|
|
// Run after destruction tickers.
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
// Run normal tickers.
|
2019-04-07 12:00:11 -05:00
|
|
|
for (const uuid in this._entities) {
|
|
|
|
const entity = this._entities[uuid];
|
2019-04-12 13:12:11 -05:00
|
|
|
entity.tick(elapsed);
|
|
|
|
}
|
2019-04-19 14:46:00 -05:00
|
|
|
// Update state.
|
2019-04-23 15:25:03 -05:00
|
|
|
if (AVOCADO_SERVER) {
|
|
|
|
this.state = this.state.withMutations((state) => {
|
|
|
|
for (const uuid in this._entities) {
|
|
|
|
const entity = this._entities[uuid];
|
|
|
|
if (!entity.isDirty) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
entity.isDirty = false;
|
|
|
|
state.set(uuid, entity.state);
|
2019-04-16 23:44:03 -05:00
|
|
|
}
|
2019-04-23 15:25:03 -05:00
|
|
|
});
|
|
|
|
}
|
2019-03-17 23:45:48 -05:00
|
|
|
}
|
|
|
|
|
2019-03-27 01:47:38 -05:00
|
|
|
visibleEntities(query) {
|
2019-04-16 23:59:08 -05:00
|
|
|
const entities = [];
|
2019-03-27 01:47:38 -05:00
|
|
|
const quadTree = this._quadTree;
|
2019-04-16 23:59:08 -05:00
|
|
|
const nodes = quadTree.search(query);
|
|
|
|
for (let i = 0; i < nodes.length; ++i) {
|
|
|
|
const node = nodes[i];
|
|
|
|
const entity = node.data[2];
|
|
|
|
// Make sure they're actually in the query due to expanded AABB.
|
|
|
|
if (!Rectangle.intersects(query, entity.visibleBoundingBox)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
entities.push(entity);
|
|
|
|
}
|
2019-03-27 01:47:38 -05:00
|
|
|
// Hitting multiple points for each entity can return duplicates.
|
|
|
|
return arrayUnique(entities);
|
|
|
|
}
|
|
|
|
|
2019-03-17 23:45:48 -05:00
|
|
|
}
|
2019-04-13 20:53:02 -05:00
|
|
|
|
|
|
|
export {EntityListView} from './view';
|