diff --git a/.claude/skills/wizwar-duel/SKILL.md b/.claude/skills/wizwar-duel/SKILL.md index a19e80e..4808f6d 100644 --- a/.claude/skills/wizwar-duel/SKILL.md +++ b/.claude/skills/wizwar-duel/SKILL.md @@ -225,6 +225,21 @@ probing legality is free, so when unsure, try it and read the error. expect neither bias nor exploitability from the bots — their BFS is natively non-Euclidean. +## Game-thirteen scars (the goat) + +- **BUTT-HEAD is a cross-map physical nuke**: damage = the full walked + distance of the charge, and it found 8 points across half a maze. + ABSORB SPELL answers spells ONLY — at low life with no teleport in + hand, there is NO answer to big physical; your only defenses are + position and never being worth the charge. Audit the hand for + physical answers separately from spell answers. +- He parks his OWN chest on open floor when he needs free arms — + custody is a dial, not a state. Watch every drop: grounded chests + are targets again the moment they land. +- My game-one wall and his one dust cloud shaped the ENTIRE game: + single-square terrain on a junction outlives every spell cast near + it. Place terrain at junctions, not corridors. + ## The board is the truth (I keep paying for this one) - NEVER narrate a persistent effect without re-verifying it in the diff --git a/packages/engine/src/game.ts b/packages/engine/src/game.ts index 06c5f75..aafa082 100644 --- a/packages/engine/src/game.ts +++ b/packages/engine/src/game.ts @@ -238,8 +238,10 @@ export interface CastParams { * Rev 7: DISEASE is a self-cast plague ("You're the carrier!") — the caster * carries it, sharing a square bites both directions, no counteraction. * Rev 8: the fire imp's scorch is a SPELL (FAQ) — counteractable, magical — - * and SOULSTONE's floor holds even at 3 life or below. */ -export const CURRENT_RULES_REV = 8; + * and SOULSTONE's floor holds even at 3 life or below. + * Rev 9: the WARD's bite is counteractable ("COUNTERACTIONs ... otherwise + * work as written"), though nothing a counter does touches its caster. */ +export const CURRENT_RULES_REV = 9; export interface GameConfig { playerIds: PlayerId[]; @@ -4567,8 +4569,29 @@ function doWardChoice(prev: GameState, play: boolean): CommandResult { owner.displayed = owner.displayed.filter((id) => id !== card!.instanceId); state.discard.push(card!); events.push({ type: "wardSprung", owner: owner.id, victim: taker.id }); - applyDamage(state, events, taker, 3, "warded treasure", null); - checkVictory(state, events); + // Rev 9: "COUNTERACTIONs against WARD ... otherwise work as written" — + // the bite rides the stack. `trapped` carries the card's other clause: + // nothing a counter does can touch the Ward's caster. Older games + // recorded the instant bite and replay it. + if ((state.config.deckRev ?? 1) >= 9) { + state.stack = { + attackerId: owner.id, + defenderId: taker.id, + attackCard: null, + numberValue: null, + amplifyFactor: 1, + extendFactor: 1, + powerAttackPoints: 0, + params: { damage: 3 }, + kind: "spell", + counters: [], + waitingOn: taker.id, + trapped: true, + }; + } else { + applyDamage(state, events, taker, 3, "warded treasure", null); + checkVictory(state, events); + } } return { ok: true, state, events }; } @@ -5839,7 +5862,7 @@ function resolveStack(state: GameState, events: GameEvent[]): void { ? (stack.params?.damage ?? 1) // a creature's blow : stack.tearTreasure ? 0 // the tear wrestles; it does not bruise - : 1; // a punch + : (stack.params?.damage ?? 1); // a punch — or a cardless trap's bite // Webs burn: "Any fire damage done to player in webs causes two extra points." if (attackId === "fireball" && sustainedOn(state, defender.id, "sticky-web").length > 0) { base += 2;