Rev 26: BUTT-HEAD's ram is priced into the blow, so STRENGTH, AMPLIFY, and the counters weigh it (KWUZ)

Ramses cast STRENGTH, then an amplified BUTT-HEAD three squares off, and
the goat dealt three: the ram was applied in onResolved, past the pipe
that doubles, soaks, and adds. Older games keep the bare distance.

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-23 08:16:30 -04:00
co-authored by Claude Fable 5.1
parent 5193b12b8e
commit 31bfcf7bc0
2 changed files with 61 additions and 2 deletions
+17 -2
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 = 25;
export const CURRENT_RULES_REV = 26;
/** 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
@@ -277,6 +277,7 @@ export const RULES_REVISIONS: { rev: number; note: string }[] = [
{ rev: 23, note: "POWER DRAIN drains the number played: the caster gains it whether the blow is BLUNTed or ABSORBed (FAQ: the counter blunts the damage done, not the drain), and a wall drained for its points gives them up too. Older games gave the caster only what the opponent lost, and nothing from a wall." },
{ 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." },
];
export interface GameConfig {
@@ -2452,7 +2453,10 @@ const CARD_EFFECTS: Record<string, AttackEffect | NeutralEffect | CounterEffect>
ctx.state.turn.movementUsed += d;
ctx.attacker.position = { ...ctx.defender.position };
ctx.events.push({ type: "rammed", attacker: ctx.attacker.id, target: ctx.defender.id, distance: d });
resolvedBlow(ctx, d, "goat ram", "physical");
// Rev 26: the ram was priced into the blow itself (ramDistance),
// so STRENGTH, AMPLIFY, and the counters have already weighed it.
// Older games dealt the bare distance here, past all of them.
if ((ctx.state.config.deckRev ?? 1) < 26) resolvedBlow(ctx, d, "goat ram", "physical");
return;
}
const d = Math.abs(ctx.defender.position.x - ctx.attacker.position.x) +
@@ -6035,6 +6039,14 @@ function checkAmbushes(
/** Damage dealt inside an attack's own resolution hook still honors
* EMPATHY — "Any attack done in ANY form against you acts against both you
* and the caster". */
/** How far the goat charges: the corridor distance to its mark within
* this turn's legs, or zero when the mark is underfoot or out of reach. */
function ramDistance(state: GameState, attacker: PlayerState, defender: PlayerState): number {
const legs = state.turn.movementAllowance - state.turn.movementUsed;
const d = walkingDistance(state, attacker.position, defender.position, chargeReach(state, legs));
return d > legs ? 0 : d;
}
function resolvedBlow(
ctx: ResolutionContext, dmg: number, source: string, kind?: "physical",
): void {
@@ -6371,6 +6383,9 @@ function resolveStack(state: GameState, events: GameEvent[]): void {
isMagicStone(c.cardId) && defender.displayed.includes(c.instanceId));
base = (stack.numberValue ?? 1) * inPlay.length;
}
// 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);
base *= stack.amplifyFactor;
base += stack.powerAttackPoints;
// SWARTHMORE'S ENCHANTMENT: an enchanted thrown object bites one deeper.