using HSchool.Content; using HSchool.People; namespace HSchool.Simulation; /// /// After marks or absences: info toast, class-teacher log, and optional auto summons weight. /// Illness absences do not become summons offenses. /// internal static class GradeTrailSystem { public static void AfterMark(School school, Person person) { if (school.Catalog?.BehaviorRules is not { } rules) { return; } TryReact(school, person, rules); } public static void AfterAbsent(School school, Person person, string? absenceReason) { if (school.Catalog?.BehaviorRules is not { } rules) { return; } if (GradeTrail.CountsAsSummonOffense(absenceReason) && OffenseMemory.Record(person, OffenseKinds.Truancy, school.Clock.Time, otherPersonId: null, rules)) { school.RosterTalkDirty = true; } TryReact(school, person, rules); } private static void TryReact(School school, Person person, BehaviorDef rules) { if (!GradeTrail.IsPoor(person, rules)) { return; } if (!school.TryClaimGradeTrailNotice(person.Id, school.Clock.Time.Date)) { return; } school.RaiseWorldEvent(new WorldEvent(EventTriggers.PoorGradeTrail, person.Id)); var schoolClass = ClassOf(school, person); if (schoolClass?.ClassTeacherId is { } teacherId) { school.AppendDayLog(new PersonLogEvent( person.Id, school.Clock.Time, PersonLogTypes.ClassTeacherGradeNote, teacherId)); } if (rules.GradeTrailSummonChance > 0f) { DirectorSummonSystem.TryEnqueue(school, person.Id, rules.GradeTrailSummonChance); } } private static SchoolClass? ClassOf(School school, Person person) { if (person.ClassId is null || school.Roster is null) { return null; } return school.Roster.Classes.FirstOrDefault(row => row.Id.Equals(person.ClassId, StringComparison.Ordinal)); } }