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,6 +2,7 @@ using AcDream.App.Spells;
|
|||
using AcDream.App.UI;
|
||||
using AcDream.App.UI.Layout;
|
||||
using AcDream.Core.Items;
|
||||
using AcDream.Core.Selection;
|
||||
using AcDream.Core.Spells;
|
||||
using System.Numerics;
|
||||
|
||||
|
|
@ -238,6 +239,130 @@ public sealed class SpellbookWindowControllerTests
|
|||
Assert.Empty(VisibleSpellIds(list));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Components_UseAuthoredCategoryAndItemTemplates_WithRetailCountsAndSelection()
|
||||
{
|
||||
ImportedLayout layout = FixtureLoader.LoadSpellbook();
|
||||
var objects = new ClientObjectTable();
|
||||
objects.AddOrUpdate(new ClientObject
|
||||
{
|
||||
ObjectId = 10u,
|
||||
WeenieClassId = 100u,
|
||||
Name = "Copper Scarab",
|
||||
Type = ItemType.SpellComponents,
|
||||
IconId = 0x06000100u,
|
||||
StackSize = 2,
|
||||
ContainerId = 1u,
|
||||
IsComponentPack = true,
|
||||
});
|
||||
objects.AddOrUpdate(new ClientObject
|
||||
{
|
||||
ObjectId = 11u,
|
||||
WeenieClassId = 101u,
|
||||
Name = "Diamond Scarab",
|
||||
Type = ItemType.SpellComponents,
|
||||
IconId = 0x06000101u,
|
||||
StackSize = 1,
|
||||
ContainerId = 1u,
|
||||
IsComponentPack = true,
|
||||
});
|
||||
objects.AddOrUpdate(new ClientObject
|
||||
{
|
||||
ObjectId = 12u,
|
||||
WeenieClassId = 200u,
|
||||
Name = "Amaranth",
|
||||
Type = ItemType.SpellComponents,
|
||||
IconId = 0x06000200u,
|
||||
StackSize = 3,
|
||||
ContainerId = 1u,
|
||||
IsComponentPack = true,
|
||||
});
|
||||
var components = new Dictionary<uint, SpellComponentDescriptor>
|
||||
{
|
||||
[100u] = new(100u, "Copper Scarab", 0u, 0x06000100u),
|
||||
[101u] = new(101u, "Diamond Scarab", 0u, 0x06000101u),
|
||||
[200u] = new(200u, "Amaranth", 1u, 0x06000200u),
|
||||
};
|
||||
Spellbook book = CreateSpellbook();
|
||||
book.SetDesiredComponent(100u, 7u);
|
||||
var selected = new List<uint>();
|
||||
var desired = new List<(uint Component, uint Amount)>();
|
||||
var selection = new SelectionState();
|
||||
|
||||
using SpellbookWindowController controller = Bind(
|
||||
layout,
|
||||
book,
|
||||
objects: objects,
|
||||
components: components,
|
||||
selection: selection,
|
||||
selectObject: objectId =>
|
||||
{
|
||||
selected.Add(objectId);
|
||||
selection.Select(objectId, SelectionChangeSource.Inventory);
|
||||
},
|
||||
setDesiredComponent: (component, amount) => desired.Add((component, amount)),
|
||||
resolveComponentIcon: icon => icon + 0x1000u)!;
|
||||
controller.ShowPage(SpellbookWindowPage.Components);
|
||||
|
||||
UiItemList list = Assert.IsType<UiItemList>(
|
||||
layout.FindElement(SpellbookWindowController.ComponentListId)?.Children.Single());
|
||||
Assert.Equal(5, list.GetNumUIItems());
|
||||
Assert.Same(
|
||||
list.Scroll,
|
||||
Assert.IsType<UiScrollbar>(
|
||||
layout.FindElement(SpellbookWindowController.ComponentScrollbarId)).Model);
|
||||
|
||||
UiTemplateListSlot scarabs = Assert.IsType<UiTemplateListSlot>(list.GetItem(0));
|
||||
UiText scarabTitle = Assert.IsType<UiText>(scarabs.Content.Root);
|
||||
Assert.Equal("SCARABS", Assert.Single(scarabTitle.LinesProvider()).Text);
|
||||
Assert.Equal(0x06001392u, scarabTitle.BackgroundSprite);
|
||||
|
||||
UiTemplateListSlot copper = Assert.IsType<UiTemplateListSlot>(list.GetItem(1));
|
||||
UiDatElement copperRoot = Assert.IsType<UiDatElement>(copper.Content.Root);
|
||||
Assert.Equal((0x06005E21u, 1), copperRoot.ActiveMedia());
|
||||
Assert.Equal("Copper Scarab", Assert.Single(
|
||||
Assert.IsType<UiText>(copper.Content.FindElement(
|
||||
ComponentBookTemplateFactory.NameId)).LinesProvider()).Text);
|
||||
Assert.Equal("2", Assert.Single(
|
||||
Assert.IsType<UiText>(copper.Content.FindElement(
|
||||
ComponentBookTemplateFactory.OwnedCountId)).LinesProvider()).Text);
|
||||
UiField desiredField = Assert.IsType<UiField>(copper.Content.FindElement(
|
||||
ComponentBookTemplateFactory.DesiredCountId));
|
||||
Assert.Equal((224f, 15f, 60f, 15f),
|
||||
(desiredField.Left, desiredField.Top, desiredField.Width, desiredField.Height));
|
||||
Assert.True(desiredField.RightAligned);
|
||||
Assert.Equal("7", desiredField.Text);
|
||||
UiElement iconHost = Assert.IsAssignableFrom<UiElement>(copper.Content.FindElement(
|
||||
ComponentBookTemplateFactory.IconId));
|
||||
Assert.Equal(
|
||||
0x06001100u,
|
||||
Assert.IsType<UiTextureElement>(Assert.Single(iconHost.Children)).Texture);
|
||||
|
||||
copper.OnEvent(new UiEvent(0u, copper, UiEventType.Click));
|
||||
Assert.Equal([10u], selected);
|
||||
Assert.True(copper.Selected);
|
||||
Assert.Equal(UiButtonStateMachine.Highlight, copperRoot.ActiveRetailStateId);
|
||||
Assert.Equal((0x06005E22u, 1), copperRoot.ActiveMedia());
|
||||
|
||||
desiredField.SetText("21");
|
||||
desiredField.OnSubmit!(desiredField.Text);
|
||||
Assert.Equal([(100u, 21u)], desired);
|
||||
desiredField.SetText("5001");
|
||||
desiredField.OnSubmit!(desiredField.Text);
|
||||
Assert.Equal("21", desiredField.Text);
|
||||
Assert.Equal([(100u, 21u)], desired);
|
||||
|
||||
UiTemplateListSlot herbs = Assert.IsType<UiTemplateListSlot>(list.GetItem(3));
|
||||
Assert.Equal("HERBS", Assert.Single(
|
||||
Assert.IsType<UiText>(herbs.Content.Root).LinesProvider()).Text);
|
||||
UiTemplateListSlot amaranth = Assert.IsType<UiTemplateListSlot>(list.GetItem(4));
|
||||
selection.Select(12u, SelectionChangeSource.Radar);
|
||||
Assert.True(amaranth.Selected);
|
||||
Assert.False(copper.Selected);
|
||||
selection.Select(0xDEADBEEFu, SelectionChangeSource.World);
|
||||
Assert.False(amaranth.Selected);
|
||||
}
|
||||
|
||||
private static SpellbookWindowController? Bind(
|
||||
ImportedLayout layout,
|
||||
Spellbook spellbook,
|
||||
|
|
@ -245,23 +370,35 @@ public sealed class SpellbookWindowControllerTests
|
|||
Action<uint>? sendFilter = null,
|
||||
Action<uint>? removeSpell = null,
|
||||
Action<string, Action<bool>>? showConfirmation = null,
|
||||
Action? close = null)
|
||||
Action? close = null,
|
||||
ClientObjectTable? objects = null,
|
||||
IReadOnlyDictionary<uint, SpellComponentDescriptor>? components = null,
|
||||
SelectionState? selection = null,
|
||||
Action<uint>? selectObject = null,
|
||||
Action<uint, uint>? setDesiredComponent = null,
|
||||
Func<uint, uint>? resolveComponentIcon = null)
|
||||
=> SpellbookWindowController.Bind(
|
||||
layout,
|
||||
spellbook,
|
||||
new ClientObjectTable(),
|
||||
objects ?? new ClientObjectTable(),
|
||||
() => 1u,
|
||||
new Dictionary<uint, SpellComponentDescriptor>(),
|
||||
components ?? new Dictionary<uint, SpellComponentDescriptor>(),
|
||||
selection ?? new SelectionState(),
|
||||
spellId => spellId + 1000u,
|
||||
iconId => iconId,
|
||||
resolveComponentIcon ?? (iconId => iconId),
|
||||
spellId => spellId == 103u ? 8 : 1,
|
||||
_ => { },
|
||||
selectObject ?? (_ => { }),
|
||||
addFavorite ?? (_ => { }),
|
||||
sendFilter ?? (_ => { }),
|
||||
removeSpell ?? (_ => { }),
|
||||
showConfirmation ?? ((_, _) => { }),
|
||||
(_, _) => { },
|
||||
setDesiredComponent ?? ((_, _) => { }),
|
||||
close ?? (() => { }),
|
||||
new ComponentBookTemplateFactory(
|
||||
FixtureLoader.LoadComponentCategoryTemplateInfos(),
|
||||
FixtureLoader.LoadComponentRowTemplateInfos(),
|
||||
_ => (0u, 0, 0),
|
||||
defaultFont: null),
|
||||
RowStyle,
|
||||
rowFont: null);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue