Wording throughout the app, the server's messages and the engine's refusals trades its stacked dashes for sentences, while the maze still trusts you to honor its curses, the gallery still listens, and the clockwork still learns its best tricks from the humans at the table. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
97 lines
3.6 KiB
Svelte
97 lines
3.6 KiB
Svelte
<script lang="ts">
|
|
// A standing WALL OF FIRE: board furniture, not a flourish — it burns for
|
|
// as long as the spell holds, so the animation is pure CSS on a handful
|
|
// of flat cel shapes, with a short held-pose flicker.
|
|
import { holdToPeek } from "./peek";
|
|
let { x, y, kind, onpeek }: {
|
|
x: number; y: number; kind: "V" | "H";
|
|
/** Press-and-hold (touch has no hover): report what this fire is. */
|
|
onpeek?: (tip: string) => void;
|
|
} = $props();
|
|
const TIP = "a wall of fire that burns for 4 as you pass through";
|
|
const { press, release } = holdToPeek(() => onpeek?.(TIP));
|
|
const CELL = 48;
|
|
const WALL = 7;
|
|
const cx = $derived(kind === "V" ? (x + 1) * CELL : x * CELL + CELL / 2);
|
|
const cy = $derived(kind === "V" ? y * CELL + CELL / 2 : (y + 1) * CELL);
|
|
// Tongues sit along the edge's axis; every flame rises screen-up.
|
|
const TONGUES = [
|
|
{ t: -0.38, h: 11, d: 0.0 },
|
|
{ t: -0.19, h: 15, d: 0.45 },
|
|
{ t: 0.0, h: 18, d: 0.15 },
|
|
{ t: 0.21, h: 13, d: 0.6 },
|
|
{ t: 0.4, h: 16, d: 0.3 },
|
|
];
|
|
const EMBERS = [
|
|
{ t: -0.28, d: 0.0 },
|
|
{ t: 0.08, d: 0.9 },
|
|
{ t: 0.33, d: 1.7 },
|
|
];
|
|
const px = (t: number) => (kind === "V" ? 0 : t * CELL);
|
|
const py = (t: number) => (kind === "V" ? t * CELL : 0);
|
|
</script>
|
|
|
|
<g transform={`translate(${cx} ${cy})`} class="fw">
|
|
<title>{TIP}</title>
|
|
<!-- the burning bar itself, seated on the wall line -->
|
|
{#if kind === "V"}
|
|
<rect x={-WALL / 2} y={-CELL / 2 - 2} width={WALL} height={CELL + 4} rx="3"
|
|
fill="#bd3b1b" class="fw-bar"
|
|
role="img" onpointerdown={press} onpointerup={release} onpointerleave={release} />
|
|
{:else}
|
|
<rect x={-CELL / 2 - 2} y={-WALL / 2} width={CELL + 4} height={WALL} rx="3"
|
|
fill="#bd3b1b" class="fw-bar"
|
|
role="img" onpointerdown={press} onpointerup={release} onpointerleave={release} />
|
|
{/if}
|
|
<!-- tongues of flame, each on its own beat; the bar beneath is the hit target -->
|
|
{#each TONGUES as f (f.t)}
|
|
<g transform={`translate(${px(f.t)} ${py(f.t)})`} class="fw-art">
|
|
<g class="fw-tongue" style={`animation-delay: ${-f.d}s`}>
|
|
<path d={`M -3.5 1 Q -6 ${-f.h*.35} -1.7 ${-f.h*.61} Q 1 ${-f.h*.81} -.5 ${-f.h} Q 5 ${-f.h*.82} 2.5 ${-f.h*.47} Q 6 ${-f.h*.6} 5 ${-f.h*.75} Q 9 ${-f.h*.27} 3.4 1 Z`}
|
|
fill="#ef681e" stroke="#922e1a" stroke-width="0.65" stroke-linejoin="round" />
|
|
<path d={`M -1.8 1 Q -3 ${-f.h*.25} 1 ${-f.h*.58} Q .1 ${-f.h*.2} 3 ${-f.h*.34} Q 4 ${-f.h*.12} 1.8 1 Z`} fill="#ffd34e" />
|
|
<path d={`M -.5 1 Q -1 ${-f.h*.12} 1.2 ${-f.h*.28} L 1.4 1 Z`} fill="#fff09a" />
|
|
</g>
|
|
</g>
|
|
{/each}
|
|
{#each EMBERS as em (em.t)}
|
|
<path d={`M ${px(em.t)} ${py(em.t)-9} q -3 3 -.5 5 q 3 -1 .5 -5 Z`} class="fw-ember"
|
|
style={`animation-delay: ${-em.d}s`} />
|
|
{/each}
|
|
</g>
|
|
|
|
<style>
|
|
.fw-art, .fw-ember { pointer-events: none; }
|
|
.fw-bar {
|
|
stroke: #7c1a14;
|
|
stroke-width: 1;
|
|
animation: fw-coals 1.3s steps(2, end) infinite alternate;
|
|
}
|
|
.fw-tongue {
|
|
transform-origin: 0px 1px;
|
|
animation: fw-lick 0.9s steps(3, end) infinite alternate;
|
|
}
|
|
.fw-ember {
|
|
fill: #ffd166;
|
|
opacity: 0;
|
|
animation: fw-rise 2.6s ease-out infinite;
|
|
}
|
|
@keyframes fw-coals {
|
|
from { fill: #bd3b1b; }
|
|
to { fill: #d64b1c; }
|
|
}
|
|
@keyframes fw-lick {
|
|
from { transform: scale(0.95, 0.82) rotate(-4deg); opacity: 1; }
|
|
to { transform: scale(1.04, 1.08) rotate(3deg); opacity: 1; }
|
|
}
|
|
@keyframes fw-rise {
|
|
0% { opacity: 0; transform: translateY(0); }
|
|
15% { opacity: 0.9; }
|
|
100% { opacity: 0; transform: translateY(-16px); }
|
|
}
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.fw-bar, .fw-tongue, .fw-ember { animation: none; }
|
|
.fw-ember { opacity: 0.6; }
|
|
}
|
|
</style>
|