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)),
|
||||
|
||||
+1
-1
@@ -2847,7 +2847,7 @@ export const RULE_TOPICS: RuleTopic[] = [
|
||||
summary: 'Build a legal combination and decide what to keep after winning.',
|
||||
steps: [
|
||||
'Choose one weapon, one defense, both, or neither. A worthless card can occupy a weapon or defense slot.',
|
||||
'Choose an available leader or Cheap Hero when required. Without either, battle cards cannot be played. In supported fresh classic Basic battles, an inability is announced before plans. When dead leaders may be revived, a preparation opportunity lets you play Ghola from your hand before continuing; a later revival updates the public notice. Other Basic and Advanced leaderless plans announce on sealing; its dial, spice and other choices remain sealed. Refresh retains the same announcement without submitting another plan.',
|
||||
'Choose an available leader or Cheap Hero when required. Without either, battle cards cannot be played. In supported fresh classic Basic battles, an inability is announced before plans. When dead leaders may be revived, a preparation opportunity lets you play Ghola from your hand before continuing; a later revival updates the public notice. Harkonnen can use a captive leader; its announcement stays at plan submission while it holds a concealed captive. Other Basic and Advanced leaderless plans announce on sealing; its dial, spice and other choices remain sealed. Refresh retains the same announcement without submitting another plan.',
|
||||
'On Arrakis, a surviving leader remains in its battle territory for the rest of the Battle phase. A later Lasgun–Shield explosion there also kills previously surviving leader discs still present, even if a different leader fights that later battle. The Basic application is provisional; a successful Traitor call prevents the explosion.',
|
||||
'A loser normally discards all played cards. If allied with Moritani and the battle has a winner, the defeated ally may retain one played card that a winner could keep. A winner may discard selected played cards; Cheap Heroes are discarded after use.',
|
||||
],
|
||||
|
||||
Reference in new issue
Block a user