Initialize occupation benefits in original World module setups
This commit is contained in:
1 parent
396f9e0ccb
commit
09eb857583
18 files changed
+916
-12
No files matched your search
+10
-4
@@ -17,7 +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 { homeworldOccupationLeaderSkillsProfile, homeworldPhaseModulesProfile } from './homeworld-module-profile';
|
||||
import { homeworldOccupationLeaderSkillsProfile, homeworldOccupationModulesProfile, 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';
|
||||
@@ -12098,7 +12098,6 @@ export function initializeLeaderSkillsGameForAudit(state: Game): Game {
|
||||
mixedE3 || standaloneE3NexusLeaderSkillsProfile(g) || pairedE3 || guildBetrayalNexusLeaderSkillsProfile(g), false,
|
||||
independentDiscoveryProfile(g) || classicDiscoveryLeaderSkillsProfile(g) || nativeDiscoveryLeaderSkillsProfile(g) || (g.discoveryEnabled === true && classicNexus), true,
|
||||
g.expansions.length === 1 && g.expansions[0] === 'choam', nativeExpansionLeaderSkillsProfile(g), independent);
|
||||
if (homeworldOccupationLeaderSkillsProfile(initialized)) initializeHomeworldOccupationState(initialized);
|
||||
if (advancedMoritaniLeaderSkillsProfile(initialized) || ((independentDiscoveryProfile(initialized) || independentNexusProfile(initialized) || mixedE3 || pairedE3) && initialized.advanced &&
|
||||
byFaction(initialized, 'moritani'))) initializeMoritaniAssassinateState(initialized);
|
||||
if ((independentDiscoveryProfile(initialized) || independentNexusProfile(initialized) || nativeEcazLeaderSkillsProfile(initialized) || mixedE3 || homeworlds || nativeDiscoveryLeaderSkillsProfile(initialized) || pairedE3) && byFaction(initialized, 'ecaz')) initialized.ecazOccupyPreview = true;
|
||||
@@ -12177,7 +12176,6 @@ export function initializeEcazTreacheryGameForAudit(state: Game): Game {
|
||||
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;
|
||||
@@ -12191,6 +12189,13 @@ function initializeSetupGameForAudit(state: Game, homeworlds: boolean, nexus = f
|
||||
homeworldShipmentIntegrity(state);
|
||||
const g = structuredClone(state);
|
||||
requireFreshSetup(g, homeworlds || nexus || ix || factions);
|
||||
if (homeworlds) requireRule(g.homeworlds?.custody === null &&
|
||||
[g.homeworldOccupationPreview, g.homeworldOccupationHistory,
|
||||
g.pendingHomeworldOccupiedIncome, g.homeworldOccupiedIncomeHistory,
|
||||
g.homeworldOccupiedPercentageLedger, g.pendingHomeworldOccupiedPercentage,
|
||||
g.pendingHomeworldOccupiedBonus, g.homeworldTupilePreview, g.homeworldTupileState,
|
||||
g.pendingHomeworldTupileCleanup, g.homeworldEcazVictoryPreview].every(value => value === undefined),
|
||||
'Fresh Homeworld setup cannot replace existing occupation resources or history.');
|
||||
const homeworldSkills = leaderSkills && homeworldLeaderSkillsProfile(g);
|
||||
const skillStronghold = leaderSkills && strongholdLeaderSkillsProfile(g);
|
||||
const skillTech = leaderSkills && (classicTechLeaderSkillsProfile(g) || nativeTechLeaderSkillsProfile(g) || skillStronghold || homeworldSkills || classicDiscoveryLeaderSkillsProfile(g));
|
||||
@@ -12292,6 +12297,7 @@ function initializeSetupGameForAudit(state: Game, homeworlds: boolean, nexus = f
|
||||
g.semutaPreview = true;
|
||||
}
|
||||
initializeSetup(g, strongholdFactions || !!g.strongholdCards);
|
||||
if (homeworlds) initializeHomeworldOccupationState(g);
|
||||
if (nativeTech && byFaction(g, 'ecaz')) g.ecazOccupyPreview = true;
|
||||
return normalizeAutomaticGame(g);
|
||||
}
|
||||
@@ -24223,7 +24229,7 @@ function homeworldOccupiedIncomeIntegrity(g: Game) {
|
||||
return;
|
||||
}
|
||||
requireRule(g.homeworldOccupationPreview === true && g.homeworlds &&
|
||||
(homeworldPhaseModulesProfile(g) || homeworldOccupationLeaderSkillsProfile(g) || (!g.nexusCards &&
|
||||
(homeworldPhaseModulesProfile(g) || homeworldOccupationLeaderSkillsProfile(g) || homeworldOccupationModulesProfile(g) || (!g.nexusCards &&
|
||||
(!g.leaderSkills || !g.expansions.length && homeworldLeaderSkillsProfile(g)) &&
|
||||
!g.techTokens && !g.strongholdCards && !g.discoveryEnabled)) &&
|
||||
Array.isArray(history) && pending !== undefined,
|
||||
|
||||
@@ -1,6 +1,26 @@
|
||||
import { FACTIONS } from './catalog';
|
||||
import type { FactionModuleProfile } from './faction-module-profile';
|
||||
import { homeworldLeaderSkillsProfile } from './leader-skill-profile';
|
||||
import { independentDiscoveryProfile, nexusCoreProfile, type FactionModuleProfile } from './faction-module-profile';
|
||||
import {
|
||||
classicNexusLeaderSkillsProfile,
|
||||
homeworldLeaderSkillsProfile,
|
||||
nativeDiscoveryLeaderSkillsProfile,
|
||||
pairedNexusLeaderSkillsProfile,
|
||||
} from './leader-skill-profile';
|
||||
import {
|
||||
classicDiscoveryLeaderSkillsProfile,
|
||||
classicDiscoveryNexusProfile,
|
||||
homeworldDiscoveryProfile,
|
||||
pairedDiscoveryNexusProfile,
|
||||
} from './discovery-module-profile';
|
||||
import {
|
||||
classicNexusModulesProfile,
|
||||
mixedE1E2NexusProfile,
|
||||
mixedE3NexusProfile,
|
||||
pairedE3NexusProfile,
|
||||
pairedNexusModulesProfile,
|
||||
singleE1E2NexusProfile,
|
||||
standaloneE3NexusProfile,
|
||||
} from './nexus-module-profile';
|
||||
|
||||
export type HomeworldPhaseModulesProfile = FactionModuleProfile & {
|
||||
homeworldOccupationPreview?: unknown;
|
||||
@@ -72,3 +92,43 @@ export function homeworldOccupationLeaderSkillsProfile(game: HomeworldPhaseModul
|
||||
(!game.homeworldEcazVictoryPreview || game.homeworldOccupationPreview === true && ecaz) &&
|
||||
homeworldLeaderSkillsProfile(game);
|
||||
}
|
||||
|
||||
/** Occupation benefits for already-admitted original World/Nexus or Discovery
|
||||
* setups. Pass the actual configuration to its unchanged admission predicates:
|
||||
* live effect stamps and Discovery frames are not replacement module choices. */
|
||||
export function homeworldOccupationModulesProfile(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.nexusCards && game.discoveryEnabled !== true) ||
|
||||
(game.discoveryEnabled !== true && (game.discoveryEnabled || game.discoveries || game.discoveryStash || game.greatMaker)) ||
|
||||
game.semutaPreview || game.advancedPreview || game.kullPreview ||
|
||||
game.nexusKullPreview || game.richeseBetrayalPreview) return false;
|
||||
if (![game.ecazOccupyPreview, game.moritaniAssassinatePreview, game.homeworldOccupationPreview,
|
||||
game.homeworldTupilePreview, game.homeworldEcazVictoryPreview, game.spiceBankerIncomePreview,
|
||||
game.guildBetrayalPreview, game.nexusIxianReplacementPreview, game.nexusIxianBetrayalPreview,
|
||||
game.nexusHarkonnenBetrayalPreview].every(optionalFlag)) return false;
|
||||
const seated = (faction: string) => game.players.some(player => player.faction === faction);
|
||||
if ((game.ecazOccupyPreview && !seated('ecaz')) ||
|
||||
(game.moritaniAssassinatePreview && (!game.advanced || !seated('moritani'))) ||
|
||||
(game.homeworldTupilePreview && (game.homeworldOccupationPreview !== true || !seated('choam'))) ||
|
||||
(game.homeworldEcazVictoryPreview && (game.homeworldOccupationPreview !== true || !seated('ecaz'))) ||
|
||||
(game.spiceBankerIncomePreview && !game.leaderSkills) ||
|
||||
(game.guildBetrayalPreview && (!game.nexusCards || !seated('guild'))) ||
|
||||
(game.nexusIxianReplacementPreview && (!game.nexusCards || seated('ixians'))) ||
|
||||
(game.nexusIxianBetrayalPreview && (!game.nexusCards || !seated('ixians'))) ||
|
||||
(game.nexusHarkonnenBetrayalPreview && (!game.nexusCards || !seated('harkonnen')))) return false;
|
||||
|
||||
if (game.leaderSkills) return homeworldLeaderSkillsProfile(game) &&
|
||||
(game.discoveryEnabled !== true || independentDiscoveryProfile(game) ||
|
||||
classicDiscoveryLeaderSkillsProfile(game) || nativeDiscoveryLeaderSkillsProfile(game) ||
|
||||
classicNexusLeaderSkillsProfile(game));
|
||||
if (game.discoveryEnabled === true) return homeworldDiscoveryProfile(game) ||
|
||||
classicDiscoveryNexusProfile(game) || pairedDiscoveryNexusProfile(game);
|
||||
return nexusCoreProfile(game) || classicNexusModulesProfile(game) || pairedNexusModulesProfile(game) ||
|
||||
singleE1E2NexusProfile(game) || mixedE1E2NexusProfile(game) || standaloneE3NexusProfile(game) ||
|
||||
pairedE3NexusProfile(game) || mixedE3NexusProfile(game) ||
|
||||
// The original paired initializer also admits bare E1/E2 World/Nexus.
|
||||
// This public configuration predicate describes that same undealt roster.
|
||||
pairedNexusLeaderSkillsProfile(game);
|
||||
}
|
||||
+2
-1
@@ -4816,6 +4816,7 @@ export const RULE_TOPICS: RuleTopic[] = [
|
||||
'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.',
|
||||
'Fresh clean registered World/all14 starts also include original occupation benefit state beside normal Banker. Admission and native role limits remain. Real other-payer Bank payments can earn a deferred skill grant; occupied Collection and phase tokens do not become immediate Banker cash. Actual Mentat settles the original skill receipt once. Nexus/Discovery/Semuta contexts retain their own scope; played games are not retrofitted.',
|
||||
'Existing admitted World starts with Nexus and/or Discoveries also initialize original occupation benefits through fresh setup, optionally with Skills and original phase modules. Occupied Collection, ordinary deposits, nested entry, earned Nexus/Stronghold cards and deferred Banker retain their separate events. Original start/role limits remain; existing occupation history is never replaced and played games are not converted.',
|
||||
'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. 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.',
|
||||
@@ -5009,7 +5010,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/homeworld-occupation-skills-profile.test.ts', 'tests/world-skills-occupation-runtime.test.ts', 'tests/world-skills-occupation-controls.test.tsx', 'tests/world-skills-occupation-income.test.ts', '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',
|
||||
evidence: ['tests/homeworld-occupation-modules-profile.test.ts', 'tests/world-module-occupation-runtime.test.ts', 'tests/world-module-occupation-controls.test.tsx', 'tests/world-module-occupation-income.test.ts', 'tests/homeworld-occupation-skills-profile.test.ts', 'tests/world-skills-occupation-runtime.test.ts', 'tests/world-skills-occupation-controls.test.tsx', 'tests/world-skills-occupation-income.test.ts', '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