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).
This commit is contained in:
@@ -144,18 +144,25 @@ BoardAnim :: struct {
|
||||
active: bool;
|
||||
elapsed: f32;
|
||||
move: AnimMove;
|
||||
// Highest 1-based cascade round whose ascending combo cue has already played,
|
||||
// so the frame loop's per-round SFX is edge-triggered: a round's cue fires once,
|
||||
// when its clear begins, never re-fired every frame. Reset whenever a move
|
||||
// (re)starts; advanced by the frame loop as rounds clear.
|
||||
cascade_fired: s64;
|
||||
|
||||
init :: (self: *BoardAnim) {
|
||||
self.active = false;
|
||||
self.elapsed = 0.0;
|
||||
self.move.legal = false;
|
||||
self.move.rounds = List(AnimRound).{};
|
||||
self.cascade_fired = 0;
|
||||
}
|
||||
|
||||
begin :: (self: *BoardAnim, m: AnimMove) {
|
||||
self.move = m;
|
||||
self.elapsed = 0.0;
|
||||
self.active = true;
|
||||
self.cascade_fired = 0;
|
||||
}
|
||||
|
||||
// Total wall-clock length: the swap segment plus a clear+fall pair per round.
|
||||
@@ -190,6 +197,22 @@ BoardAnim :: struct {
|
||||
}
|
||||
}
|
||||
|
||||
// Per-round cascade-cue timing (P10.10): how many cascade rounds have BEGUN their
|
||||
// clear (pop) by `elapsed`, on the SAME swap→(clear,fall)* timeline `phase` walks.
|
||||
// Round k (0-based) starts clearing at SWAP_ANIM_DUR + k*(CLEAR_ANIM_DUR +
|
||||
// FALL_ANIM_DUR), so this is the count of rounds whose ascending combo cue should
|
||||
// have fired by now (clamped to the move's round count). The frame loop diffs it
|
||||
// against `BoardAnim.cascade_fired` to play one cue per newly-cleared round. Pure +
|
||||
// headless so the per-round playback is snapshot-testable without audio.
|
||||
cascade_rounds_started :: (elapsed: f32, num_rounds: s64) -> s64 {
|
||||
if num_rounds <= 0 { return 0; }
|
||||
if elapsed < SWAP_ANIM_DUR { return 0; }
|
||||
seg := CLEAR_ANIM_DUR + FALL_ANIM_DUR;
|
||||
started := cast(s64) ((elapsed - SWAP_ANIM_DUR) / seg) + 1;
|
||||
if started > num_rounds { return num_rounds; }
|
||||
started
|
||||
}
|
||||
|
||||
// Input gate: the board accepts a new swipe/tap gesture only when no move
|
||||
// animation is in flight. The view checks this at gesture START (mouse_down),
|
||||
// not at commit (mouse_up), so a gesture begun while a timeline is playing never
|
||||
|
||||
Reference in New Issue
Block a user