feat(ui): finish retail toolbar controls
Discover all seven panel launchers from their DAT panel-id attributes, route mounted panels through event-driven retained window state, and ghost unavailable panels. Port Use and Examine selection/target behavior with exact Appraise dispatch and retail cursor modes. Co-Authored-By: Codex <codex@openai.com>
This commit is contained in:
parent
21fefce0e0
commit
d3d1c895a0
19 changed files with 478 additions and 72 deletions
|
|
@ -20,6 +20,18 @@ public class ToolbarControllerTests
|
|||
private static readonly uint[] CombatIds = { 0x10000192u, 0x10000193u, 0x10000194u, 0x10000195u };
|
||||
private const uint CharacterButtonId = 0x10000199u;
|
||||
private const uint InventoryButtonId = 0x100001B1u;
|
||||
private const uint UseButtonId = 0x1000019Du;
|
||||
private const uint ExamineButtonId = 0x100001A5u;
|
||||
private static readonly (uint ElementId, uint PanelId)[] PanelButtons =
|
||||
{
|
||||
(0x10000197u, 12u),
|
||||
(0x10000198u, 13u),
|
||||
(CharacterButtonId, RetailPanelCatalog.Character),
|
||||
(0x1000055Au, 25u),
|
||||
(0x1000019Au, 16u),
|
||||
(0x1000019Bu, 10u),
|
||||
(InventoryButtonId, RetailPanelCatalog.Inventory),
|
||||
};
|
||||
|
||||
private static (ImportedLayout layout, Dictionary<uint, UiItemList> slots,
|
||||
Dictionary<uint, UiElement> indicators) FakeToolbar()
|
||||
|
|
@ -37,8 +49,10 @@ public class ToolbarControllerTests
|
|||
AddButton(id);
|
||||
indicators[id] = dict[id];
|
||||
}
|
||||
AddButton(CharacterButtonId);
|
||||
AddButton(InventoryButtonId);
|
||||
foreach (var (elementId, panelId) in PanelButtons)
|
||||
AddButton(elementId, panelId);
|
||||
AddButton(UseButtonId);
|
||||
AddButton(ExamineButtonId);
|
||||
return (new ImportedLayout(root, dict), slots, indicators);
|
||||
|
||||
void AddSlot(uint id)
|
||||
|
|
@ -47,7 +61,7 @@ public class ToolbarControllerTests
|
|||
dict[id] = list; slots[id] = list; root.AddChild(list);
|
||||
}
|
||||
|
||||
void AddButton(uint id)
|
||||
void AddButton(uint id, uint? panelId = null)
|
||||
{
|
||||
var info = new ElementInfo
|
||||
{
|
||||
|
|
@ -59,6 +73,25 @@ public class ToolbarControllerTests
|
|||
};
|
||||
info.StateMedia["Normal"] = (0x1u, 1);
|
||||
info.StateMedia["Highlight"] = (0x2u, 1);
|
||||
info.StateMedia["Ghosted"] = (0x3u, 1);
|
||||
if (panelId is { } value)
|
||||
{
|
||||
info.States[UiStateInfo.DirectStateId] = new UiStateInfo
|
||||
{
|
||||
Id = UiStateInfo.DirectStateId,
|
||||
Properties = new UiPropertyBag
|
||||
{
|
||||
Values =
|
||||
{
|
||||
[0x10000029u] = new UiPropertyValue
|
||||
{
|
||||
Kind = UiPropertyKind.Enum,
|
||||
UnsignedValue = value,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
var button = new UiButton(info, _ => (0u, 0, 0)) { Width = 32, Height = 32 };
|
||||
dict[id] = button;
|
||||
root.AddChild(button);
|
||||
|
|
@ -140,25 +173,30 @@ public class ToolbarControllerTests
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void WindowToggleButtons_clickCallbacks_fire()
|
||||
public void PanelButtons_bindByDatPanelId_andGhostUnavailablePanels()
|
||||
{
|
||||
var (layout, _, _) = FakeToolbar();
|
||||
var repo = new ClientObjectTable();
|
||||
int inventoryClicks = 0;
|
||||
int characterClicks = 0;
|
||||
var toggles = new List<uint>();
|
||||
|
||||
var ctrl = ToolbarController.Bind(layout, repo,
|
||||
() => Array.Empty<ShortcutEntry>(),
|
||||
iconIds: (_,_,_,_,_) => 0u, useItem: _ => { });
|
||||
ctrl.BindWindowToggles(
|
||||
toggleInventory: () => inventoryClicks++,
|
||||
toggleCharacter: () => characterClicks++);
|
||||
ctrl.BindPanelButtons(
|
||||
panelId => panelId is RetailPanelCatalog.Inventory or RetailPanelCatalog.Character,
|
||||
toggles.Add);
|
||||
|
||||
((UiButton)layout.FindElement(InventoryButtonId)!).OnEvent(new UiEvent(0u, null, UiEventType.Click));
|
||||
((UiButton)layout.FindElement(CharacterButtonId)!).OnEvent(new UiEvent(0u, null, UiEventType.Click));
|
||||
|
||||
Assert.Equal(1, inventoryClicks);
|
||||
Assert.Equal(1, characterClicks);
|
||||
Assert.Equal(new[] { RetailPanelCatalog.Inventory, RetailPanelCatalog.Character }, toggles);
|
||||
foreach (var (elementId, panelId) in PanelButtons)
|
||||
{
|
||||
var button = (UiButton)layout.FindElement(elementId)!;
|
||||
Assert.Equal(
|
||||
panelId is RetailPanelCatalog.Inventory or RetailPanelCatalog.Character,
|
||||
button.Enabled);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -195,9 +233,9 @@ public class ToolbarControllerTests
|
|||
iconIds: (_,_,_,_,_) => 0u,
|
||||
useItem: _ => { },
|
||||
itemInteraction: interaction);
|
||||
ctrl.BindWindowToggles(
|
||||
toggleInventory: () => inventoryClicks++,
|
||||
toggleCharacter: () => { });
|
||||
ctrl.BindPanelButtons(
|
||||
panelId => panelId == RetailPanelCatalog.Inventory,
|
||||
_ => inventoryClicks++);
|
||||
interaction.ActivateItem(kit);
|
||||
|
||||
((UiButton)layout.FindElement(InventoryButtonId)!).OnEvent(new UiEvent(0u, null, UiEventType.Click));
|
||||
|
|
@ -271,19 +309,82 @@ public class ToolbarControllerTests
|
|||
var inventoryButton = (UiButton)layout.FindElement(InventoryButtonId)!;
|
||||
var characterButton = (UiButton)layout.FindElement(CharacterButtonId)!;
|
||||
|
||||
ctrl.SetInventoryOpen(true);
|
||||
ctrl.SetCharacterOpen(true);
|
||||
ctrl.BindPanelButtons(_ => true, _ => { });
|
||||
ctrl.SetPanelOpen(RetailPanelCatalog.Inventory, true);
|
||||
ctrl.SetPanelOpen(RetailPanelCatalog.Character, true);
|
||||
|
||||
Assert.Equal("Highlight", inventoryButton.ActiveState);
|
||||
Assert.Equal("Highlight", characterButton.ActiveState);
|
||||
|
||||
ctrl.SetInventoryOpen(false);
|
||||
ctrl.SetCharacterOpen(false);
|
||||
ctrl.SetPanelOpen(RetailPanelCatalog.Inventory, false);
|
||||
ctrl.SetPanelOpen(RetailPanelCatalog.Character, false);
|
||||
|
||||
Assert.Equal("Normal", inventoryButton.ActiveState);
|
||||
Assert.Equal("Normal", characterButton.ActiveState);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RetailFixture_panelButtonsExposeExactDatPanelIds()
|
||||
{
|
||||
ImportedLayout layout = FixtureLoader.LoadToolbar();
|
||||
|
||||
foreach (var (elementId, panelId) in PanelButtons)
|
||||
{
|
||||
var button = Assert.IsType<UiButton>(layout.FindElement(elementId));
|
||||
Assert.True(button.TryGetEnumAttribute(0x10000029u, out uint actual));
|
||||
Assert.Equal(panelId, actual);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UseAndExamineButtons_dispatchSelectedObjectPolicy()
|
||||
{
|
||||
const uint player = 0x50000001u;
|
||||
const uint pack = 0x50000002u;
|
||||
const uint item = 0x50000003u;
|
||||
var (layout, _, _) = FakeToolbar();
|
||||
var repo = new ClientObjectTable();
|
||||
repo.AddOrUpdate(new ClientObject { ObjectId = player, Type = ItemType.Creature });
|
||||
repo.AddOrUpdate(new ClientObject { ObjectId = pack, Type = ItemType.Container });
|
||||
repo.MoveItem(pack, player, 0);
|
||||
repo.AddOrUpdate(new ClientObject
|
||||
{
|
||||
ObjectId = item,
|
||||
Type = ItemType.Misc,
|
||||
Useability = ItemUseability.Contained,
|
||||
});
|
||||
repo.MoveItem(item, pack, 0);
|
||||
var uses = new List<uint>();
|
||||
var examines = new List<uint>();
|
||||
uint selected = item;
|
||||
var interaction = new ItemInteractionController(
|
||||
repo,
|
||||
() => player,
|
||||
sendUse: uses.Add,
|
||||
sendUseWithTarget: null,
|
||||
sendWield: null,
|
||||
sendDrop: null,
|
||||
sendExamine: examines.Add);
|
||||
ToolbarController.Bind(
|
||||
layout,
|
||||
repo,
|
||||
() => Array.Empty<ShortcutEntry>(),
|
||||
iconIds: (_, _, _, _, _) => 0u,
|
||||
useItem: _ => { },
|
||||
itemInteraction: interaction,
|
||||
selectedObjectId: () => selected);
|
||||
|
||||
((UiButton)layout.FindElement(UseButtonId)!).OnClick!.Invoke();
|
||||
((UiButton)layout.FindElement(ExamineButtonId)!).OnClick!.Invoke();
|
||||
|
||||
Assert.Equal(new[] { item }, uses);
|
||||
Assert.Equal(new[] { item }, examines);
|
||||
|
||||
selected = 0;
|
||||
((UiButton)layout.FindElement(ExamineButtonId)!).OnClick!.Invoke();
|
||||
Assert.Equal(InteractionModeKind.Examine, interaction.InteractionState.Current.Kind);
|
||||
}
|
||||
|
||||
// ── C1: combat-mode indicator tests ─────────────────────────────────────
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue