Deepened the tabular hash nesting.
Deepened the tabular hash nesting.

--- a/AntennaRange.cfg
+++ b/AntennaRange.cfg
@@ -48,8 +48,3 @@
 	}
 }
 
-@PART[*]:HAS[MODULE[ModuleLimitedDataTransmitter]]:Final
-{
-	isAntenna = True
-}
-

file:b/AntennaRelay.cs (new)
--- /dev/null
+++ b/AntennaRelay.cs
@@ -1,1 +1,276 @@
-
+// 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;
+
+		/// <summary>
+		/// Gets the parent Vessel.
+		/// </summary>
+		/// <value>The parent Vessel.</value>
+		public Vessel vessel
+		{
+			get;
+			protected set;
+		}
+
+		/// <summary>
+		/// Gets or sets the nearest relay.
+		/// </summary>
+		/// <value>The nearest relay</value>
+		public IAntennaRelay nearestRelay
+		{
+			get;
+			protected set;
+		}
+
+		/// <summary>
+		/// Gets the transmit distance.
+		/// </summary>
+		/// <value>The transmit distance.</value>
+		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);
+				}
+			}
+		}
+
+		/// <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;
+		}
+
+		/// <summary>
+		/// Determines whether this instance can transmit.
+		/// </summary>
+		/// <returns><c>true</c> if this instance can transmit; otherwise, <c>false</c>.</returns>
+		public bool CanTransmit()
+		{
+			if (this.transmitDistance > this.maxTransmitDistance)
+			{
+				return false;
+			}
+			else
+			{
+				return true;
+			}
+		}
+
+		/// <summary>
+		/// Finds the nearest relay.
+		/// </summary>
+		/// <returns>The nearest relay or null, if no relays in range.</returns>
+		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<Vessel> 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<IAntennaRelay> 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
+			);
+		}
+
+		/// <summary>
+		/// Initializes a new instance of the <see cref="AntennaRange.ProtoDataTransmitter"/> class.
+		/// </summary>
+		/// <param name="ms"><see cref="ProtoPartModuleSnapshot"/></param>
+		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<IAntennaRelay> for use in sorting relays by distance.
+		 * */
+		internal class RelayComparer : IComparer<IAntennaRelay>
+		{
+			/// <summary>
+			/// The reference Vessel (usually the active vessel).
+			/// </summary>
+			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);
+			}
+		}
+	}
+}
+
+

--- a/Extensions.cs
+++ b/Extensions.cs
@@ -15,11 +15,9 @@
 //
 // This software uses code from the MuMechLib library, © 2013 r4m0n, used under the GNU GPL version 3.
 
-using KSP;
 using System;
 using System.Collections.Generic;
 using System.Linq;
-using UnityEngine;
 
 namespace AntennaRange
 {
@@ -53,7 +51,7 @@
 		/// </summary>
 		/// <param name="relay">This <see cref="IAntennaRelay"/></param>
 		/// <param name="Vessel">A <see cref="Vessel"/></param>
-		public static double DistanceTo(this IAntennaRelay relay, Vessel Vessel)
+		public static double DistanceTo(this AntennaRelay relay, Vessel Vessel)
 		{
 			return relay.vessel.DistanceTo(Vessel);
 		}
@@ -63,7 +61,7 @@
 		/// </summary>
 		/// <param name="relay">This <see cref="IAntennaRelay"/></param>
 		/// <param name="body">A <see cref="CelestialBody"/></param>
-		public static double DistanceTo(this IAntennaRelay relay, CelestialBody body)
+		public static double DistanceTo(this AntennaRelay relay, CelestialBody body)
 		{
 			return relay.vessel.DistanceTo(body);
 		}
@@ -73,7 +71,7 @@
 		/// </summary>
 		/// <param name="relayOne">This <see cref="IAntennaRelay"/></param>
 		/// <param name="relayTwo">Another <see cref="IAntennaRelay"/></param>
-		public static double DistanceTo(this IAntennaRelay relayOne, IAntennaRelay relayTwo)
+		public static double DistanceTo(this AntennaRelay relayOne, IAntennaRelay relayTwo)
 		{
 			return relayOne.DistanceTo(relayTwo.vessel);
 		}
@@ -105,7 +103,7 @@
 					.SelectMany (p => p.Modules.OfType<IAntennaRelay> ())
 					.ToList();
 			}
-			// If the vessel is not loaded, we need to find ProtoPartModuleSnapshots with a true IsAntenna field.
+			// If the vessel is not loaded, we need to build ProtoAntennaRelays when we find relay ProtoPartSnapshots.
 			else
 			{
 				Tools.PostDebugMessage(string.Format(
@@ -114,19 +112,53 @@
 					vessel.name
 					));
 
+				Dictionary<int, IAntennaRelay> prebuiltProtoRelays;
+
 				Transmitters = new List<IAntennaRelay>();
+
+				if (relayDatabase.ContainsKey(vessel.id))
+				{
+					return relayDatabase[vessel.id].Values.ToList();
+				}
+				else
+				{
+					prebuiltProtoRelays = new Dictionary<int, IAntennaRelay>();
+					relayDatabase[vessel.id] = prebuiltProtoRelays;
+				}
 
 				// Loop through the ProtoPartModuleSnapshots in this Vessel
 				foreach (ProtoPartSnapshot pps in vessel.protoVessel.protoPartSnapshots)
 				{
-					foreach (PartModule prefabModule in PartLoader.getPartInfoByName(pps.partName).partPrefab.Modules)
+					IAntennaRelay relayModule;
+					ProtoAntennaRelay protoRelay;
+					int partHash;
+
+					relayModule = null;
+					protoRelay = null;
+					partHash = pps.GetHashCode();
+
+					if (prebuiltProtoRelays.ContainsKey(partHash))
 					{
-						if (prefabModule is ModuleLimitedDataTransmitter)
+						protoRelay = (ProtoAntennaRelay)prebuiltProtoRelays[partHash];
+					}
+					else
+					{
+						foreach (PartModule module in PartLoader.getPartInfoByName(pps.partName).partPrefab.Modules)
 						{
-							pps.Load(vessel, false);
+							if (module is IAntennaRelay)
+							{
+								relayModule = module as IAntennaRelay;
 
-							Transmitters.Add(pps.partRef.Modules.OfType<ModuleLimitedDataTransmitter>().First());
+								protoRelay = new ProtoAntennaRelay(relayModule, vessel);
+								prebuiltProtoRelays[partHash] = protoRelay;
+								break;
+							}
 						}
+					}
+
+					if (protoRelay != null)
+					{
+						Transmitters.Add(protoRelay);
 					}
 				}
 			}
@@ -141,6 +173,9 @@
 			// Return the list of IAntennaRelays
 			return Transmitters;
 		}
+
+		private static Dictionary<Guid, Dictionary<int, IAntennaRelay>> relayDatabase =
+			new Dictionary<Guid, Dictionary<int, IAntennaRelay>>();
 	}
 }
 

--- a/IAntennaRelay.cs
+++ b/IAntennaRelay.cs
@@ -32,12 +32,6 @@
 		Vessel vessel { get; }
 
 		/// <summary>
-		/// Gets the nearest relay.
-		/// </summary>
-		/// <value>The nearest relay.</value>
-		IAntennaRelay nearestRelay { get; }
-
-		/// <summary>
 		/// Gets the distance to the nearest relay or Kerbin, whichever is closer.
 		/// </summary>
 		/// <value>The distance to the nearest relay or Kerbin, whichever is closer.</value>

--- a/ModuleLimitedDataTransmitter.cs
+++ b/ModuleLimitedDataTransmitter.cs
@@ -18,6 +18,7 @@
 using System;
 using System.Collections.Generic;
 using System.Linq;
+using System.Text;
 using KSP;
 using UnityEngine;
 
@@ -41,29 +42,38 @@
 	 * */
 	public class ModuleLimitedDataTransmitter : ModuleDataTransmitter, IScienceDataTransmitter, IAntennaRelay
 	{
-		protected System.Diagnostics.Stopwatch searchTimer;
-		protected long millisecondsBetweenSearches;
-
 		// Stores the packetResourceCost as defined in the .cfg file.
 		protected float _basepacketResourceCost;
 
 		// Stores the packetSize as defined in the .cfg file.
 		protected float _basepacketSize;
 
+		// Every antenna is a relay.
+		protected AntennaRelay relay;
+
 		// Keep track of vessels with transmitters for relay purposes.
 		protected List<Vessel> _relayVessels;
 
 		// Sometimes we will need to communicate errors; this is how we do it.
 		protected ScreenMessage ErrorMsg;
-
-		// Let's make the error text pretty!
-		protected UnityEngine.GUIStyle ErrorStyle;
 
 		// The distance from Kerbin at which the antenna will perform exactly as prescribed by packetResourceCost
 		// and packetSize.
 		[KSPField(isPersistant = false)]
 		public float nominalRange;
 
+		[KSPField(isPersistant = false, guiActive = true, guiName = "Transmission Distance")]
+		public string UItransmitDistance;
+
+		[KSPField(isPersistant = false, guiActive = true, guiName = "Maximum Distance")]
+		public string UImaxTransmitDistance;
+
+		[KSPField(isPersistant = false, guiActive = true, guiName = "Packet Size")]
+		public string UIpacketSize;
+
+		[KSPField(isPersistant = false, guiActive = true, guiName = "Packet Cost")]
+		public string UIpacketCost;
+
 		// The multiplier on packetResourceCost that defines the maximum power output of the antenna.  When the power
 		// cost exceeds packetResourceCost * maxPowerFactor, transmission will fail.
 		[KSPField(isPersistant = false)]
@@ -73,60 +83,26 @@
 		[KSPField(isPersistant = false)]
 		public float maxDataFactor;
 
-		protected CelestialBody _Kerbin;
+		protected bool actionUIUpdate;
 
 		/*
 		 * Properties
 		 * */
-		// We don't have a Bard, so we'll hide Kerbin here.
-		protected CelestialBody Kerbin
-		{
-			get
-			{
-				if (this._Kerbin == null)
-				{
-					foreach (CelestialBody cb in FlightGlobals.Bodies)
-					{
-						if (cb.name == "Kerbin")
-						{
-							this._Kerbin = cb;
-							break;
-						}
-					}
-				}
-				return this._Kerbin;
-			}
-		}
-
-		/// <summary>
-		/// Gets or sets the nearest relay.
-		/// </summary>
-		/// <value>The nearest relay</value>
-		public IAntennaRelay nearestRelay
-		{
-			get;
-			protected set;
-		}
-
+		// Returns the parent vessel housing this antenna.
+		public new Vessel vessel
+		{
+			get
+			{
+				return base.vessel;
+			}
+		}
 
 		// Returns the distance to the nearest relay or Kerbin, whichever is closer.
 		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);
-				}
+				return this.relay.transmitDistance;
 			}
 		}
 
@@ -206,8 +182,10 @@
 		// Reports whether this antenna has been checked as a viable relay already in the current FindNearestRelay.
 		public bool relayChecked
 		{
-			get;
-			protected set;
+			get
+			{
+				return this.relay.relayChecked;
+			}
 		}
 
 		/*
@@ -216,21 +194,24 @@
 		// Build ALL the objects.
 		public ModuleLimitedDataTransmitter () : base()
 		{
-			// Make the error posting prettier.
-			this.ErrorStyle = new UnityEngine.GUIStyle();
-			this.ErrorStyle.normal.textColor = (UnityEngine.Color)XKCDColors.OrangeRed;
-			this.ErrorStyle.active.textColor = (UnityEngine.Color)XKCDColors.OrangeRed;
-			this.ErrorStyle.hover.textColor = (UnityEngine.Color)XKCDColors.OrangeRed;
-			this.ErrorStyle.fontStyle = UnityEngine.FontStyle.Normal;
-			this.ErrorStyle.padding.top = 32;
-
-			this.ErrorMsg = new ScreenMessage("", 4f, false, ScreenMessageStyle.UPPER_LEFT, this.ErrorStyle);
+			this.ErrorMsg = new ScreenMessage("", 4f, false, ScreenMessageStyle.UPPER_LEFT);
 		}
 
 		// At least once, when the module starts with a state on the launch pad or later, go find Kerbin.
 		public override void OnStart (StartState state)
 		{
 			base.OnStart (state);
+
+			if (state >= StartState.PreLaunch)
+			{
+				this.relay = new AntennaRelay(vessel);
+				this.relay.maxTransmitDistance = this.maxTransmitDistance;
+
+				this.UImaxTransmitDistance = Tools.MuMech_ToSI(this.maxTransmitDistance) + "m";
+
+				GameEvents.onPartActionUICreate.Add(this.onPartActionUICreate);
+				GameEvents.onPartActionUIDismiss.Add(this.onPartActionUIDismiss);
+			}
 		}
 
 		// When the module loads, fetch the Squad KSPFields from the base.  This is necessary in part because
@@ -245,9 +226,6 @@
 
 			this._basepacketSize = base.packetSize;
 			this._basepacketResourceCost = base.packetResourceCost;
-
-			this.searchTimer = new System.Diagnostics.Stopwatch();
-			this.millisecondsBetweenSearches = 5000;
 
 			Tools.PostDebugMessage(string.Format(
 				"{0} loaded:\n" +
@@ -275,9 +253,18 @@
 				Tools.MuMech_ToSI((double)this.transmitDistance, 2)
 				);
 
-			this.ErrorMsg.message = ErrorText;
-
-			ScreenMessages.PostScreenMessage(this.ErrorMsg, true);
+			this.ErrorMsg.message = string.Format(
+				"<color='#{0}{1}{2}{3}'><b>{4}</b></color>",
+				((int)(XKCDColors.OrangeRed.r * 255f)).ToString("x2"),
+				((int)(XKCDColors.OrangeRed.g * 255f)).ToString("x2"),
+				((int)(XKCDColors.OrangeRed.b * 255f)).ToString("x2"),
+				((int)(XKCDColors.OrangeRed.a * 255f)).ToString("x2"),
+				ErrorText
+			);
+
+			Tools.PostDebugMessage(this.GetType().Name + ": " + this.ErrorMsg.message);
+
+			ScreenMessages.PostScreenMessage(this.ErrorMsg, false);
 		}
 
 		// Before transmission, set packetResourceCost.  Per above, packet cost increases with the square of
@@ -324,21 +311,7 @@
 		// Override ModuleDataTransmitter.CanTransmit to return false when transmission is not possible.
 		public new bool CanTransmit()
 		{
-			Tools.PostDebugMessage(string.Format(
-				"{0}: Checking if {1} on {2} can transmit.",
-				this.GetType().Name,
-				base.part.name,
-				this.vessel
-			));
-
-			if (this.transmitDistance > this.maxTransmitDistance)
-			{
-				return false;
-			}
-			else
-			{
-				return true;
-			}
+			return this.relay.CanTransmit();
 		}
 
 		// Override ModuleDataTransmitter.TransmitData to check against CanTransmit and fail out when CanTransmit
@@ -361,103 +334,6 @@
 			);
 		}
 
-		/// <summary>
-		/// Finds the nearest relay.
-		/// </summary>
-		/// <returns>The nearest relay or null, if no relays in range.</returns>
-		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<Vessel> 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<IAntennaRelay> 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;
-		}
-
 		// Override ModuleDataTransmitter.StartTransmission to check against CanTransmit and fail out when CanTransmit
 		// returns false.
 		public new void StartTransmission()
@@ -473,20 +349,22 @@
 
 			if (this.CanTransmit())
 			{
-				string message;
-
-				message = "Beginning transmission ";
-
-				if (this.nearestRelay == null)
-				{
-					message += "directly to Kerbin.";
+				StringBuilder message = new StringBuilder();
+
+				message.Append("[" + base.part.partInfo.title + "] ");
+
+				message.Append("Beginning transmission ");
+
+				if (this.relay.nearestRelay == null)
+				{
+					message.Append("directly to Kerbin.");
 				}
 				else
 				{
-					message += "via relay " + this.nearestRelay;
-				}
-
-				ScreenMessages.PostScreenMessage(message, 4f, ScreenMessageStyle.UPPER_LEFT);
+					message.Append("via relay " + this.relay.nearestRelay);
+				}
+
+				ScreenMessages.PostScreenMessage(message.ToString(), 4f, ScreenMessageStyle.UPPER_LEFT);
 
 				base.StartTransmission();
 			}
@@ -496,44 +374,29 @@
 			}
 		}
 
-
-		/*		
-		 * Class implementing IComparer<IAntennaRelay> for use in sorting relays by distance.
-		 * */
-		internal class RelayComparer : IComparer<IAntennaRelay>
-		{
-			/// <summary>
-			/// The reference Vessel (usually the active vessel).
-			/// </summary>
-			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);
+		public void Update()
+		{
+			if (this.actionUIUpdate)
+			{
+				this.UItransmitDistance = Tools.MuMech_ToSI(this.transmitDistance) + "m";
+				this.UIpacketSize = this.CanTransmit() ? Tools.MuMech_ToSI(this.DataRate) + "MiT" : "N/A";
+				this.UIpacketCost = this.CanTransmit() ? Tools.MuMech_ToSI(this.DataResourceCost) + "E" : "N/A";
+			}
+		}
+
+		public void onPartActionUICreate(Part eventPart)
+		{
+			if (eventPart == base.part)
+			{
+				this.actionUIUpdate = true;
+			}
+		}
+
+		public void onPartActionUIDismiss(Part eventPart)
+		{
+			if (eventPart == base.part)
+			{
+				this.actionUIUpdate = false;
 			}
 		}
 
@@ -571,7 +434,7 @@
 				this.DataRate,
 				this.DataResourceCost,
 				ScienceUtil.GetTransmitterScore(this),
-				this.FindNearestRelay()
+				this.relay.FindNearestRelay()
 				);
 			ScreenMessages.PostScreenMessage (new ScreenMessage (msg, 4f, ScreenMessageStyle.UPPER_RIGHT));
 		}

--- /dev/null
+++ b/ProtoAntennaRelay.cs
@@ -1,1 +1,68 @@
+// 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.Linq;
+
+namespace AntennaRange
+{
+	/*
+	 * Wrapper class for ProtoPartModuleSnapshot extending AntennaRelay and implementing IAntennaRelay.
+	 * This is used for finding relays in unloaded Vessels.
+	 * */
+	public class ProtoAntennaRelay : AntennaRelay, IAntennaRelay
+	{
+		// Stores the relay prefab
+		protected IAntennaRelay relayPrefab;
+
+		/// <summary>
+		/// The maximum distance at which this transmitter can operate.
+		/// </summary>
+		/// <value>The max transmit distance.</value>
+		public override float maxTransmitDistance
+		{
+			get
+			{
+				return relayPrefab.maxTransmitDistance;
+			}
+		}
+
+		/// <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 override bool relayChecked
+		{
+			get;
+			protected set;
+		}
+
+		/// <summary>
+		/// Initializes a new instance of the <see cref="AntennaRange.ProtoAntennaRelay"/> class.
+		/// </summary>
+		/// <param name="ms">The ProtoPartModuleSnapshot to wrap</param>
+		/// <param name="vessel">The parent Vessel</param>
+		public ProtoAntennaRelay(IAntennaRelay prefabRelay, Vessel vessel) : base(vessel)
+		{
+			this.relayPrefab = prefabRelay;
+			this.vessel = vessel;
+		}
+	}
+}
+
+