]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Suffocation alerts for nitrogen breathers (#24373)
authorErrant <35878406+Errant-4@users.noreply.github.com>
Tue, 23 Jan 2024 20:17:40 +0000 (21:17 +0100)
committerGitHub <noreply@github.com>
Tue, 23 Jan 2024 20:17:40 +0000 (15:17 -0500)
* Respiratorsystem namespace

* WIP gas alert update, does not work

* Finally

12 files changed:
Content.Server/Body/Components/LungComponent.cs
Content.Server/Body/Systems/RespiratorSystem.cs
Content.Server/Disposal/Unit/EntitySystems/BeingDisposedSystem.cs
Content.Server/Mech/Systems/MechSystem.cs
Content.Server/Medical/InsideCryoPodSystem.cs
Content.Server/Storage/EntitySystems/EntityStorageSystem.cs
Content.Shared/Alert/AlertType.cs
Resources/Locale/en-US/alerts/alerts.ftl
Resources/Prototypes/Alerts/alerts.yml
Resources/Prototypes/Body/Organs/Animal/slimes.yml
Resources/Prototypes/Body/Organs/slime.yml
Resources/Prototypes/Body/Organs/vox.yml

index 1d997a995024f43367ad36d313bee8826abe0d4c..0656ef8fad37a13febc590ca0d59e86f92b8b5c1 100644 (file)
@@ -1,5 +1,6 @@
 using Content.Server.Atmos;
 using Content.Server.Body.Systems;
+using Content.Shared.Alert;
 using Content.Shared.Atmos;
 using Content.Shared.Chemistry.Components;
 
@@ -27,4 +28,10 @@ public sealed partial class LungComponent : Component
     /// </summary>
     [DataField]
     public Entity<SolutionComponent>? Solution = null;
+
+    /// <summary>
+    /// The type of gas this lung needs. Used only for the breathing alerts, not actual metabolism.
+    /// </summary>
+    [DataField]
+    public AlertType Alert = AlertType.LowOxygen;
 }
index b814422181c054191ad3701ef6211913dbdbc723..0fd61a9cb7b2dd3c1f29c34325e78da0dad52e66 100644 (file)
@@ -13,202 +13,211 @@ using Content.Shared.Mobs.Systems;
 using JetBrains.Annotations;
 using Robust.Shared.Timing;
 
-namespace Content.Server.Body.Systems
+namespace Content.Server.Body.Systems;
+
+[UsedImplicitly]
+public sealed class RespiratorSystem : EntitySystem
 {
-    [UsedImplicitly]
-    public sealed class RespiratorSystem : EntitySystem
+    [Dependency] private readonly IAdminLogManager _adminLogger = default!;
+    [Dependency] private readonly IGameTiming _gameTiming = default!;
+    [Dependency] private readonly AlertsSystem _alertsSystem = default!;
+    [Dependency] private readonly AtmosphereSystem _atmosSys = default!;
+    [Dependency] private readonly BodySystem _bodySystem = default!;
+    [Dependency] private readonly DamageableSystem _damageableSys = default!;
+    [Dependency] private readonly LungSystem _lungSystem = default!;
+    [Dependency] private readonly PopupSystem _popupSystem = default!;
+    [Dependency] private readonly MobStateSystem _mobState = default!;
+    [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
+
+    public override void Initialize()
     {
-        [Dependency] private readonly IAdminLogManager _adminLogger = default!;
-        [Dependency] private readonly IGameTiming _gameTiming = default!;
-        [Dependency] private readonly AlertsSystem _alertsSystem = default!;
-        [Dependency] private readonly AtmosphereSystem _atmosSys = default!;
-        [Dependency] private readonly BodySystem _bodySystem = default!;
-        [Dependency] private readonly DamageableSystem _damageableSys = default!;
-        [Dependency] private readonly LungSystem _lungSystem = default!;
-        [Dependency] private readonly PopupSystem _popupSystem = default!;
-        [Dependency] private readonly MobStateSystem _mobState = default!;
-        [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
-
-        public override void Initialize()
-        {
-            base.Initialize();
+        base.Initialize();
 
-            // We want to process lung reagents before we inhale new reagents.
-            UpdatesAfter.Add(typeof(MetabolizerSystem));
-            SubscribeLocalEvent<RespiratorComponent, ApplyMetabolicMultiplierEvent>(OnApplyMetabolicMultiplier);
-        }
+        // We want to process lung reagents before we inhale new reagents.
+        UpdatesAfter.Add(typeof(MetabolizerSystem));
+        SubscribeLocalEvent<RespiratorComponent, ApplyMetabolicMultiplierEvent>(OnApplyMetabolicMultiplier);
+    }
 
-        public override void Update(float frameTime)
-        {
-            base.Update(frameTime);
+    public override void Update(float frameTime)
+    {
+        base.Update(frameTime);
 
-            var query = EntityQueryEnumerator<RespiratorComponent, BodyComponent>();
-            while (query.MoveNext(out var uid, out var respirator, out var body))
+        var query = EntityQueryEnumerator<RespiratorComponent, BodyComponent>();
+        while (query.MoveNext(out var uid, out var respirator, out var body))
+        {
+            if (_mobState.IsDead(uid))
             {
-                if (_mobState.IsDead(uid))
-                {
-                    continue;
-                }
+                continue;
+            }
 
-                respirator.AccumulatedFrametime += frameTime;
+            respirator.AccumulatedFrametime += frameTime;
 
-                if (respirator.AccumulatedFrametime < respirator.CycleDelay)
-                    continue;
-                respirator.AccumulatedFrametime -= respirator.CycleDelay;
-                UpdateSaturation(uid, -respirator.CycleDelay, respirator);
+            if (respirator.AccumulatedFrametime < respirator.CycleDelay)
+                continue;
+            respirator.AccumulatedFrametime -= respirator.CycleDelay;
+            UpdateSaturation(uid, -respirator.CycleDelay, respirator);
 
-                if (!_mobState.IsIncapacitated(uid)) // cannot breathe in crit.
+            if (!_mobState.IsIncapacitated(uid)) // cannot breathe in crit.
+            {
+                switch (respirator.Status)
                 {
-                    switch (respirator.Status)
-                    {
-                        case RespiratorStatus.Inhaling:
-                            Inhale(uid, body);
-                            respirator.Status = RespiratorStatus.Exhaling;
-                            break;
-                        case RespiratorStatus.Exhaling:
-                            Exhale(uid, body);
-                            respirator.Status = RespiratorStatus.Inhaling;
-                            break;
-                    }
+                    case RespiratorStatus.Inhaling:
+                        Inhale(uid, body);
+                        respirator.Status = RespiratorStatus.Exhaling;
+                        break;
+                    case RespiratorStatus.Exhaling:
+                        Exhale(uid, body);
+                        respirator.Status = RespiratorStatus.Inhaling;
+                        break;
                 }
+            }
 
-                if (respirator.Saturation < respirator.SuffocationThreshold)
+            if (respirator.Saturation < respirator.SuffocationThreshold)
+            {
+                if (_gameTiming.CurTime >= respirator.LastGaspPopupTime + respirator.GaspPopupCooldown)
                 {
-                    if (_gameTiming.CurTime >= respirator.LastGaspPopupTime + respirator.GaspPopupCooldown)
-                    {
-                        respirator.LastGaspPopupTime = _gameTiming.CurTime;
-                        _popupSystem.PopupEntity(Loc.GetString("lung-behavior-gasp"), uid);
-                    }
-
-                    TakeSuffocationDamage(uid, respirator);
-                    respirator.SuffocationCycles += 1;
-                    continue;
+                    respirator.LastGaspPopupTime = _gameTiming.CurTime;
+                    _popupSystem.PopupEntity(Loc.GetString("lung-behavior-gasp"), uid);
                 }
 
-                StopSuffocation(uid, respirator);
-                respirator.SuffocationCycles = 0;
+                TakeSuffocationDamage(uid, respirator);
+                respirator.SuffocationCycles += 1;
+                continue;
             }
-        }
 
-        public void Inhale(EntityUid uid, BodyComponent? body = null)
-        {
-            if (!Resolve(uid, ref body, false))
-                return;
-
-            var organs = _bodySystem.GetBodyOrganComponents<LungComponent>(uid, body);
+            StopSuffocation(uid, respirator);
+            respirator.SuffocationCycles = 0;
+        }
+    }
 
-            // Inhale gas
-            var ev = new InhaleLocationEvent();
-            RaiseLocalEvent(uid, ev);
+    public void Inhale(EntityUid uid, BodyComponent? body = null)
+    {
+        if (!Resolve(uid, ref body, false))
+            return;
 
-            ev.Gas ??= _atmosSys.GetContainingMixture(uid, false, true);
+        var organs = _bodySystem.GetBodyOrganComponents<LungComponent>(uid, body);
 
-            if (ev.Gas == null)
-            {
-                return;
-            }
+        // Inhale gas
+        var ev = new InhaleLocationEvent();
+        RaiseLocalEvent(uid, ev);
 
-            var actualGas = ev.Gas.RemoveVolume(Atmospherics.BreathVolume);
+        ev.Gas ??= _atmosSys.GetContainingMixture(uid, false, true);
 
-            var lungRatio = 1.0f / organs.Count;
-            var gas = organs.Count == 1 ? actualGas : actualGas.RemoveRatio(lungRatio);
-            foreach (var (lung, _) in organs)
-            {
-                // Merge doesn't remove gas from the giver.
-                _atmosSys.Merge(lung.Air, gas);
-                _lungSystem.GasToReagent(lung.Owner, lung);
-            }
+        if (ev.Gas == null)
+        {
+            return;
         }
 
-        public void Exhale(EntityUid uid, BodyComponent? body = null)
+        var actualGas = ev.Gas.RemoveVolume(Atmospherics.BreathVolume);
+
+        var lungRatio = 1.0f / organs.Count;
+        var gas = organs.Count == 1 ? actualGas : actualGas.RemoveRatio(lungRatio);
+        foreach (var (lung, _) in organs)
         {
-            if (!Resolve(uid, ref body, false))
-                return;
+            // Merge doesn't remove gas from the giver.
+            _atmosSys.Merge(lung.Air, gas);
+            _lungSystem.GasToReagent(lung.Owner, lung);
+        }
+    }
 
-            var organs = _bodySystem.GetBodyOrganComponents<LungComponent>(uid, body);
+    public void Exhale(EntityUid uid, BodyComponent? body = null)
+    {
+        if (!Resolve(uid, ref body, false))
+            return;
 
-            // exhale gas
+        var organs = _bodySystem.GetBodyOrganComponents<LungComponent>(uid, body);
 
-            var ev = new ExhaleLocationEvent();
-            RaiseLocalEvent(uid, ev, false);
+        // exhale gas
 
-            if (ev.Gas == null)
-            {
-                ev.Gas = _atmosSys.GetContainingMixture(uid, false, true);
+        var ev = new ExhaleLocationEvent();
+        RaiseLocalEvent(uid, ev, false);
 
-                // Walls and grids without atmos comp return null. I guess it makes sense to not be able to exhale in walls,
-                // but this also means you cannot exhale on some grids.
-                ev.Gas ??= GasMixture.SpaceGas;
-            }
+        if (ev.Gas == null)
+        {
+            ev.Gas = _atmosSys.GetContainingMixture(uid, false, true);
 
-            var outGas = new GasMixture(ev.Gas.Volume);
-            foreach (var (lung, _) in organs)
-            {
-                _atmosSys.Merge(outGas, lung.Air);
-                lung.Air.Clear();
+            // Walls and grids without atmos comp return null. I guess it makes sense to not be able to exhale in walls,
+            // but this also means you cannot exhale on some grids.
+            ev.Gas ??= GasMixture.SpaceGas;
+        }
 
-                if (_solutionContainerSystem.ResolveSolution(lung.Owner, lung.SolutionName, ref lung.Solution))
-                    _solutionContainerSystem.RemoveAllSolution(lung.Solution.Value);
-            }
+        var outGas = new GasMixture(ev.Gas.Volume);
+        foreach (var (lung, _) in organs)
+        {
+            _atmosSys.Merge(outGas, lung.Air);
+            lung.Air.Clear();
 
-            _atmosSys.Merge(ev.Gas, outGas);
+            if (_solutionContainerSystem.ResolveSolution(lung.Owner, lung.SolutionName, ref lung.Solution))
+                _solutionContainerSystem.RemoveAllSolution(lung.Solution.Value);
         }
 
-        private void TakeSuffocationDamage(EntityUid uid, RespiratorComponent respirator)
-        {
-            if (respirator.SuffocationCycles == 2)
-                _adminLogger.Add(LogType.Asphyxiation, $"{ToPrettyString(uid):entity} started suffocating");
+        _atmosSys.Merge(ev.Gas, outGas);
+    }
+
+    private void TakeSuffocationDamage(EntityUid uid, RespiratorComponent respirator)
+    {
+        if (respirator.SuffocationCycles == 2)
+            _adminLogger.Add(LogType.Asphyxiation, $"{ToPrettyString(uid):entity} started suffocating");
 
-            if (respirator.SuffocationCycles >= respirator.SuffocationCycleThreshold)
+        if (respirator.SuffocationCycles >= respirator.SuffocationCycleThreshold)
+        {
+            // TODO: This is not going work with multiple different lungs, if that ever becomes a possibility
+            var organs = _bodySystem.GetBodyOrganComponents<LungComponent>(uid);
+            foreach (var (comp, _) in organs)
             {
-                _alertsSystem.ShowAlert(uid, AlertType.LowOxygen);
+                _alertsSystem.ShowAlert(uid, comp.Alert);
             }
-
-            _damageableSys.TryChangeDamage(uid, respirator.Damage, false, false);
         }
 
-        private void StopSuffocation(EntityUid uid, RespiratorComponent respirator)
-        {
-            if (respirator.SuffocationCycles >= 2)
-                _adminLogger.Add(LogType.Asphyxiation, $"{ToPrettyString(uid):entity} stopped suffocating");
+        _damageableSys.TryChangeDamage(uid, respirator.Damage, false, false);
+    }
 
-            _alertsSystem.ClearAlert(uid, AlertType.LowOxygen);
+    private void StopSuffocation(EntityUid uid, RespiratorComponent respirator)
+    {
+        if (respirator.SuffocationCycles >= 2)
+            _adminLogger.Add(LogType.Asphyxiation, $"{ToPrettyString(uid):entity} stopped suffocating");
 
-            _damageableSys.TryChangeDamage(uid, respirator.DamageRecovery);
+        // TODO: This is not going work with multiple different lungs, if that ever becomes a possibility
+        var organs = _bodySystem.GetBodyOrganComponents<LungComponent>(uid);
+        foreach (var (comp, _) in organs)
+        {
+            _alertsSystem.ClearAlert(uid, comp.Alert);
         }
 
-        public void UpdateSaturation(EntityUid uid, float amount,
-            RespiratorComponent? respirator = null)
-        {
-            if (!Resolve(uid, ref respirator, false))
-                return;
+        _damageableSys.TryChangeDamage(uid, respirator.DamageRecovery);
+    }
 
-            respirator.Saturation += amount;
-            respirator.Saturation =
-                Math.Clamp(respirator.Saturation, respirator.MinSaturation, respirator.MaxSaturation);
-        }
+    public void UpdateSaturation(EntityUid uid, float amount,
+        RespiratorComponent? respirator = null)
+    {
+        if (!Resolve(uid, ref respirator, false))
+            return;
 
-        private void OnApplyMetabolicMultiplier(EntityUid uid, RespiratorComponent component,
+        respirator.Saturation += amount;
+        respirator.Saturation =
+            Math.Clamp(respirator.Saturation, respirator.MinSaturation, respirator.MaxSaturation);
+    }
+
+    private void OnApplyMetabolicMultiplier(EntityUid uid, RespiratorComponent component,
             ApplyMetabolicMultiplierEvent args)
+    {
+        if (args.Apply)
         {
-            if (args.Apply)
-            {
-                component.CycleDelay *= args.Multiplier;
-                component.Saturation *= args.Multiplier;
-                component.MaxSaturation *= args.Multiplier;
-                component.MinSaturation *= args.Multiplier;
-                return;
-            }
-
-            // This way we don't have to worry about it breaking if the stasis bed component is destroyed
-            component.CycleDelay /= args.Multiplier;
-            component.Saturation /= args.Multiplier;
-            component.MaxSaturation /= args.Multiplier;
-            component.MinSaturation /= args.Multiplier;
-            // Reset the accumulator properly
-            if (component.AccumulatedFrametime >= component.CycleDelay)
-                component.AccumulatedFrametime = component.CycleDelay;
+            component.CycleDelay *= args.Multiplier;
+            component.Saturation *= args.Multiplier;
+            component.MaxSaturation *= args.Multiplier;
+            component.MinSaturation *= args.Multiplier;
+            return;
         }
+
+        // This way we don't have to worry about it breaking if the stasis bed component is destroyed
+        component.CycleDelay /= args.Multiplier;
+        component.Saturation /= args.Multiplier;
+        component.MaxSaturation /= args.Multiplier;
+        component.MinSaturation /= args.Multiplier;
+        // Reset the accumulator properly
+        if (component.AccumulatedFrametime >= component.CycleDelay)
+            component.AccumulatedFrametime = component.CycleDelay;
     }
 }
 
index 72d86bd64bc31ac91d246f2c71851cbbe30bcc6f..6fbfb1523a1a562b828861ac329e50ba5f980446 100644 (file)
@@ -1,4 +1,5 @@
 using Content.Server.Atmos.EntitySystems;
+using Content.Server.Body.Systems;
 using Content.Server.Disposal.Unit.Components;
 
 namespace Content.Server.Disposal.Unit.EntitySystems;
index fe903655b54e51dabc6a73184d4ee24866f2d613..eef9bcb8ecdb121dab3a5fffed69c8b1e33f6a60 100644 (file)
@@ -16,6 +16,7 @@ using Content.Shared.Popups;
 using Content.Shared.Tools.Components;
 using Content.Shared.Verbs;
 using Content.Shared.Wires;
+using Content.Server.Body.Systems;
 using Robust.Server.Containers;
 using Robust.Server.GameObjects;
 using Robust.Shared.Containers;
index a2252fa395a069dd96f7f525933304509fdc608d..21827c105f9904f4a87b0cf159443f888e42821a 100644 (file)
@@ -1,4 +1,5 @@
 using Content.Server.Atmos.EntitySystems;
+using Content.Server.Body.Systems;
 using Content.Server.Medical.Components;
 using Content.Shared.Medical.Cryogenics;
 
index 9bc49165eede411fbb939a2aa630616aa885f525..176fd9b9ed21a7711369fc9f9e3814f5f75366c8 100644 (file)
@@ -1,5 +1,6 @@
 using System.Diagnostics.CodeAnalysis;
 using Content.Server.Atmos.EntitySystems;
+using Content.Server.Body.Systems;
 using Content.Server.Construction;
 using Content.Server.Construction.Components;
 using Content.Server.Storage.Components;
index 68db360231089a320be6866af3e6f0efe61b9845..b917dd692d7e55c8c83b8ca3500b3eb3da755b5d 100644 (file)
@@ -9,6 +9,7 @@ namespace Content.Shared.Alert
     {
         Error,
         LowOxygen,
+        LowNitrogen,
         LowPressure,
         HighPressure,
         Fire,
index 1e9b8a2017437dec0019b10d0d8ac8ad6569f767..795d740141b9bbe2965fbafe5e30beb01794ce7c 100644 (file)
@@ -1,6 +1,9 @@
 alerts-low-oxygen-name = [color=red]Low Oxygen[/color]
 alerts-low-oxygen-desc = There is [color=red]not enough oxygen[/color] in the air you are breathing. Put on [color=green]internals[/color].
 
+alerts-low-nitrogen-name = [color=red]Low Nitrogen[/color]
+alerts-low-nitrogen-desc = There is [color=red]not enough nitrogen[/color] in the air you are breathing. Put on [color=green]internals[/color].
+
 alerts-high-toxin-name = [color=red]High Toxin Level[/color]
 alerts-high-toxin-desc = There are [color=red]too many toxins[/color] in the air you are breathing. Put on [color=green]internals[/color] or get away.
 
index bc3137b07f4e33da302cf70631f4b8ae096b48c6..2d1c9062e61d2c3a5c575c055f4ac9688fbcea07 100644 (file)
   name: alerts-low-oxygen-name
   description: alerts-low-oxygen-desc
 
+- type: alert
+  id: LowNitrogen
+  category: Breathing
+  icons:
+    - sprite: /Textures/Interface/Alerts/breathing.rsi
+      state: not_enough_nitro
+  name: alerts-low-nitrogen-name
+  description: alerts-low-nitrogen-desc
+
 - type: alert
   id: Toxins
   category: Toxins
index f5d94554498734f01cab8f5d3088a8f0536614bd..3bdb1aef8f1c90f8cce6168e6f96c6322c81cea5 100644 (file)
@@ -37,6 +37,7 @@
         - state: lung-l-slime
         - state: lung-r-slime
     - type: Lung
+      Alert: LowNitrogen
     - type: Metabolizer
       removeEmpty: true
       solutionOnBody: false
index a532c1223a3fdbe21aecdcf5fff9a04cbae4df1e..46b18010770e5584ad912825a80225dc645fafee 100644 (file)
@@ -43,6 +43,7 @@
       - state: lung-l-slime
       - state: lung-r-slime
   - type: Lung
+    alert: LowNitrogen
   - type: Metabolizer
     removeEmpty: true
     solutionOnBody: false
index b9280204443d438f8a29e359d0076f7987544edb..1b4d12116f86f0f07a183181ef2c56158ac367c6 100644 (file)
@@ -5,4 +5,5 @@
   components:
   - type: Metabolizer
     metabolizerTypes: [ Vox ]
-
+  - type: Lung
+    alert: LowNitrogen