Let each variable of a behavior choose whether calls keep it (#229)
game-maker / gate (push) Successful in 3m13s
game-maker / suite (push) Failing after 8m46s

The first version of #229 put a Keep state switch on the Run behavior
node. The user wanted it where the variable is declared instead. Whether
a value lives on belongs to the variable, not to every call site.

- The node switch keepState is gone again. RunBehavior, CallResult and
  nested.ts are back to what they were before #229.
- BehaviorAsset.keptVariables lists the kept names. It lives on the
  asset and not in PropertyDef, because that is the schema of every
  inspector row.
- A variable added from now on starts with Keep on. Variables in
  existing projects start with Keep off, so old projects run unchanged.
- SetVariableKeptCommand flips the switch as one undo step. Add, remove
  and rename carry the list along. Loading drops names that are not
  declared.
- The interpreter keeps only the listed variables, per Run behavior
  node and per object as before. The others start at their defaults.
- Local state machines in a called behavior start empty on every call
  again, as before #229. Global and scene machines are unchanged.
- The inspector shows a Keep button in each variable row of a behavior.
  Its state is part of the asset signature, so a click redraws the row.
  Without that the button kept its old look until the inspector was
  reopened.

Found: the project Garden of Sacrifices still has keepState = true on
the call Player -> Character Movement. It is ignored now. Direction
there predates the switch, so Keep is off until set by hand.

Tests:
- ./check.sh: 163 files, 2392 tests, CHECK EXIT 0 (before 2388)
- tests/core/keep-state.test.ts rewritten, 15 tests
- e2e 19: ALL PASSED, 35 checks. 19c-ii now clicks the Keep button
  of a new variable: on when added, off by a click, on again, off
  after undo.
- Counter-checks, each fails its own test: keep list ignored (2),
  states kept (1), new variable not kept (8), rename drops the list
  (1), no Keep button (e2e 19c-ii).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 5.5 committed 2026-09-25 15:50:59 +02:00
1 parent 7c70245c79
commit 454d202109
14 files changed
+333 -149

No files matched your search

+21 -1
View File
@@ -29,13 +29,33 @@ export interface SceneAsset extends AssetBase {
objects: SceneObject[];
}
/** `variables` is this asset's own property schema; `props` holds their default values. */
/**
* `variables` is this asset's own property schema; `props` holds their default values.
*
* `keptVariables` names the variables a `Run behavior` call hands on to the next call from the same
* node on the same object (#229); the others start at their default every call. Absent keeps none,
* so a project from before the switch runs as it did – a variable added since starts out kept.
*/
export interface BehaviorAsset extends AssetBase {
type: 'behavior';
variables: PropertyDef[];
keptVariables?: string[];
graph: Graph;
}
/** Whether a call of this behavior hands `key` on to the next call (#229). */
export function isKeptVariable(asset: BehaviorAsset, key: string): boolean {
return asset.keptVariables?.includes(key) === true;
}
/** Writes the switch for one variable; an empty list is dropped rather than stored. */
export function setKeptVariable(asset: BehaviorAsset, key: string, on: boolean): void {
const rest = (asset.keptVariables ?? []).filter((k) => k !== key);
const next = on ? [...rest, key] : rest;
if (next.length > 0) asset.keptVariables = next;
else delete asset.keptVariables;
}
/**
* Declarations shared by the whole run: a `Variables` node naming this asset reads and writes one
* value per key, however many graphs point at it. Nothing is placed, so nothing overrides it –
+7 -2
View File
@@ -5,14 +5,19 @@ import { findAssetOf, type Project } from './Project';
/**
* What a graph may see of the project around it. A behavior's `defaults` are its variable values;
* every call copies them, so a nested behavior never carries state from one call to the next.
* every call starts from a copy of them, except for the variables it keeps (#229).
*/
export function graphLookup(project: Project): GraphLookup {
return {
behavior: (assetId) => {
const asset = findAssetOf(project, assetId, 'behavior');
if (!asset) return null;
return { name: asset.name, graph: asset.graph, defaults: resolveAssetProps(project, asset) };
return {
name: asset.name,
graph: asset.graph,
defaults: resolveAssetProps(project, asset),
kept: asset.keptVariables ?? [],
};
},
machine: (assetId) => {
const asset = findAssetOf(project, assetId, 'statemachine');