P16.2: organic illegal swap — springy bounce-back (+ M3TE_BADSWAP hook)
render_swap's rejected-swap branch now drives the two gems with a P15.1 spring-based bounce-back (bad_swap_bounce): a quick lunge toward the neighbour, then a damped spring home that overshoots rest by a bounded amount and settles to exactly 0. f(0)=f(1)=0, so the move stays purely visual — board byte-identical to pre-swap, no score/move spent. - board_anim.sx: add bad_swap_bounce envelope (lunge via ease_out_cubic, settle via 1 - spring(u)); BADSWAP_LUNGE_T/AMP constants. - board_view.sx: replace the linear ping-out illegal branch with the bounce. - main.sx: add illegal_swaps (complement of legal_swaps, same row-major order) + the startup-only M3TE_BADSWAP=n capture hook; mirrors M3TE_FX. - tests/easing.sx: append bounce-envelope assertions (endpoints, single lunge peak + location, damped settle); regenerate expected snapshot. - README.md: document the M3TE_BADSWAP recipe + goldens/p16_badswap.png. Gate green: ios-sim build links, 22 logic snapshots pass (anim_plan model invariants unchanged; SWAP_ANIM_DUR untouched so cascade-cue snapshots do not churn).
This commit is contained in:
@@ -399,7 +399,8 @@ BoardView :: struct {
|
||||
|
||||
// Swap segment: the board sits still (pre-swap) except the two swapped gems,
|
||||
// which slide between their cells. A legal swap slides fully (a→b, b→a); an
|
||||
// illegal one pings out to the neighbour and back, ending where it started.
|
||||
// illegal one lunges toward the neighbour and springs back to rest, ending
|
||||
// exactly where it started.
|
||||
render_swap :: (self: *BoardView, ctx: *RenderContext, mv: *AnimMove, inset: f32, dim: f32, t: f32) {
|
||||
ai := Board.idx(mv.a.col, mv.a.row);
|
||||
bi := Board.idx(mv.b.col, mv.b.row);
|
||||
@@ -420,10 +421,12 @@ BoardView :: struct {
|
||||
// into place. ease_out_back pins f(0)=0 and f(1)=1, so t==0 is the rest
|
||||
// pose and t==1 lands byte-on-cell — the swap stays purely visual.
|
||||
p = ease_out_back(t);
|
||||
} else if t < 0.5 {
|
||||
p = ease_out_cubic(t * 2.0);
|
||||
} else {
|
||||
p = ease_out_cubic((1.0 - t) * 2.0);
|
||||
// Rejected swap: a springy, slightly-damped bounce-back. The gems lunge
|
||||
// toward each other then spring home, overshooting rest by a bounded
|
||||
// amount before settling. bad_swap_bounce pins f(0)=0 and f(1)=0, so the
|
||||
// move stays purely visual — the board is byte-identical to pre-swap.
|
||||
p = bad_swap_bounce(t);
|
||||
}
|
||||
|
||||
afc := cast(f32) mv.a.col; afr := cast(f32) mv.a.row;
|
||||
|
||||
Reference in New Issue
Block a user