avocado-old/packages/topdown/room.synchronized.js
2019-09-22 18:45:33 -05:00

227 lines
5.9 KiB
JavaScript

import * as I from 'immutable';
import {compose, EventEmitter, Property} from '@avocado/core';
import {EntityCreatePacket, EntityPacket, EntityRemovePacket} from '@avocado/entity';
import {Vector} from '@avocado/math';
import {RectangleShape} from '@avocado/physics';
import {Resource} from '@avocado/resource';
import {Layers} from './layers';
import {LayerCreatePacket} from './packets/layer-create.packet';
import {RoomSizeUpdatePacket} from './packets/room-size-update.packet';
import {TileUpdatePacket} from './packets/tile-update.packet';
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,
}),
);
export class Room extends decorate(Resource) {
constructor(json) {
super();
this.bounds = [];
const layerJSON = 'undefined' !== typeof json ? json.layers : undefined;
this.layers = new Layers(layerJSON);
this.queuedEntityPackets = {};
// 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);
if ('undefined' !== typeof json && json.size) {
this.size = json.size;
}
}
acceptPacket(packet) {
if (packet instanceof EntityCreatePacket) {
this.queuedEntityPackets[packet.data.uuid] = [];
this.layers.acceptPacket(packet);
}
if (
packet instanceof LayerCreatePacket
|| packet instanceof TileUpdatePacket
) {
this.layers.acceptPacket(packet);
}
if (packet instanceof EntityRemovePacket) {
const entity = this.findEntity(packet.data.uuid);
if (entity) {
entity.destroyGently();
}
return;
}
if (packet instanceof EntityPacket) {
const entity = this.findEntity(packet.data.uuid);
if (entity) {
entity.acceptPacket(packet);
}
else {
const queuedPackets = this.queuedEntityPackets[packet.data.uuid];
if (queuedPackets) {
queuedPackets.push(packet);
}
}
}
if (packet instanceof RoomSizeUpdatePacket) {
const x = packet.data & 0xFFFF;
const y = packet.data >> 16;
this.size = [x, y];
}
}
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.setIntoRoom(this);
if (AVOCADO_CLIENT) {
const queuedPackets = this.queuedEntityPackets[entity.instanceUuid];
if (queuedPackets) {
for (let i = 0; i < queuedPackets.length; i++) {
entity.acceptPacket(queuedPackets[i]);
}
}
this.queuedEntityPackets[entity.instanceUuid] = undefined;
}
this.emit('entityAdded', entity)
}
onEntityRemovedFromRoom(entity) {
entity.removeFromRoom();
this.emit('entityRemoved', entity);
}
onLayerAdded(layer) {
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();
}
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;
}
removeEntityFromLayer(entity, layerIndex) {
this.layers.removeEntityFromLayer(entity, layerIndex);
}
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) {
this.layers.tick(elapsed);
if (this.world) {
this.world.tick(elapsed);
}
}
visibleEntities(query) {
return this.layers.visibleEntities(query);
}
}