The interruption window opens for actual casting
OPPORTUNITY FIRE and INTERRUPT bought a moment the client would not
honor: every selection and targeting gate asked "is it your turn?",
and mid-interruption it is not — so the window's holder could click
attack cards into silence while the engine sat ready to accept the
cast. A window is now yourMoment: hand selection, player/creature/
cell/edge targeting, and the dimming aid all open exactly as on your
own turn, and the slip says what to do with it ("pick an attack card
and a target") instead of the cryptic "cast now".
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
94ec714e35
commit
44c14f5ce1
@@ -144,6 +144,11 @@
|
|||||||
view !== null && view.phase === "playing" && view.activePlayerId === view.you && !view.stack,
|
view !== null && view.phase === "playing" && view.activePlayerId === view.you && !view.stack,
|
||||||
);
|
);
|
||||||
const youMustRespond = $derived(view?.stack != null && view.stack.waitingOn === view.you);
|
const youMustRespond = $derived(view?.stack != null && view.stack.waitingOn === view.you);
|
||||||
|
/** An interruption window (INTERRUPT / OPPORTUNITY FIRE) is your moment:
|
||||||
|
* casting and targeting open up as if the turn were yours. */
|
||||||
|
const yourMoment = $derived(
|
||||||
|
isYourTurn || (view != null && view.outOfTurnWindow?.playerId === view.you),
|
||||||
|
);
|
||||||
const attackNoticeKey = $derived(
|
const attackNoticeKey = $derived(
|
||||||
youMustRespond && view?.stack
|
youMustRespond && view?.stack
|
||||||
? `${view.stack.attackerId}:${view.stack.attackCard?.instanceId ?? view.stack.creatureId ?? "punch"}:${view.stack.counters.length}`
|
? `${view.stack.attackerId}:${view.stack.attackCard?.instanceId ?? view.stack.creatureId ?? "punch"}:${view.stack.counters.length}`
|
||||||
@@ -379,7 +384,7 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (actionsSpent) return; // picking up an object ended the turn's actions
|
if (actionsSpent) return; // picking up an object ended the turn's actions
|
||||||
if (!isYourTurn) {
|
if (!isYourTurn && !yourMoment) {
|
||||||
// Live interruption: Interrupt / Opportunity Fire may be played during
|
// Live interruption: Interrupt / Opportunity Fire may be played during
|
||||||
// another player's turn (when no attack is pending).
|
// another player's turn (when no attack is pending).
|
||||||
if (!view.stack && (card.cardId === "interrupt" || card.cardId === "opportunity-fire")) {
|
if (!view.stack && (card.cardId === "interrupt" || card.cardId === "opportunity-fire")) {
|
||||||
@@ -497,7 +502,7 @@
|
|||||||
clearSelection();
|
clearSelection();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!isYourTurn) { peekAt(cell); return; }
|
if (!yourMoment) { peekAt(cell); return; }
|
||||||
if (pendingCellFor && selectedCard) {
|
if (pendingCellFor && selectedCard) {
|
||||||
// Stage 2 of teleport-opponent: destination chosen.
|
// Stage 2 of teleport-opponent: destination chosen.
|
||||||
dispatch({
|
dispatch({
|
||||||
@@ -727,7 +732,7 @@
|
|||||||
if (!view) return;
|
if (!view) return;
|
||||||
const creature = view.creatures.find((c) => c.id === creatureId);
|
const creature = view.creatures.find((c) => c.id === creatureId);
|
||||||
if (!creature) return;
|
if (!creature) return;
|
||||||
if (!isYourTurn) { peekAt(creature.position); return; }
|
if (!yourMoment) { peekAt(creature.position); return; }
|
||||||
if (selectedCard && CELL_CARDS.has(selectedCard.cardId)) {
|
if (selectedCard && CELL_CARDS.has(selectedCard.cardId)) {
|
||||||
// Cell-target spells aimed at a creature (DISPEL CREATION on a wraith)
|
// Cell-target spells aimed at a creature (DISPEL CREATION on a wraith)
|
||||||
// land on its square — the engine finds the creature there.
|
// land on its square — the engine finds the creature there.
|
||||||
@@ -762,7 +767,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function clickPlayer(playerId: string) {
|
function clickPlayer(playerId: string) {
|
||||||
if (!view || !isYourTurn) return;
|
if (!view || !yourMoment) return;
|
||||||
if (selectedCreature) {
|
if (selectedCreature) {
|
||||||
dispatch({ type: "creatureAttack", creatureId: selectedCreature, targetId: playerId });
|
dispatch({ type: "creatureAttack", creatureId: selectedCreature, targetId: playerId });
|
||||||
selectedCreature = null;
|
selectedCreature = null;
|
||||||
@@ -824,7 +829,7 @@
|
|||||||
);
|
);
|
||||||
/** Squares a selected L.O.S./ADJACENT card can reach; null = no dimming. */
|
/** Squares a selected L.O.S./ADJACENT card can reach; null = no dimming. */
|
||||||
const litCells = $derived.by(() => {
|
const litCells = $derived.by(() => {
|
||||||
if (!view || !selectedDef || !isYourTurn) return null;
|
if (!view || !selectedDef || !yourMoment) return null;
|
||||||
if (ambushVia || ambushSpell || ambushTrigger) return null; // ambushes aim at the future
|
if (ambushVia || ambushSpell || ambushTrigger) return null; // ambushes aim at the future
|
||||||
if (!me) return null;
|
if (!me) return null;
|
||||||
if (CELL_CARDS.has(selectedCard!.cardId)) {
|
if (CELL_CARDS.has(selectedCard!.cardId)) {
|
||||||
@@ -1308,7 +1313,7 @@
|
|||||||
<section class="board-zone">
|
<section class="board-zone">
|
||||||
<Board
|
<Board
|
||||||
{view}
|
{view}
|
||||||
edgeSelectMode={edgeSelectMode && isYourTurn}
|
edgeSelectMode={edgeSelectMode && yourMoment}
|
||||||
selectedCreatureId={selectedCreature}
|
selectedCreatureId={selectedCreature}
|
||||||
onCellClick={clickCell}
|
onCellClick={clickCell}
|
||||||
onEdgeClick={clickEdge}
|
onEdgeClick={clickEdge}
|
||||||
@@ -1347,7 +1352,7 @@
|
|||||||
{:else if view.stack}
|
{:else if view.stack}
|
||||||
<div class="slip">Waiting on {view.stack.waitingOn}…</div>
|
<div class="slip">Waiting on {view.stack.waitingOn}…</div>
|
||||||
{:else if view.outOfTurnWindow?.playerId === view.you}
|
{:else if view.outOfTurnWindow?.playerId === view.you}
|
||||||
<div class="slip urgent">Your interruption — cast now, or <button class="stamp tiny" onclick={pass}>waste it</button></div>
|
<div class="slip urgent">Your interruption — pick an attack card and a target, or <button class="stamp tiny" onclick={pass}>waste it</button></div>
|
||||||
{:else if isYourTurn}
|
{:else if isYourTurn}
|
||||||
<div class="slip yours">
|
<div class="slip yours">
|
||||||
Your turn — round {view.turn.round}.
|
Your turn — round {view.turn.round}.
|
||||||
|
|||||||
Reference in New Issue
Block a user