From 64709f6c9cb355d7afea0b99bc3a5cfae958d0c1 Mon Sep 17 00:00:00 2001 From: Eric Wagoner Date: Mon, 17 Aug 2026 10:47:42 -0400 Subject: [PATCH] Stone to water splashes like it means it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The transmutation fired no flourish: its event carried no coordinates for the wall case and the mapper knew neither it nor the wave it raises. The event now carries its edge; the burst splashes both sides of the vanished wall (or the freed square, for a block), and every wizard the collapsing wave carries leaves a wash streak — which also gives the plain WATERWALL's wave its motion for free. Co-Authored-By: Claude Fable 5 --- packages/engine/src/game.ts | 4 ++-- packages/web/src/fx.ts | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/packages/engine/src/game.ts b/packages/engine/src/game.ts index 7677bb8..90b4fd1 100644 --- a/packages/engine/src/game.ts +++ b/packages/engine/src/game.ts @@ -540,7 +540,7 @@ export type GameEvent = | { type: "safeCreated"; caster: PlayerId; at: Cell } | { type: "safeOpened"; player: PlayerId; at: Cell } | { type: "itemsTraded"; caster: PlayerId; a: Cell; b: Cell } - | { type: "stoneTurnedToWater"; caster: PlayerId; at: Cell | null } + | { type: "stoneTurnedToWater"; caster: PlayerId; at: Cell | null; edge?: { cell: Cell; side: Side } } | { type: "handsSwapped"; a: PlayerId; b: PlayerId } | { type: "handsScrambled"; caster: PlayerId } | { type: "rammed"; attacker: PlayerId; target: PlayerId; distance: number } @@ -1730,7 +1730,7 @@ const CARD_EFFECTS: Record if (!losToEdge(view, caster.position, cell, side)) return "no line of sight"; state.edgeOverrides[key] = "open"; delete state.createdEdges[key]; - events.push({ type: "stoneTurnedToWater", caster: caster.id, at: null }); + events.push({ type: "stoneTurnedToWater", caster: caster.id, at: null, edge: { cell, side } }); // "Wall turns into a WATERWALL with a range and damage of 2." waveFromEdge(state, events, cell, side, 2); checkVictory(state, events); diff --git a/packages/web/src/fx.ts b/packages/web/src/fx.ts index c5da140..6cbc1d4 100644 --- a/packages/web/src/fx.ts +++ b/packages/web/src/fx.ts @@ -159,6 +159,27 @@ export function fxForEvents( push({ kind: "edge-dust", cell: e.edge.cell, side: e.edge.side }); beat++; break; + case "stoneTurnedToWater": { + // The wall (or block) bursts into water: splash both sides of the + // vanished edge, or the freed square itself. + if (e.at) { + push({ kind: "splash", at: e.at }); + } else if (e.edge) { + const n = { + x: e.edge.cell.x + (e.edge.side === "E" ? 1 : e.edge.side === "W" ? -1 : 0), + y: e.edge.cell.y + (e.edge.side === "S" ? 1 : e.edge.side === "N" ? -1 : 0), + }; + push({ kind: "splash", at: e.edge.cell }); + push({ kind: "splash", at: n }, 120); + } + beat++; + break; + } + case "washedBack": + // The collapsing wave carries them: a streak per victim. + push({ kind: "streak", from: e.from, to: e.to }); + beat++; + break; case "died": { const at = posOf(e.player); if (at) { push({ kind: "soul", at }, 250); beat++; }