P7.2 fix: reset per-gem landing state on restart

The restart button (BoardView.do_restart) reseeded the model and dropped
selection/drag/anim/FX, but left GemMotion.land_at carrying the prior move's
landing stamps. A restart fired right after a terminal cascade therefore
replayed that move's squash-bounce on the freshly seeded board instead of
showing a clean resting pose.

Factor the landing reset into GemMotion.reset_landings (init now delegates to
it) and call it from do_restart, so a restart returns every cell to its
resting idle pose. The idle clock keeps running, so the always-on idle simply
resumes from rest.

Regression: tests/gem_pose.sx section 7 stamps a cell mid-squash, asserts it
is squashing, then asserts reset_landings returns every cell to rest while
leaving the clock untouched. Fails on the pre-fix (no-op reset) behaviour,
passes after. Gate green: ios-sim build + 18/18 logic tests.
This commit is contained in:
swipelab
2026-06-05 15:17:37 +03:00
parent 5be379f180
commit 0f84b09f7b
4 changed files with 42 additions and 2 deletions

View File

@@ -10,4 +10,6 @@ land_start_rest true land_end_rest true land_mid_wobbles true
clear_start_full true clear_end_gone true clear_overshoots true
== gem motion land bookkeeping ==
motion_init true motion_no_land true motion_fresh_land true
== gem motion restart resets landings ==
restart_pre_squashing true restart_post_rest true restart_clock_kept true
ok: per-gem animation rests at t=0 and stays bounded

View File

@@ -96,6 +96,33 @@ main :: () -> s32 {
if !no_land { fails += 1; }
if !fresh_land { fails += 1; }
// 7. Restart resets the landing bounce. The restart button (BoardView.
// do_restart) reseeds the model AND must clear the per-gem landing state,
// or a restart fired right after a terminal cascade carries that move's
// squash onto the freshly seeded board. reset_landings is the factored
// reset do_restart calls: a cell stamped a moment ago is mid-squash, and
// after reset_landings every cell is back at rest — while the idle clock
// is deliberately left running (idle resumes from its normal phase).
print("== gem motion restart resets landings ==\n");
r : GemMotion = ---;
r.init();
r.clock = 5.0;
r.stamp_land(3);
r.stamp_land(42);
r.clock = 5.08; // 0.08s past impact: well inside LAND_DUR, so mid-squash.
pre_squashing := fabs(land_squash(r.land_local(3))) > 0.01
and fabs(land_squash(r.land_local(42))) > 0.01;
r.reset_landings();
post_rest := land_squash(r.land_local(3)) == 0.0
and land_squash(r.land_local(42)) == 0.0
and land_squash(r.land_local(0)) == 0.0;
clock_kept := r.clock == 5.08;
print("restart_pre_squashing {} restart_post_rest {} restart_clock_kept {}\n",
pre_squashing, post_rest, clock_kept);
if !pre_squashing { fails += 1; }
if !post_rest { fails += 1; }
if !clock_kept { fails += 1; }
if fails == 0 {
print("ok: per-gem animation rests at t=0 and stays bounded\n");
return 0;