131 lines
8.6 KiB
TypeScript
131 lines
8.6 KiB
TypeScript
import assert from 'node:assert/strict';
|
||
import test from 'node:test';
|
||
import { applyAction, viewGame } from '../game/engine';
|
||
import { weaponKills } from '../game/battle-cards';
|
||
import {
|
||
applyDiplomatContextStep, assertDiplomatContextCustody, copyDiplomatContextJourney,
|
||
createDiplomatContextJourney, diplomatContextPlayer, finishDiplomatContextJourney,
|
||
revealDiplomatContextJourney, settleDiplomatContextJourney,
|
||
} from './fixture-diplomat-context-journey';
|
||
|
||
void test('original native World vote closes, invader uses actual late Portable, and Diplomat copies only its poison role before typed custody settles', () => {
|
||
const fixture = createDiplomatContextJourney();
|
||
const { game, owner, opponent, trainer, enemyLeader, source, worthless, territory } = fixture;
|
||
assert.ok(source);
|
||
const shipment = fixture.shipments[0];
|
||
assert.equal(diplomatContextPlayer(shipment.before, opponent).spice -
|
||
diplomatContextPlayer(shipment.after, opponent).spice, 2);
|
||
assert.equal(diplomatContextPlayer(shipment.before, opponent).reserves -
|
||
diplomatContextPlayer(shipment.after, opponent).reserves, 3);
|
||
const worlds = viewGame(game, owner).homeworlds!.worlds!;
|
||
const nativeWorld = worlds.find(world => world.id === territory)!;
|
||
const salusa = worlds.find(world => world.id === 'homeworld:emperor:salusa')!;
|
||
assert.equal(nativeWorld.card, 'kaitain');
|
||
assert.deepEqual(nativeWorld.forces[owner], { normal: 15, elite: 0 });
|
||
assert.deepEqual(nativeWorld.forces[opponent], { normal: 3, elite: 0 });
|
||
assert.deepEqual(salusa.forces[owner], { normal: 0, elite: 5 });
|
||
|
||
const revealed = revealDiplomatContextJourney(fixture);
|
||
assert.equal(revealed.battle!.diplomatDefense, undefined);
|
||
assert.deepEqual(viewGame(revealed, owner).battle!.traitorVoters, [owner]);
|
||
assert.equal(revealed.battle!.plans[opponent].defense, null);
|
||
const nativeVoted = applyAction(revealed, owner, { type: 'traitorCall', call: false });
|
||
assert.equal(nativeVoted.decision?.kind, 'homeworldDefense');
|
||
assert.equal(nativeVoted.decision?.player, opponent);
|
||
assert.equal(nativeVoted.battle!.traitorCalls[owner], false);
|
||
assert.equal(nativeVoted.battle!.traitorCalls[opponent], undefined);
|
||
assert.ok(nativeVoted.decision?.kind === 'homeworldDefense');
|
||
const used = applyDiplomatContextStep(nativeVoted, { actor: opponent,
|
||
action: { type: 'decision', event: nativeVoted.decision.event, use: true } });
|
||
assert.equal(used.decision?.kind, 'diplomatDefense');
|
||
assert.equal(used.battle!.lateDefense![opponent], source);
|
||
assert.deepEqual(used.battle!.homeworldDefensePassed, [opponent]);
|
||
assert.equal(used.battle!.traitorCalls[owner], false);
|
||
assert.deepEqual(used.battle!.diplomatDefense!.cards, [worthless]);
|
||
assert.equal(used.battle!.diplomatDefense!.source, source);
|
||
assert.equal(used.battle!.diplomatDefense!.kind, 'snooper');
|
||
assert.deepEqual(used.battle!.plans, revealed.battle!.plans);
|
||
const copied = copyDiplomatContextJourney(used, worthless);
|
||
assert.throws(() => applyAction(copied, owner, { type: 'traitorCall', call: false }),
|
||
'The submitted native-only vote cannot be reopened after the copied defense.');
|
||
const settled = settleDiplomatContextJourney(copied);
|
||
assert.equal(settled.lastBattleContext!.winner, owner);
|
||
assert.equal(diplomatContextPlayer(settled, owner).leaders.find(l => l.id === trainer)!.dead, false);
|
||
assert.equal(diplomatContextPlayer(settled, opponent).leaders.find(l => l.id === enemyLeader)!.dead, false);
|
||
const finalWorlds = viewGame(settled, owner).homeworlds!.worlds!;
|
||
assert.deepEqual(finalWorlds.find(world => world.id === territory)!.forces[owner], nativeWorld.forces[owner]);
|
||
assert.deepEqual(finalWorlds.find(world => world.id === 'homeworld:emperor:salusa')!.forces[owner],
|
||
{ normal: 0, elite: 5 }, 'Copying a defense never spends or relabels Sardaukar.');
|
||
assert.deepEqual(finalWorlds.find(world => world.id === territory)!.forces[opponent] ?? { normal: 0, elite: 0 },
|
||
{ normal: 0, elite: 0 });
|
||
assert.equal(diplomatContextPlayer(settled, opponent).tanks, 3);
|
||
assert.equal(diplomatContextPlayer(settled, owner).elites!.tanks, 0);
|
||
assert.equal(settled.discard.filter(card => card.id === worthless).length, 1);
|
||
assert.equal(settled.discard.filter(card => card.id === source).length, 1);
|
||
assert.equal(diplomatContextPlayer(settled, owner).hand.some(card => card.id === worthless), false);
|
||
assertDiplomatContextCustody(settled);
|
||
assert.equal(finishDiplomatContextJourney(settled).phase, 7);
|
||
});
|
||
|
||
void test('an actually earned Carthag layers its own Snooper onto a copied printed Shield while preserving physical custody', () => {
|
||
const fixture = createDiplomatContextJourney({ kind: 'held-carthag' });
|
||
const { owner, opponent, trainer, enemyLeader, worthless, source, firstMentat } = fixture;
|
||
assert.ok(firstMentat && source);
|
||
assert.equal(firstMentat.before.strongholdCards!.owners.carthag, null);
|
||
assert.equal(firstMentat.before.players.find(p => p.id === owner)!.forces['carthag:11'], 1);
|
||
assert.equal(firstMentat.after.strongholdCards!.owners.carthag, owner);
|
||
assert.equal(fixture.game.strongholdCards!.owners.carthag, owner);
|
||
assert.equal(fixture.game.players.find(p => p.id === opponent)!.forces['carthag:11'], 1);
|
||
for (const shipment of fixture.shipments) {
|
||
assert.equal(diplomatContextPlayer(shipment.before, shipment.step.actor).reserves -
|
||
diplomatContextPlayer(shipment.after, shipment.step.actor).reserves, 1);
|
||
assert.equal(diplomatContextPlayer(shipment.before, shipment.step.actor).spice -
|
||
diplomatContextPlayer(shipment.after, shipment.step.actor).spice, 1);
|
||
}
|
||
assert.equal(weaponKills(
|
||
diplomatContextPlayer(fixture.game, opponent).hand.find(card => card.id === fixture.enemyWeapon),
|
||
diplomatContextPlayer(fixture.game, opponent).hand.find(card => card.id === source),
|
||
), true, 'The exact copied printed Shield by itself does not protect against the exact poison weapon.');
|
||
const revealed = revealDiplomatContextJourney(fixture);
|
||
assert.equal(revealed.battle!.diplomatDefense!.source, source);
|
||
assert.equal(revealed.battle!.diplomatDefense!.kind, 'shield');
|
||
const settled = settleDiplomatContextJourney(copyDiplomatContextJourney(revealed, worthless));
|
||
assert.equal(settled.lastBattleContext!.winner, owner);
|
||
assert.equal(diplomatContextPlayer(settled, owner).leaders.find(l => l.id === trainer)!.dead, false,
|
||
'Shield alone cannot stop poison: the holder’s original Carthag independently supplies Snooper.');
|
||
assert.equal(diplomatContextPlayer(settled, opponent).leaders.find(l => l.id === enemyLeader)!.dead, false);
|
||
assert.equal(diplomatContextPlayer(settled, owner).forces['carthag:11'], 1);
|
||
assert.equal(diplomatContextPlayer(settled, owner).tanks, 0);
|
||
assert.equal(diplomatContextPlayer(settled, owner).elites!.tanks, 0);
|
||
assert.equal(diplomatContextPlayer(settled, opponent).tanks, 1);
|
||
assert.equal(settled.strongholdCards!.owners.carthag, owner);
|
||
assert.equal(settled.discard.filter(card => card.id === worthless).length, 1);
|
||
assert.equal(settled.discard.filter(card => card.id === source).length, 1);
|
||
assert.equal(diplomatContextPlayer(settled, owner).hand.some(card => card.id === source), false);
|
||
assertDiplomatContextCustody(settled);
|
||
assert.equal(finishDiplomatContextJourney(settled).phase, 7);
|
||
});
|
||
|
||
void test('an earned Carthag with only a physical Worthless and no opposing actual defense supplies neither a copy source nor poison protection', () => {
|
||
const fixture = createDiplomatContextJourney({ kind: 'empty-carthag' });
|
||
const { owner, opponent, trainer, worthless } = fixture;
|
||
assert.equal(fixture.firstMentat!.after.strongholdCards!.owners.carthag, owner);
|
||
const revealed = revealDiplomatContextJourney(fixture);
|
||
assert.equal(revealed.battle!.plans[owner].weapon, worthless);
|
||
assert.equal(revealed.battle!.plans[owner].defense, null);
|
||
assert.equal(revealed.battle!.plans[opponent].defense, null);
|
||
assert.equal(revealed.battle!.diplomatDefense, undefined);
|
||
assert.notEqual(revealed.decision?.kind, 'diplomatDefense');
|
||
const settled = settleDiplomatContextJourney(revealed);
|
||
assert.equal(diplomatContextPlayer(settled, owner).leaders.find(l => l.id === trainer)!.dead, true);
|
||
assert.equal(settled.lastBattleContext!.winner, opponent);
|
||
assert.equal(settled.strongholdCards!.owners.carthag, owner,
|
||
'Battle occupancy does not replace the original END Mentat card owner.');
|
||
assert.equal(diplomatContextPlayer(settled, owner).forces['carthag:11'] ?? 0, 0);
|
||
assert.equal(diplomatContextPlayer(settled, owner).tanks, 1);
|
||
assert.equal(diplomatContextPlayer(settled, owner).elites!.tanks, 0);
|
||
assert.equal(settled.discard.filter(card => card.id === worthless).length, 1);
|
||
assertDiplomatContextCustody(settled);
|
||
});
|
||
|