);
}
/** Real time a rest lasts: at least one contract board period (decision 0.5.46). */
export function restRealMs(): number {
const { restGameHours, timeCompression } = content.config;
return (restGameHours * MS_PER_HOUR) / timeCompression;
}
/** Duty used of the limit, or the rest countdown; and "Rest now" with a confirmation (CR-004). */
function Duty({ pilot, canRest }: { pilot: Pilot; canRest: boolean }) {
const rest = useGameAction("restPilot");
const [confirming, setConfirming] = useState(false);
if (pilot.resting && pilot.restEndsAt !== null) {
return (
Resting · wakes in
);
}
const used = Math.min(pilot.dutyHours, pilot.dutyLimit);
return (
Duty
{formatGameHours(pilot.dutyHours)} of {formatGameHours(pilot.dutyLimit)}
{pilot.name} rests for {formatDuration(restRealMs())} and can't fly until then.
Rest always lasts at least one board refresh, so they wake to new contracts.
)}
{rest.isError && (
{errorMessage(rest.error)}
)}
);
}
/** Assign an unassigned pilot to an idle, uncrewed ship at the same location. */
function AssignShip({ pilot }: { pilot: Pilot }) {
const state = useLoadedPlayer();
const assign = useGameAction("assignPilot");
const ships = state.ships.filter(
(s) => s.status === "idle" && s.pilotId === null && s.locationId === pilot.locationId,
);
const [shipId, setShipId] = useState("");
if (ships.length === 0) {
return (
No uncrewed ships at {locationName(pilot.locationId ?? "")}.