A creature's blow is named on the stack: the counter is played against the fire imp's scorch, not a punch (SC59)

The caster whose creature is countered now sees "your attack, answered"
and the name of their own blow, instead of an incoming punch with the
opponent's counter card standing in the attack slot.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
This commit is contained in:
Eric Wagoner
2026-09-24 21:32:34 -04:00
co-authored by Claude Fable 5.1
parent c3f63d4eb7
commit 7a0a972452
4 changed files with 58 additions and 10 deletions
+16 -6
View File
@@ -276,7 +276,11 @@
let peekTreasure = $state<{ src: string; note: string } | null>(null);
/** Cardless blows that a card nonetheless made: the stack names the
* blow, and this names the card to show for it. */
const BLOW_CARD: Record<string, string> = { "warded treasure": "ward" };
const BLOW_CARD: Record<string, string> = {
"warded treasure": "ward",
"fire imp's scorch": "fire-imp", "troll's blow": "troll", "skeleton's blow": "skeleton",
"wraith's touch": "wraith", "democratic monster's claw": "democratic-monster",
};
/** Bare-knuckle demolition: click a wall to punch it. */
let punchWallMode = $state(false);
@@ -415,6 +419,11 @@
? `${view.stack.attackerId}:${view.stack.attackCard?.instanceId ?? view.stack.creatureId ?? "punch"}:${view.stack.counters.length}`
: null,
);
/** The attack on the stack, named for its caster: the card, the creature's blow, or the punch. */
const yourAttackName = $derived(
view?.stack?.attackCard ? cardDef(view.stack.attackCard.cardId).name
: view?.stack?.sourceName ?? "punch",
);
const attackingCreature = $derived(
view?.stack?.creatureId ? view.creatures.find((c) => c.id === view!.stack!.creatureId) ?? null : null,
);
@@ -2424,7 +2433,7 @@
{:else if view.stack.defenderId === view.you}
<strong>{view.stack.attackerId}</strong> attacks you!
{:else}
<strong>{view.stack.counters[view.stack.counters.length - 1]?.player}</strong> counters your spell!
<strong>{view.stack.counters[view.stack.counters.length - 1]?.player}</strong> counters your {yourAttackName}!
{/if}
</div>
{#if view.stack.defenderId === view.you && attackingCreature}
@@ -2948,7 +2957,7 @@
: view.stack.sourceName ? `a ${view.stack.sourceName}` : "a punch"} —
tap a counteraction card, or
{:else}
{view.stack.waitingOn === view.you ? "They counter your spell — counter back, or" : ""}
{view.stack.waitingOn === view.you ? `They counter your ${yourAttackName} — counter back, or` : ""}
{/if}
</span>
<button class="stamp tiny" onclick={pass}>let it resolve</button>
@@ -3450,16 +3459,17 @@
{#if castPanelOn && view}
<div class="cast-panel" class:respond={youMustRespond} aria-label={youMustRespond ? "counteraction" : "casting"}>
{#if youMustRespond && view.stack}
<div class="cast-panel-head">⚔ incoming</div>
{@const defending = view.stack.defenderId === view.you}
<div class="cast-panel-head">{defending ? "⚔ incoming" : "⚔ your attack, answered"}</div>
{#if view.stack.attackCard}
<div class="cast-panel-card"><Card card={view.stack.attackCard} onfaq={(id) => (faqCardId = id)} /></div>
{:else if view.stack.sourceName}
{#if BLOW_CARD[view.stack.sourceName]}
<div class="cast-panel-card"><Card card={{ instanceId: `blow-${view.stack.sourceName}`, cardId: BLOW_CARD[view.stack.sourceName]! }} onfaq={(id) => (faqCardId = id)} /></div>
{/if}
<div class="cast-panel-note">{view.stack.sourceName} — it bites for {view.stack.params?.damage ?? 0}</div>
<div class="cast-panel-note">{defending ? "" : "your "}{view.stack.sourceName} — it bites for {view.stack.params?.damage ?? 0}</div>
{:else}
<div class="cast-panel-note">a physical blow — no card behind it</div>
<div class="cast-panel-note">{defending ? "a physical blow" : "your punch"} — no card behind it</div>
{/if}
{#if view.stack.numberValue != null || view.stack.amplifyFactor > 1}
<div class="cast-panel-note">
+5 -1
View File
@@ -52,7 +52,11 @@ export function humanize(e: GameEvent): string | null {
const at = e.target ? ` at ${e.target}` : "";
return `${e.caster} casts ${cardDef(e.cardId).name}${num}${amp}${at}.`;
}
case "counteractionPlayed": return `${e.player} counters with ${cardDef(e.cardId).name}!`;
case "counteractionPlayed": {
// A card's id has no spaces; a creature's blow or the ward's bite is named in words.
const what = e.against === "punch" ? "the punch" : /[ ']/.test(e.against) ? `the ${e.against}` : spellName(e.against);
return `${e.player} counters ${what} with ${cardDef(e.cardId).name}!`;
}
case "counterNullified": return `${cardDef(e.card.cardId).name} is nullified by Anti-Anti!`;
case "attackAbsorbedIntoHand": return `${e.player} absorbs ${cardDef(e.attackCard.cardId).name} into their hand — the card is theirs now!`;
case "attackResolved":