feat(ui): port retail item appraisal reports

Follow ItemExamineUI's EoR dispatch and wording for item assessment instead of the generic projection. Resolve material and creature names through installed DAT maps, cover specialized item branches and item-XP curves, and narrow AP-110 to the remaining live/localized preview seams.
This commit is contained in:
Erik 2026-07-23 19:08:05 +02:00
parent 2c00d53db2
commit d78d09cfbc
12 changed files with 1258 additions and 193 deletions

View file

@ -123,6 +123,15 @@ bytes into `255/255`. CreateObject now sign-extends both capacity bytes, so
`Appraisal_ShowCapacity`'s existing positive tests naturally suppress the
sentinels without a name/type workaround.
The subsequent exhaustive item-report pass ports the remaining EoR formatter
branches: literal equipment-set names, positive-only ratings, salvage
workmanship, clothing coverage, failed weapon unknowns, exact item-XP curves,
activation/healer/rare/magic prose, and decorated lifespan/material/gem/portal
descriptions. Installed DAT maps now supply material and creature/slayer names.
AP-110 retains only item preview, live player/localization projections,
character detail, and creature FontInfo state; the connected item visual is
the remaining acceptance gate.
**Files:** `src/AcDream.Core.Net/Messages/AppraiseInfoParser.cs`;
`src/AcDream.App/UI/RetailUiRuntime.cs`;
`src/AcDream.App/UI/Layout/AppraisalUiController.cs`;

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 retail-ordered item reports covering weapon/armor/caster modifiers, spell names plus DAT descriptions, requirements, capacities, cooldown/special properties, uses, crafting, rare, and description data. Item reports also preserve appraisal-only unknowns, hook/capacity/lock presence rules, paragraph boundaries, and the authored normal/beneficial/detrimental color list. It still lacks item-object preview, player-dependent effective shield projection, DAT display-name resolution for equipment sets and slayer creature types, some expiry/material/portal/PK-specialized description branches, 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/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 remaining examination mechanisms require player-state or DAT display-name resolvers and object-preview ownership rather than fabricated content | Uncommon item/character assessments may omit the listed specialized branches and item-object presentation; unresolved equipment-set/slayer values use their numeric identity; 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`; `ItemExamineUI::SetAppraiseInfo @ 0x004B72B0`; `ItemExamineUI::AddItemInfo @ 0x004AC050`; `ItemExamineUI::Appraisal_ShowCapacity @ 0x004B2680`; `ItemExamineUI::Appraisal_ShowSpecialProperties @ 0x004B0140`; `ItemExamineUI::Appraisal_ShowWeaponAndArmorData @ 0x004B10E0`; `ItemExamineUI::Appraisal_ShowMagicInfo @ 0x004B2E10`; `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, and the full EoR item-report dispatch: appraisal-only unknowns; exact equipment-set/rating/tinkering/weapon/armor/caster/requirement/XP/healer/rare prose; ordinary/enchantment DAT spell descriptions; 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`; `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

@ -14,8 +14,8 @@ bodies begin: spell-bar overflow controls, status-bar Use/Assess commands,
assessment information, equipped-child picking, vendor browsing, and
server-authoritative vendor transactions. Slice 1's DAT-authored arrow-only
favorite-spell scrollbar, fixed 18-cell capacity, authored arrow sides/media,
and persistent manual offset passed the connected gate. Slice 2 is active: the
status hand follows canonical selection/object notices, ghosts empty and
and persistent manual offset passed the connected gate. Slice 2's status hand
follows canonical selection/object notices, ghosts empty and
explicitly unusable selections, and routes enabled weapons, direct-use items,
and use-on-target tools through the existing interaction transaction and
cursor owners. Its connected state gate passed. A named-retail correction now
@ -38,8 +38,11 @@ shared main-panel content; and inscription was display-only. Exact flag
decoding now reaches the balanced request owner, retail's
`gmFloatyExaminationUI` has an independent top-level lifetime, and the
imported multiline field sends the exact `SetInscription (0x00BF)`
transaction under retail ownership/authorship rules. The corrective connected
gate is next.
transaction under retail ownership/authorship rules; those corrections are
user-confirmed. The item view now follows the complete EoR formatter dispatch,
including literal set/rating/tinkering prose, weapon/armor/magic/XP/
activation/healer/rare branches, and installed-DAT material and creature
names. Its connected item-report comparison is Slice 3's remaining gate.
---

View file

@ -89,8 +89,8 @@ transactions. This is deliberately using the extracted interaction owners and
canonical shared main-panel host before quest/emote/character-creation bodies
broaden the feature surface.
Slice 1's 18-cell favorite-spell overflow bar is user-accepted. Slice 2 is
active: the status hand's selected-object availability and existing
Slice 1's 18-cell favorite-spell overflow bar is user-accepted. Slice 2's
status hand selected-object availability and existing
Use/AutoWield/use-on-target command routing passed their connected gate.
The follow-up retail correction for zero-valued direct-use items, named
AutoWear blockers, and carried-use delivery is user-confirmed. The requested
@ -104,8 +104,11 @@ appraisal busy reference held, retail's examination UI had been mounted as a
shared main-panel page rather than its own floaty window, and inscription was
display-only. Exact response flags, the independent
`gmFloatyExaminationUI` lifetime, and the retail `SetInscription (0x00BF)`
edit transaction are now ported. The corrective connected
examination/inscription gate is next.
edit transaction are now ported and user-confirmed. Its item path now follows
the complete EoR formatter dispatch, including literal set/rating/tinkering
prose, weapon/armor/magic/XP/activation/healer/rare branches, and installed-DAT
material/creature names. The connected item-report comparison is Slice 3's
remaining gate.
Carried:
#153 far-teleport residual, #116 slide-response, the remaining R6 ownership cleanup plus

View file

@ -196,6 +196,23 @@ 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.
## Slice 1 — spell-bar overflow arrows
### Retail oracle

View file

@ -517,6 +517,139 @@ ShowRemainingItemFields(profile):
short and long descriptions
```
### Exhaustive EoR item-report branches
The common projection above was re-audited against every helper called by
`ItemExamineUI::SetAppraiseInfo @ 0x004B72B0`. The remaining item branches are
not generic property labels; retail applies the following exact rules.
```text
ShowTinkering:
if NumTimesTinkered (171):
"This item has been tinkered %d time[s]."
if TinkerName (39): "Last tinkered by %s."
if ImbuerName (40): "Imbued by %s."
if Workmanship (105):
if NumItemsInMaterial (170) is absent:
"Workmanship: <adjective> (<integer workmanship>)"
else:
average = workmanship / NumItemsInMaterial
"Workmanship: <adjective of rounded average> (<average, two decimals>)"
blank line
"Salvaged from %d items."
ShowSet:
EquipmentSetId (265) selects the literal EoR set-name table.
Unknown and explicitly unused entries produce no row.
Known entries produce "Set: <name>".
ShowRatings:
append only positive ratings, in this order:
Dam, Dam Resist, Crit, Crit Dam, Crit Resist, Crit Dam Resist,
Heal Boost, Nether Resist, Life Resist
render as "Ratings: Dam 3, Crit 2" (there is no plus sign)
positive Vitality (379) is a separate
"This item adds %d Vitality."
ShowWeaponAndArmorData:
only interpret WeaponProfile inside weapon/shield valid-location bits
preserve retail Unknown sentinels for damage, speed, and range
for ordinary clothing append:
"Covers Head, Chest, Abdomen, Upper Arms, Lower Arms, Hands,
Upper Legs, Lower Legs, Feet"
selecting the names from ClothingPriority bits in that exact order
ShowShortMagic:
list only ordinary spell ids (high bit clear)
a failed appraisal prints "Spells: unknown."
ShowMagic:
a failed appraisal also prints "Spells: unknown."
ordinary spell rows are:
"Spell Descriptions:\n~ <name>: <DAT description>"
enchantment rows are:
"Enchantments:\n~ <name>: <DAT description>"
ShowLevelLimits:
min == max: "Restricted to characters of Level %d."
min + max: "Restricted to characters of Levels %d to %d."
min only: "Restricted to characters of Level %d or greater."
max only: "Restricted to characters of Level %d or below."
ShowItemLevel:
require ItemBaseXp (int64 5), ItemMaxLevel (319), and ItemXpStyle (320)
currentLevel = ItemTotalXPToLevel(ItemTotalXp, baseXp, maxLevel, style)
displayedLevel = min(currentLevel + 1, maxLevel)
nextThreshold = ItemLevelToTotalXP(min(currentLevel + 1, maxLevel), ...)
"Item Level: <displayedLevel> / <maxLevel>"
"Item XP: <grouped total> / <grouped nextThreshold>"
ItemLevelToTotalXP(level, base, max, style):
clamp level to [0, max]
style 1: base * level
style 2: base * (2^level - 1)
style 3: base * level * (level + 1) / 2
ShowActivationRequirements:
successful appraisals only
append Arcane Lore, Allegiance Rank, Heritage, skill, primary-attribute,
and secondary-attribute requirements to one "Activation requires " row
in that order
ShowBoostValue / ShowHealKitValues:
ordinary consumables render Health/Mana "... when used."
Stamina is "... when consumed."
healer and hooked-healer profiles instead interpret BoostValue as
"Bonus to Healing Skill" and float 100 as "Restoration Bonus"
ShowRareInfo:
RareUsesTimer (bool 108):
"This rare item has a timer restriction of 3 minutes. You will not
be able to use another rare item with a timer within 3 minutes of
using this one."
RareId (17): "Rare #%d"
ShowDescription:
if lifespan, creation timestamp, and remaining lifespan are present:
remaining < 0: "This item is in the act of disintegrating."
otherwise: "This item expires in <years/days/hours/minutes/seconds>"
LongDesc replaces ShortDesc when present; both are never printed
GearPlatingName (52) replaces LongDesc before decoration
AppraisalItemSkill (172) decorates that description:
bit 1 prepends workmanship
MaterialType (131) prepends the DAT material display name and removes
its first occurrence from the authored description
bit 4 appends ", set with <count> <pluralized material>"
PortalBitmask (111) appends, in bit order:
Player Killers may not use this portal.
Lite Player Killers may not use this portal.
Non-Player Killers may not use this portal.
This portal cannot be recalled nor linked to.
This portal cannot be summoned.
```
The material name is not a locally invented table.
`MaterialTypeEnumMapper::MaterialTypeToString @ 0x005CD500` performs
`DBObj::GetByEnum(DBObjType=0x28, clientEnum=0x10000001, subEnum=1)`:
the master map resolves the root `EnumIDMap`, sub-enum 1 resolves the material
`DualEnumIDMap`, and its client name is read with underscores replaced by
spaces. Creature/slayer
names use `EnumMapper::GetString(0x10000005, value)`; heritage restrictions use
`EnumMapper::GetString(0x10000002, value)` with retail's explicit
Gharu'ndim/Umbraen/Olthoi spellings.
Additional named sources for these branches:
- `ItemExamineUI::Appraisal_ShowSet @ 0x004AE880`
- `ItemExamineUI::Appraisal_ShowRatings @ 0x004AF080`
- `ItemExamineUI::Appraisal_ShowTinkeringInfo @ 0x004B0E70`
- `ItemExamineUI::Appraisal_ShowLevelLimitInfo @ 0x004B2920`
- `ItemExamineUI::Appraisal_ShowItemLevelInfo @ 0x004B3C60`
- `ItemExamineUI::Appraisal_ShowDescription @ 0x004B6990`
- `ExperienceSystem::ItemLevelToTotalXP @ 0x005C8100`
- `ExperienceSystem::ItemTotalXPToLevel @ 0x005C81B0`
- `MaterialTypeEnumMapper::MaterialTypeToString @ 0x005CD500`
Sources:
- `ItemExamineUI::SetAppraiseInfo @ 0x004B72B0`
@ -677,11 +810,13 @@ implements the same behavior.
The request lifetime, exact packet flags, independent floaty window,
inscription permission/edit/write transaction, subview choice, authored outer
layout, ordered creature stats and ratings, animated creature preview,
viewport/list/text compositing, selection-follow, scrollbar ownership, and
combat refresh are in scope. Common item report rows now preserve retail's
appraisal-only unknown values, hook/capacity/lock presence rules, exact
paragraph boundaries, and authored normal/beneficial/detrimental font-color
selection. Retail's item-object preview, exhaustive specialized
`ItemExamineUI` detail branches, creature incomplete/high/low FontInfo
selection, and PSR-only scribe-account/override behavior remain explicit
AP-110 work.
viewport/list/text compositing, selection-follow, scrollbar ownership, combat
refresh, and the complete EoR item-report dispatch are in scope. Item rows now
preserve retail's appraisal-only unknown values, hook/capacity/lock presence
rules, exact paragraph boundaries, authored normal/beneficial/detrimental
font-color selection, equipment-set/ratings/tinkering/weapon/armor/magic/XP/
activation/healer/rare/description ordering, and installed-DAT material and
creature names. Retail's item-object preview, live player-dependent effective
shield/cooldown projections, localized augmentation-cost `StringInfo`,
creature incomplete/high/low FontInfo selection, complete character detail,
and PSR-only scribe-account/override behavior remain explicit AP-110 work.