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>
136 lines
6.4 KiB
Markdown
136 lines
6.4 KiB
Markdown
# 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:
|
||
1. **PhysicsScript** — no loader, no runtime, no sky-side integration. User-visible:
|
||
rain doesn't spawn when retail rolls a PES-carrying SkyObject.
|
||
2. **Fog on sky** — shader ignores fog uniforms; retail's D3D fog applies to sky.
|
||
3. **Lightning flash trigger** — storm timer + visual not ported.
|
||
4. **Weather / DayGroup crossfade** — retail's 10-second smooth blend between
|
||
keyframe sets not ported.
|
||
5. **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 interpreter
|
||
- `2026-04-23-sky-pes-wiring.md` — sky → PES lifecycle
|
||
|
||
Sub-phases:
|
||
- **5a** Port `PhysicsScript` dat type + any nested types. Add to `AcDream.Core/Dat/`.
|
||
- **5b** Port the runtime interpreter to C#. `AcDream.Core/Vfx/PhysicsScriptRunner.cs`.
|
||
Wire into existing `ParticleSystem` as 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-zero `DefaultPesObjectId`, ensure its PES is running.
|
||
Despawn on visibility loss or DayGroup change.
|
||
- **5d** Replace `WeatherSystem.SetKindFromDayGroupName`'s crude
|
||
`"Rainy" → WeatherKind.Rain` string match with PES-driven spawning. The
|
||
`WeatherKind` enum 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.vert` computes fog factor per vertex. Formula from the agent's
|
||
findings (expected: linear per-vertex based on eye-space Z).
|
||
- **6b** `sky.frag` applies `mix(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.z` lightning-flash uniform in the UBO
|
||
(sky.frag already consumes it).
|
||
- **7c** Wire thunder audio cue via `AdminEnvirons.Thunder1Sound..Thunder6Sound`
|
||
or 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_008427a9` flag + `_DAT_008427b8` progress 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`.
|
||
- `AdminEnvirons` override arrives — smooth fog transition to the override
|
||
color.
|
||
- **8c** AdminEnvirons wiring: the packet handler stub in `WeatherSystem.Override`
|
||
already 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
|
||
|
||
1. A `+Acdream` character 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).
|
||
2. Rolling a DayGroup that contains a rain-emitting SkyObject causes
|
||
acdream to spawn rain particles MATCHING retail's rain cadence (drop rate,
|
||
direction, lifetime).
|
||
3. During a Stormy DayGroup, acdream shows lightning flashes at the retail
|
||
cadence (8–30 sec between strikes, flash rises in ~50ms, decays in ~200ms).
|
||
4. An `AdminEnvirons RedFog` packet 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
|
||
`ParticleSystem` backend. 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.SpawnEmitter` API 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).
|