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 {
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) {

View File

@ -8,10 +8,25 @@ class Flow {
}
};
static contextDescription() {
static parallel(actions, context) {
return actions.parallel(context);
}
static serial(actions, context) {
return actions.serial(context);
}
}
export function behaviorContextGlobals() {
return {
type: 'object',
children: {
Flow: [Flow, 'Flow'],
};
}
export function behaviorContextTypes() {
return {
Flow: {
conditional: {
type: 'ticking-promise',
label: 'If $1 then run $2.',
@ -42,22 +57,7 @@ class Flow {
}],
],
},
}
};
}
static parallel(actions, context) {
return actions.parallel(context);
}
static serial(actions, context) {
return actions.serial(context);
}
}
export function behaviorContextGlobals() {
return {
Flow,
},
};
}

View File

@ -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'],
};
}

View File

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

View File

@ -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);

View File

@ -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) {

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;
}
static contextDescription() {
static behaviorContextTypes() {
return {};
}

View File

@ -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);

View File

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

View File

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

View File

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

View File

@ -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',

View File

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

View File

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

View File

@ -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,
}),
};
}

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 {
type: 'Vector',
children: {

View File

@ -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);

View File

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