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 }; }