refactor: nix layers
This commit is contained in:
parent
6dae625953
commit
2a8ae10ec7
|
@ -28,7 +28,10 @@ export default (latus) => {
|
||||||
if ('create' === s13nType) {
|
if ('create' === s13nType) {
|
||||||
const {Entity} = latus.get('%resources');
|
const {Entity} = latus.get('%resources');
|
||||||
const {id} = packet.data.synchronized;
|
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 = []) {
|
async load(json = []) {
|
||||||
await super.load(json);
|
await super.load(json);
|
||||||
|
if (0 === json.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const {Entity} = latus.get('%resources');
|
const {Entity} = latus.get('%resources');
|
||||||
const entityInstances = await Promise.all(json.map((entity) => Entity.load(entity)));
|
const entityInstances = await Promise.all(json.map((entity) => Entity.load(entity)));
|
||||||
for (let i = 0; i < entityInstances.length; i++) {
|
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 {Vector} from '@avocado/math';
|
||||||
import {Class, compose} from '@latus/core';
|
import {Class, compose} from '@latus/core';
|
||||||
|
|
||||||
import {Image} from './resources/image';
|
import Image from './image';
|
||||||
|
|
||||||
const decorate = compose(
|
const decorate = compose(
|
||||||
Vector.Mixin('size', 'width', 'height', {
|
Vector.Mixin('size', 'width', 'height', {
|
||||||
|
|
|
@ -40,6 +40,12 @@ export default class Container extends Renderable {
|
||||||
this.container.addChild(child.internal);
|
this.container.addChild(child.internal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addChildren(children) {
|
||||||
|
for (let i = 0; i < children.length; i++) {
|
||||||
|
this.addChild(children[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
addFilter(id, type, options = {}) {
|
addFilter(id, type, options = {}) {
|
||||||
if (this.container.isFake) {
|
if (this.container.isFake) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -15,7 +15,7 @@ const cache = 'production' === process.env.NODE_ENV
|
||||||
set: () => {},
|
set: () => {},
|
||||||
};
|
};
|
||||||
|
|
||||||
export class Image extends Resource {
|
export default class Image extends Resource {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
@ -110,5 +110,3 @@ export class Image extends Resource {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default () => Image;
|
|
|
@ -2,11 +2,12 @@ import {gatherWithLatus} from '@latus/core';
|
||||||
|
|
||||||
import './init';
|
import './init';
|
||||||
|
|
||||||
|
export {default as Atlas} from './atlas';
|
||||||
export {default as Canvas} from './canvas';
|
export {default as Canvas} from './canvas';
|
||||||
export {default as Color} from './color';
|
export {default as Color} from './color';
|
||||||
export {default as Stage} from './components/stage';
|
export {default as Stage} from './components/stage';
|
||||||
export {default as Container} from './container';
|
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 Primitives} from './primitives';
|
||||||
export {default as Renderable} from './renderable';
|
export {default as Renderable} from './renderable';
|
||||||
export {default as Renderer} from './renderer';
|
export {default as Renderer} from './renderer';
|
||||||
|
@ -15,9 +16,6 @@ export {default as Text} from './text';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
hooks: {
|
hooks: {
|
||||||
'@avocado/resource/resources': gatherWithLatus(
|
|
||||||
require.context('./resources', false, /\.js$/),
|
|
||||||
),
|
|
||||||
'@avocado/traits/traits': gatherWithLatus(
|
'@avocado/traits/traits': gatherWithLatus(
|
||||||
require.context('./traits', false, /\.js$/),
|
require.context('./traits', false, /\.js$/),
|
||||||
),
|
),
|
||||||
|
|
|
@ -3,6 +3,7 @@ import {StateProperty, Trait} from '@avocado/traits';
|
||||||
import {Rectangle, Vector} from '@avocado/math';
|
import {Rectangle, Vector} from '@avocado/math';
|
||||||
import {compose} from '@latus/core';
|
import {compose} from '@latus/core';
|
||||||
|
|
||||||
|
import Image from '../image';
|
||||||
import Sprite from '../sprite';
|
import Sprite from '../sprite';
|
||||||
|
|
||||||
const decorate = compose(
|
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 = '';
|
#currentImage = '';
|
||||||
|
|
||||||
|
@ -161,7 +162,6 @@ export default (latus) => class Pictured extends decorate(Trait) {
|
||||||
.filter(([, {uri}]) => !!uri),
|
.filter(([, {uri}]) => !!uri),
|
||||||
);
|
);
|
||||||
this.#currentImage = this.state.currentImage;
|
this.#currentImage = this.state.currentImage;
|
||||||
const {Image} = latus.get('%resources');
|
|
||||||
this.#images = await mapValuesAsync(images, ({uri}) => Image.load(uri));
|
this.#images = await mapValuesAsync(images, ({uri}) => Image.load(uri));
|
||||||
this.#sprites = await mapValuesAsync(this.#images, async (image) => new Sprite(image));
|
this.#sprites = await mapValuesAsync(this.#images, async (image) => new Sprite(image));
|
||||||
Object.keys(this.#sprites).forEach((key) => {
|
Object.keys(this.#sprites).forEach((key) => {
|
||||||
|
|
|
@ -58,9 +58,7 @@ export default () => class Rastered extends Trait {
|
||||||
},
|
},
|
||||||
|
|
||||||
onYChanged: () => {
|
onYChanged: () => {
|
||||||
if (this.#usingAutoZIndex) {
|
this.onYChanged();
|
||||||
this.#container.zIndex = this.entity.y;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
opacityChanged: () => {
|
opacityChanged: () => {
|
||||||
|
@ -77,16 +75,6 @@ export default () => class Rastered extends Trait {
|
||||||
this.#container.rotation = this.entity.rotation;
|
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: () => {
|
visibleScaleChanged: () => {
|
||||||
if (this.#container) {
|
if (this.#container) {
|
||||||
this.#container.scale = this.entity.visibleScale;
|
this.#container.scale = this.entity.visibleScale;
|
||||||
|
@ -103,6 +91,13 @@ export default () => class Rastered extends Trait {
|
||||||
async load(json) {
|
async load(json) {
|
||||||
await super.load(json);
|
await super.load(json);
|
||||||
if ('client' === process.env.SIDE) {
|
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;
|
const {filter} = this.params;
|
||||||
if (filter) {
|
if (filter) {
|
||||||
this.#container.setFilter(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) {
|
onZIndexChanged(zIndex) {
|
||||||
|
this.#usingAutoZIndex = AUTO_ZINDEX === zIndex;
|
||||||
if (!this.#container) {
|
if (!this.#container) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.#usingAutoZIndex = AUTO_ZINDEX === zIndex;
|
|
||||||
if (!this.#usingAutoZIndex) {
|
if (!this.#usingAutoZIndex) {
|
||||||
this.#container.zIndex = zIndex;
|
this.#container.zIndex = zIndex;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.#container.zIndex = this.entity.y;
|
this.onYChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
renderTick() {
|
renderTick() {
|
||||||
this.synchronizePosition();
|
this.synchronizePosition();
|
||||||
|
this.synchronizeZIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronizePosition() {
|
synchronizePosition() {
|
||||||
|
@ -172,7 +174,10 @@ export default () => class Rastered extends Trait {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.#container.position = this.entity.position;
|
this.#container.position = this.entity.position;
|
||||||
if (this.#usingAutoZIndex) {
|
}
|
||||||
|
|
||||||
|
synchronizeZIndex() {
|
||||||
|
if (this.#container && this.#usingAutoZIndex) {
|
||||||
this.#container.zIndex = this.entity.y;
|
this.#container.zIndex = this.entity.y;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import {assert, expect} from 'chai';
|
import {assert, expect} from 'chai';
|
||||||
import {validate} from 'uuid';
|
import {validate} from 'uuid';
|
||||||
|
|
||||||
import {Image} from '../src/resources/image';
|
import Image from '../src/image';
|
||||||
|
|
||||||
Image.root = 'test/fixtures';
|
Image.root = 'test/fixtures';
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,24 @@
|
||||||
export default (EntityList) => class PhysicsEntityList extends EntityList {
|
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) {
|
set world(world) {
|
||||||
const entities = Object.values(this.entities);
|
const entities = Object.values(this.entities);
|
||||||
for (let i = 0; i < entities.length; i++) {
|
for (let i = 0; i < entities.length; i++) {
|
||||||
|
@ -8,6 +27,7 @@ export default (EntityList) => class PhysicsEntityList extends EntityList {
|
||||||
entity.world = world;
|
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();
|
const world = new World();
|
||||||
world.stepTime = 1 / 60;
|
world.stepTime = 1 / 60;
|
||||||
this.world = world;
|
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)) {
|
if (Vector.isZero(this.size)) {
|
||||||
return;
|
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 Room} from './components/room';
|
||||||
|
|
||||||
export {default as Camera} from './camera';
|
export {default as Camera} from './camera';
|
||||||
export {default as LayerView} from './layer-view';
|
|
||||||
export {default as RoomView} from './room-view';
|
export {default as RoomView} from './room-view';
|
||||||
|
|
||||||
export default {
|
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) {
|
return class Room extends decorate(JsonResource) {
|
||||||
|
|
||||||
|
entityList;
|
||||||
|
|
||||||
|
tiles = [];
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this._s13nId = s13nId++;
|
this._s13nId = s13nId++;
|
||||||
const {Layers} = latus.get('%resources');
|
const {EntityList} = latus.get('%resources');
|
||||||
this.setLayers(new Layers());
|
this.entityList = new EntityList();
|
||||||
|
this.entityList.on('entityAdded', this.onEntityAdded, this);
|
||||||
|
this.entityList.on('entityRemoved', this.onEntityRemoved, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
addEntityToLayer(entity, layerIndex = 0) {
|
addEntity(entity) {
|
||||||
this.layers.addEntityToLayer(entity, layerIndex);
|
this.entityList.addEntity(entity);
|
||||||
}
|
|
||||||
|
|
||||||
destroy() {
|
|
||||||
super.destroy();
|
|
||||||
this.layers.destroy();
|
|
||||||
this.layers.off('entityAdded', this.onEntityAddedToRoom);
|
|
||||||
this.layers.off('entityRemoved', this.onEntityRemovedFromRoom);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get entities() {
|
get entities() {
|
||||||
return this.layers.entities;
|
return this.entityList.entities;
|
||||||
}
|
}
|
||||||
|
|
||||||
findEntity(uuid) {
|
findEntity(uuid) {
|
||||||
return this.layers.findEntity(uuid);
|
return this.entityList.findEntity(uuid);
|
||||||
}
|
|
||||||
|
|
||||||
layer(index) {
|
|
||||||
return this.layers.layer(index);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async load(json = {}) {
|
async load(json = {}) {
|
||||||
await super.load(json);
|
await super.load(json);
|
||||||
const {layers, size} = json;
|
const {Tiles} = latus.get('%resources');
|
||||||
this.size = size || [0, 0];
|
const {
|
||||||
const {Layers} = latus.get('%resources');
|
entities,
|
||||||
this.setLayers(
|
size = [0, 0],
|
||||||
layers
|
tiles,
|
||||||
? await Layers.load(layers)
|
} = json;
|
||||||
: new Layers(),
|
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
|
// eslint-disable-next-line no-param-reassign
|
||||||
entity.room = this;
|
entity.room = this;
|
||||||
entity.emit('addedToRoom');
|
entity.emit('addedToRoom');
|
||||||
this.emit('entityAdded', entity);
|
this.emit('entityAdded', entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
onEntityRemovedFromRoom(entity) {
|
onEntityRemoved(entity) {
|
||||||
// eslint-disable-next-line no-param-reassign
|
// eslint-disable-next-line no-param-reassign
|
||||||
entity.room = null;
|
entity.room = null;
|
||||||
entity.emit('removedFromRoom', this);
|
entity.emit('removedFromRoom', this);
|
||||||
this.emit('entityRemoved', entity);
|
this.emit('entityRemoved', entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
removeEntityFromLayer(entity, layerIndex) {
|
removeEntity(entity) {
|
||||||
this.layers.removeEntityFromLayer(entity, layerIndex);
|
this.entityList.removeEntity(entity);
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get s13nId() {
|
get s13nId() {
|
||||||
|
@ -100,20 +84,25 @@ export default (latus) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
tick(elapsed) {
|
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() {
|
toJSON() {
|
||||||
return {
|
return {
|
||||||
layer: this.layers.toJSON(),
|
entities: this.entityList.toJSON(),
|
||||||
size: this.size,
|
size: this.size,
|
||||||
|
tiles: this.tiles.map((tiles) => tiles.toJSON()),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
toNetwork(informed) {
|
toNetwork(informed) {
|
||||||
return {
|
return {
|
||||||
layers: this.layers.toNetwork(informed),
|
entities: this.entityList.toNetwork(informed),
|
||||||
size: this.size,
|
size: this.size,
|
||||||
|
tiles: this.tiles.map((tiles) => tiles.toNetwork(informed)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,38 +1,49 @@
|
||||||
|
import {Atlas} from '@avocado/graphics';
|
||||||
import {
|
import {
|
||||||
floodwalk2D,
|
floodwalk2D,
|
||||||
Rectangle,
|
Rectangle,
|
||||||
Vector,
|
Vector,
|
||||||
Vertice,
|
Vertice,
|
||||||
} from '@avocado/math';
|
} from '@avocado/math';
|
||||||
|
import {JsonResource} from '@avocado/resource';
|
||||||
|
import {Synchronized} from '@avocado/s13n';
|
||||||
import {
|
import {
|
||||||
Class,
|
|
||||||
compose,
|
compose,
|
||||||
deflate,
|
deflate,
|
||||||
EventEmitter,
|
EventEmitter,
|
||||||
inflate,
|
inflate,
|
||||||
} from '@latus/core';
|
} from '@latus/core';
|
||||||
import {Synchronized} from '@avocado/s13n';
|
|
||||||
|
|
||||||
// TODO: rows, with nulls
|
// TODO: rows, with nulls
|
||||||
|
|
||||||
export default (latus) => {
|
export default (latus) => {
|
||||||
const decorate = compose(
|
const decorate = compose(
|
||||||
EventEmitter,
|
EventEmitter,
|
||||||
Vector.Mixin('size', 'width', 'height', {
|
Vector.Mixin('area', 'width', 'height', {
|
||||||
default: [0, 0],
|
default: [0, 0],
|
||||||
}),
|
}),
|
||||||
Synchronized(latus),
|
Synchronized(latus),
|
||||||
);
|
);
|
||||||
return class Tiles extends decorate(Class) {
|
return class Tiles extends decorate(JsonResource) {
|
||||||
|
|
||||||
#data = [];
|
#atlas = new Atlas();
|
||||||
|
|
||||||
|
#data = new Uint16Array();
|
||||||
|
|
||||||
#hasUpdates = false;
|
#hasUpdates = false;
|
||||||
|
|
||||||
#packets = [];
|
#packets = [];
|
||||||
|
|
||||||
|
#s13nId = 0;
|
||||||
|
|
||||||
|
#tileImageUri;
|
||||||
|
|
||||||
|
#tileSize = [0, 0];
|
||||||
|
|
||||||
#updates = [];
|
#updates = [];
|
||||||
|
|
||||||
|
#zIndex = 0;
|
||||||
|
|
||||||
acceptPacket(packet) {
|
acceptPacket(packet) {
|
||||||
if ('TilesUpdate' === packet.constructor.type) {
|
if ('TilesUpdate' === packet.constructor.type) {
|
||||||
const {position: [x, y], size: [w, h], tiles} = packet.data;
|
const {position: [x, y], size: [w, h], tiles} = packet.data;
|
||||||
|
@ -46,6 +57,10 @@ export default (latus) => {
|
||||||
this.#updates = [];
|
this.#updates = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
this.#atlas.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
static indexHulls(indices, data, size) {
|
static indexHulls(indices, data, size) {
|
||||||
const hulls = [];
|
const hulls = [];
|
||||||
const seen = [];
|
const seen = [];
|
||||||
|
@ -108,18 +123,43 @@ export default (latus) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
indexHulls(indices) {
|
indexHulls(indices) {
|
||||||
return this.constructor.indexHulls(indices, this.#data, this.size);
|
return this.constructor.indexHulls(indices, this.#data, this.area);
|
||||||
}
|
}
|
||||||
|
|
||||||
async load(json) {
|
async load(json) {
|
||||||
const {data, size} = json;
|
const {
|
||||||
if (size) {
|
area,
|
||||||
this.size = size;
|
data,
|
||||||
|
s13nId,
|
||||||
|
tileImageUri,
|
||||||
|
tileSize,
|
||||||
|
zIndex,
|
||||||
|
} = json;
|
||||||
|
if (area) {
|
||||||
|
this.area = area;
|
||||||
}
|
}
|
||||||
if (data) {
|
if (data) {
|
||||||
const {buffer, byteOffset, length} = inflate(Buffer.from(data, 'base64'));
|
const {buffer, byteOffset, length} = inflate(Buffer.from(data, 'base64'));
|
||||||
this.#data = new Uint16Array(buffer, byteOffset, length / 2);
|
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() {
|
packetsFor() {
|
||||||
|
@ -144,11 +184,15 @@ export default (latus) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
get rectangle() {
|
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) {
|
setTileAt([x, y], tile) {
|
||||||
const [w, h] = this.size;
|
const [w, h] = this.area;
|
||||||
const index = y * w + x;
|
const index = y * w + x;
|
||||||
if (x < 0 || x >= w || y < 0 || y >= h || this.#data[index] === tile) {
|
if (x < 0 || x >= w || y < 0 || y >= h || this.#data[index] === tile) {
|
||||||
return;
|
return;
|
||||||
|
@ -165,7 +209,7 @@ export default (latus) => {
|
||||||
if (w <= 0 || h <= 0) {
|
if (w <= 0 || h <= 0) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
const [fw, fh] = this.size;
|
const [fw, fh] = this.area;
|
||||||
const n = fw - w;
|
const n = fw - w;
|
||||||
const slice = new Array(w * h);
|
const slice = new Array(w * h);
|
||||||
let i = y * fw + x;
|
let i = y * fw + x;
|
||||||
|
@ -189,7 +233,7 @@ export default (latus) => {
|
||||||
if (0 === w || 0 === h || 0 === tiles.length) {
|
if (0 === w || 0 === h || 0 === tiles.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const [fw, fh] = this.size;
|
const [fw, fh] = this.area;
|
||||||
if (x < 0 || x >= fw || y < 0 || y >= fh) {
|
if (x < 0 || x >= fw || y < 0 || y >= fh) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -219,6 +263,10 @@ export default (latus) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
subimage(index) {
|
||||||
|
return this.#atlas.subimage(index);
|
||||||
|
}
|
||||||
|
|
||||||
tick() {
|
tick() {
|
||||||
if (this.#hasUpdates) {
|
if (this.#hasUpdates) {
|
||||||
this.emit('update');
|
this.emit('update');
|
||||||
|
@ -227,20 +275,33 @@ export default (latus) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
tileAt([x, y]) {
|
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];
|
return x < 0 || x >= w || y < 0 || y >= h ? undefined : this.#data[y * w + x];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get tileSize() {
|
||||||
|
return this.#tileSize;
|
||||||
|
}
|
||||||
|
|
||||||
toNetwork() {
|
toNetwork() {
|
||||||
return this.toJSON();
|
return {
|
||||||
|
...this.toJSON(),
|
||||||
|
s13nId: this.#s13nId,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
toJSON() {
|
toJSON() {
|
||||||
return {
|
return {
|
||||||
size: this.size,
|
area: this.area,
|
||||||
data: deflate(this.#data).toString('base64'),
|
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 {Container} from '@avocado/graphics';
|
||||||
|
|
||||||
import LayersView from './layers-view';
|
import TilesView from './tiles-view';
|
||||||
|
|
||||||
|
const views = new Set();
|
||||||
|
|
||||||
export default class RoomView extends Container {
|
export default class RoomView extends Container {
|
||||||
|
|
||||||
|
layers = [];
|
||||||
|
|
||||||
constructor(room, renderer) {
|
constructor(room, renderer) {
|
||||||
super();
|
super();
|
||||||
this.room = room;
|
this.lastExtent = [0, 0, 0, 0];
|
||||||
this.layersView = new LayersView(room.layers, renderer);
|
this.renderer = renderer;
|
||||||
this.addChild(this.layersView);
|
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();
|
this.sort();
|
||||||
|
if (module.hot) {
|
||||||
|
views.add(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
super.destroy();
|
||||||
|
if (module.hot) {
|
||||||
|
views.delete(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
renderChunksForExtent(extent) {
|
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];
|
static CHUNK_SIZE = [8, 8];
|
||||||
|
|
||||||
constructor(tiles, tileset, renderer) {
|
constructor(tiles, renderer) {
|
||||||
super();
|
super();
|
||||||
this.wrapper = new Container();
|
this.wrapper = new Container();
|
||||||
this.addChild(this.wrapper);
|
this.addChild(this.wrapper);
|
||||||
this.tiles = tiles;
|
this.tiles = tiles;
|
||||||
this.tiles.on('update', this.onTilesUpdate, this);
|
this.tiles.on('update', this.onTilesUpdate, this);
|
||||||
this.tileset = tileset;
|
|
||||||
this.renderer = renderer;
|
this.renderer = renderer;
|
||||||
this.rendered = [];
|
this.rendered = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
chunksForExtent([x, y, w, h]) {
|
chunksForExtent([x, y, w, h]) {
|
||||||
const [tw, th] = this.tileset.tileSize;
|
const [tw, th] = this.tiles.tileSize;
|
||||||
/* eslint-disable no-param-reassign */
|
/* eslint-disable no-param-reassign */
|
||||||
x /= tw;
|
x /= tw;
|
||||||
w /= tw;
|
w /= tw;
|
||||||
|
@ -38,11 +37,11 @@ export default class TilesView extends Container {
|
||||||
}
|
}
|
||||||
|
|
||||||
chunksForUnitExtent([x, y, w, h]) {
|
chunksForUnitExtent([x, y, w, h]) {
|
||||||
const [fw, fh] = this.tiles.size;
|
const [fw, fh] = this.tiles.area;
|
||||||
/* eslint-disable no-param-reassign */
|
|
||||||
if (x >= fw || y >= fh) {
|
if (x >= fw || y >= fh) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
/* eslint-disable no-param-reassign */
|
||||||
if (x < 0) {
|
if (x < 0) {
|
||||||
w += x;
|
w += x;
|
||||||
x = 0;
|
x = 0;
|
||||||
|
@ -109,7 +108,7 @@ export default class TilesView extends Container {
|
||||||
if (cux < 0 || cuy < 0) {
|
if (cux < 0 || cuy < 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const [fw, fh] = this.tiles.size;
|
const [fw, fh] = this.tiles.area;
|
||||||
const [cw, ch] = this.constructor.CHUNK_SIZE;
|
const [cw, ch] = this.constructor.CHUNK_SIZE;
|
||||||
const [cx, cy] = Vector.mul([cux, cuy], [cw, ch]);
|
const [cx, cy] = Vector.mul([cux, cuy], [cw, ch]);
|
||||||
if (cx >= fw || cy >= fh) {
|
if (cx >= fw || cy >= fh) {
|
||||||
|
@ -119,7 +118,7 @@ export default class TilesView extends Container {
|
||||||
cx + cw > fw ? (cx + cw) - fw : cw,
|
cx + cw > fw ? (cx + cw) - fw : cw,
|
||||||
cy + ch > fh ? (cy + ch) - fh : ch,
|
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 slice = this.tiles.slice([cx - 1, cy - 1, sw + 2, sh + 2]);
|
||||||
const container = new Container();
|
const container = new Container();
|
||||||
const mask = this.renderMask(slice, [sw + 2, sh + 2]);
|
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) {
|
for (let i = 0; i < slice.length; ++i) {
|
||||||
const index = slice[i];
|
const index = slice[i];
|
||||||
if (index > 0) {
|
if (index > 0) {
|
||||||
const subimage = this.tileset.subimage(index);
|
const subimage = this.tiles.subimage(index);
|
||||||
if (subimage) {
|
if (subimage) {
|
||||||
const sprite = new Sprite(subimage);
|
const sprite = new Sprite(subimage);
|
||||||
sprite.anchor = [0, 0];
|
sprite.anchor = [0, 0];
|
||||||
|
@ -186,8 +185,7 @@ export default class TilesView extends Container {
|
||||||
if (0 === hulls.length) {
|
if (0 === hulls.length) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
const {tileSize} = this.tileset;
|
const canvasSize = Vector.mul(size, this.tiles.tileSize);
|
||||||
const canvasSize = Vector.mul(size, tileSize);
|
|
||||||
const canvas = window.document.createElement('canvas');
|
const canvas = window.document.createElement('canvas');
|
||||||
[canvas.width, canvas.height] = canvasSize;
|
[canvas.width, canvas.height] = canvasSize;
|
||||||
const ctx = canvas.getContext('2d');
|
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() {
|
get camera() {
|
||||||
return this.#camera;
|
return this.#camera;
|
||||||
}
|
}
|
||||||
|
@ -50,16 +42,6 @@ export default () => class Followed extends Trait {
|
||||||
|
|
||||||
addedToRoom: () => {
|
addedToRoom: () => {
|
||||||
this.onRoomSizeChanged();
|
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