Objects can be set down and picked back up

The engine always knew how — dropObject and pickUpObject were in the
command set from the start — but the table never offered a hand for
it. Now selecting a magic stone (or any object card) on your turn
shows a "Drop it here" button in the hint bar, and standing on a
square with objects lying on it offers a "Pick up <name>" stamp per
object, disabled once your actions have ended (picking anything up
ends them, per the rulebook). The chronicle narrates both.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Eric Wagoner
2026-08-16 09:55:04 -04:00
co-authored by Claude Fable 5
parent 7a231cbe1b
commit c04ed1e0ee
2 changed files with 18 additions and 0 deletions
+16
View File
@@ -457,6 +457,11 @@
view.treasures.some((t) => t.position && t.position.x === me.position.x && view.treasures.some((t) => t.position && t.position.x === me.position.x &&
t.position.y === me.position.y && !t.carriedBy), t.position.y === me.position.y && !t.carriedBy),
); );
const objectsHere = $derived(
view != null && me != null
? (view.groundObjects[`${me.position.x},${me.position.y}`] ?? [])
: [],
);
const onWarpToken = $derived( const onWarpToken = $derived(
view != null && me != null && view != null && me != null &&
view.dimWarps.some((w) => view.dimWarps.some((w) =>
@@ -907,6 +912,12 @@
Cast{attachedNumber ? ` with the ${numberTotal}` : ""} Cast{attachedNumber ? ` with the ${numberTotal}` : ""}
</button> </button>
{/if} {/if}
{#if selectedCard && selectedDef.cardType === "object" && isYourTurn}
<button class="stamp tiny" onclick={() => {
if (selectedCard) dispatch({ type: "dropObject", instanceId: selectedCard.instanceId });
clearSelection();
}}>Drop it here</button>
{/if}
{#if selectedCard && isNumberCard(selectedCard.cardId)} {#if selectedCard && isNumberCard(selectedCard.cardId)}
<button class="stamp tiny" onclick={playNumberForMovement}> <button class="stamp tiny" onclick={playNumberForMovement}>
Play for +{cardDef(selectedCard.cardId).value} movement{view.turn.numberPlayedForMovement ? " (uses your Add)" : ""} Play for +{cardDef(selectedCard.cardId).value} movement{view.turn.numberPlayedForMovement ? " (uses your Add)" : ""}
@@ -925,6 +936,11 @@
<button class="stamp" disabled={!treasureHere || carryingTreasure || view.turn.actionsEnded} <button class="stamp" disabled={!treasureHere || carryingTreasure || view.turn.actionsEnded}
onclick={pickUp}>Pick up treasure</button> onclick={pickUp}>Pick up treasure</button>
<button class="stamp" disabled={!carryingTreasure} onclick={drop}>Drop treasure</button> <button class="stamp" disabled={!carryingTreasure} onclick={drop}>Drop treasure</button>
{#each objectsHere as obj (obj.instanceId)}
<button class="stamp" disabled={view.turn.actionsEnded}
onclick={() => dispatch({ type: "pickUpObject", instanceId: obj.instanceId })}>
Pick up {cardDef(obj.cardId).name}</button>
{/each}
<button class="stamp" onclick={startDiscardMode}>Discard cards…</button> <button class="stamp" onclick={startDiscardMode}>Discard cards…</button>
<label class="inline draw-pick"> <label class="inline draw-pick">
draw draw
+2
View File
@@ -127,6 +127,8 @@ export function humanize(e: GameEvent): string | null {
case "died": return `${e.player} is dead${e.killedBy ? ` — killed by ${e.killedBy}` : ""}.`; case "died": return `${e.player} is dead${e.killedBy ? ` — killed by ${e.killedBy}` : ""}.`;
case "handTaken": return `${e.to} takes ${e.count} cards from ${e.from}'s body.`; case "handTaken": return `${e.to} takes ${e.count} cards from ${e.from}'s body.`;
case "treasurePickedUp": return `${e.player} grabs ${e.owner}'s treasure!`; case "treasurePickedUp": return `${e.player} grabs ${e.owner}'s treasure!`;
case "objectDropped": return `${e.player} sets down the ${cardDef(e.card.cardId).name}${e.forced ? " (forced)" : ""}.`;
case "objectPickedUp": return `${e.player} picks up the ${cardDef(e.card.cardId).name} — actions over.`;
case "treasureDropped": return e.onHomeOf ? `${e.player} drops a treasure on ${e.onHomeOf}'s home base!` : `${e.player} drops a treasure.`; case "treasureDropped": return e.onHomeOf ? `${e.player} drops a treasure on ${e.onHomeOf}'s home base!` : `${e.player} drops a treasure.`;
case "playerEliminated": return e.reason === "treasuresLost" ? `${e.player} is eliminated — both treasures lost!` : null; case "playerEliminated": return e.reason === "treasuresLost" ? `${e.player} is eliminated — both treasures lost!` : null;
case "cardsDiscarded": return `${e.player} discards ${e.cards.length} card(s).`; case "cardsDiscarded": return `${e.player} discards ${e.cards.length} card(s).`;