From ce556b55085ee664012eb568ebcda49915bcb032 Mon Sep 17 00:00:00 2001 From: Eric Wagoner Date: Tue, 18 Aug 2026 09:07:36 -0400 Subject: [PATCH] Wall of Fire burns like it means it The pulsing red rectangle becomes a standing fire: a glowing gradient bar seated on the wall line, five tongues of flame licking on staggered beats, and embers drifting off the top. Pure CSS on a handful of shapes - no turbulence filters, which stay budgeted for the one-shot Fireball, since firewalls burn indefinitely. Vertical walls flame just as well as horizontal ones; reduced motion stills the fire but keeps it visible. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_0138A8CjeQRpvzKxuMfz1Bqc --- packages/web/src/Board.svelte | 49 ++++++------- packages/web/src/FirewallEdge.svelte | 100 +++++++++++++++++++++++++++ 2 files changed, 122 insertions(+), 27 deletions(-) create mode 100644 packages/web/src/FirewallEdge.svelte diff --git a/packages/web/src/Board.svelte b/packages/web/src/Board.svelte index 808d739..c3c891f 100644 --- a/packages/web/src/Board.svelte +++ b/packages/web/src/Board.svelte @@ -5,6 +5,7 @@ import type { BoardFx } from "./fx"; import { colorIndexOf as sharedColorIndex, wizardColor } from "./colors"; import { CREATURE_ART, objectArt, TERRAIN_ART, tokenArt } from "./art"; + import FirewallEdge from "./FirewallEdge.svelte"; import TokenArt from "./TokenArt.svelte"; import { FX_SPRITES } from "./fx-sprites"; @@ -382,23 +383,27 @@ {#each edges as e (`${e.kind}:${e.x},${e.y}`)} - {@const cls = e.state === "door" ? `door${e.lock ? ` ${e.lock}` : ""}` : e.state === "firewall" ? "firewall" : "wall"} - {@const lockTitle = e.lock === "removed" ? "lock removed — swings free" - : e.lock === "jammed" ? "lock jammed — sealed for good" - : e.lock === "held" ? "held open by a standing wizard" - : e.lock === "ajar" ? "unlocked until end of turn" : null} - {#if e.kind === "V"} - {#if lockTitle}{lockTitle}{/if} + {#if e.state === "firewall"} + {:else} - {#if lockTitle}{lockTitle}{/if} + {@const cls = e.state === "door" ? `door${e.lock ? ` ${e.lock}` : ""}` : "wall"} + {@const lockTitle = e.lock === "removed" ? "lock removed — swings free" + : e.lock === "jammed" ? "lock jammed — sealed for good" + : e.lock === "held" ? "held open by a standing wizard" + : e.lock === "ajar" ? "unlocked until end of turn" : null} + {#if e.kind === "V"} + {#if lockTitle}{lockTitle}{/if} + {:else} + {#if lockTitle}{lockTitle}{/if} + {/if} {/if} {/each} @@ -707,16 +712,6 @@ .door.ajar { fill: #d4bd8e; stroke-dasharray: 5 3; } /* Held open: a wizard stands propping it. */ .door.held { fill: #d4bd8e; stroke: #2e7d32; } - .firewall { - fill: #d0342c; - stroke: #7c1a14; - stroke-width: 1.2; - animation: firewall-lick 1.1s ease-in-out infinite alternate; - } - @keyframes firewall-lick { - from { fill: #d0342c; filter: drop-shadow(0 0 2px rgba(255, 120, 20, 0.5)); } - to { fill: #ef6c3a; filter: drop-shadow(0 0 6px rgba(255, 140, 30, 0.9)); } - } .stone { fill: #6a6458; stroke: #3a362e; stroke-width: 2; } .bush { fill: #2e7d32; stroke: #1b4d1e; stroke-width: 2; } .rose { fill: #2e7d32; stroke: #b0245a; stroke-width: 3; } @@ -874,7 +869,7 @@ .marked-cell, .marked-sector, .ghost-slot, .warp-dest { animation: none; } .sight-line, .sight-mouth { animation: none; } .fx-layer { display: none; } - .firewall, :global(.token-art.ghosted) { animation: none; } + :global(.token-art.ghosted) { animation: none; } .mover { transition: none; } } .wizard { cursor: pointer; } diff --git a/packages/web/src/FirewallEdge.svelte b/packages/web/src/FirewallEdge.svelte new file mode 100644 index 0000000..832a9e9 --- /dev/null +++ b/packages/web/src/FirewallEdge.svelte @@ -0,0 +1,100 @@ + + + + +