acdream/docs/research/2026-06-02-retail-cell-render-research-prompt.md
Erik 840c1b6442 docs(render): Phase W (rev) — 4-model research + transition-membership/PView design
Four independent decomp studies (Opus 4.8 x2, Sonnet 4.6, external Codex)
converge: retail carries the cell through the collision sweep (validate_transition
advances curr_cell only on an accepted move, reverts on a block) and commits it in
SetPositionInternal — it never re-derives membership from a static resting position.
acdream already ports the sweep machinery (sp.CurCellId/CheckCellId, ValidateTransition,
CheckOtherCells) but ResolveWithTransition discards the swept cell and re-derives
statically via ResolveCellId (PhysicsEngine.cs:909/928) — the root of the
0170<->0031 doorway/cellar ping-pong. The do_not_load_cells prune is secondary
(static/cross-cell lists), not the anti-flicker; W2b was doubly misplaced and is reverted.

Render: one PView::ConstructView portal traversal over the same cell graph, rooted at
the physics current cell; seen_outside (not a dungeon flag) gates landscape; the outside
draws through exit portals clipped to the doorway (no blue-hole, no stencil split).
Dungeons/interiors share the machinery; "underground" is emergent.

Design doc lays out the staged, evidence-first rewrite (Stage 0 diagnostic ->
Stage 1 transition-owned membership [visual gate] -> Stage 2 CELLARRAY/prune parity ->
Stages 3-5 render root + PView seal + entity clip). Adds the shared research prompt and
all four study reports as the grounding record.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 13:58:51 +02:00

129 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Research task — Retail AC: cell transitions, underground/dungeons, and seamless inside/outside rendering
> **Shared prompt for a multi-model study (2026-06-02).** The same prompt is run on
> several models (Opus 4.6/4.7/4.8, Sonnet 4.6, and an external model) so we can compare
> independent reads before committing to an architecture. Study the **source** (retail
> decomp + reference repos) and **cite everything** — do not guess. Depth + citations
> matter far more than brevity.
## 0. Why this study exists (context)
**acdream** is a modern C# .NET port of the retail Asheron's Call client (Sept 2013 EoR
build). The rule: *the code is modern, the behavior is retail.* Every AC-specific algorithm
is ported faithfully from the named retail decomp.
We are at an architecture decision and want ground truth before choosing. Two coupled
problems:
1. **Cell-membership flicker (physics).** The player's "current cell" ping-pongs at
boundaries — at a near-static position the cell flips e.g. `0xA9B40170` (indoor cottage
vestibule) ↔ `0xA9B40031` (outdoor landcell) every physics tick, and also
`vestibule ↔ room` and inside the cellar. Root finding so far: acdream runs retail's
collision *sweep* but then **discards the swept cell** and **re-derives the cell from the
final static position every tick** (`PhysicsEngine.ResolveCellId`), which flips as the
collision push-back jitters the end position ±~8 cm across the boundary.
2. **Non-seamless indoor render.** Standing inside a cottage/cellar the interior does not
seal: the ceiling isn't capped, the doorway opening shows the blue clear-color instead of
the real outside (no sky / no rain visible through the door), entities/particles bleed
through walls, and at the threshold the view strobes between "indoor (incomplete)" and
"outdoor." acdream's render maintains its **own** cell/visibility system separate from
physics; we believe retail renders inside+outside seamlessly through a single
portal-visibility traversal.
We have a candidate fix direction ("track the cell through the transition sweep like
retail's `validate_transition` + `change_cell`, drop the static re-derive, add the
`do_not_load_cells` prune, and make the render obey one portal-visibility traversal"), but
we want a **solid, decomp-grounded understanding of how retail ACTUALLY does all of this**
before we commit — patches/guesses in this exact area have failed ~10× historically.
## 1. Your deliverable
A comprehensive, decomp-cited markdown report. For **every** non-trivial claim, cite the
retail function **name + address** (from the decomp) or the reference **file:line** you
verified it from. Include short pseudocode where it clarifies control flow. End with a
concrete "Recommended acdream architecture" section (questions D1416).
Write your report to the output path given to you (e.g.
`docs/research/2026-06-02-retail-cell-render-study-<model>.md`).
## 2. Sources to study
**Primary oracle — the retail decomp (study this first and most):**
- `docs/research/named-retail/acclient_2013_pseudo_c.txt` — 1.4 M lines of named pseudo-C.
Grep by `Class::method` (e.g. `CTransition::validate_transition`).
- `docs/research/named-retail/acclient.h` — verbatim retail struct definitions.
- `docs/research/named-retail/symbols.json` — name ↔ address index (grep by name or addr).
**Reference repos (cross-check at least two per topic; the intersection is usually truth):**
- `references/ACE/` — server-side C# physics port. `Source/ACE.Server/Physics/` has
`Common/` (ObjCell, EnvCell, LandCell, Position), `Animation/` and the transition/
sphere-path/cell logic. Authoritative C# reading of `find_cell_list`, `change_cell`,
`Transition`, `SpherePath`.
- `references/ACViewer/` — MonoGame client that renders world + dungeons. `Physics/Common/`
(EnvCell, ObjCell, CellArray) and `Render/` (how cells/portals are drawn).
- `references/WorldBuilder/` — the render base acdream extracted from. EnvCell/portal/
visibility/scenery managers; how it draws interiors + the (flat-stencil) inside/outside
split it uses.
- `references/Chorizite.ACProtocol/`, `references/AC2D/`, `references/holtburger/` — use as
relevant (struct field order, simpler client confirmations).
**acdream's current code (so your synthesis is actionable, not abstract):**
- `src/AcDream.Core/Physics/PhysicsEngine.cs``ResolveCellId` (~:272), `ResolveWithTransition` (~:651, see the two `ResolveCellId(sp.GlobalSphere[0].Origin,…)` calls at ~:909/:928 that discard the swept cell).
- `src/AcDream.Core/Physics/CellTransit.cs``FindCellList`/`FindCellSet`/`BuildCellSetAndPickContaining`, `FindTransitCellsSphere`, `AddAllOutsideCells`, `CheckBuildingTransit` (note: NO `do_not_load_cells` prune today).
- `src/AcDream.Core/Physics/TransitionTypes.cs``SpherePath` (`CheckCellId`/`CheckPos`, only set at `InitPath`/reset — NOT advanced through the sweep), `Transition` (`FindEnvCollisions`, `CheckOtherCells`).
- `src/AcDream.Core/World/Cells/` — the W1 unified cell graph (`ObjCell`/`EnvCell`/`LandCell`/`CellGraph`/`CellPortal`).
- `src/AcDream.App/Rendering/CellVisibility.cs` + `PortalVisibilityBuilder.cs` + `GameWindow.cs` (terrain/shell/entity draw gates ~:71507420).
## 3. Decomp anchors (verified starting points — confirm and expand; find more yourself)
Physics / cell tracking:
- `CPhysicsObj::change_cell` @ `0x00513390` (pseudo_c ~:281192) — the leave/enter setter.
- `CPhysicsObj::SetPositionInternal` @ `0x00515330` (~:283399) — reads `arg2->sphere_path.curr_cell`, calls `change_cell` only when it differs.
- `CTransition::validate_transition` @ `0x0050aa70` (~:272547) — advances `sphere_path.curr_cell = check_cell` on an accepted move (~:272608-272619); resets to `curr_cell` on a block/slide (~:272593).
- `CTransition::check_collisions` @ `0x0050aa00` (~:272530); `CTransition::check_other_cells` @ `0x0050ae50` (~:272717) — calls `find_cell_list`.
- `CTransition::transitional_insert` @ `0x0050b6f0` (~:273137) — the sweep stepper.
- `CObjCell::find_cell_list` @ `0x0052b4e0` (~:308742) — builds the cell array, picks the containing cell (`*arg5`), applies the `do_not_load_cells` prune (~:308829-308867).
- `CObjCell::GetVisible` (pseudo_c ~:308209) magnitude dispatch; `CEnvCell::GetVisible`, `CLandCell::GetVisible`, `CLandCell::add_all_outside_cells`, `CObjCell::point_in_cell` (vtable +0x84), `CEnvCell::find_transit_cells`.
Cell structs (acclient.h): `CObjCell` (:30915), `CEnvCell` (:32072), `CLandCell` (:31886),
`CSortCell` (:31880), `CCellPortal` (:32300), `CBldPortal` (:32094), `CellStruct` (:32275),
plus `SPHEREPATH`, `CELLARRAY`, `Position`.
Rendering / visibility (verify addresses; these are from prior notes):
- `ConstructView` (~:433750 / :433827), `InitCell` (~:432896),
`CObjCell::find_visible_child_cell` (~:311397). Find the full PVS/PView traversal,
the EnvCell draw path, how exit portals / `seen_outside` feed render, terrain/sky gating.
## 4. Questions to answer (be comprehensive + cited)
### A. Cell membership & transitions (physics)
1. How does retail represent and store "the cell I'm in" (`curr_cell`)? When/where is it updated? Trace the full chain: per-step sweep → `find_cell_list``validate_transition` advance → `SetPositionInternal`/`change_cell`.
2. Exactly how does `find_cell_list` build the candidate cell array, and how does it pick the single containing cell (`*arg5`)? What is the `do_not_load_cells` prune — when is it set, what does it remove, and what stability does it buy?
3. **Precisely how does retail avoid cell flicker** at a doorway / indoor↔outdoor / room↔room? Is it directed portal-crossing, swept-path containment with accept-on-move, the prune, `point_in_cell` semantics, or a combination? What guarantees a blocked/standing-still step does NOT change the cell?
4. How does a player transition indoor→outdoor (exit) and outdoor→indoor (enter)? Between interior cells? What do `CCellPortal` vs `CBldPortal` do, and how does the exit portal / outdoor landcell get added and chosen?
5. Is the cell ARRAY (for collision) the same mechanism as `curr_cell` (membership), or two? How do they relate within one transition?
### B. Underground / dungeons
6. How are dungeons represented in the dats and at runtime — EnvCell graph, portals, the absence of terrain? How does this differ from building interiors (cottage/inn) which sit ON a landblock with terrain?
7. How does the player move through a dungeon: cell tracking, dungeon cell loading/streaming, and how the engine knows there is no sky/terrain to draw?
8. Is there an explicit "underground" flag/state (e.g. on `Position`/landblock/cell), or is "underground" simply "current cell is an EnvCell with no outdoor reachability"? Cite the flag/field if it exists.
### C. Rendering inside and outside (the seamless seal)
9. Trace retail's render visibility: how does it build the visible set in ONE pass (`ConstructView`/`InitCell`/`find_visible_child_cell`/PVS/PView)? What does it output (visible cell list, per-portal clip regions/frustum)?
10. How does retail draw the OUTSIDE seen through a doorway/window from inside (sky, rain, terrain, exterior buildings) so there is **no blue clear-color hole**? How are exit portals / `seen_outside` handled in the render traversal?
11. How does retail seal interiors — cap ceilings, prevent the outdoor world from bleeding in, and clip entities/particles to the visible cells?
12. How does retail decide to draw terrain + sky vs not, as a function of the current cell (indoor / outdoor / underground)?
13. **Is the render cell/visibility the SAME `curr_cell`/cell-graph as physics, or separate?** Trace whether render reads the physics `curr_cell` and traverses the shared cell graph, or maintains its own. (Central to acdream's decision.)
### D. Synthesis for acdream (be concrete)
14. Given acdream re-derives membership statically per tick (instead of tracking it through the sweep) and renders with a separate cell system, what is the **retail-faithful target architecture** we should port?
15. Specifically: should membership be advanced inside the transition sweep (port `validate_transition`'s `curr_cell` advance + the `do_not_load_cells` prune + drop the static `ResolveCellId`, reading `sphere_path.curr_cell` like `SetPositionInternal`)? Should the render obey the physics `curr_cell` + a single portal-visibility traversal? Justify from the decomp.
16. List the **must-port functions** (with decomp addresses), the integration order, the main risks (esp. anything touching the collision sweep where acdream has a history of bugs), and what conformance tests would prove faithfulness.
## 5. Method reminders
- Grep the named decomp by `Class::method` FIRST; confirm addresses in `symbols.json`.
- The decomp is ground truth; ACE/ACViewer/WorldBuilder are interpretation aids — when they
disagree, the decomp wins, but note the disagreement.
- Distinguish what you VERIFIED in source from what you INFER. Flag inferences.
- Cite: `function_name @ 0xADDR (pseudo_c:LINE)` or `repo/path/File.cs:LINE`.