Add dead leader battle preparation
Verify and publish checkpoint container / container (push) Canceled after 0s
Verify and publish checkpoint container / container (push) Canceled after 0s
This commit is contained in:
1 parent
bcca08cac5
commit
e439f3f7b6
8 files changed
+386
-21
No files matched your search
+33
-12
@@ -991,6 +991,8 @@ export type Battle = {
|
||||
/** Fresh clean Basic battles announce a bounded no-revival case before plans. */
|
||||
basicEarlyLeaderlessVersion?: 1;
|
||||
basicEarlyLeaderless?: string[];
|
||||
/** Fresh classic battles with a neutral dead-leader preparation opportunity. */
|
||||
basicDeadLeaderPreparation?: true;
|
||||
revealed: boolean;
|
||||
traitorCalls: Record<string, boolean>;
|
||||
traitorDecisionPromises?: TraitorDecisionPromise[];
|
||||
@@ -43024,9 +43026,7 @@ function applyActionWithInitiator(state: Game, id: string, action: Action, initi
|
||||
charityOrderIntegrity(g);
|
||||
return g;
|
||||
}
|
||||
/** The published inability is safe before a plan only when no leader can be
|
||||
* revived and this clean Basic roster cannot privately transfer a Cheap Hero.
|
||||
* Other cases retain the original seal-time announcement. */
|
||||
/** Publish a clean Basic inability once preparation and battle powers finish. */
|
||||
function announceEarlyBasicLeaderless(g: Game) {
|
||||
const b = g.battle;
|
||||
if (!b || b.basicEarlyLeaderlessVersion !== 1 || g.status !== 'playing' || g.advanced ||
|
||||
@@ -43034,11 +43034,20 @@ function announceEarlyBasicLeaderless(g: Game) {
|
||||
g.response || g.decision || g.truthtrance || g.phaseOpening || g.pendingKarama ||
|
||||
g.pendingKull || g.pendingTreacheryDiscard || g.pendingNullentropy) return;
|
||||
for (const id of [b.attacker, b.defender]) {
|
||||
if (b.plans[id] || b.basicEarlyLeaderless?.includes(id)) continue;
|
||||
if (b.plans[id]) continue;
|
||||
const p = getPlayer(g, id), leaders = controlledLeaders(g, p);
|
||||
if (!leaders.length || leaders.some(l => l.dead || l.capturedBy || !l.usedAt || l.usedAt === b.territory) ||
|
||||
p.hand.some(c => c.kind === 'hero' && !(b.voice?.target === id && !b.voice.must &&
|
||||
playedVoiceMatch(c, 'leader', b.voice.kind)))) continue;
|
||||
const availableLeader = leaders.some(l => !l.dead && !l.capturedBy && (!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)) {
|
||||
if (b.basicDeadLeaderPreparation && availableLeader) {
|
||||
b.basicEarlyLeaderless = b.basicEarlyLeaderless.filter(player => player !== id);
|
||||
log(g, `${p.name} now has an available leader for this battle.`);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (!leaders.length || 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.`);
|
||||
}
|
||||
@@ -47358,15 +47367,27 @@ 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 &&
|
||||
!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')
|
||||
? [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));
|
||||
}) : [];
|
||||
g.battle = {
|
||||
...choice,
|
||||
chooserEvent: battleChooserEvent(g.turn, battleOrderAfterBattle(g), g.battleOrder?.uses.length ?? 0),
|
||||
event: battleEvent,
|
||||
...(!g.advanced && g.expansions.length === 0 && !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))
|
||||
? { basicEarlyLeaderlessVersion: 1 as const } : {}),
|
||||
...(earlyBasic ? { basicEarlyLeaderlessVersion: 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 } : {}),
|
||||
|
||||
+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 fresh classic Basic battles without optional modules, when all your leaders are alive but unavailable elsewhere and you lack a legal Cheap Hero, your inability is announced before plans. 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. 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