122 lines
6.1 KiB
TypeScript
122 lines
6.1 KiB
TypeScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { viewGame, type Game } from '../game/engine';
|
|
import { isAdvisor } from '../game/advisors';
|
|
import {
|
|
createRevivalTerrorFixture as fixture, commitRevivalTerror as commit,
|
|
actRevivalTerror as act, passRevivalTerror as pass, finishRevivalTerror as finish,
|
|
revivalTerrorPlayer as player, revivalTerrorInventory as inventory,
|
|
type RevivalTerrorFixture,
|
|
} from './fixture-revival-terror';
|
|
|
|
function enter(f: RevivalTerrorFixture) {
|
|
let g = commit(f.game, f).game;
|
|
if (g.decision?.kind === 'advisor') {
|
|
assert.equal(g.decision.shipment, f.entrant);
|
|
g = act(g, f.bg!, {type:'decision',accept:false});
|
|
}
|
|
assert.equal(player(g,f.entrant).forces[f.location],1);
|
|
assert.equal(g.homeworldRevivalReturn?.stage,'arrival');
|
|
return g;
|
|
}
|
|
function declineAmbassador(g: Game, f: RevivalTerrorFixture) {
|
|
assert.equal(g.decision?.kind,'ecazAmbassador');
|
|
assert.equal(g.decision?.player,f.ecaz);
|
|
return act(g,f.ecaz!,{type:'decision',event:g.pendingAmbassador!.event,decline:true});
|
|
}
|
|
function complete(g: Game, f: RevivalTerrorFixture) {
|
|
g = finish(g);
|
|
assert.equal(g.homeworldRevivalReturn?.stage,'complete');
|
|
assert.equal(g.pendingArrivalOverlap ?? null,null);
|
|
assert.equal(g.pendingArrivalReaction ?? null,null);
|
|
assert.equal(g.pendingTerrorEntry ?? null,null);
|
|
assert.equal(g.pendingAmbassador ?? null,null);
|
|
assert.equal(player(g,f.entrant).forces[f.location],1);
|
|
assert.equal(player(g,f.entrant).spice,player(f.beforeRequest,f.entrant).spice+1);
|
|
assert.equal(player(g,f.entrant).shipped,player(f.game,f.entrant).shipped);
|
|
assert.deepEqual(inventory(g),f.originalInventory);
|
|
return g;
|
|
}
|
|
|
|
for (const advanced of [false,true]) for (const ambassadorFirst of [false,true]) {
|
|
void test(`${advanced?'Advanced':'Basic'} native revived overlap: ${ambassadorFirst?'Ambassador':'Terror'} acts first and both declines finish original income`,()=>{
|
|
const f=fixture({advanced,ecaz:true,ambassadorFirst}), g=enter(f);
|
|
assert.equal(g.pendingArrivalOverlap?.first,ambassadorFirst?'ambassador':'terror');
|
|
assert.equal(g.pendingArrivalOverlap?.cause,'homeworldRevival');
|
|
assert.equal(g.pendingArrivalOverlap?.revivalArrival?.event,f.event);
|
|
const second=ambassadorFirst?declineAmbassador(g,f):act(g,f.owner,f.declineAction);
|
|
assert.equal(second.homeworldRevivalReturn?.stage,'arrival');
|
|
const result=complete(ambassadorFirst?act(second,f.owner,f.declineAction):declineAmbassador(second,f),f);
|
|
assert.ok(result.homeworldRevivalReturn!.ambassadors!.every(entry=>entry.completed));
|
|
assert.equal(result.moritaniTerror!.tokens.find(token=>token.id===f.token)!.status,'placed');
|
|
assert.equal(result.ecazAmbassadors!.tokens.find(token=>token.id===f.ambassadorToken)!.zone,'placed');
|
|
});
|
|
}
|
|
|
|
for (const ambassadorFirst of [false,true]) {
|
|
void test(`native revival Sabotage discard retains ${ambassadorFirst?'completed':'pending'} original Ambassador opportunity`,()=>{
|
|
const f=fixture({kind:'sabotage',ecaz:true,ambassadorFirst});
|
|
let g=enter(f);
|
|
if (ambassadorFirst) g=declineAmbassador(g,f);
|
|
g=act(g,f.owner,f.revealAction);
|
|
assert.equal(g.pendingTreacheryDiscard ?? null,null);
|
|
assert.equal(g.pendingTerrorEntry?.stage,'gift');
|
|
assert.equal(g.pendingTerrorEntry?.revivalArrival?.event,f.event);
|
|
assert.equal(g.discard.filter(card=>card.id===f.karama).length,1);
|
|
assert.equal(player(g,f.entrant).hand.length,0);
|
|
g=act(g,f.owner,{type:'decision',decline:true});
|
|
if (!ambassadorFirst) g=declineAmbassador(g,f);
|
|
complete(g,f);
|
|
});
|
|
}
|
|
|
|
for (const advanced of [false,true]) {
|
|
void test(`${advanced?'Advanced':'Basic'} native revived Sneak preserves original Ambassador then opens its own BG and Ambassador arrivals`,()=>{
|
|
const f=fixture({advanced,kind:'sneakAttack',bg:true,ecaz:true}), source=enter(f);
|
|
assert.equal(source.pendingArrivalOverlap?.first,'terror');
|
|
let g=act(source,f.owner,f.revealAction);
|
|
g=act(g,f.owner,{type:'decision',amount:1});
|
|
assert.equal(g.decision?.kind,'ecazAmbassador');
|
|
assert.equal(g.pendingAmbassador?.entrant,f.entrant);
|
|
assert.equal(g.pendingArrivalReaction?.[0].revivalSneakAdvisor,true);
|
|
assert.equal(player(g,f.owner).forces[f.location],1);
|
|
g=declineAmbassador(g,f);
|
|
assert.equal(g.decision?.kind,'advisor');
|
|
if(g.decision?.kind!=='advisor')throw Error('Expected native Sneak Spiritual control');
|
|
assert.equal(g.decision.shipment,f.owner);
|
|
assert.equal(g.decision.revivalEvent,f.event);
|
|
assert.equal(g.decision.revivalTerror?.owner,f.owner);
|
|
g=act(g,f.bg!,{type:'decision',accept:true,accompany:advanced,amount:1});
|
|
assert.equal(g.response?.kind,'advisor');
|
|
assert.equal(g.response?.spiritualAdvisorSource?.revivalEvent,f.event);
|
|
g=pass(g);
|
|
assert.equal(g.decision?.kind,'ecazAmbassador');
|
|
assert.equal(g.pendingAmbassador?.entrant,f.owner);
|
|
const child=g.homeworldRevivalReturn!.ambassadors!.at(-1)!;
|
|
assert.equal(child.terror?.owner,f.owner);
|
|
assert.equal(child.terror?.amount,1);
|
|
g=declineAmbassador(g,f);
|
|
g=complete(g,f);
|
|
assert.equal(player(g,f.owner).reserves,13);
|
|
assert.equal(player(g,f.bg!).forces[advanced?f.location:'polar_sink:0'],advanced?1:2);
|
|
if(advanced)assert.equal(isAdvisor(player(g,f.bg!),f.territory),true);
|
|
assert.deepEqual(g.homeworldRevivalReturn!.ambassadors!.map(entry=>entry.entrant),[f.entrant,f.owner]);
|
|
});
|
|
}
|
|
|
|
void test('native original Karama stops only the Sneak-derived free BG packet, retaining both committed groups',()=>{
|
|
const f=fixture({kind:'sneakAttack',bg:true});
|
|
let g=enter(f);
|
|
g=act(g,f.owner,f.revealAction);
|
|
g=act(g,f.owner,{type:'decision',amount:1});
|
|
assert.equal(g.decision?.kind,'advisor');
|
|
g=act(g,f.bg!,{type:'decision',accept:true,accompany:true,amount:1});
|
|
assert.equal(g.response?.spiritualAdvisorSource?.revivalEvent,f.event);
|
|
g=act(g,f.holder,f.cancelAction);
|
|
g=complete(g,f);
|
|
assert.equal(player(g,f.owner).forces[f.location],1);
|
|
assert.equal(player(g,f.bg!).forces[f.location],undefined);
|
|
assert.equal(g.discard.filter(card=>card.id===f.karama).length,1);
|
|
assert.equal(viewGame(g,f.entrant).terrorEntry,null);
|
|
});
|