// Aircraft catalog — in-code constants for now (no DB table yet). One type is // enough for the MVP; add more entries to PLANE_TYPES as they are designed. export interface PlaneType { id: string; name: string; // Fuel capacity, liters. maxFuel: number; // Fuel burn per km of flight at a FULL tank, liters/km (cruise) — the weighted burn // model (v0.16.0) calibrates its rate constant to this value at F = maxFuel. burnPerKm: number; // Operating empty weight in kg (v0.16.0) — feeds the weight-proportional burn model. emptyWeightKg: number; // Cruise speed, km/h — used by the sim tick to advance planes along routes. speedKmh: number; // Market price in € (v0.8.0): buying costs `price`, selling pays out // SELL_FRACTION × price. price: number; // Max payload in kg (v0.27.0, ISS-045 — replaces the v0.12.0 slot count): total // package weight that can be loaded on one plane. ATR 72-600-class figure, tuned // so every cargo type at max count × 1.3 jitter still fits alone (see UNIT_WEIGHT_KG). maxPayloadKg: number; } export const PLANE_TYPES: Record = { atr72: { id: 'atr72', name: 'ATR 72-600', maxFuel: 6300, burnPerKm: 1.75, // full-tank rate: ~893 L/h ≈ 714 kg/h at 510 km/h cruise (Jet-A1 ≈ 0.8 kg/L); the weighted model burns less as fuel drops off emptyWeightKg: 9_300, // ATR 72-600 operating empty weight speedKmh: 510, // €200k — with the starting cash of €20k and typical hop rewards (~€1–3k), // a second ATR takes sustained play to afford, as intended for the MVP sink. price: 200_000, // ponytail: balance-tunable payload (kg) — every cargo type at max count × 1.3 jitter // must stay below this so any single package fits alone; typical loads carry ~3–6 packages. maxPayloadKg: 4_800, }, // Progression ladder above the ATR starter (v0.28.0, ISS-047): bigger tanks, faster airframes, // higher prices — everything downstream is data-driven via PLANE_TYPES[model]. Values are // balance-tunable; keep maxPayloadKg ≥ every cargo type at max count × 1.3 jitter so any // single package still fits alone (same rule as the ATR above). crj900: { id: 'crj900', name: 'CRJ-900', maxFuel: 8_000, burnPerKm: 2.1, emptyWeightKg: 17_500, speedKmh: 830, price: 450_000, maxPayloadKg: 6_500, }, e175: { id: 'e175', name: 'Embraer E175', maxFuel: 9_500, burnPerKm: 2.0, emptyWeightKg: 23_000, speedKmh: 840, price: 900_000, maxPayloadKg: 9_000, }, a320: { id: 'a320', name: 'Airbus A320-200', maxFuel: 12_000, burnPerKm: 2.2, emptyWeightKg: 41_000, speedKmh: 845, price: 1_800_000, maxPayloadKg: 18_000, }, a330: { id: 'a330', name: 'Airbus A330-200', maxFuel: 20_000, burnPerKm: 2.16, emptyWeightKg: 95_000, speedKmh: 910, price: 4_500_000, maxPayloadKg: 48_000, }, b777: { id: 'b777', name: 'Boeing 777-300ER', maxFuel: 30_000, burnPerKm: 2.5, emptyWeightKg: 130_000, speedKmh: 920, price: 9_000_000, maxPayloadKg: 80_000, }, }; // The plane the player starts with. export const STARTER_MODEL = 'atr72'; // Jet-A1 fuel price in €/liter (v0.8.0): refueling cost = (maxFuel − fuel) × this. export const FUEL_PRICE_PER_L = 0.8; // Fraction of the catalog price paid out when selling a plane (v0.8.0). export const SELL_FRACTION = 0.5; // Snapshot shape of one catalog entry in GET /api/state (v0.10.0): `model` is the // id used by buy/dispatch, `label` the display name — clients compute all market // previews (buy price, sell payout, refuel cost) from these + the economy constants. export interface CatalogEntry { model: string; label: string; price: number; maxFuel: number; burnPerKm: number; // Weighted burn model inputs (v0.16.0) — clients can mirror the closed forms in // lib/sim/movement.ts with these + FUEL_DENSITY_KG_PER_L. emptyWeightKg: number; speedKmh: number; // Max payload in kg (v0.27.0) — total package weight that can be loaded on one plane. maxPayloadKg: number; } // The catalog as a flat array, one entry per plane type — the single source of // truth for both the sim (PLANE_TYPES) and the client snapshot. export function catalogEntries(): CatalogEntry[] { return Object.values(PLANE_TYPES).map((t) => ({ model: t.id, label: t.name, price: t.price, maxFuel: t.maxFuel, burnPerKm: t.burnPerKm, emptyWeightKg: t.emptyWeightKg, speedKmh: t.speedKmh, maxPayloadKg: t.maxPayloadKg, })); }