Add DiseaseDef diseases with weather vectors and excused absence.
Vanilla ships several diseases whose stages drive lesson gain, stay-home rolls, and illness attendance; cold/rain/snow raise onset via BehaviorDef scale.
This commit is contained in:
@@ -0,0 +1,163 @@
|
||||
using HSchool.Content;
|
||||
|
||||
namespace HSchool.People;
|
||||
|
||||
/// <summary>
|
||||
/// Reads DiseaseDef stages for lesson gain, stay-home rolls and warmth decay.
|
||||
/// Contagion between people is phase 79.
|
||||
/// </summary>
|
||||
public static class DiseaseEffects
|
||||
{
|
||||
public static bool TryStage(DiseaseDef disease, float severity, out DiseaseStage stage)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(disease);
|
||||
DiseaseStage? best = null;
|
||||
foreach (var candidate in disease.Stages)
|
||||
{
|
||||
if (severity + 1e-6f < candidate.MinSeverity)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (best is null || candidate.MinSeverity >= best.MinSeverity)
|
||||
{
|
||||
best = candidate;
|
||||
}
|
||||
}
|
||||
|
||||
if (best is null)
|
||||
{
|
||||
stage = null!;
|
||||
return false;
|
||||
}
|
||||
|
||||
stage = best;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool IsIncubated(HealthCondition condition, DiseaseDef disease, DateTime now)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(condition);
|
||||
ArgumentNullException.ThrowIfNull(disease);
|
||||
var elapsed = (DateTime.SpecifyKind(now, DateTimeKind.Utc) - condition.StartedAt).TotalDays;
|
||||
return elapsed >= disease.IncubationDays;
|
||||
}
|
||||
|
||||
/// <summary>Product of active post-incubation lesson factors (1 when healthy).</summary>
|
||||
public static float LessonLearningFactor(Person person, DefCatalog catalog, DateTime now)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(person);
|
||||
ArgumentNullException.ThrowIfNull(catalog);
|
||||
var list = person.Conditions;
|
||||
if (list is null || list.Count == 0)
|
||||
{
|
||||
return 1f;
|
||||
}
|
||||
|
||||
var factor = 1f;
|
||||
foreach (var condition in list)
|
||||
{
|
||||
if (!catalog.Diseases.TryGetValue(condition.DefName, out var disease) || disease.Abstract)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!IsIncubated(condition, disease, now) || !TryStage(disease, condition.Severity, out var stage))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
factor *= stage.LessonLearningFactor;
|
||||
}
|
||||
|
||||
return factor;
|
||||
}
|
||||
|
||||
/// <summary>Highest stay-home chance among incubated conditions.</summary>
|
||||
public static float StayHomeChance(Person person, DefCatalog catalog, DateTime now)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(person);
|
||||
ArgumentNullException.ThrowIfNull(catalog);
|
||||
var list = person.Conditions;
|
||||
if (list is null || list.Count == 0)
|
||||
{
|
||||
return 0f;
|
||||
}
|
||||
|
||||
var chance = 0f;
|
||||
foreach (var condition in list)
|
||||
{
|
||||
if (!catalog.Diseases.TryGetValue(condition.DefName, out var disease) || disease.Abstract)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!IsIncubated(condition, disease, now) || !TryStage(disease, condition.Severity, out var stage))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (stage.StayHomeChance > chance)
|
||||
{
|
||||
chance = stage.StayHomeChance;
|
||||
}
|
||||
}
|
||||
|
||||
return chance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deterministic per person+day: whether illness keeps them off campus today.
|
||||
/// </summary>
|
||||
public static bool ShouldStayHome(Person person, DefCatalog catalog, int peopleSeed, DateTime now)
|
||||
{
|
||||
var chance = StayHomeChance(person, catalog, now);
|
||||
if (chance <= 0f)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (chance >= 1f)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var dayNumber = DateOnly.FromDateTime(DateTime.SpecifyKind(now, DateTimeKind.Utc)).DayNumber;
|
||||
var roll = Seed.Mix(peopleSeed, person.Id, dayNumber, Seed.DiseaseSalt);
|
||||
var unit = (roll & int.MaxValue) / (float)int.MaxValue;
|
||||
return unit < chance;
|
||||
}
|
||||
|
||||
/// <summary>Max Warmth decay multiplier from incubated stages (1 when healthy).</summary>
|
||||
public static float WarmthDecayFactor(Person person, DefCatalog catalog, DateTime now)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(person);
|
||||
ArgumentNullException.ThrowIfNull(catalog);
|
||||
var list = person.Conditions;
|
||||
if (list is null || list.Count == 0)
|
||||
{
|
||||
return 1f;
|
||||
}
|
||||
|
||||
var factor = 1f;
|
||||
foreach (var condition in list)
|
||||
{
|
||||
if (!catalog.Diseases.TryGetValue(condition.DefName, out var disease) || disease.Abstract)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!IsIncubated(condition, disease, now) || !TryStage(disease, condition.Severity, out var stage))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (stage.WarmthDecayFactor > factor)
|
||||
{
|
||||
factor = stage.WarmthDecayFactor;
|
||||
}
|
||||
}
|
||||
|
||||
return factor;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user