diff --git a/Content.Client/Atmos/EntitySystems/GasPressureRegulatorSystem.cs b/Content.Client/Atmos/EntitySystems/GasPressureRegulatorSystem.cs
new file mode 100644
index 0000000000..6f12297ff0
--- /dev/null
+++ b/Content.Client/Atmos/EntitySystems/GasPressureRegulatorSystem.cs
@@ -0,0 +1,31 @@
+using Content.Shared.Atmos.EntitySystems;
+using Content.Shared.Atmos.Piping.Binary.Components;
+
+namespace Content.Client.Atmos.EntitySystems;
+
+///
+/// Represents the client system responsible for managing and updating the gas pressure regulator interface.
+/// Inherits from the shared system .
+///
+public sealed partial class GasPressureRegulatorSystem : SharedGasPressureRegulatorSystem
+{
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent(OnValveUpdate);
+ }
+
+ private void OnValveUpdate(Entity ent, ref AfterAutoHandleStateEvent args)
+ {
+ UpdateUi(ent);
+ }
+
+ protected override void UpdateUi(Entity ent)
+ {
+ if (UserInterfaceSystem.TryGetOpenUi(ent.Owner, GasPressureRegulatorUiKey.Key, out var bui))
+ {
+ bui.Update();
+ }
+ }
+}
diff --git a/Content.Client/Atmos/UI/GasPressureRegulatorBoundUserInterface.cs b/Content.Client/Atmos/UI/GasPressureRegulatorBoundUserInterface.cs
new file mode 100644
index 0000000000..9d66a9985f
--- /dev/null
+++ b/Content.Client/Atmos/UI/GasPressureRegulatorBoundUserInterface.cs
@@ -0,0 +1,58 @@
+using Content.Shared.Atmos.Piping.Binary.Components;
+using Content.Shared.IdentityManagement;
+using Content.Shared.Localizations;
+using Robust.Client.UserInterface;
+
+namespace Content.Client.Atmos.UI;
+
+public sealed class GasPressureRegulatorBoundUserInterface(EntityUid owner, Enum uiKey)
+ : BoundUserInterface(owner, uiKey)
+{
+ private GasPressureRegulatorWindow? _window;
+
+ protected override void Open()
+ {
+ base.Open();
+
+ _window = this.CreateWindow();
+
+ _window.SetEntity(Owner);
+
+ _window.ThresholdPressureChanged += OnThresholdChanged;
+
+ if (EntMan.TryGetComponent(Owner, out GasPressureRegulatorComponent? comp))
+ _window.SetThresholdPressureInput(comp.Threshold);
+
+ Update();
+ }
+
+ public override void Update()
+ {
+ if (_window == null)
+ return;
+
+ _window.Title = Identity.Name(Owner, EntMan);
+
+ if (!EntMan.TryGetComponent(Owner, out GasPressureRegulatorComponent? comp))
+ return;
+
+ _window.SetThresholdPressureLabel(comp.Threshold);
+ _window.UpdateInfo(comp.InletPressure, comp.OutletPressure, comp.FlowRate);
+ }
+
+ private void OnThresholdChanged(string newThreshold)
+ {
+ var sentThreshold = 0f;
+
+ if (UserInputParser.TryFloat(newThreshold, out var parsedNewThreshold) && parsedNewThreshold >= 0 &&
+ !float.IsInfinity(parsedNewThreshold))
+ {
+ sentThreshold = parsedNewThreshold;
+ }
+
+ // Autofill to zero if the user inputs an invalid value.
+ _window?.SetThresholdPressureInput(sentThreshold);
+
+ SendPredictedMessage(new GasPressureRegulatorChangeThresholdMessage(sentThreshold));
+ }
+}
diff --git a/Content.Client/Atmos/UI/GasPressureRegulatorWindow.xaml b/Content.Client/Atmos/UI/GasPressureRegulatorWindow.xaml
new file mode 100644
index 0000000000..b6a55f769e
--- /dev/null
+++ b/Content.Client/Atmos/UI/GasPressureRegulatorWindow.xaml
@@ -0,0 +1,96 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Content.Client/Atmos/UI/GasPressureRegulatorWindow.xaml.cs b/Content.Client/Atmos/UI/GasPressureRegulatorWindow.xaml.cs
new file mode 100644
index 0000000000..a7c0570bac
--- /dev/null
+++ b/Content.Client/Atmos/UI/GasPressureRegulatorWindow.xaml.cs
@@ -0,0 +1,129 @@
+using System.Globalization;
+using Content.Client.UserInterface.Controls;
+using Robust.Client.AutoGenerated;
+using Robust.Client.UserInterface.XAML;
+using Robust.Shared.Timing;
+
+namespace Content.Client.Atmos.UI;
+
+///
+/// Client-side UI for controlling a pressure regulator.
+///
+[GenerateTypedNameReferences]
+public sealed partial class GasPressureRegulatorWindow : FancyWindow
+{
+ private float _flowRate;
+
+ public GasPressureRegulatorWindow()
+ {
+ RobustXamlLoader.Load(this);
+
+ ThresholdInput.OnTextChanged += _ => SetThresholdButton.Disabled = false;
+ SetThresholdButton.OnPressed += _ =>
+ {
+ ThresholdPressureChanged?.Invoke(ThresholdInput.Text ??= "");
+ SetThresholdButton.Disabled = true;
+ };
+
+ SetToCurrentPressureButton.OnPressed += _ =>
+ {
+ if (InletPressureLabel.Text != null)
+ {
+ ThresholdInput.Text = InletPressureLabel.Text;
+ }
+
+ SetThresholdButton.Disabled = false;
+ };
+
+ ZeroThresholdButton.OnPressed += _ =>
+ {
+ ThresholdInput.Text = "0";
+ SetThresholdButton.Disabled = false;
+ };
+
+ Add1000Button.OnPressed += _ => AdjustThreshold(1000);
+ Add100Button.OnPressed += _ => AdjustThreshold(100);
+ Add10Button.OnPressed += _ => AdjustThreshold(10);
+ Subtract10Button.OnPressed += _ => AdjustThreshold(-10);
+ Subtract100Button.OnPressed += _ => AdjustThreshold(-100);
+ Subtract1000Button.OnPressed += _ => AdjustThreshold(-1000);
+ return;
+
+ void AdjustThreshold(float adjustment)
+ {
+ if (float.TryParse(ThresholdInput.Text, out var currentValue))
+ {
+ ThresholdInput.Text = (currentValue + adjustment).ToString(CultureInfo.CurrentCulture);
+ SetThresholdButton.Disabled = false;
+ }
+ }
+ }
+
+ public event Action? ThresholdPressureChanged;
+
+ ///
+ /// Sets the current threshold pressure label. This is not setting the threshold input box.
+ ///
+ /// Threshold to set.
+ public void SetThresholdPressureLabel(float threshold)
+ {
+ TargetPressureLabel.Text = threshold.ToString(CultureInfo.CurrentCulture);
+ }
+
+ ///
+ /// Sets the threshold pressure input field with the given value.
+ /// When the client opens the UI the field will be autofilled with the current threshold pressure.
+ ///
+ /// The threshold pressure value to autofill into the input field.
+ public void SetThresholdPressureInput(float input)
+ {
+ ThresholdInput.Text = input.ToString(CultureInfo.CurrentCulture);
+ }
+
+ ///
+ /// Sets the entity to be visible in the UI.
+ ///
+ ///
+ public void SetEntity(EntityUid entity)
+ {
+ EntityView.SetEntity(entity);
+ }
+
+ ///
+ /// Updates the UI for the labels.
+ ///
+ /// The current pressure at the valve's inlet.
+ /// The current pressure at the valve's outlet.
+ /// The current flow rate through the valve.
+ public void UpdateInfo(float inletPressure, float outletPressure, float flowRate)
+ {
+ if (float.TryParse(TargetPressureLabel.Text, out var parsedfloat))
+ ToTargetBar.Value = inletPressure / parsedfloat;
+
+ InletPressureLabel.Text = float.Round(inletPressure).ToString(CultureInfo.CurrentCulture);
+ OutletPressureLabel.Text = float.Round(outletPressure).ToString(CultureInfo.CurrentCulture);
+ CurrentFlowLabel.Text = float.IsNaN(flowRate) ? "0" : flowRate.ToString(CultureInfo.CurrentCulture);
+ _flowRate = flowRate;
+ }
+
+ protected override void FrameUpdate(FrameEventArgs args)
+ {
+ base.FrameUpdate(args);
+
+ // Defines the flow rate at which the progress bar fills in one second.
+ // If the flow rate is >50 L/s, the bar will take <1 second to fill.
+ // If the flow rate is <50 L/s, the bar will take >1 second to fill.
+ const int barFillPerSecond = 50;
+
+ var maxValue = FlowRateBar.MaxValue;
+
+ // Increment the progress bar value based on elapsed time
+ FlowRateBar.Value += (_flowRate / barFillPerSecond) * args.DeltaSeconds;
+
+ // Reset the progress bar when it is fully filled
+ if (FlowRateBar.Value >= maxValue)
+ {
+ FlowRateBar.Value = 0f;
+ }
+ }
+}
diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Gases.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Gases.cs
index f38ec3fef6..6893940a97 100644
--- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Gases.cs
+++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Gases.cs
@@ -252,6 +252,128 @@ namespace Content.Server.Atmos.EntitySystems
Merge(destination, buffer);
}
+ ///
+ /// Calculates the dimensionless fraction of gas required to equalize pressure between two gas mixtures.
+ ///
+ /// The first gas mixture involved in the pressure equalization.
+ /// This mixture should be the one you always expect to be the highest pressure.
+ /// The second gas mixture involved in the pressure equalization.
+ /// A float (from 0 to 1) representing the dimensionless fraction of gas that needs to be transferred from the
+ /// mixture of higher pressure to the mixture of lower pressure.
+ ///
+ ///
+ /// This properly takes into account the effect
+ /// of gas merging from inlet to outlet affecting the temperature
+ /// (and possibly increasing the pressure) in the outlet.
+ ///
+ ///
+ /// The gas is assumed to expand freely,
+ /// so the temperature of the gas with the greater pressure is not changing.
+ ///
+ ///
+ ///
+ /// If you want to calculate the moles required to equalize pressure between an inlet and an outlet,
+ /// multiply the fraction returned by the source moles.
+ ///
+ public float FractionToEqualizePressure(GasMixture gasMixture1, GasMixture gasMixture2)
+ {
+ /*
+ Problem: the gas being merged from the inlet to the outlet could affect the
+ temp. of the gas and cause a pressure rise.
+ We want the pressure to be equalized, so we have to account for this.
+
+ For clarity, let's assume that gasMixture1 is the inlet and gasMixture2 is the outlet.
+
+ We require mechanical equilibrium, so \( P_1' = P_2' \)
+
+ Before the transfer, we have:
+ \( P_1 = \frac{n_1 R T_1}{V_1} \)
+ \( P_2 = \frac{n_2 R T_2}{V_2} \)
+
+ After removing fraction \( x \) moles from the inlet, we have:
+ \( P_1' = \frac{(1 - x) n_1 R T_1}{V_1} \)
+
+ The outlet will gain the same \( x n_1 \) moles of gas.
+ So \( n_2' = n_2 + x n_1 \)
+
+ After mixing, the outlet temperature will be changed.
+ Denote the new mixture temperature as \( T_2' \).
+ Volume is constant.
+ So we have:
+ \( P_2' = \frac{(n_2 + x n_1) R T_2}{V_2} \)
+
+ The total energy of the incoming inlet to outlet gas at \( T_1 \) plus the existing energy of the outlet gas at \( T_2 \)
+ will be equal to the energy of the new outlet gas at \( T_2' \).
+ This leads to the following derivation:
+ \( x n_1 C_1 T_1 + n_2 C_2 T_2 = (x n_1 C_1 + n_2 C_2) T_2' \)
+
+ Where \( C_1 \) and \( C_2 \) are the heat capacities of the inlet and outlet gases, respectively.
+
+ Solving for \( T_2' \) gives us:
+ \( T_2' = \frac{x n_1 C_1 T_1 + n_2 C_2 T_2}{x n_1 C_1 + n_2 C_2} \)
+
+ Once again, we require mechanical equilibrium (\( P_1' = P_2' \)),
+ so we can substitute \( T_2' \) into the pressure equation:
+
+ \( \frac{(1 - x) n_1 R T_1}{V_1} =
+ \frac{(n_2 + x n_1) R}{V_2} \cdot
+ \frac{x n_1 C_1 T_1 + n_2 C_2 T_2}
+ {x n_1 C_1 + n_2 C_2} \)
+
+ Now it's a matter of solving for \( x \).
+ Not going to show the full derivation here, just steps.
+ 1. Cancel common factor \( R \).
+ 2. Multiply both sides by \( x n_1 C_1 + n_2 C_2 \), so that everything
+ becomes a polynomial in terms of \( x \).
+ 3. Expand both sides.
+ 4. Collect like powers of \( x \).
+ 5. After collecting, you should end up with a polynomial of the form:
+
+ \( (-n_1 C_1 T_1 (1 + \frac{V_2}{V_1})) x^2 +
+ (n_1 T_1 \frac{V_2}{V_1} (C_1 - C_2) - n_2 C_1 T_1 - n_1 C_2 T_2) x +
+ (n_1 T_1 \frac{V_2}{V_1} C_2 - n_2 C_2 T_2) = 0 \)
+
+ Divide through by \( n_1 C_1 T_1 \) and replace each ratio with a symbol for clarity:
+ \( k_V = \frac{V_2}{V_1} \)
+ \( k_n = \frac{n_2}{n_1} \)
+ \( k_T = \frac{T_2}{T_1} \)
+ \( k_C = \frac{C_2}{C_1} \)
+ */
+
+ // Ensure that P_1 > P_2 so the quadratic works out.
+ if (gasMixture1.Pressure < gasMixture2.Pressure)
+ {
+ (gasMixture1, gasMixture2) = (gasMixture2, gasMixture1);
+ }
+
+ // Establish the dimensionless ratios.
+ var volumeRatio = gasMixture2.Volume / gasMixture1.Volume;
+ var molesRatio = gasMixture2.TotalMoles / gasMixture1.TotalMoles;
+ var temperatureRatio = gasMixture2.Temperature / gasMixture1.Temperature;
+ var heatCapacityRatio = GetHeatCapacity(gasMixture2) / GetHeatCapacity(gasMixture1);
+
+ // The quadratic equation is solved for the transfer fraction.
+ var quadraticA = 1 + volumeRatio;
+ var quadraticB = molesRatio - volumeRatio + heatCapacityRatio * (temperatureRatio + volumeRatio);
+ var quadraticC = heatCapacityRatio * (molesRatio * temperatureRatio - volumeRatio);
+
+ return (-quadraticB + MathF.Sqrt(quadraticB * quadraticB - 4 * quadraticA * quadraticC)) / (2 * quadraticA);
+ }
+
+ ///
+ /// Determines the number of moles that need to be removed from a to reach a target pressure threshold.
+ ///
+ /// The gas mixture whose moles and properties will be used in the calculation.
+ /// The target pressure threshold to calculate against.
+ /// The difference in moles required to reach the target pressure threshold.
+ /// The temperature of the gas is assumed to be not changing due to a free expansion.
+ public static float MolesToPressureThreshold(GasMixture gasMixture, float targetPressure)
+ {
+ // Kid named PV = nRT.
+ return gasMixture.TotalMoles -
+ targetPressure * gasMixture.Volume / (Atmospherics.R * gasMixture.Temperature);
+ }
+
///
/// Checks whether a gas mixture is probably safe.
/// This only checks temperature and pressure, not gas composition.
diff --git a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasPressureRegulatorSystem.cs b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasPressureRegulatorSystem.cs
new file mode 100644
index 0000000000..076d5adf8e
--- /dev/null
+++ b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasPressureRegulatorSystem.cs
@@ -0,0 +1,202 @@
+using Content.Server.Atmos.EntitySystems;
+using Content.Server.Atmos.Piping.Components;
+using Content.Server.NodeContainer.EntitySystems;
+using Content.Server.NodeContainer.Nodes;
+using Content.Shared.Atmos;
+using Content.Shared.Atmos.EntitySystems;
+using Content.Shared.Atmos.Piping;
+using Content.Shared.Atmos.Piping.Binary.Components;
+using Content.Shared.Audio;
+using JetBrains.Annotations;
+using Robust.Shared.Timing;
+
+namespace Content.Server.Atmos.Piping.Binary.EntitySystems;
+
+///
+/// Handles serverside logic for pressure regulators. Gas will only flow through the regulator
+/// if the pressure on the inlet side is over a certain pressure threshold.
+/// See https://en.wikipedia.org/wiki/Pressure_regulator
+///
+[UsedImplicitly]
+public sealed class GasPressureRegulatorSystem : SharedGasPressureRegulatorSystem
+{
+ [Dependency] private readonly SharedAmbientSoundSystem _ambientSound = default!;
+ [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
+ [Dependency] private readonly AtmosphereSystem _atmosphere = default!;
+ [Dependency] private readonly NodeContainerSystem _nodeContainer = default!;
+ [Dependency] private readonly IGameTiming _timing = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent(OnInit);
+ SubscribeLocalEvent(OnPressureRegulatorUpdated);
+ SubscribeLocalEvent(OnMapInit);
+ }
+
+ private void OnMapInit(Entity ent, ref MapInitEvent args)
+ {
+ ent.Comp.NextUiUpdate = _timing.CurTime + ent.Comp.UpdateInterval;
+ }
+
+ ///
+ /// Dirties the regulator every second or so, so that the UI can update.
+ /// The UI automatically updates after an AutoHandleStateEvent.
+ ///
+ ///
+ public override void Update(float frameTime)
+ {
+ base.Update(frameTime);
+
+ var query = EntityQueryEnumerator();
+
+ while (query.MoveNext(out var uid, out var comp))
+ {
+ if (comp.NextUiUpdate > _timing.CurTime)
+ continue;
+
+ comp.NextUiUpdate += comp.UpdateInterval;
+
+ DirtyFields(uid,
+ comp,
+ null,
+ nameof(comp.InletPressure),
+ nameof(comp.OutletPressure),
+ nameof(comp.FlowRate));
+ }
+ }
+
+ private void OnInit(Entity ent, ref ComponentInit args)
+ {
+ UpdateAppearance(ent);
+ }
+
+ ///
+ /// Handles the updating logic for the pressure regulator.
+ ///
+ /// the of the pressure regulator
+ /// Args provided to us via
+ private void OnPressureRegulatorUpdated(Entity ent,
+ ref AtmosDeviceUpdateEvent args)
+ {
+ if (!_nodeContainer.TryGetNodes(ent.Owner,
+ ent.Comp.InletName,
+ ent.Comp.OutletName,
+ out PipeNode? inletPipeNode,
+ out PipeNode? outletPipeNode))
+ {
+ ChangeStatus(false, ent, inletPipeNode, outletPipeNode, 0);
+ return;
+ }
+
+ /*
+ It's time for some math! :)
+
+ Gas is simply transferred from the inlet to the outlet, restricted by flow rate and pressure.
+ We want to transfer enough gas to bring the inlet pressure below the threshold,
+ and only as much as our max flow rate allows.
+
+ The equations:
+ PV = nRT
+ P1 = P2
+
+ Can be used to calculate the amount of gas we need to transfer.
+ */
+
+ var p1 = inletPipeNode.Air.Pressure;
+ var p2 = outletPipeNode.Air.Pressure;
+
+ if (p1 <= ent.Comp.Threshold || p2 >= p1)
+ {
+ ChangeStatus(false, ent, inletPipeNode, outletPipeNode, 0);
+ return;
+ }
+
+ var t1 = inletPipeNode.Air.Temperature;
+
+ // First, calculate the amount of gas we need to transfer to bring us below the threshold.
+ var deltaMolesToPressureThreshold =
+ AtmosphereSystem.MolesToPressureThreshold(inletPipeNode.Air, ent.Comp.Threshold);
+
+ // Second, calculate the moles required to equalize the pressure.
+ // We round here to avoid the valve staying enabled for 0.00001 pressure differences.
+ var deltaMolesToEqualizePressure =
+ float.Round(_atmosphere.FractionToEqualizePressure(inletPipeNode.Air, outletPipeNode.Air) *
+ inletPipeNode.Air.TotalMoles,
+ 1,
+ MidpointRounding.ToPositiveInfinity);
+
+ // Third, make sure we only transfer the minimum of the two.
+ // We do this so that we don't accidentally transfer so much gas to the point
+ // where the outlet pressure is higher than the inlet.
+ var deltaMolesToTransfer = Math.Min(deltaMolesToPressureThreshold, deltaMolesToEqualizePressure);
+
+ // Fourth, convert to the desired volume to transfer.
+ var desiredVolumeToTransfer = deltaMolesToTransfer * ((Atmospherics.R * t1) / p1);
+
+ // And finally, limit the transfer volume to the max flow rate of the valve.
+ var actualVolumeToTransfer = Math.Min(desiredVolumeToTransfer,
+ ent.Comp.MaxTransferRate * _atmosphere.PumpSpeedup() * args.dt);
+
+ // We remove the gas from the inlet and merge it into the outlet.
+ var removed = inletPipeNode.Air.RemoveVolume(actualVolumeToTransfer);
+ _atmosphere.Merge(outletPipeNode.Air, removed);
+
+ // Calculate the flow rate in L/s for the UI.
+ var sentFlowRate = MathF.Round(actualVolumeToTransfer / args.dt, 1);
+
+ ChangeStatus(true, ent, inletPipeNode, outletPipeNode, sentFlowRate);
+ }
+
+ ///
+ /// Updates the visual appearance of the pressure regulator based on its current state.
+ ///
+ /// The
+ /// representing the pressure regulator with respective components.
+ private void UpdateAppearance(Entity ent)
+ {
+ _appearance.SetData(ent,
+ PressureRegulatorVisuals.State,
+ ent.Comp.Enabled);
+ }
+
+ ///
+ /// Updates the pressure regulator's appearance and sound based on its current state, while
+ /// also preventing network spamming.
+ /// Also prepares data for dirtying.
+ ///
+ /// The new state to set
+ /// The pressure regulator to update
+ /// The inlet node of the pressure regulator
+ /// The outlet node of the pressure regulator
+ /// Current flow rate of the pressure regulator
+ private void ChangeStatus(bool enabled,
+ Entity ent,
+ PipeNode? inletNode,
+ PipeNode? outletNode,
+ float flowRate)
+ {
+ // First, set data on the component server-side.
+ ent.Comp.InletPressure = inletNode?.Air.Pressure ?? 0f;
+ ent.Comp.OutletPressure = outletNode?.Air.Pressure ?? 0f;
+ ent.Comp.FlowRate = flowRate;
+
+ // We need to prevent spamming the network with updates, so only check if we've
+ // switched states.
+ if (ent.Comp.Enabled == enabled)
+ return;
+
+ ent.Comp.Enabled = enabled;
+ _ambientSound.SetAmbience(ent, enabled);
+ UpdateAppearance(ent);
+
+ // The regulator has changed state, so we need to dirty all applicable fields *right now* so the UI updates
+ // at the same time as everything else.
+ DirtyFields(ent.AsNullable(),
+ null,
+ nameof(ent.Comp.InletPressure),
+ nameof(ent.Comp.OutletPressure),
+ nameof(ent.Comp.FlowRate));
+ }
+}
diff --git a/Content.Shared/Atmos/EntitySystems/SharedGasPressureRegulatorSystem.cs b/Content.Shared/Atmos/EntitySystems/SharedGasPressureRegulatorSystem.cs
new file mode 100644
index 0000000000..6e2d3e38ce
--- /dev/null
+++ b/Content.Shared/Atmos/EntitySystems/SharedGasPressureRegulatorSystem.cs
@@ -0,0 +1,70 @@
+using Content.Shared.Administration.Logs;
+using Content.Shared.Atmos.Components;
+using Content.Shared.Atmos.Piping.Binary.Components;
+using Content.Shared.Database;
+using Content.Shared.Examine;
+
+namespace Content.Shared.Atmos.EntitySystems;
+
+///
+/// Handles all shared interactions with the gas pressure regulator.
+///
+public abstract class SharedGasPressureRegulatorSystem : EntitySystem
+{
+ [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
+ [Dependency] protected readonly SharedUserInterfaceSystem UserInterfaceSystem = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent(OnExamined);
+ SubscribeLocalEvent(
+ OnThresholdChangeMessage);
+ }
+
+ ///
+ /// Presents predicted examine information to the person examining the valve.
+ ///
+ /// of the valve
+ /// Event arguments for examination
+ private void OnExamined(Entity ent, ref ExaminedEvent args)
+ {
+ if (!Transform(ent).Anchored || !args.IsInDetailsRange)
+ return;
+
+ using (args.PushGroup(nameof(GasPressureRegulatorComponent)))
+ {
+ args.PushMarkup(Loc.GetString("gas-pressure-regulator-system-examined",
+ ("statusColor", ent.Comp.Enabled ? "green" : "red"),
+ ("open", ent.Comp.Enabled)));
+
+ args.PushMarkup(Loc.GetString("gas-pressure-regulator-examined-threshold-pressure",
+ ("threshold", $"{ent.Comp.Threshold:0.#}")));
+
+ args.PushMarkup(Loc.GetString("gas-pressure-regulator-examined-flow-rate",
+ ("flowRate", $"{ent.Comp.FlowRate:0.#}")));
+ }
+ }
+
+ ///
+ /// Validates, logs, and updates the pressure threshold of the valve.
+ ///
+ /// The of the valve.
+ /// The received pressure from the message.
+ private void OnThresholdChangeMessage(Entity ent,
+ ref GasPressureRegulatorChangeThresholdMessage args)
+ {
+ ent.Comp.Threshold = Math.Max(0f, args.ThresholdPressure);
+ _adminLogger.Add(LogType.AtmosVolumeChanged,
+ LogImpact.Medium,
+ $"{ToPrettyString(args.Actor):player} set the pressure threshold on {ToPrettyString(ent):device} to {ent.Comp.Threshold}");
+ // Dirty the entire entity to ensure we get all of that Fresh:tm: UI info from the server.
+ Dirty(ent);
+ UpdateUi(ent);
+ }
+
+ protected virtual void UpdateUi(Entity ent)
+ {
+ }
+}
diff --git a/Content.Shared/Atmos/Piping/Binary/Components/GasPressureRegulatorComponent.cs b/Content.Shared/Atmos/Piping/Binary/Components/GasPressureRegulatorComponent.cs
new file mode 100644
index 0000000000..2e893d28fc
--- /dev/null
+++ b/Content.Shared/Atmos/Piping/Binary/Components/GasPressureRegulatorComponent.cs
@@ -0,0 +1,88 @@
+using Content.Shared.Guidebook;
+using Robust.Shared.GameStates;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
+
+namespace Content.Shared.Atmos.Piping.Binary.Components;
+
+///
+/// Defines a gas pressure regulator,
+/// which releases gas depending on a set pressure threshold between two pipe nodes.
+///
+[RegisterComponent, NetworkedComponent]
+[AutoGenerateComponentState(true, true), AutoGenerateComponentPause]
+public sealed partial class GasPressureRegulatorComponent : Component
+{
+ ///
+ /// Determines whether the valve is open or closed.
+ /// Used for showing the valve animation, the UI,
+ /// and on examine.
+ ///
+ [DataField, AutoNetworkedField]
+ public bool Enabled;
+
+ ///
+ /// Specifies the pipe node name to be treated as the inlet.
+ ///
+ [DataField]
+ public string InletName = "inlet";
+
+ ///
+ /// Specifies the pipe node name to be treated as the outlet.
+ ///
+ [DataField]
+ public string OutletName = "outlet";
+
+ ///
+ /// The max transfer rate of the pressure regulator.
+ ///
+ [GuidebookData]
+ [DataField]
+ public float MaxTransferRate = Atmospherics.MaxTransferRate;
+
+ ///
+ /// The server time at which the next UI update will be sent.
+ ///
+ [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
+ [AutoPausedField]
+ public TimeSpan NextUiUpdate = TimeSpan.Zero;
+
+ ///
+ /// Sets the opening threshold of the pressure regulator.
+ ///
+ /// If set to 500 kPa, the regulator will only
+ /// open if the pressure in the inlet side is above
+ /// 500 kPa.
+ [DataField, AutoNetworkedField]
+ public float Threshold;
+
+ ///
+ /// How often the UI update is sent.
+ ///
+ [DataField]
+ public TimeSpan UpdateInterval = TimeSpan.FromSeconds(1);
+
+ #region UI/Examine Info
+
+ ///
+ /// The current flow rate of the pressure regulator.
+ ///
+ [ViewVariables(VVAccess.ReadOnly)]
+ [DataField, AutoNetworkedField]
+ public float FlowRate;
+
+ ///
+ /// Current inlet pressure the pressure regulator.
+ ///
+ [ViewVariables(VVAccess.ReadOnly)]
+ [DataField, AutoNetworkedField]
+ public float InletPressure;
+
+ ///
+ /// Current outlet pressure of the pressure regulator.
+ ///
+ [ViewVariables(VVAccess.ReadOnly)]
+ [DataField, AutoNetworkedField]
+ public float OutletPressure;
+
+ #endregion
+}
diff --git a/Content.Shared/Atmos/Piping/Binary/Components/SharedGasPressureRegulatorComponent.cs b/Content.Shared/Atmos/Piping/Binary/Components/SharedGasPressureRegulatorComponent.cs
new file mode 100644
index 0000000000..74ca041d10
--- /dev/null
+++ b/Content.Shared/Atmos/Piping/Binary/Components/SharedGasPressureRegulatorComponent.cs
@@ -0,0 +1,25 @@
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.Atmos.Piping.Binary.Components;
+
+///
+/// Represents the unique key for the UI.
+///
+[Serializable, NetSerializable]
+public enum GasPressureRegulatorUiKey : byte
+{
+ Key,
+}
+
+///
+/// Message sent to change the pressure threshold of the gas pressure regulator.
+///
+/// The new pressure threshold value.
+[Serializable, NetSerializable]
+public sealed class GasPressureRegulatorChangeThresholdMessage(float pressure) : BoundUserInterfaceMessage
+{
+ ///
+ /// Gets the new threshold pressure value.
+ ///
+ public float ThresholdPressure { get; } = pressure;
+}
diff --git a/Content.Shared/Atmos/Piping/EnabledAtmosDeviceVisuals.cs b/Content.Shared/Atmos/Piping/EnabledAtmosDeviceVisuals.cs
index dfc7b7a386..5193afcdd9 100644
--- a/Content.Shared/Atmos/Piping/EnabledAtmosDeviceVisuals.cs
+++ b/Content.Shared/Atmos/Piping/EnabledAtmosDeviceVisuals.cs
@@ -31,4 +31,10 @@ namespace Content.Shared.Atmos.Piping
{
Enabled,
}
+
+ [Serializable, NetSerializable]
+ public enum PressureRegulatorVisuals : byte
+ {
+ State,
+ }
}
diff --git a/Resources/Locale/en-US/atmos/gas-pressure-regulator-system.ftl b/Resources/Locale/en-US/atmos/gas-pressure-regulator-system.ftl
new file mode 100644
index 0000000000..0f30f3d75d
--- /dev/null
+++ b/Resources/Locale/en-US/atmos/gas-pressure-regulator-system.ftl
@@ -0,0 +1,7 @@
+# Examine Text
+gas-pressure-regulator-system-examined = The valve is [color={$statusColor}]{$open ->
+[true] open
+*[false] closed
+}[/color].
+gas-pressure-regulator-examined-threshold-pressure = The threshold pressure is set at [color=lightblue]{$threshold} kPa[/color].
+gas-pressure-regulator-examined-flow-rate = The flow rate meter indicates [color=lightblue]{$flowRate} L/s[/color].
diff --git a/Resources/Locale/en-US/components/gas-pressure-regulator-component.ftl b/Resources/Locale/en-US/components/gas-pressure-regulator-component.ftl
new file mode 100644
index 0000000000..2434e2df63
--- /dev/null
+++ b/Resources/Locale/en-US/components/gas-pressure-regulator-component.ftl
@@ -0,0 +1,19 @@
+# UI Labels
+gas-pressure-regulator-ui-set-threshold = Set
+gas-pressure-regulator-ui-zero-threshold = Zero
+gas-pressure-regulator-ui-set-to-current-pressure = Set to Inlet Pressure
+gas-pressure-regulator-ui-add-10 = +10
+gas-pressure-regulator-ui-add-100 = +100
+gas-pressure-regulator-ui-add-1000 = +1000
+gas-pressure-regulator-ui-subtract-10 = -10
+gas-pressure-regulator-ui-subtract-100 = -100
+gas-pressure-regulator-ui-subtract-1000 = -1000
+gas-pressure-regulator-ui-title = Inlet Pressure Regulator
+gas-pressure-regulator-ui-target = Setpoint
+gas-pressure-regulator-ui-flow = Flow
+gas-pressure-regulator-ui-outlet = Outlet
+gas-pressure-regulator-ui-inlet = Inlet
+
+# Units
+gas-pressure-regulator-ui-flow-rate-unit = L/s
+gas-pressure-regulator-ui-pressure-unit = kPa
diff --git a/Resources/Locale/en-US/guidebook/guides.ftl b/Resources/Locale/en-US/guidebook/guides.ftl
index 004b6b39a5..b55193fef6 100644
--- a/Resources/Locale/en-US/guidebook/guides.ftl
+++ b/Resources/Locale/en-US/guidebook/guides.ftl
@@ -20,6 +20,7 @@ guide-entry-manualvalve = Manual Valve
guide-entry-signalvalve = Signal Valve
guide-entry-pneumaticvalve = Pneumatic Valve
guide-entry-passivegate = Passive Gate
+guide-entry-ressureregulator = Pressure Regulator
guide-entry-mixingandfiltering = Mixing and Filtering
guide-entry-gascanisters = Gas Canisters
guide-entry-thermomachines = Thermomachines
diff --git a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/binary.yml b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/binary.yml
index 88940f4882..e9c5403805 100644
--- a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/binary.yml
+++ b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/binary.yml
@@ -155,6 +155,62 @@
guides:
- Pumps
+- type: entity
+ parent: GasBinaryBase
+ id: GasPressureRegulator
+ name: inlet pressure regulator
+ description: A valve that releases gas when the inlet pressure exceeds a certain threshold.
+ placement:
+ mode: SnapgridCenter
+ components:
+ - type: Rotatable
+ - type: Transform
+ noRot: false
+ - type: SubFloorHide
+ visibleLayers:
+ - enum.SubfloorLayers.FirstLayer
+ - type: Sprite
+ sprite: Structures/Piping/Atmospherics/pump.rsi
+ layers:
+ - sprite: Structures/Piping/Atmospherics/pipe.rsi
+ state: pipeStraight
+ map: [ "enum.PipeVisualLayers.Pipe" ]
+ - state: pumpPressureRegulator
+ map: [ "enum.SubfloorLayers.FirstLayer", "enabled" ]
+ - type: Appearance
+ - type: GenericVisualizer
+ visuals:
+ enum.PressureRegulatorVisuals.State:
+ enabled:
+ True: { state: pumpPressureRegulatorOn }
+ False: { state: pumpPressureRegulator }
+ - type: PipeColorVisuals
+ - type: GasPressureRegulator
+ enabled: false
+ threshold: 4500
+ - type: Construction
+ graph: GasBinary
+ node: pressureregulator
+ - type: ActivatableUI
+ key: enum.GasPressureRegulatorUiKey.Key
+ blockSpectators: true
+ - type: ActivatableUIRequiresAnchor
+ - type: UserInterface
+ interfaces:
+ enum.GasPressureRegulatorUiKey.Key:
+ type: GasPressureRegulatorBoundUserInterface
+ - type: AmbientSound
+ enabled: false
+ volume: -9
+ range: 5
+ sound:
+ path: /Audio/Ambience/Objects/gas_hiss.ogg
+ - type: AtmosMonitoringConsoleDevice
+ navMapBlip: GasFlowRegulator
+ - type: GuideHelp
+ guides:
+ - PressureRegulator
+
- type: entity
parent: GasBinaryBase
id: GasPassiveGate
diff --git a/Resources/Prototypes/Guidebook/engineering.yml b/Resources/Prototypes/Guidebook/engineering.yml
index e0803044dc..96e049a399 100644
--- a/Resources/Prototypes/Guidebook/engineering.yml
+++ b/Resources/Prototypes/Guidebook/engineering.yml
@@ -120,6 +120,7 @@
- SignalValve
- PneumaticValve
- PassiveGate
+ - PressureRegulator
- type: guideEntry
id: ManualValve
@@ -141,6 +142,11 @@
name: guide-entry-passivegate
text: "/ServerInfo/Guidebook/Engineering/PassiveGate.xml"
+- type: guideEntry
+ id: PressureRegulator
+ name: guide-entry-ressureregulator
+ text: "/ServerInfo/Guidebook/Engineering/PressureRegulator.xml"
+
- type: guideEntry
id: MixingAndFiltering
name: guide-entry-mixingandfiltering
diff --git a/Resources/Prototypes/Recipes/Construction/Graphs/utilities/atmos_binary.yml b/Resources/Prototypes/Recipes/Construction/Graphs/utilities/atmos_binary.yml
index aed9167178..6159bc5936 100644
--- a/Resources/Prototypes/Recipes/Construction/Graphs/utilities/atmos_binary.yml
+++ b/Resources/Prototypes/Recipes/Construction/Graphs/utilities/atmos_binary.yml
@@ -22,6 +22,12 @@
amount: 2
doAfter: 1
+ - to: pressureregulator
+ steps:
+ - material: Steel
+ amount: 2
+ doAfter: 1
+
- to: valve
steps:
- material: Steel
@@ -106,6 +112,22 @@
- tool: Welding
doAfter: 1
+ - node: pressureregulator
+ entity: GasPressureRegulator
+ edges:
+ - to: start
+ conditions:
+ - !type:EntityAnchored
+ anchored: false
+ completed:
+ - !type:SpawnPrototype
+ prototype: SheetSteel1
+ amount: 2
+ - !type:DeleteEntity
+ steps:
+ - tool: Welding
+ doAfter: 1
+
- node: valve
entity: GasValve
edges:
diff --git a/Resources/Prototypes/Recipes/Construction/utilities.yml b/Resources/Prototypes/Recipes/Construction/utilities.yml
index 85a0f5d3b1..f9c9867aac 100644
--- a/Resources/Prototypes/Recipes/Construction/utilities.yml
+++ b/Resources/Prototypes/Recipes/Construction/utilities.yml
@@ -572,6 +572,17 @@
conditions:
- !type:NoUnstackableInTile
+- type: construction
+ id: GasPressureRegulator
+ graph: GasBinary
+ startNode: start
+ targetNode: pressureregulator
+ category: construction-category-utilities
+ placementMode: SnapgridCenter
+ canBuildInImpassable: false
+ conditions:
+ - !type:NoUnstackableInTile
+
- type: construction
id: GasValve
graph: GasBinary
diff --git a/Resources/ServerInfo/Guidebook/Engineering/GasManipulation.xml b/Resources/ServerInfo/Guidebook/Engineering/GasManipulation.xml
index 3a3857ae4c..6070e3b985 100644
--- a/Resources/ServerInfo/Guidebook/Engineering/GasManipulation.xml
+++ b/Resources/ServerInfo/Guidebook/Engineering/GasManipulation.xml
@@ -49,6 +49,7 @@
+
## Valves
diff --git a/Resources/ServerInfo/Guidebook/Engineering/PressureRegulator.xml b/Resources/ServerInfo/Guidebook/Engineering/PressureRegulator.xml
new file mode 100644
index 0000000000..2b23ea5039
--- /dev/null
+++ b/Resources/ServerInfo/Guidebook/Engineering/PressureRegulator.xml
@@ -0,0 +1,23 @@
+
+ # Inlet Pressure Regulator
+ The Inlet Pressure Regulator is a passive device that allows gas to escape from a [textlink="pipenet" link="PipeNetworks"] when the pressure exceeds a certain threshold.
+
+
+
+
+
+ ## Operation
+ The valve will automatically [color=green]open[/color] when the pressure in the pipe exceeds the set threshold, allowing gas to escape to the connected output pipe.
+
+ The valve will [color=red]close[/color] again when the pressure drops below the set threshold.
+
+ The flow rate of the valve is limited to [color=orange][protodata="GasPressureRegulator" comp="GasPressureRegulator" member="MaxTransferRate"/] L/s[/color].
+
+ ## Example Uses
+ The valve is commonly used to prevent overpressure situations in gas systems, such as [textlink="TEG" link="TEG"] cooling loops and [textlink="pipes" link="Pipes"], which would cause a failure in the system (clogged [textlink="pumps" link="Pumps"]).
+
+ The valve can also be used to vent off ready-to-use, hot gas from a burn chamber.
+ For example, it may be undesirable to allow a burn chamber to drop below a specific pressure for a long time, as this may cause the gas to cool down too much and thin out, which would cause a flameout.
+
+ An inlet pressure regulator can be used to vent off excess gas, while keeping the pressure in the burn chamber above a certain threshold, which may help in sustaining a fire in the chamber.
+
diff --git a/Resources/ServerInfo/Guidebook/Engineering/Valves.xml b/Resources/ServerInfo/Guidebook/Engineering/Valves.xml
index db8c18807c..46f5b0f629 100644
--- a/Resources/ServerInfo/Guidebook/Engineering/Valves.xml
+++ b/Resources/ServerInfo/Guidebook/Engineering/Valves.xml
@@ -6,6 +6,7 @@
+
All valves do not require [textlink="power" link="Power"] to operate.
diff --git a/Resources/Textures/Structures/Piping/Atmospherics/pump.rsi/meta.json b/Resources/Textures/Structures/Piping/Atmospherics/pump.rsi/meta.json
index 632c7bcf5b..4dceb4af72 100644
--- a/Resources/Textures/Structures/Piping/Atmospherics/pump.rsi/meta.json
+++ b/Resources/Textures/Structures/Piping/Atmospherics/pump.rsi/meta.json
@@ -1,62 +1,165 @@
{
- "version":1,
- "size":{
- "x":32,
- "y":32
- },
- "license":"CC-BY-SA-3.0",
- "copyright":"Taken from https://github.com/tgstation/tgstation at commit 57cd1d59ca019dd0e7811ac451f295f818e573da. Signal valve is a digital valve modified by deltanedas. Manual valve modified by Deerstop at https://github.com/space-wizards/space-station-14/pull/34378",
- "states":[
- {
- "name":"pumpDigitalValve",
- "directions":4
- },
- {
- "name":"pumpManualValve",
- "directions":4
- },
- {
- "name":"pumpManualValveOn",
- "directions":4
- },
- {
- "name":"pumpSignalValve",
- "directions":4
- },
- {
- "name":"pumpSignalValveOn",
- "directions":4
- },
- {
- "name":"pumpPassiveGate",
- "directions":4
- },
- {
- "name":"pumpPassiveGateOn",
- "directions":4
- },
- {
- "name":"pumpPressure",
- "directions":4
- },
- {
- "name":"pumpPressureOn",
- "directions":4,
- "delays":[ [ 0.1, 0.1, 0.1, 0.1, 0.1 ], [ 0.1, 0.1, 0.1, 0.1, 0.1 ], [ 0.1, 0.1, 0.1, 0.1, 0.1 ], [ 0.1, 0.1, 0.1, 0.1, 0.1 ] ]
- },
- {
- "name":"pumpVolume",
- "directions":4
- },
- {
- "name":"pumpVolumeOn",
- "directions":4,
- "delays":[ [ 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1 ], [ 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1 ], [ 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1 ], [ 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1 ] ]
- },
- {
- "name":"pumpVolumeBlocked",
- "directions":4,
- "delays":[ [ 1.0, 1.0 ], [ 1.0, 1.0 ], [ 1.0, 1.0 ], [ 1.0, 1.0 ] ]
- }
- ]
+ "version": 1,
+ "license": "CC-BY-SA-3.0",
+ "copyright": "Taken from https://github.com/tgstation/tgstation at commit 57cd1d59ca019dd0e7811ac451f295f818e573da. Signal valve is a digital valve modified by deltanedas. Manual valve modified by Deerstop at https://github.com/space-wizards/space-station-14/pull/34378. pvalve taken from https://github.com/tgstation/tgstation/commit/584068b59e271c0108557902e8516c70d6ae56f2 and modified by ArtisticRoomba (GitHub)",
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "states": [
+ {
+ "name": "pumpDigitalValve",
+ "directions": 4
+ },
+ {
+ "name": "pumpManualValve",
+ "directions": 4
+ },
+ {
+ "name": "pumpManualValveOn",
+ "directions": 4
+ },
+ {
+ "name": "pumpSignalValve",
+ "directions": 4
+ },
+ {
+ "name": "pumpSignalValveOn",
+ "directions": 4
+ },
+ {
+ "name": "pumpPassiveGate",
+ "directions": 4
+ },
+ {
+ "name": "pumpPassiveGateOn",
+ "directions": 4
+ },
+ {
+ "name": "pumpPressure",
+ "directions": 4
+ },
+ {
+ "name": "pumpPressureOn",
+ "directions": 4,
+ "delays": [
+ [
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1
+ ],
+ [
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1
+ ],
+ [
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1
+ ],
+ [
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1
+ ]
+ ]
+ },
+ {
+ "name": "pumpVolume",
+ "directions": 4
+ },
+ {
+ "name": "pumpVolumeOn",
+ "directions": 4,
+ "delays": [
+ [
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1
+ ],
+ [
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1
+ ],
+ [
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1
+ ],
+ [
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1
+ ]
+ ]
+ },
+ {
+ "name": "pumpVolumeBlocked",
+ "directions": 4,
+ "delays": [
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ]
+ ]
+ },
+ {
+ "name": "pumpPressureRegulator",
+ "directions": 4
+ },
+ {
+ "name": "pumpPressureRegulatorOn",
+ "directions": 4
+ }
+ ]
}
diff --git a/Resources/Textures/Structures/Piping/Atmospherics/pump.rsi/pumpPressure.png b/Resources/Textures/Structures/Piping/Atmospherics/pump.rsi/pumpPressure.png
index 8903bc8564..eb35e1a42e 100644
Binary files a/Resources/Textures/Structures/Piping/Atmospherics/pump.rsi/pumpPressure.png and b/Resources/Textures/Structures/Piping/Atmospherics/pump.rsi/pumpPressure.png differ
diff --git a/Resources/Textures/Structures/Piping/Atmospherics/pump.rsi/pumpPressureRegulator.png b/Resources/Textures/Structures/Piping/Atmospherics/pump.rsi/pumpPressureRegulator.png
new file mode 100644
index 0000000000..cb7322d040
Binary files /dev/null and b/Resources/Textures/Structures/Piping/Atmospherics/pump.rsi/pumpPressureRegulator.png differ
diff --git a/Resources/Textures/Structures/Piping/Atmospherics/pump.rsi/pumpPressureRegulatorOn.png b/Resources/Textures/Structures/Piping/Atmospherics/pump.rsi/pumpPressureRegulatorOn.png
new file mode 100644
index 0000000000..8a8f85cec7
Binary files /dev/null and b/Resources/Textures/Structures/Piping/Atmospherics/pump.rsi/pumpPressureRegulatorOn.png differ
diff --git a/Resources/Textures/Structures/Piping/Atmospherics/pump_alt1.rsi/meta.json b/Resources/Textures/Structures/Piping/Atmospherics/pump_alt1.rsi/meta.json
index ef75896795..d7a2ccb3b6 100644
--- a/Resources/Textures/Structures/Piping/Atmospherics/pump_alt1.rsi/meta.json
+++ b/Resources/Textures/Structures/Piping/Atmospherics/pump_alt1.rsi/meta.json
@@ -1,11 +1,11 @@
{
"version": 1,
+ "license": "CC-BY-SA-3.0",
+ "copyright": "Taken from https://github.com/tgstation/tgstation at commit 57cd1d59ca019dd0e7811ac451f295f818e573da. Signal valve is a digital valve modified by deltanedas. Manual valve modified by Deerstop at https://github.com/space-wizards/space-station-14/pull/34378. Modified by chromiumboy. Modified by ArtisticRoomba.",
"size": {
"x": 32,
"y": 32
},
- "license": "CC-BY-SA-3.0",
- "copyright": "Taken from https://github.com/tgstation/tgstation at commit 57cd1d59ca019dd0e7811ac451f295f818e573da. Signal valve is a digital valve modified by deltanedas. Manual valve modified by Deerstop at https://github.com/space-wizards/space-station-14/pull/34378. Modified by chromiumboy.",
"states": [
{
"name": "pumpDigitalValve",
@@ -136,22 +136,30 @@
"directions": 4,
"delays": [
[
- 1.0,
- 1.0
+ 1,
+ 1
],
[
- 1.0,
- 1.0
+ 1,
+ 1
],
[
- 1.0,
- 1.0
+ 1,
+ 1
],
[
- 1.0,
- 1.0
+ 1,
+ 1
]
]
+ },
+ {
+ "name": "pumpPressureRegulator",
+ "directions": 4
+ },
+ {
+ "name": "pumpPressureRegulatorOn",
+ "directions": 4
}
]
}
diff --git a/Resources/Textures/Structures/Piping/Atmospherics/pump_alt1.rsi/pumpPressure.png b/Resources/Textures/Structures/Piping/Atmospherics/pump_alt1.rsi/pumpPressure.png
index 3a634fc929..94e5322cfc 100644
Binary files a/Resources/Textures/Structures/Piping/Atmospherics/pump_alt1.rsi/pumpPressure.png and b/Resources/Textures/Structures/Piping/Atmospherics/pump_alt1.rsi/pumpPressure.png differ
diff --git a/Resources/Textures/Structures/Piping/Atmospherics/pump_alt1.rsi/pumpPressureRegulator.png b/Resources/Textures/Structures/Piping/Atmospherics/pump_alt1.rsi/pumpPressureRegulator.png
new file mode 100644
index 0000000000..ee53abca29
Binary files /dev/null and b/Resources/Textures/Structures/Piping/Atmospherics/pump_alt1.rsi/pumpPressureRegulator.png differ
diff --git a/Resources/Textures/Structures/Piping/Atmospherics/pump_alt1.rsi/pumpPressureRegulatorOn.png b/Resources/Textures/Structures/Piping/Atmospherics/pump_alt1.rsi/pumpPressureRegulatorOn.png
new file mode 100644
index 0000000000..8523dbcba3
Binary files /dev/null and b/Resources/Textures/Structures/Piping/Atmospherics/pump_alt1.rsi/pumpPressureRegulatorOn.png differ
diff --git a/Resources/Textures/Structures/Piping/Atmospherics/pump_alt2.rsi/meta.json b/Resources/Textures/Structures/Piping/Atmospherics/pump_alt2.rsi/meta.json
index ef75896795..d7a2ccb3b6 100644
--- a/Resources/Textures/Structures/Piping/Atmospherics/pump_alt2.rsi/meta.json
+++ b/Resources/Textures/Structures/Piping/Atmospherics/pump_alt2.rsi/meta.json
@@ -1,11 +1,11 @@
{
"version": 1,
+ "license": "CC-BY-SA-3.0",
+ "copyright": "Taken from https://github.com/tgstation/tgstation at commit 57cd1d59ca019dd0e7811ac451f295f818e573da. Signal valve is a digital valve modified by deltanedas. Manual valve modified by Deerstop at https://github.com/space-wizards/space-station-14/pull/34378. Modified by chromiumboy. Modified by ArtisticRoomba.",
"size": {
"x": 32,
"y": 32
},
- "license": "CC-BY-SA-3.0",
- "copyright": "Taken from https://github.com/tgstation/tgstation at commit 57cd1d59ca019dd0e7811ac451f295f818e573da. Signal valve is a digital valve modified by deltanedas. Manual valve modified by Deerstop at https://github.com/space-wizards/space-station-14/pull/34378. Modified by chromiumboy.",
"states": [
{
"name": "pumpDigitalValve",
@@ -136,22 +136,30 @@
"directions": 4,
"delays": [
[
- 1.0,
- 1.0
+ 1,
+ 1
],
[
- 1.0,
- 1.0
+ 1,
+ 1
],
[
- 1.0,
- 1.0
+ 1,
+ 1
],
[
- 1.0,
- 1.0
+ 1,
+ 1
]
]
+ },
+ {
+ "name": "pumpPressureRegulator",
+ "directions": 4
+ },
+ {
+ "name": "pumpPressureRegulatorOn",
+ "directions": 4
}
]
}
diff --git a/Resources/Textures/Structures/Piping/Atmospherics/pump_alt2.rsi/pumpPressure.png b/Resources/Textures/Structures/Piping/Atmospherics/pump_alt2.rsi/pumpPressure.png
index cb5286e46c..a5961b0996 100644
Binary files a/Resources/Textures/Structures/Piping/Atmospherics/pump_alt2.rsi/pumpPressure.png and b/Resources/Textures/Structures/Piping/Atmospherics/pump_alt2.rsi/pumpPressure.png differ
diff --git a/Resources/Textures/Structures/Piping/Atmospherics/pump_alt2.rsi/pumpPressureRegulator.png b/Resources/Textures/Structures/Piping/Atmospherics/pump_alt2.rsi/pumpPressureRegulator.png
new file mode 100644
index 0000000000..dcca92daaa
Binary files /dev/null and b/Resources/Textures/Structures/Piping/Atmospherics/pump_alt2.rsi/pumpPressureRegulator.png differ
diff --git a/Resources/Textures/Structures/Piping/Atmospherics/pump_alt2.rsi/pumpPressureRegulatorOn.png b/Resources/Textures/Structures/Piping/Atmospherics/pump_alt2.rsi/pumpPressureRegulatorOn.png
new file mode 100644
index 0000000000..3f5eabda34
Binary files /dev/null and b/Resources/Textures/Structures/Piping/Atmospherics/pump_alt2.rsi/pumpPressureRegulatorOn.png differ