import * as I from 'immutable'; import {compose, EventEmitter, Property} from '@avocado/core'; import {Vector} from '@avocado/math'; import {RectangleShape} from '@avocado/physics'; import {Synchronized} from '@avocado/state'; import {Layers} from './layers'; const ROOM_BOUND_SIZE = 64; const HALF_ROOM_BOUND_SIZE = ROOM_BOUND_SIZE / 2; const decorate = compose( EventEmitter, Property('world', { track: true, }), Vector.Mixin('size', 'width', 'height', { default: [0, 0], track: true, }), Synchronized, ); export class Room extends decorate(class {}) { constructor() { super(); this.bounds = []; this.layers = new Layers(); // Listeners. this.layers.on('entityAdded', this.onEntityAddedToRoom, this); this.layers.on('entityRemoved', this.onEntityRemovedFromRoom, this); this.layers.on('layerAdded', this.onLayerAdded, this); this.on('sizeChanged', this.onSizeChanged, this); this.on('worldChanged', this.onWorldChanged, this); this.initializeSynchronizedChildren(); } addEntityToLayer(entity, layerIndex = 0) { this.layers.addEntityToLayer(entity, layerIndex); } allEntities() { return this.layers.allEntities(); } destroy() { this.layers.destroy(); this.layers.off('entityAdded', this.onEntityAddedToRoom); this.layers.off('entityRemoved', this.onEntityRemovedFromRoom); this.off('worldChanged', this.onWorldChanged); } findEntity(uuid) { return this.layers.findEntity(uuid); } fromJSON(json) { if (json.layers) { this.layers.fromJSON(json.layers); } if (json.size) { this.size = json.size; } return this; } layer(index) { return this.layers.layer(index); } onEntityAddedToRoom(entity) { entity.room = this; this.emit('entityAdded', entity) } onEntityRemovedFromRoom(entity) { this.emit('entityRemoved', entity); } onLayerAdded(layer, index) { layer.world = this.world; } onSizeChanged() { this.updateBounds(); } onWorldChanged() { const world = this.world; for (const {layer} of this.layers) { layer.world = world; for (const entity of layer.entityList) { if (entity.is('physical')) { entity.world = world; } } } // Update bounds. this.updateBounds(); } removeEntityFromLayer(entity, layerIndex = 0) { this.layers.removeEntityFromLayer(entity, layerIndex); } synchronizedChildren() { return [ 'width', 'height', 'layers', ]; } updateBounds() { const world = this.world; if (!world) { return; } for (const bound of this.bounds) { world.removeBody(bound); } if (Vector.isZero(this.size)) { return; } this.bounds = [ // Top. world.createBody((new RectangleShape()).fromJSON({ position: [this.width / 2, -HALF_ROOM_BOUND_SIZE], size: [this.width + ROOM_BOUND_SIZE, ROOM_BOUND_SIZE], })), // Right. world.createBody((new RectangleShape()).fromJSON({ position: [this.width + HALF_ROOM_BOUND_SIZE, this.height / 2], size: [ROOM_BOUND_SIZE, this.height + ROOM_BOUND_SIZE], })), // Bottom. world.createBody((new RectangleShape()).fromJSON({ position: [this.width / 2, this.height + HALF_ROOM_BOUND_SIZE], size: [this.width + ROOM_BOUND_SIZE, ROOM_BOUND_SIZE], })), // Left. world.createBody((new RectangleShape()).fromJSON({ position: [-HALF_ROOM_BOUND_SIZE, this.height / 2], size: [ROOM_BOUND_SIZE, this.height + ROOM_BOUND_SIZE], })), ]; this.bounds.forEach((bound) => { bound.static = true; world.addBody(bound); }); } tick(elapsed) { super.tick(elapsed); if (this.world) { this.world.tick(elapsed); } } visibleEntities(query) { return this.layers.visibleEntities(query); } }