7 Commits
Author SHA1 Message Date
adminandClaude Opus 5.5 e53121a0bb Let a behavior hear a song, pattern or sound come to its end (#373)
feuerwerk / gate (push) Successful in 5m56s
feuerwerk / suite (push) Failing after 20m55s
A game should be able to start the next piece of music when one is
over, or wait for a sound before it goes on. Until now a behavior
could start and stop music and sounds, but never learn that they had
ended.

- Two new events: On music finished with the fields Song and Pattern
  and the input playback, and On sound finished with the field Sound
  and the input voice. Both give out what ended and stopped.
- Two new data nodes, Music playing and Sound playing, ask whether
  something is still heard.
- Over means nothing is heard any more: no tail of the effects, and
  a fade-out has run down.
- stopped is on for Stop song, pattern or sound, when the behavior
  that owns it ends and on a scene change. For a sound also when a
  newer voice needs its place or the file does not load.
- Only what the Play nodes of this run started is heard, across
  scene changes too. Previews in the tabs, compositions and notes of
  instruments do not count.
- Switch song ends nothing. The number goes on, and the end is told
  under the song heard last.
- Playbacks and the SoundLibrary tell each end once to a new
  PlayedEnds per run. The AudioSwitch passes them on from both sinks.

Found and fixed: a pattern whose last bar ends in silence counted as
done up to one planning window before its end. In e2e 65 the next
pattern started after 1.962 s instead of 2 s. Scheduler.done now also
wants the playhead to have reached the end. Cleaning up done
playbacks therefore comes a few tenths of a second later. One test
that expected the early end is adjusted.

Tests:
- Unit: 23 new (played-ends 12, sound-library 5, node catalog 5,
  audio-switch 1), all green.
- Counter-probes: 19 bends, all red. One was green at first for want
  of a case, which is added now.
- e2e 65 on atx: ALL PASSED, 78 checks. New 65k6: the event came
  once with stopped off, and the second pattern started 2.051 s after
  the stinger. Stop song gave exactly one end with stopped on, and
  the switch from 65k3 gave none.
- Gate on atx: CHECK EXIT 0, 337 files, 5222 tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 14:48:16 +01:00
adminandClaude Opus 5.5 b17a924157 Let a behavior play, pause and seek a video and hear it end (#410)
feuerwerk / gate (push) Successful in 5m58s
feuerwerk / suite (push) Failing after 17m35s
A game should be able to stop a video on a sprite, jump to a spot in
it and start it again, and react when it has run out. Until now a
video on a sprite either ran free on its own or followed a
composition, and no behavior could touch it.

- Five new nodes: Play video, Pause video and Seek video (time in s
  from the clip start), On video finished with a Video field, and the
  data node Video time with time, length and playing.
- Play, Pause and Seek act on the owner when object is not linked.
- On video finished with an empty field hears every video. A chosen
  video also hears its clips.
- A new VideoSystem keeps a clock for every sprite with a video. It
  counts from the moment the sprite appears, as long as it is on. For
  a Driven sprite the clock is its Video time. A paused game holds all
  clocks.
- A free sprite that one of the nodes touches gets the clock
  "steered" at run time. It then has its own player, and other
  sprites with the same clip play on. Pause of the game and sound go
  the existing Driven way.
- On a Driven sprite and in the editor run the nodes do nothing and
  write a line to the log.
- Until its own player has a frame, a steered sprite shows the frame
  of the shared player. Without that it would blink out on the first
  Pause video.
- Play at the end of a clip without loop starts it from the start.
  The issue does not say, this is my choice.

Found, not fixed: with a Driven sprite in the scene the run waits up
to 3 s for its frame at the start. The world does not tick then, but
the free videos already play. Their clocks lag behind the picture by
that wait, so On video finished comes that much late.

Tests:
- Unit: 23 new (video-steer 16, node catalog 7), affected groups
  4820/4820.
- Counter-probes: 16 bends, all red.
- e2e 66 on atx: ALL PASSED, 39 checks. New 66s: Pause at 0.15 s
  held its own player at 0.2 s, the picture stayed red for 1 s. Seek
  landed on 0.5 s exactly, green, held. Play showed green, blue,
  white, and white stayed at the end. On video finished came once.
  Without the clock "steered" all three 66s checks are red.
- Gate on atx: CHECK EXIT 0, 336 files, 5199 tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 10:13:52 +01:00
adminandClaude Opus 5.5 a6a41b6b69 Let a behavior follow the beat, bar and markers of the music (#338)
feuerwerk / gate (push) Failing after 15m41s
feuerwerk / suite (push) Canceled after 18m24s
Stage 2 of #338. A game that syncs to its music has to know where the
music is heard. This stage reads that on the audio clock and hands it
to behaviors as events and as a position.

- New event nodes On beat (playback, bar, beat, late), On bar
  (playback, bar, late) and On song marker (playback, marker, name,
  late).
- New data node Song position with bar, beat, fraction, seconds and
  playing.
- The nodes have the fields Song and Pattern. With neither picked,
  every playback counts. A linked playback input picks that one.
  Compositions do not count.
- The Marker field offers the markers of all songs as Song / Marker,
  as a sound field offers its clips.
- Everything follows what is heard: the audio clock minus the output
  latency. That holds for baked songs and for Play pattern too.
- Only the last beat since the frame before fires, and only when it is
  at most half a beat late, 0.15 s at most. Bars and markers alike.
- A copied song gets markers with ids of its own, so a Marker field
  never names two songs.
- The audio clock is only asked while something plays. Asked in every
  frame, it made the AudioContext without a user gesture (#436).

Tests:
- beats 14 and music-events 6 new, 4 in song-play, 2 in song-markers,
  2 catalog cases, the touched groups 756/756
- 16 counter-probes, all compile, all red
- e2e 65 on atx: ALL PASSED, 71 checks, 65k2 new: 11 beats 1.1 to 2.4
  and 2.1 again, none missing or twice, across the pause too. late
  0.015 to 0.027 s. Counter-probe without the last position: red
- gate on atx: CHECK EXIT 0, 334 files, 5121 tests

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 19:48:51 +01:00
adminandClaude Opus 5.5 0334d2508d Place compositions in scenes and play them from behaviors (#417)
A composition can now sit in a scene like a container and play there.
Its items become real objects while they run, so behaviors, colliders
and events work on them as on any other object.

- A composition is placeable with its own symbol, transform and render
  layer. The scene view shows its picture at frame 0. A click on that
  picture selects the placement, not a single item.
- CompositionSystem spawns each item as an object under the placement
  when it begins, with the id <placement>~<item>. On start and the
  other events run. The object is destroyed when the item ends.
- The top track lies in front no matter which item started first.
  World.arrange() orders the entity list and the scene objects.
- Clips and driven videos follow the same rules as compositionAt().
  Loop, restart, stop and the game pause work.
- New nodes: Play composition (with restart), Stop composition and the
  event On composition finished. They were planned for step 4 but come
  now, because without autoplay nothing could play otherwise.
- The editor run ignores Play composition and says so in the log. It
  writes into the saved project, and the items would stay behind.
- contentId() moved to instance.ts. The animation system uses it for
  nested ids too.
- Compositions get no "Sort with parent" for now.

Tests: the gate passed with 301 files and 4643 tests, 18 more than
step 1. 13 of them are in tests/core/composition-play.test.ts. The
first gate run failed in event-order.test.ts, which counts the event
nodes and now finds 19. Five counter-checks each turned exactly their
test red. Two order tests stayed green without the fix at first and
were rebuilt. e2e 68 is new and passed with 5 own checks. Its
counter-check without arrange() failed as expected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 07:06:33 +02:00
adminandClaude Opus 5.5 ded0022903 Run behaviors on a fixed step with an On fixed update node
Adding up the frame's dt in On update makes anything that counts in
steps depend on the frame rate. A pitch sweep sounds a little
different on every run. On fixed update runs 60 times per second of
game time with dt exactly 1/60, so the same graph gives the same
result every time.

- fixedSteps in src/core/time.ts is the one rule for the fixed step:
  spend the frame in steps of 1/60, keep the rest, catch up 8 steps
  at most after a hitch.
- The physics now counts its steps with fixedSteps too. It behaves
  as before, and On fixed update runs exactly as often as the engine
  steps.
- The steps run after On mouse wheel and before On update, each as a
  kind of its own with the usual Order setting.
- Each step gets its own context, so a node inside that reads dt
  (Time, Move, Apply force) gets 1/60 rather than the frame.
- A paused game has no steps, also for a behavior that runs while
  paused.
- The engine still takes all of a frame's steps before the behaviors
  run. A force set in On fixed update acts from the next frame on.

Tests:
- fixed-update.test.ts (new): 5 tests. Step counting, uneven frames
  adding up to 60 steps a second, the hitch cap, two steps at 30 Hz
  before On update with dt 1/60 for the event and for Time, one step
  every other frame at 120 Hz, none while paused.
- event-order, node-catalog, physics-system, game-pause: 430 tests
  together with the new file, all green.
- Counter-check: with the frame's dt inside a step and without the
  pause guard both world tests fail.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 21:46:15 +02:00
adminandClaude Opus 5.5 46c620ae3b Give a behavior more ways in and out, and hear it as an event (#294)
game-maker / gate (push) Successful in 2m48s
game-maker / suite (push) Failing after 13m34s
A behavior run by another one had a single flow port at its Start
and at its End, so it could not branch for its caller. And it could
not be used as an event: a call needs a pulse at its flow input,
and the events of a called graph never fire. An input like
"Confirm" (Space or Enter) had to be built again in every graph.

- A declared port of a Start or an End node can be of the kind
  Flow. It sits above the values and shares their names, exec stays
  reserved. Only these two nodes declare one, a struct does not.
- Run behavior shows a flow port of the Start as a further flow
  input. A call arriving through it leaves the Start through the
  output of that name and not through exec.
- Run behavior shows a flow port of the End as a further flow
  output. The call goes on through the one the End was reached by
  last, and then through exec. exec fires always, as before.
- On behavior is a new event node without a flow input. The
  behavior it names runs at the node for as long as the behavior
  around it runs, with all of its own events and one set of
  variables per node.
- Its outputs are those of the End. It fires once the inner pulse
  is through, by the output the End was reached by last, exec only
  for End.exec. A pulse that never reaches the End is not seen.
- The value ports of the Start are its inputs, read anew for each
  inner pulse. The Start itself never fires there.
- It nests to any depth. Under a Run behavior it stays silent. A
  behavior hearing itself, however far round, is not started and
  the node says "A behavior cannot listen to itself".
- The palette offers every other behavior with an End once more as
  "On <name>". A node naming one without End says so.
- An On node removed while the game runs ends its behavior with On
  disable and On destroy. The sounds of the entity end only with
  the behavior placed on it.
- Renaming and deleting a declared port follows the edges at On
  behavior nodes as it does at Run behavior nodes.
- The flow outputs of the Start of a behavior placed on an object
  never fire, like its exec.
- The engine prompt of the benchmark names flow ports and On
  behavior.

Tests:
- Gate on melektaus-atx right before this commit: 215 files, 3319
  tests (3278 + 41), exit 0.
- e2e 19 on melektaus-atx: 39 checks (+4), all pass. e2e
  1-6,17-24,26,36,51: 628 checks, all pass.
- 19 unit tests in flow-ports.test.ts, 19 in on-behavior.test.ts
  on a real world, the rest in the node catalog.
- Four new e2e checks in module 19: a flow port declared by mouse
  at the End, On Confirm in the graph, the call leaving through the
  further output while playing, Enter and Space on a real keyboard.
- 19 unit counter-probes all fail their tests. One did not at first
  (a removed node kept its instance), a test was added. Two browser
  counter-probes fail their checks.
- One screenshot looked at: On Confirm with exec and pressed, the
  second flow pin at the calling node.
- The e2e sum of 1680 is counted (1676 + 4), the full suite and
  the smokes did not run.
- Not seen in the browser: flow inputs at the Start, nesting, the
  ring, values through On behavior, the palette entry. No test
  holds that the sounds of the entity go on when a heard behavior
  ends.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 19:33:34 +02:00
adminandClaude Opus 5.5 5fbc6eb088 Set the order in which event nodes run across behaviors (#264)
game-maker / gate (push) Successful in 3m42s
game-maker / suite (push) Failing after 11m7s
Which of two events ran first depended on hidden lists: the order of
the behavior instances and the order in which nodes were created. A
graph had no way to say that one On update must run before another,
and an On update of one behavior could run before a key event of
another.

- A frame now runs event kind by event kind over all behaviors: On
  disable and On enable, key down, key up, key held, timer, collision
  enter, stay and exit, animation finished, click, mouse wheel and at
  last On update.
- All 17 event nodes have a field Order, a whole number, default 0,
  negative allowed. Within one kind a lower Order runs earlier, no
  matter which behavior holds the node.
- On a tie the current hierarchy decides from top to bottom, then the
  order in which the nodes were created. The old rule that a behavior
  enabled later goes to the end is gone.
- The node shows Order: 5 only when the value is not 0, so existing
  graphs look the same.
- The rule lives as pure functions in core/graph/eventOrder.ts. The
  interpreter can now fire a single event node.

Changes in existing projects:

- An object destroyed in a frame gets no further event in that frame.
  On disable and On destroy still come at the end of the frame.
- New instances get all their On start first, then all On enable.
  Teardown runs all On disable first, then all On destroy.
- State events follow right after the event kind that caused them.
- Four unit tests had fixed the old order and are adjusted:
  two in behavior-events, one in physics-system, one in key-repeat.

Tests:

- ./check.sh: 191 files, 2784 tests, CHECK EXIT 0.
- GM_STEPS=1-6: ALL PASSED, 280 checks.
- Full e2e run (local): ALL PASSED, 1551 checks, measured.
- tests/core/event-order.test.ts: 11 tests for the rule, the field on
  all 17 nodes, order across behaviors, key before update, two nodes
  in one graph, On start, hierarchy after late enable and reorder at
  runtime, state events, save and load.
- e2e 6z-viii, 2 checks with real mouse and keyboard: Order -1 set in
  the inspector shows on the node, and a run logs B, A although B is
  lower in the hierarchy.
- Counter-checks: 5 in unit tests and 1 in the browser, all fail as
  expected. The originals were restored and checked with cmp.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 09:21:31 +02:00