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)
///