import type { GameConfig } from "@deadhead/content"; import { MS_PER_HOUR, type RealTime } from "./clock"; import { LONG_HAUL_SKILL_ID, skillLevel, type SkillLevels } from "./pilot"; /** * Pilot duty and rest (CR-004, decisions 0.5.42–0.5.46). Rest is derived from a * stored rest-ends-at time, so nothing has to run when it ends. */ /** Game hours a pilot may fly before mandatory rest: base plus a Long Haul bonus. */ export function dutyLimit(skills: SkillLevels, config: GameConfig): number { const longHaul = skillLevel(skills, LONG_HAUL_SKILL_ID); return config.dutyLimitGameHours + longHaul * config.longHaulDutyBonusGameHours; } /** Whether a pilot is resting at real time `now`: until (not at) rest-ends-at. */ export function isResting(restEndsAt: RealTime | null, now: RealTime): boolean { return restEndsAt !== null && now < restEndsAt; } /** Game hours of a trip from its real departure and arrival times. */ export function tripGameHours( departureAt: RealTime, arrivalAt: RealTime, config: GameConfig, ): number { return ((arrivalAt - departureAt) * config.timeCompression) / MS_PER_HOUR; } /** Duty hours after a trip: tutorial runs add nothing (decision 0.5.42). */ export function dutyAfterTrip(dutyHours: number, tripHours: number, tutorial: boolean): number { return tutorial ? dutyHours : dutyHours + tripHours; } /** Whether duty at or over the limit forces rest on arrival (decision 0.5.44). */ export function needsRest(dutyHours: number, limit: number): boolean { return dutyHours >= limit; } /** When a rest starting at real time `start` ends (decision 0.5.45). */ export function restEndsAt(start: RealTime, config: GameConfig): RealTime { return start + (config.restGameHours * MS_PER_HOUR) / config.timeCompression; }