74 lines
4.8 KiB
TypeScript
74 lines
4.8 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
import { applyAction, viewGame, type Game } from '../game/engine';
|
|
import { advanceNativeLeaderless, basicNativeLeaderlessFixture, nativeLeaderlessClean,
|
|
nativeLeaderlessPlayer as player } from './fixture-basic-native-early-leaderless';
|
|
import { advanceBasicSemutaAmbassador, basicSemutaAmbassadorInventory } from './fixture-basic-semuta-ambassador';
|
|
|
|
const announce = (game: Game, id: string) => game.log.filter(entry =>
|
|
entry.text === `${player(game, id).name} has no available leader or Cheap Hero for this battle.`);
|
|
|
|
/** Public setup, real movement and battle choice come from the native fixture.
|
|
* The tests stage only prior leader use/death and conserved physical cards. */
|
|
void test('Basic Richese keeps its shared preparation when a battle leader is dead', () => {
|
|
const fixture = basicNativeLeaderlessFixture('richese', {dead: true, ghola: true});
|
|
let game = advanceNativeLeaderless(fixture.game, state => !!state.battle && nativeLeaderlessClean(state) &&
|
|
!state.battle.preparation && !!state.battle.preLeader && !state.battle.preLeader.closed);
|
|
const battle = game.battle!;
|
|
assert.equal(battle.basicEarlyLeaderlessVersion, 1);
|
|
assert.equal(battle.basicDeadLeaderPreparation, true);
|
|
assert.deepEqual(battle.preLeader!.ready, [], 'Richese Sapho preparation keeps both combatants involved');
|
|
assert.equal(announce(game, 'richese').length, 0);
|
|
const event = battle.preLeader!.event;
|
|
game = applyAction(game, 'richese', {type:'battlePreparationReady', event});
|
|
assert.equal(game.battle!.preLeader!.closed, false);
|
|
assert.equal(announce(game, 'richese').length, 0);
|
|
game = applyAction(game, 'guild', {type:'battlePreparationReady', event});
|
|
assert.equal(game.battle!.preLeader!.closed, true);
|
|
assert.equal(announce(game, 'richese').length, 1);
|
|
const richese = player(game, 'richese'), ghola = richese.hand.find(card => card.effect === 'ghola');
|
|
const leader = richese.leaders.find(item => item.dead); assert.ok(ghola && leader);
|
|
game = applyAction(game, 'richese', {type:'card', card:ghola.id, leader:leader.id});
|
|
game = advanceBasicSemutaAmbassador(game, state => !!state.battle && nativeLeaderlessClean(state));
|
|
assert.equal(player(game, 'richese').leaders.find(item => item.id === leader.id)!.dead, false);
|
|
assert.ok(!(viewGame(game, 'guild').battle!.earlyLeaderless ?? []).includes('richese'));
|
|
});
|
|
|
|
void test('Basic Distrans receipt of a Cheap Hero corrects an unsealed early announcement', () => {
|
|
const fixture = basicNativeLeaderlessFixture('richese');
|
|
let game = structuredClone(fixture.beforeBattle);
|
|
const recipient = player(game, 'guild'), donor = player(game, 'emperor');
|
|
for (const leader of recipient.leaders) {
|
|
assert.ok(!leader.dead && !leader.capturedBy);
|
|
leader.usedAt = fixture.territory === 'carthag' ? 'arrakeen' : 'carthag';
|
|
}
|
|
// Keep the recipient below its hand limit with no existing Hero, then move
|
|
// the original Distrans and Hero into the donor's hand without creating a card.
|
|
for (let i = recipient.hand.length - 1; i >= 0; i--)
|
|
if (recipient.hand[i].kind === 'hero' || recipient.hand.length >= 4)
|
|
game.deck.push(...recipient.hand.splice(i, 1));
|
|
while (donor.hand.length > 2) game.deck.push(donor.hand.pop()!);
|
|
const distransIndex = game.richeseCache!.findIndex(card => card.effect === 'distrans');
|
|
assert.ok(distransIndex >= 0);
|
|
const distrans = game.richeseCache!.splice(distransIndex, 1)[0];
|
|
const heroIndex = game.deck.findIndex(card => card.kind === 'hero'); assert.ok(heroIndex >= 0);
|
|
const hero = game.deck.splice(heroIndex, 1)[0];
|
|
donor.hand.push(distrans, hero);
|
|
const inventory = basicSemutaAmbassadorInventory(game);
|
|
game = applyAction(game, fixture.chooser, fixture.choose);
|
|
game = advanceNativeLeaderless(game, state => !!state.battle && nativeLeaderlessClean(state) &&
|
|
!state.battle.preparation && (!state.battle.preLeader || state.battle.preLeader.closed));
|
|
assert.ok(viewGame(game, 'richese').battle!.earlyLeaderless?.includes('guild'));
|
|
assert.equal(announce(game, 'guild').length, 1);
|
|
assert.equal(viewGame(game, 'emperor').distrans?.blocked, null);
|
|
game = applyAction(game, 'emperor', {type:'card', card:distrans.id, target:'guild', give:hero.id});
|
|
game = advanceBasicSemutaAmbassador(game, state => !!state.battle && nativeLeaderlessClean(state));
|
|
assert.ok(player(game, 'guild').hand.some(card => card.id === hero.id));
|
|
assert.ok(!(viewGame(game, 'richese').battle!.earlyLeaderless ?? []).includes('guild'));
|
|
assert.equal(game.log.filter(entry => entry.text ===
|
|
`${player(game, 'guild').name} now has an available Cheap Hero for this battle.`).length, 1);
|
|
assert.deepEqual(basicSemutaAmbassadorInventory(game), inventory);
|
|
game = applyAction(game, 'guild', {type:'battlePlan', leader:hero.id, dial:0, support:0, weapon:null, defense:null});
|
|
assert.equal(game.battle!.plans.guild.leader, hero.id);
|
|
});
|