Files
Flight-Ops/lib/game/catalog.ts
T
kamilkrzywda a801311fc5
CI / build + unit + e2e (pull_request) Failing after 5m28s
feat: v0.16.0 — ISS-005 weight-proportional fuel burn + refuel just enough for the loaded package
Part A: r(F) = k(We + rho*F) L/km, k calibrated so a full tank burns exactly
the old constant; closed forms in lib/sim/movement.ts (fuelAtWeighted,
requiredFuelFor, burnRateConstant, FUEL_DENSITY_KG_PER_L). Catalog gains
emptyWeightKg (ATR72 OEW 9300 kg), exposed in the snapshot. All three burn
call sites (engine landing, state live fuel, fly validation) moved to the
weighted model; old constant-rate fuelAt removed.

Part B: POST /api/planes/{planeId}/refuel takes an optional body
{mode:'full'|'forFlight', packageId} — no body keeps today's fill-to-full
behavior exactly (fuel lands bit-exact on maxFuel); forFlight adds just
enough for the loaded package's destination leg (409 not-loaded/expired/
doesn't-fit, 400 bad body). UI: per-cargo-row 'Refuel' button
(refuel-for-{pkg.id}), market button renamed 'Refuel to full'.

OpenAPI: RefuelRequest schema + optional requestBody + 400 on refuel, fly's
409 description updated, CatalogEntry.emptyWeightKg. Tests: new weighted-burn
suite, forFlight action/route cases (incl. ~7670 km UNNT<->LPPD doesn't-fit),
openapi guards; landing-fuel assertions in engine.test.ts + e2e/actions.spec.ts
use the closed form; the e2e 'return leg unfuelable' scenario reworked — a full
tank now covers the EDDF<->LEZL round trip, so the 409 moves to the second
outbound. New e2e: row Refuel deducts exactly L*price / raises fuel by exactly
L via the endpoint's own response snapshot.
2026-09-19 12:24:34 +00:00

77 lines
2.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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;
// Cargo slot capacity (v0.12.0): how many packages can be loaded on one plane.
capacity: number;
}
export const PLANE_TYPES: Record<string, PlaneType> = {
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,
capacity: 6,
},
};
// 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;
capacity: 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,
capacity: t.capacity,
}));
}