Slow Death's draw is one blow, countered against its total (table ruling)

Two cards drawn is a two-point blow: ABSORB soaks up to three of the
total, BLUNT halves it rounding up (so it finally earns a place — 2
becomes 1), counters may chain, declining takes the rest. The client
labels each counter with the exact points it leaves; the bots weigh
the total against their floor. Amends rev 13 minutes after it shipped;
every production ledger still replays clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
This commit is contained in:
Eric Wagoner
2026-09-01 10:41:47 -04:00
co-authored by Claude Fable 5
parent 92613d7386
commit 8cfd911a3e
5 changed files with 91 additions and 51 deletions
+15 -9
View File
@@ -1563,23 +1563,29 @@
{/if}
{#if view?.slowDeathPending && view.slowDeathPending.playerId === view.you}
{@const sdAbsorbs = view.yourHand.filter((c) => c.cardId === "absorb")}
{@const sdPoints = view.slowDeathPending.points}
{@const sdAbsorb = view.yourHand.find((c) => c.cardId === "absorb")}
{@const sdBlunt = view.yourHand.find((c) => c.cardId === "blunt")}
<div class="scrim attack-scrim" role="alertdialog" aria-label="slow death bites">
<div class="attack-notice">
<div class="attack-title">Slow Death bites for {view.slowDeathPending.points}!</div>
<div class="attack-title">Slow Death bites for {sdPoints}!</div>
<div class="attack-power">
"ABSORB against SLOW DEATH will only stop the damage incurred by
one card" — each Absorb you spend soaks exactly one point.
The draw's bites land as one blow — counter the total: Absorb
soaks up to three points, Blunt halves it (fractions round up).
</div>
<div class="attack-actions">
{#if sdAbsorbs.length > 0}
{@const firstAbsorb = sdAbsorbs[0]!}
{#if sdAbsorb}
<button class="stamp primary"
onclick={() => dispatch({ type: "slowDeathChoice", absorbInstanceId: firstAbsorb.instanceId })}>
Absorb a point ({sdAbsorbs.length} in hand)</button>
onclick={() => dispatch({ type: "slowDeathChoice", counterInstanceId: sdAbsorb.instanceId })}>
Absorb — take {Math.max(0, sdPoints - 3)} instead</button>
{/if}
{#if sdBlunt}
<button class="stamp" disabled={Math.ceil(sdPoints / 2) >= sdPoints}
onclick={() => dispatch({ type: "slowDeathChoice", counterInstanceId: sdBlunt.instanceId })}>
Blunt — take {Math.ceil(sdPoints / 2)} instead</button>
{/if}
<button class="hint-cancel" onclick={() => dispatch({ type: "slowDeathChoice" })}>
take {view.slowDeathPending.points === 1 ? "the point" : `all ${view.slowDeathPending.points}`}</button>
take {sdPoints === 1 ? "the point" : `all ${sdPoints}`}</button>
</div>
</div>
</div>
+2 -2
View File
@@ -82,8 +82,8 @@ export function humanize(e: GameEvent): string | null {
case "safeOpened": return `${e.player}'s ${cardDef(e.withCardId).name} clicks the safe open — until turn's end.`;
case "safeDamaged": return `${e.attacker} batters the safe — ${e.amount} damage (${e.total}/15).`;
case "safeSmashed": return `💥 The safe BURSTS open under ${e.attacker}'s assault!`;
case "slowDeathWindow": return `Slow Death bites ${e.player} for ${e.points} — an Absorb hovers over the wound…`;
case "slowDeathAbsorbed": return `${e.player}'s Absorb soaks one point of the rot (${e.remaining} to go).`;
case "slowDeathWindow": return `Slow Death bites ${e.player} for ${e.points} — a counter hovers over the wound…`;
case "slowDeathCountered": return `${e.player}'s ${cardDef(e.cardId).name} blunts the rot ${e.remaining} point${e.remaining === 1 ? "" : "s"} still coming.`;
case "spellSustained": return `${spellName(e.cardId)} settles over ${e.target} (${e.turns} turn${e.turns === 1 ? "" : "s"}).`;
case "spellExpired": return `${spellName(e.cardId)} wears off ${e.target}.`;
case "teleported": return e.by === e.player