Enable Basic mixed-deck Emperor Extortion gifts
Verify and publish checkpoint container / container (push) Successful in 12m11s
Verify and publish checkpoint container / container (push) Successful in 12m11s
This commit is contained in:
1 parent
dac1b432db
commit
3bfcd10140
7 files changed
+374
-9
No files matched your search
+30
-8
@@ -6727,6 +6727,16 @@ function emperorAuctionGiftResumeMatches(g: Game, response: ResponseWindow, deci
|
||||
: decision?.kind === 'auctionPayment' && decision.player === pending.recipient &&
|
||||
!!g.auction && g.auction.bidder === pending.recipient && !g.richeseAuction);
|
||||
}
|
||||
/** A suspended Extortion gift must resume the very same payer and queue. */
|
||||
function emperorExtortionGiftResumeMatches(g: Game, response: ResponseWindow, decision: Game['decision']): boolean {
|
||||
const parent = response.emperorExtortionGift, extortion = g.moritaniExtortion;
|
||||
return !!parent && response.kind === 'emperorGift' && g.phase === 8 &&
|
||||
!!extortion && extortion.stage === 'payment' && decision?.kind === 'moritaniExtortion' &&
|
||||
decision.player === response.recipient && decision.player === extortion.queue[extortion.cursor] &&
|
||||
decision.event === extortionEvent(extortion) && Number.isSafeInteger(response.amount) &&
|
||||
typeof response.amount === 'number' && response.amount > 0 &&
|
||||
parent.signature === emperorExtortionGiftSignature({...g, decision}, response.owner, decision.player, response.amount);
|
||||
}
|
||||
/** Preserve physical identity: a Shrine authorizes the role, not a manufactured card. */
|
||||
function semutaKaramaRoleCost(g: Game, owner: string, card: Card): boolean {
|
||||
return card.effect === 'karama' || (card.effect === 'truthtrance' &&
|
||||
@@ -8737,7 +8747,8 @@ function treacheryDiscardIntegrity(g: Game) {
|
||||
JSON.stringify(c.resume.response) === JSON.stringify(c.use.response) &&
|
||||
(c.kind === 'karamaBgConversionCancelDiscard' ||
|
||||
((c.kind === 'karamaTerminalDiscard' || c.kind === 'karamaCombatDiscard' || !c.resume.decision ||
|
||||
c.kind === 'karamaEmperorShareDiscard' && emperorAuctionGiftResumeMatches(g, c.use.response, c.resume.decision)) &&
|
||||
c.kind === 'karamaEmperorShareDiscard' && (emperorAuctionGiftResumeMatches(g, c.use.response, c.resume.decision) ||
|
||||
emperorExtortionGiftResumeMatches(g, c.use.response, c.resume.decision))) &&
|
||||
!c.resume.pendingKarama && !c.resume.phaseOpening)) &&
|
||||
batch.cause === `karama:${c.use.response.kind}` &&
|
||||
batch.entries.length === 1 &&
|
||||
@@ -10187,7 +10198,8 @@ function semutaOfferSupported(
|
||||
: ['choamCharity', 'choamInflation', 'bgCharity'].includes(continuation.resume.response?.kind ?? '')) &&
|
||||
(terminalDiscard || combatDiscard || !continuation.resume.decision ||
|
||||
continuation.kind === 'karamaEmperorShareDiscard' &&
|
||||
emperorAuctionGiftResumeMatches(g, continuation.use.response, continuation.resume.decision)) && !continuation.resume.pendingKarama &&
|
||||
(emperorAuctionGiftResumeMatches(g, continuation.use.response, continuation.resume.decision) ||
|
||||
emperorExtortionGiftResumeMatches(g, continuation.use.response, continuation.resume.decision))) && !continuation.resume.pendingKarama &&
|
||||
!continuation.resume.phaseOpening && !g.pendingKarama &&
|
||||
(!g.response || JSON.stringify(g.response) === JSON.stringify(continuation.resume.response))
|
||||
: true) &&
|
||||
@@ -14159,6 +14171,12 @@ function moritaniExtortionIntegrity(g: Game) {
|
||||
const savedDecisions = pending.stage === 'payment'
|
||||
? homeworldSavedDecisions(g).filter(decision => decision.kind === 'moritaniExtortion')
|
||||
: [];
|
||||
const continuation = g.pendingTreacheryDiscard?.continuation;
|
||||
const frame = g.pendingKull ?? (continuation?.kind === 'kullCounterDiscard' ? continuation.frame : null);
|
||||
if (!savedDecisions.length && frame?.intent.kind === 'ordinary' && frame.intent.use.kind === 'cancel' &&
|
||||
frame.resume.decision?.kind === 'moritaniExtortion' &&
|
||||
emperorExtortionGiftResumeMatches(g, frame.intent.use.response, frame.resume.decision))
|
||||
savedDecisions.push(frame.resume.decision);
|
||||
requireRule(pending.stage === 'reserved'
|
||||
? g.phase <= 8
|
||||
: pending.stage === 'payment' && g.phase === 8 &&
|
||||
@@ -20251,7 +20269,7 @@ function kullSettledRestriction(g: Game, frame: KullFrame): KullPhaseRestriction
|
||||
// Independent printed descriptors, assembled once rather than trusting a saved
|
||||
// hand to define its own identity or rebuilding the physical deck per quote.
|
||||
const kullCanonicalCards: Readonly<Record<string, Card>> =
|
||||
Object.fromEntries(treacheryDeck(['choam', 'ix']).map(card => [card.id, card]));
|
||||
Object.fromEntries([...treacheryDeck(['choam', 'ix']), ...richeseCards()].map(card => [card.id, card]));
|
||||
function canonicalKullCard(card: Card): boolean {
|
||||
const expected = Object.hasOwn(kullCanonicalCards, card.id) ? kullCanonicalCards[card.id] : undefined;
|
||||
return !!expected && card.name === expected.name && card.kind === expected.kind &&
|
||||
@@ -20861,7 +20879,8 @@ function spendKarama(g: Game, p: Player, card: Card, use: KaramaUse, kullOffered
|
||||
g.decision = null; g.pendingKarama = null; g.phaseOpening = null;
|
||||
} else if (continuation.kind === 'karamaTerminalDiscard' || continuation.kind === 'karamaCombatDiscard' ||
|
||||
continuation.kind === 'karamaEmperorShareDiscard' &&
|
||||
emperorAuctionGiftResumeMatches(g, continuation.use.response, continuation.resume.decision)) g.decision = null;
|
||||
(emperorAuctionGiftResumeMatches(g, continuation.use.response, continuation.resume.decision) ||
|
||||
emperorExtortionGiftResumeMatches(g, continuation.use.response, continuation.resume.decision))) g.decision = null;
|
||||
continuation.stateSignature = karamaResponseDiscardSignature(g, continuation);
|
||||
stageTreacheryDiscard(g, `karama:${use.response.kind}`, entries, continuation);
|
||||
return;
|
||||
@@ -30550,9 +30569,10 @@ function emperorExtortionGiftSignature(g: Game, owner: string, recipient: string
|
||||
}
|
||||
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 =>
|
||||
!['atreides', 'beneGesserit', 'emperor', 'fremen', 'guild', 'harkonnen', 'moritani'].includes(seat.faction)) ||
|
||||
if (g.status !== 'playing' || g.phase !== 8 ||
|
||||
(g.advanced ? g.expansions.length !== 1 || g.expansions[0] !== 'ecaz' || g.players.some(seat =>
|
||||
!['atreides', 'beneGesserit', 'emperor', 'fremen', 'guild', 'harkonnen', 'moritani'].includes(seat.faction))
|
||||
: !g.expansions.includes('ecaz') || !selectedPhaseModuleRoster(g)) ||
|
||||
g.ecazTreachery || g.techTokens || g.strongholdCards || g.leaderSkills || g.homeworlds || g.nexusCards ||
|
||||
g.discoveryEnabled || g.sandtrout || g.coalitionState || g.response || g.phaseOpening ||
|
||||
g.truthtrance || g.pendingKarama || g.pendingTreacheryDiscard || g.battle ||
|
||||
@@ -43432,7 +43452,9 @@ function applyActionWithInitiator(state: Game, id: string, action: Action, initi
|
||||
const extortionGiftResponse = state.response?.emperorExtortionGift ||
|
||||
(state.pendingKarama?.use.kind === 'cancel' && state.pendingKarama.use.response.emperorExtortionGift);
|
||||
if (extortionGiftResponse && action?.type !== 'advanceBots')
|
||||
requireRule(action?.type === 'passResponse' || action?.type === 'card' && action.mode === 'cancel',
|
||||
requireRule(action?.type === 'passResponse' || action?.type === 'card' && action.mode === 'cancel' ||
|
||||
action?.type === 'kullDecision' && !!state.pendingKull &&
|
||||
state.pendingKull.resume.response?.emperorExtortionGift?.event === extortionGiftResponse.event,
|
||||
'Resolve the Emperor gift response before another action.');
|
||||
if (state.pendingEmperorAuctionGift)
|
||||
requireRule(!['dealPay', 'dealOffer', 'dealRespond', 'dealWithdraw', 'dealRelease'].includes(action?.type),
|
||||
|
||||
Reference in new issue
Block a user