diff --git a/Content.IntegrationTests/Tests/DiseaseTest.cs b/Content.IntegrationTests/Tests/DiseaseTest.cs new file mode 100644 index 0000000000..d3c637238c --- /dev/null +++ b/Content.IntegrationTests/Tests/DiseaseTest.cs @@ -0,0 +1,48 @@ +using System.Threading.Tasks; +using Content.Shared.Disease; +using NUnit.Framework; +using Robust.Shared.Prototypes; + +namespace Content.IntegrationTests.Tests; + +[TestFixture] +public sealed class DiseaseTest +{ + /// + /// Asserts that a disease prototype has valid stages for its effects and cures. + /// + [Test] + public async Task Stages() + { + await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true}); + var server = pairTracker.Pair.Server; + + var protoManager = server.ResolveDependency(); + + await server.WaitAssertion(() => + { + foreach (var proto in protoManager.EnumeratePrototypes()) + { + var stages = proto.Stages; + + foreach (var effect in proto.Effects) + { + for (var i = 0; i < effect.Stages.Length; i++) + { + Assert.That(stages.Contains(i), $"Disease {proto.ID} has an effect with an incorrect stage, {i}!"); + } + } + + foreach (var cure in proto.Cures) + { + for (var i = 0; i < cure.Stages.Length; i++) + { + Assert.That(stages.Contains(i), $"Disease {proto.ID} has a cure with an incorrect stage, {i}!"); + } + } + } + }); + + await pairTracker.CleanReturnAsync(); + } +} diff --git a/Content.Server/Disease/DiseaseSystem.cs b/Content.Server/Disease/DiseaseSystem.cs index 071460817e..a0b7405a5a 100644 --- a/Content.Server/Disease/DiseaseSystem.cs +++ b/Content.Server/Disease/DiseaseSystem.cs @@ -99,6 +99,7 @@ namespace Content.Server.Disease { var disease = carrierComp.Diseases[i]; disease.Accumulator += frameTime; + disease.TotalAccumulator += frameTime; if (disease.Accumulator < disease.TickTime) continue; @@ -107,9 +108,21 @@ namespace Content.Server.Disease var args = new DiseaseEffectArgs(carrierComp.Owner, disease, EntityManager); disease.Accumulator -= disease.TickTime; + int stage = 0; //defaults to stage 0 because you should always have one + float lastThreshold = 0; + for (var j = 0; j < disease.Stages.Count; j++) + { + if (disease.TotalAccumulator >= disease.Stages[j] && + disease.Stages[j] > lastThreshold) + { + lastThreshold = disease.Stages[j]; + stage = j; + } + } + foreach (var cure in disease.Cures) { - if (cure.Cure(args)) + if (cure.Stages.AsSpan().Contains(stage) && cure.Cure(args)) CureDisease(carrierComp, disease); } @@ -117,7 +130,7 @@ namespace Content.Server.Disease { foreach (var effect in disease.Effects) { - if (_random.Prob(effect.Probability)) + if (effect.Stages.AsSpan().Contains(stage) && _random.Prob(effect.Probability)) effect.Effect(args); } } diff --git a/Content.Shared/Disease/DiseaseCure.cs b/Content.Shared/Disease/DiseaseCure.cs index e933c9e591..9534772bae 100644 --- a/Content.Shared/Disease/DiseaseCure.cs +++ b/Content.Shared/Disease/DiseaseCure.cs @@ -11,7 +11,12 @@ namespace Content.Shared.Disease /// and false otherwise /// public abstract bool Cure(DiseaseEffectArgs args); - + /// + /// What stages the cure applies to. + /// probably should be all, but go wild + /// + [DataField("stages")] + public readonly int[] Stages = { 0 }; /// /// This is used by the disease diangoser machine /// to generate reports to tell people all of a disease's diff --git a/Content.Shared/Disease/DiseaseEffect.cs b/Content.Shared/Disease/DiseaseEffect.cs index bc40bfc978..bca00c00f8 100644 --- a/Content.Shared/Disease/DiseaseEffect.cs +++ b/Content.Shared/Disease/DiseaseEffect.cs @@ -12,6 +12,11 @@ namespace Content.Shared.Disease [DataField("probability")] public float Probability = 1.0f; /// + /// What stages this effect triggers on + /// + [DataField("stages")] + public readonly int[] Stages = { 0 }; + /// /// What effect the disease will have. /// public abstract void Effect(DiseaseEffectArgs args); diff --git a/Content.Shared/Disease/DiseasePrototype.cs b/Content.Shared/Disease/DiseasePrototype.cs index c846f56343..fc680fc0a3 100644 --- a/Content.Shared/Disease/DiseasePrototype.cs +++ b/Content.Shared/Disease/DiseasePrototype.cs @@ -37,6 +37,19 @@ namespace Content.Shared.Disease /// public float Accumulator = 0f; /// + /// Since accumulator is reset with TickTime, this just tracks + /// the total amount of time a disease has been present. + /// + public float TotalAccumulator = 0f; + /// + /// Stores all the separate stages of the disease plus the time + /// thresholds for their activation + /// int: the disease stage (0 for baseline, 1, 2, etc.) + /// float: the time it takes for the stage to begin. + /// + [DataField("stages", serverOnly: true)] + public readonly List Stages = new() { 0f }; + /// /// List of effects the disease has that will /// run every second (by default anyway) ///