ForceAccumulator is now resettable.
ForceAccumulator is now resettable.

// //
// 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 System.Linq; using System.Linq;
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 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;
  private float maxMach;
   
#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 { return this.compactMode; } get { return this.compactMode; }
set { this.compactMode = value; } set { this.compactMode = value; }
} }
   
/// <summary> /// <summary>
/// Gets and sets whether to show all stages. /// Gets and sets whether to show all stages.
/// </summary> /// </summary>
public bool ShowAllStages public bool ShowAllStages
{ {
get { return this.showAllStages; } get { return this.showAllStages; }
set { this.showAllStages = value; } set { this.showAllStages = value; }
} }
   
/// <summary> /// <summary>
/// Gets and sets whether to use atmospheric details. /// Gets and sets whether to use atmospheric details.
/// </summary> /// </summary>
public bool ShowAtmosphericDetails public bool ShowAtmosphericDetails
{ {
get { return this.showAtmosphericDetails; } get { return this.showAtmosphericDetails; }
set { this.showAtmosphericDetails = value; } set { this.showAtmosphericDetails = value; }
} }
   
/// <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 { return this.showSettings; } get { return this.showSettings; }
set { this.showSettings = value; } set { this.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 { return this.visible; } get { return this.visible; }
set { this.visible = value; } set { this.visible = value; }
} }
   
#endregion #endregion
   
#region Methods #region Methods
   
protected void Awake() protected void Awake()
{ {
try try
{ {
Instance = this; Instance = this;
this.bodiesList = this.gameObject.AddComponent<DropDown>(); this.bodiesList = this.gameObject.AddComponent<DropDown>();
this.bodiesList.DrawCallback = this.DrawBodiesList; this.bodiesList.DrawCallback = this.DrawBodiesList;
this.Load(); this.Load();
   
SimManager.UpdateModSettings(); SimManager.UpdateModSettings();
SimManager.OnReady -= this.GetStageInfo; SimManager.OnReady -= this.GetStageInfo;
SimManager.OnReady += this.GetStageInfo; SimManager.OnReady += this.GetStageInfo;
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Exception(ex); 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
{ {
var handler = new SettingHandler(); var handler = new SettingHandler();
handler.Set("visible", this.visible); handler.Set("visible", this.visible);
handler.Set("windowPositionX", this.position.x); handler.Set("windowPositionX", this.position.x);
handler.Set("windowPositionY", this.position.y); handler.Set("windowPositionY", this.position.y);
handler.Set("compactMode", this.compactMode); handler.Set("compactMode", this.compactMode);
handler.Set("compactCollapseRight", this.compactCollapseRight); handler.Set("compactCollapseRight", this.compactCollapseRight);
handler.Set("showAllStages", this.showAllStages); handler.Set("showAllStages", this.showAllStages);
handler.Set("showAtmosphericDetails", this.showAtmosphericDetails); handler.Set("showAtmosphericDetails", this.showAtmosphericDetails);
handler.Set("showSettings", this.showSettings); handler.Set("showSettings", this.showSettings);
handler.Set("selectedBodyName", CelestialBodies.SelectedBody.Name); handler.Set("selectedBodyName", CelestialBodies.SelectedBody.Name);
handler.Save("BuildAdvanced.xml"); handler.Save("BuildAdvanced.xml");
GuiDisplaySize.OnSizeChanged -= this.OnSizeChanged; GuiDisplaySize.OnSizeChanged -= this.OnSizeChanged;
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Exception(ex); Logger.Exception(ex, "BuildAdvanced.OnDestroy()");
} }
} }
   
protected void OnGUI() protected void OnGUI()
{ {
try try
{ {
if (!this.visible || EditorLogic.fetch == null || EditorLogic.fetch.ship.parts.Count == 0 || EditorLogic.fetch.editorScreen != EditorScreen.Parts) if (!this.visible || EditorLogic.fetch == null || EditorLogic.fetch.ship.parts.Count == 0 || EditorLogic.fetch.editorScreen != EditorScreen.Parts)
{ {
return; return;
} }
   
if (this.stages == null) if (this.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.
var title = !this.compactMode ? "KERBAL ENGINEER REDUX " + EngineerGlobals.AssemblyVersion : "K.E.R. " + EngineerGlobals.AssemblyVersion + (this.showAtmosphericDetails ? " (ATMOS.)" : String.Empty); var title = !this.compactMode ? "KERBAL ENGINEER REDUX " + EngineerGlobals.AssemblyVersion : "K.E.R. " + EngineerGlobals.AssemblyVersion + (this.showAtmosphericDetails ? " (ATMOS.)" : String.Empty);
   
// Reset the window size when the staging or something else has changed. // Reset the window size when the staging or something else has changed.
var stageCount = this.stages.Count(stage => this.showAllStages || stage.deltaV > 0); var stageCount = this.stages.Count(stage => this.showAllStages || stage.deltaV > 0);
if (this.hasChanged || stageCount != this.numberOfStages) if (this.hasChanged || stageCount != this.numberOfStages)
{ {
this.hasChanged = false; this.hasChanged = false;
this.numberOfStages = stageCount; this.numberOfStages = stageCount;
   
this.position.width = 0; this.position.width = 0;
this.position.height = 0; this.position.height = 0;
} }
   
GUI.skin = null; GUI.skin = null;
this.position = GUILayout.Window(this.GetInstanceID(), this.position, this.Window, title, this.windowStyle).ClampToScreen(); this.position = GUILayout.Window(this.GetInstanceID(), this.position, this.Window, title, this.windowStyle).ClampToScreen();
   
if (this.compactCheck > 0 && this.compactCollapseRight) if (this.compactCheck > 0 && this.compactCollapseRight)
{ {
this.position.x = this.compactRight - this.position.width; this.position.x = this.compactRight - this.position.width;
this.compactCheck--; this.compactCheck--;
} }
else if (this.compactCheck > 0) else if (this.compactCheck > 0)
{ {
this.compactCheck = 0; this.compactCheck = 0;
} }
   
// Check editor lock to manage click-through. // Check editor lock to manage click-through.
this.CheckEditorLock(); this.CheckEditorLock();
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Exception(ex); Logger.Exception(ex, "BuildAdvanced.OnGUI()");
} }
} }
   
protected void Start() protected void Start()
{ {
try try
{ {
this.InitialiseStyles(); this.InitialiseStyles();
GuiDisplaySize.OnSizeChanged += this.OnSizeChanged; GuiDisplaySize.OnSizeChanged += this.OnSizeChanged;
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Exception(ex); Logger.Exception(ex, "BuildAdvanced.Start()");
} }
} }
   
protected void Update() protected void Update()
{ {
try try
{ {
if (Input.GetKeyDown(KeyBinder.EditorShowHide)) if (Input.GetKeyDown(KeyBinder.EditorShowHide))
{ {
this.visible = !this.visible; this.visible = !this.visible;
if (!this.visible) if (!this.visible)
{ {
this.EditorLock(false); this.EditorLock(false);
} }
} }
   
if (!this.visible || EditorLogic.fetch == null || EditorLogic.fetch.ship.parts.Count == 0) if (!this.visible || EditorLogic.fetch == null || EditorLogic.fetch.ship.parts.Count == 0)
{ {
this.bodiesList.enabled = false; this.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 (this.showAtmosphericDetails) if (this.showAtmosphericDetails)
{ {
SimManager.Atmosphere = CelestialBodies.SelectedBody.GetAtmospheres(Altitude); SimManager.Atmosphere = CelestialBodies.SelectedBody.GetAtmospheres(Altitude);
} }
else else
{ {
SimManager.Atmosphere = 0; SimManager.Atmosphere = 0;
} }
   
SimManager.Mach = this.atmosphericMach; SimManager.Mach = this.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 ((this.position.MouseIsOver() || this.bodiesList.Position.MouseIsOver()) && !this.isEditorLocked) if ((this.position.MouseIsOver() || this.bodiesList.Position.MouseIsOver()) && !this.isEditorLocked)
{ {
this.EditorLock(true); this.EditorLock(true);
} }
else if (!this.position.MouseIsOver() && !this.bodiesList.Position.MouseIsOver() && this.isEditorLocked) else if (!this.position.MouseIsOver() && !this.bodiesList.Position.MouseIsOver() && this.isEditorLocked)
{ {
this.EditorLock(false); this.EditorLock(false);
} }
} }
   
/// <summary> /// <summary>
/// Draws the atmospheric settings. /// Draws the atmospheric settings.
/// </summary> /// </summary>
private void DrawAtmosphericDetails() private void DrawAtmosphericDetails()
{ {
GUILayout.BeginHorizontal(); try
GUILayout.BeginVertical(); {
GUILayout.Label("Altitude: " + (Altitude * 0.001f).ToString("F1") + "km", this.settingAtmoStyle, GUILayout.Width(125.0f * GuiDisplaySize.Offset)); GUILayout.BeginHorizontal();
GUI.skin = HighLogic.Skin; GUILayout.BeginVertical();
Altitude = GUILayout.HorizontalSlider(Altitude, 0.0f, (float)(CelestialBodies.SelectedBody.CelestialBody.atmosphereDepth)); GUILayout.Label("Altitude: " + (Altitude * 0.001f).ToString("F1") + "km", this.settingAtmoStyle, GUILayout.Width(125.0f * GuiDisplaySize.Offset));
GUI.skin = null; GUI.skin = HighLogic.Skin;
GUILayout.EndVertical(); Altitude = GUILayout.HorizontalSlider(Altitude, 0.0f, (float)(CelestialBodies.SelectedBody.CelestialBody.atmosphereDepth));
  GUI.skin = null;
GUILayout.Space(5.0f); GUILayout.EndVertical();
   
GUILayout.BeginVertical(); GUILayout.Space(5.0f);
GUILayout.Label("Mach: " + this.atmosphericMach.ToString("F1"), this.settingAtmoStyle, GUILayout.Width(125.0f * GuiDisplaySize.Offset));  
GUI.skin = HighLogic.Skin; GUILayout.BeginVertical();
atmosphericMach = GUILayout.HorizontalSlider(Mathf.Clamp(atmosphericMach, 0.0f, SimManager.LastStage.maxMach), 0.0f, SimManager.LastStage.maxMach); GUILayout.Label("Mach: " + this.atmosphericMach.ToString("F1"), this.settingAtmoStyle, GUILayout.Width(125.0f * GuiDisplaySize.Offset));
GUI.skin = null; GUI.skin = HighLogic.Skin;
GUILayout.EndVertical(); atmosphericMach = GUILayout.HorizontalSlider(Mathf.Clamp(atmosphericMach, 0.0f, maxMach), 0.0f, maxMach);
GUILayout.EndHorizontal(); GUI.skin = null;
  GUILayout.EndVertical();
  GUILayout.EndHorizontal();
  }
  catch (Exception ex)
  {
  Logger.Exception(ex, "BuildAdvanced.DrawAtmosphericDetails()");
  }
} }
   
private void DrawBodiesList() private void DrawBodiesList()
{ {
if (CelestialBodies.SystemBody == CelestialBodies.SelectedBody) if (CelestialBodies.SystemBody == CelestialBodies.SelectedBody)
{ {
this.DrawBody(CelestialBodies.SystemBody); this.DrawBody(CelestialBodies.SystemBody);
} }
else else
{ {
foreach (var body in CelestialBodies.SystemBody.Children) foreach (var body in CelestialBodies.SystemBody.Children)
{ {
this.DrawBody(body); this.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 ? this.bodiesButtonActiveStyle : this.bodiesButtonStyle)) if (GUILayout.Button(bodyInfo.Children.Count > 0 ? bodyInfo.Name + " [" + bodyInfo.Children.Count + "]" : bodyInfo.Name, bodyInfo.Selected && bodyInfo.SelectedDepth == 0 ? this.bodiesButtonActiveStyle : this.bodiesButtonStyle))
{ {
CelestialBodies.SetSelectedBody(bodyInfo.Name); CelestialBodies.SetSelectedBody(bodyInfo.Name);
Altitude = 0.0f; Altitude = 0.0f;
this.bodiesList.Resize = true; this.bodiesList.Resize = true;
} }
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
   
if (bodyInfo.Selected) if (bodyInfo.Selected)
{ {
foreach (var body in bodyInfo.Children) foreach (var body in bodyInfo.Children)
{ {
this.DrawBody(body, depth + 1); this.DrawBody(body, 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", this.titleStyle); GUILayout.Label("BURN", this.titleStyle);
foreach (var stage in this.stages) foreach (var stage in this.stages)
{ {
if (this.showAllStages || stage.deltaV > 0) if (this.showAllStages || stage.deltaV > 0)
{ {
GUILayout.Label(TimeFormatter.ConvertToString(stage.time), this.infoStyle); GUILayout.Label(TimeFormatter.ConvertToString(stage.time), this.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", this.titleStyle); GUILayout.Label("COST", this.titleStyle);
foreach (var stage in this.stages) foreach (var stage in this.stages)
{ {
if (this.showAllStages || stage.deltaV > 0) if (this.showAllStages || stage.deltaV > 0)
{ {
GUILayout.Label(Units.Cost(stage.cost, stage.totalCost), this.infoStyle); GUILayout.Label(Units.Cost(stage.cost, stage.totalCost), this.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", this.titleStyle); GUILayout.Label("DELTA-V", this.titleStyle);
foreach (var stage in this.stages) foreach (var stage in this.stages)
{ {
if (this.showAllStages || stage.deltaV > 0) if (this.showAllStages || stage.deltaV > 0)
{ {
GUILayout.Label(stage.deltaV.ToString("N0") + " / " + stage.inverseTotalDeltaV.ToString("N0") + "m/s", this.infoStyle); GUILayout.Label(stage.deltaV.ToString("N0") + " / " + stage.inverseTotalDeltaV.ToString("N0") + "m/s", this.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", this.titleStyle); GUILayout.Label("ISP", this.titleStyle);
foreach (var stage in this.stages) foreach (var stage in this.stages)
{ {
if (this.showAllStages || stage.deltaV > 0) if (this.showAllStages || stage.deltaV > 0)
{ {
GUILayout.Label(stage.isp.ToString("F1") + "s", this.infoStyle); GUILayout.Label(stage.isp.ToString("F1") + "s", this.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", this.titleStyle); GUILayout.Label("MASS", this.titleStyle);
foreach (var stage in this.stages) foreach (var stage in this.stages)
{ {
if (this.showAllStages || stage.deltaV > 0) if (this.showAllStages || stage.deltaV > 0)
{ {
GUILayout.Label(Units.ToMass(stage.mass, stage.totalMass), this.infoStyle); GUILayout.Label(Units.ToMass(stage.mass, stage.totalMass), this.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", this.titleStyle); GUILayout.Label("PARTS", this.titleStyle);
foreach (var stage in this.stages) foreach (var stage in this.stages)
{ {
if (this.showAllStages || stage.deltaV > 0) if (this.showAllStages || stage.deltaV > 0)
{ {
GUILayout.Label(stage.partCount + " / " + stage.totalPartCount, this.infoStyle); GUILayout.Label(stage.partCount + " / " + stage.totalPartCount, this.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:", this.settingStyle); GUILayout.Label("Compact mode collapses to the:", this.settingStyle);
this.compactCollapseRight = !GUILayout.Toggle(!this.compactCollapseRight, "LEFT", this.buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset)); this.compactCollapseRight = !GUILayout.Toggle(!this.compactCollapseRight, "LEFT", this.buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset));
this.compactCollapseRight = GUILayout.Toggle(this.compactCollapseRight, "RIGHT", this.buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset)); this.compactCollapseRight = GUILayout.Toggle(this.compactCollapseRight, "RIGHT", this.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", this.buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset)); SimManager.vectoredThrust = GUILayout.Toggle(SimManager.vectoredThrust, "ENABLED", this.buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset));
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
   
GUILayout.BeginHorizontal(); GUILayout.BeginHorizontal();
GUILayout.Label("Build Engineer Overlay:", this.settingStyle); GUILayout.Label("Build Engineer Overlay:", this.settingStyle);
BuildOverlay.Visible = GUILayout.Toggle(BuildOverlay.Visible, "VISIBLE", this.buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset)); BuildOverlay.Visible = GUILayout.Toggle(BuildOverlay.Visible, "VISIBLE", this.buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset));
BuildOverlayPartInfo.NamesOnly = GUILayout.Toggle(BuildOverlayPartInfo.NamesOnly, "NAMES ONLY", this.buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset)); BuildOverlayPartInfo.NamesOnly = GUILayout.Toggle(BuildOverlayPartInfo.NamesOnly, "NAMES ONLY", this.buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset));
BuildOverlayPartInfo.ClickToOpen = GUILayout.Toggle(BuildOverlayPartInfo.ClickToOpen, "CLICK TO OPEN", this.buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset)); BuildOverlayPartInfo.ClickToOpen = GUILayout.Toggle(BuildOverlayPartInfo.ClickToOpen, "CLICK TO OPEN", this.buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset));
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
   
GUILayout.BeginHorizontal(); GUILayout.BeginHorizontal();
GUILayout.Label("Flight Engineer activation mode:", this.settingStyle); GUILayout.Label("Flight Engineer activation mode:", this.settingStyle);
FlightEngineerCore.IsCareerMode = GUILayout.Toggle(FlightEngineerCore.IsCareerMode, "CAREER", this.buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset)); FlightEngineerCore.IsCareerMode = GUILayout.Toggle(FlightEngineerCore.IsCareerMode, "CAREER", this.buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset));
FlightEngineerCore.IsCareerMode = !GUILayout.Toggle(!FlightEngineerCore.IsCareerMode, "PARTLESS", this.buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset)); FlightEngineerCore.IsCareerMode = !GUILayout.Toggle(!FlightEngineerCore.IsCareerMode, "PARTLESS", this.buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset));
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
   
GUILayout.BeginHorizontal(); GUILayout.BeginHorizontal();
GUILayout.Label("Flight Engineer Career Limitations:", this.settingStyle); GUILayout.Label("Flight Engineer Career Limitations:", this.settingStyle);
FlightEngineerCore.IsKerbalLimited = GUILayout.Toggle(FlightEngineerCore.IsKerbalLimited, "KERBAL", this.buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset)); FlightEngineerCore.IsKerbalLimited = GUILayout.Toggle(FlightEngineerCore.IsKerbalLimited, "KERBAL", this.buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset));
FlightEngineerCore.IsTrackingStationLimited = GUILayout.Toggle(FlightEngineerCore.IsTrackingStationLimited, "TRACKING", this.buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset)); FlightEngineerCore.IsTrackingStationLimited = GUILayout.Toggle(FlightEngineerCore.IsTrackingStationLimited, "TRACKING", this.buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset));
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
   
GUILayout.BeginHorizontal(); GUILayout.BeginHorizontal();
GUILayout.Label("GUI Size: " + GuiDisplaySize.Increment, this.settingStyle); GUILayout.Label("GUI Size: " + GuiDisplaySize.Increment, this.settingStyle);
if (GUILayout.Button("<", this.buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset))) if (GUILayout.Button("<", this.buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset)))
{ {
GuiDisplaySize.Increment--; GuiDisplaySize.Increment--;
} }
if (GUILayout.Button(">", this.buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset))) if (GUILayout.Button(">", this.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", this.settingStyle); GUILayout.Label("Minimum delay between simulations: " + SimManager.minSimTime.Milliseconds + "ms", this.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, this.titleStyle); GUILayout.Label(string.Empty, this.titleStyle);
foreach (var stage in this.stages) foreach (var stage in this.stages)
{ {
if (this.showAllStages || stage.deltaV > 0) if (this.showAllStages || stage.deltaV > 0)
{ {
GUILayout.Label("S" + stage.number, this.titleStyle); GUILayout.Label("S" + stage.number, this.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", this.titleStyle); GUILayout.Label("THRUST", this.titleStyle);
foreach (var stage in this.stages) foreach (var stage in this.stages)
{ {
if (this.showAllStages || stage.deltaV > 0) if (this.showAllStages || stage.deltaV > 0)
{ {
GUILayout.Label(stage.thrust.ToForce(), this.infoStyle); GUILayout.Label(stage.thrust.ToForce(), this.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", this.titleStyle); GUILayout.Label("TORQUE", this.titleStyle);
foreach (var stage in this.stages) foreach (var stage in this.stages)
{ {
if (this.showAllStages || stage.deltaV > 0) if (this.showAllStages || stage.deltaV > 0)
{ {
GUILayout.Label(stage.maxThrustTorque.ToTorque(), this.infoStyle); GUILayout.Label(stage.maxThrustTorque.ToTorque(), this.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)", this.titleStyle); GUILayout.Label("TWR (MAX)", this.titleStyle);
foreach (var stage in this.stages) foreach (var stage in this.stages)
{ {
if (this.showAllStages || stage.deltaV > 0) if (this.showAllStages || stage.deltaV > 0)
{ {
GUILayout.Label(stage.thrustToWeight.ToString("F2") + " (" + stage.maxThrustToWeight.ToString("F2") + ")", this.infoStyle); GUILayout.Label(stage.thrustToWeight.ToString("F2") + " (" + stage.maxThrustToWeight.ToString("F2") + ")", this.infoStyle);
} }
} }
GUILayout.EndVertical(); GUILayout.EndVertical();
} }
   
private void EditorLock(bool state) private void EditorLock(bool state)
{ {
if (state) if (state)
{ {
EditorLogic.fetch.Lock(true, true, true, "KER_BuildAdvanced"); EditorLogic.fetch.Lock(true, true, true, "KER_BuildAdvanced");
BuildOverlayPartInfo.Hidden = true; BuildOverlayPartInfo.Hidden = true;
this.isEditorLocked = true; this.isEditorLocked = true;
} }
else else
{ {
EditorLogic.fetch.Unlock("KER_BuildAdvanced"); EditorLogic.fetch.Unlock("KER_BuildAdvanced");
BuildOverlayPartInfo.Hidden = false; BuildOverlayPartInfo.Hidden = false;
this.isEditorLocked = false; this.isEditorLocked = false;
} }
} }
   
private void GetStageInfo() private void GetStageInfo()
{ {
this.stages = SimManager.Stages; stages = SimManager.Stages;
  if (stages != null && stages.Length > 0)
  {
  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()
{ {
this.windowStyle = new GUIStyle(HighLogic.Skin.window) this.windowStyle = new GUIStyle(HighLogic.Skin.window)
{ {
alignment = TextAnchor.UpperLeft alignment = TextAnchor.UpperLeft
}; };
   
this.areaStyle = new GUIStyle(HighLogic.Skin.box) this.areaStyle = new GUIStyle(HighLogic.Skin.box)
{ {
padding = new RectOffset(0, 0, 9, 0) padding = new RectOffset(0, 0, 9, 0)
}; };
   
this.areaSettingStyle = new GUIStyle(HighLogic.Skin.box) this.areaSettingStyle = new GUIStyle(HighLogic.Skin.box)
{ {
padding = new RectOffset(10, 10, 10, 10) padding = new RectOffset(10, 10, 10, 10)
}; };
   
this.buttonStyle = new GUIStyle(HighLogic.Skin.button) this.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
}; };
   
this.titleStyle = new GUIStyle(HighLogic.Skin.label) this.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,
}; };
   
this.infoStyle = new GUIStyle(HighLogic.Skin.label) this.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
}; };
   
this.settingStyle = new GUIStyle(this.titleStyle) this.settingStyle = new GUIStyle(this.titleStyle)
{ {
alignment = TextAnchor.MiddleLeft, alignment = TextAnchor.MiddleLeft,
stretchWidth = true, stretchWidth = true,
stretchHeight = true stretchHeight = true
}; };
   
this.settingAtmoStyle = new GUIStyle(this.titleStyle) this.settingAtmoStyle = new GUIStyle(this.titleStyle)
{ {
margin = new RectOffset(), margin = new RectOffset(),
padding = new RectOffset(), padding = new RectOffset(),
alignment = TextAnchor.UpperLeft alignment = TextAnchor.UpperLeft
}; };
   
this.bodiesButtonStyle = new GUIStyle(HighLogic.Skin.button) this.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
}; };
   
this.bodiesButtonActiveStyle = new GUIStyle(this.bodiesButtonStyle) this.bodiesButtonActiveStyle = new GUIStyle(this.bodiesButtonStyle)
{ {
normal = this.bodiesButtonStyle.onNormal, normal = this.bodiesButtonStyle.onNormal,
hover = this.bodiesButtonStyle.onHover hover = this.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
{ {
var handler = SettingHandler.Load("BuildAdvanced.xml"); var handler = SettingHandler.Load("BuildAdvanced.xml");
handler.Get("visible", ref this.visible); handler.Get("visible", ref this.visible);
this.position.x = handler.Get("windowPositionX", this.position.x); this.position.x = handler.Get("windowPositionX", this.position.x);
this.position.y = handler.Get("windowPositionY", this.position.y); this.position.y = handler.Get("windowPositionY", this.position.y);
handler.Get("compactMode", ref this.compactMode); handler.Get("compactMode", ref this.compactMode);
handler.Get("compactCollapseRight", ref this.compactCollapseRight); handler.Get("compactCollapseRight", ref this.compactCollapseRight);
handler.Get("showAllStages", ref this.showAllStages); handler.Get("showAllStages", ref this.showAllStages);
handler.Get("showAtmosphericDetails", ref this.showAtmosphericDetails); handler.Get("showAtmosphericDetails", ref this.showAtmosphericDetails);
handler.Get("showSettings", ref this.showSettings); handler.Get("showSettings", ref this.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()
{ {
this.InitialiseStyles(); this.InitialiseStyles();
this.hasChanged = true; this.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
{ {
// Draw the compact mode toggle. // Draw the compact mode toggle.
if (GUI.Toggle(new Rect(this.position.width - 70.0f * GuiDisplaySize.Offset, 5.0f, 65.0f * GuiDisplaySize.Offset, 20.0f), this.compactMode, "COMPACT", this.buttonStyle) != this.compactMode) if (GUI.Toggle(new Rect(this.position.width - 70.0f * GuiDisplaySize.Offset, 5.0f, 65.0f * GuiDisplaySize.Offset, 20.0f), this.compactMode, "COMPACT", this.buttonStyle) != this.compactMode)
{ {
this.hasChanged = true; this.hasChanged = true;
this.compactCheck = 2; this.compactCheck = 2;
this.compactRight = this.position.xMax; this.compactRight = this.position.xMax;
this.compactMode = !this.compactMode; this.compactMode = !this.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 (!this.compactMode) if (!this.compactMode)
{ {
if (GUI.Toggle(new Rect(this.position.width - 143.0f * GuiDisplaySize.Offset, 5.0f, 70.0f * GuiDisplaySize.Offset, 20.0f), this.showSettings, "SETTINGS", this.buttonStyle) != this.showSettings) if (GUI.Toggle(new Rect(this.position.width - 143.0f * GuiDisplaySize.Offset, 5.0f, 70.0f * GuiDisplaySize.Offset, 20.0f), this.showSettings, "SETTINGS", this.buttonStyle) != this.showSettings)
{ {
this.hasChanged = true; this.hasChanged = true;
this.showSettings = !this.showSettings; this.showSettings = !this.showSettings;
} }
   
if (GUI.Toggle(new Rect(this.position.width - 226.0f * GuiDisplaySize.Offset, 5.0f, 80.0f * GuiDisplaySize.Offset, 20.0f), this.showAllStages, "ALL STAGES", this.buttonStyle) != this.showAllStages) if (GUI.Toggle(new Rect(this.position.width - 226.0f * GuiDisplaySize.Offset, 5.0f, 80.0f * GuiDisplaySize.Offset, 20.0f), this.showAllStages, "ALL STAGES", this.buttonStyle) != this.showAllStages)
{ {
this.hasChanged = true; this.hasChanged = true;
this.showAllStages = !this.showAllStages; this.showAllStages = !this.showAllStages;
} }
   
if (GUI.Toggle(new Rect(this.position.width - 324.0f * GuiDisplaySize.Offset, 5.0f, 95.0f * GuiDisplaySize.Offset, 20.0f), this.showAtmosphericDetails, "ATMOSPHERIC", this.buttonStyle) != this.showAtmosphericDetails) if (GUI.Toggle(new Rect(this.position.width - 324.0f * GuiDisplaySize.Offset, 5.0f, 95.0f * GuiDisplaySize.Offset, 20.0f), this.showAtmosphericDetails, "ATMOSPHERIC", this.buttonStyle) != this.showAtmosphericDetails)
{ {
this.hasChanged = true; this.hasChanged = true;
this.showAtmosphericDetails = !this.showAtmosphericDetails; this.showAtmosphericDetails = !this.showAtmosphericDetails;
} }
   
this.bodiesListPosition = new Rect(this.position.width - 452.0f * GuiDisplaySize.Offset, 5.0f, 125.0f * GuiDisplaySize.Offset, 20.0f); this.bodiesListPosition = new Rect(this.position.width - 452.0f * GuiDisplaySize.Offset, 5.0f, 125.0f * GuiDisplaySize.Offset, 20.0f);
this.bodiesList.enabled = GUI.Toggle(this.bodiesListPosition, this.bodiesList.enabled, "BODY: " + CelestialBodies.SelectedBody.Name.ToUpper(), this.buttonStyle); this.bodiesList.enabled = GUI.Toggle(this.bodiesListPosition, this.bodiesList.enabled, "BODY: " + CelestialBodies.SelectedBody.Name.ToUpper(), this.buttonStyle);
this.bodiesList.SetPosition(this.bodiesListPosition.Translate(this.position)); this.bodiesList.SetPosition(this.bodiesListPosition.Translate(this.position));
} }
   
// Draw the main informational display box. // Draw the main informational display box.
if (!this.compactMode) if (!this.compactMode)
{ {
GUILayout.BeginHorizontal(this.areaStyle); GUILayout.BeginHorizontal(this.areaStyle);
this.DrawStageNumbers(); this.DrawStageNumbers();
this.DrawPartCount(); this.DrawPartCount();
this.DrawCost(); this.DrawCost();
this.DrawMass(); this.DrawMass();
this.DrawIsp(); this.DrawIsp();
this.DrawThrust(); this.DrawThrust();
this.DrawTorque(); this.DrawTorque();
this.DrawTwr(); this.DrawTwr();
this.DrawDeltaV(); this.DrawDeltaV();
this.DrawBurnTime(); this.DrawBurnTime();
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
   
if (this.showAtmosphericDetails) if (this.showAtmosphericDetails)
{ {
GUILayout.BeginVertical(this.areaSettingStyle); GUILayout.BeginVertical(this.areaSettingStyle);
this.DrawAtmosphericDetails(); this.DrawAtmosphericDetails();
GUILayout.EndVertical(); GUILayout.EndVertical();
} }
   
if (this.showSettings) if (this.showSettings)
{ {
GUILayout.BeginVertical(this.areaSettingStyle); GUILayout.BeginVertical(this.areaSettingStyle);
this.DrawSettings(); this.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(this.areaStyle); GUILayout.BeginHorizontal(this.areaStyle);
this.DrawStageNumbers(); this.DrawStageNumbers();
this.DrawTwr(); this.DrawTwr();
this.DrawDeltaV(); this.DrawDeltaV();
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
} }
   
GUI.DragWindow(); GUI.DragWindow();
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Exception(ex); 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/>.
// //
   
using System; using System;
   
namespace KerbalEngineer namespace KerbalEngineer
{ {
public class VectorAverager public class VectorAverager
{ {
private Vector3d sum = Vector3d.zero; private Vector3d sum = Vector3d.zero;
private uint count = 0; private uint count = 0;
   
public void Add(Vector3d v) { public void Add(Vector3d v) {
sum += v; sum += v;
count += 1; count += 1;
} }
   
public Vector3d Get() { public Vector3d Get() {
if (count > 0) { if (count > 0) {
return sum / count; return sum / count;
} else { } else {
return Vector3d.zero; return Vector3d.zero;
} }
} }
   
  public void Reset()
  {
  sum = Vector3d.zero;
  count = 0;
  }
} }
   
public class WeightedVectorAverager public class WeightedVectorAverager
{ {
private Vector3d sum = Vector3d.zero; private Vector3d sum = Vector3d.zero;
private double totalweight = 0; private double totalweight = 0;
   
public void Add(Vector3d v, double weight) { public void Add(Vector3d v, double weight) {
sum += v * weight; sum += v * weight;
totalweight += weight; totalweight += weight;
} }
   
public Vector3d Get() { public Vector3d Get() {
if (totalweight > 0) { if (totalweight > 0) {
return sum / totalweight; return sum / totalweight;
} else { } else {
return Vector3d.zero; return Vector3d.zero;
} }
} }
   
public double GetTotalWeight() { public double GetTotalWeight() {
return totalweight; return totalweight;
} }
   
  public void Reset()
  {
  sum = Vector3d.zero;
  totalweight = 0.0;
  }
} }
} }
   
   
// //
// Kerbal Engineer Redux // Kerbal Engineer Redux
// //
// Copyright (C) 2014 CYBUTEK // Copyright (C) 2014 CYBUTEK
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
// //
   
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
   
namespace KerbalEngineer namespace KerbalEngineer
{ {
// a (force, application point) tuple // a (force, application point) tuple
public class AppliedForce public class AppliedForce
{ {
public Vector3d vector; public Vector3d vector;
public Vector3d applicationPoint; public Vector3d applicationPoint;
   
public AppliedForce(Vector3d vector, Vector3d applicationPoint) { public AppliedForce(Vector3d vector, Vector3d applicationPoint) {
this.vector = vector; this.vector = vector;
this.applicationPoint = applicationPoint; this.applicationPoint = applicationPoint;
} }
} }
   
// This class was mostly adapted from FARCenterQuery, part of FAR, by ferram4, GPLv3 // This class was mostly adapted from FARCenterQuery, part of FAR, by ferram4, GPLv3
// https://github.com/ferram4/Ferram-Aerospace-Research/blob/master/FerramAerospaceResearch/FARCenterQuery.cs // https://github.com/ferram4/Ferram-Aerospace-Research/blob/master/FerramAerospaceResearch/FARCenterQuery.cs
// Also see https://en.wikipedia.org/wiki/Resultant_force // Also see https://en.wikipedia.org/wiki/Resultant_force
   
// It accumulates forces and their points of applications, and provides methods for // It accumulates forces and their points of applications, and provides methods for
// calculating the effective torque at any position, as well as the minimum-torque net force application point. // calculating the effective torque at any position, as well as the minimum-torque net force application point.
// //
// The latter is a non-trivial issue; there is a 1-dimensional line of physically-equivalent solutions parallel // The latter is a non-trivial issue; there is a 1-dimensional line of physically-equivalent solutions parallel
// to the resulting force vector; the solution closest to the weighted average of force positions is chosen. // to the resulting force vector; the solution closest to the weighted average of force positions is chosen.
// In the case of non-parallel forces, there usually is an infinite number of such lines, all of which have // In the case of non-parallel forces, there usually is an infinite number of such lines, all of which have
// some amount of residual torque. The line with the least amount of residual torque is chosen. // some amount of residual torque. The line with the least amount of residual torque is chosen.
public class ForceAccumulator public class ForceAccumulator
{ {
// Total force. // Total force.
private Vector3d totalForce = Vector3d.zero; private Vector3d totalForce = Vector3d.zero;
// Torque needed to compensate if force were applied at origin. // Torque needed to compensate if force were applied at origin.
private Vector3d totalZeroOriginTorque = Vector3d.zero; private Vector3d totalZeroOriginTorque = Vector3d.zero;
   
// Weighted average of force application points. // Weighted average of force application points.
private WeightedVectorAverager avgApplicationPoint = new WeightedVectorAverager(); private WeightedVectorAverager avgApplicationPoint = new WeightedVectorAverager();
   
// Feed an force to the accumulator. // Feed an force to the accumulator.
public void AddForce(Vector3d applicationPoint, Vector3d force) public void AddForce(Vector3d applicationPoint, Vector3d force)
{ {
totalForce += force; totalForce += force;
totalZeroOriginTorque += Vector3d.Cross(applicationPoint, force); totalZeroOriginTorque += Vector3d.Cross(applicationPoint, force);
avgApplicationPoint.Add(applicationPoint, force.magnitude); avgApplicationPoint.Add(applicationPoint, force.magnitude);
} }
   
public Vector3d GetAverageForceApplicationPoint() { public Vector3d GetAverageForceApplicationPoint() {
return avgApplicationPoint.Get(); return avgApplicationPoint.Get();
} }
   
public void AddForce(AppliedForce force) { public void AddForce(AppliedForce force) {
AddForce(force.applicationPoint, force.vector); AddForce(force.applicationPoint, force.vector);
} }
   
// Residual torque for given force application point. // Residual torque for given force application point.
public Vector3d TorqueAt(Vector3d origin) public Vector3d TorqueAt(Vector3d origin)
{ {
return totalZeroOriginTorque - Vector3d.Cross(origin, totalForce); return totalZeroOriginTorque - Vector3d.Cross(origin, totalForce);
} }
   
// Total force vector. // Total force vector.
public Vector3d GetTotalForce() public Vector3d GetTotalForce()
{ {
return totalForce; return totalForce;
} }
   
// Returns the minimum-residual-torque force application point that is closest to origin. // Returns the minimum-residual-torque force application point that is closest to origin.
// Note that TorqueAt(GetMinTorquePos()) is always parallel to totalForce. // Note that TorqueAt(GetMinTorquePos()) is always parallel to totalForce.
public Vector3d GetMinTorqueForceApplicationPoint(Vector3d origin) public Vector3d GetMinTorqueForceApplicationPoint(Vector3d origin)
{ {
double fmag = totalForce.sqrMagnitude; double fmag = totalForce.sqrMagnitude;
if (fmag <= 0) { if (fmag <= 0) {
return origin; return origin;
} }
   
return origin + Vector3d.Cross(totalForce, TorqueAt(origin)) / fmag; return origin + Vector3d.Cross(totalForce, TorqueAt(origin)) / fmag;
} }
   
public Vector3d GetMinTorqueForceApplicationPoint() public Vector3d GetMinTorqueForceApplicationPoint()
{ {
return GetMinTorqueForceApplicationPoint(avgApplicationPoint.Get()); return GetMinTorqueForceApplicationPoint(avgApplicationPoint.Get());
} }
   
  public void Reset()
  {
  totalForce = Vector3d.zero;
  totalZeroOriginTorque = Vector3d.zero;
  avgApplicationPoint.Reset();
  }
} }
} }
  namespace KerbalEngineer.VesselSimulator
  {
  using System.Collections.Generic;
 
  public class Pool<T> where T : new()
  {
  private static List<T> available = new List<T>();
  private static List<T> inUse = new List<T>();
 
  public static int PoolCount
  {
  get
  {
  return available.Count + inUse.Count;
  }
  }
 
  public static T GetPoolObject()
  {
  T obj;
  if (available.Count > 0)
  {
  obj = available[0];
  available.RemoveAt(0);
  }
  else
  {
  obj = new T();
  }
 
  inUse.Add(obj);
  return obj;
  }
 
  public static void Release(T obj)
  {
  if (inUse.Contains(obj))
  {
  inUse.Remove(obj);
  available.Add(obj);
  }
  }
 
  public static void ReleaseAll()
  {
  for (int i = 0; i < inUse.Count; ++i)
  {
  available.Add(inUse[i]);
  }
  inUse.Clear();
  }
  }
  }
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{39806613-E0B7-46E0-89A6-A569EC538CBB}</ProjectGuid> <ProjectGuid>{39806613-E0B7-46E0-89A6-A569EC538CBB}</ProjectGuid>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder> <AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>KerbalEngineer</RootNamespace> <RootNamespace>KerbalEngineer</RootNamespace>
<AssemblyName>KerbalEngineer</AssemblyName> <AssemblyName>KerbalEngineer</AssemblyName>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion> <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>false</DebugSymbols> <DebugSymbols>false</DebugSymbols>
<DebugType>none</DebugType> <DebugType>none</DebugType>
<Optimize>false</Optimize> <Optimize>false</Optimize>
<OutputPath>..\Output\KerbalEngineer\</OutputPath> <OutputPath>..\Output\KerbalEngineer\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants> <DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<UseVSHostingProcess>false</UseVSHostingProcess> <UseVSHostingProcess>false</UseVSHostingProcess>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>none</DebugType> <DebugType>none</DebugType>
<Optimize>true</Optimize> <Optimize>true</Optimize>
<OutputPath>..\Output\KerbalEngineer\</OutputPath> <OutputPath>..\Output\KerbalEngineer\</OutputPath>
<DefineConstants> <DefineConstants>
</DefineConstants> </DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<UseVSHostingProcess>false</UseVSHostingProcess> <UseVSHostingProcess>false</UseVSHostingProcess>
<AllowUnsafeBlocks>false</AllowUnsafeBlocks> <AllowUnsafeBlocks>false</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Control\IControlPanel.cs" /> <Compile Include="Control\IControlPanel.cs" />
<Compile Include="Control\Panels\BuildOverlayPanel.cs" /> <Compile Include="Control\Panels\BuildOverlayPanel.cs" />
<Compile Include="Control\Panels\BuildEngineerPanel.cs" /> <Compile Include="Control\Panels\BuildEngineerPanel.cs" />
<Compile Include="Editor\BuildAdvanced.cs" /> <Compile Include="Editor\BuildAdvanced.cs" />
<Compile Include="Editor\BuildOverlay.cs" /> <Compile Include="Editor\BuildOverlay.cs" />
<Compile Include="CelestialBodies.cs" /> <Compile Include="CelestialBodies.cs" />
<Compile Include="Editor\BuildOverlayPartInfo.cs" /> <Compile Include="Editor\BuildOverlayPartInfo.cs" />
<Compile Include="Editor\BuildOverlayResources.cs" /> <Compile Include="Editor\BuildOverlayResources.cs" />
<Compile Include="Editor\BuildOverlayVessel.cs" /> <Compile Include="Editor\BuildOverlayVessel.cs" />
<Compile Include="Editor\BuildToolbar.cs" /> <Compile Include="Editor\BuildToolbar.cs" />
<Compile Include="Editor\PartInfoItem.cs" /> <Compile Include="Editor\PartInfoItem.cs" />
<Compile Include="Editor\ResourceInfoItem.cs" /> <Compile Include="Editor\ResourceInfoItem.cs" />
<Compile Include="Extensions\FloatExtensions.cs" /> <Compile Include="Extensions\FloatExtensions.cs" />
<Compile Include="Extensions\OrbitExtensions.cs" /> <Compile Include="Extensions\OrbitExtensions.cs" />
<Compile Include="Flight\ActionMenuGui.cs" /> <Compile Include="Flight\ActionMenuGui.cs" />
<Compile Include="Flight\Presets\Preset.cs" /> <Compile Include="Flight\Presets\Preset.cs" />
<Compile Include="Flight\Readouts\Miscellaneous\SystemTime.cs" /> <Compile Include="Flight\Readouts\Miscellaneous\SystemTime.cs" />
<Compile Include="Flight\Readouts\Miscellaneous\VectoredThrustToggle.cs" /> <Compile Include="Flight\Readouts\Miscellaneous\VectoredThrustToggle.cs" />
<Compile Include="Flight\Readouts\Miscellaneous\TimeReference.cs" /> <Compile Include="Flight\Readouts\Miscellaneous\TimeReference.cs" />
<Compile Include="Flight\Readouts\Miscellaneous\Separator.cs" /> <Compile Include="Flight\Readouts\Miscellaneous\Separator.cs" />
<Compile Include="Flight\Readouts\Miscellaneous\GuiSizeAdjustor.cs" /> <Compile Include="Flight\Readouts\Miscellaneous\GuiSizeAdjustor.cs" />
<Compile Include="Flight\Readouts\Orbital\AngleToEquatorialDescendingNode.cs" /> <Compile Include="Flight\Readouts\Orbital\AngleToEquatorialDescendingNode.cs" />
<Compile Include="Flight\Readouts\Orbital\AngleToEquatorialAscendingNode.cs" /> <Compile Include="Flight\Readouts\Orbital\AngleToEquatorialAscendingNode.cs" />
<Compile Include="Flight\Readouts\Orbital\AngleToRetrograde.cs" /> <Compile Include="Flight\Readouts\Orbital\AngleToRetrograde.cs" />
<Compile Include="Flight\Readouts\Orbital\AngleToPrograde.cs" /> <Compile Include="Flight\Readouts\Orbital\AngleToPrograde.cs" />
<Compile Include="Flight\Readouts\Orbital\ManoeuvreNode\NodeRadialDeltaV.cs" /> <Compile Include="Flight\Readouts\Orbital\ManoeuvreNode\NodeRadialDeltaV.cs" />
<Compile Include="Flight\Readouts\Orbital\ManoeuvreNode\ManoeuvreProcessor.cs" /> <Compile Include="Flight\Readouts\Orbital\ManoeuvreNode\ManoeuvreProcessor.cs" />
<Compile Include="Flight\Readouts\Orbital\ManoeuvreNode\NodeTimeToHalfBurn.cs" /> <Compile Include="Flight\Readouts\Orbital\ManoeuvreNode\NodeTimeToHalfBurn.cs" />
<Compile Include="Flight\Readouts\Orbital\ManoeuvreNode\NodeTimeToManoeuvre.cs" /> <Compile Include="Flight\Readouts\Orbital\ManoeuvreNode\NodeTimeToManoeuvre.cs" />
<Compile Include="Flight\Readouts\Orbital\ManoeuvreNode\NodeHalfBurnTime.cs" /> <Compile Include="Flight\Readouts\Orbital\ManoeuvreNode\NodeHalfBurnTime.cs" />
<Compile Include="Flight\Readouts\Orbital\ManoeuvreNode\NodeBurnTime.cs" /> <Compile Include="Flight\Readouts\Orbital\ManoeuvreNode\NodeBurnTime.cs" />
<Compile Include="Flight\Readouts\Orbital\ManoeuvreNode\NodeAngleToRetrograde.cs" /> <Compile Include="Flight\Readouts\Orbital\ManoeuvreNode\NodeAngleToRetrograde.cs" />
<Compile Include="Flight\Readouts\Orbital\ManoeuvreNode\NodeNormalDeltaV.cs" /> <Compile Include="Flight\Readouts\Orbital\ManoeuvreNode\NodeNormalDeltaV.cs" />
<Compile Include="Flight\Readouts\Orbital\ManoeuvreNode\NodeAngleToPrograde.cs" /> <Compile Include="Flight\Readouts\Orbital\ManoeuvreNode\NodeAngleToPrograde.cs" />
<Compile Include="Flight\Readouts\Orbital\ManoeuvreNode\NodeTotalDeltaV.cs" /> <Compile Include="Flight\Readouts\Orbital\ManoeuvreNode\NodeTotalDeltaV.cs" />
<Compile Include="Flight\Readouts\Orbital\ManoeuvreNode\NodeProgradeDeltaV.cs" /> <Compile Include="Flight\Readouts\Orbital\ManoeuvreNode\NodeProgradeDeltaV.cs" />
<Compile Include="Flight\Readouts\Orbital\MeanAnomalyAtEpoc.cs" /> <Compile Include="Flight\Readouts\Orbital\MeanAnomalyAtEpoc.cs" />
<Compile Include="Flight\Readouts\Orbital\MeanAnomaly.cs" /> <Compile Include="Flight\Readouts\Orbital\MeanAnomaly.cs" />
<Compile Include="Flight\Readouts\Orbital\EccentricAnomaly.cs" /> <Compile Include="Flight\Readouts\Orbital\EccentricAnomaly.cs" />
<Compile Include="Flight\Readouts\Orbital\ArgumentOfPeriapsis.cs" /> <Compile Include="Flight\Readouts\Orbital\ArgumentOfPeriapsis.cs" />
<Compile Include="Flight\Readouts\Orbital\CurrentSoi.cs" /> <Compile Include="Flight\Readouts\Orbital\CurrentSoi.cs" />
<Compile Include="Flight\Readouts\Orbital\TrueAnomaly.cs" /> <Compile Include="Flight\Readouts\Orbital\TrueAnomaly.cs" />
<Compile Include="Flight\Readouts\Orbital\TimeToEquatorialAscendingNode.cs" /> <Compile Include="Flight\Readouts\Orbital\TimeToEquatorialAscendingNode.cs" />
<Compile Include="Flight\Readouts\Orbital\TimeToEquatorialDescendingNode.cs" /> <Compile Include="Flight\Readouts\Orbital\TimeToEquatorialDescendingNode.cs" />
<Compile Include="Flight\Readouts\Rendezvous\RelativeSpeed.cs" /> <Compile Include="Flight\Readouts\Rendezvous\RelativeSpeed.cs" />
<Compile Include="Flight\Readouts\Rendezvous\RelativeVelocity.cs" /> <Compile Include="Flight\Readouts\Rendezvous\RelativeVelocity.cs" />
<Compile Include="Flight\Readouts\Rendezvous\SemiMinorAxis.cs" /> <Compile Include="Flight\Readouts\Rendezvous\SemiMinorAxis.cs" />
<Compile Include="Flight\Readouts\Rendezvous\SemiMajorAxis.cs" /> <Compile Include="Flight\Readouts\Rendezvous\SemiMajorAxis.cs" />
<Compile Include="Flight\Readouts\Rendezvous\TimeToRelativeDescendingNode.cs" /> <Compile Include="Flight\Readouts\Rendezvous\TimeToRelativeDescendingNode.cs" />
<Compile Include="Flight\Readouts\Rendezvous\TimeToRelativeAscendingNode.cs" /> <Compile Include="Flight\Readouts\Rendezvous\TimeToRelativeAscendingNode.cs" />
<Compile Include="Flight\Readouts\Surface\ImpactBiome.cs" /> <Compile Include="Flight\Readouts\Surface\ImpactBiome.cs" />
<Compile Include="Flight\Readouts\Surface\Slope.cs" /> <Compile Include="Flight\Readouts\Surface\Slope.cs" />
<Compile Include="Flight\Readouts\Surface\Biome.cs" /> <Compile Include="Flight\Readouts\Surface\Biome.cs" />
<Compile Include="Flight\Readouts\Surface\HorizontalAcceleration.cs" /> <Compile Include="Flight\Readouts\Surface\HorizontalAcceleration.cs" />
<Compile Include="Flight\Readouts\Surface\VerticalAcceleration.cs" /> <Compile Include="Flight\Readouts\Surface\VerticalAcceleration.cs" />
<Compile Include="Flight\Readouts\Vessel\AttitudeProcessor.cs" /> <Compile Include="Flight\Readouts\Vessel\AttitudeProcessor.cs" />
<Compile Include="Flight\Readouts\Vessel\DeltaVCurrentTotal.cs" /> <Compile Include="Flight\Readouts\Vessel\DeltaVCurrentTotal.cs" />
<Compile Include="Flight\Readouts\Vessel\PitchRate.cs" /> <Compile Include="Flight\Readouts\Vessel\PitchRate.cs" />
<Compile Include="Flight\Readouts\Vessel\HeadingRate.cs" /> <Compile Include="Flight\Readouts\Vessel\HeadingRate.cs" />
<Compile Include="Flight\Readouts\Vessel\RollRate.cs" /> <Compile Include="Flight\Readouts\Vessel\RollRate.cs" />
<Compile Include="Flight\Readouts\Vessel\Roll.cs" /> <Compile Include="Flight\Readouts\Vessel\Roll.cs" />
<Compile Include="Flight\Readouts\Vessel\Pitch.cs" /> <Compile Include="Flight\Readouts\Vessel\Pitch.cs" />
<Compile Include="Flight\Readouts\Vessel\Heading.cs" /> <Compile Include="Flight\Readouts\Vessel\Heading.cs" />
<Compile Include="Flight\Readouts\Vessel\PartCount.cs" /> <Compile Include="Flight\Readouts\Vessel\PartCount.cs" />
<Compile Include="Flight\Readouts\Vessel\SuicideBurnDeltaV.cs" /> <Compile Include="Flight\Readouts\Vessel\SuicideBurnDeltaV.cs" />
<Compile Include="Flight\Readouts\Vessel\SuicideBurnAltitude.cs" /> <Compile Include="Flight\Readouts\Vessel\SuicideBurnAltitude.cs" />
<Compile Include="Flight\Readouts\Vessel\SuicideBurnDistance.cs" /> <Compile Include="Flight\Readouts\Vessel\SuicideBurnDistance.cs" />
<Compile Include="Flight\Readouts\Vessel\DeltaVCurrent.cs" /> <Compile Include="Flight\Readouts\Vessel\DeltaVCurrent.cs" />
<Compile Include="Flight\Readouts\Vessel\IntakeAirUsage.cs" /> <Compile Include="Flight\Readouts\Vessel\IntakeAirUsage.cs" />
<Compile Include="Flight\Readouts\Vessel\IntakeAirDemandSupply.cs" /> <Compile Include="Flight\Readouts\Vessel\IntakeAirDemandSupply.cs" />
<Compile Include="Flight\Readouts\Vessel\IntakeAirSupply.cs" /> <Compile Include="Flight\Readouts\Vessel\IntakeAirSupply.cs" />
<Compile Include="Flight\Readouts\Vessel\IntakeAirDemand.cs" /> <Compile Include="Flight\Readouts\Vessel\IntakeAirDemand.cs" />
<Compile Include="Flight\Readouts\Miscellaneous\SimulationDelay.cs" /> <Compile Include="Flight\Readouts\Miscellaneous\SimulationDelay.cs" />
<Compile Include="Flight\Readouts\Vessel\SimulationProcessor.cs" /> <Compile Include="Flight\Readouts\Vessel\SimulationProcessor.cs" />
<Compile Include="Flight\Readouts\Vessel\Acceleration.cs" /> <Compile Include="Flight\Readouts\Vessel\Acceleration.cs" />
<Compile Include="Flight\Presets\PresetLibrary.cs" /> <Compile Include="Flight\Presets\PresetLibrary.cs" />
<Compile Include="Flight\Readouts\Vessel\SuicideBurnProcessor.cs" /> <Compile Include="Flight\Readouts\Vessel\SuicideBurnProcessor.cs" />
<Compile Include="Flight\Readouts\Vessel\SurfaceThrustToWeight.cs" /> <Compile Include="Flight\Readouts\Vessel\SurfaceThrustToWeight.cs" />
<Compile Include="Flight\Readouts\Surface\Situation.cs" /> <Compile Include="Flight\Readouts\Surface\Situation.cs" />
<Compile Include="Flight\Readouts\Vessel\ThrustOffsetAngle.cs" /> <Compile Include="Flight\Readouts\Vessel\ThrustOffsetAngle.cs" />
<Compile Include="Flight\Readouts\Vessel\ThrustTorque.cs" /> <Compile Include="Flight\Readouts\Vessel\ThrustTorque.cs" />
<Compile Include="GuiDisplaySize.cs" /> <Compile Include="GuiDisplaySize.cs" />
<Compile Include="Helpers\AngleHelper.cs" /> <Compile Include="Helpers\AngleHelper.cs" />
<Compile Include="Helpers\Averager.cs" /> <Compile Include="Helpers\Averager.cs" />
<Compile Include="Helpers\ForceAccumulator.cs" /> <Compile Include="Helpers\ForceAccumulator.cs" />
<Compile Include="Helpers\TextureHelper.cs" /> <Compile Include="Helpers\TextureHelper.cs" />
<Compile Include="Helpers\Units.cs" /> <Compile Include="Helpers\Units.cs" />
<Compile Include="Helpers\TimeFormatter.cs" /> <Compile Include="Helpers\TimeFormatter.cs" />
<Compile Include="KeyBinder.cs" /> <Compile Include="KeyBinder.cs" />
<Compile Include="Control\ControlCentre.cs" /> <Compile Include="Control\ControlCentre.cs" />
<Compile Include="UIControls\DropDown.cs" /> <Compile Include="UIControls\DropDown.cs" />
<Compile Include="Logger.cs" /> <Compile Include="Logger.cs" />
<Compile Include="EngineerGlobals.cs" /> <Compile Include="EngineerGlobals.cs" />
<Compile Include="Extensions\DoubleExtensions.cs" /> <Compile Include="Extensions\DoubleExtensions.cs" />
<Compile Include="Extensions\PartExtensions.cs" /> <Compile Include="Extensions\PartExtensions.cs" />
<Compile Include="Extensions\PartResourceExtensions.cs" /> <Compile Include="Extensions\PartResourceExtensions.cs" />
<Compile Include="Extensions\RectExtensions.cs" /> <Compile Include="Extensions\RectExtensions.cs" />
<Compile Include="Flight\ActionMenu.cs" /> <Compile Include="Flight\ActionMenu.cs" />
<Compile Include="Flight\DisplayStack.cs" /> <Compile Include="Flight\DisplayStack.cs" />
<Compile Include="Flight\FlightEngineerCore.cs" /> <Compile Include="Flight\FlightEngineerCore.cs" />
<Compile Include="Flight\FlightEngineerModule.cs" /> <Compile Include="Flight\FlightEngineerModule.cs" />
<Compile Include="Flight\IUpdatable.cs" /> <Compile Include="Flight\IUpdatable.cs" />
<Compile Include="Flight\IUpdateRequest.cs" /> <Compile Include="Flight\IUpdateRequest.cs" />
<Compile Include="Flight\Readouts\Orbital\ApoapsisHeight.cs" /> <Compile Include="Flight\Readouts\Orbital\ApoapsisHeight.cs" />
<Compile Include="Flight\Readouts\Orbital\Eccentricity.cs" /> <Compile Include="Flight\Readouts\Orbital\Eccentricity.cs" />
<Compile Include="Flight\Readouts\Orbital\Inclination.cs" /> <Compile Include="Flight\Readouts\Orbital\Inclination.cs" />
<Compile Include="Flight\Readouts\Orbital\LongitudeOfAscendingNode.cs" /> <Compile Include="Flight\Readouts\Orbital\LongitudeOfAscendingNode.cs" />
<Compile Include="Flight\Readouts\Orbital\LongitudeOfPeriapsis.cs" /> <Compile Include="Flight\Readouts\Orbital\LongitudeOfPeriapsis.cs" />
<Compile Include="Flight\Readouts\Orbital\OrbitalPeriod.cs" /> <Compile Include="Flight\Readouts\Orbital\OrbitalPeriod.cs" />
<Compile Include="Flight\Readouts\Orbital\OrbitalSpeed.cs" /> <Compile Include="Flight\Readouts\Orbital\OrbitalSpeed.cs" />
<Compile Include="Flight\Readouts\Orbital\PeriapsisHeight.cs" /> <Compile Include="Flight\Readouts\Orbital\PeriapsisHeight.cs" />
<Compile Include="Flight\Readouts\Orbital\SemiMajorAxis.cs" /> <Compile Include="Flight\Readouts\Orbital\SemiMajorAxis.cs" />
<Compile Include="Flight\Readouts\Orbital\SemiMinorAxis.cs" /> <Compile Include="Flight\Readouts\Orbital\SemiMinorAxis.cs" />
<Compile Include="Flight\Readouts\Orbital\TimeToApoapsis.cs" /> <Compile Include="Flight\Readouts\Orbital\TimeToApoapsis.cs" />
<Compile Include="Flight\Readouts\Orbital\TimeToPeriapsis.cs" /> <Compile Include="Flight\Readouts\Orbital\TimeToPeriapsis.cs" />
<Compile Include="Flight\Readouts\ReadoutCategory.cs" /> <Compile Include="Flight\Readouts\ReadoutCategory.cs" />
<Compile Include="Flight\Readouts\ReadoutLibrary.cs" /> <Compile Include="Flight\Readouts\ReadoutLibrary.cs" />
<Compile Include="Flight\Readouts\ReadoutModule.cs" /> <Compile Include="Flight\Readouts\ReadoutModule.cs" />
<Compile Include="Flight\Readouts\Rendezvous\TimeToPeriapsis.cs" /> <Compile Include="Flight\Readouts\Rendezvous\TimeToPeriapsis.cs" />
<Compile Include="Flight\Readouts\Rendezvous\TimeToApoapsis.cs" /> <Compile Include="Flight\Readouts\Rendezvous\TimeToApoapsis.cs" />
<Compile Include="Flight\Readouts\Rendezvous\PeriapsisHeight.cs" /> <Compile Include="Flight\Readouts\Rendezvous\PeriapsisHeight.cs" />
<Compile Include="Flight\Readouts\Rendezvous\ApoapsisHeight.cs" /> <Compile Include="Flight\Readouts\Rendezvous\ApoapsisHeight.cs" />
<Compile Include="Flight\Readouts\Rendezvous\InterceptAngle.cs" /> <Compile Include="Flight\Readouts\Rendezvous\InterceptAngle.cs" />
<Compile Include="Flight\Readouts\Rendezvous\OrbitalPeriod.cs" /> <Compile Include="Flight\Readouts\Rendezvous\OrbitalPeriod.cs" />
<Compile Include="Flight\Readouts\Rendezvous\Distance.cs" /> <Compile Include="Flight\Readouts\Rendezvous\Distance.cs" />
<Compile Include="Flight\Readouts\Rendezvous\AltitudeSeaLevel.cs" /> <Compile Include="Flight\Readouts\Rendezvous\AltitudeSeaLevel.cs" />
<Compile Include="Flight\Readouts\Rendezvous\AngleToRelativeDescendingNode.cs" /> <Compile Include="Flight\Readouts\Rendezvous\AngleToRelativeDescendingNode.cs" />
<Compile Include="Flight\Readouts\Rendezvous\AngleToRelativeAscendingNode.cs" /> <Compile Include="Flight\Readouts\Rendezvous\AngleToRelativeAscendingNode.cs" />
<Compile Include="Flight\Readouts\Rendezvous\PhaseAngle.cs" /> <Compile Include="Flight\Readouts\Rendezvous\PhaseAngle.cs" />
<Compile Include="Flight\Readouts\Rendezvous\RelativeInclination.cs" /> <Compile Include="Flight\Readouts\Rendezvous\RelativeInclination.cs" />
<Compile Include="Flight\Readouts\Rendezvous\RendezvousProcessor.cs" /> <Compile Include="Flight\Readouts\Rendezvous\RendezvousProcessor.cs" />
<Compile Include="Flight\Readouts\Rendezvous\TargetSelector.cs" /> <Compile Include="Flight\Readouts\Rendezvous\TargetSelector.cs" />
<Compile Include="Flight\Readouts\Surface\AltitudeSeaLevel.cs" /> <Compile Include="Flight\Readouts\Surface\AltitudeSeaLevel.cs" />
<Compile Include="Flight\Readouts\Surface\AltitudeTerrain.cs" /> <Compile Include="Flight\Readouts\Surface\AltitudeTerrain.cs" />
<Compile Include="Flight\Readouts\Surface\ImpactLatitude.cs" /> <Compile Include="Flight\Readouts\Surface\ImpactLatitude.cs" />
<Compile Include="Flight\Readouts\Surface\ImpactAltitude.cs" /> <Compile Include="Flight\Readouts\Surface\ImpactAltitude.cs" />
<Compile Include="Flight\Readouts\Surface\ImpactLongitude.cs" /> <Compile Include="Flight\Readouts\Surface\ImpactLongitude.cs" />
<Compile Include="Flight\Readouts\Surface\ImpactTime.cs" /> <Compile Include="Flight\Readouts\Surface\ImpactTime.cs" />
<Compile Include="Flight\Readouts\Surface\AtmosphericProcessor.cs" /> <Compile Include="Flight\Readouts\Surface\AtmosphericProcessor.cs" />
<Compile Include="Flight\Readouts\Surface\AtmosphericEfficiency.cs" /> <Compile Include="Flight\Readouts\Surface\AtmosphericEfficiency.cs" />
<Compile Include="Flight\Readouts\Surface\GeeForce.cs" /> <Compile Include="Flight\Readouts\Surface\GeeForce.cs" />
<Compile Include="Flight\Readouts\Surface\HorizontalSpeed.cs" /> <Compile Include="Flight\Readouts\Surface\HorizontalSpeed.cs" />
<Compile Include="Flight\Readouts\Surface\ImpactProcessor.cs" /> <Compile Include="Flight\Readouts\Surface\ImpactProcessor.cs" />
<Compile Include="Flight\Readouts\Surface\Latitude.cs" /> <Compile Include="Flight\Readouts\Surface\Latitude.cs" />
<Compile Include="Flight\Readouts\Surface\Longitude.cs" /> <Compile Include="Flight\Readouts\Surface\Longitude.cs" />
<Compile Include="Flight\Readouts\Surface\TerminalVelocity.cs" /> <Compile Include="Flight\Readouts\Surface\TerminalVelocity.cs" />
<Compile Include="Flight\Readouts\Surface\VerticalSpeed.cs" /> <Compile Include="Flight\Readouts\Surface\VerticalSpeed.cs" />
<Compile Include="Flight\Readouts\Vessel\DeltaVStaged.cs" /> <Compile Include="Flight\Readouts\Vessel\DeltaVStaged.cs" />
<Compile Include="Flight\Readouts\Vessel\DeltaVTotal.cs" /> <Compile Include="Flight\Readouts\Vessel\DeltaVTotal.cs" />
<Compile Include="Flight\Readouts\Vessel\Mass.cs" /> <Compile Include="Flight\Readouts\Vessel\Mass.cs" />
<Compile Include="Flight\Readouts\Vessel\Thrust.cs" /> <Compile Include="Flight\Readouts\Vessel\Thrust.cs" />
<Compile Include="Flight\Readouts\Vessel\SpecificImpulse.cs" /> <Compile Include="Flight\Readouts\Vessel\SpecificImpulse.cs" />
<Compile Include="Flight\Readouts\Vessel\ThrustToWeight.cs" /> <Compile Include="Flight\Readouts\Vessel\ThrustToWeight.cs" />
<Compile Include="Flight\Sections\SectionEditor.cs" /> <Compile Include="Flight\Sections\SectionEditor.cs" />
<Compile Include="Flight\Sections\SectionLibrary.cs" /> <Compile Include="Flight\Sections\SectionLibrary.cs" />
<Compile Include="Flight\Sections\SectionModule.cs" /> <Compile Include="Flight\Sections\SectionModule.cs" />
<Compile Include="Flight\Sections\SectionWindow.cs" /> <Compile Include="Flight\Sections\SectionWindow.cs" />
<Compile Include="LogMsg.cs" /> <Compile Include="LogMsg.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Settings\SettingHandler.cs" /> <Compile Include="Settings\SettingHandler.cs" />
<Compile Include="Settings\SettingItem.cs" /> <Compile Include="Settings\SettingItem.cs" />
<Compile Include="TapeDriveAnimator.cs" /> <Compile Include="TapeDriveAnimator.cs" />
<Compile Include="UIControls\WindowObject.cs" /> <Compile Include="UIControls\WindowObject.cs" />
<Compile Include="VesselSimulator\AttachNodeSim.cs" /> <Compile Include="VesselSimulator\AttachNodeSim.cs" />
<Compile Include="VesselSimulator\EngineSim.cs" /> <Compile Include="VesselSimulator\EngineSim.cs" />
  <Compile Include="Helpers\Pool.cs" />
<Compile Include="VesselSimulator\PartSim.cs" /> <Compile Include="VesselSimulator\PartSim.cs" />
<Compile Include="VesselSimulator\ResourceContainer.cs" /> <Compile Include="VesselSimulator\ResourceContainer.cs" />
<Compile Include="VesselSimulator\SimManager.cs" /> <Compile Include="VesselSimulator\SimManager.cs" />
<Compile Include="VesselSimulator\Simulation.cs" /> <Compile Include="VesselSimulator\Simulation.cs" />
<Compile Include="VesselSimulator\Stage.cs" /> <Compile Include="VesselSimulator\Stage.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Assembly-CSharp"> <Reference Include="Assembly-CSharp">
<HintPath>..\Game\KSP_Data\Managed\Assembly-CSharp.dll</HintPath> <HintPath>..\Game\KSP_Data\Managed\Assembly-CSharp.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
<Reference Include="System"> <Reference Include="System">
<HintPath>..\Game\KSP_Data\Managed\System.dll</HintPath> <HintPath>..\Game\KSP_Data\Managed\System.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
<Reference Include="System.Xml"> <Reference Include="System.Xml">
<HintPath>..\Game\KSP_Data\Managed\System.Xml.dll</HintPath> <HintPath>..\Game\KSP_Data\Managed\System.Xml.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
<Reference Include="UnityEngine"> <Reference Include="UnityEngine">
<HintPath>..\Game\KSP_Data\Managed\UnityEngine.dll</HintPath> <HintPath>..\Game\KSP_Data\Managed\UnityEngine.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="PostBuildMacros"> <Target Name="PostBuildMacros">
<GetAssemblyIdentity AssemblyFiles="$(TargetPath)"> <GetAssemblyIdentity AssemblyFiles="$(TargetPath)">
<Output TaskParameter="Assemblies" ItemName="Targets" /> <Output TaskParameter="Assemblies" ItemName="Targets" />
</GetAssemblyIdentity> </GetAssemblyIdentity>
<ItemGroup> <ItemGroup>
<VersionNumber Include="@(Targets->'%(Version)')" /> <VersionNumber Include="@(Targets->'%(Version)')" />
</ItemGroup> </ItemGroup>
</Target> </Target>
<PropertyGroup> <PropertyGroup>
<PostBuildEventDependsOn> <PostBuildEventDependsOn>
$(PostBuildEventDependsOn); $(PostBuildEventDependsOn);
PostBuildMacros; PostBuildMacros;
</PostBuildEventDependsOn> </PostBuildEventDependsOn>
<PostBuildEvent>xcopy "$(SolutionDir)Output\*" "$(SolutionDir)Game\GameData\*" /E /Y <PostBuildEvent>xcopy "$(SolutionDir)Output\*" "$(SolutionDir)Game\GameData\*" /E /Y
del "$(SolutionDir)Release\*" /Q del "$(SolutionDir)Release\*" /Q
xcopy "$(SolutionDir)Documents\*" "$(SolutionDir)Release\Documents\*" /E /Y xcopy "$(SolutionDir)Documents\*" "$(SolutionDir)Release\Documents\*" /E /Y
7z.exe a -tzip -mx3 "$(SolutionDir)Release\$(ProjectName)-@(VersionNumber).zip" "$(SolutionDir)Output\*" 7z.exe a -tzip -mx3 "$(SolutionDir)Release\$(ProjectName)-@(VersionNumber).zip" "$(SolutionDir)Output\*"
7z.exe a -tzip -mx3 "$(SolutionDir)Release\$(ProjectName)-@(VersionNumber).zip" "$(SolutionDir)Documents\*"</PostBuildEvent> 7z.exe a -tzip -mx3 "$(SolutionDir)Release\$(ProjectName)-@(VersionNumber).zip" "$(SolutionDir)Documents\*"</PostBuildEvent>
</PropertyGroup> </PropertyGroup>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">
</Target> </Target>
<Target Name="AfterBuild"> <Target Name="AfterBuild">
</Target> </Target>
--> -->
</Project> </Project>
// //
// Kerbal Engineer Redux // Kerbal Engineer Redux
// //
// Copyright (C) 2014 CYBUTEK // Copyright (C) 2014 CYBUTEK
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
// //
   
#region Using Directives #region Using Directives
   
using System;  
using System.Text;  
   
#endregion #endregion
   
namespace KerbalEngineer.VesselSimulator namespace KerbalEngineer.VesselSimulator
{ {
internal class AttachNodeSim using System;
  using System.Text;
   
  internal class AttachNodeSim : Pool<AttachNodeSim>
{ {
public PartSim attachedPartSim; public PartSim attachedPartSim;
public String id; public String id;
public AttachNode.NodeType nodeType; public AttachNode.NodeType nodeType;
   
public AttachNodeSim(PartSim partSim, String newId, AttachNode.NodeType newNodeType)  
{  
this.attachedPartSim = partSim;  
this.nodeType = newNodeType;  
this.id = newId;  
}  
   
public void DumpToBuffer(StringBuilder buffer) public void DumpToBuffer(StringBuilder buffer)
{ {
if (this.attachedPartSim == null) if (attachedPartSim == null)
{ {
buffer.Append("<staged>:<n>"); buffer.Append("<staged>:<n>");
} }
else else
{ {
buffer.Append(this.attachedPartSim.name); buffer.Append(attachedPartSim.name);
buffer.Append(":"); buffer.Append(":");
buffer.Append(this.attachedPartSim.partId); buffer.Append(attachedPartSim.partId);
} }
buffer.Append("#"); buffer.Append("#");
buffer.Append(this.nodeType); buffer.Append(nodeType);
buffer.Append(":"); buffer.Append(":");
buffer.Append(this.id); buffer.Append(id);
  }
   
  public AttachNodeSim Initialise(PartSim partSim, String newId, AttachNode.NodeType newNodeType)
  {
  attachedPartSim = partSim;
  nodeType = newNodeType;
  id = newId;
   
  return this;
} }
} }
} }
// //
// 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.Text; using System.Text;
using Editor; using Editor;
using Helpers; using Helpers;
using UnityEngine; using UnityEngine;
   
public class EngineSim public class EngineSim : Pool<EngineSim>
{ {
public double actualThrust = 0; public double actualThrust = 0.0;
public List<AppliedForce> appliedForces; public List<AppliedForce> appliedForces = new List<AppliedForce>();
public bool isActive = false; public bool isActive = false;
public double isp = 0; public double isp = 0;
public PartSim partSim; public PartSim partSim;
public float maxMach; public float maxMach;
   
public double thrust = 0; public double thrust = 0;
   
// Add thrust vector to account for directional losses // Add thrust vector to account for directional losses
public Vector3 thrustVec; public Vector3 thrustVec;
private readonly ResourceContainer resourceConsumptions = new ResourceContainer(); private readonly ResourceContainer resourceConsumptions = new ResourceContainer();
   
public EngineSim(PartSim theEngine, public EngineSim Initialise(PartSim theEngine,
double atmosphere, double atmosphere,
float machNumber, float machNumber,
float maxFuelFlow, float maxFuelFlow,
float minFuelFlow, float minFuelFlow,
float thrustPercentage, float thrustPercentage,
Vector3 vecThrust, Vector3 vecThrust,
FloatCurve atmosphereCurve, FloatCurve atmosphereCurve,
bool atmChangeFlow, bool atmChangeFlow,
FloatCurve atmCurve, FloatCurve atmCurve,
FloatCurve velCurve, FloatCurve velCurve,
float currentThrottle, float currentThrottle,
bool throttleLocked, bool throttleLocked,
List<Propellant> propellants, List<Propellant> propellants,
bool active, bool active,
float resultingThrust, float resultingThrust,
List<Transform> thrustTransforms) List<Transform> thrustTransforms)
{ {
StringBuilder buffer = null; StringBuilder buffer = null;
   
  isp = 0.0;
  maxMach = 0.0f;
  actualThrust = 0.0;
partSim = theEngine; partSim = theEngine;
isActive = active; isActive = active;
thrustVec = vecThrust; thrustVec = vecThrust;
  resourceConsumptions.Reset();
  appliedForces.Clear();
   
double flowRate = 0.0; double flowRate = 0.0;
if (partSim.hasVessel) if (partSim.hasVessel)
{ {
float flowModifier = GetFlowModifier(atmChangeFlow, atmCurve, partSim.part.atmDensity, velCurve, machNumber, ref maxMach); float flowModifier = GetFlowModifier(atmChangeFlow, atmCurve, partSim.part.atmDensity, velCurve, machNumber, ref maxMach);
isp = atmosphereCurve.Evaluate((float)atmosphere); isp = atmosphereCurve.Evaluate((float)atmosphere);
thrust = GetThrust(Mathf.Lerp(minFuelFlow, maxFuelFlow, GetThrustPercent(thrustPercentage)) * flowModifier, isp); thrust = GetThrust(Mathf.Lerp(minFuelFlow, maxFuelFlow, GetThrustPercent(thrustPercentage)) * flowModifier, isp);
actualThrust = isActive ? resultingThrust : 0.0; actualThrust = isActive ? resultingThrust : 0.0;
   
if (throttleLocked) if (throttleLocked)
{ {
flowRate = GetFlowRate(thrust, isp); flowRate = GetFlowRate(thrust, isp);
} }
else else
{ {
if (currentThrottle > 0.0f && partSim.isLanded == false) if (currentThrottle > 0.0f && partSim.isLanded == false)
{ {
flowRate = GetFlowRate(actualThrust, isp); flowRate = GetFlowRate(actualThrust, isp);
} }
else else
{ {
flowRate = GetFlowRate(thrust, isp); flowRate = GetFlowRate(thrust, isp);
} }
} }
} }
else else
{ {
float flowModifier = GetFlowModifier(atmChangeFlow, atmCurve, CelestialBodies.SelectedBody.GetDensity(BuildAdvanced.Altitude), velCurve, machNumber, ref maxMach); float flowModifier = GetFlowModifier(atmChangeFlow, atmCurve, CelestialBodies.SelectedBody.GetDensity(BuildAdvanced.Altitude), velCurve, machNumber, ref maxMach);
isp = atmosphereCurve.Evaluate((float)atmosphere); isp = atmosphereCurve.Evaluate((float)atmosphere);
thrust = GetThrust(Mathf.Lerp(minFuelFlow, maxFuelFlow, GetThrustPercent(thrustPercentage)) * flowModifier, isp); thrust = GetThrust(Mathf.Lerp(minFuelFlow, maxFuelFlow, GetThrustPercent(thrustPercentage)) * flowModifier, isp);
flowRate = GetFlowRate(thrust, isp); flowRate = GetFlowRate(thrust, isp);
} }
   
if (SimManager.logOutput) float flowMass = 0.0f;
{  
buffer = new StringBuilder(1024); for (int i = 0; i < propellants.Count; ++i)
buffer.AppendFormat("flowRate = {0:g6}\n", flowRate); {
} Propellant propellant = propellants[i];
   
float flowMass = 0f;  
foreach (Propellant propellant in propellants)  
{  
flowMass += propellant.ratio * ResourceContainer.GetResourceDensity(propellant.id); flowMass += propellant.ratio * ResourceContainer.GetResourceDensity(propellant.id);
}  
   
if (SimManager.logOutput)  
{  
buffer.AppendFormat("flowMass = {0:g6}\n", flowMass);  
}  
   
foreach (Propellant propellant in propellants)  
{  
if (propellant.name == "ElectricCharge" || propellant.name == "IntakeAir")  
{  
continue;  
}  
   
double consumptionRate = propellant.ratio * flowRate / flowMass; double consumptionRate = propellant.ratio * flowRate / flowMass;
if (SimManager.logOutput)  
{  
buffer.AppendFormat("Add consumption({0}, {1}:{2:d}) = {3:g6}\n", ResourceContainer.GetResourceName(propellant.id), theEngine.name, theEngine.partId, consumptionRate);  
}  
resourceConsumptions.Add(propellant.id, consumptionRate); resourceConsumptions.Add(propellant.id, consumptionRate);
} }
   
if (SimManager.logOutput)  
{  
MonoBehaviour.print(buffer);  
}  
   
appliedForces = new List<AppliedForce>();  
double thrustPerThrustTransform = thrust / thrustTransforms.Count; double thrustPerThrustTransform = thrust / thrustTransforms.Count;
foreach (Transform thrustTransform in thrustTransforms) for (int i = 0; i < thrustTransforms.Count; ++i)
{ {
  Transform thrustTransform = thrustTransforms[i];
   
Vector3d direction = thrustTransform.forward.normalized; Vector3d direction = thrustTransform.forward.normalized;
Vector3d position = thrustTransform.position; Vector3d position = thrustTransform.position;
appliedForces.Add(new AppliedForce(direction * thrustPerThrustTransform, position)); appliedForces.Add(new AppliedForce(direction * thrustPerThrustTransform, position));
} }
   
  return this;
} }
   
public ResourceContainer ResourceConsumptions public ResourceContainer ResourceConsumptions
{ {
get get
{ {
return resourceConsumptions; return resourceConsumptions;
} }
} }
   
public static double GetExhaustVelocity(double isp) public static double GetExhaustVelocity(double isp)
{ {
return isp * Units.GRAVITY; return isp * Units.GRAVITY;
} }
   
public static float GetFlowModifier(bool atmChangeFlow, FloatCurve atmCurve, double atmDensity, FloatCurve velCurve, float machNumber, ref float maxMach) public static float GetFlowModifier(bool atmChangeFlow, FloatCurve atmCurve, double atmDensity, FloatCurve velCurve, float machNumber, ref float maxMach)
{ {
float flowModifier = 1.0f; float flowModifier = 1.0f;
if (atmChangeFlow) if (atmChangeFlow)
{ {
flowModifier = (float)(atmDensity / 1.225); flowModifier = (float)(atmDensity / 1.225);
if (atmCurve != null) if (atmCurve != null)
{ {
flowModifier = atmCurve.Evaluate(flowModifier); flowModifier = atmCurve.Evaluate(flowModifier);
} }
} }
if (velCurve != null) if (velCurve != null)
{ {
flowModifier = flowModifier * velCurve.Evaluate(machNumber); flowModifier = flowModifier * velCurve.Evaluate(machNumber);
maxMach = velCurve.maxTime; maxMach = velCurve.maxTime;
} }
if (flowModifier < float.Epsilon) if (flowModifier < float.Epsilon)
{ {
flowModifier = float.Epsilon; flowModifier = float.Epsilon;
} }
return flowModifier; return flowModifier;
} }
   
public static double GetFlowRate(double thrust, double isp) public static double GetFlowRate(double thrust, double isp)
{ {
return thrust / GetExhaustVelocity(isp); return thrust / GetExhaustVelocity(isp);
} }
   
public static float GetThrottlePercent(float currentThrottle, float thrustPercentage) public static float GetThrottlePercent(float currentThrottle, float thrustPercentage)
{ {
return currentThrottle * GetThrustPercent(thrustPercentage); return currentThrottle * GetThrustPercent(thrustPercentage);
} }
   
public static double GetThrust(double flowRate, double isp) public static double GetThrust(double flowRate, double isp)
{ {
return flowRate * GetExhaustVelocity(isp); return flowRate * GetExhaustVelocity(isp);
} }
   
public static float GetThrustPercent(float thrustPercentage) public static float GetThrustPercent(float thrustPercentage)
{ {
return thrustPercentage * 0.01f; return thrustPercentage * 0.01f;
} }
   
public void DumpEngineToBuffer(StringBuilder buffer, String prefix) public void DumpEngineToBuffer(StringBuilder buffer, String prefix)
{ {
buffer.Append(prefix); buffer.Append(prefix);
buffer.AppendFormat("[thrust = {0:g6}, actual = {1:g6}, isp = {2:g6}\n", thrust, actualThrust, isp); buffer.AppendFormat("[thrust = {0:g6}, actual = {1:g6}, isp = {2:g6}\n", thrust, actualThrust, isp);
} }
   
public bool SetResourceDrains(List<PartSim> allParts, List<PartSim> allFuelLines, HashSet<PartSim> drainingParts) public bool SetResourceDrains(List<PartSim> allParts, List<PartSim> allFuelLines, HashSet<PartSim> drainingParts)
{ {
LogMsg log = null; LogMsg log = null;
   
// A dictionary to hold a set of parts for each resource // A dictionary to hold a set of parts for each resource
Dictionary<int, HashSet<PartSim>> sourcePartSets = new Dictionary<int, HashSet<PartSim>>(); Dictionary<int, HashSet<PartSim>> sourcePartSets = new Dictionary<int, HashSet<PartSim>>();
   
foreach (int type in resourceConsumptions.Types) for (int i = 0; i < resourceConsumptions.Types.Count; ++i)
{ {
  int type = resourceConsumptions.Types[i];
HashSet<PartSim> sourcePartSet = null; HashSet<PartSim> sourcePartSet = null;
switch (ResourceContainer.GetResourceFlowMode(type)) switch (ResourceContainer.GetResourceFlowMode(type))
{ {
case ResourceFlowMode.NO_FLOW: case ResourceFlowMode.NO_FLOW:
if (partSim.resources[type] > SimManager.RESOURCE_MIN && partSim.resourceFlowStates[type] != 0) if (partSim.resources[type] > SimManager.RESOURCE_MIN && partSim.resourceFlowStates[type] != 0)
{ {
sourcePartSet = new HashSet<PartSim>(); sourcePartSet = new HashSet<PartSim>();
//MonoBehaviour.print("SetResourceDrains(" + name + ":" + partId + ") setting sources to just this"); //MonoBehaviour.print("SetResourceDrains(" + name + ":" + partId + ") setting sources to just this");
sourcePartSet.Add(partSim); sourcePartSet.Add(partSim);
} }
break; break;
   
case ResourceFlowMode.ALL_VESSEL: case ResourceFlowMode.ALL_VESSEL:
foreach (PartSim aPartSim in allParts) for (int j = 0; j < allParts.Count; ++j)
{ {
  PartSim aPartSim = allParts[j];
   
if (aPartSim.resources[type] > SimManager.RESOURCE_MIN && aPartSim.resourceFlowStates[type] != 0) if (aPartSim.resources[type] > SimManager.RESOURCE_MIN && aPartSim.resourceFlowStates[type] != 0)
{ {
if (sourcePartSet == null) if (sourcePartSet == null)
{ {
sourcePartSet = new HashSet<PartSim>(); sourcePartSet = new HashSet<PartSim>();
} }
   
sourcePartSet.Add(aPartSim); sourcePartSet.Add(aPartSim);
} }
} }
break; break;
   
case ResourceFlowMode.STAGE_PRIORITY_FLOW: case ResourceFlowMode.STAGE_PRIORITY_FLOW:
Dictionary<int, HashSet<PartSim>> stagePartSets = new Dictionary<int, HashSet<PartSim>>(); Dictionary<int, HashSet<PartSim>> stagePartSets = new Dictionary<int, HashSet<PartSim>>();
int maxStage = -1; int maxStage = -1;
   
//Logger.Log(type); for (int j = 0; j < allParts.Count; ++j)
foreach (PartSim aPartSim in allParts) {
{ PartSim aPartSim = allParts[j];
   
if (aPartSim.resources[type] <= SimManager.RESOURCE_MIN || aPartSim.resourceFlowStates[type] == 0) if (aPartSim.resources[type] <= SimManager.RESOURCE_MIN || aPartSim.resourceFlowStates[type] == 0)
{ {
continue; continue;
} }
   
int stage = aPartSim.DecouplerCount(); int stage = aPartSim.DecouplerCount();
if (stage > maxStage) if (stage > maxStage)
{ {
maxStage = stage; maxStage = stage;
} }
   
if (!stagePartSets.TryGetValue(stage, out sourcePartSet)) if (!stagePartSets.TryGetValue(stage, out sourcePartSet))
{ {
sourcePartSet = new HashSet<PartSim>(); sourcePartSet = new HashSet<PartSim>();
stagePartSets.Add(stage, sourcePartSet); stagePartSets.Add(stage, sourcePartSet);
} }
sourcePartSet.Add(aPartSim); sourcePartSet.Add(aPartSim);
} }
   
for (int i = 0; i <= maxStage; i++) for (int j = 0; j <= maxStage; j++)
{ {
HashSet<PartSim> stagePartSet; HashSet<PartSim> stagePartSet;
if (stagePartSets.TryGetValue(i, out stagePartSet) && stagePartSet.Count > 0) if (stagePartSets.TryGetValue(j, out stagePartSet) && stagePartSet.Count > 0)
{ {
sourcePartSet = stagePartSet; sourcePartSet = stagePartSet;
} }
} }
break; break;
   
case ResourceFlowMode.STACK_PRIORITY_SEARCH: case ResourceFlowMode.STACK_PRIORITY_SEARCH:
HashSet<PartSim> visited = new HashSet<PartSim>(); HashSet<PartSim> visited = new HashSet<PartSim>();
   
if (SimManager.logOutput) if (SimManager.logOutput)
{ {
log = new LogMsg(); log = new LogMsg();
log.buf.AppendLine("Find " + ResourceContainer.GetResourceName(type) + " sources for " + partSim.name + ":" + partSim.partId); log.buf.AppendLine("Find " + ResourceContainer.GetResourceName(type) + " sources for " + partSim.name + ":" + partSim.partId);
} }
sourcePartSet = partSim.GetSourceSet(type, allParts, visited, log, ""); sourcePartSet = partSim.GetSourceSet(type, allParts, visited, log, "");
if (SimManager.logOutput) if (SimManager.logOutput)
{ {
MonoBehaviour.print(log.buf); MonoBehaviour.print(log.buf);
} }
break; break;
   
default: default:
MonoBehaviour.print("SetResourceDrains(" + partSim.name + ":" + partSim.partId + ") Unexpected flow type for " + ResourceContainer.GetResourceName(type) + ")"); MonoBehaviour.print("SetResourceDrains(" + partSim.name + ":" + partSim.partId + ") Unexpected flow type for " + ResourceContainer.GetResourceName(type) + ")");
break; break;
} }
   
if (sourcePartSet != null && sourcePartSet.Count > 0) if (sourcePartSet != null && sourcePartSet.Count > 0)
{ {
sourcePartSets[type] = sourcePartSet; sourcePartSets[type] = sourcePartSet;
if (SimManager.logOutput) if (SimManager.logOutput)
{ {
log = new LogMsg(); log = new LogMsg();
log.buf.AppendLine("Source parts for " + ResourceContainer.GetResourceName(type) + ":"); log.buf.AppendLine("Source parts for " + ResourceContainer.GetResourceName(type) + ":");
foreach (PartSim partSim in sourcePartSet) foreach (PartSim partSim in sourcePartSet)
{ {
log.buf.AppendLine(partSim.name + ":" + partSim.partId); log.buf.AppendLine(partSim.name + ":" + partSim.partId);
} }
MonoBehaviour.print(log.buf); MonoBehaviour.print(log.buf);
} }
} }
} }
   
// If we don't have sources for all the needed resources then return false without setting up any drains // If we don't have sources for all the needed resources then return false without setting up any drains
foreach (int type in resourceConsumptions.Types) for(int i = 0; i < resourceConsumptions.Types.Count; ++i)
{ {
  int type = resourceConsumptions.Types[i];
   
if (!sourcePartSets.ContainsKey(type)) if (!sourcePartSets.ContainsKey(type))
{ {
if (SimManager.logOutput) if (SimManager.logOutput)
{ {
MonoBehaviour.print("No source of " + ResourceContainer.GetResourceName(type)); MonoBehaviour.print("No source of " + ResourceContainer.GetResourceName(type));
} }
   
isActive = false; isActive = false;
return false; return false;
} }
} }
   
// Now we set the drains on the members of the sets and update the draining parts set // Now we set the drains on the members of the sets and update the draining parts set
foreach (int type in resourceConsumptions.Types) for (int i = 0; i < resourceConsumptions.Types.Count; ++i)
{ {
  int type = resourceConsumptions.Types[i];
   
HashSet<PartSim> sourcePartSet = sourcePartSets[type]; HashSet<PartSim> sourcePartSet = sourcePartSets[type];
// Loop through the members of the set // Loop through the members of the set
double amount = resourceConsumptions[type] / sourcePartSet.Count; double amount = resourceConsumptions[type] / sourcePartSet.Count;
foreach (PartSim partSim in sourcePartSet) foreach (PartSim partSim in sourcePartSet)
{ {
if (SimManager.logOutput) if (SimManager.logOutput)
{ {
MonoBehaviour.print("Adding drain of " + amount + " " + ResourceContainer.GetResourceName(type) + " to " + partSim.name + ":" + partSim.partId); MonoBehaviour.print("Adding drain of " + amount + " " + ResourceContainer.GetResourceName(type) + " to " + partSim.name + ":" + partSim.partId);
} }
   
partSim.resourceDrains.Add(type, amount); partSim.resourceDrains.Add(type, amount);
drainingParts.Add(partSim); drainingParts.Add(partSim);
} }
} }
   
return true; return true;
} }
} }
} }
// //
// Kerbal Engineer Redux // Kerbal Engineer Redux
// //
// Copyright (C) 2014 CYBUTEK // Copyright (C) 2014 CYBUTEK
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
// //
   
#region Using Directives #region Using Directives
   
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
   
using KerbalEngineer.Extensions; using KerbalEngineer.Extensions;
   
using UnityEngine; using UnityEngine;
   
#endregion #endregion
   
namespace KerbalEngineer.VesselSimulator namespace KerbalEngineer.VesselSimulator
{ {
using CompoundParts; using CompoundParts;
   
public class PartSim public class PartSim : Pool<PartSim>
{ {
private readonly List<AttachNodeSim> attachNodes = new List<AttachNodeSim>(); private readonly List<AttachNodeSim> attachNodes = new List<AttachNodeSim>();
public Vector3d centerOfMass; public Vector3d centerOfMass;
public double baseMass = 0d; public double baseMass;
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 localCorrectThrust; public bool localCorrectThrust;
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;
   
public PartSim(Part thePart, int id, double atmosphere, LogMsg log) private static void Reset(PartSim partSim)
{ {
  partSim.attachNodes.Clear();
  partSim.fuelTargets.Clear();
  partSim.resourceDrains.Reset();
  partSim.resourceFlowStates.Reset();
  partSim.resources.Reset();
  partSim.baseMass = 0.0;
  partSim.startMass = 0.0;
  partSim.centerOfMass = Vector3d.zero;
  partSim.cost = 0.0;
  partSim.decoupledInStage = 0;
  partSim.fuelCrossFeed = false;
  partSim.hasModuleEngines = false;
  partSim.hasModuleEnginesFX = false;
  partSim.hasMultiModeEngine = false;
  partSim.hasVessel = false;
  partSim.initialVesselName = null;
  partSim.inverseStage = 0;
  partSim.isDecoupler = false;
  partSim.isEngine = false;
  partSim.isFuelLine = false;
  partSim.isFuelTank = false;
  partSim.isLanded = false;
  partSim.isNoPhysics = false;
  partSim.isSepratron = false;
  partSim.localCorrectThrust = false;
  partSim.name = null;
  partSim.noCrossFeedNodeKey = null;
  partSim.parent = null;
  partSim.parentAttach = AttachModes.SRF_ATTACH;
  partSim.part = null;
  partSim.partId = 0;
  partSim.vesselName = null;
  partSim.vesselType = VesselType.Base;
  }
   
  public PartSim Initialise(Part thePart, int id, double atmosphere, LogMsg log)
  {
  Reset(this);
   
this.part = thePart; this.part = thePart;
this.centerOfMass = thePart.transform.TransformPoint(thePart.CoMOffset); this.centerOfMass = thePart.transform.TransformPoint(thePart.CoMOffset);
this.partId = id; this.partId = id;
this.name = this.part.partInfo.name; this.name = this.part.partInfo.name;
   
if (log != null) if (log != null)
{ {
log.buf.AppendLine("Create PartSim for " + this.name); log.buf.AppendLine("Create PartSim for " + this.name);
} }
   
this.parent = null; this.parent = null;
this.parentAttach = part.attachMode; this.parentAttach = part.attachMode;
this.fuelCrossFeed = this.part.fuelCrossFeed; this.fuelCrossFeed = this.part.fuelCrossFeed;
this.noCrossFeedNodeKey = this.part.NoCrossFeedNodeKey; this.noCrossFeedNodeKey = this.part.NoCrossFeedNodeKey;
this.decoupledInStage = this.DecoupledInStage(this.part); this.decoupledInStage = this.DecoupledInStage(this.part);
this.isFuelLine = this.part.HasModule<CModuleFuelLine>(); this.isFuelLine = this.part.HasModule<CModuleFuelLine>();
this.isFuelTank = this.part is FuelTank; this.isFuelTank = this.part is FuelTank;
this.isSepratron = this.IsSepratron(); this.isSepratron = this.IsSepratron();
this.inverseStage = this.part.inverseStage; this.inverseStage = this.part.inverseStage;
//MonoBehaviour.print("inverseStage = " + inverseStage); //MonoBehaviour.print("inverseStage = " + inverseStage);
   
this.cost = this.part.GetCostWet(); this.cost = this.part.GetCostWet();
   
// Work out if the part should have no physical significance // Work out if the part should have no physical significance
this.isNoPhysics = this.part.HasModule<LaunchClamp>() || this.isNoPhysics = this.part.HasModule<LaunchClamp>();
this.part.physicalSignificance == Part.PhysicalSignificance.NONE ||  
this.part.PhysicsSignificance == 1; if (isNoPhysics == false)
  {
if (!this.isNoPhysics) baseMass = part.mass;
{  
this.baseMass = this.part.mass;  
} }
   
if (SimManager.logOutput) if (SimManager.logOutput)
{ {
MonoBehaviour.print((this.isNoPhysics ? "Ignoring" : "Using") + " part.mass of " + this.part.mass); MonoBehaviour.print((this.isNoPhysics ? "Ignoring" : "Using") + " part.mass of " + this.part.mass);
} }
   
foreach (PartResource resource in this.part.Resources) for (int i = 0; i < part.Resources.Count; ++i)
{ {
  PartResource resource = 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 (SimManager.logOutput)
{ {
MonoBehaviour.print(resource.resourceName + " = " + resource.amount); MonoBehaviour.print(resource.resourceName + " = " + resource.amount);
} }
   
this.resources.Add(resource.info.id, resource.amount); this.resources.Add(resource.info.id, resource.amount);
this.resourceFlowStates.Add(resource.info.id, resource.flowState ? 1 : 0); this.resourceFlowStates.Add(resource.info.id, resource.flowState ? 1 : 0);
} }
else else
{ {
MonoBehaviour.print(resource.resourceName + " is NaN. Skipping."); MonoBehaviour.print(resource.resourceName + " is NaN. Skipping.");
} }
} }
   
this.startMass = this.GetMass(); this.startMass = this.GetMass();
   
this.hasVessel = (this.part.vessel != null); this.hasVessel = (this.part.vessel != null);
this.isLanded = this.hasVessel && this.part.vessel.Landed; this.isLanded = this.hasVessel && this.part.vessel.Landed;
if (this.hasVessel) if (this.hasVessel)
{ {
this.vesselName = this.part.vessel.vesselName; this.vesselName = this.part.vessel.vesselName;
this.vesselType = this.part.vesselType; this.vesselType = this.part.vesselType;
} }
this.initialVesselName = this.part.initialVesselName; this.initialVesselName = this.part.initialVesselName;
   
this.hasMultiModeEngine = this.part.HasModule<MultiModeEngine>(); this.hasMultiModeEngine = this.part.HasModule<MultiModeEngine>();
this.hasModuleEnginesFX = this.part.HasModule<ModuleEnginesFX>(); this.hasModuleEnginesFX = this.part.HasModule<ModuleEnginesFX>();
this.hasModuleEngines = this.part.HasModule<ModuleEngines>(); this.hasModuleEngines = this.part.HasModule<ModuleEngines>();
   
this.isEngine = this.hasMultiModeEngine || this.hasModuleEnginesFX || this.hasModuleEngines; this.isEngine = this.hasMultiModeEngine || this.hasModuleEnginesFX || this.hasModuleEngines;
   
if (SimManager.logOutput) if (SimManager.logOutput)
{ {
MonoBehaviour.print("Created " + this.name + ". Decoupled in stage " + this.decoupledInStage); MonoBehaviour.print("Created " + this.name + ". Decoupled in stage " + this.decoupledInStage);
} }
   
  return this;
} }
   
public ResourceContainer Resources public ResourceContainer Resources
{ {
get { return this.resources; } get { return this.resources; }
} }
   
public ResourceContainer ResourceDrains public ResourceContainer ResourceDrains
{ {
get { return this.resourceDrains; } get { return this.resourceDrains; }
} }
   
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(this.part); bool correctThrust = SimManager.DoesEngineUseCorrectedThrust(this.part);
if (log != null) if (log != null)
{ {
log.buf.AppendLine("CreateEngineSims for " + this.name); log.buf.AppendLine("CreateEngineSims for " + this.name);
   
foreach (PartModule partMod in this.part.Modules) foreach (PartModule partMod in this.part.Modules)
{ {
log.buf.AppendLine("Module: " + partMod.moduleName); log.buf.AppendLine("Module: " + partMod.moduleName);
} }
   
log.buf.AppendLine("correctThrust = " + correctThrust); log.buf.AppendLine("correctThrust = " + correctThrust);
} }
   
if (this.hasMultiModeEngine) if (this.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 = this.part.GetModule<MultiModeEngine>().mode; string mode = this.part.GetModule<MultiModeEngine>().mode;
   
foreach (ModuleEnginesFX engine in this.part.GetModules<ModuleEnginesFX>()) List<ModuleEnginesFX> engines = part.GetModules<ModuleEnginesFX>();
{  
  for (int i = 0; i < engines.Count; ++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 = new EngineSim(this, EngineSim engineSim = EngineSim.GetPoolObject().Initialise(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.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 (this.hasModuleEngines) if (this.hasModuleEngines)
{ {
foreach (ModuleEngines engine in this.part.GetModules<ModuleEngines>()) List<ModuleEngines> engines = part.GetModules<ModuleEngines>();
{ for (int i = 0; i < engines.Count; ++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 = new EngineSim(this, EngineSim engineSim = EngineSim.GetPoolObject().Initialise(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.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();
} }
} }
   
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;
foreach (Transform trans in thrustTransforms) for (int i = 0; i < thrustTransforms.Count; ++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;
} }
   
public void SetupParent(Dictionary<Part, PartSim> partSimLookup, LogMsg log) public void SetupParent(Dictionary<Part, PartSim> partSimLookup, LogMsg log)
{ {
if (this.part.parent != null) if (this.part.parent != null)
{ {
this.parent = null; this.parent = null;
if (partSimLookup.TryGetValue(this.part.parent, out this.parent)) if (partSimLookup.TryGetValue(this.part.parent, out this.parent))
{ {
if (log != null) if (log != null)
{ {
log.buf.AppendLine("Parent part is " + this.parent.name + ":" + this.parent.partId); log.buf.AppendLine("Parent part is " + this.parent.name + ":" + this.parent.partId);
} }
} }
else else
{ {
if (log != null) if (log != null)
{ {
log.buf.AppendLine("No PartSim for parent part (" + this.part.parent.partInfo.name + ")"); log.buf.AppendLine("No PartSim for parent part (" + this.part.parent.partInfo.name + ")");
} }
} }
} }
} }
   
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 " + this.name + ":" + this.partId + ""); log.buf.AppendLine("SetupAttachNodes for " + this.name + ":" + this.partId + "");
} }
   
this.attachNodes.Clear(); attachNodes.Clear();
foreach (AttachNode attachNode in this.part.attachNodes)  
{ for (int i = 0; i < part.attachNodes.Count; ++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 + "");
} }
   
this.attachNodes.Add(new AttachNodeSim(attachedSim, attachNode.id, attachNode.nodeType)); attachNodes.Add(AttachNodeSim.GetPoolObject().Initialise(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 + ")");
} }
} }
} }
} }
   
foreach (Part p in this.part.fuelLookupTargets) for (int i = 0; i < part.fuelLookupTargets.Count; ++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);
} }
   
this.fuelTargets.Add(targetSim); this.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 + ")");
} }
} }
} }
} }
} }
   
private int DecoupledInStage(Part thePart, int stage = -1) private int DecoupledInStage(Part thePart, int stage = -1)
{ {
if (this.IsDecoupler(thePart)) if (this.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 = this.DecoupledInStage(thePart.parent, stage); stage = this.DecoupledInStage(thePart.parent, stage);
} }
   
return stage; return stage;
} }
   
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 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 IsSepratron() private bool IsSepratron()
{ {
if (!this.part.ActivatesEvenIfDisconnected) if (!this.part.ActivatesEvenIfDisconnected)
{ {
return false; return false;
} }
   
if (this.part is SolidRocket) if (this.part is SolidRocket)
{ {
return true; return true;
} }
   
var modList = this.part.Modules.OfType<ModuleEngines>(); var modList = this.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;
} }
   
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 HashSet<PartSim> GetSourceSet(int type, List<PartSim> allParts, HashSet<PartSim> visited, LogMsg log, String indent) public HashSet<PartSim> GetSourceSet(int type, List<PartSim> allParts, HashSet<PartSim> visited, LogMsg log, String indent)
{ {
if (log != null) if (log != null)
{ {
log.buf.AppendLine(indent + "GetSourceSet(" + ResourceContainer.GetResourceName(type) + ") for " + this.name + ":" + this.partId); log.buf.AppendLine(indent + "GetSourceSet(" + ResourceContainer.GetResourceName(type) + ") for " + this.name + ":" + this.partId);
indent += " "; indent += " ";
} }
   
HashSet<PartSim> allSources = new HashSet<PartSim>(); HashSet<PartSim> allSources = new HashSet<PartSim>();
HashSet<PartSim> partSources = null; HashSet<PartSim> partSources = null;
   
// Rule 1: Each part can be only visited once, If it is visited for second time in particular search it returns empty list. // Rule 1: Each part can be only visited once, If it is visited for second time in particular search it returns empty list.
if (visited.Contains(this)) if (visited.Contains(this))
{ {
if (log != null) if (log != null)
{ {
log.buf.AppendLine(indent + "Returning empty set, already visited (" + this.name + ":" + this.partId + ")"); log.buf.AppendLine(indent + "Returning empty set, already visited (" + this.name + ":" + this.partId + ")");
} }
   
return allSources; return allSources;
} }
   
//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("foreach fuel line");
  for (int i = 0; i < fuelTargets.Count; ++i)
foreach (PartSim partSim in this.fuelTargets) {
{ PartSim partSim = fuelTargets[i];
   
if (visited.Contains(partSim)) if (visited.Contains(partSim))
{ {
//if (log != null) //if (log != null)
// log.buf.AppendLine(indent + "Fuel target already visited, skipping (" + partSim.name + ":" + partSim.partId + ")"); // log.buf.AppendLine(indent + "Fuel target already visited, skipping (" + partSim.name + ":" + partSim.partId + ")");
} }
else else
{ {
//if (log != null) //if (log != null)
// log.buf.AppendLine(indent + "Adding fuel target as source (" + partSim.name + ":" + partSim.partId + ")"); // log.buf.AppendLine(indent + "Adding fuel target as source (" + partSim.name + ":" + partSim.partId + ")");
   
partSources = partSim.GetSourceSet(type, allParts, visited, log, indent); partSources = partSim.GetSourceSet(type, allParts, visited, log, indent);
if (partSources.Count > 0) if (partSources.Count > 0)
{ {
allSources.UnionWith(partSources); allSources.UnionWith(partSources);
partSources.Clear(); partSources.Clear();
} }
} }
} }
   
if (allSources.Count > 0) if (allSources.Count > 0)
{ {
if (log != null) if (log != null)
{ {
log.buf.AppendLine(indent + "Returning " + allSources.Count + " fuel target sources (" + this.name + ":" + this.partId + ")"); log.buf.AppendLine(indent + "Returning " + allSources.Count + " fuel target sources (" + this.name + ":" + this.partId + ")");
} }
   
return allSources; return allSources;
} }
   
// 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 (this.fuelCrossFeed) if (this.fuelCrossFeed)
{ {
//MonoBehaviour.print("foreach attach node"); //MonoBehaviour.print("foreach attach node");
foreach (AttachNodeSim attachSim in this.attachNodes) for (int i = 0; i < attachNodes.Count; ++i)
{ {
  AttachNodeSim attachSim = attachNodes[i];
   
if (attachSim.attachedPartSim != null) if (attachSim.attachedPartSim != null)
{ {
if (attachSim.nodeType == AttachNode.NodeType.Stack) if (attachSim.nodeType == AttachNode.NodeType.Stack)
{ {
if (!(this.noCrossFeedNodeKey != null && this.noCrossFeedNodeKey.Length > 0 && attachSim.id.Contains(this.noCrossFeedNodeKey))) if (!(this.noCrossFeedNodeKey != null && this.noCrossFeedNodeKey.Length > 0 && 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 + ")");
   
partSources = attachSim.attachedPartSim.GetSourceSet(type, allParts, visited, log, indent); partSources = attachSim.attachedPartSim.GetSourceSet(type, allParts, visited, log, indent);
if (partSources.Count > 0) if (partSources.Count > 0)
{ {
allSources.UnionWith(partSources); allSources.UnionWith(partSources);
partSources.Clear(); partSources.Clear();
} }
} }
} }
} }
} }
} }
   
if (allSources.Count > 0) if (allSources.Count > 0)
{ {
if (log != null) if (log != null)
{ {
log.buf.AppendLine(indent + "Returning " + allSources.Count + " attached sources (" + this.name + ":" + this.partId + ")"); log.buf.AppendLine(indent + "Returning " + allSources.Count + " attached sources (" + this.name + ":" + this.partId + ")");
} }
   
return allSources; return allSources;
} }
} }
   
// 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 (this.resources.HasType(type) && this.resourceFlowStates[type] != 0) if (this.resources.HasType(type) && this.resourceFlowStates[type] != 0)
{ {
if (this.resources[type] > SimManager.RESOURCE_MIN) if (this.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 (" + this.name + ":" + this.partId + ")"); log.buf.AppendLine(indent + "Returning enabled tank as only source (" + this.name + ":" + this.partId + ")");
} }
} }
   
return allSources; return allSources;
} }
   
// 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 (this.parent != null && this.parentAttach == AttachModes.SRF_ATTACH) if (this.parent != null && this.parentAttach == AttachModes.SRF_ATTACH)
{ {
if (this.fuelCrossFeed) if (this.fuelCrossFeed)
{ {
if (visited.Contains(this.parent)) if (visited.Contains(this.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
{ {
allSources = this.parent.GetSourceSet(type, allParts, visited, log, indent); allSources = this.parent.GetSourceSet(type, allParts, visited, log, indent);
if (allSources.Count > 0) if (allSources.Count > 0)
{ {
if (log != null) if (log != null)
{ {
log.buf.AppendLine(indent + "Returning " + allSources.Count + " parent sources (" + this.name + ":" + this.partId + ")"); log.buf.AppendLine(indent + "Returning " + allSources.Count + " parent sources (" + this.name + ":" + this.partId + ")");
} }
   
return allSources; return allSources;
} }
} }
} }
} }
   
// 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 allSources; return allSources;
} }
   
public void RemoveAttachedParts(HashSet<PartSim> partSims) public void RemoveAttachedParts(HashSet<PartSim> partSims)
{ {
// Loop through the attached parts // Loop through the attached parts
foreach (AttachNodeSim attachSim in this.attachNodes) for (int i = 0; i < attachNodes.Count; ++i)
{ {
  AttachNodeSim attachSim = 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;
} }
} }
} }
   
public void DrainResources(double time) public void DrainResources(double time)
{ {
//MonoBehaviour.print("DrainResources(" + name + ":" + partId + ", " + time + ")"); //MonoBehaviour.print("DrainResources(" + name + ":" + partId + ", " + time + ")");
foreach (int type in this.resourceDrains.Types) for (int i = 0; i < resourceDrains.Types.Count; ++i)
{ {
  int type = resourceDrains.Types[i];
   
//MonoBehaviour.print("draining " + (time * resourceDrains[type]) + " " + ResourceContainer.GetResourceName(type)); //MonoBehaviour.print("draining " + (time * resourceDrains[type]) + " " + ResourceContainer.GetResourceName(type));
this.resources.Add(type, -time * this.resourceDrains[type]); this.resources.Add(type, -time * this.resourceDrains[type]);
//MonoBehaviour.print(ResourceContainer.GetResourceName(type) + " left = " + resources[type]); //MonoBehaviour.print(ResourceContainer.GetResourceName(type) + " left = " + resources[type]);
} }
} }
   
public double TimeToDrainResource() public double TimeToDrainResource()
{ {
//MonoBehaviour.print("TimeToDrainResource(" + name + ":" + partId + ")"); //MonoBehaviour.print("TimeToDrainResource(" + name + ":" + partId + ")");
double time = double.MaxValue; double time = double.MaxValue;
   
foreach (int type in this.resourceDrains.Types) for (int i = 0; i < resourceDrains.Types.Count; ++i)
{ {
  int type = resourceDrains.Types[i];
   
if (this.resourceDrains[type] > 0) if (this.resourceDrains[type] > 0)
{ {
time = Math.Min(time, this.resources[type] / this.resourceDrains[type]); time = Math.Min(time, this.resources[type] / this.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;
} }
   
public bool EmptyOf(HashSet<int> types) public bool EmptyOf(HashSet<int> types)
{ {
foreach (int type in types) foreach (int type in types)
{ {
if (this.resources.HasType(type) && this.resourceFlowStates[type] != 0 && (double)this.resources[type] > SimManager.RESOURCE_MIN) if (this.resources.HasType(type) && this.resourceFlowStates[type] != 0 && (double)this.resources[type] > SimManager.RESOURCE_MIN)
{ {
return false; return false;
} }
} }
   
return true; return true;
} }
   
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 double GetStartMass() public double GetStartMass()
{ {
return this.startMass; return this.startMass;
} }
   
public double GetMass() public double GetMass()
{ {
double mass = this.baseMass; double mass = this.baseMass;
   
foreach (int type in this.resources.Types) for (int i = 0; i < resources.Types.Count; ++i)
{ {
mass += this.resources.GetResourceMass(type); mass += this.resources.GetResourceMass(resources.Types[i]);
} }
   
return mass; return mass;
} }
   
public String DumpPartAndParentsToBuffer(StringBuilder buffer, String prefix) public String DumpPartAndParentsToBuffer(StringBuilder buffer, String prefix)
{ {
if (this.parent != null) if (this.parent != null)
{ {
prefix = this.parent.DumpPartAndParentsToBuffer(buffer, prefix) + " "; prefix = this.parent.DumpPartAndParentsToBuffer(buffer, prefix) + " ";
} }
   
this.DumpPartToBuffer(buffer, prefix); this.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(this.name); buffer.Append(this.name);
buffer.AppendFormat(":[id = {0:d}, decouple = {1:d}, invstage = {2:d}", this.partId, this.decoupledInStage, this.inverseStage); buffer.AppendFormat(":[id = {0:d}, decouple = {1:d}, invstage = {2:d}", this.partId, this.decoupledInStage, this.inverseStage);
   
buffer.AppendFormat(", vesselName = '{0}'", this.vesselName); buffer.AppendFormat(", vesselName = '{0}'", this.vesselName);
buffer.AppendFormat(", vesselType = {0}", SimManager.GetVesselTypeString(this.vesselType)); buffer.AppendFormat(", vesselType = {0}", SimManager.GetVesselTypeString(this.vesselType));
buffer.AppendFormat(", initialVesselName = '{0}'", this.initialVesselName); buffer.AppendFormat(", initialVesselName = '{0}'", this.initialVesselName);
   
buffer.AppendFormat(", fuelCF = {0}", this.fuelCrossFeed); buffer.AppendFormat(", fuelCF = {0}", this.fuelCrossFeed);
buffer.AppendFormat(", noCFNKey = '{0}'", this.noCrossFeedNodeKey); buffer.AppendFormat(", noCFNKey = '{0}'", this.noCrossFeedNodeKey);
   
buffer.AppendFormat(", isSep = {0}", this.isSepratron); buffer.AppendFormat(", isSep = {0}", this.isSepratron);
   
foreach (int type in this.resources.Types) foreach (int type in this.resources.Types)
{ {
buffer.AppendFormat(", {0} = {1:g6}", ResourceContainer.GetResourceName(type), this.resources[type]); buffer.AppendFormat(", {0} = {1:g6}", ResourceContainer.GetResourceName(type), this.resources[type]);
} }
   
if (this.attachNodes.Count > 0) if (this.attachNodes.Count > 0)
{ {
buffer.Append(", attached = <"); buffer.Append(", attached = <");
this.attachNodes[0].DumpToBuffer(buffer); this.attachNodes[0].DumpToBuffer(buffer);
for (int i = 1; i < this.attachNodes.Count; i++) for (int i = 1; i < this.attachNodes.Count; i++)
{ {
buffer.Append(", "); buffer.Append(", ");
this.attachNodes[i].DumpToBuffer(buffer); this.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) foreach (PartSim partSim in allParts)
{ {
if (partSim.parent == this) if (partSim.parent == this)
{ {
partSim.DumpPartToBuffer(buffer, newPrefix, allParts); partSim.DumpPartToBuffer(buffer, newPrefix, allParts);
} }
} }
} }
} }
} }
} }
// //
// Kerbal Engineer Redux // Kerbal Engineer Redux
// //
// Copyright (C) 2014 CYBUTEK // Copyright (C) 2014 CYBUTEK
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
// //
   
#region Using Directives #region Using Directives
   
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
   
using UnityEngine; using UnityEngine;
   
#endregion #endregion
   
namespace KerbalEngineer.VesselSimulator namespace KerbalEngineer.VesselSimulator
{ {
public class ResourceContainer public class ResourceContainer
{ {
private Hashtable resources = new Hashtable(); private Hashtable resources = new Hashtable();
   
public double this[int type] public double this[int type]
{ {
get get
{ {
if (this.resources.ContainsKey(type)) if (this.resources.ContainsKey(type))
{ {
return (double)this.resources[type]; return (double)this.resources[type];
} }
   
return 0d; return 0d;
} }
set set
{ {
if (this.resources.ContainsKey(type)) if (this.resources.ContainsKey(type))
{ {
this.resources[type] = value; this.resources[type] = value;
} }
else else
{ {
this.resources.Add(type, value); this.resources.Add(type, value);
} }
} }
} }
   
public List<int> Types public List<int> Types
{ {
get get
{ {
List<int> types = new List<int>(); List<int> types = new List<int>();
   
foreach (int key in this.resources.Keys) foreach (int key in this.resources.Keys)
{ {
types.Add(key); types.Add(key);
} }
   
return types; return types;
} }
} }
   
public double Mass public double Mass
{ {
get get
{ {
double mass = 0d; double mass = 0d;
   
foreach (double resource in this.resources.Values) foreach (double resource in this.resources.Values)
{ {
mass += resource; mass += resource;
} }
   
return mass; return mass;
} }
} }
   
public bool Empty public bool Empty
{ {
get get
{ {
foreach (int type in this.resources.Keys) foreach (int type in this.resources.Keys)
{ {
if ((double)this.resources[type] > SimManager.RESOURCE_MIN) if ((double)this.resources[type] > SimManager.RESOURCE_MIN)
{ {
return false; return false;
} }
} }
   
return true; return true;
} }
} }
   
public bool HasType(int type) public bool HasType(int type)
{ {
return this.resources.ContainsKey(type); return this.resources.ContainsKey(type);
} }
   
public bool EmptyOf(HashSet<int> types) public bool EmptyOf(HashSet<int> types)
{ {
foreach (int type in types) foreach (int type in types)
{ {
if (this.HasType(type) && (double)this.resources[type] > SimManager.RESOURCE_MIN) if (this.HasType(type) && (double)this.resources[type] > SimManager.RESOURCE_MIN)
{ {
return false; return false;
} }
} }
   
return true; return true;
} }
   
public void Add(int type, double amount) public void Add(int type, double amount)
{ {
if (this.resources.ContainsKey(type)) if (this.resources.ContainsKey(type))
{ {
this.resources[type] = (double)this.resources[type] + amount; this.resources[type] = (double)this.resources[type] + amount;
} }
else else
{ {
this.resources.Add(type, amount); this.resources.Add(type, amount);
} }
} }
   
public void Reset() public void Reset()
{ {
this.resources = new Hashtable(); this.resources.Clear();
} }
   
public void Debug() public void Debug()
{ {
foreach (int key in this.resources.Keys) foreach (int key in this.resources.Keys)
{ {
MonoBehaviour.print(" -> " + GetResourceName(key) + " = " + this.resources[key]); MonoBehaviour.print(" -> " + GetResourceName(key) + " = " + this.resources[key]);
} }
} }
   
public double GetResourceMass(int type) public double GetResourceMass(int type)
{ {
double density = GetResourceDensity(type); double density = GetResourceDensity(type);
return density == 0d ? 0d : (double)this.resources[type] * density; return density == 0d ? 0d : (double)this.resources[type] * density;
} }
   
public static ResourceFlowMode GetResourceFlowMode(int type) public static ResourceFlowMode GetResourceFlowMode(int type)
{ {
return PartResourceLibrary.Instance.GetDefinition(type).resourceFlowMode; return PartResourceLibrary.Instance.GetDefinition(type).resourceFlowMode;
} }
   
public static ResourceTransferMode GetResourceTransferMode(int type) public static ResourceTransferMode GetResourceTransferMode(int type)
{ {
return PartResourceLibrary.Instance.GetDefinition(type).resourceTransferMode; return PartResourceLibrary.Instance.GetDefinition(type).resourceTransferMode;
} }
   
public static float GetResourceDensity(int type) public static float GetResourceDensity(int type)
{ {
return PartResourceLibrary.Instance.GetDefinition(type).density; return PartResourceLibrary.Instance.GetDefinition(type).density;
} }
   
public static string GetResourceName(int type) public static string GetResourceName(int type)
{ {
return PartResourceLibrary.Instance.GetDefinition(type).name; return PartResourceLibrary.Instance.GetDefinition(type).name;
} }
} }
} }
// //
// 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;
   
#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)
{ {
MonoBehaviour.print("Exception in RunSimulation: " + e); Logger.Exception(e, "SimManager.RunSimulation()");
Logger.Exception(e);  
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);
} }
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)
{ {
MonoBehaviour.print("Exception in StartSimulation: " + e); Logger.Exception(e, "SimManager.StartSimulation()");
Logger.Exception(e);  
failMessage = e.ToString(); failMessage = e.ToString();
lock (locker) lock (locker)
{ {
bRunning = false; bRunning = false;
} }
logOutput = false; logOutput = false;
} }
dumpTree = false; dumpTree = false;
} }
   
#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 System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Text; using System.Text;
   
using UnityEngine; using UnityEngine;
   
#endregion #endregion
   
namespace KerbalEngineer.VesselSimulator namespace KerbalEngineer.VesselSimulator
{ {
using CompoundParts; using CompoundParts;
using Extensions; using Extensions;
  using Helpers;
   
public class Simulation public class Simulation
{ {
private const double STD_GRAVITY = 9.82;  
private const double SECONDS_PER_DAY = 86400; private const double SECONDS_PER_DAY = 86400;
private readonly Stopwatch _timer = new Stopwatch(); private readonly Stopwatch _timer = new Stopwatch();
private List<EngineSim> activeEngines = new List<EngineSim>(); private List<EngineSim> activeEngines = new List<EngineSim>();
private List<EngineSim> allEngines = new List<EngineSim>(); private List<EngineSim> allEngines = new List<EngineSim>();
private List<PartSim> allFuelLines = new List<PartSim>(); private List<PartSim> allFuelLines = new List<PartSim>();
private List<PartSim> allParts = new List<PartSim>(); private List<PartSim> allParts = new List<PartSim>();
private Dictionary<Part, PartSim> partSimLookup = new Dictionary<Part, PartSim>(); private Dictionary<Part, PartSim> partSimLookup = new Dictionary<Part, PartSim>();
private double atmosphere; private double atmosphere;
private int currentStage; private int currentStage;
private double currentisp; private double currentisp;
private bool doingCurrent; private bool doingCurrent;
private List<PartSim> dontStageParts = new List<PartSim>(); private List<PartSim> dontStageParts = new List<PartSim>();
List<List<PartSim>> dontStagePartsLists = new List<List<PartSim>>(); List<List<PartSim>> dontStagePartsLists = new List<List<PartSim>>();
private HashSet<PartSim> drainingParts = new HashSet<PartSim>(); private HashSet<PartSim> drainingParts = new HashSet<PartSim>();
private HashSet<int> drainingResources = new HashSet<int>(); private HashSet<int> drainingResources = new HashSet<int>();
private HashSet<PartSim> decoupledParts = new HashSet<PartSim>(); private HashSet<PartSim> decoupledParts = new HashSet<PartSim>();
private double gravity; private double gravity;
   
private int lastStage; private int lastStage;
private List<Part> partList = new List<Part>(); private List<Part> partList = new List<Part>();
private double simpleTotalThrust; private double simpleTotalThrust;
private double stageStartMass; private double stageStartMass;
private Vector3d stageStartCom; private Vector3d stageStartCom;
private double stageTime; private double stageTime;
private double stepEndMass; private double stepEndMass;
private double stepStartMass; private double stepStartMass;
private double totalStageActualThrust; private double totalStageActualThrust;
private double totalStageFlowRate; private double totalStageFlowRate;
private double totalStageIspFlowRate; private double totalStageIspFlowRate;
private double totalStageThrust; private double totalStageThrust;
private ForceAccumulator totalStageThrustForce; private ForceAccumulator totalStageThrustForce = new ForceAccumulator();
private Vector3 vecActualThrust; private Vector3 vecActualThrust;
private Vector3 vecStageDeltaV; private Vector3 vecStageDeltaV;
private Vector3 vecThrust; private Vector3 vecThrust;
private double mach; private double mach;
private float maxMach; private float maxMach;
public String vesselName; public String vesselName;
public VesselType vesselType; public VesselType vesselType;
  private WeightedVectorAverager vectorAverager = new WeightedVectorAverager();
   
public Simulation() public Simulation()
{ {
if (SimManager.logOutput) if (SimManager.logOutput)
{ {
MonoBehaviour.print("Simulation created"); MonoBehaviour.print("Simulation created");
} }
} }
   
private double ShipMass private double ShipMass
{ {
get get
{ {
double mass = 0d; double mass = 0d;
   
foreach (PartSim partSim in this.allParts) for (int i = 0; i < allParts.Count; ++i) {
{ mass += allParts[i].GetMass();
mass += partSim.GetMass();  
} }
   
return mass; return mass;
} }
} }
   
private Vector3d ShipCom private Vector3d ShipCom
{ {
get get
{ {
WeightedVectorAverager averager = new WeightedVectorAverager(); vectorAverager.Reset();
   
foreach (PartSim partSim in this.allParts) for (int i = 0; i < allParts.Count; ++i)
{ {
averager.Add(partSim.centerOfMass, partSim.GetMass()); PartSim partSim = allParts[i];
} vectorAverager.Add(partSim.centerOfMass, partSim.GetMass());
  }
return averager.Get();  
  return vectorAverager.Get();
} }
} }
   
// This function prepares the simulation by creating all the necessary data structures it will // This function prepares the simulation by creating all the necessary data structures it will
// need during the simulation. All required data is copied from the core game data structures // need during the simulation. All required data is copied from the core game data structures
// so that the simulation itself can be run in a background thread without having issues with // so that the simulation itself can be run in a background thread without having issues with
// the core game changing the data while the simulation is running. // the core game changing the data while the simulation is running.
public bool PrepareSimulation(List<Part> parts, double theGravity, double theAtmosphere = 0, double theMach = 0, bool dumpTree = false, bool vectoredThrust = false, bool fullThrust = false) public bool PrepareSimulation(List<Part> parts, double theGravity, double theAtmosphere = 0, double theMach = 0, bool dumpTree = false, bool vectoredThrust = false, bool fullThrust = false)
{ {
LogMsg log = null; LogMsg log = null;
if (SimManager.logOutput) if (SimManager.logOutput)
{ {
log = new LogMsg(); log = new LogMsg();
log.buf.AppendLine("PrepareSimulation started"); log.buf.AppendLine("PrepareSimulation started");
dumpTree = true; dumpTree = true;
} }
this._timer.Start(); this._timer.Start();
   
// Store the parameters in members for ease of access in other functions // Store the parameters in members for ease of access in other functions
this.partList = parts; this.partList = parts;
this.gravity = theGravity; this.gravity = theGravity;
this.atmosphere = theAtmosphere; this.atmosphere = theAtmosphere;
this.mach = theMach; this.mach = theMach;
this.lastStage = Staging.lastStage; this.lastStage = Staging.lastStage;
//MonoBehaviour.print("lastStage = " + lastStage); //MonoBehaviour.print("lastStage = " + lastStage);
   
// Clear the lists for our simulation parts // Clear the lists for our simulation parts
allParts.Clear(); allParts.Clear();
allFuelLines.Clear(); allFuelLines.Clear();
drainingParts.Clear(); drainingParts.Clear();
allEngines.Clear(); allEngines.Clear();
activeEngines.Clear(); activeEngines.Clear();
drainingResources.Clear(); drainingResources.Clear();
   
  PartSim.ReleaseAll();
  EngineSim.ReleaseAll();
  AttachNodeSim.ReleaseAll();
   
// A dictionary for fast lookup of Part->PartSim during the preparation phase // A dictionary for fast lookup of Part->PartSim during the preparation phase
partSimLookup.Clear(); partSimLookup.Clear();
   
if (this.partList.Count > 0 && this.partList[0].vessel != null) if (this.partList.Count > 0 && this.partList[0].vessel != null)
{ {
this.vesselName = this.partList[0].vessel.vesselName; this.vesselName = this.partList[0].vessel.vesselName;
this.vesselType = this.partList[0].vessel.vesselType; this.vesselType = this.partList[0].vessel.vesselType;
} }
   
// First we create a PartSim for each Part (giving each a unique id) // First we create a PartSim for each Part (giving each a unique id)
int partId = 1; int partId = 1;
foreach (Part part in this.partList) for (int i = 0; i < partList.Count; ++i)
{ {
  Part part = partList[i];
   
// If the part is already in the lookup dictionary then log it and skip to the next part // If the part is already in the lookup dictionary then log it and skip to the next part
if (partSimLookup.ContainsKey(part)) if (partSimLookup.ContainsKey(part))
{ {
if (log != null) if (log != null)
{ {
log.buf.AppendLine("Part " + part.name + " appears in vessel list more than once"); log.buf.AppendLine("Part " + part.name + " appears in vessel list more than once");
} }
continue; continue;
} }
   
// Create the PartSim // Create the PartSim
PartSim partSim = new PartSim(part, partId, this.atmosphere, log); PartSim partSim = PartSim.GetPoolObject().Initialise(part, partId, this.atmosphere, log);
   
// Add it to the Part lookup dictionary and the necessary lists // Add it to the Part lookup dictionary and the necessary lists
partSimLookup.Add(part, partSim); partSimLookup.Add(part, partSim);
this.allParts.Add(partSim); this.allParts.Add(partSim);
if (partSim.isFuelLine) if (partSim.isFuelLine)
{ {
this.allFuelLines.Add(partSim); this.allFuelLines.Add(partSim);
} }
if (partSim.isEngine) if (partSim.isEngine)
{ {
partSim.CreateEngineSims(this.allEngines, this.atmosphere, this.mach, vectoredThrust, fullThrust, log); partSim.CreateEngineSims(this.allEngines, this.atmosphere, this.mach, vectoredThrust, fullThrust, log);
} }
   
partId++; partId++;
} }
   
for (int i = 0; i < allEngines.Count; ++i) for (int i = 0; i < allEngines.Count; ++i)
{ {
maxMach = Mathf.Max(maxMach, allEngines[i].maxMach); maxMach = Mathf.Max(maxMach, allEngines[i].maxMach);
} }
   
this.UpdateActiveEngines(); this.UpdateActiveEngines();
   
// Now that all the PartSims have been created we can do any set up that needs access to other parts // Now that all the PartSims have been created we can do any set up that needs access to other parts
// First we set up all the parent links // First we set up all the parent links
foreach (PartSim partSim in this.allParts) foreach (PartSim partSim in this.allParts)
{ {
partSim.SetupParent(partSimLookup, log); partSim.SetupParent(partSimLookup, log);
} }
   
// Then, in the VAB/SPH, we add the parent of each fuel line to the fuelTargets list of their targets // Then, in the VAB/SPH, we add the parent of each fuel line to the fuelTargets list of their targets
if (HighLogic.LoadedSceneIsEditor) if (HighLogic.LoadedSceneIsEditor)
{ {
foreach (PartSim partSim in this.allFuelLines) for (int i = 0; i < allFuelLines.Count; ++i)
{ {
  PartSim partSim = allFuelLines[i];
   
CModuleFuelLine fuelLine = partSim.part.GetModule<CModuleFuelLine>(); CModuleFuelLine fuelLine = partSim.part.GetModule<CModuleFuelLine>();
if (fuelLine.target != null) if (fuelLine.target != null)
{ {
PartSim targetSim; PartSim targetSim;
if (partSimLookup.TryGetValue(fuelLine.target, out targetSim)) if (partSimLookup.TryGetValue(fuelLine.target, out targetSim))
{ {
if (log != null) if (log != null)
{ {
log.buf.AppendLine("Fuel line target is " + targetSim.name + ":" + targetSim.partId); log.buf.AppendLine("Fuel line target is " + targetSim.name + ":" + targetSim.partId);
} }
   
targetSim.fuelTargets.Add(partSim.parent); targetSim.fuelTargets.Add(partSim.parent);
} }
else else
{ {
if (log != null) if (log != null)
{ {
log.buf.AppendLine("No PartSim for fuel line target (" + partSim.part.partInfo.name + ")"); log.buf.AppendLine("No PartSim for fuel line target (" + partSim.part.partInfo.name + ")");
} }
} }
} }
else else
{ {
if (log != null) if (log != null)
{ {
log.buf.AppendLine("Fuel line target is null"); log.buf.AppendLine("Fuel line target is null");
} }
} }
} }
} }
   
//MonoBehaviour.print("SetupAttachNodes and count stages"); //MonoBehaviour.print("SetupAttachNodes and count stages");
foreach (PartSim partSim in this.allParts) for (int i = 0; i < allParts.Count; ++i)
{ {
  PartSim partSim = allParts[i];
   
partSim.SetupAttachNodes(partSimLookup, log); partSim.SetupAttachNodes(partSimLookup, log);
if (partSim.decoupledInStage >= this.lastStage) if (partSim.decoupledInStage >= this.lastStage)
{ {
this.lastStage = partSim.decoupledInStage + 1; this.lastStage = partSim.decoupledInStage + 1;
} }
} }
   
// And finally release the Part references from all the PartSims // And finally release the Part references from all the PartSims
//MonoBehaviour.print("ReleaseParts"); //MonoBehaviour.print("ReleaseParts");
foreach (PartSim partSim in this.allParts) for (int i = 0; i < allParts.Count; ++i)
{ {
partSim.ReleasePart(); allParts[i].ReleasePart();
} }
   
// And dereference the core's part list // And dereference the core's part list
this.partList = null; this.partList = null;
   
this._timer.Stop(); this._timer.Stop();
if (log != null) if (log != null)
{ {
log.buf.AppendLine("PrepareSimulation: " + this._timer.ElapsedMilliseconds + "ms"); log.buf.AppendLine("PrepareSimulation: " + this._timer.ElapsedMilliseconds + "ms");
log.Flush(); log.Flush();
} }
   
if (dumpTree) if (dumpTree)
{ {
this.Dump(); this.Dump();
} }
   
return true; return true;
} }
   
// This function runs the simulation and returns a newly created array of Stage objects // This function runs the simulation and returns a newly created array of Stage objects
public Stage[] RunSimulation() public Stage[] RunSimulation()
{ {
if (SimManager.logOutput) if (SimManager.logOutput)
{ {
MonoBehaviour.print("RunSimulation started"); MonoBehaviour.print("RunSimulation started");
} }
   
this._timer.Start(); this._timer.Start();
   
LogMsg log = null; LogMsg log = null;
if (SimManager.logOutput) if (SimManager.logOutput)
{ {
log = new LogMsg(); log = new LogMsg();
} }
   
// Start with the last stage to simulate // Start with the last stage to simulate
// (this is in a member variable so it can be accessed by AllowedToStage and ActivateStage) // (this is in a member variable so it can be accessed by AllowedToStage and ActivateStage)
this.currentStage = this.lastStage; this.currentStage = this.lastStage;
   
// Work out which engines would be active if just doing the staging and if this is different to the // Work out which engines would be active if just doing the staging and if this is different to the
// currently active engines then generate an extra stage // currently active engines then generate an extra stage
// Loop through all the engines // Loop through all the engines
bool anyActive = false; bool anyActive = false;
foreach (EngineSim engine in this.allEngines) for (int i = 0; i < allEngines.Count; ++i)
{ {
  EngineSim engine = allEngines[i];
   
if (log != null) if (log != null)
{ {
log.buf.AppendLine("Testing engine mod of " + engine.partSim.name + ":" + engine.partSim.partId); log.buf.AppendLine("Testing engine mod of " + engine.partSim.name + ":" + engine.partSim.partId);
} }
bool bActive = engine.isActive; bool bActive = engine.isActive;
bool bStage = (engine.partSim.inverseStage >= this.currentStage); bool bStage = (engine.partSim.inverseStage >= this.currentStage);
if (log != null) if (log != null)
{ {
log.buf.AppendLine("bActive = " + bActive + " bStage = " + bStage); log.buf.AppendLine("bActive = " + bActive + " bStage = " + bStage);
} }
if (HighLogic.LoadedSceneIsFlight) if (HighLogic.LoadedSceneIsFlight)
{ {
if (bActive) if (bActive)
{ {
anyActive = true; anyActive = true;
} }
if (bActive != bStage) if (bActive != bStage)
{ {
// If the active state is different to the state due to staging // If the active state is different to the state due to staging
if (log != null) if (log != null)
{ {
log.buf.AppendLine("Need to do current active engines first"); log.buf.AppendLine("Need to do current active engines first");
} }
   
this.doingCurrent = true; this.doingCurrent = true;
} }
} }
else else
{ {
if (bStage) if (bStage)
{ {
if (log != null) if (log != null)
{ {
log.buf.AppendLine("Marking as active"); log.buf.AppendLine("Marking as active");
} }
   
engine.isActive = true; engine.isActive = true;
} }
} }
} }
   
// If we need to do current because of difference in engine activation and there actually are active engines // If we need to do current because of difference in engine activation and there actually are active engines
// then we do the extra stage otherwise activate the next stage and don't treat it as current // then we do the extra stage otherwise activate the next stage and don't treat it as current
if (this.doingCurrent && anyActive) if (this.doingCurrent && anyActive)
{ {
this.currentStage++; this.currentStage++;
} }
else else
{ {
this.ActivateStage(); this.ActivateStage();
this.doingCurrent = false; this.doingCurrent = false;
} }
   
// Create a list of lists of PartSims that prevent decoupling // Create a list of lists of PartSims that prevent decoupling
BuildDontStageLists(log); BuildDontStageLists(log);
   
if (log != null) if (log != null)
{ {
log.Flush(); log.Flush();
} }
   
// Create the array of stages that will be returned // Create the array of stages that will be returned
Stage[] stages = new Stage[this.currentStage + 1]; Stage[] stages = new Stage[this.currentStage + 1];
   
   
// Loop through the stages // Loop through the stages
while (this.currentStage >= 0) while (this.currentStage >= 0)
{ {
if (log != null) if (log != null)
{ {
log.buf.AppendLine("Simulating stage " + this.currentStage); log.buf.AppendLine("Simulating stage " + this.currentStage);
log.buf.AppendLine("ShipMass = " + this.ShipMass); log.buf.AppendLine("ShipMass = " + this.ShipMass);
log.Flush(); log.Flush();
this._timer.Reset(); this._timer.Reset();
this._timer.Start(); this._timer.Start();
} }
   
// Update active engines and resource drains // Update active engines and resource drains
this.UpdateResourceDrains(); this.UpdateResourceDrains();
   
// Create the Stage object for this stage // Create the Stage object for this stage
Stage stage = new Stage(); Stage stage = new Stage();
   
this.stageTime = 0d; this.stageTime = 0d;
this.vecStageDeltaV = Vector3.zero; this.vecStageDeltaV = Vector3.zero;
   
this.stageStartMass = this.ShipMass; this.stageStartMass = this.ShipMass;
this.stageStartCom = this.ShipCom; this.stageStartCom = this.ShipCom;
   
this.stepStartMass = this.stageStartMass; this.stepStartMass = this.stageStartMass;
this.stepEndMass = 0; this.stepEndMass = 0;
   
this.CalculateThrustAndISP(); this.CalculateThrustAndISP();
   
// Store various things in the Stage object // Store various things in the Stage object
stage.thrust = this.totalStageThrust; stage.thrust = this.totalStageThrust;
//MonoBehaviour.print("stage.thrust = " + stage.thrust); //MonoBehaviour.print("stage.thrust = " + stage.thrust);
stage.thrustToWeight = this.totalStageThrust / (this.stageStartMass * this.gravity); stage.thrustToWeight = this.totalStageThrust / (this.stageStartMass * this.gravity);
stage.maxThrustToWeight = stage.thrustToWeight; stage.maxThrustToWeight = stage.thrustToWeight;
//MonoBehaviour.print("StageMass = " + stageStartMass); //MonoBehaviour.print("StageMass = " + stageStartMass);
//MonoBehaviour.print("Initial maxTWR = " + stage.maxThrustToWeight); //MonoBehaviour.print("Initial maxTWR = " + stage.maxThrustToWeight);
stage.actualThrust = this.totalStageActualThrust; stage.actualThrust = this.totalStageActualThrust;
stage.actualThrustToWeight = this.totalStageActualThrust / (this.stageStartMass * this.gravity); stage.actualThrustToWeight = this.totalStageActualThrust / (this.stageStartMass * this.gravity);
   
// calculate torque and associates // calculate torque and associates
stage.maxThrustTorque = this.totalStageThrustForce.TorqueAt(this.stageStartCom).magnitude; stage.maxThrustTorque = this.totalStageThrustForce.TorqueAt(this.stageStartCom).magnitude;
   
// torque divided by thrust. imagine that all engines are at the end of a lever that tries to turn the ship. // torque divided by thrust. imagine that all engines are at the end of a lever that tries to turn the ship.
// this numerical value, in meters, would represent the length of that lever. // this numerical value, in meters, would represent the length of that lever.
double torqueLeverArmLength = (stage.thrust <= 0) ? 0 : stage.maxThrustTorque / stage.thrust; double torqueLeverArmLength = (stage.thrust <= 0) ? 0 : stage.maxThrustTorque / stage.thrust;
   
// how far away are the engines from the CoM, actually? // how far away are the engines from the CoM, actually?
double thrustDistance = (this.stageStartCom - this.totalStageThrustForce.GetAverageForceApplicationPoint()).magnitude; double thrustDistance = (this.stageStartCom - this.totalStageThrustForce.GetAverageForceApplicationPoint()).magnitude;
   
// the combination of the above two values gives an approximation of the offset angle. // the combination of the above two values gives an approximation of the offset angle.
double sinThrustOffsetAngle = 0; double sinThrustOffsetAngle = 0;
if (thrustDistance > 1e-7) { if (thrustDistance > 1e-7) {
sinThrustOffsetAngle = torqueLeverArmLength / thrustDistance; sinThrustOffsetAngle = torqueLeverArmLength / thrustDistance;
if (sinThrustOffsetAngle > 1) { if (sinThrustOffsetAngle > 1) {
sinThrustOffsetAngle = 1; sinThrustOffsetAngle = 1;
} }
} }
   
stage.thrustOffsetAngle = Math.Asin(sinThrustOffsetAngle) * 180 / Math.PI; stage.thrustOffsetAngle = Math.Asin(sinThrustOffsetAngle) * 180 / Math.PI;
   
// Calculate the cost and mass of this stage and add all engines and tanks that are decoupled // Calculate the cost and mass of this stage and add all engines and tanks that are decoupled
// in the next stage to the dontStageParts list // in the next stage to the dontStageParts list
foreach (PartSim partSim in this.allParts) for (int i = 0; i < allParts.Count; ++i)
{ {
  PartSim partSim = allParts[i];
   
if (partSim.decoupledInStage == this.currentStage - 1) if (partSim.decoupledInStage == this.currentStage - 1)
{ {
stage.cost += partSim.cost; stage.cost += partSim.cost;
stage.mass += partSim.GetStartMass(); stage.mass += partSim.GetStartMass();
} }
} }
   
this.dontStageParts = dontStagePartsLists[this.currentStage]; this.dontStageParts = dontStagePartsLists[this.currentStage];
   
if (log != null) if (log != null)
{ {
log.buf.AppendLine("Stage setup took " + this._timer.ElapsedMilliseconds + "ms"); log.buf.AppendLine("Stage setup took " + this._timer.ElapsedMilliseconds + "ms");
   
if (this.dontStageParts.Count > 0) if (this.dontStageParts.Count > 0)
{ {
log.buf.AppendLine("Parts preventing staging:"); log.buf.AppendLine("Parts preventing staging:");
foreach (PartSim partSim in this.dontStageParts) foreach (PartSim partSim in this.dontStageParts)
{ {
partSim.DumpPartToBuffer(log.buf, ""); partSim.DumpPartToBuffer(log.buf, "");
} }
} }
else else
{ {
log.buf.AppendLine("No parts preventing staging"); log.buf.AppendLine("No parts preventing staging");
} }
   
log.Flush(); log.Flush();
} }
   
// Now we will loop until we are allowed to stage // Now we will loop until we are allowed to stage
int loopCounter = 0; int loopCounter = 0;
while (!this.AllowedToStage()) while (!this.AllowedToStage())
{ {
loopCounter++; loopCounter++;
//MonoBehaviour.print("loop = " + loopCounter); //MonoBehaviour.print("loop = " + loopCounter);
   
// Calculate how long each draining tank will take to drain and run for the minimum time // Calculate how long each draining tank will take to drain and run for the minimum time
double resourceDrainTime = double.MaxValue; double resourceDrainTime = double.MaxValue;
PartSim partMinDrain = null; PartSim partMinDrain = null;
foreach (PartSim partSim in this.drainingParts) foreach (PartSim partSim in this.drainingParts)
{ {
double time = partSim.TimeToDrainResource(); double time = partSim.TimeToDrainResource();
if (time < resourceDrainTime) if (time < resourceDrainTime)
{ {
resourceDrainTime = time; resourceDrainTime = time;
partMinDrain = partSim; partMinDrain = partSim;
} }
} }
   
if (log != null) if (log != null)
{ {
MonoBehaviour.print("Drain time = " + resourceDrainTime + " (" + partMinDrain.name + ":" + partMinDrain.partId + ")"); MonoBehaviour.print("Drain time = " + resourceDrainTime + " (" + partMinDrain.name + ":" + partMinDrain.partId + ")");
} }
   
foreach (PartSim partSim in this.drainingParts) foreach (PartSim partSim in this.drainingParts)
{ {
partSim.DrainResources(resourceDrainTime); partSim.DrainResources(resourceDrainTime);
} }
   
// Get the mass after draining // Get the mass after draining
this.stepEndMass = this.ShipMass; this.stepEndMass = this.ShipMass;
this.stageTime += resourceDrainTime; this.stageTime += resourceDrainTime;
   
double stepEndTWR = this.totalStageThrust / (this.stepEndMass * this.gravity); double stepEndTWR = this.totalStageThrust / (this.stepEndMass * this.gravity);
//MonoBehaviour.print("After drain mass = " + stepEndMass); //MonoBehaviour.print("After drain mass = " + stepEndMass);
//MonoBehaviour.print("currentThrust = " + totalStageThrust); //MonoBehaviour.print("currentThrust = " + totalStageThrust);
//MonoBehaviour.print("currentTWR = " + stepEndTWR); //MonoBehaviour.print("currentTWR = " + stepEndTWR);
if (stepEndTWR > stage.maxThrustToWeight) if (stepEndTWR > stage.maxThrustToWeight)
{ {
stage.maxThrustToWeight = stepEndTWR; stage.maxThrustToWeight = stepEndTWR;
} }
   
//MonoBehaviour.print("newMaxTWR = " + stage.maxThrustToWeight); //MonoBehaviour.print("newMaxTWR = " + stage.maxThrustToWeight);
   
// If we have drained anything and the masses make sense then add this step's deltaV to the stage total // If we have drained anything and the masses make sense then add this step's deltaV to the stage total
if (resourceDrainTime > 0d && this.stepStartMass > this.stepEndMass && this.stepStartMass > 0d && this.stepEndMass > 0d) if (resourceDrainTime > 0d && this.stepStartMass > this.stepEndMass && this.stepStartMass > 0d && this.stepEndMass > 0d)
{ {
this.vecStageDeltaV += this.vecThrust * (float)((this.currentisp * STD_GRAVITY * Math.Log(this.stepStartMass / this.stepEndMass)) / this.simpleTotalThrust); this.vecStageDeltaV += this.vecThrust * (float)((this.currentisp * Units.GRAVITY * Math.Log(this.stepStartMass / this.stepEndMass)) / this.simpleTotalThrust);
} }
   
// Update the active engines and resource drains for the next step // Update the active engines and resource drains for the next step
this.UpdateResourceDrains(); this.UpdateResourceDrains();
   
// Recalculate the current thrust and isp for the next step // Recalculate the current thrust and isp for the next step
this.CalculateThrustAndISP(); this.CalculateThrustAndISP();
   
// Check if we actually changed anything // Check if we actually changed anything
if (this.stepStartMass == this.stepEndMass) if (this.stepStartMass == this.stepEndMass)
{ {
//MonoBehaviour.print("No change in mass"); //MonoBehaviour.print("No change in mass");
break; break;
} }
   
// Check to stop rampant looping // Check to stop rampant looping
if (loopCounter == 1000) if (loopCounter == 1000)
{ {
MonoBehaviour.print("exceeded loop count"); MonoBehaviour.print("exceeded loop count");
MonoBehaviour.print("stageStartMass = " + this.stageStartMass); MonoBehaviour.print("stageStartMass = " + this.stageStartMass);
MonoBehaviour.print("stepStartMass = " + this.stepStartMass); MonoBehaviour.print("stepStartMass = " + this.stepStartMass);
MonoBehaviour.print("StepEndMass = " + this.stepEndMass); MonoBehaviour.print("StepEndMass = " + this.stepEndMass);
Logger.Log("exceeded loop count"); Logger.Log("exceeded loop count");
Logger.Log("stageStartMass = " + this.stageStartMass); Logger.Log("stageStartMass = " + this.stageStartMass);
Logger.Log("stepStartMass = " + this.stepStartMass); Logger.Log("stepStartMass = " + this.stepStartMass);
Logger.Log("StepEndMass = " + this.stepEndMass); Logger.Log("StepEndMass = " + this.stepEndMass);
break; break;
} }
   
// The next step starts at the mass this one ended at // The next step starts at the mass this one ended at
this.stepStartMass = this.stepEndMass; this.stepStartMass = this.stepEndMass;
} }
   
// Store more values in the Stage object and stick it in the array // Store more values in the Stage object and stick it in the array
   
// Store the magnitude of the deltaV vector // Store the magnitude of the deltaV vector
stage.deltaV = this.vecStageDeltaV.magnitude; stage.deltaV = this.vecStageDeltaV.magnitude;
stage.resourceMass = this.stageStartMass - this.stepEndMass; stage.resourceMass = this.stageStartMass - this.stepEndMass;
   
// Recalculate effective stage isp from the stage deltaV (flip the standard deltaV calculation around) // Recalculate effective stage isp from the stage deltaV (flip the standard deltaV calculation around)
// Note: If the mass doesn't change then this is a divide by zero // Note: If the mass doesn't change then this is a divide by zero
if (this.stageStartMass != this.stepStartMass) if (this.stageStartMass != this.stepStartMass)
{ {
stage.isp = stage.deltaV / (STD_GRAVITY * Math.Log(this.stageStartMass / this.stepStartMass)); stage.isp = stage.deltaV / (Units.GRAVITY * Math.Log(this.stageStartMass / this.stepStartMass));
} }
else else
{ {
stage.isp = 0; stage.isp = 0;
} }
   
// Zero stage time if more than a day (this should be moved into the window code) // Zero stage time if more than a day (this should be moved into the window code)
stage.time = (this.stageTime < SECONDS_PER_DAY) ? this.stageTime : 0d; stage.time = (this.stageTime < SECONDS_PER_DAY) ? this.stageTime : 0d;
stage.number = this.doingCurrent ? -1 : this.currentStage; // Set the stage number to -1 if doing current engines stage.number = this.doingCurrent ? -1 : this.currentStage; // Set the stage number to -1 if doing current engines
stage.totalPartCount = this.allParts.Count; stage.totalPartCount = this.allParts.Count;
stage.maxMach = maxMach; stage.maxMach = maxMach;
stages[this.currentStage] = stage; stages[this.currentStage] = stage;
   
// Now activate the next stage // Now activate the next stage
this.currentStage--; this.currentStage--;
this.doingCurrent = false; this.doingCurrent = false;
   
if (log != null) if (log != null)
{ {
// Log how long the stage took // Log how long the stage took
this._timer.Stop(); this._timer.Stop();
MonoBehaviour.print("Simulating stage took " + this._timer.ElapsedMilliseconds + "ms"); MonoBehaviour.print("Simulating stage took " + this._timer.ElapsedMilliseconds + "ms");
stage.Dump(); stage.Dump();
this._timer.Reset(); this._timer.Reset();
this._timer.Start(); this._timer.Start();
} }
   
// Activate the next stage // Activate the next stage
this.ActivateStage(); this.ActivateStage();
   
if (log != null) if (log != null)
{ {
// Log how long it took to activate // Log how long it took to activate
this._timer.Stop(); this._timer.Stop();
MonoBehaviour.print("ActivateStage took " + this._timer.ElapsedMilliseconds + "ms"); MonoBehaviour.print("ActivateStage took " + this._timer.ElapsedMilliseconds + "ms");
} }
} }
   
// Now we add up the various total fields in the stages // Now we add up the various total fields in the stages
for (int i = 0; i < stages.Length; i++) for (int i = 0; i < stages.Length; i++)
{ {
// For each stage we total up the cost, mass, deltaV and time for this stage and all the stages above // For each stage we total up the cost, mass, deltaV and time for this stage and all the stages above
for (int j = i; j >= 0; j--) for (int j = i; j >= 0; j--)
{ {
stages[i].totalCost += stages[j].cost; stages[i].totalCost += stages[j].cost;
stages[i].totalMass += stages[j].mass; stages[i].totalMass += stages[j].mass;
stages[i].totalDeltaV += stages[j].deltaV; stages[i].totalDeltaV += stages[j].deltaV;
stages[i].totalTime += stages[j].time; stages[i].totalTime += stages[j].time;
stages[i].partCount = i > 0 ? stages[i].totalPartCount - stages[i - 1].totalPartCount : stages[i].totalPartCount; stages[i].partCount = i > 0 ? stages[i].totalPartCount - stages[i - 1].totalPartCount : stages[i].totalPartCount;
} }
// We also total up the deltaV for stage and all stages below // We also total up the deltaV for stage and all stages below
for (int j = i; j < stages.Length; j++) for (int j = i; j < stages.Length; j++)
{ {
stages[i].inverseTotalDeltaV += stages[j].deltaV; stages[i].inverseTotalDeltaV += stages[j].deltaV;
} }
   
// Zero the total time if the value will be huge (24 hours?) to avoid the display going weird // Zero the total time if the value will be huge (24 hours?) to avoid the display going weird
// (this should be moved into the window code) // (this should be moved into the window code)
if (stages[i].totalTime > SECONDS_PER_DAY) if (stages[i].totalTime > SECONDS_PER_DAY)
{ {
stages[i].totalTime = 0d; stages[i].totalTime = 0d;
} }
} }
   
if (log != null) if (log != null)
{ {
this._timer.Stop(); this._timer.Stop();
MonoBehaviour.print("RunSimulation: " + this._timer.ElapsedMilliseconds + "ms"); MonoBehaviour.print("RunSimulation: " + this._timer.ElapsedMilliseconds + "ms");
} }
   
return stages; return stages;
} }
   
private void BuildDontStageLists(LogMsg log) private void BuildDontStageLists(LogMsg log)
{ {
if (log != null) if (log != null)
{ {
log.buf.AppendLine("Creating list with capacity of " + (this.currentStage + 1)); log.buf.AppendLine("Creating list with capacity of " + (this.currentStage + 1));
} }
   
dontStagePartsLists.Clear(); dontStagePartsLists.Clear();
for (int i = 0; i <= this.currentStage; i++) for (int i = 0; i <= this.currentStage; i++)
{ {
if (i < dontStagePartsLists.Count) if (i < dontStagePartsLists.Count)
{ {
dontStagePartsLists[i].Clear(); dontStagePartsLists[i].Clear();
} }
else else
{ {
dontStagePartsLists.Add(new List<PartSim>()); dontStagePartsLists.Add(new List<PartSim>());
} }
} }
   
foreach (PartSim partSim in this.allParts) for (int i = 0; i < allParts.Count; ++i)
{ {
  PartSim partSim = allParts[i];
   
if (partSim.isEngine || !partSim.Resources.Empty) if (partSim.isEngine || !partSim.Resources.Empty)
{ {
if (log != null) if (log != null)
{ {
log.buf.AppendLine(partSim.name + ":" + partSim.partId + " is engine or tank, decoupled = " + partSim.decoupledInStage); log.buf.AppendLine(partSim.name + ":" + partSim.partId + " is engine or tank, decoupled = " + partSim.decoupledInStage);
} }
   
if (partSim.decoupledInStage < -1 || partSim.decoupledInStage > this.currentStage - 1) if (partSim.decoupledInStage < -1 || partSim.decoupledInStage > this.currentStage - 1)
{ {
if (log != null) if (log != null)
{ {
log.buf.AppendLine("decoupledInStage out of range"); log.buf.AppendLine("decoupledInStage out of range");
} }
} }
else else
{ {
dontStagePartsLists[partSim.decoupledInStage + 1].Add(partSim); dontStagePartsLists[partSim.decoupledInStage + 1].Add(partSim);
} }
} }
} }
   
for (int i = 1; i <= this.lastStage; i++) for (int i = 1; i <= this.lastStage; i++)
{ {
if (dontStagePartsLists[i].Count == 0) if (dontStagePartsLists[i].Count == 0)
{ {
dontStagePartsLists[i] = dontStagePartsLists[i - 1]; dontStagePartsLists[i] = dontStagePartsLists[i - 1];
} }
} }
} }
   
// This function simply rebuilds the active engines by testing the isActive flag of all the engines // This function simply rebuilds the active engines by testing the isActive flag of all the engines
private void UpdateActiveEngines() private void UpdateActiveEngines()
{ {
this.activeEngines.Clear(); this.activeEngines.Clear();
foreach (EngineSim engine in this.allEngines) for (int i = 0; i < allEngines.Count; ++i)
{ {
  EngineSim engine = allEngines[i];
   
if (engine.isActive) if (engine.isActive)
{ {
this.activeEngines.Add(engine); this.activeEngines.Add(engine);
} }
} }
} }
   
private void CalculateThrustAndISP() private void CalculateThrustAndISP()
{ {
// Reset all the values // Reset all the values
this.vecThrust = Vector3.zero; this.vecThrust = Vector3.zero;
this.vecActualThrust = Vector3.zero; this.vecActualThrust = Vector3.zero;
this.simpleTotalThrust = 0d; this.simpleTotalThrust = 0d;
this.totalStageThrust = 0d; this.totalStageThrust = 0d;
this.totalStageActualThrust = 0d; this.totalStageActualThrust = 0d;
this.totalStageFlowRate = 0d; this.totalStageFlowRate = 0d;
this.totalStageIspFlowRate = 0d; this.totalStageIspFlowRate = 0d;
this.totalStageThrustForce = new ForceAccumulator(); this.totalStageThrustForce.Reset();
   
// Loop through all the active engines totalling the thrust, actual thrust and mass flow rates // Loop through all the active engines totalling the thrust, actual thrust and mass flow rates
// The thrust is totalled as vectors // The thrust is totalled as vectors
foreach (EngineSim engine in this.activeEngines) for (int i = 0; i < activeEngines.Count; ++i)
{ {
  EngineSim engine = activeEngines[i];
   
this.simpleTotalThrust += engine.thrust; this.simpleTotalThrust += engine.thrust;
this.vecThrust += ((float)engine.thrust * engine.thrustVec); this.vecThrust += ((float)engine.thrust * engine.thrustVec);
this.vecActualThrust += ((float)engine.actualThrust * engine.thrustVec); this.vecActualThrust += ((float)engine.actualThrust * engine.thrustVec);
   
this.totalStageFlowRate += engine.ResourceConsumptions.Mass; this.totalStageFlowRate += engine.ResourceConsumptions.Mass;
this.totalStageIspFlowRate += engine.ResourceConsumptions.Mass * engine.isp; this.totalStageIspFlowRate += engine.ResourceConsumptions.Mass * engine.isp;
   
foreach (AppliedForce f in engine.appliedForces) { for (int j = 0; j < engine.appliedForces.Count; ++j)
this.totalStageThrustForce.AddForce(f); {
  this.totalStageThrustForce.AddForce(engine.appliedForces[j]);
} }
} }
   
//MonoBehaviour.print("vecThrust = " + vecThrust.ToString() + " magnitude = " + vecThrust.magnitude); //MonoBehaviour.print("vecThrust = " + vecThrust.ToString() + " magnitude = " + vecThrust.magnitude);
   
this.totalStageThrust = this.vecThrust.magnitude; this.totalStageThrust = this.vecThrust.magnitude;
this.totalStageActualThrust = this.vecActualThrust.magnitude; this.totalStageActualThrust = this.vecActualThrust.magnitude;
   
// Calculate the effective isp at this point // Calculate the effective isp at this point
if (this.totalStageFlowRate > 0d && this.totalStageIspFlowRate > 0d) if (this.totalStageFlowRate > 0d && this.totalStageIspFlowRate > 0d)
{ {
this.currentisp = this.totalStageIspFlowRate / this.totalStageFlowRate; this.currentisp = this.totalStageIspFlowRate / this.totalStageFlowRate;
} }
else else
{ {
this.currentisp = 0; this.currentisp = 0;
} }
} }
   
// This function does all the hard work of working out which engines are burning, which tanks are being drained // This function does all the hard work of working out which engines are burning, which tanks are being drained
// and setting the drain rates // and setting the drain rates
private void UpdateResourceDrains() private void UpdateResourceDrains()
{ {
// Update the active engines // Update the active engines
this.UpdateActiveEngines(); this.UpdateActiveEngines();
   
// Empty the draining resources set // Empty the draining resources set
this.drainingResources.Clear(); this.drainingResources.Clear();
   
// Reset the resource drains of all draining parts // Reset the resource drains of all draining parts
foreach (PartSim partSim in this.drainingParts) foreach (PartSim partSim in this.drainingParts)
{ {
partSim.ResourceDrains.Reset(); partSim.ResourceDrains.Reset();
} }
   
// Empty the draining parts set // Empty the draining parts set
this.drainingParts.Clear(); this.drainingParts.Clear();
   
// Loop through all the active engine modules // Loop through all the active engine modules
foreach (EngineSim engine in this.activeEngines) for (int i = 0; i < activeEngines.Count; ++i)
{ {
  EngineSim engine = activeEngines[i];
   
// Set the resource drains for this engine // Set the resource drains for this engine
if (engine.SetResourceDrains(this.allParts, this.allFuelLines, this.drainingParts)) if (engine.SetResourceDrains(this.allParts, this.allFuelLines, this.drainingParts))
{ {
// If it is active then add the consumed resource types to the set // If it is active then add the consumed resource types to the set
foreach (int type in engine.ResourceConsumptions.Types) for (int j = 0; j < engine.ResourceConsumptions.Types.Count; ++j)
{ {
this.drainingResources.Add(type); drainingResources.Add(engine.ResourceConsumptions.Types[j]);
} }
} }
} }
   
// Update the active engines again to remove any engines that have no fuel supply // Update the active engines again to remove any engines that have no fuel supply
this.UpdateActiveEngines(); this.UpdateActiveEngines();
   
if (SimManager.logOutput) if (SimManager.logOutput)
{ {
StringBuilder buffer = new StringBuilder(1024); StringBuilder buffer = new StringBuilder(1024);
buffer.AppendFormat("Active engines = {0:d}\n", this.activeEngines.Count); buffer.AppendFormat("Active engines = {0:d}\n", this.activeEngines.Count);
int i = 0; int i = 0;
foreach (EngineSim engine in this.activeEngines) foreach (EngineSim engine in this.activeEngines)
{ {
engine.DumpEngineToBuffer(buffer, "Engine " + (i++) + ":"); engine.DumpEngineToBuffer(buffer, "Engine " + (i++) + ":");
} }
MonoBehaviour.print(buffer); MonoBehaviour.print(buffer);
} }
} }
   
// This function works out if it is time to stage // This function works out if it is time to stage
private bool AllowedToStage() private bool AllowedToStage()
{ {
StringBuilder buffer = null; StringBuilder buffer = null;
if (SimManager.logOutput) if (SimManager.logOutput)
{ {
buffer = new StringBuilder(1024); buffer = new StringBuilder(1024);
buffer.AppendLine("AllowedToStage"); buffer.AppendLine("AllowedToStage");
buffer.AppendFormat("currentStage = {0:d}\n", this.currentStage); buffer.AppendFormat("currentStage = {0:d}\n", this.currentStage);
} }
   
if (this.activeEngines.Count > 0) if (this.activeEngines.Count > 0)
{ {
foreach (PartSim partSim in this.dontStageParts) for (int i = 0; i < dontStageParts.Count; ++i)
{ {
  PartSim partSim = dontStageParts[i];
   
if (SimManager.logOutput) if (SimManager.logOutput)
{ {
partSim.DumpPartToBuffer(buffer, "Testing: "); partSim.DumpPartToBuffer(buffer, "Testing: ");
} }
//buffer.AppendFormat("isSepratron = {0}\n", partSim.isSepratron ? "true" : "false"); //buffer.AppendFormat("isSepratron = {0}\n", partSim.isSepratron ? "true" : "false");
   
if (!partSim.isSepratron && !partSim.EmptyOf(this.drainingResources)) if (!partSim.isSepratron && !partSim.EmptyOf(this.drainingResources))
{ {
if (SimManager.logOutput) if (SimManager.logOutput)
{ {
partSim.DumpPartToBuffer(buffer, "Decoupled part not empty => false: "); partSim.DumpPartToBuffer(buffer, "Decoupled part not empty => false: ");
MonoBehaviour.print(buffer); MonoBehaviour.print(buffer);
} }
return false; return false;
} }
   
if (partSim.isEngine) if (partSim.isEngine)
{ {
foreach (EngineSim engine in this.activeEngines) for (int j = 0; j < activeEngines.Count; ++j)
{ {
  EngineSim engine = activeEngines[j];
   
if (engine.partSim == partSim) if (engine.partSim == partSim)
{ {
if (SimManager.logOutput) if (SimManager.logOutput)
{ {
partSim.DumpPartToBuffer(buffer, "Decoupled part is active engine => false: "); partSim.DumpPartToBuffer(buffer, "Decoupled part is active engine => false: ");
MonoBehaviour.print(buffer); MonoBehaviour.print(buffer);
} }
return false; return false;
} }
} }
} }
} }
} }
   
if (this.currentStage == 0 && this.doingCurrent) if (this.currentStage == 0 && this.doingCurrent)
{ {
if (SimManager.logOutput) if (SimManager.logOutput)
{ {
buffer.AppendLine("Current stage == 0 && doingCurrent => false"); buffer.AppendLine("Current stage == 0 && doingCurrent => false");
MonoBehaviour.print(buffer); MonoBehaviour.print(buffer);
} }
return false; return false;
} }
   
if (SimManager.logOutput) if (SimManager.logOutput)
{ {
buffer.AppendLine("Returning true"); buffer.AppendLine("Returning true");
MonoBehaviour.print(buffer); MonoBehaviour.print(buffer);
} }
return true; return true;
} }
   
// This function activates the next stage // This function activates the next stage
// currentStage must be updated before calling this function // currentStage must be updated before calling this function
private void ActivateStage() private void ActivateStage()
{ {
// Build a set of all the parts that will be decoupled // Build a set of all the parts that will be decoupled
decoupledParts.Clear(); decoupledParts.Clear();
foreach (PartSim partSim in this.allParts) for (int i = 0; i < allParts.Count; ++i)
{ {
  PartSim partSim = allParts[i];
   
if (partSim.decoupledInStage >= this.currentStage) if (partSim.decoupledInStage >= this.currentStage)
{ {
decoupledParts.Add(partSim); decoupledParts.Add(partSim);
} }
} }
   
foreach (PartSim partSim in decoupledParts) foreach (PartSim partSim in decoupledParts)
{ {
// Remove it from the all parts list // Remove it from the all parts list
this.allParts.Remove(partSim); this.allParts.Remove(partSim);
if (partSim.isEngine) if (partSim.isEngine)
{ {
// If it is an engine then loop through all the engine modules and remove all the ones from this engine part // If it is an engine then loop through all the engine modules and remove all the ones from this engine part
for (int i = this.allEngines.Count - 1; i >= 0; i--) for (int i = this.allEngines.Count - 1; i >= 0; i--)
{ {
if (this.allEngines[i].partSim == partSim) if (this.allEngines[i].partSim == partSim)
{ {
this.allEngines.RemoveAt(i); this.allEngines.RemoveAt(i);
} }
} }
} }
// If it is a fuel line then remove it from the list of all fuel lines // If it is a fuel line then remove it from the list of all fuel lines
if (partSim.isFuelLine) if (partSim.isFuelLine)
{ {
this.allFuelLines.Remove(partSim); this.allFuelLines.Remove(partSim);
} }
} }
   
// Loop through all the (remaining) parts // Loop through all the (remaining) parts
foreach (PartSim partSim in this.allParts) for (int i = 0; i < allParts.Count; ++i) {
{  
// Ask the part to remove all the parts that are decoupled // Ask the part to remove all the parts that are decoupled
partSim.RemoveAttachedParts(decoupledParts); allParts[i].RemoveAttachedParts(decoupledParts);
} }
   
// Now we loop through all the engines and activate those that are ignited in this stage // Now we loop through all the engines and activate those that are ignited in this stage
foreach (EngineSim engine in this.allEngines) for (int i = 0; i < allEngines.Count; ++i)
{ {
  EngineSim engine = allEngines[i];
if (engine.partSim.inverseStage == this.currentStage) if (engine.partSim.inverseStage == this.currentStage)
{ {
engine.isActive = true; engine.isActive = true;
} }
} }
} }
   
public void Dump() public void Dump()
{ {
StringBuilder buffer = new StringBuilder(1024); StringBuilder buffer = new StringBuilder(1024);
buffer.AppendFormat("Part count = {0:d}\n", this.allParts.Count); buffer.AppendFormat("Part count = {0:d}\n", this.allParts.Count);
   
// Output a nice tree view of the rocket // Output a nice tree view of the rocket
if (this.allParts.Count > 0) if (this.allParts.Count > 0)
{ {
PartSim root = this.allParts[0]; PartSim root = this.allParts[0];
while (root.parent != null) while (root.parent != null)
{ {
root = root.parent; root = root.parent;
} }
   
if (root.hasVessel) if (root.hasVessel)
{ {
buffer.AppendFormat("vesselName = '{0}' vesselType = {1}\n", this.vesselName, SimManager.GetVesselTypeString(this.vesselType)); buffer.AppendFormat("vesselName = '{0}' vesselType = {1}\n", this.vesselName, SimManager.GetVesselTypeString(this.vesselType));
} }
   
root.DumpPartToBuffer(buffer, "", this.allParts); root.DumpPartToBuffer(buffer, "", this.allParts);
} }
   
MonoBehaviour.print(buffer); MonoBehaviour.print(buffer);
} }
} }
} }
 Binary files a/Output/KerbalEngineer/KerbalEngineer.dll and b/Output/KerbalEngineer/KerbalEngineer.dll differ