Version 1.10.1 1.10.1
Version 1.10.1

// AntennaRange // AntennaRange
// //
// ARFlightController.cs // ARFlightController.cs
// //
// Copyright © 2014-2015, toadicus // Copyright © 2014-2015, toadicus
// All rights reserved. // All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without modification, // Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met: // are permitted provided that the following conditions are met:
// //
// 1. Redistributions of source code must retain the above copyright notice, // 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer. // this list of conditions and the following disclaimer.
// //
// 2. Redistributions in binary form must reproduce the above copyright notice, // 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation and/or other // this list of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution. // materials provided with the distribution.
// //
// 3. Neither the name of the copyright holder nor the names of its contributors may be used // 3. Neither the name of the copyright holder nor the names of its contributors may be used
// to endorse or promote products derived from this software without specific prior written permission. // to endorse or promote products derived from this software without specific prior written permission.
// //
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   
#pragma warning disable 1591 #pragma warning disable 1591
   
using KSP; using KSP;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using ToadicusTools; using ToadicusTools;
using UnityEngine; using UnityEngine;
   
namespace AntennaRange namespace AntennaRange
{ {
[KSPAddon(KSPAddon.Startup.Flight, false)] [KSPAddon(KSPAddon.Startup.Flight, false)]
public class ARFlightController : MonoBehaviour public class ARFlightController : MonoBehaviour
{ {
#region Static #region Static
private static List<IAntennaRelay> usefulRelays; private static List<IAntennaRelay> usefulRelays;
public static IList<IAntennaRelay> UsefulRelays; public static IList<IAntennaRelay> UsefulRelays;
#endregion #endregion
   
#region Fields #region Fields
private Dictionary<ConnectionStatus, string> toolbarTextures; private Dictionary<ConnectionStatus, string> toolbarTextures;
private Dictionary<ConnectionStatus, Texture> appLauncherTextures; private Dictionary<ConnectionStatus, Texture> appLauncherTextures;
   
private ARMapRenderer mapRenderer; private ARMapRenderer mapRenderer;
   
private IButton toolbarButton; private IButton toolbarButton;
   
private ApplicationLauncherButton appLauncherButton; private ApplicationLauncherButton appLauncherButton;
private Tools.DebugLogger log; private Tools.DebugLogger log;
   
private System.Diagnostics.Stopwatch updateTimer; private System.Diagnostics.Stopwatch updateTimer;
#endregion #endregion
   
#region Properties #region Properties
public ConnectionStatus currentConnectionStatus public ConnectionStatus currentConnectionStatus
{ {
get; get;
private set; private set;
} }
   
private string currentConnectionTexture private string currentConnectionTexture
{ {
get get
{ {
return this.toolbarTextures[this.currentConnectionStatus]; return this.toolbarTextures[this.currentConnectionStatus];
} }
} }
   
private Texture currentAppLauncherTexture private Texture currentAppLauncherTexture
{ {
get get
{ {
return this.appLauncherTextures[this.currentConnectionStatus]; return this.appLauncherTextures[this.currentConnectionStatus];
} }
} }
   
public ControlTypes currentControlLock public ControlTypes currentControlLock
{ {
get get
{ {
if (this.lockID == string.Empty) if (this.lockID == string.Empty)
{ {
return ControlTypes.None; return ControlTypes.None;
} }
   
return InputLockManager.GetControlLock(this.lockID); return InputLockManager.GetControlLock(this.lockID);
} }
} }
   
public string lockID public string lockID
{ {
get; get;
private set; private set;
} }
   
public ControlTypes lockSet public ControlTypes lockSet
{ {
get get
{ {
return ControlTypes.ALL_SHIP_CONTROLS; return ControlTypes.ALL_SHIP_CONTROLS;
} }
} }
   
public Vessel vessel public Vessel vessel
{ {
get get
{ {
if (FlightGlobals.ready && FlightGlobals.ActiveVessel != null) if (FlightGlobals.ready && FlightGlobals.ActiveVessel != null)
{ {
return FlightGlobals.ActiveVessel; return FlightGlobals.ActiveVessel;
} }
   
return null; return null;
} }
} }
#endregion #endregion
   
#region MonoBehaviour LifeCycle #region MonoBehaviour LifeCycle
private void Awake() private void Awake()
{ {
this.lockID = "ARConnectionRequired"; this.lockID = "ARConnectionRequired";
   
this.log = Tools.DebugLogger.New(this); this.log = Tools.DebugLogger.New(this);
   
this.updateTimer = new System.Diagnostics.Stopwatch(); this.updateTimer = new System.Diagnostics.Stopwatch();
   
this.toolbarTextures = new Dictionary<ConnectionStatus, string>(); this.toolbarTextures = new Dictionary<ConnectionStatus, string>();
   
this.toolbarTextures[ConnectionStatus.None] = "AntennaRange/Textures/toolbarIconNoConnection"; this.toolbarTextures[ConnectionStatus.None] = "AntennaRange/Textures/toolbarIconNoConnection";
this.toolbarTextures[ConnectionStatus.Suboptimal] = "AntennaRange/Textures/toolbarIconSubOptimal"; this.toolbarTextures[ConnectionStatus.Suboptimal] = "AntennaRange/Textures/toolbarIconSubOptimal";
this.toolbarTextures[ConnectionStatus.Optimal] = "AntennaRange/Textures/toolbarIcon"; this.toolbarTextures[ConnectionStatus.Optimal] = "AntennaRange/Textures/toolbarIcon";
   
this.appLauncherTextures = new Dictionary<ConnectionStatus, Texture>(); this.appLauncherTextures = new Dictionary<ConnectionStatus, Texture>();
   
this.appLauncherTextures[ConnectionStatus.None] = this.appLauncherTextures[ConnectionStatus.None] =
GameDatabase.Instance.GetTexture("AntennaRange/Textures/appLauncherIconNoConnection", false); GameDatabase.Instance.GetTexture("AntennaRange/Textures/appLauncherIconNoConnection", false);
this.appLauncherTextures[ConnectionStatus.Suboptimal] = this.appLauncherTextures[ConnectionStatus.Suboptimal] =
GameDatabase.Instance.GetTexture("AntennaRange/Textures/appLauncherIconSubOptimal", false); GameDatabase.Instance.GetTexture("AntennaRange/Textures/appLauncherIconSubOptimal", false);
this.appLauncherTextures[ConnectionStatus.Optimal] = this.appLauncherTextures[ConnectionStatus.Optimal] =
GameDatabase.Instance.GetTexture("AntennaRange/Textures/appLauncherIcon", false); GameDatabase.Instance.GetTexture("AntennaRange/Textures/appLauncherIcon", false);
   
if (ToolbarManager.ToolbarAvailable) if (ToolbarManager.ToolbarAvailable)
{ {
this.toolbarButton = ToolbarManager.Instance.add("AntennaRange", "ARConnectionStatus"); this.toolbarButton = ToolbarManager.Instance.add("AntennaRange", "ARConnectionStatus");
   
this.toolbarButton.TexturePath = this.toolbarTextures[ConnectionStatus.None]; this.toolbarButton.TexturePath = this.toolbarTextures[ConnectionStatus.None];
this.toolbarButton.Text = "AntennaRange"; this.toolbarButton.Text = "AntennaRange";
this.toolbarButton.Visibility = new GameScenesVisibility(GameScenes.FLIGHT); this.toolbarButton.Visibility = new GameScenesVisibility(GameScenes.FLIGHT);
this.toolbarButton.OnClick += (e) => (this.buttonToggle()); this.toolbarButton.OnClick += (e) => (this.buttonToggle());
} }
   
GameEvents.onGameSceneLoadRequested.Add(this.onSceneChangeRequested); GameEvents.onGameSceneLoadRequested.Add(this.onSceneChangeRequested);
GameEvents.onVesselChange.Add(this.onVesselChange); GameEvents.onVesselChange.Add(this.onVesselChange);
   
usefulRelays = new List<IAntennaRelay>(); usefulRelays = new List<IAntennaRelay>();
UsefulRelays = usefulRelays.AsReadOnly(); UsefulRelays = usefulRelays.AsReadOnly();
} }
   
private void FixedUpdate() private void FixedUpdate()
{ {
this.log.Clear(); this.log.Clear();
   
VesselCommand availableCommand; VesselCommand availableCommand;
   
if (ARConfiguration.RequireConnectionForControl) if (ARConfiguration.RequireConnectionForControl)
{ {
availableCommand = this.vessel.CurrentCommand(); availableCommand = this.vessel.CurrentCommand();
} }
else else
{ {
availableCommand = VesselCommand.Crew; availableCommand = VesselCommand.Crew;
} }
   
log.AppendFormat("availableCommand: {0}\n\t" + log.AppendFormat("availableCommand: {0}\n\t" +
"(availableCommand & VesselCommand.Crew) == VesselCommand.Crew: {1}\n\t" + "(availableCommand & VesselCommand.Crew) == VesselCommand.Crew: {1}\n\t" +
"(availableCommand & VesselCommand.Probe) == VesselCommand.Probe: {2}\n\t" + "(availableCommand & VesselCommand.Probe) == VesselCommand.Probe: {2}\n\t" +
"vessel.HasConnectedRelay(): {3}", "vessel.HasConnectedRelay(): {3}",
(int)availableCommand, (int)availableCommand,
(availableCommand & VesselCommand.Crew) == VesselCommand.Crew, (availableCommand & VesselCommand.Crew) == VesselCommand.Crew,
(availableCommand & VesselCommand.Probe) == VesselCommand.Probe, (availableCommand & VesselCommand.Probe) == VesselCommand.Probe,
vessel.HasConnectedRelay() vessel.HasConnectedRelay()
); );
   
// If we are requiring a connection for control, the vessel does not have any adequately staffed pods, // If we are requiring a connection for control, the vessel does not have any adequately staffed pods,
// and the vessel does not have any connected relays... // and the vessel does not have any connected relays...
if ( if (
HighLogic.LoadedSceneIsFlight && HighLogic.LoadedSceneIsFlight &&
ARConfiguration.RequireConnectionForControl && ARConfiguration.RequireConnectionForControl &&
this.vessel != null && this.vessel != null &&
this.vessel.vesselType != VesselType.EVA && this.vessel.vesselType != VesselType.EVA &&
!( !(
(availableCommand & VesselCommand.Crew) == VesselCommand.Crew || (availableCommand & VesselCommand.Crew) == VesselCommand.Crew ||
(availableCommand & VesselCommand.Probe) == VesselCommand.Probe && vessel.HasConnectedRelay() (availableCommand & VesselCommand.Probe) == VesselCommand.Probe && vessel.HasConnectedRelay()
)) ))
{ {
// ...and if the controls are not currently locked... // ...and if the controls are not currently locked...
if (currentControlLock == ControlTypes.None) if (currentControlLock == ControlTypes.None)
{ {
// ...lock the controls. // ...lock the controls.
InputLockManager.SetControlLock(this.lockSet, this.lockID); InputLockManager.SetControlLock(this.lockSet, this.lockID);
} }
} }
// ...otherwise, if the controls are locked... // ...otherwise, if the controls are locked...
else if (currentControlLock != ControlTypes.None) else if (currentControlLock != ControlTypes.None)
{ {
// ...unlock the controls. // ...unlock the controls.
InputLockManager.RemoveControlLock(this.lockID); InputLockManager.RemoveControlLock(this.lockID);
} }
   
log.Print(); log.Print();
} }
   
private void Update() private void Update()
{ {
if (MapView.MapIsEnabled && this.mapRenderer == null) if (MapView.MapIsEnabled && this.mapRenderer == null)
{ {
this.mapRenderer = MapView.MapCamera.gameObject.AddComponent<ARMapRenderer>(); this.mapRenderer = MapView.MapCamera.gameObject.AddComponent<ARMapRenderer>();
} }
   
if (this.toolbarButton != null) if (this.toolbarButton != null)
{ {
this.toolbarButton.Enabled = MapView.MapIsEnabled; this.toolbarButton.Enabled = MapView.MapIsEnabled;
} }
   
if (this.appLauncherButton == null && !ToolbarManager.ToolbarAvailable && ApplicationLauncher.Ready) if (this.appLauncherButton == null && !ToolbarManager.ToolbarAvailable && ApplicationLauncher.Ready)
{ {
this.appLauncherButton = ApplicationLauncher.Instance.AddModApplication( this.appLauncherButton = ApplicationLauncher.Instance.AddModApplication(
this.buttonToggle, this.buttonToggle, this.buttonToggle, this.buttonToggle,
ApplicationLauncher.AppScenes.FLIGHT | ApplicationLauncher.AppScenes.MAPVIEW, ApplicationLauncher.AppScenes.FLIGHT | ApplicationLauncher.AppScenes.MAPVIEW,
this.appLauncherTextures[ConnectionStatus.None] this.appLauncherTextures[ConnectionStatus.None]
); );
} }
   
if (!this.updateTimer.IsRunning || this.updateTimer.ElapsedMilliseconds > ARConfiguration.UpdateDelay) if (!this.updateTimer.IsRunning || this.updateTimer.ElapsedMilliseconds > ARConfiguration.UpdateDelay)
{ {
this.updateTimer.Restart(); this.updateTimer.Restart();
} }
else else
{ {
return; return;
} }
   
this.log.Clear(); this.log.Clear();
   
  this.log.Append("[ARFlightController]: Update");
   
if (HighLogic.LoadedSceneIsFlight && FlightGlobals.ready && FlightGlobals.ActiveVessel != null) if (HighLogic.LoadedSceneIsFlight && FlightGlobals.ready && FlightGlobals.ActiveVessel != null)
{ {
Vessel vessel; Vessel vessel;
IAntennaRelay relay; IAntennaRelay relay;
  IAntennaRelay bestActiveRelay;
IList<IAntennaRelay> activeVesselRelays; IList<IAntennaRelay> activeVesselRelays;
   
usefulRelays.Clear(); usefulRelays.Clear();
   
for (int vIdx = 0; vIdx < FlightGlobals.Vessels.Count; vIdx++) for (int vIdx = 0; vIdx < FlightGlobals.Vessels.Count; vIdx++)
{ {
vessel = FlightGlobals.Vessels[vIdx]; vessel = FlightGlobals.Vessels[vIdx];
   
if (vessel == null || vessel == FlightGlobals.ActiveVessel) if (vessel == null || vessel == FlightGlobals.ActiveVessel)
{ {
continue; continue;
} }
   
log.AppendFormat("Fetching best relay for vessel {0}", vessel); log.AppendFormat("\nFetching best relay for vessel {0}", vessel);
   
relay = vessel.GetBestRelay(); relay = vessel.GetBestRelay();
   
if (relay != null) if (relay != null)
{ {
log.AppendFormat("Finding nearest relay for best relay {0}", relay); log.AppendFormat("\n\tAdding useful relay {0}", relay);
   
usefulRelays.Add(relay); usefulRelays.Add(relay);
relay.FindNearestRelay(); }
} }
}  
  bestActiveRelay = RelayDatabase.Instance.GetBestVesselRelay(FlightGlobals.ActiveVessel);
activeVesselRelays = RelayDatabase.Instance[FlightGlobals.ActiveVessel]; activeVesselRelays = RelayDatabase.Instance[FlightGlobals.ActiveVessel];
for (int rIdx = 0; rIdx < activeVesselRelays.Count; rIdx++) for (int rIdx = 0; rIdx < activeVesselRelays.Count; rIdx++)
{ {
relay = activeVesselRelays[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.FindNearestRelay();
} }
   
usefulRelays.Add(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();
  }
   
if (this.toolbarButton != null || this.appLauncherButton != null) if (this.toolbarButton != null || this.appLauncherButton != null)
{ {
log.Append("Checking vessel relay status.\n"); log.Append("\nChecking active vessel relay status.");
   
this.currentConnectionStatus = FlightGlobals.ActiveVessel.GetConnectionStatus(); this.currentConnectionStatus = FlightGlobals.ActiveVessel.GetConnectionStatus();
   
log.AppendFormat("currentConnectionStatus: {0}, setting texture to {1}", log.AppendFormat("\n\tcurrentConnectionStatus: {0}, setting texture to {1}",
this.currentConnectionStatus, this.currentConnectionTexture); this.currentConnectionStatus, this.currentConnectionTexture);
   
if (this.toolbarButton != null) if (this.toolbarButton != null)
{ {
this.toolbarButton.TexturePath = this.currentConnectionTexture; this.toolbarButton.TexturePath = this.currentConnectionTexture;
   
if (this.currentConnectionStatus == ConnectionStatus.None) if (this.currentConnectionStatus == ConnectionStatus.None)
{ {
if (!this.toolbarButton.Important) this.toolbarButton.Important = true; if (!this.toolbarButton.Important) this.toolbarButton.Important = true;
} }
else else
{ {
if (this.toolbarButton.Important) this.toolbarButton.Important = false; if (this.toolbarButton.Important) this.toolbarButton.Important = false;
} }
} }
if (this.appLauncherButton != null) if (this.appLauncherButton != null)
{ {
this.appLauncherButton.SetTexture(this.currentAppLauncherTexture); this.appLauncherButton.SetTexture(this.currentAppLauncherTexture);
} }
} }
} }
   
log.Print(); log.Print(false);
} }
   
private void OnDestroy() private void OnDestroy()
{ {
InputLockManager.RemoveControlLock(this.lockID); InputLockManager.RemoveControlLock(this.lockID);
   
if (this.mapRenderer != null) if (this.mapRenderer != null)
{ {
GameObject.Destroy(this.mapRenderer); GameObject.Destroy(this.mapRenderer);
} }
   
if (this.toolbarButton != null) if (this.toolbarButton != null)
{ {
this.toolbarButton.Destroy(); this.toolbarButton.Destroy();
} }
   
if (this.appLauncherButton != null) if (this.appLauncherButton != null)
{ {
ApplicationLauncher.Instance.RemoveModApplication(this.appLauncherButton); ApplicationLauncher.Instance.RemoveModApplication(this.appLauncherButton);
this.appLauncherButton = null; this.appLauncherButton = null;
} }
   
GameEvents.onGameSceneLoadRequested.Remove(this.onSceneChangeRequested); GameEvents.onGameSceneLoadRequested.Remove(this.onSceneChangeRequested);
GameEvents.onVesselChange.Remove(this.onVesselChange); GameEvents.onVesselChange.Remove(this.onVesselChange);
   
print("ARFlightController: Destroyed."); print("ARFlightController: Destroyed.");
} }
#endregion #endregion
   
private void buttonToggle() private void buttonToggle()
{ {
if (MapView.MapIsEnabled) if (MapView.MapIsEnabled)
{ {
ARConfiguration.PrettyLines = !ARConfiguration.PrettyLines; ARConfiguration.PrettyLines = !ARConfiguration.PrettyLines;
} }
} }
   
#region Event Handlers #region Event Handlers
private void onSceneChangeRequested(GameScenes scene) private void onSceneChangeRequested(GameScenes scene)
{ {
print("ARFlightController: Requesting Destruction."); print("ARFlightController: Requesting Destruction.");
MonoBehaviour.Destroy(this); MonoBehaviour.Destroy(this);
} }
   
private void onVesselChange(Vessel vessel) private void onVesselChange(Vessel vessel)
{ {
InputLockManager.RemoveControlLock(this.lockID); InputLockManager.RemoveControlLock(this.lockID);
} }
#endregion #endregion
} }
} }
   
// AntennaRange // AntennaRange
// //
// AntennaRelay.cs // AntennaRelay.cs
// //
// Copyright © 2014-2015, toadicus // Copyright © 2014-2015, toadicus
// All rights reserved. // All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without modification, // Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met: // are permitted provided that the following conditions are met:
// //
// 1. Redistributions of source code must retain the above copyright notice, // 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer. // this list of conditions and the following disclaimer.
// //
// 2. Redistributions in binary form must reproduce the above copyright notice, // 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation and/or other // this list of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution. // materials provided with the distribution.
// //
// 3. Neither the name of the copyright holder nor the names of its contributors may be used // 3. Neither the name of the copyright holder nor the names of its contributors may be used
// to endorse or promote products derived from this software without specific prior written permission. // to endorse or promote products derived from this software without specific prior written permission.
// //
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using ToadicusTools; using ToadicusTools;
   
namespace AntennaRange namespace AntennaRange
{ {
/// <summary> /// <summary>
/// Relay code at the heart of AntennaRange /// Relay code at the heart of AntennaRange
/// </summary> /// </summary>
public class AntennaRelay public class AntennaRelay
{ {
// We don't have a Bard, so we'll hide Kerbin here. // We don't have a Bard, so we'll hide Kerbin here.
private static CelestialBody _Kerbin; private static CelestialBody _Kerbin;
   
/// <summary> /// <summary>
/// Fetches, caches, and returns a <see cref="CelestialBody"/> reference to Kerbin /// Fetches, caches, and returns a <see cref="CelestialBody"/> reference to Kerbin
/// </summary> /// </summary>
public static CelestialBody Kerbin public static CelestialBody Kerbin
{ {
get get
{ {
if (_Kerbin == null && FlightGlobals.ready) if (_Kerbin == null && FlightGlobals.ready)
{ {
_Kerbin = FlightGlobals.GetHomeBody(); _Kerbin = FlightGlobals.GetHomeBody();
} }
   
return _Kerbin; return _Kerbin;
} }
} }
   
#if BENCH #if BENCH
private static ushort relayCount = 0; private static ushort relayCount = 0;
private static ulong searchCount = 0u; private static ulong searchCount = 0u;
private static ulong searchTimer = 0u; private static ulong searchTimer = 0u;
private readonly static RollingAverage averager = new RollingAverage(16); private readonly static RollingAverage averager = new RollingAverage(16);
private static long doubleAverageTime = long.MaxValue; private static long doubleAverageTime = long.MaxValue;
   
   
private System.Diagnostics.Stopwatch performanceTimer = new System.Diagnostics.Stopwatch(); private System.Diagnostics.Stopwatch performanceTimer = new System.Diagnostics.Stopwatch();
#endif #endif
   
private bool canTransmit; private bool canTransmit;
   
private IAntennaRelay nearestRelay; private IAntennaRelay nearestRelay;
private IAntennaRelay bestOccludedRelay; private IAntennaRelay bestOccludedRelay;
   
/// <summary> /// <summary>
/// The <see cref="AntennaRange.ModuleLimitedDataTransmitter"/> reference underlying this AntennaRelay, as an /// The <see cref="AntennaRange.ModuleLimitedDataTransmitter"/> reference underlying this AntennaRelay, as an
/// <see cref="AntennaRange.IAntennaRelay"/> /// <see cref="AntennaRange.IAntennaRelay"/>
/// </summary> /// </summary>
protected IAntennaRelay moduleRef; protected IAntennaRelay moduleRef;
   
/// <summary> /// <summary>
/// Gets the parent Vessel. /// Gets the parent Vessel.
/// </summary> /// </summary>
/// <value>The parent Vessel.</value> /// <value>The parent Vessel.</value>
public virtual Vessel vessel public virtual Vessel vessel
{ {
get get
{ {
return this.moduleRef.vessel; return this.moduleRef.vessel;
} }
} }
   
/// <summary> /// <summary>
/// Gets the target <see cref="AntennaRange.IAntennaRelay"/>relay. /// Gets the target <see cref="AntennaRange.IAntennaRelay"/>relay.
/// </summary> /// </summary>
public IAntennaRelay targetRelay public IAntennaRelay targetRelay
{ {
get; get;
protected set; protected set;
} }
   
/// <summary> /// <summary>
/// Gets a value indicating whether this <see cref="AntennaRange.IAntennaRelay"/> Relay is communicating /// Gets a value indicating whether this <see cref="AntennaRange.IAntennaRelay"/> Relay is communicating
/// directly with Kerbin. /// directly with Kerbin.
/// </summary> /// </summary>
public virtual bool KerbinDirect public virtual bool KerbinDirect
{ {
get; get;
protected set; protected set;
} }
   
/// <summary> /// <summary>
/// Gets or sets the nominal link distance, in meters. /// Gets or sets the nominal link distance, in meters.
/// </summary> /// </summary>
public virtual double NominalLinkSqrDistance public virtual double NominalLinkSqrDistance
{ {
get; get;
protected set; protected set;
} }
   
/// <summary> /// <summary>
/// Gets or sets the maximum link distance, in meters. /// Gets or sets the maximum link distance, in meters.
/// </summary> /// </summary>
public virtual double MaximumLinkSqrDistance public virtual double MaximumLinkSqrDistance
{ {
get; get;
protected set; protected set;
} }
   
/// <summary> /// <summary>
/// Gets the first <see cref="CelestialBody"/> found to be blocking line of sight. /// Gets the first <see cref="CelestialBody"/> found to be blocking line of sight.
/// </summary> /// </summary>
public virtual CelestialBody firstOccludingBody public virtual CelestialBody firstOccludingBody
{ {
get; get;
protected set; protected set;
} }
   
/// <summary> /// <summary>
/// Gets the transmit distance. /// Gets the transmit distance.
/// </summary> /// </summary>
/// <value>The transmit distance.</value> /// <value>The transmit distance.</value>
public double CurrentLinkSqrDistance public double CurrentLinkSqrDistance
{ {
get get
{ {
if (this.KerbinDirect || this.targetRelay == null) if (this.KerbinDirect || this.targetRelay == null)
{ {
return this.SqrDistanceTo(Kerbin); return this.SqrDistanceTo(Kerbin);
} }
else else
{ {
return this.SqrDistanceTo(this.targetRelay); return this.SqrDistanceTo(this.targetRelay);
} }
} }
} }
   
/// <summary> /// <summary>
/// Gets or sets the link status. /// Gets or sets the link status.
/// </summary> /// </summary>
public virtual ConnectionStatus LinkStatus public virtual ConnectionStatus LinkStatus
{ {
get; get;
protected set; protected set;
} }
   
/// <summary> /// <summary>
/// Gets the nominal transmit distance at which the Antenna behaves just as prescribed by Squad's config. /// Gets the nominal transmit distance at which the Antenna behaves just as prescribed by Squad's config.
/// </summary> /// </summary>
public virtual double nominalTransmitDistance public virtual double nominalTransmitDistance
{ {
get; get;
set; set;
} }
   
/// <summary> /// <summary>
/// The maximum distance at which this relay can operate. /// The maximum distance at which this relay can operate.
/// </summary> /// </summary>
/// <value>The max transmit distance.</value> /// <value>The max transmit distance.</value>
public virtual double maxTransmitDistance public virtual double maxTransmitDistance
{ {
get; get;
set; set;
} }
   
/// <summary> /// <summary>
/// Determines whether this instance can transmit. /// Determines whether this instance can transmit.
/// </summary> /// </summary>
/// <returns><c>true</c> if this instance can transmit; otherwise, <c>false</c>.</returns> /// <returns><c>true</c> if this instance can transmit; otherwise, <c>false</c>.</returns>
public virtual bool CanTransmit() public virtual bool CanTransmit()
{ {
return this.canTransmit; return this.canTransmit;
} }
   
/// <summary> /// <summary>
/// Finds the nearest relay. /// Finds the nearest relay.
/// </summary> /// </summary>
/// <returns>The nearest relay or null, if no relays in range.</returns> /// <returns>The nearest relay or null, if no relays in range.</returns>
public void FindNearestRelay() public void FindNearestRelay()
{ {
if (!FlightGlobals.ready) if (!FlightGlobals.ready)
{ {
return; return;
} }
   
Tools.DebugLogger log; Tools.DebugLogger log;
#if DEBUG #if DEBUG
log = Tools.DebugLogger.New(this); log = Tools.DebugLogger.New(this);
#endif #endif
   
#if BENCH #if BENCH
this.performanceTimer.Restart(); this.performanceTimer.Restart();
   
long startVesselLoopTicks; long startVesselLoopTicks;
long totalVesselLoopTicks; long totalVesselLoopTicks;
   
string slowestLOSVesselName = string.Empty; string slowestLOSVesselName = string.Empty;
long slowestLOSVesselTicks = long.MinValue; long slowestLOSVesselTicks = long.MinValue;
long startLOSVesselTicks; long startLOSVesselTicks;
long totalLOSVesselTicks; long totalLOSVesselTicks;
   
string slowestCircularVesselName = string.Empty; string slowestCircularVesselName = string.Empty;
long slowestCircularVesselTicks = long.MinValue; long slowestCircularVesselTicks = long.MinValue;
long startCircularVesselTicks; long startCircularVesselTicks;
long totalCircularVesselTicks; long totalCircularVesselTicks;
   
long startKerbinLOSTicks; long startKerbinLOSTicks;
long totalKerbinLOSTicks; long totalKerbinLOSTicks;
long statusResolutionTicks; long statusResolutionTicks;
   
ushort usefulVesselCount = 0; ushort usefulVesselCount = 0;
#endif #endif
   
log.AppendFormat("{0}: Target search started).", this.ToString()); log.AppendFormat("{0}: Target search started).", this.ToString());
   
#if DEBUG #if DEBUG
try { try {
#endif #endif
   
// Declare a bunch of variables we'll be using. // Declare a bunch of variables we'll be using.
CelestialBody bodyOccludingBestOccludedRelay = null; CelestialBody bodyOccludingBestOccludedRelay = null;
IAntennaRelay needle; IAntennaRelay needle;
   
double nearestRelaySqrQuotient = double.PositiveInfinity; double nearestRelaySqrQuotient = double.PositiveInfinity;
double bestOccludedSqrQuotient = double.PositiveInfinity; double bestOccludedSqrQuotient = double.PositiveInfinity;
   
double potentialSqrDistance; double potentialSqrDistance;
double maxLinkSqrDistance; double maxLinkSqrDistance;
double potentialSqrQuotient; double potentialSqrQuotient;
   
double kerbinSqrDistance; double kerbinSqrDistance;
double kerbinSqrQuotient; double kerbinSqrQuotient;
   
bool isCircular; bool isCircular;
int iterCount; int iterCount;
   
// Blank everything we're trying to find before the search. // Blank everything we're trying to find before the search.
this.firstOccludingBody = null; this.firstOccludingBody = null;
this.bestOccludedRelay = null; this.bestOccludedRelay = null;
this.targetRelay = null; this.targetRelay = null;
this.nearestRelay = null; this.nearestRelay = null;
   
// Default to KerbinDirect = true in case something in here doesn't work right. // Default to KerbinDirect = true in case something in here doesn't work right.
this.KerbinDirect = true; this.KerbinDirect = true;
   
/* /*
* Loop through the useful relays as determined by ARFlightController and check each for line of sight and * 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 * 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. * can transmit, also stashing the "best" relay outside of line of sight for failure report.
* */ * */
IAntennaRelay potentialBestRelay; IAntennaRelay potentialBestRelay;
CelestialBody fob; CelestialBody fob;
   
#if BENCH #if BENCH
startVesselLoopTicks = performanceTimer.ElapsedTicks; startVesselLoopTicks = performanceTimer.ElapsedTicks;
#endif #endif
   
for (int rIdx = 0; rIdx < ARFlightController.UsefulRelays.Count; rIdx++) for (int rIdx = 0; rIdx < ARFlightController.UsefulRelays.Count; rIdx++)
{ {
potentialBestRelay = ARFlightController.UsefulRelays[rIdx]; potentialBestRelay = ARFlightController.UsefulRelays[rIdx];
log.AppendFormat("\n\t\tgot best vessel relay {0}", log.AppendFormat("\n\tgot useful relay {0}",
potentialBestRelay == null ? "null" : potentialBestRelay.ToString()); potentialBestRelay == null ? "null" : potentialBestRelay.ToString());
   
if (potentialBestRelay == null) if (potentialBestRelay == null)
{ {
log.Append("\n\t\t...skipping null relay"); log.Append("\n\t...skipping null relay");
continue; continue;
} }
   
if (potentialBestRelay == this || potentialBestRelay.vessel == this.vessel) 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; continue;
} }
   
#if BENCH #if BENCH
usefulVesselCount++; usefulVesselCount++;
#endif #endif
   
// Find the distance from here to the vessel... // Find the distance from here to the vessel...
log.Append("\n\tgetting distance to potential vessel"); log.Append("\n\tgetting distance to potential vessel");
potentialSqrDistance = this.SqrDistanceTo(potentialBestRelay); potentialSqrDistance = this.SqrDistanceTo(potentialBestRelay);
log.Append("\n\tgetting best vessel relay"); log.Append("\n\tgetting best vessel relay");
   
log.Append("\n\tgetting max link distance to potential relay"); log.Append("\n\tgetting max link distance to potential relay");
   
if (ARConfiguration.UseAdditiveRanges) if (ARConfiguration.UseAdditiveRanges)
{ {
maxLinkSqrDistance = this.maxTransmitDistance * potentialBestRelay.maxTransmitDistance; maxLinkSqrDistance = this.maxTransmitDistance * potentialBestRelay.maxTransmitDistance;
} }
else else
{ {
maxLinkSqrDistance = this.maxTransmitDistance * this.maxTransmitDistance; maxLinkSqrDistance = this.maxTransmitDistance * this.maxTransmitDistance;
} }
   
log.AppendFormat("\n\tmax link distance: {0}", maxLinkSqrDistance); log.AppendFormat("\n\tmax link distance: {0}", maxLinkSqrDistance);
   
potentialSqrQuotient = potentialSqrDistance / maxLinkSqrDistance; potentialSqrQuotient = potentialSqrDistance / maxLinkSqrDistance;
   
#if BENCH #if BENCH
startLOSVesselTicks = performanceTimer.ElapsedTicks; startLOSVesselTicks = performanceTimer.ElapsedTicks;
#endif #endif
   
log.Append("\n\t\tdoing LOS check"); log.Append("\n\t\tdoing LOS check");
// Skip vessels to which we do not have line of sight. // Skip vessels to which we do not have line of sight.
if ( if (
ARConfiguration.RequireLineOfSight && ARConfiguration.RequireLineOfSight &&
!this.vessel.hasLineOfSightTo(potentialBestRelay.vessel, out fob, ARConfiguration.RadiusRatio) !this.vessel.hasLineOfSightTo(potentialBestRelay.vessel, out fob, ARConfiguration.RadiusRatio)
) )
{ {
#if BENCH #if BENCH
totalLOSVesselTicks = performanceTimer.ElapsedTicks - startLOSVesselTicks; totalLOSVesselTicks = performanceTimer.ElapsedTicks - startLOSVesselTicks;
   
if (totalLOSVesselTicks > slowestLOSVesselTicks) if (totalLOSVesselTicks > slowestLOSVesselTicks)
{ {
slowestLOSVesselTicks = totalLOSVesselTicks; slowestLOSVesselTicks = totalLOSVesselTicks;
slowestLOSVesselName = vessel.vesselName; slowestLOSVesselName = vessel.vesselName;
} }
#endif #endif
   
log.Append("\n\t\t...failed LOS check"); log.Append("\n\t\t...failed LOS check");
   
log.AppendFormat("\n\t\t\t{0}: Relay {1} not in line of sight.", log.AppendFormat("\n\t\t\t{0}: Relay {1} not in line of sight.",
this.ToString(), potentialBestRelay); this.ToString(), potentialBestRelay);
log.AppendFormat("\n\t\t\tpotentialSqrDistance: {0}", potentialSqrDistance); log.AppendFormat("\n\t\t\tpotentialSqrDistance: {0}", potentialSqrDistance);
log.AppendFormat("\n\t\t\tbestOccludedSqrQuotient: {0}", bestOccludedSqrQuotient); log.AppendFormat("\n\t\t\tbestOccludedSqrQuotient: {0}", bestOccludedSqrQuotient);
log.AppendFormat("\n\t\t\tmaxTransmitSqrDistance: {0}", maxLinkSqrDistance); log.AppendFormat("\n\t\t\tmaxTransmitSqrDistance: {0}", maxLinkSqrDistance);
   
if ( if (
(potentialSqrQuotient < bestOccludedSqrQuotient) && (potentialSqrQuotient < bestOccludedSqrQuotient) &&
(potentialSqrQuotient <= 1d) && (potentialSqrQuotient <= 1d) &&
potentialBestRelay.CanTransmit() potentialBestRelay.CanTransmit()
) )
{ {
log.Append("\n\t\t...vessel is close enough to and potentialBestRelay can transmit"); log.Append("\n\t\t...vessel is close enough to and potentialBestRelay can transmit");
log.AppendFormat("\n\t\t...{0} found new best occluded relay {1}", this, potentialBestRelay); log.AppendFormat("\n\t\t...{0} found new best occluded relay {1}", this, potentialBestRelay);
   
this.bestOccludedRelay = potentialBestRelay; this.bestOccludedRelay = potentialBestRelay;
bodyOccludingBestOccludedRelay = fob; bodyOccludingBestOccludedRelay = fob;
bestOccludedSqrQuotient = potentialSqrQuotient; bestOccludedSqrQuotient = potentialSqrQuotient;
} }
else else
{ {
log.Append("\n\t\t...vessel is not close enough to check for occluded relays, carrying on"); log.Append("\n\t\t...vessel is not close enough to check for occluded relays, carrying on");
} }
continue; continue;
} }
#if BENCH #if BENCH
else else
{ {
totalLOSVesselTicks = performanceTimer.ElapsedTicks - startLOSVesselTicks; totalLOSVesselTicks = performanceTimer.ElapsedTicks - startLOSVesselTicks;
} }
   
if (totalLOSVesselTicks > slowestLOSVesselTicks) if (totalLOSVesselTicks > slowestLOSVesselTicks)
{ {
slowestLOSVesselTicks = totalLOSVesselTicks; slowestLOSVesselTicks = totalLOSVesselTicks;
slowestLOSVesselName = vessel.vesselName; slowestLOSVesselName = vessel.vesselName;
} }
#endif #endif
   
log.Append("\n\t\t...passed LOS check"); 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. * ...so that we can skip the vessel if it is further away than a vessel we've already checked.
* */ * */
if (potentialSqrQuotient > nearestRelaySqrQuotient) if (potentialSqrQuotient > nearestRelaySqrQuotient)
{ {
log.AppendFormat("\n\t{0}: Relay {1} discarded because it is farther than another the nearest relay.", log.AppendFormat("\n\t{0}: Relay {1} discarded because it is farther than another the nearest relay.",
this.ToString(), this.ToString(),
potentialBestRelay potentialBestRelay
); );
continue; continue;
} }
   
log.Append("\n\t\t...passed distance check"); log.Append("\n\t\t...passed distance check");
   
if (potentialBestRelay.CanTransmit()) if (potentialBestRelay.CanTransmit())
{ {
#if BENCH #if BENCH
startCircularVesselTicks = performanceTimer.ElapsedTicks; startCircularVesselTicks = performanceTimer.ElapsedTicks;
#endif #endif
   
needle = potentialBestRelay; needle = potentialBestRelay;
isCircular = false; isCircular = false;
   
iterCount = 0; iterCount = 0;
while (needle != null) while (needle != null)
{ {
iterCount++; iterCount++;
   
if (needle.KerbinDirect) if (needle.KerbinDirect)
{ {
break; break;
} }
   
if (needle.targetRelay == null) if (needle.targetRelay == null)
{ {
break; break;
} }
   
if (needle.targetRelay.vessel == this.vessel || needle == this.moduleRef) if (needle.targetRelay.vessel == this.vessel || needle == this.moduleRef)
{ {
isCircular = true; isCircular = true;
break; break;
} }
   
// Avoid infinite loops when we're not catching things right. // Avoid infinite loops when we're not catching things right.
if (iterCount > FlightGlobals.Vessels.Count) if (iterCount > FlightGlobals.Vessels.Count)
{ {
Tools.PostErrorMessage( this.LogError(
"[{0}] iterCount exceeded while checking for circular network; assuming it is circular" + "iterCount exceeded while checking for circular network; assuming it is circular" +
"\n\tneedle={1}" + "\n\tneedle={0}" +
"\n\tthis.moduleRef={2}", "\n\tthis.moduleRef={1}",
this,  
needle == null ? "null" : string.Format( needle == null ? "null" : string.Format(
"{0}, needle.KerbinDirect={1}, needle.targetRelay={2}", "{0}, needle.KerbinDirect={1}, needle.targetRelay={2}",
needle, needle.KerbinDirect, needle.targetRelay == null ? "null" : string.Format( needle, needle.KerbinDirect, needle.targetRelay == null ? "null" : string.Format(
"{0}\n\tneedle.targetRelay.vessel={1}", "{0}\n\tneedle.targetRelay.vessel={1}",
needle.targetRelay, needle.targetRelay,
needle.targetRelay.vessel == null ? needle.targetRelay.vessel == null ?
"null" : needle.targetRelay.vessel.vesselName "null" : needle.targetRelay.vessel.vesselName
) )
), ),
this.moduleRef == null ? "null" : this.moduleRef.ToString() this.moduleRef == null ? "null" : this.moduleRef.ToString()
); );
isCircular = true; isCircular = true;
break; break;
} }
   
needle = needle.targetRelay; needle = needle.targetRelay;
} }
   
if (!isCircular) if (!isCircular)
{ {
nearestRelaySqrQuotient = potentialSqrQuotient; nearestRelaySqrQuotient = potentialSqrQuotient;
this.nearestRelay = potentialBestRelay; this.nearestRelay = potentialBestRelay;
   
log.AppendFormat("\n\t{0}: found new nearest relay {1} ({2}m²)", log.AppendFormat("\n\t{0}: found new nearest relay {1} ({2}m²)",
this.ToString(), this.ToString(),
this.nearestRelay.ToString(), this.nearestRelay.ToString(),
Math.Sqrt(nearestRelaySqrQuotient) Math.Sqrt(nearestRelaySqrQuotient)
); );
} }
else else
{ {
log.AppendFormat("\n\t\t...connection to {0} would result in a circular network, skipping", log.AppendFormat("\n\t\t...connection to {0} would result in a circular network, skipping",
potentialBestRelay potentialBestRelay
); );
} }
   
#if BENCH #if BENCH
totalCircularVesselTicks = performanceTimer.ElapsedTicks - startCircularVesselTicks; totalCircularVesselTicks = performanceTimer.ElapsedTicks - startCircularVesselTicks;
   
if (totalCircularVesselTicks > slowestCircularVesselTicks) if (totalCircularVesselTicks > slowestCircularVesselTicks)
{ {
slowestCircularVesselName = vessel.vesselName; slowestCircularVesselName = vessel.vesselName;
slowestCircularVesselTicks = totalCircularVesselTicks; slowestCircularVesselTicks = totalCircularVesselTicks;
} }
   
#endif #endif
} }
} }
   
#if BENCH #if BENCH
totalVesselLoopTicks = performanceTimer.ElapsedTicks - startVesselLoopTicks; totalVesselLoopTicks = performanceTimer.ElapsedTicks - startVesselLoopTicks;
#endif #endif
   
CelestialBody bodyOccludingKerbin = null; CelestialBody bodyOccludingKerbin = null;
   
kerbinSqrDistance = this.vessel.DistanceTo(Kerbin) - Kerbin.Radius; kerbinSqrDistance = this.vessel.DistanceTo(Kerbin) - Kerbin.Radius;
kerbinSqrDistance *= kerbinSqrDistance; kerbinSqrDistance *= kerbinSqrDistance;
   
if (ARConfiguration.UseAdditiveRanges) if (ARConfiguration.UseAdditiveRanges)
{ {
kerbinSqrQuotient = kerbinSqrDistance / kerbinSqrQuotient = kerbinSqrDistance /
(this.maxTransmitDistance * ARConfiguration.KerbinRelayRange); (this.maxTransmitDistance * ARConfiguration.KerbinRelayRange);
} }
else else
{ {
kerbinSqrQuotient = kerbinSqrDistance / kerbinSqrQuotient = kerbinSqrDistance /
(this.maxTransmitDistance * this.maxTransmitDistance); (this.maxTransmitDistance * this.maxTransmitDistance);
} }
   
log.AppendFormat("\n{0} ({1}): Search done, figuring status.", this.ToString(), this.GetType().Name); log.AppendFormat("\n{0} ({1}): Search done, figuring status.", this.ToString(), this.GetType().Name);
log.AppendFormat( log.AppendFormat(
"\n{0}: nearestRelay={1} ({2}m²)), bestOccludedRelay={3} ({4}m²), kerbinSqrDistance={5}m²)", "\n{0}: nearestRelay={1} ({2})), bestOccludedRelay={3} ({4}), kerbinSqrDistance={5}m²)",
this, this,
this.nearestRelay == null ? "null" : this.nearestRelay.ToString(), this.nearestRelay == null ? "null" : this.nearestRelay.ToString(),
nearestRelaySqrQuotient, nearestRelaySqrQuotient,
this.bestOccludedRelay == null ? "null" : this.bestOccludedRelay.ToString(), this.bestOccludedRelay == null ? "null" : this.bestOccludedRelay.ToString(),
bestOccludedSqrQuotient, bestOccludedSqrQuotient,
kerbinSqrDistance kerbinSqrDistance
); );
   
#if BENCH #if BENCH
startKerbinLOSTicks = this.performanceTimer.ElapsedTicks; startKerbinLOSTicks = this.performanceTimer.ElapsedTicks;
#endif #endif
   
// If we don't have LOS to Kerbin, focus on relays // If we don't have LOS to Kerbin, focus on relays
if ( if (
ARConfiguration.RequireLineOfSight && ARConfiguration.RequireLineOfSight &&
!this.vessel.hasLineOfSightTo(Kerbin, out bodyOccludingKerbin, ARConfiguration.RadiusRatio) !this.vessel.hasLineOfSightTo(Kerbin, out bodyOccludingKerbin, ARConfiguration.RadiusRatio)
) )
{ {
#if BENCH #if BENCH
totalKerbinLOSTicks = this.performanceTimer.ElapsedTicks - startKerbinLOSTicks; totalKerbinLOSTicks = this.performanceTimer.ElapsedTicks - startKerbinLOSTicks;
#endif #endif
log.AppendFormat("\n\tKerbin LOS is blocked by {0}.", bodyOccludingKerbin.bodyName); log.AppendFormat("\n\tKerbin LOS is blocked by {0}.", bodyOccludingKerbin.bodyName);
   
// nearestRelaySqrDistance will be infinity if all relays are occluded or none exist. // nearestRelaySqrDistance will be infinity if all relays are occluded or none exist.
// Therefore, this will only be true if a valid relay is in range. // Therefore, this will only be true if a valid relay is in range.
if (nearestRelaySqrQuotient <= 1d) if (nearestRelaySqrQuotient <= 1d)
{ {
log.AppendFormat("\n\t\tCan transmit to nearby relay {0} ({1} <= {2}).", log.AppendFormat("\n\t\tCan transmit to nearby relay {0} ({1} <= {2}).",
this.nearestRelay == null ? "null" : this.nearestRelay.ToString(), this.nearestRelay == null ? "null" : this.nearestRelay.ToString(),
nearestRelaySqrQuotient, 1d); nearestRelaySqrQuotient, 1d);
   
this.KerbinDirect = false; this.KerbinDirect = false;
this.canTransmit = true; this.canTransmit = true;
this.targetRelay = this.nearestRelay; this.targetRelay = this.nearestRelay;
} }
// If this isn't true, we can't transmit, but pick a second best of bestOccludedRelay and Kerbin anyway // If this isn't true, we can't transmit, but pick a second best of bestOccludedRelay and Kerbin anyway
else else
{ {
log.AppendFormat("\n\t\tCan't transmit to nearby relay {0} ({1} > {2}).", log.AppendFormat("\n\t\tCan't transmit to nearby relay {0} ({1} > {2}).",
this.nearestRelay == null ? "null" : this.nearestRelay.ToString(), this.nearestRelay == null ? "null" : this.nearestRelay.ToString(),
nearestRelaySqrQuotient, 1d); nearestRelaySqrQuotient, 1d);
   
this.canTransmit = false; this.canTransmit = false;
   
// If the best occluded relay is closer than Kerbin, check it against the nearest relay. // If the best occluded relay is closer than Kerbin, check it against the nearest relay.
// Since bestOccludedSqrDistance is infinity if there are no occluded relays, this is safe // Since bestOccludedSqrDistance is infinity if there are no occluded relays, this is safe
if (bestOccludedSqrQuotient < kerbinSqrQuotient) if (bestOccludedSqrQuotient < kerbinSqrQuotient)
{ {
log.AppendFormat("\n\t\t\tBest occluded relay is closer than Kerbin ({0} < {1})", log.AppendFormat("\n\t\t\tBest occluded relay is closer than Kerbin ({0} < {1})",
bestOccludedRelay, kerbinSqrDistance); bestOccludedRelay, kerbinSqrDistance);
this.KerbinDirect = false; this.KerbinDirect = false;
   
// If the nearest relay is closer than the best occluded relay, pick it. // If the nearest relay is closer than the best occluded relay, pick it.
// Since nearestRelaySqrDistane is infinity if there are no nearby relays, this is safe. // Since nearestRelaySqrDistane is infinity if there are no nearby relays, this is safe.
if (nearestRelaySqrQuotient < bestOccludedSqrQuotient) if (nearestRelaySqrQuotient < bestOccludedSqrQuotient)
{ {
log.AppendFormat("\n\t\t\t\t...but the nearest relay is closer ({0} < {1}), so picking it.", log.AppendFormat("\n\t\t\t\t...but the nearest relay is closer ({0} < {1}), so picking it.",
nearestRelaySqrQuotient, bestOccludedSqrQuotient); nearestRelaySqrQuotient, bestOccludedSqrQuotient);
this.targetRelay = this.nearestRelay; this.targetRelay = this.nearestRelay;
this.firstOccludingBody = null; this.firstOccludingBody = null;
} }
// Otherwise, target the best occluded relay. // Otherwise, target the best occluded relay.
else else
{ {
log.AppendFormat("\n\t\t\t\t...and closer than the nearest relay ({0} >= {1}), so picking it.", log.AppendFormat("\n\t\t\t\t...and closer than the nearest relay ({0} >= {1}), so picking it.",
nearestRelaySqrQuotient, bestOccludedSqrQuotient); nearestRelaySqrQuotient, bestOccludedSqrQuotient);
this.targetRelay = bestOccludedRelay; this.targetRelay = bestOccludedRelay;
this.firstOccludingBody = bodyOccludingBestOccludedRelay; this.firstOccludingBody = bodyOccludingBestOccludedRelay;
} }
} }
// Otherwise, check Kerbin against the nearest relay. // Otherwise, check Kerbin against the nearest relay.
// Since we have LOS, blank the first occluding body. // Since we have LOS, blank the first occluding body.
else else
{ {
log.AppendFormat("\n\t\t\tKerbin is closer than the best occluded relay ({0} >= {1})", log.AppendFormat("\n\t\t\tKerbin is closer than the best occluded relay ({0} >= {1})",
bestOccludedRelay, kerbinSqrDistance); bestOccludedRelay, kerbinSqrDistance);
// If the nearest relay is closer than Kerbin, pick it. // If the nearest relay is closer than Kerbin, pick it.
// Since nearestRelaySqrDistane is infinity if there are no nearby relays, this is safe. // Since nearestRelaySqrDistane is infinity if there are no nearby relays, this is safe.
if (nearestRelaySqrQuotient < kerbinSqrQuotient) if (nearestRelaySqrQuotient < kerbinSqrQuotient)
{ {
log.AppendFormat("\n\t\t\t\t...but the nearest relay is closer ({0} < {1}), so picking it.", log.AppendFormat("\n\t\t\t\t...but the nearest relay is closer ({0} < {1}), so picking it.",
nearestRelaySqrQuotient, kerbinSqrQuotient); nearestRelaySqrQuotient, kerbinSqrQuotient);
this.KerbinDirect = false; this.KerbinDirect = false;
this.firstOccludingBody = null; this.firstOccludingBody = null;
this.targetRelay = this.nearestRelay; this.targetRelay = this.nearestRelay;
} }
// Otherwise, pick Kerbin. // Otherwise, pick Kerbin.
else else
{ {
log.AppendFormat("\n\t\t\t\t...and closer than the nearest relay ({0} >= {1}), so picking it.", log.AppendFormat("\n\t\t\t\t...and closer than the nearest relay ({0} >= {1}), so picking it.",
nearestRelaySqrQuotient, kerbinSqrQuotient); nearestRelaySqrQuotient, kerbinSqrQuotient);
this.KerbinDirect = true; this.KerbinDirect = true;
this.firstOccludingBody = bodyOccludingKerbin; this.firstOccludingBody = bodyOccludingKerbin;
this.targetRelay = null; this.targetRelay = null;
} }
} }
} }
} }
// If we do have LOS to Kerbin, try to prefer the closest of nearestRelay and Kerbin // If we do have LOS to Kerbin, try to prefer the closest of nearestRelay and Kerbin
else else
{ {
#if BENCH #if BENCH
totalKerbinLOSTicks = this.performanceTimer.ElapsedTicks - startKerbinLOSTicks; totalKerbinLOSTicks = this.performanceTimer.ElapsedTicks - startKerbinLOSTicks;
#endif #endif
   
log.AppendFormat("\n\tKerbin is in LOS."); log.AppendFormat("\n\tKerbin is in LOS.");
   
// If the nearest relay is closer than Kerbin and in range, transmit to it. // If the nearest relay is closer than Kerbin and in range, transmit to it.
if (nearestRelaySqrQuotient <= 1d) if (nearestRelaySqrQuotient <= 1d)
{ {
log.AppendFormat("\n\t\tCan transmit to nearby relay {0} ({1} <= {2}).", log.AppendFormat("\n\t\tCan transmit to nearby relay {0} ({1} <= {2}).",
this.nearestRelay == null ? "null" : this.nearestRelay.ToString(), this.nearestRelay == null ? "null" : this.nearestRelay.ToString(),
nearestRelaySqrQuotient, 1d); nearestRelaySqrQuotient, 1d);
   
this.canTransmit = true; this.canTransmit = true;
   
// If the nearestRelay is closer than Kerbin, use it. // If the nearestRelay is closer than Kerbin, use it.
if (nearestRelaySqrQuotient < kerbinSqrQuotient) if (nearestRelaySqrQuotient < kerbinSqrQuotient)
{ {
log.AppendFormat("\n\t\t\tPicking relay {0} over Kerbin ({1} < {2}).", log.AppendFormat("\n\t\t\tPicking relay {0} over Kerbin ({1} < {2}).",
this.nearestRelay == null ? "null" : this.nearestRelay.ToString(), this.nearestRelay == null ? "null" : this.nearestRelay.ToString(),
nearestRelaySqrQuotient, kerbinSqrQuotient); nearestRelaySqrQuotient, kerbinSqrQuotient);
   
this.KerbinDirect = false; this.KerbinDirect = false;
this.targetRelay = this.nearestRelay; this.targetRelay = this.nearestRelay;
} }
// Otherwise, Kerbin is closer, so use it. // Otherwise, Kerbin is closer, so use it.
else else
{ {
log.AppendFormat("\n\t\t\tBut picking Kerbin over nearby relay {0} ({1} >= {2}).", log.AppendFormat("\n\t\t\tBut picking Kerbin over nearby relay {0} ({1} >= {2}).",
this.nearestRelay == null ? "null" : this.nearestRelay.ToString(), this.nearestRelay == null ? "null" : this.nearestRelay.ToString(),
nearestRelaySqrQuotient, kerbinSqrQuotient); nearestRelaySqrQuotient, kerbinSqrQuotient);
   
this.KerbinDirect = true; this.KerbinDirect = true;
this.targetRelay = null; this.targetRelay = null;
} }
} }
// If the nearest relay is out of range, we still need to check on Kerbin. // If the nearest relay is out of range, we still need to check on Kerbin.
else else
{ {
log.AppendFormat("\n\t\tCan't transmit to nearby relay {0} ({1} > {2}).", log.AppendFormat("\n\t\tCan't transmit to nearby relay {0} ({1} > {2}).",
this.nearestRelay == null ? "null" : this.nearestRelay.ToString(), this.nearestRelay == null ? "null" : this.nearestRelay.ToString(),
nearestRelaySqrQuotient, 1d); nearestRelaySqrQuotient, 1d);
   
// If Kerbin is in range, use it. // If Kerbin is in range, use it.
if (kerbinSqrQuotient <= 1d) if (kerbinSqrQuotient <= 1d)
{ {
log.AppendFormat("\n\t\t\tCan transmit to Kerbin ({0} <= {1}).", log.AppendFormat("\n\t\t\tCan transmit to Kerbin ({0} <= {1}).",
kerbinSqrQuotient, 1d); kerbinSqrQuotient, 1d);
   
this.canTransmit = true; this.canTransmit = true;
this.KerbinDirect = true; this.KerbinDirect = true;
this.targetRelay = null; this.targetRelay = null;
} }
// If Kerbin is out of range and the nearest relay is out of range, pick a second best between // If Kerbin is out of range and the nearest relay is out of range, pick a second best between
// Kerbin and bestOccludedRelay // Kerbin and bestOccludedRelay
else else
{ {
log.AppendFormat("\n\t\t\tCan't transmit to Kerbin ({0} > {1}).", log.AppendFormat("\n\t\t\tCan't transmit to Kerbin ({0} > {1}).",
kerbinSqrQuotient, 1d); kerbinSqrQuotient, 1d);
   
this.canTransmit = false; this.canTransmit = false;
   
// If the best occluded relay is closer than Kerbin, check it against the nearest relay. // If the best occluded relay is closer than Kerbin, check it against the nearest relay.
// Since bestOccludedSqrDistance is infinity if there are no occluded relays, this is safe // Since bestOccludedSqrDistance is infinity if there are no occluded relays, this is safe
if (bestOccludedSqrQuotient < kerbinSqrQuotient) if (bestOccludedSqrQuotient < kerbinSqrQuotient)
{ {
log.AppendFormat("\n\t\t\tBest occluded relay is closer than Kerbin ({0} < {1})", log.AppendFormat("\n\t\t\tBest occluded relay is closer than Kerbin ({0} < {1})",
bestOccludedRelay, kerbinSqrDistance); bestOccludedRelay, kerbinSqrDistance);
this.KerbinDirect = false; this.KerbinDirect = false;
   
// If the nearest relay is closer than the best occluded relay, pick it. // If the nearest relay is closer than the best occluded relay, pick it.
// Since nearestRelaySqrDistane is infinity if there are no nearby relays, this is safe. // Since nearestRelaySqrDistane is infinity if there are no nearby relays, this is safe.
if (nearestRelaySqrQuotient < bestOccludedSqrQuotient) if (nearestRelaySqrQuotient < bestOccludedSqrQuotient)
{ {
log.AppendFormat("\n\t\t\t\t...but the nearest relay is closer ({0} < {1}), so picking it.", log.AppendFormat("\n\t\t\t\t...but the nearest relay is closer ({0} < {1}), so picking it.",
nearestRelaySqrQuotient, bestOccludedSqrQuotient); nearestRelaySqrQuotient, bestOccludedSqrQuotient);
this.targetRelay = this.nearestRelay; this.targetRelay = this.nearestRelay;
this.firstOccludingBody = null; this.firstOccludingBody = null;
} }
// Otherwise, target the best occluded relay. // Otherwise, target the best occluded relay.
else else
{ {
log.AppendFormat("\n\t\t\t\t...and closer than the nearest relay ({0} >= {1}), so picking it.", log.AppendFormat("\n\t\t\t\t...and closer than the nearest relay ({0} >= {1}), so picking it.",
nearestRelaySqrQuotient, bestOccludedSqrQuotient); nearestRelaySqrQuotient, bestOccludedSqrQuotient);
this.targetRelay = bestOccludedRelay; this.targetRelay = bestOccludedRelay;
this.firstOccludingBody = bodyOccludingBestOccludedRelay; this.firstOccludingBody = bodyOccludingBestOccludedRelay;
} }
} }
// Otherwise, check Kerbin against the nearest relay. // Otherwise, check Kerbin against the nearest relay.
// Since we have LOS, blank the first occluding body. // Since we have LOS, blank the first occluding body.
else else
{ {
log.AppendFormat("\n\t\t\tKerbin is closer than the best occluded relay ({0} >= {1})", log.AppendFormat("\n\t\t\tKerbin is closer than the best occluded relay ({0} >= {1})",
bestOccludedRelay, kerbinSqrDistance); bestOccludedRelay, kerbinSqrDistance);
this.firstOccludingBody = null; this.firstOccludingBody = null;
   
// If the nearest relay is closer than Kerbin, pick it. // If the nearest relay is closer than Kerbin, pick it.
// Since nearestRelaySqrDistane is infinity if there are no nearby relays, this is safe. // Since nearestRelaySqrDistane is infinity if there are no nearby relays, this is safe.
if (nearestRelaySqrQuotient < kerbinSqrQuotient) if (nearestRelaySqrQuotient < kerbinSqrQuotient)
{ {
log.AppendFormat("\n\t\t\t\t...but the nearest relay is closer ({0} < {1}), so picking it.", log.AppendFormat("\n\t\t\t\t...but the nearest relay is closer ({0} < {1}), so picking it.",
nearestRelaySqrQuotient, kerbinSqrQuotient); nearestRelaySqrQuotient, kerbinSqrQuotient);
this.KerbinDirect = false; this.KerbinDirect = false;
this.targetRelay = this.nearestRelay; this.targetRelay = this.nearestRelay;
} }
// Otherwise, pick Kerbin. // Otherwise, pick Kerbin.
else else
{ {
log.AppendFormat("\n\t\t\t\t...and closer than the nearest relay ({0} >= {1}), so picking it.", log.AppendFormat("\n\t\t\t\t...and closer than the nearest relay ({0} >= {1}), so picking it.",
nearestRelaySqrQuotient, kerbinSqrQuotient); nearestRelaySqrQuotient, kerbinSqrQuotient);
this.KerbinDirect = true; this.KerbinDirect = true;
this.targetRelay = null; this.targetRelay = null;
} }
} }
} }
} }
} }
   
if (ARConfiguration.UseAdditiveRanges) if (ARConfiguration.UseAdditiveRanges)
{ {
if (this.KerbinDirect) if (this.KerbinDirect)
{ {
this.NominalLinkSqrDistance = this.nominalTransmitDistance * ARConfiguration.KerbinNominalRange; this.NominalLinkSqrDistance = this.nominalTransmitDistance * ARConfiguration.KerbinNominalRange;
this.MaximumLinkSqrDistance = this.maxTransmitDistance * ARConfiguration.KerbinRelayRange; this.MaximumLinkSqrDistance = this.maxTransmitDistance * ARConfiguration.KerbinRelayRange;
} }
else else
{ {
this.NominalLinkSqrDistance = this.nominalTransmitDistance * this.targetRelay.nominalTransmitDistance; this.NominalLinkSqrDistance = this.nominalTransmitDistance * this.targetRelay.nominalTransmitDistance;
this.MaximumLinkSqrDistance = this.maxTransmitDistance * this.targetRelay.maxTransmitDistance; this.MaximumLinkSqrDistance = this.maxTransmitDistance * this.targetRelay.maxTransmitDistance;
} }
} }
else else
{ {
this.NominalLinkSqrDistance = this.nominalTransmitDistance * this.nominalTransmitDistance; this.NominalLinkSqrDistance = this.nominalTransmitDistance * this.nominalTransmitDistance;
this.MaximumLinkSqrDistance = this.maxTransmitDistance * this.maxTransmitDistance; this.MaximumLinkSqrDistance = this.maxTransmitDistance * this.maxTransmitDistance;
} }
   
if (this.canTransmit) if (this.canTransmit)
{ {
if (this.CurrentLinkSqrDistance < this.NominalLinkSqrDistance) if (this.CurrentLinkSqrDistance < this.NominalLinkSqrDistance)
{ {
this.LinkStatus = ConnectionStatus.Optimal; this.LinkStatus = ConnectionStatus.Optimal;
} }
else else
{ {
this.LinkStatus = ConnectionStatus.Suboptimal; this.LinkStatus = ConnectionStatus.Suboptimal;
} }
} }
else else
{ {
this.LinkStatus = ConnectionStatus.None; this.LinkStatus = ConnectionStatus.None;
} }
   
#if BENCH #if BENCH
statusResolutionTicks = performanceTimer.ElapsedTicks - startKerbinLOSTicks - totalKerbinLOSTicks; statusResolutionTicks = performanceTimer.ElapsedTicks - startKerbinLOSTicks - totalKerbinLOSTicks;
#endif #endif
   
log.AppendFormat("\n{0}: Target search and status determination complete.", this.ToString()); log.AppendFormat("\n{0}: Target search and status determination complete.", this.ToString());
#if DEBUG #if DEBUG
} catch (Exception ex) { } catch (Exception ex) {
log.AppendFormat("\nCaught {0}: {1}\n{2}", ex.GetType().FullName, ex.ToString(), ex.StackTrace); log.AppendFormat("\nCaught {0}: {1}\n{2}", ex.GetType().FullName, ex.ToString(), ex.StackTrace);
#if QUIT_ON_EXCEPTION #if QUIT_ON_EXCEPTION
UnityEngine.Application.Quit(); UnityEngine.Application.Quit();
#endif #endif
} finally { } finally {
#endif #endif
log.Print(false); log.Print(false);
#if DEBUG #if DEBUG
} }
#endif #endif
   
#if BENCH #if BENCH
AntennaRelay.searchTimer += (ulong)this.performanceTimer.ElapsedTicks; AntennaRelay.searchTimer += (ulong)this.performanceTimer.ElapsedTicks;
AntennaRelay.searchCount++; AntennaRelay.searchCount++;
this.performanceTimer.Stop(); this.performanceTimer.Stop();
   
double averageSearchTime = (double)AntennaRelay.searchTimer / (double)AntennaRelay.searchCount; double averageSearchTime = (double)AntennaRelay.searchTimer / (double)AntennaRelay.searchCount;
   
if (AntennaRelay.searchCount >= 8000u / (ulong)ARConfiguration.UpdateDelay) if (AntennaRelay.searchCount >= 8000u / (ulong)ARConfiguration.UpdateDelay)
{ {
AntennaRelay.searchCount = 0u; AntennaRelay.searchCount = 0u;
AntennaRelay.searchTimer = 0u; AntennaRelay.searchTimer = 0u;
   
AntennaRelay.averager.AddItem(averageSearchTime); AntennaRelay.averager.AddItem(averageSearchTime);
AntennaRelay.doubleAverageTime = (long)(AntennaRelay.averager.Average * 2d); AntennaRelay.doubleAverageTime = (long)(AntennaRelay.averager.Average * 2d);
} }
   
if (this.performanceTimer.ElapsedTicks > AntennaRelay.doubleAverageTime) if (this.performanceTimer.ElapsedTicks > AntennaRelay.doubleAverageTime)
{ {
System.Text.StringBuilder sb = Tools.GetStringBuilder(); System.Text.StringBuilder sb = Tools.GetStringBuilder();
   
sb.AppendFormat(Tools.SIFormatter, "[AntennaRelay] FindNearestRelay search for {0}" + sb.AppendFormat(Tools.SIFormatter, "[AntennaRelay] FindNearestRelay search for {0}" +
" took significantly longer than average ({1:S3}s vs {2:S3}s)", " took significantly longer than average ({1:S3}s vs {2:S3}s)",
this.ToString(), this.ToString(),
(double)this.performanceTimer.ElapsedTicks / (double)System.Diagnostics.Stopwatch.Frequency, (double)this.performanceTimer.ElapsedTicks / (double)System.Diagnostics.Stopwatch.Frequency,
(double)AntennaRelay.averager.Average / (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", sb.AppendFormat(Tools.SIFormatter, "\n\tVessel loop time: {0:S3}s",
(double)totalVesselLoopTicks / (double)System.Diagnostics.Stopwatch.Frequency (double)totalVesselLoopTicks / (double)System.Diagnostics.Stopwatch.Frequency
); );
   
sb.AppendFormat(Tools.SIFormatter, "\n\t\tAverage vessel time for each of {1} vessels: {0:S3}s", 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)totalVesselLoopTicks / (double)System.Diagnostics.Stopwatch.Frequency /
(double)usefulVesselCount, (double)usefulVesselCount,
usefulVesselCount usefulVesselCount
); );
   
sb.AppendFormat(Tools.SIFormatter, "\n\t\tSlowest vessel LOS check: {0:S3}s to {1}", sb.AppendFormat(Tools.SIFormatter, "\n\t\tSlowest vessel LOS check: {0:S3}s to {1}",
(double)slowestLOSVesselTicks / (double)System.Diagnostics.Stopwatch.Frequency, (double)slowestLOSVesselTicks / (double)System.Diagnostics.Stopwatch.Frequency,
slowestLOSVesselName slowestLOSVesselName
); );
   
sb.AppendFormat(Tools.SIFormatter, "\n\t\tSlowest circular relay check: {0:S3}s for {1}", sb.AppendFormat(Tools.SIFormatter, "\n\t\tSlowest circular relay check: {0:S3}s for {1}",
(double)slowestCircularVesselTicks / (double)System.Diagnostics.Stopwatch.Frequency, (double)slowestCircularVesselTicks / (double)System.Diagnostics.Stopwatch.Frequency,
slowestCircularVesselName slowestCircularVesselName
); );
   
sb.AppendFormat(Tools.SIFormatter, "\n\tKerbin LOS check: {0:S3}s", sb.AppendFormat(Tools.SIFormatter, "\n\tKerbin LOS check: {0:S3}s",
(double)totalKerbinLOSTicks / (double)System.Diagnostics.Stopwatch.Frequency (double)totalKerbinLOSTicks / (double)System.Diagnostics.Stopwatch.Frequency
); );
   
sb.AppendFormat(Tools.SIFormatter, "\n\tStatus resolution check: {0:S3}s", sb.AppendFormat(Tools.SIFormatter, "\n\tStatus resolution check: {0:S3}s",
(double)statusResolutionTicks / (double)System.Diagnostics.Stopwatch.Frequency (double)statusResolutionTicks / (double)System.Diagnostics.Stopwatch.Frequency
); );
   
// sb.AppendFormat(Tools.SIFormatter, "", start) // sb.AppendFormat(Tools.SIFormatter, "", start)
   
Tools.PostWarningMessage(sb.ToString()); this.LogWarning(sb.ToString());
   
Tools.PutStringBuilder(sb); Tools.PutStringBuilder(sb);
} }
#endif #endif
} }
   
/// <summary> /// <summary>
/// Returns a <see cref="System.String"/> that represents the current <see cref="AntennaRange.AntennaRelay"/>. /// Returns a <see cref="System.String"/> that represents the current <see cref="AntennaRange.AntennaRelay"/>.
/// </summary> /// </summary>
/// <returns>A <see cref="System.String"/> that represents the current <see cref="AntennaRange.AntennaRelay"/>.</returns> /// <returns>A <see cref="System.String"/> that represents the current <see cref="AntennaRange.AntennaRelay"/>.</returns>
public override string ToString() public override string ToString()
{ {
if (this is ProtoAntennaRelay) if (this is ProtoAntennaRelay)
{ {
return (this as ProtoAntennaRelay).ToString(); return (this as ProtoAntennaRelay).ToString();
} }
return this.moduleRef.ToString(); return this.moduleRef.ToString();
} }
   
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="AntennaRange.AntennaRelay"/> class. /// Initializes a new instance of the <see cref="AntennaRange.AntennaRelay"/> class.
/// </summary> /// </summary>
/// <param name="module">The module reference underlying this AntennaRelay, /// <param name="module">The module reference underlying this AntennaRelay,
/// as an <see cref="AntennaRange.IAntennaRelay"/></param> /// as an <see cref="AntennaRange.IAntennaRelay"/></param>
public AntennaRelay(IAntennaRelay module) public AntennaRelay(IAntennaRelay module)
{ {
this.moduleRef = module; this.moduleRef = module;
   
#if BENCH #if BENCH
AntennaRelay.relayCount++; AntennaRelay.relayCount++;
#endif #endif
   
Tools.PostLogMessage("{0}: constructed {1}", this.GetType().Name, this.ToString()); this.LogDebug("{0}: constructed {1}", this.GetType().Name, this.ToString());
} }
} }
} }
   
   
// AntennaRange // AntennaRange
// //
// AssemblyInfo.cs // AssemblyInfo.cs
// //
// Copyright © 2014-2015, toadicus // Copyright © 2014-2015, toadicus
// All rights reserved. // All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without modification, // Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met: // are permitted provided that the following conditions are met:
// //
// 1. Redistributions of source code must retain the above copyright notice, // 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer. // this list of conditions and the following disclaimer.
// //
// 2. Redistributions in binary form must reproduce the above copyright notice, // 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation and/or other // this list of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution. // materials provided with the distribution.
// //
// 3. Neither the name of the copyright holder nor the names of its contributors may be used // 3. Neither the name of the copyright holder nor the names of its contributors may be used
// to endorse or promote products derived from this software without specific prior written permission. // to endorse or promote products derived from this software without specific prior written permission.
// //
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
   
[assembly: KSPAssemblyDependency("ToadicusTools", 0, 0)] [assembly: KSPAssemblyDependency("ToadicusTools", 0, 0)]
   
// Information about this assembly is defined by the following attributes. // Information about this assembly is defined by the following attributes.
// Change them to the values specific to your project. // Change them to the values specific to your project.
[assembly: AssemblyTitle("AntennaRange")] [assembly: AssemblyTitle("AntennaRange")]
[assembly: AssemblyDescription("Enforce and Encourage Antenna Diversity")] [assembly: AssemblyDescription("Enforce and Encourage Antenna Diversity")]
[assembly: AssemblyCopyright("toadicus")] [assembly: AssemblyCopyright("toadicus")]
// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
// The form "{Major}.{Minor}.*" will automatically update the build and revision, // The form "{Major}.{Minor}.*" will automatically update the build and revision,
// and "{Major}.{Minor}.{Build}.*" will update just the revision. // and "{Major}.{Minor}.{Build}.*" will update just the revision.
[assembly: AssemblyVersion("1.10.*")] [assembly: AssemblyVersion("1.10.1.*")]
// The following attributes are used to specify the signing key for the assembly, // The following attributes are used to specify the signing key for the assembly,
// if desired. See the Mono documentation for more information about signing. // if desired. See the Mono documentation for more information about signing.
//[assembly: AssemblyDelaySign(false)] //[assembly: AssemblyDelaySign(false)]
//[assembly: AssemblyKeyFile("")] //[assembly: AssemblyKeyFile("")]
   
// AntennaRange // AntennaRange
// //
// ProtoAntennaRelay.cs // ProtoAntennaRelay.cs
// //
// Copyright © 2014-2015, toadicus // Copyright © 2014-2015, toadicus
// All rights reserved. // All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without modification, // Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met: // are permitted provided that the following conditions are met:
// //
// 1. Redistributions of source code must retain the above copyright notice, // 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer. // this list of conditions and the following disclaimer.
// //
// 2. Redistributions in binary form must reproduce the above copyright notice, // 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation and/or other // this list of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution. // materials provided with the distribution.
// //
// 3. Neither the name of the copyright holder nor the names of its contributors may be used // 3. Neither the name of the copyright holder nor the names of its contributors may be used
// to endorse or promote products derived from this software without specific prior written permission. // to endorse or promote products derived from this software without specific prior written permission.
// //
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   
using KSP; using KSP;
using System; using System;
using ToadicusTools; using ToadicusTools;
   
namespace AntennaRange namespace AntennaRange
{ {
/// <summary> /// <summary>
/// Wrapper class for ProtoPartModuleSnapshot extending AntennaRelay and implementing IAntennaRelay. /// Wrapper class for ProtoPartModuleSnapshot extending AntennaRelay and implementing IAntennaRelay.
/// This is used for finding relays in unloaded Vessels. /// This is used for finding relays in unloaded Vessels.
/// </summary> /// </summary>
public class ProtoAntennaRelay : AntennaRelay, IAntennaRelay public class ProtoAntennaRelay : AntennaRelay, IAntennaRelay
{ {
// Stores the prototype part so we can make sure we haven't exploded or so. // Stores the prototype part so we can make sure we haven't exploded or so.
private ProtoPartSnapshot protoPart; private ProtoPartSnapshot protoPart;
   
/// <summary> /// <summary>
/// Gets the parent Vessel. /// Gets the parent Vessel.
/// </summary> /// </summary>
public override Vessel vessel public override Vessel vessel
{ {
get get
{ {
if ( if (
this.protoPart != null && this.protoPart != null &&
this.protoPart.pVesselRef != null && this.protoPart.pVesselRef != null &&
this.protoPart.pVesselRef.vesselRef != null this.protoPart.pVesselRef.vesselRef != null
) )
{ {
return this.protoPart.pVesselRef.vesselRef; return this.protoPart.pVesselRef.vesselRef;
} }
else else
{ {
Tools.PostErrorMessage("{0}: Could not fetch vessel! {1}{2}{3}", this.LogError("Could not fetch vessel! {0}{1}{2}",
this.ToString(),  
this.protoPart == null ? "\n\tprotoPart=null" : string.Empty, this.protoPart == null ? "\n\tprotoPart=null" : string.Empty,
this.protoPart != null && this.protoPart.pVesselRef == null ? this.protoPart != null && this.protoPart.pVesselRef == null ?
"\n\tthis.protoPart.pVesselRef=null" : string.Empty, "\n\tthis.protoPart.pVesselRef=null" : string.Empty,
this.protoPart != null && this.protoPart.pVesselRef != null && this.protoPart != null && this.protoPart.pVesselRef != null &&
this.protoPart.pVesselRef.vesselRef == null ? this.protoPart.pVesselRef.vesselRef == null ?
"\n\tthis.protoPart.pVesselRef.vesselRef=null" : string.Empty "\n\tthis.protoPart.pVesselRef.vesselRef=null" : string.Empty
); );
return null; return null;
} }
} }
} }
   
/// <summary> /// <summary>
/// Gets the nominal transmit distance at which the Antenna behaves just as prescribed by Squad's config. /// Gets the nominal transmit distance at which the Antenna behaves just as prescribed by Squad's config.
/// </summary> /// </summary>
public override double nominalTransmitDistance public override double nominalTransmitDistance
{ {
get get
{ {
return this.moduleRef.nominalTransmitDistance; return this.moduleRef.nominalTransmitDistance;
} }
} }
   
/// <summary> /// <summary>
/// The maximum distance at which this relay can operate. /// The maximum distance at which this relay can operate.
/// </summary> /// </summary>
public override double maxTransmitDistance public override double maxTransmitDistance
{ {
get get
{ {
return moduleRef.maxTransmitDistance; return moduleRef.maxTransmitDistance;
} }
} }
   
/// <summary> /// <summary>
/// Gets the underlying part's title. /// Gets the underlying part's title.
/// </summary> /// </summary>
/// <value>The title.</value> /// <value>The title.</value>
public string Title public string Title
{ {
get get
{ {
if (this.protoPart != null && this.protoPart.partInfo != null) if (this.protoPart != null && this.protoPart.partInfo != null)
{ {
return this.protoPart.partInfo.title; return this.protoPart.partInfo.title;
} }
   
return string.Empty; return string.Empty;
} }
} }
   
/// <summary> /// <summary>
/// Determines whether this instance can transmit. /// Determines whether this instance can transmit.
/// <c>true</c> if this instance can transmit; otherwise, <c>false</c>. /// <c>true</c> if this instance can transmit; otherwise, <c>false</c>.
/// </summary> /// </summary>
public override bool CanTransmit() public override bool CanTransmit()
{ {
PartStates partState = (PartStates)this.protoPart.state; PartStates partState = (PartStates)this.protoPart.state;
if (partState == PartStates.DEAD || partState == PartStates.DEACTIVATED) if (partState == PartStates.DEAD || partState == PartStates.DEACTIVATED)
{ {
Tools.PostDebugMessage(string.Format( Tools.PostDebugMessage(string.Format(
"{0}: {1} on {2} cannot transmit: {3}", "{0}: {1} on {2} cannot transmit: {3}",
this.GetType().Name, this.GetType().Name,
this.Title, this.Title,
this.vessel.vesselName, this.vessel.vesselName,
Enum.GetName(typeof(PartStates), partState) Enum.GetName(typeof(PartStates), partState)
)); ));
return false; return false;
} }
return base.CanTransmit(); return base.CanTransmit();
} }
   
/// <summary> /// <summary>
/// Returns a <see cref="System.String"/> that represents the current <see cref="AntennaRange.ProtoAntennaRelay"/>. /// Returns a <see cref="System.String"/> that represents the current <see cref="AntennaRange.ProtoAntennaRelay"/>.
/// </summary> /// </summary>
/// <returns>A <see cref="System.String"/> that represents the current <see cref="AntennaRange.ProtoAntennaRelay"/>.</returns> /// <returns>A <see cref="System.String"/> that represents the current <see cref="AntennaRange.ProtoAntennaRelay"/>.</returns>
public override string ToString() public override string ToString()
{ {
System.Text.StringBuilder sb = Tools.GetStringBuilder(); System.Text.StringBuilder sb = Tools.GetStringBuilder();
   
sb.Append(this.Title); sb.Append(this.Title);
   
if (this.protoPart != null && this.protoPart.pVesselRef != null) if (this.protoPart != null && this.protoPart.pVesselRef != null)
{ {
sb.AppendFormat(" on {0}", this.protoPart.pVesselRef.vesselName); sb.AppendFormat(" on {0}", this.protoPart.pVesselRef.vesselName);
} }
   
Tools.PutStringBuilder(sb); Tools.PutStringBuilder(sb);
   
return sb.ToString(); return sb.ToString();
} }
   
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="AntennaRange.AntennaRelay"/> class. /// Initializes a new instance of the <see cref="AntennaRange.AntennaRelay"/> class.
/// </summary> /// </summary>
/// <param name="prefabRelay">The module reference underlying this AntennaRelay, /// <param name="prefabRelay">The module reference underlying this AntennaRelay,
/// as an <see cref="AntennaRange.IAntennaRelay"/></param> /// as an <see cref="AntennaRange.IAntennaRelay"/></param>
/// <param name="pps">The prototype partreference on which the module resides.</param> /// <param name="pps">The prototype partreference on which the module resides.</param>
public ProtoAntennaRelay(IAntennaRelay prefabRelay, ProtoPartSnapshot pps) : base(prefabRelay) public ProtoAntennaRelay(IAntennaRelay prefabRelay, ProtoPartSnapshot pps) : base(prefabRelay)
{ {
this.protoPart = pps; this.protoPart = pps;
   
Tools.PostLogMessage("{0}: constructed {1}", this.GetType().Name, this.ToString()); this.Log("constructed ({0})", this.GetType().Name);
} }
} }
} }
   
   
// AntennaRange // AntennaRange
// //
// Extensions.cs // Extensions.cs
// //
// Copyright © 2014-2015, toadicus // Copyright © 2014-2015, toadicus
// All rights reserved. // All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without modification, // Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met: // are permitted provided that the following conditions are met:
// //
// 1. Redistributions of source code must retain the above copyright notice, // 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer. // this list of conditions and the following disclaimer.
// //
// 2. Redistributions in binary form must reproduce the above copyright notice, // 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation and/or other // this list of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution. // materials provided with the distribution.
// //
// 3. Neither the name of the copyright holder nor the names of its contributors may be used // 3. Neither the name of the copyright holder nor the names of its contributors may be used
// to endorse or promote products derived from this software without specific prior written permission. // to endorse or promote products derived from this software without specific prior written permission.
// //
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using ToadicusTools; using ToadicusTools;
   
namespace AntennaRange namespace AntennaRange
{ {
/// <summary> /// <summary>
/// A class of utility extensions for Vessels and Relays to help find a relay path back to Kerbin. /// A class of utility extensions for Vessels and Relays to help find a relay path back to Kerbin.
/// </summary> /// </summary>
public static class RelayExtensions public static class RelayExtensions
{ {
/// <summary> /// <summary>
/// Returns the distance between two IAntennaRelays. /// Returns the distance between two IAntennaRelays.
/// </summary> /// </summary>
/// <param name="relayOne">Relay one.</param> /// <param name="relayOne">Relay one.</param>
/// <param name="relayTwo">Relay two.</param> /// <param name="relayTwo">Relay two.</param>
public static double DistanceTo(this IAntennaRelay relayOne, IAntennaRelay relayTwo) public static double DistanceTo(this IAntennaRelay relayOne, IAntennaRelay relayTwo)
{ {
return relayOne.vessel.DistanceTo(relayTwo.vessel); return relayOne.vessel.DistanceTo(relayTwo.vessel);
} }
   
/// <summary> /// <summary>
/// Returns the distance from this IAntennaRelay to the given CelestialBody /// Returns the distance from this IAntennaRelay to the given CelestialBody
/// </summary> /// </summary>
/// <param name="relay">Relay.</param> /// <param name="relay">Relay.</param>
/// <param name="body">Body.</param> /// <param name="body">Body.</param>
public static double SqrDistanceTo(this IAntennaRelay relay, CelestialBody body) public static double SqrDistanceTo(this IAntennaRelay relay, CelestialBody body)
{ {
double range = relay.vessel.DistanceTo(body) - body.Radius; double range = relay.vessel.DistanceTo(body) - body.Radius;
   
return range * range; return range * range;
} }
   
/// <summary> /// <summary>
/// Returns the distance between two IAntennaRelays. /// Returns the distance between two IAntennaRelays.
/// </summary> /// </summary>
/// <param name="relayOne">Relay one.</param> /// <param name="relayOne">Relay one.</param>
/// <param name="relayTwo">Relay two.</param> /// <param name="relayTwo">Relay two.</param>
public static double SqrDistanceTo(this IAntennaRelay relayOne, IAntennaRelay relayTwo) public static double SqrDistanceTo(this IAntennaRelay relayOne, IAntennaRelay relayTwo)
{ {
return relayOne.vessel.sqrDistanceTo(relayTwo.vessel); return relayOne.vessel.sqrDistanceTo(relayTwo.vessel);
} }
   
/// <summary> /// <summary>
/// Returns the distance from this IAntennaRelay to the given CelestialBody /// Returns the distance from this IAntennaRelay to the given CelestialBody
/// </summary> /// </summary>
/// <param name="relay">Relay.</param> /// <param name="relay">Relay.</param>
/// <param name="body">Body.</param> /// <param name="body">Body.</param>
public static double DistanceTo(this IAntennaRelay relay, CelestialBody body) public static double DistanceTo(this IAntennaRelay relay, CelestialBody body)
{ {
double range = relay.vessel.DistanceTo(body) - body.Radius; double range = relay.vessel.DistanceTo(body) - body.Radius;
   
return range; return range;
} }
   
/// <summary> /// <summary>
/// Returns the distance between this IAntennaRelay and a Vessel /// Returns the distance between this IAntennaRelay and a Vessel
/// </summary> /// </summary>
/// <param name="relay">This <see cref="IAntennaRelay"/></param> /// <param name="relay">This <see cref="IAntennaRelay"/></param>
/// <param name="Vessel">A <see cref="Vessel"/></param> /// <param name="Vessel">A <see cref="Vessel"/></param>
public static double DistanceTo(this AntennaRelay relay, Vessel Vessel) public static double DistanceTo(this AntennaRelay relay, Vessel Vessel)
{ {
return relay.vessel.DistanceTo(Vessel); return relay.vessel.DistanceTo(Vessel);
} }
   
/// <summary> /// <summary>
/// Returns the distance between this IAntennaRelay and a CelestialBody /// Returns the distance between this IAntennaRelay and a CelestialBody
/// </summary> /// </summary>
/// <param name="relay">This <see cref="IAntennaRelay"/></param> /// <param name="relay">This <see cref="IAntennaRelay"/></param>
/// <param name="body">A <see cref="CelestialBody"/></param> /// <param name="body">A <see cref="CelestialBody"/></param>
public static double DistanceTo(this AntennaRelay relay, CelestialBody body) public static double DistanceTo(this AntennaRelay relay, CelestialBody body)
{ {
return relay.vessel.DistanceTo(body) - body.Radius; return relay.vessel.DistanceTo(body) - body.Radius;
} }
   
/// <summary> /// <summary>
/// Returns the distance between this IAntennaRelay and another IAntennaRelay /// Returns the distance between this IAntennaRelay and another IAntennaRelay
/// </summary> /// </summary>
/// <param name="relayOne">This <see cref="IAntennaRelay"/></param> /// <param name="relayOne">This <see cref="IAntennaRelay"/></param>
/// <param name="relayTwo">Another <see cref="IAntennaRelay"/></param> /// <param name="relayTwo">Another <see cref="IAntennaRelay"/></param>
public static double DistanceTo(this AntennaRelay relayOne, IAntennaRelay relayTwo) public static double DistanceTo(this AntennaRelay relayOne, IAntennaRelay relayTwo)
{ {
return relayOne.DistanceTo(relayTwo.vessel); return relayOne.DistanceTo(relayTwo.vessel);
} }
   
/// <summary> /// <summary>
/// Returns the square of the distance between this IAntennaRelay and a Vessel /// Returns the square of the distance between this IAntennaRelay and a Vessel
/// </summary> /// </summary>
/// <param name="relay">This <see cref="IAntennaRelay"/></param> /// <param name="relay">This <see cref="IAntennaRelay"/></param>
/// <param name="vessel">A <see cref="Vessel"/></param> /// <param name="vessel">A <see cref="Vessel"/></param>
public static double SqrDistanceTo(this AntennaRelay relay, Vessel vessel) public static double SqrDistanceTo(this AntennaRelay relay, Vessel vessel)
{ {
return relay.vessel.sqrDistanceTo(vessel); return relay.vessel.sqrDistanceTo(vessel);
} }
   
/// <summary> /// <summary>
/// Returns the square of the distance between this IAntennaRelay and a CelestialBody /// Returns the square of the distance between this IAntennaRelay and a CelestialBody
/// </summary> /// </summary>
/// <param name="relay">This <see cref="IAntennaRelay"/></param> /// <param name="relay">This <see cref="IAntennaRelay"/></param>
/// <param name="body">A <see cref="CelestialBody"/></param> /// <param name="body">A <see cref="CelestialBody"/></param>
public static double SqrDistanceTo(this AntennaRelay relay, CelestialBody body) public static double SqrDistanceTo(this AntennaRelay relay, CelestialBody body)
{ {
double dist = (relay.vessel.GetWorldPos3D() - body.position).magnitude - body.Radius; double dist = (relay.vessel.GetWorldPos3D() - body.position).magnitude - body.Radius;
   
return dist * dist; return dist * dist;
} }
   
/// <summary> /// <summary>
/// Returns the square of the distance between this IAntennaRelay and another IAntennaRelay /// Returns the square of the distance between this IAntennaRelay and another IAntennaRelay
/// </summary> /// </summary>
/// <param name="relayOne">This <see cref="IAntennaRelay"/></param> /// <param name="relayOne">This <see cref="IAntennaRelay"/></param>
/// <param name="relayTwo">Another <see cref="IAntennaRelay"/></param> /// <param name="relayTwo">Another <see cref="IAntennaRelay"/></param>
public static double SqrDistanceTo(this AntennaRelay relayOne, IAntennaRelay relayTwo) public static double SqrDistanceTo(this AntennaRelay relayOne, IAntennaRelay relayTwo)
{ {
return relayOne.vessel.sqrDistanceTo(relayTwo.vessel); return relayOne.vessel.sqrDistanceTo(relayTwo.vessel);
} }
   
/// <summary> /// <summary>
/// Returns all of the PartModules or ProtoPartModuleSnapshots implementing IAntennaRelay in this Vessel. /// Returns all of the PartModules or ProtoPartModuleSnapshots implementing IAntennaRelay in this Vessel.
/// </summary> /// </summary>
/// <param name="vessel">This <see cref="Vessel"/></param> /// <param name="vessel">This <see cref="Vessel"/></param>
public static IList<IAntennaRelay> GetAntennaRelays (this Vessel vessel) public static IList<IAntennaRelay> GetAntennaRelays (this Vessel vessel)
{ {
return RelayDatabase.Instance[vessel]; return RelayDatabase.Instance[vessel];
} }
   
/// <summary> /// <summary>
/// Determines if the specified vessel has a connected relay. /// Determines if the specified vessel has a connected relay.
/// </summary> /// </summary>
/// <returns><c>true</c> if the specified vessel has a connected relay; otherwise, <c>false</c>.</returns> /// <returns><c>true</c> if the specified vessel has a connected relay; otherwise, <c>false</c>.</returns>
/// <param name="vessel"></param> /// <param name="vessel"></param>
public static bool HasConnectedRelay(this Vessel vessel) public static bool HasConnectedRelay(this Vessel vessel)
{ {
IList<IAntennaRelay> vesselRelays = RelayDatabase.Instance[vessel]; IList<IAntennaRelay> vesselRelays = RelayDatabase.Instance[vessel];
IAntennaRelay relay; IAntennaRelay relay;
for (int rIdx = 0; rIdx < vesselRelays.Count; rIdx++) for (int rIdx = 0; rIdx < vesselRelays.Count; rIdx++)
{ {
relay = vesselRelays[rIdx]; relay = vesselRelays[rIdx];
if (relay.CanTransmit()) if (relay.CanTransmit())
{ {
return true; return true;
} }
} }
   
return false; return false;
} }
   
/// <summary> /// <summary>
/// Gets the <see cref="AntennaRange.ConnectionStatus"/> for this <see cref="Vessel"/> /// Gets the <see cref="AntennaRange.ConnectionStatus"/> for this <see cref="Vessel"/>
/// </summary> /// </summary>
/// <param name="vessel">This <see cref="Vessel"/></param> /// <param name="vessel">This <see cref="Vessel"/></param>
public static ConnectionStatus GetConnectionStatus(this Vessel vessel) public static ConnectionStatus GetConnectionStatus(this Vessel vessel)
{ {
bool canTransmit = false; bool canTransmit = false;
   
IList<IAntennaRelay> vesselRelays = RelayDatabase.Instance[vessel]; IList<IAntennaRelay> vesselRelays = RelayDatabase.Instance[vessel];
IAntennaRelay relay; IAntennaRelay relay;
for (int rIdx = 0; rIdx < vesselRelays.Count; rIdx++) for (int rIdx = 0; rIdx < vesselRelays.Count; rIdx++)
{ {
relay = vesselRelays[rIdx]; relay = vesselRelays[rIdx];
if (relay.LinkStatus > ConnectionStatus.None) if (relay.LinkStatus > ConnectionStatus.None)
{ {
canTransmit = true; canTransmit = true;
   
if (relay.LinkStatus == ConnectionStatus.Optimal) if (relay.LinkStatus == ConnectionStatus.Optimal)
{ {
return ConnectionStatus.Optimal; return ConnectionStatus.Optimal;
} }
} }
} }
   
if (canTransmit) if (canTransmit)
{ {
return ConnectionStatus.Suboptimal; return ConnectionStatus.Suboptimal;
} }
else else
{ {
return ConnectionStatus.None; return ConnectionStatus.None;
} }
} }
   
/// <summary> /// <summary>
/// Gets the best relay on this Vessel. The best relay may not be able to transmit. /// Gets the best relay on this Vessel. The best relay may not be able to transmit.
/// </summary> /// </summary>
/// <param name="vessel">This <see cref="Vessel"/></param> /// <param name="vessel">This <see cref="Vessel"/></param>
public static IAntennaRelay GetBestRelay(this Vessel vessel) public static IAntennaRelay GetBestRelay(this Vessel vessel)
{ {
return RelayDatabase.Instance.GetBestVesselRelay(vessel); return RelayDatabase.Instance.GetBestVesselRelay(vessel);
  }
   
  /// <summary>
  /// Logs a message on behalf of this relay
  /// </summary>
  public static void Log(this AntennaRelay relay, string format, params object[] args)
  {
  Tools.PostLogMessage(string.Format("[{0}] {1}", relay.ToString(), format), args);
  }
   
  /// <summary>
  /// Logs a message on behalf of this relay
  /// </summary>
  public static void Log(this AntennaRelay relay, string msg)
  {
  Tools.PostLogMessage("[{0}] {1}", relay.ToString(), msg);
  }
   
  /// <summary>
  /// Logs a warning message on behalf of this relay
  /// </summary>
  public static void LogWarning(this AntennaRelay relay, string format, params object[] args)
  {
  Tools.PostWarningMessage(string.Format("[{0}] {1}", relay.ToString(), format), args);
  }
   
  /// <summary>
  /// Logs a warning message on behalf of this relay
  /// </summary>
  public static void LogWarning(this AntennaRelay relay, string msg)
  {
  Tools.PostWarningMessage("[{0}] {1}", relay.ToString(), msg);
  }
   
  /// <summary>
  /// Logs an error message on behalf of this relay
  /// </summary>
  public static void LogError(this AntennaRelay relay, string format, params object[] args)
  {
  Tools.PostErrorMessage(string.Format("[{0}] {1}", relay.ToString(), format), args);
  }
   
  /// <summary>
  /// Logs an error message on behalf of this relay
  /// </summary>
  public static void LogError(this AntennaRelay relay, string msg)
  {
  Tools.PostErrorMessage("[{0}] {1}", relay.ToString(), msg);
  }
   
  /// <summary>
  /// Logs a debug-only message on behalf of this relay
  /// </summary>
  [System.Diagnostics.Conditional("DEBUG")]
  public static void LogDebug(this AntennaRelay relay, string format, params object[] args)
  {
  Tools.PostDebugMessage(string.Format("[{0}] {1}", relay.ToString(), format), args);
  }
   
  /// <summary>
  /// Logs a debug-only message on behalf of this relay
  /// </summary>
  [System.Diagnostics.Conditional("DEBUG")]
  public static void LogDebug(this AntennaRelay relay, string msg)
  {
  Tools.PostDebugMessage("[{0}] {1}", relay.ToString(), msg);
} }
} }
   
#pragma warning disable 1591 #pragma warning disable 1591
/// <summary> /// <summary>
/// An Enum describing the connection status of a vessel or relay. /// An Enum describing the connection status of a vessel or relay.
/// </summary> /// </summary>
public enum ConnectionStatus public enum ConnectionStatus
{ {
None, None,
Suboptimal, Suboptimal,
Optimal Optimal
} }
} }