import * as I from 'immutable'; import {compose} from '@avocado/core'; import {Vector} from '@avocado/math'; import {EventEmitter, Property} from '@avocado/mixins'; import {RectangleShape} from '@avocado/physics'; import {Synchronized} from '@avocado/state'; import {Layers} from './layers'; const decorate = compose( EventEmitter, Property('world', { track: true, }), Vector.Mixin('size', 'width', 'height', { default: [0, 0], track: true, }) ); export class Room extends decorate(Synchronized) { constructor() { super(); this.bounds = []; this.layers = new Layers(); // Listeners. this.onEntityAddedToRoom = this.onEntityAddedToRoom.bind(this); this.onEntityRemovedFromRoom = this.onEntityRemovedFromRoom.bind(this); this.layers.on('entityAdded', this.onEntityAddedToRoom); this.layers.on('entityRemoved', this.onEntityRemovedFromRoom); this.onSizeChanged = this.onSizeChanged.bind(this); this.on('sizeChanged', this.onSizeChanged); this.onWorldChanged = this.onWorldChanged.bind(this); this.on('worldChanged', this.onWorldChanged); } addEntityToLayer(entity, layerIndex = 0) { this.layers.addEntityToLayer(entity, layerIndex); } 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.addTrait('roomed'); entity.room = this; this.emit('entityAdded', entity) } onEntityRemovedFromRoom(entity) { if (entity.is('roomed')) { entity.removeTrait('roomed'); } this.emit('entityRemoved', entity); } onSizeChanged() { this.updateBounds(); } onWorldChanged() { const world = this.world; for (const {layer} of this.layers) { 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, -8], size: [this.width, 16], })), // Right. world.createBody((new RectangleShape()).fromJSON({ position: [this.width + 8, this.height / 2], size: [16, this.height], })), // Bottom. world.createBody((new RectangleShape()).fromJSON({ position: [this.width / 2, this.height + 8], size: [this.width, 16], })), // Left. world.createBody((new RectangleShape()).fromJSON({ position: [-8, this.height / 2], size: [16, this.height], })), ]; 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); } }