Start of cleanup of all ModuleEngines related code that was hurriedly changed for KSP 1.0
Start of cleanup of all ModuleEngines related code that was hurriedly changed for KSP 1.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/>.
// //
   
namespace KerbalEngineer.Extensions namespace KerbalEngineer.Extensions
{ {
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using CompoundParts; using CompoundParts;
   
public static class PartExtensions public static class PartExtensions
{ {
//private static Part cachePart; //private static Part cachePart;
//private static PartModule cachePartModule; //private static PartModule cachePartModule;
//private static PartResource cachePartResource; //private static PartResource cachePartResource;
   
/// <summary> /// <summary>
/// Gets whether the part contains a specific resource. /// Gets whether the part contains a specific resource.
/// </summary> /// </summary>
public static bool ContainsResource(this Part part, int resourceId) public static bool ContainsResource(this Part part, int resourceId)
{ {
return part.Resources.Contains(resourceId); return part.Resources.Contains(resourceId);
} }
   
/// <summary> /// <summary>
/// Gets whether the part contains resources. /// Gets whether the part contains resources.
/// </summary> /// </summary>
public static bool ContainsResources(this Part part) public static bool ContainsResources(this Part part)
{ {
for (int i = 0; i < part.Resources.list.Count; ++i) for (int i = 0; i < part.Resources.list.Count; ++i)
{ {
if (part.Resources.list[i].amount > 0.0) if (part.Resources.list[i].amount > 0.0)
{ {
return true; return true;
} }
} }
return false; return false;
} }
   
/// <summary> /// <summary>
/// Gets whether the part has fuel. /// Gets whether the part has fuel.
/// </summary> /// </summary>
  /* not used
public static bool EngineHasFuel(this Part part) public static bool EngineHasFuel(this Part part)
{ {
PartModule cachePartModule = GetModule<ModuleEngines>(part); PartModule cachePartModule = GetModule<ModuleEngines>(part);
if (cachePartModule != null) if (cachePartModule != null)
{ {
return (cachePartModule as ModuleEngines).getFlameoutState; return (cachePartModule as ModuleEngines).getFlameoutState;
} }
   
cachePartModule = GetModuleMultiModeEngine(part); cachePartModule = GetModuleMultiModeEngine(part);
if (cachePartModule != null) if (cachePartModule != null)
{ {
return (cachePartModule as ModuleEnginesFX).getFlameoutState; return (cachePartModule as ModuleEnginesFX).getFlameoutState;
} }
   
return false; return false;
} }
  */
/// <summary> /// <summary>
/// Gets the cost of the part excluding resources. /// Gets the cost of the part excluding resources.
/// </summary> /// </summary>
public static double GetCostDry(this Part part) public static double GetCostDry(this Part part)
{ {
return part.partInfo.cost - GetResourceCostMax(part) + part.GetModuleCosts(0.0f); return part.partInfo.cost - GetResourceCostMax(part) + part.GetModuleCosts(0.0f);
} }
   
/// <summary> /// <summary>
/// Gets the cost of the part including maximum resources. /// Gets the cost of the part including maximum resources.
/// </summary> /// </summary>
public static double GetCostMax(this Part part) public static double GetCostMax(this Part part)
{ {
return part.partInfo.cost + part.GetModuleCosts(0.0f); return part.partInfo.cost + part.GetModuleCosts(0.0f);
} }
   
/// <summary> /// <summary>
/// Gets the cost of the part modules /// Gets the cost of the part modules
/// Same as stock but without mem allocation /// Same as stock but without mem allocation
/// </summary> /// </summary>
public static double GetModuleCostsNoAlloc(this Part part, float defaultCost) public static double GetModuleCostsNoAlloc(this Part part, float defaultCost)
{ {
float cost = 0f; float cost = 0f;
for (int i = 0; i < part.Modules.Count; i++) for (int i = 0; i < part.Modules.Count; i++)
{ {
PartModule pm = part.Modules[i]; PartModule pm = part.Modules[i];
if (pm is IPartCostModifier) if (pm is IPartCostModifier)
cost += (pm as IPartCostModifier).GetModuleCost(defaultCost); cost += (pm as IPartCostModifier).GetModuleCost(defaultCost);
} }
return cost; return cost;
} }
   
/// <summary> /// <summary>
/// Gets the cost of the part including resources. /// Gets the cost of the part including resources.
/// </summary> /// </summary>
public static double GetCostWet(this Part part) public static double GetCostWet(this Part part)
{ {
return part.partInfo.cost - GetResourceCostInverted(part) + part.GetModuleCostsNoAlloc(0.0f); // part.GetModuleCosts allocate 44B per call. return part.partInfo.cost - GetResourceCostInverted(part) + part.GetModuleCostsNoAlloc(0.0f); // part.GetModuleCosts allocate 44B per call.
} }
   
/// <summary> /// <summary>
/// Gets the dry mass of the part. /// Gets the dry mass of the part.
/// </summary> /// </summary>
public static double GetDryMass(this Part part) public static double GetDryMass(this Part part)
{ {
return (part.physicalSignificance == Part.PhysicalSignificance.FULL) ? part.mass : 0d; return (part.physicalSignificance == Part.PhysicalSignificance.FULL) ? part.mass : 0d;
} }
   
/// <summary> /// <summary>
/// Gets the maximum thrust of the part if it's an engine. /// Gets the maximum thrust of the part if it's an engine.
/// </summary> /// </summary>
  /* not used
public static double GetMaxThrust(this Part part) public static double GetMaxThrust(this Part part)
{ {
PartModule cachePartModule = GetModule<ModuleEngines>(part); PartModule cachePartModule = GetModule<ModuleEngines>(part);
if (cachePartModule != null) if (cachePartModule != null)
{ {
return (cachePartModule as ModuleEngines).maxThrust; return (cachePartModule as ModuleEngines).maxThrust;
} }
   
cachePartModule = GetModuleMultiModeEngine(part) ?? GetModule<ModuleEnginesFX>(part); cachePartModule = GetModuleMultiModeEngine(part) ?? GetModule<ModuleEnginesFX>(part);
if (cachePartModule != null) if (cachePartModule != null)
{ {
return (cachePartModule as ModuleEnginesFX).maxThrust; return (cachePartModule as ModuleEnginesFX).maxThrust;
} }
   
return 0.0; return 0.0;
} }
  */
   
/// <summary> /// <summary>
/// Gets the first typed PartModule in the part's module list. /// Gets the first typed PartModule in the part's module list.
/// </summary> /// </summary>
public static T GetModule<T>(this Part part) where T : PartModule public static T GetModule<T>(this Part part) where T : PartModule
{ {
for (int i = 0; i < part.Modules.Count; i++) for (int i = 0; i < part.Modules.Count; i++)
{ {
PartModule pm = part.Modules[i]; PartModule pm = part.Modules[i];
if (pm is T) if (pm is T)
return (T)pm; return (T)pm;
} }
return null; return null;
} }
   
/// <summary> /// <summary>
/// Gets a typed PartModule. /// Gets a typed PartModule.
/// </summary> /// </summary>
public static T GetModule<T>(this Part part, string className) where T : PartModule public static T GetModule<T>(this Part part, string className) where T : PartModule
{ {
return part.Modules[className] as T; return part.Modules[className] as T;
} }
   
/// <summary> /// <summary>
/// Gets a typed PartModule. /// Gets a typed PartModule.
/// </summary> /// </summary>
public static T GetModule<T>(this Part part, int classId) where T : PartModule public static T GetModule<T>(this Part part, int classId) where T : PartModule
{ {
return part.Modules[classId] as T; return part.Modules[classId] as T;
} }
   
/// <summary> /// <summary>
/// Gets a ModuleAlternator typed PartModule. /// Gets a ModuleAlternator typed PartModule.
/// </summary> /// </summary>
public static ModuleAlternator GetModuleAlternator(this Part part) public static ModuleAlternator GetModuleAlternator(this Part part)
{ {
return GetModule<ModuleAlternator>(part); return GetModule<ModuleAlternator>(part);
} }
   
/// <summary> /// <summary>
/// Gets a ModuleDeployableSolarPanel typed PartModule. /// Gets a ModuleDeployableSolarPanel typed PartModule.
/// </summary> /// </summary>
public static ModuleDeployableSolarPanel GetModuleDeployableSolarPanel(this Part part) public static ModuleDeployableSolarPanel GetModuleDeployableSolarPanel(this Part part)
{ {
return GetModule<ModuleDeployableSolarPanel>(part); return GetModule<ModuleDeployableSolarPanel>(part);
} }
   
/// <summary> /// <summary>
/// Gets a ModuleEngines typed PartModule. /// Gets a ModuleEngines typed PartModule.
/// </summary> /// </summary>
public static ModuleEngines GetModuleEngines(this Part part) public static ModuleEngines GetModuleEngines(this Part part)
{ {
return GetModule<ModuleEngines>(part); return GetModule<ModuleEngines>(part);
} }
   
public static ModuleEnginesFX GetModuleEnginesFx(this Part part) public static ModuleEnginesFX GetModuleEnginesFx(this Part part)
{ {
return GetModule<ModuleEnginesFX>(part); return GetModule<ModuleEnginesFX>(part);
} }
   
/// <summary> /// <summary>
/// Gets a ModuleGenerator typed PartModule. /// Gets a ModuleGenerator typed PartModule.
/// </summary> /// </summary>
public static ModuleGenerator GetModuleGenerator(this Part part) public static ModuleGenerator GetModuleGenerator(this Part part)
{ {
return GetModule<ModuleGenerator>(part); return GetModule<ModuleGenerator>(part);
} }
   
/// <summary> /// <summary>
/// Gets a ModuleGimbal typed PartModule. /// Gets a ModuleGimbal typed PartModule.
/// </summary> /// </summary>
public static ModuleGimbal GetModuleGimbal(this Part part) public static ModuleGimbal GetModuleGimbal(this Part part)
{ {
return GetModule<ModuleGimbal>(part); return GetModule<ModuleGimbal>(part);
} }
   
/// <summary> /// <summary>
/// Gets the current selected ModuleEnginesFX. /// Gets the current selected ModuleEnginesFX.
/// </summary> /// </summary>
public static ModuleEnginesFX GetModuleMultiModeEngine(this Part part) public static ModuleEnginesFX GetModuleMultiModeEngine(this Part part)
{ {
ModuleEnginesFX moduleEngineFx; ModuleEnginesFX moduleEngineFx;
string mode = GetModule<MultiModeEngine>(part).mode; MultiModeEngine multiMod = GetModule<MultiModeEngine>(part);
for (int i = 0; i < part.Modules.Count; ++i) if (multiMod != null)
{ {
moduleEngineFx = part.Modules[i] as ModuleEnginesFX; string mode = multiMod.mode;
if (moduleEngineFx != null && moduleEngineFx.engineID == mode) for (int i = 0; i < part.Modules.Count; ++i)
{ {
return moduleEngineFx; moduleEngineFx = part.Modules[i] as ModuleEnginesFX;
  if (moduleEngineFx != null && moduleEngineFx.engineID == mode)
  {
  return moduleEngineFx;
  }
} }
} }
return null; return null;
} }
   
/// <summary> /// <summary>
/// Gets a ModuleParachute typed PartModule. /// Gets a ModuleParachute typed PartModule.
/// </summary> /// </summary>
public static ModuleParachute GetModuleParachute(this Part part) public static ModuleParachute GetModuleParachute(this Part part)
{ {
return GetModule<ModuleParachute>(part); return GetModule<ModuleParachute>(part);
} }
   
public static ModuleRCS GetModuleRcs(this Part part) public static ModuleRCS GetModuleRcs(this Part part)
{ {
return GetModule<ModuleRCS>(part); return GetModule<ModuleRCS>(part);
} }
   
/// <summary> /// <summary>
/// Gets a typed list of PartModules. /// Gets a typed list of PartModules.
/// </summary> /// </summary>
public static List<T> GetModules<T>(this Part part) where T : PartModule public static List<T> GetModules<T>(this Part part) where T : PartModule
{ {
List<T> list = new List<T>(); List<T> list = new List<T>();
for (int i = 0; i < part.Modules.Count; ++i) for (int i = 0; i < part.Modules.Count; ++i)
{ {
T module = part.Modules[i] as T; T module = part.Modules[i] as T;
if (module != null) if (module != null)
{ {
list.Add(module); list.Add(module);
} }
} }
return list; return list;
} }
   
public static ProtoModuleDecoupler GetProtoModuleDecoupler(this Part part) public static ProtoModuleDecoupler GetProtoModuleDecoupler(this Part part)
{ {
PartModule cachePartModule = GetModule<ModuleDecouple>(part); PartModule cachePartModule = GetModule<ModuleDecouple>(part);
if (cachePartModule == null) if (cachePartModule == null)
{ {
cachePartModule = GetModule<ModuleAnchoredDecoupler>(part); cachePartModule = GetModule<ModuleAnchoredDecoupler>(part);
} }
if (cachePartModule != null) if (cachePartModule != null)
{ {
return new ProtoModuleDecoupler(cachePartModule); return new ProtoModuleDecoupler(cachePartModule);
} }
   
return null; return null;
} }
   
/// <summary> /// <summary>
/// Gets a generic proto engine for the current engine module attached to the part. /// Gets a generic proto engine for the current engine module attached to the part.
/// </summary> /// </summary>
public static ProtoModuleEngine GetProtoModuleEngine(this Part part) public static ProtoModuleEngine GetProtoModuleEngine(this Part part)
{ {
PartModule cachePartModule = GetModule<ModuleEngines>(part); PartModule cachePartModule = GetModule<ModuleEngines>(part);
if (cachePartModule != null) if (cachePartModule != null)
{ {
return new ProtoModuleEngine(cachePartModule); return new ProtoModuleEngine(cachePartModule);
} }
   
cachePartModule = GetModuleMultiModeEngine(part) ?? GetModule<ModuleEnginesFX>(part); cachePartModule = GetModuleMultiModeEngine(part) ?? GetModule<ModuleEnginesFX>(part);
if (cachePartModule != null) if (cachePartModule != null)
{ {
return new ProtoModuleEngine(cachePartModule); return new ProtoModuleEngine(cachePartModule);
} }
   
return null; return null;
} }
   
/// <summary> /// <summary>
/// Gets the cost of the part's contained resources. /// Gets the cost of the part's contained resources.
/// </summary> /// </summary>
public static double GetResourceCost(this Part part) public static double GetResourceCost(this Part part)
{ {
double cost = 0.0; double cost = 0.0;
for (int i = 0; i < part.Resources.list.Count; ++i) for (int i = 0; i < part.Resources.list.Count; ++i)
{ {
PartResource cachePartResource = part.Resources.list[i]; PartResource cachePartResource = part.Resources.list[i];
cost = cost + (cachePartResource.amount * cachePartResource.info.unitCost); cost = cost + (cachePartResource.amount * cachePartResource.info.unitCost);
} }
return cost; return cost;
} }
   
/// <summary> /// <summary>
/// Gets the cost of the part's contained resources, inverted. /// Gets the cost of the part's contained resources, inverted.
/// </summary> /// </summary>
public static double GetResourceCostInverted(this Part part) public static double GetResourceCostInverted(this Part part)
{ {
double sum = 0; double sum = 0;
for (int i = 0; i < part.Resources.list.Count; i++) for (int i = 0; i < part.Resources.list.Count; i++)
{ {
PartResource r = part.Resources.list[i]; PartResource r = part.Resources.list[i];
sum += (r.maxAmount - r.amount) * r.info.unitCost; sum += (r.maxAmount - r.amount) * r.info.unitCost;
} }
return sum; return sum;
} }
   
/// <summary> /// <summary>
/// Gets the cost of the part's maximum contained resources. /// Gets the cost of the part's maximum contained resources.
/// </summary> /// </summary>
public static double GetResourceCostMax(this Part part) public static double GetResourceCostMax(this Part part)
{ {
double cost = 0.0; double cost = 0.0;
for (int i = 0; i < part.Resources.list.Count; ++i) for (int i = 0; i < part.Resources.list.Count; ++i)
{ {
PartResource cachePartResource = part.Resources.list[i]; PartResource cachePartResource = part.Resources.list[i];
cost = cost + (cachePartResource.maxAmount * cachePartResource.info.unitCost); cost = cost + (cachePartResource.maxAmount * cachePartResource.info.unitCost);
} }
return cost; return cost;
} }
   
/// <summary> /// <summary>
/// Gets the current specific impulse for the engine. /// Gets the current specific impulse for the engine.
/// </summary> /// </summary>
  /* not used
public static double GetSpecificImpulse(this Part part, float atmosphere) public static double GetSpecificImpulse(this Part part, float atmosphere)
{ {
PartModule cachePartModule = GetModule<ModuleEngines>(part); PartModule cachePartModule = GetModule<ModuleEngines>(part);
if (cachePartModule != null) if (cachePartModule != null)
{ {
return (cachePartModule as ModuleEngines).atmosphereCurve.Evaluate(atmosphere); return (cachePartModule as ModuleEngines).atmosphereCurve.Evaluate(atmosphere);
} }
   
cachePartModule = GetModuleMultiModeEngine(part) ?? GetModule<ModuleEnginesFX>(part); cachePartModule = GetModuleMultiModeEngine(part) ?? GetModule<ModuleEnginesFX>(part);
if (cachePartModule != null) if (cachePartModule != null)
{ {
return (cachePartModule as ModuleEnginesFX).atmosphereCurve.Evaluate(atmosphere); return (cachePartModule as ModuleEnginesFX).atmosphereCurve.Evaluate(atmosphere);
} }
   
return 0.0; return 0.0;
} }
  */
   
/// <summary> /// <summary>
/// Gets the total mass of the part including resources. /// Gets the total mass of the part including resources.
/// </summary> /// </summary>
public static double GetWetMass(this Part part) public static double GetWetMass(this Part part)
{ {
return (part.physicalSignificance == Part.PhysicalSignificance.FULL) ? part.mass + part.GetResourceMass() : part.GetResourceMass(); return (part.physicalSignificance == Part.PhysicalSignificance.FULL) ? part.mass + part.GetResourceMass() : part.GetResourceMass();
} }
   
/// <summary> /// <summary>
/// Gets whether the part contains a PartModule. /// Gets whether the part contains a PartModule.
/// </summary> /// </summary>
public static bool HasModule<T>(this Part part) where T : PartModule public static bool HasModule<T>(this Part part) where T : PartModule
{ {
for (int i = 0; i < part.Modules.Count; i++) for (int i = 0; i < part.Modules.Count; i++)
{ {
if (part.Modules[i] is T) if (part.Modules[i] is T)
return true; return true;
} }
return false; return false;
} }
   
/// <summary> /// <summary>
/// Gets whether the part contains a PartModule conforming to the supplied predicate. /// Gets whether the part contains a PartModule conforming to the supplied predicate.
/// </summary> /// </summary>
public static bool HasModule<T>(this Part part, Func<T, bool> predicate) where T : PartModule public static bool HasModule<T>(this Part part, Func<T, bool> predicate) where T : PartModule
{ {
for (int i = 0; i < part.Modules.Count; i++) for (int i = 0; i < part.Modules.Count; i++)
{ {
PartModule pm = part.Modules[i]; PartModule pm = part.Modules[i];
if (pm is T && predicate(pm as T)) if (pm is T && predicate(pm as T))
return true; return true;
} }
return false; return false;
} }
   
/// <summary> /// <summary>
/// Gets whether the part contains a PartModule. /// Gets whether the part contains a PartModule.
/// </summary> /// </summary>
public static bool HasModule(this Part part, string className) public static bool HasModule(this Part part, string className)
{ {
return part.Modules.Contains(className); return part.Modules.Contains(className);
} }
   
/// <summary> /// <summary>
/// Gets whether the part contains a PartModule. /// Gets whether the part contains a PartModule.
/// </summary> /// </summary>
public static bool HasModule(this Part part, int moduleId) public static bool HasModule(this Part part, int moduleId)
{ {
return part.Modules.Contains(moduleId); return part.Modules.Contains(moduleId);
} }
   
/// <summary> /// <summary>
/// Gets whether the part has a one shot animation. /// Gets whether the part has a one shot animation.
/// </summary> /// </summary>
public static bool HasOneShotAnimation(this Part part) public static bool HasOneShotAnimation(this Part part)
{ {
PartModule cachePartModule = GetModule<ModuleAnimateGeneric>(part); PartModule cachePartModule = GetModule<ModuleAnimateGeneric>(part);
return cachePartModule != null && (cachePartModule as ModuleAnimateGeneric).isOneShot; return cachePartModule != null && (cachePartModule as ModuleAnimateGeneric).isOneShot;
} }
   
/// <summary> /// <summary>
/// Gets whether the part is a command module. /// Gets whether the part is a command module.
/// </summary> /// </summary>
public static bool IsCommandModule(this Part part) public static bool IsCommandModule(this Part part)
{ {
return HasModule<ModuleCommand>(part); return HasModule<ModuleCommand>(part);
} }
   
/// <summary> /// <summary>
/// Gets whether the part is decoupled in a specified stage. /// Gets whether the part is decoupled in a specified stage.
/// </summary> /// </summary>
public static bool IsDecoupledInStage(this Part part, int stage) public static bool IsDecoupledInStage(this Part part, int stage)
{ {
if ((IsDecoupler(part) || IsLaunchClamp(part)) && part.inverseStage == stage) if ((IsDecoupler(part) || IsLaunchClamp(part)) && part.inverseStage == stage)
{ {
return true; return true;
} }
if (part.parent == null) if (part.parent == null)
{ {
return false; return false;
} }
return IsDecoupledInStage(part.parent, stage); return IsDecoupledInStage(part.parent, stage);
} }
   
/// <summary> /// <summary>
/// Gets whether the part is a decoupler. /// Gets whether the part is a decoupler.
/// </summary> /// </summary>
public static bool IsDecoupler(this Part part) public static bool IsDecoupler(this Part part)
{ {
return HasModule<ModuleDecouple>(part) || HasModule<ModuleAnchoredDecoupler>(part); return HasModule<ModuleDecouple>(part) || HasModule<ModuleAnchoredDecoupler>(part);
} }
   
/// <summary> /// <summary>
/// Gets whether the part is an active engine. /// Gets whether the part is an active engine.
/// </summary> /// </summary>
public static bool IsEngine(this Part part) public static bool IsEngine(this Part part)
{ {
return HasModule<ModuleEngines>(part) || HasModule<ModuleEnginesFX>(part); return HasModule<ModuleEngines>(part) || HasModule<ModuleEnginesFX>(part);
} }
   
/// <summary> /// <summary>
/// Gets whether the part is a fuel line. /// Gets whether the part is a fuel line.
/// </summary> /// </summary>
public static bool IsFuelLine(this Part part) public static bool IsFuelLine(this Part part)
{ {
return HasModule<CModuleFuelLine>(part); return HasModule<CModuleFuelLine>(part);
} }
   
/// <summary> /// <summary>
/// Gets whether the part is a generator. /// Gets whether the part is a generator.
/// </summary> /// </summary>
public static bool IsGenerator(this Part part) public static bool IsGenerator(this Part part)
{ {
return HasModule<ModuleGenerator>(part); return HasModule<ModuleGenerator>(part);
} }
   
/// <summary> /// <summary>
/// Gets whether the part is a launch clamp. /// Gets whether the part is a launch clamp.
/// </summary> /// </summary>
public static bool IsLaunchClamp(this Part part) public static bool IsLaunchClamp(this Part part)
{ {
return HasModule<LaunchClamp>(part); return HasModule<LaunchClamp>(part);
} }
   
/// <summary> /// <summary>
/// Gets whether the part is a parachute. /// Gets whether the part is a parachute.
/// </summary> /// </summary>
public static bool IsParachute(this Part part) public static bool IsParachute(this Part part)
{ {
return HasModule<ModuleParachute>(part); return HasModule<ModuleParachute>(part);
} }
   
/// <summary> /// <summary>
/// Gets whether the part is considered a primary part on the vessel. /// Gets whether the part is considered a primary part on the vessel.
/// </summary> /// </summary>
public static bool IsPrimary(this Part part, List<Part> partsList, PartModule module) public static bool IsPrimary(this Part part, List<Part> partsList, PartModule module)
{ {
for (int i = 0; i < partsList.Count; i++) for (int i = 0; i < partsList.Count; i++)
{ {
var vesselPart = partsList[i]; var vesselPart = partsList[i];
if (!vesselPart.HasModule(module.ClassID)) if (!vesselPart.HasModule(module.ClassID))
{ {
continue; continue;
} }
if (vesselPart == part) if (vesselPart == part)
{ {
return true; return true;
} }
break; break;
} }
   
return false; return false;
} }
   
public static bool IsRcsModule(this Part part) public static bool IsRcsModule(this Part part)
{ {
return HasModule<ModuleRCS>(part); return HasModule<ModuleRCS>(part);
} }
   
/// <summary> /// <summary>
/// Gets whether the part is a sepratron. /// Gets whether the part is a sepratron.
/// </summary> /// </summary>
public static bool IsSepratron(this Part part) public static bool IsSepratron(this Part part)
{ {
return IsSolidRocket(part) && part.ActivatesEvenIfDisconnected && IsDecoupledInStage(part, part.inverseStage); return IsSolidRocket(part) && part.ActivatesEvenIfDisconnected && IsDecoupledInStage(part, part.inverseStage);
} }
   
/// <summary> /// <summary>
/// Gets whether the part is a deployable solar panel. /// Gets whether the part is a deployable solar panel.
/// </summary> /// </summary>
public static bool IsSolarPanel(this Part part) public static bool IsSolarPanel(this Part part)
{ {
return HasModule<ModuleDeployableSolarPanel>(part); return HasModule<ModuleDeployableSolarPanel>(part);
} }
   
/// <summary> /// <summary>
/// Gets whether the part is a solid rocket motor. /// Gets whether the part is a solid rocket motor.
/// </summary> /// </summary>
public static bool IsSolidRocket(this Part part) public static bool IsSolidRocket(this Part part)
{ {
return (part.HasModule<ModuleEngines>() && part.GetModuleEngines().throttleLocked) || (part.HasModule<ModuleEnginesFX>() && part.GetModuleEnginesFx().throttleLocked); return (part.HasModule<ModuleEngines>() && part.GetModuleEngines().throttleLocked) || (part.HasModule<ModuleEnginesFX>() && part.GetModuleEnginesFx().throttleLocked);
} }
   
public class ProtoModuleDecoupler public class ProtoModuleDecoupler
{ {
private readonly PartModule module; private readonly PartModule module;
   
public ProtoModuleDecoupler(PartModule module) public ProtoModuleDecoupler(PartModule module)
{ {
this.module = module; this.module = module;
   
if (this.module is ModuleDecouple) if (this.module is ModuleDecouple)
{ {
SetModuleDecouple(); SetModuleDecouple();
} }
else if (this.module is ModuleAnchoredDecoupler) else if (this.module is ModuleAnchoredDecoupler)
{ {
SetModuleAnchoredDecoupler(); SetModuleAnchoredDecoupler();
} }
} }
   
public double EjectionForce { get; private set; } public double EjectionForce { get; private set; }
public bool IsOmniDecoupler { get; private set; } public bool IsOmniDecoupler { get; private set; }
   
private void SetModuleAnchoredDecoupler() private void SetModuleAnchoredDecoupler()
{ {
ModuleAnchoredDecoupler decoupler = module as ModuleAnchoredDecoupler; ModuleAnchoredDecoupler decoupler = module as ModuleAnchoredDecoupler;
if (decoupler == null) if (decoupler == null)
{ {
return; return;
} }
   
EjectionForce = decoupler.ejectionForce; EjectionForce = decoupler.ejectionForce;
} }
   
private void SetModuleDecouple() private void SetModuleDecouple()
{ {
ModuleDecouple decoupler = module as ModuleDecouple; ModuleDecouple decoupler = module as ModuleDecouple;
if (decoupler == null) if (decoupler == null)
{ {
return; return;
} }
   
EjectionForce = decoupler.ejectionForce; EjectionForce = decoupler.ejectionForce;
IsOmniDecoupler = decoupler.isOmniDecoupler; IsOmniDecoupler = decoupler.isOmniDecoupler;
} }
} }
   
  // This needs updating to handle multi-mode engines and engines with multiple ModuleEngines correctly.
  // It currently just shows the stats of the currently active module for multi-mode engines and just
  // the first ModuleEngines for engines with multiple modules.
  // It should really show all the modes for multi-mode engines as separate sections.
  // For other engines with multiple ModuleEngines it should combine the separate modules into a single set of data
  // The constructor should be changed to take the Part itself. It can be called if HasModule<ModuleEngines>() is true.
public class ProtoModuleEngine public class ProtoModuleEngine
{ {
private readonly PartModule module; private readonly PartModule module;
   
public ProtoModuleEngine(PartModule module) public ProtoModuleEngine(PartModule module)
{ {
this.module = module; this.module = module;
   
if (module is ModuleEngines) if (module is ModuleEngines)
{ {
SetModuleEngines(); SetModuleEngines();
} }
} }
   
public double MaximumThrust { get; private set; } public double MaximumThrust { get; private set; }
public double MinimumThrust { get; private set; } public double MinimumThrust { get; private set; }
public List<Propellant> Propellants { get; private set; } public List<Propellant> Propellants { get; private set; }
   
public float GetSpecificImpulse(float atmosphere) public float GetSpecificImpulse(float atmosphere)
{ {
if (module is ModuleEngines) if (module is ModuleEngines)
{ {
return (module as ModuleEngines).atmosphereCurve.Evaluate(atmosphere); return (module as ModuleEngines).atmosphereCurve.Evaluate(atmosphere);
} }
return 0.0f; return 0.0f;
} }
   
private void SetModuleEngines() private void SetModuleEngines()
{ {
ModuleEngines engine = module as ModuleEngines; ModuleEngines engine = module as ModuleEngines;
if (engine == null) if (engine == null)
{ {
return; return;
} }
   
MaximumThrust = engine.maxThrust * (engine.thrustPercentage * 0.01); MaximumThrust = engine.maxThrust * (engine.thrustPercentage * 0.01);
MinimumThrust = engine.minThrust; MinimumThrust = engine.minThrust;
Propellants = engine.propellants; Propellants = engine.propellants;
} }
   
private void SetModuleEnginesFx()  
{  
ModuleEnginesFX engine = module as ModuleEnginesFX;  
if (engine == null)  
{  
return;  
}  
   
MaximumThrust = engine.maxThrust * (engine.thrustPercentage * 0.01);  
MinimumThrust = engine.minThrust;  
Propellants = engine.propellants;  
}  
} }
} }
} }
// //
// Kerbal Engineer Redux // Kerbal Engineer Redux
// //
// Copyright (C) 2014 CYBUTEK // Copyright (C) 2015 CYBUTEK
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
// //
   
#region Using Directives  
   
using System;  
   
using KerbalEngineer.Extensions;  
using KerbalEngineer.Helpers;  
   
#endregion  
   
namespace KerbalEngineer.Flight.Readouts.Rendezvous namespace KerbalEngineer.Flight.Readouts.Rendezvous
{ {
  using System;
  using Extensions;
  using Helpers;
   
public class RendezvousProcessor : IUpdatable, IUpdateRequest public class RendezvousProcessor : IUpdatable, IUpdateRequest
{ {
#region Fields  
   
private static readonly RendezvousProcessor instance = new RendezvousProcessor(); private static readonly RendezvousProcessor instance = new RendezvousProcessor();
   
private Orbit originOrbit; private Orbit originOrbit;
private Orbit targetOrbit; private Orbit targetOrbit;
   
#endregion  
   
#region Properties  
   
/// <summary> /// <summary>
/// Gets the target's altitude above its reference body. /// Gets the target's altitude above its reference body.
/// </summary> /// </summary>
public static double AltitudeSeaLevel { get; private set; } public static double AltitudeSeaLevel { get; private set; }
   
/// <summary> /// <summary>
/// Gets the angle from the origin position to the ascending node. /// Gets the angle from the origin position to the ascending node.
/// </summary> /// </summary>
public static double AngleToAscendingNode { get; private set; } public static double AngleToAscendingNode { get; private set; }
   
/// <summary> /// <summary>
/// Gets the angle from the origin position to the descending node. /// Gets the angle from the origin position to the descending node.
/// </summary> /// </summary>
public static double AngleToDescendingNode { get; private set; } public static double AngleToDescendingNode { get; private set; }
   
/// <summary> /// <summary>
/// Gets the target's apoapsis above its reference body. /// Gets the target's apoapsis above its reference body.
/// </summary> /// </summary>
public static double ApoapsisHeight { get; private set; } public static double ApoapsisHeight { get; private set; }
   
/// <summary> /// <summary>
/// Gets the distance from the origin position to the target position. /// Gets the distance from the origin position to the target position.
/// </summary> /// </summary>
public static double Distance { get; private set; } public static double Distance { get; private set; }
   
/// <summary> /// <summary>
/// Gets the current instance of the rendezvous processor. /// Gets the current instance of the rendezvous processor.
/// </summary> /// </summary>
public static RendezvousProcessor Instance public static RendezvousProcessor Instance
{ {
get { return instance; } get
  {
  return instance;
  }
} }
   
/// <summary> /// <summary>
/// Gets the difference in angle from the origin position to where it is most efficient to burn for an encounter. /// Gets the difference in angle from the origin position to where it is most efficient to burn for an encounter.
/// </summary> /// </summary>
public static double InterceptAngle { get; private set; } public static double InterceptAngle { get; private set; }
   
/// <summary> /// <summary>
/// Gets the orbital period of the target orbit. /// Gets the orbital period of the target orbit.
/// </summary> /// </summary>
public static double OrbitalPeriod { get; private set; } public static double OrbitalPeriod { get; private set; }
   
/// <summary> /// <summary>
/// Gets the target's periapsis above its reference body. /// Gets the target's periapsis above its reference body.
/// </summary> /// </summary>
public static double PeriapsisHeight { get; private set; } public static double PeriapsisHeight { get; private set; }
   
/// <summary> /// <summary>
/// Gets the difference in angle from the origin position to the target position based on a common reference. /// Gets the difference in angle from the origin position to the target position based on a common reference.
/// </summary> /// </summary>
public static double PhaseAngle { get; private set; } public static double PhaseAngle { get; private set; }
   
/// <summary> /// <summary>
/// Gets the angular difference between the origin and target orbits. /// Gets the angular difference between the origin and target orbits.
/// </summary> /// </summary>
public static double RelativeInclination { get; private set; } public static double RelativeInclination { get; private set; }
   
/// <summary> /// <summary>
/// Gets the relative orbital speed between the vessel and target. /// Gets the relative orbital speed between the vessel and target.
/// </summary> /// </summary>
public static double RelativeSpeed { get; private set; } public static double RelativeSpeed { get; private set; }
   
/// <summary> /// <summary>
/// Gets the relative orbital velocity between the vessel and target. /// Gets the relative orbital velocity between the vessel and target.
/// </summary> /// </summary>
public static double RelativeVelocity { get; private set; } public static double RelativeVelocity { get; private set; }
   
/// <summary> /// <summary>
/// Gets the semi-major axis of the target orbit. /// Gets the semi-major axis of the target orbit.
/// </summary> /// </summary>
public static double SemiMajorAxis { get; private set; } public static double SemiMajorAxis { get; private set; }
   
/// <summary> /// <summary>
/// Gets the semi-minor axis of the target orbit. /// Gets the semi-minor axis of the target orbit.
/// </summary> /// </summary>
public static double SemiMinorAxis { get; private set; } public static double SemiMinorAxis { get; private set; }
   
/// <summary> /// <summary>
/// Gets whether the details are ready to be shown. /// Gets whether the details are ready to be shown.
/// </summary> /// </summary>
public static bool ShowDetails { get; private set; } public static bool ShowDetails { get; private set; }
   
/// <summary> /// <summary>
/// Gets the target's time to apoapsis. /// Gets the target's time to apoapsis.
/// </summary> /// </summary>
public static double TimeToApoapsis { get; private set; } public static double TimeToApoapsis { get; private set; }
   
/// <summary> /// <summary>
/// Gets the time it will take to reach the ascending node. /// Gets the time it will take to reach the ascending node.
/// </summary> /// </summary>
public static double TimeToAscendingNode { get; private set; } public static double TimeToAscendingNode { get; private set; }
   
/// <summary> /// <summary>
/// Gets the time it will take to reach the descending node. /// Gets the time it will take to reach the descending node.
/// </summary> /// </summary>
public static double TimeToDescendingNode { get; private set; } public static double TimeToDescendingNode { get; private set; }
   
/// <summary> /// <summary>
/// Gets the target's time to periapsis. /// Gets the target's time to periapsis.
/// </summary> /// </summary>
public static double TimeToPeriapsis { get; private set; } public static double TimeToPeriapsis { get; private set; }
   
/// <summary> /// <summary>
/// Gets and sets whether the updatable object should be updated. /// Gets and sets whether the updatable object should be updated.
/// </summary> /// </summary>
public bool UpdateRequested { get; set; } public bool UpdateRequested { get; set; }
   
#endregion  
   
#region Methods: public  
   
/// <summary> /// <summary>
/// Request and update to calculate the details. /// Request and update to calculate the details.
/// </summary> /// </summary>
public static void RequestUpdate() public static void RequestUpdate()
{ {
instance.UpdateRequested = true; instance.UpdateRequested = true;
} }
   
/// <summary> /// <summary>
/// Updates the details by recalculating if requested. /// Updates the details by recalculating if requested.
/// </summary> /// </summary>
public void Update() public void Update()
{ {
if (FlightGlobals.fetch.VesselTarget == null) if (FlightGlobals.fetch == null ||
  FlightGlobals.fetch.VesselTarget == null ||
  FlightGlobals.ActiveVessel == null ||
  FlightGlobals.ActiveVessel.targetObject == null ||
  FlightGlobals.ActiveVessel.targetObject.GetOrbit() == null ||
  FlightGlobals.ship_orbit == null ||
  FlightGlobals.ship_orbit.referenceBody == null)
{ {
ShowDetails = false; ShowDetails = false;
return; return;
} }
   
ShowDetails = true; ShowDetails = true;
   
this.targetOrbit = FlightGlobals.fetch.VesselTarget.GetOrbit(); targetOrbit = FlightGlobals.fetch.VesselTarget.GetOrbit();
this.originOrbit = (FlightGlobals.ship_orbit.referenceBody == Planetarium.fetch.Sun || FlightGlobals.ship_orbit.referenceBody == FlightGlobals.ActiveVessel.targetObject.GetOrbit().referenceBody) originOrbit = (FlightGlobals.ship_orbit.referenceBody == Planetarium.fetch.Sun ||
  FlightGlobals.ship_orbit.referenceBody == FlightGlobals.ActiveVessel.targetObject.GetOrbit().referenceBody)
? FlightGlobals.ship_orbit ? FlightGlobals.ship_orbit
: FlightGlobals.ship_orbit.referenceBody.orbit; : FlightGlobals.ship_orbit.referenceBody.orbit;
   
RelativeInclination = this.originOrbit.GetRelativeInclination(this.targetOrbit); RelativeInclination = originOrbit.GetRelativeInclination(targetOrbit);
RelativeVelocity = FlightGlobals.ship_tgtSpeed; RelativeVelocity = FlightGlobals.ship_tgtSpeed;
RelativeSpeed = FlightGlobals.ship_obtSpeed - this.targetOrbit.orbitalSpeed; RelativeSpeed = FlightGlobals.ship_obtSpeed - targetOrbit.orbitalSpeed;
PhaseAngle = this.originOrbit.GetPhaseAngle(this.targetOrbit); PhaseAngle = originOrbit.GetPhaseAngle(targetOrbit);
InterceptAngle = this.CalcInterceptAngle(); InterceptAngle = CalcInterceptAngle();
TimeToAscendingNode = this.originOrbit.GetTimeToVector(this.GetAscendingNode()); TimeToAscendingNode = originOrbit.GetTimeToVector(GetAscendingNode());
TimeToDescendingNode = this.originOrbit.GetTimeToVector(this.GetDescendingNode()); TimeToDescendingNode = originOrbit.GetTimeToVector(GetDescendingNode());
AngleToAscendingNode = this.originOrbit.GetAngleToVector(this.GetAscendingNode()); AngleToAscendingNode = originOrbit.GetAngleToVector(GetAscendingNode());
AngleToDescendingNode = this.originOrbit.GetAngleToVector(this.GetDescendingNode()); AngleToDescendingNode = originOrbit.GetAngleToVector(GetDescendingNode());
AltitudeSeaLevel = this.targetOrbit.altitude; AltitudeSeaLevel = targetOrbit.altitude;
ApoapsisHeight = this.targetOrbit.ApA; ApoapsisHeight = targetOrbit.ApA;
PeriapsisHeight = this.targetOrbit.PeA; PeriapsisHeight = targetOrbit.PeA;
TimeToApoapsis = this.targetOrbit.timeToAp; TimeToApoapsis = targetOrbit.timeToAp;
TimeToPeriapsis = this.targetOrbit.timeToPe; TimeToPeriapsis = targetOrbit.timeToPe;
SemiMajorAxis = this.targetOrbit.semiMajorAxis; SemiMajorAxis = targetOrbit.semiMajorAxis;
SemiMinorAxis = this.targetOrbit.semiMinorAxis; SemiMinorAxis = targetOrbit.semiMinorAxis;
   
Distance = Vector3d.Distance(this.targetOrbit.pos, this.originOrbit.pos); Distance = Vector3d.Distance(targetOrbit.pos, originOrbit.pos);
OrbitalPeriod = this.targetOrbit.period; OrbitalPeriod = targetOrbit.period;
} }
   
#endregion  
   
#region Methods: private  
   
private double CalcInterceptAngle() private double CalcInterceptAngle()
{ {
var originRadius = (this.originOrbit.semiMinorAxis + this.originOrbit.semiMajorAxis) * 0.5; double originRadius = (originOrbit.semiMinorAxis + originOrbit.semiMajorAxis) * 0.5;
var targetRadius = (this.targetOrbit.semiMinorAxis + this.targetOrbit.semiMajorAxis) * 0.5; double targetRadius = (targetOrbit.semiMinorAxis + targetOrbit.semiMajorAxis) * 0.5;
var angle = 180.0 * (1.0 - Math.Pow((originRadius + targetRadius) / (2.0 * targetRadius), 1.5)); double angle = 180.0 * (1.0 - Math.Pow((originRadius + targetRadius) / (2.0 * targetRadius), 1.5));
angle = PhaseAngle - angle; angle = PhaseAngle - angle;
return RelativeInclination < 90.0 ? AngleHelper.Clamp360(angle) : AngleHelper.Clamp360(360.0 - (180.0 - angle)); return RelativeInclination < 90.0 ? AngleHelper.Clamp360(angle) : AngleHelper.Clamp360(360.0 - (180.0 - angle));
} }
   
private Vector3d GetAscendingNode() private Vector3d GetAscendingNode()
{ {
return Vector3d.Cross(this.targetOrbit.GetOrbitNormal(), this.originOrbit.GetOrbitNormal()); return Vector3d.Cross(targetOrbit.GetOrbitNormal(), originOrbit.GetOrbitNormal());
} }
   
private Vector3d GetDescendingNode() private Vector3d GetDescendingNode()
{ {
return Vector3d.Cross(this.originOrbit.GetOrbitNormal(), this.targetOrbit.GetOrbitNormal()); return Vector3d.Cross(originOrbit.GetOrbitNormal(), targetOrbit.GetOrbitNormal());
} }
   
#endregion  
} }
} }
// //
// Kerbal Engineer Redux // Kerbal Engineer Redux
// //
// Copyright (C) 2014 CYBUTEK // Copyright (C) 2014 CYBUTEK
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
// //
   
#region Using Directives  
   
using KerbalEngineer.Flight.Sections;  
   
#endregion  
   
namespace KerbalEngineer.Flight.Readouts.Surface namespace KerbalEngineer.Flight.Readouts.Surface
{ {
  using Helpers;
  using Sections;
   
public class Longitude : ReadoutModule public class Longitude : ReadoutModule
{ {
#region Constructors  
   
public Longitude() public Longitude()
{ {
this.Name = "Longitude"; Name = "Longitude";
this.Category = ReadoutCategory.GetCategory("Surface"); Category = ReadoutCategory.GetCategory("Surface");
this.HelpString = "Shows the vessel's longitude around a celestial body. Longitude is the angle from the bodies prime meridian."; HelpString = "Shows the vessel's longitude around a celestial body. Longitude is the angle from the bodies prime meridian.";
this.IsDefault = true; IsDefault = true;
} }
   
#endregion  
   
#region Methods: public  
   
public override void Draw(SectionModule section) public override void Draw(SectionModule section)
{ {
this.DrawLine(KSPUtil.PrintLongitude(FlightGlobals.ship_longitude), section.IsHud); double angle = AngleHelper.Clamp180(FlightGlobals.ship_longitude);
  DrawLine(Units.ToAngleDMS(angle) + (angle < 0.0 ? "W" : " E"), section.IsHud);
} }
   
#endregion  
} }
} }
// //
// Kerbal Engineer Redux // Kerbal Engineer Redux
// //
// Copyright (C) 2014 CYBUTEK // Copyright (C) 2014 CYBUTEK
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
// //
   
#region Using Directives  
   
using UnityEngine;  
   
#endregion  
   
namespace KerbalEngineer.Helpers namespace KerbalEngineer.Helpers
{ {
  using UnityEngine;
   
public static class AngleHelper public static class AngleHelper
{ {
#region Methods: public public static double Clamp180(double angle)
  {
  angle = Clamp360(angle);
  if (angle > 180.0)
  {
  angle = angle - 360.0;
  }
  return angle;
  }
   
public static double Clamp360(double value) public static double Clamp360(double angle)
{ {
return ClampBetween(value, 0.0, 360.0); angle = angle % 360.0;
  if (angle < 0.0)
  {
  angle = angle + 360.0;
  }
  return angle;
} }
   
public static double ClampBetween(double value, double minimum, double maximum) public static double ClampBetween(double value, double minimum, double maximum)
{ {
while (value < minimum) while (value < minimum)
{ {
value += maximum; value += maximum;
} }
   
while (value > maximum) while (value > maximum)
{ {
value -= maximum; value -= maximum;
} }
   
return value; return value;
} }
   
public static double GetAngleBetweenVectors(Vector3d left, Vector3d right) public static double GetAngleBetweenVectors(Vector3d left, Vector3d right)
{ {
var angle = Vector3d.Angle(left, right); double angle = Vector3d.Angle(left, right);
var rotated = QuaternionD.AngleAxis(90.0, Vector3d.forward) * right; Vector3d rotated = QuaternionD.AngleAxis(90.0, Vector3d.forward) * right;
   
if (Vector3d.Angle(rotated, left) > 90.0) if (Vector3d.Angle(rotated, left) > 90.0)
{ {
return 360.0 - angle; return 360.0 - angle;
} }
return angle; return angle;
} }
   
#endregion  
} }
} }
<?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="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>..\..\..\..\..\..\Program Files (x86)\Steam\SteamApps\common\Kerbal Space Program\KSP_Data\Managed\Assembly-CSharp.dll</HintPath> <HintPath>..\Game\KSP_Data\Managed\Assembly-CSharp.dll</HintPath>
<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>..\..\..\..\..\..\Program Files (x86)\Steam\SteamApps\common\Kerbal Space Program\KSP_Data\Managed\UnityEngine.dll</HintPath> <HintPath>..\Game\KSP_Data\Managed\UnityEngine.dll</HintPath>
<Private>False</Private>  
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<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/>.
// //
   
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
{ {
private static readonly Pool<EngineSim> pool = new Pool<EngineSim>(Create, Reset); private static readonly Pool<EngineSim> pool = new Pool<EngineSim>(Create, Reset);
   
private readonly ResourceContainer resourceConsumptions = new ResourceContainer(); private readonly ResourceContainer resourceConsumptions = new ResourceContainer();
  private readonly ResourceContainer resourceFlowModes = new ResourceContainer();
   
public double actualThrust = 0; public double actualThrust = 0;
public bool isActive = false; public bool isActive = false;
public double isp = 0; public double isp = 0;
public PartSim partSim; public PartSim partSim;
public List<AppliedForce> appliedForces = new List<AppliedForce>(); public List<AppliedForce> appliedForces = new List<AppliedForce>();
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 static EngineSim Create() private static EngineSim Create()
{ {
return new EngineSim(); return new EngineSim();
} }
   
private static void Reset(EngineSim engineSim) private static void Reset(EngineSim engineSim)
{ {
engineSim.resourceConsumptions.Reset(); engineSim.resourceConsumptions.Reset();
  engineSim.resourceFlowModes.Reset();
engineSim.actualThrust = 0; engineSim.actualThrust = 0;
engineSim.isActive = false; engineSim.isActive = false;
engineSim.isp = 0; engineSim.isp = 0;
for (int i = 0; i < engineSim.appliedForces.Count; i++) for (int i = 0; i < engineSim.appliedForces.Count; i++)
{ {
engineSim.appliedForces[i].Release(); engineSim.appliedForces[i].Release();
} }
engineSim.appliedForces.Clear(); engineSim.appliedForces.Clear();
engineSim.thrust = 0; engineSim.thrust = 0;
engineSim.maxMach = 0f; engineSim.maxMach = 0f;
} }
   
public void Release() public void Release()
{ {
pool.Release(this); pool.Release(this);
} }
   
public static EngineSim New(PartSim theEngine, public static EngineSim New(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,
float IspG, float IspG,
bool throttleLocked, bool throttleLocked,
List<Propellant> propellants, List<Propellant> propellants,
bool active, bool active,
float resultingThrust, float resultingThrust,
List<Transform> thrustTransforms, List<Transform> thrustTransforms,
LogMsg log) LogMsg log)
{ {
EngineSim engineSim = pool.Borrow(); EngineSim engineSim = pool.Borrow();
   
engineSim.isp = 0.0; engineSim.isp = 0.0;
engineSim.maxMach = 0.0f; engineSim.maxMach = 0.0f;
engineSim.actualThrust = 0.0; engineSim.actualThrust = 0.0;
engineSim.partSim = theEngine; engineSim.partSim = theEngine;
engineSim.isActive = active; engineSim.isActive = active;
engineSim.thrustVec = vecThrust; engineSim.thrustVec = vecThrust;
engineSim.resourceConsumptions.Reset(); engineSim.resourceConsumptions.Reset();
  engineSim.resourceFlowModes.Reset();
engineSim.appliedForces.Clear(); engineSim.appliedForces.Clear();
   
double flowRate = 0.0; double flowRate = 0.0;
if (engineSim.partSim.hasVessel) if (engineSim.partSim.hasVessel)
{ {
  if (log != null) log.buf.AppendLine("hasVessel is true");
   
float flowModifier = GetFlowModifier(atmChangeFlow, atmCurve, engineSim.partSim.part.atmDensity, velCurve, machNumber, ref engineSim.maxMach); float flowModifier = GetFlowModifier(atmChangeFlow, atmCurve, engineSim.partSim.part.atmDensity, velCurve, machNumber, ref engineSim.maxMach);
engineSim.isp = atmosphereCurve.Evaluate((float)atmosphere); engineSim.isp = atmosphereCurve.Evaluate((float)atmosphere);
engineSim.thrust = GetThrust(Mathf.Lerp(minFuelFlow, maxFuelFlow, GetThrustPercent(thrustPercentage)) * flowModifier, engineSim.isp); engineSim.thrust = GetThrust(Mathf.Lerp(minFuelFlow, maxFuelFlow, GetThrustPercent(thrustPercentage)) * flowModifier, engineSim.isp);
engineSim.actualThrust = engineSim.isActive ? resultingThrust : 0.0; engineSim.actualThrust = engineSim.isActive ? resultingThrust : 0.0;
  if (log != null)
  {
  log.buf.AppendFormat("flowMod = {0:g6}\n", flowModifier);
  log.buf.AppendFormat("isp = {0:g6}\n", engineSim.isp);
  log.buf.AppendFormat("thrust = {0:g6}\n", engineSim.thrust);
  log.buf.AppendFormat("actual = {0:g6}\n", engineSim.actualThrust);
  }
   
if (throttleLocked) if (throttleLocked)
{ {
  if (log != null) log.buf.AppendLine("throttleLocked is true, using thrust for flowRate");
flowRate = GetFlowRate(engineSim.thrust, engineSim.isp); flowRate = GetFlowRate(engineSim.thrust, engineSim.isp);
} }
else else
{ {
if (currentThrottle > 0.0f && engineSim.partSim.isLanded == false) if (currentThrottle > 0.0f && engineSim.partSim.isLanded == false)
{ {
  if (log != null) log.buf.AppendLine("throttled up and not landed, using actualThrust for flowRate");
flowRate = GetFlowRate(engineSim.actualThrust, engineSim.isp); flowRate = GetFlowRate(engineSim.actualThrust, engineSim.isp);
} }
else else
{ {
  if (log != null) log.buf.AppendLine("throttled down or landed, using thrust for flowRate");
flowRate = GetFlowRate(engineSim.thrust, engineSim.isp); flowRate = GetFlowRate(engineSim.thrust, engineSim.isp);
} }
} }
} }
else else
{ {
  if (log != null) log.buf.AppendLine("hasVessel is false");
float flowModifier = GetFlowModifier(atmChangeFlow, atmCurve, CelestialBodies.SelectedBody.GetDensity(BuildAdvanced.Altitude), velCurve, machNumber, ref engineSim.maxMach); float flowModifier = GetFlowModifier(atmChangeFlow, atmCurve, CelestialBodies.SelectedBody.GetDensity(BuildAdvanced.Altitude), velCurve, machNumber, ref engineSim.maxMach);
engineSim.isp = atmosphereCurve.Evaluate((float)atmosphere); engineSim.isp = atmosphereCurve.Evaluate((float)atmosphere);
engineSim.thrust = GetThrust(Mathf.Lerp(minFuelFlow, maxFuelFlow, GetThrustPercent(thrustPercentage)) * flowModifier, engineSim.isp); engineSim.thrust = GetThrust(Mathf.Lerp(minFuelFlow, maxFuelFlow, GetThrustPercent(thrustPercentage)) * flowModifier, engineSim.isp);
  engineSim.actualThrust = 0d;
  if (log != null)
  {
  log.buf.AppendFormat("flowMod = {0:g6}\n", flowModifier);
  log.buf.AppendFormat("isp = {0:g6}\n", engineSim.isp);
  log.buf.AppendFormat("thrust = {0:g6}\n", engineSim.thrust);
  log.buf.AppendFormat("actual = {0:g6}\n", engineSim.actualThrust);
  }
   
  if (log != null) log.buf.AppendLine("no vessel, using thrust for flowRate");
flowRate = GetFlowRate(engineSim.thrust, engineSim.isp); flowRate = GetFlowRate(engineSim.thrust, engineSim.isp);
} }
   
if (log != null) log.buf.AppendFormat("flowRate = {0:g6}\n", flowRate); if (log != null) log.buf.AppendFormat("flowRate = {0:g6}\n", flowRate);
   
engineSim.thrust = flowRate * (engineSim.isp * IspG);  
// I did not look into the diff between those 2 so I made them equal...  
engineSim.actualThrust = engineSim.thrust;  
   
float flowMass = 0f; float flowMass = 0f;
for (int i = 0; i < propellants.Count; ++i) for (int i = 0; i < propellants.Count; ++i)
{ {
Propellant propellant = propellants[i]; Propellant propellant = propellants[i];
flowMass += propellant.ratio * ResourceContainer.GetResourceDensity(propellant.id); if (!propellant.ignoreForIsp)
  flowMass += propellant.ratio * ResourceContainer.GetResourceDensity(propellant.id);
} }
   
if (log != null) log.buf.AppendFormat("flowMass = {0:g6}\n", flowMass); if (log != null) log.buf.AppendFormat("flowMass = {0:g6}\n", flowMass);
   
for (int i = 0; i < propellants.Count; ++i) for (int i = 0; i < propellants.Count; ++i)
{ {
Propellant propellant = propellants[i]; Propellant propellant = propellants[i];
   
if (propellant.name == "ElectricCharge" || propellant.name == "IntakeAir") if (propellant.name == "ElectricCharge" || propellant.name == "IntakeAir")
{ {
continue; continue;
} }
   
double consumptionRate = propellant.ratio * flowRate / flowMass; double consumptionRate = propellant.ratio * flowRate / flowMass;
if (log != null) log.buf.AppendFormat( if (log != null) log.buf.AppendFormat(
"Add consumption({0}, {1}:{2:d}) = {3:g6}\n", "Add consumption({0}, {1}:{2:d}) = {3:g6}\n",
ResourceContainer.GetResourceName(propellant.id), ResourceContainer.GetResourceName(propellant.id),
theEngine.name, theEngine.name,
theEngine.partId, theEngine.partId,
consumptionRate); consumptionRate);
engineSim.resourceConsumptions.Add(propellant.id, consumptionRate); engineSim.resourceConsumptions.Add(propellant.id, consumptionRate);
  engineSim.resourceFlowModes.Add(propellant.id, (double)propellant.GetFlowMode());
} }
   
double thrustPerThrustTransform = engineSim.thrust / thrustTransforms.Count; double thrustPerThrustTransform = engineSim.thrust / thrustTransforms.Count;
for (int i = 0; i < thrustTransforms.Count; i++) for (int i = 0; i < thrustTransforms.Count; i++)
{ {
Transform thrustTransform = thrustTransforms[i]; Transform thrustTransform = thrustTransforms[i];
Vector3d direction = thrustTransform.forward.normalized; Vector3d direction = thrustTransform.forward.normalized;
Vector3d position = thrustTransform.position; Vector3d position = thrustTransform.position;
   
AppliedForce appliedForce = AppliedForce.New(direction * thrustPerThrustTransform, position); AppliedForce appliedForce = AppliedForce.New(direction * thrustPerThrustTransform, position);
engineSim.appliedForces.Add(appliedForce); engineSim.appliedForces.Add(appliedForce);
} }
   
return engineSim; return engineSim;
} }
   
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);
} }
   
// 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>>();
   
Dictionary<int, HashSet<PartSim>> stagePartSets = new Dictionary<int, HashSet<PartSim>>(); Dictionary<int, HashSet<PartSim>> stagePartSets = new Dictionary<int, HashSet<PartSim>>();
   
HashSet<PartSim> visited = new HashSet<PartSim>(); HashSet<PartSim> visited = new HashSet<PartSim>();
   
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;
foreach (HashSet<PartSim> sourcePartSet in sourcePartSets.Values) foreach (HashSet<PartSim> sourcePartSet in sourcePartSets.Values)
{ {
sourcePartSet.Clear(); sourcePartSet.Clear();
} }
   
for (int index = 0; index < this.resourceConsumptions.Types.Count; index++) for (int index = 0; index < this.resourceConsumptions.Types.Count; index++)
{ {
int type = this.resourceConsumptions.Types[index]; int type = this.resourceConsumptions.Types[index];
   
HashSet<PartSim> sourcePartSet; HashSet<PartSim> sourcePartSet;
if (!sourcePartSets.TryGetValue(type, out sourcePartSet)) if (!sourcePartSets.TryGetValue(type, out sourcePartSet))
{ {
sourcePartSet = new HashSet<PartSim>(); sourcePartSet = new HashSet<PartSim>();
sourcePartSets.Add(type, sourcePartSet); sourcePartSets.Add(type, sourcePartSet);
} }
   
switch (ResourceContainer.GetResourceFlowMode(type)) switch ((ResourceFlowMode)this.resourceFlowModes[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:
for (int i = 0; i < allParts.Count; i++) for (int i = 0; i < allParts.Count; i++)
{ {
PartSim aPartSim = allParts[i]; PartSim aPartSim = allParts[i];
if (aPartSim.resources[type] > SimManager.RESOURCE_MIN && aPartSim.resourceFlowStates[type] != 0) if (aPartSim.resources[type] > SimManager.RESOURCE_MIN && aPartSim.resourceFlowStates[type] != 0)
{ {
sourcePartSet.Add(aPartSim); sourcePartSet.Add(aPartSim);
} }
} }
break; break;
   
case ResourceFlowMode.STAGE_PRIORITY_FLOW: case ResourceFlowMode.STAGE_PRIORITY_FLOW:
   
foreach (HashSet<PartSim> stagePartSet in stagePartSets.Values) foreach (HashSet<PartSim> stagePartSet in stagePartSets.Values)
{ {
stagePartSet.Clear(); stagePartSet.Clear();
} }
var maxStage = -1; var maxStage = -1;
   
//Logger.Log(type); //Logger.Log(type);
for (int i = 0; i < allParts.Count; i++) for (int i = 0; i < allParts.Count; i++)
{ {
var aPartSim = allParts[i]; var aPartSim = allParts[i];
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 j = 0; j <= maxStage; j++) for (int j = 0; j <= maxStage; j++)
{ {
HashSet<PartSim> stagePartSet; HashSet<PartSim> stagePartSet;
if (stagePartSets.TryGetValue(j, 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:
visited.Clear(); visited.Clear();
   
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);
} }
partSim.GetSourceSet(type, allParts, visited, sourcePartSet, log, ""); partSim.GetSourceSet(type, allParts, visited, sourcePartSet, 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.Count > 0) if (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
for (int i = 0; i < this.resourceConsumptions.Types.Count; i++) for (int i = 0; i < this.resourceConsumptions.Types.Count; i++)
{ {
int type = this.resourceConsumptions.Types[i]; int type = this.resourceConsumptions.Types[i];
HashSet<PartSim> sourcePartSet; HashSet<PartSim> sourcePartSet;
if (!sourcePartSets.TryGetValue(type, out sourcePartSet) || sourcePartSet.Count == 0) if (!sourcePartSets.TryGetValue(type, out sourcePartSet) || sourcePartSet.Count == 0)
{ {
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
for (int i = 0; i < this.resourceConsumptions.Types.Count; i++) for (int i = 0; i < this.resourceConsumptions.Types.Count; i++)
{ {
int type = this.resourceConsumptions.Types[i]; int type = this.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( MonoBehaviour.print(
"Adding drain of " + amount + " " + ResourceContainer.GetResourceName(type) + " to " + partSim.name + ":" + "Adding drain of " + amount + " " + ResourceContainer.GetResourceName(type) + " to " + partSim.name + ":" +
partSim.partId); 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.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; using Helpers;
   
public class Simulation public class Simulation
{ {
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 double atmosphere; private double atmosphere;
private int currentStage; private int currentStage;
private double currentisp; private double currentisp;
private HashSet<PartSim> decoupledParts = new HashSet<PartSim>(); private HashSet<PartSim> decoupledParts = new HashSet<PartSim>();
private bool doingCurrent; private bool doingCurrent;
private List<PartSim> dontStageParts; private List<PartSim> dontStageParts;
private List<List<PartSim>> dontStagePartsLists = new List<List<PartSim>>(); private List<List<PartSim>> dontStagePartsLists = new List<List<PartSim>>();
private HashSet<PartSim> drainingParts; private HashSet<PartSim> drainingParts;
private HashSet<int> drainingResources; private HashSet<int> drainingResources;
private double gravity; private double gravity;
private Dictionary<Part, PartSim> partSimLookup; private Dictionary<Part, PartSim> partSimLookup;
   
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 = new ForceAccumulator(); 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(); private WeightedVectorAverager vectorAverager = new WeightedVectorAverager();
private static ModuleProceduralFairing moduleProceduralFairing; private static ModuleProceduralFairing moduleProceduralFairing;
   
public Simulation() public Simulation()
{ {
this.allParts = new List<PartSim>(); this.allParts = new List<PartSim>();
this.allFuelLines = new List<PartSim>(); this.allFuelLines = new List<PartSim>();
this.drainingParts = new HashSet<PartSim>(); this.drainingParts = new HashSet<PartSim>();
this.allEngines = new List<EngineSim>(); this.allEngines = new List<EngineSim>();
this.activeEngines = new List<EngineSim>(); this.activeEngines = new List<EngineSim>();
this.drainingResources = new HashSet<int>(); this.drainingResources = new HashSet<int>();
this.totalStageThrustForce = new ForceAccumulator(); this.totalStageThrustForce = new ForceAccumulator();
   
// 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 = new Dictionary<Part, PartSim>(); partSimLookup = new Dictionary<Part, PartSim>();
   
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;
   
for (int i = 0; i < allParts.Count; ++i) { for (int i = 0; i < allParts.Count; ++i) {
mass += allParts[i].GetMass(currentStage); mass += allParts[i].GetMass(currentStage);
} }
   
return mass; return mass;
} }
} }
   
private Vector3d ShipCom private Vector3d ShipCom
{ {
get get
{ {
vectorAverager.Reset(); vectorAverager.Reset();
   
for (int i = 0; i < allParts.Count; ++i) for (int i = 0; i < allParts.Count; ++i)
{ {
PartSim partSim = allParts[i]; PartSim partSim = allParts[i];
vectorAverager.Add(partSim.centerOfMass, partSim.GetMass(currentStage, true)); vectorAverager.Add(partSim.centerOfMass, partSim.GetMass(currentStage, true));
} }
   
return vectorAverager.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.Reset();
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;
this.maxMach = 1.0f; this.maxMach = 1.0f;
//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();
   
// 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;
} }
//MonoBehaviour.print("PrepareSimulation pool size = " + PartSim.pool.Count()); //MonoBehaviour.print("PrepareSimulation pool size = " + PartSim.pool.Count());
// 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;
for (int i = 0; i < partList.Count; ++i) for (int i = 0; i < partList.Count; ++i)
{ {
Part part = partList[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 = PartSim.New(part, partId, this.atmosphere, log); PartSim partSim = PartSim.New(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
for (int i = 0; i < this.allParts.Count; i++) for (int i = 0; i < this.allParts.Count; i++)
{ {
PartSim partSim = this.allParts[i]; PartSim partSim = this.allParts[i];
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)
{ {
for (int i = 0; i < allFuelLines.Count; ++i) for (int i = 0; i < allFuelLines.Count; ++i)
{ {
PartSim partSim = allFuelLines[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");
for (int i = 0; i < allParts.Count; ++i) for (int i = 0; i < allParts.Count; ++i)
{ {
PartSim partSim = allParts[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");
for (int i = 0; i < allParts.Count; ++i) for (int i = 0; i < allParts.Count; ++i)
{ {
allParts[i].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.Reset();
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;
for (int i = 0; i < allEngines.Count; ++i) for (int i = 0; i < allEngines.Count; ++i)
{ {
EngineSim engine = allEngines[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 the masses of the parts to correctly handle "no physics" parts // Update the masses of the parts to correctly handle "no physics" parts
this.UpdatePartMasses(); this.UpdatePartMasses();
   
if (log != null) if (log != null)
this.allParts[0].DumpPartToBuffer(log.buf, "", this.allParts); this.allParts[0].DumpPartToBuffer(log.buf, "", this.allParts);
   
// 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;
if (log != null) log.buf.AppendLine("stage.thrust = " + stage.thrust); if (log != null) log.buf.AppendLine("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;
if (log != null) log.buf.AppendLine("StageMass = " + stageStartMass); if (log != null) log.buf.AppendLine("StageMass = " + stageStartMass);
if (log != null) log.buf.AppendLine("Initial maxTWR = " + stage.maxThrustToWeight); if (log != null) log.buf.AppendLine("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
for (int i = 0; i < allParts.Count; ++i) for (int i = 0; i < allParts.Count; ++i)
{ {
PartSim partSim = allParts[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();
} }
   
if (partSim.hasVessel == false && partSim.isFairing && partSim.inverseStage == currentStage) if (partSim.hasVessel == false && partSim.isFairing && partSim.inverseStage == currentStage)
{ {
stage.mass += partSim.moduleMass; stage.mass += partSim.moduleMass;
} }
} }
   
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:");
for (int i = 0; i < this.dontStageParts.Count; i++) for (int i = 0; i < this.dontStageParts.Count; i++)
{ {
PartSim partSim = this.dontStageParts[i]; PartSim partSim = this.dontStageParts[i];
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 * Units.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 / (Units.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");
} }
FreePooledObject(); FreePooledObject();
return stages; return stages;
} }
   
public void UpdatePartMasses() public void UpdatePartMasses()
{ {
for (int i = 0; i < this.allParts.Count; i++) for (int i = 0; i < this.allParts.Count; i++)
{ {
this.allParts[i].baseMass = this.allParts[i].realMass; this.allParts[i].baseMass = this.allParts[i].realMass;
this.allParts[i].baseMassForCoM = this.allParts[i].realMass; this.allParts[i].baseMassForCoM = this.allParts[i].realMass;
} }
   
for (int i = 0; i < this.allParts.Count; i++) for (int i = 0; i < this.allParts.Count; i++)
{ {
PartSim part = this.allParts[i]; PartSim part = this.allParts[i];
// If the part has a parent // If the part has a parent
if (part.parent != null) if (part.parent != null)
{ {
if (part.isNoPhysics) if (part.isNoPhysics)
{ {
if (part.parent.isNoPhysics && part.parent.parent != null) if (part.parent.isNoPhysics && part.parent.parent != null)
{ {
part.baseMass = 0d; part.baseMass = 0d;
part.baseMassForCoM = 0d; part.baseMassForCoM = 0d;
} }
else else
{ {
part.parent.baseMassForCoM += part.baseMassForCoM; part.parent.baseMassForCoM += part.baseMassForCoM;
part.baseMassForCoM = 0d; part.baseMassForCoM = 0d;
} }
} }
} }
} }
   
for (int i = 0; i < this.allParts.Count; i++) for (int i = 0; i < this.allParts.Count; i++)
this.allParts[i].startMass = this.allParts[i].GetMass(currentStage); this.allParts[i].startMass = this.allParts[i].GetMass(currentStage);
} }
   
// Make sure we free them all, even if they should all be free already at this point // Make sure we free them all, even if they should all be free already at this point
public void FreePooledObject() public void FreePooledObject()
{ {
//MonoBehaviour.print("FreePooledObject pool size before = " + PartSim.pool.Count() + " for " + allParts.Count + " parts"); //MonoBehaviour.print("FreePooledObject pool size before = " + PartSim.pool.Count() + " for " + allParts.Count + " parts");
foreach (PartSim part in allParts) foreach (PartSim part in allParts)
{ {
part.Release(); part.Release();
} }
//MonoBehaviour.print("FreePooledObject pool size after = " + PartSim.pool.Count()); //MonoBehaviour.print("FreePooledObject pool size after = " + PartSim.pool.Count());
   
//MonoBehaviour.print("FreePooledObject pool size before = " + EngineSim.pool.Count() + " for " + allEngines.Count + " engines"); //MonoBehaviour.print("FreePooledObject pool size before = " + EngineSim.pool.Count() + " for " + allEngines.Count + " engines");
foreach (EngineSim engine in allEngines) foreach (EngineSim engine in allEngines)
{ {
engine.Release(); engine.Release();
} }
//MonoBehaviour.print("FreePooledObject pool size after = " + EngineSim.pool.Count()); //MonoBehaviour.print("FreePooledObject pool size after = " + EngineSim.pool.Count());
} }
   
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>());
} }
} }
   
for (int i = 0; i < allParts.Count; ++i) for (int i = 0; i < allParts.Count; ++i)
{ {
PartSim partSim = allParts[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( log.buf.AppendLine(
partSim.name + ":" + partSim.partId + " is engine or tank, decoupled = " + partSim.decoupledInStage); 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();
for (int i = 0; i < allEngines.Count; ++i) for (int i = 0; i < allEngines.Count; ++i)
{ {
EngineSim engine = allEngines[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.Reset(); 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
for (int i = 0; i < activeEngines.Count; ++i) for (int i = 0; i < activeEngines.Count; ++i)
{ {
EngineSim engine = activeEngines[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;
   
for (int j = 0; j < engine.appliedForces.Count; ++j) for (int j = 0; j < engine.appliedForces.Count; ++j)
{ {
this.totalStageThrustForce.AddForce(engine.appliedForces[j]); 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
for (int i = 0; i < activeEngines.Count; ++i) for (int i = 0; i < activeEngines.Count; ++i)
{ {
EngineSim engine = activeEngines[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
for (int j = 0; j < engine.ResourceConsumptions.Types.Count; ++j) for (int j = 0; j < engine.ResourceConsumptions.Types.Count; ++j)
{ {
drainingResources.Add(engine.ResourceConsumptions.Types[j]); 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;
for (int j = 0; j < this.activeEngines.Count; j++) for (int j = 0; j < this.activeEngines.Count; j++)
{ {
EngineSim engine = this.activeEngines[j]; EngineSim engine = this.activeEngines[j];
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)
{ {
for (int i = 0; i < dontStageParts.Count; ++i) for (int i = 0; i < dontStageParts.Count; ++i)
{ {
PartSim partSim = dontStageParts[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)
{ {
for (int j = 0; j < activeEngines.Count; ++j) for (int j = 0; j < activeEngines.Count; ++j)
{ {
EngineSim engine = activeEngines[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();
for (int i = 0; i < allParts.Count; ++i) for (int i = 0; i < allParts.Count; ++i)
{ {
PartSim partSim = allParts[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);
partSim.Release(); partSim.Release();
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--)
{ {
EngineSim engine = this.allEngines[i]; EngineSim engine = this.allEngines[i];
if (engine.partSim == partSim) if (engine.partSim == partSim)
{ {
this.allEngines.RemoveAt(i); this.allEngines.RemoveAt(i);
engine.Release(); engine.Release();
} }
} }
} }
// 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
for (int i = 0; i < allParts.Count; ++i) 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
allParts[i].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
for (int i = 0; i < allEngines.Count; ++i) for (int i = 0; i < allEngines.Count; ++i)
{ {
EngineSim engine = allEngines[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