Prevent vanilla tooltip for event item and implement #28

This commit is contained in:
Infi
2024-04-30 03:49:53 +02:00
parent 695fd997d7
commit a74cdcb899
11 changed files with 110 additions and 40 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<Version>1.23.0</Version> <Version>1.23.1</Version>
<TargetFramework>net8.0-windows</TargetFramework> <TargetFramework>net8.0-windows</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
+2
View File
@@ -190,6 +190,7 @@ internal class Tab
public bool PopOut; public bool PopOut;
public bool IndependentOpacity; public bool IndependentOpacity;
public float Opacity = 100f; public float Opacity = 100f;
public bool InputDisabled;
[NonSerialized] [NonSerialized]
public uint Unread; public uint Unread;
@@ -273,6 +274,7 @@ internal class Tab
IndependentOpacity = IndependentOpacity, IndependentOpacity = IndependentOpacity,
Opacity = Opacity, Opacity = Opacity,
Identifier = Identifier, Identifier = Identifier,
InputDisabled = InputDisabled,
}; };
} }
} }
+27 -18
View File
@@ -24,7 +24,7 @@ internal unsafe class GameFunctions : IDisposable
#region Functions #region Functions
[Signature("E8 ?? ?? ?? ?? 84 C0 74 0D B0 02", Fallibility = Fallibility.Fallible)] [Signature("E8 ?? ?? ?? ?? 84 C0 74 0D B0 02", Fallibility = Fallibility.Fallible)]
private readonly delegate* unmanaged<IntPtr, byte> IsMentorNative = null!; private readonly delegate* unmanaged<nint, byte> IsMentorNative = null!;
[Signature("48 89 5C 24 ?? 57 48 83 EC 20 48 8B FA 48 8B D9 E8 ?? ?? ?? ?? 48 8B 8B ?? ?? ?? ?? 48 85 C9", Fallibility = Fallibility.Fallible)] [Signature("48 89 5C 24 ?? 57 48 83 EC 20 48 8B FA 48 8B D9 E8 ?? ?? ?? ?? 48 8B 8B ?? ?? ?? ?? 48 85 C9", Fallibility = Fallibility.Fallible)]
private readonly delegate* unmanaged<AgentInterface*, ulong, byte> OpenPartyFinderNative = null!; private readonly delegate* unmanaged<AgentInterface*, ulong, byte> OpenPartyFinderNative = null!;
@@ -37,7 +37,7 @@ internal unsafe class GameFunctions : IDisposable
#endregion #endregion
#region Hooks #region Hooks
private delegate IntPtr ResolveTextCommandPlaceholderDelegate(IntPtr a1, byte* placeholderText, byte a3, byte a4); private delegate nint ResolveTextCommandPlaceholderDelegate(nint a1, byte* placeholderText, byte a3, byte a4);
[Signature(Signatures.ResolveTextCommandPlaceholder, DetourName = nameof(ResolveTextCommandPlaceholderDetour))] [Signature(Signatures.ResolveTextCommandPlaceholder, DetourName = nameof(ResolveTextCommandPlaceholderDetour))]
private Hook<ResolveTextCommandPlaceholderDelegate>? ResolveTextCommandPlaceholderHook { get; init; } private Hook<ResolveTextCommandPlaceholderDelegate>? ResolveTextCommandPlaceholderHook { get; init; }
@@ -48,7 +48,7 @@ internal unsafe class GameFunctions : IDisposable
private readonly byte? CurrentChatEntryOffset; private readonly byte? CurrentChatEntryOffset;
[Signature(Signatures.IsMentorA1, ScanType = ScanType.StaticAddress)] [Signature(Signatures.IsMentorA1, ScanType = ScanType.StaticAddress)]
private readonly IntPtr? IsMentorA1; private readonly nint? IsMentorA1;
#pragma warning restore 0649 #pragma warning restore 0649
private Plugin Plugin { get; } private Plugin Plugin { get; }
@@ -77,7 +77,7 @@ internal unsafe class GameFunctions : IDisposable
Marshal.FreeHGlobal(PlaceholderNamePtr); Marshal.FreeHGlobal(PlaceholderNamePtr);
} }
internal IntPtr GetInfoProxyByIndex(uint idx) internal nint GetInfoProxyByIndex(uint idx)
{ {
var infoModule = InfoModule.Instance(); var infoModule = InfoModule.Instance();
if (infoModule == null) if (infoModule == null)
@@ -91,7 +91,7 @@ internal unsafe class GameFunctions : IDisposable
if (CurrentChatEntryOffset == null) if (CurrentChatEntryOffset == null)
return null; return null;
var log = (IntPtr) Framework.Instance()->GetUiModule()->GetRaptureLogModule(); var log = (nint) Framework.Instance()->GetUiModule()->GetRaptureLogModule();
return *(uint*) (log + CurrentChatEntryOffset.Value); return *(uint*) (log + CurrentChatEntryOffset.Value);
} }
@@ -120,8 +120,8 @@ internal unsafe class GameFunctions : IDisposable
{ {
var unitManager = AtkStage.GetSingleton()->RaptureAtkUnitManager; var unitManager = AtkStage.GetSingleton()->RaptureAtkUnitManager;
var addon = (IntPtr) unitManager->GetAddonByName(name); var addon = (nint) unitManager->GetAddonByName(name);
if (addon == IntPtr.Zero) if (addon == nint.Zero)
return; return;
var flags = (uint*) (addon + 0x180); var flags = (uint*) (addon + 0x180);
@@ -143,8 +143,8 @@ internal unsafe class GameFunctions : IDisposable
{ {
var unitManager = AtkStage.GetSingleton()->RaptureAtkUnitManager; var unitManager = AtkStage.GetSingleton()->RaptureAtkUnitManager;
var addon = (IntPtr) unitManager->GetAddonByName(name); var addon = (nint) unitManager->GetAddonByName(name);
if (addon == IntPtr.Zero) if (addon == nint.Zero)
return false; return false;
var flags = (uint*) (addon + 0x180); var flags = (uint*) (addon + 0x180);
@@ -161,8 +161,7 @@ internal unsafe class GameFunctions : IDisposable
if (agent == null || addon == null) if (agent == null || addon == null)
return; return;
var agentPtr = (IntPtr) agent; var agentPtr = (nint) agent;
// addresses mentioned here are 6.11 // addresses mentioned here are 6.11
// see the call near the end of AgentItemDetail.Update // see the call near the end of AgentItemDetail.Update
// offsets valid as of 6.11 // offsets valid as of 6.11
@@ -177,6 +176,8 @@ internal unsafe class GameFunctions : IDisposable
*(uint*) (agentPtr + 0x120) = 0; *(uint*) (agentPtr + 0x120) = 0;
// A558A5: skips a check to do with inventory // A558A5: skips a check to do with inventory
*(byte*) (agentPtr + 0x128) &= 0xEF; *(byte*) (agentPtr + 0x128) &= 0xEF;
// Is also set to the ID of the item when in chat
*(uint*) (agentPtr + 0x138) = id;
// A54B3F: when set to 1, lets everything continue (one frame) // A54B3F: when set to 1, lets everything continue (one frame)
*(byte*) (agentPtr + 0x14A) = 1; *(byte*) (agentPtr + 0x14A) = 1;
// A54B59: skips early return // A54B59: skips early return
@@ -186,9 +187,9 @@ internal unsafe class GameFunctions : IDisposable
agent->AddonId = addon->ID; agent->AddonId = addon->ID;
// vcall from E8 ?? ?? ?? ?? 0F B7 C0 48 83 C4 60 (FF 50 28 48 8B D3 48 8B CF) // vcall from E8 ?? ?? ?? ?? 0F B7 C0 48 83 C4 60 (FF 50 28 48 8B D3 48 8B CF)
var vf5 = (delegate* unmanaged<AtkUnitBase*, byte, uint, void>*) ((IntPtr) addon->VTable + 40); var vf5 = (delegate* unmanaged<AtkUnitBase*, byte, uint, void>*) ((nint) addon->VTable + 40);
// EA8BED: lets vf5 actually run // EA8BED: lets vf5 actually run
*(byte*) ((IntPtr) atkStage + 0x2B4) |= 2; *(byte*) ((nint) atkStage + 0x2B4) |= 2;
(*vf5)(addon, 0, 15); (*vf5)(addon, 0, 15);
} }
@@ -201,7 +202,15 @@ internal unsafe class GameFunctions : IDisposable
var agent = Framework.Instance()->GetUiModule()->GetAgentModule()->GetAgentByInternalId(AgentId.ItemDetail); var agent = Framework.Instance()->GetUiModule()->GetAgentModule()->GetAgentByInternalId(AgentId.ItemDetail);
if (agent != null) if (agent != null)
{
Plugin.Log.Information("close and clean was called");
agent->Hide(); agent->Hide();
// The game sets them to 0 whenever tooltips aren't hovered anymore
var agentPtr = (nint)agent;
*(uint*) (agentPtr + 0x138) = 0;
*(uint*) (agentPtr + 0x13C) = 0;
}
} }
internal static void OpenPartyFinder() internal static void OpenPartyFinder()
@@ -219,7 +228,7 @@ internal unsafe class GameFunctions : IDisposable
else else
{ {
// 6.05: 8443DD // 6.05: 8443DD
if (*(uint*) ((IntPtr) lfg + 0x2C20) > 0) if (*(uint*) ((nint) lfg + 0x2C20) > 0)
lfg->Hide(); lfg->Hide();
else else
lfg->Show(); lfg->Show();
@@ -228,7 +237,7 @@ internal unsafe class GameFunctions : IDisposable
internal bool IsMentor() internal bool IsMentor()
{ {
if (IsMentorNative == null || IsMentorA1 == null || IsMentorA1.Value == IntPtr.Zero) if (IsMentorNative == null || IsMentorA1 == null || IsMentorA1.Value == nint.Zero)
return false; return false;
return IsMentorNative(IsMentorA1.Value) > 0; return IsMentorNative(IsMentorA1.Value) > 0;
@@ -286,16 +295,16 @@ internal unsafe class GameFunctions : IDisposable
vf0(agent, &result, &value, 0, 0); vf0(agent, &result, &value, 0, 0);
} }
private readonly IntPtr PlaceholderNamePtr = Marshal.AllocHGlobal(128); private readonly nint PlaceholderNamePtr = Marshal.AllocHGlobal(128);
private readonly string Placeholder = $"<{Guid.NewGuid():N}>"; private readonly string Placeholder = $"<{Guid.NewGuid():N}>";
private string? ReplacementName; private string? ReplacementName;
private IntPtr ResolveTextCommandPlaceholderDetour(IntPtr a1, byte* placeholderText, byte a3, byte a4) private nint ResolveTextCommandPlaceholderDetour(nint a1, byte* placeholderText, byte a3, byte a4)
{ {
if (ReplacementName == null) if (ReplacementName == null)
goto Original; goto Original;
var placeholder = MemoryHelper.ReadStringNullTerminated((IntPtr) placeholderText); var placeholder = MemoryHelper.ReadStringNullTerminated((nint) placeholderText);
if (placeholder != Placeholder) if (placeholder != Placeholder)
goto Original; goto Original;
+10 -8
View File
@@ -32,17 +32,17 @@ public sealed class PayloadHandler {
private ChatLogWindow LogWindow { get; } private ChatLogWindow LogWindow { get; }
private (Chunk, Payload?)? Popup { get; set; } private (Chunk, Payload?)? Popup { get; set; }
private bool _handleTooltips; public bool _handleTooltips;
private uint _hoveredItem; public uint _hoveredItem;
private uint _hoverCounter; public uint _hoverCounter;
private uint _lastHoverCounter; public uint _lastHoverCounter;
private readonly ExcelSheet<Item> ItemSheet; private readonly ExcelSheet<Item> ItemSheet;
private readonly ExcelSheet<EventItem> EventItemSheet; private readonly ExcelSheet<EventItem> EventItemSheet;
private readonly ExcelSheet<TerritoryType> TerritorySheet; private readonly ExcelSheet<TerritoryType> TerritorySheet;
private readonly ExcelSheet<EventItemHelp> EventItemHelpSheet; private readonly ExcelSheet<EventItemHelp> EventItemHelpSheet;
private uint PopupSfx = 1u; private const uint PopupSfx = 1u;
internal PayloadHandler(ChatLogWindow logWindow) internal PayloadHandler(ChatLogWindow logWindow)
{ {
@@ -230,9 +230,11 @@ public sealed class PayloadHandler {
DoHover(() => HoverStatus(status), hoverSize); DoHover(() => HoverStatus(status), hoverSize);
break; break;
case ItemPayload item: case ItemPayload item:
if (LogWindow.Plugin.Config.NativeItemTooltips) // Event Item do weird things with the client, so we stop vanilla tooltips for now
if (LogWindow.Plugin.Config.NativeItemTooltips && item.Kind != ItemPayload.ItemKind.EventItem)
{ {
GameFunctions.GameFunctions.OpenItemTooltip(item.RawItemId); if (!_handleTooltips)
GameFunctions.GameFunctions.OpenItemTooltip(item.RawItemId);
_handleTooltips = true; _handleTooltips = true;
if (_hoveredItem != item.RawItemId) if (_hoveredItem != item.RawItemId)
@@ -245,7 +247,7 @@ public sealed class PayloadHandler {
_lastHoverCounter = _hoverCounter; _lastHoverCounter = _hoverCounter;
} }
break; return;
} }
DoHover(() => HoverItem(item), hoverSize); DoHover(() => HoverItem(item), hoverSize);
+2 -6
View File
@@ -1,7 +1,6 @@
using System.Diagnostics; using System.Diagnostics;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Globalization; using System.Globalization;
using System.Reflection;
using ChatTwo.Ipc; using ChatTwo.Ipc;
using ChatTwo.Resources; using ChatTwo.Resources;
using ChatTwo.Ui; using ChatTwo.Ui;
@@ -41,9 +40,6 @@ public sealed class Plugin : IDalamudPlugin
[PluginService] internal static INotificationManager Notification { get; private set; } = null!; [PluginService] internal static INotificationManager Notification { get; private set; } = null!;
[PluginService] internal static IAddonLifecycle AddonLifecycle { get; private set; } = null!; [PluginService] internal static IAddonLifecycle AddonLifecycle { get; private set; } = null!;
public const string Authors = "Infi, Anna";
public static readonly string Version = Assembly.GetExecutingAssembly().GetName().Version?.ToString(3) ?? "Unknown";
public readonly WindowSystem WindowSystem = new(PluginName); public readonly WindowSystem WindowSystem = new(PluginName);
public SettingsWindow SettingsWindow { get; } public SettingsWindow SettingsWindow { get; }
public ChatLogWindow ChatLogWindow { get; } public ChatLogWindow ChatLogWindow { get; }
@@ -124,12 +120,12 @@ public sealed class Plugin : IDalamudPlugin
Interface.UiBuilder.Draw += Draw; Interface.UiBuilder.Draw += Draw;
Interface.LanguageChanged += LanguageChanged; Interface.LanguageChanged += LanguageChanged;
#if !DEBUG #if !DEBUG
// Avoid 300ms hitch when sending first message by preloading the // Avoid 300ms hitch when sending first message by preloading the
// auto-translate cache. Don't do this in debug because it makes // auto-translate cache. Don't do this in debug because it makes
// profiling difficult. // profiling difficult.
AutoTranslate.PreloadCache(DataManager); AutoTranslate.PreloadCache(DataManager);
#endif #endif
} }
catch catch
{ {
+18
View File
@@ -77,6 +77,15 @@ namespace ChatTwo.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Input is disabled for this tab.
/// </summary>
internal static string ChatLog_DisabledInput {
get {
return ResourceManager.GetString("ChatLog_DisabledInput", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Hide chat. /// Looks up a localized string similar to Hide chat.
/// </summary> /// </summary>
@@ -2525,6 +2534,15 @@ namespace ChatTwo.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Disable input for this channel.
/// </summary>
internal static string Options_Tabs_NoInput {
get {
return ResourceManager.GetString("Options_Tabs_NoInput", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to &lt;None&gt;. /// Looks up a localized string similar to &lt;None&gt;.
/// </summary> /// </summary>
+6
View File
@@ -1012,4 +1012,10 @@
<data name="Options_Database_Old_Migration" xml:space="preserve"> <data name="Options_Database_Old_Migration" xml:space="preserve">
<value>Open migration window</value> <value>Open migration window</value>
</data> </data>
<data name="Options_Tabs_NoInput" xml:space="preserve">
<value>Disable input for this channel</value>
</data>
<data name="ChatLog_DisabledInput" xml:space="preserve">
<value>Input is disabled for this tab</value>
</data>
</root> </root>
+27 -2
View File
@@ -74,7 +74,7 @@ public sealed class ChatLogWindow : Window
public unsafe ImGuiViewport* LastViewport; public unsafe ImGuiViewport* LastViewport;
private bool WasDocked; private bool WasDocked;
private PayloadHandler PayloadHandler { get; } public PayloadHandler PayloadHandler { get; }
internal Lender<PayloadHandler> HandlerLender { get; } internal Lender<PayloadHandler> HandlerLender { get; }
private Dictionary<string, ChatType> TextCommandChannels { get; } = new(); private Dictionary<string, ChatType> TextCommandChannels { get; } = new();
private HashSet<string> AllCommands { get; } = []; private HashSet<string> AllCommands { get; } = [];
@@ -578,6 +578,32 @@ public sealed class ChatLogWindow : Window
} }
} }
var showNovice = Plugin.Config.ShowNoviceNetwork && Plugin.Functions.IsMentor();
if (activeTab is { InputDisabled: true })
{
Plugin.FontManager.FontAwesome.Push();
var buttonSize = ImGui.CalcTextSize(FontAwesomeIcon.Cog.ToIconString());
var spacing = ImGui.GetContentRegionAvail().X - buttonSize.X * (showNovice ? 2 : 1);
Plugin.FontManager.FontAwesome.Pop();
ImGui.Dummy(buttonSize with { X = spacing });
if (ImGui.IsItemHovered())
ImGui.SetTooltip(Language.ChatLog_DisabledInput);
ImGui.SameLine(spacing);
if (ImGuiUtil.IconButton(FontAwesomeIcon.Cog))
Plugin.SettingsWindow.Toggle();
if (!showNovice)
return;
ImGui.SameLine();
if (ImGuiUtil.IconButton(FontAwesomeIcon.Leaf))
Plugin.Functions.ClickNoviceNetworkButton();
return;
}
var beforeIcon = ImGui.GetCursorPos(); var beforeIcon = ImGui.GetCursorPos();
if (ImGuiUtil.IconButton(FontAwesomeIcon.Comment) && activeTab is not { Channel: not null }) if (ImGuiUtil.IconButton(FontAwesomeIcon.Comment) && activeTab is not { Channel: not null })
ImGui.OpenPopup(ChatChannelPicker); ImGui.OpenPopup(ChatChannelPicker);
@@ -629,7 +655,6 @@ public sealed class ChatLogWindow : Window
var afterIcon = ImGui.GetCursorPos(); var afterIcon = ImGui.GetCursorPos();
var buttonWidth = afterIcon.X - beforeIcon.X; var buttonWidth = afterIcon.X - beforeIcon.X;
var showNovice = Plugin.Config.ShowNoviceNetwork && Plugin.Functions.IsMentor();
var inputWidth = ImGui.GetContentRegionAvail().X - buttonWidth * (showNovice ? 2 : 1); var inputWidth = ImGui.GetContentRegionAvail().X - buttonWidth * (showNovice ? 2 : 1);
var inputType = TempChannel?.ToChatType() ?? activeTab?.Channel?.ToChatType() ?? Plugin.Functions.Chat.Channel.Channel.ToChatType(); var inputType = TempChannel?.ToChatType() ?? activeTab?.Channel?.ToChatType() ?? Plugin.Functions.Chat.Channel.Channel.ToChatType();
+11 -1
View File
@@ -1,5 +1,7 @@
using System.Numerics; using System.Numerics;
using Dalamud.Interface.Windowing; using Dalamud.Interface.Windowing;
using FFXIVClientStructs.FFXIV.Client.System.Framework;
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
using ImGuiNET; using ImGuiNET;
namespace ChatTwo.Ui; namespace ChatTwo.Ui;
@@ -37,8 +39,16 @@ public class DebuggerWindow : Window
private void Toggle(string _, string __) => Toggle(); private void Toggle(string _, string __) => Toggle();
public override void Draw() public override unsafe void Draw()
{ {
var agent = (nint) Framework.Instance()->GetUiModule()->GetAgentModule()->GetAgentByInternalId(AgentId.ItemDetail);
ImGui.TextUnformatted($"Current Cursor Pos: {ChatLogWindow.CursorPos}"); ImGui.TextUnformatted($"Current Cursor Pos: {ChatLogWindow.CursorPos}");
if (ImGui.Selectable($"Agent Address: {agent:X}"))
ImGui.SetClipboardText(agent.ToString("X"));
ImGui.TextUnformatted($"Handle Tooltips: {ChatLogWindow.PayloadHandler._handleTooltips}");
ImGui.TextUnformatted($"Hovered Item: {ChatLogWindow.PayloadHandler._hoveredItem}");
ImGui.TextUnformatted($"Hover Counter: {ChatLogWindow.PayloadHandler._hoverCounter}");
ImGui.TextUnformatted($"Last Hover Counter: {ChatLogWindow.PayloadHandler._lastHoverCounter}");
} }
} }
+2 -2
View File
@@ -41,7 +41,7 @@ internal sealed class About : ISettingsTab
ImGui.TextUnformatted(Language.Options_About_Authors); ImGui.TextUnformatted(Language.Options_About_Authors);
ImGui.SameLine(); ImGui.SameLine();
ImGui.TextColored(ImGuiColors.ParsedGold, Plugin.Authors); ImGui.TextColored(ImGuiColors.ParsedGold, Plugin.Interface.Manifest.Author);
ImGui.TextUnformatted(Language.Options_About_Discord); ImGui.TextUnformatted(Language.Options_About_Discord);
ImGui.SameLine(); ImGui.SameLine();
@@ -49,7 +49,7 @@ internal sealed class About : ISettingsTab
ImGui.TextUnformatted(Language.Options_About_Version); ImGui.TextUnformatted(Language.Options_About_Version);
ImGui.SameLine(); ImGui.SameLine();
ImGui.TextColored(ImGuiColors.ParsedOrange, Plugin.Version); ImGui.TextColored(ImGuiColors.ParsedOrange, Plugin.Interface.Manifest.AssemblyVersion.ToString(3));
ImGuiHelpers.ScaledDummy(10.0f); ImGuiHelpers.ScaledDummy(10.0f);
+4 -2
View File
@@ -108,9 +108,11 @@ internal sealed class Tabs : ISettingsTab
} }
} }
var input = tab.Channel?.ToChatType().Name() ?? Language.Options_Tabs_NoInputChannel; ImGui.Checkbox(Language.Options_Tabs_NoInput, ref tab.InputDisabled);
using (var combo = ImGuiUtil.BeginComboVertical(Language.Options_Tabs_InputChannel, input)) if (!tab.InputDisabled)
{ {
var input = tab.Channel?.ToChatType().Name() ?? Language.Options_Tabs_NoInputChannel;
using var combo = ImGuiUtil.BeginComboVertical(Language.Options_Tabs_InputChannel, input);
if (combo) if (combo)
{ {
if (ImGui.Selectable(Language.Options_Tabs_NoInputChannel, tab.Channel == null)) if (ImGui.Selectable(Language.Options_Tabs_NoInputChannel, tab.Channel == null))