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

@ -847,6 +847,8 @@ public sealed class RetailUiRuntime : IDisposable
uint rootId = positive ? EffectsUiController.PositiveRootId : EffectsUiController.NegativeRootId;
ElementInfo? rootInfo;
ImportedLayout? layout;
EffectRowTemplateFactory? rowTemplates;
string selectPrompt;
lock (_bindings.Assets.DatLock)
{
rootInfo = LayoutImporter.ImportInfos(
@ -861,10 +863,20 @@ public sealed class RetailUiRuntime : IDisposable
_bindings.Assets.DefaultFont,
_bindings.Assets.ResolveFont,
new DatStringResolver(_bindings.Assets.Dats).Resolve);
rowTemplates = EffectRowTemplateFactory.TryLoad(
_bindings.Assets.Dats,
_bindings.Assets.ResolveSprite,
_bindings.Assets.DefaultFont,
_bindings.Assets.ResolveFont);
var strings = new DatStringResolver(_bindings.Assets.Dats);
selectPrompt = strings.Resolve(
0x23000001u,
DatStringResolver.ComputeHash("ID_Effects_Info_SelectASpell"))
?? "SELECT A SPELL";
}
if (rootInfo is null || layout is null)
if (rootInfo is null || layout is null || rowTemplates is null)
{
Console.WriteLine($"[M3] effects: root 0x{rootId:X8} not found.");
Console.WriteLine($"[M3] effects: root or row template for 0x{rootId:X8} not found.");
return;
}
@ -875,6 +887,8 @@ public sealed class RetailUiRuntime : IDisposable
_bindings.Magic.ServerTime,
_bindings.Assets.ResolveSprite,
_bindings.Magic.ResolveSpellIcon,
rowTemplates,
selectPrompt,
close: () => CloseWindow(
positive ? WindowNames.PositiveEffects : WindowNames.NegativeEffects));
if (controller is null)
@ -893,14 +907,17 @@ public sealed class RetailUiRuntime : IDisposable
new RetailWindowFrame.Options
{
WindowName = positive ? WindowNames.PositiveEffects : WindowNames.NegativeEffects,
Chrome = RetailWindowChrome.Imported,
Chrome = RetailWindowChrome.NineSlice,
Left = Math.Max(0f, Host.Root.Width - root.Width - 12f),
Top = 18f,
Visible = false,
Resizable = false,
ResizeX = false,
ResizeY = false,
ResizeY = true,
ResizableEdges = ResizeEdges.Bottom,
ConstrainDragToParent = true,
ConstrainResizeToParent = true,
ContentAnchors = AnchorEdges.Left | AnchorEdges.Top
| AnchorEdges.Right | AnchorEdges.Bottom,
ContentClickThrough = false,
Controller = controller,
});
@ -919,6 +936,41 @@ public sealed class RetailUiRuntime : IDisposable
MountLinkStatusPanel();
MountVitaePanel();
MountMiniGamePanel();
MountCharacterInformationPanel();
}
private void MountCharacterInformationPanel()
{
ElementInfo? rootInfo;
ImportedLayout? layout;
lock (_bindings.Assets.DatLock)
{
rootInfo = LayoutImporter.ImportInfos(
_bindings.Assets.Dats,
CharacterController.LayoutId,
CharacterController.RootId);
layout = rootInfo is null
? null
: LayoutImporter.Build(
rootInfo,
_bindings.Assets.ResolveSprite,
_bindings.Assets.DefaultFont,
_bindings.Assets.ResolveFont,
new DatStringResolver(_bindings.Assets.Dats).Resolve);
}
if (rootInfo is null || layout is null) return;
CharacterController.Bind(
layout,
_bindings.Character.Provider.BuildSheet,
_bindings.Assets.DefaultFont,
() => CloseWindow(WindowNames.CharacterInformation));
RegisterIndicatorDetailPanel(
RetailPanelCatalog.CharacterInformation,
WindowNames.CharacterInformation,
rootInfo,
layout.Root,
controller: null);
}
private void MountLinkStatusPanel()
@ -1054,7 +1106,7 @@ public sealed class RetailUiRuntime : IDisposable
string windowName,
ElementInfo rootInfo,
UiElement root,
IRetainedPanelController controller)
IRetainedPanelController? controller)
{
RetailWindowHandle handle = RetailWindowFrame.Mount(
Host.Root,
@ -1063,14 +1115,17 @@ public sealed class RetailUiRuntime : IDisposable
new RetailWindowFrame.Options
{
WindowName = windowName,
Chrome = RetailWindowChrome.Imported,
Chrome = RetailWindowChrome.NineSlice,
Left = 18f,
Top = 18f,
Visible = false,
Resizable = false,
ResizeX = false,
ResizeY = false,
ResizeY = true,
ResizableEdges = ResizeEdges.Bottom,
ConstrainDragToParent = true,
ConstrainResizeToParent = true,
ContentAnchors = AnchorEdges.Left | AnchorEdges.Top
| AnchorEdges.Right | AnchorEdges.Bottom,
ContentClickThrough = false,
Controller = controller,
});