Hopefully the last bulk update for relays. Also includes a bunch of commenting.
[AntennaRange.git] / AntennaRelay.cs
blob:a/AntennaRelay.cs -> blob:b/AntennaRelay.cs
  // AntennaRange
  //
  // AntennaRelay.cs
  //
  // Copyright © 2014, toadicus
  // All rights reserved.
  //
  // Redistribution and use in source and binary forms, with or without modification,
  // are permitted provided that the following conditions are met:
  //
  // 1. Redistributions of source code must retain the above copyright notice,
  // this list of conditions and the following disclaimer.
  //
  // 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
  // materials provided with the distribution.
  //
  // 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.
  //
  // 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
  // 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
  // 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
  // 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 System.Linq; using ToadicusTools;
   
namespace AntennaRange namespace AntennaRange
{ {
public class AntennaRelay : IAntennaRelay /// <summary>
  /// Relay code at the heart of AntennaRange
  /// </summary>
  public class AntennaRelay
{ {
  private static readonly System.Diagnostics.Stopwatch searchTimer = new System.Diagnostics.Stopwatch();
  private const long millisecondsBetweenSearches = 125L;
   
// We don't have a Bard, so we'll hide Kerbin here. // We don't have a Bard, so we'll hide Kerbin here.
protected CelestialBody Kerbin; private static CelestialBody _Kerbin;
   
  /// <summary>
  /// Fetches, caches, and returns a <see cref="CelestialBody"/> reference to Kerbin
  /// </summary>
  public static CelestialBody Kerbin
  {
  get
  {
  if (_Kerbin == null && FlightGlobals.ready)
  {
  _Kerbin = FlightGlobals.GetHomeBody();
  }
   
  return _Kerbin;
  }
  }
   
  private long lastSearch;
   
  private bool canTransmit;
  private bool isChecked;
   
  private IAntennaRelay nearestRelay;
  private IAntennaRelay bestOccludedRelay;
   
  /// <summary>
  /// The <see cref="AntennaRange.ModuleLimitedDataTransmitter"/> reference underlying this AntennaRelay, as an
  /// <see cref="AntennaRange.IAntennaRelay"/>
  /// </summary>
  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 Vessel vessel public virtual Vessel vessel
  {
  get
  {
  return this.moduleRef.vessel;
  }
  }
   
  /// <summary>
  /// Gets the target <see cref="AntennaRange.IAntennaRelay"/>relay.
  /// </summary>
  public IAntennaRelay targetRelay
{ {
get; get;
protected set; protected set;
} }
   
/// <summary> /// <summary>
  /// Gets the first <see cref="CelestialBody"/> found to be blocking line of sight.
  /// </summary>
  public virtual CelestialBody firstOccludingBody
  {
  get;
  protected set;
  }
   
  /// <summary>
/// Gets the transmit distance. /// Gets the transmit distance.
/// </summary> /// </summary>
/// <value>The transmit distance.</value> /// <value>The transmit distance.</value>
public double transmitDistance public double transmitDistance
{ {
get get
{ {
IAntennaRelay nearestRelay = this.FindNearestRelay(); if (this.KerbinDirect || this.targetRelay == null)
  {
// If there is no available relay nearby... return this.DistanceTo(Kerbin);
if (nearestRelay == null)  
{  
// .. return the distance to Kerbin  
return this.DistanceTo(this.Kerbin);  
} }
else else
{ {
/// ...otherwise, return the distance to the nearest available relay. return this.DistanceTo(this.targetRelay);
return this.DistanceTo(nearestRelay); }
} }
} }
}  
  /// <summary>
/// <summary> /// Gets the nominal transmit distance at which the Antenna behaves just as prescribed by Squad's config.
/// The maximum distance at which this relay can operate. /// </summary>
/// </summary> public virtual double nominalTransmitDistance
/// <value>The max transmit distance.</value>  
public virtual float maxTransmitDistance  
{ {
get; get;
set; set;
} }
   
/// <summary> /// <summary>
/// Gets a value indicating whether this <see cref="AntennaRange.ProtoDataTransmitter"/> has been checked during /// The maximum distance at which this relay can operate.
/// the current relay attempt. /// </summary>
/// </summary> /// <value>The max transmit distance.</value>
/// <value><c>true</c> if relay checked; otherwise, <c>false</c>.</value> public virtual double maxTransmitDistance
public virtual bool relayChecked {
  get;
  set;
  }
   
  /// <summary>
  /// Gets a value indicating whether this <see cref="AntennaRange.IAntennaRelay"/> Relay is communicating
  /// directly with Kerbin.
  /// </summary>
  public virtual bool KerbinDirect
{ {
get; get;
protected set; protected 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 bool CanTransmit() public virtual bool CanTransmit()
{ {
if (this.transmitDistance > this.maxTransmitDistance) return this.canTransmit;
{ }
return false;  
} /// <summary>
  /// Finds the nearest relay.
  /// </summary>
  /// <returns>The nearest relay or null, if no relays in range.</returns>
  public void FindNearestRelay()
  {
  if (!FlightGlobals.ready)
  {
  return;
  }
   
  if (!searchTimer.IsRunning)
  {
  searchTimer.Start();
  }
   
  Tools.DebugLogger log;
  #if DEBUG
  log = Tools.DebugLogger.New(this);
  #endif
   
  long searchTime = searchTimer.ElapsedMilliseconds;
  long timeSinceLast = searchTime - this.lastSearch;
   
  if (timeSinceLast < millisecondsBetweenSearches)
  {
  log.AppendFormat(
  "{0}: Target search skipped because it's not time to search again yet ({1} - {2}) < {3})",
  this, searchTime, this.lastSearch, millisecondsBetweenSearches
  );
  log.Print();
  return;
  }
   
  // Skip vessels that have already been checked for a nearest relay this pass.
  if (this.isChecked)
  {
  log.AppendFormat("{0}: Target search skipped because our vessel has been checked already this search.",
  this);
  log.Print();
  return;
  }
   
  log.AppendFormat("{0}: Target search started at {1} ms ({2} ms since last search).",
  this.ToString(), searchTime, timeSinceLast);
   
  #if DEBUG
  try {
  #endif
  // Set this vessel as checked, so that we don't check it again.
  this.isChecked = true;
   
  this.lastSearch = searchTime;
   
  // Blank everything we're trying to find before the search.
  this.firstOccludingBody = null;
  this.bestOccludedRelay = null;
  this.targetRelay = null;
  this.nearestRelay = null;
   
  // Default to KerbinDirect = true in case something in here doesn't work right.
  this.KerbinDirect = true;
   
  CelestialBody bodyOccludingBestOccludedRelay = null;
   
  double nearestRelaySqrDistance = double.PositiveInfinity;
  double bestOccludedSqrDistance = double.PositiveInfinity;
  double maxTransmitSqrDistance = this.maxTransmitDistance * this.maxTransmitDistance;
   
  /*
  * Loop through all the vessels and exclude this vessel, vessels of the wrong type, and vessels that are too
  * far away. When we find a candidate, get through its antennae for relays which have not been checked yet
  * and that can transmit. Once we find a suitable candidate, assign it to nearestRelay for comparison
  * against future finds.
  * */
  Vessel potentialVessel;
  IList<IAntennaRelay> vesselRelays;
  for (int vIdx = 0; vIdx < FlightGlobals.Vessels.Count; vIdx++)
  {
  log.AppendFormat("\nFetching vessel at index {0}", vIdx);
  potentialVessel = FlightGlobals.Vessels[vIdx];
   
  if (potentialVessel == null)
  {
  log.AppendFormat("\n\tSkipping vessel at index {0} because it is null.", vIdx);
  log.Print();
  return;
  }
  #if DEBUG
  else
  {
  log.AppendFormat("\n\tGot vessel {0}", potentialVessel);
  }
  #endif
   
  // Skip vessels of the wrong type.
  log.Append("\n\tchecking vessel type");
  switch (potentialVessel.vesselType)
  {
  case VesselType.Debris:
  case VesselType.Flag:
  case VesselType.EVA:
  case VesselType.SpaceObject:
  case VesselType.Unknown:
  log.Append("\n\tSkipping because vessel is the wrong type.");
  continue;
  default:
  break;
  }
   
  log.Append("\n\tchecking if vessel is this vessel");
  // Skip vessels with the wrong ID
  if (potentialVessel.id == vessel.id)
  {
  log.Append("\n\tSkipping because vessel is this vessel.");
  continue;
  }
   
  // Find the distance from here to the vessel...
  log.Append("\n\tgetting distance to potential vessel");
  double potentialSqrDistance = this.sqrDistanceTo(potentialVessel);
  log.Append("\n\tgetting vessel relays");
  vesselRelays = potentialVessel.GetAntennaRelays();
  log.AppendFormat("\n\t\tvesselRelays: {0}",
  vesselRelays == null ? "null" : vesselRelays.Count.ToString());
   
  CelestialBody fob = null;
   
  log.Append("\n\tdoing LOS check");
  // Skip vessels to which we do not have line of sight.
  if (
  ARConfiguration.RequireLineOfSight &&
  !this.vessel.hasLineOfSightTo(potentialVessel, out fob, ARConfiguration.RadiusRatio)
  )
  {
  log.Append("\n\tfailed LOS check");
  this.firstOccludingBody = fob;
   
  log.AppendFormat("\n\t{0}: Vessel {1} not in line of sight.",
  this.ToString(), potentialVessel.vesselName);
   
  log.AppendFormat("\n\t\tpotentialSqrDistance: {0}", potentialSqrDistance);
  log.AppendFormat("\n\t\tbestOccludedSqrDistance: {0}", bestOccludedSqrDistance);
  log.AppendFormat("\n\t\tmaxTransmitSqrDistance: {0}", maxTransmitSqrDistance);
   
  if (
  (potentialSqrDistance < bestOccludedSqrDistance) &&
  (potentialSqrDistance < maxTransmitSqrDistance)
  )
  {
  log.Append("\n\t\t...vessel is close enough to check for occluded relays");
  log.AppendFormat("\n\t\tthis: {0}", this);
  log.AppendFormat("\n\t\tpotentialVessel: {0}",
  potentialVessel == null ? "null" : potentialVessel.ToString());
  log.AppendFormat("\n\t\tvesselRelays: {0}",
  vesselRelays == null ? "null" : vesselRelays.ToString());
   
  log.AppendFormat("\n\t\t{0}: Checking {1} relays on occluded vessel {2}.",
  this.ToString(),
  vesselRelays.Count,
  potentialVessel
  );
   
  IAntennaRelay occludedRelay;
  for (int rIdx = 0; rIdx < vesselRelays.Count; rIdx++)
  {
  occludedRelay = vesselRelays[rIdx];
   
  log.AppendFormat(
  "\n\t\t{0}: Checking candidate for bestOccludedRelay: {1}" +
  "\n\t\tCanTransmit: {2}",
  this.ToString(), occludedRelay, occludedRelay.CanTransmit()
  );
   
  if (occludedRelay.CanTransmit())
  {
  this.bestOccludedRelay = occludedRelay;
  bodyOccludingBestOccludedRelay = fob;
  bestOccludedSqrDistance = potentialSqrDistance;
   
  log.AppendFormat("\n\t{0}: Found new bestOccludedRelay: {1}" +
  " (blocked by {2}; distance: {3} m)",
  this.ToString(),
  occludedRelay.ToString(),
  fob,
  potentialSqrDistance
  );
  break;
  }
  }
  }
   
  log.Append("\n\t\t...vessel is not close enough to check for occluded relays, carrying on");
  continue;
  }
   
  log.Append("\n\tpassed LOS check");
   
  /*
  * ...so that we can skip the vessel if it is further away than a vessel we've already checked.
  * */
  if (potentialSqrDistance > nearestRelaySqrDistance)
  {
   
  log.AppendFormat("\n\t{0}: Vessel {1} discarded because it is farther than another the nearest relay.",
  this.ToString(),
  potentialVessel.vesselName
  );
  continue;
  }
   
  log.Append("\n\tpassed distance check");
   
  IAntennaRelay potentialRelay;
  for (int rIdx = 0; rIdx < vesselRelays.Count; rIdx++)
  {
  log.AppendFormat("\n\t\tfetching vessel relay at index {0}", rIdx);
  potentialRelay = vesselRelays[rIdx];
  log.AppendFormat("\n\t\tgot relay {0}", potentialRelay == null ? "null" : potentialRelay.ToString());
   
  if (potentialRelay == null)
  {
  log.Append("\n\t\t...skipping null relay");
  continue;
  }
   
  if (
  potentialRelay.CanTransmit() &&
  (potentialRelay.targetRelay == null || potentialRelay.targetRelay.vessel != this.vessel))
  {
  // @TODO: Moved this here from outside the loop; why was it there?
  nearestRelaySqrDistance = potentialSqrDistance;
  this.nearestRelay = potentialRelay;
   
  log.AppendFormat("\n\t{0}: found new nearest relay {1} ({2}m)",
  this.ToString(),
  this.nearestRelay.ToString(),
  Math.Sqrt(nearestRelaySqrDistance)
  );
   
  break;
  }
  }
  }
   
  CelestialBody bodyOccludingKerbin = null;
   
  double kerbinSqrDistance = this.vessel.DistanceTo(Kerbin) - Kerbin.Radius;
  kerbinSqrDistance *= kerbinSqrDistance;
   
  log.AppendFormat("\n{0} ({1}): Search done, figuring status.", this.ToString(), this.GetType().Name);
   
  // If we don't have LOS to Kerbin, focus on relays
  if (!this.vessel.hasLineOfSightTo(Kerbin, out bodyOccludingKerbin, ARConfiguration.RadiusRatio))
  {
  log.AppendFormat("\n\tKerbin LOS is blocked by {0}.", bodyOccludingKerbin.bodyName);
   
  // 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.
  if (nearestRelaySqrDistance <= maxTransmitSqrDistance)
  {
  log.AppendFormat("\n\t\tCan transmit to nearby relay {0} ({1} <= {2}).",
  this.nearestRelay == null ? "null" : this.nearestRelay.ToString(),
  nearestRelaySqrDistance, maxTransmitSqrDistance);
   
  this.KerbinDirect = false;
  this.canTransmit = true;
  this.targetRelay = this.nearestRelay;
  }
  // If this isn't true, we can't transmit, but pick a second best of bestOccludedRelay and Kerbin anyway
  else
  {
  log.AppendFormat("\n\t\tCan't transmit to nearby relay {0} ({1} > {2}).",
  this.nearestRelay == null ? "null" : this.nearestRelay.ToString(),
  nearestRelaySqrDistance, maxTransmitSqrDistance);
   
  this.canTransmit = false;
   
  // 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
  if (bestOccludedSqrDistance < kerbinSqrDistance)
  {
  log.AppendFormat("\n\t\t\tBest occluded relay is closer than Kerbin ({0} < {1})",
  bestOccludedRelay, kerbinSqrDistance);
   
  this.KerbinDirect = false;
   
  // 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.
  if (nearestRelaySqrDistance < bestOccludedSqrDistance)
  {
  log.AppendFormat("\n\t\t\t\t...but the nearest relay is closer ({0} < {1}), so picking it.",
  nearestRelaySqrDistance, bestOccludedSqrDistance);
   
  this.targetRelay = nearestRelay;
  this.firstOccludingBody = null;
  }
  // Otherwise, target the best occluded relay.
  else
  {
  log.AppendFormat("\n\t\t\t\t...and closer than the nearest relay ({0} >= {1}), so picking it.",
  nearestRelaySqrDistance, bestOccludedSqrDistance);
   
  this.targetRelay = bestOccludedRelay;
  this.firstOccludingBody = bodyOccludingBestOccludedRelay;
  }
  }
  // Otherwise, check Kerbin against the nearest relay.
  // Since we have LOS, blank the first occluding body.
  else
  {
  log.AppendFormat("\n\t\t\tKerbin is closer than the best occluded relay ({0} >= {1})",
  bestOccludedRelay, kerbinSqrDistance);
   
  this.firstOccludingBody = null;
   
  // If the nearest relay is closer than Kerbin, pick it.
  // Since nearestRelaySqrDistane is infinity if there are no nearby relays, this is safe.
  if (nearestRelaySqrDistance < kerbinSqrDistance)
  {
  log.AppendFormat("\n\t\t\t\t...but the nearest relay is closer ({0} < {1}), so picking it.",
  nearestRelaySqrDistance, kerbinSqrDistance);
   
  this.KerbinDirect = false;
  this.targetRelay = nearestRelay;
  }
  // Otherwise, pick Kerbin.
  else
  {
  log.AppendFormat("\n\t\t\t\t...and closer than the nearest relay ({0} >= {1}), so picking it.",
  nearestRelaySqrDistance, kerbinSqrDistance);
   
  this.KerbinDirect = true;
  this.targetRelay = null;
  }
  }
  }
  }
  // If we do have LOS to Kerbin, try to prefer the closest of nearestRelay and Kerbin
else else
{ {
return true; log.AppendFormat("\n\tKerbin is in LOS.");
}  
} // If the nearest relay is closer than Kerbin and in range, transmit to it.
  if (nearestRelaySqrDistance <= maxTransmitSqrDistance)
/// <summary> {
/// Finds the nearest relay. log.AppendFormat("\n\t\tCan transmit to nearby relay {0} ({1} <= {2}).",
/// </summary> this.nearestRelay == null ? "null" : this.nearestRelay.ToString(),
/// <returns>The nearest relay or null, if no relays in range.</returns> nearestRelaySqrDistance, maxTransmitSqrDistance);
public IAntennaRelay FindNearestRelay()  
{ this.canTransmit = true;
// Set this relay as checked, so that we don't check it again.  
this.relayChecked = true; // If the nearestRelay is closer than Kerbin, use it.
  if (nearestRelaySqrDistance < kerbinSqrDistance)
// Get a list of vessels within transmission range. {
List<Vessel> nearbyVessels = FlightGlobals.Vessels log.AppendFormat("\n\t\t\tPicking relay {0} over Kerbin ({1} < {2}).",
.Where(v => (v.GetWorldPos3D() - vessel.GetWorldPos3D()).magnitude < this.maxTransmitDistance) this.nearestRelay == null ? "null" : this.nearestRelay.ToString(),
.ToList(); nearestRelaySqrDistance, kerbinSqrDistance);
   
Tools.PostDebugMessage(string.Format( this.KerbinDirect = false;
"{0}: Vessels in range: {1}", this.targetRelay = this.nearestRelay;
this.GetType().Name, }
nearbyVessels.Count // Otherwise, Kerbin is closer, so use it.
)); else
  {
// Remove this vessel. log.AppendFormat("\n\t\t\tBut picking Kerbin over nearby relay {0} ({1} >= {2}).",
nearbyVessels.RemoveAll(v => v.id == vessel.id); this.nearestRelay == null ? "null" : this.nearestRelay.ToString(),
  nearestRelaySqrDistance, kerbinSqrDistance);
Tools.PostDebugMessage(string.Format(  
"{0}: Vessels in range excluding self: {1}", this.KerbinDirect = true;
this.GetType().Name, this.targetRelay = null;
nearbyVessels.Count }
)); }
  // If the nearest relay is out of range, we still need to check on Kerbin.
// Get a flattened list of all IAntennaRelay modules and protomodules in transmission range. else
List<IAntennaRelay> nearbyRelays = nearbyVessels.SelectMany(v => v.GetAntennaRelays()).ToList(); {
  log.AppendFormat("\n\t\tCan't transmit to nearby relay {0} ({1} > {2}).",
Tools.PostDebugMessage(string.Format( this.nearestRelay == null ? "null" : this.nearestRelay.ToString(),
"{0}: Found {1} nearby relays.", nearestRelaySqrDistance, maxTransmitSqrDistance);
this.GetType().Name,  
nearbyRelays.Count // If Kerbin is in range, use it.
)); if (kerbinSqrDistance <= maxTransmitSqrDistance)
  {
// Remove all relays already checked this time. log.AppendFormat("\n\t\t\tCan transmit to Kerbin ({0} <= {1}).",
nearbyRelays.RemoveAll(r => r.relayChecked); kerbinSqrDistance, maxTransmitSqrDistance);
   
Tools.PostDebugMessage(string.Format( this.canTransmit = true;
"{0}: Found {1} nearby relays not already checked.", this.KerbinDirect = true;
this.GetType().Name, this.targetRelay = null;
nearbyRelays.Count }
)); // If Kerbin is out of range and the nearest relay is out of range, pick a second best between
  // Kerbin and bestOccludedRelay
// Remove all relays that cannot transmit. else
// This call to r.CanTransmit() starts a depth-first recursive search for relays with a path back to Kerbin. {
nearbyRelays.RemoveAll(r => !r.CanTransmit()); log.AppendFormat("\n\t\t\tCan't transmit to Kerbin ({0} > {1}).",
  kerbinSqrDistance, maxTransmitSqrDistance);
Tools.PostDebugMessage(string.Format(  
"{0}: Found {1} nearby relays not already checked that can transmit.", this.canTransmit = false;
this.GetType().Name,  
nearbyRelays.Count // 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
  if (bestOccludedSqrDistance < kerbinSqrDistance)
// Sort the available relays by distance. {
nearbyRelays.Sort(new RelayComparer(this.vessel)); log.AppendFormat("\n\t\t\tBest occluded relay is closer than Kerbin ({0} < {1})",
  bestOccludedRelay, kerbinSqrDistance);
// Get the nearest available relay, or null if there are no available relays nearby.  
IAntennaRelay nearestRelay = nearbyRelays.FirstOrDefault(); this.KerbinDirect = false;
   
  // 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.
  if (nearestRelaySqrDistance < bestOccludedSqrDistance)
  {
  log.AppendFormat("\n\t\t\t\t...but the nearest relay is closer ({0} < {1}), so picking it.",
  nearestRelaySqrDistance, bestOccludedSqrDistance);
   
  this.targetRelay = nearestRelay;
  this.firstOccludingBody = null;
  }
  // Otherwise, target the best occluded relay.
  else
  {
  log.AppendFormat("\n\t\t\t\t...and closer than the nearest relay ({0} >= {1}), so picking it.",
  nearestRelaySqrDistance, bestOccludedSqrDistance);
   
  this.targetRelay = bestOccludedRelay;
  this.firstOccludingBody = bodyOccludingBestOccludedRelay;
  }
  }
  // Otherwise, check Kerbin against the nearest relay.
  // Since we have LOS, blank the first occluding body.
  else
  {
  log.AppendFormat("\n\t\t\tKerbin is closer than the best occluded relay ({0} >= {1})",
  bestOccludedRelay, kerbinSqrDistance);
   
  this.firstOccludingBody = null;
   
  // If the nearest relay is closer than Kerbin, pick it.
  // Since nearestRelaySqrDistane is infinity if there are no nearby relays, this is safe.
  if (nearestRelaySqrDistance < kerbinSqrDistance)
  {
  log.AppendFormat("\n\t\t\t\t...but the nearest relay is closer ({0} < {1}), so picking it.",
  nearestRelaySqrDistance, kerbinSqrDistance);
   
  this.KerbinDirect = false;
  this.targetRelay = nearestRelay;
  }
  // Otherwise, pick Kerbin.
  else
  {
  log.AppendFormat("\n\t\t\t\t...and closer than the nearest relay ({0} >= {1}), so picking it.",
  nearestRelaySqrDistance, kerbinSqrDistance);
   
  this.KerbinDirect = true;
  this.targetRelay = null;
  }
  }
  }
  }
  }
   
  log.AppendFormat("{0}: Target search completed at {1} ms ({2} ms elapsed).",
  this.ToString(), searchTimer.ElapsedMilliseconds, searchTimer.ElapsedMilliseconds - searchTime);;
   
  log.AppendFormat("\n{0}: Status determination complete.", this.ToString());
   
  #if DEBUG
  } catch (Exception ex) {
  log.AppendFormat("\nCaught {0}: {1}\n{2}", ex.GetType().FullName, ex.ToString(), ex.StackTrace);
  #if QUIT_ON_EXCEPTION
  UnityEngine.Application.Quit();
  #endif
  } finally {
  #endif
  log.Print(false);
  #if DEBUG
  }
  #endif
// Now that we're done with our recursive CanTransmit checks, flag this relay as not checked so it can be // Now that we're done with our recursive CanTransmit checks, flag this relay as not checked so it can be
// used next time. // used next time.
this.relayChecked = false; this.isChecked = false;
  }
// Return the nearest available relay, or null if there are no available relays nearby.  
return nearestRelay; /// <summary>
} /// 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>
/// Initializes a new instance of the <see cref="AntennaRange.ProtoDataTransmitter"/> class. public override string ToString()
/// </summary> {
/// <param name="ms"><see cref="ProtoPartModuleSnapshot"/></param> if (this is ProtoAntennaRelay)
public AntennaRelay(Vessel v) {
{ return (this as ProtoAntennaRelay).ToString();
this.vessel = v; }
  return this.moduleRef.ToString();
// HACK: This might not be safe in all circumstances, but since AntennaRelays are not built until Start, }
// we hope it is safe enough.  
this.Kerbin = FlightGlobals.Bodies.FirstOrDefault(b => b.name == "Kerbin"); /// <summary>
} /// Initializes a new instance of the <see cref="AntennaRange.AntennaRelay"/> class.
  /// </summary>
/* /// <param name="module">The module reference underlying this AntennaRelay,
* Class implementing IComparer<IAntennaRelay> for use in sorting relays by distance. /// as an <see cref="AntennaRange.IAntennaRelay"/></param>
* */ public AntennaRelay(IAntennaRelay module)
internal class RelayComparer : IComparer<IAntennaRelay> {
{ this.moduleRef = module;
/// <summary> this.isChecked = false;
/// The reference Vessel (usually the active vessel).  
/// </summary> Tools.PostLogMessage("{0}: constructed {1}", this.GetType().Name, this.ToString());
protected Vessel referenceVessel;  
   
// We don't want no stinking public parameterless constructors.  
private RelayComparer() {}  
   
/// <summary>  
/// Initializes a new instance of the <see cref="AntennaRange.AntennaRelay+RelayComparer"/> class for use  
/// in sorting relays by distance.  
/// </summary>  
/// <param name="reference">The reference Vessel</param>  
public RelayComparer(Vessel reference)  
{  
this.referenceVessel = reference;  
}  
   
/// <summary>  
/// Compare the <see cref="IAntennaRelay"/>s "one" and "two".  
/// </summary>  
/// <param name="one">The first IAntennaRelay in the comparison</param>  
/// <param name="two">The second IAntennaRelay in the comparison</param>  
public int Compare(IAntennaRelay one, IAntennaRelay two)  
{  
double distanceOne;  
double distanceTwo;  
   
distanceOne = one.vessel.DistanceTo(referenceVessel);  
distanceTwo = two.vessel.DistanceTo(referenceVessel);  
   
return distanceOne.CompareTo(distanceTwo);  
}  
} }
} }
} }