Preserve retail's one-pending-appraisal busy lifetime, parse the complete gated response, and mount the authored examination layout in the shared main-panel host. Keep known 3D preview and inscription-write gaps explicit in AP-110.
243 lines
14 KiB
Markdown
243 lines
14 KiB
Markdown
# World interaction completion — pre-M4 program
|
||
|
||
**Status:** Slice 1 user-accepted 2026-07-23. Slice 2 Use-hand selection,
|
||
zero-useability, carried direct-use, AutoWear correction, and the requested
|
||
shared item-cooldown follow-up passed their connected gates. The existing
|
||
retail Assess command has been reconciled and Slice 3's examination panel is
|
||
code-complete; its connected visual gate is next.
|
||
**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.
|
||
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`](../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.
|
||
The user accepted the live cooldown presentation 2026-07-23.
|
||
|
||
Slice 3 preserves the existing toolbar/keyboard Assess command and ports the
|
||
retail response owner around it. One shared UI-busy reference covers the
|
||
latest pending GUID, stale replies are rejected, the accepted reply becomes
|
||
the current appraisal, and combat-time creature/player refreshes occur every
|
||
0.75 seconds only while the examination window is visible. Complete
|
||
`IdentifyObjectResponse` parsing now includes the three-word HookProfile and
|
||
rejects truncated gated payloads rather than continuing from a corrupt cursor.
|
||
LayoutDesc `0x2100006B`, root `0x100005F2`, supplies the complete 310 x 400
|
||
floaty chrome, title, item/creature/character subviews, close control, and
|
||
authored scrollbars. The panel participates in the canonical one-active
|
||
main-panel geometry/lifecycle rather than owning a parallel location. Basic
|
||
item, creature, and player reports are live; exact 3D preview/specialized
|
||
detail regions, conditional inscription editing, and selection-follow remain
|
||
the narrowed AP-110 residual. Research:
|
||
[`../research/2026-07-23-retail-appraisal-ui-pseudocode.md`](../research/2026-07-23-retail-appraisal-ui-pseudocode.md).
|
||
The focused parser/router/request/controller/fixture tests pass, as do the
|
||
Release solution build with 17 pre-existing tracked test warnings, 3,488 App
|
||
tests / 3 skips, and 7,885 complete-solution tests / 5 skips.
|
||
|
||
## 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.
|
||
|
||
### Slice 2 closeout
|
||
|
||
- The shared item-cooldown visual gate passed.
|
||
- `gmToolbarUI::ListenToElementMessage @ 0x004BEE90` confirms the existing
|
||
magnifying-glass path was already correct: assess the selected GUID
|
||
immediately, otherwise enter one-shot Examine target mode.
|
||
- Slice 3 owns the response lifetime and retained examination presentation;
|
||
Slice 2 adds no duplicate command path.
|
||
|
||
## Slice 3 — assessment information panel
|
||
|
||
### Implemented ownership
|
||
|
||
1. `AppraiseInfoParser` owns the complete positional response payload,
|
||
including HookProfile and strict truncation failure.
|
||
2. `ItemInteractionController` owns the pending/current GUID pair and the
|
||
balanced shared busy reference.
|
||
3. `AppraisalUiController` owns response acceptance, item/creature/character
|
||
subview selection, report projection, scroll preservation, first-response
|
||
visibility, and visible-combat refresh.
|
||
4. `RetailUiRuntime` imports the retail layout and registers it with
|
||
`RetailPanelUiController`, so Inventory, Skills, Spellbook, effects, and
|
||
Examination remain views of the same movable/resizable main panel.
|
||
5. Network workers deliver immutable parsed data through the existing
|
||
session router; retained state changes remain on the update thread.
|
||
|
||
### Connected gate
|
||
|
||
- Select an item and click the magnifying glass (or press Assess): the shared
|
||
panel should show its name, available properties, descriptions,
|
||
inscription/signature, retail chrome, and working scrollbar.
|
||
- Assess a monster, NPC, and player: the correct creature/character page
|
||
should appear and the busy cursor should clear.
|
||
- In combat, leave a creature assessment open long enough to observe a health
|
||
refresh; closing it or opening another shared panel must stop refreshes.
|
||
- Move/resize Examination, switch to Inventory or Skills, and confirm the
|
||
shared rectangle is retained.
|