Reflected Swap Meet: the reflector chooses the trade (rev 27)
'FULL REFLECTION lets the other player decide which objects, if any, will be swapped' — the reflector's choice now rides their counteract (params.cardId, reflector's item first), resolved with the roles swapped when the reflection settles; 'none' or no choice trades nothing. The client walks the reflector through the same give-and-take picker, with a 'swap nothing' refusal. Also from room XRT7: the trade picker now honors each room's own rules revision (older rooms trade only object-typed cards), so it can no longer offer a wizardblade a rev-25 engine will quietly refuse — and a fizzled swap finally says so in the log instead of vanishing. 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
3aef6694f3
commit
2bb2ca62d8
@@ -445,3 +445,50 @@ describe("swap meet trades treasures too", () => {
|
||||
expect(state.treasures.find((t) => t.id === t2.id)!.carriedBy).toBe(attacker);
|
||||
});
|
||||
});
|
||||
|
||||
describe("full reflection hands the swap meet choice to the reflector (rev 27)", () => {
|
||||
function reflectedSwapRig(reflectorChoice?: string) {
|
||||
let { state } = createGame({ playerIds: ["alice", "bob"], seed: 42, sets: ["basic", "expansion1"], deckRev: 27 });
|
||||
state = toRound2(state);
|
||||
const { attacker, defender } = faceOff(state);
|
||||
const a = state.players.find((p) => p.id === attacker)!;
|
||||
// The caster carries a treasure and offers a dagger; the reflector may
|
||||
// instead demand the trade of their own choosing.
|
||||
const t = state.treasures.find((t) => t.owner === attacker)!;
|
||||
t.position = null; t.carriedBy = attacker; a.carriedTreasureId = t.id;
|
||||
const sm = giveCard(state, attacker, "swap-meet");
|
||||
giveCard(state, attacker, "dagger", "D", 1);
|
||||
giveCard(state, defender, "full-reflection", "FR", 0);
|
||||
giveCard(state, defender, "large-rock", "R", 1);
|
||||
state = must(state, attacker, {
|
||||
type: "cast", instanceId: sm.instanceId,
|
||||
target: { kind: "player", playerId: defender },
|
||||
params: { cardId: "dagger;large-rock" },
|
||||
});
|
||||
state = must(state, defender, {
|
||||
type: "counteract", instanceId: "full-reflection#FR",
|
||||
...(reflectorChoice ? { params: { cardId: reflectorChoice } } : {}),
|
||||
});
|
||||
state = must(state, attacker, { type: "pass" });
|
||||
return { state, attacker, defender, treasureId: t.id };
|
||||
}
|
||||
|
||||
it("the reflector rewrites the trade: their rock for the caster's treasure", () => {
|
||||
const { state, attacker, defender, treasureId } = reflectedSwapRig("large-rock;treasure");
|
||||
const d = state.players.find((p) => p.id === defender)!;
|
||||
expect(d.carriedTreasureId).toBe(treasureId);
|
||||
expect(state.players.find((p) => p.id === attacker)!.hand.some((c) => c.cardId === "large-rock")).toBe(true);
|
||||
expect(state.players.find((p) => p.id === attacker)!.carriedTreasureId).toBeNull();
|
||||
});
|
||||
|
||||
it("the reflector may decline: 'none' trades nothing", () => {
|
||||
const { state, attacker, defender, treasureId } = reflectedSwapRig("none");
|
||||
expect(state.players.find((p) => p.id === attacker)!.carriedTreasureId).toBe(treasureId);
|
||||
expect(state.players.find((p) => p.id === defender)!.hand.some((c) => c.cardId === "large-rock")).toBe(true);
|
||||
});
|
||||
|
||||
it("a bare full reflection with no choice trades nothing", () => {
|
||||
const { state, attacker } = reflectedSwapRig(undefined);
|
||||
expect(state.players.find((p) => p.id === attacker)!.hand.some((c) => c.cardId === "dagger")).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user