Megacommit towards making relays that work for CelestialBodies. I think I'm overcomplicating this. BodyRelayDev
Megacommit towards making relays that work for CelestialBodies. I think I'm overcomplicating this.

--- a/ARConfiguration.cs
+++ b/ARConfiguration.cs
@@ -24,6 +24,8 @@
 		private const string PRETTY_LINES_KEY = "drawPrettyLines";
 		private const string UPDATE_DELAY_KEY = "updateDelay";
 
+		private bool runOnce;
+
 		/// <summary>
 		/// Indicates whether connections require line of sight.
 		/// </summary>
@@ -133,9 +135,23 @@
 
 			GameEvents.onGameSceneLoadRequested.Add(this.onSceneChangeRequested);
 
+			this.runOnce = true;
+
 			Debug.Log(string.Format("{0} v{1} - ARConfiguration loaded!", this.GetType().Name, this.runningVersion));
 
 			Tools.PostDebugMessage(this, "Awake.");
+		}
+
+		public void Update()
+		{
+			if (runOnce)
+			{
+				KerbinRelay.TrackingStationMaxLevel =
+					ScenarioUpgradeableFacilities.protoUpgradeables[SpaceCenterFacility.TrackingStation.ToString()]
+						.facilityRefs[0].MaxLevel;
+				
+				runOnce = false;
+			}
 		}
 
 		public void OnGUI()

--- a/ARMapRenderer.cs
+++ b/ARMapRenderer.cs
@@ -39,7 +39,7 @@
 	public class ARMapRenderer : MonoBehaviour
 	{
 		#region Fields
-		private Dictionary<Guid, LineRenderer> vesselLineRenderers;
+		private Dictionary<IPositionedObject, LineRenderer> vesselLineRenderers;
 
 		// Debug Stuff
 		#pragma warning disable 649
@@ -55,13 +55,13 @@
 		#endregion
 
 		#region Properties
-		public LineRenderer this[Guid idx]
+		public LineRenderer this[IPositionedObject idx]
 		{
 			get
 			{
 				if (this.vesselLineRenderers == null)
 				{
-					this.vesselLineRenderers = new Dictionary<Guid, LineRenderer>();
+					this.vesselLineRenderers = new Dictionary<IPositionedObject, LineRenderer>();
 				}
 
 				LineRenderer lr;
@@ -94,7 +94,7 @@
 		{
 			if (ARConfiguration.PrettyLines)
 			{
-				this.vesselLineRenderers = new Dictionary<Guid, LineRenderer>();
+				this.vesselLineRenderers = new Dictionary<IPositionedObject, LineRenderer>();
 			}
 
 			#if DEBUG
@@ -216,14 +216,14 @@
 		{
 			log.AppendFormat("\n\t\tDrawing line for relay chain starting at {0}.", relay);
 
-			if (relay.vessel == null)
+			if (relay.Host == null)
 			{
 				log.Append("\n\t\tvessel is null, bailing out");
 				return;
 			}
 
-			LineRenderer renderer = this[relay.vessel.id];
-			Vector3d start = ScaledSpace.LocalToScaledSpace(relay.vessel.GetWorldPos3D());
+			LineRenderer renderer = this[relay.Host];
+			Vector3d start = ScaledSpace.LocalToScaledSpace(relay.Host.WorldPos);
 
 			float lineWidth;
 			float d = Screen.height / 2f + 0.01f;
@@ -273,11 +273,11 @@
 
 			if (relay.KerbinDirect)
 			{
-				nextPoint = ScaledSpace.LocalToScaledSpace(AntennaRelay.Kerbin.position);
+				nextPoint = ScaledSpace.LocalToScaledSpace(AntennaRelay.Kerbin.WorldPos);
 			}
 			else
 			{
-				if (relay.targetRelay == null || relay.targetRelay.vessel == null)
+				if (relay.targetRelay == null || relay.targetRelay.Host == null)
 				{
 					this.LogError(
 						"SetRelayVertices: relay {0} has null target relay or vessel when not KerbinDirect, bailing out!",
@@ -288,7 +288,7 @@
 					return;
 				}
 
-				nextPoint = ScaledSpace.LocalToScaledSpace(relay.targetRelay.vessel.GetWorldPos3D());
+				nextPoint = ScaledSpace.LocalToScaledSpace(relay.targetRelay.Host.WorldPos);
 			}
 
 			renderer.SetColors(thisColor, thisColor);

--- a/AntennaRange.csproj
+++ b/AntennaRange.csproj
@@ -80,6 +80,8 @@
     <Compile Include="ARConfiguration.cs" />
     <Compile Include="ARFlightController.cs" />
     <Compile Include="ARMapRenderer.cs" />
+    <Compile Include="PositionedObject.cs" />
+    <Compile Include="KerbinRelay.cs" />
   </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
   <ItemGroup>

--- a/AntennaRelay.cs
+++ b/AntennaRelay.cs
@@ -35,21 +35,21 @@
 	/// <summary>
 	/// Relay code at the heart of AntennaRange
 	/// </summary>
-	public class AntennaRelay
+	public class AntennaRelay : IAntennaRelay
 	{
 		// We don't have a Bard, so we'll hide Kerbin here.
-		private static CelestialBody _Kerbin;
+		private static BodyWrapper _Kerbin;
 
 		/// <summary>
 		/// Fetches, caches, and returns a <see cref="CelestialBody"/> reference to Kerbin
 		/// </summary>
-		public static CelestialBody Kerbin
+		public static BodyWrapper Kerbin
 		{
 			get
 			{
 				if (_Kerbin == null && FlightGlobals.ready)
 				{
-					_Kerbin = FlightGlobals.GetHomeBody();
+					_Kerbin = (BodyWrapper)FlightGlobals.GetHomeBody();
 				}
 
 				return _Kerbin;
@@ -72,11 +72,11 @@
 		/// Gets the parent Vessel.
 		/// </summary>
 		/// <value>The parent Vessel.</value>
-		public virtual Vessel vessel
+		public virtual IPositionedObject Host
 		{
 			get
 			{
-				return this.moduleRef.vessel;
+				return this.moduleRef.Host;
 			}
 		}
 
@@ -144,6 +144,14 @@
 		{
 			get;
 			protected set;
+		}
+
+		public virtual string Title
+		{
+			get
+			{
+				return this.moduleRef.Title;
+			}
 		}
 
 		/// <summary>
@@ -251,7 +259,7 @@
 				
 				log.Append("\n\tchecking if vessel is this vessel");
 				// Skip vessels with the wrong ID
-				if (potentialVessel.id == vessel.id)
+				if (OBJ.ReferenceEquals(potentialVessel, Host.HostObject))
 				{
 					log.Append("\n\tSkipping because vessel is this vessel.");
 					continue;
@@ -276,7 +284,7 @@
 				// Skip vessels to which we do not have line of sight.
 				if (
 					ARConfiguration.RequireLineOfSight &&
-					!this.vessel.hasLineOfSightTo(potentialVessel, out fob, ARConfiguration.RadiusRatio)
+					!this.Host.hasLineOfSightTo((VesselWrapper)potentialVessel, out fob, ARConfiguration.RadiusRatio)
 				)
 				{
 					log.Append("\n\t\t...failed LOS check");
@@ -346,7 +354,7 @@
 							break;
 						}
 
-						if (needle.targetRelay.vessel == this.vessel || needle == this.moduleRef)
+						if (needle.targetRelay.Host == this.Host || needle == this.moduleRef)
 						{
 							isCircular = true;
 							break;
@@ -363,10 +371,10 @@
 								needle == null ? "null" : string.Format(
 									"{0}, needle.KerbinDirect={1}, needle.targetRelay={2}",
 									needle, needle.KerbinDirect, needle.targetRelay == null ? "null" : string.Format(
-										"{0}\n\tneedle.targetRelay.vessel={1}",
+										"{0}\n\tneedle.targetRelay.Host={1}",
 										needle.targetRelay,
-										needle.targetRelay.vessel == null ?
-											"null" : needle.targetRelay.vessel.vesselName
+										needle.targetRelay.Host == null ?
+											"null" : needle.targetRelay.Host.ToString()
 									)
 								),
 								this.moduleRef == null ? "null" : this.moduleRef.ToString()
@@ -400,8 +408,7 @@
 
 			CelestialBody bodyOccludingKerbin = null;
 
-			double kerbinSqrDistance = this.vessel.DistanceTo(Kerbin) - Kerbin.Radius;
-			kerbinSqrDistance *= kerbinSqrDistance;
+			double kerbinSqrDistance = this.sqrDistanceTo(Kerbin);
 
 			log.AppendFormat("\n{0} ({1}): Search done, figuring status.", this.ToString(), this.GetType().Name);
 			log.AppendFormat(
@@ -417,7 +424,7 @@
 			// If we don't have LOS to Kerbin, focus on relays
 			if (
 				ARConfiguration.RequireLineOfSight &&
-				!this.vessel.hasLineOfSightTo(Kerbin, out bodyOccludingKerbin, ARConfiguration.RadiusRatio)
+				!this.Host.hasLineOfSightTo((BodyWrapper)Kerbin, out bodyOccludingKerbin, ARConfiguration.RadiusRatio)
 			)
 			{
 				log.AppendFormat("\n\tKerbin LOS is blocked by {0}.", bodyOccludingKerbin.bodyName);

--- a/IAntennaRelay.cs
+++ b/IAntennaRelay.cs
@@ -39,7 +39,7 @@
 		/// <summary>
 		/// Gets the parent Vessel.
 		/// </summary>
-		Vessel vessel { get; }
+		IPositionedObject Host { get; }
 
 		/// <summary>
 		/// Gets the target <see cref="AntennaRange.IAntennaRelay"/>relay.

file:b/KerbinRelay.cs (new)
--- /dev/null
+++ b/KerbinRelay.cs
@@ -1,1 +1,241 @@
-
+// AntennaRange
+//
+// BodyRelay.cs
+//
+// Copyright © 2015, toadicus
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+//    this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation and/or other
+//    materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be used
+//    to endorse or promote products derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#pragma warning disable 1591
+#define DEBUG
+
+using KSP;
+using System;
+using System.Collections.Generic;
+using ToadicusTools;
+using UnityEngine;
+
+namespace AntennaRange
+{
+	[KSPAddon(KSPAddon.Startup.Flight, false)]
+	public class KerbinRelay : MonoBehaviour, IAntennaRelay
+	{
+		public static KerbinRelay Kerbin;
+		public static int TrackingStationMaxLevel = 2;
+
+		private List<double> stationLevelRanges;
+
+		public IList<double> StationLevelRange
+		{
+			get;
+			private set;
+		}
+
+		/// <summary>
+		/// Gets the parent Vessel.
+		/// </summary>
+		public BodyWrapper Host
+		{
+			get;
+			private set;
+		}
+		IPositionedObject IAntennaRelay.Host
+		{
+			get
+			{
+				return (IPositionedObject)this.Host;
+			}
+		}
+
+		/// <summary>
+		/// Gets the target <see cref="AntennaRange.IAntennaRelay"/>relay.
+		/// </summary>
+		public IAntennaRelay targetRelay
+		{
+			get
+			{
+				return null;
+			}
+		}
+
+		/// <summary>
+		/// Gets the distance to the nearest relay or Kerbin, whichever is closer.
+		/// </summary>
+		public double transmitDistance
+		{
+			get
+			{
+				return double.PositiveInfinity;
+			}
+		}
+
+		/// <summary>
+		/// Gets the nominal transmit distance at which the Antenna behaves just as prescribed by Squad's config.
+		/// </summary>
+		public double nominalTransmitDistance
+		{
+			get;
+			private set;
+		}
+
+		/// <summary>
+		/// The maximum distance at which this relay can operate.
+		/// </summary>
+		public double maxTransmitDistance
+		{
+			get;
+			private set;
+		}
+
+		/// <summary>
+		/// The first CelestialBody blocking line of sight to a 
+		/// </summary>
+		public CelestialBody firstOccludingBody
+		{
+			get
+			{
+				return null;
+			}
+		}
+
+		/// <summary>
+		/// Gets a value indicating whether this <see cref="AntennaRange.IAntennaRelay"/> Relay is communicating
+		/// directly with Kerbin.
+		/// </summary>
+		public bool KerbinDirect
+		{
+			get
+			{
+				return true;
+			}
+		}
+
+		/// <summary>
+		/// Gets the Part title.
+		/// </summary>
+		public string Title
+		{
+			get
+			{
+				if (this.Host != null)
+				{
+					return string.Format("Tracking Station on {0}", this.Host.HostObject.bodyName);
+				}
+				else
+				{
+					return "Tracking Station on an unknown world";
+				}
+			}
+		}
+
+		/// <summary>
+		/// Determines whether this instance can transmit.
+		/// <c>true</c> if this instance can transmit; otherwise, <c>false</c>.
+		/// </summary>
+		public bool CanTransmit()
+		{
+			return true;
+		}
+
+		/// <summary>
+		/// Finds the nearest relay.
+		/// </summary>
+		public void FindNearestRelay()
+		{
+			return;
+		}
+
+		/// <summary>
+		/// Returns a <see cref="System.String"/> that represents the current <see cref="AntennaRange.IAntennaRelay"/>.
+		/// </summary>
+		public override string ToString()
+		{
+			return this.Title;
+		}
+
+		private void Awake()
+		{
+			this.stationLevelRanges = new List<double>();
+			this.StationLevelRange = this.stationLevelRanges.AsReadOnly();
+
+			this.stationLevelRanges.Add(51696576d);
+			this.stationLevelRanges.Add(37152180000d);
+			this.stationLevelRanges.Add(224770770000d);
+
+			if (HighLogic.CurrentGame.Mode == Game.Modes.CAREER)
+			{
+				GameEvents.OnKSCFacilityUpgraded.Add(this.onFacilityUpgraded);
+			}
+
+		}
+
+		private void Update()
+		{
+			if (FlightGlobals.ready && Kerbin == null)
+			{
+				this.Host = (BodyWrapper)FlightGlobals.GetHomeBody();
+
+				if (HighLogic.CurrentGame.Mode == Game.Modes.CAREER)
+				{
+					this.maxTransmitDistance = this.stationLevelRanges[this.TrackingStationLevel()];
+				}
+				else
+				{
+					this.maxTransmitDistance = this.stationLevelRanges[2];
+				}
+
+				this.nominalTransmitDistance = this.maxTransmitDistance;
+
+				Kerbin = this;
+			}
+		}
+
+		private void OnDestroy()
+		{
+			GameEvents.OnKSCFacilityUpgraded.Remove(this.onFacilityUpgraded);
+		}
+
+		public int TrackingStationLevel()
+		{
+			this.LogDebug("Tracking station level: {0} ({1} * {2})",(int)(
+				ScenarioUpgradeableFacilities.GetFacilityLevel(SpaceCenterFacility.TrackingStation) *
+				(float)TrackingStationMaxLevel),
+				ScenarioUpgradeableFacilities.GetFacilityLevel(SpaceCenterFacility.TrackingStation),
+				TrackingStationMaxLevel
+			);
+
+			return (int)(
+				ScenarioUpgradeableFacilities.GetFacilityLevel(SpaceCenterFacility.TrackingStation) *
+				(float)TrackingStationMaxLevel);
+		}
+
+		private void onFacilityUpgraded(Upgradeables.UpgradeableFacility fac, int level)
+		{
+			// fac.FacilityLevel
+			this.maxTransmitDistance = this.stationLevelRanges[this.TrackingStationLevel()];
+			this.nominalTransmitDistance = this.maxTransmitDistance;
+		}
+	}
+}
+
+

--- a/ModuleLimitedDataTransmitter.cs
+++ b/ModuleLimitedDataTransmitter.cs
@@ -137,17 +137,17 @@
 		/// <summary>
 		/// Gets the parent Vessel.
 		/// </summary>
-		public new Vessel vessel
+		public VesselWrapper Host
 		{
 			get
 			{
 				if (base.vessel != null)
 				{
-					return base.vessel;
+					return (VesselWrapper)base.vessel;
 				}
 				else if (this.part != null && this.part.vessel != null)
 				{
-					return this.part.vessel;
+					return (VesselWrapper)this.part.vessel;
 				}
 				else if (
 					this.part.protoPartSnapshot != null &&
@@ -155,13 +155,20 @@
 					this.part.protoPartSnapshot.pVesselRef.vesselRef != null
 				)
 				{
-					return this.part.protoPartSnapshot.pVesselRef.vesselRef;
+					return (VesselWrapper)this.part.protoPartSnapshot.pVesselRef.vesselRef;
 				}
 				else
 				{
 					this.LogError("Vessel and/or part reference are null, returning null vessel.");
 					return null;
 				}
+			}
+		}
+		IPositionedObject IAntennaRelay.Host
+		{
+			get
+			{
+				return this.Host;
 			}
 		}
 
@@ -434,7 +441,7 @@
 						"{0}: {1} on {2} cannot transmit: {3}",
 						this.GetType().Name,
 						this.part.partInfo.title,
-						this.vessel.vesselName,
+						this.Host.HostObject.vesselName,
 						Enum.GetName(typeof(PartStates), this.part.State)
 					));
 					return false;
@@ -499,7 +506,7 @@
 
 				var logger = Tools.DebugLogger.New(this);
 
-				IList<ModuleScienceContainer> vesselContainers = this.vessel.getModulesOfType<ModuleScienceContainer>();
+				IList<ModuleScienceContainer> vesselContainers = this.Host.HostObject.getModulesOfType<ModuleScienceContainer>();
 				ModuleScienceContainer scienceContainer;
 				for (int cIdx = 0; cIdx < vesselContainers.Count; cIdx++)
 				{
@@ -658,7 +665,7 @@
 
 				if (this.KerbinDirect)
 				{
-					this.UIrelayTarget = AntennaRelay.Kerbin.bodyName;
+					this.UIrelayTarget = AntennaRelay.Kerbin.HostObject.bodyName;
 				}
 				else
 				{
@@ -678,10 +685,10 @@
 
 			sb.Append(this.part.partInfo.title);
 
-			if (vessel != null)
+			if (Host != null)
 			{
 				sb.Append(" on ");
-				sb.Append(vessel.vesselName);
+				sb.Append(Host.HostObject.vesselName);
 			}
 			else if (
 				this.part != null &&

--- /dev/null
+++ b/PositionedObject.cs
@@ -1,1 +1,158 @@
+// AntennaRange
+//
+// PositionedObject.cs
+//
+// Copyright © 2015, toadicus
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+//    this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation and/or other
+//    materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be used
+//    to endorse or promote products derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+using System;
+using System.Collections.Generic;
+
+#pragma warning disable 1591
+
+namespace AntennaRange
+{
+	public interface IPositionedObject
+	{
+		object HostObject { get; }
+		Vector3d WorldPos { get; }
+	}
+
+	public interface IPositionedObject<T>
+	{
+		T HostObject { get; }
+	}
+
+	public abstract class PositionedObject<T> : IPositionedObject<T>, IPositionedObject
+	{
+		public abstract T HostObject { get; protected set; }
+
+		object IPositionedObject.HostObject
+		{
+			get
+			{
+				return (object)this.HostObject;
+			}
+		}
+
+		public abstract Vector3d WorldPos { get; }
+
+
+		protected static Dictionary<T, PositionedObject<T>> bin = new Dictionary<T, PositionedObject<T>>(); 
+	}
+
+	public class VesselWrapper : PositionedObject<Vessel>
+	{
+		public override Vessel HostObject
+		{
+			get;
+			protected set;
+		}
+
+		public override Vector3d WorldPos
+		{
+			get
+			{
+				if (this.HostObject != null)
+				{
+					return this.HostObject.GetWorldPos3D();
+				}
+				else
+				{
+					return Vector3d.zero;
+				}
+			}
+		}
+
+		public VesselWrapper(Vessel host)
+		{
+			this.HostObject = host;
+		}
+
+		public static explicit operator VesselWrapper(Vessel vessel)
+		{
+			PositionedObject<Vessel> wrapper;
+			if (!bin.TryGetValue(vessel, out wrapper))
+			{
+				wrapper = new VesselWrapper(vessel);
+				bin[vessel] = wrapper;
+			}
+
+			return (VesselWrapper)wrapper;
+		}
+
+		public static implicit operator Vessel(VesselWrapper wrapper)
+		{
+			return wrapper.HostObject;
+		}
+	}
+
+	public class BodyWrapper : PositionedObject<CelestialBody>
+	{
+		public override CelestialBody HostObject
+		{
+			get;
+			protected set;
+		}
+
+		public override Vector3d WorldPos
+		{
+			get
+			{
+				if (this.HostObject != null)
+				{
+					return this.HostObject.position;
+				}
+				else
+				{
+					return Vector3d.zero;
+				}
+			}
+		}
+
+		public BodyWrapper(CelestialBody host)
+		{
+			this.HostObject = host;
+		}
+
+		public static explicit operator BodyWrapper(CelestialBody body)
+		{
+			PositionedObject<CelestialBody> wrapper;
+			if (!bin.TryGetValue(body, out wrapper))
+			{
+				wrapper = new BodyWrapper(body);
+				bin[body] = wrapper;
+			}
+
+			return (BodyWrapper)wrapper;
+		}
+
+		public static implicit operator CelestialBody(BodyWrapper wrapper)
+		{
+			return wrapper.HostObject;
+		}
+	}
+}
+
+

--- a/ProtoAntennaRelay.cs
+++ b/ProtoAntennaRelay.cs
@@ -44,7 +44,7 @@
 		/// <summary>
 		/// Gets the parent Vessel.
 		/// </summary>
-		public override Vessel vessel
+		public override IPositionedObject Host
 		{
 			get
 			{
@@ -54,7 +54,7 @@
 					this.protoPart.pVesselRef.vesselRef != null
 				)
 				{
-					return this.protoPart.pVesselRef.vesselRef;
+					return (VesselWrapper)this.protoPart.pVesselRef.vesselRef;
 				}
 				else
 				{
@@ -124,7 +124,7 @@
 					"{0}: {1} on {2} cannot transmit: {3}",
 					this.GetType().Name,
 					this.Title,
-					this.vessel.vesselName,
+					this.Host.ToString(),
 					Enum.GetName(typeof(PartStates), partState)
 				));
 				return false;

--- a/RelayExtensions.cs
+++ b/RelayExtensions.cs
@@ -38,63 +38,115 @@
 	public static class RelayExtensions
 	{
 		/// <summary>
-		/// Returns the distance between this IAntennaRelay and a Vessel
-		/// </summary>
-		/// <param name="relay">This <see cref="IAntennaRelay"/></param>
-		/// <param name="Vessel">A <see cref="Vessel"/></param>
-		public static double DistanceTo(this AntennaRelay relay, Vessel Vessel)
-		{
-			return relay.vessel.DistanceTo(Vessel);
-		}
-
-		/// <summary>
-		/// Returns the distance between this IAntennaRelay and a CelestialBody
-		/// </summary>
-		/// <param name="relay">This <see cref="IAntennaRelay"/></param>
-		/// <param name="body">A <see cref="CelestialBody"/></param>
-		public static double DistanceTo(this AntennaRelay relay, CelestialBody body)
-		{
-			return relay.vessel.DistanceTo(body) - body.Radius;
-		}
-
-		/// <summary>
-		/// Returns the distance between this IAntennaRelay and another IAntennaRelay
-		/// </summary>
-		/// <param name="relayOne">This <see cref="IAntennaRelay"/></param>
-		/// <param name="relayTwo">Another <see cref="IAntennaRelay"/></param>
-		public static double DistanceTo(this AntennaRelay relayOne, IAntennaRelay relayTwo)
-		{
-			return relayOne.DistanceTo(relayTwo.vessel);
-		}
-
-		/// <summary>
-		/// Returns the square of the distance between this IAntennaRelay and a Vessel
-		/// </summary>
-		/// <param name="relay">This <see cref="IAntennaRelay"/></param>
-		/// <param name="vessel">A <see cref="Vessel"/></param>
-		public static double sqrDistanceTo(this AntennaRelay relay, Vessel vessel)
-		{
-			return relay.vessel.sqrDistanceTo(vessel);
-		}
-
-		/// <summary>
-		/// Returns the square of the distance between this IAntennaRelay and a CelestialBody
-		/// </summary>
-		/// <param name="relay">This <see cref="IAntennaRelay"/></param>
-		/// <param name="body">A <see cref="CelestialBody"/></param>
-		public static double sqrDistanceTo(this AntennaRelay relay, CelestialBody body)
-		{
-			return relay.vessel.sqrDistanceTo(body);
-		}
-
-		/// <summary>
-		/// Returns the square of the distance between this IAntennaRelay and another IAntennaRelay
-		/// </summary>
-		/// <param name="relayOne">This <see cref="IAntennaRelay"/></param>
-		/// <param name="relayTwo">Another <see cref="IAntennaRelay"/></param>
-		public static double sqrDistanceTo(this AntennaRelay relayOne, IAntennaRelay relayTwo)
-		{
-			return relayOne.vessel.sqrDistanceTo(relayTwo.vessel);
+		/// Returns the world distance between two <see cref="AntennaRange.IPositionedObject"/> objects.
+		/// </summary>
+		public static double DistanceTo(this IPositionedObject object1, IPositionedObject object2)
+		{
+			double dist = (object1.WorldPos - object2.WorldPos).magnitude;
+
+			if (object1 is BodyWrapper)
+			{
+				dist -= ((BodyWrapper)object1).HostObject.Radius;
+			}
+
+			if (object2 is BodyWrapper)
+			{
+				dist -= ((BodyWrapper)object2).HostObject.Radius;
+			}
+
+			return dist;
+		}
+
+		/// <summary>
+		/// Returns the world distance between an <see cref="AntennaRange.IAntennaRelay"/> and
+		/// an <see cref="AntennaRange.IPositionedObject"/>.
+		/// </summary>
+		public static double DistanceTo(this IAntennaRelay relay, IPositionedObject obj)
+		{
+			return relay.Host.DistanceTo(obj);
+		}
+
+		/// <summary>
+		/// Returns the world distance between two <see cref="AntennaRange.IAntennaRelay"/> objects.
+		/// </summary>
+		public static double DistanceTo(this IAntennaRelay relay1, IAntennaRelay relay2)
+		{
+			return relay1.Host.DistanceTo(relay2.Host);
+		}
+
+		/// <summary>
+		/// Returns the world distance between an <see cref="AntennaRange.IAntennaRelay"/> and a <see cref="Vessel"/>.
+		/// </summary>
+		public static double DistanceTo(this IAntennaRelay relay, Vessel vessel)
+		{
+			return relay.Host.DistanceTo((VesselWrapper)vessel);
+		}
+
+		/// <summary>
+		/// Returns the square of the world distance between two <see cref="AntennaRange.IPositionedObject"/> objects.
+		/// </summary>
+		public static double sqrDistanceTo(this IPositionedObject object1, IPositionedObject object2)
+		{
+			if (object1 is BodyWrapper || object2 is BodyWrapper)
+			{
+				double dist = object1.DistanceTo(object2);
+				dist *= dist;
+				return dist;
+			}
+			else
+			{
+				return (object1.WorldPos - object2.WorldPos).sqrMagnitude;
+			}
+		}
+
+		/// <summary>
+		/// Returns the square of the world distance between an <see cref="AntennaRange.IAntennaRelay"/>
+		/// and a <see cref="Vessel"/>.
+		/// </summary>
+		public static double sqrDistanceTo(this IAntennaRelay relay, Vessel vessel)
+		{
+			return relay.Host.sqrDistanceTo((VesselWrapper)vessel);
+		}
+
+		/// <summary>
+		/// Returns the square of the world distance between an <see cref="AntennaRange.IAntennaRelay"/>
+		/// and a <see cref="CelestialBody"/>.
+		/// </summary>
+		public static double sqrDistanceTo(this IAntennaRelay relay, CelestialBody body)
+		{
+			return relay.Host.sqrDistanceTo((BodyWrapper)body);
+		}
+
+		/// <summary>
+		/// Returns <c>true</c> if the origin <see cref="AntennaRange.IPositionedObject"/> has line of sight to the
+		/// target <see cref="AntennaRange.IPositionedObject"/>, <c>false</c> otherwise.  If not, firstOccludingBody
+		/// outputs the first <see cref="CelestialBody"/> blocking line of sight.
+		/// </summary>
+		public static bool hasLineOfSightTo(
+			this IPositionedObject origin,
+			IPositionedObject target,
+			out CelestialBody firstOccludingBody,
+			double sqrRatio = 1d
+		)
+		{
+			CelestialBody[] excludedBodies;
+
+			if (target is BodyWrapper)
+			{
+				excludedBodies = new CelestialBody[] { (target as BodyWrapper).HostObject };
+			}
+			else
+			{
+				excludedBodies = null;
+			}
+
+			return VectorTools.IsLineOfSightBetween(
+				origin.WorldPos,
+				target.WorldPos,
+				out firstOccludingBody,
+				excludedBodies,
+				sqrRatio
+			);
 		}
 
 		/// <summary>