Support Sapho ordering at Testing Station
Verify and publish checkpoint container / container (push) Successful in 10m32s
Verify and publish checkpoint container / container (push) Successful in 10m32s
This commit is contained in:
1 parent
220d78c779
commit
1972622c11
11 files changed
+445
-9
No files matched your search
+28
-4
@@ -144,7 +144,7 @@ import { ECAZ_START_FORCES, quoteEcazStartingForces } from './ecaz-setup';
|
||||
import { battleCardSlotEligible, battleCategoryInspectionValue, fixedBattleInspectionMatches, validBattleSlotPair, usesNativeBattleSpecial } from './battle-card-slots';
|
||||
import { HARASS_WITHDRAW_CARD, HarassWithdrawError, isHarassWithdraw, quoteHarassWithdraw, defaultHarassWithdrawAllocation, harassWithdrawNeedsAllocation, type HarassWithdrawSelection, type HarassWithdrawContext, type HarassWithdrawPreview } from './harass-withdraw';
|
||||
import { discoveryChoices, discoveryStashSignature, type DiscoveryStash } from './discovery-actions';
|
||||
import { createStormSource, validateStormSource, discoveryStormOffer, createDiscoveryStorm, chooseDiscoveryStorm, validateDiscoveryStorm, finishDiscoveryStorm, discoveryStormDistance, type StormMovementSource, type DiscoveryStorm } from './discovery-storm';
|
||||
import { createStormSource, validateStormSource, discoveryStormOffer, createDiscoveryStorm, chooseDiscoveryStorm, validateDiscoveryStorm, finishDiscoveryStorm, discoveryStormDistance, discoveryStormSaphoModes, reorderDiscoveryStorm, type StormMovementSource, type DiscoveryStorm } from './discovery-storm';
|
||||
import { completeDiscoveryFlight, discoveryFlightOffer, quoteDiscoveryFlight, validateDiscoveryFlight, type DiscoveryFlightReceipt } from './discovery-flight';
|
||||
import { advanceDiscoveryEntry, beginDiscoveryEntryArrival, createDiscoveryEntryRound, finishDiscoveryEntryArrival, quoteDiscoveryEntry, validateDiscoveryEntryArrivalChild, validateDiscoveryEntryRound, type DiscoveryEntryArrivalChild, type DiscoveryEntryRound, type DiscoveryEntrySelection } from './discovery-entry';
|
||||
import { createDiscoveryState, validateDiscoveryState, placeDiscovery, rememberDiscoveryFace, revealDiscoveryToken, isDiscoverySpiceCardId, isDiscoveryTokenId, DISCOVERY_SPICE_CARDS, DISCOVERY_CARD_PLACEMENTS, DISCOVERY_TOKEN_BY_ID, JACURUTU_SIETCH, type DiscoveryState } from './discoveries';
|
||||
@@ -29071,7 +29071,7 @@ function offerMoritaniDuke(g: Game) {
|
||||
return true;
|
||||
}
|
||||
type SaphoOption = {
|
||||
scope: 'onceAround' | 'cyclicAuction' | 'movement' | 'battleOrder' | 'extortion' | 'revival' | 'charity';
|
||||
scope: 'onceAround' | 'cyclicAuction' | 'movement' | 'battleOrder' | 'extortion' | 'revival' | 'charity' | 'testingStation';
|
||||
event: string;
|
||||
mode: 'first' | 'last';
|
||||
/** Display only; action submission keeps scope/event/mode. */
|
||||
@@ -29158,6 +29158,13 @@ function saphoExtortionDecision(g: Game) {
|
||||
decision?.kind === 'moritaniExtortion' && decision.player === state.queue[state.cursor] &&
|
||||
decision.event === extortionEvent(state);
|
||||
}
|
||||
function saphoTestingStationDecision(g: Game) {
|
||||
const station = g.ecologicalStorm, decision = g.decision;
|
||||
return g.phase === 0 && !g.pendingTreacheryDiscard && !g.semutaPreview &&
|
||||
!g.semutaAdvancedOrdinaryPreview && !g.semutaRecruitsPreview &&
|
||||
station?.stage === 'choose' && decision?.kind === 'ecologicalStorm' &&
|
||||
decision.player === station.owner && decision.event === station.event;
|
||||
}
|
||||
function saphoBattleCardReserved(g: Game, player: string, card: string): boolean {
|
||||
const battle = g.battle, plan = battle?.plans[player];
|
||||
return !!battle && (!!plan && [plan.weapon, plan.defense, plan.leader].includes(card) ||
|
||||
@@ -29312,7 +29319,7 @@ function saphoCleanWindow(g: Game) {
|
||||
!g.truthtrance &&
|
||||
!g.phaseOpening &&
|
||||
!g.response &&
|
||||
(!g.decision || saphoExtortionDecision(g)) &&
|
||||
(!g.decision || saphoExtortionDecision(g) || saphoTestingStationDecision(g)) &&
|
||||
!g.pendingNullentropy &&
|
||||
!g.pendingRicheseGift &&
|
||||
!g.pendingExchange &&
|
||||
@@ -29408,6 +29415,12 @@ function saphoOptions(g: Game, p: Player): SaphoOption[] {
|
||||
)
|
||||
return [];
|
||||
const options: SaphoOption[] = [];
|
||||
if (saphoTestingStationDecision(g)) {
|
||||
const frame = g.ecologicalStorm!;
|
||||
for (const mode of nexusRule(() => discoveryStormSaphoModes(g, frame, p.id)))
|
||||
options.push({scope: 'testingStation', event: frame.event, mode});
|
||||
return options;
|
||||
}
|
||||
if (g.phase===2 && g.advanced && g.charityOrder) {
|
||||
const context=charityOrderContext(g);
|
||||
const event=charityOrderRule(()=>saphoCharityOrderEvent(context,g.charityOrder!));
|
||||
@@ -29514,7 +29527,18 @@ function playSapho(g: Game, p: Player, action: Action) {
|
||||
'This Juice of Sapho opportunity is unavailable, already completed or expired.',
|
||||
);
|
||||
recordNativePrintedCardPlay(g, p.id, 'juiceOfSapho');
|
||||
if (option.scope === 'battleAggressor') {
|
||||
if (option.scope === 'testingStation') {
|
||||
requireRule(saphoTestingStationDecision(g) && g.ecologicalStorm,
|
||||
'The original Testing Station choice is no longer open.');
|
||||
const frame = nexusRule(() => reorderDiscoveryStorm(g, g.ecologicalStorm!, {
|
||||
holder: p.id, event: option.event, mode: option.mode, nextEvent: crypto.randomUUID(),
|
||||
}));
|
||||
discard(g, p, 'richese-juice-of-sapho');
|
||||
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.`,
|
||||
{faction:p.faction,name:'Juice of Sapho'});
|
||||
} else if (option.scope === 'battleAggressor') {
|
||||
const b = g.battle!;
|
||||
const next = saphoAggressorRule(() => changeSaphoAggressor(b.saphoAggressor,
|
||||
b.saphoAggressorEvents, b, g.turn, p.id, option.event));
|
||||
|
||||
Reference in new issue
Block a user