Stone Dead counts only the stones in play (rules rev 34)
Same reading as fireball's rev 33: 'carrying' means displayed. Counting hidden hand stones both inflated the damage and broadcast the secret count in the total. The automatons' estimate (displayed as the visible floor) is now simply the truth. All production ledgers verified clean before deploy. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0138A8CjeQRpvzKxuMfz1Bqc
This commit is contained in:
co-authored by
Claude Fable 5
parent
b67f2a4c8a
commit
e8c25ddc19
@@ -5402,9 +5402,15 @@ function resolveStack(state: GameState, events: GameEvent[]): void {
|
||||
if (attackId === "fireball" && sustainedOn(state, defender.id, "sticky-web").length > 0) {
|
||||
base += 2;
|
||||
}
|
||||
// STONE DEAD: number x the stones the defender carries.
|
||||
// STONE DEAD: number x the stones the defender carries — and "carrying"
|
||||
// means displayed, as with fireball's burning (rules rev 34): a hidden
|
||||
// stone is a secret card, and counting it would broadcast the secret in
|
||||
// the damage total. Earlier games counted the whole hand and replay so.
|
||||
if (attackId === "stone-dead") {
|
||||
base = (stack.numberValue ?? 1) * defender.hand.filter((c) => isMagicStone(c.cardId)).length;
|
||||
const inPlay = (state.config.deckRev ?? 1) >= 34
|
||||
? defender.hand.filter((c) => isMagicStone(c.cardId) && defender.displayed.includes(c.instanceId))
|
||||
: defender.hand.filter((c) => isMagicStone(c.cardId));
|
||||
base = (stack.numberValue ?? 1) * inPlay.length;
|
||||
}
|
||||
base *= stack.amplifyFactor;
|
||||
base += stack.powerAttackPoints;
|
||||
|
||||
@@ -1083,3 +1083,26 @@ describe("fireball burns only the stones in play (rules rev 33)", () => {
|
||||
expect(after.hand.some((c) => c.instanceId === "speedstone#T")).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("stone dead counts only the stones in play (rules rev 34)", () => {
|
||||
it("hidden stones neither add damage nor betray their count", () => {
|
||||
let { state } = createGame({ playerIds: ["a", "b"], seed: 42, sets: ["basic", "expansion1"], deckRev: 34 });
|
||||
state = toRound2(state);
|
||||
const { attacker, defender } = faceOff(state);
|
||||
const sd = giveCard(state, attacker, "stone-dead");
|
||||
giveCard(state, attacker, "number-3", "N", 1);
|
||||
const d = state.players.find((p) => p.id === defender)!;
|
||||
d.hand[0] = { instanceId: "powerstone#T", cardId: "powerstone" };
|
||||
d.hand[1] = { instanceId: "speedstone#T", cardId: "speedstone" };
|
||||
d.hand[2] = { instanceId: "bloodstone#T", cardId: "bloodstone" };
|
||||
d.displayed = ["powerstone#T"]; // one on the table, two secret
|
||||
state = must(state, attacker, {
|
||||
type: "cast", instanceId: sd.instanceId, numberInstanceIds: ["number-3#N"],
|
||||
target: { kind: "player", playerId: defender },
|
||||
});
|
||||
state = must(state, defender, { type: "pass" });
|
||||
// 3 x 1 displayed = 3 damage... minus the displayed bloodstone? It is
|
||||
// hidden, so no soak either: 15 - 3 = 12.
|
||||
expect(state.players.find((p) => p.id === defender)!.life).toBe(12);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -54,7 +54,7 @@ export interface Room {
|
||||
const rooms = new Map<string, Room>();
|
||||
|
||||
/** Rules revision new games are dealt under (stored games keep their own). */
|
||||
const RULES_REV = 33;
|
||||
const RULES_REV = 34;
|
||||
|
||||
const ROOM_CODE_ALPHABET = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
|
||||
|
||||
|
||||
@@ -136,7 +136,7 @@ class LocalGame {
|
||||
seed,
|
||||
sets: expansion ? ["basic", "expansion1"] : ["basic"],
|
||||
...(colors ? { colors } : {}),
|
||||
deckRev: 33,
|
||||
deckRev: 34,
|
||||
};
|
||||
const { state, events } = createGame(config);
|
||||
for (const e of events) {
|
||||
|
||||
Reference in New Issue
Block a user