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
|
|
@ -45,13 +45,19 @@ public sealed class ToolbarController : IItemListDragHandler, IRetainedPanelCont
|
|||
private static readonly uint[] CombatIndicatorIds =
|
||||
{ 0x10000192u, 0x10000193u, 0x10000194u, 0x10000195u };
|
||||
|
||||
private const uint CharacterButtonId = 0x10000199u;
|
||||
private const uint PanelIdAttribute = 0x10000029u;
|
||||
private const uint UseButtonId = 0x1000019Du;
|
||||
private const uint ExamineButtonId = 0x100001A5u;
|
||||
private const uint InventoryButtonId = 0x100001B1u;
|
||||
private static readonly uint[] PanelButtonIds =
|
||||
{ 0x10000197u, 0x10000198u, 0x10000199u, 0x1000055Au, 0x1000019Au, 0x1000019Bu, InventoryButtonId };
|
||||
|
||||
private readonly UiItemList?[] _slots = new UiItemList?[SlotIds.Length];
|
||||
private readonly UiElement?[] _combatIndicators = new UiElement?[CombatIndicatorIds.Length];
|
||||
private readonly UiButton? _characterButton;
|
||||
private readonly UiButton? _inventoryButton;
|
||||
private readonly UiButton? _useButton;
|
||||
private readonly UiButton? _examineButton;
|
||||
private readonly List<(uint PanelId, UiButton Button)> _panelButtons = new();
|
||||
private readonly ClientObjectTable _repo;
|
||||
private readonly CombatState? _combatState;
|
||||
private readonly Func<IReadOnlyList<ShortcutEntry>> _shortcuts;
|
||||
|
|
@ -63,6 +69,7 @@ public sealed class ToolbarController : IItemListDragHandler, IRetainedPanelCont
|
|||
private readonly Action<uint>? _sendRemoveShortcut; // (index)
|
||||
private readonly ItemInteractionController? _itemInteraction;
|
||||
private readonly Action<uint>? _selectItem;
|
||||
private readonly Func<uint> _selectedObjectId;
|
||||
private readonly Func<uint>? _playerGuid;
|
||||
private readonly Action<uint, uint, int>? _sendPutItemInContainer;
|
||||
private bool _disposed;
|
||||
|
|
@ -94,6 +101,7 @@ public sealed class ToolbarController : IItemListDragHandler, IRetainedPanelCont
|
|||
Action<uint>? sendRemoveShortcut = null,
|
||||
Action? toggleCombat = null,
|
||||
Action<uint>? selectItem = null,
|
||||
Func<uint>? selectedObjectId = null,
|
||||
Func<uint>? playerGuid = null,
|
||||
Action<uint, uint, int>? sendPutItemInContainer = null)
|
||||
{
|
||||
|
|
@ -109,6 +117,7 @@ public sealed class ToolbarController : IItemListDragHandler, IRetainedPanelCont
|
|||
_sendAddShortcut = sendAddShortcut;
|
||||
_sendRemoveShortcut = sendRemoveShortcut;
|
||||
_selectItem = selectItem;
|
||||
_selectedObjectId = selectedObjectId ?? (() => 0u);
|
||||
_playerGuid = playerGuid;
|
||||
_sendPutItemInContainer = sendPutItemInContainer;
|
||||
|
||||
|
|
@ -140,7 +149,13 @@ public sealed class ToolbarController : IItemListDragHandler, IRetainedPanelCont
|
|||
button.OnClick = toggleCombat;
|
||||
}
|
||||
|
||||
_characterButton = layout.FindElement(CharacterButtonId) as UiButton;
|
||||
foreach (uint elementId in PanelButtonIds)
|
||||
{
|
||||
if (layout.FindElement(elementId) is UiButton panelButton
|
||||
&& panelButton.TryGetEnumAttribute(PanelIdAttribute, out uint panelId))
|
||||
_panelButtons.Add((panelId, panelButton));
|
||||
}
|
||||
|
||||
_inventoryButton = layout.FindElement(InventoryButtonId) as UiButton;
|
||||
if (_inventoryButton is not null)
|
||||
{
|
||||
|
|
@ -152,6 +167,13 @@ public sealed class ToolbarController : IItemListDragHandler, IRetainedPanelCont
|
|||
_inventoryButton.OnItemDrop = HandleInventoryButtonDrop;
|
||||
}
|
||||
|
||||
_useButton = layout.FindElement(UseButtonId) as UiButton;
|
||||
_examineButton = layout.FindElement(ExamineButtonId) as UiButton;
|
||||
if (_useButton is not null)
|
||||
_useButton.OnClick = () => _itemInteraction?.UseSelectedOrEnterMode(_selectedObjectId());
|
||||
if (_examineButton is not null)
|
||||
_examineButton.OnClick = () => _itemInteraction?.ExamineSelectedOrEnterMode(_selectedObjectId());
|
||||
|
||||
// Port of gmToolbarUI::RecvNotice_SetCombatMode (acclient_2013_pseudo_c.txt:196632-196669):
|
||||
// exactly one indicator visible at a time. Default to NonCombat (peace) — the player
|
||||
// always spawns in peace mode; retail has not yet called SetVisible when PostInit runs.
|
||||
|
|
@ -240,42 +262,54 @@ public sealed class ToolbarController : IItemListDragHandler, IRetainedPanelCont
|
|||
Action<uint>? sendRemoveShortcut = null,
|
||||
Action? toggleCombat = null,
|
||||
Action<uint>? selectItem = null,
|
||||
Func<uint>? selectedObjectId = null,
|
||||
Func<uint>? playerGuid = null,
|
||||
Action<uint, uint, int>? sendPutItemInContainer = null)
|
||||
{
|
||||
var c = new ToolbarController(layout, repo, shortcuts, iconIds, useItem, combatState,
|
||||
peaceDigits, warDigits, emptyDigits, itemInteraction,
|
||||
sendAddShortcut, sendRemoveShortcut, toggleCombat, selectItem,
|
||||
playerGuid, sendPutItemInContainer);
|
||||
selectedObjectId, playerGuid, sendPutItemInContainer);
|
||||
c.Populate();
|
||||
return c;
|
||||
}
|
||||
|
||||
/// <summary>Wire the retail status-bar launch buttons to top-level window toggles.</summary>
|
||||
public void BindWindowToggles(Action? toggleInventory, Action? toggleCharacter)
|
||||
/// <summary>
|
||||
/// Binds all seven authored panel buttons by DAT panel id. Buttons whose panel
|
||||
/// is not registered are ghosted, matching retail unavailable-panel behavior.
|
||||
/// </summary>
|
||||
public void BindPanelButtons(Func<uint, bool> isAvailable, Action<uint> togglePanel)
|
||||
{
|
||||
if (_inventoryButton is not null)
|
||||
ArgumentNullException.ThrowIfNull(isAvailable);
|
||||
ArgumentNullException.ThrowIfNull(togglePanel);
|
||||
|
||||
foreach (var (panelId, button) in _panelButtons)
|
||||
{
|
||||
_inventoryButton.OnClick = () =>
|
||||
bool available = isAvailable(panelId);
|
||||
button.Enabled = available;
|
||||
button.OnClick = !available ? null : () =>
|
||||
{
|
||||
if (_itemInteraction?.AcquireSelfTarget() == true) return;
|
||||
toggleInventory?.Invoke();
|
||||
if (ReferenceEquals(button, _inventoryButton)
|
||||
&& _itemInteraction?.OfferSelfPrimaryClick()
|
||||
is not null and not ItemPrimaryClickResult.NotActive)
|
||||
return;
|
||||
togglePanel(panelId);
|
||||
};
|
||||
}
|
||||
if (_characterButton is not null)
|
||||
_characterButton.OnClick = toggleCharacter;
|
||||
}
|
||||
|
||||
public void SetInventoryOpen(bool open) => SetButtonOpen(_inventoryButton, open);
|
||||
|
||||
public void SetCharacterOpen(bool open) => SetButtonOpen(_characterButton, open);
|
||||
|
||||
private static void SetButtonOpen(UiButton? button, bool open)
|
||||
public void SetPanelOpen(uint panelId, bool open)
|
||||
{
|
||||
if (button is not null)
|
||||
button.TrySetRetailState(open
|
||||
// gmToolbarUI::RecvNotice_SetPanelVisibility @ 0x004BD300 maps visible
|
||||
// to state 6 (Highlight) and hidden to state 1 (Normal).
|
||||
foreach (var entry in _panelButtons)
|
||||
{
|
||||
if (entry.PanelId != panelId || !entry.Button.Enabled) continue;
|
||||
entry.Button.TrySetRetailState(open
|
||||
? UiButtonStateMachine.Highlight
|
||||
: UiButtonStateMachine.Normal);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -584,6 +618,21 @@ public sealed class ToolbarController : IItemListDragHandler, IRetainedPanelCont
|
|||
if (_disposed) return;
|
||||
_disposed = true;
|
||||
|
||||
foreach (var (_, button) in _panelButtons)
|
||||
button.OnClick = null;
|
||||
foreach (var indicator in _combatIndicators)
|
||||
if (indicator is UiButton button)
|
||||
button.OnClick = null;
|
||||
if (_useButton is not null)
|
||||
_useButton.OnClick = null;
|
||||
if (_examineButton is not null)
|
||||
_examineButton.OnClick = null;
|
||||
if (_inventoryButton is not null)
|
||||
{
|
||||
_inventoryButton.OnItemDragOver = null;
|
||||
_inventoryButton.OnItemDrop = null;
|
||||
}
|
||||
|
||||
if (_combatState is not null)
|
||||
_combatState.CombatModeChanged -= SetCombatMode;
|
||||
_repo.ObjectAdded -= OnRepositoryObjectChanged;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue