145 lines
8.8 KiB
TypeScript
145 lines
8.8 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
import { applyAction, viewGame } from '../game/engine';
|
|
import { forceRevivalQuote } from '../game/revival';
|
|
import { advanceSpiceBankerIncomeToMentat, nextSpiceBankerIncomeNativeStep } from './fixture-spice-banker-income';
|
|
import {
|
|
advanceNativeBankerIncomeToPhase, createNativeBankerIncomeFixture,
|
|
payNativeBankerIncomeFixture, type NativeBankerIncomeOptions,
|
|
} from './fixture-native-banker-income';
|
|
|
|
const nativeCases: NativeBankerIncomeOptions[] = [
|
|
{ family: 'ixians' },
|
|
{ family: 'ixians', withTleilaxu: true },
|
|
{ family: 'tleilaxu' },
|
|
{ family: 'choam' },
|
|
{ family: 'moritani' },
|
|
];
|
|
|
|
for (const options of nativeCases) {
|
|
void test(`Basic ${options.family}${options.withTleilaxu ? '+Tleilaxu' : ''}: original bank auction buys its card once, defers Banker, and collects only at native Mentat`, () => {
|
|
const fixture = createNativeBankerIncomeFixture(options);
|
|
const buyer = fixture.game.players.find(p => p.id === fixture.payer)!;
|
|
const owner = fixture.game.players.find(p => p.id === fixture.owner)!;
|
|
const purchased = fixture.game.auction!.cards[fixture.game.auction!.index];
|
|
assert.notEqual(buyer.id, owner.id);
|
|
const paid = payNativeBankerIncomeFixture(fixture);
|
|
assert.equal(paid.players.find(p => p.id === buyer.id)!.spice, buyer.spice - 4);
|
|
assert.equal(paid.players.find(p => p.id === buyer.id)!.hand.filter(c => c.id === purchased.id).length, 1);
|
|
assert.equal(paid.players.find(p => p.id === owner.id)!.spice, owner.spice);
|
|
assert.deepEqual(viewGame(paid, owner.id).spiceBankerIncome!.deferred, [{ owner: owner.id, amount: 1 }]);
|
|
assert.deepEqual(paid.spiceBankerIncome!.sources.find(row => row.grant)!.source.bankLegs,
|
|
[{ payer: buyer.id, amount: 4 }]);
|
|
|
|
// Ordinary auction/Shipment/Battle/Collection suffixes continue naturally;
|
|
// Collection's own native income is already in this baseline wallet.
|
|
const collection = advanceNativeBankerIncomeToPhase(paid, 7);
|
|
const balance = collection.players.find(p => p.id === owner.id)!.spice;
|
|
assert.deepEqual(viewGame(collection, owner.id).spiceBankerIncome!.deferred, [{ owner: owner.id, amount: 1 }]);
|
|
const mentat = advanceSpiceBankerIncomeToMentat(collection);
|
|
assert.equal(mentat.players.find(p => p.id === owner.id)!.spice, balance + 1);
|
|
assert.deepEqual(viewGame(mentat, owner.id).spiceBankerIncome!.deferred, []);
|
|
assert.equal(mentat.players.find(p => p.id === buyer.id)!.hand.filter(c => c.id === purchased.id).length, 1);
|
|
});
|
|
}
|
|
|
|
void test('Basic native Tleilaxu Banker does not count a real auction paid to Emperor as a bank payment', () => {
|
|
const fixture = createNativeBankerIncomeFixture({ family: 'tleilaxu', payerFaction: 'atreides' });
|
|
const payer = fixture.game.players.find(p => p.id === fixture.payer)!;
|
|
const emperor = fixture.game.players.find(p => p.faction === 'emperor')!;
|
|
const card = fixture.game.auction!.cards[fixture.game.auction!.index];
|
|
const paid = payNativeBankerIncomeFixture(fixture);
|
|
assert.equal(paid.players.find(p => p.id === payer.id)!.spice, payer.spice - 4);
|
|
assert.equal(paid.players.find(p => p.id === emperor.id)!.spice, emperor.spice + 4);
|
|
assert.equal(paid.players.find(p => p.id === payer.id)!.hand.filter(c => c.id === card.id).length, 1);
|
|
assert.deepEqual(viewGame(paid, fixture.owner).spiceBankerIncome!.deferred, []);
|
|
});
|
|
|
|
for (const amount of [1, 3]) {
|
|
void test(`Basic native Tleilaxu revival recipient: ${amount === 1 ? 'zero payer debit/free reward' : 'four-spice player payment'} returns original forces without Banker income`, () => {
|
|
const fixture = createNativeBankerIncomeFixture({ family: 'tleilaxu', kind: 'force-revival', amount });
|
|
const payer = fixture.game.players.find(p => p.id === fixture.payer)!;
|
|
const recipient = fixture.game.players.find(p => p.faction === 'tleilaxu')!;
|
|
const quote = forceRevivalQuote(fixture.game, payer, amount);
|
|
assert.equal(quote.free, 1);
|
|
assert.equal(quote.cost, amount === 1 ? 0 : 4);
|
|
const paid = payNativeBankerIncomeFixture(fixture);
|
|
const revived = paid.players.find(p => p.id === payer.id)!;
|
|
assert.equal(revived.spice, payer.spice - quote.cost);
|
|
assert.equal(revived.reserves, payer.reserves + amount);
|
|
assert.equal(revived.tanks, payer.tanks - amount);
|
|
assert.equal(revived.revived, payer.revived + amount);
|
|
// The printed free-revival bonus comes from the Bank, not from this payer.
|
|
assert.equal(paid.players.find(p => p.id === recipient.id)!.spice, recipient.spice + quote.cost + 1);
|
|
assert.deepEqual(viewGame(paid, fixture.owner).spiceBankerIncome!.deferred, []);
|
|
assert.equal(paid.spiceBankerIncome!.sources.some(row => row.grant), false);
|
|
});
|
|
}
|
|
|
|
void test('Basic native Tleilaxu own paid revival really pays the Bank: another native Banker earns one, not the free-income reward', () => {
|
|
const fixture = createNativeBankerIncomeFixture({
|
|
family: 'tleilaxu', bankerFaction: 'atreides', payerFaction: 'tleilaxu',
|
|
kind: 'force-revival', amount: 6,
|
|
});
|
|
const payer = fixture.game.players.find(p => p.id === fixture.payer)!;
|
|
const owner = fixture.game.players.find(p => p.id === fixture.owner)!;
|
|
const quote = forceRevivalQuote(fixture.game, payer, 6);
|
|
assert.equal(quote.free, 2);
|
|
assert.equal(quote.cost, 4);
|
|
const paid = payNativeBankerIncomeFixture(fixture);
|
|
const revived = paid.players.find(p => p.id === payer.id)!;
|
|
assert.equal(revived.reserves, payer.reserves + 6);
|
|
assert.equal(revived.tanks, payer.tanks - 6);
|
|
assert.equal(revived.spice, payer.spice - 4 + 1);
|
|
assert.equal(paid.players.find(p => p.id === owner.id)!.spice, owner.spice);
|
|
assert.deepEqual(viewGame(paid, owner.id).spiceBankerIncome!.deferred, [{ owner: owner.id, amount: 1 }]);
|
|
assert.deepEqual(paid.spiceBankerIncome!.sources.find(row => row.grant)!.source.bankLegs,
|
|
[{ payer: payer.id, amount: 4 }]);
|
|
});
|
|
|
|
for (const cost of [0, 3, 4]) {
|
|
void test(`canceled native Tleilaxu income: actual payer debit ${cost}, not the extra free reward, controls the Banker threshold`, () => {
|
|
const options: NativeBankerIncomeOptions = cost === 3
|
|
? { family: 'ixians', withTleilaxu: true, bankerFaction: 'atreides', payerFaction: 'ixians', amount: 2, elite: 2 }
|
|
: { family: 'tleilaxu', bankerFaction: 'atreides', payerFaction: 'emperor', amount: cost === 0 ? 1 : 3 };
|
|
const fixture = createNativeBankerIncomeFixture({ ...options, kind: 'force-revival', stageIncomeCounter: true });
|
|
const payer = fixture.game.players.find(p => p.id === fixture.payer)!;
|
|
const recipient = fixture.game.players.find(p => p.faction === 'tleilaxu')!;
|
|
const owner = fixture.game.players.find(p => p.id === fixture.owner)!;
|
|
const counter = owner.hand.find(card => card.effect === 'karama')!;
|
|
assert.equal(fixture.amount, cost);
|
|
let game = applyAction(fixture.game, fixture.actor, fixture.paymentAction);
|
|
for (let i = 0; game.response?.kind !== 'revivalIncome' && i < 100; i++) {
|
|
const next = nextSpiceBankerIncomeNativeStep(game);
|
|
assert.ok(next);
|
|
game = applyAction(game, next.actor, next.action);
|
|
}
|
|
assert.ok(game.response?.kind === 'revivalIncome');
|
|
assert.equal(game.response.amount, cost + 1);
|
|
assert.equal(game.players.find(p => p.id === payer.id)!.spice, payer.spice - cost);
|
|
assert.equal(game.players.find(p => p.id === recipient.id)!.spice, recipient.spice);
|
|
assert.deepEqual(viewGame(game, owner.id).spiceBankerIncome!.deferred, []);
|
|
game = applyAction(game, owner.id, { type: 'card', card: counter.id, mode: 'cancel' });
|
|
for (let i = 0; (game.response || game.decision) && i < 100; i++) {
|
|
const next = nextSpiceBankerIncomeNativeStep(game);
|
|
assert.ok(next);
|
|
game = applyAction(game, next.actor, next.action);
|
|
}
|
|
assert.ok(!game.response && !game.decision && !game.pendingRevival);
|
|
assert.equal(game.players.find(p => p.id === payer.id)!.spice, payer.spice - cost);
|
|
assert.equal(game.players.find(p => p.id === recipient.id)!.spice, recipient.spice);
|
|
assert.equal(game.players.find(p => p.id === owner.id)!.spice, owner.spice);
|
|
assert.equal(game.players.find(p => p.id === payer.id)!.tanks, payer.tanks - options.amount!);
|
|
if (cost === 3) {
|
|
assert.equal(game.players.find(p => p.id === payer.id)!.elites!.tanks, payer.elites!.tanks - 2);
|
|
assert.equal(game.players.find(p => p.id === payer.id)!.elites!.reserves, payer.elites!.reserves + 2);
|
|
}
|
|
assert.equal(game.discard.filter(card => card.id === counter.id).length, 1);
|
|
assert.deepEqual(viewGame(game, owner.id).spiceBankerIncome!.deferred,
|
|
cost === 4 ? [{ owner: owner.id, amount: 1 }] : []);
|
|
assert.equal(game.spiceBankerIncome!.sources.some(row => row.grant), cost === 4);
|
|
if (cost > 0) assert.deepEqual(game.spiceBankerIncome!.sources.at(-1)!.source.bankLegs,
|
|
[{ payer: payer.id, amount: cost }]);
|
|
});
|
|
}
|