refactor: nix layers
This commit is contained in:
parent
6dae625953
commit
2a8ae10ec7
|
@ -28,7 +28,10 @@ export default (latus) => {
|
|||
if ('create' === s13nType) {
|
||||
const {Entity} = latus.get('%resources');
|
||||
const {id} = packet.data.synchronized;
|
||||
this.addEntity(this.synchronized(Entity.resourceId, id));
|
||||
const entity = this.synchronized(Entity.resourceId, id);
|
||||
if (entity) {
|
||||
this.addEntity(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -72,6 +75,9 @@ export default (latus) => {
|
|||
|
||||
async load(json = []) {
|
||||
await super.load(json);
|
||||
if (0 === json.length) {
|
||||
return;
|
||||
}
|
||||
const {Entity} = latus.get('%resources');
|
||||
const entityInstances = await Promise.all(json.map((entity) => Entity.load(entity)));
|
||||
for (let i = 0; i < entityInstances.length; i++) {
|
||||
|
|
60
packages/graphics/src/atlas.js
Normal file
60
packages/graphics/src/atlas.js
Normal file
|
@ -0,0 +1,60 @@
|
|||
import {Rectangle, Vector} from '@avocado/math';
|
||||
import {JsonResource} from '@avocado/resource';
|
||||
|
||||
import Image from './image';
|
||||
|
||||
export default class Atlas extends JsonResource {
|
||||
|
||||
#image;
|
||||
|
||||
#size = [0, 0];
|
||||
|
||||
#subimages = [];
|
||||
|
||||
destroy() {
|
||||
this.#subimages.forEach((subimage) => {
|
||||
subimage.destroy();
|
||||
});
|
||||
this.#subimages = [];
|
||||
}
|
||||
|
||||
get image() {
|
||||
return this.#image;
|
||||
}
|
||||
|
||||
async load(json = {}) {
|
||||
await super.load(json);
|
||||
this.destroy();
|
||||
const {imageUri = json.uri?.replace('.atlas.json', '.png'), type = 'grid'} = json;
|
||||
if (!imageUri) {
|
||||
return;
|
||||
}
|
||||
this.#image = await Image.load(imageUri);
|
||||
switch (type) {
|
||||
case 'grid': {
|
||||
const {size} = json;
|
||||
if (Vector.isNull(size)) {
|
||||
return;
|
||||
}
|
||||
const grid = Vector.div(this.#image.size, size);
|
||||
const rectangle = Rectangle.compose([0, 0], size);
|
||||
for (let j = 0; j < grid[1]; ++j) {
|
||||
for (let i = 0; i < grid[0]; ++i) {
|
||||
const subimage = this.#image.subimage(rectangle);
|
||||
this.#subimages.push(subimage);
|
||||
rectangle[0] += size[0];
|
||||
}
|
||||
rectangle[0] = 0;
|
||||
rectangle[1] += size[1];
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
subimage(index) {
|
||||
return this.#subimages[index];
|
||||
}
|
||||
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
import {Vector} from '@avocado/math';
|
||||
import {Class, compose} from '@latus/core';
|
||||
|
||||
import {Image} from './resources/image';
|
||||
import Image from './image';
|
||||
|
||||
const decorate = compose(
|
||||
Vector.Mixin('size', 'width', 'height', {
|
||||
|
|
|
@ -40,6 +40,12 @@ export default class Container extends Renderable {
|
|||
this.container.addChild(child.internal);
|
||||
}
|
||||
|
||||
addChildren(children) {
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
this.addChild(children[i]);
|
||||
}
|
||||
}
|
||||
|
||||
addFilter(id, type, options = {}) {
|
||||
if (this.container.isFake) {
|
||||
return;
|
||||
|
|
|
@ -15,7 +15,7 @@ const cache = 'production' === process.env.NODE_ENV
|
|||
set: () => {},
|
||||
};
|
||||
|
||||
export class Image extends Resource {
|
||||
export default class Image extends Resource {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
@ -110,5 +110,3 @@ export class Image extends Resource {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
export default () => Image;
|
|
@ -2,11 +2,12 @@ import {gatherWithLatus} from '@latus/core';
|
|||
|
||||
import './init';
|
||||
|
||||
export {default as Atlas} from './atlas';
|
||||
export {default as Canvas} from './canvas';
|
||||
export {default as Color} from './color';
|
||||
export {default as Stage} from './components/stage';
|
||||
export {default as Container} from './container';
|
||||
export {Image} from './resources/image';
|
||||
export {default as Image} from './image';
|
||||
export {default as Primitives} from './primitives';
|
||||
export {default as Renderable} from './renderable';
|
||||
export {default as Renderer} from './renderer';
|
||||
|
@ -15,9 +16,6 @@ export {default as Text} from './text';
|
|||
|
||||
export default {
|
||||
hooks: {
|
||||
'@avocado/resource/resources': gatherWithLatus(
|
||||
require.context('./resources', false, /\.js$/),
|
||||
),
|
||||
'@avocado/traits/traits': gatherWithLatus(
|
||||
require.context('./traits', false, /\.js$/),
|
||||
),
|
||||
|
|
|
@ -3,6 +3,7 @@ import {StateProperty, Trait} from '@avocado/traits';
|
|||
import {Rectangle, Vector} from '@avocado/math';
|
||||
import {compose} from '@latus/core';
|
||||
|
||||
import Image from '../image';
|
||||
import Sprite from '../sprite';
|
||||
|
||||
const decorate = compose(
|
||||
|
@ -11,7 +12,7 @@ const decorate = compose(
|
|||
}),
|
||||
);
|
||||
|
||||
export default (latus) => class Pictured extends decorate(Trait) {
|
||||
export default () => class Pictured extends decorate(Trait) {
|
||||
|
||||
#currentImage = '';
|
||||
|
||||
|
@ -161,7 +162,6 @@ export default (latus) => class Pictured extends decorate(Trait) {
|
|||
.filter(([, {uri}]) => !!uri),
|
||||
);
|
||||
this.#currentImage = this.state.currentImage;
|
||||
const {Image} = latus.get('%resources');
|
||||
this.#images = await mapValuesAsync(images, ({uri}) => Image.load(uri));
|
||||
this.#sprites = await mapValuesAsync(this.#images, async (image) => new Sprite(image));
|
||||
Object.keys(this.#sprites).forEach((key) => {
|
||||
|
|
|
@ -58,9 +58,7 @@ export default () => class Rastered extends Trait {
|
|||
},
|
||||
|
||||
onYChanged: () => {
|
||||
if (this.#usingAutoZIndex) {
|
||||
this.#container.zIndex = this.entity.y;
|
||||
}
|
||||
this.onYChanged();
|
||||
},
|
||||
|
||||
opacityChanged: () => {
|
||||
|
@ -77,16 +75,6 @@ export default () => class Rastered extends Trait {
|
|||
this.#container.rotation = this.entity.rotation;
|
||||
},
|
||||
|
||||
traitAdded: () => {
|
||||
if (this.#container) {
|
||||
this.#container.alpha = this.entity.opacity;
|
||||
this.#container.rotation = this.entity.rotation;
|
||||
this.#container.scale = this.entity.visibleScale;
|
||||
}
|
||||
this.synchronizePosition();
|
||||
this.onZIndexChanged(this.entity.zIndex);
|
||||
},
|
||||
|
||||
visibleScaleChanged: () => {
|
||||
if (this.#container) {
|
||||
this.#container.scale = this.entity.visibleScale;
|
||||
|
@ -103,6 +91,13 @@ export default () => class Rastered extends Trait {
|
|||
async load(json) {
|
||||
await super.load(json);
|
||||
if ('client' === process.env.SIDE) {
|
||||
if (this.#container) {
|
||||
this.#container.alpha = this.entity.opacity;
|
||||
this.#container.rotation = this.entity.rotation;
|
||||
this.#container.scale = this.entity.visibleScale;
|
||||
}
|
||||
this.onZIndexChanged(this.entity.zIndex);
|
||||
this.renderTick();
|
||||
const {filter} = this.params;
|
||||
if (filter) {
|
||||
this.#container.setFilter(filter);
|
||||
|
@ -150,21 +145,28 @@ export default () => class Rastered extends Trait {
|
|||
}
|
||||
}
|
||||
|
||||
onYChanged() {
|
||||
if (this.#usingAutoZIndex) {
|
||||
this.#container.zIndex = this.entity.y;
|
||||
}
|
||||
}
|
||||
|
||||
onZIndexChanged(zIndex) {
|
||||
this.#usingAutoZIndex = AUTO_ZINDEX === zIndex;
|
||||
if (!this.#container) {
|
||||
return;
|
||||
}
|
||||
this.#usingAutoZIndex = AUTO_ZINDEX === zIndex;
|
||||
if (!this.#usingAutoZIndex) {
|
||||
this.#container.zIndex = zIndex;
|
||||
}
|
||||
else {
|
||||
this.#container.zIndex = this.entity.y;
|
||||
this.onYChanged();
|
||||
}
|
||||
}
|
||||
|
||||
renderTick() {
|
||||
this.synchronizePosition();
|
||||
this.synchronizeZIndex();
|
||||
}
|
||||
|
||||
synchronizePosition() {
|
||||
|
@ -172,7 +174,10 @@ export default () => class Rastered extends Trait {
|
|||
return;
|
||||
}
|
||||
this.#container.position = this.entity.position;
|
||||
if (this.#usingAutoZIndex) {
|
||||
}
|
||||
|
||||
synchronizeZIndex() {
|
||||
if (this.#container && this.#usingAutoZIndex) {
|
||||
this.#container.zIndex = this.entity.y;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import {assert, expect} from 'chai';
|
||||
import {validate} from 'uuid';
|
||||
|
||||
import {Image} from '../src/resources/image';
|
||||
import Image from '../src/image';
|
||||
|
||||
Image.root = 'test/fixtures';
|
||||
|
||||
|
|
|
@ -1,5 +1,24 @@
|
|||
export default (EntityList) => class PhysicsEntityList extends EntityList {
|
||||
|
||||
#world;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.on('entityAdded', this.onEntityAdded, this);
|
||||
this.on('entityRemoved', this.onEntityRemoved, this);
|
||||
}
|
||||
|
||||
onEntityAdded(entity) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
entity.world = this.#world;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
onEntityRemoved(entity) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
entity.world = null;
|
||||
}
|
||||
|
||||
set world(world) {
|
||||
const entities = Object.values(this.entities);
|
||||
for (let i = 0; i < entities.length; i++) {
|
||||
|
@ -8,6 +27,7 @@ export default (EntityList) => class PhysicsEntityList extends EntityList {
|
|||
entity.world = world;
|
||||
}
|
||||
}
|
||||
this.#world = world;
|
||||
}
|
||||
|
||||
};
|
||||
|
|
|
@ -1,77 +0,0 @@
|
|||
import {
|
||||
Vector,
|
||||
Vertice,
|
||||
} from '@avocado/math';
|
||||
|
||||
import PolygonShape from '../../shape/polygon';
|
||||
|
||||
export default (Layer) => class PhysicsLayer extends Layer {
|
||||
|
||||
#impassable = {};
|
||||
|
||||
#tileBodies = [];
|
||||
|
||||
#world;
|
||||
|
||||
addTileBodies() {
|
||||
if (!this.#world) {
|
||||
return;
|
||||
}
|
||||
const {tileset} = this;
|
||||
if (!tileset) {
|
||||
return;
|
||||
}
|
||||
const hulls = this.tiles.indexHulls(this.#impassable);
|
||||
if (0 === hulls.length) {
|
||||
return;
|
||||
}
|
||||
for (let j = 0; j < hulls.length; ++j) {
|
||||
const scaled = [];
|
||||
for (let k = 0; k < hulls[j].length; ++k) {
|
||||
scaled.push(Vector.mul(hulls[j][k], tileset.tileSize));
|
||||
}
|
||||
const [vertices, position] = Vertice.localize(scaled);
|
||||
const shape = new PolygonShape({position, vertices});
|
||||
const body = this.#world.createBody(shape);
|
||||
body.static = true;
|
||||
this.#tileBodies.push(body);
|
||||
this.#world.addBody(body);
|
||||
}
|
||||
}
|
||||
|
||||
async load(json = {}) {
|
||||
await super.load(json);
|
||||
const {impassable = []} = json;
|
||||
this.#impassable = new Set(impassable);
|
||||
}
|
||||
|
||||
onTilesUpdate() {
|
||||
this.removeTileBodies();
|
||||
this.addTileBodies();
|
||||
}
|
||||
|
||||
removeTileBodies() {
|
||||
if (this.#tileBodies.length > 0 && this.#world) {
|
||||
for (let i = 0; i < this.#tileBodies.length; i++) {
|
||||
this.#world.removeBody(this.#tileBodies[i]);
|
||||
}
|
||||
this.#tileBodies = [];
|
||||
}
|
||||
}
|
||||
|
||||
setTiles(tiles) {
|
||||
if (this.tiles) {
|
||||
this.tiles.off('update', this.onTilesUpdate);
|
||||
}
|
||||
super.setTiles(tiles);
|
||||
this.tiles.on('update', this.onTilesUpdate, this);
|
||||
}
|
||||
|
||||
set world(world) {
|
||||
this.removeTileBodies();
|
||||
this.#world = world;
|
||||
this.entityList.world = world;
|
||||
this.addTileBodies();
|
||||
}
|
||||
|
||||
};
|
|
@ -1,9 +0,0 @@
|
|||
export default (Layers) => class PhysicsLayers extends Layers {
|
||||
|
||||
set world(world) {
|
||||
for (let index = 0; index < this.layers.length; index++) {
|
||||
this.layers[index].world = world;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
|
@ -22,7 +22,11 @@ export default (Room) => class PhysicsRoom extends Room {
|
|||
const world = new World();
|
||||
world.stepTime = 1 / 60;
|
||||
this.world = world;
|
||||
this.layers.world = world;
|
||||
this.entityList.world = world;
|
||||
this.tiles.forEach((tiles) => {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
tiles.world = world;
|
||||
});
|
||||
if (Vector.isZero(this.size)) {
|
||||
return;
|
||||
}
|
||||
|
|
65
packages/physics/src/resources/decorators/tiles.js
Normal file
65
packages/physics/src/resources/decorators/tiles.js
Normal file
|
@ -0,0 +1,65 @@
|
|||
import {
|
||||
Vector,
|
||||
Vertice,
|
||||
} from '@avocado/math';
|
||||
|
||||
import PolygonShape from '../../shape/polygon';
|
||||
|
||||
export default (Tiles) => class PhysicsTiles extends Tiles {
|
||||
|
||||
#bodies = [];
|
||||
|
||||
#impassable = {};
|
||||
|
||||
#world;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.on('update', this.onTilesUpdate, this);
|
||||
}
|
||||
|
||||
addBodies() {
|
||||
if (!this.#world) {
|
||||
return;
|
||||
}
|
||||
const hulls = this.indexHulls(this.#impassable);
|
||||
for (let i = 0; i < hulls.length; ++i) {
|
||||
const scaled = [];
|
||||
for (let j = 0; j < hulls[i].length; ++j) {
|
||||
scaled.push(Vector.mul(hulls[i][j], this.tileSize));
|
||||
}
|
||||
const [vertices, position] = Vertice.localize(scaled);
|
||||
const body = this.#world.createBody(new PolygonShape({position, vertices}));
|
||||
body.static = true;
|
||||
this.#bodies.push(body);
|
||||
this.#world.addBody(body);
|
||||
}
|
||||
}
|
||||
|
||||
async load(json = {}) {
|
||||
await super.load(json);
|
||||
const {impassable = []} = json;
|
||||
this.#impassable = new Set(impassable);
|
||||
}
|
||||
|
||||
onTilesUpdate() {
|
||||
this.removeBodies();
|
||||
this.addBodies();
|
||||
}
|
||||
|
||||
removeBodies() {
|
||||
if (this.#bodies.length > 0 && this.#world) {
|
||||
for (let i = 0; i < this.#bodies.length; i++) {
|
||||
this.#world.removeBody(this.#bodies[i]);
|
||||
}
|
||||
this.#bodies = [];
|
||||
}
|
||||
}
|
||||
|
||||
set world(world) {
|
||||
this.removeBodies();
|
||||
this.#world = world;
|
||||
this.addBodies();
|
||||
}
|
||||
|
||||
};
|
|
@ -3,7 +3,6 @@ import {gatherWithLatus} from '@latus/core';
|
|||
export {default as Room} from './components/room';
|
||||
|
||||
export {default as Camera} from './camera';
|
||||
export {default as LayerView} from './layer-view';
|
||||
export {default as RoomView} from './room-view';
|
||||
|
||||
export default {
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
import {EntityListView} from '@avocado/entity';
|
||||
import {Container} from '@avocado/graphics';
|
||||
|
||||
import TilesView from './tiles-view';
|
||||
|
||||
const views = new Set();
|
||||
|
||||
export default class LayerView extends Container {
|
||||
|
||||
constructor(layer, renderer) {
|
||||
super();
|
||||
this.entityListView = new EntityListView(layer.entityList);
|
||||
this.tilesView = new TilesView(layer.tiles, layer.tileset, renderer);
|
||||
this.lastExtent = [0, 0, 0, 0];
|
||||
this.layer = layer;
|
||||
this.renderer = renderer;
|
||||
this.addChild(this.tilesView);
|
||||
this.addChild(this.entityListView);
|
||||
if (module.hot) {
|
||||
views.add(this);
|
||||
}
|
||||
}
|
||||
|
||||
destroy() {
|
||||
super.destroy();
|
||||
if (module.hot) {
|
||||
views.delete(this);
|
||||
}
|
||||
}
|
||||
|
||||
renderChunksForExtent(extent) {
|
||||
this.lastExtent = extent;
|
||||
this.tilesView.renderChunksForExtent(extent);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (module.hot) {
|
||||
module.hot.accept('./tiles-view', () => {
|
||||
const it = views.values();
|
||||
for (let value = it.next(); value.done !== true; value = it.next()) {
|
||||
const {value: view} = value;
|
||||
const {lastExtent} = view;
|
||||
view.removeChild(view.entityListView);
|
||||
view.removeChild(view.tilesView);
|
||||
view.tilesView = new TilesView(view.layer.tiles, view.layer.tileset, view.renderer);
|
||||
view.renderChunksForExtent(lastExtent);
|
||||
view.addChild(view.tilesView);
|
||||
view.addChild(view.entityListView);
|
||||
}
|
||||
});
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
import {Container} from '@avocado/graphics';
|
||||
|
||||
import LayerView from './layer-view';
|
||||
|
||||
export default class LayersView extends Container {
|
||||
|
||||
constructor(layers, renderer) {
|
||||
super();
|
||||
this.layers = layers;
|
||||
this.layerViews = [];
|
||||
this.renderer = renderer;
|
||||
this.layers.on('layerAdded', this.onLayerAdded, this);
|
||||
for (let i = 0; i < this.layers.layers.length; i++) {
|
||||
this.onLayerAdded(this.layers.layers[i], i);
|
||||
}
|
||||
this.layers.on('layerRemoved', this.onLayerRemoved, this);
|
||||
}
|
||||
|
||||
onLayerAdded(layer, i) {
|
||||
const layerView = new LayerView(layer, this.renderer);
|
||||
this.layerViews[i] = layerView;
|
||||
this.addChild(layerView);
|
||||
}
|
||||
|
||||
onLayerRemoved(layer, i) {
|
||||
const layerView = this.layerViews[i];
|
||||
if (!layerView) {
|
||||
return;
|
||||
}
|
||||
this.removeChild(layerView);
|
||||
}
|
||||
|
||||
renderChunksForExtent(extent) {
|
||||
for (let i = 0; i < this.layerViews.length; i++) {
|
||||
this.layerViews[i].renderChunksForExtent(extent);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,157 +0,0 @@
|
|||
import {Property} from '@avocado/core';
|
||||
import {JsonResource} from '@avocado/resource';
|
||||
import {Synchronized} from '@avocado/s13n';
|
||||
import {compose, EventEmitter} from '@latus/core';
|
||||
|
||||
export default (latus) => {
|
||||
const decorate = compose(
|
||||
EventEmitter,
|
||||
Property('tileset', {
|
||||
track: true,
|
||||
}),
|
||||
Synchronized(latus),
|
||||
);
|
||||
return class Layer extends decorate(JsonResource) {
|
||||
|
||||
#index;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
const {EntityList, Tiles} = latus.get('%resources');
|
||||
this.setEntityList(new EntityList());
|
||||
this.setTiles(new Tiles());
|
||||
}
|
||||
|
||||
addEntity(entity) {
|
||||
this.entityList.addEntity(entity);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.entityList.destroy();
|
||||
this.entityList.off('entityAdded', this.onEntityAddedToLayer);
|
||||
this.entityList.off('entityRemoved', this.onEntityRemovedFromLayer);
|
||||
if (this.tileset) {
|
||||
this.tileset.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
get entities() {
|
||||
return this.entityList.entities;
|
||||
}
|
||||
|
||||
findEntity(uuid) {
|
||||
return this.entityList.findEntity(uuid);
|
||||
}
|
||||
|
||||
get index() {
|
||||
return this.#index;
|
||||
}
|
||||
|
||||
set index(index) {
|
||||
this.#index = index;
|
||||
}
|
||||
|
||||
async load(json = {}) {
|
||||
await super.load(json);
|
||||
const {
|
||||
entities,
|
||||
tiles,
|
||||
tilesetUri,
|
||||
} = json;
|
||||
const {EntityList, Tileset} = latus.get('%resources');
|
||||
await this.tiles.load(tiles);
|
||||
this.tileset = tilesetUri
|
||||
? await Tileset.load({extends: tilesetUri})
|
||||
: new Tileset();
|
||||
this.setEntityList(
|
||||
entities
|
||||
? await EntityList.load(entities)
|
||||
: new EntityList(),
|
||||
);
|
||||
}
|
||||
|
||||
async onEntityAddedToLayer(entity) {
|
||||
await entity.addTrait('Layered');
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
entity.layer = this;
|
||||
entity.emit('addedToLayer');
|
||||
this.emit('entityAdded', entity);
|
||||
}
|
||||
|
||||
onEntityRemovedFromLayer(entity) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
entity.layer = null;
|
||||
entity.emit('removedFromLayer', this);
|
||||
entity.removeTrait('Layered');
|
||||
this.emit('entityRemoved', entity);
|
||||
}
|
||||
|
||||
removeEntity(entity) {
|
||||
this.entityList.removeEntity(entity);
|
||||
}
|
||||
|
||||
get s13nId() {
|
||||
return this.#index;
|
||||
}
|
||||
|
||||
setEntityList(entityList) {
|
||||
if (this.entityList) {
|
||||
this.stopSynchronizing(this.entityList);
|
||||
Object.values(this.entityList.entities).forEach((entity) => {
|
||||
this.onEntityRemovedFromLayer(entity);
|
||||
});
|
||||
this.entityList.off('entityRemoved', this.onEntityRemovedFromLayer);
|
||||
this.entityList.off('entityAdded', this.onEntityAddedToLayer);
|
||||
}
|
||||
this.entityList = entityList;
|
||||
this.entityList.on('entityAdded', this.onEntityAddedToLayer, this);
|
||||
this.entityList.on('entityRemoved', this.onEntityRemovedFromLayer, this);
|
||||
Object.values(this.entityList.entities).forEach((entity) => {
|
||||
this.onEntityAddedToLayer(entity);
|
||||
});
|
||||
this.startSynchronizing(this.entityList);
|
||||
}
|
||||
|
||||
setTileAt(position, tile) {
|
||||
this.tiles.setTileAt(position, tile);
|
||||
}
|
||||
|
||||
setTiles(tiles) {
|
||||
if (this.tiles) {
|
||||
this.stopSynchronizing(this.tiles);
|
||||
}
|
||||
this.tiles = tiles;
|
||||
this.startSynchronizing(tiles);
|
||||
}
|
||||
|
||||
stampAt([x, y, w, h], tiles) {
|
||||
this.tiles.stampAt([x, y, w, h], tiles);
|
||||
}
|
||||
|
||||
tick(elapsed) {
|
||||
this.entityList.tick(elapsed);
|
||||
this.tiles.tick();
|
||||
}
|
||||
|
||||
tileAt(position) {
|
||||
return this.tiles.tileAt(position);
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
return {
|
||||
entities: this.entityList.toJSON(),
|
||||
tilesetUri: this.tileset.uri,
|
||||
tiles: this.tiles.toJSON(),
|
||||
};
|
||||
}
|
||||
|
||||
toNetwork(informed) {
|
||||
return {
|
||||
entities: this.entityList.toNetwork(informed),
|
||||
tilesetUri: this.tileset.uri,
|
||||
tiles: this.tiles.toNetwork(informed),
|
||||
};
|
||||
}
|
||||
|
||||
};
|
||||
};
|
|
@ -1,128 +0,0 @@
|
|||
import {compose, EventEmitter} from '@latus/core';
|
||||
import {JsonResource} from '@avocado/resource';
|
||||
import {Synchronized} from '@avocado/s13n';
|
||||
|
||||
export default (latus) => {
|
||||
const decorate = compose(
|
||||
EventEmitter,
|
||||
Synchronized(latus),
|
||||
);
|
||||
return class Layers extends decorate(JsonResource) {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.layers = [];
|
||||
}
|
||||
|
||||
addEntityToLayer(entity, layerIndex) {
|
||||
const layer = this.layers[layerIndex];
|
||||
if (!layer) {
|
||||
return;
|
||||
}
|
||||
layer.addEntity(entity);
|
||||
}
|
||||
|
||||
addLayer(layer) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
layer.index = this.layers.length;
|
||||
this.startSynchronizing(layer);
|
||||
layer.on('entityAdded', this.onEntityAddedToLayers, this);
|
||||
layer.on('entityRemoved', this.onEntityRemovedFromLayers, this);
|
||||
this.layers.push(layer);
|
||||
this.emit('layerAdded', layer);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
for (let i = 0; i < this.layers.length; i++) {
|
||||
const layer = this.layers[i];
|
||||
this.removeLayer(layer);
|
||||
layer.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
get entities() {
|
||||
const entities = {};
|
||||
for (let i = 0; i < this.layers.length; i++) {
|
||||
const layerEntities = Object.entries(this.layers[i].entities);
|
||||
for (let j = 0; j < layerEntities.length; j++) {
|
||||
const [uuid, entity] = layerEntities[j];
|
||||
entities[uuid] = entity;
|
||||
}
|
||||
}
|
||||
return entities;
|
||||
}
|
||||
|
||||
findEntity(uuid) {
|
||||
for (let i = 0; i < this.layers.length; i++) {
|
||||
const foundEntity = this.layers[i].findEntity(uuid);
|
||||
if (foundEntity) {
|
||||
return foundEntity;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
layer(index) {
|
||||
return this.layers[index];
|
||||
}
|
||||
|
||||
async load(json = []) {
|
||||
await super.load(json);
|
||||
this.removeAllLayers();
|
||||
const {Layer} = latus.get('%resources');
|
||||
const layers = await Promise.all(json.map((layer) => Layer.load(layer)));
|
||||
for (let i = 0; i < layers.length; i++) {
|
||||
this.addLayer(layers[i]);
|
||||
}
|
||||
}
|
||||
|
||||
onEntityAddedToLayers(entity) {
|
||||
this.emit('entityAdded', entity);
|
||||
}
|
||||
|
||||
onEntityRemovedFromLayers(entity) {
|
||||
this.emit('entityRemoved', entity);
|
||||
}
|
||||
|
||||
removeAllLayers() {
|
||||
for (let i = 0; i < this.layers.length; i++) {
|
||||
this.removeLayer(this.layers[i]);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
this.stopSynchronizing(layer);
|
||||
}
|
||||
|
||||
tick(elapsed) {
|
||||
for (let i = 0; i < this.layers.length; i++) {
|
||||
this.layers[i].tick(elapsed);
|
||||
}
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
return this.layers.map((layer) => layer.toJSON());
|
||||
}
|
||||
|
||||
toNetwork(informed) {
|
||||
return this.layers.map((layer) => layer.toNetwork(informed));
|
||||
}
|
||||
|
||||
};
|
||||
};
|
|
@ -15,84 +15,68 @@ export default (latus) => {
|
|||
);
|
||||
return class Room extends decorate(JsonResource) {
|
||||
|
||||
entityList;
|
||||
|
||||
tiles = [];
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._s13nId = s13nId++;
|
||||
const {Layers} = latus.get('%resources');
|
||||
this.setLayers(new Layers());
|
||||
const {EntityList} = latus.get('%resources');
|
||||
this.entityList = new EntityList();
|
||||
this.entityList.on('entityAdded', this.onEntityAdded, this);
|
||||
this.entityList.on('entityRemoved', this.onEntityRemoved, this);
|
||||
}
|
||||
|
||||
addEntityToLayer(entity, layerIndex = 0) {
|
||||
this.layers.addEntityToLayer(entity, layerIndex);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
super.destroy();
|
||||
this.layers.destroy();
|
||||
this.layers.off('entityAdded', this.onEntityAddedToRoom);
|
||||
this.layers.off('entityRemoved', this.onEntityRemovedFromRoom);
|
||||
addEntity(entity) {
|
||||
this.entityList.addEntity(entity);
|
||||
}
|
||||
|
||||
get entities() {
|
||||
return this.layers.entities;
|
||||
return this.entityList.entities;
|
||||
}
|
||||
|
||||
findEntity(uuid) {
|
||||
return this.layers.findEntity(uuid);
|
||||
}
|
||||
|
||||
layer(index) {
|
||||
return this.layers.layer(index);
|
||||
return this.entityList.findEntity(uuid);
|
||||
}
|
||||
|
||||
async load(json = {}) {
|
||||
await super.load(json);
|
||||
const {layers, size} = json;
|
||||
this.size = size || [0, 0];
|
||||
const {Layers} = latus.get('%resources');
|
||||
this.setLayers(
|
||||
layers
|
||||
? await Layers.load(layers)
|
||||
: new Layers(),
|
||||
);
|
||||
const {Tiles} = latus.get('%resources');
|
||||
const {
|
||||
entities,
|
||||
size = [0, 0],
|
||||
tiles,
|
||||
} = json;
|
||||
if (entities) {
|
||||
await this.entityList.load(entities);
|
||||
}
|
||||
this.startSynchronizing(this.entityList);
|
||||
this.size = size;
|
||||
if (tiles) {
|
||||
this.tiles = await Promise.all(tiles.map((tiles, i) => Tiles.load({s13nId: i, ...tiles})));
|
||||
this.tiles.forEach((tiles) => {
|
||||
this.startSynchronizing(tiles);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onEntityAddedToRoom(entity) {
|
||||
onEntityAdded(entity) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
entity.room = this;
|
||||
entity.emit('addedToRoom');
|
||||
this.emit('entityAdded', entity);
|
||||
}
|
||||
|
||||
onEntityRemovedFromRoom(entity) {
|
||||
onEntityRemoved(entity) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
entity.room = null;
|
||||
entity.emit('removedFromRoom', this);
|
||||
this.emit('entityRemoved', entity);
|
||||
}
|
||||
|
||||
removeEntityFromLayer(entity, layerIndex) {
|
||||
this.layers.removeEntityFromLayer(entity, layerIndex);
|
||||
}
|
||||
|
||||
setLayers(layers) {
|
||||
if (this.layers) {
|
||||
this.stopSynchronizing(this.layers);
|
||||
const entities = Object.values(this.layers.entities);
|
||||
for (let i = 0; i < entities.length; i++) {
|
||||
this.onEntityRemovedFromRoom(entities[i]);
|
||||
}
|
||||
this.layers.off('entityAdded', this.onEntityAddedToRoom);
|
||||
this.layers.off('entityRemoved', this.onEntityRemovedFromRoom);
|
||||
}
|
||||
this.layers = layers;
|
||||
this.layers.on('entityRemoved', this.onEntityRemovedFromRoom, this);
|
||||
this.layers.on('entityAdded', this.onEntityAddedToRoom, this);
|
||||
const entities = Object.values(this.layers.entities);
|
||||
for (let i = 0; i < entities.length; i++) {
|
||||
this.onEntityAddedToRoom(entities[i]);
|
||||
}
|
||||
this.startSynchronizing(this.layers);
|
||||
removeEntity(entity) {
|
||||
this.entityList.removeEntity(entity);
|
||||
}
|
||||
|
||||
get s13nId() {
|
||||
|
@ -100,20 +84,25 @@ export default (latus) => {
|
|||
}
|
||||
|
||||
tick(elapsed) {
|
||||
this.layers.tick(elapsed);
|
||||
this.entityList.tick(elapsed);
|
||||
for (let i = 0; i < this.tiles.length; i++) {
|
||||
this.tiles[i].tick(elapsed);
|
||||
}
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
return {
|
||||
layer: this.layers.toJSON(),
|
||||
entities: this.entityList.toJSON(),
|
||||
size: this.size,
|
||||
tiles: this.tiles.map((tiles) => tiles.toJSON()),
|
||||
};
|
||||
}
|
||||
|
||||
toNetwork(informed) {
|
||||
return {
|
||||
layers: this.layers.toNetwork(informed),
|
||||
entities: this.entityList.toNetwork(informed),
|
||||
size: this.size,
|
||||
tiles: this.tiles.map((tiles) => tiles.toNetwork(informed)),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,38 +1,49 @@
|
|||
import {Atlas} from '@avocado/graphics';
|
||||
import {
|
||||
floodwalk2D,
|
||||
Rectangle,
|
||||
Vector,
|
||||
Vertice,
|
||||
} from '@avocado/math';
|
||||
import {JsonResource} from '@avocado/resource';
|
||||
import {Synchronized} from '@avocado/s13n';
|
||||
import {
|
||||
Class,
|
||||
compose,
|
||||
deflate,
|
||||
EventEmitter,
|
||||
inflate,
|
||||
} from '@latus/core';
|
||||
import {Synchronized} from '@avocado/s13n';
|
||||
|
||||
// TODO: rows, with nulls
|
||||
|
||||
export default (latus) => {
|
||||
const decorate = compose(
|
||||
EventEmitter,
|
||||
Vector.Mixin('size', 'width', 'height', {
|
||||
Vector.Mixin('area', 'width', 'height', {
|
||||
default: [0, 0],
|
||||
}),
|
||||
Synchronized(latus),
|
||||
);
|
||||
return class Tiles extends decorate(Class) {
|
||||
return class Tiles extends decorate(JsonResource) {
|
||||
|
||||
#data = [];
|
||||
#atlas = new Atlas();
|
||||
|
||||
#data = new Uint16Array();
|
||||
|
||||
#hasUpdates = false;
|
||||
|
||||
#packets = [];
|
||||
|
||||
#s13nId = 0;
|
||||
|
||||
#tileImageUri;
|
||||
|
||||
#tileSize = [0, 0];
|
||||
|
||||
#updates = [];
|
||||
|
||||
#zIndex = 0;
|
||||
|
||||
acceptPacket(packet) {
|
||||
if ('TilesUpdate' === packet.constructor.type) {
|
||||
const {position: [x, y], size: [w, h], tiles} = packet.data;
|
||||
|
@ -46,6 +57,10 @@ export default (latus) => {
|
|||
this.#updates = [];
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.#atlas.destroy();
|
||||
}
|
||||
|
||||
static indexHulls(indices, data, size) {
|
||||
const hulls = [];
|
||||
const seen = [];
|
||||
|
@ -108,18 +123,43 @@ export default (latus) => {
|
|||
}
|
||||
|
||||
indexHulls(indices) {
|
||||
return this.constructor.indexHulls(indices, this.#data, this.size);
|
||||
return this.constructor.indexHulls(indices, this.#data, this.area);
|
||||
}
|
||||
|
||||
async load(json) {
|
||||
const {data, size} = json;
|
||||
if (size) {
|
||||
this.size = size;
|
||||
const {
|
||||
area,
|
||||
data,
|
||||
s13nId,
|
||||
tileImageUri,
|
||||
tileSize,
|
||||
zIndex,
|
||||
} = json;
|
||||
if (area) {
|
||||
this.area = area;
|
||||
}
|
||||
if (data) {
|
||||
const {buffer, byteOffset, length} = inflate(Buffer.from(data, 'base64'));
|
||||
this.#data = new Uint16Array(buffer, byteOffset, length / 2);
|
||||
}
|
||||
else if (area) {
|
||||
this.#data = new Uint16Array(Vector.area(area));
|
||||
}
|
||||
if (tileImageUri && tileSize) {
|
||||
this.#tileSize = tileSize;
|
||||
this.#tileImageUri = tileImageUri;
|
||||
await this.#atlas.load({
|
||||
imageUri: tileImageUri,
|
||||
type: 'grid',
|
||||
size: tileSize,
|
||||
});
|
||||
}
|
||||
if (s13nId) {
|
||||
this.#s13nId = s13nId;
|
||||
}
|
||||
if (zIndex > 0) {
|
||||
this.#zIndex = zIndex;
|
||||
}
|
||||
}
|
||||
|
||||
packetsFor() {
|
||||
|
@ -144,11 +184,15 @@ export default (latus) => {
|
|||
}
|
||||
|
||||
get rectangle() {
|
||||
return Rectangle.compose([0, 0], this.size);
|
||||
return Rectangle.compose([0, 0], this.area);
|
||||
}
|
||||
|
||||
get s13nId() {
|
||||
return this.#s13nId;
|
||||
}
|
||||
|
||||
setTileAt([x, y], tile) {
|
||||
const [w, h] = this.size;
|
||||
const [w, h] = this.area;
|
||||
const index = y * w + x;
|
||||
if (x < 0 || x >= w || y < 0 || y >= h || this.#data[index] === tile) {
|
||||
return;
|
||||
|
@ -165,7 +209,7 @@ export default (latus) => {
|
|||
if (w <= 0 || h <= 0) {
|
||||
return [];
|
||||
}
|
||||
const [fw, fh] = this.size;
|
||||
const [fw, fh] = this.area;
|
||||
const n = fw - w;
|
||||
const slice = new Array(w * h);
|
||||
let i = y * fw + x;
|
||||
|
@ -189,7 +233,7 @@ export default (latus) => {
|
|||
if (0 === w || 0 === h || 0 === tiles.length) {
|
||||
return;
|
||||
}
|
||||
const [fw, fh] = this.size;
|
||||
const [fw, fh] = this.area;
|
||||
if (x < 0 || x >= fw || y < 0 || y >= fh) {
|
||||
return;
|
||||
}
|
||||
|
@ -219,6 +263,10 @@ export default (latus) => {
|
|||
}
|
||||
}
|
||||
|
||||
subimage(index) {
|
||||
return this.#atlas.subimage(index);
|
||||
}
|
||||
|
||||
tick() {
|
||||
if (this.#hasUpdates) {
|
||||
this.emit('update');
|
||||
|
@ -227,20 +275,33 @@ export default (latus) => {
|
|||
}
|
||||
|
||||
tileAt([x, y]) {
|
||||
const [w, h] = this.size;
|
||||
const [w, h] = this.area;
|
||||
return x < 0 || x >= w || y < 0 || y >= h ? undefined : this.#data[y * w + x];
|
||||
}
|
||||
|
||||
get tileSize() {
|
||||
return this.#tileSize;
|
||||
}
|
||||
|
||||
toNetwork() {
|
||||
return this.toJSON();
|
||||
return {
|
||||
...this.toJSON(),
|
||||
s13nId: this.#s13nId,
|
||||
};
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
return {
|
||||
size: this.size,
|
||||
area: this.area,
|
||||
data: deflate(this.#data).toString('base64'),
|
||||
tileSize: this.#tileSize,
|
||||
...(this.#tileImageUri ? {tileImageUri: this.#tileImageUri} : []),
|
||||
};
|
||||
}
|
||||
|
||||
get zIndex() {
|
||||
return this.#zIndex;
|
||||
}
|
||||
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,86 +0,0 @@
|
|||
import {Property} from '@avocado/core';
|
||||
import {Image} from '@avocado/graphics';
|
||||
import {Rectangle, Vector} from '@avocado/math';
|
||||
import {JsonResource} from '@avocado/resource';
|
||||
import {compose, EventEmitter} from '@latus/core';
|
||||
|
||||
const decorate = compose(
|
||||
EventEmitter,
|
||||
Property('image', {
|
||||
track: true,
|
||||
}),
|
||||
Vector.Mixin('tileSize', 'tileWidth', 'tileHeight', {
|
||||
default: [0, 0],
|
||||
}),
|
||||
);
|
||||
|
||||
export default () => class Tileset extends decorate(JsonResource) {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.subimages = [];
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.subimages.forEach((subimage) => {
|
||||
subimage.destroy();
|
||||
});
|
||||
this.subimages = [];
|
||||
}
|
||||
|
||||
get image() {
|
||||
return super.image;
|
||||
}
|
||||
|
||||
set image(image) {
|
||||
this.recalculateSubimages(image);
|
||||
super.image = image;
|
||||
}
|
||||
|
||||
async load(json = {}) {
|
||||
await super.load(json);
|
||||
const {imageUri, tileSize} = json;
|
||||
if (imageUri) {
|
||||
this.image = await Image.load(imageUri);
|
||||
}
|
||||
if (tileSize) {
|
||||
this.tileSize = tileSize;
|
||||
}
|
||||
}
|
||||
|
||||
recalculateSubimages(image) {
|
||||
this.destroy();
|
||||
if (!image) {
|
||||
return;
|
||||
}
|
||||
const {tileSize} = this;
|
||||
if (Vector.isNull(tileSize)) {
|
||||
return;
|
||||
}
|
||||
const grid = Vector.div(image.size, tileSize);
|
||||
const rectangle = Rectangle.compose([0, 0], tileSize);
|
||||
for (let j = 0; j < grid[1]; ++j) {
|
||||
for (let i = 0; i < grid[0]; ++i) {
|
||||
const subimage = image.subimage(rectangle);
|
||||
this.subimages.push(subimage);
|
||||
rectangle[0] += tileSize[0];
|
||||
}
|
||||
rectangle[0] = 0;
|
||||
rectangle[1] += tileSize[1];
|
||||
}
|
||||
}
|
||||
|
||||
subimage(index) {
|
||||
return this.subimages[index];
|
||||
}
|
||||
|
||||
get tileSize() {
|
||||
return super.tileSize;
|
||||
}
|
||||
|
||||
set tileSize(tileSize) {
|
||||
super.tileSize = tileSize;
|
||||
this.recalculateSubimages(this.image);
|
||||
}
|
||||
|
||||
};
|
|
@ -1,19 +1,55 @@
|
|||
import {EntityListView} from '@avocado/entity';
|
||||
import {Container} from '@avocado/graphics';
|
||||
|
||||
import LayersView from './layers-view';
|
||||
import TilesView from './tiles-view';
|
||||
|
||||
const views = new Set();
|
||||
|
||||
export default class RoomView extends Container {
|
||||
|
||||
layers = [];
|
||||
|
||||
constructor(room, renderer) {
|
||||
super();
|
||||
this.room = room;
|
||||
this.layersView = new LayersView(room.layers, renderer);
|
||||
this.addChild(this.layersView);
|
||||
this.lastExtent = [0, 0, 0, 0];
|
||||
this.renderer = renderer;
|
||||
this.entityListView = new EntityListView(room.entityList);
|
||||
this.tilesViews = room.tiles.map((tiles) => new TilesView(tiles, renderer));
|
||||
this.addChildren(this.tilesViews);
|
||||
this.addChild(this.entityListView);
|
||||
this.sort();
|
||||
if (module.hot) {
|
||||
views.add(this);
|
||||
}
|
||||
}
|
||||
|
||||
destroy() {
|
||||
super.destroy();
|
||||
if (module.hot) {
|
||||
views.delete(this);
|
||||
}
|
||||
}
|
||||
|
||||
renderChunksForExtent(extent) {
|
||||
this.layersView.renderChunksForExtent(extent);
|
||||
this.tilesViews.forEach((tilesView) => {
|
||||
tilesView.renderChunksForExtent(extent);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (module.hot) {
|
||||
module.hot.accept('./tiles-view', () => {
|
||||
const it = views.values();
|
||||
for (let value = it.next(); value.done !== true; value = it.next()) {
|
||||
const {value: view} = value;
|
||||
const {lastExtent} = view;
|
||||
view.removeChild(view.entityListView);
|
||||
view.removeChild(view.tilesView);
|
||||
view.tilesView = new TilesView(view.room.tiles, view.renderer);
|
||||
view.renderChunksForExtent(lastExtent);
|
||||
view.addChild(view.tilesView);
|
||||
view.addChild(view.entityListView);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -15,19 +15,18 @@ export default class TilesView extends Container {
|
|||
|
||||
static CHUNK_SIZE = [8, 8];
|
||||
|
||||
constructor(tiles, tileset, renderer) {
|
||||
constructor(tiles, renderer) {
|
||||
super();
|
||||
this.wrapper = new Container();
|
||||
this.addChild(this.wrapper);
|
||||
this.tiles = tiles;
|
||||
this.tiles.on('update', this.onTilesUpdate, this);
|
||||
this.tileset = tileset;
|
||||
this.renderer = renderer;
|
||||
this.rendered = [];
|
||||
}
|
||||
|
||||
chunksForExtent([x, y, w, h]) {
|
||||
const [tw, th] = this.tileset.tileSize;
|
||||
const [tw, th] = this.tiles.tileSize;
|
||||
/* eslint-disable no-param-reassign */
|
||||
x /= tw;
|
||||
w /= tw;
|
||||
|
@ -38,11 +37,11 @@ export default class TilesView extends Container {
|
|||
}
|
||||
|
||||
chunksForUnitExtent([x, y, w, h]) {
|
||||
const [fw, fh] = this.tiles.size;
|
||||
/* eslint-disable no-param-reassign */
|
||||
const [fw, fh] = this.tiles.area;
|
||||
if (x >= fw || y >= fh) {
|
||||
return [];
|
||||
}
|
||||
/* eslint-disable no-param-reassign */
|
||||
if (x < 0) {
|
||||
w += x;
|
||||
x = 0;
|
||||
|
@ -109,7 +108,7 @@ export default class TilesView extends Container {
|
|||
if (cux < 0 || cuy < 0) {
|
||||
return;
|
||||
}
|
||||
const [fw, fh] = this.tiles.size;
|
||||
const [fw, fh] = this.tiles.area;
|
||||
const [cw, ch] = this.constructor.CHUNK_SIZE;
|
||||
const [cx, cy] = Vector.mul([cux, cuy], [cw, ch]);
|
||||
if (cx >= fw || cy >= fh) {
|
||||
|
@ -119,7 +118,7 @@ export default class TilesView extends Container {
|
|||
cx + cw > fw ? (cx + cw) - fw : cw,
|
||||
cy + ch > fh ? (cy + ch) - fh : ch,
|
||||
];
|
||||
const [tw, th] = this.tileset.tileSize;
|
||||
const [tw, th] = this.tiles.tileSize;
|
||||
const slice = this.tiles.slice([cx - 1, cy - 1, sw + 2, sh + 2]);
|
||||
const container = new Container();
|
||||
const mask = this.renderMask(slice, [sw + 2, sh + 2]);
|
||||
|
@ -132,7 +131,7 @@ export default class TilesView extends Container {
|
|||
for (let i = 0; i < slice.length; ++i) {
|
||||
const index = slice[i];
|
||||
if (index > 0) {
|
||||
const subimage = this.tileset.subimage(index);
|
||||
const subimage = this.tiles.subimage(index);
|
||||
if (subimage) {
|
||||
const sprite = new Sprite(subimage);
|
||||
sprite.anchor = [0, 0];
|
||||
|
@ -186,8 +185,7 @@ export default class TilesView extends Container {
|
|||
if (0 === hulls.length) {
|
||||
return undefined;
|
||||
}
|
||||
const {tileSize} = this.tileset;
|
||||
const canvasSize = Vector.mul(size, tileSize);
|
||||
const canvasSize = Vector.mul(size, this.tiles.tileSize);
|
||||
const canvas = window.document.createElement('canvas');
|
||||
[canvas.width, canvas.height] = canvasSize;
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
|
|
@ -27,14 +27,6 @@ export default () => class Followed extends Trait {
|
|||
};
|
||||
}
|
||||
|
||||
destroy() {
|
||||
super.destroy();
|
||||
const {room} = this.entity;
|
||||
if (room) {
|
||||
room.off('sizeChanged', this.onRoomSizeChanged);
|
||||
}
|
||||
}
|
||||
|
||||
get camera() {
|
||||
return this.#camera;
|
||||
}
|
||||
|
@ -50,16 +42,6 @@ export default () => class Followed extends Trait {
|
|||
|
||||
addedToRoom: () => {
|
||||
this.onRoomSizeChanged();
|
||||
this.entity.room.on('sizeChanged', this.onRoomSizeChanged, this);
|
||||
},
|
||||
|
||||
removedFromRoom: (room) => {
|
||||
room.off('sizeChanged', this.onRoomSizeChanged);
|
||||
},
|
||||
|
||||
traitAdded: () => {
|
||||
this.onRoomSizeChanged();
|
||||
this.updatePosition();
|
||||
},
|
||||
|
||||
};
|
||||
|
|
|
@ -1,105 +0,0 @@
|
|||
import {Vector} from '@avocado/math';
|
||||
import {Trait} from '@avocado/traits';
|
||||
|
||||
export default () => class Layered extends Trait {
|
||||
|
||||
#tile = [0, 0];
|
||||
|
||||
#tileOffset = [0, 0];
|
||||
|
||||
static behaviorTypes() {
|
||||
return {
|
||||
layer: {
|
||||
type: 'layer',
|
||||
label: 'Layer',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
static dependencies() {
|
||||
return [
|
||||
'Positioned',
|
||||
];
|
||||
}
|
||||
|
||||
destroy() {
|
||||
super.destroy();
|
||||
this.detachFromLayer(this.entity.layer);
|
||||
}
|
||||
|
||||
detachFromLayer(layer) {
|
||||
layer?.off('tilesetChanged', this.onLayerTilesetChanged);
|
||||
}
|
||||
|
||||
hotJson() {
|
||||
return {
|
||||
tile: this.#tile,
|
||||
tileOffset: this.#tileOffset,
|
||||
};
|
||||
}
|
||||
|
||||
listeners() {
|
||||
return {
|
||||
|
||||
addedToLayer: () => {
|
||||
this.entity.layer.on('tilesetChanged', this.onLayerTilesetChanged, this);
|
||||
this.setCurrentTileFromLayer();
|
||||
},
|
||||
|
||||
positionChanged: () => {
|
||||
this.setCurrentTileFromLayer();
|
||||
},
|
||||
|
||||
removedFromLayer: (layer) => {
|
||||
this.detachFromLayer(layer);
|
||||
},
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
async load(json) {
|
||||
await super.load(json);
|
||||
this.entity.layer = null;
|
||||
this.setCurrentTileFromLayer();
|
||||
}
|
||||
|
||||
onLayerTilesetChanged() {
|
||||
this.setCurrentTileFromLayer();
|
||||
}
|
||||
|
||||
setCurrentTileFromLayer() {
|
||||
const {layer} = this.entity;
|
||||
if (!layer) {
|
||||
return;
|
||||
}
|
||||
const {tileset} = layer;
|
||||
if (!tileset) {
|
||||
return;
|
||||
}
|
||||
const oldTile = Vector.copy(this.#tile);
|
||||
this.#tile = Vector.div(
|
||||
this.entity.position,
|
||||
tileset.tileSize,
|
||||
);
|
||||
if (!Vector.equals(oldTile, this.#tile)) {
|
||||
this.entity.emit('tileChanged', oldTile, this.#tile);
|
||||
}
|
||||
const oldTileOffset = Vector.copy(this.#tileOffset);
|
||||
this.#tileOffset = Vector.mod(
|
||||
this.entity.position,
|
||||
tileset.tileSize,
|
||||
);
|
||||
if (!Vector.equals(oldTileOffset, this.#tileOffset)) {
|
||||
this.entity.emit('tileOffsetChanged', oldTileOffset, this.#tileOffset);
|
||||
}
|
||||
}
|
||||
|
||||
get tile() {
|
||||
return this.#tile;
|
||||
}
|
||||
|
||||
get tileOffset() {
|
||||
return this.#tileOffset;
|
||||
}
|
||||
|
||||
};
|
|
@ -1,32 +0,0 @@
|
|||
import {Image} from '@avocado/graphics';
|
||||
import {Latus} from '@latus/core';
|
||||
import {expect} from 'chai';
|
||||
|
||||
Image.root = 'test/fixtures';
|
||||
|
||||
describe('Tileset', () => {
|
||||
let latus;
|
||||
beforeEach(async () => {
|
||||
latus = Latus.mock({
|
||||
'@avocado/resource': require('@avocado/resource'),
|
||||
'@avocado/topdown': require('../src'),
|
||||
});
|
||||
await Promise.all(latus.invokeFlat('@latus/core/starting'));
|
||||
const {Tileset} = latus.get('%resources');
|
||||
Tileset.root = 'test/fixtures';
|
||||
});
|
||||
it("has sane defaults", async () => {
|
||||
const {Tileset} = latus.get('%resources');
|
||||
const tileset = new Tileset();
|
||||
expect(tileset.tileSize).to.deep.equal([0, 0]);
|
||||
expect(tileset.image).to.equal(undefined);
|
||||
});
|
||||
it("can load", async () => {
|
||||
const {Tileset} = latus.get('%resources');
|
||||
const tileset = await Tileset.load({extends: '/test.tileset.json'});
|
||||
expect(tileset.tileSize).to.deep.equal([4, 4]);
|
||||
expect(tileset.image.size).to.deep.equal([16, 16]);
|
||||
expect(tileset.subimages.length).to.equal(16);
|
||||
expect(tileset.subimage(0).size).to.deep.equal([4, 4]);
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue
Block a user