acdream/memory/project_retail_research_index.md
Erik d951304875 memory: capture full 2026-04-17 session + permanent research index
Two memory files landed:

1. Updated memory/project_session_2026_04_17.md — covers all three
   commits today:
   - ff325ab debug overlay + mouse controls
   - 7230c15 retail UI research + C# scaffold
   - 3f913f1 13-slice deep-dive marathon + scaffolds + roadmap

   Includes the "what to build tomorrow" lookup table, the architectural
   headline findings, and session lessons (Opus-4.7 parallel swarms are
   worth the cost; keystone.dll landmine; GameEvent dispatcher is the
   biggest network gap).

2. New memory/project_retail_research_index.md — permanent index for
   the 20 research docs (6 UI slices + 13 subsystem slices). Quick-
   lookup table "use this slice when you're doing X". Also captures the
   critical cross-cutting findings (architecture, wire, dat ranges) and
   already-extracted retail-faithful formulas for instant reference.

   This file is the standing invariant: before writing any retail-AC-
   specific code, open it first to find the matching slice.
2026-04-18 13:24:11 +02:00

121 lines
6.6 KiB
Markdown

# Retail AC research index — permanent lookup table
Written 2026-04-17. Points at the 20 research documents produced in
that day's dual-swarm marathon (6 UI agents + 13 subsystem agents,
all Opus-4.7 high-effort).
**When to use:** any time you're about to write code for a retail AC
subsystem. Open the slice for that subsystem BEFORE writing the first
line — every slice has pseudocode + C# port sketch + citations.
---
## UI layer (`docs/research/retail-ui/`) — 6 slices, ~30,000 words
| ID | File | Use when… |
|----|------|-----------|
| — | [`00-master-synthesis.md`](../docs/research/retail-ui/00-master-synthesis.md) | First read. Has cross-references + port decisions |
| 01 | [`01-architecture-and-init.md`](../docs/research/retail-ui/01-architecture-and-init.md) | Extending GameWindow's init flow or main loop |
| 02 | [`02-class-hierarchy.md`](../docs/research/retail-ui/02-class-hierarchy.md) | Adding new widget types to our UI toolkit |
| 03 | [`03-rendering.md`](../docs/research/retail-ui/03-rendering.md) | Building AcFont from portal.dat, dat sprites, scissor |
| 04 | [`04-input-events.md`](../docs/research/retail-ui/04-input-events.md) | Adding drag-drop, tooltip, focus, modal |
| 05 | [`05-panels.md`](../docs/research/retail-ui/05-panels.md) | Porting chat, attributes, paperdoll, spellbook, inv |
| 06 | [`06-hud-and-assets.md`](../docs/research/retail-ui/06-hud-and-assets.md) | Vital orbs, radar, compass, any dat asset lookup |
## Subsystems (`docs/research/deepdives/`) — 13 slices, ~78,000 words
| ID | File | Use when… |
|-----|------|-----------|
| — | [`00-master-synthesis.md`](../docs/research/deepdives/00-master-synthesis.md) | First read. Has the full dependency graph + phase sequence |
| R1 | [`r01-spell-system.md`](../docs/research/deepdives/r01-spell-system.md) | Implementing cast flow, fizzle, mana cost, buffs, recalls |
| R2 | [`r02-combat-system.md`](../docs/research/deepdives/r02-combat-system.md) | Damage formula, hit-chance, crit, body-part targeting |
| R3 | [`r03-motion-animation.md`](../docs/research/deepdives/r03-motion-animation.md) | Expanding MotionInterpreter, hook delivery, stance blends |
| R4 | [`r04-vfx-particles.md`](../docs/research/deepdives/r04-vfx-particles.md) | Building the particle system, PhysicsScript dispatch |
| R5 | [`r05-audio-sound.md`](../docs/research/deepdives/r05-audio-sound.md) | Audio engine, SoundTable + Wave loaders, 3D falloff |
| R6 | [`r06-items-inventory.md`](../docs/research/deepdives/r06-items-inventory.md) | Item model, Container, Appraise, burden, stack-split |
| R7 | [`r07-character-creation.md`](../docs/research/deepdives/r07-character-creation.md) | Char-creation panel, heritages, skill credits |
| R8 | [`r08-network-protocol-atlas.md`](../docs/research/deepdives/r08-network-protocol-atlas.md) | Anything wire-format. Complete opcode map. |
| R9 | [`r09-dungeon-portal-space.md`](../docs/research/deepdives/r09-dungeon-portal-space.md) | Dungeon streaming, PlayerTeleport flow, portal visibility |
| R10 | [`r10-quest-dialogs.md`](../docs/research/deepdives/r10-quest-dialogs.md) | Emote scripts, NPC dialog, quest timers, contract tracker |
| R11 | [`r11-allegiance.md`](../docs/research/deepdives/r11-allegiance.md) | Allegiance tree, XP passup, allegiance chat channels |
| R12 | [`r12-weather-daynight.md`](../docs/research/deepdives/r12-weather-daynight.md) | Sky dome, day/night, rain/snow, Portal Year clock |
| R13 | [`r13-dynamic-lighting.md`](../docs/research/deepdives/r13-dynamic-lighting.md) | LightInfo dat, 8-light cap, cell ambient, blob shadow |
---
## Existing research from prior sessions (separate from these 20)
- [`docs/research/acclient_function_map.md`](../docs/research/acclient_function_map.md) — already-mapped FUN_ addresses (physics, motion, terrain, landblock)
- [`docs/research/2026-04-12-movement-deep-dive.md`](../docs/research/2026-04-12-movement-deep-dive.md) — cross-client movement cross-check
- [`docs/research/2026-04-13-phase-audit.md`](../docs/research/2026-04-13-phase-audit.md) — audit of decompile-verified vs guessed AC-specific code
---
## Critical cross-cutting findings (quick reference)
### Architecture facts
- **Retail UI runs in `keystone.dll`**, not `acclient.exe`. Own-toolkit
approach is the only viable path. Use retail event codes (0x01, 0x15,
0x3E, 0x201) so hand-ported panel code translates cleanly.
- **Weather is client-deterministic from Portal Year time.** No
`SetWeather` opcode exists. Same clock everywhere → same sky everywhere.
- **Lighting is D3D fixed-function, 8-light cap**, no attenuation inside
Range then hard cutoff. Terrain baked per-vertex via `AdjustPlanes`.
- **Motion hooks drive audio + VFX + combat timing.** R3 motion-hook
expansion is the blocker for R2, R4, R5 to feel retail-faithful.
### Wire protocol facts
- Of 94 GameEvents (S→C envelope `0xF7B0`), **0 are handled today**.
Biggest network gap.
- GameMessage `0xF7E0` SystemChat, `0xF7DE` TurbineChat, `0xF748`
UpdateMotion, `0xF751` PlayerTeleport, `0xF750` PlaySound, `0xF755`
PlayEffect are all unhandled.
- LoginComplete (`0x00A1`) must be re-sent after `PlayerTeleport`.
### Dat-ID ranges (de-duplicated across all 20 docs)
| Range | Type | Used by |
|---|---|---|
| `0x0A000000..0x0A00FFFF` | Wave | R5 |
| `0x20000000..0x2000FFFF` | SoundTable | R5 |
| `0x0E000002` | CharGen | R7 |
| `0x2F…` | SpellTable | R1 |
| `0x30…` | SpellComponentsTable | R1 |
| `0x40000000..0x40000FFF` | Font | UI03 |
| `0x06……` | RenderSurface | UI03, UI06, R13 shadow `0x0600102F` |
| `0x21…` | LayoutDesc | UI06 |
| `0x14…` | MasterInputMap | UI06 |
### Retail-faithful formulas already extracted
```
// Spell fizzle (R1)
chance = 1 / (1 + exp(-0.07 * (skill - difficulty))) // 0 if skill < diff-50
// Hit chance physical (R2)
chance = 1 - 1 / (1 + exp(0.03 * (attackSkill - defenseSkill)))
// Hit chance magic (R2)
chance = 1 - 1 / (1 + exp(0.07 * (attackSkill - defenseSkill)))
// Burden max (R6)
max = 150 * Strength + Strength * bonusBurden
// Audio falloff (R5)
att = (minDist^2) / (dist^2) (clamped 0..1, no doppler/cone/HRTF)
// Allegiance XP passup (R11)
direct = 50 + 22.5 * loyalty/291 * (1 + RT/730 * IG/720)
indirect = 16 + 8.0 * loyalty/291 * (1 + RT/730 * IG/720)
```
---
## Standing invariant for future sessions
**Before writing any retail-AC-specific code**, grep this index for the
subsystem and open the matching slice. The sliceds give you pseudocode +
C# port sketch + citations — you'll save hours and avoid hallucinating
constants. If the subsystem isn't in the 20 slices, add it to the
roadmap and consider dispatching an Opus-agent research pass for it
before coding.