diff --git a/ChatTwo/ChatTwo.csproj b/ChatTwo/ChatTwo.csproj index 166a7f4..dba76af 100755 --- a/ChatTwo/ChatTwo.csproj +++ b/ChatTwo/ChatTwo.csproj @@ -1,6 +1,6 @@ - 1.23.0 + 1.23.1 net8.0-windows enable enable diff --git a/ChatTwo/Configuration.cs b/ChatTwo/Configuration.cs index f35ef6d..8d0390a 100755 --- a/ChatTwo/Configuration.cs +++ b/ChatTwo/Configuration.cs @@ -190,6 +190,7 @@ internal class Tab public bool PopOut; public bool IndependentOpacity; public float Opacity = 100f; + public bool InputDisabled; [NonSerialized] public uint Unread; @@ -273,6 +274,7 @@ internal class Tab IndependentOpacity = IndependentOpacity, Opacity = Opacity, Identifier = Identifier, + InputDisabled = InputDisabled, }; } } diff --git a/ChatTwo/GameFunctions/GameFunctions.cs b/ChatTwo/GameFunctions/GameFunctions.cs index 278c9ae..75c87e1 100755 --- a/ChatTwo/GameFunctions/GameFunctions.cs +++ b/ChatTwo/GameFunctions/GameFunctions.cs @@ -24,7 +24,7 @@ internal unsafe class GameFunctions : IDisposable #region Functions [Signature("E8 ?? ?? ?? ?? 84 C0 74 0D B0 02", Fallibility = Fallibility.Fallible)] - private readonly delegate* unmanaged IsMentorNative = null!; + private readonly delegate* unmanaged 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)] private readonly delegate* unmanaged OpenPartyFinderNative = null!; @@ -37,7 +37,7 @@ internal unsafe class GameFunctions : IDisposable #endregion #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))] private Hook? ResolveTextCommandPlaceholderHook { get; init; } @@ -48,7 +48,7 @@ internal unsafe class GameFunctions : IDisposable private readonly byte? CurrentChatEntryOffset; [Signature(Signatures.IsMentorA1, ScanType = ScanType.StaticAddress)] - private readonly IntPtr? IsMentorA1; + private readonly nint? IsMentorA1; #pragma warning restore 0649 private Plugin Plugin { get; } @@ -77,7 +77,7 @@ internal unsafe class GameFunctions : IDisposable Marshal.FreeHGlobal(PlaceholderNamePtr); } - internal IntPtr GetInfoProxyByIndex(uint idx) + internal nint GetInfoProxyByIndex(uint idx) { var infoModule = InfoModule.Instance(); if (infoModule == null) @@ -91,7 +91,7 @@ internal unsafe class GameFunctions : IDisposable if (CurrentChatEntryOffset == null) return null; - var log = (IntPtr) Framework.Instance()->GetUiModule()->GetRaptureLogModule(); + var log = (nint) Framework.Instance()->GetUiModule()->GetRaptureLogModule(); return *(uint*) (log + CurrentChatEntryOffset.Value); } @@ -120,8 +120,8 @@ internal unsafe class GameFunctions : IDisposable { var unitManager = AtkStage.GetSingleton()->RaptureAtkUnitManager; - var addon = (IntPtr) unitManager->GetAddonByName(name); - if (addon == IntPtr.Zero) + var addon = (nint) unitManager->GetAddonByName(name); + if (addon == nint.Zero) return; var flags = (uint*) (addon + 0x180); @@ -143,8 +143,8 @@ internal unsafe class GameFunctions : IDisposable { var unitManager = AtkStage.GetSingleton()->RaptureAtkUnitManager; - var addon = (IntPtr) unitManager->GetAddonByName(name); - if (addon == IntPtr.Zero) + var addon = (nint) unitManager->GetAddonByName(name); + if (addon == nint.Zero) return false; var flags = (uint*) (addon + 0x180); @@ -161,8 +161,7 @@ internal unsafe class GameFunctions : IDisposable if (agent == null || addon == null) return; - var agentPtr = (IntPtr) agent; - + var agentPtr = (nint) agent; // addresses mentioned here are 6.11 // see the call near the end of AgentItemDetail.Update // offsets valid as of 6.11 @@ -177,6 +176,8 @@ internal unsafe class GameFunctions : IDisposable *(uint*) (agentPtr + 0x120) = 0; // A558A5: skips a check to do with inventory *(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) *(byte*) (agentPtr + 0x14A) = 1; // A54B59: skips early return @@ -186,9 +187,9 @@ internal unsafe class GameFunctions : IDisposable agent->AddonId = addon->ID; // vcall from E8 ?? ?? ?? ?? 0F B7 C0 48 83 C4 60 (FF 50 28 48 8B D3 48 8B CF) - var vf5 = (delegate* unmanaged*) ((IntPtr) addon->VTable + 40); + var vf5 = (delegate* unmanaged*) ((nint) addon->VTable + 40); // EA8BED: lets vf5 actually run - *(byte*) ((IntPtr) atkStage + 0x2B4) |= 2; + *(byte*) ((nint) atkStage + 0x2B4) |= 2; (*vf5)(addon, 0, 15); } @@ -201,7 +202,15 @@ internal unsafe class GameFunctions : IDisposable var agent = Framework.Instance()->GetUiModule()->GetAgentModule()->GetAgentByInternalId(AgentId.ItemDetail); if (agent != null) + { + Plugin.Log.Information("close and clean was called"); 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() @@ -219,7 +228,7 @@ internal unsafe class GameFunctions : IDisposable else { // 6.05: 8443DD - if (*(uint*) ((IntPtr) lfg + 0x2C20) > 0) + if (*(uint*) ((nint) lfg + 0x2C20) > 0) lfg->Hide(); else lfg->Show(); @@ -228,7 +237,7 @@ internal unsafe class GameFunctions : IDisposable internal bool IsMentor() { - if (IsMentorNative == null || IsMentorA1 == null || IsMentorA1.Value == IntPtr.Zero) + if (IsMentorNative == null || IsMentorA1 == null || IsMentorA1.Value == nint.Zero) return false; return IsMentorNative(IsMentorA1.Value) > 0; @@ -286,16 +295,16 @@ internal unsafe class GameFunctions : IDisposable 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 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) goto Original; - var placeholder = MemoryHelper.ReadStringNullTerminated((IntPtr) placeholderText); + var placeholder = MemoryHelper.ReadStringNullTerminated((nint) placeholderText); if (placeholder != Placeholder) goto Original; diff --git a/ChatTwo/PayloadHandler.cs b/ChatTwo/PayloadHandler.cs index 5f481e9..ac499ac 100755 --- a/ChatTwo/PayloadHandler.cs +++ b/ChatTwo/PayloadHandler.cs @@ -32,17 +32,17 @@ public sealed class PayloadHandler { private ChatLogWindow LogWindow { get; } private (Chunk, Payload?)? Popup { get; set; } - private bool _handleTooltips; - private uint _hoveredItem; - private uint _hoverCounter; - private uint _lastHoverCounter; + public bool _handleTooltips; + public uint _hoveredItem; + public uint _hoverCounter; + public uint _lastHoverCounter; private readonly ExcelSheet ItemSheet; private readonly ExcelSheet EventItemSheet; private readonly ExcelSheet TerritorySheet; private readonly ExcelSheet EventItemHelpSheet; - private uint PopupSfx = 1u; + private const uint PopupSfx = 1u; internal PayloadHandler(ChatLogWindow logWindow) { @@ -230,9 +230,11 @@ public sealed class PayloadHandler { DoHover(() => HoverStatus(status), hoverSize); break; 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; if (_hoveredItem != item.RawItemId) @@ -245,7 +247,7 @@ public sealed class PayloadHandler { _lastHoverCounter = _hoverCounter; } - break; + return; } DoHover(() => HoverItem(item), hoverSize); diff --git a/ChatTwo/Plugin.cs b/ChatTwo/Plugin.cs index 07f956a..bd785d3 100755 --- a/ChatTwo/Plugin.cs +++ b/ChatTwo/Plugin.cs @@ -1,7 +1,6 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; -using System.Reflection; using ChatTwo.Ipc; using ChatTwo.Resources; using ChatTwo.Ui; @@ -41,9 +40,6 @@ public sealed class Plugin : IDalamudPlugin [PluginService] internal static INotificationManager Notification { 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 SettingsWindow SettingsWindow { get; } public ChatLogWindow ChatLogWindow { get; } @@ -124,12 +120,12 @@ public sealed class Plugin : IDalamudPlugin Interface.UiBuilder.Draw += Draw; Interface.LanguageChanged += LanguageChanged; -#if !DEBUG + #if !DEBUG // Avoid 300ms hitch when sending first message by preloading the // auto-translate cache. Don't do this in debug because it makes // profiling difficult. AutoTranslate.PreloadCache(DataManager); -#endif + #endif } catch { diff --git a/ChatTwo/Resources/Language.Designer.cs b/ChatTwo/Resources/Language.Designer.cs index 7e248b3..5ff840f 100755 --- a/ChatTwo/Resources/Language.Designer.cs +++ b/ChatTwo/Resources/Language.Designer.cs @@ -77,6 +77,15 @@ namespace ChatTwo.Resources { } } + /// + /// Looks up a localized string similar to Input is disabled for this tab. + /// + internal static string ChatLog_DisabledInput { + get { + return ResourceManager.GetString("ChatLog_DisabledInput", resourceCulture); + } + } + /// /// Looks up a localized string similar to Hide chat. /// @@ -2525,6 +2534,15 @@ namespace ChatTwo.Resources { } } + /// + /// Looks up a localized string similar to Disable input for this channel. + /// + internal static string Options_Tabs_NoInput { + get { + return ResourceManager.GetString("Options_Tabs_NoInput", resourceCulture); + } + } + /// /// Looks up a localized string similar to <None>. /// diff --git a/ChatTwo/Resources/Language.resx b/ChatTwo/Resources/Language.resx index c9f249f..bc70254 100644 --- a/ChatTwo/Resources/Language.resx +++ b/ChatTwo/Resources/Language.resx @@ -1012,4 +1012,10 @@ Open migration window + + Disable input for this channel + + + Input is disabled for this tab + diff --git a/ChatTwo/Ui/ChatLogWindow.cs b/ChatTwo/Ui/ChatLogWindow.cs index 41e7fcc..18f4db5 100644 --- a/ChatTwo/Ui/ChatLogWindow.cs +++ b/ChatTwo/Ui/ChatLogWindow.cs @@ -74,7 +74,7 @@ public sealed class ChatLogWindow : Window public unsafe ImGuiViewport* LastViewport; private bool WasDocked; - private PayloadHandler PayloadHandler { get; } + public PayloadHandler PayloadHandler { get; } internal Lender HandlerLender { get; } private Dictionary TextCommandChannels { get; } = new(); private HashSet 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(); if (ImGuiUtil.IconButton(FontAwesomeIcon.Comment) && activeTab is not { Channel: not null }) ImGui.OpenPopup(ChatChannelPicker); @@ -629,7 +655,6 @@ public sealed class ChatLogWindow : Window var afterIcon = ImGui.GetCursorPos(); var buttonWidth = afterIcon.X - beforeIcon.X; - var showNovice = Plugin.Config.ShowNoviceNetwork && Plugin.Functions.IsMentor(); var inputWidth = ImGui.GetContentRegionAvail().X - buttonWidth * (showNovice ? 2 : 1); var inputType = TempChannel?.ToChatType() ?? activeTab?.Channel?.ToChatType() ?? Plugin.Functions.Chat.Channel.Channel.ToChatType(); diff --git a/ChatTwo/Ui/Debugger.cs b/ChatTwo/Ui/Debugger.cs index d193dac..fac39f0 100644 --- a/ChatTwo/Ui/Debugger.cs +++ b/ChatTwo/Ui/Debugger.cs @@ -1,5 +1,7 @@ using System.Numerics; using Dalamud.Interface.Windowing; +using FFXIVClientStructs.FFXIV.Client.System.Framework; +using FFXIVClientStructs.FFXIV.Client.UI.Agent; using ImGuiNET; namespace ChatTwo.Ui; @@ -37,8 +39,16 @@ public class DebuggerWindow : Window 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}"); + 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}"); } } diff --git a/ChatTwo/Ui/SettingsTabs/About.cs b/ChatTwo/Ui/SettingsTabs/About.cs index 1f81526..884e818 100755 --- a/ChatTwo/Ui/SettingsTabs/About.cs +++ b/ChatTwo/Ui/SettingsTabs/About.cs @@ -41,7 +41,7 @@ internal sealed class About : ISettingsTab ImGui.TextUnformatted(Language.Options_About_Authors); ImGui.SameLine(); - ImGui.TextColored(ImGuiColors.ParsedGold, Plugin.Authors); + ImGui.TextColored(ImGuiColors.ParsedGold, Plugin.Interface.Manifest.Author); ImGui.TextUnformatted(Language.Options_About_Discord); ImGui.SameLine(); @@ -49,7 +49,7 @@ internal sealed class About : ISettingsTab ImGui.TextUnformatted(Language.Options_About_Version); ImGui.SameLine(); - ImGui.TextColored(ImGuiColors.ParsedOrange, Plugin.Version); + ImGui.TextColored(ImGuiColors.ParsedOrange, Plugin.Interface.Manifest.AssemblyVersion.ToString(3)); ImGuiHelpers.ScaledDummy(10.0f); diff --git a/ChatTwo/Ui/SettingsTabs/Tabs.cs b/ChatTwo/Ui/SettingsTabs/Tabs.cs index 35fbe9a..be98190 100755 --- a/ChatTwo/Ui/SettingsTabs/Tabs.cs +++ b/ChatTwo/Ui/SettingsTabs/Tabs.cs @@ -108,9 +108,11 @@ internal sealed class Tabs : ISettingsTab } } - var input = tab.Channel?.ToChatType().Name() ?? Language.Options_Tabs_NoInputChannel; - using (var combo = ImGuiUtil.BeginComboVertical(Language.Options_Tabs_InputChannel, input)) + ImGui.Checkbox(Language.Options_Tabs_NoInput, ref tab.InputDisabled); + 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 (ImGui.Selectable(Language.Options_Tabs_NoInputChannel, tab.Channel == null))