feat: topdown networking

This commit is contained in:
cha0s 2019-03-27 01:52:24 -05:00
parent a4040a39b9
commit ab522d80f8
6 changed files with 332 additions and 41 deletions

View File

@ -1,23 +1,58 @@
import {Container, Renderable, Sprite} from '@avocado/graphics';
import {Container, Sprite} from '@avocado/graphics';
import {Layer, Tileset, TilesRenderer} from '@avocado/topdown';
export class LayerView extends Renderable {
export class LayerView extends Container {
constructor(layer, renderer) {
super();
this.container = new Container();
this.layer = layer;
this.entityContainer = new Container();
this.layerContainer = new Container();
this.container.addChild(this.layerContainer);
this._renderer = renderer;
this._tileset = undefined;
Tileset.load(layer.tilesetUri).then((tileset) => {
this.tileset = tileset;
});
// Handle entity add/remove.
this.onEntityAdded = this.onEntityAdded.bind(this);
layer.on('entityAdded', this.onEntityAdded);
this.onEntityRemoved = this.onEntityRemoved.bind(this);
layer.on('entityRemoved', this.onEntityRemoved);
for (const entity of layer.entityList) {
this.onEntityAdded(entity);
}
// Watch tileset URI change.
this.onTilesetUriChanged = this.onTilesetUriChanged.bind(this);
layer.on('tilesetUriChanged', this.onTilesetUriChanged);
this.onTilesetUriChanged();
// Add to graphics.
this.addChild(this.layerContainer);
this.addChild(this.entityContainer);
}
get internal() {
return this.container.internal;
destroy() {
super.destroy();
if (this._tileset) {
this._tileset.destroy();
}
}
onEntityAdded(entity) {
if ('container' in entity) {
this.entityContainer.addChild(entity.container);
}
}
onEntityRemoved(entity) {
if ('container' in entity) {
this.entityContainer.removeChild(entity.container);
}
}
onTilesetUriChanged() {
if (!this.layer.tilesetUri) {
return;
}
Tileset.load(this.layer.tilesetUri).then((tileset) => {
this.tileset = tileset;
});
}
set tileset(tileset) {

View File

@ -1,20 +1,61 @@
import * as I from 'immutable';
import {EntityList} from '@avocado/entity';
import {compose} from '@avocado/core';
import {create as createEntity, EntityList} from '@avocado/entity';
import {EventEmitter, Property} from '@avocado/mixins';
import {Tiles} from '@avocado/topdown';
export class Layer {
const decorate = compose(
EventEmitter,
Property('tilesetUri', {
track: true,
}),
);
class LayerBase {
constructor() {
this.tiles = undefined;
this.tilesetUri = undefined;
this.entityList = new EntityList();
this._state = I.Map();
this.tiles = new Tiles();
// Listeners.
this.onEntityAddedToLayer = this.onEntityAddedToLayer.bind(this);
this.onEntityRemovedFromLayer = this.onEntityRemovedFromLayer.bind(this);
this.entityList.on('entityAdded', this.onEntityAddedToLayer);
this.entityList.on('entityRemoved', this.onEntityRemovedFromLayer);
}
acceptStateChange(change) {
if (change.entityList) {
this.entityList.acceptStateChange(change.entityList);
}
if (change.tilesetUri) {
this.tilesetUri = change.tilesetUri;
}
if (change.tiles) {
this.tiles.acceptStateChange(change.tiles);
}
}
addEntity(entity) {
this.entityList.addEntity(entity);
}
destroy() {
this.entityList.destroy();
this.entityList.off('entityAdded', this.onEntityAddedToLayer);
this.entityList.off('entityRemoved', this.onEntityRemovedFromLayer);
}
fromJSON(json) {
if (json.entities) {
json.entities.forEach((entityJSON) => {
const entity = createEntity();
this.entityList.addEntity(entity.fromJSON(entityJSON));
});
}
if (json.tiles) {
this.tiles = (new Tiles()).fromJSON(json.tiles)
this.tiles.fromJSON(json.tiles)
}
if (json.tilesetUri) {
this.tilesetUri = json.tilesetUri;
@ -22,6 +63,23 @@ export class Layer {
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);
}
removeEntity(entity) {
this.entityList.removeEntity(entity);
}
get state() {
return this._state;
}
@ -31,6 +89,11 @@ export class Layer {
this.entityList.tick(elapsed);
this._state = this._state.set('entityList', this.entityList.state);
}
this._state = this._state.set('tilesetUri', this.tilesetUri);
this.tiles.tick(elapsed);
this._state = this._state.set('tiles', this.tiles.state);
}
}
}
export class Layer extends decorate(LayerBase) {}

View File

@ -0,0 +1,37 @@
import {Container} from '@avocado/graphics';
import {LayerView} from './layer-view';
export class LayersView extends Container {
constructor(layers, renderer) {
super();
this.layers = layers;
this.layerViews = [];
this.renderer = renderer;
this.layers.on('layerAdded', (layer) => {
this.onLayerAdded(layer);
});
for (const layer of layers) {
this.onLayerAdded(layer);
}
}
onLayerAdded(layer) {
const layerView = new LayerView(layer, this.renderer);
this.layerViews.push(layerView);
this.addChild(layerView);
}
onLayerRemoved(layer) {
const layerView = this.layerViews.find((layerView) => {
return layerView.layer === layer;
});
if (!layerView) {
return;
}
this.removeChild(layerView);
}
}

114
packages/topdown/layers.js Normal file
View File

@ -0,0 +1,114 @@
import * as I from 'immutable';
import {compose} from '@avocado/core';
import {EventEmitter} from '@avocado/mixins';
import {Layer} from '@avocado/topdown';
const decorate = compose(
EventEmitter,
);
class LayersBase {
constructor() {
this.layers = [];
this._state = I.Map();
this.onEntityAddedToLayers = this.onEntityAddedToLayers.bind(this);
this.onEntityRemovedFromLayers = this.onEntityRemovedFromLayers.bind(this);
}
*[Symbol.iterator]() {
for (const layer of this.layers) {
yield layer;
}
}
acceptStateChange(change) {
for (const i in change) {
const index = parseInt(i);
const layer = this.layers[index] ? this.layers[index] : new Layer();
if (!this.layers[index]) {
this.addLayer(layer);
}
layer.acceptStateChange(change[i]);
}
}
addEntityToLayer(entity, layerIndex) {
const layer = this.layers[layerIndex];
if (!layer) {
return;
}
layer.addEntity(entity);
}
addLayer(layer) {
layer.on('entityAdded', this.onEntityAddedToLayers);
layer.on('entityRemoved', this.onEntityRemovedFromLayers);
this.layers.push(layer);
this.emit('layerAdded', layer);
}
destroy() {
while (this.layers.length > 0) {
const layer = this.layers.pop();
this.removeLayer(layer);
layer.destroy();
}
}
fromJSON(json) {
if (json) {
for (let i = 0; i < json.length; ++i) {
const layer = new Layer()
this.addLayer(layer);
layer.fromJSON(json[i]);
}
}
return this;
}
onEntityAddedToLayers(entity) {
this.emit('entityAdded', entity);
}
onEntityRemovedFromLayers(entity) {
this.emit('entityRemoved', entity);
}
removeEntityFromLayer(entity, layerIndex) {
const layer = this.layers[layerIndex];
if (!layer) {
return;
}
layer.removeEntity(entity);
}
removeLayer(layer) {
const index = this.layers.indexOf(layer);
if (-1 === index) {
return;
}
layer.off('entityAdded', this.onEntityAddedToLayers);
layer.off('entityRemoved', this.onEntityRemovedFromLayers);
this.layers.splice(index, 1);
this.emit('layerRemoved', layer);
}
get state() {
return this._state;
}
tick(elapsed) {
if (this.layers) {
for (let i = 0; i < this.layers.length; ++i) {
const layer = this.layers[i];
layer.tick(elapsed);
this._state = this._state.set(i, layer.state);
}
}
}
}
export class Layers extends decorate(LayersBase) {}

View File

@ -1,22 +1,14 @@
import {Container, Renderable} from '@avocado/graphics';
import {Container} from '@avocado/graphics';
import {LayerView} from './layer-view';
import {LayersView} from './layers-view';
export class RoomView extends Renderable {
export class RoomView extends Container {
constructor(room, renderer) {
super();
this.container = new Container();
this.room = room;
if (room.layers) {
room.layers.forEach((layer) => {
this.container.addChild(new LayerView(layer, renderer));
});
}
}
get internal() {
return this.container.internal;
const layersView = new LayersView(room.layers, renderer);
this.addChild(layersView);
}
}

View File

@ -1,35 +1,85 @@
import * as I from 'immutable';
import {Layer} from './layer';
import {compose} from '@avocado/core';
import {EventEmitter} from '@avocado/mixins';
export class Room {
import {Layers} from './layers';
const decorate = compose(
EventEmitter,
);
class RoomBase {
constructor() {
this.layers = [];
this._layersState = I.Map();
this.layers = new Layers();
this._state = I.Map();
this._world = undefined;
// 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);
}
acceptStateChange(change) {
if (change.layers) {
this.layers.acceptStateChange(change.layers);
}
}
addEntityToLayer(entity, layerIndex = 0) {
this.layers.addEntityToLayer(entity, layerIndex);
}
destroy() {
this.layers.destroy();
}
fromJSON(json) {
if (json.layers) {
this.layers = json.layers.map((layerJSON) => {
return (new Layer()).fromJSON(layerJSON);
});
this.layers.fromJSON(json.layers);
}
return this;
}
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);
}
removeEntityFromLayer(entity, layerIndex = 0) {
this.layers.removeEntityFromLayer(entity, layerIndex);
}
get state() {
return this._state;
}
tick(elapsed) {
for (let i = 0; i < this.layers.length; ++i) {
const layer = this.layers[i];
layer.tick(elapsed);
this._layersState = this._layersState.set(i, layer.state);
this.layers.tick(elapsed);
this._state = this._state.set('layers', this.layers.state);
if (this._world) {
this._world.tick(elapsed);
}
this._state = this._state.set('layers', this._layersState);
}
}
get world() {
return this._world;
}
set world(world) {
this._world = world;
}
}
export class Room extends decorate(RoomBase) {}