Sub-phase under existing F.5 (Core panels) capturing the immediate follow-up to ISSUES.md #13: now that PlayerDescriptionParser surfaces the full trailer (Inventory / Equipped / Shortcuts / HotbarSpells / DesiredComps / Options1+2 / SpellbookFilters) and GameEventWiring populates ItemRepository at login, F.5a wires that data into minimal ImGui dev panels under ACDREAM_DEVTOOLS=1 so it's observable in-game. Establishes the binding pattern (AcDream.UI.Abstractions ViewModels → ImGui renderer) that the eventual D.2b retail-skinned F.5 panels reuse. Spec to brainstorm before code.
838 lines
71 KiB
Markdown
838 lines
71 KiB
Markdown
# acdream — strategic roadmap
|
||
|
||
**Status:** Living document. Updated 2026-05-09 for Phase N.5b shipping (terrain on the modern rendering path via Path C — mirror WB's `TerrainRenderManager` pattern, consume `LandblockMesh.Build` for retail formula compliance; closes ISSUE #51). N.6 (perf polish) remains the in-flight phase.
|
||
**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 MVP resolver foundation — terrain contact, CellSurface prototype, streaming-populated collision inputs, and first `PhysicsEngine` resolver path. Not the complete retail collision system. | 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 window — wire layer + panel + outbound input + holtburger inbound parity all shipped (I.1-I.7). Talk (0x0015) / Tell (0x005D) / ChatChannel (0x0147) outbound + EmoteText (0x01E0) / SoulEmote (0x01E2) / ServerMessage (0xF7E0) / PlayerKilled (0x019E) / TurbineChat (0xF7DE) / SetTurbineChatChannels (0x0295) inbound; `ChatPanel` with Enter-to-submit input + slash commands; `CombatChatTranslator` posts combat events as chat lines. | Live ✓ |
|
||
| 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 ✓ |
|
||
| I.1 | `IPanelRenderer` widget extension — TextColored / Checkbox / Combo / InputTextSubmit / BeginTable + ~9 more widget signatures on `IPanelRenderer`; matching `ImGuiPanelRenderer` impls. Foundation for I.2 + I.4. | Tests ✓ |
|
||
| I.2 | DebugPanel migration — replaced 473-LOC StbTrueTypeSharp `DebugOverlay` with `AcDream.UI.Abstractions/Panels/Debug/DebugPanel` (collapsing-headers + diagnostics checkboxes + combat-event tail). `DebugOverlay.cs` deleted; `TextRenderer` + `BitmapFont` retained for the future world-space HUD (D.6). | Live ✓ |
|
||
| I.3 | `LiveCommandBus` + `WorldSession.SendTalk` / `SendTell` / `SendChannel` — replaces `NullCommandBus.Instance` with a real handler-registry `ICommandBus`. New `SendChatCmd` record + `ChannelResolver` legacy-id mapping (per holtburger). 3-line wrappers around existing `ChatRequests.BuildTalk/Tell/ChatChannel`. | Tests ✓ |
|
||
| I.4 | `ChatPanel` input field + slash commands — Enter-to-submit input field; `ChatInputParser` recognises `/say` `/t` `/tell` `/r` `/g` `/f` `/a` `/m` `/p` `/v` `/cv` `/lfg` `/trade` `/role` `/society` `/olthoi`; `ChatVM.LastIncomingTellSender` tracks for `/r` reply. `ImGui.WantCaptureKeyboard` already suppresses WASD on focus. | Live ✓ |
|
||
| I.5 | Holtburger inbound chat parity + Windows-1252 codec — `EmoteText (0x01E0)`, `SoulEmote (0x01E2)`, `ServerMessage (0xF7E0)`, `PlayerKilled (0x019E)` parsers + `WeenieError` routing through `GameEventWiring`. Global string codec switch from `Encoding.ASCII` to `Encoding.GetEncoding(1252)` so accented names round-trip per retail + holtburger. | Tests ✓ |
|
||
| I.6 | TurbineChat codec + `ChatChannelInfo` — full `0xF7DE` codec with three payload variants (`EventSendToRoom`, `RequestSendToRoomById`, `Response`), UTF-16LE strings with variable-length prefix, `SetTurbineChatChannels (0x0295)` parser, unified `ChatChannelInfo` (Legacy + Turbine variants), `TurbineChatState`. **ACE doesn't host a TurbineChat server — codec is ready when retail-emulating servers exist.** | Tests ✓ |
|
||
| I.7 | `CombatChatTranslator` — retail-faithful combat-text formatters into `ChatLog` ("You hit drudge for 50 slashing damage (87%)"). Subscribes to `CombatState`'s `DamageTaken` / `DamageDealtAccepted` / `EvadedIncoming` / `MissedOutgoing` / `AttackDone` / `KillLanded`; templates ported verbatim from holtburger `panels/chat.rs:221-308`. | Tests ✓ |
|
||
| K | Input architecture — `Action` enum, `KeyChord`, `KeyBindings`, multicast `InputDispatcher` with scope-stack + modal capture, retail-default keymap (152 bindings), `keybinds.json` persistence, F11 Settings panel with click-to-rebind + conflict detection, main menu bar + View menu | Live ✓ |
|
||
| L.0 | Full retail-style Settings interface — F11 tabbed panel with 6 tabs (Keybinds + Display + Audio + Gameplay + Chat + Character). `settings.json` at `%LOCALAPPDATA%\acdream\`, per-toon `Character` keying (swapped on EnterWorld). Display GL knobs (Resolution / Fullscreen / VSync / FOV / ShowFps) + Audio (Master / SFX) live-wired; Gameplay / Chat / Character settings persist for server-sync wiring later. Tab API extension to `IPanelRenderer`; chat Copy mode (read-only multi-line); per-panel layout reset; FramebufferResize handler keeps GL viewport + camera aspect + panel positions in sync. | Live ✓ |
|
||
| C.1 | PES particle system + sky-pass refinements — retail-faithful `ParticleEmitterInfo` unpack with all 13 motion integrators (`Particle::Init`/`Update` ports of `0x0051c290`/`0x0051c930`), `PhysicsScriptRunner` with `CallPES` self-loop semantics, `ParticleHookSink` with `EmitterDied` cleanup, instanced billboard `ParticleRenderer` with material-derived blend (DAT emitters never default additive — pulled from particle GfxObj surface), global back-to-front sort, BC clipmap alpha-keying, AttachLocal `is_parent_local=1` live-parent follow via `UpdateEmitterAnchor`. Sky pass: `Translucent+ClipMap` → alpha-blend cloud sheet (matches `D3DPolyRender::SetSurface` `0x0059c4d0`), raw-`Additive` fog-skip (matches `0x0059c882`), per-keyframe `SkyObjectReplace` Translucency/Luminosity/MaxBright divide-by-100, bit `0x01` pre/post-scene split (matches `GameSky::CreateDeletePhysicsObjects` `0x005073c0`), Setup-backed (`0x020xxxxx`) sky objects via `SetupMesh.Flatten`, persistent GL sampler objects (Wrap + ClampToEdge) replace per-frame wrap-mode mutation (ported from WorldBuilder's `OpenGLGraphicsDevice`), post-scene Z-offset gated on `(Properties & 4) != 0 && (Properties & 8) == 0` per `GameSky::UpdatePosition` `0x00506dd0`. Sky-PES playback disabled by default (named-retail proves `GameSky` drops `pes_id`); `ACDREAM_ENABLE_SKY_PES=1` opens the experimental path. 1325 → 1331 tests. | Live ✓ |
|
||
| N.1 | WorldBuilder-backed scenery (Chorizite/WorldBuilder fork as submodule, SceneryHelpers + TerrainUtils replace our inline ports) | Live ✓ |
|
||
| N.3 | WorldBuilder-backed texture decode — `SurfaceDecoder` delegates INDEX16 / P8 / A8R8G8B8 / R8G8B8 / A8(+Additive) to `TextureHelpers.Fill*`; `isAdditive` threaded through (terrain alpha → `FillA8Additive`, non-additive entity surfaces → `FillA8`). R5G6B5 + A4R4G4B4 newly handled (previously magenta). X8R8G8B8, DXT1/3/5, SolidColor remain ours (no WB equivalent). 9 conformance tests prove byte-identical equivalence per format. | Live ✓ |
|
||
| N.4 | Rendering pipeline foundation — adopted WB's `ObjectMeshManager` as the production mesh pipeline behind `ACDREAM_USE_WB_FOUNDATION` (default-on). `WbMeshAdapter` is the single seam (owns `ObjectMeshManager`, drains the staged-upload queue per frame, populates `AcSurfaceMetadataTable` with per-batch translucency / luminosity / fog metadata). `WbDrawDispatcher` is the production draw path: groups all visible (entity, batch) pairs, single-uploads the matrix buffer, fires one `glDrawElementsInstancedBaseVertexBaseInstance` per group with `BaseInstance` slicing into the shared instance VBO. `LandblockSpawnAdapter` + `EntitySpawnAdapter` bridge spawn lifecycle to WB ref-counts (atlas tier vs per-instance). Perf wins shipped as part of N.4: per-entity frustum cull, opaque front-to-back sort, palette-hash memoization (compute once per entity, reuse across batches). Visual verification at Holtburg passed: scenery + connected characters with full close-detail geometry (Issue #47 regression resolved). Legacy `InstancedMeshRenderer` retained as `ACDREAM_USE_WB_FOUNDATION=0` escape hatch until N.6 (retired early in N.5 ship amendment). | Live ✓ |
|
||
| N.5 | Modern rendering path — lifted `WbDrawDispatcher` onto bindless textures (`GL_ARB_bindless_texture`) + `glMultiDrawElementsIndirect`. Per-frame entity rendering: 3 SSBO uploads (instance matrices @ binding=0, batch data @ binding=1, indirect commands) + 2 indirect draw calls (opaque + transparent). ~12-15 GL calls per frame regardless of group count, down from hundreds-of-per-group in N.4. CPU dispatcher: 1.23 ms/frame median at Holtburg courtyard (1662 groups, ~810 fps sustained). All textures on the WB modern path use 1-layer `Texture2DArray` + `sampler2DArray`. Legacy callers keep `Texture2D` / `sampler2D` via the parallel `TextureCache` path until N.6 retires them. Three gotchas captured in memory: texture target lock-in, bindless Dispose order (two-phase non-resident before delete), GL_TIME_ELAPSED double-buffering. **Ship amendment 2026-05-08:** legacy renderers (`InstancedMeshRenderer`, `StaticMeshRenderer`, `WbFoundationFlag`) retired within N.5 — modern path is mandatory; missing bindless throws `NotSupportedException` at startup. N.6 scope narrowed accordingly. Plan archived at `docs/superpowers/plans/2026-05-08-phase-n5-modern-rendering.md`. | Live ✓ |
|
||
| N.5b | Terrain on the modern rendering path — `TerrainModernRenderer` replaces `TerrainChunkRenderer` (the latter plus `TerrainRenderer` + `terrain.vert/.frag` deleted). Single global VBO/EBO with slot allocator (one slot per landblock), per-frame `DrawElementsIndirectCommand[]` upload + `glMultiDrawElementsIndirect`, bindless atlas handles passed as `uvec2` uniforms reconstructed via `sampler2DArray(handle)`. **Path C** chosen: mirrors WB's `TerrainRenderManager` pattern but consumes `LandblockMesh.Build` so retail's `FSplitNESW` formula is preserved (closes ISSUE #51). Path A killed by 49.98% measured divergence between WB's `CalculateSplitDirection` and retail's at addr `00531d10`; Path B (fork-patch WB) rejected for permanent maintenance burden. Perf at Holtburg radius=5 (commit `da56063`): modern 6.4-7.0 µs / 9-14 µs p95 vs legacy 1.5 µs / 3.0 µs — **modern is ~4× SLOWER on CPU at radius=5** because legacy's 16×16-LB chunking collapsed visible LBs to one `glDrawElements`. Architectural wins (zero `glBindTexture`/frame, constant-cost dispatch, per-LB frustum cull) manifest at higher radius (A.5 territory). Spec acceptance criterion 5 ("≥10% lower CPU at radius=5") amended via `docs/plans/2026-05-09-phase-n5b-perf-baseline.md`. Three gotchas captured in memory: `uniform sampler2DArray` + `glProgramUniformHandleARB` GL_INVALID_OPERATIONs on at least one driver (use `uniform uvec2` + `sampler2DArray(handle)` constructor instead — N.5's mesh_modern pattern); `MaybeFlushTerrainDiag` median-calc underflow on first sample; visual gates need actual visual confirmation, not assent. Plan archived at `docs/superpowers/plans/2026-05-09-phase-n5b-terrain-modern.md`. | 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.
|
||
- **A.5 — Two-tier streaming + terrain horizon LOD.** Split `ACDREAM_STREAM_RADIUS` into two: `ACDREAM_TERRAIN_RADIUS` (large, 8-12 cells = 1.5-2.3km) for terrain mesh + `ACDREAM_ENTITY_RADIUS` (small, 2-3 cells, current default) for entities + scenery. Distant landblocks render terrain only — no NPCs, no procedural scenery, no static objects. Tune `SceneLightingUbo`'s `uFogParams` so the far edge fades into sky color (eliminates the hard streaming boundary visible at higher radii). Optional: terrain LOD via mesh decimation for very distant chunks (combine 2×2 landblocks into one decimated mesh; cribs from `references/WorldBuilder/Chorizite.OpenGLSDLBackend/Lib/TerrainRenderManager.cs`). Motivation: at radius=5 today, perf scales from ~810 fps → ~200-300 fps because everything stays full-detail; both retail and WorldBuilder render terrain way out and strip entities/scenery at distance. Enables WB-style horizon visibility. **Estimate: 3-5 days for the radius split + fog tuning; +1 week if terrain LOD is included.** Not yet brainstormed.
|
||
|
||
**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 MVP resolver foundation.** Terrain contact, CellSurface prototype, streaming-populated collision inputs, and first `PhysicsEngine` resolver path. This shipped enough foundation for outdoor walking and early portal experiments, but it is not the complete retail collision system. Current conformance work lives under **Phase L.2 — Movement & Collision Conformance**. Spec history: `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:**
|
||
- **✓ SHIPPED — C.1 — VFX / particle system + sky-pass refinements.** Retail-faithful `ParticleEmitterInfo` runtime + 13-type motion integrator port + `PhysicsScript` runner + instanced billboard renderer with material-derived blend + global back-to-front sort + AttachLocal live-parent follow. Sky-pass refinements: Translucent+ClipMap alpha-blend, raw-Additive fog-skip, per-keyframe SkyObjectReplace divide-by-100, sampler-object wrap selection (ported from WorldBuilder), gated post-scene Z-offset. Sky-PES disabled by default — named-retail decomp proves `GameSky` drops `pes_id`. **Portal swirls, chimney smoke, fireplace flames** still need a Phase C.1.5 follow-up to wire entity-attached emitters to retail effect IDs (the data layer is ready; only the wiring is deferred). Lands as merge `feat(vfx): Phase C.1 — PES particle renderer + post-review fixes` (`ec1bbb4`) + `refactor(sky): replace per-frame wrap-mode mutation with persistent samplers` (`3d21c13`).
|
||
- **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`](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.)** The *chat* panel originally listed under D.5 shipped early in Phase I (I.4 input + I.7 combat translator superseded the chat-panel design here); this entry now covers Attributes / Skills / Paperdoll / Inventory / Spellbook / Fellowship / Allegiance only.
|
||
- **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.)** Phase I.2 retired the StbTrueTypeSharp `DebugOverlay` but kept `TextRenderer` + `BitmapFont` alive specifically for D.6's world-space HUD elements (damage floaters, name plates) — they need raw GL text drawing that ImGui can't reach into the 3D scene.
|
||
- **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 (`0x00C8` → `0x00C9`). 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.)**
|
||
- **F.5a — Visible-at-login dev panels.** First deliverable on top of #13 (PD trailer parser shipped 2026-05-10): wire `PlayerDescriptionParser.Parsed.{Inventory, Equipped, Shortcuts, HotbarSpells, DesiredComps, Options1, Options2, SpellbookFilters}` and `ItemRepository.Items` into minimal ImGui dev panels under `ACDREAM_DEVTOOLS=1` so the parsed data is observable in-game without a real retail-skin panel. Establishes the binding pattern (`AcDream.UI.Abstractions` ViewModels → ImGui renderer) the eventual D.2b retail-skinned panels reuse. Acceptance: log in, open dev overlay, see your inventory list / hotbars / shortcuts / character-options bitfields populated from the live PD message. **Targets:** `src/AcDream.UI.Abstractions/` (ViewModels) + `src/AcDream.App/UI/ImGui/` (panels). Spec to brainstorm before code.
|
||
|
||
**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. **Blocked on L.2e** for trustworthy `cell_bsp`, indoor/outdoor portal transit, adjacent-cell ownership, and building entry/exit collision boundaries. 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.
|
||
|
||
- **✓ SHIPPED — H.1 — Chat window.** UI panel + all 6 wire opcodes (Channel, Tell, System, HearSpeech, HearRangedSpeech, TurbineChat). Wire layer + panel + outbound input + holtburger inbound parity + combat translator all shipped across I.1-I.7 on 2026-04-25. Targets `AcDream.UI.Abstractions`; will be reskinned when D.2b's custom retail-look toolkit 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 I — UI consolidation + complete chat system
|
||
|
||
**Goal:** retire the dual UI stack (custom StbTrueTypeSharp DebugOverlay + ImGui)
|
||
in favour of ImGui hosting *all* dev/debug surfaces, then finish the chat
|
||
system end-to-end (input, slash commands, holtburger inbound parity, combat
|
||
translator). After this phase, dev tools live in one place and chat works
|
||
the way retail + holtburger expect.
|
||
|
||
**Sub-pieces (all ✓ SHIPPED 2026-04-25):**
|
||
|
||
- **✓ SHIPPED — I.1 — `IPanelRenderer` widget extension.** Adds `TextColored`, `Checkbox`, `Combo`, `InputTextSubmit`, `BeginTable` + ~9 more widget signatures to `IPanelRenderer` and matching `ImGuiPanelRenderer` impls. Foundation for I.2 (DebugPanel) and I.4 (chat input). Commit `b131514`.
|
||
- **✓ SHIPPED — I.2 — DebugPanel migration.** Replaces the 473-LOC StbTrueTypeSharp `DebugOverlay` with `AcDream.UI.Abstractions/Panels/Debug/DebugPanel` (collapsing-headers + diagnostics checkboxes + combat-event tail). `DebugOverlay.cs` deleted; `TextRenderer` + `BitmapFont` retained for the future world-space HUD (D.6 — damage floaters, name plates). Commit `56037a4`.
|
||
- **✓ SHIPPED — I.3 — `LiveCommandBus` + `WorldSession.Send{Talk,Tell,Channel}`.** Replaces `NullCommandBus.Instance` with a real handler-registry `ICommandBus`. New `SendChatCmd` record + `ChatChannelKind` enum + `ChannelResolver` legacy-id mapping (per holtburger). `WorldSession.SendTalk` / `SendTell` / `SendChannel` are 3-line wrappers around existing `ChatRequests.BuildTalk/Tell/ChatChannel`. Commit `8e6e5a0`.
|
||
- **✓ SHIPPED — I.4 — `ChatPanel` input field + slash commands.** Enter-to-submit input field on `ChatPanel`; `ChatInputParser` recognises `/say` `/t` `/tell` `/r` `/g` `/f` `/a` `/m` `/p` `/v` `/cv` `/lfg` `/trade` `/role` `/society` `/olthoi`; `ChatVM.LastIncomingTellSender` tracks for `/r` reply. `ImGui.WantCaptureKeyboard` already suppresses WASD on input focus. Commit `f14296c`.
|
||
- **✓ SHIPPED — I.5 — Holtburger inbound chat parity + Windows-1252.** `EmoteText (0x01E0)`, `SoulEmote (0x01E2)`, `ServerMessage (0xF7E0)`, `PlayerKilled (0x019E)` parsers + `WeenieError` routing through `GameEventWiring`. Global string codec switch from `Encoding.ASCII` to `Encoding.GetEncoding(1252)` so accented names round-trip per retail + holtburger. Commit `ff5ed9e`.
|
||
- **✓ SHIPPED — I.6 — TurbineChat codec + `ChatChannelInfo`.** Full `0xF7DE` codec with three payload variants (`EventSendToRoom`, `RequestSendToRoomById`, `Response`), UTF-16LE strings with variable-length prefix, `SetTurbineChatChannels (0x0295)` parser, unified `ChatChannelInfo` (Legacy + Turbine variants), `TurbineChatState`. **ACE doesn't host a TurbineChat server — codec is ready when retail-emulating servers exist.** Commit `ca968fc`.
|
||
- **✓ SHIPPED — I.7 — `CombatChatTranslator`.** Retail-faithful combat-text formatters into `ChatLog` ("You hit drudge for 50 slashing damage (87%)"). Subscribes to `CombatState`'s `DamageTaken` / `DamageDealtAccepted` / `EvadedIncoming` / `MissedOutgoing` / `AttackDone` / `KillLanded`; templates ported verbatim from holtburger `panels/chat.rs:221-308`. Commit `3d26c8e`.
|
||
- **✓ SHIPPED — I.8 — Docs alignment.** Roadmap (this file) + `docs/ISSUES.md` issues #14-#20 closed + `memory/project_chat_pipeline.md` crib + `MEMORY.md` index entry + `CLAUDE.md` UI strategy paragraph all updated to reflect Phase I shipped state. Commit `(this commit)`.
|
||
|
||
**Acceptance (verified 2026-04-25):**
|
||
- All ImGui dev panels open with `ACDREAM_DEVTOOLS=1`: VitalsPanel + ChatPanel + DebugPanel.
|
||
- Old `DebugOverlay.cs` is deleted; `TextRenderer` / `BitmapFont` still compile for D.6.
|
||
- `/say hello` in chat sends a Talk packet; local echo lands in the panel.
|
||
- Attacking a creature produces "You hit Drudge for 12 slashing damage (87%)" lines in chat.
|
||
- Accented character names round-trip through the wire correctly (CP1252).
|
||
|
||
**Memory crib:** `memory/project_chat_pipeline.md`.
|
||
|
||
---
|
||
|
||
### Phase K — Input architecture + retail bindings + Settings panel
|
||
|
||
**Goal:** retire the hardcoded WASD-style scheme; ship retail-faithful
|
||
bindings as the default; let users remap any action via an in-game
|
||
Settings panel; auto-enter player mode at login; replace mouse-X-yaw
|
||
with retail's MMB-hold mouse-look.
|
||
|
||
**Sub-pieces (all ✓ SHIPPED 2026-04-26):**
|
||
|
||
- **✓ SHIPPED — K.1a — Input architecture skeleton.** Action enum,
|
||
multicast `InputDispatcher` with scope stack, `KeyChord` /
|
||
`Binding` / `KeyBindings`, Silk.NET adapters, parallel to existing
|
||
handlers — no behavior change. Commit `84512d3`.
|
||
- **✓ SHIPPED — K.1b — Cut handlers over to dispatcher.** Drop the
|
||
legacy mouse-X-character-yaw path, fix `WantCaptureMouse` gating,
|
||
single input path. Commit `256e962`.
|
||
- **✓ SHIPPED — K.1c — Retail-default keymap + JSON persistence.**
|
||
~149 bindings matching
|
||
`docs/research/named-retail/retail-default.keymap.txt` + JSON
|
||
load/save with merge-over-defaults migration at
|
||
`%LOCALAPPDATA%\acdream\keybinds.json`. Acdream debug F-keys
|
||
relocated to `Ctrl+F*` to avoid retail conflicts. Commit `da18910`.
|
||
- **✓ SHIPPED — K.2 — Login flow + MMB mouse-look + free-fly button.**
|
||
Auto-enter player mode at login (one-shot guard reusing the existing
|
||
Tab handler), MMB-hold mouse-look (retail's `CameraInstantMouseLook`
|
||
— cursor-locked camera + character yaw drive together), `DebugPanel`
|
||
"Toggle Free-Fly Mode" button, `Tab → ToggleChatEntry →
|
||
ChatPanel.FocusInput()`. Commit `af74eac`.
|
||
- **✓ SHIPPED — K.3 — Settings panel + Keybindings UI + roadmap/issues
|
||
update.** `SettingsPanel` with click-to-rebind UX (modal capture
|
||
via `InputDispatcher.BeginCapture`, Esc cancels, conflict-detection-
|
||
with-confirmation prompt, draft / Save / Cancel semantics), F11
|
||
toggle + ImGui MainMenuBar entry, per-action / per-section /
|
||
reset-all-defaults buttons. Roadmap + ISSUES + memory crib +
|
||
CLAUDE.md updated in this commit. Commit `(this commit)`.
|
||
|
||
**Acceptance (verified 2026-04-26):**
|
||
- Login → spawn at the character's actual world position in chase
|
||
camera (NOT Holtburg orbit). Tab not required.
|
||
- W = run forward; X = run back; A/D = turn; Z/C = strafe;
|
||
Alt+A/D/Left/Right = strafe (alternate); Q = autorun; Space = jump;
|
||
Shift = walk modifier; S = stop; Y/G/H/B = postures.
|
||
- Mouse alone does NOT drive character yaw. MMB-hold = cursor-locked
|
||
camera + character yaw drive together. RMB and LMB are
|
||
`SelectRight` / `SelectLeft` per retail.
|
||
- F11 / View → Settings opens panel. Click "Rebind" next to any
|
||
action → press a key → if conflict, prompt. Save writes
|
||
`%LOCALAPPDATA%\acdream\keybinds.json`. Restart preserves
|
||
customizations.
|
||
- Mode-dependent combat keys (Insert/PgUp/Delete/End/PgDn) bound but
|
||
dormant — light up in Phase L when `CombatState.CurrentMode` is
|
||
wired (issue #L.3).
|
||
|
||
**Memory crib:** `memory/project_input_pipeline.md`.
|
||
|
||
---
|
||
|
||
### Phase L.1 — Animation system completion
|
||
|
||
**Status:** IN PROGRESS on `feature/animation-system-complete`.
|
||
|
||
**Goal:** complete the retail-faithful animation surface beyond the
|
||
locomotion/jump K-fix series: combat swings, spell casting, emotes, death,
|
||
item-use, NPC/monster special actions, remote observer parity, and the
|
||
remaining floating-point polish around style transitions, modifiers, action
|
||
queues, speed scaling, and PosFrame root motion.
|
||
|
||
**Plan of record:** `docs/plans/animation-system-audit.md`.
|
||
|
||
**Coupling to L.2:** L.1 owns animation/motion parity. L.2 owns collision,
|
||
contact truth, movement packets, and server-visible placement. They meet where
|
||
root motion or observer movement changes the predicted body path; any such
|
||
change must keep both phase plans in sync.
|
||
|
||
**Sub-pieces:**
|
||
- **L.1a — Audit & inventory.** Map retail named-decomp evidence, ACE
|
||
cross-references, existing acdream hook points, and current gaps for each
|
||
animation category. Output: `docs/plans/animation-system-audit.md`.
|
||
- **L.1b — Command router + motion-state cleanup.** Extract tested
|
||
`SetCycle` vs `PlayAction` routing, add missing `MotionCommand` constants,
|
||
and split death `Sanctuary` action from persistent `Dead` substate.
|
||
- **L.1c — Combat animation wiring.** Combat mode tracking, draw/sheath
|
||
style transitions, attack swings by stance/power/height, hit reactions,
|
||
evades/blocks/parries, and death handoff.
|
||
- **L.1d — Spell casting wiring.** Cast command classification, windup,
|
||
release, fizzle/interruption, recoil, and school/effect distinctions.
|
||
- **L.1e — Emotes + postures.** Outbound slash emotes, inbound
|
||
command-list emotes, and persistent sit/lie/kneel/sleep states.
|
||
- **L.1f — NPC/monster + item-use coverage.** Scripted gestures, monster
|
||
special actions, potion/food/scroll/recall cycles, and remote parity.
|
||
- **L.1g — Polish + conformance.** Style-transition chain, durable
|
||
modifiers/action queues, root-motion handling, speed scaling, and broad
|
||
synthetic MotionTable tests.
|
||
|
||
**Acceptance:**
|
||
- `dotnet build` and `dotnet test` green at each commit.
|
||
- Test count grows by at least 30 with one representative cycle/action test
|
||
per major animation category.
|
||
- Every AC-specific behavior cites named retail decomp or ACE/holtburger
|
||
cross-reference evidence in code comments, tests, or commit notes.
|
||
- User visual sign-off for local and remote attack, spell, emote, death, and
|
||
item-use animation parity before marking shipped.
|
||
|
||
---
|
||
|
||
### Phase L.2 — Movement & Collision Conformance
|
||
|
||
**Status:** ACTIVE.
|
||
|
||
**Goal:** make acdream's movement and collision behavior retail-faithful across
|
||
terrain, buildings, walls, roof edges, cell seams, portal boundaries, outbound
|
||
movement packets, and server correction. This is the holistic bucket for the
|
||
work previously scattered across B.3 physics follow-ups, L.1 motion coupling,
|
||
and G.3 dungeon/portal ownership.
|
||
|
||
**Plan of record:** `docs/plans/2026-04-29-movement-collision-conformance.md`.
|
||
|
||
**Current foundation:** `PhysicsEngine.ResolveWithTransition`,
|
||
`BSPQuery`, `TransitionTypes`, `PhysicsDataCache`, and
|
||
`ShadowObjectRegistry` exist and are active. They are partial retail ports and
|
||
diagnostic scaffolding, not yet the final collision system.
|
||
|
||
**Sub-lanes:**
|
||
- **L.2a — Truth & diagnostics.** Local placement/contact/cell logs, object-hit
|
||
probes, correction-delta diagnostics, retail-observer capture workflow, and
|
||
real-DAT fixture capture.
|
||
- **L.2b — Movement wire/contact authority.** Fix outbound contact truth,
|
||
full-cell id handling, packet cadence, and routine server correction handling.
|
||
- **L.2c — Transition parity: edge/slide/neg-poly.** Port and test retail
|
||
`edge_slide`, `cliff_slide`, `precipice_slide`, step-up/down slide, and
|
||
`NegPolyHit` dispatch behavior.
|
||
- **L.2d — Shape fidelity: sphere/cylsphere/building objects.** Finish
|
||
`CSphere` / `CCylSphere` parity, live-entity object shapes, building object
|
||
collision identity, and `Setup.Radius` fallback audit.
|
||
- **L.2e — Cell ownership: outdoor seams, `CELLARRAY`, `cell_bsp`.** Update
|
||
low outdoor cell id across 24m seams, port adjacent-cell checks, activate
|
||
`cell_bsp`, and hand G.3 a trustworthy building/portal boundary model.
|
||
- **L.2f — Real-DAT and live retail-observer conformance.** Promote synthetic
|
||
tests to real-world fixtures and verify local acdream view plus retail
|
||
observer view. ACE accepting a position is a compatibility check, not proof
|
||
of fine-grained retail collision parity.
|
||
|
||
**Acceptance:**
|
||
- A developer can trace the active movement path: input/motion -> body
|
||
prediction -> `ResolveWithTransition` -> contact/cell result -> outbound
|
||
packets -> server echo/correction.
|
||
- Buildings, edge-slide, wall-slide, cell seams, packet authority, and dungeon
|
||
portal ownership each have an L.2 lane.
|
||
- Every AC-specific algorithm port cites named retail decomp, or a documented
|
||
fallback when named retail lacks the body.
|
||
- `dotnet build` and `dotnet test` are green for each implementation slice.
|
||
|
||
---
|
||
|
||
### Phase M — Network Stack Conformance
|
||
|
||
**Status:** PLANNED.
|
||
|
||
**Goal:** replace the current happy-path `WorldSession` networking shape with a
|
||
proper AC client network stack that reaches functional parity with
|
||
`references/holtburger/` while preserving acdream's stricter packet checksum
|
||
verification and live ACE compatibility. This phase owns packet reliability,
|
||
ordered delivery, retransmission, ACK piggybacking, echo/keepalive, typed
|
||
message/action routing, diagnostics, and the migration of low-level network
|
||
responsibilities out of the render tick.
|
||
|
||
**Why now:** The Phase 4/A.3 stack is good enough for local ACE smoke testing:
|
||
login, ISAAC, checksums, per-packet ACKs, fragments, movement, chat, combat,
|
||
and object updates all work. It is not yet a complete client network runtime.
|
||
Compared with holtburger it lacks ordered S2C delivery, retransmit request
|
||
handling, outbound packet caching/retransmission, ACK piggybacking,
|
||
EchoRequest/EchoResponse handling, runtime ping/timeout policy, and a typed
|
||
protocol/action layer. These gaps will become expensive as movement, dungeons,
|
||
inventory, combat, and plugins depend on stable packet semantics.
|
||
|
||
**Plan of record:** create
|
||
`docs/superpowers/specs/2026-05-02-network-stack-conformance.md` before
|
||
implementation starts. Treat holtburger as the client-behavior oracle for this
|
||
phase; cross-check wire details against named retail, ACE, Chorizite, and AC2D
|
||
before porting.
|
||
|
||
**Sub-lanes:**
|
||
- **M.1 — Audit & parity map.** Produce a source-by-source comparison of
|
||
acdream `AcDream.Core.Net` and holtburger `holtburger-session`,
|
||
`holtburger-protocol`, and `holtburger-core` networking code. Inventory each
|
||
packet flag, optional header, session transition, control packet, fragment
|
||
path, game message, and game action. Mark each as `parity`, `partial`,
|
||
`missing`, or `intentional divergence`.
|
||
- **M.2 — Layer extraction.** Split the low-level stack under `WorldSession`
|
||
into testable components: `INetTransport`, `PacketCodec`,
|
||
`ReliablePacketSession`, `FragmentSession`, `GameMessageSession`, and the
|
||
high-level `WorldSession` behavior layer. Preserve existing public events and
|
||
live-client call sites during the migration.
|
||
- **M.3 — Reliability core.** Port holtburger-style sequence tracking:
|
||
last-delivered server sequence, duplicate/old-packet suppression,
|
||
out-of-order buffering, missing-sequence detection, throttled
|
||
`RequestRetransmit`, outbound packet cache, server-requested C2S retransmit,
|
||
`RejectRetransmit` handling, and retransmission checksum recomputation.
|
||
- **M.4 — ACK and control-packet policy.** Replace standalone-only ACKs with
|
||
queued ACK state, ACK piggybacking on normal outbound packets, standalone ACK
|
||
flush when idle, and clean handling for ACK-only packets. Add EchoRequest /
|
||
EchoResponse and idle ping/timeout behavior matching holtburger unless named
|
||
retail proves a different cadence.
|
||
- **M.5 — Fragment and payload completeness.** Keep inbound multi-fragment
|
||
assembly, add TTL/eviction diagnostics for orphaned partials, implement
|
||
outbound multi-fragment splitting when payloads exceed the current single
|
||
fragment limit, and verify fragment id/sequence/queue behavior against
|
||
holtburger, ACE, and retail evidence.
|
||
- **M.6 — Typed protocol surface.** Introduce typed `GameMessage` and
|
||
`GameAction` routing modeled on holtburger. Migrate current builders and
|
||
parsers first: login complete, DDD, movement, chat/TurbineChat, combat,
|
||
spellcast, item/appraise, object/update/motion/position, and teleport.
|
||
Unknown opcodes must remain observable and non-fatal.
|
||
- **M.7 — Runtime loop and diagnostics.** Move decode/order/reassembly/control
|
||
handling out of the render tick into a dedicated network runtime that emits
|
||
clean session events through channels. Add byte counters, packet trace hooks,
|
||
optional capture/replay fixtures, idle/disconnect state, and dev-panel
|
||
diagnostics suitable for packet debugging.
|
||
- **M.8 — Conformance tests and live validation.** Add deterministic unit tests
|
||
for checksum, ISAAC key consumption, optional headers, ordering, retransmit,
|
||
ACK piggybacking, echo, fragments, typed actions, and login flow. Add
|
||
replay/capture tests from holtburger-style fixtures where possible. Finish
|
||
with `dotnet build`, `dotnet test`, local ACE login, chat, movement, combat
|
||
action smoke test, reconnect test, and user visual sign-off where networking
|
||
affects the running client.
|
||
|
||
**Non-goals:**
|
||
- Do not reimplement ACE server behavior.
|
||
- Do not replace acdream's stricter inbound checksum verification unless named
|
||
retail proves it is wrong.
|
||
- Do not rewrite renderer/gameplay systems as part of this phase; migrate
|
||
network call sites through compatibility adapters first.
|
||
- Do not remove unknown-message visibility. Plugins and devtools need packet
|
||
trace surfaces even when parsers are incomplete.
|
||
|
||
**Acceptance:**
|
||
- acdream has a layered, testable network stack rather than one monolithic
|
||
`WorldSession`.
|
||
- Every holtburger session capability has an acdream equivalent, an explicit
|
||
test, or a documented intentional divergence with retail/ACE evidence.
|
||
- Packet ordering, retransmit, outbound cache, ACK piggybacking, echo/keepalive,
|
||
fragment assembly/splitting, and typed message/action routing are covered by
|
||
tests.
|
||
- Live ACE loop succeeds: login, enter world, movement, chat, combat action,
|
||
teleport/reconnect, and clean logout.
|
||
- `dotnet build` and `dotnet test` are green for every implementation slice.
|
||
|
||
---
|
||
|
||
### Phase N — WorldBuilder Rendering Migration
|
||
|
||
**Goal:** Stop re-porting AC-specific rendering / dat-handling
|
||
algorithms. Depend on a fork of `Chorizite/WorldBuilder` (MIT) for
|
||
terrain, scenery, static objects, EnvCells, portals, sky, particles,
|
||
texture decoding, mesh extraction, and visibility. Acdream keeps its
|
||
own network, physics, animation, motion, UI, plugin, audio, chat
|
||
layers (those aren't in WB).
|
||
|
||
**Why now (2026-05-08):** the scenery edge-vertex bug at landblock
|
||
`0xA9B1` was the third subtle porting bug in a quarter (after the
|
||
triangle-Z bug and the hover-over-terrain bug). Even when our code
|
||
looked byte-identical to WB's, our output diverged. WB renders the
|
||
world correctly; the cost of "we re-port retail algorithms" is now
|
||
higher than "we depend on WB's tested port."
|
||
|
||
**Design + inventory:**
|
||
|
||
- `docs/architecture/worldbuilder-inventory.md` — full taxonomy of
|
||
what WB has and what we keep porting ourselves.
|
||
- `docs/superpowers/specs/2026-05-08-phase-n-worldbuilder-migration-design.md` —
|
||
parent design doc.
|
||
- `docs/superpowers/specs/2026-05-08-phase-n1-scenery-via-wb-helpers-design.md` —
|
||
N.1 detailed design.
|
||
|
||
**Integration model:** fork at
|
||
`https://github.com/eriknihlen/WorldBuilder` (already created), git
|
||
submodule replacing `references/WorldBuilder/` snapshot, project
|
||
references in our solution. Long-lived `acdream` branch in the fork
|
||
for our deletions/additions; merge upstream `master` periodically.
|
||
|
||
**Lessons from N.1 (apply to N.2-N.10):**
|
||
|
||
1. **Per-helper conformance tests work.** The N.1 conformance test caught a
|
||
~180° rotation bug in our retail port that had been silently wrong
|
||
forever. Write the conformance test BEFORE the substitution in each
|
||
sub-phase.
|
||
|
||
2. **ACME ≠ Chorizite/WorldBuilder.** ACME is a downstream fork of WB with
|
||
additional retail-faithful filters that upstream WB (our submodule)
|
||
doesn't have. When a visual discrepancy appears, check ACME's source
|
||
(`references/WorldBuilder-ACME-Edition/`) for delta filters BEFORE
|
||
investigating retail decomp directly. ACME's deltas tend to come as
|
||
coherent units — porting one filter without its companions can
|
||
over-suppress.
|
||
|
||
3. **"Whackamole" is the warning sign.** If a phase generates 3+ visual
|
||
regressions on default-on, stop, accept the cosmetic deltas as
|
||
ISSUES.md entries, ship the migration. Bugs we leave behind are
|
||
debuggable; bugs we never ship are forgotten.
|
||
|
||
4. **Subagent-driven execution holds up at this scope.** Fresh subagent
|
||
per task with the full task text inline keeps quality high without
|
||
polluting the controller's context. Each task should be self-contained
|
||
enough that a subagent without session history can complete it.
|
||
|
||
**Sub-phases (strangler-fig with feature flags):**
|
||
|
||
- **✓ SHIPPED — N.0 — Setup.** Shipped 2026-05-08 (commit `c8782c9`).
|
||
WorldBuilder fork at `github.com/eriknihlen/WorldBuilder.git` registered
|
||
as git submodule at `references/WorldBuilder/` tracking the `acdream`
|
||
branch. `AcDream.Core.csproj` references `WorldBuilder.Shared` +
|
||
`Chorizite.OpenGLSDLBackend`. Build green, all 28 scenery/terrain tests
|
||
passing.
|
||
- **✓ SHIPPED — N.1 — Scenery algorithm calls.** Shipped 2026-05-08.
|
||
Replaced `IsOnRoad` / `DisplaceObject` / slope-normal calc / rotation /
|
||
scale inside `SceneryGenerator.Generate()` with calls to WB's
|
||
`SceneryHelpers` + `TerrainUtils`. Adapter `WbSceneryAdapter` produces
|
||
`TerrainEntry[]`. Visual verification at Holtburg confirmed Issue #49's
|
||
previously missing edge-vertex trees still visible after the migration;
|
||
rotation bug fixed (our retail port's `yawDeg = -(450-degrees)%360`
|
||
formula was ~180° off from retail's actual `Frame::set_heading` atan2
|
||
round-trip). One known cosmetic difference filed in ISSUES.md
|
||
(road-edge tree at landblock 0xA9B1).
|
||
- **N.2 — Terrain math helpers.** ⚠️ **Blocked on N.5 — do not attempt
|
||
in isolation.** Originally scoped as a 1-2 day low-risk substitution
|
||
of `TerrainSurface.SampleZ` / `SampleSurface` / `SampleSurfacePolygon`
|
||
with WB's `TerrainUtils.GetHeight` / `GetNormal`. Audit during N.3
|
||
follow-up uncovered that **WB's `CalculateSplitDirection` uses a
|
||
different formula than retail's `FSplitNESW`** (the AC2D-cited
|
||
polynomial `0x0CCAC033` / `0x421BE3BD` / `0x6C1AC587` / `0x519B8F25`
|
||
that our visual terrain mesh and physics already share). The
|
||
formulas pick different cell-diagonals on disputed cells, producing
|
||
up to ~2m Z divergence at the same world position. Substituting
|
||
physics-side alone would un-sync physics from the still-ours visual
|
||
mesh — exactly the triangle-Z hover bug class. N.1's conformance
|
||
test proved WB's `GetNormal` is good enough for slope-filtering
|
||
(boolean walkable check) but NOT that WB's height formula matches
|
||
retail. Resolution: fold this work into **N.5** when the visual
|
||
mesh switches to WB's renderer in lockstep with physics. Until
|
||
then, leave `TerrainSurface` alone. See ISSUE #51.
|
||
- **✓ SHIPPED — N.3 — Texture decoding.** Shipped 2026-05-08. `SurfaceDecoder`
|
||
now delegates INDEX16 / P8 / A8R8G8B8 / R8G8B8 / A8 to WB's
|
||
`TextureHelpers.Fill*`. The A8 divergence (our old code did R=G=B=A=val
|
||
always; WB splits additive vs non-additive) was resolved by threading an
|
||
`isAdditive` parameter through `DecodeRenderSurface`: terrain alpha masks
|
||
pass `isAdditive: true` (matches our prior behavior, preserves the
|
||
shader's `.r` blend-weight read), entity surfaces pass
|
||
`surface.Type.HasFlag(SurfaceType.Additive)`. Bonus: R5G6B5 + A4R4G4B4
|
||
formats now decode (previously fell to magenta). X8R8G8B8, DXT1/3/5, and
|
||
SolidColor remain ours (no WB equivalent). **9 conformance tests prove
|
||
byte-identical equivalence per format** before substitution; updated
|
||
`SurfaceDecoderTests` to match the new A8 split semantics. Visual
|
||
verification at Holtburg passed 2026-05-08 — no texture regressions.
|
||
- **✓ SHIPPED — N.4 — Rendering pipeline foundation.** Shipped 2026-05-08.
|
||
WB's `ObjectMeshManager` is integrated as the production mesh pipeline
|
||
behind `ACDREAM_USE_WB_FOUNDATION=1` (default-on). The integration is
|
||
three pieces: `WbMeshAdapter` (single seam owning the WB pipeline,
|
||
drains the staged-upload queue per frame, populates
|
||
`AcSurfaceMetadataTable` for translucency / luminosity / fog),
|
||
`WbDrawDispatcher` (production draw path — groups all visible
|
||
(entity, batch) pairs, uploads matrices in a single `glBufferData`,
|
||
fires one `glDrawElementsInstancedBaseVertexBaseInstance` per group
|
||
with `BaseInstance` slicing the shared instance VBO), and the
|
||
`LandblockSpawnAdapter` + `EntitySpawnAdapter` bridge that wires our
|
||
streaming loader to WB's `IncrementRefCount` / `PrepareMeshDataAsync`
|
||
lifecycle (atlas tier vs per-instance customized).
|
||
Issue #47 (close-detail mesh) preserved; sky pass structurally
|
||
independent of the WB foundation. Perf wins shipped as part of N.4:
|
||
per-entity AABB frustum cull, opaque front-to-back sort, palette-hash
|
||
memoization. Legacy `InstancedMeshRenderer` retained as flag-off
|
||
fallback until N.6 fully retires it. Plan archived at
|
||
`docs/superpowers/plans/2026-05-08-phase-n4-rendering-foundation.md`.
|
||
- **✓ SHIPPED — N.5 — Modern rendering path.** Shipped 2026-05-08.
|
||
**Rebranded from "Terrain rendering" 2026-05-08 after N.4 perf
|
||
review.** Lifted `WbDrawDispatcher` onto bindless textures
|
||
(`GL_ARB_bindless_texture`) + `glMultiDrawElementsIndirect`. Per-frame
|
||
entity rendering: 3 SSBO uploads (instance matrices @ binding=0, batch
|
||
data @ binding=1, indirect commands) + 2 indirect calls (opaque +
|
||
transparent). ~12-15 GL calls per frame regardless of group count, down
|
||
from hundreds-of-per-group in N.4. CPU dispatcher: 1.23 ms/frame median
|
||
at Holtburg (1662 groups, ~810 fps). All textures on the modern path use
|
||
1-layer `Texture2DArray` + `sampler2DArray`; legacy callers retain
|
||
`Texture2D` via the parallel `TextureCache` path until N.6 retires them.
|
||
Three gotchas in memory (`project_phase_n5_state.md`): texture target
|
||
lock-in, bindless Dispose two-phase order, GL_TIME_ELAPSED double-
|
||
buffering. Plan archived at
|
||
`docs/superpowers/plans/2026-05-08-phase-n5-modern-rendering.md`.
|
||
- **✓ SHIPPED — N.5b — Terrain on the modern rendering path.** Shipped
|
||
2026-05-09. **Path C** (mirror WB's `TerrainRenderManager` pattern but
|
||
consume `LandblockMesh.Build` for retail-formula compliance). Path A
|
||
(substitute WB's `CalculateSplitDirection`) killed during pre-implementation
|
||
divergence test: WB's formula disagrees with retail's `FSplitNESW`
|
||
(addr `00531d10`) on **49.98%** of cells across `tests/AcDream.Core.Tests/Terrain/SplitFormulaDivergenceTest.cs`'s
|
||
sweep — wholly incompatible with our shared physics + visual mesh.
|
||
Path B (fork-patch WB to use retail's formula) rejected for permanent
|
||
maintenance burden. Path C ships the architectural pattern (single
|
||
global VBO/EBO + slot allocator + bindless atlas + `glMultiDrawElementsIndirect`)
|
||
while keeping retail's formula via `LandblockMesh.Build` →
|
||
`TerrainBlending.CalculateSplitDirection`. `TerrainModernRenderer` +
|
||
`terrain_modern.vert/.frag` shipped, `TerrainChunkRenderer` +
|
||
`TerrainRenderer` + legacy `terrain.vert/.frag` deleted in T9.
|
||
Closes ISSUE #51. **Perf reality check:** at radius=5 in Holtburg,
|
||
modern is ~4× SLOWER on CPU than legacy was (6.4 µs vs 1.5 µs median;
|
||
legacy collapsed radius=5's visible LBs into one `glDrawElements`
|
||
via 16×16-LB chunking). Architectural wins (zero `glBindTexture`/frame,
|
||
constant-cost dispatch as A.5 raises radius, per-LB frustum cull)
|
||
manifest at higher radius. Spec acceptance criterion #5 was wrong;
|
||
amended via `docs/plans/2026-05-09-phase-n5b-perf-baseline.md`. Plan
|
||
archived at `docs/superpowers/plans/2026-05-09-phase-n5b-terrain-modern.md`.
|
||
- **N.6 — Perf polish.** **Currently in flight.**
|
||
Builds on N.5 + N.5b. Legacy renderer retirement was pulled forward
|
||
into N.5 ship amendment — `InstancedMeshRenderer`, `StaticMeshRenderer`,
|
||
`WbFoundationFlag` are gone — and the terrain legacy renderer
|
||
(`TerrainChunkRenderer` + `TerrainRenderer` + `terrain.vert/.frag`)
|
||
retired in N.5b. N.6 scope: WB atlas adoption for memory savings
|
||
on shared content, persistent-mapped buffers if `glBufferData` shows
|
||
up in profiling (the modern terrain path's per-frame DEIC `BufferSubData`
|
||
is a candidate), GPU-side culling via compute pre-pass (eliminates
|
||
the per-frame slot walk + DEIC build entirely), GL_TIME_ELAPSED query
|
||
double-buffering (deferred from N.5 — diagnostic shows `gpu_us=0/0`
|
||
under `ACDREAM_WB_DIAG=1`), direct higher-radius perf comparison once
|
||
A.5 lands (where modern's architectural wins manifest), retire the
|
||
legacy `Texture2D`/`sampler2D` path in `TextureCache` (currently kept
|
||
for Sky + Debug + particle paths now that Terrain has migrated).
|
||
Plan + spec written when work begins. **Estimate: 1-2 weeks.**
|
||
- **N.7 — EnvCells / dungeons.** Replace EnvCell rendering with WB's
|
||
`EnvCellRenderManager` + `PortalRenderManager` on top of N.4's
|
||
foundation. **Estimate: 1-2 weeks** (was 2-3 — naturally smaller now
|
||
that infrastructure is shared).
|
||
- **N.8 — Sky + particles.** Replace sky rendering + particle pipeline
|
||
(#36 / C.1 work) with WB's `SkyboxRenderManager` +
|
||
`ParticleEmitterRenderer`. **Estimate: ~1 week** (was 1.5-2 — C.1
|
||
already shipped most of this; N.8 is glue + sampler-object reuse).
|
||
- **N.9 — Visibility / culling.** Replace `CellVisibility` +
|
||
`FrustumCuller` with WB's `VisibilityManager`. **Estimate: ~1 week**
|
||
(was 3-5 days, slight bump for streaming-loader interaction).
|
||
- **N.10 — GL infrastructure consolidation (optional).** Replace our
|
||
`Shader` / `TextureCache` / `SamplerCache` plumbing with WB's
|
||
`ManagedGL*` wrappers + `OpenGLGraphicsDevice`. **Largely subsumed by
|
||
N.4** — `OpenGLGraphicsDevice` arrives as the host of `ObjectMeshManager`
|
||
and atlas. May not need a dedicated phase; revisit after N.6.
|
||
|
||
**Estimated calendar:** **2.5-3 months / 9-13 engineering weeks for
|
||
N.4-N.9 (N.10 likely subsumed; N.2 folded into N.5; N.3 shipped).**
|
||
Revised 2026-05-08 after recognizing N.4-N.6 are one rendering rebuild
|
||
on shared infrastructure rather than three independent substitutions.
|
||
|
||
**Each sub-phase:**
|
||
- Ships behind `ACDREAM_USE_WB_<NAME>=1` flag.
|
||
- Has its own conformance test (side-by-side against existing path).
|
||
- Visual verification before flag becomes default-on.
|
||
- Old code deleted after default-on lands cleanly.
|
||
|
||
**N.2-N.10 detailed specs are NOT yet written** — each gets its own
|
||
brainstorm + spec when we reach it.
|
||
|
||
**Acceptance:**
|
||
- All 10 sub-phases shipped, feature flags removed, old rendering code
|
||
paths deleted.
|
||
- Visual verification at Holtburg + Foundry statue + a representative
|
||
dungeon shows no regression vs Phase C.1.
|
||
- WB upstream merges into our `acdream` branch are clean (or have
|
||
documented conflict-resolution patterns).
|
||
|
||
---
|
||
|
||
### 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)
|
||
|
||
---
|
||
|
||
### Phase R — Retail research infrastructure
|
||
|
||
**Goal:** sustainable, scalable access to retail-client decompilation —
|
||
named symbols, struct layouts, wire schemas — so every future port is
|
||
a 5-second grep instead of 30-minute archaeology.
|
||
|
||
**Sub-pieces:**
|
||
|
||
- **✓ SHIPPED — R.1 — Named-retail corpus committed.** Shipped 2026-04-25
|
||
(commit `a9a01d8`). `docs/research/named-retail/{acclient_2013_pseudo_c.txt,
|
||
acclient.h, acclient.c}` + `docs/research/data/spells.csv` + vendored
|
||
`references/actestclient/`. 1.4 M lines of named pseudo-C (99.6% function
|
||
naming) + 70K lines of verbatim retail headers + 3,956 spells with `Family`
|
||
for buff stacking + machine-readable wire schema in `messages.xml`.
|
||
- **✓ SHIPPED — R.2 — pdb-extract tool + JSON sidecars.** Shipped 2026-04-25
|
||
(commit `69d884a`). `tools/pdb-extract/pdb_extract.py` reads
|
||
`refs/acclient.pdb` (Sept 2013 EoR build) and emits `symbols.json`
|
||
(18,366 named functions) + `types.json` (5,371 named struct types) to
|
||
`docs/research/named-retail/`. Pure Python, no deps, runs in <1 s.
|
||
- **✓ SHIPPED — R.3 — actestclient vendored.** Shipped 2026-04-25 alongside
|
||
R.1. `references/actestclient/` (covered by `references/` gitignore)
|
||
includes the canonical `messages.xml` AC-protocol wire schema.
|
||
|
||
**Acceptance:** Step 0 of `CLAUDE.md`'s development workflow points at
|
||
`named-retail/` first; subsequent issue closures (#6 / #7 / #9 / #11)
|
||
all consume this foundation.
|
||
|
||
**Effects on other phases:** Issue closures unblocked by Phase R land
|
||
under their existing letter phases (#6 enchantment buffs → Phase D /
|
||
F.5 maintenance; #7 PlayerDescription trailer → Phase H.1 / F.2
|
||
maintenance). The PDB symbols + headers also accelerate any future
|
||
port in any phase — no separate listing here.
|
||
|
||
---
|
||
|
||
## 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.2/B.3 FIXED** ✓ for coarse server movement; fine retail collision parity is **Phase L.2** |
|
||
| Packet loss / out-of-order UDP causes stale or missing world updates | **Phase M.3** |
|
||
| Server asks for retransmit and client doesn't resend cached packets | **Phase M.3** |
|
||
| ACKs are standalone-only instead of piggybacked like a full client | **Phase M.4** |
|
||
| Echo / idle keepalive / reconnect behavior is incomplete | **Phase M.4 + M.7** |
|
||
| Large outbound game messages exceed the current single-fragment path | **Phase M.5** |
|
||
| Network protocol coverage is spread across ad-hoc builders/parsers | **Phase M.6** |
|
||
| Sliding along buildings / walls feels wrong | **Phase L.2c + L.2d** |
|
||
| Roof edge / cliff / precipice blocks or slides wrong | **Phase L.2c** |
|
||
| Crossing outdoor cell seams reports the wrong cell | **Phase L.2e** |
|
||
| 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 / I.1-I.7 FIXED** ✓ |
|
||
| Chat? You can finally type and send messages | **I.4 FIXED** ✓ (shipped 2026-04-25) |
|
||
| Combat doesn't show in the chat log | **I.7 FIXED** ✓ |
|
||
| Accented character names show as `?` or garbled | **I.5 FIXED** ✓ (Windows-1252 codec) |
|
||
| No sound | **Phase E.2** |
|
||
| Dungeons / foundry interior missing | **Phase G.3** after **L.2e** cell/building ownership |
|
||
| 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.
|