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,
|
||||
);
|
||||
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(
|
||||
youMustRespond && view?.stack
|
||||
? `${view.stack.attackerId}:${view.stack.attackCard?.instanceId ?? view.stack.creatureId ?? "punch"}:${view.stack.counters.length}`
|
||||
@@ -379,7 +384,7 @@
|
||||
return;
|
||||
}
|
||||
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
|
||||
// another player's turn (when no attack is pending).
|
||||
if (!view.stack && (card.cardId === "interrupt" || card.cardId === "opportunity-fire")) {
|
||||
@@ -497,7 +502,7 @@
|
||||
clearSelection();
|
||||
return;
|
||||
}
|
||||
if (!isYourTurn) { peekAt(cell); return; }
|
||||
if (!yourMoment) { peekAt(cell); return; }
|
||||
if (pendingCellFor && selectedCard) {
|
||||
// Stage 2 of teleport-opponent: destination chosen.
|
||||
dispatch({
|
||||
@@ -727,7 +732,7 @@
|
||||
if (!view) return;
|
||||
const creature = view.creatures.find((c) => c.id === creatureId);
|
||||
if (!creature) return;
|
||||
if (!isYourTurn) { peekAt(creature.position); return; }
|
||||
if (!yourMoment) { peekAt(creature.position); return; }
|
||||
if (selectedCard && CELL_CARDS.has(selectedCard.cardId)) {
|
||||
// Cell-target spells aimed at a creature (DISPEL CREATION on a wraith)
|
||||
// land on its square — the engine finds the creature there.
|
||||
@@ -762,7 +767,7 @@
|
||||
}
|
||||
|
||||
function clickPlayer(playerId: string) {
|
||||
if (!view || !isYourTurn) return;
|
||||
if (!view || !yourMoment) return;
|
||||
if (selectedCreature) {
|
||||
dispatch({ type: "creatureAttack", creatureId: selectedCreature, targetId: playerId });
|
||||
selectedCreature = null;
|
||||
@@ -824,7 +829,7 @@
|
||||
);
|
||||
/** Squares a selected L.O.S./ADJACENT card can reach; null = no dimming. */
|
||||
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 (!me) return null;
|
||||
if (CELL_CARDS.has(selectedCard!.cardId)) {
|
||||
@@ -1308,7 +1313,7 @@
|
||||
<section class="board-zone">
|
||||
<Board
|
||||
{view}
|
||||
edgeSelectMode={edgeSelectMode && isYourTurn}
|
||||
edgeSelectMode={edgeSelectMode && yourMoment}
|
||||
selectedCreatureId={selectedCreature}
|
||||
onCellClick={clickCell}
|
||||
onEdgeClick={clickEdge}
|
||||
@@ -1347,7 +1352,7 @@
|
||||
{:else if view.stack}
|
||||
<div class="slip">Waiting on {view.stack.waitingOn}…</div>
|
||||
{: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}
|
||||
<div class="slip yours">
|
||||
Your turn — round {view.turn.round}.
|
||||
|
||||
Reference in New Issue
Block a user