Compose the deferred Terror arrival and green the mixed E3 programmes
Queue a committed Terror entry whose arrival also opened its own
interaction — Guild income, a Bene Gesserit fighter Intrusion or advisor
choice, or Fremen storm protection — in the additive
Game.pendingArrivalReaction list, and open the oldest queued entry from
settleAutomaticContinuations once nothing is pending. Payment, force
transfer and the original movement never replay; a Terror entry already in
flight and every Ambassador combination still reject.
Both withdrawn mixed E3 programme pairs are green: Moritani 9/9 and Ecaz
14/14, stable across 20 consecutive full-file runs. Their recorded resume
constraints became real fixture repairs, and the two restored consumer
omissions were corrected against the engine rather than re-pinned.
Repair pre-existing failures inherited at 45e6084 against current engine
behavior: stale Discovery and module gates in the native skills, Ix and
expansion-faction prototypes, the advanced Moritani skill boundary, the
Richese Nexus betrayal sale-credit expectation, one incidental gate
message and one flaky Collection-readiness assertion that reproduced at
HEAD.
Offline suite 7,793/7,793 with typecheck and lint clean. No game, seat,
saved-state, deployment, public-start or played-save conversion changes.
This commit is contained in:
1 parent
45e6084424
commit
e05a205a47
30 files changed
+2467
-117
No files matched your search
@@ -1691,6 +1691,19 @@ export type Game = {
|
||||
resume: 'none' | 'wormRide' | 'ambassador';
|
||||
ambassadorEvent?: string;
|
||||
} | null;
|
||||
/** Committed entries whose optional reactions must wait for the arrival's own
|
||||
* pending interaction (payment, advisor choice, storm protection). */
|
||||
pendingArrivalReaction?: {
|
||||
entrant: string;
|
||||
territory: string;
|
||||
sector: number;
|
||||
amount: number;
|
||||
elite: number;
|
||||
cause: NonNullable<Game['pendingTerrorEntry']>['cause'];
|
||||
resume: 'none' | 'wormRide';
|
||||
turn: number;
|
||||
phase: number;
|
||||
}[] | null;
|
||||
/** One committed entry with two independently optional expansion reactions. */
|
||||
pendingArrivalOverlap?: {
|
||||
event: string;
|
||||
@@ -14826,6 +14839,16 @@ function openTerritoryEntry(
|
||||
cause: NonNullable<Game['pendingTerrorEntry']>['cause'],
|
||||
resume: 'none' | 'wormRide' = 'none',
|
||||
) {
|
||||
// The arrival's own interaction (guild payment, advisor choice, storm
|
||||
// protection) settles first; the entry then opens from the automatic pass.
|
||||
if (g.response || g.decision || g.pendingTerrorEntry || g.pendingAmbassador ||
|
||||
g.pendingArrivalOverlap) {
|
||||
(g.pendingArrivalReaction ??= []).push({
|
||||
entrant: entrant.id, territory: to, sector, amount, elite, cause, resume,
|
||||
turn: g.turn, phase: g.phase,
|
||||
});
|
||||
return true;
|
||||
}
|
||||
const owner = byFaction(g, 'ecaz');
|
||||
const token = g.ecazAmbassadors?.tokens.find(
|
||||
(t) => t.zone === 'placed' && t.location === to,
|
||||
@@ -27456,6 +27479,16 @@ function settleAutomaticContinuations(g: Game) {
|
||||
prepareTupileCleanup(g);
|
||||
if (g.pendingHomeworldTupileCleanup || tupileSourceBlock(g)) return;
|
||||
if (g.truthtrance || g.phaseOpening || g.status === 'finished') return;
|
||||
if (g.pendingArrivalReaction?.length && !g.response && !g.decision &&
|
||||
!g.pendingTerrorEntry && !g.pendingAmbassador && !g.pendingArrivalOverlap) {
|
||||
const deferred = g.pendingArrivalReaction.shift()!;
|
||||
if (!g.pendingArrivalReaction.length) g.pendingArrivalReaction = null;
|
||||
const entrant = g.players.find(p => p.id === deferred.entrant);
|
||||
requireRule(!!entrant, 'The deferred arrival entry has lost its entrant.');
|
||||
openTerritoryEntry(g, entrant!, deferred.territory, deferred.sector,
|
||||
deferred.amount, deferred.elite, deferred.cause, deferred.resume);
|
||||
continue;
|
||||
}
|
||||
const response = g.response;
|
||||
const before = JSON.stringify(g);
|
||||
const bonus = g.pendingHomeworldOccupiedBonus;
|
||||
|
||||
Reference in new issue
Block a user