Credibility pass: comments say what the code can't, and the seams are gone
Engine: pit-rim exits live in one helper (pitRimExits) shared by the resolver and the automaton; chargeReach names the six-stride cap; dust sight is inDust + dustAtEnd; PushPending is exported once; the force-field counter builds its events in one list; drawUnderSlowDeath says what it draws under. Test groups are named for the rule they pin, not the revision that introduced it. Client: one smoothstep; one edgeScar per battle-scarred edge; one board-zone rule (which also seats the caption strip on phones); the compass names live in SIDE_NAMES; net.flash() is the one toast; MediaQuery replaces two hand-rolled matchMedia listeners; sprites carry their sizes as plain numbers and their CSS in one rule each; canvas helpers are formatted like the rest of the tree. Comments that told how a cel or a fallback used to look now say what it draws. The actions-over notice no longer blames a pickup when slime ended the turn. Server and ops: dataRoot() is the one data directory; headlineOf builds its deeds without a cast; the rollup and skills read the same way the code behaves. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
85690f0e23
commit
6e49fc9020
@@ -7,7 +7,7 @@
|
||||
// BERSERKER for blood, the WORRIER for the shadows between the two.
|
||||
|
||||
import { cardDef, type CardInstance } from "./cards";
|
||||
import { cellKey, edgeKey, neighbor, opposite, stepTarget, SIDES, type Cell, type Side } from "./board";
|
||||
import { cellKey, edgeKey, neighbor, opposite, stepTarget, SIDES, type Cell, type Side, pitRimExits } from "./board";
|
||||
import { bentSightFor, sightedCellsFor, type GameView } from "./view";
|
||||
import { wallIgnoringDistance } from "./game";
|
||||
import type { AmbushTrigger, Command, PlayerId } from "./game";
|
||||
@@ -584,31 +584,19 @@ function wallBlastTarget(
|
||||
return { cell: best.cell, side: best.side };
|
||||
}
|
||||
|
||||
/** The sides a walker may leave a pit by, having entered it heading
|
||||
* `dir`: neighbouring squares first, a warp mouth on the rim only when
|
||||
* no square offers — the one reading legal under every deckRev. */
|
||||
function pitExits(view: GameView, pit: Cell, dir: Side): Side[] {
|
||||
const { floor, mouths } = pitRimExits(view.board, pit, dir, (c) => view.squareContents[cellKey(c)]?.kind === "stone");
|
||||
return floor.length > 0 ? floor : mouths;
|
||||
}
|
||||
|
||||
/**
|
||||
* BFS over walkable steps toward the nearest goal. Doors count as passable
|
||||
* when the clockwork can unlock them; the first such door is reported so the
|
||||
* key gets used before the boot.
|
||||
*/
|
||||
/** Can a wizard entering the pit at `pit` heading `dir` get off its rim
|
||||
* — the maze's own test: some side other than the way back with a cell
|
||||
* there, no wall between, and no stone on it. */
|
||||
/** The sides a walker may leave a pit by, having entered it heading
|
||||
* `dir`: neighbouring squares first; a warp mouth on the rim only when
|
||||
* no square offers, the reading every revision accepts. */
|
||||
function pitExits(view: GameView, pit: Cell, dir: Side): Side[] {
|
||||
const footing = (d: Side) => {
|
||||
if (d === opposite(dir)) return null;
|
||||
const t = stepTarget(view.board, pit, d);
|
||||
if (t.kind === "blocked" || view.squareContents[cellKey(t.to)]?.kind === "stone") return null;
|
||||
return t.kind;
|
||||
};
|
||||
const floor = SIDES.filter((d) => footing(d) === "step");
|
||||
return floor.length > 0 ? floor : SIDES.filter((d) => footing(d) === "warp");
|
||||
}
|
||||
|
||||
function pitLandable(view: GameView, pit: Cell, dir: Side): boolean {
|
||||
return pitExits(view, pit, dir).length > 0;
|
||||
}
|
||||
|
||||
export function pathToward(
|
||||
view: GameView,
|
||||
@@ -634,12 +622,12 @@ export function pathToward(
|
||||
const { to, viaDoor } = step;
|
||||
const k = cellKey(to);
|
||||
if (seen.has(k)) continue;
|
||||
if (view.squareContents[k]?.kind === "stone") continue;
|
||||
const hazard = view.squareContents[k]?.kind;
|
||||
if (hazard === "stone") continue;
|
||||
// A pit is entered by leaping to the square beyond it in the same
|
||||
// direction; with nothing to land on, the maze bounces the leaper
|
||||
// back and charges the stride. Goal or waypoint, that is no road.
|
||||
if (hazard === "pit" && !pitLandable(view, to, dir)) continue;
|
||||
if (hazard === "pit" && pitExits(view, to, dir).length === 0) continue;
|
||||
// Out of a pit only by one of its exits.
|
||||
if (view.squareContents[cellKey(c)]?.kind === "pit") {
|
||||
const entry = cameBy.get(cellKey(c))?.dir;
|
||||
@@ -766,7 +754,7 @@ function overLimit(view: GameView): number {
|
||||
* clogged hand never refreshes; only cards the brain has no play for are
|
||||
* shed (good counters and attacks are hoarded, as a human would). */
|
||||
function endTurnDrawing(view: GameView, tier: TierTraits, style?: AutomatonStyle): Command {
|
||||
const draw = drawUnderCurses(view, tier.draw);
|
||||
const draw = drawUnderSlowDeath(view, tier.draw);
|
||||
const deficit = draw - (handLimitOf(view) - view.yourHand.length);
|
||||
if (tier.sheds && deficit > 0) {
|
||||
const shed = [...view.yourHand]
|
||||
@@ -782,7 +770,7 @@ function endTurnDrawing(view: GameView, tier: TierTraits, style?: AutomatonStyle
|
||||
/** SLOW DEATH bleeds a point per card drawn: the clockwork draws only what
|
||||
* it can afford, keeping four life in hand, and nothing at all when it
|
||||
* cannot — a bare hand beats a bare grave. */
|
||||
export function drawUnderCurses(view: GameView, draw: number): number {
|
||||
export function drawUnderSlowDeath(view: GameView, draw: number): number {
|
||||
const slow = view.sustained.some((e) => e.cardId === "slow-death" && e.targetId === view.you && !e.data?.reversed);
|
||||
if (!slow) return draw;
|
||||
return Math.max(0, Math.min(draw, me(view).life - 4));
|
||||
|
||||
Reference in New Issue
Block a user