Let a value be a bundle of named fields
PropertyValue was number | string | boolean | list. A struct joins it, which costs two things the typechecker found rather than I did. An object on a data edge used to mean a live entity, so asEntity would have taken a struct for one and asString would have read .name off it. EntityHandle now carries a symbol brand – a symbol so no field name can impersonate it, and it never serializes, which suits a thing that exists only while the game runs. It is built in exactly one place. String(value) would have written a struct as [object Object] at 39 call sites. They go through textOf(), which is identical for primitives. normalizeGraph dropped everything but primitives from node params, so the List node lost its value on every save ([3,1,4] round-tripped to undefined). Variables were spared: they have a schema, params have none.
This commit is contained in:
1 parent
3b3f374e10
commit
2cf788eedd
34 files changed
+284
-82
No files matched your search
@@ -2,6 +2,7 @@ import type { NodeDef } from '../../NodeRegistry';
|
||||
import { asEntity, writeTyped } from '../../values';
|
||||
import { any, exec, obj } from '../shared';
|
||||
|
||||
import { textOf } from '../../../model/Properties';
|
||||
export const SetProperty: NodeDef = {
|
||||
type: 'SetProperty',
|
||||
label: 'Set property',
|
||||
@@ -12,7 +13,7 @@ export const SetProperty: NodeDef = {
|
||||
params: [{ key: 'property', label: 'Property', kind: 'text', default: 'width' }],
|
||||
run: (_ctx, inputs, params) => {
|
||||
const target = asEntity(inputs.object);
|
||||
const key = String(params.property ?? '');
|
||||
const key = textOf(params.property ?? '');
|
||||
if (target && key) writeTyped(target.props, key, inputs.value);
|
||||
return { next: 'exec' };
|
||||
},
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { NodeDef } from '../../NodeRegistry';
|
||||
import { asEntity, asNumber } from '../../values';
|
||||
import { assetParam, exec, num, obj } from '../shared';
|
||||
|
||||
import { textOf } from '../../../model/Properties';
|
||||
export const Spawn: NodeDef = {
|
||||
type: 'Spawn',
|
||||
label: 'Spawn',
|
||||
@@ -13,7 +14,7 @@ export const Spawn: NodeDef = {
|
||||
outputs: [exec(), obj('object')],
|
||||
params: [assetParam],
|
||||
run: (ctx, inputs, params) => {
|
||||
const assetId = String(params.asset ?? '');
|
||||
const assetId = textOf(params.asset ?? '');
|
||||
const position = { x: asNumber(inputs.x), y: asNumber(inputs.y), z: asNumber(inputs.z) };
|
||||
const spawned = assetId ? ctx.spawn(assetId, position, asEntity(inputs.parent)) : null;
|
||||
if (!spawned) ctx.log(assetId ? `Spawn: asset ${assetId} not found` : 'Spawn: no asset selected');
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { NodeDef } from '../../NodeRegistry';
|
||||
import { bool, normalize, num } from '../shared';
|
||||
|
||||
import { textOf } from '../../../model/Properties';
|
||||
export const AxisInput: NodeDef = {
|
||||
type: 'AxisInput',
|
||||
label: 'Axis input',
|
||||
@@ -15,7 +16,7 @@ export const AxisInput: NodeDef = {
|
||||
{ key: 'down', label: 'Down', kind: 'key', default: 'ArrowDown' },
|
||||
],
|
||||
run: (ctx, _inputs, params) => {
|
||||
const held = (key: string, fallback: string) => (ctx.isKeyDown(String(params[key] ?? fallback)) ? 1 : 0);
|
||||
const held = (key: string, fallback: string) => (ctx.isKeyDown(textOf(params[key] ?? fallback)) ? 1 : 0);
|
||||
const dx = held('right', 'ArrowRight') - held('left', 'ArrowLeft');
|
||||
const dy = held('down', 'ArrowDown') - held('up', 'ArrowUp');
|
||||
const { x, y, length } = normalize(dx, dy);
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { NodeDef } from '../../NodeRegistry';
|
||||
import { asNumber } from '../../values';
|
||||
import { bool, num } from '../shared';
|
||||
|
||||
import { textOf } from '../../../model/Properties';
|
||||
const OPERATORS: Record<string, (a: number, b: number) => boolean> = {
|
||||
'==': (a, b) => a === b,
|
||||
'!=': (a, b) => a !== b,
|
||||
@@ -30,7 +31,7 @@ export const Compare: NodeDef = {
|
||||
run: (_ctx, inputs, params) => {
|
||||
// The fallback matches the param's declared default, so a node with no setting behaves
|
||||
// like a freshly created one.
|
||||
const compare = OPERATORS[String(params.op)] ?? OPERATORS['<']!;
|
||||
const compare = OPERATORS[textOf(params.op)] ?? OPERATORS['<']!;
|
||||
return { outputs: { result: compare(asNumber(inputs.a), asNumber(inputs.b)) } };
|
||||
},
|
||||
};
|
||||
@@ -2,6 +2,7 @@ import type { NodeDef } from '../../NodeRegistry';
|
||||
import { asEntity } from '../../values';
|
||||
import { any, obj } from '../shared';
|
||||
|
||||
import { textOf } from '../../../model/Properties';
|
||||
export const GetProperty: NodeDef = {
|
||||
type: 'GetProperty',
|
||||
label: 'Get property',
|
||||
@@ -11,6 +12,6 @@ export const GetProperty: NodeDef = {
|
||||
outputs: [any('value')],
|
||||
params: [{ key: 'property', label: 'Property', kind: 'text', default: 'width' }],
|
||||
run: (_ctx, inputs, params) => ({
|
||||
outputs: { value: asEntity(inputs.object)?.props[String(params.property ?? '')] ?? null },
|
||||
outputs: { value: asEntity(inputs.object)?.props[textOf(params.property ?? '')] ?? null },
|
||||
}),
|
||||
};
|
||||
@@ -2,6 +2,7 @@ import type { NodeDef } from '../../NodeRegistry';
|
||||
import { asEntity } from '../../values';
|
||||
import { assetParam, bool, obj } from '../shared';
|
||||
|
||||
import { textOf } from '../../../model/Properties';
|
||||
export const IsAsset: NodeDef = {
|
||||
type: 'IsAsset',
|
||||
label: 'Is asset',
|
||||
@@ -11,7 +12,7 @@ export const IsAsset: NodeDef = {
|
||||
outputs: [bool('result')],
|
||||
params: [assetParam],
|
||||
run: (_ctx, inputs, params) => {
|
||||
const assetId = String(params.asset ?? '');
|
||||
const assetId = textOf(params.asset ?? '');
|
||||
return { outputs: { result: Boolean(assetId) && asEntity(inputs.object)?.assetId === assetId } };
|
||||
},
|
||||
};
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { NodeDef } from '../../NodeRegistry';
|
||||
import { bool, keyParam } from '../shared';
|
||||
|
||||
import { textOf } from '../../../model/Properties';
|
||||
export const KeyIsDown: NodeDef = {
|
||||
type: 'KeyIsDown',
|
||||
label: 'Key is down',
|
||||
@@ -9,5 +10,5 @@ export const KeyIsDown: NodeDef = {
|
||||
inputs: [],
|
||||
outputs: [bool('down')],
|
||||
params: [keyParam],
|
||||
run: (ctx, _inputs, params) => ({ outputs: { down: ctx.isKeyDown(String(params.key ?? '')) } }),
|
||||
run: (ctx, _inputs, params) => ({ outputs: { down: ctx.isKeyDown(textOf(params.key ?? '')) } }),
|
||||
};
|
||||
@@ -9,6 +9,7 @@ export const bool = (key: string, label = key): PortDef => ({ key, label, type:
|
||||
export const str = (key: string, label = key): PortDef => ({ key, label, type: 'string' });
|
||||
export const obj = (key: string, label = key): PortDef => ({ key, label, type: 'object' });
|
||||
export const list = (key: string, label = key): PortDef => ({ key, label, type: 'list' });
|
||||
export const struct = (key: string, label = key): PortDef => ({ key, label, type: 'struct' });
|
||||
export const any = (key: string, label = key): PortDef => ({ key, label, type: 'any' });
|
||||
|
||||
export const withDefault = (port: PortDef, value: PropertyValue): PortDef => ({ ...port, default: value });
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { NodeDef } from '../../NodeRegistry';
|
||||
import { any, variableParam } from '../shared';
|
||||
|
||||
import { textOf } from '../../../model/Properties';
|
||||
export const GetVariable: NodeDef = {
|
||||
type: 'GetVariable',
|
||||
label: 'Get variable',
|
||||
@@ -10,6 +11,6 @@ export const GetVariable: NodeDef = {
|
||||
outputs: [any('value')],
|
||||
params: [variableParam],
|
||||
run: (ctx, _inputs, params) => ({
|
||||
outputs: { value: ctx.variables[String(params.variable ?? '')] ?? null },
|
||||
outputs: { value: ctx.variables[textOf(params.variable ?? '')] ?? null },
|
||||
}),
|
||||
};
|
||||
@@ -2,6 +2,7 @@ import type { NodeDef } from '../../NodeRegistry';
|
||||
import { writeTyped } from '../../values';
|
||||
import { any, exec, variableParam } from '../shared';
|
||||
|
||||
import { textOf } from '../../../model/Properties';
|
||||
export const SetVariable: NodeDef = {
|
||||
type: 'SetVariable',
|
||||
label: 'Set variable',
|
||||
@@ -11,7 +12,7 @@ export const SetVariable: NodeDef = {
|
||||
outputs: [exec()],
|
||||
params: [variableParam],
|
||||
run: (ctx, inputs, params) => {
|
||||
const key = String(params.variable ?? '');
|
||||
const key = textOf(params.variable ?? '');
|
||||
if (key) writeTyped(ctx.variables, key, inputs.value);
|
||||
return { next: 'exec' };
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user