fix: unify native Bureaucrat allied funding receipts
This commit is contained in:
1 parent
60d1bd3b59
commit
06342ad34c
15 files changed
+478
-156
No files matched your search
@@ -50,13 +50,6 @@ export function bureaucratPaymentModeSupported(game: LeaderSkillProfile): boolea
|
||||
(game.expansions.length === 1 && game.expansions[0] === 'choam') ||
|
||||
nativeExpansionLeaderSkillsProfile(game))) || homeworldLeaderSkillsProfile(game) || classicTechLeaderSkillsProfile(game) || nativeTechLeaderSkillsProfile(game) || strongholdLeaderSkillsProfile(game) || classicNexusLeaderSkillsProfile(game) || standaloneE3NexusLeaderSkillsProfile(game) || classicDiscoveryLeaderSkillsProfile(game) || nativeDiscoveryLeaderSkillsProfile(game);
|
||||
}
|
||||
export function bureaucratAlliedFundingModeSupported(game: LeaderSkillProfile): boolean {
|
||||
if (!bureaucratPaymentModeSupported(game)) return false;
|
||||
if (game.homeworlds) return homeworldLeaderSkillsProfile(game);
|
||||
if (game.expansions.length === 1 && (game.expansions[0] === 'ecaz' || game.expansions[0] === 'ix'))
|
||||
return pairedNexusLeaderSkillsProfile(game) || pairedE3NexusLeaderSkillsProfile(game);
|
||||
return !game.expansions.length || game.expansions.length === 1 && game.expansions[0] === 'choam';
|
||||
}
|
||||
export function bureaucratUsed(
|
||||
used: readonly BureaucratPaymentUse[],
|
||||
turn: number,
|
||||
|
||||
+7
-9
@@ -7,7 +7,7 @@ import { quoteSpicePlacement, stormExposesTerritory, stormSectorAfter, wormConsu
|
||||
import { isStormCardDistance, type StormCardComponent } from './storm-cards';
|
||||
import { createLeaderSkills, validateLeaderSkills, dealLeaderSkills, chooseLeaderSkill, returnDeadLeaderSkills, offerRevivedLeaderSkill, drawRevivedLeaderSkills, declineRevivedLeaderSkill, LeaderSkillError, type LeaderSkillsState, type LeaderSkillsView } from './leader-skills';
|
||||
import { leaderSkillCard } from './leader-skill-cards';
|
||||
import { bureaucratAlliedFundingModeSupported, bureaucratPaymentModeSupported, bureaucratPaymentSignature, bureaucratUsed, quoteBureaucratPayment, type BureaucratPaymentSource, type BureaucratPaymentUse, type BureaucratPaymentView } from './bureaucrat-payment';
|
||||
import { bureaucratPaymentModeSupported, bureaucratPaymentSignature, bureaucratUsed, quoteBureaucratPayment, type BureaucratPaymentSource, type BureaucratPaymentUse, type BureaucratPaymentView } from './bureaucrat-payment';
|
||||
import { MENTAT_EMPTY_HAND, MentatQuestionError, mentatQuestionModeSupported, mentatWeaponNames, mentatHand, quoteMentatReveal, mentatSignature, type MentatQuestionReceipt, type MentatObservation, type MentatView } from './mentat-question';
|
||||
import {
|
||||
usesPlanetologistBattleSpecial, planetologistSpecialNeedsChoice,
|
||||
@@ -9079,15 +9079,13 @@ function bureaucratPaymentQuote(g: Game,source: Pick<BureaucratPaymentSource,'pa
|
||||
const owner = nativeBureaucrat(g)?.owner ?? null;
|
||||
return quoteBureaucratPayment({...source,owner,turn:g.turn,phase:g.phase,used:g.bureaucratPayments?.used ?? []});
|
||||
}
|
||||
function checkBureaucratContributors(g: Game,payee:string|undefined,shares:{payer:string;amount:number}[],privateFunding=false) {
|
||||
function checkBureaucratContributors(g: Game,payee:string|undefined,shares:{payer:string;amount:number}[]) {
|
||||
if (!g.leaderSkills || !payee) return;
|
||||
const paid = shares.filter(share => share.amount > 0);
|
||||
const owner = nativeBureaucrat(g)?.owner;
|
||||
if (!owner || owner === payee || bureaucratUsed(g.bureaucratPayments?.used ?? [],g.turn,g.phase) ||
|
||||
!paid.some(share => share.amount >= 5 && share.payer !== owner && share.payer !== payee)) return;
|
||||
requireRule(bureaucratPaymentModeSupported(g),'Bureaucrat with other optional modules is still being integrated.');
|
||||
requireRule(paid.length === 1 && !privateFunding || bureaucratAlliedFundingModeSupported(g),
|
||||
'Bureaucrat on allied funding or multiple payer contributions in this module composition is awaiting integration.');
|
||||
}
|
||||
function stampBureaucratPayment(g: Game,kind:BureaucratPaymentSource['kind'],payer:string,payee:string,amount:number,
|
||||
contributions?:BureaucratPaymentSource['contributions']):
|
||||
@@ -9103,7 +9101,7 @@ function stampBureaucratContributors(g:Game,kind:'auction'|'shipment',payee:stri
|
||||
const paid = shares.filter(share => share.amount > 0);
|
||||
if (!privateFunding && paid.length === 1)
|
||||
return stampBureaucratPayment(g,kind,paid[0].payer,payee,paid[0].amount);
|
||||
if (!bureaucratAlliedFundingModeSupported(g)) return {};
|
||||
if (!bureaucratPaymentModeSupported(g)) return {};
|
||||
const owner = nativeBureaucrat(g)?.owner;
|
||||
const qualifying = paid.find(share => share.amount >= 5 && share.payer !== owner && share.payer !== payee);
|
||||
return qualifying ? stampBureaucratPayment(g,kind,qualifying.payer,payee,
|
||||
@@ -9125,7 +9123,7 @@ function validateBureaucratSource(g: Game,source:BureaucratPaymentSource) {
|
||||
Number.isSafeInteger(source.amount) && source.amount >= 5 && source.signature === bureaucratPaymentSignature(source),
|
||||
'The saved Bureaucrat payment changed its actual payer, payee, amount or event.');
|
||||
if (source.contributions) {
|
||||
requireRule(bureaucratAlliedFundingModeSupported(g) && source.privateFunding === true &&
|
||||
requireRule(bureaucratPaymentModeSupported(g) && source.privateFunding === true &&
|
||||
(source.kind === 'auction' || source.kind === 'shipment') &&
|
||||
source.contributions.length >= 1 && source.contributions.length <= 2 &&
|
||||
source.contributions.every(share => g.players.some(player => player.id === share.payer)) &&
|
||||
@@ -17854,7 +17852,7 @@ function checkShipmentIncomeFunding(g: Game, p: Player, cost: number, allyPaymen
|
||||
if (!guild) return;
|
||||
const amounts = shipmentIncomeContributions(g, p, cost, allyPayment);
|
||||
checkBureaucratContributors(g,g.karamaShipping?.player === p.id ? undefined : guild.id,[{payer:p.id,amount:p.id === guild.id ? 0 : cost-allyPayment},
|
||||
...(allyPayment ? [{payer:p.ally!,amount:p.ally === guild.id ? 0 : allyPayment}] : [])],allyPayment > 0);
|
||||
...(allyPayment ? [{payer:p.ally!,amount:p.ally === guild.id ? 0 : allyPayment}] : [])]);
|
||||
validateGuildPaymentContributions(g, amounts.reduce((sum, amount) => sum + amount, 0), amounts);
|
||||
}
|
||||
function guildPaymentResponse(g: Game, owner: string, amounts: number[],payer?:string,
|
||||
@@ -32834,7 +32832,7 @@ function applyActionInner(
|
||||
validateRicheseFunding(amount, allyPayment, ownAvailable, allyAvailable);
|
||||
if (amount !== null) {
|
||||
const parties = auctionBureaucratParties(g,p,amount,allyPayment,lot.owner);
|
||||
checkBureaucratContributors(g,parties.payee,parties.shares,allyPayment > 0);
|
||||
checkBureaucratContributors(g,parties.payee,parties.shares);
|
||||
}
|
||||
g.richeseAuction = submitRicheseBid(
|
||||
lot,
|
||||
@@ -32888,7 +32886,7 @@ function applyActionInner(
|
||||
? contribution(g, p, a.bid, action.allyPayment)
|
||||
: 0;
|
||||
const parties = auctionBureaucratParties(g,p,a.bid,a.allyPayment);
|
||||
checkBureaucratContributors(g,parties.payee,parties.shares,a.allyPayment > 0);
|
||||
checkBureaucratContributors(g,parties.payee,parties.shares);
|
||||
a.bidder = id;
|
||||
a.passed = [];
|
||||
} else if (!a.passed.includes(id)) a.passed.push(id);
|
||||
|
||||
@@ -4956,6 +4956,7 @@ export const RULE_TOPICS: RuleTopic[] = [
|
||||
'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.',
|
||||
'Registered all14 tables use one ordinary Bureaucrat receipt capability for both single and allied funding. Selected native E1/E2/E3 decks, original Worlds and supported phase modules need no incidental Nexus Cards or exact single-family selection. A native worm alliance is not a Nexus Card. Module and Advanced role admission remain separate; full combined-mode acceptance is 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.',
|
||||
'In classic and supported E1/E2 native Tech composition, actual Smuggler prices and normal Banker qualifying fees remain distinct from phase-end Tech income. Tech piles cannot fund their triggering action; Banker front-shield grants still wait for actual Mentat. Winning physical Suk rescue and Sandmaster board spice precede original played-card cleanup and mandatory winner token reward. One token transfers automatically, several require a selection, and no token skips without an invented decline. A later Face Dancer retains the original winner’s cards, bounty and Tech reward, kills the matching trainer without another bounty and replaces only actual survivors from real sources. Native Tleilaxu-only free revival excludes Axlotl; CHOAM initial income is not Charity. Richese’s free companion does not change its marker price or create the concealed counters early.',
|
||||
'Physical Fremen Ambassador relocation offers optional Sandmaster destination collection before arrival reactions, without an invented route or ordinary movement use. Same-territory repositioning earns none; concealed relocation and other special movement retain their own limits. Normal Banker income is mandatory in fresh supported ordinary Skills setups, with provisional original-earned-faction custody and no retrofit. Diplomat retreat retains its conservative outcome order. Native Advanced foreign gholas/Face Dancers, skill-first Moritani assassination and ordinary-disc Ecaz coalition skills follow their bounded contracts; shared-Duke assignment, exceptional roles and other combined modules remain unfinished. Normal module starts are labelled previews, not complete-mode acceptance.',
|
||||
|
||||
Reference in new issue
Block a user