public void Update()
        {
            if (!HighLogic.LoadedSceneIsEditor)
            {
                return;
            }

            SetAnimationRatio(0, throttleAnimationState);

            foreach (var engine in moduleEngines)
            {
                currentEngine = engine;

                UpdateFX();
            }
        }
        // Finds the active moduleEngine module from the MultiModeEngine partModule
        private void FetchActiveMode()
        {
            for (var i = 0; i < moduleEngines.Length; i++)
            {
                var persistentEngineModule = moduleEngines[i];
                persistentEngineModule.powerEffectName   = powerEffectNameList[i];
                persistentEngineModule.runningEffectName = runningEffectNameList[i];

                ApplyEffect(persistentEngineModule.powerEffectName, 0);
                ApplyEffect(persistentEngineModule.runningEffectName, 0);
            }

            if (!isMultiMode)
            {
                return;
            }

            currentEngine = moduleEngines.FirstOrDefault(m =>
                                                         m.engine == (multiModeEngine.runningPrimary
                    ? multiModeEngine.PrimaryEngine
                    : multiModeEngine.SecondaryEngine));
        }
        // Physics update
        public void FixedUpdate() // FixedUpdate is also called while not staged
        {
            if (this.vessel is null || currentEngine.engine is null || !isEnabled)
            {
                return;
            }

            fixedUpdateCount++;
            var universalTime = Planetarium.GetUniversalTime();

            // restore heading at load
            if (HasPersistentHeadingEnabled && fixedUpdateCount <= 60 && vesselAlignmentWithAutopilotMode > 0.995)
            {
                vessel.Autopilot.SetMode(persistentAutopilotMode);
                vessel.PersistHeading(TimeWarp.fixedDeltaTime, headingTolerance, vesselChangedSoiCountdown > 0);
            }
            else
            {
                persistentAutopilotMode = vessel.Autopilot.Mode;
            }

            //vesselHeadingVersusManeuver = vessel.VesselOrbitHeadingVersusManeuverVector();
            //vesselHeadingVersusManeuverInDegrees = Math.Acos(Math.Max(-1, Math.Min(1, vesselHeadingVersusManeuver))) * Rad2Deg;

            _kerbalismResourceChangeRequest.Clear();

            if (vesselChangedSoiCountdown > 0)
            {
                vesselChangedSoiCountdown--;
            }

            // Checks if moduleEngine mode wasn't switched
            FetchActiveMode();

            var processedEngines = isMultiMode ? new[] { currentEngine } : moduleEngines;

            // Realtime mode
            if (!vessel.packed)
            {
                vesselAlignmentWithAutopilotMode = vessel.HeadingVersusAutopilotVector(universalTime);

                // Update persistent thrust throttle if NOT transitioning from warp to realtime
                if (!warpToReal)
                {
                    UpdatePersistentThrottle();
                }

                for (var i = 0; i < processedEngines.Length; i++)
                {
                    currentEngine = processedEngines[i];

                    ResetMonitoringVariables();

                    // Update persistent thrust isp if NOT transitioning from warp to realtime
                    if (!warpToReal)
                    {
                        UpdatePersistentIsp();
                    }

                    currentEngine.engineHasAnyMassLessPropellants = currentEngine.engine.propellants.Any(m => m.resourceDef.density == 0);

                    if (processMasslessSeparately && currentEngine.engineHasAnyMassLessPropellants)
                    {
                        ReloadPropellantsWithoutMasslessPropellants();
                    }

                    //if (vesselHeadingVersusManeuverInDegrees > maneuverTolerance)
                    //{
                    //    moduleEngine.maxFuelFlow = 1e-10f;
                    //    finalThrust = 0;
                    //}
                    //else

                    if (!currentEngine.engine.getIgnitionState)
                    {
                        currentEngine.finalThrust = 0;

                        // restore maximum flow
                        RestoreMaxFuelFlow();
                    }
                    else if (!currentEngine.engineHasAnyMassLessPropellants && currentEngine.engine.propellantReqMet > 0)
                    {
                        // Mass flow rate
                        var massFlowRate = currentEngine.persistentIsp > 0
                            ? currentEngine.engine.currentThrottle * currentEngine.engine.maxThrust / (currentEngine.persistentIsp * PhysicsGlobals.GravitationalAcceleration)
                            : 0;
                        // Change in mass over time interval dT
                        var deltaMass = massFlowRate * TimeWarp.fixedDeltaTime;
                        // Resource demand from propellants with mass
                        currentEngine.demandMass = currentEngine.averageDensity > 0 ? deltaMass / currentEngine.averageDensity : 0;
                        // Calculate resource demands
                        currentEngine.fuelDemands = CalculateDemands(currentEngine.demandMass);
                        // Apply resource demands & test for resource depletion
                        ApplyDemands(currentEngine.fuelDemands, ref currentEngine.propellantReqMetFactor);

                        // calculate maximum flow
                        var maxFuelFlow = currentEngine.persistentIsp > 0 ? currentEngine.engine.maxThrust / (currentEngine.persistentIsp * PhysicsGlobals.GravitationalAcceleration) : 0;

                        // adjust fuel flow
                        currentEngine.engine.maxFuelFlow = maxFuelFlow > 0 && currentEngine.propellantReqMetFactor > 0 ? (float)(maxFuelFlow * currentEngine.propellantReqMetFactor) : 1e-10f;

                        // update displayed thrust and fx
                        currentEngine.finalThrust = currentEngine.engine.currentThrottle * currentEngine.engine.maxThrust * Math.Min(currentEngine.propellantReqMetFactor, currentEngine.engine.propellantReqMet * 0.01f);
                    }
                    else
                    {
                        // restore maximum flow
                        RestoreMaxFuelFlow();

                        currentEngine.propellantReqMetFactor = currentEngine.engine.propellantReqMet * 0.01f;

                        currentEngine.finalThrust = currentEngine.engine.GetCurrentThrust();
                    }

                    UpdatePropellantReqMetFactorQueue();

                    UpdateFX();

                    SetThrottleAnimation();

                    UpdateBuffers();
                }
            }
            else
            {
                for (var i = 0; i < processedEngines.Length; i++)
                {
                    currentEngine = processedEngines[i];

                    ResetMonitoringVariables();

                    // restore maximum flow
                    RestoreMaxFuelFlow();

                    if (persistentThrottle > 0 && currentEngine.persistentIsp > 0 && isPersistentEngine && HasPersistentThrust)
                    {
                        if (TimeWarp.CurrentRateIndex == 0)
                        {
                            warpToReal = true; // Set to true for transition to realtime
                        }
                        // Calculated requested thrust
                        //var requestedThrust = vesselHeadingVersusManeuverInDegrees <= maneuverTolerance ? moduleEngine.thrustPercentage * 0.01f * persistentThrottle * moduleEngine.maxThrust : 0;
                        var requestedThrust = currentEngine.engine.thrustPercentage * 0.01f * persistentThrottle * currentEngine.engine.maxThrust;

                        var thrustVector = part.transform.up; // Thrust direction unit vector
                        // Calculate deltaV vector & resource demand from propellants with mass
                        var deltaVVector = CalculateDeltaVVector(currentEngine.averageDensity, vessel.totalMass, TimeWarp.fixedDeltaTime, requestedThrust, currentEngine.persistentIsp, thrustVector, out currentEngine.demandMass);
                        // Calculate resource demands
                        currentEngine.fuelDemands = CalculateDemands(currentEngine.demandMass);
                        // Apply resource demands & test for resource depletion
                        ApplyDemands(currentEngine.fuelDemands, ref currentEngine.propellantReqMetFactor);

                        // Apply deltaV vector at UT & dT to orbit if resources not depleted
                        if (currentEngine.propellantReqMetFactor > 0)
                        {
                            currentEngine.finalThrust = requestedThrust * currentEngine.propellantReqMetFactor;
                            vessel.orbit.Perturb(deltaVVector * currentEngine.propellantReqMetFactor, universalTime);
                        }

                        // Otherwise log warning and drop out of TimeWarp if throttle on & depleted
                        else if (persistentThrottle > 0)
                        {
                            currentEngine.finalThrust = 0;
                            Debug.Log("[PersistentThrust]: Thrust warp stopped - propellant depleted");
                            ScreenMessages.PostScreenMessage(Localizer.Format("#LOC_PT_StoppedDepleted"), 5.0f, ScreenMessageStyle.UPPER_CENTER);
                            // Return to realtime
                            TimeWarp.SetRate(0, true);
                            if (!vessel.IsControllable)
                            {
                                persistentThrottle            = 0;
                                vessel.ctrlState.mainThrottle = 0;
                            }
                        }
                        else
                        {
                            currentEngine.finalThrust = 0;
                        }

                        SetThrottleAnimation();

                        UpdateFX();
                    }
                    else
                    {
                        currentEngine.finalThrust = 0;

                        SetThrottleAnimation();

                        UpdateFX();

                        UpdateBuffers();
                    }
                }

                if (vessel.IsControllable && HasPersistentHeadingEnabled)
                {
                    vesselAlignmentWithAutopilotMode = vessel.PersistHeading(TimeWarp.fixedDeltaTime, headingTolerance, vesselChangedSoiCountdown > 0, vesselAlignmentWithAutopilotMode == 1);
                }
            }
            // display final thrust in a user friendly way
            thrustTxt = Utils.FormatThrust(moduleEngines.Sum(m => m.finalThrust));

            previousFixedDeltaTime = TimeWarp.fixedDeltaTime;

            UpdateMasslessConsumption();
        }
        // Update is called during refresh frame, which can be less frequent than FixedUpdate which is called every processing frame
        public override void OnUpdate()
        {
            var processedEngines = isMultiMode ? new[] { currentEngine } : moduleEngines;

            for (var i = 0; i < processedEngines.Length; i++)
            {
                currentEngine = processedEngines[i];

                if (currentEngine.engine == null)
                {
                    continue;
                }

                // hide stock thrust
                currentEngine.engine.Fields["finalThrust"].guiActive      = false;
                currentEngine.engine.Fields["realIsp"].guiActive          = false;
                currentEngine.engine.Fields["propellantReqMet"].guiActive = false;
            }

            var averagePropellantReqMetFactor = isMultiMode
                ? currentEngine.propellantReqMetFactor
                : moduleEngines.Average(m => m.propellantReqMetFactor);

            propellantReqMet = averagePropellantReqMetFactor * 100;

            var anyMasslessPropellants = isMultiMode
                ? currentEngine.engine.propellants.Any(m => m.resourceDef.density == 0)
                : moduleEngines.SelectMany(m => m.engine.propellants.Where(p => p.resourceDef.density == 0)).Any();

            var anyAutoMaximizePersistentIsp = isMultiMode
                ? currentEngine.autoMaximizePersistentIsp
                : moduleEngines.Any(m => m.autoMaximizePersistentIsp);

            realIsp = !vessel.packed && !anyMasslessPropellants
                ? currentEngine.engine.realIsp
                : vessel.packed && (MaximizePersistentIsp || anyAutoMaximizePersistentIsp) || persistentThrottle == 0
                    ? currentEngine.persistentIsp
                    : currentEngine.persistentIsp * averagePropellantReqMetFactor;

            if (!isPersistentEngine || !HasPersistentThrust)
            {
                return;
            }

            // When transitioning from TimeWarp to real update throttle
            if (warpToReal)
            {
                SetThrottle(persistentThrottle, true);
                warpToReal = false;
            }

            if (vessel.packed)
            {
                // maintain thrust setting during TimeWarp
                vessel.ctrlState.mainThrottle = persistentThrottle;

                // stop engines when X pressed
                if (Input.GetKeyDown(KeyCode.X))
                {
                    SetThrottle(0, returnToRealtimeAfterKeyPressed);
                }
                // full throttle when Z pressed
                else if (Input.GetKeyDown(KeyCode.Z))
                {
                    SetThrottle(1, returnToRealtimeAfterKeyPressed);
                }
                // increase throttle when Shift pressed
                else if (Input.GetKeyDown(KeyCode.LeftShift))
                {
                    SetThrottle(Mathf.Min(1, persistentThrottle + 0.01f), returnToRealtimeAfterKeyPressed);
                }
                // decrease throttle when Ctrl pressed
                else if (Input.GetKeyDown(KeyCode.LeftControl))
                {
                    SetThrottle(Mathf.Max(0, persistentThrottle - 0.01f), returnToRealtimeAfterKeyPressed);
                }
            }
            else
            {
                TimeWarp.GThreshold = 12;
            }
        }
        // Make "moduleEngine" and "moduleEngineFx" fields refer to the ModuleEngines and ModuleEnginesFX modules in part.Modules
        private void FindModuleEngines()
        {
            var moduleEnginesCount = 0;

            var moduleEnginesList = new List <PersistentEngineModule>();

            foreach (var partModule in part.Modules)
            {
                if (partModule is MultiModeEngine multiMode)
                {
                    multiModeEngine = multiMode;
                }
                else if (partModule is ModuleEngines engine)
                {
                    currentEngine = new PersistentEngineModule {
                        engine = engine
                    };

                    if (engine is ModuleEnginesFX engineFx)
                    {
                        powerEffectNameList.Add(engineFx.powerEffectName);
                        currentEngine.powerEffectName = engineFx.powerEffectName;
                        ApplyEffect(currentEngine.powerEffectName, 0);

                        runningEffectNameList.Add(engineFx.runningEffectName);
                        currentEngine.runningEffectName = engineFx.runningEffectName;
                        ApplyEffect(currentEngine.runningEffectName, 0);
                    }

                    moduleEnginesList.Add(currentEngine);

                    moduleEnginesCount++;
                }
            }

            moduleEngines = moduleEnginesList.ToArray();

            if (moduleEnginesCount == 1 && multiModeEngine == null)
            {
                Debug.Log("[PersistentThrust]: enabled for " + part.partInfo.title + " " + part.persistentId);
                isPersistentEngine = true;
            }
            else if (moduleEnginesCount == 0)
            {
                Debug.LogError("[PersistentThrust]: found no compatible engines, disabling PersistentThrust for " + part.partInfo.title + " " + part.persistentId);
                isPersistentEngine = false;
            }
            else if (moduleEnginesCount == 1 && multiModeEngine != null)
            {
                Debug.LogWarning("[PersistentThrust]: found Insufficient engines for MultiMode, using single engine mode PersistentThrust for " + part.partInfo.title + " " + part.persistentId);
                isPersistentEngine = false;
            }
            else if (moduleEnginesCount > 1 && multiModeEngine == null)
            {
                Debug.LogWarning("[PersistentThrust]: found multiple engines but no MultiMode PartModule, enabled multi engine PersistentThrust for " + part.partInfo.title + " " + part.persistentId);
                isPersistentEngine = true;
            }
            else if (multiModeEngine != null && moduleEnginesCount == 2)
            {
                Debug.Log("[PersistentThrust]: enabled MultiMode for " + part.partInfo.title + " " + part.persistentId);
                isPersistentEngine = true;
                isMultiMode        = true;
            }
            else
            {
                Debug.LogError("[PersistentThrust]: failed to initialize for " + part.partInfo.title + " " + part.persistentId);
                isPersistentEngine = false;
            }

            if (!isPersistentEngine)
            {
                return;
            }

            var engineFxList = part.FindModulesImplementing <ModuleEnginesFX>();

            foreach (var engineFx in engineFxList)
            {
                engineFx.powerEffectName   = string.Empty;
                engineFx.runningEffectName = string.Empty;
            }
        }