// AntennaRange © 2014 toadicus
//
// AntennaRange provides incentive and requirements for the use of the various antenna parts.
// Nominally, the breakdown is as follows:
//
// Communotron 16 - Suitable up to Kerbalsynchronous Orbit
// Comms DTS-M1 - Suitable throughout the Kerbin subsystem
// 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
// 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
// 3.0 Uported License.
//
// This software uses code from the MuMechLib library, © 2013 r4m0n, used under the GNU GPL version 3.
using System;
using System.Collections.Generic;
using System.Linq;
namespace AntennaRange
{
public class AntennaRelay : IAntennaRelay
{
// We don't have a Bard, so we'll hide Kerbin here.
protected CelestialBody Kerbin;
protected System.Diagnostics.Stopwatch searchTimer;
protected long millisecondsBetweenSearches;
///
/// Gets the parent Vessel.
///
/// The parent Vessel.
public Vessel vessel
{
get;
protected set;
}
///
/// Gets or sets the nearest relay.
///
/// The nearest relay
public IAntennaRelay nearestRelay
{
get;
protected set;
}
///
/// Gets the transmit distance.
///
/// The transmit distance.
public double transmitDistance
{
get
{
this.nearestRelay = this.FindNearestRelay();
// If there is no available relay nearby...
if (nearestRelay == null)
{
// .. return the distance to Kerbin
return this.DistanceTo(this.Kerbin);
}
else
{
/// ...otherwise, return the distance to the nearest available relay.
return this.DistanceTo(nearestRelay);
}
}
}
///
/// The maximum distance at which this relay can operate.
///
/// The max transmit distance.
public virtual float maxTransmitDistance
{
get;
set;
}
///
/// Gets a value indicating whether this has been checked during
/// the current relay attempt.
///
/// true if relay checked; otherwise, false.
public virtual bool relayChecked
{
get;
protected set;
}
///
/// Determines whether this instance can transmit.
///
/// true if this instance can transmit; otherwise, false.
public bool CanTransmit()
{
if (this.transmitDistance > this.maxTransmitDistance)
{
return false;
}
else
{
return true;
}
}
///
/// Finds the nearest relay.
///
/// The nearest relay or null, if no relays in range.
public IAntennaRelay FindNearestRelay()
{
if (this.searchTimer.IsRunning && this.searchTimer.ElapsedMilliseconds < this.millisecondsBetweenSearches)
{
return this.nearestRelay;
}
if (this.searchTimer.IsRunning)
{
this.searchTimer.Stop();
this.searchTimer.Reset();
}
this.searchTimer.Start();
// Set this relay as checked, so that we don't check it again.
this.relayChecked = true;
// Get a list of vessels within transmission range.
List nearbyVessels = FlightGlobals.Vessels
.Where(v => (v.GetWorldPos3D() - vessel.GetWorldPos3D()).magnitude < this.maxTransmitDistance)
.ToList();
nearbyVessels.RemoveAll(v => v.vesselType == VesselType.Debris);
Tools.PostDebugMessage(string.Format(
"{0}: Non-debris vessels in range: {1}",
this.GetType().Name,
nearbyVessels.Count
));
// Remove this vessel.
nearbyVessels.RemoveAll(v => v.id == vessel.id);
Tools.PostDebugMessage(string.Format(
"{0}: Vessels in range excluding self: {1}",
this.GetType().Name,
nearbyVessels.Count
));
// Get a flattened list of all IAntennaRelay modules and protomodules in transmission range.
List nearbyRelays = nearbyVessels.SelectMany(v => v.GetAntennaRelays()).ToList();
Tools.PostDebugMessage(string.Format(
"{0}: Found {1} nearby relays.",
this.GetType().Name,
nearbyRelays.Count
));
// Remove all relays already checked this time.
nearbyRelays.RemoveAll(r => r.relayChecked);
Tools.PostDebugMessage(string.Format(
"{0}: Found {1} nearby relays not already checked.",
this.GetType().Name,
nearbyRelays.Count
));
// 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.
nearbyRelays.RemoveAll(r => !r.CanTransmit());
Tools.PostDebugMessage(string.Format(
"{0}: Found {1} nearby relays not already checked that can transmit.",
this.GetType().Name,
nearbyRelays.Count
));
// Sort the available relays by distance.
nearbyRelays.Sort(new RelayComparer(this.vessel));
// Get the nearest available relay, or null if there are no available relays nearby.
IAntennaRelay _nearestRelay = nearbyRelays.FirstOrDefault();
// If we have a nearby relay...
if (_nearestRelay != null)
{
// ...but that relay is farther than Kerbin...
if (this.DistanceTo(_nearestRelay) > this.DistanceTo(Kerbin))
{
// ...just use Kerbin.
_nearestRelay = null;
}
}
// Now that we're done with our recursive CanTransmit checks, flag this relay as not checked so it can be
// used next time.
this.relayChecked = false;
// Return the nearest available relay, or null if there are no available relays nearby.
return _nearestRelay;
}
public override string ToString()
{
return string.Format(
"Antenna relay on vessel {0}.",
vessel
);
}
///
/// Initializes a new instance of the class.
///
///
public AntennaRelay(Vessel v)
{
this.vessel = v;
this.searchTimer = new System.Diagnostics.Stopwatch();
this.millisecondsBetweenSearches = 5000;
// 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");
}
/*
* Class implementing IComparer for use in sorting relays by distance.
* */
internal class RelayComparer : IComparer
{
///
/// The reference Vessel (usually the active vessel).
///
protected Vessel referenceVessel;
// We don't want no stinking public parameterless constructors.
private RelayComparer() {}
///
/// Initializes a new instance of the class for use
/// in sorting relays by distance.
///
/// The reference Vessel
public RelayComparer(Vessel reference)
{
this.referenceVessel = reference;
}
///
/// Compare the s "one" and "two".
///
/// The first IAntennaRelay in the comparison
/// The second IAntennaRelay in the comparison
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);
}
}
}
}