ProtoAntennaRelay: Make ToString safer.
ProtoAntennaRelay: Make ToString safer.

// AntennaRange // AntennaRange
// //
// ARMapRenderer.cs // ARMapRenderer.cs
// //
// Copyright © 2014, toadicus // Copyright © 2014, 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 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; private Dictionary<Guid, bool> vesselFrameCache;
  private bool dumpBool;
#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 = MapView.OrbitLinesMaterial; 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()
{ {
if (ARConfiguration.PrettyLines) if (ARConfiguration.PrettyLines)
{ {
this.vesselLineRenderers = new Dictionary<Guid, LineRenderer>(); this.vesselLineRenderers = new Dictionary<Guid, LineRenderer>();
this.vesselFrameCache = new Dictionary<Guid, bool>(); this.vesselFrameCache = new Dictionary<Guid, bool>();
} }
} }
   
private void OnPreCull() private void OnPreCull()
{ {
if (!HighLogic.LoadedSceneIsFlight || !MapView.MapIsEnabled || !ARConfiguration.PrettyLines) if (!HighLogic.LoadedSceneIsFlight || !MapView.MapIsEnabled || !ARConfiguration.PrettyLines)
{ {
this.Cleanup(); this.Cleanup();
   
return; return;
} }
   
Tools.DebugLogger log = Tools.DebugLogger.New(this); Tools.DebugLogger log = Tools.DebugLogger.New(this);
   
try try
{ {
log.AppendFormat("OnPreCull.\n"); log.AppendFormat("OnPreCull.\n");
   
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",
MapView.Draw3DLines, MapView.Draw3DLines,
MapView.MapCamera.camera.fieldOfView, MapView.MapCamera.camera.fieldOfView,
MapView.MapCamera.Distance MapView.MapCamera.Distance
); );
   
this.vesselFrameCache.Clear(); this.vesselFrameCache.Clear();
   
log.AppendLine("vesselFrameCache cleared."); 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) foreach (Vessel vessel in FlightGlobals.Vessels)
{ {
if (vessel == null) if (vessel == null)
{ {
log.AppendFormat("Skipping vessel {0} altogether because it is null.\n"); log.AppendFormat("Skipping vessel {0} altogether because it is null.\n");
continue; continue;
} }
   
  if (this.vesselFrameCache.TryGetValue(vessel.id, out dumpBool))
  {
  log.AppendFormat("Skipping vessel {0} because it's already been processed this frame.");
  continue;
  }
   
log.AppendFormat("Checking vessel {0}.\n", vessel.vesselName); 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("\tDiscarded because vessel is of invalid type {0}\n",
vessel.vesselType); vessel.vesselType);
continue; continue;
} }
   
IAntennaRelay vesselRelay = vessel.GetBestRelay(); IAntennaRelay vesselRelay = vessel.GetBestRelay();
   
if (vesselRelay != null) if (vesselRelay != null)
{ {
this.SetRelayVertices(vesselRelay); this.SetRelayVertices(vesselRelay);
} }
} }
} }
} }
catch (Exception) catch (Exception)
{ {
this.Cleanup(); this.Cleanup();
} }
#if DEBUG #if DEBUG
finally finally
{ {
log.Print(); log.Print();
} }
#endif #endif
} }
   
private void OnDestroy() private void OnDestroy()
{ {
this.Cleanup(); this.Cleanup();
   
print("ARMapRenderer: Destroyed."); print("ARMapRenderer: Destroyed.");
} }
#endregion #endregion
   
private void SetRelayVertices(IAntennaRelay relay) private void SetRelayVertices(IAntennaRelay relay)
{ {
if (relay == null) Color lastColor = default(Color);
{ Color thisColor;
return;  
}  
   
LineRenderer renderer = this[relay.vessel.id]; LineRenderer renderer = this[relay.vessel.id];
  Vector3d start = ScaledSpace.LocalToScaledSpace(relay.vessel.GetWorldPos3D());
Vector3d start;  
Vector3d end; float lineWidth;
  float d = Screen.height / 2f + 0.01f;
renderer.enabled = true;  
  if (MapView.Draw3DLines)
if (!relay.CanTransmit()) {
{ lineWidth = 0.005859375f * MapView.MapCamera.Distance;
renderer.SetColors(Color.red, Color.red);  
} }
else else
{ {
if (relay.transmitDistance < relay.nominalTransmitDistance) lineWidth = 2f;
{  
renderer.SetColors(Color.green, Color.green); start = MapView.MapCamera.camera.WorldToScreenPoint(start);
   
  start.z = start.z >= 0f ? d : -d;
  }
   
  renderer.SetWidth(lineWidth, lineWidth);
   
  renderer.SetPosition(0, start);
   
  int idx = 0;
   
  while (relay != null)
  {
  Vector3d nextPoint;
   
  renderer.enabled = true;
   
  if (!relay.CanTransmit())
  {
  thisColor = Color.red;
} }
else else
{ {
renderer.SetColors(Color.yellow, Color.yellow); if (relay.transmitDistance < relay.nominalTransmitDistance)
} {
} thisColor = Color.green;
  }
start = ScaledSpace.LocalToScaledSpace(relay.vessel.GetWorldPos3D()); else
  {
if (relay.KerbinDirect) thisColor = Color.yellow;
{ }
end = ScaledSpace.LocalToScaledSpace(AntennaRelay.Kerbin.position); }
}  
else if (lastColor != default(Color) && thisColor != lastColor)
{ {
if (relay.targetRelay == null) break;
{ }
return;  
} lastColor = thisColor;
end = ScaledSpace.LocalToScaledSpace(relay.targetRelay.vessel.GetWorldPos3D()); renderer.SetColors(thisColor, thisColor);
}  
  this.vesselFrameCache[relay.vessel.id] = true;
float lineWidth;  
  if (relay.KerbinDirect)
if (MapView.Draw3DLines) {
{ nextPoint = ScaledSpace.LocalToScaledSpace(AntennaRelay.Kerbin.position);
lineWidth = 0.005859375f * MapView.MapCamera.Distance; relay = null;
} }
else else
{ {
lineWidth = 2f; if (relay.targetRelay == null)
  {
start = MapView.MapCamera.camera.WorldToScreenPoint(start); return;
end = MapView.MapCamera.camera.WorldToScreenPoint(end); }
   
float d = Screen.height / 2f + 0.01f; nextPoint = ScaledSpace.LocalToScaledSpace(relay.targetRelay.vessel.GetWorldPos3D());
start.z = start.z >= 0f ? d : -d; relay = relay.targetRelay;
end.z = end.z >= 0f ? d : -d; }
}  
  if (!MapView.Draw3DLines)
renderer.SetWidth(lineWidth, lineWidth); {
  nextPoint = MapView.MapCamera.camera.WorldToScreenPoint(nextPoint);
renderer.SetPosition(0, start); nextPoint.z = nextPoint.z >= 0f ? d : -d;
renderer.SetPosition(1, end); }
   
  renderer.SetPosition(++idx, nextPoint);
  }
} }
   
public void Cleanup() public void Cleanup()
{ {
if (this.vesselLineRenderers != null && this.vesselLineRenderers.Count > 0) if (this.vesselLineRenderers != null && this.vesselLineRenderers.Count > 0)
{ {
foreach (LineRenderer lineRenderer in this.vesselLineRenderers.Values) foreach (LineRenderer lineRenderer in this.vesselLineRenderers.Values)
{ {
lineRenderer.enabled = false; lineRenderer.enabled = false;
GameObject.Destroy(lineRenderer.gameObject); GameObject.Destroy(lineRenderer.gameObject);
} }
this.vesselLineRenderers.Clear(); this.vesselLineRenderers.Clear();
} }
   
if (this.vesselFrameCache != null && this.vesselFrameCache.Count > 0) if (this.vesselFrameCache != null && this.vesselFrameCache.Count > 0)
{ {
this.vesselFrameCache.Clear(); this.vesselFrameCache.Clear();
} }
} }
} }
} }
   
   
// AntennaRange // AntennaRange
// //
// ProtoAntennaRelay.cs // ProtoAntennaRelay.cs
// //
// Copyright © 2014, toadicus // Copyright © 2014, 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 System.Linq;
using ToadicusTools; using ToadicusTools;
   
namespace AntennaRange namespace AntennaRange
{ {
/* /*
* 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.
* */ * */
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.
private ProtoPartSnapshot protoPart; private ProtoPartSnapshot protoPart;
   
/// <summary> /// <summary>
/// Gets the parent Vessel. /// Gets the parent Vessel.
/// </summary> /// </summary>
public override Vessel vessel public override Vessel vessel
{ {
get get
{ {
if (this.protoPart != null && this.protoPart.pVesselRef != null) if (this.protoPart != null && this.protoPart.pVesselRef != null)
{ {
return this.protoPart.pVesselRef.vesselRef; return this.protoPart.pVesselRef.vesselRef;
} }
else else
{ {
return null; return null;
} }
} }
} }
   
/// <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 override double nominalTransmitDistance public override double nominalTransmitDistance
{ {
get get
{ {
return this.moduleRef.nominalTransmitDistance; return this.moduleRef.nominalTransmitDistance;
} }
} }
   
/// <summary> /// <summary>
/// The maximum distance at which this relay can operate. /// The maximum distance at which this relay can operate.
/// </summary> /// </summary>
public override double maxTransmitDistance public override double maxTransmitDistance
{ {
get get
{ {
return moduleRef.maxTransmitDistance; return moduleRef.maxTransmitDistance;
} }
} }
   
/// <summary> /// <summary>
/// Gets the underlying part's title. /// Gets the underlying part's title.
/// </summary> /// </summary>
/// <value>The title.</value> /// <value>The title.</value>
public string Title public string Title
{ {
get get
{ {
if (this.protoPart != null && this.protoPart.partInfo != null) if (this.protoPart != null && this.protoPart.partInfo != null)
{ {
return this.protoPart.partInfo.title; return this.protoPart.partInfo.title;
} }
   
return string.Empty; return string.Empty;
} }
} }
   
/// <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 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> /// <summary>
/// Returns a <see cref="System.String"/> that represents the current <see cref="AntennaRange.ProtoAntennaRelay"/>. /// Returns a <see cref="System.String"/> that represents the current <see cref="AntennaRange.ProtoAntennaRelay"/>.
/// </summary> /// </summary>
/// <returns>A <see cref="System.String"/> that represents the current <see cref="AntennaRange.ProtoAntennaRelay"/>.</returns> /// <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 = new System.Text.StringBuilder();
"{0} on {1}",  
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);
  }
   
  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.ProtoAntennaRelay"/> class.
/// </summary> /// </summary>
/// <param name="ms">The ProtoPartModuleSnapshot to wrap</param> /// <param name="ms">The ProtoPartModuleSnapshot to wrap</param>
/// <param name="vessel">The parent Vessel</param> /// <param name="vessel">The parent Vessel</param>
public ProtoAntennaRelay(IAntennaRelay prefabRelay, ProtoPartSnapshot pps) : base(prefabRelay) public ProtoAntennaRelay(IAntennaRelay prefabRelay, ProtoPartSnapshot pps) : base(prefabRelay)
{ {
this.protoPart = pps; this.protoPart = pps;
} }
   
~ProtoAntennaRelay() ~ProtoAntennaRelay()
{ {
Tools.PostDebugMessage(string.Format( Tools.PostDebugMessage(string.Format(
"{0}: destroyed", "{0}: destroyed",
this.ToString() this.ToString()
)); ));
} }
} }
} }