diff --git a/components/game-table.tsx b/components/game-table.tsx index df1ab649..80bd3635 100644 --- a/components/game-table.tsx +++ b/components/game-table.tsx @@ -1165,7 +1165,9 @@ export function GameTable({ Semuta Drug development preview · bounded fresh-discard reactions, not complete Richese or certified expansion rules. Original Tech Tokens may remain in supported fresh 3–6-seat CHOAM/Ix profiles; the original - winner’s award follows card cleanup and precedes Face Dance. Native Moritani + winner’s award follows card cleanup and precedes Face Dance. Original + Stronghold Cards may remain in Advanced 2–6-seat profiles and are claimed + only at the end of Mentat. Native Moritani support is Basic only: clean shipment/movement Sabotage and Robbery overflow plus native losing-ally card-retention cleanup; other arrival continuations remain guarded.{' '} diff --git a/docs/CURRENT_STATUS.md b/docs/CURRENT_STATUS.md index 7a5a14db..6e68fab8 100644 --- a/docs/CURRENT_STATUS.md +++ b/docs/CURRENT_STATUS.md @@ -29,6 +29,22 @@ optional skill bands, combinations and public expansion starts remain open. Rules implementation continues; comprehensive assurance and administration remain deferred until all rules are implemented. +**Semuta / original Advanced Strongholds — 11 October.** Fresh Richese +CHOAM/optional-Ix games now retain original unused Stronghold Cards at2–6 +Advanced seats, optionally original Tech at3–6. The existing native initializer +and extracted roster predicate are reused; no redeal, Skills/other overlay, +Basic Moritani composition, played-save retrofit or public gate opens. +Both closing regressions fail before entry and pass after. Types/lint and +161 affected unit cases across13 files pass; entry help executes. Nine genuine +setup/first-Storm runtime cases and twelve closing cases cover both +configurations, claim/pass and four legal profiles. Gamont removes sole +Carthag control before Semuta restores CHOAM's original final owner window. +Only its actual Finish assigns the six public cards: Arrakeen to Atreides, +Carthag unclaimed, claim2, next turn3Storm; Tech/card custody stays intact. +Both styled390px paths show those original controls and public ownership with +no overflow. Existing games/storage/user PDF/deployment remain untouched. +See [original Stronghold composition](STRONGHOLD_CARDS.md#semuta-composition--11-october-2026). + **Semuta / printed native battle-cancellation costs — 11 October.** One `karamaBattleDiscard` receipt now pauses the physical printed Karama before native capture, Auditor, quoted battle-income, Ixian-substitution or Basic diff --git a/docs/IMPLEMENTATION_STATUS.md b/docs/IMPLEMENTATION_STATUS.md index beb68bc9..37b73e79 100644 --- a/docs/IMPLEMENTATION_STATUS.md +++ b/docs/IMPLEMENTATION_STATUS.md @@ -1,5 +1,38 @@ # Dune implementation status +## 11 October 2026 — original Advanced Strongholds in fresh Semuta games + +Fresh Advanced Richese/CHOAM/optional-Ix Semuta now retains unused original +Strongholds at2–6, optionally original Tech at3–6. The native Stronghold +initializer is reused, and its original E1/E2 roster condition is extracted +once for both setup and Semuta validation. Standalone E3 behavior remains +unchanged; Basic Moritani/Semuta does not gain modules. No redeal, played-game +conversion, Skills/other overlay or public gate changes. + +Two consumer-visible closing regressions fail at the old optional-module +exclusion and pass after. Genuine native setup/first Storm precedes conserved +later card/force staging. Original CHOAM Gamont returns the sole Carthag force; +Semuta claim/pass preserves unowned module custody and restores the same final +CHOAM window. Only the original owner Finish assigns cards from the changed +board: Arrakeen held by Atreides, Carthag unclaimed, claim2, turn3Storm. +Tech owners/ledgers and physical Treachery remain unchanged; no early or repeat +claim and no keeper award to the card claimant. + +Types/lint and161 unit cases across13 affected files pass; actual launcher +help prints the updated entry without store access. Nine genuine Advanced +starts cover Strongholds2–6 and both modules3–6. Twelve closing runtime cases +cover both configurations, claim/pass/four legal profiles and original final +assignment. Styled real GameTable at390px completes Return this force→Commit +→ original Finish in both entries. Public module inspection shows last claim2, +Arrakeen held/Carthag unclaimed, with original Tech owners visible in the combo +and no horizontal overflow. A role combobox lookup failed despite its fresh +AX entry; fresh id-based selection worked. No production control repair needed. + +Existing prototype dispatch needs no change because it delegates to the same +initializer. Warning, help, reference and canonical Semuta/Stronghold contracts +are updated. Temporary probes/services/tabs removed; existing games, storage, +user PDF and deployment untouched. No complete-mode/deployed certification. + ## 11 October 2026 — printed native battle-cancellation cost reactions One shared `karamaBattleDiscard` receipt extends the existing response-discard diff --git a/docs/SEMUTA_SOURCE_UPDATE_20260907.md b/docs/SEMUTA_SOURCE_UPDATE_20260907.md index f9d4f60e..030aa0f6 100644 --- a/docs/SEMUTA_SOURCE_UPDATE_20260907.md +++ b/docs/SEMUTA_SOURCE_UPDATE_20260907.md @@ -82,7 +82,15 @@ Original unused Tech Tokens may also remain in the CHOAM/optional-Ix entry at three through six seats, Basic or Advanced, under its existing native roster prerequisites. Genuine setup and printed/first-Storm ownership remain native. This does not add Tech to the separate Basic CHOAM/Ecaz Moritani entry or admit -Skills, Strongholds, other overlays, played-game conversion or public starts. +Skills, other overlays, played-game conversion or public starts. + +Advanced additionally preserves original unused Stronghold Cards at two +through six seats in the same CHOAM/optional-Ix entry. Combining original Tech +requires three through six. The existing native Stronghold initializer and +its shared roster predicate supply genuine setup; original first-Storm Tech +and final-Mentat Stronghold custody remain native. The six separate public +module cards never become Treachery discard candidates. + It interrupts **clean public ordinary-card, paid Box, retired Ornithopter, used Distrans, final @@ -703,3 +711,26 @@ the app's Tailwind PostCSS plugin; that was corrected from `vite.config.ts`, and all five styled paths were repeated. Earlier unstyled screenshots do not certify styled presentation. No production dialog patch was needed. These are local changed-path proofs, not complete mode or deployed acceptance. + +**11 October 2026 — original Advanced Stronghold composition.** The fresh +Semuta entry now dispatches selected unused Strongholds through the original +native initializer, retaining optional original Tech. Original E1/E2 roster +eligibility is extracted once and reused by that initializer and Semuta +validation; no second profile, redeal, played-save retrofit or public start +is introduced. Skills, Homeworlds, Nexus, Discovery and the separate Basic +CHOAM/Ecaz Moritani entry remain outside this composition. + +Nine genuine Advanced lobby/setup/first-Storm cases cover Strongholds2–6 and +both modules3–6. Twelve closing runtime cases cover Strongholds alone/both, +claim/pass and four legal Semuta profiles. Gamont returns the sole Carthag +counter, then Semuta retires before the original CHOAM finish. All six cards +remain unclaimed until that actual END Mentat action; only Arrakeen is awarded +to Atreides from the changed board, with Carthag unclaimed. Tech owners/ledgers +and physical Treachery remain unchanged, next turn is3Storm, and retired use +rejects. Both permanent cases fail before entry admission and pass after. +Styled real GameTable at390px completes Return this force→Commit→original +Finish and check victory in both configurations. Public cards show last +claim2, Arrakeen held by Atreides and Carthag unclaimed, with no overflow. +The combo's original Tech owners remain visible; source/reference/help and +persistent warning match the boundary. Full mode/deployment acceptance is +not certified by these local changed-path proofs. diff --git a/docs/STRONGHOLD_CARDS.md b/docs/STRONGHOLD_CARDS.md index 7fe65b39..f2cd80f0 100644 --- a/docs/STRONGHOLD_CARDS.md +++ b/docs/STRONGHOLD_CARDS.md @@ -2,6 +2,28 @@ Historical development module checkpoint, 7 September 2026: the six printed effects, public card custody, mobile declaration, support accounting, AI choices and readable faces are integrated. The later user-authorized [classic Advanced preview](ADVANCED_PREVIEW.md) already permits Stronghold Cards; preserve that access. Complete Advanced/expansion release and arbitrary combinations remain gated. +## Semuta composition — 11 October 2026 + +The fresh `semuta` entry with native Richese, CHOAM and optional Ix decks now +preserves original unused Strongholds in Advanced2–6; optional unused Tech +requires3–6. It reuses the original native setup/roster prerequisites and does +not modify physical card quantities, effects or public release gates. + +Closing CHOAM Gamont may offer its used Treachery card to Semuta before the +original owner finishes. Stronghold custody stays unclaimed across that +reaction and is assigned only by actual final Mentat using the changed board. +The six module cards are not Treachery hand/discard cards or Semuta targets. +No Skills, Homeworlds, Nexus, Discovery, Basic Strongholds, E3 mixture or +played-game conversion is added by this entry. + +Nine actual fresh Advanced setup/first-Storm cases and twelve closing runtime +cases cover both configurations, claim/pass and four legal profiles. Both +390px styled table paths retain the original closing control, reach turn3 +Storm and display Arrakeen held by Atreides/Carthag unclaimed at claim2 after +Gamont removed the sole Carthag force. This is bounded local evidence, not +complete Stronghold/Semuta/mode or deployed acceptance. + + ## Original Discovery composition — 5 October 2026 Fresh original `discovery` entry preserves selected unused Strongholds6 in diff --git a/game/engine.ts b/game/engine.ts index 93065c71..47e40f5f 100644 --- a/game/engine.ts +++ b/game/engine.ts @@ -5529,7 +5529,8 @@ function treacheryDiscardIntegrity(g: Game) { requireRule(g.semutaPreview === undefined || g.semutaPreview === true && g.expansions.includes('choam') && !!byFaction(g, 'richese') && !g.homeworlds && !g.nexusCards && !g.leaderSkills && !g.discoveries && - !g.discoveryEnabled && (!g.techTokens || nativeFactionTechProfile(g, true)) && !g.strongholdCards, + !g.discoveryEnabled && (!g.techTokens || nativeFactionTechProfile(g, true)) && + (!g.strongholdCards || nativeStrongholdFactionRoster(g)), 'This Semuta preview no longer matches its original Richese profile.'); const pending = g.pendingTreacheryDiscard; const sequence = g.treacheryDiscardSequence ?? 0; @@ -9545,15 +9546,19 @@ export function initializeEcazOccupyGameForAudit(state: Game): Game { g.ecazOccupyPreview = true; return g; } +/** Original E1/E2 Stronghold roster; shared by fresh setup and Semuta validation. */ +function nativeStrongholdFactionRoster(g: Game): boolean { + return g.advanced === true && g.players.length >= 2 && g.players.length <= 6 && + Array.isArray(g.expansions) && g.expansions.length >= 1 && g.expansions.length <= 2 && + g.expansions.every(id => id === 'ix' || id === 'choam') && + (g.expansions.length < 2 || g.expansions[0] !== g.expansions[1]) && + g.players.some(p => faction(p.faction).expansion !== 'base') && + g.players.every(p => faction(p.faction).expansion === 'base' || + g.expansions.includes(faction(p.faction).expansion)); +} /** Fresh native Stronghold composition; other overlays and public starts stay gated. */ export function initializeStrongholdFactionsGameForAudit(state: Game): Game { - requireRule(e3StrongholdFactionProfile(state) || - (state.advanced === true && Array.isArray(state.expansions) && - state.expansions.length >= 1 && state.expansions.length <= 2 && - state.expansions.every(id => id === 'ix' || id === 'choam') && - new Set(state.expansions).size === state.expansions.length && - state.players.some(p => faction(p.faction).expansion !== 'base') && - state.players.every(p => faction(p.faction).expansion === 'base' || state.expansions.includes(faction(p.faction).expansion))), + requireRule(e3StrongholdFactionProfile(state) || nativeStrongholdFactionRoster(state), 'Stronghold factions require supported Advanced native factions, classic opponents and their selected distinct decks.'); requireRule(!state.homeworlds && !state.nexusCards && !state.leaderSkills && !state.discoveryEnabled && !state.discoveries && !state.discoveryStash && !state.greatMaker && @@ -9597,8 +9602,8 @@ export function initializeMoritaniAssassinateGameForAudit(state: Game): Game { initializeMoritaniAssassinateState(g); return g; } -/** Development-only Semuta in fresh Richese games: optional Ix deck and original - * native Tech profile, or bounded Basic native Moritani/E3. Public starts stay gated. */ +/** Development-only Semuta in fresh Richese games: original native Tech and + * Advanced Stronghold compositions, or bounded Basic Moritani/E3. Public starts stay gated. */ export function initializeSemutaGameForAudit(state: Game): Game { const nativeTerror = !state.advanced && state.expansions.length === 2 && state.expansions.includes('choam') && state.expansions.includes('ecaz') && @@ -9609,8 +9614,10 @@ export function initializeSemutaGameForAudit(state: Game): Game { state.expansions.includes('choam') && (state.expansions.every(id => id === 'choam' || id === 'ix') || nativeTerror) && state.players.some(p => p.faction === 'richese'), - 'Semuta needs a fresh Richese faction game: CHOAM with optional Ix and original native Tech prerequisites, or Basic native Moritani with CHOAM and Ecaz decks and classic opponents; other optional modules remain excluded.'); - const g = initializeFactionExpansionsGameForAudit(state); + 'Semuta needs a fresh Richese faction game: CHOAM with optional Ix, original native Tech prerequisites and Advanced Stronghold Cards, or Basic native Moritani with CHOAM and Ecaz decks and classic opponents; other optional modules remain excluded.'); + const g = state.strongholdCards + ? initializeStrongholdFactionsGameForAudit(state) + : initializeFactionExpansionsGameForAudit(state); g.semutaPreview = true; return g; } diff --git a/game/reference.ts b/game/reference.ts index 66912e01..c66bb320 100644 --- a/game/reference.ts +++ b/game/reference.ts @@ -1176,7 +1176,7 @@ export const RULE_TOPICS: RuleTopic[] = [ 'Mandatory and winner battle discards also pause before native Tleilaxu Face Dance. Claim or Continue preserves the original winner’s casualties and card cleanup, then restores the same private Face Dancer opportunity. Replacement, leader death and force sources remain native; the claimed Treachery card stays with its claimant, and no Face Dancer card becomes a Semuta target.', 'Mandatory and winner battle discards also pause before quoted Advanced CHOAM force-payment income. Claim or Continue leaves the original amount unpaid through casualties and card cleanup, then opens the same allow/Karama response. Allow pays once; cancellation sends that income to the bank without refunding the original force payments. The original post-battle market still completes afterward.', 'Mandatory and winner battle discards also pause before a native Auditor opportunity. Claim or Continue preserves the original survival limit and opponent-used card identities, then eligibility and the full cancellation price use the current opposing hand. A card acquired from the other combatant can be eligible if the audited player did not use it. Payment or private inspection resumes once; only CHOAM receives the historical snapshot.', - 'Fresh three-through-six-seat Basic/Advanced Richese games with CHOAM and optional Ix may retain original unused Tech Tokens under the existing native roster prerequisites. Mandatory and winner discards now offer Semuta before the original winner takes one loser-owned token. Claim or Continue leaves every token and income ledger in place until original cleanup finishes; the winner’s selected award still precedes Face Dance. Skills, Strongholds, other overlays and the separate Basic Moritani entry remain excluded.', + 'Fresh three-through-six-seat Basic/Advanced Richese games with CHOAM and optional Ix may retain original unused Tech Tokens under the existing native roster prerequisites. Mandatory and winner discards now offer Semuta before the original winner takes one loser-owned token. Claim or Continue leaves every token and income ledger in place until original cleanup finishes; the winner’s selected award still precedes Face Dance. Advanced may additionally retain original unused Stronghold Cards at two through six seats; combining Tech requires three through six. The six separate public module cards remain unowned until the actual final Mentat assignment, using the board after effects such as Gamont. Neither Semuta claim nor its original closing CHOAM continuation awards a Stronghold Card early. Skills, other overlays and the separate Basic Moritani entry remain excluded.', 'A mandatory loser-card discard may also offer Semuta while the original winner’s compulsory used card remains reserved. For native Ixians, claim or Continue settles the original casualties, then restores the one-for-one Suboid/Cyborg substitution and its Karama response. The winning used Poison Tooth is not discarded until substitution or decline finishes; only then is that physical Tooth a separate fresh target before optional winner cleanup.', 'In the Basic native Moritani entry, winner compulsory/optional card discards may pause before the losing ally’s original retention choice. After that ally chooses one eligible played card or declines, native Karama finishes first; the completed unkept-card batch then offers Semuta. A kept or unrelated hand card is never a target. Multiple fresh cards require commitment and one private selection; taking a winner’s card does not add it to the original retention pool. Advanced Moritani, Nexus retention and unresolved casualty parents remain separate.', 'A printed Karama canceling native Harkonnen capture, CHOAM Auditor or quoted battle income, Ixian casualty substitution, or Basic Moritani allied retention now offers that exact public cost before the original cancellation finishes. Claim or Continue restores the saved response and cancels once. Acquiring the cost card cannot restore a captive, inspect a hand, pay the canceled income, exchange casualties or keep the proposed allied card. Later native card cleanup and markets still finish; Worthless conversion, other cancellations and module combinations remain separate.', diff --git a/tests/fixture-semuta-strongholds.ts b/tests/fixture-semuta-strongholds.ts new file mode 100644 index 00000000..df2f0cd3 --- /dev/null +++ b/tests/fixture-semuta-strongholds.ts @@ -0,0 +1,43 @@ +import assert from 'node:assert/strict'; +import {applyAction,createGame,joinGame,newPlayer,initializeSemutaGameForAudit,viewGame,type Game} from '../game/engine'; +import {botActions} from '../game/bots'; +import {SEMUTA_DRUG_ID} from '../game/semuta-drug'; + +export function initializeSemutaStrongholdsGame(count=3,tech=false):Game { + const factions=['richese','atreides','choam','ixians','tleilaxu','fremen'] as const; + let game=createGame(`SEMUTASH${count}${tech?'T':''}`,newPlayer('r','Richese','richese'),true,count>=4?['choam','ix']:['choam']); + const ids=['r','a','e','i','t','f']; + for(let n=1;ntoken.owner)); + return game; +} +export function semutaStrongholdsClosingFixture(tech=false) { + const game=initializeSemutaStrongholdsGame(3,tech); + const cards=[...game.deck,...game.discard,...game.richeseCache!,...game.players.flatMap(p=>p.hand)].map(c=>c.id).sort(); + game.deck.push(...game.players.flatMap(p=>p.hand));for(const player of game.players)player.hand=[]; + const take=(id:string)=>{for(const zone of [game.deck,game.discard,game.richeseCache!]){const index=zone.findIndex(card=>card.id===id);if(index>=0)return zone.splice(index,1)[0];}throw Error(`Missing physical ${id}`);}; + const semuta=take(SEMUTA_DRUG_ID),gamont=take(game.deck.find(c=>c.name==='Trip to Gamont')!.id); + game.players.find(p=>p.id==='r')!.hand.push(semuta);game.players.find(p=>p.id==='e')!.hand.push(gamont); + for(const player of game.players){player.reserves+=player.tanks+Object.values(player.forces).reduce((sum,n)=>sum+n,0);player.forces={};player.tanks=0;} + const target=game.players.find(p=>p.id==='a')!; + target.reserves-=4;target.forces={'arrakeen:10':3,'carthag:11':1}; + Object.assign(game,{turn:2,phase:7,active:null,movementRemaining:[],ready:[],response:null,decision:null,phaseOpening:null,stormPending:null}); + let state=game; + for(const id of ['a','e','r'])state=applyAction(state,id,{type:'ready'}); + state=applyAction(state,'e',{type:'decision',done:true}); + assert.equal(state.phase,8); + for(const id of ['a','e','r'])state=applyAction(state,id,{type:'ready'}); + state=applyAction(state,'e',{type:'decision',done:true}); + assert.equal(state.decision?.kind,'choamMentat'); + return {game:state,semuta:semuta.id,gamont:gamont.id,cards}; +} diff --git a/tests/semuta-strongholds.test.ts b/tests/semuta-strongholds.test.ts new file mode 100644 index 00000000..20c0b29a --- /dev/null +++ b/tests/semuta-strongholds.test.ts @@ -0,0 +1,38 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import {applyAction,type Game} from '../game/engine'; +import {semutaStrongholdsClosingFixture} from './fixture-semuta-strongholds'; +const reload=(game:Game):Game=>JSON.parse(JSON.stringify(game)); +const inventory=(game:Game)=>[...game.deck,...game.discard,...game.richeseCache!,...game.players.flatMap(p=>p.hand)].map(c=>c.id).sort(); +for(const tech of [false,true]) void test(`Closing Semuta Gamont preserves original Stronghold claim timing${tech?' with native Tech':''}`,()=>{ + const f=semutaStrongholdsClosingFixture(tech),before=f.game; + assert.equal(before.strongholdCards!.claimedTurn,0); + assert.equal(before.strongholdCards!.owners.arrakeen,null); + assert.equal(before.strongholdCards!.owners.carthag,null); + let pending=applyAction(before,'e',{type:'card',mode:'choam',card:f.gamont,target:'a',from:'carthag:11',elite:0}); + while(pending.response){const actor=pending.players.find(p=>!pending.response!.passed.includes(p.id))!;pending=applyAction(pending,actor.id,{type:'passResponse'});} + assert.equal(pending.pendingTreacheryDiscard?.reaction?.stage,'offer'); + const event=pending.pendingTreacheryDiscard!.batch.event; + const originalCards=structuredClone(pending.strongholdCards),originalTech=structuredClone(pending.techTokens); + for(const claim of [false,true]) { + let resumed=reload(pending); + if(claim)resumed=applyAction(resumed,'r',{type:'semutaCommit',event}); + else for(const id of ['a','e','r'])resumed=applyAction(resumed,id,{type:'semutaPass',event}); + assert.equal(resumed.decision?.kind,'choamMentat');assert.equal(resumed.decision?.player,'e'); + assert.deepEqual(resumed.players.find(p=>p.id==='a')!.forces,{'arrakeen:10':3}); + assert.equal(resumed.players.find(p=>p.id==='a')!.reserves,17); + assert.deepEqual(resumed.strongholdCards,originalCards);assert.deepEqual(resumed.techTokens,originalTech); + assert.throws(()=>applyAction(resumed,'a',{type:'decision',done:true})); + const next=applyAction(resumed,'e',{type:'decision',done:true}); + assert.equal(next.strongholdCards!.claimedTurn,2); + assert.equal(next.strongholdCards!.owners.arrakeen,'a'); + assert.equal(next.strongholdCards!.owners.carthag,null); + assert.equal(next.status,'playing');assert.equal(next.turn,3);assert.equal(next.phase,0); + assert.deepEqual(next.techTokens,originalTech); + assert.equal(next.players.find(p=>p.id==='r')!.hand.some(c=>c.id===f.gamont),claim); + assert.equal(next.discard.some(c=>c.id===f.semuta),claim); + assert.deepEqual(inventory(next),f.cards); + assert.throws(()=>applyAction(next,'r',{type:'semutaCommit',event})); + assert.throws(()=>applyAction(next,'e',{type:'decision',done:true})); + } +}); diff --git a/tools/start-prototype.ts b/tools/start-prototype.ts index 6b44a659..eea757f9 100644 --- a/tools/start-prototype.ts +++ b/tools/start-prototype.ts @@ -25,7 +25,7 @@ async function main() { console.log( 'Usage: node --import tsx tools/start-prototype.ts --profile ix|discovery|homeworld-occupation|leader-skills|banker-income|factions|stronghold-factions|nexus|moritani-assassinate|ecaz-treachery|ecaz-occupy|semuta|kull|richese-betrayal|nexus-kull|guild-betrayal|ixian-replacement|ixian-betrayal|harkonnen-betrayal --db PATH --room CODE --version NUMBER --out /private/new-directory\nStarts only a fresh ready lobby for the selected development prototype. Stronghold-factions opts a fresh ready 2–6-seat Advanced roster with at least one selected native Ixians, Tleilaxu, CHOAM or Richese faction and otherwise classic opponents into Stronghold Cards, optionally preserving unused Tech Tokens at 3–6 seats. Select one or both distinct Ix/CHOAM decks; each native faction requires its family deck. The selected decks supply 47 Ix or combined, or 35 CHOAM canonical physical Treachery Cards, with a separate ten-card Richese cache when Richese plays. Native setup choices, HMS placement, Face Dancers, Richese cache and CHOAM Auditor remain genuine; custody starts unowned and is claimed by actual END Mentat. It does not redeal, reset or retrofit a saved game. Banker-income opts a fresh 2–6-seat classic Basic/Advanced base-deck roster, supported Basic Ixian/Tleilaxu/CHOAM/Moritani skill roster with its single required deck, or Advanced native Ixians, Tleilaxu and/or CHOAM plus classic roster with distinct required family decks, into automatic deferred normal income and native Mentat collection with all fourteen Leader Skills. Original bank payer costs exclude the separate Tleilaxu free-revival award; already-earned spice stays with its original faction under the existing provisional policy. Nexus enables the existing private Nexus setup. Moritani-assassinate explicitly opts into the bounded Advanced assassination preview. Semuta opts a fresh Richese roster into bounded clean-discard reactions; an included Ix deck supplies physical Thumper without opening public starts. Kull opts a fresh eligible CHOAM roster into the user-selected Karama interception preview. Richese-betrayal opts a fresh paired CHOAM/Richese Nexus roster into cache-purchase veto and sale-bank diversion. Nexus-kull opts a fresh CHOAM-plus-classic roster with CHOAM/Ix decks and Nexus into source-aware printed or any-Treachery-fueled Cunning Kull. Guild-betrayal opts a fresh classic roster with native Guild into full funded shipment-payment replacement; it preserves an explicitly enabled Homeworld module for Junction routes. Ixian-replacement opts a fresh classic Basic/Advanced base-deck roster, or Basic Tleilaxu plus classic opponents with the original Ix47 deck and no optional Sandtrout, into the exact normal-auction purchased-card replacement with Nexus alone. Native Ixians, Harkonnen buyers and special purchase sources remain unsupported. Ixian-betrayal opts a fresh 2–6-seat Basic/Advanced native Ixian plus classic/Tleilaxu roster with Ix 47-card Treachery and Nexus only into prevention of one actual native Bidding draw or Advanced Technology exchange, after native Karama counters. Harkonnen-betrayal opts a fresh 2–6-seat classic Basic/Advanced roster with native Harkonnen, base 33-card Treachery and Nexus alone into cancellation of an actual declared personal or allied Harkonnen Traitor Card after native counters, physical retirement and shuffle, and one private actual Mentat replacement. Modules, overlays, public starts and old-game retrofits remain excluded except for each profile’s documented composition. Nexus is not dealt initially: a qualifying alliance requires at least three seats; two unallied receivers require four. Backs up all rooms first, preserves sessions and existing games, and rejects stale versions. Normal game-start and publication gates remain closed.', '\nSemuta additionally admits a fresh Basic roster with native Richese and Moritani, classic opponents, exactly CHOAM and Ecaz decks, and no optional modules. Native setup remains genuine. Private Sabotage and Robbery overflow discards may offer Semuta before a clean ordinary shipment/movement arrival resumes. The victim/draw and original payment/forces are already committed; candidate faces stay hidden until commitment. Advanced Moritani, native Ecaz, Ix/E3 mixtures, worm/Ambassador/overlap arrivals and public starts remain outside this entry.', - '\nSemuta with CHOAM and optional Ix additionally preserves original unused Tech Tokens in fresh 3–6-seat Basic/Advanced Richese lobbies under existing native roster prerequisites. Original setup and first-Storm owners remain. Mandatory/winner card discards react before the original winner takes one loser-owned token, still before Face Dance. Other modules, the separate CHOAM/Ecaz Moritani entry, played-game retrofit and public starts remain excluded.', + '\nSemuta with CHOAM and optional Ix preserves original unused Tech Tokens in fresh 3–6-seat Basic/Advanced Richese lobbies under existing native prerequisites. Advanced additionally retains original unused Stronghold Cards at 2–6 seats; both modules require3–6. Native setup, first-Storm Tech and final-Mentat Stronghold assignment remain original. Mandatory/winner discards react before the original winner takes one loser-owned token, before Face Dance. Closing Gamont reacts before the original CHOAM finish and changed-board Stronghold claim. Skills, other modules, the separate CHOAM/Ecaz Moritani entry, played-game retrofit and public starts remain excluded.', '\nEcaz-occupy (--profile ecaz-occupy) opts only a fresh ready 2–6-seat lobby with native Ecaz and classic/Ixian/Tleilaxu/CHOAM/Richese opponents (optional native Moritani). Advanced uses distinct selected ecaz/ix/choam decks; Basic uses the exact ecaz deck with classic/optional Moritani opponents. Ordinary33/47/35 cards retain real original offers/HMS/Auditor/Face Dancers and Richese cache. Explicit --ecaz-treachery independently adds the three physical variant cards, making36/50/38 (Advanced only). Basic combined battles require an even Ecaz force count; the preserved publisher odd-force casualty-rounding conflict rejects before any lead choice. Native Richese ordinary and marker-only pools compose with selected lead plans; original own mixed No-Field dialing, whole-plan disclosure and optional overlays remain guarded.' + '\nLeader-skills retains existing classic and Basic native entries and adds native Basic Richese/CHOAM. Advanced Ixians, Tleilaxu, CHOAM and/or Richese plus classic rosters use distinct required family decks and all fourteen skills, with original cache, Face Dancers, Auditor exclusion and typed aftermath. Foreign revival grants no new skill. Separate non-Harkonnen Advanced Moritani uses the ecaz deck and original skill-first assassination preview. Native Ecaz supports its five ordinary discs, original Ambassador/Duke custody and ecaz deck with classic opponents in Basic/Advanced; Advanced Harkonnen retains its existing Duke capture exclusion. Normal Mentat questions use a uniform private card-show step in supported Leader Skills games. Banker-income additionally supports native Richese and Ecaz, never ordinary seller/allowed Emperor income or a bank grant as a payer cost. Basic auction recipients remain unchanged. Public activation, exhausted-cache counts, native Ixian Technology on Richese lots, own mixed No-Field, captured replacement, shared-Duke assignment, combined Occupy skills and other pending module/rulings remain guarded.', '\nStronghold-factions additionally admits standalone Advanced Ecaz OR Moritani with classic opponents and the exact ecaz deck, six Stronghold Cards with optional unused Tech Tokens at 3–6 seats. Original setup and actual end-Mentat claims remain. Ecaz holds shared-control cards and only the actual selected holder-plan benefits. Native Moritani retains original assassination with printed bounty and one private Mentat replacement; Harkonnen is excluded there. An E3 pair, E1/E2/E3 mixtures and other modules remain separate.' +