feat(ui): port retail component book rows
Instantiate gmSpellComponentUI category and component entries from LayoutDesc 0x21000033 instead of synthesizing catalog rows. Preserve localized category titles, authored textures and geometry, live icon/count/desired bindings, selected-object synchronization, and retail's inclusive 0..5000 desired-level contract. Co-authored-by: OpenAI Codex <codex@openai.com>
This commit is contained in:
parent
15bb61e05d
commit
a1175e6aac
13 changed files with 2395 additions and 60 deletions
|
|
@ -292,6 +292,102 @@ sorts by `CSpellBase::_display_order`; school and level buttons apply the
|
|||
server-persisted filter bitfield. Component rows use the installed component
|
||||
table and the server-persisted desired purchase amounts.
|
||||
|
||||
### Component-book listbox templates and synchronization
|
||||
|
||||
Named-retail anchors:
|
||||
|
||||
- `gmSpellComponentUI::PostInit @ 0x0048A190`
|
||||
- `gmSpellComponentUI::ListenToElementMessage @ 0x0048A420`
|
||||
- `gmSpellComponentUI::SyncComponentRegionWithData @ 0x0048A620`
|
||||
- `gmSpellComponentUI::UpdateComponents @ 0x0048A910`
|
||||
- `gmSpellComponentUI::RecvNotice_SelectionChanged @ 0x00489C40`
|
||||
- `gmSpellComponentUI::UpdateBuyRates @ 0x00489FE0`
|
||||
|
||||
The component page is not a generic icon catalog. LayoutDesc `0x21000033`
|
||||
authors a listbox with two entry templates, referenced in order by list
|
||||
property `0x64`:
|
||||
|
||||
```text
|
||||
component list 0x10000464 (284 x 600)
|
||||
component scrollbar 0x10000465 (16 x 600)
|
||||
|
||||
template 0 / category 0x10000466 (284 x 32, UIElement_Text)
|
||||
background 0x06001392
|
||||
centered category font 0x40000004
|
||||
|
||||
template 1 / component 0x10000467 (284 x 32, UIElement_Field)
|
||||
normal background 0x06005E21
|
||||
Highlight background 0x06005E22
|
||||
icon region 0x10000468 (x=0, 32 x 32)
|
||||
name 0x10000469 (x=42, width=180, height=32)
|
||||
owned count 0x1000046A (x=224, y=0, width=60, height=15)
|
||||
desired purchase count 0x1000046B (x=224, y=15, width=60, height=15)
|
||||
```
|
||||
|
||||
The seven localized headings are string hashes in local string table
|
||||
`0x23000001`: `SCARABS`, `HERBS`, `POWDERED GEMS`, `ALCHEMICAL SUBSTANCES`,
|
||||
`TALISMANS`, `TAPERS`, and `PEAS`. They correspond in order to retail
|
||||
`SpellComponentCategory` values 0 through 6. Category 6 is Peas; treating it
|
||||
as an invented "Prismatic Components" group is not retail behavior.
|
||||
|
||||
```text
|
||||
PostInit():
|
||||
componentList = GetChildRecursive(0x10000464) as ListBox
|
||||
register component/player/selection notices
|
||||
if PlayerDescription already arrived: UpdateBuyRates(all components)
|
||||
|
||||
UpdateComponents(change):
|
||||
tracker = PlayerSystem.ComponentTracker
|
||||
insertion = 0
|
||||
for category in 0..6:
|
||||
if tracker.componentLists[category] is empty:
|
||||
remove a stale category entry for this category, if present
|
||||
continue
|
||||
|
||||
if entry[insertion] is not this category header:
|
||||
header = list.AddItemFromTemplateList(templateIndex=0, insertion)
|
||||
header.attribute[0x1000004D] = category
|
||||
header.text = localizedCategoryTitle[category]
|
||||
insertion++
|
||||
|
||||
reconcile component rows against tracker.componentLists[category]
|
||||
each new row = list.AddItemFromTemplateList(templateIndex=1, insertion)
|
||||
row.attribute[0x1000004C] = componentWCID
|
||||
row[0x10000468] = MakeIcon(componentWCID)
|
||||
row[0x10000469] = componentName
|
||||
row[0x1000046A] = totalOwnedCount
|
||||
row[0x1000046B] = desiredPurchaseCount
|
||||
row[0x1000046B].inputFilter = NumberInputFilter
|
||||
insertion++
|
||||
|
||||
ListenToElementMessage(message):
|
||||
if selection changed:
|
||||
if selected entry is a category:
|
||||
clear selected world object
|
||||
else if selected entry is a component:
|
||||
select the first owned object represented by that component region
|
||||
|
||||
if edit completed on 0x1000046B:
|
||||
parse integer
|
||||
if 0 <= value <= 5000:
|
||||
send SetDesiredComponentLevel(componentWCID, value)
|
||||
update local PlayerModule projection
|
||||
else:
|
||||
restore the authoritative desired value in the field
|
||||
|
||||
RecvNotice_SelectionChanged():
|
||||
if the selected world object belongs to a tracked component region:
|
||||
select and expose its matching component row
|
||||
else:
|
||||
clear component-list selection
|
||||
```
|
||||
|
||||
The retained port instantiates these resolved templates through
|
||||
`ComponentBookTemplateFactory` and `UiTemplateListSlot`, the architectural
|
||||
equivalent of `AddItemFromTemplateList`. Controllers bind only the live icon,
|
||||
texts, selection, and edit callbacks. They do not duplicate the row sprites,
|
||||
fonts, or geometry.
|
||||
|
||||
### Spellbook row, selection, filtering, dragging, and deletion
|
||||
|
||||
Named-retail anchors:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue