avocado-old/packages/entity/list/index.js
2019-09-16 21:52:08 -05:00

201 lines
5.4 KiB
JavaScript

import {compose, EventEmitter} from '@avocado/core';
import {QuadTree, Rectangle, Vector} from '@avocado/math';
import {Synchronized} from '@avocado/state';
import {EntityCreatePacket} from '../packets/entity-create.packet';
import {EntityRemovePacket} from '../packets/entity-remove.packet';
import {Entity} from '../entity.resource';
const decorate = compose(
EventEmitter,
Synchronized,
);
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) {
if (packet instanceof EntityCreatePacket) {
if (!this.findEntity(packet.data.uuid)) {
Entity.loadOrInstance(packet.data).then((entity) => {
entity.instanceUuid = packet.data.uuid;
this.addEntity(entity);
});
}
}
}
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 ('undefined' !== entityJSON.uri) {
Entity.read(entityJSON.uri).then((readJSON) => {
this.addEntity(new Entity(readJSON, entityJSON));
});
}
else {
this.addEntity(new Entity(json[i]));
}
}
}
packetsForUpdate(force = false) {
const packets = [];
if (!force) {
for (let i = 0; i < this._entitiesJustAdded.length; i++) {
const entity = this._entitiesJustAdded[i];
packets.push(new EntityCreatePacket(entity.mergeDiff(), entity));
}
this._entitiesJustAdded = [];
}
for (let i = 0; i < this._flatEntities.length; i++) {
const entityPackets = this._flatEntities[i].packetsForUpdate(force);
for (let j = 0; j < entityPackets.length; j++) {
packets.push(entityPackets[j]);
}
}
if (!force) {
for (let i = 0; i < this._entitiesJustRemoved.length; i++) {
const entity = this._entitiesJustRemoved[i];
packets.push(new EntityRemovePacket({}, entity));
}
this._entitiesJustRemoved = [];
}
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);
}
}
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';