deadhead-space-freight-tycoon/packages/sim/src/fuel.ts

98 lines
3.5 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.

import type { GameConfig, Location, ShipModel } from "@deadhead/content";
/**
* The distance pay and fuel are charged on (decision 0.5.84): the route distance
* scaled down for long legs, `reference × (distance / reference)^exponent`.
* A leg of the reference length (Earth–Moon) is unchanged; an exponent of 1
* makes it the plain distance. Travel time and XP use the plain distance.
*/
export function economicDistance(distanceMillionKm: number, config: GameConfig): number {
const { referenceMillionKm, exponent } = config.economicDistance;
return referenceMillionKm * (distanceMillionKm / referenceMillionKm) ** exponent;
}
/** The plain distance whose economic distance is `economic` (the inverse of the curve). */
export function plainDistance(economic: number, config: GameConfig): number {
const { referenceMillionKm, exponent } = config.economicDistance;
return referenceMillionKm * (economic / referenceMillionKm) ** (1 / exponent);
}
/**
* Fuel a leg burns, in fu (decision 0.5.95): burn rate × the economic distance
* of the leg shortened by the origin's range factor, so launches from
* low-gravity bodies burn less.
*/
export function legFuel(
ship: ShipModel,
origin: Location,
distanceMillionKm: number,
config: GameConfig,
): number {
return (
ship.burnFuPerMillionKm * economicDistance(distanceMillionKm / origin.launchRangeFactor, config)
);
}
/**
* How far a full tank flies from an origin, in million km (decisions 0.5.78,
* 0.5.95): the plain distance whose leg fuel fills the tank.
*/
export function fullTankReach(ship: ShipModel, origin: Location, config: GameConfig): number {
return (
plainDistance(ship.tankCapacityFu / ship.burnFuPerMillionKm, config) * origin.launchRangeFactor
);
}
/** A location's fuel price in credits per fu (decision 0.5.96). */
export function fuelPriceAt(location: Location, config: GameConfig): number {
return config.baseFuelPricePerFu * location.fuelPrice;
}
/** What a ship's tank holds (decision 0.5.94): fuel, and its average purchase price. */
export interface Tank {
fuelFu: number;
/** Credits per fu, weighted by how much was bought at each price (decision 0.5.98). */
avgPrice: number;
}
export interface Refuel {
tank: Tank;
boughtFu: number;
/** Whole credits. */
cost: number;
}
/**
* Adds up to `amountFu` at `price`, never past `capacityFu` (decision 0.5.97),
* and keeps the tank's weighted average price.
*/
export function refuel(tank: Tank, capacityFu: number, amountFu: number, price: number): Refuel {
const boughtFu = Math.max(0, Math.min(amountFu, capacityFu - tank.fuelFu));
const fuelFu = tank.fuelFu + boughtFu;
const avgPrice =
fuelFu > 0 ? (tank.fuelFu * tank.avgPrice + boughtFu * price) / fuelFu : tank.avgPrice;
return {
tank: { fuelFu, avgPrice },
boughtFu,
cost: Math.min(Math.round(boughtFu * price), Math.ceil(boughtFu * price)),
};
}
/** The most fuel up to a full tank that `credits` can pay for (Auto top-up, decision 0.5.97). */
export function affordableRefuel(
tank: Tank,
capacityFu: number,
price: number,
credits: number,
): Refuel {
const affordable = Math.max(0, credits) / price;
const result = refuel(tank, capacityFu, affordable, price);
// Rounding never charges more than the player has.
return { ...result, cost: Math.min(result.cost, Math.max(0, Math.floor(credits))) };
}
/** What a leg's fuel cost the player (decision 0.5.98): fu burned × the tank's average price. */
export function legFuelCost(burnedFu: number, tank: Tank): number {
return Math.round(burnedFu * tank.avgPrice);
}