Make passive gates great (#9816)
This commit is contained in:
@@ -5,17 +5,6 @@ namespace Content.Server.Atmos.Piping.Binary.Components
|
|||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
public sealed class GasPassiveGateComponent : Component
|
public sealed class GasPassiveGateComponent : Component
|
||||||
{
|
{
|
||||||
[DataField("enabled")]
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public bool Enabled { get; set; } = true;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// This is the minimum difference needed to overcome the friction in the mechanism.
|
|
||||||
/// </summary>
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
[DataField("frictionDifference")]
|
|
||||||
public float FrictionPressureDifference { get; set; } = 10f;
|
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
[DataField("inlet")]
|
[DataField("inlet")]
|
||||||
public string InletName { get; set; } = "inlet";
|
public string InletName { get; set; } = "inlet";
|
||||||
@@ -24,8 +13,8 @@ namespace Content.Server.Atmos.Piping.Binary.Components
|
|||||||
[DataField("outlet")]
|
[DataField("outlet")]
|
||||||
public string OutletName { get; set; } = "outlet";
|
public string OutletName { get; set; } = "outlet";
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadOnly)]
|
||||||
[DataField("targetPressure")]
|
[DataField("flowRate")]
|
||||||
public float TargetPressure { get; set; } = Atmospherics.OneAtmosphere;
|
public float FlowRate { get; set; } = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using Content.Server.Atmos.Piping.Components;
|
|||||||
using Content.Server.NodeContainer;
|
using Content.Server.NodeContainer;
|
||||||
using Content.Server.NodeContainer.Nodes;
|
using Content.Server.NodeContainer.Nodes;
|
||||||
using Content.Shared.Atmos;
|
using Content.Shared.Atmos;
|
||||||
|
using Content.Shared.Examine;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
|
|
||||||
namespace Content.Server.Atmos.Piping.Binary.EntitySystems
|
namespace Content.Server.Atmos.Piping.Binary.EntitySystems
|
||||||
@@ -12,19 +13,18 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems
|
|||||||
public sealed class GasPassiveGateSystem : EntitySystem
|
public sealed class GasPassiveGateSystem : EntitySystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
|
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
|
||||||
|
[Dependency] private readonly ExamineSystemShared _examineSystem = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
SubscribeLocalEvent<GasPassiveGateComponent, AtmosDeviceUpdateEvent>(OnPassiveGateUpdated);
|
SubscribeLocalEvent<GasPassiveGateComponent, AtmosDeviceUpdateEvent>(OnPassiveGateUpdated);
|
||||||
|
SubscribeLocalEvent<GasPassiveGateComponent, ExaminedEvent>(OnExamined);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnPassiveGateUpdated(EntityUid uid, GasPassiveGateComponent gate, AtmosDeviceUpdateEvent args)
|
private void OnPassiveGateUpdated(EntityUid uid, GasPassiveGateComponent gate, AtmosDeviceUpdateEvent args)
|
||||||
{
|
{
|
||||||
if (!gate.Enabled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer))
|
if (!EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -32,23 +32,59 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems
|
|||||||
|| !nodeContainer.TryGetNode(gate.OutletName, out PipeNode? outlet))
|
|| !nodeContainer.TryGetNode(gate.OutletName, out PipeNode? outlet))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var outputStartingPressure = outlet.Air.Pressure;
|
var n1 = inlet.Air.TotalMoles;
|
||||||
var inputStartingPressure = inlet.Air.Pressure;
|
var n2 = outlet.Air.TotalMoles;
|
||||||
|
var P1 = inlet.Air.Pressure;
|
||||||
|
var P2 = outlet.Air.Pressure;
|
||||||
|
var V1 = inlet.Air.Volume;
|
||||||
|
var V2 = outlet.Air.Volume;
|
||||||
|
var T1 = inlet.Air.Temperature;
|
||||||
|
var T2 = outlet.Air.Temperature;
|
||||||
|
var pressureDelta = P1 - P2;
|
||||||
|
|
||||||
if (outputStartingPressure >= MathF.Min(gate.TargetPressure, inputStartingPressure - gate.FrictionPressureDifference))
|
float dt = 1/_atmosphereSystem.AtmosTickRate;
|
||||||
return; // No need to pump gas, target reached or input pressure too low.
|
float dV = 0;
|
||||||
|
var denom = (T1*V2 + T2*V1);
|
||||||
|
|
||||||
if (inlet.Air.TotalMoles > 0 && inlet.Air.Temperature > 0)
|
if (pressureDelta > 0 && P1 > 0 && denom > 0)
|
||||||
{
|
{
|
||||||
// We calculate the necessary moles to transfer using our good ol' friend PV=nRT.
|
// Calculate the number of moles to transfer to equalize the final pressure of
|
||||||
var pressureDelta = MathF.Min(gate.TargetPressure - outputStartingPressure, (inputStartingPressure - outputStartingPressure)/2);
|
// both sides of the valve. You can derive this equation yourself by solving
|
||||||
// We can't have a pressure delta that would cause outlet pressure > inlet pressure.
|
// the equations:
|
||||||
|
//
|
||||||
|
// P_inlet,final = P_outlet,final (pressure equilibrium)
|
||||||
|
// n_inlet,initial + n_outlet,initial = n_inlet,final + n_outlet,final (mass conservation)
|
||||||
|
//
|
||||||
|
// These simplifying assumptions allow an easy closed-form solution:
|
||||||
|
//
|
||||||
|
// T_inlet,initial = T_inlet,final
|
||||||
|
// T_outlet,initial = T_outlet,final
|
||||||
|
//
|
||||||
|
// If you don't want to push through the math, just know that this behaves like a
|
||||||
|
// pump that can equalize pressure instantly, i.e. much faster than pressure or
|
||||||
|
// volume pumps.
|
||||||
|
var transferMoles = n1 - (n1+n2)*T2*V1 / denom;
|
||||||
|
|
||||||
var transferMoles = pressureDelta * outlet.Air.Volume / (inlet.Air.Temperature * Atmospherics.R);
|
// Get the volume transfered to update our flow meter.
|
||||||
|
dV = n1*Atmospherics.R*T1/P1;
|
||||||
|
|
||||||
// Actually transfer the gas.
|
// Actually transfer the gas.
|
||||||
_atmosphereSystem.Merge(outlet.Air, inlet.Air.Remove(transferMoles));
|
_atmosphereSystem.Merge(outlet.Air, inlet.Air.Remove(transferMoles));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update transfer rate with an exponential moving average.
|
||||||
|
var tau = 1; // Time constant (averaging time) in seconds
|
||||||
|
var a = dt/tau;
|
||||||
|
gate.FlowRate = a*dV/tau + (1-a)*gate.FlowRate; // in L/sec
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnExamined(EntityUid uid, GasPassiveGateComponent gate, ExaminedEvent args)
|
||||||
|
{
|
||||||
|
if (!EntityManager.GetComponent<TransformComponent>(gate.Owner).Anchored || !args.IsInDetailsRange) // Not anchored? Out of range? No status.
|
||||||
|
return;
|
||||||
|
|
||||||
|
var str = Loc.GetString("gas-passive-gate-examined", ("flowRate", $"{gate.FlowRate:0.#}"));
|
||||||
|
args.PushMarkup(str);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
gas-passive-gate-examined = The flow rate meter indicates [color=lightblue]{$flowRate} liters/sec[/color].
|
||||||
Reference in New Issue
Block a user