]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
mech nitrogen filtering 2 (#19868)
authordeltanedas <39013340+deltanedas@users.noreply.github.com>
Mon, 6 Nov 2023 03:15:13 +0000 (03:15 +0000)
committerGitHub <noreply@github.com>
Mon, 6 Nov 2023 03:15:13 +0000 (20:15 -0700)
* target oxygen logic

* filter out nitrogen when low on oxygen

* vvrw and datafield for everything

* :trollface:

* bruh does work

* tagless chicken

* move into atmos, make it not depend on mech

* update mech prototype

---------

Co-authored-by: deltanedas <@deltanedas:kde.org>
Content.Server/Atmos/Components/AirFilterComponent.cs [new file with mode: 0644]
Content.Server/Atmos/Components/AirIntakeComponent.cs [new file with mode: 0644]
Content.Server/Atmos/EntitySystems/AirFilterSystem.cs [new file with mode: 0644]
Content.Server/Mech/Components/MechAirComponent.cs
Content.Server/Mech/Components/MechAirFilterComponent.cs [deleted file]
Content.Server/Mech/Components/MechAirIntakeComponent.cs [deleted file]
Content.Server/Mech/Systems/MechSystem.Filtering.cs [deleted file]
Content.Server/Mech/Systems/MechSystem.cs
Resources/Prototypes/Entities/Objects/Specific/Mech/mechs.yml

diff --git a/Content.Server/Atmos/Components/AirFilterComponent.cs b/Content.Server/Atmos/Components/AirFilterComponent.cs
new file mode 100644 (file)
index 0000000..6b65a64
--- /dev/null
@@ -0,0 +1,46 @@
+using Content.Server.Atmos.EntitySystems;
+using Content.Shared.Atmos;
+
+namespace Content.Server.Atmos.Components;
+
+/// <summary>
+/// This is basically a reverse scrubber but using <see cref="GetFilterAirEvent"/>.
+/// </summary>
+[RegisterComponent, Access(typeof(AirFilterSystem))]
+public sealed partial class AirFilterComponent : Component
+{
+    /// <summary>
+    /// Gases that will be filtered out of internal air
+    /// </summary>
+    [DataField(required: true)]
+    public HashSet<Gas> Gases = new();
+
+    /// <summary>
+    /// Gases that will be filtered out of internal air to maintain oxygen ratio.
+    /// When oxygen is below <see cref="TargetOxygen"/>, these gases will be filtered instead of <see cref="Gases"/>.
+    /// </summary>
+    [DataField(required: true)]
+    public HashSet<Gas> OverflowGases = new();
+
+    /// <summary>
+    /// Minimum oxygen fraction before it will start removing <see cref="OverflowGases"/>.
+    /// </summary>
+    [DataField, ViewVariables(VVAccess.ReadWrite)]
+    public float TargetOxygen = 0.21f;
+
+    /// <summary>
+    /// Gas to consider oxygen for <see cref="TargetOxygen"/> and <see cref="OverflowGases"/> logic.
+    /// </summary>
+    /// <remarks>
+    /// For slime you might want to change this to be nitrogen, and overflowgases to remove oxygen.
+    /// However theres still no real danger since standard atmos is mostly nitrogen so nitrogen tends to 100% anyway.
+    /// </remarks>
+    [DataField, ViewVariables(VVAccess.ReadWrite)]
+    public Gas Oxygen = Gas.Oxygen;
+
+    /// <summary>
+    /// Fraction of target volume to transfer every second.
+    /// </summary>
+    [DataField, ViewVariables(VVAccess.ReadWrite)]
+    public float TransferRate = 0.1f;
+}
diff --git a/Content.Server/Atmos/Components/AirIntakeComponent.cs b/Content.Server/Atmos/Components/AirIntakeComponent.cs
new file mode 100644 (file)
index 0000000..dafaed9
--- /dev/null
@@ -0,0 +1,29 @@
+using Content.Server.Atmos.EntitySystems;
+using Content.Shared.Atmos;
+
+namespace Content.Server.Atmos.Components;
+
+/// <summary>
+/// This is basically a siphon vent for <see cref="GetFilterAirEvent"/>.
+/// </summary>
+[RegisterComponent, Access(typeof(AirFilterSystem))]
+public sealed partial class AirIntakeComponent : Component
+{
+    /// <summary>
+    /// Target pressure change for a single atmos tick
+    /// </summary>
+    [DataField, ViewVariables(VVAccess.ReadWrite)]
+    public float TargetPressureChange = 5f;
+
+    /// <summary>
+    /// How strong the intake pump is, it will be able to replenish air from lower pressure areas.
+    /// </summary>
+    [DataField, ViewVariables(VVAccess.ReadWrite)]
+    public float PumpPower = 2f;
+
+    /// <summary>
+    /// Pressure to intake gases up to, maintains pressure of the air volume.
+    /// </summary>
+    [DataField, ViewVariables(VVAccess.ReadWrite)]
+    public float Pressure = Atmospherics.OneAtmosphere;
+}
diff --git a/Content.Server/Atmos/EntitySystems/AirFilterSystem.cs b/Content.Server/Atmos/EntitySystems/AirFilterSystem.cs
new file mode 100644 (file)
index 0000000..a7404c0
--- /dev/null
@@ -0,0 +1,113 @@
+using Content.Server.Atmos;
+using Content.Server.Atmos.Components;
+using Content.Server.Atmos.Piping.Components;
+using Content.Shared.Atmos;
+using Robust.Shared.GameObjects;
+using Robust.Shared.Map;
+using System.Diagnostics.CodeAnalysis;
+
+namespace Content.Server.Atmos.EntitySystems;
+
+/// <summary>
+/// Handles gas filtering and intake for <see cref="AirIntakeComponent"/> and <see cref="AirFilterComponent"/>.
+/// </summary>
+public sealed class AirFilterSystem : EntitySystem
+{
+    [Dependency] private readonly AtmosphereSystem _atmosphere = default!;
+    [Dependency] private readonly IMapManager _map = default!;
+    [Dependency] private readonly SharedTransformSystem _transform = default!;
+
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<AirIntakeComponent, AtmosDeviceUpdateEvent>(OnIntakeUpdate);
+        SubscribeLocalEvent<AirFilterComponent, AtmosDeviceUpdateEvent>(OnFilterUpdate);
+    }
+
+    private void OnIntakeUpdate(EntityUid uid, AirIntakeComponent intake, AtmosDeviceUpdateEvent args)
+    {
+        if (!GetAir(uid, out var air))
+            return;
+
+        // if the volume is filled there is nothing to do
+        if (air.Pressure >= intake.Pressure)
+            return;
+
+        var environment = _atmosphere.GetContainingMixture(uid, true, true);
+        // nothing to intake from
+        if (environment == null)
+            return;
+
+        // absolute maximum pressure change
+        var pressureDelta = args.dt * intake.TargetPressureChange;
+        pressureDelta = MathF.Min(pressureDelta, intake.Pressure - air.Pressure);
+        if (pressureDelta <= 0)
+            return;
+
+        // how many moles to transfer to change internal pressure by pressureDelta
+        // ignores temperature difference because lazy
+        var transferMoles = pressureDelta * air.Volume / (environment.Temperature * Atmospherics.R);
+        _atmosphere.Merge(air, environment.Remove(transferMoles));
+    }
+
+    private void OnFilterUpdate(EntityUid uid, AirFilterComponent filter, AtmosDeviceUpdateEvent args)
+    {
+        if (!GetAir(uid, out var air))
+            return;
+
+        var ratio = MathF.Min(1f, args.dt * filter.TransferRate);
+        var removed = air.RemoveRatio(ratio);
+        // nothing left to remove from the volume
+        if (MathHelper.CloseToPercent(removed.TotalMoles, 0f))
+            return;
+
+        // when oxygen gets too low start removing overflow gases (nitrogen) to maintain oxygen ratio
+        var oxygen = air.GetMoles(filter.Oxygen) / air.TotalMoles;
+        var gases = oxygen >= filter.TargetOxygen ? filter.Gases : filter.OverflowGases;
+
+        var coordinates = Transform(uid).MapPosition;
+        GasMixture? destination = null;
+        if (_map.TryFindGridAt(coordinates, out _, out var grid))
+        {
+            var tile = grid.GetTileRef(coordinates);
+            destination = _atmosphere.GetTileMixture(tile.GridUid, null, tile.GridIndices, true);
+        }
+
+        if (destination != null)
+        {
+            _atmosphere.ScrubInto(removed, destination, gases);
+        }
+        else
+        {
+            // filtering into space/planet so just discard them
+            foreach (var gas in gases)
+            {
+                removed.SetMoles(gas, 0f);
+            }
+        }
+
+        _atmosphere.Merge(air, removed);
+    }
+
+    /// <summary>
+    /// Uses <see cref="GetFilterAirEvent"/> to get an internal volume of air on an entity.
+    /// Used for both filter and intake.
+    /// </summary>
+    public bool GetAir(EntityUid uid, [NotNullWhen(true)] out GasMixture? air)
+    {
+        air = null;
+
+        var ev = new GetFilterAirEvent();
+        RaiseLocalEvent(uid, ref ev);
+        air = ev.Air;
+        return air != null;
+    }
+}
+
+/// <summary>
+/// Get a reference to an entity's air volume to filter.
+/// Do not create a new mixture as this will be modified when filtering and intaking air.
+/// </summary>
+[ByRefEvent]
+public record struct GetFilterAirEvent(GasMixture? Air = null);
index 7e989f6481a384da9bebf2d337fe062edc05bc14..c533b3d8343d6b5da0abe957ee6ea403b830e31a 100644 (file)
@@ -6,7 +6,8 @@ namespace Content.Server.Mech.Components;
 public sealed partial class MechAirComponent : Component
 {
     //TODO: this doesn't support a tank implant for mechs or anything like that
-    [ViewVariables(VVAccess.ReadWrite)]
+    [DataField, ViewVariables(VVAccess.ReadWrite)]
     public GasMixture Air = new (GasMixVolume);
+
     public const float GasMixVolume = 70f;
 }
diff --git a/Content.Server/Mech/Components/MechAirFilterComponent.cs b/Content.Server/Mech/Components/MechAirFilterComponent.cs
deleted file mode 100644 (file)
index d35b4af..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-using Content.Shared.Atmos;
-
-namespace Content.Server.Mech.Components;
-
-/// <summary>
-/// This is basically a reverse scrubber for MechAir
-/// </summary>
-[RegisterComponent]
-public sealed partial class MechAirFilterComponent : Component
-{
-    /// <summary>
-    /// Gases that will be filtered out of internal air
-    /// </summary>
-    [DataField("gases", required: true)]
-    public HashSet<Gas> Gases = new();
-
-    /// <summary>
-    /// Target volume to transfer every second.
-    /// </summary>
-    [DataField("transferRate")]
-    public float TransferRate = MechAirComponent.GasMixVolume * 0.1f;
-}
diff --git a/Content.Server/Mech/Components/MechAirIntakeComponent.cs b/Content.Server/Mech/Components/MechAirIntakeComponent.cs
deleted file mode 100644 (file)
index 7b3a84c..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-using Content.Shared.Atmos;
-
-namespace Content.Server.Mech.Components;
-
-/// <summary>
-/// This is basically a siphon vent for mech but not using pump vent component because MechAir bad
-/// </summary>
-[RegisterComponent]
-public sealed partial class MechAirIntakeComponent : Component
-{
-    /// <summary>
-    /// Target pressure change for a single atmos tick
-    /// </summary>
-    [DataField("targetPressureChange")]
-    public float TargetPressureChange = 5f;
-
-    /// <summary>
-    /// How strong the intake pump is, it will be able to replenish air from lower pressure areas.
-    /// </summary>
-    [DataField("pumpPower")]
-    public float PumpPower = 2f;
-
-    /// <summary>
-    /// Pressure to intake gases up to, maintains MechAir pressure.
-    /// </summary>
-    [DataField("pressure")]
-    public float Pressure = Atmospherics.OneAtmosphere;
-}
diff --git a/Content.Server/Mech/Systems/MechSystem.Filtering.cs b/Content.Server/Mech/Systems/MechSystem.Filtering.cs
deleted file mode 100644 (file)
index 3de151b..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-using Content.Server.Atmos;
-using Content.Server.Atmos.Piping.Components;
-using Content.Server.Mech.Components;
-using Content.Shared.Atmos;
-using Content.Shared.Mech.Components;
-
-namespace Content.Server.Mech.Systems;
-
-// TODO: this could be reused for gasmask or something if MechAir wasnt a thing
-public sealed partial class MechSystem
-{
-    [Dependency] private readonly SharedTransformSystem _transform = default!;
-
-    private void InitializeFiltering()
-    {
-        SubscribeLocalEvent<MechAirIntakeComponent, AtmosDeviceUpdateEvent>(OnIntakeUpdate);
-        SubscribeLocalEvent<MechAirFilterComponent, AtmosDeviceUpdateEvent>(OnFilterUpdate);
-    }
-
-    private void OnIntakeUpdate(EntityUid uid, MechAirIntakeComponent intake, AtmosDeviceUpdateEvent args)
-    {
-        if (!TryComp<MechComponent>(uid, out var mech) || !mech.Airtight || !TryComp<MechAirComponent>(uid, out var mechAir))
-            return;
-
-        // if the mech is filled there is nothing to do
-        if (mechAir.Air.Pressure >= intake.Pressure)
-            return;
-
-        var environment = _atmosphere.GetContainingMixture(uid, true, true);
-        // nothing to intake from
-        if (environment == null)
-            return;
-
-        // absolute maximum pressure change
-        var pressureDelta = args.dt * intake.TargetPressureChange;
-        pressureDelta = MathF.Min(pressureDelta, intake.Pressure - mechAir.Air.Pressure);
-        if (pressureDelta <= 0)
-            return;
-
-        // how many moles to transfer to change internal pressure by pressureDelta
-        // ignores temperature difference because lazy
-        var transferMoles = pressureDelta * mechAir.Air.Volume / (environment.Temperature * Atmospherics.R);
-        _atmosphere.Merge(mechAir.Air, environment.Remove(transferMoles));
-    }
-
-    private void OnFilterUpdate(EntityUid uid, MechAirFilterComponent filter, AtmosDeviceUpdateEvent args)
-    {
-        if (!TryComp<MechComponent>(uid, out var mech) || !mech.Airtight || !TryComp<MechAirComponent>(uid, out var mechAir))
-            return;
-
-        var ratio = MathF.Min(1f, args.dt * filter.TransferRate / mechAir.Air.Volume);
-        var removed = mechAir.Air.RemoveRatio(ratio);
-        // nothing left to remove from the mech
-        if (MathHelper.CloseToPercent(removed.TotalMoles, 0f))
-            return;
-
-
-        var coordinates = Transform(uid).MapPosition;
-        GasMixture? destination = null;
-        if (_map.TryFindGridAt(coordinates, out var gridId, out var grid))
-        {
-            var tile = _mapSystem.GetTileRef(gridId, grid, coordinates);
-            destination = _atmosphere.GetTileMixture(tile.GridUid, null, tile.GridIndices, true);
-        }
-
-        if (destination != null)
-        {
-            _atmosphere.ScrubInto(removed, destination, filter.Gases);
-        }
-        else
-        {
-            // filtering into space/planet so just discard them
-            foreach (var gas in filter.Gases)
-            {
-                removed.SetMoles(gas, 0f);
-            }
-        }
-        _atmosphere.Merge(mechAir.Air, removed);
-    }
-}
index a0ca94197ea668d17d06b4efd5f6f2fd87e84cb1..780dbac82b59ae39db429f17884d87d58ea00058 100644 (file)
@@ -47,8 +47,6 @@ public sealed partial class MechSystem : SharedMechSystem
 
         _sawmill = Logger.GetSawmill("mech");
 
-        InitializeFiltering();
-
         SubscribeLocalEvent<MechComponent, InteractUsingEvent>(OnInteractUsing);
         SubscribeLocalEvent<MechComponent, EntInsertedIntoContainerMessage>(OnInsertBattery);
         SubscribeLocalEvent<MechComponent, MapInitEvent>(OnMapInit);
@@ -69,6 +67,8 @@ public sealed partial class MechSystem : SharedMechSystem
         SubscribeLocalEvent<MechPilotComponent, ExhaleLocationEvent>(OnExhale);
         SubscribeLocalEvent<MechPilotComponent, AtmosExposedGetAirEvent>(OnExpose);
 
+        SubscribeLocalEvent<MechAirComponent, GetFilterAirEvent>(OnGetFilterAir);
+
         #region Equipment UI message relays
         SubscribeLocalEvent<MechComponent, MechGrabberEjectMessage>(ReceiveEquipmentUiMesssages);
         SubscribeLocalEvent<MechComponent, MechSoundboardPlayMessage>(ReceiveEquipmentUiMesssages);
@@ -423,5 +423,17 @@ public sealed partial class MechSystem : SharedMechSystem
 
         args.Handled = true;
     }
+
+    private void OnGetFilterAir(EntityUid uid, MechAirComponent comp, ref GetFilterAirEvent args)
+    {
+        if (args.Air != null)
+            return;
+
+        // only airtight mechs get internal air
+        if (!TryComp<MechComponent>(uid, out var mech) || !mech.Airtight)
+            return;
+
+        args.Air = comp.Air;
+    }
     #endregion
 }
index 5eab64e73acefcd5e6dc4aebddc2c5b41065614c..fb2f44158c8b5246d330f16874cf2a8433fe865e 100644 (file)
@@ -6,18 +6,29 @@
   - type: MobMover
   - type: Mech
   - type: MechAir
-  - type: MechAirFilter
+  - type: AirFilter
     # everything except oxygen and nitrogen
     gases:
-    - 2
-    - 3
-    - 4
-    - 5
-    - 6
-    - 7
-    - 8
-    #- 9 TODO: fusion
-  - type: MechAirIntake
+    - CarbonDioxide
+    - Plasma
+    - Tritium
+    - WaterVapor
+    - Miasma
+    - NitrousOxide
+    - Frezon
+    #- Helium3 TODO: fusion
+    # remove everything except oxygen to maintain oxygen ratio
+    overflowGases:
+    - Nitrogen
+    - CarbonDioxide
+    - Plasma
+    - Tritium
+    - WaterVapor
+    - Miasma
+    - NitrousOxide
+    - Frezon
+    #- Helium3 TODO: fusion
+  - type: AirIntake
   # for intake and filter to work
   - type: AtmosDevice
     requireAnchored: false