feat: production chain
This commit is contained in:
@@ -7,12 +7,14 @@ import {
|
||||
resolveOrbitalAnchorPosition,
|
||||
toThreeVector,
|
||||
} from "./viewerMath";
|
||||
import { describeActiveSpace } from "./viewerSelection";
|
||||
import {
|
||||
resolveShipHeading,
|
||||
updateSystemStarPresentation,
|
||||
updateSystemSummaryPresentation,
|
||||
getAnimatedShipLocalPosition,
|
||||
} from "./viewerPresentation";
|
||||
import { rawObject } from "./viewerScenePrimitives";
|
||||
import type {
|
||||
LocalBubbleDelta,
|
||||
LocalBubbleSnapshot,
|
||||
@@ -22,6 +24,7 @@ import type {
|
||||
import type {
|
||||
BubbleVisual,
|
||||
ClaimVisual,
|
||||
Selectable,
|
||||
ConstructionSiteVisual,
|
||||
NodeVisual,
|
||||
OrbitalAnchor,
|
||||
@@ -59,15 +62,17 @@ export interface WorldPresentationContext extends WorldOrbitalContext {
|
||||
systemSummaryVisuals: Map<string, SystemSummaryVisual>;
|
||||
toDisplayLocalPosition: (localPosition: THREE.Vector3, systemId?: string) => THREE.Vector3;
|
||||
updateSystemDetailVisibility: () => void;
|
||||
setShellReticleOpacity: (sprite: THREE.Sprite, opacity: number) => void;
|
||||
setShellReticleOpacity: (sprite: SystemVisual["shellReticle"], opacity: number) => void;
|
||||
}
|
||||
|
||||
export interface GameStatusParams {
|
||||
statusEl: HTMLDivElement;
|
||||
summaryEl?: HTMLSpanElement;
|
||||
world?: WorldState;
|
||||
activeSystemId?: string;
|
||||
cameraMode: CameraMode;
|
||||
zoomLevel: ZoomLevel;
|
||||
selectedItems: Selectable[];
|
||||
mode: string;
|
||||
}
|
||||
|
||||
@@ -77,59 +82,59 @@ export function updateWorldPresentation(context: WorldPresentationContext) {
|
||||
|
||||
for (const visual of context.shipVisuals.values()) {
|
||||
const worldPosition = getAnimatedShipLocalPosition(visual, now);
|
||||
visual.mesh.position.copy(context.toDisplayLocalPosition(worldPosition, visual.systemId));
|
||||
visual.icon.position.copy(visual.mesh.position);
|
||||
visual.mesh.setPosition(context.toDisplayLocalPosition(worldPosition, visual.systemId));
|
||||
visual.icon.setPosition(rawObject(visual.mesh).position.clone());
|
||||
const shipVisible = visual.systemId === context.activeSystemId;
|
||||
visual.mesh.visible = shipVisible;
|
||||
visual.icon.visible = shipVisible && visual.icon.visible;
|
||||
visual.mesh.setVisible(shipVisible);
|
||||
visual.icon.setVisible(shipVisible && rawObject(visual.icon).visible);
|
||||
const desiredHeading = resolveShipHeading(visual, worldPosition, context.orbitYaw);
|
||||
if (desiredHeading.lengthSq() > 0.01) {
|
||||
visual.mesh.lookAt(visual.mesh.position.clone().add(desiredHeading));
|
||||
visual.mesh.lookAt(rawObject(visual.mesh).position.clone().add(desiredHeading));
|
||||
}
|
||||
}
|
||||
|
||||
for (const visual of context.nodeVisuals.values()) {
|
||||
const animatedLocalPosition = computeNodeLocalPosition(context, visual, worldTimeSeconds);
|
||||
visual.mesh.position.copy(context.toDisplayLocalPosition(animatedLocalPosition, visual.systemId));
|
||||
visual.icon.position.copy(visual.mesh.position);
|
||||
visual.mesh.visible = visual.systemId === context.activeSystemId;
|
||||
visual.mesh.setPosition(context.toDisplayLocalPosition(animatedLocalPosition, visual.systemId));
|
||||
visual.icon.setPosition(rawObject(visual.mesh).position.clone());
|
||||
visual.mesh.setVisible(visual.systemId === context.activeSystemId);
|
||||
}
|
||||
|
||||
for (const visual of context.spatialNodeVisuals.values()) {
|
||||
const animatedLocalPosition = computeSpatialNodeLocalPosition(context, visual, worldTimeSeconds);
|
||||
visual.mesh.position.copy(context.toDisplayLocalPosition(animatedLocalPosition, visual.systemId));
|
||||
visual.icon.position.copy(visual.mesh.position);
|
||||
visual.mesh.visible = visual.systemId === context.activeSystemId;
|
||||
visual.icon.visible = visual.systemId === context.activeSystemId;
|
||||
visual.mesh.setPosition(context.toDisplayLocalPosition(animatedLocalPosition, visual.systemId));
|
||||
visual.icon.setPosition(rawObject(visual.mesh).position.clone());
|
||||
visual.mesh.setVisible(visual.systemId === context.activeSystemId);
|
||||
visual.icon.setVisible(visual.systemId === context.activeSystemId);
|
||||
}
|
||||
|
||||
for (const visual of context.bubbleVisuals.values()) {
|
||||
const animatedLocalPosition = resolveBubbleAnimatedLocalPosition(context, visual, worldTimeSeconds);
|
||||
visual.mesh.position.copy(context.toDisplayLocalPosition(animatedLocalPosition, visual.systemId));
|
||||
visual.mesh.visible = visual.systemId === context.activeSystemId;
|
||||
visual.mesh.setPosition(context.toDisplayLocalPosition(animatedLocalPosition, visual.systemId));
|
||||
visual.mesh.setVisible(visual.systemId === context.activeSystemId);
|
||||
}
|
||||
|
||||
for (const visual of context.stationVisuals.values()) {
|
||||
const animatedLocalPosition = resolveStructureAnimatedLocalPosition(context, visual, worldTimeSeconds);
|
||||
visual.mesh.position.copy(context.toDisplayLocalPosition(animatedLocalPosition, visual.systemId));
|
||||
visual.icon.position.copy(visual.mesh.position);
|
||||
visual.mesh.visible = visual.systemId === context.activeSystemId;
|
||||
visual.mesh.setPosition(context.toDisplayLocalPosition(animatedLocalPosition, visual.systemId));
|
||||
visual.icon.setPosition(rawObject(visual.mesh).position.clone());
|
||||
visual.mesh.setVisible(visual.systemId === context.activeSystemId);
|
||||
}
|
||||
|
||||
for (const visual of context.claimVisuals.values()) {
|
||||
const animatedLocalPosition = computeSpatialNodeLocalPositionById(context, visual.nodeId, worldTimeSeconds) ?? visual.localPosition.clone();
|
||||
visual.mesh.position.copy(context.toDisplayLocalPosition(animatedLocalPosition, visual.systemId));
|
||||
visual.icon.position.copy(visual.mesh.position);
|
||||
visual.mesh.visible = visual.systemId === context.activeSystemId;
|
||||
visual.icon.visible = visual.systemId === context.activeSystemId;
|
||||
visual.mesh.setPosition(context.toDisplayLocalPosition(animatedLocalPosition, visual.systemId));
|
||||
visual.icon.setPosition(rawObject(visual.mesh).position.clone());
|
||||
visual.mesh.setVisible(visual.systemId === context.activeSystemId);
|
||||
visual.icon.setVisible(visual.systemId === context.activeSystemId);
|
||||
}
|
||||
|
||||
for (const visual of context.constructionSiteVisuals.values()) {
|
||||
const animatedLocalPosition = computeSpatialNodeLocalPositionById(context, visual.nodeId, worldTimeSeconds) ?? visual.localPosition.clone();
|
||||
visual.mesh.position.copy(context.toDisplayLocalPosition(animatedLocalPosition, visual.systemId));
|
||||
visual.icon.position.copy(visual.mesh.position);
|
||||
visual.mesh.visible = visual.systemId === context.activeSystemId;
|
||||
visual.icon.visible = visual.systemId === context.activeSystemId;
|
||||
visual.mesh.setPosition(context.toDisplayLocalPosition(animatedLocalPosition, visual.systemId));
|
||||
visual.icon.setPosition(rawObject(visual.mesh).position.clone());
|
||||
visual.mesh.setVisible(visual.systemId === context.activeSystemId);
|
||||
visual.icon.setVisible(visual.systemId === context.activeSystemId);
|
||||
}
|
||||
|
||||
updateSystemStarPresentation(
|
||||
@@ -218,22 +223,26 @@ export function renderRecentEvents(world: WorldState | undefined, entityKind: st
|
||||
}
|
||||
|
||||
export function updateGameStatus(params: GameStatusParams) {
|
||||
const { statusEl, world, activeSystemId, cameraMode, zoomLevel, mode } = params;
|
||||
const { statusEl, summaryEl, world, activeSystemId, cameraMode, zoomLevel, selectedItems, mode } = params;
|
||||
const sequence = world?.sequence ?? 0;
|
||||
const generatedAt = world?.generatedAtUtc
|
||||
? new Date(world.generatedAtUtc).toLocaleTimeString()
|
||||
: "n/a";
|
||||
const activeSystem = activeSystemId ?? "deep-space";
|
||||
const cameraModeLabel = cameraMode === "follow" ? "camera-follow" : "tactical";
|
||||
const displayZoomLevel = activeSystemId ? zoomLevel : "universe";
|
||||
const activeSpace = describeActiveSpace(world, displayZoomLevel, activeSystemId, selectedItems);
|
||||
const cameraModeLabel = cameraMode === "follow" ? "follow" : "map";
|
||||
|
||||
statusEl.textContent = [
|
||||
`mode: ${mode}`,
|
||||
`camera: ${cameraModeLabel}`,
|
||||
`zoom: ${zoomLevel}`,
|
||||
`system: ${activeSystem}`,
|
||||
`zoom: ${displayZoomLevel}`,
|
||||
`space: ${activeSpace}`,
|
||||
`sequence: ${sequence}`,
|
||||
`snapshot: ${generatedAt}`,
|
||||
].join("\n");
|
||||
if (summaryEl) {
|
||||
summaryEl.textContent = `${mode} | ${displayZoomLevel} | ${activeSpace}`;
|
||||
}
|
||||
}
|
||||
|
||||
export function deriveNodeOrbital(
|
||||
@@ -371,7 +380,7 @@ export function computeSpatialNodeLocalPositionById(
|
||||
|
||||
export function setBubbleVisualState(visual: BubbleVisual, bubble: LocalBubbleSnapshot | LocalBubbleDelta) {
|
||||
const intensity = bubble.occupantShipIds.length + bubble.occupantStationIds.length + bubble.occupantConstructionSiteIds.length;
|
||||
const material = visual.mesh.material as THREE.LineBasicMaterial;
|
||||
const material = (rawObject(visual.mesh) as THREE.LineLoop).material as THREE.LineBasicMaterial;
|
||||
material.opacity = THREE.MathUtils.clamp(0.18 + intensity * 0.05, 0.18, 0.72);
|
||||
material.color.set(intensity > 0 ? "#7fffd4" : "#6ed6ff");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user