71 lines
2.6 KiB
TypeScript
71 lines
2.6 KiB
TypeScript
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);
|
||
}
|