acdream/docs/plans/2026-07-23-world-interaction-completion.md
Erik 2dd5cb80d2 fix(ui): assess retained items on right click
Port UIElement_ItemList's physical-item right-click branch through the shared retained list. Select and appraise backpack, loot, paperdoll, and shortcut items through their canonical owners, while preventing RMB movement from lifting items or issuing appraisal requests.

Co-authored-by: Codex <codex@openai.com>
2026-07-24 05:55:51 +02:00

368 lines
22 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,
zero-useability, carried direct-use, AutoWear correction, and the requested
shared item-cooldown follow-up passed their connected gates. Slice 3's first
connected gate exposed an incorrect IdentifyResponse flag table, a non-retail
shared-panel mount, and its missing inscription transaction. All three are
corrected and live-confirmed. The creature-page follow-up now has retail's
ordered stat rows and animated private preview. The follow-up item report and
authored 310 x 400 layout correction are implemented; their connected visual
gate is pending.
**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 independent movable/resizable retail floaty examination window.
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 | retained controller in an independent floaty examination window |
| 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 uses ACE/retail's exact flag values and
positional order, includes the three-word HookProfile, and rejects truncated
gated payloads rather than continuing from a corrupt cursor. The first live
monster gate found that `0x0100` had been mislabeled WeaponProfile; the parser
dropped every creature response before response acceptance, leaving the one
busy reference held. A literal ACE `0x0100` creature fixture now protects that
packet-to-controller path.
LayoutDesc `0x2100006B`, root `0x100005F2`, supplies the complete 310 x 400
floaty chrome, title, item/creature/character subviews, close control, and
authored scrollbars. `gmFloatyExaminationUI` is an independent top-level
window, so Inventory/Skills/Spellbook no longer replace it or inherit its
geometry. The imported multiline inscription field now ports
`ItemExamineUI`'s public/hook inscribability, inventory ownership and
same-scribe permission rules, placeholder/signature presentation, exact
failure notices, focus commit behavior, and CP-1252 `SetInscription (0x00BF)`
transaction. Basic item and player reports are live. The creature page now
resolves its type through retail EnumMapper `0x2200000E`, preserves the
authored Character/Level header, creates the exact nine stat rows from
template `0x10000166`, and renders a fixed-heading animated clone through a
private viewport using retail's bounding-box camera and distant light.
The visual-gate correction also ports the separate `0x10000335`
damage/critical/resistance rating list, places authored row chrome behind the
animated preview and text in front, adds the balanced row inset, and follows
the current selection automatically while the examination window is visible.
Item-object preview, specialized item/character detail regions, and exact
creature appraisal font-state selection remain the narrowed AP-110 residual.
The item report now retains `PublicWeenieDesc` hook identity, applies
appraisal-only Value/Burden unknowns, suppresses mounted-hook sentinel
capacities, preserves retail line/paragraph boundaries, and selects the
authored white/green/red item font-color entries. 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,496 App
tests / 3 skips, and 7,913 complete-solution tests / 5 skips.
The creature presentation follow-up adds real-DAT row-template and EnumMapper
fixtures plus pure conformance coverage for row order/formatting, enchantment
semantics, failed assessments, stable clone identity, live animated mesh
updates, hydrated mesh bounds, and retail camera fitting. The warning-free App code,
Release solution build, 3,506 App tests / 3 skips, and 7,923 complete-solution
tests / 5 skips pass. The connected visual result remains the closeout gate.
The rating/layering/selection-follow correction adds 18 focused green tests,
passes the Release solution build with the 17 warnings already tracked by
#228, 3,510 App tests / 3 skips, and 7,927 complete-solution tests / 5 skips.
Its corrected connected visual result remains the closeout gate.
The item-report/layout correction restores the authored 310 x 400 examination
size for the connected profile, leaves retail's ordinary resize range intact,
keeps row chrome at its LayoutDesc origin while insetting only foreground
creature text, and starts generated item prose at the top of its authored
scroll surface. `ItemAppraisalTextFormatter` now owns the decomp-ordered item
projection outside `GameWindow` and outside the examination controller. It
ports common weapon damage ranges/speed/range/ammunition, armor protection
bands, defense/caster modifiers, workmanship, ratings, wield/use/activation
requirements, item XP/capacity/lock/mana/uses/creator/rare data, cooldown and
imbued special properties, and both short spell lists and full DAT spell
descriptions. The conformance follow-up replaces public-value fallbacks with
retail's appraisal presence semantics, restores hook/capacity/lock behavior,
and carries each `AddItemInfo` fragment's paragraph and font-color index into
the retained text shaper. AP-110 now names only the remaining specialized,
player-dependent, DAT-display-name, creature-font-state, and object-preview gaps.
Focused conformance fixtures cover the geometry layering plus melee, launcher,
armor, spell, and special-property reports. The Release solution build passes
with the 17 warnings already tracked by #228, 3,514 App tests / 3 skips, and
7,931 complete-solution tests / 5 skips. The connected visual result remains
the closeout gate.
The item-format conformance correction adds literal hook-tail cursor fixtures,
wire/session/object-table propagation tests, the exact Black Phyntos Hive
report, capacity/page/lock presence cases, structured paragraph checks, and
LayoutDesc color-palette/enchantment-style coverage. The isolated Release
solution build passes with the same 17 tracked warnings; 3,522 App tests / 3
skips and 7,942 complete-solution tests / 5 skips pass while the previously
launched client retains the normal Release output lock. The corrected
connected item visual remains the closeout gate.
The exhaustive item-report correction replaces the remaining generic/numeric
approximations with the complete named-retail dispatch. Equipment sets use the
literal EoR table; ratings, tinkering/salvage averages, coverage, failed weapon
unknowns, level restrictions, all three item-XP curves, activation heritage,
healer/ordinary boost behavior, rare timers, and magic `~ Name: Description`
rows now preserve retail wording and ordering. Description construction ports
lifespan prose, workmanship/material/gem decoration and portal/PK restriction
bits. `RetailAppraisalNameResolver` follows retail's master
`EnumIDMap -> sub-enum 1 DualEnumIDMap` material chain and shares the installed
creature mapper for slayers and wield requirements; a production-DAT test pins
Ruby and Ghost. AP-110 is narrowed to item preview and the projections that
need live player/localization state (effective shield, cooldown remaining, and
augmentation-cost `StringInfo`), plus character and creature-font residuals.
The focused item formatter suite passes 21/21, App Release passes 3,531 tests /
3 skips, and the complete Release solution passes 7,952 tests / 5 skips. The
connected item-report comparison is the remaining Slice 3 gate.
The material-title and section-boundary correction carries
`PublicWeenieDesc.MaterialType` from CreateObject through the canonical object
table and resolves `ACCWeenieObject::GetObjectName(NAME_APPROPRIATE)` through
the installed DAT material map. Examination titles now produce names such as
`Reed Shark Hide Steel Toed Boots` without duplicating a material already
present in the authored base name. Empty `AddItemInfo` calls are retained as
real report fragments, restoring retail's intentional blank rows after
workmanship, before armor level, around rating/special-property blocks, and at
the later use/item-level boundaries. Focused wire, projection, object-table,
title, and boots-layout fixtures protect the full path. The Release solution
build and 3,533 App tests / 3 skips plus 7,956 complete-solution tests /
5 skips pass; the connected boots comparison is the remaining visual gate.
The world right-click follow-up ports
`UIElement_SmartBoxWrapper::MouseUp @ 0x004E5820` and the
`sr_Examine` branch of `RecvNotice_SmartBoxObjectFound @ 0x004E5AD0`.
The configurable `SelectRight` binding now completes on release, cancels when
pointer travel crosses the retail-observed three-pixel drag threshold, and
routes through the existing world picker, lighting pulse, canonical
`SelectionState`, and appraisal request owner. Empty world space remains a
no-op, right-drag camera orbit does not appraise its release point, and the
independent configurable `SelectionExamine` action now reaches the same
request/target-mode path.
The retained follow-up ports the separate
`UIElement_ItemList::ListenToElementMessage @ 0x004E4D50` branch: an occupied
backpack, side-bag, loot, paperdoll-slot, or physical toolbar cell now selects
its item and enters that same appraisal owner on a completed right-click.
Right-button movement cancels the click and can never begin an item drag.
## 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 and registers retail's
`gmFloatyExaminationUI` as its own top-level window. It deliberately does
not enter `RetailPanelUiController`; Inventory, Skills, Spellbook, and
effects keep their shared main-panel geometry while Examination coexists.
5. Network workers deliver immutable parsed data through the existing
session router; retained state changes remain on the update thread.
6. `AppraisalUiController` owns inscription presentation and optimistic field
state; `WorldSession` owns the exact `0x00BF` GameAction send. Authoritative
inventory/appraisal data remains the source on the next response.
### Connected gate
- Select an item and click the magnifying glass (or press Assess): a separate
floaty window should show its name, available properties, descriptions,
inscription/signature, retail chrome, and working scrollbars.
- Assess a monster, NPC, and player: the correct creature/character page
should appear and the busy cursor should clear.
- On a monster, verify the animated creature is centered behind the exact
Strength/Endurance/Coordination/Quickness/Focus/Self/Health/Stamina/Mana
order, with its creature type and level in the authored header. Assess
differently sized monsters to verify the retail bounding-box fit.
- In combat, leave a creature assessment open long enough to observe a health
refresh; closing the examination window must stop refreshes.
- Keep Examination open while opening/moving Inventory or Skills and confirm
both windows coexist with independent rectangles.
- Assess an owned inscribable weapon. Edit and clear its inscription by
clicking elsewhere, then reassess it. An item authored by another player is
read-only and reports the exact retail permission line when clicked.