/// <summary> /// Gets the potential link cost, in EC/MiT. /// </summary> /// <returns>The potential link cost, in EC/MiT.</returns> /// <param name="potentialTarget">Potential target relay.</param> public RelayDataCost GetPotentialLinkCost(IAntennaRelay potentialTarget) { if (potentialTarget == null) { return(RelayDataCost.Infinity); } double currentSqrDistance = this.SqrDistanceTo(potentialTarget); if (currentSqrDistance > this.MaxLinkSqrDistanceTo(potentialTarget)) { return(RelayDataCost.Infinity); } double nominalSqrDistance; if (ARConfiguration.UseAdditiveRanges) { nominalSqrDistance = this.nominalTransmitDistance * potentialTarget.nominalTransmitDistance; } else { nominalSqrDistance = this.nominalTransmitDistance * this.nominalTransmitDistance; } return(GetPotentialLinkCost(currentSqrDistance, nominalSqrDistance)); }
/// <summary> /// Initializes a new instance of the <see cref="AntennaRange.AntennaRelay"/> class. /// </summary> /// <param name="prefabRelay">The module reference underlying this AntennaRelay, /// as an <see cref="AntennaRange.IAntennaRelay"/></param> /// <param name="pps">The prototype partreference on which the module resides.</param> public ProtoAntennaRelay(IAntennaRelay prefabRelay, ProtoPartSnapshot pps) : base(prefabRelay) { this.protoPart = pps; this.Log("constructed ({0})", this.GetType().Name); this.RecalculateMaxRange(); }
/// <summary> /// Determines if relayOne is in range of the specified relayTwo. /// </summary> /// <returns><c>true</c> if relayOne is in range of the specifie relayTwo; otherwise, <c>false</c>.</returns> /// <param name="relayOne">Relay one.</param> /// <param name="relayTwo">Relay two.</param> public static bool IsInRangeOf(this AntennaRelay relayOne, IAntennaRelay relayTwo) { if (relayOne == null || relayTwo == null) { return(false); } return(relayOne.SqrDistanceTo(relayTwo) <= relayOne.MaxLinkSqrDistanceTo(relayTwo)); }
/// <summary> /// Initializes a new instance of the <see cref="AntennaRange.AntennaRelay"/> class. /// </summary> /// <param name="module">The module reference underlying this AntennaRelay, /// as an <see cref="AntennaRange.IAntennaRelay"/></param> public AntennaRelay(IAntennaRelay module) { this.KerbinDirect = true; this.moduleRef = module; #if BENCH AntennaRelay.relayCount++; #endif this.LogDebug("{0}: constructed {1}", this.GetType().Name, this.ToString()); }
/// <summary> /// Returns the square of the maximum link range between two relays. /// </summary> /// <returns>The maximum link range between two relays.</returns> /// <param name="relayOne">Relay one.</param> /// <param name="relayTwo">Relay two.</param> public static double MaxLinkSqrDistanceTo(this AntennaRelay relayOne, IAntennaRelay relayTwo) { if (ARConfiguration.UseAdditiveRanges) { return(relayOne.maxTransmitDistance * relayTwo.maxTransmitDistance); } else { return(relayOne.maxTransmitDistance * relayOne.maxTransmitDistance); } }
/// <summary> /// Finds the nearest relay. /// </summary> /// <returns>The nearest relay or null, if no relays in range.</returns> public void FindNearestRelay() { if (!FlightGlobals.ready) { return; } PooledDebugLogger log; #if DEBUG log = PooledDebugLogger.New(this); #endif #if BENCH this.performanceTimer.Restart(); long startVesselLoopTicks; long totalVesselLoopTicks; string slowestLOSVesselName = string.Empty; long slowestLOSVesselTicks = long.MinValue; long startLOSVesselTicks; long totalLOSVesselTicks; string slowestCircularVesselName = string.Empty; long slowestCircularVesselTicks = long.MinValue; long startCircularVesselTicks; long totalCircularVesselTicks; long startKerbinLOSTicks; long totalKerbinLOSTicks; long statusResolutionTicks; ushort usefulVesselCount = 0; #endif log.AppendFormat("{0}: Target search started).", this.ToString()); #if DEBUG try { #endif // Declare a bunch of variables we'll be using. CelestialBody bodyOccludingBestOccludedRelay = null; IAntennaRelay needle; RelayDataCost cheapestRelayRate = RelayDataCost.Infinity; RelayDataCost cheapestOccludedRelayRate = RelayDataCost.Infinity; RelayDataCost potentialRelayRate; RelayDataCost kerbinRelayRate = this.GetPotentialLinkCost(Kerbin); bool isCircular; int iterCount; // Blank everything we're trying to find before the search. this.firstOccludingBody = null; this.bestOccludedRelay = null; this.targetRelay = null; this.nearestRelay = null; // Default to KerbinDirect = true in case something in here doesn't work right. this.KerbinDirect = true; /* * Loop through the useful relays as determined by ARFlightController and check each for line of sight and * distance, searching for the relay with the best distance/maxRange ratio that is in sight, in range, and * can transmit, also stashing the "best" relay outside of line of sight for failure report. * */ IAntennaRelay potentialBestRelay; CelestialBody fob; #if BENCH startVesselLoopTicks = performanceTimer.ElapsedTicks; #endif for (int rIdx = 0; rIdx < ARFlightController.UsefulRelays.Count; rIdx++) { potentialBestRelay = ARFlightController.UsefulRelays[rIdx]; log.AppendFormat("\n\tgot useful relay {0}", potentialBestRelay == null ? "null" : potentialBestRelay.ToString()); if (potentialBestRelay == null) { log.Append("\n\t...skipping null relay"); continue; } if (potentialBestRelay == this || potentialBestRelay.vessel == this.vessel) { log.AppendFormat( "\n\t...skipping relay {0} because it or its vessel ({1}) is the same as ours" + "\n\t\t(our vessel is {2})", potentialBestRelay, potentialBestRelay.vessel == null ? "null" : potentialBestRelay.vessel.vesselName, this.vessel == null ? "null" : this.vessel.vesselName ); continue; } #if BENCH usefulVesselCount++; #endif // Find the distance from here to the vessel... log.Append("\n\tgetting cost to potential vessel"); potentialRelayRate = potentialBestRelay.CurrentNetworkLinkCost + this.GetPotentialLinkCost(potentialBestRelay); log.AppendFormat( "\n\tpotentialRelayRate = {0} ({1} + {2})", potentialRelayRate, potentialBestRelay.CurrentNetworkLinkCost, this.GetPotentialLinkCost(potentialBestRelay) ); #if BENCH startLOSVesselTicks = performanceTimer.ElapsedTicks; #endif log.Append("\n\t\tdoing LOS check"); // Skip vessels to which we do not have line of sight. if ( ARConfiguration.RequireLineOfSight && !this.vessel.hasLineOfSightTo(potentialBestRelay.vessel, out fob, ARConfiguration.RadiusRatio) ) { #if BENCH totalLOSVesselTicks = performanceTimer.ElapsedTicks - startLOSVesselTicks; if (totalLOSVesselTicks > slowestLOSVesselTicks) { slowestLOSVesselTicks = totalLOSVesselTicks; slowestLOSVesselName = vessel.vesselName; } #endif log.Append("\n\t\t...failed LOS check"); log.AppendFormat("\n\t\t\t{0}: Relay {1} not in line of sight.", this.ToString(), potentialBestRelay); log.AppendFormat("\n\t\t\tpotentialRelayRate: {0}", potentialRelayRate); log.AppendFormat("\n\t\t\tcheapestOccludedRelayRate: {0}", cheapestOccludedRelayRate); if ( (potentialRelayRate < cheapestRelayRate) && this.IsInRangeOf(potentialBestRelay) && potentialBestRelay.CanTransmit() ) { log.Append("\n\t\t...vessel is cheapest and in range and potentialBestRelay can transmit"); log.AppendFormat("\n\t\t...{0} found new best occluded relay {1}", this, potentialBestRelay); this.bestOccludedRelay = potentialBestRelay; bodyOccludingBestOccludedRelay = fob; cheapestOccludedRelayRate = potentialRelayRate; } else { log.Append("\n\t\t...vessel is not close enough to check for occluded relays, carrying on"); } continue; } #if BENCH else { totalLOSVesselTicks = performanceTimer.ElapsedTicks - startLOSVesselTicks; } if (totalLOSVesselTicks > slowestLOSVesselTicks) { slowestLOSVesselTicks = totalLOSVesselTicks; slowestLOSVesselName = vessel.vesselName; } #endif log.Append("\n\t\t...passed LOS check"); /* * ...so that we can skip the vessel if it is further away than a vessel we've already checked. * */ if (potentialRelayRate > cheapestRelayRate) { log.AppendFormat( "\n\t{0}: Relay {1} discarded because it is more expensive than the cheapest relay." + "\n\t\t({2}, {3} > {4})", this.ToString(), potentialBestRelay, this.nearestRelay == null ? "NULL" : this.nearestRelay.ToString(), potentialRelayRate, cheapestRelayRate ); continue; } log.Append("\n\t\t...passed distance check"); if (potentialBestRelay.CanTransmit()) { #if BENCH startCircularVesselTicks = performanceTimer.ElapsedTicks; #endif needle = potentialBestRelay; isCircular = false; iterCount = 0; while (needle != null) { iterCount++; if (needle.KerbinDirect) { break; } if (needle.targetRelay == null) { break; } if (needle.targetRelay.vessel == this.vessel || needle == this.moduleRef) { isCircular = true; break; } // Avoid infinite loops when we're not catching things right. if (iterCount > FlightGlobals.Vessels.Count) { this.LogError( "iterCount exceeded while checking for circular network; assuming it is circular" + "\n\tneedle={0}" + "\n\tthis.moduleRef={1}", needle == null ? "null" : string.Format( "{0}, needle.KerbinDirect={1}, needle.targetRelay={2}", needle, needle.KerbinDirect, needle.targetRelay == null ? "null" : string.Format( "{0}\n\tneedle.targetRelay.vessel={1}", needle.targetRelay, needle.targetRelay.vessel == null ? "null" : needle.targetRelay.vessel.vesselName ) ), this.moduleRef == null ? "null" : this.moduleRef.ToString() ); isCircular = true; break; } needle = needle.targetRelay; } if (!isCircular) { cheapestRelayRate = potentialRelayRate; this.nearestRelay = potentialBestRelay; log.AppendFormat("\n\t{0}: found new cheapest relay {1} ({2} EC/MiT)", this.ToString(), this.nearestRelay.ToString(), cheapestRelayRate ); } else { log.AppendFormat("\n\t\t...connection to {0} would result in a circular network, skipping", potentialBestRelay ); } #if BENCH totalCircularVesselTicks = performanceTimer.ElapsedTicks - startCircularVesselTicks; if (totalCircularVesselTicks > slowestCircularVesselTicks) { slowestCircularVesselName = vessel.vesselName; slowestCircularVesselTicks = totalCircularVesselTicks; } #endif } } #if BENCH totalVesselLoopTicks = performanceTimer.ElapsedTicks - startVesselLoopTicks; #endif CelestialBody bodyOccludingKerbin = null; log.AppendFormat("\n{0} ({1}): Search done, figuring status.", this.ToString(), this.GetType().Name); log.AppendFormat( "\n{0}: nearestRelay={1} ({2})), bestOccludedRelay={3} ({4}), kerbinRelayRate={5} EC/MiT)", this, this.nearestRelay == null ? "null" : this.nearestRelay.ToString(), cheapestRelayRate, this.bestOccludedRelay == null ? "null" : this.bestOccludedRelay.ToString(), cheapestOccludedRelayRate, kerbinRelayRate ); #if BENCH startKerbinLOSTicks = this.performanceTimer.ElapsedTicks; #endif // If we don't have LOS to Kerbin, focus on relays if ( ARConfiguration.RequireLineOfSight && !this.vessel.hasLineOfSightTo(Kerbin, out bodyOccludingKerbin, ARConfiguration.RadiusRatio) ) { #if BENCH totalKerbinLOSTicks = this.performanceTimer.ElapsedTicks - startKerbinLOSTicks; #endif log.AppendFormat("\n\tKerbin LOS is blocked by {0}.", bodyOccludingKerbin.bodyName); // If we're in range of the "nearest" (actually cheapest) relay, use it. if (this.IsInRangeOf(this.nearestRelay)) { log.AppendFormat("\n\t\tCan transmit to nearby relay {0}).", this.nearestRelay == null ? "null" : this.nearestRelay.ToString() ); this.KerbinDirect = false; this.canTransmit = true; this.targetRelay = this.nearestRelay; } // If this isn't true, we can't transmit, but pick a second best of bestOccludedRelay and Kerbin anyway else { log.AppendFormat("\n\t\tCan't transmit to nearby relay {0}.", this.nearestRelay == null ? "null" : this.nearestRelay.ToString() ); this.canTransmit = false; // If the best occluded relay is cheaper than Kerbin, check it against the nearest relay. // Since cheapestOccludedRelayRate is infinity if there are no occluded relays, this is safe if (cheapestOccludedRelayRate < kerbinRelayRate) { log.AppendFormat("\n\t\t\tBest occluded relay is cheaper than Kerbin ({0} < {1})", cheapestOccludedRelayRate, kerbinRelayRate); this.KerbinDirect = false; // If the nearest relay is cheaper than the best occluded relay, pick it. // Since cheapestRelayRate is infinity if there are no nearby relays, this is safe. if (cheapestRelayRate < cheapestOccludedRelayRate) { log.AppendFormat("\n\t\t\t\t...but the cheapest relay is cheaper ({0} < {1}), so picking it.", cheapestRelayRate, cheapestOccludedRelayRate); this.targetRelay = this.nearestRelay; this.firstOccludingBody = null; } // Otherwise, target the best occluded relay. else { log.AppendFormat("\n\t\t\t\t...and cheaper than the nearest relay ({0} >= {1}), so picking it.", cheapestRelayRate, cheapestOccludedRelayRate); this.targetRelay = bestOccludedRelay; this.firstOccludingBody = bodyOccludingBestOccludedRelay; } } // Otherwise, check Kerbin against the "nearest" (cheapest) relay. else { log.AppendFormat("\n\t\t\tKerbin is cheaper than the best occluded relay ({0} >= {1})", cheapestOccludedRelayRate, kerbinRelayRate); // If the "nearest" (cheapest) relay is cheaper than Kerbin, pick it. // Since cheapestRelayRate is infinity if there are no nearby relays, this is safe. if (cheapestRelayRate < kerbinRelayRate) { log.AppendFormat("\n\t\t\t\t...but the nearest relay is cheaper ({0} < {1}), so picking it.", cheapestRelayRate, kerbinRelayRate); // Since we have LOS, blank the first occluding body. this.firstOccludingBody = null; this.KerbinDirect = false; this.targetRelay = this.nearestRelay; } // Otherwise, pick Kerbin. else { log.AppendFormat("\n\t\t\t\t...and cheaper than the nearest relay ({0} >= {1}), so picking it.", cheapestRelayRate, kerbinRelayRate); this.KerbinDirect = true; this.firstOccludingBody = bodyOccludingKerbin; this.targetRelay = null; } } } } // If we do have LOS to Kerbin, try to prefer the closest of nearestRelay and Kerbin else { #if BENCH totalKerbinLOSTicks = this.performanceTimer.ElapsedTicks - startKerbinLOSTicks; #endif log.AppendFormat("\n\tKerbin is in LOS."); // If the nearest relay is in range, we can transmit. if (this.IsInRangeOf(this.nearestRelay)) { log.AppendFormat("\n\t\tCan transmit to nearby relay {0} (in range).", this.nearestRelay == null ? "null" : this.nearestRelay.ToString() ); this.canTransmit = true; // If the nearestRelay is closer than Kerbin, use it. if (cheapestRelayRate < kerbinRelayRate) { log.AppendFormat("\n\t\t\tPicking relay {0} over Kerbin ({1} < {2}).", this.nearestRelay == null ? "null" : this.nearestRelay.ToString(), cheapestRelayRate, kerbinRelayRate); this.KerbinDirect = false; this.targetRelay = this.nearestRelay; } // Otherwise, Kerbin is closer, so use it. else { log.AppendFormat("\n\t\t\tBut picking Kerbin over nearby relay {0} ({1} >= {2}).", this.nearestRelay == null ? "null" : this.nearestRelay.ToString(), cheapestRelayRate, kerbinRelayRate); this.KerbinDirect = true; this.targetRelay = null; } } // If the nearest relay is out of range, we still need to check on Kerbin. else { log.AppendFormat("\n\t\tCheapest relay {0} is out of range.", this.nearestRelay == null ? "null" : this.nearestRelay.ToString() ); // If Kerbin is in range, use it. if (this.IsInRangeOf(Kerbin)) { log.AppendFormat("\n\t\t\tCan transmit to Kerbin (in range)."); this.canTransmit = true; this.KerbinDirect = true; this.targetRelay = null; } // If Kerbin is out of range and the nearest relay is out of range, pick a second best between // Kerbin and bestOccludedRelay else { log.AppendFormat("\n\t\t\tCan't transmit to Kerbin (out of range)."); this.canTransmit = false; // If the best occluded relay is cheaper than Kerbin, check it against the nearest relay. // Since bestOccludedSqrDistance is infinity if there are no occluded relays, this is safe if (cheapestOccludedRelayRate < kerbinRelayRate) { log.AppendFormat("\n\t\t\tBest occluded relay is closer than Kerbin ({0} < {1})", cheapestOccludedRelayRate, kerbinRelayRate); this.KerbinDirect = false; // If the nearest relay is closer than the best occluded relay, pick it. // Since cheapestRelayRate is infinity if there are no nearby relays, this is safe. if (cheapestRelayRate < cheapestOccludedRelayRate) { log.AppendFormat("\n\t\t\t\t...but the cheapest relay is cheaper ({0} < {1}), so picking it.", cheapestRelayRate, cheapestOccludedRelayRate); this.targetRelay = this.nearestRelay; this.firstOccludingBody = null; } // Otherwise, target the best occluded relay. else { log.AppendFormat("\n\t\t\t\t...and cheaper than the cheapest relay ({0} >= {1}), so picking it.", cheapestRelayRate, cheapestOccludedRelayRate); this.targetRelay = bestOccludedRelay; this.firstOccludingBody = bodyOccludingBestOccludedRelay; } } // Otherwise, check Kerbin against the nearest relay. // Since we have LOS, blank the first occluding body. else { log.AppendFormat("\n\t\t\tKerbin is cheaper than the best occluded relay ({0} >= {1})", cheapestOccludedRelayRate, kerbinRelayRate); this.firstOccludingBody = null; // If the nearest relay is cheaper than Kerbin, pick it. // Since cheapestRelayRate is infinity if there are no nearby relays, this is safe. if (cheapestRelayRate < kerbinRelayRate) { log.AppendFormat("\n\t\t\t\t...but the nearest relay is cheaper ({0} < {1}), so picking it.", cheapestRelayRate, kerbinRelayRate); this.KerbinDirect = false; this.targetRelay = this.nearestRelay; } // Otherwise, pick Kerbin. else { log.AppendFormat("\n\t\t\t\t...and cheaper than the nearest relay ({0} >= {1}), so picking it.", cheapestRelayRate, kerbinRelayRate); this.KerbinDirect = true; this.targetRelay = null; } } } } } if (ARConfiguration.UseAdditiveRanges) { if (this.KerbinDirect) { this.NominalLinkSqrDistance = this.nominalTransmitDistance * ARConfiguration.KerbinNominalRange; this.MaximumLinkSqrDistance = this.maxTransmitDistance * ARConfiguration.KerbinRelayRange; } else { this.NominalLinkSqrDistance = this.nominalTransmitDistance * this.targetRelay.nominalTransmitDistance; this.MaximumLinkSqrDistance = this.maxTransmitDistance * this.targetRelay.maxTransmitDistance; } } else { this.NominalLinkSqrDistance = this.nominalTransmitDistance * this.nominalTransmitDistance; this.MaximumLinkSqrDistance = this.maxTransmitDistance * this.maxTransmitDistance; } if (this.canTransmit) { if (this.CurrentLinkSqrDistance < this.NominalLinkSqrDistance) { this.LinkStatus = ConnectionStatus.Optimal; } else { this.LinkStatus = ConnectionStatus.Suboptimal; } } else { this.LinkStatus = ConnectionStatus.None; } #if BENCH statusResolutionTicks = performanceTimer.ElapsedTicks - startKerbinLOSTicks - totalKerbinLOSTicks; #endif log.AppendFormat("\n{0}: Target search and status determination complete.", this.ToString()); #if DEBUG } catch (Exception ex) { log.AppendFormat("\nCaught {0}: {1}\n{2}", ex.GetType().FullName, ex.ToString(), ex.StackTrace); #if QUIT_ON_EXCEPTION UnityEngine.Application.Quit(); #endif } finally { #endif log.Print(false); #if DEBUG } #endif #if BENCH AntennaRelay.searchTimer += (ulong)this.performanceTimer.ElapsedTicks; AntennaRelay.searchCount++; this.performanceTimer.Stop(); double averageSearchTime = (double)AntennaRelay.searchTimer / (double)AntennaRelay.searchCount; if (AntennaRelay.searchCount >= 8000u / (ulong)ARConfiguration.UpdateDelay) { AntennaRelay.searchCount = 0u; AntennaRelay.searchTimer = 0u; AntennaRelay.averager.AddItem(averageSearchTime); AntennaRelay.doubleAverageTime = (long)(AntennaRelay.averager.Average * 2d); } if (this.performanceTimer.ElapsedTicks > AntennaRelay.doubleAverageTime) { System.Text.StringBuilder sb = Tools.GetStringBuilder(); sb.AppendFormat(Tools.SIFormatter, "[AntennaRelay] FindNearestRelay search for {0}" + " took significantly longer than average ({1:S3}s vs {2:S3}s)", this.ToString(), (double)this.performanceTimer.ElapsedTicks / (double)System.Diagnostics.Stopwatch.Frequency, (double)AntennaRelay.averager.Average / (double)System.Diagnostics.Stopwatch.Frequency ); sb.AppendFormat(Tools.SIFormatter, "\n\tVessel loop time: {0:S3}s", (double)totalVesselLoopTicks / (double)System.Diagnostics.Stopwatch.Frequency ); sb.AppendFormat(Tools.SIFormatter, "\n\t\tAverage vessel time for each of {1} vessels: {0:S3}s", (double)totalVesselLoopTicks / (double)System.Diagnostics.Stopwatch.Frequency / (double)usefulVesselCount, usefulVesselCount ); sb.AppendFormat(Tools.SIFormatter, "\n\t\tSlowest vessel LOS check: {0:S3}s to {1}", (double)slowestLOSVesselTicks / (double)System.Diagnostics.Stopwatch.Frequency, slowestLOSVesselName ); sb.AppendFormat(Tools.SIFormatter, "\n\t\tSlowest circular relay check: {0:S3}s for {1}", (double)slowestCircularVesselTicks / (double)System.Diagnostics.Stopwatch.Frequency, slowestCircularVesselName ); sb.AppendFormat(Tools.SIFormatter, "\n\tKerbin LOS check: {0:S3}s", (double)totalKerbinLOSTicks / (double)System.Diagnostics.Stopwatch.Frequency ); sb.AppendFormat(Tools.SIFormatter, "\n\tStatus resolution check: {0:S3}s", (double)statusResolutionTicks / (double)System.Diagnostics.Stopwatch.Frequency ); // sb.AppendFormat(Tools.SIFormatter, "", start) this.LogWarning(sb.ToString()); Tools.PutStringBuilder(sb); } #endif }
private void Update() { if (MapView.MapIsEnabled && this.mapRenderer == null) { this.mapRenderer = MapView.MapCamera.gameObject.AddComponent <ARMapRenderer>(); } if (this.toolbarButton != null) { this.toolbarButton.Enabled = MapView.MapIsEnabled; } if (this.appLauncherButton == null && !ToolbarManager.ToolbarAvailable && ApplicationLauncher.Ready) { this.appLauncherButton = ApplicationLauncher.Instance.AddModApplication( this.buttonToggle, this.buttonToggle, ApplicationLauncher.AppScenes.FLIGHT | ApplicationLauncher.AppScenes.MAPVIEW, this.appLauncherTextures[ConnectionStatus.None] ); } if (!this.updateTimer.IsRunning || this.updateTimer.ElapsedMilliseconds > ARConfiguration.UpdateDelay) { this.updateTimer.Restart(); } else { return; } this.log.Clear(); this.log.Append("[ARFlightController]: Update"); if (HighLogic.LoadedSceneIsFlight && FlightGlobals.ready && FlightGlobals.ActiveVessel != null) { Vessel vessel; IAntennaRelay relay; IAntennaRelay bestActiveRelay = null; IList <IAntennaRelay> activeVesselRelays; usefulRelays.Clear(); for (int vIdx = 0; vIdx < FlightGlobals.Vessels.Count; vIdx++) { vessel = FlightGlobals.Vessels[vIdx]; if (vessel == null || vessel == FlightGlobals.ActiveVessel) { continue; } switch (vessel.vesselType) { case VesselType.Debris: case VesselType.Flag: case VesselType.Unknown: continue; } log.AppendFormat("\nFetching best relay for vessel {0}", vessel); relay = vessel.GetBestRelay(); if (relay != null) { log.AppendFormat("\n\tAdding useful relay {0}", relay); usefulRelays.Add(relay); } } activeVesselRelays = RelayDatabase.Instance[FlightGlobals.ActiveVessel]; if (activeVesselRelays.Count > 0) { bestActiveRelay = RelayDatabase.Instance.GetBestVesselRelay(FlightGlobals.ActiveVessel); log.AppendFormat("\n\tAdding best active vessel relay {0} to usefulRelays", bestActiveRelay); usefulRelays.Add(bestActiveRelay); } log.AppendFormat("\n\tDoing target searches for {0} useful relays", usefulRelays.Count); for (int uIdx = 0; uIdx < usefulRelays.Count; uIdx++) { relay = usefulRelays[uIdx]; if (relay == null) { continue; } log.AppendFormat("\n\tDoing target search for useful relay {0}", relay); relay.FindNearestRelay(); relay.RecalculateTransmissionRates(); } // Very last, find routes for the non-best relays on the active vessel. for (int rIdx = 0; rIdx < activeVesselRelays.Count; rIdx++) { relay = activeVesselRelays[rIdx]; // The best active relay will get checked with the other useful relays later. if (relay == null || relay == bestActiveRelay) { continue; } log.AppendFormat("\nFinding nearest relay for active vessel relay {0}", relay); relay.FindNearestRelay(); relay.RecalculateTransmissionRates(); } if (this.toolbarButton != null || this.appLauncherButton != null) { log.Append("\nChecking active vessel relay status."); this.currentConnectionStatus = FlightGlobals.ActiveVessel.GetConnectionStatus(); log.AppendFormat("\n\tcurrentConnectionStatus: {0}, setting texture to {1}", this.currentConnectionStatus, this.currentConnectionTexture); if (this.toolbarButton != null) { this.toolbarButton.TexturePath = this.currentConnectionTexture; if (this.currentConnectionStatus == ConnectionStatus.None) { if (!this.toolbarButton.Important) { this.toolbarButton.Important = true; } } else { if (this.toolbarButton.Important) { this.toolbarButton.Important = false; } } } if (this.appLauncherButton != null) { this.appLauncherButton.SetTexture(this.currentAppLauncherTexture); } } } log.Print(false); }
/// <summary> /// Returns the distance between two IAntennaRelays. /// </summary> /// <param name="relayOne">Relay one.</param> /// <param name="relayTwo">Relay two.</param> public static double SqrDistanceTo(this IAntennaRelay relayOne, IAntennaRelay relayTwo) { return relayOne.vessel.sqrDistanceTo(relayTwo.vessel); }
/// <summary> /// Determines if relayOne is in range of the specified relayTwo. /// </summary> /// <returns><c>true</c> if relayOne is in range of the specifie relayTwo; otherwise, <c>false</c>.</returns> /// <param name="relayOne">Relay one.</param> /// <param name="relayTwo">Relay two.</param> public static bool IsInRangeOf(this AntennaRelay relayOne, IAntennaRelay relayTwo) { if (relayOne == null || relayTwo == null) { return false; } return relayOne.SqrDistanceTo(relayTwo) <= relayOne.MaxLinkSqrDistanceTo(relayTwo); }
/// <summary> /// Returns the square of the maximum link range between two relays. /// </summary> /// <returns>The maximum link range between two relays.</returns> /// <param name="relayOne">Relay one.</param> /// <param name="relayTwo">Relay two.</param> public static double MaxLinkSqrDistanceTo(this AntennaRelay relayOne, IAntennaRelay relayTwo) { if (ARConfiguration.UseAdditiveRanges) { return relayOne.maxTransmitDistance * relayTwo.maxTransmitDistance; } else { return relayOne.maxTransmitDistance * relayOne.maxTransmitDistance; } }
private void SetRelayVertices(IAntennaRelay relay) { log.AppendFormat("\n\t\tDrawing line for relay chain starting at {0}.", relay); if (relay.vessel == null) { log.Append("\n\t\tvessel is null, bailing out"); return; } LineRenderer renderer = this[relay.vessel.id]; Vector3 start = ScaledSpace.LocalToScaledSpace(relay.vessel.GetWorldPos3D()); float lineWidth; float d = Screen.height / 2f + 0.01f; if (MapView.Draw3DLines) { lineWidth = 0.00833333333f * MapView.MapCamera.Distance; } else { lineWidth = 3f; // TODO: No idea if this substitution is right. // start = MapView.MapCamera.camera.WorldToScreenPoint(start); start = PlanetariumCamera.Camera.WorldToScreenPoint(start); start.z = start.z >= 0f ? d : -d; } renderer.SetWidth(lineWidth, lineWidth); renderer.SetPosition(0, start); int idx = 0; #if DEBUG relayStart = timer.ElapsedMilliseconds; #endif Vector3 nextPoint; renderer.enabled = true; if (!relay.CanTransmit()) { thisColor = Color.red; } else { if (relay.LinkStatus == ConnectionStatus.Optimal) { thisColor = Color.green; } else { thisColor = Color.yellow; } } if (relay.KerbinDirect) { nextPoint = ScaledSpace.LocalToScaledSpace(AntennaRelay.Kerbin.position); } else { if (relay.targetRelay == null || relay.targetRelay.vessel == null) { this.LogError( "SetRelayVertices: relay {0} has null target relay or vessel when not KerbinDirect, bailing out!", relay ); renderer.enabled = false; return; } switch (relay.targetRelay.vessel.vesselType) { case VesselType.Debris: case VesselType.Flag: case VesselType.Unknown: renderer.enabled = false; return; default: break; } nextPoint = ScaledSpace.LocalToScaledSpace(relay.targetRelay.vessel.GetWorldPos3D()); } renderer.SetColors(thisColor, thisColor); if (!MapView.Draw3DLines) { // TODO: No idea if this substitution is right. // nextPoint = MapView.MapCamera.camera.WorldToScreenPoint(nextPoint); nextPoint = PlanetariumCamera.Camera.WorldToScreenPoint(nextPoint); nextPoint.z = nextPoint.z >= 0f ? d : -d; } idx++; renderer.SetVertexCount(idx + 1); renderer.SetPosition(idx, nextPoint); log.AppendFormat("\n\t\t\t...finished segment in {0} ms", timer.ElapsedMilliseconds - relayStart); }
/// <summary> /// Returns the distance from this IAntennaRelay to the given CelestialBody /// </summary> /// <param name="relay">Relay.</param> /// <param name="body">Body.</param> public static double DistanceTo(this IAntennaRelay relay, CelestialBody body) { double range = relay.vessel.DistanceTo(body) - body.Radius; return(range); }
/// <summary> /// Returns the distance between two IAntennaRelays. /// </summary> /// <param name="relayOne">Relay one.</param> /// <param name="relayTwo">Relay two.</param> public static double SqrDistanceTo(this IAntennaRelay relayOne, IAntennaRelay relayTwo) { return(relayOne.vessel.sqrDistanceTo(relayTwo.vessel)); }
/// <summary> /// Gets the potential link cost, in EC/MiT. /// </summary> /// <returns>The potential link cost, in EC/MiT.</returns> /// <param name="potentialTarget">Potential target relay.</param> public RelayDataCost GetPotentialLinkCost(IAntennaRelay potentialTarget) { if (potentialTarget == null) { return RelayDataCost.Infinity; } double currentSqrDistance = this.SqrDistanceTo(potentialTarget); if (currentSqrDistance > this.MaxLinkSqrDistanceTo(potentialTarget)) { return RelayDataCost.Infinity; } double nominalSqrDistance; if (ARConfiguration.UseAdditiveRanges) { nominalSqrDistance = this.nominalTransmitDistance * potentialTarget.nominalTransmitDistance; } else { nominalSqrDistance = this.nominalTransmitDistance * this.nominalTransmitDistance; } return GetPotentialLinkCost(currentSqrDistance, nominalSqrDistance); }
// Produce a Part-hashed table of relays for the given vessel private void getVesselRelays(Vessel vessel, ref List <IAntennaRelay> relays) { // We're going to completely regen this table, so dump the current contents. relays.Clear(); Logging.PostDebugMessage(string.Format( "{0}: Getting antenna relays from vessel {1}.", "IAntennaRelay", vessel.vesselName )); double bestRelayRange = double.NegativeInfinity; IAntennaRelay bestRelay = null; IAntennaRelay relay; // If the vessel is loaded, we can fetch modules implementing IAntennaRelay directly. if (vessel.loaded) { Logging.PostDebugMessage(string.Format( "{0}: vessel {1} is loaded, searching for modules in loaded parts.", "IAntennaRelay", vessel.vesselName )); // Loop through the Parts in the Vessel... Part part; for (int partIdx = 0; partIdx < vessel.Parts.Count; partIdx++) { part = vessel.Parts[partIdx]; // ...loop through the PartModules in the Part... PartModule module; for (int modIdx = 0; modIdx < part.Modules.Count; modIdx++) { module = part.Modules[modIdx]; // ...if the module is a relay... if (module is IAntennaRelay) { relay = (module as IAntennaRelay); if (relay.maxTransmitDistance > bestRelayRange) { bestRelayRange = relay.maxTransmitDistance; bestRelay = relay; } // ...add the module to the table relays.Add(relay); // ...neglect relay objects after the first in each part. break; } } } } // If the vessel is not loaded, we need to build ProtoAntennaRelays when we find relay ProtoPartSnapshots. else { Logging.PostDebugMessage(string.Format( "{0}: vessel {1} is not loaded, searching for modules in prototype parts.", this.GetType().Name, vessel.vesselName )); // Loop through the ProtoPartModuleSnapshots in the Vessel... ProtoPartSnapshot pps; for (int ppsIdx = 0; ppsIdx < vessel.protoVessel.protoPartSnapshots.Count; ppsIdx++) { pps = vessel.protoVessel.protoPartSnapshots[ppsIdx]; Logging.PostDebugMessage(string.Format( "{0}: Searching in protopartsnapshot {1}", this.GetType().Name, pps )); // ...Fetch the prefab, because it's more useful for what we're doing. Part partPrefab = PartLoader.getPartInfoByName(pps.partName).partPrefab; Logging.PostDebugMessage(string.Format( "{0}: Got partPrefab {1} in protopartsnapshot {2}", this.GetType().Name, partPrefab, pps )); // ...loop through the PartModules in the prefab... PartModule module; for (int modIdx = 0; modIdx < partPrefab.Modules.Count; modIdx++) { module = partPrefab.Modules[modIdx]; Logging.PostDebugMessage(string.Format( "{0}: Searching in partmodule {1}", this.GetType().Name, module )); // ...if the module is a relay... if (module is IAntennaRelay) { Logging.PostDebugMessage(string.Format( "{0}: partmodule {1} is antennarelay", this.GetType().Name, module )); relay = new ProtoAntennaRelay(module as IAntennaRelay, pps); if (relay.maxTransmitDistance > bestRelayRange) { bestRelayRange = relay.maxTransmitDistance; bestRelay = relay; } // ...build a new ProtoAntennaRelay and add it to the table relays.Add(relay); // ...neglect relay objects after the first in each part. break; } } } } this.bestRelayTable[vessel.id] = bestRelay; Logging.PostDebugMessage(string.Format( "{0}: vessel '{1}' ({2}) has {3} transmitters.", "IAntennaRelay", vessel.vesselName, vessel.id, relays.Count )); }