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); }; }