Teleport waits for a second tap

One tap cast the jump, and a player reaching for click-per-square
movement spent their teleport on the adjacent floor. Now the first
tap only marks the destination (the familiar pulsing square), and the
jump commits on a second tap of the same square or the hint-bar
button — with the hint spelling out the four-square, walls-ignored
range. The escape teleport gets the same two-tap arming; a counter
wasted on a misclick is a counter wasted.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Eric Wagoner
2026-08-16 21:49:27 -04:00
co-authored by Claude Fable 5
parent 22153b1e1b
commit 04dc9bc750
+41 -2
View File
@@ -95,6 +95,8 @@
let rotateCW = $state(true);
/** mega-monster: which stat the chosen monster doubles. */
let megaBoost = $state<"life" | "movement">("life");
/** teleport: the marked destination awaiting its confirming second tap. */
let pendingTeleport = $state<{ x: number; y: number } | null>(null);
/** Cards marked for discard. */
let discardSelection = $state<Set<string>>(new Set());
/** Voluntary discard mode: hand clicks mark cards instead of playing them. */
@@ -171,6 +173,16 @@
return out;
});
function confirmTeleport() {
if (!selectedCard || !pendingTeleport) return;
dispatch({
type: "cast", instanceId: selectedCard.instanceId,
target: { kind: "cell", cell: pendingTeleport },
...(attachedNumber ? { numberInstanceIds: [attachedNumber.instanceId] } : {}),
});
clearSelection();
}
function clickGhostSlot(origin: { x: number; y: number }) {
if (!selectedCard || !pendingSectorFrom) return;
dispatch({
@@ -237,6 +249,7 @@
selectedCreature = null;
selectedCard = null;
megaBoost = "life";
pendingTeleport = null;
attachedNumber = null;
attachedMods = [];
wbDamage = 0;
@@ -419,6 +432,12 @@
function clickCell(cell: { x: number; y: number }) {
if (!view) return;
if (youMustRespond && selectedCard?.cardId === "teleport") {
// Mark first, jump on the second tap: an escape spent on a misclick
// is an escape wasted.
if (!pendingTeleport || cellKey(pendingTeleport) !== cellKey(cell)) {
pendingTeleport = cell;
return;
}
dispatch({ type: "counteract", instanceId: selectedCard.instanceId, params: { cell } });
clearSelection();
return;
@@ -434,6 +453,16 @@
clearSelection();
return;
}
if (selectedCard?.cardId === "teleport") {
// Teleport is one jump, not a walk: the first tap only marks the
// destination, the second (or the hint-bar button) commits it.
if (!pendingTeleport || cellKey(pendingTeleport) !== cellKey(cell)) {
pendingTeleport = cell;
return;
}
confirmTeleport();
return;
}
if (selectedCard?.cardId === "relocate-sector") {
// Any board click (re-)picks the sector; the landing is chosen from the
// dashed ghost slots beyond the maze, since every on-board slot is taken.
@@ -1194,7 +1223,7 @@
onPlayerClick={clickPlayer}
onCreatureClick={clickCreature}
onWarpClick={clickWarp}
markedCell={tradeFrom}
markedCell={tradeFrom ?? pendingTeleport}
markedSector={pendingSectorOrigin}
ghostSlots={relocateGhosts}
onGhostClick={clickGhostSlot}
@@ -1381,7 +1410,9 @@
{#if youMustRespond && view.stack}
<span class="hint-alert">
{#if view.stack.defenderId === view.you && selectedCard?.cardId === "teleport"}
Escaping by teleport — click a square within four spaces.
{pendingTeleport
? "Escaping by teleport — tap the marked square again to jump."
: "Escaping by teleport — tap a square within four spaces to mark it."}
{:else if view.stack.defenderId === view.you}
{view.stack.attackerId} attacks with
{attackingCreature ? `the ${cardDef(attackingCreature.kind).name}` : view.stack.attackCard ? cardDef(view.stack.attackCard.cardId).name : "a punch"} —
@@ -1422,6 +1453,14 @@
{:else if selectedCard && TWO_CELL_CARDS.has(selectedCard.cardId)}
<span>{tradeFrom ? "— now the second square" : "— click the first square"}</span>
{/if}
{#if selectedCard?.cardId === "teleport" && !youMustRespond}
<span>{pendingTeleport
? "— tap the marked square again to jump, or pick another"
: "— tap a destination (up to four squares, walls ignored)"}</span>
{#if pendingTeleport}
<button class="stamp tiny" onclick={confirmTeleport}>teleport!</button>
{/if}
{/if}
{#if selectedCard?.cardId === "mega-monster"}
<span> double its</span>
<button class="stamp tiny" class:lit={megaBoost === "life"}