9 Commits

Author SHA1 Message Date
swipelab
31d1012806 shed local vendors: stb + kb_text_shape + file_utils now ship with sx
The local vendors/ copies existed because the old modules/ffi/stb*.sx
resolved C paths CWD-relative, forcing every consumer to carry
identically-named copies. sx now ships these as proper library vendors
(#import "vendors/<name>/<name>.sx"), so the copies and the retired
ffi module imports both go. Verified: sx build --target ios-sim
bundles M3te.app; tools/run_tests.sh 23/23.
2026-06-12 18:35:12 +03:00
swipelab
6f7d2f4db2 lang migration: rename signed integer types sN -> iN
Mechanical sweep of all .sx sources, plan docs, and tests/expected
snapshots for the sx language rename (s8/s16/s32/s64 -> i8/i16/i32/i64).
Verified: tools/run_tests.sh 23/23.

Note: the ios-sim build has 2 pre-existing 'restart' dot-call errors
from the sx opt-in UFCS change (sx a47ea14) — independent of this
rename (present pre-sweep); migrated in the follow-up commit.
2026-06-12 09:36:51 +03:00
swipelab
1ab74c7d08 migrate allocator calls to alloc_bytes / libc_free 2026-06-12 09:34:13 +03:00
swipelab
38815c7d50 migrate to the restructured sx stdlib paths
modules/compiler.sx -> modules/build.sx; stb/stb_truetype/opengl/sdl3 ->
modules/ffi/; modules/process.sx -> modules/std/process.sx. Rebuilt for
macos + ios-sim; 23/23 logic snapshots pass.
2026-06-11 08:46:32 +03:00
swipelab
a7b41ccbca migrate to the new for-loop syntax
Drop the ':' before captures (for xs (x) / for 0..n (i)); the index
capture becomes the trailing open range (for xs, 0.. (x, i)). 136
headers across 26 files, mechanical.

Five headless tests (banner_layout, hit_test, swipe_commit,
swipe_intent, swipe_reshuffle) also gain a direct
#import "modules/ui/types.sx" — they named Point/Frame through a
transitive import, which bare visibility no longer permits.

Gates: sx build --target ios-sim main.sx links; tools/run_tests.sh
23/23.
2026-06-10 20:39:59 +03:00
swipelab
5eaf91b22d P18.2: organic combine — staggered clear ripple (sx, iOS sim)
Within a clearing round the matched gems no longer all explode at once: each
gem's pop (and its burst) START is offset by a bounded per-gem delay so the
cells detonate as a ripple.

- board_anim.sx: clear_ripple_t(t,u) mirrors fall_stagger_t's (t-delay)/window,
  delaying a gem's pop START by CLEAR_STAGGER_MAX*u (0.45 of the clear window).
  Bounded: every gem still reaches local 1 (scale 0) by t==1, so none is left
  mid-pop at the seam to the fall. clear_diag_span/clear_rank rank each matched
  gem 0..1 by diagonal (col+row) PER ROUND, so even a 3-match ripples across the
  full budget.
- board_view.sx render_clear: feed each matched gem's ranked, staggered local t
  through the P18.1 clear_pop_scale (locked endpoints unchanged).
- board_fx.sx: bursts carry the same per-gem delay so they ripple in lockstep
  with the pops. Per-round audio cue (P10.10) still fires once at t0, not per gem.
- Model untouched (same cells cleared, same final board); CLEAR_ANIM_DUR fixed,
  so cascade-cue snapshots don't churn and M3TE_ANIM_TIME=0 still rests.
- tests/easing.sx: pin clear_ripple_t endpoints, bounded completion by t==1,
  monotonicity, ripple ordering, and the diagonal rank.
- goldens: add p18_stagger (M3TE_FX=3 @ 0.22); refresh p18_pop, p6_fx_match,
  p11_combo_deep (all pinned mid-clear, now showing the ripple).
2026-06-06 13:20:52 +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
c2548aa854 P6.2: score popups & match FX (sx, iOS sim)
Add a purely-visual, transient juice layer over a committed move — score
popups + a tinted particle/flash burst at the clears — with no change to the
model, score, moves, or settled board.

- assets/fx/particle.png: key the painted transparency checkerboard out of the
  provided particle art to real alpha (8-connected border flood fill +
  smooth luminance falloff that preserves the soft glow), downscaled to a
  256x256 RGBA white sparkle. tools/key_particle.sx is the reproducible tool.
- board_fx.sx: BoardFx (live particle bursts + one "+points" popup) and
  BoardFxAssets. The engine image path samples texture*white (no draw-time
  tint), so the white sprite is tinted per gem colour at LOAD time into one
  texture per colour; a burst animates by scale (grow -> shrink) and the soft
  texture edges carry the fade. Combos (cascade depth > 1) burst bigger and the
  popup is larger + gold. All driven by delta_time and self-pruning.
- board_anim.sx: AnimMove carries the model's cascade.awarded so the popup
  shows the real payout without re-deriving any scoring in the view.
- board_view.sx / main.sx: wire BoardFx + the tinted assets, tick each frame,
  spawn on a legal commit, and render bursts (clipped to the grid) under the
  popups (drawn on top). Input-lock (BoardAnim.active) is untouched; FX never
  gate input and may outlast the move slightly before vanishing.

Goldens (iPhone-17-class sim, iOS 26): p6_fx.png (combo: gold "+480" + bursts
mid-cascade), p6_fx_match.png (single match: "+30" + red burst), p6_fx_after.png
(settled board, FX fully gone). Gate: ios-sim build links, 15/15 logic tests
green (scoring/cascade goldens unchanged).
2026-06-05 02:18:55 +03:00