Utility class with pure math helpers for in-game time UI. It computes a total-hour value that preserves the current day index when setting an hour, and maps a 0..1 slider fraction to a quantized hour-of-day snapped to in-game minutes.
using System;
namespace FieldGuide.DayNight;
/// <summary>Pure helpers for setting the clock from a UI. Kept separate from the networked component so they
/// are trivially unit-testable and reusable (a preview tool, an editor slider, a debug console).</summary>
public static class TimeMath
{
/// <summary>Set the clock to an hour-of-day while PRESERVING the current day index, so the deterministic
/// per-day weather roll does not re-roll when a player scrubs the time within a day. Result =
/// floor(current/24)*24 + clamp(hourOfDay, 0, 24). Example: day 1 at 15:30 (total 39.5), set 06:00 → 30.0
/// (still day 1).</summary>
public static float ComputeSetHour( float currentTotalHours, float hourOfDay )
=> MathF.Floor( currentTotalHours / 24f ) * 24f + Math.Clamp( hourOfDay, 0f, 24f );
/// <summary>Map a slider/drag fraction across a track (0 left, 1 right) to a quantized hour-of-day in
/// [0,24], rounded to the nearest in-game MINUTE (1/60 h). A pixel-cheap throttle with no timer state: a
/// drag emits at most one distinct value per minute of readout. Feed the result into
/// <see cref="ComputeSetHour"/> to keep the day index.
///
/// The result is clamped AFTER quantizing, and that clamp is load-bearing: one minute is not exactly
/// representable in binary, so 1440 steps of 1/60 accumulate to 24.000002 and a full-right drag would
/// hand <see cref="ComputeSetHour"/> a value past the end of the day. Round first, clamp second.</summary>
public static float ComputeSliderHour( float frac )
{
float hour = Math.Clamp( frac, 0f, 1f ) * 24f;
const float step = 1f / 60f; // one in-game minute
return Math.Clamp( MathF.Round( hour / step ) * step, 0f, 24f );
}
}