Add EventDef notices, morning and lesson bells, and info toasts.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
using HSchool.Content;
|
||||
|
||||
namespace HSchool.Simulation;
|
||||
|
||||
/// <summary>
|
||||
/// Turns clock edges into world facts. One dayStart per morning crossing, one lessonStart per
|
||||
/// school when the bell enters a lesson period — not one per class.
|
||||
/// </summary>
|
||||
internal static class EventSystem
|
||||
{
|
||||
public static IReadOnlyList<WorldEvent> Detect(School school, DateTime before, DateTime after)
|
||||
{
|
||||
if (school.Catalog is null || after <= before)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
var facts = new List<WorldEvent>(2);
|
||||
if (CrossedWorkMorning(school.Catalog, before, after, school.SchoolWeekDays))
|
||||
{
|
||||
facts.Add(new WorldEvent(EventTriggers.DayStart));
|
||||
}
|
||||
|
||||
if (EnteredLessonPeriod(school.Catalog, before, after, school.SchoolWeekDays))
|
||||
{
|
||||
facts.Add(new WorldEvent(EventTriggers.LessonStart));
|
||||
}
|
||||
|
||||
return facts;
|
||||
}
|
||||
|
||||
private static bool CrossedWorkMorning(DefCatalog catalog, DateTime before, DateTime after, int weekDays)
|
||||
{
|
||||
if (!PersonDayLog.CrossedDayStart(before, after))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return SchoolDay.IsWorkday(catalog, after, weekDays);
|
||||
}
|
||||
|
||||
private static bool EnteredLessonPeriod(DefCatalog catalog, DateTime before, DateTime after, int weekDays)
|
||||
{
|
||||
var beforeSlot = SchoolDay.At(catalog, before, weekDays);
|
||||
var afterSlot = SchoolDay.At(catalog, after, weekDays);
|
||||
return afterSlot.Kind == DaySlotKind.Lesson && beforeSlot != afterSlot;
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ public sealed class School : IDisposable
|
||||
private bool _disposed;
|
||||
private readonly List<PersonLogEvent> _dayLog = [];
|
||||
private readonly HashSet<string> _lessonLogOnce = new(StringComparer.Ordinal);
|
||||
private readonly List<WorldEvent> _worldEvents = [];
|
||||
|
||||
internal School(int id, string name, DateTime startDate, DefCatalog? catalog, MapLayout? map)
|
||||
{
|
||||
@@ -297,6 +298,7 @@ public sealed class School : IDisposable
|
||||
NeedDecay.Apply(World, Catalog, (next.Value - before).TotalMinutes);
|
||||
peopleChanged |= ApparelWear.Apply(this, gameMinutes: 0, before);
|
||||
SyncWeather(force: true);
|
||||
RecordWorldEvents(before, next.Value);
|
||||
return new SkipEmptyResult(SkipEmptyError.None, next.Value, peopleChanged);
|
||||
}
|
||||
|
||||
@@ -368,6 +370,8 @@ public sealed class School : IDisposable
|
||||
var heavyMinutes = ClockSpeed.HeavyGameMinutes(Clock.SpeedIndex, gameMinutes);
|
||||
peopleChanged |= ApplyHeavySystems(heavyMinutes);
|
||||
}
|
||||
|
||||
RecordWorldEvents(before, Clock.Time);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -377,6 +381,24 @@ public sealed class School : IDisposable
|
||||
return peopleChanged;
|
||||
}
|
||||
|
||||
/// <summary>Facts raised since the last drain. The worker maps them to notices; simulation has no UI.</summary>
|
||||
public IReadOnlyList<WorldEvent> DrainWorldEvents()
|
||||
{
|
||||
if (_worldEvents.Count == 0)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
var copy = _worldEvents.ToArray();
|
||||
_worldEvents.Clear();
|
||||
return copy;
|
||||
}
|
||||
|
||||
private void RecordWorldEvents(DateTime before, DateTime after)
|
||||
{
|
||||
_worldEvents.AddRange(EventSystem.Detect(this, before, after));
|
||||
}
|
||||
|
||||
private bool ApplyHeavySystems(double gameMinutes)
|
||||
{
|
||||
HeavySystemsInvocations++;
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace HSchool.Simulation;
|
||||
|
||||
/// <summary>
|
||||
/// A fact the school raised this step. Same seam as <c>AffinityEvent</c>: a list, no UI.
|
||||
/// </summary>
|
||||
public readonly record struct WorldEvent(string Trigger);
|
||||
Reference in New Issue
Block a user