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
+48
-10
@@ -2155,20 +2155,58 @@ const CARD_EFFECTS: Record<string, AttackEffect | NeutralEffect | CounterEffect>
|
||||
const [mineId, theirsId] = (ctx.stack.params!.cardId ?? "").split(";");
|
||||
// "Swap any two carried items": every movable object trades — daggers,
|
||||
// rocks, wands, stones (rules rev 26; earlier games matched only
|
||||
// object-typed cards and replay so).
|
||||
// object-typed cards and replay so) — and a carried TREASURE is an
|
||||
// item too, named by the "treasure" token (never in older ledgers).
|
||||
const tradable = (id: string) =>
|
||||
(ctx.state.config.deckRev ?? 1) >= 26
|
||||
? isMovableObject(id)
|
||||
: cardDef(id).cardType === "object";
|
||||
const mine = ctx.attacker.hand.findIndex((c) => c.cardId === mineId && tradable(c.cardId));
|
||||
const theirs = ctx.defender.hand.findIndex((c) => c.cardId === theirsId && tradable(c.cardId));
|
||||
if (mine === -1 || theirs === -1) return;
|
||||
const [a] = ctx.attacker.hand.splice(mine, 1);
|
||||
const [b] = ctx.defender.hand.splice(theirs, 1);
|
||||
ctx.attacker.hand.push(b!);
|
||||
ctx.defender.hand.push(a!);
|
||||
ctx.attacker.displayed = ctx.attacker.displayed.filter((id) => id !== a!.instanceId);
|
||||
ctx.defender.displayed = ctx.defender.displayed.filter((id) => id !== b!.instanceId);
|
||||
type TradeSide = { kind: "card"; idx: number } | { kind: "treasure" };
|
||||
const side = (p: PlayerState, id: string): TradeSide | null => {
|
||||
if (id === "treasure") return p.carriedTreasureId ? { kind: "treasure" } : null;
|
||||
const idx = p.hand.findIndex((c) => c.cardId === id && tradable(c.cardId));
|
||||
return idx === -1 ? null : { kind: "card", idx };
|
||||
};
|
||||
const mine = side(ctx.attacker, mineId ?? "");
|
||||
const theirs = side(ctx.defender, theirsId ?? "");
|
||||
if (!mine || !theirs) return;
|
||||
// A one-way treasure needs an open carry slot and an able back:
|
||||
// "you are too weak to carry treasure" holds in a trade as at a grab.
|
||||
const weak = (p: PlayerState) => sustainedOn(ctx.state, p.id, "weakness").length > 0;
|
||||
if (mine.kind === "treasure" && theirs.kind !== "treasure" &&
|
||||
(ctx.defender.carriedTreasureId != null || weak(ctx.defender))) return;
|
||||
if (theirs.kind === "treasure" && mine.kind !== "treasure" &&
|
||||
(ctx.attacker.carriedTreasureId != null || weak(ctx.attacker))) return;
|
||||
const giveTreasure = (from: PlayerState, to: PlayerState) => {
|
||||
const t = ctx.state.treasures.find((t) => t.id === from.carriedTreasureId)!;
|
||||
from.carriedTreasureId = null;
|
||||
t.carriedBy = to.id;
|
||||
return t;
|
||||
};
|
||||
const takeCard = (p: PlayerState, s: Extract<TradeSide, { kind: "card" }>) => {
|
||||
const [card] = p.hand.splice(s.idx, 1);
|
||||
p.displayed = p.displayed.filter((id) => id !== card!.instanceId);
|
||||
return card!;
|
||||
};
|
||||
if (mine.kind === "treasure" && theirs.kind === "treasure") {
|
||||
const tA = giveTreasure(ctx.attacker, ctx.defender);
|
||||
const tB = giveTreasure(ctx.defender, ctx.attacker);
|
||||
ctx.defender.carriedTreasureId = tA.id;
|
||||
ctx.attacker.carriedTreasureId = tB.id;
|
||||
} else if (mine.kind === "treasure") {
|
||||
const t = giveTreasure(ctx.attacker, ctx.defender);
|
||||
ctx.defender.carriedTreasureId = t.id;
|
||||
ctx.attacker.hand.push(takeCard(ctx.defender, theirs as Extract<TradeSide, { kind: "card" }>));
|
||||
} else if (theirs.kind === "treasure") {
|
||||
const t = giveTreasure(ctx.defender, ctx.attacker);
|
||||
ctx.attacker.carriedTreasureId = t.id;
|
||||
ctx.defender.hand.push(takeCard(ctx.attacker, mine));
|
||||
} else {
|
||||
const a = takeCard(ctx.attacker, mine);
|
||||
const b = takeCard(ctx.defender, theirs);
|
||||
ctx.attacker.hand.push(b);
|
||||
ctx.defender.hand.push(a);
|
||||
}
|
||||
ctx.events.push({ type: "itemsSwapped", a: ctx.attacker.id, b: ctx.defender.id });
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user