Files
tbd-station-14/Content.Server/Atmos/Piping/Binary/EntitySystems/GasRecyclerSystem.cs
Kevin Zheng a2ed25ad03 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.
2023-12-11 15:59:47 -08:00

142 lines
6.2 KiB
C#

using Content.Server.Atmos.EntitySystems;
using Content.Server.Atmos.Piping.Binary.Components;
using Content.Server.Atmos.Piping.Components;
using Content.Server.Construction;
using Content.Server.NodeContainer;
using Content.Server.NodeContainer.EntitySystems;
using Content.Server.NodeContainer.Nodes;
using Content.Shared.Atmos;
using Content.Shared.Atmos.Piping;
using Content.Shared.Audio;
using Content.Shared.Examine;
using JetBrains.Annotations;
using Robust.Server.GameObjects;
namespace Content.Server.Atmos.Piping.Binary.EntitySystems
{
[UsedImplicitly]
public sealed class GasReyclerSystem : EntitySystem
{
[Dependency] private readonly AppearanceSystem _appearance = default!;
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
[Dependency] private readonly SharedAmbientSoundSystem _ambientSoundSystem = default!;
[Dependency] private readonly NodeContainerSystem _nodeContainer = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<GasRecyclerComponent, AtmosDeviceEnabledEvent>(OnEnabled);
SubscribeLocalEvent<GasRecyclerComponent, AtmosDeviceUpdateEvent>(OnUpdate);
SubscribeLocalEvent<GasRecyclerComponent, AtmosDeviceDisabledEvent>(OnDisabled);
SubscribeLocalEvent<GasRecyclerComponent, ExaminedEvent>(OnExamined);
SubscribeLocalEvent<GasRecyclerComponent, RefreshPartsEvent>(OnRefreshParts);
SubscribeLocalEvent<GasRecyclerComponent, UpgradeExamineEvent>(OnUpgradeExamine);
}
private void OnEnabled(EntityUid uid, GasRecyclerComponent comp, AtmosDeviceEnabledEvent args)
{
UpdateAppearance(uid, comp);
}
private void OnExamined(Entity<GasRecyclerComponent> ent, ref ExaminedEvent args)
{
var comp = ent.Comp;
if (!EntityManager.GetComponent<TransformComponent>(ent).Anchored || !args.IsInDetailsRange) // Not anchored? Out of range? No status.
return;
if (!EntityManager.TryGetComponent(ent, out NodeContainerComponent? nodeContainer)
|| !_nodeContainer.TryGetNode(nodeContainer, comp.InletName, out PipeNode? inlet)
|| !_nodeContainer.TryGetNode(nodeContainer, comp.OutletName, out PipeNode? _))
{
return;
}
if (comp.Reacting)
{
args.PushMarkup(Loc.GetString("gas-recycler-reacting"));
}
else
{
if (inlet.Air.Pressure < comp.MinPressure)
{
args.PushMarkup(Loc.GetString("gas-recycler-low-pressure"));
}
if (inlet.Air.Temperature < comp.MinTemp)
{
args.PushMarkup(Loc.GetString("gas-recycler-low-temperature"));
}
}
}
private void OnUpdate(Entity<GasRecyclerComponent> ent, ref AtmosDeviceUpdateEvent args)
{
var comp = ent.Comp;
if (!EntityManager.TryGetComponent(ent, out NodeContainerComponent? nodeContainer)
|| !_nodeContainer.TryGetNode(nodeContainer, comp.InletName, out PipeNode? inlet)
|| !_nodeContainer.TryGetNode(nodeContainer, comp.OutletName, out PipeNode? outlet))
{
_ambientSoundSystem.SetAmbience(ent, false);
return;
}
// The gas recycler is a passive device, so it permits gas flow even if nothing is being reacted.
comp.Reacting = inlet.Air.Temperature >= comp.MinTemp && inlet.Air.Pressure >= comp.MinPressure;
var removed = inlet.Air.RemoveVolume(PassiveTransferVol(inlet.Air, outlet.Air));
if (comp.Reacting)
{
var nCO2 = removed.GetMoles(Gas.CarbonDioxide);
removed.AdjustMoles(Gas.CarbonDioxide, -nCO2);
removed.AdjustMoles(Gas.Oxygen, nCO2);
var nN2O = removed.GetMoles(Gas.NitrousOxide);
removed.AdjustMoles(Gas.NitrousOxide, -nN2O);
removed.AdjustMoles(Gas.Nitrogen, nN2O);
}
_atmosphereSystem.Merge(outlet.Air, removed);
UpdateAppearance(ent, comp);
_ambientSoundSystem.SetAmbience(ent, true);
}
public float PassiveTransferVol(GasMixture inlet, GasMixture outlet)
{
if (inlet.Pressure < outlet.Pressure)
{
return 0;
}
float overPressConst = 300; // pressure difference (in atm) to get 200 L/sec transfer rate
float alpha = Atmospherics.MaxTransferRate * _atmosphereSystem.PumpSpeedup() / (float)Math.Sqrt(overPressConst*Atmospherics.OneAtmosphere);
return alpha * (float)Math.Sqrt(inlet.Pressure - outlet.Pressure);
}
private void OnDisabled(EntityUid uid, GasRecyclerComponent comp, AtmosDeviceDisabledEvent args)
{
comp.Reacting = false;
UpdateAppearance(uid, comp);
}
private void UpdateAppearance(EntityUid uid, GasRecyclerComponent? comp = null)
{
if (!Resolve(uid, ref comp, false))
return;
_appearance.SetData(uid, PumpVisuals.Enabled, comp.Reacting);
}
private void OnRefreshParts(EntityUid uid, GasRecyclerComponent component, RefreshPartsEvent args)
{
var ratingTemp = args.PartRatings[component.MachinePartMinTemp];
var ratingPressure = args.PartRatings[component.MachinePartMinPressure];
component.MinTemp = component.BaseMinTemp * MathF.Pow(component.PartRatingMinTempMultiplier, ratingTemp - 1);
component.MinPressure = component.BaseMinPressure * MathF.Pow(component.PartRatingMinPressureMultiplier, ratingPressure - 1);
}
private void OnUpgradeExamine(EntityUid uid, GasRecyclerComponent component, UpgradeExamineEvent args)
{
args.AddPercentageUpgrade("gas-recycler-upgrade-min-temp", component.MinTemp / component.BaseMinTemp);
args.AddPercentageUpgrade("gas-recycler-upgrade-min-pressure", component.MinPressure / component.BaseMinPressure);
}
}
}