Rev 27: an ILLUSIONARY ATTACK is priced as the spell it fakes, so BLUNT and the rest meet it (UPJE)

The believed illusion was dealt after the counter trail had already
spoken over a zero, so a BLUNT trailed for nothing and the full points
landed. Now the fake carries its spell's points through the pipeline and
the belief roll decides whether the weighed blow lands. Games dealt under
rev 26 and before replay as they were.

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 20:40:47 -04:00
co-authored by Claude Fable 5.1
parent 991a6a0443
commit c3f63d4eb7
2 changed files with 80 additions and 1 deletions
+28 -1
View File
@@ -247,7 +247,7 @@ export interface CastParams {
}
/** The revision new games are dealt under; GameConfig.deckRev pins it per game. */
export const CURRENT_RULES_REV = 26;
export const CURRENT_RULES_REV = 27;
/** Every rulings revision since the baseline, newest last — the entries a
* game's deckRev freezes it before or after. Shown to players as the house
@@ -278,6 +278,7 @@ export const RULES_REVISIONS: { rev: number; note: string }[] = [
{ rev: 24, note: "A REFLECTION against a permanent curse afflicts both wizards: WALKING DEAD and IDIOT say so on the card, and SLOW DEATH follows REFLECTION's own rule that a spell works for both parties. Older games left the caster untouched." },
{ rev: 25, note: "An attack on a bush or the ooze is an attack on an inanimate object like any wall: POWER ATTACK's bought points count, and a thrown DAGGER or LARGE ROCK lands at the foot of the bush instead of vanishing into the discard. Older games took neither." },
{ rev: 26, note: "BUTT-HEAD's ram is physical damage like any other: STRENGTH doubles it, AMPLIFY doubles it, WEAKNESS doubles it, POWER ATTACK adds to it, and ABSORB or BLUNT soak it. Older games dealt the bare distance, whatever rode the cast." },
{ rev: 27, note: "An ILLUSIONARY ATTACK is priced as the spell it fakes, so a BLUNT halves it, an ABSORB soaks it, and a shield stops it, exactly as against the real thing; the belief roll then decides whether the weighed blow lands at all. Older games dealt a believed illusion's full points past every counteraction." },
];
export interface GameConfig {
@@ -2542,6 +2543,8 @@ const CARD_EFFECTS: Record<string, AttackEffect | NeutralEffect | CounterEffect>
return null;
},
onResolved: (ctx) => {
// Rev 27 prices the illusion into the pipeline and rolls belief there.
if ((ctx.state.config.deckRev ?? 1) >= 27) return;
if (ctx.fullyStopped || !ctx.defender.alive) return;
const chosen = ctx.stack.params!.cardId!;
const fx = CARD_EFFECTS[chosen] as AttackEffect;
@@ -6386,6 +6389,13 @@ function resolveStack(state: GameState, events: GameEvent[]): void {
// Rev 26: BUTT-HEAD's ram is priced here, as the physical blow it is,
// so everything below weighs it like any other.
if (attackId === "butt-head" && (state.config.deckRev ?? 1) >= 26) base = ramDistance(state, attacker, defender);
// Rev 27: an ILLUSIONARY ATTACK is priced as the spell it fakes, so the
// trail weighs it like the real thing. Whether the weighed blow lands at
// all is the belief roll's, taken below once the trail has spoken.
if (attackId === "illusionary-attack" && (state.config.deckRev ?? 1) >= 27 && stack.params?.cardId) {
const faked = CARD_EFFECTS[stack.params.cardId];
if (faked?.kind === "attack") base = faked.baseDamage(stack.numberValue, stack.params);
}
base *= stack.amplifyFactor;
base += stack.powerAttackPoints;
// SWARTHMORE'S ENCHANTMENT: an enchanted thrown object bites one deeper.
@@ -6482,6 +6492,23 @@ function resolveStack(state: GameState, events: GameEvent[]): void {
}
weigh();
// Rev 27: "Your opponent has a 50% chance of believing the spell to be
// real. If so, he takes damage from the spell" — the weighed damage, the
// counteractions having met the illusion as they would the real spell.
// Disbelief leaves nothing to land, and a total stop leaves nothing to
// believe in, so no die is rolled then.
if (attackId === "illusionary-attack" && (state.config.deckRev ?? 1) >= 27 && !stack.reflectedBase &&
(pipe.damage > 0 || pipe.reflectedDamage > 0 || pipe.duration > 0)) {
const [roll, rngNext] = rollDie(state.rng);
state.rng = rngNext;
const believed = roll <= 2;
events.push({ type: "illusionBelieved", player: defender.id, cardId: stack.params!.cardId!, believed });
if (!believed) {
pipe.damage = 0;
pipe.reflectedDamage = 0;
pipe.duration = 0;
}
}
// A surviving teleport counter whisks the defender away before anything lands.
const escape = stack.counters.find((c) => !c.nullified && c.card.cardId === "teleport" && c.cell);