A state change is something the graph can react to, so the machine announces it (#53, #54, #55, #57)
game-maker / suite (push) Successful in 10m58s
game-maker / suite (push) Successful in 10m58s
Four issues that all reach for the same line: how the current state is read and written. Splitting them into four commits would mean four states of the tree that do not compile, so they arrive together. #55 start state — the machine asset carries an optional `start` holding a State node's id. Right-click a state to set or remove it, a ring in the node's title says which one it is. A dead id (the state was deleted) reads as "no start", so nothing has to clean up after a deletion. #53 entered/exited — `On state entered` and `On state exited`, one exec output per state. Every change now goes through a single `setState` that reports it; BehaviorSystem delivers it after the exec flow that caused it, `exited` of the old before `entered` of the new, round after round until the queue is empty, with a cap that stops a ping-pong graph with an endless-loop error. A local machine tells the instance that switched, a global one tells every instance holding a matching event node. The start state fires `entered` too: per instance for a local machine, once per run for a global one. #57 reading — `Current state` with `name` for showing and `id` for comparing, `Is in state` with one boolean per state. Neither has a `run`, so `read` learned `def` and `lookup`, and the catalog test knows this third kind of node. #54 seeing it — during a run the state machine tab marks every state some instance is holding faintly, and the one held by the selected scene object's instance strongly. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
bc57f61c91
commit
7babf88636
30 files changed
+906
-44
No files matched your search
@@ -1,6 +1,6 @@
|
||||
import { textOf } from '../model/Properties';
|
||||
import { flatDef } from '../properties/PropertySchema';
|
||||
import { edgesInto, type Graph, type GraphNode } from './Graph';
|
||||
import { edgesInto, type Graph, type GraphNode, type PortType } from './Graph';
|
||||
import type { MachineState, MachineTransition, StateMachine } from './lookup';
|
||||
import type { NodeDef, PortDef, PortSource } from './NodeRegistry';
|
||||
|
||||
@@ -47,6 +47,15 @@ export function statesOf(graph: Graph): MachineState[] {
|
||||
return graph.nodes.filter(isStateNode).map((node) => ({ id: node.id, name: stateName(node) }));
|
||||
}
|
||||
|
||||
/**
|
||||
* The state a machine starts in, as far as its graph agrees: the marked id when a state of that id
|
||||
* is still there, else none. A start pointing at a deleted state is not an error to repair – it
|
||||
* reads as "no start", which is exactly what the machine had before anyone marked one.
|
||||
*/
|
||||
export function startStateOf(graph: Graph, start: string | undefined): string {
|
||||
return start && graph.nodes.some((n) => n.id === start && isStateNode(n)) ? start : '';
|
||||
}
|
||||
|
||||
/** The transitions of a machine's graph: every edge between two states, in edge order. */
|
||||
export function transitionsOf(graph: Graph): MachineTransition[] {
|
||||
const states = new Set(graph.nodes.filter(isStateNode).map((n) => n.id));
|
||||
@@ -86,22 +95,24 @@ export const machinePort = (): PortDef => ({
|
||||
});
|
||||
|
||||
/**
|
||||
* The machine a node's `machine` input names while the graph is drawn: the inline pick, or the
|
||||
* The machine asset a node's `machine` input names while the graph is drawn: the inline pick, or the
|
||||
* `State machine` literal an edge brings in. A port is derived from the graph, not from a running
|
||||
* value, so a variable or a list item feeding the input yields no state ports.
|
||||
* value, so a variable or a list item feeding the input yields nothing.
|
||||
*/
|
||||
export function machineOf({ node, graph, lookup }: PortSource): StateMachine | null {
|
||||
export function machineIdOf({ node, graph }: PortSource): string {
|
||||
const edge = edgesInto(graph, node.id, MACHINE_PORT)[0];
|
||||
const source = edge && graph.nodes.find((n) => n.id === edge.from.node);
|
||||
const id = edge
|
||||
? source?.type === STATE_MACHINE
|
||||
? textOf(source.params.value ?? '')
|
||||
: ''
|
||||
: textOf(node.params[MACHINE_PORT] ?? '');
|
||||
return id ? lookup.machine(id) : null;
|
||||
if (!edge) return textOf(node.params[MACHINE_PORT] ?? '');
|
||||
return source?.type === STATE_MACHINE ? textOf(source.params.value ?? '') : '';
|
||||
}
|
||||
|
||||
/** One exec port per state, keyed by the state's id – a rename keeps every edge. */
|
||||
export function statePorts(machine: StateMachine | null): PortDef[] {
|
||||
return (machine?.states ?? []).map((state) => ({ key: state.id, label: state.name, type: 'exec' }));
|
||||
/** What that machine is, looked up – null where the input names none, or none that exists. */
|
||||
export function machineOf(source: PortSource): StateMachine | null {
|
||||
const id = machineIdOf(source);
|
||||
return id ? source.lookup.machine(id) : null;
|
||||
}
|
||||
|
||||
/** One port per state, keyed by the state's id – a rename keeps every edge. */
|
||||
export function statePorts(machine: StateMachine | null, type: PortType = 'exec'): PortDef[] {
|
||||
return (machine?.states ?? []).map((state) => ({ key: state.id, label: state.name, type }));
|
||||
}
|
||||
Reference in new issue
Block a user