Drive turn, movement, jump, and combat through the production InputDispatcher so connected Release testing works without an interactive Windows desktop. Track held automation actions through normal completion and every shutdown path. Add a seven-destination ACE route with post-liveness memory, allocation, update, fatal-log, outbound-movement, and graceful-close gates. Record dynamic ACE population changes as context instead of a false lifetime oracle, and document the accepted rebaseline. Co-authored-by: OpenAI Codex <codex@openai.com>
934 lines
55 KiB
Markdown
934 lines
55 KiB
Markdown
# acdream — milestones (morale + scope layer)
|
||
|
||
**Status:** Living document. Created 2026-05-12.
|
||
**Sits above:** [`docs/plans/2026-04-11-roadmap.md`](2026-04-11-roadmap.md) (the strategic phase index).
|
||
**Currently working toward:** **M3 — "Cast a spell." M2 — "Kill a drudge"
|
||
LANDED 2026-07-15.** The complete connected melee/missile, death/loot,
|
||
inventory, and item-giving demo is user-gated. The shared projectile/effect
|
||
foundation is hardened through Step 9. Spellbook/component-book filtering,
|
||
favorite spell bar, connected casting/enchantment effects, and portal-space
|
||
presentation have passed their single-client visual gates. The corrective bounded
|
||
render/resource-lifetime integration passed its seven-destination connected
|
||
stress gate without a crash or cumulative collapse. The remaining M3 visual
|
||
requirement is the final two-client portal-out/materialize observer gate. The
|
||
new R6 object-update cutover has passed its unattended connected input/portal/
|
||
performance rebaseline; its locomotion/collision/projectile/teleport visual
|
||
gate remains for the next local viewing session before that observer gate.
|
||
Carried:
|
||
#145-residual, #116 slide-response, the remaining R6 ownership cleanup plus
|
||
registered TS-50/TS-51 timing residuals (the complete-root-Frame/object-workset
|
||
cutover is automated-test complete 2026-07-20; three independent re-reviews are
|
||
clean, Release builds with zero warnings, and 6,452 tests pass / 5 skip), and
|
||
Track MP0.
|
||
|
||
---
|
||
|
||
## Why this document exists
|
||
|
||
The roadmap is a phase index — week- to month-scale, ~50 phases by the time
|
||
v1.0 lands. Phases ship in vertical slices (architecture-first, horizontal
|
||
completion deferred), which is the right strategy for a solo open-source
|
||
project at this scale — but it leaves a chronic "everything is half-built"
|
||
feeling because no single phase ship feels like a real milestone.
|
||
|
||
This document sits **one altitude above** the roadmap. Each milestone is:
|
||
|
||
- **~6–10 weeks of focused work** (not a single phase, not a whole year).
|
||
- Defined by a **concrete playable scenario** — when the scenario works
|
||
end-to-end, the milestone lands.
|
||
- A **scope-freeze event**: when a milestone lands, the phases it covers go
|
||
off-limits until v1.0's final polish pass (M7).
|
||
|
||
Crossing a milestone is a textual event — milestones doc gets the writeup,
|
||
the freeze list flips, CLAUDE.md's "currently working toward" line advances.
|
||
Phases ship; milestones **land**.
|
||
|
||
---
|
||
|
||
## Operating rules
|
||
|
||
1. **One active milestone at a time.** Everything not on the critical path to
|
||
the current milestone gets filed in `docs/ISSUES.md` with a `post-MN` tag
|
||
and explicitly muted until the milestone hits. This is what kills the
|
||
jumping-between-things feeling.
|
||
|
||
2. **Frozen phases are off-limits.** "Frozen" means no rework, no polish, no
|
||
follow-up commits unless something is actively broken (player crash,
|
||
regression). Visual nice-to-haves, "while I'm here" cleanups, and
|
||
architecture second-guesses are all post-M7. The freeze is the discipline
|
||
mechanism that makes the milestone meaningful — without it, M0's many
|
||
shipped phases keep silently consuming attention.
|
||
|
||
3. **The milestone log is the morale instrument.** When a milestone hits:
|
||
- Pin a one-paragraph writeup at the top of this doc describing what
|
||
works end-to-end (any caveats or known regressions are explicit).
|
||
- Update the freeze list. Update CLAUDE.md's "currently working toward"
|
||
line to the next milestone.
|
||
- NO demo videos. User explicitly removed that requirement 2026-05-16
|
||
("pointless of recording videos, for what purpose?").
|
||
|
||
4. **State both altitudes at session start.** "Currently working toward M1.
|
||
Current phase: L.2 collision. Next concrete step: L.2d slice 1 spec." This
|
||
keeps the high-level orientation visible alongside the immediate task and
|
||
makes mid-session drift obvious.
|
||
|
||
---
|
||
|
||
## The milestones
|
||
|
||
### M0 — "Connect & explore" — ✅ DONE (crossed months ago)
|
||
|
||
**Demo scenario:** Log in, walk Holtburg in chase camera, see other characters
|
||
animate, send a chat message and see the echo, watch day turn to night,
|
||
listen to footsteps and ambient audio.
|
||
|
||
**Phases included (frozen):**
|
||
|
||
| Phase | What landed |
|
||
|---|---|
|
||
| 1–3 | Terrain + per-vertex normals + per-cell texture blending |
|
||
| 4 | UDP codec + handshake + character login + WorldSession |
|
||
| 5 | ObjDesc: AnimPartChange + TextureChanges + SubPalettes + ObjScale |
|
||
| 6.1–6.7 | Motion + animation foundation (idle, frame playback, slerp, UpdateMotion, UpdatePosition) |
|
||
| 7.1 | EnvCell room geometry — walls/floors/ceilings |
|
||
| 9.1–9.2 | Translucent render pass + back-face culling |
|
||
| A.1–A.5 | Streaming landblock loader → two-tier streaming |
|
||
| B.1–B.3 | Outbound ack pump + player movement + physics MVP resolver |
|
||
| D.1, D.2a | 2D overlay + ImGui scaffold + `AcDream.UI.Abstractions` layer |
|
||
| E.1–E.5 | Motion hooks + audio + particles + combat wire + spell wire |
|
||
| F.1, F.2 | GameEvent dispatcher + item model + Appraise |
|
||
| G.1, G.2 | Sky + day/night + weather + dynamic lighting |
|
||
| H.1, I.1–I.8 | Chat wire + UI consolidation + holtburger inbound parity + combat translator |
|
||
| K, L.0 | Input architecture + retail bindings + Settings panel |
|
||
| N.0–N.6.1 | WB rendering migration (modern path mandatory) |
|
||
| C.1, C.1.5a/b | Particle system + portal/EnvCell static-script wiring |
|
||
| R.1–R.3 | Retail research infrastructure (PDB extract + named decomp) |
|
||
|
||
**Status:** This is everything shipped through 2026-05-12. ~25 phase ships.
|
||
**Worth saying out loud: this is the hard half of the project.** The engine
|
||
runs, the world renders correctly, the network connects, the input is wired,
|
||
the data layers for combat/spells/items/audio/particles all exist. What's
|
||
missing is the gameplay loop on top.
|
||
|
||
---
|
||
|
||
### M1 — "Walkable + clickable world" — ✅ LANDED 2026-05-16
|
||
|
||
**Landing writeup (2026-05-16, after Phase B.6 ship `d640ed7`):**
|
||
All four M1 demo targets work end-to-end. You can log into `+Acdream`
|
||
at Holtburg, walk freely without getting stuck (L.2 collision is
|
||
retail-faithful through the doorway). Click any inn door at any
|
||
range, press R, the character runs (or walks if close) toward it
|
||
with the correct animation cycle including the leg-shuffle turn-first
|
||
phase, opens the door via ACE's server-side `MoveToChain` callback.
|
||
Same for clicking an NPC — runs over, body rotates to face, dialogue
|
||
fires from ACE without any client-side retry. Pickup items at any
|
||
range with F or R; spell components, food, money, weapons all work;
|
||
signs and other `BF_STUCK` scenery correctly block at the gate.
|
||
AP cadence matches retail (0 Hz idle, ~1 Hz smooth motion, per-event
|
||
on cell/plane changes). Run/walk threshold matches retail observation
|
||
(1m of distance left to walk). The earlier ladder of workarounds —
|
||
client-side retry, per-frame chatty AP, MoveToState suppression
|
||
grace period — all deleted via the Phase B.6 architectural refactor
|
||
(`d640ed7`). M1's demo path is now bit-for-bit retail-faithful end
|
||
to end.
|
||
|
||
|
||
|
||
**Demo scenario:** Walk through Holtburg without getting stuck on the inn
|
||
doorway. Open the inn door. Click an NPC and see selection feedback. Pick
|
||
up an item from the ground.
|
||
|
||
**Demo-target status (as of 2026-05-14):**
|
||
|
||
| # | Target | Status | Evidence |
|
||
|---|---|---|---|
|
||
| 1 | Walk through Holtburg without getting stuck | ✅ met | L.2a/d/g shipped 2026-05-12; Holtburg doorway verified |
|
||
| 2 | Open the inn door | ✅ met | B.4b (interaction) + B.4c (swing animation) shipped 2026-05-13 |
|
||
| 3 | Click an NPC and see selection feedback | ✅ met | B.4b chain + chat handlers; verified 2026-05-14 (Tirenia + Royal Guard double-click → NPC dialogue in chat panel) |
|
||
| 4 | Pick up an item from the ground | ✅ met (close-range path) | B.5 + post-B.5 `PickupEvent (0xF74A)` fix shipped 2026-05-14; visual-verified at Holtburg; creature-pickup guard added in `a01ebd5` |
|
||
|
||
**Landing artifacts done 2026-05-16:**
|
||
- ✅ Landing writeup pinned at top of this milestone block (above the table).
|
||
- ✅ Freeze list applied (see below).
|
||
- ✅ `CLAUDE.md`'s "currently working toward" advanced to M2.
|
||
|
||
**Known polish items deferred to post-M7 (do not gate M1 landing):**
|
||
- **#61** — AnimationSequencer link→cycle frame-0 flash on door swing. LOW.
|
||
- **#62** — PARTSDIAG null-guard. Latent, not reachable today.
|
||
- **#63** — ✅ CLOSED by Phase B.6 (`d640ed7`). Server-initiated
|
||
`MoveToObject` is now honored end-to-end; ACE's `MoveToChain`
|
||
callback fires server-side on arrival.
|
||
- **#64** — Local-player pickup animation does not render (retail
|
||
observers see it correctly). LOW.
|
||
- **#69, #74, #75** — all closed by Phase B.6 (`d640ed7`). Turn-first
|
||
animation, retail-narrow AP cadence, body-direct auto-walk
|
||
architecture.
|
||
|
||
**Phases that shipped to clear M1:**
|
||
- **L.2 (a + d + g sub-lanes)** — Movement & Collision Conformance.
|
||
L.2a slices 1+2+3 + L.2d slice 1+1.5 + L.2g slice 1+1b+1c shipped
|
||
2026-05-12 / 2026-05-13. Visual-verified via the B.4b doorway test.
|
||
- **B.4b** — outbound Use + `WorldPicker` + double-click detection +
|
||
`CollisionExemption` widening + `ServerGuid→entity.Id` translation
|
||
(the ID-mismatch trap surfaced during L.2g slice 1c). Shipped
|
||
2026-05-13.
|
||
- **B.4c** — door swing animation: spawn-time `AnimationSequencer`
|
||
registration + stance-value fix (`NonCombat = 0x3D` not `0x01`, which
|
||
had been causing doors to render halfway underground). Shipped
|
||
2026-05-13.
|
||
- **B.5** — `BuildPickUp` (PutItemInContainer 0x0019) + `SendPickUp`
|
||
helper + F-key wiring + new `PickupEvent (0xF74A)` despawn handler.
|
||
Shipped 2026-05-14.
|
||
- **B.5 polish** (`a01ebd5`) — guard `SendPickUp` against creature
|
||
targets so F-on-NPC produces a "Can't pick that up" toast instead of
|
||
the malformed pickup that triggered ACE's `WeenieError 0x0029` + NPC
|
||
emote chain. (Briefly visited adding "You pick up the X." chat /
|
||
toast feedback for ground pickups in `87ba5c9`, then reverted in
|
||
`20ecb23` — retail doesn't show that line for ground pickups; only
|
||
for items received from NPCs / other characters, and that path is
|
||
separate.)
|
||
|
||
**Freeze on landing:**
|
||
- L.2 zone (collision, cell ownership, transition parity, wire authority)
|
||
- B.4 zone (interaction outbound)
|
||
- B.5 zone (pickup outbound + inbound despawn)
|
||
|
||
**What "M1 lands" looks like:** the existing Holtburg traversal works as a
|
||
retail player would expect. Doorways are walkable. Buildings have solid
|
||
walls. Outdoor cell seams report the right cell. Clicking an NPC selects it
|
||
and produces NPC chat. The Use action opens doors. F picks up items at
|
||
close range and the player sees "You pick up the X." in chat.
|
||
|
||
---
|
||
|
||
### M1.5 — "Indoor world feels right" — ✅ LANDED 2026-07-10 (building/cellar demo + full dungeon round-trip both user-gated; EXTENDED 2026-06-13 to include dungeon support / Phase G.3)
|
||
|
||
**✅ M1.5 LANDED 2026-07-10.** The indoor world reads as solid end-to-end.
|
||
A player can walk into a building and climb a multi-floor inn without
|
||
sling-out or wall-clip; descend a cottage cellar and ascend without falling
|
||
through; and — the piece that extended this milestone — **enter a dungeon
|
||
via portal, navigate its rooms, and portal back out to the outdoor world**,
|
||
with walls blocking, lighting reading correctly, and no position desync.
|
||
Both demo halves are user-gated: the building/cellar demo across the 2026-06
|
||
sessions, and the full dungeon round-trip (enter + navigate re-confirmed,
|
||
then the exit round-trip confirmed) on 2026-07-10. Dungeon support (full
|
||
Phase G.3) shipped with every sub-piece individually gated: teleport-in
|
||
(#133), collision (#137), portal-graph visibility (#95), doors (#187/#188),
|
||
lighting (#79/#93/#80). The indoor-physics workarounds (#90 stickiness +
|
||
`TryFindIndoorWalkablePlane` synthesis) were removed by A6.P4 — no duct-tape
|
||
left. **Explicit caveats carried forward (NOT M1.5 blockers — tracked as
|
||
their own follow-ups):** the far-town teleport-OUT arrival cascade
|
||
(#145-residual, REOPENED — a narrow far-teleport streaming edge case,
|
||
capture-harness-first) and #116 slide-response feel-debt. Next: **M2 —
|
||
"Kill a drudge."**
|
||
|
||
**EXTENDED 2026-06-13 — dungeons pulled into M1.5 scope; G.3 SHIPPED.** The
|
||
building/cellar demo (below) is DONE + user-gated. Attempting the dungeon
|
||
demo initially surfaced that dungeons didn't work AT ALL — terrain-less
|
||
dungeon landblocks appeared unsupported anywhere in the streaming/load/
|
||
render/physics pipeline (`LandblockLoader.Load` returning null, no terrain
|
||
mesh, teleport snap Resolving before hydration — issue #133). That premise
|
||
was refuted at design time (commit `90786c19`: the dungeon `LandBlock` is
|
||
flat-terrain and streams via the existing pipeline). The user chose the
|
||
FULL Phase G.3 scope (dungeon streaming + portal-space loading screen +
|
||
multi-landblock dungeon LOD + `PlayerTeleport` handling), and it has since
|
||
**SHIPPED**: teleport-into-dungeon (G.3a hold-until-hydrate FSM closes
|
||
#133), dungeon collision (#137 CLOSED), the #95 portal-graph visibility
|
||
blowup (RESOLVED), dungeon doors (#187/#188), and A7 dungeon lighting
|
||
(#79/#93/#80/#154, all CLOSED). M1.5's dungeon-demo scenario (enter via
|
||
portal, navigate 3-5 rooms, walls block, smooth transitions) is
|
||
functionally met; M1.5 now awaits ONE recorded end-to-end dungeon
|
||
round-trip user gate. The sole remaining live-code residual is the
|
||
far-town teleport-OUT arrival cascade (**#145-residual, REOPENED** — see
|
||
below).
|
||
|
||
**Building/cellar demo — DONE + user-gated.** The indoor world reads as
|
||
solid. Across the
|
||
2026-06 sessions the holistic retail-faithful render port (Option A: ONE
|
||
`DrawInside(viewer_cell)`, no inside/outside branch — BR-2..BR-7 / T1..T6)
|
||
shipped and was user-gated, and the indoor physics/membership family was
|
||
brought to retail fidelity (the A6.P4 per-cell shadow architecture; the
|
||
#107/#111/#112 spawn + membership fixes; the cellar-lip wedge). End-to-end,
|
||
user-gated this milestone: walk into a building and climb a multi-floor inn
|
||
without sling-out or wall-clip; descend a cottage cellar and ascend it
|
||
without falling through (the #98 + cellar-lip + #108 grass-window closes);
|
||
walls block everywhere (indoor + stab-shell, the #99 door run-through
|
||
closed); cell transitions are smooth (the doorway "flap" family killed —
|
||
#119/#128, #112, #113, #124, #129/#130/#131/#132, #108-residual, #127 all
|
||
closed with user gates). The #90-stickiness + `TryFindIndoorWalkablePlane`
|
||
synthesis workarounds were removed by A6.P4. Remaining feel-level debt is
|
||
tracked (#116 slide-response, partial Ghidra fix shipped). A7 indoor/
|
||
dungeon lighting fidelity CLOSED 2026-07-09 (#79/#93/#80/#154).
|
||
|
||
**Interleaved parity tracks (reconciled 2026-07-04 — NOT milestones).** Two
|
||
work streams have run alongside M1.5, both driven by the user's live
|
||
side-by-side-vs-retail sessions, both issue-level per the operating rules
|
||
(observed defects → ISSUES → fix → user gate), and both stay subordinate
|
||
to M1.5 rather than becoming milestones of their own: (1) **D.2b retail
|
||
UI** — inventory window / paperdoll / item interaction shipped and gated;
|
||
next container-switching (`claude-memory/project_d2b_retail_ui.md`);
|
||
(2) **the R5 movement-manager arc — DONE 2026-07-05** — the retail
|
||
MovementManager family port (TargetManager voyeur system V2,
|
||
StickyManager/PositionManager V3, V4 behavioral items, V5 MovementManager
|
||
facade — every entity now holds ONE `MovementManager` owning its
|
||
interp+moveto pair) that closed the #170 chase-slide and #171 pack-melee
|
||
gates (`docs/research/2026-07-03-r5-managers/r5-wiring-handoff.md` has the
|
||
arc close-out). Rationale:
|
||
rule 1's "one active milestone" governs where NEW scope goes — these
|
||
tracks fix observed retail-parity defects in already-shipped systems,
|
||
which the operating rules have always allowed; declaring them milestones
|
||
would manufacture scope where a tactical ledger (ISSUES + handoffs)
|
||
already carries them.
|
||
|
||
**Modern Pipeline side track (Track MP — user-commissioned 2026-07-05; NOT
|
||
a milestone, NOT part of M1.5).** A performance modernization arc (baked
|
||
asset pipeline → retail distance-degrade → ECS render world with delta
|
||
submission → zero-alloc frame loop) targeting smoothness first, then 300+
|
||
FPS in dense towns. Spec:
|
||
`docs/superpowers/specs/2026-07-05-modern-pipeline-design.md`; phase table
|
||
in the roadmap ("Track MP"). **Freeze exception (explicit, user-authorized
|
||
2026-07-05):** this track may reopen the otherwise-frozen streaming and
|
||
WB-rendering subsystems — but only under its own gated phases (pixel- and
|
||
feel-identical conformance gates, legacy path deleted at each gate), never
|
||
as ad-hoc rework. Rule 2's freeze still binds all NON-MP work. MP runs in
|
||
dedicated side-track sessions; the M1.5 critical path wins every conflict,
|
||
and MP4 is hard-queued behind #137.
|
||
|
||
The 2026-07-18 repeated-portal lifetime repair is a freeze-allowed corrective
|
||
change, not a resumption of Track MP. It keeps the current renderer and visual
|
||
ranges while balancing owners, bounding residency, and fence-delaying physical
|
||
GPU reuse; MP's pak/ECS/throughput redesign remains parked.
|
||
|
||
**Dungeon support (Phase G.3, issue #133) — SHIPPED.** The original
|
||
premise here (terrain-less dungeon landblocks unsupported anywhere in the
|
||
streaming/load/render/physics pipeline) was refuted at design time
|
||
(commit `90786c19`: the dungeon `LandBlock` is flat-terrain and streams
|
||
via the existing pipeline). Full G.3 scope (streaming + portal-space
|
||
loading screen + multi-landblock LOD + `PlayerTeleport` handling) has
|
||
shipped: teleport-into-dungeon (G.3a hold-until-hydrate FSM,
|
||
`7947d7a`/`2ce5e5c8`/`47ae237e`) closed #133; dungeon collision closed
|
||
#137 (`0efa7ed2`); the #95 portal-graph visibility blowup RESOLVED
|
||
(9.1M→~39k instances, `a40c38e8`); dungeon doors shipped (#187
|
||
`e2e285b8`, #188 `3284dd0a`); A7 dungeon lighting closed
|
||
(#79/#93/#80/#154, `44cced71`/`d275ed55`/`9ebb2060`). Remaining: one
|
||
recorded end-to-end dungeon round-trip user gate, plus the far-town
|
||
teleport-OUT arrival cascade residual (**#145-residual, REOPENED** —
|
||
capture-harness-first).
|
||
|
||
**2026-07-15 portal presentation completion (single-client visual gate passed
|
||
2026-07-16; non-blocking polish deferred).** The
|
||
temporary black tunnel cover is retired. F751 transit now renders retail's
|
||
client-enum-resolved DAT Setup `0x02000306` with animation `0x030005AC` at
|
||
40 frames/s in a dedicated CreatureMode-equivalent viewport, using retail's
|
||
camera, distant light, random eased forward-axis roll, frame-aligned exit
|
||
window, exact 100-sample view-plane curve, projection warp, and destination
|
||
viewer reset. Retail switches the portal and world viewports directly without
|
||
a black alpha fade; both compose below retained UI, so
|
||
chat, panels, toolbar, cursor, and input remain live throughout travel. See
|
||
`docs/research/2026-07-15-retail-portal-space-pseudocode.md`.
|
||
|
||
The live frame barrier now also matches `SmartBox::UseTime`: local/remote
|
||
object, projectile, animation, final-pose, animation-hook, particle, and
|
||
owner-script work completes before the inbound network queue is dispatched.
|
||
Local movement/jump output is committed on that same pre-network side, while a
|
||
non-advancing post-network pass reconciles children and attached effect/light
|
||
anchors after authoritative root moves. The accepted Hidden and UnHide paths
|
||
now also port `CPhysicsObj::set_hidden @ 0x00514C60` completely. Hidden invokes
|
||
the PartArray boundary after its effect, child visibility, collision stop, and
|
||
cell hide; UnHide invokes it after effect/child/collision restoration but before
|
||
the cell becomes visible. Both route through `CPartArray::HandleEnterWorld` →
|
||
`MotionTableManager::HandleEnterWorld`. That
|
||
strips every link animation and aborts pending motion completions before a
|
||
spell-cast recoil or recall remainder can survive portal space. This is the
|
||
generic retail visibility boundary, not a recall classifier, an arrival reset,
|
||
or a Hidden-time animation exception. Installed-DAT conformance covers both
|
||
the Lifestone recall boundary and a MagicPortal casting timeline. The user
|
||
confirmed `/ls` and spell-recall arrivals no longer replay their animation
|
||
tails; additional portal visual polish is explicitly deferred.
|
||
|
||
**2026-07-16 corrective portal gate (#218).** A side-by-side retail observer
|
||
check found three distinct state-publication defects after the arrival-tail
|
||
fix. Hidden now recomposes the post-`HandleEnterWorld` cyclic pose without
|
||
advancing animation time before its zero-time purple PES hooks run. Portal
|
||
space now waits for both completed static/EnvCell mesh uploads (including the
|
||
texture flush) and collision residency; dense
|
||
outdoor landblocks no longer abort at the obsolete 256-entry procedural
|
||
scenery id boundary (`0x8XXYYIII`, 4,096 entries). Finally, successful indoor
|
||
transitions commit the full resolved EnvCell/frame into the canonical outbound
|
||
Position, preventing stop-time snaps to the dungeon entrance on retail
|
||
observers. GPU-evicted meshes are re-staged from a bounded texture-complete CPU
|
||
cache instead of becoming false-ready or permanently absent. Portal readiness
|
||
also requires Near-tier ownership throughout the ring; stale worker completions
|
||
carry a hard-recenter generation and cannot overwrite a newer tier/window, and
|
||
a first Near completion demoted in flight is published as its equivalent
|
||
terrain-only Far payload rather than dropped. Upload completion is separate
|
||
from logical mesh ownership; landblock/static/EnvCell pins now balance across
|
||
unload and zero-owner late uploads remain evictable. A self-contained promotion
|
||
that supersedes a queued Far load publishes directly as a real Near landblock;
|
||
there is no provisional transaction waiting indefinitely for a base that the
|
||
streamer intentionally cancelled. Near-to-Far now has an explicit cross-layer
|
||
demotion transaction that preserves terrain while retiring EnvCells, indoor
|
||
physics/buildings, static collision, lights, translucency state, entities, and
|
||
render pins. Cross-landblock static shadow footprints are removed by logical
|
||
owner while dynamic registrations remain refloodable; duplicate Near
|
||
completions are publication-idempotent. Full unload shares the static-owner
|
||
teardown, and surviving adjacent owners remember withdrawn prefixes so their
|
||
cross-seam rows return when a landblock is promoted again.
|
||
Automated gates are green; the connected single-/two-client visual gate is
|
||
pending.
|
||
|
||
---
|
||
|
||
#### (historical M1.5 working notes below)
|
||
|
||
🔵 ACTIVE (resumed 2026-05-21 after Phase O ship)
|
||
|
||
**2026-05-30 — render-pipeline pivot.** The indoor *rendering* seam (seamless
|
||
in/out: the flap, missing/transparent walls, terrain bleed) will be solved by a
|
||
**single unified retail-faithful render pipeline (Phase U)**, replacing the
|
||
abandoned two-pipe inside/outside split (A8/A8.F, issue #103). The two-pipe split
|
||
is a WorldBuilder inheritance; retail uses one portal-visibility pass and is
|
||
seamless by construction. Decision + scope:
|
||
[`docs/research/2026-05-30-unified-render-pipeline-decision-and-handoff.md`](../research/2026-05-30-unified-render-pipeline-decision-and-handoff.md).
|
||
Camera-collision + a physics viewer-cap fix shipped 2026-05-30 and are kept (they
|
||
were a detour from the real seam fix, but retail-faithful and worth keeping). A6
|
||
(physics) and A7 (lighting) are unaffected.
|
||
|
||
**Phase O — DatPath Unification — shipped 2026-05-21.** ONE thing
|
||
touches the DATs. ~33 WB files (~7.7K LOC) extracted into
|
||
`src/AcDream.{Core,App}/Rendering/Wb/`; project references to
|
||
`WorldBuilder.Shared` + `Chorizite.OpenGLSDLBackend` dropped;
|
||
`DefaultDatReaderWriter` eliminated. Visual side-by-side passed at
|
||
Holtburg town, inn interior, and dungeon. Phase O pre-empted M1.5
|
||
per user direction 2026-05-21; M1.5 now resumes from the 2026-05-20
|
||
baseline with no code changes lost — Phase O did not touch dat-loading
|
||
infrastructure for physics or collision, only the rendering pipeline.
|
||
M1.5's planned phases (A6 + A7) are unaffected.
|
||
|
||
**Demo scenario (updated 2026-05-21):** The original demo target was
|
||
"enter the Holtburg Sewer dungeon" but that location doesn't exist on
|
||
this ACE server (discovered during A6.P1 capture session). Additionally,
|
||
A6.P1 surfaced **issue #95** (portal-graph visibility blowup) which makes
|
||
ANY dungeon visually unusable on entry. **[SUPERSEDED — #95 RESOLVED
|
||
(9.1M→~39k instances, `a40c38e8`); dungeons ship; see M1.5 section
|
||
above.]** Demo scenario revised in two parts:
|
||
|
||
**Building/cellar demo (achievable after A6.P3 lands):**
|
||
Walk into the Holtburg inn, climb to the 2nd floor, walk around without
|
||
sling-out or wall-clip. Enter a cottage cellar, descend without falling
|
||
through. Throughout:
|
||
- Walls block — no walk-through anywhere, indoor or stab-shell.
|
||
- Stairs work — ascend + descend without falling through or stuck-in-falling.
|
||
- Items block — furniture, decorations.
|
||
- Cell transitions are smooth — no CellId ping-pong, no flicker.
|
||
- Lighting reads correctly — torchlit rooms are bright, no spotlight
|
||
artifacts, static decorations participate in env lighting.
|
||
|
||
**Dungeon demo (blocked on issue #95 fix; promote to post-M1.5 if
|
||
the visibility bug isn't addressed in M1.5 scope):** **[SUPERSEDED — #95
|
||
RESOLVED, dungeons ship; see M1.5 section above.]**
|
||
Enter any dungeon via portal (substitute for "Holtburg Sewer"). Navigate
|
||
~3-5 rooms without rendering corruption (no see-through-walls, no
|
||
other-dungeons-rendered-inside). Walls block, stairs work, items block,
|
||
lighting correct, cell transitions smooth.
|
||
|
||
**Why this is its own milestone:** M1 landed walkable + clickable as a
|
||
specification (the doorways open, NPCs select, items pick up — all visible
|
||
in the demo target). But continued indoor testing surfaced a deep family of
|
||
physics + lighting bugs (BSP push-back distance probably diverges from
|
||
retail, per-frame ContactPlane synthesis is a known unfaithful stop-gap,
|
||
indoor lighting + item-spotlight bugs reported during 2026-05-21 sessions).
|
||
Three workarounds shipped today (#89 sphere-overlap CheckBuildingTransit,
|
||
#90 sphere-overlap stickiness, #92 spawn-cell-id seed) closed the visible
|
||
symptom at Holtburg inn, but #90 specifically is a CLAUDE.md-rules
|
||
workaround (explicit retail divergence) that needs a proper root-cause fix.
|
||
The umbrella indoor-physics issue (#83) has been open since 2026-05-19 with
|
||
multiple aborted fix attempts. Promoting this to milestone scope forces the
|
||
fix to be central, retail-anchored, and complete — not another whack-a-mole
|
||
patch.
|
||
|
||
**Phases included:**
|
||
|
||
| Phase | What it does |
|
||
|---|---|
|
||
| A6 — Indoor physics fidelity (cdb-driven) | Capture retail's per-tick BSP collision response state at 9 scenarios (4 buildings + 5 dungeon sites). Analyze the gap vs ours. Fix BSP correction paths. Remove #90 stickiness + `TryFindIndoorWalkablePlane` synthesis workarounds. |
|
||
| A7 — Indoor lighting fidelity (RenderDoc + retail-decomp) | Capture per-cell light state + per-pixel attribution at the same 9 scenarios. Analyze cell-light association, visibility culling, per-entity light direction. Fix indoor lighting + #80 (upper-floor dark) + #81 (static-stab atmospheric) + the held-item-spotlight bug. |
|
||
|
||
**Issues in scope (M1.5):**
|
||
|
||
- **#80** — Camera on 2nd floor goes very dark
|
||
- **#81** — Static building stabs don't react to atmospheric lighting
|
||
- **#83** — Indoor multi-Z walking broken (cellars, 2nd floors, intermittent falling-stuck)
|
||
- **#88** — Indoor static objects vibrate (suspected sub-step state corruption — A6.P2 maps to Finding 2 family)
|
||
- **#90** — CellId ping-pong (workaround in place; remove during A6.P4)
|
||
- **#95** — Portal-graph visibility blowup (filed 2026-05-21; **blocks the dungeon half of the M1.5 demo** but is NOT in A6 scope; either add a dedicated phase inside M1.5 to fix it OR promote the dungeon demo to post-M1.5) **[SUPERSEDED — RESOLVED, 9.1M→~39k instances, `a40c38e8`; dungeons ship; see M1.5 section above.]**
|
||
- **L-indoor** — Lighting indoors broken (file as new # during M1.5 kickoff)
|
||
- **L-spotlight** — Items projecting spotlight on walls (file as new # during M1.5 kickoff)
|
||
- **Stairs walk-through** — captured + characterized by A6.P2 (Finding 2 family); fix in A6.P3
|
||
- **2nd-floor walking** — captured + characterized by A6.P2 (Finding 2 — scen3 shows infinite CP-write ratio on flat 2nd-floor walk); fix in A6.P3
|
||
- **Cellar descent** — same physics family as stairs; fix in A6.P3
|
||
- **Indoor sling-out** (new symptom from A6.P1 scen4) — captured + mapped to A6.P2 Finding 3 (cell-resolver in ResolveCellId / CheckBuildingTransit); fix in A6.P3
|
||
- **`TryFindIndoorWalkablePlane`** — synthesis workaround removal (Bug A's original goal, finally unblocked; A6.P4)
|
||
|
||
**Frozen phases during M1.5:** all M0 + M1 phases stay frozen. Plus
|
||
specifically the recently-shipped A4 + #89 + #91 + #92 — those
|
||
land in main as the M1.5 baseline and shouldn't be revisited except as part
|
||
of A6.P4 removal of the workarounds.
|
||
|
||
**Frozen as of M1.5 landing (2026-07-10):** all M1.5 phases are now frozen
|
||
per rule 2 — the indoor render port (Option A `DrawInside(viewer_cell)`),
|
||
the A6 physics/membership family + A6.P4 per-cell shadow architecture, A7
|
||
indoor/dungeon lighting, and **full Phase G.3 dungeon support** (streaming
|
||
collapse/expand, teleport in/out, dungeon collision/lighting/doors). No
|
||
rework until M7 polish. Exempt from the freeze as still-open bugs (rule 2's
|
||
"actively broken" clause): #145-residual (far-town teleport-OUT cascade) and
|
||
#116 (slide-response) — these are tracked follow-ups, not frozen work.
|
||
|
||
**Estimated timeline:** 3–5 weeks calendar (17–26 days focused work). Bigger
|
||
than a normal milestone because lighting is open-ended (less existing
|
||
diagnostic infrastructure than physics). Could be shorter if the cdb
|
||
analysis surfaces a single-fix opportunity.
|
||
|
||
**What "M1.5 lands" looks like:** the indoor world reads as solid. Players
|
||
can navigate buildings, basements, multi-floor inns, and dungeons without
|
||
encountering walls they walk through, lighting that looks wrong, or
|
||
position glitches. The two known workarounds (#90 stickiness +
|
||
TryFindIndoorWalkablePlane synthesis) are removed; the codebase no longer
|
||
has indoor-physics "duct-tape." Dungeons are usable enough to support M2's
|
||
"kill a drudge" demo target (drudges live in dungeons; this milestone
|
||
unblocks that).
|
||
|
||
---
|
||
|
||
### M2 — "Kill a drudge" — ✅ LANDED 2026-07-15
|
||
|
||
**Demo scenario:** Equip a sword. Walk to a drudge. Swing. See "You hit
|
||
Drudge for 12 slashing damage (87%)" in chat. Watch the swing animation
|
||
play. Drudge dies, drops loot. Pick up the loot. Open the inventory panel
|
||
and see it.
|
||
|
||
**Landed:** The connected Release client now completes that entire loop. Both
|
||
melee and missile weapons switch through server-authoritative inventory
|
||
transactions; attacks face the selected hostile target before release; combat
|
||
animation, damage text, persistent death, auto-target, loot, inventory/stack
|
||
operations, paperdoll/toolbar interaction, and world-item giving have passed
|
||
user gates. Work discovered outside this demo remains explicitly carried below
|
||
rather than keeping the milestone open indefinitely.
|
||
|
||
**First port target when M2 starts (per the M2 combat-math research memo,
|
||
`docs/research/2026-06-04-combat-math-deep-dive.md`):**
|
||
`CombatMath.ComputeDamage` — damage-calc + armor-resists are port-ready
|
||
(ACE is the high-confidence oracle; two known scaffold bugs in
|
||
`CombatModel.cs` identified — additive attributeBonus + subtractive armor).
|
||
Hit-roll is well-documented client-side; the server sigmoid/crit +
|
||
weapon-timing (the x87 `GetPowerBarLevel` artifact) come after. NOTE: M2
|
||
was briefly started 2026-06-13 then re-deferred when M1.5 was extended to
|
||
include dungeons.
|
||
|
||
**Phases to ship:**
|
||
- **F.2 (panels)** — Inventory panel reading `ItemRepository` (data already
|
||
shipped in F.2 base; M2 ships the visual surface).
|
||
- **F.3** — Combat math + damage flow.
|
||
- **F.5a** — Visible-at-login dev panels (Attributes, Skills, Equipped,
|
||
Inventory list) — minimal ImGui surfaces, retail-skin deferred to M5.
|
||
- **L.1c** — Combat animation wiring (draw/sheath, attack swings by
|
||
stance/power/height, hit reactions, evades).
|
||
- **L.1c held-object foundation (implemented and live-gated 2026-07-11)** — exact
|
||
equipment-aware default combat mode (bow→Missile, caster→Magic) plus retail
|
||
CreateObject/ParentEvent child attachment and per-frame hand-follow rendering.
|
||
- **L.1c inventory weapon switching (implemented and live-gated 2026-07-15)** —
|
||
inventory double-click ports retail's two-stage `AutoWield`: return the occupied
|
||
primary weapon to the main pack, then any incompatible shield/ammo, waiting
|
||
for every server move confirmation before wielding the requested
|
||
bow/sword/caster/two-hander. The shared transaction works
|
||
in peace and war, remains busy through the exact final `WieldObject`
|
||
acknowledgement independently of rollback bookkeeping, and prevents rapid
|
||
switches from overlapping or remaining stuck; ACE owns stance-specific motions and its PropertyInt 40 update
|
||
now drives the client `CombatState`.
|
||
- **M2 item giving (implemented and starter-dungeon live-gated 2026-07-13)** —
|
||
a physical inventory drag released over a 3-D creature now ports
|
||
`ItemHolder::AttemptPlaceIn3D` through the exact `GiveObjectRequest 0x00CD`
|
||
packet, including selected partial-stack quantities and authoritative-only
|
||
inventory mutation. Release coordinates feed the existing world picker, and
|
||
`PlayerDescription.Options1` preserves retail's player secure-trade
|
||
preference. See
|
||
`docs/research/2026-07-13-retail-give-item-pseudocode.md` and issue #216.
|
||
- **M2/M3 missile, effect, and portal presentation campaign (automated Steps 0–9
|
||
complete and independently reviewed 2026-07-14)** — named-retail projectile and
|
||
physics-script behavior is pinned in one oracle, the complete `PhysicsDesc`
|
||
and F754/F755 wire surfaces are parsed with retail timestamp gates, and
|
||
`LiveEntityRuntime` now separates logical lifetime from spatial rebucketing.
|
||
Landblock churn and equipment attachment preserve one stable local entity and
|
||
cannot replay create-time scripts. A narrow raw-hook seam now decodes retail's
|
||
inherited `CreateBlockingParticle` payload for both PhysicsScripts and
|
||
Animations, while exact stored-order `PhysicsScriptTable` threshold lookup and
|
||
Setup-versus-network default precedence are pinned by conformance tests.
|
||
`EntityEffectController` now replays pre-create F754/F755 once in mixed order
|
||
against the canonical local owner; the Core scheduler uses one serial FIFO per
|
||
owner, preserves duplicates, supports deterministic delayed `CallPES`, and
|
||
routes default/part-default hooks through the complete hook fan-out. The
|
||
update-frame clock is published before packet/default dispatch and republished
|
||
before animation hooks. Existing cell-less/Frozen queues pause and resume with
|
||
retail eligibility, while new ordinary F754/F755/default plays on an
|
||
already-created cell-less owner drop. Hidden/UnHide bypasses that ordinary
|
||
gate through retail `set_hidden` -> `play_script_internal`, queuing until
|
||
eligibility returns. Attached children advance through their parent. Every sink shares canonical
|
||
`WorldEntity.Id`, and static allocator overflow fails before aliasing another
|
||
landblock. Live SoundTable ownership is installed independently of animation
|
||
eligibility and replaced/cleared on each PhysicsDesc application; DAT statics
|
||
retain Setup defaults. Concurrent-safe DAT reads stay off the streaming lock; malformed
|
||
zero-time recursive chains fail diagnostically while valid timed weather loops
|
||
continue. The
|
||
live-pose step now publishes exact root/indexed rigid-part transforms after
|
||
animation and equipped-child composition, advances existing FP hooks, drains
|
||
animation hooks, refreshes attached emitters/lights, advances particles, then
|
||
ticks PhysicsScripts in fixed retail frame order. World-released particles remain at their birth positions while
|
||
parent-local particles follow; normal/blocking/anonymous logical emitter
|
||
semantics are exact; pending projections skip particle updates/draw while
|
||
retaining absolute creation times, the same emitter, and its logical ID, so
|
||
re-entry expires elapsed state once without backlog emission. Moving Setup lights follow top-level and held-object
|
||
roots and honor authoritative Lighting/SetLight transitions; missing emitter
|
||
assets diagnose without a fabricated fallback. Stable Setup-part indices do
|
||
not collapse around missing visuals, nested attachments update parent-first,
|
||
and per-child pose buffers are reused. Rigid attachment/effect frames exclude
|
||
visual Setup scale, attachment withdrawal cascades through descendants, and
|
||
later pose publication recovers the complete descendant chain parent-first;
|
||
light registration follows final projection visibility. Step 6 adds the pure
|
||
Core retail projectile driver: 0.2-second catch-up quanta, the 50-unit/second
|
||
clamp, final-state gravity, world-space omega, full-3D AlignPath, scaled
|
||
Setup-local sphere sweeps, exact missile target/ethereal exclusions,
|
||
self-shadow rejection, retail collision response, and full-cell landblock
|
||
rebasing. Missile adds PathClipped but never PerfectClip. AP-67, TS-2, TS-10,
|
||
TS-11, and TS-12 are retired; AP-83/AP-91 remain dormant. Step 7 attaches
|
||
that primitive to the canonical `LiveEntityRecord`: arrows, bolts, and spell
|
||
missiles share one body and one renderer identity across prediction,
|
||
authoritative State/Vector/Position/Movement corrections, full-cell
|
||
rebucketing, pending-landblock residence, delete, session reset, and GUID
|
||
reuse. MovementManager presentation shares the same body and the canonical
|
||
live-record cell regardless of which component was created first;
|
||
pending/leave-world shadows retain their registration for exact re-entry even
|
||
if Missile classification changes while pending; static missile effect
|
||
roots follow every predicted frame, and materialized rehydration never uses a
|
||
stale spawn cell. No impact, damage, effect, or delete event is synthesized
|
||
client-side. Step 8 ports retail's constructor-state → PhysicsDesc
|
||
transition, ordered Lighting/NoDraw/Hidden state side effects, DAT-driven
|
||
typed Hidden/UnHide effects, direct-child NoDraw propagation, retained
|
||
particles/lights, mesh/collision/interaction suppression, and exact remote
|
||
`teleport_hook` teardown followed by a placement snap before the airborne
|
||
gate. Ordinary visible CreateObject does not fabricate UnHide; pending
|
||
F754/F755 packets replay only after construction-state effects. TS-43 is
|
||
retired. Step 9 adds a deterministic 96-owner whole-lifecycle gate spanning
|
||
projectile ticks, repeated DAT-effect plays, loaded↔pending landblock churn,
|
||
dynamic lights, particle bindings, collision shadows, rapid deletion,
|
||
same-GUID generation reuse, unknown pre-create effect packets, and session
|
||
reset. A separate 96-owner renderer-seam gate balances every
|
||
`EntitySpawnAdapter` mesh reference. Teardown now proves zero retained live
|
||
records/bodies/projectiles, render owners, spatial buckets/rescues,
|
||
PhysicsScript queues/anchors, effect profiles/packets, emitters/logical IDs,
|
||
poses, lights, and active/suspended shadow registrations. The hardening pass
|
||
also fixed undrained persistent-rescue retention and rejects queued spatial
|
||
visibility edges that no longer match current incarnation truth. A companion
|
||
twelve-cycle gate drives exact recall motion `0x10000153` through CallPES,
|
||
Hidden, unloaded-destination placement, hydration, and UnHide. Logical
|
||
teardown is incarnation-specific across callback-driven GUID reuse; per-GUID
|
||
epochs reject create resurrection after a nested delete/reset, resource
|
||
registration is atomic, terminal session clear rejects registrations outside
|
||
its teardown snapshot, and visibility fan-out drains all owners before
|
||
aggregating observer failures. Canonical cell/projection state finishes
|
||
committing before those aggregates rethrow; per-GUID epoch tombstones survive
|
||
accepted delete until session clear, and superseded creates cannot discard a
|
||
prior-incarnation cleanup failure. Per-record projection tokens also prevent
|
||
an outer rebucket/withdrawal from overwriting a replacement incarnation or a
|
||
newer same-record projection created synchronously by an observer.
|
||
TS-26's
|
||
`UpdatePosition` freshness gap is retired by the nine-channel timestamp
|
||
gates; TS-49 remains the explicit
|
||
DetectionManager/`LeftDetection` gap. A 2026-07-14 corrective visual slice
|
||
ports `CPhysicsPart::Always2D`: mode-1/no-degrade particles now keep every
|
||
authored mesh polygon through the modern global buffer, while other degrade
|
||
modes retain the billboard path. This restores the four-piece apex of
|
||
Armor/protection shield effects. MP2's 2026-07-17 corrective performance
|
||
slice now ports the complete retail particle distance/cell-view gate and
|
||
degraded update branches: DAT-authored ranges (Aerlinthe's dominant swarm is
|
||
64 world units), exact infinite-emitter freeze/finite-emitter conditional
|
||
stop bookkeeping, and emitter-first render rejection. The landscape renderer
|
||
publishes outdoor landcells independently of surviving object meshes.
|
||
Terrain/entity streaming distance is
|
||
unchanged; the user-requested default Extended setting doubles only particle
|
||
range, while Retail remains available as the exact cutoff. The
|
||
final two-client visual gate is still pending.
|
||
- **L.1c local attack receive path (implemented and live-gated 2026-07-15)** —
|
||
local non-autonomous mt-0 UpdateMotion now uses retail's wholesale interpreted
|
||
funnel and action-stamp gate, so ACE's server-selected melee/missile action
|
||
drives the player's motion table; direct command-list replay is removed.
|
||
- **M2 basic combat bar (implemented and live-gated 2026-07-15)** — authored
|
||
`gmCombatUI` LayoutDesc `0x21000073` now pops for Melee/Missile and shares one
|
||
press/hold/release attack-request owner across DAT buttons and keybindings.
|
||
Retail x86 resolves full charge to 1.0 s (0.8 s dual wield), also retiring the
|
||
guessed jump-charge timing. The corrective gate ports the right-to-left red
|
||
meter, silent AttackDone control status, target-frame Keep in View with
|
||
retained manual orbit, persistent corpse motion protected from the AP-80
|
||
velocity adaptation, and post-death nearest-target acquisition when Auto
|
||
Target is enabled. Both multi-frame and static/reactive spawns now share
|
||
retail's wire-driven description-before-enter-world lifecycle: the corpse's
|
||
authoritative Dead state is retained and `HandleEnterWorld` strips its
|
||
Ready→Dead transition before the first rendered tick. Corpse persistence was
|
||
visually confirmed live on 2026-07-12. The follow-up #205 correction makes
|
||
the toolbar consume the final reentrant Auto Target selection and restricts
|
||
automatic candidates to hostile non-player monsters; live gate passed
|
||
2026-07-12. See
|
||
`docs/research/2026-07-12-death-and-auto-target-pseudocode.md`.
|
||
- **Retail client command families (implemented 2026-07-13; corrective live gate pending)** —
|
||
both chat surfaces now route a named-decomp command catalog into an App-layer
|
||
controller, keeping ACE administrator verbs on the explicit server-command
|
||
path. The implemented set covers travel, character/time queries, local UI and
|
||
location commands, named/automatic layout persistence, AFK/consent, emotes,
|
||
friends, squelch/filter, and fill-components, with exact Core.Net messages and
|
||
authoritative social-state parsing. See
|
||
`docs/research/2026-07-13-retail-client-command-families-pseudocode.md`.
|
||
The first connected gate passed the family except for raw suicide success
|
||
code `0x004A` and the incomplete title-bar-only FPS toggle; the corrective
|
||
pass maps retail's success text and mounts the authored SmartBox FPS element.
|
||
Confirmation reuse now follows retail's `DialogFactory`: fresh DAT roots,
|
||
property-backed results, context ids, independent queues/priority, callbacks,
|
||
close notices, and server abort handling; `/die`, gameplay requests, and
|
||
guarded item uses share the type-1 presenter.
|
||
- **L.1b** — Command router + motion-state cleanup (prereq for L.1c).
|
||
|
||
**Freeze on landing:**
|
||
- Combat math zone
|
||
- Inventory zone (data + dev panel; retail-skin reopens in M5)
|
||
- L.1b/c combat-animation zone
|
||
|
||
**What "M2 lands" looks like:** the gameplay loop is real. You can fight,
|
||
take damage, kill things, see loot, manage your inventory. The game becomes
|
||
a game.
|
||
|
||
---
|
||
|
||
### M3 — "Cast a spell" — 🔵 ACTIVE (started 2026-07-15)
|
||
|
||
**Demo scenario:** Cast Flame Bolt at a drudge. Watch the cast animation,
|
||
the projectile, the impact. Self-cast a buff (Strength Self). See the
|
||
enchantment in a buff list. Recall to lifestone — full recall animation,
|
||
correct teleport, correct re-spawn.
|
||
|
||
**Starting position:** E.5 already supplies the cast wire and authoritative
|
||
spell/enchantment state, while the M2/M3 campaign supplies traveling physical
|
||
and spell missiles, DAT PhysicsScripts, live part-attached particles/lights,
|
||
Hidden/UnHide, and recall motion/effects. Representative projectiles and
|
||
protection effects pass the single-client visual gate. M3 begins with a focused
|
||
named-retail gap audit of F.4/L.1d—not a rewrite of this foundation—then fills
|
||
the spellbook/active-enchantment UI and completes the two-client portal gate.
|
||
|
||
**Implementation update (2026-07-15):** The automated F.4/L.1d audit and port
|
||
are complete. Cast requests now use retail component-formula selection,
|
||
target compatibility, shared busy/completion ownership, and exact
|
||
targeted/untargeted messages. Live enchantment add/remove/dispel/purge state is
|
||
fully parsed with layer identity and client-clock normalization. The authored
|
||
Magic combat page, eight favorite tabs, caster endowment, spellbook/component
|
||
book, scoped keys, positive/negative effects windows, and the authored
|
||
seven-control gameplay indicator strip are mounted in the retained runtime.
|
||
The strip now uses retail DAT states for link quality, Helpful/Harmful effects,
|
||
Vitae, burden, mini-game availability, and end-character-session; live link
|
||
age, player load, and enchantment registries drive their retail thresholds and
|
||
the burden/effect/logout actions use the shared panel/dialog owners. Main-panel
|
||
visibility now flows through retail's one-active `gmPanelUI` owner. M3 remains
|
||
active only for the connected single-client magic
|
||
UI/casting visual gate and the final two-client portal observer gate.
|
||
The 2026-07-17 indicator follow-up also mounts authored Character Information,
|
||
Link Status, Vitae, and Mini Game detail roots as children of that same
|
||
main-panel owner. Helpful, Harmful, Character Information, Link, and Vitae
|
||
therefore replace Inventory/Character/Magic in one canonical window rectangle.
|
||
Helpful/Harmful instantiate retail's DAT icon/name/time row and selected-spell
|
||
information presentation. The burden icon sends panel 3 (Character Information),
|
||
not panel 11 (Attributes/Skills). Link uses retail's payload-free ping/RTT
|
||
cadence; Vitae uses PropertyInt 129/139 and the retail XP-pool threshold formula.
|
||
The detail conformance pass also restores Vitae's omitted penalty explanation,
|
||
ports the exact Character Information report/property meanings, prevents
|
||
double-compositing authored translucent panel centers, and initializes the
|
||
end-session control in its visible Normal DAT state before hover.
|
||
Helpful/Harmful selected descriptions now use retail text reflow and the
|
||
authored lower information scrollbar independently of the spell-list
|
||
scrollbar; Vitae recovery XP uses grouped client-language integer formatting.
|
||
The first connected gate exposed and corrected two retained-import defects:
|
||
spellbook tabs are authored stateful text controls (not buttons), and retail
|
||
text starts with zero margins. Real-DAT fixtures now pin spellbook binding,
|
||
Closed/Open child-state propagation, and Magic-tab strings I through VIII.
|
||
The follow-up spellbook slice now clones the DAT-authored one-column
|
||
spell-shortcut presentation (icon, name, separator, selected overlay), binds
|
||
its authored scrollbar, ports exact school/level filter masks and stable
|
||
display-order sorting, carries learned spells into the open favorite bar, and
|
||
routes Delete through the shared retail confirmation factory plus
|
||
server-authoritative `CM_Magic::Event_RemoveSpell (0x01A8)` response path.
|
||
The component-book follow-up now instantiates retail's category and component
|
||
listbox templates from LayoutDesc `0x21000033`, preserving localized headings,
|
||
row/selection textures, icon/name/count columns, and desired-value editing.
|
||
The spell metadata source is now the complete installed end-of-retail
|
||
SpellTable DID `0x0E00000E` (6,266 records). The former 3,956-row CSV is kept
|
||
only as a research fixture, so every spell ACE can teach resolves through the
|
||
same DAT-backed catalog used by formulas, casting, spellbook rows, and effects.
|
||
|
||
**Phases to ship:**
|
||
- **F.4** — Spell cast state machine (buffs + recalls first, projectile
|
||
spells second).
|
||
- **L.1d** — Spell-casting animation wiring (cast command classification,
|
||
windup, release, fizzle/interruption, recoil).
|
||
- **F.5 (Spellbook panel)** — dev-skin surface for learned spells + active
|
||
enchantments.
|
||
|
||
**Freeze on landing:**
|
||
- F.4 spell zone
|
||
- L.1d cast-animation zone
|
||
- Spellbook dev-panel surface
|
||
|
||
**What "M3 lands" looks like:** mages are real characters. Buffs work,
|
||
recalls work, the first projectile spells work. Combat from M2 + casting
|
||
from M3 = retail-equivalent gameplay loop for melee and casters.
|
||
|
||
---
|
||
|
||
### M4 — "Live in the world" — 🔵 (~6–10 weeks)
|
||
|
||
**Demo scenario:** Create a fresh character from scratch (no ACE admin).
|
||
Spawn. Talk to an NPC. Accept a quest. Walk to a dungeon entrance. Portal
|
||
in (pink-bubble loading). Walk through the dungeon. Complete the quest.
|
||
Walk back out.
|
||
|
||
**Phases to ship:**
|
||
- **H.3** — Emote scripts + quests + dialogs (122 EmoteType × 39 Trigger
|
||
mini-VM).
|
||
- **G.3** — Dungeon streaming + portal space + `PlayerTeleport` handling.
|
||
(Unblocked by L.2e from M1.)
|
||
- **H.4** — Character creation (`0xE000002 CharGen` + heritages + appearance
|
||
picker + preview).
|
||
- **L.1e** — Emote + posture animation wiring.
|
||
|
||
**Freeze on landing:**
|
||
- H.3 dialog/quest zone
|
||
- G.3 dungeon zone
|
||
- H.4 character-creation zone
|
||
- L.1e emote-animation zone
|
||
|
||
**What "M4 lands" looks like:** the world feels populated and interactive.
|
||
You can do quests, enter dungeons, create characters. Combined with M2/M3,
|
||
the client is **functionally playable** — minus visual polish.
|
||
|
||
---
|
||
|
||
### M5 — "Looks like retail" — 🟢 PARALLELIZABLE WITH M3/M4 (~4–8 weeks)
|
||
|
||
**Demo scenario:** Side-by-side screenshot of acdream vs retail at the same
|
||
location, same time of day, same character. Hard to tell apart at a glance.
|
||
Open the inventory panel — retail-skinned with the right font, icons, and
|
||
9-slice panel borders.
|
||
|
||
**Phases to ship:**
|
||
- **C.1.5c** — Sky-PES dispatch chain (closes #2 lightning, #28 aurora,
|
||
#29 cloud thinness).
|
||
- **C.2** — Dynamic point lights (fireplaces, lamps, torches with proper
|
||
local lighting).
|
||
- **C.3** — Palette range tuning (skin/hair/eye colors match retail).
|
||
- **C.4** — Double-sided translucent polys.
|
||
- **D.2b** — Custom retail-look UI backend. **In progress:** vitals + chat + toolbar + inventory (window manager, B-Grid, B-Controller, B-Wire, drag-drop, container-switching) + paperdoll (equip slots, 3-D doll UiViewport, Slots toggle) + **Character window** (Attributes tab, visually confirmed 2026-06-26) + **UI Studio** (standalone panel previewer + 26-window retail dump + interactive click-routing + headless screenshot) + **importer dat-fidelity** (Fix A/B/C: justification, FontColor, per-element FontDid resolver [studio-only; #157 tracks live-game path]) are shipped. Current campaign: shared retained-widget/window architecture, then selected-object mana/stack completion. **Retail correction:** the main toolbar is object-only; the eight favorite-spell lists drive separate spell bars. Execution plan: `docs/superpowers/plans/2026-07-10-retail-ui-fidelity-completion.md`.
|
||
- **D.3–D.7** — AcFont + dat sprites + core panels reskinned + HUD +
|
||
cursor manager. **D.6 radar/compass shipped 2026-07-10 and the separate retail
|
||
jump power bar was implemented 2026-07-13; D.7 complete retail
|
||
cursor catalog/precedence shipped and user-confirmed 2026-07-11.** The radar uses the retained
|
||
UI architecture using the retail DAT layout and recovered runtime behavior;
|
||
the jump bar uses the same retained composition with movement-owned charge timing.
|
||
Live-game visual confirmation remains the user gate.
|
||
- **L.1f** — NPC/monster + item-use animation coverage.
|
||
|
||
**Freeze on landing:**
|
||
- Visual polish zone (C.1.5c, C.2, C.3, C.4)
|
||
- D.2b → D.7 UI-skin zone
|
||
- L.1f NPC-anim zone
|
||
|
||
**What "M5 lands" looks like:** the client visually convinces. Screenshots
|
||
become postable. The "old / broken vs retail" feeling that drives most of
|
||
the chronic ISSUES.md entries is gone.
|
||
|
||
---
|
||
|
||
### M6 — "Plugins ship" — 🟢 (~4 weeks)
|
||
|
||
**Demo scenario:** A third party (not you) writes a small plugin against
|
||
the published API — XP tracker, loot logger, simple chat filter — and it
|
||
loads cleanly. Sample plugin lives in the repo with documented build steps.
|
||
|
||
**Phases to ship:**
|
||
- Plugin API surface: stable contract over `AcDream.Core` + `AcDream.UI.Abstractions`,
|
||
versioned, with the world-state interfaces exposed.
|
||
- Plugin host: load isolation, lifecycle, error containment.
|
||
- Sample plugin (XP tracker or loot logger) — proves the API by using it.
|
||
- Plugin docs page.
|
||
|
||
**Freeze on landing:**
|
||
- Plugin API v1 surface (additive changes only post-freeze).
|
||
|
||
**What "M6 lands" looks like:** the differentiator vs the retail client is
|
||
real. acdream offers something retail never did, and the API is documented
|
||
well enough that other people can build on it.
|
||
|
||
---
|
||
|
||
### M7 — "v1.0" — 🟢 (open-ended polish)
|
||
|
||
**Demo scenario:** Long-running stress test: log in, play for 4 hours
|
||
across outdoor + dungeon + portal + combat + spell + chat scenarios,
|
||
reconnect once mid-session, log out clean. No crashes, no protocol errors,
|
||
no visual regressions, no audio dropouts.
|
||
|
||
**Phases to ship:**
|
||
- **Phase M** — Network stack conformance (retransmit, ACK piggybacking,
|
||
echo/keepalive, fragment splitting, typed actions). Deferred until now
|
||
because ACE handles loss gracefully — but v1.0 needs proper network
|
||
hardening.
|
||
- **H.2** — Allegiance.
|
||
- **N.6 slice 2 + N.7–N.10** — Finish WB rendering migration (EnvCells,
|
||
sky/particles via WB, visibility manager, GL infrastructure
|
||
consolidation).
|
||
- **Phase J long-tail** — Player rig polish, group/fellowship UI, trade
|
||
window, salvage/tinker UI, house ownership, society UI, dev-mode tools.
|
||
- **L.1g** — Animation polish + conformance.
|
||
- Final visual + audio polish pass against ISSUES.md chronic backlog.
|
||
|
||
**What "M7 lands" looks like:** v1.0. Ship.
|
||
|
||
---
|
||
|
||
## Estimated timeline
|
||
|
||
| Milestone | Effort | Cumulative |
|
||
|---|---|---|
|
||
| M0 | DONE | DONE |
|
||
| M1 | ~4–6 wk | ~5 wk |
|
||
| M2 | ~6–10 wk | ~13 wk |
|
||
| M3 | ~3–4 wk | ~17 wk |
|
||
| M4 | ~6–10 wk | ~25 wk |
|
||
| M5 | ~4–8 wk (parallel) | overlaps M3/M4 |
|
||
| M6 | ~4 wk | ~29 wk |
|
||
| M7 | open-ended | v1.0 |
|
||
|
||
**Roughly 9–12 months of focused solo work from 2026-05-12 to v1.0.** That's
|
||
honest for an open-source project of this scale. The biggest single rock is
|
||
M2 (combat math + animations + inventory panels lining up); M5 can be
|
||
chipped at in parallel by subagents while you drive M3/M4.
|
||
|
||
---
|
||
|
||
## What this document is **not**
|
||
|
||
- **Not a release schedule.** Internal morale + scope layer only. If acdream
|
||
goes public-alpha at some point, that's a separate decision built on top
|
||
of one of these milestones.
|
||
- **Not immutable.** When reality and the milestones diverge, update the
|
||
milestones in the same session you discover the divergence. Same rule as
|
||
the roadmap.
|
||
- **Not a replacement for the roadmap.** Phases are still where the
|
||
implementation details live. This doc is the orientation layer above them.
|
||
- **Not granular enough for daily work.** Daily work happens at the phase /
|
||
sub-phase / commit level. The milestone is the multi-week target you're
|
||
aiming at.
|