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:
@@ -487,14 +487,17 @@ BoardView :: struct {
|
|||||||
|
|
||||||
// Restart action behind the banner's button: reseed the SAME starting level
|
// Restart action behind the banner's button: reseed the SAME starting level
|
||||||
// through the model (board.restart) and drop every transient view layer
|
// through the model (board.restart) and drop every transient view layer
|
||||||
// (selection, in-flight drag, move animation, FX) so the board returns to a
|
// (selection, in-flight drag, move animation, FX, and the per-gem landing
|
||||||
// clean in_progress state.
|
// bounce) so the board returns to a clean, resting in_progress state. Without
|
||||||
|
// the motion reset a restart fired right after a terminal cascade would carry
|
||||||
|
// that move's landing squash onto the freshly seeded board.
|
||||||
do_restart :: (self: *BoardView) {
|
do_restart :: (self: *BoardView) {
|
||||||
self.board.restart(self.seed);
|
self.board.restart(self.seed);
|
||||||
self.sel.clear();
|
self.sel.clear();
|
||||||
self.drag.clear();
|
self.drag.clear();
|
||||||
if self.anim != null { self.anim.init(); }
|
if self.anim != null { self.anim.init(); }
|
||||||
if self.fx != null { self.fx.clear(); }
|
if self.fx != null { self.fx.clear(); }
|
||||||
|
self.motion.reset_landings();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -103,6 +103,14 @@ GemMotion :: struct {
|
|||||||
init :: (self: *GemMotion) {
|
init :: (self: *GemMotion) {
|
||||||
self.clock = 0.0;
|
self.clock = 0.0;
|
||||||
self.pinned = false;
|
self.pinned = false;
|
||||||
|
self.reset_landings();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drop every landing stamp back to the never-landed sentinel so no cell
|
||||||
|
// carries a squash-bounce. `restart` calls this so a reseeded board starts at
|
||||||
|
// its resting pose instead of replaying the prior move's landing wobble; the
|
||||||
|
// idle clock keeps running, so the always-on idle simply resumes from rest.
|
||||||
|
reset_landings :: (self: *GemMotion) {
|
||||||
for 0..BOARD_CELLS: (i) { self.land_at[i] = -1000.0; }
|
for 0..BOARD_CELLS: (i) { self.land_at[i] = -1000.0; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
clear_start_full true clear_end_gone true clear_overshoots true
|
||||||
== gem motion land bookkeeping ==
|
== gem motion land bookkeeping ==
|
||||||
motion_init true motion_no_land true motion_fresh_land true
|
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
|
ok: per-gem animation rests at t=0 and stays bounded
|
||||||
|
|||||||
@@ -96,6 +96,33 @@ main :: () -> s32 {
|
|||||||
if !no_land { fails += 1; }
|
if !no_land { fails += 1; }
|
||||||
if !fresh_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 {
|
if fails == 0 {
|
||||||
print("ok: per-gem animation rests at t=0 and stays bounded\n");
|
print("ok: per-gem animation rests at t=0 and stays bounded\n");
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user