Protect Sapho priority at the Testing Station
Verify and publish checkpoint container / container (push) Successful in 11m16s
Verify and publish checkpoint container / container (push) Successful in 11m16s
This commit is contained in:
1 parent
f73a6e0a9c
commit
a6be17fc5d
12 files changed
+391
-9
No files matched your search
+44
-1
@@ -1674,6 +1674,8 @@ export type Game = {
|
||||
} | null;
|
||||
stormMovementSource?: StormMovementSource;
|
||||
ecologicalStorm?: DiscoveryStorm;
|
||||
/** Neutral opportunity before either of two Station occupants chooses. */
|
||||
saphoStationPriority?: { event: string; passed: string[] };
|
||||
discoveryEnabled?: boolean;
|
||||
discoveries?: DiscoveryState;
|
||||
discoveryStash?: DiscoveryStash;
|
||||
@@ -14119,6 +14121,9 @@ function startStormMovement(g: Game, n: number) {
|
||||
if (offer && !offer.blocked) {
|
||||
g.ecologicalStorm = nexusRule(() => createDiscoveryStorm(g,crypto.randomUUID()));
|
||||
g.decision = {kind:'ecologicalStorm',player:g.ecologicalStorm.owner,event:g.ecologicalStorm.event};
|
||||
if (g.ecologicalStorm.queue?.order.length === 2 && byFaction(g, 'richese') &&
|
||||
!g.semutaAdvancedOrdinaryPreview && !g.semutaRecruitsPreview)
|
||||
g.saphoStationPriority = {event:g.ecologicalStorm.event, passed:[]};
|
||||
} else moveStorm(g,n);
|
||||
}
|
||||
function choamStormOptions(g: Game) {
|
||||
@@ -14653,6 +14658,16 @@ function discoveryIntegrity(g: Game) {
|
||||
}
|
||||
function ecologicalStormIntegrity(g: Game) {
|
||||
const frame = g.ecologicalStorm, continuation = g.pendingTreacheryDiscard?.continuation;
|
||||
if (g.saphoStationPriority) {
|
||||
const priority = g.saphoStationPriority;
|
||||
requireRule(frame?.stage === 'choose' && frame.event === priority.event &&
|
||||
frame.queue?.order.length === 2 && frame.queue.choices.length === 0 &&
|
||||
g.decision?.kind === 'ecologicalStorm' && g.decision.event === priority.event &&
|
||||
Array.isArray(priority.passed) && priority.passed.length < 2 &&
|
||||
new Set(priority.passed).size === priority.passed.length &&
|
||||
priority.passed.every(id => frame.queue!.order.includes(id)),
|
||||
'The Testing Station order opportunity has lost its original occupants.');
|
||||
}
|
||||
requireRule(!(g.status === 'playing' && g.phase === 0 && g.advanced && g.turn > 1 &&
|
||||
g.stormDialers.length === 0 && g.stormPending !== null) || !!g.stormMovementSource,
|
||||
'The revealed Advanced storm has lost its original movement source.');
|
||||
@@ -29849,6 +29864,8 @@ function saphoOptions(g: Game, p: Player): SaphoOption[] {
|
||||
const options: SaphoOption[] = [];
|
||||
if (saphoTestingStationDecision(g)) {
|
||||
const frame = g.ecologicalStorm!;
|
||||
if (g.saphoStationPriority &&
|
||||
(!frame.queue?.order.includes(p.id) || g.saphoStationPriority.passed.includes(p.id))) return options;
|
||||
for (const mode of nexusRule(() => discoveryStormSaphoModes(g, frame, p.id)))
|
||||
options.push({scope: 'testingStation', event: frame.event, mode});
|
||||
return options;
|
||||
@@ -29966,6 +29983,7 @@ function playSapho(g: Game, p: Player, action: Action) {
|
||||
holder: p.id, event: option.event, mode: option.mode, nextEvent: crypto.randomUUID(),
|
||||
}));
|
||||
const used = discard(g, p, 'richese-juice-of-sapho');
|
||||
delete g.saphoStationPriority;
|
||||
g.ecologicalStorm = frame;
|
||||
g.decision = {kind:'ecologicalStorm', player:frame.owner, event:frame.event};
|
||||
log(g, `${p.name} discarded Juice of Sapho to take the Testing Station choice ${option.mode}. Both occupants retain one adjustment and the original storm movement remains.`,
|
||||
@@ -43504,6 +43522,27 @@ function applyActionWithInitiator(state: Game, id: string, action: Action, initi
|
||||
decideSaphoArrivalOrder(game, getPlayer(game, id), action);
|
||||
return game.pendingTreacheryDiscard?.reaction ? game : normalizeAutomaticGame(game);
|
||||
}
|
||||
if (state.saphoStationPriority) {
|
||||
const priority = state.saphoStationPriority, station = state.ecologicalStorm;
|
||||
ecologicalStormIntegrity(state);
|
||||
getPlayer(state, id);
|
||||
if (action?.type === 'advanceBots') return state;
|
||||
requireRule(station?.stage === 'choose' && station.event === priority.event &&
|
||||
station.queue?.order.includes(id) && !priority.passed.includes(id),
|
||||
'This Testing Station priority is not available to you.');
|
||||
if (action.type === 'saphoStationPass') {
|
||||
requireRule(Object.keys(action).sort().join(',') === 'event,type' && action.event === priority.event,
|
||||
'Continue only the current Testing Station order opportunity.');
|
||||
const game = structuredClone(state);
|
||||
game.saphoStationPriority!.passed.push(id);
|
||||
if (game.saphoStationPriority!.passed.length === station.queue!.order.length)
|
||||
delete game.saphoStationPriority;
|
||||
return game;
|
||||
}
|
||||
requireRule(action.type === 'card' && action.card === 'richese-juice-of-sapho' &&
|
||||
action.scope === 'testingStation' && action.event === priority.event,
|
||||
'Finish the Testing Station priority before choosing storm movement.');
|
||||
}
|
||||
if (action.alliancePromiseEvent !== undefined) {
|
||||
const guide = projectedAlliancePromiseGuide(state, id);
|
||||
requireRule(guide && Object.keys(action).sort().join(',') === Object.keys(guide.action).sort().join(',') &&
|
||||
@@ -49391,7 +49430,11 @@ export function viewGame(state: Game, id: string, now?: number) {
|
||||
? {event:g.battle.jacurutuDial.event,owner:g.battle.jacurutuDial.owner,winner:g.battle.jacurutuDial.winner,
|
||||
choices:structuredClone(g.battle.jacurutuDial.choices)} : null,
|
||||
ecologicalStorm: g.decision?.kind === 'ecologicalStorm' && g.decision.player === id && g.ecologicalStorm?.stage === 'choose'
|
||||
? {...nexusRule(() => discoveryStormOffer(g,g.ecologicalStorm))!,event:g.ecologicalStorm.event} : null,
|
||||
? {...nexusRule(() => discoveryStormOffer(g,g.ecologicalStorm))!,event:g.ecologicalStorm.event,
|
||||
...(g.saphoStationPriority ? {blocked:'Finish the Testing Station order opportunity first.'} : {})} : null,
|
||||
saphoStationPriority: g.saphoStationPriority ? {event:g.saphoStationPriority.event,
|
||||
canPass:!!g.ecologicalStorm?.queue?.order.includes(id) && !g.saphoStationPriority.passed.includes(id),
|
||||
waiting:g.saphoStationPriority.passed.includes(id) || !g.ecologicalStorm?.queue?.order.includes(id)} : null,
|
||||
discoveryEntry: g.discoveryEntry?.stage === 'choose' && g.decision?.kind === 'discoveryEntry' && g.decision.player === id
|
||||
? {...nexusRule(() => quoteDiscoveryEntry({...g,discoveries:g.discoveries!},g.discoveryEntry!,id)),event:g.decision.event} : null,
|
||||
greatMaker: g.greatMaker ? { event:g.greatMaker.event, turn:g.greatMaker.turn, stage:g.greatMaker.stage, votes:structuredClone(g.greatMaker.votes), order:[...g.greatMaker.order], ride:greatMakerRideOptions(g,id) } : null,
|
||||
|
||||
Reference in new issue
Block a user