Version 1.10.2 1.10.2
Version 1.10.2

--- a/ARConfiguration.cs
+++ b/ARConfiguration.cs
@@ -5,7 +5,10 @@
 
 using KSP;
 using System;
-using ToadicusTools;
+using ToadicusTools.Extensions;
+using ToadicusTools.Text;
+using ToadicusTools.GUIUtils;
+using ToadicusTools.Wrappers;
 using UnityEngine;
 
 namespace AntennaRange
@@ -275,7 +278,7 @@
 					GUILayout.ExpandWidth(true)
 				);
 
-				configPos = Tools.ClampRectToScreen(configPos, 20);
+				configPos = WindowTools.ClampRectToScreen(configPos, 20);
 
 				if (configPos != this.configWindowPos)
 				{
@@ -291,7 +294,7 @@
 
 			GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
 
-			bool requireLineOfSight = GUITools.Toggle(ARConfiguration.RequireLineOfSight, "Require Line of Sight");
+			bool requireLineOfSight = Layout.Toggle(ARConfiguration.RequireLineOfSight, "Require Line of Sight");
 			if (requireLineOfSight != ARConfiguration.RequireLineOfSight)
 			{
 				ARConfiguration.RequireLineOfSight = requireLineOfSight;
@@ -303,7 +306,7 @@
 			GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
 
 			bool requireConnectionForControl =
-				GUITools.Toggle(
+				Layout.Toggle(
 					ARConfiguration.RequireConnectionForControl,
 					"Require Connection for Probe Control"
 				);
@@ -317,7 +320,7 @@
 
 			GUILayout.BeginHorizontal();
 
-			bool fixedPowerCost = GUITools.Toggle(ARConfiguration.FixedPowerCost, "Use Fixed Power Cost");
+			bool fixedPowerCost = Layout.Toggle(ARConfiguration.FixedPowerCost, "Use Fixed Power Cost");
 			if (fixedPowerCost != ARConfiguration.FixedPowerCost)
 			{
 				ARConfiguration.FixedPowerCost = fixedPowerCost;
@@ -328,7 +331,7 @@
 
 			GUILayout.BeginHorizontal();
 
-			bool useAdditive = GUITools.Toggle(ARConfiguration.UseAdditiveRanges, "Use Additive Ranges");
+			bool useAdditive = Layout.Toggle(ARConfiguration.UseAdditiveRanges, "Use Additive Ranges");
 			if (useAdditive != ARConfiguration.UseAdditiveRanges)
 			{
 				ARConfiguration.UseAdditiveRanges = useAdditive;
@@ -339,7 +342,7 @@
 
 			GUILayout.BeginHorizontal();
 
-			bool prettyLines = GUITools.Toggle(ARConfiguration.PrettyLines, "Draw Pretty Lines");
+			bool prettyLines = Layout.Toggle(ARConfiguration.PrettyLines, "Draw Pretty Lines");
 			if (prettyLines != ARConfiguration.PrettyLines)
 			{
 				ARConfiguration.PrettyLines = prettyLines;

--- a/ARFlightController.cs
+++ b/ARFlightController.cs
@@ -31,7 +31,10 @@
 using KSP;
 using System;
 using System.Collections.Generic;
-using ToadicusTools;
+using ToadicusTools.Extensions;
+using ToadicusTools.Text;
+using ToadicusTools.DebugTools;
+using ToadicusTools.Wrappers;
 using UnityEngine;
 
 namespace AntennaRange
@@ -53,7 +56,7 @@
 		private IButton toolbarButton;
 
 		private ApplicationLauncherButton appLauncherButton;
-		private Tools.DebugLogger log;
+		private PooledDebugLogger log;
 
 		private System.Diagnostics.Stopwatch updateTimer;
 		#endregion
@@ -127,7 +130,7 @@
 		{
 			this.lockID = "ARConnectionRequired";
 
-			this.log = Tools.DebugLogger.New(this);
+			this.log = PooledDebugLogger.New(this);
 
 			this.updateTimer = new System.Diagnostics.Stopwatch();
 
@@ -249,10 +252,13 @@
 
 			this.log.Clear();
 
+			this.log.Append("[ARFlightController]: Update");
+
 			if (HighLogic.LoadedSceneIsFlight && FlightGlobals.ready && FlightGlobals.ActiveVessel != null)
 			{
 				Vessel vessel;
 				IAntennaRelay relay;
+				IAntennaRelay bestActiveRelay;
 				IList<IAntennaRelay> activeVesselRelays;
 
 				usefulRelays.Clear();
@@ -266,36 +272,67 @@
 						continue;
 					}
 
-					log.AppendFormat("Fetching best relay for vessel {0}", vessel);
+					log.AppendFormat("\nFetching best relay for vessel {0}", vessel);
 
 					relay = vessel.GetBestRelay();
 
 					if (relay != null)
 					{
-						log.AppendFormat("Finding nearest relay for best relay {0}", relay);
+						log.AppendFormat("\n\tAdding useful relay {0}", relay);
 
 						usefulRelays.Add(relay);
+					}
+				}
+
+				activeVesselRelays = RelayDatabase.Instance[FlightGlobals.ActiveVessel];
+
+				if (activeVesselRelays.Count > 0)
+				{
+					bestActiveRelay = RelayDatabase.Instance.GetBestVesselRelay(FlightGlobals.ActiveVessel);
+
+					for (int rIdx = 0; rIdx < activeVesselRelays.Count; rIdx++)
+					{
+						relay = activeVesselRelays[rIdx];
+
+						// The best active relay will get checked with the other useful relays later.
+						if (relay == null || relay == bestActiveRelay)
+						{
+							continue;
+						}
+
+						log.AppendFormat("\nFinding nearest relay for active vessel relay {0}", relay);
+
 						relay.FindNearestRelay();
 					}
-				}
-
-				activeVesselRelays = RelayDatabase.Instance[FlightGlobals.ActiveVessel];
-				for (int rIdx = 0; rIdx < activeVesselRelays.Count; rIdx++)
-				{
-					relay = activeVesselRelays[rIdx];
+
+					log.AppendFormat("\n\tAdding best active vessel relay {0} to usefulRelays", bestActiveRelay);
+
+					usefulRelays.Add(bestActiveRelay);
+				}
+
+				log.AppendFormat("\n\tDoing target searches for {0} useful relays", usefulRelays.Count);
+
+				for (int uIdx = 0; uIdx < usefulRelays.Count; uIdx++)
+				{
+					relay = usefulRelays[uIdx];
+
+					if (relay == null)
+					{
+						continue;
+					}
+
+					log.AppendFormat("\n\tDoing target search for useful relay {0}", relay);
 
 					relay.FindNearestRelay();
 				}
 
-				usefulRelays.Add(RelayDatabase.Instance.GetBestVesselRelay(FlightGlobals.ActiveVessel));
-
 				if (this.toolbarButton != null || this.appLauncherButton != null)
 				{
-					log.Append("Checking vessel relay status.\n");
+					log.Append("\nChecking active vessel relay status.");
 
 					this.currentConnectionStatus = FlightGlobals.ActiveVessel.GetConnectionStatus();
 
-					log.AppendFormat("currentConnectionStatus: {0}, setting texture to {1}",
+					log.AppendFormat("\n\tcurrentConnectionStatus: {0}, setting texture to {1}",
 						this.currentConnectionStatus, this.currentConnectionTexture);
 
 					if (this.toolbarButton != null)
@@ -318,7 +355,7 @@
 				}
 			}
 
-			log.Print();
+			log.Print(false);
 		}
 
 		private void OnDestroy()

--- a/ARMapRenderer.cs
+++ b/ARMapRenderer.cs
@@ -31,7 +31,8 @@
 using KSP;
 using System;
 using System.Collections.Generic;
-using ToadicusTools;
+using ToadicusTools.Extensions;
+using ToadicusTools.DebugTools;
 using UnityEngine;
 
 namespace AntennaRange
@@ -51,7 +52,7 @@
 		// Debug Stuff
 		#pragma warning disable 649
 		private System.Diagnostics.Stopwatch timer;
-		private Tools.DebugLogger log;
+		private PooledDebugLogger log;
 		private long relayStart;
 		private long start;
 		#pragma warning restore 649
@@ -99,7 +100,7 @@
 			this.timer = new System.Diagnostics.Stopwatch();
 			#endif
 			#if DEBUG
-			this.log = Tools.DebugLogger.New(this);
+			this.log = PooledDebugLogger.Get(this);
 			#endif
 		}
 

--- a/AntennaRelay.cs
+++ b/AntennaRelay.cs
@@ -28,7 +28,8 @@
 
 using System;
 using System.Collections.Generic;
-using ToadicusTools;
+using ToadicusTools.DebugTools;
+using ToadicusTools.Extensions;
 
 namespace AntennaRange
 {
@@ -203,9 +204,9 @@
 				return;
 			}
 
-			Tools.DebugLogger log;
+			PooledDebugLogger log;
 			#if DEBUG
-			log = Tools.DebugLogger.New(this);
+			log = PooledDebugLogger.New(this);
 			#endif
 
 			#if BENCH
@@ -274,21 +275,28 @@
 			#if BENCH
 			startVesselLoopTicks = performanceTimer.ElapsedTicks;
 			#endif
-
+			
 			for (int rIdx = 0; rIdx < ARFlightController.UsefulRelays.Count; rIdx++)
 			{
 				potentialBestRelay = ARFlightController.UsefulRelays[rIdx];
-				log.AppendFormat("\n\t\tgot best vessel relay {0}",
+				log.AppendFormat("\n\tgot useful relay {0}",
 					potentialBestRelay == null ? "null" : potentialBestRelay.ToString());
 
 				if (potentialBestRelay == null)
 				{
-					log.Append("\n\t\t...skipping null relay");
+					log.Append("\n\t...skipping null relay");
 					continue;
 				}
 
 				if (potentialBestRelay == this || potentialBestRelay.vessel == this.vessel)
 				{
+					log.AppendFormat(
+						"\n\t...skipping relay {0} because it or its vessel ({1}) is the same as ours" +
+						"\n\t\t(our vessel is {2})",
+						potentialBestRelay,
+						potentialBestRelay.vessel == null ? "null" : potentialBestRelay.vessel.vesselName,
+						this.vessel == null ? "null" : this.vessel.vesselName
+					);
 					continue;
 				}
 
@@ -429,11 +437,10 @@
 						// Avoid infinite loops when we're not catching things right.
 						if (iterCount > FlightGlobals.Vessels.Count)
 						{
-							Tools.PostErrorMessage(
-								"[{0}] iterCount exceeded while checking for circular network; assuming it is circular" +
-								"\n\tneedle={1}" +
-								"\n\tthis.moduleRef={2}",
-								this,
+							this.LogError(
+								"iterCount exceeded while checking for circular network; assuming it is circular" +
+								"\n\tneedle={0}" +
+								"\n\tthis.moduleRef={1}",
 								needle == null ? "null" : string.Format(
 									"{0}, needle.KerbinDirect={1}, needle.targetRelay={2}",
 									needle, needle.KerbinDirect, needle.targetRelay == null ? "null" : string.Format(
@@ -505,7 +512,7 @@
 
 			log.AppendFormat("\n{0} ({1}): Search done, figuring status.", this.ToString(), this.GetType().Name);
 			log.AppendFormat(
-				"\n{0}: nearestRelay={1} ({2}m²)), bestOccludedRelay={3} ({4}m²), kerbinSqrDistance={5}m²)",
+				"\n{0}: nearestRelay={1} ({2})), bestOccludedRelay={3} ({4}), kerbinSqrDistance={5}m²)",
 				this,
 				this.nearestRelay == null ? "null" : this.nearestRelay.ToString(),
 				nearestRelaySqrQuotient,
@@ -513,7 +520,7 @@
 				bestOccludedSqrQuotient,
 				kerbinSqrDistance
 			);
-
+			
 			#if BENCH
 			startKerbinLOSTicks = this.performanceTimer.ElapsedTicks;
 			#endif
@@ -848,7 +855,7 @@
 
 				// sb.AppendFormat(Tools.SIFormatter, "", start)
 
-				Tools.PostWarningMessage(sb.ToString());
+				this.LogWarning(sb.ToString());
 
 				Tools.PutStringBuilder(sb);
 			}
@@ -881,7 +888,7 @@
 			AntennaRelay.relayCount++;
 			#endif
 
-			Tools.PostLogMessage("{0}: constructed {1}", this.GetType().Name, this.ToString());
+			this.LogDebug("{0}: constructed {1}", this.GetType().Name, this.ToString());
 		}
 	}
 }

--- a/GameData/AntennaRange/AntennaRange.cfg
+++ b/GameData/AntennaRange/AntennaRange.cfg
@@ -37,7 +37,7 @@
 // maxDataFactor:	The multipler on packetSize that defines the maximum data bandwidth of the antenna.
 // 
 
-@PART[longAntenna]:FOR[AntennaRange]:NEEDS[!RemoteTech2]
+@PART[longAntenna]:FOR[AntennaRange]:NEEDS[!RemoteTech]
 {
 	@MODULE[ModuleDataTransmitter]
 	{
@@ -59,7 +59,7 @@
 	}
 }
 
-@PART[mediumDishAntenna]:FOR[AntennaRange]:NEEDS[!RemoteTech2]
+@PART[mediumDishAntenna]:FOR[AntennaRange]:NEEDS[!RemoteTech]
 {
 	@MODULE[ModuleDataTransmitter]
 	{
@@ -81,7 +81,7 @@
 	}
 }
 
-@PART[commDish]:FOR[AntennaRange]:NEEDS[!RemoteTech2]
+@PART[commDish]:FOR[AntennaRange]:NEEDS[!RemoteTech]
 {
 	@MODULE[ModuleDataTransmitter]
 	{
@@ -91,6 +91,31 @@
 		simpleRange = 56250000000
 		maxPowerFactor = 16
 		maxDataFactor = 2
+	}
+
+	MODULE
+	{
+		name = ModuleScienceContainer
+
+		dataIsCollectable = true
+		dataIsStorable = false
+
+		storageRange = 2
+	}
+}
+
+@PART[HighGainAntenna]:FOR[AntennaRange]:NEEDS[AsteroidDay,!RemoteTech]
+{
+	@TechRequired = electronics
+	@description = Repurposed for medium range probes, the HG-55 provdes high speed directional data transmission.
+
+	@MODULE[ModuleDataTransmitter]
+	{
+		@name = ModuleLimitedDataTransmitter
+		nominalRange = 7774867578
+		simpleRange = 25030376544
+		maxPowerFactor = 2.6180339887498948
+		maxDataFactor = 9
 	}
 
 	MODULE
@@ -130,7 +155,7 @@
 EVA_RESOURCE
 {
 	name = ElectricCharge
-	amount = 100
+	amount = 0
 	maxAmount = 100
 }
 

--- a/ModuleLimitedDataTransmitter.cs
+++ b/ModuleLimitedDataTransmitter.cs
@@ -30,7 +30,9 @@
 using System;
 using System.Collections.Generic;
 using System.Text;
-using ToadicusTools;
+using ToadicusTools.DebugTools;
+using ToadicusTools.Extensions;
+using ToadicusTools.Text;
 using UnityEngine;
 
 namespace AntennaRange
@@ -49,6 +51,11 @@
 	public class ModuleLimitedDataTransmitter
 		: ModuleDataTransmitter, IScienceDataTransmitter, IAntennaRelay, IModuleInfo
 	{
+		private const string tooltipSkinName = "PartTooltipSkin";
+		private static GUISkin partTooltipSkin;
+		private static GUIStyle partTooltipBodyStyle;
+		private static GUIStyle partTooltipHeaderStyle;
+
 		// Stores the packetResourceCost as defined in the .cfg file.
 		private float _basepacketResourceCost;
 
@@ -423,20 +430,19 @@
 			this._basepacketResourceCost = base.packetResourceCost;
 			this.moduleInfoContent = new GUIContent();
 
-			Tools.PostDebugMessage(string.Format(
-				"{0} loaded:\n" +
+			this.LogDebug("{0} loaded:\n" +
 				"packetSize: {1}\n" +
 				"packetResourceCost: {2}\n" +
 				"nominalTransmitDistance: {3}\n" +
 				"maxPowerFactor: {4}\n" +
 				"maxDataFactor: {5}\n",
-				this.name,
+				this,
 				base.packetSize,
 				this._basepacketResourceCost,
 				this.nominalTransmitDistance,
 				this.maxPowerFactor,
 				this.maxDataFactor
-			));
+			);
 		}
 
 		/// <summary>
@@ -455,7 +461,7 @@
 				this.relay.nominalTransmitDistance = this.nominalTransmitDistance;
 				this.relay.maxTransmitDistance = this.maxTransmitDistance;
 
-				this.UImaxTransmitDistance = string.Format(Tools.SIFormatter, "{0:S3}m", this.maxTransmitDistance);
+				this.UImaxTransmitDistance = TextTools.Format("{0:S3}m", this.maxTransmitDistance);
 
 				GameEvents.onPartActionUICreate.Add(this.onPartActionUICreate);
 				GameEvents.onPartActionUIDismiss.Add(this.onPartActionUIDismiss);
@@ -500,21 +506,45 @@
 		{
 			this.moduleInfoContent.text = this.GetInfo();
 
-			GUIStyle style0 = PartListTooltips.fetch.tooltipSkin.customStyles[0];
-			GUIStyle style1 = PartListTooltips.fetch.tooltipSkin.customStyles[1];
+			if (partTooltipSkin == null)
+			{
+				UnityEngine.Object[] skins = Resources.FindObjectsOfTypeAll(typeof(GUISkin));
+				GUISkin skin;
+				for (int sIdx = 0; sIdx < skins.Length; sIdx++)
+				{
+					skin = (GUISkin)skins[sIdx];
+
+					if (skin.name == tooltipSkinName)
+					{
+						partTooltipSkin = skin;
+						partTooltipBodyStyle = partTooltipSkin.customStyles[0];
+						partTooltipHeaderStyle = partTooltipSkin.customStyles[1];
+					}
+				}
+
+				if (partTooltipSkin == null)
+				{
+					this.LogError("Could not find GUISkin {0}?  Please report this!", tooltipSkinName);
+					return;
+				}
+				else
+				{
+					this.Log("Loaded GUISkin {0}", tooltipSkinName);
+				}
+			}
 
 			float width = rect.width;
 			float orgHeight = rect.height;
-			float height = style0.CalcHeight(this.moduleInfoContent, width);
+			float height = partTooltipBodyStyle.CalcHeight(this.moduleInfoContent, width);
 
 			rect.height = height;
 
-			GUI.Box(rect, this.moduleInfoContent, style0);
-			GUI.Label(rect, this.GetModuleTitle(), style1);
+			GUI.Box(rect, this.moduleInfoContent, partTooltipBodyStyle);
+			GUI.Label(rect, this.GetModuleTitle(), partTooltipHeaderStyle);
 
 			GUILayout.Space(height - orgHeight
-				- style0.padding.bottom - style0.padding.top
-				- 2f * (style0.margin.bottom + style0.margin.top)
+				- partTooltipBodyStyle.padding.bottom - partTooltipBodyStyle.padding.top
+				- 2f * (partTooltipBodyStyle.margin.bottom + partTooltipBodyStyle.margin.top)
 			);
 		}
 
@@ -531,34 +561,34 @@
 		/// </summary>
 		public override string GetInfo()
 		{
-			StringBuilder sb = Tools.GetStringBuilder();
-			string text;
-
-			sb.Append(base.GetInfo());
-
-			if (ARConfiguration.UseAdditiveRanges)
-			{
-				sb.AppendFormat(Tools.SIFormatter, "Nominal Range to Kerbin: {0:S3}m\n",
-					Math.Sqrt(this.nominalTransmitDistance * ARConfiguration.KerbinNominalRange)
-				);
-				sb.AppendFormat(Tools.SIFormatter, "Maximum Range to Kerbin: {0:S3}m",
-					Math.Sqrt(
-						this.nominalTransmitDistance * Math.Sqrt(this.maxPowerFactor) *
-						ARConfiguration.KerbinRelayRange
-					)
-				);
-			}
-			else
-			{
-				sb.AppendFormat(Tools.SIFormatter, "Nominal Range: {0:S3}m\n", this.nominalTransmitDistance);
-				sb.AppendFormat(Tools.SIFormatter, "Maximum Range: {0:S3}m", this.maxTransmitDistance);
-			}
-
-			text = sb.ToString();
-
-			Tools.PutStringBuilder(sb);
-
-			return text;
+			using (PooledStringBuilder sb = PooledStringBuilder.Get())
+			{
+				string text;
+
+				sb.Append(base.GetInfo());
+
+				if (ARConfiguration.UseAdditiveRanges)
+				{
+					sb.AppendFormat("Nominal Range to Kerbin: {0:S3}m\n",
+						Math.Sqrt(this.nominalTransmitDistance * ARConfiguration.KerbinNominalRange)
+					);
+					sb.AppendFormat("Maximum Range to Kerbin: {0:S3}m",
+						Math.Sqrt(
+							this.nominalTransmitDistance * Math.Sqrt(this.maxPowerFactor) *
+							ARConfiguration.KerbinRelayRange
+						)
+					);
+				}
+				else
+				{
+					sb.AppendFormat("Nominal Range: {0:S3}m\n", this.nominalTransmitDistance);
+					sb.AppendFormat("Maximum Range: {0:S3}m", this.maxTransmitDistance);
+				}
+
+				text = sb.ToString();
+
+				return text;
+			}
 		}
 
 		/// <summary>
@@ -576,13 +606,13 @@
 			{
 				case PartStates.DEAD:
 				case PartStates.DEACTIVATED:
-					Tools.PostDebugMessage(string.Format(
+					this.LogDebug(
 						"{0}: {1} on {2} cannot transmit: {3}",
 						this.GetType().Name,
 						this.part.partInfo.title,
 						this.vessel.vesselName,
 						Enum.GetName(typeof(PartStates), this.part.State)
-					));
+					);
 					return false;
 				default:
 					break;
@@ -641,9 +671,9 @@
 			}
 			else
 			{
-				Tools.PostDebugMessage(this, "{0} unable to transmit during TransmitData.", this.part.partInfo.title);
-
-				var logger = Tools.DebugLogger.New(this);
+				this.LogDebug("{0} unable to transmit during TransmitData.", this.part.partInfo.title);
+
+				var logger = PooledDebugLogger.New(this);
 
 				IList<ModuleScienceContainer> vesselContainers = this.vessel.getModulesOfType<ModuleScienceContainer>();
 				ModuleScienceContainer scienceContainer;
@@ -699,31 +729,30 @@
 
 				if (dataQueue.Count > 0)
 				{
-					StringBuilder sb = Tools.GetStringBuilder();
-
-					sb.Append('[');
-					sb.Append(this.part.partInfo.title);
-					sb.AppendFormat("]: {0} data items could not be saved: no space available in data containers.\n");
-					sb.Append("Data to be discarded:\n");
-
-					ScienceData data;
-					for (int dIdx = 0; dIdx < dataQueue.Count; dIdx++)
+					using (PooledStringBuilder sb = PooledStringBuilder.Get())
 					{
-						data = dataQueue[dIdx];
-						sb.AppendFormat("\t{0}\n", data.title);
+						sb.Append('[');
+						sb.Append(this.part.partInfo.title);
+						sb.AppendFormat("]: {0} data items could not be saved: no space available in data containers.\n");
+						sb.Append("Data to be discarded:\n");
+
+						ScienceData data;
+						for (int dIdx = 0; dIdx < dataQueue.Count; dIdx++)
+						{
+							data = dataQueue[dIdx];
+							sb.AppendFormat("\t{0}\n", data.title);
+						}
+
+						ScreenMessages.PostScreenMessage(sb.ToString(), 4f, ScreenMessageStyle.UPPER_LEFT);
+
+						this.LogDebug(sb.ToString());
 					}
-
-					ScreenMessages.PostScreenMessage(sb.ToString(), 4f, ScreenMessageStyle.UPPER_LEFT);
-
-					Tools.PostDebugMessage(sb.ToString());
-
-					Tools.PutStringBuilder(sb);
 				}
 
 				this.PostCannotTransmitError();
 			}
 
-			Tools.PostDebugMessage (
+			this.LogDebug(
 				"distance: " + this.CurrentLinkSqrDistance
 				+ " packetSize: " + this.packetSize
 				+ " packetResourceCost: " + this.packetResourceCost
@@ -756,7 +785,7 @@
 			PreTransmit_SetPacketSize ();
 			PreTransmit_SetPacketResourceCost ();
 
-			Tools.PostDebugMessage (
+			this.LogDebug(
 				"distance: " + this.CurrentLinkSqrDistance
 				+ " packetSize: " + this.packetSize
 				+ " packetResourceCost: " + this.packetResourceCost
@@ -781,31 +810,31 @@
 		{
 			if (this.actionUIUpdate)
 			{
-				this.UImaxTransmitDistance = string.Format(Tools.SIFormatter, "{0:S3}m",
+				this.UImaxTransmitDistance = TextTools.Format("{0:S3}m",
 					Math.Sqrt(this.MaximumLinkSqrDistance));
-				this.UInominalLinkDistance = string.Format(Tools.SIFormatter, "{0:S3}m",
+				this.UInominalLinkDistance = TextTools.Format("{0:S3}m",
 					Math.Sqrt(this.NominalLinkSqrDistance));
 				
 				if (this.CanTransmit())
 				{
 					this.UIrelayStatus = this.LinkStatus.ToString();
-					this.UItransmitDistance = string.Format(Tools.SIFormatter, "{0:S3}m",
+					this.UItransmitDistance = TextTools.Format("{0:S3}m",
 						Math.Sqrt(this.CurrentLinkSqrDistance));
-					this.UIpacketSize = string.Format(Tools.SIFormatter, "{0:S3}MiT", this.DataRate);
-					this.UIpacketCost = string.Format(Tools.SIFormatter, "{0:S3}EC", this.DataResourceCost);
+					this.UIpacketSize = TextTools.Format("{0:S3}MiT", this.DataRate);
+					this.UIpacketCost = TextTools.Format("{0:S3}EC", this.DataResourceCost);
 				}
 				else
 				{
 					if (this.relay.firstOccludingBody == null)
 					{
-						this.UItransmitDistance = string.Format(Tools.SIFormatter, "{0:S3}m",
+						this.UItransmitDistance = TextTools.Format("{0:S3}m",
 							Math.Sqrt(this.CurrentLinkSqrDistance));
 						this.UIrelayStatus = "Out of range";
 					}
 					else
 					{
 						this.UItransmitDistance = "N/A";
-						this.UIrelayStatus = string.Format("Blocked by {0}", this.relay.firstOccludingBody.bodyName);
+						this.UIrelayStatus = TextTools.Format("Blocked by {0}", this.relay.firstOccludingBody.bodyName);
 					}
 					this.UIpacketSize = "N/A";
 					this.UIpacketCost = "N/A";
@@ -817,7 +846,14 @@
 				}
 				else
 				{
-					this.UIrelayTarget = this.targetRelay.ToString();
+					if (this.targetRelay != null)
+					{
+						this.UIrelayTarget = this.targetRelay.ToString();
+					}
+					else
+					{
+						this.UIrelayTarget = "A mysterious null entity";
+					}
 				}
 			}
 		}
@@ -828,32 +864,31 @@
 		/// <returns>A <see cref="System.String"/> that represents the current <see cref="AntennaRange.ModuleLimitedDataTransmitter"/>.</returns>
 		public override string ToString()
 		{
-			StringBuilder sb = Tools.GetStringBuilder();
-			string msg;
-
-			sb.Append(this.part.partInfo.title);
-
-			if (vessel != null)
-			{
-				sb.Append(" on ");
-				sb.Append(vessel.vesselName);
-			}
-			else if (
-				this.part != null &&
-				this.part.protoPartSnapshot != null &&
-				this.part.protoPartSnapshot != null &&
-				this.part.protoPartSnapshot.pVesselRef != null
-			)
-			{
-				sb.Append(" on ");
-				sb.Append(this.part.protoPartSnapshot.pVesselRef.vesselName);
-			}
-
-			msg = sb.ToString();
-
-			Tools.PutStringBuilder(sb);
-
-			return msg;
+			using (PooledStringBuilder sb = PooledStringBuilder.Get())
+			{
+				string msg;
+
+				sb.Append(this.part.partInfo.title);
+
+				if (vessel != null)
+				{
+					sb.Append(" on ");
+					sb.Append(vessel.vesselName);
+				}
+				else if (
+					this.part != null &&
+					this.part.protoPartSnapshot != null &&
+					this.part.protoPartSnapshot != null &&
+					this.part.protoPartSnapshot.pVesselRef != null)
+				{
+					sb.Append(" on ");
+					sb.Append(this.part.protoPartSnapshot.pVesselRef.vesselName);
+				}
+
+				msg = sb.ToString();
+
+				return msg;
+			}
 		}
 
 		// When we catch an onPartActionUICreate event for our part, go ahead and update every frame to look pretty.
@@ -889,7 +924,7 @@
 				ErrorText
 			);
 
-			Tools.PostDebugMessage(this.GetType().Name + ": " + this.ErrorMsg.message);
+			this.LogDebug(this.ErrorMsg.message);
 
 			ScreenMessages.PostScreenMessage(this.ErrorMsg, false);
 		}
@@ -936,30 +971,30 @@
 
 		private string buildTransmitMessage()
 		{
-			StringBuilder sb = Tools.GetStringBuilder();
-			string msg;
-
-			sb.Append("[");
-			sb.Append(base.part.partInfo.title);
-			sb.Append("]: ");
-
-			sb.Append("Beginning transmission ");
-
-			if (this.KerbinDirect)
-			{
-				sb.Append("directly to Kerbin.");
-			}
-			else
-			{
-				sb.Append("via ");
-				sb.Append(this.relay.targetRelay);
-			}
-
-			msg = sb.ToString();
-
-			Tools.PutStringBuilder(sb);
-
-			return msg;
+			using (PooledStringBuilder sb = PooledStringBuilder.Get())
+			{
+				string msg;
+
+				sb.Append("[");
+				sb.Append(base.part.partInfo.title);
+				sb.Append("]: ");
+
+				sb.Append("Beginning transmission ");
+
+				if (this.KerbinDirect)
+				{
+					sb.Append("directly to Kerbin.");
+				}
+				else
+				{
+					sb.Append("via ");
+					sb.Append(this.relay.targetRelay);
+				}
+
+				msg = sb.ToString();
+
+				return msg;
+			}
 		}
 
 		#if DEBUG

--- a/Properties/AssemblyInfo.cs
+++ b/Properties/AssemblyInfo.cs
@@ -39,7 +39,7 @@
 // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
 // The form "{Major}.{Minor}.*" will automatically update the build and revision,
 // and "{Major}.{Minor}.{Build}.*" will update just the revision.
-[assembly: AssemblyVersion("1.10.*")]
+[assembly: AssemblyVersion("1.10.2.*")]
 // The following attributes are used to specify the signing key for the assembly,
 // if desired. See the Mono documentation for more information about signing.
 //[assembly: AssemblyDelaySign(false)]

--- a/ProtoAntennaRelay.cs
+++ b/ProtoAntennaRelay.cs
@@ -29,6 +29,7 @@
 using KSP;
 using System;
 using ToadicusTools;
+using ToadicusTools.Text;
 
 namespace AntennaRange
 {
@@ -58,8 +59,7 @@
 				}
 				else
 				{
-					Tools.PostErrorMessage("{0}: Could not fetch vessel!  {1}{2}{3}",
-						this.ToString(),
+					this.LogError("Could not fetch vessel!  {0}{1}{2}",
 						this.protoPart == null ? "\n\tprotoPart=null" : string.Empty,
 						this.protoPart != null && this.protoPart.pVesselRef == null ?
 							"\n\tthis.protoPart.pVesselRef=null" : string.Empty,
@@ -120,7 +120,7 @@
 			PartStates partState = (PartStates)this.protoPart.state;
 			if (partState == PartStates.DEAD || partState == PartStates.DEACTIVATED)
 			{
-				Tools.PostDebugMessage(string.Format(
+				Logging.PostDebugMessage(string.Format(
 					"{0}: {1} on {2} cannot transmit: {3}",
 					this.GetType().Name,
 					this.Title,
@@ -138,18 +138,17 @@
 		/// <returns>A <see cref="System.String"/> that represents the current <see cref="AntennaRange.ProtoAntennaRelay"/>.</returns>
 		public override string ToString()
 		{
-			System.Text.StringBuilder sb = Tools.GetStringBuilder();
+			using (PooledStringBuilder sb = PooledStringBuilder.Get())
+			{
+				sb.Append(this.Title);
 
-			sb.Append(this.Title);
+				if (this.protoPart != null && this.protoPart.pVesselRef != null)
+				{
+					sb.AppendFormat(" on {0}", this.protoPart.pVesselRef.vesselName);
+				}
 
-			if (this.protoPart != null && this.protoPart.pVesselRef != null)
-			{
-				sb.AppendFormat(" on {0}", this.protoPart.pVesselRef.vesselName);
+				return sb.ToString();
 			}
-
-			Tools.PutStringBuilder(sb);
-
-			return sb.ToString();
 		}
 
 		/// <summary>
@@ -162,7 +161,7 @@
 		{
 			this.protoPart = pps;
 
-			Tools.PostLogMessage("{0}: constructed {1}", this.GetType().Name, this.ToString());
+			this.Log("constructed ({0})", this.GetType().Name);
 		}
 	}
 }

--- a/RelayDatabase.cs
+++ b/RelayDatabase.cs
@@ -96,10 +96,10 @@
 		public void DirtyVessel(Vessel vessel)
 		{
 			#if DEBUG
-			Tools.PostDebugMessage("RelayDatabase: Dirtying cache for vessel {0} in frame {1}",
+			Logging.PostDebugMessage("RelayDatabase: Dirtying cache for vessel {0} in frame {1}",
 				vessel, new System.Diagnostics.StackTrace().ToString());
 			#else
-			Tools.PostLogMessage("RelayDatabase: Dirtying cache for vessel {0}", vessel.vesselName);
+			Logging.PostLogMessage("RelayDatabase: Dirtying cache for vessel {0}", vessel.vesselName);
 			#endif
 
 			this.relayDatabase.Remove(vessel.id);
@@ -109,7 +109,7 @@
 
 		public void ClearCache()
 		{
-			Tools.PostLogMessage("RelayDatabase: onSceneChange clearing entire cache.");
+			Logging.PostLogMessage("RelayDatabase: onSceneChange clearing entire cache.");
 
 			this.relayDatabase.Clear();
 			this.bestRelayTable.Clear();
@@ -206,7 +206,7 @@
 				// or if we've just docked or undocked)...
 				if (this.vesselPartCountTable[vessel.id] != vessel.Parts.Count || vessel.loaded)
 				{
-					Tools.PostDebugMessage(string.Format(
+					Logging.PostDebugMessage(string.Format(
 						"{0}: dirtying cache for vessel '{1}' ({2}).",
 						this.GetType().Name,
 						vessel.vesselName,
@@ -222,7 +222,7 @@
 		// Runs when the player requests a scene change, such as when changing vessels or leaving flight.
 		private void onSceneChange(GameScenes scene)
 		{
-			Tools.PostDebugMessage(
+			Logging.PostDebugMessage(
 				"RelayDatabase: caught onSceneChangeRequested in scene {0} to scene {1}.  ActiveVessel is {2}",
 				HighLogic.LoadedScene,
 				scene,
@@ -235,7 +235,7 @@
 				{
 					if (FlightGlobals.ActiveVessel != null)
 					{
-						Tools.PostDebugMessage("RelayDatabase: onSceneChange clearing {0} from cache.",
+						Logging.PostDebugMessage("RelayDatabase: onSceneChange clearing {0} from cache.",
 							FlightGlobals.ActiveVessel.vesselName);
 
 						this.onVesselEvent(FlightGlobals.ActiveVessel);
@@ -275,7 +275,7 @@
 			// We're going to completely regen this table, so dump the current contents.
 			relays.Clear();
 
-			Tools.PostDebugMessage(string.Format(
+			Logging.PostDebugMessage(string.Format(
 				"{0}: Getting antenna relays from vessel {1}.",
 				"IAntennaRelay",
 				vessel.vesselName
@@ -287,7 +287,7 @@
 
 			// If the vessel is loaded, we can fetch modules implementing IAntennaRelay directly.
 			if (vessel.loaded) {
-				Tools.PostDebugMessage(string.Format(
+				Logging.PostDebugMessage(string.Format(
 					"{0}: vessel {1} is loaded, searching for modules in loaded parts.",
 					"IAntennaRelay",
 					vessel.vesselName
@@ -327,7 +327,7 @@
 			// If the vessel is not loaded, we need to build ProtoAntennaRelays when we find relay ProtoPartSnapshots.
 			else
 			{
-				Tools.PostDebugMessage(string.Format(
+				Logging.PostDebugMessage(string.Format(
 					"{0}: vessel {1} is not loaded, searching for modules in prototype parts.",
 					this.GetType().Name,
 					vessel.vesselName
@@ -339,7 +339,7 @@
 				{
 					pps = vessel.protoVessel.protoPartSnapshots[ppsIdx];
 
-					Tools.PostDebugMessage(string.Format(
+					Logging.PostDebugMessage(string.Format(
 						"{0}: Searching in protopartsnapshot {1}",
 						this.GetType().Name,
 						pps
@@ -348,7 +348,7 @@
 					// ...Fetch the prefab, because it's more useful for what we're doing.
 					Part partPrefab = PartLoader.getPartInfoByName(pps.partName).partPrefab;
 
-					Tools.PostDebugMessage(string.Format(
+					Logging.PostDebugMessage(string.Format(
 						"{0}: Got partPrefab {1} in protopartsnapshot {2}",
 						this.GetType().Name,
 						partPrefab,
@@ -361,7 +361,7 @@
 					{
 						module = partPrefab.Modules[modIdx];
 
-						Tools.PostDebugMessage(string.Format(
+						Logging.PostDebugMessage(string.Format(
 							"{0}: Searching in partmodule {1}",
 							this.GetType().Name,
 							module
@@ -370,7 +370,7 @@
 						// ...if the module is a relay...
 						if (module is IAntennaRelay)
 						{
-							Tools.PostDebugMessage(string.Format(
+							Logging.PostDebugMessage(string.Format(
 								"{0}: partmodule {1} is antennarelay",
 								this.GetType().Name,
 								module
@@ -395,7 +395,7 @@
 
 			this.bestRelayTable[vessel.id] = bestRelay;
 
-			Tools.PostDebugMessage(string.Format(
+			Logging.PostDebugMessage(string.Format(
 				"{0}: vessel '{1}' ({2}) has {3} transmitters.",
 				"IAntennaRelay",
 				vessel.vesselName,
@@ -436,7 +436,7 @@
 			GameEvents.onPartUndock.Remove(this.onPartEvent);
 			GameEvents.onGameStateLoad.Remove(this.onGameLoaded);
 
-			Tools.PostDebugMessage(this.GetType().Name + " destroyed.");
+			Logging.PostDebugMessage(this.GetType().Name + " destroyed.");
 
 			KSPLog.print(string.Format(
 				"{0} destructed.  Cache hits: {1}, misses: {2}, hit rate: {3:P1}",
@@ -469,7 +469,7 @@
 				}
 			}
 
-			Tools.PostDebugMessage(sb.ToString());
+			Logging.PostDebugMessage(sb.ToString());
 
 			Tools.PutStringBuilder(sb);
 		}

--- a/RelayExtensions.cs
+++ b/RelayExtensions.cs
@@ -28,7 +28,7 @@
 
 using System;
 using System.Collections.Generic;
-using ToadicusTools;
+using ToadicusTools.Extensions;
 
 namespace AntennaRange
 {
@@ -214,6 +214,72 @@
 		public static IAntennaRelay GetBestRelay(this Vessel vessel)
 		{
 			return RelayDatabase.Instance.GetBestVesselRelay(vessel);
+		}
+
+		/// <summary>
+		/// Logs a message on behalf of this relay
+		/// </summary>
+		public static void Log(this AntennaRelay relay, string format, params object[] args)
+		{
+			ToadicusTools.Logging.PostLogMessage(string.Format("[{0}] {1}", relay.ToString(), format), args);
+		}
+
+		/// <summary>
+		/// Logs a message on behalf of this relay
+		/// </summary>
+		public static void Log(this AntennaRelay relay, string msg)
+		{
+			ToadicusTools.Logging.PostLogMessage("[{0}] {1}", relay.ToString(), msg);
+		}
+
+		/// <summary>
+		/// Logs a warning message on behalf of this relay
+		/// </summary>
+		public static void LogWarning(this AntennaRelay relay, string format, params object[] args)
+		{
+			ToadicusTools.Logging.PostWarningMessage(string.Format("[{0}] {1}", relay.ToString(), format), args);
+		}
+
+		/// <summary>
+		/// Logs a warning message on behalf of this relay
+		/// </summary>
+		public static void LogWarning(this AntennaRelay relay, string msg)
+		{
+			ToadicusTools.Logging.PostWarningMessage("[{0}] {1}", relay.ToString(), msg);
+		}
+
+		/// <summary>
+		/// Logs an error message on behalf of this relay
+		/// </summary>
+		public static void LogError(this AntennaRelay relay, string format, params object[] args)
+		{
+			ToadicusTools.Logging.PostErrorMessage(string.Format("[{0}] {1}", relay.ToString(), format), args);
+		}
+
+		/// <summary>
+		/// Logs an error message on behalf of this relay
+		/// </summary>
+		public static void LogError(this AntennaRelay relay, string msg)
+		{
+			ToadicusTools.Logging.PostErrorMessage("[{0}] {1}", relay.ToString(), msg);
+		}
+
+		/// <summary>
+		/// Logs a debug-only message on behalf of this relay
+		/// </summary>
+		[System.Diagnostics.Conditional("DEBUG")]
+		public static void LogDebug(this AntennaRelay relay, string format, params object[] args)
+		{
+			ToadicusTools.Logging.PostDebugMessage(string.Format("[{0}] {1}", relay.ToString(), format), args);
+		}
+
+		/// <summary>
+		/// Logs a debug-only message on behalf of this relay
+		/// </summary>
+		[System.Diagnostics.Conditional("DEBUG")]
+		public static void LogDebug(this AntennaRelay relay, string msg)
+		{
+			ToadicusTools.Logging.PostDebugMessage("[{0}] {1}", relay.ToString(), msg);
 		}
 	}