feat: context types

This commit is contained in:
cha0s 2020-06-19 17:59:14 -05:00
parent 983db36bab
commit ea28c58cef
20 changed files with 148 additions and 122 deletions

View File

@ -8,10 +8,10 @@ const debug = D('@avocado:behavior:context');
export class Context { export class Context {
constructor(defaults = {}) { constructor(defaults = {}) {
this.typeMap = new Map();
this.variableMap = new Map();
this.initialize(); this.initialize();
for (const key in defaults) { this.addObjectMap(defaults);
this.add(key, defaults[key]);
}
} }
static describe(description, fn) { static describe(description, fn) {
@ -21,34 +21,41 @@ export class Context {
} }
static globals() { static globals() {
if (!Context._globals) { return invokeHookFlat('behaviorContextGlobals')
Context._globals = {}; .reduce((r, globals) => ({...r, ...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;
} }
add(key, value) { static typeDescription(type, variable) {
this.map.set(key, value); 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() { all() {
return objectFromEntries(Array.from(this.map.entries())); return Array.from(this.variableMap.keys())
.reduce((r, key) => ({...r, [key]: this.get(key)}), {});
} }
initialize() { initialize() {
this.map = new Map(); this.typeMap.clear()
this.add('context', this); this.variableMap.clear()
const globals = this.constructor.globals(); this.add('context', this, 'context');
for (const key in globals) { this.addObjectMap(this.constructor.globals());
this.add(key, globals[key]);
}
} }
clear() { clear() {
@ -56,7 +63,12 @@ export class Context {
} }
destroy() { destroy() {
this.map.clear(); this.typeMap.clear()
this.variableMap.clear();
}
get(key) {
return [this.variableMap.get(key), this.typeMap.get(key)];
} }
static renderSteps(steps) { static renderSteps(steps) {
@ -126,7 +138,7 @@ export class Context {
previousNode = node; previousNode = node;
return result; return result;
}); });
}, this.map.get(first.key)); }, this.variableMap.get(first.key));
} }
traverseOneStep(steps, previousNode, node, step) { traverseOneStep(steps, previousNode, node, step) {

View File

@ -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) { static parallel(actions, context) {
return actions.parallel(context); return actions.parallel(context);
} }
@ -58,6 +20,44 @@ class Flow {
export function behaviorContextGlobals() { export function behaviorContextGlobals() {
return { 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',
}],
],
},
},
};
}

View File

@ -2,23 +2,6 @@ import {TickingPromise} from '@avocado/core';
class Timing { class Timing {
static contextDescription() {
return {
type: 'object',
children: {
wait: {
type: 'ticking-promise',
label: 'Wait for $1 seconds.',
args: [
['duration', {
type: 'number',
}],
],
},
},
};
}
static wait (duration) { static wait (duration) {
return new TickingPromise( return new TickingPromise(
() => {}, () => {},
@ -33,8 +16,24 @@ class Timing {
} }
export function behaviorContextGlobals() { export function behaviorContextTypes() {
return { return {
Timing, Timing: {
wait: {
type: 'ticking-promise',
label: 'Wait for $1 seconds.',
args: [
['duration', {
type: 'number',
}],
],
},
},
};
}
export function behaviorContextGlobals() {
return {
Timing: [Timing, 'Timing'],
}; };
} }

View File

@ -34,6 +34,6 @@ class Utility {
export function behaviorContextGlobals() { export function behaviorContextGlobals() {
return { return {
Utility, Utility: [Utility, 'Utility'],
}; };
} }

View File

@ -55,7 +55,7 @@ export default class Behaved extends decorate(Trait) {
constructor(entity, params, state) { constructor(entity, params, state) {
super(entity, params, state); super(entity, params, state);
this._context = new Context({ this._context = new Context({
entity: this.entity, entity: [this.entity, 'entity'],
}); });
this._currentRoutine = undefined; this._currentRoutine = undefined;
this._routines = (new Routines()).fromJSON(this.params.routines); this._routines = (new Routines()).fromJSON(this.params.routines);

View File

@ -227,20 +227,6 @@ export default class Entity extends decorate(Resource) {
this._fastDirtyCheck = false; 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) { fromJSON(json) {
super.fromJSON(json); super.fromJSON(json);
if (json.instanceUuid) { if (json.instanceUuid) {

View File

@ -0,0 +1,15 @@
export function behaviorContextTypes() {
return {
entity: (entity) => (
Object.values(entity.allTraitInstances())
.reduce(
(r, {constructor: {behaviorContextTypes, describeParams, describeState}}) => ({
...r,
...behaviorContextTypes(),
...describeParams(),
...describeState(),
}), {},
)
),
};
}

View File

@ -38,7 +38,7 @@ export class Trait extends decorate(class {}) {
this._fastDirtyCheck = false; this._fastDirtyCheck = false;
} }
static contextDescription() { static behaviorContextTypes() {
return {}; return {};
} }

View File

@ -25,7 +25,7 @@ const decorate = compose(
export default class Alive extends decorate(Trait) { export default class Alive extends decorate(Trait) {
static contextDescription() { static behaviorContextTypes() {
return { return {
forceDeath: { forceDeath: {
type: 'void', type: 'void',
@ -112,7 +112,7 @@ export default class Alive extends decorate(Trait) {
constructor(entity, params, state) { constructor(entity, params, state) {
super(entity, params, state); super(entity, params, state);
this._context = new Context({ this._context = new Context({
entity: this.entity, entity: [this.entity, 'entity'],
}); });
this._deathActions = behaviorItemFromJSON(this.params.deathActions); this._deathActions = behaviorItemFromJSON(this.params.deathActions);
this._deathCondition = behaviorItemFromJSON(this.params.deathCondition); this._deathCondition = behaviorItemFromJSON(this.params.deathCondition);

View File

@ -10,7 +10,7 @@ const decorate = compose(
export default class Existent extends decorate(Trait) { export default class Existent extends decorate(Trait) {
static contextDescription() { static behaviorContextTypes() {
return { return {
transition: { transition: {
type: 'ticking-promise', type: 'ticking-promise',

View File

@ -4,7 +4,7 @@ import {Trait} from '../trait';
export default class Listed extends Trait { export default class Listed extends Trait {
static contextDescription() { static behaviorContextTypes() {
return { return {
detachFromList: { detachFromList: {
type: 'void', type: 'void',

View File

@ -10,7 +10,7 @@ const decorate = compose(
export default class Mobile extends decorate(Trait) { export default class Mobile extends decorate(Trait) {
static contextDescription() { static behaviorContextTypes() {
return { return {
moveFor: { moveFor: {
type: 'void', type: 'void',

View File

@ -17,7 +17,7 @@ const decorate = compose(
// < 16768 will pack into 1 short per axe and give +/- 0.25 precision. // < 16768 will pack into 1 short per axe and give +/- 0.25 precision.
export default class Positioned extends decorate(Trait) { export default class Positioned extends decorate(Trait) {
static contextDescription() { static behaviorContextTypes() {
return { return {
setPosition: { setPosition: {
type: 'void', type: 'void',

View File

@ -13,7 +13,7 @@ const decorate = compose(
export default class Spawner extends decorate(Trait) { export default class Spawner extends decorate(Trait) {
static contextDescription() { static behaviorContextTypes() {
return { return {
spawn: { spawn: {
type: 'entity', type: 'entity',

View File

@ -30,7 +30,7 @@ const decorate = compose(
export default class Visible extends decorate(Trait) { export default class Visible extends decorate(Trait) {
static contextDescription() { static behaviorContextTypes() {
return { return {
updateVisibleBoundingBox: { updateVisibleBoundingBox: {
advanced: true, advanced: true,

View File

@ -1,9 +1,14 @@
import * as MathExt from '.'; import * as MathExt from '.';
MathExt.contextDescription = () => { export function behaviorContextGlobals() {
return { return {
type: 'module', Math: [MathExt, 'Math'],
children: { };
}
export function behaviorContextTypes() {
return {
Math: (Math) => ({
floor: { floor: {
type: 'number', type: 'number',
label: 'Floor $1.', label: 'Floor $1.',
@ -28,12 +33,6 @@ MathExt.contextDescription = () => {
Vector: { Vector: {
type: 'Vector', type: 'Vector',
}, },
}, }),
};
};
export function behaviorContextGlobals() {
return {
Math: MathExt,
}; };
} }

View File

@ -0,0 +1,15 @@
export function behaviorContextTypes() {
return {
Vector: (Math) => ({
fromDirection: {
type: 'vector',
label: '$1 as a movement vector.',
args: [
['direction', {
type: 'number',
}],
],
},
}),
};
}

View File

@ -409,7 +409,7 @@ export class Range extends MathRange {
} }
export function contextDescription() { export function behaviorContextTypes() {
return { return {
type: 'Vector', type: 'Vector',
children: { children: {

View File

@ -10,7 +10,7 @@ const decorate = compose(
export default class Collider extends decorate(Trait) { export default class Collider extends decorate(Trait) {
static contextDescription() { static behaviorContextTypes() {
return { return {
collidesWith: { collidesWith: {
advanced: true, advanced: true,
@ -182,7 +182,7 @@ export default class Collider extends decorate(Trait) {
pushCollisionTickingPromise(actions, other) { pushCollisionTickingPromise(actions, other) {
const context = new Context({ const context = new Context({
entity: this.entity, entity: [this.entity, 'entity'],
other, other,
}); });
const tickingPromise = actions.tickingPromise(context); const tickingPromise = actions.tickingPromise(context);

View File

@ -4,7 +4,7 @@ import {Sound} from '..';
export default class Audible extends Trait { export default class Audible extends Trait {
static contextDescription() { static behaviorContextTypes() {
return { return {
playSound: { playSound: {
type: 'void', type: 'void',