// 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); } } }