A sprung Ward is named as one, not as a punch
Eric grabbed a warded treasure and took the Ward's three points — but
the attack modal showed a bare-knuckled punch, the hint line said the
owner "attacks with a punch", and the cast panel called it a physical
blow with no card behind it. The engine knew better: the stack has
carried sourceName ("warded treasure") since rev 9, and the damage
line used it. The client never read it.
Now every cardless stack with a name shows that name — and, for the
Ward, the Ward card itself — in the modal, the hint line, and the
cast panel, with the damage inbound. In the chronicle, a counteraction
played against it records "warded treasure" rather than "punch", and
an EMPATHY echo of it names the same. Chronicle-only, ungated; all 58
production ledgers replay clean.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
3723b8f211
commit
3f909b485b
@@ -5779,7 +5779,7 @@ function doCounteract(
|
|||||||
}
|
}
|
||||||
player.hand.push(attackCard);
|
player.hand.push(attackCard);
|
||||||
const events: GameEvent[] = [
|
const events: GameEvent[] = [
|
||||||
{ type: "counteractionPlayed", player: playerId, card, cardId: card.cardId, against: stack.attackCard?.cardId ?? "punch" },
|
{ type: "counteractionPlayed", player: playerId, card, cardId: card.cardId, against: stack.attackCard?.cardId ?? stack.sourceName ?? "punch" },
|
||||||
{ type: "attackAbsorbedIntoHand", player: playerId, attackCard },
|
{ type: "attackAbsorbedIntoHand", player: playerId, attackCard },
|
||||||
{ type: "attackResolved", attacker: stack.attackerId, defender: stack.defenderId, attackCardId: attackCard.cardId, damageDealt: 0, reflectedDamage: 0, fullyStopped: true, redirected: false },
|
{ type: "attackResolved", attacker: stack.attackerId, defender: stack.defenderId, attackCardId: attackCard.cardId, damageDealt: 0, reflectedDamage: 0, fullyStopped: true, redirected: false },
|
||||||
];
|
];
|
||||||
@@ -5809,7 +5809,7 @@ function doCounteract(
|
|||||||
return {
|
return {
|
||||||
ok: true,
|
ok: true,
|
||||||
state,
|
state,
|
||||||
events: [{ type: "counteractionPlayed", player: playerId, card, cardId: card.cardId, against: stack.attackCard?.cardId ?? "punch" }],
|
events: [{ type: "counteractionPlayed", player: playerId, card, cardId: card.cardId, against: stack.attackCard?.cardId ?? stack.sourceName ?? "punch" }],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5821,7 +5821,7 @@ function doCounteract(
|
|||||||
takeFromHand(player, instanceId);
|
takeFromHand(player, instanceId);
|
||||||
state.discard.push(card);
|
state.discard.push(card);
|
||||||
const events: GameEvent[] = [
|
const events: GameEvent[] = [
|
||||||
{ type: "counteractionPlayed", player: playerId, card, cardId: card.cardId, against: stack.attackCard?.cardId ?? "punch" },
|
{ type: "counteractionPlayed", player: playerId, card, cardId: card.cardId, against: stack.attackCard?.cardId ?? stack.sourceName ?? "punch" },
|
||||||
];
|
];
|
||||||
attachSustained(state, events, "invisible", playerId, playerId, duration);
|
attachSustained(state, events, "invisible", playerId, playerId, duration);
|
||||||
stack.counters.push({ player: playerId, card, nullified: false });
|
stack.counters.push({ player: playerId, card, nullified: false });
|
||||||
@@ -5838,7 +5838,7 @@ function doCounteract(
|
|||||||
takeFromHand(player, instanceId);
|
takeFromHand(player, instanceId);
|
||||||
state.discard.push(card);
|
state.discard.push(card);
|
||||||
const events: GameEvent[] = [
|
const events: GameEvent[] = [
|
||||||
{ type: "counteractionPlayed", player: playerId, card, cardId: card.cardId, against: stack.attackCard?.cardId ?? "punch" },
|
{ type: "counteractionPlayed", player: playerId, card, cardId: card.cardId, against: stack.attackCard?.cardId ?? stack.sourceName ?? "punch" },
|
||||||
];
|
];
|
||||||
attachSustained(state, events, "empathy", playerId, playerId, duration);
|
attachSustained(state, events, "empathy", playerId, playerId, duration);
|
||||||
stack.counters.push({ player: playerId, card, nullified: false });
|
stack.counters.push({ player: playerId, card, nullified: false });
|
||||||
@@ -5877,7 +5877,7 @@ function doCounteract(
|
|||||||
return {
|
return {
|
||||||
ok: true,
|
ok: true,
|
||||||
state,
|
state,
|
||||||
events: [{ type: "counteractionPlayed", player: playerId, card, cardId: card.cardId, against: stack.attackCard?.cardId ?? "punch" }],
|
events: [{ type: "counteractionPlayed", player: playerId, card, cardId: card.cardId, against: stack.attackCard?.cardId ?? stack.sourceName ?? "punch" }],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5897,7 +5897,7 @@ function doCounteract(
|
|||||||
return {
|
return {
|
||||||
ok: true,
|
ok: true,
|
||||||
state,
|
state,
|
||||||
events: [{ type: "counteractionPlayed", player: playerId, card, cardId: card.cardId, against: stack.attackCard?.cardId ?? "punch" }],
|
events: [{ type: "counteractionPlayed", player: playerId, card, cardId: card.cardId, against: stack.attackCard?.cardId ?? stack.sourceName ?? "punch" }],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6246,7 +6246,7 @@ function resolveStack(state: GameState, events: GameEvent[]): void {
|
|||||||
// EMPATHY: "Any attack done in any form against you acts against both
|
// EMPATHY: "Any attack done in any form against you acts against both
|
||||||
// you and the caster of the spell."
|
// you and the caster of the spell."
|
||||||
if (damageDealt > 0 && sustainedOn(state, defender.id, "empathy").length > 0 && attacker.alive) {
|
if (damageDealt > 0 && sustainedOn(state, defender.id, "empathy").length > 0 && attacker.alive) {
|
||||||
applyDamage(state, events, attacker, damageDealt, `${attackId ?? "punch"} (empathy)`, defender.id, pipe.kind);
|
applyDamage(state, events, attacker, damageDealt, `${attackId ?? stack.sourceName ?? "punch"} (empathy)`, defender.id, pipe.kind);
|
||||||
}
|
}
|
||||||
// SHADOWSTONE: physical damage you deal feeds your life total.
|
// SHADOWSTONE: physical damage you deal feeds your life total.
|
||||||
if (damageDealt > 0 && pipe.kind === "physical" && displays(attacker, "shadowstone") && attacker.alive) {
|
if (damageDealt > 0 && pipe.kind === "physical" && displays(attacker, "shadowstone") && attacker.alive) {
|
||||||
|
|||||||
@@ -230,6 +230,10 @@
|
|||||||
let peekToken = $state<string | null>(null);
|
let peekToken = $state<string | null>(null);
|
||||||
/** Ledger chest tapped: the treasure full-size, with whose and who-holds. */
|
/** Ledger chest tapped: the treasure full-size, with whose and who-holds. */
|
||||||
let peekTreasure = $state<{ src: string; note: string } | null>(null);
|
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" };
|
||||||
|
|
||||||
/** Bare-knuckle demolition: click a wall to punch it. */
|
/** Bare-knuckle demolition: click a wall to punch it. */
|
||||||
let punchWallMode = $state(false);
|
let punchWallMode = $state(false);
|
||||||
/** Leafing through the face-up discard pile. */
|
/** Leafing through the face-up discard pile. */
|
||||||
@@ -1864,6 +1868,15 @@
|
|||||||
{view.stack.amplifyFactor > 1 ? ` — AMPLIFIED ×${view.stack.amplifyFactor}` : ""}
|
{view.stack.amplifyFactor > 1 ? ` — AMPLIFIED ×${view.stack.amplifyFactor}` : ""}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
{:else if view.stack.defenderId === view.you && view.stack.sourceName}
|
||||||
|
<!-- A cardless bite with a name — the WARD springing on a grabbed
|
||||||
|
treasure. The card that did it is shown; nobody threw it. -->
|
||||||
|
{#if BLOW_CARD[view.stack.sourceName]}
|
||||||
|
<div class="attack-card"><Card card={{ instanceId: `blow-${view.stack.sourceName}`, cardId: BLOW_CARD[view.stack.sourceName]! }} onfaq={(id) => (faqCardId = id)} /></div>
|
||||||
|
{/if}
|
||||||
|
<div class="attack-power">
|
||||||
|
{view.stack.sourceName.toUpperCase()} — {view.stack.params?.damage ?? 0} damage inbound unless you answer it
|
||||||
|
</div>
|
||||||
{:else if view.stack.defenderId === view.you}
|
{:else if view.stack.defenderId === view.you}
|
||||||
<div class="attack-fist">👊 a bare-knuckled punch</div>
|
<div class="attack-fist">👊 a bare-knuckled punch</div>
|
||||||
{:else if view.stack.counters.length > 0}
|
{:else if view.stack.counters.length > 0}
|
||||||
@@ -2194,7 +2207,8 @@
|
|||||||
: view.stack.attackCard?.cardId === "illusionary-attack" && view.stack.params?.cardId
|
: view.stack.attackCard?.cardId === "illusionary-attack" && view.stack.params?.cardId
|
||||||
? `an illusionary ${cardDef(view.stack.params.cardId).name}`
|
? `an illusionary ${cardDef(view.stack.params.cardId).name}`
|
||||||
: view.stack.attackCard ? cardDef(view.stack.attackCard.cardId).name
|
: view.stack.attackCard ? cardDef(view.stack.attackCard.cardId).name
|
||||||
: view.stack.tearTreasure ? "a grab at your treasure" : "a punch"} —
|
: view.stack.tearTreasure ? "a grab at your treasure"
|
||||||
|
: view.stack.sourceName ? `a ${view.stack.sourceName}` : "a punch"} —
|
||||||
tap a counteraction card, or
|
tap a counteraction card, or
|
||||||
{:else}
|
{:else}
|
||||||
{view.stack.waitingOn === view.you ? "They counter your spell — counter back, or" : ""}
|
{view.stack.waitingOn === view.you ? "They counter your spell — counter back, or" : ""}
|
||||||
@@ -2609,6 +2623,11 @@
|
|||||||
<div class="cast-panel-head">⚔ incoming</div>
|
<div class="cast-panel-head">⚔ incoming</div>
|
||||||
{#if view.stack.attackCard}
|
{#if view.stack.attackCard}
|
||||||
<div class="cast-panel-card"><Card card={view.stack.attackCard} onfaq={(id) => (faqCardId = id)} /></div>
|
<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>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="cast-panel-note">a physical blow — no card behind it</div>
|
<div class="cast-panel-note">a physical blow — no card behind it</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
Reference in New Issue
Block a user