Compatibility with multi-mode engines.
Compatibility with multi-mode engines.

--- a/KerbalEngineer/BuildEngineer/BuildAdvanced.cs
+++ b/KerbalEngineer/BuildEngineer/BuildAdvanced.cs
@@ -192,17 +192,16 @@
         // Checks whether the editor should be looked to stop click-through.
         private void CheckEditorLock()
         {
-            if (_windowPosition.MouseIsOver() && !EditorLogic.editorLocked)
-            {
-                EditorLogic.fetch.Lock(true, true, true);
+            if (_windowPosition.MouseIsOver() && !_isEditorLocked) // Lock editor
+            {
+                EditorLogic.fetch.Lock(true, true, true, _windowID.ToString());
                 _isEditorLocked = true;
             }
-            else if (_isEditorLocked && !_windowPosition.MouseIsOver() && EditorLogic.editorLocked)
-            {
-                EditorLogic.fetch.Unlock();
-            }
-
-            if (!EditorLogic.editorLocked) _isEditorLocked = false;
+            else if (!_windowPosition.MouseIsOver() && _isEditorLocked) // Unlock editor
+            {
+                EditorLogic.fetch.Unlock(_windowID.ToString());
+                _isEditorLocked = false;
+            }
         }
 
         private void Window(int windowID)

--- a/KerbalEngineer/EngineerGlobals.cs
+++ b/KerbalEngineer/EngineerGlobals.cs
@@ -2,8 +2,10 @@
 // Author:  CYBUTEK
 // License: Attribution-NonCommercial-ShareAlike 3.0 Unported
 
+using System.Diagnostics;
 using System.IO;
 using System.Reflection;
+using UnityEngine;
 
 namespace KerbalEngineer
 {

--- a/KerbalEngineer/Extensions/PartExtensions.cs
+++ b/KerbalEngineer/Extensions/PartExtensions.cs
@@ -36,6 +36,18 @@
         }
 
         /// <summary>
+        /// Gets the first typed PartModule in the part's module list.
+        /// </summary>
+        public static T GetModule<T>(this Part part) where T : PartModule
+        {
+            foreach (PartModule module in part.Modules)
+                if (module is T)
+                    return module as T;
+
+            return null;
+        }
+
+        /// <summary>
         /// Gets a typed PartModule.
         /// </summary>
         public static T GetModule<T>(this Part part, string className) where T : PartModule
@@ -56,7 +68,23 @@
         /// </summary>
         public static ModuleEngines GetModuleEngines(this Part part)
         {
-            return part.GetModule<ModuleEngines>("ModuleEngines");
+            return part.GetModule<ModuleEngines>();
+        }
+
+        /// <summary>
+        /// Gets the current selected ModuleEnginesFX.
+        /// </summary>
+        public static ModuleEnginesFX GetModuleEnginesFX(this Part part)
+        {
+            string mode = part.GetModule<MultiModeEngine>().mode;
+
+            foreach (ModuleEnginesFX engine in part.Modules.OfType<ModuleEnginesFX>())
+            {
+                if (engine.engineID == mode)
+                    return engine;
+            }
+
+            return null;
         }
 
         /// <summary>
@@ -64,7 +92,7 @@
         /// </summary>
         public static ModuleGimbal GetModuleGimbal(this Part part)
         {
-            return part.GetModule<ModuleGimbal>("ModuleGimbal");
+            return part.GetModule<ModuleGimbal>();
         }
 
         /// <summary>
@@ -72,7 +100,7 @@
         /// </summary>
         public static ModuleDeployableSolarPanel GetModuleDeployableSolarPanel(this Part part)
         {
-            return part.GetModule<ModuleDeployableSolarPanel>("ModuleDeployableSolarPanel");
+            return part.GetModule<ModuleDeployableSolarPanel>();
         }
 
         /// <summary>
@@ -80,7 +108,7 @@
         /// </summary>
         public static ModuleAlternator GetModuleAlternator(this Part part)
         {
-            return part.GetModule<ModuleAlternator>("ModuleAlternator");
+            return part.GetModule<ModuleAlternator>();
         }
 
         /// <summary>
@@ -88,7 +116,7 @@
         /// </summary>
         public static ModuleGenerator GetModuleGenerator(this Part part)
         {
-            return part.GetModule<ModuleGenerator>("ModuleGenerator");
+            return part.GetModule<ModuleGenerator>();
         }
 
         /// <summary>
@@ -96,7 +124,7 @@
         /// </summary>
         public static ModuleParachute GetModuleParachute(this Part part)
         {
-            return part.GetModule<ModuleParachute>("ModuleParachute");
+            return part.GetModule<ModuleParachute>();
         }
 
         /// <summary>
@@ -120,12 +148,33 @@
         /// </summary>
         public static double GetMaxThrust(this Part part)
         {
-            return (part.IsEngine()) ? part.GetModuleEngines().maxThrust : 0d;
-        }
-
+            if (part.HasModule<ModuleEngines>())
+            {
+                return part.GetModuleEngines().maxThrust;
+            }
+            else if (part.HasModule<MultiModeEngine>())
+            {
+                return part.GetModuleEnginesFX().maxThrust;
+            }
+
+            return 0d;
+        }
+
+        /// <summary>
+        /// Gets the current specific impulse for the engine.
+        /// </summary>
         public static double GetSpecificImpulse(this Part part, float atmosphere)
         {
-            return (part.IsEngine()) ? part.GetModuleEngines().atmosphereCurve.Evaluate(atmosphere) : 0d;
+            if (part.HasModule<ModuleEngines>())
+            {
+                return part.GetModuleEngines().atmosphereCurve.Evaluate(atmosphere);
+            }
+            else if (part.HasModule<MultiModeEngine>())
+            {
+                return part.GetModuleEnginesFX().atmosphereCurve.Evaluate(atmosphere);
+            }
+
+            return 0d;
         }
 
         /// <summary>
@@ -133,7 +182,16 @@
         /// </summary>
         public static bool EngineHasFuel(this Part part)
         {
-            return part.IsEngine() && !part.GetModuleEngines().getFlameoutState;
+            if (part.HasModule<ModuleEngines>())
+            {
+                return part.GetModuleEngines().getFlameoutState;
+            }
+            else if (part.HasModule<MultiModeEngine>())
+            {
+                return part.GetModuleEnginesFX().getFlameoutState;
+            }
+
+            return false;
         }
 
         /// <summary>
@@ -154,7 +212,7 @@
         /// </summary>
         public static bool IsDecoupler(this Part part)
         {
-            return part.HasModule("ModuleDecouple") || part.HasModule("ModuleAnchoredDecoupler");
+            return part.HasModule<ModuleDecouple>() || part.HasModule<ModuleAnchoredDecoupler>();
         }
 
         /// <summary>
@@ -172,7 +230,7 @@
         /// </summary>
         public static bool IsLaunchClamp(this Part part)
         {
-            return part.HasModule("LaunchClamp");
+            return part.HasModule<LaunchClamp>();
         }
 
         /// <summary>
@@ -180,7 +238,7 @@
         /// </summary>
         public static bool IsEngine(this Part part)
         {
-            return part.HasModule("ModuleEngines");
+            return part.HasModule<ModuleEngines>() || part.HasModule<MultiModeEngine>();
         }
 
         /// <summary>
@@ -188,7 +246,7 @@
         /// </summary>
         public static bool IsSolarPanel(this Part part)
         {
-            return part.HasModule("ModuleDeployableSolarPanel");
+            return part.HasModule<ModuleDeployableSolarPanel>();
         }
 
         /// <summary>
@@ -196,7 +254,7 @@
         /// </summary>
         public static bool IsGenerator(this Part part)
         {
-            return part.HasModule("ModuleGenerator");
+            return part.HasModule<ModuleGenerator>();
         }
 
         /// <summary>
@@ -204,7 +262,7 @@
         /// </summary>
         public static bool IsCommandModule(this Part part)
         {
-            return part.HasModule("ModuleCommand");
+            return part.HasModule<ModuleCommand>();
         }
 
         /// <summary>
@@ -212,7 +270,7 @@
         /// </summary>
         public static bool IsParachute(this Part part)
         {
-            return part.HasModule("ModuleParachute");
+            return part.HasModule<ModuleParachute>();
         }
 
         /// <summary>
@@ -220,7 +278,7 @@
         /// </summary>
         public static bool IsSolidRocket(this Part part)
         {
-            return part.IsEngine() && part.GetModuleEngines().throttleLocked;
+            return part.HasModule<ModuleEngines>() && part.GetModuleEngines().throttleLocked;
         }
 
         /// <summary>
@@ -267,9 +325,9 @@
         /// </summary>
         public static bool HasOneShotAnimation(this Part part)
         {
-            if (part.HasModule("ModuleAnimateGeneric"))
-            {
-                return part.GetModule<ModuleAnimateGeneric>("ModuleAnimateGeneric").isOneShot;
+            if (part.HasModule<ModuleAnimateGeneric>())
+            {
+                return part.GetModule<ModuleAnimateGeneric>().isOneShot;
             }
 
             return false;

--- a/KerbalEngineer/KerbalEngineer.csproj
+++ b/KerbalEngineer/KerbalEngineer.csproj
@@ -13,10 +13,10 @@
     <FileAlignment>512</FileAlignment>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
-    <DebugSymbols>true</DebugSymbols>
-    <DebugType>full</DebugType>
+    <DebugSymbols>false</DebugSymbols>
+    <DebugType>none</DebugType>
     <Optimize>false</Optimize>
-    <OutputPath>bin\Debug\</OutputPath>
+    <OutputPath>..\Output\KerbalEngineer\</OutputPath>
     <DefineConstants>DEBUG;TRACE</DefineConstants>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
@@ -25,7 +25,8 @@
     <DebugType>none</DebugType>
     <Optimize>true</Optimize>
     <OutputPath>..\Output\KerbalEngineer\</OutputPath>
-    <DefineConstants>TRACE</DefineConstants>
+    <DefineConstants>
+    </DefineConstants>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
     <UseVSHostingProcess>false</UseVSHostingProcess>
@@ -105,7 +106,9 @@
     <Compile Include="Simulation\Stage.cs" />
     <Compile Include="TapeDriveAnimator.cs" />
   </ItemGroup>
-  <ItemGroup />
+  <ItemGroup>
+    <Folder Include="FlightEngineer\Misc\" />
+  </ItemGroup>
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
   <PropertyGroup>
     <PostBuildEvent>xcopy "$(SolutionDir)Output\*" "$(SolutionDir)Game\GameData\*" /D /E /C /R /I /K /Y</PostBuildEvent>

--- a/KerbalEngineer/Simulation/PartSim.cs
+++ b/KerbalEngineer/Simulation/PartSim.cs
@@ -11,6 +11,7 @@
 using System.Collections.Generic;
 using System.Diagnostics;
 using System.Linq;
+using KerbalEngineer.Extensions;
 using UnityEngine;
 
 namespace KerbalEngineer.Simulation
@@ -39,21 +40,37 @@
             }
 
             foreach (ModuleEngines engine in part.Modules.OfType<ModuleEngines>())
-            {
-                if (part.vessel != null)
-                {
-                    actualThrust += engine.requestedThrust;
-                    isp = engine.atmosphereCurve.Evaluate((float)part.staticPressureAtm);
-                }
-                else
-                {
-                    isp = engine.atmosphereCurve.Evaluate((float)atmosphere);
-                }
-
-                thrust += engine.maxThrust;
+                SetEngineInfo(engine.requestedThrust, engine.maxThrust, engine.thrustPercentage, engine.atmosphereCurve, atmosphere);
+
+            if (part.HasModule<MultiModeEngine>())
+            {
+                string mode = part.GetModule<MultiModeEngine>().mode;
+
+                foreach (ModuleEnginesFX engine in part.Modules.OfType<ModuleEnginesFX>())
+                {
+                    if (engine.engineID == mode)
+                        SetEngineInfo(engine.requestedThrust, engine.maxThrust, engine.thrustPercentage, engine.atmosphereCurve, atmosphere);
+                }
             }
 
             decoupledInStage = DecoupledInStage();
+        }
+
+        public void SetEngineInfo(float requestedThrust, float maxThrust, float thrustPercentage, FloatCurve atmosphereCurve, double atmosphere)
+        {
+            float thrustLimiter = (thrustPercentage > 0) ? thrustPercentage / 100f : 0f;
+
+            if (part.vessel != null)
+            {
+                actualThrust += requestedThrust * thrustLimiter;
+                isp = atmosphereCurve.Evaluate((float)part.staticPressureAtm);
+            }
+            else
+            {
+                isp = atmosphereCurve.Evaluate((float)atmosphere);
+            }
+
+            thrust += maxThrust * thrustLimiter;
         }
 
         public bool CanDrawNeededResources(List<PartSim> partSims)
@@ -97,54 +114,79 @@
         {
             foreach (ModuleEngines engine in part.Modules.OfType<ModuleEngines>())
             {
-                double flowRate = 0d;
-                if (part.vessel != null)
-                {
-                    if (engine.throttleLocked)
-                    {
-                        flowRate = engine.maxThrust / (isp * 9.81d);
+                float thrustLimiter = (engine.thrustPercentage > 0) ? engine.thrustPercentage / 100f : 0f;
+                double flowRate = GetPropellantFlowRate(engine.requestedThrust * thrustLimiter, engine.maxThrust * thrustLimiter, engine.throttleLocked);
+                double flowMass = GetPropellantFlowMass(engine.propellants);
+                SetPropellantConsumptionRate(engine.propellants, flowRate, flowMass);
+            }
+
+            if (part.HasModule<MultiModeEngine>())
+            {
+                string mode = part.GetModule<MultiModeEngine>().mode;
+
+                foreach (ModuleEnginesFX engine in part.Modules.OfType<ModuleEnginesFX>())
+                {
+                    if (engine.engineID == mode)
+                    {
+                        float thrustLimiter = (engine.thrustPercentage > 0) ? engine.thrustPercentage / 100f : 0f;
+                        double flowRate = GetPropellantFlowRate(engine.requestedThrust * thrustLimiter, engine.maxThrust * thrustLimiter, engine.throttleLocked);
+                        double flowMass = GetPropellantFlowMass(engine.propellants);
+                        SetPropellantConsumptionRate(engine.propellants, flowRate, flowMass);
+                    }
+                }
+            }
+        }
+
+        public double GetPropellantFlowRate(float requestedThrust, float maxThrust, bool throttleLocked)
+        {
+            if (part.vessel != null)
+            {
+                if (throttleLocked)
+                {
+                    return maxThrust / (isp * Simulation.STD_GRAVITY);
+                }
+                else
+                {
+                    if (part.vessel.Landed)
+                    {
+                        return Math.Max(0.000001d, maxThrust * FlightInputHandler.state.mainThrottle) / (isp * Simulation.STD_GRAVITY);
                     }
                     else
                     {
-                        if (part.vessel.Landed)
-                        {
-                            flowRate = Math.Max(0.000001d, engine.maxThrust * FlightInputHandler.state.mainThrottle) / (isp * 9.81d);
-                        }
+                        if (requestedThrust > 0)
+                            return requestedThrust / (isp * Simulation.STD_GRAVITY);
                         else
-                        {
-                            if (engine.requestedThrust > 0)
-                            {
-                                flowRate = engine.requestedThrust / (isp * 9.81d);
-                            }
-                            else
-                            {
-                                flowRate = engine.maxThrust / (isp * 9.81d);
-                            }
-                        }
-                    }
-                }
-                else
-                {
-                    flowRate = engine.maxThrust / (isp * 9.81d);
-                }
-
-                float flowMass = 0f;
-
-                foreach (ModuleEngines.Propellant propellant in engine.propellants)
-                {
-                    flowMass += propellant.ratio * ResourceContainer.GetResourceDensity(propellant.id);
-                }
-
-                foreach (ModuleEngines.Propellant propellant in engine.propellants)
-                {
-                    if (propellant.name == "ElectricCharge" || propellant.name == "IntakeAir")
-                    {
-                        continue;
-                    }
-
-                    double consumptionRate = propellant.ratio * flowRate / flowMass;
-                    resourceConsumptions.Add(propellant.id, consumptionRate);
-                }
+                            return maxThrust / (isp * Simulation.STD_GRAVITY);
+                    }
+                }
+            }
+            else
+            {
+                return maxThrust / (isp * Simulation.STD_GRAVITY);
+            }
+        }
+
+        public double GetPropellantFlowMass(List<Propellant> propellants)
+        {
+            double flowMass = 0d;
+            foreach (Propellant propellant in propellants)
+            {
+                flowMass += propellant.ratio * ResourceContainer.GetResourceDensity(propellant.id);
+            }
+            return flowMass;
+        }
+
+        public void SetPropellantConsumptionRate(List<Propellant> propellants, double flowRate, double flowMass)
+        {
+            foreach (Propellant propellant in propellants)
+            {
+                if (propellant.name == "ElectricCharge" || propellant.name == "IntakeAir")
+                {
+                    continue;
+                }
+
+                double consumptionRate = propellant.ratio * flowRate / flowMass;
+                resourceConsumptions.Add(propellant.id, consumptionRate);
             }
         }
 
@@ -262,26 +304,6 @@
             return part is Decoupler || part is RadialDecoupler || part.Modules.OfType<ModuleDecouple>().Count() > 0 || part.Modules.OfType<ModuleAnchoredDecoupler>().Count() > 0;
         }
 
-        public bool IsDockingNode(Part part = null)
-        {
-            if (part == null)
-            {
-                part = this.part;
-            }
-
-            return part.Modules.OfType<ModuleDockingNode>().Count() > 0;
-        }
-
-        public bool IsStrutFuelLine(Part part = null)
-        {
-            if (part == null)
-            {
-                part = this.part;
-            }
-
-            return (part is StrutConnector || part is FuelLine) ? true : false;
-        }
-
         private void SetResourceDrainRateAllVessel(int type, double amount, List<PartSim> partSims)
         {
             PartSim source = null;
@@ -484,52 +506,16 @@
             }
         }
 
-        public bool IsEngine
+        public bool IsSolidMotor
         {
             get
             {
-                return thrust > 0;
-            }
-        }
-
-        public bool IsSolidMotor
-        {
-            get
-            {
                 foreach (ModuleEngines engine in part.Modules.OfType<ModuleEngines>())
                 {
                     if (engine.throttleLocked)
                     {
                         return true;
                     }
-                }
-
-                return false;
-            }
-        }
-
-        public bool IsSepratron
-        {
-            get
-            {
-                if (!part.ActivatesEvenIfDisconnected)
-                {
-                    return false;
-                }
-
-                if (part is SolidRocket)
-                {
-                    return true;
-                }
-
-                if (part.Modules.OfType<ModuleEngines>().Count() == 0)
-                {
-                    return false;
-                }
-
-                if (part.Modules.OfType<ModuleEngines>().First().throttleLocked == true)
-                {
-                    return true;
                 }
 
                 return false;

--- a/KerbalEngineer/Simulation/SimulationManager.cs
+++ b/KerbalEngineer/Simulation/SimulationManager.cs
@@ -114,8 +114,8 @@
 
             if (parts.Count > 0)
             {
-                ThreadPool.QueueUserWorkItem(RunSimulation, new Simulation(parts));
-                //RunSimulation(new Simulation(parts));
+                //ThreadPool.QueueUserWorkItem(RunSimulation, new Simulation(parts));
+                RunSimulation(new Simulation(parts));
             }
             else
             {

 Binary files a/Output/KerbalEngineer/KerbalEngineer.dll and b/Output/KerbalEngineer/KerbalEngineer.dll differ