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:
co-authored by
Claude Fable 5
parent
6e1ccb0195
commit
ef921a0c2b
@@ -57,6 +57,10 @@ export interface GameView {
|
||||
/** Duration spells in play (public knowledge). */
|
||||
sustained: SustainedEffect[];
|
||||
squareContents: Record<string, SquareContent>;
|
||||
/** Cells whose contents are glued down (public: the cast was seen). */
|
||||
gluedCells: Record<string, true>;
|
||||
/** Safes standing open (their combination entered this turn). */
|
||||
openSafes: string[];
|
||||
groundObjects: Record<string, CardInstance[]>;
|
||||
doorStates: Record<string, "jammed" | "removed">;
|
||||
/** Accumulated attack damage per edge (public — cracks show). */
|
||||
@@ -144,6 +148,8 @@ export function viewFor(state: GameState, playerId: PlayerId): GameView {
|
||||
pendingDiscard: state.pendingDiscard,
|
||||
sustained: state.sustained.map((s) => ({ ...s })),
|
||||
squareContents: { ...state.squareContents },
|
||||
gluedCells: { ...state.gluedCells },
|
||||
openSafes: [...state.openSafes],
|
||||
groundObjects: Object.fromEntries(
|
||||
Object.entries(state.groundObjects).map(([k, v]) => [k, [...v]]),
|
||||
),
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user