]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
RespiratorSystem Cleanup (#38572)
authorPrincess Cheeseballs <66055347+Princess-Cheeseballs@users.noreply.github.com>
Fri, 27 Jun 2025 02:33:43 +0000 (19:33 -0700)
committerGitHub <noreply@github.com>
Fri, 27 Jun 2025 02:33:43 +0000 (22:33 -0400)
* Respirator Debodied

* Forgot about alerts (also respirator testa and events)

* Fix Urist eating air and not giving it back

* Stop nuke ops from taking in a breath then taking in a second breath causing them to get a headache from carbon dioxide poisoning and failing TryStopNukeOpsFromConstantlyFailing();

* Consts are smelly,

* Actually we don't need to raise the entity, just the component

* Don't forget to remove the unused code today, said me yesterday

* Remove all fallbacks

* Debody that too

---------

Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com>
Content.Server/Body/Components/RespiratorComponent.cs
Content.Server/Body/Systems/InternalsSystem.cs
Content.Server/Body/Systems/LungSystem.cs
Content.Server/Body/Systems/RespiratorSystem.cs

index a81062362aebe326d4c27520659041447f5f06be..19585e9f0016ab367762d12a82801cf911598a8a 100644 (file)
@@ -1,4 +1,6 @@
 using Content.Server.Body.Systems;
+using Content.Shared.Alert;
+using Content.Shared.Atmos;
 using Content.Shared.Chat.Prototypes;
 using Content.Shared.Damage;
 using Robust.Shared.Prototypes;
@@ -9,6 +11,28 @@ namespace Content.Server.Body.Components
     [RegisterComponent, Access(typeof(RespiratorSystem))]
     public sealed partial class RespiratorComponent : Component
     {
+        /// <summary>
+        ///     Gas container for this entity
+        /// </summary>
+        [DataField]
+        public GasMixture Air = new()
+        {
+            Volume = 6, // 6 liters, the average lung capacity for a human according to Google
+            Temperature = Atmospherics.NormalBodyTemperature
+        };
+
+        /// <summary>
+        ///     Volume of our breath in liters
+        /// </summary>
+        [DataField]
+        public float BreathVolume = Atmospherics.BreathVolume;
+
+        /// <summary>
+        ///     How much of the gas we inhale is metabolized? Value range is (0, 1]
+        /// </summary>
+        [DataField]
+        public float Ratio = 1.0f;
+
         /// <summary>
         ///     The next time that this body will inhale or exhale.
         /// </summary>
index 93dedf5ffffc698eded0fb18956a6a5b519bb846..72c91fe2bea7f567322b835639536705802dc98e 100644 (file)
@@ -1,4 +1,5 @@
 using Content.Server.Atmos.EntitySystems;
+using Content.Server.Body.Components;
 using Content.Server.Popups;
 using Content.Shared.Alert;
 using Content.Shared.Atmos;
@@ -58,7 +59,7 @@ public sealed class InternalsSystem : SharedInternalsSystem
         if (AreInternalsWorking(ent))
         {
             var gasTank = Comp<GasTankComponent>(ent.Comp.GasTankEntity!.Value);
-            args.Gas = _gasTank.RemoveAirVolume((ent.Comp.GasTankEntity.Value, gasTank), Atmospherics.BreathVolume);
+            args.Gas = _gasTank.RemoveAirVolume((ent.Comp.GasTankEntity.Value, gasTank), args.Respirator.BreathVolume);
             // TODO: Should listen to gas tank updates instead I guess?
             _alerts.ShowAlert(ent, ent.Comp.InternalsAlert, GetSeverity(ent));
         }
index 273a8466ca0b52fc781061cfcd9e042ea4b53c50..fdc071c5f178251bda23cd8449f653465ab37fe0 100644 (file)
@@ -63,6 +63,9 @@ public sealed class LungSystem : EntitySystem
         _solutionContainerSystem.UpdateChemicals(lung.Solution.Value);
     }
 
+    /* This should really be moved to somewhere in the atmos system and modernized,
+     so that other systems, like CondenserSystem, can use it.
+     */
     private void GasToReagent(GasMixture gas, Solution solution)
     {
         foreach (var gasId in Enum.GetValues<Gas>())
index 1b4cf4d69894f30ac6dc5270cdc17fb7e3de149a..bc571087206a4e0248444a6c9774ced019343612 100644 (file)
@@ -50,6 +50,8 @@ public sealed class RespiratorSystem : EntitySystem
         SubscribeLocalEvent<RespiratorComponent, MapInitEvent>(OnMapInit);
         SubscribeLocalEvent<RespiratorComponent, EntityUnpausedEvent>(OnUnpaused);
         SubscribeLocalEvent<RespiratorComponent, ApplyMetabolicMultiplierEvent>(OnApplyMetabolicMultiplier);
+        SubscribeLocalEvent<BodyComponent, InhaledGasEvent>(OnGasInhaled);
+        SubscribeLocalEvent<BodyComponent, ExhaledGasEvent>(OnGasExhaled);
     }
 
     private void OnMapInit(Entity<RespiratorComponent> ent, ref MapInitEvent args)
@@ -77,18 +79,18 @@ public sealed class RespiratorSystem : EntitySystem
             if (_mobState.IsDead(uid))
                 continue;
 
-            UpdateSaturation(uid, -(float) respirator.UpdateInterval.TotalSeconds, respirator);
+            UpdateSaturation(uid, -(float)respirator.UpdateInterval.TotalSeconds, respirator);
 
             if (!_mobState.IsIncapacitated(uid)) // cannot breathe in crit.
             {
                 switch (respirator.Status)
                 {
                     case RespiratorStatus.Inhaling:
-                        Inhale(uid, body);
+                        Inhale(uid);
                         respirator.Status = RespiratorStatus.Exhaling;
                         break;
                     case RespiratorStatus.Exhaling:
-                        Exhale(uid, body);
+                        Exhale(uid);
                         respirator.Status = RespiratorStatus.Inhaling;
                         break;
                 }
@@ -99,7 +101,10 @@ public sealed class RespiratorSystem : EntitySystem
                 if (_gameTiming.CurTime >= respirator.LastGaspEmoteTime + respirator.GaspEmoteCooldown)
                 {
                     respirator.LastGaspEmoteTime = _gameTiming.CurTime;
-                    _chat.TryEmoteWithChat(uid, respirator.GaspEmote, ChatTransmitRange.HideChat, ignoreActionBlocker: true);
+                    _chat.TryEmoteWithChat(uid,
+                        respirator.GaspEmote,
+                        ChatTransmitRange.HideChat,
+                        ignoreActionBlocker: true);
                 }
 
                 TakeSuffocationDamage((uid, respirator));
@@ -112,68 +117,54 @@ public sealed class RespiratorSystem : EntitySystem
         }
     }
 
-    public void Inhale(EntityUid uid, BodyComponent? body = null)
+    public bool Inhale(Entity<RespiratorComponent?> entity)
     {
-        if (!Resolve(uid, ref body, logMissing: false))
-            return;
-
-        var organs = _bodySystem.GetBodyOrganEntityComps<LungComponent>((uid, body));
+        if (!Resolve(entity, ref entity.Comp, logMissing: false))
+            return false;
 
         // Inhale gas
-        var ev = new InhaleLocationEvent();
-        RaiseLocalEvent(uid, ref ev);
+        var ev = new InhaleLocationEvent
+        {
+            Respirator = entity.Comp,
+        };
+        RaiseLocalEvent(entity, ref ev);
 
-        ev.Gas ??= _atmosSys.GetContainingMixture(uid, excite: true);
+        ev.Gas ??= _atmosSys.GetContainingMixture(entity.Owner, excite: true);
 
         if (ev.Gas is null)
         {
-            return;
+            return false;
         }
 
-        var actualGas = ev.Gas.RemoveVolume(Atmospherics.BreathVolume);
+        var gas = ev.Gas.RemoveVolume(entity.Comp.BreathVolume);
 
-        var lungRatio = 1.0f / organs.Count;
-        var gas = organs.Count == 1 ? actualGas : actualGas.RemoveRatio(lungRatio);
-        foreach (var (organUid, lung, _) in organs)
-        {
-            // Merge doesn't remove gas from the giver.
-            _atmosSys.Merge(lung.Air, gas);
-            _lungSystem.GasToReagent(organUid, lung);
-        }
+        var inhaleEv = new InhaledGasEvent(gas);
+        RaiseLocalEvent(entity, ref inhaleEv);
+
+        return inhaleEv.Handled && inhaleEv.Succeeded;
     }
 
-    public void Exhale(EntityUid uid, BodyComponent? body = null)
+    public void Exhale(Entity<RespiratorComponent?> entity)
     {
-        if (!Resolve(uid, ref body, logMissing: false))
+        if (!Resolve(entity, ref entity.Comp, logMissing: false))
             return;
 
-        var organs = _bodySystem.GetBodyOrganEntityComps<LungComponent>((uid, body));
-
         // exhale gas
 
         var ev = new ExhaleLocationEvent();
-        RaiseLocalEvent(uid, ref ev, broadcast: false);
+        RaiseLocalEvent(entity, ref ev, broadcast: false);
 
         if (ev.Gas is null)
         {
-            ev.Gas = _atmosSys.GetContainingMixture(uid, excite: true);
+            ev.Gas = _atmosSys.GetContainingMixture(entity.Owner, excite: true);
 
             // 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;
         }
 
-        var outGas = new GasMixture(ev.Gas.Volume);
-        foreach (var (organUid, lung, _) in organs)
-        {
-            _atmosSys.Merge(outGas, lung.Air);
-            lung.Air.Clear();
-
-            if (_solutionContainerSystem.ResolveSolution(organUid, lung.SolutionName, ref lung.Solution))
-                _solutionContainerSystem.RemoveAllSolution(lung.Solution.Value);
-        }
-
-        _atmosSys.Merge(ev.Gas, outGas);
+        var exhaleEv = new ExhaledGasEvent(ev.Gas);
+        RaiseLocalEvent(entity, ref exhaleEv);
     }
 
     /// <summary>
@@ -199,14 +190,15 @@ public sealed class RespiratorSystem : EntitySystem
         if (!Resolve(ent, ref ent.Comp))
             return false;
 
-        var ev = new InhaleLocationEvent();
-        RaiseLocalEvent(ent, ref ev);
-
-        var gas = ev.Gas ?? _atmosSys.GetContainingMixture(ent.Owner);
-        if (gas == null)
+        if (!Inhale(ent))
             return false;
 
-        return CanMetabolizeGas(ent, gas);
+        // If we don't have a body we can't be poisoned by gas, yet...
+        var success = TryMetabolizeGas((ent, ent.Comp));
+
+        // Don't keep that gas in our lungs lest it poisons a poor nuclear operative.
+        Exhale(ent);
+        return success;
     }
 
     /// <summary>
@@ -224,7 +216,7 @@ public sealed class RespiratorSystem : EntitySystem
 
         gas = new GasMixture(gas);
         var lungRatio = 1.0f / organs.Count;
-        gas.Multiply(MathF.Min(lungRatio * gas.Volume/Atmospherics.BreathVolume, lungRatio));
+        gas.Multiply(MathF.Min(lungRatio * gas.Volume / ent.Comp.BreathVolume, lungRatio));
         var solution = _lungSystem.GasToReagent(gas);
 
         float saturation = 0;
@@ -238,6 +230,71 @@ public sealed class RespiratorSystem : EntitySystem
         return saturation > ent.Comp.UpdateInterval.TotalSeconds;
     }
 
+    public bool TryInhaleGasToBody(Entity<BodyComponent?> entity, GasMixture gas)
+    {
+        if (!Resolve(entity, ref entity.Comp))
+            return false;
+
+        var organs = _bodySystem.GetBodyOrganEntityComps<LungComponent>((entity, entity.Comp));
+        if (organs.Count == 0)
+            return false;
+
+        var lungRatio = 1.0f / organs.Count;
+        var splitGas = organs.Count == 1 ? gas : gas.RemoveRatio(lungRatio);
+        foreach (var (organUid, lung, _) in organs)
+        {
+            // Merge doesn't remove gas from the giver.
+            _atmosSys.Merge(lung.Air, splitGas);
+            _lungSystem.GasToReagent(organUid, lung);
+        }
+
+        return true;
+    }
+
+    public void RemoveGasFromBody(Entity<BodyComponent> ent, GasMixture gas)
+    {
+        var outGas = new GasMixture(gas.Volume);
+
+        var organs = _bodySystem.GetBodyOrganEntityComps<LungComponent>((ent, ent.Comp));
+        if (organs.Count == 0)
+            return;
+
+        foreach (var (organUid, lung, _) in organs)
+        {
+            _atmosSys.Merge(outGas, lung.Air);
+            lung.Air.Clear();
+
+            if (_solutionContainerSystem.ResolveSolution(organUid, lung.SolutionName, ref lung.Solution))
+                _solutionContainerSystem.RemoveAllSolution(lung.Solution.Value);
+        }
+
+        _atmosSys.Merge(gas, outGas);
+    }
+
+    /// <summary>
+    /// Tries to safely metabolize the current solutions in a body's lungs.
+    /// </summary>
+    private bool TryMetabolizeGas(Entity<RespiratorComponent, BodyComponent?> ent)
+    {
+        if (!Resolve(ent, ref ent.Comp2))
+            return false;
+
+        var organs = _bodySystem.GetBodyOrganEntityComps<LungComponent>((ent, null));
+        if (organs.Count == 0)
+            return false;
+
+        float saturation = 0;
+        foreach (var organ in organs)
+        {
+            var solution = _lungSystem.GasToReagent(organ.Comp1.Air);
+            saturation += GetSaturation(solution, organ.Owner, out var toxic);
+            if (toxic)
+                return false;
+        }
+
+        return saturation > ent.Comp1.UpdateInterval.TotalSeconds;
+    }
+
     /// <summary>
     /// Get the amount of saturation that would be generated if the lung were to metabolize the given solution.
     /// </summary>
@@ -301,6 +358,8 @@ public sealed class RespiratorSystem : EntitySystem
         if (ent.Comp.SuffocationCycles == 2)
             _adminLogger.Add(LogType.Asphyxiation, $"{ToPrettyString(ent):entity} started suffocating");
 
+        _damageableSys.TryChangeDamage(ent, ent.Comp.Damage, interruptsDoAfters: false);
+
         if (ent.Comp.SuffocationCycles >= ent.Comp.SuffocationCycleThreshold)
         {
             // TODO: This is not going work with multiple different lungs, if that ever becomes a possibility
@@ -310,8 +369,6 @@ public sealed class RespiratorSystem : EntitySystem
                 _alertsSystem.ShowAlert(ent, entity.Comp1.Alert);
             }
         }
-
-        _damageableSys.TryChangeDamage(ent, ent.Comp.Damage, interruptsDoAfters: false);
     }
 
     private void StopSuffocation(Entity<RespiratorComponent> ent)
@@ -319,18 +376,17 @@ public sealed class RespiratorSystem : EntitySystem
         if (ent.Comp.SuffocationCycles >= 2)
             _adminLogger.Add(LogType.Asphyxiation, $"{ToPrettyString(ent):entity} stopped suffocating");
 
+        _damageableSys.TryChangeDamage(ent, ent.Comp.DamageRecovery);
+
         // TODO: This is not going work with multiple different lungs, if that ever becomes a possibility
         var organs = _bodySystem.GetBodyOrganEntityComps<LungComponent>((ent, null));
         foreach (var entity in organs)
         {
             _alertsSystem.ClearAlert(ent, entity.Comp1.Alert);
         }
-
-        _damageableSys.TryChangeDamage(ent, ent.Comp.DamageRecovery);
     }
 
-    public void UpdateSaturation(EntityUid uid, float amount,
-        RespiratorComponent? respirator = null)
+    public void UpdateSaturation(EntityUid uid, float amount, RespiratorComponent? respirator = null)
     {
         if (!Resolve(uid, ref respirator, false))
             return;
@@ -362,10 +418,30 @@ public sealed class RespiratorSystem : EntitySystem
         ent.Comp.MaxSaturation /= args.Multiplier;
         ent.Comp.MinSaturation /= args.Multiplier;
     }
+
+    private void OnGasInhaled(Entity<BodyComponent> entity, ref InhaledGasEvent args)
+    {
+        args.Handled = true;
+
+        args.Succeeded = TryInhaleGasToBody((entity, entity.Comp), args.Gas);
+    }
+
+    private void OnGasExhaled(Entity<BodyComponent> entity, ref ExhaledGasEvent args)
+    {
+        args.Handled = true;
+
+        RemoveGasFromBody(entity, args.Gas);
+    }
 }
 
 [ByRefEvent]
-public record struct InhaleLocationEvent(GasMixture? Gas);
+public record struct InhaleLocationEvent(GasMixture? Gas, RespiratorComponent Respirator);
 
 [ByRefEvent]
 public record struct ExhaleLocationEvent(GasMixture? Gas);
+
+[ByRefEvent]
+public record struct InhaledGasEvent(GasMixture Gas, bool Handled = false, bool Succeeded = false);
+
+[ByRefEvent]
+public record struct ExhaledGasEvent(GasMixture Gas, bool Handled = false);