diff --git a/SolverEngines/DeferredEngineExhaustDamage.cs b/SolverEngines/DeferredEngineExhaustDamage.cs new file mode 100644 index 0000000..ad3f76c --- /dev/null +++ b/SolverEngines/DeferredEngineExhaustDamage.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections.Generic; +using Unity.Collections; +using Unity.Jobs; +using UnityEngine; + +namespace SolverEngines +{ + [DefaultExecutionOrder(1)] + public class DeferredEngineExhaustDamage : MonoBehaviour + { + private int layerMask; + private readonly List engines = new List(128); + private readonly List thrustTransforms = new List(128); + private readonly List multipliers = new List(128); + private readonly Dictionary damagedParts = new Dictionary(16); + + public void Start() + { + layerMask = LayerUtil.DefaultEquivalent | (1 << LayerMask.NameToLayer("Parts")); + } + + public void AddEngine(ModuleEngines engine) + { + if (engine.exhaustDamage) + { + foreach (var thrustTransform in engine.thrustTransforms) + { + engines.Add(engine); + thrustTransforms.Add(thrustTransform); + } + multipliers.AddRange(engine.thrustTransformMultipliers); + } + } + + public void FixedUpdate() + { + int raysCount = engines.Count; + if (raysCount == 0) return; + + var results = new NativeArray(raysCount, Allocator.Temp); + var commands = new NativeArray(raysCount, Allocator.Temp); + + for (int index = 0; index < raysCount; index++) + { + Transform thrustTransform = thrustTransforms[index]; + ModuleEngines engine = engines[index]; + commands[index++] = new RaycastCommand(thrustTransform.position, thrustTransform.forward, engine.exhaustDamageMaxRange, layerMask, maxHits: 1); + } + RaycastCommand.ScheduleBatch(commands, results, 1).Complete(); + + for (int index = 0; index < raysCount; index++) + { + Transform thrustTransform = thrustTransforms[index]; + ModuleEngines engine = engines[index]; + RaycastHit hit = results[index]; + double mult = multipliers[index]; + if (hit.collider != null) + { + Transform transform = hit.collider.transform; + Part partUpwardsCached = FlightGlobals.GetPartUpwardsCached(transform.gameObject); + if (partUpwardsCached != null && partUpwardsCached != engine.part && !transform.GetComponentInChildren()) + { + double flux = engine.finalThrust * mult * engine.exhaustDamageMultiplier; + double x = Math.Max(0.001, hit.distance + engine.exhaustDamageDistanceOffset); + double falloff = Math.Pow(x, -engine.exhaustDamageFalloffPower); + double splashback = Math.Pow(x, -engine.exhaustDamageSplashbackFallofPower) * engine.exhaustDamageSplashbackMult; + falloff = Math.Min(falloff, engine.exhaustDamageMaxMutliplier); + splashback = Math.Min(splashback, engine.exhaustDamageSplashbackMaxMutliplier); + partUpwardsCached.AddSkinThermalFlux(flux * falloff); + engine.part.AddSkinThermalFlux(flux * splashback); + partUpwardsCached.AddForceAtPosition(thrustTransform.forward * engine.finalThrust * multipliers[index], hit.point); + if (engine.exhaustDamageLogEvent) + damagedParts.Add(engine.part, partUpwardsCached); + } + } + } + if (damagedParts.Count > 0) + foreach (var srcDest in damagedParts) + GameEvents.onSplashDamage.Fire(new EventReport(FlightEvents.SPLASHDAMAGE, srcDest.Key, srcDest.Value.partInfo.title, srcDest.Key.partInfo.title)); + + results.Dispose(); + commands.Dispose(); + engines.Clear(); + thrustTransforms.Clear(); + multipliers.Clear(); + damagedParts.Clear(); + } + } +} diff --git a/SolverEngines/EngineModule.cs b/SolverEngines/EngineModule.cs index f05ff17..4f851d2 100644 --- a/SolverEngines/EngineModule.cs +++ b/SolverEngines/EngineModule.cs @@ -3,6 +3,7 @@ using UnityEngine; using KSP.UI.Screens; using SolverEngines.EngineFitting; +using UnityEngine.Profiling; namespace SolverEngines { @@ -14,19 +15,19 @@ public abstract class ModuleEnginesSolver : ModuleEnginesFX, IModuleInfo, IEngin { // base fields - [KSPField(isPersistant = false, guiActiveEditor = true, guiFormat = "F3")] + [KSPField(guiActiveEditor = true, guiFormat = "F3")] public float Need_Area; - [KSPField(isPersistant = false, guiActive = true, guiName = "Current Throttle", guiFormat = "N2", guiUnits = "%")] + [KSPField(guiActive = true, guiName = "Current Throttle", guiFormat = "N2", guiUnits = "%")] public float actualThrottle; [KSPField(guiActive = true, guiName = "Mass Flow", guiUnits = " kg/s", guiFormat = "F5")] public float massFlowGui; - [KSPField(isPersistant = false)] + [KSPField] public double thrustUpperLimit = double.MaxValue; - [KSPField(isPersistant = false)] + [KSPField] public bool multiplyThrustByFuelFrac = true; [KSPField] @@ -44,15 +45,15 @@ public abstract class ModuleEnginesSolver : ModuleEnginesFX, IModuleInfo, IEngin // engine temp stuff // fields - [KSPField(isPersistant = false)] + [KSPField] public double maxEngineTemp; - [KSPField(isPersistant = false, guiActive = true, guiName = "Eng. Internal Temp")] - public string engineTempString; + [KSPField(guiActive = true, guiName = "Eng. Internal Temp", guiFormat = "N0")] + public double engineTemp = 288.15d; [KSPField] public double tempGaugeMin = 0.8d; // internals - protected double tempRatio = 0d, engineTemp = 288.15d; + protected double tempRatio = 0d; public double GetEngineTemp => engineTemp; @@ -66,6 +67,7 @@ public abstract class ModuleEnginesSolver : ModuleEnginesFX, IModuleInfo, IEngin // protected internals protected EngineSolver engineSolver = null; + protected DeferredEngineExhaustDamage exhaustDamager = null; protected EngineThermodynamics ambientTherm = new EngineThermodynamics(); protected EngineThermodynamics inletTherm = new EngineThermodynamics(); @@ -105,13 +107,14 @@ virtual public void Start() { CreateEngine(); Need_Area = RequiredIntakeArea(); - Fields["Need_Area"].guiActiveEditor = Need_Area > 0f; + Fields[nameof(Need_Area)].guiActiveEditor = Need_Area > 0f; currentThrottle = 0f; flameout = false; SetUnflameout(); - Fields["fuelFlowGui"].guiActive = false; - Fields["massFlowGui"].guiUnits = " kg/s"; + Fields[nameof(fuelFlowGui)].guiActive = false; + Fields[nameof(massFlowGui)].guiUnits = " kg/s"; flowKG = true; + Fields[nameof(engineTemp)].guiUnits = $" K / {maxEngineTemp:N0} K"; } public override void OnStart(PartModule.StartState state) @@ -127,12 +130,13 @@ public override void OnStart(PartModule.StartState state) // Get emissives emissiveAnims = new List(); - int mCount = part.Modules.Count; - for (int i = 0; i < mCount; ++i) - if (part.Modules[i] is ModuleAnimateHeat) - emissiveAnims.Add(part.Modules[i] as ModuleAnimateHeat); + foreach (var pm in part.Modules) + if (pm is ModuleAnimateHeat) + emissiveAnims.Add(pm as ModuleAnimateHeat); CreateEngineIfNecessary(); + if (HighLogic.LoadedSceneIsFlight && !vessel.TryGetComponent(out exhaustDamager)) + exhaustDamager = vessel.gameObject.AddComponent(); } public override void OnLoad(ConfigNode node) @@ -147,12 +151,9 @@ public override void OnLoad(ConfigNode node) { if (trfNode.name != "THRUST_TRANSFORM") continue; - ThrustTransformInfo info; - try { - info = new ThrustTransformInfo(trfNode); - thrustTransformInfos.Add(info); + thrustTransformInfos.Add(new ThrustTransformInfo(trfNode)); } catch (Exception e) { @@ -291,7 +292,7 @@ protected void InitializeThrustTransforms() part.AddForceAtPosition(thrustRot * (axis * thrustTransformMultipliers[i] * finalThrust), t.position + t.rotation * thrustOffset); } } - EngineExhaustDamage(); + DeferredEngineExhaustDamage(); double thermalFlux = tempRatio * tempRatio * heatProduction * vessel.VesselValues.HeatProduction.value * PhysicsGlobals.InternalHeatProductionFactor * part.thermalMass; part.AddThermalFlux(thermalFlux); @@ -303,6 +304,8 @@ protected void InitializeThrustTransforms() } } + public virtual void DeferredEngineExhaustDamage() => exhaustDamager?.AddEngine(this); + public override bool CanStart() { return base.CanStart() || flameout; @@ -310,17 +313,21 @@ public override bool CanStart() public override void FXUpdate() { + Profiler.BeginSample("EngineSolver.FXUpdate"); part.Effect(directThrottleEffectName, engineSolver.GetFXThrottle()); part.Effect(spoolEffectName, engineSolver.GetFXSpool()); part.Effect(runningEffectName, engineSolver.GetFXRunning()); + Profiler.BeginSample("EngineSolver.FXUpdate.GetFXPower"); part.Effect(powerEffectName, engineSolver.GetFXPower()); + Profiler.EndSample(); + Profiler.EndSample(); } virtual protected void UpdateTemp() { if (tempRatio > 1d && !CheatOptions.IgnoreMaxTemperature) { - FlightLogger.eventLog.Add("[" + FormatTime(vessel.missionTime) + "] " + part.partInfo.title + " melted its internals from heat."); + FlightLogger.eventLog.Add($"[{FormatTime(vessel.missionTime)}] {part.partInfo.title} melted its internals from heat."); part.explode(); } else @@ -338,7 +345,6 @@ virtual public void UpdateInletEffects(EngineThermodynamics inletTherm, double a this.inletTherm = inletTherm; this.areaRatio = areaRatio; - } public override void UpdateThrottle() @@ -355,20 +361,24 @@ virtual public void UpdateFlightCondition() virtual public void UpdateSolver(EngineThermodynamics ambientTherm, double altitude, Vector3d vel, double mach, bool ignited, bool oxygen, bool underwater) { // In flight, these are the same and this will just return + Profiler.BeginSample("EngineSolver.UpdateSolver"); this.ambientTherm = ambientTherm; engineSolver.SetEngineState(ignited, lastPropellantFraction); engineSolver.SetFreestreamAndInlet(ambientTherm, inletTherm, altitude, mach, vel, oxygen, underwater); + Profiler.BeginSample("EngineSolver.UpdateSolver.CalculatePerformance"); engineSolver.CalculatePerformance(areaRatio, currentThrottle, flowMult * multFlow, ispMult * multIsp); + Profiler.EndSample(); + Profiler.EndSample(); } virtual public void CalculateEngineParams() { + Profiler.BeginSample("EngineSolver.CalculateEngineParams"); SetEmissive(engineSolver.GetEmissive()); // Heat engineTemp = engineSolver.GetEngineTemp(); tempRatio = engineTemp / maxEngineTemp; - engineTempString = engineTemp.ToString("N0") + " K / " + maxEngineTemp.ToString("n0") + " K"; double thrustIn = engineSolver.GetThrust(); //in N double isp = engineSolver.GetIsp(); @@ -399,6 +409,7 @@ virtual public void CalculateEngineParams() } else { + Profiler.BeginSample("EngineSolver.CalculateEngineParams.RunningEngine"); // calc flow double vesselValue = vessel.VesselValues.FuelUsage.value; if (vesselValue == 0d) @@ -416,7 +427,9 @@ virtual public void CalculateEngineParams() { if (massFlow > 0d) { + Profiler.BeginSample("EngineSolver.CalculateEngineParams.RunningEngine.RequestPropellant"); lastPropellantFraction = RequestPropellant(massFlow); + Profiler.EndSample(); } else { @@ -424,6 +437,7 @@ virtual public void CalculateEngineParams() } } this.propellantReqMet = (float)this.lastPropellantFraction * 100; + Profiler.EndSample(); // set produced thrust if (multiplyThrustByFuelFrac) @@ -438,30 +452,20 @@ virtual public void CalculateEngineParams() // set fuel flow fuelFlowGui = (float)(fuelFlow * 0.001d * mixtureDensityRecip / ratioSum); // Also in tons - if (fuelFlow > 1000d) + // If we're displaying in the wrong mode, swap + if (flowKG != (fuelFlow <= 1000)) { - fuelFlow *= 0.001d; - if (flowKG) - { - Fields["massFlowGui"].guiUnits = " ton/s"; - flowKG = false; - } - } - else - { - if (!flowKG) - { - Fields["massFlowGui"].guiUnits = " kg/s"; - flowKG = true; - } + flowKG = fuelFlow <= 1000; + Fields[nameof(massFlowGui)].guiUnits = flowKG ? " kg/s" : " ton/s"; } + if (fuelFlow > 1000d) + fuelFlow *= 0.001d; massFlowGui = (float)fuelFlow; - - realIsp = (float)isp; } finalThrust = (float)producedThrust * vessel.VesselValues.EnginePower.value; + Profiler.EndSample(); } virtual public bool PropellantAvailable() diff --git a/SolverEngines/Properties/AssemblyInfo.cs b/SolverEngines/Properties/AssemblyInfo.cs index 4f7439b..c7121db 100644 --- a/SolverEngines/Properties/AssemblyInfo.cs +++ b/SolverEngines/Properties/AssemblyInfo.cs @@ -32,7 +32,7 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("3.3.0.0")] // Don't change until breaking changes occur -[assembly: AssemblyFileVersion("3.12.0.0")] +[assembly: AssemblyVersion("3.13.0.0")] // Don't change until breaking changes occur +[assembly: AssemblyFileVersion("3.13.0.0")] -[assembly: KSPAssembly("SolverEngines", 3, 12, 0)] +[assembly: KSPAssembly("SolverEngines", 3, 13, 0)] diff --git a/SolverEngines/SolverEngines.csproj b/SolverEngines/SolverEngines.csproj index 13fc280..f193fee 100644 --- a/SolverEngines/SolverEngines.csproj +++ b/SolverEngines/SolverEngines.csproj @@ -18,7 +18,7 @@ portable false ..\GameData\SolverEngines\Plugins\ - DEBUG;TRACE + TRACE;DEBUG;ENABLE_PROFILER prompt 4 false @@ -37,6 +37,7 @@ +