Support Moritani assassination of captured and foreign-ghola targets

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.
This commit is contained in:
Quintine committed 2026-10-24 19:03:03 +11:00
1 parent 61828150b9
commit eff668b202
25 files changed
+1041 -57

No files matched your search

+64 -2
View File
@@ -1,9 +1,10 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import { leaders } from '../game/cards';
import { createAuditorLeader, leaders } from '../game/cards';
import {
createMoritaniAssassinateOpportunity,
moritaniAssassinateChoices,
projectedMoritaniAssassinateChoices,
moritaniAssassinateSignature,
moritaniAssassinateTrigger,
quoteMoritaniAssassinate,
@@ -201,7 +202,7 @@ void test('the sourced normal-reveal forfeiture and already normally revealed ca
assert.deepEqual(moritaniAssassinateChoices(context).cards, []);
});
void test('missing, duplicated, altered or exceptional target custody cannot select another physical leader or invent bounty', () => {
void test('missing, duplicated, altered or invalid target custody cannot select another physical leader or invent bounty', () => {
for (const damage of [
(c: MoritaniAssassinateContext) => {
c.leaders = c.leaders.filter((l) => l.id !== 'atreides-1');
@@ -248,6 +249,67 @@ void test('missing, duplicated, altered or exceptional target custody cannot sel
}
});
void test('native capture and foreign-ghola targets retain one printed choice without disclosing life or custody', () => {
const base = fixture();
base.custodians = [{ id: 'h', faction: 'harkonnen' }, { id: 't', faction: 'tleilaxu' }];
const expected = projectedMoritaniAssassinateChoices(base);
const original = base.leaders[1];
for (const patch of [
{},
{ dead: true, deaths: 1 },
{ capturedBy: 'h', concealed: { captor: 'h', controller: 'a', dead: false, deaths: 0 } },
{ dead: true, deaths: 2, concealed: { captor: 'h', controller: 'a', dead: false, deaths: 0 } },
{ gholaBy: 't', deaths: 1 },
{ gholaBy: 't', dead: true, deaths: 2 },
{ gholaBy: 't', capturedBy: 'h', deaths: 1,
concealed: { captor: 'h', controller: 't', dead: false, deaths: 1 } },
]) {
const context = structuredClone(base);
Object.assign(context.leaders[1], patch);
const before = structuredClone(context);
const projected = projectedMoritaniAssassinateChoices(context);
assert.deepEqual(projected, expected);
assert.deepEqual(Object.keys(projected.cards[0]).sort(), ['card', 'name', 'potentialBounty']);
const quote = quoteMoritaniAssassinate(context, original.id);
assert.equal(quote.kill, !context.leaders[1].dead);
assert.equal(quote.bounty, context.leaders[1].dead ? 0 : original.strength);
assert.deepEqual(context, before);
}
});
void test('native producer identities and the existing native controller are still required for exceptional targets', () => {
const base = fixture();
base.custodians = [{ id: 'h', faction: 'harkonnen' }, { id: 't', faction: 'tleilaxu' }];
for (const patch of [
{ capturedBy: 't' }, { gholaBy: 'h' },
{ concealed: { captor: 't', controller: 'a', dead: false, deaths: 0 } },
{ capturedBy: 'h', controller: 'h' }, { gholaBy: 't', controller: 't' },
]) {
const context = structuredClone(base);
Object.assign(context.leaders[1], patch);
assert.ok(!moritaniAssassinateChoices(context).cards.some(choice => choice.card === 'atreides-1'));
assert.throws(() => quoteMoritaniAssassinate(context, 'atreides-1'));
}
});
void test('Auditor remains a native printed two-spice target with its explicit capture and foreign-ghola exclusions', () => {
const context = fixture('choam');
const auditor = createAuditorLeader();
context.leaders = [...context.leaders, auditor];
context.held = [auditor.id];
context.custodians = [{ id: 'h', faction: 'harkonnen' }, { id: 't', faction: 'tleilaxu' }];
assert.equal(quoteMoritaniAssassinate(context, auditor.id).bounty, 2);
for (const patch of [
{ capturedBy: 'h' }, { gholaBy: 't' },
{ concealed: { captor: 'h', controller: 'a', dead: false, deaths: 0 } },
]) {
const changed = structuredClone(context);
Object.assign(changed.leaders.at(-1)!, patch);
assert.deepEqual(projectedMoritaniAssassinateChoices(changed).cards, []);
assert.throws(() => quoteMoritaniAssassinate(changed, auditor.id));
}
});
void test('a revealed card remains held until one Mentat replacement, and the used opposing faction cannot be assassinated again', () => {
const context = fixture(),
revealed = quoteMoritaniAssassinate(context, 'atreides-1').receipt;