From 5a5efa11cf1d68708a70d48a286cb97fde96afdf Mon Sep 17 00:00:00 2001 From: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Date: Wed, 17 Apr 2024 19:42:24 +0200 Subject: [PATCH] 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. --- .../Atmos/UI/GasAnalyzerWindow.xaml.cs | 20 +++++++++++ .../Atmos/EntitySystems/GasAnalyzerSystem.cs | 27 +++++++++----- .../Atmos/EntitySystems/GasTankSystem.cs | 3 +- .../Trinary/EntitySystems/GasFilterSystem.cs | 35 +++++++++++++------ .../Trinary/EntitySystems/GasMixerSystem.cs | 34 ++++++++++++------ .../Unary/EntitySystems/GasCanisterSystem.cs | 12 +++++-- .../Unary/EntitySystems/GasVentPumpSystem.cs | 16 +++++---- .../Atmos/Portable/PortableScrubberSystem.cs | 6 ++-- Content.Server/Medical/CryoPodSystem.cs | 13 +++++-- .../EntitySystems/RadiationCollectorSystem.cs | 3 +- .../Atmos/Components/GasAnalyzerComponent.cs | 4 ++- .../en-US/atmos/gas-analyzer-component.ftl | 2 ++ 12 files changed, 126 insertions(+), 49 deletions(-) diff --git a/Content.Client/Atmos/UI/GasAnalyzerWindow.xaml.cs b/Content.Client/Atmos/UI/GasAnalyzerWindow.xaml.cs index b105e629cf..b54af3a587 100644 --- a/Content.Client/Atmos/UI/GasAnalyzerWindow.xaml.cs +++ b/Content.Client/Atmos/UI/GasAnalyzerWindow.xaml.cs @@ -163,6 +163,26 @@ namespace Content.Client.Atmos.UI parent.AddChild(panel); 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 var presBox = new BoxContainer { Orientation = BoxContainer.LayoutOrientation.Horizontal }; diff --git a/Content.Server/Atmos/EntitySystems/GasAnalyzerSystem.cs b/Content.Server/Atmos/EntitySystems/GasAnalyzerSystem.cs index 2eedb1c6a7..1f5ca80935 100644 --- a/Content.Server/Atmos/EntitySystems/GasAnalyzerSystem.cs +++ b/Content.Server/Atmos/EntitySystems/GasAnalyzerSystem.cs @@ -132,7 +132,7 @@ namespace Content.Server.Atmos.EntitySystems /// 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; DisableAnalyzer(uid, component); } @@ -169,7 +169,7 @@ namespace Content.Server.Atmos.EntitySystems // Check if position is out of range => don't update and disable 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); DisableAnalyzer(uid, component, component.User); return false; @@ -182,13 +182,13 @@ namespace Content.Server.Atmos.EntitySystems var tileMixture = _atmo.GetContainingMixture(uid, true); 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))); } else { // 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; @@ -209,8 +209,8 @@ namespace Content.Server.Atmos.EntitySystems { foreach (var mixes in ev.GasMixtures) { - if(mixes.Value != null) - gasMixList.Add(new GasMixEntry(mixes.Key, mixes.Value.Pressure, mixes.Value.Temperature, GenerateGasEntryArray(mixes.Value))); + if (mixes.Item2 != null) + gasMixList.Add(new GasMixEntry(mixes.Item1, mixes.Item2.Volume, mixes.Item2.Pressure, mixes.Item2.Temperature, GenerateGasEntryArray(mixes.Item2))); } deviceFlipped = ev.DeviceFlipped; @@ -223,7 +223,16 @@ namespace Content.Server.Atmos.EntitySystems foreach (var pair in node.Nodes) { 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 { /// - /// 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. /// - public Dictionary? GasMixtures; + public List<(string, GasMixture?)>? GasMixtures; /// /// If the device is flipped. Flipped is defined as when the inline input is 90 degrees CW to the side input diff --git a/Content.Server/Atmos/EntitySystems/GasTankSystem.cs b/Content.Server/Atmos/EntitySystems/GasTankSystem.cs index 80842416e8..dd84756e45 100644 --- a/Content.Server/Atmos/EntitySystems/GasTankSystem.cs +++ b/Content.Server/Atmos/EntitySystems/GasTankSystem.cs @@ -359,7 +359,8 @@ namespace Content.Server.Atmos.EntitySystems /// private void OnAnalyzed(EntityUid uid, GasTankComponent component, GasAnalyzerScanEvent args) { - args.GasMixtures = new Dictionary { {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) diff --git a/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasFilterSystem.cs b/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasFilterSystem.cs index fbd4260469..c0c2b930f6 100644 --- a/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasFilterSystem.cs +++ b/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasFilterSystem.cs @@ -73,7 +73,7 @@ namespace Content.Server.Atmos.Piping.Trinary.EntitySystems 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)); removed.SetMoles(filter.FilteredGas.Value, 0f); @@ -180,17 +180,30 @@ namespace Content.Server.Atmos.Piping.Trinary.EntitySystems /// private void OnFilterAnalyzed(EntityUid uid, GasFilterComponent component, GasAnalyzerScanEvent args) { - if (!EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer)) - return; + args.GasMixtures ??= new List<(string, GasMixture?)>(); - args.GasMixtures ??= new Dictionary(); - - if(_nodeContainer.TryGetNode(nodeContainer, component.InletName, out PipeNode? inlet)) - args.GasMixtures.Add(Loc.GetString("gas-analyzer-window-text-inlet"), inlet.Air); - if(_nodeContainer.TryGetNode(nodeContainer, component.FilterName, out PipeNode? filterNode)) - args.GasMixtures.Add(Loc.GetString("gas-analyzer-window-text-filter"), filterNode.Air); - if(_nodeContainer.TryGetNode(nodeContainer, component.OutletName, out PipeNode? outlet)) - args.GasMixtures.Add(Loc.GetString("gas-analyzer-window-text-outlet"), outlet.Air); + // 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) + { + var inletAirLocal = inlet.Air.Clone(); + inletAirLocal.Multiply(inlet.Volume / inlet.Air.Volume); + inletAirLocal.Volume = inlet.Volume; + args.GasMixtures.Add((Loc.GetString("gas-analyzer-window-text-inlet"), inletAirLocal)); + } + 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(); } diff --git a/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasMixerSystem.cs b/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasMixerSystem.cs index ba8ebf3c9a..4d7fc134c7 100644 --- a/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasMixerSystem.cs +++ b/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasMixerSystem.cs @@ -205,19 +205,31 @@ namespace Content.Server.Atmos.Piping.Trinary.EntitySystems /// private void OnMixerAnalyzed(EntityUid uid, GasMixerComponent component, GasAnalyzerScanEvent args) { - if (!EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer)) - return; + args.GasMixtures ??= new List<(string, GasMixture?)>(); - var gasMixDict = new Dictionary(); + // 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(); } } diff --git a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCanisterSystem.cs b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCanisterSystem.cs index 3e4340bf1d..bdc9e76538 100644 --- a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCanisterSystem.cs +++ b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCanisterSystem.cs @@ -294,9 +294,17 @@ public sealed class GasCanisterSystem : EntitySystem /// /// Returns the gas mixture for the gas analyzer /// - private void OnAnalyzed(EntityUid uid, GasCanisterComponent component, GasAnalyzerScanEvent args) + private void OnAnalyzed(EntityUid uid, GasCanisterComponent canisterComponent, GasAnalyzerScanEvent args) { - args.GasMixtures = new Dictionary { {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(tank); + args.GasMixtures.Add((Name(tank), tankComponent.Air)); + } } /// diff --git a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentPumpSystem.cs b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentPumpSystem.cs index a986385f5e..7c12cf3f77 100644 --- a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentPumpSystem.cs +++ b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentPumpSystem.cs @@ -80,7 +80,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems return; } - var timeDelta = args.dt; + var timeDelta = args.dt; var pressureDelta = timeDelta * vent.TargetPressureChange; if (vent.PumpDirection == VentPumpDirection.Releasing && pipe.Air.Pressure > 0) @@ -292,7 +292,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems /// private void OnAnalyzed(EntityUid uid, GasVentPumpComponent component, GasAnalyzerScanEvent args) { - var gasMixDict = new Dictionary(); + args.GasMixtures ??= new List<(string, GasMixture?)>(); // these are both called pipe, above it switches using this so I duplicated that...? var nodeName = component.PumpDirection switch @@ -301,10 +301,14 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems VentPumpDirection.Siphoning => component.Outlet, _ => throw new ArgumentOutOfRangeException() }; - if (_nodeContainer.TryGetNode(uid, nodeName, out PipeNode? pipe)) - gasMixDict.Add(nodeName, pipe.Air); - - args.GasMixtures = gasMixDict; + // 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, nodeName, out PipeNode? pipe) && pipe.Air.Volume != 0f) + { + 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) diff --git a/Content.Server/Atmos/Portable/PortableScrubberSystem.cs b/Content.Server/Atmos/Portable/PortableScrubberSystem.cs index f9043c091a..91ee588057 100644 --- a/Content.Server/Atmos/Portable/PortableScrubberSystem.cs +++ b/Content.Server/Atmos/Portable/PortableScrubberSystem.cs @@ -151,10 +151,8 @@ namespace Content.Server.Atmos.Portable /// private void OnScrubberAnalyzed(EntityUid uid, PortableScrubberComponent component, GasAnalyzerScanEvent args) { - args.GasMixtures ??= new Dictionary { { Name(uid), component.Air } }; - // If it's connected to a port, include the port side - if (_nodeContainer.TryGetNode(uid, component.PortName, out PipeNode? port)) - args.GasMixtures.Add(component.PortName, port.Air); + args.GasMixtures ??= new List<(string, GasMixture?)>(); + args.GasMixtures.Add((Name(uid), component.Air)); } } } diff --git a/Content.Server/Medical/CryoPodSystem.cs b/Content.Server/Medical/CryoPodSystem.cs index a949d980be..02e8cebbe0 100644 --- a/Content.Server/Medical/CryoPodSystem.cs +++ b/Content.Server/Medical/CryoPodSystem.cs @@ -276,10 +276,17 @@ public sealed partial class CryoPodSystem : SharedCryoPodSystem if (!TryComp(entity, out CryoPodAirComponent? cryoPodAir)) return; - args.GasMixtures ??= new Dictionary { { 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 (_nodeContainer.TryGetNode(entity.Owner, entity.Comp.PortName, out PipeNode? port)) - args.GasMixtures.Add(entity.Comp.PortName, port.Air); + // 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(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 cryoPod, ref EntRemovedFromContainerMessage args) diff --git a/Content.Server/Singularity/EntitySystems/RadiationCollectorSystem.cs b/Content.Server/Singularity/EntitySystems/RadiationCollectorSystem.cs index b26ab301c6..9107ff2e32 100644 --- a/Content.Server/Singularity/EntitySystems/RadiationCollectorSystem.cs +++ b/Content.Server/Singularity/EntitySystems/RadiationCollectorSystem.cs @@ -151,7 +151,8 @@ public sealed class RadiationCollectorSystem : EntitySystem if (!TryGetLoadedGasTank(uid, out var gasTankComponent)) return; - args.GasMixtures = new Dictionary { { 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) diff --git a/Content.Shared/Atmos/Components/GasAnalyzerComponent.cs b/Content.Shared/Atmos/Components/GasAnalyzerComponent.cs index 51ae8cc740..dec9516c01 100644 --- a/Content.Shared/Atmos/Components/GasAnalyzerComponent.cs +++ b/Content.Shared/Atmos/Components/GasAnalyzerComponent.cs @@ -56,13 +56,15 @@ public sealed partial class GasAnalyzerComponent : Component /// Name of the tab in the UI /// public readonly string Name; + public readonly float Volume; public readonly float Pressure; public readonly float Temperature; 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; + Volume = volume; Pressure = pressure; Temperature = temperature; Gases = gases; diff --git a/Resources/Locale/en-US/atmos/gas-analyzer-component.ftl b/Resources/Locale/en-US/atmos/gas-analyzer-component.ftl index 03a920cb64..652bb19cb5 100644 --- a/Resources/Locale/en-US/atmos/gas-analyzer-component.ftl +++ b/Resources/Locale/en-US/atmos/gas-analyzer-component.ftl @@ -12,6 +12,8 @@ gas-analyzer-window-refresh-button = Refresh gas-analyzer-window-no-data = No Data gas-analyzer-window-no-gas-text = No Gases 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-val-text = {$pressure} kPa gas-analyzer-window-temperature-text = Temperature: