From 3adfd0023f43313e5569b55a5637aa6629bd8a15 Mon Sep 17 00:00:00 2001 From: Rxup Date: Sat, 23 Mar 2024 18:27:16 +0300 Subject: [PATCH] Disease fixes (#530) --- .../Tests/Backmen/Disease/TryAddDisease.cs | 9 +- .../Components/DiseaseInfectionSpeadEvent.cs | 10 +++ .../Disease/Cures/DiseaseBedrestCure.cs | 25 ++++-- .../Cures/DiseaseBodyTemperatureCure.cs | 17 +++- .../Disease/Cures/DiseaseCureSystem.cs | 48 +++++------ .../Disease/Cures/DiseaseJustWaitCure.cs | 17 +++- .../Disease/Cures/DiseaseReagentCure.cs | 20 +++-- .../Backmen/Disease/DiseaseInfectionSpread.cs | 32 +++++++ .../Backmen/Disease/DiseaseSystem.cs | 63 +++++++------- .../Disease/Effects/DiseaseAddComponent.cs | 15 +++- .../Disease/Effects/DiseaseAdjustReagent.cs | 23 +++-- .../Effects/DiseaseGenericStatusEffect.cs | 23 +++-- .../Disease/Effects/DiseaseHealthChange.cs | 13 ++- .../Disease/Effects/DiseasePolymorph.cs | 28 ++++-- .../Backmen/Disease/Effects/DiseasePopUp.cs | 19 +++-- .../Backmen/Disease/Effects/DiseaseSnough.cs | 13 ++- .../Backmen/Disease/Effects/DiseaseVomit.cs | 13 ++- .../Backmen/Disease/Effects/EffectSystem.cs | 85 ++++++++++--------- Content.Shared/Backmen/CCVar/CCVars.cs | 3 + Content.Shared/Backmen/Disease/DiseaseCure.cs | 10 ++- .../Backmen/Disease/DiseaseEffect.cs | 9 +- 21 files changed, 339 insertions(+), 156 deletions(-) create mode 100644 Content.Server/Backmen/Disease/Components/DiseaseInfectionSpeadEvent.cs create mode 100644 Content.Server/Backmen/Disease/DiseaseInfectionSpread.cs diff --git a/Content.IntegrationTests/Tests/Backmen/Disease/TryAddDisease.cs b/Content.IntegrationTests/Tests/Backmen/Disease/TryAddDisease.cs index 0b18d567cab..fd9e1efd614 100644 --- a/Content.IntegrationTests/Tests/Backmen/Disease/TryAddDisease.cs +++ b/Content.IntegrationTests/Tests/Backmen/Disease/TryAddDisease.cs @@ -37,10 +37,15 @@ await server.WaitAssertion(() => { await server.WaitAssertion(() => { - diseaseSystem.TryAddDisease(sickEntity, diseaseProto); + diseaseSystem.TryAddDisease(sickEntity, diseaseProto.ID); }); await server.WaitIdleAsync(); - server.RunTicks(1); + server.RunTicks(5); + await server.WaitAssertion(() => + { + if(!entManager.HasComponent(sickEntity)) + Assert.Fail("MobHuman has not DiseasedComponent"); + }); if (!entManager.TryGetComponent(sickEntity, out var diseaseCarrierComponent)) { Assert.Fail("MobHuman has not DiseaseCarrierComponent"); diff --git a/Content.Server/Backmen/Disease/Components/DiseaseInfectionSpeadEvent.cs b/Content.Server/Backmen/Disease/Components/DiseaseInfectionSpeadEvent.cs new file mode 100644 index 00000000000..f1647fe1fd2 --- /dev/null +++ b/Content.Server/Backmen/Disease/Components/DiseaseInfectionSpeadEvent.cs @@ -0,0 +1,10 @@ +using Content.Shared.Backmen.Disease; + +namespace Content.Server.Backmen.Disease.Components; + +public sealed class DiseaseInfectionSpreadEvent : EntityEventArgs +{ + public EntityUid Owner { get; init; } = default!; + public DiseasePrototype Disease { get; init; } = default!; + public float Range { get; init; } = default!; +} diff --git a/Content.Server/Backmen/Disease/Cures/DiseaseBedrestCure.cs b/Content.Server/Backmen/Disease/Cures/DiseaseBedrestCure.cs index 90e0c4da47e..2cd15e6c097 100644 --- a/Content.Server/Backmen/Disease/Cures/DiseaseBedrestCure.cs +++ b/Content.Server/Backmen/Disease/Cures/DiseaseBedrestCure.cs @@ -2,6 +2,7 @@ using Content.Shared.Backmen.Disease; using Content.Shared.Bed.Sleep; using Content.Shared.Buckle.Components; +using Robust.Shared.Prototypes; namespace Content.Server.Backmen.Disease.Cures; @@ -22,23 +23,33 @@ public override string CureText() { return (Loc.GetString("diagnoser-cure-bedrest", ("time", MaxLength), ("sleep", (MaxLength / SleepMultiplier)))); } + + public override object GenerateEvent(Entity ent, ProtoId disease) + { + return new DiseaseCureArgs(ent, disease, this); + } } public sealed partial class DiseaseCureSystem { - private void DiseaseBedrestCure(DiseaseCureArgs args, DiseaseBedrestCure ds) + private void DiseaseBedrestCure(Entity ent, ref DiseaseCureArgs args) { - if (!TryComp(args.DiseasedEntity, out var buckle) || - !HasComp(buckle.BuckledTo)) + if(args.Handled) + return; + + args.Handled = true; + + if (!_buckleQuery.TryGetComponent(args.DiseasedEntity, out var buckle) || + !_healOnBuckleQuery.HasComponent(buckle.BuckledTo)) return; var ticks = 1; - if (HasComp(args.DiseasedEntity)) - ticks *= ds.SleepMultiplier; + if (_sleepingComponentQuery.HasComponent(args.DiseasedEntity)) + ticks *= args.DiseaseCure.SleepMultiplier; if (buckle.Buckled) - ds.Ticker += ticks; - if (ds.Ticker >= ds.MaxLength) + args.DiseaseCure.Ticker += ticks; + if (args.DiseaseCure.Ticker >= args.DiseaseCure.MaxLength) { _disease.CureDisease(args.DiseasedEntity, args.Disease); } diff --git a/Content.Server/Backmen/Disease/Cures/DiseaseBodyTemperatureCure.cs b/Content.Server/Backmen/Disease/Cures/DiseaseBodyTemperatureCure.cs index 402fe41149b..a53f6434bee 100644 --- a/Content.Server/Backmen/Disease/Cures/DiseaseBodyTemperatureCure.cs +++ b/Content.Server/Backmen/Disease/Cures/DiseaseBodyTemperatureCure.cs @@ -1,5 +1,6 @@ using Content.Server.Temperature.Components; using Content.Shared.Backmen.Disease; +using Robust.Shared.Prototypes; namespace Content.Server.Backmen.Disease.Cures; @@ -20,16 +21,26 @@ public override string CureText() return Loc.GetString("diagnoser-cure-temp-both", ("max", Math.Round(Max)), ("min", Math.Round(Min))); } + + public override object GenerateEvent(Entity ent, ProtoId disease) + { + return new DiseaseCureArgs(ent, disease, this); + } } public sealed partial class DiseaseCureSystem { - private void DiseaseBodyTemperatureCure(DiseaseCureArgs args, DiseaseBodyTemperatureCure ds) + private void DiseaseBodyTemperatureCure(Entity ent, ref DiseaseCureArgs args) { - if (!TryComp(args.DiseasedEntity, out var temp)) + if(args.Handled) + return; + + args.Handled = true; + + if (!_temperatureQuery.TryGetComponent(args.DiseasedEntity, out var temp)) return; - if(temp.CurrentTemperature > ds.Min && temp.CurrentTemperature < float.MaxValue) + if(temp.CurrentTemperature > args.DiseaseCure.Min && temp.CurrentTemperature < float.MaxValue) { _disease.CureDisease(args.DiseasedEntity, args.Disease); } diff --git a/Content.Server/Backmen/Disease/Cures/DiseaseCureSystem.cs b/Content.Server/Backmen/Disease/Cures/DiseaseCureSystem.cs index 19d382733bd..75b2b8293bd 100644 --- a/Content.Server/Backmen/Disease/Cures/DiseaseCureSystem.cs +++ b/Content.Server/Backmen/Disease/Cures/DiseaseCureSystem.cs @@ -1,40 +1,34 @@ -using Content.Shared.Backmen.Disease; +using Content.Server.Bed.Components; +using Content.Server.Body.Components; +using Content.Server.Temperature.Components; +using Content.Shared.Backmen.Disease; +using Content.Shared.Bed.Sleep; +using Content.Shared.Buckle.Components; namespace Content.Server.Backmen.Disease.Cures; public sealed partial class DiseaseCureSystem : EntitySystem { [Dependency] private readonly DiseaseSystem _disease = default!; + private EntityQuery _buckleQuery; + private EntityQuery _healOnBuckleQuery; + private EntityQuery _sleepingComponentQuery; + private EntityQuery _bloodstreamQuery; + private EntityQuery _temperatureQuery; + public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(DoGenericCure); - } - - private void DoGenericCure(DiseaseCureArgs args) - { - if(args.Handled) - return; + SubscribeLocalEvent>(DiseaseBedrestCure); + SubscribeLocalEvent>(DiseaseBodyTemperatureCure); + SubscribeLocalEvent>(DiseaseJustWaitCure); + SubscribeLocalEvent>(DiseaseReagentCure); - switch (args.DiseaseCure) - { - case DiseaseBedrestCure ds1: - args.Handled = true; - DiseaseBedrestCure(args, ds1); - return; - case DiseaseBodyTemperatureCure ds2: - args.Handled = true; - DiseaseBodyTemperatureCure(args, ds2); - return; - case DiseaseJustWaitCure ds3: - args.Handled = true; - DiseaseJustWaitCure(args, ds3); - return; - case DiseaseReagentCure ds4: - args.Handled = true; - DiseaseReagentCure(args, ds4); - return; - } + _buckleQuery = GetEntityQuery(); + _healOnBuckleQuery = GetEntityQuery(); + _sleepingComponentQuery = GetEntityQuery(); + _bloodstreamQuery = GetEntityQuery(); + _temperatureQuery = GetEntityQuery(); } } diff --git a/Content.Server/Backmen/Disease/Cures/DiseaseJustWaitCure.cs b/Content.Server/Backmen/Disease/Cures/DiseaseJustWaitCure.cs index 332add09432..82e23f97606 100644 --- a/Content.Server/Backmen/Disease/Cures/DiseaseJustWaitCure.cs +++ b/Content.Server/Backmen/Disease/Cures/DiseaseJustWaitCure.cs @@ -1,4 +1,5 @@ using Content.Shared.Backmen.Disease; +using Robust.Shared.Prototypes; namespace Content.Server.Backmen.Disease.Cures; @@ -21,14 +22,24 @@ public override string CureText() { return Loc.GetString("diagnoser-cure-wait", ("time", MaxLength)); } + + public override object GenerateEvent(Entity ent, ProtoId disease) + { + return new DiseaseCureArgs(ent, disease, this); + } } public sealed partial class DiseaseCureSystem { - private void DiseaseJustWaitCure(DiseaseCureArgs args, DiseaseJustWaitCure ds) + private void DiseaseJustWaitCure(Entity ent, ref DiseaseCureArgs args) { - ds.Ticker++; - if (ds.Ticker >= ds.MaxLength) + if(args.Handled) + return; + + args.Handled = true; + + args.DiseaseCure.Ticker++; + if (args.DiseaseCure.Ticker >= args.DiseaseCure.MaxLength) { _disease.CureDisease(args.DiseasedEntity, args.Disease); } diff --git a/Content.Server/Backmen/Disease/Cures/DiseaseReagentCure.cs b/Content.Server/Backmen/Disease/Cures/DiseaseReagentCure.cs index a3b45078a4a..a15df844145 100644 --- a/Content.Server/Backmen/Disease/Cures/DiseaseReagentCure.cs +++ b/Content.Server/Backmen/Disease/Cures/DiseaseReagentCure.cs @@ -24,25 +24,35 @@ public override string CureText() return string.Empty; return (Loc.GetString("diagnoser-cure-reagent", ("units", Min), ("reagent", reagentProt.LocalizedName))); } + + public override object GenerateEvent(Entity ent, ProtoId disease) + { + return new DiseaseCureArgs(ent, disease, this); + } } public sealed partial class DiseaseCureSystem { - private void DiseaseReagentCure(DiseaseCureArgs args, DiseaseReagentCure ds) + private void DiseaseReagentCure(Entity ent, ref DiseaseCureArgs args) { - if (!TryComp(args.DiseasedEntity, out var bloodstream) + if(args.Handled) + return; + + args.Handled = true; + + if (!_bloodstreamQuery.TryGetComponent(args.DiseasedEntity, out var bloodstream) || bloodstream.ChemicalSolution == null) return; var chemicalSolution = bloodstream.ChemicalSolution.Value; var quant = FixedPoint2.Zero; - if (ds.Reagent != null && chemicalSolution.Comp.Solution.ContainsReagent(ds.Reagent.Value)) + if (args.DiseaseCure.Reagent != null && chemicalSolution.Comp.Solution.ContainsReagent(args.DiseaseCure.Reagent.Value)) { - quant = chemicalSolution.Comp.Solution.GetReagentQuantity(ds.Reagent.Value); + quant = chemicalSolution.Comp.Solution.GetReagentQuantity(args.DiseaseCure.Reagent.Value); } - if (quant >= ds.Min) + if (quant >= args.DiseaseCure.Min) { _disease.CureDisease(args.DiseasedEntity, args.Disease); } diff --git a/Content.Server/Backmen/Disease/DiseaseInfectionSpread.cs b/Content.Server/Backmen/Disease/DiseaseInfectionSpread.cs new file mode 100644 index 00000000000..131b050488f --- /dev/null +++ b/Content.Server/Backmen/Disease/DiseaseInfectionSpread.cs @@ -0,0 +1,32 @@ +using System.Threading; +using System.Threading.Tasks; +using Content.Server.Backmen.Disease.Components; +using Content.Server.Backmen.Disease.Effects; +using Robust.Shared.CPUJob.JobQueues; +using Robust.Shared.Timing; + +namespace Content.Server.Backmen.Disease; + +public sealed class DiseaseInfectionSpread : Job +{ + private readonly DiseaseInfectionSpreadEvent _ev; + private readonly DiseaseEffectSystem _effectSystem; + + public DiseaseInfectionSpread(DiseaseInfectionSpreadEvent ev, DiseaseEffectSystem effectSystem, double maxTime, CancellationToken cancellation = default) : base(maxTime, cancellation) + { + _ev = ev; + _effectSystem = effectSystem; + } + + public DiseaseInfectionSpread(DiseaseInfectionSpreadEvent ev, DiseaseEffectSystem effectSystem, double maxTime, IStopwatch stopwatch, CancellationToken cancellation = default) : base(maxTime, stopwatch, cancellation) + { + _ev = ev; + _effectSystem = effectSystem; + } + + protected override async Task Process() + { + _effectSystem.DoSpread(_ev.Owner, _ev.Disease, _ev.Range); + return null; + } +} diff --git a/Content.Server/Backmen/Disease/DiseaseSystem.cs b/Content.Server/Backmen/Disease/DiseaseSystem.cs index bbfaaa5972f..241a0557be8 100644 --- a/Content.Server/Backmen/Disease/DiseaseSystem.cs +++ b/Content.Server/Backmen/Disease/DiseaseSystem.cs @@ -4,6 +4,7 @@ using Content.Server.Chat.Systems; using Content.Server.Nutrition.Components; using Content.Server.Popups; +using Content.Shared.Backmen.CCVar; using Content.Shared.Backmen.Disease; using Content.Shared.Backmen.Disease.Events; using Content.Shared.Clothing.Components; @@ -16,6 +17,7 @@ using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Systems; using Content.Shared.Rejuvenate; +using Robust.Shared.Configuration; using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Serialization.Manager; @@ -34,13 +36,12 @@ public sealed class DiseaseSystem : EntitySystem [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!; - [Dependency] private readonly EntityLookupSystem _lookup = default!; - [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; [Dependency] private readonly InventorySystem _inventorySystem = default!; [Dependency] private readonly MobStateSystem _mobStateSystem = default!; [Dependency] private readonly ChatSystem _chatSystem = default!; [Dependency] private readonly IParallelManager _parallel = default!; - [Dependency] private readonly SharedTransformSystem _transform = default!; + + [Dependency] private readonly IConfigurationManager _cfg = default!; public override void Initialize() { @@ -57,8 +58,12 @@ public override void Initialize() SubscribeLocalEvent(OnApplyMetabolicMultiplier); _carrierQuery = GetEntityQuery(); + + _cfg.OnValueChanged(CCVars.GameDiseaseEnabled, v=> _enabled = v, true); } + private bool _enabled = true; + private EntityQuery _carrierQuery; private readonly HashSet _addQueue = new(); @@ -72,6 +77,10 @@ public override void Initialize() public override void Update(float frameTime) { base.Update(frameTime); + + if (!_enabled) + return; + foreach (var entity in _addQueue) { if (TerminatingOrDeleted(entity)) @@ -99,7 +108,6 @@ public override void Update(float frameTime) { if (carrierComp.Diseases.Count == 0) { - RemCompDeferred(owner); continue; } @@ -161,8 +169,7 @@ private void Process(Entity owner, float frameTime, int { if (!cure.Stages.Contains(stage)) continue; - var args = new DiseaseCureArgs(owner, disease.ID, cure); - QueueLocalEvent(args); + RaiseLocalEvent(owner, cure.GenerateEvent(owner,disease.ID)); } if (!doEffects) @@ -172,8 +179,7 @@ private void Process(Entity owner, float frameTime, int { if (!effect.Stages.Contains(stage) || !_random.Prob(effect.Probability)) continue; - var args = new DiseaseEffectArgs(owner, disease.ID, effect); - QueueLocalEvent(args); + RaiseLocalEvent(owner, effect.GenerateEvent(owner,disease.ID)); } } /// @@ -297,7 +303,7 @@ private void OnEntitySpeak(EntityUid uid, DiseasedComponent component, EntitySpo { if (TryComp(uid, out var carrier)) { - SneezeCough(uid, _random.Pick(carrier.Diseases), string.Empty); + SneezeCough(uid, _random.Pick(carrier.Diseases).ID, string.Empty); } } @@ -391,17 +397,24 @@ private void InteractWithDiseased(EntityUid diseased, EntityUid target, DiseaseC /// for TryInfect. /// public void TryAddDisease(EntityUid host, DiseasePrototype addedDisease, DiseaseCarrierComponent? target = null) + { + TryAddDisease(host, addedDisease.ID, target); + } + + public void TryAddDisease(EntityUid host, ProtoId addedDisease, DiseaseCarrierComponent? target = null) { if (!Resolve(host, ref target, false)) return; foreach (var disease in target.AllDiseases) { - if (disease == addedDisease.ID) //ID because of the way protoypes work + if (disease == addedDisease) //ID because of the way protoypes work return; } - var freshDisease = _serializationManager.CreateCopy(addedDisease, notNullableOverride: true); + if (!_prototypeManager.TryIndex(addedDisease, out var added)) + return; + var freshDisease = _serializationManager.CreateCopy(added, notNullableOverride: true); //if (freshDisease == null) // return; @@ -410,14 +423,6 @@ public void TryAddDisease(EntityUid host, DiseasePrototype addedDisease, Disease _addQueue.Add(host); } - public void TryAddDisease(EntityUid host, string? addedDisease, DiseaseCarrierComponent? target = null) - { - if (addedDisease == null || !_prototypeManager.TryIndex(addedDisease, out var added)) - return; - - TryAddDisease(host, added, target); - } - /// /// Pits the infection chance against the /// person's disease resistance and @@ -453,7 +458,7 @@ public void TryInfect(Entity carrier, ProtoId - public bool SneezeCough(EntityUid uid, DiseasePrototype? disease, string emoteId, bool airTransmit = true, TransformComponent? xform = null) + public bool SneezeCough(EntityUid uid, ProtoId diseaseId, string emoteId, bool airTransmit = true, TransformComponent? xform = null) { if (!Resolve(uid, ref xform)) return false; @@ -468,26 +473,26 @@ public bool SneezeCough(EntityUid uid, DiseasePrototype? disease, string emoteId _chatSystem.TryEmoteWithChat(uid, emoteId); + var disease = _prototypeManager.Index(diseaseId); + if (disease is not { Infectious: true } || !airTransmit) return true; if (_inventorySystem.TryGetSlotEntity(uid, "mask", out var maskUid) && - EntityManager.TryGetComponent(maskUid, out var blocker) && + TryComp(maskUid, out var blocker) && blocker.Enabled) return true; - foreach (var entity in _lookup.GetEntitiesInRange(_transform.GetMapCoordinates(uid, xform), 2f)) + QueueLocalEvent(new DiseaseInfectionSpreadEvent { - if (!_carrierQuery.TryGetComponent(entity, out var carrier) || - !_interactionSystem.InRangeUnobstructed(uid, entity)) - continue; + Owner = uid, + Disease = disease, + Range = 2f + }); - TryInfect((entity,carrier), disease, 0.3f); - } return true; } - - } +} /// /// This event is fired by chems diff --git a/Content.Server/Backmen/Disease/Effects/DiseaseAddComponent.cs b/Content.Server/Backmen/Disease/Effects/DiseaseAddComponent.cs index 6f02e72b260..214a4e6173a 100644 --- a/Content.Server/Backmen/Disease/Effects/DiseaseAddComponent.cs +++ b/Content.Server/Backmen/Disease/Effects/DiseaseAddComponent.cs @@ -16,20 +16,29 @@ public sealed partial class DiseaseAddComponent : DiseaseEffect /// [DataField("components")] public ComponentRegistry Components = new(); + + public override object GenerateEvent(Entity ent, ProtoId disease) + { + return new DiseaseEffectArgs(ent, disease, this); + } } public sealed partial class DiseaseEffectSystem { [Dependency] private readonly ISerializationManager _serialization = default!; - private void DiseaseAddComponent(DiseaseEffectArgs args, DiseaseAddComponent ds) + private void DiseaseAddComponent(Entity ent, ref DiseaseEffectArgs args) { - if (ds.Components.Count == 0) + if(args.Handled) + return; + args.Handled = true; + + if (args.DiseaseEffect.Components.Count == 0) return; var uid = args.DiseasedEntity; - foreach (var compReg in ds.Components.Values) + foreach (var compReg in args.DiseaseEffect.Components.Values) { var compType = compReg.Component.GetType(); diff --git a/Content.Server/Backmen/Disease/Effects/DiseaseAdjustReagent.cs b/Content.Server/Backmen/Disease/Effects/DiseaseAdjustReagent.cs index b7d10240ee8..13a25398202 100644 --- a/Content.Server/Backmen/Disease/Effects/DiseaseAdjustReagent.cs +++ b/Content.Server/Backmen/Disease/Effects/DiseaseAdjustReagent.cs @@ -1,5 +1,6 @@ using Content.Server.Body.Components; using Content.Server.Chemistry.Containers.EntitySystems; +using Content.Server.Chemistry.ReagentEffects; using Content.Shared.Backmen.Disease; using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reagent; @@ -25,15 +26,25 @@ public sealed partial class DiseaseAdjustReagent : DiseaseEffect [DataField("amount", required: true)] public FixedPoint2 Amount = default!; + + public override object GenerateEvent(Entity ent, ProtoId disease) + { + return new DiseaseEffectArgs(ent, disease, this); + } } public sealed partial class DiseaseEffectSystem { [Dependency] private readonly SharedSolutionContainerSystem _solutionContainer = default!; - private void DiseaseAdjustReagent(DiseaseEffectArgs args, DiseaseAdjustReagent ds) + private void DiseaseAdjustReagent(Entity ent, ref DiseaseEffectArgs args) { - if (ds.Reagent == null) + if(args.Handled) + return; + + args.Handled = true; + + if (args.DiseaseEffect.Reagent == null) return; if (!TryComp(args.DiseasedEntity, out var bloodstream)) @@ -43,9 +54,9 @@ private void DiseaseAdjustReagent(DiseaseEffectArgs args, DiseaseAdjustReagent d if (stream == null) return; - if (ds.Amount < 0 && stream.Value.Comp.Solution.ContainsReagent(ds.Reagent.Value)) - _solutionContainer.RemoveReagent(stream.Value,ds.Reagent.Value, -ds.Amount); - if (ds.Amount > 0) - _solutionContainer.TryAddReagent(stream.Value, ds.Reagent.Value, ds.Amount, out _); + if (args.DiseaseEffect.Amount < 0 && stream.Value.Comp.Solution.ContainsReagent(args.DiseaseEffect.Reagent.Value)) + _solutionContainer.RemoveReagent(stream.Value,args.DiseaseEffect.Reagent.Value, -args.DiseaseEffect.Amount); + if (args.DiseaseEffect.Amount > 0) + _solutionContainer.TryAddReagent(stream.Value, args.DiseaseEffect.Reagent.Value, args.DiseaseEffect.Amount, out _); } } diff --git a/Content.Server/Backmen/Disease/Effects/DiseaseGenericStatusEffect.cs b/Content.Server/Backmen/Disease/Effects/DiseaseGenericStatusEffect.cs index 068b546477b..dc656776765 100644 --- a/Content.Server/Backmen/Disease/Effects/DiseaseGenericStatusEffect.cs +++ b/Content.Server/Backmen/Disease/Effects/DiseaseGenericStatusEffect.cs @@ -1,6 +1,7 @@ using Content.Shared.Backmen.Disease; using Content.Shared.StatusEffect; using JetBrains.Annotations; +using Robust.Shared.Prototypes; namespace Content.Server.Backmen.Disease.Effects; @@ -41,6 +42,11 @@ public sealed partial class DiseaseGenericStatusEffect : DiseaseEffect /// [DataField("type")] public StatusEffectDiseaseType Type = StatusEffectDiseaseType.Add; + + public override object GenerateEvent(Entity ent, ProtoId disease) + { + return new DiseaseEffectArgs(ent, disease, this); + } } /// See status effects for how these work @@ -55,19 +61,22 @@ public sealed partial class DiseaseEffectSystem { [Dependency] private readonly StatusEffectsSystem _effectsSystem = default!; - private void DiseaseGenericStatusEffect(DiseaseEffectArgs args, DiseaseGenericStatusEffect ds) + private void DiseaseGenericStatusEffect(Entity ent, ref DiseaseEffectArgs args) { - if (ds.Type == StatusEffectDiseaseType.Add && ds.Component != "") + if(args.Handled) + return; + args.Handled = true; + if (args.DiseaseEffect.Type == StatusEffectDiseaseType.Add && args.DiseaseEffect.Component != "") { - _effectsSystem.TryAddStatusEffect(args.DiseasedEntity, ds.Key, TimeSpan.FromSeconds(ds.Time), ds.Refresh, ds.Component); + _effectsSystem.TryAddStatusEffect(args.DiseasedEntity, args.DiseaseEffect.Key, TimeSpan.FromSeconds(args.DiseaseEffect.Time), args.DiseaseEffect.Refresh, args.DiseaseEffect.Component); } - else if (ds.Type == StatusEffectDiseaseType.Remove) + else if (args.DiseaseEffect.Type == StatusEffectDiseaseType.Remove) { - _effectsSystem.TryRemoveTime(args.DiseasedEntity, ds.Key, TimeSpan.FromSeconds(ds.Time)); + _effectsSystem.TryRemoveTime(args.DiseasedEntity, args.DiseaseEffect.Key, TimeSpan.FromSeconds(args.DiseaseEffect.Time)); } - else if (ds.Type == StatusEffectDiseaseType.Set) + else if (args.DiseaseEffect.Type == StatusEffectDiseaseType.Set) { - _effectsSystem.TrySetTime(args.DiseasedEntity, ds.Key, TimeSpan.FromSeconds(ds.Time)); + _effectsSystem.TrySetTime(args.DiseasedEntity, args.DiseaseEffect.Key, TimeSpan.FromSeconds(args.DiseaseEffect.Time)); } } } diff --git a/Content.Server/Backmen/Disease/Effects/DiseaseHealthChange.cs b/Content.Server/Backmen/Disease/Effects/DiseaseHealthChange.cs index 10864d2dad0..240d106e306 100644 --- a/Content.Server/Backmen/Disease/Effects/DiseaseHealthChange.cs +++ b/Content.Server/Backmen/Disease/Effects/DiseaseHealthChange.cs @@ -1,6 +1,7 @@ using Content.Shared.Backmen.Disease; using Content.Shared.Damage; using JetBrains.Annotations; +using Robust.Shared.Prototypes; namespace Content.Server.Backmen.Disease.Effects; @@ -13,14 +14,22 @@ public sealed partial class DiseaseHealthChange : DiseaseEffect [DataField("damage", required: true)] [ViewVariables(VVAccess.ReadWrite)] public DamageSpecifier Damage = default!; + + public override object GenerateEvent(Entity ent, ProtoId disease) + { + return new DiseaseEffectArgs(ent, disease, this); + } } public sealed partial class DiseaseEffectSystem { [Dependency] private readonly DamageableSystem _damageable = default!; - private void DiseaseHealthChange(DiseaseEffectArgs args, DiseaseHealthChange ds) + private void DiseaseHealthChange(Entity ent, ref DiseaseEffectArgs args) { - _damageable.TryChangeDamage(args.DiseasedEntity, ds.Damage, true, false); + if(args.Handled) + return; + args.Handled = true; + _damageable.TryChangeDamage(args.DiseasedEntity, args.DiseaseEffect.Damage, true, false); } } diff --git a/Content.Server/Backmen/Disease/Effects/DiseasePolymorph.cs b/Content.Server/Backmen/Disease/Effects/DiseasePolymorph.cs index 3e0658ee578..73ec762b637 100644 --- a/Content.Server/Backmen/Disease/Effects/DiseasePolymorph.cs +++ b/Content.Server/Backmen/Disease/Effects/DiseasePolymorph.cs @@ -26,6 +26,15 @@ public sealed partial class DiseasePolymorph : DiseaseEffect [DataField("polymorphMessage")] [ViewVariables(VVAccess.ReadWrite)] public string? PolymorphMessage; + + [DataField("cureAfter")] + [ViewVariables(VVAccess.ReadWrite)] + public bool CureAfter = true; + + public override object GenerateEvent(Entity ent, ProtoId disease) + { + return new DiseaseEffectArgs(ent, disease, this); + } } public sealed partial class DiseaseEffectSystem @@ -34,15 +43,22 @@ public sealed partial class DiseaseEffectSystem [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; - private void DiseasePolymorph(DiseaseEffectArgs args, DiseasePolymorph ds) + private void DiseasePolymorph(Entity ent, ref DiseaseEffectArgs args) { - var polyUid = _polymorph.PolymorphEntity(args.DiseasedEntity, ds.PolymorphId); + if(args.Handled) + return; + args.Handled = true; + + var polyUid = _polymorph.PolymorphEntity(args.DiseasedEntity, args.DiseaseEffect.PolymorphId); - if (ds.PolymorphSound != null && polyUid != null) + if (args.DiseaseEffect.PolymorphSound != null && polyUid != null) { - _audio.PlayPvs(ds.PolymorphSound, polyUid.Value, AudioParams.Default.WithVariation(0.2f)); + _audio.PlayPvs(args.DiseaseEffect.PolymorphSound, polyUid.Value, AudioParams.Default.WithVariation(0.2f)); } - if (ds.PolymorphMessage != null && polyUid != null) - _popup.PopupEntity(Loc.GetString(ds.PolymorphMessage), polyUid.Value, polyUid.Value, PopupType.Large); + if (args.DiseaseEffect.PolymorphMessage != null && polyUid != null) + _popup.PopupEntity(Loc.GetString(args.DiseaseEffect.PolymorphMessage), polyUid.Value, polyUid.Value, PopupType.Large); + + if(args.DiseaseEffect.CureAfter) + _disease.CureDisease(ent, args.Disease); } } diff --git a/Content.Server/Backmen/Disease/Effects/DiseasePopUp.cs b/Content.Server/Backmen/Disease/Effects/DiseasePopUp.cs index 451fe6be269..33e4f62006b 100644 --- a/Content.Server/Backmen/Disease/Effects/DiseasePopUp.cs +++ b/Content.Server/Backmen/Disease/Effects/DiseasePopUp.cs @@ -2,6 +2,7 @@ using Content.Shared.IdentityManagement; using Content.Shared.Popups; using JetBrains.Annotations; +using Robust.Shared.Prototypes; namespace Content.Server.Backmen.Disease.Effects; @@ -21,6 +22,11 @@ public sealed partial class DiseasePopUp : DiseaseEffect [DataField("visualType")] public PopupType VisualType = PopupType.Small; + + public override object GenerateEvent(Entity ent, ProtoId disease) + { + return new DiseaseEffectArgs(ent, disease, this); + } } public enum PopupRecipients { @@ -30,11 +36,14 @@ public enum PopupRecipients public sealed partial class DiseaseEffectSystem { - private void DiseasePopUp(DiseaseEffectArgs args, DiseasePopUp ds) + private void DiseasePopUp(Entity ent, ref DiseaseEffectArgs args) { - if (ds.Type == PopupRecipients.Local) - _popup.PopupEntity(Loc.GetString(ds.Message), args.DiseasedEntity, args.DiseasedEntity, ds.VisualType); - else if (ds.Type == PopupRecipients.Pvs) - _popup.PopupEntity(Loc.GetString(ds.Message, ("person", Identity.Entity(args.DiseasedEntity, EntityManager))), args.DiseasedEntity, ds.VisualType); + if(args.Handled) + return; + args.Handled = true; + if (args.DiseaseEffect.Type == PopupRecipients.Local) + _popup.PopupEntity(Loc.GetString(args.DiseaseEffect.Message), args.DiseasedEntity, args.DiseasedEntity, args.DiseaseEffect.VisualType); + else if (args.DiseaseEffect.Type == PopupRecipients.Pvs) + _popup.PopupEntity(Loc.GetString(args.DiseaseEffect.Message, ("person", Identity.Entity(args.DiseasedEntity, EntityManager))), args.DiseasedEntity, args.DiseaseEffect.VisualType); } } diff --git a/Content.Server/Backmen/Disease/Effects/DiseaseSnough.cs b/Content.Server/Backmen/Disease/Effects/DiseaseSnough.cs index 2333684ae64..1a6c0965937 100644 --- a/Content.Server/Backmen/Disease/Effects/DiseaseSnough.cs +++ b/Content.Server/Backmen/Disease/Effects/DiseaseSnough.cs @@ -1,6 +1,7 @@ using Content.Shared.Backmen.Disease; using Content.Shared.Chat.Prototypes; using JetBrains.Annotations; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Server.Backmen.Disease.Effects; @@ -23,12 +24,20 @@ public sealed partial class DiseaseSnough : DiseaseEffect /// [DataField("airTransmit")] public bool AirTransmit = true; + + public override object GenerateEvent(Entity ent, ProtoId disease) + { + return new DiseaseEffectArgs(ent, disease, this); + } } public sealed partial class DiseaseEffectSystem { [Dependency] private readonly DiseaseSystem _disease = default!; - private void DiseaseSnough(DiseaseEffectArgs args, DiseaseSnough ds) + private void DiseaseSnough(Entity ent, ref DiseaseEffectArgs args) { - _disease.SneezeCough(args.DiseasedEntity, _prototypeManager.Index(args.Disease), ds.EmoteId, ds.AirTransmit); + if(args.Handled) + return; + args.Handled = true; + _disease.SneezeCough(args.DiseasedEntity, args.Disease, args.DiseaseEffect.EmoteId, args.DiseaseEffect.AirTransmit); } } diff --git a/Content.Server/Backmen/Disease/Effects/DiseaseVomit.cs b/Content.Server/Backmen/Disease/Effects/DiseaseVomit.cs index ef727076140..789ae4522c3 100644 --- a/Content.Server/Backmen/Disease/Effects/DiseaseVomit.cs +++ b/Content.Server/Backmen/Disease/Effects/DiseaseVomit.cs @@ -1,6 +1,7 @@ using Content.Server.Medical; using Content.Shared.Backmen.Disease; using JetBrains.Annotations; +using Robust.Shared.Prototypes; namespace Content.Server.Backmen.Disease.Effects; @@ -16,12 +17,20 @@ public sealed partial class DiseaseVomit : DiseaseEffect /// How many units of hunger to add each time we vomit [DataField("hungerAmount")] public float HungerAmount = -40f; + + public override object GenerateEvent(Entity ent, ProtoId disease) + { + return new DiseaseEffectArgs(ent, disease, this); + } } public sealed partial class DiseaseEffectSystem { [Dependency] private readonly VomitSystem _vomit = default!; - private void DiseaseVomit(DiseaseEffectArgs args, DiseaseVomit ds) + private void DiseaseVomit(Entity ent, ref DiseaseEffectArgs args) { - _vomit.Vomit(args.DiseasedEntity, ds.ThirstAmount, ds.HungerAmount); + if(args.Handled) + return; + args.Handled = true; + _vomit.Vomit(args.DiseasedEntity, args.DiseaseEffect.ThirstAmount, args.DiseaseEffect.HungerAmount); } } diff --git a/Content.Server/Backmen/Disease/Effects/EffectSystem.cs b/Content.Server/Backmen/Disease/Effects/EffectSystem.cs index 7420ab56a86..0fe2200a188 100644 --- a/Content.Server/Backmen/Disease/Effects/EffectSystem.cs +++ b/Content.Server/Backmen/Disease/Effects/EffectSystem.cs @@ -1,58 +1,65 @@ -using Content.Shared.Backmen.Disease; +using System.Linq; +using Content.Server.Backmen.Disease.Components; +using Content.Shared.Backmen.Disease; using Content.Shared.Backmen.Disease.Effects; +using Content.Shared.Interaction; +using Robust.Shared.CPUJob.JobQueues.Queues; using Robust.Shared.Prototypes; namespace Content.Server.Backmen.Disease.Effects; public sealed partial class DiseaseEffectSystem : SharedDiseaseEffectSystem { - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly EntityLookupSystem _lookup = default!; + [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; + public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(DoGenericEffect); + SubscribeLocalEvent>(DiseaseAddComponent); + SubscribeLocalEvent>(DiseaseAdjustReagent); + SubscribeLocalEvent>(DiseaseGenericStatusEffect); + SubscribeLocalEvent>(DiseaseHealthChange); + SubscribeLocalEvent>(DiseasePolymorph); + SubscribeLocalEvent>(DiseasePopUp); + SubscribeLocalEvent>(DiseaseSnough); + SubscribeLocalEvent>(DiseaseVomit); + + SubscribeLocalEvent(OnSpreadEvent); } - private void DoGenericEffect(DiseaseEffectArgs args) + private const double SpreadJobTime = 0.005; + private readonly JobQueue _spreadJobQueue = new(SpreadJobTime); + + private readonly HashSet> _diseaseCarrierSpread = new(); + private void OnSpreadEvent(DiseaseInfectionSpreadEvent ev) { - if(args.Handled) - return; + _spreadJobQueue.EnqueueJob(new DiseaseInfectionSpread(ev,this,SpreadJobTime)); + } - switch (args.DiseaseEffect) + public void DoSpread(EntityUid uid, DiseasePrototype disease, float range) + { + _diseaseCarrierSpread.Clear(); + var pos = _transform.GetMapCoordinates(uid); + _lookup.GetEntitiesInRange(pos, range, _diseaseCarrierSpread, LookupFlags.Uncontained); + foreach (var entity in _diseaseCarrierSpread) { - case DiseaseAddComponent ds1: - args.Handled = true; - DiseaseAddComponent(args, ds1); - return; - case DiseaseAdjustReagent ds2: - args.Handled = true; - DiseaseAdjustReagent(args, ds2); - return; - case DiseaseGenericStatusEffect ds3: - args.Handled = true; - DiseaseGenericStatusEffect(args, ds3); - return; - case DiseaseHealthChange ds4: - args.Handled = true; - DiseaseHealthChange(args, ds4); - return; - case DiseasePolymorph ds5: - args.Handled = true; - DiseasePolymorph(args, ds5); - return; - case DiseasePopUp ds6: - args.Handled = true; - DiseasePopUp(args, ds6); - return; - case DiseaseSnough ds7: - args.Handled = true; - DiseaseSnough(args, ds7); - return; - case DiseaseVomit ds8: - args.Handled = true; - DiseaseVomit(args, ds8); - return; + if (entity.Owner == uid) + continue; + var tarPos = _transform.GetMapCoordinates(entity); + if (!_interactionSystem.InRangeUnobstructed(pos, tarPos, range)) + continue; + + _disease.TryInfect(entity, disease, 0.3f); } } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + _spreadJobQueue.Process(); + } } diff --git a/Content.Shared/Backmen/CCVar/CCVars.cs b/Content.Shared/Backmen/CCVar/CCVars.cs index 2c2d7967782..8f6c94ae222 100644 --- a/Content.Shared/Backmen/CCVar/CCVars.cs +++ b/Content.Shared/Backmen/CCVar/CCVars.cs @@ -7,6 +7,9 @@ namespace Content.Shared.Backmen.CCVar; [CVarDefs] public sealed class CCVars { + public static readonly CVarDef + GameDiseaseEnabled = CVarDef.Create("game.disease", true, CVar.SERVERONLY); + /* * GPT */ diff --git a/Content.Shared/Backmen/Disease/DiseaseCure.cs b/Content.Shared/Backmen/Disease/DiseaseCure.cs index a7e77a2fe54..76473d2e56d 100644 --- a/Content.Shared/Backmen/Disease/DiseaseCure.cs +++ b/Content.Shared/Backmen/Disease/DiseaseCure.cs @@ -22,13 +22,15 @@ public abstract partial class DiseaseCure /// the cure /// public abstract string CureText(); + + public abstract object GenerateEvent(Entity ent, ProtoId disease); } -public sealed class DiseaseCureArgs( +public sealed class DiseaseCureArgs( Entity diseasedEntity, ProtoId disease, - DiseaseCure diseaseCure) - : DiseaseArgs(diseasedEntity, disease) + T diseaseCure) + : DiseaseArgs(diseasedEntity, disease) where T : DiseaseCure { - public readonly DiseaseCure DiseaseCure = diseaseCure; + public readonly T DiseaseCure = diseaseCure; } diff --git a/Content.Shared/Backmen/Disease/DiseaseEffect.cs b/Content.Shared/Backmen/Disease/DiseaseEffect.cs index 359559736c6..82b65024cc9 100644 --- a/Content.Shared/Backmen/Disease/DiseaseEffect.cs +++ b/Content.Shared/Backmen/Disease/DiseaseEffect.cs @@ -17,6 +17,7 @@ public abstract partial class DiseaseEffect : HandledEntityEventArgs /// [DataField("stages")] public HashSet Stages { get; private set; } = [0]; + public abstract object GenerateEvent(Entity ent, ProtoId disease); } public abstract class DiseaseArgs(Entity diseasedEntity, ProtoId disease) @@ -31,11 +32,11 @@ public abstract class DiseaseArgs(Entity diseasedEntity /// Includes an entity manager because it is out of scope /// otherwise. /// -public sealed class DiseaseEffectArgs( +public sealed class DiseaseEffectArgs( Entity diseasedEntity, ProtoId disease, - DiseaseEffect diseaseEffect) - : DiseaseArgs(diseasedEntity, disease) + T diseaseEffect) + : DiseaseArgs(diseasedEntity, disease) where T : DiseaseEffect { - public readonly DiseaseEffect DiseaseEffect = diseaseEffect; + public readonly T DiseaseEffect = diseaseEffect; }