From: deltanedas <39013340+deltanedas@users.noreply.github.com>
Date: Mon, 6 Nov 2023 03:15:13 +0000 (+0000)
Subject: mech nitrogen filtering 2 (#19868)
X-Git-Url: https://git.smokeofanarchy.ru/gitweb.cgi?a=commitdiff_plain;h=f0f54b58722cee0478d414d5e8a817e8e92eae0b;p=space-station-14.git
mech nitrogen filtering 2 (#19868)
* 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>
---
diff --git a/Content.Server/Atmos/Components/AirFilterComponent.cs b/Content.Server/Atmos/Components/AirFilterComponent.cs
new file mode 100644
index 0000000000..6b65a646f2
--- /dev/null
+++ b/Content.Server/Atmos/Components/AirFilterComponent.cs
@@ -0,0 +1,46 @@
+using Content.Server.Atmos.EntitySystems;
+using Content.Shared.Atmos;
+
+namespace Content.Server.Atmos.Components;
+
+///
+/// This is basically a reverse scrubber but using .
+///
+[RegisterComponent, Access(typeof(AirFilterSystem))]
+public sealed partial class AirFilterComponent : Component
+{
+ ///
+ /// Gases that will be filtered out of internal air
+ ///
+ [DataField(required: true)]
+ public HashSet Gases = new();
+
+ ///
+ /// Gases that will be filtered out of internal air to maintain oxygen ratio.
+ /// When oxygen is below , these gases will be filtered instead of .
+ ///
+ [DataField(required: true)]
+ public HashSet OverflowGases = new();
+
+ ///
+ /// Minimum oxygen fraction before it will start removing .
+ ///
+ [DataField, ViewVariables(VVAccess.ReadWrite)]
+ public float TargetOxygen = 0.21f;
+
+ ///
+ /// Gas to consider oxygen for and logic.
+ ///
+ ///
+ /// 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.
+ ///
+ [DataField, ViewVariables(VVAccess.ReadWrite)]
+ public Gas Oxygen = Gas.Oxygen;
+
+ ///
+ /// Fraction of target volume to transfer every second.
+ ///
+ [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
index 0000000000..dafaed93a5
--- /dev/null
+++ b/Content.Server/Atmos/Components/AirIntakeComponent.cs
@@ -0,0 +1,29 @@
+using Content.Server.Atmos.EntitySystems;
+using Content.Shared.Atmos;
+
+namespace Content.Server.Atmos.Components;
+
+///
+/// This is basically a siphon vent for .
+///
+[RegisterComponent, Access(typeof(AirFilterSystem))]
+public sealed partial class AirIntakeComponent : Component
+{
+ ///
+ /// Target pressure change for a single atmos tick
+ ///
+ [DataField, ViewVariables(VVAccess.ReadWrite)]
+ public float TargetPressureChange = 5f;
+
+ ///
+ /// How strong the intake pump is, it will be able to replenish air from lower pressure areas.
+ ///
+ [DataField, ViewVariables(VVAccess.ReadWrite)]
+ public float PumpPower = 2f;
+
+ ///
+ /// Pressure to intake gases up to, maintains pressure of the air volume.
+ ///
+ [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
index 0000000000..a7404c08f4
--- /dev/null
+++ b/Content.Server/Atmos/EntitySystems/AirFilterSystem.cs
@@ -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;
+
+///
+/// Handles gas filtering and intake for and .
+///
+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(OnIntakeUpdate);
+ SubscribeLocalEvent(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);
+ }
+
+ ///
+ /// Uses to get an internal volume of air on an entity.
+ /// Used for both filter and intake.
+ ///
+ 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;
+ }
+}
+
+///
+/// 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.
+///
+[ByRefEvent]
+public record struct GetFilterAirEvent(GasMixture? Air = null);
diff --git a/Content.Server/Mech/Components/MechAirComponent.cs b/Content.Server/Mech/Components/MechAirComponent.cs
index 7e989f6481..c533b3d834 100644
--- a/Content.Server/Mech/Components/MechAirComponent.cs
+++ b/Content.Server/Mech/Components/MechAirComponent.cs
@@ -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
index d35b4af471..0000000000
--- a/Content.Server/Mech/Components/MechAirFilterComponent.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using Content.Shared.Atmos;
-
-namespace Content.Server.Mech.Components;
-
-///
-/// This is basically a reverse scrubber for MechAir
-///
-[RegisterComponent]
-public sealed partial class MechAirFilterComponent : Component
-{
- ///
- /// Gases that will be filtered out of internal air
- ///
- [DataField("gases", required: true)]
- public HashSet Gases = new();
-
- ///
- /// Target volume to transfer every second.
- ///
- [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
index 7b3a84c66b..0000000000
--- a/Content.Server/Mech/Components/MechAirIntakeComponent.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-using Content.Shared.Atmos;
-
-namespace Content.Server.Mech.Components;
-
-///
-/// This is basically a siphon vent for mech but not using pump vent component because MechAir bad
-///
-[RegisterComponent]
-public sealed partial class MechAirIntakeComponent : Component
-{
- ///
- /// Target pressure change for a single atmos tick
- ///
- [DataField("targetPressureChange")]
- public float TargetPressureChange = 5f;
-
- ///
- /// How strong the intake pump is, it will be able to replenish air from lower pressure areas.
- ///
- [DataField("pumpPower")]
- public float PumpPower = 2f;
-
- ///
- /// Pressure to intake gases up to, maintains MechAir pressure.
- ///
- [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
index 3de151ba70..0000000000
--- a/Content.Server/Mech/Systems/MechSystem.Filtering.cs
+++ /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(OnIntakeUpdate);
- SubscribeLocalEvent(OnFilterUpdate);
- }
-
- private void OnIntakeUpdate(EntityUid uid, MechAirIntakeComponent intake, AtmosDeviceUpdateEvent args)
- {
- if (!TryComp(uid, out var mech) || !mech.Airtight || !TryComp(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(uid, out var mech) || !mech.Airtight || !TryComp(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);
- }
-}
diff --git a/Content.Server/Mech/Systems/MechSystem.cs b/Content.Server/Mech/Systems/MechSystem.cs
index a0ca94197e..780dbac82b 100644
--- a/Content.Server/Mech/Systems/MechSystem.cs
+++ b/Content.Server/Mech/Systems/MechSystem.cs
@@ -47,8 +47,6 @@ public sealed partial class MechSystem : SharedMechSystem
_sawmill = Logger.GetSawmill("mech");
- InitializeFiltering();
-
SubscribeLocalEvent(OnInteractUsing);
SubscribeLocalEvent(OnInsertBattery);
SubscribeLocalEvent(OnMapInit);
@@ -69,6 +67,8 @@ public sealed partial class MechSystem : SharedMechSystem
SubscribeLocalEvent(OnExhale);
SubscribeLocalEvent(OnExpose);
+ SubscribeLocalEvent(OnGetFilterAir);
+
#region Equipment UI message relays
SubscribeLocalEvent(ReceiveEquipmentUiMesssages);
SubscribeLocalEvent(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(uid, out var mech) || !mech.Airtight)
+ return;
+
+ args.Air = comp.Air;
+ }
#endregion
}
diff --git a/Resources/Prototypes/Entities/Objects/Specific/Mech/mechs.yml b/Resources/Prototypes/Entities/Objects/Specific/Mech/mechs.yml
index 5eab64e73a..fb2f44158c 100644
--- a/Resources/Prototypes/Entities/Objects/Specific/Mech/mechs.yml
+++ b/Resources/Prototypes/Entities/Objects/Specific/Mech/mechs.yml
@@ -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