Reflected permanent curses return to their caster (rules rev 11)

SLOW DEATH, WALKING DEAD, and IDIOT ride no damage or duration pipe, so
a FULL REFLECTION used to evaporate them; per the FAQ reflections act on
the cast, so the returned curse now opens its stack against the caster
like any reflected spell. Marked permanentCurse on the three defs; older
ledgers keep the evaporation.

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-08-29 22:23:53 -04:00
co-authored by Claude Fable 5
parent e885325098
commit dd6ba59220
2 changed files with 34 additions and 3 deletions
+14 -3
View File
@@ -271,7 +271,9 @@ export interface GameConfig {
* corridors on the turn's legal legs (three plus numbers) and spends
* them.
* Rev 11: a REVERSE against SLOW DEATH turns the whole curse (FAQ)
* the victim gains a point for every card drawn, permanently.
* the victim gains a point for every card drawn, permanently and a
* FULL REFLECTION returns a permanent curse (SLOW DEATH, WALKING DEAD,
* IDIOT) onto its caster instead of letting it evaporate.
*/
deckRev?: number;
}
@@ -826,6 +828,10 @@ type AttackEffect = {
baseDamage: (numberValue: number | null, params: CastParams | null) => number;
/** A duration spell: attach a sustained effect on resolution. */
sustains?: boolean;
/** A permanent curse attached by onResolved (SLOW DEATH, WALKING DEAD,
* IDIOT): it rides no damage or duration pipe, so a FULL REFLECTION
* must return it explicitly or the curse would evaporate. */
permanentCurse?: true;
/** Card stays in hand and is displayed rather than discarded (WIZARDBLADE). */
keepInHand?: boolean;
validate?: (state: GameState, cmd: Extract<Command, { type: "cast" }>, caster?: PlayerState, target?: PlayerState) => string | null;
@@ -1720,6 +1726,7 @@ const CARD_EFFECTS: Record<string, AttackEffect | NeutralEffect | CounterEffect>
kind: "attack",
requiresLos: true,
baseDamage: () => 0,
permanentCurse: true,
// "This is permanent. Once SLOW DEATH is on, it can't be turned off."
// Rev 11, per the FAQ: "If SLOW DEATH is REVERSED, the player receives
// a point for every card drawn" — the whole curse attaches backward,
@@ -2210,6 +2217,7 @@ const CARD_EFFECTS: Record<string, AttackEffect | NeutralEffect | CounterEffect>
kind: "attack",
requiresLos: true,
baseDamage: () => 0,
permanentCurse: true,
// "1/2 point of damage for every space moved. This spell is permanent."
onResolved: (ctx) => {
if (ctx.fullyStopped || !ctx.defender.alive) return;
@@ -2554,6 +2562,7 @@ const CARD_EFFECTS: Record<string, AttackEffect | NeutralEffect | CounterEffect>
kind: "attack",
requiresLos: true,
baseDamage: () => 0,
permanentCurse: true,
// "Opponent heads straight for the nearest of his own treasures ... This
// lasts until opponent is on his own treasure."
sustains: false,
@@ -6043,9 +6052,11 @@ function resolveStack(state: GameState, events: GameEvent[]): void {
return;
}
// The returned spell is a fresh attack on its own caster — who gets a
// defender's counteraction window against it.
// defender's counteraction window against it. Rev 11: permanent curses
// ride the return too; older games let a reflected curse evaporate.
const returnsCurse = effect?.permanentCurse === true && (state.config.deckRev ?? 1) >= 11;
if (!stack.trapped && !attackingCreature &&
attacker.alive && (pipe.damage > 0 || pipe.duration > 0)) {
attacker.alive && (pipe.damage > 0 || pipe.duration > 0 || returnsCurse)) {
state.stack = {
attackerId: defender.id,
defenderId: attacker.id,