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

@ -101,11 +101,18 @@ animated clone, bounding-box camera, and private-viewport light.
Its correction ports the second authored damage/critical/resistance rating
list, composites row chrome behind the animated preview and row text in front,
adds balanced text inset, and automatically reassesses each new selection
while the floaty remains visible.
while the floaty remains visible. The next correction restores the authored
310 x 400 profile size, insets only foreground row text, lays generated item
text out from the top, and replaces the sparse property dump with retail's
ordered item-report branches for common weapons, armor, magic and DAT spell
descriptions, requirements, capacities, cooldown/special properties, uses,
crafting, ratings, rare state, and prose. AP-110 records the remaining
specialized/player-dependent/DAT-name/font/object-preview branches explicitly.
**Files:** `src/AcDream.Core.Net/Messages/AppraiseInfoParser.cs`;
`src/AcDream.App/UI/RetailUiRuntime.cs`;
`src/AcDream.App/UI/Layout/AppraisalUiController.cs`;
`src/AcDream.App/UI/Layout/ItemAppraisalTextFormatter.cs`;
`src/AcDream.App/UI/UiField.cs`;
`src/AcDream.Core.Net/Messages/InventoryActions.cs`.
@ -119,7 +126,9 @@ noninscribable cases show retail's exact permission messages. Monster pages
show the animated target, mapped creature type, level, and nine ordered
retail stat rows; rated creatures show retail's paired rating rows; the
animated target remains above row chrome but below text; changing selection
updates the open window without another magnifier click.
updates the open window without another magnifier click. Item text begins at
the top and assessed melee/missile/armor/magic items show their retail-ordered
stats and full DAT spell descriptions.
---

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, item/creature/character reports, inscription transaction, retail creature stat/rating/animated-preview presentation, and default selection-follow, but still lacks item-object preview, exhaustive specialized item/character detail regions, and exact appraisal FontInfo-list selection. | `src/AcDream.App/UI/RetailUiRuntime.cs`; `src/AcDream.App/UI/Layout/AppraisalUiController.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 loop cover the active loops; the remaining examination mechanisms require additional retail field/font/object-preview projections rather than fabricated content | Specialized item/character assessments omit some retail fields and item-object presentation; enchanted/incomplete 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::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 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. 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 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 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::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-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

@ -6,7 +6,9 @@ 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; its visual gate is pending.
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.
@ -160,6 +162,24 @@ 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. AP-110 now names only the remaining specialized,
player-dependent, DAT-display-name, 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.
## Slice 1 — spell-bar overflow arrows
### Retail oracle

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