Static utility that derives the in-game town clock from UTC time, a configurable day length and offset. Also exposes formatted time text, a daylight value and an IsNight flag used by lighting and car headlights, with an editor debug override.
namespace HorizonRP;
/// <summary>
/// L'heure de la ville (Julien, 29/10/2026) : une journée en jeu dure <see cref="DayMinutes"/>
/// minutes réelles (48 par défaut, comme GTA : une minute réelle, une demi-heure en jeu). Elle se
/// déduit de l'heure UTC et des réglages du serveur (« jour_duree_minutes », « heure_decalage »,
/// déclarés aussi dans SETTINGS d'api.php), si bien que toutes les machines ont la même sans rien
/// échanger. Le téléphone l'affiche ; la date, elle, reste la vraie. Le jour et la nuit suivent
/// (<see cref="DayNight"/>), comme les phares des voitures.
/// </summary>
public static class GameClock
{
public static float DayMinutes => Math.Max( 10, ServerSettings.Number( "jour_duree_minutes" ) );
public static float OffsetMinutes => ServerSettings.Number( "heure_decalage" );
/// <summary>Essais depuis l'éditeur : une heure imposée (en heures, 0 à 24), au lieu de celle de la ville.</summary>
public static float? DebugHours { get; set; }
/// <summary>Les minutes écoulées depuis minuit, en jeu (0 à 1440).</summary>
public static float Minutes
{
get
{
if ( DebugHours is { } forced )
return Math.Clamp( forced, 0, 23.99f ) * 60f;
var seconds = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() / 1000.0;
var minutes = seconds * 1440.0 / (DayMinutes * 60.0) + OffsetMinutes;
return (float)(((minutes % 1440) + 1440) % 1440);
}
}
public static float Hours => Minutes / 60f;
/// <summary>« 14:05 ».</summary>
public static string Text
{
get
{
var m = (int)Minutes;
return $"{m / 60:00}:{m % 60:00}";
}
}
/// <summary>
/// La lumière du jour, de 0 (nuit) à 1 (plein jour) : l'aube de 6 h à 8 h, le crépuscule de
/// 18 h à 20 h 30.
/// </summary>
public static float Daylight
{
get
{
var h = Hours;
if ( h >= 8 && h < 18 ) return 1;
if ( h >= 6 && h < 8 ) return Smooth( (h - 6) / 2 );
if ( h >= 18 && h < 20.5f ) return 1 - Smooth( (h - 18) / 2.5f );
return 0;
}
}
/// <summary>La nuit : les lampadaires s'allument, les phares aussi en montant en voiture.</summary>
public static bool IsNight => Daylight < 0.5f;
static float Smooth( float t )
{
t = Math.Clamp( t, 0, 1 );
return t * t * (3 - 2 * t);
}
}