Support Atreides special Karama truth commitments
Verify and publish checkpoint container / container (push) Successful in 10m46s
Verify and publish checkpoint container / container (push) Successful in 10m46s
This commit is contained in:
1 parent
add8e1d51a
commit
5079ab556f
16 files changed
+408
-19
No files matched your search
+36
-4
@@ -19001,7 +19001,8 @@ function karamaSpendingBlock(g: Game, p: Player, card: Card, purpose?: KullPurpo
|
||||
required.effect === 'karamaBattleCancel' && !!g.response &&
|
||||
purpose === karamaUsePurpose(g, { kind: 'cancel', response: g.response }) ||
|
||||
required.effect === 'karamaHarkonnenSpecial' && purpose === 'special:harkonnen' ||
|
||||
required.effect === 'karamaEmperorSpecial' && purpose === 'special:emperor') &&
|
||||
required.effect === 'karamaEmperorSpecial' && purpose === 'special:emperor' ||
|
||||
required.effect === 'karamaAtreidesSpecial' && purpose === 'special:atreides') &&
|
||||
windowCardOpportunity(g, required.effect)?.source === required.source))
|
||||
return 'Retain this opportunity without a forbidden printed Karama cost.';
|
||||
const kullBlock = kullKaramaBlock(g, p, purpose);
|
||||
@@ -20493,7 +20494,7 @@ function spendKarama(g: Game, p: Player, card: Card, use: KaramaUse, kullOffered
|
||||
// still-open Harkonnen special promise before a foreign response can park
|
||||
// the shared reconciler.
|
||||
if (card.kind === 'special' && card.effect === 'karama' && auctionRows.some(row => {
|
||||
return (['karamaHarkonnenSpecial', 'karamaEmperorSpecial'] as const).some(effect => {
|
||||
return (['karamaHarkonnenSpecial', 'karamaEmperorSpecial', 'karamaAtreidesSpecial'] as const).some(effect => {
|
||||
const stage = row.windowCards?.[effect];
|
||||
return stage && !stage.closed && windowCardOpportunity(g, effect, stage.source)?.source === stage.source;
|
||||
});
|
||||
@@ -22387,6 +22388,18 @@ function quotedAmalReady(g: Game, actor: string): boolean {
|
||||
function* nativeWindowCardActions(g: Game, actor: string, effect: WindowCardEffect): Generator<Action> {
|
||||
if (!windowCardOpportunity(g, effect)) return;
|
||||
const player = getPlayer(g, actor);
|
||||
if (effect === 'karamaAtreidesSpecial') {
|
||||
if (player.faction !== 'atreides' || g.kullPreview || g.pendingKull || g.semutaPreview ||
|
||||
g.semutaRecruitsPreview || g.semutaAdvancedOrdinaryPreview || g.truthtrance || g.response ||
|
||||
g.decision || g.phaseOpening || g.pendingKarama || g.pendingTreacheryDiscard || g.pendingNullentropy || !g.battle) return;
|
||||
for (const card of player.hand.filter(card => card.kind === 'special' && card.effect === 'karama'))
|
||||
for (const target of [g.battle.attacker, g.battle.defender]) {
|
||||
const action: Action = { type: 'card', mode: 'special', card: card.id, target };
|
||||
try { if (prepareSpecialKaramaIntent(g, actor, action).kind === 'atreides') yield action; }
|
||||
catch (error) { if (!(error instanceof RuleError)) throw error; }
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (effect === 'karamaEmperorSpecial') {
|
||||
if (player.faction !== 'emperor' || g.kullPreview || g.pendingKull || g.semutaPreview ||
|
||||
g.semutaRecruitsPreview || g.semutaAdvancedOrdinaryPreview || g.truthtrance ||
|
||||
@@ -22505,7 +22518,17 @@ function* turnActionWindowCardSeeds(source: Game, actor: string, requirements: r
|
||||
for (const action of nativeWindowCardActions(state, actor, required.effect)) {
|
||||
try {
|
||||
let next: Game;
|
||||
if (required.effect === 'karamaHarkonnenSpecial') {
|
||||
if (required.effect === 'karamaAtreidesSpecial') {
|
||||
// Quote the cost and public target-first obligation without
|
||||
// inspecting or sampling the opponent's sealed battle plan.
|
||||
next = structuredClone(state);
|
||||
const intent = prepareSpecialKaramaIntent(next, actor, action);
|
||||
requireRule(intent.kind === 'atreides' && next.battle, 'Retain the original Atreides special battle.');
|
||||
const owner = getPlayer(next, actor);
|
||||
discard(next, owner, intent.card);
|
||||
owner.specialKaramaUsed = true;
|
||||
next.battle.fullPlan = { owner: actor, target: intent.target };
|
||||
} else if (required.effect === 'karamaHarkonnenSpecial') {
|
||||
// An opaque legal witness returns the unseen drawn cards to the
|
||||
// target. It spends the actual card but never samples, copies or
|
||||
// derives a benefit from another player's private hand.
|
||||
@@ -23761,7 +23784,11 @@ function projectedTurnActionGuide(g: Game, player: Player): { event: string; act
|
||||
: action.type === 'move' ? player.hand.find(card => card.id === action.movementCard) : undefined;
|
||||
const printedWindow = held?.kind === 'special' && held.effect === 'karama' && action.mode === 'purchase'
|
||||
? 'karamaAuction' : held?.kind === 'special' && held.effect === 'karama' && action.mode === 'shipment'
|
||||
? 'karamaShipment' : isWindowCardEffect(held?.effect) ? held.effect : null;
|
||||
? 'karamaShipment' : held?.kind === 'special' && held.effect === 'karama' && action.mode === 'special'
|
||||
? player.faction === 'harkonnen' ? 'karamaHarkonnenSpecial'
|
||||
: player.faction === 'emperor' ? 'karamaEmperorSpecial'
|
||||
: player.faction === 'atreides' ? 'karamaAtreidesSpecial' : null
|
||||
: isWindowCardEffect(held?.effect) ? held.effect : null;
|
||||
const windows: WindowCardEffect[] = printedWindow ? [printedWindow]
|
||||
: action.type === 'decision' && action.karama === true ? ['karamaAuction']
|
||||
: action.type === 'ready' || held?.effect === 'nullentropyBox' ? [...WINDOW_CARD_EFFECTS] : [];
|
||||
@@ -42773,7 +42800,12 @@ function resolveSpecialKaramaEffect(g: Game, p: Player, card: Card, intent: Spec
|
||||
} else if (intent.kind === 'atreides') {
|
||||
const b = g.battle!,
|
||||
target = intent.target;
|
||||
const opportunity = windowCardOpportunity(g, 'karamaAtreidesSpecial');
|
||||
if (opportunity && card.kind === 'special' && card.effect === 'karama')
|
||||
recordNativeTurnActionWindowCard(g, p.id, 'karamaAtreidesSpecial', opportunity.source);
|
||||
p.specialKaramaUsed = true;
|
||||
if (liveTurnActionPromises(g.turnActionPromises ?? [], p.id, g.turn).length)
|
||||
guardTurnActionPrintedCardCost(g, p, card);
|
||||
b.fullPlan = { owner: p.id, target };
|
||||
g.decision = null;
|
||||
log(
|
||||
|
||||
Reference in new issue
Block a user