Connect paired E3 Skills without Nexus and retain native arrival independence

Reuse original paired roster validation with plain all14 native setup and optional independent cards. Preserve real coalition, trained death and Mentat paths; extend the existing paid Ambassador/Terror queue to paired Skills and card-only factions. Frozen29-file323/323, types/lint/build, native programmes and390px entry pass. No save conversion or deployment.
This commit is contained in:
Quintine committed 2026-10-17 21:12:35 +11:00
1 parent fde44baab5
commit 17adffa6a7
20 files changed
+573 -93

No files matched your search

+3 -4
View File
@@ -1,7 +1,7 @@
import { classicNexusModulesProfile, mixedE1E2NexusProfile, mixedE3NexusProfile, pairedE3NexusProfile, pairedNexusModulesProfile, singleE1E2NexusProfile, standaloneE3NexusProfile } from './nexus-module-profile';
import { nativeFactionTechProfile } from './faction-module-profile';
import { classicDiscoveryLeaderSkillsProfile, classicDiscoveryNexusProfile, homeworldDiscoveryProfile, discoveryModeSupported, discoveryModuleProfile, mixedE1E2DiscoveryNexusProfile, mixedE3DiscoveryNexusProfile, pairedDiscoveryNexusProfile, pairedE3DiscoveryNexusProfile, singleE1E2DiscoveryNexusProfile, standaloneE3DiscoveryNexusProfile } from './discovery-module-profile';
import { advancedMoritaniLeaderSkillsProfile, advancedNativeLeaderSkillsProfile, homeworldLeaderSkillsProfile, independentTreacheryLeaderSkillsProfile, classicNexusLeaderSkillsProfile, classicTechLeaderSkillsProfile, guildBetrayalNexusLeaderSkillsProfile, mixedE1E2NexusLeaderSkillsProfile, mixedE3NexusLeaderSkillsProfile, nativeDiscoveryLeaderSkillsProfile, nativeEcazLeaderSkillsProfile, nativeExpansionLeaderSkillsProfile, nativeTechLeaderSkillsProfile, ordinaryLeaderSkillModeSupported, pairedE3NexusLeaderSkillsProfile, pairedNexusLeaderSkillsProfile, singleE1E2NexusLeaderSkillsProfile, standaloneE3NexusLeaderSkillsProfile, strongholdLeaderSkillsProfile } from './leader-skill-profile';
import { advancedMoritaniLeaderSkillsProfile, advancedNativeLeaderSkillsProfile, homeworldLeaderSkillsProfile, independentTreacheryLeaderSkillsProfile, classicNexusLeaderSkillsProfile, classicTechLeaderSkillsProfile, guildBetrayalNexusLeaderSkillsProfile, mixedE1E2NexusLeaderSkillsProfile, mixedE3NexusLeaderSkillsProfile, nativeDiscoveryLeaderSkillsProfile, nativeEcazLeaderSkillsProfile, nativeExpansionLeaderSkillsProfile, nativeTechLeaderSkillsProfile, ordinaryLeaderSkillModeSupported, pairedE3LeaderSkillsProfile, pairedE3NexusLeaderSkillsProfile, pairedNexusLeaderSkillsProfile, singleE1E2NexusLeaderSkillsProfile, standaloneE3NexusLeaderSkillsProfile, strongholdLeaderSkillsProfile } from './leader-skill-profile';
import { bribeTimingBlock, maximumBribe, type BribeOptions } from './bribe-options';
import { quoteSpicePlacement, stormExposesTerritory, stormSectorAfter, wormConsumesForces } from './disaster-rules';
import { isStormCardDistance, type StormCardComponent } from './storm-cards';
@@ -16264,14 +16264,13 @@ function ambassadorEffectBlock(
function arrivalOverlapModeSupported(g: Game): boolean {
if ((mixedE3NexusLeaderSkillsProfile(g) || mixedE3NexusProfile(g)) &&
byFaction(g, 'ecaz') && byFaction(g, 'moritani')) return true;
return pairedE3NexusLeaderSkillsProfile(g) || pairedE3NexusProfile(g) ||
return (!!g.leaderSkills && pairedE3LeaderSkillsProfile(g)) || pairedE3NexusLeaderSkillsProfile(g) || pairedE3NexusProfile(g) ||
(!!byFaction(g, 'ecaz') && !!byFaction(g, 'moritani') &&
g.players.every(player => CLASSIC_FACTIONS[player.faction] === true ||
player.faction === 'ecaz' || player.faction === 'moritani') &&
g.expansions.every(expansion => expansion === 'ecaz') &&
!g.homeworlds && !g.nexusCards && !g.leaderSkills &&
!g.discoveryEnabled && !g.techTokens && !g.strongholdCards &&
!g.ecazTreachery);
!g.discoveryEnabled && !g.techTokens && !g.strongholdCards);
}
function arrivalOverlapIntegrity(g: Game) {
const record = g.pendingArrivalOverlap;
+27 -6
View File
@@ -166,6 +166,10 @@ export function mixedE1E2NexusLeaderSkillsProfile(game: LeaderSkillProfile): boo
export function pairedE3NexusLeaderSkillsProfile(game: LeaderSkillProfile): boolean {
if (game.expansions.length !== 1 || game.expansions[0] !== 'ecaz' ||
!nexusLeaderSkillModulesSupported(game, game.advanced === true, true)) return false;
return pairedE3LeaderSkillRoster(game);
}
function pairedE3LeaderSkillRoster(game: LeaderSkillProfile): boolean {
const players = game.players!;
let ecaz = false, moritani = false;
for (let i = 0; i < players.length; i++) {
@@ -184,6 +188,20 @@ export function pairedE3NexusLeaderSkillsProfile(game: LeaderSkillProfile): bool
return ecaz && moritani;
}
/** Both original E3 natives without an unrelated module; independent cards are optional. */
export function pairedE3LeaderSkillsProfile(game: LeaderSkillProfile): boolean {
if (game.expansions.length !== 1 || game.expansions[0] !== 'ecaz' ||
typeof game.advanced !== 'boolean' || !game.players ||
game.players.length < 2 || game.players.length > 6 ||
(!noOtherLeaderSkillModules(game) && !independentTreacheryLeaderSkillsProfile(game)) ||
game.discoveryStash || game.greatMaker || game.semutaPreview || game.advancedPreview ||
(game.moritaniAssassinatePreview && !game.advanced) ||
game.kullPreview || game.nexusKullPreview || game.guildBetrayalPreview ||
game.richeseBetrayalPreview || game.nexusIxianReplacementPreview ||
game.nexusIxianBetrayalPreview || game.nexusHarkonnenBetrayalPreview) return false;
return pairedE3LeaderSkillRoster(game);
}
/** Original two/three selected families with native E3, Nexus and optional original modules. */
export function mixedE3NexusLeaderSkillsProfile(game: LeaderSkillProfile): boolean {
return nexusLeaderSkillModulesSupported(game, game.advanced === true, true) &&
@@ -304,6 +322,7 @@ export function strongholdLeaderSkillsProfile(game: LeaderSkillProfile): boolean
/** Bounded integration of the ordinary Moritani roster, not Advanced assassination. */
export function basicMoritaniLeaderSkillsProfile(game: LeaderSkillProfile): boolean {
if (game.advanced === false && pairedE3LeaderSkillsProfile(game)) return true;
return game.advanced === false && game.expansions.length === 1 &&
game.expansions[0] === 'ecaz' && e3LeaderSkillModulesSupported(game) &&
!!game.players?.some(player => player.faction === 'moritani') &&
@@ -313,6 +332,7 @@ export function basicMoritaniLeaderSkillsProfile(game: LeaderSkillProfile): bool
/** Existing bounded Advanced assassination roster, composed with native skills. */
export function advancedMoritaniLeaderSkillsProfile(game: LeaderSkillProfile): boolean {
if (game.advanced === true && pairedE3LeaderSkillsProfile(game)) return true;
const players = game.players;
if (game.advanced !== true || game.expansions.length !== 1 ||
game.expansions[0] !== 'ecaz' || !players || players.length < 2 || players.length > 6 ||
@@ -338,6 +358,7 @@ export function advancedMoritaniLeaderSkillsProfile(game: LeaderSkillProfile): b
/** Ecaz's five native discs; the separate temporarily controlled Duke is not an assignment disc. */
export function nativeEcazLeaderSkillsProfile(game: LeaderSkillProfile): boolean {
if (pairedE3LeaderSkillsProfile(game)) return true;
const players = game.players;
if (typeof game.advanced !== 'boolean' || game.expansions.length !== 1 ||
game.expansions[0] !== 'ecaz' || !players || players.length < 2 || players.length > 6 ||
@@ -508,7 +529,7 @@ export function homeworldLeaderSkillsProfile(game: LeaderSkillProfile): boolean
return true;
}
/** Independent cards with classic, selected E1/E2 or standalone E3 all14 setup. */
/** Independent cards with classic, selected E1/E2 or standalone/paired E3 all14 setup. */
export function independentTreacheryLeaderSkillsProfile(game: LeaderSkillProfile): boolean {
const e3 = game.expansions.length === 1 && game.expansions[0] === 'ecaz';
if (game.ecazTreachery !== true || game.expansions.length > 2 ||
@@ -525,21 +546,21 @@ export function independentTreacheryLeaderSkillsProfile(game: LeaderSkillProfile
for (let j = 0; j < i; j++) if (game.expansions[j] === game.expansions[i]) return false;
}
const players = game.players;
let e3Natives = 0;
let nativeE3 = false;
for (let i = 0; i < players.length; i++) {
for (let j = 0; j < i; j++) if (players[j].faction === players[i].faction) return false;
let selected = false;
for (const faction of FACTIONS)
if (faction.id === players[i].faction) {
selected = faction.expansion === 'base' || game.expansions.includes(faction.expansion);
if (faction.expansion === 'ecaz') e3Natives++;
if (faction.expansion === 'ecaz') nativeE3 = true;
break;
}
if (!selected) return false;
}
// Preserve the original standalone native Skills/Duke roster boundaries.
if (e3 && (e3Natives > 1 || (e3Natives > 0 && game.advanced &&
players.some(player => player.faction === 'harkonnen')))) return false;
// Preserve the native Advanced Skills/Harkonnen and shared-Duke boundaries.
if (e3 && nativeE3 && game.advanced &&
players.some(player => player.faction === 'harkonnen')) return false;
if (game.moritaniAssassinatePreview && (!e3 || !game.advanced ||
!players.some(player => player.faction === 'moritani'))) return false;
return true;
+4 -4
View File
@@ -1429,7 +1429,7 @@ export const RULE_TOPICS: RuleTopic[] = [
'Before any initial Traitor Cards are drawn, randomly select one of the five ordinary Ecaz Traitor Cards and set it aside face up. Everyone may inspect it. It remains outside the deck for the rest of the game, including later Traitor and Face Dancer draws.',
'Loyalty is automatic and cannot be stopped with Karama. It creates no player choice, payment or acknowledgment. The card names a leader, but does not remove, kill, transfer or otherwise change that leader disc. Duke Vidal has no Traitor Card and is never a candidate.',
'In new Advanced development games with Ecaz, the table displays the selected card before the Traitor choices. Inspect traitor enlarges its identity; refreshing or resuming the saved game keeps the same card. Basic games and games without native Ecaz do not use this power.',
'Older saved games that have already dealt their Traitor Cards retain that inventory; they do not gain a retroactive Loyalty draw. Bounded native Ecaz Leader Skills retain this original draw after public assignment and now support their original allied Arrakis Occupy without an unrelated module. Unsupported Moritani/shared-Duke and other roster compositions remain guarded.',
'Older saved games that have already dealt their Traitor Cards retain that inventory; they do not gain a retroactive Loyalty draw. Standalone or paired native Ecaz/Moritani all14 Skills retains the original draw after public assignment, with optional independent cards and no unrelated modules. Ordinary allied Arrakis Occupy remains connected; shared-Duke training, Advanced Harkonnen and unsupported roster/module combinations stay guarded.',
],
checklist: [
{ area: 'Implementation', status: 'Partial', detail: 'One persisted native Ecaz card is selected before genuine initial dealing, excluded from setup and Nexus deck construction, and validated against all known circulating or retired Traitor locations. Older saves stay unchanged. Broader combinations remain gated.', evidence: ['game/ecaz-loyalty.ts', 'game/engine.ts'] },
@@ -3236,7 +3236,7 @@ export const RULE_TOPICS: RuleTopic[] = [
'A Mentat replacement is an ordinary physical Traitor Card, including the original Ix Hero identity when present. After original owned initialization, retire the victim permanently. The replacement itself may later be legally exchanged, returned and drawn again; keep its old receipt unchanged and count one live card, not one per historical reference.',
'The authorized revised Advanced PDF, physical page35, requires that you have not revealed your Traitor Card normally this game. A normal own-card reveal therefore forfeits Assassinate Leaders for the rest of this game, across turns and opponents. Existing state preserves that forfeiture; Karama has no effect against the ability. This is the authorized source, not a new GF9 erratum.',
'The surviving actual played disc need not be native to its victorious controller. Native Ecaz/Duke6, Tleilaxu/foreign Guild5, CHOAM/Auditor2 and Harkonnen captives use the winner’s printed-faction targets. Captivity or Ghola control never rewrites native identity. Auditor has a matching Traitor outside CHOAM’s ordinary five; it can be a different native target for two but not the disc just played. Duke has no Traitor, and Cheap Hero is not a physical opposing disc. Exceptional captured/Ghola targets remain separate.',
'Terror Assassination chooses a random leader when its token is triggered. It does not use this battle-loss condition, different-leader Traitor Card or once-per-faction allowance. Fresh supported plain/card Advanced Moritani starts initialize this printed advantage automatically; Basic never gains it. Independent cards also compose standalone Moritani/classic all14 Skills without other modules, retaining the Advanced Harkonnen and E3 pair/mixed Skills guards. Played saves do not gain invented prior-call history.',
'Terror Assassination chooses a random leader when its token is triggered. It does not use this battle-loss condition, different-leader Traitor Card or once-per-faction allowance. Fresh supported plain/card Advanced Moritani starts initialize this printed advantage automatically; Basic never gains it. Independent cards also compose standalone or paired Ecaz/Moritani/classic all14 Skills without other modules, retaining Advanced Harkonnen and mixed-family Skills guards. Played saves do not gain invented prior-call history.',
],
checklist: [
{area:'Implementation',status:'Partial',detail:'Canonical played-disc/native target semantics remain. Moritani replacement accepts physical Hero separately; permanent victim cards join the shared removed census, while historical replacement IDs may move/recur legally. Exchange, Face Dancer, Betrayal and Rihani consumers bind their own source zone. Current authorization/census, revealed-card reservation and original parent remain.',evidence:['game/moritani-assassinate.ts','game/nexus-traitor-exchange.ts','game/nexus-face-dancers.ts','game/nexus-harkonnen-betrayal.ts','game/rihani-decipherer.ts','game/engine.ts']},
@@ -4929,9 +4929,9 @@ export const RULE_TOPICS: RuleTopic[] = [
steps: [
'Fresh all14 Leader Skills preserves original classic, supported E1/E2 or standalone Ecaz OR Moritani setup with Homeworlds/Discovery/Tech3+/Advanced Strongholds under each envelope. Explicit --nexus-cards additionally admits single/paired one-family E1/E2, BOTH selected Ix+CHOAM native families/union47 and standalone OR paired Ecaz/Moritani/ecaz33 with classics. Basic/Advanced2–6, original Ix pre-training offer, ordinary native discs and actual hands remain; Auditor/shared Duke never train. Winning normal/trained Suk uses source-labelled native, visitor or nested fixed-loss pools; native saved counters stay at that home and external Emperor returns allocate original homes. Original phase-end industry, held benefits, cleanup/Tech before Face Dance and native assassination remain; unreconciled bands, mixed E3/three-family, public starts and played-save conversion stay separate.',
'Explicit guild-betrayal --leader-skills retains the original classic native-Guild Nexus profile without Homeworlds, Tech, Strongholds, Discovery, Semuta or other overlays. All14 training remains native. Ordinary skill quotes now admit the composition, restoring actual Mentat private questions before posture and Sandmaster ground collection rather than only enabling Bureaucrat. Native card custody, paid forces, original Betrayal Use/Pass and movement/plan continuation stay unchanged. Other combinations and complete-mode acceptance remain separate.',
'Basic Moritani retains base opponents including Harkonnen. Its separate bounded Advanced classic/non-Harkonnen roster now composes the original assassination preview and all fourteen skills: winner Suk rescue and Rihani choice finish first, then an eligible different trained disc may be assassinated with its exact card return and printed bounty. No casualties repeat; original set-aside and one private Mentat replacement remain. See Moritani with Leader Skills for the exact scope.',
'Basic Moritani retains classic opponents including Harkonnen. Standalone or paired Ecaz/Moritani Skills without other modules, optionally independent cards, retains native setup and the Advanced non-Harkonnen assassination roster. Winner Suk rescue and Rihani choice finish first, then an eligible different trained disc may be assassinated with its exact skill return and printed bounty. No casualties repeat; original set-aside and one private Mentat replacement remain. Shared-Duke training and mixed-family Skills stay guarded.',
'Fresh Advanced profiles compose native Ixians, Tleilaxu, CHOAM and/or Richese with classic opponents and distinct required family decks. All fourteen skills and native starts remain. Auditor stays unskilled; cyborg casualties, original CHOAM support and Richese capped marker revelation remain physical. Foreign revival creates no new skill. Supplied Advanced p34 excludes Richese auctions from Ixian Technology; own mixed No-Field, captured replacement, Advanced Atreides Suk/KH and unrelated overlays retain their guards.',
'The independent Recruits, Reinforcements and Harass & Withdraw cards may be selected with all fourteen Skills in fresh classic, selected Ix/CHOAM native/base or standalone Ecaz OR Moritani/classic Basic/Advanced games, without other modules. Original36/38/50 decks, Richese cache, native setup, free-revival exceptions and paid Cyborg prices stay. Card fee/withdrawal/Suk and ordinary Smuggler, Planetologist and Sandmaster capabilities share admission. Ecaz retains selected-lead/separate armies; Advanced Moritani keeps skill-first assassination and one Mentat replacement. Advanced Harkonnen, E3 native-pair/mixed Skills and other module guards remain.',
'The independent Recruits, Reinforcements and Harass & Withdraw cards may be selected with all fourteen Skills in fresh classic, selected Ix/CHOAM native/base or standalone/paired Ecaz/Moritani/classic Basic/Advanced games, without other modules. Original36/38/50 decks, Richese cache, native setup, free-revival exceptions and paid Cyborg prices stay. Card fee/withdrawal/Suk and ordinary Smuggler, Planetologist and Sandmaster capabilities share admission. Ecaz retains selected-lead/separate armies; Advanced Moritani keeps skill-first assassination and one Mentat replacement. Advanced Harkonnen, mixed E3 families and other module guards remain.',
'Fresh three-through-six-seat Basic/Advanced development tables may compose all fourteen Skills with original Tech Tokens: classic factions, supported E1/E2 native families, or standalone Ecaz/Moritani with the exact ecaz deck and original roster exclusions. Starting cards/cache, skill/Traitor choices and printed/first-Storm assignment stay genuine. Existing leader-skills/banker-income entry preserves unused selected tokens; mixed E3 families, other overlays, public activation and saved-game conversion remain separate.',
'Fresh Advanced classic or supported native two-through-six-seat skill tables may retain six original Stronghold Cards, with Tech at three or more seats. Original end-Mentat/local held effects remain. Arrakeen changes actual payer cost, not rescue or Banker eligibility; native CHOAM income retains bank support. Physical defenses, normal disciplines, holder ties and original dial/Worthless earnings remain. Standalone Ecaz Duke/Arrakis coalition and Moritani assassination compose original rules; mixed E3, exceptional copied defenses, active skill/Stone and other guards stay separate.',
'Fresh classic Leader Skills may explicitly add Nexus through local leader-skills --nexus-cards, Basic/Advanced two through six players, retaining selected original Tech at three or more seats and/or Advanced Strongholds. --discoveries additionally composes original Discovery7/8 in this classic profile. Saved hands/offers remain authoritative; the actual end-Spice closing draw follows Maker votes/typed rides, both Advanced piles and settled alliance. Free-return ledgers, bank purchase and Traitor Deck declarations stay distinct from training. Original phase-end industry, held payer support/physical defenses, physical Suk rescue and subsequent mandatory winner Tech rewards retain their own timing. Homeworlds, Banker/Mentat previews, borrowed Smuggler arithmetic and unresolved interactions remain separate.',