Keep behavior variables private to their own graph (#257)
game-maker / gate (push) Successful in 3m40s
game-maker / suite (push) Successful in 10m56s

A behavior often needs working state such as a timer or a counter.
Until now every variable showed up on each placed object and in the
Properties menu, so the inspector filled up with values nobody should
touch. A private variable works like any other inside its graph but
is not offered anywhere outside.

- Each behavior variable gets a lock switch next to Keep. It starts
  off, so existing projects stay as they are (`privateVariables`).
- A placed object shows no private variable, neither in the
  inspector nor while the game runs. If all are private, it says so.
- Switching a variable to private drops its overrides on every
  placed object in one undo step. Switching back does not restore
  them.
- A running game does not write a private value back as an
  override, and a project change during the run does not reset it.
- `+ Property` no longer offers private variables. A port that
  names one by hand still reads and writes it without a warning.
- The default value of a private variable is read-only in the asset
  inspector. Its shape (name, fields, type, delete) stays editable.
- Keep is now a pin icon instead of a word. With the lock added, the
  header of a list variable had no room left and the lock covered
  `+ Item`.

Not done: the animation track picker still offers private variables.

Tests: ./check.sh -> 186 files, 2711 tests, CHECK EXIT 0.
e2e 1,19,20,21,56 -> ALL PASSED, 119 checks. New module 56 has
7 checks with a real mouse. Counter-checks fail as expected: unit
without the write-back filter, the sync guard, dropping overrides or
loading the flag. Browser with all variables on the object, an
unfiltered menu, or no value lock.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 5.5 committed 2026-09-28 13:54:57 +02:00
1 parent cf6565d108
commit e8b3ac0a3b
16 files changed
+455 -49

No files matched your search

+27 -3
View File
@@ -37,14 +37,20 @@ export interface SceneAsset extends AssetBase {
* `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, and a new variable starts out not kept.
*
* `privateVariables` names the variables only the behavior's own graph sees (#257): a placed object
* neither shows nor overrides them, and a `Properties` node does not offer them. Absent is none.
*/
export interface BehaviorAsset extends AssetBase {
type: 'behavior';
variables: PropertyDef[];
keptVariables?: string[];
privateVariables?: string[];
graph: Graph;
}
type VariableSwitch = 'keptVariables' | 'privateVariables';
/** 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;
@@ -52,10 +58,28 @@ export function isKeptVariable(asset: BehaviorAsset, key: string): boolean {
/** 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);
setSwitch(asset, 'keptVariables', key, on);
}
/** Whether `key` is hidden from everything outside the behavior's own graph (#257). */
export function isPrivateVariable(asset: Asset, key: string): boolean {
return asset.type === 'behavior' && asset.privateVariables?.includes(key) === true;
}
export function setPrivateVariable(asset: BehaviorAsset, key: string, on: boolean): void {
setSwitch(asset, 'privateVariables', key, on);
}
/** The variables a placed object shows and overrides: all but the private ones. */
export function publicVariables(asset: BehaviorAsset): PropertyDef[] {
return asset.variables.filter((def) => !isPrivateVariable(asset, def.key));
}
function setSwitch(asset: BehaviorAsset, list: VariableSwitch, key: string, on: boolean): void {
const rest = (asset[list] ?? []).filter((k) => k !== key);
const next = on ? [...rest, key] : rest;
if (next.length > 0) asset.keptVariables = next;
else delete asset.keptVariables;
if (next.length > 0) asset[list] = next;
else delete asset[list];
}
/**