Safes finally open to keys — and the clockwork stops clawing at the lid

'All LOCK-type cards will work on it' was never wired: openSafes was
reset every turn and filled by nothing. A PICK LOCK or MASTER KEY
aimed at a safe's square (underfoot or beside) now opens it until
turn's end, with its own chronicle line; the dead never-emitted
safeOpened variant and its null humanize go. The automaton — H4EN's
Automaton II spent six turns grabbing at a locked lid saying 'I meant
to do that' — now cracks the box first (key or DISPEL CREATION, which
it held the whole time) and drops unopenable safes from its goals
rather than marching to one forever.

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-09-01 09:22:56 -04:00
co-authored by Claude Fable 5
parent 12ece6065a
commit eabc10f18c
6 changed files with 119 additions and 3 deletions
+22 -1
View File
@@ -652,6 +652,17 @@ function treasureGoals(view: GameView): Set<string> {
}
for (const t of view.treasures) {
if (!t.position || t.carriedBy) continue;
// A chest inside someone else's SAFE is no goal without a way in — a
// lock card turns the combination, DISPEL CREATION removes the box.
// Marching to an unopenable safe stalls the clockwork on it forever.
const boxKey = cellKey(t.position);
if (view.squareContents[boxKey]?.kind === "safe" &&
view.squareContents[boxKey]!.createdBy !== view.you &&
!view.openSafes.includes(boxKey) &&
!inHand(view, "pick-lock") && !inHand(view, "master-key") &&
!inHand(view, "dispel-creation")) {
continue;
}
if (t.owner === view.you) {
// REPOSSESSION: my own gold banked at an enemy's home is a point on
// THEIR scoreboard. Marching to take it back is worth the detour
@@ -1302,7 +1313,17 @@ export function automatonCommand(
// Repossessing my own gold off an enemy's home square.
view.players.some((p) => p.id !== you && cellKey(p.home) === here)),
);
if (prize) return { type: "pickUpTreasure", treasureId: prize.id };
if (prize) {
// A SAFE over the prize: turn the combination (or dispel the box)
// before reaching for the gold — the grab itself would be refused.
const boxed = view.squareContents[here]?.kind === "safe" &&
view.squareContents[here]!.createdBy !== you && !view.openSafes.includes(here);
if (!boxed) return { type: "pickUpTreasure", treasureId: prize.id };
const key = inHand(view, "pick-lock") ?? inHand(view, "master-key");
if (key) return { type: "cast", instanceId: key.instanceId, target: { kind: "cell", cell: self.position } };
const dispel = inHand(view, "dispel-creation");
if (dispel) return { type: "cast", instanceId: dispel.instanceId, target: { kind: "cell", cell: self.position } };
}
}
// Wounded clockwork teleports clear of visible hunters.