diff --git a/packages/behavior/context/context.js b/packages/behavior/context/context.js index 9bf4b73..32e3c52 100644 --- a/packages/behavior/context/context.js +++ b/packages/behavior/context/context.js @@ -8,10 +8,10 @@ const debug = D('@avocado:behavior:context'); export class Context { constructor(defaults = {}) { + this.typeMap = new Map(); + this.variableMap = new Map(); this.initialize(); - for (const key in defaults) { - this.add(key, defaults[key]); - } + this.addObjectMap(defaults); } static describe(description, fn) { @@ -21,34 +21,41 @@ export class Context { } static globals() { - if (!Context._globals) { - Context._globals = {}; - const globalss = invokeHookFlat('behaviorContextGlobals'); - for (let i = 0; i < globalss.length; i++) { - const globals = globalss[i]; - for (const key in globals) { - Context._globals[key] = globals[key]; - } - } - } - return Context._globals; + return invokeHookFlat('behaviorContextGlobals') + .reduce((r, globals) => ({...r, ...globals}), {}); } - add(key, value) { - this.map.set(key, value); + static typeDescription(type, variable) { + const types = invokeHookFlat('behaviorContextTypes') + .reduce((r, globals) => ({...r, ...globals}), {}); + if (!types[type]) { + return {}; + } + if ('function' === typeof types[type]) { + return types[type](variable); + } + return types[type]; + } + + add(key, value, type) { + this.typeMap.set(key, type); + this.variableMap.set(key, value, type); + } + + addObjectMap(map) { + Object.entries(map).forEach(([key, [variable, type]]) => this.add(key, variable, type)); } all() { - return objectFromEntries(Array.from(this.map.entries())); + return Array.from(this.variableMap.keys()) + .reduce((r, key) => ({...r, [key]: this.get(key)}), {}); } initialize() { - this.map = new Map(); - this.add('context', this); - const globals = this.constructor.globals(); - for (const key in globals) { - this.add(key, globals[key]); - } + this.typeMap.clear() + this.variableMap.clear() + this.add('context', this, 'context'); + this.addObjectMap(this.constructor.globals()); } clear() { @@ -56,7 +63,12 @@ export class Context { } destroy() { - this.map.clear(); + this.typeMap.clear() + this.variableMap.clear(); + } + + get(key) { + return [this.variableMap.get(key), this.typeMap.get(key)]; } static renderSteps(steps) { @@ -126,7 +138,7 @@ export class Context { previousNode = node; return result; }); - }, this.map.get(first.key)); + }, this.variableMap.get(first.key)); } traverseOneStep(steps, previousNode, node, step) { diff --git a/packages/behavior/context/flow.hooks.js b/packages/behavior/context/flow.hooks.js index acd38fb..dd526d8 100644 --- a/packages/behavior/context/flow.hooks.js +++ b/packages/behavior/context/flow.hooks.js @@ -8,44 +8,6 @@ class Flow { } }; - static contextDescription() { - return { - type: 'object', - children: { - conditional: { - type: 'ticking-promise', - label: 'If $1 then run $2.', - args: [ - ['condition', { - type: 'condition', - }], - ['actions', { - type: 'actions', - }], - ], - }, - parallel: { - type: 'ticking-promise', - label: 'Run $1 in parallel.', - args: [ - ['actions', { - type: 'actions', - }], - ], - }, - serial: { - type: 'ticking-promise', - label: 'Run $1 serially.', - args: [ - ['actions', { - type: 'actions', - }], - ], - }, - } - }; - } - static parallel(actions, context) { return actions.parallel(context); } @@ -58,6 +20,44 @@ class Flow { export function behaviorContextGlobals() { return { - Flow, + Flow: [Flow, 'Flow'], }; } + +export function behaviorContextTypes() { + return { + Flow: { + conditional: { + type: 'ticking-promise', + label: 'If $1 then run $2.', + args: [ + ['condition', { + type: 'condition', + }], + ['actions', { + type: 'actions', + }], + ], + }, + parallel: { + type: 'ticking-promise', + label: 'Run $1 in parallel.', + args: [ + ['actions', { + type: 'actions', + }], + ], + }, + serial: { + type: 'ticking-promise', + label: 'Run $1 serially.', + args: [ + ['actions', { + type: 'actions', + }], + ], + }, + }, + }; +} + diff --git a/packages/behavior/context/timing.hooks.js b/packages/behavior/context/timing.hooks.js index 572a2ff..7a9bb1f 100644 --- a/packages/behavior/context/timing.hooks.js +++ b/packages/behavior/context/timing.hooks.js @@ -2,23 +2,6 @@ import {TickingPromise} from '@avocado/core'; class Timing { - static contextDescription() { - return { - type: 'object', - children: { - wait: { - type: 'ticking-promise', - label: 'Wait for $1 seconds.', - args: [ - ['duration', { - type: 'number', - }], - ], - }, - }, - }; - } - static wait (duration) { return new TickingPromise( () => {}, @@ -33,8 +16,24 @@ class Timing { } -export function behaviorContextGlobals() { +export function behaviorContextTypes() { return { - Timing, + Timing: { + wait: { + type: 'ticking-promise', + label: 'Wait for $1 seconds.', + args: [ + ['duration', { + type: 'number', + }], + ], + }, + }, + }; +} + +export function behaviorContextGlobals() { + return { + Timing: [Timing, 'Timing'], }; } diff --git a/packages/behavior/context/utility.hooks.js b/packages/behavior/context/utility.hooks.js index 773ef14..864bab0 100644 --- a/packages/behavior/context/utility.hooks.js +++ b/packages/behavior/context/utility.hooks.js @@ -34,6 +34,6 @@ class Utility { export function behaviorContextGlobals() { return { - Utility, + Utility: [Utility, 'Utility'], }; } diff --git a/packages/behavior/traits/behaved.trait.js b/packages/behavior/traits/behaved.trait.js index be04d86..dfe38e2 100644 --- a/packages/behavior/traits/behaved.trait.js +++ b/packages/behavior/traits/behaved.trait.js @@ -55,7 +55,7 @@ export default class Behaved extends decorate(Trait) { constructor(entity, params, state) { super(entity, params, state); this._context = new Context({ - entity: this.entity, + entity: [this.entity, 'entity'], }); this._currentRoutine = undefined; this._routines = (new Routines()).fromJSON(this.params.routines); diff --git a/packages/entity/entity.synchronized.js b/packages/entity/entity.synchronized.js index 7129101..e31a718 100644 --- a/packages/entity/entity.synchronized.js +++ b/packages/entity/entity.synchronized.js @@ -227,20 +227,6 @@ export default class Entity extends decorate(Resource) { this._fastDirtyCheck = false; } - contextDescription() { - return { - type: 'entity', - children: this._traitsFlat.reduce( - (r, trait) => ({ - ...r, - ...trait.constructor.contextDescription(), - ...trait.constructor.describeParams(), - ...trait.constructor.describeState(), - }), {}, - ), - }; - } - fromJSON(json) { super.fromJSON(json); if (json.instanceUuid) { diff --git a/packages/entity/index.hooks.js b/packages/entity/index.hooks.js new file mode 100644 index 0000000..5d6247d --- /dev/null +++ b/packages/entity/index.hooks.js @@ -0,0 +1,15 @@ +export function behaviorContextTypes() { + return { + entity: (entity) => ( + Object.values(entity.allTraitInstances()) + .reduce( + (r, {constructor: {behaviorContextTypes, describeParams, describeState}}) => ({ + ...r, + ...behaviorContextTypes(), + ...describeParams(), + ...describeState(), + }), {}, + ) + ), + }; +} diff --git a/packages/entity/trait/index.js b/packages/entity/trait/index.js index a6690e6..025d3e0 100644 --- a/packages/entity/trait/index.js +++ b/packages/entity/trait/index.js @@ -38,7 +38,7 @@ export class Trait extends decorate(class {}) { this._fastDirtyCheck = false; } - static contextDescription() { + static behaviorContextTypes() { return {}; } diff --git a/packages/entity/traits/alive.trait.js b/packages/entity/traits/alive.trait.js index 5afa609..7d36ebf 100644 --- a/packages/entity/traits/alive.trait.js +++ b/packages/entity/traits/alive.trait.js @@ -25,7 +25,7 @@ const decorate = compose( export default class Alive extends decorate(Trait) { - static contextDescription() { + static behaviorContextTypes() { return { forceDeath: { type: 'void', @@ -112,7 +112,7 @@ export default class Alive extends decorate(Trait) { constructor(entity, params, state) { super(entity, params, state); this._context = new Context({ - entity: this.entity, + entity: [this.entity, 'entity'], }); this._deathActions = behaviorItemFromJSON(this.params.deathActions); this._deathCondition = behaviorItemFromJSON(this.params.deathCondition); diff --git a/packages/entity/traits/existent.trait.js b/packages/entity/traits/existent.trait.js index 45310ba..bccb6c3 100644 --- a/packages/entity/traits/existent.trait.js +++ b/packages/entity/traits/existent.trait.js @@ -10,7 +10,7 @@ const decorate = compose( export default class Existent extends decorate(Trait) { - static contextDescription() { + static behaviorContextTypes() { return { transition: { type: 'ticking-promise', diff --git a/packages/entity/traits/listed.trait.js b/packages/entity/traits/listed.trait.js index d4075e5..e8142cf 100644 --- a/packages/entity/traits/listed.trait.js +++ b/packages/entity/traits/listed.trait.js @@ -4,7 +4,7 @@ import {Trait} from '../trait'; export default class Listed extends Trait { - static contextDescription() { + static behaviorContextTypes() { return { detachFromList: { type: 'void', diff --git a/packages/entity/traits/mobile.trait.js b/packages/entity/traits/mobile.trait.js index 1654c9a..f8dca6d 100644 --- a/packages/entity/traits/mobile.trait.js +++ b/packages/entity/traits/mobile.trait.js @@ -10,7 +10,7 @@ const decorate = compose( export default class Mobile extends decorate(Trait) { - static contextDescription() { + static behaviorContextTypes() { return { moveFor: { type: 'void', diff --git a/packages/entity/traits/positioned.trait.js b/packages/entity/traits/positioned.trait.js index e248a33..d2adadb 100644 --- a/packages/entity/traits/positioned.trait.js +++ b/packages/entity/traits/positioned.trait.js @@ -17,7 +17,7 @@ const decorate = compose( // < 16768 will pack into 1 short per axe and give +/- 0.25 precision. export default class Positioned extends decorate(Trait) { - static contextDescription() { + static behaviorContextTypes() { return { setPosition: { type: 'void', diff --git a/packages/entity/traits/spawner.trait.js b/packages/entity/traits/spawner.trait.js index 66946ed..764262c 100644 --- a/packages/entity/traits/spawner.trait.js +++ b/packages/entity/traits/spawner.trait.js @@ -13,7 +13,7 @@ const decorate = compose( export default class Spawner extends decorate(Trait) { - static contextDescription() { + static behaviorContextTypes() { return { spawn: { type: 'entity', diff --git a/packages/graphics/traits/visible.trait.js b/packages/graphics/traits/visible.trait.js index 03ec7f9..ebd6d0d 100644 --- a/packages/graphics/traits/visible.trait.js +++ b/packages/graphics/traits/visible.trait.js @@ -30,7 +30,7 @@ const decorate = compose( export default class Visible extends decorate(Trait) { - static contextDescription() { + static behaviorContextTypes() { return { updateVisibleBoundingBox: { advanced: true, diff --git a/packages/math/globals.hooks.js b/packages/math/index.hooks.js similarity index 85% rename from packages/math/globals.hooks.js rename to packages/math/index.hooks.js index 74d3a50..6b90766 100644 --- a/packages/math/globals.hooks.js +++ b/packages/math/index.hooks.js @@ -1,9 +1,14 @@ import * as MathExt from '.'; -MathExt.contextDescription = () => { +export function behaviorContextGlobals() { return { - type: 'module', - children: { + Math: [MathExt, 'Math'], + }; +} + +export function behaviorContextTypes() { + return { + Math: (Math) => ({ floor: { type: 'number', label: 'Floor $1.', @@ -28,12 +33,6 @@ MathExt.contextDescription = () => { Vector: { type: 'Vector', }, - }, - }; -}; - -export function behaviorContextGlobals() { - return { - Math: MathExt, + }), }; } diff --git a/packages/math/vector/index.hooks.js b/packages/math/vector/index.hooks.js new file mode 100644 index 0000000..305b3ae --- /dev/null +++ b/packages/math/vector/index.hooks.js @@ -0,0 +1,15 @@ +export function behaviorContextTypes() { + return { + Vector: (Math) => ({ + fromDirection: { + type: 'vector', + label: '$1 as a movement vector.', + args: [ + ['direction', { + type: 'number', + }], + ], + }, + }), + }; +} diff --git a/packages/math/vector/index.js b/packages/math/vector/index.js index 39a7840..c660eb5 100644 --- a/packages/math/vector/index.js +++ b/packages/math/vector/index.js @@ -409,7 +409,7 @@ export class Range extends MathRange { } -export function contextDescription() { +export function behaviorContextTypes() { return { type: 'Vector', children: { diff --git a/packages/physics/traits/collider.trait.js b/packages/physics/traits/collider.trait.js index 25e0d2f..8c6ec16 100644 --- a/packages/physics/traits/collider.trait.js +++ b/packages/physics/traits/collider.trait.js @@ -10,7 +10,7 @@ const decorate = compose( export default class Collider extends decorate(Trait) { - static contextDescription() { + static behaviorContextTypes() { return { collidesWith: { advanced: true, @@ -182,7 +182,7 @@ export default class Collider extends decorate(Trait) { pushCollisionTickingPromise(actions, other) { const context = new Context({ - entity: this.entity, + entity: [this.entity, 'entity'], other, }); const tickingPromise = actions.tickingPromise(context); diff --git a/packages/sound/traits/audible.trait.js b/packages/sound/traits/audible.trait.js index a7d4b30..dc558f2 100644 --- a/packages/sound/traits/audible.trait.js +++ b/packages/sound/traits/audible.trait.js @@ -4,7 +4,7 @@ import {Sound} from '..'; export default class Audible extends Trait { - static contextDescription() { + static behaviorContextTypes() { return { playSound: { type: 'void',