74 lines
4.7 KiB
TypeScript
74 lines
4.7 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
import { viewGame } from '../game/engine';
|
|
import { SEMUTA_DRUG_ID } from '../game/semuta-drug';
|
|
import { passArrivalOrder, passCurrentSemuta } from './fixture-semuta-arrival-overlap';
|
|
import { act, player, semutaDiscoverySkillsOverlapFixture } from './semuta-discovery-skills-overlap-fixture';
|
|
|
|
// Select a legal Ambassador cohort and prior card acquisitions at the clean
|
|
// pre-arrival boundary. The shipment, Ambassador grant, discard and Terror
|
|
// continuation are all original player actions, never installed as receipts.
|
|
for (const effect of ['choam', 'ixians'] as const) for (const outcome of ['claim', 'pass'] as const)
|
|
void test(`Basic Discovery and Skills: ${effect} Ambassador ${outcome} resumes original Terror`, () => {
|
|
const f = semutaDiscoverySkillsOverlapFixture(false);
|
|
let game = f.game;
|
|
const ambassadors = game.ecazAmbassadors!;
|
|
const previous = ambassadors.tokens.find(t => t.id === f.token)!;
|
|
const marker = ambassadors.tokens.find(t => t.effect === effect)!;
|
|
if (!ambassadors.cohort.includes(marker.id)) {
|
|
const replacedId = ambassadors.cohort[0];
|
|
const replaced = ambassadors.tokens.find(t => t.id === replacedId)!;
|
|
assert.equal(replaced.zone, 'supply');
|
|
replaced.zone = 'pool'; marker.zone = 'supply';
|
|
ambassadors.cohort[0] = marker.id;
|
|
}
|
|
assert.equal(previous.zone, 'placed'); assert.equal(marker.zone, 'supply');
|
|
previous.zone = 'supply'; previous.location = null;
|
|
marker.zone = 'placed'; marker.location = f.destination;
|
|
const cache = game.richeseCache!;
|
|
const semutaIndex = cache.findIndex(card => card.id === SEMUTA_DRUG_ID);
|
|
assert.ok(semutaIndex >= 0);
|
|
player(game, 'richese').hand.push(cache.splice(semutaIndex, 1)[0]);
|
|
const owner = player(game, 'ecaz');
|
|
while (owner.hand.length < 2) { const card = game.deck.shift(); assert.ok(card); owner.hand.push(card); }
|
|
const chosen = owner.hand.slice(0, effect === 'choam' ? 2 : 1).map(card => card.id);
|
|
game = passArrivalOrder(act(game, 'guild', f.shipment));
|
|
game = f.advance(game, state => !!state.pendingAmbassador || !!state.pendingTerrorEntry);
|
|
assert.equal(game.pendingArrivalOverlap?.first, 'ambassador');
|
|
assert.equal(game.pendingAmbassador?.token, marker.id);
|
|
game = act(game, 'ecaz', {type:'decision', event:game.pendingAmbassador!.event,
|
|
trigger:true, beneficiary:'ecaz', choice:'effect'});
|
|
assert.equal(game.pendingAmbassador?.stage, 'cards');
|
|
const balance = player(game, 'ecaz').spice;
|
|
const deckSize = game.deck.length, replacement = game.deck[0]?.id;
|
|
game = act(game, 'ecaz', {type:'decision', event:game.pendingAmbassador!.event, cards:chosen});
|
|
assert.equal(game.pendingTreacheryDiscard?.continuation.kind, 'ambassador');
|
|
assert.equal(game.pendingTreacheryDiscard?.reaction?.stage, 'offer');
|
|
assert.equal(game.pendingArrivalOverlap?.stage, 'first');
|
|
assert.deepEqual(viewGame(game, 'guild').semutaReaction!.candidates, []);
|
|
assert.deepEqual(viewGame(game, 'richese').semutaReaction!.candidates, []);
|
|
assert.equal(player(game, 'ecaz').spice, balance + (effect === 'choam' ? 6 : 0));
|
|
assert.equal(game.deck.length, deckSize, 'The Ixian replacement must wait for the discard reaction');
|
|
if (outcome === 'claim') {
|
|
const event = game.pendingTreacheryDiscard!.batch.event;
|
|
game = act(game, 'richese', {type:'semutaCommit', event});
|
|
if (game.pendingTreacheryDiscard?.reaction?.stage === 'select') {
|
|
assert.deepEqual(viewGame(game, 'richese').semutaReaction!.candidates.map(card => card.id), chosen);
|
|
assert.deepEqual(viewGame(game, 'guild').semutaReaction!.candidates, []);
|
|
game = act(game, 'richese', {type:'semutaSelect', event, card:chosen[0]});
|
|
}
|
|
assert.ok(player(game, 'richese').hand.some(card => card.id === chosen[0]));
|
|
} else game = passCurrentSemuta(game);
|
|
assert.equal(player(game, 'ecaz').spice, balance + (effect === 'choam' ? 6 : 0));
|
|
assert.equal(game.deck.length, deckSize - Number(effect === 'ixians'));
|
|
if (effect === 'ixians') assert.ok(player(game, 'ecaz').hand.some(card => card.id === replacement));
|
|
assert.equal(game.pendingArrivalOverlap?.stage, 'second');
|
|
assert.equal(game.pendingTerrorEntry?.token, f.terror);
|
|
game = act(game, 'moritani', {type:'decision', reveal:true});
|
|
game = act(game, 'moritani', {type:'decision', choice:'spice'});
|
|
assert.equal(game.pendingArrivalOverlap ?? null, null);
|
|
assert.equal(game.pendingTreacheryDiscard ?? null, null);
|
|
assert.equal(player(game, 'guild').forces[`${f.destination}:${f.sector}`], 1);
|
|
assert.equal(game.moritaniTerror!.tokens.find(token => token.id === f.terror)?.status, 'removed');
|
|
});
|