diff --git a/Content.Server/Disease/DiseaseSystem.cs b/Content.Server/Disease/DiseaseSystem.cs index 443ae721a7..c9ba3e75f6 100644 --- a/Content.Server/Disease/DiseaseSystem.cs +++ b/Content.Server/Disease/DiseaseSystem.cs @@ -9,6 +9,7 @@ using Content.Server.Popups; using Content.Shared.Clothing.Components; using Content.Shared.Disease; using Content.Shared.Disease.Components; +using Content.Shared.Disease.Events; using Content.Shared.Examine; using Content.Shared.IdentityManagement; using Content.Shared.Interaction; @@ -17,6 +18,8 @@ using Content.Shared.Inventory; using Content.Shared.Inventory.Events; using Content.Shared.MobState.Components; using Content.Shared.Rejuvenate; +using Robust.Shared.Audio; +using Robust.Server.GameObjects; using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Random; @@ -31,6 +34,7 @@ namespace Content.Server.Disease /// public sealed class DiseaseSystem : EntitySystem { + [Dependency] private readonly AudioSystem _audioSystem = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly ISerializationManager _serializationManager = default!; [Dependency] private readonly IRobustRandom _random = default!; @@ -264,7 +268,7 @@ namespace Content.Server.Disease { if (TryComp(uid, out var carrier)) { - SneezeCough(uid, _random.Pick(carrier.Diseases), string.Empty); + SneezeCough(uid, _random.Pick(carrier.Diseases), string.Empty, null); } } @@ -430,24 +434,34 @@ namespace Content.Server.Disease } /// - /// Plays a sneeze/cough popup if applicable + /// Raises an event for systems to cancel the snough if needed + /// Plays a sneeze/cough sound and popup if applicable /// and then tries to infect anyone in range /// if the snougher is not wearing a mask. /// - public void SneezeCough(EntityUid uid, DiseasePrototype? disease, string snoughMessage, bool airTransmit = true, TransformComponent? xform = null) + public bool SneezeCough(EntityUid uid, DiseasePrototype? disease, string snoughMessage, SoundSpecifier? snoughSound, bool airTransmit = true, TransformComponent? xform = null) { - if (!Resolve(uid, ref xform)) return; + if (!Resolve(uid, ref xform)) return false; + + if (_mobStateSystem.IsDead(uid)) return false; + + var attemptSneezeCoughEvent = new AttemptSneezeCoughEvent(uid, snoughMessage, snoughSound); + RaiseLocalEvent(uid, ref attemptSneezeCoughEvent); + if (attemptSneezeCoughEvent.Cancelled) return false; if (!string.IsNullOrEmpty(snoughMessage)) _popupSystem.PopupEntity(Loc.GetString(snoughMessage, ("person", Identity.Entity(uid, EntityManager))), uid, Filter.Pvs(uid)); + if (snoughSound != null) + _audioSystem.PlayPvs(snoughSound, uid); + if (disease is not { Infectious: true } || !airTransmit) - return; + return true; if (_inventorySystem.TryGetSlotEntity(uid, "mask", out var maskUid) && EntityManager.TryGetComponent(maskUid, out var blocker) && blocker.Enabled) - return; + return true; var carrierQuery = GetEntityQuery(); @@ -458,6 +472,7 @@ namespace Content.Server.Disease TryInfect(carrier, disease, 0.3f); } + return true; } /// diff --git a/Content.Server/Disease/Effects/DiseaseSnough.cs b/Content.Server/Disease/Effects/DiseaseSnough.cs index 06fd93e782..990f60799b 100644 --- a/Content.Server/Disease/Effects/DiseaseSnough.cs +++ b/Content.Server/Disease/Effects/DiseaseSnough.cs @@ -1,8 +1,6 @@ using Content.Shared.Disease; using JetBrains.Annotations; -using Content.Shared.Audio; using Robust.Shared.Audio; -using Robust.Shared.Player; namespace Content.Server.Disease { @@ -13,6 +11,7 @@ namespace Content.Server.Disease [UsedImplicitly] public sealed class DiseaseSnough : DiseaseEffect { + /// /// Message to play when snoughing /// @@ -24,6 +23,7 @@ namespace Content.Server.Disease /// [DataField("snoughSound")] public SoundSpecifier? SnoughSound; + /// /// Whether to spread the disease through the air /// @@ -32,9 +32,7 @@ namespace Content.Server.Disease public override void Effect(DiseaseEffectArgs args) { - if (SnoughSound != null) - SoundSystem.Play(SnoughSound.GetSound(), Filter.Pvs(args.DiseasedEntity), args.DiseasedEntity, AudioHelpers.WithVariation(0.2f)); - EntitySystem.Get().SneezeCough(args.DiseasedEntity, args.Disease, SnoughMessage, AirTransmit); + EntitySystem.Get().SneezeCough(args.DiseasedEntity, args.Disease, SnoughMessage, SnoughSound, AirTransmit); } } } diff --git a/Content.Server/Traits/Assorted/UncontrollableSnoughSystem.cs b/Content.Server/Traits/Assorted/UncontrollableSnoughSystem.cs index 266dc5a8cc..5797e219f2 100644 --- a/Content.Server/Traits/Assorted/UncontrollableSnoughSystem.cs +++ b/Content.Server/Traits/Assorted/UncontrollableSnoughSystem.cs @@ -1,8 +1,4 @@ using Content.Server.Disease; -using Content.Shared.Audio; -using Robust.Server.GameObjects; -using Robust.Shared.Audio; -using Robust.Shared.Player; using Robust.Shared.Random; namespace Content.Server.Traits.Assorted; @@ -14,7 +10,6 @@ public sealed class UncontrollableSnoughSystem : EntitySystem { [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly DiseaseSystem _diseaseSystem = default!; - [Dependency] private readonly AudioSystem _audioSystem = default!; /// public override void Initialize() @@ -43,10 +38,7 @@ public sealed class UncontrollableSnoughSystem : EntitySystem snough.NextIncidentTime += _random.NextFloat(snough.TimeBetweenIncidents.X, snough.TimeBetweenIncidents.Y); - if (snough.SnoughSound != null) - _audioSystem.PlayPvs(snough.SnoughSound, snough.Owner); - - _diseaseSystem.SneezeCough(snough.Owner, null, snough.SnoughMessage, false); + _diseaseSystem.SneezeCough(snough.Owner, null, snough.SnoughMessage, snough.SnoughSound, false); } } } diff --git a/Content.Server/Zombies/ZombieSystem.cs b/Content.Server/Zombies/ZombieSystem.cs index d31ee28067..28e61cac04 100644 --- a/Content.Server/Zombies/ZombieSystem.cs +++ b/Content.Server/Zombies/ZombieSystem.cs @@ -1,21 +1,24 @@ using System.Linq; -using Robust.Shared.Random; using Content.Server.Body.Systems; +using Content.Server.Chat.Systems; +using Content.Server.Disease; using Content.Server.Disease.Components; using Content.Server.Drone.Components; -using Content.Shared.Chemistry.Components; -using Content.Shared.MobState.Components; -using Content.Server.Disease; -using Content.Shared.Inventory; -using Content.Shared.MobState; using Content.Server.Inventory; -using Robust.Shared.Prototypes; using Content.Server.Speech; +using Content.Shared.Bed.Sleep; +using Content.Shared.Chemistry.Components; using Content.Server.Chat.Systems; using Content.Shared.Bed.Sleep; using Content.Shared.Damage; +using Content.Shared.Disease.Events; +using Content.Shared.Inventory; +using Content.Shared.MobState; +using Content.Shared.MobState.Components; using Content.Shared.Weapons.Melee.Events; using Content.Shared.Zombies; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; namespace Content.Server.Zombies { @@ -37,6 +40,7 @@ namespace Content.Server.Zombies SubscribeLocalEvent(OnMeleeHit); SubscribeLocalEvent(OnMobState); SubscribeLocalEvent(OnDamage); + SubscribeLocalEvent(OnSneeze); SubscribeLocalEvent(OnSleepAttempt); } @@ -60,6 +64,11 @@ namespace Content.Server.Zombies DoGroan(uid, component); } + private void OnSneeze(EntityUid uid, ActiveZombieComponent component, ref AttemptSneezeCoughEvent args) + { + args.Cancelled = true; + } + private float GetZombieInfectionChance(EntityUid uid, ZombieComponent component) { var baseChance = component.MaxZombieInfectionChance; diff --git a/Content.Shared/Disease/Events/AttemptSneezeCoughEvent.cs b/Content.Shared/Disease/Events/AttemptSneezeCoughEvent.cs new file mode 100644 index 0000000000..6a1d9816bf --- /dev/null +++ b/Content.Shared/Disease/Events/AttemptSneezeCoughEvent.cs @@ -0,0 +1,10 @@ +using Robust.Shared.Audio; + +namespace Content.Shared.Disease.Events; + +/// +/// Raised by an entity about to sneeze/cough. +/// Set Cancelled to true on event handling to suppress the sneeze +/// +[ByRefEvent] +public record struct AttemptSneezeCoughEvent(EntityUid uid, string SnoughMessage, SoundSpecifier? SnoughSound, bool Cancelled = false); diff --git a/Content.Shared/MobState/EntitySystems/SharedMobStateSystem.cs b/Content.Shared/MobState/EntitySystems/SharedMobStateSystem.cs index f7e62bc29d..3e98da62ea 100644 --- a/Content.Shared/MobState/EntitySystems/SharedMobStateSystem.cs +++ b/Content.Shared/MobState/EntitySystems/SharedMobStateSystem.cs @@ -5,6 +5,7 @@ using Content.Shared.Alert; using Content.Shared.Bed.Sleep; using Content.Shared.Damage; using Content.Shared.Database; +using Content.Shared.Disease.Events; using Content.Shared.DragDrop; using Content.Shared.Emoting; using Content.Shared.FixedPoint; @@ -58,6 +59,7 @@ namespace Content.Shared.MobState.EntitySystems SubscribeLocalEvent(OnMoveAttempt); SubscribeLocalEvent(OnStandAttempt); SubscribeLocalEvent(OnSleepAttempt); + SubscribeLocalEvent(OnSneezeAttempt); SubscribeLocalEvent(OnStateChanged); // Note that there's no check for Down attempts because if a mob's in crit or dead, they can be downed... } @@ -68,6 +70,12 @@ namespace Content.Shared.MobState.EntitySystems args.Cancelled = true; } + private void OnSneezeAttempt(EntityUid uid, MobStateComponent component, ref AttemptSneezeCoughEvent args) + { + if(IsDead(uid, component)) + args.Cancelled = true; + } + private void OnGettingStripped(EntityUid uid, MobStateComponent component, BeforeGettingStrippedEvent args) { // Incapacitated or dead targets get stripped two or three times as fast. Makes stripping corpses less tedious.