From f0f54b58722cee0478d414d5e8a817e8e92eae0b Mon Sep 17 00:00:00 2001 From: deltanedas <39013340+deltanedas@users.noreply.github.com> Date: Mon, 6 Nov 2023 03:15:13 +0000 Subject: [PATCH] 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> --- .../Atmos/Components/AirFilterComponent.cs | 46 +++++++ .../Atmos/Components/AirIntakeComponent.cs | 29 +++++ .../Atmos/EntitySystems/AirFilterSystem.cs | 113 ++++++++++++++++++ .../Mech/Components/MechAirComponent.cs | 3 +- .../Mech/Components/MechAirFilterComponent.cs | 22 ---- .../Mech/Components/MechAirIntakeComponent.cs | 28 ----- .../Mech/Systems/MechSystem.Filtering.cs | 80 ------------- Content.Server/Mech/Systems/MechSystem.cs | 16 ++- .../Entities/Objects/Specific/Mech/mechs.yml | 31 +++-- 9 files changed, 225 insertions(+), 143 deletions(-) create mode 100644 Content.Server/Atmos/Components/AirFilterComponent.cs create mode 100644 Content.Server/Atmos/Components/AirIntakeComponent.cs create mode 100644 Content.Server/Atmos/EntitySystems/AirFilterSystem.cs delete mode 100644 Content.Server/Mech/Components/MechAirFilterComponent.cs delete mode 100644 Content.Server/Mech/Components/MechAirIntakeComponent.cs delete mode 100644 Content.Server/Mech/Systems/MechSystem.Filtering.cs 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 -- 2.51.2