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

@ -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.