Implement IModuleInfo and tweak GetInfo to allow part tooltips in the editor to be more useful. Needs some cleanup.
Implement IModuleInfo and tweak GetInfo to allow part tooltips in the editor to be more useful. Needs some cleanup.

// AntennaRange // AntennaRange
// //
// ModuleLimitedDataTransmitter.cs // ModuleLimitedDataTransmitter.cs
// //
// Copyright © 2014-2015, 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.Text; using System.Text;
using ToadicusTools; using ToadicusTools;
using UnityEngine; using UnityEngine;
   
namespace AntennaRange namespace AntennaRange
{ {
/// <summary> /// <summary>
/// <para>ModuleLimitedDataTransmitter is designed as a drop-in replacement for ModuleDataTransmitter, and handles /// <para>ModuleLimitedDataTransmitter is designed as a drop-in replacement for ModuleDataTransmitter, and handles
/// rangefinding, power scaling, and data scaling for antennas during science transmission. Its functionality /// rangefinding, power scaling, and data scaling for antennas during science transmission. Its functionality
/// varies with three tunables: nominalRange, maxPowerFactor, and maxDataFactor, set in .cfg files.</para> /// varies with three tunables: nominalRange, maxPowerFactor, and maxDataFactor, set in .cfg files.</para>
/// ///
/// <para>In general, the scaling functions assume the following relation:</para> /// <para>In general, the scaling functions assume the following relation:</para>
/// ///
/// <para> D² α P/R,</para> /// <para> D² α P/R,</para>
/// ///
/// <para>where D is the total transmission distance, P is the transmission power, and R is the data rate.</para> /// <para>where D is the total transmission distance, P is the transmission power, and R is the data rate.</para>
/// </summary> /// </summary>
public class ModuleLimitedDataTransmitter : ModuleDataTransmitter, IScienceDataTransmitter, IAntennaRelay public class ModuleLimitedDataTransmitter
  : ModuleDataTransmitter, IScienceDataTransmitter, IAntennaRelay, IModuleInfo
{ {
// Stores the packetResourceCost as defined in the .cfg file. // Stores the packetResourceCost as defined in the .cfg file.
private float _basepacketResourceCost; private float _basepacketResourceCost;
   
// Stores the packetSize as defined in the .cfg file. // Stores the packetSize as defined in the .cfg file.
private float _basepacketSize; private float _basepacketSize;
   
// Every antenna is a relay. // Every antenna is a relay.
private AntennaRelay relay; private AntennaRelay relay;
   
// 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.
private ScreenMessage ErrorMsg; private ScreenMessage ErrorMsg;
   
/// <summary> /// <summary>
/// When additive ranges are enabled, the distance from Kerbin at which the antenna will perform exactly as /// When additive ranges are enabled, the distance from Kerbin at which the antenna will perform exactly as
/// prescribed by packetResourceCost and packetSize. /// prescribed by packetResourceCost and packetSize.
/// </summary> /// </summary>
[KSPField(isPersistant = false)] [KSPField(isPersistant = false)]
public double nominalRange; public double nominalRange;
   
/// <summary> /// <summary>
/// When additive ranges are disabled, the distance from Kerbin at which the antenna will perform exactly as /// When additive ranges are disabled, the distance from Kerbin at which the antenna will perform exactly as
/// prescribed by packetResourceCost and packetSize. /// prescribed by packetResourceCost and packetSize.
/// </summary> /// </summary>
[KSPField(isPersistant = false)] [KSPField(isPersistant = false)]
public double simpleRange; public double simpleRange;
   
/// <summary> /// <summary>
/// Relay status string for use in action menus. /// Relay status string for use in action menus.
/// </summary> /// </summary>
[KSPField(isPersistant = false, guiActive = true, guiName = "Status")] [KSPField(isPersistant = false, guiActive = true, guiName = "Status")]
public string UIrelayStatus; public string UIrelayStatus;
   
/// <summary> /// <summary>
/// Relay target string for use in action menus. /// Relay target string for use in action menus.
/// </summary> /// </summary>
[KSPField(isPersistant = false, guiActive = true, guiName = "Relay")] [KSPField(isPersistant = false, guiActive = true, guiName = "Relay")]
public string UIrelayTarget; public string UIrelayTarget;
   
/// <summary> /// <summary>
/// Transmit distance string for use in action menus. /// Transmit distance string for use in action menus.
/// </summary> /// </summary>
[KSPField(isPersistant = false, guiActive = true, guiName = "Transmission Distance")] [KSPField(isPersistant = false, guiActive = true, guiName = "Transmission Distance")]
public string UItransmitDistance; public string UItransmitDistance;
   
/// <summary> /// <summary>
/// The nominal range string for use in action menus. /// The nominal range string for use in action menus.
/// </summary> /// </summary>
[KSPField(isPersistant = false, guiActive = true, guiName = "Nominal Range")] [KSPField(isPersistant = false, guiActive = true, guiName = "Nominal Range")]
public string UInominalLinkDistance; public string UInominalLinkDistance;
   
/// <summary> /// <summary>
/// Maximum distance string for use in action menus. /// Maximum distance string for use in action menus.
/// </summary> /// </summary>
[KSPField(isPersistant = false, guiActive = true, guiName = "Maximum Range")] [KSPField(isPersistant = false, guiActive = true, guiName = "Maximum Range")]
public string UImaxTransmitDistance; public string UImaxTransmitDistance;
   
/// <summary> /// <summary>
/// Packet size string for use in action menus. /// Packet size string for use in action menus.
/// </summary> /// </summary>
[KSPField(isPersistant = false, guiActive = true, guiName = "Packet Size")] [KSPField(isPersistant = false, guiActive = true, guiName = "Packet Size")]
public string UIpacketSize; public string UIpacketSize;
   
/// <summary> /// <summary>
/// Packet cost string for use in action menus. /// Packet cost string for use in action menus.
/// </summary> /// </summary>
[KSPField(isPersistant = false, guiActive = true, guiName = "Packet Cost")] [KSPField(isPersistant = false, guiActive = true, guiName = "Packet Cost")]
public string UIpacketCost; public string UIpacketCost;
   
/// <summary> /// <summary>
/// The multiplier on packetResourceCost that defines the maximum power output of the antenna. When the power /// The multiplier on packetResourceCost that defines the maximum power output of the antenna. When the power
/// cost exceeds packetResourceCost * maxPowerFactor, transmission will fail. /// cost exceeds packetResourceCost * maxPowerFactor, transmission will fail.
/// </summary> /// </summary>
[KSPField(isPersistant = false)] [KSPField(isPersistant = false)]
public float maxPowerFactor; public float maxPowerFactor;
   
/// <summary> /// <summary>
/// The multipler on packetSize that defines the maximum data bandwidth of the antenna. /// The multipler on packetSize that defines the maximum data bandwidth of the antenna.
/// </summary> /// </summary>
[KSPField(isPersistant = false)] [KSPField(isPersistant = false)]
public float maxDataFactor; public float maxDataFactor;
   
/// <summary> /// <summary>
/// The packet throttle. /// The packet throttle.
/// </summary> /// </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;
   
private bool actionUIUpdate; private bool actionUIUpdate;
   
/* /*
* Properties * Properties
* */ * */
/// <summary> /// <summary>
/// Gets the parent Vessel. /// Gets the parent Vessel.
/// </summary> /// </summary>
public new Vessel vessel public new Vessel vessel
{ {
get get
{ {
if (base.vessel != null) if (base.vessel != null)
{ {
return base.vessel; return base.vessel;
} }
else if (this.part != null && this.part.vessel != null) else if (this.part != null && this.part.vessel != null)
{ {
return this.part.vessel; return this.part.vessel;
} }
else if ( else if (
this.part.protoPartSnapshot != null && this.part.protoPartSnapshot != null &&
this.part.protoPartSnapshot.pVesselRef != null && this.part.protoPartSnapshot.pVesselRef != null &&
this.part.protoPartSnapshot.pVesselRef.vesselRef != null this.part.protoPartSnapshot.pVesselRef.vesselRef != null
) )
{ {
return this.part.protoPartSnapshot.pVesselRef.vesselRef; return this.part.protoPartSnapshot.pVesselRef.vesselRef;
} }
else else
{ {
this.LogError("Vessel and/or part reference are null, returning null vessel."); this.LogError("Vessel and/or part reference are null, returning null vessel.");
this.LogError(new System.Diagnostics.StackTrace().ToString()); this.LogError(new System.Diagnostics.StackTrace().ToString());
return null; return null;
} }
} }
} }
   
/// <summary> /// <summary>
/// Gets the target <see cref="AntennaRange.IAntennaRelay"/>relay. /// Gets the target <see cref="AntennaRange.IAntennaRelay"/>relay.
/// </summary> /// </summary>
public IAntennaRelay targetRelay public IAntennaRelay targetRelay
{ {
get get
{ {
if (this.relay == null) if (this.relay == null)
{ {
return null; return null;
} }
   
return this.relay.targetRelay; return this.relay.targetRelay;
} }
} }
   
/// <summary> /// <summary>
/// Gets a value indicating whether this <see cref="AntennaRange.IAntennaRelay"/> Relay is communicating /// Gets a value indicating whether this <see cref="AntennaRange.IAntennaRelay"/> Relay is communicating
/// directly with Kerbin. /// directly with Kerbin.
/// </summary> /// </summary>
public bool KerbinDirect public bool KerbinDirect
{ {
get get
{ {
if (this.relay != null) if (this.relay != null)
{ {
return this.relay.KerbinDirect; return this.relay.KerbinDirect;
} }
   
return false; return false;
} }
} }
   
/// <summary> /// <summary>
/// Gets or sets the nominal link distance, in meters. /// Gets or sets the nominal link distance, in meters.
/// </summary> /// </summary>
public double NominalLinkSqrDistance public double NominalLinkSqrDistance
{ {
get get
{ {
if (this.relay != null) if (this.relay != null)
{ {
return this.relay.NominalLinkSqrDistance; return this.relay.NominalLinkSqrDistance;
} }
   
return 0d; return 0d;
} }
} }
   
/// <summary> /// <summary>
/// Gets or sets the maximum link distance, in meters. /// Gets or sets the maximum link distance, in meters.
/// </summary> /// </summary>
public double MaximumLinkSqrDistance public double MaximumLinkSqrDistance
{ {
get get
{ {
if (this.relay != null) if (this.relay != null)
{ {
return this.relay.MaximumLinkSqrDistance; return this.relay.MaximumLinkSqrDistance;
} }
   
return 0d; return 0d;
} }
} }
   
/// <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>
public double CurrentLinkSqrDistance public double CurrentLinkSqrDistance
{ {
get get
{ {
if (this.relay == null) if (this.relay == null)
{ {
return double.PositiveInfinity; return double.PositiveInfinity;
} }
   
return this.relay.CurrentLinkSqrDistance; return this.relay.CurrentLinkSqrDistance;
} }
} }
   
/// <summary> /// <summary>
/// Gets the link status. /// Gets the link status.
/// </summary> /// </summary>
public ConnectionStatus LinkStatus public ConnectionStatus LinkStatus
{ {
get get
{ {
if (this.relay == null) if (this.relay == null)
{ {
return ConnectionStatus.None; return ConnectionStatus.None;
} }
   
return this.relay.LinkStatus; return this.relay.LinkStatus;
} }
} }
   
/// <summary> /// <summary>
/// Gets the nominal transmit distance at which the Antenna behaves just as prescribed by Squad's config. /// Gets the nominal transmit distance at which the Antenna behaves just as prescribed by Squad's config.
/// </summary> /// </summary>
public double nominalTransmitDistance public double nominalTransmitDistance
{ {
get get
{ {
if (ARConfiguration.UseAdditiveRanges) if (ARConfiguration.UseAdditiveRanges)
{ {
return this.nominalRange; return this.nominalRange;
} }
else else
{ {
return this.simpleRange; return this.simpleRange;
} }
} }
} }
   
/// <summary> /// <summary>
/// The maximum distance at which this relay can operate. /// The maximum distance at which this relay can operate.
/// </summary> /// </summary>
public double maxTransmitDistance public double maxTransmitDistance
{ {
get; get;
protected set; protected set;
} }
   
/// <summary> /// <summary>
/// The first CelestialBody blocking line of sight to a /// The first CelestialBody blocking line of sight to a
/// </summary> /// </summary>
public CelestialBody firstOccludingBody public CelestialBody firstOccludingBody
{ {
get get
{ {
return this.relay.firstOccludingBody; 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.
* */ * */
/// <summary> /// <summary>
/// Override ModuleDataTransmitter.DataRate to just return packetSize, because we want antennas to be scored in /// Override ModuleDataTransmitter.DataRate to just return packetSize, because we want antennas to be scored in
/// terms of joules/byte /// terms of joules/byte
/// </summary> /// </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;
} }
} }
} }
   
/// <summary> /// <summary>
/// Override ModuleDataTransmitter.DataResourceCost to just return packetResourceCost, because we want antennas /// Override ModuleDataTransmitter.DataResourceCost to just return packetResourceCost, because we want antennas
/// to be scored in terms of joules/byte /// to be scored in terms of joules/byte
/// </summary> /// </summary>
public new double DataResourceCost 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;
} }
} }
} }
   
/// <summary> /// <summary>
/// Gets the Part title. /// Gets the Part title.
/// </summary> /// </summary>
public string Title public string Title
{ {
get get
{ {
if (this.part != null && this.part.partInfo != null) if (this.part != null && this.part.partInfo != null)
{ {
return this.part.partInfo.title; return this.part.partInfo.title;
} }
   
return string.Empty; 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;
} }
   
/// <summary> /// <summary>
/// PartModule OnAwake override; runs at Unity Awake. /// PartModule OnAwake override; runs at Unity Awake.
/// </summary> /// </summary>
public override void OnAwake() public override void OnAwake()
{ {
base.OnAwake(); base.OnAwake();
   
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" +
"nominalTransmitDistance: {3}\n" + "nominalTransmitDistance: {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.nominalTransmitDistance, this.nominalTransmitDistance,
this.maxPowerFactor, this.maxPowerFactor,
this.maxDataFactor this.maxDataFactor
)); ));
} }
   
/// <summary> /// <summary>
/// PartModule OnStart override; runs at Unity Start. /// PartModule OnStart override; runs at Unity Start.
/// </summary> /// </summary>
/// <param name="state">State.</param> /// <param name="state">State.</param>
public override void OnStart (StartState state) public override void OnStart (StartState state)
{ {
base.OnStart (state); base.OnStart (state);
   
  this.maxTransmitDistance = Math.Sqrt(this.maxPowerFactor) * this.nominalTransmitDistance;
   
if (state >= StartState.PreLaunch) if (state >= StartState.PreLaunch)
{ {
this.maxTransmitDistance = Math.Sqrt(this.maxPowerFactor) * this.nominalTransmitDistance;  
   
this.relay = new AntennaRelay(this); this.relay = new AntennaRelay(this);
this.relay.nominalTransmitDistance = this.nominalTransmitDistance; this.relay.nominalTransmitDistance = this.nominalTransmitDistance;
this.relay.maxTransmitDistance = this.maxTransmitDistance; this.relay.maxTransmitDistance = this.maxTransmitDistance;
   
this.UImaxTransmitDistance = string.Format(Tools.SIFormatter, "{0:S3}m", this.maxTransmitDistance); this.UImaxTransmitDistance = string.Format(Tools.SIFormatter, "{0:S3}m", this.maxTransmitDistance);
   
GameEvents.onPartActionUICreate.Add(this.onPartActionUICreate); GameEvents.onPartActionUICreate.Add(this.onPartActionUICreate);
GameEvents.onPartActionUIDismiss.Add(this.onPartActionUIDismiss); GameEvents.onPartActionUIDismiss.Add(this.onPartActionUIDismiss);
} }
} }
   
/// <summary> /// <summary>
/// When the module loads, fetch the Squad KSPFields from the base. This is necessary in part because /// 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 /// overloading packetSize and packetResourceCostinto a property in ModuleLimitedDataTransmitter didn't
/// work. /// work.
/// </summary> /// </summary>
/// <param name="node"><see cref="ConfigNode"/> with data for this module.</param> /// <param name="node"><see cref="ConfigNode"/> with data for this module.</param>
public override void OnLoad(ConfigNode node) public override void OnLoad(ConfigNode node)
{ {
this.Fields.Load(node); this.Fields.Load(node);
base.Fields.Load(node); base.Fields.Load(node);
   
base.OnLoad (node); base.OnLoad (node);
   
this.maxTransmitDistance = Math.Sqrt(this.maxPowerFactor) * this.nominalTransmitDistance; this.maxTransmitDistance = Math.Sqrt(this.maxPowerFactor) * this.nominalTransmitDistance;
} }
   
  public string GetModuleTitle()
  {
  return "Comms Transceiver";
  }
   
  public Callback<Rect> GetDrawModulePanelCallback()
  {
  return this.drawTooltipWidget;
  }
   
  private void drawTooltipWidget(Rect rect)
  {
  GUIContent content = new GUIContent(this.GetInfo());
  GUIStyle style0 = PartListTooltips.fetch.tooltipSkin.customStyles[0];
  GUIStyle style1 = PartListTooltips.fetch.tooltipSkin.customStyles[1];
   
  float width = rect.width;
  float orgHeight = rect.height;
  float height = style0.CalcHeight(content, width);
   
  rect.height = height;
   
  GUI.Box(rect, content, style0);
  GUI.Label(rect, this.GetModuleTitle(), style1);
   
  GUILayout.Space(height - orgHeight
  - style0.padding.bottom - style0.padding.top
  - 2f * (style0.margin.bottom + style0.margin.top)
  );
  }
   
  public string GetPrimaryField()
  {
  return string.Empty;
  }
   
/// <summary> /// <summary>
/// Override ModuleDataTransmitter.GetInfo to add nominal and maximum range to the VAB description. /// Override ModuleDataTransmitter.GetInfo to add nominal and maximum range to the VAB description.
/// </summary> /// </summary>
public override string GetInfo() public override string GetInfo()
{ {
StringBuilder sb = Tools.GetStringBuilder(); StringBuilder sb = Tools.GetStringBuilder();
string text; string text;
   
sb.Append(base.GetInfo()); sb.Append(base.GetInfo());
sb.AppendFormat(Tools.SIFormatter, "Nominal Range: {0:S3}m\n", this.nominalTransmitDistance);  
sb.AppendFormat(Tools.SIFormatter, "Maximum Range: {0:S3}m\n", this.maxTransmitDistance); if (ARConfiguration.UseAdditiveRanges)
  {
  sb.AppendFormat(Tools.SIFormatter, "Nominal Range to Kerbin: {0:S3}m\n",
  Math.Sqrt(this.nominalTransmitDistance * ARConfiguration.KerbinNominalRange)
  );
  sb.AppendFormat(Tools.SIFormatter, "Maximum Range to Kerbin: {0:S3}m",
  Math.Sqrt(this.maxTransmitDistance * ARConfiguration.KerbinRelayRange)
  );
  }
  else
  {
  sb.AppendFormat(Tools.SIFormatter, "Nominal Range: {0:S3}m\n", this.nominalTransmitDistance);
  sb.AppendFormat(Tools.SIFormatter, "Maximum Range: {0:S3}m", this.maxTransmitDistance);
  }
   
text = sb.ToString(); text = sb.ToString();
   
Tools.PutStringBuilder(sb); Tools.PutStringBuilder(sb);
   
return text; return text;
} }
   
/// <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>. /// <c>true</c> if this instance can transmit; otherwise, <c>false</c>.
/// </summary> /// </summary>
public new bool CanTransmit() public new bool CanTransmit()
{ {
if (this.part == null || this.relay == null) if (this.part == null || this.relay == null)
{ {
return false; return false;
} }
   
switch (this.part.State) switch (this.part.State)
{ {
case PartStates.DEAD: case PartStates.DEAD:
case PartStates.DEACTIVATED: case 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.part.partInfo.title, this.part.partInfo.title,
this.vessel.vesselName, this.vessel.vesselName,
Enum.GetName(typeof(PartStates), this.part.State) Enum.GetName(typeof(PartStates), this.part.State)
)); ));
return false; return false;
default: default:
break; break;
} }
   
return this.relay.CanTransmit(); return this.relay.CanTransmit();
} }
   
/// <summary> /// <summary>
/// Finds the nearest relay. /// Finds the nearest relay.
/// </summary> /// </summary>
public void FindNearestRelay() public void FindNearestRelay()
{ {
if (this.relay != null) if (this.relay != null)
{ {
this.relay.FindNearestRelay(); this.relay.FindNearestRelay();
} }
} }
   
/// <summary> /// <summary>
/// Override ModuleDataTransmitter.TransmitData to check against CanTransmit and fail out when CanTransmit /// Override ModuleDataTransmitter.TransmitData to check against CanTransmit and fail out when CanTransmit
/// returns false. /// returns false.
/// </summary> /// </summary>
/// <param name="dataQueue">List of <see cref="ScienceData"/> to transmit.</param> /// <param name="dataQueue">List of <see cref="ScienceData"/> to transmit.</param>
/// <param name="callback">Callback function</param> /// <param name="callback">Callback function</param>
public new void TransmitData(List<ScienceData> dataQueue, Callback callback) public new void TransmitData(List<ScienceData> dataQueue, Callback callback)
{ {
this.LogDebug( this.LogDebug(
"TransmitData(List<ScienceData> dataQueue, Callback callback) called. dataQueue.Count={0}", "TransmitData(List<ScienceData> dataQueue, Callback callback) called. dataQueue.Count={0}",
dataQueue.Count dataQueue.Count
); );
   
this.FindNearestRelay(); this.FindNearestRelay();
   
this.PreTransmit_SetPacketSize(); this.PreTransmit_SetPacketSize();
this.PreTransmit_SetPacketResourceCost(); this.PreTransmit_SetPacketResourceCost();
   
if (this.CanTransmit()) if (this.CanTransmit())
{ {
ScreenMessages.PostScreenMessage(this.buildTransmitMessage(), 4f, ScreenMessageStyle.UPPER_LEFT); ScreenMessages.PostScreenMessage(this.buildTransmitMessage(), 4f, ScreenMessageStyle.UPPER_LEFT);
   
this.LogDebug( this.LogDebug(
"CanTransmit in TransmitData, calling base.TransmitData with dataQueue=[{0}] and callback={1}", "CanTransmit in TransmitData, calling base.TransmitData with dataQueue=[{0}] and callback={1}",
dataQueue.SPrint(), dataQueue.SPrint(),
callback == null ? "null" : callback.ToString() callback == null ? "null" : callback.ToString()
); );
   
if (callback == null) if (callback == null)
{ {
base.TransmitData(dataQueue); base.TransmitData(dataQueue);
} }
else else
{ {
base.TransmitData(dataQueue, callback); base.TransmitData(dataQueue, callback);
} }
} }
else else
{ {
Tools.PostDebugMessage(this, "{0} unable to transmit during TransmitData.", this.part.partInfo.title); Tools.PostDebugMessage(this, "{0} unable to transmit during TransmitData.", this.part.partInfo.title);
   
var logger = Tools.DebugLogger.New(this); var logger = Tools.DebugLogger.New(this);
   
IList<ModuleScienceContainer> vesselContainers = this.vessel.getModulesOfType<ModuleScienceContainer>(); IList<ModuleScienceContainer> vesselContainers = this.vessel.getModulesOfType<ModuleScienceContainer>();
ModuleScienceContainer scienceContainer; ModuleScienceContainer scienceContainer;
for (int cIdx = 0; cIdx < vesselContainers.Count; cIdx++) for (int cIdx = 0; cIdx < vesselContainers.Count; cIdx++)
{ {
scienceContainer = vesselContainers[cIdx]; scienceContainer = vesselContainers[cIdx];
   
logger.AppendFormat("Checking ModuleScienceContainer in {0}\n", logger.AppendFormat("Checking ModuleScienceContainer in {0}\n",
scienceContainer.part.partInfo.title); scienceContainer.part.partInfo.title);
   
if ( if (
scienceContainer.capacity != 0 && scienceContainer.capacity != 0 &&
scienceContainer.GetScienceCount() >= scienceContainer.capacity scienceContainer.GetScienceCount() >= scienceContainer.capacity
) )
{ {
logger.Append("\tInsufficient capacity, skipping.\n"); logger.Append("\tInsufficient capacity, skipping.\n");
continue; continue;
} }
   
List<ScienceData> dataStored = new List<ScienceData>(); List<ScienceData> dataStored = new List<ScienceData>();
   
ScienceData data; ScienceData data;
for (int dIdx = 0; dIdx < dataQueue.Count; dIdx++) for (int dIdx = 0; dIdx < dataQueue.Count; dIdx++)
{ {
data = dataQueue[dIdx]; data = dataQueue[dIdx];
if (!scienceContainer.allowRepeatedSubjects && scienceContainer.HasData(data)) if (!scienceContainer.allowRepeatedSubjects && scienceContainer.HasData(data))
{ {
logger.Append("\tAlready contains subject and repeated subjects not allowed, skipping.\n"); logger.Append("\tAlready contains subject and repeated subjects not allowed, skipping.\n");
continue; continue;
} }
   
logger.AppendFormat("\tAcceptable, adding data on subject {0}... ", data.subjectID); logger.AppendFormat("\tAcceptable, adding data on subject {0}... ", data.subjectID);
if (scienceContainer.AddData(data)) if (scienceContainer.AddData(data))
{ {
logger.Append("done, removing from queue.\n"); logger.Append("done, removing from queue.\n");
   
dataStored.Add(data); dataStored.Add(data);
} }
#if DEBUG #if DEBUG
else else
{ {
logger.Append("failed.\n"); logger.Append("failed.\n");
} }
#endif #endif
} }
   
dataQueue.RemoveAll(i => dataStored.Contains(i)); dataQueue.RemoveAll(i => dataStored.Contains(i));
   
logger.AppendFormat("\t{0} data left in queue.", dataQueue.Count); logger.AppendFormat("\t{0} data left in queue.", dataQueue.Count);
} }
   
logger.Print(); logger.Print();
   
if (dataQueue.Count > 0) if (dataQueue.Count > 0)
{ {
StringBuilder sb = Tools.GetStringBuilder(); StringBuilder sb = Tools.GetStringBuilder();
   
sb.Append('['); sb.Append('[');
sb.Append(this.part.partInfo.title); sb.Append(this.part.partInfo.title);
sb.AppendFormat("]: {0} data items could not be saved: no space available in data containers.\n"); sb.AppendFormat("]: {0} data items could not be saved: no space available in data containers.\n");
sb.Append("Data to be discarded:\n"); sb.Append("Data to be discarded:\n");
   
ScienceData data; ScienceData data;
for (int dIdx = 0; dIdx < dataQueue.Count; dIdx++) for (int dIdx = 0; dIdx < dataQueue.Count; dIdx++)
{ {
data = dataQueue[dIdx]; data = dataQueue[dIdx];
sb.AppendFormat("\t{0}\n", data.title); sb.AppendFormat("\t{0}\n", data.title);
} }
   
ScreenMessages.PostScreenMessage(sb.ToString(), 4f, ScreenMessageStyle.UPPER_LEFT); ScreenMessages.PostScreenMessage(sb.ToString(), 4f, ScreenMessageStyle.UPPER_LEFT);
   
Tools.PostDebugMessage(sb.ToString()); Tools.PostDebugMessage(sb.ToString());
   
Tools.PutStringBuilder(sb); Tools.PutStringBuilder(sb);
} }
   
this.PostCannotTransmitError(); this.PostCannotTransmitError();
} }
   
Tools.PostDebugMessage ( Tools.PostDebugMessage (
"distance: " + this.CurrentLinkSqrDistance "distance: " + this.CurrentLinkSqrDistance
+ " packetSize: " + this.packetSize + " packetSize: " + this.packetSize
+ " packetResourceCost: " + this.packetResourceCost + " packetResourceCost: " + this.packetResourceCost
); );
} }
   
/// <summary> /// <summary>
/// Override ModuleDataTransmitter.TransmitData to check against CanTransmit and fail out when CanTransmit /// Override ModuleDataTransmitter.TransmitData to check against CanTransmit and fail out when CanTransmit
/// returns false. /// returns false.
/// </summary> /// </summary>
/// <param name="dataQueue">List of <see cref="ScienceData"/> to transmit.</param> /// <param name="dataQueue">List of <see cref="ScienceData"/> to transmit.</param>
public new void TransmitData(List<ScienceData> dataQueue) public new void TransmitData(List<ScienceData> dataQueue)
{ {
this.LogDebug( this.LogDebug(
"TransmitData(List<ScienceData> dataQueue) called, dataQueue.Count={0}", "TransmitData(List<ScienceData> dataQueue) called, dataQueue.Count={0}",
dataQueue.Count dataQueue.Count
); );
   
this.TransmitData(dataQueue, null); this.TransmitData(dataQueue, null);
} }
   
/// <summary> /// <summary>
/// Override ModuleDataTransmitter.StartTransmission to check against CanTransmit and fail out when CanTransmit /// Override ModuleDataTransmitter.StartTransmission to check against CanTransmit and fail out when CanTransmit
/// returns false. /// returns false.
/// </summary> /// </summary>
public new void StartTransmission() public new void StartTransmission()
{ {
this.FindNearestRelay(); this.FindNearestRelay();
   
PreTransmit_SetPacketSize (); PreTransmit_SetPacketSize ();
PreTransmit_SetPacketResourceCost (); PreTransmit_SetPacketResourceCost ();
   
Tools.PostDebugMessage ( Tools.PostDebugMessage (
"distance: " + this.CurrentLinkSqrDistance "distance: " + this.CurrentLinkSqrDistance
+ " packetSize: " + this.packetSize + " packetSize: " + this.packetSize
+ " packetResourceCost: " + this.packetResourceCost + " packetResourceCost: " + this.packetResourceCost
); );
   
if (this.CanTransmit()) if (this.CanTransmit())
{ {
ScreenMessages.PostScreenMessage(this.buildTransmitMessage(), 4f, ScreenMessageStyle.UPPER_LEFT); ScreenMessages.PostScreenMessage(this.buildTransmitMessage(), 4f, ScreenMessageStyle.UPPER_LEFT);
   
base.StartTransmission(); base.StartTransmission();
} }
else else
{ {
this.PostCannotTransmitError (); this.PostCannotTransmitError ();
} }
} }
   
/// <summary> /// <summary>
/// MonoBehaviour Update /// MonoBehaviour Update
/// </summary> /// </summary>
public void Update() public void Update()
{ {
if (this.actionUIUpdate) if (this.actionUIUpdate)
{ {
this.UImaxTransmitDistance = string.Format(Tools.SIFormatter, "{0:S3}m", this.UImaxTransmitDistance = string.Format(Tools.SIFormatter, "{0:S3}m",
Math.Sqrt(this.MaximumLinkSqrDistance)); Math.Sqrt(this.MaximumLinkSqrDistance));
this.UInominalLinkDistance = string.Format(Tools.SIFormatter, "{0:S3}m", this.UInominalLinkDistance = string.Format(Tools.SIFormatter, "{0:S3}m",
Math.Sqrt(this.NominalLinkSqrDistance)); Math.Sqrt(this.NominalLinkSqrDistance));
if (this.CanTransmit()) if (this.CanTransmit())
{ {
this.UIrelayStatus = this.LinkStatus.ToString(); this.UIrelayStatus = this.LinkStatus.ToString();
this.UItransmitDistance = string.Format(Tools.SIFormatter, "{0:S3}m", this.UItransmitDistance = string.Format(Tools.SIFormatter, "{0:S3}m",
Math.Sqrt(this.CurrentLinkSqrDistance)); Math.Sqrt(this.CurrentLinkSqrDistance));
this.UIpacketSize = string.Format(Tools.SIFormatter, "{0:S3}MiT", this.DataRate); this.UIpacketSize = string.Format(Tools.SIFormatter, "{0:S3}MiT", this.DataRate);
this.UIpacketCost = string.Format(Tools.SIFormatter, "{0:S3}EC", this.DataResourceCost); this.UIpacketCost = string.Format(Tools.SIFormatter, "{0:S3}EC", this.DataResourceCost);
} }
else else
{ {
if (this.relay.firstOccludingBody == null) if (this.relay.firstOccludingBody == null)
{ {
this.UItransmitDistance = string.Format(Tools.SIFormatter, "{0:S3}m", this.UItransmitDistance = string.Format(Tools.SIFormatter, "{0:S3}m",
Math.Sqrt(this.CurrentLinkSqrDistance)); Math.Sqrt(this.CurrentLinkSqrDistance));
this.UIrelayStatus = "Out of range"; this.UIrelayStatus = "Out of range";
} }
else else
{ {
this.UItransmitDistance = "N/A"; this.UItransmitDistance = "N/A";
this.UIrelayStatus = string.Format("Blocked by {0}", this.relay.firstOccludingBody.bodyName); this.UIrelayStatus = string.Format("Blocked by {0}", this.relay.firstOccludingBody.bodyName);
} }
this.UIpacketSize = "N/A"; this.UIpacketSize = "N/A";
this.UIpacketCost = "N/A"; this.UIpacketCost = "N/A";
} }
   
if (this.KerbinDirect) if (this.KerbinDirect)
{ {
this.UIrelayTarget = AntennaRelay.Kerbin.bodyName; this.UIrelayTarget = AntennaRelay.Kerbin.bodyName;
} }
else else
{ {
this.UIrelayTarget = this.targetRelay.ToString(); this.UIrelayTarget = this.targetRelay.ToString();
} }
} }
} }
   
/// <summary> /// <summary>
/// Returns a <see cref="System.String"/> that represents the current <see cref="AntennaRange.ModuleLimitedDataTransmitter"/>. /// Returns a <see cref="System.String"/> that represents the current <see cref="AntennaRange.ModuleLimitedDataTransmitter"/>.
/// </summary> /// </summary>
/// <returns>A <see cref="System.String"/> that represents the current <see cref="AntennaRange.ModuleLimitedDataTransmitter"/>.</returns> /// <returns>A <see cref="System.String"/> that represents the current <see cref="AntennaRange.ModuleLimitedDataTransmitter"/>.</returns>
public override string ToString() public override string ToString()
{ {
StringBuilder sb = Tools.GetStringBuilder(); StringBuilder sb = Tools.GetStringBuilder();
string msg; string msg;
   
sb.Append(this.part.partInfo.title); sb.Append(this.part.partInfo.title);
   
if (vessel != null) if (vessel != null)
{ {
sb.Append(" on "); sb.Append(" on ");
sb.Append(vessel.vesselName); sb.Append(vessel.vesselName);
} }
else if ( else if (
this.part != null && this.part != null &&
this.part.protoPartSnapshot != null && this.part.protoPartSnapshot != null &&
this.part.protoPartSnapshot != null && this.part.protoPartSnapshot != null &&
this.part.protoPartSnapshot.pVesselRef != null this.part.protoPartSnapshot.pVesselRef != null
) )
{ {
sb.Append(" on "); sb.Append(" on ");
sb.Append(this.part.protoPartSnapshot.pVesselRef.vesselName); sb.Append(this.part.protoPartSnapshot.pVesselRef.vesselName);
} }
   
msg = sb.ToString(); msg = sb.ToString();
   
Tools.PutStringBuilder(sb); Tools.PutStringBuilder(sb);
   
return msg; return msg;
} }
   
// When we catch an onPartActionUICreate event for our part, go ahead and update every frame to look pretty. // When we catch an onPartActionUICreate event for our part, go ahead and update every frame to look pretty.
private void onPartActionUICreate(Part eventPart) private void onPartActionUICreate(Part eventPart)
{ {
if (eventPart == base.part) if (eventPart == base.part)
{ {
this.actionUIUpdate = true; this.actionUIUpdate = true;
} }
} }
   
// When we catch an onPartActionUIDismiss event for our part, stop updating every frame to look pretty. // When we catch an onPartActionUIDismiss event for our part, stop updating every frame to look pretty.
private void onPartActionUIDismiss(Part eventPart) private void onPartActionUIDismiss(Part eventPart)
{ {
if (eventPart == base.part) if (eventPart == base.part)
{ {
this.actionUIUpdate = false; 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.
private 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).
private void PreTransmit_SetPacketResourceCost() private void PreTransmit_SetPacketResourceCost()
{ {
if (ARConfiguration.FixedPowerCost || this.CurrentLinkSqrDistance <= this.NominalLinkSqrDistance) if (ARConfiguration.FixedPowerCost || this.CurrentLinkSqrDistance <= this.NominalLinkSqrDistance)
{ {
base.packetResourceCost = this._basepacketResourceCost; base.packetResourceCost = this._basepacketResourceCost;
} }
else else
{ {
float rangeFactor = (float)(this.CurrentLinkSqrDistance / this.NominalLinkSqrDistance); float rangeFactor = (float)(this.CurrentLinkSqrDistance / this.NominalLinkSqrDistance);
   
base.packetResourceCost = this._basepacketResourceCost * rangeFactor; base.packetResourceCost = this._basepacketResourceCost * 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.
private void PreTransmit_SetPacketSize() private void PreTransmit_SetPacketSize()
{ {
if (!ARConfiguration.FixedPowerCost && this.CurrentLinkSqrDistance >= this.NominalLinkSqrDistance) if (!ARConfiguration.FixedPowerCost && this.CurrentLinkSqrDistance >= this.NominalLinkSqrDistance)
{ {
base.packetSize = this._basepacketSize; base.packetSize = this._basepacketSize;
} }
else else
{ {
float rangeFactor = (float)(this.NominalLinkSqrDistance / this.CurrentLinkSqrDistance); float rangeFactor = (float)(this.NominalLinkSqrDistance / this.CurrentLinkSqrDistance);
   
base.packetSize = Mathf.Min( base.packetSize = Mathf.Min(
this._basepacketSize * rangeFactor, this._basepacketSize * rangeFactor,
this._basepacketSize * this.maxDataFactor this._basepacketSize * this.maxDataFactor
); );
} }
   
base.packetSize *= this.packetThrottle / 100f; base.packetSize *= this.packetThrottle / 100f;
} }
   
private string buildTransmitMessage() private string buildTransmitMessage()
{ {
StringBuilder sb = Tools.GetStringBuilder(); StringBuilder sb = Tools.GetStringBuilder();
string msg; string msg;
   
sb.Append("["); sb.Append("[");
sb.Append(base.part.partInfo.title); sb.Append(base.part.partInfo.title);
sb.Append("]: "); sb.Append("]: ");
   
sb.Append("Beginning transmission "); sb.Append("Beginning transmission ");
   
if (this.KerbinDirect) if (this.KerbinDirect)
{ {
sb.Append("directly to Kerbin."); sb.Append("directly to Kerbin.");
} }
else else
{ {
sb.Append("via "); sb.Append("via ");
sb.Append(this.relay.targetRelay); sb.Append(this.relay.targetRelay);
} }
   
msg = sb.ToString(); msg = sb.ToString();
   
Tools.PutStringBuilder(sb); Tools.PutStringBuilder(sb);
   
return msg; return msg;
} }
   
#if DEBUG #if DEBUG
// When debugging, it's nice to have a button that just tells you everything. // When debugging, it's nice to have a button that just tells you everything.
[KSPEvent (guiName = "Show Debug Info", active = true, guiActive = true)] [KSPEvent (guiName = "Show Debug Info", active = true, guiActive = true)]
public void DebugInfo() public void DebugInfo()
{ {
PreTransmit_SetPacketSize (); PreTransmit_SetPacketSize ();
PreTransmit_SetPacketResourceCost (); PreTransmit_SetPacketResourceCost ();
   
DebugPartModule.DumpClassObject(this); DebugPartModule.DumpClassObject(this);
} }
   
[KSPEvent (guiName = "Dump Vessels", active = true, guiActive = true)] [KSPEvent (guiName = "Dump Vessels", active = true, guiActive = true)]
public void PrintAllVessels() public void PrintAllVessels()
{ {
StringBuilder sb = Tools.GetStringBuilder(); StringBuilder sb = Tools.GetStringBuilder();
sb.Append("Dumping FlightGlobals.Vessels:"); sb.Append("Dumping FlightGlobals.Vessels:");
   
Vessel vessel; Vessel vessel;
for (int i = 0; i < FlightGlobals.Vessels.Count; i++) for (int i = 0; i < FlightGlobals.Vessels.Count; i++)
{ {
vessel = FlightGlobals.Vessels[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); 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
} }
} }