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.
This commit is contained in:
Kevin Zheng
2023-12-11 15:59:47 -08:00
committed by GitHub
parent 5d8d668a71
commit a2ed25ad03
16 changed files with 35 additions and 12 deletions

View File

@@ -56,7 +56,7 @@ public sealed class AirFilterSystem : EntitySystem
if (!GetAir(uid, out var air)) if (!GetAir(uid, out var air))
return; 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); var removed = air.RemoveRatio(ratio);
// nothing left to remove from the volume // nothing left to remove from the volume
if (MathHelper.CloseToPercent(removed.TotalMoles, 0f)) if (MathHelper.CloseToPercent(removed.TotalMoles, 0f))

View File

@@ -24,6 +24,7 @@ namespace Content.Server.Atmos.EntitySystems
public bool ExcitedGroupsSpaceIsAllConsuming { get; private set; } public bool ExcitedGroupsSpaceIsAllConsuming { get; private set; }
public float AtmosMaxProcessTime { get; private set; } public float AtmosMaxProcessTime { get; private set; }
public float AtmosTickRate { get; private set; } public float AtmosTickRate { get; private set; }
public float Speedup { get; private set; }
/// <summary> /// <summary>
/// Time between each atmos sub-update. If you are writing an atmos device, use AtmosDeviceUpdateEvent.dt /// 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.Superconduction, value => Superconduction = value, true);
_cfg.OnValueChanged(CCVars.AtmosMaxProcessTime, value => AtmosMaxProcessTime = value, true); _cfg.OnValueChanged(CCVars.AtmosMaxProcessTime, value => AtmosMaxProcessTime = value, true);
_cfg.OnValueChanged(CCVars.AtmosTickRate, value => AtmosTickRate = 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.ExcitedGroups, value => ExcitedGroups = value, true);
_cfg.OnValueChanged(CCVars.ExcitedGroupsSpaceIsAllConsuming, value => ExcitedGroupsSpaceIsAllConsuming = value, true); _cfg.OnValueChanged(CCVars.ExcitedGroupsSpaceIsAllConsuming, value => ExcitedGroupsSpaceIsAllConsuming = value, true);
} }

View File

@@ -59,7 +59,17 @@ namespace Content.Server.Atmos.EntitySystems
Span<float> tmp = stackalloc float[moles.Length]; Span<float> tmp = stackalloc float[moles.Length];
NumericsHelpers.Multiply(moles, GasSpecificHeats, tmp); 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;
}
/// <summary>
/// Return speedup factor for pumped or flow-based devices that depend on MaxTransferRate.
/// </summary>
public float PumpSpeedup()
{
return MathF.Sqrt(Speedup);
} }
/// <summary> /// <summary>

View File

@@ -105,7 +105,7 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems
return 0; return 0;
} }
float overPressConst = 300; // pressure difference (in atm) to get 200 L/sec transfer rate 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); return alpha * (float)Math.Sqrt(inlet.Pressure - outlet.Pressure);
} }

View File

@@ -105,7 +105,7 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems
return; return;
// We multiply the transfer rate in L/s by the seconds passed since the last process to get the liters. // 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. // Some of the gas from the mixture leaks when overclocked.
if (pump.Overclocked) if (pump.Overclocked)

View File

@@ -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. // 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) if (transferVol <= 0)
{ {

View File

@@ -62,7 +62,7 @@ namespace Content.Server.Atmos.Piping.Trinary.EntitySystems
else else
{ {
comp.Enabled = true; comp.Enabled = true;
transferRate = Math.Min(control * comp.Gain, comp.MaxTransferRate); transferRate = Math.Min(control * comp.Gain, comp.MaxTransferRate * _atmosphereSystem.PumpSpeedup());
} }
UpdateAppearance(uid, comp); UpdateAppearance(uid, comp);

View File

@@ -73,7 +73,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems
var timeDelta = args.dt; var timeDelta = args.dt;
// TODO adjust ratio so that environment does not go above MaxPressure? // 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); var removed = inlet.Air.RemoveRatio(ratio);
_atmosphereSystem.Merge(environment, removed); _atmosphereSystem.Merge(environment, removed);

View File

@@ -90,7 +90,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems
private void Scrub(float timeDelta, GasVentScrubberComponent scrubber, GasMixture? tile, PipeNode outlet) 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);
} }
/// <summary> /// <summary>

View File

@@ -145,7 +145,7 @@ namespace Content.Server.Atmos.Portable
private bool Scrub(float timeDelta, PortableScrubberComponent scrubber, GasMixture? tile) 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) private void UpdateAppearance(EntityUid uid, bool isFull, bool isRunning)

View File

@@ -45,6 +45,7 @@ public sealed partial class FrezonCoolantReaction : IGasReactionEffect
energyReleased = burnRate * Atmospherics.FrezonCoolEnergyReleased * energyModifier; energyReleased = burnRate * Atmospherics.FrezonCoolEnergyReleased * energyModifier;
} }
energyReleased /= atmosphereSystem.Speedup; // adjust energy to make sure speedup doesn't cause mega temperature rise
if (energyReleased >= 0f) if (energyReleased >= 0f)
return ReactionResult.NoReaction; return ReactionResult.NoReaction;

View File

@@ -56,6 +56,7 @@ namespace Content.Server.Atmos.Reactions
mixture.AdjustMoles(Gas.CarbonDioxide, plasmaBurnRate * (1.0f - supersaturation)); mixture.AdjustMoles(Gas.CarbonDioxide, plasmaBurnRate * (1.0f - supersaturation));
energyReleased += Atmospherics.FirePlasmaEnergyReleased * plasmaBurnRate; 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); mixture.ReactionResults[GasReaction.Fire] += plasmaBurnRate * (1 + oxygenBurnRate);
} }
} }

View File

@@ -47,6 +47,7 @@ namespace Content.Server.Atmos.Reactions
mixture.ReactionResults[GasReaction.Fire] += burnedFuel; mixture.ReactionResults[GasReaction.Fire] += burnedFuel;
} }
energyReleased /= atmosphereSystem.Speedup; // adjust energy to make sure speedup doesn't cause mega temperature rise
if (energyReleased > 0) if (energyReleased > 0)
{ {
var newHeatCapacity = atmosphereSystem.GetHeatCapacity(mixture); var newHeatCapacity = atmosphereSystem.GetHeatCapacity(mixture);

View File

@@ -1044,6 +1044,15 @@ namespace Content.Shared.CCVar
public static readonly CVarDef<float> AtmosTickRate = public static readonly CVarDef<float> AtmosTickRate =
CVarDef.Create("atmos.tickrate", 15f, CVar.SERVERONLY); CVarDef.Create("atmos.tickrate", 15f, CVar.SERVERONLY);
/// <summary>
/// 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.
/// </summary>
public static readonly CVarDef<float> AtmosSpeedup =
CVarDef.Create("atmos.speedup", 64f, CVar.SERVERONLY);
/* /*
* MIDI instruments * MIDI instruments
*/ */

View File

@@ -291,7 +291,7 @@
True: { state: freezerOn } True: { state: freezerOn }
False: { state: freezerOff } False: { state: freezerOff }
- type: GasThermoMachine - type: GasThermoMachine
coefficientOfPerformance: -64 coefficientOfPerformance: -3.9
- type: ApcPowerReceiver - type: ApcPowerReceiver
powerDisabled: true #starts off powerDisabled: true #starts off
- type: Machine - type: Machine
@@ -334,7 +334,7 @@
True: { state: heaterOn } True: { state: heaterOn }
False: { state: heaterOff } False: { state: heaterOff }
- type: GasThermoMachine - type: GasThermoMachine
coefficientOfPerformance: 32 coefficientOfPerformance: 0.95
- type: ApcPowerReceiver - type: ApcPowerReceiver
powerDisabled: true #starts off powerDisabled: true #starts off
- type: Machine - type: Machine

View File

@@ -57,7 +57,6 @@
# It fires processing on behalf of its connected circulators. # It fires processing on behalf of its connected circulators.
- type: AtmosDevice - type: AtmosDevice
- type: TegGenerator - type: TegGenerator
powerFactor: 0.025
- type: DeviceNetwork - type: DeviceNetwork
deviceNetId: AtmosDevices deviceNetId: AtmosDevices