Refactor simulation and viewer architecture
This commit is contained in:
216
apps/viewer/src/viewerSelection.ts
Normal file
216
apps/viewer/src/viewerSelection.ts
Normal file
@@ -0,0 +1,216 @@
|
||||
import type { SystemSnapshot } from "./contracts";
|
||||
import type {
|
||||
CameraMode,
|
||||
OrbitalAnchor,
|
||||
Selectable,
|
||||
SelectionGroup,
|
||||
WorldState,
|
||||
} from "./viewerTypes";
|
||||
|
||||
export function describeSelectable(world: WorldState | undefined, item: Selectable): string {
|
||||
if (!world) {
|
||||
return item.kind;
|
||||
}
|
||||
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 === "node") {
|
||||
return item.id;
|
||||
}
|
||||
if (item.kind === "spatial-node") {
|
||||
return `${world.spatialNodes.get(item.id)?.kind ?? "node"} ${item.id}`;
|
||||
}
|
||||
if (item.kind === "bubble") {
|
||||
return `bubble ${item.id}`;
|
||||
}
|
||||
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}`;
|
||||
}
|
||||
return world.systems.get(item.id)?.label ?? item.id;
|
||||
}
|
||||
|
||||
export function getSelectionGroup(item: Selectable): SelectionGroup {
|
||||
if (item.kind === "ship") {
|
||||
return "ships";
|
||||
}
|
||||
if (
|
||||
item.kind === "station"
|
||||
|| item.kind === "node"
|
||||
|| item.kind === "spatial-node"
|
||||
|| item.kind === "bubble"
|
||||
|| 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 === "spatial-node") {
|
||||
return world.spatialNodes.get(selection.id)?.systemId;
|
||||
}
|
||||
if (selection.kind === "bubble") {
|
||||
return world.localBubbles.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;
|
||||
}
|
||||
return selection.id;
|
||||
}
|
||||
|
||||
export function resolveFocusedBubbleId(world: WorldState | undefined, selectedItems: Selectable[]): string | undefined {
|
||||
if (!world || selectedItems.length !== 1) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const selected = selectedItems[0];
|
||||
if (selected.kind === "bubble") {
|
||||
return selected.id;
|
||||
}
|
||||
if (selected.kind === "ship") {
|
||||
return world.ships.get(selected.id)?.bubbleId ?? world.ships.get(selected.id)?.spatialState.currentBubbleId ?? undefined;
|
||||
}
|
||||
if (selected.kind === "station") {
|
||||
return world.stations.get(selected.id)?.bubbleId ?? undefined;
|
||||
}
|
||||
if (selected.kind === "spatial-node") {
|
||||
return world.spatialNodes.get(selected.id)?.bubbleId ?? undefined;
|
||||
}
|
||||
if (selected.kind === "claim") {
|
||||
return world.claims.get(selected.id)?.bubbleId ?? undefined;
|
||||
}
|
||||
if (selected.kind === "construction-site") {
|
||||
return world.constructionSites.get(selected.id)?.bubbleId ?? undefined;
|
||||
}
|
||||
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 spatialNodeCount = 0;
|
||||
let bubbleCount = 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 node of world.spatialNodes.values()) {
|
||||
if (node.systemId === system.id) {
|
||||
spatialNodeCount += 1;
|
||||
}
|
||||
}
|
||||
for (const bubble of world.localBubbles.values()) {
|
||||
if (bubble.systemId === system.id) {
|
||||
bubbleCount += 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.moonCount;
|
||||
}
|
||||
|
||||
const followText = activeContext && cameraMode === "follow" && cameraTargetShipId
|
||||
? `<p>Camera locked to ${world.ships.get(cameraTargetShipId)?.label ?? cameraTargetShipId}</p>`
|
||||
: "";
|
||||
|
||||
return `
|
||||
<p>${system.id}${activeContext ? " · active system" : ""}</p>
|
||||
<p>${system.starKind} · ${system.starCount} star${system.starCount > 1 ? "s" : ""}</p>
|
||||
<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>${system.planets.slice(0, 8).map((planet) => `${planet.label} (${planet.planetType})`).join("<br>")}</p>
|
||||
${followText}
|
||||
`;
|
||||
}
|
||||
Reference in New Issue
Block a user