Commit Graph
22 Commits
Author SHA1 Message Date
adminandClaude Opus 5.5 64d0d472e5 Show a spawned object's first animation frame right away
An object that appears during a frame and plays an animation in its
On start was drawn once in its original state. A composition item
showed frame 0 one frame late. An object from the Spawn node even ran
its On start only on the next frame, after it had been drawn.

- BehaviorSystem: at the end of the behavior pass, objects that came
  to be in this frame hear On start and On enable. This covers spawned
  objects and behaviors switched on for the first time. Their
  On update starts with the next frame.
- AnimationSystem.showStarted: right after that, every animation
  started in this frame shows its frame 0. The clock runs from the next
  frame, and an end is told no earlier than then. A paused frame skips
  this, its animations stand still anyway.
- What such a late On start spawns in turn starts in the next frame.
  Otherwise a spawn chain could stretch one frame without end.
- Scene start is unchanged. There On start already ran before the
  first frame.
- Wiki Behaviors, Lifecycle: the new timing.

Tests: gate green on stein2 (melektaus-atx went offline mid-run),
305 files, 4716 tests (2 new). e2e 68 on melektaus-atx green,
10 checks. Without the late pass the Spawn test
reads 7 instead of 0, without showStarted both new tests are red.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 21:55:35 +02:00
adminandClaude Opus 5.5 c425eea004 Key what an empty timeline row shows on a double-click (#412)
A double-click into a timeline row without keys set the property's
default, while the field in the row header showed the value the target
actually holds. A sprite standing on x 40 got a key with 0, so the
first key moved it. The double-click now keys the value the row shows.

- trackHeld in src/core/model/animation.ts says what a row without keys
  holds. The field in the row header and SetAnimationKeyCommand both
  ask it, so they cannot drift apart again.
- A relative row still gets 0, a row with keys still takes the value of
  its curve at that frame, and a row whose target is gone still only
  gives a notice and no key.
- README and the wiki page Development name 2047 checks.

Tests:
- Unit animation: 87 green, 1 new. A sprite on x 40 with the override
  scale 2 gets keys with 40 and 2. Red with the old def.default.
- e2e 25 on melektaus-atx: ALL PASSED, 35 checks, 25i-ii new. The arm
  stands on x 40 and a double-click on frame 3 keys { frame: 3,
  value: 40 }.
- Gate on melektaus-atx: green, 304 files, 4703 tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 18:09:02 +02:00
adminandClaude Opus 5.5 8679dfbe65 Group timeline rows by object and edit values in the row (#408)
The rows of the animation timeline stood in the order they were keyed.
With several objects and properties the list was hard to read, and a
value could only be changed by picking a key and using the inspector.
The Play button also kept saying Pause after scrubbing had paused.

- Each object gets a header row with its name, indented by depth. The
  groups follow the scene hierarchy, the root first. Rows whose object
  is missing go to the end.
- Under an object the rows follow the order in which it lists its
  properties: X, Y, Z, then the order of the inspector. Only the view
  is sorted, the animation itself is unchanged and there is no undo
  step. After a move in the hierarchy the order is right by itself.
- The row head shows the same field as the inspector with the value at
  the playhead. Without keys it shows the value of the object, for a
  relative row the offset.
- A change in that field sets or changes the key at the playhead,
  selects it and is one undo step. A running playback pauses first.
- The Play/Pause button follows every change of the playing state, so
  scrubbing that pauses shows Play again.

Tests:
- Unit animation + timeline-view: 100 passed, 3 new.
- e2e 25 on melektaus-atx: ALL PASSED, 34 checks, 3 new (groups,
  order and values at frame 4, a typed 55 becomes a selected key on
  frame 4, Play then a click on the ruler shows Play and pauses).
- Gate on melektaus-atx: exit 0, 297 files, 4561 tests.
- tsc, eslint, Prettier clean.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 17:42:10 +02:00
adminandClaude Opus 5.5 7b1796baac Let a container animation reach alike-named objects in another one
game-maker / gate (push) Successful in 3m10s
game-maker / suite (push) Successful in 15m43s
An animation over a container aims its rows at template ids. Played on
another container, those ids are not there, so every row below the
root was skipped without a word. "Enemy - Hit" built over the Slime
flashed the slime's Sprite but never the skeleton's.

- When the template id is not found below the object, the runtime
  takes the names on the way to the row's target in the root and
  walks the same names below the object, level by level.
- The id still comes first, so the root's own instances bind as
  before.
- A name missing at some level, or taken twice, skips the row, as
  a gone target always did.
- The rule is the pure function targetNamePath in animation.ts. The
  AnimationSystem only walks the children.

Tests:
- animation.test.ts: 84 tests, all green (+2). One plays over a
  second container built alike, renamed and with the name twice.
  The other names a row two containers deep.
- Counter-check: with the name fallback off, the new runtime test
  fails (1 of 84).
- Project cbbf160bb7e8 (revision 14407), Demo scene: "Enemy - Hit"
  and "Enemy - Die" change Flash and scale on the skeleton's Sprite.
  Without the fallback the skeleton stays unchanged.
- The gate did not run before this commit. CI covers it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 11:16:42 +02:00
adminandClaude Opus 5.5 4407c02078 Keep who played an animation beside its player, not on it
d6fd000 wrote the behaviors that played an animation on the player
itself. A player is plain data, and those behaviors are entities of
the run with their parents and children. The actor smoke reads the
world as JSON and threw "Converting circular structure to JSON" as
soon as its graph had played a walk animation.

- The starters live in a WeakMap next to the player and are read
  with startersOf. The player carries no field for them any more.
- The animation system reads them from there when a pass ends. What
  a behavior hears does not change.
- Only the hook the check scripts read the world through was hit.
  The editor and a running game never serialise a player.

Tests:
- Gate on melektaus-atx: 219 files, 3400 tests, CHECK EXIT 0. It
  ran on the working tree, which also held the fix to e2e 47 and
  the kept panel sizes that are not committed yet. Seven of the
  tests belong to those.
- One new unit test: a player played by an asker that reaches back
  to its holder stays plain data, through JSON and structuredClone,
  and names the asker once. Its counter-probe bites.
- Smokes on melektaus-atx: 24, 19 and 21 checks, all pass. Before
  the fix the third stopped after 9 checks.
- e2e 6,17-26 on melektaus-atx: 480 checks, all pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 23:15:21 +02:00
adminandClaude Opus 5.5 d6fd000160 Let a behavior hear the end of an animation it played itself
game-maker / gate (push) Successful in 3m11s
game-maker / suite (push) Failing after 12m9s
A behavior that played an animation on the parent of its owner never
heard it finish. On animation finished listened to the owner and to
what stands below it, and the parent stands above.

- Play animation notes on the player which behavior asked for it.
- On animation finished hears, as before, every animation on the
  owner or below it, and now also every animation the behavior
  played itself, on whatever object. Its output object names that
  object.
- Two behaviors that both played the same animation on the same
  object each hear its end once.
- A behavior that is called by Run behavior or heard by an On node
  counts as the behavior it sits in.
- The description of the node and the engine prompt say so.

Tests:
- Gate on melektaus-atx: 218 files, 3392 tests, CHECK EXIT 0.
- Two new unit tests: the behavior hears the animation on the parent
  of its owner while a bystander does not, and two starters hear it
  once each.
- Both counter-probes bite.
- e2e 6,17-26 on melektaus-atx: 480 checks, all pass.
- No new e2e check, and nothing was looked at in the browser. The
  rule for called and heard behaviors has no test of its own.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 22:37:04 +02:00
adminandClaude Opus 5.5 c1cc80bb45 Add nodes that ask whether an animation still runs (#233)
A graph could start, pause and stop an animation and hear it finish,
but it could not ask in between whether one runs or how far it is.
Two data nodes answer that now.

- Animation playing takes an object and an animation and gives
  playing, progress and time left (s). It answers at the moment it is
  read: right after Play animation in the same pass it says on, and in
  the frame On animation finished fires it says off.
- With Animation at None it answers for the running animation started
  last on the object.
- Animation length gives the seconds of one pass, from the timeline
  and the frame rate alone.
- The rule is a pure function in src/core/model/animation.ts
  (playerStatus, leadingPlayer). The nodes reach an animation's
  length through a new field animation on GraphLookup.
- animationPort is exported so both nodes show the same picker as
  Play animation.

Decided in the issue: a Once (reset) counts as running while it blends
out, and progress and time left also hold for Loop and Ping-pong, at
the place of the playhead. On Ping-pong progress falls again on the
way back and time left counts to the next turn.

Decided here, the issue says nothing on it:
- A paused animation does not count as running. Progress and time
  left stand still.
- One that Stop animation fades out does not count as running, and
  its time left is 0.
- Time left of a Once (reset) includes its blend out. While it blends
  out progress is 1 and time left is the rest of the fade.

Finding, not fixed: on the node the name of the chosen animation
overlaps the output label progress in the same row. That comes from
the fixed node width in NodeView and was seen in the Fit view only.

Tests:
- Gate: 198 files, 2903 tests (2891 + 12), exit 0
- e2e 1-6: 282 checks passed, 06 has +2 for block 6z-ix
- Nine unit counter-probes fail, 1 to 4 tests each
- The browser counter-probe fails 6z-ix with ["false","false"]
- e2e total 1615 is computed, not measured in a full run. The smokes
  were not run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 10:57:31 +02:00
adminandClaude Opus 5.5 6491888f56 Stop offering private variables as new animation rows (#270)
A behavior variable switched private is hidden from the placed object
and from the Properties node, but the row dialog of an animation kept
offering it. Rows that exist already stay as they are, as decided in
the issue.

- offeredDefs in src/core/model/animation.ts is what the dialog offers
  now: what the target can animate, less private variables and less
  what another row animates already. The filter moved there from
  RowDialog.ts, so it can be tested without the editor.
- animatableDefsOf is unchanged. The label and the runtime read it, so
  a row on a variable that went private keeps playing and keeps its
  label.
- Deviation from the proposal: under Change target the row's own
  variable stays on offer, private or not. The dialog would open with
  no property and a disabled button otherwise. Other private variables
  are not offered there either.
- The command does not refuse a private variable. Only the dialog does
  not offer it.

Finding, not fixed: e2e 56 fails when it runs alone, in 56a-ii from
#257 (typing "9" did not arrive: focus is BODY). With 55 before it the
module passes, so a full run does not show it. It was not measured on
HEAD without this change.

Tests:
- Gate: 198 files, 2883 tests (2882 + 1), exit 0
- e2e 55,56: 27 checks passed, 56 has +56e
- e2e 25: 31 checks passed, it uses the same dialog
- Three unit counter-probes each fail the new test
- The browser counter-probe fails 56e, mood shows up in the dialog
- e2e total 1610 is computed, not measured in a full run

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 07:45:05 +02:00
adminandClaude Opus 5.5 fae827c087 Let an animation key Z so an object can pass behind another
The timeline offered X and Y as position rows but never Z. The list
of position rows was simply written without it when animations came
in with #97. A swinging weapon could therefore not go behind its
character.

- Z is the third position row, after X and Y, on every placed root
  or object. A scene and a behavior still offer no position.
- Play and preview already read and write any position row by name,
  so the one entry in POSITION_KEYS is all the code change.
- The draw order is sorted on every render, and play writes z back
  into the scene each frame. A keyed Z therefore takes effect at
  once. A blended 4.5 sits above 4 and below 5.

Tests:
- ./check.sh: 165 files, 2416 tests, CHECK EXIT 0.
- 3 new unit tests in tests/core/animation.test.ts: Z is offered
  after X and Y and not on a behavior or scene, a running game moves
  the object from behind to in front of another one, and play,
  scene write-back and preview agree on z.
- e2e 25: ALL PASSED, 29 checks. The new check reads X, Y, Z as the
  first rows of the row dialog. Check sum 1396 -> 1397.
- Counter-check: without z in the list all 3 new unit tests fail.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 22:08:00 +02:00
adminandClaude Opus 5 3427f84a55 A game can pause itself and set its own time scale (#167)
game-maker / gate (push) Successful in 2m11s
game-maker / suite (push) Successful in 7m48s
Until now only the editor toolbar could pause a running game. A pause
menu inside the game had no way to stop the world while its own graph
kept running, and there was no slow motion.

- Five new nodes: Pause game, Resume game, Is game paused, Set time
  scale and Time scale. Pause and resume take effect on the next frame.
- A game pause stops world time, physics, animations, particles and
  shader time. Only behaviors with the new "Run while paused" switch
  keep running.
- Set time scale 0 freezes the world but graphs keep running, as
  agreed in the issue.
- Play and Stop reset the time scale to 1, Load scene keeps it. The
  pause belongs to the scene, so Load scene and Restart unpause.
- The toolbar pause stays separate and also freezes the menu. When the
  game pauses itself the status line reads "The game paused itself".
- While paused, collision events are not reported, sounds keep
  playing, and the editor run ignores both nodes with a log line.

Findings:
- The first status line was longer than any before and got cut with
  an ellipsis. It is shorter now.
- The e2e harness did not know the key "p". It does now.

Tests:
- ./check.sh: 133 files, 1968 tests, exit 0 (before 132 / 1952)
- npm run build: exit 0
- GM_STEPS=18 fresh: ALL PASSED, 33 checks (before 26)
- 16 new unit tests: 8 catalog cases, game-pause.test.ts, editor run
- 7 new e2e checks in block 18i, driven by real keys in the game
- Six counter-checks all bite: behavior filter, collision lock, world
  time, editor run, clock reset and the game clock in the browser
- Check total 1217 in README is computed (1210 + 7), CI measures it

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-21 10:27:17 +02:00
adminandClaude Opus 5 2a390e52ff The scene view and the animation preview share one camera, the three form dialogs one base, and keys are found through one set of helpers (#85)
game-maker / gate (push) Successful in 1m36s
game-maker / suite (push) Successful in 5m41s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 14:19:53 +02:00
adminandClaude Opus 5 bf02c9e8b3 Timeline keys can be copied and pasted at the playhead, duplicated with Ctrl+drag, and frames inserted or removed from a right-click menu (#102)
game-maker / gate (push) Successful in 1m34s
game-maker / suite (push) Successful in 5m38s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 12:53:56 +02:00
adminandClaude Opus 5 195fc94e08 Animations blend out on Stop and at the end of Once (reset), and Play can restart a running animation (#106)
game-maker / gate (push) Successful in 1m33s
game-maker / suite (push) Successful in 5m37s
Stop animation gets a blend (s) port that fades the animation back to what
lies underneath. Play animation gets blend out (s) for Once (reset) and a
restart port that starts a running, paused or fading animation from frame 0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 11:50:10 +02:00
adminandClaude Opus 5 8a377d9239 Play animation's blend is given in seconds, not in frames of the new animation (#106)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 11:14:15 +02:00
adminandClaude Opus 5 04dc254b3d Play animation can blend into the new animation over a number of frames (#106)
game-maker / gate (push) Successful in 1m33s
game-maker / suite (push) Successful in 5m34s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 10:03:53 +02:00
adminandClaude Opus 5 beeea355d9 Animation rows can target behavior variables and shader parameters (#104)
game-maker / gate (push) Successful in 1m33s
game-maker / suite (push) Successful in 5m38s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 09:04:29 +02:00
adminandClaude Opus 5 b278605c53 Keys carry an easing for the span to the next key: linear, step, ease in, ease out, ease in-out (#101)
game-maker / gate (push) Successful in 1m32s
game-maker / suite (push) Successful in 5m36s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 07:27:44 +02:00
adminandClaude Opus 5 cc14988338 Ping-pong turns on the last key, and stepped rows hold the key they last passed (#112)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 07:08:44 +02:00
adminandClaude Opus 5 f68941fa3a An animation reaches the scene's own background and objects inside placed containers (#109)
game-maker / gate (push) Successful in 1m33s
game-maker / suite (push) Successful in 5m37s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 06:48:21 +02:00
adminandClaude Opus 5 4ed2edd0f0 An animation is a value a graph can pick, and playing a running one makes it win again (#97)
Play, Pause and Stop animation take the animation on a port instead of a
fixed setting, fed by the new Animation literal or a list item tagged
`$animation`. Playing an animation already running keeps its frame but
counts it as started last, so turning back to a walk that never stopped
shows that walk. The actor demo walks through one Play animation over
`ItemAt(walk, facing)` again and stops every animation to stand.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 20:47:31 +02:00
adminandClaude Opus 5 193378e6a9 An animation is a timeline of rows over one asset, played on an object rather than worn as a picture (#97)
Part (a) of the animation rework: model, commands, saving, runtime and nodes.

- AnimationAsset holds a root (sprite, text, container or scene) and rows; a row keys one
  property of the root or of an object inside it, keys sit on whole frames.
- The last key decides the length (its frame counts as a frame of its own). The playhead runs
  between frames, so numbers and colors blend every tick even at 10 fps; everything else holds.
- Playback gains `once-reset`, which puts back what was there before the start. A row can be
  relative, adding to where its target stood.
- Play / Pause / Stop animation act on an object; each instance plays on its own, the later
  start wins on a shared property, and a behavior writing the same property still writes last.
- A sprite no longer wears an animation through `texture`. "Create animation from texture"
  builds a sprite plus a texture row keyed frame by frame instead.
- The Animation tab shows a placeholder until the timeline editor (part b) lands.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 20:02:25 +02:00
rootandClaude Opus 5 1135bf4562 Play sprite animations from an animation asset worn through texture
An animation asset is a list of pictures (whole textures or frames) with
frames per second and Loop/Once/Ping-pong playback. A sprite wears it
through its texture property, so the picker, instance overrides and the
Properties node work unchanged; there is no dedicated play node.

A different animation starts from its first frame, the same one set again
runs on, so a graph may choose Run or Idle every frame. The clock lives on
the entity, not in props, which the world writes back as overrides. The
AnimationSystem ticks before behaviors, and On animation finished fires
once when a Once animation reaches its end.

The Animation tab plays a preview above a frame strip: reorder by dragging,
Delete, duplicate, + Frames, pause/resume, and wheel zoom about the pointer
with pan and Fit. Scene and Container tabs show the first frame; edit mode
has no clock. Right-clicking a texture creates an animation from its frames.

Button now measures its label with the bold font it draws in, which cut
the Animation tab and red dialog buttons short.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-11 23:52:34 +02:00