import * as I from 'immutable'; import {compose} from '@avocado/core'; import {Entity, EntityList} from '@avocado/entity'; import {Vector} from '@avocado/math'; import {EventEmitter, Property} from '@avocado/mixins'; import {ShapeList} from '@avocado/physics'; import {Synchronized} from '@avocado/state'; import {Tiles} from './tiles'; import {Tileset} from './tileset'; const decorate = compose( EventEmitter, Property('tilesetUri', { track: true, }), Property('tileset', { track: true, }), Property('world', { track: true, }), Synchronized, ); export class Layer extends decorate(class {}) { constructor() { super(); this.entityList = new EntityList(); this.tileGeometry = []; this.tiles = new Tiles(); // 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.onTilesetUriChanged(); this.on('worldChanged', this.onWorldChanged, this); } addEntity(entity) { this.entityList.addEntity(entity); } addTileGeometry() { 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; } 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) { json.entities.forEach((entityJSON) => { const entity = new Entity(); this.entityList.addEntity(entity.fromJSON(entityJSON)); }); } if (json.tiles) { this.tiles.fromJSON(json.tiles) } if (json.tilesetUri) { this.tilesetUri = json.tilesetUri; } return this; } onEntityAddedToLayer(entity) { entity.addTrait('layered'); entity.layer = this; this.emit('entityAdded', entity) } onEntityRemovedFromLayer(entity) { if (entity.is('layered')) { entity.removeTrait('layered'); } 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() { 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'); } } removeEntity(entity) { this.entityList.removeEntity(entity); } removeTileGeometry() { // ... tag geometry in world for removal? } setTileAt(x, y, tile) { this.tiles.setTileAt(x, y, tile); } synchronizedChildren() { return [ 'entityList', 'tiles', 'tilesetUri', ]; } visibleEntities(query) { return this.entityList.visibleEntities(query); } }