acdream/docs/plans/2026-04-11-roadmap.md
Erik 55aaca7a14 feat(ui): Phase D.2a — VitalsPanel wired into GameWindow + backend pivot
Closes Phase D.2a. Launch with ACDREAM_DEVTOOLS=1 now shows a live
ImGui "Vitals" window whose HP bar reads CombatState.GetHealthPercent
for the local player. Without the env var the branches are dead code,
no ImGui context is created, and behaviour is identical to before.

GameWindow hunks:
  - fields: _imguiBootstrap / _panelHost / _vitalsVm + DevToolsEnabled
  - init (OnLoad): construct bootstrap + host, register VitalsPanel
  - GUID push: _vitalsVm?.SetLocalPlayerGuid(chosen.Id) at live-connect
  - frame begin: _imguiBootstrap.BeginFrame(dt) after GL clear
  - frame end: _panelHost.RenderAll(ctx) + _imguiBootstrap.Render() after debug overlay
  - input gating: skip WASD when ImGui.GetIO().WantCaptureKeyboard

Backend pivot: Hexa.NET.ImGui → ImGui.NET + Silk.NET.OpenGL.Extensions.ImGui.

First-light integration with the Hexa backend crashed 0xC0000005 inside
Hexa.NET.ImGui.Backends.OpenGL3.ImGuiImplOpenGL3.InitNative. Root cause:
Hexa's native OpenGL3 backend resolves GL function pointers via GLFW or
SDL internally; with Silk.NET (which uses neither) the pointers are null
and the native code crashes on first use. The mitigation path was
already planned — the design doc's Risk section called a pivot to
ImGui.NET a "one-morning operation" — and that's exactly what happened.

  - Packages: Hexa.NET.ImGui 2.2.9 + Hexa.NET.ImGui.Backends 1.0.18
    → ImGui.NET 1.91.6.1 + Silk.NET.OpenGL.Extensions.ImGui 2.23.0
  - ImGuiBootstrapper: was static Initialize(gl)+Shutdown() wrapping
    Hexa's OpenGL3 init; now an IDisposable wrapping Silk.NET's
    ImGuiController instance which handles GL backend init + input
    subscription in one go.
  - SilkInputBridge.cs deleted (~190 LOC): ImGuiController subscribes
    IKeyboard / IMouse events itself, we don't need a bespoke bridge.
  - ImGuiPanelRenderer: ImGuiNET.ImGui.* calls instead of
    Hexa.NET.ImGui.ImGui.*. Widget surface unchanged.

Boundary discipline is preserved — no panel imports ImGuiNET; only
ImGuiPanelRenderer does. The D.2b custom toolkit will implement the
same IPanelRenderer contract without touching panel code.

Out of scope (tracked for follow-up):
  - Stam/Mana currently return float? null (VitalsVM). Absolute values
    need LocalPlayerState + PlayerDescription (0x0013) parsing to be
    stored rather than discarded — filed as a post-D.2a issue.
  - Mouse-capture gating (WorldMouseFallThrough-style click-through
    tests) — not needed until we add clickable inventory items.

Roadmap + memory + architecture doc + UI framework plan updated in the
same commit per CLAUDE.md roadmap-discipline rules. 753 tests pass
(550 Core + 192 Core.Net + 11 new UI.Abstractions), 0 build warnings.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 00:43:46 +02:00

26 KiB
Raw Blame History

acdream — strategic roadmap

Status: Living document. Updated 2026-04-11 after Phase 6, 7.1, 9.1, 9.2 landed. Purpose: One source of truth for where the project is and where it's going. Every observed defect or missing feature has a named phase that owns it; when something looks wrong in-game, look here to find the phase that'll address it. Implementation details live in per-phase specs under docs/superpowers/specs/, not in this file.


Phases already shipped

Phase What landed Verification
1 Terrain rendering, plugin host scaffold Visual ✓
2a Static stabs/buildings (126 entities) Visual ✓
2b Textured 3×3 landblock grid + FlyCamera + IGameState Visual ✓
2c Procedural scenery (419 trees/rocks/bushes) Visual ✓
2d Interior EnvCell walker (475 static interior objects) Visual ✓
3a/3b Directional sun lighting + per-vertex terrain normals Visual ✓
3c Per-cell terrain texture blending (alpha atlas) Visual ✓
4 Full UDP codec + handshake + character login + WorldSession Live ✓
5 ObjDesc: AnimPartChange + TextureChanges + SubPalettes + ObjScale + Placement.Resting Live ✓
6.1 Idle motion frame resolution (MotionResolver MVP) Live ✓
6.2 Server-sent MovementData stance + forward command honored Live ✓
6.3 Server-supplied MotionTableId override (fixes drudge statue) Live ✓
6.4 Per-frame animation playback (breathing, idle cycles) Live ✓
6.5 Slerp between keyframes for smooth animation Live ✓
6.6 UpdateMotion (0xF74C) parser + dispatch to animation tick Live ✓
6.7 UpdatePosition (0xF748) parser + position reseating Live ✓
7.1 EnvCell room geometry — walls/floors/ceilings via CellStruct + Environment dats Visual ✓
9.1 Translucent render pass (AlphaBlend / Additive / InvAlpha + per-kind blend funcs) Visual ✓
9.2 Back-face culling in translucent pass (fixes lifestone crystal) Visual ✓
A.1 Streaming landblock loader — runtime-configurable visible window (default 5×5, ACDREAM_STREAM_RADIUS), camera-centered offline / player-centered live, hysteresis-based unloads, pending-spawn list for late CreateObject events Live ✓
A.2 Frustum culling — per-landblock AABB test (Gribb-Hartmann), terrain + static-mesh renderers skip culled landblocks, perf overlay in window title Visual ✓
A.3 Background net receive thread — dedicated daemon thread buffers UDP into Channel, render thread drains Visual ✓
B.3 Physics collision engine — TerrainSurface (heightmap Z), CellSurface (indoor floor polygon projection), PhysicsEngine (resolver with step-height + cell transitions). Populated from streaming pipeline. Tests ✓
B.2 Player movement mode — Tab-toggled WASD ground walking, walk/run/idle animations, third-person chase camera, MoveToState + AutonomousPosition outbound, portal entry. Outdoor-only MVP. Live ✓
D.1 2D ortho overlay + font rendering (StbTrueTypeSharp atlas + TextRenderer + DebugOverlay) Visual ✓
E.1 Motion-hook expansion — AnimationSequencer fires all 27 hook types per crossed frame; PosFrames root motion + vel/omega exposure; IAnimationHookSink + AnimationHookRouter fan-out Tests ✓
E.2 Audio engine — OpenAL 16-voice 3D pool with retail-faithful quieter-slot eviction, SoundTable cookbook (probability-weighted variant picking), Wave PCM decoder, AudioHookSink wiring Tests ✓
E.3 Particle system (data layer) — ParticleSystem with 13 motion integrators, EmitterDescRegistry, ParticleHookSink wiring all CreateParticle / DestroyParticle / StopParticle hooks Tests ✓
E.4 Combat notifications + outbound — AttackTargetRequest (0x0008), 7 combat notification parsers (Victim/Defender/Attacker/Evasion/AttackDone/UpdateHealth), CombatState per-entity health tracker Tests ✓
E.5 Spell cast wire — CastSpellRequest targeted (0x004A) + untargeted (0x0048), Spellbook (learned spells + active-enchantment layers), 5 enchantment GameEvent parsers Tests ✓
F.1 GameEvent (0xF7B0) envelope dispatcher — all 94 sub-opcodes routed, exception-isolated handler registry, unhandled-count diagnostic bag; 18 event-payload parsers Tests ✓
F.2 Item model + Appraise — ItemRepository with move/equip/property events, AppraiseRequest (0x00C8), IdentifyObjectResponse header, WieldObject + InventoryPutObjInContainer Tests ✓
G.1 Sky + day/night — DerethDateTime (retail-exact 7620-tick calendar + 16-hour names + PY year), SkyStateProvider (4-keyframe default with angular-wrap lerp), WorldTimeService (server-synced clock with real-time advance) Tests ✓
G.2 Dynamic lighting (selection) — LightSource + LightManager with retail 8-light cap, range-squared with 1.1× slack, slot 0 reserved for Sun, OwnerId-keyed unregister Tests ✓
G.1+ Full sky visuals + weather + dynamic-light shader — SkyDescLoader parses Region 0x13000000 dat keyframes with retail fog fields (start/end/mode); WeatherSystem picks Clear/Overcast/Rain/Snow/Storm deterministically per in-game day with 10s fade; SkyRenderer draws far-plane-1e6 celestial meshes with UV scroll; SceneLightingUbo binds at std140 location=1 with 8 Light slots + fog + lightning flash; terrain.vert + mesh.frag + mesh_instanced.frag + sky.frag all consume the shared UBO; LightingHookSink auto-registers Setup.Lights per entity + flips IsLit on SetLightHook; ParticleRenderer renders rain/snow billboards; F7 cycles day time override, F10 cycles weather; WorldSession surfaces server time via ServerTimeUpdated (ConnectRequest + TimeSync flag) Tests ✓
H.1 Chat wire layer — Talk (0x0015) / Tell (0x005D) / ChatChannel (0x0147) outbound, HearSpeech (0x02BB local + 0x02BC ranged) inbound, ChatLog ring buffer with adapters for every chat source Tests ✓
Glue GameEventWiring.WireAll — single-call registration mapping parsed GameEvents → Core state classes (ChatLog, CombatState, Spellbook, ItemRepository); GameWindow exposes state classes + wires them to live session Tests ✓
D.2a UI scaffold — AcDream.UI.Abstractions stable contract (IPanel / IPanelHost / IPanelRenderer / ICommandBus + VitalsVM / VitalsPanel); AcDream.UI.ImGui backend on ImGui.NET + Silk.NET.OpenGL.Extensions.ImGui (pivoted from Hexa.NET.ImGui on 2026-04-25 — Hexa's native OpenGL3 backend resolves GL via GLFW/SDL and crashed 0xC0000005 without them); VitalsPanel wired into GameWindow behind ACDREAM_DEVTOOLS=1 with ImGui.WantCaptureKeyboard WASD suppression. 11 new tests. Live ✓

Plus polish that doesn't get its own phase number:

  • FlyCamera default speed lowered + Shift-to-boost
  • SurfaceDecoder: PFID_P8 / PFID_R8G8B8 / PFID_X8R8G8B8 decoders
  • GfxObjMesh: emit both pos and neg sides of double-sided polygons
  • EnvCell mesh Z-lift to fix ground-floor / terrain flicker

Phases ahead — agreed order

Phase A — Foundation (in progress)

Goal: walk across 10+ landblocks without crashes, without hitches at landblock boundaries, and without framerate cratering.

Sub-pieces:

  • ✓ SHIPPED — A.1 — Streaming landblock loader. Runtime-configurable visible window (default 5×5, ACDREAM_STREAM_RADIUS env var override). Center follows the fly camera offline and the server-sent player position in live mode. Currently runs synchronously on the render thread — the original async-worker design hit DatCollection's lack of thread safety and was reverted for correctness. The Channel-based outbox API is preserved so async loading can return cleanly when Phase A.3 introduces a thread-safe dat wrapper. Pending-spawn list in GpuWorldState parks live CreateObject events whose target landblock hasn't been streamed in yet and back-fills them when it arrives, so spawn-vs-streaming races are no longer racy at all. MaxCompletionsPerFrame=4 spreads the 5×5 first-frame load over ~7 frames (~116ms) to avoid GPU OOM.
  • ✓ SHIPPED — A.2 — Frustum culling. Per-landblock AABB test (Gribb-Hartmann plane extraction + positive-vertex AABB test) in both TerrainRenderer.Draw and StaticMeshRenderer.Draw. Per-entity culling deferred. LOD deferred to Phase C. Performance overlay in window title shows FPS, frame time, visible/total landblock ratio, entity count, animated count. ~160fps uncapped at 5×5 radius.
  • ✓ SHIPPED — A.3 — Background net receive thread. Dedicated daemon thread continuously pulls raw UDP datagrams from the kernel buffer into a Channel<byte[]>. Render thread's Tick() drains the channel. All decode, fragment assembly, ISAAC crypto, event dispatch, and ack-sending remain on the render thread — minimal change that prevents packet drops during frame stalls. Thread starts after EnterWorld() completes; PumpOnce() during handshake still reads the socket directly.
  • A.4 — Async dat decoding. Folded into the streaming worker — it's the worker's read path, not a separate subsystem. Called out here because regressions in dat caching could land on this surface.

Acceptance:

  • Walk across 10+ landblocks in any direction, no crashes, no empty voids.
  • Landblock-boundary crossings produce no visible hitch.
  • Runtime window radius toggleable via environment variable.

Detailed spec: docs/superpowers/specs/2026-04-11-foundation-phase-design.md


Phase B — Gameplay / interaction

Goal: actually play the game — walk the character on the server, click NPCs, pick up items, chat, basic combat loop.

Sub-pieces:

  • ✓ SHIPPED — B.1 — Outbound ack pump. Shipped as Phase 4.9 — per-packet ACK_SEQUENCE, not periodic. Server no longer drops idle clients.
  • ✓ SHIPPED — B.2 — Player movement mode. Tab-toggled WASD ground walking with collision-resolved outdoor terrain, walk/run/idle/turn-right animations, third-person chase camera, outbound MoveToState (0xF61C) + AutonomousPosition (0xF753) server messages, portal entry works. Outdoor→indoor transition disabled for MVP (CellSurface floor polygons too aggressive without portal-based detection). Minor polish remaining: strafe animation, turn-left animation. Spec: docs/superpowers/specs/2026-04-12-player-movement-design.md.
  • ✓ SHIPPED — B.3 — Physics collision engine. TerrainSurface (heightmap bilinear Z), CellSurface (indoor floor polygon projection via barycentric interpolation), PhysicsEngine (top-level resolver with step-height enforcement, outdoor↔indoor cell transitions, gravity reporting). Populated from streaming pipeline. 16 unit tests with fake data. Spec: docs/superpowers/specs/2026-04-12-physics-collision-engine-design.md.
  • B.4 — Use / UseWithTarget / PickUp. Outbound interaction messages. Drives opening doors, looting, talking to vendors.
  • B.5 — Chat. SendTell, SendChat outbound + receive/display inbound (display side depends on Phase D.1).

References:

  • references/ACE/Source/ACE.Server/Network/Handlers/MovementHandler.cs
  • references/ACE/Source/ACE.Server/Network/Handlers/UseObjectHandler.cs
  • references/holtburger/src/session/send.rs for outbound packet-building patterns

Acceptance: walk on-server with your character, open a door, talk to an NPC, send a chat message and see the echo.


Phase C — Polish / visuals

Goal: close the visible gaps that make the world read as "old / broken" compared to retail.

Sub-pieces:

  • C.1 — VFX / particle system. PhysicsScript parser, per-entity ParticleEmitter state, billboarded-quad particle renderer that lives in the Phase 9.1/9.2 translucent pass. Delivers portal swirls, chimney smoke, and fireplace flames in one implementation.
  • C.2 — Dynamic point lights. Fireplaces and lamps need local lighting; small upgrade to the mesh shader to accumulate N (e.g., 4) nearest point lights per draw. Uniform-buffer or UBO-friendly layout.
  • C.3 — Palette range tuning. Small per-range offset/length tweaks to match retail skin/hair/eye colors. Mostly diff and verify work, no architecture change.
  • C.4 — Double-sided translucent polys. Edge case left by Phase 9.2: neg-side translucent polys are culled because cull is always BACK. Fix by tracking per-sub-mesh CullMode and flipping GL state per draw (or drawing twice with opposite cull). Minor.
  • C.5 — Shadow mapping (optional). Deferred unless it becomes a bottleneck in screenshots — dynamic shadows are a known complexity trap.

References:

  • references/ACE/Source/ACE.DatLoader/FileTypes/PhysicsScript.cs for the emitter schema
  • references/ACViewer/ACViewer/Physics/Particles/ for the visual model
  • references/WorldBuilder/Chorizite.OpenGLSDLBackend/Lib/ParticleBatcher.cs for the Silk.NET-flavored implementation

Acceptance: portals look like swirly gates, chimneys smoke, fireplaces burn, character skin matches retail screenshots.


Phase D — UI / HUD + Sound

Goal: chat window, nameplates, inventory, and audio. Can run concurrently with Phase B or C because it doesn't touch gameplay/net/rendering surfaces.

Updated 2026-04-24 — staged backend strategy. We split Phase D into two stages so game logic can be validated quickly without waiting for the full retail-look custom toolkit. See docs/plans/2026-04-24-ui-framework.md for the full design. Short version:

  1. D.2a — ImGui as the short-term backend. Wire up in days, iterate game logic (chat-send, inventory actions, vitals HUD reading real state) in weeks. Looks like a debugger; that's fine. (Shipped 2026-04-25 on ImGui.NET + Silk.NET.OpenGL.Extensions.ImGui after a day-one pivot away from Hexa.NET.ImGui; see shipped table.)
  2. Stable AcDream.UI.Abstractions layer — ViewModels + Commands + IPanel / IPanelRenderer interfaces. Backend-agnostic. Plugin API publishes against this layer and never sees ImGui.
  3. D.2b onward — custom retail-look backend using dat assets (icons, panels, fonts). Swap one panel at a time; ImGui stays forever as the ACDREAM_DEVTOOLS=1 overlay for packet trace / state dump / dat browser.

Every sub-piece below (D.3 AcFont, D.4 dat sprites, D.5 core panels, D.6 HUD, D.7 cursors) ships against the abstraction layer, so it doesn't matter which backend is drawing when the port lands.

Sub-pieces:

  • D.1 — 2D ortho overlay + font rendering. SHIPPED 2026-04-17 as the dev-facing debug overlay (StbTrueTypeSharp system-font atlas + TextRenderer + DebugOverlay).
  • ✓ SHIPPED — D.2a — ImGui scaffold + AcDream.UI.Abstractions layer. Shipped 2026-04-25. Wires ImGui as the short-term backend behind ACDREAM_DEVTOOLS=1. Defines IPanel / IPanelHost / IPanelRenderer / ICommandBus + the first ViewModel (VitalsVM) in the new AcDream.UI.Abstractions project. First real panel: VitalsPanel reading HP from CombatState.GetHealthPercent. Backend pivoted Hexa.NET.ImGui → ImGui.NET + Silk.NET.OpenGL.Extensions.ImGui during integration — Hexa's native OpenGL3 backend does its own GL function resolution via GLFW/SDL and crashed with 0xC0000005 in ImGuiImplOpenGL3.InitNative against Silk.NET (no GLFW/SDL present). The Silk.NET extension is purpose-built for this scenario and is the ImGui.NET mitigation path that docs/plans/2026-04-24-ui-framework.md already called out as a "one-morning operation". Stam/Mana return float? null in D.2a because absolute values need LocalPlayerState + PlayerDescription (0x0013) parsing (filed post-D.2a). 11 new AcDream.UI.Abstractions.Tests green.
  • D.2b — Custom retail-look backend. Implements the same IPanel / IPanelRenderer contracts using a custom retained-mode toolkit sourced from retail dat assets. Requires D.2a shipped. Panels get reskinned one at a time; ImGui stays as the ACDREAM_DEVTOOLS=1 overlay forever. The original 2026-04-17 scaffold research (UiRoot / UiElement / UiPanel / UiHost + retail event codes + focus / drag-drop state machine + WorldMouseFallThrough) is the implementation foundation here — see docs/research/retail-ui/.
  • D.3 — AcFont from portal.dat. Replace stb_truetype system font with retail Font DBObjs (0x40000000..0x40000FFF) baked from RenderSurface source sheets — see research slice 03 §4. Preserves retail visual identity. (D.2b dependency — needs the custom renderer.)
  • D.4 — Dat sprites + 9-slice panel backgrounds. Load RenderSurface (0x06xxxxxx) as GL textures; add DrawSprite to UiRenderContext. Enables retail panel art. (D.2b dependency.)
  • D.5 — Core panels. Attributes (chunk_00470000.c:FUN_0047ba70), Skills (same), Paperdoll (chunk_004A0000.c:FUN_004A5200), Inventory, Spellbook (chunk_004C0000.c), Fellowship, Allegiance. Each uses the port sketches in slice 05. (Targets AcDream.UI.Abstractions — ships with D.2a using ImGui-rendered widgets; reskinned by D.2b.)
  • D.6 — HUD. Vital orbs (scissor-rect partial fill, dat sprites 0x060013B2), radar (0x06001388 / 0x06004CC1, 1.18× range factor), compass strip (scrolling U), target name plate, damage floaters, selection indicator. See slice 06. (Targets AcDream.UI.Abstractions — ships with D.2a; reskinned by D.2b.)
  • D.7 — Cursor manager. OS + dat-sourced custom cursors (FUN_0043c1c0 GDI HCURSOR builder pattern from slice 03). (D.2b dependency.)
  • D.8 — Sound. Superseded — shipped as Phase E.2 (SoundTable/Sound dat decode, OpenAL 16-voice engine, per-entity 3D positional audio via AudioHookSink). Entry kept here for history; see the shipped table.

Reference docs: docs/research/retail-ui/00-master-synthesis.md + slices 01-06. Every AC-specific behavior has a decompiled FUN_ / DAT_ citation.

Acceptance: chat messages display in a retail-style panel, health/stamina/mana orbs fill correctly, attributes panel shows player stats, inventory opens with drag-drop working, and sound plays on hit/footstep.


Phase E — "Feel alive" — motion hooks + audio + VFX

Unlocks the sensory layer: footsteps, swing whooshes, impact sparks, spell auras. All three systems share animation-hook delivery as the trigger source — motion hooks are the blocker.

Research backing: docs/research/deepdives/00-master-synthesis.md + R3 + R4 + R5.

  • E.1 — Motion-hook expansion. Extend MotionInterpreter + AnimationSequencer to deliver all 27 AnimationHookType values (Sound=1, SoundTable=2, CreateParticle=18, AttackFrame=20, SoundTweaked=21, …) during frame advance. See r03-motion-animation.md.
  • E.2 — Audio engine. AudioEngine via Silk.NET.OpenAL, inverse-square CPU falloff, 16-voice pool. SoundTable + Wave dat decoders. Motion-hook wiring for footsteps + attacks. See r05-audio-sound.md.
  • E.3 — Particle system. ParticleSystem on Silk.NET GL (port WorldBuilder's ParticleBatcher). 13 motion-type integrators. PhysicsScript dispatcher tied to motion-hooks. See r04-vfx-particles.md.

Acceptance: walk around, footstep sounds per material; swing a weapon, hear whoosh; combat impact shows sparks + hit sound.

Phase F — Fight + cast + gear

Core gameplay loop on top of Phase E.

Research: R1 + R2 + R6 + R8 + UI slices 04/05.

  • F.1 — GameEvent envelope dispatcher. 94 sub-opcodes in 0xF7B0. Zero handled today. Start with PlayerDescription (0x0013) + property-update events. See r08-network-protocol-atlas.md.
  • F.2 — Item + inventory model. ItemInstance / Container / PropertyBundle. Appraise round-trip (0x00C80x00C9). Burden math. See r06-items-inventory.md + src/AcDream.Core/Items/.
  • F.3 — Combat math + damage flow. Damage formula, per-body-part AL, crit, hit-chance sigmoid. Server broadcasts damage events; client displays + HP bar. See r02-combat-system.md + src/AcDream.Core/Combat/.
  • F.4 — Spell cast state machine. SpellCastStateMachine + active buff tracking. Buffs + recalls first, projectile spells later. Fizzle sigmoid + mana conversion. See r01-spell-system.md + src/AcDream.Core/Spells/.
  • F.5 — Core panels. Attributes / Skills / Paperdoll / Inventory / Spellbook — using the retail-ui framework from Phase D.2. See 05-panels.md under retail-ui. (Targets AcDream.UI.Abstractions; unblocked by D.2a — ships with ImGui widgets — and reskinned when D.2b lands.)

Acceptance: equip a weapon, swing at a monster, see damage numbers, buff yourself, recall to the lifestone.

Phase G — World systems

Research: R9 + R12 + R13.

  • ✓ SHIPPED — G.1 — Sky + weather + day-night. Deterministic client-side from Portal Year time. Sky dome geometry + keyframe gradients + rain/snow particles. See r12-weather-daynight.md. Full data + visual stack shipped: Region dat loader, keyframe interp, WeatherSystem with 5-kind PDF + transitions + storm flashes, WorldSession→WorldTimeService sync via ConnectRequest+TimeSync, SkyRenderer with sky-object arcs + UV scroll, rain/snow billboard renderer, F7/F10 debug cycle keys.
  • ✓ SHIPPED — G.2 — Dynamic lighting. 8-light D3D-style fixed pipeline. Hard-cutoff at Range, no attenuation inside. Cell ambient. Shader UBO per frame. See r13-dynamic-lighting.md. SceneLightingUbo std140 at binding=1 feeds terrain + mesh + mesh_instanced + sky shaders. LightingHookSink auto-registers Setup.Lights at entity stream-in, flips IsLit on SetLightHook, unregisters on landblock unload.
  • G.3 — Dungeon streaming + portal space. EnvCellStreamer, portal-visibility BFS, PlayerTeleport (0xF751) handling with LoginComplete re-send, "pink bubble" loading state. See r09-dungeon-portal-space.md.

Acceptance: walk outside at dusk, see the sky gradient + sun moving; enter a torch-lit dungeon via portal; leave back to daylight.

Phase H — Social + progression

Research: R7 + R10 + R11 + UI slice 05.

  • H.1 — Chat window. UI panel + all 6 wire opcodes (Channel, Tell, System, HearSpeech, HearRangedSpeech, TurbineChat). (Wire layer already shipped per the shipped table; remaining work is the panel, which targets AcDream.UI.Abstractions — unblocked by D.2a, reskinned when D.2b lands.)
  • H.2 — Allegiance. Tree model + XP pass-up math + 5 allegiance chat channels + MOTD. See r11-allegiance.md.
  • H.3 — Emote scripts + quests + dialogs. 122 EmoteType × 39 Trigger mini-VM. Contract tracker UI. NPC dialog rendered via chat with <Tell:…> markup. See r10-quest-dialogs.md.
  • H.4 — Character creation. 0xE000002 CharGen dat + 13 heritages + templates + appearance picker + preview renderer. See r07-character-creation.md.

Acceptance: create a character from scratch, talk to an NPC, get + complete a quest, gain XP that passes up to the patron.

Phase J — Long-tail (deferred / low-priority)

Not detailed here; each gets its own brainstorm when it becomes relevant.

  • Player character full rig (held weapons, spell effects, death/revive animation)
  • Group/fellowship UI
  • Trade window (multi-player confirms)
  • Salvage + tinker UI
  • House ownership + hooks
  • Society UI
  • GM visible tools (we explicitly don't build GM powers; but DEV-mode tools like teleport anywhere are useful)

Cross-cutting work tracked in parallel

  • Test coverage. Each phase lands with unit + integration tests in tests/. Current count: 98 Core + 96 Core.Net = 194. Keep the ratio as new phases land.
  • Memory files. Project state under memory/project_phase_*_state.md is updated when a phase ships. MEMORY.md is the index.
  • CLAUDE.md discipline. Check all four references (ACE, ACViewer, WorldBuilder, Chorizite) before committing to an approach. WorldBuilder is the closest stack match and should be checked first.

Explicitly out of scope

  • Server emulation — we use ACE for server, never reimplement.
  • Account creation — direct user to ACE tooling.
  • Anti-cheat / GM tools / live-ops — irrelevant for personal use.
  • Cross-platform support — Windows-only; the dat path assumptions depend on retail Windows install layout. Silk.NET is cross-platform but we don't promise.
  • Custom game content — this is a client for existing AC data, not a toolchain.

"When will my specific complaint be fixed?" — quick lookup

Observation Phase
Drudge statue in wrong pose 6.3 FIXED
Characters in T-pose / wrong idle 6.1 FIXED
No breathing on NPCs 6.4 + sentinel fix FIXED
Lifestone crystal has one side missing 9.2 FIXED
Ground floor flickering with terrain 7.1 FIXED
Houses missing second floors / walls 7.1 FIXED ✓ (interior mesh landed)
Character clothing missing / wrong 5 FIXED
Statue wrong color / wrong scale 5 FIXED
Holtburg sign half-buried 5 FIXED
Can't walk past the loaded 3×3 window A.1 FIXED ✓ (5×5 default, ACDREAM_STREAM_RADIUS to tune)
Frame hitch crossing landblock boundary Phase A.3 (synchronous loader for now; async returns when DatCollection is thread-safe)
Walking around doesn't move me on the server Phase B.3 FIXED
Can't talk to NPCs Phase H.3 (emote scripts + dialogs)
Can't open a door Phase F (object-use action)
Portals render as a rotating black disk Phase E.3 (particle system)
Chimneys have no smoke Phase E.3
Houses have no fireplace fire Phase E.3
No fireplace / torch lighting Phase G.2 (shipped; Setup.Lights auto-register, 8-light cap with hard-cutoff)
Skin/hair color slightly off Phase C.3
No chat window Phase H.1
No sound Phase E.2
Dungeons / foundry interior missing Phase G.3
Can't fight monsters Phase F.3 (combat math + damage)
Can't cast spells Phase F.4
No inventory panel Phase F.2 + F.5
No character creation — must use ACE admin Phase H.4
Sky is a flat color Phase G.1 (shipped; F7 cycles time, F10 cycles weather)
Can't join allegiance Phase H.2
No quest tracker Phase H.3

If you see something not on this list, add it here and assign a phase.