From eff668b2029fe9935971b41e9f0597c1a014e78b Mon Sep 17 00:00:00 2001 From: Quintine <15965533+Quintine@users.noreply.github.com> Date: Sat, 24 Oct 2026 19:03:03 +1100 Subject: [PATCH] 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. --- components/moritani-assassinate.tsx | 41 ++- docs/CURRENT_STATUS.md | 23 ++ docs/IMPLEMENTATION_STATUS.md | 35 +++ docs/MORITANI_ASSASSINATE_LEADERS.md | 97 ++++++- docs/RULE_DECISIONS.md | 20 ++ game/bots.ts | 2 +- game/engine.ts | 17 +- game/moritani-assassinate.ts | 33 ++- game/reference.ts | 12 +- ...vanced-moritani-skills-assassinate.test.ts | 2 +- ...standalone-nexus-e3-skills-runtime.test.ts | 2 +- ...xture-moritani-assassinate-ghola-target.ts | 181 ++++++++++++ tests/fixture-moritani-assassinate-target.ts | 267 ++++++++++++++++++ .../fixture-moritani-foreign-ghola-winner.ts | 9 +- tests/moritani-assassinate-controls.test.tsx | 117 +++++++- tests/moritani-assassinate-engine.test.ts | 4 +- tests/moritani-assassinate-quote.test.ts | 66 ++++- tests/moritani-assassinate-target.test.ts | 152 ++++++++++ tests/moritani-auditor-winner.test.ts | 4 +- tests/moritani-ecaz-duke-winner.test.ts | 2 +- tests/moritani-foreign-ghola-winner.test.ts | 2 +- tests/moritani-hero-replacement.test.ts | 2 +- ...oritani-independent-skills-runtime.test.ts | 4 +- ...itani-nexus-skills-modules-runtime.test.ts | 2 +- tests/moritani-skills-modules-runtime.test.ts | 2 +- 25 files changed, 1041 insertions(+), 57 deletions(-) create mode 100644 tests/fixture-moritani-assassinate-ghola-target.ts create mode 100644 tests/fixture-moritani-assassinate-target.ts create mode 100644 tests/moritani-assassinate-target.test.ts diff --git a/components/moritani-assassinate.tsx b/components/moritani-assassinate.tsx index 0f1afcbe..d03dd332 100644 --- a/components/moritani-assassinate.tsx +++ b/components/moritani-assassinate.tsx @@ -17,6 +17,22 @@ export function MoritaniAssassinate({ busy: boolean; }) { const [selected, setSelected] = useState(''); + return ; +} + +export function MoritaniAssassinateChoice({ + game, + act, + busy, + selected, + onSelect, +}: { + game: GameView; + act: (action: Action) => void; + busy: boolean; + selected: string; + onSelect: (card: string) => void; +}) { const offer = game.moritaniAssassinate; const decision = game.decision; if ( @@ -30,9 +46,9 @@ export function MoritaniAssassinate({ const pending = offer.pending; const card = pending.cards.find((candidate) => candidate.card === selected) ?? pending.cards[0]; const blocked = busy || !!game.response || !!game.truthtrance; + const revealBlocked = blocked || !!pending.blocked; const opponent = game.players.find((player) => player.id === pending.opponent); const factionName = opponent ? faction(opponent.faction).name : 'the opposing faction'; - const selectedLeader = card && game.allLeaders.find((leader) => leader.id === card.card); return (

Assassinate Leaders

@@ -43,7 +59,10 @@ export function MoritaniAssassinate({

This advantage is available once against each faction. It cannot be canceled - with Karama. A dead target remains a legal choice, but yields no spice. + with Karama. The potential bounty is the leader’s printed strength and is + awarded only if the target is alive. An already-dead target remains a + legal choice and awards no spice. Revealing the physical Traitor Card + makes the actual outcome public.

{pending.blocked &&

{pending.blocked}

} {pending.cards.length > 0 ? ( @@ -54,31 +73,32 @@ export function MoritaniAssassinate({ aria-label="Traitor Card to reveal" className="min-h-11 w-full rounded border border-[#65644b] bg-[#171b17] px-3 py-2" value={card?.card ?? ''} - disabled={blocked} - onChange={(event) => setSelected(event.currentTarget.value)} + disabled={revealBlocked} + onChange={(event) => onSelect(event.currentTarget.value)} > {pending.cards.map((candidate) => ( ))} {card && (
-

{card.name} · {card.dead ? '0 spice; target already dead' : `${card.bounty} spice bounty`}

-

Printed leader strength: {selectedLeader?.strength ?? 'unknown'}

+

{card.name} · {card.potentialBounty} spice potential bounty

+

Printed leader strength: {card.potentialBounty}

)} @@ -87,6 +107,7 @@ export function MoritaniAssassinate({

No eligible opposing-faction Traitor Card is available for this advantage.

)}