Increased Orbital Period precision + fixes various NullRefs.
Increased Orbital Period precision + fixes various NullRefs.

// //
// Kerbal Engineer Redux // Kerbal Engineer Redux
// //
// Copyright (C) 2014 CYBUTEK // Copyright (C) 2014 CYBUTEK
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
// //
#region #region
using System; using System;
using System.Linq; using System.Linq;
using KerbalEngineer.Extensions; using KerbalEngineer.Extensions;
using KerbalEngineer.Settings; using KerbalEngineer.Settings;
using KerbalEngineer.VesselSimulator; using KerbalEngineer.VesselSimulator;
using UnityEngine; using UnityEngine;
#endregion #endregion
namespace KerbalEngineer.Editor namespace KerbalEngineer.Editor
{ {
[KSPAddon(KSPAddon.Startup.EditorAny, false)] [KSPAddon(KSPAddon.Startup.EditorAny, false)]
public class BuildAdvanced : MonoBehaviour public class BuildAdvanced : MonoBehaviour
{ {
#region Instance #region Instance
/// <summary> /// <summary>
/// Gets the current instance if started or returns null. /// Gets the current instance if started or returns null.
/// </summary> /// </summary>
public static BuildAdvanced Instance { get; private set; } public static BuildAdvanced Instance { get; private set; }
#endregion #endregion
#region Fields #region Fields
private bool hasChanged; private bool hasChanged;
private bool isEditorLocked; private bool isEditorLocked;
private int numberOfStages; private int numberOfStages;
private float atmosphericPercentage = 1.0f; private float atmosphericPercentage = 1.0f;
private float atmosphericVelocity; private float atmosphericVelocity;
private int windowId; private int windowId;
private Rect windowPosition = new Rect(265.0f, 45.0f, 0, 0); private Rect windowPosition = new Rect(265.0f, 45.0f, 0, 0);
#region Styles #region Styles
private GUIStyle areaBodiesStyle; private GUIStyle areaBodiesStyle;
private GUIStyle areaStyle; private GUIStyle areaStyle;
private GUIStyle buttonStyle; private GUIStyle buttonStyle;
private GUIStyle infoStyle; private GUIStyle infoStyle;
private GUIStyle settingStyle;  
private GUIStyle titleStyle; private GUIStyle titleStyle;
private GUIStyle windowStyle; private GUIStyle windowStyle;
#endregion #endregion
#endregion #endregion
#region Properties #region Properties
private bool compactMode; private bool compactMode;
private bool showAllStages; private bool showAllStages;
private bool showAtmosphericDetails; private bool showAtmosphericDetails;
private bool showReferenceBodies; private bool showReferenceBodies;
private bool visible = true; private bool visible = true;
/// <summary> /// <summary>
/// Gets and sets whether the display is enabled. /// Gets and sets whether the display is enabled.
/// </summary> /// </summary>
public bool Visible public bool Visible
{ {
get { return this.visible; } get { return this.visible; }
set set
{ {
this.visible = value; this.visible = value;
Logger.Log("BuildAdvanced->Visible = " + value); Logger.Log("BuildAdvanced->Visible = " + value);
} }
} }
/// <summary> /// <summary>
/// Gets and sets whether to show in compact mode. /// Gets and sets whether to show in compact mode.
/// </summary> /// </summary>
public bool CompactMode public bool CompactMode
{ {
get { return this.compactMode; } get { return this.compactMode; }
set { this.compactMode = value; } set
  {
  this.compactMode = value;
  Logger.Log("BuildAdvanced->CompactMode = " + value);
  }
} }
/// <summary> /// <summary>
/// Gets and sets whether to show all stages. /// Gets and sets whether to show all stages.
/// </summary> /// </summary>
public bool ShowAllStages public bool ShowAllStages
{ {
get { return this.showAllStages; } get { return this.showAllStages; }
set { this.showAllStages = value; } set
  {
  this.showAllStages = value;
  Logger.Log("BuildAdvanced->ShowAllStages = " + value);
  }
} }
/// <summary> /// <summary>
/// Gets and sets whether to use atmospheric details. /// Gets and sets whether to use atmospheric details.
/// </summary> /// </summary>
public bool ShowAtmosphericDetails public bool ShowAtmosphericDetails
{ {
get { return this.showAtmosphericDetails; } get { return this.showAtmosphericDetails; }
set { this.showAtmosphericDetails = value; } set
  {
  this.showAtmosphericDetails = value;
  Logger.Log("BuildAdvanced->ShowAtmosphericDetails = " + value);
  }
} }
/// <summary> /// <summary>
/// Gets and sets whether to show the reference body selection. /// Gets and sets whether to show the reference body selection.
/// </summary> /// </summary>
public bool ShowReferenceBodies public bool ShowReferenceBodies
{ {
get { return this.showReferenceBodies; } get { return this.showReferenceBodies; }
set { this.showReferenceBodies = value; } set
  {
  this.showReferenceBodies = value;
  Logger.Log("BuildAdvanced->ShowReferenceBodies = " + value);
  }
} }
#endregion #endregion
#region Initialisation #region Initialisation
private void Awake() private void Awake()
{ {
Instance = this; Instance = this;
this.Load(); this.Load();
} }
private void Start() private void Start()
{ {
this.windowId = this.GetHashCode(); this.windowId = this.GetHashCode();
this.InitialiseStyles(); this.InitialiseStyles();
RenderingManager.AddToPostDrawQueue(0, this.OnDraw); RenderingManager.AddToPostDrawQueue(0, this.OnDraw);
} }
/// <summary> /// <summary>
/// Initialises all the styles that are required. /// Initialises all the styles that are required.
/// </summary> /// </summary>
private void InitialiseStyles() private void InitialiseStyles()
{ {
this.areaBodiesStyle = new GUIStyle(HighLogic.Skin.box); try
  {
this.windowStyle = new GUIStyle(HighLogic.Skin.window) this.areaBodiesStyle = new GUIStyle(HighLogic.Skin.box);
{  
alignment = TextAnchor.UpperLeft this.windowStyle = new GUIStyle(HighLogic.Skin.window)
}; {
  alignment = TextAnchor.UpperLeft
this.areaStyle = new GUIStyle(HighLogic.Skin.box) };
{  
padding = new RectOffset(0, 0, 9, 0) this.areaStyle = new GUIStyle(HighLogic.Skin.box)
}; {
  padding = new RectOffset(0, 0, 9, 0)
this.buttonStyle = new GUIStyle(HighLogic.Skin.button) };
{  
normal = this.buttonStyle = new GUIStyle(HighLogic.Skin.button)
{ {
textColor = Color.white normal =
}, {
fontSize = 11, textColor = Color.white
fontStyle = FontStyle.Bold, },
alignment = TextAnchor.MiddleCenter fontSize = 11,
}; fontStyle = FontStyle.Bold,
  alignment = TextAnchor.MiddleCenter
this.titleStyle = new GUIStyle(HighLogic.Skin.label) };
{  
normal = this.titleStyle = new GUIStyle(HighLogic.Skin.label)
{ {
textColor = Color.white normal =
}, {
fontSize = 11, textColor = Color.white
fontStyle = FontStyle.Bold, },
alignment = TextAnchor.MiddleCenter, fontSize = 11,
stretchWidth = true fontStyle = FontStyle.Bold,
}; alignment = TextAnchor.MiddleCenter,
  stretchWidth = true
this.infoStyle = new GUIStyle(HighLogic.Skin.label) };
{  
fontSize = 11, this.infoStyle = new GUIStyle(HighLogic.Skin.label)
fontStyle = FontStyle.Bold, {
alignment = TextAnchor.MiddleCenter, fontSize = 11,
stretchWidth = true fontStyle = FontStyle.Bold,
}; alignment = TextAnchor.MiddleCenter,
  stretchWidth = true
this.settingStyle = new GUIStyle(HighLogic.Skin.label) };
{ }
normal = catch (Exception ex)
{ {
textColor = Color.white Logger.Exception(ex, "BuildAdvanced->InitialiseStyles");
}, }
fontSize = 11,  
fontStyle = FontStyle.Bold,  
alignment = TextAnchor.MiddleLeft,  
stretchWidth = true  
};  
} }
#endregion #endregion
#region Update and Drawing #region Update and Drawing
private void Update() private void Update()
{ {
try try
{ {
if (!this.visible || EditorLogic.fetch == null || EditorLogic.fetch.ship.parts.Count == 0) if (!this.visible || EditorLogic.fetch == null || EditorLogic.fetch.ship.parts.Count == 0)
{ {
return; return;
} }
// Configure the simulation parameters based on the selected reference body. // Configure the simulation parameters based on the selected reference body.
SimManager.Gravity = CelestialBodies.Instance.SelectedBodyInfo.Gravity; SimManager.Gravity = CelestialBodies.Instance.SelectedBodyInfo.Gravity;
if (this.showAtmosphericDetails) if (this.showAtmosphericDetails)
{ {
SimManager.Atmosphere = CelestialBodies.Instance.SelectedBodyInfo.Atmosphere * 0.01d * this.atmosphericPercentage; SimManager.Atmosphere = CelestialBodies.Instance.SelectedBodyInfo.Atmosphere * 0.01d * this.atmosphericPercentage;
} }
else else
{ {
SimManager.Atmosphere = 0; SimManager.Atmosphere = 0;
} }
SimManager.Velocity = this.atmosphericVelocity; SimManager.Velocity = this.atmosphericVelocity;
SimManager.TryStartSimulation(); SimManager.TryStartSimulation();
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Log("BuildAdvanced->Update"); Logger.Exception(ex, "BuildAdvanced->Update");
Logger.Exception(ex);  
} }
} }
private void OnDraw() private void OnDraw()
{ {
try try
{ {
if (!this.visible || EditorLogic.fetch == null || EditorLogic.fetch.ship.parts.Count == 0) if (!this.visible || EditorLogic.fetch == null || EditorLogic.fetch.ship.parts.Count == 0)
{ {
return; return;
} }
SimManager.RequestSimulation(); SimManager.RequestSimulation();
// Change the window title based on whether in compact mode or not. // Change the window title based on whether in compact mode or not.
var title = !this.compactMode ? "KERBAL ENGINEER REDUX " + EngineerGlobals.AssemblyVersion : "K.E.R. " + EngineerGlobals.AssemblyVersion; var title = !this.compactMode ? "KERBAL ENGINEER REDUX " + EngineerGlobals.AssemblyVersion : "K.E.R. " + EngineerGlobals.AssemblyVersion;
// Reset the window size when the staging or something else has changed. if (SimManager.Stages != null)
var stageCount = SimManager.Stages != null ? SimManager.Stages.Count(stage => this.showAllStages || stage.deltaV > 0) : 0; {
if (this.hasChanged || stageCount != this.numberOfStages) // Reset the window size when the staging or something else has changed.
{ var stageCount = SimManager.Stages.Count(stage => this.showAllStages || stage.deltaV > 0);
this.hasChanged = false; if (this.hasChanged || stageCount != this.numberOfStages)
this.numberOfStages = stageCount; {
  this.hasChanged = false;
this.windowPosition.width = 0; this.numberOfStages = stageCount;
this.windowPosition.height = 0;  
} this.windowPosition.width = 0;
  this.windowPosition.height = 0;
this.windowPosition = GUILayout.Window(this.windowId, this.windowPosition, this.Window, title, this.windowStyle).ClampToScreen(); }
   
// Check editor lock to manage click-through. this.windowPosition = GUILayout.Window(this.windowId, this.windowPosition, this.Window, title, this.windowStyle).ClampToScreen();
this.CheckEditorLock();  
} // Check editor lock to manage click-through.
catch (Exception ex) this.CheckEditorLock();
{ }
Logger.Log("BuildAdvanced->OnDraw"); }
Logger.Exception(ex); catch (Exception ex)
  {
  Logger.Exception(ex, "BuildAdvanced->OnDraw");
} }
} }
/// <summary> /// <summary>
/// Checks whether the editor should be locked to stop click-through. /// Checks whether the editor should be locked to stop click-through.
/// </summary> /// </summary>
private void CheckEditorLock() private void CheckEditorLock()
{ {
if (this.windowPosition.MouseIsOver()) try
{ {
EditorLogic.fetch.State = EditorLogic.EditorState.GUI_SELECTED; if (this.windowPosition.MouseIsOver())
this.isEditorLocked = true; {
} EditorLogic.fetch.State = EditorLogic.EditorState.GUI_SELECTED;
else if (!this.windowPosition.MouseIsOver() && this.isEditorLocked) this.isEditorLocked = true;
{ }
EditorLogic.fetch.State = EditorLogic.EditorState.PAD_UNSELECTED; else if (!this.windowPosition.MouseIsOver() && this.isEditorLocked)
this.isEditorLocked = false; {
  EditorLogic.fetch.State = EditorLogic.EditorState.PAD_UNSELECTED;
  this.isEditorLocked = false;
  }
  }
  catch (Exception ex)
  {
  Logger.Exception(ex, "BuildAdvanced->CheckEditorLock");
} }
} }
/// <summary> /// <summary>
/// Draws the OnGUI window. /// Draws the OnGUI window.
/// </summary> /// </summary>
private void Window(int windowId) private void Window(int windowId)
{ {
// Draw the compact mode toggle. try
if (GUI.Toggle(new Rect(this.windowPosition.width - 70.0f, 5.0f, 65.0f, 20.0f), this.compactMode, "COMPACT", this.buttonStyle) != this.compactMode) {
{ // Draw the compact mode toggle.
this.hasChanged = true; if (GUI.Toggle(new Rect(this.windowPosition.width - 70.0f, 5.0f, 65.0f, 20.0f), this.compactMode, "COMPACT", this.buttonStyle) != this.compactMode)
this.compactMode = !this.compactMode;  
}  
   
// When not in compact mode draw the 'All Stages' and 'Atmospheric' toggles.  
if (!this.compactMode)  
{  
if (GUI.Toggle(new Rect(this.windowPosition.width - 153.0f, 5.0f, 80.0f, 20.0f), this.showAllStages, "ALL STAGES", this.buttonStyle) != this.showAllStages)  
{ {
this.hasChanged = true; this.hasChanged = true;
this.showAllStages = !this.showAllStages; this.compactMode = !this.compactMode;
} }
   
if (GUI.Toggle(new Rect(this.windowPosition.width - 251.0f, 5.0f, 95.0f, 20.0f), this.showAtmosphericDetails, "ATMOSPHERIC", this.buttonStyle) != this.showAtmosphericDetails) // When not in compact mode draw the 'All Stages' and 'Atmospheric' toggles.
{ if (!this.compactMode)
this.hasChanged = true; {
this.showAtmosphericDetails = !this.showAtmosphericDetails; if (GUI.Toggle(new Rect(this.windowPosition.width - 153.0f, 5.0f, 80.0f, 20.0f), this.showAllStages, "ALL STAGES", this.buttonStyle) != this.showAllStages)
} {
  this.hasChanged = true;
if (GUI.Toggle(new Rect(this.windowPosition.width - 379.0f, 5.0f, 125.0f, 20.0f), this.showReferenceBodies, "REFERENCE BODIES", this.buttonStyle) != this.showReferenceBodies) this.showAllStages = !this.showAllStages;
{ }
this.hasChanged = true;  
this.showReferenceBodies = !this.showReferenceBodies; if (GUI.Toggle(new Rect(this.windowPosition.width - 251.0f, 5.0f, 95.0f, 20.0f), this.showAtmosphericDetails, "ATMOSPHERIC", this.buttonStyle) != this.showAtmosphericDetails)
} {
} this.hasChanged = true;
  this.showAtmosphericDetails = !this.showAtmosphericDetails;
// Draw the main informational display box. }
   
if (!this.compactMode) if (GUI.Toggle(new Rect(this.windowPosition.width - 379.0f, 5.0f, 125.0f, 20.0f), this.showReferenceBodies, "REFERENCE BODIES", this.buttonStyle) != this.showReferenceBodies)
{ {
GUILayout.BeginHorizontal(this.areaStyle); this.hasChanged = true;
this.DrawStageNumbers(); this.showReferenceBodies = !this.showReferenceBodies;
//this.DrawPartCount(); }
this.DrawCost(); }
this.DrawMass();  
this.DrawIsp(); // Draw the main informational display box.
this.DrawThrust();  
this.DrawTwr(); if (!this.compactMode)
this.DrawDeltaV(); {
this.DrawBurnTime(); GUILayout.BeginHorizontal(this.areaStyle);
  this.DrawStageNumbers();
  //this.DrawPartCount();
  this.DrawCost();
  this.DrawMass();
  this.DrawIsp();
  this.DrawThrust();
  this.DrawTwr();
  this.DrawDeltaV();
  this.DrawBurnTime();
  GUILayout.EndHorizontal();
   
  if (this.showAtmosphericDetails)
  {
  GUILayout.BeginVertical(this.areaStyle);
  this.DrawAtmosphericDetails();
  GUILayout.EndVertical();
  }
   
  if (this.showReferenceBodies)
  {
  GUILayout.BeginVertical(this.areaBodiesStyle);
  this.DrawReferenceBodies();
  GUILayout.EndVertical();
  }
  }
  else // Draw only a few details when in compact mode.
  {
  GUILayout.BeginHorizontal(this.areaStyle);
  this.DrawStageNumbers();
  this.DrawTwr();
  this.DrawDeltaV();
  GUILayout.EndHorizontal();
  }
   
  GUI.DragWindow();
  }
  catch (Exception ex)
  {
  Logger.Exception(ex, "BuildAdvanced->Window");
  }
  }
   
  /// <summary>
  /// Draws the atmospheric settings.
  /// </summary>
  private void DrawAtmosphericDetails()
  {
  try
  {
  GUILayout.BeginHorizontal();
  GUILayout.Label("Pressure " + (this.atmosphericPercentage * 100.0f).ToString("F1") + "%", this.titleStyle, GUILayout.Width(125.0f));
  GUI.skin = HighLogic.Skin;
  this.atmosphericPercentage = GUILayout.HorizontalSlider(this.atmosphericPercentage, 0, 1.0f);
  GUI.skin = null;
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
  GUILayout.BeginHorizontal();
if (this.showAtmosphericDetails) GUILayout.Label("Velocity " + this.atmosphericVelocity.ToString("F1") + "m/s", this.titleStyle, GUILayout.Width(125.0f));
{ GUI.skin = HighLogic.Skin;
GUILayout.BeginVertical(this.areaStyle); this.atmosphericVelocity = GUILayout.HorizontalSlider(this.atmosphericVelocity, 0, 2500f);
this.DrawAtmosphericDetails(); GUI.skin = null;
GUILayout.EndVertical();  
}  
   
if (this.showReferenceBodies)  
{  
GUILayout.BeginVertical(this.areaBodiesStyle);  
this.DrawReferenceBodies();  
GUILayout.EndVertical();  
}  
}  
else // Draw only a few details when in compact mode.  
{  
GUILayout.BeginHorizontal(this.areaStyle);  
this.DrawStageNumbers();  
this.DrawTwr();  
this.DrawDeltaV();  
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
} }
  catch (Exception ex)
GUI.DragWindow(); {
} Logger.Exception(ex, "BuildAdvanced->DrawAtmosphericDetails");
  }
/// <summary>  
/// Draws the atmospheric settings.  
/// </summary>  
private void DrawAtmosphericDetails()  
{  
GUILayout.BeginHorizontal();  
GUILayout.Label("Pressure " + (this.atmosphericPercentage * 100.0f).ToString("F1") + "%", this.titleStyle, GUILayout.Width(125.0f));  
GUI.skin = HighLogic.Skin;  
this.atmosphericPercentage = GUILayout.HorizontalSlider(this.atmosphericPercentage, 0, 1.0f);  
GUI.skin = null;  
GUILayout.EndHorizontal();  
GUILayout.BeginHorizontal();  
GUILayout.Label("Velocity " + this.atmosphericVelocity.ToString("F1") + "m/s", this.titleStyle, GUILayout.Width(125.0f));  
GUI.skin = HighLogic.Skin;  
this.atmosphericVelocity = GUILayout.HorizontalSlider(this.atmosphericVelocity, 0, 2500f);  
GUI.skin = null;  
GUILayout.EndHorizontal();  
} }
/// <summary> /// <summary>
/// Draws all the reference bodies. /// Draws all the reference bodies.
/// </summary> /// </summary>
private void DrawReferenceBodies() private void DrawReferenceBodies()
{ {
var index = 0; try
  {
foreach (var bodyName in CelestialBodies.Instance.BodyList.Keys) var index = 0;
{  
if (index % 8 == 0) foreach (var bodyName in CelestialBodies.Instance.BodyList.Keys)
{ {
if (index > 0) if (index % 8 == 0)
{ {
GUILayout.EndHorizontal(); if (index > 0)
} {
GUILayout.BeginHorizontal(); GUILayout.EndHorizontal();
} }
if (GUILayout.Toggle(CelestialBodies.Instance.SelectedBodyName == bodyName, bodyName, this.buttonStyle)) GUILayout.BeginHorizontal();
{ }
CelestialBodies.Instance.SelectedBodyName = bodyName; if (GUILayout.Toggle(CelestialBodies.Instance.SelectedBodyName == bodyName, bodyName, this.buttonStyle))
} {
index++; CelestialBodies.Instance.SelectedBodyName = bodyName;
} }
GUILayout.EndHorizontal(); index++;
  }
  GUILayout.EndHorizontal();
  }
  catch (Exception ex)
  {
  Logger.Exception(ex, "BuildAdvanced->DrawReferenceBodies");
  }
} }
/// <summary> /// <summary>
/// Draws the stage number column. /// Draws the stage number column.
/// </summary> /// </summary>
private void DrawStageNumbers() private void DrawStageNumbers()
{ {
GUILayout.BeginVertical(GUILayout.Width(30.0f)); try
GUILayout.Label(string.Empty, this.titleStyle); {
foreach (var stage in SimManager.Stages) GUILayout.BeginVertical(GUILayout.Width(30.0f));
{ GUILayout.Label(string.Empty, this.titleStyle);
if (this.showAllStages || stage.deltaV > 0) foreach (var stage in SimManager.Stages)
{ {
GUILayout.Label("S" + stage.number, this.titleStyle); if (this.showAllStages || stage.deltaV > 0)
} {
} GUILayout.Label("S" + stage.number, this.titleStyle);
GUILayout.EndVertical(); }
  }
  GUILayout.EndVertical();
  }
  catch (Exception ex)
  {
  Logger.Exception(ex, "BuildAdvanced->DrawStageNumbers");
  }
} }
/// <summary> /// <summary>
/// Draws the part count column. /// Draws the part count column.
/// </summary> /// </summary>
private void DrawPartCount() private void DrawPartCount()
{ {
GUILayout.BeginVertical(GUILayout.Width(50.0f)); try
GUILayout.Label("PARTS", this.titleStyle); {
foreach (var stage in SimManager.Stages)  
{ GUILayout.BeginVertical(GUILayout.Width(50.0f));
if (this.showAllStages || stage.deltaV > 0) GUILayout.Label("PARTS", this.titleStyle);
{ foreach (var stage in SimManager.Stages)
//GUILayout.Label(stage.PartCount.ToString("N0"), this.infoStyle); {
} if (this.showAllStages || stage.deltaV > 0)
} {
GUILayout.EndVertical(); //GUILayout.Label(stage.PartCount.ToString("N0"), this.infoStyle);
  }
  }
  GUILayout.EndVertical();
  }
  catch (Exception ex)
  {
  Logger.Exception(ex, "BuildAdvanced->DrawPartCount");
  }
} }
/// <summary> /// <summary>
/// Draws the cost column. /// Draws the cost column.
/// </summary> /// </summary>
private void DrawCost() private void DrawCost()
{ {
GUILayout.BeginVertical(GUILayout.Width(110.0f)); try
GUILayout.Label("COST", this.titleStyle); {
foreach (var stage in SimManager.Stages) GUILayout.BeginVertical(GUILayout.Width(110.0f));
{ GUILayout.Label("COST", this.titleStyle);
if (this.showAllStages || stage.deltaV > 0) foreach (var stage in SimManager.Stages)
{ {
GUILayout.Label(stage.cost.ToString("N0") + " / " + stage.totalCost.ToString("N0"), this.infoStyle); if (this.showAllStages || stage.deltaV > 0)
} {
} GUILayout.Label(stage.cost.ToString("N0") + " / " + stage.totalCost.ToString("N0"), this.infoStyle);
GUILayout.EndVertical(); }
  }
  GUILayout.EndVertical();
  }
  catch (Exception ex)
  {
  Logger.Exception(ex, "BuildAdvanced->DrawCost");
  }
} }
/// <summary> /// <summary>
/// Draws the mass column. /// Draws the mass column.
/// </summary> /// </summary>
private void DrawMass() private void DrawMass()
{ {
GUILayout.BeginVertical(GUILayout.Width(110.0f)); try
GUILayout.Label("MASS", this.titleStyle); {
foreach (var stage in SimManager.Stages)  
{ GUILayout.BeginVertical(GUILayout.Width(110.0f));
if (this.showAllStages || stage.deltaV > 0) GUILayout.Label("MASS", this.titleStyle);
{ foreach (var stage in SimManager.Stages)
GUILayout.Label(stage.mass.ToMass(false) + " / " + stage.totalMass.ToMass(), this.infoStyle); {
} if (this.showAllStages || stage.deltaV > 0)
} {
GUILayout.EndVertical(); GUILayout.Label(stage.mass.ToMass(false) + " / " + stage.totalMass.ToMass(), this.infoStyle);
  }
  }
  GUILayout.EndVertical();
  }
  catch (Exception ex)
  {
  Logger.Exception(ex, "BuildAdvanced->DrawMass");
  }
} }
/// <summary> /// <summary>
/// Draws the specific impluse column. /// Draws the specific impluse column.
/// </summary> /// </summary>
private void DrawIsp() private void DrawIsp()
{ {
GUILayout.BeginVertical(GUILayout.Width(75.0f)); try
GUILayout.Label("ISP", this.titleStyle); {
foreach (var stage in SimManager.Stages) GUILayout.BeginVertical(GUILayout.Width(75.0f));
{ GUILayout.Label("ISP", this.titleStyle);
if (this.showAllStages || stage.deltaV > 0) foreach (var stage in SimManager.Stages)
{ {
GUILayout.Label(stage.isp.ToString("F1") + "s", this.infoStyle); if (this.showAllStages || stage.deltaV > 0)
} {
} GUILayout.Label(stage.isp.ToString("F1") + "s", this.infoStyle);
GUILayout.EndVertical(); }
  }
  GUILayout.EndVertical();
  }
  catch (Exception ex)
  {
  Logger.Exception(ex, "BuildAdvanced->DrawIsp");
  }
} }
/// <summary> /// <summary>
/// Draws the thrust column. /// Draws the thrust column.
/// </summary> /// </summary>
private void DrawThrust() private void DrawThrust()
{ {
GUILayout.BeginVertical(GUILayout.Width(75.0f)); try
GUILayout.Label("THRUST", this.titleStyle); {
foreach (var stage in SimManager.Stages) GUILayout.BeginVertical(GUILayout.Width(75.0f));
{ GUILayout.Label("THRUST", this.titleStyle);
if (this.showAllStages || stage.deltaV > 0) foreach (var stage in SimManager.Stages)
{ {
GUILayout.Label(stage.thrust.ToForce(), this.infoStyle); if (this.showAllStages || stage.deltaV > 0)
} {
} GUILayout.Label(stage.thrust.ToForce(), this.infoStyle);
GUILayout.EndVertical(); }
  }
  GUILayout.EndVertical();
  }
  catch (Exception ex)
  {
  Logger.Exception(ex, "BuildAdvanced->DrawThrust");
  }
} }
/// <summary> /// <summary>
/// Drwas the thrust to weight ratio column. /// Drwas the thrust to weight ratio column.
/// </summary> /// </summary>
private void DrawTwr() private void DrawTwr()
{ {
GUILayout.BeginVertical(GUILayout.Width(100.0f)); try
GUILayout.Label("TWR (MAX)", this.titleStyle); {
foreach (var stage in SimManager.Stages) GUILayout.BeginVertical(GUILayout.Width(100.0f));
{ GUILayout.Label("TWR (MAX)", this.titleStyle);
if (this.showAllStages || stage.deltaV > 0) foreach (var stage in SimManager.Stages)
{ {
GUILayout.Label(stage.thrustToWeight.ToString("F2") + " (" + stage.maxThrustToWeight.ToString("F2") + ")", this.infoStyle); if (this.showAllStages || stage.deltaV > 0)
} {
} GUILayout.Label(stage.thrustToWeight.ToString("F2") + " (" + stage.maxThrustToWeight.ToString("F2") + ")", this.infoStyle);
GUILayout.EndVertical(); }
  }
  GUILayout.EndVertical();
  }
  catch (Exception ex)
  {
  Logger.Exception(ex, "BuildAdvanced->DrawTwr");
  }
} }
/// <summary> /// <summary>
/// Draws the deltaV column. /// Draws the deltaV column.
/// </summary> /// </summary>
private void DrawDeltaV() private void DrawDeltaV()
{ {
GUILayout.BeginVertical(GUILayout.Width(100.0f)); try
GUILayout.Label("DELTA-V", this.titleStyle); {
foreach (var stage in SimManager.Stages) GUILayout.BeginVertical(GUILayout.Width(100.0f));
{ GUILayout.Label("DELTA-V", this.titleStyle);
if (this.showAllStages || stage.deltaV > 0) foreach (var stage in SimManager.Stages)
{ {
GUILayout.Label(stage.deltaV.ToString("N0") + " / " + stage.inverseTotalDeltaV.ToString("N0") + "m/s", this.infoStyle); if (this.showAllStages || stage.deltaV > 0)
} {
} GUILayout.Label(stage.deltaV.ToString("N0") + " / " + stage.inverseTotalDeltaV.ToString("N0") + "m/s", this.infoStyle);
GUILayout.EndVertical(); }
  }
  GUILayout.EndVertical();
  }
  catch (Exception ex)
  {
  Logger.Exception(ex, "BuildAdvanced->DrawDeltaV");
  }
} }
/// <summary> /// <summary>
/// Draws the burn time column. /// Draws the burn time column.
/// </summary> /// </summary>
private void DrawBurnTime() private void DrawBurnTime()
{ {
GUILayout.BeginVertical(GUILayout.Width(75.0f)); try
GUILayout.Label("BURN", this.titleStyle); {
foreach (var stage in SimManager.Stages) GUILayout.BeginVertical(GUILayout.Width(75.0f));
{ GUILayout.Label("BURN", this.titleStyle);
if (this.showAllStages || stage.deltaV > 0) foreach (var stage in SimManager.Stages)
{ {
GUILayout.Label(stage.time.ToTime(), this.infoStyle); if (this.showAllStages || stage.deltaV > 0)
} {
} GUILayout.Label(stage.time.ToTime(), this.infoStyle);
GUILayout.EndVertical(); }
  }
  GUILayout.EndVertical();
  }
  catch (Exception ex)
  {
  Logger.Exception(ex, "BuildAdvanced->DrawBurnTime");
  }
} }
#endregion #endregion
#region Save and Load #region Save and Load
/// <summary> /// <summary>
/// Saves the settings when this object is destroyed. /// Saves the settings when this object is destroyed.
/// </summary> /// </summary>
private void OnDestroy() private void OnDestroy()
{ {
try try
{ {
var handler = new SettingHandler(); var handler = new SettingHandler();
handler.Set("visible", this.visible); handler.Set("visible", this.visible);
handler.Set("windowPositionX", this.windowPosition.x); handler.Set("windowPositionX", this.windowPosition.x);
handler.Set("windowPositionY", this.windowPosition.y); handler.Set("windowPositionY", this.windowPosition.y);
handler.Set("compactMode", this.compactMode); handler.Set("compactMode", this.compactMode);
handler.Set("showAllStages", this.showAllStages); handler.Set("showAllStages", this.showAllStages);
handler.Set("showAtmosphericDetails", this.showAtmosphericDetails); handler.Set("showAtmosphericDetails", this.showAtmosphericDetails);
handler.Set("showReferenceBodies", this.showReferenceBodies); handler.Set("showReferenceBodies", this.showReferenceBodies);
handler.Set("selectedBodyName", CelestialBodies.Instance.SelectedBodyName); handler.Set("selectedBodyName", CelestialBodies.Instance.SelectedBodyName);
handler.Save("BuildAdvanced.xml"); handler.Save("BuildAdvanced.xml");
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Log("BuildAdvanced->OnDestroy"); Logger.Exception(ex, "BuildAdvanced->OnDestroy");
Logger.Exception(ex);  
} }
} }
/// <summary> /// <summary>
/// Loads the settings when this object is created. /// Loads the settings when this object is created.
/// </summary> /// </summary>
private void Load() private void Load()
{ {
try try
{ {
var handler = SettingHandler.Load("BuildAdvanced.xml"); var handler = SettingHandler.Load("BuildAdvanced.xml");
handler.Get("visible", ref this.visible); handler.Get("visible", ref this.visible);
this.windowPosition.x = handler.Get("windowPositionX", this.windowPosition.x); this.windowPosition.x = handler.Get("windowPositionX", this.windowPosition.x);
this.windowPosition.y = handler.Get("windowPositionY", this.windowPosition.y); this.windowPosition.y = handler.Get("windowPositionY", this.windowPosition.y);
handler.Get("compactMode", ref this.compactMode); handler.Get("compactMode", ref this.compactMode);
handler.Get("showAllStages", ref this.showAllStages); handler.Get("showAllStages", ref this.showAllStages);
handler.Get("showAtmosphericDetails", ref this.showAtmosphericDetails); handler.Get("showAtmosphericDetails", ref this.showAtmosphericDetails);
CelestialBodies.Instance.SelectedBodyName = handler.Get("selectedBodyName", CelestialBodies.Instance.SelectedBodyName); CelestialBodies.Instance.SelectedBodyName = handler.Get("selectedBodyName", CelestialBodies.Instance.SelectedBodyName);
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Log("BuildAdvanced->Load"); Logger.Exception(ex, "BuildAdvanced->Load");
Logger.Exception(ex);  
} }
} }
#endregion #endregion
} }
} }
// //
// Kerbal Engineer Redux // Kerbal Engineer Redux
// //
// Copyright (C) 2014 CYBUTEK // Copyright (C) 2014 CYBUTEK
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
// //
#region #region
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using KerbalEngineer.Extensions; using KerbalEngineer.Extensions;
using KerbalEngineer.Settings; using KerbalEngineer.Settings;
using KerbalEngineer.VesselSimulator; using KerbalEngineer.VesselSimulator;
using UnityEngine; using UnityEngine;
#endregion #endregion
namespace KerbalEngineer.Editor namespace KerbalEngineer.Editor
{ {
[KSPAddon(KSPAddon.Startup.EditorAny, false)] [KSPAddon(KSPAddon.Startup.EditorAny, false)]
public class BuildOverlay : MonoBehaviour public class BuildOverlay : MonoBehaviour
{ {
#region Instance #region Instance
/// <summary> /// <summary>
/// Gets the current instance if started or returns null. /// Gets the current instance if started or returns null.
/// </summary> /// </summary>
public static BuildOverlay Instance { get; private set; } public static BuildOverlay Instance { get; private set; }
#endregion #endregion
#region Fields #region Fields
private readonly Stopwatch tooltipInfoTimer = new Stopwatch(); private readonly Stopwatch tooltipInfoTimer = new Stopwatch();
private Part selectedPart; private Part selectedPart;
private int windowId; private int windowId;
private Rect windowPosition = new Rect(300.0f, 0, 0, 0); private Rect windowPosition = new Rect(300.0f, 0, 0, 0);
#endregion #endregion
#region Constructors #region Constructors
private void Awake() private void Awake()
{ {
Instance = this; Instance = this;
} }
private void Start() private void Start()
{ {
this.windowId = this.GetHashCode(); this.windowId = this.GetHashCode();
this.InitialiseStyles(); this.InitialiseStyles();
this.Load(); this.Load();
RenderingManager.AddToPostDrawQueue(0, this.OnDraw); RenderingManager.AddToPostDrawQueue(0, this.OnDraw);
} }
#endregion #endregion
#region Properties #region Properties
private float tooltipInfoDelay = 0.5f; private float tooltipInfoDelay = 0.5f;
private bool visible = true; private bool visible = true;
public float TooltipInfoDelay public float TooltipInfoDelay
{ {
get { return this.tooltipInfoDelay; } get { return this.tooltipInfoDelay; }
set { this.tooltipInfoDelay = value; } set
  {
  this.tooltipInfoDelay = value;
  Logger.Log("BuildOverlay->TooltipInfoDelay = " + value);
  }
} }
/// <summary> /// <summary>
/// Gets and sets whether the display is enabled. /// Gets and sets whether the display is enabled.
/// </summary> /// </summary>
public bool Visible public bool Visible
{ {
get { return this.visible; } get { return this.visible; }
set set
{ {
this.visible = value; this.visible = value;
Logger.Log("BuildOverlay->Visible = " + value); Logger.Log("BuildOverlay->Visible = " + value);
} }
} }
#endregion #endregion
#region GUIStyles #region GUIStyles
private GUIStyle infoStyle; private GUIStyle infoStyle;
private GUIStyle titleStyle; private GUIStyle titleStyle;
private GUIStyle tooltipInfoStyle; private GUIStyle tooltipInfoStyle;
private GUIStyle tooltipTitleStyle; private GUIStyle tooltipTitleStyle;
private GUIStyle windowStyle; private GUIStyle windowStyle;
private void InitialiseStyles() private void InitialiseStyles()
{ {
this.windowStyle = new GUIStyle(GUIStyle.none) try
{ {
margin = new RectOffset(), this.windowStyle = new GUIStyle(GUIStyle.none)
padding = new RectOffset() {
}; margin = new RectOffset(),
  padding = new RectOffset()
this.titleStyle = new GUIStyle(HighLogic.Skin.label) };
{  
normal = this.titleStyle = new GUIStyle(HighLogic.Skin.label)
{ {
textColor = Color.white normal =
}, {
margin = new RectOffset(), textColor = Color.white
padding = new RectOffset(), },
fontSize = 11, margin = new RectOffset(),
fontStyle = FontStyle.Bold, padding = new RectOffset(),
stretchWidth = true fontSize = 11,
}; fontStyle = FontStyle.Bold,
  stretchWidth = true
this.infoStyle = new GUIStyle(HighLogic.Skin.label) };
{  
margin = new RectOffset(), this.infoStyle = new GUIStyle(HighLogic.Skin.label)
padding = new RectOffset(), {
fontSize = 11, margin = new RectOffset(),
fontStyle = FontStyle.Bold, padding = new RectOffset(),
stretchWidth = true fontSize = 11,
}; fontStyle = FontStyle.Bold,
  stretchWidth = true
this.tooltipTitleStyle = new GUIStyle(HighLogic.Skin.label) };
{  
normal = this.tooltipTitleStyle = new GUIStyle(HighLogic.Skin.label)
{ {
textColor = Color.white normal =
}, {
fontSize = 11, textColor = Color.white
fontStyle = FontStyle.Bold, },
stretchWidth = true fontSize = 11,
}; fontStyle = FontStyle.Bold,
  stretchWidth = true
this.tooltipInfoStyle = new GUIStyle(HighLogic.Skin.label) };
{  
fontSize = 11, this.tooltipInfoStyle = new GUIStyle(HighLogic.Skin.label)
fontStyle = FontStyle.Bold, {
stretchWidth = true fontSize = 11,
}; fontStyle = FontStyle.Bold,
  stretchWidth = true
  };
  }
  catch (Exception ex)
  {
  Logger.Exception(ex, "BuildOverlay->InitialiseStyles");
  }
} }
#endregion #endregion
#region Update and Drawing #region Update and Drawing
private void Update() private void Update()
{ {
try try
{ {
if (!this.visible || BuildAdvanced.Instance == null || EditorLogic.fetch == null || EditorLogic.fetch.ship.parts.Count == 0 || EditorLogic.fetch.editorScreen != EditorLogic.EditorScreen.Parts) if (!this.visible || BuildAdvanced.Instance == null || EditorLogic.fetch == null || EditorLogic.fetch.ship.parts.Count == 0 || EditorLogic.fetch.editorScreen != EditorLogic.EditorScreen.Parts)
{ {
return; return;
} }
// Configure the simulation parameters based on the selected reference body. // Configure the simulation parameters based on the selected reference body.
SimManager.Gravity = CelestialBodies.Instance.SelectedBodyInfo.Gravity; SimManager.Gravity = CelestialBodies.Instance.SelectedBodyInfo.Gravity;
if (BuildAdvanced.Instance.ShowAtmosphericDetails) if (BuildAdvanced.Instance.ShowAtmosphericDetails)
{ {
SimManager.Atmosphere = CelestialBodies.Instance.SelectedBodyInfo.Atmosphere * 0.01d; SimManager.Atmosphere = CelestialBodies.Instance.SelectedBodyInfo.Atmosphere * 0.01d;
} }
else else
{ {
SimManager.Atmosphere = 0; SimManager.Atmosphere = 0;
} }
SimManager.TryStartSimulation(); SimManager.TryStartSimulation();
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Log("BuildOverlay->Update"); Logger.Exception(ex, "BuildOverlay->Update");
Logger.Exception(ex);  
} }
} }
private void OnDraw() private void OnDraw()
{ {
try try
{ {
if (!this.visible || EditorLogic.fetch == null || EditorLogic.fetch.ship.parts.Count == 0 || EditorLogic.fetch.editorScreen != EditorLogic.EditorScreen.Parts) if (!this.visible || EditorLogic.fetch == null || EditorLogic.fetch.ship.parts.Count == 0 || EditorLogic.fetch.editorScreen != EditorLogic.EditorScreen.Parts || SimManager.LastStage == null)
{ {
return; return;
} }
SimManager.RequestSimulation(); SimManager.RequestSimulation();
this.windowPosition = GUILayout.Window(this.windowId, this.windowPosition, this.Window, string.Empty, this.windowStyle); this.windowPosition = GUILayout.Window(this.windowId, this.windowPosition, this.Window, string.Empty, this.windowStyle);
// Check and set that the window is at the bottom of the screen. // Check and set that the window is at the bottom of the screen.
if (this.windowPosition.y + this.windowPosition.height != Screen.height - 5.0f) if (this.windowPosition.y + this.windowPosition.height != Screen.height - 5.0f)
{ {
this.windowPosition.y = Screen.height - this.windowPosition.height - 5.0f; this.windowPosition.y = Screen.height - this.windowPosition.height - 5.0f;
} }
// Find if a part is selected or being hovered over. // Find if a part is selected or being hovered over.
if (EditorLogic.SelectedPart != null) if (EditorLogic.SelectedPart != null)
{ {
// Do not allow the extended information to be shown. // Do not allow the extended information to be shown.
if (this.selectedPart != null) if (this.selectedPart != null)
{ {
this.selectedPart = null; this.selectedPart = null;
this.tooltipInfoTimer.Reset(); this.tooltipInfoTimer.Reset();
} }
this.DrawTooltip(EditorLogic.SelectedPart); this.DrawTooltip(EditorLogic.SelectedPart);
} }
else else
{ {
var isPartSelected = false; var isPartSelected = false;
foreach (var part in EditorLogic.SortedShipList) foreach (var part in EditorLogic.SortedShipList)
{ {
if (part.stackIcon.highlightIcon) if (part.stackIcon.highlightIcon)
{ {
// Start the extended information timer. // Start the extended information timer.
if (part != this.selectedPart) if (part != this.selectedPart)
{ {
this.selectedPart = part; this.selectedPart = part;
this.tooltipInfoTimer.Reset(); this.tooltipInfoTimer.Reset();
this.tooltipInfoTimer.Start(); this.tooltipInfoTimer.Start();
} }
isPartSelected = true; isPartSelected = true;
this.DrawTooltip(part); this.DrawTooltip(part);
break; break;
} }
} }
// If no part is being hovered over we must reset the extended information timer. // If no part is being hovered over we must reset the extended information timer.
if (!isPartSelected && this.selectedPart != null) if (!isPartSelected && this.selectedPart != null)
{ {
this.selectedPart = null; this.selectedPart = null;
this.tooltipInfoTimer.Reset(); this.tooltipInfoTimer.Reset();
} }
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Log("BuildOverlay->OnDraw"); Logger.Exception(ex, "BuildOverlay->OnDraw");
Logger.Exception(ex);  
} }
} }
private void Window(int windowId) private void Window(int windowId)
{ {
GUILayout.BeginHorizontal(); try
  {
// Titles GUILayout.BeginHorizontal();
GUILayout.BeginVertical(GUILayout.Width(75.0f));  
//GUILayout.Label("Parts:", this.titleStyle); // Titles
GUILayout.Label("Delta-V:", this.titleStyle); GUILayout.BeginVertical(GUILayout.Width(75.0f));
GUILayout.Label("TWR:", this.titleStyle); //GUILayout.Label("Parts:", this.titleStyle);
GUILayout.EndVertical(); GUILayout.Label("Delta-V:", this.titleStyle);
  GUILayout.Label("TWR:", this.titleStyle);
// Details GUILayout.EndVertical();
GUILayout.BeginVertical(GUILayout.Width(100.0f));  
//GUILayout.Label(SimulationManager.Instance.LastStage.partCount.ToString("N0"), this.infoStyle); // Details
GUILayout.Label(SimManager.LastStage.totalDeltaV.ToString("N0") + " m/s", this.infoStyle); GUILayout.BeginVertical(GUILayout.Width(100.0f));
GUILayout.Label(SimManager.LastStage.thrustToWeight.ToString("F2"), this.infoStyle); //GUILayout.Label(SimulationManager.Instance.LastStage.partCount.ToString("N0"), this.infoStyle);
GUILayout.EndVertical(); GUILayout.Label(SimManager.LastStage.totalDeltaV.ToString("N0") + " m/s", this.infoStyle);
  GUILayout.Label(SimManager.LastStage.thrustToWeight.ToString("F2"), this.infoStyle);
GUILayout.EndHorizontal(); GUILayout.EndVertical();
   
  GUILayout.EndHorizontal();
  }
  catch (Exception ex)
  {
  Logger.Exception(ex, "BuildOverlay->Window");
  }
} }
/// <summary> /// <summary>
/// Draws the tooltip details of the selected/highlighted part. /// Draws the tooltip details of the selected/highlighted part.
/// </summary> /// </summary>
private void DrawTooltip(Part part) private void DrawTooltip(Part part)
{ {
// Tooltip title (name of part). try
var content = new GUIContent(part.partInfo.title); {
var size = this.tooltipTitleStyle.CalcSize(content); // Tooltip title (name of part).
var position = new Rect(Event.current.mousePosition.x + 16.0f, Event.current.mousePosition.y, size.x, size.y).ClampInsideScreen(); var content = new GUIContent(part.partInfo.title);
  var size = this.tooltipTitleStyle.CalcSize(content);
if (position.x < Event.current.mousePosition.x + 16.0f) var position = new Rect(Event.current.mousePosition.x + 16.0f, Event.current.mousePosition.y, size.x, size.y).ClampInsideScreen();
{  
  if (position.x < Event.current.mousePosition.x + 16.0f)
  {
  position.y += 16.0f;
  }
  GUI.Label(position, content, this.tooltipTitleStyle);
   
  // After hovering for a period of time, show extended information.
  if (this.tooltipInfoTimer.Elapsed.TotalSeconds >= this.tooltipInfoDelay)
  {
  // Stop the timer as it is no longer needed.
  if (this.tooltipInfoTimer.IsRunning)
  {
  this.tooltipInfoTimer.Stop();
  }
   
  // Show the dry mass of the part if applicable.
  if (part.physicalSignificance == Part.PhysicalSignificance.FULL)
  {
  this.DrawTooltipInfo(ref position, "Dry Mass: " + part.GetDryMass().ToMass());
  }
   
  // Show resources contained within the part.
  if (part.ContainsResources())
  {
  // Show the wet mass of the part if applicable.
  if (part.GetResourceMass() > 0)
  {
  this.DrawTooltipInfo(ref position, "Wet Mass: " + part.GetWetMass().ToMass());
  }
   
  // List all the resources contained within the part.
  foreach (PartResource resource in part.Resources)
  {
  if (resource.GetDensity() > 0)
  {
  this.DrawTooltipInfo(ref position, resource.info.name + ": " + resource.GetMass().ToMass() + " (" + resource.amount + ")");
  }
  else
  {
  this.DrawTooltipInfo(ref position, resource.info.name + ": " + resource.amount);
  }
  }
  }
   
  // Show details for engines.
  if (part.IsEngine())
  {
  this.DrawTooltipInfo(ref position, "Maximum Thrust: " + part.GetMaxThrust().ToForce());
  this.DrawTooltipInfo(ref position, "Specific Impulse: " + part.GetSpecificImpulse(1f) + " / " + part.GetSpecificImpulse(0f) + "s");
   
  // Thrust vectoring.
  if (part.HasModule("ModuleGimbal"))
  {
  this.DrawTooltipInfo(ref position, "Thrust Vectoring Enabled");
  }
   
  // Contains alternator.
  if (part.HasModule("ModuleAlternator"))
  {
  this.DrawTooltipInfo(ref position, "Contains Alternator");
  }
  }
   
  // Show details for RCS.
  if (part.IsRcsModule())
  {
  var moduleRcs = part.GetModuleRcs();
  this.DrawTooltipInfo(ref position, "Thrust Power: " + moduleRcs.thrusterPower.ToDouble().ToForce());
  this.DrawTooltipInfo(ref position, "Specific Impulse: " + moduleRcs.atmosphereCurve.Evaluate(1f) + " / " + moduleRcs.atmosphereCurve.Evaluate(0f) + "s");
  }
   
  // Show details for solar panels.
  if (part.IsSolarPanel())
  {
  this.DrawTooltipInfo(ref position, "Charge Rate: " + part.GetModuleDeployableSolarPanel().chargeRate.ToDouble().ToRate());
  }
   
  // Show details for generators.
  if (part.IsGenerator())
  {
  foreach (var resource in part.GetModuleGenerator().inputList)
  {
  this.DrawTooltipInfo(ref position, "Input: " + resource.name + " (" + resource.rate.ToDouble().ToRate() + ")");
  }
   
  foreach (var resource in part.GetModuleGenerator().outputList)
  {
  this.DrawTooltipInfo(ref position, "Output: " + resource.name + " (" + resource.rate.ToDouble().ToRate() + ")");
  }
  }
   
  // Show details for parachutes.
  if (part.IsParachute())
  {
  var module = part.GetModuleParachute();
  this.DrawTooltipInfo(ref position, "Semi Deployed Drag: " + module.semiDeployedDrag);
  this.DrawTooltipInfo(ref position, "Fully Deployed Drag: " + module.fullyDeployedDrag);
  this.DrawTooltipInfo(ref position, "Deployment Altitude: " + module.deployAltitude.ToDouble().ToDistance());
  }
   
  // Contains stability augmentation system.
  if (part.HasModule("ModuleSAS"))
  {
  this.DrawTooltipInfo(ref position, "Contains SAS");
  }
   
  // Contains reaction wheels.
  if (part.HasModule("ModuleReactionWheel"))
  {
  this.DrawTooltipInfo(ref position, "Contains Reaction Wheels");
  }
   
  // Show if the part has an animation that can only be used once.
  if (part.HasOneShotAnimation())
  {
  this.DrawTooltipInfo(ref position, "Single Activation Only");
  }
  }
  }
  catch (Exception ex)
  {
  Logger.Exception(ex, "BuildOverlay->DrawTooltip");
  }
  }
   
  /// <summary>
  /// Draws a line of extended information below the previous.
  /// </summary>
  private void DrawTooltipInfo(ref Rect position, string value)
  {
  try
  {
  var content = new GUIContent(value);
  var size = this.tooltipInfoStyle.CalcSize(content);
   
position.y += 16.0f; position.y += 16.0f;
} position.width = size.x;
GUI.Label(position, content, this.tooltipTitleStyle); position.height = size.y;
  GUI.Label(position, content, this.tooltipInfoStyle);
// After hovering for a period of time, show extended information. }
if (this.tooltipInfoTimer.Elapsed.TotalSeconds >= this.tooltipInfoDelay) catch (Exception ex)
{ {
// Stop the timer as it is no longer needed. Logger.Exception(ex, "BuildOverlay->DrawTooltipInfo");
if (this.tooltipInfoTimer.IsRunning) }
{  
this.tooltipInfoTimer.Stop();  
}  
   
// Show the dry mass of the part if applicable.  
if (part.physicalSignificance == Part.PhysicalSignificance.FULL)  
{  
this.DrawTooltipInfo(ref position, "Dry Mass: " + part.GetDryMass().ToMass());  
}  
   
// Show resources contained within the part.  
if (part.ContainsResources())  
{  
// Show the wet mass of the part if applicable.  
if (part.GetResourceMass() > 0)  
{  
this.DrawTooltipInfo(ref position, "Wet Mass: " + part.GetWetMass().ToMass());  
}  
   
// List all the resources contained within the part.  
foreach (PartResource resource in part.Resources)  
{  
if (resource.GetDensity() > 0)  
{  
this.DrawTooltipInfo(ref position, resource.info.name + ": " + resource.GetMass().ToMass() + " (" + resource.amount + ")");  
}  
else  
{  
this.DrawTooltipInfo(ref position, resource.info.name + ": " + resource.amount);  
}  
}  
}  
   
// Show details for engines.  
if (part.IsEngine())  
{  
this.DrawTooltipInfo(ref position, "Maximum Thrust: " + part.GetMaxThrust().ToForce());  
this.DrawTooltipInfo(ref position, "Specific Impulse: " + part.GetSpecificImpulse(1f) + " / " + part.GetSpecificImpulse(0f) + "s");  
   
// Thrust vectoring.  
if (part.HasModule("ModuleGimbal"))  
{  
this.DrawTooltipInfo(ref position, "Thrust Vectoring Enabled");  
}  
   
// Contains alternator.  
if (part.HasModule("ModuleAlternator"))  
{  
this.DrawTooltipInfo(ref position, "Contains Alternator");  
}  
}  
   
// Show details for RCS.  
if (part.IsRcsModule())  
{  
var moduleRcs = part.GetModuleRcs();  
this.DrawTooltipInfo(ref position, "Thrust Power: " + moduleRcs.thrusterPower.ToDouble().ToForce());  
this.DrawTooltipInfo(ref position, "Specific Impulse: " + moduleRcs.atmosphereCurve.Evaluate(1f) + " / " + moduleRcs.atmosphereCurve.Evaluate(0f) + "s");  
}  
   
// Show details for solar panels.  
if (part.IsSolarPanel())  
{  
this.DrawTooltipInfo(ref position, "Charge Rate: " + part.GetModuleDeployableSolarPanel().chargeRate.ToDouble().ToRate());  
}  
   
// Show details for generators.  
if (part.IsGenerator())  
{  
foreach (var resource in part.GetModuleGenerator().inputList)  
{  
this.DrawTooltipInfo(ref position, "Input: " + resource.name + " (" + resource.rate.ToDouble().ToRate() + ")");  
}  
   
foreach (var resource in part.GetModuleGenerator().outputList)  
{  
this.DrawTooltipInfo(ref position, "Output: " + resource.name + " (" + resource.rate.ToDouble().ToRate() + ")");  
}  
}  
   
// Show details for parachutes.  
if (part.IsParachute())  
{  
var module = part.GetModuleParachute();  
this.DrawTooltipInfo(ref position, "Semi Deployed Drag: " + module.semiDeployedDrag);  
this.DrawTooltipInfo(ref position, "Fully Deployed Drag: " + module.fullyDeployedDrag);  
this.DrawTooltipInfo(ref position, "Deployment Altitude: " + module.deployAltitude.ToDouble().ToDistance());  
}  
   
// Contains stability augmentation system.  
if (part.HasModule("ModuleSAS"))  
{  
this.DrawTooltipInfo(ref position, "Contains SAS");  
}  
   
// Contains reaction wheels.  
if (part.HasModule("ModuleReactionWheel"))  
{  
this.DrawTooltipInfo(ref position, "Contains Reaction Wheels");  
}  
   
// Show if the part has an animation that can only be used once.  
if (part.HasOneShotAnimation())  
{  
this.DrawTooltipInfo(ref position, "Single Activation Only");  
}  
}  
}  
   
/// <summary>  
/// Draws a line of extended information below the previous.  
/// </summary>  
private void DrawTooltipInfo(ref Rect position, string value)  
{  
var content = new GUIContent(value);  
var size = this.tooltipInfoStyle.CalcSize(content);  
   
position.y += 16.0f;  
position.width = size.x;  
position.height = size.y;  
GUI.Label(position, content, this.tooltipInfoStyle);  
} }
#endregion #endregion
#region Save and Load #region Save and Load
/// <summary> /// <summary>
/// Saves the settings when this object is destroyed. /// Saves the settings when this object is destroyed.
/// </summary> /// </summary>
private void OnDestroy() private void OnDestroy()
{ {
try try
{ {
var handler = new SettingHandler(); var handler = new SettingHandler();
handler.Set("visible", this.visible); handler.Set("visible", this.visible);
handler.Save("BuildOverlay.xml"); handler.Save("BuildOverlay.xml");
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Log("BuildOverlay->OnDestroy"); Logger.Exception(ex, "BuildOverlay->OnDestroy");
Logger.Exception(ex);  
} }
} }
/// <summary> /// <summary>
/// Loads the settings when this object is created. /// Loads the settings when this object is created.
/// </summary> /// </summary>
private void Load() private void Load()
{ {
try try
{ {
var handler = SettingHandler.Load("BuildOverlay.xml"); var handler = SettingHandler.Load("BuildOverlay.xml");
handler.Get("visible", ref this.visible); handler.Get("visible", ref this.visible);
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Log("BuildOverlay->Load"); Logger.Exception(ex, "BuildOverlay->Load");
Logger.Exception(ex);  
} }
} }
#endregion #endregion
} }
} }
// //
// Kerbal Engineer Redux // Kerbal Engineer Redux
// //
// Copyright (C) 2014 CYBUTEK // Copyright (C) 2014 CYBUTEK
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
// //
  #region Using Directives
   
  using System;
   
  #endregion
   
namespace KerbalEngineer.Extensions namespace KerbalEngineer.Extensions
{ {
public static class DoubleExtensions public static class DoubleExtensions
{ {
/// <summary> /// <summary>
/// Convert to a single precision floating point number. /// Convert to a single precision floating point number.
/// </summary> /// </summary>
public static float ToFloat(this double value) public static float ToFloat(this double value)
{ {
return (float)value; try
  {
  return (float)value;
  }
  catch (Exception ex)
  {
  Logger.Exception(ex, "DoubleExtentions->ToFloat");
  return 0;
  }
} }
/// <summary> /// <summary>
/// Convert to a string formatted as a mass. /// Convert to a string formatted as a mass.
/// </summary> /// </summary>
public static string ToMass(this double value, bool showNotation = true) public static string ToMass(this double value, bool showNotation = true)
{ {
return ToFloat(value).ToMass(showNotation); try
  {
  value *= 1000;
  return showNotation ? value.ToString("N0") + "kg" : value.ToString("N0");
  }
  catch (Exception ex)
  {
  Logger.Exception(ex, "DoubleExtentions->ToMass");
  return "ERR";
  }
} }
/// <summary> /// <summary>
/// Convert to string formatted as a force. /// Convert to string formatted as a force.
/// </summary> /// </summary>
public static string ToForce(this double value, bool showNotation = true) public static string ToForce(this double value, bool showNotation = true)
{ {
return ToFloat(value).ToForce(showNotation); try
  {
  return showNotation ? value.ToString("N2") + "kN" : value.ToString("N2");
  }
  catch (Exception ex)
  {
  Logger.Exception(ex, "DoubleExtentions->ToForce");
  return "ERR";
  }
} }
/// <summary> /// <summary>
/// Convert to string formatted as a speed. /// Convert to string formatted as a speed.
/// </summary> /// </summary>
public static string ToSpeed(this double value, bool showNotation = true) public static string ToSpeed(this double value, bool showNotation = true)
{ {
return ToFloat(value).ToSpeed(showNotation); try
  {
  return showNotation ? value.ToString("N2") + "m/s" : value.ToString("N2");
  }
  catch (Exception ex)
  {
  Logger.Exception(ex, "DoubleExtentions->ToSpeed");
  return "ERR";
  }
} }
/// <summary> /// <summary>
/// Convert to string formatted as a distance. /// Convert to string formatted as a distance.
/// </summary> /// </summary>
public static string ToDistance(this double value) public static string ToDistance(this double value)
{ {
return ToFloat(value).ToDistance(); try
  {
  var negative = value < 0;
   
  if (negative)
  {
  value = -value;
  }
   
  if (value < 1000000.0f)
  {
  if (value < 1.0f)
  {
  value *= 1000.0f;
   
  if (negative)
  {
  value = -value;
  }
  return value.ToString("N0") + "mm";
  }
   
  if (negative)
  {
  value = -value;
  }
  return value.ToString("N0") + "m";
  }
   
  value /= 1000.0f;
  if (value >= 1000000.0f)
  {
  value /= 1000.0f;
   
  if (negative)
  {
  value = -value;
  }
  return value.ToString("N0") + "Mm";
  }
   
  if (negative)
  {
  value = -value;
  }
  return value.ToString("N0") + "km";
  }
  catch (Exception ex)
  {
  Logger.Exception(ex, "DoubleExtentions->ToDistance");
  return "ERR";
  }
} }
/// <summary> /// <summary>
/// Convert to string formatted as a rate. /// Convert to string formatted as a rate.
/// </summary> /// </summary>
public static string ToRate(this double value) public static string ToRate(this double value)
{ {
return ToFloat(value).ToRate(); try
  {
  return value > 0 ? value.ToString("F1") + "/sec" : (60.0f * value).ToString("F1") + "/min";
  }
  catch (Exception ex)
  {
  Logger.Exception(ex, "DoubleExtentions->ToRate");
  return "ERR";
  }
} }
/// <summary> /// <summary>
/// Convert to string formatted as an angle. /// Convert to string formatted as an angle.
/// </summary> /// </summary>
public static string ToAngle(this double value) public static string ToAngle(this double value)
{ {
return ToFloat(value).ToAngle(); try
  {
  return value.ToString("F3") + "°";
  }
  catch (Exception ex)
  {
  Logger.Exception(ex, "DoubleExtentions->ToAngle");
  return "ERR";
  }
} }
/// <summary> /// <summary>
/// Convert to string formatted as a time. /// Convert to string formatted as a time.
/// </summary> /// </summary>
public static string ToTime(this double value) public static string ToTime(this double value, string format = "F1")
{ {
return ToFloat(value).ToTime(); try
  {
  var s = value;
  var m = 0;
  var h = 0;
  var d = 0;
  var y = 0;
   
  // Years
  while (s >= 31536000)
  {
  y++;
  s -= 31536000;
  }
   
  // Days
  while (s >= 86400)
  {
  d++;
  s -= 86400;
  }
   
  // Hours
  while (s >= 3600)
  {
  h++;
  s -= 3600;
  }
   
  // Minutes
  while (s >= 60)
  {
  m++;
  s -= 60;
  }
   
  if (y > 0)
  {
  return y + "y " + d + "d " + h + "h " + m + "m " + s.ToString(format) + "s";
  }
   
  if (d > 0)
  {
  return d + "d " + h + "h " + m + "m " + s.ToString(format) + "s";
  }
   
  if (h > 0)
  {
  return h + "h " + m + "m " + s.ToString(format) + "s";
  }
   
  if (m > 0)
  {
  return m + "m " + s.ToString(format) + "s";
  }
   
  return s.ToString(format) + "s";
  }
  catch (Exception ex)
  {
  Logger.Exception(ex, "DoubleExtentions->ToTime");
  return "ERR";
  }
} }
} }
} }
// //
// Kerbal Engineer Redux // Kerbal Engineer Redux
// //
// Copyright (C) 2014 CYBUTEK // Copyright (C) 2014 CYBUTEK
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
// //
  using System;
   
namespace KerbalEngineer.Extensions namespace KerbalEngineer.Extensions
{ {
public static class FloatExtensions public static class FloatExtensions
{ {
/// <summary> /// <summary>
/// Convert to a double precision floating point number. /// Convert to a double precision floating point number.
/// </summary> /// </summary>
public static double ToDouble(this float value) public static double ToDouble(this float value)
{ {
return value; try
  {
  return value;
  }
  catch (Exception ex)
  {
  Logger.Exception(ex, "FloatExtentions->ToDouble");
  return 0;
  }
} }
/// <summary> /// <summary>
/// Convert to a string formatted as a mass. /// Convert to a string formatted as a mass.
/// </summary> /// </summary>
public static string ToMass(this float value, bool showNotation = true) public static string ToMass(this float value, bool showNotation = true)
{ {
value *= 1000; try
  {
return showNotation ? value.ToString("N0") + "kg" : value.ToString("N0"); value *= 1000;
  return showNotation ? value.ToString("N0") + "kg" : value.ToString("N0");
  }
  catch (Exception ex)
  {
  Logger.Exception(ex, "FloatExtentions->ToMass");
  return "ERR";
  }
} }
/// <summary> /// <summary>
/// Convert to string formatted as a force. /// Convert to string formatted as a force.
/// </summary> /// </summary>
public static string ToForce(this float value, bool showNotation = true) public static string ToForce(this float value, bool showNotation = true)
{ {
return showNotation ? value.ToString("N2") + "kN" : value.ToString("N2"); try
  {
  return showNotation ? value.ToString("N2") + "kN" : value.ToString("N2");
  }
  catch (Exception ex)
  {
  Logger.Exception(ex, "FloatExtentions->ToForce");
  return "ERR";
  }
} }
/// <summary> /// <summary>
/// Convert to string formatted as a speed. /// Convert to string formatted as a speed.
/// </summary> /// </summary>
public static string ToSpeed(this float value, bool showNotation = true) public static string ToSpeed(this float value, bool showNotation = true)
{ {
return showNotation ? value.ToString("N2") + "m/s" : value.ToString("N2"); try
  {
  return showNotation ? value.ToString("N2") + "m/s" : value.ToString("N2");
  }
  catch (Exception ex)
  {
  Logger.Exception(ex, "FloatExtentions->ToSpeed");
  return "ERR";
  }
} }
/// <summary> /// <summary>
/// Convert to string formatted as a distance. /// Convert to string formatted as a distance.
/// </summary> /// </summary>
public static string ToDistance(this float value) public static string ToDistance(this float value)
{ {
var negative = value < 0; try
  {
if (negative) var negative = value < 0;
{  
value = -value; if (negative)
} {
  value = -value;
if (value < 1000000.0f) }
{  
if (value < 1.0f) if (value < 1000000.0f)
{ {
value *= 1000.0f; if (value < 1.0f)
  {
  value *= 1000.0f;
   
  if (negative)
  {
  value = -value;
  }
  return value.ToString("N0") + "mm";
  }
if (negative) if (negative)
{ {
value = -value; value = -value;
} }
return value.ToString("N0") + "mm"; return value.ToString("N0") + "m";
  }
   
  value /= 1000.0f;
  if (value >= 1000000.0f)
  {
  value /= 1000.0f;
   
  if (negative)
  {
  value = -value;
  }
  return value.ToString("N0") + "Mm";
} }
if (negative) if (negative)
{ {
value = -value; value = -value;
} }
return value.ToString("N0") + "m"; return value.ToString("N0") + "km";
} }
  catch (Exception ex)
value /= 1000.0f; {
if (value >= 1000000.0f) Logger.Exception(ex, "FloatExtentions->ToDistance");
{ return "ERR";
value /= 1000.0f; }
   
if (negative)  
{  
value = -value;  
}  
return value.ToString("N0") + "Mm";  
}  
   
if (negative)  
{  
value = -value;  
}  
return value.ToString("N0") + "km";  
} }
/// <summary> /// <summary>
/// Convert to string formatted as a rate. /// Convert to string formatted as a rate.
/// </summary> /// </summary>
public static string ToRate(this float value) public static string ToRate(this float value)
{ {
return value > 0 ? value.ToString("F1") + "/sec" : (60.0f * value).ToString("F1") + "/min"; try
  {
  return value > 0 ? value.ToString("F1") + "/sec" : (60.0f * value).ToString("F1") + "/min";
  }
  catch (Exception ex)
  {
  Logger.Exception(ex, "FloatExtentions->ToRate");
  return "ERR";
  }
} }
/// <summary> /// <summary>
/// Convert to string formatted as an angle. /// Convert to string formatted as an angle.
/// </summary> /// </summary>
public static string ToAngle(this float value) public static string ToAngle(this float value)
{ {
return value.ToString("F3") + "°"; try
  {
  return value.ToString("F3") + "°";
  }
  catch (Exception ex)
  {
  Logger.Exception(ex, "FloatExtentions->ToAngle");
  return "ERR";
  }
} }
/// <summary> /// <summary>
/// Convert to string formatted as a time. /// Convert to string formatted as a time.
/// </summary> /// </summary>
public static string ToTime(this float value) public static string ToTime(this float value, string format = "F1")
{ {
var s = value; try
var m = 0; {
var h = 0; var s = value;
var d = 0; var m = 0;
var y = 0; var h = 0;
  var d = 0;
// Years var y = 0;
while (s >= 31536000)  
{ // Years
y++; while (s >= 31536000)
s -= 31536000; {
} y++;
  s -= 31536000;
// Days }
while (s >= 86400)  
{ // Days
d++; while (s >= 86400)
s -= 86400; {
} d++;
  s -= 86400;
// Hours }
while (s >= 3600)  
{ // Hours
h++; while (s >= 3600)
s -= 3600; {
} h++;
  s -= 3600;
// Minutes }
while (s >= 60)  
{ // Minutes
m++; while (s >= 60)
s -= 60; {
} m++;
  s -= 60;
if (y > 0) }
{  
return y + "y " + d + "d " + h + "h " + m + "m " + s.ToString("F1") + "s"; if (y > 0)
} {
  return y + "y " + d + "d " + h + "h " + m + "m " + s.ToString(format) + "s";
if (d > 0) }
{  
return d + "d " + h + "h " + m + "m " + s.ToString("F1") + "s"; if (d > 0)
} {
  return d + "d " + h + "h " + m + "m " + s.ToString(format) + "s";
if (h > 0) }
{  
return h + "h " + m + "m " + s.ToString("F1") + "s"; if (h > 0)
} {
  return h + "h " + m + "m " + s.ToString(format) + "s";
if (m > 0) }
{  
return m + "m " + s.ToString("F1") + "s"; if (m > 0)
} {
  return m + "m " + s.ToString(format) + "s";
return s.ToString("F1") + "s"; }
   
  return s.ToString(format) + "s";
  }
  catch (Exception ex)
  {
  Logger.Exception(ex, "FloatExtensions->ToTime");
  return "ERR";
  }
} }
} }
} }
// //
// Kerbal Engineer Redux // Kerbal Engineer Redux
// //
// Copyright (C) 2014 CYBUTEK // Copyright (C) 2014 CYBUTEK
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
// //
#region Using Directives #region Using Directives
using KerbalEngineer.Extensions; using KerbalEngineer.Extensions;
#endregion #endregion
namespace KerbalEngineer.Flight.Readouts.Orbital namespace KerbalEngineer.Flight.Readouts.Orbital
{ {
public class OrbitalPeriod : ReadoutModule public class OrbitalPeriod : ReadoutModule
{ {
public OrbitalPeriod() public OrbitalPeriod()
{ {
this.Name = "Orbital Period"; this.Name = "Orbital Period";
this.Category = ReadoutCategory.Orbital; this.Category = ReadoutCategory.Orbital;
this.HelpString = "Shows the amount of time it will take to complete a full orbit."; this.HelpString = "Shows the amount of time it will take to complete a full orbit.";
this.IsDefault = true; this.IsDefault = true;
} }
public override void Draw() public override void Draw()
{ {
this.DrawLine(FlightGlobals.ship_orbit.period.ToTime()); this.DrawLine(FlightGlobals.ship_orbit.period.ToTime("F3"));
} }
} }
} }
// //
// Kerbal Engineer Redux // Kerbal Engineer Redux
// //
// Copyright (C) 2014 CYBUTEK // Copyright (C) 2014 CYBUTEK
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
// //
#region Using Directives #region Using Directives
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Reflection; using System.Reflection;
using UnityEngine; using UnityEngine;
#endregion #endregion
namespace KerbalEngineer namespace KerbalEngineer
{ {
[KSPAddon(KSPAddon.Startup.Instantly, false)] [KSPAddon(KSPAddon.Startup.Instantly, false)]
public class Logger : MonoBehaviour public class Logger : MonoBehaviour
{ {
#region Constants #region Constants
private const string FileName = "KerbalEngineer.log"; private const string FileName = "KerbalEngineer.log";
#endregion #endregion
#region Fields #region Fields
private static readonly List<string> messages = new List<string>(); private static readonly List<string> messages = new List<string>();
#endregion #endregion
#region Initialisation #region Initialisation
private void Awake() private void Awake()
{ {
DontDestroyOnLoad(this); DontDestroyOnLoad(this);
File.Delete(FileName); File.Delete(FileName);
using (var file = File.AppendText(FileName)) using (var file = File.AppendText(FileName))
{ {
messages.Add("Version: " + Assembly.GetExecutingAssembly().GetName().Version); messages.Add("Version: " + Assembly.GetExecutingAssembly().GetName().Version);
Blank(); Blank();
} }
} }
#endregion #endregion
#region Printing #region Printing
public static void Blank() public static void Blank()
{ {
lock (messages) lock (messages)
{ {
messages.Add(string.Empty); messages.Add(string.Empty);
} }
} }
public static void Log(object obj) public static void Log(object obj)
{ {
lock (messages) lock (messages)
{ {
messages.Add("[Log " + DateTime.Now.TimeOfDay + "]: " + obj); messages.Add("[Log " + DateTime.Now.TimeOfDay + "]: " + obj);
} }
} }
public static void Log(string message) public static void Log(string message)
{ {
lock (messages) lock (messages)
{ {
messages.Add("[Log " + DateTime.Now.TimeOfDay + "]: " + message); messages.Add("[Log " + DateTime.Now.TimeOfDay + "]: " + message);
} }
} }
public static void Warning(string message) public static void Warning(string message)
{ {
lock (messages) lock (messages)
{ {
messages.Add("[Warning " + DateTime.Now.TimeOfDay + "]: " + message); messages.Add("[Warning " + DateTime.Now.TimeOfDay + "]: " + message);
} }
} }
public static void Error(string message) public static void Error(string message)
{ {
lock (messages) lock (messages)
{ {
messages.Add("[Error " + DateTime.Now.TimeOfDay + "]: " + message); messages.Add("[Error " + DateTime.Now.TimeOfDay + "]: " + message);
} }
} }
public static void Exception(Exception ex) public static void Exception(Exception ex)
{ {
lock (messages) lock (messages)
{ {
messages.Add("[Exception " + DateTime.Now.TimeOfDay + "]: " + ex.Message); messages.Add("[Exception " + DateTime.Now.TimeOfDay + "]: " + ex.Message);
messages.Add(ex.StackTrace); messages.Add(ex.StackTrace);
messages.Add(string.Empty); messages.Add(string.Empty);
} }
} }
  public static void Exception(Exception ex, string location)
  {
  lock (messages)
  {
  messages.Add("[Exception " + DateTime.Now.TimeOfDay + "]: " + location + " // " + ex.Message);
  messages.Add(ex.StackTrace);
  messages.Add(string.Empty);
  }
  }
   
#endregion #endregion
#region Flushing #region Flushing
public static void Flush() public static void Flush()
{ {
lock (messages) lock (messages)
{ {
if (messages.Count > 0) if (messages.Count > 0)
{ {
using (var file = File.AppendText(FileName)) using (var file = File.AppendText(FileName))
{ {
foreach (var message in messages) foreach (var message in messages)
{ {
file.WriteLine(message); file.WriteLine(message);
} }
} }
messages.Clear(); messages.Clear();
} }
} }
} }
private void LateUpdate() private void LateUpdate()
{ {
Flush(); Flush();
} }
#endregion #endregion
} }
} }
 Binary files a/Output/KerbalEngineer/KerbalEngineer.dll and b/Output/KerbalEngineer/KerbalEngineer.dll differ
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta charset='UTF-8'> <meta charset='UTF-8'>
<title>Kerbal Engineer Redux - ReadMe</title> <title>Kerbal Engineer Redux - ReadMe</title>
<link href='http://fonts.googleapis.com/css?family=PT+Sans:400,700' rel='stylesheet' type='text/css'> <link href='http://fonts.googleapis.com/css?family=PT+Sans:400,700' rel='stylesheet' type='text/css'>
<style> <style>
* { * {
margin: 0; padding: 0; margin: 0; padding: 0;
box-sizing: border-box; moz-box-sizing: border-box; webkit-box-sizing: border-box; box-sizing: border-box; moz-box-sizing: border-box; webkit-box-sizing: border-box;
} }
html, body { html, body {
height: 100%; height: 100%;
font-family: 'PT Sans', Verdana, Geneva, sans-serif; font-family: 'PT Sans', Verdana, Geneva, sans-serif;
} }
a { a {
text-decoration: none; text-decoration: none;
font-weight: 700; font-weight: 700;
color: inherit; color: inherit;
} }
a:hover { a:hover {
text-decoration: underline; text-decoration: underline;
} }
h3 { h3 {
display: block; display: block;
line-height: 1.5em; line-height: 1.5em;
font-size: 1.1em; font-size: 1.1em;
font-weight: 700; font-weight: 700;
} }
p { p {
display: block; display: block;
margin: 0 0.5em; margin: 0 0.5em;
text-align: justify; text-align: justify;
} }
ul { ul {
display: block; display: block;
margin-left: 1.5em; margin-left: 1.5em;
list-style-type: square; list-style-type: square;
} }
li { li {
margin-top: 0.5em; margin-top: 0.5em;
text-align: justify; text-align: justify;
} }
header { header {
display: block; display: block;
padding: 25px; padding: 25px;
text-align: center; text-align: center;
font-size: 3rem; font-size: 3rem;
font-weight: 700; font-weight: 700;
letter-spacing: 0.2em; letter-spacing: 0.2em;
color: white; color: white;
background-color: black; background-color: black;
} }
section { section {
display: block; display: block;
margin: 10px; margin: 10px;
padding: 0.5em 1em; padding: 0.5em 1em;
line-height: 1.5em; line-height: 1.5em;
background-color: #DDD; background-color: #DDD;
} }
footer { footer {
display: block; display: block;
text-align: center; text-align: center;
line-height: 50px; line-height: 50px;
font-size: 0.8rem; font-size: 0.8rem;
letter-spacing: 0.2em; letter-spacing: 0.2em;
color: white; color: white;
background-color: black; background-color: black;
} }
#changelog > li { #changelog > li {
margin-top: 1em; margin-top: 1em;
} }
#changelog > li:first-child { #changelog > li:first-child {
margin-top: 0.5em; margin-top: 0.5em;
} }
#root { #root {
clear: both; clear: both;
min-height: 100%; min-height: 100%;
height: auto !important; height: auto !important;
height: 100%; height: 100%;
margin-bottom: -50px; margin-bottom: -50px;
} }
#root > #root_footer { #root > #root_footer {
height: 50px; height: 50px;
} }
#footer { #footer {
clear: both; clear: both;
position: relative; position: relative;
height: 50px; height: 50px;
} }
</style> </style>
</head> </head>
<body> <body>
<div id='root'> <div id='root'>
<header>Kerbal Engineer Redux v1.0</header> <header>Kerbal Engineer Redux v1.0</header>
<section> <section>
<h3>Current Version</h3> <h3>Current Version</h3>
<p>1.0.0.0</p> <p>1.0.0.0</p>
</section> </section>
<section> <section>
<h3>Developers</h3> <h3>Developers</h3>
<p> <p>
CYBUTEK: Developer<br> CYBUTEK: Developer<br>
Padishar: Simulation Logic<br> Padishar: Simulation Logic<br>
Mic_e: Impact Readouts Mic_e: Impact Readouts
</p> </p>
</section> </section>
<section> <section>
<h3>KSP-AVC Ready</h3> <h3>KSP-AVC Ready</h3>
<p> <p>
KSP Add-on Version Checker is a standardised system for versioning mods. You can get more information on the KSP Add-on Version Checker is a standardised system for versioning mods. You can get more information on the
<a href='http://forum.kerbalspaceprogram.com/threads/79745' target='_blank'>forum thread</a>. <a href='http://forum.kerbalspaceprogram.com/threads/79745' target='_blank'>forum thread</a>.
</p> </p>
</section> </section>
<section> <section>
<h3>Description</h3> <h3>Description</h3>
<p> <p>
This plugin will allow you to view important statistics about your ship on the fly, whilst building it and in flight. This plugin will allow you to view important statistics about your ship on the fly, whilst building it and in flight.
</p> </p>
</section> </section>
<section> <section>
<h3>Installation</h3> <h3>Installation</h3>
<ul> <ul>
<li>Copy the 'KerbalEngineer' folder into the 'GameData' folder located within your Kerbal Space Program installation directory.</li> <li>Copy the 'KerbalEngineer' folder into the 'GameData' folder located within your Kerbal Space Program installation directory.</li>
<li> <li>
Attach an Engineer part from the science tab to your ship. Attach an Engineer part from the science tab to your ship.
<ul> <ul>
<li>ER7500 includes flight engineer.</li> <li>ER7500 includes flight engineer.</li>
</ul> </ul>
</li> </li>
</ul> </ul>
</section> </section>
<section> <section>
<h3>Change Log</h3> <h3>Change Log</h3>
<ul id='changelog'> <ul id='changelog'>
<li> <li>
1.0.0.0 1.0.0.0
<ul> <ul>
<li>Initial release.</li> <li>Initial release for public testing.</li>
</ul> </ul>
</li> </li>
</ul> </ul>
</section> </section>
<section> <section>
<h3>Software License</h3> <h3>Software License</h3>
<p> <p>
Copyright &copy; 2014 CYBUTEK Copyright &copy; 2014 CYBUTEK
<br><br> <br><br>
This program is free software: you can redistribute it and/or modify<br> This program is free software: you can redistribute it and/or modify<br>
it under the terms of the GNU General Public License as published by<br> it under the terms of the GNU General Public License as published by<br>
the Free Software Foundation, either version 3 of the License, or<br> the Free Software Foundation, either version 3 of the License, or<br>
(at your option) any later version. (at your option) any later version.
<br><br> <br><br>
This program is distributed in the hope that it will be useful,<br> This program is distributed in the hope that it will be useful,<br>
but WITHOUT ANY WARRANTY; without even the implied warranty of<br> but WITHOUT ANY WARRANTY; without even the implied warranty of<br>
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br> MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br>
GNU General Public License for more details. GNU General Public License for more details.
<br><br> <br><br>
Licensed under <a href='http://www.gnu.org/licenses/gpl-3.0.html' target='_blank'>GNU General Public License v3</a> Licensed under <a href='http://www.gnu.org/licenses/gpl-3.0.html' target='_blank'>GNU General Public License v3</a>
</p> </p>
</section> </section>
<div id='root_footer'></div> <div id='root_footer'></div>
</div> </div>
<footer id='footer'> <footer id='footer'>
ReadMe Design by <a href='http://ksp.cybutek.net' target='_blank'><strong>CYBUTEK</strong></a> (GPLv3) ReadMe Design by <a href='http://ksp.cybutek.net' target='_blank'><strong>CYBUTEK</strong></a> (GPLv3)
</footer> </footer>
</body> </body>
</html> </html>