Three table manners: Safari replays, docked creature cards, a quiet hand
Safari collapsed the replay board to nothing: an svg with only a viewBox has no intrinsic size there, and inside the replay's max-height flex column WebKit sized it 0x0 while Chrome inferred the ratio and filled the frame. Explicit width/height attributes (doubled, so the CSS max-* caps still govern the display size) give every engine the same answer — verified 560x560 in both WebKit and Chromium, with the main board-zone rendering unchanged. Commanding your own creature no longer opens the centered card peek — a modal squarely blocks the squares you are about to march it across. The card now docks in the paper rail with its live stats, the one place a card enlarges outside the center of the table. Enemy creatures and idle peeks keep the modal; they ask nothing of the board. And once an object is pocketed, the hand goes visibly quiet: cards dim and refuse selection instead of walking you through target-picking toward an engine refusal, with a rail slip saying what remains (draw and end the turn). Counters, chaos shields, and discards still play — an ambush sprung by the very pickup must be answerable. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
7bd82d7679
commit
1275ac9287
+44
-11
@@ -108,6 +108,17 @@
|
||||
);
|
||||
const youMustDiscard = $derived(view != null && view.pendingDiscard === view.you);
|
||||
const youMustShield = $derived(view?.chaosPending?.queue[0] === view?.you && view != null);
|
||||
/** Picking up an object ends the turn's actions: the hand goes quiet until
|
||||
* the draw — but never while a counter, shield, or discard is owed. */
|
||||
const actionsSpent = $derived(
|
||||
view != null && isYourTurn && view.turn.actionsEnded &&
|
||||
!youMustRespond && !youMustShield && !youMustDiscard,
|
||||
);
|
||||
const commandedCreature = $derived(
|
||||
selectedCreature && view
|
||||
? (view.creatures.find((c) => c.id === selectedCreature) ?? null)
|
||||
: null,
|
||||
);
|
||||
const holdingWard = $derived(view?.yourHand.some((c) => c.cardId === "ward") ?? false);
|
||||
/** A live moment to interrupt: another wizard acts, no attack is pending. */
|
||||
const canInterruptNow = $derived(
|
||||
@@ -228,6 +239,7 @@
|
||||
discardSelection = next;
|
||||
return;
|
||||
}
|
||||
if (actionsSpent) return; // picking up an object ended the turn's actions
|
||||
if (!isYourTurn) {
|
||||
// Live interruption: Interrupt / Opportunity Fire may be played during
|
||||
// another player's turn (when no attack is pending).
|
||||
@@ -571,15 +583,10 @@
|
||||
}
|
||||
const mine = creature.controllerId === view.you || creature.kind === "democratic-monster";
|
||||
if (mine) {
|
||||
const selecting = selectedCreature !== creatureId;
|
||||
selectedCreature = selecting ? creatureId : null;
|
||||
if (selecting) {
|
||||
peekCard = { instanceId: `peek-${creature.kind}`, cardId: creature.kind };
|
||||
peekCreatureId = creature.id;
|
||||
} else {
|
||||
peekCard = null;
|
||||
peekCreatureId = null;
|
||||
}
|
||||
// Commanding your own creature docks its card in the rail instead of
|
||||
// opening the centered peek — a modal would block the very board
|
||||
// squares you are about to march it across.
|
||||
selectedCreature = selectedCreature !== creatureId ? creatureId : null;
|
||||
} else {
|
||||
peekAt(creature.position);
|
||||
}
|
||||
@@ -1153,6 +1160,18 @@
|
||||
Tap its token to command it.
|
||||
</div>
|
||||
{/each}
|
||||
{#if commandedCreature}
|
||||
<div class="creature-dock">
|
||||
<Card
|
||||
card={{ instanceId: `dock-${commandedCreature.kind}`, cardId: commandedCreature.kind }}
|
||||
onfaq={(id) => (faqCardId = id)}
|
||||
/>
|
||||
<div class="peek-note">{creatureStats(commandedCreature)}</div>
|
||||
</div>
|
||||
{/if}
|
||||
{#if actionsSpent}
|
||||
<div class="slip">Your spellwork is spent for this turn — all that remains is to draw and end it.</div>
|
||||
{/if}
|
||||
|
||||
{#if view.yourAmbushes.length > 0}
|
||||
{#each view.yourAmbushes as a (a.id)}
|
||||
@@ -1258,7 +1277,7 @@
|
||||
{#if sc}
|
||||
<span>Commanding <strong>{cardDef(sc.kind).name}</strong> — {creatureStats(sc)}.
|
||||
Tap a square beside it to march, a target in its square to attack.</span>
|
||||
<button class="hint-cancel" onclick={() => { selectedCreature = null; peekCard = null; peekCreatureId = null; }}>done</button>
|
||||
<button class="hint-cancel" onclick={() => (selectedCreature = null)}>done</button>
|
||||
{/if}
|
||||
{/if}
|
||||
{#if youMustRespond && view.stack}
|
||||
@@ -1427,7 +1446,7 @@
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="hand" aria-label="your hand">
|
||||
<div class="hand" class:spent={actionsSpent && !discardMode && discardSelection.size === 0} aria-label="your hand">
|
||||
{#each view.phase === "finished" ? [] : view.yourHand as card (card.instanceId)}
|
||||
<Card
|
||||
{card}
|
||||
@@ -2018,6 +2037,20 @@
|
||||
.big-peek :global(.card:hover) { transform: scale(2.1); }
|
||||
.big-peek { display: flex; flex-direction: column; align-items: center; }
|
||||
.big-peek-note { margin-top: 6.8rem; max-width: 15rem; font-size: 0.8rem; }
|
||||
.creature-dock {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 0.3rem;
|
||||
}
|
||||
.creature-dock :global(.card) { cursor: default; }
|
||||
.creature-dock :global(.card:hover) { transform: none; }
|
||||
.hand.spent :global(.card) {
|
||||
opacity: 0.45;
|
||||
filter: grayscale(0.5);
|
||||
cursor: default;
|
||||
}
|
||||
.hand.spent :global(.card:hover) { transform: none; }
|
||||
.tier-pick {
|
||||
background: #f6f0df;
|
||||
border: 1px solid #b3a687;
|
||||
|
||||
Reference in New Issue
Block a user