feat: migrate simulation to physically-based unit system
Replace arbitrary game units with real-world measurements throughout the simulation and viewer: planet orbits in AU, sizes in km, galaxy positions in light-years. Add SimulationUnits helpers for conversions, separate WarpSpeed from FtlSpeed for ships, fix FTL transit progress to use galaxy-space distances, overhaul Lagrange point placement with Hill sphere approximation, and update the viewer to scale and format all distances correctly. Ships in FTL transit now render in galaxy view. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,7 @@ import type {
|
||||
SelectionGroup,
|
||||
WorldState,
|
||||
} from "./viewerTypes";
|
||||
import { formatGalaxyDistance } from "./viewerMath";
|
||||
|
||||
export function describeSelectable(world: WorldState | undefined, item: Selectable): string {
|
||||
if (!world) {
|
||||
@@ -38,6 +39,83 @@ export function describeSelectable(world: WorldState | undefined, item: Selectab
|
||||
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") {
|
||||
return world.ships.get(item.id)?.label ?? item.id;
|
||||
}
|
||||
|
||||
if (item.kind === "station") {
|
||||
return world.stations.get(item.id)?.label ?? item.id;
|
||||
}
|
||||
|
||||
if (item.kind === "system") {
|
||||
return world.systems.get(item.id)?.label ?? item.id;
|
||||
}
|
||||
|
||||
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 === "node") {
|
||||
const node = world.nodes.get(item.id);
|
||||
if (!node) {
|
||||
return item.id;
|
||||
}
|
||||
|
||||
const anchorPath = node.anchorNodeId
|
||||
? describeSpatialNodePathWithinSystem(world, node.systemId, node.anchorNodeId)
|
||||
: undefined;
|
||||
return anchorPath ? `${anchorPath} / ${node.itemId}` : `${node.systemId} / ${node.itemId}`;
|
||||
}
|
||||
|
||||
if (item.kind === "spatial-node") {
|
||||
const node = world.spatialNodes.get(item.id);
|
||||
if (!node) {
|
||||
return item.id;
|
||||
}
|
||||
|
||||
if (node.kind === "star") {
|
||||
const system = world.systems.get(node.systemId);
|
||||
return system ? `${system.label} star` : `${node.systemId} star`;
|
||||
}
|
||||
|
||||
return describeSpatialNodePathWithinSystem(world, node.systemId, node.id) ?? `${node.systemId} / ${node.kind}`;
|
||||
}
|
||||
|
||||
if (item.kind === "bubble") {
|
||||
const bubble = world.localBubbles.get(item.id);
|
||||
const anchorPath = bubble?.nodeId
|
||||
? describeSpatialNodePathWithinSystem(world, bubble.systemId, bubble.nodeId)
|
||||
: undefined;
|
||||
return anchorPath ? `${anchorPath} bubble` : `Bubble ${item.id}`;
|
||||
}
|
||||
|
||||
if (item.kind === "claim") {
|
||||
const claim = world.claims.get(item.id);
|
||||
const anchorPath = claim?.nodeId
|
||||
? describeSpatialNodePathWithinSystem(world, claim.systemId, claim.nodeId)
|
||||
: undefined;
|
||||
return anchorPath ? `${anchorPath} claim` : `Claim ${item.id}`;
|
||||
}
|
||||
|
||||
if (item.kind === "construction-site") {
|
||||
const site = world.constructionSites.get(item.id);
|
||||
const anchorPath = site?.nodeId
|
||||
? describeSpatialNodePathWithinSystem(world, site.systemId, site.nodeId)
|
||||
: 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";
|
||||
@@ -209,7 +287,7 @@ export function renderSystemDetails(
|
||||
<p>Planets ${system.planets.length}<br>Moons ${moonCount}<br>Ships ${shipCount}<br>Stations ${stationCount}</p>
|
||||
<p>Spatial nodes ${spatialNodeCount}<br>Resource nodes ${nodeCount}<br>Bubbles ${bubbleCount}</p>
|
||||
<p>Claims ${claimCount}<br>Construction sites ${constructionCount}</p>
|
||||
<p>Height ${system.galaxyPosition.y.toFixed(0)}</p>
|
||||
<p>Height ${formatGalaxyDistance(system.galaxyPosition.y)}</p>
|
||||
<p>${system.planets.slice(0, 8).map((planet) => `${planet.label} (${planet.planetType})`).join("<br>")}</p>
|
||||
${followText}
|
||||
`;
|
||||
|
||||
Reference in New Issue
Block a user