Commit graph

855 commits

Author SHA1 Message Date
Erik
c71b32f73d feat(D.2b): main-pack cell draws the constant backpack icon (AP-51)
The m_topContainer cell (0x100001C9) rendered blank (tex=0). Retail's
IconData::RenderIcons (0x0058d1ee) has an IsThePlayer() branch that draws a
CONSTANT backpack — m_idIcon = GetDIDByEnum(0x10000004, 7) = 0x060011F4,
m_itemType = TYPE_CONTAINER — NOT the player's body icon (the original AP-51
"equipped-pack weenie icon" premise was wrong). Compose that base over the
Container type-underlay via the existing _iconIds delegate. Verified vs decomp
(407546-407549) + IconComposer.GetIcon (base=arg2, type drives underlay) + a
live dat dump (map 0x25000008 index 7 = 0x060011F4). Test locks type+literal.
AP-51 reworded to the residual hardcoded-vs-runtime-resolve nuance (cf. AP-55).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 15:04:08 +02:00
Erik
895d8eed80 docs(D.2b): divergence register — retire AP-56, reword AP-53, add overlay/selection rows
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 14:33:53 +02:00
Erik
24a0b84162 docs(D.2b): container-switching implementation plan
8-task TDD plan: ReplaceContents (Core) -> ViewContents full-replace
wiring (Core.Net) -> SendUse wrapper -> UiItemSlot triangle/square
overlays -> InventoryController _openContainer/_selectedItem +
click roles + indicators -> GameWindow callback wiring -> divergence
register -> full-suite + visual gate. Exact code per step.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 14:22:39 +02:00
Erik
bfdab5b9e0 docs(D.2b): container-switching design spec
Brainstorm output for inventory container-switching. Key decision from
the brainstorm: the handoff conflated two orthogonal retail mechanisms —
the open-container TRIANGLE (0x06005D9C, UpdateOpenContainerIndicator) and
the selected-item SQUARE (0x06004D21, ItemList_SetSelectedItem). User
confirmed both are real (a bag is just an item, so it gets the square too)
and chose to ship both this phase, the square uniform across grid+bags and
visual-only (no selected-object-bar wiring yet).

Pins the ViewContents full-REPLACE (ACE writes entries OrderBy
PlacementPosition, no slot field -> ContainerSlot=index), the Use 0x0036 ->
ViewContents 0x0196 round-trip, and the 6 touched files.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 14:15:42 +02:00
Erik
3ce1fae332 feat(teleport C): TAS-driven fade transit + retire TeleportArrivalController
Wires the dormant TeleportAnimSequencer as the transit driver: on PlayerTeleport
the player holds in PortalSpace behind a full-screen fade (FadeOverlay) until the
destination terrain is resident (TeleportWorldReady, gated on the priority-applied
landblock), then materializes (Place), and after the world fades back in regains
control + acks the server (FireLoginComplete). No movement resolves against the
empty world, so the outbound cell frame can't corrupt. Outdoor changes from
place-immediately back to hold-until-resident (now fast, not a band-aid).

- FadeOverlay: fullscreen NDC black quad, alpha = ShowTunnel ? 1 : FadeAlpha.
- Retires TeleportArrivalController + its 2 tests (TAS subsumes the driver role).
- Divergence register: AD-2 updated to the new mechanism; AD-31 (fade vs swirl).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 14:03:25 +02:00
Erik
02f4be72c0 fix(teleport D): cell-march preserves seed landblock id when no resident LB (no more lbX=0 outbound)
BuildCellSetAndPickContaining discarded the bool from TryGetTerrainOrigin — when
the current landblock's terrain hadn't been applied yet (priority-apply in flight
after a teleport or dungeon exit), blockOrigin was silently set to (0,0,0). The
AdjustToOutside/GetOutsideLcoord math treated world-frame sphere coordinates as
block-local and marched the cell one landblock per tick in the direction of movement
until lbX or lbY underflowed to 0x00. ACE rejected every subsequent move as a
failed transition.

Fix: honor the bool return. When terrain is unregistered for an OUTDOOR seed
(low < 0x0100), return currentCellId verbatim — "no block-local frame →
preserve". This mirrors the NO-LANDBLOCK verbatim contract in PhysicsEngine.Resolve
and is correct: the cell stays last-known-correct until terrain registers.
Indoor seeds are explicitly excluded (blockOrigin is never consumed by the indoor
pick path; outdoorPickAllowed=false for indoor seeds).

Reproduce + verify via CellMarchLandblockPreservationTests (two new FAILING-before
tests: WestEdge and SouthEdge with empty cache, no anchor → lbX/lbY preserved).
TeleportFarTownRunawayTests updated: no-anchor path now also preserves (pre-fix it
marched south to 0x59; post-fix returns currentCell unchanged).
CellTransitFindCellSetTests, Issue112MembershipTests, PhysicsEngineTests: added
RegisterTerrain for the streaming-center block (in production it is always resident
before outdoor resolves run; tests that used blockOrigin=(0,0,0) as an implicit
fallback now register the block explicitly). All 1567 tests pass.

Divergence AD-30 added to retail-divergence-register.md.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-22 13:38:48 +02:00
Erik
ad27d1395a docs(D.2b): container-switching handoff + flag stale M1.5 banner
Banks the next clean inventory win (container-switching: Use 0x0036 -> ViewContents
0x0196 full-replace + the selected-container indicator that retires AP-56) as a
handoff for a fresh session, per the late-session-handoff lesson. Wire layer
already exists; design sketch + open questions captured.

Also flags the CLAUDE.md "Current state" banner as stale — it still tracks M1.5
(indoor world, 2026-06-14) while the active stream has been the D.2b retail-UI
track for weeks. Left the milestone reframing for a fresh reconciliation against
the milestones doc rather than a tired guess.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 13:17:55 +02:00
Erik
b869128df3 docs(teleport): TDD implementation plan — priority residency + fade cover
6 tasks: (1) StreamingController priority-apply, (2) PhysicsEngine residency
query, (3) TAS drives transit + outdoor hold-until-resident (retire the
TeleportArrivalController driver), (4) fullscreen fade overlay, (5) cell-march
lbX hardening, (6) verify + strip the tp-probe. Reuses the dormant
TeleportAnimSequencer as the transit driver; worldReady gates on the
priority-applied destination landblock.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 12:51:07 +02:00
Erik
1f8dd7a93f docs: D.2b empty-slot art — inventory portion visually confirmed
User confirmed the container column reads correct after the inheritance-resolved
fix (22d9231). Status: inventory empty-slot art FIXED + VISUALLY CONFIRMED.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 12:49:29 +02:00
Erik
22d92315e5 fix(ui): D.2b empty-slot art — container cells use inner slot bg, not the selected-container triangle
Visual gate (2026-06-22) showed the side-bag/main-pack empty cells drawing yellow
triangles. Root cause: the frame-first heuristic grabbed the 36x36 container
prototype's DirectState child 0x06005D9C — which is the open/SELECTED-container
triangle indicator, NOT a background frame — and stamped it onto every empty cell.

Fix: drop frame-first; FindIconEmpty resolves the inner m_elem_Icon ItemSlot_Empty
THROUGH BaseElement inheritance (0x1000033F -> 0x10000340 -> base 0x1000033E ->
0x1000033B -> 0x06000F6E), so containers get the dark slot background matching the
inventory. Pin test now asserts exact ids (0x06004D20 contents / 0x06000F6E
containers) — the old structural-only assertion let the wrong-but-valid 0x06005D9C
pass (Opus review Minor 2, now vindicated). The triangle + green/yellow selection
square is deferred to container-switching (AP-56 reworded). Full suite green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 12:45:36 +02:00
Erik
31602c6a24 docs(teleport): spec — retail residency + fade-cover (refutes _datLock starvation)
Live tp-probe capture refuted the handoff's _datLock-starvation hypothesis:
worker BUILD is fast + uncontended (waited=0ms); the 10-14s 'long transition'
is render-thread APPLY latency, and 'dropped at wrong position' is the
per-frame resolve corrupting the outbound cell frame (lbX zeroed) while the
player sits on an empty world. Design: priority-apply the player's dest
landblock + hold-until-resident behind a retail fade cover (reuse the dormant
TeleportAnimSequencer) + cell-march landblock-id hardening. Foundation-first,
not a hold over slow streaming. Flood-timeslicing + 3D swirl deferred.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 11:18:16 +02:00
Erik
499485a672 docs: D.2b empty-slot art — close inventory portion of the empty-slot-art issue
Inventory contents grid / side-bag / main-pack empty cells now dat-resolve their
art via ItemListCellTemplate (0x1000000e -> 0x21000037). Paperdoll equip
silhouettes + main-pack icon remain open (Sub-phase C / AP-51).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 11:10:39 +02:00
Erik
edbe5bafd5 feat(ui): D.2b empty-slot art — GameWindow resolves + wires per-list empty sprites
Inventory contents grid / side-bag / main-pack cells now render the retail
pack-slot empty art (dat cell template via ItemListCellTemplate) instead of the
generic toolbar square. Divergence rows AP-55 (toolbar still hardcoded) + AP-56
(flat single-sprite cell vs retail''s layered prototype).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 11:07:58 +02:00
Erik
69ad2cf12d docs(D.2b): implementation plan — inventory empty-slot art via cell-template resolution
5-task TDD plan for the OPEN empty-slot-art issue: ItemListCellTemplate resolver
(ports UIElement_ItemList::InternalCreateItem's 0x1000000e -> 0x21000037 lookup),
UiItemList.CellEmptySprite, InventoryController + GameWindow wiring, divergence
rows AP-55/AP-56, ISSUES close + visual gate. Each task is test-first with exact
code; Task 1 pins the exact retail sprites from the live dat.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 10:40:51 +02:00
Erik
660bcc2fcf docs(D.2b): design — faithful inventory empty-slot art via cell-template resolution
Brainstorm + decomp investigation for the OPEN issue "Inventory + equipment
slots show the wrong empty-slot background art". Verified against the named
decomp that retail's UIElement_ItemList::InternalCreateItem (004e3570) sources
each list's empty-cell sprite from attribute 0x1000000e on the list's own
ElementDesc -> catalog LayoutDesc 0x21000037 -> the prototype's ItemSlot_Empty
(0x1000001c) media. acdream hardcodes 0x060074CF (the generic toolbar square),
bypassing the per-list cell-template inheritance entirely.

Design ports the resolver: a new ItemListCellTemplate.ResolveEmptySprite helper
(mirrors the existing GameWindow 0x21000037 digit-array read), a
UiItemList.CellEmptySprite property, and InventoryController wiring for the
contents grid / side-bag / main-pack lists. Toolbar untouched (its 0x060074CF
is correct); paperdoll silhouettes stay Sub-phase C.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 10:31:38 +02:00
Erik
f8cda7e86d docs(teleport): foundation-investigation handoff for a fresh-look session
Captures the full teleport-flow journey (Slice 1 kept, Slice 2 hold built +
reverted), the root-cause findings (the IsLandblockLoaded key bug + the real
foundation problem: destination doesn't stream fast/complete during teleport,
likely _datLock starvation from the CreateObject flood), the open foundation
issues (#138 slow/incomplete streaming, lost-collision-after-teleport, FPS
leak Work-C, the PortalSpace freeze-vs-run-through question), and the clean
baseline (dd2eb8b). Written as evidence + open questions, not conclusions, so
a fresh session can re-examine the whole approach.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 10:00:23 +02:00
Erik
e6c8b65f77 docs(D.2b): inventory finish Stage 1 — handoff + divergence rows + ISSUES
Handoff doc for the fresh session (scroll/frame/resize/102-slots shipped +
visually confirmed; next = wrong empty-slot art, then main-pack icon, then
Sub-phase C paperdoll). Divergence AP-52 (side-bag 7 fallback), AP-53 (main-pack
102 fallback), AP-54 (resize approximation). ISSUES: closed the contents-grid
overflow; filed the wrong empty-slot-background issue; Stage 1 SHIPPED entry.
(Memory digest project_d2b_retail_ui.md + MEMORY.md + the don't-kill-clients
feedback updated out-of-tree.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 21:31:24 +02:00
Erik
7aba6d235c docs(D.2b): inventory window finish (Stage 1) implementation plan
6 tasks: (1) UiItemList clip+scroll via the shared UiScrollable + whole-row
clip; (2) mouse-wheel; (3) InventoryController binds the gutter scrollbar
0x100001C7 like ChatWindowController; (4) side-bag column 36px pitch + empty-
slot padding to capacity; (5) backdrop coverage — screenshot-gated, primary
fix is the clip; (6) verify + bookkeeping. TDD on the pure/controller logic
(internals test-visible); backdrop is the visual gate. Grounded in the dat
dumps + the existing scroll/scrollbar machinery.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 20:15:48 +02:00
Erik
0fbb76b2e7 docs(D.2b): inventory window finish (Stage 1) spec
Stage 1 of the "full retail inventory" arc (Stage 2 = paperdoll, separate
spec). Grounded in the dat dumps: contents grid 0x100001C6 is 192x96 (6x3) +
scrollbar 0x100001C7 (16x96); backdrop 0x100001D0 is full-window 300x362
(so the "torn" look is the unclipped grid overflowing below the frame —
fixed by clipping); side-bag column 0x100001CA is 36x252 (7 slots).
Components: (A) UiItemList clip+scroll reusing UiScrollable + bind the gutter
scrollbar like ChatWindowController; (B) backdrop coverage — primary fix is A,
residual gated on the post-A screenshot; (C) side-bag column pads empty slots
up to capacity at 36px pitch. Paperdoll + B-Drag out of scope.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 20:07:10 +02:00
Erik
2c8cd887e5 docs(slice-1): retail-divergence-register — TAS smoothstep approximation row (spec §5 row 2)
AP-49: TeleportAnimSequencer.ComputeFadeAlpha uses smoothstep in place of
retail's unrecovered 1024-entry GetAnimLevel lookup table
(gmSmartBoxUI::UseTime 0x004d6e30). Retire when the table contents are
extracted via cdb (spec §8).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 20:03:03 +02:00
Erik
82462ff153 docs(teleport-flow): implementation plan — 22 TDD tasks across 5 slices (Work item B)
Bite-sized TDD plan for the retail teleport flow. Slice 1: pure
TeleportAnimSequencer (7-state TAS) + golden-timing tests. Slice 2: the
#145 readiness-gate fix (IsLandblockLoaded + Decide outdoorReady axis +
apparatus probe) — ships independently of the visuals. Slice 3:
TeleportFlowController (delegate-injected, unit-tested) + TeleportFadeOverlay
+ portal wiring (PlaceTeleportArrival split: place vs InWorld so the input
lock persists the whole animation). Slice 4: one yaw-freeze + portal sounds
via the EnumIDMap chain. Slice 5: de-dup login readiness onto Decide, route
login/death through the controller, logout (Shift+Esc) + 0xF653 + disconnect,
remove dead _teleportArrival plumbing. Slice 6 (literal 3D swirl) is a
follow-up plan gated on a cdb asset trace.

Drafted via two research + drafting workflows; slices 3-5 redrafted as one
cohesive unit against a pinned controller API after the first parallel pass
produced cross-slice inconsistencies (missing controller task, triplicated
yaw-freeze, a fabricated PlayerMovementController.Update signature). All
load-bearing signatures personally verified against the tree.

Plan: docs/superpowers/plans/2026-06-21-retail-teleport-flow.md

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 19:42:28 +02:00
Erik
7c006d103a docs(D.2b-B): B-Wire shipped — AP-48/AP-49 fallback-only + ISSUES entry
Reword AP-48 (burden) and AP-49 (carry-aug) to fallback/default-only: B-Wire
ports the retail wire read (login PD-bundle UpsertProperties + live 0x02CD;
ObjectTableWiring applies all ints), so SumCarriedBurden / aug=0 are now
defensive only. Confirm the server sends EncumbranceVal/0xE6 at the visual
gate, then delete the rows. Add the D.2b-B B-Wire SHIPPED entry to ISSUES.
(Memory digest project_d2b_retail_ui.md + MEMORY.md updated out-of-tree.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 19:24:25 +02:00
Erik
aab9ddaf6b docs(teleport-flow): design — unified retail TeleportAnimState flow (Work item B keystone)
Brainstormed + decomp-researched the retail teleport flow, the keystone of
the #138/#145 teleport cluster. Five oracle calls locked: full retail TAS;
hold-until-landblock-loaded readiness (folds in the #145 residual / Work
item A); unify all four entry points (login/logout/death/portal); build the
literal portal swirl this pass; logout = animation + 0xF653 + disconnect
(char-select UI deferred).

Grounded in a verified 5-agent decomp pass (workflow wf_f0c07c93-7aa): the
7-state TeleportAnimState machine + golden constants (FADE=1s, MIN/MAX
CONTINUE=2/5s, FPS=40), the teleport_in_progress hold gate, per-entry start
states, sounds, input lock, exit (LoginComplete 0xA1). Load-bearing acdream
facts re-verified personally: AddLandblock atomicity, streaming-progresses-
during-hold, outdoor place-immediately.

Spec: docs/superpowers/specs/2026-06-21-retail-teleport-flow-design.md

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 19:03:10 +02:00
Erik
ca94d1d2f6 docs(D.2b-B): B-Wire implementation plan
16 bite-sized TDD tasks for the inventory wire pass: ClientObjectTable
UpsertProperties/UpdateStackSize (Core); PrivateUpdatePropertyInt 0x02CD,
SetStackSize 0x0197, InventoryRemoveObject 0x0024 parsers; ParseViewContents
0x0196; 0x0022 4th-field + 0x00A0 error fixes; DropItem/GetAndWieldItem/
NoLongerViewingContents builders + Send wrappers; PD player-property delivery
+ GameEvent registration; WorldSession dispatch; ObjectTableWiring apply-all-
ints + player-int route + stack/remove; GameWindow + InventoryController
wiring; divergence/ISSUES/roadmap/memory bookkeeping. Each pure parser/builder
/table-method is TDD'd; glue follows the codebase convention (dispatcher-
routed GameEvents unit-tested via the GameEventWiringTests harness, top-level
GameMessages build+run-verified). Plan derived from reading every call site.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 18:41:43 +02:00
Erik
5737951a19 docs(D.2b-B): B-Wire spec — inventory wire layer
Design/spec for B-Wire (follows B-Controller c38f098). Root cause found:
the burden binding already reads wire EncumbranceVal (PropertyInt 5) but
the value never arrives — login PD parses the player's int table then
drops it, the live 0x02CD (PrivateUpdatePropertyInt, no guid) is unparsed,
and ObjectTableWiring gates all non-UiEffects ints out.

Scope (user chose the full wire pass): player-property delivery (login PD
bundle upsert + 0x02CD parse + loosen the apply gate, retiring AP-48/AP-49),
latent-bug fixes (0x0022 4th field, 0x00A0 error), new C→S builders
(DropItem 0x001B / GetAndWieldItem 0x001A / NoLongerViewingContents 0x0195),
new S→C parsers (ViewContents 0x0196 GameEvent; SetStackSize 0x0197 +
InventoryRemoveObject 0x0024 GameMessages), and WireAll registration.
Opcodes pinned against ACE GameMessageOpcode.cs / GameActionType.cs; every
format requires grep-named → cross-ref → pseudocode → port + conformance.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 18:25:58 +02:00
Erik
adaec1845f docs: handoff — fold in retail teleport-anim (TAS) flow + acdream gap analysis for the teleport-flow feature
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 18:24:08 +02:00
Erik
59b4868408 docs: handoff — teleport issues cluster (#145 residual + retail teleport flow + FPS leak)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 18:11:37 +02:00
Erik
1230c07af3 docs(handoff): D.2b B-Controller shipped + visually confirmed; B-Wire next
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 18:08:05 +02:00
Erik
b7ca33118b docs(#145): REOPEN — cascade recurs on unstreamed-arrival-near-edge (streamed case fixed)
3rd live session found the carried-anchor fix is incomplete: the cascade recurs
when a teleport arrives onto a NOT-YET-STREAMED landblock near an edge (0xC98C
arrival at local Y=190.3, NO-LANDBLOCK -> marches 0x8C->0xFE, wire localY=-21684,
ACE rejects). Streamed-arrival case IS fixed (verified ~10 landblocks). Same root
as the Z free-fall (#135/#138 placed-but-unstreamed gap). Prior 'gate passed' was
premature. Needs apparatus (anchor/guard diagnostic at the crossing) before a fix.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 18:04:54 +02:00
Erik
c38f098ec0 docs(D.2b-B): B-Controller visually confirmed; #145 continuation + overflow follow-up
- #145: the original fix was incomplete (mounted panels inherited ZLevel 1000 →
  behind the backdrop → washed out); continuation 417b137 closes it. Updated status.
- D.2b-B SHIPPED entry: visually confirmed; the two render bugs (backdrop wash-out
  + captions host-direct) documented.
- New OPEN issue: contents grid overflows (needs the gutter scrollbar wired; scroll
  was out-of-scope for B-Controller per the spec).
- Roadmap ledger: B-Controller visually confirmed + the render fix noted.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 18:01:38 +02:00
Erik
b0cbc09ba0 docs(#145): cascade FIXED + user-gate passed — far-town round-trip works (Slices 1-3+7)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 17:30:11 +02:00
Erik
865aae8876 docs(#145): Slice 3 — capture cellId/anchor-consistency + indoor-staleness edge cases + sweep threading map
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 11:08:13 +02:00
Erik
9e184eb861 docs(#145): Slice 3 design — carried-anchor (body.Position - CellPosition.Origin) replaces TryGetTerrainOrigin
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 11:05:54 +02:00
Erik
7cae03951e docs(#145): plan — split Slice 2 into 2a (Core, shipped) + fold 2b seeding into Slice 3
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 10:57:04 +02:00
Erik
c980763322 refactor(physics #145): Slice 1 — rename Frame->CellFrame to avoid DatReaderWriter.Types.Frame collision
The new value type collided with DatReaderWriter.Types.Frame (used in
physics-adjacent code like ShadowShapeBuilder), which the structural fix
(per-file using-aliases across 6 files) would have re-incurred in every
later physics slice. Renamed the TYPE to CellFrame; the Position.Frame
MEMBER keeps retail's name. Restored the 5 alias-only files to their
pre-Slice-1 state; synced spec + plan. Core 1522 passed / 0 failed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 10:34:36 +02:00
Erik
ed32db70d7 docs(#145): implementation plan — cell-relative physics frame (7 slices)
Bite-sized, TDD-structured 7-slice plan for subagent-driven execution:
(1) Position/Frame types + GetBlockOffset, (2) PhysicsBody carries
Position, (3) membership via AdjustToOutside — closes the cascade,
(4) inter-tick collision state cell-relative + validate_transition
lockstep, (5) wire off Position, (6) _liveCenter render-only, (7)
teleport velocity-idle. Conformance via transform-on-read.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 10:23:37 +02:00
Erik
fd1f86b771 docs(#145): Option B design spec — cell-relative physics Position frame
Approved design for the cell-relative physics frame port: retire
_liveCenter from physics (render-only), carry Position{ObjCellId,
Frame{local in [0,192), quat}} as the source of truth, port
get_block_offset (verified 0x0043e630 nets to delta-landblock * 192 m),
translate inter-tick collision state per-cell like retail (0x0050a592).
7-slice parallel-frame migration; conformance via transform-on-read (no
fixture re-capture).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 10:18:18 +02:00
Erik
67f98e8e72 docs(#145): verified root cause + decision to port cell-relative physics frame (Option B) + handoff
Workflow wf_87607d15-c43 (4 research streams + synthesis + 3 adversarial verifiers, HIGH confidence) confirmed the far-town runaway is a cell-membership label cascade from a discarded TryGetTerrainOrigin bool (CellTransit.cs:736 -> (0,0) origin for unstreamed neighbors), not a free-fall; 17410 is a wire artifact. User chose the architectural fix: port retail's cell-relative Position + retire _liveCenter from physics. Handoff doc carries the verified mechanism, the retail port table (decomp addresses), acdream divergence sites, apparatus (desync-capture.jsonl + probes + harness template), and the brainstorming-gate requirement.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 09:47:01 +02:00
Erik
1ccf07b705 fix(ui): D.2b-B — address phase-boundary review (burden % saturation + equipped filter)
Opus phase-boundary review findings:
- I1 (faithfulness): LoadToPercent now computes from the CLAMPED fill
  (floor(LoadToFill(load)*300)), so the burden % SATURATES at 300% like retail
  (decomp 176544-176576 clamps arg2 to [0,1] BEFORE the *300). The old
  floor(load*100) over-read to 400% at 4x capacity. Golden test corrected.
- I2 (partition): exclude equipped items (CurrentlyEquippedLocation != None) from
  the contents grid + selector — a mid-session self-wield routes them through
  MoveItem(item, WielderGuid=player) into GetContents(player); retail's gm3DItemsUI
  shows pack contents only. New conformance test.
- N1: drop dead 'using System.Collections.Generic' (left after the 383e8b7 cleanup).
- N3: AP-48 risk wording (drift can be low OR high vs server EncumbranceVal).

Build green; BurdenMath 17, InventoryController/UiMeter 10 tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 09:28:29 +02:00
Erik
7bb4bd3ae8 docs(D.2b-B): divergence rows + issues/roadmap for inventory population
AP-48: client-side SumCarriedBurden fallback (EncumbranceVal not yet wired).
AP-49: aug capacity (PropertyInt 0xE6) not tracked → un-augmented Str×150.
AP-50: meter direction from geometry (m_eDirection/0x6f not read from LayoutDesc).
AP-51: main-pack cell placeholder icon (equip-pack DID deferred to Sub-phase C).

AP count: 43 → 47 rows. ISSUES: D.2b-B closed entry + remaining B-Wire/B-Drag/C
gaps noted. Roadmap: D.5 sub-phase ledger updated to reflect B-Controller shipped.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 09:15:05 +02:00
Erik
a4f0b51894 docs(issues): reopen #145 — far-town teleport resolver runaway (residual of the source-drop fix)
Captured: teleport to far town (201,91) places correctly via the #145 verbatim path, then the per-frame resolve marches membership one landblock south/frame (un-rebased local position) until ACE rejects the inconsistent (cell, local) pair. #138 re-hydrate exonerated. Root cause under multi-agent research (acdream code + retail decomp oracle + capture).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 08:54:05 +02:00
Erik
5b3295ad13 docs(D.2b-B): InventoryController implementation plan (8 tasks, TDD)
Bite-sized TDD tasks: BurdenMath encumbrance ports (T1), SumCarriedBurden
fallback (T2), UiMeter vertical fill (T3), InventoryController bind+populate
(T4) + burden+captions (T5), GameWindow wiring (T6), divergence/docs (T7),
visual gate (T8). Self-reviewed against the spec; test helpers verified
against the shipped widget APIs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 08:50:21 +02:00
Erik
0e273ff2ac docs(D.2b-B): InventoryController (B-Controller) design spec
Read-only population of the gmInventoryUI tree: bind 0x21000023 by id,
fill the 'Contents of Backpack' grid + the right-strip pack-selector from
ClientObjectTable, drive the vertical burden meter, render the Type-0
captions. Faithful selector + full faithful burden meter (per brainstorm).

Ported AC algorithms with decomp anchors: CACQualities::InqLoad (0x0058f130),
EncumbranceSystem::EncumbranceCapacity (0x004fcc00) / Load (0x004fcc40),
gmBackpackUI::SetLoadLevel (0x004a6ea0), UIElement_Meter::DrawChildren
(0x0046fbd0) / Initialize (0x0046f7b0, m_eDirection from property 0x6f).
Key finding: BuildMeter's single-image sprite assignment is already correct
(meter-own=track, child=fill); only vertical fill is new.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 08:31:21 +02:00
Erik
b07825cd24 docs(research): #138 handoff — RESOLVED banner + correct the re-hydrate source (not ClientObjectTable)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 08:09:36 +02:00
Erik
aa4a04d28e docs(issues): #138 fix shipped — re-delivery confirmed; handoff source-table corrected
Records the confirmed root cause (ACE never clears KnownObjects on a
teleport so it won't re-send known objects; retail/holtburger keep the
client object table and re-render from it) and the two-part fix
(re-hydrate from _lastSpawnByGuid; pending-bucket persistent rescue).

Corrects the 2026-06-21 handoff: ClientObjectTable is the inventory data
model with no world position/Setup and cannot rebuild a render entity;
the real retained world-object table is GameWindow._lastSpawnByGuid.
Status: FIX SHIPPED, pending user visual gate.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 08:07:42 +02:00
Erik
bf66fb4123 fix(streaming): #138 — re-hydrate server objects from the retained spawn table on reload
Doors/NPCs/portals vanished after a portal OUT of the 0x0007 dungeon back
to Holtburg. Root cause confirmed via ACE + holtburger cross-reference:
the dungeon collapse drops a landblock's render entities for FPS, and ACE
will NOT re-broadcast objects whose guid is still in its per-player
KnownObjects set (never cleared on a normal teleport — ACE relies on the
client retaining its object table and culling stale objects itself). So
nothing restored them on the way back.

Retail-faithful fix: a real client keeps its weenie_object_table and
re-renders the world from it (holtburger keeps the table across a
teleport; only suspends physics bodies). acdream's _lastSpawnByGuid (the
parsed CreateObject records — position + Setup + appearance) IS that
table and survives the collapse (the collapse path never calls
RemoveLiveEntityByServerGuid, the only thing that prunes it). On landblock
(re)load, replay OnLiveEntitySpawnedLocked for retained spawns whose
render entity is absent — independent of any ACE re-send.

- LandblockEntityRehydrator: pure selection (landblock match; skip
  already-present, the player, and mesh-less spawns), unit-tested (7).
- StreamingController: onLandblockLoaded callback after AddLandblock
  (Loaded = dungeon-exit expand) and AddEntitiesToExistingLandblock
  (Promoted = Far->Near).
- GameWindow.RehydrateServerEntitiesForLandblock: present-gate keys on
  GpuWorldState (NOT _entitiesByServerGuid, which holds collapse
  orphans), replay under _datLock; the replay's own
  RemoveLiveEntityByServerGuid de-dup scrubs the orphan state.

Corrects the handoff: ClientObjectTable is inventory-only (no world
position/Setup) and cannot rebuild a render entity; _lastSpawnByGuid is
the world-object table. Register row AP-48 (no retail 25s visibility
cull). dotnet build + 1518 Core tests green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 08:06:41 +02:00
Erik
4e23a7b9bc docs(handoff): D.2b A+B-Grid shipped, #145 fixed, B-Controller next
Session handoff: window manager (F12) + inventory sub-window mount
(inheritance-children) + UiItemList grid all shipped; #145 ZLevel z-order
fixed (backdrop behind panels). B-Controller next (populate grids + burden
meter from ClientObjectTable). Key discoveries, B-Controller readiness, and
the new-session prompt captured.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 07:52:40 +02:00
Erik
4904ff4e21 docs(issues): #145 DONE — ZLevel fix renders the inventory panels
Occlusion fixed (45a5cc5); paperdoll equip-slot positions visually
confirmed. Remaining gaps are next-sub-step content/art, not occlusion:
backpack/3D-items population → B-Controller; per-slot paperdoll
silhouettes → Sub-phase C.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 07:36:55 +02:00
Erik
b9445f53fe docs: handoff for #138 (entity re-delivery after teleport) — next session
Full orientation for a fresh session to fix #138: confirmed root (server
objects unloaded on teleport-IN, not restored on return; ACE re-broadcast
unreliable), DO-NOT-RETRY table (cache + render-cull eliminated), the
ClientObjectTable re-hydrate fix direction with file:line pointers, the
launch/probe/account setup, and the gotchas (re-broadcast latency, stale
sessions, don't-kill-clients, entity.Id != ServerGuid).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 07:29:00 +02:00
Erik
c0b2cf2f7b docs(issues): #138 re-scoped — it's entity RE-DELIVERY across a teleport, not render-cull/cache
Deep dive (this session) eliminated the 2026-06-20 hypotheses for #138
(server objects + own avatar not showing after a teleport-out):
- NOT the Tier-1 classification cache: re-created live entities get a fresh
  monotonic Id (_liveEntityIdCounter++), so the cache (keyed on Id) is always
  a miss for them. (Side-finding: the cache has a real demote-vs-unload
  invalidation asymmetry — RemoveLandblock doesn't fire _onLandblockUnloaded
  while RemoveEntitiesFromLandblock does — but it's NOT the #138 cause.)
- The render path is fine when entities are present (login: [dyn] dyn=54
  drawn=33; the dynamics partition + DrawDynamicsLast draw them).
- The actual cause: re-delivery is unreliable. notan/+Je walk-around run after
  teleport-out: live:spawn doors=0, [ent]+ door appends=0, [ent-flat] server=1
  — the server delivered ZERO Holtburg objects on return; they never reach
  acdream. acdream unloads them on teleport-IN (the collapse) and nothing
  restores them; ACE doesn't reliably re-broadcast. "Other clients see +Je"
  confirms it's acdream's local world, not server state.

Fix direction (next session): re-hydrate GpuWorldState from the retained
ClientObjectTable on AddLandblock instead of depending on an ACE re-broadcast
(or treat in-range server objects as persistent across the collapse). Entity-
lifecycle/protocol change, best started fresh.

Diagnostic scaffolding (probes + the unrelated cache-asymmetry fix) reverted;
tree is back at the green #145 state (a15bd3b).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 07:24:01 +02:00