Credibility pass: the accretion sanded smooth
Blind reviews over d2b40ec..HEAD (engine / web / server+deploy). Orphaned doc comments rejoined their functions; the swap-meet tradables collapsed from four pasted deriveds to one (killing the "the's treasure" label); FEAR's aura and banner now share the engine's own dreadDistance; the long-press peek got one home; rulebook.ts stopped wearing JSON quotes; process-named tests and war-story comments now state the constraints they pin; the protocol doc caught up to its handlers; verify-ledgers.sh can actually count failures; the runbook learned about the determinism gate and the nightly backups. No behavior changes — 255 tests and all 48 production ledgers replay identically. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
3445d12206
commit
6adcbe23b5
+12
-12
@@ -958,7 +958,7 @@ const CARD_EFFECTS: Record<string, AttackEffect | NeutralEffect | CounterEffect>
|
||||
if (ctx.fullyStopped || !ctx.defender.alive) return;
|
||||
if (isLockedInPlace(ctx.state, ctx.defender.id)) return;
|
||||
const to = ctx.stack.params?.cell;
|
||||
if (!to) return; // an old ambush sprang it with no destination: fizzle
|
||||
if (!to) return; // pre-rev-35 ambushes carry no destination: fizzle
|
||||
const from = ctx.defender.position;
|
||||
ctx.defender.position = to;
|
||||
ctx.events.push({
|
||||
@@ -2077,7 +2077,7 @@ const CARD_EFFECTS: Record<string, AttackEffect | NeutralEffect | CounterEffect>
|
||||
if (ctx.fullyStopped || !ctx.defender.alive) return;
|
||||
if (isLockedInPlace(ctx.state, ctx.defender.id)) return;
|
||||
const to = ctx.stack.params?.cell;
|
||||
if (!to) return; // an old ambush sprang it with no destination: fizzle
|
||||
if (!to) return; // pre-rev-35 ambushes carry no destination: fizzle
|
||||
if (walkingDistance(ctx.state, ctx.defender.position, to) > 3) return;
|
||||
const from = ctx.defender.position;
|
||||
ctx.defender.position = to;
|
||||
@@ -2586,9 +2586,8 @@ function waveForce(state: GameState, range: number, dist: number): number {
|
||||
|
||||
/** A collapsing waterwall wave from an edge: wash players back `range`. */
|
||||
function waveFromEdge(state: GameState, events: GameEvent[], cell: Cell, side: Side, range: number, reason = "rushing water"): void {
|
||||
const away = (s2: Side): Side => (s2 === "N" ? "S" : s2 === "S" ? "N" : s2 === "E" ? "W" : "E");
|
||||
const pushes: { start: Cell; dir: Side }[] = [
|
||||
{ start: cell, dir: away(side) },
|
||||
{ start: cell, dir: opposite(side) },
|
||||
{ start: neighbor(cell, side), dir: side },
|
||||
];
|
||||
for (const { start, dir } of pushes) {
|
||||
@@ -3734,23 +3733,24 @@ function takeFromHand(p: PlayerState, instanceId: string): CardInstance | null {
|
||||
|
||||
// --- Movement ---------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* FEAR: "no player or monster will move to within 3 spaces of you." True
|
||||
* for every WILLING move — walking, warp steps, self-teleports, commanded
|
||||
* monsters (rules rev 32 widened it beyond walking wizards; forced movement
|
||||
* such as knockback or a wave is not willing and passes).
|
||||
*/
|
||||
/**
|
||||
* FEAR's measure — FAQ: "as a Wizard walks (if he could walk through
|
||||
* walls), not diagonally." Orthogonal steps, walls ignored — and the maze
|
||||
* wraps, so stepping off one rim continues from the opposite one.
|
||||
* Exported so the client draws the aura by the same yardstick.
|
||||
*/
|
||||
function dreadDistance(board: AssembledBoard, a: Cell, b: Cell): number {
|
||||
export function dreadDistance(board: AssembledBoard, a: Cell, b: Cell): number {
|
||||
const dx = Math.abs(a.x - b.x);
|
||||
const dy = Math.abs(a.y - b.y);
|
||||
return Math.min(dx, board.width - dx) + Math.min(dy, board.height - dy);
|
||||
}
|
||||
|
||||
/**
|
||||
* FEAR: "no player or monster will move to within 3 spaces of you." True
|
||||
* for every WILLING move — walking, warp steps, self-teleports, commanded
|
||||
* monsters (rules rev 32 widened it beyond walking wizards; forced movement
|
||||
* such as knockback or a wave is not willing and passes).
|
||||
*/
|
||||
function fearRepels(state: GameState, moverId: PlayerId | null, from: Cell, to: Cell): boolean {
|
||||
// The wrap joined the measure at rev 32; older ledgers were recorded
|
||||
// under the flat diamond and replay so.
|
||||
@@ -4271,7 +4271,6 @@ function touchesEdge(position: Cell, cell: Cell, side: Side): boolean {
|
||||
return cellKey(position) === cellKey(cell) || cellKey(position) === cellKey(neighbor(cell, side));
|
||||
}
|
||||
|
||||
/** Arm (or stand down) the WARD trap on your treasures. Your secret. */
|
||||
/** The owner's answer to a hanging grab: spring the WARD, or let them go. */
|
||||
function doWardChoice(prev: GameState, play: boolean): CommandResult {
|
||||
const state = clone(prev);
|
||||
@@ -4293,6 +4292,7 @@ function doWardChoice(prev: GameState, play: boolean): CommandResult {
|
||||
return { ok: true, state, events };
|
||||
}
|
||||
|
||||
/** Arm (or stand down) the WARD trap on your treasures. Your secret. */
|
||||
function doArmWard(prev: GameState, armed: boolean): CommandResult {
|
||||
// Rev 31 reads the card literally: the Ward is played in the moment of
|
||||
// the grab, never set ahead. Older games keep their arming and replay so.
|
||||
|
||||
Reference in New Issue
Block a user