weather(phase-7): gut WeatherSystem.Snapshot — passthrough keyframe fog

Final pre-decompile-era invention cleanup. Snapshot() now returns
the keyframe's fog (color, start, end) directly in all cases.
AdminEnvirons override replaces fog COLOR only; distances stay at
the keyframe's MinWorldFog/MaxWorldFog.

Removed:
  - FogForKind(kind, kf): the per-WeatherKind fog table with
    invented constants (Overcast 40-150m grey, Storm 25-90m dark,
    Rain 40-150m blue, Snow 60-200m white). Retail has no such
    logic — Agent #3's decompile scan found zero per-Kind fog
    manipulation in chunk_005* / chunk_006*. The SkyTimeOfDay
    keyframe interp (FUN_00501860) does all fog value selection.
  - OvercastFogStart/End, StormFogStart/End constants.
  - Storm-kind random lightning timer + _strikeJitter. Retail's
    lightning is server-driven via PlayScript (Phase 6), not a
    client timer — Agents #3 + #5 both rule this out.
  - Per-Kind cross-fade (_transitionT and TransitionSeconds-based
    lerp). Retail has a different crossfade — SkyTimeOfDay step
    blending via LightTickSize gating (_DAT_008427b8 + _DAT_007c7208)
    — which is the deferred Phase 5c "polish" item.

Result:
  - Clear: keyframe fog passthrough — unchanged behaviour.
  - Overcast / Rain / Snow / Storm: now ALSO keyframe passthrough.
    Previously these clobbered the keyframe with the invented
    constants, producing a grey-wall sky that extended no further
    than ~150m. User observation 2026-04-23: "retail sky extends
    all the way into the horizon, we cap at a grey wall." Fixed.
  - EnvironOverride (AdminEnvirons RedFog, BlueFog, etc):
    substitutes the fog COLOR preset, keeps keyframe distances.

WeatherKind enum retained as purely informational (debug overlay,
telemetry). Internal RollKind fallback retained for offline tests
that drive Tick() directly without SetKindFromDayGroupName.
TriggerFlash()/flash decay retained as a test-only hook for the
UBO's lightning-flash channel — production flash stays 0 since
retail drives lightning visuals through particle emitters, not
through a UBO uniform.

Tests updated: `Transition_EasesAcrossTenSeconds` deleted (codified
the Storm=dense-fog invention we just removed) and replaced by
`Snapshot_AlwaysPassesKeyframeFog_RegardlessOfKind` which asserts
every WeatherKind returns the keyframe fog directly.

Build + 742 tests green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-04-24 12:55:19 +02:00
parent e4cf3a9b6b
commit 889b235886
2 changed files with 84 additions and 97 deletions

View file

@ -38,19 +38,30 @@ public sealed class WeatherSystemTests
}
[Fact]
public void Transition_EasesAcrossTenSeconds()
public void Snapshot_AlwaysPassesKeyframeFog_RegardlessOfKind()
{
// Force Storm, then Clear, sample snapshot fog distance mid-transition.
var sys = new WeatherSystem();
sys.ForceWeather(WeatherKind.Storm);
sys.Tick(0, 1, 100f); // finalize
// Phase 7: retail DOES NOT override fog by WeatherKind — Storm
// doesn't produce denser fog, Overcast doesn't shrink distance.
// Every Kind renders the keyframe's fog directly. This test
// replaces the old "Transition_EasesAcrossTenSeconds" which
// codified the invented per-Kind fog behaviour.
var kf = SkyStateProvider.Default().Interpolate(0.5f);
var stormFog = sys.Snapshot(in kf);
Assert.Equal(WeatherKind.Storm, stormFog.Kind);
// Snapshot should have a small fog end (storm fog is dense).
Assert.True(stormFog.FogEnd < 120f, $"storm fog end too large: {stormFog.FogEnd}");
foreach (var kind in new[] {
WeatherKind.Clear, WeatherKind.Overcast,
WeatherKind.Rain, WeatherKind.Snow, WeatherKind.Storm,
})
{
var sys = new WeatherSystem();
sys.ForceWeather(kind);
sys.Tick(0, 1, 100f); // finalize any transition
var snap = sys.Snapshot(in kf);
Assert.Equal(kind, snap.Kind);
Assert.Equal(kf.FogStart, snap.FogStart, precision: 2);
Assert.Equal(kf.FogEnd, snap.FogEnd, precision: 2);
Assert.Equal(kf.FogColor, snap.FogColor);
}
}
[Fact]