using System; using System.Linq; using System.Collections.Generic; using RemoteTech.SimpleTypes; using RemoteTech.UI; using UnityEngine; using Debug = System.Diagnostics.Debug; namespace RemoteTech { [Flags] public enum MapFilter { None = 0, Omni = 1, Dish = 2, Sphere = 4, Cone = 8, Planet = 8, // For backward compatibility with RemoteTech 1.4 and earlier // Cone should be first, so that it's the one that appears in settings file Path = 16 } public class NetworkRenderer : MonoBehaviour { public MapFilter Filter { get { return RTSettings.Instance.MapFilter; } set { RTSettings.Instance.MapFilter = value; RTSettings.Instance.Save(); } } private static readonly Texture2D mTexMark; private readonly HashSet> mEdges = new HashSet>(); private readonly List mLines = new List(); private readonly List mCones = new List(); public bool ShowOmni { get { return (Filter & MapFilter.Omni) == MapFilter.Omni; } } public bool ShowDish { get { return (Filter & MapFilter.Dish) == MapFilter.Dish; } } public bool ShowPath { get { return (Filter & MapFilter.Path) == MapFilter.Path; } } public bool ShowRange { get { return (Filter & MapFilter.Sphere) == MapFilter.Sphere; } } public bool ShowCone { get { return (Filter & MapFilter.Cone) == MapFilter.Cone; } } public GUIStyle smallStationText; public GUIStyle smallStationHead; static NetworkRenderer() { RTUtil.LoadImage(out mTexMark, "mark"); } public static NetworkRenderer CreateAndAttach() { var renderer = MapView.MapCamera.gameObject.GetComponent(); if (renderer) { Destroy(renderer); } renderer = MapView.MapCamera.gameObject.AddComponent(); RTCore.Instance.Network.OnLinkAdd += renderer.OnLinkAdd; RTCore.Instance.Network.OnLinkRemove += renderer.OnLinkRemove; RTCore.Instance.Satellites.OnUnregister += renderer.OnSatelliteUnregister; renderer.smallStationHead = new GUIStyle(HighLogic.Skin.label) { fontSize = 12 }; renderer.smallStationText = new GUIStyle(HighLogic.Skin.label) { fontSize = 10, normal = { textColor = Color.white } }; return renderer; } public void OnPreCull() { if (MapView.MapIsEnabled) { UpdateNetworkEdges(); UpdateNetworkCones(); } } public void OnGUI() { if (Event.current.type == EventType.Repaint && MapView.MapIsEnabled) { foreach (ISatellite s in RTCore.Instance.Satellites.FindCommandStations().Concat(RTCore.Instance.Network.GroundStations.Values)) { bool showOnMapview = true; var worldPos = ScaledSpace.LocalToScaledSpace(s.Position); if (MapView.MapCamera.transform.InverseTransformPoint(worldPos).z < 0f) continue; Vector3 pos = PlanetariumCamera.Camera.WorldToScreenPoint(worldPos); var screenRect = new Rect((pos.x - 8), (Screen.height - pos.y) - 8, 16, 16); // Hide the current ISatellite if it is behind its body if (RTSettings.Instance.HideGroundStationsBehindBody && IsOccluded(s.Position, s.Body)) showOnMapview = false; if (RTSettings.Instance.HideGroundStationsOnDistance && !IsOccluded(s.Position, s.Body) && this.IsCamDistanceToWide(s.Position)) showOnMapview = false; // orbiting remote stations are always shown if(s.isVessel && !s.parentVessel.Landed) showOnMapview = true; if (showOnMapview) { Color pushColor = GUI.color; // tint the white mark.png into the defined color GUI.color = s.MarkColor; // draw the mark.png GUI.DrawTexture(screenRect, mTexMark, ScaleMode.ScaleToFit, true); GUI.color = pushColor; // Show Mouse over informations to the ground station if (RTSettings.Instance.ShowMouseOverInfoGroundStations && s is MissionControlSatellite && screenRect.ContainsMouse()) { Rect headline = screenRect; Vector2 nameDim = this.smallStationHead.CalcSize(new GUIContent(s.Name)); headline.x -= nameDim.x + 10; headline.y -= 3; headline.width = nameDim.x; headline.height = 14; // draw headline of the station GUI.Label(headline, s.Name, this.smallStationHead); // loop antennas String antennaRanges = String.Empty; foreach (var antenna in s.Antennas) { if(antenna.Omni > 0) { antennaRanges += "Omni: "+ RTUtil.FormatSI(antenna.Omni,"m") + Environment.NewLine; } if (antenna.Dish > 0) { antennaRanges += "Dish: " + RTUtil.FormatSI(antenna.Dish, "m") + Environment.NewLine; } } if(!antennaRanges.Equals(String.Empty)) { Rect antennas = screenRect; GUIContent content = new GUIContent(antennaRanges); Vector2 antennaDim = this.smallStationText.CalcSize(content); float maxHeight = this.smallStationText.CalcHeight(content, antennaDim.x); antennas.y += headline.height - 3; antennas.x -= antennaDim.x + 10; antennas.width = antennaDim.x; antennas.height = maxHeight; // draw antenna infos of the station GUI.Label(antennas, antennaRanges, this.smallStationText); } } } } } } ///

/// Checks whether the location is behind the body /// Original code by regex from https://github.com/NathanKell/RealSolarSystem/blob/master/Source/KSCSwitcher.cs /// private bool IsOccluded(Vector3d loc, CelestialBody body) { Vector3d camPos = ScaledSpace.ScaledToLocalSpace(PlanetariumCamera.Camera.transform.position); if (Vector3d.Angle(camPos - loc, body.position - loc) > 90) { return false; } return true; } /// /// Calculates the distance between the camera position and the ground station, and /// returns true if the distance is >= DistanceToHideGroundStations from the settings file. /// /// Position of the ground station /// True if the distance is to wide, otherwise false private bool IsCamDistanceToWide(Vector3d loc) { Vector3d camPos = ScaledSpace.ScaledToLocalSpace(PlanetariumCamera.Camera.transform.position); float distance = Vector3.Distance(camPos, loc); // distance to wide? if(distance >= RTSettings.Instance.DistanceToHideGroundStations) return true; return false; } private void UpdateNetworkCones() { List antennas = (ShowCone ? RTCore.Instance.Antennas.Where( ant => ant.Powered && ant.CanTarget && RTCore.Instance.Satellites[ant.Guid] != null && ant.Target != Guid.Empty) : Enumerable.Empty()).ToList(); int oldLength = mCones.Count; int newLength = antennas.Count; // Free any unused lines for (int i = newLength; i < oldLength; i++) { GameObject.Destroy(mCones[i]); mCones[i] = null; } mCones.RemoveRange(Math.Min(oldLength, newLength), Math.Max(oldLength - newLength, 0)); mCones.AddRange(Enumerable.Repeat((NetworkCone) null, Math.Max(newLength - oldLength, 0))); for (int i = 0; i < newLength; i++) { var center = RTCore.Instance.Network.GetPositionFromGuid(antennas[i].Target); Debug.Assert(center != null, "center != null", String.Format("GetPositionFromGuid returned a null value for the target {0}", antennas[i].Target) ); if (!center.HasValue) continue; mCones[i] = mCones[i] ?? NetworkCone.Instantiate(); mCones[i].Material = MapView.fetch.orbitLinesMaterial; mCones[i].LineWidth = 2.0f; mCones[i].Antenna = antennas[i]; mCones[i].Color = Color.gray; mCones[i].Active = ShowCone; mCones[i].Center = center.Value; } } private void UpdateNetworkEdges() { var edges = mEdges.Where(CheckVisibility).ToList(); int oldLength = mLines.Count; int newLength = edges.Count; // Free any unused lines for (int i = newLength; i < oldLength; i++) { Destroy(mLines[i]); mLines[i] = null; } mLines.RemoveRange(Math.Min(oldLength, newLength), Math.Max(oldLength - newLength, 0)); mLines.AddRange(Enumerable.Repeat(null, Math.Max(newLength - oldLength, 0))); // Iterate over all satellites, updating or creating new lines. var it = edges.GetEnumerator(); for (int i = 0; i < newLength; i++) { it.MoveNext(); mLines[i] = mLines[i] ?? NetworkLine.Instantiate(); mLines[i].Material = MapView.fetch.orbitLinesMaterial; mLines[i].LineWidth = 3.0f; mLines[i].Edge = it.Current; mLines[i].Color = CheckColor(it.Current); mLines[i].Active = true; } } private bool CheckVisibility(BidirectionalEdge edge) { var vessel = PlanetariumCamera.fetch.target.vessel; var satellite = RTCore.Instance.Satellites[vessel]; if (satellite != null && ShowPath) { var connections = RTCore.Instance.Network[satellite]; if (connections.Any() && connections[0].Contains(edge)) return true; } if (edge.Type == LinkType.Omni && !ShowOmni) return false; if (edge.Type == LinkType.Dish && !ShowDish) return false; if (!edge.A.Visible || !edge.B.Visible) return false; return true; } private Color CheckColor(BidirectionalEdge edge) { var vessel = PlanetariumCamera.fetch.target.vessel; var satellite = RTCore.Instance.Satellites[vessel]; if (satellite != null && ShowPath) { var connections = RTCore.Instance.Network[satellite]; if (connections.Any() && connections[0].Contains(edge)) return RTSettings.Instance.ActiveConnectionColor; } if (edge.Type == LinkType.Omni) return RTSettings.Instance.OmniConnectionColor; if (edge.Type == LinkType.Dish) return RTSettings.Instance.DishConnectionColor; return XKCDColors.Grey; } private void OnSatelliteUnregister(ISatellite s) { mEdges.RemoveWhere(e => e.A == s || e.B == s); } private void OnLinkAdd(ISatellite a, NetworkLink link) { mEdges.Add(new BidirectionalEdge(a, link.Target, link.Port)); } private void OnLinkRemove(ISatellite a, NetworkLink link) { mEdges.Remove(new BidirectionalEdge(a, link.Target, link.Port)); } public void Detach() { for (int i = 0; i < mLines.Count; i++) { GameObject.DestroyImmediate(mLines[i]); } mLines.Clear(); for (int i = 0; i < mCones.Count; i++) { GameObject.DestroyImmediate(mCones[i]); } mCones.Clear(); DestroyImmediate(this); } public void OnDestroy() { RTCore.Instance.Network.OnLinkAdd -= OnLinkAdd; RTCore.Instance.Network.OnLinkRemove -= OnLinkRemove; RTCore.Instance.Satellites.OnUnregister -= OnSatelliteUnregister; } } }