Files
m3te/tests/cascade_rounds.sx
swipelab 51b3397ade P10.10: play one ascending combo cue per cascade round
For combos, play a sound for each match ascending (Candy-Crush cascade run):
as a chain resolves, EACH successive round plays the next higher cue
(combo1, combo2, … clamped at combo5) instead of a single combo cue keyed
to the final cascade depth at commit.

- board_anim.sx: add `BoardAnim.cascade_fired` (edge-trigger high-water mark,
  reset on init/begin) and the pure `cascade_rounds_started(elapsed, n)` helper
  — how many rounds have begun clearing on the swap→(clear,fall)* timeline.
- main.sx: in the frame loop, diff `cascade_rounds_started` against
  `cascade_fired` and play one ascending cue per newly-cleared round, once each,
  gated on a real multi-round chain (rounds >= 2). Additive; never touches
  board/score/move state.
- board_view.sx: drop the single `sfx_cascade(final depth)` at commit; keep
  `sfx_swap` / `sfx_match` (and win/lose) exactly as before.
- tests/cascade_rounds.sx: headless snapshot of the per-round timing + the
  ascending combo1..combo5 run with the combo5 clamp.

Sim (M3TE_FX=11, depth-5): log show shows combo1→combo2→combo3→combo4→combo5
at successive timestamps ~0.36s apart (= CLEAR+FALL per-round spacing).
2026-06-06 08:37:46 +03:00

65 lines
3.3 KiB
Plaintext

// P10.10 — Per-round cascade-cue timing snapshot: prove the frame loop plays ONE
// ascending combo cue PER cascade round, edge-triggered as each round's clear
// begins — combo1, combo2, … clamped at combo5, an audible ascending run — NOT a
// single combo cue keyed to the final cascade depth. The pure timing helper
// `cascade_rounds_started` (board_anim.sx) reports how many rounds have begun
// clearing by a given `elapsed` on the SAME swap→(clear,fall)* timeline the view
// animates; the round→cue mapping reuses `cascade_cue_index`/`cascade_cue_name`
// (audio.sx), the exact functions `sfx_cascade` plays. Simulating the frame loop
// over a 5-round chain yields the locked combo1..combo5 run. Links headless like
// tests/anim_plan.sx (board_anim pulls no GL) + tests/cascade_cue.sx (audio alone
// links). Failure is a non-zero exit code.
#import "modules/std.sx";
#import "board_anim.sx";
#import "audio.sx";
main :: () -> s32 {
print("== per-round cascade cue timing ==\n");
// `cascade_rounds_started` = how many cascade rounds have BEGUN clearing by
// `elapsed`, on the swap(0.16s)→[clear(0.14s),fall(0.22s)] per-round timeline.
// Round k (0-based) starts clearing at 0.16 + k*0.36; sampled safely INSIDE
// each round window so the integer step is unambiguous. Locked for 5 rounds.
print("-- started-count across a 5-round chain --\n");
rounds : s64 = 5;
print("e=0.00 -> {}\n", cascade_rounds_started(0.00, rounds));
print("e=0.10 -> {}\n", cascade_rounds_started(0.10, rounds));
print("e=0.20 -> {}\n", cascade_rounds_started(0.20, rounds));
print("e=0.50 -> {}\n", cascade_rounds_started(0.50, rounds));
print("e=0.55 -> {}\n", cascade_rounds_started(0.55, rounds));
print("e=0.90 -> {}\n", cascade_rounds_started(0.90, rounds));
print("e=1.30 -> {}\n", cascade_rounds_started(1.30, rounds));
print("e=1.70 -> {}\n", cascade_rounds_started(1.70, rounds));
print("e=5.00 -> {}\n", cascade_rounds_started(5.00, rounds));
// Edge-triggered playback: stepping the clock, each rising edge of the
// started-count plays the NEXT round's cue — combo1..combo5, once each,
// ascending. This IS the loop main's frame loop runs; the emitted run is the
// locked acceptance ordering.
print("-- ascending per-round run --\n");
fired : s64 = 0;
elapsed : f32 = 0.0;
while fired < rounds {
started := cascade_rounds_started(elapsed, rounds);
while fired < started {
fired += 1;
print("round {} -> {}\n", fired, cascade_cue_name(cascade_cue_index(fired)));
}
elapsed += 0.02;
}
// Non-cascade guards: a 1-round (single match) and 0-round (illegal) move. The
// frame loop gates the run on rounds>=2, so neither plays a combo run; the
// helper still reports the lone/zero round so the gate lives in the caller.
print("-- non-cascade --\n");
print("1-round@end {}\n", cascade_rounds_started(5.0, 1));
print("0-round@end {}\n", cascade_rounds_started(5.0, 0));
// Deep chain: the cue tail clamps at combo5 for round >= 5 (cascade_cue_index).
print("-- deep-chain cue clamp --\n");
for 1..8: (r) { print("round {} -> {}\n", r, cascade_cue_name(cascade_cue_index(r))); }
print("ok: one ascending combo cue per cascade round, clamped at combo5\n");
return 0;
}