This commit is contained in:
cha0s 2021-01-02 22:01:57 -06:00
parent 156e7c7f47
commit ebdf85c8a1
95 changed files with 1618 additions and 894 deletions

View File

@ -3,3 +3,4 @@
!/.*
!/webpack.config.js
!src/**/*.js
!/test/**/*.js

View File

@ -0,0 +1,9 @@
import {expect} from 'chai';
const {name} = require('../package.json');
describe(name, () => {
it('exists', () => {
expect(true).to.equal(true);
})
});

View File

@ -3,3 +3,4 @@
!/.*
!/webpack.config.js
!src/**/*.js
!/test/**/*.js

View File

@ -1,4 +1,4 @@
const globals = (latus) => latus.invokeReduced('@avocado/behavior/globals');
const globals = (latus) => latus.invokeReduce('@avocado/behavior/globals');
export default class Context {

View File

@ -2,8 +2,9 @@ import compilers from './compilers';
import globals from './globals';
import traits from './traits';
export {default as Actions} from './actions';
export * from './builders';
export {default as compile} from './compilers/compile';
export {
default as Context,
} from './context';

View File

@ -0,0 +1,14 @@
import {expect} from 'chai';
import {
buildCondition,
buildInvoke,
buildExpression,
buildValue,
} from '../src/builders';
describe('@avocado/behavior', () => {
describe('builders', () => {
describe('.buildValue', () => {
});
})
});

View File

@ -0,0 +1,9 @@
import {expect} from 'chai';
const {name} = require('../package.json');
describe(name, () => {
it('exists', () => {
expect(true).to.equal(true);
})
});

View File

@ -3,3 +3,4 @@
!/.*
!/webpack.config.js
!src/**/*.js
!/test/**/*.js

View File

@ -0,0 +1,9 @@
import {expect} from 'chai';
const {name} = require('../package.json');
describe(name, () => {
it('exists', () => {
expect(true).to.equal(true);
})
});

View File

@ -0,0 +1,98 @@
import {expect} from 'chai';
import Property from '../src/property';
describe('@avocado/core', () => {
describe('Property', () => {
it("dies if meta isn't an object", () => {
expect(() => {
Property('property', 420)(class {});
}).to.throw();
});
it('dies if duplicate properties are defined', () => {
expect(() => {
Property('property')(class {property() {}});
}).to.throw();
});
it('accepts initializer', () => {
let hasInitialized = false;
class Class extends Property('property', {
initialize() {
hasInitialized = this instanceof Class;
},
})(class {}) {};
const object = new Class();
expect(hasInitialized).to.equal(true);
});
it('accepts defaults', () => {
class Class extends Property('property', {
default: 420,
})(class {}) {};
const object = new Class();
expect(object.property).to.equal(420);
});
it('accepts getter', () => {
class Class extends Property('property', {
default: 69,
get() {
return 420;
},
})(class {}) {};
const object = new Class();
expect(object.property).to.equal(420);
});
it('accepts setter', () => {
let s;
class Class extends Property('property', {
set(value) {
s = value;
},
})(class {}) {};
const object = new Class();
object.property = 420;
expect(s).to.equal(420);
});
it('tracks property values', () => {
let emitted = false;
class Class extends Property('property', {
track: true,
})(class {}) {
emit(type, o, v) {emitted = ('propertyChanged' === type && 420 === v)}
};
const object = new Class();
expect(emitted).to.equal(false);
object.property = 420;
expect(emitted).to.equal(true);
});
it('accepts emitter', () => {
let emitted = false;
class Class extends Property('property', {
emit: (type, o, v) => {
emitted = ('propertyChanged' === type && 420 === v);
},
track: true,
})(class {}) {};
const object = new Class();
expect(emitted).to.equal(false);
object.property = 420;
expect(emitted).to.equal(true);
});
it('accepts comparator', () => {
let emitted = false;
const emit = (type, o, v) => {
emitted = ('propertyChanged' === type);
};
class Class extends Property('property', {
default: [0, 0],
emit,
eq: (l, r) => l[0] === r[0] && l[1] === r[1],
track: true,
})(class {}) {};
const object = new Class();
expect(emitted).to.equal(false);
object.property = [0, 0];
expect(emitted).to.equal(false);
object.property = [1, 0];
expect(emitted).to.equal(true);
});
});
});

View File

@ -0,0 +1,56 @@
import {assert, expect} from 'chai';
import TickingPromise from '../src/ticking-promise';
const createTickingPromise = (duration) => new TickingPromise(
() => {},
(elapsed, resolve, reject) => {
duration -= elapsed;
if (duration <= 0) {
resolve();
}
},
);
describe('@avocado/core', () => {
describe('TickingPromise', () => {
it('can behave as a promise', async () => {
try {
await new TickingPromise((resolve, reject) => {
resolve();
});
}
catch (error) {
assert.fail('TickingPromise threw');
}
try {
await new TickingPromise((resolve, reject) => {
reject(new Error());
});
}
catch (error) {
return;
}
assert.fail("TickingPromise didn't throw");
});
it('accepts a ticker', async () => {
const promise = createTickingPromise(1);
for (let i = 0; i < 4; ++i) {
promise.tick(0.3);
}
await promise;
});
it('implements a compatible .all', async () => {
await TickingPromise.all([
new Promise((resolve, reject) => {
resolve();
}),
]);
const promise = createTickingPromise(1);
const allPromise = TickingPromise.all([promise]);
for (let i = 0; i < 4; ++i) {
allPromise.tick(0.3);
}
await allPromise;
});
});
});

View File

@ -0,0 +1,20 @@
import {expect} from 'chai';
import virtualizeStatic from '../src/virtualize-static';
const NoMethod = virtualizeStatic(['method'], class {});
const Method = class extends virtualizeStatic(['method'])(class {}) {static method() {}};
describe('@avocado/core', () => {
describe('virtualizeStatic', () => {
it("throws if static virtual methods aren't implemented", () => {
expect(() => {
new NoMethod();
}).to.throw();
});
it("doesn't throw if static virtual methods are implemented", () => {
expect(() => {
new Method();
}).to.not.throw();
});
});
});

View File

@ -0,0 +1,20 @@
import {assert, expect} from 'chai';
import virtualize from '../src/virtualize';
const NoMethod = virtualize(['method'], class {});
const Method = class extends virtualize(['method'])(class {}) {method() {}};
describe('@avocado/core', () => {
describe('virtualize', () => {
it("throws if virtual methods aren't implemented", () => {
expect(() => {
new NoMethod();
}).to.throw();
});
it("doesn't throw if virtual methods are implemented", () => {
expect(() => {
new Method();
}).to.not.throw();
});
});
});

View File

@ -3,3 +3,4 @@
!/.*
!/webpack.config.js
!src/**/*.js
!/test/**/*.js

View File

@ -22,6 +22,7 @@
"dependencies": {
"@avocado/behavior": "2.0.0",
"@avocado/core": "2.0.0",
"@avocado/graphics": "^2.0.0",
"@avocado/math": "2.0.0",
"@avocado/resource": "2.0.0",
"@avocado/s13n": "2.0.0",

View File

@ -26,7 +26,7 @@ function traitAccessorForProperty(type, property) {
}
export function defineTraitAccessors(from, to, instance) {
const type = instance.constructor.type();
const {type} = instance.constructor;
do {
// eslint-disable-next-line no-loop-func
Object.getOwnPropertyNames(from).forEach((accessorKey) => {

View File

@ -4,7 +4,8 @@ import without from 'lodash.without';
import {fastApply} from '@avocado/core';
import {normalize, Synchronized} from '@avocado/s13n';
import Resource from '@avocado/resource';
import {JsonResource} from '@avocado/resource';
import {traits} from '@avocado/traits';
import {
compose,
EventEmitter,
@ -15,7 +16,10 @@ import {
defineTraitAccessors,
enumerateTraitAccessorKeys,
} from './accessors';
import {traitFromType} from './trait';
const {
SIDE,
} = process.env;
const debug = D('@avocado:entity:traits');
@ -24,15 +28,16 @@ const decorate = compose(
Synchronized,
);
let numericUid = AVOCADO_SERVER ? 1 : 1000000000;
export default (latus) => class Entity extends decorate(Resource) {
let numericUid = 'client' !== SIDE ? 1 : 1000000000;
constructor(json, jsonext) {
export default (latus) => class Entity extends decorate(JsonResource) {
constructor({instanceUuid, traits = []} = {}) {
super();
this._fastDirtyCheck = true;
this._hooks = {};
this._hydrationPromise = undefined;
this._json = Entity.jsonWithDefaults(json);
// this._json = Entity.jsonWithDefaults(json);
this._tickingPromisesTickers = [];
this._traits = {};
this._traitsFlat = [];
@ -51,9 +56,11 @@ export default (latus) => class Entity extends decorate(Resource) {
this.room = null;
this.visibleAabb = [0, 0, 0, 0];
// Fast path for instance.
if ('undefined' !== typeof json) {
this.fromJSON(merge(json, jsonext));
}
// if ('undefined' !== typeof json) {
// this.fromJSON(merge(json, jsonext));
// }
this.instanceUuid = instanceUuid || this.numericId;
this.addTraits(traits);
}
acceptPacket(packet) {
@ -79,14 +86,15 @@ export default (latus) => class Entity extends decorate(Resource) {
});
}
addTrait(type, json = {}) {
if (this.is(type)) {
debug(`Tried to add trait "${type}" when it already exists!`);
addTrait([Trait, json]) {
// const Trait = trait.constructor;
if (this.is(Trait.type)) {
debug(`Tried to add trait "${Trait.type}" when it already exists!`);
return;
}
const {[type]: Trait} = traitFromType(latus);
// const {fromType: {[type]: Trait}} = traits(latus);
if (!Trait) {
debug(`Tried to add trait "${type}" which isn't registered!`);
debug(`Tried to add trait "${Trait.type}" which isn't registered!`);
return;
}
// Ensure dependencies.
@ -95,31 +103,33 @@ export default (latus) => class Entity extends decorate(Resource) {
const lacking = without(dependencies, ...allTypes);
if (lacking.length > 0) {
debug(
`Tried to add trait "${type}" but lack one or more dependents: "${
`Tried to add trait "${Trait.type}" but lack one or more dependents: "${
lacking.join('", "')
}"!`,
);
// return;
}
// Instantiate.
const {params, state} = json;
const instance = new Trait(this, params, state);
// eslint-disable-next-line no-param-reassign
json.entity = this;
// const instance = new Trait(json);
const trait = new Trait(json);
// Proxy properties.
defineTraitAccessors(Trait.prototype, this, instance);
defineTraitAccessors(Trait.prototype, this, trait);
// Attach listeners.
const listeners = Object.entries(instance.memoizedListeners());
const listeners = Object.entries(trait.memoizedListeners());
for (let i = 0; i < listeners.length; i++) {
const [event, listener] = listeners[i];
this.on(event, listener);
}
// Proxy methods.
const methods = Object.entries(instance.methods());
const methods = Object.entries(trait.methods());
for (let i = 0; i < methods.length; i++) {
const [key, method] = methods[i];
this[key] = method;
}
// Register hook listeners.
const hooks = Object.entries(instance.hooks());
const hooks = Object.entries(trait.hooks());
for (let i = 0; i < hooks.length; i++) {
const [key, fn] = hooks[i];
this._hooks[key] = this._hooks[key] || [];
@ -129,26 +139,29 @@ export default (latus) => class Entity extends decorate(Resource) {
});
}
// Track trait.
this._traits[type] = instance;
this._traitsFlat.push(instance);
if ('tick' in instance) {
this._traitTickers.push(instance.tick);
this._traits[Trait.type] = trait;
this._traitsFlat.push(trait);
if ('tick' in trait) {
this._traitTickers.push(trait.tick);
}
if ('renderTick' in instance) {
this._traitRenderTickers.push(instance.renderTick);
if ('renderTick' in trait) {
this._traitRenderTickers.push(trait.renderTick);
}
if ('acceptPacket' in instance) {
this._traitsAcceptingPackets.push(instance);
if ('acceptPacket' in trait) {
this._traitsAcceptingPackets.push(trait);
}
this.emit('traitAdded', type, instance);
this.emit('traitAdded', Trait.type, trait);
}
addTraits(traits) {
const entries = Object.entries(traits);
for (let i = 0; i < entries.length; i++) {
const [type, trait] = entries[i];
this.addTrait(type, trait);
for (let i = 0; i < traits.length; i++) {
this.addTrait(traits[i]);
}
// const entries = Object.entries(traits);
// for (let i = 0; i < entries.length; i++) {
// const [type, trait] = entries[i];
// this.addTrait(type, trait);
// }
}
allTraitInstances() {
@ -169,27 +182,59 @@ export default (latus) => class Entity extends decorate(Resource) {
this._fastDirtyCheck = false;
}
fromJSON(json) {
super.fromJSON(json);
if (json.instanceUuid) {
this.instanceUuid = json.instanceUuid;
}
this.addTraits(json.traits);
return this;
}
// fromJSON(json) {
// super.fromJSON(json);
// if (json.instanceUuid) {
// this.instanceUuid = json.instanceUuid;
// }
// this.addTraits(json.traits);
// return this;
// }
hydrate() {
if (!this._hydrationPromise) {
const promises = [];
for (let i = 0; i < this._traitsFlat.length; i++) {
promises.push(this._traitsFlat[i].hydrate());
}
this._hydrationPromise = Promise.all(promises);
this._hydrationPromise.then(() => {
this.tick(0);
// hydrate() {
// if (!this._hydrationPromise) {
// const promises = [];
// for (let i = 0; i < this._traitsFlat.length; i++) {
// promises.push(this._traitsFlat[i].hydrate());
// }
// this._hydrationPromise = Promise.all(promises);
// this._hydrationPromise.then(() => {
// this.tick(0);
// });
// }
// return this._hydrationPromise;
// }
static async extendJson(json) {
const extended = await super.extendJson(json);
// const {fromResourceType: {EntityList, Tileset}} = resource(latus);
if (extended.traits) {
const entries = Object.entries(extended.traits);
// extended.traits = [];
const promises = entries.map(async ([type, json]) => {
const {fromType: {[type]: Trait}} = traits(latus);
if (!Trait) {
debug(`Tried to add trait "${type}" which isn't registered!`);
return undefined;
}
return [Trait, await Trait.extendJson(json)];
});
extended.traits = (await Promise.all(promises)).filter((entry) => !!entry);
// for (let i = 0; i < entries.length; i++) {
// const [type, json] = entries[i];
// const {fromType: {[type]: Trait}} = traits(latus);
// if (!Trait) {
// debug(`Tried to add trait "${type}" which isn't registered!`);
// }
// else {
// extended.traits.push(await Trait.load(json));
// }
// }
}
return this._hydrationPromise;
// if (extended.tilesetUri) {
// extended.tileset = await Tileset.load(extended.tilesetUri);
// }
return extended;
}
invokeHook(hook, ...args) {
@ -213,21 +258,21 @@ export default (latus) => class Entity extends decorate(Resource) {
return type in this._traits;
}
static jsonWithDefaults(json) {
if ('undefined' === typeof json) {
return undefined;
}
const pristine = JSON.parse(JSON.stringify(json));
const traits = Object.entries(json.traits);
for (let i = 0; i < traits.length; i++) {
const [type, trait] = traits[i];
const {[type]: Trait} = traitFromType(latus);
if (Trait) {
pristine.traits[type] = merge(Trait.defaultJSON(), trait);
}
}
return pristine;
}
// static jsonWithDefaults(json) {
// if ('undefined' === typeof json) {
// return undefined;
// }
// const pristine = JSON.parse(JSON.stringify(json));
// const traits = Object.entries(json.traits);
// for (let i = 0; i < traits.length; i++) {
// const [type, trait] = traits[i];
// const {[type]: Trait} = traitFromType(latus);
// if (Trait) {
// pristine.traits[type] = merge(Trait.defaultJSON(), trait);
// }
// }
// return pristine;
// }
mergeDiff(json) {
if (!this.uri) {
@ -287,7 +332,7 @@ export default (latus) => class Entity extends decorate(Resource) {
const implementation = this._hooks[hook].find(({type: hookType}) => hookType === type);
this._hooks[hook].splice(this._hooks[hook].indexOf(implementation), 1);
}
const {[type]: Trait} = traitFromType(latus);
const {fromType: {[type]: Trait}} = traits(latus);
const properties = enumerateTraitAccessorKeys(Trait.prototype);
for (let i = 0; i < properties.length; ++i) {
const property = properties[i];
@ -350,19 +395,19 @@ export default (latus) => class Entity extends decorate(Resource) {
};
}
toJSON() {
const json = {};
const traits = Object.entries(this._traits);
for (let i = 0; i < traits.length; i++) {
const [type, trait] = traits[i];
json[type] = trait.toJSON();
}
return {
...super.toJSON(),
instanceUuid: this.instanceUuid,
traits: json,
};
}
// toJSON() {
// const json = {};
// const traits = Object.entries(this._traits);
// for (let i = 0; i < traits.length; i++) {
// const [type, trait] = traits[i];
// json[type] = trait.toJSON();
// }
// return {
// ...super.toJSON(),
// instanceUuid: this.instanceUuid,
// traits: json,
// };
// }
traitInstance(type) {
return this._traits[type];

View File

@ -1,12 +1,22 @@
import {resource} from '@avocado/resource';
import Entity from './entity';
import {EntityList} from './list';
import Packets from './packets';
import Traits from './traits';
export * from './list';
export {Entity};
export default {
hooks: {
'@avocado/traits': Traits,
'@avocado/s13n/synchronized': (latus) => ({
'@avocado/resource/resources': (latus) => ({
Entity: Entity(latus),
EntityList: EntityList(latus),
}),
'@avocado/s13n/synchronized': (latus) => ({
Entity: resource(latus).fromResourceType.Entity,
}),
'@latus/socket/packets': Packets,
},

View File

@ -1,2 +1,2 @@
export {EntityList} from './list';
export {EntityListView} from './view';
export {default as EntityList} from './list';
export {default as EntityListView} from './view';

View File

@ -1,17 +1,24 @@
import {Class, compose, EventEmitter} from '@latus/core';
import {compose, EventEmitter} from '@latus/core';
import {QuadTree, Rectangle} from '@avocado/math';
import {
JsonResource,
resource,
} from '@avocado/resource';
import {
normalize,
synchronized,
SynchronizedCreatePacket,
SynchronizedDestroyPacket,
} from '@avocado/s13n';
const {
SIDE,
} = process.env;
const decorate = compose(
EventEmitter,
);
export default (latus) => class EntityList extends decorate(Class) {
export default (latus) => class EntityList extends decorate(JsonResource) {
constructor(entities = []) {
super();
@ -21,20 +28,9 @@ export default (latus) => class EntityList extends decorate(Class) {
this._flatEntities = [];
this._informedEntities = new Map();
this._quadTree = new QuadTree();
const {fromName: {Entity}} = synchronized(latus);
const entities = await Promise.all(entities.map(Entity.load));
for (let i = 0; i < entities.length; i++) {
const entityJSON = entities[i];
if (entityJSON.uri) {
Entity.read(entityJSON.uri).then((readJSON) => {
this.addEntity(new Entity(readJSON, entityJSON));
});
}
else {
this.addEntity(new Entity(json[i]));
}
this.addEntity(entities[i]);
}
}
async acceptPacket(packet) {
@ -49,7 +45,7 @@ export default (latus) => class EntityList extends decorate(Class) {
}
}
if (packet instanceof SynchronizedCreatePacket) {
const {fromName: {Entity}} = synchronized(latus);
const {fromResourceType: {Entity}} = resource(latus);
this.addEntity(await Entity.load(packet.data.spec));
}
if (packet instanceof SynchronizedDestroyPacket) {
@ -69,7 +65,7 @@ export default (latus) => class EntityList extends decorate(Class) {
this._entities[uuid] = entity;
this._flatEntities.push(entity);
this._entityTickers.push(entity.tick);
if (AVOCADO_SERVER) {
if ('client' !== SIDE) {
this._informedEntities.set(entity, []);
}
entity.hydrate();
@ -99,15 +95,14 @@ export default (latus) => class EntityList extends decorate(Class) {
}
}
// static async extendJson(json) {
// const extended = await super.extendJson(json);
// if (extended)
// extended.sound = new Howl(json);
// await new Promise((resolve) => {
// extended.sound.once('load', resolve);
// });
// return extended;
// }
static async extendJson(json) {
const extended = await super.extendJson(json);
if (extended.entities) {
const {fromResourceType: {Entity}} = resource(latus);
extended.entities = await Promise.all(extended.entities.map(Entity.load));
}
return extended;
}
findEntity(uuid) {
return uuid in this._entities
@ -115,20 +110,6 @@ export default (latus) => class EntityList extends decorate(Class) {
: undefined;
}
fromJSON(json) {
for (let i = 0; i < json.length; i++) {
const entityJSON = json[i];
if (entityJSON.uri) {
Entity.read(entityJSON.uri).then((readJSON) => {
this.addEntity(new Entity(readJSON, entityJSON));
});
}
else {
this.addEntity(new Entity(json[i]));
}
}
}
packets(informed) {
const packets = [];
// Visible entities.
@ -183,7 +164,7 @@ export default (latus) => class EntityList extends decorate(Class) {
if (!(uuid in this._entities)) {
return;
}
if (AVOCADO_SERVER) {
if ('client' !== SIDE) {
this._informedEntities.delete(entity);
}
entity.detachFromList();

View File

@ -1,19 +1,18 @@
import {Container, Sprite} from '@avocado/graphics';
import {Container} from '@avocado/graphics';
export class EntityListView extends Container {
export default class EntityListView extends Container {
constructor(entityList) {
super();
this.entityList = entityList;
entityList.on('entityAdded', this.onListEntityAdded, this);
entityList.on('entityRemoved', this.onListEntityRemoved, this);
for (const entity of entityList) {
this.onListEntityAdded(entity);
for (let i = 0; i < entityList._entities.length; i++) {
this.onListEntityAdded(entityList._entities[i]);
}
}
onListEntityAdded(entity) {
entity.hydrate();
if (entity.is('visible')) {
this.addChild(entity.container);
}
@ -26,8 +25,8 @@ export class EntityListView extends Container {
}
renderTick(elapsed) {
for (const entity of this.entityList) {
entity.renderTick(elapsed);
for (let i = 0; i < this.entityList._entities.length; i++) {
this.entityList._entities[i].renderTick(elapsed);
}
super.renderTick(elapsed);
}

View File

@ -1,7 +1,7 @@
import {SynchronizedUpdatePacket} from '@avocado/s13n';
import {packetFromName} from '@latus/socket';
import {traitFromId, traitFromType} from '../trait';
// import {traitFromId, traitFromType} from '../trait';
export default (latus) => class EntityUpdateTraitPacket extends SynchronizedUpdatePacket {

View File

@ -1,22 +0,0 @@
import {gather} from '@latus/core';
export {default as Trait} from './trait';
export {default as StateProperty} from './state-property';
export const traitFromId = (latus) => latus.config['%traits'].fromId;
export const traitFromType = (latus) => latus.config['%traits'].fromType;
export default {
hooks: {
'@latus/core/starting': (latus) => {
// eslint-disable-next-line no-param-reassign
latus.config['%traits'] = gather(
latus,
'@avocado/traits',
'id',
'type',
);
},
},
};

View File

@ -1,26 +0,0 @@
import {Property} from '@avocado/core';
export default function StateProperty(key, meta = {}) {
const transformedKey = `$$avocado_state_property_${key}`;
return (Superclass) => {
/* eslint-disable func-names, no-new-func, no-param-reassign */
meta.emit = meta.emit || function (...args) {
this.entity.emit(...args);
};
meta.initialize = meta.initialize || function () {
this[transformedKey] = this.state[key];
};
meta.get = meta.get || new Function(`
return this.${transformedKey};
`);
meta.set = meta.set || new Function('value', `
if (value !== this.${transformedKey}) {
this.setDirty();
}
this.${transformedKey} = value;
this.state['${key}'] = value;
`);
/* eslint-enable func-names, no-new-func, no-param-reassign */
return Property(key, meta)(Superclass);
};
}

View File

@ -1,157 +0,0 @@
import {Synchronized} from '@avocado/s13n';
import {compose, Class} from '@latus/core';
const decorate = compose(
Synchronized,
);
export default class Trait extends decorate(Class) {
constructor(entity, params, state) {
super();
this.entity = entity;
const ctor = this.constructor;
this._fastDirtyCheck = true;
this._memoizedListeners = undefined;
this.params = ctor.defaultParamsWith(params);
this.state = ctor.defaultStateWith(state);
this.previousState = JSON.parse(JSON.stringify(this.state));
if (this.tick) {
this.tick = this.tick.bind(this);
}
if (this.renderTick) {
this.renderTick = this.renderTick.bind(this);
}
}
// eslint-disable-next-line class-methods-use-this, no-unused-vars
acceptPacket(packet) {}
cleanPackets() {
if (!this._fastDirtyCheck) {
return;
}
const keys = Object.keys(this.state);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
this.previousState[key] = this.state[key];
}
this._fastDirtyCheck = false;
}
static behaviorTypes() {
return {};
}
static defaultJSON() {
return {
params: this.defaultParams(),
state: this.defaultState(),
};
}
static defaultParams() {
return {};
}
static defaultParamsWith(defaults) {
return {...this.defaultParams(), ...defaults};
}
static defaultState() {
return {};
}
static defaultStateWith(defaults) {
return {...this.defaultState(), ...defaults};
}
static dependencies() {
return [];
}
static describeParams() {
return {};
}
static describeState() {
return {};
}
// eslint-disable-next-line class-methods-use-this
destroy() {}
// eslint-disable-next-line class-methods-use-this
hooks() {
return {};
}
// eslint-disable-next-line class-methods-use-this, no-empty-function
async hydrate() {}
get isDirty() {
const keys = Object.keys(this.state);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
if (this.state[key] !== this.previousState[key]) {
return true;
}
}
return false;
}
// eslint-disable-next-line class-methods-use-this
listeners() {
return {};
}
memoizedListeners() {
if (!this._memoizedListeners) {
this._memoizedListeners = this.listeners();
}
return this._memoizedListeners;
}
// eslint-disable-next-line class-methods-use-this
methods() {
return {};
}
// eslint-disable-next-line class-methods-use-this, no-unused-vars
packets(informed) {
return [];
}
// eslint-disable-next-line class-methods-use-this
packetsAreIdempotent() {
return false;
}
setDirty() {
this._fastDirtyCheck = true;
this.entity._fastDirtyCheck = true;
}
stateDifferences() {
const differences = {};
const keys = Object.keys(this.state);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
if (this.state[key] !== this.previousState[key]) {
differences[key] = {
old: this.previousState[key],
value: this.state[key],
};
}
}
return differences;
}
toJSON() {
return {
params: this.params,
state: this.state,
};
}
}

View File

@ -21,7 +21,7 @@ const decorate = compose(
}),
);
export default class Alive extends decorate(Trait) {
export default (latus) => class Alive extends decorate(Trait) {
static behaviorTypes() {
return {
@ -107,17 +107,16 @@ export default class Alive extends decorate(Trait) {
};
}
static type() {
return 'alive';
}
constructor(entity, params, state) {
super(entity, params, state);
this._context = new Context({
entity: [this.entity, 'entity'],
});
this._deathActions = new Actions(compile(this.params.deathActions));
this._deathCondition = compile(this.params.deathCondition);
constructor(...args) {
super(...args);
this._context = new Context(
{
entity: [this.entity, 'entity'],
},
latus,
);
this._deathActions = new Actions(compile(this.params.deathActions, latus));
this._deathCondition = compile(this.params.deathCondition, latus);
}
destroy() {
@ -203,4 +202,4 @@ export default class Alive extends decorate(Trait) {
}
}
}
};

View File

@ -57,12 +57,8 @@ export default class Directional extends decorate(Trait) {
};
}
static type() {
return 'directional';
}
constructor(entity, params, state) {
super(entity, params, state);
constructor(...args) {
super(...args);
this.directionCount = this.params.directionCount;
}

View File

@ -58,12 +58,8 @@ export default class Existent extends decorate(Trait) {
};
}
static type() {
return 'existent';
}
constructor(entity, params, state) {
super(entity, params, state);
constructor(...args) {
super(...args);
this._isDestroying = false;
this._isTicking = this.params.isTicking;
}

View File

@ -8,7 +8,7 @@ import Positioned from './positioned';
import Spawner from './spawner';
export default (latus) => ({
Alive,
Alive: Alive(latus),
Directional,
Existent,
Listed,

View File

@ -1,6 +1,9 @@
import {Rectangle} from '@avocado/math';
import {Trait} from '@avocado/traits';
import {Trait} from '../trait';
const {
SIDE,
} = process.env;
export default class Listed extends Trait {
@ -22,12 +25,8 @@ export default class Listed extends Trait {
};
}
static type() {
return 'listed';
}
constructor(entity, params, state) {
super(entity, params, state);
constructor(...args) {
super(...args);
this.entity.list = null;
this.quadTreeAabb = [];
this.quadTreeNodes = [];
@ -77,7 +76,7 @@ export default class Listed extends Trait {
}
resetQuadTreeNodes() {
if (AVOCADO_SERVER) {
if ('client' !== SIDE) {
const aabb = this.entity.visibleAabb;
if (
this.quadTreeAabb.length > 0

View File

@ -74,12 +74,8 @@ export default class Mobile extends decorate(Trait) {
};
}
static type() {
return 'mobile';
}
constructor(entity, params, state) {
super(entity, params, state);
constructor(...args) {
super(...args);
this.appliedMovement = [0, 0];
}

View File

@ -1,7 +1,6 @@
import {Trait} from '@avocado/traits';
import {compose} from '@latus/core';
import {Trait} from '../trait';
const decorate = compose(
);
@ -22,12 +21,8 @@ export default class Perishable extends decorate(Trait) {
};
}
static type() {
return 'perishable';
}
constructor(entity, params, state) {
super(entity, params, state);
constructor(...args) {
super(...args);
this.ttl = this.params.ttl;
}

View File

@ -1,7 +1,10 @@
import {Vector} from '@avocado/math';
import {Trait} from '@avocado/traits';
import {compose, EventEmitter} from '@latus/core';
import {Trait} from '../trait';
const {
SIDE,
} = process.env;
const decorate = compose(
EventEmitter,
@ -54,18 +57,14 @@ export default class Positioned extends decorate(Trait) {
};
}
static type() {
return 'positioned';
}
constructor(entity, params, state) {
super(entity, params, state);
constructor(...args) {
super(...args);
this.on('_positionChanged', this.on_positionChanged, this);
const {x, y} = this.state;
this._position = [x, y];
this.entity.position[0] = x;
this.entity.position[1] = y;
if (AVOCADO_CLIENT) {
if ('client' === SIDE) {
this.serverPosition = this._position;
this.serverPositionDirty = false;
this.on('serverPositionChanged', this.onServerPositionChanged, this);
@ -74,7 +73,7 @@ export default class Positioned extends decorate(Trait) {
destroy() {
this.off('_positionChanged', this.on_positionChanged);
if (AVOCADO_CLIENT) {
if ('client' === SIDE) {
this.off('serverPositionChanged', this.onServerPositionChanged);
}
}
@ -88,7 +87,7 @@ export default class Positioned extends decorate(Trait) {
// eslint-disable-next-line camelcase
on_positionChanged(oldPosition, newPosition) {
[this.entity.position[0], this.entity.position[1]] = newPosition;
if (AVOCADO_SERVER) {
if ('client' !== SIDE) {
[this.state.x, this.state.y] = newPosition;
this.setDirty();
}
@ -117,7 +116,7 @@ export default class Positioned extends decorate(Trait) {
isTickingChanged: () => {
// Snap position on ticking change.
if (AVOCADO_CLIENT) {
if ('client' === SIDE) {
this._position = this.serverPosition;
}
},

View File

@ -1,5 +1,5 @@
import {compose} from '@latus/core';
import {synchronized} from '@avocado/s13n';
import {resource} from '@avocado/resource';
import {StateProperty, Trait} from '@avocado/traits';
import merge from 'deepmerge';
@ -118,10 +118,6 @@ export default (latus) => class Spawner extends decorate(Trait) {
.reduce((r, key) => ({...r, [key]: key}), {});
}
static type() {
return 'spawner';
}
destroy() {
while (this.children.length > 0) {
const child = this.children.pop();
@ -131,8 +127,8 @@ export default (latus) => class Spawner extends decorate(Trait) {
}
}
constructor(entity, params, state) {
super(entity, params, state);
constructor(...args) {
super(...args);
this.children = [];
this.childrenListeners = new Map();
this.spawnJSONs = this.params.spawns;
@ -241,7 +237,7 @@ export default (latus) => class Spawner extends decorate(Trait) {
const childIndex = this.children.length;
this.children.push(null);
const list = this.destinationEntityList();
const {fromName: {Entity}} = synchronized(latus);
const {fromResourceType: {Entity}} = resource(latus);
const child = await Entity.loadOrInstance(json);
this.children[childIndex] = child;
// Listen for destroy event.

View File

@ -0,0 +1,126 @@
import {resource} from '@avocado/resource';
import {Trait, traits} from '@avocado/traits';
import {Latus} from '@latus/core';
import {expect} from 'chai';
const {name} = require('../package.json');
describe(name, () => {
let latus;
let Entity;
beforeEach(async () => {
latus = Latus.mock([
['@avocado/entity', `${__dirname}/../src`],
'@avocado/resource',
'@avocado/traits',
]);
await Promise.all(latus.invokeFlat('@latus/core/starting'));
({fromResourceType: {Entity}} = resource(latus));
});
it('has sane defaults', () => {
const entity = new Entity();
expect(entity.allTraitInstances()).to.deep.equal({});
expect(entity.allTraitTypes()).to.deep.equal([]);
});
it('can add and remove traits', async () => {
const entity = new Entity();
const TestTrait = class extends Trait {
static get type() {
return 'TestTrait';
}
};
traits(latus).fromType['TestTrait'] = TestTrait;
const trait = await TestTrait.load();
entity.addTrait([TestTrait, {}]);
expect(entity.is('TestTrait')).to.equal(true);
entity.removeTrait('TestTrait');
expect(entity.is('TestTrait')).to.equal(false);
});
it('can add traits asynchronously', async () => {
const DELAY = 30;
class AsyncTrait extends Trait {
static async extendJson(json) {
const extended = await super.extendJson(json);
await new Promise((resolve) => setTimeout(resolve, DELAY));
return extended;
}
}
traits(latus).fromType['Async'] = AsyncTrait;
let start = Date.now();
const entity = await Entity.load({
traits: {
Async: {},
},
});
expect(Date.now() - start).to.be.at.least(DELAY);
});
describe('Traits', () => {
it('is Alive', async () => {
const entity = await Entity.load({
traits: {
Alive: {},
},
});
expect(entity.is('Alive')).to.equal(true);
});
it('is Directional', async () => {
const entity = await Entity.load({
traits: {
Directional: {},
},
});
expect(entity.is('Directional')).to.equal(true);
});
it('is Existent', async () => {
const entity = await Entity.load({
traits: {
Existent: {},
},
});
expect(entity.is('Existent')).to.equal(true);
});
it('is Listed', async () => {
const entity = await Entity.load({
traits: {
Listed: {},
},
});
expect(entity.is('Listed')).to.equal(true);
});
it('is Mobile', async () => {
const entity = await Entity.load({
traits: {
Mobile: {},
},
});
expect(entity.is('Mobile')).to.equal(true);
});
it('is Perishable', async () => {
const entity = await Entity.load({
traits: {
Perishable: {},
},
});
expect(entity.is('Perishable')).to.equal(true);
});
it('is Positioned', async () => {
const entity = await Entity.load({
traits: {
Positioned: {},
},
});
expect(entity.is('Positioned')).to.equal(true);
});
it('is Spawner', async () => {
const entity = await Entity.load({
traits: {
Spawner: {},
},
});
expect(entity.is('Spawner')).to.equal(true);
});
});
});

View File

@ -0,0 +1,9 @@
import {expect} from 'chai';
const {name} = require('../package.json');
describe(name, () => {
it('exists', () => {
expect(true).to.equal(true);
})
});

View File

@ -2,13 +2,13 @@
# yarn lockfile v1
"@avocado/behavior@2.0.0", "@avocado/behavior@^2.0.0":
"@avocado/behavior@2.0.0":
version "2.0.0"
resolved "http://172.18.0.8:4873/@avocado%2fbehavior/-/behavior-2.0.0.tgz#5a59518aa2dadb24dcc7a81dfad3a544a0131e15"
integrity sha512-AUrVcrNUNTDLrKa9PsxuvnCuRm+n1yKtDXjoXYzJ4NgHBT0//A6Jp6S0odURTprVsd1Ef77U8WpAj8GdU7ekQQ==
resolved "https://npm.i12e.cha0s.io/@avocado%2fbehavior/-/behavior-2.0.0.tgz#24d29c0cff73aea30ad323b5c5cb474ee9fe8c8a"
integrity sha512-BQVlDW80DeRj6ZoPDjq7EuAezCYGixsLhNemrDaDnmPfk+Y3nPIMR5sbq7acIw2QM6NkW6aud5sLxboxgwIMbA==
dependencies:
"@avocado/core" "2.0.0"
"@avocado/entity" "2.0.0"
"@avocado/traits" "^2.0.0"
"@latus/core" "2.0.0"
debug "4.3.1"
deepmerge "^4.2.2"
@ -16,40 +16,23 @@
"@avocado/core@2.0.0", "@avocado/core@^2.0.0":
version "2.0.0"
resolved "https://npm.i12e.cha0s.io/@avocado%2fcore/-/core-2.0.0.tgz#636ea3c3b54a38538c59485080f6a0e48f1798e7"
integrity sha512-VW+ygRHaQQwaL5rKZGm0n0DNfvj+H89qQx+67veCUmUuRav3XAeE0iYs8Lgfc3CJLPz/alqt/dVPMXd5QDR+Mg==
resolved "https://npm.i12e.cha0s.io/@avocado%2fcore/-/core-2.0.0.tgz#193488e95ae8d6dcae4125adb7a62ee127e370b0"
integrity sha512-rM7BzgsNvoHxadCU2nh4+1QyHJGDf3os3TWrjS1jCcR/YOIs7ZVvdcCqjx5jFECyL0CE8FEpPiFfD/JS0QIcCA==
dependencies:
debug "4.3.1"
"@avocado/entity@2.0.0", "@avocado/entity@^2.0.0":
"@avocado/graphics@2.0.0", "@avocado/graphics@^2.0.0":
version "2.0.0"
resolved "https://npm.i12e.cha0s.io/@avocado%2fentity/-/entity-2.0.0.tgz#935d667171b849d5764728ab1af30fe4a017c7b3"
integrity sha512-F/yDjVQbBnZEQPQ29ZKO0x8dgPiqM1VUnAr4qLekxIvIgQt6Vn7n7AOaIZZLsHy5yjZI0JzjoaELqUlT3NHi2w==
dependencies:
"@avocado/behavior" "^2.0.0"
"@avocado/core" "2.0.0"
"@avocado/math" "2.0.0"
"@avocado/resource" "2.0.0"
"@avocado/s13n" "^2.0.0"
"@avocado/timing" "^2.0.0"
"@latus/core" "^2.0.0"
"@latus/socket" "^2.0.0"
debug "4.3.1"
deepmerge "^4.2.2"
lodash.without "^4.4.0"
"@avocado/graphics@^2.0.0":
version "2.0.0"
resolved "https://npm.i12e.cha0s.io/@avocado%2fgraphics/-/graphics-2.0.0.tgz#cc83b17a13ba5baf4bc1ed5345ead5090bb41fff"
integrity sha512-aHpkLHaeR9Ujd1sxLCALKK79BV1CGau8MilqZxucc9JbnXllXhACBKrvHjRg33OT9NKlXLDF87svATJsrFqwGQ==
resolved "https://npm.i12e.cha0s.io/@avocado%2fgraphics/-/graphics-2.0.0.tgz#cbb8907c96a6386fe98f7abe138a389e0267f7ee"
integrity sha512-vPHr/GBZZuGcRzlXVOuWMTLHfv+hlySsG0ItUJWbP8E6ZMruqG4smgC9QypqYYpvIB0Y1iN+xDJRAhmgF0r4/w==
dependencies:
"@avocado/core" "2.0.0"
"@avocado/entity" "2.0.0"
"@avocado/input" "2.0.0"
"@avocado/math" "2.0.0"
"@avocado/resource" "2.0.0"
"@avocado/traits" "^2.0.0"
"@latus/core" "^2.0.0"
"@latus/socket" "^2.0.0"
"@latus/socket" "2.0.0"
"@pixi/constants" "^5.3.6"
"@pixi/core" "^5.3.6"
"@pixi/display" "^5.3.6"
@ -64,63 +47,65 @@
"@avocado/input@2.0.0":
version "2.0.0"
resolved "https://npm.i12e.cha0s.io/@avocado%2finput/-/input-2.0.0.tgz#fd6c9eb436416a008f5502cf6bba3f9277256021"
integrity sha512-4R7loMM8edahKhMQcpZToBc2Q/y2AKeHIbwssq4LtxA4cP8W3Yn/JNcrkwILQ/CAWmeR7ZALLQ7MYrO+A/1AKg==
resolved "https://npm.i12e.cha0s.io/@avocado%2finput/-/input-2.0.0.tgz#fb02293506a390c109b0662e25cc48049163faf9"
integrity sha512-bfhhlFAwNnVdDjhDOHG5zr7TwBnlr3kD9s16nAgvDOV+wijUw+1N3cpbj2NEyXxhATsmvCQ6I7Jyzik32jYX3A==
dependencies:
"@latus/core" "^2.0.0"
"@latus/socket" "^2.0.0"
"@latus/core" "2.0.0"
"@latus/socket" "2.0.0"
debug "4.3.1"
"@avocado/math@2.0.0", "@avocado/math@^2.0.0":
"@avocado/math@2.0.0":
version "2.0.0"
resolved "https://npm.i12e.cha0s.io/@avocado%2fmath/-/math-2.0.0.tgz#8c1a04a08a1085c646029c21e7e49798227bee35"
integrity sha512-SvF3pEYkg6L082ydxHNS/TMOrdC1d55fiYIsocX7y8ym7ySO2lH2pzoOo4/1AndQjkU5zpw3ENAm+FCDT0goug==
resolved "https://npm.i12e.cha0s.io/@avocado%2fmath/-/math-2.0.0.tgz#6de3d9b4ae9118c3d2e8580534cd834efd96350d"
integrity sha512-uBQs/xEyQ7X9NR40PFO2vwsAguLwSmORYxGM3/LZH0DzmcEiWouxIYCzuk2neNL38Fiz+vBMQXdLDSEp9gIgYw==
dependencies:
"@avocado/core" "^2.0.0"
"@avocado/core" "2.0.0"
"@latus/core" "^2.0.0"
d3-quadtree "^2.0.0"
debug "4.3.1"
"@avocado/resource@2.0.0", "@avocado/resource@^2.0.0":
version "2.0.0"
resolved "https://npm.i12e.cha0s.io/@avocado%2fresource/-/resource-2.0.0.tgz#2eb014c2c876f3f51bc71d6594d93091b22013c8"
integrity sha512-3dks0rsuY1twlLm+vTrsqfV9anXR/0ZdQfe2YAJIdmiVQ7QEB2mQhUgMvqjgsuhI7ad1ygxD/I9hXBjl6CvyjA==
resolved "https://npm.i12e.cha0s.io/@avocado%2fresource/-/resource-2.0.0.tgz#7cbc44874386c067d58bcfc8a5ba4c42e3261565"
integrity sha512-qpYip27Ih6dfYo7E5ZbqjKuYtKMo4WytullPSTJcuyZ892dRbEKHEqbzH/z65W39wtuVfTVpwVBJV7SCRYnRpw==
dependencies:
"@avocado/core" "^2.0.0"
"@latus/core" "^2.0.0"
"@avocado/core" "2.0.0"
"@latus/core" "2.0.0"
debug "4.3.1"
deepmerge "^4.2.2"
uuid "^8.3.2"
"@avocado/s13n@2.0.0", "@avocado/s13n@^2.0.0":
version "2.0.0"
resolved "https://npm.i12e.cha0s.io/@avocado%2fs13n/-/s13n-2.0.0.tgz#ec6b7c9d28753c21c5fe265a5d2f2be5bc752d69"
integrity sha512-b+eTosKoNIh4Mhd5HEbCJ366sXMsYgnqFYHHHRDWGScOnr+RvYI4aa8gIqDRQ+F0WMfXs03R9sEGB/fmoFYCtg==
resolved "https://npm.i12e.cha0s.io/@avocado%2fs13n/-/s13n-2.0.0.tgz#a32e92c197e2915b1b7049d1926bb6d6c14a8f35"
integrity sha512-GMl0CwYz4eZV3PnDlaCnd5CseKtwdza6zG5TWy2g+5xBi+OcOwjKRAqKI4gOiiaJcGYtxnXJvSajdE12fhZGmA==
dependencies:
"@latus/core" "^2.0.0"
"@latus/socket" "^2.0.0"
"@latus/core" "2.0.0"
"@latus/socket" "2.0.0"
debug "4.3.1"
msgpack-lite "^0.1.26"
"@avocado/timing@2.0.0", "@avocado/timing@^2.0.0":
"@avocado/timing@2.0.0":
version "2.0.0"
resolved "https://npm.i12e.cha0s.io/@avocado%2ftiming/-/timing-2.0.0.tgz#de76571519cb835a7f45528e91bdb9a18f1947fa"
integrity sha512-pJITg7q3kX9H2XEFP+jHPMiY317o4rCb/zzBdUbqC1RY5z5Et/LeTkQa/pd4IPqXeUK9omW4eM24jdCp8uH1tg==
resolved "https://npm.i12e.cha0s.io/@avocado%2ftiming/-/timing-2.0.0.tgz#8cc6e50449da6b8fa7473befc147b86b45540d5e"
integrity sha512-PK70bGLbbFTqy2ieibeaoEQQNRFZK1sqGlLfg/SafjEHXdX8Dzm3kxh6mgudOiPDtBwo1M8jl1IQi6aYJEGXIA==
dependencies:
"@avocado/core" "^2.0.0"
"@avocado/entity" "^2.0.0"
"@avocado/graphics" "^2.0.0"
"@avocado/math" "^2.0.0"
"@avocado/resource" "^2.0.0"
"@latus/core" "^2.0.0"
"@latus/socket" "^2.0.0"
"@avocado/core" "2.0.0"
"@avocado/graphics" "2.0.0"
"@avocado/math" "2.0.0"
"@avocado/resource" "2.0.0"
"@avocado/traits" "^2.0.0"
"@latus/core" "2.0.0"
"@latus/socket" "2.0.0"
debug "4.3.1"
"@avocado/traits@^2.0.0":
version "2.0.0"
resolved "http://172.18.0.8:4873/@avocado%2ftraits/-/traits-2.0.0.tgz#4717919e101a4fa81469bc5efe7c5478bb293a4e"
integrity sha512-7siD4XME3NzhTholUPndRYloHowV8z/s+FnzqG7p2/idNHrXURXsVBeYBI9lcM0SX1ONY+9OVdH0ykiu5qBScg==
resolved "https://npm.i12e.cha0s.io/@avocado%2ftraits/-/traits-2.0.0.tgz#eb8dbefdb96e0680caab79ccb5b7bb57a9bf5c35"
integrity sha512-SQLMt/DvzFvIBTJr3hNr/i5TO6pvcCTMwqEmiSXtSBK8GHoBO5Zv1h6KMbeXkRtfQAut0nuPdN3PiHcw2S1ydg==
dependencies:
"@avocado/core" "^2.0.0"
"@avocado/resource" "^2.0.0"
"@avocado/s13n" "^2.0.0"
"@latus/core" "^2.0.0"
debug "4.3.1"
@ -1017,20 +1002,19 @@
"@latus/core@2.0.0", "@latus/core@^2.0.0":
version "2.0.0"
resolved "https://npm.i12e.cha0s.io/@latus%2fcore/-/core-2.0.0.tgz#4447b4bba265b684035be1dacefd6ba4913a47d4"
integrity sha512-OSsbO4wvVJFPZTMRDX5LAXOA+MQY8FOSK7PcTEJKeiRqOMeMIVkZc6q6KNlUIYivE+87rlM7S/CctHoA7uvQvg==
resolved "https://npm.i12e.cha0s.io/@latus%2fcore/-/core-2.0.0.tgz#74444a0a269246930dceb8d445b30873d7ed73a2"
integrity sha512-ob2Mbxd10yRqLRv0bNZkiZ9OWjQrHKBXxQsu++n42lWkM6A/TXudp955CemJbl5IMzBkCAG5spXiID0eIZb/9Q==
dependencies:
debug "4.3.1"
js-yaml "3.14.0"
lodash.capitalize "^4.2.1"
lodash.flatten "^4.4.0"
"@latus/http@^2.0.0":
"@latus/http@2.0.0":
version "2.0.0"
resolved "https://npm.i12e.cha0s.io/@latus%2fhttp/-/http-2.0.0.tgz#ca4bf307c3227fc4cd20281bf300d49f7a0b8437"
integrity sha512-IjoHtULmHzgn81g4e6Dm40EndkdEPyv3hh3DYMipGNK9GVF4BxiZI62v5hpuxaaluS+i3US2w5iuU2vwf/Sw7g==
resolved "https://npm.i12e.cha0s.io/@latus%2fhttp/-/http-2.0.0.tgz#4e7e855c28dbd3287caaca93fc220184be3732b9"
integrity sha512-Zq9mx6N+yYHMAf5vZ34Vu4wcCWBxV643rSnzRwxIijpx444zZXGverKZunEDq12DcfsTIrHCmbgVT9E3wdu7dg==
dependencies:
"@latus/core" "^2.0.0"
"@latus/core" "2.0.0"
"@neutrinojs/web" "^9.1.0"
debug "4.3.1"
express "^4.17.1"
@ -1043,13 +1027,13 @@
webpack-hot-middleware "^2.25.0"
webpack-virtual-modules "^0.4.1"
"@latus/socket@2.0.0", "@latus/socket@^2.0.0":
"@latus/socket@2.0.0":
version "2.0.0"
resolved "https://npm.i12e.cha0s.io/@latus%2fsocket/-/socket-2.0.0.tgz#3dc0f02ec3e24e515ba6fc9302e7ac4a63351e25"
integrity sha512-0NlEYCBPkxtFX0utLhMacqGhd6rtSaQTudMXTtwuWxqRdkzJb4aC5BJ2BtDj0iH5KGCdbcQ4kE1cGGKdOk5mMA==
resolved "https://npm.i12e.cha0s.io/@latus%2fsocket/-/socket-2.0.0.tgz#f8843d8733c3f84b7ac9f72afb62a1c783077a7d"
integrity sha512-+hpxoIo0lILIZ2U/xkvD3R6Mv5/SJKaMjKTBbyefMPxwK1TDnhO3peZb5SEizITNw8xmsM/Y06h7s2TrlYN64Q==
dependencies:
"@latus/core" "^2.0.0"
"@latus/http" "^2.0.0"
"@latus/core" "2.0.0"
"@latus/http" "2.0.0"
buffer "^6.0.3"
debug "4.3.1"
proxy-addr "^2.0.6"
@ -1335,9 +1319,9 @@
integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
"@types/node@*":
version "14.14.16"
resolved "https://npm.i12e.cha0s.io/@types%2fnode/-/node-14.14.16.tgz#3cc351f8d48101deadfed4c9e4f116048d437b4b"
integrity sha512-naXYePhweTi+BMv11TgioE2/FXU4fSl29HAH1ffxVciNsH3rYXjNP2yM8wqmSm7jS20gM8TIklKiTen+1iVncw==
version "14.14.19"
resolved "https://npm.i12e.cha0s.io/@types%2fnode/-/node-14.14.19.tgz#5135176a8330b88ece4e9ab1fdcfc0a545b4bab4"
integrity sha512-4nhBPStMK04rruRVtVc6cDqhu7S9GZai0fpXgPXrFpcPX6Xul8xnrjSdGB4KPBVYG/R5+fXWdCM8qBoiULWGPQ==
"@types/source-list-map@*":
version "0.1.2"
@ -1585,6 +1569,16 @@ ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.4, ajv@^6.12.5:
json-schema-traverse "^0.4.1"
uri-js "^4.2.2"
ajv@^7.0.2:
version "7.0.3"
resolved "https://npm.i12e.cha0s.io/ajv/-/ajv-7.0.3.tgz#13ae747eff125cafb230ac504b2406cf371eece2"
integrity sha512-R50QRlXSxqXcQP5SvKUrw8VZeypvo12i2IX0EeR5PiZ7bEKeHWgzgo264LDadUsCU42lTJVhFikTqJwNeH34gQ==
dependencies:
fast-deep-equal "^3.1.1"
json-schema-traverse "^1.0.0"
require-from-string "^2.0.2"
uri-js "^4.2.2"
ansi-colors@4.1.1, ansi-colors@^4.1.1:
version "4.1.1"
resolved "https://npm.i12e.cha0s.io/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348"
@ -1971,7 +1965,7 @@ bonjour@^3.5.0:
multicast-dns "^6.0.1"
multicast-dns-service-types "^1.1.0"
boolbase@~1.0.0:
boolbase@^1.0.0, boolbase@~1.0.0:
version "1.0.0"
resolved "https://npm.i12e.cha0s.io/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24=
@ -2654,20 +2648,20 @@ css-loader@^3.6.0:
schema-utils "^2.7.0"
semver "^6.3.0"
css-select@^1.1.0:
version "1.2.0"
resolved "https://npm.i12e.cha0s.io/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858"
integrity sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=
css-select@^2.0.2:
version "2.1.0"
resolved "https://npm.i12e.cha0s.io/css-select/-/css-select-2.1.0.tgz#6a34653356635934a81baca68d0255432105dbef"
integrity sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==
dependencies:
boolbase "~1.0.0"
css-what "2.1"
domutils "1.5.1"
nth-check "~1.0.1"
boolbase "^1.0.0"
css-what "^3.2.1"
domutils "^1.7.0"
nth-check "^1.0.2"
css-what@2.1:
version "2.1.3"
resolved "https://npm.i12e.cha0s.io/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2"
integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==
css-what@^3.2.1:
version "3.4.2"
resolved "https://npm.i12e.cha0s.io/css-what/-/css-what-3.4.2.tgz#ea7026fcb01777edbde52124e21f327e7ae950e4"
integrity sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==
cssesc@^3.0.0:
version "3.0.0"
@ -2958,15 +2952,7 @@ domhandler@^2.3.0:
dependencies:
domelementtype "1"
domutils@1.5.1:
version "1.5.1"
resolved "https://npm.i12e.cha0s.io/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf"
integrity sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=
dependencies:
dom-serializer "0"
domelementtype "1"
domutils@^1.5.1:
domutils@^1.5.1, domutils@^1.7.0:
version "1.7.0"
resolved "https://npm.i12e.cha0s.io/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a"
integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==
@ -3347,9 +3333,9 @@ eslint-visitor-keys@^2.0.0:
integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==
eslint@^7:
version "7.16.0"
resolved "https://npm.i12e.cha0s.io/eslint/-/eslint-7.16.0.tgz#a761605bf9a7b32d24bb7cde59aeb0fd76f06092"
integrity sha512-iVWPS785RuDA4dWuhhgXTNrGxHHK3a8HLSMBgbbU59ruJDubUraXN8N5rn7kb8tG6sjg74eE0RA3YWT51eusEw==
version "7.17.0"
resolved "https://npm.i12e.cha0s.io/eslint/-/eslint-7.17.0.tgz#4ccda5bf12572ad3bf760e6f195886f50569adb0"
integrity sha512-zJk08MiBgwuGoxes5sSQhOtibZ75pz0J35XTRlZOk9xMffhpA9BTbQZxoXZzOl5zMbleShbGwtw+1kGferfFwQ==
dependencies:
"@babel/code-frame" "^7.0.0"
"@eslint/eslintrc" "^0.2.2"
@ -4157,7 +4143,7 @@ html-webpack-plugin@^4.5.0:
tapable "^1.1.3"
util.promisify "1.0.0"
htmlparser2@^3.3.0:
htmlparser2@^3.10.1:
version "3.10.1"
resolved "https://npm.i12e.cha0s.io/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f"
integrity sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==
@ -4207,9 +4193,9 @@ http-errors@~1.7.2:
toidentifier "1.0.0"
http-parser-js@>=0.5.1:
version "0.5.2"
resolved "https://npm.i12e.cha0s.io/http-parser-js/-/http-parser-js-0.5.2.tgz#da2e31d237b393aae72ace43882dd7e270a8ff77"
integrity sha512-opCO9ASqg5Wy2FNo7A0sxy71yGbbkJJXLdgMK04Tcypw9jr2MgWbyubb0+WdmDmGnFflO7fRbqbaihh/ENDlRQ==
version "0.5.3"
resolved "https://npm.i12e.cha0s.io/http-parser-js/-/http-parser-js-0.5.3.tgz#01d2709c79d41698bb01d4decc5e9da4e4a033d9"
integrity sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg==
http-proxy-middleware@0.19.1:
version "0.19.1"
@ -4687,6 +4673,11 @@ json-schema-traverse@^0.4.1:
resolved "https://npm.i12e.cha0s.io/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
json-schema-traverse@^1.0.0:
version "1.0.0"
resolved "https://npm.i12e.cha0s.io/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2"
integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==
json-stable-stringify-without-jsonify@^1.0.1:
version "1.0.1"
resolved "https://npm.i12e.cha0s.io/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
@ -4826,11 +4817,6 @@ locate-path@^6.0.0:
dependencies:
p-locate "^5.0.0"
lodash.capitalize@^4.2.1:
version "4.2.1"
resolved "https://npm.i12e.cha0s.io/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz#f826c9b4e2a8511d84e3aca29db05e1a4f3b72a9"
integrity sha1-+CbJtOKoUR2E46yinbBeGk87cqk=
lodash.clonedeep@^4.5.0:
version "4.5.0"
resolved "https://npm.i12e.cha0s.io/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
@ -5027,22 +5013,17 @@ miller-rabin@^4.0.0:
bn.js "^4.0.0"
brorand "^1.0.1"
mime-db@1.44.0:
version "1.44.0"
resolved "https://npm.i12e.cha0s.io/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92"
integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==
"mime-db@>= 1.43.0 < 2":
mime-db@1.45.0, "mime-db@>= 1.43.0 < 2":
version "1.45.0"
resolved "https://npm.i12e.cha0s.io/mime-db/-/mime-db-1.45.0.tgz#cceeda21ccd7c3a745eba2decd55d4b73e7879ea"
integrity sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w==
mime-types@^2.1.27, mime-types@~2.1.17, mime-types@~2.1.24:
version "2.1.27"
resolved "https://npm.i12e.cha0s.io/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f"
integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==
version "2.1.28"
resolved "https://npm.i12e.cha0s.io/mime-types/-/mime-types-2.1.28.tgz#1160c4757eab2c5363888e005273ecf79d2a0ecd"
integrity sha512-0TO2yJ5YHYr7M2zzT7gDU1tbwHxEUWBCLt0lscSNpcdAfFyJOVEpRYNS7EXVcTLNj/25QO8gulHC5JtTzSE2UQ==
dependencies:
mime-db "1.44.0"
mime-db "1.45.0"
mime@1.6.0:
version "1.6.0"
@ -5373,7 +5354,7 @@ npm-run-path@^2.0.0:
dependencies:
path-key "^2.0.0"
nth-check@~1.0.1:
nth-check@^1.0.2:
version "1.0.2"
resolved "https://npm.i12e.cha0s.io/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c"
integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==
@ -6272,13 +6253,13 @@ remove-trailing-separator@^1.0.1:
integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8=
renderkid@^2.0.4:
version "2.0.4"
resolved "https://npm.i12e.cha0s.io/renderkid/-/renderkid-2.0.4.tgz#d325e532afb28d3f8796ffee306be8ffd6fc864c"
integrity sha512-K2eXrSOJdq+HuKzlcjOlGoOarUu5SDguDEhE7+Ah4zuOWL40j8A/oHvLlLob9PSTNvVnBd+/q0Er1QfpEuem5g==
version "2.0.5"
resolved "https://npm.i12e.cha0s.io/renderkid/-/renderkid-2.0.5.tgz#483b1ac59c6601ab30a7a596a5965cabccfdd0a5"
integrity sha512-ccqoLg+HLOHq1vdfYNm4TBeaCDIi1FLt3wGojTDSvdewUv65oTmI3cnT2E4hRjl1gzKZIPK+KZrXzlUYKnR+vQ==
dependencies:
css-select "^1.1.0"
css-select "^2.0.2"
dom-converter "^0.2"
htmlparser2 "^3.3.0"
htmlparser2 "^3.10.1"
lodash "^4.17.20"
strip-ansi "^3.0.0"
@ -6297,6 +6278,11 @@ require-directory@^2.1.1:
resolved "https://npm.i12e.cha0s.io/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I=
require-from-string@^2.0.2:
version "2.0.2"
resolved "https://npm.i12e.cha0s.io/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==
require-main-filename@^2.0.0:
version "2.0.0"
resolved "https://npm.i12e.cha0s.io/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
@ -7035,11 +7021,11 @@ supports-color@^6.1.0:
has-flag "^3.0.0"
table@^6.0.4:
version "6.0.4"
resolved "https://npm.i12e.cha0s.io/table/-/table-6.0.4.tgz#c523dd182177e926c723eb20e1b341238188aa0d"
integrity sha512-sBT4xRLdALd+NFBvwOz8bw4b15htyythha+q+DVZqy2RS08PPC8O2sZFgJYEY7bJvbCFKccs+WIZ/cd+xxTWCw==
version "6.0.6"
resolved "https://npm.i12e.cha0s.io/table/-/table-6.0.6.tgz#e9223f1e851213e2e43ab584b0fec33fb09a8e7a"
integrity sha512-OInCtPmDNieVBkVFi6C8RwU2S2H0h8mF3e3TQK4nreaUNCpooQUkI+A/KuEkm5FawfhWIfNqG+qfelVVR+V00g==
dependencies:
ajv "^6.12.4"
ajv "^7.0.2"
lodash "^4.17.20"
slice-ansi "^4.0.0"
string-width "^4.2.0"

View File

@ -3,3 +3,4 @@
!/.*
!/webpack.config.js
!src/**/*.js
!/test/**/*.js

View File

@ -0,0 +1,9 @@
import {expect} from 'chai';
const {name} = require('../package.json');
describe(name, () => {
it('exists', () => {
expect(true).to.equal(true);
})
});

View File

@ -0,0 +1,22 @@
import {assert, expect} from 'chai';
import merge from 'deepmerge';
import {validate} from 'uuid';
import Image from '../src/image';
Image.root = 'test/fixtures';
describe('@avocado/graphics', () => {
describe('Image', () => {
it("has a sane default", async () => {
const image = new Image();
expect(image.height).to.equal(0);
expect(image.width).to.equal(0);
});
it("can load images", async () => {
const image = await Image.load('/test.png');
expect(image.height).to.equal(16);
expect(image.width).to.equal(16);
});
});
});

View File

@ -3,3 +3,4 @@
!/.*
!/webpack.config.js
!src/**/*.js
!/test/**/*.js

View File

@ -0,0 +1,9 @@
import {expect} from 'chai';
const {name} = require('../package.json');
describe(name, () => {
it('exists', () => {
expect(true).to.equal(true);
})
});

View File

@ -3,3 +3,4 @@
!/.*
!/webpack.config.js
!src/**/*.js
!/test/**/*.js

View File

@ -166,8 +166,8 @@ export function normalize(v) {
const normalized = scale(v, 1 / Math.sqrt(dp));
// Don't let NaN poison our equations.
return [
Math.isNan(normalized[0]) ? 0 : normalized[0],
Math.isNan(normalized[1]) ? 0 : normalized[1],
Number.isNaN(normalized[0]) ? 0 : normalized[0],
Number.isNaN(normalized[1]) ? 0 : normalized[1],
];
}
@ -261,11 +261,11 @@ export function toDirection4(vector) {
return vector[1] > 0 ? 2 : 0;
}
export function toAngle(v) {
return (Math.atan2(-v[1], v[0]) + Math.PI * 2) % (Math.PI * 2);
export function toRadians(v) {
return (Math.atan2(v[1], v[0]) + Math.PI * 2) % (Math.PI * 2);
}
export function fromAngle(angle) {
export function fromRadians(angle) {
return [Math.cos(angle), -Math.sin(angle)];
}
@ -289,13 +289,16 @@ export function toDirection8(v) {
// eslint-disable-next-line no-param-reassign
v = normalize(v);
// Orient radians.
let rad = (TWO_PI + toAngle(v)) % TWO_PI;
rad = (rad + HALF_PI) % TWO_PI;
let rad = toRadians(v);
if (0 === rad) {
return 1;
}
rad = (TWO_PI + rad) % TWO_PI;
// Truncate.
rad = Math.floor(rad * 100000) / 100000;
let stepStart = TWO_PI - EIGHTH_PI;
// Clockwise direction map.
const directions = [0, 4, 1, 5, 2, 6, 3, 7];
const directions = [1, 5, 2, 6, 3, 7, 0, 4];
for (let i = 0; i < directions.length; i++) {
const direction = directions[i];
let stepEnd = (stepStart + QUARTER_PI) % TWO_PI;

View File

@ -0,0 +1,9 @@
import {expect} from 'chai';
const {name} = require('../package.json');
describe(name, () => {
it('exists', () => {
expect(true).to.equal(true);
})
});

View File

@ -0,0 +1,39 @@
// describe('@avocado/math', () => {
// describe('Vector.Mixin', () => {
// var O, spy;
// O = null;
// spy = null;
// beforeEach(() => {
// O = new ((function(_super) {
// __extends(_Class, _super);
// function _Class() {
// return _Class.__super__.constructor.apply(this, arguments);
// }
// return _Class;
// })(MixinOf(EventEmitter, Vector.Mixin())));
// return spy = jasmine.createSpy('listener');
// });
// it('can detect changes', () => {
// O.on('xChanged', spy);
// O.on('yChanged', spy);
// O.on('vectorChanged', spy);
// O.setVector([20, 20]);
// return expect(spy.callCount).toEqual(3);
// });
// return it('can detect changes in the correct order', () => {
// var accum;
// accum = 300;
// O.on('xChanged yChanged', () => {
// return accum /= 10;
// });
// O.on('vectorChanged', () => {
// return accum += 200;
// });
// O.setVector([20, 20]);
// return expect(accum).toEqual(203);
// });
// });
// });

View File

@ -1,36 +0,0 @@
import {EventEmitter, MixinOf} from '@avocado/composition'
import {Vector} from '@avocado/math'
describe 'Vector.Mixin', ->
O = null
spy = null
beforeEach ->
O = new class extends MixinOf(
EventEmitter
Vector.Mixin()
)
spy = jasmine.createSpy 'listener'
it 'can detect changes', ->
O.on 'xChanged', spy
O.on 'yChanged', spy
O.on 'vectorChanged', spy
O.setVector [20, 20]
expect(spy.callCount).toEqual 3
it 'can detect changes in the correct order', ->
accum = 300
O.on 'xChanged yChanged', -> accum /= 10
O.on 'vectorChanged', -> accum += 200
O.setVector [20, 20]
expect(accum).toEqual 203

View File

@ -0,0 +1,66 @@
import {expect} from 'chai';
import * as Rectangle from '../src/rectangle'
describe('@avocado/math', () => {
describe('Rectangle', function() {
it('can calculate intersections', function() {
expect(Rectangle.intersects([0, 0, 16, 16], [8, 8, 24, 24])).to.equal(true);
expect(Rectangle.intersects([0, 0, 16, 16], [16, 16, 32, 32])).to.equal(false);
expect(Rectangle.isTouching([0, 0, 16, 16], [0, 0])).to.equal(true);
expect(Rectangle.isTouching([0, 0, 16, 16], [16, 16])).to.equal(false);
expect(Rectangle.intersection([0, 0, 16, 16], [8, 8, 24, 24])).to.deep.equal([8, 8, 8, 8]);
expect(Rectangle.united([0, 0, 4, 4], [4, 4, 8, 8])).to.deep.equal([0, 0, 12, 12]);
});
it('can compose and decompose', function() {
var rectangle;
rectangle = Rectangle.compose([0, 0], [16, 16]);
expect(Rectangle.equals(rectangle, [0, 0, 16, 16])).to.equal(true);
expect(Rectangle.position(rectangle)).to.deep.equal([0, 0]);
expect(Rectangle.size(rectangle)).to.deep.equal([16, 16]);
});
it('can make a deep copy', function() {
var rectangle, rectangle2;
rectangle = [0, 0, 16, 16];
rectangle2 = Rectangle.copy(rectangle);
expect(Rectangle.equals(rectangle, rectangle2)).to.equal(true);
rectangle[0] = 6;
expect(Rectangle.equals(rectangle, rectangle2)).to.equal(false);
});
it('can convert to an object', function() {
var rectangle;
rectangle = [0, 0, 16, 16];
expect(Rectangle.toObject(rectangle)).to.deep.equal({
x: 0,
y: 0,
width: 16,
height: 16
});
expect(Rectangle.toObject(rectangle, true)).to.deep.equal({
x: 0,
y: 0,
w: 16,
h: 16
});
});
it('can translate by vector', function() {
expect(Rectangle.translated([0, 0, 16, 16], [8, 8])).to.deep.equal([8, 8, 16, 16]);
});
it('can check for null', function() {
expect(Rectangle.isNull(null)).to.equal(true);
expect(Rectangle.isNull(3)).to.equal(true);
expect(Rectangle.isNull([1])).to.equal(true);
expect(Rectangle.isNull([1, 1])).to.equal(true);
expect(Rectangle.isNull([1, 1, 1])).to.equal(true);
expect(Rectangle.isNull([1, 1, 1, 1, 1])).to.equal(true);
expect(Rectangle.isNull([0, 0, 1, 1])).to.equal(false);
expect(Rectangle.isNull([0, 0, 1, 0])).to.equal(true);
});
it('can round', function() {
expect(Rectangle.round([3.14, 4.70, 5.32, 1.8])).to.deep.equal([3, 5, 5, 2]);
});
it('can floor', function() {
expect(Rectangle.floor([3.14, 4.70, 5.32, 1.8])).to.deep.equal([3, 4, 5, 1]);
});
});
});

View File

@ -0,0 +1,101 @@
import {expect} from 'chai';
import {QUARTER_PI} from '../src/math';
import * as Vector from '../src/vector';
describe('@avocado/math', () => {
describe('Vector', () => {
it('can scale', () => {
expect(Vector.scale([.5, 1.5], 2)).to.deep.equal([1, 3]);
});
it('can add', () => {
expect(Vector.add([1, 2], [1, 1])).to.deep.equal([2, 3]);
});
it('can sub', () => {
expect(Vector.sub([9, 5], [5, 2])).to.deep.equal([4, 3]);
});
it('can mul', () => {
expect(Vector.mul([3, 5], [5, 5])).to.deep.equal([15, 25]);
});
it('can div', () => {
expect(Vector.div([15, 5], [5, 5])).to.deep.equal([3, 1]);
});
it('can mod', () => {
expect(Vector.mod([13, 6], [5, 5])).to.deep.equal([3, 1]);
});
it('can distance', () => {
expect(Vector.distance([0, 0], [1, 1])).to.equal(Math.sqrt(2));
});
it('can min', () => {
expect(Vector.min([-10, 10], [0, 0])).to.deep.equal([-10, 0]);
});
it('can max', () => {
expect(Vector.max([-10, 10], [0, 0])).to.deep.equal([0, 10]);
});
it('can clamp', () => {
expect(Vector.clamp([-10, 10], [0, 0], [5, 5])).to.deep.equal([0, 5]);
});
it('can round', () => {
expect(Vector.round([3.14, 4.70])).to.deep.equal([3, 5]);
});
it('can dot', () => {
expect(Vector.dot([2, 3], [4, 5])).to.equal(23);
});
it('can normalize', () => {
expect(Vector.normalize([.5, .7])).to.deep.equal([0.5812381937190965, 0.813733471206735]);
});
it('can abs', () => {
expect(Vector.abs([23, -5.20])).to.deep.equal([23, 5.20]);
});
it('can floor', () => {
expect(Vector.floor([3.14, 4.70])).to.deep.equal([3, 4]);
});
it('can area', () => {
expect(Vector.area([3, 6])).to.equal(18);
});
it('can deep copy', () => {
const vector = [0, 0];
const vector2 = Vector.copy(vector);
expect(Vector.equals(vector, vector2)).to.equal(true);
vector[0] = 1;
expect(Vector.equals(vector, vector2)).to.equal(false);
});
it('can test for 0', () => {
expect(Vector.isZero([0, 0])).to.equal(true);
expect(Vector.isZero([1, 0])).to.equal(false);
});
it('can test for NULL', () => {
expect(Vector.isNull([0, 1])).to.equal(true);
expect(Vector.isNull([1, 1])).to.equal(false);
expect(Vector.isNull(null)).to.equal(true);
expect(Vector.isNull([1])).to.equal(true);
expect(Vector.isNull([1, 1, 1])).to.equal(true);
});
it('can calculate angle', () => {
expect(Vector.toRadians([1, 0])).to.be.closeTo(0, 0.0001);
expect(Vector.toRadians([1, 1])).to.be.closeTo(1 * QUARTER_PI, 0.0001);
expect(Vector.toRadians([0, 1])).to.be.closeTo(2 * QUARTER_PI, 0.0001);
expect(Vector.toRadians([-1, 1])).to.be.closeTo(3 * QUARTER_PI, 0.0001);
expect(Vector.toRadians([-1, 0])).to.be.closeTo(4 * QUARTER_PI, 0.0001);
expect(Vector.toRadians([-1, -1])).to.be.closeTo(5 * QUARTER_PI, 0.0001);
expect(Vector.toRadians([0, -1])).to.be.closeTo(6 * QUARTER_PI, 0.0001);
expect(Vector.toRadians([1, -1])).to.be.closeTo(7 * QUARTER_PI, 0.0001);
});
it('can convert to/from directions', () => {
expect(Vector.toDirection4([0, 1])).to.equal(2);
expect(Vector.toDirection4([1, 0])).to.equal(1);
expect(Vector.toDirection8([1, 1])).to.equal(5);
expect(Vector.toDirection8([1, 0])).to.equal(1);
expect(Vector.toDirection([0, 1], 4)).to.equal(2);
for (let i = 0; i < 8; ++i) {
expect(i).to.equal(Vector.toDirection(Vector.fromDirection(i), 8));
}
});
it('can convert to object', () => {
expect(Vector.toObject([0, 16])).to.deep.equal({
x: 0,
y: 16
});
});
});
});

View File

@ -1,85 +0,0 @@
import {Vector} from '@avocado/math'
describe 'Vector', ->
it 'can do mathematical operations', ->
expect(Vector.scale [.5, 1.5], 2).toEqual [1, 3]
expect(Vector.add [1, 2], [1, 1]).toEqual [2, 3]
expect(Vector.sub [9, 5], [5, 2]).toEqual [4, 3]
expect(Vector.mul [3, 5], [5, 5]).toEqual [15, 25]
expect(Vector.div [15, 5], [5, 5]).toEqual [3, 1]
expect(Vector.mod [13, 6], [5, 5]).toEqual [3, 1]
expect(Vector.cartesianDistance [0, 0], [1, 1]).toBe Math.sqrt 2
expect(Vector.min [-10, 10], [0, 0]).toEqual [-10, 0]
expect(Vector.max [-10, 10], [0, 0]).toEqual [0, 10]
expect(Vector.clamp [-10, 10], [0, 0], [5, 5]).toEqual [0, 5]
expect(Vector.round [3.14, 4.70]).toEqual [3, 5]
expect(Vector.dot [2, 3], [4, 5]).toEqual 23
expect(Vector.normalize [5, 5], [6, 7]).toEqual [
-0.4472135954999579, -0.8944271909999159
]
expect(Vector.normalize [.5, .7]).toEqual [
0.5812381937190965, 0.813733471206735
]
expect(Vector.abs [23, -5.20]).toEqual [23, 5.20]
expect(Vector.floor [3.14, 4.70]).toEqual [3, 4]
expect(Vector.area [3, 6]).toBe 18
it 'can deep copy', ->
vector = [0, 0]
vector2 = Vector.copy vector
expect(Vector.equals vector, vector2).toBe true
vector[0] = 1
expect(Vector.equals vector, vector2).toBe false
it 'can test for 0 or NULL', ->
expect(Vector.isZero [0, 0]).toBe true
expect(Vector.isZero [1, 0]).toBe false
expect(Vector.isNull [0, 1]).toBe true
expect(Vector.isNull [1, 1]).toBe false
expect(Vector.isNull null).toBe true
expect(Vector.isNull [1]).toBe true
expect(Vector.isNull [1, 1, 1]).toBe true
it 'can convert to/from directions', ->
expect(Vector.toDirection4 [0, 1]).toBe 2
expect(Vector.toDirection4 [1, 0]).toBe 1
expect(Vector.toDirection8 [1, 1]).toBe 5
expect(Vector.toDirection8 [1, 0]).toBe 1
expect(Vector.toDirection [0, 1], 4).toBe 2
for i in [0...8]
vector = Vector.fromDirection i
expect(i).toBe Vector.toDirection vector, 8
it 'can convert to object', ->
expect(Vector.toObject [0, 16]).toEqual x: 0, y: 16

View File

@ -3,3 +3,4 @@
!/.*
!/webpack.config.js
!src/**/*.js
!/test/**/*.js

View File

@ -1,5 +1,5 @@
{
"name": "@avocado/package",
"name": "@avocado/physics",
"version": "1.0.0",
"main": "index.js",
"author": "cha0s",
@ -25,6 +25,7 @@
"@avocado/entity": "^2.0.0",
"@avocado/graphics": "^2.0.0",
"@avocado/math": "^2.0.0",
"@avocado/resource": "^2.0.0",
"@avocado/s13n": "^2.0.0",
"@avocado/timing": "^2.0.0",
"@avocado/traits": "^2.0.0",

View File

@ -1,5 +1,5 @@
import {PI_180} from '@avocado/math';
import {synchronized} from '@avocado/s13n';
import {resource} from '@avocado/resource';
import {Ticker} from '@avocado/timing';
import {Trait} from '@avocado/traits';
import {compose} from '@latus/core';
@ -135,7 +135,7 @@ export default (latus) => class Emitter extends decorate(Trait) {
}
emitParticleJson(json, emitter) {
const {fromName: {Entity}} = synchronized(latus);
const {fromResourceType: {Entity}} = resource(latus);
Entity.loadOrInstance(json).then((particle) => {
this.entity.emitParticleEntity(particle);
emitter.emit(particle);

View File

@ -0,0 +1,9 @@
import {expect} from 'chai';
const {name} = require('../package.json');
describe(name, () => {
it('exists', () => {
expect(true).to.equal(true);
})
});

View File

@ -82,7 +82,7 @@
d3-quadtree "^2.0.0"
debug "4.3.1"
"@avocado/resource@2.0.0":
"@avocado/resource@2.0.0", "@avocado/resource@^2.0.0":
version "2.0.0"
resolved "https://npm.i12e.cha0s.io/@avocado%2fresource/-/resource-2.0.0.tgz#6c30d8dd41ceffe32a51ff068c4f4a67bb0e9731"
integrity sha512-t6DrhjOa3RI5zZwZ9nsoNiLwBEoGAqN6/HMz/EwSJwj07Ak2I6lEk3OEyo1p1HYlwSTq1plAACiIvpwaNZmP5w==

View File

@ -3,3 +3,4 @@
!/.*
!/webpack.config.js
!src/**/*.js
!/test/**/*.js

View File

@ -1,2 +1,18 @@
export {default as Resource} from './resource';
import {gather} from '@latus/core';
export {default as Resource, resource} from './resource';
export {default as JsonResource} from './json-resource';
export default {
hooks: {
'@latus/core/starting': (latus) => {
// eslint-disable-next-line no-param-reassign
latus.config['%resources'] = gather(
latus,
'@avocado/resource/resources',
'resourceId',
'resourceType',
);
},
},
};

View File

@ -16,7 +16,7 @@ export default class JsonResource extends Resource {
return this.mergeJson(json);
}
static async load(json) {
static async load(json = {}) {
const {extends: uri} = json;
const resource = new this(await this.extendJson(json));
resource.uri = uri;

View File

@ -12,6 +12,8 @@ const decorate = compose(
Property('uri'),
);
export const resource = ({config}) => config['%resources'];
export default class Resource extends decorate(Class) {
constructor(...args) {

View File

@ -0,0 +1,9 @@
import {expect} from 'chai';
const {name} = require('../package.json');
describe(name, () => {
it('exists', () => {
expect(true).to.equal(true);
})
});

View File

@ -0,0 +1,74 @@
import {assert, expect} from 'chai';
import merge from 'deepmerge';
import {validate} from 'uuid';
import Resource from '../src/resource';
import JsonResource from '../src/json-resource';
Resource.root = 'test/fixtures';
JsonResource.root = 'test/fixtures';
class ResourceSubclass extends Resource {
constructor(buffer) {
super();
this.buffer = buffer;
}
}
class JsonResourceSubclass extends JsonResource {
constructor(json) {
super();
this.json = json;
}
}
const testJsonBuffer = Buffer.from([
0x7b, 0x0a, 0x20, 0x20, 0x22, 0x66, 0x6f, 0x6f,
0x22, 0x3a, 0x20, 0x22, 0x62, 0x61, 0x72, 0x22,
0x2c, 0x0a, 0x20, 0x20, 0x22, 0x62, 0x61, 0x7a,
0x22, 0x3a, 0x20, 0x36, 0x39, 0x0a, 0x7d, 0x0a,
]);
describe('@avocado/resource', () => {
describe('Resource', () => {
it("can read resources", async () => {
const buffer = await Resource.read('/test.json');
expect(Buffer.compare(testJsonBuffer, buffer)).to.equal(0);
});
it("generates unique uuids", async () => {
const resources = [
new Resource(),
new Resource(),
];
expect(resources[0].instanceUuid).to.not.equal(resources[1].instanceUuid);
assert(validate(resources[0].instanceUuid));
assert(validate(resources[1].instanceUuid));
});
it("can be subclassed", async () => {
const resource = await ResourceSubclass.load('/test.json');
expect(Buffer.compare(testJsonBuffer, resource.buffer)).to.equal(0);
});
});
describe('JsonResource', () => {
it("can read resources", async () => {
const json = await JsonResource.read('/test.json');
expect(json.foo).to.equal('bar');
expect(json.baz).to.equal(69);
});
it("can read extended resources", async () => {
const json = await JsonResource.read('/test2.json');
expect(json.foo).to.equal('baz');
expect(json.baz).to.equal(69);
});
it("can be subclassed", async () => {
const resource = await JsonResourceSubclass.load({extends: '/test.json'});
expect(resource.json.foo).to.equal('bar');
expect(resource.json.baz).to.equal(69);
});
it("can load flat", async () => {
const resource = await JsonResourceSubclass.load({foo: 420, bar: 'hoo'});
expect(resource.json.foo).to.equal(420);
expect(resource.json.bar).to.equal('hoo');
});
});
});

View File

@ -3,3 +3,4 @@
!/.*
!/webpack.config.js
!src/**/*.js
!/test/**/*.js

View File

@ -1,6 +1,7 @@
import SynchronizedCreatePacket from '../packets/synchronized-create';
import SynchronizedDestroyPacket from '../packets/synchronized-destroy';
import SynchronizedUpdatePacket from '../packets/synchronized-update';
import {synchronized} from '../synchronized';
export default class ClientSynchronizer {
@ -12,7 +13,7 @@ export default class ClientSynchronizer {
acceptPacket(packet) {
if ((packet instanceof SynchronizedCreatePacket)) {
const {id, type} = packet.data.synchronized;
const {default: Synchronized} = this.latus.config['%synchronized'].fromS13nType[type];
const {fromS13Type: {[type]: Synchronized}} = synchronized(this.latus);
if (!(type in this._synchronized)) {
this._synchronized[type] = {};
}
@ -36,7 +37,7 @@ export default class ClientSynchronizer {
}
addSynchronized(synchronized) {
const type = synchronized.constructor.s13nType;
const {s13nType: type} = synchronized.constructor;
if (!(type in this._synchronized)) {
this._synchronized[type] = {};
}

View File

@ -2,9 +2,7 @@ import {gather} from '@latus/core';
export {default as normalize} from './normalize';
export * from './packets';
export {default as Synchronized} from './synchronized';
export const synchronized = ({config}) => config['%synchronized'];
export {default as Synchronized, synchronized} from './synchronized';
export default {
hooks: {
@ -13,8 +11,8 @@ export default {
latus.config['%synchronized'] = gather(
latus,
'@avocado/s13n/synchronized',
'id',
'type',
's13nId',
's13nType',
);
},
},

View File

@ -12,7 +12,7 @@ export default class ServerSynchronizer {
return;
}
this._added.push(synchronized);
const type = synchronized.constructor.s13nType;
const {s13nType: type} = synchronized.constructor;
if (!(type in this._synchronized)) {
this._synchronized[type] = {};
}
@ -35,7 +35,7 @@ export default class ServerSynchronizer {
this._removed.push(synchronized);
const index = this._synchronizedFlat.indexOf(synchronized);
this._synchronizedFlat.splice(index, 1);
const type = synchronized.constructor.s13nType;
const {s13nType: type} = synchronized.constructor;
const id = synchronized.synchronizationId();
delete this._synchronized[type][id];
}

View File

@ -2,6 +2,8 @@ import normalize from './normalize';
import SynchronizedCreatePacket from './packets/synchronized-create';
import SynchronizedDestroyPacket from './packets/synchronized-destroy';
export const synchronized = ({config}) => config['%synchronized'];
export default function SynchronizedMixin(Superclass) {
return class Synchronized extends Superclass {
@ -20,7 +22,7 @@ export default function SynchronizedMixin(Superclass) {
return new SynchronizedCreatePacket({
synchronized: {
id,
type: this.constructor.s13nType,
type: this.constructor.type,
},
spec: this.toNetwork(informed),
});
@ -34,7 +36,7 @@ export default function SynchronizedMixin(Superclass) {
return new SynchronizedDestroyPacket({
synchronized: {
id,
type: this.constructor.s13nType,
type: this.constructor.type,
},
});
}
@ -63,7 +65,7 @@ export default function SynchronizedMixin(Superclass) {
for (let i = 0; i < packets.length; i++) {
packets[i].data.synchronized = {
id,
type: this.constructor.s13nType,
type: this.constructor.type,
};
}
if (this.packetsAreIdempotent()) {

View File

@ -0,0 +1,9 @@
import {expect} from 'chai';
const {name} = require('../package.json');
describe(name, () => {
it('exists', () => {
expect(true).to.equal(true);
})
});

View File

@ -3,3 +3,4 @@
!/.*
!/webpack.config.js
!src/**/*.js
!/test/**/*.js

View File

@ -1,5 +1,5 @@
{
"name": "@avocado/package",
"name": "@avocado/sound",
"version": "1.0.0",
"main": "index.js",
"author": "cha0s",

View File

@ -0,0 +1,9 @@
import {expect} from 'chai';
const {name} = require('../package.json');
describe(name, () => {
it('exists', () => {
expect(true).to.equal(true);
})
});

View File

@ -3,3 +3,4 @@
!/.*
!/webpack.config.js
!src/**/*.js
!/test/**/*.js

View File

@ -0,0 +1,9 @@
import {expect} from 'chai';
const {name} = require('../package.json');
describe(name, () => {
it('exists', () => {
expect(true).to.equal(true);
})
});

View File

@ -3,3 +3,4 @@
!/.*
!/webpack.config.js
!src/**/*.js
!/test/**/*.js

View File

@ -21,6 +21,7 @@
],
"dependencies": {
"@avocado/core": "2.0.0",
"@avocado/entity": "^2.0.0",
"@avocado/graphics": "2.0.0",
"@avocado/math": "2.0.0",
"@avocado/resource": "2.0.0",

View File

@ -1,8 +1,24 @@
import Layer from './layer';
import Room from './room';
import Tiles from './tiles';
import Tileset from './tileset';
export {default as Camera} from './camera';
export {default as Layer} from './layer';
export {default as LayerView} from './layer-view';
export {default as Room} from './room';
export {default as RoomView} from './room-view';
export {default as TilesRenderer} from './tiles-renderer';
export {default as Tiles} from './tiles';
export {default as Tileset} from './tileset';
export {
Layer,
};
export default {
hooks: {
'@avocado/resource/resources': (latus) => ({
Layer: Layer(latus),
Room: Room(latus),
Tiles: Tiles(latus),
Tileset: Tileset(latus),
}),
},
};

View File

@ -1,23 +1,19 @@
import {Property} from '@avocado/core';
import {
EntityList,
} from '@avocado/entity';
JsonResource,
resource,
} from '@avocado/resource';
import {
normalize,
SynchronizedCreatePacket,
SynchronizedDestroyPacket,
} from '@avocado/s13n';
import {Class, compose, EventEmitter} from '@latus/core';
import {compose, EventEmitter} from '@latus/core';
import Tiles from './tiles';
import Tileset from './tileset';
// import Tiles from './tiles';
const decorate = compose(
EventEmitter,
Property('tilesetUri', {
default: '',
track: true,
}),
Property('tileset', {
track: true,
}),
@ -26,30 +22,35 @@ const decorate = compose(
}),
);
export default class Layer extends decorate(Class) {
export default (latus) => class Layer extends decorate(JsonResource) {
constructor({entities, tiles, tilesetUri} = {}) {
constructor({
entityList,
tiles,
tileset,
world,
} = {}) {
super();
this.entityList = new EntityList(entities);
const {fromResourceType: {EntityList, Tiles, Tileset}} = resource(latus);
this.entityList = entityList || new EntityList();
this.index = -1;
this.tileEntities = {};
this.tileGeometry = [];
this.tiles = new Tiles(tiles);
this._tilesetUriChanged = false;
// Listeners.
this.entityList.on('entityAdded', this.onEntityAddedToLayer, this);
this.entityList.on('entityRemoved', this.onEntityRemovedFromLayer, this);
this.tiles.on('dataChanged', this.onTileDataChanged, this);
this.on('tilesetChanged', this.onTilesetChanged, this);
this.on('tilesetUriChanged', this.onTilesetUriChanged, this);
this.on('worldChanged', this.onWorldChanged, this);
this.tilesetUri = tilesetUri;
// this.on('tilesetChanged', this.onTilesetChanged, this);
// this.on('worldChanged', this.onWorldChanged, this);
this.tileset = tileset || new Tileset();
this.world = world;
}
acceptPacket(packet) {
if ('LayerUpdateTilesetUriPacket' === packet.constructor.name) {
this.tilesetUri = packet.data;
}
// if ('LayerUpdateTilesetUriPacket' === packet.constructor.name) {
// this.tilesetUri = packet.data;
// }
if ('EntityListUpdateEntityPacket' === packet.constructor.name) {
this.entityList.acceptPacket(packet);
}
@ -107,12 +108,7 @@ export default class Layer extends decorate(Class) {
// return true;
// }
allEntities() {
return Array.from(this.entityList);
}
cleanPackets() {
this._tilesetUriChanged = false;
this.entityList.cleanPackets();
this.tiles.cleanPackets();
}
@ -122,24 +118,39 @@ export default class Layer extends decorate(Class) {
this.entityList.off('entityAdded', this.onEntityAddedToLayer);
this.entityList.off('entityRemoved', this.onEntityRemovedFromLayer);
this.tiles.off('dataChanged', this.onTileDataChanged);
this.off('tilesetUriChanged', this.onTilesetUriChanged);
if (this.tileset) {
this.tileset.destroy();
}
}
get entities() {
return this.entityList._entities;
}
static async extendJson(json) {
const extended = await super.extendJson(json);
const {fromResourceType: {EntityList, Tileset}} = resource(latus);
if (extended.entities) {
extended.entityList = await EntityList.load(extended.entities);
}
if (extended.tilesetUri) {
extended.tileset = await Tileset.load(extended.tilesetUri);
}
return extended;
}
findEntity(uuid) {
return this.entityList.findEntity(uuid);
}
hasTileEntityWithUriAt(tilePosition, uri) {
const tileEntities = this.tileEntitiesAt(tilePosition);
if (0 === tileEntities.length) {
return false;
}
const entitiesWithUri = tileEntities.filter((entity) => entity.uri === uri);
return entitiesWithUri.length > 0;
}
// hasTileEntityWithUriAt(tilePosition, uri) {
// const tileEntities = this.tileEntitiesAt(tilePosition);
// if (0 === tileEntities.length) {
// return false;
// }
// const entitiesWithUri = tileEntities.filter((entity) => entity.uri === uri);
// return entitiesWithUri.length > 0;
// }
indexAt(position) {
return this.tiles.indexAt(position);
@ -159,52 +170,36 @@ export default class Layer extends decorate(Class) {
this.emit('tileDataChanged');
}
onTilesetChanged(oldTileset) {
let didChange = false;
if (oldTileset) {
this.removeTileGeometry();
didChange = true;
}
if (this.addTileGeometry()) {
didChange = true;
}
if (didChange) {
this.emit('tileGeometryChanged');
}
}
// onTilesetChanged(oldTileset) {
// let didChange = false;
// if (oldTileset) {
// this.removeTileGeometry();
// didChange = true;
// }
// if (this.addTileGeometry()) {
// didChange = true;
// }
// if (didChange) {
// this.emit('tileGeometryChanged');
// }
// }
onTilesetUriChanged() {
this._tilesetUriChanged = true;
if (!this.tilesetUri) {
return;
}
Tileset.load(this.tilesetUri).then((tileset) => {
this.tileset = tileset;
});
}
onWorldChanged(oldWorld) {
let didChange = false;
if (oldWorld) {
this.removeTileGeometry();
didChange = true;
}
if (this.addTileGeometry()) {
didChange = true;
}
if (didChange) {
this.emit('tileGeometryChanged');
}
}
// onWorldChanged(oldWorld) {
// let didChange = false;
// if (oldWorld) {
// this.removeTileGeometry();
// didChange = true;
// }
// if (this.addTileGeometry()) {
// didChange = true;
// }
// if (didChange) {
// this.emit('tileGeometryChanged');
// }
// }
packets(informed) {
const packets = [];
if (this._tilesetUriChanged) {
packets.push([
'LayerUpdateTilesetUriPacket',
this.tilesetUri,
]);
}
const entityListPackets = normalize(this.entityList.packets(informed));
for (let i = 0; i < entityListPackets.length; i++) {
packets.push(entityListPackets[i]);
@ -220,16 +215,16 @@ export default class Layer extends decorate(Class) {
this.entityList.removeEntity(entity);
}
removeTileEntity(entity, index) {
if (!this.tileEntities[index]) {
return;
}
const entityIndex = this.tileEntities[index].indexOf(entity);
if (-1 === entityIndex) {
return;
}
this.tileEntities[index].splice(entityIndex, 1);
}
// removeTileEntity(entity, index) {
// if (!this.tileEntities[index]) {
// return;
// }
// const entityIndex = this.tileEntities[index].indexOf(entity);
// if (-1 === entityIndex) {
// return;
// }
// this.tileEntities[index].splice(entityIndex, 1);
// }
setTileAt(position, tile) {
this.tiles.setTileAt(position, tile);
@ -243,26 +238,18 @@ export default class Layer extends decorate(Class) {
return this.tiles.tileAt(position);
}
tileEntitiesAt(tilePosition) {
const index = this.indexAt(tilePosition);
if (!this.tileEntities[index]) {
return [];
}
return this.tileEntities[index];
}
toJSON() {
return {
entities: this.entityList.toJSON(),
tilesetUri: this.tilesetUri,
tiles: this.tiles.toJSON(),
};
}
// tileEntitiesAt(tilePosition) {
// const index = this.indexAt(tilePosition);
// if (!this.tileEntities[index]) {
// return [];
// }
// return this.tileEntities[index];
// }
toNetwork(informed) {
return {
entities: this.entityList.toNetwork(informed),
tilesetUri: this.tilesetUri,
tilesetUri: this.tileset.uri,
tiles: this.tiles.toNetwork(informed),
};
}
@ -271,8 +258,8 @@ export default class Layer extends decorate(Class) {
return this.entityList.visibleEntities(query);
}
visibleEntitiesWithUri(query, uri) {
return this.entityList.visibleEntitiesWithUri(query, uri);
}
// visibleEntitiesWithUri(query, uri) {
// return this.entityList.visibleEntitiesWithUri(query, uri);
// }
}
};

View File

@ -1,19 +1,21 @@
import {Class, compose, EventEmitter} from '@latus/core';
import {compose, EventEmitter} from '@latus/core';
import {
JsonResource,
resource,
} from '@avocado/resource';
import {normalize} from '@avocado/s13n';
import Layer from './layer';
const decorate = compose(
EventEmitter,
);
export default class Layers extends decorate(Class) {
export default (latus) => class Layers extends decorate(JsonResource) {
constructor(layers) {
constructor({layers = []}) {
super();
this.layers = [];
for (let i = 0; i < layers.length; i++) {
this.addLayer(new Layer(layers[i]));
this.addLayer(layers[i]);
}
}
@ -70,6 +72,15 @@ export default class Layers extends decorate(Class) {
}
}
static async extendJson(json) {
const extended = await super.extendJson(json);
if (extended.layers) {
const {fromResourceType: {Layer}} = resource(latus);
extended.layers = await Promise.all(extended.layers.map((layer) => Layer.load(layer)));
}
return extended;
}
findEntity(uuid) {
for (let i = 0; i < this.layers.length; i++) {
const foundEntity = this.layers[i].findEntity(uuid);
@ -163,7 +174,8 @@ export default class Layers extends decorate(Class) {
for (let index = 0; index < this.layers.length; index++) {
const layer = this.layers[index];
layer.world = world;
for (const entity of layer.entityList) {
for (let i = 0; i < layer.entityList._entities.length; i++) {
const entity = layer.entityList._entities[i];
if (entity.is('physical')) {
entity.world = world;
}
@ -171,4 +183,4 @@ export default class Layers extends decorate(Class) {
}
}
}
};

View File

@ -1,6 +1,6 @@
import {Container} from '@avocado/graphics';
import {LayersView} from './layers-view';
import LayersView from './layers-view';
export default class RoomView extends Container {

View File

@ -1,11 +1,14 @@
import {Property} from '@avocado/core';
import {Vector} from '@avocado/math';
// import {RectangleShape} from '@avocado/physics';
import {Resource} from '@avocado/resource';
import {
JsonResource,
resource,
} from '@avocado/resource';
import {normalize, Synchronized} from '@avocado/s13n';
import {compose, EventEmitter} from '@latus/core';
import Layers from './layers';
// import Layers from './layers';
// const ROOM_BOUND_SIZE = 64;
// const HALF_ROOM_BOUND_SIZE = ROOM_BOUND_SIZE / 2;
@ -24,14 +27,14 @@ const decorate = compose(
let synchronizationId = 1;
export default class Room extends decorate(Resource) {
export default (latus) => class Room extends decorate(JsonResource) {
constructor({layers, size} = {}) {
super();
this.bounds = [];
this._sizeChanged = false;
this._synchronizationId = synchronizationId++;
this.layers = new Layers(layers);
this.layers = layers;
// Listeners.
this.layers.on('entityAdded', this.onEntityAddedToRoom, this);
this.layers.on('entityRemoved', this.onEntityRemovedFromRoom, this);
@ -83,6 +86,15 @@ export default class Room extends decorate(Resource) {
this.off('worldChanged', this.onWorldChanged);
}
static async extendJson(json) {
const extended = await super.extendJson(json);
if (extended.layers) {
const {fromResourceType: {Layers}} = resource(latus);
extended.layers = await Layers.load(extended.layers);
}
return extended;
}
findEntity(uuid) {
return this.layers.findEntity(uuid);
}
@ -218,4 +230,4 @@ export default class Room extends decorate(Resource) {
return this.layers.visibleEntities(query);
}
}
};

View File

@ -8,7 +8,7 @@ const decorate = compose(
}),
);
export default class Tiles extends decorate(Class) {
export default () => class Tiles extends decorate(Class) {
constructor({data, size} = {}) {
super();
@ -108,13 +108,6 @@ export default class Tiles extends decorate(Class) {
return index < 0 || index >= this.data.length ? undefined : this.data[index];
}
toJSON() {
return {
size: this.size,
data: this.data,
};
}
toNetwork() {
return {
size: this.size,
@ -122,4 +115,4 @@ export default class Tiles extends decorate(Class) {
};
}
}
};

View File

@ -14,7 +14,7 @@ const decorate = compose(
}),
);
export default class Tileset extends decorate(JsonResource) {
export default () => class Tileset extends decorate(JsonResource) {
constructor({image, tileSize} = {}) {
super();
@ -86,4 +86,4 @@ export default class Tileset extends decorate(JsonResource) {
this.recalculateSubimages(this.image);
}
}
};

View File

@ -1,4 +1,4 @@
import {Trait} from '@avocado/entity';
import {Trait} from '@avocado/traits';
import Camera from '../camera';

View File

@ -1,5 +1,5 @@
import {Trait} from '@avocado/entity';
import {Vector} from '@avocado/math';
import {Trait} from '@avocado/traits';
export default class Layered extends Trait {

View File

@ -1,4 +1,4 @@
import {Trait} from '@avocado/entity';
import {Trait} from '@avocado/traits';
export default class Roomed extends Trait {

View File

@ -0,0 +1,9 @@
import {expect} from 'chai';
const {name} = require('../package.json');
describe(name, () => {
it('exists', () => {
expect(true).to.equal(true);
})
});

View File

@ -0,0 +1,49 @@
import {resource} from '@avocado/resource';
import {Latus} from '@latus/core';
import {expect} from 'chai';
const {name} = require('../package.json');
describe(name, () => {
describe('Layer', () => {
let latus;
beforeEach(async () => {
latus = Latus.mock([
'@avocado/entity',
'@avocado/resource',
['@avocado/topdown', `${__dirname}/../src`],
'@avocado/traits',
]);
await Promise.all(latus.invokeFlat('@latus/core/starting'));
});
it('has sane defaults', async () => {
const {fromResourceType: {Layer}} = resource(latus);
const layer = new Layer();
expect(Object.keys(layer.entities).length).to.equal(0);
expect(layer.tiles.size).to.deep.equal([0, 0]);
});
describe('entity list interaction', () => {
let entity;
let entityList;
let layer;
beforeEach(() => {
const {fromResourceType: {Entity, EntityList, Layer}} = resource(latus);
entity = new Entity({
traits: {
existent: {},
listed: {},
},
});
entityList = new EntityList();
entityList.addEntity(entity);
layer = new Layer({entityList});
})
it('delegates to entity list', async () => {
expect(Object.keys(layer.entities).length).to.equal(1);
});
it('can find entities', async () => {
expect(layer.findEntity(entity.instanceUuid)).to.equal(entity);
});
});
});
});

View File

@ -0,0 +1,54 @@
import {resource} from '@avocado/resource';
import {Latus} from '@latus/core';
import {expect} from 'chai';
describe('@avocado/topdown', () => {
describe('Tiles', () => {
let latus;
beforeEach(async () => {
latus = Latus.mock([
'@avocado/resource',
['@avocado/topdown', `${__dirname}/../src`],
]);
await Promise.all(latus.invokeFlat('@latus/core/starting'));
});
it("has sane defaults", async () => {
const {fromResourceType: {Tiles}} = resource(latus);
const tiles = new Tiles();
expect(tiles.size).to.deep.equal([0, 0]);
expect(tiles.data.length).to.equal(0);
});
it("gets and sets tile data", async () => {
const {fromResourceType: {Tiles}} = resource(latus);
const tiles = new Tiles({data: [0, 1, 2, 3], size: [2, 2]});
expect(tiles.tileAt([0, 0])).to.equal(0);
expect(tiles.tileAt([1, 0])).to.equal(1);
expect(tiles.tileAt([0, 1])).to.equal(2);
expect(tiles.tileAt([1, 1])).to.equal(3);
tiles.setTileAt([0, 0], 69);
tiles.setTileAt([1, 0], 420);
tiles.setTileAt([0, 1], 311);
tiles.setTileAt([1, 1], 42);
expect(tiles.data).to.deep.equal([69, 420, 311, 42]);
});
it("can get a data slice", async () => {
const {fromResourceType: {Tiles}} = resource(latus);
const tiles = new Tiles(
{
data: [
0, 1, 2, 3,
4, 5, 6, 7,
8, 9, 10, 11,
12, 13, 14, 15,
],
size: [4, 4],
},
);
expect(tiles.slice([1, 1, 2, 2])).to.deep.equal([5, 6, 9, 10]);
expect(tiles.slice([1, 0, 3, 3])).to.deep.equal([1, 2, 3, 5, 6, 7, 9, 10, 11]);
expect(tiles.slice([-1, -1, 5, 5])).to.deep.equal([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]);
expect(tiles.slice([1, 1, 1, 1])).to.deep.equal([5]);
expect(tiles.slice([1, 1, 0, 0])).to.deep.equal([]);
});
});
});

View File

@ -0,0 +1,35 @@
import {Image} from '@avocado/graphics';
import {resource} from '@avocado/resource';
import {Latus} from '@latus/core';
import {expect} from 'chai';
Image.root = 'test/fixtures';
describe('@avocado/topdown', () => {
describe('Tileset', () => {
let latus;
beforeEach(async () => {
latus = Latus.mock([
'@avocado/resource',
['@avocado/topdown', `${__dirname}/../src`],
]);
await Promise.all(latus.invokeFlat('@latus/core/starting'));
const {fromResourceType: {Tileset}} = resource(latus);
Tileset.root = 'test/fixtures';
});
it("has sane defaults", async () => {
const {fromResourceType: {Tileset}} = resource(latus);
const tileset = new Tileset();
expect(tileset.tileSize).to.deep.equal([0, 0]);
expect(tileset.image).to.equal(undefined);
});
it("can load", async () => {
const {fromResourceType: {Tileset}} = resource(latus);
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]);
});
});
});

View File

@ -2,14 +2,45 @@
# yarn lockfile v1
"@avocado/behavior@2.0.0":
version "2.0.0"
resolved "https://npm.i12e.cha0s.io/@avocado%2fbehavior/-/behavior-2.0.0.tgz#d53f1ca12f1e31387eac35e3a49b1391aebf6494"
integrity sha512-qYUh6IJbbeUcb1iWBGJJuA8XBdpaIWXg4ELWLDEa10ppFtNIYpkhthbv15AZVNyFYaE7UkMX6dvUq2N6QF41AA==
dependencies:
"@avocado/core" "2.0.0"
"@avocado/traits" "^2.0.0"
"@latus/core" "2.0.0"
debug "4.3.1"
deepmerge "^4.2.2"
lodash.mapvalues "^4.6.0"
"@avocado/core@2.0.0", "@avocado/core@^2.0.0":
version "2.0.0"
resolved "https://npm.i12e.cha0s.io/@avocado%2fcore/-/core-2.0.0.tgz#ac7f912135c1cbb433b825c0fd1aa9a14b91ffed"
integrity sha512-PX6UVuhcmUaf2yzhTEd+dUle/Z3JRWJXm7NwGN63oL3w5QqUVOEzqUQNkKem2b+mlyJl56UnWzZJYYLixTOzvQ==
resolved "https://npm.i12e.cha0s.io/@avocado%2fcore/-/core-2.0.0.tgz#193488e95ae8d6dcae4125adb7a62ee127e370b0"
integrity sha512-rM7BzgsNvoHxadCU2nh4+1QyHJGDf3os3TWrjS1jCcR/YOIs7ZVvdcCqjx5jFECyL0CE8FEpPiFfD/JS0QIcCA==
dependencies:
debug "4.3.1"
"@avocado/graphics@2.0.0":
"@avocado/entity@^2.0.0":
version "2.0.0"
resolved "https://npm.i12e.cha0s.io/@avocado%2fentity/-/entity-2.0.0.tgz#beed06069926edffa5eccb256a3479438b6f6462"
integrity sha512-x6UAmKPggt2nLDCvBV/v4i8TRccRL+o4G2i5c6NBFL61VVHlPyYdpj+DKLjaL35ENcUk9ZN9sv0wmgxP60IEgA==
dependencies:
"@avocado/behavior" "2.0.0"
"@avocado/core" "2.0.0"
"@avocado/graphics" "^2.0.0"
"@avocado/math" "2.0.0"
"@avocado/resource" "2.0.0"
"@avocado/s13n" "2.0.0"
"@avocado/timing" "2.0.0"
"@avocado/traits" "^2.0.0"
"@latus/core" "2.0.0"
"@latus/socket" "2.0.0"
debug "4.3.1"
deepmerge "^4.2.2"
lodash.without "^4.4.0"
"@avocado/graphics@2.0.0", "@avocado/graphics@^2.0.0":
version "2.0.0"
resolved "https://npm.i12e.cha0s.io/@avocado%2fgraphics/-/graphics-2.0.0.tgz#cbb8907c96a6386fe98f7abe138a389e0267f7ee"
integrity sha512-vPHr/GBZZuGcRzlXVOuWMTLHfv+hlySsG0ItUJWbP8E6ZMruqG4smgC9QypqYYpvIB0Y1iN+xDJRAhmgF0r4/w==
@ -54,8 +85,8 @@
"@avocado/resource@2.0.0":
version "2.0.0"
resolved "https://npm.i12e.cha0s.io/@avocado%2fresource/-/resource-2.0.0.tgz#6c30d8dd41ceffe32a51ff068c4f4a67bb0e9731"
integrity sha512-t6DrhjOa3RI5zZwZ9nsoNiLwBEoGAqN6/HMz/EwSJwj07Ak2I6lEk3OEyo1p1HYlwSTq1plAACiIvpwaNZmP5w==
resolved "https://npm.i12e.cha0s.io/@avocado%2fresource/-/resource-2.0.0.tgz#3986f46c7313e208d71df3f3cd507ede32e6df6e"
integrity sha512-pRTc0Aw6SVvR3BXLdr4cn4t8YoQvMkSCcPqum6xnQpnhEZ8n8pfPw1ilZ3VAzB61OwdLJUWUfnIw7s34EZhTJA==
dependencies:
"@avocado/core" "2.0.0"
"@latus/core" "2.0.0"
@ -65,14 +96,28 @@
"@avocado/s13n@2.0.0", "@avocado/s13n@^2.0.0":
version "2.0.0"
resolved "https://npm.i12e.cha0s.io/@avocado%2fs13n/-/s13n-2.0.0.tgz#cf8207f7cfbf3d2aa316fe12bb6342b62e9d87aa"
integrity sha512-gUDT5GDW6GrnrGXD1DERMEVvpUKwlajZMluXkAxtkBnAR3Xws/SBEeaFdsRqY75tj+4wF6MmM34J6u1jRqyo1A==
resolved "https://npm.i12e.cha0s.io/@avocado%2fs13n/-/s13n-2.0.0.tgz#a32e92c197e2915b1b7049d1926bb6d6c14a8f35"
integrity sha512-GMl0CwYz4eZV3PnDlaCnd5CseKtwdza6zG5TWy2g+5xBi+OcOwjKRAqKI4gOiiaJcGYtxnXJvSajdE12fhZGmA==
dependencies:
"@latus/core" "2.0.0"
"@latus/socket" "2.0.0"
debug "4.3.1"
msgpack-lite "^0.1.26"
"@avocado/timing@2.0.0":
version "2.0.0"
resolved "https://npm.i12e.cha0s.io/@avocado%2ftiming/-/timing-2.0.0.tgz#8cc6e50449da6b8fa7473befc147b86b45540d5e"
integrity sha512-PK70bGLbbFTqy2ieibeaoEQQNRFZK1sqGlLfg/SafjEHXdX8Dzm3kxh6mgudOiPDtBwo1M8jl1IQi6aYJEGXIA==
dependencies:
"@avocado/core" "2.0.0"
"@avocado/graphics" "2.0.0"
"@avocado/math" "2.0.0"
"@avocado/resource" "2.0.0"
"@avocado/traits" "^2.0.0"
"@latus/core" "2.0.0"
"@latus/socket" "2.0.0"
debug "4.3.1"
"@avocado/traits@^2.0.0":
version "2.0.0"
resolved "https://npm.i12e.cha0s.io/@avocado%2ftraits/-/traits-2.0.0.tgz#4717919e101a4fa81469bc5efe7c5478bb293a4e"
@ -975,12 +1020,11 @@
"@latus/core@2.0.0", "@latus/core@^2.0.0":
version "2.0.0"
resolved "https://npm.i12e.cha0s.io/@latus%2fcore/-/core-2.0.0.tgz#4447b4bba265b684035be1dacefd6ba4913a47d4"
integrity sha512-OSsbO4wvVJFPZTMRDX5LAXOA+MQY8FOSK7PcTEJKeiRqOMeMIVkZc6q6KNlUIYivE+87rlM7S/CctHoA7uvQvg==
resolved "https://npm.i12e.cha0s.io/@latus%2fcore/-/core-2.0.0.tgz#74444a0a269246930dceb8d445b30873d7ed73a2"
integrity sha512-ob2Mbxd10yRqLRv0bNZkiZ9OWjQrHKBXxQsu++n42lWkM6A/TXudp955CemJbl5IMzBkCAG5spXiID0eIZb/9Q==
dependencies:
debug "4.3.1"
js-yaml "3.14.0"
lodash.capitalize "^4.2.1"
lodash.flatten "^4.4.0"
"@latus/http@2.0.0":
@ -1293,9 +1337,9 @@
integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
"@types/node@*":
version "14.14.17"
resolved "https://npm.i12e.cha0s.io/@types%2fnode/-/node-14.14.17.tgz#29fab92f3986c0e379968ad3c2043683d8020dbb"
integrity sha512-G0lD1/7qD60TJ/mZmhog76k7NcpLWkPVGgzkRy3CTlnFu4LUQh5v2Wa661z6vnXmD8EQrnALUyf0VRtrACYztw==
version "14.14.19"
resolved "https://npm.i12e.cha0s.io/@types%2fnode/-/node-14.14.19.tgz#5135176a8330b88ece4e9ab1fdcfc0a545b4bab4"
integrity sha512-4nhBPStMK04rruRVtVc6cDqhu7S9GZai0fpXgPXrFpcPX6Xul8xnrjSdGB4KPBVYG/R5+fXWdCM8qBoiULWGPQ==
"@types/source-list-map@*":
version "0.1.2"
@ -1543,6 +1587,16 @@ ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.4, ajv@^6.12.5:
json-schema-traverse "^0.4.1"
uri-js "^4.2.2"
ajv@^7.0.2:
version "7.0.3"
resolved "https://npm.i12e.cha0s.io/ajv/-/ajv-7.0.3.tgz#13ae747eff125cafb230ac504b2406cf371eece2"
integrity sha512-R50QRlXSxqXcQP5SvKUrw8VZeypvo12i2IX0EeR5PiZ7bEKeHWgzgo264LDadUsCU42lTJVhFikTqJwNeH34gQ==
dependencies:
fast-deep-equal "^3.1.1"
json-schema-traverse "^1.0.0"
require-from-string "^2.0.2"
uri-js "^4.2.2"
ansi-colors@4.1.1, ansi-colors@^4.1.1:
version "4.1.1"
resolved "https://npm.i12e.cha0s.io/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348"
@ -1929,7 +1983,7 @@ bonjour@^3.5.0:
multicast-dns "^6.0.1"
multicast-dns-service-types "^1.1.0"
boolbase@~1.0.0:
boolbase@^1.0.0, boolbase@~1.0.0:
version "1.0.0"
resolved "https://npm.i12e.cha0s.io/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24=
@ -2612,20 +2666,20 @@ css-loader@^3.6.0:
schema-utils "^2.7.0"
semver "^6.3.0"
css-select@^1.1.0:
version "1.2.0"
resolved "https://npm.i12e.cha0s.io/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858"
integrity sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=
css-select@^2.0.2:
version "2.1.0"
resolved "https://npm.i12e.cha0s.io/css-select/-/css-select-2.1.0.tgz#6a34653356635934a81baca68d0255432105dbef"
integrity sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==
dependencies:
boolbase "~1.0.0"
css-what "2.1"
domutils "1.5.1"
nth-check "~1.0.1"
boolbase "^1.0.0"
css-what "^3.2.1"
domutils "^1.7.0"
nth-check "^1.0.2"
css-what@2.1:
version "2.1.3"
resolved "https://npm.i12e.cha0s.io/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2"
integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==
css-what@^3.2.1:
version "3.4.2"
resolved "https://npm.i12e.cha0s.io/css-what/-/css-what-3.4.2.tgz#ea7026fcb01777edbde52124e21f327e7ae950e4"
integrity sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==
cssesc@^3.0.0:
version "3.0.0"
@ -2916,15 +2970,7 @@ domhandler@^2.3.0:
dependencies:
domelementtype "1"
domutils@1.5.1:
version "1.5.1"
resolved "https://npm.i12e.cha0s.io/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf"
integrity sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=
dependencies:
dom-serializer "0"
domelementtype "1"
domutils@^1.5.1:
domutils@^1.5.1, domutils@^1.7.0:
version "1.7.0"
resolved "https://npm.i12e.cha0s.io/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a"
integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==
@ -3305,9 +3351,9 @@ eslint-visitor-keys@^2.0.0:
integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==
eslint@^7:
version "7.16.0"
resolved "https://npm.i12e.cha0s.io/eslint/-/eslint-7.16.0.tgz#a761605bf9a7b32d24bb7cde59aeb0fd76f06092"
integrity sha512-iVWPS785RuDA4dWuhhgXTNrGxHHK3a8HLSMBgbbU59ruJDubUraXN8N5rn7kb8tG6sjg74eE0RA3YWT51eusEw==
version "7.17.0"
resolved "https://npm.i12e.cha0s.io/eslint/-/eslint-7.17.0.tgz#4ccda5bf12572ad3bf760e6f195886f50569adb0"
integrity sha512-zJk08MiBgwuGoxes5sSQhOtibZ75pz0J35XTRlZOk9xMffhpA9BTbQZxoXZzOl5zMbleShbGwtw+1kGferfFwQ==
dependencies:
"@babel/code-frame" "^7.0.0"
"@eslint/eslintrc" "^0.2.2"
@ -4115,7 +4161,7 @@ html-webpack-plugin@^4.5.0:
tapable "^1.1.3"
util.promisify "1.0.0"
htmlparser2@^3.3.0:
htmlparser2@^3.10.1:
version "3.10.1"
resolved "https://npm.i12e.cha0s.io/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f"
integrity sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==
@ -4165,9 +4211,9 @@ http-errors@~1.7.2:
toidentifier "1.0.0"
http-parser-js@>=0.5.1:
version "0.5.2"
resolved "https://npm.i12e.cha0s.io/http-parser-js/-/http-parser-js-0.5.2.tgz#da2e31d237b393aae72ace43882dd7e270a8ff77"
integrity sha512-opCO9ASqg5Wy2FNo7A0sxy71yGbbkJJXLdgMK04Tcypw9jr2MgWbyubb0+WdmDmGnFflO7fRbqbaihh/ENDlRQ==
version "0.5.3"
resolved "https://npm.i12e.cha0s.io/http-parser-js/-/http-parser-js-0.5.3.tgz#01d2709c79d41698bb01d4decc5e9da4e4a033d9"
integrity sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg==
http-proxy-middleware@0.19.1:
version "0.19.1"
@ -4645,6 +4691,11 @@ json-schema-traverse@^0.4.1:
resolved "https://npm.i12e.cha0s.io/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
json-schema-traverse@^1.0.0:
version "1.0.0"
resolved "https://npm.i12e.cha0s.io/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2"
integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==
json-stable-stringify-without-jsonify@^1.0.1:
version "1.0.1"
resolved "https://npm.i12e.cha0s.io/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
@ -4784,11 +4835,6 @@ locate-path@^6.0.0:
dependencies:
p-locate "^5.0.0"
lodash.capitalize@^4.2.1:
version "4.2.1"
resolved "https://npm.i12e.cha0s.io/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz#f826c9b4e2a8511d84e3aca29db05e1a4f3b72a9"
integrity sha1-+CbJtOKoUR2E46yinbBeGk87cqk=
lodash.clonedeep@^4.5.0:
version "4.5.0"
resolved "https://npm.i12e.cha0s.io/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
@ -4799,11 +4845,21 @@ lodash.flatten@^4.4.0:
resolved "https://npm.i12e.cha0s.io/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f"
integrity sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=
lodash.mapvalues@^4.6.0:
version "4.6.0"
resolved "https://npm.i12e.cha0s.io/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz#1bafa5005de9dd6f4f26668c30ca37230cc9689c"
integrity sha1-G6+lAF3p3W9PJmaMMMo3IwzJaJw=
lodash.omit@^4.5.0:
version "4.5.0"
resolved "https://npm.i12e.cha0s.io/lodash.omit/-/lodash.omit-4.5.0.tgz#6eb19ae5a1ee1dd9df0b969e66ce0b7fa30b5e60"
integrity sha1-brGa5aHuHdnfC5aeZs4Lf6MLXmA=
lodash.without@^4.4.0:
version "4.4.0"
resolved "https://npm.i12e.cha0s.io/lodash.without/-/lodash.without-4.4.0.tgz#3cd4574a00b67bae373a94b748772640507b7aac"
integrity sha1-PNRXSgC2e643OpS3SHcmQFB7eqw=
lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20:
version "4.17.20"
resolved "https://npm.i12e.cha0s.io/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
@ -4975,22 +5031,17 @@ miller-rabin@^4.0.0:
bn.js "^4.0.0"
brorand "^1.0.1"
mime-db@1.44.0:
version "1.44.0"
resolved "https://npm.i12e.cha0s.io/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92"
integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==
"mime-db@>= 1.43.0 < 2":
mime-db@1.45.0, "mime-db@>= 1.43.0 < 2":
version "1.45.0"
resolved "https://npm.i12e.cha0s.io/mime-db/-/mime-db-1.45.0.tgz#cceeda21ccd7c3a745eba2decd55d4b73e7879ea"
integrity sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w==
mime-types@^2.1.27, mime-types@~2.1.17, mime-types@~2.1.24:
version "2.1.27"
resolved "https://npm.i12e.cha0s.io/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f"
integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==
version "2.1.28"
resolved "https://npm.i12e.cha0s.io/mime-types/-/mime-types-2.1.28.tgz#1160c4757eab2c5363888e005273ecf79d2a0ecd"
integrity sha512-0TO2yJ5YHYr7M2zzT7gDU1tbwHxEUWBCLt0lscSNpcdAfFyJOVEpRYNS7EXVcTLNj/25QO8gulHC5JtTzSE2UQ==
dependencies:
mime-db "1.44.0"
mime-db "1.45.0"
mime@1.6.0:
version "1.6.0"
@ -5321,7 +5372,7 @@ npm-run-path@^2.0.0:
dependencies:
path-key "^2.0.0"
nth-check@~1.0.1:
nth-check@^1.0.2:
version "1.0.2"
resolved "https://npm.i12e.cha0s.io/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c"
integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==
@ -6220,13 +6271,13 @@ remove-trailing-separator@^1.0.1:
integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8=
renderkid@^2.0.4:
version "2.0.4"
resolved "https://npm.i12e.cha0s.io/renderkid/-/renderkid-2.0.4.tgz#d325e532afb28d3f8796ffee306be8ffd6fc864c"
integrity sha512-K2eXrSOJdq+HuKzlcjOlGoOarUu5SDguDEhE7+Ah4zuOWL40j8A/oHvLlLob9PSTNvVnBd+/q0Er1QfpEuem5g==
version "2.0.5"
resolved "https://npm.i12e.cha0s.io/renderkid/-/renderkid-2.0.5.tgz#483b1ac59c6601ab30a7a596a5965cabccfdd0a5"
integrity sha512-ccqoLg+HLOHq1vdfYNm4TBeaCDIi1FLt3wGojTDSvdewUv65oTmI3cnT2E4hRjl1gzKZIPK+KZrXzlUYKnR+vQ==
dependencies:
css-select "^1.1.0"
css-select "^2.0.2"
dom-converter "^0.2"
htmlparser2 "^3.3.0"
htmlparser2 "^3.10.1"
lodash "^4.17.20"
strip-ansi "^3.0.0"
@ -6245,6 +6296,11 @@ require-directory@^2.1.1:
resolved "https://npm.i12e.cha0s.io/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I=
require-from-string@^2.0.2:
version "2.0.2"
resolved "https://npm.i12e.cha0s.io/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==
require-main-filename@^2.0.0:
version "2.0.0"
resolved "https://npm.i12e.cha0s.io/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
@ -6983,11 +7039,11 @@ supports-color@^6.1.0:
has-flag "^3.0.0"
table@^6.0.4:
version "6.0.4"
resolved "https://npm.i12e.cha0s.io/table/-/table-6.0.4.tgz#c523dd182177e926c723eb20e1b341238188aa0d"
integrity sha512-sBT4xRLdALd+NFBvwOz8bw4b15htyythha+q+DVZqy2RS08PPC8O2sZFgJYEY7bJvbCFKccs+WIZ/cd+xxTWCw==
version "6.0.6"
resolved "https://npm.i12e.cha0s.io/table/-/table-6.0.6.tgz#e9223f1e851213e2e43ab584b0fec33fb09a8e7a"
integrity sha512-OInCtPmDNieVBkVFi6C8RwU2S2H0h8mF3e3TQK4nreaUNCpooQUkI+A/KuEkm5FawfhWIfNqG+qfelVVR+V00g==
dependencies:
ajv "^6.12.4"
ajv "^7.0.2"
lodash "^4.17.20"
slice-ansi "^4.0.0"
string-width "^4.2.0"

View File

@ -3,3 +3,4 @@
!/.*
!/webpack.config.js
!src/**/*.js
!/test/**/*.js

View File

@ -21,6 +21,7 @@
],
"dependencies": {
"@avocado/core": "^2.0.0",
"@avocado/resource": "^2.0.0",
"@avocado/s13n": "^2.0.0",
"@latus/core": "^2.0.0",
"debug": "4.3.1"

View File

@ -1,6 +1,18 @@
export {default as Trait} from './trait';
import {gather} from '@latus/core';
export {default as Trait, traits} from './trait';
export {default as StateProperty} from './state-property';
export const traitFromId = (latus) => latus.config['%traits'].fromId;
export const traitFromType = (latus) => latus.config['%traits'].fromType;
export default {
hooks: {
'@latus/core/starting': (latus) => {
// eslint-disable-next-line no-param-reassign
latus.config['%traits'] = gather(
latus,
'@avocado/traits',
'id',
'type',
);
},
},
};

View File

@ -1,13 +1,16 @@
import {JsonResource} from '@avocado/resource';
import {Synchronized} from '@avocado/s13n';
import {compose, Class} from '@latus/core';
import {compose} from '@latus/core';
const decorate = compose(
Synchronized,
);
export default class Trait extends decorate(Class) {
export const traits = (latus) => latus.config['%traits'];
constructor(entity, params, state) {
export default class Trait extends decorate(JsonResource) {
constructor({entity, params, state}) {
super();
this.entity = entity;
const ctor = this.constructor;

View File

@ -0,0 +1,9 @@
import {expect} from 'chai';
const {name} = require('../package.json');
describe(name, () => {
it('exists', () => {
expect(true).to.equal(true);
})
});

View File

@ -2,13 +2,24 @@
# yarn lockfile v1
"@avocado/core@^2.0.0":
"@avocado/core@2.0.0", "@avocado/core@^2.0.0":
version "2.0.0"
resolved "http://172.18.0.8:4873/@avocado%2fcore/-/core-2.0.0.tgz#636ea3c3b54a38538c59485080f6a0e48f1798e7"
integrity sha512-VW+ygRHaQQwaL5rKZGm0n0DNfvj+H89qQx+67veCUmUuRav3XAeE0iYs8Lgfc3CJLPz/alqt/dVPMXd5QDR+Mg==
dependencies:
debug "4.3.1"
"@avocado/resource@^2.0.0":
version "2.0.0"
resolved "https://npm.i12e.cha0s.io/@avocado%2fresource/-/resource-2.0.0.tgz#3986f46c7313e208d71df3f3cd507ede32e6df6e"
integrity sha512-pRTc0Aw6SVvR3BXLdr4cn4t8YoQvMkSCcPqum6xnQpnhEZ8n8pfPw1ilZ3VAzB61OwdLJUWUfnIw7s34EZhTJA==
dependencies:
"@avocado/core" "2.0.0"
"@latus/core" "2.0.0"
debug "4.3.1"
deepmerge "^4.2.2"
uuid "^8.3.2"
"@avocado/s13n@^2.0.0":
version "2.0.0"
resolved "http://172.18.0.8:4873/@avocado%2fs13n/-/s13n-2.0.0.tgz#bfa5a84dba68da9de80060b5fca937e22dc8efa6"
@ -2549,6 +2560,11 @@ deepmerge@^2.2.1:
resolved "http://172.18.0.8:4873/deepmerge/-/deepmerge-2.2.1.tgz#5d3ff22a01c00f645405a2fbc17d0778a1801170"
integrity sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==
deepmerge@^4.2.2:
version "4.2.2"
resolved "https://npm.i12e.cha0s.io/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955"
integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==
default-gateway@^4.2.0:
version "4.2.0"
resolved "http://172.18.0.8:4873/default-gateway/-/default-gateway-4.2.0.tgz#167104c7500c2115f6dd69b0a536bb8ed720552b"
@ -7117,6 +7133,11 @@ uuid@^3.3.2, uuid@^3.4.0:
resolved "http://172.18.0.8:4873/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
uuid@^8.3.2:
version "8.3.2"
resolved "https://npm.i12e.cha0s.io/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
v8-compile-cache@^2.0.3, v8-compile-cache@^2.1.1:
version "2.2.0"
resolved "http://172.18.0.8:4873/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz#9471efa3ef9128d2f7c6a7ca39c4dd6b5055b132"