Captures where we stand after Phase 4b and lays out the remaining retail-faithful port work across four phases (5-8): - Phase 5: PhysicsScript loader + runtime + sky lifecycle. Replaces WeatherSystem's crude "DayGroup name contains Rainy → spawn rain" shortcut with retail's actual PES-driven particle emission. - Phase 6: Fog on sky meshes. The sky frag currently ignores fog uniforms; retail's D3D fog applies to sky. - Phase 7: Lightning flash trigger + thunder audio for storm keyframes. - Phase 8: Weather / DayGroup crossfade (DAT_008427a9 / _DAT_008427b8 lerp) + AdminEnvirons override → fog crossfade. User observation 2026-04-23 during Phase 4b verification: "Now it is raining when it should not be." Root cause traced to the SetKindFromDayGroupName string match firing rain particles on a "Rainy" DayGroup regardless of whether that DayGroup actually has a visible rain-emitting SkyObject. Proper fix requires porting PhysicsScript. Also commits the earlier research from agent Q1-Q6: `docs/research/2026-04-23-sky-material-state.md`. Four parallel decompile agents are in flight as of this commit: - PhysicsScript dat + runtime - Sky↔PES wiring + emitter lifecycle - Lightning + weather crossfade - Fog on sky + vertex distance Phase 5 implementation starts once those land. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6.4 KiB
Phase 5+ Port Plan — Sky / Weather / Lightning, retail-verbatim
Date: 2026-04-23 Scope: Port the remaining retail-accurate pieces of the sky/weather/lightning system so acdream visually matches a side-by-side retail client in all day/night + weather states (clear, cloudy, rainy, stormy).
Where we are today (main, commit 2802fb2)
Sky core, landed across Phases 1-4b:
- Region-dat SkyDesc loader with GameTime offsets ✓
- Retail LCG DayGroup picker (seed = Year × DaysPerYear + DayOfYear, Phase 3g) ✓
- Calendar tick extraction with
GameTime.ZeroTimeOfYear = 3600(Phase 3f) ✓ - Per-vertex D3D-fixed-function lighting formula (Phase 4, Phase 4b clamp) ✓
- Sky objects drawn with visibility, arc sweep, UV scroll ✓
- ACDREAM_DUMP_SKY diagnostic for retail-faithfulness verification ✓
- RetailTimeProbe tool for live memory comparison ✓
Left to do:
- PhysicsScript — no loader, no runtime, no sky-side integration. User-visible: rain doesn't spawn when retail rolls a PES-carrying SkyObject.
- Fog on sky — shader ignores fog uniforms; retail's D3D fog applies to sky.
- Lightning flash trigger — storm timer + visual not ported.
- Weather / DayGroup crossfade — retail's 10-second smooth blend between keyframe sets not ported.
- AdminEnvirons override — packet handler exists as a stub on the wire side; not wired to our rendering.
Phases (execute in order)
Phase 5 — PhysicsScript loader + runtime + sky wiring
Output of parallel research agents #1 + #2 (2026-04-23):
2026-04-23-physicsscript.md— dat schema + runtime interpreter2026-04-23-sky-pes-wiring.md— sky → PES lifecycle
Sub-phases:
- 5a Port
PhysicsScriptdat type + any nested types. Add toAcDream.Core/Dat/. - 5b Port the runtime interpreter to C#.
AcDream.Core/Vfx/PhysicsScriptRunner.cs. Wire into existingParticleSystemas the spawner — we do NOT build a new emitter class, reuse what's there. - 5c Hook into
SkyRenderer→ on per-frame sky-object iteration, for each visible SkyObject with non-zeroDefaultPesObjectId, ensure its PES is running. Despawn on visibility loss or DayGroup change. - 5d Replace
WeatherSystem.SetKindFromDayGroupName's crude"Rainy" → WeatherKind.Rainstring match with PES-driven spawning. TheWeatherKindenum becomes fog/tone info only; particle emission is 100% PES-gated.
Tests: PhysicsScript parser conformance (golden bytes → expected struct), runtime determinism (same script + same seed → same particle stream).
Phase 6 — Fog on sky meshes
Output of research agent #4: 2026-04-23-sky-fog.md.
Sub-phases:
- 6a
sky.vertcomputes fog factor per vertex. Formula from the agent's findings (expected: linear per-vertex based on eye-space Z). - 6b
sky.fragappliesmix(fragment, fogColor, fogFactor)before the lightning-flash bump. - 6c If sky meshes render at distances that saturate the keyframe's
FOGEND (sky would be pure fog color), either:
- Cap sky mesh eye-space Z at FOGEND - epsilon for fog purposes only, OR
- Use a separate "sky fog" distance parameter per retail's behavior.
Tests: render-golden at 4 canonical times (dawn/noon/dusk/midnight) + 3 DayGroups (Sunny / Cloudy / Stormy) — compare against retail screenshots.
Phase 7 — Lightning flash trigger
Output of research agent #3: 2026-04-23-lightning-crossfade.md (shared with
Phase 8 findings).
Sub-phases:
- 7a Port retail's storm-keyframe lightning timer.
- 7b Wire to existing
uFogParams.zlightning-flash uniform in the UBO (sky.frag already consumes it). - 7c Wire thunder audio cue via
AdminEnvirons.Thunder1Sound..Thunder6Soundor a local per-flash delay (retail uses speed-of-sound distance).
Phase 8 — Weather / DayGroup crossfade
Also from agent #3.
Sub-phases:
- 8a Port
DAT_008427a9flag +_DAT_008427b8progress mechanics into our SkyStateProvider or a new CrossfadeOrchestrator class. - 8b Trigger a crossfade when:
- DayGroup index changes (day rollover hits a new weather roll) — smooth
swap of keyframe set over retail's step constant
_DAT_007c7208. AdminEnvironsoverride arrives — smooth fog transition to the override color.
- DayGroup index changes (day rollover hits a new weather roll) — smooth
swap of keyframe set over retail's step constant
- 8c AdminEnvirons wiring: the packet handler stub in
WeatherSystem.Overridealready exists; wire it to the crossfade trigger + our renderer.
Optional Phase 9 — Per-cell AdjustPlanes terrain relight
From earlier research (2026-04-23-sky-decompile-hunt-A.md §1): retail reruns
FUN_00532440 on every terrain cell whenever the sky keyframe advances.
We currently bake terrain vertex lighting once and don't refresh. Visible effect:
terrain doesn't darken smoothly as the sun sets.
Deferred because it's higher effort and lower payoff than 5-8.
Success criteria
- A
+Acdreamcharacter stationary in outdoor Holtburg for 30 real minutes (about 15 Derethian minutes with our 1:1 tick rate) produces a sky that, side-by-side with retail, is visually indistinguishable within lighting equipment tolerances (color temperature, saturation). - Rolling a DayGroup that contains a rain-emitting SkyObject causes acdream to spawn rain particles MATCHING retail's rain cadence (drop rate, direction, lifetime).
- During a Stormy DayGroup, acdream shows lightning flashes at the retail cadence (8–30 sec between strikes, flash rises in ~50ms, decays in ~200ms).
- An
AdminEnvirons RedFogpacket arriving mid-play crossfades acdream's fog to the red tint within ~10 real seconds, same direction retail does.
Non-goals (for this plan)
- PhysicsScript author tools — we parse + run; we don't edit.
- Retail-accurate GPU particle rendering — reuse our existing
ParticleSystembackend. PhysicsScript drives IT, not a new emitter. - Exotic EnvironChangeTypes (the Thunder3Sound, DarkLaughSound, etc. non-fog variants) — those are admin-only and we can stub-log them.
- Per-landblock weather variation — retail weather is Dereth-wide.
Open risks / unknowns (to be resolved by agents)
- Will our
ParticleSystem.SpawnEmitterAPI be sufficient, or does retail's PhysicsScript need commands we don't expose? (agent #1). - Does sky mesh vertex data need a 1e6-far-plane fog distance rescaling, or is retail's FOGEND authored large enough to cover sky? (agent #4).
- Does retail sync the lightning random timer across all clients (so everyone sees the same strike), or is it truly client-local? (agent #3).