Support classic Advanced movement Truthtrance promises

This commit is contained in:
Quintine committed 2026-10-25 18:38:02 +11:00
1 parent 0d8481e821
commit 2ae2b984c0
15 files changed
+634 -23

No files matched your search

+26 -2
View File
@@ -16635,6 +16635,14 @@ function groundMovementEvent(move: MovementOrder) {
function projectGroundMovement(source: Game, move: MovementOrder): Game {
const game = structuredClone(source), player = getPlayer(game, move.player);
commitGroundMovementPieces(game, move);
// Witness the allowed owned arrival without opening another real response.
// The live declaration retains its matching/conversion counters. Settlement
// also removes emptied advisor groups and preserves an existing alone hold.
if (move.wantsFighters) {
if (player.advisors) delete player.advisors[move.to];
game.response = null;
}
settleAdvisors(game);
const flight = game.ornithopter;
if (flight?.player === player.id) {
flight.completed++;
@@ -16725,6 +16733,18 @@ function groundMovementCompletionAtMovement(game: Game, actor: string, promises:
}
return;
}
if (state.advanced && player.faction === 'beneGesserit') {
for (const territory of quoteAdvisorPreparationWindow(state, owner).territories) {
const action: Action = { type: 'prepareAdvisors', territory };
try {
const prepared = applyActionInner(state, owner, action, 'movementPreparation');
// Assume this owned advantage is allowed in the witness; the live
// action still opens its real independently counterable response.
if (prepared.response?.advisorPreparationSource) finishResponse(prepared, false);
yield { game: groundMovementProbe(prepared, owner), actions: [action] };
} catch (error) { if (!(error instanceof RuleError)) throw error; }
}
}
yield* movementShipmentPreparations(state, owner, (source, actor, action) => {
const payer = getPlayer(source, actor), share = Number(action.allyPayment);
const cost = reserveShipmentCost({ faction: payer.faction, halfRate: shipmentHalfRate(source, payer) },
@@ -16860,9 +16880,13 @@ function reconcileGroundMovementPromises(g: Game, actor?: string, action?: Actio
const live = liveMovementPromises(g.movementPromises ?? [], p.id, g.turn);
if (!live.some(promise => promise.answer)) continue;
if (g.pendingFremenMove?.order.player === p.id || g.pendingIxMove?.player === p.id ||
g.pendingChoamMove?.player === p.id) continue;
g.pendingChoamMove?.player === p.id ||
g.pendingAdvisorMatching?.intent.kind === 'movement' && g.pendingAdvisorMatching.intent.order.player === p.id ||
g.response?.kind === 'advisorFlip' && g.response.owner === p.id ||
g.pendingAutomaticAdvisorFlips?.some(source => source.owner === p.id ||
source.producer === 'groundMovement' && source.mover === p.id)) continue;
const early = g.phase < 5 || sourcePhase < 5;
const voluntary = actor === p.id && (!early || !!action &&
const voluntary = actor === p.id && action?.type !== 'passResponse' && (!early || !!action &&
(['card', 'bribe', 'pledgeAid', 'emperorGift', 'bid', 'richeseGift', 'revive', 'reviveLeader', 'reviveKwisatz', 'emperorRevival', 'decision'].includes(action.type) ||
action.type === 'richeseBid' && typeof action.amount === 'number' && action.amount > 0 ||
action.type === 'ready' && [2, 4].includes(sourcePhase)));