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>
91 lines
3 KiB
C#
91 lines
3 KiB
C#
using System.Numerics;
|
|
using AcDream.Core.World;
|
|
using Xunit;
|
|
|
|
namespace AcDream.Core.Tests.World;
|
|
|
|
public sealed class SkyStateTests
|
|
{
|
|
[Fact]
|
|
public void Default_Has4Keyframes()
|
|
{
|
|
var sky = SkyStateProvider.Default();
|
|
Assert.Equal(4, sky.KeyframeCount);
|
|
}
|
|
|
|
[Fact]
|
|
public void Interpolate_AtExactKeyframe_ReturnsThatFrameData()
|
|
{
|
|
var sky = SkyStateProvider.Default();
|
|
var noon = sky.Interpolate(0.5f); // noon keyframe
|
|
|
|
// Noon sky color should be near white (1.0 ish).
|
|
Assert.InRange(noon.SunColor.X, 0.9f, 1.1f);
|
|
Assert.InRange(noon.SunColor.Y, 0.9f, 1.1f);
|
|
}
|
|
|
|
[Fact]
|
|
public void Interpolate_BetweenKeyframes_LerpsColors()
|
|
{
|
|
var sky = SkyStateProvider.Default();
|
|
var dawn = sky.Interpolate(0.25f);
|
|
var noon = sky.Interpolate(0.5f);
|
|
var midPt = sky.Interpolate(0.375f);
|
|
|
|
// Midpoint should fall between dawn & noon for sun color Y (green channel).
|
|
float low = System.Math.Min(dawn.SunColor.Y, noon.SunColor.Y);
|
|
float high = System.Math.Max(dawn.SunColor.Y, noon.SunColor.Y);
|
|
Assert.InRange(midPt.SunColor.Y, low, high);
|
|
}
|
|
|
|
[Fact]
|
|
public void Interpolate_Wraps_AcrossMidnight()
|
|
{
|
|
var sky = SkyStateProvider.Default();
|
|
var justAfterMidnight = sky.Interpolate(0.01f);
|
|
|
|
// Should return finite valid state (not NaN).
|
|
Assert.False(float.IsNaN(justAfterMidnight.SunColor.X));
|
|
Assert.False(float.IsNaN(justAfterMidnight.AmbientColor.X));
|
|
}
|
|
|
|
[Fact]
|
|
public void SunDirectionFromKeyframe_ReturnsUnitVector()
|
|
{
|
|
var kf = new SkyKeyframe(
|
|
Begin: 0.5f,
|
|
SunHeadingDeg: 180f, // south
|
|
SunPitchDeg: 70f,
|
|
SunColor: Vector3.One,
|
|
AmbientColor: Vector3.One,
|
|
FogColor: Vector3.One,
|
|
FogDensity: 0.001f);
|
|
|
|
var dir = SkyStateProvider.SunDirectionFromKeyframe(kf);
|
|
Assert.InRange(dir.Length(), 0.99f, 1.01f);
|
|
}
|
|
|
|
[Fact]
|
|
public void WorldTimeService_SyncFromServer_SetsTicks()
|
|
{
|
|
var service = new WorldTimeService(SkyStateProvider.Default());
|
|
service.SyncFromServer(12345.0);
|
|
|
|
// NowTicks advances by real elapsed time; but immediately after
|
|
// sync it should be at or very close to the synced value.
|
|
Assert.InRange(service.NowTicks, 12345.0, 12346.0);
|
|
}
|
|
|
|
[Fact]
|
|
public void WorldTimeService_DayFraction_RespectsSync()
|
|
{
|
|
var service = new WorldTimeService(SkyStateProvider.Default());
|
|
// Need to aim for dayFraction 0.5 (Gloaming-and-Half, slot 15 since tick 0 = slot 7).
|
|
// Sync to (0.5 - 7/16) * DayTicks = (1/16) * DayTicks — 1 slot past Morntide-and-Half = Midsong.
|
|
// Actually simpler: target fraction 7/16 (slot 7 = Morntide-and-Half) by syncing to tick 0.
|
|
service.SyncFromServer(0);
|
|
|
|
Assert.InRange(service.DayFraction, 0.43, 0.44); // 7/16 = 0.4375
|
|
Assert.True(service.IsDaytime);
|
|
}
|
|
}
|