feat: topdown networking
This commit is contained in:
parent
a4040a39b9
commit
ab522d80f8
|
@ -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) {
|
||||
|
|
|
@ -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) {}
|
||||
|
|
37
packages/topdown/layers-view.js
Normal file
37
packages/topdown/layers-view.js
Normal 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
114
packages/topdown/layers.js
Normal 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) {}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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) {}
|
||||
|
|
Loading…
Reference in New Issue
Block a user