acdream/docs/plans/2026-07-23-world-interaction-completion.md
Erik 6b1ae4fb76 feat(items): port retail shared cooldown overlays
Preserve public shared-cooldown metadata, resolve the authoritative cooldown enchantment with retail expiry semantics, and project the exact ten DAT-authored radial steps through the shared retained item-slot architecture.

Co-authored-by: Codex <codex@openai.com>
2026-07-23 10:41:16 +02:00

11 KiB
Raw Blame History

World interaction completion — pre-M4 program

Status: Slice 1 user-accepted 2026-07-23. Slice 2 Use-hand selection, zero-useability, carried direct-use, and AutoWear correction passed their connected gates. The requested shared item-cooldown follow-up is code-complete and awaits its visual gate before Assess continues. Milestone: M4 prerequisite/preamble. Architecture: retained gameplay UI over shared selection, object, and interaction state. GameWindow remains a composition/callback shell.

Outcome

Close the remaining retail interaction surfaces before the larger M4 quest/emote/character-creation bodies begin:

  1. Favorite spell bars expose their DAT-authored overflow arrows and scroll through every server-persisted favorite.
  2. The status bar's hand and magnifying-glass controls invoke the same Use and Assess commands as their keyboard paths.
  3. Assessing a creature, player, NPC, or object opens its retail information in the canonical shared main-panel host.
  4. World picking can resolve visible equipped children, such as a character's wielded weapon, while selection markers remain anchored to the picked child.
  5. Vendor use opens the authored vendor surface, publishes its inventory, and supports retail selection/browsing.
  6. Vendor buy/sell transactions, quantities, pending-state ownership, and authoritative inventory reconciliation complete the loop.

The program reuses the existing retained-window host, SelectionState, ClientObjectTable, interaction transaction owner, and server-authoritative inventory updates. It does not create parallel panel positions, item tables, selection state, or optimistic inventory outcomes.

Ordered slices

Slice Deliverable Principal owner
1 Spell-bar overflow arrows SpellcastingUiController + generic retained scrollbar/list
2 Status Use/Assess commands focused status-bar controller binding to the existing action router
3 Assessment information panel new retained panel controller mounted by the canonical main-panel host
4 Equipped-child world picking pure world-query/picking policy plus presentation anchor
5 Vendor browse lifecycle vendor session/controller plus authored panel
6 Vendor transactions server-authoritative buy/sell command and reconciliation owner

Each slice begins with named-retail research, produces pseudocode and conformance tests, updates the divergence register if required, and lands as a separate bisectable commit. A visual gate follows each UI-bearing slice.

Slice 1 now imports both 23-pixel arrow buttons, their rollover/pressed media, and HideDisabled from the real combat fixture; places their artwork by the authored leading/trailing positions; shares the list's single pixel-scroll model; advances one 32-pixel cell per press; and exposes only actively selected spells. Passive object/endowment refresh preserves a manual offset. The mixed 610/800-pixel DAT anchor chain is solved to a fixed 747-pixel combat root, producing exactly 18 visible 32-pixel favorite cells (nine numbered plus nine unnumbered) without consuming the overflow. The initial 57-test/full-suite gate passed; the corrective 104-test focus set, 3,472 App tests / 3 skips, Release solution build, and 7,845 complete-solution tests / 5 skips pass. The corrected 18-cell bar and both overflow directions passed the connected gate.

Slice 2 now drives the toolbar hand from canonical SelectionState changes and live ClientObjectTable updates. The pure Core predicate ports gmToolbarUI::HandleSelectionChanged: combat-use items, armor/clothing/jewelry, and ItemUses values without USEABLE_NO remain active. Clicking still enters the existing ItemInteractionController command path, so weapons use the server-confirmed AutoWield transaction and targeted tools enter the existing use-on-target cursor. Empty selection and explicitly unusable spell components are ghosted and ignore clicks. Empty-selection ghosting is the user's explicit choice over retail's generic TARGET_MODE_USE entry and is registered as AP-122. The 14-test Core interaction focus, 49-test toolbar focus, Release solution build, 3,474 App tests / 3 skips, and 7,848 complete-solution tests / 5 skips pass.

The connected selection-state gate passed. Its follow-up exposed three deeper shared-policy/delivery defects, now corrected from named retail plus the matching binary. ItemUses::IsUseable @ 0x004FCCC0 tests only USEABLE_NO; reset/absent value zero is usable. ItemHolder::UseObject @ 0x00588A80 sends a carried direct-use item immediately; acdream had incorrectly routed that packet through a world approach lookup, where Blackmoor's Favor has no spatial entity and was silently cancelled. Owned Use now bypasses that query while world-object Use retains the registered AD-27 approach seam. AutoWear applies CPlayerSystem::AutoWearIsLegal @ 0x0055EF40 through the same double-click and toolbar-hand path, resolves the overlapping worn object from the retail-ordered equipment projection, and emits the exact system line You must remove your <item> to wear that. Research: ../research/2026-07-23-retail-item-use-and-autowear-pseudocode.md. The focused 25-test Core and 85-test App sets pass, as do the warning-free Release solution build, 3,476 App tests / 3 skips, and 7,857 complete-solution tests / 5 skips. The carried-use delivery correction adds an end-to-end App pin from Favor activation through wire dispatch and authoritative UseDone busy release. The Release build retains the 17 tracked test-project warnings; 3,477 App tests / 3 skips and 7,858 complete-solution tests / 5 skips pass. The user confirmed Blackmoor's Favor now activates correctly.

Before Assess, the user requested the adjacent retail item-cooldown presentation. PublicWeenieDesc now preserves the optional shared cooldown id and duration, assessed property updates reach the same object fields, and CEnchantmentRegistry::OnCooldown @ 0x005943C0 plus UIElement_UIItem::UpdateCooldownDisplay @ 0x004E1E20 are ported through the canonical Core registry and one pure display projection. Every retained item list shares one controller and the exact ten DAT-authored radial sprites 0x060067CF..0x060067D8; items with the same group display the same server-authored cooldown. Research: ../research/2026-07-23-retail-item-cooldown-pseudocode.md. The focused parser/Core/UI/production-DAT tests, warning-free Release solution build, 3,482 App tests / 3 skips, and 7,875 complete-solution tests / 5 skips pass. AP-123 records only the retained toolkit's procedural-child adaptation; the visible assets, ordering, timing, and shared-group behavior are exact.

Slice 1 — spell-bar overflow arrows

Retail oracle

The authored Magic combat layout contains, inside every favorite-tab group:

  • horizontal scrollbar 0x100000B5, 685×36 at the 800-pixel design width;
  • decrement button 0x10000071, 23×36;
  • increment button 0x10000072, 23×36;
  • item list 0x100000B6, inset by 23 pixels on both sides and 32 pixels high.

The list references the scrollbar through UIElement_Scrollable property 0x71. The scrollbar references increment/decrement buttons through properties 0x77/0x78, and property 0x79 enables HideDisabled. There is no track or thumb in this specific control: the two authored arrows are the whole visible scrollbar.

Named retail references and executable pseudocode are recorded in ../research/2026-07-23-retail-spellbar-overflow-pseudocode.md.

Implementation plan

  1. Generalize DatWidgetFactory.BuildScrollbar so horizontal and vertical scrollbars both import the referenced decrement/increment children, including their authored positions, dimensions, Normal/rollover/pressed media, and HideDisabled property.
  2. Generalize UiScrollbar to use distinct authored decrement/increment extents for rendering, hit-testing, track geometry, and dragging. Preserve the existing 16-pixel default for layouts whose button children are absent.
  3. Reproduce retail disabled presentation: a model without overflow rejects pointer input; when HideDisabled is authored, the scrollbar also draws nothing and does not claim hit tests.
  4. In SpellcastingUiController, bind each group's scrollbar to its UiItemList.Scroll, enable horizontal scrolling, and retain one independent pixel offset per favorite tab.
  5. Match SpellCastSubMenu::SetSelected: selecting a spell through keyboard, shortcut, or code scrolls that item into view; passive state refresh does not re-expose it.
  6. Carry the combat root's mixed-parent raw-edge policies through the complete imported tree and solve for the retail 18-cell favorite viewport; keep that HUD capacity fixed across desktop resizes.
  7. Pin the importer, arrow hit extents, 32-pixel step, no-overflow behavior, controller binding, and selection exposure with focused App tests.
  8. Run the App Release suite, solution Release build, and complete Release suite. Then update the roadmap/memory and request the connected visual gate: place more favorites than fit, scroll both directions, change tabs, and verify arrows disappear on a non-overflowing tab.

Invariants

  • DAT supplies the controls and their artwork; no new spell-bar texture or overlay is invented.
  • The scrollbar and list share one UiScrollable; there is no second offset.
  • One arrow press moves one 32-pixel favorite cell, matching UIElement_ListBox::InqScrollDelta.
  • Hidden disabled arrows cannot intercept combat-page dragging or clicks.
  • Existing stack, combat-power, chat, inventory, spellbook, and external container scrollbars retain their current behavior.
  • No substantial feature body enters GameWindow.

Slice 2 — status Use/Assess commands

Use-hand implementation

  1. ItemInteractionPolicy.IsToolbarUseEnabled is the pure named-retail selection predicate.
  2. ItemInteractionController.IsToolbarUseEnabled adapts the selected live object without triggering a request or consuming the use throttle.
  3. ToolbarController subscribes to canonical SelectionState.Changed and selected-object add/update/remove notices, then sets the imported button's normal or ghosted state through UiButton.Enabled.
  4. Enabled clicks retain the one existing activation path: equipment enters AutoWieldController, ordinary use enters the normal Use request owner, and targeted items enter UseItemOnTarget, which already owns the retail target cursor.

Remaining Slice 2 work

  • Run the connected visual gate for the shared item cooldown: use Blackmoor's Favor or a summon/stone and verify its inventory and shortcut aliases count down together, then clear.
  • Port the magnifying-glass Assess command state and activation.
  • Continue directly into the Assess information slice after the combined status-control gate.