Expand galaxy generation and viewer rendering
This commit is contained in:
@@ -2,6 +2,7 @@ import * as THREE from "three";
|
||||
import { fetchWorldSnapshot, openWorldStream } from "./api";
|
||||
import type {
|
||||
FactionSnapshot,
|
||||
PlanetSnapshot,
|
||||
ResourceNodeDelta,
|
||||
ResourceNodeSnapshot,
|
||||
ShipDelta,
|
||||
@@ -36,6 +37,19 @@ interface ShipVisual {
|
||||
blendDurationMs: number;
|
||||
}
|
||||
|
||||
interface PlanetVisual {
|
||||
planet: PlanetSnapshot;
|
||||
mesh: THREE.Mesh;
|
||||
icon: THREE.Sprite;
|
||||
ring?: THREE.Mesh;
|
||||
moons: MoonVisual[];
|
||||
}
|
||||
|
||||
interface MoonVisual {
|
||||
mesh: THREE.Mesh;
|
||||
orbit: THREE.LineLoop;
|
||||
}
|
||||
|
||||
interface WorldState {
|
||||
label: string;
|
||||
seed: number;
|
||||
@@ -116,6 +130,7 @@ export class GameViewer {
|
||||
private readonly stationMeshes = new Map<string, THREE.Mesh>();
|
||||
private readonly shipVisuals = new Map<string, ShipVisual>();
|
||||
private readonly systemSummaryVisuals = new Map<string, SystemSummaryVisual>();
|
||||
private readonly planetVisuals: PlanetVisual[] = [];
|
||||
private readonly orbitLines: THREE.Object3D[] = [];
|
||||
private readonly statusEl: HTMLDivElement;
|
||||
private readonly detailTitleEl: HTMLHeadingElement;
|
||||
@@ -126,6 +141,7 @@ export class GameViewer {
|
||||
private readonly marqueeEl: HTMLDivElement;
|
||||
|
||||
private world?: WorldState;
|
||||
private worldTimeSyncMs = performance.now();
|
||||
private stream?: EventSource;
|
||||
private readonly networkStats: NetworkStats = {
|
||||
snapshotBytes: 0,
|
||||
@@ -286,6 +302,7 @@ export class GameViewer {
|
||||
}
|
||||
|
||||
private applySnapshot(snapshot: WorldSnapshot) {
|
||||
this.worldTimeSyncMs = performance.now();
|
||||
const signature = `${snapshot.seed}|${snapshot.systems.length}`;
|
||||
if (signature !== this.worldSignature) {
|
||||
this.worldSignature = signature;
|
||||
@@ -306,6 +323,7 @@ export class GameViewer {
|
||||
return;
|
||||
}
|
||||
|
||||
this.worldTimeSyncMs = performance.now();
|
||||
this.world.sequence = delta.sequence;
|
||||
this.world.tickIntervalMs = delta.tickIntervalMs;
|
||||
this.world.generatedAtUtc = delta.generatedAtUtc;
|
||||
@@ -337,6 +355,7 @@ export class GameViewer {
|
||||
this.systemGroup.clear();
|
||||
this.selectableTargets.clear();
|
||||
this.presentationEntries.length = 0;
|
||||
this.planetVisuals.length = 0;
|
||||
this.orbitLines.length = 0;
|
||||
this.systemSummaryVisuals.clear();
|
||||
|
||||
@@ -344,58 +363,56 @@ export class GameViewer {
|
||||
const root = new THREE.Group();
|
||||
root.position.set(system.position.x, system.position.y, system.position.z);
|
||||
|
||||
const star = new THREE.Mesh(
|
||||
new THREE.SphereGeometry(system.starSize, 32, 32),
|
||||
new THREE.MeshBasicMaterial({ color: system.starColor }),
|
||||
);
|
||||
const halo = new THREE.Mesh(
|
||||
new THREE.SphereGeometry(system.starSize * 1.65, 24, 24),
|
||||
new THREE.MeshBasicMaterial({
|
||||
color: system.starColor,
|
||||
transparent: true,
|
||||
opacity: 0.14,
|
||||
side: THREE.BackSide,
|
||||
}),
|
||||
);
|
||||
const starCluster = this.createStarCluster(system);
|
||||
const systemIcon = this.createTacticalIcon(system.starColor, 96);
|
||||
const summaryVisual = this.createSystemSummaryVisual(new THREE.Vector3(system.position.x, system.position.y + system.starSize + 110, system.position.z));
|
||||
const summaryVisual = this.createSystemSummaryVisual(new THREE.Vector3(system.position.x, system.position.y + system.starSize + 140, system.position.z));
|
||||
summaryVisual.sprite.position.set(0, system.starSize + 110, 0);
|
||||
root.add(star, halo, systemIcon, summaryVisual.sprite);
|
||||
this.registerPresentation(star, systemIcon, true);
|
||||
this.registerPresentation(halo, systemIcon, true);
|
||||
root.add(starCluster, systemIcon, summaryVisual.sprite);
|
||||
this.registerPresentation(starCluster, systemIcon, true);
|
||||
this.systemSummaryVisuals.set(system.id, summaryVisual);
|
||||
this.selectableTargets.set(star, { kind: "system", id: system.id });
|
||||
this.selectableTargets.set(halo, { kind: "system", id: system.id });
|
||||
starCluster.traverse((child) => {
|
||||
if (child instanceof THREE.Mesh) {
|
||||
this.selectableTargets.set(child, { kind: "system", id: system.id });
|
||||
}
|
||||
});
|
||||
this.selectableTargets.set(systemIcon, { kind: "system", id: system.id });
|
||||
|
||||
for (const [planetIndex, planet] of system.planets.entries()) {
|
||||
const orbit = new THREE.LineLoop(
|
||||
new THREE.BufferGeometry().setFromPoints(
|
||||
Array.from({ length: 80 }, (_, index) => {
|
||||
const angle = (index / 80) * Math.PI * 2;
|
||||
return new THREE.Vector3(
|
||||
Math.cos(angle) * planet.orbitRadius,
|
||||
0,
|
||||
Math.sin(angle) * planet.orbitRadius,
|
||||
);
|
||||
}),
|
||||
),
|
||||
new THREE.LineBasicMaterial({ color: 0x17314d, transparent: true, opacity: 0.45 }),
|
||||
);
|
||||
const orbit = this.createPlanetOrbit(planet);
|
||||
const planetMesh = new THREE.Mesh(
|
||||
new THREE.SphereGeometry(planet.size, 18, 18),
|
||||
new THREE.MeshStandardMaterial({
|
||||
color: planet.color,
|
||||
roughness: 0.92,
|
||||
metalness: 0.08,
|
||||
emissive: new THREE.Color(planet.color).multiplyScalar(0.04),
|
||||
}),
|
||||
);
|
||||
planetMesh.position.set(planet.orbitRadius, 0, 0);
|
||||
planetMesh.position.copy(this.computePlanetLocalPosition(planet, this.currentWorldTimeSeconds()));
|
||||
const planetIcon = this.createTacticalIcon(planet.color, Math.max(24, planet.size * 2));
|
||||
planetIcon.position.copy(planetMesh.position);
|
||||
const ring = planet.hasRing ? this.createPlanetRing(planet) : undefined;
|
||||
if (ring) {
|
||||
ring.position.copy(planetMesh.position);
|
||||
}
|
||||
const moons = this.createMoonVisuals(planet);
|
||||
root.add(orbit, planetMesh, planetIcon);
|
||||
if (ring) {
|
||||
root.add(ring);
|
||||
}
|
||||
for (const moon of moons) {
|
||||
moon.orbit.position.copy(planetMesh.position);
|
||||
moon.mesh.position.copy(planetMesh.position);
|
||||
root.add(moon.orbit, moon.mesh);
|
||||
this.orbitLines.push(moon.orbit);
|
||||
this.registerPresentation(moon.mesh, planetIcon, true, true);
|
||||
}
|
||||
this.orbitLines.push(orbit);
|
||||
this.registerPresentation(planetMesh, planetIcon, true, true);
|
||||
if (ring) {
|
||||
this.registerPresentation(ring, planetIcon, true, true);
|
||||
}
|
||||
this.planetVisuals.push({ planet, mesh: planetMesh, icon: planetIcon, ring, moons });
|
||||
this.selectableTargets.set(planetMesh, { kind: "planet", systemId: system.id, planetIndex });
|
||||
this.selectableTargets.set(planetIcon, { kind: "planet", systemId: system.id, planetIndex });
|
||||
}
|
||||
@@ -410,7 +427,7 @@ export class GameViewer {
|
||||
|
||||
for (const node of nodes) {
|
||||
const mesh = this.createNodeMesh(node);
|
||||
const icon = this.createTacticalIcon("#d2b07a", 20);
|
||||
const icon = this.createTacticalIcon(node.sourceKind === "gas-cloud" ? "#7fd6ff" : "#d2b07a", 20);
|
||||
icon.position.copy(mesh.position);
|
||||
this.nodeMeshes.set(node.id, mesh);
|
||||
this.nodeGroup.add(mesh, icon);
|
||||
@@ -585,7 +602,8 @@ export class GameViewer {
|
||||
this.detailTitleEl.textContent = `Node ${node.id}`;
|
||||
this.detailBodyEl.innerHTML = `
|
||||
<p>${node.systemId}</p>
|
||||
<p>${node.itemId} ${node.oreRemaining.toFixed(0)} / ${node.maxOre.toFixed(0)}</p>
|
||||
<p>Source ${node.sourceKind}<br>Resource ${node.itemId}</p>
|
||||
<p>Stock ${node.oreRemaining.toFixed(0)} / ${node.maxOre.toFixed(0)}</p>
|
||||
`;
|
||||
return;
|
||||
}
|
||||
@@ -599,7 +617,9 @@ export class GameViewer {
|
||||
this.detailTitleEl.textContent = planet.label;
|
||||
this.detailBodyEl.innerHTML = `
|
||||
<p>${system.label}</p>
|
||||
<p>Orbit ${planet.orbitRadius.toFixed(0)}<br>Size ${planet.size.toFixed(0)}</p>
|
||||
<p>${planet.planetType} · ${planet.shape} · Moons ${planet.moonCount}</p>
|
||||
<p>Orbit ${planet.orbitRadius.toFixed(0)}<br>Speed ${planet.orbitSpeed.toFixed(3)}<br>Ecc ${planet.orbitEccentricity.toFixed(3)}<br>Inc ${planet.orbitInclination.toFixed(1)}°</p>
|
||||
<p>Phase ${planet.orbitPhaseAtEpoch.toFixed(1)}°</p>
|
||||
`;
|
||||
return;
|
||||
}
|
||||
@@ -611,12 +631,15 @@ export class GameViewer {
|
||||
this.detailTitleEl.textContent = system.label;
|
||||
this.detailBodyEl.innerHTML = `
|
||||
<p>${system.id}</p>
|
||||
<p>${system.starKind} · ${system.starCount} star${system.starCount > 1 ? "s" : ""}</p>
|
||||
<p>Planets ${system.planets.length}<br>Height ${system.position.y.toFixed(0)}</p>
|
||||
`;
|
||||
}
|
||||
|
||||
private render() {
|
||||
const delta = Math.min(this.clock.getDelta(), 0.033);
|
||||
this.updateCamera(delta);
|
||||
this.updatePlanetPresentation();
|
||||
this.updateShipPresentation();
|
||||
this.updateNetworkPanel();
|
||||
this.applyZoomPresentation();
|
||||
@@ -770,16 +793,132 @@ export class GameViewer {
|
||||
this.updateSystemSummaryPresentation();
|
||||
}
|
||||
|
||||
private updatePlanetPresentation() {
|
||||
const nowSeconds = this.currentWorldTimeSeconds();
|
||||
for (const visual of this.planetVisuals) {
|
||||
const position = this.computePlanetLocalPosition(visual.planet, nowSeconds);
|
||||
visual.mesh.position.copy(position);
|
||||
visual.icon.position.copy(position);
|
||||
if (visual.ring) {
|
||||
visual.ring.position.copy(position);
|
||||
}
|
||||
for (const [moonIndex, moon] of visual.moons.entries()) {
|
||||
moon.orbit.position.copy(position);
|
||||
moon.mesh.position.copy(position).add(this.computeMoonLocalPosition(visual.planet, moonIndex, nowSeconds));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private createNodeMesh(node: ResourceNodeSnapshot) {
|
||||
const isGas = node.sourceKind === "gas-cloud" || node.itemId === "gas";
|
||||
const mesh = new THREE.Mesh(
|
||||
new THREE.IcosahedronGeometry(12, 0),
|
||||
new THREE.MeshStandardMaterial({ color: 0xd2b07a, flatShading: true }),
|
||||
isGas ? new THREE.SphereGeometry(18, 14, 14) : new THREE.IcosahedronGeometry(12, 0),
|
||||
new THREE.MeshStandardMaterial({
|
||||
color: isGas ? 0x7fd6ff : 0xd2b07a,
|
||||
flatShading: !isGas,
|
||||
transparent: isGas,
|
||||
opacity: isGas ? 0.68 : 1,
|
||||
emissive: new THREE.Color(isGas ? 0x7fd6ff : 0xd2b07a).multiplyScalar(isGas ? 0.22 : 0.05),
|
||||
}),
|
||||
);
|
||||
mesh.position.copy(this.toThreeVector(node.position));
|
||||
mesh.scale.setScalar(0.8 + (node.oreRemaining / Math.max(node.maxOre, 1)) * 0.6);
|
||||
return mesh;
|
||||
}
|
||||
|
||||
private createStarCluster(system: SystemSnapshot) {
|
||||
const root = new THREE.Group();
|
||||
const offsets = system.starCount > 1
|
||||
? [new THREE.Vector3(-system.starSize * 0.55, 0, 0), new THREE.Vector3(system.starSize * 0.75, system.starSize * 0.08, 0)]
|
||||
: [new THREE.Vector3(0, 0, 0)];
|
||||
|
||||
for (const [index, offset] of offsets.entries()) {
|
||||
const sizeScale = index === 0 ? 1 : 0.72;
|
||||
const star = new THREE.Mesh(
|
||||
new THREE.SphereGeometry(system.starSize * sizeScale, 28, 28),
|
||||
new THREE.MeshBasicMaterial({ color: system.starColor }),
|
||||
);
|
||||
const halo = new THREE.Mesh(
|
||||
new THREE.SphereGeometry(system.starSize * sizeScale * 1.72, 24, 24),
|
||||
new THREE.MeshBasicMaterial({
|
||||
color: system.starColor,
|
||||
transparent: true,
|
||||
opacity: this.starHaloOpacity(system.starKind),
|
||||
side: THREE.BackSide,
|
||||
}),
|
||||
);
|
||||
star.position.copy(offset);
|
||||
halo.position.copy(offset);
|
||||
root.add(star, halo);
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
private createPlanetOrbit(planet: PlanetSnapshot) {
|
||||
const points = Array.from({ length: 120 }, (_, index) => {
|
||||
const phaseDegrees = (index / 120) * 360;
|
||||
return this.computePlanetLocalPosition(planet, 0, phaseDegrees);
|
||||
});
|
||||
|
||||
return new THREE.LineLoop(
|
||||
new THREE.BufferGeometry().setFromPoints(points),
|
||||
new THREE.LineBasicMaterial({ color: 0x17314d, transparent: true, opacity: 0.22 }),
|
||||
);
|
||||
}
|
||||
|
||||
private createPlanetRing(planet: PlanetSnapshot) {
|
||||
const ring = new THREE.Mesh(
|
||||
new THREE.RingGeometry(planet.size * 1.35, planet.size * 2.15, 48),
|
||||
new THREE.MeshBasicMaterial({
|
||||
color: 0xdac89a,
|
||||
transparent: true,
|
||||
opacity: 0.42,
|
||||
side: THREE.DoubleSide,
|
||||
}),
|
||||
);
|
||||
ring.rotation.x = Math.PI / 2;
|
||||
ring.rotation.z = THREE.MathUtils.degToRad(planet.orbitInclination * 0.25);
|
||||
return ring;
|
||||
}
|
||||
|
||||
private createMoonVisuals(planet: PlanetSnapshot) {
|
||||
const moonCount = Math.min(planet.moonCount, 12);
|
||||
const moons: MoonVisual[] = [];
|
||||
|
||||
for (let moonIndex = 0; moonIndex < moonCount; moonIndex += 1) {
|
||||
const orbitRadius = this.computeMoonOrbitRadius(planet, moonIndex);
|
||||
const orbit = new THREE.LineLoop(
|
||||
new THREE.BufferGeometry().setFromPoints(
|
||||
Array.from({ length: 48 }, (_, index) => {
|
||||
const angle = (index / 48) * Math.PI * 2;
|
||||
return new THREE.Vector3(
|
||||
Math.cos(angle) * orbitRadius,
|
||||
0,
|
||||
Math.sin(angle) * orbitRadius,
|
||||
);
|
||||
}),
|
||||
),
|
||||
new THREE.LineBasicMaterial({ color: 0x3b5065, transparent: true, opacity: 0.1 }),
|
||||
);
|
||||
orbit.rotation.x = THREE.MathUtils.degToRad(planet.orbitInclination * 0.35);
|
||||
|
||||
const moonSize = this.computeMoonSize(planet, moonIndex);
|
||||
const mesh = new THREE.Mesh(
|
||||
new THREE.SphereGeometry(moonSize, 12, 12),
|
||||
new THREE.MeshStandardMaterial({
|
||||
color: new THREE.Color(planet.color).lerp(new THREE.Color("#d9dee7"), 0.55),
|
||||
roughness: 0.96,
|
||||
metalness: 0.02,
|
||||
}),
|
||||
);
|
||||
|
||||
moons.push({ mesh, orbit });
|
||||
}
|
||||
|
||||
return moons;
|
||||
}
|
||||
|
||||
private createStationMesh(station: StationSnapshot) {
|
||||
const mesh = new THREE.Mesh(
|
||||
new THREE.CylinderGeometry(24, 24, 18, 10),
|
||||
@@ -893,6 +1032,7 @@ export class GameViewer {
|
||||
const ships = shipCounts.get(systemId) ?? 0;
|
||||
const stations = stationCounts.get(systemId) ?? 0;
|
||||
const structures = structureCounts.get(systemId) ?? 0;
|
||||
const gasClouds = [...this.world.nodes.values()].filter((node) => node.systemId === systemId && node.sourceKind === "gas-cloud").length;
|
||||
const total = ships + stations + structures;
|
||||
if (total > 0) {
|
||||
context.fillStyle = "rgba(3, 8, 18, 0.72)";
|
||||
@@ -902,7 +1042,7 @@ export class GameViewer {
|
||||
|
||||
this.drawCountIcon(context, "ship", 126, 98, ships, "#8bc0ff");
|
||||
this.drawCountIcon(context, "station", 256, 98, stations, "#ffbf69");
|
||||
this.drawCountIcon(context, "structure", 386, 98, structures, "#98adc4");
|
||||
this.drawCountIcon(context, "structure", 386, 98, structures, gasClouds > 0 ? "#7fd6ff" : "#98adc4");
|
||||
}
|
||||
|
||||
visual.texture.needsUpdate = true;
|
||||
@@ -1081,6 +1221,94 @@ export class GameViewer {
|
||||
return new THREE.Vector3(vector.x, vector.y, vector.z);
|
||||
}
|
||||
|
||||
private currentWorldTimeSeconds() {
|
||||
if (!this.world) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const baseUtcMs = Date.parse(this.world.generatedAtUtc);
|
||||
const elapsedMs = performance.now() - this.worldTimeSyncMs;
|
||||
return ((baseUtcMs + elapsedMs) / 1000) + (this.world.seed * 97);
|
||||
}
|
||||
|
||||
private computePlanetLocalPosition(planet: PlanetSnapshot, timeSeconds: number, phaseOverrideDegrees?: number) {
|
||||
const eccentricity = THREE.MathUtils.clamp(planet.orbitEccentricity, 0, 0.85);
|
||||
const meanAnomaly = THREE.MathUtils.degToRad(phaseOverrideDegrees ?? planet.orbitPhaseAtEpoch) + (timeSeconds * planet.orbitSpeed);
|
||||
const eccentricAnomaly = meanAnomaly
|
||||
+ (eccentricity * Math.sin(meanAnomaly))
|
||||
+ (0.5 * eccentricity * eccentricity * Math.sin(2 * meanAnomaly));
|
||||
const semiMajorAxis = planet.orbitRadius;
|
||||
const semiMinorAxis = semiMajorAxis * Math.sqrt(Math.max(1 - (eccentricity * eccentricity), 0.05));
|
||||
const local = new THREE.Vector3(
|
||||
semiMajorAxis * (Math.cos(eccentricAnomaly) - eccentricity),
|
||||
0,
|
||||
semiMinorAxis * Math.sin(eccentricAnomaly),
|
||||
);
|
||||
|
||||
local.applyAxisAngle(new THREE.Vector3(0, 1, 0), THREE.MathUtils.degToRad(planet.orbitArgumentOfPeriapsis));
|
||||
local.applyAxisAngle(new THREE.Vector3(1, 0, 0), THREE.MathUtils.degToRad(planet.orbitInclination));
|
||||
local.applyAxisAngle(new THREE.Vector3(0, 1, 0), THREE.MathUtils.degToRad(planet.orbitLongitudeOfAscendingNode));
|
||||
return local;
|
||||
}
|
||||
|
||||
private computeMoonLocalPosition(planet: PlanetSnapshot, moonIndex: number, timeSeconds: number) {
|
||||
const orbitRadius = this.computeMoonOrbitRadius(planet, moonIndex);
|
||||
const speed = this.computeMoonOrbitSpeed(planet, moonIndex);
|
||||
const phase = this.hashUnit(`${planet.label}:${moonIndex}:phase`) * Math.PI * 2;
|
||||
const inclination = THREE.MathUtils.degToRad((this.hashUnit(`${planet.label}:${moonIndex}:inclination`) - 0.5) * 28);
|
||||
const node = THREE.MathUtils.degToRad(this.hashUnit(`${planet.label}:${moonIndex}:node`) * 360);
|
||||
const angle = phase + (timeSeconds * speed);
|
||||
|
||||
const local = new THREE.Vector3(
|
||||
Math.cos(angle) * orbitRadius,
|
||||
0,
|
||||
Math.sin(angle) * orbitRadius,
|
||||
);
|
||||
local.applyAxisAngle(new THREE.Vector3(1, 0, 0), inclination);
|
||||
local.applyAxisAngle(new THREE.Vector3(0, 1, 0), node);
|
||||
return local;
|
||||
}
|
||||
|
||||
private computeMoonOrbitRadius(planet: PlanetSnapshot, moonIndex: number) {
|
||||
const spacing = planet.size * 1.4;
|
||||
const variance = this.hashUnit(`${planet.label}:${moonIndex}:radius`) * planet.size * 0.9;
|
||||
return (planet.size * 1.8) + (moonIndex * spacing) + variance;
|
||||
}
|
||||
|
||||
private computeMoonOrbitSpeed(planet: PlanetSnapshot, moonIndex: number) {
|
||||
const radius = this.computeMoonOrbitRadius(planet, moonIndex);
|
||||
return 0.9 / Math.sqrt(Math.max(radius, 1)) + (moonIndex * 0.003);
|
||||
}
|
||||
|
||||
private computeMoonSize(planet: PlanetSnapshot, moonIndex: number) {
|
||||
const base = Math.max(2.2, planet.size * 0.11);
|
||||
const variance = this.hashUnit(`${planet.label}:${moonIndex}:size`) * Math.max(planet.size * 0.16, 2.5);
|
||||
return Math.min(base + variance, planet.size * 0.42);
|
||||
}
|
||||
|
||||
private hashUnit(value: string) {
|
||||
let hash = this.world?.seed ?? 1;
|
||||
for (let index = 0; index < value.length; index += 1) {
|
||||
hash = ((hash << 5) - hash) + value.charCodeAt(index);
|
||||
hash |= 0;
|
||||
}
|
||||
|
||||
return (hash >>> 0) / 0xffffffff;
|
||||
}
|
||||
|
||||
private starHaloOpacity(starKind: string) {
|
||||
if (starKind.includes("neutron")) {
|
||||
return 0.22;
|
||||
}
|
||||
if (starKind.includes("white-dwarf")) {
|
||||
return 0.18;
|
||||
}
|
||||
if (starKind.includes("brown-dwarf")) {
|
||||
return 0.1;
|
||||
}
|
||||
return 0.14;
|
||||
}
|
||||
|
||||
private screenPointFromClient(clientX: number, clientY: number) {
|
||||
const bounds = this.renderer.domElement.getBoundingClientRect();
|
||||
return new THREE.Vector2(clientX - bounds.left, clientY - bounds.top);
|
||||
|
||||
@@ -41,6 +41,8 @@ export interface SystemSnapshot {
|
||||
id: string;
|
||||
label: string;
|
||||
position: Vector3Dto;
|
||||
starKind: string;
|
||||
starCount: number;
|
||||
starColor: string;
|
||||
starSize: number;
|
||||
planets: PlanetSnapshot[];
|
||||
@@ -48,7 +50,16 @@ export interface SystemSnapshot {
|
||||
|
||||
export interface PlanetSnapshot {
|
||||
label: string;
|
||||
planetType: string;
|
||||
shape: string;
|
||||
moonCount: number;
|
||||
orbitRadius: number;
|
||||
orbitSpeed: number;
|
||||
orbitEccentricity: number;
|
||||
orbitInclination: number;
|
||||
orbitLongitudeOfAscendingNode: number;
|
||||
orbitArgumentOfPeriapsis: number;
|
||||
orbitPhaseAtEpoch: number;
|
||||
size: number;
|
||||
color: string;
|
||||
hasRing: boolean;
|
||||
@@ -58,6 +69,7 @@ export interface ResourceNodeSnapshot {
|
||||
id: string;
|
||||
systemId: string;
|
||||
position: Vector3Dto;
|
||||
sourceKind: string;
|
||||
oreRemaining: number;
|
||||
maxOre: number;
|
||||
itemId: string;
|
||||
|
||||
Reference in New Issue
Block a user