fix(client): restore retail interaction parity
All checks were successful
CI / linux-portable (push) Successful in 3m27s
CI / windows-gate (push) Successful in 6m42s
CI / release (push) Successful in 2m12s

Harden keyboard and camera routing, inventory and vendor interactions, chat/emotes, relog portal flow, and paperdoll rendering. Add retail research, connected gate coverage, and release-gate validation.
This commit is contained in:
Erik 2026-08-26 20:45:11 +02:00
parent 0c699240e0
commit f6fe0f2a4f
151 changed files with 10162 additions and 1211 deletions

View file

@ -115,7 +115,7 @@ public static class DatWidgetFactory
// pre-OP2 UiDatElement fallback (media drawn, ClickThrough=true, state
// propagation) because nothing ever activates it.
5 => new UiTemplateListBox(info, resolve, info.TemplateList, info.ScrollbarElementId),
6 => new UiMenu(), // UIElement_Menu (reg :120163)
6 => BuildMenu(info, resolve, elementFont, fontResolve), // UIElement_Menu (reg :120163)
7 => BuildMeter(info, resolve, elementFont, stringResolve), // UIElement_Meter
// UIElement_Panel (Type 8) — retail's tab-strip host (dat property 0x2E;
// research doc §1.3/§10.1). OP2 rework (docs/research/2026-08-11-op2-
@ -133,6 +133,7 @@ public static class DatWidgetFactory
11 => BuildScrollbar(info, resolve), // UIElement_Scrollbar (reg :124137)
12 => BuildText(info, resolve, elementFont, stringResolve), // UIElement_Text
0x13 => new UiDialogRoot(), // ConfirmationDialog
0x14 => new UiDialogRoot(), // ConfirmationMenuDialog
0x15 => new UiDialogRoot(), // ConfirmationTextInputDialog
0x17 => new UiDialogRoot(), // MessageDialog
0x19 => new UiDialogRoot(), // WaitDialog (catalog root 0x31 — OP8 #396)
@ -163,7 +164,7 @@ public static class DatWidgetFactory
// ArrowCapClosedSprite doc comment). Built blank, exactly like the Type-6
// case above — a page controller wires its sprites/items the same way
// ChatWindowController wires the channel menu.
0x10000038u => new UiMenu(),
0x10000038u => BuildMenu(info, resolve, elementFont, fontResolve),
// UIOption_CheckboxBitfield64 (Type 0x10000044): the Chat tab's per-window
// text-filter block. OP2 rework (docs/research/2026-08-11-op2-review-
// mechanism.md MUST-FIX 4): the authored template (0x10000520) DOES author
@ -209,6 +210,42 @@ public static class DatWidgetFactory
return e;
}
/// <summary>
/// Retail's generic menu class supplies its standard face/popup chrome even
/// when no game-specific controller customizes it. This matters for catalog
/// dialogs such as ConfirmationMenu: their Type-6 leaf is the whole control,
/// and <see cref="UiMenu.ConsumesDatChildren"/> intentionally absorbs the
/// authored label child. Existing chat/vendor/options controllers overwrite
/// these defaults with their own probed variants.
/// </summary>
private static UiMenu BuildMenu(
ElementInfo info,
Func<uint, (uint, int, int)> resolve,
UiDatFont? elementFont,
Func<uint, UiDatFont?>? fontResolve)
{
ElementInfo? label = info.Children.FirstOrDefault(
static child => child.Type == 12u);
UiDatFont? labelFont = label is { FontDid: not 0u } && fontResolve is not null
? fontResolve(label.FontDid) ?? elementFont
: elementFont;
var menu = new UiMenu
{
SpriteResolve = resolve,
DatFont = labelFont,
ButtonDatFont = labelFont,
NormalSprite = 0x06004D65u,
PressedSprite = 0x06004D66u,
PopupBgSprite = 0x0600124Cu,
ItemNormalSprite = 0x0600124Eu,
ItemHighlightSprite = 0x0600124Du,
ButtonTextCentered = label?.HJustify == HJustify.Center,
};
if (label?.FontColor is { } color)
menu.TextColor = color;
return menu;
}
/// <summary>
/// Bind inherited scrollbar media structurally. Property 0x77 names the
/// increment button and 0x78 the decrement button; retail