import {compose, EventEmitter, Property} from '@avocado/core'; import { Entity, EntityList, EntityListUpdateEntityPacket, } from '@avocado/entity'; import {Vector} from '@avocado/math'; import { SynchronizedCreatePacket, SynchronizedDestroyPacket, } from '@avocado/net'; import {ShapeList} from '@avocado/physics'; import { LayerUpdateTilesetUriPacket, } from './packets/layer-update-tileset-uri.packet'; import { TilesUpdatePacket, } from './packets/tiles-update.packet'; import {Tiles} from './tiles'; import {Tileset} from './tileset'; const decorate = compose( EventEmitter, Property('tilesetUri', { default: '', track: true, }), Property('tileset', { track: true, }), Property('world', { track: true, }), ); export class Layer extends decorate(class {}) { constructor(json) { super(); this.entityList = new EntityList(); this.index = -1; this.tileEntities = {}; this.tileGeometry = []; this.tiles = new Tiles(); this._tilesetUriChanged = false; // Listeners. this.entityList.on('entityAdded', this.onEntityAddedToLayer, this); this.entityList.on('entityRemoved', this.onEntityRemovedFromLayer, this); this.tiles.on('dataChanged', this.onTileDataChanged, this); this.on('tilesetChanged', this.onTilesetChanged, this); this.on('tilesetUriChanged', this.onTilesetUriChanged, this); this.on('worldChanged', this.onWorldChanged, this); if ('undefined' !== typeof json) { this.fromJSON(json); } } acceptPacket(packet) { if (packet instanceof LayerUpdateTilesetUriPacket) { this.tilesetUri = packet.data; } if (packet instanceof EntityListUpdateEntityPacket) { this.entityList.acceptPacket(packet); } // May have to check type... if (packet instanceof SynchronizedCreatePacket) { this.entityList.acceptPacket(packet); } if (packet instanceof SynchronizedDestroyPacket) { this.entityList.acceptPacket(packet); } if (packet instanceof TilesUpdatePacket) { this.tiles.acceptPacket(packet); } } addEntity(entity) { this.entityList.addEntity(entity); } addTileEntity(entity, index) { if (!this.tileEntities[index]) { this.tileEntities[index] = []; } if (-1 !== this.tileEntities[index].indexOf(entity)) { return; } this.tileEntities[index].push(entity); } addTileGeometry() { return false; const tileset = this.tileset; if (!tileset) { return false; } const world = this.world; if (!world) { return false; } const halfTileSize = Vector.scale(tileset.tileSize, 0.5); this.tiles.forEachTile((tile, x, y, i) => { const shape = this.tileset.geometry(tile); if (!shape) { return; } shape.position = Vector.add( halfTileSize, Vector.mul([x, y], tileset.tileSize), ); const body = world.createBody(shape); body.static = true; world.addBody(body); this.tileGeometry.push(body); }); return true; } allEntities() { return Array.from(this.entityList); } cleanPackets() { this._tilesetUriChanged = false; this.entityList.cleanPackets(); this.tiles.cleanPackets(); } destroy() { this.entityList.destroy(); this.entityList.off('entityAdded', this.onEntityAddedToLayer); this.entityList.off('entityRemoved', this.onEntityRemovedFromLayer); this.tiles.off('dataChanged', this.onTileDataChanged); this.off('tilesetUriChanged', this.onTilesetUriChanged); if (this.tileset) { this.tileset.destroy(); } } findEntity(uuid) { return this.entityList.findEntity(uuid); } fromJSON(json) { if (json.entities) { this.entityList.fromJSON(json.entities); } if (json.tiles) { this.tiles.fromJSON(json.tiles) } if (json.tilesetUri) { this.tilesetUri = json.tilesetUri; } return this; } hasTileEntityWithUriAt(tilePosition, uri) { const tileEntities = this.tileEntitiesAt(tilePosition); if (0 === tileEntities.length) { return false; } const entitiesWithUri = tileEntities.filter((entity) => { return entity.uri === uri; }); return entitiesWithUri.length > 0; } indexAt(position) { return this.tiles.indexAt(position); } onEntityAddedToLayer(entity) { entity.setIntoLayer(this); this.emit('entityAdded', entity) } onEntityRemovedFromLayer(entity) { entity.removeFromLayer(); this.emit('entityRemoved', entity); } onTileDataChanged() { this.emit('tileDataChanged'); } onTilesetChanged(oldTileset) { let didChange = false; if (oldTileset) { this.removeTileGeometry(); didChange = true; } if (this.addTileGeometry()) { didChange = true; } if (didChange) { this.emit('tileGeometryChanged'); } } onTilesetUriChanged() { this._tilesetUriChanged = true; if (!this.tilesetUri) { return; } Tileset.load(this.tilesetUri).then((tileset) => { this.tileset = tileset; }); } onWorldChanged(oldWorld) { let didChange = false; if (oldWorld) { this.removeTileGeometry(); didChange = true; } if (this.addTileGeometry()) { didChange = true; } if (didChange) { this.emit('tileGeometryChanged'); } } packets(informed) { const packets = []; if (this._tilesetUriChanged) { packets.push(new LayerUpdateTilesetUriPacket(this.tilesetUri)); } const entityListPackets = this.entityList.packets(informed); for (let i = 0; i < entityListPackets.length; i++) { packets.push(entityListPackets[i]); } const tilesPackets = this.tiles.packets(informed); for (let i = 0; i < tilesPackets.length; i++) { packets.push(tilesPackets[i]); } return packets; } removeEntity(entity) { this.entityList.removeEntity(entity); } removeTileEntity(entity, index) { if (!this.tileEntities[index]) { return; } const entityIndex = this.tileEntities[index].indexOf(entity); if (-1 === entityIndex) { return; } this.tileEntities[index].splice(entityIndex, 1); } removeTileGeometry() { // ... tag geometry in world for removal? } setTileAt(position, tile) { this.tiles.setTileAt(position, tile); } tick(elapsed) { this.entityList.tick(elapsed); } tileAt(position) { return this.tiles.tileAt(position); } tileEntitiesAt(tilePosition) { const index = this.indexAt(tilePosition); if (!this.tileEntities[index]) { return []; } return this.tileEntities[index]; } toJSON() { return { entities: this.entityList.toJSON(), tilesetUri: this.tilesetUri, tiles: this.tiles.toJSON(), }; } toNetwork(informed) { const {areaToInform} = informed; const visibleEntities = this.visibleEntities(areaToInform); const visibleEntitiesDiffs = visibleEntities.map((visibleEntity) => { return visibleEntity.mergeDiff(); }); return { entities: visibleEntitiesDiffs, tilesetUri: this.tilesetUri, tiles: this.tiles.toNetwork(informed), }; } visibleEntities(query) { return this.entityList.visibleEntities(query); } visibleEntitiesWithUri(query, uri) { return this.entityList.visibleEntitiesWithUri(query, uri); } }