The aurora was never missing data — it was a missing mechanism plus a misread. New decompile evidence closes the April-2026 contradiction: retail plays the sky carriers' PES through the Setup's own DefaultScript (GameSky::MakeObject @0x00506EE0 -> CPhysicsObj::makeObject @0x00513970 sets state|=0x80000; animate_static_object @0x00513DF0 ticks ScriptManager + ParticleManager). The pes_id column stays dead — that half of the April finding stands; the ids are byte-equal mirrors. - SkyPesFrameController is now the production owner (ACDREAM_ENABLE_SKY_PES deleted): script ids resolve from the Setup DefaultScript (SkyObjectData.DefaultScriptId; the pes_id column is a one-time-logged cross-check), slots persist by (index, gfx id, properties) per CreateDeletePhysicsObjects @0x005073C0 — a day-group swap keeping the carrier no longer restarts its emitters — and stale slots stop before replacements claim the slot-derived owner id. - RetailParticleFacing ports calc_draw_frame @0x0050DFA0: degrade mode 2 faces the viewer roll-free (set_vector_heading) instead of the camera plane; modes 3/4/5 spin the authored frame around one local axis (rotate_around_axis_to_vector) — Dereth authors 54 mode-5 emitters that previously got no facing at all; 1,583 mode-2 emitters get the exact law; authored/mode-1 paths are unchanged. - The 2026-08-23 'whole-sky tint' was the Rainy-group lightning/thunder PES playing at the debug anchor inside their 0.03-0.19 window, not the aurora: the aurora is nine faint viewer-facing glows pulsing on 6.7/15/55-minute rebirth cycles, in every day group, all day. Research: docs/research/2026-08-23-sky-default-script-port.md. Register: AD-112 filed (camera-anchored synthetic owners vs sky-cell physics objects). ISSUES #2 corrected (the playback ban is lifted by the new evidence); #28 fix landed pending the connected night gate. Tests: RetailParticleFacingTests (16), SkyPesFrameControllerTests (6); hermetic suites App 6,076/0, Core 4,905/0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
6.5 KiB
Sky default-script (aurora/lightning/thunder) — the retail mechanism, proven
Date: 2026-08-23 · Issues: #28 (aurora), #2 (lightning, partial), #29 (clouds — not addressed here) · Phase: C.1.5c
The April contradiction, resolved
Two prior research passes (2026-04-23-sky-pes-wiring.md, 2026-04-28-pes-pseudocode.md)
correctly proved GameSky never reads CelestialPosition.pes_id
(SkyDesc::GetSky @0x00501EC0 writes it at 0x00501FC9; no reader exists), and
issue #2 therefore banned per-SkyObject PES playback "without new decompile
evidence". This document is that evidence. Both were right and both missed the
actual route:
The sky PES ids ride the sky Setups' own DefaultScript, and retail plays
them through the ordinary object default-script machinery — the pes_id
column is a dead mirror of the same ids.
Verified in the installed Dereth DAT: Setup 0x02000714 (aurora carrier,
parts 0x010001EC) has DefaultScript = 0x330007DB — byte-equal to its
SkyObject's PesObjectId. Same holds for 0x02000589→0x3300042C (thunder
ping-pong), 0x02000588→0x33000428 (thunder variant), 0x02000BA6→0x33000453
(lightning flash).
The retail chain (named decomp, every link read this session)
GameSky::UseTime @0x005075B0(30 Hz) →CRegionDesc::GetSky→SkyDesc::GetSky @0x00501EC0rebuilds theCelestialPositionlist. Per-object visibility: ifbegin_time == end_timethe object is always included; otherwisebegin <= t <= endgates the gfx id (out-of-window →INVALID_DID). The aurora object hasbegin=end=0.00in all 20 day groups → always present, all day.GameSky::CreateDeletePhysicsObjects @0x005073C0: an existing sky object is kept when its current DataID equals the wanted gfx id AND the properties word is unchanged AND (props & 4→LScape::weather_enableddid not flip). Only a mismatch destroys/recreates. ⇒ the aurora object — same id in every group — persists across day-group changes, and its emitters keep their particle population.GameSky::MakeObject @0x00506EE0:props & 4objects are only created whenLScape::weather_enabled != 0;props & 1selectsafter_sky_cell(post-scene) vsbefore_sky_cell. Creation isCPhysicsObj::makeObject(gfx_id, 0, 0).CPhysicsObj::makeObject @0x00513970→InitPartArrayObject: a Setup withdefault_script_id != 0setsstate |= 0x80000and registers viaCPhysics::AddStaticAnimatingObject @0x00509AF0.CPhysicsObj::animate_static_object @0x00513DF0(per tick): state 0x80000 →ScriptManager::UpdateScripts(starts/advances the default script — the PES) and updates the object'sParticleManager. This is the ~150/minCallPESchurn the 2026-04-30 live trace counted (the thunder PES0x3300042Cping-pongs viaCallPESchains).
The particle laws confirmed against our port
-
ParticleEmitterInfo::GetRandomOffset @0x005174A0: random vector, minus its projection ontooffset_dir, normalized, × rand[min,max] — a disk PERPENDICULAR to the dir (a shell when dir is zero). OurParticleSystem.RandomOffsetis byte-faithful. The aurora's 450–700 m "Z-dir" offsets are therefore a horizontal RING around the sky-object origin, not a column above it. -
Particle::Update @0x0051C290writes only the part origin per type formula (ourComputePositionmatches, incl. Swarm cos/sin); only the GR/LR parabolic variants rotate. -
ParticleEmitter::SetInfo @0x0051CE90: parts are ordinaryCPhysicsPart::makePhysicsPart(hw_gfxobj_id); there is no special "2D particle" draw.CPhysicsPart::Draw @0x0050D7A0always feedsDrawMesh(gfxobj[deg_level], &draw_pos). -
The facing law —
CPhysicsPart::calc_draw_frame @0x0050DFA0, driven by the FIRST degrade entry's mode (GfxObjDegradeInfo::get_degrade @0x0051E4B0;viewer_heading= normalized part→viewer fromUpdateViewerDistance @0x0050E030):draw = pos switch deg_mode: 2: Frame::set_vector_heading(draw, viewer_heading) // face viewer, roll-free 3/4/5: Frame::rotate_around_axis_to_vector(draw, X/Y/Z, vh) // cylindrical, one free axis else: authored orientation (mode 1, mode 0, out of range)Our renderer camera-plane-aligned every "billboard" particle (
cameraRight/cameraUp); retail faces each part toward the viewer per-part and honors constrained modes.CPhysicsPart::Always2D @0x0050D8A0(mode != 1) is only consulted for cell membership (CLandCell::add_all_outside_cells @0x00533360), not drawing. -
The aurora emitters (
0x32000455/56/57): BirthratePerSec 10, max 3, initial 3, lifespans 3300/900/400 s, StartTrans 0.8 → FinalTrans 1.0 (≤20 % opacity fading to nothing), StartScale 7–8, sprites0x01001A61..63= single ±137.5 m quads whose 64×64 additive textures are soft glow blobs (row/col profiled — no banding; peak RGB 36–71/255). First degrade mode = 2 on all three. The visible aurora is therefore nine huge, faint, viewer-facing glows in a slow Swarm drift — a pulse that re-brightens when a cohort is reborn (~6.7/15/55 min cycles), not a steady fixture.
Why the 2026-08-23 experiment looked like "whole-sky tint"
The debug controller (ACDREAM_ENABLE_SKY_PES=1) ran under day group 16
(Rainy) at t=0.125 — inside the lightning window (0.03–0.19) — so the
lightning-flash and thunder PES played at the camera anchor alongside the
aurora. A flash sprite at the anchor IS a full-screen additive wash. The
aurora itself was drowned under a faithful-but-wrong-conditions storm.
Port deltas (this change)
- Sky default-script playback becomes production (no env flag): every
visible sky object whose Setup carries a
DefaultScriptplays it throughPhysicsScriptRunner, anchored at the camera (retail sky-cell space is viewer-centered), pass-routed byprops & 1. - Persistence contract: script/emitter state is keyed by
(gfx id, properties) per
CreateDeletePhysicsObjects— a day-group flip that keeps the same carrier Setup must NOT restart its emitters. - Weather gating:
props & 4objects follow the weather-enabled state (acdream's weather system), matchingMakeObject's guard. calc_draw_framefacing law in the particle renderer: per-sprite degrade mode picks face-viewer (2), axis-constrained (3/4/5), or authored (else) orientation — replacing the blanket camera-plane alignment.
Script ids resolve from the Setup's DefaultScript (the retail source);
the PesObjectId column is only a cross-check.