Added assembly dependency on ToadicusTools.
Added assembly dependency on ToadicusTools.

--- /dev/null
+++ b/ARConfiguration.cs
@@ -1,1 +1,127 @@
+// AntennaRange © 2014 toadicus
+//
+// 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/
 
+using KSP;
+using System;
+using ToadicusTools;
+using UnityEngine;
+
+[assembly: KSPAssemblyDependency("ToadicusTools", 0, 0)]
+
+namespace AntennaRange
+{
+	[KSPAddon(KSPAddon.Startup.SpaceCentre, false)]
+	public class ARConfiguration : MonoBehaviour
+	{
+		private bool showConfigWindow;
+		private Rect configWindowPos;
+
+		private IButton toolbarButton;
+
+		public void Awake()
+		{
+			Tools.PostDebugMessage(this, "Waking up.");
+
+			this.showConfigWindow = false;
+			this.configWindowPos = new Rect(Screen.width / 4, Screen.height / 2, 180, 15);
+
+			Tools.PostDebugMessage(this, "Awake.");
+		}
+
+		public void OnGUI()
+		{
+			if (this.toolbarButton == null && ToolbarManager.ToolbarAvailable)
+			{
+				Tools.PostDebugMessage(this, "Toolbar available; initializing button.");
+
+				this.toolbarButton = ToolbarManager.Instance.add("AntennaRange", "ARConfiguration");
+				this.toolbarButton.Visibility = new GameScenesVisibility(GameScenes.SPACECENTER);
+				this.toolbarButton.Text = "AR";
+				this.toolbarButton.TexturePath = "AntennaRange/Textures/toolbarIcon";
+				this.toolbarButton.TextColor = (Color)XKCDColors.Amethyst;
+				this.toolbarButton.OnClick += delegate(ClickEvent e)
+				{
+					this.showConfigWindow = !this.showConfigWindow;
+				};
+
+				var config = KSP.IO.PluginConfiguration.CreateForType<AntennaRelay>();
+
+				config.load();
+
+				this.configWindowPos = config.GetValue<Rect>("configWindowPos", this.configWindowPos);
+				AntennaRelay.requireLineOfSight = config.GetValue<bool>("requireLineOfSight", false);
+
+				config.save();
+			}
+
+			if (this.showConfigWindow)
+			{
+				Rect configPos = GUILayout.Window(354163056,
+					this.configWindowPos,
+					this.ConfigWindow,
+					"AntennaRange Configuration",
+					GUILayout.ExpandHeight(true),
+					GUILayout.ExpandWidth(true)
+				);
+
+				configPos = Tools.ClampRectToScreen(configPos, 20);
+
+				if (configPos != this.configWindowPos)
+				{
+					this.configWindowPos = configPos;
+					this.SaveConfigValue("configWindowPos", this.configWindowPos);
+				}
+			}
+		}
+
+		public void ConfigWindow(int _)
+		{
+			GUILayout.BeginVertical(GUILayout.ExpandHeight(true));
+
+			GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
+			bool requireLineOfSight = GUILayout.Toggle(AntennaRelay.requireLineOfSight, "Require Line of Sight");
+			if (requireLineOfSight != AntennaRelay.requireLineOfSight)
+			{
+				AntennaRelay.requireLineOfSight = requireLineOfSight;
+				this.SaveConfigValue("requireLineOfSight", requireLineOfSight);
+			}
+
+			GUILayout.EndHorizontal();
+
+			GUILayout.EndVertical();
+
+			GUI.DragWindow();
+		}
+
+		public void Destroy()
+		{
+			if (this.toolbarButton != null)
+			{
+				this.toolbarButton.Destroy();
+			}
+		}
+
+		private T LoadConfigValue<T>(string key, T defaultValue)
+		{
+			var config = KSP.IO.PluginConfiguration.CreateForType<AntennaRelay>();
+
+			config.load();
+
+			return config.GetValue(key, defaultValue);
+		}
+
+		private void SaveConfigValue<T>(string key, T value)
+		{
+			var config = KSP.IO.PluginConfiguration.CreateForType<AntennaRelay>();
+
+			config.load();
+
+			config.SetValue(key, value);
+
+			config.save();
+		}
+	}
+}
+

--- a/AntennaRange.cfg
+++ b/AntennaRange.cfg
@@ -37,7 +37,7 @@
 // maxDataFactor:	The multipler on packetSize that defines the maximum data bandwidth of the antenna.
 // 
 
-@PART[longAntenna]
+@PART[longAntenna]:FOR[AntennaRange]:NEEDS[!RemoteTech2]
 {
 	@MODULE[ModuleDataTransmitter]
 	{
@@ -48,7 +48,7 @@
 	}
 }
 
-@PART[mediumDishAntenna]
+@PART[mediumDishAntenna]:FOR[AntennaRange]:NEEDS[!RemoteTech2]
 {
 	@MODULE[ModuleDataTransmitter]
 	{
@@ -59,7 +59,7 @@
 	}
 }
 
-@PART[commDish]
+@PART[commDish]:FOR[AntennaRange]:NEEDS[!RemoteTech2]
 {
 	@MODULE[ModuleDataTransmitter]
 	{

--- a/AntennaRange.csproj
+++ b/AntennaRange.csproj
@@ -3,7 +3,7 @@
   <PropertyGroup>
     <Configuration Condition=" '$(Configuration)' == '' ">Debug_win</Configuration>
     <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
-    <ProductVersion>10.0.0</ProductVersion>
+    <ProductVersion>8.0.30703</ProductVersion>
     <SchemaVersion>2.0</SchemaVersion>
     <ProjectGuid>{B36F2C11-962E-4A75-9F41-61AD56D11493}</ProjectGuid>
     <OutputType>Library</OutputType>
@@ -43,17 +43,33 @@
       </CustomCommands>
     </CustomCommands>
   </PropertyGroup>
-  <ItemGroup>
-    <Reference Include="System">
-      <HintPath>..\..\..\Games\KSP_win\KSP_Data\Managed\System.dll</HintPath>
-    </Reference>
-    <Reference Include="Assembly-CSharp">
-      <HintPath>..\..\..\Games\KSP_win\KSP_Data\Managed\Assembly-CSharp.dll</HintPath>
-    </Reference>
-    <Reference Include="UnityEngine">
-      <HintPath>..\..\..\Games\KSP_win\KSP_Data\Managed\UnityEngine.dll</HintPath>
-    </Reference>
-  </ItemGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug_linux|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug</OutputPath>
+    <DefineConstants>DEBUG;TRACE;</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <ConsolePause>false</ConsolePause>
+    <CustomCommands>
+      <CustomCommands>
+        <Command type="AfterBuild" command="cp -afv ${TargetFile} ${ProjectDir}/${ProjectName}.cfg /opt/games/KSP_linux/GameData/${ProjectName}/" />
+      </CustomCommands>
+    </CustomCommands>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release_linux|AnyCPU' ">
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release</OutputPath>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <CustomCommands>
+      <CustomCommands>
+        <Command type="AfterBuild" command="cp -afv ${TargetFile} ${ProjectDir}/${ProjectName}.cfg /opt/games/KSP_linux/GameData/${ProjectName}/" />
+      </CustomCommands>
+    </CustomCommands>
+    <ConsolePause>false</ConsolePause>
+  </PropertyGroup>
   <ItemGroup>
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="IAntennaRelay.cs" />
@@ -80,12 +96,35 @@
     <Compile Include="..\ToadicusTools\IModuleDB.cs">
       <Link>ToadicusTools\IModuleDB.cs</Link>
     </Compile>
+    <Compile Include="ARConfiguration.cs" />
+    <Compile Include="..\ToadicusTools\Wrapper\ToolbarWrapper.cs">
+      <Link>ToadicusTools\ToolbarWrapper.cs</Link>
+    </Compile>
+    <Compile Include="..\ToadicusTools\WindowTools.cs">
+      <Link>WindowTools.cs</Link>
+    </Compile>
   </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
   <ItemGroup>
-    <None Include="AntennaRange.cfg" />
+    <None Include="AntennaRange.cfg">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
   </ItemGroup>
   <ItemGroup>
     <Folder Include="ToadicusTools\" />
   </ItemGroup>
+  <ItemGroup>
+    <Reference Include="Assembly-CSharp">
+      <HintPath>..\_KSPAssemblies\Assembly-CSharp.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="System">
+      <HintPath>..\_KSPAssemblies\System.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="UnityEngine">
+      <HintPath>..\_KSPAssemblies\UnityEngine.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+  </ItemGroup>
 </Project>

--- a/AntennaRelay.cs
+++ b/AntennaRelay.cs
@@ -35,6 +35,8 @@
 {
 	public class AntennaRelay
 	{
+		public static bool requireLineOfSight;
+
 		// We don't have a Bard, so we'll hide Kerbin here.
 		protected CelestialBody Kerbin;
 
@@ -130,7 +132,10 @@
 		/// <returns><c>true</c> if this instance can transmit; otherwise, <c>false</c>.</returns>
 		public virtual bool CanTransmit()
 		{
-			if (this.transmitDistance > this.maxTransmitDistance)
+			if (
+				this.transmitDistance > this.maxTransmitDistance ||
+				(requireLineOfSight && this.nearestRelay == null && !this.vessel.hasLineOfSightTo(this.Kerbin))
+			)
 			{
 				return false;
 			}
@@ -169,7 +174,7 @@
 			// Set this vessel as checked, so that we don't check it again.
 			RelayDatabase.Instance.CheckedVesselsTable[vessel.id] = true;
 
-			double nearestDistance = double.PositiveInfinity;
+			double nearestSqrDistance = double.PositiveInfinity;
 			IAntennaRelay _nearestRelay = null;
 
 			/*
@@ -181,14 +186,10 @@
 			foreach (Vessel potentialVessel in FlightGlobals.Vessels)
 			{
 				// Skip vessels that have already been checked for a nearest relay this pass.
-				try
-				{
-					if (RelayDatabase.Instance.CheckedVesselsTable[potentialVessel.id])
-					{
-						continue;
-					}
-				}
-				catch (KeyNotFoundException) { /* If the key doesn't exist, don't skip it. */}
+				if (RelayDatabase.Instance.CheckedVesselsTable.ContainsKey(potentialVessel.id))
+				{
+					continue;
+				}
 
 				// Skip vessels of the wrong type.
 				switch (potentialVessel.vesselType)
@@ -209,19 +210,41 @@
 					continue;
 				}
 
+				// Skip vessels to which we do not have line of sight.
+				if (requireLineOfSight && !this.vessel.hasLineOfSightTo(potentialVessel))
+				{
+					Tools.PostDebugMessage(
+						this,
+						"Vessel {0} discarded because we do not have line of sight.",
+						potentialVessel.vesselName
+					);
+					continue;
+				}
+
 				// Find the distance from here to the vessel...
-				double potentialDistance = (potentialVessel.GetWorldPos3D() - vessel.GetWorldPos3D()).magnitude;
+				double potentialSqrDistance = (potentialVessel.GetWorldPos3D() - vessel.GetWorldPos3D()).sqrMagnitude;
 
 				/*
 				 * ...so that we can skip the vessel if it is further away than Kerbin, our transmit distance, or a
 				 * vessel we've already checked.
 				 * */
-				if (potentialDistance > Tools.Min(this.maxTransmitDistance, nearestDistance, vessel.DistanceTo(Kerbin)))
-				{
+				if (
+					potentialSqrDistance > Tools.Min(
+						this.maxTransmitDistance * this.maxTransmitDistance,
+						nearestSqrDistance,
+						this.vessel.sqrDistanceTo(Kerbin)
+					)
+				)
+				{
+					Tools.PostDebugMessage(
+						this,
+						"Vessel {0} discarded because it is out of range, or farther than another relay.",
+						potentialVessel.vesselName
+					);
 					continue;
 				}
 
-				nearestDistance = potentialDistance;
+				nearestSqrDistance = potentialSqrDistance;
 
 				foreach (IAntennaRelay potentialRelay in potentialVessel.GetAntennaRelays())
 				{
@@ -261,6 +284,17 @@
 			// we hope it is safe enough.
 			this.Kerbin = FlightGlobals.Bodies.FirstOrDefault(b => b.name == "Kerbin");
 		}
+
+		static AntennaRelay()
+		{
+			var config = KSP.IO.PluginConfiguration.CreateForType<AntennaRelay>();
+
+			config.load();
+
+			AntennaRelay.requireLineOfSight = config.GetValue<bool>("requireLineOfSight", false);
+
+			config.save();
+		}
 	}
 }
 

--- a/ModuleLimitedDataTransmitter.cs
+++ b/ModuleLimitedDataTransmitter.cs
@@ -94,6 +94,16 @@
 		// The multipler on packetSize that defines the maximum data bandwidth of the antenna.
 		[KSPField(isPersistant = false)]
 		public float maxDataFactor;
+
+		[KSPField(
+			isPersistant = true,
+			guiName = "Packet Throttle",
+			guiUnits = "%",
+			guiActive = true,
+			guiActiveEditor = false
+		)]
+		[UI_FloatRange(maxValue = 100f, minValue = 2.5f, stepIncrement = 2.5f)]
+		public float packetThrottle;
 
 		protected bool actionUIUpdate;
 
@@ -207,6 +217,7 @@
 		public ModuleLimitedDataTransmitter () : base()
 		{
 			this.ErrorMsg = new ScreenMessage("", 4f, false, ScreenMessageStyle.UPPER_LEFT);
+			this.packetThrottle = 100f;
 		}
 
 		// At least once, when the module starts with a state on the launch pad or later, go find Kerbin.
@@ -259,11 +270,7 @@
 		// is only one reason for this.
 		protected void PostCannotTransmitError()
 		{
-			string ErrorText = string.Format (
-				"Unable to transmit: out of range!  Maximum range = {0}m; Current range = {1}m.",
-				Tools.MuMech_ToSI((double)this.maxTransmitDistance, 2),
-				Tools.MuMech_ToSI((double)this.transmitDistance, 2)
-				);
+			string ErrorText = string.Intern("Unable to transmit: no visible receivers in range!");
 
 			this.ErrorMsg.message = string.Format(
 				"<color='#{0}{1}{2}{3}'><b>{4}</b></color>",
@@ -309,6 +316,8 @@
 					this._basepacketSize * (float)Math.Pow (this.nominalRange / this.transmitDistance, 2),
 					this._basepacketSize * this.maxDataFactor);
 			}
+
+			base.packetSize *= this.packetThrottle / 100f;
 		}
 
 		// Override ModuleDataTransmitter.GetInfo to add nominal and maximum range to the VAB description.