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:
co-authored by
Claude Fable 5
parent
22153b1e1b
commit
04dc9bc750
@@ -95,6 +95,8 @@
|
|||||||
let rotateCW = $state(true);
|
let rotateCW = $state(true);
|
||||||
/** mega-monster: which stat the chosen monster doubles. */
|
/** mega-monster: which stat the chosen monster doubles. */
|
||||||
let megaBoost = $state<"life" | "movement">("life");
|
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. */
|
/** Cards marked for discard. */
|
||||||
let discardSelection = $state<Set<string>>(new Set());
|
let discardSelection = $state<Set<string>>(new Set());
|
||||||
/** Voluntary discard mode: hand clicks mark cards instead of playing them. */
|
/** Voluntary discard mode: hand clicks mark cards instead of playing them. */
|
||||||
@@ -171,6 +173,16 @@
|
|||||||
return out;
|
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 }) {
|
function clickGhostSlot(origin: { x: number; y: number }) {
|
||||||
if (!selectedCard || !pendingSectorFrom) return;
|
if (!selectedCard || !pendingSectorFrom) return;
|
||||||
dispatch({
|
dispatch({
|
||||||
@@ -237,6 +249,7 @@
|
|||||||
selectedCreature = null;
|
selectedCreature = null;
|
||||||
selectedCard = null;
|
selectedCard = null;
|
||||||
megaBoost = "life";
|
megaBoost = "life";
|
||||||
|
pendingTeleport = null;
|
||||||
attachedNumber = null;
|
attachedNumber = null;
|
||||||
attachedMods = [];
|
attachedMods = [];
|
||||||
wbDamage = 0;
|
wbDamage = 0;
|
||||||
@@ -419,6 +432,12 @@
|
|||||||
function clickCell(cell: { x: number; y: number }) {
|
function clickCell(cell: { x: number; y: number }) {
|
||||||
if (!view) return;
|
if (!view) return;
|
||||||
if (youMustRespond && selectedCard?.cardId === "teleport") {
|
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 } });
|
dispatch({ type: "counteract", instanceId: selectedCard.instanceId, params: { cell } });
|
||||||
clearSelection();
|
clearSelection();
|
||||||
return;
|
return;
|
||||||
@@ -434,6 +453,16 @@
|
|||||||
clearSelection();
|
clearSelection();
|
||||||
return;
|
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") {
|
if (selectedCard?.cardId === "relocate-sector") {
|
||||||
// Any board click (re-)picks the sector; the landing is chosen from the
|
// 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.
|
// dashed ghost slots beyond the maze, since every on-board slot is taken.
|
||||||
@@ -1194,7 +1223,7 @@
|
|||||||
onPlayerClick={clickPlayer}
|
onPlayerClick={clickPlayer}
|
||||||
onCreatureClick={clickCreature}
|
onCreatureClick={clickCreature}
|
||||||
onWarpClick={clickWarp}
|
onWarpClick={clickWarp}
|
||||||
markedCell={tradeFrom}
|
markedCell={tradeFrom ?? pendingTeleport}
|
||||||
markedSector={pendingSectorOrigin}
|
markedSector={pendingSectorOrigin}
|
||||||
ghostSlots={relocateGhosts}
|
ghostSlots={relocateGhosts}
|
||||||
onGhostClick={clickGhostSlot}
|
onGhostClick={clickGhostSlot}
|
||||||
@@ -1381,7 +1410,9 @@
|
|||||||
{#if youMustRespond && view.stack}
|
{#if youMustRespond && view.stack}
|
||||||
<span class="hint-alert">
|
<span class="hint-alert">
|
||||||
{#if view.stack.defenderId === view.you && selectedCard?.cardId === "teleport"}
|
{#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}
|
{:else if view.stack.defenderId === view.you}
|
||||||
{view.stack.attackerId} attacks with
|
{view.stack.attackerId} attacks with
|
||||||
{attackingCreature ? `the ${cardDef(attackingCreature.kind).name}` : view.stack.attackCard ? cardDef(view.stack.attackCard.cardId).name : "a punch"} —
|
{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)}
|
{:else if selectedCard && TWO_CELL_CARDS.has(selectedCard.cardId)}
|
||||||
<span>{tradeFrom ? "— now the second square" : "— click the first square"}</span>
|
<span>{tradeFrom ? "— now the second square" : "— click the first square"}</span>
|
||||||
{/if}
|
{/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"}
|
{#if selectedCard?.cardId === "mega-monster"}
|
||||||
<span>— double its</span>
|
<span>— double its</span>
|
||||||
<button class="stamp tiny" class:lit={megaBoost === "life"}
|
<button class="stamp tiny" class:lit={megaBoost === "life"}
|
||||||
|
|||||||
Reference in New Issue
Block a user