95 lines
6.0 KiB
TypeScript
95 lines
6.0 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
import { applyAction, normalizeAutomaticGame, type Action, type Game } from '../game/engine';
|
|
import { nativeHomeworldArmy } from './fixture-homeworld-native-e1e2-modules';
|
|
import {
|
|
advanceSelectedDeckSkills, assertSelectedDeckSkillsInventory, createSelectedDeckSkillsProgramme,
|
|
createSelectedDeckSkillsRevivalCase, selectedDeckSkillsClean, selectedDeckSkillsPlayer,
|
|
selectedDeckSkillsQuote,
|
|
} from './fixture-selected-deck-skills';
|
|
|
|
const reload = (game: Game): Game => JSON.parse(JSON.stringify(game)) as Game;
|
|
|
|
void test('bare Basic Ix/CHOAM table deals native setup and continues a paid shipment without independent cards', () => {
|
|
const f = createSelectedDeckSkillsProgramme();
|
|
const owner = f.owner, guild = f.guild;
|
|
assert.equal(f.afterSetup.richeseCache!.length, 10, 'A seated Richese keeps its separate ten-card cache.');
|
|
// The real Ix card draft happens before any module technique.
|
|
assert.equal(f.started.setupStage, 'skillTreachery');
|
|
assert.ok(f.ixOffer && f.ixOffer.after.setupStage === 'leaderSkills');
|
|
assert.equal(f.ixOffer.after.leaderSkills!.assignments.length, 0);
|
|
// Real spice pays for a real shipment into the untouched Guild army.
|
|
const committed = selectedDeckSkillsPlayer(f.shipment.before, guild).forces['tueks_sietch:5'];
|
|
assert.equal(selectedDeckSkillsPlayer(f.shipment.before, owner).spice -
|
|
selectedDeckSkillsPlayer(f.shipment.after, owner).spice, 2);
|
|
assert.equal(selectedDeckSkillsPlayer(f.shipment.after, owner).forces['tueks_sietch:5'], 2);
|
|
assert.equal(selectedDeckSkillsPlayer(f.shipment.after, guild).forces['tueks_sietch:5'], committed);
|
|
// The trained Mentat really changes the battle total.
|
|
const quote = selectedDeckSkillsQuote(f.game);
|
|
assert.equal(quote.winner, owner);
|
|
assert.deepEqual(quote.leaderSkillBonuses.attacker.applied,
|
|
[{ skill: 'mentat', amount: 2, mode: 'skilled' }]);
|
|
assert.equal(quote.leaderSkillBonuses.defender.bonus, 0);
|
|
// The same position continues into the next real phase with real losses.
|
|
const done = advanceSelectedDeckSkills(f.game, g => !g.battle && selectedDeckSkillsClean(g), f.actions);
|
|
assert.equal(done.phase, 7);
|
|
assert.equal(selectedDeckSkillsPlayer(done, owner).forces['tueks_sietch:5'], 2, 'The winner keeps the territory.');
|
|
assert.equal(selectedDeckSkillsPlayer(done, guild).forces['tueks_sietch:5'], undefined);
|
|
assert.equal(selectedDeckSkillsPlayer(done, guild).tanks, selectedDeckSkillsPlayer(f.game, guild).tanks + committed);
|
|
assertSelectedDeckSkillsInventory(done);
|
|
});
|
|
|
|
void test('the same bare table with original Homeworlds ships out of its real native pools', () => {
|
|
const f = createSelectedDeckSkillsProgramme({ homeworlds: true });
|
|
const owner = f.owner, home = `homeworld:${selectedDeckSkillsPlayer(f.shipment.before, owner).faction}`;
|
|
assert.ok(f.afterSetup.homeworlds?.custody);
|
|
const before = nativeHomeworldArmy(f.shipment.before, owner, home);
|
|
const after = nativeHomeworldArmy(f.shipment.after, owner, home);
|
|
assert.equal(before.normal + before.elite - after.normal - after.elite, 2);
|
|
assert.equal(selectedDeckSkillsPlayer(f.shipment.before, owner).spice -
|
|
selectedDeckSkillsPlayer(f.shipment.after, owner).spice, 2);
|
|
assert.equal(selectedDeckSkillsPlayer(f.shipment.after, owner).forces['tueks_sietch:5'], 2);
|
|
assertSelectedDeckSkillsInventory(f.shipment.after);
|
|
const done = advanceSelectedDeckSkills(f.game, g => !g.battle && selectedDeckSkillsClean(g), f.actions);
|
|
assert.equal(done.phase, 7);
|
|
assertSelectedDeckSkillsInventory(done);
|
|
});
|
|
|
|
void test('a base-only table needs only its selected ordinary deck, even when no seat uses that deck', () => {
|
|
const f = createSelectedDeckSkillsProgramme({ factions: ['emperor', 'atreides', 'guild'], expansions: ['ecaz'] });
|
|
assert.equal(selectedDeckSkillsQuote(f.game).winner, f.owner);
|
|
const done = advanceSelectedDeckSkills(f.game, g => !g.battle && selectedDeckSkillsClean(g), f.actions);
|
|
assert.equal(done.phase, 7);
|
|
assertSelectedDeckSkillsInventory(done);
|
|
});
|
|
|
|
void test('a real battle death keeps the native Advanced-only foreign ghola guard on a bare table', () => {
|
|
const basic = createSelectedDeckSkillsRevivalCase({ advanced: false });
|
|
const advanced = createSelectedDeckSkillsRevivalCase({ advanced: true });
|
|
for (const f of [basic, advanced]) {
|
|
assert.equal(f.game.turn, 2);
|
|
assert.equal(f.game.phase, 4);
|
|
assert.equal(f.game.players.flatMap(p => p.leaders).filter(l => l.id === f.dead).length, 1);
|
|
assert.equal(f.game.leaderSkills!.assignments
|
|
.some(a => a.owner === f.tleilaxu && a.leader === f.trainer), false,
|
|
'The poisoned trainer returned its physical technique.');
|
|
assertSelectedDeckSkillsInventory(f.game);
|
|
}
|
|
const action: Action = { type: 'reviveForeignGhola', leader: basic.dead };
|
|
const untouched = reload(basic.game);
|
|
assert.throws(() => applyAction(basic.game, basic.tleilaxu, action), /advanced Tleilaxu/);
|
|
assert.deepEqual(basic.game, untouched, 'A Basic bare table pays nothing and revives nothing.');
|
|
const disc = selectedDeckSkillsPlayer(advanced.game, advanced.guild).leaders.find(l => l.id === advanced.dead)!;
|
|
const cost = Math.ceil(disc.strength / 2), purse = selectedDeckSkillsPlayer(advanced.game, advanced.tleilaxu).spice;
|
|
const revived = applyAction(reload(advanced.game), advanced.tleilaxu, action);
|
|
assert.equal(disc.dead, true, 'The caller-owned input keeps its own physical disc.');
|
|
assert.equal(selectedDeckSkillsPlayer(revived, advanced.guild).leaders.find(l => l.id === advanced.dead)!.dead, false);
|
|
assert.equal(selectedDeckSkillsPlayer(revived, advanced.guild).leaders.find(l => l.id === advanced.dead)!.gholaBy,
|
|
advanced.tleilaxu);
|
|
assert.equal(selectedDeckSkillsPlayer(revived, advanced.tleilaxu).spice, purse - cost);
|
|
assert.ok(!revived.response && !revived.pendingRevival);
|
|
assert.deepEqual(normalizeAutomaticGame(reload(revived)), revived,
|
|
'The Advanced bare selected-deck table keeps the foreign ghola custody authorized.');
|
|
assertSelectedDeckSkillsInventory(revived);
|
|
});
|