The great simplification: every vintage gate collapses, the count restarts at 1
All 50 rooms were retired (backed up on the droplet and in Spaces), so no ledger needs an old branch to replay: forty revisions of deckRev gates fold into one canonical ruleset — the newest behavior everywhere. The ward's arming, the idiot's steering, the lazy illusion roll, the legacy redirection swap, and the flat wave all leave with their gates; RULES_REV restarts at 1 for whenever the rules fork again. Old-vintage test pins go with them. The opening screen now quietly drops seats whose rooms the server no longer knows, instead of listing them "unreachable" forever. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
e8df6efcde
commit
8a50ff7f36
@@ -143,13 +143,16 @@ describe("illusion wall", () => {
|
||||
expect(after.ok).toBe(true);
|
||||
});
|
||||
|
||||
it("opponents test the illusion when they bump it — some see through, some believe", () => {
|
||||
it("an untested illusion blocks the bump; a tested eye rolls its verdict", () => {
|
||||
let believed = 0, sawThrough = 0;
|
||||
for (let seed = 1; seed <= 12; seed++) {
|
||||
const { state, caster, side } = setupIllusion(seed);
|
||||
const other = state.players.find((p) => p.id !== caster)!;
|
||||
other.position = { ...state.players.find((p) => p.id === caster)!.position };
|
||||
let s = must(state, caster, { type: "endTurn", draw: 0 });
|
||||
// Bumping blind is refused: eyes must be tested first.
|
||||
expect(applyCommand(s, other.id, { type: "move", direction: side }).ok).toBe(false);
|
||||
s = must(s, other.id, { type: "testIllusion", cell: other.position, side });
|
||||
const result = applyCommand(s, other.id, { type: "move", direction: side });
|
||||
if (result.ok) sawThrough++;
|
||||
else believed++;
|
||||
@@ -223,11 +226,16 @@ describe("sector manipulation", () => {
|
||||
type: "cast", instanceId: rel.instanceId,
|
||||
target: { kind: "cell", cell: dest }, params: { cell: me.position },
|
||||
});
|
||||
// The maze zero-anchors after the move; measure against the sector's
|
||||
// origin as it settled, not the requested landing.
|
||||
const after = state.players.find((p) => p.id === me.id)!;
|
||||
const dx = dest.x - myOrigin.x, dy = dest.y - myOrigin.y;
|
||||
const newOrigin = state.board.placements[idx]!.origin;
|
||||
const dx = newOrigin.x - myOrigin.x, dy = newOrigin.y - myOrigin.y;
|
||||
expect(after.position).toEqual({ x: posBefore.x + dx, y: posBefore.y + dy });
|
||||
expect(after.home).toEqual({ x: homeBefore.x + dx, y: homeBefore.y + dy });
|
||||
expect(state.board.placements[idx]!.origin).toEqual(dest);
|
||||
// My sector sits east of the other, as asked.
|
||||
const otherAfter = state.board.placements[idx === 0 ? 1 : 0]!.origin;
|
||||
expect(newOrigin).toEqual({ x: otherAfter.x + 5, y: otherAfter.y });
|
||||
// The map reassembled: every treasure/wizard cell exists on the new board.
|
||||
for (const t of state.treasures) {
|
||||
if (t.position) expect(state.board.cells[cellKey(t.position)]).toBe(true);
|
||||
@@ -319,8 +327,8 @@ describe("6e card-face corrections", () => {
|
||||
params: { damage: 4, knockback: 0 },
|
||||
});
|
||||
state = must(state, defender.id, { type: "counteract", instanceId: "wall-of-fire#WOF" });
|
||||
// The fire is a total stop: the attacker's pass settles it.
|
||||
state = must(state, attacker.id, { type: "pass" });
|
||||
state = must(state, defender.id, { type: "pass" });
|
||||
expect(state.players.find((p) => p.id === defender.id)!.life).toBe(15);
|
||||
|
||||
// But it cannot counter a fireball.
|
||||
@@ -337,7 +345,7 @@ describe("6e card-face corrections", () => {
|
||||
|
||||
describe("relocation past the origin (rules rev 11)", () => {
|
||||
function rev11Game() {
|
||||
const { state } = createGame({ playerIds: ["alice", "bob"], seed: 42, sets: ["basic"], deckRev: 11 });
|
||||
const { state } = createGame({ playerIds: ["alice", "bob"], seed: 42, sets: ["basic"] });
|
||||
return state;
|
||||
}
|
||||
|
||||
@@ -438,7 +446,7 @@ describe("relocation past the origin (rules rev 11)", () => {
|
||||
|
||||
describe("junction alterations roll for their sector (rules rev 20)", () => {
|
||||
it("a conjured wall on the seam obeys the die: 1-2 stays, 3-4 travels", () => {
|
||||
const { state: fresh } = createGame({ playerIds: ["a", "b"], seed: 42, sets: ["basic"], deckRev: 20 });
|
||||
const { state: fresh } = createGame({ playerIds: ["a", "b"], seed: 42, sets: ["basic"] });
|
||||
let state = toRound2(fresh);
|
||||
// The 2p board is two sectors stacked: the seam runs between them.
|
||||
const [p0, p1] = state.board.placements;
|
||||
@@ -503,7 +511,7 @@ describe("sight tracing", () => {
|
||||
|
||||
describe("illusions are tested by choice (rules rev 29)", () => {
|
||||
function shimmerRig() {
|
||||
let { state } = createGame({ playerIds: ["a", "b"], seed: 42, sets: ["basic"], deckRev: 29 });
|
||||
let { state } = createGame({ playerIds: ["a", "b"], seed: 42, sets: ["basic"] });
|
||||
state = toRound2(state);
|
||||
const caster = activePlayer(state);
|
||||
const mark = state.players.find((p) => p.id !== caster.id)!;
|
||||
|
||||
Reference in New Issue
Block a user