AntennaRelay: Exclude flag vessels from the search.
[AntennaRange.git] / AntennaRelay.cs
blob:a/AntennaRelay.cs -> blob:b/AntennaRelay.cs
// AntennaRange © 2014 toadicus // AntennaRange © 2014 toadicus
// //
// AntennaRange provides incentive and requirements for the use of the various antenna parts. // AntennaRange provides incentive and requirements for the use of the various antenna parts.
// Nominally, the breakdown is as follows: // Nominally, the breakdown is as follows:
// //
// Communotron 16 - Suitable up to Kerbalsynchronous Orbit // Communotron 16 - Suitable up to Kerbalsynchronous Orbit
// Comms DTS-M1 - Suitable throughout the Kerbin subsystem // Comms DTS-M1 - Suitable throughout the Kerbin subsystem
// Communotron 88-88 - Suitable throughout the Kerbol system. // Communotron 88-88 - Suitable throughout the Kerbol system.
// //
// This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. To view a // This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. To view a
// copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/3.0/ // copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/3.0/
// //
// This software uses the ModuleManager library © 2013 ialdabaoth, used under a Creative Commons Attribution-ShareAlike // This software uses the ModuleManager library © 2013 ialdabaoth, used under a Creative Commons Attribution-ShareAlike
// 3.0 Uported License. // 3.0 Uported License.
// //
// This software uses code from the MuMechLib library, © 2013 r4m0n, used under the GNU GPL version 3. // This software uses code from the MuMechLib library, © 2013 r4m0n, used under the GNU GPL version 3.
   
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
   
namespace AntennaRange namespace AntennaRange
{ {
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.
protected CelestialBody Kerbin; protected CelestialBody Kerbin;
   
protected System.Diagnostics.Stopwatch searchTimer; protected System.Diagnostics.Stopwatch searchTimer;
protected long millisecondsBetweenSearches; 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 Vessel vessel public Vessel vessel
{ {
get; get;
protected set; protected set;
} }
   
/// <summary> /// <summary>
/// Gets or sets the nearest relay. /// Gets or sets the nearest relay.
/// </summary> /// </summary>
/// <value>The nearest relay</value> /// <value>The nearest relay</value>
public IAntennaRelay nearestRelay public IAntennaRelay nearestRelay
{ {
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 transmitDistance public double transmitDistance
{ {
get get
{ {
this.nearestRelay = this.FindNearestRelay(); this.nearestRelay = this.FindNearestRelay();
   
// If there is no available relay nearby... // If there is no available relay nearby...
if (nearestRelay == null) if (nearestRelay == null)
{ {
// .. return the distance to Kerbin // .. return the distance to Kerbin
return this.DistanceTo(this.Kerbin); return this.DistanceTo(this.Kerbin);
} }
else else
{ {
/// ...otherwise, return the distance to the nearest available relay. /// ...otherwise, return the distance to the nearest available relay.
return this.DistanceTo(nearestRelay); return this.DistanceTo(nearestRelay);
} }
} }
} }
   
/// <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 float maxTransmitDistance public virtual float maxTransmitDistance
{ {
get; get;
set; set;
} }
   
/// <summary> /// <summary>
/// Gets a value indicating whether this <see cref="AntennaRange.ProtoDataTransmitter"/> has been checked during /// Gets a value indicating whether this <see cref="AntennaRange.ProtoDataTransmitter"/> has been checked during
/// the current relay attempt. /// the current relay attempt.
/// </summary> /// </summary>
/// <value><c>true</c> if relay checked; otherwise, <c>false</c>.</value> /// <value><c>true</c> if relay checked; otherwise, <c>false</c>.</value>
public virtual bool relayChecked public virtual bool relayChecked
{ {
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 virtual bool CanTransmit() public virtual bool CanTransmit()
{ {
if (this.transmitDistance > this.maxTransmitDistance) if (this.transmitDistance > this.maxTransmitDistance)
{ {
return false; return false;
} }
else else
{ {
return true; return true;
} }
} }
   
/// <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 IAntennaRelay FindNearestRelay() public IAntennaRelay FindNearestRelay()
{ {
if (this.searchTimer.IsRunning && this.searchTimer.ElapsedMilliseconds < this.millisecondsBetweenSearches) if (this.searchTimer.IsRunning && this.searchTimer.ElapsedMilliseconds < this.millisecondsBetweenSearches)
{ {
return this.nearestRelay; return this.nearestRelay;
} }
   
if (this.searchTimer.IsRunning) if (this.searchTimer.IsRunning)
{ {
this.searchTimer.Stop(); this.searchTimer.Stop();
this.searchTimer.Reset(); this.searchTimer.Reset();
} }
   
this.searchTimer.Start(); this.searchTimer.Start();
   
// Set this relay as checked, so that we don't check it again. // Set this relay as checked, so that we don't check it again.
this.relayChecked = true; this.relayChecked = true;
   
// Get a list of vessels within transmission range. // Get a list of vessels within transmission range.
List<Vessel> nearbyVessels = FlightGlobals.Vessels List<Vessel> nearbyVessels = FlightGlobals.Vessels
.Where(v => (v.GetWorldPos3D() - vessel.GetWorldPos3D()).magnitude < this.maxTransmitDistance) .Where(v => (v.GetWorldPos3D() - vessel.GetWorldPos3D()).magnitude < this.maxTransmitDistance)
.ToList(); .ToList();
   
nearbyVessels.RemoveAll(v => v.vesselType == VesselType.Debris); nearbyVessels.RemoveAll(v => v.vesselType == VesselType.Debris);
  nearbyVessels.RemoveAll(v => v.vesselType == VesselType.Flag);
Tools.PostDebugMessage(string.Format(  
"{0}: Non-debris vessels in range: {1}", Tools.PostDebugMessage(string.Format(
  "{0}: Non-debris, non-flag vessels in range: {1}",
this.GetType().Name, this.GetType().Name,
nearbyVessels.Count nearbyVessels.Count
)); ));
   
// Remove this vessel. // Remove this vessel.
nearbyVessels.RemoveAll(v => v.id == vessel.id); nearbyVessels.RemoveAll(v => v.id == vessel.id);
   
Tools.PostDebugMessage(string.Format( Tools.PostDebugMessage(string.Format(
"{0}: Vessels in range excluding self: {1}", "{0}: Vessels in range excluding self: {1}",
this.GetType().Name, this.GetType().Name,
nearbyVessels.Count nearbyVessels.Count
)); ));
   
// Get a flattened list of all IAntennaRelay modules and protomodules in transmission range. // Get a flattened list of all IAntennaRelay modules and protomodules in transmission range.
List<IAntennaRelay> nearbyRelays = nearbyVessels.SelectMany(v => v.GetAntennaRelays()).ToList(); List<IAntennaRelay> nearbyRelays = nearbyVessels.SelectMany(v => v.GetAntennaRelays()).ToList();
   
Tools.PostDebugMessage(string.Format( Tools.PostDebugMessage(string.Format(
"{0}: Found {1} nearby relays.", "{0}: Found {1} nearby relays.",
this.GetType().Name, this.GetType().Name,
nearbyRelays.Count nearbyRelays.Count
)); ));
   
// Remove all relays already checked this time. // Remove all relays already checked this time.
nearbyRelays.RemoveAll(r => r.relayChecked); nearbyRelays.RemoveAll(r => r.relayChecked);
   
Tools.PostDebugMessage(string.Format( Tools.PostDebugMessage(string.Format(
"{0}: Found {1} nearby relays not already checked.", "{0}: Found {1} nearby relays not already checked.",
this.GetType().Name, this.GetType().Name,
nearbyRelays.Count nearbyRelays.Count
)); ));
   
// Remove all relays that cannot transmit. // Remove all relays that cannot transmit.
// This call to r.CanTransmit() starts a depth-first recursive search for relays with a path back to Kerbin. // This call to r.CanTransmit() starts a depth-first recursive search for relays with a path back to Kerbin.
nearbyRelays.RemoveAll(r => !r.CanTransmit()); nearbyRelays.RemoveAll(r => !r.CanTransmit());
   
Tools.PostDebugMessage(string.Format( Tools.PostDebugMessage(string.Format(
"{0}: Found {1} nearby relays not already checked that can transmit.", "{0}: Found {1} nearby relays not already checked that can transmit.",
this.GetType().Name, this.GetType().Name,
nearbyRelays.Count nearbyRelays.Count
)); ));
   
// Sort the available relays by distance. // Sort the available relays by distance.
nearbyRelays.Sort(new RelayComparer(this.vessel)); nearbyRelays.Sort(new RelayComparer(this.vessel));
   
// Get the nearest available relay, or null if there are no available relays nearby. // Get the nearest available relay, or null if there are no available relays nearby.
IAntennaRelay _nearestRelay = nearbyRelays.FirstOrDefault(); IAntennaRelay _nearestRelay = nearbyRelays.FirstOrDefault();
   
// If we have a nearby relay... // If we have a nearby relay...
if (_nearestRelay != null) if (_nearestRelay != null)
{ {
// ...but that relay is farther than Kerbin... // ...but that relay is farther than Kerbin...
if (this.DistanceTo(_nearestRelay) > this.DistanceTo(Kerbin)) if (this.DistanceTo(_nearestRelay) > this.DistanceTo(Kerbin))
{ {
// ...just use Kerbin. // ...just use Kerbin.
_nearestRelay = null; _nearestRelay = null;
} }
} }
   
// 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.relayChecked = false;
   
// Return the nearest available relay, or null if there are no available relays nearby. // Return the nearest available relay, or null if there are no available relays nearby.
return _nearestRelay; return _nearestRelay;
} }
   
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="AntennaRange.ProtoDataTransmitter"/> class. /// Initializes a new instance of the <see cref="AntennaRange.ProtoDataTransmitter"/> class.
/// </summary> /// </summary>
/// <param name="ms"><see cref="ProtoPartModuleSnapshot"/></param> /// <param name="ms"><see cref="ProtoPartModuleSnapshot"/></param>
public AntennaRelay(Vessel v) public AntennaRelay(Vessel v)
{ {
this.vessel = v; this.vessel = v;
   
this.searchTimer = new System.Diagnostics.Stopwatch(); this.searchTimer = new System.Diagnostics.Stopwatch();
this.millisecondsBetweenSearches = 5000; this.millisecondsBetweenSearches = 5000;
   
// HACK: This might not be safe in all circumstances, but since AntennaRelays are not built until Start, // HACK: This might not be safe in all circumstances, but since AntennaRelays are not built until Start,
// we hope it is safe enough. // we hope it is safe enough.
this.Kerbin = FlightGlobals.Bodies.FirstOrDefault(b => b.name == "Kerbin"); this.Kerbin = FlightGlobals.Bodies.FirstOrDefault(b => b.name == "Kerbin");
} }
   
/* /*
* Class implementing IComparer<IAntennaRelay> for use in sorting relays by distance. * Class implementing IComparer<IAntennaRelay> for use in sorting relays by distance.
* */ * */
internal class RelayComparer : IComparer<IAntennaRelay> internal class RelayComparer : IComparer<IAntennaRelay>
{ {
/// <summary> /// <summary>
/// The reference Vessel (usually the active vessel). /// The reference Vessel (usually the active vessel).
/// </summary> /// </summary>
protected Vessel referenceVessel; protected Vessel referenceVessel;
   
// We don't want no stinking public parameterless constructors. // We don't want no stinking public parameterless constructors.
private RelayComparer() {} private RelayComparer() {}
   
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="AntennaRange.AntennaRelay+RelayComparer"/> class for use /// Initializes a new instance of the <see cref="AntennaRange.AntennaRelay+RelayComparer"/> class for use
/// in sorting relays by distance. /// in sorting relays by distance.
/// </summary> /// </summary>
/// <param name="reference">The reference Vessel</param> /// <param name="reference">The reference Vessel</param>
public RelayComparer(Vessel reference) public RelayComparer(Vessel reference)
{ {
this.referenceVessel = reference; this.referenceVessel = reference;
} }
   
/// <summary> /// <summary>
/// Compare the <see cref="IAntennaRelay"/>s "one" and "two". /// Compare the <see cref="IAntennaRelay"/>s "one" and "two".
/// </summary> /// </summary>
/// <param name="one">The first IAntennaRelay in the comparison</param> /// <param name="one">The first IAntennaRelay in the comparison</param>
/// <param name="two">The second IAntennaRelay in the comparison</param> /// <param name="two">The second IAntennaRelay in the comparison</param>
public int Compare(IAntennaRelay one, IAntennaRelay two) public int Compare(IAntennaRelay one, IAntennaRelay two)
{ {
double distanceOne; double distanceOne;
double distanceTwo; double distanceTwo;
   
distanceOne = one.vessel.DistanceTo(referenceVessel); distanceOne = one.vessel.DistanceTo(referenceVessel);
distanceTwo = two.vessel.DistanceTo(referenceVessel); distanceTwo = two.vessel.DistanceTo(referenceVessel);
   
return distanceOne.CompareTo(distanceTwo); return distanceOne.CompareTo(distanceTwo);
} }
} }
} }
} }