Resolve Sneak arrivals during Great Maker rides
Verify and publish checkpoint container / container (push) Canceled after 0s

This commit is contained in:
Quintine committed 2026-10-29 19:59:10 +11:00
1 parent 57321c2c8d
commit 2ad7f127ec
9 files changed
+290 -13

No files matched your search

+17 -3
View File
@@ -2345,6 +2345,8 @@ export type Game = {
rider: string;
parent: {kind: 'terror' | 'ambassador' | 'overlap'; event: string};
stage: 'parent' | 'queued' | 'resolving';
/** Bind the held ride to the exact Great Maker encounter when applicable. */
greatMaker?: {event: string; signature: string};
} | null;
/** One committed entry with two independently optional expansion reactions. */
pendingArrivalOverlap?: {
@@ -14578,6 +14580,7 @@ function afterWorm(g: Game) {
g.pendingAmbassador?.resume === 'wormRide' ||
g.pendingTerrorEntry?.resume === 'wormRide' ||
g.pendingArrivalOverlap?.resume === 'wormRide' ||
!!g.pendingSneakWorm ||
(g.pendingKarama?.use.kind === 'cancel' &&
g.pendingKarama.use.response.kind === 'advisorFlip' &&
g.pendingKarama.use.response.advisorResume === 'wormRide') ||
@@ -14890,6 +14893,7 @@ function stampGreatMaker(g: Game) {
g.greatMaker!.signature = greatMakerSignature(g.greatMaker!);
}
function greatMakerIntegrity(g: Game) {
sneakWormIntegrity(g);
const frame = g.greatMaker, continuation = g.pendingTreacheryDiscard?.continuation;
const controls = [g, g.pendingExchange, g.pendingNullentropy?.resume, g.pendingRicheseGift?.resume,
g.pendingRichesePurchaseIncome?.resume, continuation && 'resume' in continuation ? continuation.resume : null,
@@ -14935,6 +14939,12 @@ function greatMakerIntegrity(g: Game) {
controls.some(control => control?.decision?.kind === 'intrusion' && control.decision.wormRide && control.decision.player === byFaction(g,'beneGesserit')?.id && control.decision.territory === frame.arrival!.territory) ||
responses.some(response => response?.kind === 'advisorFlip' && response.advisorResume === 'wormRide' && response.owner === byFaction(g,'beneGesserit')?.id && response.location === frame.arrival!.territory) ||
(g.pendingAmbassador?.resume === 'wormRide' && g.pendingAmbassador.territory === frame.arrival!.territory && g.pendingAmbassador.sector === frame.arrival!.sector) ||
(g.pendingArrivalOverlap?.resume === 'wormRide' && g.pendingArrivalOverlap.cause === 'wormRide' &&
g.pendingArrivalOverlap.entrant === byFaction(g, 'fremen')?.id &&
g.pendingArrivalOverlap.territory === frame.arrival!.territory && g.pendingArrivalOverlap.sector === frame.arrival!.sector) ||
(g.pendingSneakWorm?.greatMaker?.event === frame.event && g.pendingSneakWorm.greatMaker.signature === frame.signature &&
g.pendingSneakWorm.rider === byFaction(g, 'fremen')?.id &&
g.pendingSneakWorm.turn === g.turn && g.pendingSneakWorm.phase === g.phase) ||
(g.pendingTerrorEntry?.resume === 'wormRide' && g.pendingTerrorEntry.territory === frame.arrival!.territory && g.pendingTerrorEntry.sector === frame.arrival!.sector),
'The Great Maker has lost its reserve arrival interaction.');
}
@@ -15131,6 +15141,9 @@ function sneakWormIntegrity(g: Game): void {
g.players.some(player => player.id === frame.rider && player.faction === 'fremen') &&
!!frame.parent && ['terror','ambassador','overlap'].includes(frame.parent.kind) &&
typeof frame.parent.event === 'string' && frame.parent.event.length > 0 &&
(frame.greatMaker === undefined || !!frame.greatMaker && g.greatMaker?.stage === 'arrival' &&
frame.greatMaker.event === g.greatMaker.event && frame.greatMaker.signature === g.greatMaker.signature) &&
(g.greatMaker?.stage !== 'arrival' || frame.greatMaker !== undefined) &&
(frame.stage === 'parent' || frame.stage === 'queued' ? queued.length === 1 :
frame.stage === 'resolving' && queued.length === 0),
'The saved Sneak arrival lost its original worm continuation.');
@@ -27920,8 +27933,6 @@ function sneakAttackOptions(
let blocked: string | null = null;
try {
allowedEntry(g, owner, entry.territory, entry.sector);
if (g.advanced && g.greatMaker?.stage === 'arrival')
blocked = 'Sneak Attack during a Great Maker arrival needs its original continuation; send no forces or leave the token hidden.';
} catch (error) {
if (!(error instanceof RuleError)) throw error;
blocked = error.message;
@@ -28218,7 +28229,10 @@ function decideTerror(g: Game, p: Player, action: Action) {
requireRule(rider && !g.pendingSneakWorm && g.phase === 1,
'This Sneak arrival cannot retain its original worm rider.');
const event = crypto.randomUUID();
g.pendingSneakWorm = {event, turn:g.turn, phase:g.phase, rider:rider.id, parent:wormParent!, stage:'parent'};
const greatMaker = g.greatMaker?.stage === 'arrival'
? {event:g.greatMaker.event, signature:g.greatMaker.signature} : undefined;
g.pendingSneakWorm = {event, turn:g.turn, phase:g.phase, rider:rider.id, parent:wormParent!, stage:'parent',
...(greatMaker ? {greatMaker} : {})};
(g.pendingArrivalReaction ??= []).push({entrant:p.id,territory:entry.territory,
sector:entry.sector,amount,elite:0,cause:'shipment',resume:'none',turn:g.turn,
phase:g.phase,nativeSneakAdvisor:true,sneakWormEvent:event});