Add the Variable node and show the variable name in node titles
Get/Set variable nodes now title themselves "Get <name>" / "Set <name>", and
the new Variable node just "<name>"; the title is the same folded or open,
and a collapsed node shrinks to it. The redundant "Variable: <name>" row is
gone unless the variable was deleted ("<name>?" is the only hint then).
The Variable node sets its value when an exec flow arrives and exposes the
value at any time. Its output is read when a downstream node pulls it, not
frozen at the write, so count = count + 1 is one Variable node plus an Add.
Get variable keeps reading once per pulse: switching it would change the
behaviour of existing games.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
5f8a308d62
commit
e9fa592078
19 files changed
+354
-38
No files matched your search
@@ -0,0 +1,102 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { RenameVariableCommand } from '../../src/core/commands/variableCommands';
|
||||
import { GraphInterpreter, resolveDef } from '../../src/core/graph';
|
||||
import type { BehaviorAsset } from '../../src/core/model/Asset';
|
||||
import { behaviorLookup } from '../../src/core/model/behaviorPrograms';
|
||||
import { fakeContext, graphEditor, makeBehavior, makeProject } from '../helpers';
|
||||
|
||||
function setup() {
|
||||
const fixture = makeProject();
|
||||
const behavior = makeBehavior(fixture, 'B', ['count', 'copy']);
|
||||
return { fixture, behavior, edit: graphEditor(fixture, behavior) };
|
||||
}
|
||||
|
||||
function run(behavior: BehaviorAsset, fixture: ReturnType<typeof makeProject>, count = 5) {
|
||||
const ctx = fakeContext({ variables: { count, copy: 0 } });
|
||||
new GraphInterpreter(behavior.graph, behaviorLookup(fixture.project)).fire('OnStart', ctx);
|
||||
return ctx.variables;
|
||||
}
|
||||
|
||||
const titleOf = (behavior: BehaviorAsset, index: number) =>
|
||||
resolveDef(behavior.graph.nodes[index]!, behavior.graph, () => null)!.label;
|
||||
|
||||
describe('the Variable node', () => {
|
||||
it('can be read without a flow ever reaching it', () => {
|
||||
const { fixture, behavior, edit } = setup();
|
||||
const start = edit.addNode('OnStart');
|
||||
const variable = edit.addNode('Variable', { variable: 'count' });
|
||||
const copy = edit.addNode('SetVariable', { variable: 'copy' });
|
||||
edit.wire([
|
||||
[start, 'exec', copy, 'exec'],
|
||||
[variable, 'value', copy, 'value'],
|
||||
]);
|
||||
expect(run(behavior, fixture)).toEqual({ count: 5, copy: 5 });
|
||||
});
|
||||
|
||||
it('writes its input when the flow arrives, unconnected as well as through an edge', () => {
|
||||
const { fixture, behavior, edit } = setup();
|
||||
const start = edit.addNode('OnStart');
|
||||
const inline = edit.addNode('Variable', { variable: 'count', value: 7 });
|
||||
const number = edit.addNode('Number', { value: 3 });
|
||||
const wired = edit.addNode('Variable', { variable: 'copy' });
|
||||
edit.wire([
|
||||
[start, 'exec', inline, 'exec'],
|
||||
[inline, 'exec', wired, 'exec'],
|
||||
[number, 'value', wired, 'value'],
|
||||
]);
|
||||
expect(run(behavior, fixture)).toEqual({ count: 7, copy: 3 });
|
||||
});
|
||||
|
||||
it('counts up through its own output: count = count + 1', () => {
|
||||
const { fixture, behavior, edit } = setup();
|
||||
const start = edit.addNode('OnStart');
|
||||
const variable = edit.addNode('Variable', { variable: 'count' });
|
||||
const add = edit.addNode('Add', { b: 1 });
|
||||
edit.wire([
|
||||
[start, 'exec', variable, 'exec'],
|
||||
[variable, 'value', add, 'a'],
|
||||
[add, 'result', variable, 'value'],
|
||||
]);
|
||||
expect(run(behavior, fixture).count).toBe(6);
|
||||
});
|
||||
|
||||
it('is read at the moment it is asked, so it sees a later write by another node', () => {
|
||||
const { fixture, behavior, edit } = setup();
|
||||
const start = edit.addNode('OnStart');
|
||||
const variable = edit.addNode('Variable', { variable: 'count', value: 7 });
|
||||
const overwrite = edit.addNode('SetVariable', { variable: 'count', value: 9 });
|
||||
const copy = edit.addNode('SetVariable', { variable: 'copy' });
|
||||
edit.wire([
|
||||
[start, 'exec', variable, 'exec'],
|
||||
[variable, 'exec', overwrite, 'exec'],
|
||||
[overwrite, 'exec', copy, 'exec'],
|
||||
[variable, 'value', copy, 'value'],
|
||||
]);
|
||||
expect(run(behavior, fixture)).toEqual({ count: 9, copy: 9 });
|
||||
});
|
||||
});
|
||||
|
||||
describe('variable node titles', () => {
|
||||
it('name the picked variable, and keep the plain title until one is picked', () => {
|
||||
const { behavior, edit } = setup();
|
||||
edit.addNode('GetVariable', { variable: 'count' });
|
||||
edit.addNode('SetVariable', { variable: 'count' });
|
||||
edit.addNode('Variable', { variable: 'count' });
|
||||
edit.addNode('GetVariable');
|
||||
edit.addNode('Variable');
|
||||
expect([0, 1, 2, 3, 4].map((i) => titleOf(behavior, i))).toEqual([
|
||||
'Get count',
|
||||
'Set count',
|
||||
'count',
|
||||
'Get variable',
|
||||
'Variable',
|
||||
]);
|
||||
});
|
||||
|
||||
it('follow a renamed variable', () => {
|
||||
const { fixture, behavior, edit } = setup();
|
||||
edit.addNode('Variable', { variable: 'count' });
|
||||
fixture.stack.execute(new RenameVariableCommand(behavior.id, 'count', 'score'));
|
||||
expect(titleOf(behavior, 0)).toBe('score');
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user