Support Sapho ordering for shared arrivals
Verify and publish checkpoint container / container (push) Canceled after 0s

This commit is contained in:
Quintine committed 2026-10-29 05:29:51 +11:00
1 parent b18a767470
commit e457478f7d
18 files changed
+808 -23

No files matched your search

+20 -2
View File
@@ -20,6 +20,22 @@ export const actSemutaArrivalOverlap = actBasicSemutaAmbassador;
export const player = basicSemutaAmbassadorPlayer;
export const inventory = basicSemutaAmbassadorInventory;
/** Complete only the original public ordering opportunity. Owners pass even
* when neither holds Sapho; no card custody or arrival outcome is staged. */
export function passArrivalOrder(source: Game): Game {
let game = source;
for (let count = 0; count < 2; count++) {
const overlap = game.pendingArrivalOverlap, order = overlap?.saphoOrder;
if (!order || game.pendingAmbassador || game.pendingTerrorEntry || game.pendingTreacheryDiscard ||
order.frame.passed.length === order.frame.holders.length) break;
const actor = order.frame.holders.find(id => !order.frame.passed.includes(id));
assert.ok(actor);
game = actSemutaArrivalOverlap(game, actor, { type: 'arrivalOrderPass', event: order.frame.event,
revision: order.frame.revision });
}
return game;
}
/** Quiet native choices after the inherited turn-one prefix; token placement,
* arrival and both reactions remain explicit caller actions. */
export function nextSemutaArrivalOverlapStep(game: Game): BasicSemutaAmbassadorStep | null {
@@ -160,6 +176,7 @@ export function claimOverlapSemuta(source: Game, actor: string, card: string): G
export function openOverlapAmbassadorGrant(source: Game, f: SemutaArrivalOverlapFixture): Game {
let game = source;
if (!game.pendingArrivalOverlap) game = actSemutaArrivalOverlap(game, f.arrivalStep.actor, f.arrivalStep.action);
game = passArrivalOrder(game);
assert.ok(game.pendingArrivalOverlap);
if (game.pendingTerrorEntry?.stage === 'offer')
game = actSemutaArrivalOverlap(game, f.moritani, { type: 'decision', decline: true });
@@ -171,8 +188,9 @@ export function openOverlapAmbassadorGrant(source: Game, f: SemutaArrivalOverlap
assert.equal(game.pendingAmbassador?.stage, 'cards'); return game;
}
export function openOverlapTerrorDiscard(source: Game, f: SemutaArrivalOverlapFixture): Game {
assert.equal(source.pendingTerrorEntry?.stage, 'offer');
let game = actSemutaArrivalOverlap(source, f.moritani, { type: 'decision', reveal: true });
let game = passArrivalOrder(source);
assert.equal(game.pendingTerrorEntry?.stage, 'offer');
game = actSemutaArrivalOverlap(game, f.moritani, { type: 'decision', reveal: true });
if (f.terror === 'robbery') {
assert.equal(game.pendingTerrorEntry?.stage, 'robbery');
game = actSemutaArrivalOverlap(game, f.moritani, { type: 'decision', choice: 'card' });