fix(ui): select and examine favorite spells like retail

This commit is contained in:
Erik 2026-07-24 06:47:54 +02:00
parent 043ab10b3c
commit 3e31b0ac70
23 changed files with 974 additions and 34 deletions

View file

@ -158,6 +158,14 @@ three-pixel drag threshold. Target mode is intercepted at that same press.
Bag opening, item use/equip, and shortcut activation remain completed
click/double-click actions, so a drag or target-consumed press cannot also
activate the item.
The corresponding favorite-spell branch is now ported without conflating a
spell ID with a physical object GUID. Favorite selection occurs on left press;
right-click opens the authored local Spell examination subview with
name, school, mana, duration, range, description, and the current appropriate
component formula. It sends no Appraise request and takes no busy reference,
so the status-bar magnifier remains object-only. Switching the shared floaty
from a pending object appraisal to a spell releases exactly that appraisal
transaction before showing the SpellPanel.
**Files:** `src/AcDream.Core.Net/Messages/AppraiseInfoParser.cs`;
`src/AcDream.App/UI/RetailUiRuntime.cs`;
@ -185,6 +193,9 @@ Right-clicking an occupied retained item cell selects and examines it without
using, equipping, looting, or dragging it.
Left-pressing one selects it before the button is released; holding and
dragging preserves that selection without activating the item.
Left-pressing a favorite spell likewise changes its selection immediately.
Right-clicking it opens the spell examination subview; selecting it and using
the status-bar magnifier does not examine the spell.
The Black Phyntos Hive specifically reads `Value: ???`, `Burden: Unknown`,
then its description after one retail paragraph break, without the bogus
255-item/255-container line.

View file

@ -205,7 +205,7 @@ AP-94..AP-112 for the confirmed retail-UI completion gaps.
| ~~AP-107~~ | **RETIRED 2026-07-11 (Wave 3.3 / #197)** — typed `OfferPrimaryClick` returns `NotActive`, `ConsumedSuccess`, or `ConsumedRejected`; every retained item surface plus radar/world offers active target mode before local selection/open/use fallback. Rejections are consumed and cannot drift selection. | `src/AcDream.App/UI/ItemInteractionController.cs`; inventory/paperdoll/toolbar/radar/world call sites | — | — | `UIElement_ItemList::HandleTargetedUseLeftClick @ 0x004E24D0` |
| AP-108 | Paperdoll/AutoWield still omit the remaining missile/held restrictions and corrupt-mask branch of full `AutoWieldIsLegal`, dual-wield/off-hand rules, double-click examine/drag from the doll, body-part selection lighting, and retail's synchronous `" - cannot unwield the %s"` failure suffix (the current send seam reports rejection asynchronously). **AutoWear legality retired from this row 2026-07-23:** inventory activation and paperdoll drops now apply the retail clothing-priority/location blocker lookup and exact `"You must remove your %s to wear that"` system notice. **Primary replacement retired 2026-07-14; Aetheria retired 2026-07-13.** | `src/AcDream.App/UI/Layout/PaperdollController.cs`; `src/AcDream.App/UI/AutoWieldController.cs` | Basic equip slots, Aetheria, live doll, AutoWear conflict reporting, and primary weapon/incompatible shield/mismatched ammo blocker sequencing work in peace and war | Remaining illegal/off-hand cases, asynchronous dequip rejection wording, doll examine/drag, and selection lighting still differ functionally | `CPlayerSystem::AutoWieldIsLegal @ 0x0055ED60`; `CPlayerSystem::AutoWearIsLegal @ 0x0055EF40`; `CPlayerSystem::AutoWield @ 0x00560A60`; `gmPaperDollUI @ 0x004A3590..0x004A5F90` |
| AP-109 | Character Titles page is inert and live displayed-title/luminance state is absent | `src/AcDream.App/UI/Layout/CharacterStatController.cs`; `CharacterSheetProvider.cs` | Attributes/skills core output is user-accepted | Titles cannot be selected/displayed and level-200 luminance fields are missing | `gmCharacterTitleUI @ 0x0049A610`; `gmStatManagementUI::UpdateExperience @ 0x004F0A70` |
| AP-110 | Remaining retained gameplay panels and world HUD are absent: advanced-combat powerbar, residual social/floating chat, quests/map/options/smartbox, vendor/trade/salvage/tinkering, mini-game gameplay, Link Status NAK/retransmission packet-loss averaging, and D.6 nameplates/floaters. Examination has its independent authored floaty layout, inscription transaction, retail creature stat/rating/animated-preview presentation, default selection-follow, and the full EoR item-report dispatch: appraisal-only unknowns; exact equipment-set/rating/tinkering/weapon/armor/caster/requirement/XP/healer/rare prose and intentional blank section rows; ordinary/enchantment DAT spell descriptions; live material-decorated appropriate titles plus DAT material and creature names; expiry, decorated material/gem descriptions; and portal/PK restrictions with authored item colors. It still lacks item-object preview, player-dependent effective shield projection, live cooldown-remaining projection, localized augmentation-cost `StringInfo`, exhaustive character detail regions, and exact creature appraisal FontInfo-list selection. | `src/AcDream.App/UI/RetailUiRuntime.cs`; `src/AcDream.App/UI/Layout/AppraisalUiController.cs`; `src/AcDream.App/UI/Layout/ItemAppraisalTextFormatter.cs`; `src/AcDream.App/UI/Layout/RetailAppraisalNameResolver.cs`; `src/AcDream.App/UI/Layout/CreatureAppraisalRows.cs`; `src/AcDream.App/Rendering/CreatureAppraisalPresentation.cs`; `src/AcDream.Core.Net/LinkStatusSnapshot.cs`; D.5/D.6 roadmap | Basic combat, M3 magic/Link/Vitae surfaces, and the core examination request/presentation/inscription/creature-preview/item-report loop cover the active loops; the residual examination mechanisms require live player/enchantment/localization state or object-preview ownership rather than fabricated content | Item assessments omit only the listed live/localized/preview projections; enchanted/incomplete creature appraisal rows use the normal authored font until the exact FontInfo list is bound; other absent panels remain unavailable; real packet loss is displayed as 0.00% instead of retail's moving average | `BasicCreatureExamineUI::Init @ 0x004AB9C0`; `CreatureExamineUI::SetAppraiseInfo @ 0x004B3FF0`; `gmExaminationUI::RecvNotice_SelectionChanged @ 0x004AB3D0`; `AttributeInfoRegion::Update @ 0x004F1D90`; `gmExaminationUI::SetAppraiseInfo @ 0x004ADAE0`; `ACCWeenieObject::GetObjectName @ 0x0058E6E0`; `ItemExamineUI::SetAppraiseInfo @ 0x004B72B0`; `ItemExamineUI::AddItemInfo @ 0x004AC050`; `ItemExamineUI::Appraisal_ShowCapacity @ 0x004B2680`; `ItemExamineUI::Appraisal_ShowSpecialProperties @ 0x004B0140`; `ItemExamineUI::Appraisal_ShowWeaponAndArmorData @ 0x004B10E0`; `ItemExamineUI::Appraisal_ShowMagicInfo @ 0x004B2E10`; `ItemExamineUI::Appraisal_ShowDescription @ 0x004B6990`; `MaterialTypeEnumMapper::MaterialTypeToString @ 0x005CD500`; `ItemExamineUI::SetInscription @ 0x004AE2F0`; `CM_Writing::Event_SetInscription @ 0x006A98B0`; `CLinkStatusAverages::GetAveragePacketLoss @ 0x00546610`; LayoutDesc catalog |
| AP-110 | Remaining retained gameplay panels and world HUD are absent: advanced-combat powerbar, residual social/floating chat, quests/map/options/smartbox, vendor/trade/salvage/tinkering, mini-game gameplay, Link Status NAK/retransmission packet-loss averaging, and D.6 nameplates/floaters. Examination has its independent authored floaty layout, inscription transaction, retail creature stat/rating/animated-preview presentation, default selection-follow, authored local spell subview with appropriate-formula component state, and the full EoR item-report dispatch: appraisal-only unknowns; exact equipment-set/rating/tinkering/weapon/armor/caster/requirement/XP/healer/rare prose and intentional blank section rows; ordinary/enchantment DAT spell descriptions; live material-decorated appropriate titles plus DAT material and creature names; expiry, decorated material/gem descriptions; and portal/PK restrictions with authored item colors. It still lacks item-object preview, player-dependent effective shield projection, live cooldown-remaining projection, localized augmentation-cost `StringInfo`, exhaustive character detail regions, and exact creature appraisal FontInfo-list selection. | `src/AcDream.App/UI/RetailUiRuntime.cs`; `src/AcDream.App/UI/Layout/AppraisalUiController.cs`; `src/AcDream.App/UI/Layout/ItemAppraisalTextFormatter.cs`; `src/AcDream.App/UI/Layout/RetailAppraisalNameResolver.cs`; `src/AcDream.App/UI/Layout/CreatureAppraisalRows.cs`; `src/AcDream.App/Rendering/CreatureAppraisalPresentation.cs`; `src/AcDream.Core.Net/LinkStatusSnapshot.cs`; D.5/D.6 roadmap | Basic combat, M3 magic/Link/Vitae surfaces, and the core examination request/presentation/inscription/creature-preview/item-report loop cover the active loops; the residual examination mechanisms require live player/enchantment/localization state or object-preview ownership rather than fabricated content | Item assessments omit only the listed live/localized/preview projections; enchanted/incomplete creature appraisal rows use the normal authored font until the exact FontInfo list is bound; other absent panels remain unavailable; real packet loss is displayed as 0.00% instead of retail's moving average | `BasicCreatureExamineUI::Init @ 0x004AB9C0`; `CreatureExamineUI::SetAppraiseInfo @ 0x004B3FF0`; `gmExaminationUI::RecvNotice_SelectionChanged @ 0x004AB3D0`; `gmExaminationUI::ExamineSpell @ 0x004B6900`; `SpellExamineUI::ExamineSpell @ 0x004B6210`; `AttributeInfoRegion::Update @ 0x004F1D90`; `gmExaminationUI::SetAppraiseInfo @ 0x004ADAE0`; `ACCWeenieObject::GetObjectName @ 0x0058E6E0`; `ItemExamineUI::SetAppraiseInfo @ 0x004B72B0`; `ItemExamineUI::AddItemInfo @ 0x004AC050`; `ItemExamineUI::Appraisal_ShowCapacity @ 0x004B2680`; `ItemExamineUI::Appraisal_ShowSpecialProperties @ 0x004B0140`; `ItemExamineUI::Appraisal_ShowWeaponAndArmorData @ 0x004B10E0`; `ItemExamineUI::Appraisal_ShowMagicInfo @ 0x004B2E10`; `ItemExamineUI::Appraisal_ShowDescription @ 0x004B6990`; `MaterialTypeEnumMapper::MaterialTypeToString @ 0x005CD500`; `ItemExamineUI::SetInscription @ 0x004AE2F0`; `CM_Writing::Event_SetInscription @ 0x006A98B0`; `CLinkStatusAverages::GetAveragePacketLoss @ 0x00546610`; LayoutDesc catalog |
| ~~AP-111~~ | **RETIRED 2026-07-11 (M2 held-object parenting)** — equipped hand items are no longer omitted from the render world. CreateObject now preserves Placement/Parent/position timestamp bootstrap; live `0xF749` ParentEvent is parsed with retail sequence freshness; a focused render controller resolves `Setup.HoldingLocations`, applies the child's placement frame, and recomposes the separate child entity after every parent animation tick. Pickup retains the weenie's visual metadata for a later wield. | `src/AcDream.Core.Net/Messages/{CreateObject,ParentEvent}.cs`; `src/AcDream.Core/Meshing/EquippedChildAttachment.cs`; `src/AcDream.App/Rendering/EquippedChildRenderController.cs` | — | — | `ClientCombatSystem::GetDefaultCombatMode @ 0x0056B310`; `SmartBox::HandleParentEvent @ 0x004535D0`; `CPhysicsObj::set_parent @ 0x00515A90`; `CPhysicsObj::UpdateChild @ 0x00512D50` |
| AP-112 | The basic combat bar ports visibility, height selection, desired-power slider, exact 1.0/0.8-second charge, ready-stance gating, request/release, `MaybeStopCompletely`, server-response queueing, and auto-repeat, but still omits `StartAttackRequest`'s `FinishJump` call and exact trained-Recklessness visibility semantics (IA-20 keeps the dark range as the accepted baseline) | `src/AcDream.App/Combat/CombatAttackController.cs`; `src/AcDream.App/UI/Layout/CombatUiController.cs` | The shared player movement owner now performs retail's server-control-gated full stop and movement report before an attack build; the remaining seams require the jump owner and a distinct Recklessness treatment | Starting an attack while charging a jump may not finish that jump exactly when retail does; trained/untrained Recklessness presentation is identical | `ClientCombatSystem::StartAttackRequest @ 0x0056C040`; `CommandInterpreter::MaybeStopCompletely @ 0x006B3B90`; `gmCombatUI::ListenToElementMessage @ 0x004CC430` |
| AP-113 | Invalid lifestone-command arguments display the local text `Usage: /lifestone`; retail definitely emits a local usage/error line but Binary Ninja misidentifies the referenced wide-string address, so its exact wording is not yet recovered | `src/AcDream.UI.Abstractions/Panels/Chat/ChatCommandRouter.cs`; `RetailClientCommandCatalog.cs` | The behavior boundary is exact (handled locally, no chat and no game action); only a low-impact diagnostic sentence differs | `/ls now` can show different wording/color from retail while still refusing the invalid request correctly | `ClientCommunicationSystem::DoLifestone @ 0x0056FC70` |

View file

@ -1,6 +1,6 @@
# acdream — strategic roadmap
**Status:** Living document. Updated 2026-07-23. **M3 landed; M4 is active.** M3's retail casting/UI, R6 locomotion/collision/projectile/teleport/radar rebaseline, deterministic fresh-login/portal world lifecycle, and final two-client portal observer flow are user-gated. All eight slices of the behavior-preserving ownership campaign in [`docs/architecture/code-structure.md`](../architecture/code-structure.md), their automated closeout, and the user's connected visual matrix are complete. Issue #225's lifestone/particle alpha comparison remains a separate rendering visual gate.
**Status:** Living document. Updated 2026-07-24. **M3 landed; M4 is active.** M3's retail casting/UI, R6 locomotion/collision/projectile/teleport/radar rebaseline, deterministic fresh-login/portal world lifecycle, and final two-client portal observer flow are user-gated. All eight slices of the behavior-preserving ownership campaign in [`docs/architecture/code-structure.md`](../architecture/code-structure.md), their automated closeout, and the user's connected visual matrix are complete. Issue #225's lifestone/particle alpha comparison remains a separate rendering visual gate.
**Purpose:** One source of truth for where the project is and where it's going. Every observed defect or missing feature has a named phase that owns it; when something looks wrong in-game, look here to find the phase that'll address it. Implementation details live in per-phase specs under `docs/superpowers/specs/`, not in this file.
---
@ -38,7 +38,14 @@ selects and appraises occupied inventory, external-container, paperdoll, and
physical-shortcut cells through the same request owner. Its shared physical
left-click contract now also matches retail's press-time selection: the
selection frame updates on MouseDown before drag promotion, while completed
click/double-click remains the activation boundary. Slice 3's first connected gate exposed
click/double-click remains the activation boundary. The same timing is now
ported for favorite spells through a distinct catalog contract: left press
selects without waiting for release; right-click opens the examination
floaty's authored SpellPanel locally; and spell IDs never enter global object
selection or the status-bar magnifier path. The view projects retail's
name/school/mana/duration/range/description and authored appropriate-formula
component cells, including live missing-component state. Its connected visual
gate is pending. Slice 3's first connected gate exposed
three deeper defects, now corrected: the response enum mislabeled ACE's
creature bit `0x0100` and dropped monster packets while retaining the busy
cursor; the imported 310 x 400 examination layout was incorrectly hosted as

View file

@ -108,6 +108,12 @@ The same shared ItemList contract now ports retail's left-button-down
selection timing across inventory, loot, paperdoll, and physical shortcuts:
selection is visible before release or drag promotion, while use/open/equip
remains release-completed.
Favorite spells now use the parallel retail catalog contract: MouseDown
selects immediately, right-click opens the examination floaty's authored
SpellPanel locally, and no spell ID enters global object selection or the
status-bar magnifier command. Exact spell fields and the current appropriate
formula's authored component cells are automated; the connected visual gate
is pending.
Slice 3's first connected gate found and corrected the real remaining defects:
ACE creature flag `0x0100` was mislabeled as a weapon profile and left the
appraisal busy reference held, retail's examination UI had been mounted as a

View file

@ -8,7 +8,8 @@ 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.
gate is pending. Favorite-spell press-time selection and right-click local
SpellPanel examination are implemented and await their connected visual gate.
**Milestone:** M4 prerequisite/preamble.
**Architecture:** retained gameplay UI over shared selection, object, and
interaction state. `GameWindow` remains a composition/callback shell.
@ -249,6 +250,16 @@ and the retained green frame update during left-button down, before the
three-pixel drag threshold. Target mode is offered first. Opening, using,
equipping, looting, and shortcut activation remain completed-click or
double-click actions and are suppressed when target mode consumed the press.
Favorite spells now use the parallel non-weenie path:
`gmSpellcastingUI::ListenToElementMessage @ 0x004C7AB0` selects the favorite
on left press, while the spell branch of
`UIElement_ItemList::ListenToElementMessage @ 0x004E4D50` opens the authored
SpellPanel locally on right-click. Spell IDs never enter `SelectionState` or
the status-bar magnifier path, and no Appraise/busy transaction is invented.
The view projects exact spell fields and the current appropriate formula
through authored component template `0x1000032E`; its connected visual gate is
pending. The 61-test focused App gate, Release solution build, 3,553 App tests
/ 3 skips, and 7,984 complete-solution tests / 5 skips pass.
## Slice 1 — spell-bar overflow arrows
@ -371,6 +382,11 @@ Named retail references and executable pseudocode are recorded in
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.
- On the combat spell bar, press and hold a different favorite: its selection
and name should update before release. Right-click a favorite: the same
examination floaty should show the SpellPanel with name, school, mana,
duration, range, description, and component formula. The toolbar magnifier
must not treat the selected spell as an object.
- 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.

View file

@ -219,6 +219,85 @@ Outbound character events (`CM_Character`):
0x0224 SetDesiredComponentLevel(componentDid:u32, amount:i32)
```
### Favorite press selection and local spell examination
Retail:
- `gmSpellcastingUI::ListenToElementMessage @ 0x004C7AB0`
- `SpellCastSubMenu::SetSelected @ 0x004C5B00`
- `UIElement_ItemList::ListenToElementMessage @ 0x004E4D50`
- `ClientUISystem::ExamineSpell @ 0x00564A70`
- `gmExaminationUI::ExamineSpell @ 0x004B6900`
- `SpellExamineUI::ExamineSpell @ 0x004B6210`
- `SpellExamineUI::DetermineSpellRange @ 0x004AD450`
A favorite spell is selected on the UIItem's left-button-down message, not on
button release. Double-click remains a completed gesture and casts the spell
that the press already selected.
```text
favorite UIItem message:
if message parameter == 7: // left press
submenu.SetSelected(spellId)
if message parameter == 10: // completed double click
Cast()
UIElement_ItemList right click:
if itemId != 0:
select itemId as the global weenie selection
ExamineObject(itemId)
else if spellId != 0:
ClientUISystem.ExamineSpell(spellId)
```
The spell branch is deliberately local. It never stores the spell ID in
`ACCWeenieObject::selectedID`, never sends an Appraise request, and never takes
a new UI-busy reference. The toolbar magnifying glass therefore remains an
object-only command; selecting a favorite cannot make it assess that spell.
The examination floaty is shared between object and spell subviews. Switching
to a spell cancels only an in-flight/current object appraisal:
```text
gmExaminationUI::ExamineSpell(spellId):
if awaiting object appraisal:
clear pending object
decrement that appraisal's UI-busy reference
clear current object
send Appraise(0)
else if a current object appraisal exists:
clear current object
send Appraise(0)
SpellExamineUI.ExamineSpell(spellId)
activate SpellPanel
show examination window
```
The authored spell subview is part of LayoutDesc `0x2100006B`:
```text
SpellPanel 0x10000153
school text 0x1000015E
spell icon 0x1000015F
mana text 0x10000160
duration text 0x10000161
range text 0x10000162
description/components text 0x10000163
formula list 0x1000032D
formula-cell template 0x1000032E
missing-component overlay 0x10000330
```
Presentation uses the current appropriate formula, preserving its order and
duplicates. Each component icon keeps the authored missing-component overlay
until the player owns that component. Duration is shown as integer seconds
below one minute and integer minutes from one minute upward. Range is
`min(baseConstant + baseModifier * schoolSkill, 75) / 0.9144`, displayed with
one decimal place in yards. School skill IDs are Creature `0x1F`, Item `0x20`,
Life `0x21`, War `0x22`, and Void `0x2B`; an unclassified spell uses the
highest of those five skills.
## Spellbook and component book
Retail: `gmSpellbookUI::PostInit` `0x0048B2B0` and