From d7afe3a5ca9040704b95cc9db4e7656c7511bef7 Mon Sep 17 00:00:00 2001 From: Eric Wagoner Date: Sat, 15 Aug 2026 23:31:42 -0400 Subject: [PATCH] Letter the warp openings and show where they lead MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Paired openings now share a printed letter (A, B, C ...), exactly as the physical boards mark them — making the 2-player crossed pairings legible at a glance. While standing on an opening, its destination square pulses with a dashed green ring, so "leave at B, re-enter at B" stops being a leap of faith. Co-Authored-By: Claude Fable 5 --- packages/web/src/Board.svelte | 50 +++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/packages/web/src/Board.svelte b/packages/web/src/Board.svelte index 63e5f13..71344c7 100644 --- a/packages/web/src/Board.svelte +++ b/packages/web/src/Board.svelte @@ -64,6 +64,20 @@ return boxes; }); + // Warp pairs share a letter, like the printed openings on the real boards. + const warpLetters = $derived.by(() => { + const letters = new Map(); + let next = 0; + for (const w of view.board.warps) { + const a = `${w.from.cell.x},${w.from.cell.y}`; + const b = `${w.to.cell.x},${w.to.cell.y}`; + const key = [a, b].sort().join("|"); + if (!letters.has(key)) letters.set(key, String.fromCharCode(65 + next++)); + letters.set(a, letters.get(key)!); + } + return letters; + }); + // Group players by cell so co-located wizards fan out. const wizardsByCell = $derived.by(() => { const map = new Map(); @@ -209,6 +223,7 @@ (w.from.side === "S" ? CELL * 0.42 : w.from.side === "N" ? -CELL * 0.42 : 0)} {@const standing = view.players.some((p) => p.id === view.you && p.position.x === w.from.cell.x && p.position.y === w.from.cell.y)} + {@const letter = warpLetters.get(`${w.from.cell.x},${w.from.cell.y}`) ?? ""} {w.from.side === "N" ? "↑" : w.from.side === "S" ? "↓" : w.from.side === "E" ? "→" : "←"} + {letter} + {#if standing} + + + {/if} {/each} @@ -342,6 +369,29 @@ stroke-width: 2; } .warp-g.standing .warp { opacity: 1; } + .warp-letter { + font-family: "Oswald", sans-serif; + font-size: 10px; + font-weight: 600; + text-anchor: middle; + fill: #6a5c44; + pointer-events: none; + } + .warp-dest { + fill: none; + stroke: #2e7d32; + stroke-width: 2.5; + stroke-dasharray: 7 4; + pointer-events: none; + animation: warp-pulse 1.6s ease-in-out infinite; + } + @keyframes warp-pulse { + 0%, 100% { opacity: 0.9; } + 50% { opacity: 0.35; } + } + @media (prefers-reduced-motion: reduce) { + .warp-dest { animation: none; } + } .wizard { cursor: pointer; } .wizard-label { font-family: "Oswald", sans-serif;