2019-03-26 17:04:52 -05:00
|
|
|
import * as I from 'immutable';
|
|
|
|
|
2019-04-28 23:45:03 -05:00
|
|
|
import {compose, EventEmitter, Property} from '@avocado/core';
|
2019-05-13 21:07:51 -05:00
|
|
|
import {EntityCreatePacket, EntityPacket, EntityRemovePacket} from '@avocado/entity';
|
2019-03-27 23:22:05 -05:00
|
|
|
import {Vector} from '@avocado/math';
|
|
|
|
import {RectangleShape} from '@avocado/physics';
|
2019-04-07 11:43:50 -05:00
|
|
|
import {Synchronized} from '@avocado/state';
|
2019-03-26 17:04:52 -05:00
|
|
|
|
2019-03-27 01:52:24 -05:00
|
|
|
import {Layers} from './layers';
|
2019-05-13 21:07:51 -05:00
|
|
|
import {LayerCreatePacket} from './packets/layer-create.packet';
|
|
|
|
import {RoomSizeUpdatePacket} from './packets/room-size-update.packet';
|
|
|
|
import {TileUpdatePacket} from './packets/tile-update.packet';
|
2019-03-27 01:52:24 -05:00
|
|
|
|
2019-04-26 00:24:13 -05:00
|
|
|
const ROOM_BOUND_SIZE = 64;
|
|
|
|
const HALF_ROOM_BOUND_SIZE = ROOM_BOUND_SIZE / 2;
|
|
|
|
|
2019-03-27 01:52:24 -05:00
|
|
|
const decorate = compose(
|
|
|
|
EventEmitter,
|
2019-03-27 16:11:37 -05:00
|
|
|
Property('world', {
|
|
|
|
track: true,
|
|
|
|
}),
|
2019-03-27 23:22:05 -05:00
|
|
|
Vector.Mixin('size', 'width', 'height', {
|
|
|
|
default: [0, 0],
|
2019-03-28 02:04:29 -05:00
|
|
|
track: true,
|
2019-04-16 17:52:56 -05:00
|
|
|
}),
|
|
|
|
Synchronized,
|
2019-03-27 01:52:24 -05:00
|
|
|
);
|
|
|
|
|
2019-04-16 17:52:56 -05:00
|
|
|
export class Room extends decorate(class {}) {
|
2019-03-26 17:04:52 -05:00
|
|
|
|
|
|
|
constructor() {
|
2019-03-27 16:18:27 -05:00
|
|
|
super();
|
2019-03-27 23:22:05 -05:00
|
|
|
this.bounds = [];
|
2019-03-27 01:52:24 -05:00
|
|
|
this.layers = new Layers();
|
2019-05-17 05:37:29 -05:00
|
|
|
this.queuedEntityPackets = {};
|
2019-03-27 01:52:24 -05:00
|
|
|
// Listeners.
|
2019-04-12 18:58:38 -05:00
|
|
|
this.layers.on('entityAdded', this.onEntityAddedToRoom, this);
|
|
|
|
this.layers.on('entityRemoved', this.onEntityRemovedFromRoom, this);
|
2019-04-12 20:16:31 -05:00
|
|
|
this.layers.on('layerAdded', this.onLayerAdded, this);
|
2019-04-12 18:58:38 -05:00
|
|
|
this.on('sizeChanged', this.onSizeChanged, this);
|
|
|
|
this.on('worldChanged', this.onWorldChanged, this);
|
2019-03-27 01:52:24 -05:00
|
|
|
}
|
|
|
|
|
2019-05-13 21:07:51 -05:00
|
|
|
acceptPacket(packet) {
|
2019-05-17 05:37:29 -05:00
|
|
|
if (packet instanceof EntityCreatePacket) {
|
|
|
|
this.queuedEntityPackets[packet.data.uuid] = [];
|
|
|
|
this.layers.acceptPacket(packet);
|
|
|
|
}
|
2019-05-13 21:07:51 -05:00
|
|
|
if (
|
|
|
|
packet instanceof LayerCreatePacket
|
|
|
|
|| packet instanceof TileUpdatePacket
|
|
|
|
) {
|
|
|
|
this.layers.acceptPacket(packet);
|
|
|
|
}
|
|
|
|
if (packet instanceof EntityRemovePacket) {
|
|
|
|
const entity = this.findEntity(packet.data.uuid);
|
|
|
|
if (entity) {
|
2019-05-14 05:58:04 -05:00
|
|
|
entity.destroyGently();
|
2019-05-13 21:07:51 -05:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (packet instanceof EntityPacket) {
|
|
|
|
const entity = this.findEntity(packet.data.uuid);
|
|
|
|
if (entity) {
|
|
|
|
entity.acceptPacket(packet);
|
|
|
|
}
|
2019-05-17 05:37:29 -05:00
|
|
|
else {
|
|
|
|
const queuedPackets = this.queuedEntityPackets[packet.data.uuid];
|
|
|
|
if (queuedPackets) {
|
|
|
|
queuedPackets.push(packet);
|
|
|
|
}
|
|
|
|
}
|
2019-05-13 21:07:51 -05:00
|
|
|
}
|
|
|
|
if (packet instanceof RoomSizeUpdatePacket) {
|
|
|
|
const x = packet.data & 0xFFFF;
|
|
|
|
const y = packet.data >> 16;
|
|
|
|
this.size = [x, y];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-27 01:52:24 -05:00
|
|
|
addEntityToLayer(entity, layerIndex = 0) {
|
|
|
|
this.layers.addEntityToLayer(entity, layerIndex);
|
|
|
|
}
|
|
|
|
|
2019-04-28 22:33:20 -05:00
|
|
|
allEntities() {
|
|
|
|
return this.layers.allEntities();
|
|
|
|
}
|
|
|
|
|
2019-03-27 01:52:24 -05:00
|
|
|
destroy() {
|
|
|
|
this.layers.destroy();
|
2019-03-27 16:11:37 -05:00
|
|
|
this.layers.off('entityAdded', this.onEntityAddedToRoom);
|
|
|
|
this.layers.off('entityRemoved', this.onEntityRemovedFromRoom);
|
|
|
|
this.off('worldChanged', this.onWorldChanged);
|
2019-03-26 17:04:52 -05:00
|
|
|
}
|
|
|
|
|
2019-03-27 17:36:57 -05:00
|
|
|
findEntity(uuid) {
|
|
|
|
return this.layers.findEntity(uuid);
|
|
|
|
}
|
|
|
|
|
2019-03-26 17:04:52 -05:00
|
|
|
fromJSON(json) {
|
|
|
|
if (json.layers) {
|
2019-03-27 01:52:24 -05:00
|
|
|
this.layers.fromJSON(json.layers);
|
2019-03-26 17:04:52 -05:00
|
|
|
}
|
2019-03-27 23:22:05 -05:00
|
|
|
if (json.size) {
|
|
|
|
this.size = json.size;
|
|
|
|
}
|
2019-03-26 17:04:52 -05:00
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2019-03-27 17:15:53 -05:00
|
|
|
layer(index) {
|
|
|
|
return this.layers.layer(index);
|
|
|
|
}
|
|
|
|
|
2019-03-27 01:52:24 -05:00
|
|
|
onEntityAddedToRoom(entity) {
|
|
|
|
entity.room = this;
|
2019-05-17 05:37:29 -05:00
|
|
|
if (AVOCADO_CLIENT) {
|
|
|
|
const queuedPackets = this.queuedEntityPackets[entity.instanceUuid];
|
|
|
|
for (let i = 0; i < queuedPackets.length; i++) {
|
|
|
|
entity.acceptPacket(queuedPackets[i]);
|
|
|
|
}
|
|
|
|
this.queuedEntityPackets[entity.instanceUuid] = undefined;
|
|
|
|
}
|
2019-03-27 01:52:24 -05:00
|
|
|
this.emit('entityAdded', entity)
|
|
|
|
}
|
|
|
|
|
|
|
|
onEntityRemovedFromRoom(entity) {
|
|
|
|
this.emit('entityRemoved', entity);
|
|
|
|
}
|
|
|
|
|
2019-05-13 21:07:51 -05:00
|
|
|
onLayerAdded(layer) {
|
2019-04-12 20:16:31 -05:00
|
|
|
layer.world = this.world;
|
|
|
|
}
|
|
|
|
|
2019-03-27 23:22:05 -05:00
|
|
|
onSizeChanged() {
|
|
|
|
this.updateBounds();
|
|
|
|
}
|
|
|
|
|
2019-03-27 16:11:37 -05:00
|
|
|
onWorldChanged() {
|
2019-03-27 23:22:05 -05:00
|
|
|
const world = this.world;
|
2019-05-13 21:07:51 -05:00
|
|
|
for (const layer of this.layers) {
|
2019-04-12 20:16:31 -05:00
|
|
|
layer.world = world;
|
2019-03-27 16:11:37 -05:00
|
|
|
for (const entity of layer.entityList) {
|
|
|
|
if (entity.is('physical')) {
|
2019-03-27 23:22:05 -05:00
|
|
|
entity.world = world;
|
2019-03-27 16:11:37 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-03-27 23:22:05 -05:00
|
|
|
// Update bounds.
|
|
|
|
this.updateBounds();
|
2019-03-27 16:11:37 -05:00
|
|
|
}
|
|
|
|
|
2019-05-13 21:07:51 -05:00
|
|
|
packetsForUpdate(force = false) {
|
|
|
|
const packets = [];
|
|
|
|
if (force) {
|
|
|
|
const packed = (this.height << 16) | (this.width);
|
|
|
|
packets.push(new RoomSizeUpdatePacket(packed));
|
|
|
|
}
|
|
|
|
// Layers packets.
|
|
|
|
const layersPacketsForUpdate = this.layers.packetsForUpdate(force);
|
|
|
|
for (let i = 0; i < layersPacketsForUpdate.length; i++) {
|
|
|
|
packets.push(layersPacketsForUpdate[i]);
|
|
|
|
}
|
|
|
|
return packets;
|
2019-03-27 01:52:24 -05:00
|
|
|
}
|
|
|
|
|
2019-05-13 21:07:51 -05:00
|
|
|
removeEntityFromLayer(entity, layerIndex) {
|
|
|
|
this.layers.removeEntityFromLayer(entity, layerIndex);
|
2019-04-07 11:43:50 -05:00
|
|
|
}
|
|
|
|
|
2019-03-27 23:22:05 -05:00
|
|
|
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({
|
2019-04-26 00:24:13 -05:00
|
|
|
position: [this.width / 2, -HALF_ROOM_BOUND_SIZE],
|
2019-04-28 22:43:24 -05:00
|
|
|
size: [this.width + ROOM_BOUND_SIZE, ROOM_BOUND_SIZE],
|
2019-03-27 23:22:05 -05:00
|
|
|
})),
|
|
|
|
// Right.
|
|
|
|
world.createBody((new RectangleShape()).fromJSON({
|
2019-04-26 00:24:13 -05:00
|
|
|
position: [this.width + HALF_ROOM_BOUND_SIZE, this.height / 2],
|
2019-04-28 22:43:24 -05:00
|
|
|
size: [ROOM_BOUND_SIZE, this.height + ROOM_BOUND_SIZE],
|
2019-03-27 23:22:05 -05:00
|
|
|
})),
|
|
|
|
// Bottom.
|
|
|
|
world.createBody((new RectangleShape()).fromJSON({
|
2019-04-26 00:24:13 -05:00
|
|
|
position: [this.width / 2, this.height + HALF_ROOM_BOUND_SIZE],
|
2019-04-28 22:43:24 -05:00
|
|
|
size: [this.width + ROOM_BOUND_SIZE, ROOM_BOUND_SIZE],
|
2019-03-27 23:22:05 -05:00
|
|
|
})),
|
|
|
|
// Left.
|
|
|
|
world.createBody((new RectangleShape()).fromJSON({
|
2019-04-26 00:24:13 -05:00
|
|
|
position: [-HALF_ROOM_BOUND_SIZE, this.height / 2],
|
2019-04-28 22:43:24 -05:00
|
|
|
size: [ROOM_BOUND_SIZE, this.height + ROOM_BOUND_SIZE],
|
2019-03-27 23:22:05 -05:00
|
|
|
})),
|
|
|
|
];
|
|
|
|
this.bounds.forEach((bound) => {
|
|
|
|
bound.static = true;
|
|
|
|
world.addBody(bound);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-03-26 17:04:52 -05:00
|
|
|
tick(elapsed) {
|
2019-05-13 21:07:51 -05:00
|
|
|
this.layers.tick(elapsed);
|
2019-03-27 16:11:37 -05:00
|
|
|
if (this.world) {
|
|
|
|
this.world.tick(elapsed);
|
2019-03-26 17:04:52 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-07 14:59:24 -05:00
|
|
|
visibleEntities(query) {
|
|
|
|
return this.layers.visibleEntities(query);
|
|
|
|
}
|
|
|
|
|
2019-03-27 16:11:37 -05:00
|
|
|
}
|