From 87a5067c77236d16700ac09b6af69bae3e213f4f Mon Sep 17 00:00:00 2001 From: Eric Wagoner Date: Sat, 15 Aug 2026 23:28:51 -0400 Subject: [PATCH] Make wraparound warps usable: click the arrow to step through MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The engine's warps worked all along (verified in all six directions), but the client offered no way to trigger one short of clicking the destination cell across the map. The warp arrows are now buttons: when you stand on an opening its arrow lights green — click it to step through. Clicking a far arrow behaves as a click on its cell. Co-Authored-By: Claude Fable 5 --- packages/web/src/App.svelte | 12 +++++++++++ packages/web/src/Board.svelte | 38 ++++++++++++++++++++++++++--------- 2 files changed, 41 insertions(+), 9 deletions(-) diff --git a/packages/web/src/App.svelte b/packages/web/src/App.svelte index 7c5dea3..b2b0465 100644 --- a/packages/web/src/App.svelte +++ b/packages/web/src/App.svelte @@ -263,6 +263,17 @@ } } + function clickWarp(cell: { x: number; y: number }, side: Side) { + if (!view || !isYourTurn || !me) return; + // Standing on the opening: step through. Otherwise treat it as a cell + // click on the opening square (e.g. to walk toward it). + if (me.position.x === cell.x && me.position.y === cell.y) { + net.command({ type: "move", direction: side }); + } else { + clickCell(cell); + } + } + function clickEdge(cell: { x: number; y: number }, side: Side) { if (!selectedCard || !edgeSelectMode) return; net.command({ @@ -453,6 +464,7 @@ onEdgeClick={clickEdge} onPlayerClick={clickPlayer} onCreatureClick={clickCreature} + onWarpClick={clickWarp} /> diff --git a/packages/web/src/Board.svelte b/packages/web/src/Board.svelte index a4f0c2c..63e5f13 100644 --- a/packages/web/src/Board.svelte +++ b/packages/web/src/Board.svelte @@ -13,6 +13,7 @@ onEdgeClick, onPlayerClick, onCreatureClick, + onWarpClick, }: { view: GameView; edgeSelectMode?: boolean; @@ -21,6 +22,7 @@ onEdgeClick?: (cell: { x: number; y: number }, side: Side) => void; onPlayerClick?: (playerId: string) => void; onCreatureClick?: (creatureId: string) => void; + onWarpClick?: (cell: { x: number; y: number }, side: Side) => void; } = $props(); const PLAYER_COLORS = ["#1a9c46", "#d3352b", "#c9308f", "#3a3ac0", "#2ab0c9", "#c9a72a"]; @@ -199,15 +201,24 @@ {/if} {/each} - + {#each view.board.warps as w, i (i)} - {w.from.side === "N" ? "↑" : w.from.side === "S" ? "↓" : w.from.side === "E" ? "→" : "←"} + {@const wx = w.from.cell.x * CELL + CELL / 2 + + (w.from.side === "E" ? CELL * 0.42 : w.from.side === "W" ? -CELL * 0.42 : 0)} + {@const wy = w.from.cell.y * CELL + CELL / 2 + + (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)} + { ev.stopPropagation(); onWarpClick?.(w.from.cell, w.from.side); }} + onkeydown={() => {}} + > + + {w.from.side === "N" ? "↑" : w.from.side === "S" ? "↓" : w.from.side === "E" ? "→" : "←"} + {/each} @@ -321,7 +332,16 @@ .treasure { stroke: #2b2218; stroke-width: 1.3; } .treasure-swirl { fill: none; stroke: #2b2218; stroke-width: 1.2; stroke-dasharray: 5 2.2; } .treasure-core { fill: #2b2218; } - .warp { font-size: 14px; text-anchor: middle; fill: #2b2218; font-weight: bold; opacity: 0.65; } + .warp { font-size: 14px; text-anchor: middle; fill: #2b2218; font-weight: bold; opacity: 0.65; pointer-events: none; } + .warp-g { cursor: pointer; } + .warp-hit { fill: transparent; } + .warp-g:hover .warp-hit { fill: rgba(43, 34, 24, 0.12); } + .warp-g.standing .warp-hit { + fill: rgba(46, 125, 50, 0.25); + stroke: #2e7d32; + stroke-width: 2; + } + .warp-g.standing .warp { opacity: 1; } .wizard { cursor: pointer; } .wizard-label { font-family: "Oswald", sans-serif;