From a2ed25ad0361f641f9ca20fc7c7143722e695673 Mon Sep 17 00:00:00 2001 From: Kevin Zheng Date: Mon, 11 Dec 2023 15:59:47 -0800 Subject: [PATCH] Add atmos time compression (#21954) Add atmos.speedup which is effectively a atmos-only time compression CVar. This adjusts heat capacities and transfer rates to effectively globally speed up the time constants of atmos. This allows faster response to heating/cooling changes and faster cleanups (by buffing scrubbers, pumps, and everything else) that is tunable through one global time compression CVar. It also achieves this without any thermodynamic unsoundness. --- .../Atmos/EntitySystems/AirFilterSystem.cs | 2 +- .../Atmos/EntitySystems/AtmosphereSystem.CVars.cs | 2 ++ .../Atmos/EntitySystems/AtmosphereSystem.Gases.cs | 12 +++++++++++- .../Piping/Binary/EntitySystems/GasRecyclerSystem.cs | 2 +- .../Binary/EntitySystems/GasVolumePumpSystem.cs | 2 +- .../Piping/Trinary/EntitySystems/GasFilterSystem.cs | 2 +- .../EntitySystems/PressureControlledValveSystem.cs | 2 +- .../Unary/EntitySystems/GasOutletInjectorSystem.cs | 2 +- .../Unary/EntitySystems/GasVentScrubberSystem.cs | 2 +- .../Atmos/Portable/PortableScrubberSystem.cs | 2 +- .../Atmos/Reactions/FrezonCoolantReaction.cs | 1 + Content.Server/Atmos/Reactions/PlasmaFireReaction.cs | 1 + .../Atmos/Reactions/TritiumFireReaction.cs | 1 + Content.Shared/CCVar/CCVars.cs | 9 +++++++++ .../Structures/Piping/Atmospherics/unary.yml | 4 ++-- .../Entities/Structures/Power/Generation/teg.yml | 1 - 16 files changed, 35 insertions(+), 12 deletions(-) diff --git a/Content.Server/Atmos/EntitySystems/AirFilterSystem.cs b/Content.Server/Atmos/EntitySystems/AirFilterSystem.cs index a7404c08f4..6230a184d7 100644 --- a/Content.Server/Atmos/EntitySystems/AirFilterSystem.cs +++ b/Content.Server/Atmos/EntitySystems/AirFilterSystem.cs @@ -56,7 +56,7 @@ public sealed class AirFilterSystem : EntitySystem if (!GetAir(uid, out var air)) return; - var ratio = MathF.Min(1f, args.dt * filter.TransferRate); + var ratio = MathF.Min(1f, args.dt * filter.TransferRate * _atmosphere.PumpSpeedup()); var removed = air.RemoveRatio(ratio); // nothing left to remove from the volume if (MathHelper.CloseToPercent(removed.TotalMoles, 0f)) diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.CVars.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.CVars.cs index 60e215204d..1da9054505 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.CVars.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.CVars.cs @@ -24,6 +24,7 @@ namespace Content.Server.Atmos.EntitySystems public bool ExcitedGroupsSpaceIsAllConsuming { get; private set; } public float AtmosMaxProcessTime { get; private set; } public float AtmosTickRate { get; private set; } + public float Speedup { get; private set; } /// /// Time between each atmos sub-update. If you are writing an atmos device, use AtmosDeviceUpdateEvent.dt @@ -49,6 +50,7 @@ namespace Content.Server.Atmos.EntitySystems _cfg.OnValueChanged(CCVars.Superconduction, value => Superconduction = value, true); _cfg.OnValueChanged(CCVars.AtmosMaxProcessTime, value => AtmosMaxProcessTime = value, true); _cfg.OnValueChanged(CCVars.AtmosTickRate, value => AtmosTickRate = value, true); + _cfg.OnValueChanged(CCVars.AtmosSpeedup, value => Speedup = value, true); _cfg.OnValueChanged(CCVars.ExcitedGroups, value => ExcitedGroups = value, true); _cfg.OnValueChanged(CCVars.ExcitedGroupsSpaceIsAllConsuming, value => ExcitedGroupsSpaceIsAllConsuming = value, true); } diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Gases.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Gases.cs index 0726606ae0..afed8e223c 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Gases.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Gases.cs @@ -59,7 +59,17 @@ namespace Content.Server.Atmos.EntitySystems Span tmp = stackalloc float[moles.Length]; NumericsHelpers.Multiply(moles, GasSpecificHeats, tmp); - return MathF.Max(NumericsHelpers.HorizontalAdd(tmp), Atmospherics.MinimumHeatCapacity); + // Adjust heat capacity by speedup, because this is primarily what + // determines how quickly gases heat up/cool. + return MathF.Max(NumericsHelpers.HorizontalAdd(tmp), Atmospherics.MinimumHeatCapacity) / Speedup; + } + + /// + /// Return speedup factor for pumped or flow-based devices that depend on MaxTransferRate. + /// + public float PumpSpeedup() + { + return MathF.Sqrt(Speedup); } /// diff --git a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasRecyclerSystem.cs b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasRecyclerSystem.cs index 359123aabf..5ee239936c 100644 --- a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasRecyclerSystem.cs +++ b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasRecyclerSystem.cs @@ -105,7 +105,7 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems return 0; } float overPressConst = 300; // pressure difference (in atm) to get 200 L/sec transfer rate - float alpha = Atmospherics.MaxTransferRate / (float)Math.Sqrt(overPressConst*Atmospherics.OneAtmosphere); + float alpha = Atmospherics.MaxTransferRate * _atmosphereSystem.PumpSpeedup() / (float)Math.Sqrt(overPressConst*Atmospherics.OneAtmosphere); return alpha * (float)Math.Sqrt(inlet.Pressure - outlet.Pressure); } diff --git a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasVolumePumpSystem.cs b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasVolumePumpSystem.cs index 3cfbda27af..eb4fcf183e 100644 --- a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasVolumePumpSystem.cs +++ b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasVolumePumpSystem.cs @@ -105,7 +105,7 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems return; // We multiply the transfer rate in L/s by the seconds passed since the last process to get the liters. - var removed = inlet.Air.RemoveVolume(pump.TransferRate * args.dt); + var removed = inlet.Air.RemoveVolume(pump.TransferRate * _atmosphereSystem.PumpSpeedup() * args.dt); // Some of the gas from the mixture leaks when overclocked. if (pump.Overclocked) diff --git a/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasFilterSystem.cs b/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasFilterSystem.cs index 7032e7fe0e..62df033f93 100644 --- a/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasFilterSystem.cs +++ b/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasFilterSystem.cs @@ -65,7 +65,7 @@ namespace Content.Server.Atmos.Piping.Trinary.EntitySystems } // We multiply the transfer rate in L/s by the seconds passed since the last process to get the liters. - var transferVol = filter.TransferRate * args.dt; + var transferVol = filter.TransferRate * _atmosphereSystem.PumpSpeedup() * args.dt; if (transferVol <= 0) { diff --git a/Content.Server/Atmos/Piping/Trinary/EntitySystems/PressureControlledValveSystem.cs b/Content.Server/Atmos/Piping/Trinary/EntitySystems/PressureControlledValveSystem.cs index ceea449a31..e805a4d0ae 100644 --- a/Content.Server/Atmos/Piping/Trinary/EntitySystems/PressureControlledValveSystem.cs +++ b/Content.Server/Atmos/Piping/Trinary/EntitySystems/PressureControlledValveSystem.cs @@ -62,7 +62,7 @@ namespace Content.Server.Atmos.Piping.Trinary.EntitySystems else { comp.Enabled = true; - transferRate = Math.Min(control * comp.Gain, comp.MaxTransferRate); + transferRate = Math.Min(control * comp.Gain, comp.MaxTransferRate * _atmosphereSystem.PumpSpeedup()); } UpdateAppearance(uid, comp); diff --git a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasOutletInjectorSystem.cs b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasOutletInjectorSystem.cs index adde584cc3..45771b49d3 100644 --- a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasOutletInjectorSystem.cs +++ b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasOutletInjectorSystem.cs @@ -73,7 +73,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems var timeDelta = args.dt; // TODO adjust ratio so that environment does not go above MaxPressure? - var ratio = MathF.Min(1f, timeDelta * injector.TransferRate / inlet.Air.Volume); + var ratio = MathF.Min(1f, timeDelta * injector.TransferRate * _atmosphereSystem.PumpSpeedup() / inlet.Air.Volume); var removed = inlet.Air.RemoveRatio(ratio); _atmosphereSystem.Merge(environment, removed); diff --git a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentScrubberSystem.cs b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentScrubberSystem.cs index d63b521a35..37c8358a6f 100644 --- a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentScrubberSystem.cs +++ b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentScrubberSystem.cs @@ -90,7 +90,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems private void Scrub(float timeDelta, GasVentScrubberComponent scrubber, GasMixture? tile, PipeNode outlet) { - Scrub(timeDelta, scrubber.TransferRate, scrubber.PumpDirection, scrubber.FilterGases, tile, outlet.Air); + Scrub(timeDelta, scrubber.TransferRate*_atmosphereSystem.PumpSpeedup(), scrubber.PumpDirection, scrubber.FilterGases, tile, outlet.Air); } /// diff --git a/Content.Server/Atmos/Portable/PortableScrubberSystem.cs b/Content.Server/Atmos/Portable/PortableScrubberSystem.cs index 17e2ae591f..3e72b75f14 100644 --- a/Content.Server/Atmos/Portable/PortableScrubberSystem.cs +++ b/Content.Server/Atmos/Portable/PortableScrubberSystem.cs @@ -145,7 +145,7 @@ namespace Content.Server.Atmos.Portable private bool Scrub(float timeDelta, PortableScrubberComponent scrubber, GasMixture? tile) { - return _scrubberSystem.Scrub(timeDelta, scrubber.TransferRate, ScrubberPumpDirection.Scrubbing, scrubber.FilterGases, tile, scrubber.Air); + return _scrubberSystem.Scrub(timeDelta, scrubber.TransferRate * _atmosphereSystem.PumpSpeedup(), ScrubberPumpDirection.Scrubbing, scrubber.FilterGases, tile, scrubber.Air); } private void UpdateAppearance(EntityUid uid, bool isFull, bool isRunning) diff --git a/Content.Server/Atmos/Reactions/FrezonCoolantReaction.cs b/Content.Server/Atmos/Reactions/FrezonCoolantReaction.cs index 36a0f28686..ce134126be 100644 --- a/Content.Server/Atmos/Reactions/FrezonCoolantReaction.cs +++ b/Content.Server/Atmos/Reactions/FrezonCoolantReaction.cs @@ -45,6 +45,7 @@ public sealed partial class FrezonCoolantReaction : IGasReactionEffect energyReleased = burnRate * Atmospherics.FrezonCoolEnergyReleased * energyModifier; } + energyReleased /= atmosphereSystem.Speedup; // adjust energy to make sure speedup doesn't cause mega temperature rise if (energyReleased >= 0f) return ReactionResult.NoReaction; diff --git a/Content.Server/Atmos/Reactions/PlasmaFireReaction.cs b/Content.Server/Atmos/Reactions/PlasmaFireReaction.cs index 6aae69b2f2..9553b5ebfb 100644 --- a/Content.Server/Atmos/Reactions/PlasmaFireReaction.cs +++ b/Content.Server/Atmos/Reactions/PlasmaFireReaction.cs @@ -56,6 +56,7 @@ namespace Content.Server.Atmos.Reactions mixture.AdjustMoles(Gas.CarbonDioxide, plasmaBurnRate * (1.0f - supersaturation)); energyReleased += Atmospherics.FirePlasmaEnergyReleased * plasmaBurnRate; + energyReleased /= atmosphereSystem.Speedup; // adjust energy to make sure speedup doesn't cause mega temperature rise mixture.ReactionResults[GasReaction.Fire] += plasmaBurnRate * (1 + oxygenBurnRate); } } diff --git a/Content.Server/Atmos/Reactions/TritiumFireReaction.cs b/Content.Server/Atmos/Reactions/TritiumFireReaction.cs index cc58f0cf7d..78a149aa25 100644 --- a/Content.Server/Atmos/Reactions/TritiumFireReaction.cs +++ b/Content.Server/Atmos/Reactions/TritiumFireReaction.cs @@ -47,6 +47,7 @@ namespace Content.Server.Atmos.Reactions mixture.ReactionResults[GasReaction.Fire] += burnedFuel; } + energyReleased /= atmosphereSystem.Speedup; // adjust energy to make sure speedup doesn't cause mega temperature rise if (energyReleased > 0) { var newHeatCapacity = atmosphereSystem.GetHeatCapacity(mixture); diff --git a/Content.Shared/CCVar/CCVars.cs b/Content.Shared/CCVar/CCVars.cs index dc35ee4368..3014bbce0b 100644 --- a/Content.Shared/CCVar/CCVars.cs +++ b/Content.Shared/CCVar/CCVars.cs @@ -1044,6 +1044,15 @@ namespace Content.Shared.CCVar public static readonly CVarDef AtmosTickRate = CVarDef.Create("atmos.tickrate", 15f, CVar.SERVERONLY); + /// + /// Scale factor for how fast things happen in our atmosphere + /// simulation compared to real life. 1x means that a room takes as + /// long to heat up in game as real life. Players typically expect + /// things to happen faster in-game. + /// + public static readonly CVarDef AtmosSpeedup = + CVarDef.Create("atmos.speedup", 64f, CVar.SERVERONLY); + /* * MIDI instruments */ diff --git a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/unary.yml b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/unary.yml index 1d08bd8c6e..0fdad5d967 100644 --- a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/unary.yml +++ b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/unary.yml @@ -291,7 +291,7 @@ True: { state: freezerOn } False: { state: freezerOff } - type: GasThermoMachine - coefficientOfPerformance: -64 + coefficientOfPerformance: -3.9 - type: ApcPowerReceiver powerDisabled: true #starts off - type: Machine @@ -334,7 +334,7 @@ True: { state: heaterOn } False: { state: heaterOff } - type: GasThermoMachine - coefficientOfPerformance: 32 + coefficientOfPerformance: 0.95 - type: ApcPowerReceiver powerDisabled: true #starts off - type: Machine diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/teg.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/teg.yml index 090a015d97..723a1480de 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/teg.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/teg.yml @@ -57,7 +57,6 @@ # It fires processing on behalf of its connected circulators. - type: AtmosDevice - type: TegGenerator - powerFactor: 0.025 - type: DeviceNetwork deviceNetId: AtmosDevices