623 lines
19 KiB
TypeScript
623 lines
19 KiB
TypeScript
import type { CelestialSnapshot, ShipSnapshot, SystemSnapshot } from "./contracts";
|
||
import type {
|
||
CameraMode,
|
||
OrbitalAnchor,
|
||
Selectable,
|
||
SelectionGroup,
|
||
WorldState,
|
||
} from "./viewerTypes";
|
||
import { formatGalaxyDistance } from "./viewerMath";
|
||
import { getShipBehaviorLabel, getShipOrderLabel } from "./shipAutomationPresentation";
|
||
|
||
export function describeSelectable(world: WorldState | undefined, item: Selectable): string {
|
||
if (!world) {
|
||
return item.kind;
|
||
}
|
||
if (item.kind === "ship") {
|
||
return world.ships.get(item.id)?.name ?? item.id;
|
||
}
|
||
if (item.kind === "station") {
|
||
return world.stations.get(item.id)?.label ?? item.id;
|
||
}
|
||
if (item.kind === "node") {
|
||
const node = world.nodes.get(item.id);
|
||
return node ? `${node.itemId} source` : item.id;
|
||
}
|
||
if (item.kind === "celestial") {
|
||
const celestial = world.celestials.get(item.id);
|
||
if (!celestial) {
|
||
return item.id;
|
||
}
|
||
|
||
return describeCelestialPathWithinSystem(world, celestial.systemId, celestial.id)
|
||
?? `${world.systems.get(celestial.systemId)?.label ?? celestial.systemId} / ${celestial.kind}`;
|
||
}
|
||
if (item.kind === "claim") {
|
||
return `claim ${item.id}`;
|
||
}
|
||
if (item.kind === "construction-site") {
|
||
return `construction ${item.id}`;
|
||
}
|
||
if (item.kind === "planet") {
|
||
return world.systems.get(item.systemId)?.planets[item.planetIndex]?.label ?? `${item.systemId}:${item.planetIndex}`;
|
||
}
|
||
if (item.kind === "moon") {
|
||
const planet = world.systems.get(item.systemId)?.planets[item.planetIndex];
|
||
return planet?.moons[item.moonIndex]?.label ?? `moon ${item.moonIndex + 1}`;
|
||
}
|
||
return world.systems.get(item.id)?.label ?? item.id;
|
||
}
|
||
|
||
export function describeHoverLabel(world: WorldState | undefined, item: Selectable): string | undefined {
|
||
if (!world) {
|
||
return undefined;
|
||
}
|
||
|
||
if (item.kind === "ship") {
|
||
const ship = world.ships.get(item.id);
|
||
if (!ship) {
|
||
return item.id;
|
||
}
|
||
|
||
const lines = [
|
||
ship.name,
|
||
`Behavior ${describeShipBehavior(ship)}`,
|
||
`State ${describeShipState(world, ship)}`,
|
||
`Order ${describeShipOrder(ship)}`,
|
||
];
|
||
return lines.join("\n");
|
||
}
|
||
|
||
if (item.kind === "station") {
|
||
return world.stations.get(item.id)?.label ?? item.id;
|
||
}
|
||
|
||
if (item.kind === "system") {
|
||
const system = world.systems.get(item.id);
|
||
if (!system) {
|
||
return item.id;
|
||
}
|
||
const starLabel = system.stars.length > 1 ? `${system.stars.length}× ${system.stars[0]?.kind}` : (system.stars[0]?.kind ?? "unknown");
|
||
const planetCount = system.planets.length;
|
||
const shipCount = [...world.ships.values()].filter((s) => s.systemId === item.id).length;
|
||
const stationCount = [...world.stations.values()].filter((s) => s.systemId === item.id).length;
|
||
const lines = [
|
||
system.label,
|
||
`${starLabel} · ${planetCount} planet${planetCount !== 1 ? "s" : ""}`,
|
||
];
|
||
const parts: string[] = [];
|
||
if (shipCount > 0) {
|
||
parts.push(`${shipCount} ship${shipCount !== 1 ? "s" : ""}`);
|
||
}
|
||
if (stationCount > 0) {
|
||
parts.push(`${stationCount} station${stationCount !== 1 ? "s" : ""}`);
|
||
}
|
||
if (parts.length > 0) {
|
||
lines.push(parts.join(" · "));
|
||
}
|
||
return lines.join("\n");
|
||
}
|
||
|
||
if (item.kind === "planet") {
|
||
const system = world.systems.get(item.systemId);
|
||
const planet = system?.planets[item.planetIndex];
|
||
return planet ? `${system?.label ?? item.systemId} / ${planet.label}` : `${item.systemId} / planet ${item.planetIndex + 1}`;
|
||
}
|
||
|
||
if (item.kind === "moon") {
|
||
const system = world.systems.get(item.systemId);
|
||
const planet = system?.planets[item.planetIndex];
|
||
const moon = planet?.moons[item.moonIndex];
|
||
if (moon) {
|
||
return `${system?.label ?? item.systemId} / ${planet?.label ?? `planet ${item.planetIndex + 1}`} / ${moon.label}`;
|
||
}
|
||
return `${item.systemId} / planet ${item.planetIndex + 1} / moon ${item.moonIndex + 1}`;
|
||
}
|
||
|
||
if (item.kind === "node") {
|
||
const node = world.nodes.get(item.id);
|
||
if (!node) {
|
||
return item.id;
|
||
}
|
||
|
||
const anchorPath = describeAnchorPathWithinSystem(world, node.systemId, node.anchorId);
|
||
return anchorPath ? `${anchorPath} / ${node.itemId}` : `${node.systemId} / ${node.itemId}`;
|
||
}
|
||
|
||
if (item.kind === "celestial") {
|
||
const celestial = world.celestials.get(item.id);
|
||
if (!celestial) {
|
||
return item.id;
|
||
}
|
||
|
||
if (celestial.kind === "star") {
|
||
const system = world.systems.get(celestial.systemId);
|
||
return system ? `${system.label} star` : `${celestial.systemId} star`;
|
||
}
|
||
|
||
return describeCelestialPathWithinSystem(world, celestial.systemId, celestial.id) ?? `${celestial.systemId} / ${celestial.kind}`;
|
||
}
|
||
|
||
if (item.kind === "claim") {
|
||
const claim = world.claims.get(item.id);
|
||
const anchorPath = claim
|
||
? describeAnchorPathWithinSystem(world, claim.systemId, claim.anchorId)
|
||
: undefined;
|
||
return anchorPath ? `${anchorPath} claim` : `Claim ${item.id}`;
|
||
}
|
||
|
||
if (item.kind === "construction-site") {
|
||
const site = world.constructionSites.get(item.id);
|
||
const anchorPath = site
|
||
? describeAnchorPathWithinSystem(world, site.systemId, site.anchorId)
|
||
: undefined;
|
||
const siteLabel = site ? (site.blueprintId ?? site.targetDefinitionId) : item.id;
|
||
return anchorPath ? `${anchorPath} / ${siteLabel}` : `Construction ${siteLabel}`;
|
||
}
|
||
|
||
return describeSelectable(world, item);
|
||
}
|
||
|
||
export function getSelectionGroup(item: Selectable): SelectionGroup {
|
||
if (item.kind === "ship") {
|
||
return "ships";
|
||
}
|
||
if (
|
||
item.kind === "station"
|
||
|| item.kind === "node"
|
||
|| item.kind === "claim"
|
||
|| item.kind === "construction-site"
|
||
) {
|
||
return "structures";
|
||
}
|
||
return "celestials";
|
||
}
|
||
|
||
export function resolveSelectableSystemId(world: WorldState | undefined, selection: Selectable): string | undefined {
|
||
if (!world) {
|
||
return undefined;
|
||
}
|
||
|
||
if (selection.kind === "ship") {
|
||
return world.ships.get(selection.id)?.systemId;
|
||
}
|
||
if (selection.kind === "station") {
|
||
return world.stations.get(selection.id)?.systemId;
|
||
}
|
||
if (selection.kind === "node") {
|
||
return world.nodes.get(selection.id)?.systemId;
|
||
}
|
||
if (selection.kind === "celestial") {
|
||
return world.celestials.get(selection.id)?.systemId;
|
||
}
|
||
if (selection.kind === "claim") {
|
||
return world.claims.get(selection.id)?.systemId;
|
||
}
|
||
if (selection.kind === "construction-site") {
|
||
return world.constructionSites.get(selection.id)?.systemId;
|
||
}
|
||
if (selection.kind === "planet") {
|
||
return selection.systemId;
|
||
}
|
||
if (selection.kind === "moon") {
|
||
return selection.systemId;
|
||
}
|
||
return selection.id;
|
||
}
|
||
|
||
export function resolveFocusedCelestialId(world: WorldState | undefined, selectedItems: Selectable[]): string | undefined {
|
||
if (!world || selectedItems.length !== 1) {
|
||
return undefined;
|
||
}
|
||
|
||
const selected = selectedItems[0];
|
||
if (selected.kind === "celestial") {
|
||
return selected.id;
|
||
}
|
||
if (selected.kind === "ship") {
|
||
const ship = world.ships.get(selected.id);
|
||
return ship?.spatialState.currentAnchorId && world.celestials.has(ship.spatialState.currentAnchorId)
|
||
? ship.spatialState.currentAnchorId
|
||
: (ship?.anchorId && world.celestials.has(ship.anchorId) ? ship.anchorId : undefined);
|
||
}
|
||
if (selected.kind === "station") {
|
||
const station = world.stations.get(selected.id);
|
||
return station?.anchorId && world.celestials.has(station.anchorId) ? station.anchorId : undefined;
|
||
}
|
||
if (selected.kind === "claim") {
|
||
const claim = world.claims.get(selected.id);
|
||
return claim && world.celestials.has(claim.anchorId) ? claim.anchorId : undefined;
|
||
}
|
||
if (selected.kind === "construction-site") {
|
||
const site = world.constructionSites.get(selected.id);
|
||
return site && world.celestials.has(site.anchorId) ? site.anchorId : undefined;
|
||
}
|
||
return undefined;
|
||
}
|
||
|
||
export function resolveFocusedAnchorId(world: WorldState | undefined, selectedItems: Selectable[]): string | undefined {
|
||
if (!world || selectedItems.length !== 1) {
|
||
return undefined;
|
||
}
|
||
|
||
const selected = selectedItems[0];
|
||
if (selected.kind === "node") {
|
||
return world.nodes.get(selected.id)?.anchorId;
|
||
}
|
||
if (selected.kind === "ship") {
|
||
const ship = world.ships.get(selected.id);
|
||
return ship?.spatialState.currentAnchorId
|
||
?? ship?.anchorId
|
||
?? resolveFocusedCelestialId(world, selectedItems);
|
||
}
|
||
if (selected.kind === "station") {
|
||
const station = world.stations.get(selected.id);
|
||
return station?.anchorId
|
||
?? resolveFocusedCelestialId(world, selectedItems);
|
||
}
|
||
if (selected.kind === "claim") {
|
||
const claim = world.claims.get(selected.id);
|
||
return claim?.anchorId
|
||
?? resolveFocusedCelestialId(world, selectedItems);
|
||
}
|
||
if (selected.kind === "construction-site") {
|
||
const site = world.constructionSites.get(selected.id);
|
||
return site?.anchorId
|
||
?? resolveFocusedCelestialId(world, selectedItems);
|
||
}
|
||
if (selected.kind === "celestial") {
|
||
if (world.anchors.has(selected.id)) {
|
||
return selected.id;
|
||
}
|
||
|
||
const orbitBackedAnchor = [...world.anchors.values()].find((anchor) => anchor.orbitReferenceId === selected.id);
|
||
return orbitBackedAnchor?.id;
|
||
}
|
||
if (selected.kind === "planet") {
|
||
return `node-${selected.systemId}-planet-${selected.planetIndex + 1}`;
|
||
}
|
||
if (selected.kind === "moon") {
|
||
return `node-${selected.systemId}-planet-${selected.planetIndex + 1}-moon-${selected.moonIndex + 1}`;
|
||
}
|
||
|
||
return undefined;
|
||
}
|
||
|
||
export function describeOrbitalParent(world: WorldState | undefined, systemId?: string, anchor?: OrbitalAnchor): string {
|
||
if (!world || !systemId) {
|
||
return "unknown";
|
||
}
|
||
|
||
const system = world.systems.get(systemId);
|
||
if (!system) {
|
||
return systemId;
|
||
}
|
||
|
||
if (!anchor || anchor.kind === "star") {
|
||
return `${system.label} star`;
|
||
}
|
||
|
||
const planet = system.planets[anchor.planetIndex];
|
||
if (!planet) {
|
||
return `${system.label} star`;
|
||
}
|
||
|
||
if (anchor.kind === "planet") {
|
||
return planet.label;
|
||
}
|
||
|
||
return `${planet.label} moon ${anchor.moonIndex + 1}`;
|
||
}
|
||
|
||
export function renderSystemDetails(
|
||
world: WorldState | undefined,
|
||
system: SystemSnapshot,
|
||
activeContext: boolean,
|
||
cameraMode: CameraMode,
|
||
cameraTargetShipId?: string,
|
||
): string {
|
||
if (!world) {
|
||
return "";
|
||
}
|
||
|
||
let shipCount = 0;
|
||
let stationCount = 0;
|
||
let nodeCount = 0;
|
||
let celestialCount = 0;
|
||
let claimCount = 0;
|
||
let constructionCount = 0;
|
||
let moonCount = 0;
|
||
|
||
for (const ship of world.ships.values()) {
|
||
if (ship.systemId === system.id) {
|
||
shipCount += 1;
|
||
}
|
||
}
|
||
for (const station of world.stations.values()) {
|
||
if (station.systemId === system.id) {
|
||
stationCount += 1;
|
||
}
|
||
}
|
||
for (const node of world.nodes.values()) {
|
||
if (node.systemId === system.id) {
|
||
nodeCount += 1;
|
||
}
|
||
}
|
||
for (const celestial of world.celestials.values()) {
|
||
if (celestial.systemId === system.id) {
|
||
celestialCount += 1;
|
||
}
|
||
}
|
||
for (const claim of world.claims.values()) {
|
||
if (claim.systemId === system.id) {
|
||
claimCount += 1;
|
||
}
|
||
}
|
||
for (const site of world.constructionSites.values()) {
|
||
if (site.systemId === system.id) {
|
||
constructionCount += 1;
|
||
}
|
||
}
|
||
for (const planet of system.planets) {
|
||
moonCount += planet.moons.length;
|
||
}
|
||
|
||
const followText = activeContext && cameraMode === "follow" && cameraTargetShipId
|
||
? `<p>Camera locked to ${world.ships.get(cameraTargetShipId)?.name ?? cameraTargetShipId}</p>`
|
||
: "";
|
||
|
||
return `
|
||
<p>${system.id}${activeContext ? " · active system" : ""}</p>
|
||
<p>${system.stars[0]?.kind ?? "unknown"} · ${system.stars.length} star${system.stars.length > 1 ? "s" : ""}</p>
|
||
<p>Planets ${system.planets.length}<br>Moons ${moonCount}<br>Ships ${shipCount}<br>Stations ${stationCount}</p>
|
||
<p>Celestials ${celestialCount}<br>Resource nodes ${nodeCount}</p>
|
||
<p>Claims ${claimCount}<br>Construction sites ${constructionCount}</p>
|
||
<p>Height ${formatGalaxyDistance(system.galaxyPosition.y)}</p>
|
||
<p>${system.planets.slice(0, 8).map((planet) => `${planet.label} (${planet.planetType})`).join("<br>")}</p>
|
||
${followText}
|
||
`;
|
||
}
|
||
|
||
export function describeShipState(world: WorldState | undefined, ship: ShipSnapshot): string {
|
||
const baseState = ship.state;
|
||
const currentSubTask = ship.activeSubTasks[0];
|
||
if (baseState === "capacitor-starved") {
|
||
return currentSubTask ? `${baseState} while ${titleTask(currentSubTask.kind)}` : baseState;
|
||
}
|
||
|
||
if (!world || (baseState !== "ftl" && baseState !== "spooling-ftl" && baseState !== "warping" && baseState !== "spooling-warp")) {
|
||
return baseState;
|
||
}
|
||
|
||
const destinationAnchorId = ship.spatialState.destinationAnchorId ?? ship.spatialState.transit?.destinationAnchorId;
|
||
if (!destinationAnchorId) {
|
||
return baseState;
|
||
}
|
||
|
||
const destinationAnchor = destinationAnchorId ? world.anchors.get(destinationAnchorId) : undefined;
|
||
if (baseState === "warping" || baseState === "spooling-warp") {
|
||
const destinationSystemId = destinationAnchor?.systemId ?? ship.spatialState.currentSystemId ?? ship.systemId;
|
||
const destinationPath = describeAnchorPathWithinSystem(
|
||
world,
|
||
destinationSystemId,
|
||
destinationAnchorId,
|
||
);
|
||
return `${baseState} -> ${destinationPath ?? destinationAnchorId}`;
|
||
}
|
||
|
||
const destinationSystemId = destinationAnchor?.systemId
|
||
?? ship.spatialState.currentSystemId
|
||
?? ship.systemId;
|
||
const destinationSystem = world.systems.get(destinationSystemId);
|
||
if (!destinationSystem) {
|
||
return `${baseState} -> ${destinationAnchorId}`;
|
||
}
|
||
|
||
return `${baseState} -> ${destinationSystem.label}`;
|
||
}
|
||
|
||
export function describeShipObjective(objective: string): string {
|
||
return objective.replace(/[-_]+/g, " ");
|
||
}
|
||
|
||
export function describeShipBehavior(ship: ShipSnapshot): string {
|
||
const parts = [getShipBehaviorLabel(ship.defaultBehavior.kind)];
|
||
if (ship.assignment?.kind) {
|
||
parts.push(ship.assignment.kind);
|
||
}
|
||
return parts.join(" · ");
|
||
}
|
||
|
||
export function describeShipOrder(ship: ShipSnapshot): string {
|
||
const activeOrder = ship.orderQueue.find((order) => order.status === "queued" || order.status === "active");
|
||
if (activeOrder) {
|
||
return activeOrder.label ?? getShipOrderLabel(activeOrder.kind);
|
||
}
|
||
|
||
if (ship.assignment?.kind) {
|
||
return describeShipObjective(ship.assignment.kind);
|
||
}
|
||
|
||
return getShipBehaviorLabel(ship.defaultBehavior.kind);
|
||
}
|
||
|
||
export function describeShipCurrentAction(ship: ShipSnapshot): { label: string; progress: number } | undefined {
|
||
const subTask = ship.activeSubTasks[0];
|
||
if (!subTask) {
|
||
return undefined;
|
||
}
|
||
|
||
return {
|
||
label: subTask.summary || titleTask(subTask.kind),
|
||
progress: Math.max(0, Math.min(subTask.progress, 1)),
|
||
};
|
||
}
|
||
|
||
function titleTask(value: string): string {
|
||
return value.replace(/[-_]+/g, " ");
|
||
}
|
||
|
||
export function describeShipLocation(world: WorldState | undefined, ship: ShipSnapshot): { system: string; local?: string } {
|
||
const systemId = ship.spatialState.currentSystemId || ship.systemId;
|
||
const system = world?.systems.get(systemId);
|
||
const systemLabel = system?.label ?? systemId;
|
||
if (!world || !system) {
|
||
return { system: systemLabel };
|
||
}
|
||
|
||
if (ship.dockedStationId) {
|
||
const station = world.stations.get(ship.dockedStationId);
|
||
if (station) {
|
||
const anchorPath = station.anchorId
|
||
? describeAnchorPathWithinSystem(world, station.systemId, station.anchorId)
|
||
: undefined;
|
||
return {
|
||
system: systemLabel,
|
||
local: anchorPath ? `${anchorPath}/${station.label}` : station.label,
|
||
};
|
||
}
|
||
}
|
||
|
||
const currentAnchorId = ship.spatialState.currentAnchorId ?? ship.anchorId;
|
||
if (currentAnchorId) {
|
||
const anchorPath = describeAnchorPathWithinSystem(world, systemId, currentAnchorId);
|
||
if (anchorPath) {
|
||
return { system: systemLabel, local: anchorPath };
|
||
}
|
||
}
|
||
|
||
return { system: systemLabel };
|
||
}
|
||
|
||
export function describeActiveSpace(
|
||
world: WorldState | undefined,
|
||
povLevel: "local" | "system" | "galaxy",
|
||
activeSystemId: string | undefined,
|
||
selectedItems: Selectable[],
|
||
): string {
|
||
if (!world || povLevel === "galaxy") {
|
||
return "deep-space";
|
||
}
|
||
|
||
const activeSystem = activeSystemId ? world.systems.get(activeSystemId) : undefined;
|
||
if (!activeSystem) {
|
||
return "deep-space";
|
||
}
|
||
|
||
if (povLevel !== "local") {
|
||
return activeSystem.label;
|
||
}
|
||
|
||
const anchorId = resolveFocusedAnchorId(world, selectedItems);
|
||
if (anchorId) {
|
||
const localPath = describeAnchorPathWithinSystem(world, activeSystem.id, anchorId);
|
||
return localPath
|
||
? `${activeSystem.label} / ${localPath}`
|
||
: activeSystem.label;
|
||
}
|
||
|
||
const selected = selectedItems.length === 1 ? selectedItems[0] : undefined;
|
||
if (selected?.kind === "planet" && selected.systemId === activeSystem.id) {
|
||
const planet = activeSystem.planets[selected.planetIndex];
|
||
return planet
|
||
? `${activeSystem.label} / ${planet.label}`
|
||
: activeSystem.label;
|
||
}
|
||
|
||
return activeSystem.label;
|
||
}
|
||
|
||
export function describeCelestialPathWithinSystem(world: WorldState, systemId: string, celestialId: string): string | undefined {
|
||
const celestial = world.celestials.get(celestialId);
|
||
const system = world.systems.get(systemId);
|
||
if (!celestial || !system) {
|
||
return undefined;
|
||
}
|
||
|
||
const anchorId = resolveAnchorIdForCelestial(world, celestialId);
|
||
if (anchorId) {
|
||
return describeAnchorPathWithinSystem(world, systemId, anchorId);
|
||
}
|
||
|
||
if (celestial.kind === "star") {
|
||
return undefined;
|
||
}
|
||
|
||
return describeCelestialSegment(system, celestial);
|
||
}
|
||
|
||
export function describeAnchorPathWithinSystem(world: WorldState, systemId: string, anchorId: string, celestialId?: string | null): string | undefined {
|
||
const anchor = world.anchors.get(anchorId);
|
||
if (anchor?.parentAnchorId) {
|
||
const parentPath = describeAnchorPathWithinSystem(world, systemId, anchor.parentAnchorId);
|
||
const segment = describeAnchorSegment(anchor);
|
||
return parentPath ? `${parentPath}/${segment}` : segment;
|
||
}
|
||
|
||
if (celestialId) {
|
||
return describeCelestialPathWithinSystem(world, systemId, celestialId);
|
||
}
|
||
|
||
if (!anchor) {
|
||
return undefined;
|
||
}
|
||
|
||
return describeAnchorSegment(anchor);
|
||
}
|
||
|
||
function describeAnchorSegment(anchor: { kind: string; id: string; orbitReferenceId?: string | null }): string {
|
||
if (anchor.orbitReferenceId) {
|
||
return describeAnchorOrbitReference(anchor.orbitReferenceId);
|
||
}
|
||
|
||
if (anchor.kind === "resource-node") {
|
||
return anchor.id;
|
||
}
|
||
|
||
return anchor.kind.replace(/-/g, " ");
|
||
}
|
||
|
||
function resolveAnchorIdForCelestial(world: WorldState, celestialId: string): string | undefined {
|
||
return world.anchors.has(celestialId) ? celestialId : undefined;
|
||
}
|
||
|
||
function describeAnchorOrbitReference(referenceId: string): string {
|
||
const lagrangeMatch = referenceId.match(/(l[1-5])$/i);
|
||
if (lagrangeMatch) {
|
||
return lagrangeMatch[1].toUpperCase();
|
||
}
|
||
|
||
const moonMatch = referenceId.match(/moon-(\d+)$/i);
|
||
if (moonMatch) {
|
||
return `Moon ${moonMatch[1]}`;
|
||
}
|
||
|
||
const planetMatch = referenceId.match(/planet-(\d+)$/i);
|
||
if (planetMatch) {
|
||
return `Planet ${planetMatch[1]}`;
|
||
}
|
||
|
||
return referenceId;
|
||
}
|
||
|
||
function describeCelestialSegment(system: SystemSnapshot, celestial: CelestialSnapshot): string {
|
||
const moonMatch = celestial.id.match(/-planet-(\d+)-moon-(\d+)$/);
|
||
if (moonMatch) {
|
||
const moonIndex = Number.parseInt(moonMatch[2], 10);
|
||
return `Moon ${moonIndex}`;
|
||
}
|
||
|
||
const lagrangeMatch = celestial.id.match(/-planet-\d+-(l[1-5])$/);
|
||
if (lagrangeMatch) {
|
||
return lagrangeMatch[1].toUpperCase();
|
||
}
|
||
|
||
const planetMatch = celestial.id.match(/-planet-(\d+)$/);
|
||
if (planetMatch) {
|
||
const planetIndex = Number.parseInt(planetMatch[1], 10) - 1;
|
||
return system.planets[planetIndex]?.label ?? `Planet ${planetMatch[1]}`;
|
||
}
|
||
|
||
return celestial.orbitReferenceId ?? celestial.kind;
|
||
}
|