Connect Advanced Auditor repeated revival and provisional native cycles

This commit is contained in:
Quintine committed 2026-10-08 16:51:05 +11:00
1 parent 85729f9819
commit 869412bebd
10 files changed
+113 -24

No files matched your search

+1
View File
@@ -32683,6 +32683,7 @@ export function viewGame(state: Game, id: string) {
leaders: leaderSkillRevivalBlocked(g, id) ? [] : leaderRevivals.leaders,
kwisatz: leaderRevivals.kwisatz,
cycleBlock: leaderRevivals.cycleBlock,
...(leaderRevivals.auditorCyclePolicy ? { auditorCyclePolicy: leaderRevivals.auditorCyclePolicy } : {}),
dukeBlocked: leaderRevivals.dukeBlocked,
prevented: revivalPrevented(g, me.id),
eliteRemaining: eliteRevivalRemaining(me, g.advanced),
+1
View File
@@ -22,6 +22,7 @@ export const FACTION_RULES: Record<
'Reserve spice to pay for some or all of your ally’s battle support. Each sealed plan records its own and allied shares. A Karama response may prevent this alliance benefit for one battle; unused funding returns at the end of Battle.',
'Receive half of each other player’s actual force-support payment, rounded down, unless any traitor is revealed. Your own payments, including support paid for your ally, go to the bank. A separate Karama response can cancel this battle’s income. See CHOAM combat for examples and outstanding interaction audits.',
'The Auditor is an additional strength-two leader. After using it in battle, CHOAM may inspect two randomly selected unused opposing Treachery Cards if it survives, or one if it dies, limited by the unused cards available. The opponent may pay CHOAM one spice per card that would be inspected to prevent the whole inspection; Karama can also cancel the power. Use the Auditor guide for the exact private choices and limits. Setup and inspection are supported in development games; complete combined play remains unfinished.',
'Supplied Advanced p29 permits Auditor revival each turn even after repeat deaths, for two spice before discounts and using the ordinary one-leader allowance. The five-ordinary-disc cycle policy excludes Auditor and is visibly provisional; it neither opens nor delays ordinary revival.',
],
},
ixians: {
+9 -8
View File
@@ -3626,6 +3626,7 @@ export const RULE_TOPICS: RuleTopic[] = [
title: 'CHOAM Auditor',
category: 'Advanced & expansions',
coverage: 'Partial',
developmentStage: 'Prototyped',
summary:
'An additional strength-2 leader inspects unused opposing hand cards after battle.',
steps: [
@@ -3635,8 +3636,8 @@ export const RULE_TOPICS: RuleTopic[] = [
'Karama cancellation resolves first. If the power remains active, the opponent can pay CHOAM one spice per actually viewable card to cancel the entire inspection. A surviving Auditor with only one eligible card costs one spice to stop. There is no partial payment.',
'If the opponent cannot afford the full payment, inspection proceeds automatically. An empty eligible hand needs no decision. Sampling happens once after cancellation choices; no card is transferred or discarded.',
'Only CHOAM receives the inspected faces. Its private snapshot survives refresh and the transition out of Battle, and expires at the next battle or turn. It does not track later changes in the opposing hand and needs no confirmation to continue.',
'The Auditor cannot be captured, acquired as a foreign ghola by Tleilaxu, or receive a leader skill. CHOAM’s own Ghola card can revive it. Its first ordinary revival can occur before the other leaders die, costs two spice before any discount, and consumes the usual one-leader allowance.',
'Repeated Auditor revival and the effect of its sixth disc on ordinary CHOAM death cycles await a ruling. Full expansion starts remain disabled until all required powers and interactions are complete.',
'The Auditor cannot be captured, acquired as a foreign ghola by Tleilaxu, or receive a leader skill. CHOAM’s own Ghola card can revive it. Supplied Advanced p29 permits ordinary revival each turn even after repeated deaths, for two spice before discounts, using the usual one-leader allowance.',
'The ordinary-cycle first version explicitly excludes Auditor from the five ordinary CHOAM discs: its life or repeated deaths neither delay nor open that cohort. This is a visibly provisional policy, not a publisher clarification or numerical user ruling. Full expansion starts remain disabled.',
],
example:
'CHOAM loses a battle but its Auditor survives. The opponent keeps a played Shield and has one unplayed Crysknife. Only Crysknife is eligible: the opponent can pay one spice to CHOAM or let CHOAM inspect it.',
@@ -3652,7 +3653,7 @@ export const RULE_TOPICS: RuleTopic[] = [
area: 'Implementation',
status: 'Partial',
detail:
'Setup identity, inspection, payment, Karama, capture/foreign-ghola exclusions and first revival are integrated; repeat cycles and full combinations remain open.',
'Setup identity, inspection, payment, Karama and capture/foreign-ghola exclusions are integrated. Supplied each-turn Auditor revival and provisional five-ordinary-disc cycles are connected; wider combinations remain open.',
evidence: [
'game/choam-auditor.ts',
'game/engine.ts',
@@ -3663,8 +3664,8 @@ export const RULE_TOPICS: RuleTopic[] = [
area: 'Player controls',
status: 'Implemented',
detail:
'Auditor offer, exact cancellation payment and private inspectable card results have dedicated controls.',
evidence: ['components/choam-auditor.tsx'],
'Auditor offer, exact cancellation payment and private inspectable card results have dedicated controls. Owned Revival lists repeat-death Auditor and eligible ordinary discs with an explicit provisional cycle warning.',
evidence: ['components/choam-auditor.tsx', 'components/game-table.tsx'],
},
{
area: 'AI',
@@ -3684,8 +3685,8 @@ export const RULE_TOPICS: RuleTopic[] = [
area: 'Verification',
status: 'Partial',
detail:
'Identity, uniform sampling and focused engine/lifecycle/recovery scenarios are being verified; full expansion combinations remain uncertified.',
evidence: ['tests/choam-auditor.test.ts'],
'Two before/after native Easy revival smokes, two focused cycle regressions and isolated GameTable SSR Chromium surfaces pass. Earlier identity/sampling evidence remains; full expansion combinations and deployed acceptance remain uncertified.',
evidence: ['tests/choam-auditor.test.ts', 'tests/choam-auditor-lifecycle.test.ts'],
},
],
},
@@ -3702,7 +3703,7 @@ export const RULE_TOPICS: RuleTopic[] = [
'A canceled request within the new allowance uses the new price. If it is too large or cannot be paid, no forces or spice move; choose another request. Completed earlier revivals remain intact.',
'Fremen may grant their allied CHOAM three free revivals. The free allowance is tracked across requests. Tleilaxu may separately offer its ally half price, rounded up on the total payment; canceling that discount retains CHOAM’s native price if it is still active.',
'Emperor-funded extra revivals keep their separate allowance and price. Advanced Tleilaxu special Karama can prevent the normal request before either pricing response. Revival payments go to Tleilaxu when present, with a separate income response.',
'Force revival has tested support. Auditor’s first normal return and separate inspection power are described in its topic; repeated death cycles, combined-expansion pricing and the complete faction audit remain unfinished. CHOAM starts stay disabled.',
'Force revival has tested support. Auditor’s each-turn return and provisional five-ordinary-disc cycles are described in its topic. Combined-expansion pricing and the complete faction audit remain unfinished; CHOAM starts stay disabled.',
],
related: [
'revival',
+22 -12
View File
@@ -131,11 +131,18 @@ export function forceRevivalRemaining(
);
}
export function normalRevivalCycle(
p: Pick<Player, 'leaders' | 'revivalCycle'>,
p: Pick<Player, 'faction' | 'leaders' | 'revivalCycle'>,
) {
return p.leaders.every((l) => l.dead || l.capturedBy || l.gholaBy)
? Math.min(...p.leaders.map((l) => (l.dead ? l.deaths : l.deaths + 1)))
: p.revivalCycle;
let cycle = Infinity;
for (const leader of p.leaders) {
// Provisional CHOAM policy: the independently revivable extra Auditor
// does not open or delay the ordinary five-disc cohort.
if (p.faction === 'choam' && isAuditorLeader(leader)) continue;
if (!leader.dead && !leader.capturedBy && !leader.gholaBy)
return p.revivalCycle;
cycle = Math.min(cycle, leader.dead ? leader.deaths : leader.deaths + 1);
}
return cycle;
}
/** The five-disc Ecaz opening is recorded in revivalCycle on the first
@@ -184,16 +191,15 @@ export function leaderRevivalOptions(
const leaders = p.leaders.flatMap((leader) => {
if (!enabled || !leader.dead || leader.capturedBy || leader.gholaBy)
return [];
// Auditor's first return expressly waives the all-in-Tanks prerequisite.
// Repeat-death and sixth-disc cycle interpretations remain separately unverified.
const auditorFirstReturn =
// Supplied Advanced p29 E permits Auditor revival each turn regardless
// of the other leaders in Tanks; retain the ordinary one-leader allowance.
const auditorReturn =
g.advanced &&
p.faction === 'choam' &&
isAuditorLeader(leader) &&
leader.deaths === 1;
isAuditorLeader(leader);
const normal =
!p.leaderRevived &&
(auditorFirstReturn || (cycle > 0 && leader.deaths === cycle));
(auditorReturn || (cycle > 0 && leader.deaths === cycle));
if (
!normal &&
(p.faction !== 'tleilaxu' ||
@@ -233,8 +239,12 @@ export function leaderRevivalOptions(
cycle >= (p.kwisatz.revivalCycle ?? 1)
? { cost: kwisatzCost, affordable: p.spice >= kwisatzCost }
: null;
return { cycle, leaders, kwisatz, dukeBlocked, cycleBlock: repeatHistory
? 'Ecaz six-disc repeated revival cycles are not yet resolved.' : null };
return { cycle, leaders, kwisatz, dukeBlocked,
...(g.advanced && p.faction === 'choam' && p.leaders.some(isAuditorLeader)
? { auditorCyclePolicy: 'Supplied Advanced p29 permits Auditor revival each turn. Provisional ordinary-cycle policy: only the five ordinary CHOAM discs determine revival cycles; a living or repeatedly killed Auditor neither delays nor opens them. Auditor still uses the normal one-leader allowance.' }
: {}),
cycleBlock: repeatHistory
? 'Ecaz six-disc repeated revival cycles are not yet resolved.' : null };
}
export type PendingRevival = {
player: string;