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
|
|
@ -2,9 +2,9 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using AcDream.App.Spells;
|
||||
using AcDream.Core.Items;
|
||||
using AcDream.Core.Selection;
|
||||
using AcDream.Core.Spells;
|
||||
|
||||
namespace AcDream.App.UI.Layout;
|
||||
|
|
@ -48,6 +48,7 @@ public sealed class SpellbookWindowController : IRetainedPanelController
|
|||
private readonly ClientObjectTable _objects;
|
||||
private readonly Func<uint> _playerGuid;
|
||||
private readonly IReadOnlyDictionary<uint, SpellComponentDescriptor> _components;
|
||||
private readonly SelectionState _selection;
|
||||
private readonly Func<uint, uint> _resolveSpellIcon;
|
||||
private readonly Func<uint, uint> _resolveComponentIcon;
|
||||
private readonly Func<uint, int> _spellLevel;
|
||||
|
|
@ -66,10 +67,12 @@ public sealed class SpellbookWindowController : IRetainedPanelController
|
|||
private readonly UiButton _deleteButton;
|
||||
private readonly UiItemList _spellList;
|
||||
private readonly UiItemList _componentList;
|
||||
private readonly ComponentBookTemplateFactory _componentTemplates;
|
||||
private readonly List<(UiButton Button, uint Mask)> _filters = new();
|
||||
private readonly SpellbookRowStyle _rowStyle;
|
||||
private readonly UiDatFont? _rowFont;
|
||||
private uint? _selectedSpell;
|
||||
private uint? _selectedComponent;
|
||||
private bool _spellsDirty;
|
||||
private bool _componentsDirty;
|
||||
private bool _componentEditActive;
|
||||
|
|
@ -81,6 +84,7 @@ public sealed class SpellbookWindowController : IRetainedPanelController
|
|||
ClientObjectTable objects,
|
||||
Func<uint> playerGuid,
|
||||
IReadOnlyDictionary<uint, SpellComponentDescriptor> components,
|
||||
SelectionState selection,
|
||||
Func<uint, uint> resolveSpellIcon,
|
||||
Func<uint, uint> resolveComponentIcon,
|
||||
Func<uint, int> spellLevel,
|
||||
|
|
@ -99,6 +103,7 @@ public sealed class SpellbookWindowController : IRetainedPanelController
|
|||
UiButton deleteButton,
|
||||
UiItemList spellList,
|
||||
UiItemList componentList,
|
||||
ComponentBookTemplateFactory componentTemplates,
|
||||
SpellbookRowStyle rowStyle,
|
||||
UiDatFont? rowFont)
|
||||
{
|
||||
|
|
@ -106,6 +111,7 @@ public sealed class SpellbookWindowController : IRetainedPanelController
|
|||
_objects = objects;
|
||||
_playerGuid = playerGuid;
|
||||
_components = components;
|
||||
_selection = selection;
|
||||
_resolveSpellIcon = resolveSpellIcon;
|
||||
_resolveComponentIcon = resolveComponentIcon;
|
||||
_spellLevel = spellLevel;
|
||||
|
|
@ -124,6 +130,7 @@ public sealed class SpellbookWindowController : IRetainedPanelController
|
|||
_deleteButton = deleteButton;
|
||||
_spellList = spellList;
|
||||
_componentList = componentList;
|
||||
_componentTemplates = componentTemplates;
|
||||
_rowStyle = rowStyle;
|
||||
_rowFont = rowFont;
|
||||
|
||||
|
|
@ -148,6 +155,7 @@ public sealed class SpellbookWindowController : IRetainedPanelController
|
|||
_objects.ObjectRemoved += OnObjectRemoved;
|
||||
_objects.ObjectMoved += OnObjectMoved;
|
||||
_objects.ContainerContentsReplaced += OnContainerContentsReplaced;
|
||||
_selection.Changed += OnSelectionChanged;
|
||||
ShowPage(SpellbookWindowPage.Spells);
|
||||
RebuildAll();
|
||||
}
|
||||
|
|
@ -160,6 +168,7 @@ public sealed class SpellbookWindowController : IRetainedPanelController
|
|||
ClientObjectTable objects,
|
||||
Func<uint> playerGuid,
|
||||
IReadOnlyDictionary<uint, SpellComponentDescriptor> components,
|
||||
SelectionState selection,
|
||||
Func<uint, uint> resolveSpellIcon,
|
||||
Func<uint, uint> resolveComponentIcon,
|
||||
Func<uint, int> spellLevel,
|
||||
|
|
@ -170,6 +179,7 @@ public sealed class SpellbookWindowController : IRetainedPanelController
|
|||
Action<string, Action<bool>> showConfirmation,
|
||||
Action<uint, uint> setDesiredComponent,
|
||||
Action close,
|
||||
ComponentBookTemplateFactory componentTemplates,
|
||||
SpellbookRowStyle rowStyle,
|
||||
UiDatFont? rowFont)
|
||||
{
|
||||
|
|
@ -199,13 +209,14 @@ public sealed class SpellbookWindowController : IRetainedPanelController
|
|||
}
|
||||
|
||||
return new SpellbookWindowController(
|
||||
layout, spellbook, objects, playerGuid, components,
|
||||
layout, spellbook, objects, playerGuid, components, selection,
|
||||
resolveSpellIcon, resolveComponentIcon,
|
||||
spellLevel, selectObject,
|
||||
addFavorite, sendFilter, removeSpell, showConfirmation,
|
||||
setDesiredComponent, close,
|
||||
spellPage, componentPage, spellTab, componentTab, closeButton,
|
||||
deleteButton, spellList, componentList, rowStyle, rowFont);
|
||||
deleteButton, spellList, componentList, componentTemplates,
|
||||
rowStyle, rowFont);
|
||||
}
|
||||
|
||||
public void ShowPage(SpellbookWindowPage page)
|
||||
|
|
@ -337,40 +348,29 @@ public sealed class SpellbookWindowController : IRetainedPanelController
|
|||
if (category != nextCategory)
|
||||
{
|
||||
category = nextCategory;
|
||||
_componentList.AddItem(new UiCatalogSlot
|
||||
UiTemplateListSlot categoryRow =
|
||||
_componentTemplates.CreateCategoryRow(nextCategory);
|
||||
categoryRow.Clicked = () =>
|
||||
{
|
||||
EntryId = uint.MaxValue,
|
||||
Label = ComponentCategoryName(nextCategory),
|
||||
ShowLabel = true,
|
||||
SpriteResolve = _componentList.SpriteResolve,
|
||||
});
|
||||
_selectedComponent = null;
|
||||
SyncComponentSelection();
|
||||
_selectObject(0u);
|
||||
};
|
||||
_componentList.AddItem(categoryRow);
|
||||
}
|
||||
uint desired = _spellbook.DesiredComponents.TryGetValue(componentId, out uint amount) ? amount : 0u;
|
||||
var row = new UiCatalogSlot
|
||||
{
|
||||
EntryId = componentId,
|
||||
CatalogIconTexture = _resolveComponentIcon(
|
||||
pack.IconId != 0 ? pack.IconId : descriptor?.IconId ?? 0u),
|
||||
Label = string.IsNullOrWhiteSpace(pack.Name) ? descriptor?.Name ?? $"Component {componentId}" : pack.Name,
|
||||
Detail = packGroup.Quantity.ToString(CultureInfo.InvariantCulture),
|
||||
ShowLabel = true,
|
||||
SpriteResolve = _componentList.SpriteResolve,
|
||||
};
|
||||
var desiredField = new UiField
|
||||
{
|
||||
Left = width - 47f,
|
||||
Top = 4f,
|
||||
Width = 43f,
|
||||
Height = 24f,
|
||||
MaxCharacters = 4,
|
||||
ClearOnSubmit = false,
|
||||
RecordHistory = false,
|
||||
SelectAllOnFocus = true,
|
||||
RightAligned = true,
|
||||
CharacterFilter = char.IsAsciiDigit,
|
||||
BackgroundColor = new Vector4(0f, 0f, 0f, 0.65f),
|
||||
};
|
||||
desiredField.SetText(desired.ToString(CultureInfo.InvariantCulture));
|
||||
string componentName = string.IsNullOrWhiteSpace(pack.Name)
|
||||
? descriptor?.Name ?? $"Component {componentId}"
|
||||
: pack.Name;
|
||||
ComponentBookTemplateFactory.ComponentRow row =
|
||||
_componentTemplates.CreateComponentRow(
|
||||
componentId,
|
||||
_resolveComponentIcon(
|
||||
pack.IconId != 0 ? pack.IconId : descriptor?.IconId ?? 0u),
|
||||
componentName,
|
||||
packGroup.Quantity,
|
||||
desired);
|
||||
UiField desiredField = row.DesiredField;
|
||||
desiredField.OnFocusGained = () => _componentEditActive = true;
|
||||
uint committed = desired;
|
||||
void Commit(string value)
|
||||
|
|
@ -392,25 +392,18 @@ public sealed class SpellbookWindowController : IRetainedPanelController
|
|||
Commit(value);
|
||||
_componentEditActive = false;
|
||||
};
|
||||
row.AddChild(desiredField);
|
||||
row.Clicked = () => _selectObject(pack.ObjectId);
|
||||
_componentList.AddItem(row);
|
||||
row.Slot.Clicked = () =>
|
||||
{
|
||||
_selectedComponent = componentId;
|
||||
SyncComponentSelection();
|
||||
_selectObject(pack.ObjectId);
|
||||
};
|
||||
_componentList.AddItem(row.Slot);
|
||||
}
|
||||
}
|
||||
SyncComponentSelectionFromWorld();
|
||||
}
|
||||
|
||||
private static string ComponentCategoryName(uint category) => category switch
|
||||
{
|
||||
0u => "Scarabs",
|
||||
1u => "Herbs",
|
||||
2u => "Powdered Gems",
|
||||
3u => "Alchemical Substances",
|
||||
4u => "Talismans",
|
||||
5u => "Tapers",
|
||||
6u => "Prismatic Components",
|
||||
_ => "Other Components",
|
||||
};
|
||||
|
||||
private bool IsOwnedByPlayer(ClientObject item, uint playerGuid)
|
||||
{
|
||||
if (playerGuid == 0) return item.ContainerId != 0 || item.WielderId != 0;
|
||||
|
|
@ -479,6 +472,23 @@ public sealed class SpellbookWindowController : IRetainedPanelController
|
|||
slot.Selected = slot.EntryId == _selectedSpell;
|
||||
}
|
||||
|
||||
private void SyncComponentSelection()
|
||||
{
|
||||
for (int i = 0; i < _componentList.GetNumUIItems(); i++)
|
||||
if (_componentList.GetItem(i) is UiTemplateListSlot slot)
|
||||
slot.SetSelected(slot.EntryId == _selectedComponent);
|
||||
}
|
||||
|
||||
private void SyncComponentSelectionFromWorld()
|
||||
{
|
||||
_selectedComponent = _selection.SelectedObjectId is uint selected
|
||||
&& _objects.Get(selected) is { } item
|
||||
&& IsComponent(item)
|
||||
? item.WeenieClassId
|
||||
: null;
|
||||
SyncComponentSelection();
|
||||
}
|
||||
|
||||
public void Tick()
|
||||
{
|
||||
if (_spellsDirty)
|
||||
|
|
@ -505,6 +515,7 @@ public sealed class SpellbookWindowController : IRetainedPanelController
|
|||
}
|
||||
private void OnObjectMoved(ClientObjectMove _) => _componentsDirty = true;
|
||||
private void OnContainerContentsReplaced(uint _) => _componentsDirty = true;
|
||||
private void OnSelectionChanged(SelectionTransition _) => SyncComponentSelectionFromWorld();
|
||||
private bool IsComponent(ClientObject item)
|
||||
=> item.IsComponentPack || _components.ContainsKey(item.WeenieClassId);
|
||||
public void OnShown() => RebuildAll();
|
||||
|
|
@ -520,6 +531,7 @@ public sealed class SpellbookWindowController : IRetainedPanelController
|
|||
_objects.ObjectRemoved -= OnObjectRemoved;
|
||||
_objects.ObjectMoved -= OnObjectMoved;
|
||||
_objects.ContainerContentsReplaced -= OnContainerContentsReplaced;
|
||||
_selection.Changed -= OnSelectionChanged;
|
||||
RetailTabBinding.SetClick(_spellTab, null);
|
||||
RetailTabBinding.SetClick(_componentTab, null);
|
||||
_closeButton.OnClick = null;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue