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:
37
README.md
37
README.md
@@ -240,6 +240,41 @@ env SIMCTL_CHILD_M3TE_FX=3 SIMCTL_CHILD_M3TE_ANIM_TIME=0 \
|
||||
3-match in column 5; at `M3TE_ANIM_TIME=0.10` the red lands ~8 % left of col-5
|
||||
and the green ~12 % right of col-6 (every unswapped gem stays centered).
|
||||
|
||||
### Organic illegal swap (P16.2)
|
||||
|
||||
A *rejected* swap no longer pings flatly out and back: `render_swap` now drives the
|
||||
two gems with `bad_swap_bounce` (a P15.1 `spring`-based envelope), so they lunge
|
||||
toward each other then spring home, overshooting rest by a bounded amount before
|
||||
settling. The curve pins `f(0)=0` and `f(1)=0`, so the move stays purely visual —
|
||||
both `t=0` and `t=1` are the rest pose, the board is byte-identical to pre-swap, no
|
||||
move or score is spent. The envelope (endpoints, single lunge peak + location,
|
||||
damped settle) is locked headlessly by `tests/easing.sx`.
|
||||
|
||||
The bounce only plays for an *illegal* swap, which the sim can't script, so one more
|
||||
env hook forces a known rejected pair at startup — combine it with `M3TE_ANIM_TIME`
|
||||
to freeze the phase:
|
||||
|
||||
- `M3TE_BADSWAP=<n>` commits the **n-th currently-ILLEGAL orthogonally-adjacent
|
||||
pair** — the complement of `legal_swaps`, enumerated in the SAME stable row-major
|
||||
order (right-before-down), 1-based + clamped — through the normal `plan_and_commit`
|
||||
path (which reverts the illegal swap), then begins the move timeline so the
|
||||
swap-segment bounce can be screenshot. No FX begins (a rejected swap clears
|
||||
nothing). Startup-only and guarded by the var, so normal play is byte-identical
|
||||
with it unset.
|
||||
|
||||
The lunge peak is at swap-phase `t = BADSWAP_LUNGE_T (0.36)`, i.e. animation time
|
||||
`0.36 × SWAP_ANIM_DUR (0.16) ≈ 0.0576 s`. For seed 1337, `M3TE_BADSWAP=41` is the
|
||||
mid-board horizontal pair `(2,3)↔(3,3)`:
|
||||
|
||||
```bash
|
||||
# Springy lunge caught at its extreme (gems nudged toward each other): goldens/p16_badswap.png
|
||||
env SIMCTL_CHILD_M3TE_BADSWAP=41 SIMCTL_CHILD_M3TE_ANIM_TIME=0.0576 \
|
||||
xcrun simctl launch --terminate-running-process booted co.swipelab.m3te
|
||||
# Same rejected pair at exact rest (t=0) — gems sit dead-on their pre-swap cells:
|
||||
env SIMCTL_CHILD_M3TE_BADSWAP=41 SIMCTL_CHILD_M3TE_ANIM_TIME=0 \
|
||||
xcrun simctl launch --terminate-running-process booted co.swipelab.m3te
|
||||
```
|
||||
|
||||
## Audio bank (P10) — final model
|
||||
|
||||
The SFX bank (`audio.sx`) is a purely additive layer over iOS **System Sound
|
||||
@@ -365,6 +400,6 @@ sips -z 128 768 --setProperty format png <generated>.png --out assets/gems/gems.
|
||||
```
|
||||
|
||||
After any art change, re-capture the affected goldens with the deterministic hooks
|
||||
above (`M3TE_ANIM_TIME` / `M3TE_SELECT` / `M3TE_FX` / `M3TE_TARGET` /
|
||||
above (`M3TE_ANIM_TIME` / `M3TE_SELECT` / `M3TE_FX` / `M3TE_BADSWAP` / `M3TE_TARGET` /
|
||||
`M3TE_MOVE_LIMIT` / `M3TE_RESTART`) and state per golden whether it was refreshed,
|
||||
left unchanged, or removed.
|
||||
|
||||
Reference in New Issue
Block a user