Files
h-school/src/HSchool.Simulation/GameClock.cs
T
Leonid Pershin 4137400621
ci / server (push) Failing after 3m39s
ci / client (push) Successful in 14s
Enhance AI and simulation components with presence management and routing capabilities
- Introduced the `HSchool.Ai` project, responsible for routing, day plans, and presence management.
- Updated the `HSchool.Simulation` project to integrate with the new AI functionalities, improving decision-making and presence tracking.
- Added `travelMinutes` to room and territory definitions, ensuring accurate movement calculations within the simulation.
- Enhanced the `School` class to manage presence and implement empty-time skipping functionality.
- Updated documentation to reflect the new AI features and their impact on school simulation.
- Added tests for presence management and routing to ensure robust functionality and reliability.
2026-08-19 16:33:52 +03:00

81 lines
2.9 KiB
C#

namespace HSchool.Simulation;
/// <summary>
/// In-game calendar of one school. Time only moves while <see cref="IsRunning"/> is set, and it
/// moves by whole fixed steps — never by wall-clock deltas — so the same tick count always
/// produces the same date.
/// </summary>
public sealed class GameClock
{
/// <summary>Earliest date a school may start at; anything below is a typo, not a design choice.</summary>
public static readonly DateTime MinStartDate = new(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc);
public static readonly DateTime MaxStartDate = new(2999, 12, 31, 23, 59, 59, DateTimeKind.Utc);
private int _speedIndex = ClockSpeed.DefaultIndex;
public GameClock(DateTime startDate)
{
if (!IsValidStartDate(startDate))
{
throw new ArgumentOutOfRangeException(nameof(startDate), startDate, "Start date is outside the supported range.");
}
// The game calendar is not tied to a real time zone; UTC keeps serialization unambiguous.
Time = DateTime.SpecifyKind(startDate, DateTimeKind.Utc);
}
public DateTime Time { get; private set; }
/// <summary>
/// Schools live on their own: a new calendar starts running and only the player's pause
/// button stops it. Leaving for the menu does not.
/// </summary>
public bool IsRunning { get; set; } = true;
/// <summary>Index into <see cref="ClockSpeed.Multipliers"/>; invalid values are ignored.</summary>
public int SpeedIndex
{
get => _speedIndex;
set
{
if (ClockSpeed.IsValid(value))
{
_speedIndex = value;
}
}
}
public double Multiplier => ClockSpeed.MultiplierAt(_speedIndex);
public static bool IsValidStartDate(DateTime date) => date >= MinStartDate && date <= MaxStartDate;
/// <summary>Empty-time skip. Not a tick — the calendar jumps to an instant already known to be legal.</summary>
public void JumpTo(DateTime time)
{
if (!IsValidStartDate(time))
{
throw new ArgumentOutOfRangeException(nameof(time), time, "Jump target is outside the supported range.");
}
Time = DateTime.SpecifyKind(time, DateTimeKind.Utc);
}
/// <summary>
/// Advances the calendar by one fixed step of <paramref name="realSeconds"/>, scaled by the
/// base rate and the current speed. Does nothing while paused.
/// </summary>
/// <returns>Game minutes actually added; zero while paused.</returns>
public double Advance(double realSeconds, double gameMinutesPerRealSecond)
{
if (!IsRunning)
{
return 0;
}
var gameMinutes = realSeconds * gameMinutesPerRealSecond * Multiplier;
Time = Time.AddMinutes(gameMinutes);
return gameMinutes;
}
}