Rev 15: the held doorway carries sight — the whole point of holding it

The table holds doors open to cast back through them at pursuers;
our held door opened for feet but not for eyes. From rev 15 a held
door is an open doorway to sight — casting at players, creatures,
and squares sees through it, the client's dimming aid and the
automatons alike (they share sightedCellsFor). REMOVE LOCK's "still
considered to block L.O.S." stands: that speaks of a CLOSED door,
and an unheld unlocked door still blocks. Rev-gated because rev-14
ledgers may already contain holds and must replay blind.

Pinned three ways: the holder blasts a pursuer through the held
doorway; an unheld unlocked door still blocks; rev 14 stays blind.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Eric Wagoner
2026-08-17 13:20:45 -04:00
co-authored by Claude Fable 5
parent a0c65413ef
commit adf463effa
5 changed files with 88 additions and 6 deletions
+12 -2
View File
@@ -326,9 +326,19 @@ export function losBlockers(state: GameState): Record<string, true> {
return blockers;
}
/** A held-open door is an open doorway to the eye (rules rev 15): REMOVE
* LOCK's "still considered to block L.O.S." speaks of a CLOSED door, and
* the table holds doors open precisely to cast back through them. */
function openHeldDoors(state: GameState, board: AssembledBoard): AssembledBoard {
if ((state.config.deckRev ?? 1) < 15 || state.heldDoors.length === 0) return board;
const edges = { ...board.edges };
for (const h of state.heldDoors) delete edges[h.key];
return { ...board, edges };
}
/** LOS including square-filling blockers. */
export function gameLos(state: GameState, from: Cell, to: Cell): boolean {
return sightBetween(boardView(state), from, to, losBlockers(state));
return sightBetween(openHeldDoors(state, boardView(state)), from, to, losBlockers(state));
}
/** Parse an edge key back into its north/west cell and side. */
@@ -404,7 +414,7 @@ function casterLos(
to: Cell,
events: GameEvent[] = [],
): boolean {
const board = perceivedBoard(state, events, caster.id, { from, to });
const board = openHeldDoors(state, perceivedBoard(state, events, caster.id, { from, to }));
const blockers = losBlockers(state);
if (sightBetween(board, from, to, blockers)) return true;
if (!displays(caster, "visionstone")) return false;