feat: v0.10.0 — right Jobs & Market pane (contracts accept/dispatch, market buy/sell/refuel with live previews); catalog + economy constants exposed in /api/state; shared applySnapshot for instant action feedback

This commit is contained in:
2026-09-17 19:18:42 +00:00
parent 03653fc50e
commit cf811dcf4d
15 changed files with 894 additions and 14 deletions
+20
View File
@@ -18,3 +18,23 @@ export function formatSimClock(clockMs: number): string {
const pad = (n: number) => String(n).padStart(2, '0');
return `${d.getUTCFullYear()}-${pad(d.getUTCMonth() + 1)}-${pad(d.getUTCDate())} ${pad(d.getUTCHours())}:${pad(d.getUTCMinutes())}:${pad(d.getUTCSeconds())} UTC`;
}
// Compact game-time duration rounded to the minute: "2d 3h" / "14h 30m" / "45m".
export function formatDuration(ms: number): string {
const totalMin = Math.max(0, Math.round(ms / 60_000));
const d = Math.floor(totalMin / (24 * 60));
const h = Math.floor((totalMin % (24 * 60)) / 60);
const m = totalMin % 60;
if (d > 0) return `${d}d ${h}h`;
if (h > 0) return `${h}h ${m}m`;
return `${m}m`;
}
// Contract deadline relative to the sim clock: "in 14h 30m"; a passed deadline reads
// "overdue" and a sub-minute one "now". Pure — unit-testable, no game logic.
export function formatDeadlineRelative(deadlineMs: number, nowMs: number): string {
const diff = deadlineMs - nowMs;
if (diff <= 0) return 'overdue';
if (diff < 60_000) return 'now'; // sub-minute — "in 1m" starts at a full minute
return `in ${formatDuration(diff)}`;
}