Attacks aimed at a bush or the ooze: five points clear the square

A THORNBUSH or ROSEBUSH "will be destroyed" by five points of damage,
and the FAQ lets only fire hurt a KILLER OOZE; the table refused every
attack aimed at one. An attack tapped onto a bush or ooze now lands on
it, the damage accumulating across attackers and turns, and the
fifth point clears the square. A WIZARDBLADE is swung from the square
beside it, as its text asks of a target that fills a square.

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-16 22:35:01 -04:00
co-authored by Claude Fable 5.1
parent 23b4a7675e
commit bc99ee3eb7
4 changed files with 130 additions and 3 deletions
@@ -705,3 +705,68 @@ describe("spells cast at a slime wait in the gel", () => {
expect(hit.slimeTraps[cellKey(spot.cell)]).toBeUndefined();
});
});
describe("attacks aimed at a bush or the ooze", () => {
function withContent(kind: "thornbush" | "rosebush" | "ooze") {
const state = toRound2(newGame().state);
const me = activePlayer(state);
const { cell } = emptyNeighborCell(state, me.position);
state.squareContents[cellKey(cell)] = { kind, damage: 0, createdBy: "bob" };
return { state, me, cell };
}
it("a FIREBALL tears a thornbush apart at five points", () => {
const { state, me, cell } = withContent("thornbush");
const fireball = giveCard(state, me.id, "fireball");
const r = applyCommand(state, me.id, { type: "cast", instanceId: fireball.instanceId, target: { kind: "cell", cell } });
expect(r.ok).toBe(true);
if (!r.ok) return;
expect(r.events).toContainEqual(expect.objectContaining({ type: "squareContentDamaged", kind: "thornbush", amount: 5, total: 5 }));
expect(r.events).toContainEqual(expect.objectContaining({ type: "squareContentDestroyed", kind: "thornbush" }));
expect(r.state.squareContents[cellKey(cell)]).toBeUndefined();
expect(r.state.turn.attackUsed).toBe(true);
});
it("a thrown dagger scratches a rosebush, and the scratch stays", () => {
const { state, me, cell } = withContent("rosebush");
const dagger = giveCard(state, me.id, "dagger");
const next = must(state, me.id, { type: "cast", instanceId: dagger.instanceId, target: { kind: "cell", cell } });
expect(next.squareContents[cellKey(cell)]).toEqual(expect.objectContaining({ kind: "rosebush", damage: 3 }));
});
it("only fire hurts the ooze", () => {
const { state, me, cell } = withContent("ooze");
const dagger = giveCard(state, me.id, "dagger");
const refused = applyCommand(state, me.id, { type: "cast", instanceId: dagger.instanceId, target: { kind: "cell", cell } });
expect(refused.ok).toBe(false);
if (!refused.ok) expect(refused.error).toMatch(/only fire/);
const fireball = giveCard(state, me.id, "fireball");
const burned = must(state, me.id, { type: "cast", instanceId: fireball.instanceId, target: { kind: "cell", cell } });
expect(burned.squareContents[cellKey(cell)]).toBeUndefined();
});
it("a WIZARDBLADE is swung at a bush from the square beside it, never from afar", () => {
const { state, me, cell } = withContent("thornbush");
const blade = giveCard(state, me.id, "wizardblade");
const three = giveCard(state, me.id, "number-3", "N", 1);
const near = applyCommand(state, me.id, {
type: "cast", instanceId: blade.instanceId, numberInstanceIds: [three.instanceId], target: { kind: "cell", cell },
});
expect(near.ok).toBe(true);
if (near.ok) expect(near.state.squareContents[cellKey(cell)]).toEqual(expect.objectContaining({ kind: "thornbush", damage: 3 }));
const far = sightedCellsFor(viewFor(state, me.id));
const farKey = [...far].find((k) => {
const [x, y] = k.split(",").map(Number);
return Math.abs(x! - me.position.x) + Math.abs(y! - me.position.y) >= 2 && !state.squareContents[k];
});
if (!farKey) return;
const [fx, fy] = farKey.split(",").map(Number);
state.squareContents[farKey] = { kind: "thornbush", damage: 0, createdBy: "bob" };
const refused = applyCommand(state, me.id, {
type: "cast", instanceId: blade.instanceId, numberInstanceIds: [three.instanceId], target: { kind: "cell", cell: { x: fx!, y: fy! } },
});
expect(refused.ok).toBe(false);
if (!refused.ok) expect(refused.error).toMatch(/beside/);
});
});