fix #234: port retail appraisal floaty and inscriptions
This commit is contained in:
parent
643cdfe66e
commit
f1a7912160
23 changed files with 1310 additions and 106 deletions
|
|
@ -17,13 +17,25 @@ retained examination window.
|
|||
- `CreatureExamineUI::SetAppraiseInfo @ 0x004B3FF0`
|
||||
- `CharExamineUI::SetAppraiseInfo @ 0x004B45F0`
|
||||
- `ItemExamineUI::SetAppraiseInfo @ 0x004B72B0`
|
||||
- `ItemExamineUI::HandleInscriptionGainingFocus @ 0x004AC0E0`
|
||||
- `ItemExamineUI::HandleInscriptionMousePresses @ 0x004AC230`
|
||||
- `ItemExamineUI::SetInscriptionEditableState @ 0x004AC720`
|
||||
- `ItemExamineUI::SetInscription @ 0x004AE2F0`
|
||||
- `ItemExamineUI::HandleInscriptionLosingFocus @ 0x004AE620`
|
||||
- `gmFloatyExaminationUI::ResizeTo @ 0x004D4470`
|
||||
- `AppraisalProfile::IsHookedItemInscribable @ 0x005B2F90`
|
||||
- `HookAppraisalProfile::IsInscribable @ 0x005B64C0`
|
||||
- `CM_Writing::Event_SetInscription @ 0x006A98B0`
|
||||
- `PublicWeenieDesc::BF_INSCRIBABLE` in `acclient.h:6434`
|
||||
- Retail LayoutDesc `0x2100006B`, root `0x100005F2`
|
||||
(`gmFloatyExaminationUI`, authored size 310 x 400 pixels).
|
||||
- ACE:
|
||||
- `IdentifyResponseFlags`
|
||||
- `GameEventIdentifyObjectResponse`
|
||||
- `AppraiseInfo.Write`
|
||||
- `HookProfile.Write`
|
||||
- `GameActionSetInscription`
|
||||
- `Player.HandleActionSetInscription`
|
||||
- ACViewer's `HookAppraisalProfile` representation, used as a second
|
||||
interpretation check for the three-field hook profile.
|
||||
|
||||
|
|
@ -63,6 +75,30 @@ for the current pending GUID releases that one reference.
|
|||
|
||||
## IdentifyObjectResponse packet order
|
||||
|
||||
The flag values are part of the wire contract and are not the field-order
|
||||
indices:
|
||||
|
||||
```text
|
||||
IntStatsTable 0x0001
|
||||
BoolStatsTable 0x0002
|
||||
FloatStatsTable 0x0004
|
||||
StringStatsTable 0x0008
|
||||
SpellBook 0x0010
|
||||
WeaponProfile 0x0020
|
||||
HookProfile 0x0040
|
||||
ArmorProfile 0x0080
|
||||
CreatureProfile 0x0100
|
||||
ArmorEnchantmentBitfield 0x0200
|
||||
ResistEnchantmentBitfield 0x0400
|
||||
WeaponEnchantmentBitfield 0x0800
|
||||
DidStatsTable 0x1000
|
||||
Int64StatsTable 0x2000
|
||||
ArmorLevels 0x4000
|
||||
```
|
||||
|
||||
ACE writes the gated bodies in the order below, independent of the numerical
|
||||
position of each flag.
|
||||
|
||||
```text
|
||||
read guid
|
||||
read flags
|
||||
|
|
@ -178,16 +214,126 @@ same object preserves the user's position.
|
|||
|
||||
The LayoutDesc root already contains retail's complete floaty window chrome,
|
||||
close control, item/creature/character/spell subviews, and scrollbars.
|
||||
The root's concrete retail type is `gmFloatyExaminationUI`, not a child of
|
||||
`gmPanelUI`. It is mounted and persisted as its own top-level retained window.
|
||||
Showing it must not hide, replace, resize, or reposition the shared
|
||||
Inventory/Skills/Spellbook panel.
|
||||
`gmFloatyExaminationUI::ResizeTo` delegates to ordinary `UIElement::ResizeTo`
|
||||
and persists width/height; it adds no special resize algorithm.
|
||||
|
||||
## Slice boundary
|
||||
## Inscription presentation and eligibility
|
||||
|
||||
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.
|
||||
```text
|
||||
SetInscription(profile):
|
||||
reset inscription field to its unfocused state
|
||||
clear displayed text, signature, old inscription, scribe name/account
|
||||
|
||||
inscribable = object.PublicWeenieDesc has BF_INSCRIBABLE
|
||||
if object is a hook:
|
||||
inscribable = profile.HookProfile exists
|
||||
and (HookProfile.Flags & 1) != 0
|
||||
|
||||
if not inscribable:
|
||||
hide inscription field and signature
|
||||
return
|
||||
|
||||
scribe = profile string property 8, or empty
|
||||
oldInscription = profile string property 7, or empty
|
||||
|
||||
if scribe is empty:
|
||||
field = "<Inscribe here>"
|
||||
signature = ""
|
||||
else:
|
||||
field = oldInscription
|
||||
signature = "--" + scribe
|
||||
if local player is a PSR and scribe-account property 0x17 exists:
|
||||
signature += " <" + scribeAccount + ">"
|
||||
|
||||
show field and signature
|
||||
SetInscriptionEditableState()
|
||||
|
||||
SetInscriptionEditableState:
|
||||
editable = object exists
|
||||
and object has BF_INSCRIBABLE
|
||||
and object is owned by the local player
|
||||
and (scribe is empty
|
||||
or case-insensitive scribe == local player name)
|
||||
PSR may override the ownership/scribe restriction
|
||||
field.Editable = editable
|
||||
field.Selectable = editable
|
||||
inscription background receives mouse presses only when not editable
|
||||
```
|
||||
|
||||
The ordinary-player port does not fabricate PSR status. It applies the exact
|
||||
ownership/name rule; an eventual PSR player-description capability can add the
|
||||
retail override without changing this transaction.
|
||||
|
||||
For hooks, the appraisal HookProfile is the source of truth for presentation:
|
||||
`AppraisalProfile::IsHookedItemInscribable` returns false when it is absent and
|
||||
otherwise returns bit 0. The live public description remains the source used by
|
||||
`SetInscriptionEditableState`, matching the two retail functions rather than
|
||||
merging them into one guessed rule.
|
||||
|
||||
Noneditable mouse presses report retail's exact reason:
|
||||
|
||||
```text
|
||||
if scribe is nonempty and differs from local player:
|
||||
"Only <scribe> can change the inscription"
|
||||
else if object is absent or not owned:
|
||||
"Item must be in your inventory to inscribe."
|
||||
else if BF_INSCRIBABLE is absent:
|
||||
"This item is not inscribable."
|
||||
```
|
||||
|
||||
## Inscription edit transaction
|
||||
|
||||
```text
|
||||
on inscription focus gained:
|
||||
if scribe is empty:
|
||||
switch to the normal editing state
|
||||
clear "<Inscribe here>"
|
||||
signature = "--" + local player name
|
||||
|
||||
on inscription focus lost:
|
||||
if current object id == 0:
|
||||
return
|
||||
|
||||
text = current field text
|
||||
skipSend = text is empty and original scribe is empty
|
||||
if not skipSend and text != oldInscription:
|
||||
send CM_Writing.Event_SetInscription(object id, text)
|
||||
|
||||
if text is empty:
|
||||
restore unfocused state
|
||||
field = "<Inscribe here>"
|
||||
signature = ""
|
||||
scribe name/account = ""
|
||||
else:
|
||||
scribe name = local player name
|
||||
signature = "--" + local player name
|
||||
|
||||
oldInscription = text
|
||||
```
|
||||
|
||||
`CM_Writing::Event_SetInscription` sends a normal UI-counter GameAction:
|
||||
|
||||
```text
|
||||
u32 0xF7B1 GameAction envelope
|
||||
u32 uiCounter
|
||||
u32 0x00BF SetInscription
|
||||
u32 objectGuid
|
||||
String16L text CP-1252, padded to a four-byte boundary
|
||||
```
|
||||
|
||||
There is no successful server reply in retail captures. The client therefore
|
||||
updates its field/signature optimistically after sending; ACE deliberately
|
||||
implements the same behavior.
|
||||
|
||||
## Remaining slice boundary
|
||||
|
||||
The request lifetime, exact packet flags, independent floaty window,
|
||||
inscription permission/edit/write transaction, subview choice, authored outer
|
||||
layout, basic report content, scrollbar ownership, and combat refresh are in
|
||||
scope. Retail's exact 3D object/creature preview, exhaustive specialized
|
||||
`ItemExamineUI` detail branches, selection-follow preference, and PSR-only
|
||||
scribe-account/override behavior remain explicit AP-110 work.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue