acdream/memory/project_session_2026_04_17.md
Erik d910d570a3 memory: extend 2026-04-17 session handoff with evening bug-bash work
Adds the three evening client-debugging commits + the five new
architectural findings they produced:

- NPC clothing by-camera-angle flicker → instance-batching dedup fix
  (`(GfxObjId, PaletteHash ^ SurfaceOverridesHash)` as group key)
- Run animation broadcast fix → wire WalkForward+HoldKey.Run +
  separate LocalAnimationCommand for local RunForward cycle
- Jump animation via MotionCommand.Falling SubState swap (not Action,
  not Modifier — just a plain SubState cycle)

Also captures the lessons learned from the jump saga: trust empirical
motion-table dumps over assumptions, the retail animation taxonomy
(Style/SubState/Modifier/Action masks), and the wire/local-animation
separation pattern.

Updates the pickup table with the small remaining polish items
(backward-walk jitter, stop-running twitch, remote-char Z offset).
2026-04-18 15:55:31 +02:00

9.1 KiB

Session 2026-04-17 — Debug overlay → full-day retail-AC research + client bug-bash

Timeline

  1. Morning — Debug overlay (TTF atlas + text batcher + HUD panels) and input-control tuning (per-mode sensitivity, RMB free-orbit, wheel zoom, F8/F9 sensitivity). Shipped in commit ff325ab.
  2. Midday — User requested a deep investigation of the retail AC client's GUI subsystem. Dispatched 6 parallel Opus-4.7 high-effort agents, produced a 30,000-word research bundle + C# UI scaffold. Shipped in commit 7230c15.
  3. Afternoon — User asked to research all remaining major AC subsystems (R1-R13) while they were AFK for 2 hours. Dispatched 13 parallel Opus-4.7 agents, produced a 78,000-word research bundle + master synthesis + 5 C# port scaffolds + rewritten roadmap. Shipped in commit 3f913f1.
  4. Evening — Back to hands-on client debugging. Fixed three significant live-play bugs one after another: NPC clothing by-angle flicker, run-animation sync with retail observers, and jump animation. Shipped in 3308cdd + 08ea2c0 + 6bce9b8.

What shipped today — seven commits on main

Commit Title Insert
ff325ab feat(ui): debug overlay + refined input controls 2,725
7230c15 docs+feat(ui): retail UI deep-dive research + C# scaffold 8,042
3f913f1 docs+feat: 13 retail-AC deep-dives + scaffolds + roadmap 15,312
d951304 memory: session handoff + permanent research index 222
3308cdd fix(movement+anim+session): clothing dedup + motion wire + jump-skill 272
08ea2c0 feat(anim): motion-action-queue infrastructure + findings 128
6bce9b8 fix(anim): jump animation via Falling SubState 71

Total ~26,800 lines added, 55+ new files. All commits green on build + 470 tests.

Key files to know

Research (the goldmine)

  • docs/research/deepdives/00-master-synthesis.md — start here. Nav hub for all 13 deep-dive slices; has the dependency graph and phase sequencing (E/F/G/H).
  • docs/research/deepdives/r01-…-r13-… — 13 subsystem deep-dives, 78,000 words total, every claim cited.
  • docs/research/retail-ui/00-master-synthesis.md — UI framework synthesis. 6 slice docs total, ~30,000 words.
  • memory/project_retail_research_index.md — permanent quick-lookup index for all 20 research docs.

C# scaffolds (what to build on)

  • src/AcDream.App/UI/ — retail-style widget toolkit (UiRoot, UiElement, UiPanel, UiHost, event types matching retail codes)
  • src/AcDream.Core/Items/ItemInstance.cs — R6 inventory data model
  • src/AcDream.Core/Spells/SpellModel.cs — R1 spell cast state machine
  • src/AcDream.Core/Combat/CombatModel.cs — R2 damage math
  • src/AcDream.Core/Audio/AudioModel.cs — R5 audio interfaces
  • src/AcDream.Core/Vfx/VfxModel.cs — R4 particle data model

Updated

  • docs/plans/2026-04-11-roadmap.md — rewritten Phase E-H based on the deep-dive synthesis. Old Phase E renamed to J.

Architectural headline findings

  1. Retail UI widgets live in keystone.dll, not acclient.exe. We implement our own retained-mode toolkit with retail-faithful event codes (0x01 click, 0x15 drag, 0x3E drop, 0x201 WM_LBUTTONDOWN) and consume the same portal.dat fonts + sprites for visual identity.
  2. Of 94 GameEvents (S→C, 0xF7B0 envelope), zero are handled in acdream today. Biggest network-protocol gap; blocks inventory, chat, quest tracking, allegiance.
  3. Motion hooks are the trigger source for audio + VFX + combat timing. R3 motion-hook expansion is the prereq for "feel alive" (Phase E).
  4. Weather is 95% client-side — deterministic from Portal Year server clock. No SetWeather opcode.
  5. Retail lighting is D3D fixed-function with 8-light cap, NO attenuation inside Range, then hard cutoff. "Feels right" not physical.
  6. Retail jumps are a SubState swap, not an Action. MotionCommand.Falling (0x40000015) is a SubState cycle whose motion-table Links carry the leap-up AND landing transitions. No action-queue port was needed; SetCycle(Falling) while airborne
    • normal SetCycle on land gives retail-faithful jump animation. Costly lesson: I first tried Action (mask 0x10) + Modifier (mask 0x20) routing and broke the character into a torso. Empirical motion-table dump is always worth more than my guesses.
  7. ACE's MovementData only computes ForwardSpeed for the WalkForward/WalkBackwards branch. Sending RunForward directly leaves observers at speed=0. Correct wire format is WalkForward + HoldKey.Run + ForwardSpeed=runRate — ACE auto-upgrades to RunForward for broadcast. Our own client uses a separate LocalAnimationCommand to play the RunForward cycle locally while the wire stays WalkForward.
  8. Instance batching by GfxObjId alone is catastrophic for NPCs. Every humanoid shares the same body GfxObjs; grouping them all into one DrawInstanced batch and using the first entry's texture for the whole batch meant whoever sorted first at a given camera angle "won" the palette. Fixed by keying instance groups on (GfxObjId, PaletteHash XOR SurfaceOverridesHash).

Client state at end of session

Client runs, connects to local ACE, in-world as +Acdream. Working:

  • Player moves, turns, strafes, jumps, lands. Jump animation plays via Falling SubState. Run animation broadcasts correctly to retail observers (WalkForward + HoldKey.Run wire format).
  • Holtburg NPCs render with correct clothing / palette, stable across camera angles (instance-batching dedup fix).
  • Debug overlay: F1 help, F2 collision wireframes, F3 dump, F4/F5/F6 panel toggles, F8/F9 sensitivity tuning.
  • Mouse: per-mode sensitivity (Chase 0.15x, Fly/Orbit 1.0x), RMB free-orbit, wheel zoom.

Known small issues (non-blocking):

  • Walk-backward animation's first step has a tiny jitter.
  • Running → stopping has a slight twitch (missing SubState→Ready link blend).
  • Some retail chars have legs through the ground (Z-offset bug — parked for later diagnosis).
  • Jump feel is 95% retail, not 100%.

Pickup for next session

"What should I do tomorrow?" table:

If you want to… Build…
Make the world feel alive (footsteps, sword whooshes, spell auras) Phase E — R3 motion hooks → R5 audio → R4 particles
Actually fight monsters Phase F.3 combat math + F.1 GameEvent dispatcher
Cast a buff + recall to lifestone Phase F.4 spell state machine + F.1 GameEvent
See a character sheet Phase F.2 item model + F.5 attributes panel (UI slice 05)
Enter a dungeon Phase G.3 dungeon streaming (R9)
See a sky Phase G.1 weather/day-night (R12)
Create a character in-client Phase H.4 CharCreate (R7)
Polish animation (backward jitter + stop twitch) Link-cycle walker for SubState→Ready transitions
Remote-char feet in ground Z-offset diagnosis (compare server-sent Z vs our terrain at observer XY)

The master synthesis (docs/research/deepdives/00-master-synthesis.md §10) recommends a specific week-by-week sequence if the user wants one-thing-at-a-time focus.

Session feedback / lessons

  • Opus-4.7 high-effort parallel agents are the right tool for decompile-intensive research. 13-agent swarm for R1-R13 completed in ~13 minutes, producing ~78,000 words of grounded work. Cost is high but quality justifies it for load-bearing research.
  • keystone.dll is a landmine for anyone trying to "port the UI faithfully" — the widget toolkit isn't in the decompile we have. Own- toolkit approach is the only viable path.
  • Network protocol atlas (R8) is the single biggest unblocker — nearly every other system depends on opcodes we don't handle.
  • Diagnostic dumps > guessing. The jump-animation saga took 4 launches + reverts before I added a diagnostic that dumped the actual Links dict contents. That one dump (revealing Links[0x003D0007] had an inner entry 0x40000015 Falling) instantly showed the right approach. Trust empirical motion-table contents over any assumption about retail structure. Add dumps liberally when the code is non-cooperative; remove when verified.
  • Retail animation lexicon: Style (mask 0x80), SubState (0x40), Modifier (0x20), Action (0x10). SubStates are looping cycles (Ready/Walk/Run/Falling/Crouch). Modifiers overlay on a cycle. Actions are transitions held in the Links dict (emotes, attacks). For any new animation: dump the motion table, find where the target motion lives, use SetCycle if it's in Cycles or the Links/Modifiers path if it's transitional.
  • Wire-format vs local-animation separation: we now route the wire ForwardCommand separately from the local animation command (RunForward cycle locally, WalkForward+HoldKey.Run on wire). Matching ACE's broadcast expectations and retail client expectations simultaneously sometimes requires two independent command streams.