P17.2: organic fall — per-column stagger (cascade pour)

render_fall now offsets each COLUMN's drop START by a small bounded delay
(fall_stagger_t) so a refilled/collapsed row pours in as a left-to-right cascade
instead of every gem snapping down in one flat lockstep row. Column col waits
FALL_STAGGER_MAX (0.30) * col/7 of the fall window, then falls over the remaining
1 - 0.30, with that local progress fed through ease_in_cubic so each column still
accelerates under gravity within its own window.

Bounded by construction: the last column lands EXACTLY at t=1 and every earlier
column strictly before it, so no gem is ever left mid-air at the segment end — the
seam to the next round / settled board stays invisible and move.final is untouched.

FALL_ANIM_DUR (0.22s) and the timeline helpers (phase/total/cascade_rounds_started)
are unchanged, so the per-round cascade-cue timing snapshots don't churn and live
per-round audio is unaffected. Render-only — no board.sx model change.

tests/easing.sx pins fall_stagger_t: f(0)=0, f(1)=1 across all columns (no gem
unlanded), per-column monotonicity, and the mid-fall cascade ordering (each later
column strictly behind the one before). tests/anim_plan.sx (final==model,
contiguity) stays green.

Golden goldens/p17_stagger.png: M3TE_FX=11 (depth-5 cascade, seed 1337) pinned at
M3TE_ANIM_TIME=1.91 — round 4 refills columns 2-7 by one cell each, so the top row
reads as a left-to-right staircase (vs the pre-stagger flat row in p17_fall.png).
This commit is contained in:
swipelab
2026-06-06 11:35:36 +03:00
parent 8d4e7acd2b
commit 02d856275c
6 changed files with 98 additions and 3 deletions

View File

@@ -97,6 +97,26 @@ bad_swap_bounce :: (t: f32) -> f32 {
BADSWAP_LUNGE_AMP * (1.0 - spring(u))
}
// Per-column fall stagger (P17.2): within the fall window, each column starts its
// drop at a small BOUNDED delay so a refilled/collapsed row pours in as a cascade
// instead of every gem snapping down in one flat lockstep row. Column `col` waits
// FALL_STAGGER_MAX * col/(BOARD_COLS-1) of the window, then falls over the
// remaining `1 - FALL_STAGGER_MAX`, so the LAST column lands EXACTLY at t==1 and
// every earlier column lands strictly before it — no gem is ever left mid-air when
// the segment ends (the seam to the next round / settled board stays invisible).
// Returns the column's LOCAL 0..1 progress; render_fall feeds it through
// ease_in_cubic so each column still accelerates under gravity within its window.
// `tests/easing.sx` pins f(0)=0, f(1)=1, monotonicity, and the cascade ordering.
FALL_STAGGER_MAX :f32: 0.30;
fall_stagger_t :: (t: f32, col: s64) -> f32 {
delay := FALL_STAGGER_MAX * (cast(f32) col / cast(f32) (BOARD_COLS - 1));
window := 1.0 - FALL_STAGGER_MAX;
lt := (t - delay) / window;
if lt <= 0.0 { return 0.0; }
if lt >= 1.0 { return 1.0; }
lt
}
// One recorded cascade round. `before` is the board at the round's start (the
// swapped board for round 0, the previous round's `after` otherwise — never has
// holes). `matched` flags the cells cleared this round (they scale out). `src`