// ReSharper disable once UnusedMember.Global
        public override void OnFixedUpdateResourceSuppliable(double fixedDeltaTime)
        {
            if (_attachedEngine == null || !HighLogic.LoadedSceneIsFlight)
            {
                return;
            }

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

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

            CalculateTimeDialation();

            if (_attachedEngine is ModuleEnginesFX)
            {
                GetAllPropellants().ForEach(prop => part.Effect(prop.ParticleFXName, 0, -1)); // set all FX to zero
            }
            if (CurrentPropellant == null)
            {
                return;
            }

            _resourceBuffers.UpdateVariable(ResourceManager.FNRESOURCE_WASTEHEAT, (double)(decimal)part.mass);
            _resourceBuffers.UpdateBuffers();

            if (!vessel.packed && !_warpToReal)
            {
                storedThrotle = vessel.ctrlState.mainThrottle;
            }

            maxEffectivePower           = MaxEffectivePower;
            currentPropellantEfficiency = CurrentPropellantEfficiency;

            var sumOfAllEffectivePower = vessel.FindPartModulesImplementing <ElectricEngineControllerFX>().Where(ee => ee.IsOperational).Sum(ee => ee.MaxEffectivePower);

            _electrical_share_f = sumOfAllEffectivePower > 0 ? maxEffectivePower / sumOfAllEffectivePower : 1;

            modifiedThrotte          = ModifiedThrotte;
            modifiedMaxThrottlePower = maxEffectivePower * modifiedThrotte;

            totalPowerSupplied = getTotalPowerSupplied(ResourceManager.FNRESOURCE_MEGAJOULES);
            megaJoulesBarRatio = getResourceBarRatio(ResourceManager.FNRESOURCE_MEGAJOULES);

            effectiveResourceThrotling = megaJoulesBarRatio > 0.1 ? 1 : megaJoulesBarRatio * 10;

            availableMaximumPower = getAvailablePrioritisedStableSupply(ResourceManager.FNRESOURCE_MEGAJOULES);
            availableCurrentPower = getAvailablePrioritisedCurrentSupply(ResourceManager.FNRESOURCE_MEGAJOULES);

            maximumAvailablePowerForEngine = availableMaximumPower * _electrical_share_f;
            currentAvailablePowerForEngine = availableCurrentPower * _electrical_share_f;

            maximumThrustFromPower = EvaluateMaxThrust(maximumAvailablePowerForEngine);
            currentThrustFromPower = EvaluateMaxThrust(currentAvailablePowerForEngine);

            effectiveMaximumAvailablePowerForEngine = maximumAvailablePowerForEngine * effectiveResourceThrotling;
            effectiveCurrentAvailablePowerForEngine = currentAvailablePowerForEngine * effectiveResourceThrotling;

            modifiedMaximumPowerForEngine = effectiveMaximumAvailablePowerForEngine * modifiedThrotte;
            modifiedCurrentPowerForEngine = effectiveCurrentAvailablePowerForEngine * modifiedThrotte;

            maximum_power_request = CheatOptions.InfiniteElectricity
                ? modifiedMaximumPowerForEngine
                : currentPropellantEfficiency <= 0
                    ? 0
                    : Math.Min(modifiedMaximumPowerForEngine, modifiedMaxThrottlePower);

            current_power_request = CheatOptions.InfiniteElectricity
                ? modifiedCurrentPowerForEngine
                : currentPropellantEfficiency <= 0
                    ? 0
                    : Math.Min(modifiedCurrentPowerForEngine, modifiedMaxThrottlePower);

            // request electric power
            actualPowerReceived = CheatOptions.InfiniteElectricity
                ? current_power_request
                : consumeFNResourcePerSecond(current_power_request, maximum_power_request, ResourceManager.FNRESOURCE_MEGAJOULES);

            simulatedPowerReceived = Math.Min(effectiveMaximumAvailablePowerForEngine, maxEffectivePower);

            // produce waste heat
            var heatModifier     = (1 - currentPropellantEfficiency) * CurrentPropellant.WasteHeatMultiplier;
            var heatToProduce    = actualPowerReceived * heatModifier;
            var maxHeatToProduce = maximumAvailablePowerForEngine * heatModifier;

            _heat_production_f = CheatOptions.IgnoreMaxTemperature
                ? heatToProduce
                : supplyFNResourcePerSecondWithMax(heatToProduce, maxHeatToProduce, ResourceManager.FNRESOURCE_WASTEHEAT);

            // update GUI Values
            _electrical_consumption_f = actualPowerReceived;

            _effectiveIsp = _modifiedEngineBaseIsp * _modifiedCurrentPropellantIspMultiplier * ThrottleModifiedIsp();
            _maxIsp       = _effectiveIsp * CurrentPropellantThrustMultiplier;

            var throtteModifier = ispGears == 1 ? 1 : ModifiedThrotte;

            effectivePowerThrustModifier = timeDilation * currentPropellantEfficiency * CurrentPropellantThrustMultiplier * GetPowerThrustModifier();

            effectiveRecievedPower  = effectivePowerThrustModifier * actualPowerReceived * throtteModifier;
            effectiveSimulatedPower = effectivePowerThrustModifier * simulatedPowerReceived;

            currentThrustInSpace   = _effectiveIsp <= 0 ? 0 : effectiveRecievedPower / _effectiveIsp / GameConstants.STANDARD_GRAVITY;
            simulatedThrustInSpace = _effectiveIsp <= 0 ? 0 : effectiveSimulatedPower / _effectiveIsp / GameConstants.STANDARD_GRAVITY;

            _attachedEngine.maxThrust = (float)Math.Max(simulatedThrustInSpace, 0.001);

            _currentSpaceFuelFlowRate   = _maxIsp <= 0 ? 0 : currentThrustInSpace / _maxIsp / GameConstants.STANDARD_GRAVITY;
            _simulatedSpaceFuelFlowRate = _maxIsp <= 0 ? 0 : simulatedThrustInSpace / _maxIsp / GameConstants.STANDARD_GRAVITY;

            var maxThrustWithCurrentThrottle = currentThrustInSpace * throtteModifier;

            calculated_thrust = CurrentPropellant.SupportedEngines == 8
                ? maxThrustWithCurrentThrottle
                : Math.Max(maxThrustWithCurrentThrottle - (exitArea * vessel.staticPressurekPa), 0);

            simulated_max_thrust = CurrentPropellant.SupportedEngines == 8
                ? simulatedThrustInSpace
                : Math.Max(simulatedThrustInSpace - (exitArea * vessel.staticPressurekPa), 0);

            var throttle = _attachedEngine.getIgnitionState && _attachedEngine.currentThrottle > 0 ? Math.Max(_attachedEngine.currentThrottle, 0.01) : 0;

            if (throttle > 0)
            {
                if (IsValidPositiveNumber(calculated_thrust) && IsValidPositiveNumber(maxThrustWithCurrentThrottle))
                {
                    _atmosphereThrustEfficiency = Math.Min(1, calculated_thrust / maxThrustWithCurrentThrottle);

                    _atmosphereThrustEfficiencyPercentage = _atmosphereThrustEfficiency * 100;

                    UpdateIsp(_atmosphereThrustEfficiency);

                    _fuelFlowModifier = ispGears == 1
                        ? 1 / throttle
                        : ModifiedThrotte / throttle;

                    _maxFuelFlowRate            = (float)Math.Max(_atmosphereThrustEfficiency * _currentSpaceFuelFlowRate * _fuelFlowModifier, 1e-11);
                    _attachedEngine.maxFuelFlow = _maxFuelFlowRate;
                }
                else
                {
                    UpdateIsp(1);
                    _atmosphereThrustEfficiency = 0;
                    _maxFuelFlowRate            = 1e-11f;
                    _attachedEngine.maxFuelFlow = _maxFuelFlowRate;
                }

                if (!this.vessel.packed)
                {
                    // allow throtle to be used up to Geeforce treshold
                    TimeWarp.GThreshold = GThreshold;

                    _isFullyStarted = true;
                    _ispPersistent  = _attachedEngine.realIsp;

                    thrust_d = _attachedEngine.requestedMassFlow * GameConstants.STANDARD_GRAVITY * _ispPersistent;

                    ratioHeadingVersusRequest = 0;
                }
                else if (this.vessel.packed && _attachedEngine.isEnabled && FlightGlobals.ActiveVessel == vessel && _initializationCountdown == 0)
                {
                    _warpToReal = true; // Set to true for transition to realtime

                    thrust_d = calculated_thrust;

                    ratioHeadingVersusRequest = _attachedEngine.PersistHeading(_vesselChangedSIOCountdown > 0, ratioHeadingVersusRequest == 1);

                    if (ratioHeadingVersusRequest == 1)
                    {
                        PersistantThrust((double)(decimal)TimeWarp.fixedDeltaTime, Planetarium.GetUniversalTime(), this.part.transform.up, this.vessel.totalMass, thrust_d, _ispPersistent);
                    }
                }
                else
                {
                    IdleEngine();
                }
            }
            else
            {
                IdleEngine();
            }

            if (_attachedEngine is ModuleEnginesFX && particleEffectMult > 0)
            {
                var engineFuelFlow  = _attachedEngine.maxFuelFlow * _attachedEngine.currentThrottle;
                var maxFuelFlowRate = _attachedEngine.maxThrust / _attachedEngine.realIsp / GameConstants.STANDARD_GRAVITY;

                effectPower = Math.Min(1, particleEffectMult * (engineFuelFlow / maxFuelFlowRate));

                _particleFXName = String.IsNullOrEmpty(EffectName) ? CurrentPropellant.ParticleFXName : EffectName;

                this.part.Effect(_particleFXName, (float)effectPower, -1);
            }

            var vacuumPlasmaResource = part.Resources[InterstellarResourcesConfiguration.Instance.VacuumPlasma];

            if (isupgraded && vacuumPlasmaResource != null)
            {
                var calculatedConsumptionInTon = this.vessel.packed ? 0 : currentThrustInSpace / engineIsp / GameConstants.STANDARD_GRAVITY;
                vacuumPlasmaResource.maxAmount = Math.Max(0.0000001, calculatedConsumptionInTon * 200 * (double)(decimal)TimeWarp.fixedDeltaTime);
                part.RequestResource(InterstellarResourcesConfiguration.Instance.VacuumPlasma, -vacuumPlasmaResource.maxAmount);
            }
        }
        // Physics update
        public void FixedUpdate() // FixedUpdate is also called while not staged
        {
            if (this.vessel is null || !isEnabled)
            {
                return;
            }

            kerbalismResourceChangeRequest.Clear();

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

            // Realtime mode
            if (!this.vessel.packed)
            {
                engineHasAnyMassLessPropellants = engine.propellants.Any(m => m.resourceDef.density == 0);

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

                ratioHeadingVersusRequest = 0;

                if (!engineHasAnyMassLessPropellants)
                {
                    // Mass flow rate
                    var massFlowRate = IspPersistent > 0 ?  (engine.requestedThrottle * engine.maxThrust) / (IspPersistent * PhysicsGlobals.GravitationalAcceleration): 0;
                    // Change in mass over time interval dT
                    var deltaMass = massFlowRate * TimeWarp.fixedDeltaTime;
                    // Resource demand from propellants with mass
                    demandMass = densityAverage > 0 ? deltaMass / densityAverage : 0;

                    // Calculate resource demands
                    fuelDemands = CalculateDemands(demandMass);
                    // Apply resource demands & test for resource depletion
                    ApplyDemands(fuelDemands, ref propellantReqMetFactor);

                    // calculate maximum flow
                    var maxFuelFlow = IspPersistent > 0 ? engine.maxThrust / (IspPersistent * PhysicsGlobals.GravitationalAcceleration) : 0;
                    // adjust fuel flow
                    if (maxFuelFlow > 0)
                    {
                        engine.maxFuelFlow = (float)(maxFuelFlow * propellantReqMetFactor);
                    }
                    // update displayed thrust and fx
                    finalThrust = engine.currentThrottle * engine.maxThrust * propellantReqMetFactor;
                }
                else
                {
                    missingPowerCountdown = missingPowerCountdownSize;

                    propellantReqMetFactor = engine.propellantReqMet * 0.01f;

                    finalThrust = engine.GetCurrentThrust();
                }

                UpdatePropellantReqMetFactorQueue();

                UpdateFX(finalThrust);

                UpdateBuffers(fuelDemands);
            }
            else
            {
                if (ThrottlePersistent > 0 && IspPersistent > 0 && IsPersistentEngine && HasPersistentThrust)
                {
                    warpToReal = true; // Set to true for transition to realtime

                    if (vessel.IsControllable && HasPersistentHeadingEnabled)
                    {
                        ratioHeadingVersusRequest = engine.PersistHeading(TimeWarp.fixedDeltaTime, headingTolerance, vesselChangedSOICountdown > 0, ratioHeadingVersusRequest == 1);
                        if (ratioHeadingVersusRequest != 1)
                        {
                            finalThrust = 0;
                            return;
                        }
                    }
                    // Calculated requested thrust
                    var requestedThrust = engine.thrustPercentage * 0.01f * ThrottlePersistent * engine.maxThrust;
                    var UT       = Planetarium.GetUniversalTime(); // Universal time
                    var thrustUV = this.part.transform.up;         // Thrust direction unit vector
                    // Calculate deltaV vector & resource demand from propellants with mass
                    var deltaVV = CalculateDeltaVV(this.vessel.totalMass, TimeWarp.fixedDeltaTime, requestedThrust, IspPersistent, thrustUV, out demandMass);
                    // Calculate resource demands
                    fuelDemands = CalculateDemands(demandMass);
                    // Apply resource demands & test for resource depletion
                    ApplyDemands(fuelDemands, ref propellantReqMetFactor);

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

                    // Otherwise log warning and drop out of timewarp if throttle on & depleted
                    else if (ThrottlePersistent > 0)
                    {
                        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)
                        {
                            ThrottlePersistent            = 0;
                            vessel.ctrlState.mainThrottle = 0;
                        }
                    }
                    else
                    {
                        finalThrust = 0;
                    }

                    UpdateFX(finalThrust);
                }
                else
                {
                    finalThrust = 0;
                    if (vessel.IsControllable && HasPersistentHeadingEnabled)
                    {
                        ratioHeadingVersusRequest = engine.PersistHeading(TimeWarp.fixedDeltaTime, headingTolerance, vesselChangedSOICountdown > 0);
                    }
                    UpdateFX(0);

                    UpdateBuffers(fuelDemands);
                }
            }
        }