104 lines
6.2 KiB
TypeScript
104 lines
6.2 KiB
TypeScript
import assert from 'node:assert/strict';
|
||
import test from 'node:test';
|
||
import {viewGame, type Game} from '../game/engine';
|
||
import {holdOccupyCardsTraitor} from './fixture-ecaz-occupy-cards';
|
||
import {
|
||
STONE_CARD, MIRROR_CARD, stoneHarassOccupyFixture, stoneHarassPlayer,
|
||
takeStoneHarassCard, sealStoneHarassPlans, finishStoneHarassBattle, traitorIdentities,
|
||
} from './fixture-stone-harass';
|
||
|
||
const reinforcement = 'ecaz-reinforcements';
|
||
function prepare({lead = 'ecaz', holder = 'lead', mirror = false, opponentForces = 6,
|
||
variableDial = 1}: {lead?: 'ecaz' | 'ally'; holder?: 'lead' | 'opponent'; mirror?: boolean;
|
||
opponentForces?: number; variableDial?: number} = {}) {
|
||
const source = stoneHarassOccupyFixture({lead, harass: false,
|
||
stone: mirror ? 'opponent' : 'harass', mirror, variableDial,
|
||
support: variableDial, ecazForces: 5, allyForces: 4, opponentForces});
|
||
const owner = holder === 'lead' ? source.lead : source.opponent;
|
||
takeStoneHarassCard(source.game, owner, reinforcement);
|
||
const plan = source.plans.find(step => step.actor === owner)!.action;
|
||
if (plan.weapon) plan.defense = reinforcement;
|
||
else plan.weapon = reinforcement;
|
||
return {source, owner};
|
||
}
|
||
const count = (game: Game, id: string) => game.discard.filter(card => card.id === id).length;
|
||
const has = (game: Game, owner: string, id: string) => stoneHarassPlayer(game, owner).hand.some(card => card.id === id);
|
||
|
||
void test('same-plan Stone and Reinforcements pay only the actual fixed or variable card user without adding physical strength', () => {
|
||
for (const lead of ['ecaz', 'ally'] as const) for (const winning of [false, true]) {
|
||
const {source, owner} = prepare({lead, opponentForces: winning ? 4 : 6});
|
||
const before = structuredClone(source.game);
|
||
const done = finishStoneHarassBattle(sealStoneHarassPlans(source), {mode: 'ignore'});
|
||
assert.equal(done.lastBattleContext?.winner, winning ? source.lead : source.opponent,
|
||
'the five undialed coalition counters beat four but lose to six; Reinforcements adds no tokens');
|
||
const delta = (id: string, field: 'reserves' | 'tanks') =>
|
||
stoneHarassPlayer(done, id)[field] - stoneHarassPlayer(before, id)[field];
|
||
assert.equal(delta(owner, 'reserves'), -3);
|
||
assert.equal(delta(owner === source.ecaz ? source.ally : source.ecaz, 'reserves'), 0);
|
||
assert.equal(delta(source.ecaz, 'tanks'), winning ? lead === 'ecaz' ? 6 : 3 : lead === 'ecaz' ? 8 : 5);
|
||
assert.equal(delta(source.ally, 'tanks'), winning ? lead === 'ally' ? 4 : 1 : lead === 'ally' ? 7 : 4);
|
||
assert.equal(stoneHarassPlayer(done, source.ecaz).forces[source.location] ?? 0, winning ? 2 : 0);
|
||
assert.equal(stoneHarassPlayer(done, source.ally).forces[source.location] ?? 0, winning ? 3 : 0);
|
||
assert.equal(count(done, reinforcement), 1);
|
||
assert.equal(has(done, source.lead, STONE_CARD), winning);
|
||
assert.equal(count(done, STONE_CARD), winning ? 0 : 1);
|
||
assert.equal(done.battle, null);
|
||
}
|
||
});
|
||
|
||
void test('opposing Reinforcements cannot turn a five-token army into seven and still pays its original elite reserve cost', () => {
|
||
for (const lead of ['ecaz', 'ally'] as const) {
|
||
const {source, owner} = prepare({lead, holder: 'opponent', variableDial: 0, opponentForces: 5});
|
||
const player = stoneHarassPlayer(source.game, owner);
|
||
player.reserves = 3; player.tanks = 12;
|
||
player.elites!.reserves = 1; player.elites!.tanks = 4;
|
||
const cost = viewGame(source.game, owner).battle!.reinforcements!;
|
||
assert.equal(cost.normal, 2); assert.equal(cost.elite, 1);
|
||
const done = finishStoneHarassBattle(sealStoneHarassPlans(source), {mode: 'ignore'});
|
||
assert.equal(done.lastBattleContext?.winner, source.lead, 'six physical undialed counters beat five');
|
||
assert.equal(stoneHarassPlayer(done, owner).reserves, 0);
|
||
assert.equal(stoneHarassPlayer(done, owner).tanks, 20);
|
||
assert.equal(stoneHarassPlayer(done, owner).elites!.tanks, 5);
|
||
assert.equal(count(done, reinforcement), 1);
|
||
assert.equal(has(done, source.lead, STONE_CARD), true);
|
||
}
|
||
});
|
||
|
||
void test('the independent copied Stone mode preserves Mirror custody and the reserve fee', () => {
|
||
for (const lead of ['ecaz', 'ally'] as const) {
|
||
const {source, owner} = prepare({lead, mirror: true, variableDial: 0, opponentForces: 5});
|
||
const beforeReserves = stoneHarassPlayer(source.game, owner).reserves;
|
||
const done = finishStoneHarassBattle(sealStoneHarassPlans(source),
|
||
{modes: {[source.lead]: 'kill', [source.opponent]: 'ignore'}});
|
||
assert.equal(done.lastBattleContext?.winner, source.lead);
|
||
assert.equal(stoneHarassPlayer(done, owner).reserves, beforeReserves - 3);
|
||
assert.equal(has(done, source.lead, MIRROR_CARD), true);
|
||
assert.equal(count(done, MIRROR_CARD), 0);
|
||
assert.equal(count(done, STONE_CARD), 1);
|
||
assert.equal(count(done, reinforcement), 1);
|
||
for (const actor of [source.lead, source.opponent])
|
||
assert.equal(stoneHarassPlayer(done, actor).leaders.find(leader => leader.id === source.leaders[actor])!.dead, true);
|
||
}
|
||
});
|
||
|
||
void test('opposing successful Traitor overrides Stone kill but does not waive the Reinforcements reserve cost', () => {
|
||
for (const lead of ['ecaz', 'ally'] as const) {
|
||
const {source, owner} = prepare({lead, opponentForces: 4});
|
||
const physical = traitorIdentities(source.game);
|
||
const leader = stoneHarassPlayer(source.game, source.lead).leaders.find(leader =>
|
||
!leader.dead && !leader.usedAt && physical.has(leader.id))!;
|
||
assert.ok(leader);
|
||
source.plans.find(step => step.actor === source.lead)!.action.leader = leader.id;
|
||
holdOccupyCardsTraitor(source.game, source.opponent, leader.id);
|
||
const before = stoneHarassPlayer(source.game, owner).reserves;
|
||
const done = finishStoneHarassBattle(sealStoneHarassPlans(source), {mode: 'kill', traitors: [source.opponent]});
|
||
assert.equal(done.lastBattleContext?.result, 'traitor');
|
||
assert.equal(done.lastBattleContext?.winner, source.opponent);
|
||
assert.equal(stoneHarassPlayer(done, owner).reserves, before - 3);
|
||
assert.equal(stoneHarassPlayer(done, source.opponent).leaders.find(candidate =>
|
||
candidate.id === source.leaders[source.opponent])!.dead, false, 'the defeated Stone attack cannot kill the winning caller’s leader');
|
||
assert.equal(count(done, reinforcement), 1);
|
||
assert.equal(count(done, STONE_CARD), 1);
|
||
}
|
||
});
|