Guard Duke grants using public capture uncertainty
Verify and publish checkpoint container / container (push) Successful in 11m21s

This commit is contained in:
Quintine committed 2026-10-29 19:22:46 +11:00
1 parent 5f4eeee4f1
commit 57e7611468
15 files changed
+500 -19

No files matched your search

+25 -5
View File
@@ -146,6 +146,8 @@ import { ECAZ_START_FORCES, quoteEcazStartingForces } from './ecaz-setup';
import { battleCardSlotEligible, battleCategoryInspectionValue, fixedBattleInspectionMatches, validBattleSlotPair, usesNativeBattleSpecial } from './battle-card-slots';
import { HARASS_WITHDRAW_CARD, HarassWithdrawError, isHarassWithdraw, quoteHarassWithdraw, defaultHarassWithdrawAllocation, harassWithdrawNeedsAllocation, type HarassWithdrawSelection, type HarassWithdrawContext, type HarassWithdrawPreview } from './harass-withdraw';
import { originalAuctionInspectionLot } from './auction-inspection-source';
import { nextDukeCaptureUncertainty } from './duke-capture-uncertainty';
import { dukeCaptureUncertaintyBlock, plainAdvancedEcazDukeProfile } from './ecaz-duke-capture-policy';
import { discoveryChoices, discoveryStashSignature, type DiscoveryStash } from './discovery-actions';
import { createStormSource, validateStormSource, discoveryStormOffer, createDiscoveryStorm, chooseDiscoveryStorm, validateDiscoveryStorm, finishDiscoveryStorm, discoveryStormDistance, discoveryStormSaphoModes, reorderDiscoveryStorm, type StormMovementSource, type DiscoveryStorm } from './discovery-storm';
import { completeDiscoveryFlight, discoveryFlightOffer, quoteDiscoveryFlight, validateDiscoveryFlight, type DiscoveryFlightReceipt } from './discovery-flight';
@@ -2261,6 +2263,8 @@ export type Game = {
cost: number;
} | null;
dukeVidal?: DukeState;
/** Public capture history: absent on legacy games whose past cannot be inferred. */
dukeCaptureUncertain?: boolean;
dukeAcquisitionTurn?: number;
pendingTerrorEntry?: {
victoryEvent?: string;
@@ -5850,6 +5854,7 @@ export function createGame(
version: 0,
host: host.id,
status: 'lobby',
dukeCaptureUncertain: false,
kullDefaultPolicy: true,
advanced,
expansions: [...new Set(expansions)],
@@ -25069,6 +25074,8 @@ function ambassadorEffectBlock(
if (effect === 'ecaz') {
const owner = byFaction(g, 'ecaz');
if (!owner) return 'Duke Vidal is unavailable for this acquisition.';
const uncertainty = dukeCaptureUncertaintyBlock(g);
if (uncertainty) return uncertainty;
if (g.players.some((p) => p.leaders.some((l) => l.id === DUKE_VIDAL_ID)))
return 'Duke Vidal is unavailable for this acquisition.';
return occupiedEcazDukePriority(g) ?? ecazDukeAcquisitionBlock(g, owner.id, beneficiary.id);
@@ -27198,8 +27205,10 @@ function ecazAllianceQuote(g: Game, owner: string, entrant: string) {
}
}
function ecazAmbassadorLoanBlock(g: Game, owner: Player, entrant: Player) {
const uncertainty = dukeCaptureUncertaintyBlock(g);
if (uncertainty) return uncertainty;
if (g.advanced && byFaction(g, 'harkonnen') &&
!(independentDiscoveryProfile(g) || independentNexusProfile(g) || standaloneE3NexusProfile(g) || pairedE3NexusProfile(g) || mixedE3NexusProfile(g)))
!(plainAdvancedEcazDukeProfile(g) || independentDiscoveryProfile(g) || independentNexusProfile(g) || standaloneE3NexusProfile(g) || pairedE3NexusProfile(g) || mixedE3NexusProfile(g)))
return 'Advanced Duke loans with Harkonnen capture remain guarded in this profile.';
if (owner.ally !== entrant.id || entrant.ally !== owner.id)
return 'The new Ecaz alliance is no longer current.';
@@ -39157,13 +39166,24 @@ function finishResponse(g: Game, canceled: boolean, settlement?: BureaucratSettl
else {
const candidates = captureCandidates(g, capture.loser, capture.territory, capture.duke);
const selected = shuffle(candidates)[0];
const l = selected?.id === DUKE_VIDAL_ID
? (g.dukeVidal = captureDuke(g.dukeVidal!, capture.player, capture.loser,
g.turn, capture.territory, capture.duke)).leader : selected;
if (!l) {
if (!selected) {
finishBattle(g);
return;
}
// Record only publicly observable capture circumstances, never the
// selected concealed identity or the internal released-Duke witness.
const publicDuke = projectedSharedDuke(g, '');
const publicPlan = g.battle?.revealed ? g.battle.plans[capture.loser] : undefined;
const uncertainty = nextDukeCaptureUncertainty(g.dukeCaptureUncertain, {
publicController: publicDuke?.controller,
loser: capture.loser,
revealedLeader: publicPlan ? publicPlan.leader ?? null : undefined,
publicPlanKnown: !!publicPlan,
});
if (uncertainty !== undefined) g.dukeCaptureUncertain = uncertainty;
const l = selected?.id === DUKE_VIDAL_ID
? (g.dukeVidal = captureDuke(g.dukeVidal!, capture.player, capture.loser,
g.turn, capture.territory, capture.duke)).leader : selected;
const owner = l.id === DUKE_VIDAL_ID ? getPlayer(g, capture.loser) :
g.players.find((player) => player.leaders.includes(l))!;
if (l.id !== DUKE_VIDAL_ID) {