Show volume on the gas analyzer (#25720)

The gas analyzer now shows the volume of pipes, tanks, canisters and the environment.

Adjust gas analyzers so that the volume and number of moles shown corresponds to only the scanned element, e.g. a canister or single pipe in a pipenet.
This commit is contained in:
slarticodefast
2024-04-17 19:42:24 +02:00
committed by GitHub
parent ef72d3cf7f
commit 5a5efa11cf
12 changed files with 126 additions and 49 deletions

View File

@@ -163,6 +163,26 @@ namespace Content.Client.Atmos.UI
parent.AddChild(panel); parent.AddChild(panel);
panel.AddChild(dataContainer); panel.AddChild(dataContainer);
// Volume label
var volBox = new BoxContainer { Orientation = BoxContainer.LayoutOrientation.Horizontal };
volBox.AddChild(new Label
{
Text = Loc.GetString("gas-analyzer-window-volume-text")
});
volBox.AddChild(new Control
{
MinSize = new Vector2(10, 0),
HorizontalExpand = true
});
volBox.AddChild(new Label
{
Text = Loc.GetString("gas-analyzer-window-volume-val-text", ("volume", $"{gasMix.Volume:0.##}")),
Align = Label.AlignMode.Right,
HorizontalExpand = true
});
dataContainer.AddChild(volBox);
// Pressure label // Pressure label
var presBox = new BoxContainer { Orientation = BoxContainer.LayoutOrientation.Horizontal }; var presBox = new BoxContainer { Orientation = BoxContainer.LayoutOrientation.Horizontal };

View File

@@ -132,7 +132,7 @@ namespace Content.Server.Atmos.EntitySystems
/// </summary> /// </summary>
private void OnDisabledMessage(EntityUid uid, GasAnalyzerComponent component, GasAnalyzerDisableMessage message) private void OnDisabledMessage(EntityUid uid, GasAnalyzerComponent component, GasAnalyzerDisableMessage message)
{ {
if (message.Session.AttachedEntity is not {Valid: true}) if (message.Session.AttachedEntity is not { Valid: true })
return; return;
DisableAnalyzer(uid, component); DisableAnalyzer(uid, component);
} }
@@ -169,7 +169,7 @@ namespace Content.Server.Atmos.EntitySystems
// Check if position is out of range => don't update and disable // Check if position is out of range => don't update and disable
if (!component.LastPosition.Value.InRange(EntityManager, _transform, userPos, SharedInteractionSystem.InteractionRange)) if (!component.LastPosition.Value.InRange(EntityManager, _transform, userPos, SharedInteractionSystem.InteractionRange))
{ {
if(component.User is { } userId && component.Enabled) if (component.User is { } userId && component.Enabled)
_popup.PopupEntity(Loc.GetString("gas-analyzer-shutoff"), userId, userId); _popup.PopupEntity(Loc.GetString("gas-analyzer-shutoff"), userId, userId);
DisableAnalyzer(uid, component, component.User); DisableAnalyzer(uid, component, component.User);
return false; return false;
@@ -182,13 +182,13 @@ namespace Content.Server.Atmos.EntitySystems
var tileMixture = _atmo.GetContainingMixture(uid, true); var tileMixture = _atmo.GetContainingMixture(uid, true);
if (tileMixture != null) if (tileMixture != null)
{ {
gasMixList.Add(new GasMixEntry(Loc.GetString("gas-analyzer-window-environment-tab-label"), tileMixture.Pressure, tileMixture.Temperature, gasMixList.Add(new GasMixEntry(Loc.GetString("gas-analyzer-window-environment-tab-label"), tileMixture.Volume, tileMixture.Pressure, tileMixture.Temperature,
GenerateGasEntryArray(tileMixture))); GenerateGasEntryArray(tileMixture)));
} }
else else
{ {
// No gases were found // No gases were found
gasMixList.Add(new GasMixEntry(Loc.GetString("gas-analyzer-window-environment-tab-label"), 0f, 0f)); gasMixList.Add(new GasMixEntry(Loc.GetString("gas-analyzer-window-environment-tab-label"), 0f, 0f, 0f));
} }
var deviceFlipped = false; var deviceFlipped = false;
@@ -209,8 +209,8 @@ namespace Content.Server.Atmos.EntitySystems
{ {
foreach (var mixes in ev.GasMixtures) foreach (var mixes in ev.GasMixtures)
{ {
if(mixes.Value != null) if (mixes.Item2 != null)
gasMixList.Add(new GasMixEntry(mixes.Key, mixes.Value.Pressure, mixes.Value.Temperature, GenerateGasEntryArray(mixes.Value))); gasMixList.Add(new GasMixEntry(mixes.Item1, mixes.Item2.Volume, mixes.Item2.Pressure, mixes.Item2.Temperature, GenerateGasEntryArray(mixes.Item2)));
} }
deviceFlipped = ev.DeviceFlipped; deviceFlipped = ev.DeviceFlipped;
@@ -223,7 +223,16 @@ namespace Content.Server.Atmos.EntitySystems
foreach (var pair in node.Nodes) foreach (var pair in node.Nodes)
{ {
if (pair.Value is PipeNode pipeNode) if (pair.Value is PipeNode pipeNode)
gasMixList.Add(new GasMixEntry(pair.Key, pipeNode.Air.Pressure, pipeNode.Air.Temperature, GenerateGasEntryArray(pipeNode.Air))); {
// check if the volume is zero for some reason so we don't divide by zero
if (pipeNode.Air.Volume == 0f)
continue;
// only display the gas in the analyzed pipe element, not the whole system
var pipeAir = pipeNode.Air.Clone();
pipeAir.Multiply(pipeNode.Volume / pipeNode.Air.Volume);
pipeAir.Volume = pipeNode.Volume;
gasMixList.Add(new GasMixEntry(pair.Key, pipeAir.Volume, pipeAir.Pressure, pipeAir.Temperature, GenerateGasEntryArray(pipeAir)));
}
} }
} }
} }
@@ -286,9 +295,9 @@ namespace Content.Server.Atmos.EntitySystems
public sealed class GasAnalyzerScanEvent : EntityEventArgs public sealed class GasAnalyzerScanEvent : EntityEventArgs
{ {
/// <summary> /// <summary>
/// Key is the mix name (ex "pipe", "inlet", "filter"), value is the pipe direction and GasMixture. Add all mixes that should be reported when scanned. /// The string is for the name (ex "pipe", "inlet", "filter"), GasMixture for the corresponding gas mix. Add all mixes that should be reported when scanned.
/// </summary> /// </summary>
public Dictionary<string, GasMixture?>? GasMixtures; public List<(string, GasMixture?)>? GasMixtures;
/// <summary> /// <summary>
/// If the device is flipped. Flipped is defined as when the inline input is 90 degrees CW to the side input /// If the device is flipped. Flipped is defined as when the inline input is 90 degrees CW to the side input

View File

@@ -359,7 +359,8 @@ namespace Content.Server.Atmos.EntitySystems
/// </summary> /// </summary>
private void OnAnalyzed(EntityUid uid, GasTankComponent component, GasAnalyzerScanEvent args) private void OnAnalyzed(EntityUid uid, GasTankComponent component, GasAnalyzerScanEvent args)
{ {
args.GasMixtures = new Dictionary<string, GasMixture?> { {Name(uid), component.Air} }; args.GasMixtures ??= new List<(string, GasMixture?)>();
args.GasMixtures.Add((Name(uid), component.Air));
} }
private void OnGasTankPrice(EntityUid uid, GasTankComponent component, ref PriceCalculationEvent args) private void OnGasTankPrice(EntityUid uid, GasTankComponent component, ref PriceCalculationEvent args)

View File

@@ -73,7 +73,7 @@ namespace Content.Server.Atmos.Piping.Trinary.EntitySystems
if (filter.FilteredGas.HasValue) if (filter.FilteredGas.HasValue)
{ {
var filteredOut = new GasMixture() {Temperature = removed.Temperature}; var filteredOut = new GasMixture() { Temperature = removed.Temperature };
filteredOut.SetMoles(filter.FilteredGas.Value, removed.GetMoles(filter.FilteredGas.Value)); filteredOut.SetMoles(filter.FilteredGas.Value, removed.GetMoles(filter.FilteredGas.Value));
removed.SetMoles(filter.FilteredGas.Value, 0f); removed.SetMoles(filter.FilteredGas.Value, 0f);
@@ -180,17 +180,30 @@ namespace Content.Server.Atmos.Piping.Trinary.EntitySystems
/// </summary> /// </summary>
private void OnFilterAnalyzed(EntityUid uid, GasFilterComponent component, GasAnalyzerScanEvent args) private void OnFilterAnalyzed(EntityUid uid, GasFilterComponent component, GasAnalyzerScanEvent args)
{ {
if (!EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer)) args.GasMixtures ??= new List<(string, GasMixture?)>();
return;
args.GasMixtures ??= new Dictionary<string, GasMixture?>(); // multiply by volume fraction to make sure to send only the gas inside the analyzed pipe element, not the whole pipe system
if (_nodeContainer.TryGetNode(uid, component.InletName, out PipeNode? inlet) && inlet.Air.Volume != 0f)
if(_nodeContainer.TryGetNode(nodeContainer, component.InletName, out PipeNode? inlet)) {
args.GasMixtures.Add(Loc.GetString("gas-analyzer-window-text-inlet"), inlet.Air); var inletAirLocal = inlet.Air.Clone();
if(_nodeContainer.TryGetNode(nodeContainer, component.FilterName, out PipeNode? filterNode)) inletAirLocal.Multiply(inlet.Volume / inlet.Air.Volume);
args.GasMixtures.Add(Loc.GetString("gas-analyzer-window-text-filter"), filterNode.Air); inletAirLocal.Volume = inlet.Volume;
if(_nodeContainer.TryGetNode(nodeContainer, component.OutletName, out PipeNode? outlet)) args.GasMixtures.Add((Loc.GetString("gas-analyzer-window-text-inlet"), inletAirLocal));
args.GasMixtures.Add(Loc.GetString("gas-analyzer-window-text-outlet"), outlet.Air); }
if (_nodeContainer.TryGetNode(uid, component.FilterName, out PipeNode? filterNode) && filterNode.Air.Volume != 0f)
{
var filterNodeAirLocal = filterNode.Air.Clone();
filterNodeAirLocal.Multiply(filterNode.Volume / filterNode.Air.Volume);
filterNodeAirLocal.Volume = filterNode.Volume;
args.GasMixtures.Add((Loc.GetString("gas-analyzer-window-text-filter"), filterNodeAirLocal));
}
if (_nodeContainer.TryGetNode(uid, component.OutletName, out PipeNode? outlet) && outlet.Air.Volume != 0f)
{
var outletAirLocal = outlet.Air.Clone();
outletAirLocal.Multiply(outlet.Volume / outlet.Air.Volume);
outletAirLocal.Volume = outlet.Volume;
args.GasMixtures.Add((Loc.GetString("gas-analyzer-window-text-outlet"), outletAirLocal));
}
args.DeviceFlipped = inlet != null && filterNode != null && inlet.CurrentPipeDirection.ToDirection() == filterNode.CurrentPipeDirection.ToDirection().GetClockwise90Degrees(); args.DeviceFlipped = inlet != null && filterNode != null && inlet.CurrentPipeDirection.ToDirection() == filterNode.CurrentPipeDirection.ToDirection().GetClockwise90Degrees();
} }

View File

@@ -205,19 +205,31 @@ namespace Content.Server.Atmos.Piping.Trinary.EntitySystems
/// </summary> /// </summary>
private void OnMixerAnalyzed(EntityUid uid, GasMixerComponent component, GasAnalyzerScanEvent args) private void OnMixerAnalyzed(EntityUid uid, GasMixerComponent component, GasAnalyzerScanEvent args)
{ {
if (!EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer)) args.GasMixtures ??= new List<(string, GasMixture?)>();
return;
var gasMixDict = new Dictionary<string, GasMixture?>(); // multiply by volume fraction to make sure to send only the gas inside the analyzed pipe element, not the whole pipe system
if (_nodeContainer.TryGetNode(uid, component.InletOneName, out PipeNode? inletOne) && inletOne.Air.Volume != 0f)
{
var inletOneAirLocal = inletOne.Air.Clone();
inletOneAirLocal.Multiply(inletOne.Volume / inletOne.Air.Volume);
inletOneAirLocal.Volume = inletOne.Volume;
args.GasMixtures.Add(($"{inletOne.CurrentPipeDirection} {Loc.GetString("gas-analyzer-window-text-inlet")}", inletOneAirLocal));
}
if (_nodeContainer.TryGetNode(uid, component.InletTwoName, out PipeNode? inletTwo) && inletTwo.Air.Volume != 0f)
{
var inletTwoAirLocal = inletTwo.Air.Clone();
inletTwoAirLocal.Multiply(inletTwo.Volume / inletTwo.Air.Volume);
inletTwoAirLocal.Volume = inletTwo.Volume;
args.GasMixtures.Add(($"{inletTwo.CurrentPipeDirection} {Loc.GetString("gas-analyzer-window-text-inlet")}", inletTwoAirLocal));
}
if (_nodeContainer.TryGetNode(uid, component.OutletName, out PipeNode? outlet) && outlet.Air.Volume != 0f)
{
var outletAirLocal = outlet.Air.Clone();
outletAirLocal.Multiply(outlet.Volume / outlet.Air.Volume);
outletAirLocal.Volume = outlet.Volume;
args.GasMixtures.Add((Loc.GetString("gas-analyzer-window-text-outlet"), outletAirLocal));
}
if(_nodeContainer.TryGetNode(nodeContainer, component.InletOneName, out PipeNode? inletOne))
gasMixDict.Add($"{inletOne.CurrentPipeDirection} {Loc.GetString("gas-analyzer-window-text-inlet")}", inletOne.Air);
if(_nodeContainer.TryGetNode(nodeContainer, component.InletTwoName, out PipeNode? inletTwo))
gasMixDict.Add($"{inletTwo.CurrentPipeDirection} {Loc.GetString("gas-analyzer-window-text-inlet")}", inletTwo.Air);
if(_nodeContainer.TryGetNode(nodeContainer, component.OutletName, out PipeNode? outlet))
gasMixDict.Add(Loc.GetString("gas-analyzer-window-text-outlet"), outlet.Air);
args.GasMixtures = gasMixDict;
args.DeviceFlipped = inletOne != null && inletTwo != null && inletOne.CurrentPipeDirection.ToDirection() == inletTwo.CurrentPipeDirection.ToDirection().GetClockwise90Degrees(); args.DeviceFlipped = inletOne != null && inletTwo != null && inletOne.CurrentPipeDirection.ToDirection() == inletTwo.CurrentPipeDirection.ToDirection().GetClockwise90Degrees();
} }
} }

View File

@@ -294,9 +294,17 @@ public sealed class GasCanisterSystem : EntitySystem
/// <summary> /// <summary>
/// Returns the gas mixture for the gas analyzer /// Returns the gas mixture for the gas analyzer
/// </summary> /// </summary>
private void OnAnalyzed(EntityUid uid, GasCanisterComponent component, GasAnalyzerScanEvent args) private void OnAnalyzed(EntityUid uid, GasCanisterComponent canisterComponent, GasAnalyzerScanEvent args)
{ {
args.GasMixtures = new Dictionary<string, GasMixture?> { {Name(uid), component.Air} }; args.GasMixtures ??= new List<(string, GasMixture?)>();
args.GasMixtures.Add((Name(uid), canisterComponent.Air));
// if a tank is inserted show it on the analyzer as well
if (canisterComponent.GasTankSlot.Item != null)
{
var tank = canisterComponent.GasTankSlot.Item.Value;
var tankComponent = Comp<GasTankComponent>(tank);
args.GasMixtures.Add((Name(tank), tankComponent.Air));
}
} }
/// <summary> /// <summary>

View File

@@ -80,7 +80,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems
return; return;
} }
var timeDelta = args.dt; var timeDelta = args.dt;
var pressureDelta = timeDelta * vent.TargetPressureChange; var pressureDelta = timeDelta * vent.TargetPressureChange;
if (vent.PumpDirection == VentPumpDirection.Releasing && pipe.Air.Pressure > 0) if (vent.PumpDirection == VentPumpDirection.Releasing && pipe.Air.Pressure > 0)
@@ -292,7 +292,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems
/// </summary> /// </summary>
private void OnAnalyzed(EntityUid uid, GasVentPumpComponent component, GasAnalyzerScanEvent args) private void OnAnalyzed(EntityUid uid, GasVentPumpComponent component, GasAnalyzerScanEvent args)
{ {
var gasMixDict = new Dictionary<string, GasMixture?>(); args.GasMixtures ??= new List<(string, GasMixture?)>();
// these are both called pipe, above it switches using this so I duplicated that...? // these are both called pipe, above it switches using this so I duplicated that...?
var nodeName = component.PumpDirection switch var nodeName = component.PumpDirection switch
@@ -301,10 +301,14 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems
VentPumpDirection.Siphoning => component.Outlet, VentPumpDirection.Siphoning => component.Outlet,
_ => throw new ArgumentOutOfRangeException() _ => throw new ArgumentOutOfRangeException()
}; };
if (_nodeContainer.TryGetNode(uid, nodeName, out PipeNode? pipe)) // multiply by volume fraction to make sure to send only the gas inside the analyzed pipe element, not the whole pipe system
gasMixDict.Add(nodeName, pipe.Air); if (_nodeContainer.TryGetNode(uid, nodeName, out PipeNode? pipe) && pipe.Air.Volume != 0f)
{
args.GasMixtures = gasMixDict; var pipeAirLocal = pipe.Air.Clone();
pipeAirLocal.Multiply(pipe.Volume / pipe.Air.Volume);
pipeAirLocal.Volume = pipe.Volume;
args.GasMixtures.Add((nodeName, pipeAirLocal));
}
} }
private void OnWeldChanged(EntityUid uid, GasVentPumpComponent component, ref WeldableChangedEvent args) private void OnWeldChanged(EntityUid uid, GasVentPumpComponent component, ref WeldableChangedEvent args)

View File

@@ -151,10 +151,8 @@ namespace Content.Server.Atmos.Portable
/// </summary> /// </summary>
private void OnScrubberAnalyzed(EntityUid uid, PortableScrubberComponent component, GasAnalyzerScanEvent args) private void OnScrubberAnalyzed(EntityUid uid, PortableScrubberComponent component, GasAnalyzerScanEvent args)
{ {
args.GasMixtures ??= new Dictionary<string, GasMixture?> { { Name(uid), component.Air } }; args.GasMixtures ??= new List<(string, GasMixture?)>();
// If it's connected to a port, include the port side args.GasMixtures.Add((Name(uid), component.Air));
if (_nodeContainer.TryGetNode(uid, component.PortName, out PipeNode? port))
args.GasMixtures.Add(component.PortName, port.Air);
} }
} }
} }

View File

@@ -276,10 +276,17 @@ public sealed partial class CryoPodSystem : SharedCryoPodSystem
if (!TryComp(entity, out CryoPodAirComponent? cryoPodAir)) if (!TryComp(entity, out CryoPodAirComponent? cryoPodAir))
return; return;
args.GasMixtures ??= new Dictionary<string, GasMixture?> { { Name(entity.Owner), cryoPodAir.Air } }; args.GasMixtures ??= new List<(string, GasMixture?)>();
args.GasMixtures.Add((Name(entity.Owner), cryoPodAir.Air));
// If it's connected to a port, include the port side // If it's connected to a port, include the port side
if (_nodeContainer.TryGetNode(entity.Owner, entity.Comp.PortName, out PipeNode? port)) // multiply by volume fraction to make sure to send only the gas inside the analyzed pipe element, not the whole pipe system
args.GasMixtures.Add(entity.Comp.PortName, port.Air); if (_nodeContainer.TryGetNode(entity.Owner, entity.Comp.PortName, out PipeNode? port) && port.Air.Volume != 0f)
{
var portAirLocal = port.Air.Clone();
portAirLocal.Multiply(port.Volume / port.Air.Volume);
portAirLocal.Volume = port.Volume;
args.GasMixtures.Add((entity.Comp.PortName, portAirLocal));
}
} }
private void OnEjected(Entity<CryoPodComponent> cryoPod, ref EntRemovedFromContainerMessage args) private void OnEjected(Entity<CryoPodComponent> cryoPod, ref EntRemovedFromContainerMessage args)

View File

@@ -151,7 +151,8 @@ public sealed class RadiationCollectorSystem : EntitySystem
if (!TryGetLoadedGasTank(uid, out var gasTankComponent)) if (!TryGetLoadedGasTank(uid, out var gasTankComponent))
return; return;
args.GasMixtures = new Dictionary<string, GasMixture?> { { Name(uid), gasTankComponent.Air } }; args.GasMixtures ??= new List<(string, GasMixture?)>();
args.GasMixtures.Add((Name(uid), gasTankComponent.Air));
} }
public void ToggleCollector(EntityUid uid, EntityUid? user = null, RadiationCollectorComponent? component = null) public void ToggleCollector(EntityUid uid, EntityUid? user = null, RadiationCollectorComponent? component = null)

View File

@@ -56,13 +56,15 @@ public sealed partial class GasAnalyzerComponent : Component
/// Name of the tab in the UI /// Name of the tab in the UI
/// </summary> /// </summary>
public readonly string Name; public readonly string Name;
public readonly float Volume;
public readonly float Pressure; public readonly float Pressure;
public readonly float Temperature; public readonly float Temperature;
public readonly GasEntry[]? Gases; public readonly GasEntry[]? Gases;
public GasMixEntry(string name, float pressure, float temperature, GasEntry[]? gases = null) public GasMixEntry(string name, float volume, float pressure, float temperature, GasEntry[]? gases = null)
{ {
Name = name; Name = name;
Volume = volume;
Pressure = pressure; Pressure = pressure;
Temperature = temperature; Temperature = temperature;
Gases = gases; Gases = gases;

View File

@@ -12,6 +12,8 @@ gas-analyzer-window-refresh-button = Refresh
gas-analyzer-window-no-data = No Data gas-analyzer-window-no-data = No Data
gas-analyzer-window-no-gas-text = No Gases gas-analyzer-window-no-gas-text = No Gases
gas-analyzer-window-error-text = Error: {$errorText} gas-analyzer-window-error-text = Error: {$errorText}
gas-analyzer-window-volume-text = Volume:
gas-analyzer-window-volume-val-text = {$volume} L
gas-analyzer-window-pressure-text = Pressure: gas-analyzer-window-pressure-text = Pressure:
gas-analyzer-window-pressure-val-text = {$pressure} kPa gas-analyzer-window-pressure-val-text = {$pressure} kPa
gas-analyzer-window-temperature-text = Temperature: gas-analyzer-window-temperature-text = Temperature: