docs+feat: 13 retail-AC deep-dives (R1-R13) + C# port scaffolds + roadmap E-H

78,000 words of grounded, citation-backed research across 13 major AC
subsystems, produced by 13 parallel Opus-4.7 high-effort agents. Plus
compact C# port scaffolds for the top-5 systems and a phase-E-through-H
roadmap update sequencing the work.

Research (docs/research/deepdives/):
- 00-master-synthesis.md          (navigation hub + dependency graph)
- r01-spell-system.md        5.4K words (fizzle sigmoid, 8 tabs, 0x004A wire)
- r02-combat-system.md       5.9K words (damage formula, crit, body table)
- r03-motion-animation.md    8.2K words (450+ commands, 27 hook types)
- r04-vfx-particles.md       5.8K words (13 ParticleType, PhysicsScript)
- r05-audio-sound.md         5.6K words (DirectSound 8, CPU falloff)
- r06-items-inventory.md     7.4K words (ItemType flags, EquipMask 31 slots)
- r07-character-creation.md  6.3K words (CharGen dat, 13 heritages)
- r08-network-protocol-atlas 9.7K words (63+149+94 opcodes mapped)
- r09-dungeon-portal-space.md 6.3K words (EnvCell, PlayerTeleport flow)
- r10-quest-dialogs.md       7.1K words (emote-script VM, 122 actions)
- r11-allegiance.md          5.4K words (tree + XP passup + 5 channels)
- r12-weather-daynight.md    4.5K words (deterministic client-side)
- r13-dynamic-lighting.md    4.9K words (8-light cap, hard Range cutoff)

Every claim cites a FUN_ address, ACE file path, DatReaderWriter type,
or holtburger/ACViewer reference. The master synthesis ties them into a
dependency graph and phase sequence.

Key architectural finding: of 94 GameEvents in the 0xF7B0 envelope,
ZERO are handled today — that's the largest network-protocol gap and
blocks F.2 (items) + F.5 (panels) + H.1 (chat).

C# scaffolds (src/AcDream.Core/):
- Items/ItemInstance.cs    — ItemType/EquipMask enums, ItemInstance,
                             Container, PropertyBundle, BurdenMath
- Spells/SpellModel.cs      — SpellDatEntry, SpellComponentEntry,
                             SpellCastStateMachine, ActiveBuff,
                             SpellMath (fizzle sigmoid + mana cost)
- Combat/CombatModel.cs     — CombatMode/AttackType/DamageType/BodyPart,
                             DamageEvent record, CombatMath (hit-chance
                             sigmoids, power/accuracy mods, damage formula),
                             ArmorBuild
- Audio/AudioModel.cs       — SoundId enum, SoundEntry, WaveData,
                             IAudioEngine / ISoundCache contracts,
                             AudioFalloff (inverse-square)
- Vfx/VfxModel.cs           — 13 ParticleType integrators, EmitterDesc,
                             PhysicsScript + hooks, Particle struct,
                             ParticleEmitter, IParticleSystem contract

All Core-layer data models; platform-backed engines live in AcDream.App.
Compiles clean; 470 tests still pass.

Roadmap (docs/plans/2026-04-11-roadmap.md):
- Phase E — "Feel alive": motion-hooks + audio + VFX
- Phase F — Fight + cast + gear: GameEvent dispatch, inventory,
            combat, spell, core panels
- Phase G — World systems: sky/weather, dynamic lighting, dungeons
- Phase H — Social + progression: chat, allegiance, quests, char creation
- Phase J — Long-tail (renumbered from old Phase E)

Quick-lookup table updated with 10+ new rows mapping observations to
new phase letters.
This commit is contained in:
Erik 2026-04-18 10:32:44 +02:00
parent 7230c1590f
commit 3f913f1999
20 changed files with 15312 additions and 17 deletions

View file

@ -0,0 +1,448 @@
# acdream — Retail AC Deep-Dive Master Synthesis
**Date:** 2026-04-17
**Total research output:** ~78,000 words across 13 deep-dive slices
(plus the retail-UI set from earlier the same day — 30,000 words more).
Each slice is a self-contained 4-9 KLOC document of research +
pseudocode + C# port plan, with every claim cited to a decompiled
`FUN_` address or an ACE / DatReaderWriter / holtburger / ACViewer
reference.
This document is the navigation hub and cross-reference map. It does
NOT repeat each slice's findings; it summarizes the headline, lists
cross-system dependencies, and proposes the concrete acdream phase
sequence that falls out of the research.
---
## 1. Slice inventory
| ID | Topic | File | Words | Key finding |
|-----|---------------------------------|--------------------------------------------------------------|-------|-------------|
| R1 | Spell system | [r01-spell-system.md](r01-spell-system.md) | 5.4K | Sigmoid `1/(1+e^(-0.07·(skill-difficulty)))` fizzle curve; 8 spellbook tabs at panel offset `0x638+0x1C·tab`; spell `0x004A`/`0x0048` wire; spell book unlock via `UpdateSpell (0x02C1)` |
| R2 | Combat | [r02-combat-system.md](r02-combat-system.md) | 5.9K | `PowerMod = Level + 0.5` melee; physical logistic at `k=0.03` / magic at `k=0.07`; 12-quadrant body table; 7 damage types × layered AL; 10%/5% base crit |
| R3 | Motion + animation | [r03-motion-animation.md](r03-motion-animation.md) | 8.2K | 450+ motion commands; cycle key `(style<<16)\|(cmd & 0xFFFFFF)`; 27 hook types; no crossfade — link animations ARE the transition; `-0.65` backward factor; `1.5×` run-turn |
| R4 | VFX / particles | [r04-vfx-particles.md](r04-vfx-particles.md) | 5.8K | 13 ParticleType motion integrators; no flipbook (chain hooks instead); PlayScript enum has 174 values; `PhysicsScriptTable` mod-selection; WorldBuilder's `ParticleBatcher` ports directly |
| R5 | Audio | [r05-audio-sound.md](r05-audio-sound.md) | 5.6K | DirectSound 8 + WinMM MIDI; 16-voice cap; inverse-square CPU falloff (no doppler/cone/HRTF); 204 Sound IDs; motion-driven triggers via animation hooks 1/2/21 |
| R6 | Items + inventory | [r06-items-inventory.md](r06-items-inventory.md) | 7.4K | ItemType is `[Flags]` 32-bit; 31 EquipMask slots; 7 property tables; `Appraise` C2S `0x00C8` / S2C `0x00C9`; burden = 150·STR + STR·bonusBurden; 2-deep pack depth |
| R7 | Character creation | [r07-character-creation.md](r07-character-creation.md) | 6.3K | Dat `0xE000002 CharGen`; 13 heritages × 330 attr credits × 52 skill credits (Olthoi 68); SpecializedCost is a DELTA; `0xF656``0xF643` wire round-trip |
| R8 | Network protocol atlas | [r08-network-protocol-atlas.md](r08-network-protocol-atlas.md) | 9.7K | 63 primary GameMessages + 149 GameActions + 94 GameEvents mapped. Of GameEvents (S→C, 0xF7B0 envelope), **zero handled** today — biggest gap |
| R9 | Dungeon + portal space | [r09-dungeon-portal-space.md](r09-dungeon-portal-space.md) | 6.3K | Dungeon detect = all heights 0 & NumCells>0 & no Buildings; `PlayerTeleport (0xF751)` needs LoginComplete re-send; no loading screen — "pink bubble" hides avatar |
| R10 | Quest + NPC dialogs | [r10-quest-dialogs.md](r10-quest-dialogs.md) | 7.1K | "Quests" are emote-script trees, not a first-class system; 122 EmoteType × 39 EmoteCategory; contract tracker via `0x0314/0x0315`; dialog = chat message with `<Tell:...>` markup |
| R11 | Allegiance | [r11-allegiance.md](r11-allegiance.md) | 5.4K | Max 11 direct vassals; rank `max(lower+1, higher)` capped 10; XP passup `50+22.5·loyalty/291·(1+RT/730·IG/720)`; no swear cooldown (24h applies to name only) |
| R12 | Weather + day/night | [r12-weather-daynight.md](r12-weather-daynight.md) | 4.5K | 95% client-side — server just sends Portal Year time; determinism = same sky everywhere; no `SetWeather` opcode; sky is geometry not shader ramp |
| R13 | Dynamic lighting | [r13-dynamic-lighting.md](r13-dynamic-lighting.md) | 4.9K | Hard 8-light cap (fixed function D3D); NO distance attenuation inside Range then hard cutoff; terrain baked per-vertex via `AdjustPlanes`; blob shadow at `0x0600102F` |
**Grand total: 82,500 words of grounded, citation-backed research.**
---
## 2. The big picture — how the systems wire together
```
┌─────────────────────────────────────────────────────────────────┐
│ ACE (Asheron's Call Emulator) │
│ — server authority │
└────┬────────────────────────────────────────────────────────────┘
│ UDP packets (ISAAC, fragmented, ack'd)
┌────▼────────────────────────────────────────────────────────────┐
│ WorldSession (R8 atlas) │
│ • Login (4 msg), CharList, EnterWorld, CreateObject stream │
│ • Dispatch of 63 + 94 + 149 opcodes │
└────┬──────────────┬──────────────┬──────────────┬───────────────┘
│ │ │ │
│CreateObject │UpdateMotion │UpdateHealth │UpdateSpell
│ items │ anim │ combat │ buff/debuff
▼ ▼ ▼ ▼
┌──────────┐ ┌───────────┐ ┌──────────┐ ┌───────────┐
│ Inventory│ │ Motion │ │ Combat │ │ Spell │
│ (R6) │ │ (R3) │ │ (R2) │ │ (R1) │
│ │ │ │ │ │ │ │
│ItemInst │──│ DoMotion │──│ Damage │──│ ActiveBuf │
│Container │ │ MotionTbl │ │ fromCmd │ │ Fizzle │
│Burden │ │ Hooks ────┼──┤ FootStep │ │ Recall │
└────┬─────┘ └─┬────┬────┘ └────┬─────┘ └────┬──────┘
│ │ │ │ │
│ │ └────hooks──►│ │
│ │ │ │
└──────────┴───────►┌────────▼─────────────▼──┐
│ Audio (R5) + │
│ VFX/Particles (R4) │
│ — cosmetic layer │
└──────────────┬──────────┘
┌───────────────────────────────┐
│ UI (retail-ui set) + HUD │
│ AttrPanel + SpellBar + Chat │
│ VitalOrbs + Radar + Compass │
│ (all behind UiRoot scaffold) │
└───────────────────────────────┘
┌─ World lifecycle
┌───▼─────────────────────┐
│ Streaming (existing) │
├─────────────────────────┤
│ + Dungeon / portal (R9) │
│ + Weather sky (R12) │
│ + Dynamic lights (R13) │
└─────────────────────────┘
┌─ Social / progression
┌──────────────────┐ ┌─────────────────┐
│ Allegiance (R11) │ │ Quest + dialog │
│ • Tree + ranks │ │ (R10) │
│ • XP passup │ │ • Emote scripts │
│ • Chat channels │ │ • Contract tkr │
└──────────────────┘ └─────────────────┘
┌─ Game entry
┌──────────────────┐
│ CharCreate (R7) │
│ • Heritage + skl │
│ • Appearance │
│ • Starting town │
└──────────────────┘
```
---
## 3. Critical cross-system dependencies
These are the places where one system MUST be built before another can
land cleanly. They define the port sequence.
### 3.1 Motion (R3) blocks three other systems
- **Audio (R5)** — footstep/swing/cast sounds are triggered by
AnimationHooks (types 1=Sound, 2=SoundTable, 21=SoundTweaked). Without
the hook dispatcher expanded (R3 port plan), audio has no trigger
source beyond server-pushed `PlaySound (0xF750)`.
- **VFX (R4)** — spell release, weapon trail, footstep dust attach at
specific animation hook frames (`CreateParticleHook`, hook type 18).
- **Combat (R2)** — the damage application frame is an `AttackFrame`
hook (type 20) inside the attack animation. Without R3 hook
dispatch, combat math has to guess timing.
**Implication:** R3 motion-hook expansion is a blocker for R2, R4, R5
to feel retail-faithful. It's a prerequisite for "the game feels
alive".
### 3.2 Inventory (R6) blocks three UI panels
- **Paperdoll** — drops items into slots; requires `ItemInstance` model.
- **Spellbook (R1)** — items like scarabs are consumed via R6 burden rules.
- **Quest turn-in (R10)** — emote's "Give" trigger needs item ID +
property bundle to validate.
**Implication:** R6 is the data-model foundation for any gameplay UI.
### 3.3 Network (R8) is the permanent bottleneck
Of 94 GameEvents (0xF7B0 envelope), **zero are handled today**. These
include:
- `PlayerDescription (0x0013)` — full character at login
- `AllegianceInfoResponse (0x007E)`
- `Fellowship*` events (5 opcodes)
- `Identify/Appraise response (0x00C9)`
- `SendClientContractTracker (0x0315)`
**Implication:** R8 Phase D (chat + session health) is the unlocking
step for the whole social/quest/inventory stack.
### 3.4 CharCreate (R7) needs CharGen dat + at least one class
The CharGen `0xE000002` dat has every heritage + template + appearance
config. But we also need:
- `ObjDesc` application pipeline (already in our StaticObjectManager)
- At least one working panel to host the UI (R7 uses our UiRoot scaffold)
- Appearance preview renderer — already works for outdoor characters
**Implication:** R7 is the smallest unblocker for "user makes a char
in acdream instead of using ACE admin tools".
### 3.5 Dungeon (R9) extends existing streaming
R9's `EnvCellStreamer` + `PortalVisibility` + `TeleportController` are
additive to `StreamingController`. Risk is low.
### 3.6 Weather (R12) and Lighting (R13) share a scene-state UBO
Both write sun direction + ambient color + fog. A single
`SceneLightingState` UBO fed to `terrain.frag` and `mesh.frag`
consolidates them. R13 reads it; R12 produces it.
### 3.7 VFX (R4) + Audio (R5) share PhysicsScript/PlayScript
Retail's `PlayEffect (0xF755)` bundles both particle + sound. Our
`PhysicsScript` dispatcher should fire both R4 and R5 hooks atomically.
---
## 4. Proposed phase sequence
Based on the dependency graph + what unlocks the most gameplay fastest:
### Phase E — Core gameplay loop (highest value)
| Step | What | Research | Unblocks |
|------|--------------------------------------------------------------------|----------|----------|
| E.1 | R3 Motion hooks expansion | R3 | E.2-E.4 |
| E.2 | R5 Audio engine + motion-hook wiring | R5 | immediate feel |
| E.3 | R4 Particle system + motion-hook wiring + PlayScript dispatcher | R4 | immediate feel |
| E.4 | R2 Combat math + server-broadcast damage/health | R2, R8 | can actually fight |
| E.5 | R1 Spell cast state machine (buffs + recalls first, projectiles later) | R1, R8 | can cast spells |
**Acceptance:** player swings a sword, hears the whoosh, sees the
contact spark, damage number floats up, HP bar drops on the target,
monster dies with footstep/impact sounds. Spell buff lands with
glowing aura + cast sound.
### Phase F — Inventory + UI panels
| Step | What | Research | Unblocks |
|------|--------------------------------------------------------------------|----------|----------|
| F.1 | R8 GameEvent envelope (0xF7B0) dispatcher | R8 | F.2+ |
| F.2 | R6 Item model (`ItemInstance`, `PropertyBundle`) | R6 | F.3-F.5 |
| F.3 | Attributes panel (UI slice 05 + R8 `PlayerDescription`) | UI05, R8 | char sheet |
| F.4 | Paperdoll + Inventory panel (drag-drop from UI slice 04) | R6, UI04 | gearing up |
| F.5 | Spellbook panel (R1 tabs + icons) | R1, UI05 | cast via UI |
**Acceptance:** opens all core panels, drags items between inventory
+ paperdoll, casts spells by clicking icons.
### Phase G — World systems
| Step | What | Research |
|------|--------------------------------------------------------------------|----------|
| G.1 | R12 Sky / weather / day-night | R12 |
| G.2 | R13 Dynamic lighting (8-light fixed pipeline) | R13 |
| G.3 | R9 Dungeon streaming + portal transition | R9 |
**Acceptance:** walk out of Holtburg at dusk, sky gradient rolls,
torches cast light, enter a dungeon via teleport, see the dungeon
interior.
### Phase H — Social + progression
| Step | What | Research |
|------|------------------------------------------------|----------|
| H.1 | Chat window (UI slice 05) + all 6 wire ops | UI05, R8 |
| H.2 | R11 Allegiance panel + XP passup display | R11 |
| H.3 | R10 Quest/emote/dialog system | R10 |
| H.4 | R7 Character creation panel | R7 |
**Acceptance:** talk to an NPC, get a quest, complete it, gain XP,
allegiance MOTD shows in chat.
---
## 5. What's already shipped vs what R-slices unlock
| Domain | Shipped | Next from deep-dives |
|-------------------------|-----------------------------------------------------------|----------------------|
| World rendering | Terrain + static meshes + scenery + animation + chase cam | R12 sky / R13 lights |
| Streaming | 5×5 landblock window + EnvCell walker | R9 dungeons |
| Login + wire transport | LoginRequest → EnterWorld → CreateObject stream | R8 GameEvent envelope|
| Physics / collision | Retail-faithful BSP + cylinder + sphere-sweep + scenery | R3 motion hooks |
| Input / UI | UI framework (retail-ui set) + debug overlay | R4 VFX + R5 audio panels |
| Movement | WASD + chase cam + jump + autonomous-position sync | R3 motion commands |
| Character appearance | ObjDesc + creature palette + hair/skin/clothing | R7 char-create panel |
| **Everything else** | — | R1/R2/R6/R10/R11 |
---
## 6. Consolidated dat-ID catalog (cross-slice)
Every dat range touched by the 13 slices, de-duplicated:
| Range | DBObjType | Source slices |
|-----------------------------|-------------------|---------------|
| `0x02xxxxxx` | Setup | (existing) |
| `0x04xxxxxx` | Palette | UI04 |
| `0x05xxxxxx` | PaletteSet | UI04 |
| `0x06000000..0x06FFFFFF` | RenderSurface | UI03/UI06, R13 blob shadow `0x0600102F` |
| `0x07xxxxxx` | RenderSurface | UI04 |
| `0x08xxxxxx` | Surface | UI04 |
| `0x09xxxxxx` | SurfaceTexture | (existing) |
| `0x0A000000..0x0A00FFFF` | Wave | **R5 audio** |
| `0x0E000002` | CharGen | **R7 chargen**|
| `0x0F000000..0x0FFFFFFF` | SurfaceMaterial | UI04 |
| `0x13000000` | Region | (existing) + R12 sky + R13 lights |
| `0x14xxxxxx` | MasterInputMap | UI06 |
| `0x20000000..0x2000FFFF` | SoundTable | **R5 audio** |
| `0x21xxxxxx` | LayoutDesc | UI06 |
| `0x23/0x24xxxxxx` | StringTable | UI06 |
| `0x2Fxxxxxx` | SpellTable | **R1 spell** |
| `0x30xxxxxx` | SpellComponents | **R1 spell** |
| `0x33xxxxxx` | MotionTable | (existing) + R3 |
| `0x34000000..0x34000FFF` | Animation | (existing) + R3 |
| `0x40000000..0x40000FFF` | Font | UI03 |
| `0x41xxxxxx` | LanguageInfo | UI06 |
---
## 7. Consolidated opcode quick-reference (cross-slice)
Every opcode mentioned in any slice, for rapid grep-back:
```
Login flow
0x0001 LoginRequest
0xF7B1 GameAction envelope (C→S) ← 149 sub-opcodes
0xF7B0 GameEvent envelope (S→C) ← 94 sub-opcodes
0xF657 CharacterList
0xF656 Character_SendCharGenResult (R7)
0xF643 CharGenResponseType / CharacterError (R7)
Movement (B.3 already ported)
0xF74C MoveToState / UpdatePosition (partial overlap)
0xF753 Jump / bi-directional
0xF748 UpdateMotion (R3)
0xF751 PlayerTeleport (R9)
Combat (R2)
0x0008 AttackTargetRequest
0x000A CombatStyleRequest
0x0053 TargetAttack
0x01B7 MissileAttack
0xF7xx UpdateHealth / UpdateVital (R2, R6 vitals)
Spells (R1)
0x0048 Magic_CastUntargetedSpell (R1)
0x004A Magic_CastTargetedSpell (R1)
0x02C1 UpdateSpell (spellbook add) (R1)
0x02C2-0x02C8 Enchantment lifecycle (R1)
0x02BB HearSpeech (chatType 0x11 = syllables) (R1)
Inventory / items (R6)
0x00C8 AppraiseRequest (R6)
0x00C9 IdentifyResponse (R6)
0xF7xx InventoryMove / Give / Drop / Pickup
Chat (UI05)
0xF7B0 GameEventChannelBroadcast (0x0147 / envelope)
0xF7B0 GameEventTell (0x02BD)
0xF7E0 GameMessageSystemChat
0x02BB GameMessageHearSpeech
0x02BC GameMessageHearRangedSpeech
0xF7DE GameMessageTurbineChat
0x0295 GameEventSetTurbineChatChannels
Allegiance (R11)
0x001D Swear
0x001E Break
0x007E AllegianceInfoResponse
0x00DD AllegianceMotd
0x02AB AllegianceRecall action
Quest / contracts (R10)
0x0314 SendClientContractTrackerTable
0x0315 SendClientContractTracker
0xF751 TeleportToPoi (quest-driven recall)
Audio (R5)
0xF750 PlaySound
0xF755 PlayEffect (R4 + R5 bundle)
Dungeon / portal (R9)
0xF751 PlayerTeleport (R9)
0x00A1 LoginComplete (re-send after teleport)
Attributes (UI05)
0x02E3 PrivateUpdateAttribute
0x02E7 PrivateUpdateVital
0x02E9 PrivateUpdateAttribute2ndLevel
Admin / world
0xEA60 AdminEnvirons (colored-fog override) (R12)
```
**Full table in `r08-network-protocol-atlas.md`.**
---
## 8. Open questions that surfaced
1. **keystone.dll** — blocking for truly retail-faithful widget layout.
Would need a second decompile pass. (UI slice 02)
2. **Launcher / patcher** — similarly binary-only. (out of scope)
3. **Retail fizzle mana** — R1 flagged "5 points lost on fizzle" as
tentative; would benefit from retail packet capture.
4. **Power bar timing** — R2 notes the power bar fills over some
seconds, but exact seconds unclear in decompile.
5. **Doppler on / off** — R5 says off, but this is inferred from no
`SetFrequency` on 3D buffers. Verify with retail binary trace if
audio fidelity matters later.
6. **Sky replace animation** — R12 notes `SkyObjReplace` governs dawn
mesh swap but keyframe interp unclear.
7. **Contract tracker reward delivery** — R10 notes the
`SendClientContractTrackerTable` format but quest rewards come via
a separate emote path.
8. **Allegiance passup IG/RT decay** — R11 has the XP formula but the
"generation tax" (grandpatron vs direct patron) needs a golden-value
conformance test against ACE.
---
## 9. Risk assessment for each slice's port
Low (data models + straightforward translation):
- R6 Inventory (well-typed properties + containers)
- R7 CharCreate (one dat object, one wire roundtrip)
- R8 Protocol atlas (additive handlers; Phase-F work)
- R11 Allegiance (data + math, no renderer work)
- R12 Weather (deterministic from clock)
Medium (has math or subtle corner cases):
- R1 Spell (sigmoid fizzle, mana conversion, enchant stacking)
- R2 Combat (damage formula sensitivity, crit tables, body-part)
- R3 Motion hooks (27 types, delivery during frame advance)
- R5 Audio (inverse-square falloff, 16-voice eviction)
- R9 Dungeon (portal visibility + teleport state machine)
- R13 Lighting (8-light selection + attenuation curve fidelity)
High (novel systems for us):
- R4 VFX particle systems (new renderer subsystem)
- R10 Quest / emote scripts (mini-VM with 122 action types)
---
## 10. Port sequencing recommendation
If the user wants one-and-only-one focus per week:
- **Week 1 — "Feel alive":** R3 motion hooks → R5 audio → R4 VFX.
Ship: footsteps, swing sounds, impact particles, spell aura particle.
- **Week 2 — "Fight":** R2 combat math + R8 GameEvent envelope
dispatcher (a few critical events). Ship: actual damage flow, HP
updates, death.
- **Week 3 — "Cast":** R1 spell cast state machine (buffs + recall
first, projectile spells later). Ship: heal spell works, buff spell
works, recall spell works.
- **Week 4 — "Have gear":** R6 inventory + Attributes + Paperdoll
panels. Ship: equip weapon, see stats, manage pack contents.
- **Week 5 — "World":** R12 sky + R13 lights + R9 dungeon.
Ship: enter a dungeon, see torch lights, day/night outside.
- **Week 6 — "Social":** Chat + R11 allegiance + R10 quest +
R7 char-create. Ship: complete the loop.
If the user wants parallel work: R3/R5/R4 triad can be one stream;
R6/R8 infra can be a parallel stream; R12/R13 visual can be a third.
---
## 11. Plugin API implications
The deep-dives surfaced several places where acdream's plugin API
needs to expose state (R6 item, R1 spell, R11 allegiance, R10 quest).
These are all pure data + event streams, no 3D rendering hooks. The
current `IWorldGameState` + `IEvents` architecture scales to all of
them — each system adds a new read-only interface + an event stream.
Deferred to a separate phase (plugin-api-expansion) once the core
systems land.