fix(world): DerethDateTime tick-0 offset — sky was 7/16 of a day wrong

User observed: 'time is flipped — supposed to be day/evening, but shows
night/morning.' That's a ~half-day offset.

Root cause in ACE DerethDateTime.cs line 23:
  private const double dayZeroTicks = 0; // Morningthaw 1, 10 P.Y. - Morntide-and-Half

ACE anchors tick 0 to Morntide-and-Half (slot 7 on the 0-indexed 16-slot
scale) — NOT Darktide (slot 0 = midnight) as our DayFraction function
assumed. Confirmed by DerethDateTime.cs:145:
  private int hour = (int)Hours.Morntide_and_Half;

Fix: shift DayFraction by +7/16 * DayTicks (3333.75) so tick 0 maps to
its real calendar slot. Exposed as DayFractionOriginOffsetTicks constant
for documentation + downstream referencing.

Effect on sun: previously, server tick ~0 (just-booted ACE) produced
dayFraction 0 → midnight sky → night colors at noon real-time.
Now dayFraction 7/16 = 0.4375 → late morning sky → noon-ish colors
within 1/16 of a day, which matches what a user actually sees when
launching during daytime.

Tests updated for the corrected convention:
- DerethDateTime.DayFraction(0) = 7/16 (not 0).
- CurrentHour(0) = MorntideAndHalf (not Darktide).
- IsDaytime(0) = true.
- Midnight (Darktide, slot 0) is 9/16 of a day past tick 0.
- SkyState + WorldTimeDebug tests retargeted to the new frame.

Build green, 711 tests pass.

Ref: references/ACE/Source/ACE.Common/DerethDateTime.cs:23-25 + :145.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-04-19 14:27:49 +02:00
parent 187226f504
commit bd184e1afd
4 changed files with 84 additions and 33 deletions

View file

@ -98,14 +98,38 @@ public static class DerethDateTime
Frostfell,
}
/// <summary>
/// In ACE's calendar, <c>tick 0</c> is defined as "Morningthaw 1,
/// 10 P.Y. — Morntide-and-Half", NOT midnight
/// (<c>references/ACE/Source/ACE.Common/DerethDateTime.cs:23</c>,
/// <c>private const double dayZeroTicks = 0; // Morntide-and-Half</c>
/// + <c>private int hour = (int)Hours.Morntide_and_Half;</c>).
///
/// <para>
/// Morntide-and-Half is slot 7 on the 16-slot (0-indexed) scale,
/// i.e. late morning just before noon. Without this offset, a server
/// that just booted would have tick ≈ 0 and our sky renderer would
/// believe it's midnight — visibly wrong by roughly half a day.
/// Applying +7/16 of a full day (3333.75 ticks) in the day-fraction
/// computation re-aligns tick 0 to its real calendar slot, putting
/// noon at tick +476.25 / 2 (approximately) and Darktide at
/// tick +4286.25, which matches ACE's
/// <c>dayOneTicks = 0 + 210 + (hourTicks * 8) // Morningthaw 2, Darktide</c>.
/// </para>
/// </summary>
public const double DayFractionOriginOffsetTicks = (7.0 / 16.0) * DayTicks; // 3333.75
/// <summary>
/// Day fraction [0, 1): 0 = Darktide (midnight), 0.5 =
/// Midsong-and-Half (noon-ish), 1.0 wraps to 0.
/// Midsong-and-Half (noon-ish), 1.0 wraps to 0. Corrected for the
/// ACE tick-0 = Morntide-and-Half convention (see
/// <see cref="DayFractionOriginOffsetTicks"/>).
/// </summary>
public static double DayFraction(double ticks)
{
if (ticks < 0) ticks = 0;
double rem = ticks - Math.Floor(ticks / DayTicks) * DayTicks;
double shifted = ticks + DayFractionOriginOffsetTicks;
double rem = shifted - Math.Floor(shifted / DayTicks) * DayTicks;
return rem / DayTicks;
}