Support Truthtrance No-Field shipment promises
Verify and publish checkpoint container / container (push) Canceled after 0s
Verify and publish checkpoint container / container (push) Canceled after 0s
This commit is contained in:
1 parent
9ab9b9a613
commit
7fccd62814
16 files changed
+658
-32
No files matched your search
+49
-18
@@ -506,11 +506,13 @@ import {
|
||||
parseShipmentClaim,
|
||||
validShipmentClaim,
|
||||
shipmentClaimDestinations,
|
||||
shipmentClaimHasNoField,
|
||||
liveShipmentPromises,
|
||||
shipmentPromiseModeSupported,
|
||||
shipmentPromiseTurnAvailable,
|
||||
type ShipmentClaim,
|
||||
type ShipmentPromise,
|
||||
type ShipmentReceipt,
|
||||
} from './shipment-promises';
|
||||
import { portableSnooperPlanBlock } from './portable-snooper';
|
||||
import {
|
||||
@@ -13095,7 +13097,7 @@ function guildBetrayalAuthority(g: Game, continuation: GuildBetrayalContinuation
|
||||
approvedSupplier = {donor:shipment.alliedNoField.owner,amount:quote.ownerPayment};
|
||||
}
|
||||
if (!approvedSupplier) checkShipmentIncomeFunding(g, p, shipment.cost, shipment.allyPayment);
|
||||
checkShipmentPromises(g, getPlayer(g, shipment.player), shipment);
|
||||
checkShipmentPromises(g, getPlayer(g, shipment.player), shipmentPromiseReceipt(g, shipment));
|
||||
shipper = shipment.player; price = shipment.cost;
|
||||
allyPayment = approvedSupplier?.amount ?? shipment.allyPayment;
|
||||
originalReceiver = guildShipmentIncome({guild: guild.id, shipper, ally: p.ally,
|
||||
@@ -33213,6 +33215,7 @@ function shipmentPromiseIntegrity(g: Game) {
|
||||
(p) => p.id === promise.asker && p.id !== promise.player,
|
||||
) &&
|
||||
validShipmentClaim(promise, {allowConstants: promise.mixed !== undefined}) &&
|
||||
(!shipmentClaimHasNoField(promise) || g.players.some(p => p.id === promise.player && p.faction === 'richese')) &&
|
||||
typeof promise.answer === 'boolean' &&
|
||||
(promise.released === undefined ||
|
||||
typeof promise.released === 'boolean') &&
|
||||
@@ -33436,6 +33439,8 @@ function findShipmentCompletion(
|
||||
p.reserves,
|
||||
p.tanks,
|
||||
p.elites,
|
||||
p.noField,
|
||||
p.noFieldEvent,
|
||||
g.homeworlds,
|
||||
p.hand.map((c) => c.id),
|
||||
g.aid,
|
||||
@@ -33443,6 +33448,22 @@ function findShipmentCompletion(
|
||||
]);
|
||||
if (visited.has(signature)) continue;
|
||||
visited.add(signature);
|
||||
if (p.faction === 'richese' && p.noField && !p.noField.deployed && p.noFieldEvent && !noFieldShipBlock(g, p)) {
|
||||
for (const destination of destinations) for (const sector of territory(destination).sectors) {
|
||||
const receipt: ShipmentReceipt = { territory: destination, sector, amount: 0, noField: true };
|
||||
if (!promises.every(promise => matchesShipment(promise, receipt) === promise.answer)) continue;
|
||||
for (const token of p.noField.tokens.filter(token => token.id !== p.noField!.lastShipped)) {
|
||||
const action: Action = { type: 'ship', noField: token.id, event: p.noFieldEvent,
|
||||
territory: destination, sector };
|
||||
try {
|
||||
applyActionInner(g, p.id, action, 'movementPreparation');
|
||||
return { early, actions: [...actions, action] };
|
||||
} catch (error) {
|
||||
if (!(error instanceof RuleError)) throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const destination of destinations) for (let amount = 1; amount <= p.reserves; amount++) {
|
||||
for (const sector of territory(destination).sectors) {
|
||||
if (!promises.every(p => matchesShipment(p, { territory: destination, amount, sector }) === p.answer)) continue;
|
||||
@@ -33530,6 +33551,8 @@ function findShipmentCompletion(
|
||||
// conversion effects. More reserves/lower cost cannot remove
|
||||
// a previously available choice of physical shipment count.
|
||||
const preparations: Action[] = [];
|
||||
if (p.faction === 'richese' && p.noField?.deployed && p.noFieldEvent)
|
||||
preparations.push({ type: 'revealNoField', token: p.noField.deployed.tokenId, event: p.noFieldEvent });
|
||||
if ((g.aid[p.id]?.amount ?? 0) > outgoingFloor)
|
||||
preparations.push({ type: 'pledgeAid', amount: outgoingFloor });
|
||||
const karama = p.hand.find((c) => canUseAsKaramaRole(g, p, c));
|
||||
@@ -33637,7 +33660,7 @@ function bindShipmentTruth(
|
||||
function checkShipmentPromises(
|
||||
g: Game,
|
||||
p: Player,
|
||||
shipment: { territory: string; amount: number; sector?: number } | null,
|
||||
shipment: ShipmentReceipt | null,
|
||||
) {
|
||||
requireRule(
|
||||
liveShipmentPromises(g.shipmentPromises ?? [], p.id, g.turn).every(
|
||||
@@ -33646,24 +33669,36 @@ function checkShipmentPromises(
|
||||
'Honor your Truthtrance shipment answer before using this shipment opportunity.',
|
||||
);
|
||||
if (g.phase === 5 && g.active === p.id) guardTurnActionShipmentPriceBoundary(g, p.id,
|
||||
liveTurnActionPromises(g.turnActionPromises ?? [], p.id, g.turn), undefined, shipment);
|
||||
liveTurnActionPromises(g.turnActionPromises ?? [], p.id, g.turn), undefined,
|
||||
shipment?.noField && shipment.amount === 0 ? null : shipment);
|
||||
}
|
||||
/** Preserve physical counts while explicitly recording an owned marker. */
|
||||
function shipmentPromiseReceipt(g: Game, shipment: PendingShipment): ShipmentReceipt | null {
|
||||
if (shipment.alliedNoField) return null;
|
||||
if (!shipment.noField) return { territory: shipment.territory, sector: shipment.sector, amount: shipment.amount };
|
||||
return { territory: shipment.territory, sector: shipment.sector, noField: true,
|
||||
amount: shipment.richesePair
|
||||
? currentRichesePairForces(g, getPlayer(g, shipment.player), shipment.noFieldReserves ?? 0, shipment.richesePair.revealedTokenId).physical
|
||||
: (shipment.noFieldReserves ?? 0) + (shipment.smugglerCompanion ? 1 : 0) };
|
||||
}
|
||||
function finishShipmentPromises(
|
||||
g: Game,
|
||||
p: Player,
|
||||
shipment: { territory: string; amount: number; sector?: number } | null,
|
||||
shipment: ShipmentReceipt | null,
|
||||
) {
|
||||
checkShipmentPromises(g, p, shipment);
|
||||
// Other promise families retain their original physical-force meaning.
|
||||
const physical = shipment?.noField && shipment.amount === 0 ? null : shipment;
|
||||
const probe = (g as TurnActionCardProbe)._turnActionShipmentReceipt;
|
||||
if (probe?.player === p.id && probe.event === undefined)
|
||||
probe.event = shipment ? { territory: shipment.territory, amount: shipment.amount,
|
||||
...(shipment.sector === undefined ? {} : { sector: shipment.sector }) } : null;
|
||||
probe.event = physical ? { territory: physical.territory, amount: physical.amount,
|
||||
...(physical.sector === undefined ? {} : { sector: physical.sector }) } : null;
|
||||
if (g.phase === 5 && g.turnActionPromises) g.turnActionPromises = turnActionRule(() =>
|
||||
recordTurnActionShipment(g.turnActionPromises!, p.id, g.turn, shipment ? { territory: shipment.territory, amount: shipment.amount,
|
||||
...(shipment.sector === undefined ? {} : { sector: shipment.sector }) } : null));
|
||||
recordTurnActionShipment(g.turnActionPromises!, p.id, g.turn, physical ? { territory: physical.territory, amount: physical.amount,
|
||||
...(physical.sector === undefined ? {} : { sector: physical.sector }) } : null));
|
||||
if (g.phase === 5 && g.travelPromises) g.travelPromises = travelRule(() => recordTravelShipment(g.travelPromises!, p.id, g.turn,
|
||||
shipment ? { territory: shipment.territory, amount: shipment.amount,
|
||||
...(shipment.sector === undefined ? {} : { sector: shipment.sector }) } : null));
|
||||
physical ? { territory: physical.territory, amount: physical.amount,
|
||||
...(physical.sector === undefined ? {} : { sector: physical.sector }) } : null));
|
||||
for (const promise of liveShipmentPromises(
|
||||
g.shipmentPromises ?? [],
|
||||
p.id,
|
||||
@@ -33711,7 +33746,7 @@ function reconcileShipmentPromises(
|
||||
voluntary.action.type === 'richeseBid' && typeof voluntary.action.amount === 'number' && voluntary.action.amount > 0 ||
|
||||
voluntary.action.type === 'ready' && [2, 4].includes(voluntary.phase ?? g.phase));
|
||||
const ownVoluntary = voluntary?.actor === p.id &&
|
||||
(['card', 'bribe', 'charity', 'alliance', 'pledgeAid', 'emperorGift', 'richeseGift'].includes(voluntary.action.type) ||
|
||||
(['card', 'bribe', 'charity', 'alliance', 'pledgeAid', 'emperorGift', 'richeseGift', 'revealNoField'].includes(voluntary.action.type) ||
|
||||
earlyVoluntary ||
|
||||
future && voluntary.action.type === 'decision');
|
||||
// A foreign pending parent may spend or refund its approved funding. Wait
|
||||
@@ -40508,11 +40543,7 @@ function commitShipment(g: Game, shipment: PendingShipment, settlement?: {recipi
|
||||
finishShipmentPromises(
|
||||
g,
|
||||
getPlayer(g, shipment.player),
|
||||
shipment.noField ? (shipment.richesePair
|
||||
? { territory: shipment.territory, sector: shipment.sector, amount: currentRichesePairForces(g, getPlayer(g, shipment.player), shipment.noFieldReserves ?? 0, shipment.richesePair.revealedTokenId).physical }
|
||||
: shipment.noFieldReserves || shipment.smugglerCompanion
|
||||
? { territory: shipment.territory, sector: shipment.sector, amount: (shipment.noFieldReserves ?? 0) + (shipment.smugglerCompanion ? 1 : 0) }
|
||||
: null) : shipment.alliedNoField ? null : shipment,
|
||||
shipmentPromiseReceipt(g, shipment),
|
||||
);
|
||||
const {
|
||||
territory: to,
|
||||
@@ -47352,8 +47383,8 @@ function applyActionInner(
|
||||
}),
|
||||
...(noField ? { noField } : {}),
|
||||
};
|
||||
if (noField) checkShipmentPromises(g, p, noFieldGroup!.physical
|
||||
? { territory: to, amount: noFieldGroup!.physical, sector: s } : null);
|
||||
if (noField) checkShipmentPromises(g, p,
|
||||
{ territory: to, amount: noFieldGroup!.physical, sector: s, noField: true });
|
||||
if (noField && (g.leaderSkills || noFieldReserves) && !richesePair) {
|
||||
shipment.noFieldSkillProof = noFieldSkillProof(shipment);
|
||||
validatePhysicalShipment(g, shipment);
|
||||
|
||||
Reference in new issue
Block a user