Connect Homeworld occupation with original phase modules
This commit is contained in:
1 parent
0a98a92f4b
commit
7f9daa10fa
19 files changed
+796
-40
No files matched your search
+33
-14
@@ -17,6 +17,7 @@ import { MovementClaimError, liveMovementPromises, movementEventAllowed, movemen
|
||||
import { findMovementCompletion, type MovementCompletion } from './movement-promise-search';
|
||||
import { classicNexusModulesProfile, mixedE1E2NexusProfile, mixedE3NexusProfile, pairedE3NexusProfile, pairedNexusModulesProfile, singleE1E2NexusProfile, standaloneE3NexusProfile } from './nexus-module-profile';
|
||||
import { independentDiscoveryProfile, independentNexusProfile, nexusCoreProfile, nativeFactionTechProfile, selectedFactionStrongholdProfile, selectedIndependentCardsProfile } from './faction-module-profile';
|
||||
import { homeworldPhaseModulesProfile } from './homeworld-module-profile';
|
||||
import { classicDiscoveryLeaderSkillsProfile, classicDiscoveryNexusProfile, homeworldDiscoveryProfile, discoveryModeSupported, discoveryModuleProfile, mixedE1E2DiscoveryNexusProfile, mixedE3DiscoveryNexusProfile, pairedDiscoveryNexusProfile, pairedE3DiscoveryNexusProfile, singleE1E2DiscoveryNexusProfile, standaloneE3DiscoveryNexusProfile } from './discovery-module-profile';
|
||||
import { advancedMoritaniLeaderSkillsProfile, selectedDeckLeaderSkillsProfile, 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';
|
||||
@@ -9836,12 +9837,12 @@ function moritaniAssassinateModeSupported(g: Game) {
|
||||
(f.expansion === 'base' || g.expansions.includes(f.expansion)))) &&
|
||||
(!g.nexusCards || (!!g.leaderSkills && standaloneE3NexusLeaderSkillsProfile(g))) &&
|
||||
(!g.leaderSkills || advancedMoritaniLeaderSkillsProfile(g)) &&
|
||||
(!g.strongholdCards || selectedFactionStrongholdProfile(g) || e3StrongholdFactionProfile(g) || independentCardModulesProfile(g) ||
|
||||
(!g.strongholdCards || homeworldPhaseModulesProfile(g) || selectedFactionStrongholdProfile(g) || e3StrongholdFactionProfile(g) || independentCardModulesProfile(g) ||
|
||||
(!!g.leaderSkills && strongholdLeaderSkillsProfile(g))) &&
|
||||
(!g.homeworlds || g.homeworldOccupationPreview === true || ecazHomeworldProfile(g) ||
|
||||
(!!g.leaderSkills && homeworldLeaderSkillsProfile(g))) &&
|
||||
!g.discoveryEnabled && !g.discoveries &&
|
||||
(!g.techTokens || nativeFactionTechProfile(g) || independentCardModulesProfile(g) ||
|
||||
(!g.techTokens || homeworldPhaseModulesProfile(g) || nativeFactionTechProfile(g) || independentCardModulesProfile(g) ||
|
||||
(!!g.leaderSkills && nativeTechLeaderSkillsProfile(g)));
|
||||
}
|
||||
function moritaniAssassinateContext(g: Game, receipt: MoritaniAssassinateReceipt) {
|
||||
@@ -10836,26 +10837,34 @@ export function initializeHomeworldGameForAudit(state: Game): Game {
|
||||
!!state.homeworlds,
|
||||
'Enable the Homeworld module in the audit lobby first.',
|
||||
);
|
||||
if (homeworldPhaseModulesProfile(state)) return initializeHomeworldOccupationGameForAudit(state);
|
||||
return initializeSetupGameForAudit(state, true);
|
||||
}
|
||||
/** Fresh occupation prototype; ambiguity remains source-gated rather than assigned a controller. */
|
||||
export function initializeHomeworldOccupationGameForAudit(state: Game, leaderSkills = false): Game {
|
||||
requireRule(state.homeworlds?.custody === null && !state.homeworldOccupationPreview &&
|
||||
!state.homeworldOccupationHistory && !state.nexusCards && !state.leaderSkills &&
|
||||
!state.techTokens && !state.strongholdCards && !state.discoveryEnabled &&
|
||||
((!state.techTokens && !state.strongholdCards) || (!leaderSkills && homeworldPhaseModulesProfile(state))) && !state.discoveryEnabled &&
|
||||
!state.discoveries && !state.discoveryStash && !state.greatMaker && !state.ecazTreachery &&
|
||||
typeof state.advanced === 'boolean' && state.expansions.length <= 3 &&
|
||||
state.expansions.every((id, index) => (id === 'ix' || id === 'choam' || id === 'ecaz') &&
|
||||
state.expansions.indexOf(id) === index) &&
|
||||
state.players.every(p => FACTIONS.some(f => f.id === p.faction &&
|
||||
(f.expansion === 'base' || state.expansions.includes(f.expansion)))),
|
||||
'Occupation requires a fresh Homeworld lobby with original selected faction decks and no other modules.');
|
||||
'Occupation requires a fresh Homeworld lobby with original selected faction decks and supported original phase modules.');
|
||||
requireFreshBaseRuntime(state);
|
||||
requireFreshFactionInventory(state);
|
||||
requireRule(!leaderSkills || !state.expansions.length && homeworldLeaderSkillsProfile(state),
|
||||
'Occupation with Leader Skills requires original classic Homeworlds without other modules.');
|
||||
const g = leaderSkills ? initializeLeaderSkillsGameForAudit(state)
|
||||
: initializeSetupGameForAudit(state, true, false, false, false, false, false, true);
|
||||
initializeHomeworldOccupationState(g);
|
||||
if (moritaniAssassinateModeSupported(g)) initializeMoritaniAssassinateState(g);
|
||||
return g;
|
||||
}
|
||||
/** Original occupation benefits belong to fresh World setup, beside its native
|
||||
* custody/history; phase modules do not replace those printed benefits. */
|
||||
function initializeHomeworldOccupationState(g: Game): void {
|
||||
g.homeworldOccupationPreview = true;
|
||||
g.pendingHomeworldOccupiedIncome = null;
|
||||
g.homeworldOccupiedIncomeHistory = [];
|
||||
@@ -10871,8 +10880,6 @@ export function initializeHomeworldOccupationGameForAudit(state: Game, leaderSki
|
||||
g.homeworldEcazVictoryPreview = true;
|
||||
g.ecazOccupyPreview = true;
|
||||
}
|
||||
if (moritaniAssassinateModeSupported(g)) initializeMoritaniAssassinateState(g);
|
||||
return g;
|
||||
}
|
||||
/** Shared classic/supported-native Nexus setup; existing composition guards apply. */
|
||||
export function initializeNexusGameForAudit(state: Game): Game {
|
||||
@@ -11849,6 +11856,7 @@ function initializeFactionExpansionsGame(state: Game, semuta = false): Game {
|
||||
return initialized;
|
||||
}
|
||||
function ecazOccupyCompositionSupported(g: Game): boolean {
|
||||
if (homeworldPhaseModulesProfile(g) && byFaction(g, 'ecaz')) return true;
|
||||
// Native Basic uses the same physical coalition and selected decks.
|
||||
// Family/roster checks below remain authoritative; optional configurations
|
||||
// retain their separate registered profiles rather than inheriting admission.
|
||||
@@ -12105,13 +12113,15 @@ function independentCardRoster(g: Game): boolean {
|
||||
return family === 'base' || g.expansions.includes(family);
|
||||
});
|
||||
}
|
||||
/** Bare card World/phase rosters keep their E3 envelope; Skills uses the shared registered capability. */
|
||||
/** Legacy World/card envelope; clean registered profiles are admitted before
|
||||
* this fallback, while other module contexts keep their original scope. */
|
||||
function independentCardModuleRoster(g: Game): boolean {
|
||||
return independentCardRoster(g) &&
|
||||
(!g.players.some(player => player.faction === 'ecaz' || player.faction === 'moritani') ||
|
||||
(g.expansions.length === 1 && g.expansions[0] === 'ecaz'));
|
||||
}
|
||||
function ecazHomeworldProfile(g: Game): boolean {
|
||||
if (homeworldPhaseModulesProfile(g)) return true;
|
||||
if (g.homeworlds && (independentNexusProfile(g) || independentDiscoveryProfile(g))) return true;
|
||||
if (g.homeworlds && g.leaderSkills && !g.sandtrout && independentTreacheryLeaderSkillsProfile(g)) return true;
|
||||
return !!g.homeworlds && g.ecazTreachery === true && independentCardModuleRoster(g) &&
|
||||
@@ -12128,6 +12138,7 @@ function ecazHomeworldProfile(g: Game): boolean {
|
||||
}
|
||||
/** Independent cards compose original phase modules, not unrelated previews. */
|
||||
function independentCardModulesProfile(g: Game): boolean {
|
||||
if ((g.techTokens || g.strongholdCards) && g.ecazTreachery === true && homeworldPhaseModulesProfile(g)) return true;
|
||||
if ((g.techTokens || g.strongholdCards) && selectedIndependentCardsProfile(g)) return true;
|
||||
if ((g.techTokens || g.strongholdCards) && (independentNexusProfile(g) || independentDiscoveryProfile(g))) return true;
|
||||
if ((g.techTokens || g.strongholdCards) && g.leaderSkills && !g.sandtrout &&
|
||||
@@ -12160,7 +12171,12 @@ export function initializeEcazTreacheryGameForAudit(state: Game): Game {
|
||||
const homeworlds = !!g.homeworlds;
|
||||
requireRule(!homeworlds || ecazHomeworldProfile(g),
|
||||
'Independent World cards need a supported selected-deck roster with original phase modules and supported Nexus selections.');
|
||||
if (homeworlds && homeworldPhaseModulesProfile(g)) {
|
||||
requireFreshBaseRuntime(g);
|
||||
requireFreshFactionInventory(g);
|
||||
}
|
||||
const initialized = initializeSetupGameForAudit(g, homeworlds, independentNexusProfile(g), false, discovery, false, false, true, true);
|
||||
if (homeworlds && homeworldPhaseModulesProfile(initialized)) initializeHomeworldOccupationState(initialized);
|
||||
if (byFaction(initialized, 'ecaz')) initialized.ecazOccupyPreview = true;
|
||||
if (moritaniAssassinateModeSupported(initialized)) initializeMoritaniAssassinateState(initialized);
|
||||
return initialized;
|
||||
@@ -12188,10 +12204,11 @@ function initializeSetupGameForAudit(state: Game, homeworlds: boolean, nexus = f
|
||||
mixedE3NexusLeaderSkillsProfile(g) || singleE1E2NexusLeaderSkillsProfile(g) || mixedE1E2NexusLeaderSkillsProfile(g) ||
|
||||
pairedE3NexusLeaderSkillsProfile(g) || standaloneE3NexusLeaderSkillsProfile(g))));
|
||||
const independentModules = ecazTreachery && independentCardModulesProfile(g);
|
||||
requireRule(!g.techTokens || ((skillTech || nativeTech || nexusModules || discoveryComposition || independentModules) &&
|
||||
const worldPhaseModules = homeworlds && homeworldPhaseModulesProfile(g);
|
||||
requireRule(!g.techTokens || ((worldPhaseModules || skillTech || nativeTech || nexusModules || discoveryComposition || independentModules) &&
|
||||
JSON.stringify(g.techTokens) === JSON.stringify(createTechTokens())),
|
||||
'Tech Tokens require a fresh supported three-through-six-seat lobby with unused tokens.');
|
||||
requireRule(!g.strongholdCards || strongholdFactions || ((skillStronghold || homeworldSkills || nexusModules || discoveryComposition || independentModules) &&
|
||||
requireRule(!g.strongholdCards || strongholdFactions || ((worldPhaseModules || skillStronghold || homeworldSkills || nexusModules || discoveryComposition || independentModules) &&
|
||||
JSON.stringify(g.strongholdCards) === JSON.stringify(createStrongholdCards())),
|
||||
'Stronghold Cards require a fresh supported Advanced lobby with unused cards.');
|
||||
requireRule(
|
||||
@@ -12202,8 +12219,8 @@ function initializeSetupGameForAudit(state: Game, homeworlds: boolean, nexus = f
|
||||
(discovery || !g.discoveryEnabled) &&
|
||||
(nexus || !g.nexusCards) &&
|
||||
(ecazTreachery || !g.ecazTreachery) &&
|
||||
(!g.techTokens || skillTech || nativeTech || nexusModules || discoveryComposition || independentModules) &&
|
||||
(strongholdFactions || skillStronghold || homeworldSkills || nexusModules || discoveryComposition || independentModules || !g.strongholdCards) &&
|
||||
(!g.techTokens || worldPhaseModules || skillTech || nativeTech || nexusModules || discoveryComposition || independentModules) &&
|
||||
(worldPhaseModules || strongholdFactions || skillStronghold || homeworldSkills || nexusModules || discoveryComposition || independentModules || !g.strongholdCards) &&
|
||||
(homeworlds || !g.homeworlds) &&
|
||||
g.players.every((p) =>
|
||||
FACTIONS.some(
|
||||
@@ -18648,6 +18665,7 @@ function ambassadorEffectBlock(
|
||||
return null;
|
||||
}
|
||||
function arrivalOverlapModeSupported(g: Game, revivalArrival?: RevivalEntryParent): boolean {
|
||||
if (homeworldPhaseModulesProfile(g) && byFaction(g, 'ecaz') && byFaction(g, 'moritani')) return true;
|
||||
if (revivalArrival && g.homeworlds && byFaction(g, 'ecaz') && byFaction(g, 'moritani')) return true;
|
||||
if (bareBasicSemutaProfile(g) && byFaction(g, 'ecaz') && byFaction(g, 'moritani')) return true;
|
||||
if ((independentNexusProfile(g) || independentDiscoveryProfile(g)) && byFaction(g, 'ecaz') && byFaction(g, 'moritani')) return true;
|
||||
@@ -24203,9 +24221,10 @@ function homeworldOccupiedIncomeIntegrity(g: Game) {
|
||||
'Occupied income requires its original fresh occupation profile.');
|
||||
return;
|
||||
}
|
||||
requireRule(g.homeworldOccupationPreview === true && g.homeworlds && !g.nexusCards &&
|
||||
(!g.leaderSkills || !g.expansions.length && homeworldLeaderSkillsProfile(g)) &&
|
||||
!g.techTokens && !g.strongholdCards && !g.discoveryEnabled &&
|
||||
requireRule(g.homeworldOccupationPreview === true && g.homeworlds &&
|
||||
(homeworldPhaseModulesProfile(g) || (!g.nexusCards &&
|
||||
(!g.leaderSkills || !g.expansions.length && homeworldLeaderSkillsProfile(g)) &&
|
||||
!g.techTokens && !g.strongholdCards && !g.discoveryEnabled)) &&
|
||||
Array.isArray(history) && pending !== undefined,
|
||||
'The fresh occupation profile lost its original Homeworld module or income ledger.');
|
||||
const turns = new Set<number>();
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
import { FACTIONS } from './catalog';
|
||||
import type { FactionModuleProfile } from './faction-module-profile';
|
||||
|
||||
export type HomeworldPhaseModulesProfile = FactionModuleProfile & {
|
||||
homeworldOccupationPreview?: unknown;
|
||||
homeworldTupilePreview?: unknown;
|
||||
homeworldEcazVictoryPreview?: unknown;
|
||||
};
|
||||
|
||||
/** A real empty selection means classic decks. Do not emulate that case by
|
||||
* adding a selected family that is absent from the actual configuration. */
|
||||
function registeredRoster(game: HomeworldPhaseModulesProfile): boolean {
|
||||
if (!Array.isArray(game.expansions) || game.expansions.length > 3 ||
|
||||
!game.expansions.every(family => family === 'ix' || family === 'choam' || family === 'ecaz') ||
|
||||
new Set(game.expansions).size !== game.expansions.length ||
|
||||
!Array.isArray(game.players) || game.players.length < 2 || game.players.length > 6) return false;
|
||||
const seated = new Set<string>();
|
||||
for (const player of game.players) {
|
||||
if (!player || typeof player !== 'object' || Array.isArray(player) ||
|
||||
typeof player.faction !== 'string' || seated.has(player.faction)) return false;
|
||||
const faction = FACTIONS.find(faction => faction.id === player.faction);
|
||||
if (!faction || faction.expansion !== 'base' && !game.expansions.includes(faction.expansion)) return false;
|
||||
seated.add(player.faction);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
const optionalFlag = (value: unknown) => value === undefined || typeof value === 'boolean';
|
||||
|
||||
/** Configuration permission only: original setup validates freshness, and live
|
||||
* custody/history validators retain their own physical and occupation rules.
|
||||
* Optional independent cards do not require either phase module. */
|
||||
export function homeworldPhaseModulesProfile(game: HomeworldPhaseModulesProfile): boolean {
|
||||
if (!game || typeof game !== 'object' || !game.homeworlds || typeof game.advanced !== 'boolean' ||
|
||||
!registeredRoster(game) || !optionalFlag(game.ecazTreachery) ||
|
||||
(game.techTokens && game.players.length < 3) || (game.strongholdCards && !game.advanced) ||
|
||||
game.leaderSkills || game.nexusCards || game.discoveryEnabled || game.discoveries ||
|
||||
game.discoveryStash || game.greatMaker || game.sandtrout || game.spiceBankerIncomePreview ||
|
||||
game.semutaPreview || game.advancedPreview || game.kullPreview || game.nexusKullPreview ||
|
||||
game.guildBetrayalPreview || game.richeseBetrayalPreview || game.nexusIxianReplacementPreview ||
|
||||
game.nexusIxianBetrayalPreview || game.nexusHarkonnenBetrayalPreview) return false;
|
||||
if (![game.ecazOccupyPreview, game.moritaniAssassinatePreview, game.homeworldOccupationPreview,
|
||||
game.homeworldTupilePreview, game.homeworldEcazVictoryPreview].every(optionalFlag)) return false;
|
||||
const ecaz = game.players.some(player => player.faction === 'ecaz');
|
||||
const moritani = game.players.some(player => player.faction === 'moritani');
|
||||
const choam = game.players.some(player => player.faction === 'choam');
|
||||
return (!game.ecazOccupyPreview || ecaz) &&
|
||||
(!game.moritaniAssassinatePreview || game.advanced && moritani) &&
|
||||
(!game.homeworldTupilePreview || game.homeworldOccupationPreview === true && choam) &&
|
||||
(!game.homeworldEcazVictoryPreview || game.homeworldOccupationPreview === true && ecaz);
|
||||
}
|
||||
+6
-4
@@ -3901,7 +3901,7 @@ export const RULE_TOPICS: RuleTopic[] = [
|
||||
'Classic Advanced Nexus core also retains original Strongholds2–6 and optional Tech3–6 with base33 or selected independent36, optionally all14. Earn cards only at actual end-Mentat. Earned Arrakeen composes temporary-Sardaukar support and real Reinforcements reserve fees without making bank money personal; earned Carthag still requires a physical effective defense after an original Harkonnen call is canceled. Original cleanup, token reward, held custody and private Mentat replacement remain.',
|
||||
'Fresh Advanced all14 Skills uses registered actual seats and selected ordinary decks with original Strongholds2–6 and optional Tech3–6, including its independent-card/World envelope. Native E3 needs no Nexus or exact single family merely to use Skills. Original Nexus/Discovery/Semuta card contexts and Advanced role guards remain separate. Actual end-Mentat claims stay. Arrakeen funds only local holder-plan support, never native World support, personal cash or reserve fees; normal Banker uses the final payer debit. Rescue, cleanup, Tech and Face Dance retain their order.',
|
||||
'Ecaz/Moritani Skills may include other registered selected-family seats and unused selected ordinary decks, with original Worlds/phase modules and optional independent cards. Native five-disc training and original role exclusions remain. Normal Warmaster one on a temporary Duke never grants trained three. Winner Suk precedes unused-disc assassination, bounty, winner Tech and one Mentat replacement. Ecaz-held support belongs only to the selected Ecaz plan, not an allied plan; fixed and variable armies remain separate. Advanced E3/Harkonnen, shared Duke and effect-specific guards stay.',
|
||||
'Fresh independent Treachery previews retain clean registered selected-deck paired/mixed/all-three native rosters and classic-only unused decks, with optional Tech3–6 and Advanced Strongholds2–6. Basic mixed Ecaz also admits bare cards. Original end-Mentat custody, physical reserve fees, owner-only Harass returns and selected-winner Tech remain. Bare mixed E3 Worlds and separate Skills/Nexus/Discovery/Semuta profiles retain their own admission.',
|
||||
'Fresh independent Treachery previews retain clean registered selected-deck paired/mixed/all-three native rosters and classic-only unused decks, with optional Tech3–6 and Advanced Strongholds2–6. Basic mixed Ecaz also admits bare cards. Original end-Mentat custody, physical reserve fees, owner-only Harass returns and selected-winner Tech remain. Clean World cards use their registered occupation profile; Skills/Nexus/Discovery/Semuta retain separate admission.',
|
||||
'The six effects, public ownership and pre-plan choice are integrated in development games. Enable Stronghold Cards in an Advanced preview lobby. Complete Advanced compliance and every expansion interaction remain under verification.',
|
||||
],
|
||||
example:
|
||||
@@ -4001,7 +4001,7 @@ export const RULE_TOPICS: RuleTopic[] = [
|
||||
'Owning all three tokens counts as one additional stronghold at victory checks. An alliance cannot combine separate token holdings to qualify: one member must own the complete set. Multiple sides reaching the victory target can share the win.',
|
||||
'Token ownership and pending income are public and persist. Fresh registered selected-family all14 keeps original Tech3–6 Basic/Advanced and optional Advanced Strongholds2–6, including supported Worlds and independent cards, without an incidental Nexus or single-family E3 requirement. Original Nexus/Discovery/Semuta and role/effect guards remain. Rescue/board-spice precedes winner Tech and Face Dance. Mandatory normal Banker waits for Mentat, separate from phase-end Tech; fees/H returns are not revival/shipment. Native CHOAM opening income is not Charity. Two-player Tech and full combined acceptance remain unfinished.',
|
||||
'Without other optional overlays, fresh normal selected-deck starts retain original Tech3–6 Basic/Advanced with registered paired/mixed/all-three native families or classic-only seats and unused decks. Advanced Strongholds2–6 optionally compose. Native payments, typed casualties, selected Ecaz lead rewards and existing captured-leader Moritani assassination remain separate; split allied holdings never form a complete set. Explicit Semuta keeps its earlier scope. Existing provisional Basic odd Occupy, Richese rulings and unrelated profiles are not newly decided; played games are not converted.',
|
||||
'Clean independent-card selected-deck rosters include paired/mixed/all-three natives and classic-only unused families with optional Tech3–6 and Advanced Strongholds2–6. Recruits changes current native rates without repeating Axlotl or advancing payout. Harass deposits and reserve fees are not free Revival or off-planet shipment. Original phase income, mandatory coalition/assassination and winner rewards remain. Bare mixed E3 Worlds and unrelated module/role profiles keep separate admission.',
|
||||
'Clean independent-card selected-deck rosters include paired/mixed/all-three natives and classic-only unused families with optional Tech3–6 and Advanced Strongholds2–6. Recruits changes current native rates without repeating Axlotl or advancing payout. Harass deposits and reserve fees are not free Revival or off-planet shipment. Original phase income, mandatory coalition/assassination and winner rewards remain. Clean World cards retain registered occupation and original phase modules; unrelated module/role profiles keep separate admission.',
|
||||
'Classic Nexus core composes original Tech3–6 Basic/Advanced and optional Advanced Strongholds2–6 with base33 or selected independent36, optionally all14. Real first-Storm owners and actual token counts set income. A paid fixed Tleilaxu force group does not become free because Recruits is active; subsequent genuine free returns accrue one Axlotl pile, not one pile per request. Pending spice cannot fund Revival before phase end. Harass returns and reserve fees are not token-triggering Revival or shipment; actual battle winners take one original opposing token.',
|
||||
],
|
||||
example:
|
||||
@@ -4813,9 +4813,10 @@ export const RULE_TOPICS: RuleTopic[] = [
|
||||
).join(' '),
|
||||
steps: [
|
||||
'Homeworlds are an optional module from Ecaz & Moritani, available under the official rules with Basic or Advanced play. They are separate locations outside Arrakis; they do not receive ordinary storm movement or count as ordinary strongholds.',
|
||||
'Clean World starts admit registered classic/no-deck and selected-family paired/mixed/all-three rosters with optional independent cards, Tech3–6 and Advanced Strongholds2–6. Skills or Nexus are not incidental prerequisites. Fresh setup includes original occupation qualification, bank/percentage benefits, native Tupile and Ecaz victory state alongside physical locations; existing played games are not retrofitted. Original module-specific profiles and complete-mode gates stay separate.',
|
||||
'Fresh World all14 uses actual registered seats and selected ordinary decks in Basic/Advanced, including mixed Ecaz/Moritani with E1/E2 natives. Neither Nexus, independent cards, one exact family nor a seated native from every selected deck is required. Original Tech3+/Advanced Strongholds2+ may remain. Existing no-card Nexus/Discovery and Semuta keep their separate limits. Actual hands, Ix card-before-training and native five-disc assignment remain; Auditor/shared Duke never train. Native Suk saves stay at that home; external Emperor returns retain original typed destinations. Nested locations gain no ordinary Stronghold effect or invented saved history.',
|
||||
'Each faction uses its own card and matching world token. In Basic play the Emperor uses Kaitain alone. Advanced play adds Salusa Secundus for its Sardaukar; movement can transfer both force types between the two Emperor worlds.',
|
||||
'Registered selected-family World all14 composes independent36/38/50-card setup and original Tech3+/Advanced Strongholds2+, including mixed E3. Bare native E3 World cards retain their original roster limits. Recruits uses current native rates/typed limits and recorded paid/free use; Banker and phase-end Axlotl remain separate. Reinforcements seals exact normal-first sources and excludes an own-World fee from dial/casualties while preserving original face/support. Suk rescues committed losses, not fees. Harass returns actual visitors through native custody and is forbidden on every own World. Original Nexus/Discovery/Semuta and role/effect guards remain.',
|
||||
'Registered selected-family World all14 composes independent36/38/50-card setup and original Tech3+/Advanced Strongholds2+, including mixed E3. Clean bare World cards now use the registered selected-family profile with optional phase modules and original occupation state. Recruits uses current native rates/typed limits and recorded paid/free use; Banker and phase-end Axlotl remain separate. Reinforcements seals exact normal-first sources and excludes an own-World fee from dial/casualties while preserving original face/support. Suk rescues committed losses, not fees. Harass returns actual visitors through native custody and is forbidden on every own World. Original Nexus/Discovery/Semuta and role/effect guards remain.',
|
||||
'Native reserves determine high or low population. Salusa counts Sardaukar, while the other cards count the faction’s reserves there. Reaching the high face’s minimum restores high population. Occupation also applies the low penalty.',
|
||||
'Low population normally grants one additional free force revival and one additional bank spice when collecting CHOAM Charity. Salusa grants neither bonus; CHOAM’s Tupile reverses the usual pattern by granting an advantage while low and a penalty while high.',
|
||||
'A qualifying charity claim separates its ordinary payer from the extra bank spice. Inflation doubles both portions or prevents the whole collection. Canceling Bene Gesserit charity prevents both portions of that claim.',
|
||||
@@ -4871,6 +4872,7 @@ export const RULE_TOPICS: RuleTopic[] = [
|
||||
detail:
|
||||
"All 13 cards, saved typed custody, supported-deck setup, Emperor movement, native Arrakis shipment sources, revival destinations, low charity/free-revival benefits, Tleilax’s phase-start income penalty and high-Salusa free Sardaukar support are connected. World-to-world invasion, Guild transport from Arrakis and Guild native returns use exact source pools, funding and interception. Homeworld battles connect native bonuses, limited explosion casualties, native-only Traitors and Face Dancers, and Ixian substitution. Junction offers connect sponsored board and Homeworld routes, including own returns, foreign departures and their distinct payments. Caladan foresight, Wallach advisor counts, Ix stronghold movement and Richese token movement now respect native population. High Tupile blocks Worthless sales and Ambassador payouts, including after an interrupted Ghola revival. Kaitain paid discards share the closing Bidding opportunity with CHOAM; Ecaz poison income uses actual physical disposal and current native population. Mandatory winning-card disposal follows winner casualties. Supported low Kaitain and Junction payment receipts preserve the payer’s cost and use current native population; divergent allied rounding remains guarded. Giedi grants its once-per-phase bonus on actual positive desert receipts, including settled Ecaz shares. Low Grumman uses original public entry counts across Terror and competing-reaction checks. Richese High now connects actual paid reserve companions, one combined provisional invoice, original marker proof/responses, physical withdrawal and later reserve-capped reveal. Nexus Cunning retains that explicit paid group in its pair receipt, reserving paid counters before immediate materialization; original counter and spent-card outcomes remain. Ix High now returns physical Suboids for paid Cyborgs with provisional additional allowance and shortage handling, retaining typed free allocation, original invoice and free-event income. Southern and Tleilax preserve the exact eligible revival group through one optional placement. Provisional physical entry now opens native BG, Terror and Ambassador responses, retaining the original held income, independent counters and ordered overlap; a committed Sneak group has its own arrival source. Southern placement provisionally retains the ordinary storm and HMS entry route; Tleilax retains the Ixian-only direct off-planet restriction. Fresh occupation additionally connects bank and percentage receipts, completed Southern Collection, immediate sharing, Caladan inspection, displaced Ix pool, Richese card-only choice with native terms, real Giedi receiver choice and native protections. Fresh native CHOAM entries additionally connect original Tupile slots, actual normal-limit cleanup and restored low-query authority without resetting usage. Advanced retains sole-qualified ownership until last departure. Basic Tupile provisionally follows current sole control: observed departure, contest or native high population ends its slot, while a new activation waits for earlier normal-limit cleanup. Continuous turns do not stack slots or reset intelligence use. Exceptional Duke, concealed transport and full/public acceptance remain unfinished. Already purchased ordinary Karama now composes with immune Junction through explicit unused-price acknowledgment; original tariff/payees stay unchanged and only delivered transport consumes its grant.",
|
||||
evidence: [
|
||||
'game/homeworld-module-profile.ts',
|
||||
'game/faction-module-profile.ts',
|
||||
'game/reinforcements-homeworld.ts',
|
||||
'components/reinforcements-sources.tsx',
|
||||
@@ -5006,7 +5008,7 @@ export const RULE_TOPICS: RuleTopic[] = [
|
||||
status: 'Partial',
|
||||
detail:
|
||||
"Basic Tupile current-sole follow-up passes70/70 cases in10 files, types/lint/build, two original public gameplay/rendering programmes and four legal cleanup profiles. Its provisional policy and broader acceptance limits stay explicit. Earlier mixed E1/E2 and paired E3 Nexus added31 original physical programmes plus2 Loyalty-stock regressions. Final489/489 affected checks across42 files pass with types/lint. All64 original families finish258/288 six-seat games with338931 accepted/339426 attempted actions and9013 JSON across three frozen trees and preserved-prefix continuations. All30 remaining source-guard traces are read. Actual390px original Ixian offer/training, typed native and foreign-world payment/free Suboid plan and paired Ecaz foreign entry/living shared Duke preserve20 physical counters. Human Ixian loses, so no winning Suk rescue is claimed. Earlier qualified evidence remains canonical; full rules, assurance, calibration and deployed acceptance remain open. Current Junction follow-up passes131/131 affected cases and14 runtime/policy paths (11 original/3 controlled), with four actual original-action390px journeys. No saved/deployed acceptance is claimed.",
|
||||
evidence: ['tests/mixed-nexus-modules-runtime.test.ts', 'tests/paired-e3-nexus-modules-runtime.test.ts', 'tests/sample-custody.test.ts', 'tests/ambassador-terror-overlap-engine.test.ts', 'tests/single-nexus-e1-runtime.test.ts', 'tests/single-nexus-e2-runtime.test.ts', 'tests/homeworld-classic-skills-runtime.test.ts',
|
||||
evidence: ['tests/homeworld-module-profile.test.ts', 'tests/homeworld-phase-modules-runtime.test.ts', 'tests/homeworld-phase-modules-controls.test.tsx', 'tests/homeworld-phase-occupation-income.test.ts', 'tests/mixed-nexus-modules-runtime.test.ts', 'tests/paired-e3-nexus-modules-runtime.test.ts', 'tests/sample-custody.test.ts', 'tests/ambassador-terror-overlap-engine.test.ts', 'tests/single-nexus-e1-runtime.test.ts', 'tests/single-nexus-e2-runtime.test.ts', 'tests/homeworld-classic-skills-runtime.test.ts',
|
||||
'tests/homeworld-classic-discovery-runtime.test.ts',
|
||||
'tests/homeworld-skills-discovery-runtime.test.ts',
|
||||
'tests/homeworld-optional-modules-runtime.test.ts',
|
||||
|
||||
Reference in new issue
Block a user