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
|
|
@ -60,6 +60,7 @@ public sealed class ItemInteractionController : IDisposable
|
|||
private readonly Func<uint> _playerGuid;
|
||||
private readonly Func<long> _nowMs;
|
||||
private readonly Action<uint>? _sendUse;
|
||||
private readonly Action<uint>? _sendExamine;
|
||||
private readonly Action<uint, uint>? _sendUseWithTarget;
|
||||
private readonly Action<uint, uint>? _sendWield;
|
||||
private readonly Action<uint>? _sendDrop;
|
||||
|
|
@ -90,6 +91,7 @@ public sealed class ItemInteractionController : IDisposable
|
|||
Action<uint, uint>? sendUseWithTarget,
|
||||
Action<uint, uint>? sendWield,
|
||||
Action<uint>? sendDrop,
|
||||
Action<uint>? sendExamine = null,
|
||||
Func<long>? nowMs = null,
|
||||
Action<string>? toast = null,
|
||||
InteractionState? interactionState = null,
|
||||
|
|
@ -108,6 +110,7 @@ public sealed class ItemInteractionController : IDisposable
|
|||
_objects = objects ?? throw new ArgumentNullException(nameof(objects));
|
||||
_playerGuid = playerGuid ?? throw new ArgumentNullException(nameof(playerGuid));
|
||||
_sendUse = sendUse;
|
||||
_sendExamine = sendExamine;
|
||||
_sendUseWithTarget = sendUseWithTarget;
|
||||
_sendWield = sendWield;
|
||||
_sendDrop = sendDrop;
|
||||
|
|
@ -152,6 +155,9 @@ public sealed class ItemInteractionController : IDisposable
|
|||
public bool IsTargetModeActive
|
||||
=> _interactionState.Current.Kind == InteractionModeKind.UseItemOnTarget;
|
||||
|
||||
public bool IsAnyTargetModeActive
|
||||
=> _interactionState.Current.Kind != InteractionModeKind.None;
|
||||
|
||||
public bool IsPendingSource(uint itemGuid)
|
||||
=> itemGuid != 0 && itemGuid == PendingSourceItem;
|
||||
|
||||
|
|
@ -182,7 +188,8 @@ public sealed class ItemInteractionController : IDisposable
|
|||
/// </summary>
|
||||
public ItemPrimaryClickResult OfferPrimaryClick(uint targetGuid)
|
||||
{
|
||||
if (!IsTargetModeActive)
|
||||
InteractionModeKind mode = _interactionState.Current.Kind;
|
||||
if (mode == InteractionModeKind.None)
|
||||
{
|
||||
long now = _nowMs();
|
||||
if (targetGuid != 0
|
||||
|
|
@ -193,7 +200,25 @@ public sealed class ItemInteractionController : IDisposable
|
|||
return ItemPrimaryClickResult.NotActive;
|
||||
}
|
||||
|
||||
bool accepted = AcquireTarget(targetGuid);
|
||||
bool accepted;
|
||||
switch (mode)
|
||||
{
|
||||
case InteractionModeKind.Use:
|
||||
ClearTargetMode();
|
||||
accepted = ActivateItem(targetGuid);
|
||||
break;
|
||||
case InteractionModeKind.Examine:
|
||||
ClearTargetMode();
|
||||
accepted = targetGuid != 0 && _sendExamine is not null;
|
||||
if (accepted)
|
||||
_sendExamine!(targetGuid);
|
||||
break;
|
||||
case InteractionModeKind.UseItemOnTarget:
|
||||
accepted = AcquireTarget(targetGuid);
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException($"Unknown interaction mode {mode}.");
|
||||
}
|
||||
_consumedPrimaryClickTarget = targetGuid;
|
||||
_consumedPrimaryClickMs = _nowMs();
|
||||
return accepted
|
||||
|
|
@ -204,6 +229,33 @@ public sealed class ItemInteractionController : IDisposable
|
|||
public ItemPrimaryClickResult OfferSelfPrimaryClick()
|
||||
=> OfferPrimaryClick(_playerGuid());
|
||||
|
||||
/// <summary>
|
||||
/// Retail toolbar Use button: use the current selection immediately, or arm
|
||||
/// one-shot <c>TARGET_MODE_USE</c> when no object is selected.
|
||||
/// Retail reference: <c>gmToolbarUI::ListenToElementMessage @ 0x004BEE90</c>.
|
||||
/// </summary>
|
||||
public bool UseSelectedOrEnterMode(uint selectedObjectId)
|
||||
{
|
||||
if (selectedObjectId != 0)
|
||||
return ActivateItem(selectedObjectId);
|
||||
return _interactionState.EnterUse();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retail toolbar Examine button: appraise the current selection immediately,
|
||||
/// or arm one-shot <c>TARGET_MODE_EXAMINE</c> when no object is selected.
|
||||
/// Retail reference: <c>gmToolbarUI::ListenToElementMessage @ 0x004BEE90</c>.
|
||||
/// </summary>
|
||||
public bool ExamineSelectedOrEnterMode(uint selectedObjectId)
|
||||
{
|
||||
if (selectedObjectId == 0)
|
||||
return _interactionState.EnterExamine();
|
||||
if (_sendExamine is null)
|
||||
return false;
|
||||
_sendExamine(selectedObjectId);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool ActivateItem(uint itemGuid)
|
||||
{
|
||||
if (itemGuid == 0) return false;
|
||||
|
|
@ -264,7 +316,7 @@ public sealed class ItemInteractionController : IDisposable
|
|||
|
||||
public void CancelTargetMode()
|
||||
{
|
||||
if (!IsTargetModeActive) return;
|
||||
if (!IsAnyTargetModeActive) return;
|
||||
ClearTargetMode();
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue