Voluntary discards, and no card ever casts on the first click
The rulebook allows discarding cards purely to churn for new ones, but the client had no way to start it: a "Discard cards..." action now switches the hand into marking mode (with a "never mind" exit), and confirming sends the discard. Alongside it, the hair-trigger casts are gone: selecting a card never plays it anymore — untargeted spells and stone displays get a Cast button in the hint bar, and a bare number card gets an explicit "Play for +N movement" button. Every play is now a two-step, which is the correct cost in a game with no undo. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
d7afe3a5ca
commit
42ea9b8f2c
+37
-17
@@ -38,6 +38,8 @@
|
||||
let rotateCW = $state(true);
|
||||
/** Cards marked for discard. */
|
||||
let discardSelection = $state<Set<string>>(new Set());
|
||||
/** Voluntary discard mode: hand clicks mark cards instead of playing them. */
|
||||
let discardMode = $state(false);
|
||||
|
||||
const view = $derived(net.view);
|
||||
const isYourTurn = $derived(view !== null && view.activePlayerId === view.you && !view.stack);
|
||||
@@ -61,9 +63,7 @@
|
||||
const TWO_CELL_CARDS = new Set(["trader", "dimensional-warp", "redirection"]);
|
||||
const CREATURE_TARGET_CARDS = new Set(["mega-monster"]);
|
||||
const MODIFIER_CARDS = new Set(["amplify", "add", "extend", "around-the-corner"]);
|
||||
const SELF_CARDS = new Set([
|
||||
"invisible", "shrink", "mist-body", "strength", "empathy", "big-man", "fear", "adrenaline",
|
||||
]);
|
||||
|
||||
const NAMED_CARDS = new Set(["card-erasure", "drop-object", "deja-vu", "thief", "swap-meet", "remove-curse", "swarthmores-enchantment", "illusionary-attack"]);
|
||||
const edgeSelectMode = $derived(selectedCard != null && EDGE_CARDS.has(selectedCard.cardId));
|
||||
const cellSelectMode = $derived(
|
||||
@@ -111,7 +111,7 @@
|
||||
net.command({ type: "counteract", instanceId: card.instanceId });
|
||||
return;
|
||||
}
|
||||
if (youMustDiscard || discardSelection.size > 0) {
|
||||
if (youMustDiscard || discardMode || discardSelection.size > 0) {
|
||||
const next = new Set(discardSelection);
|
||||
next.has(card.instanceId) ? next.delete(card.instanceId) : next.add(card.instanceId);
|
||||
discardSelection = next;
|
||||
@@ -135,24 +135,32 @@
|
||||
}
|
||||
selectedCard = card;
|
||||
attachedNumber = null;
|
||||
if (isNumberCard(card.cardId)) {
|
||||
// A bare number card: play it for movement.
|
||||
net.command({ type: "playNumberForMovement", instanceId: card.instanceId });
|
||||
clearSelection();
|
||||
return;
|
||||
// Nothing casts on first click: every play is confirmed from the hint
|
||||
// bar or by choosing a target. Misclicks cost nothing.
|
||||
}
|
||||
// Instant untargeted spells (and stone displays) cast immediately;
|
||||
// duration self-spells wait so a number card can be attached.
|
||||
const INSTANT = new Set([
|
||||
|
||||
/** Untargeted spells confirmed from the hint bar (with optional number). */
|
||||
const CONFIRM_CAST = new Set([
|
||||
"speed", "pass-through-wall", "reuse-spell", "ugly", "alter-ego", "lifesaver", "mad-dash",
|
||||
"gift-from-above", "chaos", "interrupt", "opportunity-fire",
|
||||
"bloodstone", "brainstone", "powerstone", "shadowstone",
|
||||
"shieldstone", "soulstone", "speedstone", "visionstone",
|
||||
"invisible", "shrink", "mist-body", "strength", "empathy", "big-man", "fear", "adrenaline",
|
||||
]);
|
||||
if (INSTANT.has(card.cardId)) {
|
||||
net.command({ type: "cast", instanceId: card.instanceId });
|
||||
|
||||
function playNumberForMovement() {
|
||||
if (!selectedCard) return;
|
||||
net.command({ type: "playNumberForMovement", instanceId: selectedCard.instanceId });
|
||||
clearSelection();
|
||||
}
|
||||
|
||||
function startDiscardMode() {
|
||||
clearSelection();
|
||||
discardMode = true;
|
||||
}
|
||||
function cancelDiscardMode() {
|
||||
discardMode = false;
|
||||
discardSelection = new Set();
|
||||
}
|
||||
|
||||
function castSelfWithNumber() {
|
||||
@@ -351,6 +359,7 @@
|
||||
function doDiscard() {
|
||||
net.command({ type: "discard", instanceIds: [...discardSelection] });
|
||||
discardSelection = new Set();
|
||||
discardMode = false;
|
||||
}
|
||||
|
||||
function endTurn() {
|
||||
@@ -519,14 +528,19 @@
|
||||
</div>
|
||||
|
||||
<div class="table-edge">
|
||||
{#if selectedDef || youMustDiscard || discardSelection.size > 0}
|
||||
{#if selectedDef || youMustDiscard || discardMode || discardSelection.size > 0}
|
||||
<div class="hint-strip">
|
||||
{#if youMustDiscard}
|
||||
<span class="hint-alert">Hand over the limit — mark cards and discard.</span>
|
||||
{:else if discardMode && discardSelection.size === 0}
|
||||
<span>Mark the cards to throw away, then confirm.</span>
|
||||
{/if}
|
||||
{#if discardSelection.size > 0}
|
||||
<button class="stamp tiny" onclick={doDiscard}>Discard {discardSelection.size} marked</button>
|
||||
{/if}
|
||||
{#if discardMode || (discardSelection.size > 0 && !youMustDiscard)}
|
||||
<button class="hint-cancel" onclick={cancelDiscardMode}>never mind</button>
|
||||
{/if}
|
||||
{#if selectedDef}
|
||||
<strong class="hint-name">{selectedDef.name}</strong>
|
||||
{#if edgeSelectMode}<span>— click a wall line</span>{/if}
|
||||
@@ -571,9 +585,14 @@
|
||||
<label class="inline">life to trade <input class="num-input" type="number" min="1" max="10" bind:value={runPoints} /></label>
|
||||
<button class="stamp tiny" onclick={castPowerRun}>Run!</button>
|
||||
{/if}
|
||||
{#if selectedCard && SELF_CARDS.has(selectedCard.cardId)}
|
||||
{#if selectedCard && CONFIRM_CAST.has(selectedCard.cardId)}
|
||||
<button class="stamp tiny" onclick={castSelfWithNumber}>
|
||||
Cast{attachedNumber ? ` with the ${numberTotal}` : " (duration 1)"}
|
||||
Cast{attachedNumber ? ` with the ${numberTotal}` : ""}
|
||||
</button>
|
||||
{/if}
|
||||
{#if selectedCard && isNumberCard(selectedCard.cardId)}
|
||||
<button class="stamp tiny" onclick={playNumberForMovement}>
|
||||
Play for +{cardDef(selectedCard.cardId).value} movement
|
||||
</button>
|
||||
{/if}
|
||||
<button class="hint-cancel" onclick={clearSelection}>cancel</button>
|
||||
@@ -589,6 +608,7 @@
|
||||
<button class="stamp" disabled={!treasureHere || carryingTreasure || view.turn.actionsEnded}
|
||||
onclick={pickUp}>Pick up treasure</button>
|
||||
<button class="stamp" disabled={!carryingTreasure} onclick={drop}>Drop treasure</button>
|
||||
<button class="stamp" onclick={startDiscardMode}>Discard cards…</button>
|
||||
<label class="inline draw-pick">
|
||||
draw
|
||||
<select bind:value={drawCount}>
|
||||
|
||||
Reference in New Issue
Block a user