* text=auto | |
* eol=lf | |
# These files are text and should be normalized (convert crlf => lf) | |
*.cs text diff=csharp | |
*.cfg text | |
*.csproj text | |
*.sln text | |
# Images should be treated as binary | |
# (binary is a macro for -text -diff) | |
*.png binary | |
// AntennaRange © 2014 toadicus | // AntennaRange © 2014 toadicus |
// | // |
// This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. To view a | // 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/ | // copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/3.0/ |
using KSP; | using KSP; |
using System; | using System; |
using ToadicusTools; | using ToadicusTools; |
using UnityEngine; | using UnityEngine; |
namespace AntennaRange | namespace AntennaRange |
{ | { |
/// <summary> | |
/// A <see cref="UnityEngine.MonoBehaviour"/> responsible for managing configuration items for AntennaRange. | |
/// </summary> | |
[KSPAddon(KSPAddon.Startup.SpaceCentre, false)] | [KSPAddon(KSPAddon.Startup.SpaceCentre, false)] |
public class ARConfiguration : MonoBehaviour | public class ARConfiguration : MonoBehaviour |
{ | { |
/// <summary> | |
/// Indicates whether connections require line of sight. | |
/// </summary> | |
public static bool RequireLineOfSight | |
{ | |
get; | |
private set; | |
} | |
/// <summary> | |
/// A "fudge factor" ratio that pretends planets and moons are slightly smaller than reality to make | |
/// building communication constellations easier. | |
/// </summary> | |
public static double RadiusRatio | |
{ | |
get; | |
private set; | |
} | |
/// <summary> | |
/// Indicates whether unmanned vessels require a connection for control. | |
/// </summary> | |
public static bool RequireConnectionForControl | |
{ | |
get; | |
private set; | |
} | |
/// <summary> | |
/// If true, relays will fix their power cost when above nominal range, decreasing data rate instead. | |
/// </summary> | |
public static bool FixedPowerCost | |
{ | |
get; | |
private set; | |
} | |
/// <summary> | |
/// Indicates whether this AntennaRange will draw pretty lines in map view. | |
/// </summary> | |
public static bool PrettyLines | |
{ | |
get; | |
private set; | |
} | |
#pragma warning disable 1591 | |
private bool showConfigWindow; | private bool showConfigWindow; |
private Rect configWindowPos; | private Rect configWindowPos; |
private IButton toolbarButton; | private IButton toolbarButton; |
private ApplicationLauncherButton appLauncherButton; | |
private System.Version runningVersion; | private System.Version runningVersion; |
private KSP.IO.PluginConfiguration _config; | private KSP.IO.PluginConfiguration _config; |
private KSP.IO.PluginConfiguration config | private KSP.IO.PluginConfiguration config |
{ | { |
get | get |
{ | { |
if (this._config == null) | if (this._config == null) |
{ | { |
this._config = KSP.IO.PluginConfiguration.CreateForType<AntennaRelay>(); | this._config = KSP.IO.PluginConfiguration.CreateForType<AntennaRelay>(); |
} | } |
return this._config; | return this._config; |
} | } |
} | } |
public void Awake() | public void Awake() |
{ | { |
Tools.PostDebugMessage(this, "Waking up."); | Tools.PostDebugMessage(this, "Waking up."); |
this.runningVersion = this.GetType().Assembly.GetName().Version; | |
this.showConfigWindow = false; | this.showConfigWindow = false; |
this.configWindowPos = new Rect(Screen.width / 4, Screen.height / 2, 180, 15); | this.configWindowPos = new Rect(Screen.width / 4, Screen.height / 2, 180, 15); |
this.configWindowPos = this.LoadConfigValue("configWindowPos", this.configWindowPos); | |
ARConfiguration.RequireLineOfSight = this.LoadConfigValue("requireLineOfSight", false); | |
ARConfiguration.RadiusRatio = (1 - this.LoadConfigValue("graceRatio", .05d)); | |
ARConfiguration.RadiusRatio *= ARConfiguration.RadiusRatio; | |
ARConfiguration.RequireConnectionForControl = | |
this.LoadConfigValue("requireConnectionForControl", false); | |
ARConfiguration.FixedPowerCost = this.LoadConfigValue("fixedPowerCost", false); | |
ARConfiguration.PrettyLines = this.LoadConfigValue("drawPrettyLines", true); | |
GameEvents.onGameSceneLoadRequested.Add(this.onSceneChangeRequested); | |
Debug.Log(string.Format("{0} v{1} - ARConfiguration loaded!", this.GetType().Name, this.runningVersion)); | |
Tools.PostDebugMessage(this, "Awake."); | Tools.PostDebugMessage(this, "Awake."); |
} | } |
public void OnGUI() | public void OnGUI() |
{ | { |
// Only runs once, if the Toolbar is available. | // Only runs once, if the Toolbar is available. |
if (this.toolbarButton == null && ToolbarManager.ToolbarAvailable) | if (ToolbarManager.ToolbarAvailable) |
{ | { |
this.runningVersion = this.GetType().Assembly.GetName().Version; | if (this.toolbarButton == null) |
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; | Tools.PostDebugMessage(this, "Toolbar available; initializing toolbar button."); |
}; | |
this.toolbarButton = ToolbarManager.Instance.add("AntennaRange", "ARConfiguration"); | |
this.configWindowPos = this.LoadConfigValue("configWindowPos", this.configWindowPos); | this.toolbarButton.Visibility = new GameScenesVisibility(GameScenes.SPACECENTER); |
AntennaRelay.requireLineOfSight = this.LoadConfigValue("requireLineOfSight", false); | this.toolbarButton.Text = "AR"; |
ARFlightController.requireConnectionForControl = | this.toolbarButton.TexturePath = "AntennaRange/Textures/toolbarIcon"; |
this.LoadConfigValue("requireConnectionForControl", false); | this.toolbarButton.TextColor = (Color)XKCDColors.Amethyst; |
ModuleLimitedDataTransmitter.fixedPowerCost = this.LoadConfigValue("fixedPowerCost", false); | this.toolbarButton.OnClick += delegate(ClickEvent e) |
{ | |
Debug.Log(string.Format("{0} v{1} - ARonfiguration loaded!", this.GetType().Name, this.runningVersion)); | this.toggleConfigWindow(); |
}; | |
} | |
} | |
else if (this.appLauncherButton == null && ApplicationLauncher.Ready) | |
{ | |
Tools.PostDebugMessage(this, "Toolbar available; initializing AppLauncher button."); | |
this.appLauncherButton = ApplicationLauncher.Instance.AddModApplication( | |
this.toggleConfigWindow, | |
this.toggleConfigWindow, | |
ApplicationLauncher.AppScenes.SPACECENTER, | |
GameDatabase.Instance.GetTexture( | |
"AntennaRange/Textures/appLauncherIcon", | |
false | |
) | |
); | |
} | } |
if (this.showConfigWindow) | if (this.showConfigWindow) |
{ | { |
Rect configPos = GUILayout.Window(354163056, | Rect configPos = GUILayout.Window(354163056, |
this.configWindowPos, | this.configWindowPos, |
this.ConfigWindow, | this.ConfigWindow, |
string.Format("AntennaRange {0}.{1}", this.runningVersion.Major, this.runningVersion.Minor), | string.Format("AntennaRange {0}.{1}", this.runningVersion.Major, this.runningVersion.Minor), |
GUILayout.ExpandHeight(true), | GUILayout.ExpandHeight(true), |
GUILayout.ExpandWidth(true) | GUILayout.ExpandWidth(true) |
); | ); |
configPos = Tools.ClampRectToScreen(configPos, 20); | configPos = Tools.ClampRectToScreen(configPos, 20); |
if (configPos != this.configWindowPos) | if (configPos != this.configWindowPos) |
{ | { |
this.configWindowPos = configPos; | this.configWindowPos = configPos; |
this.SaveConfigValue("configWindowPos", this.configWindowPos); | this.SaveConfigValue("configWindowPos", this.configWindowPos); |
} | } |
} | } |
} | } |
public void ConfigWindow(int _) | public void ConfigWindow(int _) |
{ | { |
GUILayout.BeginVertical(GUILayout.ExpandHeight(true)); | GUILayout.BeginVertical(GUILayout.ExpandHeight(true)); |
GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true)); | GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true)); |
bool requireLineOfSight = GUILayout.Toggle(AntennaRelay.requireLineOfSight, "Require Line of Sight"); | bool requireLineOfSight = GUITools.Toggle(ARConfiguration.RequireLineOfSight, "Require Line of Sight"); |
if (requireLineOfSight != AntennaRelay.requireLineOfSight) | if (requireLineOfSight != ARConfiguration.RequireLineOfSight) |
{ | { |
AntennaRelay.requireLineOfSight = requireLineOfSight; | ARConfiguration.RequireLineOfSight = requireLineOfSight; |
this.SaveConfigValue("requireLineOfSight", requireLineOfSight); | this.SaveConfigValue("requireLineOfSight", requireLineOfSight); |
} | } |
GUILayout.EndHorizontal(); | GUILayout.EndHorizontal(); |
GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true)); | GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true)); |
bool requireConnectionForControl = | bool requireConnectionForControl = |
GUILayout.Toggle( | GUITools.Toggle( |
ARFlightController.requireConnectionForControl, | ARConfiguration.RequireConnectionForControl, |
"Require Connection for Probe Control" | "Require Connection for Probe Control" |
); | ); |
if (requireConnectionForControl != ARFlightController.requireConnectionForControl) | if (requireConnectionForControl != ARConfiguration.RequireConnectionForControl) |
{ | { |
ARFlightController.requireConnectionForControl = requireConnectionForControl; | ARConfiguration.RequireConnectionForControl = requireConnectionForControl; |
this.SaveConfigValue("requireConnectionForControl", requireConnectionForControl); | this.SaveConfigValue("requireConnectionForControl", requireConnectionForControl); |
} | } |
GUILayout.EndHorizontal(); | GUILayout.EndHorizontal(); |
GUILayout.BeginHorizontal(); | GUILayout.BeginHorizontal(); |
bool fixedPowerCost = GUILayout.Toggle(ModuleLimitedDataTransmitter.fixedPowerCost, "Use fixed power cost"); | bool fixedPowerCost = GUITools.Toggle(ARConfiguration.FixedPowerCost, "Use Fixed Power Cost"); |
if (fixedPowerCost != ModuleLimitedDataTransmitter.fixedPowerCost) | if (fixedPowerCost != ARConfiguration.FixedPowerCost) |
{ | { |
ModuleLimitedDataTransmitter.fixedPowerCost = fixedPowerCost; | ARConfiguration.FixedPowerCost = fixedPowerCost; |
this.SaveConfigValue("fixedPowerCost", fixedPowerCost); | this.SaveConfigValue("fixedPowerCost", fixedPowerCost); |
} | } |
GUILayout.EndHorizontal(); | GUILayout.EndHorizontal(); |
GUILayout.BeginHorizontal(); | |
bool prettyLines = GUITools.Toggle(ARConfiguration.PrettyLines, "Draw Pretty Lines"); | |
if (prettyLines != ARConfiguration.PrettyLines) | |
{ | |
ARConfiguration.PrettyLines = prettyLines; | |
this.SaveConfigValue("drawPrettyLines", prettyLines); | |
} | |
GUILayout.EndHorizontal(); | |
if (requireLineOfSight) | |
{ | |
GUILayout.BeginHorizontal(); | |
double graceRatio = 1d - Math.Sqrt(ARConfiguration.RadiusRatio); | |
double newRatio; | |
GUILayout.Label(string.Format("Line of Sight 'Fudge Factor': {0:P0}", graceRatio)); | |
GUILayout.EndHorizontal(); | |
GUILayout.BeginHorizontal(); | |
newRatio = GUILayout.HorizontalSlider((float)graceRatio, 0f, 1f, GUILayout.ExpandWidth(true)); | |
newRatio = Math.Round(newRatio, 2); | |
if (newRatio != graceRatio) | |
{ | |
ARConfiguration.RadiusRatio = (1d - newRatio) * (1d - newRatio); | |
this.SaveConfigValue("graceRatio", newRatio); | |
} | |
GUILayout.EndHorizontal(); | |
} | |
GUILayout.EndVertical(); | GUILayout.EndVertical(); |
GUI.DragWindow(); | GUI.DragWindow(); |
} | } |
public void Destroy() | public void OnDestroy() |
{ | { |
GameEvents.onGameSceneLoadRequested.Remove(this.onSceneChangeRequested); | |
if (this.toolbarButton != null) | if (this.toolbarButton != null) |
{ | { |
this.toolbarButton.Destroy(); | this.toolbarButton.Destroy(); |
} | } |
if (this.appLauncherButton != null) | |
{ | |
ApplicationLauncher.Instance.RemoveModApplication(this.appLauncherButton); | |
} | |
} | |
protected void onSceneChangeRequested(GameScenes scene) | |
{ | |
if (scene != GameScenes.SPACECENTER) | |
{ | |
print("ARConfiguration: Requesting Destruction."); | |
MonoBehaviour.Destroy(this); | |
} | |
} | |
private void toggleConfigWindow() | |
{ | |
this.showConfigWindow = !this.showConfigWindow; | |
} | } |
private T LoadConfigValue<T>(string key, T defaultValue) | private T LoadConfigValue<T>(string key, T defaultValue) |
{ | { |
this.config.load(); | this.config.load(); |
return config.GetValue(key, defaultValue); | return config.GetValue(key, defaultValue); |
} | } |
private void SaveConfigValue<T>(string key, T value) | private void SaveConfigValue<T>(string key, T value) |
{ | { |
this.config.load(); | this.config.load(); |
this.config.SetValue(key, value); | this.config.SetValue(key, value); |
this.config.save(); | this.config.save(); |
} | } |
} | } |
} | } |
// AntennaRange | // AntennaRange |
// | // |
// ARFlightController.cs | // ARFlightController.cs |
// | // |
// Copyright © 2014, toadicus | // Copyright © 2014-2015, toadicus |
// All rights reserved. | // All rights reserved. |
// | // |
// Redistribution and use in source and binary forms, with or without modification, | // Redistribution and use in source and binary forms, with or without modification, |
// are permitted provided that the following conditions are met: | // are permitted provided that the following conditions are met: |
// | // |
// 1. Redistributions of source code must retain the above copyright notice, | // 1. Redistributions of source code must retain the above copyright notice, |
// this list of conditions and the following disclaimer. | // this list of conditions and the following disclaimer. |
// | // |
// 2. Redistributions in binary form must reproduce the above copyright notice, | // 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 | // this list of conditions and the following disclaimer in the documentation and/or other |
// materials provided with the distribution. | // materials provided with the distribution. |
// | // |
// 3. Neither the name of the copyright holder nor the names of its contributors may be used | // 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. | // 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, | // 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 | // 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, | // 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 | // 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, | // 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 | // 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. | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
#pragma warning disable 1591 | |
using KSP; | using KSP; |
using System; | using System; |
using System.Collections.Generic; | using System.Collections.Generic; |
using ToadicusTools; | using ToadicusTools; |
using UnityEngine; | using UnityEngine; |
namespace AntennaRange | namespace AntennaRange |
{ | { |
[KSPAddon(KSPAddon.Startup.Flight, false)] | [KSPAddon(KSPAddon.Startup.Flight, false)] |
public class ARFlightController : MonoBehaviour | public class ARFlightController : MonoBehaviour |
{ | { |
#region Static Members | |
public static bool requireConnectionForControl; | |
#endregion | |
#region Fields | #region Fields |
protected Dictionary<ConnectionStatus, string> connectionTextures; | private Dictionary<ConnectionStatus, string> toolbarTextures; |
private Dictionary<ConnectionStatus, Texture> appLauncherTextures; | |
protected IButton toolbarButton; | |
private ARMapRenderer mapRenderer; | |
private IButton toolbarButton; | |
private ApplicationLauncherButton appLauncherButton; | |
private Tools.DebugLogger log; | |
private System.Diagnostics.Stopwatch updateTimer; | |
#endregion | #endregion |
#region Properties | #region Properties |
public ConnectionStatus currentConnectionStatus | public ConnectionStatus currentConnectionStatus |
{ | { |
get; | get; |
protected set; | private set; |
} | } |
protected string currentConnectionTexture | private string currentConnectionTexture |
{ | { |
get | get |
{ | { |
return this.connectionTextures[this.currentConnectionStatus]; | return this.toolbarTextures[this.currentConnectionStatus]; |
} | |
} | |
private Texture currentAppLauncherTexture | |
{ | |
get | |
{ | |
return this.appLauncherTextures[this.currentConnectionStatus]; | |
} | } |
} | } |
public ControlTypes currentControlLock | public ControlTypes currentControlLock |
{ | { |
get | get |
{ | { |
if (this.lockID == string.Empty) | if (this.lockID == string.Empty) |
{ | { |
return ControlTypes.None; | return ControlTypes.None; |
} | } |
return InputLockManager.GetControlLock(this.lockID); | return InputLockManager.GetControlLock(this.lockID); |
} | } |
} | } |
public string lockID | public string lockID |
{ | { |
get; | get; |
protected set; | private set; |
} | } |
public ControlTypes lockSet | public ControlTypes lockSet |
{ | { |
get | get |
{ | { |
return ControlTypes.ALL_SHIP_CONTROLS; | return ControlTypes.ALL_SHIP_CONTROLS; |
} | } |
} | } |
public Vessel vessel | public Vessel vessel |
{ | { |
get | get |
{ | { |
if (FlightGlobals.ready && FlightGlobals.ActiveVessel != null) | if (FlightGlobals.ready && FlightGlobals.ActiveVessel != null) |
{ | { |
return FlightGlobals.ActiveVessel; | return FlightGlobals.ActiveVessel; |
} | } |
return null; | return null; |
} | } |
} | } |
#endregion | #endregion |
#region MonoBehaviour LifeCycle | #region MonoBehaviour LifeCycle |
protected void Awake() | private void Awake() |
{ | { |
this.lockID = "ARConnectionRequired"; | this.lockID = "ARConnectionRequired"; |
this.log = Tools.DebugLogger.New(this); | |
this.updateTimer = new System.Diagnostics.Stopwatch(); | |
this.toolbarTextures = new Dictionary<ConnectionStatus, string>(); | |
this.toolbarTextures[ConnectionStatus.None] = "AntennaRange/Textures/toolbarIconNoConnection"; | |
this.toolbarTextures[ConnectionStatus.Suboptimal] = "AntennaRange/Textures/toolbarIconSubOptimal"; | |
this.toolbarTextures[ConnectionStatus.Optimal] = "AntennaRange/Textures/toolbarIcon"; | |
this.appLauncherTextures = new Dictionary<ConnectionStatus, Texture>(); | |
this.appLauncherTextures[ConnectionStatus.None] = | |
GameDatabase.Instance.GetTexture("AntennaRange/Textures/appLauncherIconNoConnection", false); | |
this.appLauncherTextures[ConnectionStatus.Suboptimal] = | |
GameDatabase.Instance.GetTexture("AntennaRange/Textures/appLauncherIconSubOptimal", false); | |
this.appLauncherTextures[ConnectionStatus.Optimal] = | |
GameDatabase.Instance.GetTexture("AntennaRange/Textures/appLauncherIcon", false); | |
if (ToolbarManager.ToolbarAvailable) | if (ToolbarManager.ToolbarAvailable) |
{ | { |
this.connectionTextures = new Dictionary<ConnectionStatus, string>(); | |
this.connectionTextures[ConnectionStatus.None] = "AntennaRange/Textures/toolbarIconNoConnection"; | |
this.connectionTextures[ConnectionStatus.Suboptimal] = "AntennaRange/Textures/toolbarIconSubOptimal"; | |
this.connectionTextures[ConnectionStatus.Optimal] = "AntennaRange/Textures/toolbarIcon"; | |
this.toolbarButton = ToolbarManager.Instance.add("AntennaRange", "ARConnectionStatus"); | this.toolbarButton = ToolbarManager.Instance.add("AntennaRange", "ARConnectionStatus"); |
this.toolbarButton.TexturePath = this.connectionTextures[ConnectionStatus.None]; | this.toolbarButton.TexturePath = this.toolbarTextures[ConnectionStatus.None]; |
this.toolbarButton.Text = "AntennaRange"; | this.toolbarButton.Text = "AntennaRange"; |
this.toolbarButton.Visibility = new GameScenesVisibility(GameScenes.FLIGHT); | this.toolbarButton.Visibility = new GameScenesVisibility(GameScenes.FLIGHT); |
this.toolbarButton.Enabled = false; | this.toolbarButton.Enabled = false; |
} | } |
GameEvents.onGameSceneLoadRequested.Add(this.onSceneChangeRequested); | GameEvents.onGameSceneLoadRequested.Add(this.onSceneChangeRequested); |
GameEvents.onVesselChange.Add(this.onVesselChange); | GameEvents.onVesselChange.Add(this.onVesselChange); |
} | } |
protected void FixedUpdate() | private void Start() |
{ | { |
this.mapRenderer = MapView.MapCamera.gameObject.AddComponent<ARMapRenderer>(); | |
} | |
private void FixedUpdate() | |
{ | |
if (this.appLauncherButton == null && !ToolbarManager.ToolbarAvailable && ApplicationLauncher.Ready) | |
{ | |
this.appLauncherButton = ApplicationLauncher.Instance.AddModApplication( | |
ApplicationLauncher.AppScenes.FLIGHT | ApplicationLauncher.AppScenes.MAPVIEW, | |
this.appLauncherTextures[ConnectionStatus.None] | |
); | |
} | |
this.log.Clear(); | |
VesselCommand availableCommand; | |
if (ARConfiguration.RequireConnectionForControl) | |
{ | |
availableCommand = this.vessel.CurrentCommand(); | |
} | |
else | |
{ | |
availableCommand = VesselCommand.Crew; | |
} | |
log.AppendFormat("availableCommand: {0}\n\t" + | |
"(availableCommand & VesselCommand.Crew) == VesselCommand.Crew: {1}\n\t" + | |
"(availableCommand & VesselCommand.Probe) == VesselCommand.Probe: {2}\n\t" + | |
"vessel.HasConnectedRelay(): {3}", | |
(int)availableCommand, | |
(availableCommand & VesselCommand.Crew) == VesselCommand.Crew, | |
(availableCommand & VesselCommand.Probe) == VesselCommand.Probe, | |
vessel.HasConnectedRelay() | |
); | |
// If we are requiring a connection for control, the vessel does not have any adequately staffed pods, | // 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... | // and the vessel does not have any connected relays... |
if ( | if ( |
HighLogic.LoadedSceneIsFlight && | HighLogic.LoadedSceneIsFlight && |
requireConnectionForControl && | ARConfiguration.RequireConnectionForControl && |
this.vessel != null && | this.vessel != null && |
this.vessel.vesselType != VesselType.EVA && | this.vessel.vesselType != VesselType.EVA && |
!this.vessel.hasCrewCommand() && | !( |
!this.vessel.HasConnectedRelay()) | (availableCommand & VesselCommand.Crew) == VesselCommand.Crew || |
(availableCommand & VesselCommand.Probe) == VesselCommand.Probe && vessel.HasConnectedRelay() | |
)) | |
{ | { |
// ...and if the controls are not currently locked... | // ...and if the controls are not currently locked... |
if (currentControlLock == ControlTypes.None) | if (currentControlLock == ControlTypes.None) |
{ | { |
// ...lock the controls. | // ...lock the controls. |
InputLockManager.SetControlLock(this.lockSet, this.lockID); | InputLockManager.SetControlLock(this.lockSet, this.lockID); |
} | } |
} | } |
// ...otherwise, if the controls are locked... | // ...otherwise, if the controls are locked... |
else if (currentControlLock != ControlTypes.None) | else if (currentControlLock != ControlTypes.None) |
{ | { |
// ...unlock the controls. | // ...unlock the controls. |
InputLockManager.RemoveControlLock(this.lockID); | InputLockManager.RemoveControlLock(this.lockID); |
} | } |
if (this.toolbarButton != null && HighLogic.LoadedSceneIsFlight && FlightGlobals.ActiveVessel != null) | log.Print(); |
{ | } |
List<ModuleLimitedDataTransmitter> relays = | |
FlightGlobals.ActiveVessel.getModulesOfType<ModuleLimitedDataTransmitter>(); | private void Update() |
{ | |
bool vesselCanTransmit = false; | if (!this.updateTimer.IsRunning || this.updateTimer.ElapsedMilliseconds > 16L) |
bool vesselHasOptimalRelay = false; | { |
this.updateTimer.Restart(); | |
foreach (ModuleLimitedDataTransmitter relay in relays) | } |
{ | else |
if (!vesselCanTransmit && relay.CanTransmit()) | { |
return; | |
} | |
this.log.Clear(); | |
if (HighLogic.LoadedSceneIsFlight && FlightGlobals.ActiveVessel != null) | |
{ | |
Vessel vessel; | |
IAntennaRelay relay; | |
IList<IAntennaRelay> activeVesselRelays; | |
for (int vIdx = 0; vIdx < FlightGlobals.Vessels.Count; vIdx++) | |
{ | |
vessel = FlightGlobals.Vessels[vIdx]; | |
if (vessel == null || vessel == FlightGlobals.ActiveVessel) | |
{ | { |
vesselCanTransmit = true; | continue; |
} | } |
if (!vesselHasOptimalRelay && relay.transmitDistance <= (double)relay.nominalRange) | log.AppendFormat("Fetching best relay for vessel {0}", vessel); |
relay = vessel.GetBestRelay(); | |
if (relay != null) | |
{ | { |
vesselHasOptimalRelay = true; | log.AppendFormat("Finding nearest relay for best relay {0}", relay); |
relay.FindNearestRelay(); | |
} | } |
} | |
if (vesselCanTransmit && vesselHasOptimalRelay) | |
activeVesselRelays = RelayDatabase.Instance[FlightGlobals.ActiveVessel]; | |
for (int rIdx = 0; rIdx < activeVesselRelays.Count; rIdx++) | |
{ | |
relay = activeVesselRelays[rIdx]; | |
relay.FindNearestRelay(); | |
} | |
if (this.toolbarButton != null || this.appLauncherButton != null) | |
{ | |
log.Append("Checking vessel relay status.\n"); | |
this.currentConnectionStatus = FlightGlobals.ActiveVessel.GetConnectionStatus(); | |
log.AppendFormat("currentConnectionStatus: {0}, setting texture to {1}", | |
this.currentConnectionStatus, this.currentConnectionTexture); | |
if (this.toolbarButton != null) | |
{ | { |
break; | this.toolbarButton.TexturePath = this.currentConnectionTexture; |
if (this.currentConnectionStatus == ConnectionStatus.None) | |
{ | |
if (!this.toolbarButton.Important) this.toolbarButton.Important = true; | |
} | |
else | |
{ | |
if (this.toolbarButton.Important) this.toolbarButton.Important = false; | |
} | |
} | } |
} | if (this.appLauncherButton != null) |
{ | |
if (vesselHasOptimalRelay) | this.appLauncherButton.SetTexture(this.currentAppLauncherTexture); |
{ | } |
this.currentConnectionStatus = ConnectionStatus.Optimal; | } |
} | } |
else if (vesselCanTransmit) | |
{ | log.Print(); |
this.currentConnectionStatus = ConnectionStatus.Suboptimal; | } |
} | |
else | private void OnDestroy() |
{ | |
this.currentConnectionStatus = ConnectionStatus.None; | |
} | |
this.toolbarButton.TexturePath = this.currentConnectionTexture; | |
} | |
} | |
protected void Destroy() | |
{ | { |
InputLockManager.RemoveControlLock(this.lockID); | InputLockManager.RemoveControlLock(this.lockID); |
if (this.mapRenderer != null) | |
{ | |
GameObject.Destroy(this.mapRenderer); | |
} | |
if (this.toolbarButton != null) | if (this.toolbarButton != null) |
{ | { |
this.toolbarButton.Destroy(); | this.toolbarButton.Destroy(); |
} | |
if (this.appLauncherButton != null) | |
{ | |
ApplicationLauncher.Instance.RemoveModApplication(this.appLauncherButton); | |
this.appLauncherButton = null; | |
} | } |
GameEvents.onGameSceneLoadRequested.Remove(this.onSceneChangeRequested); | GameEvents.onGameSceneLoadRequested.Remove(this.onSceneChangeRequested); |
GameEvents.onVesselChange.Remove(this.onVesselChange); | GameEvents.onVesselChange.Remove(this.onVesselChange); |
print("ARFlightController: Destroyed."); | |
} | } |
#endregion | #endregion |
#region Event Handlers | #region Event Handlers |
protected void onSceneChangeRequested(GameScenes scene) | private void onSceneChangeRequested(GameScenes scene) |
{ | { |
if (scene != GameScenes.FLIGHT) | print("ARFlightController: Requesting Destruction."); |
{ | MonoBehaviour.Destroy(this); |
MonoBehaviour.Destroy(this); | } |
} | |
} | private void onVesselChange(Vessel vessel) |
protected void onVesselChange(Vessel vessel) | |
{ | { |
InputLockManager.RemoveControlLock(this.lockID); | InputLockManager.RemoveControlLock(this.lockID); |
} | } |
#endregion | #endregion |
public enum ConnectionStatus | |
{ | |
None, | |
Suboptimal, | |
Optimal | |
} | |
} | } |
} | } |
// AntennaRange | // AntennaRange |
// | // |
// ARMapRenderer.cs | // ARMapRenderer.cs |
// | // |
// Copyright © 2014, toadicus | // Copyright © 2014-2015, toadicus |
// All rights reserved. | // All rights reserved. |
// | // |
// Redistribution and use in source and binary forms, with or without modification, | // Redistribution and use in source and binary forms, with or without modification, |
// are permitted provided that the following conditions are met: | // are permitted provided that the following conditions are met: |
// | // |
// 1. Redistributions of source code must retain the above copyright notice, | // 1. Redistributions of source code must retain the above copyright notice, |
// this list of conditions and the following disclaimer. | // this list of conditions and the following disclaimer. |
// | // |
// 2. Redistributions in binary form must reproduce the above copyright notice, | // 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 | // this list of conditions and the following disclaimer in the documentation and/or other |
// materials provided with the distribution. | // materials provided with the distribution. |
// | // |
// 3. Neither the name of the copyright holder nor the names of its contributors may be used | // 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. | // 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, | // 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 | // 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, | // 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 | // 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, | // 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 | // 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. | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
#pragma warning disable 1591 | |
using KSP; | using KSP; |
using System; | using System; |
using System.Collections.Generic; | using System.Collections.Generic; |
using ToadicusTools; | using ToadicusTools; |
using UnityEngine; | using UnityEngine; |
namespace AntennaRange | namespace AntennaRange |
{ | { |
public class ARMapRenderer : MonoBehaviour | public class ARMapRenderer : MonoBehaviour |
{ | { |
#region Fields | #region Fields |
private Dictionary<Guid, LineRenderer> vesselLineRenderers; | private Dictionary<Guid, LineRenderer> vesselLineRenderers; |
private Dictionary<Guid, bool> vesselFrameCache; | |
// Debug Stuff | |
#pragma warning disable 649 | |
private System.Diagnostics.Stopwatch timer; | |
private Tools.DebugLogger log; | |
private long relayStart; | |
private long start; | |
#pragma warning restore 649 | |
#pragma warning disable 414 | |
private Color thisColor; | |
#pragma warning restore 414 | |
#endregion | #endregion |
#region Properties | #region Properties |
public LineRenderer this[Guid idx] | public LineRenderer this[Guid idx] |
{ | { |
get | get |
{ | { |
if (this.vesselLineRenderers == null) | if (this.vesselLineRenderers == null) |
{ | { |
this.vesselLineRenderers = new Dictionary<Guid, LineRenderer>(); | this.vesselLineRenderers = new Dictionary<Guid, LineRenderer>(); |
} | } |
if (!this.vesselLineRenderers.ContainsKey(idx)) | LineRenderer lr; |
if (this.vesselLineRenderers.TryGetValue(idx, out lr)) | |
{ | |
return lr; | |
} | |
else | |
{ | { |
GameObject obj = new GameObject(); | GameObject obj = new GameObject(); |
obj.layer = 31; | obj.layer = 31; |
LineRenderer lr = obj.AddComponent<LineRenderer>(); | lr = obj.AddComponent<LineRenderer>(); |
lr.SetColors(Color.green, Color.green); | // lr.SetColors(Color.green, Color.green); |
lr.material = new Material(Shader.Find("Particles/Additive")); | lr.material = MapView.OrbitLinesMaterial; |
lr.SetVertexCount(2); | // lr.SetVertexCount(2); |
this.vesselLineRenderers[idx] = lr; | this.vesselLineRenderers[idx] = lr; |
} | |
return lr; | |
return this.vesselLineRenderers[idx]; | } |
} | } |
} | } |
#endregion | #endregion |
#region MonoBehaviour Lifecycle | #region MonoBehaviour Lifecycle |
private void Awake() | private void Awake() |
{ | { |
this.vesselFrameCache = new Dictionary<Guid, bool>(); | if (ARConfiguration.PrettyLines) |
{ | |
this.vesselLineRenderers = new Dictionary<Guid, LineRenderer>(); | |
} | |
#if DEBUG | |
this.timer = new System.Diagnostics.Stopwatch(); | |
this.log = Tools.DebugLogger.New(this); | |
#endif | |
} | } |
private void OnPreCull() | private void OnPreCull() |
{ | { |
if (!HighLogic.LoadedSceneIsFlight || !MapView.MapIsEnabled) | if (!HighLogic.LoadedSceneIsFlight || !MapView.MapIsEnabled || !ARConfiguration.PrettyLines) |
{ | { |
this.Cleanup(); | |
return; | return; |
} | } |
Tools.DebugLogger log = Tools.DebugLogger.New(this); | #if DEBUG |
timer.Restart(); | |
#endif | |
try | try |
{ | { |
log.Clear(); | |
log.AppendFormat("OnPreCull.\n"); | log.AppendFormat("OnPreCull.\n"); |
double sma; | |
switch (MapView.MapCamera.target.type) | |
{ | |
case MapObject.MapObjectType.CELESTIALBODY: | |
sma = MapView.MapCamera.target.celestialBody.orbit.semiMajorAxis; | |
break; | |
case MapObject.MapObjectType.VESSEL: | |
sma = MapView.MapCamera.target.vessel.orbit.semiMajorAxis; | |
break; | |
default: | |
sma = ScaledSpace.ScaleFactor; | |
break; | |
} | |
log.AppendFormat("\tMapView: Draw3DLines: {0}\n" + | log.AppendFormat("\tMapView: Draw3DLines: {0}\n" + |
"\tMapView.MapCamera.camera.fieldOfView: {1}\n" + | "\tMapView.MapCamera.camera.fieldOfView: {1}\n" + |
"\tMapView.MapCamera.Distance: {2}\n" + | "\tMapView.MapCamera.Distance: {2}\n", |
"\tDistanceVSSMAFactor: {3}\n", | |
MapView.Draw3DLines, | MapView.Draw3DLines, |
MapView.MapCamera.camera.fieldOfView, | MapView.MapCamera.camera.fieldOfView, |
MapView.MapCamera.Distance, | MapView.MapCamera.Distance |
(float)((double)MapView.MapCamera.Distance / Math.Abs(sma) * ScaledSpace.InverseScaleFactor) | |
); | ); |
this.vesselFrameCache.Clear(); | |
log.AppendLine("vesselFrameCache cleared."); | |
if (FlightGlobals.ready && FlightGlobals.Vessels != null) | if (FlightGlobals.ready && FlightGlobals.Vessels != null) |
{ | { |
log.AppendLine("FlightGlobals ready and Vessels list not null."); | log.AppendLine("FlightGlobals ready and Vessels list not null."); |
foreach (Vessel vessel in FlightGlobals.Vessels) | for (int i = 0; i < FlightGlobals.Vessels.Count; i++) |
{ | { |
Vessel vessel = FlightGlobals.Vessels[i]; | |
log.AppendFormat("\nStarting check for vessel {0} at {1}ms", vessel, timer.ElapsedMilliseconds); | |
if (vessel == null) | if (vessel == null) |
{ | { |
log.AppendFormat("Skipping vessel {0} altogether because it is null.\n"); | log.AppendFormat("\n\tSkipping vessel {0} altogether because it is null.", vessel); |
continue; | continue; |
} | } |
log.AppendFormat("Checking vessel {0}.\n", vessel.vesselName); | |
switch (vessel.vesselType) | switch (vessel.vesselType) |
{ | { |
case VesselType.Debris: | case VesselType.Debris: |
case VesselType.EVA: | case VesselType.EVA: |
case VesselType.Unknown: | case VesselType.Unknown: |
case VesselType.SpaceObject: | case VesselType.SpaceObject: |
log.AppendFormat("\tDiscarded because vessel is of invalid type {0}\n", | log.AppendFormat("\n\tDiscarded because vessel is of invalid type {0}", |
vessel.vesselType); | vessel.vesselType); |
continue; | continue; |
} | } |
if (vessel.HasConnectedRelay()) | log.AppendFormat("\n\tChecking vessel {0}.", vessel.vesselName); |
#if DEBUG | |
start = timer.ElapsedMilliseconds; | |
#endif | |
IAntennaRelay vesselRelay = vessel.GetBestRelay(); | |
if (vesselRelay == null) | |
{ | { |
log.AppendLine("\tHas a connection, checking for the best relay to use for the line."); | log.AppendFormat("\n\tGot null relay for vessel {0}", vessel.vesselName); |
continue; | |
IAntennaRelay vesselRelay = null; | |
float bestScore = float.PositiveInfinity; | |
float relayScore = float.NaN; | |
foreach (IAntennaRelay relay in RelayDatabase.Instance[vessel].Values) | |
{ | |
relayScore = (float)relay.transmitDistance / relay.maxTransmitDistance; | |
if (relayScore < bestScore) | |
{ | |
bestScore = relayScore; | |
vesselRelay = relay as IAntennaRelay; | |
} | |
} | |
if (vesselRelay != null) | |
{ | |
log.AppendFormat("\t...picked relay {0} with a score of {1}", | |
vesselRelay, relayScore | |
); | |
this.SetRelayVertices(vesselRelay); | |
} | |
} | } |
else if (this.vesselLineRenderers.ContainsKey(vessel.id)) | |
log.AppendFormat("\n\tGot best relay {0} ({3}) for vessel {1} in {2} ms", | |
vesselRelay, vessel, timer.ElapsedMilliseconds - start, vesselRelay.GetType().Name); | |
if (vesselRelay != null) | |
{ | { |
log.AppendLine("\tDisabling line because vessel has no connection."); | #if DEBUG |
this[vessel.id].enabled = false; | start = timer.ElapsedMilliseconds; |
#endif | |
this.SetRelayVertices(vesselRelay); | |
log.AppendFormat("\n\tSet relay vertices for {0} in {1}ms", | |
vessel, timer.ElapsedMilliseconds - start); | |
} | } |
} | } |
} | } |
} | } |
catch (Exception ex) | |
{ | |
this.LogError("Caught {0}: {1}\n{2}\n", ex.GetType().Name, ex.ToString(), ex.StackTrace.ToString()); | |
this.Cleanup(); | |
} | |
#if DEBUG | |
finally | finally |
{ | { |
log.AppendFormat("\n\tOnPreCull finished in {0}ms\n", timer.ElapsedMilliseconds); | |
log.Print(); | log.Print(); |
} | } |
} | #endif |
} | |
private void Destroy() | |
{ | private void OnDestroy() |
this.vesselLineRenderers.Clear(); | { |
this.vesselLineRenderers = null; | this.Cleanup(); |
this.Log("Destroyed"); | |
} | } |
#endregion | #endregion |
#region Utility | |
private void SetRelayVertices(IAntennaRelay relay) | private void SetRelayVertices(IAntennaRelay relay) |
{ | { |
do | log.AppendFormat("\n\t\tDrawing line for relay chain starting at {0}.", relay); |
{ | |
if (this.vesselFrameCache.ContainsKey(relay.vessel.id)) | if (relay.vessel == null) |
{ | { |
break; | log.Append("\n\t\tvessel is null, bailing out"); |
} | return; |
} | |
LineRenderer renderer = this[relay.vessel.id]; | |
LineRenderer renderer = this[relay.vessel.id]; | |
if (relay.CanTransmit()) | Vector3d start = ScaledSpace.LocalToScaledSpace(relay.vessel.GetWorldPos3D()); |
{ | |
Vector3d start; | float lineWidth; |
Vector3d end; | float d = Screen.height / 2f + 0.01f; |
renderer.enabled = true; | if (MapView.Draw3DLines) |
{ | |
start = ScaledSpace.LocalToScaledSpace(relay.vessel.GetWorldPos3D()); | lineWidth = 0.005859375f * MapView.MapCamera.Distance; |
} | |
if (relay.nearestRelay == null) | else |
{ | { |
end = ScaledSpace.LocalToScaledSpace(AntennaRelay.Kerbin.position); | lineWidth = 2f; |
} | |
else | start = MapView.MapCamera.camera.WorldToScreenPoint(start); |
{ | |
end = ScaledSpace.LocalToScaledSpace(relay.nearestRelay.vessel.GetWorldPos3D()); | start.z = start.z >= 0f ? d : -d; |
} | } |
float lineWidth; | renderer.SetWidth(lineWidth, lineWidth); |
if (MapView.Draw3DLines) | renderer.SetPosition(0, start); |
{ | |
lineWidth = 0.002f * MapView.MapCamera.Distance; | int idx = 0; |
} | |
else | #if DEBUG |
{ | relayStart = timer.ElapsedMilliseconds; |
lineWidth = 1f; | #endif |
start = MapView.MapCamera.camera.WorldToScreenPoint(start); | Vector3d nextPoint; |
end = MapView.MapCamera.camera.WorldToScreenPoint(end); | |
renderer.enabled = true; | |
float d = Screen.height / 2f + 0.01f; | |
start.z = start.z >= 0.15f ? d : -d; | if (!relay.CanTransmit()) |
end.z = end.z >= 0.15f ? d : -d; | { |
} | thisColor = Color.red; |
} | |
renderer.SetWidth(lineWidth, lineWidth); | else |
{ | |
renderer.SetPosition(0, start); | if (relay.transmitDistance < relay.nominalTransmitDistance) |
renderer.SetPosition(1, end); | { |
thisColor = Color.green; | |
this.vesselFrameCache[relay.vessel.id] = true; | } |
else | |
relay = relay.nearestRelay; | { |
} | thisColor = Color.yellow; |
} | } |
while (relay != null); | } |
} | |
if (relay.KerbinDirect) | |
{ | |
nextPoint = ScaledSpace.LocalToScaledSpace(AntennaRelay.Kerbin.position); | |
} | |
else | |
{ | |
if (relay.targetRelay == null || relay.targetRelay.vessel == null) | |
{ | |
this.LogError( | |
"SetRelayVertices: relay {0} has null target relay or vessel when not KerbinDirect, bailing out!", | |
relay | |
); | |
renderer.enabled = false; | |
return; | |
} | |
nextPoint = ScaledSpace.LocalToScaledSpace(relay.targetRelay.vessel.GetWorldPos3D()); | |
} | |
renderer.SetColors(thisColor, thisColor); | |
if (!MapView.Draw3DLines) | |
{ | |
nextPoint = MapView.MapCamera.camera.WorldToScreenPoint(nextPoint); | |
nextPoint.z = nextPoint.z >= 0f ? d : -d; | |
} | |
idx++; | |
renderer.SetVertexCount(idx + 1); | |
renderer.SetPosition(idx, nextPoint); | |
log.AppendFormat("\n\t\t\t...finished segment in {0} ms", timer.ElapsedMilliseconds - relayStart); | |
} | |
private void Cleanup() | |
{ | |
if (this.vesselLineRenderers != null && this.vesselLineRenderers.Count > 0) | |
{ | |
IEnumerator<LineRenderer> enumerator = this.vesselLineRenderers.Values.GetEnumerator(); | |
LineRenderer lineRenderer; | |
while (enumerator.MoveNext()) | |
{ | |
lineRenderer = enumerator.Current; | |
lineRenderer.enabled = false; | |
GameObject.Destroy(lineRenderer.gameObject); | |
} | |
this.vesselLineRenderers.Clear(); | |
} | |
} | |
#endregion | |
} | } |
} | } |
// 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 | |
} | |
} | |
<?xml version="1.0" encoding="utf-8"?> | <?xml version="1.0" encoding="utf-8"?> |
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
<PropertyGroup> | <PropertyGroup> |
<Configuration Condition=" '$(Configuration)' == '' ">Debug_win</Configuration> | <Configuration Condition=" '$(Configuration)' == '' ">Debug_win</Configuration> |
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> | <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> |
<ProductVersion>8.0.30703</ProductVersion> | <ProductVersion>8.0.30703</ProductVersion> |
<SchemaVersion>2.0</SchemaVersion> | <SchemaVersion>2.0</SchemaVersion> |
<ProjectGuid>{B36F2C11-962E-4A75-9F41-61AD56D11493}</ProjectGuid> | <ProjectGuid>{B36F2C11-962E-4A75-9F41-61AD56D11493}</ProjectGuid> |
<OutputType>Library</OutputType> | <OutputType>Library</OutputType> |
<RootNamespace>AntennaRange</RootNamespace> | <RootNamespace>AntennaRange</RootNamespace> |
<AssemblyName>AntennaRange</AssemblyName> | <AssemblyName>AntennaRange</AssemblyName> |
<ReleaseVersion>1.1</ReleaseVersion> | <ReleaseVersion>1.3</ReleaseVersion> |
<SynchReleaseVersion>false</SynchReleaseVersion> | <SynchReleaseVersion>false</SynchReleaseVersion> |
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion> | <TargetFrameworkVersion>v3.5</TargetFrameworkVersion> |
<UseMSBuildEngine>False</UseMSBuildEngine> | <UseMSBuildEngine>False</UseMSBuildEngine> |
</PropertyGroup> | </PropertyGroup> |
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug_win|AnyCPU' "> | <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug_win|AnyCPU' "> |
<DebugSymbols>true</DebugSymbols> | <DebugSymbols>true</DebugSymbols> |
<DebugType>full</DebugType> | <DebugType>full</DebugType> |
<Optimize>false</Optimize> | <Optimize>false</Optimize> |
<OutputPath>bin\Debug</OutputPath> | <OutputPath>bin\Debug</OutputPath> |
<DefineConstants>DEBUG;TRACE;</DefineConstants> | <DefineConstants>DEBUG;TRACE;</DefineConstants> |
<ErrorReport>prompt</ErrorReport> | <ErrorReport>prompt</ErrorReport> |
<WarningLevel>4</WarningLevel> | <WarningLevel>4</WarningLevel> |
<ConsolePause>false</ConsolePause> | <ConsolePause>false</ConsolePause> |
<CustomCommands> | <CustomCommands> |
<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} ${ProjectDir}\GameData\AntennaRange\" /> |
<Command type="AfterBuild" command="xcopy /y ${TargetFile} C:\Users\andy\Games\KSP_win\GameData\AntennaRange\" /> | |
</CustomCommands> | </CustomCommands> |
</CustomCommands> | </CustomCommands> |
</PropertyGroup> | </PropertyGroup> |
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release_win|AnyCPU' "> | <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release_win|AnyCPU' "> |
<Optimize>true</Optimize> | <Optimize>true</Optimize> |
<OutputPath>bin\Release</OutputPath> | <OutputPath>bin\Release</OutputPath> |
<ErrorReport>prompt</ErrorReport> | <ErrorReport>prompt</ErrorReport> |
<WarningLevel>4</WarningLevel> | <WarningLevel>4</WarningLevel> |
<ConsolePause>false</ConsolePause> | <ConsolePause>false</ConsolePause> |
<CustomCommands> | <CustomCommands> |
<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} ${ProjectDir}\GameData\AntennaRange\" /> |
<Command type="AfterBuild" command="xcopy /y ${TargetFile} C:\Users\andy\Games\KSP_win\GameData\AntennaRange\" /> | |
</CustomCommands> | </CustomCommands> |
</CustomCommands> | </CustomCommands> |
<DocumentationFile>bin\Release\AntennaRange.xml</DocumentationFile> | |
</PropertyGroup> | </PropertyGroup> |
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug_linux|AnyCPU' "> | <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug_linux|AnyCPU' "> |
<DebugSymbols>true</DebugSymbols> | <DebugSymbols>true</DebugSymbols> |
<DebugType>full</DebugType> | <DebugType>full</DebugType> |
<Optimize>false</Optimize> | <Optimize>false</Optimize> |
<OutputPath>bin\Debug</OutputPath> | <OutputPath>bin\Debug</OutputPath> |
<DefineConstants>DEBUG;TRACE;</DefineConstants> | <DefineConstants>DEBUG;TRACE;</DefineConstants> |
<ErrorReport>prompt</ErrorReport> | <ErrorReport>prompt</ErrorReport> |
<WarningLevel>4</WarningLevel> | <WarningLevel>4</WarningLevel> |
<ConsolePause>false</ConsolePause> | <ConsolePause>false</ConsolePause> |
<CustomCommands> | <CustomCommands> |
<CustomCommands> | <CustomCommands> |
<Command type="AfterBuild" command="cp -afv ${TargetFile} ${ProjectDir}/${ProjectName}.cfg /opt/games/KSP_linux/GameData/${ProjectName}/" /> | <Command type="AfterBuild" command="cp -afv ${TargetFile} ${ProjectDir}/GameData/${ProjectName}/" /> |
</CustomCommands> | </CustomCommands> |
</CustomCommands> | </CustomCommands> |
</PropertyGroup> | </PropertyGroup> |
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release_linux|AnyCPU' "> | <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release_linux|AnyCPU' "> |
<Optimize>true</Optimize> | <Optimize>true</Optimize> |
<OutputPath>bin\Release</OutputPath> | <OutputPath>bin\Release</OutputPath> |
<ErrorReport>prompt</ErrorReport> | <ErrorReport>prompt</ErrorReport> |
<WarningLevel>4</WarningLevel> | <WarningLevel>4</WarningLevel> |
<CustomCommands> | <CustomCommands> |
<CustomCommands> | <CustomCommands> |
<Command type="AfterBuild" command="cp -afv ${TargetFile} ${ProjectDir}/${ProjectName}.cfg /opt/games/KSP_linux/GameData/${ProjectName}/" /> | <Command type="AfterBuild" command="cp -afv ${TargetFile} ${ProjectDir}/GameData/${ProjectName}/" /> |
</CustomCommands> | </CustomCommands> |
</CustomCommands> | </CustomCommands> |
<ConsolePause>false</ConsolePause> | <ConsolePause>false</ConsolePause> |
</PropertyGroup> | </PropertyGroup> |
<ItemGroup> | <ItemGroup> |
<Compile Include="Properties\AssemblyInfo.cs" /> | <Compile Include="Properties\AssemblyInfo.cs" /> |
<Compile Include="IAntennaRelay.cs" /> | <Compile Include="IAntennaRelay.cs" /> |
<Compile Include="ModuleLimitedDataTransmitter.cs" /> | <Compile Include="ModuleLimitedDataTransmitter.cs" /> |
<Compile Include="AntennaRelay.cs" /> | <Compile Include="AntennaRelay.cs" /> |
<Compile Include="ProtoAntennaRelay.cs" /> | <Compile Include="ProtoAntennaRelay.cs" /> |
<Compile Include="RelayDatabase.cs" /> | <Compile Include="RelayDatabase.cs" /> |
<Compile Include="RelayExtensions.cs" /> | <Compile Include="RelayExtensions.cs" /> |
<Compile Include="ARConfiguration.cs" /> | <Compile Include="ARConfiguration.cs" /> |
<Compile Include="ARFlightController.cs" /> | <Compile Include="ARFlightController.cs" /> |
<Compile Include="ARMapRenderer.cs" /> | <Compile Include="ARMapRenderer.cs" /> |
</ItemGroup> | </ItemGroup> |
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> | <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> |
<ItemGroup> | |
<None Include="AntennaRange.cfg"> | |
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | |
</None> | |
</ItemGroup> | |
<ItemGroup> | <ItemGroup> |
<Reference Include="Assembly-CSharp"> | <Reference Include="Assembly-CSharp"> |
<HintPath>..\_KSPAssemblies\Assembly-CSharp.dll</HintPath> | <HintPath>..\_KSPAssemblies\Assembly-CSharp.dll</HintPath> |
<Private>False</Private> | <Private>False</Private> |
</Reference> | </Reference> |
<Reference Include="System"> | <Reference Include="System"> |
<HintPath>..\_KSPAssemblies\System.dll</HintPath> | <HintPath>..\_KSPAssemblies\System.dll</HintPath> |
<Private>False</Private> | <Private>False</Private> |
</Reference> | </Reference> |
<Reference Include="UnityEngine"> | <Reference Include="UnityEngine"> |
<HintPath>..\_KSPAssemblies\UnityEngine.dll</HintPath> | <HintPath>..\_KSPAssemblies\UnityEngine.dll</HintPath> |
<Private>False</Private> | <Private>False</Private> |
</Reference> | </Reference> |
</ItemGroup> | </ItemGroup> |
<ItemGroup> | <ItemGroup> |
<ProjectReference Include="..\ToadicusTools\ToadicusTools.csproj"> | <ProjectReference Include="..\ToadicusTools\ToadicusTools.csproj"> |
<Project>{D48A5542-6655-4149-BC27-B27DF0466F1C}</Project> | <Project>{D48A5542-6655-4149-BC27-B27DF0466F1C}</Project> |
<Name>ToadicusTools</Name> | <Name>ToadicusTools</Name> |
</ProjectReference> | </ProjectReference> |
</ItemGroup> | </ItemGroup> |
<ItemGroup> | |
<None Include="GameData\AntennaRange\AntennaRange.cfg" /> | |
<None Include="GameData\AntennaRange\ATM_AntennaRange.cfg" /> | |
</ItemGroup> | |
</Project> | </Project> |
// AntennaRange | // AntennaRange |
// | // |
// AntennaRelay.cs | // AntennaRelay.cs |
// | // |
// Copyright © 2014, toadicus | // Copyright © 2014-2015, toadicus |
// All rights reserved. | // All rights reserved. |
// | // |
// Redistribution and use in source and binary forms, with or without modification, | // Redistribution and use in source and binary forms, with or without modification, |
// are permitted provided that the following conditions are met: | // are permitted provided that the following conditions are met: |
// | // |
// 1. Redistributions of source code must retain the above copyright notice, | // 1. Redistributions of source code must retain the above copyright notice, |
// this list of conditions and the following disclaimer. | // this list of conditions and the following disclaimer. |
// | // |
// 2. Redistributions in binary form must reproduce the above copyright notice, | // 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 | // this list of conditions and the following disclaimer in the documentation and/or other |
// materials provided with the distribution. | // materials provided with the distribution. |
// | // |
// 3. Neither the name of the copyright holder nor the names of its contributors may be used | // 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. | // 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, | // 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 | // 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, | // 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 | // 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, | // 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 | // 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. | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
using System; | using System; |
using System.Collections.Generic; | using System.Collections.Generic; |
using System.Linq; | |
using ToadicusTools; | using ToadicusTools; |
namespace AntennaRange | namespace AntennaRange |
{ | { |
/// <summary> | |
/// Relay code at the heart of AntennaRange | |
/// </summary> | |
public class AntennaRelay | public class AntennaRelay |
{ | { |
public static bool requireLineOfSight; | |
// We don't have a Bard, so we'll hide Kerbin here. | // We don't have a Bard, so we'll hide Kerbin here. |
public static CelestialBody Kerbin; | private static CelestialBody _Kerbin; |
protected CelestialBody _firstOccludingBody; | /// <summary> |
/// Fetches, caches, and returns a <see cref="CelestialBody"/> reference to Kerbin | |
protected IAntennaRelay _nearestRelayCache; | /// </summary> |
public static CelestialBody Kerbin | |
{ | |
get | |
{ | |
if (_Kerbin == null && FlightGlobals.ready) | |
{ | |
_Kerbin = FlightGlobals.GetHomeBody(); | |
} | |
return _Kerbin; | |
} | |
} | |
private bool canTransmit; | |
private bool isChecked; | |
private IAntennaRelay nearestRelay; | |
private IAntennaRelay bestOccludedRelay; | |
/// <summary> | |
/// The <see cref="AntennaRange.ModuleLimitedDataTransmitter"/> reference underlying this AntennaRelay, as an | |
/// <see cref="AntennaRange.IAntennaRelay"/> | |
/// </summary> | |
protected IAntennaRelay moduleRef; | protected IAntennaRelay moduleRef; |
protected System.Diagnostics.Stopwatch searchTimer; | |
protected long millisecondsBetweenSearches; | |
/// <summary> | /// <summary> |
/// Gets the parent Vessel. | /// Gets the parent Vessel. |
/// </summary> | /// </summary> |
/// <value>The parent Vessel.</value> | /// <value>The parent Vessel.</value> |
public virtual Vessel vessel | public virtual Vessel vessel |
{ | { |
get | get |
{ | { |
return this.moduleRef.vessel; | return this.moduleRef.vessel; |
} | } |
} | } |
/// <summary> | /// <summary> |
/// Gets or sets the nearest relay. | /// Gets the target <see cref="AntennaRange.IAntennaRelay"/>relay. |
/// </summary> | /// </summary> |
/// <value>The nearest relay</value> | public IAntennaRelay targetRelay |
public IAntennaRelay nearestRelay | { |
{ | get; |
get | protected set; |
{ | } |
if (this.searchTimer.IsRunning && | |
this.searchTimer.ElapsedMilliseconds > this.millisecondsBetweenSearches) | /// <summary> |
{ | /// Gets the first <see cref="CelestialBody"/> found to be blocking line of sight. |
this._nearestRelayCache = this.FindNearestRelay(); | /// </summary> |
this.searchTimer.Restart(); | public virtual CelestialBody firstOccludingBody |
} | { |
get; | |
return this._nearestRelayCache; | protected set; |
} | |
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> | /// <summary> |
/// Gets the transmit distance. | /// Gets the transmit distance. |
/// </summary> | /// </summary> |
/// <value>The transmit distance.</value> | /// <value>The transmit distance.</value> |
public double transmitDistance | public double transmitDistance |
{ | { |
get | get |
{ | { |
this.nearestRelay = this.FindNearestRelay(); | if (this.KerbinDirect || this.targetRelay == null) |
{ | |
// If there is no available relay nearby... | |
if (this.nearestRelay == null) | |
{ | |
// .. return the distance to Kerbin | |
return this.DistanceTo(Kerbin); | return this.DistanceTo(Kerbin); |
} | } |
else | else |
{ | { |
/// ...otherwise, return the distance to the nearest available relay. | return this.DistanceTo(this.targetRelay); |
return this.DistanceTo(nearestRelay); | } |
} | } |
} | } |
} | |
/// <summary> | |
/// <summary> | /// Gets the nominal transmit distance at which the Antenna behaves just as prescribed by Squad's config. |
/// The maximum distance at which this relay can operate. | /// </summary> |
/// </summary> | public virtual double nominalTransmitDistance |
/// <value>The max transmit distance.</value> | |
public virtual float maxTransmitDistance | |
{ | { |
get; | get; |
set; | set; |
} | } |
/// <summary> | /// <summary> |
/// Gets a value indicating whether this <see cref="AntennaRange.ProtoDataTransmitter"/> has been checked during | /// The maximum distance at which this relay can operate. |
/// the current relay attempt. | /// </summary> |
/// </summary> | /// <value>The max transmit distance.</value> |
/// <value><c>true</c> if relay checked; otherwise, <c>false</c>.</value> | public virtual double maxTransmitDistance |
public virtual bool relayChecked | { |
get; | |
set; | |
} | |
/// <summary> | |
/// Gets a value indicating whether this <see cref="AntennaRange.IAntennaRelay"/> Relay is communicating | |
/// directly with Kerbin. | |
/// </summary> | |
public virtual bool KerbinDirect | |
{ | { |
get; | get; |
protected set; | protected set; |
} | } |
/// <summary> | /// <summary> |
/// Determines whether this instance can transmit. | /// Determines whether this instance can transmit. |
/// </summary> | /// </summary> |
/// <returns><c>true</c> if this instance can transmit; otherwise, <c>false</c>.</returns> | /// <returns><c>true</c> if this instance can transmit; otherwise, <c>false</c>.</returns> |
public virtual bool CanTransmit() | public virtual bool CanTransmit() |
{ | { |
if ( | return this.canTransmit; |
this.transmitDistance > this.maxTransmitDistance || | |
( | |
requireLineOfSight && | |
this.nearestRelay == null && | |
!this.vessel.hasLineOfSightTo(Kerbin, out this._firstOccludingBody) | |
) | |
) | |
{ | |
return false; | |
} | |
else | |
{ | |
return true; | |
} | |
} | } |
/// <summary> | /// <summary> |
/// Finds the nearest relay. | /// Finds the nearest relay. |
/// </summary> | /// </summary> |
/// <returns>The nearest relay or null, if no relays in range.</returns> | /// <returns>The nearest relay or null, if no relays in range.</returns> |
public IAntennaRelay FindNearestRelay() | public void FindNearestRelay() |
{ | { |
if (this.searchTimer.IsRunning && this.searchTimer.ElapsedMilliseconds < this.millisecondsBetweenSearches) | if (!FlightGlobals.ready) |
{ | { |
return this.nearestRelay; | return; |
} | } |
if (this.searchTimer.IsRunning) | Tools.DebugLogger log; |
{ | #if DEBUG |
this.searchTimer.Stop(); | log = Tools.DebugLogger.New(this); |
this.searchTimer.Reset(); | #endif |
} | |
// Skip vessels that have already been checked for a nearest relay this pass. | |
this.searchTimer.Start(); | if (this.isChecked) |
{ | |
Tools.PostDebugMessage(string.Format( | log.AppendFormat("{0}: Target search skipped because our vessel has been checked already this search.", |
"{0}: finding nearest relay for {1} ({2})", | this); |
this.GetType().Name, | log.Print(); |
this, | return; |
this.vessel.id | } |
)); | |
log.AppendFormat("{0}: Target search started).", this.ToString()); | |
this._firstOccludingBody = null; | |
#if DEBUG | |
try { | |
#endif | |
// Set this vessel as checked, so that we don't check it again. | // Set this vessel as checked, so that we don't check it again. |
RelayDatabase.Instance.CheckedVesselsTable[vessel.id] = true; | this.isChecked = true; |
double nearestSqrDistance = double.PositiveInfinity; | // Blank everything we're trying to find before the search. |
IAntennaRelay _nearestRelay = null; | this.firstOccludingBody = null; |
this.bestOccludedRelay = null; | |
this.targetRelay = null; | |
this.nearestRelay = null; | |
// Default to KerbinDirect = true in case something in here doesn't work right. | |
this.KerbinDirect = true; | |
CelestialBody bodyOccludingBestOccludedRelay = null; | |
IAntennaRelay needle; | |
double nearestRelaySqrDistance = double.PositiveInfinity; | |
double bestOccludedSqrDistance = double.PositiveInfinity; | |
double maxTransmitSqrDistance = this.maxTransmitDistance * this.maxTransmitDistance; | |
/* | /* |
* Loop through all the vessels and exclude this vessel, vessels of the wrong type, and vessels that are too | * 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 | * 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 | * and that can transmit. Once we find a suitable candidate, assign it to nearestRelay for comparison |
* against future finds. | * against future finds. |
* */ | * */ |
foreach (Vessel potentialVessel in FlightGlobals.Vessels) | Vessel potentialVessel; |
{ | IAntennaRelay potentialBestRelay; |
// Skip vessels that have already been checked for a nearest relay this pass. | CelestialBody fob; |
if (RelayDatabase.Instance.CheckedVesselsTable.ContainsKey(potentialVessel.id)) | |
{ | // IList<IAntennaRelay> vesselRelays; |
continue; | for (int vIdx = 0; vIdx < FlightGlobals.Vessels.Count; vIdx++) |
} | { |
log.AppendFormat("\nFetching vessel at index {0}", vIdx); | |
potentialVessel = FlightGlobals.Vessels[vIdx]; | |
if (potentialVessel == null) | |
{ | |
Tools.PostErrorMessage("{0}: Skipping vessel at index {1} because it is null.", this, vIdx); | |
log.AppendFormat("\n\tSkipping vessel at index {0} because it is null.", vIdx); | |
log.Print(); | |
return; | |
} | |
#if DEBUG | |
else | |
{ | |
log.AppendFormat("\n\tGot vessel {0}", potentialVessel); | |
} | |
#endif | |
// Skip vessels of the wrong type. | // Skip vessels of the wrong type. |
log.Append("\n\tchecking vessel type"); | |
switch (potentialVessel.vesselType) | switch (potentialVessel.vesselType) |
{ | { |
case VesselType.Debris: | case VesselType.Debris: |
case VesselType.Flag: | case VesselType.Flag: |
case VesselType.EVA: | case VesselType.EVA: |
case VesselType.SpaceObject: | case VesselType.SpaceObject: |
case VesselType.Unknown: | case VesselType.Unknown: |
log.Append("\n\tSkipping because vessel is the wrong type."); | |
continue; | continue; |
default: | default: |
break; | break; |
} | } |
log.Append("\n\tchecking if vessel is this vessel"); | |
// Skip vessels with the wrong ID | // Skip vessels with the wrong ID |
if (potentialVessel.id == vessel.id) | if (potentialVessel.id == vessel.id) |
{ | { |
log.Append("\n\tSkipping because vessel is this vessel."); | |
continue; | continue; |
} | } |
// Find the distance from here to the vessel... | |
log.Append("\n\tgetting distance to potential vessel"); | |
double potentialSqrDistance = this.sqrDistanceTo(potentialVessel); | |
log.Append("\n\tgetting best vessel relay"); | |
potentialBestRelay = potentialVessel.GetBestRelay(); | |
log.AppendFormat("\n\t\tgot best vessel relay {0}", | |
potentialBestRelay == null ? "null" : potentialBestRelay.ToString()); | |
if (potentialBestRelay == null) | |
{ | |
log.Append("\n\t\t...skipping null relay"); | |
continue; | |
} | |
log.Append("\n\t\tdoing LOS check"); | |
// Skip vessels to which we do not have line of sight. | // Skip vessels to which we do not have line of sight. |
if (requireLineOfSight && !this.vessel.hasLineOfSightTo(potentialVessel, out this._firstOccludingBody)) | if ( |
{ | ARConfiguration.RequireLineOfSight && |
Tools.PostDebugMessage( | !this.vessel.hasLineOfSightTo(potentialVessel, out fob, ARConfiguration.RadiusRatio) |
this, | ) |
"Vessel {0} discarded because we do not have line of sight.", | { |
log.Append("\n\t\t...failed LOS check"); | |
log.AppendFormat("\n\t\t\t{0}: Vessel {1} not in line of sight.", | |
this.ToString(), potentialVessel.vesselName); | |
log.AppendFormat("\n\t\t\tpotentialSqrDistance: {0}", potentialSqrDistance); | |
log.AppendFormat("\n\t\t\tbestOccludedSqrDistance: {0}", bestOccludedSqrDistance); | |
log.AppendFormat("\n\t\t\tmaxTransmitSqrDistance: {0}", maxTransmitSqrDistance); | |
if ( | |
(potentialSqrDistance < bestOccludedSqrDistance) && | |
(potentialSqrDistance < maxTransmitSqrDistance) && | |
potentialBestRelay.CanTransmit() | |
) | |
{ | |
log.Append("\n\t\t...vessel is close enough to and potentialBestRelay can transmit"); | |
log.AppendFormat("\n\t\t...{0} found new best occluded relay {1}", this, potentialBestRelay); | |
this.bestOccludedRelay = potentialBestRelay; | |
bodyOccludingBestOccludedRelay = fob; | |
bestOccludedSqrDistance = potentialSqrDistance; | |
} | |
else | |
{ | |
log.Append("\n\t\t...vessel is not close enough to check for occluded relays, carrying on"); | |
} | |
continue; | |
} | |
log.Append("\n\t\t...passed LOS check"); | |
/* | |
* ...so that we can skip the vessel if it is further away than a vessel we've already checked. | |
* */ | |
if (potentialSqrDistance > nearestRelaySqrDistance) | |
{ | |
log.AppendFormat("\n\t{0}: Vessel {1} discarded because it is farther than another the nearest relay.", | |
this.ToString(), | |
potentialVessel.vesselName | potentialVessel.vesselName |
); | ); |
continue; | continue; |
} | } |
// Find the distance from here to the vessel... | log.Append("\n\t\t...passed distance check"); |
double potentialSqrDistance = (potentialVessel.GetWorldPos3D() - vessel.GetWorldPos3D()).sqrMagnitude; | |
if (potentialBestRelay.CanTransmit()) | |
/* | { |
* ...so that we can skip the vessel if it is further away than Kerbin, our transmit distance, or a | needle = potentialBestRelay; |
* vessel we've already checked. | bool isCircular = false; |
* */ | |
if ( | while (needle != null) |
potentialSqrDistance > Tools.Min( | { |
this.maxTransmitDistance * this.maxTransmitDistance, | if (needle.KerbinDirect) |
nearestSqrDistance, | { |
this.vessel.sqrDistanceTo(Kerbin) | break; |
) | } |
) | |
{ | if (needle.targetRelay == null) |
Tools.PostDebugMessage( | { |
this, | break; |
"Vessel {0} discarded because it is out of range, or farther than another relay.", | } |
potentialVessel.vesselName | |
); | if (needle.targetRelay.vessel == this.vessel || needle == this.moduleRef) |
continue; | { |
} | isCircular = true; |
break; | |
nearestSqrDistance = potentialSqrDistance; | } |
foreach (IAntennaRelay potentialRelay in potentialVessel.GetAntennaRelays()) | needle = needle.targetRelay; |
{ | } |
if (potentialRelay.CanTransmit()) | |
{ | if (!isCircular) |
_nearestRelay = potentialRelay; | { |
Tools.PostDebugMessage(string.Format("{0}: found new best relay {1} ({2})", | nearestRelaySqrDistance = potentialSqrDistance; |
this.GetType().Name, | this.nearestRelay = potentialBestRelay; |
_nearestRelay.ToString(), | |
_nearestRelay.vessel.id | log.AppendFormat("\n\t{0}: found new nearest relay {1} ({2}m)", |
)); | this.ToString(), |
break; | this.nearestRelay.ToString(), |
} | Math.Sqrt(nearestRelaySqrDistance) |
} | ); |
} | } |
else | |
{ | |
log.AppendFormat("\n\t\t...connection to {0} would result in a circular network, skipping", | |
potentialBestRelay | |
); | |
} | |
} | |
} | |
CelestialBody bodyOccludingKerbin = null; | |
double kerbinSqrDistance = this.vessel.DistanceTo(Kerbin) - Kerbin.Radius; | |
kerbinSqrDistance *= kerbinSqrDistance; | |
log.AppendFormat("\n{0} ({1}): Search done, figuring status.", this.ToString(), this.GetType().Name); | |
log.AppendFormat( | |
"\n{0}: nearestRelay={1} ({2}m²)), bestOccludedRelay={3} ({4}m²), kerbinSqrDistance={5}m²)", | |
this, | |
this.nearestRelay == null ? "null" : this.nearestRelay.ToString(), | |
nearestRelaySqrDistance, | |
this.bestOccludedRelay == null ? "null" : this.bestOccludedRelay.ToString(), | |
bestOccludedSqrDistance, | |
kerbinSqrDistance | |
); | |
// If we don't have LOS to Kerbin, focus on relays | |
if (!this.vessel.hasLineOfSightTo(Kerbin, out bodyOccludingKerbin, ARConfiguration.RadiusRatio)) | |
{ | |
log.AppendFormat("\n\tKerbin LOS is blocked by {0}.", bodyOccludingKerbin.bodyName); | |
// nearestRelaySqrDistance will be infinity if all relays are occluded or none exist. | |
// Therefore, this will only be true if a valid relay is in range. | |
if (nearestRelaySqrDistance <= maxTransmitSqrDistance) | |
{ | |
log.AppendFormat("\n\t\tCan transmit to nearby relay {0} ({1} <= {2}).", | |
this.nearestRelay == null ? "null" : this.nearestRelay.ToString(), | |
nearestRelaySqrDistance, maxTransmitSqrDistance); | |
this.KerbinDirect = false; | |
this.canTransmit = true; | |
this.targetRelay = this.nearestRelay; | |
} | |
// If this isn't true, we can't transmit, but pick a second best of bestOccludedRelay and Kerbin anyway | |
else | |
{ | |
log.AppendFormat("\n\t\tCan't transmit to nearby relay {0} ({1} > {2}).", | |
this.nearestRelay == null ? "null" : this.nearestRelay.ToString(), | |
nearestRelaySqrDistance, maxTransmitSqrDistance); | |
this.canTransmit = false; | |
// If the best occluded relay is closer than Kerbin, check it against the nearest relay. | |
// Since bestOccludedSqrDistance is infinity if there are no occluded relays, this is safe | |
if (bestOccludedSqrDistance < kerbinSqrDistance) | |
{ | |
log.AppendFormat("\n\t\t\tBest occluded relay is closer than Kerbin ({0} < {1})", | |
bestOccludedRelay, kerbinSqrDistance); | |
this.KerbinDirect = false; | |
// If the nearest relay is closer than the best occluded relay, pick it. | |
// Since nearestRelaySqrDistane is infinity if there are no nearby relays, this is safe. | |
if (nearestRelaySqrDistance < bestOccludedSqrDistance) | |
{ | |
log.AppendFormat("\n\t\t\t\t...but the nearest relay is closer ({0} < {1}), so picking it.", | |
nearestRelaySqrDistance, bestOccludedSqrDistance); | |
this.targetRelay = this.nearestRelay; | |
this.firstOccludingBody = null; | |
} | |
// Otherwise, target the best occluded relay. | |
else | |
{ | |
log.AppendFormat("\n\t\t\t\t...and closer than the nearest relay ({0} >= {1}), so picking it.", | |
nearestRelaySqrDistance, bestOccludedSqrDistance); | |
this.targetRelay = bestOccludedRelay; | |
this.firstOccludingBody = bodyOccludingBestOccludedRelay; | |
} | |
} | |
// Otherwise, check Kerbin against the nearest relay. | |
// Since we have LOS, blank the first occluding body. | |
else | |
{ | |
log.AppendFormat("\n\t\t\tKerbin is closer than the best occluded relay ({0} >= {1})", | |
bestOccludedRelay, kerbinSqrDistance); | |
this.firstOccludingBody = null; | |
// If the nearest relay is closer than Kerbin, pick it. | |
// Since nearestRelaySqrDistane is infinity if there are no nearby relays, this is safe. | |
if (nearestRelaySqrDistance < kerbinSqrDistance) | |
{ | |
log.AppendFormat("\n\t\t\t\t...but the nearest relay is closer ({0} < {1}), so picking it.", | |
nearestRelaySqrDistance, kerbinSqrDistance); | |
this.KerbinDirect = false; | |
this.targetRelay = this.nearestRelay; | |
} | |
// Otherwise, pick Kerbin. | |
else | |
{ | |
log.AppendFormat("\n\t\t\t\t...and closer than the nearest relay ({0} >= {1}), so picking it.", | |
nearestRelaySqrDistance, kerbinSqrDistance); | |
this.KerbinDirect = true; | |
this.targetRelay = null; | |
} | |
} | |
} | |
} | |
// If we do have LOS to Kerbin, try to prefer the closest of nearestRelay and Kerbin | |
else | |
{ | |
log.AppendFormat("\n\tKerbin is in LOS."); | |
// If the nearest relay is closer than Kerbin and in range, transmit to it. | |
if (nearestRelaySqrDistance <= maxTransmitSqrDistance) | |
{ | |
log.AppendFormat("\n\t\tCan transmit to nearby relay {0} ({1} <= {2}).", | |
this.nearestRelay == null ? "null" : this.nearestRelay.ToString(), | |
nearestRelaySqrDistance, maxTransmitSqrDistance); | |
this.canTransmit = true; | |
// If the nearestRelay is closer than Kerbin, use it. | |
if (nearestRelaySqrDistance < kerbinSqrDistance) | |
{ | |
log.AppendFormat("\n\t\t\tPicking relay {0} over Kerbin ({1} < {2}).", | |
this.nearestRelay == null ? "null" : this.nearestRelay.ToString(), | |
nearestRelaySqrDistance, kerbinSqrDistance); | |
this.KerbinDirect = false; | |
this.targetRelay = this.nearestRelay; | |
} | |
// Otherwise, Kerbin is closer, so use it. | |
else | |
{ | |
log.AppendFormat("\n\t\t\tBut picking Kerbin over nearby relay {0} ({1} >= {2}).", | |
this.nearestRelay == null ? "null" : this.nearestRelay.ToString(), | |
nearestRelaySqrDistance, kerbinSqrDistance); | |
this.KerbinDirect = true; | |
this.targetRelay = null; | |
} | |
} | |
// If the nearest relay is out of range, we still need to check on Kerbin. | |
else | |
{ | |
log.AppendFormat("\n\t\tCan't transmit to nearby relay {0} ({1} > {2}).", | |
this.nearestRelay == null ? "null" : this.nearestRelay.ToString(), | |
nearestRelaySqrDistance, maxTransmitSqrDistance); | |
// If Kerbin is in range, use it. | |
if (kerbinSqrDistance <= maxTransmitSqrDistance) | |
{ | |
log.AppendFormat("\n\t\t\tCan transmit to Kerbin ({0} <= {1}).", | |
kerbinSqrDistance, maxTransmitSqrDistance); | |
this.canTransmit = true; | |
this.KerbinDirect = true; | |
this.targetRelay = null; | |
} | |
// If Kerbin is out of range and the nearest relay is out of range, pick a second best between | |
// Kerbin and bestOccludedRelay | |
else | |
{ | |
log.AppendFormat("\n\t\t\tCan't transmit to Kerbin ({0} > {1}).", | |
kerbinSqrDistance, maxTransmitSqrDistance); | |
this.canTransmit = false; | |
// If the best occluded relay is closer than Kerbin, check it against the nearest relay. | |
// Since bestOccludedSqrDistance is infinity if there are no occluded relays, this is safe | |
if (bestOccludedSqrDistance < kerbinSqrDistance) | |
{ | |
log.AppendFormat("\n\t\t\tBest occluded relay is closer than Kerbin ({0} < {1})", | |
bestOccludedRelay, kerbinSqrDistance); | |
this.KerbinDirect = false; | |
// If the nearest relay is closer than the best occluded relay, pick it. | |
// Since nearestRelaySqrDistane is infinity if there are no nearby relays, this is safe. | |
if (nearestRelaySqrDistance < bestOccludedSqrDistance) | |
{ | |
log.AppendFormat("\n\t\t\t\t...but the nearest relay is closer ({0} < {1}), so picking it.", | |
nearestRelaySqrDistance, bestOccludedSqrDistance); | |
this.targetRelay = this.nearestRelay; | |
this.firstOccludingBody = null; | |
} | |
// Otherwise, target the best occluded relay. | |
else | |
{ | |
log.AppendFormat("\n\t\t\t\t...and closer than the nearest relay ({0} >= {1}), so picking it.", | |
nearestRelaySqrDistance, bestOccludedSqrDistance); | |
this.targetRelay = bestOccludedRelay; | |
this.firstOccludingBody = bodyOccludingBestOccludedRelay; | |
} | |
} | |
// Otherwise, check Kerbin against the nearest relay. | |
// Since we have LOS, blank the first occluding body. | |
else | |
{ | |
log.AppendFormat("\n\t\t\tKerbin is closer than the best occluded relay ({0} >= {1})", | |
bestOccludedRelay, kerbinSqrDistance); | |
this.firstOccludingBody = null; | |
// If the nearest relay is closer than Kerbin, pick it. | |
// Since nearestRelaySqrDistane is infinity if there are no nearby relays, this is safe. | |
if (nearestRelaySqrDistance < kerbinSqrDistance) | |
{ | |
log.AppendFormat("\n\t\t\t\t...but the nearest relay is closer ({0} < {1}), so picking it.", | |
nearestRelaySqrDistance, kerbinSqrDistance); | |
this.KerbinDirect = false; | |
this.targetRelay = this.nearestRelay; | |
} | |
// Otherwise, pick Kerbin. | |
else | |
{ | |
log.AppendFormat("\n\t\t\t\t...and closer than the nearest relay ({0} >= {1}), so picking it.", | |
nearestRelaySqrDistance, kerbinSqrDistance); | |
this.KerbinDirect = true; | |
this.targetRelay = null; | |
} | |
} | |
} | |
} | |
} | |
log.AppendFormat("\n{0}: Target search and status determination complete.", this.ToString()); | |
#if DEBUG | |
} catch (Exception ex) { | |
log.AppendFormat("\nCaught {0}: {1}\n{2}", ex.GetType().FullName, ex.ToString(), ex.StackTrace); | |
#if QUIT_ON_EXCEPTION | |
UnityEngine.Application.Quit(); | |
#endif | |
} finally { | |
#endif | |
log.Print(false); | |
#if DEBUG | |
} | |
#endif | |
// Now that we're done with our recursive CanTransmit checks, flag this relay as not checked so it can be | // Now that we're done with our recursive CanTransmit checks, flag this relay as not checked so it can be |
// used next time. | // used next time. |
RelayDatabase.Instance.CheckedVesselsTable.Remove(vessel.id); | this.isChecked = false; |
} | |
// Return the nearest available relay, or null if there are no available relays nearby. | |
return _nearestRelay; | /// <summary> |
} | /// Returns a <see cref="System.String"/> that represents the current <see cref="AntennaRange.AntennaRelay"/>. |
/// </summary> | |
/// <summary> | /// <returns>A <see cref="System.String"/> that represents the current <see cref="AntennaRange.AntennaRelay"/>.</returns> |
/// Initializes a new instance of the <see cref="AntennaRange.ProtoDataTransmitter"/> class. | public override string ToString() |
/// </summary> | { |
/// <param name="ms"><see cref="ProtoPartModuleSnapshot"/></param> | if (this is ProtoAntennaRelay) |
{ | |
return (this as ProtoAntennaRelay).ToString(); | |
} | |
return this.moduleRef.ToString(); | |
} | |
/// <summary> | |
/// Initializes a new instance of the <see cref="AntennaRange.AntennaRelay"/> class. | |
/// </summary> | |
/// <param name="module">The module reference underlying this AntennaRelay, | |
/// as an <see cref="AntennaRange.IAntennaRelay"/></param> | |
public AntennaRelay(IAntennaRelay module) | public AntennaRelay(IAntennaRelay module) |
{ | { |
this.moduleRef = module; | this.moduleRef = module; |
this.isChecked = false; | |
this.searchTimer = new System.Diagnostics.Stopwatch(); | |
this.millisecondsBetweenSearches = 1250; | Tools.PostLogMessage("{0}: constructed {1}", this.GetType().Name, this.ToString()); |
// HACK: This might not be safe in all circumstances, but since AntennaRelays are not built until Start, | |
// we hope it is safe enough. | |
if (AntennaRelay.Kerbin == null) | |
{ | |
AntennaRelay.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(); | |
} | } |
} | } |
} | } |
ACTIVE_TEXTURE_MANAGER_CONFIG | |
{ | |
folder = AntennaRange | |
enabled = true | |
OVERRIDES | |
{ | |
AntennaRange/.* | |
{ | |
compress = true | |
mipmaps = false | |
scale = 1 | |
max_size = 0 | |
} | |
} | |
} |
// AntennaRange | |
// | |
// AntennaRange.cfg | |
// | |
// Copyright © 2014-2015, toadicus | |
// All rights reserved. | |
// | |
// Redistribution and use in source and binary forms, with or without modification, | |
// are permitted provided that the following conditions are met: | |
// | |
// 1. Redistributions of source code must retain the above copyright notice, | |
// this list of conditions and the following disclaimer. | |
// | |
// 2. Redistributions in binary form must reproduce the above copyright notice, | |
// this list of conditions and the following disclaimer in the documentation and/or other | |
// materials provided with the distribution. | |
// | |
// 3. Neither the name of the copyright holder nor the names of its contributors may be used | |
// to endorse or promote products derived from this software without specific prior written permission. | |
// | |
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, | |
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | |
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
// | |
// 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 | |
} | |
MODULE | |
{ | |
name = ModuleScienceContainer | |
dataIsCollectable = true | |
dataIsStorable = false | |
storageRange = 2 | |
} | |
} | |
@PART[mediumDishAntenna]:FOR[AntennaRange]:NEEDS[!RemoteTech2] | |
{ | |
@MODULE[ModuleDataTransmitter] | |
{ | |
@name = ModuleLimitedDataTransmitter | |
nominalRange = 30000000 | |
maxPowerFactor = 8 | |
maxDataFactor = 4 | |
} | |
MODULE | |
{ | |
name = ModuleScienceContainer | |
dataIsCollectable = true | |
dataIsStorable = false | |
storageRange = 2 | |
} | |
} | |
@PART[commDish]:FOR[AntennaRange]:NEEDS[!RemoteTech2] | |
{ | |
@MODULE[ModuleDataTransmitter] | |
{ | |
@name = ModuleLimitedDataTransmitter | |
nominalRange = 80000000000 | |
maxPowerFactor = 8 | |
maxDataFactor = 4 | |
} | |
MODULE | |
{ | |
name = ModuleScienceContainer | |
dataIsCollectable = true | |
dataIsStorable = false | |
storageRange = 2 | |
} | |
} | |
EVA_MODULE | |
{ | |
name = ModuleLimitedDataTransmitter | |
nominalRange = 5000 | |
maxPowerFactor = 1 | |
maxDataFactor = 1 | |
packetInterval = 0.2 | |
packetSize = 1 | |
packetResourceCost = 6.25 | |
requiredResource = ElectricCharge | |
} | |
EVA_RESOURCE | |
{ | |
name = ElectricCharge | |
amount = 100 | |
maxAmount = 100 | |
} | |
@EVA_RESOURCE[ElectricCharge]:AFTER[AntennaRange]:NEEDS[TacLifeSupport] | |
{ | |
!name = DELETE | |
} | |
Binary files /dev/null and b/GameData/AntennaRange/Textures/appLauncherIcon.png differ
Binary files /dev/null and b/GameData/AntennaRange/Textures/appLauncherIconNoConnection.png differ
Binary files /dev/null and b/GameData/AntennaRange/Textures/appLauncherIconSubOptimal.png differ
Binary files /dev/null and b/GameData/AntennaRange/Textures/toolbarIcon.png differ
Binary files /dev/null and b/GameData/AntennaRange/Textures/toolbarIconNoConnection.png differ
Binary files /dev/null and b/GameData/AntennaRange/Textures/toolbarIconSubOptimal.png differ
// AntennaRange | // AntennaRange |
// | // |
// IAntennaRelay.cs | // IAntennaRelay.cs |
// | // |
// Copyright © 2014, toadicus | // Copyright © 2014-2015, toadicus |
// All rights reserved. | // All rights reserved. |
// | // |
// Redistribution and use in source and binary forms, with or without modification, | // Redistribution and use in source and binary forms, with or without modification, |
// are permitted provided that the following conditions are met: | // are permitted provided that the following conditions are met: |
// | // |
// 1. Redistributions of source code must retain the above copyright notice, | // 1. Redistributions of source code must retain the above copyright notice, |
// this list of conditions and the following disclaimer. | // this list of conditions and the following disclaimer. |
// | // |
// 2. Redistributions in binary form must reproduce the above copyright notice, | // 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 | // this list of conditions and the following disclaimer in the documentation and/or other |
// materials provided with the distribution. | // materials provided with the distribution. |
// | // |
// 3. Neither the name of the copyright holder nor the names of its contributors may be used | // 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. | // 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, | // 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 | // 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, | // 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 | // 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, | // 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 | // 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. | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
using KSP; | using KSP; |
using System; | using System; |
namespace AntennaRange | namespace AntennaRange |
{ | { |
/* | /// <summary> |
* Interface defining the basic functionality of AntennaRelay modules for AntennaRange. | /// Interface defining the basic functionality of AntennaRelay modules for AntennaRange. |
* */ | /// </summary> |
public interface IAntennaRelay | public interface IAntennaRelay |
{ | { |
/// <summary> | /// <summary> |
/// Gets the parent Vessel. | /// Gets the parent Vessel. |
/// </summary> | /// </summary> |
/// <value>The parent Vessel.</value> | |
Vessel vessel { get; } | Vessel vessel { get; } |
IAntennaRelay nearestRelay { get; } | /// <summary> |
/// Gets the target <see cref="AntennaRange.IAntennaRelay"/>relay. | |
/// </summary> | |
IAntennaRelay targetRelay { get; } | |
/// <summary> | /// <summary> |
/// Gets the distance to the nearest relay or Kerbin, whichever is closer. | /// Gets the distance to the nearest relay or Kerbin, whichever is closer. |
/// </summary> | /// </summary> |
/// <value>The distance to the nearest relay or Kerbin, whichever is closer.</value> | |
double transmitDistance { get; } | double transmitDistance { get; } |
/// <summary> | |
/// Gets the nominal transmit distance at which the Antenna behaves just as prescribed by Squad's config. | |
/// </summary> | |
double nominalTransmitDistance { get; } | |
/// <summary> | /// <summary> |
/// The maximum distance at which this relay can operate. | /// The maximum distance at which this relay can operate. |
/// </summary> | /// </summary> |
/// <value>The max transmit distance.</value> | double maxTransmitDistance { get; } |
float maxTransmitDistance { get; } | |
/// <summary> | /// <summary> |
/// Gets a value indicating whether this <see cref="AntennaRange.ProtoDataTransmitter"/> has been checked during | /// The first CelestialBody blocking line of sight to a |
/// the current relay attempt. | |
/// </summary> | /// </summary> |
/// <value><c>true</c> if relay checked; otherwise, <c>false</c>.</value> | CelestialBody firstOccludingBody { get; } |
bool relayChecked { get; } | |
/// <summary> | |
/// Gets a value indicating whether this <see cref="AntennaRange.IAntennaRelay"/> Relay is communicating | |
/// directly with Kerbin. | |
/// </summary> | |
bool KerbinDirect { get; } | |
/// <summary> | |
/// Gets the Part title. | |
/// </summary> | |
string Title { get; } | |
/// <summary> | /// <summary> |
/// Determines whether this instance can transmit. | /// Determines whether this instance can transmit. |
/// <c>true</c> if this instance can transmit; otherwise, <c>false</c>. | |
/// </summary> | /// </summary> |
/// <returns><c>true</c> if this instance can transmit; otherwise, <c>false</c>.</returns> | |
bool CanTransmit(); | bool CanTransmit(); |
/// <summary> | |
/// Finds the nearest relay. | |
/// </summary> | |
void FindNearestRelay(); | |
/// <summary> | |
/// Returns a <see cref="System.String"/> that represents the current <see cref="AntennaRange.IAntennaRelay"/>. | |
/// </summary> | |
string ToString(); | |
} | } |
} | } |
// AntennaRange | // AntennaRange |
// | // |
// ModuleLimitedDataTransmitter.cs | // ModuleLimitedDataTransmitter.cs |
// | // |
// Copyright © 2014, toadicus | // Copyright © 2014-2015, toadicus |
// All rights reserved. | // All rights reserved. |
// | // |
// Redistribution and use in source and binary forms, with or without modification, | // Redistribution and use in source and binary forms, with or without modification, |
// are permitted provided that the following conditions are met: | // are permitted provided that the following conditions are met: |
// | // |
// 1. Redistributions of source code must retain the above copyright notice, | // 1. Redistributions of source code must retain the above copyright notice, |
// this list of conditions and the following disclaimer. | // this list of conditions and the following disclaimer. |
// | // |
// 2. Redistributions in binary form must reproduce the above copyright notice, | // 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 | // this list of conditions and the following disclaimer in the documentation and/or other |
// materials provided with the distribution. | // materials provided with the distribution. |
// | // |
// 3. Neither the name of the copyright holder nor the names of its contributors may be used | // 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. | // 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, | // 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 | // 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, | // 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 | // 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, | // 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 | // 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. | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
using KSP; | using KSP; |
using System; | using System; |
using System.Collections.Generic; | using System.Collections.Generic; |
using System.Linq; | |
using System.Text; | using System.Text; |
using ToadicusTools; | using ToadicusTools; |
using UnityEngine; | using UnityEngine; |
namespace AntennaRange | namespace AntennaRange |
{ | { |
/* | /// <summary> |
* ModuleLimitedDataTransmitter is designed as a drop-in replacement for ModuleDataTransmitter, and handles range- | /// <para>ModuleLimitedDataTransmitter is designed as a drop-in replacement for ModuleDataTransmitter, and handles |
* finding, power scaling, and data scaling for antennas during science transmission. Its functionality varies with | /// rangefinding, power scaling, and data scaling for antennas during science transmission. Its functionality |
* three tunables: nominalRange, maxPowerFactor, and maxDataFactor, set in .cfg files. | /// varies with three tunables: nominalRange, maxPowerFactor, and maxDataFactor, set in .cfg files.</para> |
* | /// |
* In general, the scaling functions assume the following relation: | /// <para>In general, the scaling functions assume the following relation:</para> |
* | /// |
* D² α P/R, | /// <para> D² α P/R,</para> |
* | /// |
* where D is the total transmission distance, P is the transmission power, and R is the data rate. | /// <para>where D is the total transmission distance, P is the transmission power, and R is the data rate.</para> |
* | /// </summary> |
* */ | |
/* | |
* Fields | |
* */ | |
public class ModuleLimitedDataTransmitter : ModuleDataTransmitter, IScienceDataTransmitter, IAntennaRelay | public class ModuleLimitedDataTransmitter : ModuleDataTransmitter, IScienceDataTransmitter, IAntennaRelay |
{ | { |
// 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. | // Stores the packetResourceCost as defined in the .cfg file. |
protected float _basepacketResourceCost; | private float _basepacketResourceCost; |
// Stores the packetSize as defined in the .cfg file. | // Stores the packetSize as defined in the .cfg file. |
protected float _basepacketSize; | private float _basepacketSize; |
// Every antenna is a relay. | // Every antenna is a relay. |
protected AntennaRelay relay; | private AntennaRelay relay; |
// Keep track of vessels with transmitters for relay purposes. | |
protected List<Vessel> _relayVessels; | |
// Sometimes we will need to communicate errors; this is how we do it. | // Sometimes we will need to communicate errors; this is how we do it. |
protected ScreenMessage ErrorMsg; | private ScreenMessage ErrorMsg; |
// The distance from Kerbin at which the antenna will perform exactly as prescribed by packetResourceCost | /// <summary> |
// and packetSize. | /// The distance from Kerbin at which the antenna will perform exactly as prescribed by packetResourceCost |
/// and packetSize. | |
/// </summary> | |
[KSPField(isPersistant = false)] | [KSPField(isPersistant = false)] |
public float nominalRange; | public double nominalRange; |
/// <summary> | |
/// Relay status string for use in action menus. | |
/// </summary> | |
[KSPField(isPersistant = false, guiActive = true, guiName = "Status")] | |
public string UIrelayStatus; | |
/// <summary> | |
/// Relay target string for use in action menus. | |
/// </summary> | |
[KSPField(isPersistant = false, guiActive = true, guiName = "Relay")] | [KSPField(isPersistant = false, guiActive = true, guiName = "Relay")] |
public string UIrelayStatus; | public string UIrelayTarget; |
/// <summary> | |
/// Transmit distance string for use in action menus. | |
/// </summary> | |
[KSPField(isPersistant = false, guiActive = true, guiName = "Transmission Distance")] | [KSPField(isPersistant = false, guiActive = true, guiName = "Transmission Distance")] |
public string UItransmitDistance; | public string UItransmitDistance; |
/// <summary> | |
/// Maximum distance string for use in action menus. | |
/// </summary> | |
[KSPField(isPersistant = false, guiActive = true, guiName = "Maximum Distance")] | [KSPField(isPersistant = false, guiActive = true, guiName = "Maximum Distance")] |
public string UImaxTransmitDistance; | public string UImaxTransmitDistance; |
/// <summary> | |
/// Packet size string for use in action menus. | |
/// </summary> | |
[KSPField(isPersistant = false, guiActive = true, guiName = "Packet Size")] | [KSPField(isPersistant = false, guiActive = true, guiName = "Packet Size")] |
public string UIpacketSize; | public string UIpacketSize; |
/// <summary> | |
/// Packet cost string for use in action menus. | |
/// </summary> | |
[KSPField(isPersistant = false, guiActive = true, guiName = "Packet Cost")] | [KSPField(isPersistant = false, guiActive = true, guiName = "Packet Cost")] |
public string UIpacketCost; | public string UIpacketCost; |
// The multiplier on packetResourceCost that defines the maximum power output of the antenna. When the power | /// <summary> |
// cost exceeds packetResourceCost * maxPowerFactor, transmission will fail. | /// The multiplier on packetResourceCost that defines the maximum power output of the antenna. When the power |
/// cost exceeds packetResourceCost * maxPowerFactor, transmission will fail. | |
/// </summary> | |
[KSPField(isPersistant = false)] | [KSPField(isPersistant = false)] |
public float maxPowerFactor; | public float maxPowerFactor; |
// The multipler on packetSize that defines the maximum data bandwidth of the antenna. | /// <summary> |
/// The multipler on packetSize that defines the maximum data bandwidth of the antenna. | |
/// </summary> | |
[KSPField(isPersistant = false)] | [KSPField(isPersistant = false)] |
public float maxDataFactor; | public float maxDataFactor; |
/// <summary> | |
/// The packet throttle. | |
/// </summary> | |
[KSPField( | [KSPField( |
isPersistant = true, | isPersistant = true, |
guiName = "Packet Throttle", | guiName = "Packet Throttle", |
guiUnits = "%", | guiUnits = "%", |
guiActive = true, | guiActive = true, |
guiActiveEditor = false | guiActiveEditor = false |
)] | )] |
[UI_FloatRange(maxValue = 100f, minValue = 2.5f, stepIncrement = 2.5f)] | [UI_FloatRange(maxValue = 100f, minValue = 2.5f, stepIncrement = 2.5f)] |
public float packetThrottle; | public float packetThrottle; |
protected bool actionUIUpdate; | private bool actionUIUpdate; |
/* | /* |
* Properties | * Properties |
* */ | * */ |
// Returns the parent vessel housing this antenna. | /// <summary> |
/// Gets the parent Vessel. | |
/// </summary> | |
public new Vessel vessel | public new Vessel vessel |
{ | { |
get | get |
{ | { |
return base.vessel; | if (base.vessel != null) |
} | { |
} | return base.vessel; |
} | |
public IAntennaRelay nearestRelay | else if (this.part != null && this.part.vessel != null) |
{ | |
return this.part.vessel; | |
} | |
else if ( | |
this.part.protoPartSnapshot != null && | |
this.part.protoPartSnapshot.pVesselRef != null && | |
this.part.protoPartSnapshot.pVesselRef.vesselRef != null | |
) | |
{ | |
return this.part.protoPartSnapshot.pVesselRef.vesselRef; | |
} | |
else | |
{ | |
this.LogError("Vessel and/or part reference are null, returning null vessel."); | |
return null; | |
} | |
} | |
} | |
/// <summary> | |
/// Gets the target <see cref="AntennaRange.IAntennaRelay"/>relay. | |
/// </summary> | |
public IAntennaRelay targetRelay | |
{ | { |
get | get |
{ | { |
if (this.relay == null) | if (this.relay == null) |
{ | { |
return null; | return null; |
} | } |
return this.relay.nearestRelay; | return this.relay.targetRelay; |
} | } |
} | } |
// Returns the distance to the nearest relay or Kerbin, whichever is closer. | /// <summary> |
/// Gets the distance to the nearest relay or Kerbin, whichever is closer. | |
/// </summary> | |
public double transmitDistance | public double transmitDistance |
{ | { |
get | get |
{ | { |
if (this.relay == null) | |
{ | |
return double.PositiveInfinity; | |
} | |
return this.relay.transmitDistance; | return this.relay.transmitDistance; |
} | } |
} | } |
// Returns the maximum distance this module can transmit | /// <summary> |
public float maxTransmitDistance | /// Gets the nominal transmit distance at which the Antenna behaves just as prescribed by Squad's config. |
/// </summary> | |
public double nominalTransmitDistance | |
{ | { |
get | get |
{ | { |
return Mathf.Sqrt (this.maxPowerFactor) * this.nominalRange; | return this.nominalRange; |
} | |
} | |
/// <summary> | |
/// The maximum distance at which this relay can operate. | |
/// </summary> | |
public double maxTransmitDistance | |
{ | |
get; | |
protected set; | |
} | |
/// <summary> | |
/// The first CelestialBody blocking line of sight to a | |
/// </summary> | |
public CelestialBody firstOccludingBody | |
{ | |
get | |
{ | |
return this.relay.firstOccludingBody; | |
} | } |
} | } |
/* | /* |
* The next two functions overwrite the behavior of the stock functions and do not perform equivalently, except | * The next two functions overwrite the behavior of the stock functions and do not perform equivalently, except |
* in that they both return floats. Here's some quick justification: | * in that they both return floats. Here's some quick justification: |
* | * |
* The stock implementation of GetTransmitterScore (which I cannot override) is: | * The stock implementation of GetTransmitterScore (which I cannot override) is: |
* Score = (1 + DataResourceCost) / DataRate | * Score = (1 + DataResourceCost) / DataRate |
* | * |
* The stock DataRate and DataResourceCost are: | * The stock DataRate and DataResourceCost are: |
* DataRate = packetSize / packetInterval | * DataRate = packetSize / packetInterval |
* DataResourceCost = packetResourceCost / packetSize | * DataResourceCost = packetResourceCost / packetSize |
* | * |
* So, the resulting score is essentially in terms of joules per byte per baud. Rearranging that a bit, it | * So, the resulting score is essentially in terms of joules per byte per baud. Rearranging that a bit, it |
* could also look like joule-seconds per byte per byte, or newton-meter-seconds per byte per byte. Either way, | * could also look like joule-seconds per byte per byte, or newton-meter-seconds per byte per byte. Either way, |
* that metric is not a very reasonable one. | * that metric is not a very reasonable one. |
* | * |
* Two metrics that might make more sense are joules per byte or joules per byte per second. The latter case | * Two metrics that might make more sense are joules per byte or joules per byte per second. The latter case |
* would look like: | * would look like: |
* DataRate = packetSize / packetInterval | * DataRate = packetSize / packetInterval |
* DataResourceCost = packetResourceCost | * DataResourceCost = packetResourceCost |
* | * |
* The former case, which I've chosen to implement below, is: | * The former case, which I've chosen to implement below, is: |
* DataRate = packetSize | * DataRate = packetSize |
* DataResourceCost = packetResourceCost | * DataResourceCost = packetResourceCost |
* | * |
* So... hopefully that doesn't screw with anything else. | * So... hopefully that doesn't screw with anything else. |
* */ | * */ |
// Override ModuleDataTransmitter.DataRate to just return packetSize, because we want antennas to be scored in | /// <summary> |
// terms of joules/byte | /// Override ModuleDataTransmitter.DataRate to just return packetSize, because we want antennas to be scored in |
/// terms of joules/byte | |
/// </summary> | |
public new float DataRate | public new float DataRate |
{ | { |
get | get |
{ | { |
this.PreTransmit_SetPacketSize(); | this.PreTransmit_SetPacketSize(); |
if (this.CanTransmit()) | if (this.CanTransmit()) |
{ | { |
return this.packetSize; | return this.packetSize; |
} | } |
else | else |
{ | { |
return float.Epsilon; | return float.Epsilon; |
} | } |
} | } |
} | } |
// Override ModuleDataTransmitter.DataResourceCost to just return packetResourceCost, because we want antennas | /// <summary> |
// to be scored in terms of joules/byte | /// Override ModuleDataTransmitter.DataResourceCost to just return packetResourceCost, because we want antennas |
public new float DataResourceCost | /// to be scored in terms of joules/byte |
/// </summary> | |
public new double DataResourceCost | |
{ | { |
get | get |
{ | { |
this.PreTransmit_SetPacketResourceCost(); | this.PreTransmit_SetPacketResourceCost(); |
if (this.CanTransmit()) | if (this.CanTransmit()) |
{ | { |
return this.packetResourceCost; | return this.packetResourceCost; |
} | } |
else | else |
{ | { |
return float.PositiveInfinity; | return float.PositiveInfinity; |
} | } |
} | } |
} | } |
// Reports whether this antenna has been checked as a viable relay already in the current FindNearestRelay. | /// <summary> |
public bool relayChecked | /// Gets a value indicating whether this <see cref="AntennaRange.IAntennaRelay"/> Relay is communicating |
/// directly with Kerbin. | |
/// </summary> | |
public bool KerbinDirect | |
{ | { |
get | get |
{ | { |
return this.relay.relayChecked; | if (this.relay != null) |
{ | |
return this.relay.KerbinDirect; | |
} | |
return false; | |
} | |
} | |
/// <summary> | |
/// Gets the Part title. | |
/// </summary> | |
public string Title | |
{ | |
get | |
{ | |
if (this.part != null && this.part.partInfo != null) | |
{ | |
return this.part.partInfo.title; | |
} | |
return string.Empty; | |
} | } |
} | } |
/* | /* |
* Methods | * Methods |
* */ | * */ |
// Build ALL the objects. | // Build ALL the objects. |
public ModuleLimitedDataTransmitter () : base() | public ModuleLimitedDataTransmitter () : base() |
{ | { |
this.ErrorMsg = new ScreenMessage("", 4f, false, ScreenMessageStyle.UPPER_LEFT); | this.ErrorMsg = new ScreenMessage("", 4f, false, ScreenMessageStyle.UPPER_LEFT); |
this.packetThrottle = 100f; | this.packetThrottle = 100f; |
} | } |
// At least once, when the module starts with a state on the launch pad or later, go find Kerbin. | /// <summary> |
public override void OnStart (StartState state) | /// PartModule OnAwake override; runs at Unity Awake. |
{ | /// </summary> |
base.OnStart (state); | public override void OnAwake() |
{ | |
if (state >= StartState.PreLaunch) | base.OnAwake(); |
{ | |
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); | |
} | |
} | |
// When the module loads, fetch the Squad KSPFields from the base. This is necessary in part because | |
// overloading packetSize and packetResourceCostinto a property in ModuleLimitedDataTransmitter didn't | |
// work. | |
public override void OnLoad(ConfigNode node) | |
{ | |
this.Fields.Load(node); | |
base.Fields.Load(node); | |
base.OnLoad (node); | |
this._basepacketSize = base.packetSize; | this._basepacketSize = base.packetSize; |
this._basepacketResourceCost = base.packetResourceCost; | this._basepacketResourceCost = base.packetResourceCost; |
Tools.PostDebugMessage(string.Format( | Tools.PostDebugMessage(string.Format( |
"{0} loaded:\n" + | "{0} loaded:\n" + |
"packetSize: {1}\n" + | "packetSize: {1}\n" + |
"packetResourceCost: {2}\n" + | "packetResourceCost: {2}\n" + |
"nominalRange: {3}\n" + | "nominalRange: {3}\n" + |
"maxPowerFactor: {4}\n" + | "maxPowerFactor: {4}\n" + |
"maxDataFactor: {5}\n", | "maxDataFactor: {5}\n", |
this.name, | this.name, |
base.packetSize, | base.packetSize, |
this._basepacketResourceCost, | this._basepacketResourceCost, |
this.nominalRange, | this.nominalRange, |
this.maxPowerFactor, | this.maxPowerFactor, |
this.maxDataFactor | this.maxDataFactor |
)); | )); |
} | } |
/// <summary> | |
/// PartModule OnStart override; runs at Unity Start. | |
/// </summary> | |
/// <param name="state">State.</param> | |
public override void OnStart (StartState state) | |
{ | |
base.OnStart (state); | |
if (state >= StartState.PreLaunch) | |
{ | |
this.maxTransmitDistance = Math.Sqrt(this.maxPowerFactor) * this.nominalRange; | |
this.relay = new AntennaRelay(this); | |
this.relay.maxTransmitDistance = this.maxTransmitDistance; | |
this.relay.nominalTransmitDistance = this.nominalRange; | |
this.UImaxTransmitDistance = Tools.MuMech_ToSI(this.maxTransmitDistance) + "m"; | |
GameEvents.onPartActionUICreate.Add(this.onPartActionUICreate); | |
GameEvents.onPartActionUIDismiss.Add(this.onPartActionUIDismiss); | |
} | |
} | |
/// <summary> | |
/// When the module loads, fetch the Squad KSPFields from the base. This is necessary in part because | |
/// overloading packetSize and packetResourceCostinto a property in ModuleLimitedDataTransmitter didn't | |
/// work. | |
/// </summary> | |
/// <param name="node"><see cref="ConfigNode"/> with data for this module.</param> | |
public override void OnLoad(ConfigNode node) | |
{ | |
this.Fields.Load(node); | |
base.Fields.Load(node); | |
base.OnLoad (node); | |
this.maxTransmitDistance = Math.Sqrt(this.maxPowerFactor) * this.nominalRange; | |
} | |
/// <summary> | |
/// Override ModuleDataTransmitter.GetInfo to add nominal and maximum range to the VAB description. | |
/// </summary> | |
public override string GetInfo() | |
{ | |
string text = base.GetInfo(); | |
text += "Nominal Range: " + Tools.MuMech_ToSI((double)this.nominalRange, 2) + "m\n"; | |
text += "Maximum Range: " + Tools.MuMech_ToSI((double)this.maxTransmitDistance, 2) + "m\n"; | |
return text; | |
} | |
/// <summary> | |
/// Determines whether this instance can transmit. | |
/// <c>true</c> if this instance can transmit; otherwise, <c>false</c>. | |
/// </summary> | |
public new bool CanTransmit() | |
{ | |
if (this.part == null || this.relay == null) | |
{ | |
return false; | |
} | |
switch (this.part.State) | |
{ | |
case PartStates.DEAD: | |
case 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), this.part.State) | |
)); | |
return false; | |
default: | |
break; | |
} | |
return this.relay.CanTransmit(); | |
} | |
/// <summary> | |
/// Finds the nearest relay. | |
/// </summary> | |
public void FindNearestRelay() | |
{ | |
if (this.relay != null) | |
{ | |
this.relay.FindNearestRelay(); | |
} | |
} | |
/// <summary> | |
/// Override ModuleDataTransmitter.TransmitData to check against CanTransmit and fail out when CanTransmit | |
/// returns false. | |
/// </summary> | |
/// <param name="dataQueue">List of <see cref="ScienceData"/> to transmit.</param> | |
/// <param name="callback">Callback function</param> | |
public new void TransmitData(List<ScienceData> dataQueue, Callback callback) | |
{ | |
this.LogDebug( | |
"TransmitData(List<ScienceData> dataQueue, Callback callback) called. dataQueue.Count={0}", | |
dataQueue.Count | |
); | |
this.FindNearestRelay(); | |
this.PreTransmit_SetPacketSize(); | |
this.PreTransmit_SetPacketResourceCost(); | |
if (this.CanTransmit()) | |
{ | |
ScreenMessages.PostScreenMessage(this.buildTransmitMessage(), 4f, ScreenMessageStyle.UPPER_LEFT); | |
this.LogDebug( | |
"CanTransmit in TransmitData, calling base.TransmitData with dataQueue=[{0}] and callback={1}", | |
dataQueue.SPrint(), | |
callback == null ? "null" : callback.ToString() | |
); | |
if (callback == null) | |
{ | |
base.TransmitData(dataQueue); | |
} | |
else | |
{ | |
base.TransmitData(dataQueue, callback); | |
} | |
} | |
else | |
{ | |
Tools.PostDebugMessage(this, "{0} unable to transmit during TransmitData.", this.part.partInfo.title); | |
var logger = Tools.DebugLogger.New(this); | |
IList<ModuleScienceContainer> vesselContainers = this.vessel.getModulesOfType<ModuleScienceContainer>(); | |
ModuleScienceContainer scienceContainer; | |
for (int cIdx = 0; cIdx < vesselContainers.Count; cIdx++) | |
{ | |
scienceContainer = vesselContainers[cIdx]; | |
logger.AppendFormat("Checking ModuleScienceContainer in {0}\n", | |
scienceContainer.part.partInfo.title); | |
if ( | |
scienceContainer.capacity != 0 && | |
scienceContainer.GetScienceCount() >= scienceContainer.capacity | |
) | |
{ | |
logger.Append("\tInsufficient capacity, skipping.\n"); | |
continue; | |
} | |
List<ScienceData> dataStored = new List<ScienceData>(); | |
ScienceData data; | |
for (int dIdx = 0; dIdx < dataQueue.Count; dIdx++) | |
{ | |
data = dataQueue[dIdx]; | |
if (!scienceContainer.allowRepeatedSubjects && scienceContainer.HasData(data)) | |
{ | |
logger.Append("\tAlready contains subject and repeated subjects not allowed, skipping.\n"); | |
continue; | |
} | |
logger.AppendFormat("\tAcceptable, adding data on subject {0}... ", data.subjectID); | |
if (scienceContainer.AddData(data)) | |
{ | |
logger.Append("done, removing from queue.\n"); | |
dataStored.Add(data); | |
} | |
#if DEBUG | |
else | |
{ | |
logger.Append("failed.\n"); | |
} | |
#endif | |
} | |
dataQueue.RemoveAll(i => dataStored.Contains(i)); | |
logger.AppendFormat("\t{0} data left in queue.", dataQueue.Count); | |
} | |
logger.Print(); | |
if (dataQueue.Count > 0) | |
{ | |
StringBuilder sb = Tools.GetStringBuilder(); | |
sb.Append('['); | |
sb.Append(this.part.partInfo.title); | |
sb.AppendFormat("]: {0} data items could not be saved: no space available in data containers.\n"); | |
sb.Append("Data to be discarded:\n"); | |
ScienceData data; | |
for (int dIdx = 0; dIdx < dataQueue.Count; dIdx++) | |
{ | |
data = dataQueue[dIdx]; | |
sb.AppendFormat("\t{0}\n", data.title); | |
} | |
ScreenMessages.PostScreenMessage(sb.ToString(), 4f, ScreenMessageStyle.UPPER_LEFT); | |
Tools.PostDebugMessage(sb.ToString()); | |
Tools.PutStringBuilder(sb); | |
} | |
this.PostCannotTransmitError(); | |
} | |
Tools.PostDebugMessage ( | |
"distance: " + this.transmitDistance | |
+ " packetSize: " + this.packetSize | |
+ " packetResourceCost: " + this.packetResourceCost | |
); | |
} | |
/// <summary> | |
/// Override ModuleDataTransmitter.TransmitData to check against CanTransmit and fail out when CanTransmit | |
/// returns false. | |
/// </summary> | |
/// <param name="dataQueue">List of <see cref="ScienceData"/> to transmit.</param> | |
public new void TransmitData(List<ScienceData> dataQueue) | |
{ | |
this.LogDebug( | |
"TransmitData(List<ScienceData> dataQueue) called, dataQueue.Count={0}", | |
dataQueue.Count | |
); | |
this.TransmitData(dataQueue, null); | |
} | |
/// <summary> | |
/// Override ModuleDataTransmitter.StartTransmission to check against CanTransmit and fail out when CanTransmit | |
/// returns false. | |
/// </summary> | |
public new void StartTransmission() | |
{ | |
this.FindNearestRelay(); | |
PreTransmit_SetPacketSize (); | |
PreTransmit_SetPacketResourceCost (); | |
Tools.PostDebugMessage ( | |
"distance: " + this.transmitDistance | |
+ " packetSize: " + this.packetSize | |
+ " packetResourceCost: " + this.packetResourceCost | |
); | |
if (this.CanTransmit()) | |
{ | |
ScreenMessages.PostScreenMessage(this.buildTransmitMessage(), 4f, ScreenMessageStyle.UPPER_LEFT); | |
base.StartTransmission(); | |
} | |
else | |
{ | |
this.PostCannotTransmitError (); | |
} | |
} | |
/// <summary> | |
/// MonoBehaviour Update | |
/// </summary> | |
public void Update() | |
{ | |
if (this.actionUIUpdate) | |
{ | |
if (this.CanTransmit()) | |
{ | |
this.UIrelayStatus = "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 = "Out of range"; | |
} | |
else | |
{ | |
this.UIrelayStatus = string.Format("Blocked by {0}", this.relay.firstOccludingBody.bodyName); | |
} | |
this.UIpacketSize = "N/A"; | |
this.UIpacketCost = "N/A"; | |
} | |
if (this.KerbinDirect) | |
{ | |
this.UIrelayTarget = AntennaRelay.Kerbin.bodyName; | |
} | |
else | |
{ | |
this.UIrelayTarget = this.targetRelay.ToString(); | |
} | |
} | |
} | |
/// <summary> | |
/// Returns a <see cref="System.String"/> that represents the current <see cref="AntennaRange.ModuleLimitedDataTransmitter"/>. | |
/// </summary> | |
/// <returns>A <see cref="System.String"/> that represents the current <see cref="AntennaRange.ModuleLimitedDataTransmitter"/>.</returns> | |
public override string ToString() | |
{ | |
StringBuilder sb = Tools.GetStringBuilder(); | |
string msg; | |
sb.Append(this.part.partInfo.title); | |
if (vessel != null) | |
{ | |
sb.Append(" on "); | |
sb.Append(vessel.vesselName); | |
} | |
else if ( | |
this.part != null && | |
this.part.protoPartSnapshot != null && | |
this.part.protoPartSnapshot != null && | |
this.part.protoPartSnapshot.pVesselRef != null | |
) | |
{ | |
sb.Append(" on "); | |
sb.Append(this.part.protoPartSnapshot.pVesselRef.vesselName); | |
} | |
msg = sb.ToString(); | |
Tools.PutStringBuilder(sb); | |
return msg; | |
} | |
// When we catch an onPartActionUICreate event for our part, go ahead and update every frame to look pretty. | |
private void onPartActionUICreate(Part eventPart) | |
{ | |
if (eventPart == base.part) | |
{ | |
this.actionUIUpdate = true; | |
} | |
} | |
// When we catch an onPartActionUIDismiss event for our part, stop updating every frame to look pretty. | |
private void onPartActionUIDismiss(Part eventPart) | |
{ | |
if (eventPart == base.part) | |
{ | |
this.actionUIUpdate = false; | |
} | |
} | |
// Post an error in the communication messages describing the reason transmission has failed. Currently there | // Post an error in the communication messages describing the reason transmission has failed. Currently there |
// is only one reason for this. | // is only one reason for this. |
protected void PostCannotTransmitError() | private void PostCannotTransmitError() |
{ | { |
string ErrorText = string.Intern("Unable to transmit: no visible receivers in range!"); | string ErrorText = string.Intern("Unable to transmit: no visible receivers in range!"); |
this.ErrorMsg.message = string.Format( | this.ErrorMsg.message = string.Format( |
"<color='#{0}{1}{2}{3}'><b>{4}</b></color>", | "<color='#{0}{1}{2}{3}'><b>{4}</b></color>", |
((int)(XKCDColors.OrangeRed.r * 255f)).ToString("x2"), | ((int)(XKCDColors.OrangeRed.r * 255f)).ToString("x2"), |
((int)(XKCDColors.OrangeRed.g * 255f)).ToString("x2"), | ((int)(XKCDColors.OrangeRed.g * 255f)).ToString("x2"), |
((int)(XKCDColors.OrangeRed.b * 255f)).ToString("x2"), | ((int)(XKCDColors.OrangeRed.b * 255f)).ToString("x2"), |
((int)(XKCDColors.OrangeRed.a * 255f)).ToString("x2"), | ((int)(XKCDColors.OrangeRed.a * 255f)).ToString("x2"), |
ErrorText | ErrorText |
); | ); |
Tools.PostDebugMessage(this.GetType().Name + ": " + this.ErrorMsg.message); | Tools.PostDebugMessage(this.GetType().Name + ": " + this.ErrorMsg.message); |
ScreenMessages.PostScreenMessage(this.ErrorMsg, false); | ScreenMessages.PostScreenMessage(this.ErrorMsg, false); |
} | } |
// Before transmission, set packetResourceCost. Per above, packet cost increases with the square of | // Before transmission, set packetResourceCost. Per above, packet cost increases with the square of |
// distance. packetResourceCost maxes out at _basepacketResourceCost * maxPowerFactor, at which point | // distance. packetResourceCost maxes out at _basepacketResourceCost * maxPowerFactor, at which point |
// transmission fails (see CanTransmit). | // transmission fails (see CanTransmit). |
protected void PreTransmit_SetPacketResourceCost() | private void PreTransmit_SetPacketResourceCost() |
{ | { |
if (fixedPowerCost || this.transmitDistance <= this.nominalRange) | if (ARConfiguration.FixedPowerCost || this.transmitDistance <= this.nominalRange) |
{ | { |
base.packetResourceCost = this._basepacketResourceCost; | base.packetResourceCost = this._basepacketResourceCost; |
} | } |
else | else |
{ | { |
float rangeFactor = (float)(this.transmitDistance / this.nominalRange); | |
rangeFactor *= rangeFactor; | |
base.packetResourceCost = this._basepacketResourceCost | base.packetResourceCost = this._basepacketResourceCost |
* (float)Math.Pow (this.transmitDistance / this.nominalRange, 2); | * rangeFactor; |
} | } |
base.packetResourceCost *= this.packetThrottle / 100f; | base.packetResourceCost *= this.packetThrottle / 100f; |
} | } |
// Before transmission, set packetSize. Per above, packet size increases with the inverse square of | // Before transmission, set packetSize. Per above, packet size increases with the inverse square of |
// distance. packetSize maxes out at _basepacketSize * maxDataFactor. | // distance. packetSize maxes out at _basepacketSize * maxDataFactor. |
protected void PreTransmit_SetPacketSize() | private void PreTransmit_SetPacketSize() |
{ | { |
if (!fixedPowerCost && this.transmitDistance >= this.nominalRange) | if (!ARConfiguration.FixedPowerCost && this.transmitDistance >= this.nominalRange) |
{ | { |
base.packetSize = this._basepacketSize; | base.packetSize = this._basepacketSize; |
} | } |
else | else |
{ | { |
base.packetSize = Math.Min( | float rangeFactor = (float)(this.nominalRange / this.transmitDistance); |
this._basepacketSize * (float)Math.Pow (this.nominalRange / this.transmitDistance, 2), | rangeFactor *= rangeFactor; |
base.packetSize = Mathf.Min( | |
this._basepacketSize * rangeFactor, | |
this._basepacketSize * this.maxDataFactor); | this._basepacketSize * this.maxDataFactor); |
} | } |
base.packetSize *= this.packetThrottle / 100f; | base.packetSize *= this.packetThrottle / 100f; |
} | } |
// Override ModuleDataTransmitter.GetInfo to add nominal and maximum range to the VAB description. | private string buildTransmitMessage() |
public override string GetInfo() | { |
{ | StringBuilder sb = Tools.GetStringBuilder(); |
string text = base.GetInfo(); | string msg; |
text += "Nominal Range: " + Tools.MuMech_ToSI((double)this.nominalRange, 2) + "m\n"; | |
text += "Maximum Range: " + Tools.MuMech_ToSI((double)this.maxTransmitDistance, 2) + "m\n"; | sb.Append("["); |
return text; | sb.Append(base.part.partInfo.title); |
} | sb.Append("]: "); |
// Override ModuleDataTransmitter.CanTransmit to return false when transmission is not possible. | sb.Append("Beginning transmission "); |
public new bool CanTransmit() | |
{ | if (this.KerbinDirect) |
PartStates partState = this.part.State; | { |
if (partState == PartStates.DEAD || partState == PartStates.DEACTIVATED) | sb.Append("directly to Kerbin."); |
{ | |
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(); | |
} | |
// Override ModuleDataTransmitter.TransmitData to check against CanTransmit and fail out when CanTransmit | |
// 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 | else |
{ | { |
this.PostCannotTransmitError (); | sb.Append("via "); |
} | sb.Append(this.relay.targetRelay); |
} | |
Tools.PostDebugMessage ( | |
"distance: " + this.transmitDistance | msg = sb.ToString(); |
+ " packetSize: " + this.packetSize | |
+ " packetResourceCost: " + this.packetResourceCost | Tools.PutStringBuilder(sb); |
); | |
} | return msg; |
} | |
// Override ModuleDataTransmitter.StartTransmission to check against CanTransmit and fail out when CanTransmit | |
// returns false. | #if DEBUG |
public new void StartTransmission() | // When debugging, it's nice to have a button that just tells you everything. |
[KSPEvent (guiName = "Show Debug Info", active = true, guiActive = true)] | |
public void DebugInfo() | |
{ | { |
PreTransmit_SetPacketSize (); | PreTransmit_SetPacketSize (); |
PreTransmit_SetPacketResourceCost (); | PreTransmit_SetPacketResourceCost (); |
Tools.PostDebugMessage ( | DebugPartModule.DumpClassObject(this); |
"distance: " + this.transmitDistance | |
+ " packetSize: " + this.packetSize | |
+ " packetResourceCost: " + this.packetResourceCost | |
); | |
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.StartTransmission(); | |
} | |
else | |
{ | |
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. | |
#if DEBUG | |
[KSPEvent (guiName = "Show Debug Info", active = true, guiActive = true)] | |
public void DebugInfo() | |
{ | |
PreTransmit_SetPacketSize (); | |
PreTransmit_SetPacketResourceCost (); | |
string msg = string.Format( | |
"'{0}'\n" + | |
"_basepacketSize: {1}\n" + | |
"packetSize: {2}\n" + | |
"_basepacketResourceCost: {3}\n" + | |
"packetResourceCost: {4}\n" + | |
"maxTransmitDistance: {5}\n" + | |
"transmitDistance: {6}\n" + | |
"nominalRange: {7}\n" + | |
"CanTransmit: {8}\n" + | |
"DataRate: {9}\n" + | |
"DataResourceCost: {10}\n" + | |
"TransmitterScore: {11}\n" + | |
"NearestRelay: {12}\n" + | |
"Vessel ID: {13}", | |
this.name, | |
this._basepacketSize, | |
base.packetSize, | |
this._basepacketResourceCost, | |
base.packetResourceCost, | |
this.maxTransmitDistance, | |
this.transmitDistance, | |
this.nominalRange, | |
this.CanTransmit(), | |
this.DataRate, | |
this.DataResourceCost, | |
ScienceUtil.GetTransmitterScore(this), | |
this.relay.FindNearestRelay(), | |
this.vessel.id | |
); | |
Tools.PostDebugMessage(msg); | |
} | } |
[KSPEvent (guiName = "Dump Vessels", active = true, guiActive = true)] | [KSPEvent (guiName = "Dump Vessels", active = true, guiActive = true)] |
public void PrintAllVessels() | public void PrintAllVessels() |
{ | { |
StringBuilder sb = new StringBuilder(); | StringBuilder sb = Tools.GetStringBuilder(); |
sb.Append("Dumping FlightGlobals.Vessels:"); | sb.Append("Dumping FlightGlobals.Vessels:"); |
foreach (Vessel vessel in FlightGlobals.Vessels) | Vessel vessel; |
{ | for (int i = 0; i < FlightGlobals.Vessels.Count; i++) |
{ | |
vessel = FlightGlobals.Vessels[i]; | |
sb.AppendFormat("\n'{0} ({1})'", vessel.vesselName, vessel.id); | sb.AppendFormat("\n'{0} ({1})'", vessel.vesselName, vessel.id); |
} | } |
Tools.PostDebugMessage(sb.ToString()); | Tools.PostDebugMessage(sb.ToString()); |
} | |
Tools.PutStringBuilder(sb); | |
} | |
[KSPEvent (guiName = "Dump RelayDB", active = true, guiActive = true)] | [KSPEvent (guiName = "Dump RelayDB", active = true, guiActive = true)] |
public void DumpRelayDB() | public void DumpRelayDB() |
{ | { |
RelayDatabase.Instance.Dump(); | RelayDatabase.Instance.Dump(); |
} | } |
#endif | #endif |
} | } |
} | } |
// AntennaRange | // AntennaRange |
// | // |
// AssemblyInfo.cs | // AssemblyInfo.cs |
// | // |
// Copyright © 2014, toadicus | // Copyright © 2014-2015, toadicus |
// All rights reserved. | // All rights reserved. |
// | // |
// Redistribution and use in source and binary forms, with or without modification, | // Redistribution and use in source and binary forms, with or without modification, |
// are permitted provided that the following conditions are met: | // are permitted provided that the following conditions are met: |
// | // |
// 1. Redistributions of source code must retain the above copyright notice, | // 1. Redistributions of source code must retain the above copyright notice, |
// this list of conditions and the following disclaimer. | // this list of conditions and the following disclaimer. |
// | // |
// 2. Redistributions in binary form must reproduce the above copyright notice, | // 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 | // this list of conditions and the following disclaimer in the documentation and/or other |
// materials provided with the distribution. | // materials provided with the distribution. |
// | // |
// 3. Neither the name of the copyright holder nor the names of its contributors may be used | // 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. | // 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, | // 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 | // 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, | // 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 | // 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, | // 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 | // 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. | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
using System.Reflection; | using System.Reflection; |
using System.Runtime.CompilerServices; | using System.Runtime.CompilerServices; |
[assembly: KSPAssemblyDependency("ToadicusTools", 0, 0)] | [assembly: KSPAssemblyDependency("ToadicusTools", 0, 0)] |
// Information about this assembly is defined by the following attributes. | // Information about this assembly is defined by the following attributes. |
// Change them to the values specific to your project. | // Change them to the values specific to your project. |
[assembly: AssemblyTitle("AntennaRange")] | [assembly: AssemblyTitle("AntennaRange")] |
[assembly: AssemblyDescription("Enforce and Encourage Antenna Diversity")] | [assembly: AssemblyDescription("Enforce and Encourage Antenna Diversity")] |
[assembly: AssemblyCopyright("toadicus")] | [assembly: AssemblyCopyright("toadicus")] |
// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". | // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". |
// The form "{Major}.{Minor}.*" will automatically update the build and revision, | // The form "{Major}.{Minor}.*" will automatically update the build and revision, |
// and "{Major}.{Minor}.{Build}.*" will update just the revision. | // and "{Major}.{Minor}.{Build}.*" will update just the revision. |
[assembly: AssemblyVersion("1.2.*")] | [assembly: AssemblyVersion("1.9.*")] |
// The following attributes are used to specify the signing key for the assembly, | // The following attributes are used to specify the signing key for the assembly, |
// if desired. See the Mono documentation for more information about signing. | // if desired. See the Mono documentation for more information about signing. |
//[assembly: AssemblyDelaySign(false)] | //[assembly: AssemblyDelaySign(false)] |
//[assembly: AssemblyKeyFile("")] | //[assembly: AssemblyKeyFile("")] |
// AntennaRange | // AntennaRange |
// | // |
// ProtoAntennaRelay.cs | // ProtoAntennaRelay.cs |
// | // |
// Copyright © 2014, toadicus | // Copyright © 2014-2015, toadicus |
// All rights reserved. | // All rights reserved. |
// | // |
// Redistribution and use in source and binary forms, with or without modification, | // Redistribution and use in source and binary forms, with or without modification, |
// are permitted provided that the following conditions are met: | // are permitted provided that the following conditions are met: |
// | // |
// 1. Redistributions of source code must retain the above copyright notice, | // 1. Redistributions of source code must retain the above copyright notice, |
// this list of conditions and the following disclaimer. | // this list of conditions and the following disclaimer. |
// | // |
// 2. Redistributions in binary form must reproduce the above copyright notice, | // 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 | // this list of conditions and the following disclaimer in the documentation and/or other |
// materials provided with the distribution. | // materials provided with the distribution. |
// | // |
// 3. Neither the name of the copyright holder nor the names of its contributors may be used | // 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. | // 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, | // 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 | // 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, | // 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 | // 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, | // 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 | // 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. | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
using KSP; | using KSP; |
using System; | using System; |
using System.Linq; | |
using ToadicusTools; | using ToadicusTools; |
namespace AntennaRange | namespace AntennaRange |
{ | { |
/* | /// <summary> |
* Wrapper class for ProtoPartModuleSnapshot extending AntennaRelay and implementing IAntennaRelay. | /// Wrapper class for ProtoPartModuleSnapshot extending AntennaRelay and implementing IAntennaRelay. |
* This is used for finding relays in unloaded Vessels. | /// This is used for finding relays in unloaded Vessels. |
* */ | /// </summary> |
public class ProtoAntennaRelay : AntennaRelay, IAntennaRelay | public class ProtoAntennaRelay : AntennaRelay, IAntennaRelay |
{ | { |
// Stores the prototype part so we can make sure we haven't exploded or so. | // Stores the prototype part so we can make sure we haven't exploded or so. |
protected ProtoPartSnapshot protoPart; | private ProtoPartSnapshot protoPart; |
/// <summary> | |
/// Gets the parent Vessel. | |
/// </summary> | |
public override Vessel vessel | public override Vessel vessel |
{ | { |
get | get |
{ | { |
return this.protoPart.pVesselRef.vesselRef; | if ( |
this.protoPart != null && | |
this.protoPart.pVesselRef != null && | |
this.protoPart.pVesselRef.vesselRef != null | |
) | |
{ | |
return this.protoPart.pVesselRef.vesselRef; | |
} | |
else | |
{ | |
Tools.PostErrorMessage("{0}: Could not fetch vessel! {1}{2}{3}", | |
this.ToString(), | |
this.protoPart == null ? "\n\tprotoPart=null" : string.Empty, | |
this.protoPart != null && this.protoPart.pVesselRef == null ? | |
"\n\tthis.protoPart.pVesselRef=null" : string.Empty, | |
this.protoPart != null && this.protoPart.pVesselRef != null && | |
this.protoPart.pVesselRef.vesselRef == null ? | |
"\n\tthis.protoPart.pVesselRef.vesselRef=null" : string.Empty | |
); | |
return null; | |
} | |
} | } |
} | } |
/// <summary> | /// <summary> |
/// The maximum distance at which this transmitter can operate. | /// Gets the nominal transmit distance at which the Antenna behaves just as prescribed by Squad's config. |
/// </summary> | /// </summary> |
/// <value>The max transmit distance.</value> | public override double nominalTransmitDistance |
public override float maxTransmitDistance | { |
get | |
{ | |
return this.moduleRef.nominalTransmitDistance; | |
} | |
} | |
/// <summary> | |
/// The maximum distance at which this relay can operate. | |
/// </summary> | |
public override double maxTransmitDistance | |
{ | { |
get | get |
{ | { |
return moduleRef.maxTransmitDistance; | return moduleRef.maxTransmitDistance; |
} | } |
} | } |
/// <summary> | /// <summary> |
/// Gets a value indicating whether this <see cref="AntennaRange.ProtoDataTransmitter"/> has been checked during | /// Gets the underlying part's title. |
/// the current relay attempt. | |
/// </summary> | /// </summary> |
/// <value><c>true</c> if relay checked; otherwise, <c>false</c>.</value> | /// <value>The title.</value> |
public override bool relayChecked | public string Title |
{ | { |
get; | get |
protected set; | { |
if (this.protoPart != null && this.protoPart.partInfo != null) | |
{ | |
return this.protoPart.partInfo.title; | |
} | |
return string.Empty; | |
} | |
} | } |
/// <summary> | /// <summary> |
/// Gets the underlying part's title. | /// Determines whether this instance can transmit. |
/// <c>true</c> if this instance can transmit; otherwise, <c>false</c>. | |
/// </summary> | /// </summary> |
/// <value>The title.</value> | |
public string title | |
{ | |
get | |
{ | |
return this.protoPart.partInfo.title; | |
} | |
} | |
public override bool CanTransmit() | public override bool CanTransmit() |
{ | { |
PartStates partState = (PartStates)this.protoPart.state; | PartStates partState = (PartStates)this.protoPart.state; |
if (partState == PartStates.DEAD || partState == PartStates.DEACTIVATED) | if (partState == PartStates.DEAD || partState == PartStates.DEACTIVATED) |
{ | { |
Tools.PostDebugMessage(string.Format( | Tools.PostDebugMessage(string.Format( |
"{0}: {1} on {2} cannot transmit: {3}", | "{0}: {1} on {2} cannot transmit: {3}", |
this.GetType().Name, | this.GetType().Name, |
this.title, | this.Title, |
this.vessel.vesselName, | this.vessel.vesselName, |
Enum.GetName(typeof(PartStates), partState) | Enum.GetName(typeof(PartStates), partState) |
)); | )); |
return false; | return false; |
} | } |
return base.CanTransmit(); | return base.CanTransmit(); |
} | } |
/// <summary> | |
/// Returns a <see cref="System.String"/> that represents the current <see cref="AntennaRange.ProtoAntennaRelay"/>. | |
/// </summary> | |
/// <returns>A <see cref="System.String"/> that represents the current <see cref="AntennaRange.ProtoAntennaRelay"/>.</returns> | |
public override string ToString() | public override string ToString() |
{ | { |
return string.Format( | System.Text.StringBuilder sb = Tools.GetStringBuilder(); |
"{0} on {1} (proto)", | |
this.title, | sb.Append(this.Title); |
this.protoPart.pVesselRef.vesselName | |
); | if (this.protoPart != null && this.protoPart.pVesselRef != null) |
{ | |
sb.AppendFormat(" on {0}", this.protoPart.pVesselRef.vesselName); | |
} | |
Tools.PutStringBuilder(sb); | |
return sb.ToString(); | |
} | } |
/// <summary> | /// <summary> |
/// Initializes a new instance of the <see cref="AntennaRange.ProtoAntennaRelay"/> class. | /// Initializes a new instance of the <see cref="AntennaRange.AntennaRelay"/> class. |
/// </summary> | /// </summary> |
/// <param name="ms">The ProtoPartModuleSnapshot to wrap</param> | /// <param name="prefabRelay">The module reference underlying this AntennaRelay, |
/// <param name="vessel">The parent Vessel</param> | /// as an <see cref="AntennaRange.IAntennaRelay"/></param> |
/// <param name="pps">The prototype partreference on which the module resides.</param> | |
public ProtoAntennaRelay(IAntennaRelay prefabRelay, ProtoPartSnapshot pps) : base(prefabRelay) | public ProtoAntennaRelay(IAntennaRelay prefabRelay, ProtoPartSnapshot pps) : base(prefabRelay) |
{ | { |
this.protoPart = pps; | this.protoPart = pps; |
} | |
~ProtoAntennaRelay() | Tools.PostLogMessage("{0}: constructed {1}", this.GetType().Name, this.ToString()); |
{ | |
Tools.PostDebugMessage(string.Format( | |
"{0}: destroyed", | |
this.ToString() | |
)); | |
} | } |
} | } |
} | } |
// AntennaRange | // AntennaRange |
// | // |
// RelayDatabase.cs | // RelayDatabase.cs |
// | // |
// Copyright © 2014, toadicus | // Copyright © 2014-2015, toadicus |
// All rights reserved. | // All rights reserved. |
// | // |
// Redistribution and use in source and binary forms, with or without modification, | // Redistribution and use in source and binary forms, with or without modification, |
// are permitted provided that the following conditions are met: | // are permitted provided that the following conditions are met: |
// | // |
// 1. Redistributions of source code must retain the above copyright notice, | // 1. Redistributions of source code must retain the above copyright notice, |
// this list of conditions and the following disclaimer. | // this list of conditions and the following disclaimer. |
// | // |
// 2. Redistributions in binary form must reproduce the above copyright notice, | // 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 | // this list of conditions and the following disclaimer in the documentation and/or other |
// materials provided with the distribution. | // materials provided with the distribution. |
// | // |
// 3. Neither the name of the copyright holder nor the names of its contributors may be used | // 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. | // 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, | // 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 | // 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, | // 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 | // 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, | // 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 | // 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. | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
#pragma warning disable 1591 | |
using KSP; | using KSP; |
using System; | using System; |
using System.Collections.Generic; | using System.Collections.Generic; |
using System.Text; | using System.Text; |
using ToadicusTools; | using ToadicusTools; |
using UnityEngine; | using UnityEngine; |
namespace AntennaRange | namespace AntennaRange |
{ | { |
public class RelayDatabase | public class RelayDatabase : Singleton<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 | * Instance members |
* */ | * */ |
/* | /* |
* Fields | * Fields |
* */ | * */ |
// Vessel.id-keyed hash table of Part.GetHashCode()-keyed tables of relay objects. | // Vessel.id-keyed hash table of Part.GetHashCode()-keyed tables of relay objects. |
protected Dictionary<Guid, Dictionary<int, IAntennaRelay>> relayDatabase; | private Dictionary<Guid, List<IAntennaRelay>> relayDatabase; |
private Dictionary<Guid, IAntennaRelay> bestRelayTable; | |
// Vessel.id-keyed hash table of part counts, used for caching | // Vessel.id-keyed hash table of part counts, used for caching |
protected Dictionary<Guid, int> vesselPartCountTable; | private Dictionary<Guid, int> vesselPartCountTable; |
// Vessel.id-keyed hash table of booleans to track what vessels have been checked so far this time. | private int cacheHits; |
public Dictionary<Guid, bool> CheckedVesselsTable; | private int cacheMisses; |
protected int cacheHits; | |
protected int cacheMisses; | |
/* | /* |
* Properties | * Properties |
* */ | * */ |
// Gets the Part-hashed table of relays in a given vessel | // Gets the Part-hashed table of relays in a given vessel |
public Dictionary<int, IAntennaRelay> this [Vessel vessel] | public IList<IAntennaRelay> this [Vessel vessel] |
{ | { |
get | get |
{ | { |
// If we don't have an entry for this vessel... | // If we don't have an entry for this vessel... |
if (!this.ContainsKey(vessel.id)) | if (!this.ContainsKey(vessel.id)) |
{ | { |
// ...Generate an entry for this vessel. | // ...Generate an entry for this vessel. |
this.AddVessel(vessel); | this.AddVessel(vessel); |
this.cacheMisses++; | this.cacheMisses++; |
} | } |
// If our part count disagrees with the vessel's part count... | // If our part count disagrees with the vessel's part count... |
else if (this.vesselPartCountTable[vessel.id] != vessel.Parts.Count) | else if (this.vesselPartCountTable[vessel.id] != vessel.Parts.Count) |
{ | { |
// ...Update the our vessel in the cache | // ...Update the our vessel in the cache |
this.UpdateVessel(vessel); | this.UpdateVessel(vessel); |
this.cacheMisses++; | this.cacheMisses++; |
} | } |
// Otherwise, it's a hit | // Otherwise, it's a hit |
else | else |
{ | { |
this.cacheHits++; | this.cacheHits++; |
} | } |
// Return the Part-hashed table of relays for this vessel | // Return the Part-hashed table of relays for this vessel |
return relayDatabase[vessel.id]; | return relayDatabase[vessel.id].AsReadOnly(); |
} | } |
} | } |
/* | /* |
* Methods | * Methods |
* */ | * */ |
// Remove a vessel from the cache, if it exists. | |
public void DirtyVessel(Vessel vessel) | |
{ | |
#if DEBUG | |
Tools.PostDebugMessage("RelayDatabase: Dirtying cache for vessel {0} in frame {1}", | |
vessel, new System.Diagnostics.StackTrace().ToString()); | |
#else | |
Tools.PostLogMessage("RelayDatabase: Dirtying cache for vessel {0}", vessel.vesselName); | |
#endif | |
this.relayDatabase.Remove(vessel.id); | |
this.vesselPartCountTable.Remove(vessel.id); | |
this.bestRelayTable.Remove(vessel.id); | |
} | |
public void ClearCache() | |
{ | |
Tools.PostLogMessage("RelayDatabase: onSceneChange clearing entire cache."); | |
this.relayDatabase.Clear(); | |
this.bestRelayTable.Clear(); | |
this.vesselPartCountTable.Clear(); | |
} | |
// 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); | |
} | |
public IAntennaRelay GetBestVesselRelay(Vessel vessel) | |
{ | |
IAntennaRelay relay; | |
if (this.bestRelayTable.TryGetValue(vessel.id, out relay)) | |
{ | |
return relay; | |
} | |
else | |
{ | |
var dump = this[vessel]; | |
return null; | |
} | |
} | |
// Adds a vessel to the database | // Adds a vessel to the database |
// The return for this function isn't used yet, but seems useful for potential future API-uses | // The return for this function isn't used yet, but seems useful for potential future API-uses |
public bool AddVessel(Vessel vessel) | private bool AddVessel(Vessel vessel) |
{ | { |
// If this vessel is already here... | // If this vessel is already here... |
if (this.ContainsKey(vessel)) | if (this.ContainsKey(vessel)) |
{ | { |
// ...post an error | // ...post an error |
Debug.LogWarning(string.Format( | Debug.LogWarning(string.Format( |
"{0}: Cannot add vessel '{1}' (id: {2}): Already in database.", | "{0}: Cannot add vessel '{1}' (id: {2}): Already in database.", |
this.GetType().Name, | this.GetType().Name, |
vessel.vesselName, | vessel.vesselName, |
vessel.id | vessel.id |
)); | )); |
// ...and refuse to add | // ...and refuse to add |
return false; | return false; |
} | } |
// otherwise, add the vessel to our tables... | // otherwise, add the vessel to our tables... |
else | else |
{ | { |
// Build an empty table... | // Build an empty table... |
this.relayDatabase[vessel.id] = new Dictionary<int, IAntennaRelay>(); | this.relayDatabase[vessel.id] = new List<IAntennaRelay>(); |
// Update the empty index | // Update the empty index |
this.UpdateVessel(vessel); | this.UpdateVessel(vessel); |
// Return success | // Return success |
return true; | return true; |
} | } |
} | } |
// Update the vessel's entry in the table | // Update the vessel's entry in the table |
public void UpdateVessel(Vessel vessel) | private void UpdateVessel(Vessel vessel) |
{ | { |
// Squak if the database doesn't have the vessel | // Squak if the database doesn't have the vessel |
if (!this.ContainsKey(vessel)) | if (!this.ContainsKey(vessel)) |
{ | { |
throw new InvalidOperationException(string.Format( | throw new InvalidOperationException(string.Format( |
"{0}: Update called for vessel '{1}' (id: {2}) not in database: vessel will be added.", | "{0}: Update called for vessel '{1}' (id: {2}) not in database: vessel will be added.", |
this.GetType().Name, | this.GetType().Name, |
vessel.vesselName, | vessel.vesselName, |
vessel.id | vessel.id |
)); | )); |
} | } |
Dictionary<int, IAntennaRelay> vesselTable = this.relayDatabase[vessel.id]; | List<IAntennaRelay> vesselTable = this.relayDatabase[vessel.id]; |
// Actually build and assign the table | // Actually build and assign the table |
this.getVesselRelays(vessel, ref vesselTable); | this.getVesselRelays(vessel, ref vesselTable); |
// Set the part count | // Set the part count |
this.vesselPartCountTable[vessel.id] = vessel.Parts.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) | // Runs when a vessel is modified (or when we switch to one, to catch docking events) |
public void onVesselEvent(Vessel vessel) | public void onVesselEvent(Vessel vessel) |
{ | { |
// If we have this vessel in our cache... | // If we have this vessel in our cache... |
if (this.ContainsKey(vessel)) | if (this.ContainsKey(vessel)) |
{ | { |
// If our part counts disagree (such as if a part has been added or broken off, | // If our part counts disagree (such as if a part has been added or broken off, |
// or if we've just docked or undocked)... | // or if we've just docked or undocked)... |
if (this.vesselPartCountTable[vessel.id] != vessel.Parts.Count || vessel.loaded) | if (this.vesselPartCountTable[vessel.id] != vessel.Parts.Count || vessel.loaded) |
{ | { |
Tools.PostDebugMessage(string.Format( | Tools.PostDebugMessage(string.Format( |
"{0}: dirtying cache for vessel '{1}' ({2}).", | "{0}: dirtying cache for vessel '{1}' ({2}).", |
this.GetType().Name, | this.GetType().Name, |
vessel.vesselName, | vessel.vesselName, |
vessel.id | vessel.id |
)); | )); |
// Dirty the cache (real vessels will never have negative part counts) | // Dirty the cache (real vessels will never have negative part counts) |
this.DirtyVessel(vessel); | this.DirtyVessel(vessel); |
} | } |
} | } |
} | } |
// Runs when the player requests a scene change, such as when changing vessels or leaving flight. | // Runs when the player requests a scene change, such as when changing vessels or leaving flight. |
public void onSceneChange(GameScenes scene) | private void onSceneChange(GameScenes scene) |
{ | { |
// If the active vessel is a real thing... | Tools.PostDebugMessage( |
if (FlightGlobals.ActiveVessel != null) | "RelayDatabase: caught onSceneChangeRequested in scene {0} to scene {1}. ActiveVessel is {2}", |
{ | HighLogic.LoadedScene, |
// ... dirty its cache | scene, |
this.onVesselEvent(FlightGlobals.ActiveVessel); | FlightGlobals.ActiveVessel == null ? "null" : FlightGlobals.ActiveVessel.vesselName |
} | ); |
if (scene == GameScenes.FLIGHT) | |
{ | |
if (scene == HighLogic.LoadedScene) | |
{ | |
if (FlightGlobals.ActiveVessel != null) | |
{ | |
Tools.PostDebugMessage("RelayDatabase: onSceneChange clearing {0} from cache.", | |
FlightGlobals.ActiveVessel.vesselName); | |
this.onVesselEvent(FlightGlobals.ActiveVessel); | |
} | |
} | |
else | |
{ | |
this.ClearCache(); | |
} | |
} | |
} | |
private void onGameLoaded(object data) | |
{ | |
this.ClearCache(); | |
} | } |
// Runs when parts are undocked | // Runs when parts are undocked |
public void onPartEvent(Part part) | private void onPartEvent(Part part) |
{ | { |
if (part != null && part.vessel != null) | if (part != null && part.vessel != null) |
{ | { |
this.onVesselEvent(part.vessel); | this.onVesselEvent(part.vessel); |
} | } |
} | } |
// Runs when parts are coupled, as in docking | // Runs when parts are coupled, as in docking |
public void onFromPartToPartEvent(GameEvents.FromToAction<Part, Part> data) | private void onFromPartToPartEvent(GameEvents.FromToAction<Part, Part> data) |
{ | { |
this.onPartEvent(data.from); | this.onPartEvent(data.from); |
this.onPartEvent(data.to); | this.onPartEvent(data.to); |
} | } |
// Produce a Part-hashed table of relays for the given vessel | // Produce a Part-hashed table of relays for the given vessel |
protected void getVesselRelays(Vessel vessel, ref Dictionary<int, IAntennaRelay> relays) | private void getVesselRelays(Vessel vessel, ref List<IAntennaRelay> relays) |
{ | { |
// We're going to completely regen this table, so dump the current contents. | // We're going to completely regen this table, so dump the current contents. |
relays.Clear(); | relays.Clear(); |
Tools.PostDebugMessage(string.Format( | Tools.PostDebugMessage(string.Format( |
"{0}: Getting antenna relays from vessel {1}.", | "{0}: Getting antenna relays from vessel {1}.", |
"IAntennaRelay", | "IAntennaRelay", |
vessel.vesselName | vessel.vesselName |
)); | )); |
double bestRelayRange = double.NegativeInfinity; | |
IAntennaRelay bestRelay = null; | |
IAntennaRelay relay; | |
// If the vessel is loaded, we can fetch modules implementing IAntennaRelay directly. | // If the vessel is loaded, we can fetch modules implementing IAntennaRelay directly. |
if (vessel.loaded) { | if (vessel.loaded) { |
Tools.PostDebugMessage(string.Format( | Tools.PostDebugMessage(string.Format( |
"{0}: vessel {1} is loaded, searching for modules in loaded parts.", | "{0}: vessel {1} is loaded, searching for modules in loaded parts.", |
"IAntennaRelay", | "IAntennaRelay", |
vessel.vesselName | vessel.vesselName |
)); | )); |
// Loop through the Parts in the Vessel... | // Loop through the Parts in the Vessel... |
foreach (Part part in vessel.Parts) | Part part; |
{ | for (int partIdx = 0; partIdx < vessel.Parts.Count; partIdx++) |
{ | |
part = vessel.Parts[partIdx]; | |
// ...loop through the PartModules in the Part... | // ...loop through the PartModules in the Part... |
foreach (PartModule module in part.Modules) | PartModule module; |
for (int modIdx = 0; modIdx < part.Modules.Count; modIdx++) | |
{ | { |
module = part.Modules[modIdx]; | |
// ...if the module is a relay... | // ...if the module is a relay... |
if (module is IAntennaRelay) | if (module is IAntennaRelay) |
{ | { |
relay = (module as IAntennaRelay); | |
if (relay.maxTransmitDistance > bestRelayRange) | |
{ | |
bestRelayRange = relay.maxTransmitDistance; | |
bestRelay = relay; | |
} | |
// ...add the module to the table | // ...add the module to the table |
relays.Add(part.GetHashCode(), module as IAntennaRelay); | relays.Add(relay); |
// ...neglect relay objects after the first in each part. | // ...neglect relay objects after the first in each part. |
break; | break; |
} | } |
} | } |
} | } |
} | } |
// If the vessel is not loaded, we need to build ProtoAntennaRelays when we find relay ProtoPartSnapshots. | // If the vessel is not loaded, we need to build ProtoAntennaRelays when we find relay ProtoPartSnapshots. |
else | else |
{ | { |
Tools.PostDebugMessage(string.Format( | Tools.PostDebugMessage(string.Format( |
"{0}: vessel {1} is not loaded, searching for modules in prototype parts.", | "{0}: vessel {1} is not loaded, searching for modules in prototype parts.", |
this.GetType().Name, | this.GetType().Name, |
vessel.vesselName | vessel.vesselName |
)); | )); |
// Loop through the ProtoPartModuleSnapshots in the Vessel... | // Loop through the ProtoPartModuleSnapshots in the Vessel... |
foreach (ProtoPartSnapshot pps in vessel.protoVessel.protoPartSnapshots) | ProtoPartSnapshot pps; |
{ | for (int ppsIdx = 0; ppsIdx < vessel.protoVessel.protoPartSnapshots.Count; ppsIdx++) |
{ | |
pps = vessel.protoVessel.protoPartSnapshots[ppsIdx]; | |
Tools.PostDebugMessage(string.Format( | Tools.PostDebugMessage(string.Format( |
"{0}: Searching in protopartsnapshot {1}", | "{0}: Searching in protopartsnapshot {1}", |
this.GetType().Name, | this.GetType().Name, |
pps | pps |
)); | )); |
// ...Fetch the prefab, because it's more useful for what we're doing. | // ...Fetch the prefab, because it's more useful for what we're doing. |
Part partPrefab = PartLoader.getPartInfoByName(pps.partName).partPrefab; | Part partPrefab = PartLoader.getPartInfoByName(pps.partName).partPrefab; |
Tools.PostDebugMessage(string.Format( | Tools.PostDebugMessage(string.Format( |
"{0}: Got partPrefab {1} in protopartsnapshot {2}", | "{0}: Got partPrefab {1} in protopartsnapshot {2}", |
this.GetType().Name, | this.GetType().Name, |
partPrefab, | partPrefab, |
pps | pps |
)); | )); |
// ...loop through the PartModules in the prefab... | // ...loop through the PartModules in the prefab... |
foreach (PartModule module in partPrefab.Modules) | PartModule module; |
for (int modIdx = 0; modIdx < partPrefab.Modules.Count; modIdx++) | |
{ | { |
module = partPrefab.Modules[modIdx]; | |
Tools.PostDebugMessage(string.Format( | Tools.PostDebugMessage(string.Format( |
"{0}: Searching in partmodule {1}", | "{0}: Searching in partmodule {1}", |
this.GetType().Name, | this.GetType().Name, |
module | module |
)); | )); |
// ...if the module is a relay... | // ...if the module is a relay... |
if (module is IAntennaRelay) | if (module is IAntennaRelay) |
{ | { |
Tools.PostDebugMessage(string.Format( | Tools.PostDebugMessage(string.Format( |
"{0}: partmodule {1} is antennarelay", | "{0}: partmodule {1} is antennarelay", |
this.GetType().Name, | this.GetType().Name, |
module | module |
)); | )); |
relay = new ProtoAntennaRelay(module as IAntennaRelay, pps); | |
if (relay.maxTransmitDistance > bestRelayRange) | |
{ | |
bestRelayRange = relay.maxTransmitDistance; | |
bestRelay = relay; | |
} | |
// ...build a new ProtoAntennaRelay and add it to the table | // ...build a new ProtoAntennaRelay and add it to the table |
relays.Add(pps.GetHashCode(), new ProtoAntennaRelay(module as IAntennaRelay, pps)); | relays.Add(relay); |
// ...neglect relay objects after the first in each part. | // ...neglect relay objects after the first in each part. |
break; | break; |
} | } |
} | } |
} | } |
} | } |
this.bestRelayTable[vessel.id] = bestRelay; | |
Tools.PostDebugMessage(string.Format( | Tools.PostDebugMessage(string.Format( |
"{0}: vessel '{1}' ({2}) has {3} transmitters.", | "{0}: vessel '{1}' ({2}) has {3} transmitters.", |
"IAntennaRelay", | "IAntennaRelay", |
vessel.vesselName, | vessel.vesselName, |
vessel.id, | vessel.id, |
relays.Count | relays.Count |
)); | )); |
} | } |
// Construct the singleton | // Construct the singleton |
protected RelayDatabase() | private RelayDatabase() |
{ | { |
// Initialize the databases | // Initialize the databases |
this.relayDatabase = new Dictionary<Guid, Dictionary<int, IAntennaRelay>>(); | this.relayDatabase = new Dictionary<Guid, List<IAntennaRelay>>(); |
this.bestRelayTable = new Dictionary<Guid, IAntennaRelay>(); | |
this.vesselPartCountTable = new Dictionary<Guid, int>(); | this.vesselPartCountTable = new Dictionary<Guid, int>(); |
this.CheckedVesselsTable = new Dictionary<Guid, bool>(); | |
this.cacheHits = 0; | this.cacheHits = 0; |
this.cacheMisses = 0; | this.cacheMisses = 0; |
// Subscribe to some events | // Subscribe to some events |
GameEvents.onVesselWasModified.Add(this.onVesselEvent); | GameEvents.onVesselWasModified.Add(this.onVesselEvent); |
GameEvents.onVesselChange.Add(this.onVesselEvent); | GameEvents.onVesselChange.Add(this.onVesselEvent); |
GameEvents.onVesselDestroy.Add(this.onVesselEvent); | GameEvents.onVesselDestroy.Add(this.onVesselEvent); |
GameEvents.onGameSceneLoadRequested.Add(this.onSceneChange); | GameEvents.onGameSceneLoadRequested.Add(this.onSceneChange); |
GameEvents.onPartCouple.Add(this.onFromPartToPartEvent); | GameEvents.onPartCouple.Add(this.onFromPartToPartEvent); |
GameEvents.onPartUndock.Add(this.onPartEvent); | GameEvents.onPartUndock.Add(this.onPartEvent); |
GameEvents.onGameStateLoad.Add(this.onGameLoaded); | |
} | } |
~RelayDatabase() | ~RelayDatabase() |
{ | { |
// Unsubscribe from the events | // Unsubscribe from the events |
GameEvents.onVesselWasModified.Remove(this.onVesselEvent); | GameEvents.onVesselWasModified.Remove(this.onVesselEvent); |
GameEvents.onVesselChange.Remove(this.onVesselEvent); | GameEvents.onVesselChange.Remove(this.onVesselEvent); |
GameEvents.onVesselDestroy.Remove(this.onVesselEvent); | GameEvents.onVesselDestroy.Remove(this.onVesselEvent); |
GameEvents.onGameSceneLoadRequested.Remove(this.onSceneChange); | GameEvents.onGameSceneLoadRequested.Remove(this.onSceneChange); |
GameEvents.onPartCouple.Remove(this.onFromPartToPartEvent); | GameEvents.onPartCouple.Remove(this.onFromPartToPartEvent); |
GameEvents.onPartUndock.Remove(this.onPartEvent); | GameEvents.onPartUndock.Remove(this.onPartEvent); |
GameEvents.onGameStateLoad.Remove(this.onGameLoaded); | |
Tools.PostDebugMessage(this.GetType().Name + " destroyed."); | Tools.PostDebugMessage(this.GetType().Name + " destroyed."); |
KSPLog.print(string.Format( | KSPLog.print(string.Format( |
"{0} destructed. Cache hits: {1}, misses: {2}, hit rate: {3:P1}", | "{0} destructed. Cache hits: {1}, misses: {2}, hit rate: {3:P1}", |
this.GetType().Name, | this.GetType().Name, |
this.cacheHits, | this.cacheHits, |
this.cacheMisses, | this.cacheMisses, |
(float)this.cacheHits / (float)(this.cacheMisses + this.cacheHits) | (float)this.cacheHits / (float)(this.cacheMisses + this.cacheHits) |
)); | )); |
} | } |
#if DEBUG | #if DEBUG |
public void Dump() | public void Dump() |
{ | { |
StringBuilder sb = new StringBuilder(); | StringBuilder sb = Tools.GetStringBuilder(); |
sb.Append("Dumping RelayDatabase:"); | sb.Append("Dumping RelayDatabase:"); |
foreach (Guid id in this.relayDatabase.Keys) | var dbEnum = this.relayDatabase.GetEnumerator(); |
{ | IList<IAntennaRelay> vesselRelays; |
sb.AppendFormat("\nVessel {0}:", id); | while (dbEnum.MoveNext()) |
{ | |
foreach (IAntennaRelay relay in this.relayDatabase[id].Values) | sb.AppendFormat("\nVessel {0}:", dbEnum.Current.Key); |
{ | |
vesselRelays = dbEnum.Current.Value; | |
IAntennaRelay relay; | |
for (int rIdx = 0; rIdx < vesselRelays.Count; rIdx++) | |
{ | |
relay = vesselRelays[rIdx]; | |
sb.AppendFormat("\n\t{0}", relay.ToString()); | sb.AppendFormat("\n\t{0}", relay.ToString()); |
} | } |
} | } |
Tools.PostDebugMessage(sb.ToString()); | Tools.PostDebugMessage(sb.ToString()); |
Tools.PutStringBuilder(sb); | |
} | } |
#endif | #endif |
} | } |
} | } |
// AntennaRange | // AntennaRange |
// | // |
// Extensions.cs | // Extensions.cs |
// | // |
// Copyright © 2014, toadicus | // Copyright © 2014-2015, toadicus |
// All rights reserved. | // All rights reserved. |
// | // |
// Redistribution and use in source and binary forms, with or without modification, | // Redistribution and use in source and binary forms, with or without modification, |
// are permitted provided that the following conditions are met: | // are permitted provided that the following conditions are met: |
// | // |
// 1. Redistributions of source code must retain the above copyright notice, | // 1. Redistributions of source code must retain the above copyright notice, |
// this list of conditions and the following disclaimer. | // this list of conditions and the following disclaimer. |
// | // |
// 2. Redistributions in binary form must reproduce the above copyright notice, | // 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 | // this list of conditions and the following disclaimer in the documentation and/or other |
// materials provided with the distribution. | // materials provided with the distribution. |
// | // |
// 3. Neither the name of the copyright holder nor the names of its contributors may be used | // 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. | // 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, | // 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 | // 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, | // 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 | // 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, | // 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 | // 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. | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
using System; | using System; |
using System.Collections.Generic; | using System.Collections.Generic; |
using System.Linq; | |
using ToadicusTools; | using ToadicusTools; |
namespace AntennaRange | namespace AntennaRange |
{ | { |
/* | /// <summary> |
* A class of utility extensions for Vessels and Relays to help find a relay path back to Kerbin. | /// A class of utility extensions for Vessels and Relays to help find a relay path back to Kerbin. |
* */ | /// </summary> |
public static class RelayExtensions | public static class RelayExtensions |
{ | { |
/// <summary> | /// <summary> |
/// Returns the distance between this IAntennaRelay and a Vessel | /// Returns the distance between this IAntennaRelay and a Vessel |
/// </summary> | /// </summary> |
/// <param name="relay">This <see cref="IAntennaRelay"/></param> | /// <param name="relay">This <see cref="IAntennaRelay"/></param> |
/// <param name="Vessel">A <see cref="Vessel"/></param> | /// <param name="Vessel">A <see cref="Vessel"/></param> |
public static double DistanceTo(this AntennaRelay relay, Vessel Vessel) | public static double DistanceTo(this AntennaRelay relay, Vessel Vessel) |
{ | { |
return relay.vessel.DistanceTo(Vessel); | return relay.vessel.DistanceTo(Vessel); |
} | } |
/// <summary> | /// <summary> |
/// Returns the distance between this IAntennaRelay and a CelestialBody | /// Returns the distance between this IAntennaRelay and a CelestialBody |
/// </summary> | /// </summary> |
/// <param name="relay">This <see cref="IAntennaRelay"/></param> | /// <param name="relay">This <see cref="IAntennaRelay"/></param> |
/// <param name="body">A <see cref="CelestialBody"/></param> | /// <param name="body">A <see cref="CelestialBody"/></param> |
public static double DistanceTo(this AntennaRelay relay, CelestialBody body) | public static double DistanceTo(this AntennaRelay relay, CelestialBody body) |
{ | { |
return relay.vessel.DistanceTo(body); | return relay.vessel.DistanceTo(body) - body.Radius; |
} | } |
/// <summary> | /// <summary> |
/// Returns the distance between this IAntennaRelay and another IAntennaRelay | /// Returns the distance between this IAntennaRelay and another IAntennaRelay |
/// </summary> | /// </summary> |
/// <param name="relayOne">This <see cref="IAntennaRelay"/></param> | /// <param name="relayOne">This <see cref="IAntennaRelay"/></param> |
/// <param name="relayTwo">Another <see cref="IAntennaRelay"/></param> | /// <param name="relayTwo">Another <see cref="IAntennaRelay"/></param> |
public static double DistanceTo(this AntennaRelay relayOne, IAntennaRelay relayTwo) | public static double DistanceTo(this AntennaRelay relayOne, IAntennaRelay relayTwo) |
{ | { |
return relayOne.DistanceTo(relayTwo.vessel); | return relayOne.DistanceTo(relayTwo.vessel); |
} | } |
/// <summary> | /// <summary> |
/// Returns the square of the distance between this IAntennaRelay and a Vessel | |
/// </summary> | |
/// <param name="relay">This <see cref="IAntennaRelay"/></param> | |
/// <param name="vessel">A <see cref="Vessel"/></param> | |
public static double sqrDistanceTo(this AntennaRelay relay, Vessel vessel) | |
{ | |
return relay.vessel.sqrDistanceTo(vessel); | |
} | |
/// <summary> | |
/// Returns the square of the distance between this IAntennaRelay and a CelestialBody | |
/// </summary> | |
/// <param name="relay">This <see cref="IAntennaRelay"/></param> | |
/// <param name="body">A <see cref="CelestialBody"/></param> | |
public static double sqrDistanceTo(this AntennaRelay relay, CelestialBody body) | |
{ | |
return relay.vessel.sqrDistanceTo(body); | |
} | |
/// <summary> | |
/// Returns the square of the distance between this IAntennaRelay and another IAntennaRelay | |
/// </summary> | |
/// <param name="relayOne">This <see cref="IAntennaRelay"/></param> | |
/// <param name="relayTwo">Another <see cref="IAntennaRelay"/></param> | |
public static double sqrDistanceTo(this AntennaRelay relayOne, IAntennaRelay relayTwo) | |
{ | |
return relayOne.vessel.sqrDistanceTo(relayTwo.vessel); | |
} | |
/// <summary> | |
/// Returns all of the PartModules or ProtoPartModuleSnapshots implementing IAntennaRelay in this Vessel. | /// Returns all of the PartModules or ProtoPartModuleSnapshots implementing IAntennaRelay in this Vessel. |
/// </summary> | /// </summary> |
/// <param name="vessel">This <see cref="Vessel"/></param> | /// <param name="vessel">This <see cref="Vessel"/></param> |
public static IEnumerable<IAntennaRelay> GetAntennaRelays (this Vessel vessel) | public static IList<IAntennaRelay> GetAntennaRelays (this Vessel vessel) |
{ | { |
return RelayDatabase.Instance[vessel].Values.ToList(); | return RelayDatabase.Instance[vessel]; |
} | } |
/// <summary> | /// <summary> |
/// Determines if the specified vessel has a connected relay. | /// Determines if the specified vessel has a connected relay. |
/// </summary> | /// </summary> |
/// <returns><c>true</c> if the specified vessel has a connected relay; otherwise, <c>false</c>.</returns> | /// <returns><c>true</c> if the specified vessel has a connected relay; otherwise, <c>false</c>.</returns> |
/// <param name="vessel"></param> | /// <param name="vessel"></param> |
public static bool HasConnectedRelay(this Vessel vessel) | public static bool HasConnectedRelay(this Vessel vessel) |
{ | { |
foreach (IAntennaRelay relay in RelayDatabase.Instance[vessel].Values) | IList<IAntennaRelay> vesselRelays = RelayDatabase.Instance[vessel]; |
IAntennaRelay relay; | |
for (int rIdx = 0; rIdx < vesselRelays.Count; rIdx++) | |
{ | { |
relay = vesselRelays[rIdx]; | |
if (relay.CanTransmit()) | if (relay.CanTransmit()) |
{ | { |
return true; | return true; |
} | } |
} | } |
return false; | return false; |
} | } |
/// <summary> | |
/// Gets the <see cref="AntennaRange.ConnectionStatus"/> for this <see cref="Vessel"/> | |
/// </summary> | |
/// <param name="vessel">This <see cref="Vessel"/></param> | |
public static ConnectionStatus GetConnectionStatus(this Vessel vessel) | |
{ | |
bool canTransmit = false; | |
IList<IAntennaRelay> vesselRelays = RelayDatabase.Instance[vessel]; | |
IAntennaRelay relay; | |
for (int rIdx = 0; rIdx < vesselRelays.Count; rIdx++) | |
{ | |
relay = vesselRelays[rIdx]; | |
if (relay.CanTransmit()) | |
{ | |
canTransmit = true; | |
if (relay.transmitDistance <= relay.nominalTransmitDistance) | |
{ | |
return ConnectionStatus.Optimal; | |
} | |
} | |
} | |
if (canTransmit) | |
{ | |
return ConnectionStatus.Suboptimal; | |
} | |
else | |
{ | |
return ConnectionStatus.None; | |
} | |
} | |
/// <summary> | |
/// Gets the best relay on this Vessel. The best relay may not be able to transmit. | |
/// </summary> | |
/// <param name="vessel">This <see cref="Vessel"/></param> | |
public static IAntennaRelay GetBestRelay(this Vessel vessel) | |
{ | |
return RelayDatabase.Instance.GetBestVesselRelay(vessel); | |
} | |
} | |
#pragma warning disable 1591 | |
/// <summary> | |
/// An Enum describing the connection status of a vessel or relay. | |
/// </summary> | |
public enum ConnectionStatus | |
{ | |
None, | |
Suboptimal, | |
Optimal | |
} | } |
} | } |
Binary files /dev/null and b/toolbarIcon.xcf differ
Binary files /dev/null and b/toolbarIcon_24x24.xcf differ
Binary files /dev/null and b/toolbarIcon_38x38.xcf differ