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.
This commit is contained in:
swipelab
2026-06-05 20:51:56 +03:00
parent b68b60a537
commit 0b293a2c48
7 changed files with 178 additions and 15 deletions

View File

@@ -428,6 +428,9 @@ BoardView :: struct {
// Floating "+points" popups: rise and fade above the initial clear. Drawn
// unclipped (over everything) so the number stays legible as it lifts off
// the grid. The text path honours the colour's alpha, so these truly fade.
// A combo (depth > 1) escalates with cascade depth: gold and larger, topped
// by a `COMBO xN` label naming the depth — the same depth the cascade SFX
// escalates on — so deeper cascades read as more exciting.
render_fx_popups :: (self: *BoardView, ctx: *RenderContext) {
if self.fx == null or self.fx.popups.len == 0 { return; }
cs := self.layout.cell_size;
@@ -436,8 +439,8 @@ BoardView :: struct {
lt := (q.age - q.delay) / q.life;
if lt >= 0.0 {
fade := fx_popup_fade(lt);
font := if q.combo then FX_POPUP_COMBO_FONT else FX_POPUP_FONT;
base := if q.combo then FX_POPUP_COMBO_COLOR else FX_POPUP_COLOR;
font := fx_popup_font(q.depth);
base := fx_popup_color(q.depth);
col := Color.{ r = base.r, g = base.g, b = base.b, a = cast(u8) (fade * 255.0) };
txt := format("+{}", q.points);
sz := measure_text(txt, font);
@@ -447,6 +450,16 @@ BoardView :: struct {
Frame.make(cx - sz.width * 0.5, cy - sz.height * 0.5, sz.width, sz.height),
txt, font, col
);
if q.depth > 1 {
lfont := font * FX_COMBO_LABEL_RATIO;
ltxt := format("COMBO x{}", q.depth);
lsz := measure_text(ltxt, lfont);
lcy := cy - sz.height * 0.5 - cs * FX_COMBO_LABEL_GAP - lsz.height * 0.5;
ctx.add_text(
Frame.make(cx - lsz.width * 0.5, lcy - lsz.height * 0.5, lsz.width, lsz.height),
ltxt, lfont, col
);
}
}
}
}