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
@@ -2,6 +2,7 @@ import type { PropertyBag } from '../model/Properties';
|
||||
import type { Project } from '../model/Project';
|
||||
import { isGlobal } from '../model/Scope';
|
||||
import type { NodeContext, SharedState } from './NodeContext';
|
||||
import { startStateOf } from './stateMachine';
|
||||
|
||||
export function emptySharedState(): SharedState {
|
||||
return { variables: {}, states: {} };
|
||||
@@ -26,10 +27,25 @@ export function declaredVariables(project: Project): Record<string, PropertyBag>
|
||||
export function buildSharedState(project: Project): SharedState {
|
||||
const shared: SharedState = { variables: declaredVariables(project), states: {} };
|
||||
for (const asset of project.assets)
|
||||
if (asset.type === 'statemachine' && isGlobal(asset)) shared.states[asset.id] = '';
|
||||
if (asset.type === 'statemachine' && isGlobal(asset))
|
||||
shared.states[asset.id] = startStateOf(asset.graph, asset.start);
|
||||
return shared;
|
||||
}
|
||||
|
||||
/**
|
||||
* The start state of every machine that has a usable one, keyed by asset id. A machine without one
|
||||
* is absent, which is what keeps "no start" and "starts in a deleted state" the same thing.
|
||||
*/
|
||||
export function startStates(project: Project): Record<string, string> {
|
||||
const starts: Record<string, string> = {};
|
||||
for (const asset of project.assets) {
|
||||
if (asset.type !== 'statemachine') continue;
|
||||
const start = startStateOf(asset.graph, asset.start);
|
||||
if (start) starts[asset.id] = start;
|
||||
}
|
||||
return starts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves edits made to the declarations during a run into the values it shares. A key is taken over
|
||||
* only where the document changed it since `seen`, which is what leaves a value a graph has moved
|
||||
@@ -68,3 +84,22 @@ export function variableBag(ctx: NodeContext, source: string): PropertyBag {
|
||||
export function stateBag(ctx: NodeContext, machineId: string): Record<string, string> {
|
||||
return machineId in ctx.shared.states ? ctx.shared.states : ctx.states;
|
||||
}
|
||||
|
||||
/** The current state of a machine, `''` when it is in none. */
|
||||
export function currentState(ctx: NodeContext, machineId: string): string {
|
||||
return (machineId && stateBag(ctx, machineId)[machineId]) || '';
|
||||
}
|
||||
|
||||
/**
|
||||
* The one way a machine moves: writes the new state and reports the move. Into the state it is
|
||||
* already in is no move – nothing is written, and nothing fires, which is what keeps a `Set state`
|
||||
* run every frame from firing `entered` every frame.
|
||||
*/
|
||||
export function setState(ctx: NodeContext, machineId: string, next: string): void {
|
||||
if (!machineId) return;
|
||||
const bag = stateBag(ctx, machineId);
|
||||
const previous = bag[machineId] ?? '';
|
||||
if (previous === next) return;
|
||||
bag[machineId] = next;
|
||||
ctx.stateChanged(machineId, previous, next);
|
||||
}
|
||||
Reference in new issue
Block a user