Merge branch 'phase/77-health-conditions'
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace HSchool.People;
|
||||
|
||||
/// <summary>
|
||||
/// One medical condition on a person (hediff-like). Needs stay separate — a disease may
|
||||
/// later modify decay, but hunger is still a need.
|
||||
/// </summary>
|
||||
public sealed class HealthCondition
|
||||
{
|
||||
public required string DefName { get; init; }
|
||||
|
||||
/// <summary>0…1. Stages and effects read this; DiseaseDef curves land in phase 78.</summary>
|
||||
public float Severity { get; set; }
|
||||
|
||||
/// <summary>Immunity / treatment progress 0…1.</summary>
|
||||
public float Progress { get; set; }
|
||||
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string? Source { get; init; }
|
||||
|
||||
public DateTime StartedAt { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Base severity change per game day before the person+day seed factor.
|
||||
/// Artificial conditions carry this until DiseaseDef owns the curve (phase 78).
|
||||
/// </summary>
|
||||
public float SeverityPerDay { get; init; }
|
||||
|
||||
/// <summary>Base progress change per game day before the person+day seed factor.</summary>
|
||||
public float ProgressPerDay { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>Mutates the sparse <see cref="Person.Conditions"/> list and ticks severity deterministically.</summary>
|
||||
public static class HealthConditions
|
||||
{
|
||||
public static void Add(Person person, HealthCondition condition)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(person);
|
||||
ArgumentNullException.ThrowIfNull(condition);
|
||||
|
||||
var list = person.Conditions;
|
||||
if (list is null)
|
||||
{
|
||||
list = [];
|
||||
person.Conditions = list;
|
||||
}
|
||||
|
||||
list.Add(condition);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Advances severity/progress for everyone with conditions. Same
|
||||
/// <paramref name="peopleSeed"/>, person id and <paramref name="dayNumber"/> yield the same curve.
|
||||
/// </summary>
|
||||
public static bool Tick(Person person, int peopleSeed, int dayNumber, double gameMinutes)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(person);
|
||||
var list = person.Conditions;
|
||||
if (list is null || list.Count == 0 || gameMinutes <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var daySeed = Seed.Mix(peopleSeed, person.Id, dayNumber, Seed.HealthSalt);
|
||||
var unit = (daySeed & int.MaxValue) / (float)int.MaxValue;
|
||||
var dayFactor = 0.5f + unit;
|
||||
var days = gameMinutes / (24d * 60d);
|
||||
var changed = false;
|
||||
|
||||
foreach (var condition in list)
|
||||
{
|
||||
var severityDelta = (float)(condition.SeverityPerDay * dayFactor * days);
|
||||
var progressDelta = (float)(condition.ProgressPerDay * dayFactor * days);
|
||||
if (severityDelta == 0f && progressDelta == 0f)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var nextSeverity = Math.Clamp(condition.Severity + severityDelta, 0f, 1f);
|
||||
var nextProgress = Math.Clamp(condition.Progress + progressDelta, 0f, 1f);
|
||||
if (nextSeverity == condition.Severity && nextProgress == condition.Progress)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
condition.Severity = nextSeverity;
|
||||
condition.Progress = nextProgress;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
}
|
||||
@@ -81,6 +81,12 @@ public sealed record Person
|
||||
/// </summary>
|
||||
public List<LessonMarkRecord>? LessonMarks { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Active medical conditions. Null when healthy (empty) so people.json stays compact.
|
||||
/// Needs are not replaced by this list.
|
||||
/// </summary>
|
||||
public List<HealthCondition>? Conditions { get; set; }
|
||||
|
||||
public int AgeOn(DateTime asOf) => SchoolYears.AgeYears(BirthDate, asOf);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ public static class Seed
|
||||
public const int ConflictSalt = 14;
|
||||
public const int HomeSalt = 15;
|
||||
public const int SummonSalt = 16;
|
||||
public const int HealthSalt = 17;
|
||||
|
||||
/// <summary>A stream that belongs to the school rather than to one family.</summary>
|
||||
public static int ForSchool(int schoolSeed, int salt) => Mix(schoolSeed, familyIndex: -1, salt);
|
||||
|
||||
Reference in New Issue
Block a user