Merge pull request #29 from Gerry1135/kspexp
Merge pull request #29 from Gerry1135/kspexp

Merged various fixes by 'Padishar'.

// //
// 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/>.
// //
   
namespace KerbalEngineer.Editor namespace KerbalEngineer.Editor
{ {
#region Using Directives #region Using Directives
using System; using System;
using Extensions; using Extensions;
using Flight; using Flight;
using Helpers; using Helpers;
using Settings; using Settings;
using UIControls; using UIControls;
using UnityEngine; using UnityEngine;
using VesselSimulator; using VesselSimulator;
   
#endregion #endregion
   
[KSPAddon(KSPAddon.Startup.EditorAny, false)] [KSPAddon(KSPAddon.Startup.EditorAny, false)]
public class BuildAdvanced : MonoBehaviour public class BuildAdvanced : MonoBehaviour
{ {
#region Fields #region Fields
public static float Altitude = 0.0f; public static float Altitude = 0.0f;
   
private GUIStyle areaSettingStyle; private GUIStyle areaSettingStyle;
private GUIStyle areaStyle; private GUIStyle areaStyle;
private float atmosphericMach; private float atmosphericMach;
private GUIStyle bodiesButtonActiveStyle; private GUIStyle bodiesButtonActiveStyle;
private GUIStyle bodiesButtonStyle; private GUIStyle bodiesButtonStyle;
private DropDown bodiesList; private DropDown bodiesList;
private Rect bodiesListPosition; private Rect bodiesListPosition;
private GUIStyle buttonStyle; private GUIStyle buttonStyle;
private int compactCheck; private int compactCheck;
private bool compactCollapseRight; private bool compactCollapseRight;
private bool compactMode; private bool compactMode;
private float compactRight; private float compactRight;
private bool hasChanged; private bool hasChanged;
private GUIStyle infoStyle; private GUIStyle infoStyle;
private bool isEditorLocked; private bool isEditorLocked;
private float maxMach; private float maxMach;
private int numberOfStages; private int numberOfStages;
private Rect position = new Rect(265.0f, 45.0f, 0, 0); private Rect position = new Rect(265.0f, 45.0f, 0, 0);
private GUIStyle settingAtmoStyle; private GUIStyle settingAtmoStyle;
private GUIStyle settingStyle; private GUIStyle settingStyle;
private bool showAllStages; private bool showAllStages;
private bool showAtmosphericDetails; private bool showAtmosphericDetails;
private bool showSettings; private bool showSettings;
private Stage[] stages; private Stage[] stages;
private GUIStyle titleStyle; private GUIStyle titleStyle;
private bool visible = true; private bool visible = true;
private GUIStyle windowStyle; private GUIStyle windowStyle;
#endregion #endregion
   
#region Properties #region Properties
/// <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; }
   
/// <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 get
{ {
return compactMode; return compactMode;
} }
set set
{ {
compactMode = value; 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 get
{ {
return showAllStages; return showAllStages;
} }
set set
{ {
showAllStages = value; 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 get
{ {
return showAtmosphericDetails; return showAtmosphericDetails;
} }
set set
{ {
showAtmosphericDetails = value; showAtmosphericDetails = value;
} }
} }
   
/// <summary> /// <summary>
/// Gets and sets whether to show the settings display. /// Gets and sets whether to show the settings display.
/// </summary> /// </summary>
public bool ShowSettings public bool ShowSettings
{ {
get get
{ {
return showSettings; return showSettings;
} }
set set
{ {
showSettings = value; showSettings = 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 get
{ {
return visible; return visible;
} }
set set
{ {
visible = value; visible = value;
} }
} }
#endregion #endregion
   
#region Methods #region Methods
private static Rect compactModeRect = new Rect(0.0f, 5.0f, 0.0f, 20.0f); private static Rect compactModeRect = new Rect(0.0f, 5.0f, 0.0f, 20.0f);
private static Stage stage; private static Stage stage;
private static int stagesCount; private static int stagesCount;
private static int stagesLength; private static int stagesLength;
private static string title; private static string title;
   
protected void Awake() protected void Awake()
{ {
try try
{ {
Instance = this; Instance = this;
bodiesList = gameObject.AddComponent<DropDown>(); bodiesList = gameObject.AddComponent<DropDown>();
bodiesList.DrawCallback = DrawBodiesList; bodiesList.DrawCallback = DrawBodiesList;
Load(); Load();
   
SimManager.UpdateModSettings(); SimManager.UpdateModSettings();
SimManager.OnReady -= GetStageInfo; SimManager.OnReady -= GetStageInfo;
SimManager.OnReady += GetStageInfo; SimManager.OnReady += GetStageInfo;
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Exception(ex, "BuildAdvanced.Awake()"); Logger.Exception(ex, "BuildAdvanced.Awake()");
} }
} }
   
/// <summary> /// <summary>
/// Saves the settings when this object is destroyed. /// Saves the settings when this object is destroyed.
/// </summary> /// </summary>
protected void OnDestroy() protected void OnDestroy()
{ {
try try
{ {
SettingHandler handler = new SettingHandler(); SettingHandler handler = new SettingHandler();
handler.Set("visible", visible); handler.Set("visible", visible);
handler.Set("windowPositionX", position.x); handler.Set("windowPositionX", position.x);
handler.Set("windowPositionY", position.y); handler.Set("windowPositionY", position.y);
handler.Set("compactMode", compactMode); handler.Set("compactMode", compactMode);
handler.Set("compactCollapseRight", compactCollapseRight); handler.Set("compactCollapseRight", compactCollapseRight);
handler.Set("showAllStages", showAllStages); handler.Set("showAllStages", showAllStages);
handler.Set("showAtmosphericDetails", showAtmosphericDetails); handler.Set("showAtmosphericDetails", showAtmosphericDetails);
handler.Set("showSettings", showSettings); handler.Set("showSettings", showSettings);
handler.Set("selectedBodyName", CelestialBodies.SelectedBody.Name); handler.Set("selectedBodyName", CelestialBodies.SelectedBody.Name);
handler.Save("BuildAdvanced.xml"); handler.Save("BuildAdvanced.xml");
GuiDisplaySize.OnSizeChanged -= OnSizeChanged; GuiDisplaySize.OnSizeChanged -= OnSizeChanged;
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Exception(ex, "BuildAdvanced.OnDestroy()"); Logger.Exception(ex, "BuildAdvanced.OnDestroy()");
} }
} }
   
protected void OnGUI() protected void OnGUI()
{ {
try try
{ {
if (!visible || EditorLogic.fetch == null || EditorLogic.fetch.ship.parts.Count == 0 || EditorLogic.fetch.editorScreen != EditorScreen.Parts) if (!visible || EditorLogic.fetch == null || EditorLogic.fetch.ship.parts.Count == 0 || EditorLogic.fetch.editorScreen != EditorScreen.Parts)
{ {
return; return;
} }
   
if (stages == null) if (stages == null)
{ {
return; return;
} }
   
// Change the window title based on whether in compact mode or not. // Change the window title based on whether in compact mode or not.
title = !compactMode ? "KERBAL ENGINEER REDUX " + EngineerGlobals.AssemblyVersion : "K.E.R. " + EngineerGlobals.AssemblyVersion + (showAtmosphericDetails ? " (ATMOS.)" : String.Empty); title = !compactMode ? "KERBAL ENGINEER REDUX " + EngineerGlobals.AssemblyVersion : "K.E.R. " + EngineerGlobals.AssemblyVersion;
   
// Reset the window size when the staging or something else has changed. // Reset the window size when the staging or something else has changed.
stagesLength = stages.Length; stagesLength = stages.Length;
if (showAllStages) if (showAllStages)
{ {
stagesCount = stagesLength; stagesCount = stagesLength;
} }
if (showAllStages == false) if (showAllStages == false)
{ {
stagesCount = 0; stagesCount = 0;
for (int i = 0; i < stagesLength; ++i) for (int i = 0; i < stagesLength; ++i)
{ {
if (stages[i].deltaV > 0.0f) if (stages[i].deltaV > 0.0f)
{ {
stagesCount = stagesCount + 1; stagesCount = stagesCount + 1;
} }
} }
} }
   
if (hasChanged || stagesCount != numberOfStages) if (hasChanged || stagesCount != numberOfStages)
{ {
hasChanged = false; hasChanged = false;
numberOfStages = stagesCount; numberOfStages = stagesCount;
   
position.width = 0; position.width = 0;
position.height = 0; position.height = 0;
} }
   
GUI.skin = null; GUI.skin = null;
position = GUILayout.Window(GetInstanceID(), position, Window, title, windowStyle).ClampToScreen(); position = GUILayout.Window(GetInstanceID(), position, Window, title, windowStyle).ClampToScreen();
   
if (compactCheck > 0 && compactCollapseRight) if (compactCheck > 0 && compactCollapseRight)
{ {
position.x = compactRight - position.width; position.x = compactRight - position.width;
compactCheck--; compactCheck--;
} }
else if (compactCheck > 0) else if (compactCheck > 0)
{ {
compactCheck = 0; compactCheck = 0;
} }
   
// Check editor lock to manage click-through. // Check editor lock to manage click-through.
CheckEditorLock(); CheckEditorLock();
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Exception(ex, "BuildAdvanced.OnGUI()"); Logger.Exception(ex, "BuildAdvanced.OnGUI()");
} }
} }
   
protected void Start() protected void Start()
{ {
try try
{ {
InitialiseStyles(); InitialiseStyles();
GuiDisplaySize.OnSizeChanged += OnSizeChanged; GuiDisplaySize.OnSizeChanged += OnSizeChanged;
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Exception(ex, "BuildAdvanced.Start()"); Logger.Exception(ex, "BuildAdvanced.Start()");
} }
} }
   
protected void Update() protected void Update()
{ {
try try
{ {
if (Input.GetKeyDown(KeyBinder.EditorShowHide)) if (Input.GetKeyDown(KeyBinder.EditorShowHide))
{ {
visible = !visible; visible = !visible;
if (!visible) if (!visible)
{ {
EditorLock(false); EditorLock(false);
} }
} }
   
if (!visible || EditorLogic.fetch == null || EditorLogic.fetch.ship.parts.Count == 0) if (!visible || EditorLogic.fetch == null || EditorLogic.fetch.ship.parts.Count == 0)
{ {
bodiesList.enabled = false; bodiesList.enabled = false;
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.SelectedBody.Gravity; SimManager.Gravity = CelestialBodies.SelectedBody.Gravity;
   
if (showAtmosphericDetails) if (showAtmosphericDetails)
{ {
SimManager.Atmosphere = CelestialBodies.SelectedBody.GetAtmospheres(Altitude); SimManager.Atmosphere = CelestialBodies.SelectedBody.GetAtmospheres(Altitude);
} }
else else
{ {
SimManager.Atmosphere = 0; SimManager.Atmosphere = 0;
} }
   
SimManager.Mach = atmosphericMach; SimManager.Mach = atmosphericMach;
   
SimManager.RequestSimulation(); SimManager.RequestSimulation();
SimManager.TryStartSimulation(); SimManager.TryStartSimulation();
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Exception(ex, "BuildAdvanced.Update()"); Logger.Exception(ex, "BuildAdvanced.Update()");
} }
} }
   
/// <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 ((position.MouseIsOver() || bodiesList.Position.MouseIsOver()) && !isEditorLocked) if ((position.MouseIsOver() || bodiesList.Position.MouseIsOver()) && !isEditorLocked)
{ {
EditorLock(true); EditorLock(true);
} }
else if (!position.MouseIsOver() && !bodiesList.Position.MouseIsOver() && isEditorLocked) else if (!position.MouseIsOver() && !bodiesList.Position.MouseIsOver() && isEditorLocked)
{ {
EditorLock(false); EditorLock(false);
} }
} }
   
/// <summary> /// <summary>
/// Draws the atmospheric settings. /// Draws the atmospheric settings.
/// </summary> /// </summary>
private void DrawAtmosphericDetails() private void DrawAtmosphericDetails()
{ {
try try
{ {
GUILayout.BeginHorizontal(); GUILayout.BeginHorizontal();
GUILayout.BeginVertical(); GUILayout.BeginVertical();
GUILayout.Label("Altitude: " + (Altitude * 0.001f).ToString("F1") + "km", settingAtmoStyle, GUILayout.Width(125.0f * GuiDisplaySize.Offset)); GUILayout.Label("Altitude: " + (Altitude * 0.001f).ToString("F1") + "km", settingAtmoStyle, GUILayout.Width(125.0f * GuiDisplaySize.Offset));
GUI.skin = HighLogic.Skin; GUI.skin = HighLogic.Skin;
Altitude = GUILayout.HorizontalSlider(Altitude, 0.0f, (float)(CelestialBodies.SelectedBody.CelestialBody.atmosphereDepth)); Altitude = GUILayout.HorizontalSlider(Altitude, 0.0f, (float)(CelestialBodies.SelectedBody.CelestialBody.atmosphereDepth));
GUI.skin = null; GUI.skin = null;
GUILayout.EndVertical(); GUILayout.EndVertical();
   
GUILayout.Space(5.0f); GUILayout.Space(5.0f);
   
GUILayout.BeginVertical(); GUILayout.BeginVertical();
GUILayout.Label("Mach: " + atmosphericMach.ToString("F1"), settingAtmoStyle, GUILayout.Width(125.0f * GuiDisplaySize.Offset)); GUILayout.Label("Mach: " + atmosphericMach.ToString("F2"), settingAtmoStyle, GUILayout.Width(125.0f * GuiDisplaySize.Offset));
GUI.skin = HighLogic.Skin; GUI.skin = HighLogic.Skin;
atmosphericMach = GUILayout.HorizontalSlider(Mathf.Clamp(atmosphericMach, 0.0f, maxMach), 0.0f, maxMach); atmosphericMach = GUILayout.HorizontalSlider(Mathf.Clamp(atmosphericMach, 0.0f, maxMach), 0.0f, maxMach);
GUI.skin = null; GUI.skin = null;
GUILayout.EndVertical(); GUILayout.EndVertical();
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Exception(ex, "BuildAdvanced.DrawAtmosphericDetails()"); Logger.Exception(ex, "BuildAdvanced.DrawAtmosphericDetails()");
} }
} }
   
private void DrawBodiesList() private void DrawBodiesList()
{ {
if (CelestialBodies.SystemBody == CelestialBodies.SelectedBody) if (CelestialBodies.SystemBody == CelestialBodies.SelectedBody)
{ {
DrawBody(CelestialBodies.SystemBody); DrawBody(CelestialBodies.SystemBody);
} }
else else
{ {
foreach (CelestialBodies.BodyInfo body in CelestialBodies.SystemBody.Children) foreach (CelestialBodies.BodyInfo body in CelestialBodies.SystemBody.Children)
{ {
DrawBody(body); DrawBody(body);
} }
} }
} }
   
private void DrawBody(CelestialBodies.BodyInfo bodyInfo, int depth = 0) private void DrawBody(CelestialBodies.BodyInfo bodyInfo, int depth = 0)
{ {
GUILayout.BeginHorizontal(); GUILayout.BeginHorizontal();
GUILayout.Space(20.0f * depth); GUILayout.Space(20.0f * depth);
if (GUILayout.Button(bodyInfo.Children.Count > 0 ? bodyInfo.Name + " [" + bodyInfo.Children.Count + "]" : bodyInfo.Name, bodyInfo.Selected && bodyInfo.SelectedDepth == 0 ? bodiesButtonActiveStyle : bodiesButtonStyle)) if (GUILayout.Button(bodyInfo.Children.Count > 0 ? bodyInfo.Name + " [" + bodyInfo.Children.Count + "]" : bodyInfo.Name, bodyInfo.Selected && bodyInfo.SelectedDepth == 0 ? bodiesButtonActiveStyle : bodiesButtonStyle))
{ {
CelestialBodies.SetSelectedBody(bodyInfo.Name); CelestialBodies.SetSelectedBody(bodyInfo.Name);
Altitude = 0.0f; Altitude = 0.0f;
bodiesList.Resize = true; bodiesList.Resize = true;
} }
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
   
if (bodyInfo.Selected) if (bodyInfo.Selected)
{ {
for (int i = 0; i < bodyInfo.Children.Count; ++i) for (int i = 0; i < bodyInfo.Children.Count; ++i)
{ {
DrawBody(bodyInfo.Children[i], depth + 1); DrawBody(bodyInfo.Children[i], depth + 1);
} }
} }
} }
   
/// <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 * GuiDisplaySize.Offset)); GUILayout.BeginVertical(GUILayout.Width(75.0f * GuiDisplaySize.Offset));
GUILayout.Label("BURN", titleStyle); GUILayout.Label("BURN", titleStyle);
for (int i = 0; i < stagesLength; ++i) for (int i = 0; i < stagesLength; ++i)
{ {
stage = stages[i]; stage = stages[i];
if (showAllStages || stage.deltaV > 0.0) if (showAllStages || stage.deltaV > 0.0)
{ {
GUILayout.Label(TimeFormatter.ConvertToString(stage.time), infoStyle); GUILayout.Label(TimeFormatter.ConvertToString(stage.time), infoStyle);
} }
} }
GUILayout.EndVertical(); GUILayout.EndVertical();
} }
   
/// <summary> /// <summary>
/// Draws the cost column. /// Draws the cost column.
/// </summary> /// </summary>
private void DrawCost() private void DrawCost()
{ {
GUILayout.BeginVertical(GUILayout.Width(110.0f * GuiDisplaySize.Offset)); GUILayout.BeginVertical(GUILayout.Width(110.0f * GuiDisplaySize.Offset));
GUILayout.Label("COST", titleStyle); GUILayout.Label("COST", titleStyle);
for (int i = 0; i < stagesLength; ++i) for (int i = 0; i < stagesLength; ++i)
{ {
stage = stages[i]; stage = stages[i];
if (showAllStages || stage.deltaV > 0.0) if (showAllStages || stage.deltaV > 0.0)
{ {
GUILayout.Label(Units.Cost(stage.cost, stage.totalCost), infoStyle); GUILayout.Label(Units.Cost(stage.cost, stage.totalCost), infoStyle);
} }
} }
GUILayout.EndVertical(); GUILayout.EndVertical();
} }
   
/// <summary> /// <summary>
/// Draws the deltaV column. /// Draws the deltaV column.
/// </summary> /// </summary>
private void DrawDeltaV() private void DrawDeltaV()
{ {
GUILayout.BeginVertical(GUILayout.Width(100.0f * GuiDisplaySize.Offset)); GUILayout.BeginVertical(GUILayout.Width(100.0f * GuiDisplaySize.Offset));
GUILayout.Label("DELTA-V", titleStyle); GUILayout.Label("DELTA-V", titleStyle);
for (int i = 0; i < stagesLength; ++i) for (int i = 0; i < stagesLength; ++i)
{ {
stage = stages[i]; stage = stages[i];
if (showAllStages || stage.deltaV > 0.0) if (showAllStages || stage.deltaV > 0.0)
{ {
GUILayout.Label(stage.deltaV.ToString("N0") + " / " + stage.inverseTotalDeltaV.ToString("N0") + "m/s", infoStyle); GUILayout.Label(stage.deltaV.ToString("N0") + " / " + stage.inverseTotalDeltaV.ToString("N0") + "m/s", infoStyle);
} }
} }
GUILayout.EndVertical(); GUILayout.EndVertical();
} }
   
/// <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 * GuiDisplaySize.Offset)); GUILayout.BeginVertical(GUILayout.Width(75.0f * GuiDisplaySize.Offset));
GUILayout.Label("ISP", titleStyle); GUILayout.Label("ISP", titleStyle);
for (int i = 0; i < stagesLength; ++i) for (int i = 0; i < stagesLength; ++i)
{ {
stage = stages[i]; stage = stages[i];
if (showAllStages || stage.deltaV > 0.0) if (showAllStages || stage.deltaV > 0.0)
{ {
GUILayout.Label(stage.isp.ToString("F1") + "s", infoStyle); GUILayout.Label(stage.isp.ToString("F1") + "s", infoStyle);
} }
} }
GUILayout.EndVertical(); GUILayout.EndVertical();
} }
   
/// <summary> /// <summary>
/// Draws the mass column. /// Draws the mass column.
/// </summary> /// </summary>
private void DrawMass() private void DrawMass()
{ {
GUILayout.BeginVertical(GUILayout.Width(110.0f * GuiDisplaySize.Offset)); GUILayout.BeginVertical(GUILayout.Width(110.0f * GuiDisplaySize.Offset));
GUILayout.Label("MASS", titleStyle); GUILayout.Label("MASS", titleStyle);
for (int i = 0; i < stagesLength; ++i) for (int i = 0; i < stagesLength; ++i)
{ {
stage = stages[i]; stage = stages[i];
if (showAllStages || stage.deltaV > 0.0) if (showAllStages || stage.deltaV > 0.0)
{ {
GUILayout.Label(Units.ToMass(stage.mass, stage.totalMass), infoStyle); GUILayout.Label(Units.ToMass(stage.mass, stage.totalMass), infoStyle);
} }
} }
GUILayout.EndVertical(); GUILayout.EndVertical();
} }
   
/// <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 * GuiDisplaySize.Offset)); GUILayout.BeginVertical(GUILayout.Width(50.0f * GuiDisplaySize.Offset));
GUILayout.Label("PARTS", titleStyle); GUILayout.Label("PARTS", titleStyle);
for (int i = 0; i < stagesLength; ++i) for (int i = 0; i < stagesLength; ++i)
{ {
stage = stages[i]; stage = stages[i];
if (showAllStages || stage.deltaV > 0.0) if (showAllStages || stage.deltaV > 0.0)
{ {
GUILayout.Label(stage.partCount + " / " + stage.totalPartCount, infoStyle); GUILayout.Label(stage.partCount + " / " + stage.totalPartCount, infoStyle);
} }
} }
GUILayout.EndVertical(); GUILayout.EndVertical();
} }
   
/// <summary> /// <summary>
/// Draws the settings panel. /// Draws the settings panel.
/// </summary> /// </summary>
private void DrawSettings() private void DrawSettings()
{ {
GUILayout.BeginHorizontal(); GUILayout.BeginHorizontal();
GUILayout.Label("Compact mode collapses to the:", settingStyle); GUILayout.Label("Compact mode collapses to the:", settingStyle);
compactCollapseRight = !GUILayout.Toggle(!compactCollapseRight, "LEFT", buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset)); compactCollapseRight = !GUILayout.Toggle(!compactCollapseRight, "LEFT", buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset));
compactCollapseRight = GUILayout.Toggle(compactCollapseRight, "RIGHT", buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset)); compactCollapseRight = GUILayout.Toggle(compactCollapseRight, "RIGHT", buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset));
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
   
GUILayout.BeginHorizontal(); GUILayout.BeginHorizontal();
GUILayout.Label("Simulate using vectored thrust values:"); GUILayout.Label("Simulate using vectored thrust values:");
SimManager.vectoredThrust = GUILayout.Toggle(SimManager.vectoredThrust, "ENABLED", buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset)); SimManager.vectoredThrust = GUILayout.Toggle(SimManager.vectoredThrust, "ENABLED", buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset));
  GUILayout.EndHorizontal();
   
  GUILayout.BeginHorizontal();
  GUILayout.Label("Verbose Simulation Log:");
  SimManager.logOutput = GUILayout.Toggle(SimManager.logOutput, "ENABLED", buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset));
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
   
GUILayout.BeginHorizontal(); GUILayout.BeginHorizontal();
GUILayout.Label("Build Engineer Overlay:", settingStyle); GUILayout.Label("Build Engineer Overlay:", settingStyle);
BuildOverlay.Visible = GUILayout.Toggle(BuildOverlay.Visible, "VISIBLE", buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset)); BuildOverlay.Visible = GUILayout.Toggle(BuildOverlay.Visible, "VISIBLE", buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset));
BuildOverlayPartInfo.NamesOnly = GUILayout.Toggle(BuildOverlayPartInfo.NamesOnly, "NAMES ONLY", buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset)); BuildOverlayPartInfo.NamesOnly = GUILayout.Toggle(BuildOverlayPartInfo.NamesOnly, "NAMES ONLY", buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset));
BuildOverlayPartInfo.ClickToOpen = GUILayout.Toggle(BuildOverlayPartInfo.ClickToOpen, "CLICK TO OPEN", buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset)); BuildOverlayPartInfo.ClickToOpen = GUILayout.Toggle(BuildOverlayPartInfo.ClickToOpen, "CLICK TO OPEN", buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset));
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
   
GUILayout.BeginHorizontal(); GUILayout.BeginHorizontal();
GUILayout.Label("Flight Engineer activation mode:", settingStyle); GUILayout.Label("Flight Engineer activation mode:", settingStyle);
FlightEngineerCore.IsCareerMode = GUILayout.Toggle(FlightEngineerCore.IsCareerMode, "CAREER", buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset)); FlightEngineerCore.IsCareerMode = GUILayout.Toggle(FlightEngineerCore.IsCareerMode, "CAREER", buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset));
FlightEngineerCore.IsCareerMode = !GUILayout.Toggle(!FlightEngineerCore.IsCareerMode, "PARTLESS", buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset)); FlightEngineerCore.IsCareerMode = !GUILayout.Toggle(!FlightEngineerCore.IsCareerMode, "PARTLESS", buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset));
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
   
GUILayout.BeginHorizontal(); GUILayout.BeginHorizontal();
GUILayout.Label("Flight Engineer Career Limitations:", settingStyle); GUILayout.Label("Flight Engineer Career Limitations:", settingStyle);
FlightEngineerCore.IsKerbalLimited = GUILayout.Toggle(FlightEngineerCore.IsKerbalLimited, "KERBAL", buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset)); FlightEngineerCore.IsKerbalLimited = GUILayout.Toggle(FlightEngineerCore.IsKerbalLimited, "KERBAL", buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset));
FlightEngineerCore.IsTrackingStationLimited = GUILayout.Toggle(FlightEngineerCore.IsTrackingStationLimited, "TRACKING", buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset)); FlightEngineerCore.IsTrackingStationLimited = GUILayout.Toggle(FlightEngineerCore.IsTrackingStationLimited, "TRACKING", buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset));
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
   
GUILayout.BeginHorizontal(); GUILayout.BeginHorizontal();
GUILayout.Label("GUI Size: " + GuiDisplaySize.Increment, settingStyle); GUILayout.Label("GUI Size: " + GuiDisplaySize.Increment, settingStyle);
if (GUILayout.Button("<", buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset))) if (GUILayout.Button("<", buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset)))
{ {
GuiDisplaySize.Increment--; GuiDisplaySize.Increment--;
} }
if (GUILayout.Button(">", buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset))) if (GUILayout.Button(">", buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset)))
{ {
GuiDisplaySize.Increment++; GuiDisplaySize.Increment++;
} }
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
   
GUILayout.Label("Minimum delay between simulations: " + SimManager.minSimTime.Milliseconds + "ms", settingStyle); GUILayout.Label("Minimum delay between simulations: " + SimManager.minSimTime.Milliseconds + "ms", settingStyle);
GUI.skin = HighLogic.Skin; GUI.skin = HighLogic.Skin;
SimManager.minSimTime = new TimeSpan(0, 0, 0, 0, (int)GUILayout.HorizontalSlider(SimManager.minSimTime.Milliseconds, 0, 2000.0f)); SimManager.minSimTime = new TimeSpan(0, 0, 0, 0, (int)GUILayout.HorizontalSlider(SimManager.minSimTime.Milliseconds, 0, 2000.0f));
GUI.skin = null; GUI.skin = null;
} }
   
/// <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 * GuiDisplaySize.Offset)); GUILayout.BeginVertical(GUILayout.Width(30.0f * GuiDisplaySize.Offset));
GUILayout.Label(string.Empty, titleStyle); GUILayout.Label(string.Empty, titleStyle);
for (int i = 0; i < stagesLength; ++i) for (int i = 0; i < stagesLength; ++i)
{ {
stage = stages[i]; stage = stages[i];
if (showAllStages || stage.deltaV > 0.0) if (showAllStages || stage.deltaV > 0.0)
{ {
GUILayout.Label("S" + stage.number, titleStyle); GUILayout.Label("S" + stage.number, titleStyle);
} }
} }
GUILayout.EndVertical(); GUILayout.EndVertical();
} }
   
/// <summary> /// <summary>
/// Draws the thrust column. /// Draws the thrust column.
/// </summary> /// </summary>
private void DrawThrust() private void DrawThrust()
{ {
GUILayout.BeginVertical(GUILayout.Width(75.0f * GuiDisplaySize.Offset)); GUILayout.BeginVertical(GUILayout.Width(75.0f * GuiDisplaySize.Offset));
GUILayout.Label("THRUST", titleStyle); GUILayout.Label("THRUST", titleStyle);
for (int i = 0; i < stagesLength; ++i) for (int i = 0; i < stagesLength; ++i)
{ {
stage = stages[i]; stage = stages[i];
if (showAllStages || stage.deltaV > 0.0) if (showAllStages || stage.deltaV > 0.0)
{ {
GUILayout.Label(stage.thrust.ToForce(), infoStyle); GUILayout.Label(stage.thrust.ToForce(), infoStyle);
} }
} }
GUILayout.EndVertical(); GUILayout.EndVertical();
} }
   
/// <summary> /// <summary>
/// Draws the torque column. /// Draws the torque column.
/// </summary> /// </summary>
private void DrawTorque() private void DrawTorque()
{ {
GUILayout.BeginVertical(GUILayout.Width(75.0f * GuiDisplaySize.Offset)); GUILayout.BeginVertical(GUILayout.Width(75.0f * GuiDisplaySize.Offset));
GUILayout.Label("TORQUE", titleStyle); GUILayout.Label("TORQUE", titleStyle);
for (int i = 0; i < stagesLength; ++i) for (int i = 0; i < stagesLength; ++i)
{ {
stage = stages[i]; stage = stages[i];
if (showAllStages || stage.deltaV > 0.0) if (showAllStages || stage.deltaV > 0.0)
{ {
GUILayout.Label(stage.maxThrustTorque.ToTorque(), infoStyle); GUILayout.Label(stage.maxThrustTorque.ToTorque(), infoStyle);
} }
} }
GUILayout.EndVertical(); GUILayout.EndVertical();
} }
   
/// <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 * GuiDisplaySize.Offset)); GUILayout.BeginVertical(GUILayout.Width(100.0f * GuiDisplaySize.Offset));
GUILayout.Label("TWR (MAX)", titleStyle); GUILayout.Label("TWR (MAX)", titleStyle);
for (int i = 0; i < stagesLength; ++i) for (int i = 0; i < stagesLength; ++i)
{ {
stage = stages[i]; stage = stages[i];
if (showAllStages || stage.deltaV > 0.0) if (showAllStages || stage.deltaV > 0.0)
{ {
GUILayout.Label(stage.thrustToWeight.ToString("F2") + " (" + stage.maxThrustToWeight.ToString("F2") + ")", infoStyle); GUILayout.Label(stage.thrustToWeight.ToString("F2") + " (" + stage.maxThrustToWeight.ToString("F2") + ")", infoStyle);
} }
} }
GUILayout.EndVertical(); GUILayout.EndVertical();
} }
   
private void EditorLock(bool state) private void EditorLock(bool state)
{ {
if (state) if (state)
{ {
InputLockManager.SetControlLock(ControlTypes.All, "KER_BuildAdvanced"); InputLockManager.SetControlLock(ControlTypes.All, "KER_BuildAdvanced");
BuildOverlayPartInfo.Hidden = true; BuildOverlayPartInfo.Hidden = true;
isEditorLocked = true; isEditorLocked = true;
} }
else else
{ {
InputLockManager.SetControlLock(ControlTypes.None, "KER_BuildAdvanced"); InputLockManager.SetControlLock(ControlTypes.None, "KER_BuildAdvanced");
BuildOverlayPartInfo.Hidden = false; BuildOverlayPartInfo.Hidden = false;
isEditorLocked = false; isEditorLocked = false;
} }
} }
   
private void GetStageInfo() private void GetStageInfo()
{ {
stages = SimManager.Stages; stages = SimManager.Stages;
if (stages != null && stages.Length > 0) if (stages != null && stages.Length > 0)
{ {
maxMach = stages[stages.Length - 1].maxMach; maxMach = stages[stages.Length - 1].maxMach;
} }
} }
   
/// <summary> /// <summary>
/// Initialises all the styles that are required. /// Initialises all the styles that are required.
/// </summary> /// </summary>
private void InitialiseStyles() private void InitialiseStyles()
{ {
windowStyle = new GUIStyle(HighLogic.Skin.window) windowStyle = new GUIStyle(HighLogic.Skin.window)
{ {
alignment = TextAnchor.UpperLeft alignment = TextAnchor.UpperLeft
}; };
   
areaStyle = new GUIStyle(HighLogic.Skin.box) areaStyle = new GUIStyle(HighLogic.Skin.box)
{ {
padding = new RectOffset(0, 0, 9, 0) padding = new RectOffset(0, 0, 9, 0)
}; };
   
areaSettingStyle = new GUIStyle(HighLogic.Skin.box) areaSettingStyle = new GUIStyle(HighLogic.Skin.box)
{ {
padding = new RectOffset(10, 10, 10, 10) padding = new RectOffset(10, 10, 10, 10)
}; };
   
buttonStyle = new GUIStyle(HighLogic.Skin.button) buttonStyle = new GUIStyle(HighLogic.Skin.button)
{ {
normal = normal =
{ {
textColor = Color.white textColor = Color.white
}, },
fontSize = (int)(11 * GuiDisplaySize.Offset), fontSize = (int)(11 * GuiDisplaySize.Offset),
fontStyle = FontStyle.Bold, fontStyle = FontStyle.Bold,
alignment = TextAnchor.MiddleCenter alignment = TextAnchor.MiddleCenter
}; };
   
titleStyle = new GUIStyle(HighLogic.Skin.label) titleStyle = new GUIStyle(HighLogic.Skin.label)
{ {
normal = normal =
{ {
textColor = Color.white textColor = Color.white
}, },
fontSize = (int)(11 * GuiDisplaySize.Offset), fontSize = (int)(11 * GuiDisplaySize.Offset),
fontStyle = FontStyle.Bold, fontStyle = FontStyle.Bold,
alignment = TextAnchor.MiddleCenter, alignment = TextAnchor.MiddleCenter,
stretchWidth = true, stretchWidth = true,
}; };
   
infoStyle = new GUIStyle(HighLogic.Skin.label) infoStyle = new GUIStyle(HighLogic.Skin.label)
{ {
fontSize = (int)(11 * GuiDisplaySize.Offset), fontSize = (int)(11 * GuiDisplaySize.Offset),
fontStyle = FontStyle.Bold, fontStyle = FontStyle.Bold,
alignment = TextAnchor.MiddleCenter, alignment = TextAnchor.MiddleCenter,
stretchWidth = true stretchWidth = true
}; };
   
settingStyle = new GUIStyle(titleStyle) settingStyle = new GUIStyle(titleStyle)
{ {
alignment = TextAnchor.MiddleLeft, alignment = TextAnchor.MiddleLeft,
stretchWidth = true, stretchWidth = true,
stretchHeight = true stretchHeight = true
}; };
   
settingAtmoStyle = new GUIStyle(titleStyle) settingAtmoStyle = new GUIStyle(titleStyle)
{ {
margin = new RectOffset(), margin = new RectOffset(),
padding = new RectOffset(), padding = new RectOffset(),
alignment = TextAnchor.UpperLeft alignment = TextAnchor.UpperLeft
}; };
   
bodiesButtonStyle = new GUIStyle(HighLogic.Skin.button) bodiesButtonStyle = new GUIStyle(HighLogic.Skin.button)
{ {
margin = new RectOffset(0, 0, 2, 0), margin = new RectOffset(0, 0, 2, 0),
padding = new RectOffset(5, 5, 5, 5), padding = new RectOffset(5, 5, 5, 5),
normal = normal =
{ {
textColor = Color.white textColor = Color.white
}, },
active = active =
{ {
textColor = Color.white textColor = Color.white
}, },
fontSize = (int)(11 * GuiDisplaySize.Offset), fontSize = (int)(11 * GuiDisplaySize.Offset),
fontStyle = FontStyle.Bold, fontStyle = FontStyle.Bold,
alignment = TextAnchor.MiddleCenter, alignment = TextAnchor.MiddleCenter,
fixedHeight = 20.0f fixedHeight = 20.0f
}; };
   
bodiesButtonActiveStyle = new GUIStyle(bodiesButtonStyle) bodiesButtonActiveStyle = new GUIStyle(bodiesButtonStyle)
{ {
normal = bodiesButtonStyle.onNormal, normal = bodiesButtonStyle.onNormal,
hover = bodiesButtonStyle.onHover hover = bodiesButtonStyle.onHover
}; };
} }
   
/// <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
{ {
SettingHandler handler = SettingHandler.Load("BuildAdvanced.xml"); SettingHandler handler = SettingHandler.Load("BuildAdvanced.xml");
handler.Get("visible", ref visible); handler.Get("visible", ref visible);
position.x = handler.Get("windowPositionX", position.x); position.x = handler.Get("windowPositionX", position.x);
position.y = handler.Get("windowPositionY", position.y); position.y = handler.Get("windowPositionY", position.y);
handler.Get("compactMode", ref compactMode); handler.Get("compactMode", ref compactMode);
handler.Get("compactCollapseRight", ref compactCollapseRight); handler.Get("compactCollapseRight", ref compactCollapseRight);
handler.Get("showAllStages", ref showAllStages); handler.Get("showAllStages", ref showAllStages);
handler.Get("showAtmosphericDetails", ref showAtmosphericDetails); handler.Get("showAtmosphericDetails", ref showAtmosphericDetails);
handler.Get("showSettings", ref showSettings); handler.Get("showSettings", ref showSettings);
CelestialBodies.SetSelectedBody(handler.Get("selectedBodyName", CelestialBodies.SelectedBody.Name)); CelestialBodies.SetSelectedBody(handler.Get("selectedBodyName", CelestialBodies.SelectedBody.Name));
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Exception(ex, "BuildAdvanced.Load()"); Logger.Exception(ex, "BuildAdvanced.Load()");
} }
} }
   
private void OnSizeChanged() private void OnSizeChanged()
{ {
InitialiseStyles(); InitialiseStyles();
hasChanged = true; hasChanged = true;
} }
   
/// <summary> /// <summary>
/// Draws the OnGUI window. /// Draws the OnGUI window.
/// </summary> /// </summary>
private void Window(int windowId) private void Window(int windowId)
{ {
try try
{ {
compactModeRect = new Rect(position.width - 70.0f * GuiDisplaySize.Offset, 5.0f, 65.0f * GuiDisplaySize.Offset, 20.0f); compactModeRect = new Rect(position.width - 70.0f * GuiDisplaySize.Offset, 5.0f, 65.0f * GuiDisplaySize.Offset, 20.0f);
   
// Draw the compact mode toggle. // Draw the compact mode toggle.
if (GUI.Toggle(compactModeRect, compactMode, "COMPACT", buttonStyle) != compactMode) if (GUI.Toggle(compactModeRect, compactMode, "COMPACT", buttonStyle) != compactMode)
{ {
hasChanged = true; hasChanged = true;
compactCheck = 2; compactCheck = 2;
compactRight = position.xMax; compactRight = position.xMax;
compactMode = !compactMode; compactMode = !compactMode;
} }
   
// When not in compact mode draw the 'All Stages' and 'Atmospheric' toggles. // When not in compact mode draw the 'All Stages' and 'Atmospheric' toggles.
if (!compactMode) if (!compactMode)
{ {
if (GUI.Toggle(new Rect(position.width - 143.0f * GuiDisplaySize.Offset, 5.0f, 70.0f * GuiDisplaySize.Offset, 20.0f), showSettings, "SETTINGS", buttonStyle) != showSettings) if (GUI.Toggle(new Rect(position.width - 143.0f * GuiDisplaySize.Offset, 5.0f, 70.0f * GuiDisplaySize.Offset, 20.0f), showSettings, "SETTINGS", buttonStyle) != showSettings)
{ {
hasChanged = true; hasChanged = true;
showSettings = !showSettings; showSettings = !showSettings;
} }
   
if (GUI.Toggle(new Rect(position.width - 226.0f * GuiDisplaySize.Offset, 5.0f, 80.0f * GuiDisplaySize.Offset, 20.0f), showAllStages, "ALL STAGES", buttonStyle) != showAllStages) if (GUI.Toggle(new Rect(position.width - 226.0f * GuiDisplaySize.Offset, 5.0f, 80.0f * GuiDisplaySize.Offset, 20.0f), showAllStages, "ALL STAGES", buttonStyle) != showAllStages)
{ {
hasChanged = true; hasChanged = true;
showAllStages = !showAllStages; showAllStages = !showAllStages;
} }
   
if (GUI.Toggle(new Rect(position.width - 324.0f * GuiDisplaySize.Offset, 5.0f, 95.0f * GuiDisplaySize.Offset, 20.0f), showAtmosphericDetails, "ATMOSPHERIC", buttonStyle) != showAtmosphericDetails) if (GUI.Toggle(new Rect(position.width - 324.0f * GuiDisplaySize.Offset, 5.0f, 95.0f * GuiDisplaySize.Offset, 20.0f), showAtmosphericDetails, "ATMOSPHERIC", buttonStyle) != showAtmosphericDetails)
{ {
hasChanged = true; hasChanged = true;
showAtmosphericDetails = !showAtmosphericDetails; showAtmosphericDetails = !showAtmosphericDetails;
} }
   
bodiesListPosition = new Rect(position.width - 452.0f * GuiDisplaySize.Offset, 5.0f, 125.0f * GuiDisplaySize.Offset, 20.0f); bodiesListPosition = new Rect(position.width - 452.0f * GuiDisplaySize.Offset, 5.0f, 125.0f * GuiDisplaySize.Offset, 20.0f);
bodiesList.enabled = GUI.Toggle(bodiesListPosition, bodiesList.enabled, "BODY: " + CelestialBodies.SelectedBody.Name.ToUpper(), buttonStyle); bodiesList.enabled = GUI.Toggle(bodiesListPosition, bodiesList.enabled, "BODY: " + CelestialBodies.SelectedBody.Name.ToUpper(), buttonStyle);
bodiesList.SetPosition(bodiesListPosition.Translate(position)); bodiesList.SetPosition(bodiesListPosition.Translate(position));
  }
  else
  {
  if (GUI.Toggle(new Rect(position.width - 133.0f * GuiDisplaySize.Offset, 5.0f, 60.0f * GuiDisplaySize.Offset, 20.0f), showAtmosphericDetails, "ATMO", buttonStyle) != showAtmosphericDetails)
  {
  hasChanged = true;
  showAtmosphericDetails = !showAtmosphericDetails;
  }
} }
   
// Draw the main informational display box. // Draw the main informational display box.
if (!compactMode) if (!compactMode)
{ {
GUILayout.BeginHorizontal(areaStyle); GUILayout.BeginHorizontal(areaStyle);
DrawStageNumbers(); DrawStageNumbers();
DrawPartCount(); DrawPartCount();
DrawCost(); DrawCost();
DrawMass(); DrawMass();
DrawIsp(); DrawIsp();
DrawThrust(); DrawThrust();
DrawTorque(); DrawTorque();
DrawTwr(); DrawTwr();
DrawDeltaV(); DrawDeltaV();
DrawBurnTime(); DrawBurnTime();
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
   
if (showAtmosphericDetails) if (showAtmosphericDetails && !compactMode)
{ {
GUILayout.BeginVertical(areaSettingStyle); GUILayout.BeginVertical(areaSettingStyle);
DrawAtmosphericDetails(); DrawAtmosphericDetails();
GUILayout.EndVertical(); GUILayout.EndVertical();
} }
   
if (showSettings) if (showSettings)
{ {
GUILayout.BeginVertical(areaSettingStyle); GUILayout.BeginVertical(areaSettingStyle);
DrawSettings(); DrawSettings();
GUILayout.EndVertical(); GUILayout.EndVertical();
} }
} }
else // Draw only a few details when in compact mode. else // Draw only a few details when in compact mode.
{ {
GUILayout.BeginHorizontal(areaStyle); GUILayout.BeginHorizontal(areaStyle);
DrawStageNumbers(); DrawStageNumbers();
DrawTwr(); DrawTwr();
DrawDeltaV(); DrawDeltaV();
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
} }
   
GUI.DragWindow(); GUI.DragWindow();
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Exception(ex, "BuildAdvanced.Window()"); Logger.Exception(ex, "BuildAdvanced.Window()");
} }
} }
#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 #region Using Directives
   
using KerbalEngineer.Flight.Sections; using KerbalEngineer.Flight.Sections;
   
#endregion #endregion
   
namespace KerbalEngineer.Flight.Readouts.Orbital namespace KerbalEngineer.Flight.Readouts.Orbital
{ {
public class Eccentricity : ReadoutModule public class Eccentricity : ReadoutModule
{ {
#region Constructors #region Constructors
   
public Eccentricity() public Eccentricity()
{ {
this.Name = "Eccentricity"; this.Name = "Eccentricity";
this.Category = ReadoutCategory.GetCategory("Orbital"); this.Category = ReadoutCategory.GetCategory("Orbital");
this.HelpString = "Shows the vessel's orbital eccentricity."; this.HelpString = "Shows the vessel's orbital eccentricity.";
this.IsDefault = true; this.IsDefault = true;
} }
   
#endregion #endregion
   
#region Methods: public #region Methods: public
   
public override void Draw(SectionModule section) public override void Draw(SectionModule section)
{ {
this.DrawLine(FlightGlobals.ship_orbit.eccentricity.ToString("F3"), section.IsHud); this.DrawLine(FlightGlobals.ship_orbit.eccentricity.ToString("F5"), section.IsHud);
} }
   
#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 #region Using Directives
   
using KerbalEngineer.Flight.Sections; using KerbalEngineer.Flight.Sections;
  using KerbalEngineer.Helpers;
   
#endregion #endregion
   
namespace KerbalEngineer.Flight.Readouts.Surface namespace KerbalEngineer.Flight.Readouts.Surface
{ {
public class Latitude : ReadoutModule public class Latitude : ReadoutModule
{ {
#region Constructors #region Constructors
   
public Latitude() public Latitude()
{ {
this.Name = "Latitude"; this.Name = "Latitude";
this.Category = ReadoutCategory.GetCategory("Surface"); this.Category = ReadoutCategory.GetCategory("Surface");
this.HelpString = "Shows the vessel's latitude position around the celestial body. Latitude is the angle from the equator to poles."; this.HelpString = "Shows the vessel's latitude position around the celestial body. Latitude is the angle from the equator to poles.";
this.IsDefault = true; this.IsDefault = true;
} }
   
#endregion #endregion
   
#region Methods: public #region Methods: public
   
public override void Draw(SectionModule section) public override void Draw(SectionModule section)
{ {
this.DrawLine(KSPUtil.PrintLatitude(FlightGlobals.ship_latitude), section.IsHud); this.DrawLine(Units.ToAngleDMS(FlightGlobals.ship_latitude) + (FlightGlobals.ship_latitude < 0 ? " S" : " N"), section.IsHud);
} }
   
#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/>.
// //
   
namespace KerbalEngineer.Flight.Readouts.Vessel namespace KerbalEngineer.Flight.Readouts.Vessel
{ {
#region Using Directives #region Using Directives
   
using Helpers; using Helpers;
using Sections; using Sections;
   
#endregion #endregion
   
public class PartCount : ReadoutModule public class PartCount : ReadoutModule
{ {
#region Constructors #region Constructors
   
public PartCount() public PartCount()
{ {
this.Name = "Part Count"; this.Name = "Part Count";
this.Category = ReadoutCategory.GetCategory("Vessel"); this.Category = ReadoutCategory.GetCategory("Vessel");
this.HelpString = string.Empty; this.HelpString = string.Empty;
this.IsDefault = true; this.IsDefault = true;
} }
   
#endregion #endregion
   
#region Methods #region Methods
   
public override void Draw(SectionModule section) public override void Draw(SectionModule section)
{ {
if (SimulationProcessor.ShowDetails) if (SimulationProcessor.ShowDetails)
{ {
this.DrawLine(Units.ConcatF(SimulationProcessor.LastStage.partCount, SimulationProcessor.LastStage.totalPartCount), section.IsHud); this.DrawLine(Units.ConcatF(SimulationProcessor.LastStage.partCount, SimulationProcessor.LastStage.totalPartCount, 0), section.IsHud);
} }
} }
   
public override void Reset() public override void Reset()
{ {
FlightEngineerCore.Instance.AddUpdatable(SimulationProcessor.Instance); FlightEngineerCore.Instance.AddUpdatable(SimulationProcessor.Instance);
} }
   
public override void Update() public override void Update()
{ {
SimulationProcessor.RequestUpdate(); SimulationProcessor.RequestUpdate();
} }
   
#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/>.
// //
   
namespace KerbalEngineer.Helpers namespace KerbalEngineer.Helpers
{ {
#region Using Directives #region Using Directives
using System; using System;
   
#endregion #endregion
   
public static class Units public static class Units
{ {
#region Methods #region Methods
public const double GRAVITY = 9.80665; public const double GRAVITY = 9.80665;
   
public static string Concat(int value1, int value2) public static string Concat(int value1, int value2)
{ {
return value1 + " / " + value2; return value1 + " / " + value2;
} }
   
public static string ConcatF(double value1, double value2, int decimals = 1) public static string ConcatF(double value1, double value2, int decimals = 1)
{ {
return value1.ToString("F" + decimals) + " / " + value2.ToString("F" + decimals); return value1.ToString("F" + decimals) + " / " + value2.ToString("F" + decimals);
} }
   
public static string ConcatF(double value1, double value2, double value3, int decimals = 1) public static string ConcatF(double value1, double value2, double value3, int decimals = 1)
{ {
return value1.ToString("F" + decimals) + " / " + value2.ToString("F" + decimals) + " / " + value3.ToString("F" + decimals); return value1.ToString("F" + decimals) + " / " + value2.ToString("F" + decimals) + " / " + value3.ToString("F" + decimals);
} }
   
public static string ConcatN(double value1, double value2, int decimals = 1) public static string ConcatN(double value1, double value2, int decimals = 1)
{ {
return value1.ToString("N" + decimals) + " / " + value2.ToString("N" + decimals); return value1.ToString("N" + decimals) + " / " + value2.ToString("N" + decimals);
} }
   
public static string ConcatN(double value1, double value2, double value3, int decimals = 1) public static string ConcatN(double value1, double value2, double value3, int decimals = 1)
{ {
return value1.ToString("N" + decimals) + " / " + value2.ToString("N" + decimals) + " / " + value3.ToString("N" + decimals); return value1.ToString("N" + decimals) + " / " + value2.ToString("N" + decimals) + " / " + value3.ToString("N" + decimals);
} }
   
public static string Cost(double value, int decimals = 1) public static string Cost(double value, int decimals = 1)
{ {
if (value >= 1000000.0) if (value >= 1000000.0)
{ {
return (value / 1000.0).ToString("N" + decimals) + "K"; return (value / 1000.0).ToString("N" + decimals) + "K";
} }
return value.ToString("N" + decimals); return value.ToString("N" + decimals);
} }
   
public static string Cost(double value1, double value2, int decimals = 1) public static string Cost(double value1, double value2, int decimals = 1)
{ {
if (value1 >= 1000000.0 || value2 >= 1000000.0) if (value1 >= 1000000.0 || value2 >= 1000000.0)
{ {
return (value1 / 1000.0).ToString("N" + decimals) + " / " + (value2 / 1000.0).ToString("N" + decimals) + "K"; return (value1 / 1000.0).ToString("N" + decimals) + " / " + (value2 / 1000.0).ToString("N" + decimals) + "K";
} }
return value1.ToString("N" + decimals) + " / " + value2.ToString("N" + decimals); return value1.ToString("N" + decimals) + " / " + value2.ToString("N" + decimals);
} }
   
public static string ToAcceleration(double value, int decimals = 2) public static string ToAcceleration(double value, int decimals = 2)
{ {
return value.ToString("N" + decimals) + "m/s²"; return value.ToString("N" + decimals) + "m/s²";
} }
   
public static string ToAcceleration(double value1, double value2, int decimals = 2) public static string ToAcceleration(double value1, double value2, int decimals = 2)
{ {
return value1.ToString("N" + decimals) + " / " + value2.ToString("N" + decimals) + "m/s²"; return value1.ToString("N" + decimals) + " / " + value2.ToString("N" + decimals) + "m/s²";
} }
   
public static string ToAngle(double value, int decimals = 5) public static string ToAngle(double value, int decimals = 5)
{ {
return value.ToString("F" + decimals) + "°"; return value.ToString("F" + decimals) + "°";
  }
   
  public static string ToAngleDMS(double value)
  {
  double absAngle = Math.Abs(value);
  int deg = (int)Math.Floor(absAngle);
  double rem = absAngle - deg;
  int min = (int)Math.Floor(rem * 60);
  rem -= ((double)min / 60);
  int sec = (int)Math.Floor(rem * 3600);
  return String.Format("{0:0}° {1:00}' {2:00}\"", deg, min, sec);
} }
   
public static string ToDistance(double value, int decimals = 1) public static string ToDistance(double value, int decimals = 1)
{ {
if (Math.Abs(value) < 1000000.0) if (Math.Abs(value) < 1000000.0)
{ {
if (Math.Abs(value) >= 10.0) if (Math.Abs(value) >= 10.0)
{ {
return value.ToString("N" + decimals) + "m"; return value.ToString("N" + decimals) + "m";
} }
   
value *= 100.0; value *= 100.0;
if (Math.Abs(value) >= 100.0) if (Math.Abs(value) >= 100.0)
{ {
return value.ToString("N" + decimals) + "cm"; return value.ToString("N" + decimals) + "cm";
} }
   
value *= 10.0; value *= 10.0;
return value.ToString("N" + decimals) + "mm"; return value.ToString("N" + decimals) + "mm";
} }
   
value /= 1000.0; value /= 1000.0;
if (Math.Abs(value) < 1000000.0) if (Math.Abs(value) < 1000000.0)
{ {
return value.ToString("N" + decimals) + "km"; return value.ToString("N" + decimals) + "km";
} }
   
value /= 1000.0; value /= 1000.0;
return value.ToString("N" + decimals) + "Mm"; return value.ToString("N" + decimals) + "Mm";
} }
   
public static string ToForce(double value) public static string ToForce(double value)
{ {
return value.ToString((value < 100000.0) ? (value < 10000.0) ? (value < 100.0) ? (Math.Abs(value) < Double.Epsilon) ? "N0" : "N3" : "N2" : "N1" : "N0") + "kN"; return value.ToString((value < 100000.0) ? (value < 10000.0) ? (value < 100.0) ? (Math.Abs(value) < Double.Epsilon) ? "N0" : "N3" : "N2" : "N1" : "N0") + "kN";
} }
   
public static string ToForce(double value1, double value2) public static string ToForce(double value1, double value2)
{ {
string format1 = (value1 < 100000.0) ? (value1 < 10000.0) ? (value1 < 100.0) ? (Math.Abs(value1) < Double.Epsilon) ? "N0" : "N3" : "N2" : "N1" : "N0"; string format1 = (value1 < 100000.0) ? (value1 < 10000.0) ? (value1 < 100.0) ? (Math.Abs(value1) < Double.Epsilon) ? "N0" : "N3" : "N2" : "N1" : "N0";
string format2 = (value2 < 100000.0) ? (value2 < 10000.0) ? (value2 < 100.0) ? (Math.Abs(value2) < Double.Epsilon) ? "N0" : "N3" : "N2" : "N1" : "N0"; string format2 = (value2 < 100000.0) ? (value2 < 10000.0) ? (value2 < 100.0) ? (Math.Abs(value2) < Double.Epsilon) ? "N0" : "N3" : "N2" : "N1" : "N0";
return value1.ToString(format1) + " / " + value2.ToString(format2) + "kN"; return value1.ToString(format1) + " / " + value2.ToString(format2) + "kN";
} }
   
public static string ToMass(double value, int decimals = 0) public static string ToMass(double value, int decimals = 0)
{ {
if (value >= 1000.0) if (value >= 1000.0)
{ {
return value.ToString("N" + decimals + 2) + "t"; return value.ToString("N" + decimals + 2) + "t";
} }
   
value *= 1000.0; value *= 1000.0;
return value.ToString("N" + decimals) + "kg"; return value.ToString("N" + decimals) + "kg";
} }
   
public static string ToMass(double value1, double value2, int decimals = 0) public static string ToMass(double value1, double value2, int decimals = 0)
{ {
if (value1 >= 1000.0f || value2 >= 1000.0f) if (value1 >= 1000.0f || value2 >= 1000.0f)
{ {
return value1.ToString("N" + decimals + 2) + " / " + value2.ToString("N" + decimals + 2) + "t"; return value1.ToString("N" + decimals + 2) + " / " + value2.ToString("N" + decimals + 2) + "t";
} }
   
value1 *= 1000.0; value1 *= 1000.0;
value2 *= 1000.0; value2 *= 1000.0;
return value1.ToString("N" + decimals) + " / " + value2.ToString("N" + decimals) + "kg"; return value1.ToString("N" + decimals) + " / " + value2.ToString("N" + decimals) + "kg";
} }
   
public static string ToPercent(double value, int decimals = 2) public static string ToPercent(double value, int decimals = 2)
{ {
value *= 100.0; value *= 100.0;
return value.ToString("F" + decimals) + "%"; return value.ToString("F" + decimals) + "%";
} }
   
public static string ToRate(double value, int decimals = 1) public static string ToRate(double value, int decimals = 1)
{ {
return value < 1.0 ? (value * 60.0).ToString("F" + decimals) + "/min" : value.ToString("F" + decimals) + "/sec"; return value < 1.0 ? (value * 60.0).ToString("F" + decimals) + "/min" : value.ToString("F" + decimals) + "/sec";
} }
   
public static string ToSpeed(double value, int decimals = 2) public static string ToSpeed(double value, int decimals = 2)
{ {
if (Math.Abs(value) < 1.0) if (Math.Abs(value) < 1.0)
{ {
return (value * 1000.0).ToString("N" + decimals) + "mm/s"; return (value * 1000.0).ToString("N" + decimals) + "mm/s";
} }
return value.ToString("N" + decimals) + "m/s"; return value.ToString("N" + decimals) + "m/s";
} }
   
public static string ToTime(double value) public static string ToTime(double value)
{ {
return TimeFormatter.ConvertToString(value); return TimeFormatter.ConvertToString(value);
} }
   
public static string ToTorque(double value) public static string ToTorque(double value)
{ {
return value.ToString((value < 100.0) ? (Math.Abs(value) < Double.Epsilon) ? "N0" : "N1" : "N0") + "kNm"; return value.ToString((value < 100.0) ? (Math.Abs(value) < Double.Epsilon) ? "N0" : "N1" : "N0") + "kNm";
} }
#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/>.
// //
   
   
namespace KerbalEngineer.VesselSimulator namespace KerbalEngineer.VesselSimulator
{ {
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using CompoundParts; using CompoundParts;
using Extensions; using Extensions;
using UnityEngine; using UnityEngine;
   
public class PartSim public class PartSim
{ {
private static readonly Pool<PartSim> pool = new Pool<PartSim>(Create, Reset); private static readonly Pool<PartSim> pool = new Pool<PartSim>(Create, Reset);
   
private readonly List<AttachNodeSim> attachNodes = new List<AttachNodeSim>(); private readonly List<AttachNodeSim> attachNodes = new List<AttachNodeSim>();
   
public double baseMass; public double baseMass;
public Vector3d centerOfMass; public Vector3d centerOfMass;
public double cost; public double cost;
public int decoupledInStage; public int decoupledInStage;
public bool fuelCrossFeed; public bool fuelCrossFeed;
public List<PartSim> fuelTargets = new List<PartSim>(); public List<PartSim> fuelTargets = new List<PartSim>();
public bool hasModuleEngines; public bool hasModuleEngines;
public bool hasModuleEnginesFX; public bool hasModuleEnginesFX;
public bool hasMultiModeEngine; public bool hasMultiModeEngine;
   
public bool hasVessel; public bool hasVessel;
public String initialVesselName; public String initialVesselName;
public int inverseStage; public int inverseStage;
public bool isDecoupler; public bool isDecoupler;
public bool isEngine; public bool isEngine;
public bool isFuelLine; public bool isFuelLine;
public bool isFuelTank; public bool isFuelTank;
public bool isLanded; public bool isLanded;
public bool isNoPhysics; public bool isNoPhysics;
public bool isSepratron; public bool isSepratron;
public bool isFairing; public bool isFairing;
public bool localCorrectThrust; public bool localCorrectThrust;
public float moduleMass; public float moduleMass;
public int stageIndex; public int stageIndex;
public String name; public String name;
public String noCrossFeedNodeKey; public String noCrossFeedNodeKey;
public PartSim parent; public PartSim parent;
public AttachModes parentAttach; public AttachModes parentAttach;
public Part part; // This is only set while the data structures are being initialised public Part part; // This is only set while the data structures are being initialised
public int partId = 0; public int partId = 0;
public ResourceContainer resourceDrains = new ResourceContainer(); public ResourceContainer resourceDrains = new ResourceContainer();
public ResourceContainer resourceFlowStates = new ResourceContainer(); public ResourceContainer resourceFlowStates = new ResourceContainer();
public ResourceContainer resources = new ResourceContainer(); public ResourceContainer resources = new ResourceContainer();
public double startMass = 0d; public double startMass = 0d;
public String vesselName; public String vesselName;
public VesselType vesselType; public VesselType vesselType;
   
private static PartSim Create() private static PartSim Create()
{ {
return new PartSim(); return new PartSim();
} }
   
private static void Reset(PartSim partSim) private static void Reset(PartSim partSim)
{ {
for (int i = 0; i < partSim.attachNodes.Count; i++) for (int i = 0; i < partSim.attachNodes.Count; i++)
{ {
partSim.attachNodes[i].Release(); partSim.attachNodes[i].Release();
} }
partSim.attachNodes.Clear(); partSim.attachNodes.Clear();
partSim.fuelTargets.Clear(); partSim.fuelTargets.Clear();
partSim.resourceDrains.Reset(); partSim.resourceDrains.Reset();
partSim.resourceFlowStates.Reset(); partSim.resourceFlowStates.Reset();
partSim.resources.Reset(); partSim.resources.Reset();
partSim.baseMass = 0d; partSim.baseMass = 0d;
partSim.startMass = 0d; partSim.startMass = 0d;
} }
   
public void Release() public void Release()
{ {
pool.Release(this); pool.Release(this);
} }
   
public static PartSim New(Part thePart, int id, double atmosphere, LogMsg log) public static PartSim New(Part thePart, int id, double atmosphere, LogMsg log)
{ {
PartSim partSim = pool.Borrow(); PartSim partSim = pool.Borrow();
   
   
partSim.part = thePart; partSim.part = thePart;
partSim.centerOfMass = thePart.transform.TransformPoint(thePart.CoMOffset); partSim.centerOfMass = thePart.transform.TransformPoint(thePart.CoMOffset);
partSim.partId = id; partSim.partId = id;
partSim.name = partSim.part.partInfo.name; partSim.name = partSim.part.partInfo.name;
   
if (log != null) if (log != null)
{  
log.buf.AppendLine("Create PartSim for " + partSim.name); log.buf.AppendLine("Create PartSim for " + partSim.name);
}  
   
partSim.parent = null; partSim.parent = null;
partSim.parentAttach = partSim.part.attachMode; partSim.parentAttach = partSim.part.attachMode;
partSim.fuelCrossFeed = partSim.part.fuelCrossFeed; partSim.fuelCrossFeed = partSim.part.fuelCrossFeed;
partSim.noCrossFeedNodeKey = partSim.part.NoCrossFeedNodeKey; partSim.noCrossFeedNodeKey = partSim.part.NoCrossFeedNodeKey;
partSim.decoupledInStage = partSim.DecoupledInStage(partSim.part); partSim.decoupledInStage = partSim.DecoupledInStage(partSim.part);
partSim.isFuelLine = partSim.part.HasModule<CModuleFuelLine>(); partSim.isFuelLine = partSim.part.HasModule<CModuleFuelLine>();
partSim.isFuelTank = partSim.part is FuelTank; partSim.isFuelTank = partSim.part is FuelTank;
partSim.isSepratron = partSim.IsSepratron(); partSim.isSepratron = partSim.IsSepratron();
partSim.inverseStage = partSim.part.inverseStage; partSim.inverseStage = partSim.part.inverseStage;
//MonoBehaviour.print("inverseStage = " + inverseStage); //MonoBehaviour.print("inverseStage = " + inverseStage);
   
partSim.cost = partSim.part.GetCostWet(); partSim.cost = partSim.part.GetCostWet();
   
// Work out if the part should have no physical significance // Work out if the part should have no physical significance
partSim.isNoPhysics = partSim.part.HasModule<LaunchClamp>() || partSim.isNoPhysics = partSim.part.physicalSignificance == Part.PhysicalSignificance.NONE ||
partSim.part.physicalSignificance == Part.PhysicalSignificance.NONE ||  
partSim.part.PhysicsSignificance == 1; partSim.part.PhysicsSignificance == 1;
   
if (!partSim.isNoPhysics) if (partSim.part.HasModule<LaunchClamp>())
  {
  if (log != null)
  log.buf.AppendLine("Ignoring mass of launch clamp");
  }
  else
{ {
partSim.baseMass = partSim.part.mass; partSim.baseMass = partSim.part.mass;
} if (log != null)
  log.buf.AppendLine("Using part.mass of " + partSim.part.mass);
if (SimManager.logOutput)  
{  
MonoBehaviour.print((partSim.isNoPhysics ? "Ignoring" : "Using") + " part.mass of " + partSim.part.mass);  
} }
   
for (int i = 0; i < partSim.part.Resources.Count; i++) for (int i = 0; i < partSim.part.Resources.Count; i++)
{ {
PartResource resource = partSim.part.Resources[i]; PartResource resource = partSim.part.Resources[i];
   
// Make sure it isn't NaN as this messes up the part mass and hence most of the values // Make sure it isn't NaN as this messes up the part mass and hence most of the values
// This can happen if a resource capacity is 0 and tweakable // This can happen if a resource capacity is 0 and tweakable
if (!Double.IsNaN(resource.amount)) if (!Double.IsNaN(resource.amount))
{ {
if (SimManager.logOutput) if (log != null)
{ log.buf.AppendLine(resource.resourceName + " = " + resource.amount);
MonoBehaviour.print(resource.resourceName + " = " + resource.amount);  
}  
   
partSim.resources.Add(resource.info.id, resource.amount); partSim.resources.Add(resource.info.id, resource.amount);
partSim.resourceFlowStates.Add(resource.info.id, resource.flowState ? 1 : 0); partSim.resourceFlowStates.Add(resource.info.id, resource.flowState ? 1 : 0);
} }
else else
{ {
MonoBehaviour.print(resource.resourceName + " is NaN. Skipping."); if (log != null)
  log.buf.AppendLine(resource.resourceName + " is NaN. Skipping.");
} }
} }
   
partSim.startMass = partSim.GetMass(-1); partSim.startMass = partSim.GetMass(-1);
   
partSim.hasVessel = (partSim.part.vessel != null); partSim.hasVessel = (partSim.part.vessel != null);
partSim.isLanded = partSim.hasVessel && partSim.part.vessel.Landed; partSim.isLanded = partSim.hasVessel && partSim.part.vessel.Landed;
if (partSim.hasVessel) if (partSim.hasVessel)
{ {
partSim.vesselName = partSim.part.vessel.vesselName; partSim.vesselName = partSim.part.vessel.vesselName;
partSim.vesselType = partSim.part.vesselType; partSim.vesselType = partSim.part.vesselType;
} }
partSim.initialVesselName = partSim.part.initialVesselName; partSim.initialVesselName = partSim.part.initialVesselName;
   
partSim.hasMultiModeEngine = partSim.part.HasModule<MultiModeEngine>(); partSim.hasMultiModeEngine = partSim.part.HasModule<MultiModeEngine>();
partSim.hasModuleEnginesFX = partSim.part.HasModule<ModuleEnginesFX>(); partSim.hasModuleEnginesFX = partSim.part.HasModule<ModuleEnginesFX>();
partSim.hasModuleEngines = partSim.part.HasModule<ModuleEngines>(); partSim.hasModuleEngines = partSim.part.HasModule<ModuleEngines>();
   
partSim.isEngine = partSim.hasMultiModeEngine || partSim.hasModuleEnginesFX || partSim.hasModuleEngines; partSim.isEngine = partSim.hasMultiModeEngine || partSim.hasModuleEnginesFX || partSim.hasModuleEngines;
   
if (SimManager.logOutput) if (log != null)
{ log.buf.AppendLine("Created " + partSim.name + ". Decoupled in stage " + partSim.decoupledInStage);
MonoBehaviour.print("Created " + partSim.name + ". Decoupled in stage " + partSim.decoupledInStage);  
}  
return partSim; return partSim;
} }
   
public ResourceContainer ResourceDrains public ResourceContainer ResourceDrains
{ {
get get
{ {
return resourceDrains; return resourceDrains;
} }
} }
   
public ResourceContainer Resources public ResourceContainer Resources
{ {
get get
{ {
return resources; return resources;
} }
} }
   
public void CreateEngineSims(List<EngineSim> allEngines, double atmosphere, double mach, bool vectoredThrust, bool fullThrust, LogMsg log) public void CreateEngineSims(List<EngineSim> allEngines, double atmosphere, double mach, bool vectoredThrust, bool fullThrust, LogMsg log)
{ {
bool correctThrust = SimManager.DoesEngineUseCorrectedThrust(part); bool correctThrust = SimManager.DoesEngineUseCorrectedThrust(part);
if (log != null) if (log != null)
{ {
log.buf.AppendLine("CreateEngineSims for " + this.name); log.buf.AppendLine("CreateEngineSims for " + this.name);
for (int i = 0; i < this.part.Modules.Count; i++) for (int i = 0; i < this.part.Modules.Count; i++)
{ {
PartModule partMod = this.part.Modules[i]; PartModule partMod = this.part.Modules[i];
log.buf.AppendLine("Module: " + partMod.moduleName); log.buf.AppendLine("Module: " + partMod.moduleName);
} }
   
log.buf.AppendLine("correctThrust = " + correctThrust); log.buf.AppendLine("correctThrust = " + correctThrust);
} }
   
if (hasMultiModeEngine) if (hasMultiModeEngine)
{ {
// A multi-mode engine has multiple ModuleEnginesFX but only one is active at any point // A multi-mode engine has multiple ModuleEnginesFX but only one is active at any point
// The mode of the engine is the engineID of the ModuleEnginesFX that is active // The mode of the engine is the engineID of the ModuleEnginesFX that is active
string mode = part.GetModule<MultiModeEngine>().mode; string mode = part.GetModule<MultiModeEngine>().mode;
   
List<ModuleEnginesFX> engines = part.GetModules<ModuleEnginesFX>(); List<ModuleEnginesFX> engines = part.GetModules<ModuleEnginesFX>();
for (int i = 0; i < engines.Count; ++i) for (int i = 0; i < engines.Count; ++i)
{ {
ModuleEnginesFX engine = engines[i]; ModuleEnginesFX engine = engines[i];
if (engine.engineID == mode) if (engine.engineID == mode)
{ {
if (log != null) if (log != null)
{ {
log.buf.AppendLine("Module: " + engine.moduleName); log.buf.AppendLine("Module: " + engine.moduleName);
} }
   
Vector3 thrustvec = this.CalculateThrustVector(vectoredThrust ? engine.thrustTransforms : null, log); Vector3 thrustvec = this.CalculateThrustVector(vectoredThrust ? engine.thrustTransforms : null, log);
   
EngineSim engineSim = EngineSim.New( EngineSim engineSim = EngineSim.New(
this, this,
atmosphere, atmosphere,
(float)mach, (float)mach,
engine.maxFuelFlow, engine.maxFuelFlow,
engine.minFuelFlow, engine.minFuelFlow,
engine.thrustPercentage, engine.thrustPercentage,
thrustvec, thrustvec,
engine.atmosphereCurve, engine.atmosphereCurve,
engine.atmChangeFlow, engine.atmChangeFlow,
engine.useAtmCurve ? engine.atmCurve : null, engine.useAtmCurve ? engine.atmCurve : null,
engine.useVelCurve ? engine.velCurve : null, engine.useVelCurve ? engine.velCurve : null,
engine.currentThrottle, engine.currentThrottle,
engine.g, engine.g,
engine.throttleLocked || fullThrust, engine.throttleLocked || fullThrust,
engine.propellants, engine.propellants,
engine.isOperational, engine.isOperational,
engine.resultingThrust, engine.resultingThrust,
engine.thrustTransforms); engine.thrustTransforms);
allEngines.Add(engineSim); allEngines.Add(engineSim);
} }
} }
} }
else else
{ {
if (hasModuleEngines) if (hasModuleEngines)
{ {
List<ModuleEngines> engines = part.GetModules<ModuleEngines>(); List<ModuleEngines> engines = part.GetModules<ModuleEngines>();
for (int i = 0; i < engines.Count; ++i) for (int i = 0; i < engines.Count; ++i)
{ {
ModuleEngines engine = engines[i]; ModuleEngines engine = engines[i];
if (log != null) if (log != null)
{ {
log.buf.AppendLine("Module: " + engine.moduleName); log.buf.AppendLine("Module: " + engine.moduleName);
} }
   
Vector3 thrustvec = this.CalculateThrustVector(vectoredThrust ? engine.thrustTransforms : null, log); Vector3 thrustvec = this.CalculateThrustVector(vectoredThrust ? engine.thrustTransforms : null, log);
   
EngineSim engineSim = EngineSim.New( EngineSim engineSim = EngineSim.New(
this, this,
atmosphere, atmosphere,
(float)mach, (float)mach,
engine.maxFuelFlow, engine.maxFuelFlow,
engine.minFuelFlow, engine.minFuelFlow,
engine.thrustPercentage, engine.thrustPercentage,
thrustvec, thrustvec,
engine.atmosphereCurve, engine.atmosphereCurve,
engine.atmChangeFlow, engine.atmChangeFlow,
engine.useAtmCurve ? engine.atmCurve : null, engine.useAtmCurve ? engine.atmCurve : null,
engine.useVelCurve ? engine.velCurve : null, engine.useVelCurve ? engine.velCurve : null,
engine.currentThrottle, engine.currentThrottle,
engine.g, engine.g,
engine.throttleLocked || fullThrust, engine.throttleLocked || fullThrust,
engine.propellants, engine.propellants,
engine.isOperational, engine.isOperational,
engine.resultingThrust, engine.resultingThrust,
engine.thrustTransforms); engine.thrustTransforms);
allEngines.Add(engineSim); allEngines.Add(engineSim);
} }
} }
} }
   
if (log != null) if (log != null)
{ {
log.Flush(); log.Flush();
} }
} }
   
public int DecouplerCount() public int DecouplerCount()
{ {
int count = 0; int count = 0;
PartSim partSim = this; PartSim partSim = this;
while (partSim != null) while (partSim != null)
{ {
if (partSim.isDecoupler) if (partSim.isDecoupler)
{ {
count++; count++;
} }
   
partSim = partSim.parent; partSim = partSim.parent;
} }
return count; return count;
} }
   
public void DrainResources(double time) public void DrainResources(double time)
{ {
//MonoBehaviour.print("DrainResources(" + name + ":" + partId + ", " + time + ")"); //MonoBehaviour.print("DrainResources(" + name + ":" + partId + ", " + time + ")");
for (int i = 0; i < resourceDrains.Types.Count; ++i) for (int i = 0; i < resourceDrains.Types.Count; ++i)
{ {
int type = resourceDrains.Types[i]; int type = resourceDrains.Types[i];
   
//MonoBehaviour.print("draining " + (time * resourceDrains[type]) + " " + ResourceContainer.GetResourceName(type)); //MonoBehaviour.print("draining " + (time * resourceDrains[type]) + " " + ResourceContainer.GetResourceName(type));
resources.Add(type, -time * resourceDrains[type]); resources.Add(type, -time * resourceDrains[type]);
//MonoBehaviour.print(ResourceContainer.GetResourceName(type) + " left = " + resources[type]); //MonoBehaviour.print(ResourceContainer.GetResourceName(type) + " left = " + resources[type]);
} }
} }
   
public String DumpPartAndParentsToBuffer(StringBuilder buffer, String prefix) public String DumpPartAndParentsToBuffer(StringBuilder buffer, String prefix)
{ {
if (parent != null) if (parent != null)
{ {
prefix = parent.DumpPartAndParentsToBuffer(buffer, prefix) + " "; prefix = parent.DumpPartAndParentsToBuffer(buffer, prefix) + " ";
} }
   
DumpPartToBuffer(buffer, prefix); DumpPartToBuffer(buffer, prefix);
   
return prefix; return prefix;
} }
   
public void DumpPartToBuffer(StringBuilder buffer, String prefix, List<PartSim> allParts = null) public void DumpPartToBuffer(StringBuilder buffer, String prefix, List<PartSim> allParts = null)
{ {
buffer.Append(prefix); buffer.Append(prefix);
buffer.Append(name); buffer.Append(name);
buffer.AppendFormat(":[id = {0:d}, decouple = {1:d}, invstage = {2:d}", partId, decoupledInStage, inverseStage); buffer.AppendFormat(":[id = {0:d}, decouple = {1:d}, invstage = {2:d}", partId, decoupledInStage, inverseStage);
   
buffer.AppendFormat(", vesselName = '{0}'", vesselName); buffer.AppendFormat(", vesselName = '{0}'", vesselName);
buffer.AppendFormat(", vesselType = {0}", SimManager.GetVesselTypeString(vesselType)); buffer.AppendFormat(", vesselType = {0}", SimManager.GetVesselTypeString(vesselType));
buffer.AppendFormat(", initialVesselName = '{0}'", initialVesselName); buffer.AppendFormat(", initialVesselName = '{0}'", initialVesselName);
   
buffer.AppendFormat(", fuelCF = {0}", fuelCrossFeed); buffer.AppendFormat(", fuelCF = {0}", fuelCrossFeed);
buffer.AppendFormat(", noCFNKey = '{0}'", noCrossFeedNodeKey); buffer.AppendFormat(", noCFNKey = '{0}'", noCrossFeedNodeKey);
   
buffer.AppendFormat(", isSep = {0}", isSepratron); buffer.AppendFormat(", isSep = {0}", isSepratron);
   
foreach (int type in resources.Types) for (int i = 0; i < resources.Types.Count; i++)
{ {
  int type = resources.Types[i];
buffer.AppendFormat(", {0} = {1:g6}", ResourceContainer.GetResourceName(type), resources[type]); buffer.AppendFormat(", {0} = {1:g6}", ResourceContainer.GetResourceName(type), resources[type]);
} }
   
if (attachNodes.Count > 0) if (attachNodes.Count > 0)
{ {
buffer.Append(", attached = <"); buffer.Append(", attached = <");
attachNodes[0].DumpToBuffer(buffer); attachNodes[0].DumpToBuffer(buffer);
for (int i = 1; i < attachNodes.Count; i++) for (int i = 1; i < attachNodes.Count; i++)
{ {
buffer.Append(", "); buffer.Append(", ");
attachNodes[i].DumpToBuffer(buffer); attachNodes[i].DumpToBuffer(buffer);
} }
buffer.Append(">"); buffer.Append(">");
} }
   
// Add more info here // Add more info here
   
buffer.Append("]\n"); buffer.Append("]\n");
   
if (allParts != null) if (allParts != null)
{ {
String newPrefix = prefix + " "; String newPrefix = prefix + " ";
foreach (PartSim partSim in allParts) for (int i = 0; i < allParts.Count; i++)
{ {
  PartSim partSim = allParts[i];
if (partSim.parent == this) if (partSim.parent == this)
{ {
partSim.DumpPartToBuffer(buffer, newPrefix, allParts); partSim.DumpPartToBuffer(buffer, newPrefix, allParts);
} }
} }
} }
} }
   
public bool EmptyOf(HashSet<int> types) public bool EmptyOf(HashSet<int> types)
{ {
foreach (int type in types) foreach (int type in types)
{ {
if (resources.HasType(type) && resourceFlowStates[type] != 0 && resources[type] > SimManager.RESOURCE_MIN) if (resources.HasType(type) && resourceFlowStates[type] != 0 && resources[type] > SimManager.RESOURCE_PART_EMPTY_THRESH)
{ {
return false; return false;
} }
} }
   
return true; return true;
} }
   
public double GetMass(int currentStage) public double GetMass(int currentStage)
{ {
double mass = baseMass; double mass = baseMass;
   
for (int i = 0; i < resources.Types.Count; ++i) for (int i = 0; i < resources.Types.Count; ++i)
{ {
mass += resources.GetResourceMass(resources.Types[i]); mass += resources.GetResourceMass(resources.Types[i]);
} }
   
if (hasVessel == false && isFairing && inverseStage < currentStage) if (hasVessel == false && isFairing && inverseStage < currentStage)
{ {
mass = mass + moduleMass; mass = mass + moduleMass;
} }
   
return mass; return mass;
} }
public void ReleasePart() public void ReleasePart()
{ {
this.part = null; this.part = null;
} }
   
// All functions below this point must not rely on the part member (it may be null) // All functions below this point must not rely on the part member (it may be null)
// //
   
public void GetSourceSet(int type, List<PartSim> allParts, HashSet<PartSim> visited, HashSet<PartSim> allSources, LogMsg log, String indent) public void GetSourceSet(int type, List<PartSim> allParts, HashSet<PartSim> visited, HashSet<PartSim> allSources, LogMsg log, String indent)
{ {
if (log != null) if (log != null)
{ {
log.buf.AppendLine(indent + "GetSourceSet(" + ResourceContainer.GetResourceName(type) + ") for " + name + ":" + partId); log.buf.AppendLine(indent + "GetSourceSet(" + ResourceContainer.GetResourceName(type) + ") for " + name + ":" + partId);
indent += " "; indent += " ";
} }
   
// Rule 1: Each part can be only visited once, If it is visited for second time in particular search it returns as is. // Rule 1: Each part can be only visited once, If it is visited for second time in particular search it returns as is.
if (visited.Contains(this)) if (visited.Contains(this))
{ {
if (log != null) if (log != null)
{  
log.buf.AppendLine(indent + "Returning empty set, already visited (" + name + ":" + partId + ")"); log.buf.AppendLine(indent + "Returning empty set, already visited (" + name + ":" + partId + ")");
}  
   
return; return;
} }
   
//if (log != null) if (log != null)
// log.buf.AppendLine(indent + "Adding this to visited"); log.buf.AppendLine(indent + "Adding this to visited");
   
visited.Add(this); visited.Add(this);
   
// Rule 2: Part performs scan on start of every fuel pipe ending in it. This scan is done in order in which pipes were installed. // Rule 2: Part performs scan on start of every fuel pipe ending in it. This scan is done in order in which pipes were installed.
// Then it makes an union of fuel tank sets each pipe scan returned. If the resulting list is not empty, it is returned as result. // Then it makes an union of fuel tank sets each pipe scan returned. If the resulting list is not empty, it is returned as result.
//MonoBehaviour.print("foreach fuel line"); //MonoBehaviour.print("for each fuel line");
   
int lastCount = allSources.Count; int lastCount = allSources.Count;
   
for (int i = 0; i < this.fuelTargets.Count; i++) for (int i = 0; i < this.fuelTargets.Count; i++)
{ {
PartSim partSim = this.fuelTargets[i]; PartSim partSim = this.fuelTargets[i];
if (visited.Contains(partSim)) if (partSim != null)
{ {
//if (log != null) if (visited.Contains(partSim))
// log.buf.AppendLine(indent + "Fuel target already visited, skipping (" + partSim.name + ":" + partSim.partId + ")"); {
} if (log != null)
else log.buf.AppendLine(indent + "Fuel target already visited, skipping (" + partSim.name + ":" + partSim.partId + ")");
{ }
//if (log != null) else
// log.buf.AppendLine(indent + "Adding fuel target as source (" + partSim.name + ":" + partSim.partId + ")"); {
  if (log != null)
partSim.GetSourceSet(type, allParts, visited, allSources, log, indent); log.buf.AppendLine(indent + "Adding fuel target as source (" + partSim.name + ":" + partSim.partId + ")");
   
  partSim.GetSourceSet(type, allParts, visited, allSources, log, indent);
  }
} }
} }
   
if (allSources.Count > lastCount) if (allSources.Count > lastCount)
{ {
if (log != null) if (log != null)
{  
log.buf.AppendLine(indent + "Returning " + (allSources.Count - lastCount) + " fuel target sources (" + this.name + ":" + this.partId + ")"); log.buf.AppendLine(indent + "Returning " + (allSources.Count - lastCount) + " fuel target sources (" + this.name + ":" + this.partId + ")");
}  
   
return; return;
} }
   
   
// Rule 3: This rule has been removed and merged with rules 4 and 7 to fix issue with fuel tanks with disabled crossfeed // Rule 3: This rule has been removed and merged with rules 4 and 7 to fix issue with fuel tanks with disabled crossfeed
   
// Rule 4: Part performs scan on each of its axially mounted neighbors. // Rule 4: Part performs scan on each of its axially mounted neighbors.
// Couplers (bicoupler, tricoupler, ...) are an exception, they only scan one attach point on the single attachment side, // Couplers (bicoupler, tricoupler, ...) are an exception, they only scan one attach point on the single attachment side,
// skip the points on the side where multiple points are. [Experiment] // skip the points on the side where multiple points are. [Experiment]
// Again, the part creates union of scan lists from each of its neighbor and if it is not empty, returns this list. // Again, the part creates union of scan lists from each of its neighbor and if it is not empty, returns this list.
// The order in which mount points of a part are scanned appears to be fixed and defined by the part specification file. [Experiment] // The order in which mount points of a part are scanned appears to be fixed and defined by the part specification file. [Experiment]
if (fuelCrossFeed) if (fuelCrossFeed)
{ {
lastCount = allSources.Count; lastCount = allSources.Count;
//MonoBehaviour.print("foreach attach node"); //MonoBehaviour.print("for each attach node");
for (int i = 0; i < this.attachNodes.Count; i++) for (int i = 0; i < this.attachNodes.Count; i++)
{ {
AttachNodeSim attachSim = this.attachNodes[i]; AttachNodeSim attachSim = this.attachNodes[i];
if (attachSim.attachedPartSim != null) if (attachSim.attachedPartSim != null)
{ {
if (attachSim.nodeType == AttachNode.NodeType.Stack) if (attachSim.nodeType == AttachNode.NodeType.Stack)
{ {
if ( if (
!(this.noCrossFeedNodeKey != null && this.noCrossFeedNodeKey.Length > 0 && !(this.noCrossFeedNodeKey != null && this.noCrossFeedNodeKey.Length > 0 &&
attachSim.id.Contains(this.noCrossFeedNodeKey))) attachSim.id.Contains(this.noCrossFeedNodeKey)))
{ {
if (visited.Contains(attachSim.attachedPartSim)) if (visited.Contains(attachSim.attachedPartSim))
{ {
//if (log != null) if (log != null)
// log.buf.AppendLine(indent + "Attached part already visited, skipping (" + attachSim.attachedPartSim.name + ":" + attachSim.attachedPartSim.partId + ")"); log.buf.AppendLine(indent + "Attached part already visited, skipping (" + attachSim.attachedPartSim.name + ":" + attachSim.attachedPartSim.partId + ")");
} }
else else
{ {
//if (log != null) if (log != null)
// log.buf.AppendLine(indent + "Adding attached part as source (" + attachSim.attachedPartSim.name + ":" + attachSim.attachedPartSim.partId + ")"); log.buf.AppendLine(indent + "Adding attached part as source (" + attachSim.attachedPartSim.name + ":" + attachSim.attachedPartSim.partId + ")");
   
attachSim.attachedPartSim.GetSourceSet(type, allParts, visited, allSources, log, indent); attachSim.attachedPartSim.GetSourceSet(type, allParts, visited, allSources, log, indent);
} }
} }
} }
} }
} }
   
if (allSources.Count > lastCount) if (allSources.Count > lastCount)
{ {
if (log != null) if (log != null)
{  
log.buf.AppendLine(indent + "Returning " + (allSources.Count - lastCount) + " attached sources (" + this.name + ":" + this.partId + ")"); log.buf.AppendLine(indent + "Returning " + (allSources.Count - lastCount) + " attached sources (" + this.name + ":" + this.partId + ")");
}  
   
return; return;
} }
} }
   
// Rule 5: If the part is fuel container for searched type of fuel (i.e. it has capability to contain that type of fuel and the fuel // Rule 5: If the part is fuel container for searched type of fuel (i.e. it has capability to contain that type of fuel and the fuel
// type was not disabled [Experiment]) and it contains fuel, it returns itself. // type was not disabled [Experiment]) and it contains fuel, it returns itself.
// Rule 6: If the part is fuel container for searched type of fuel (i.e. it has capability to contain that type of fuel and the fuel // Rule 6: If the part is fuel container for searched type of fuel (i.e. it has capability to contain that type of fuel and the fuel
// type was not disabled) but it does not contain the requested fuel, it returns empty list. [Experiment] // type was not disabled) but it does not contain the requested fuel, it returns empty list. [Experiment]
if (resources.HasType(type) && resourceFlowStates[type] != 0) if (resources.HasType(type) && resourceFlowStates[type] != 0)
{ {
if (resources[type] > SimManager.RESOURCE_MIN) if (resources[type] > SimManager.RESOURCE_MIN)
{ {
allSources.Add(this); allSources.Add(this);
   
if (log != null) if (log != null)
{  
log.buf.AppendLine(indent + "Returning enabled tank as only source (" + name + ":" + partId + ")"); log.buf.AppendLine(indent + "Returning enabled tank as only source (" + name + ":" + partId + ")");
}  
} }
   
return; return;
  }
  else
  {
  if (log != null)
  log.buf.AppendLine(indent + "Not fuel tank or disabled. HasType = " + resources.HasType(type) + " FlowState = " + resourceFlowStates[type]);
} }
   
// Rule 7: If the part is radially attached to another part and it is child of that part in the ship's tree structure, it scans its // Rule 7: If the part is radially attached to another part and it is child of that part in the ship's tree structure, it scans its
// parent and returns whatever the parent scan returned. [Experiment] [Experiment] // parent and returns whatever the parent scan returned. [Experiment] [Experiment]
if (parent != null && parentAttach == AttachModes.SRF_ATTACH) if (parent != null && parentAttach == AttachModes.SRF_ATTACH)
{ {
if (fuelCrossFeed) if (fuelCrossFeed)
{ {
if (visited.Contains(parent)) if (visited.Contains(parent))
{ {
//if (log != null) if (log != null)
// log.buf.AppendLine(indent + "Parent part already visited, skipping (" + parent.name + ":" + parent.partId + ")"); log.buf.AppendLine(indent + "Parent part already visited, skipping (" + parent.name + ":" + parent.partId + ")");
} }
else else
{ {
lastCount = allSources.Count; lastCount = allSources.Count;
this.parent.GetSourceSet(type, allParts, visited, allSources, log, indent); this.parent.GetSourceSet(type, allParts, visited, allSources, log, indent);
if (allSources.Count > lastCount) if (allSources.Count > lastCount)
{ {
if (log != null) if (log != null)
{  
log.buf.AppendLine(indent + "Returning " + (allSources.Count - lastCount) + " parent sources (" + this.name + ":" + this.partId + ")"); log.buf.AppendLine(indent + "Returning " + (allSources.Count - lastCount) + " parent sources (" + this.name + ":" + this.partId + ")");
}  
   
return; return;
} }
} }
} }
} }
   
// Rule 8: If all preceding rules failed, part returns empty list. // Rule 8: If all preceding rules failed, part returns empty list.
//if (log != null) if (log != null)
// log.buf.AppendLine(indent + "Returning empty set, no sources found (" + name + ":" + partId + ")"); log.buf.AppendLine(indent + "Returning empty set, no sources found (" + name + ":" + partId + ")");
   
return; return;
} }
   
public double GetStartMass() public double GetStartMass()
{ {
return startMass; return startMass;
} }
   
public void RemoveAttachedParts(HashSet<PartSim> partSims) public void RemoveAttachedParts(HashSet<PartSim> partSims)
{ {
// Loop through the attached parts // Loop through the attached parts
for (int i = 0; i < this.attachNodes.Count; i++) for (int i = 0; i < this.attachNodes.Count; i++)
{ {
AttachNodeSim attachSim = this.attachNodes[i]; AttachNodeSim attachSim = this.attachNodes[i];
// If the part is in the set then "remove" it by clearing the PartSim reference // If the part is in the set then "remove" it by clearing the PartSim reference
if (partSims.Contains(attachSim.attachedPartSim)) if (partSims.Contains(attachSim.attachedPartSim))
{ {
attachSim.attachedPartSim = null; attachSim.attachedPartSim = null;
  }
  }
   
  // Loop through the fuel targets (fuel line sources)
  for (int i = 0; i < this.fuelTargets.Count; i++)
  {
  PartSim fuelTargetSim = this.fuelTargets[i];
  // If the part is in the set then "remove" it by clearing the PartSim reference
  if (fuelTargetSim != null && partSims.Contains(fuelTargetSim))
  {
  this.fuelTargets[i] = null;
} }
} }
} }
   
public void SetupAttachNodes(Dictionary<Part, PartSim> partSimLookup, LogMsg log) public void SetupAttachNodes(Dictionary<Part, PartSim> partSimLookup, LogMsg log)
{ {
if (log != null) if (log != null)
{ {
log.buf.AppendLine("SetupAttachNodes for " + name + ":" + partId + ""); log.buf.AppendLine("SetupAttachNodes for " + name + ":" + partId + "");
} }
   
attachNodes.Clear(); attachNodes.Clear();
   
for (int i = 0; i < part.attachNodes.Count; ++i) for (int i = 0; i < part.attachNodes.Count; ++i)
{ {
AttachNode attachNode = part.attachNodes[i]; AttachNode attachNode = part.attachNodes[i];
   
if (log != null) if (log != null)
{ {
log.buf.AppendLine("AttachNode " + attachNode.id + " = " + (attachNode.attachedPart != null ? attachNode.attachedPart.partInfo.name : "null")); log.buf.AppendLine("AttachNode " + attachNode.id + " = " + (attachNode.attachedPart != null ? attachNode.attachedPart.partInfo.name : "null"));
} }
   
if (attachNode.attachedPart != null && attachNode.id != "Strut") if (attachNode.attachedPart != null && attachNode.id != "Strut")
{ {
PartSim attachedSim; PartSim attachedSim;
if (partSimLookup.TryGetValue(attachNode.attachedPart, out attachedSim)) if (partSimLookup.TryGetValue(attachNode.attachedPart, out attachedSim))
{ {
if (log != null) if (log != null)
{ {
log.buf.AppendLine("Adding attached node " + attachedSim.name + ":" + attachedSim.partId + ""); log.buf.AppendLine("Adding attached node " + attachedSim.name + ":" + attachedSim.partId + "");
} }
   
attachNodes.Add(AttachNodeSim.New(attachedSim, attachNode.id, attachNode.nodeType)); attachNodes.Add(AttachNodeSim.New(attachedSim, attachNode.id, attachNode.nodeType));
} }
else else
{ {
if (log != null) if (log != null)
{ {
log.buf.AppendLine("No PartSim for attached part (" + attachNode.attachedPart.partInfo.name + ")"); log.buf.AppendLine("No PartSim for attached part (" + attachNode.attachedPart.partInfo.name + ")");
} }
} }
} }
} }
   
for (int i = 0; i < part.fuelLookupTargets.Count; ++i) for (int i = 0; i < part.fuelLookupTargets.Count; ++i)
{ {
Part p = part.fuelLookupTargets[i]; Part p = part.fuelLookupTargets[i];
   
if (p != null) if (p != null)
{ {
PartSim targetSim; PartSim targetSim;
if (partSimLookup.TryGetValue(p, out targetSim)) if (partSimLookup.TryGetValue(p, out targetSim))
{ {
if (log != null) if (log != null)
{ {
log.buf.AppendLine("Fuel target: " + targetSim.name + ":" + targetSim.partId); log.buf.AppendLine("Fuel target: " + targetSim.name + ":" + targetSim.partId);
} }
   
fuelTargets.Add(targetSim); fuelTargets.Add(targetSim);
} }
else else
{ {
if (log != null) if (log != null)
{ {
log.buf.AppendLine("No PartSim for fuel target (" + p.name + ")"); log.buf.AppendLine("No PartSim for fuel target (" + p.name + ")");
} }
} }
} }
} }
   
  if (isNoPhysics)
  {
  if (log != null)
  log.buf.AppendLine("Moving NoPhysics part mass of " + this.baseMass + " to parent part");
   
  // Go up the parent chain until we find a part that is physically significant or we reach the root
  PartSim MassParent = parent;
  while (MassParent.isNoPhysics && (MassParent.parent != null))
  MassParent = MassParent.parent;
   
  // Apply this part's mass to the part we have found
  MassParent.baseMass += this.baseMass;
   
  // And zero out this part's mass
  this.baseMass = 0;
  }
} }
   
public void SetupParent(Dictionary<Part, PartSim> partSimLookup, LogMsg log) public void SetupParent(Dictionary<Part, PartSim> partSimLookup, LogMsg log)
{ {
if (part.parent != null) if (part.parent != null)
{ {
parent = null; parent = null;
if (partSimLookup.TryGetValue(part.parent, out parent)) if (partSimLookup.TryGetValue(part.parent, out parent))
{ {
if (log != null) if (log != null)
{  
log.buf.AppendLine("Parent part is " + parent.name + ":" + parent.partId); log.buf.AppendLine("Parent part is " + parent.name + ":" + parent.partId);
}  
} }
else else
{ {
if (log != null) if (log != null)
{  
log.buf.AppendLine("No PartSim for parent part (" + part.parent.partInfo.name + ")"); log.buf.AppendLine("No PartSim for parent part (" + part.parent.partInfo.name + ")");
}  
} }
} }
} }
   
public double TimeToDrainResource() public double TimeToDrainResource()
{ {
//MonoBehaviour.print("TimeToDrainResource(" + name + ":" + partId + ")"); //MonoBehaviour.print("TimeToDrainResource(" + name + ":" + partId + ")");
double time = double.MaxValue; double time = double.MaxValue;
   
for (int i = 0; i < resourceDrains.Types.Count; ++i) for (int i = 0; i < resourceDrains.Types.Count; ++i)
{ {
int type = resourceDrains.Types[i]; int type = resourceDrains.Types[i];
   
if (resourceDrains[type] > 0) if (resourceDrains[type] > 0)
{ {
time = Math.Min(time, resources[type] / resourceDrains[type]); time = Math.Min(time, resources[type] / resourceDrains[type]);
//MonoBehaviour.print("type = " + ResourceContainer.GetResourceName(type) + " amount = " + resources[type] + " rate = " + resourceDrains[type] + " time = " + time); //MonoBehaviour.print("type = " + ResourceContainer.GetResourceName(type) + " amount = " + resources[type] + " rate = " + resourceDrains[type] + " time = " + time);
} }
} }
   
//if (time < double.MaxValue) //if (time < double.MaxValue)
// MonoBehaviour.print("TimeToDrainResource(" + name + ":" + partId + ") = " + time); // MonoBehaviour.print("TimeToDrainResource(" + name + ":" + partId + ") = " + time);
return time; return time;
} }
   
private Vector3 CalculateThrustVector(List<Transform> thrustTransforms, LogMsg log) private Vector3 CalculateThrustVector(List<Transform> thrustTransforms, LogMsg log)
{ {
if (thrustTransforms == null) if (thrustTransforms == null)
{ {
return Vector3.forward; return Vector3.forward;
} }
   
Vector3 thrustvec = Vector3.zero; Vector3 thrustvec = Vector3.zero;
for (int i = 0; i < thrustTransforms.Count; ++i) for (int i = 0; i < thrustTransforms.Count; ++i)
{ {
Transform trans = thrustTransforms[i]; Transform trans = thrustTransforms[i];
   
if (log != null) if (log != null)
{ {
log.buf.AppendFormat("Transform = ({0:g6}, {1:g6}, {2:g6}) length = {3:g6}\n", trans.forward.x, trans.forward.y, trans.forward.z, trans.forward.magnitude); log.buf.AppendFormat("Transform = ({0:g6}, {1:g6}, {2:g6}) length = {3:g6}\n", trans.forward.x, trans.forward.y, trans.forward.z, trans.forward.magnitude);
} }
   
thrustvec -= trans.forward; thrustvec -= trans.forward;
} }
   
if (log != null) if (log != null)
{ {
log.buf.AppendFormat("ThrustVec = ({0:g6}, {1:g6}, {2:g6}) length = {3:g6}\n", thrustvec.x, thrustvec.y, thrustvec.z, thrustvec.magnitude); log.buf.AppendFormat("ThrustVec = ({0:g6}, {1:g6}, {2:g6}) length = {3:g6}\n", thrustvec.x, thrustvec.y, thrustvec.z, thrustvec.magnitude);
} }
   
thrustvec.Normalize(); thrustvec.Normalize();
   
if (log != null) if (log != null)
{ {
log.buf.AppendFormat("ThrustVecN = ({0:g6}, {1:g6}, {2:g6}) length = {3:g6}\n", thrustvec.x, thrustvec.y, thrustvec.z, thrustvec.magnitude); log.buf.AppendFormat("ThrustVecN = ({0:g6}, {1:g6}, {2:g6}) length = {3:g6}\n", thrustvec.x, thrustvec.y, thrustvec.z, thrustvec.magnitude);
} }
   
return thrustvec; return thrustvec;
} }
   
private int DecoupledInStage(Part thePart, int stage = -1) private int DecoupledInStage(Part thePart, int stage = -1)
{ {
if (IsDecoupler(thePart)) if (IsDecoupler(thePart))
{ {
if (thePart.inverseStage > stage) if (thePart.inverseStage > stage)
{ {
stage = thePart.inverseStage; stage = thePart.inverseStage;
} }
} }
   
if (thePart.parent != null) if (thePart.parent != null)
{ {
stage = DecoupledInStage(thePart.parent, stage); stage = DecoupledInStage(thePart.parent, stage);
} }
   
return stage; return stage;
} }
   
private bool IsActiveDecoupler(Part thePart) private bool IsActiveDecoupler(Part thePart)
{ {
return thePart.FindModulesImplementing<ModuleDecouple>().Any(mod => !mod.isDecoupled) || return thePart.FindModulesImplementing<ModuleDecouple>().Any(mod => !mod.isDecoupled) ||
thePart.FindModulesImplementing<ModuleAnchoredDecoupler>().Any(mod => !mod.isDecoupled); thePart.FindModulesImplementing<ModuleAnchoredDecoupler>().Any(mod => !mod.isDecoupled);
} }
   
private bool IsDecoupler(Part thePart) private bool IsDecoupler(Part thePart)
{ {
return thePart.HasModule<ModuleDecouple>() || return thePart.HasModule<ModuleDecouple>() ||
thePart.HasModule<ModuleAnchoredDecoupler>(); thePart.HasModule<ModuleAnchoredDecoupler>();
} }
   
private bool IsFairing(Part thePart) private bool IsFairing(Part thePart)
{ {
return thePart.HasModule<ModuleProceduralFairing>(); return thePart.HasModule<ModuleProceduralFairing>();
} }
   
private bool IsSepratron() private bool IsSepratron()
{ {
if (!part.ActivatesEvenIfDisconnected) if (!part.ActivatesEvenIfDisconnected)
{ {
return false; return false;
} }
   
if (part is SolidRocket) if (part is SolidRocket)
{ {
return true; return true;
} }
   
IEnumerable<ModuleEngines> modList = part.Modules.OfType<ModuleEngines>(); IEnumerable<ModuleEngines> modList = part.Modules.OfType<ModuleEngines>();
if (modList.Count() == 0) if (modList.Count() == 0)
{ {
return false; return false;
} }
   
if (modList.First().throttleLocked) if (modList.First().throttleLocked)
{ {
return true; return true;
} }
   
return false; return false;
} }
} }
} }
// //
// 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/>.
// //
   
namespace KerbalEngineer.VesselSimulator namespace KerbalEngineer.VesselSimulator
{ {
#region Using Directives #region Using Directives
   
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Reflection; using System.Reflection;
using System.Threading; using System.Threading;
using UnityEngine; using UnityEngine;
   
#endregion #endregion
   
public class SimManager public class SimManager
{ {
#region Constants #region Constants
   
public const double RESOURCE_MIN = 0.0001; public const double RESOURCE_MIN = 0.0001;
  public const double RESOURCE_PART_EMPTY_THRESH = 0.01;
   
#endregion #endregion
   
#region Fields #region Fields
   
public static bool dumpTree = false; public static bool dumpTree = false;
public static bool logOutput = false; public static bool logOutput = false;
public static TimeSpan minSimTime = new TimeSpan(0, 0, 0, 0, 150); public static TimeSpan minSimTime = new TimeSpan(0, 0, 0, 0, 150);
public static bool vectoredThrust = false; public static bool vectoredThrust = false;
private static readonly object locker = new object(); private static readonly object locker = new object();
private static readonly Stopwatch timer = new Stopwatch(); private static readonly Stopwatch timer = new Stopwatch();
   
private static bool bRequested; private static bool bRequested;
private static bool bRunning; private static bool bRunning;
private static TimeSpan delayBetweenSims; private static TimeSpan delayBetweenSims;
   
// Support for RealFuels using reflection to check localCorrectThrust without dependency // Support for RealFuels using reflection to check localCorrectThrust without dependency
   
private static bool hasCheckedForMods; private static bool hasCheckedForMods;
private static bool hasInstalledRealFuels; private static bool hasInstalledRealFuels;
private static FieldInfo RF_ModuleEngineConfigs_localCorrectThrust; private static FieldInfo RF_ModuleEngineConfigs_localCorrectThrust;
private static FieldInfo RF_ModuleHybridEngine_localCorrectThrust; private static FieldInfo RF_ModuleHybridEngine_localCorrectThrust;
private static FieldInfo RF_ModuleHybridEngines_localCorrectThrust; private static FieldInfo RF_ModuleHybridEngines_localCorrectThrust;
private static bool hasInstalledKIDS; private static bool hasInstalledKIDS;
private static MethodInfo KIDS_Utils_GetIspMultiplier; private static MethodInfo KIDS_Utils_GetIspMultiplier;
private static bool bKIDSThrustISP = false; private static bool bKIDSThrustISP = false;
private static List<Part> parts = new List<Part>(); private static List<Part> parts = new List<Part>();
   
private static Simulation simulation = new Simulation(); private static Simulation simulation = new Simulation();
#endregion #endregion
   
#region Delegates #region Delegates
   
public delegate void ReadyEvent(); public delegate void ReadyEvent();
   
#endregion #endregion
   
#region Events #region Events
   
public static event ReadyEvent OnReady; public static event ReadyEvent OnReady;
   
#endregion #endregion
   
#region Properties #region Properties
   
public static double Atmosphere { get; set; } public static double Atmosphere { get; set; }
   
public static double Gravity { get; set; } public static double Gravity { get; set; }
   
public static Stage LastStage { get; private set; } public static Stage LastStage { get; private set; }
   
public static Stage[] Stages { get; private set; } public static Stage[] Stages { get; private set; }
   
public static double Mach { get; set; } public static double Mach { get; set; }
   
public static String failMessage { get; private set; } public static String failMessage { get; private set; }
   
#endregion #endregion
   
#region Methods #region Methods
   
private static void CheckForMods() private static void CheckForMods()
{ {
hasCheckedForMods = true; hasCheckedForMods = true;
   
foreach (var assembly in AssemblyLoader.loadedAssemblies) foreach (var assembly in AssemblyLoader.loadedAssemblies)
{ {
MonoBehaviour.print("Assembly:" + assembly.assembly); MonoBehaviour.print("Assembly:" + assembly.assembly);
   
var name = assembly.assembly.ToString().Split(',')[0]; var name = assembly.assembly.ToString().Split(',')[0];
   
if (name == "RealFuels") if (name == "RealFuels")
{ {
MonoBehaviour.print("Found RealFuels mod"); MonoBehaviour.print("Found RealFuels mod");
   
var RF_ModuleEngineConfigs_Type = assembly.assembly.GetType("RealFuels.ModuleEngineConfigs"); var RF_ModuleEngineConfigs_Type = assembly.assembly.GetType("RealFuels.ModuleEngineConfigs");
if (RF_ModuleEngineConfigs_Type != null) if (RF_ModuleEngineConfigs_Type != null)
{ {
RF_ModuleEngineConfigs_localCorrectThrust = RF_ModuleEngineConfigs_Type.GetField("localCorrectThrust"); RF_ModuleEngineConfigs_localCorrectThrust = RF_ModuleEngineConfigs_Type.GetField("localCorrectThrust");
} }
   
var RF_ModuleHybridEngine_Type = assembly.assembly.GetType("RealFuels.ModuleHybridEngine"); var RF_ModuleHybridEngine_Type = assembly.assembly.GetType("RealFuels.ModuleHybridEngine");
if (RF_ModuleHybridEngine_Type != null) if (RF_ModuleHybridEngine_Type != null)
{ {
RF_ModuleHybridEngine_localCorrectThrust = RF_ModuleHybridEngine_Type.GetField("localCorrectThrust"); RF_ModuleHybridEngine_localCorrectThrust = RF_ModuleHybridEngine_Type.GetField("localCorrectThrust");
} }
   
var RF_ModuleHybridEngines_Type = assembly.assembly.GetType("RealFuels.ModuleHybridEngines"); var RF_ModuleHybridEngines_Type = assembly.assembly.GetType("RealFuels.ModuleHybridEngines");
if (RF_ModuleHybridEngines_Type != null) if (RF_ModuleHybridEngines_Type != null)
{ {
RF_ModuleHybridEngines_localCorrectThrust = RF_ModuleHybridEngines_Type.GetField("localCorrectThrust"); RF_ModuleHybridEngines_localCorrectThrust = RF_ModuleHybridEngines_Type.GetField("localCorrectThrust");
} }
   
hasInstalledRealFuels = true; hasInstalledRealFuels = true;
break; break;
} }
else if (name == "KerbalIspDifficultyScaler") else if (name == "KerbalIspDifficultyScaler")
{ {
var KIDS_Utils_Type = assembly.assembly.GetType("KerbalIspDifficultyScaler.KerbalIspDifficultyScalerUtils"); var KIDS_Utils_Type = assembly.assembly.GetType("KerbalIspDifficultyScaler.KerbalIspDifficultyScalerUtils");
if (KIDS_Utils_Type != null) if (KIDS_Utils_Type != null)
{ {
KIDS_Utils_GetIspMultiplier = KIDS_Utils_Type.GetMethod("GetIspMultiplier"); KIDS_Utils_GetIspMultiplier = KIDS_Utils_Type.GetMethod("GetIspMultiplier");
} }
   
hasInstalledKIDS = true; hasInstalledKIDS = true;
} }
} }
} }
   
public static bool DoesEngineUseCorrectedThrust(Part theEngine) public static bool DoesEngineUseCorrectedThrust(Part theEngine)
{ {
if (hasInstalledRealFuels) if (hasInstalledRealFuels)
{ {
// Look for any of the Real Fuels engine modules and call the relevant method to find out // Look for any of the Real Fuels engine modules and call the relevant method to find out
if (RF_ModuleEngineConfigs_localCorrectThrust != null && theEngine.Modules.Contains("ModuleEngineConfigs")) if (RF_ModuleEngineConfigs_localCorrectThrust != null && theEngine.Modules.Contains("ModuleEngineConfigs"))
{ {
var modEngineConfigs = theEngine.Modules["ModuleEngineConfigs"]; var modEngineConfigs = theEngine.Modules["ModuleEngineConfigs"];
if (modEngineConfigs != null) if (modEngineConfigs != null)
{ {
// Return the localCorrectThrust // Return the localCorrectThrust
return (bool)RF_ModuleEngineConfigs_localCorrectThrust.GetValue(modEngineConfigs); return (bool)RF_ModuleEngineConfigs_localCorrectThrust.GetValue(modEngineConfigs);
} }
} }
   
if (RF_ModuleHybridEngine_localCorrectThrust != null && theEngine.Modules.Contains("ModuleHybridEngine")) if (RF_ModuleHybridEngine_localCorrectThrust != null && theEngine.Modules.Contains("ModuleHybridEngine"))
{ {
var modHybridEngine = theEngine.Modules["ModuleHybridEngine"]; var modHybridEngine = theEngine.Modules["ModuleHybridEngine"];
if (modHybridEngine != null) if (modHybridEngine != null)
{ {
// Return the localCorrectThrust // Return the localCorrectThrust
return (bool)RF_ModuleHybridEngine_localCorrectThrust.GetValue(modHybridEngine); return (bool)RF_ModuleHybridEngine_localCorrectThrust.GetValue(modHybridEngine);
} }
} }
   
if (RF_ModuleHybridEngines_localCorrectThrust != null && theEngine.Modules.Contains("ModuleHybridEngines")) if (RF_ModuleHybridEngines_localCorrectThrust != null && theEngine.Modules.Contains("ModuleHybridEngines"))
{ {
var modHybridEngines = theEngine.Modules["ModuleHybridEngines"]; var modHybridEngines = theEngine.Modules["ModuleHybridEngines"];
if (modHybridEngines != null) if (modHybridEngines != null)
{ {
// Return the localCorrectThrust // Return the localCorrectThrust
return (bool)RF_ModuleHybridEngines_localCorrectThrust.GetValue(modHybridEngines); return (bool)RF_ModuleHybridEngines_localCorrectThrust.GetValue(modHybridEngines);
} }
} }
} }
   
if (hasInstalledKIDS && HighLogic.LoadedSceneIsEditor) if (hasInstalledKIDS && HighLogic.LoadedSceneIsEditor)
{ {
return bKIDSThrustISP; return bKIDSThrustISP;
} }
   
return false; return false;
} }
   
public static void UpdateModSettings() public static void UpdateModSettings()
{ {
if (!hasCheckedForMods) if (!hasCheckedForMods)
{ {
CheckForMods(); CheckForMods();
} }
   
if (hasInstalledKIDS) if (hasInstalledKIDS)
{ {
// (out ispMultiplierVac, out ispMultiplierAtm, out extendToZeroIsp, out thrustCorrection, out ispCutoff, out thrustCutoff); // (out ispMultiplierVac, out ispMultiplierAtm, out extendToZeroIsp, out thrustCorrection, out ispCutoff, out thrustCutoff);
object[] parameters = new object[6]; object[] parameters = new object[6];
KIDS_Utils_GetIspMultiplier.Invoke(null, parameters); KIDS_Utils_GetIspMultiplier.Invoke(null, parameters);
bKIDSThrustISP = (bool)parameters[3]; bKIDSThrustISP = (bool)parameters[3];
} }
} }
   
public static String GetVesselTypeString(VesselType vesselType) public static String GetVesselTypeString(VesselType vesselType)
{ {
switch (vesselType) switch (vesselType)
{ {
case VesselType.Debris: case VesselType.Debris:
return "Debris"; return "Debris";
case VesselType.SpaceObject: case VesselType.SpaceObject:
return "SpaceObject"; return "SpaceObject";
case VesselType.Unknown: case VesselType.Unknown:
return "Unknown"; return "Unknown";
case VesselType.Probe: case VesselType.Probe:
return "Probe"; return "Probe";
case VesselType.Rover: case VesselType.Rover:
return "Rover"; return "Rover";
case VesselType.Lander: case VesselType.Lander:
return "Lander"; return "Lander";
case VesselType.Ship: case VesselType.Ship:
return "Ship"; return "Ship";
case VesselType.Station: case VesselType.Station:
return "Station"; return "Station";
case VesselType.Base: case VesselType.Base:
return "Base"; return "Base";
case VesselType.EVA: case VesselType.EVA:
return "EVA"; return "EVA";
case VesselType.Flag: case VesselType.Flag:
return "Flag"; return "Flag";
} }
return "Undefined"; return "Undefined";
} }
   
public static void RequestSimulation() public static void RequestSimulation()
{ {
if (!hasCheckedForMods) if (!hasCheckedForMods)
{ {
CheckForMods(); CheckForMods();
} }
   
lock (locker) lock (locker)
{ {
bRequested = true; bRequested = true;
if (!timer.IsRunning) if (!timer.IsRunning)
{ {
timer.Start(); timer.Start();
} }
} }
} }
   
public static bool ResultsReady() public static bool ResultsReady()
{ {
lock (locker) lock (locker)
{ {
return !bRunning; return !bRunning;
} }
} }
   
public static void TryStartSimulation() public static void TryStartSimulation()
{ {
lock (locker) lock (locker)
{ {
if (!bRequested || bRunning || (timer.Elapsed < delayBetweenSims && timer.Elapsed >= TimeSpan.Zero) || (!HighLogic.LoadedSceneIsEditor && FlightGlobals.ActiveVessel == null)) if (!bRequested || bRunning || (timer.Elapsed < delayBetweenSims && timer.Elapsed >= TimeSpan.Zero) || (!HighLogic.LoadedSceneIsEditor && FlightGlobals.ActiveVessel == null))
{ {
return; return;
} }
   
bRequested = false; bRequested = false;
timer.Reset(); timer.Reset();
} }
   
StartSimulation(); StartSimulation();
} }
   
private static void ClearResults() private static void ClearResults()
{ {
failMessage = ""; failMessage = "";
Stages = null; Stages = null;
LastStage = null; LastStage = null;
} }
   
private static void RunSimulation(object simObject) private static void RunSimulation(object simObject)
{ {
try try
{ {
Stages = (simObject as Simulation).RunSimulation(); Stages = (simObject as Simulation).RunSimulation();
   
if (Stages != null && Stages.Length > 0) if (Stages != null && Stages.Length > 0)
{ {
if (logOutput) if (logOutput)
{ {
foreach (var stage in Stages) foreach (var stage in Stages)
{ {
stage.Dump(); stage.Dump();
} }
} }
LastStage = Stages[Stages.Length - 1]; LastStage = Stages[Stages.Length - 1];
} }
} }
catch (Exception e) catch (Exception e)
{ {
Logger.Exception(e, "SimManager.RunSimulation()"); Logger.Exception(e, "SimManager.RunSimulation()");
Stages = null; Stages = null;
LastStage = null; LastStage = null;
failMessage = e.ToString(); failMessage = e.ToString();
} }
lock (locker) lock (locker)
{ {
timer.Stop(); timer.Stop();
#if TIMERS #if TIMERS
MonoBehaviour.print("Total simulation time: " + timer.ElapsedMilliseconds + "ms"); MonoBehaviour.print("Total simulation time: " + timer.ElapsedMilliseconds + "ms");
#else #else
if (logOutput) if (logOutput)
{ {
MonoBehaviour.print("Total simulation time: " + timer.ElapsedMilliseconds + "ms"); MonoBehaviour.print("Total simulation time: " + timer.ElapsedMilliseconds + "ms");
} }
#endif #endif
   
delayBetweenSims = minSimTime - timer.Elapsed; delayBetweenSims = minSimTime - timer.Elapsed;
if (delayBetweenSims < TimeSpan.Zero) if (delayBetweenSims < TimeSpan.Zero)
{ {
delayBetweenSims = TimeSpan.Zero; delayBetweenSims = TimeSpan.Zero;
} }
   
timer.Reset(); timer.Reset();
timer.Start(); timer.Start();
   
bRunning = false; bRunning = false;
if (OnReady != null) if (OnReady != null)
{ {
OnReady(); OnReady();
} }
} }
   
logOutput = false; logOutput = false;
} }
   
private static void StartSimulation() private static void StartSimulation()
{ {
try try
{ {
lock (locker) lock (locker)
{ {
bRunning = true; bRunning = true;
} }
   
ClearResults(); ClearResults();
   
lock (locker) lock (locker)
{ {
timer.Start(); timer.Start();
} }
   
if (HighLogic.LoadedSceneIsEditor) if (HighLogic.LoadedSceneIsEditor)
{ {
parts = EditorLogic.fetch.ship.parts; parts = EditorLogic.fetch.ship.parts;
} }
else else
{ {
parts = FlightGlobals.ActiveVessel.Parts; parts = FlightGlobals.ActiveVessel.Parts;
Atmosphere = FlightGlobals.ActiveVessel.staticPressurekPa * PhysicsGlobals.KpaToAtmospheres; Atmosphere = FlightGlobals.ActiveVessel.staticPressurekPa * PhysicsGlobals.KpaToAtmospheres;
} }
   
// This call doesn't ever fail at the moment but we'll check and return a sensible error for display // This call doesn't ever fail at the moment but we'll check and return a sensible error for display
if (simulation.PrepareSimulation(parts, Gravity, Atmosphere, Mach, dumpTree, vectoredThrust)) if (simulation.PrepareSimulation(parts, Gravity, Atmosphere, Mach, dumpTree, vectoredThrust))
{ {
ThreadPool.QueueUserWorkItem(RunSimulation, simulation); ThreadPool.QueueUserWorkItem(RunSimulation, simulation);
//RunSimulation(simulation); //RunSimulation(simulation);
} }
else else
{ {
failMessage = "PrepareSimulation failed"; failMessage = "PrepareSimulation failed";
lock (locker) lock (locker)
{ {
bRunning = false; bRunning = false;
} }
logOutput = false; logOutput = false;
} }
} }
catch (Exception e) catch (Exception e)
{ {
Logger.Exception(e, "SimManager.StartSimulation()"); Logger.Exception(e, "SimManager.StartSimulation()");
failMessage = e.ToString(); failMessage = e.ToString();
lock (locker) lock (locker)
{ {
bRunning = false; bRunning = false;
} }
logOutput = false; logOutput = false;
} }
dumpTree = false; dumpTree = false;
} }
   
#endregion #endregion
} }
} }