Files
TheriapolisV3/Theriapolis.Core/Time/WorldClock.cs
T
Christopher Wiebe b451f83174 Initial commit: Theriapolis baseline at port/godot branch point
Captures the pre-Godot-port state of the codebase. This is the rollback
anchor for the Godot port (M0 of theriapolis-rpg-implementation-plan-godot-port.md).
All Phase 0 through Phase 6.5 work is included; Phase 7 is in flight.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 20:40:51 -07:00

49 lines
1.9 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
namespace Theriapolis.Core.Time;
public enum Season : byte { Spring, Summer, Autumn, Winter }
/// <summary>
/// Single in-game time counter. Measured in whole seconds so it serializes
/// trivially and stays deterministic — no floating-point drift over a long
/// playthrough.
///
/// Phase 4 callers advance the clock from world-map travel and tactical
/// stepping. Phase 8 weather/seasons reads from it.
/// </summary>
public sealed class WorldClock
{
/// <summary>In-game seconds since world creation. Game time, not real time.</summary>
public long InGameSeconds { get; private set; }
// Calendar constants. A 96-day year (24 days × 4 seasons) keeps the math
// tight; a real-world year would mean each season is a 90-hour playthrough.
public const int SecondsPerMinute = 60;
public const int SecondsPerHour = 3600;
public const int SecondsPerDay = SecondsPerHour * 24;
public const int DaysPerSeason = 24;
public const int DaysPerYear = DaysPerSeason * 4;
public int Day => (int)(InGameSeconds / SecondsPerDay);
public int Hour => (int)((InGameSeconds % SecondsPerDay) / SecondsPerHour);
public int Minute => (int)((InGameSeconds % SecondsPerHour) / SecondsPerMinute);
public int Year => Day / DaysPerYear;
public Season Season => (Season)((Day / DaysPerSeason) % 4);
public void Advance(long seconds)
{
if (seconds < 0) throw new ArgumentOutOfRangeException(nameof(seconds));
InGameSeconds += seconds;
}
public WorldClockState CaptureState() => new() { InGameSeconds = InGameSeconds };
public void RestoreState(WorldClockState s) => InGameSeconds = s.InGameSeconds;
/// <summary>Pretty-print like "Y0 Spring D5 14:23".</summary>
public string Format() => $"Y{Year} {Season} D{Day % DaysPerSeason} {Hour:D2}:{Minute:D2}";
}
public sealed class WorldClockState
{
public long InGameSeconds;
}