sky(phase-3e): drive WeatherSystem from DayGroup name — no more rogue rain

User reported rain in acdream while retail showed a clear sunny sky
after Phase 3d landed. Root cause: two independent weather systems
running in parallel.

  1. Retail DayGroup picker (FUN_00501990 port, Phase 3c/3c.1) —
     selected DayGroup[6] "Sunny" correctly.
  2. WeatherSystem.Tick (legacy stub from pre-decompile era) —
     kept rolling its own hardcoded PDF every day (60% Clear, 20%
     Overcast, 12% Rain, 5% Snow, 3% Storm), independent of the
     DayGroup picker. Its output drove the rain/snow particle
     emitters via UpdateWeatherParticles. If its hash happened to
     land on Rain for today's dayIndex, rain rendered even on a
     Sunny DayGroup day.

Retail has ONE source of truth for weather: the DayGroup roll. There
is no separate weather state machine — rain/snow/storm are implied by
the DayGroup name and its per-keyframe SkyObjectReplace settings.

Fix (Phase 3e):
- WeatherSystem.SetKindFromDayGroupName(string?) — loose substring
  match on the retail DayGroup name: "storm" → Storm, "snow" → Snow,
  "rain" → Rain, "cloud"/"overcast"/"dark"/"fog" → Overcast, else
  Clear. Case-insensitive. Covers the names observed in the live
  Dereth dat dump (Sunny, Clear, Cloudy, Rainy + inferred variants).
- WeatherSystem._externallyDriven flag disables the internal
  RollKind auto-roll once SetKindFromDayGroupName has been called at
  least once. Tests that drive Tick() directly keep the legacy
  hash-roll behavior (offline fallback). ForceWeather still works
  for debug overrides.
- GameWindow.RefreshSkyForCurrentDay calls
  Weather.SetKindFromDayGroupName(grp.Name) right after it installs
  the new SkyStateProvider. Logs the resulting WeatherKind on the
  same line as the DayGroup pick for correlation.
- New WeatherSystemTests.SetKindFromDayGroupName_MapsRetailNames
  (theory, 14 cases) + SetKindFromDayGroupName_DisablesInternalRoll.

Expected effect: Sunny/Clear DayGroups → no rain emitter. Rainy/Stormy
DayGroups → rain emitter active. The user's specific scenario
(DayGroup[6] "Sunny") now correctly maps to WeatherKind.Clear and no
particles spawn.

Build + 733 tests green (+16 new).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-04-24 09:32:27 +02:00
parent 75ad74e0b3
commit 5f9df4d620
3 changed files with 105 additions and 3 deletions

View file

@ -4370,10 +4370,18 @@ public sealed class GameWindow : IDisposable
new AcDream.Core.World.SkyStateProvider(
grp.SkyTimes.Select(s => s.Keyframe).ToList()));
// Phase 3e: drive the atmospheric weather (rain/snow emitters,
// fog-override categories, lightning strobe) from the retail
// DayGroup name. Stops the legacy WeatherSystem.RollKind hash
// from spawning rain particles on a "Sunny" day (user-observed
// rain regression 2026-04-23 after the retail picker landed on
// DayGroup[6] "Sunny" but the internal hash picked Rain).
Weather.SetKindFromDayGroupName(grp.Name);
Console.WriteLine(
$"sky: PY{absYear} day{dayOfYear} → DayGroup[{idx}] \"{grp.Name}\" " +
$"(Chance={grp.ChanceOfOccur:F2}, {grp.SkyObjects.Count} objects, " +
$"{grp.SkyTimes.Count} keyframes)");
$"{grp.SkyTimes.Count} keyframes, weather={Weather.Kind})");
}
}