Commit graph

3 commits

Author SHA1 Message Date
Erik
b7dc91a053 feat(ui): D.2b item interaction + retail cursors + live character sheet
Lands the codex-worktree D.2b stream plus the extraction the 2026-07-02
UI architecture review mandated before commit:

- ItemInteractionController: single owner of double-click use/equip/
  container-open, targeted-use mode (health kits), drag-out drop;
  toolbar shortcut drags don't drop the real item. ItemEquipRules for
  multi-slot (coat) coverage via equip masks.
- Cursor phase: CursorFeedbackController (semantic priority chain:
  drag > resize > window-move > target-mode > text) + RetailCursorCatalog
  (enums 0x27/0x28/0x29, hotspot 14,14; ClientUISystem::UpdateCursorState
  0x00564630) resolved through the portal EnumIDMap chain by
  RetailCursorResolver; RetailCursorManager applies dat cursor art to the
  OS cursor. Register row AP-72 covers the OS standard-cursor fallback.
- Character window goes live: CharacterSheetProvider owns sheet assembly,
  XP-curve/raise-cost math and the raise flow — extracted out of
  GameWindow per Code Structure Rule 1 instead of committing the ~430-line
  feature body there. Optimistic XP/credit debits go through eventful
  store APIs (new ClientObjectTable.UpdateInt64Property +
  LocalPlayerState.DebitIntProperty/DebitInt64Property) instead of raw
  property-dictionary writes; register row AP-73 covers the still-missing
  raise ledger (#163).
- RetailWindowFrame: the shared nine-slice window mount recipe; the
  character window uses it, remaining windows migrate via #164.
- Status-bar buttons toggle inventory/character windows; retail row-major
  backpack ordering; WorldSession.SendUseWithTarget + raise/train sends.

GameWindow shrinks 14,214 -> 13,877 lines despite the new features; the
sheet/raise logic is unit-tested in CharacterSheetProviderTests instead
of trapped in the god object. Build green; full suite 3,286 tests pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 09:18:43 +02:00
Erik
db0cac03c4 fix(D.2b): PickupEvent removes the 3D render, not the weenie — unwield lands in the pack
PickupEvent (0xF74A) and DeleteObject (0xF747) are semantically distinct:
- 0xF747 = weenie DESTROYED → evict from ClientObjectTable (weenie_object_table)
- 0xF74A = object LEFT THE 3D WORLD VIEW (moved into a container) → remove
  the 3D WorldEntity, but the weenie persists in ClientObjectTable

Before this fix, both paths fired EntityDeleted identically, causing
ObjectTableWiring to evict the weenie from ClientObjectTable. The follow-up
InventoryPutObjInContainer (0x0022) then tried MoveItem on an unknown guid
and no-op'd, so the unwielded item simply vanished.

Fix: add `bool FromPickup` (default false) to DeleteObject.Parsed. WorldSession
sets it true on the PickupEvent path and false on the DeleteObject path.
ObjectTableWiring.Wire's EntityDeleted handler skips table.Remove when
FromPickup is true, preserving the weenie for the container-move echo.

GameWindow.OnLiveEntityDeleted (3D entity removal) is untouched — it fires
for both pickups and destroys, as intended.

Divergence register: AP-65 added (data ghosts for other-player pickups until
teleport/relog clear; harmless — no UI queries ContainerId 0).

Tests: +5 (DeleteObject FromPickup parser regression; wiring retain/evict
semantics; Parsed default/explicit FromPickup). 343/343 pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 23:39:27 +02:00
Erik
82f5968316 feat(D.5.4): ObjectTableWiring (CreateObject=upsert, Delete=evict, 0x02CE) off GameWindow
CreateObject ingestion moves to Core.Net; GameWindow drops the EnrichItem call +
inline 0x02CE handler. Fixes the Coldeve blank-icon root cause: items with no PD
stub are now created, not dropped.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 16:34:56 +02:00