acdream/docs/plans/2026-07-23-world-interaction-completion.md

176 lines
9.7 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.

# World interaction completion — pre-M4 program
**Status:** Slice 1 user-accepted 2026-07-23. Slice 2 Use-hand selection state
passed its connected gate. The follow-up zero-useability/AutoWear correction is
code-complete and awaits its connected 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`](../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.
## 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`](../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
- Port the magnifying-glass Assess command state and activation.
- Run the connected correction gate: activate Blackmoor's Favor, then try to
equip armor blocked by an overlapping worn item and verify the named system
message.
- Continue directly into the Assess information slice after the combined
status-control gate.