# Retail appraisal request and examination UI pseudocode **Date:** 2026-07-23 **Scope:** status-bar Assess request, IdentifyObjectResponse parsing, and the retained examination window. ## Sources - Named retail: - `gmToolbarUI::ListenToElementMessage @ 0x004BEE90` - `gmExaminationUI::RecvNotice_ExamineObject @ 0x004AB7B0` - `gmExaminationUI::RecvNotice_SelectionChanged @ 0x004AB3D0` - `gmExaminationUI::SetActiveExamineUI @ 0x004AB420` - `gmExaminationUI::UseTime @ 0x004AB530` - `gmExaminationUI::SetAppraiseInfo @ 0x004ADAE0` - `BasicCreatureExamineUI::SetAppraiseInfo @ 0x004B3F70` - `CreatureExamineUI::SetAppraiseInfo @ 0x004B3FF0` - `CharExamineUI::SetAppraiseInfo @ 0x004B45F0` - `ItemExamineUI::SetAppraiseInfo @ 0x004B72B0` - `gmFloatyExaminationUI::ResizeTo @ 0x004D4470` - Retail LayoutDesc `0x2100006B`, root `0x100005F2` (`gmFloatyExaminationUI`, authored size 310 x 400 pixels). - ACE: - `GameEventIdentifyObjectResponse` - `AppraiseInfo.Write` - `HookProfile.Write` - ACViewer's `HookAppraisalProfile` representation, used as a second interpretation check for the three-field hook profile. The named retail client is the behavior oracle. ACE and ACViewer only clarify the server packet and structure interpretation. ## Toolbar request ```text on Examine button: if selected object id != 0: ClientUISystem.ExamineObject(selected id) else: enter TARGET_MODE_EXAMINE ``` This was already present in acdream before this slice and does not need a second command path. ## Examination request lifetime ```text on ExamineObject(guid): if guid == 0: return if awaitingAppraisalId == 0: ClientUISystem.IncrementBusyCount() awaitingAppraisalId = guid send CM_Item.Event_Appraise(guid) clear pending profile pointer ``` Replacing one pending GUID does not add a second busy reference. The response for the current pending GUID releases that one reference. ## IdentifyObjectResponse packet order ```text read guid read flags read success if IntStatsTable: read packed int table if Int64StatsTable: read packed int64 table if BoolStatsTable: read packed bool table if FloatStatsTable: read packed double table if StringStatsTable: read packed string table if DidStatsTable: read packed DID table if SpellBook: read spell list if ArmorProfile: read armor profile if CreatureProfile: read creature profile if WeaponProfile: read weapon profile if HookProfile: read flags, validLocations, ammoType (three u32) if ArmorEnchantmentBitfield: read highlight/color (two u16) if WeaponEnchantmentBitfield: read highlight/color (two u16) if ResistEnchantmentBitfield: read highlight/color (two u16) if ArmorLevels: read nine u32 armor levels ``` Every gated field is positional. A truncated gated field rejects the packet; returning a partial profile would let later fields masquerade as earlier ones. ## Applying an appraisal ```text SetAppraiseInfo(guid, profile): if guid == 0: return if guid != awaitingAppraisalId and guid != currentAppraisalId: return firstResponse = false if guid == awaitingAppraisalId: awaitingAppraisalId = 0 ClientUISystem.DecrementBusyCount() currentAppraisalId = guid firstResponse = true if profile has no CreatureProfile: subview = item else if profile has string property Template (5) or int property EquipmentSetId (0x105): subview = character/player else: subview = creature object = ClientObjMaintSystem.GetWeenieObject(guid) if object is absent: return titleName = profile string 0x34 if present else object.GetObjectName(NAME_APPROPRIATE) title = titleName with stack count newlySelectedInSubview = guid != subview.currentObjectId subview.Init(guid, object) subview.SetAppraiseInfo(profile, newlySelectedInSubview) SetActiveExamineUI(subview) if firstResponse: show examination window ``` Only the first response to an explicit request forces the window visible. Background refreshes update the current object without reopening a closed window. ## Subview and refresh behavior ```text SetActiveExamineUI(selected): hide item, creature, character, and spell subviews show selected subview only on time pulse: if examination window is visible and combat mode is not peace and active subview is creature or character and currentAppraisalId != 0 and 0.75 seconds elapsed: send another Event_Appraise(currentAppraisalId) ``` When the examination window's selection-follow option is active, a selection change appraises the new selected GUID; clearing selection hides the window. That optional retail preference remains outside this slice. ## Item presentation order `ItemExamineUI::SetAppraiseInfo` clears and rebuilds the authored item text in this order: 1. icon; 2. value and burden (the EoR layout has no separate value/burden controls, so these enter the main text); 3. inscription/signature; 4. tinkering/workmanship; 5. set and rating information; 6. weapon or armor profile; 7. defense and armor modifiers; 8. magic, special properties, usage and wield requirements; 9. caster/boost/healer/capacity/lock/mana/uses/craftsman details; 10. sale restriction, rare status, full magic, and description. 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. ## Window behavior The LayoutDesc root already contains retail's complete floaty window chrome, close control, item/creature/character/spell subviews, and scrollbars. `gmFloatyExaminationUI::ResizeTo` delegates to ordinary `UIElement::ResizeTo` and persists width/height; it adds no special resize algorithm. ## Slice boundary This slice ports the request lifetime, packet integrity, subview choice, authored outer layout, basic report content, scrollbar ownership, and combat refresh. Retail's exact 3D object/creature preview, all specialized `ItemExamineUI` detail branches, selection-follow preference, and `SetInscriptionEditableState` plus inscription submission remain explicit AP-110 work. Until that permission/write transaction exists, the imported inscription field is display-only rather than accepting edits that cannot be sent.