Make wraparound warps usable: click the arrow to step through

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 <noreply@anthropic.com>
This commit is contained in:
Eric Wagoner
2026-08-15 23:28:51 -04:00
co-authored by Claude Fable 5
parent 3e1fe059fa
commit 87a5067c77
2 changed files with 41 additions and 9 deletions
+12
View File
@@ -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}
/>
</section>
+26 -6
View File
@@ -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}
<!-- warp openings -->
<!-- warp openings: click the arrow (while standing on it) to step through -->
{#each view.board.warps as w, i (i)}
<text
x={w.from.cell.x * CELL + CELL / 2 +
{@const wx = w.from.cell.x * CELL + CELL / 2 +
(w.from.side === "E" ? CELL * 0.42 : w.from.side === "W" ? -CELL * 0.42 : 0)}
y={w.from.cell.y * CELL + CELL / 2 + 3 +
{@const wy = w.from.cell.y * CELL + CELL / 2 +
(w.from.side === "S" ? CELL * 0.42 : w.from.side === "N" ? -CELL * 0.42 : 0)}
class="warp"
{@const standing = view.players.some((p) =>
p.id === view.you && p.position.x === w.from.cell.x && p.position.y === w.from.cell.y)}
<g
class="warp-g" class:standing
role="button" tabindex="-1"
onclick={(ev) => { ev.stopPropagation(); onWarpClick?.(w.from.cell, w.from.side); }}
onkeydown={() => {}}
>
<circle cx={wx} cy={wy} r={11} class="warp-hit" />
<text x={wx} y={wy + 4.5} class="warp"
>{w.from.side === "N" ? "↑" : w.from.side === "S" ? "↓" : w.from.side === "E" ? "→" : "←"}</text>
</g>
{/each}
<!-- wizards -->
@@ -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;