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;