import type { CargoType, GameConfig } from "@deadhead/content"; import type { RealTime } from "./clock"; import { economicDistance } from "./fuel"; export interface Contract { id: string; originId: string; destinationId: string; /** The cargo class (decision 0.5.6). */ cargoTypeId: string; /** The named good (CR-007, decision 0.5.54); its class is `cargoTypeId`. */ goodId: string; /** Flavor: the company shipping it, from the origin's list (decision 0.5.58). */ shipper: string; /** A Priority contract must arrive within its delivery window (decision 0.5.56). */ priority: boolean; /** Priority only: the longest trip allowed, in game hours; null otherwise. */ deliveryWindowGameHours: number | null; /** * 0 for Standard; 1–5 otherwise, and equal to the required cargo-skill level * (decision 0.5.6). High-Value and Dangerous take it from the good (0.5.55). */ tier: number; massTonnes: number; /** Credits, fixed at offer time (decision 0.5.11). */ payout: number; /** Cargo skill and level needed; null for Standard. */ requiredSkill: { skillId: string; level: number } | null; requiredLongHaulLevel: number; offeredAt: RealTime; /** End of the board period the contract was offered in. */ expiresAt: RealTime; firstContact: boolean; /** Set on a scripted tutorial contract (decision 0.5.33): its content id. */ scriptedId?: string; } export interface PayoutInput { cargoType: CargoType; /** The good's pay multiplier (CR-007). */ payMultiplier: number; tier: number; massTonnes: number; /** Route distance at offer time. */ distanceMillionKm: number; firstContact: boolean; priority: boolean; /** This contract's market rate around 1 (decision 0.5.60); 1 when absent. */ marketRate?: number; } /** * Payout in whole credits: class base pay per tonne per million km × the good's * pay multiplier × tierMultiplier^tier × mass × distance, × the first-contact * multiplier and the Priority bonus when flagged (CR-007), × its market rate (CR-008). */ export function contractPayout(input: PayoutInput, config: GameConfig): number { const { cargoType, payMultiplier, tier, massTonnes, distanceMillionKm, firstContact } = input; const pay = cargoType.basePayPerTonnePerMillionKm * payMultiplier * cargoType.tierPayMultiplier ** tier * massTonnes * // Long legs pay on a gentler curve than their length (decision 0.5.84). economicDistance(distanceMillionKm, config) * (firstContact ? config.firstContactPayoutMultiplier : 1) * (input.priority ? config.priorityPayoutMultiplier : 1) * (input.marketRate ?? 1); return Math.round(pay); }