A variable or a state machine declared `global` keeps one value for the whole run instead of one per behavior instance. The scope sits on the declaration -- `PropertyDef.scope` and `StateMachineAsset.scope`, both optional, absence meaning `local` -- and `local` is never written, so every project made before this saves back unchanged. **The name is the key.** Two behaviors declaring a global `score` mean one variable; a behavior declaring `score` locally keeps its own. Kinds that disagree about one global name stop the run before it starts, with the two behaviors named; starting values that disagree are a warning on the behavior that declares it, and the first declaration wins (`globalConflicts`). The runtime needs no redirection layer. A global variable falls out of `schemaForObject`, so it never reaches `entity.props` -- and then the whole rule is "whoever declares the name takes their own value" (`graph/sharedState.ts`). The `Variables` node is untouched and stays transparent: switching a variable to global breaks no existing graph, because `GraphInterpreter.readAll` builds a fresh object per read anyway and only asks `port.key in all`. That same cut answers the inspector. A shared value has no owner, so the placed object shows no row for it and can hold no override -- `SetVariableScopeCommand` takes the deviations that were there and undo gives them back. Both places say it in words rather than leaving a glyph to be guessed: the declaration explains the mark below the list, and the placed object names the variable it no longer holds instead of claiming the behavior declares none -- which is what it said before, untruthfully. Screenshots of the e2e run turned that up; no check had been looking at it. `SharedState` hangs off `NodeContext`, is built in `BehaviorSystem.start()` from the *declarations* rather than from any instance (a global variable exists before its behavior is placed), and is thrown away with the run: nothing is written back, not even in the editor run. `GraphInterpreter.call` passes it on through `...ctx` while nested calls keep getting fresh local variables, which is why `program.defaults` is stripped of global keys. Two traps this hit. `defSignature`/`assetSignature` have to carry the scope or the inspector does not rebuild after the switch -- and a state machine has no rows at all, so its scope is appended to the signature by hand. And the object inspector does not go through `schemaForObject` for behaviors, it reads `asset.variables` directly; filtering in one place was not enough, and no unit test covered that path. The e2e caught it (`global: true` where false belonged), and the rule now lives in one named function, `objectRows`. Scopes `object` and `scene` are deliberately not built. The object case already has a route (`Object by name` -> `Properties`), and a run knows exactly one scene, so `scene` would be indistinguishable from `global` until #39 exists. Closes #59: a machine's scope is a word on the machine asset, not a setting per `Set state` node, so the same machine cannot live in two places at once. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
73 lines
2.1 KiB
JavaScript
73 lines
2.1 KiB
JavaScript
/**
|
|
* The e2e steps in the order they run. Each step is `async (t) => {}` over the harness
|
|
* toolbox; later steps rely on the scene state earlier ones leave behind (names given in
|
|
* the parenting step, the container created in the container step, …).
|
|
*/
|
|
import { hierarchy } from './01-hierarchy.mjs';
|
|
import { inspector } from './02-inspector.mjs';
|
|
import { play } from './03-play.mjs';
|
|
import { assets } from './04-assets.mjs';
|
|
import { drawing } from './05-drawing.mjs';
|
|
import { graph } from './06-graph.mjs';
|
|
import { parenting } from './07-parenting.mjs';
|
|
import { colliders } from './08-colliders.mjs';
|
|
import { containers } from './09-containers.mjs';
|
|
import { textAndEnabled } from './10-text-and-enabled.mjs';
|
|
import { menus } from './11-menus.mjs';
|
|
import { textures } from './12-textures.mjs';
|
|
import { frames } from './13-frames.mjs';
|
|
import { overflow } from './14-overflow.mjs';
|
|
import { aspect } from './15-aspect.mjs';
|
|
import { containerTab } from './16-container.mjs';
|
|
import { editorRun } from './17-editor-run.mjs';
|
|
import { pause } from './18-pause.mjs';
|
|
import { nestedBehavior } from './19-nested-behavior.mjs';
|
|
import { keyRepeat } from './20-key-repeat.mjs';
|
|
import { listTab } from './21-list.mjs';
|
|
import { structVariable } from './22-struct.mjs';
|
|
import { variableNodes } from './23-variable-nodes.mjs';
|
|
import { propertyNodes } from './24-property-nodes.mjs';
|
|
import { animation } from './25-animation.mjs';
|
|
import { stateMachine } from './26-state-machine.mjs';
|
|
import { consolePanel } from './27-console.mjs';
|
|
import { shader } from './28-shader.mjs';
|
|
import { transform } from './29-transform.mjs';
|
|
import { font } from './30-font.mjs';
|
|
import { particles } from './31-particles.mjs';
|
|
import { scope } from './32-scope.mjs';
|
|
|
|
export const STEPS = [
|
|
hierarchy,
|
|
inspector,
|
|
play,
|
|
assets,
|
|
drawing,
|
|
graph,
|
|
parenting,
|
|
colliders,
|
|
containers,
|
|
textAndEnabled,
|
|
menus,
|
|
textures,
|
|
frames,
|
|
overflow,
|
|
aspect,
|
|
containerTab,
|
|
editorRun,
|
|
pause,
|
|
nestedBehavior,
|
|
keyRepeat,
|
|
listTab,
|
|
structVariable,
|
|
variableNodes,
|
|
propertyNodes,
|
|
animation,
|
|
stateMachine,
|
|
consolePanel,
|
|
shader,
|
|
transform,
|
|
font,
|
|
particles,
|
|
scope,
|
|
];
|