Fear's spaces are walked, not flown — warps carry the dread
The owner caught the metric: a wizard eight squares away as the crow flies stood three WALKED spaces away through a warp, and the manhattan check waved it through (while falsely blocking approaches manhattan- near through solid walls). fearRepels now measures walkingDistance — the same BFS the maze walks, warps included, walls respected. The board's aura follows suit: the diamond becomes a violet wash on every square within three walked spaces, computed by the same BFS, warp mouths included. No rev-32 ledger existed yet, so the metric corrects inside rev 32. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0138A8CjeQRpvzKxuMfz1Bqc
This commit is contained in:
co-authored by
Claude Fable 5
parent
644d651ae0
commit
410ebc0135
@@ -3726,8 +3726,10 @@ function fearRepels(state: GameState, moverId: PlayerId | null, from: Cell, to:
|
||||
for (const other of state.players) {
|
||||
if (!other.alive || other.id === moverId) continue;
|
||||
if (sustainedOn(state, other.id, "fear").length === 0) continue;
|
||||
const d = Math.abs(other.position.x - to.x) + Math.abs(other.position.y - to.y);
|
||||
const dBefore = Math.abs(other.position.x - from.x) + Math.abs(other.position.y - from.y);
|
||||
// "Spaces" are counted through the maze — warps included, walls
|
||||
// respected — exactly as feet would walk them.
|
||||
const d = walkingDistance(state, other.position, to);
|
||||
const dBefore = walkingDistance(state, other.position, from);
|
||||
if (d <= 3 && d < dBefore) return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -739,7 +739,7 @@ describe("fear holds off monsters and unwilling feet alike (rules rev 32)", () =
|
||||
wallPassesPerTurn: 0, wallPassUsed: 0, attackUsed: false, justCreated: false,
|
||||
scorchedThisTurn: [],
|
||||
} as never);
|
||||
state.edgeOverrides[edgeKey({ x: 2, y: 8 }, "S")] = "open";
|
||||
for (const y of [5, 6, 7, 8]) state.edgeOverrides[edgeKey({ x: 2, y }, "S")] = "open";
|
||||
while (state.players[state.turn.activeIndex]!.id !== "a") {
|
||||
const r = applyCommand(state, state.players[state.turn.activeIndex]!.id, { type: "endTurn", draw: 0 });
|
||||
if (!r.ok) throw new Error(r.error);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import type { Component } from "svelte";
|
||||
import { stepTarget } from "@wizwar/engine";
|
||||
import type { GameView, SightTrace } from "@wizwar/engine";
|
||||
import type { Side } from "@wizwar/engine";
|
||||
import type { BoardFx } from "./fx";
|
||||
@@ -180,6 +181,33 @@
|
||||
return boxes;
|
||||
});
|
||||
|
||||
// FEAR's dread reaches three WALKED spaces (through warps, around walls) —
|
||||
// the same BFS the engine's refusal runs, so the wash never lies.
|
||||
const fearCells = $derived.by(() => {
|
||||
const out = new Set<string>();
|
||||
for (const fp of view.players) {
|
||||
if (!fp.alive) continue;
|
||||
if (!view.sustained.some((e) => e.cardId === "fear" && e.targetId === fp.id)) continue;
|
||||
const seen = new Map<string, number>([[`${fp.position.x},${fp.position.y}`, 0]]);
|
||||
const queue = [fp.position];
|
||||
while (queue.length > 0) {
|
||||
const cur = queue.shift()!;
|
||||
const d = seen.get(`${cur.x},${cur.y}`)!;
|
||||
if (d >= 3) continue;
|
||||
for (const side of ["N", "S", "E", "W"] as const) {
|
||||
const t = stepTarget(view.board, cur, side);
|
||||
if (t.kind === "blocked") continue;
|
||||
const k = `${t.to.x},${t.to.y}`;
|
||||
if (seen.has(k) || view.squareContents[k]?.kind === "stone") continue;
|
||||
seen.set(k, d + 1);
|
||||
queue.push(t.to);
|
||||
}
|
||||
}
|
||||
for (const k of seen.keys()) out.add(k);
|
||||
}
|
||||
return out;
|
||||
});
|
||||
|
||||
// Warp pairs share a letter, like the printed openings on the real boards.
|
||||
const warpLetters = $derived.by(() => {
|
||||
const letters = new Map<string, string>();
|
||||
@@ -633,15 +661,12 @@
|
||||
{/if}
|
||||
{/each}
|
||||
{/if}
|
||||
<!-- FEAR's bubble: no willing step crosses this line toward its center -->
|
||||
{#each view.players.filter((p) => p.alive && view.sustained.some((e) => e.cardId === "fear" && e.targetId === p.id)) as fp (fp.id)}
|
||||
{@const fx = fp.position.x * CELL + CELL / 2}
|
||||
{@const fy = fp.position.y * CELL + CELL / 2}
|
||||
{@const r = 3.5 * CELL}
|
||||
<path
|
||||
d={`M ${fx} ${fy - r} L ${fx + r} ${fy} L ${fx} ${fy + r} L ${fx - r} ${fy} Z`}
|
||||
class="fear-aura"
|
||||
/>
|
||||
<!-- FEAR's bubble: every square within three WALKED spaces (warps count) -->
|
||||
{#each fearCells as key (key)}
|
||||
{@const bx = Number(key.split(",")[0])}
|
||||
{@const by = Number(key.split(",")[1])}
|
||||
<rect x={bx * CELL + 1.5} y={by * CELL + 1.5} width={CELL - 3} height={CELL - 3}
|
||||
class="fear-aura" rx="4" />
|
||||
{/each}
|
||||
|
||||
<!-- the sight line an attack traveled, leg by leg through any warp mouth -->
|
||||
|
||||
Reference in New Issue
Block a user