ARConfiguration: Added toggle for ModuleLimitedDataTransmitter.fixedPowerCost.
--- /dev/null
+++ b/ARConfiguration.cs
@@ -1,1 +1,168 @@
+// 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;
+
+ private System.Version runningVersion;
+
+ private KSP.IO.PluginConfiguration _config;
+ private KSP.IO.PluginConfiguration config
+ {
+ get
+ {
+ if (this._config == null)
+ {
+ this._config = KSP.IO.PluginConfiguration.CreateForType<AntennaRelay>();
+ }
+
+ return this._config;
+ }
+ }
+
+ 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()
+ {
+ // Only runs once, if the Toolbar is available.
+ if (this.toolbarButton == null && ToolbarManager.ToolbarAvailable)
+ {
+ this.runningVersion = this.GetType().Assembly.GetName().Version;
+
+ 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;
+ };
+
+ this.configWindowPos = this.LoadConfigValue("configWindowPos", this.configWindowPos);
+ AntennaRelay.requireLineOfSight = this.LoadConfigValue("requireLineOfSight", false);
+ ARFlightController.requireConnectionForControl =
+ this.LoadConfigValue("requireConnectionForControl", false);
+ ModuleLimitedDataTransmitter.fixedPowerCost = this.LoadConfigValue("fixedPowerCost", false);
+
+ Debug.Log(string.Format("{0} v{1} - ARonfiguration loaded!", this.GetType().Name, this.runningVersion));
+ }
+
+ if (this.showConfigWindow)
+ {
+ Rect configPos = GUILayout.Window(354163056,
+ this.configWindowPos,
+ this.ConfigWindow,
+ string.Format("AntennaRange {0}.{1}", this.runningVersion.Major, this.runningVersion.Minor),
+ 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.BeginHorizontal(GUILayout.ExpandWidth(true));
+
+ bool requireConnectionForControl =
+ GUILayout.Toggle(
+ ARFlightController.requireConnectionForControl,
+ "Require Connection for Probe Control"
+ );
+ if (requireConnectionForControl != ARFlightController.requireConnectionForControl)
+ {
+ ARFlightController.requireConnectionForControl = requireConnectionForControl;
+ this.SaveConfigValue("requireConnectionForControl", requireConnectionForControl);
+ }
+
+ GUILayout.EndHorizontal();
+
+ GUILayout.BeginHorizontal();
+
+ bool fixedPowerCost = GUILayout.Toggle(ModuleLimitedDataTransmitter.fixedPowerCost, "Use fixed power cost");
+ if (fixedPowerCost != ModuleLimitedDataTransmitter.fixedPowerCost)
+ {
+ ModuleLimitedDataTransmitter.fixedPowerCost = fixedPowerCost;
+ this.SaveConfigValue("fixedPowerCost", fixedPowerCost);
+ }
+
+ GUILayout.EndHorizontal();
+
+ GUILayout.EndVertical();
+
+ GUI.DragWindow();
+ }
+
+ public void Destroy()
+ {
+ if (this.toolbarButton != null)
+ {
+ this.toolbarButton.Destroy();
+ }
+ }
+
+ private T LoadConfigValue<T>(string key, T defaultValue)
+ {
+ this.config.load();
+
+ return config.GetValue(key, defaultValue);
+ }
+
+ private void SaveConfigValue<T>(string key, T value)
+ {
+ this.config.load();
+
+ this.config.SetValue(key, value);
+
+ this.config.save();
+ }
+ }
+}
+
--- /dev/null
+++ b/ARFlightController.cs
@@ -1,1 +1,147 @@
+// AntennaRange
+//
+// ARFlightController.cs
+//
+// Copyright © 2014, 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 KSP;
+using System;
+using ToadicusTools;
+using UnityEngine;
+
+namespace AntennaRange
+{
+ [KSPAddon(KSPAddon.Startup.Flight, false)]
+ public class ARFlightController : MonoBehaviour
+ {
+ #region Static Members
+ public static bool requireConnectionForControl;
+ #endregion
+
+ #region Properties
+ public ControlTypes currentControlLock
+ {
+ get
+ {
+ if (this.lockID == string.Empty)
+ {
+ return ControlTypes.None;
+ }
+
+ return InputLockManager.GetControlLock(this.lockID);
+ }
+ }
+
+ public string lockID
+ {
+ get;
+ protected set;
+ }
+
+ public ControlTypes lockSet
+ {
+ get
+ {
+ return ControlTypes.ALL_SHIP_CONTROLS;
+ }
+ }
+
+ public Vessel vessel
+ {
+ get
+ {
+ if (FlightGlobals.ready && FlightGlobals.ActiveVessel != null)
+ {
+ return FlightGlobals.ActiveVessel;
+ }
+
+ return null;
+ }
+ }
+ #endregion
+
+ #region MonoBehaviour LifeCycle
+ protected void Awake()
+ {
+ this.lockID = "ARConnectionRequired";
+
+ GameEvents.onGameSceneLoadRequested.Add(this.onSceneChangeRequested);
+ GameEvents.onVesselChange.Add(this.onVesselChange);
+ }
+
+ protected void FixedUpdate()
+ {
+ // If we are requiring a connection for control, the vessel does not have any adequately staffed pods,
+ // and the vessel does not have any connected relays...
+ if (
+ HighLogic.LoadedSceneIsFlight &&
+ requireConnectionForControl &&
+ this.vessel != null &&
+ this.vessel.vesselType != VesselType.EVA &&
+ !this.vessel.hasCrewCommand() &&
+ !this.vessel.HasConnectedRelay())
+ {
+ // ...and if the controls are not currently locked...
+ if (currentControlLock == ControlTypes.None)
+ {
+ // ...lock the controls.
+ InputLockManager.SetControlLock(this.lockSet, this.lockID);
+ }
+ }
+ // ...otherwise, if the controls are locked...
+ else if (currentControlLock != ControlTypes.None)
+ {
+ // ...unlock the controls.
+ InputLockManager.RemoveControlLock(this.lockID);
+ }
+ }
+
+ protected void Destroy()
+ {
+ InputLockManager.RemoveControlLock(this.lockID);
+
+ GameEvents.onGameSceneLoadRequested.Remove(this.onSceneChangeRequested);
+ GameEvents.onVesselChange.Remove(this.onVesselChange);
+ }
+ #endregion
+
+ #region Event Handlers
+ protected void onSceneChangeRequested(GameScenes scene)
+ {
+ if (scene != GameScenes.FLIGHT)
+ {
+ InputLockManager.RemoveControlLock(this.lockID);
+ }
+ }
+
+ protected void onVesselChange(Vessel vessel)
+ {
+ InputLockManager.RemoveControlLock(this.lockID);
+ }
+ #endregion
+ }
+}
+
+
--- a/ARTools.cs
+++ /dev/null
@@ -1,130 +1,1 @@
-// 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;
-
-namespace AntennaRange
-{
- public static class Tools
- {
- private static ScreenMessage debugmsg = new ScreenMessage("", 2f, ScreenMessageStyle.UPPER_RIGHT);
-
- [System.Diagnostics.Conditional("DEBUG")]
- public static void PostDebugMessage(string Msg)
- {
- if (HighLogic.LoadedScene > GameScenes.SPACECENTER)
- {
- debugmsg.message = Msg;
- ScreenMessages.PostScreenMessage(debugmsg, true);
- }
-
- KSPLog.print(Msg);
- }
-
- /*
- * MuMech_ToSI is a part of the MuMechLib library, © 2013 r4m0n, used under the GNU GPL version 3.
- * */
- public static string MuMech_ToSI(double d, int digits = 3, int MinMagnitude = 0, int MaxMagnitude = int.MaxValue)
- {
- float exponent = (float)Math.Log10(Math.Abs(d));
- exponent = UnityEngine.Mathf.Clamp(exponent, (float)MinMagnitude, (float)MaxMagnitude);
-
- if (exponent >= 0)
- {
- switch ((int)Math.Floor(exponent))
- {
- case 0:
- case 1:
- case 2:
- return d.ToString("F" + digits);
- case 3:
- case 4:
- case 5:
- return (d / 1e3).ToString("F" + digits) + "k";
- case 6:
- case 7:
- case 8:
- return (d / 1e6).ToString("F" + digits) + "M";
- case 9:
- case 10:
- case 11:
- return (d / 1e9).ToString("F" + digits) + "G";
- case 12:
- case 13:
- case 14:
- return (d / 1e12).ToString("F" + digits) + "T";
- case 15:
- case 16:
- case 17:
- return (d / 1e15).ToString("F" + digits) + "P";
- case 18:
- case 19:
- case 20:
- return (d / 1e18).ToString("F" + digits) + "E";
- case 21:
- case 22:
- case 23:
- return (d / 1e21).ToString("F" + digits) + "Z";
- default:
- return (d / 1e24).ToString("F" + digits) + "Y";
- }
- }
- else if (exponent < 0)
- {
- switch ((int)Math.Floor(exponent))
- {
- case -1:
- case -2:
- case -3:
- return (d * 1e3).ToString("F" + digits) + "m";
- case -4:
- case -5:
- case -6:
- return (d * 1e6).ToString("F" + digits) + "μ";
- case -7:
- case -8:
- case -9:
- return (d * 1e9).ToString("F" + digits) + "n";
- case -10:
- case -11:
- case -12:
- return (d * 1e12).ToString("F" + digits) + "p";
- case -13:
- case -14:
- case -15:
- return (d * 1e15).ToString("F" + digits) + "f";
- case -16:
- case -17:
- case -18:
- return (d * 1e18).ToString("F" + digits) + "a";
- case -19:
- case -20:
- case -21:
- return (d * 1e21).ToString("F" + digits) + "z";
- default:
- return (d * 1e24).ToString("F" + digits) + "y";
- }
- }
- else
- {
- return "0";
- }
- }
- }
-}
-
-
--- a/AntennaRange.cfg
+++ b/AntennaRange.cfg
@@ -1,50 +1,72 @@
-//
-// AntennaRange © 2013 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/
-//
-// This software uses the ModuleManager library © 2013 ialdabaoth, used under a Creative Commons Attribution-ShareAlike
-// 3.0 Uported License.
-//
-// Specifications:
-// nominalRange: The distance from Kerbin at which the antenna will perform exactly as prescribed by packetResourceCost
-// and packetSize.
-// maxPowerFactor: The multiplier on packetResourceCost that defines the maximum power output of the antenna. When the
-// power cost exceeds packetResourceCost * maxPowerFactor, transmission will fail.
-// maxDataFactor: The multipler on packetSize that defines the maximum data bandwidth of the antenna.
-//
-
-@PART[longAntenna]
-{
- @MODULE[ModuleDataTransmitter]
- {
- @name = ModuleLimitedDataTransmitter
- nominalRange = 1500000
- maxPowerFactor = 8
- maxDataFactor = 4
- }
-}
-
-@PART[mediumDishAntenna]
-{
- @MODULE[ModuleDataTransmitter]
- {
- @name = ModuleLimitedDataTransmitter
- nominalRange = 30000000
- maxPowerFactor = 8
- maxDataFactor = 4
- }
-}
-
-@PART[commDish]
-{
- @MODULE[ModuleDataTransmitter]
- {
- @name = ModuleLimitedDataTransmitter
- nominalRange = 80000000000
- maxPowerFactor = 8
- maxDataFactor = 4
- }
-}
+// AntennaRange
+//
+// AntennaRange.cfg
+//
+// Copyright © 2014, 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.
+//
+// This software uses the ModuleManager library © 2013 ialdabaoth, used under a Creative Commons Attribution-ShareAlike
+// 3.0 Uported License.
+//
+// Specifications:
+// nominalRange: The distance from Kerbin at which the antenna will perform exactly as prescribed by
+// packetResourceCost and packetSize.
+// maxPowerFactor: The multiplier on packetResourceCost that defines the maximum power output of the antenna. When the
+// power cost exceeds packetResourceCost * maxPowerFactor, transmission will fail.
+// maxDataFactor: The multipler on packetSize that defines the maximum data bandwidth of the antenna.
+//
+@PART[longAntenna]:FOR[AntennaRange]:NEEDS[!RemoteTech2]
+{
+ @MODULE[ModuleDataTransmitter]
+ {
+ @name = ModuleLimitedDataTransmitter
+ nominalRange = 1500000
+ maxPowerFactor = 8
+ maxDataFactor = 4
+ }
+}
+
+@PART[mediumDishAntenna]:FOR[AntennaRange]:NEEDS[!RemoteTech2]
+{
+ @MODULE[ModuleDataTransmitter]
+ {
+ @name = ModuleLimitedDataTransmitter
+ nominalRange = 30000000
+ maxPowerFactor = 8
+ maxDataFactor = 4
+ }
+}
+
+@PART[commDish]:FOR[AntennaRange]:NEEDS[!RemoteTech2]
+{
+ @MODULE[ModuleDataTransmitter]
+ {
+ @name = ModuleLimitedDataTransmitter
+ nominalRange = 80000000000
+ maxPowerFactor = 8
+ maxDataFactor = 4
+ }
+}
+
--- /dev/null
+++ b/AntennaRange.csproj
@@ -1,1 +1,110 @@
-
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug_win</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>8.0.30703</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{B36F2C11-962E-4A75-9F41-61AD56D11493}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <RootNamespace>AntennaRange</RootNamespace>
+ <AssemblyName>AntennaRange</AssemblyName>
+ <ReleaseVersion>1.1</ReleaseVersion>
+ <SynchReleaseVersion>false</SynchReleaseVersion>
+ <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+ <UseMSBuildEngine>False</UseMSBuildEngine>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug_win|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="xcopy /y ${ProjectDir}\AntennaRange.cfg C:\Users\andy\Games\KSP_win\GameData\AntennaRange\" />
+ <Command type="AfterBuild" command="xcopy /y ${TargetFile} C:\Users\andy\Games\KSP_win\GameData\AntennaRange\" />
+ </CustomCommands>
+ </CustomCommands>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release_win|AnyCPU' ">
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release</OutputPath>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <ConsolePause>false</ConsolePause>
+ <CustomCommands>
+ <CustomCommands>
+ <Command type="AfterBuild" command="xcopy /y ${ProjectDir}\AntennaRange.cfg C:\Users\andy\Games\KSP_win\GameData\AntennaRange\" />
+ <Command type="AfterBuild" command="xcopy /y ${TargetFile} C:\Users\andy\Games\KSP_win\GameData\AntennaRange\" />
+ </CustomCommands>
+ </CustomCommands>
+ </PropertyGroup>
+ <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" />
+ <Compile Include="ModuleLimitedDataTransmitter.cs" />
+ <Compile Include="AntennaRelay.cs" />
+ <Compile Include="ProtoAntennaRelay.cs" />
+ <Compile Include="RelayDatabase.cs" />
+ <Compile Include="RelayExtensions.cs" />
+ <Compile Include="ARConfiguration.cs" />
+ <Compile Include="ARFlightController.cs" />
+ </ItemGroup>
+ <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+ <ItemGroup>
+ <None Include="AntennaRange.cfg">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </None>
+ </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>
+ <ItemGroup>
+ <ProjectReference Include="..\ToadicusTools\ToadicusTools.csproj">
+ <Project>{D48A5542-6655-4149-BC27-B27DF0466F1C}</Project>
+ <Name>ToadicusTools</Name>
+ </ProjectReference>
+ </ItemGroup>
+</Project>
--- a/AntennaRelay.cs
+++ b/AntennaRelay.cs
@@ -1,103 +1,159 @@
-// 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.
+// AntennaRange
+//
+// AntennaRelay.cs
+//
+// Copyright © 2014, 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;
using System.Linq;
+using ToadicusTools;
namespace AntennaRange
{
- public class AntennaRelay : IAntennaRelay
+ public class AntennaRelay
{
+ public static bool requireLineOfSight;
+
// We don't have a Bard, so we'll hide Kerbin here.
protected CelestialBody Kerbin;
+ protected CelestialBody _firstOccludingBody;
+
+ protected IAntennaRelay _nearestRelayCache;
+ protected IAntennaRelay moduleRef;
+
+ protected System.Diagnostics.Stopwatch searchTimer;
+ protected long millisecondsBetweenSearches;
+
/// <summary>
/// Gets the parent Vessel.
/// </summary>
/// <value>The parent Vessel.</value>
- public Vessel vessel
+ public virtual Vessel vessel
+ {
+ get
+ {
+ return this.moduleRef.vessel;
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the nearest relay.
+ /// </summary>
+ /// <value>The nearest relay</value>
+ public IAntennaRelay nearestRelay
+ {
+ get
+ {
+ if (this.searchTimer.IsRunning &&
+ this.searchTimer.ElapsedMilliseconds > this.millisecondsBetweenSearches)
+ {
+ this._nearestRelayCache = this.FindNearestRelay();
+ this.searchTimer.Restart();
+ }
+
+ return this._nearestRelayCache;
+ }
+ protected set
+ {
+ this._nearestRelayCache = value;
+ }
+ }
+
+ /// <summary>
+ /// Gets the first occluding body.
+ /// </summary>
+ /// <value>The first occluding body.</value>
+ public CelestialBody firstOccludingBody
+ {
+ get
+ {
+ return this._firstOccludingBody;
+ }
+ }
+
+ /// <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 (this.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>
- /// 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)
+ public virtual bool CanTransmit()
+ {
+ if (
+ this.transmitDistance > this.maxTransmitDistance ||
+ (
+ requireLineOfSight &&
+ this.nearestRelay == null &&
+ !this.vessel.hasLineOfSightTo(this.Kerbin, out this._firstOccludingBody)
+ )
+ )
{
return false;
}
@@ -113,144 +169,151 @@
/// <returns>The nearest relay or null, if no relays in range.</returns>
public IAntennaRelay FindNearestRelay()
{
- // 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);
+ 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();
Tools.PostDebugMessage(string.Format(
- "{0}: Non-debris vessels in range: {1}",
+ "{0}: finding nearest relay for {1} ({2})",
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;
+ this,
+ this.vessel.id
+ ));
+
+ this._firstOccludingBody = null;
+
+ // Set this vessel as checked, so that we don't check it again.
+ RelayDatabase.Instance.CheckedVesselsTable[vessel.id] = true;
+
+ double nearestSqrDistance = double.PositiveInfinity;
+ IAntennaRelay _nearestRelay = null;
+
+ /*
+ * Loop through all the vessels and exclude this vessel, vessels of the wrong type, and vessels that are too
+ * far away. When we find a candidate, get through its antennae for relays which have not been checked yet
+ * and that can transmit. Once we find a suitable candidate, assign it to _nearestRelay for comparison
+ * against future finds.
+ * */
+ foreach (Vessel potentialVessel in FlightGlobals.Vessels)
+ {
+ // Skip vessels that have already been checked for a nearest relay this pass.
+ if (RelayDatabase.Instance.CheckedVesselsTable.ContainsKey(potentialVessel.id))
+ {
+ continue;
+ }
+
+ // Skip vessels of the wrong type.
+ switch (potentialVessel.vesselType)
+ {
+ case VesselType.Debris:
+ case VesselType.Flag:
+ case VesselType.EVA:
+ case VesselType.SpaceObject:
+ case VesselType.Unknown:
+ continue;
+ default:
+ break;
+ }
+
+ // Skip vessels with the wrong ID
+ if (potentialVessel.id == vessel.id)
+ {
+ continue;
+ }
+
+ // Skip vessels to which we do not have line of sight.
+ if (requireLineOfSight && !this.vessel.hasLineOfSightTo(potentialVessel, out this._firstOccludingBody))
+ {
+ 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 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 (
+ 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;
+ }
+
+ nearestSqrDistance = potentialSqrDistance;
+
+ foreach (IAntennaRelay potentialRelay in potentialVessel.GetAntennaRelays())
+ {
+ if (potentialRelay.CanTransmit())
+ {
+ _nearestRelay = potentialRelay;
+ Tools.PostDebugMessage(string.Format("{0}: found new best relay {1} ({2})",
+ this.GetType().Name,
+ _nearestRelay.ToString(),
+ _nearestRelay.vessel.id
+ ));
+ break;
+ }
}
}
// 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;
+ RelayDatabase.Instance.CheckedVesselsTable.Remove(vessel.id);
// 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} (range to relay: {1}m)",
- vessel,
- Tools.MuMech_ToSI(transmitDistance)
- );
- }
-
/// <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;
+ public AntennaRelay(IAntennaRelay module)
+ {
+ this.moduleRef = module;
+
+ 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);
- }
+ static AntennaRelay()
+ {
+ var config = KSP.IO.PluginConfiguration.CreateForType<AntennaRelay>();
+
+ config.load();
+
+ AntennaRelay.requireLineOfSight = config.GetValue<bool>("requireLineOfSight", false);
+
+ config.save();
}
}
}
--- /dev/null
+++ b/ChangeLog
@@ -1,1 +1,7 @@
+2014-01-14 toadicus <>
+ * ModuleLimitedDataTransmitter.cs: Added a ":" to the
+ transmission communications for consistency with stock
+ behavior.
+
+
--- a/Extensions.cs
+++ /dev/null
@@ -1,158 +1,1 @@
-// 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
-{
- /*
- * A class of utility extensions for Vessels and Relays to help find a relay path back to Kerbin.
- * */
- public static class Extensions
- {
- /// <summary>
- /// Returns the distance between this Vessel and another Vessel.
- /// </summary>
- /// <param name="vesselOne">This <see cref="Vessel"/><see ></param>
- /// <param name="vesselTwo">Another <see cref="Vessel"/></param>
- public static double DistanceTo(this Vessel vesselOne, Vessel vesselTwo)
- {
- return (vesselOne.GetWorldPos3D() - vesselTwo.GetWorldPos3D()).magnitude;
- }
-
- /// <summary>
- /// Returns the distance between this Vessel and a CelestialBody
- /// </summary>
- /// <param name="vessel">This Vessel</param>
- /// <param name="body">A <see cref="CelestialBody"/></param>
- public static double DistanceTo(this Vessel vessel, CelestialBody body)
- {
- return (vessel.GetWorldPos3D() - body.position).magnitude;
- }
-
- /// <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);
- }
-
- /// <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 all of the PartModules or ProtoPartModuleSnapshots implementing IAntennaRelay in this Vessel.
- /// </summary>
- /// <param name="vessel">This <see cref="Vessel"/></param>
- public static IEnumerable<IAntennaRelay> GetAntennaRelays (this Vessel vessel)
- {
- Tools.PostDebugMessage(string.Format(
- "{0}: Getting antenna relays from vessel {1}.",
- "IAntennaRelay",
- vessel.name
- ));
-
- List<IAntennaRelay> Transmitters;
-
- // If the vessel is loaded, we can fetch modules implementing IAntennaRelay directly.
- if (vessel.loaded) {
- Tools.PostDebugMessage(string.Format(
- "{0}: vessel {1} is loaded.",
- "IAntennaRelay",
- vessel.name
- ));
-
- // Gets a list of PartModules implementing IAntennaRelay
- Transmitters = vessel.Parts
- .SelectMany (p => p.Modules.OfType<IAntennaRelay> ())
- .ToList();
- }
- // If the vessel is not loaded, we need to find ProtoPartModuleSnapshots with a true IsAntenna field.
- else
- {
- Tools.PostDebugMessage(string.Format(
- "{0}: vessel {1} is not loaded.",
- "IAntennaRelay",
- vessel.name
- ));
-
- Transmitters = new List<IAntennaRelay>();
-
- // Loop through the ProtoPartModuleSnapshots in this Vessel
- foreach (ProtoPartSnapshot pps in vessel.protoVessel.protoPartSnapshots)
- {
- ProtoPartModuleSnapshot ppms = pps.modules.FirstOrDefault(p => p.IsAntenna());
- // If they are antennas...
- if (ppms != null)
- {
- // ...add a new ProtoAntennaRelay wrapper to the list.
- Transmitters.Add(new ProtoAntennaRelay(ppms, pps, vessel));
- }
- }
- }
-
- Tools.PostDebugMessage(string.Format(
- "{0}: vessel {1} has {2} transmitters.",
- "IAntennaRelay",
- vessel.name,
- Transmitters.Count
- ));
-
- // Return the list of IAntennaRelays
- return Transmitters;
- }
-
- // Returns true if this PartModule contains a True IsAntenna field, false otherwise.
- public static bool IsAntenna (this PartModule module)
- {
- return module.Fields.GetValue<bool> ("IsAntenna");
- }
-
- // Returns true if this ProtoPartModuleSnapshot contains a persistent True IsAntenna field, false otherwise
- public static bool IsAntenna(this ProtoPartModuleSnapshot protomodule)
- {
- bool result;
-
- return Boolean.TryParse (protomodule.moduleValues.GetValue ("IsAntenna") ?? "False", out result)
- ? result : false;
- }
- }
-}
-
-
--- a/IAntennaRelay.cs
+++ b/IAntennaRelay.cs
@@ -1,19 +1,30 @@
-// AntennaRange © 2014 toadicus
+// AntennaRange
//
-// AntennaRange provides incentive and requirements for the use of the various antenna parts.
-// Nominally, the breakdown is as follows:
+// IAntennaRelay.cs
//
-// Communotron 16 - Suitable up to Kerbalsynchronous Orbit
-// Comms DTS-M1 - Suitable throughout the Kerbin subsystem
-// Communotron 88-88 - Suitable throughout the Kerbol system.
+// Copyright © 2014, toadicus
+// All rights reserved.
//
-// 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/
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
//
-// This software uses the ModuleManager library © 2013 ialdabaoth, used under a Creative Commons Attribution-ShareAlike
-// 3.0 Uported License.
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
//
-// This software uses code from the MuMechLib library, © 2013 r4m0n, used under the GNU GPL version 3.
+// 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 KSP;
using System;
--- a/ModuleLimitedDataTransmitter.cs
+++ b/ModuleLimitedDataTransmitter.cs
@@ -1,24 +1,37 @@
-// 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.
-
+// AntennaRange
+//
+// ModuleLimitedDataTransmitter.cs
+//
+// Copyright © 2014, 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 KSP;
using System;
using System.Collections.Generic;
using System.Linq;
-using KSP;
+using System.Text;
+using ToadicusTools;
using UnityEngine;
namespace AntennaRange
@@ -41,9 +54,9 @@
* */
public class ModuleLimitedDataTransmitter : ModuleDataTransmitter, IScienceDataTransmitter, IAntennaRelay
{
- // Call this an antenna so that you don't have to.
- [KSPField(isPersistant = true)]
- protected bool IsAntenna;
+ // If true, use a fixed power cost at the configured value and degrade data rates instead of increasing power
+ // requirements.
+ public static bool fixedPowerCost;
// Stores the packetResourceCost as defined in the .cfg file.
protected float _basepacketResourceCost;
@@ -59,15 +72,27 @@
// 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 = "Relay")]
+ public string UIrelayStatus;
+
+ [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)]
@@ -77,9 +102,17 @@
[KSPField(isPersistant = false)]
public float maxDataFactor;
- // This field exists to get saved to the persistence file so that relays can be found on unloaded Vessels.
- [KSPField(isPersistant = true)]
- protected float ARmaxTransmitDistance;
+ [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;
/*
* Properties
@@ -107,7 +140,7 @@
{
get
{
- return this.ARmaxTransmitDistance;
+ return Mathf.Sqrt (this.maxPowerFactor) * this.nominalRange;
}
}
@@ -144,7 +177,15 @@
get
{
this.PreTransmit_SetPacketSize();
- return this.packetSize;
+
+ if (this.CanTransmit())
+ {
+ return this.packetSize;
+ }
+ else
+ {
+ return float.Epsilon;
+ }
}
}
@@ -182,15 +223,8 @@
// 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);
+ this.packetThrottle = 100f;
}
// At least once, when the module starts with a state on the launch pad or later, go find Kerbin.
@@ -200,8 +234,13 @@
if (state >= StartState.PreLaunch)
{
- this.relay = new AntennaRelay(vessel);
+ this.relay = new AntennaRelay(this);
this.relay.maxTransmitDistance = this.maxTransmitDistance;
+
+ this.UImaxTransmitDistance = Tools.MuMech_ToSI(this.maxTransmitDistance) + "m";
+
+ GameEvents.onPartActionUICreate.Add(this.onPartActionUICreate);
+ GameEvents.onPartActionUIDismiss.Add(this.onPartActionUIDismiss);
}
}
@@ -212,9 +251,6 @@
{
this.Fields.Load(node);
base.Fields.Load(node);
-
- this.ARmaxTransmitDistance = Mathf.Sqrt (this.maxPowerFactor) * this.nominalRange;
- this.IsAntenna = true;
base.OnLoad (node);
@@ -241,15 +277,20 @@
// 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.ARmaxTransmitDistance, 2),
- Tools.MuMech_ToSI((double)this.transmitDistance, 2)
- );
-
- this.ErrorMsg.message = ErrorText;
-
- ScreenMessages.PostScreenMessage(this.ErrorMsg, true);
+ 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>",
+ ((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
@@ -257,7 +298,7 @@
// transmission fails (see CanTransmit).
protected void PreTransmit_SetPacketResourceCost()
{
- if (this.transmitDistance <= this.nominalRange)
+ if (fixedPowerCost || this.transmitDistance <= this.nominalRange)
{
base.packetResourceCost = this._basepacketResourceCost;
}
@@ -266,13 +307,15 @@
base.packetResourceCost = this._basepacketResourceCost
* (float)Math.Pow (this.transmitDistance / this.nominalRange, 2);
}
+
+ base.packetResourceCost *= this.packetThrottle / 100f;
}
// Before transmission, set packetSize. Per above, packet size increases with the inverse square of
// distance. packetSize maxes out at _basepacketSize * maxDataFactor.
protected void PreTransmit_SetPacketSize()
{
- if (this.transmitDistance >= this.nominalRange)
+ if (!fixedPowerCost && this.transmitDistance >= this.nominalRange)
{
base.packetSize = this._basepacketSize;
}
@@ -282,6 +325,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.
@@ -289,13 +334,25 @@
{
string text = base.GetInfo();
text += "Nominal Range: " + Tools.MuMech_ToSI((double)this.nominalRange, 2) + "m\n";
- text += "Maximum Range: " + Tools.MuMech_ToSI((double)this.ARmaxTransmitDistance, 2) + "m\n";
+ text += "Maximum Range: " + Tools.MuMech_ToSI((double)this.maxTransmitDistance, 2) + "m\n";
return text;
}
// Override ModuleDataTransmitter.CanTransmit to return false when transmission is not possible.
public new bool CanTransmit()
{
+ PartStates partState = this.part.State;
+ if (partState == PartStates.DEAD || partState == PartStates.DEACTIVATED)
+ {
+ Tools.PostDebugMessage(string.Format(
+ "{0}: {1} on {2} cannot transmit: {3}",
+ this.GetType().Name,
+ this.part.partInfo.title,
+ this.vessel.vesselName,
+ Enum.GetName(typeof(PartStates), partState)
+ ));
+ return false;
+ }
return this.relay.CanTransmit();
}
@@ -303,8 +360,31 @@
// returns false.
public new void TransmitData(List<ScienceData> dataQueue)
{
+ this.PreTransmit_SetPacketSize();
+ this.PreTransmit_SetPacketResourceCost();
+
if (this.CanTransmit())
{
+ StringBuilder message = new StringBuilder();
+
+ message.Append("[");
+ message.Append(base.part.partInfo.title);
+ message.Append("]: ");
+
+ message.Append("Beginning transmission ");
+
+ if (this.relay.nearestRelay == null)
+ {
+ message.Append("directly to Kerbin.");
+ }
+ else
+ {
+ message.Append("via ");
+ message.Append(this.relay.nearestRelay);
+ }
+
+ ScreenMessages.PostScreenMessage(message.ToString(), 4f, ScreenMessageStyle.UPPER_LEFT);
+
base.TransmitData(dataQueue);
}
else
@@ -334,18 +414,25 @@
if (this.CanTransmit())
{
- this.ErrorMsg.message = "Beginning transmission ";
+ StringBuilder message = new StringBuilder();
+
+ message.Append("[");
+ message.Append(base.part.partInfo.title);
+ message.Append("]: ");
+
+ message.Append("Beginning transmission ");
if (this.relay.nearestRelay == null)
{
- this.ErrorMsg.message += "directly to Kerbin.";
+ message.Append("directly to Kerbin.");
}
else
{
- this.ErrorMsg.message += "via relay " + this.relay.nearestRelay;
- }
-
- ScreenMessages.PostScreenMessage(this.ErrorMsg);
+ message.Append("via ");
+ message.Append(this.relay.nearestRelay);
+ }
+
+ ScreenMessages.PostScreenMessage(message.ToString(), 4f, ScreenMessageStyle.UPPER_LEFT);
base.StartTransmission();
}
@@ -353,6 +440,65 @@
{
this.PostCannotTransmitError ();
}
+ }
+
+ public void Update()
+ {
+ if (this.actionUIUpdate)
+ {
+ if (this.CanTransmit())
+ {
+ this.UIrelayStatus = string.Intern("Connected");
+ this.UItransmitDistance = Tools.MuMech_ToSI(this.transmitDistance) + "m";
+ this.UIpacketSize = Tools.MuMech_ToSI(this.DataRate) + "MiT";
+ this.UIpacketCost = Tools.MuMech_ToSI(this.DataResourceCost) + "E";
+ }
+ else
+ {
+ if (this.relay.firstOccludingBody == null)
+ {
+ this.UIrelayStatus = string.Intern("Out of range");
+ }
+ else
+ {
+ this.UIrelayStatus = string.Format("Blocked by {0}", this.relay.firstOccludingBody.bodyName);
+ }
+ this.UImaxTransmitDistance = "N/A";
+ this.UIpacketSize = "N/A";
+ this.UIpacketCost = "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;
+ }
+ }
+
+ public override string ToString()
+ {
+ StringBuilder msg = new StringBuilder();
+
+ msg.Append(this.part.partInfo.title);
+
+ if (vessel != null)
+ {
+ msg.Append(" on ");
+ msg.Append(vessel.vesselName);
+ }
+
+ return msg.ToString();
}
// When debugging, it's nice to have a button that just tells you everything.
@@ -376,22 +522,45 @@
"DataRate: {9}\n" +
"DataResourceCost: {10}\n" +
"TransmitterScore: {11}\n" +
- "NearestRelay: {12}",
+ "NearestRelay: {12}\n" +
+ "Vessel ID: {13}",
this.name,
this._basepacketSize,
base.packetSize,
this._basepacketResourceCost,
base.packetResourceCost,
- this.ARmaxTransmitDistance,
+ this.maxTransmitDistance,
this.transmitDistance,
this.nominalRange,
this.CanTransmit(),
this.DataRate,
this.DataResourceCost,
ScienceUtil.GetTransmitterScore(this),
- this.relay.FindNearestRelay()
+ this.relay.FindNearestRelay(),
+ this.vessel.id
);
- ScreenMessages.PostScreenMessage (new ScreenMessage (msg, 4f, ScreenMessageStyle.UPPER_RIGHT));
+ Tools.PostDebugMessage(msg);
+ }
+
+ [KSPEvent (guiName = "Dump Vessels", active = true, guiActive = true)]
+ public void PrintAllVessels()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ sb.Append("Dumping FlightGlobals.Vessels:");
+
+ foreach (Vessel vessel in FlightGlobals.Vessels)
+ {
+ sb.AppendFormat("\n'{0} ({1})'", vessel.vesselName, vessel.id);
+ }
+
+ Tools.PostDebugMessage(sb.ToString());
+ }
+
+ [KSPEvent (guiName = "Dump RelayDB", active = true, guiActive = true)]
+ public void DumpRelayDB()
+ {
+ RelayDatabase.Instance.Dump();
}
#endif
}
--- /dev/null
+++ b/Properties/AssemblyInfo.cs
@@ -1,1 +1,46 @@
+// AntennaRange
+//
+// AssemblyInfo.cs
+//
+// Copyright © 2014, 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.Reflection;
+using System.Runtime.CompilerServices;
+
+// Information about this assembly is defined by the following attributes.
+// Change them to the values specific to your project.
+[assembly: AssemblyTitle("AntennaRange")]
+[assembly: AssemblyDescription("Enforce and Encourage Antenna Diversity")]
+[assembly: AssemblyCopyright("toadicus")]
+// 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.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)]
+//[assembly: AssemblyKeyFile("")]
+
+
--- /dev/null
+++ b/Properties/ChangeLog
@@ -1,1 +1,5 @@
+2014-01-14 toadicus <>
+ * AssemblyInfo.cs: New AssemblyInfo file for reason.
+
+
--- a/ProtoAntennaRelay.cs
+++ b/ProtoAntennaRelay.cs
@@ -1,22 +1,35 @@
-// AntennaRange © 2014 toadicus
+// AntennaRange
//
-// AntennaRange provides incentive and requirements for the use of the various antenna parts.
-// Nominally, the breakdown is as follows:
+// ProtoAntennaRelay.cs
//
-// Communotron 16 - Suitable up to Kerbalsynchronous Orbit
-// Comms DTS-M1 - Suitable throughout the Kerbin subsystem
-// Communotron 88-88 - Suitable throughout the Kerbol system.
+// Copyright © 2014, toadicus
+// All rights reserved.
//
-// 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/
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
//
-// This software uses the ModuleManager library © 2013 ialdabaoth, used under a Creative Commons Attribution-ShareAlike
-// 3.0 Uported License.
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
//
-// This software uses code from the MuMechLib library, © 2013 r4m0n, used under the GNU GPL version 3.
+// 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 KSP;
using System;
using System.Linq;
+using ToadicusTools;
namespace AntennaRange
{
@@ -26,8 +39,16 @@
* */
public class ProtoAntennaRelay : AntennaRelay, IAntennaRelay
{
- protected ProtoPartModuleSnapshot protoModule;
- protected Part partPrefab;
+ // Stores the prototype part so we can make sure we haven't exploded or so.
+ protected ProtoPartSnapshot protoPart;
+
+ public override Vessel vessel
+ {
+ get
+ {
+ return this.protoPart.pVesselRef.vesselRef;
+ }
+ }
/// <summary>
/// The maximum distance at which this transmitter can operate.
@@ -37,7 +58,7 @@
{
get
{
- return this.partPrefab.Modules.OfType<ModuleLimitedDataTransmitter>().First().maxTransmitDistance;
+ return moduleRef.maxTransmitDistance;
}
}
@@ -48,23 +69,46 @@
/// <value><c>true</c> if relay checked; otherwise, <c>false</c>.</value>
public override bool relayChecked
{
+ get;
+ protected set;
+ }
+
+ /// <summary>
+ /// Gets the underlying part's title.
+ /// </summary>
+ /// <value>The title.</value>
+ public string title
+ {
get
{
- bool result;
- Boolean.TryParse(this.protoModule.moduleValues.GetValue("relayChecked"), out result);
- return result;
+ return this.protoPart.partInfo.title;
}
- protected set
+ }
+
+ public override bool CanTransmit()
+ {
+ PartStates partState = (PartStates)this.protoPart.state;
+ if (partState == PartStates.DEAD || partState == PartStates.DEACTIVATED)
{
- if (this.protoModule.moduleValues.HasValue("relayChecked"))
- {
- this.protoModule.moduleValues.SetValue("relayChecked", value.ToString ());
- }
- else
- {
- this.protoModule.moduleValues.AddValue("relayChecked", value);
- }
+ Tools.PostDebugMessage(string.Format(
+ "{0}: {1} on {2} cannot transmit: {3}",
+ this.GetType().Name,
+ this.title,
+ this.vessel.vesselName,
+ Enum.GetName(typeof(PartStates), partState)
+ ));
+ return false;
}
+ return base.CanTransmit();
+ }
+
+ public override string ToString()
+ {
+ return string.Format(
+ "{0} on {1} (proto)",
+ this.title,
+ this.protoPart.pVesselRef.vesselName
+ );
}
/// <summary>
@@ -72,10 +116,17 @@
/// </summary>
/// <param name="ms">The ProtoPartModuleSnapshot to wrap</param>
/// <param name="vessel">The parent Vessel</param>
- public ProtoAntennaRelay(ProtoPartModuleSnapshot ppms, ProtoPartSnapshot pps, Vessel vessel) : base(vessel)
+ public ProtoAntennaRelay(IAntennaRelay prefabRelay, ProtoPartSnapshot pps) : base(prefabRelay)
{
- this.protoModule = ppms;
- this.partPrefab = PartLoader.getPartInfoByName(pps.partName).partPrefab;
+ this.protoPart = pps;
+ }
+
+ ~ProtoAntennaRelay()
+ {
+ Tools.PostDebugMessage(string.Format(
+ "{0}: destroyed",
+ this.ToString()
+ ));
}
}
}
--- /dev/null
+++ b/RelayDatabase.cs
@@ -1,1 +1,408 @@
-
+// AntennaRange
+//
+// RelayDatabase.cs
+//
+// Copyright © 2014, 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 KSP;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using ToadicusTools;
+using UnityEngine;
+
+namespace AntennaRange
+{
+ public class RelayDatabase
+ {
+ /*
+ * Static members
+ * */
+ // Singleton storage
+ protected static RelayDatabase _instance;
+ // Gets the singleton
+ public static RelayDatabase Instance
+ {
+ get
+ {
+ if (_instance == null)
+ {
+ _instance = new RelayDatabase();
+ }
+
+ return _instance;
+ }
+ }
+
+ /*
+ * Instance members
+ * */
+
+ /*
+ * Fields
+ * */
+ // Vessel.id-keyed hash table of Part.GetHashCode()-keyed tables of relay objects.
+ protected Dictionary<Guid, Dictionary<int, IAntennaRelay>> relayDatabase;
+
+ // Vessel.id-keyed hash table of part counts, used for caching
+ protected Dictionary<Guid, int> vesselPartCountTable;
+
+ // Vessel.id-keyed hash table of booleans to track what vessels have been checked so far this time.
+ public Dictionary<Guid, bool> CheckedVesselsTable;
+
+ protected int cacheHits;
+ protected int cacheMisses;
+
+ /*
+ * Properties
+ * */
+ // Gets the Part-hashed table of relays in a given vessel
+ public Dictionary<int, IAntennaRelay> this [Vessel vessel]
+ {
+ get
+ {
+ // If we don't have an entry for this vessel...
+ if (!this.ContainsKey(vessel.id))
+ {
+ // ...Generate an entry for this vessel.
+ this.AddVessel(vessel);
+ this.cacheMisses++;
+ }
+ // If our part count disagrees with the vessel's part count...
+ else if (this.vesselPartCountTable[vessel.id] != vessel.Parts.Count)
+ {
+ // ...Update the our vessel in the cache
+ this.UpdateVessel(vessel);
+ this.cacheMisses++;
+ }
+ // Otherwise, it's a hit
+ else
+ {
+ this.cacheHits++;
+ }
+
+ // Return the Part-hashed table of relays for this vessel
+ return relayDatabase[vessel.id];
+ }
+ }
+
+ /*
+ * Methods
+ * */
+ // Adds a vessel to the database
+ // The return for this function isn't used yet, but seems useful for potential future API-uses
+ public bool AddVessel(Vessel vessel)
+ {
+ // If this vessel is already here...
+ if (this.ContainsKey(vessel))
+ {
+ // ...post an error
+ Debug.LogWarning(string.Format(
+ "{0}: Cannot add vessel '{1}' (id: {2}): Already in database.",
+ this.GetType().Name,
+ vessel.vesselName,
+ vessel.id
+ ));
+
+ // ...and refuse to add
+ return false;
+ }
+ // otherwise, add the vessel to our tables...
+ else
+ {
+ // Build an empty table...
+ this.relayDatabase[vessel.id] = new Dictionary<int, IAntennaRelay>();
+
+ // Update the empty index
+ this.UpdateVessel(vessel);
+
+ // Return success
+ return true;
+ }
+ }
+
+ // Update the vessel's entry in the table
+ public void UpdateVessel(Vessel vessel)
+ {
+ // Squak if the database doesn't have the vessel
+ if (!this.ContainsKey(vessel))
+ {
+ throw new InvalidOperationException(string.Format(
+ "{0}: Update called for vessel '{1}' (id: {2}) not in database: vessel will be added.",
+ this.GetType().Name,
+ vessel.vesselName,
+ vessel.id
+ ));
+ }
+
+ Dictionary<int, IAntennaRelay> vesselTable = this.relayDatabase[vessel.id];
+
+ // Actually build and assign the table
+ this.getVesselRelays(vessel, ref vesselTable);
+ // Set the part count
+ this.vesselPartCountTable[vessel.id] = vessel.Parts.Count;
+ }
+
+ // Remove a vessel from the cache, if it exists.
+ public void DirtyVessel(Vessel vessel)
+ {
+ if (this.relayDatabase.ContainsKey(vessel.id))
+ {
+ this.relayDatabase.Remove(vessel.id);
+ }
+ if (this.vesselPartCountTable.ContainsKey(vessel.id))
+ {
+ this.vesselPartCountTable.Remove(vessel.id);
+ }
+ }
+
+ // Returns true if both the relayDatabase and the vesselPartCountDB contain the vessel id.
+ public bool ContainsKey(Guid key)
+ {
+ return this.relayDatabase.ContainsKey(key);
+ }
+
+ // Returns true if both the relayDatabase and the vesselPartCountDB contain the vessel.
+ public bool ContainsKey(Vessel vessel)
+ {
+ return this.ContainsKey(vessel.id);
+ }
+
+ // Runs when a vessel is modified (or when we switch to one, to catch docking events)
+ public void onVesselEvent(Vessel vessel)
+ {
+ // If we have this vessel in our cache...
+ if (this.ContainsKey(vessel))
+ {
+ // If our part counts disagree (such as if a part has been added or broken off,
+ // or if we've just docked or undocked)...
+ if (this.vesselPartCountTable[vessel.id] != vessel.Parts.Count || vessel.loaded)
+ {
+ Tools.PostDebugMessage(string.Format(
+ "{0}: dirtying cache for vessel '{1}' ({2}).",
+ this.GetType().Name,
+ vessel.vesselName,
+ vessel.id
+ ));
+
+ // Dirty the cache (real vessels will never have negative part counts)
+ this.DirtyVessel(vessel);
+ }
+ }
+ }
+
+ // Runs when the player requests a scene change, such as when changing vessels or leaving flight.
+ public void onSceneChange(GameScenes scene)
+ {
+ // If the active vessel is a real thing...
+ if (FlightGlobals.ActiveVessel != null)
+ {
+ // ... dirty its cache
+ this.onVesselEvent(FlightGlobals.ActiveVessel);
+ }
+ }
+
+ // Runs when parts are undocked
+ public void onPartEvent(Part part)
+ {
+ if (part != null && part.vessel != null)
+ {
+ this.onVesselEvent(part.vessel);
+ }
+ }
+
+ // Runs when parts are coupled, as in docking
+ public void onFromPartToPartEvent(GameEvents.FromToAction<Part, Part> data)
+ {
+ this.onPartEvent(data.from);
+ this.onPartEvent(data.to);
+ }
+
+ // Produce a Part-hashed table of relays for the given vessel
+ protected void getVesselRelays(Vessel vessel, ref Dictionary<int, IAntennaRelay> relays)
+ {
+ // We're going to completely regen this table, so dump the current contents.
+ relays.Clear();
+
+ Tools.PostDebugMessage(string.Format(
+ "{0}: Getting antenna relays from vessel {1}.",
+ "IAntennaRelay",
+ vessel.vesselName
+ ));
+
+ // If the vessel is loaded, we can fetch modules implementing IAntennaRelay directly.
+ if (vessel.loaded) {
+ Tools.PostDebugMessage(string.Format(
+ "{0}: vessel {1} is loaded, searching for modules in loaded parts.",
+ "IAntennaRelay",
+ vessel.vesselName
+ ));
+
+ // Loop through the Parts in the Vessel...
+ foreach (Part part in vessel.Parts)
+ {
+ // ...loop through the PartModules in the Part...
+ foreach (PartModule module in part.Modules)
+ {
+ // ...if the module is a relay...
+ if (module is IAntennaRelay)
+ {
+ // ...add the module to the table
+ relays.Add(part.GetHashCode(), module as IAntennaRelay);
+ // ...neglect relay objects after the first in each part.
+ break;
+ }
+ }
+ }
+ }
+ // If the vessel is not loaded, we need to build ProtoAntennaRelays when we find relay ProtoPartSnapshots.
+ else
+ {
+ Tools.PostDebugMessage(string.Format(
+ "{0}: vessel {1} is not loaded, searching for modules in prototype parts.",
+ this.GetType().Name,
+ vessel.vesselName
+ ));
+
+ // Loop through the ProtoPartModuleSnapshots in the Vessel...
+ foreach (ProtoPartSnapshot pps in vessel.protoVessel.protoPartSnapshots)
+ {
+ Tools.PostDebugMessage(string.Format(
+ "{0}: Searching in protopartsnapshot {1}",
+ this.GetType().Name,
+ pps
+ ));
+
+ // ...Fetch the prefab, because it's more useful for what we're doing.
+ Part partPrefab = PartLoader.getPartInfoByName(pps.partName).partPrefab;
+
+ Tools.PostDebugMessage(string.Format(
+ "{0}: Got partPrefab {1} in protopartsnapshot {2}",
+ this.GetType().Name,
+ partPrefab,
+ pps
+ ));
+
+ // ...loop through the PartModules in the prefab...
+ foreach (PartModule module in partPrefab.Modules)
+ {
+ Tools.PostDebugMessage(string.Format(
+ "{0}: Searching in partmodule {1}",
+ this.GetType().Name,
+ module
+ ));
+
+ // ...if the module is a relay...
+ if (module is IAntennaRelay)
+ {
+ Tools.PostDebugMessage(string.Format(
+ "{0}: partmodule {1} is antennarelay",
+ this.GetType().Name,
+ module
+ ));
+
+ // ...build a new ProtoAntennaRelay and add it to the table
+ relays.Add(pps.GetHashCode(), new ProtoAntennaRelay(module as IAntennaRelay, pps));
+ // ...neglect relay objects after the first in each part.
+ break;
+ }
+ }
+ }
+ }
+
+ Tools.PostDebugMessage(string.Format(
+ "{0}: vessel '{1}' ({2}) has {3} transmitters.",
+ "IAntennaRelay",
+ vessel.vesselName,
+ vessel.id,
+ relays.Count
+ ));
+ }
+
+ // Construct the singleton
+ protected RelayDatabase()
+ {
+ // Initialize the databases
+ this.relayDatabase = new Dictionary<Guid, Dictionary<int, IAntennaRelay>>();
+ this.vesselPartCountTable = new Dictionary<Guid, int>();
+ this.CheckedVesselsTable = new Dictionary<Guid, bool>();
+
+ this.cacheHits = 0;
+ this.cacheMisses = 0;
+
+ // Subscribe to some events
+ GameEvents.onVesselWasModified.Add(this.onVesselEvent);
+ GameEvents.onVesselChange.Add(this.onVesselEvent);
+ GameEvents.onVesselDestroy.Add(this.onVesselEvent);
+ GameEvents.onGameSceneLoadRequested.Add(this.onSceneChange);
+ GameEvents.onPartCouple.Add(this.onFromPartToPartEvent);
+ GameEvents.onPartUndock.Add(this.onPartEvent);
+ }
+
+ ~RelayDatabase()
+ {
+ // Unsubscribe from the events
+ GameEvents.onVesselWasModified.Remove(this.onVesselEvent);
+ GameEvents.onVesselChange.Remove(this.onVesselEvent);
+ GameEvents.onVesselDestroy.Remove(this.onVesselEvent);
+ GameEvents.onGameSceneLoadRequested.Remove(this.onSceneChange);
+ GameEvents.onPartCouple.Remove(this.onFromPartToPartEvent);
+ GameEvents.onPartUndock.Remove(this.onPartEvent);
+
+ Tools.PostDebugMessage(this.GetType().Name + " destroyed.");
+
+ KSPLog.print(string.Format(
+ "{0} destructed. Cache hits: {1}, misses: {2}, hit rate: {3:P1}",
+ this.GetType().Name,
+ this.cacheHits,
+ this.cacheMisses,
+ (float)this.cacheHits / (float)(this.cacheMisses + this.cacheHits)
+ ));
+ }
+
+ #if DEBUG
+ public void Dump()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ sb.Append("Dumping RelayDatabase:");
+
+ foreach (Guid id in this.relayDatabase.Keys)
+ {
+ sb.AppendFormat("\nVessel {0}:", id);
+
+ foreach (IAntennaRelay relay in this.relayDatabase[id].Values)
+ {
+ sb.AppendFormat("\n\t{0}", relay.ToString());
+ }
+ }
+
+ Tools.PostDebugMessage(sb.ToString());
+ }
+ #endif
+ }
+}
+
+
--- /dev/null
+++ b/RelayExtensions.cs
@@ -1,1 +1,100 @@
+// AntennaRange
+//
+// Extensions.cs
+//
+// Copyright © 2014, 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;
+using System.Linq;
+using ToadicusTools;
+
+namespace AntennaRange
+{
+ /*
+ * A class of utility extensions for Vessels and Relays to help find a relay path back to Kerbin.
+ * */
+ 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);
+ }
+
+ /// <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 all of the PartModules or ProtoPartModuleSnapshots implementing IAntennaRelay in this Vessel.
+ /// </summary>
+ /// <param name="vessel">This <see cref="Vessel"/></param>
+ public static IEnumerable<IAntennaRelay> GetAntennaRelays (this Vessel vessel)
+ {
+ return RelayDatabase.Instance[vessel].Values.ToList();
+ }
+
+ /// <summary>
+ /// Determines if the specified vessel has a connected relay.
+ /// </summary>
+ /// <returns><c>true</c> if the specified vessel has a connected relay; otherwise, <c>false</c>.</returns>
+ /// <param name="vessel"></param>
+ public static bool HasConnectedRelay(this Vessel vessel)
+ {
+ foreach (IAntennaRelay relay in RelayDatabase.Instance[vessel].Values)
+ {
+ if (relay.CanTransmit())
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+}
+
+