Commit Graph

11 Commits

Author SHA1 Message Date
swipelab
8402f49503 P16.1: organic legal swap — overshoot/settle (ease_out_back)
Replace the legal swap's flat ease_out_cubic decelerating slide with the
P15.1 ease_out_back overshoot curve: the two swapped gems shoot ~10% PAST
their target cells, then settle exactly onto them. Purely visual — the curve
pins f(0)=0 / f(1)=1, so t==0 is the rest pose and t==1 lands byte-on-cell;
the committed move and final board are unchanged. SWAP_ANIM_DUR (0.16 s) is
untouched, so the cascade-cue timing snapshots do not churn.

Only the legal branch of render_swap changes; the illegal ping-back is left
as-is for P16.2. Model/logic untouched (FFI is the only non-sx surface).

Golden goldens/p16_swap.png: M3TE_FX=3 (top-row swap (5,0)<->(6,0)) pinned at
M3TE_ANIM_TIME=0.10 (swap-phase t~0.625, near the overshoot peak). Measured
against calibrated cell centers: the red lands ~8% left of col-5 and the green
~12% right of col-6 (both PAST target), while every unswapped gem stays
centered; at M3TE_ANIM_TIME=0 the same gems sit dead-on their pre-swap cells.

Gate: `sx build --target ios-sim main.sx` links clean; `tools/run_tests.sh`
22/22 (anim_plan, easing, cascade_rounds, cascade_cue all green).
2026-06-06 10:32:42 +03:00
swipelab
d043319d00 P13.1: fix F2 — retire stale procedural synth, doc the real Triple Treat DSP path
The README audio-regeneration section called tools/synth_audio.py the
'build-time DSP path', but that script is the original P10.1 procedural
synthesizer: its main() overwrites assets/audio/*.wav with note-frequency
synthetic audio. Following the documented path would clobber the curated
Triple Treat pack cues. Remove the script (cleanest resolution — kills the
clobber hazard) and rewrite the README to describe the actual production:
real pack clips (per-cue source in assets/audio/LICENSE.txt) down-mixed,
trimmed, faded, peak-normalized to ~-15 dBFS, re-wrapped via afconvert;
combo ladder = real Match FX ordered by brightness (P10.9); cascade one cue
per round (P10.10). measure_pitch.py kept (read-only verification, never
writes a WAV). WAVs/.sx/goldens byte-unchanged.
2026-06-06 09:23:20 +03:00
swipelab
5fa0a95cb4 P13.1: fix README cue-capture recipe — terminate before each env-pinned relaunch
The M3TE_* capture pins are read only at app startup, so a second `simctl
launch` against the still-running app reused its PID and silently ignored the
new pin (the win->lose cue recipe only ever produced `cue win`). Add
`--terminate-running-process` to every pinned launch across all capture recipes
and document the startup-only rule explicitly. Docs-only; no .sx change.
2026-06-06 09:12:10 +03:00
swipelab
2f5d60b9e1 P13.1: final vibe integration — playthrough validation, full golden sweep, finalized docs
Integration-only (no logic changes). Validated the full candy vibe in the booted
iPhone sim and brought every artifact in line with the shipped candy palette +
Triple Treat SFX bank.

Goldens — swept all 23:
- Refreshed 15 that predated the candy palette (P12): p6_idle_t0, p6_idle_mid,
  p6_select, p7_restart, p5_swap_before/after, p6_anim_swap/clear/fall/after,
  p6_fx, p6_fx_after, p6_fx_match, p6_inputlock_board, p11_combo_deep — re-captured
  via the documented M3TE_* hooks.
- Left 5 unchanged (board+HUD region byte-identical to the current build, verified
  by cropped-region hash): p4_board, p4_hud, p9_polish, p7_win, p7_lose.
- Removed 3 obsolete pre-board orange-quad goldens (app no longer renders them):
  p0_quad, p0_input_before, p0_input_after.

Docs — README.md:
- Section 2 now describes the candy board (not the old orange quad) and points at
  goldens/p6_idle_t0.png; dropped the removed p0_* references.
- Added the final audio model: Triple Treat SFX provenance + per-cue mapping, the
  per-round ascending cascade (one combo cue per round, clamped at combo5), the
  WAVE/mono/44100/Int16 @ -15 dBFS format spec, and the cue-log capture commands.
- Added image-art asset regeneration (codex imagegen via codex exec + sips
  normalize to exact per-asset dims/format).

Gate: ios-sim build links (exit 0); 21/21 pure-sx logic tests pass. Playthrough
evidence (cue NSLog ascending combo1..combo5 + win/lose stingers, screenshots)
captured in the worker report.
2026-06-06 08:57:26 +03:00
swipelab
cd8667d170 P11.3: glossier candy gem & selection feel (sx / iOS)
Make the selection highlight read as glossy candy without new art and
without disturbing the idle-rest invariant. board_view's render_selection
layers a soft outward glow (two concentric stroked rings — the renderer has
no blur), a warm wash, a bright rim doubled by a thin inner highlight for a
glassy edge, and a wet sheen that rides the selected gem's live pose. Every
layer is a rect/overlay (issue 0002 forbids a draw-time gem-texture tint).

The gloss is selection-only: render_selection runs solely when a cell is
selected, so the resting board (no selection) is byte-identical to before
and the t==0 idle pose stays exactly the static sprite (locked by
tests/gem_pose). The selection-pop motion still comes from gem_anim; no
board / score / move state changes and input stays gated by BoardAnim.active.

Updated goldens/p6_select.png; README documents the P11.3 selection gloss
and its reproduce commands (reusing the P6.3 M3TE_SELECT + M3TE_ANIM_TIME
hooks).
2026-06-05 21:15:44 +03:00
swipelab
0b293a2c48 P11.2: escalating combo emphasis tied to cascade depth (sx / iOS)
Scale the combo FX with cascade depth (mv.rounds.len) — the same depth the
cascade SFX (play_cascade) steps up on — so deeper cascades read as more
exciting and land in lockstep with the audio escalation. Purely visual and
self-pruning: no board / score / move state changes, and input stays gated by
BoardAnim.active alone.

- board_fx.sx: add fx_combo_level (mirrors audio's cascade_cue_index clamp:
  depth<=1 -> floor, depth>=5 -> ceiling). The +points popup now carries the
  cascade depth and grows one font step + lerps gold -> hot-gold per level
  (fx_popup_font / fx_popup_color). Every burst of a deep cascade gets a
  whole-move depth boost (FX_BURST_DEPTH) on top of the existing per-round bump.
- board_view.sx: render_fx_popups derives styling from depth and tops a combo
  with a "COMBO xN" label naming the true cascade depth.
- tests/fx_combo.sx: headless snapshot locking the depth->level/font table and
  asserting fx_combo_level matches the cascade-cue index column entry-for-entry.
- goldens/p11_combo_deep.png + README: deterministic depth-5 capture (M3TE_FX=11)
  vs the depth-1 single clear (M3TE_FX=3); FX gone after settle at a later phase.
2026-06-05 20:51:56 +03:00
swipelab
b65e592a8c P11.1: juicier match pops & brighter bursts (sx / iOS)
Visual-juice vibe-pass, FX-only — no logic/state changes, input gating
still owned by BoardAnim.active.

- board_fx.sx: bigger, punchier match bursts — peak size 1.95->2.50 cells,
  combo bonus 0.55->0.72, and the per-gem fx tints saturated a touch (low
  channel trimmed, dominant/mid lifted) so every burst pops as a brighter,
  more vivid candy colour. The hot per-pixel tint loop's hoisted locals are
  preserved (issue 0001).
- gem_anim.sx: snappier clear pop — faster rise (0.30->0.18 of the window)
  to a bigger overshoot (CLEAR_POP_A 0.22->0.34) so the matched-gem clear
  reads as a candy snap. gem_pose's clear-pop invariants still hold.
- main.sx: M3TE_FX=<n> deterministic match-FX capture hook, mirroring the
  M3TE_SELECT pattern. Commits the n-th currently-legal swap at startup via
  the normal plan_and_commit path and begins the move timeline + burst/popup
  FX; M3TE_ANIM_TIME pins the phase and the frame loop holds the move/FX
  frozen while pinned, so the burst + "+points" screenshot identically every
  run. A larger M3TE_ANIM_TIME captures the settled, FX-gone board. Startup-
  only and guarded, so normal play is untouched.
- README.md: document the new M3TE_FX pin alongside the other capture hooks.
- goldens/p6_fx_match.png: updated deterministic golden (iOS 26 sim,
  SIMCTL_CHILD_M3TE_FX=3 SIMCTL_CHILD_M3TE_ANIM_TIME=0.22) — the vertical
  red 3-match, burst region +1.4% mean luminance / 3.2:1 brighter:dimmer vs
  the same scene on the pre-juice constants.

Gate: ios-sim build links, 19/19 logic tests green (incl. gem_pose t=0 rest).
2026-06-05 20:36:25 +03:00
swipelab
5be379f180 P7.2: goal HUD + win/lose banner + restart button (sx, iOS sim)
Extend the HUD to show the per-level goal (SCORE x / target) alongside
moves. When the model's level_status (P7.1) is won/lost, draw a centered
overlay banner ("YOU WIN!" / "OUT OF MOVES") with a "PLAY AGAIN" restart
button over the dimmed board; the banner appears once any winning/losing
cascade animation settles. Status is read from the model, never recomputed
in the view.

A finished level freezes board-cell input; only the restart button is live.
Its rect is derived from the shared BoardLayout grid (new BannerLayout), so
the hit-test lands exactly on the drawn button. A tap reseeds the same
starting level through board.restart and clears the transient view layers,
returning to a clean in_progress board.

Banner is text + rects only (honours colour/alpha; no draw-time image tint,
issue 0002). New env capture hooks (M3TE_TARGET / M3TE_MOVE_LIMIT /
M3TE_RESTART) force a terminal status / restart for deterministic goldens.

Tests: tests/banner_layout.sx locks the restart button rect <-> hit-test
round-trip headlessly. Goldens p7_win / p7_lose / p7_restart captured on the
iOS simulator.
2026-06-05 14:57:27 +03:00
swipelab
d35fa8a5a6 P6.3: per-gem idle/select/land/clear animations (sx, iOS sim)
New gem_anim.sx adds a purely-visual per-gem pose set driven by a single
animation clock: a calm always-on idle breath (scale-pulse + bob, per-gem
phase, ramped in from rest), a selection pop, a landing squash-bounce, and
a clear pop. BoardView draws every settled gem through gem_pose_at /
gem_pose_frame; the move timeline (P6.1) and FX (P6.2) are untouched and the
input-lock semantics are unchanged (idle never locks input).

Determinism: the idle is always-on, so main reads M3TE_ANIM_TIME=<seconds>
to freeze the clock at a chosen phase (t==0 == the resting board, so the
pre-P6.3 goldens reproduce) and M3TE_SELECT=<cellIndex> to force a selection
for capture. tests/gem_pose.sx locks the t==0-rest invariant and the reaction
envelopes headlessly (fails if the idle ramp is dropped).

Goldens (deterministic capture): p6_idle_t0 (resting), p6_idle_mid (pinned
mid-breath), p6_select (selection pop on cell 3,3). Purely visual: no change
to model/score/moves/hit-testing.
2026-06-05 07:59:16 +03:00
swipelab
6d9aee67ba P0.4: wire the logic verification gate (sx assert helper + snapshot runner)
Add a real logic-test gate so future pure-sx game logic fails the build on a
bad assertion:

- tests/test.sx: `expect(cond, msg)` assert helper — prints a greppable
  `FAIL <file>:<line>: <msg>` and exits non-zero via process.exit on failure.
- tools/run_tests.sh: snapshot runner mirroring sx/tests/run_examples.sh; runs
  each tests/<name>.sx and diffs stdout + exit code against tests/expected/.
  Exits 0 iff all tests pass.
- tests/arith.sx (+ expected snapshots): seed passing sanity test.
- README.md: document both halves of the gate — logic runner and the
  reproducible ios-sim build/launch sequence (with device discovery).
2026-06-04 18:57:21 +03:00
swipelab
7a9d01c510 Seed m3te repo 2026-06-04 17:49:06 +03:00