// 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); } } } }