Support public leader preparation with Harkonnen
Verify and publish checkpoint container / container (push) Successful in 10m56s
Verify and publish checkpoint container / container (push) Successful in 10m56s
This commit is contained in:
1 parent
f9ca7ed9a9
commit
31c6e79c80
6 files changed
+288
-11
No files matched your search
+27
-8
@@ -993,6 +993,8 @@ export type Battle = {
|
||||
basicEarlyLeaderless?: string[];
|
||||
/** Fresh classic battles with a neutral dead-leader preparation opportunity. */
|
||||
basicDeadLeaderPreparation?: true;
|
||||
/** Fresh Harkonnen games qualify early notices from public leader snapshots. */
|
||||
basicPublicLeaderlessVersion?: 1;
|
||||
revealed: boolean;
|
||||
traitorCalls: Record<string, boolean>;
|
||||
traitorDecisionPromises?: TraitorDecisionPromise[];
|
||||
@@ -43026,6 +43028,17 @@ function applyActionWithInitiator(state: Game, id: string, action: Action, initi
|
||||
charityOrderIntegrity(g);
|
||||
return g;
|
||||
}
|
||||
/** Public native-disc state, independent of concealed capture identity. */
|
||||
function publicNativeBattleLeaders(player: Player, territory: string) {
|
||||
const leaders = player.leaders.map(leader => ({
|
||||
dead: leader.concealed ? leader.concealed.dead : leader.dead,
|
||||
usedAt: leader.concealed ? leader.concealed.usedAt : leader.usedAt,
|
||||
captured: !leader.concealed && !!leader.capturedBy,
|
||||
}));
|
||||
return { hasDead: leaders.some(leader => leader.dead),
|
||||
unavailable: !leaders.some(leader => !leader.dead && !leader.captured &&
|
||||
(!leader.usedAt || leader.usedAt === territory)) };
|
||||
}
|
||||
/** Publish a clean Basic inability once preparation and battle powers finish. */
|
||||
function announceEarlyBasicLeaderless(g: Game) {
|
||||
const b = g.battle;
|
||||
@@ -43036,7 +43049,9 @@ function announceEarlyBasicLeaderless(g: Game) {
|
||||
for (const id of [b.attacker, b.defender]) {
|
||||
if (b.plans[id]) continue;
|
||||
const p = getPlayer(g, id), leaders = controlledLeaders(g, p);
|
||||
const availableLeader = leaders.some(l => !l.dead && !l.capturedBy && (!l.usedAt || l.usedAt === b.territory));
|
||||
// controlledLeaders already includes captives that Harkonnen may use.
|
||||
// Keep this identical to the availability check in validatePlan.
|
||||
const availableLeader = leaders.some(l => !l.dead && (!l.usedAt || l.usedAt === b.territory));
|
||||
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)) {
|
||||
@@ -43046,7 +43061,11 @@ function announceEarlyBasicLeaderless(g: Game) {
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (!leaders.length || availableLeader || availableHero ||
|
||||
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;
|
||||
if ((!leaders.length && !b.basicDeadLeaderPreparation) || availableLeader || availableHero ||
|
||||
!b.basicDeadLeaderPreparation && leaders.some(l => l.dead || l.capturedBy)) continue;
|
||||
(b.basicEarlyLeaderless ??= []).push(id);
|
||||
log(g, `${p.name} has no available leader or Cheap Hero for this battle.`);
|
||||
@@ -47371,19 +47390,19 @@ function applyActionInner(
|
||||
!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));
|
||||
// The candidate depends only on public native leaders, never either hand.
|
||||
// Harkonnen's concealed captures need a separate public-projection rule.
|
||||
const deadLeaderCandidates = earlyBasic && !byFaction(g, 'harkonnen')
|
||||
// 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 leaders = getPlayer(g, playerId).leaders;
|
||||
return leaders.some(leader => leader.dead) && !leaders.some(leader =>
|
||||
!leader.dead && !leader.capturedBy && (!leader.usedAt || leader.usedAt === choice.territory));
|
||||
const publicState = publicNativeBattleLeaders(getPlayer(g, playerId), choice.territory);
|
||||
return publicState.hasDead && publicState.unavailable;
|
||||
}) : [];
|
||||
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)),
|
||||
|
||||
Reference in new issue
Block a user