Announce leaderless native Basic battles before plans
Verify and publish checkpoint container / container (push) Successful in 10m14s
Verify and publish checkpoint container / container (push) Successful in 10m14s
This commit is contained in:
1 parent
00e01a7cd2
commit
f73a6e0a9c
9 files changed
+481
-21
No files matched your search
+40
-20
@@ -38383,6 +38383,7 @@ function finishResponse(g: Game, canceled: boolean, settlement?: BureaucratSettl
|
||||
!Object.keys(b.plans).length, 'Resolve Occupy before native powers and plans.');
|
||||
occupy.canceled = canceled;
|
||||
ecazBattleProfile(g);
|
||||
prepareEarlyBasicLeaderless(g);
|
||||
// The chosen plan owner and final Occupy force profile are now fixed.
|
||||
// A nonleading ally keeps its waiting receipt for its own later battle.
|
||||
bindFutureBattleLeaderQuestions(g);
|
||||
@@ -43791,6 +43792,27 @@ function publicNativeBattleLeaders(player: Player, territory: string) {
|
||||
unavailable: !leaders.some(leader => !leader.dead && !leader.captured &&
|
||||
(!leader.usedAt || leader.usedAt === territory)) };
|
||||
}
|
||||
function publicSharedDukeAvailable(g: Game, player: string, territory: string): boolean {
|
||||
const duke = projectedSharedDuke(g, '');
|
||||
return !!duke && duke.controller === player && !duke.leader.dead &&
|
||||
!duke.leader.capturedBy && !duke.leader.gholaBy &&
|
||||
(!duke.leader.usedAt || duke.leader.usedAt === territory);
|
||||
}
|
||||
/** Open the neutral Ghola opportunity only for the final battle participants.
|
||||
* Richese already opens the same shared preparation for Sapho. */
|
||||
function prepareEarlyBasicLeaderless(g: Game) {
|
||||
const b = g.battle;
|
||||
if (!b?.event || b.basicEarlyLeaderlessVersion !== 1) return;
|
||||
const participants = [b.attacker, b.defender];
|
||||
const candidates = participants.filter(id => {
|
||||
const publicState = publicNativeBattleLeaders(getPlayer(g, id), b.territory);
|
||||
return publicState.hasDead && publicState.unavailable && !publicSharedDukeAvailable(g, id, b.territory);
|
||||
});
|
||||
if (!candidates.length) return;
|
||||
b.basicDeadLeaderPreparation = true;
|
||||
if (!b.preLeader) b.preLeader = {event: b.event,
|
||||
ready: participants.filter(id => !candidates.includes(id)), closed: false};
|
||||
}
|
||||
/** Publish a clean Basic inability once preparation and battle powers finish. */
|
||||
function announceEarlyBasicLeaderless(g: Game) {
|
||||
const b = g.battle;
|
||||
@@ -43807,16 +43829,18 @@ function announceEarlyBasicLeaderless(g: Game) {
|
||||
const availableHero = p.hand.some(c => c.kind === 'hero' && !(b.voice?.target === id && !b.voice.must &&
|
||||
playedVoiceMatch(c, 'leader', b.voice.kind)));
|
||||
if (b.basicEarlyLeaderless?.includes(id)) {
|
||||
if (b.basicDeadLeaderPreparation && availableLeader) {
|
||||
if (availableLeader || availableHero) {
|
||||
b.basicEarlyLeaderless = b.basicEarlyLeaderless.filter(player => player !== id);
|
||||
log(g, `${p.name} now has an available leader for this battle.`);
|
||||
log(g, availableLeader ? `${p.name} now has an available leader for this battle.` :
|
||||
`${p.name} now has an available Cheap Hero for this battle.`);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (b.basicPublicLeaderlessVersion === 1 &&
|
||||
(!publicNativeBattleLeaders(p, b.territory).unavailable ||
|
||||
p.faction === 'harkonnen' && g.players.some(owner => owner.leaders.some(leader =>
|
||||
leader.concealed?.captor === p.id && leader.capturedBy === p.id)))) continue;
|
||||
(!publicNativeBattleLeaders(p, b.territory).unavailable || publicSharedDukeAvailable(g, id, b.territory) ||
|
||||
p.faction === 'harkonnen' && (g.players.some(owner => owner.leaders.some(leader =>
|
||||
leader.concealed?.captor === p.id && leader.capturedBy === p.id)) ||
|
||||
g.dukeVidal?.leader.concealed?.captor === p.id))) continue;
|
||||
if ((!leaders.length && !b.basicDeadLeaderPreparation) || availableLeader || availableHero ||
|
||||
!b.basicDeadLeaderPreparation && leaders.some(l => l.dead || l.capturedBy)) continue;
|
||||
(b.basicEarlyLeaderless ??= []).push(id);
|
||||
@@ -44951,6 +44975,9 @@ function applyActionInner(
|
||||
// The selected lead supplies normal aggressor priority, not the scheduling ally.
|
||||
if (g.order.indexOf(b.attacker) > g.order.indexOf(b.defender))
|
||||
[b.attacker, b.defender] = [b.defender, b.attacker];
|
||||
// Richese's shared preparation must name the final combatants.
|
||||
if (b.basicEarlyLeaderlessVersion === 1 && b.preLeader)
|
||||
b.preLeader.ready = [];
|
||||
ecazBattleProfile(g);
|
||||
g.decision = null;
|
||||
g.response = { kind: 'ecazOccupy', owner: occupy.ecaz, passed: [] };
|
||||
@@ -48186,27 +48213,17 @@ function applyActionInner(
|
||||
'Combined Occupy requires source-selected native factions; optional overlays remain gated.');
|
||||
}
|
||||
const battleEvent = crypto.randomUUID();
|
||||
const earlyBasic = !g.advanced && g.expansions.length === 0 && !g.ecazTreachery && !g.techTokens &&
|
||||
const earlyBasic = !g.advanced && (g.expansions.length === 0
|
||||
? g.players.every(seat => CLASSIC_FACTIONS[seat.faction] === true)
|
||||
: selectedPhaseModuleRoster(g)) && !g.ecazTreachery && !g.techTokens &&
|
||||
!g.strongholdCards && !g.leaderSkills && !g.homeworlds && !g.nexusCards && !g.discoveryEnabled &&
|
||||
!g.sandtrout && !g.coalitionState && g.players.every(seat =>
|
||||
['atreides', 'beneGesserit', 'emperor', 'fremen', 'guild', 'harkonnen'].includes(seat.faction));
|
||||
// Use the same pre-capture snapshot visible to uninvolved players. A
|
||||
// neutral preparation opportunity must not identify a concealed captive.
|
||||
const deadLeaderCandidates = earlyBasic
|
||||
? [choice.attacker, choice.defender].filter(playerId => {
|
||||
const publicState = publicNativeBattleLeaders(getPlayer(g, playerId), choice.territory);
|
||||
return publicState.hasDead && publicState.unavailable;
|
||||
}) : [];
|
||||
!g.coalitionState && !g.kullPreview && !g.nexusKullPreview;
|
||||
g.battle = {
|
||||
...choice,
|
||||
chooserEvent: battleChooserEvent(g.turn, battleOrderAfterBattle(g), g.battleOrder?.uses.length ?? 0),
|
||||
event: battleEvent,
|
||||
...(earlyBasic ? { basicEarlyLeaderlessVersion: 1 as const } : {}),
|
||||
...(earlyBasic && byFaction(g, 'harkonnen') ? { basicPublicLeaderlessVersion: 1 as const } : {}),
|
||||
...(deadLeaderCandidates.length ? { basicDeadLeaderPreparation: true as const,
|
||||
preLeader: { event: battleEvent,
|
||||
ready: [choice.attacker, choice.defender].filter(playerId => !deadLeaderCandidates.includes(playerId)),
|
||||
closed: false } } : {}),
|
||||
stoneAllocationVersion: 1,
|
||||
...(g.leaderSkills ? { smugglerCollectionVersion: 1 as const } : {}),
|
||||
...(g.leaderSkills && mentatQuestionModeSupported(g) ? { mentatQuestionVersion: 1 as const } : {}),
|
||||
@@ -48223,7 +48240,10 @@ function applyActionInner(
|
||||
revealed: false,
|
||||
traitorCalls: {},
|
||||
};
|
||||
if (!combined) bindFutureBattleLeaderQuestions(g);
|
||||
if (!combined) {
|
||||
prepareEarlyBasicLeaderless(g);
|
||||
bindFutureBattleLeaderQuestions(g);
|
||||
}
|
||||
if (combined) {
|
||||
g.battle.ecazOccupy = { event: battleEvent, ecaz: ecaz!.id, ally: ally!.id, lead: null, canceled: false };
|
||||
const opponent = [choice.attacker, choice.defender].find(id => id !== ecaz!.id && id !== ally!.id)!;
|
||||
|
||||
Reference in new issue
Block a user