The whole punch list: rev 8, general FAQ, and four promoted UX items
Rules rev 8 closes the last two fidelity threads. A SPEED bonus turn burns a turn of duration spells on the hastened wizard — recipient- counted, per the FAQ's most obscure ruling — with self-cast durations already burning through the normal turn-start sweep (expiry extracted into expireEffect so both paths share the cleanup). And nothing can be created on a DIMENSIONAL WARP token, as the card face always said. The general-topic FAQ sections (Combat, Line of Sight, Monsters, Treasures, and five more) join the rules tab verbatim under a double- ruled divider, completing the FAQ's absorption: card rulings on the cards, general rulings in the rules. The four promoted UX items: cell-target spells now dim ineligible squares (creations mirror emptySquareTarget from the viewer's knowledge, summons want clear sighted squares, teleport BFSes its four spaces exactly as the engine does, stone-to-water lights only stone, dispel lights only creations); hotseat games get the full replay reel, each step seen from its actor's own seat; a "stop announcing attacks on this device" link in the fanfare modal (undo lives beside the notification toggle in the lobby); the corner inspector now serves only the selected casting card, with every examine-a-card peek unified on the centered treatment; and the dev socket URL keys on vite's port instead of hijacking every localhost. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
f657144127
commit
d6d34887e3
@@ -734,3 +734,38 @@ describe("answering counteractions (FAQ rulings)", () => {
|
||||
expect(cellKey(after.position)).toBe(cellKey(escape));
|
||||
});
|
||||
});
|
||||
|
||||
describe("rules revision 8", () => {
|
||||
it("a SPEED bonus turn burns a turn of durations on the hastened wizard", () => {
|
||||
let { state } = createGame({ playerIds: ["alice", "bob"], seed: 42, sets: ["basic"], deckRev: 8 });
|
||||
state = toRound2(state);
|
||||
const me = activePlayer(state);
|
||||
const other = state.players.find((p) => p.id !== me.id)!;
|
||||
state.sustained.push({
|
||||
id: "fx-blind", cardId: "blind", casterId: other.id, targetId: me.id,
|
||||
remainingTurns: 2, data: {},
|
||||
});
|
||||
giveCard(state, me.id, "speed", "SP", 0);
|
||||
state = must(state, me.id, { type: "cast", instanceId: "speed#SP" });
|
||||
state = must(state, me.id, { type: "endTurn", draw: 0 });
|
||||
// The bonus turn began: one duration turn burned, recipient-counted.
|
||||
expect(activePlayer(state).id).toBe(me.id);
|
||||
expect(state.sustained.find((s) => s.id === "fx-blind")!.remainingTurns).toBe(1);
|
||||
state = must(state, me.id, { type: "endTurn", draw: 0 });
|
||||
expect(activePlayer(state).id).toBe(other.id);
|
||||
});
|
||||
|
||||
it("nothing can be created on a dimensional warp token", () => {
|
||||
let { state } = createGame({ playerIds: ["alice", "bob"], seed: 42, sets: ["basic", "expansion1"], deckRev: 8 });
|
||||
state = toRound2(state);
|
||||
const me = activePlayer(state);
|
||||
const spot = { x: me.position.x + 1, y: me.position.y };
|
||||
state.dimWarps.push({ a: spot, b: { x: 0, y: 0 } });
|
||||
const tb = giveCard(state, me.id, "thornbush", "T", 0);
|
||||
const r = applyCommand(state, me.id, {
|
||||
type: "cast", instanceId: tb.instanceId, target: { kind: "cell", cell: spot },
|
||||
});
|
||||
expect(r.ok).toBe(false);
|
||||
if (!r.ok) expect(r.error).toMatch(/warp shimmers/);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user