sky(phase-3c): port retail FUN_00501990 DayGroup picker (uniform LCG)
Decompile agent located the retail DayGroup selection function at
FUN_00501990 (chunk_00500000.c:1276). It is a straight-line 32-bit
signed LCG — NOT a ChanceOfOccur-weighted CDF. Replaces the SplitMix64
approximation from Phase 3a.
Algorithm (verbatim from the decompile):
seed = year * secondsPerDay + dayOfYear // TimeOfDay+0x64/+0x10/+0x68
hash = seed * 0x6A42FDB2 + 0x8ABE1652 // signed 32-bit LCG
index = floor(dayGroupCount * (uint)hash / 2^32)
if (index >= dayGroupCount) index = 0 // float-rounding safety
Uniform over all DayGroups. Dereth's 20 groups all carry ChanceOfOccur=5.0
so uniform matches the statistical intent; the weighted walk Phase 3a
attempted is NOT what retail does. The SecondsPerDay multiplier is
load-bearing — without it, adjacent years would share adjacent LCG
seeds and divergence from retail would recur annually.
Result (this session's local ACE):
server: PY106 ColdMeet 17 MorntideAndHalf, ticks=291130073
→ year=106, dayOfYear=(106×0 + 17 across ColdMeet) via DerethDateTime
→ retail picker returns a deterministic uniform index from LCG.
Acdream and retail now agree on the pick for any (Year, DayOfYear)
since both drive from the same server PortalYearTicks.
Changes:
- src/AcDream.Core/World/DerethDateTime.cs: add Year(ticks) and
DayOfYear(ticks) helpers (match retail TimeOfDay+0x64 / +0x68).
- src/AcDream.Core/World/SkyDescLoader.cs:
- SelectDayGroupIndex signature: (year, secondsPerDay, dayOfYear)
instead of the flat dayIndex used by the SplitMix64 approximation.
- Body: retail LCG line-by-line port with decompile citations.
- ACDREAM_DAY_GROUP env var still overrides (for A/B verification).
- src/AcDream.App/Rendering/GameWindow.cs: RefreshSkyForCurrentDay now
feeds Year / DayOfYear / SecondsPerDay=7620 to the picker instead
of a flat dayIndex. Composite `year*360+dayOfYear` still tracked
internally as the day-change key for provider-rebuild idempotence.
- docs/research/2026-04-23-daygroup-selection.md committed with the
full decompile trail (new agent-produced research).
Build + 717 tests green. User visual verification (retail side-by-side)
next.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
027ccb46b9
commit
6ea87b7ea8
4 changed files with 346 additions and 52 deletions
|
|
@ -4316,10 +4316,21 @@ public sealed class GameWindow : IDisposable
|
|||
if (_loadedSkyDesc is null || _loadedSkyDesc.DayGroups.Count == 0)
|
||||
return;
|
||||
|
||||
long dayIndex = (long)System.Math.Floor(
|
||||
WorldTime.NowTicks / AcDream.Core.World.DerethDateTime.DayTicks);
|
||||
// Retail FUN_00501990 uses the full (Year, SecondsPerDay, DayOfYear)
|
||||
// triple — NOT a flat dayIndex. The SecondsPerDay multiplier is
|
||||
// load-bearing: without it, adjacent years map to adjacent LCG
|
||||
// seeds and convergence to acdream != retail would recur every
|
||||
// year. We use the retail calendar constants from DerethDateTime.
|
||||
double ticks = WorldTime.NowTicks;
|
||||
int year = AcDream.Core.World.DerethDateTime.Year(ticks);
|
||||
int dayOfYear = AcDream.Core.World.DerethDateTime.DayOfYear(ticks);
|
||||
int secondsPerDay = (int)AcDream.Core.World.DerethDateTime.DayTicks; // 7620
|
||||
|
||||
int idx = _loadedSkyDesc.SelectDayGroupIndex(dayIndex);
|
||||
// Compute a composite "dayIndex" for our own change-detection
|
||||
// and logging (doesn't feed into the roll itself).
|
||||
long dayIndex = (long)year * 360 + dayOfYear;
|
||||
|
||||
int idx = _loadedSkyDesc.SelectDayGroupIndex(year, secondsPerDay, dayOfYear);
|
||||
var grp = idx >= 0 && idx < _loadedSkyDesc.DayGroups.Count
|
||||
? _loadedSkyDesc.DayGroups[idx]
|
||||
: null;
|
||||
|
|
@ -4339,7 +4350,7 @@ public sealed class GameWindow : IDisposable
|
|||
grp.SkyTimes.Select(s => s.Keyframe).ToList()));
|
||||
|
||||
Console.WriteLine(
|
||||
$"sky: day {dayIndex} → DayGroup[{idx}] \"{grp.Name}\" " +
|
||||
$"sky: PY{year} day{dayOfYear} → DayGroup[{idx}] \"{grp.Name}\" " +
|
||||
$"(Chance={grp.ChanceOfOccur:F2}, {grp.SkyObjects.Count} objects, " +
|
||||
$"{grp.SkyTimes.Count} keyframes)");
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue