]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Pepper makes you cough (#36358)
authorāda <ss.adasts@gmail.com>
Mon, 19 May 2025 18:23:38 +0000 (13:23 -0500)
committerGitHub <noreply@github.com>
Mon, 19 May 2025 18:23:38 +0000 (11:23 -0700)
Content.Server/Body/Systems/RespiratorSystem.cs
Content.Server/EntityEffects/EffectConditions/BreathingCondition.cs [new file with mode: 0644]
Content.Server/EntityEffects/EffectConditions/InternalsCondition.cs [new file with mode: 0644]
Content.Server/EntityEffects/Effects/Emote.cs
Resources/Locale/en-US/guidebook/chemistry/conditions.ftl
Resources/Locale/en-US/guidebook/chemistry/effects.ftl
Resources/Prototypes/Reagents/Consumable/Food/ingredients.yml
Resources/Prototypes/Reagents/fun.yml
Resources/Prototypes/Reagents/toxins.yml

index 6209f00419d39063b894c889a05c1111d1a68565..bebf92f977611687b5c4bd7a2ddf6c0bab118c38 100644 (file)
@@ -173,6 +173,20 @@ public sealed class RespiratorSystem : EntitySystem
         _atmosSys.Merge(ev.Gas, outGas);
     }
 
+    /// <summary>
+    /// Returns true if the entity is above their SuffocationThreshold and alive.
+    /// </summary>
+    public bool IsBreathing(Entity<RespiratorComponent?> ent)
+    {
+        if (_mobState.IsIncapacitated(ent))
+            return false;
+
+        if (!Resolve(ent, ref ent.Comp))
+            return false;
+
+        return (ent.Comp.Saturation > ent.Comp.SuffocationThreshold);
+    }
+
     /// <summary>
     /// Check whether or not an entity can metabolize inhaled air without suffocating or taking damage (i.e., no toxic
     /// gasses).
diff --git a/Content.Server/EntityEffects/EffectConditions/BreathingCondition.cs b/Content.Server/EntityEffects/EffectConditions/BreathingCondition.cs
new file mode 100644 (file)
index 0000000..d87e686
--- /dev/null
@@ -0,0 +1,33 @@
+using Content.Server.Body.Components;
+using Content.Server.Body.Systems;
+using Content.Shared.EntityEffects;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server.EntityEffects.EffectConditions;
+
+/// <summary>
+///     Condition for if the entity is successfully breathing.
+/// </summary>
+public sealed partial class Breathing : EntityEffectCondition
+{
+    /// <summary>
+    ///     If true, the entity must not have trouble breathing to pass.
+    /// </summary>
+    [DataField]
+    public bool IsBreathing = true;
+
+    public override bool Condition(EntityEffectBaseArgs args)
+    {
+        if (!args.EntityManager.TryGetComponent(args.TargetEntity, out RespiratorComponent? respiratorComp))
+            return !IsBreathing; // They do not breathe.
+
+        var breathingState = args.EntityManager.System<RespiratorSystem>().IsBreathing((args.TargetEntity, respiratorComp));
+        return IsBreathing == breathingState;
+    }
+
+    public override string GuidebookExplanation(IPrototypeManager prototype)
+    {
+        return Loc.GetString("reagent-effect-condition-guidebook-breathing",
+                            ("isBreathing", IsBreathing));
+    }
+}
diff --git a/Content.Server/EntityEffects/EffectConditions/InternalsCondition.cs b/Content.Server/EntityEffects/EffectConditions/InternalsCondition.cs
new file mode 100644 (file)
index 0000000..1bc5b26
--- /dev/null
@@ -0,0 +1,31 @@
+using Content.Shared.Body.Components;
+using Content.Shared.EntityEffects;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server.EntityEffects.EffectConditions;
+
+/// <summary>
+///     Condition for if the entity is or isn't wearing internals.
+/// </summary>
+public sealed partial class Internals : EntityEffectCondition
+{
+    /// <summary>
+    ///     To pass, the entity's internals must have this same state.
+    /// </summary>
+    [DataField]
+    public bool UsingInternals = true;
+
+    public override bool Condition(EntityEffectBaseArgs args)
+    {
+        if (!args.EntityManager.TryGetComponent(args.TargetEntity, out InternalsComponent? internalsComp))
+            return !UsingInternals; // They have no internals to wear.
+
+        var internalsState = internalsComp.GasTankEntity == null;
+        return UsingInternals == internalsState;
+    }
+
+    public override string GuidebookExplanation(IPrototypeManager prototype)
+    {
+        return Loc.GetString("reagent-effect-condition-guidebook-internals", ("usingInternals", UsingInternals));
+    }
+}
index 00bdaec455c30491856f0ab1ce0cf4d6316eff92..227e60a175ff31c9142b1d77e1fa2ad0c3996bda 100644 (file)
@@ -8,34 +8,49 @@ using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototy
 namespace Content.Server.EntityEffects.Effects;
 
 /// <summary>
-///     Tries to force someone to emote (scream, laugh, etc). Still respects whitelists/blacklists and other limits of the specified emote unless forced.
+///     Tries to force someone to emote (scream, laugh, etc). Still respects whitelists/blacklists and other limits unless specially forced.
 /// </summary>
 [UsedImplicitly]
 public sealed partial class Emote : EntityEffect
 {
-    [DataField("emote", customTypeSerializer: typeof(PrototypeIdSerializer<EmotePrototype>))]
-    public string? EmoteId;
-
+    /// <summary>
+    ///     The emote the entity will preform.
+    /// </summary>
+    [DataField("emote", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<EmotePrototype>))]
+    public string EmoteId;
+
+    /// <summary>
+    ///     If the emote should be recorded in chat.
+    /// </summary>
     [DataField]
     public bool ShowInChat;
 
+    /// <summary>
+    ///     If the forced emote will be listed in the guidebook.
+    /// </summary>
+    [DataField]
+    public bool ShowInGuidebook;
+
+    /// <summary>
+    ///     If true, the entity will preform the emote even if they normally can't.
+    /// </summary>
     [DataField]
     public bool Force = false;
 
-    // JUSTIFICATION: Emoting is flavor, so same reason popup messages are not in here.
     protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
-        => null;
+    {
+        if (!ShowInGuidebook)
+            return null; // JUSTIFICATION: Emoting is mostly flavor, so same reason popup messages are not in here.
+
+        return Loc.GetString("reagent-effect-guidebook-emote", ("chance", Probability), ("emote", EmoteId));
+    }
 
     public override void Effect(EntityEffectBaseArgs args)
     {
-        if (EmoteId == null)
-            return;
-
         var chatSys = args.EntityManager.System<ChatSystem>();
         if (ShowInChat)
             chatSys.TryEmoteWithChat(args.TargetEntity, EmoteId, ChatTransmitRange.GhostRangeLimit, forceEmote: Force);
         else
             chatSys.TryEmoteWithoutChat(args.TargetEntity, EmoteId);
-
     }
 }
index 95aaf9126d7e0556f995e1f979b26570eda3f9c0..fe31dd62f82fd71c5c871aaabfc71964dc3dcb7d 100644 (file)
@@ -62,3 +62,15 @@ reagent-effect-condition-guidebook-has-tag =
                 } the tag {$tag}
 
 reagent-effect-condition-guidebook-this-reagent = this reagent
+
+reagent-effect-condition-guidebook-breathing =
+    the metabolizer is { $isBreathing ->
+                [true] breathing normally
+                *[false] suffocating
+               }
+
+reagent-effect-condition-guidebook-internals =
+    the metabolizer is { $usingInternals ->
+                [true] using internals
+                *[false] breathing atmospheric air
+               }
index ba6ae96c82cea009d9259e0e071328536d807204..a5ddb03f0a932dff61711728db8974c0966ef656 100644 (file)
@@ -258,6 +258,12 @@ reagent-effect-guidebook-electrocute =
         *[other] electrocute
     } the metabolizer for {NATURALFIXED($time, 3)} {MANY("second", $time)}
 
+reagent-effect-guidebook-emote =
+    { $chance ->
+        [1] Will force
+        *[other] force
+    } the metabolizer to [bold][color=white]{$emote}[/color][/bold]
+
 reagent-effect-guidebook-extinguish-reaction =
     { $chance ->
         [1] Extinguishes
index d2cf44e6d64b2423ee009008f50a14f1694cbf2e..6e4114cecf39c4f2700dac37b43a796919a03a02 100644 (file)
   flavor: peppery
   color: black
   recognizable: true
+  metabolisms:
+    Food:
+      effects:
+      - !type:Emote
+        emote: Cough
+        showInChat: true
+        showInGuidebook: true
+        probability: 0.05
+  reactiveEffects:
+    Acidic:
+      methods: [ Touch ]
+      effects:
+      - !type:Emote
+        emote: Cough
+        showInGuidebook: true
+        conditions:
+        - !type:Breathing
+        - !type:Internals
+          usingInternals: false
 
 - type: reagent
   id: Vinegar
index adbf202c7e90504a478f5b98b14a26918714f9e7..df02839940dc864865f8c4681b572e09c4062981 100644 (file)
       effects:
       - !type:Emote
         emote: Laugh
+        showInGuidebook: true
         probability: 0.3
       - !type:PopupMessage
         type: Local
       - !type:Emote
         emote: Weh
         showInChat: true
+        showInGuidebook: true
         force: true
         probability: 0.5
       - !type:Polymorph
       - !type:Emote
         emote: Hew
         showInChat: true
+        showInGuidebook: true
         force: true
         probability: 0.5
       - !type:Polymorph
index 27057cb7158070de5f30e2338ea228c53c417a4e..a06224e9dd0f2e822a59a6dd74eec1bc3fbf4eb1 100644 (file)
       - !type:Emote
         emote: Honk
         showInChat: true
+        showInGuidebook: true
         force: true
         probability: 0.2
       - !type:HealthChange