import assert from 'node:assert/strict'; import test from 'node:test'; import { applyAction, createGame, joinGame, newPlayer, viewGame, type Action, type Game } from '../game/engine'; import { baseDeck } from '../game/cards'; import { LEADER_SKILL_CARDS } from '../game/leader-skill-cards'; import { validateLeaderSkills } from '../game/leader-skills'; import { validateDiscoveryState } from '../game/discoveries'; import { validateNexusCards } from '../game/nexus-cards'; import { forceRevivalRemaining } from '../game/revival'; import { ownedTech } from '../game/tech-tokens'; import { DIFFICULTIES } from '../game/bot-profiles'; import { botActions } from '../game/bots'; import { quoteClassicSkillsStrongholdBattle } from './fixture-classic-skills-stronghold'; import { advanceDiscoveryClassicNexusSkills, closeDiscoveryClassicNexusSkills, createDiscoveryClassicNexusSkillsFixture, discoveryClassicNexusSkillsClean, discoveryClassicNexusSkillsPlayer as player, enterDiscoveryClassicNexusSkills, finishDiscoveryClassicNexusSkillsBattle, initializeDiscoveryClassicNexusSkillsSetup, openDiscoveryClassicNexusSkillsBattle, revealDiscoveryClassicNexusSkillsBattle, stepDiscoveryClassicNexusSkills, } from './fixture-discovery-classic-nexus-skills'; import { finishClassicDiscoveryNexusMentat, formClassicDiscoveryNexusAlliance, openClassicDiscoveryNexusAlliance, } from './fixture-discovery-classic-nexus'; function inventory(game: Game): void { validateLeaderSkills(game.leaderSkills!, game.players); validateDiscoveryState(game.discoveries!); validateNexusCards(game.nexusCards!.cards!, game.players); assert.deepEqual([...game.leaderSkills!.deck, ...Object.values(game.leaderSkills!.offers).flatMap(offer => offer.cards), ...game.leaderSkills!.assignments.map(assignment => assignment.skill)].sort(), LEADER_SKILL_CARDS.map(card => card.id).sort()); assert.deepEqual([...game.deck, ...game.discard, ...game.players.flatMap(seat => seat.hand)].map(card => card.id).sort(), baseDeck().map(card => card.id).sort()); for (const seat of game.players) { assert.equal(seat.reserves + seat.tanks + Object.values(seat.forces).reduce((sum, amount) => sum + amount, 0), 20); if (seat.elites) assert.equal(seat.elites.reserves + seat.elites.tanks + Object.values(seat.elites.forces).reduce((sum, amount) => sum + amount, 0), seat.faction === 'emperor' ? 5 : 3); } assert.ok(!(game.discoveryEntry && game.nexusCards!.phase?.stage === 'drawing')); assert.ok(!(game.greatMaker?.stage === 'vote' && game.nexusCards!.phase?.stage === 'drawing')); } function reject(game: Game, actor: string, action: Action): void { const before = structuredClone(game); assert.throws(() => applyAction(game, actor, action)); assert.deepEqual(game, before); } void test('original classic all14/all12/Discovery setup trains before traitors and reaches physical next-turn entry at Basic/Advanced two through six seats', () => { for (const advanced of [false, true]) for (const seats of [2, 3, 4, 5, 6] as const) { const fixture = createDiscoveryClassicNexusSkillsFixture({ advanced, seats, tech: seats >= 3, strongholds: advanced }); assert.equal(fixture.setup.status, 'setup'); assert.equal(fixture.setup.leaderSkills!.assignments.length, 0); assert.equal(fixture.setup.spiceDeck.length, 28); assert.equal(fixture.setup.discoveries!.tokens.length, 8); assert.ok(fixture.setup.players.every(seat => !seat.traitors.length && !seat.traitorChoices.length)); assert.equal(fixture.trained.leaderSkills!.assignments.length, seats); const trainingSteps = fixture.actions.filter(step => step.action.type === 'leaderSkill'); assert.equal(trainingSteps.length, seats); const lastTraining = fixture.actions.findLastIndex(step => step.action.type === 'leaderSkill'); const firstTraitor = fixture.actions.findIndex(step => step.action.type === 'traitor'); assert.ok(firstTraitor > lastTraining, 'Actual training precedes the original Traitor selection.'); assert.equal(fixture.firstMentat.before.phase, 8); assert.equal(fixture.firstMentat.after.turn, 2); assert.equal(fixture.game.phase, 0); assert.equal(fixture.game.storm, fixture.firstMentat.before.storm); const before = player(fixture.game, fixture.owner), entered = enterDiscoveryClassicNexusSkills(fixture), after = player(entered, fixture.owner); assert.equal(after.forces['cistern:0'], 3); assert.equal(after.forces[fixture.source] ?? 0, 0); assert.equal(after.reserves, before.reserves); assert.equal(after.spice, before.spice); assert.equal(after.shipped, before.shipped); assert.equal(after.moved, before.moved); assert.deepEqual(entered.leaderSkills, fixture.game.leaderSkills); if (advanced) { assert.equal(entered.strongholdCards!.owners.arrakeen, fixture.owner); assert.equal(entered.strongholdCards!.claimedTurn, 1); } inventory(fixture.setup); inventory(entered); } }); void test('authenticated original lobby and undealt human setup retain actual offered training, original hands and actors without demanding a specific skill', () => { const lobby = createGame('HUMANCLASSICNXSKILLDISCOVERY', newPlayer('human-collector', 'Guild', 'guild'), true); joinGame(lobby, newPlayer('human-emperor', 'Emperor', 'emperor')); joinGame(lobby, newPlayer('human-fremen', 'Fremen', 'fremen')); const before = structuredClone(lobby); const setup = initializeDiscoveryClassicNexusSkillsSetup({ initial: lobby, tech: true, strongholds: true }); assert.deepEqual(lobby, before); const offered = structuredClone(setup); const fixture = createDiscoveryClassicNexusSkillsFixture({ initial: setup }); assert.deepEqual(setup, offered); assert.deepEqual(fixture.setup, offered); assert.deepEqual(fixture.game.players.map(seat => seat.id), ['human-collector', 'human-emperor', 'human-fremen']); for (const step of fixture.actions.filter(step => step.action.type === 'leaderSkill')) { assert.ok(offered.leaderSkills!.offers[step.actor].cards.some(skill => skill === step.action.skill)); assert.ok(offered.players.find(seat => seat.id === step.actor)!.leaders.some(leader => leader.id === step.action.leader)); } assert.deepEqual(fixture.setup.players.map(seat => seat.hand), offered.players.map(seat => seat.hand)); assert.equal(fixture.game.decision?.player, 'human-collector'); const closing = closeDiscoveryClassicNexusSkills(fixture); const originalTop = closing.beforeDraw.nexusCards!.cards!.deck[0]; assert.equal(closing.game.nexusCards!.cards!.hands[fixture.owner], originalTop, 'Human closing draw consumes the actual original top card, not a demanded Nexus identity.'); assert.equal(closing.game.nexusCards!.cards!.deck.length, closing.beforeDraw.nexusCards!.cards!.deck.length - 1); inventory(closing.game); assert.throws(() => createDiscoveryClassicNexusSkillsFixture({ initial: fixture.game })); }); void test('actual Great Maker losses and all votes precede both Advanced piles, settled alliance and the qualified closing draw', () => { const fixture = createDiscoveryClassicNexusSkillsFixture({ advanced: true, tech: true, strongholds: true }); const closing = closeDiscoveryClassicNexusSkills(fixture, 'emperor'); assert.equal(player(closing.vote, fixture.owner).tanks, 6); assert.equal(player(closing.vote, fixture.owner).forces[fixture.wormSource], undefined); assert.equal(player(closing.vote, fixture.owner).forces['cistern:0'], 3); assert.equal(closing.vote.nexusCards!.cards!.hands[fixture.owner], null); assert.notEqual(closing.vote.nexusCards!.phase?.stage, 'drawing'); assert.equal(closing.alliance.greatMaker?.stage, 'complete'); assert.equal(closing.beforeDraw.phase, 1); assert.equal(closing.beforeDraw.nexus, false); assert.deepEqual(closing.beforeDraw.nexusCards!.phase!.eligible, [fixture.owner]); assert.equal(closing.beforeDraw.spiceWindow, null); assert.equal(closing.beforeDraw.spiceResolution, null); assert.ok(closing.beforeDraw.spiceDiscard.flat().some(card => 'territory' in card && card.territory === 'broken_land')); assert.ok(closing.beforeDraw.spiceDiscard.flat().filter(card => 'territory' in card && card.territory === 'rock_outcroppings').length >= 1); assert.equal(closing.game.nexusCards!.cards!.hands[fixture.owner], 'emperor'); assert.equal(closing.game.phase, 2); assert.equal(closing.game.nexusCards!.phase!.stage, 'complete'); assert.deepEqual(closing.game.leaderSkills, fixture.game.leaderSkills); reject(closing.game, fixture.owner, closing.drawStep.action); inventory(closing.vote); inventory(closing.game); }); void test('own-faction redraw preference and later allied forfeiture use cards supplied by genuine closing draws only', () => { const fixture = createDiscoveryClassicNexusSkillsFixture(); const closing = closeDiscoveryClassicNexusSkills(fixture, 'atreides'); const before = structuredClone(closing.beforeDraw), cards = before.nexusCards!.cards!; const index = cards.deck.indexOf('emperor'); assert.ok(index > 0); cards.deck.splice(1, 0, cards.deck.splice(index, 1)[0]); const redraw = stepDiscoveryClassicNexusSkills(before, { actor: fixture.owner, action: { ...closing.drawStep.action, ownRedraws: 1 } }); assert.equal(redraw.nexusCards!.cards!.hands[fixture.owner], 'emperor'); assert.equal(redraw.nexusCards!.cards!.discard.filter(card => card === 'atreides').length, 1); assert.equal(redraw.nexusCards!.cards!.deck.length, before.nexusCards!.cards!.deck.length - 2); inventory(redraw); let game = finishClassicDiscoveryNexusMentat(closing.game).after; game = advanceDiscoveryClassicNexusSkills(game, game => game.phase === 1 && discoveryClassicNexusSkillsClean(game)); const wormIndex = game.spiceDeck.findIndex(card => 'worm' in card && !card.greatMaker); assert.ok(wormIndex >= 0); const worm = game.spiceDeck.splice(wormIndex, 1)[0]; const landIndex = game.spiceDeck.findIndex(card => 'territory' in card && !card.discovery); assert.ok(landIndex >= 0); game.spiceDeck.unshift(worm, game.spiceDeck.splice(landIndex, 1)[0]); game = openClassicDiscoveryNexusAlliance(game); const heldWindow = structuredClone(game); const keep = advanceDiscoveryClassicNexusSkills(heldWindow, game => game.nexusCards?.phase?.stage === 'drawing'); const kept = stepDiscoveryClassicNexusSkills(keep, { actor: fixture.owner, action: { type: 'nexusCardChoice', turn: keep.turn, card: 'atreides', choice: 'keep', ownRedraws: 0 } }); assert.equal(kept.nexusCards!.cards!.hands[fixture.owner], 'atreides'); assert.deepEqual(kept.nexusCards!.cards!.deck, keep.nexusCards!.cards!.deck); game = stepDiscoveryClassicNexusSkills(game, { actor: fixture.opponent, action: { type: 'alliance', target: null } }); game = formClassicDiscoveryNexusAlliance(game, fixture.owner, fixture.opponent); assert.equal(game.nexusCards!.cards!.hands[fixture.owner], null); assert.equal(game.nexusCards!.cards!.discard.filter(card => card === 'atreides').length, 1); game = advanceDiscoveryClassicNexusSkills(game, game => game.nexusCards?.phase?.stage === 'drawing'); assert.ok(!game.nexusCards!.phase!.eligible.includes(fixture.owner)); assert.deepEqual(viewGame(game, fixture.owner).nexusCards!.choices, []); inventory(kept); inventory(game); }); for (const advanced of [false, true]) for (const kind of ['emperor', 'fremen'] as const) { void test(`${advanced ? 'Advanced' : 'Basic'} trained Discovery ${kind} returns native Great Maker casualties with distinct ordinary ledgers and actual Axlotl settlement`, () => { const fixture = createDiscoveryClassicNexusSkillsFixture({ advanced, tech: true, strongholds: advanced }); const closing = closeDiscoveryClassicNexusSkills(fixture, kind); const game = advanceDiscoveryClassicNexusSkills(closing.game, game => game.phase === 4 && discoveryClassicNexusSkillsClean(game)); assert.equal(player(game, fixture.owner).tanks, 6, 'Tanks arise from the original worm action, never an injected death.'); const view = viewGame(game, fixture.owner), action: Action = kind === 'emperor' ? { type: 'nexusEmperorRevive', event: view.nexusEmperorSecretAlly!.event, elite: 0 } : { type: 'nexusFremenRevival', event: view.nexusFremenRevival!.event }; reject(game, fixture.opponent, action); reject(game, fixture.owner, { ...action, event: 'stale-native-revival' }); if (kind === 'fremen') assert.equal(viewGame(game, fixture.opponent).nexusFremenRevival, null); const before = player(game, fixture.owner); let paid = stepDiscoveryClassicNexusSkills(structuredClone(game), { actor: fixture.owner, action }); if (kind === 'fremen') { assert.deepEqual(player(paid, fixture.owner), before, 'Spending grants quota without returning casualties'); assert.deepEqual(paid.techTokens, game.techTokens); paid = stepDiscoveryClassicNexusSkills(paid, { actor: fixture.owner, action: { type: 'revive', amount: 3, elite: 0 } }); paid = advanceDiscoveryClassicNexusSkills(paid, discoveryClassicNexusSkillsClean); } const after = player(paid, fixture.owner); assert.equal(after.reserves, before.reserves + 3); assert.equal(after.tanks, 3); assert.equal(after.spice, before.spice); assert.equal(after.revived, kind === 'fremen' ? 3 : 0); assert.equal(after.freeForcesRevived ?? 0, kind === 'fremen' ? 3 : 0); assert.equal(forceRevivalRemaining(paid, after), kind === 'fremen' ? 0 : 3); assert.equal(paid.nexusCards!.cards!.hands[fixture.owner], null); assert.equal(paid.nexusCards!.cards!.discard.filter(card => card === kind).length, 1); assert.deepEqual(paid.leaderSkills, game.leaderSkills); const history = kind === 'emperor' ? paid.nexusEmperorSecretHistory! : paid.nexusFremenRevivalHistory!; assert.equal(history.length, 1); assert.equal(history[0].owner, fixture.owner); if (kind === 'emperor') { const receipt = paid.nexusEmperorSecretHistory![0]; assert.ok(receipt.after, 'The requested Nexus return must settle before physical receipt consumption.'); assert.equal(receipt.before.tanks - receipt.after.tanks, 3); } else { const grant = paid.nexusFremenRevivalHistory![0]; assert.equal(grant.revivedAtUse, 0); assert.equal(grant.freeAtUse, 0); } reject(paid, fixture.owner, action); if (kind === 'emperor') { paid = stepDiscoveryClassicNexusSkills(paid, { actor: fixture.owner, action: { type: 'revive', amount: 3 } }); paid = advanceDiscoveryClassicNexusSkills(paid, discoveryClassicNexusSkillsClean); assert.equal(player(paid, fixture.owner).tanks, 0); assert.equal(player(paid, fixture.owner).revived, 3); assert.equal(player(paid, fixture.owner).freeForcesRevived, 2); assert.equal(player(paid, fixture.owner).spice, before.spice - 2); } else reject(paid, fixture.owner, { type: 'revive', amount: 1 }); const owner = paid.techTokens!.axlotl.owner!; const credit = ownedTech(paid.techTokens, owner).length; assert.equal(paid.techTokens!.axlotl.triggeredTurn, 2); assert.equal(paid.techTokens!.axlotl.spice, credit); const wallet = player(paid, owner).spice; const movement = advanceDiscoveryClassicNexusSkills(paid, game => game.phase === 5 && discoveryClassicNexusSkillsClean(game)); assert.equal(player(movement, owner).spice, wallet + credit); assert.equal(movement.techTokens!.axlotl.spice, 0); assert.deepEqual(movement.leaderSkills, fixture.game.leaderSkills); inventory(movement); let ordinaryFirst = stepDiscoveryClassicNexusSkills(structuredClone(game), { actor: fixture.owner, action: { type: 'revive', amount: kind === 'emperor' ? 3 : 1 } }); ordinaryFirst = advanceDiscoveryClassicNexusSkills(ordinaryFirst, discoveryClassicNexusSkillsClean); if (kind === 'emperor') { const extra = stepDiscoveryClassicNexusSkills(ordinaryFirst, { actor: fixture.owner, action }); assert.equal(player(extra, fixture.owner).tanks, 0); assert.equal(player(extra, fixture.owner).revived, 3); assert.equal(player(extra, fixture.owner).freeForcesRevived, 2); assert.equal(player(extra, fixture.owner).spice, player(ordinaryFirst, fixture.owner).spice); assert.deepEqual(extra.techTokens, ordinaryFirst.techTokens, 'Extra three cannot retrigger the already earned original Axlotl activity.'); inventory(extra); } else { const granted = stepDiscoveryClassicNexusSkills(ordinaryFirst, { actor: fixture.owner, action }); assert.deepEqual(player(granted, fixture.owner), player(ordinaryFirst, fixture.owner)); reject(granted, fixture.owner, { type: 'revive', amount: 3, elite: 0 }); const returned = advanceDiscoveryClassicNexusSkills(stepDiscoveryClassicNexusSkills(granted, { actor: fixture.owner, action: { type: 'revive', amount: 2, elite: 0 } }), discoveryClassicNexusSkillsClean); assert.equal(player(returned, fixture.owner).tanks, 3); assert.equal(player(returned, fixture.owner).revived, 3); assert.equal(player(returned, fixture.owner).freeForcesRevived, 3); assert.equal(player(returned, fixture.owner).spice, player(ordinaryFirst, fixture.owner).spice); assert.equal(returned.nexusCards!.cards!.hands[fixture.owner], null); assert.deepEqual(returned.techTokens, ordinaryFirst.techTokens, 'The ordinary return cannot retrigger already earned Axlotl activity'); assert.deepEqual(returned.leaderSkills, game.leaderSkills); inventory(returned); } }); } void test('borrowed Guild nested shipment retains physical training and held Arrakeen, then actual skilled Warmaster cleanup and mandatory Tech transfer occur in order', () => { const fixture = createDiscoveryClassicNexusSkillsFixture({ advanced: true, tech: true, strongholds: true, skill: 'warmaster' }); const closing = closeDiscoveryClassicNexusSkills(fixture, 'guild'); const battle = openDiscoveryClassicNexusSkillsBattle(fixture, closing.game); const before = player(battle.shipmentWindow, fixture.owner), arrived = player(battle.arrived, fixture.owner); assert.equal(arrived.forces['cistern:0'], 6); assert.equal(arrived.reserves, before.reserves - 3); assert.equal(arrived.spice, before.spice - 2); assert.equal(arrived.shipped, true); assert.equal(arrived.moved, before.moved); assert.equal(battle.arrived.nexusCards!.cards!.hands[fixture.owner], null); assert.equal(battle.arrived.nexusGuildSecretHistory!.length, 1); assert.equal(battle.arrived.nexusGuildSecretHistory![0].receipt.owner, fixture.owner); assert.equal(battle.arrived.strongholdCards!.owners.arrakeen, fixture.owner); assert.deepEqual(battle.arrived.leaderSkills, battle.shipmentWindow.leaderSkills); reject(battle.shipmentWindow, fixture.owner, { ...battle.shipmentStep.action, smuggler: true }); const industryOwner = battle.arrived.techTokens!.heighliners.owner!; const industryCredit = ownedTech(battle.arrived.techTokens, industryOwner).length; assert.equal(battle.arrived.techTokens!.heighliners.triggeredTurn, 2); assert.equal(battle.arrived.techTokens!.heighliners.spice, industryCredit); assert.equal(battle.game.techTokens!.heighliners.spice, 0); assert.equal(player(battle.game, industryOwner).spice, player(battle.arrived, industryOwner).spice + industryCredit, 'Actual movement end settles shipping industry exactly once before the private nested plans.'); const revealed = revealDiscoveryClassicNexusSkillsBattle(battle), quote = quoteClassicSkillsStrongholdBattle(revealed); assert.equal(quote.winner, fixture.owner); assert.equal(viewGame(revealed, fixture.owner).battle!.strongholdEffects[fixture.owner], null); const payment = quote.payments.find(receipt => receipt.player === fixture.owner)!; assert.equal(payment.bankSupport, 0); assert.equal(payment.ownPayment, 1); const side = revealed.battle!.attacker === fixture.owner ? 'attacker' : 'defender'; assert.deepEqual(quote.leaderSkillBonuses[side].applied, [{ skill: 'warmaster', amount: 3, mode: 'skilled' }]); const loser = player(revealed, fixture.opponent), reward = ownedTech(revealed.techTokens, fixture.opponent); assert.ok(reward.length > 0); const cleanup = finishDiscoveryClassicNexusSkillsBattle(revealed, true); assert.equal(player(cleanup, fixture.owner).forces['cistern:0'], 5); assert.equal(player(cleanup, fixture.opponent).forces['cistern:0'] ?? 0, 0); assert.equal(player(cleanup, fixture.opponent).tanks, loser.tanks + 2); assert.deepEqual(cleanup.techTokens, revealed.techTokens, 'Battle cleanup still owns its priority before the winner reward.'); const done = finishDiscoveryClassicNexusSkillsBattle(cleanup); assert.equal(done.lastBattleContext!.winner, fixture.owner); assert.ok(player(done, fixture.owner).hand.some(card => card.id === battle.weapon)); assert.ok(!done.discard.some(card => card.id === battle.weapon)); assert.equal(viewGame(done, fixture.owner).leaderSkills!.assignments.find(assignment => assignment.leader === fixture.leader)!.faceUp, true); for (const token of reward) assert.equal(done.techTokens![token].owner, fixture.owner); assert.deepEqual(ownedTech(done.techTokens, fixture.opponent), []); assert.deepEqual(done.nexusGuildSecretHistory, battle.arrived.nexusGuildSecretHistory); assert.equal(done.strongholdCards!.owners.arrakeen, fixture.owner); inventory(done); }); void test('all four minimal policies produce actual offered training, physical entry, qualified Nexus draw and source-clear return outcomes', () => { const fixture = createDiscoveryClassicNexusSkillsFixture({ advanced: true, tech: true }); const closing = closeDiscoveryClassicNexusSkills(fixture, 'emperor'); const revival = advanceDiscoveryClassicNexusSkills(closing.game, game => game.phase === 4 && discoveryClassicNexusSkillsClean(game)); const training = advanceDiscoveryClassicNexusSkills(structuredClone(fixture.setup), game => game.setupStage === 'leaderSkills'); for (const difficulty of DIFFICULTIES) { const cases = [ { game: training, actor: Object.keys(training.leaderSkills!.offers)[0], type: 'leaderSkill' }, { game: fixture.game, actor: fixture.owner, type: 'decision' }, { game: closing.beforeDraw, actor: fixture.owner, type: 'nexusCardChoice' }, { game: revival, actor: fixture.owner, type: 'nexusEmperorRevive' }, ]; for (const current of cases) { const view = viewGame(current.game, current.actor); view.players.find(seat => seat.id === current.actor)!.bot = difficulty; const untouched = structuredClone(view), action = botActions(view).find(action => action.type === current.type); assert.ok(action, `${difficulty} needs the actual owned ${current.type}.`); const after = stepDiscoveryClassicNexusSkills(structuredClone(current.game), { actor: current.actor, action }); assert.deepEqual(view, untouched); if (current.type === 'leaderSkill') { assert.equal(after.leaderSkills!.assignments.length, 1); const assignment = after.leaderSkills!.assignments[0]; assert.equal(assignment.owner, current.actor); assert.ok(current.game.leaderSkills!.offers[current.actor].cards.includes(assignment.skill)); assert.equal(after.leaderSkills!.offers[current.actor], undefined); assert.deepEqual(after.players.map(seat => seat.hand), current.game.players.map(seat => seat.hand)); } else if (current.type === 'decision') { assert.equal(after.discoveryEntry, undefined); const entered = player(after, current.actor).forces['cistern:0'] ?? 0; assert.equal((player(after, current.actor).forces[fixture.source] ?? 0) + entered, 3); assert.equal(player(after, current.actor).spice, player(current.game, current.actor).spice); assert.deepEqual(after.leaderSkills, current.game.leaderSkills); } else if (current.type === 'nexusCardChoice') { assert.equal(after.nexusCards!.phase!.stage, 'complete'); assert.ok(after.nexusCards!.cards!.hands[current.actor]); assert.equal(after.phase, 2); } else { assert.equal(player(after, current.actor).tanks, 3); assert.equal(player(after, current.actor).reserves, player(current.game, current.actor).reserves + 3); assert.equal(player(after, current.actor).revived, 0); assert.equal(after.nexusCards!.cards!.hands[current.actor], null); assert.equal(after.techTokens!.axlotl.triggeredTurn, 2); assert.deepEqual(after.leaderSkills, current.game.leaderSkills); } inventory(after); } } });