Around the Corner bends every spell, hairpins included

Two holes, found when a SAFE cast at a treasure 'around the 180 degree
corner' refused with no line of sight (room LZ5R). First: the bend was
honored only in the attack path — every neutral resolve (creations,
summons, utilities, edge targets) checked straight sight and ignored
the attached card, though the card says 'any line of sight spell'.
castSight/castSightEdge now thread the bend through all of them, and
the chronicle narrates the neutral bend too.

Second: the bend itself pivoted only at cell centers, so the card's
flagship shot — doubling back up to 180 degrees around a wall's end —
never resolved: both legs graze the corner and die. The pivot may now
sit in the wall's open gap itself (bentSightThroughGap), which is what
rounding a corner physically is. Closed doors still block; two corners
still refuse.

The client lights bent-reachable squares for real now instead of the
old one-step-adjacency guess, creations included. Pure widening, so no
rev bump: every one of these casts was refused before, so no ledger
holds one. All 26 production ledgers replay clean; the LZ5R moment
itself replayed and the exact refused cast now lands.

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-27 17:34:48 -04:00
co-authored by Claude Fable 5
parent f53f26ace9
commit 3bf3c33fdc
5 changed files with 263 additions and 49 deletions
+4 -15
View File
@@ -14,7 +14,7 @@
import { prefs, savePrefs } from "./prefs.svelte";
import { CREATURE_ART, objectArt, TERRAIN_ART, tokenArt } from "./art";
import { local } from "./local.svelte";
import { allCardDefs, cardDef, dreadDistance, isNumberCard, isPermanentDuration, SIDES, stepTarget, cellKey, edgeKey, isMovableObject, sightedCellsFor, stackSightTrace, type GameView, eligibleCellsFor } from "@wizwar/engine";
import { allCardDefs, cardDef, dreadDistance, isNumberCard, isPermanentDuration, SIDES, stepTarget, cellKey, edgeKey, isMovableObject, sightedCellsFor, bentSightedCellsFor, stackSightTrace, type GameView, eligibleCellsFor } from "@wizwar/engine";
import type { CardInstance, GameEvent, Side } from "@wizwar/engine";
net.connect();
@@ -1172,8 +1172,9 @@
if (!view || !selectedDef || !yourMoment) return null;
if (ambushVia || ambushSpell || ambushTrigger) return null; // ambushes aim at the future
if (!me) return null;
const bent = attachedMods.some((m) => m.cardId === "around-the-corner");
if (CELL_CARDS.has(selectedCard!.cardId)) {
return eligibleCellsFor(view, selectedCard!.cardId);
return eligibleCellsFor(view, selectedCard!.cardId, bent);
}
if (selectedDef.adjacent === true) {
const { x, y } = me.position;
@@ -1184,19 +1185,7 @@
);
}
if (selectedDef.los !== true) return null;
const sighted = sightedCellsFor(view);
if (attachedMods.some((m) => m.cardId === "around-the-corner")) {
const widened = new Set(sighted);
for (const key of Object.keys(view.board.cells)) {
if (widened.has(key)) continue;
const [cx, cy] = key.split(",").map(Number);
if ([[1, 0], [-1, 0], [0, 1], [0, -1]].some(([dx, dy]) => sighted.has(`${cx! + dx!},${cy! + dy!}`))) {
widened.add(key);
}
}
return widened;
}
return sighted;
return bent ? bentSightedCellsFor(view) : sightedCellsFor(view);
});
/** Creatures awaiting your orders this turn (yours + any democratic monster). */