Made things less verbose.
[AntennaRange.git] / AntennaRelay.cs
blob:a/AntennaRelay.cs -> blob:b/AntennaRelay.cs
// AntennaRange // AntennaRange
// //
// AntennaRelay.cs // AntennaRelay.cs
// //
// Copyright © 2014, toadicus // Copyright © 2014, 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 System.Linq; using System.Linq;
using ToadicusTools; using ToadicusTools;
   
// @DONE TODO: Retool nearestRelay to always contain the nearest relay, even if out of range.  
// @DONE TODO: Retool CanTransmit to not rely on nearestRelay == null.  
// TODO: Track occluded vessels somehow.  
   
namespace AntennaRange namespace AntennaRange
{ {
  /// <summary>
  /// Relay code at the heart of AntennaRange
  /// </summary>
public class AntennaRelay 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.
private static CelestialBody _Kerbin; private static CelestialBody _Kerbin;
   
  /// <summary>
  /// Fetches, caches, and returns a <see cref="CelestialBody"/> reference to Kerbin
  /// </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;
} }
} }
   
protected bool canTransmit; private long lastSearch;
   
  private bool canTransmit;
   
  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; protected IAntennaRelay moduleRef;
   
protected System.Diagnostics.Stopwatch searchTimer;  
protected long millisecondsBetweenSearches;  
   
/// <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 or sets the nearest relay. /// Gets the target <see cref="AntennaRange.IAntennaRelay"/>relay.
/// </summary> /// </summary>
/// <value>The nearest relay</value> public IAntennaRelay targetRelay
public IAntennaRelay nearestRelay  
{ {
get; get;
protected set; protected set;
} }
   
public IAntennaRelay bestOccludedRelay /// <summary>
  /// Gets the first <see cref="CelestialBody"/> found to be blocking line of sight.
  /// </summary>
  public virtual CelestialBody firstOccludingBody
{ {
get; get;
protected set; protected set;
} }
   
public IAntennaRelay targetRelay /// <summary>
  /// Gets the transmit distance.
  /// </summary>
  /// <value>The transmit distance.</value>
  public double transmitDistance
  {
  get
  {
  this.FindNearestRelay();
   
  if (this.KerbinDirect || this.targetRelay == null)
  {
  return this.DistanceTo(Kerbin);
  }
  else
  {
  return this.DistanceTo(this.targetRelay);
  }
  }
  }
   
  /// <summary>
  /// Gets the nominal transmit distance at which the Antenna behaves just as prescribed by Squad's config.
  /// </summary>
  public virtual double nominalTransmitDistance
  {
  get;
  set;
  }
   
  /// <summary>
  /// The maximum distance at which this relay can operate.
  /// </summary>
  /// <value>The max transmit distance.</value>
  public virtual double maxTransmitDistance
  {
  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>
/// 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.  
/// </summary>  
/// <value>The transmit distance.</value>  
public double transmitDistance  
{  
get  
{  
this.FindNearestRelay();  
   
if (this.KerbinDirect || this.targetRelay == null)  
{  
return this.DistanceTo(Kerbin);  
}  
else  
{  
return this.DistanceTo(this.targetRelay);  
}  
}  
}  
   
public virtual double nominalTransmitDistance  
{  
get;  
set;  
}  
   
/// <summary>  
/// The maximum distance at which this relay can operate.  
/// </summary>  
/// <value>The max transmit distance.</value>  
public virtual float maxTransmitDistance  
{  
get;  
set;  
}  
   
/// <summary>  
/// Gets a value indicating whether this <see cref="AntennaRange.ProtoDataTransmitter"/> has been checked during  
/// the current relay attempt.  
/// </summary>  
/// <value><c>true</c> if relay checked; otherwise, <c>false</c>.</value>  
public virtual bool relayChecked  
{  
get;  
protected set;  
}  
   
public virtual bool KerbinDirect  
{  
get;  
protected set;  
}  
   
/// <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()
{ {
this.FindNearestRelay(); this.FindNearestRelay();
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>
private void FindNearestRelay() private void FindNearestRelay()
{ {
if (!this.searchTimer.IsRunning || this.searchTimer.ElapsedMilliseconds > this.millisecondsBetweenSearches) if (!searchTimer.IsRunning)
{ {
this.searchTimer.Reset(); searchTimer.Start();
} }
else  
  long searchTime = searchTimer.ElapsedMilliseconds;
  long timeSinceLast = searchTime - this.lastSearch;
   
  if (timeSinceLast < Math.Max(millisecondsBetweenSearches, UnityEngine.Time.smoothDeltaTime))
{ {
return; return;
} }
   
  this.lastSearch = searchTime;
   
// Skip vessels that have already been checked for a nearest relay this pass. // Skip vessels that have already been checked for a nearest relay this pass.
if (RelayDatabase.Instance.CheckedVesselsTable.ContainsKey(this.vessel.id)) if (RelayDatabase.Instance.CheckedVesselsTable.ContainsKey(this.vessel.id))
{ {
return; return;
} }
   
if (FlightGlobals.ActiveVessel != null && FlightGlobals.ActiveVessel.id == this.vessel.id) Tools.DebugLogger log;
{ #if DEBUG
Tools.PostDebugMessage(string.Format( log = Tools.DebugLogger.New(this);
"{0}: finding nearest relay for {1}", #endif
this.GetType().Name,  
this.ToString() log.AppendFormat("{0}: Target search started at {1} ms ({2} ms since last search).",
)); this.ToString(), searchTime, timeSinceLast);
}  
   
// Set this vessel as checked, so that we don't check it again. // Set this vessel as checked, so that we don't check it again.
RelayDatabase.Instance.CheckedVesselsTable[vessel.id] = true; RelayDatabase.Instance.CheckedVesselsTable[vessel.id] = true;
   
// 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.
  this.KerbinDirect = true;
   
CelestialBody bodyOccludingBestOccludedRelay = null; CelestialBody bodyOccludingBestOccludedRelay = null;
   
double nearestRelaySqrDistance = double.PositiveInfinity; double nearestRelaySqrDistance = double.PositiveInfinity;
double bestOccludedSqrDistance = double.PositiveInfinity; double bestOccludedSqrDistance = double.PositiveInfinity;
double maxTransmitSqrDistance = this.maxTransmitDistance * this.maxTransmitDistance; 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 * 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 * 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 * and that can transmit. Once we find a suitable candidate, assign it to nearestRelay for comparison
* against future finds. * against future finds.
* */ * */
foreach (Vessel potentialVessel in FlightGlobals.Vessels) foreach (Vessel potentialVessel in FlightGlobals.Vessels)
{ {
// Skip vessels of the wrong type. // Skip vessels of the wrong type.
switch (potentialVessel.vesselType) switch (potentialVessel.vesselType)
{ {
case VesselType.Debris: case VesselType.Debris:
case VesselType.Flag: case VesselType.Flag:
case VesselType.EVA: case VesselType.EVA:
case VesselType.SpaceObject: case VesselType.SpaceObject:
case VesselType.Unknown: case VesselType.Unknown:
continue; continue;
default: default:
break; break;
} }
   
// Skip vessels with the wrong ID // Skip vessels with the wrong ID
if (potentialVessel.id == vessel.id) if (potentialVessel.id == vessel.id)
{ {
continue; continue;
} }
   
// Find the distance from here to the vessel... // Find the distance from here to the vessel...
double potentialSqrDistance = this.sqrDistanceTo(potentialVessel); double potentialSqrDistance = this.sqrDistanceTo(potentialVessel);
   
CelestialBody fob = null; CelestialBody fob = null;
   
// 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(potentialVessel, out fob, ARConfiguration.RadiusRatio) !this.vessel.hasLineOfSightTo(potentialVessel, out fob, ARConfiguration.RadiusRatio)
) )
{ {
this.firstOccludingBody = fob; this.firstOccludingBody = fob;
   
if (FlightGlobals.ActiveVessel != null && FlightGlobals.ActiveVessel.id == this.vessel.id) log.AppendFormat("\n\t{0}: Vessel {1} not in line of sight.",
{ this.ToString(), potentialVessel.vesselName);
Tools.PostDebugMessage("{6}: Vessel {0} discarded because we do not have line of sight." +  
"\npotentialSqrDistance: {1}, bestOccludedSqrDistance: {2}, maxTransmitSqrDistance: {3}" +  
"\npotentialSqrDistance < bestOccludedSqrDistance: {4}" +  
"\npotentialSqrDistance < (this.maxTransmitDistance * this.maxTransmitDistance): {5}",  
potentialVessel.vesselName,  
potentialSqrDistance, bestOccludedSqrDistance, this.maxTransmitDistance * this.maxTransmitDistance,  
potentialSqrDistance < bestOccludedSqrDistance,  
potentialSqrDistance < (this.maxTransmitDistance * this.maxTransmitDistance),  
this.ToString()  
);  
}  
   
if ( if (
(potentialSqrDistance < bestOccludedSqrDistance) && (potentialSqrDistance < bestOccludedSqrDistance) &&
(potentialSqrDistance < maxTransmitSqrDistance) (potentialSqrDistance < maxTransmitSqrDistance)
) )
{ {
if (FlightGlobals.ActiveVessel != null && FlightGlobals.ActiveVessel.id == this.vessel.id)  
{ log.AppendFormat("\n\t\t{0}: Checking {1} relays on occluded vessel {2}.",
Tools.PostDebugMessage("{0}: Checking {1} relays on {2}.", this.ToString(),
this.ToString(), potentialVessel.GetAntennaRelays().Count(),
potentialVessel.GetAntennaRelays().Count(), potentialVessel
potentialVessel );
   
  foreach (IAntennaRelay occludedRelay in potentialVessel.GetAntennaRelays())
  {
   
  log.AppendFormat(
  "\n\t\t{0}: Checking candidate for bestOccludedRelay: {1}" +
  "\n\t\tCanTransmit: {2}",
  this.ToString(), occludedRelay, occludedRelay.CanTransmit()
); );
}  
   
foreach (IAntennaRelay occludedRelay in potentialVessel.GetAntennaRelays())  
{  
if (FlightGlobals.ActiveVessel != null && FlightGlobals.ActiveVessel.id == this.vessel.id)  
{  
Tools.PostDebugMessage(this.ToString() + " Checking candidate for bestOccludedRelay: {0}" +  
"\n\tCanTransmit: {1}", occludedRelay, occludedRelay.CanTransmit());  
}  
   
if (occludedRelay.CanTransmit()) if (occludedRelay.CanTransmit())
{ {
this.bestOccludedRelay = occludedRelay; this.bestOccludedRelay = occludedRelay;
bodyOccludingBestOccludedRelay = fob; bodyOccludingBestOccludedRelay = fob;
bestOccludedSqrDistance = potentialSqrDistance; bestOccludedSqrDistance = potentialSqrDistance;
   
if (FlightGlobals.ActiveVessel != null && FlightGlobals.ActiveVessel.id == this.vessel.id) log.AppendFormat("\n\t{0}: Found new bestOccludedRelay: {1}" +
{ " (blocked by {2}; distance: {3} m)",
Tools.PostDebugMessage(this.ToString() + " Found new bestOccludedRelay: {0}" + this.ToString(),
"\nfirstOccludingBody: {1}" + occludedRelay.ToString(),
"\nbestOccludedSqrDistance: {2}", fob,
occludedRelay, potentialSqrDistance
fob, );
potentialSqrDistance  
);  
}  
break; break;
} }
} }
} }
   
continue; continue;
} }
   
/* /*
* ...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 (potentialSqrDistance > nearestRelaySqrDistance) if (potentialSqrDistance > nearestRelaySqrDistance)
{ {
if (FlightGlobals.ActiveVessel != null && FlightGlobals.ActiveVessel.id == this.vessel.id)  
{ log.AppendFormat("\n\t{0}: Vessel {1} discarded because it is farther than another the nearest relay.",
Tools.PostDebugMessage("{0}: Vessel {1} discarded because it is out of range, or farther than another relay.", this.ToString(),
this.ToString(), potentialVessel.vesselName
potentialVessel.vesselName );
);  
}  
continue; continue;
} }
   
nearestRelaySqrDistance = potentialSqrDistance;  
   
foreach (IAntennaRelay potentialRelay in potentialVessel.GetAntennaRelays()) foreach (IAntennaRelay potentialRelay in potentialVessel.GetAntennaRelays())
{ {
if (potentialRelay.CanTransmit() && potentialRelay.targetRelay != this) if (potentialRelay.CanTransmit() && potentialRelay.targetRelay != this)
{ {
  // @TODO: Moved this here from outside the loop; why was it there?
  nearestRelaySqrDistance = potentialSqrDistance;
this.nearestRelay = potentialRelay; this.nearestRelay = potentialRelay;
   
if (FlightGlobals.ActiveVessel != null && FlightGlobals.ActiveVessel.id == this.vessel.id) if (FlightGlobals.ActiveVessel != null && FlightGlobals.ActiveVessel.id == this.vessel.id)
{ {
Tools.PostDebugMessage(string.Format("{0}: found new best relay {1} ({2})", log.AppendFormat("\n\t{0}: found new nearest relay {1} ({2}m)",
this.ToString(), this.ToString(),
this.nearestRelay.ToString(), this.nearestRelay.ToString(),
this.nearestRelay.vessel.id Math.Sqrt(nearestRelaySqrDistance)
)); );
} }
break; break;
} }
} }
} }
   
CelestialBody bodyOccludingKerbin = null; CelestialBody bodyOccludingKerbin = null;
   
double kerbinSqrDistance = this.vessel.DistanceTo(Kerbin) - Kerbin.Radius; double kerbinSqrDistance = this.vessel.DistanceTo(Kerbin) - Kerbin.Radius;
kerbinSqrDistance *= kerbinSqrDistance; kerbinSqrDistance *= kerbinSqrDistance;
   
Tools.DebugLogger log = Tools.DebugLogger.New(this); log.AppendFormat("\n{0} ({1}): Search done, figuring status.", this.ToString(), this.GetType().Name);
   
log.AppendFormat("{0} ({1}): Search done, figuring status.", this.ToString(), this.GetType().Name);  
   
// If we don't have LOS to Kerbin, focus on relays // If we don't have LOS to Kerbin, focus on relays
if (!this.vessel.hasLineOfSightTo(Kerbin, out bodyOccludingKerbin, ARConfiguration.RadiusRatio)) if (!this.vessel.hasLineOfSightTo(Kerbin, out bodyOccludingKerbin, ARConfiguration.RadiusRatio))
{ {
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 (nearestRelaySqrDistance <= maxTransmitSqrDistance) if (nearestRelaySqrDistance <= maxTransmitSqrDistance)
{ {
log.AppendFormat("\n\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(),
nearestRelaySqrDistance, maxTransmitSqrDistance); nearestRelaySqrDistance, maxTransmitSqrDistance);
   
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\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(),
nearestRelaySqrDistance, maxTransmitSqrDistance); nearestRelaySqrDistance, maxTransmitSqrDistance);
   
this.canTransmit = false; this.canTransmit = false;
   
// If the best occluded relay is closer than Kerbin, target it. // 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) if (bestOccludedSqrDistance < kerbinSqrDistance)
{ {
log.AppendFormat("\n\t\tPicking occluded relay {0} as target ({1} < {2}).", log.AppendFormat("\n\t\t\tBest occluded relay is closer than Kerbin ({0} < {1})",
this.bestOccludedRelay == null ? "null" : this.bestOccludedRelay.ToString(), bestOccludedRelay, kerbinSqrDistance);
bestOccludedSqrDistance, kerbinSqrDistance);  
   
this.KerbinDirect = false; this.KerbinDirect = false;
this.targetRelay = this.bestOccludedRelay;  
this.firstOccludingBody = bodyOccludingBestOccludedRelay; // 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.
// Otherwise, target Kerbin and report the first body blocking it. 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 else
{ {
log.AppendFormat("\n\t\tPicking Kerbin as target ({0} >= {1}).", log.AppendFormat("\n\t\t\tKerbin is closer than the best occluded relay ({0} >= {1})",
bestOccludedSqrDistance, kerbinSqrDistance); 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
  {
  log.AppendFormat("\n\tKerbin is in LOS.");
   
  // If the nearest relay is closer than Kerbin and in range, transmit to it.
  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.canTransmit = true;
   
  // If the nearestRelay is closer than Kerbin, use it.
  if (nearestRelaySqrDistance < kerbinSqrDistance)
  {
  log.AppendFormat("\n\t\t\tPicking relay {0} over Kerbin ({1} < {2}).",
  this.nearestRelay == null ? "null" : this.nearestRelay.ToString(),
  nearestRelaySqrDistance, kerbinSqrDistance);
   
  this.KerbinDirect = false;
  this.targetRelay = this.nearestRelay;
  }
  // Otherwise, Kerbin is closer, so use it.
  else
  {
  log.AppendFormat("\n\t\t\tBut picking Kerbin over nearby relay {0} ({1} >= {2}).",
  this.nearestRelay == null ? "null" : this.nearestRelay.ToString(),
  nearestRelaySqrDistance, kerbinSqrDistance);
   
this.KerbinDirect = true; this.KerbinDirect = true;
this.targetRelay = null; this.targetRelay = null;
this.firstOccludingBody = bodyOccludingKerbin; }
} }
} // If the nearest relay is out of range, we still need to check on Kerbin.
} else
// If we do have LOS to Kerbin, try to prefer the closest of nearestRelay and Kerbin {
else log.AppendFormat("\n\t\tCan't transmit to nearby relay {0} ({1} > {2}).",
{  
log.AppendFormat("\n\tKerbin is in LOS.");  
   
// If the nearest relay is closer than Kerbin and in range, transmit to it.  
if (nearestRelaySqrDistance <= maxTransmitSqrDistance)  
{  
log.AppendFormat("\n\tCan transmit to nearby relay {0} ({1} <= {2}).",  
this.nearestRelay == null ? "null" : this.nearestRelay.ToString(), this.nearestRelay == null ? "null" : this.nearestRelay.ToString(),
nearestRelaySqrDistance, maxTransmitSqrDistance); nearestRelaySqrDistance, maxTransmitSqrDistance);
   
this.canTransmit = true;  
   
// If the nearestRelay is closer than Kerbin, use it.  
if (nearestRelaySqrDistance < kerbinSqrDistance)  
{  
log.AppendFormat("\n\tPicking relay {0} over Kerbin ({1} < {2}).",  
this.nearestRelay == null ? "null" : this.nearestRelay.ToString(),  
nearestRelaySqrDistance, kerbinSqrDistance);  
   
this.KerbinDirect = false;  
this.targetRelay = this.nearestRelay;  
}  
// Otherwise, Kerbin is closer, so use it.  
else  
{  
log.AppendFormat("\n\tBut picking Kerbin over nearby relay {0} ({1} >= {2}).",  
this.nearestRelay == null ? "null" : this.nearestRelay.ToString(),  
nearestRelaySqrDistance, kerbinSqrDistance);  
   
this.KerbinDirect = true;  
this.targetRelay = null;  
}  
}  
// If the nearest relay is out of range, we still need to check on Kerbin.  
else  
{  
log.AppendFormat("\n\tCan't transmit to nearby relay {0} ({1} > {2}).",  
this.nearestRelay == null ? "null" : this.nearestRelay.ToString(),  
nearestRelaySqrDistance, maxTransmitSqrDistance);  
   
// If Kerbin is in range, use it. // If Kerbin is in range, use it.
if (kerbinSqrDistance <= maxTransmitSqrDistance) if (kerbinSqrDistance <= maxTransmitSqrDistance)
{ {
log.AppendFormat("\n\tCan transmit to Kerbin ({0} <= {1}).", log.AppendFormat("\n\t\t\tCan transmit to Kerbin ({0} <= {1}).",
kerbinSqrDistance, maxTransmitSqrDistance); kerbinSqrDistance, maxTransmitSqrDistance);
   
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\tCan't transmit to Kerbin ({0} > {1}).", log.AppendFormat("\n\t\t\tCan't transmit to Kerbin ({0} > {1}).",
kerbinSqrDistance, maxTransmitSqrDistance); kerbinSqrDistance, maxTransmitSqrDistance);
   
this.canTransmit = false; this.canTransmit = false;
   
// If the best occluded relay is closer than Kerbin, use it. // 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, // Since bestOccludedSqrDistance is infinity if there are no occluded relays, this is safe
// this is safe  
if (bestOccludedSqrDistance < kerbinSqrDistance) if (bestOccludedSqrDistance < kerbinSqrDistance)
{ {
log.AppendFormat("\n\t\tPicking occluded relay {0} as target ({1} < {2}).", log.AppendFormat("\n\t\t\tBest occluded relay is closer than Kerbin ({0} < {1})",
this.bestOccludedRelay == null ? "null" : this.bestOccludedRelay.ToString(), bestOccludedRelay, kerbinSqrDistance);
bestOccludedSqrDistance, kerbinSqrDistance);  
   
this.KerbinDirect = false; this.KerbinDirect = false;
this.targetRelay = bestOccludedRelay;  
this.firstOccludingBody = bodyOccludingBestOccludedRelay; // 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.
// Otherwise, target Kerbin. Since we have LOS, blank the first occluding body. 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 else
{ {
log.AppendFormat("\n\t\tPicking Kerbin as target ({0} >= {1}).", log.AppendFormat("\n\t\t\tKerbin is closer than the best occluded relay ({0} >= {1})",
bestOccludedSqrDistance, kerbinSqrDistance); bestOccludedRelay, kerbinSqrDistance);
   
this.KerbinDirect = true;  
this.targetRelay = null;  
this.firstOccludingBody = null; 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()); log.AppendFormat("\n{0}: Status determination complete.", this.ToString());
   
log.Print(); log.Print(false);
   
// 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.
RelayDatabase.Instance.CheckedVesselsTable.Remove(vessel.id); RelayDatabase.Instance.CheckedVesselsTable.Remove(vessel.id);
} }
   
  /// <summary>
  /// Returns a <see cref="System.String"/> that represents the current <see cref="AntennaRange.AntennaRelay"/>.
  /// </summary>
  /// <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.ProtoDataTransmitter"/> class. /// Initializes a new instance of the <see cref="AntennaRange.AntennaRelay"/> class.
/// </summary> /// </summary>
/// <param name="ms"><see cref="ProtoPartModuleSnapshot"/></param> /// <param name="module">The module reference underlying this AntennaRelay,
  /// as an <see cref="AntennaRange.IAntennaRelay"/></param>
public AntennaRelay(IAntennaRelay module) public AntennaRelay(IAntennaRelay module)
{ {
this.moduleRef = module; this.moduleRef = module;
   
this.searchTimer = new System.Diagnostics.Stopwatch();  
this.millisecondsBetweenSearches = 125L;  
} }
} }
} }