Credibility pass: session residue swept from the rev-10 run
War-story comments become timeless constraints (carrier rule, boobytrap tell, remove-curse prescription); the rev changelog reunites under GameConfig.deckRev; three hand-copied cardless stack literals fold into openCardlessStack; the visionstone one-edge loop is shared via throughOneEdge with the edge-target hairpin exclusion stated; drain() moves to test helpers and replaces four inline copies; the butt-head sweep test loses its dead scaffolding and its expect(refused || true) tautology and now asserts both branches; a process-named suite is renamed for the behavior it pins; dead .bezel-spacer CSS and the fx effect's fossilized cleanup story go. No behavior changes: 304 engine tests pass, 36/36 ledgers replay, web typecheck clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
This commit is contained in:
co-authored by
Claude Fable 5
parent
f2af1e26f8
commit
e93bcc15a2
@@ -423,8 +423,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
/** Every targeted cast goes out through here so attachments never drop —
|
||||
* the attached NUMBER included, unless the call site already carried it. */
|
||||
/** Attachment plumbing for a cast command: the chosen mods and the
|
||||
* attached NUMBER ride along (an explicit numberInstanceIds wins). */
|
||||
function withMods<T extends Parameters<typeof net.command>[0] & { type: "cast" }>(cmd: T): T {
|
||||
applyMods(cmd);
|
||||
if (attachedNumber && !cmd.numberInstanceIds) {
|
||||
@@ -695,7 +695,6 @@
|
||||
dispatch(withMods({
|
||||
type: "cast", instanceId: selectedCard.instanceId,
|
||||
target: { kind: "cell", cell },
|
||||
...(attachedNumber ? { numberInstanceIds: [attachedNumber.instanceId] } : {}),
|
||||
}));
|
||||
clearSelection();
|
||||
return;
|
||||
@@ -880,7 +879,6 @@
|
||||
target: { kind: "edge", cell, side },
|
||||
...(holdDoor && (selectedCard.cardId === "pick-lock" || selectedCard.cardId === "master-key")
|
||||
? { params: { hold: true } } : {}),
|
||||
...(attachedNumber ? { numberInstanceIds: [attachedNumber.instanceId] } : {}),
|
||||
}));
|
||||
clearSelection();
|
||||
}
|
||||
@@ -901,7 +899,6 @@
|
||||
type: "cast", instanceId: selectedCard.instanceId,
|
||||
target: { kind: "creature", creatureId },
|
||||
...(selectedCard.cardId === "mega-monster" ? { params: { boost: megaBoost } } : {}),
|
||||
...(attachedNumber ? { numberInstanceIds: [attachedNumber.instanceId] } : {}),
|
||||
}));
|
||||
clearSelection();
|
||||
return;
|
||||
@@ -958,13 +955,12 @@
|
||||
swapMeetTarget = playerId;
|
||||
return; // next: pick your item, then theirs
|
||||
}
|
||||
const cmd: Parameters<typeof net.command>[0] = {
|
||||
const cmd: Parameters<typeof net.command>[0] & { type: "cast" } = {
|
||||
type: "cast",
|
||||
instanceId: selectedCard.instanceId,
|
||||
target: { kind: "player", playerId },
|
||||
};
|
||||
if (attachedNumber) cmd.numberInstanceIds = [attachedNumber.instanceId];
|
||||
applyMods(cmd);
|
||||
withMods(cmd);
|
||||
if (selectedCard.cardId === "waterbolt") {
|
||||
cmd.params = { damage: wbDamage, knockback: numberTotal - wbDamage };
|
||||
}
|
||||
|
||||
@@ -87,7 +87,6 @@
|
||||
: { x1: x * CELL, y1: (y + 1) * CELL, x2: (x + 1) * CELL, y2: (y + 1) * CELL };
|
||||
}
|
||||
|
||||
|
||||
const SECTOR = 5;
|
||||
/** Ghost slots can lie beyond the assembled maze — on any side, including
|
||||
* negative coordinates (the maze renormalizes after the landing). The
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
import { castRay, SIDE_ANGLE, OPPOSITE } from "./fpv/raycast";
|
||||
import { deepestFacing } from "./fpv/director";
|
||||
import { aimOfEvents, gatherGlides, hurledIn, shortestArc } from "./fpv/director";
|
||||
import { untrack } from "svelte";
|
||||
import { onDestroy, untrack } from "svelte";
|
||||
import type { GameEvent, GameView, Side } from "@wizwar/engine";
|
||||
|
||||
let { view, batch, onhide, onstride = null, canStride = false, ontarget = null, onfacing = null, litCells = null, edgeSelect = false, onpickup = null, onCreatureMove = null, onCreatureAttack = null, onbody = null, onWarpStep = null }: {
|
||||
@@ -224,11 +224,10 @@
|
||||
let actorPos = $state<Record<string, { x: number; y: number }>>({});
|
||||
|
||||
// --- The fx: each batch plays once, on its own beat. -----------------
|
||||
// The effect must track ONLY the batch: the server sends `events` then
|
||||
// `state` back to back, so anything else in the dependency set (me, the
|
||||
// view) re-runs this before a zero-delay timer can fire — and an eager
|
||||
// cleanup would cancel every spell before it left the wand. Timers die
|
||||
// only with the component.
|
||||
// Track ONLY the batch: the server sends `events` then `state` back to
|
||||
// back, so a wider dependency set re-runs this within the same frame,
|
||||
// before a zero-delay timer can fire. No per-run cleanup for the same
|
||||
// reason — timers die only with the component.
|
||||
let playedBatch = 0;
|
||||
const fxTimers = new Set<ReturnType<typeof setTimeout>>();
|
||||
$effect(() => {
|
||||
@@ -250,9 +249,8 @@
|
||||
fxTimers.add(starter);
|
||||
}
|
||||
});
|
||||
return undefined;
|
||||
});
|
||||
$effect(() => () => fxTimers.forEach(clearTimeout));
|
||||
onDestroy(() => fxTimers.forEach(clearTimeout));
|
||||
|
||||
// --- Other bodies glide between views. -------------------------------
|
||||
let prevView: GameView | null = null;
|
||||
@@ -528,7 +526,6 @@
|
||||
}
|
||||
.bezel-top { border-bottom: 1px solid #2a2620; }
|
||||
.bezel-bottom { border-top: 1px solid #2a2620; }
|
||||
.bezel-spacer { flex: 1; }
|
||||
.bezel-zone { flex: 1; display: flex; align-items: center; gap: 8px; }
|
||||
.bezel-center { justify-content: center; }
|
||||
.bezel-right { justify-content: flex-end; }
|
||||
|
||||
Reference in New Issue
Block a user