Compose original World Nexus core

This commit is contained in:
Quintine committed 2026-10-18 06:54:54 +11:00
1 parent ce716f3438
commit 8d5c8da6c4
18 files changed
+754 -16

No files matched your search

+11 -8
View File
@@ -9714,6 +9714,7 @@ function harkonnenNexusBetrayalParent(g: Game): string {
hand:p.hand,traitors:p.traitors,revealedTraitors:p.revealedTraitors,leaders:p.leaders,
forces:p.forces,reserves:p.reserves,tanks:p.tanks,elites:p.elites,advisors:p.advisors})),
deck:g.deck,discard:g.discard,traitorReserve:g.traitorReserve,nexus:g.nexusCards,
...(g.homeworlds ? {homeworlds:g.homeworlds} : {}),
response:g.response,decision:g.decision,truthtrance:g.truthtrance,
phaseOpening:g.phaseOpening,pendingKarama:g.pendingKarama,
pendingNullentropy:g.pendingNullentropy,pendingTreacheryDiscard:g.pendingTreacheryDiscard,
@@ -10043,6 +10044,7 @@ function ixianNexusReplacementParent(g: Game): string {
order:g.order,active:g.active,auction:g.auction,sale:g.currentAuctionSale,
players:g.players.map(p => ({id:p.id,faction:p.faction,ally:p.ally,spice:p.spice,hand:p.hand})),
deck:g.deck,discard:g.discard,aid:g.aid,nexus:g.nexusCards,
...(g.homeworlds ? {homeworlds:g.homeworlds} : {}),
...(byFaction(g, 'richese') ? { richeseBidding:g.richeseBidding,
richeseCache:g.richeseCache,richeseRemoved:g.richeseRemoved,
...(g.richeseAuction ? { richeseAuction:g.richeseAuction } : {}) } : {}),
@@ -10269,8 +10271,8 @@ function guildBetrayalAuthority(g: Game, continuation: GuildBetrayalContinuation
'richesePair', 'noField', 'alliedNoField', 'noFieldSkillProof'].includes(key)),
'The saved reserve shipment contains foreign source fields.');
requireRule(!shipment.source && !shipment.noField && !shipment.alliedNoField &&
!shipment.smuggler && !shipment.smugglerCompanion && !shipment.richesePair &&
!shipment.guildSecretEvent &&
!shipment.richesePair && !shipment.guildSecretEvent &&
((shipment.smuggler && classicNexusCoreProfile(g)) || (!shipment.smuggler && !shipment.smugglerCompanion)) &&
((!shipment.guildNexusEvent && !shipment.nexusEvent) || classicNexusCoreProfile(g)),
'This Guild Betrayal profile requires a supported original physical shipment source.');
validatePhysicalShipment(g, shipment);
@@ -10302,8 +10304,8 @@ function guildBetrayalAuthority(g: Game, continuation: GuildBetrayalContinuation
['player', 'destination', 'sources', 'route', 'event', 'turn', 'amount', 'elite', 'cost',
'allyPayment', 'pools', 'guildSecretEvent', 'guildNexusEvent'].includes(key)),
'The saved Homeworld shipment contains foreign source fields.');
requireRule(!shipment.guildSecretEvent && !shipment.guildNexusEvent,
'This Guild Betrayal profile supports native Homeworld shipment routes only.');
requireRule(!shipment.guildSecretEvent && (!shipment.guildNexusEvent || classicNexusCoreProfile(g)),
'This Guild Betrayal profile requires a supported original Homeworld shipment source.');
const quote = validateHomeworldShipment(g, shipment);
shipper = shipment.player; price = quote.cost; allyPayment = shipment.allyPayment;
originalReceiver = getPlayer(g, shipper).ally !== guild.id && allyPayment > 0 ? guild.id : null;
@@ -10740,7 +10742,7 @@ export function initializeLeaderSkillsGameForAudit(state: Game): Game {
const homeworlds = homeworldLeaderSkillsProfile(state);
const independent = independentTreacheryLeaderSkillsProfile(state);
if (state.ecazTreachery) {
requireRule(independent, 'Independent Skills cards require supported selected decks with original World/phase modules, or classic Nexus with original phase modules; Discovery and unrelated preview contexts remain gated.');
requireRule(independent, 'Independent Skills cards require supported selected decks with original World/phase modules, or classic Nexus with original phase modules without Homeworlds; independent World/Nexus and unrelated preview contexts remain gated.');
requireFreshBaseRuntime(state);
requireFreshFactionInventory(state);
}
@@ -10826,7 +10828,7 @@ export function initializeEcazTreacheryGameForAudit(state: Game): Game {
g.ecazTreachery = true;
const homeworlds = !!g.homeworlds;
requireRule(!homeworlds || ecazHomeworldProfile(g),
'Independent cards with Homeworlds need a fresh supported selected-deck roster with original Tech or Advanced Strongholds.');
'Independent World cards need a supported selected-deck roster and original phase modules without Nexus; independent World/Nexus remains a separate pending interaction.');
const initialized = initializeSetupGameForAudit(g, homeworlds, classicIndependentNexusProfile(g), false, false, false, false, true, true);
if (byFaction(initialized, 'ecaz')) initialized.ecazOccupyPreview = true;
if (moritaniAssassinateModeSupported(initialized)) initializeMoritaniAssassinateState(initialized);
@@ -28236,8 +28238,9 @@ function nexusGuildCunningIntegrity(g: Game) {
for (const d of decisions) validateGuildShipmentDecision(g,d);
for (const rate of rates) validateGuildRateResponse(g,rate);
} else if (active.shipment.kind === 'homeworld') {
requireRule(g.pendingHomeworldShipment?.guildNexusEvent === active.receipt.event,'Guild Cunning lost its pending Homeworld shipment.');
validateGuildCunningShipment(g,owner.id,active.receipt.event,'homeworld',g.pendingHomeworldShipment);
const pending = g.pendingHomeworldShipment ?? (suspended?.source === 'homeworld' ? suspended.shipment : null);
requireRule(pending?.guildNexusEvent === active.receipt.event,'Guild Cunning lost its pending Homeworld shipment.');
validateGuildCunningShipment(g,owner.id,active.receipt.event,'homeworld',pending);
homeworldShipmentIntegrity(g);
} else if (active.shipment.kind === 'guild') {
requireRule(suspended?.source === 'guildTransport', 'Guild Cunning lost its original transport payment acknowledgement.');
+2 -2
View File
@@ -31,12 +31,12 @@ type ClassicNexusProfile = Partial<FactionModuleProfile> & Pick<FactionModulePro
};
/** Classic Nexus12 with selected base33/independent36, optional all14 and original phase modules.
* Native Guild/Harkonnen/Ixian effect stamps are not additional selected modules. */
* Original Homeworld core uses base33; native effect stamps are not selected overlays. */
export function classicNexusCoreProfile(game: ClassicNexusProfile): boolean {
const players = game.players;
if (typeof game.advanced !== 'boolean' || !game.nexusCards ||
game.expansions.length !== 0 || !players || players.length < (game.techTokens ? 3 : 2) || players.length > 6 ||
game.homeworlds || (game.strongholdCards && !game.advanced) || game.sandtrout ||
(game.homeworlds && game.ecazTreachery) || (game.strongholdCards && !game.advanced) || game.sandtrout ||
game.discoveryEnabled || game.discoveries || game.discoveryStash || game.greatMaker ||
game.semutaPreview || game.moritaniAssassinatePreview || game.advancedPreview || game.kullPreview || game.nexusKullPreview ||
game.richeseBetrayalPreview ||
+1
View File
@@ -463,6 +463,7 @@ function homeworldLeaderSkillModulesSupported(game: LeaderSkillProfile): boolean
/** Original Homeworld/Skills under the existing classic or native Nexus roster contracts. */
export function homeworldLeaderSkillsProfile(game: LeaderSkillProfile): boolean {
if (!game.homeworlds) return false;
if (classicNexusCoreProfile(game)) return true;
if (game.expansions.length) return nativeExpansionLeaderSkillsProfile(game);
if (!homeworldLeaderSkillModulesSupported(game)) return false;
const players = game.players!;
+2 -1
View File
@@ -4104,7 +4104,8 @@ export const RULE_TOPICS: RuleTopic[] = [
'Caladan shares the admitted original Bidding inspection with the occupier, not its ally; native inspection can still be prevented independently. Ix gives the occupier the actual private drawn pool and selection, not unrelated setup or Technology. Richese sets its own method/direction before the occupier chooses only the physical cache card; seller, payment and other decisions stay native. Giedi gives one original purchase bonus to the occupier or its reciprocal ally with real receiver capacity, even when Harkonnen’s purchased card fills its hand.',
'The separately marked fresh Tupile entry grants one hand slot to the proved occupier and reciprocal ally. Every server-derived hand limit includes it, so Harkonnen can hold nine without receiving a tenth bonus. After a proved loss, each old holder privately selects exactly its excess original eligible cards to return to the normal limit; committed cards remain protected. Original phase opening and remaining movement resume without another shipment or payment. Basic unknown lifecycle blocks rather than guesses cleanup. Advanced departure can restore CHOAM’s low intelligence without clearing its previous answers or used factions.',
'The fresh native Ecaz high-card preview shows a separate prospective conjunction: native seven-plus population, at least one actual jointly held stronghold and proved current alliance holdings of two other native factions’ Homeworlds. This does not change the ordinary four-stronghold or three-joint Ecaz targets. Original Mentat decides the win before prediction/fallback; source uncertainty remains blocked. Duke authority and the Ecaz-only revival ruling are separate.',
'The card collection below is available for reference. Starting a Homeworld game remains disabled while shipment, battles, occupation, economy and faction interactions are being completed.',
'The card collection below is available for reference. Homeworld starts are enabled in supported fresh preview envelopes; the remaining occupation variants and combinations stay under verification.',
'Original base33 World/Nexus now composes optional all14, Tech3+ and Advanced Strongholds2+ with real first-Storm token owners and end-Mentat card claims. Guild World shipments and Cunning visitor returns pass native permission/rate before the funded fee; borrowed Caladan fee1/fee5 settles to the distinct holder while typed custody waits. Temporary-Sardaukar own-Kaitain battles keep ordinary-only casualties and true Salusa custody; Giedi native-call cancellation follows original capture/execution/cleanup and one private replacement. Normal Smuggler companions may also supply the fee in core. Independent36 World/Nexus remains a separate pending own-World fee/conversion gate with public messaging; other envelopes stay separate.',
],
example:
'Atreides with six reserves on Caladan uses the high face. Dropping to five uses the low face and removes the next-Spice-Blow inspection advantage. Either face adds two to native battle strength on Caladan.',