fix(ui): complete retail indicator detail panels

Port the authored effect row template, remaining-time and selection details, synchronize the full gmPanelUI child geometry, and route the burden indicator to Character Information panel 3.

Co-authored-by: OpenAI Codex <codex@openai.com>
This commit is contained in:
Erik 2026-07-17 11:36:43 +02:00
parent a96767ba6d
commit d1d603105f
24 changed files with 3696 additions and 147 deletions

View file

@ -569,6 +569,8 @@ negative root 0x10000121 (inherits 0x10000122)
shared effects template 0x10000122
effect list 0x10000123
information text 0x10000126
effect row template 0x10000128 (300 x 32)
row icon / name / duration 0x10000129 / 0x1000012A / 0x1000012B
effect UI type property 0x1000000C
```
@ -622,6 +624,16 @@ an active token stores its spell ID, so every layer of that spell highlights
together and shows the shared name/description; clicking either layer again
clears the selection.
`InfoRegion::InfoRegion @ 0x004F1450` creates each row with
`UIElement_ListBox::AddItemFromTemplateList(template 0)`. In installed retail
data the icon is x=0 width 32, name is x=37 width 188, and duration is x=225
width 50. `EffectInfoRegion::Update @ 0x004F1C00` formats finite time as
`m:ss` or `h:mm:ss`; a negative/permanent duration leaves the field empty.
`gmEffectsUI::UpdateSelection @ 0x004B7F90` displays localized
`ID_Effects_Info_SelectASpell` while nothing is selected, otherwise spell name
plus two newlines plus description. Normal rows use state 1 and every row with
the selected spell ID uses Highlight state 6.
## Portal presentation boundary
Recall and portal presentation remains DAT-driven. `/ls` starts the retail

View file

@ -223,15 +223,44 @@ For every toolbar or indicator detail page:
register page with gmPanelUI panel id
when shown:
hide the current child
move the requested child to the shared main-panel placement
apply the shared main-panel rectangle to the requested child
show only the requested child
when a page with bool property 0x10000049 closes:
restore the deferred ordinary child
```
Helpful/Harmful, Link Status, Vitae, and the Mini Game shell therefore share
the same retained host position as Inventory, Character, and Magic. They do
not remember independent positions.
Character Information, Helpful/Harmful, Link Status, Vitae, and the Mini Game
shell therefore share the same retained host size and position as Inventory,
Character/Skills, and Magic. They do not remember independent rectangles.
The burden/backpack indicator sends panel id `3`, whose production child is
root `0x10000183` under LayoutDesc `0x2100006E`. Panel `11` is the separate
Attributes/Skills child and is not the burden indicator destination.
### Helpful and harmful effect rows
```text
gmEffectsUI::RebuildList @ 0x004B8350:
sort visible enchantment tokens alphabetically by spell name
AddItemFromTemplateList(template 0)
EffectInfoRegion row template 0x10000128 (LayoutDesc 0x2100001B):
icon 0x10000129 at x=0, 32 x 32
name 0x1000012A at x=37, 188 x 32
time left 0x1000012B at x=225, 50 x 32
gmEffectsUI::UpdateSelection @ 0x004B7F90:
no selected spell -> display ID_Effects_Info_SelectASpell
selected spell -> display name + "\n\n" + description
matching rows use Highlight state 6; other rows use Normal state 1
gmEffectsUI::SetSelectedSpell @ 0x004B8290:
clicking the selected spell again clears selection
```
The installed local.dat resolves `ID_Effects_Info_SelectASpell` to
`SELECT A SPELL`. Remaining finite durations use `m:ss` below one hour and
`h:mm:ss` at one hour or more.
### End character session

View file

@ -1,4 +1,4 @@
# Retail shared main-panel placement — pseudocode
# Retail shared main-panel geometry — pseudocode
**Date:** 2026-07-17
**Symptom:** moving Inventory and then opening Character/Skills or Magic opens
@ -73,10 +73,11 @@ SetPanelVisibility(panelId, show):
requested.child.SetVisible(false)
```
The decisive placement fact is structural: Inventory, Character/Skills, and
Magic are children of one `gmPanelUI`. The switch changes child visibility;
there is no child move and no per-panel top-level position. Dragging the parent
therefore moves the one position subsequently used by every child.
The decisive geometry fact is structural: Inventory, Character Information,
Character/Skills, Magic, Effects, and the other detail pages are children of
one `gmPanelUI`. The switch changes child visibility; there is no child move,
resize, or per-panel top-level rectangle. Moving or resizing the parent
therefore changes the one rectangle subsequently used by every child.
## Resize follow-up
@ -86,9 +87,12 @@ The production end-of-retail LayoutDesc supplies the missing geometry oracle:
LayoutDesc 0x2100006E
gmPanelUI host 0x100005FE 310 x 372
content parent 0x10000180 300 x 362, anchored on all edges
Inventory child 0x1000018B 300 x 362, anchored on all edges
Character child 0x1000018E 300 x 362, anchored on all edges
Magic child 0x10000190 300 x 362, anchored on all edges
Character Information 0x10000183 panel 3, 300 x 362
Helpful Effects 0x10000184 panel 4, 300 x 362
Harmful Effects 0x10000185 panel 5, 300 x 362
Inventory child 0x1000018B panel 7, 300 x 362
Character/Skills child 0x1000018E panel 11, 300 x 362
Magic child 0x10000190 panel 13, 300 x 362
top-center 0x1000065C Type 2 Dragbar, cursor 0x06006119
bottom-center 0x10000660 Type 9 Resizebar, cursor 0x06005E66
@ -102,7 +106,8 @@ properties `0x3C..0x3F`. There is no retail 760-pixel Character limit and no
maximum captured from the host's original screen position.
acdream retains independent wrappers under IA-12, so the faithful adaptation
is to give each primary wrapper the same bottom-only vertical resize policy,
is to give each registered child wrapper the same bottom-only vertical resize
policy,
leave its maximum unbounded by an authored substitute, and calculate the
available desktop extent from its current position during the resize. The
viewport remains the physical limit; an old static maximum must not become a
@ -113,31 +118,31 @@ smaller limit after the shared panel moves.
acdream imports these child LayoutDesc roots independently, so each currently
has its own retained wrapper. `RetailPanelUiController` already ports retail's
one-active-child and restore-previous lifecycle. Complete that logical-parent
ownership by adding one canonical placement for the primary toolbar children
(Inventory `7`, Character `11`, Magic `13`):
ownership by adding one canonical rectangle for all registered main-panel
children, including Character Information `3`, Helpful/Harmful `4`/`5`,
Inventory `7`, Character/Skills `11`, and Magic `13`:
```text
on primary child frame moved:
canonicalPosition = moved frame position
move every other primary child frame to canonicalPosition
on registered child frame moved or resized:
canonicalGeometry = frame left, top, width, height
apply canonicalGeometry to every hidden sibling
before showing a primary child:
if canonicalPosition exists:
move requested child frame to canonicalPosition
before showing a registered child:
if canonicalGeometry exists:
apply canonicalGeometry to requested child frame
else:
canonicalPosition = requested child frame position
canonicalGeometry = requested child frame geometry
before hiding the active primary child:
capture its current position as canonicalPosition
before hiding the active child:
capture its current geometry as canonicalGeometry
```
The controller owns this synchronization and subscribes to typed
`RetailWindowHandle.Moved` events. Toolbar commands, keyboard commands,
restore-previous effects, persistence restore, and direct window dragging all
therefore pass through the same owner. Panel-authored size/resize policy stays
with the active child, but the three primary wrappers apply the shared host's
same vertical resize contract; only placement is synchronized between the
independent frames.
`RetailWindowHandle.Moved` and `Resized` events. Toolbar, indicator, and
keyboard commands, restore-previous effects, persistence restore, and direct
window dragging all therefore pass through the same owner. Each wrapper
retains its own imported content tree, but its outer rectangle and bottom-only
resize contract mirror the shared retail host.
ACE contains no client UI implementation, and the in-tree WorldBuilder
reference contains no `gmPanelUI` port, so neither supplies a competing