A variable could only be a number, a text or a yes/no, so every set of values had to be smuggled through a string - which is where Tetris got its unreadable "03066326048763714" colours from. A list is now a value of its own, with an item kind (number, text, yes/no, colour) that decides how the inspector reads the one comma-separated line it is typed as, and eight nodes to work with: List, Fill, Count, Item at, Set item, Sub list, Join lists, Index of. Two traps came with it. An array is typeof 'object', so every place that told a value from an entity had to learn the difference, or a list would have been passed around as a game object. And a value was compared by identity in three places - with lists that marks everything as an overridden property after each reload, so those comparisons now compare contents. There is no list of objects: a list is written to the project file and an object is a live handle that no reload survives. A set of objects stays a list of names. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
92 lines
3.3 KiB
TypeScript
92 lines
3.3 KiB
TypeScript
import type { PropertyValue } from '../../model/Properties';
|
|
import type { PropertyDef } from '../../properties/PropertySchema';
|
|
import type { NodeDef, PortDef } from '../NodeRegistry';
|
|
import { asBoolean, asNumber } from '../values';
|
|
|
|
// --- ports -----------------------------------------------------------------------------------
|
|
|
|
export const exec = (key = 'exec', label = ''): PortDef => ({ key, label, type: 'exec' });
|
|
export const num = (key: string, label = key): PortDef => ({ key, label, type: 'number' });
|
|
export const bool = (key: string, label = key): PortDef => ({ key, label, type: 'boolean' });
|
|
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 any = (key: string, label = key): PortDef => ({ key, label, type: 'any' });
|
|
|
|
export const withDefault = (port: PortDef, value: PropertyValue): PortDef => ({ ...port, default: value });
|
|
|
|
/** Outputs of the three collision events: the other body, its collider and the own collider. */
|
|
export const collisionOutputs = (): PortDef[] => [
|
|
exec(),
|
|
obj('other'),
|
|
obj('otherCollider', 'other collider'),
|
|
obj('collider'),
|
|
];
|
|
|
|
// --- params ----------------------------------------------------------------------------------
|
|
|
|
export const keyParam: PropertyDef = { key: 'key', label: 'Key', kind: 'key', default: 'ArrowRight' };
|
|
|
|
/** Options are empty on purpose: the graph inspector fills in the behavior's variables. */
|
|
export const variableParam: PropertyDef = {
|
|
key: 'variable',
|
|
label: 'Variable',
|
|
kind: 'enum',
|
|
default: '',
|
|
options: [],
|
|
};
|
|
|
|
/** No `assetTypes`: any placeable asset is a valid answer here. */
|
|
export const assetParam: PropertyDef = { key: 'asset', label: 'Asset', kind: 'asset', default: '' };
|
|
|
|
// --- node factories --------------------------------------------------------------------------
|
|
|
|
export function binaryMath(type: string, description: string, fn: (a: number, b: number) => number): NodeDef {
|
|
return {
|
|
type,
|
|
label: type,
|
|
category: 'data',
|
|
description,
|
|
inputs: [num('a'), num('b')],
|
|
outputs: [num('result')],
|
|
params: [],
|
|
run: (_ctx, inputs) => ({ outputs: { result: fn(asNumber(inputs.a), asNumber(inputs.b)) } }),
|
|
};
|
|
}
|
|
|
|
export function unaryMath(type: string, description: string, fn: (value: number) => number): NodeDef {
|
|
return {
|
|
type,
|
|
label: type,
|
|
category: 'data',
|
|
description,
|
|
inputs: [num('value')],
|
|
outputs: [num('result')],
|
|
params: [],
|
|
run: (_ctx, inputs) => ({ outputs: { result: fn(asNumber(inputs.value)) } }),
|
|
};
|
|
}
|
|
|
|
export function binaryLogic(
|
|
type: string,
|
|
description: string,
|
|
fn: (a: boolean, b: boolean) => boolean,
|
|
): NodeDef {
|
|
return {
|
|
type,
|
|
label: type,
|
|
category: 'data',
|
|
description,
|
|
inputs: [bool('a'), bool('b')],
|
|
outputs: [bool('result')],
|
|
params: [],
|
|
run: (_ctx, inputs) => ({ outputs: { result: fn(asBoolean(inputs.a), asBoolean(inputs.b)) } }),
|
|
};
|
|
}
|
|
|
|
/** Scales (x, y) to length 1; the zero vector stays zero. */
|
|
export function normalize(x: number, y: number): { x: number; y: number; length: number } {
|
|
const length = Math.hypot(x, y);
|
|
return length === 0 ? { x: 0, y: 0, length: 0 } : { x: x / length, y: y / length, length };
|
|
}
|