Files
space-game/apps/viewer/src/viewerControllerFactory.ts

300 lines
14 KiB
TypeScript

import * as THREE from "three";
import { ViewerInteractionController } from "./viewerInteractionController";
import { ViewerNavigationController } from "./viewerNavigationController";
import { ViewerPresentationController } from "./viewerPresentationController";
import { ViewerSceneDataController } from "./viewerSceneDataController";
import { ViewerWorldLifecycle } from "./viewerWorldLifecycle";
import { ViewerHistoryWindowController } from "./viewerHistoryWindowController";
import { useViewerSceneStore } from "./ui/stores/viewerScene";
import { useViewerOrderContextMenuStore } from "./ui/stores/viewerOrderContextMenu";
import { viewerPinia } from "./ui/stores/pinia";
export function createViewerControllers(host: any) {
const sceneStore = useViewerSceneStore(viewerPinia);
const orderContextMenuStore = useViewerOrderContextMenuStore(viewerPinia);
const sceneDataController = new ViewerSceneDataController({
documentRef: document,
getWorldOrbitalTimeSeconds: () => host.world?.orbitalTimeSeconds,
getOrbitalSimulationSpeed: () => host.world?.orbitalSimulation.simulatedSecondsPerRealSecond ?? 0,
getWorldSeed: () => host.world?.seed ?? 1,
getWorldTimeSyncMs: () => host.worldTimeSyncMs,
getWorldPresentationContext: () => host.createWorldPresentationContext(),
getActiveSystemId: () => host.activeSystemId,
galaxySystemGroup: host.galaxyLayer.systemGroup,
systemScene: host.systemLayer.scene,
celestialGroup: host.systemLayer.celestialGroup,
nodeGroup: host.systemLayer.nodeGroup,
stationGroup: host.systemLayer.stationGroup,
claimGroup: host.systemLayer.claimGroup,
constructionSiteGroup: host.systemLayer.constructionSiteGroup,
shipGroup: host.systemLayer.shipGroup,
galaxySelectableTargets: host.galaxyLayer.selectableTargets,
systemSelectableTargets: host.systemLayer.selectableTargets,
systemVisuals: host.galaxyLayer.systemVisuals,
planetVisuals: host.systemLayer.planetVisuals,
celestialVisuals: host.systemLayer.celestialVisuals,
nodeVisuals: host.systemLayer.nodeVisuals,
stationVisuals: host.systemLayer.stationVisuals,
claimVisuals: host.systemLayer.claimVisuals,
constructionSiteVisuals: host.systemLayer.constructionSiteVisuals,
shipVisuals: host.systemLayer.shipVisuals,
});
const navigationController = new ViewerNavigationController({
getWorld: () => host.world,
getWorldTimeSyncMs: () => host.worldTimeSyncMs,
getActiveSystemId: () => host.activeSystemId,
setActiveSystemId: (value) => {
host.activeSystemId = value;
sceneStore.setViewContext(value ?? null, host.povLevel);
},
onActiveSystemChanged: (oldId, newId) => {
sceneDataController.onActiveSystemChanged(oldId, newId);
},
getCameraMode: () => host.cameraMode,
setCameraMode: (value) => {
host.cameraMode = value;
},
getCameraTargetShipId: () => host.cameraTargetShipId,
setCameraTargetShipId: (value) => {
host.cameraTargetShipId = value;
},
getCurrentDistance: () => host.currentDistance,
getPovLevel: () => host.povLevel,
getSelectedItems: () => host.selectedItems,
getOrbitYaw: () => host.orbitYaw,
getFollowOrbitYaw: () => host.followOrbitYaw,
getFollowOrbitPitch: () => host.followOrbitPitch,
galaxyAnchor: host.galaxyAnchor,
systemAnchor: host.systemAnchor,
galaxyCamera: host.galaxyLayer.camera,
systemCamera: host.systemLayer.camera,
shipVisuals: host.systemLayer.shipVisuals,
nodeVisuals: host.systemLayer.nodeVisuals,
planetVisuals: host.systemLayer.planetVisuals,
systemVisuals: host.galaxyLayer.systemVisuals,
followCameraPosition: host.followCameraPosition,
followCameraFocus: host.followCameraFocus,
followCameraDirection: host.followCameraDirection,
followCameraDesiredDirection: host.followCameraDesiredDirection,
followCameraOffset: host.followCameraOffset,
createWorldPresentationContext: () => host.createWorldPresentationContext(),
updatePanels: () => host.updatePanels(),
updateGamePanel: (mode: string) => host.updateGamePanel(mode),
});
const presentationController = new ViewerPresentationController({
renderer: host.renderer,
hudState: host.hudState,
galaxyScene: host.galaxyLayer.scene,
galaxyCamera: host.galaxyLayer.camera,
systemCamera: host.systemLayer.camera,
galaxyAnchor: host.galaxyAnchor,
systemAnchor: host.systemAnchor,
ambienceGroup: host.universeLayer.ambienceGroup,
networkStats: host.networkStats,
performanceStats: host.performanceStats,
getWorld: () => host.world,
getActiveSystemId: () => host.activeSystemId,
getCameraMode: () => host.cameraMode,
getCameraTargetShipId: () => host.cameraTargetShipId,
getPovLevel: () => host.povLevel,
getSelectedItems: () => host.selectedItems,
getWorldTimeSyncMs: () => host.worldTimeSyncMs,
getCurrentDistance: () => host.currentDistance,
planetVisuals: host.systemLayer.planetVisuals,
systemVisuals: host.galaxyLayer.systemVisuals,
createWorldPresentationContext: () => host.createWorldPresentationContext(),
});
const worldLifecycle = new ViewerWorldLifecycle({
getWorld: () => host.world,
setWorld: (world) => {
host.world = world;
},
getWorldTimeSyncMs: () => host.worldTimeSyncMs,
setWorldTimeSyncMs: (value) => {
host.worldTimeSyncMs = value;
},
getWorldSignature: () => host.worldSignature,
setWorldSignature: (value) => {
host.worldSignature = value;
},
getStream: () => host.stream,
setStream: (stream) => {
host.stream = stream;
},
getCurrentStreamScopeKey: () => host.currentStreamScopeKey,
setCurrentStreamScopeKey: (value) => {
host.currentStreamScopeKey = value;
},
getPovLevel: () => host.povLevel,
getActiveSystemId: () => host.activeSystemId,
getSelectedItems: () => host.selectedItems,
getCameraMode: () => host.cameraMode,
getCameraTargetShipId: () => host.cameraTargetShipId,
getNetworkStats: () => host.networkStats,
getSystemSummaryVisuals: () => new Map(),
hudState: host.hudState,
worldLabel: () => host.world?.label ?? "",
rebuildSystems: (systems) => sceneDataController.rebuildSystems(systems),
syncCelestials: (celestials) => sceneDataController.syncCelestials(celestials),
syncNodes: (nodes) => sceneDataController.syncNodes(nodes),
syncStations: (stations) => sceneDataController.syncStations(stations),
syncClaims: (claims) => sceneDataController.syncClaims(claims),
syncConstructionSites: (sites) => sceneDataController.syncConstructionSites(sites),
syncShips: (ships, tickIntervalMs) => sceneDataController.syncShips(ships, tickIntervalMs),
applyCelestialDeltas: (celestials) => sceneDataController.applyCelestialDeltas(celestials),
applyNodeDeltas: (nodes) => sceneDataController.applyNodeDeltas(nodes),
applyStationDeltas: (stations) => sceneDataController.applyStationDeltas(stations),
applyClaimDeltas: (claims) => sceneDataController.applyClaimDeltas(claims),
applyConstructionSiteDeltas: (sites) => sceneDataController.applyConstructionSiteDeltas(sites),
applyShipDeltas: (ships, tickIntervalMs) => sceneDataController.applyShipDeltas(ships, tickIntervalMs),
refreshHistoryWindows: () => host.refreshHistoryWindows(),
resolveFocusedCelestialId: () => host.resolveFocusedCelestialId(),
updateSystemSummaries: () => host.updateSystemSummaries(),
applyZoomPresentation: () => presentationController.applyZoomPresentation(),
updateNetworkPanel: () => presentationController.updateNetworkPanel(),
updateSystemPanel: () => host.updateSystemPanel(),
updateGamePanel: (mode) => host.updateGamePanel(mode),
describeSelectionParent: (selection) => host.describeSelectionParent(selection),
});
const historyController = new ViewerHistoryWindowController({
historyWindows: host.historyWindows,
getWorld: () => host.world,
getHistoryWindowCounter: () => host.historyWindowCounter,
setHistoryWindowCounter: (value) => {
host.historyWindowCounter = value;
},
getHistoryWindowZCounter: () => host.historyWindowZCounter,
setHistoryWindowZCounter: (value) => {
host.historyWindowZCounter = value;
},
getHistoryWindowDragId: () => host.historyWindowDragId,
setHistoryWindowDragId: (value) => {
host.historyWindowDragId = value;
},
getHistoryWindowDragPointerId: () => host.historyWindowDragPointerId,
setHistoryWindowDragPointerId: (value) => {
host.historyWindowDragPointerId = value;
},
historyWindowDragOffset: host.historyWindowDragOffset,
renderRecentEvents: (entityKind, entityId) => presentationController.renderRecentEvents(entityKind, entityId),
});
const interactionController = new ViewerInteractionController({
renderer: host.renderer,
raycaster: host.raycaster,
mouse: host.mouse,
galaxyCamera: host.galaxyLayer.camera,
systemCamera: host.systemLayer.camera,
galaxySelectableTargets: host.galaxyLayer.selectableTargets,
systemSelectableTargets: host.systemLayer.selectableTargets,
hoverLabelEl: host.hoverLabelEl,
hoverConnectorLineEl: host.hoverConnectorLineEl,
marqueeEl: host.marqueeEl,
hudState: host.hudState,
keyState: host.keyState,
getWorld: () => host.world,
getActiveSystemId: () => host.activeSystemId,
getPovLevel: () => host.povLevel,
getSelectedItems: () => host.selectedItems,
setSelectedItems: (items) => {
host.applySelectedItems(items, "viewer");
},
getDragMode: () => host.dragMode,
setDragMode: (mode) => {
host.dragMode = mode;
},
getDragPointerId: () => host.dragPointerId,
setDragPointerId: (pointerId) => {
host.dragPointerId = pointerId;
},
dragStart: host.dragStart,
dragLast: host.dragLast,
getMarqueeActive: () => host.marqueeActive,
setMarqueeActive: (value) => {
host.marqueeActive = value;
},
getSuppressClickSelection: () => host.suppressClickSelection,
setSuppressClickSelection: (value) => {
host.suppressClickSelection = value;
},
getDesiredDistance: () => host.desiredDistance,
setDesiredDistance: (value) => {
host.desiredDistance = value;
},
getCameraMode: () => host.cameraMode,
setCameraMode: (value) => {
host.cameraMode = value;
},
getCameraTargetShipId: () => host.cameraTargetShipId,
setCameraTargetShipId: (value) => {
host.cameraTargetShipId = value;
},
getFollowCameraPosition: () => host.followCameraPosition,
getFollowCameraFocus: () => host.followCameraFocus,
screenPointFromClient: (x, y) => presentationController.screenPointFromClient(x, y),
applyOrbitDelta: (delta: THREE.Vector2) => {
if (host.cameraMode === "follow") {
host.followOrbitYaw += delta.x * 0.008;
host.followOrbitPitch = THREE.MathUtils.clamp(host.followOrbitPitch + delta.y * 0.004, 0.02, 1.45);
} else {
host.orbitYaw += delta.x * 0.008;
host.orbitPitch = THREE.MathUtils.clamp(host.orbitPitch + delta.y * 0.004, 0.18, 1.3);
}
},
syncFollowStateFromSelection: () => navigationController.syncFollowStateFromSelection(),
updatePanels: () => host.updatePanels(),
focusOnSelection: (selection) => navigationController.focusOnSelection(selection),
updateGamePanel: (mode) => host.updateGamePanel(mode),
openOrderContextMenu: (x, y, target) => orderContextMenuStore.open(x, y, target),
closeOrderContextMenu: () => orderContextMenuStore.close(),
historyController,
});
return {
historyController,
sceneDataController,
navigationController,
presentationController,
worldLifecycle,
interactionController,
};
}
export function wireViewerEvents(host: any) {
const canvas = host.renderer.domElement;
canvas.addEventListener("pointerdown", host.interactionController.onPointerDown);
canvas.addEventListener("pointermove", host.interactionController.onPointerMove);
canvas.addEventListener("pointerup", host.interactionController.onPointerUp);
canvas.addEventListener("pointerleave", host.interactionController.onPointerUp);
canvas.addEventListener("click", host.interactionController.onClick);
canvas.addEventListener("contextmenu", host.interactionController.onContextMenu);
canvas.addEventListener("dblclick", host.interactionController.onDoubleClick);
canvas.addEventListener("wheel", host.interactionController.onWheel, { passive: false });
host.historyLayerEl.addEventListener("click", host.interactionController.onHistoryLayerClick);
host.historyLayerEl.addEventListener("pointerdown", host.interactionController.onHistoryLayerPointerDown);
window.addEventListener("pointermove", host.interactionController.onHistoryWindowPointerMove);
window.addEventListener("pointerup", host.interactionController.onHistoryWindowPointerUp);
window.addEventListener("keydown", host.interactionController.onKeyDown);
window.addEventListener("keyup", host.interactionController.onKeyUp);
return () => {
canvas.removeEventListener("pointerdown", host.interactionController.onPointerDown);
canvas.removeEventListener("pointermove", host.interactionController.onPointerMove);
canvas.removeEventListener("pointerup", host.interactionController.onPointerUp);
canvas.removeEventListener("pointerleave", host.interactionController.onPointerUp);
canvas.removeEventListener("click", host.interactionController.onClick);
canvas.removeEventListener("contextmenu", host.interactionController.onContextMenu);
canvas.removeEventListener("dblclick", host.interactionController.onDoubleClick);
canvas.removeEventListener("wheel", host.interactionController.onWheel);
host.historyLayerEl.removeEventListener("click", host.interactionController.onHistoryLayerClick);
host.historyLayerEl.removeEventListener("pointerdown", host.interactionController.onHistoryLayerPointerDown);
window.removeEventListener("pointermove", host.interactionController.onHistoryWindowPointerMove);
window.removeEventListener("pointerup", host.interactionController.onHistoryWindowPointerUp);
window.removeEventListener("keydown", host.interactionController.onKeyDown);
window.removeEventListener("keyup", host.interactionController.onKeyUp);
};
}