Swap Meet trades treasures — a carried treasure is a carried item
The trade grammar gains a 'treasure' token (never present in stored ledgers, so no gate needed): a dagger can buy back the treasure in a thief's arms, or two armfuls of gold can change hands outright. The one-treasure carry limit and WEAKNESS both hold in a trade as at a grab — a one-way treasure needs an open slot and an able back. The picker offers each side's carried treasure alongside displayed items, withholding theirs when your own arms are already full. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0138A8CjeQRpvzKxuMfz1Bqc
This commit is contained in:
co-authored by
Claude Fable 5
parent
00517e08b1
commit
3aef6694f3
@@ -834,14 +834,36 @@
|
||||
}
|
||||
|
||||
// SWAP MEET: pick one of your carried items, then one of theirs you can
|
||||
// see (their displayed items — hidden hand cards are not on the table).
|
||||
// see (their displayed items and any treasure in their arms — hidden
|
||||
// hand cards are not on the table).
|
||||
let swapMeetTarget = $state<string | null>(null);
|
||||
let swapMine = $state<string | null>(null);
|
||||
const myTradables = $derived(view ? view.yourHand.filter((c) => isMovableObject(c.cardId)) : []);
|
||||
const myTradables = $derived.by(() => {
|
||||
if (!view) return [];
|
||||
const opts = new Map<string, string>();
|
||||
for (const c of view.yourHand) {
|
||||
if (isMovableObject(c.cardId)) opts.set(c.cardId, cardDef(c.cardId).name);
|
||||
}
|
||||
if (me?.carriedTreasureId) {
|
||||
const t = view.treasures.find((t) => t.id === me.carriedTreasureId);
|
||||
opts.set("treasure", `${t?.owner ?? "the"}'s treasure (carried)`);
|
||||
}
|
||||
return [...opts].map(([key, label]) => ({ key, label }));
|
||||
});
|
||||
const theirTradables = $derived.by(() => {
|
||||
if (!view || !swapMeetTarget) return [];
|
||||
const t = view.players.find((p) => p.id === swapMeetTarget);
|
||||
return t ? t.displayed.filter((c) => isMovableObject(c.cardId)) : [];
|
||||
const them = view.players.find((p) => p.id === swapMeetTarget);
|
||||
if (!them) return [];
|
||||
const opts = new Map<string, string>();
|
||||
for (const c of them.displayed) {
|
||||
if (isMovableObject(c.cardId)) opts.set(c.cardId, cardDef(c.cardId).name);
|
||||
}
|
||||
// Their treasure is claimable unless your arms are already full.
|
||||
if (them.carriedTreasureId && (swapMine === "treasure" || !me?.carriedTreasureId)) {
|
||||
const t = view.treasures.find((t) => t.id === them.carriedTreasureId);
|
||||
opts.set("treasure", `${t?.owner ?? "the"}'s treasure (carried)`);
|
||||
}
|
||||
return [...opts].map(([key, label]) => ({ key, label }));
|
||||
});
|
||||
function castSwapMeet(theirs: string) {
|
||||
if (!selectedCard || !swapMeetTarget || !swapMine) return;
|
||||
@@ -1706,15 +1728,15 @@
|
||||
<span>— you carry no items to trade</span>
|
||||
{:else if !swapMine}
|
||||
<span>— trade away your:</span>
|
||||
{#each myTradables as c (c.instanceId)}
|
||||
<button class="stamp tiny" onclick={() => (swapMine = c.cardId)}>{cardDef(c.cardId).name}</button>
|
||||
{#each myTradables as c (c.key)}
|
||||
<button class="stamp tiny" onclick={() => (swapMine = c.key)}>{c.label}</button>
|
||||
{/each}
|
||||
{:else if theirTradables.length === 0}
|
||||
<span>— they show no item you could claim</span>
|
||||
{:else}
|
||||
<span>— and take their:</span>
|
||||
{#each theirTradables as c (c.instanceId)}
|
||||
<button class="stamp tiny" onclick={() => castSwapMeet(c.cardId)}>{cardDef(c.cardId).name}</button>
|
||||
{#each theirTradables as c (c.key)}
|
||||
<button class="stamp tiny" onclick={() => castSwapMeet(c.key)}>{c.label}</button>
|
||||
{/each}
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
Reference in New Issue
Block a user