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: