Three more cautions: the un-grabbed chest, the biting ground, the starved draw

Ending a turn on a treasure nobody picked up now asks first (unless the
auto-grab preference is about to take it, or the grab would be refused —
laden arms, weakness, spent actions, glue, a locked safe, or your own
home where the chest already scores; the view learns gluedCells and
openSafes so the check is honest). Stepping onto ground that bites —
thornbush, tacks, pit, ooze — warns with the exact price, unless you are
mist. And the sticky draw picker no longer quietly starves anyone: a
draw below what would fit asks too.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Eric Wagoner
2026-08-23 11:04:05 -04:00
co-authored by Claude Fable 5
parent 6e1ccb0195
commit ef921a0c2b
2 changed files with 43 additions and 1 deletions
+37 -1
View File
@@ -63,7 +63,8 @@
if (!prefs.cautions || reasons.length === 0) go();
else caution = { reasons, go };
}
/** A move with the delivery check: stepping off your home still laden. */
/** A move with its regret checks: stepping off your home still laden,
* or stepping onto ground that bites. */
function tryMove(direction: Side, over?: boolean) {
const reasons: string[] = [];
if (view && me && me.carriedTreasureId && cellKey(me.position) === cellKey(me.home)) {
@@ -72,6 +73,22 @@
? `you stand on your home with ${t.owner}'s treasure in your arms — drop it first and it scores`
: "you stand on your home with your own treasure in your arms — drop it first and it is safe");
}
if (view && me) {
const misted = view.sustained.some((e) => e.cardId === "mist-body" && e.targetId === view.you);
const steps = over ? 2 : 1;
const dest = { x: me.position.x + (direction === "E" ? steps : direction === "W" ? -steps : 0),
y: me.position.y + (direction === "S" ? steps : direction === "N" ? -steps : 0) };
const ground = view.squareContents[cellKey(dest)]?.kind;
if (!misted && ground) {
const bite: Record<string, string> = {
thornbush: "the thorns end your turn, cost you the NEXT one, and prick for 1",
tacks: "the tacks bite for 3",
pit: "the pit: roll a 1 and you fall in, hurt and stuck",
ooze: "the ooze burns 1 — and may drop you flat, treasure and all",
};
if (bite[ground]) reasons.push(bite[ground]);
}
}
withCaution(reasons, () => dispatch({ type: "move", direction, ...(over ? { over: true } : {}) }));
}
function playFx(events: Parameters<typeof scheduleFx>[0]) {
@@ -919,6 +936,25 @@
? `${t.owner}'s treasure is still in your arms — drop it here and it scores`
: "your own treasure is still in your arms — drop it here and it is safe");
}
// Treasure underfoot un-grabbed — unless the auto-grab preference (or
// this caution's own "do it anyway") is about to handle it, or the
// grab would be refused (laden, weakened, actions spent, glue, safe,
// or your own home where a stolen chest is already scoring).
const here = cellKey(me.position);
const autoWillGrab = prefs.autoGrab && treasuresHere.length === 1;
const weakened = view.sustained.some((e) => e.cardId === "weakness" && e.targetId === view.you);
const safed = view.squareContents[here]?.kind === "safe" &&
view.squareContents[here]!.createdBy !== view.you && !view.openSafes.includes(here);
if (treasuresHere.length > 0 && !autoWillGrab && !me.carriedTreasureId &&
!view.turn.actionsEnded && !weakened && !view.gluedCells[here] && !safed &&
here !== cellKey(me.home)) {
reasons.push(`a treasure lies at your feet, un-grabbed`);
}
// The draw picker is sticky: a 0 or 1 chosen turns ago quietly starves.
const drawRoom = Math.min(2, room);
if (drawCount < drawRoom) {
reasons.push(`drawing only ${drawCount} when ${drawRoom} would fit`);
}
}
withCaution(reasons, () => {
// Preference: a lone grabbable treasure underfoot is picked up on the