feat(ui): complete retail item appraisal reports

Restore the authored examination geometry and top-origin item list, then port retail's ordered weapon, armor, magic, requirement, capacity, cooldown, use, and description branches into a dedicated formatter with DAT spell prose. Keep the remaining specialized display-name and preview gaps explicit in AP-110.

Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
Erik 2026-07-23 17:03:26 +02:00
parent d96ea2de98
commit bc47bc4917
9 changed files with 1587 additions and 297 deletions

View file

@ -329,6 +329,143 @@ this order:
9. caster/boost/healer/capacity/lock/mana/uses/craftsman details;
10. sale restriction, rare status, full magic, and description.
The individual branches are not a generic property dump. They project the
wire profile into retail prose:
```text
ShowWeaponAndArmor(validLocations, profile):
ammoType = live PublicWeenieDesc ammo type
if object is a hook:
ammoType = HookProfile ammo type
if validLocations contains Shield:
append "Base Shield Level: <ArmorLevel>"
append the player-dependent effective Shield level when available
if validLocations contains a weapon location:
weapon = profile.WeaponProfile
append "Skill: <WeaponSkill>"
if WeaponType exists:
append the parenthesized weapon subtype
launcher = validLocations contains MissileWeapon and ammoType != None
append launcher ? "Damage Bonus: " : "Damage: "
minimum = (1 - DamageVariance) * Damage
if Damage - minimum > 0.0002:
append "<minimum> - <maximum>"
else:
append "<maximum>"
append damage type for non-launchers
append Elemental Damage Bonus when present
append launcher Damage Modifier when present
if validLocations contains MeleeWeapon, MissileWeapon, or TwoHanded:
append "Speed: <category> (<raw weapon time>)"
categories:
raw < 11 -> Very Fast
raw < 31 -> Fast
raw < 50 -> Average
raw < 80 -> Slow
otherwise -> Very Slow
if launcher:
rangeYards = min(
85,
2 * MaxVelocity^2 * (1 / 9.8) * 1.094)
if rangeYards < 10:
displayedRange = ceil(rangeYards)
else:
displayedRange = truncate down to a multiple of 5
append "Range: <displayedRange> yds."
if MaxVelocityEstimated:
append " (based on STRENGTH 100)"
if WeaponOffense != 1 and this is not launcher damage:
append "Bonus to Attack Skill: <signed percent>."
append the retail ammunition sentence:
ammunition -> "Used as ammunition by bows/crossbows/atlatls."
launcher -> "Uses arrows/quarrels/atlatl darts as ammunition."
ShowArmor(profile):
append "Armor Level: <base AL>"
for Slashing, Piercing, Bludgeoning, Fire, Cold, Acid, Electric, Nether:
adjective from DamageResistanceToString:
0.0 -> None
(0.0, 0.4) -> Poor
[0.4, 0.8) -> Below Average
[0.8, 1.2) -> Average
[1.2, 1.6) -> Above Average
[1.6, 2.0) -> Excellent
>= 2.0 -> Unparalleled
append "<damage>: <adjective> (<base AL * modifier, rounded>)"
ShowMagic(profile):
if the spellbook exists and appraisal failed:
append "Spells: unknown."
return
split spell ids by the high enchantment bit
resolve every id with that bit masked through ClientMagicSystem
for ordinary spells:
append Spellcraft, Mana, and Mana Cost when present
append "Spell Descriptions:"
append each spell name and its DAT description
for high-bit enchantments:
append "Enchantments:"
append each spell name and its DAT description
ShowSpecialProperties(profile):
append carry limit and authored cooldown duration when present
append cleave count
build one "Properties: " line from:
creature slayer, Multi-Strike, imbued-effect flags,
Magic Absorbing, Unenchantable, Attuned/Bonded/death behavior,
Retained, Crushing Blow, Biting Strike, Armor/Resistance Cleaving,
Cast on Strike, Ivoryable, and Dyeable
if an imbued-effect flag exists:
append "This item cannot be further imbued."
if AutowieldLeft:
append the left-hand tether sentence
ShowRemainingItemFields(profile):
append, in SetAppraiseInfo order:
usage text
level, direct heritage restriction, and wield requirements
(up to four requirement triplets)
use restrictions
item level and item XP
activation requirements
caster, boost, healing, capacity, lock, and mana-stone data
remaining key/charge counts
creator, last tinkerer, and imbuer
cannot-be-sold and rare notices
short and long descriptions
```
Sources:
- `ItemExamineUI::SetAppraiseInfo @ 0x004B72B0`
- `ItemExamineUI::Appraisal_ShowWieldRequirements @ 0x004AF9A0`
- `ItemExamineUI::Appraisal_ShowSpecialProperties @ 0x004B0140`
- `ItemExamineUI::Appraisal_ShowWeaponAndArmorData @ 0x004B10E0`
- `ItemExamineUI::Appraisal_ShowArmorModsData @ 0x004B1F60`
- `ItemExamineUI::Appraisal_ShowMagicInfo @ 0x004B2E10`
- `ItemExamineUI::Appraisal_ShowActivationRequirements @ 0x004B3270`
- `AppraisalSystem::WeaponTimeToString @ 0x005B4970`
- `AppraisalSystem::DamageResistanceToString @ 0x005B5490`
- `AppraisalSystem::InqWorkmanshipAdjective @ 0x005B48E0`
- `ClientUISystem::DeltaTimeToString @ 0x00565E10`
ACE's `AppraiseInfo` serializer and property enums were used to pin the
typed-table IDs carried by the network profile. ACViewer's portal.dat
SpellTable path was used as the second reference check that item spell names
and descriptions come from DAT spell metadata rather than a hand-maintained
client list. Retail's named functions remain the behavioral oracle.
The main text and inscription areas use their authored scrollbars. A newly
selected object resets those scroll positions to the top; a refresh of the
same object preserves the user's position.
@ -344,6 +481,14 @@ Inventory/Skills/Spellbook panel.
`gmFloatyExaminationUI::ResizeTo` delegates to ordinary `UIElement::ResizeTo`
and persists width/height; it adds no special resize algorithm.
The authored root is exactly 310 by 400 pixels and has 310-by-400 minimum
constraints. Its maximum constraints are 2000 by 2000, so 310 by 400 is the
retail opening/default size, not a fixed-size panel. The main item report
control is authored with bottom vertical justification, but
`ItemExamineUI::AddItemInfo` appends rows from the start of the cleared text
surface. The retained projection must therefore lay out that generated report
from the top while leaving the separate authored inscription behavior intact.
## Inscription presentation and eligibility
```text