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).
This commit is contained in:
@@ -46,6 +46,8 @@ AnimRound :: struct {
|
||||
// a legal swap has >=1 round and `final` is the settled board; an illegal swap
|
||||
// has zero rounds, `pre == final`, and the view plays a slide-and-return. `a`/`b`
|
||||
// are the swapped cells; `pre` is the board before the swap (the slide's start).
|
||||
// `awarded` carries the model's own payout for this move (cascade.awarded) so the
|
||||
// score-popup FX (P6.2) shows the real number without re-deriving any scoring.
|
||||
AnimMove :: struct {
|
||||
legal: bool;
|
||||
a: Cell;
|
||||
@@ -53,6 +55,7 @@ AnimMove :: struct {
|
||||
pre: [BOARD_CELLS]Gem;
|
||||
rounds: List(AnimRound);
|
||||
final: [BOARD_CELLS]Gem;
|
||||
awarded: s64;
|
||||
}
|
||||
|
||||
// Commit the player's swap authoritatively AND record its visual timeline. The
|
||||
@@ -65,6 +68,7 @@ plan_and_commit :: (board: *Board, a: Cell, b: Cell) -> AnimMove {
|
||||
move.b = b;
|
||||
move.rounds = List(AnimRound).{};
|
||||
move.pre = board.cells;
|
||||
move.awarded = 0;
|
||||
|
||||
// Snapshot the entire model state (cells + RNG + score + moves) before the
|
||||
// commit so the replay below is bit-identical to what commit_swap does.
|
||||
@@ -72,6 +76,7 @@ plan_and_commit :: (board: *Board, a: Cell, b: Cell) -> AnimMove {
|
||||
|
||||
mv := commit_swap(board, a, b);
|
||||
move.legal = mv.legal;
|
||||
move.awarded = mv.cascade.awarded;
|
||||
if !mv.legal {
|
||||
move.final = board.cells;
|
||||
return move;
|
||||
|
||||
Reference in New Issue
Block a user