import type { FactionId } from './catalog'; /** Ordinary reserve shipment; other transport powers have separate prices. */ export function reserveShipmentCost( player: { faction: FactionId; halfRate: boolean }, territoryType: string, amount: number, ) { if (player.faction === 'fremen') return 0; const standard = amount * (territoryType === 'stronghold' ? 1 : 2); return player.halfRate ? Math.ceil(standard / 2) : standard; } /** Guild cross-planet transport and returns; Fremen southern reserves pay this * tariff too. Pass 'reserves' for a return, not a board territory type. */ export function guildShipmentCost( destinationType: string, amount: number, halfRate = true, ) { const normal = amount * (destinationType === 'reserves' || destinationType === 'stronghold' ? 1 : 2); return halfRate ? Math.ceil(normal / 2) : normal; } const ordinaryAlliedNoFieldPricing = Object.freeze({ stronghold: 1, other: 2, bankOnly: false, policy: null, }); const discountedAlliedNoFieldPricing = Object.freeze({ stronghold: 1, other: 1, bankOnly: true, policy: 'Provisional pricing: a Guild recipient or already-active Karama rate uses the rounded one-force Guild fee (1 spice), paid entirely to the bank. This price/payee precedence is not an adjudicated publisher ruling.', }); const canceledGuildAlliedNoFieldPricing = Object.freeze({ stronghold: 1, other: 2, bankOnly: true, policy: 'Provisional pricing: canceling a Guild recipient’s native rate charges the full one-force fee, still paid entirely to the bank. This price/payee precedence is not an adjudicated publisher ruling.', }); /** Shared first-version price/payee policy for Basic and Advanced. */ export function alliedNoFieldShipmentPricing( context: { purchasedGuildRate: boolean; nativeGuildRateCanceled: boolean; }, recipient: { faction: FactionId }, ) { if (context.purchasedGuildRate) return discountedAlliedNoFieldPricing; if (recipient.faction !== 'guild') return ordinaryAlliedNoFieldPricing; return context.nativeGuildRateCanceled ? canceledGuildAlliedNoFieldPricing : discountedAlliedNoFieldPricing; } /** A pledge is already escrowed. Selecting a share never spends a donor's * unpledged balance or enlarges the contribution they authorized. */ export function shipmentPaymentBounds( cost: number, ownSpice: number, pledgedSpice: number, ) { return { minimum: Math.max(0, cost - ownSpice), maximum: Math.min(cost, pledgedSpice), }; } /** November 2020 FAQ, Alliances: each contributor pays the Guild directly; * money contributed by the Guild itself goes to the bank. An independent * Karama shipment routes the entire payment to the bank. */ export function guildShipmentIncome(payment: { guild: string | undefined; shipper: string; ally: string | null; cost: number; allyPayment: number; bankOnly: boolean; }) { if (!payment.guild || payment.bankOnly) return 0; return ( (payment.shipper === payment.guild ? 0 : payment.cost - payment.allyPayment) + (payment.ally === payment.guild ? 0 : payment.allyPayment) ); }