Retain printed native identity and valid original custody while projecting only uniform potential bounty before commitment. Resolve living/dead targets once, clear selected capture/concealment and retain ordinary Ghola return custody. Selected concealed-outcome disclosure is explicitly provisional. Verified: 14 affected offline files100/100, types/lint and production build; six actual choice/gameplay/GameTable SSR journeys and twelve profile paths. Native capture source47 actions; actual paid Ghola prefix140 actions followed by explicit conserved seven-action battle. No saved-room/D1/reset/restart or deployment operation. Runtime-only driver fixes reused unchanged passed checks.
88 lines
5.0 KiB
TypeScript
88 lines
5.0 KiB
TypeScript
import assert from 'node:assert/strict';
|
||
import test from 'node:test';
|
||
import { viewGame, type Action } from '../game/engine';
|
||
import { leaders } from '../game/cards';
|
||
import {
|
||
FOREIGN_GHOLA_DISC, createMoritaniForeignGholaWinnerFixture,
|
||
rejectForeignGholaWinnerAction, revealMoritaniForeignGholaWitness,
|
||
} from './fixture-moritani-foreign-ghola-winner';
|
||
import { mixedE3MoritaniPlayer } from './fixture-mixed-e3-moritani-nexus';
|
||
|
||
/**
|
||
* The consumer-visible regression for one actual physical winning disc whose
|
||
* printed faction differs from the victorious player: a Guild native disc dies
|
||
* in original combat, Tleilaxu legally revives it as a foreign ghola, and that
|
||
* surviving disc then wins a real battle against Moritani. Before the played-
|
||
* disc registry admitted ordinary canonical identities independently of the
|
||
* winner’s faction, no private opportunity was produced at all.
|
||
*/
|
||
void test('a real Tleilaxu foreign ghola victory opens Moritani’s private loss choice against the opposing native Tleilaxu Traitor', () => {
|
||
const f = createMoritaniForeignGholaWinnerFixture();
|
||
assert.equal(mixedE3MoritaniPlayer(f.afterSetup, f.moritani).traitors.includes(f.target), true);
|
||
|
||
// Authentic acquisition: the disc really died and the return is a real action.
|
||
assert.equal(f.guildBattle.winner, f.tleilaxu);
|
||
assert.equal(mixedE3MoritaniPlayer(f.guildBattle.after, f.guild)
|
||
.leaders.find((l) => l.id === FOREIGN_GHOLA_DISC)!.dead, true);
|
||
assert.equal(mixedE3MoritaniPlayer(f.revival.after, f.tleilaxu).spice,
|
||
mixedE3MoritaniPlayer(f.revival.before, f.tleilaxu).spice - 3);
|
||
const borrowed = mixedE3MoritaniPlayer(f.window.game, f.guild).leaders.find(l => l.id === FOREIGN_GHOLA_DISC)!;
|
||
assert.equal(borrowed.dead, false);
|
||
assert.equal(borrowed.faction, 'guild');
|
||
assert.equal(borrowed.gholaBy, f.tleilaxu);
|
||
|
||
// The real window: the winning disc belongs to no Tleilaxu disc, yet the
|
||
// logical faction and target pool stay the victorious player’s printed one.
|
||
assert.equal(f.window.opposingLeader, FOREIGN_GHOLA_DISC);
|
||
assert.equal(f.window.faction, 'tleilaxu');
|
||
assert.notEqual(f.window.territory, f.guildBattle.territory);
|
||
assert.equal(f.window.opponent, f.tleilaxu);
|
||
assert.equal(f.window.blocked, null);
|
||
assert.equal(f.window.game.moritaniAssassinate!.normalTraitorCall, false);
|
||
assert.ok(f.window.cards.some((choice) => choice.card === f.target));
|
||
assert.equal(f.window.cards.every((choice) =>
|
||
leaders('tleilaxu').some((l) => l.id === choice.card)), true,
|
||
'Only the opposing player’s native Tleilaxu identities are targets.');
|
||
assert.equal(f.window.cards.some((choice) => choice.card === FOREIGN_GHOLA_DISC), false,
|
||
'The borrowed foreign disc never becomes a target identity.');
|
||
assert.equal(f.window.cards.find((choice) => choice.card === f.target)!.potentialBounty, f.bounty);
|
||
|
||
// Original continuation: printed bounty, one physical death, Mentat replacement.
|
||
const outcome = revealMoritaniForeignGholaWitness(f);
|
||
assert.notEqual(outcome.replacement, f.target);
|
||
assert.equal(outcome.mentat.traitorReserve!.includes(outcome.replacement), false,
|
||
'The drawn replacement left the reserve for Moritani’s private hand.');
|
||
assert.equal(mixedE3MoritaniPlayer(outcome.mentat, f.moritani)
|
||
.traitors.includes(outcome.replacement), true);
|
||
assert.deepEqual(outcome.mentat.moritaniAssassinate!.opportunities.map((r) => r.stage),
|
||
['replaced']);
|
||
const ghola = mixedE3MoritaniPlayer(outcome.mentat, f.guild)
|
||
.leaders.find((l) => l.id === FOREIGN_GHOLA_DISC)!;
|
||
assert.equal(ghola.gholaBy, f.tleilaxu, 'The surviving foreign ghola stays in Tleilaxu custody.');
|
||
assert.equal(ghola.dead, false);
|
||
assert.equal(mixedE3MoritaniPlayer(outcome.mentat, f.tleilaxu)
|
||
.leaders.find((l) => l.id === f.target)!.dead, true, 'The revealed native Tleilaxu disc died.');
|
||
});
|
||
|
||
void test('the foreign-ghola window stays private and rejects any borrowed or foreign identity', () => {
|
||
const f = createMoritaniForeignGholaWinnerFixture();
|
||
const rival = viewGame(f.window.game, f.tleilaxu).moritaniAssassinate!;
|
||
assert.equal(rival.pending?.cards.length, 0);
|
||
assert.equal(rival.pending?.blocked, null);
|
||
const owner = viewGame(f.window.game, f.moritani).moritaniAssassinate!;
|
||
assert.equal(owner.history.length, 0, 'Nothing is public until the reveal actually commits.');
|
||
|
||
const foreign: Action = { type: 'decision', event: f.window.event, card: 'guild-1' };
|
||
rejectForeignGholaWinnerAction(f.window.game, f.moritani, foreign);
|
||
rejectForeignGholaWinnerAction(f.window.game, f.moritani,
|
||
{ type: 'decision', event: 'stale-foreign-ghola', card: f.target });
|
||
rejectForeignGholaWinnerAction(f.window.game, f.tleilaxu,
|
||
{ type: 'decision', event: f.window.event, card: f.target });
|
||
rejectForeignGholaWinnerAction(f.window.game, f.moritani,
|
||
{ type: 'decision', event: f.window.event, card: f.window.opposingLeader });
|
||
|
||
assert.equal(mixedE3MoritaniPlayer(f.window.game, f.tleilaxu)
|
||
.leaders.find((l) => l.id === f.target)!.dead, false,
|
||
'A rejected submission never kills the held target.');
|
||
});
|