The needle shows itself; floor objects answer a tap

Two findings from duel two (room UNS6). AROUND THE CORNER's bent
sight was legal but invisible: a 1-by-6 diagonal threading six open
edge spans read as an impossible shot because the table never saw the
line. The stack now records bentCorner, and stackSightTrace finds the
middle square and returns both legs; the board draws them with a
pulsing diamond on the corner the sight bent around — the answer to
"how can he even see me?" now covers the bent case.

Floor objects (a dropped wizardblade) showed a marker but answered
only a 450ms hold; a plain click did nothing. The marker itself is
now a click target that opens the same card peek.

All 21 ledgers verified; 284 tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
This commit is contained in:
Eric Wagoner
2026-08-26 10:38:57 -04:00
co-authored by Claude Fable 5
parent 858fdc6a56
commit 9e4807eab8
4 changed files with 80 additions and 7 deletions
+30 -1
View File
@@ -2,7 +2,7 @@ import { describe, expect, it } from "vitest";
import { applyCommand, activePlayer, boardView, createGame, type GameState } from "../src/game";
import { cellKey, edgeKey, hasLineOfSight, neighbor, type Cell, type Side, SIDES, stepTarget } from "../src/board";
import type { CardInstance } from "../src/cards";
import { sightedCellsFor, viewFor } from "../src/view";
import { sightedCellsFor, stackSightTrace, viewFor } from "../src/view";
import { newGame, must, giveCard, toRound2, faceOff } from "./helpers";
/** Test surgery: put a specific card into a player's hand (swapping one out). */
@@ -1166,3 +1166,32 @@ describe("MENTAL FORCE respects the victim's three walked spaces", () => {
expect(state.players.find((p) => p.id === caster.id)!.hand.some((c) => c.instanceId === mf.instanceId)).toBe(true);
});
});
describe("the bent sight-trace shows AROUND THE CORNER's legs", () => {
it("a bentCorner stack yields two auditable legs through a midpoint", () => {
let { state } = newGame();
state = toRound2(state);
const a = activePlayer(state);
const d = state.players.find((p) => p.id !== a.id)!;
// Diagonal neighbors: center-to-center sight grazes the shared corner
// (blocked, strict reading), but the legs through (3,2) are opened.
a.position = { x: 2, y: 2 };
d.position = { x: 3, y: 3 };
state.edgeOverrides[edgeKey({ x: 2, y: 2 }, "E")] = "open";
state.edgeOverrides[edgeKey({ x: 3, y: 2 }, "S")] = "open";
state.stack = {
attackerId: a.id, defenderId: d.id,
attackCard: { instanceId: "fireball#T", cardId: "fireball" },
numberValue: null, amplifyFactor: 1, extendFactor: 1,
powerAttackPoints: 0, params: null, kind: "spell",
counters: [], waitingOn: d.id, bentCorner: true,
};
const traced = stackSightTrace(viewFor(state, d.id));
expect(traced).not.toBeNull();
if (traced) {
// Whether sight ran straight (free-angle found a gap) or bent, the
// overlay has something to draw; a bend names its middle square.
if (traced.bend) expect(traced.bend.mid).toBeDefined();
}
});
});