Preserve Emperor gift opportunity before bot Extortion
Verify and publish checkpoint container / container (push) Successful in 10m54s
Verify and publish checkpoint container / container (push) Successful in 10m54s
This commit is contained in:
1 parent
a6be17fc5d
commit
7c1f4b4e09
13 files changed
+370
-13
No files matched your search
+35
-2
@@ -2379,6 +2379,8 @@ export type Game = {
|
||||
moritaniTerror?: TerrorState;
|
||||
moritaniAtomics?: AtomicsAftermath | null;
|
||||
moritaniExtortion?: ExtortionState;
|
||||
/** Public release of the human Emperor's optional gift opportunity. */
|
||||
emperorExtortionGiftRelease?: { event: string; emperor: string; recipient: string };
|
||||
grummanCollection?: GrummanCollection;
|
||||
pendingMoritaniPlacement?: {
|
||||
token: string;
|
||||
@@ -30345,7 +30347,7 @@ function emperorExtortionGiftSignature(g: Game, owner: string, recipient: string
|
||||
return JSON.stringify([g.turn, g.phase, g.active, g.order, g.decision,
|
||||
g.moritaniExtortion, g.moritaniTerror?.placementTurn, owner, recipient, amount]);
|
||||
}
|
||||
function emperorExtortionGiftQuote(g: Game, p: Player): {recipient: string; maximum: number} | null {
|
||||
function emperorExtortionGiftPublicEligible(g: Game, p: Player): boolean {
|
||||
const decision = g.decision, extortion = g.moritaniExtortion;
|
||||
if (g.status !== 'playing' || g.phase !== 8 || g.expansions.length !== 1 ||
|
||||
g.expansions[0] !== 'ecaz' || g.players.some(seat =>
|
||||
@@ -30357,7 +30359,11 @@ function emperorExtortionGiftQuote(g: Game, p: Player): {recipient: string; maxi
|
||||
!extortion || extortion.stage !== 'payment' || decision?.kind !== 'moritaniExtortion' ||
|
||||
decision.event !== extortionEvent(extortion) || decision.player !== extortion.queue[extortion.cursor] ||
|
||||
p.faction !== 'emperor' || !p.ally || decision.player !== p.ally ||
|
||||
!g.players.some(seat => seat.id === p.ally && seat.ally === p.id) ||
|
||||
!g.players.some(seat => seat.id === p.ally && seat.ally === p.id)) return false;
|
||||
return true;
|
||||
}
|
||||
function emperorExtortionGiftQuote(g: Game, p: Player): {recipient: string; maximum: number} | null {
|
||||
if (!emperorExtortionGiftPublicEligible(g, p) || !p.ally ||
|
||||
liveShipmentPromises(g.shipmentPromises ?? [], p.id, g.turn).length ||
|
||||
hasMovementPromises(g, p.id, true) || hasBribePromises(g, p.id, true) ||
|
||||
liveRevivalPromises(g.revivalPromises ?? [], p.id, g.turn).some(row => row.answer) ||
|
||||
@@ -30368,6 +30374,18 @@ function emperorExtortionGiftQuote(g: Game, p: Player): {recipient: string; maxi
|
||||
const maximum = Math.max(0, uncommittedSpice(g, p));
|
||||
return maximum > 0 ? {recipient: p.ally, maximum} : null;
|
||||
}
|
||||
/** A neutral window for a human Emperor before an automated ally answers.
|
||||
* The visible wait never depends on either player's private spice or promises. */
|
||||
function emperorExtortionPriority(g: Game): {event: string; emperor: string; recipient: string} | null {
|
||||
const emperor = byFaction(g, 'emperor'), decision = g.decision;
|
||||
if (!emperor || (emperor.bot ?? emperor.autopilot) || !emperorExtortionGiftPublicEligible(g, emperor) ||
|
||||
decision?.kind !== 'moritaniExtortion') return null;
|
||||
const recipient = g.players.find(player => player.id === decision.player);
|
||||
if (!recipient || !(recipient.bot ?? recipient.autopilot)) return null;
|
||||
const release = g.emperorExtortionGiftRelease;
|
||||
if (release?.event === decision.event && release.emperor === emperor.id && release.recipient === recipient.id) return null;
|
||||
return {event:decision.event, emperor:emperor.id, recipient:recipient.id};
|
||||
}
|
||||
function validateEmperorExtortionGift(g: Game, response: ResponseWindow) {
|
||||
const parent = response.emperorExtortionGift;
|
||||
if (!parent) return;
|
||||
@@ -44943,6 +44961,14 @@ function applyActionInner(
|
||||
specialKarama(g, p, action);
|
||||
return g;
|
||||
}
|
||||
if (t === 'emperorExtortionContinue') {
|
||||
const priority = emperorExtortionPriority(g);
|
||||
requireRule(priority && priority.emperor === id && action.event === priority.event &&
|
||||
Object.keys(action).sort().join(',') === 'event,type',
|
||||
'Continue only your current Emperor Extortion gift opportunity.');
|
||||
g.emperorExtortionGiftRelease = priority;
|
||||
return g;
|
||||
}
|
||||
if (t === 'emperorGift' && g.decision?.kind === 'moritaniExtortion' && !g.response) {
|
||||
const quote = emperorExtortionGiftQuote(g, p);
|
||||
requireRule(quote && Object.keys(action).sort().join(',') === 'amount,type',
|
||||
@@ -45081,6 +45107,9 @@ function applyActionInner(
|
||||
t === 'decision' && decision.player === id,
|
||||
'Waiting for the player with the pending decision.',
|
||||
);
|
||||
if (decision.kind === 'moritaniExtortion')
|
||||
requireRule(!emperorExtortionPriority(g),
|
||||
'Wait for the Emperor to finish the allied gift opportunity.');
|
||||
g.decision = null;
|
||||
requireRule(decision.kind !== 'leaderSkillVisibility' && decision.kind !== 'leaderSkillRevival', 'Use the Leader Skills controls for this decision.');
|
||||
if (decision.kind === 'moritaniAssassinate') {actMoritaniAssassinate(g,decision,action);return g;}
|
||||
@@ -49913,6 +49942,10 @@ export function viewGame(state: Game, id: string, now?: number) {
|
||||
},
|
||||
emperorGuildTimingGift: emperorGuildTimingGiftQuote(g, me),
|
||||
emperorExtortionGift: emperorExtortionGiftQuote(g, me),
|
||||
emperorExtortionPriority: (() => {
|
||||
const priority = emperorExtortionPriority(g);
|
||||
return priority ? {event:priority.event, recipient:priority.recipient, canContinue:priority.emperor === id} : null;
|
||||
})(),
|
||||
emperorPhaseOpeningGift: emperorPhaseOpeningGiftQuote(g, me),
|
||||
emperorAuctionGiftPending: g.pendingEmperorAuctionGift
|
||||
? { owner: g.pendingEmperorAuctionGift.owner } : null,
|
||||
|
||||
Reference in new issue
Block a user