fix(ui): resolve retail spell slot background

Keep shared shortcut digit overlays separate from the per-ItemList background. Resolve the magic favorite list through its cross-layout inherited cell prototype and use the pinned brown/gold ItemSlot_Empty surface instead of the blue toolbar slot.

Co-authored-by: OpenAI Codex <codex@openai.com>
This commit is contained in:
Erik 2026-07-15 19:23:46 +02:00
parent e3605672bb
commit 09612f9981
9 changed files with 128 additions and 53 deletions

View file

@ -167,7 +167,7 @@ public sealed class RetailUiRuntime : IDisposable
private GameplayConfirmationController? _gameplayConfirmationController;
private RetailItemConfirmationController? _itemConfirmationController;
private RetailSkillTrainingConfirmationController? _skillTrainingConfirmationController;
private UiShortcutSlotGraphics? _shortcutSlotGraphics;
private UiShortcutDigitGraphics? _shortcutDigitGraphics;
private bool _disposed;
private RetailUiRuntime(RetailUiRuntimeBindings bindings)
@ -559,12 +559,12 @@ public sealed class RetailUiRuntime : IDisposable
return;
}
UiShortcutSlotGraphics shortcutGraphics = LoadShortcutSlotGraphics();
UiShortcutDigitGraphics shortcutDigits = LoadShortcutDigitGraphics();
ToolbarRuntimeBindings b = _bindings.Toolbar;
ToolbarController = Layout.ToolbarController.Bind(
layout, b.Objects, b.Shortcuts, b.ResolveIcon, b.UseItem, b.Combat,
shortcutGraphics.RegularDigits, shortcutGraphics.GhostedDigits,
shortcutGraphics.EmptyDigits, b.ItemInteraction,
shortcutDigits.RegularDigits, shortcutDigits.GhostedDigits,
shortcutDigits.EmptyDigits, b.ItemInteraction,
b.SendAddShortcut, b.SendRemoveShortcut,
toggleCombat: b.ToggleCombat,
selectItem: guid => b.Selection.Select(guid, SelectionChangeSource.Toolbar),
@ -625,9 +625,16 @@ public sealed class RetailUiRuntime : IDisposable
ElementInfo? info;
ImportedLayout? layout;
CombatUiLabels? labels;
uint favoriteEmptySprite;
lock (_bindings.Assets.DatLock)
{
info = LayoutImporter.ImportInfos(_bindings.Assets.Dats, CombatUiController.LayoutId);
favoriteEmptySprite = info is null
? 0u
: ItemListCellTemplate.ResolveEmptySprite(
_bindings.Assets.Dats,
info,
SpellcastingUiController.FavoriteListId);
var strings = new DatStringResolver(_bindings.Assets.Dats);
layout = info is null ? null : LayoutImporter.Build(
info,
@ -675,7 +682,8 @@ public sealed class RetailUiRuntime : IDisposable
_bindings.Magic.Selection,
_bindings.Magic.AddFavorite,
_bindings.Magic.RemoveFavorite,
LoadShortcutSlotGraphics());
LoadShortcutDigitGraphics(),
favoriteEmptySprite);
if (spellcasting is null)
Console.WriteLine("[M3] spellcasting: required controls missing in LayoutDesc 0x21000073.");
@ -706,7 +714,8 @@ public sealed class RetailUiRuntime : IDisposable
controller.SyncVisibility();
Console.WriteLine(spellcasting is null
? "[M2] retail combat from LayoutDesc 0x21000073; magic binding unavailable."
: "[M3] retail combat + spell bar from LayoutDesc 0x21000073.");
: $"[M3] retail combat + spell bar from LayoutDesc 0x21000073; " +
$"favorite empty=0x{favoriteEmptySprite:X8}.");
}
private void MountSpellbook()
@ -1016,13 +1025,12 @@ public sealed class RetailUiRuntime : IDisposable
$"[UI] retail DialogFactory from LayoutDesc 0x{layoutId:X8}; confirmation root 0x15.");
}
private UiShortcutSlotGraphics LoadShortcutSlotGraphics()
private UiShortcutDigitGraphics LoadShortcutDigitGraphics()
{
if (_shortcutSlotGraphics is not null)
return _shortcutSlotGraphics;
if (_shortcutDigitGraphics is not null)
return _shortcutDigitGraphics;
uint[]? regular = null, ghosted = null, empty = null;
uint emptySprite = 0u;
lock (_bindings.Assets.DatLock)
{
var layout = _bindings.Assets.Dats.Get<DatReaderWriter.DBObjs.LayoutDesc>(0x21000037u);
@ -1039,10 +1047,6 @@ public sealed class RetailUiRuntime : IDisposable
&& emptyComposite.Children.TryGetValue(0x1000034Au, out var emptyNumber)
&& emptyNumber.StateDesc?.Properties is { } emptyProps)
empty = ReadDataIds(emptyProps, 0x1000005Eu);
emptySprite = ItemListCellTemplate.ResolveEmptySprite(
_bindings.Assets.Dats,
CombatUiController.LayoutId,
SpellcastingUiController.FavoriteListId);
}
regular ??=
@ -1055,13 +1059,12 @@ public sealed class RetailUiRuntime : IDisposable
0x06001ACCu, 0x06001ACDu, 0x06001ACEu, 0x06001ACFu, 0x06001AD0u,
0x06001AD1u, 0x06001AD2u, 0x06001AD3u, 0x06001AD4u,
];
_shortcutSlotGraphics = new UiShortcutSlotGraphics(
regular, ghosted, empty, emptySprite);
_shortcutDigitGraphics = new UiShortcutDigitGraphics(
regular, ghosted, empty);
Console.WriteLine(
$"[D.5.1] shared shortcut graphics ready: regular={regular.Length}, " +
$"ghosted={ghosted.Length}, empty={empty?.Length ?? 0}, " +
$"slot=0x{emptySprite:X8}.");
return _shortcutSlotGraphics;
$"[D.5.1] shared shortcut digits ready: regular={regular.Length}, " +
$"ghosted={ghosted.Length}, empty={empty?.Length ?? 0}.");
return _shortcutDigitGraphics;
}
private static uint[]? ReadDataIds(