using System; using System.Collections; using System.Collections.Generic; using System.Globalization; using System.Linq; using GameCult.Aetheria.EveRuntime; using GameCult.Aetheria.State.Verse; using GameCult.Eve.Surface; using UniRx; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; using UnityEngine.UIElements; using Unity.Mathematics; using static Unity.Mathematics.math; using float2 = Unity.Mathematics.float2; public class SectorRenderer : MonoBehaviour, IBeginDragHandler, IDragHandler, IScrollHandler { public ClickRaycaster Raycaster; public Canvas Canvas; public SectorMap Map; public Camera SectorCamera; public MeshRenderer SectorBackgroundRenderer; public float ZoomSpeed; public float MinViewSize = .1f; public float MaxViewSize = 2; // public float PathAnimationDamping = .01f; // public float PathAnimationDuration = 30; // public float PathAnimationDurationPadding = 1.1f; public GameObject LegendPanel; public float LinkAnimationDuration; public float IconAnimationDuration; private float2 _startMousePosition; private float2 _startMapPosition; private Transform _sectorBackgroundTransform; private Transform _sectorCameraTransform; private UnityEngine.UI.Image _outputImage; private RenderTexture _outputTexture; private int2 _size; private bool _init; private float _aspectRatio; private float _sectorBackgroundDepth; private float _sectorCameraDepth; private UIDocument _zoneDetailsSurfaceDocument; private readonly AetheriaEveUnitySurfaceChrome _zoneDetailsSurfaceChrome = new AetheriaEveUnitySurfaceChrome { RootAlignItems = Align.FlexEnd, RootPaddingTop = 24f, RootPaddingRight = 24f, Width = 360f, MinWidth = 0f, MaxWidth = 420f, PaddingLeft = 18f, PaddingRight = 18f, PaddingTop = 18f, PaddingBottom = 18f }; private float2 _position = float2(0.5f); private float _viewSize = .5f; void Start() { _outputImage = GetComponent(); _sectorBackgroundTransform = SectorBackgroundRenderer.transform; _sectorBackgroundDepth = _sectorBackgroundTransform.position.z; _sectorCameraTransform = SectorCamera.transform; _sectorCameraDepth = _sectorCameraTransform.position.z; Raycaster.OnClickMiss += data => { HideZoneDetailsSurface(); }; Map.ZoneClicked.Subscribe(zone => { RenderZoneDetailsSurface(zone); }); // PathAnimationButton.onClick.AddListener(() => // { // Map.StartCoroutine(AnimatePath()); // }); } private void RenderZoneDetailsSurface(int zoneIndex) { var sectorZone = ResolveSectorZone(zoneIndex); var zoneDetails = ResolveZoneDetails(zoneIndex); var ownerFactionIndex = sectorZone?.OwnerFactionIndex ?? -1; var otherFactions = (sectorZone?.FactionIndices ?? Array.Empty()) .Where(index => index >= 0 && index != ownerFactionIndex) .Distinct() .Select(FormatFaction) .ToArray(); var zoneFacts = AetheriaRuntimeZoneDetailsSurfaceBuilder.Facts( zoneDetails, ResolveHullType); _zoneDetailsSurfaceDocument = AetheriaEveUnitySurfaceHost.RenderRuntime( transform, _zoneDetailsSurfaceDocument, "Aetheria Sector Zone Details Surface", AetheriaRuntimeZoneDetailsSurfaceBuilder.Build( ResolveZoneName(sectorZone, zoneDetails), ownerFactionIndex >= 0 ? FormatFaction(ownerFactionIndex) : "None", FormatValue((float)zoneFacts.Mass), FormatValue((float)zoneFacts.Radius), otherFactions, zoneFacts.Bodies, zoneFacts.Entities, zoneFacts.HasContents, DateTime.UtcNow.ToString("O")), HandleZoneDetailsSurfaceCommand, _zoneDetailsSurfaceChrome); } private void HandleZoneDetailsSurfaceCommand(EveSurfaceCommandRequest request) { if (!AetheriaRuntimeZoneDetailsSurfaceCommands.TryRead(request, out var command)) { Debug.LogWarning($"Unknown sector-map zone details command: {request?.Command}"); return; } if (command.Kind == AetheriaRuntimeZoneDetailsCommandKind.Close) { HideZoneDetailsSurface(); return; } Debug.LogWarning($"Unknown sector-map zone details command: {request?.Command}"); } private void HideZoneDetailsSurface() { if (_zoneDetailsSurfaceDocument == null) return; AetheriaEveUnitySurfaceHost.Hide(_zoneDetailsSurfaceDocument); } private string ResolveZoneName( AetheriaRuntimeSectorMapZone sectorZone, AetheriaRuntimeZoneDetailsDocument zoneDetails) { if (!string.IsNullOrWhiteSpace(zoneDetails?.ZoneName)) return zoneDetails.ZoneName; if (!string.IsNullOrWhiteSpace(sectorZone?.Name)) return sectorZone.Name; return sectorZone == null ? "Unknown" : $"Zone {sectorZone.ZoneIndex}"; } private AetheriaRuntimeSectorMapZone ResolveSectorZone(int zoneIndex) { if (zoneIndex < 0) return null; try { var sectorMap = AetheriaUnityRuntimeClientProvider .RuntimeState("unity-sector-renderer") .CurrentSectorMap(); return (sectorMap?.Zones ?? Array.Empty()) .FirstOrDefault(zone => zone.ZoneIndex == zoneIndex); } catch (Exception ex) { Debug.LogWarning($"Failed to read Aetheria sector map zone from local Verse state: {ex.Message}"); return null; } } private static string FormatFaction(int factionIndex) { return factionIndex < 0 ? "None" : $"Faction {factionIndex}"; } private AetheriaRuntimeZoneDetailsDocument ResolveZoneDetails(int zoneIndex) { if (zoneIndex < 0) return null; try { return AetheriaUnityRuntimeClientProvider .RuntimeState("unity-sector-renderer") .CurrentZoneDetails(zoneIndex); } catch (Exception ex) { Debug.LogWarning($"Failed to read Aetheria sector zone details from local Verse state: {ex.Message}"); return null; } } private string ResolveHullType(string hullItemKey) { var typedHull = ResolveCatalog()?.FindItem(hullItemKey ?? ""); return typedHull?.HullType ?? ""; } private AetheriaRuntimeCatalogSnapshot ResolveCatalog() { try { return AetheriaUnityRuntimeClientProvider .RuntimeState("unity-sector-renderer") .CurrentCatalog(); } catch (Exception ex) { Debug.LogWarning($"Failed to read Aetheria sector catalog from local Verse state: {ex.Message}"); return null; } } private AetheriaRuntimePlayerSettingsDocument ResolvePlayerSettings() { try { return AetheriaUnityRuntimeClientProvider .RuntimeState("unity-sector-renderer") .CurrentPlayerSettings(); } catch (Exception ex) { Debug.LogWarning($"Failed to read Aetheria sector player settings from local Verse state: {ex.Message}"); return null; } } private string FormatValue(float value) { var digits = ResolvePlayerSettings()?.SignificantDigits ?? 3; var magnitude = value == 0f ? 0 : (int)Math.Floor(Math.Log10(Math.Abs(value))) + 1; digits -= magnitude; if (digits < 0) digits = 0; var formatted = value.ToString($"N{digits}"); var decimalSeparator = Convert.ToChar(CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator); return formatted.Contains(CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator) ? formatted.TrimEnd('0').TrimEnd(decimalSeparator) : formatted; } private void OnEnable() { _init = true; SectorCamera.gameObject.SetActive(true); var currentZone = ResolveCurrentZone(); _position = currentZone == null ? float2.zero : new float2((float)currentZone.PositionX, (float)currentZone.PositionY); _viewSize = .25f; Map.StartReveal(LinkAnimationDuration, IconAnimationDuration); if (currentZone != null) Map.TryMarkPlayerLocation(currentZone.ZoneIndex); } private AetheriaRuntimeCurrentZoneDocument ResolveCurrentZone() { try { return AetheriaUnityRuntimeClientProvider .RuntimeState("unity-sector-renderer") .CurrentZoneState(); } catch (Exception ex) { Debug.LogWarning($"Failed to read Aetheria sector current zone from local Verse state: {ex.Message}"); return null; } } private void OnDisable() { HideZoneDetailsSurface(); if (_outputTexture != null) { _outputTexture.Release(); _outputTexture = null; } SectorCamera.gameObject.SetActive(false); } private void OnDestroy() { if (_zoneDetailsSurfaceDocument != null) { AetheriaEveUnitySurfaceHost.DestroyDocument(_zoneDetailsSurfaceDocument); _zoneDetailsSurfaceDocument = null; } } void LateUpdate() { var size = int2(Screen.width, Screen.height); if (_init || size.x != _size.x || size.y != _size.y) { _aspectRatio = (float) size.x / size.y; _size = size; if (_outputTexture != null) { _outputTexture.Release(); } _outputTexture = new RenderTexture(_size.x, _size.y, 0, RenderTextureFormat.Default); SectorCamera.targetTexture = _outputTexture; _outputImage.material.SetTexture("_DetailTex", _outputTexture); } UpdateCamera(); } void UpdateCamera() { var halfSize = _viewSize / 2; var bounds = float4( _position.x - _aspectRatio * halfSize, _position.y - halfSize, _position.x + _aspectRatio * halfSize, _position.y + halfSize); _sectorBackgroundTransform.position = new Vector3(_position.x, _position.y, _sectorBackgroundDepth); _sectorBackgroundTransform.localScale = new Vector3(_aspectRatio * _viewSize, _viewSize); _sectorCameraTransform.position = new Vector3(_position.x, _position.y, _sectorCameraDepth); SectorCamera.orthographicSize = halfSize; SectorBackgroundRenderer.material.SetVector("Extents", bounds); } public void OnBeginDrag(PointerEventData eventData) { _startMousePosition = eventData.position; _startMapPosition = _position; } public void OnDrag(PointerEventData eventData) { _position = _startMapPosition - ((float2)eventData.position - _startMousePosition) / _size.y * _viewSize; } public void OnScroll(PointerEventData eventData) { var mapCenter = float2((float)Screen.width / 2, (float)Screen.height / 2); var oldPointerPosition = _position + ((float2)eventData.position - mapCenter) / Screen.height * _viewSize; _viewSize = clamp(_viewSize * (1 - eventData.scrollDelta.y * ZoomSpeed), MinViewSize, MaxViewSize); var pointerPosition = _position + ((float2)eventData.position - mapCenter) / Screen.height * _viewSize; _position += oldPointerPosition - pointerPosition; } }