show focus target on visibility hover

This commit is contained in:
2025-12-03 13:59:30 +09:00
parent c335489cee
commit 1c36db97dc
4 changed files with 149 additions and 9 deletions

View File

@@ -14,6 +14,7 @@ using LightlessSync.Interop;
using LightlessSync.LightlessConfiguration; using LightlessSync.LightlessConfiguration;
using LightlessSync.PlayerData.Factories; using LightlessSync.PlayerData.Factories;
using LightlessSync.PlayerData.Handlers; using LightlessSync.PlayerData.Handlers;
using LightlessSync.PlayerData.Pairs;
using LightlessSync.Services.ActorTracking; using LightlessSync.Services.ActorTracking;
using LightlessSync.Services.Mediator; using LightlessSync.Services.Mediator;
using LightlessSync.Utils; using LightlessSync.Utils;
@@ -41,10 +42,13 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
private readonly ILogger<DalamudUtilService> _logger; private readonly ILogger<DalamudUtilService> _logger;
private readonly IObjectTable _objectTable; private readonly IObjectTable _objectTable;
private readonly ActorObjectService _actorObjectService; private readonly ActorObjectService _actorObjectService;
private readonly ITargetManager _targetManager;
private readonly PerformanceCollectorService _performanceCollector; private readonly PerformanceCollectorService _performanceCollector;
private readonly LightlessConfigService _configService; private readonly LightlessConfigService _configService;
private readonly PlayerPerformanceConfigService _playerPerformanceConfigService; private readonly PlayerPerformanceConfigService _playerPerformanceConfigService;
private readonly Lazy<PairFactory> _pairFactory; private readonly Lazy<PairFactory> _pairFactory;
private PairUniqueIdentifier? _FocusPairIdent;
private IGameObject? _FocusOriginalTarget;
private uint? _classJobId = 0; private uint? _classJobId = 0;
private DateTime _delayedFrameworkUpdateCheck = DateTime.UtcNow; private DateTime _delayedFrameworkUpdateCheck = DateTime.UtcNow;
private string _lastGlobalBlockPlayer = string.Empty; private string _lastGlobalBlockPlayer = string.Empty;
@@ -68,6 +72,7 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
_gameData = gameData; _gameData = gameData;
_gameConfig = gameConfig; _gameConfig = gameConfig;
_actorObjectService = actorObjectService; _actorObjectService = actorObjectService;
_targetManager = targetManager;
_blockedCharacterHandler = blockedCharacterHandler; _blockedCharacterHandler = blockedCharacterHandler;
Mediator = mediator; Mediator = mediator;
_performanceCollector = performanceCollector; _performanceCollector = performanceCollector;
@@ -125,20 +130,24 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
mediator.Subscribe<TargetPairMessage>(this, (msg) => mediator.Subscribe<TargetPairMessage>(this, (msg) =>
{ {
if (clientState.IsPvP) return; if (clientState.IsPvP) return;
var pair = _pairFactory.Value.Create(msg.Pair.UniqueIdent) ?? msg.Pair; if (!ResolvePairAddress(msg.Pair, out var pair, out var addr)) return;
var name = pair.PlayerName;
if (string.IsNullOrEmpty(name)) return;
if (!_actorObjectService.TryGetPlayerByName(name, out var descriptor))
return;
var addr = descriptor.Address;
if (addr == nint.Zero) return;
var useFocusTarget = _configService.Current.UseFocusTarget; var useFocusTarget = _configService.Current.UseFocusTarget;
_ = RunOnFrameworkThread(() => _ = RunOnFrameworkThread(() =>
{ {
var gameObject = CreateGameObject(addr);
if (gameObject is null) return;
if (useFocusTarget) if (useFocusTarget)
targetManager.FocusTarget = CreateGameObject(addr); {
_targetManager.FocusTarget = gameObject;
if (_FocusPairIdent.HasValue && _FocusPairIdent.Value.Equals(pair.UniqueIdent))
{
_FocusOriginalTarget = _targetManager.FocusTarget;
}
}
else else
targetManager.Target = CreateGameObject(addr); {
_targetManager.Target = gameObject;
}
}).ConfigureAwait(false); }).ConfigureAwait(false);
}); });
IsWine = Util.IsWine(); IsWine = Util.IsWine();
@@ -148,6 +157,61 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
private Lazy<ulong> RebuildCID() => new(GetCID); private Lazy<ulong> RebuildCID() => new(GetCID);
public bool IsWine { get; init; } public bool IsWine { get; init; }
private bool ResolvePairAddress(Pair pair, out Pair resolvedPair, out nint address)
{
resolvedPair = _pairFactory.Value.Create(pair.UniqueIdent) ?? pair;
address = nint.Zero;
var name = resolvedPair.PlayerName;
if (string.IsNullOrEmpty(name)) return false;
if (!_actorObjectService.TryGetPlayerByName(name, out var descriptor))
return false;
address = descriptor.Address;
return address != nint.Zero;
}
public void FocusVisiblePair(Pair pair)
{
if (_clientState.IsPvP) return;
if (!ResolvePairAddress(pair, out var resolvedPair, out var address)) return;
_ = RunOnFrameworkThread(() => FocusPairUnsafe(address, resolvedPair.UniqueIdent));
}
public void ReleaseVisiblePairFocus()
{
_ = RunOnFrameworkThread(ReleaseFocusUnsafe);
}
private void FocusPairUnsafe(nint address, PairUniqueIdentifier pairIdent)
{
var target = CreateGameObject(address);
if (target is null) return;
if (!_FocusPairIdent.HasValue)
{
_FocusOriginalTarget = _targetManager.FocusTarget;
}
_targetManager.FocusTarget = target;
_FocusPairIdent = pairIdent;
}
private void ReleaseFocusUnsafe()
{
if (!_FocusPairIdent.HasValue)
{
return;
}
var previous = _FocusOriginalTarget;
if (previous != null && !IsObjectPresent(previous))
{
previous = null;
}
_targetManager.FocusTarget = previous;
_FocusPairIdent = null;
_FocusOriginalTarget = null;
}
public unsafe GameObject* GposeTarget public unsafe GameObject* GposeTarget
{ {
@@ -505,6 +569,17 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
Mediator.UnsubscribeAll(this); Mediator.UnsubscribeAll(this);
_framework.Update -= FrameworkOnUpdate; _framework.Update -= FrameworkOnUpdate;
if (_FocusPairIdent.HasValue)
{
if (_framework.IsInFrameworkUpdateThread)
{
ReleaseFocusUnsafe();
}
else
{
_ = RunOnFrameworkThread(ReleaseFocusUnsafe);
}
}
return Task.CompletedTask; return Task.CompletedTask;
} }

View File

@@ -103,6 +103,7 @@ public record PairDataChangedMessage : MessageBase;
public record PairUiUpdatedMessage(PairUiSnapshot Snapshot) : MessageBase; public record PairUiUpdatedMessage(PairUiSnapshot Snapshot) : MessageBase;
public record CensusUpdateMessage(byte Gender, byte RaceId, byte TribeId) : MessageBase; public record CensusUpdateMessage(byte Gender, byte RaceId, byte TribeId) : MessageBase;
public record TargetPairMessage(Pair Pair) : MessageBase; public record TargetPairMessage(Pair Pair) : MessageBase;
public record PairFocusCharacterMessage(Pair Pair) : SameThreadMessage;
public record CombatStartMessage : MessageBase; public record CombatStartMessage : MessageBase;
public record CombatEndMessage : MessageBase; public record CombatEndMessage : MessageBase;
public record PerformanceStartMessage : MessageBase; public record PerformanceStartMessage : MessageBase;

View File

@@ -56,6 +56,7 @@ public class CompactUi : WindowMediatorSubscriberBase
private readonly TagHandler _tagHandler; private readonly TagHandler _tagHandler;
private readonly UiSharedService _uiSharedService; private readonly UiSharedService _uiSharedService;
private readonly LightFinderService _broadcastService; private readonly LightFinderService _broadcastService;
private readonly DalamudUtilService _dalamudUtilService;
private List<IDrawFolder> _drawFolders; private List<IDrawFolder> _drawFolders;
private Pair? _lastAddedUser; private Pair? _lastAddedUser;
@@ -68,6 +69,9 @@ public class CompactUi : WindowMediatorSubscriberBase
private float _windowContentWidth; private float _windowContentWidth;
private readonly SeluneBrush _seluneBrush = new(); private readonly SeluneBrush _seluneBrush = new();
private const float _connectButtonHighlightThickness = 14f; private const float _connectButtonHighlightThickness = 14f;
private Pair? _focusedPair;
private Pair? _pendingFocusPair;
private int _pendingFocusFrame = -1;
public CompactUi( public CompactUi(
ILogger<CompactUi> logger, ILogger<CompactUi> logger,
@@ -109,7 +113,9 @@ public class CompactUi : WindowMediatorSubscriberBase
_ipcManager = ipcManager; _ipcManager = ipcManager;
_broadcastService = broadcastService; _broadcastService = broadcastService;
_pairLedger = pairLedger; _pairLedger = pairLedger;
_dalamudUtilService = dalamudUtilService;
_tabMenu = new TopTabMenu(Mediator, _apiController, _uiSharedService, pairRequestService, dalamudUtilService, lightlessNotificationService); _tabMenu = new TopTabMenu(Mediator, _apiController, _uiSharedService, pairRequestService, dalamudUtilService, lightlessNotificationService);
Mediator.Subscribe<PairFocusCharacterMessage>(this, msg => RegisterFocusCharacter(msg.Pair));
AllowPinning = true; AllowPinning = true;
AllowClickthrough = false; AllowClickthrough = false;
@@ -178,6 +184,12 @@ public class CompactUi : WindowMediatorSubscriberBase
_lightlessMediator = mediator; _lightlessMediator = mediator;
} }
public override void OnClose()
{
ForceReleaseFocus();
base.OnClose();
}
protected override void DrawInternal() protected override void DrawInternal()
{ {
var drawList = ImGui.GetWindowDrawList(); var drawList = ImGui.GetWindowDrawList();
@@ -268,6 +280,8 @@ public class CompactUi : WindowMediatorSubscriberBase
selune.Animate(ImGui.GetIO().DeltaTime); selune.Animate(ImGui.GetIO().DeltaTime);
} }
ProcessFocusTracker();
var lastAddedPair = _pairUiService.GetLastAddedPair(); var lastAddedPair = _pairUiService.GetLastAddedPair();
if (_configService.Current.OpenPopupOnAdd && lastAddedPair is not null) if (_configService.Current.OpenPopupOnAdd && lastAddedPair is not null)
{ {
@@ -1117,4 +1131,50 @@ public class CompactUi : WindowMediatorSubscriberBase
_wasOpen = IsOpen; _wasOpen = IsOpen;
IsOpen = false; IsOpen = false;
} }
private void RegisterFocusCharacter(Pair pair)
{
_pendingFocusPair = pair;
_pendingFocusFrame = ImGui.GetFrameCount();
}
private void ProcessFocusTracker()
{
var frame = ImGui.GetFrameCount();
Pair? character = _pendingFocusFrame == frame ? _pendingFocusPair : null;
if (!ReferenceEquals(character, _focusedPair))
{
if (character is null)
{
_dalamudUtilService.ReleaseVisiblePairFocus();
}
else
{
_dalamudUtilService.FocusVisiblePair(character);
}
_focusedPair = character;
}
if (_pendingFocusFrame == frame)
{
_pendingFocusPair = null;
_pendingFocusFrame = -1;
}
}
private void ForceReleaseFocus()
{
if (_focusedPair is null)
{
_pendingFocusPair = null;
_pendingFocusFrame = -1;
return;
}
_dalamudUtilService.ReleaseVisiblePairFocus();
_focusedPair = null;
_pendingFocusPair = null;
_pendingFocusFrame = -1;
}
} }

View File

@@ -247,6 +247,10 @@ public class DrawUserPair
else if (_pair.IsVisible) else if (_pair.IsVisible)
{ {
_uiSharedService.IconText(FontAwesomeIcon.Eye, UIColors.Get("LightlessBlue")); _uiSharedService.IconText(FontAwesomeIcon.Eye, UIColors.Get("LightlessBlue"));
if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenBlockedByActiveItem | ImGuiHoveredFlags.AllowWhenOverlapped | ImGuiHoveredFlags.AllowWhenDisabled))
{
_mediator.Publish(new PairFocusCharacterMessage(_pair));
}
if (ImGui.IsItemClicked()) if (ImGui.IsItemClicked())
{ {
_mediator.Publish(new TargetPairMessage(_pair)); _mediator.Publish(new TargetPairMessage(_pair));