import { content, type Location } from "@deadhead/content"; import { realToGame } from "@deadhead/sim"; import { useState, type ReactNode } from "react"; import { Link } from "react-router"; import { useClock } from "../clock/clock"; import { formatGameTime } from "../clock/GameClock"; import { Countdown } from "../components/Countdown"; import { cargoName, formatMillionKm, locationName } from "../format"; import { distanceAt } from "../game"; import { DEFAULT_CAMERA, useCamera } from "../map/camera"; import type { MapSelection } from "../map/hitTest"; import { MapCanvas } from "../map/MapCanvas"; import { modelsReachingFromEarth } from "../map/reach"; import { useLoadedPlayer } from "../state/actions"; const TYPE_LABELS: Record = { "atmospheric-world": "Atmospheric world", "airless-body": "Airless body", "cloud-platform": "Cloud platform", "belt-station": "Belt station", }; /** The solar system map (Phase 10). Tap a location or ship for details. */ export function MapScreen() { const state = useLoadedPlayer(); const [selection, setSelection] = useState(null); return (

Map

{selection?.kind === "location" && ( setSelection(null)} /> )} {selection?.kind === "flight" && ( setSelection(null)} /> )}
); } function Panel({ title, onClose, children, }: { title: string; onClose: () => void; children: ReactNode; }) { return (

{title}

{children}
); } /** A tapped location: details if discovered, and a link to its board if there is a bay. */ function LocationPanel({ locationId, onClose }: { locationId: string; onClose: () => void }) { const state = useLoadedPlayer(); const now = useClock((s) => s.now); const location = content.location(locationId)!; const discovered = state.locations.some((l) => l.id === locationId && l.discovered); const hasBay = state.bays.some((b) => b.locationId === locationId); const reachable = discovered ? [] : modelsReachingFromEarth(locationId, now); return ( {discovered ? ( <>

{TYPE_LABELS[location.type]} ·{" "} {location.id === "earth" ? "Home" : `${formatMillionKm(distanceAt("earth", location.id, now))} from Earth`}

Produces {location.produces.map(cargoName).join(", ") || "nothing"} · demands{" "} {location.demands.map(cargoName).join(", ") || "nothing"}

{hasBay ? ( View contract board ) : (

No bay here yet.

)} ) : ( <>

Undiscovered — deliver here to unlock.

{reachable.length > 0 ? `Reachable with: ${reachable.map((m) => m.name).join(", ")}` : "No ship can reach it from Earth right now."}

)}
); } /** A tapped ship in flight: its route, pilot and ETA. */ function FlightPanel({ dispatchId, onClose }: { dispatchId: string; onClose: () => void }) { const state = useLoadedPlayer(); const dispatch = state.activeDispatches.find((d) => d.id === dispatchId); if (!dispatch) return null; const shipName = dispatch.tutorial ? "Company loaner" : (state.ships.find((s) => s.id === dispatch.shipId)?.name ?? "Ship"); const pilotName = state.pilots.find((p) => p.id === dispatch.pilotId)?.name ?? "Unknown pilot"; return (

{locationName(dispatch.originId)} → {locationName(dispatch.destinationId)}

Pilot: {pilotName}

Arrives in ( {formatGameTime(realToGame(dispatch.arrivalAt, content.config))})

); }