Connect Diplomat used-defense contexts

This commit is contained in:
Quintine committed 2026-10-17 12:12:19 +11:00
1 parent 63298be1e1
commit 628f59b73a
14 files changed
+1048 -66

No files matched your search

+9 -4
View File
@@ -86,7 +86,11 @@ export type ResolutionCombatant = ResolutionParticipant & {
reinforcementsReserves?: { normal: number; elite: number };
harassWithdraw?: HarassWithdrawContext;
harassSelection?: HarassWithdrawSelection;
diplomatDefense?: Pick<DiplomatDefenseQuote, 'leader' | 'source' | 'kind'> & { card: string };
diplomatDefense?: Pick<DiplomatDefenseQuote, 'leader' | 'source' | 'kind'> & {
card: string;
/** Authoritative current battle opted into used-defense contexts. */
contextual?: true;
};
spice: number;
hand: readonly Card[];
plan: ResolutionPlan;
@@ -381,10 +385,11 @@ function calculate(input: BattleResolutionInput): BattleResolutionQuote {
if (copy === undefined) return defense;
const quote = quoteDiplomatDefense({ assignments: side.leaderSkills ?? [], selectedLeader: side.plan.leader,
weapon: card(side, side.plan.weapon), defense: card(side, side.plan.defense),
opposingDefense: card(other, other.plan.defense), opposingWeapon: card(other, other.plan.weapon),
opposingDefense: card(other, copy?.contextual ? other.lateDefense ?? other.plan.defense : other.plan.defense),
opposingWeapon: card(other, other.plan.weapon),
expandedDefenses: true });
requireQuote(copy && quote && !side.lateDefense && !other.lateDefense &&
!side.stronghold && !other.stronghold && !input.homeworld &&
requireQuote(copy && quote && !side.lateDefense &&
(copy.contextual || (!other.lateDefense && !side.stronghold && !other.stronghold && !input.homeworld)) &&
copy.leader === quote.leader && copy.source === quote.source && copy.kind === quote.kind && quote.cards.includes(copy.card),
'The copied Diplomat defense lost its native trainer, committed Worthless or opposing printed defense.');
return copiedDiplomatDefense(quote, copy.card);
+34 -15
View File
@@ -733,7 +733,7 @@ export type Battle = {
mentatQuestionEvent?: string;
mentatQuestion?: MentatQuestionReceipt;
/** New battles opt into the revealed Diplomat step; legacy battles are not reopened. */
diplomatDefenseVersion?: 1 | 2;
diplomatDefenseVersion?: 1 | 2 | 3;
diplomatDefenseEvent?: string;
diplomatDefense?: DiplomatDefenseReceipt;
/** New battles may pause after the outcome is known, before loser losses. */
@@ -22573,6 +22573,10 @@ function validatePortableSnooper(
!b.lateDefense?.[owner.id],
'You have already added Portable Snooper to this battle.',
);
requireRule(
b.diplomatDefense?.player !== owner.id || b.diplomatDefense.stage !== 'copied',
'Your copied Diplomat card already supplies a defense.',
);
const card = owner.hand.find((c) => c.id === cardId);
requireRule(
card && isPortableSnooper(card),
@@ -22705,10 +22709,15 @@ function battleSpecialWeaponIntegrity(g: Game) {
}
function diplomatDefenseOffer(g: Game): (DiplomatDefenseQuote & { player: string }) | null {
const b = g.battle;
if (!b?.revealed || !g.leaderSkills || !diplomatDefenseModeSupported(g) ||
b.lateDefense && Object.keys(b.lateDefense).length) return null;
if (!b?.revealed || !g.leaderSkills || !diplomatDefenseModeSupported(g)) return null;
const contextual = b.diplomatDefenseVersion === 3;
if (!contextual && b.lateDefense && Object.keys(b.lateDefense).length) return null;
if (contextual && !b.diplomatDefense && traitorVoters(g, b).some(id => b.traitorCalls[id] === true)) return null;
for (const player of [b.attacker, b.defender]) {
const p = getPlayer(g, player), plan = b.plans[player];
// A decline remains historical if its owner later adds a real defense.
// Only a fresh copy opportunity needs the owner's unused defense role.
if (contextual && b.lateDefense?.[player] && !b.diplomatDefense) continue;
const other = getPlayer(g, b.attacker === player ? b.defender : b.attacker);
const opposingPlan = b.plans[other.id];
if (!plan || !opposingPlan) continue;
@@ -22718,9 +22727,9 @@ function diplomatDefenseOffer(g: Game): (DiplomatDefenseQuote & { player: string
.map(a => ({ skill: a.skill, leader: a.leader, faceUp: !b.leaderSkillHidden?.[player], captured: false })),
selectedLeader: plan.leader,
weapon: cardOf(p, plan.weapon), defense: cardOf(p, plan.defense),
opposingDefense: cardOf(other, opposingPlan.defense),
opposingDefense: cardOf(other, contextual ? b.lateDefense?.[other.id] ?? opposingPlan.defense : opposingPlan.defense),
opposingWeapon: cardOf(other, opposingPlan.weapon),
expandedDefenses: b.diplomatDefenseVersion === 2,
expandedDefenses: b.diplomatDefenseVersion === 2 || contextual,
});
if (quote) return { ...quote, player };
}
@@ -22728,8 +22737,14 @@ function diplomatDefenseOffer(g: Game): (DiplomatDefenseQuote & { player: string
}
function diplomatDefenseFrame(g: Game): string {
const b = g.battle!;
// Bind the opponent's used source, not a later own defense after declining.
const player = b.diplomatDefenseVersion === 3
? b.diplomatDefense?.player ?? diplomatDefenseOffer(g)?.player : null;
const opponent = player ? getPlayer(g, player === b.attacker ? b.defender : b.attacker) : null;
return JSON.stringify({ event: b.event, turn: g.turn, territory: b.territory,
attacker: b.attacker, defender: b.defender, plans: b.plans, hidden: b.leaderSkillHidden,
...(b.diplomatDefenseVersion === 3
? { lateSource: opponent ? cardOf(opponent, b.lateDefense?.[opponent.id] ?? null) ?? null : null } : {}),
cards: [b.attacker, b.defender].map(id => {
const p = getPlayer(g, id), plan = b.plans[id];
return [plan.weapon, plan.defense, plan.leader].map(selected => cardOf(p, selected) ?? null);
@@ -22743,7 +22758,7 @@ function diplomatDefenseIntegrity(g: Game) {
const decisions = homeworldSavedDecisions(g).filter(d => d.kind === 'diplomatDefense');
if (b?.diplomatDefenseVersion === undefined && b?.diplomatDefense === undefined &&
b?.diplomatDefenseEvent === undefined && !decisions.length) return;
requireRule(b && (b.diplomatDefenseVersion === 1 || b.diplomatDefenseVersion === 2),
requireRule(b && (b.diplomatDefenseVersion === 1 || b.diplomatDefenseVersion === 2 || b.diplomatDefenseVersion === 3),
'The saved Diplomat choice lost its original battle version.');
const receipt = b.diplomatDefense;
const offer = diplomatDefenseOffer(g);
@@ -22766,8 +22781,10 @@ function diplomatDefenseIntegrity(g: Game) {
'The committed Diplomat cards must retain unique physical custody.');
requireRule(decisions.length === (receipt.stage === 'offered' ? 1 : 0),
'The saved Diplomat defense lost or reopened its unique public decision.');
if (receipt.stage === 'offered') requireRule(Object.keys(b.traitorCalls).length === 0,
'Diplomat defense must be chosen before any traitor decisions.');
if (receipt.stage === 'offered') requireRule(b.diplomatDefenseVersion === 3
? traitorVoters(g, b).every(id => b.traitorCalls[id] !== true)
: Object.keys(b.traitorCalls).length === 0,
'Choose Diplomat defense before a successful traitor declaration or battle outcome.');
for (const decision of decisions) requireRule(decision.player === receipt.player &&
decision.event === receipt.event && decision.source === receipt.source &&
JSON.stringify(decision.cards) === JSON.stringify(receipt.cards),
@@ -22775,11 +22792,11 @@ function diplomatDefenseIntegrity(g: Game) {
}
function offerDiplomatDefense(g: Game): boolean {
const b = g.battle!;
if ((b.diplomatDefenseVersion !== 1 && b.diplomatDefenseVersion !== 2) || b.diplomatDefense) return false;
if ((b.diplomatDefenseVersion !== 1 && b.diplomatDefenseVersion !== 2 && b.diplomatDefenseVersion !== 3) || b.diplomatDefense) return false;
const offer = diplomatDefenseOffer(g);
if (!offer) return false;
requireRule(b.event && !Object.keys(b.traitorCalls).length,
'Diplomat defense needs the current revealed battle before traitor decisions.');
requireRule(b.event && (b.diplomatDefenseVersion === 3 || !Object.keys(b.traitorCalls).length),
'Diplomat defense needs the current revealed battle before its unresolved outcome.');
const event = crypto.randomUUID();
const receipt: DiplomatDefenseReceipt = { ...offer, event, battle: b.event, turn: g.turn,
territory: b.territory, frame: diplomatDefenseFrame(g), stage: 'offered', card: null, signature: '' };
@@ -22787,7 +22804,7 @@ function offerDiplomatDefense(g: Game): boolean {
b.diplomatDefense = receipt;
b.diplomatDefenseEvent = event;
g.decision = { kind: 'diplomatDefense', player: offer.player, event, cards: [...offer.cards], source: offer.source };
log(g, `${getPlayer(g, offer.player).name} may use Diplomat to make one committed Worthless card copy the opposing ${diplomatDefenseLabel(offer.kind)} before traitor decisions.`,
log(g, `${getPlayer(g, offer.player).name} may use Diplomat to make one committed Worthless card copy the opposing ${diplomatDefenseLabel(offer.kind)} before battle resolution.`,
{ faction: getPlayer(g, offer.player).faction, name: 'Diplomat defense' });
return true;
}
@@ -22994,7 +23011,8 @@ function currentBattleResolutionQuote(g: Game, canceledVoter?: string) {
lateDefense: b.lateDefense?.[p.id],
...(b.diplomatDefense?.stage === 'copied' && b.diplomatDefense.player === p.id
? { diplomatDefense: { leader: b.diplomatDefense.leader, card: b.diplomatDefense.card!,
source: b.diplomatDefense.source, kind: b.diplomatDefense.kind } } : {}),
source: b.diplomatDefense.source, kind: b.diplomatDefense.kind,
...(b.diplomatDefenseVersion === 3 ? { contextual: true as const } : {}) } } : {}),
poisonTooth: b.poisonTooth?.[p.id],
stoneMode: b.stoneBurner?.[p.id],
...(aid ? { aid: { donor: p.ally!, amount: aid.amount } } : {}),
@@ -30444,6 +30462,7 @@ function applyActionInner(
`${p.name} played Portable Snooper after revealing their battle plan. It protects against ordinary poison without changing the original plan. Battle and traitor decisions continue.`,
{ faction: p.faction, name: 'Portable Snooper' },
);
offerDiplomatDefense(g);
return g;
}
if (t === 'battlePreparationReady') {
@@ -31760,7 +31779,7 @@ function applyActionInner(
log(g, `${p.name} added Portable Snooper after the Homeworld battle plans were revealed.`, {faction: p.faction, name: 'Portable Snooper'});
}
(b.homeworldDefensePassed ??= []).push(id);
advanceHomeworldReveal(g);
if (!offerDiplomatDefense(g)) advanceHomeworldReveal(g);
} else if (decision.kind === 'homeworldExplosion') {
homeworldBattleLossIntegrity(g);
homeworldSubstitutionIntegrity(g);
@@ -33795,7 +33814,7 @@ function applyActionInner(
event: battleEvent,
...(g.leaderSkills ? { smugglerCollectionVersion: 1 as const } : {}),
...(g.leaderSkills && mentatQuestionModeSupported(g) ? { mentatQuestionVersion: 1 as const } : {}),
...(g.leaderSkills && diplomatDefenseModeSupported(g) ? { diplomatDefenseVersion: 2 as const } : {}),
...(g.leaderSkills && diplomatDefenseModeSupported(g) ? { diplomatDefenseVersion: 3 as const } : {}),
...(g.leaderSkills && ordinaryLeaderSkillModeSupported(g) ? { diplomatRetreatVersion: 1 as const } : {}),
...(g.ecazTreachery ? { harassAllocationVersion: 1 as const } : {}),
...(byFaction(g, 'richese')
+2 -2
View File
@@ -4952,7 +4952,7 @@ export const RULE_TOPICS: RuleTopic[] = [
'Rihani normally inspects two random Traitor Deck cards privately, then reshuffles them. A surviving selected native Rihani also offers a separate optional draw of two. Decide before seeing them: once drawn, keep one new card and publicly reveal and return one unused old card. The other new card returns secretly. A captured Rihani grants only the optional exchange. Your private history records both samples; a card just called to win is already used.',
'Smuggler can include one free accompanying force in an off-planet shipment into an empty territory. Select the entire physical total; three forces cost the ordinary price of two. The checkbox can decline this optional benefit. Guild rounding follows the free force. Richese may instead ship one separate free reserve force beside its owned No-Field into an empty territory; the marker retains its ordinary price and hidden value. Existing forces, including advisors, prevent the bonus; ordinary Fremen on-planet reinforcement has no bonus.',
'Spice Banker can commit one through three own spice with its Battle Plan, separate from force support. Only the surviving selected skilled disc gains that strength. The amount stays private until authorized plan inspection or public reveal. It is paid on win or loss, including leader death, except a sole traitor-call winner spends nothing. Normal deferred income connects only in the separate fresh classic or supported native banker-income profiles, including the bounded Advanced Moritani roster; ordinary/default starts are unchanged.',
'Diplomat may use one committed Worthless as the opponent’s canonical printed defensive role when you played no actual defense: Shield, Snooper, Shield Snooper, defensive Weirding Way or Chemistry, or sealed Portable Snooper. Hybrid protection, Weirding’s non-Shield behavior and Chemistry’s Poison Tooth exception remain distinct. Copying the used role without its activation or own-slot prerequisite is the explicit first-version interpretation. Choose or decline after reveal, before traitors; the Worthless must be discarded even if you win. Already-open base-only battles keep that source window. A native Diplomat can protect its own selected disc; a captive grants no upper band. A surviving trained loser may retreat selected undialed physical counters up to printed/copied disc strength to one empty adjacent non-stronghold before losses. Late/mirror/numeric modifications remain separate.',
'Diplomat may use one committed Worthless as the opponent’s canonical printed defensive role when you played no actual defense: Shield, Snooper, Shield Snooper, defensive Weirding Way/Chemistry, or sealed/actually late-played Portable Snooper. Named protection stays distinct; copying the used role imports no activation, numeric modifier or late-play right. Sealed sources offer after reveal; a new late source may offer before the unresolved outcome, preserving prior false traitor decisions. No fresh opportunity follows a successful traitor declaration. The selected Worthless discards even after a win. Older base/printed-only battles keep their original source windows. Current World/Stronghold contexts retain independent original effects; own earned Carthag may add Snooper to a copied Shield but cannot create protection without a used source. A native Diplomat can protect its selected disc; captives grant no normal band. Surviving trained losers retain the separate printed/copied-strength retreat policy. Mirror/numeric inheritance remains separate.',
'Normal Mentat questions use the user-approved uniform private card-show step before posture and faction powers in supported Leader Skills games. The opponent shows the named held weapon or chooses another held card, even for an exact match or sole eligible card. Weirding Way is a nameable default weapon; Chemistry is not. Cards stay held and need not enter a Battle Plan. New battles activate the question without an extra opt-in; already-opened unversioned battles retain their original flow. Empty hands skip without an answer. Private history records an observation, not a later hand guarantee.',
'Bureaucrat may redirect two spice when one other player pays at least five to a player other than the skill owner, once per phase. Payer costs stay unchanged; declining preserves later use. Bribes qualify at transfer and remain unavailable until Mentat Pause. Supported ordinary/Richese auctions, Guild shipments and paid Tleilaxu revival have connected paths; a separate free-revival award cannot make a smaller price qualify. Allied payer shares never pool to reach five. Trainer or recipient contributions cannot qualify but do not hide an eligible other payer. Public prompts show gross and recipient, not private shares or which payer qualified. Supported classic and native E1/E2/E3 profiles and Homeworld Skills retain original costs and callbacks. Native Guild/Emperor/revival Karama cancellation resolves before Bureaucrat; Richese retains direct seller credit with no invented cancellation window. Basic has no Black Market; free Karama purchases remain normal-only. By user ruling Bureaucrat takes two off gross before occupied Homeworld allocation. Fresh classic occupation plus Skills now connects this: eight becomes two to Bank, three native and three occupied, with full original payer contributions retained. Decline gives four plus four; native income cancellation preserves the separate occupied share and creates no Bureaucrat offer. The low/occupied Guild two-odd contributor rounding boundary is unchanged. By user ruling Guild Nexus Betrayal also carries Bureaucrat on its holder receipt in the bounded classic Nexus plus Skills composition; the holder receives the fee minus two. Emperor gifts remain excluded by user ruling, without threshold disclosure. Other profiles, overlays and full-mode acceptance remain unfinished.',
'Smuggler records existing positive battle-territory piles at public reveal. Provisional modifier policy: cap by copied/printed disc plus declared Leader Skill bonuses and Kwisatz2. Pending spice cannot fund plans. A surviving controller collects even when losing or no forces remain, unless an actual zero-strength weapon effect resolves. Artillery Strike supplies that effect; the ordinary projectile Stunner does not. A sole own Traitor bypasses weapons and retains collection. Partial multi-pile collection asks its owner for exact sectors; all/none and single piles stay automatic. Both the modifier and allocation policies are explicit interpretations, not publisher rulings. Collection precedes Sandmaster; a drained pile receives none. Other module guards remain.',
@@ -4966,7 +4966,7 @@ export const RULE_TOPICS: RuleTopic[] = [
{ area:'Player controls',status:'Partial',detail:'Private card/leader selection, readable inspectors, public battle posture and revival draw/decline, Planetologist range/gather controls, Special weapon selection and Suk Graduate physical rescue choices are connected. Rihani has private history and separate keep-new/reveal-old controls; automatic battle effects need no confirmation. Smuggler has explicit ordinary shipping opt-out and No-Field companion opt-in with physical-versus-priced force counts, plus surviving-leader battle collection with reveal-time guidance and owned multi-pile allocations. Banker exposes separate sealed spending and authorized inspection. Sandmaster exposes explicit legal routes, per-territory collection choices, optional worm/physical Ambassador destination checkboxes and direct victory-pile placement when several piles remain. Diplomat offers a named committed Worthless choice or decline after public reveal, and a separate surviving-loser destination/normal/elite retreat or decline. Bureaucrat offers redirect or full payment with explicit recipient amounts and deferred bribe custody. Remaining effect controls are missing.',evidence:['components/leader-skills.tsx','components/planetologist-movement.tsx','components/suk-graduate.tsx','components/rihani-decipherer.tsx','components/mentat-question.tsx','components/bureaucrat-payment.tsx','components/leader-skill-battle-guide.tsx','components/smuggler-shipment.tsx','components/smuggler-battle-collection.tsx','components/spice-banker.tsx','components/sandmaster-movement.tsx','components/sandmaster-worm.tsx','components/sandmaster-victory-spice.tsx','components/ambassador-movement.tsx','components/diplomat-defense.tsx','components/diplomat-retreat.tsx','components/game-table.tsx'] },
{ area:'AI',status:'Partial',detail:'All four profiles use private offers, legal visibility/revival actions and Planetologist movement/battle candidates and legal Suk Graduate rescues, Rihani exchanges, shared battle scoring, Smuggler reserve/owned No-Field shipments and modifier-inclusive collection plans, exact multi-pile Smuggler allocations, Sandmaster route, worm, physical Ambassador destination collection and victory-pile placement, funded Banker commitments and optional Diplomat copying from public revealed defenses or choosing legal losing-battle retreat. Bureaucrat uses the public payee alliance to redirect enemy income or preserve allied income. These are legal prototype paths; complete skill strategy and difficulty calibration remain unfinished.',evidence:['game/bots.ts'] },
{ area:'Documentation',status:'Partial',detail:'All fourteen faces and common rules are recorded. Skilled capture follows public physical-card custody. The 13 October 2026 ruling resolves the captured-card replacement question: the original assignment is retained and no replacement is drawn while it exists; other material timing interpretations remain explicit.',evidence:['docs/LEADER_SKILLS_RULES.md','docs/LEADER_SKILLS_CAPTURE.md','docs/LEADER_SKILLS_RUNTIME.md','docs/PLANETOLOGIST_RULES.md','docs/SUK_GRADUATE_RULES.md','docs/LEADER_BATTLE_EFFECTS.md','docs/MENTAT_QUESTION.md','docs/BUREAUCRAT_PAYMENTS.md','docs/SMUGGLER_SHIPMENT.md','docs/SPICE_BANKER_RUNTIME.md','docs/SANDMASTER_WORM.md','docs/DIPLOMAT_DEFENSE.md'] },
{ area: 'Verification', status: 'Partial', detail: 'Earlier native/optional-module and recovery evidence remains dated in linked contracts. Modified Smuggler adds actual seven-loss KH activation, short/ample reveal caps, shielded Artillery zeroing, sole-own-Traitor precedence and ordinary Stunner collection. Focused23 cases pass. Four390px journeys/32 actions retain winner cleanup and separate Collection; eight four-policy programmes/64 actions use offered modified KH plans. Provisional policy is not a publisher ruling or full-mode/deployed acceptance.', evidence: ['tests/smuggler-kwisatz-journey.test.ts', 'tests/fixture-smuggler-kwisatz-journey.ts', 'tests/smuggler-artillery-journey.test.ts', 'tests/fixture-smuggler-artillery-journey.ts', 'docs/SMUGGLER_BATTLE.md', 'tests/homeworld-classic-nexus-skills-runtime.test.ts', 'tests/homeworld-classic-discovery-nexus-runtime.test.ts','tests/homeworld-native-e1e2-modules-runtime.test.ts','tests/homeworld-native-e3-modules-runtime.test.ts','tests/homeworld-classic-skills-runtime.test.ts','tests/homeworld-skills-discovery-runtime.test.ts','tests/homeworld-optional-modules-runtime.test.ts','tests/leader-skills-engine.test.ts','tests/leader-skills-capture.test.ts','tests/captured-skill-revival.test.ts','tests/fixture-captured-skill-revival.ts','tests/captured-skill-special-revival.test.ts','tests/fixture-captured-skill-special-revival.ts','tests/leader-battle-effects.test.ts','tests/suk-graduate.test.ts','tests/mentat-question.test.ts','tests/bureaucrat-payment.test.ts','tests/smuggler-battle.test.ts','tests/spice-banker.test.ts','tests/advanced-native-skills.test.ts','tests/fixture-advanced-native-skills.ts','tests/skills-tech-payments-runtime.test.ts','tests/skills-tech-battle-runtime.test.ts','tests/ix-choam-skills-tech-runtime.test.ts','tests/tleilaxu-skills-tech-runtime.test.ts','tests/richese-skills-tech-runtime.test.ts','tests/classic-skills-stronghold-runtime.test.ts','tests/native-skills-stronghold-runtime.test.ts','tests/ecaz-skills-modules-runtime.test.ts','tests/moritani-skills-modules-runtime.test.ts','docs/LEADER_SKILLS_RUNTIME.md','docs/HOMEWORLD_RUNTIME_IMPLEMENTATION.md'] },
{ area: 'Verification', status: 'Partial', detail: 'Earlier native/Smuggler/optional-module and recovery evidence remains dated in linked contracts. Used-defense capability3 adds preserved false-vote late copying, native-only Homeworld continuation and independently earned Carthag protection, with true-traitor, own-defense and legacy boundaries. Four390px journeys/25 actions reach original Collection; twelve four-policy programmes/40 actions retain legal copy/decline behavior. Printed-role/context scheduling remains provisional, not complete-mode, full strategy or deployed acceptance.', evidence: ['tests/diplomat-used-defense.test.ts', 'tests/diplomat-late-journey.test.ts', 'tests/fixture-diplomat-late-journey.ts', 'tests/diplomat-context-journey.test.ts', 'tests/fixture-diplomat-context-journey.ts', 'docs/DIPLOMAT_DEFENSE.md', 'tests/smuggler-kwisatz-journey.test.ts', 'tests/fixture-smuggler-kwisatz-journey.ts', 'tests/smuggler-artillery-journey.test.ts', 'tests/fixture-smuggler-artillery-journey.ts', 'docs/SMUGGLER_BATTLE.md', 'tests/homeworld-classic-nexus-skills-runtime.test.ts', 'tests/homeworld-classic-discovery-nexus-runtime.test.ts','tests/homeworld-native-e1e2-modules-runtime.test.ts','tests/homeworld-native-e3-modules-runtime.test.ts','tests/homeworld-classic-skills-runtime.test.ts','tests/homeworld-skills-discovery-runtime.test.ts','tests/homeworld-optional-modules-runtime.test.ts','tests/leader-skills-engine.test.ts','tests/leader-skills-capture.test.ts','tests/captured-skill-revival.test.ts','tests/fixture-captured-skill-revival.ts','tests/captured-skill-special-revival.test.ts','tests/fixture-captured-skill-special-revival.ts','tests/leader-battle-effects.test.ts','tests/suk-graduate.test.ts','tests/mentat-question.test.ts','tests/bureaucrat-payment.test.ts','tests/smuggler-battle.test.ts','tests/spice-banker.test.ts','tests/advanced-native-skills.test.ts','tests/fixture-advanced-native-skills.ts','tests/skills-tech-payments-runtime.test.ts','tests/skills-tech-battle-runtime.test.ts','tests/ix-choam-skills-tech-runtime.test.ts','tests/tleilaxu-skills-tech-runtime.test.ts','tests/richese-skills-tech-runtime.test.ts','tests/classic-skills-stronghold-runtime.test.ts','tests/native-skills-stronghold-runtime.test.ts','tests/ecaz-skills-modules-runtime.test.ts','tests/moritani-skills-modules-runtime.test.ts','docs/LEADER_SKILLS_RUNTIME.md','docs/HOMEWORLD_RUNTIME_IMPLEMENTATION.md'] },
],
},
{