More CS updates

This commit is contained in:
Infi
2024-06-09 12:59:29 +02:00
parent 6ee209c1ad
commit 70989da680
8 changed files with 107 additions and 334 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<Version>1.25.5</Version> <Version>1.26.0</Version>
<TargetFramework>net8.0-windows</TargetFramework> <TargetFramework>net8.0-windows</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
+49 -145
View File
@@ -28,102 +28,36 @@ namespace ChatTwo.GameFunctions;
internal sealed unsafe class Chat : IDisposable internal sealed unsafe class Chat : IDisposable
{ {
// Functions // Functions
// TODO Replace with CS in RaptureShellModule
[Signature("E8 ?? ?? ?? ?? 0F B7 44 37 ??", Fallibility = Fallibility.Fallible)]
private readonly delegate* unmanaged<RaptureShellModule*, int, uint, Utf8String*, byte, void> ChangeChatChannel = null!;
// TODO Replace with CS in RaptureShellModule
[Signature("48 89 5C 24 ?? 55 56 57 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 84 24 ?? ?? ?? ?? 48 8B 02", Fallibility = Fallibility.Fallible)]
private readonly delegate* unmanaged<RaptureShellModule*, Utf8String*, Utf8String*, ushort, ulong, ushort, byte, bool> SetChannelTargetTell = null!;
[Signature("E8 ?? ?? ?? ?? 48 8D 4D A0 8B F8")] [Signature("E8 ?? ?? ?? ?? 48 8D 4D A0 8B F8")]
private readonly delegate* unmanaged<nint, Utf8String*, nint, uint> GetKeybindNative = null!; private readonly delegate* unmanaged<nint, Utf8String*, nint, uint> GetKeybindNative = null!;
// TODO Replace with CS in AcquaintanceModule
[Signature("44 8B 89 ?? ?? ?? ?? 4C 8B C1 45 85 C9")]
private readonly delegate* unmanaged<void*, int, nint> GetTellHistory = null!;
[Signature("E8 ?? ?? ?? ?? 48 8D 4D 50 E8 ?? ?? ?? ?? 48 8B 17")] [Signature("E8 ?? ?? ?? ?? 48 8D 4D 50 E8 ?? ?? ?? ?? 48 8B 17")]
private readonly delegate* unmanaged<RaptureLogModule*, ushort, Utf8String*, Utf8String*, ulong, ushort, byte, int, byte, void> PrintTellNative = null!; private readonly delegate* unmanaged<RaptureLogModule*, ushort, Utf8String*, Utf8String*, ulong, ushort, byte, int, byte, void> PrintTellNative = null!;
[Signature("E8 ?? ?? ?? ?? 48 8D 4C 24 ?? E8 ?? ?? ?? ?? 48 8D 8C 24 ?? ?? ?? ?? E8 ?? ?? ?? ?? B0 01")] [Signature("E8 ?? ?? ?? ?? 48 8D 4C 24 ?? E8 ?? ?? ?? ?? 48 8D 8C 24 ?? ?? ?? ?? E8 ?? ?? ?? ?? B0 01")]
private readonly delegate* unmanaged<NetworkModule*, ulong, ushort, Utf8String*, Utf8String*, byte, ulong, bool> SendTellNative = null!; private readonly delegate* unmanaged<NetworkModule*, ulong, ushort, Utf8String*, Utf8String*, byte, ulong, bool> SendTellNative = null!;
// TODO Replace with CS in InfoProxyCrossworldLinkshell // TODO Replace with CS version after https://github.com/aers/FFXIVClientStructs/pull/911
[Signature("E8 ?? ?? ?? ?? 48 8B C8 E8 ?? ?? ?? ?? 45 8D 46 FB")]
private readonly delegate* unmanaged<nint, uint, Utf8String*> GetCrossLinkshellNameNative = null!;
// TODO Replace with CS in InfoProxyLinkshell
[Signature("3B 51 10 73 0F 8B C2 48 83 C0 0B")]
private readonly delegate* unmanaged<nint, uint, ulong*> GetLinkshellInfo = null!;
// TODO Replace with CS in InfoProxyLinkshell
[Signature("E8 ?? ?? ?? ?? 4C 8B C8 44 8D 47 01")]
private readonly delegate* unmanaged<nint, ulong, byte*> GetLinkshellNameNative = null!;
// TODO Replace with CS virtual function
[Signature("40 56 41 54 41 55 41 57 48 83 EC 28 48 8B 01", Fallibility = Fallibility.Fallible)]
private readonly delegate* unmanaged<UIModule*, int, ulong> RotateLinkshellHistoryNative;
// TODO Replace with CS virtual function
[Signature("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 41 56 41 57 48 83 EC 20 48 8B 01 44 8B F2", Fallibility = Fallibility.Fallible)]
private readonly delegate* unmanaged<UIModule*, int, ulong> RotateCrossLinkshellHistoryNative;
// TODO Replace with CS version on Utf8String
[Signature("E8 ?? ?? ?? ?? EB 0A 48 8D 4C 24 ?? E8 ?? ?? ?? ?? 48 8D 8D")] [Signature("E8 ?? ?? ?? ?? EB 0A 48 8D 4C 24 ?? E8 ?? ?? ?? ?? 48 8D 8D")]
private readonly delegate* unmanaged<Utf8String*, int, nint, void> SanitiseString = null!; private readonly delegate* unmanaged<Utf8String*, int, nint, void> SanitiseString = null!;
// Hooks
private delegate byte ChatLogRefreshDelegate(nint log, ushort eventId, AtkValue* value);
private delegate nint ChangeChannelNameDelegate(nint agent);
private delegate void ReplyInSelectedChatModeDelegate(AgentInterface* agent);
private delegate byte SetChatLogTellTarget(nint a1, Utf8String* name, Utf8String* a3, ushort world, ulong contentId, ushort a6, byte a7);
private delegate void EurekaContextMenuTellDelegate(RaptureShellModule* param1, Utf8String* playerName, Utf8String* worldName, ushort world, ulong contentId, ushort param6);
// Client::UI::AddonChatLog.OnRefresh // Client::UI::AddonChatLog.OnRefresh
[Signature("40 53 56 57 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 84 24 ?? ?? ?? ?? 49 8B F0 8B FA", DetourName = nameof(ChatLogRefreshDetour))] [Signature("40 53 56 57 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 84 24 ?? ?? ?? ?? 49 8B F0 8B FA", DetourName = nameof(ChatLogRefreshDetour))]
private Hook<ChatLogRefreshDelegate>? ChatLogRefreshHook { get; init; } private Hook<ChatLogRefreshDelegate>? ChatLogRefreshHook { get; init; }
private delegate byte ChatLogRefreshDelegate(nint log, ushort eventId, AtkValue* value);
// TODO Replace with CS version // TODO Replace all with delegate hooks in API X
[Signature("E8 ?? ?? ?? ?? BA ?? ?? ?? ?? 48 8D 4D B0 48 8B F8 E8 ?? ?? ?? ?? 41 8B D6", DetourName = nameof(ChangeChannelNameDetour))]
private Hook<ChangeChannelNameDelegate>? ChangeChannelNameHook { get; init; } private Hook<ChangeChannelNameDelegate>? ChangeChannelNameHook { get; init; }
private delegate nint ChangeChannelNameDelegate(nint agent);
// TODO Replace with CS version
[Signature("48 89 5C 24 ?? 57 48 83 EC 30 8B B9 ?? ?? ?? ?? 48 8B D9 83 FF FE", DetourName = nameof(ReplyInSelectedChatModeDetour))]
private Hook<ReplyInSelectedChatModeDelegate>? ReplyInSelectedChatModeHook { get; init; } private Hook<ReplyInSelectedChatModeDelegate>? ReplyInSelectedChatModeHook { get; init; }
private delegate void ReplyInSelectedChatModeDelegate(AgentInterface* agent);
// TODO Replace with CS version
[Signature("E8 ?? ?? ?? ?? 4C 8B 7C 24 ?? EB 34", DetourName = nameof(SetChatLogTellTargetDetour))]
private Hook<SetChatLogTellTarget>? SetChatLogTellTargetHook { get; init; } private Hook<SetChatLogTellTarget>? SetChatLogTellTargetHook { get; init; }
private delegate byte SetChatLogTellTarget(nint a1, Utf8String* name, Utf8String* a3, ushort world, ulong contentId, ushort a6, byte a7);
// TODO Replace with CS version
[Signature("E8 ?? ?? ?? ?? EB 8A 48 8B 1D", DetourName = nameof(EurekaContextMenuTell))]
private Hook<EurekaContextMenuTellDelegate>? EurekaContextMenuTellHook { get; init; } private Hook<EurekaContextMenuTellDelegate>? EurekaContextMenuTellHook { get; init; }
private delegate void EurekaContextMenuTellDelegate(RaptureShellModule* param1, Utf8String* playerName, Utf8String* worldName, ushort world, ulong contentId, ushort param6);
// Offsets
#pragma warning disable 0649
// TODO: Replace with CS version under AgentChatLog
[Signature("8B B9 ?? ?? ?? ?? 48 8B D9 83 FF FE 0F 84", Offset = 2)]
private readonly int? ReplyChannelOffset;
// TODO: Replace with CS version under RaptureShellModule
[Signature("89 83 ?? ?? ?? ?? 48 8B 01 83 FE 13 7C 05 41 8B D4 EB 03 83 CA FF FF 90", Offset = 2)]
private readonly int? ShellChannelOffset;
// TODO: Replace with CS version under UiModule
[Signature("4C 8D B6 ?? ?? ?? ?? 41 8B 1E 45 85 E4 74 7A 33 FF 8B EF 66 0F 1F 44 00", Offset = 3)]
private readonly int? LinkshellCycleOffset;
#pragma warning restore 0649
// Pointers // Pointers
@@ -132,11 +66,11 @@ internal sealed unsafe class Chat : IDisposable
// Events // Events
internal event ChatActivatedEventDelegate? Activated;
internal delegate void ChatActivatedEventDelegate(ChatActivatedArgs args); internal delegate void ChatActivatedEventDelegate(ChatActivatedArgs args);
internal event ChatActivatedEventDelegate? Activated;
private Plugin Plugin { get; } private Plugin Plugin { get; }
/// <summary> /// <summary>
/// Holds the current game channel details. /// Holds the current game channel details.
/// `TellPlayerName` and `TellWorldId` are only set when the channel is `InputChannel.Tell`. /// `TellPlayerName` and `TellWorldId` are only set when the channel is `InputChannel.Tell`.
@@ -155,10 +89,18 @@ internal sealed unsafe class Chat : IDisposable
Plugin.GameInteropProvider.InitializeFromAttributes(this); Plugin.GameInteropProvider.InitializeFromAttributes(this);
ChatLogRefreshHook?.Enable(); ChatLogRefreshHook?.Enable();
ChangeChannelNameHook?.Enable();
ReplyInSelectedChatModeHook?.Enable(); ChangeChannelNameHook = Plugin.GameInteropProvider.HookFromAddress<ChangeChannelNameDelegate>(AgentChatLog.Addresses.ChangeChannelName.Value, ChangeChannelNameDetour);
SetChatLogTellTargetHook?.Enable(); ChangeChannelNameHook.Enable();
EurekaContextMenuTellHook?.Enable();
ReplyInSelectedChatModeHook = Plugin.GameInteropProvider.HookFromAddress<ReplyInSelectedChatModeDelegate>(RaptureShellModule.Addresses.ReplyInSelectedChatMode.Value, ReplyInSelectedChatModeDetour);
ReplyInSelectedChatModeHook.Enable();
SetChatLogTellTargetHook = Plugin.GameInteropProvider.HookFromAddress<SetChatLogTellTarget>(RaptureShellModule.Addresses.SetContextTellTarget.Value, SetChatLogTellTargetDetour);
SetChatLogTellTargetHook.Enable();
EurekaContextMenuTellHook = Plugin.GameInteropProvider.HookFromAddress<EurekaContextMenuTellDelegate>(RaptureShellModule.Addresses.SetContextTellTargetInForay.Value, EurekaContextMenuTell);
EurekaContextMenuTellHook.Enable();
Plugin.Framework.Update += InterceptKeybinds; Plugin.Framework.Update += InterceptKeybinds;
Plugin.ClientState.Login += Login; Plugin.ClientState.Login += Login;
@@ -181,48 +123,35 @@ internal sealed unsafe class Chat : IDisposable
internal string? GetLinkshellName(uint idx) internal string? GetLinkshellName(uint idx)
{ {
var infoProxy = Plugin.Functions.GetInfoProxyByIndex(InfoProxyId.LinkShell); var instance = InfoProxyLinkShell.Instance();
if (infoProxy == nint.Zero) var lsInfo = instance->GetLinkshellInfo(idx);
return null;
var lsInfo = GetLinkshellInfo(infoProxy, idx);
if (lsInfo == null) if (lsInfo == null)
return null; return null;
var utf = GetLinkshellNameNative(infoProxy, *lsInfo); var utf = instance->GetLinkshellName(lsInfo);
return utf == null ? null : MemoryHelper.ReadStringNullTerminated((nint) utf); return utf == null ? null : MemoryHelper.ReadStringNullTerminated((nint) utf);
} }
internal string? GetCrossLinkshellName(uint idx) internal string? GetCrossLinkshellName(uint idx)
{ {
var infoProxy = Plugin.Functions.GetInfoProxyByIndex(InfoProxyId.CrossWorldLinkShell); var utf = InfoProxyCrossWorldLinkShell.Instance()->GetCrossworldLinkshellName(idx);
if (infoProxy == nint.Zero)
return null;
var utf = GetCrossLinkshellNameNative(infoProxy, idx);
return utf == null ? null : utf->ToString(); return utf == null ? null : utf->ToString();
} }
internal ulong RotateLinkshellHistory(RotateMode mode) internal static int RotateLinkshellHistory(RotateMode mode)
{ {
if (mode == RotateMode.None && LinkshellCycleOffset != null) var uiModule = UIModule.Instance();
{ if (mode == RotateMode.None)
// for the branch at 6.08: 5E1680 uiModule->LinkshellCycle = -1;
var uiModule = (nint) Framework.Instance()->GetUiModule();
*(int*) (uiModule + LinkshellCycleOffset.Value) = -1;
}
return RotateLinkshellHistoryInternal(RotateLinkshellHistoryNative, mode); return RotateLinkshellHistoryInternal(uiModule->RotateLinkshellHistory, mode);
} }
internal ulong RotateCrossLinkshellHistory(RotateMode mode) => RotateLinkshellHistoryInternal(RotateCrossLinkshellHistoryNative, mode); internal static int RotateCrossLinkshellHistory(RotateMode mode) =>
RotateLinkshellHistoryInternal(UIModule.Instance()->RotateCrossLinkshellHistory, mode);
private static ulong RotateLinkshellHistoryInternal(delegate* unmanaged<UIModule*, int, ulong> func, RotateMode mode) private static int RotateLinkshellHistoryInternal(Func<int, int> func, RotateMode mode)
{ {
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
if (func == null)
return 0;
var idx = mode switch var idx = mode switch
{ {
RotateMode.Forward => 1, RotateMode.Forward => 1,
@@ -230,8 +159,7 @@ internal sealed unsafe class Chat : IDisposable
_ => 0, _ => 0,
}; };
var uiModule = Framework.Instance()->GetUiModule(); return func(idx);
return func(uiModule, idx);
} }
// This function looks up a channel's user-defined color. // This function looks up a channel's user-defined color.
@@ -502,17 +430,7 @@ internal sealed unsafe class Chat : IDisposable
if (agent == nint.Zero) if (agent == nint.Zero)
return ret; return ret;
// E8 ?? ?? ?? ?? 8D 48 F7 var channel = (uint) RaptureShellModule.Instance()->ChatType;
// RaptureShellModule + 0xFD0
var shellModule = (nint) Framework.Instance()->GetUiModule()->GetRaptureShellModule();
if (shellModule == nint.Zero)
return ret;
var channel = (uint) InputChannel.Say;
if (ShellChannelOffset != null)
channel = *(uint*) (shellModule + ShellChannelOffset.Value);
// var channel = *(uint*) (agent + 0x40);
if (channel is 17 or 18) if (channel is 17 or 18)
channel = (uint) InputChannel.Tell; channel = (uint) InputChannel.Tell;
@@ -549,16 +467,14 @@ internal sealed unsafe class Chat : IDisposable
private void ReplyInSelectedChatModeDetour(AgentInterface* agent) private void ReplyInSelectedChatModeDetour(AgentInterface* agent)
{ {
if (ReplyChannelOffset == null) var replyMode = AgentChatLog.Instance()->ReplyChannel;
goto Original;
var replyMode = *(int*) ((nint) agent + ReplyChannelOffset.Value);
if (replyMode == -2) if (replyMode == -2)
goto Original; {
ReplyInSelectedChatModeHook!.Original(agent);
return;
}
SetChannel((InputChannel) replyMode); SetChannel((InputChannel) replyMode);
Original:
ReplyInSelectedChatModeHook!.Original(agent); ReplyInSelectedChatModeHook!.Original(agent);
} }
@@ -592,25 +508,18 @@ internal sealed unsafe class Chat : IDisposable
PreviousChannel = Channel.Channel; PreviousChannel = Channel.Channel;
} }
if (SetChannelTargetTell != null) RaptureShellModule.Instance()->SetTellTargetInForay(playerName, worldName, world, id, param6, false);
SetChannelTargetTell(param1, playerName, worldName, world, id, param6, 0);
EurekaContextMenuTellHook!.Original(param1, playerName, worldName, world, id, param6); EurekaContextMenuTellHook!.Original(param1, playerName, worldName, world, id, param6);
} }
internal ulong GetContentIdForEntry(int index) internal static void SetChannel(InputChannel channel, string? tellTarget = null)
{
return Framework.Instance()->GetUiModule()->GetRaptureLogModule()->GetContentIdForLogMessage(index);
}
internal void SetChannel(InputChannel channel, string? tellTarget = null)
{ {
// ExtraChat linkshells aren't supported in game so we never want to // ExtraChat linkshells aren't supported in game so we never want to
// call the ChangeChatChannel function with them. // call the ChangeChatChannel function with them.
// //
// Callers should call ChatLogWindow.SetChannel() which handles // Callers should call ChatLogWindow.SetChannel() which handles
// ExtraChat channels // ExtraChat channels
if (ChangeChatChannel == null || channel.IsExtraChatLinkshell()) if (channel.IsExtraChatLinkshell())
return; return;
var target = Utf8String.FromString(tellTarget ?? ""); var target = Utf8String.FromString(tellTarget ?? "");
@@ -618,18 +527,15 @@ internal sealed unsafe class Chat : IDisposable
if (idx == uint.MaxValue) if (idx == uint.MaxValue)
idx = 0; idx = 0;
ChangeChatChannel(RaptureShellModule.Instance(), (int) channel, idx, target, 1); RaptureShellModule.Instance()->ChangeChatChannel((int) channel, idx, target, true);
target->Dtor(true); target->Dtor(true);
} }
internal void SetEurekaTellChannel(string name, string worldName, ushort worldId, ulong objectId, ushort param6, byte param7) internal void SetEurekaTellChannel(string name, string worldName, ushort worldId, ulong objectId, ushort param6, bool param7)
{ {
// param6 is 0 for contentId and 1 for objectId // param6 is 0 for contentId and 1 for objectId
// param7 is always 0 ? // param7 is always 0 ?
if (SetChannelTargetTell == null)
return;
if (!UsesTellTempChannel) if (!UsesTellTempChannel)
{ {
UsesTellTempChannel = true; UsesTellTempChannel = true;
@@ -639,7 +545,7 @@ internal sealed unsafe class Chat : IDisposable
var utfName = Utf8String.FromString(name); var utfName = Utf8String.FromString(name);
var utfWorld = Utf8String.FromString(worldName); var utfWorld = Utf8String.FromString(worldName);
SetChannelTargetTell(RaptureShellModule.Instance(), utfName, utfWorld, worldId, objectId, param6, param7); RaptureShellModule.Instance()->SetTellTargetInForay(utfName, utfWorld, worldId, objectId, param6, param7);
utfName->Dtor(true); utfName->Dtor(true);
utfWorld->Dtor(true); utfWorld->Dtor(true);
@@ -687,12 +593,10 @@ internal sealed unsafe class Chat : IDisposable
internal TellHistoryInfo? GetTellHistoryInfo(int index) internal TellHistoryInfo? GetTellHistoryInfo(int index)
{ {
var acquaintanceModule = Framework.Instance()->GetUiModule()->GetAcquaintanceModule(); var ptr = AcquaintanceModule.Instance()->GetTellHistory(index);
if (acquaintanceModule == null)
return null;
var ptr = GetTellHistory(acquaintanceModule, index); // TODO does this check work?
if (ptr == nint.Zero) if (ptr->ContentId == 0)
return null; return null;
var name = MemoryHelper.ReadStringNullTerminated(*(nint*) ptr); var name = MemoryHelper.ReadStringNullTerminated(*(nint*) ptr);
+9 -51
View File
@@ -1,83 +1,41 @@
using ChatTwo.Util; using ChatTwo.Util;
using Dalamud.Utility.Signatures;
using FFXIVClientStructs.FFXIV.Client.System.Framework; using FFXIVClientStructs.FFXIV.Client.System.Framework;
using FFXIVClientStructs.FFXIV.Client.UI.Agent; using FFXIVClientStructs.FFXIV.Client.UI.Agent;
using FFXIVClientStructs.FFXIV.Client.UI.Info; using FFXIVClientStructs.FFXIV.Client.UI.Info;
using FFXIVClientStructs.FFXIV.Component.GUI;
namespace ChatTwo.GameFunctions; namespace ChatTwo.GameFunctions;
internal sealed unsafe class Context internal sealed unsafe class Context
{ {
// TODO: Replace with CS version after https://github.com/aers/FFXIVClientStructs/pull/873 got merged internal static void InviteToNoviceNetwork(string name, ushort world)
[Signature("E8 ?? ?? ?? ?? E9 ?? ?? ?? ?? 48 8B CB E8 ?? ?? ?? ?? 45 33 C9", Fallibility = Fallibility.Fallible)]
private readonly delegate* unmanaged<IntPtr, ulong, ushort, byte*, byte> InviteToNoviceNetworkNative = null!;
[Signature("E8 ?? ?? ?? ?? EB 7B 49 8B 06", Fallibility = Fallibility.Fallible)]
private readonly delegate* unmanaged<AgentInterface*, uint, void> LinkItemNative = null!;
[Signature("E8 ?? ?? ?? ?? EB 3F 83 F8 FE", Fallibility = Fallibility.Fallible)]
private readonly delegate* unmanaged<AgentInterface*, ushort, uint, byte, void> ItemComparisonNative = null!;
private Plugin Plugin { get; }
internal Context(Plugin plugin)
{ {
Plugin = plugin;
Plugin.GameInteropProvider.InitializeFromAttributes(this);
}
internal void InviteToNoviceNetwork(string name, ushort world)
{
if (InviteToNoviceNetworkNative == null)
return;
// 6.3: 221EFD
var a1 = Plugin.Functions.GetInfoProxyByIndex((InfoProxyId) 0x14);
// can specify content id if we have it, but there's no need // can specify content id if we have it, but there's no need
fixed (byte* namePtr = name.ToTerminatedBytes()) { fixed (byte* namePtr = name.ToTerminatedBytes()) {
InviteToNoviceNetworkNative(a1, 0, world, namePtr); InfoProxyNoviceNetwork.Instance()->InviteToNoviceNetwork(0, world, namePtr);
} }
} }
// These context menu things come from AgentChatLog.vf0 at the bottom internal static void TryOn(uint itemId, byte stainId)
// 0x10000: item comparison
// 0x10001: try on
// 0x10002: search for item
// 0x10003: link
// 0x10005: copy item name
// 0x10006: search recipes using this material
internal void TryOn(uint itemId, byte stainId)
{ {
AgentTryon.TryOn(0xFF, itemId, stainId, 0, 0); AgentTryon.TryOn(0xFF, itemId, stainId, 0, 0);
} }
internal void LinkItem(uint itemId) internal static void LinkItem(uint itemId)
{ {
if (LinkItemNative == null) AgentChatLog.Instance()->LinkItem(itemId);
return;
var agent = Framework.Instance()->GetUiModule()->GetAgentModule()->GetAgentByInternalId(AgentId.ChatLog);
LinkItemNative(agent, itemId);
} }
internal void OpenItemComparison(uint itemId) internal static void OpenItemComparison(uint itemId)
{ {
if (ItemComparisonNative == null) AgentItemComp.Instance()->CompareItem(0x4D, itemId, 0);
return;
var agent = Framework.Instance()->GetUiModule()->GetAgentModule()->GetAgentByInternalId(AgentId.ItemCompare);
ItemComparisonNative(agent, 0x4D, itemId, 0);
} }
internal void SearchForRecipesUsingItem(uint itemId) internal static void SearchForRecipesUsingItem(uint itemId)
{ {
AgentRecipeProductList.Instance()->SearchForRecipesUsingItem(itemId); AgentRecipeProductList.Instance()->SearchForRecipesUsingItem(itemId);
} }
internal void SearchForItem(uint itemId) internal static void SearchForItem(uint itemId)
{ {
Framework.Instance()->GetUiModule()->GetItemFinderModule()->SearchForItem(itemId, true); Framework.Instance()->GetUiModule()->GetItemFinderModule()->SearchForItem(itemId, true);
} }
+8 -32
View File
@@ -17,16 +17,6 @@ namespace ChatTwo.GameFunctions;
internal unsafe class GameFunctions : IDisposable internal unsafe class GameFunctions : IDisposable
{ {
#region Functions
// TODO: Can be replaced with CS version soon
[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!;
// TODO: Can be replaced with CS version soon
[Signature("E8 ?? ?? ?? ?? EB 20 48 8B 46 28", Fallibility = Fallibility.Fallible)]
private readonly delegate* unmanaged<AgentInterface*, uint, void> OpenAchievementNative = null!;
#endregion
#region Hooks #region Hooks
private delegate nint ResolveTextCommandPlaceholderDelegate(nint a1, byte* placeholderText, byte a3, byte a4); private delegate nint ResolveTextCommandPlaceholderDelegate(nint a1, byte* placeholderText, byte a3, byte a4);
@@ -35,16 +25,12 @@ internal unsafe class GameFunctions : IDisposable
#endregion #endregion
private Plugin Plugin { get; } private Plugin Plugin { get; }
internal Party Party { get; }
internal Chat Chat { get; } internal Chat Chat { get; }
internal Context Context { get; }
internal GameFunctions(Plugin plugin) internal GameFunctions(Plugin plugin)
{ {
Plugin = plugin; Plugin = plugin;
Party = new Party(Plugin);
Chat = new Chat(Plugin); Chat = new Chat(Plugin);
Context = new Context(Plugin);
Plugin.GameInteropProvider.InitializeFromAttributes(this); Plugin.GameInteropProvider.InitializeFromAttributes(this);
@@ -207,37 +193,27 @@ internal unsafe class GameFunctions : IDisposable
} }
} }
internal bool IsMentor() internal static bool IsMentor()
{ {
return PlayerState.Instance()->IsMentor(); return PlayerState.Instance()->IsMentor();
} }
internal void OpenPartyFinder(uint id) internal static void OpenPartyFinder(uint id)
{ {
if (OpenPartyFinderNative == null) AgentLookingForGroup.Instance()->OpenListing(id);
return;
var agent = Framework.Instance()->GetUiModule()->GetAgentModule()->GetAgentByInternalId(AgentId.LookingForGroup);
if (agent != null)
OpenPartyFinderNative(agent, id);
} }
internal void OpenAchievement(uint id) internal static void OpenAchievement(uint id)
{ {
if (OpenAchievementNative == null) AgentAchievement.Instance()->OpenById(id);
return;
var agent = Framework.Instance()->GetUiModule()->GetAgentModule()->GetAgentByInternalId(AgentId.Achievement);
if (agent != null)
OpenAchievementNative(agent, id);
} }
internal bool IsInInstance() internal static bool IsInInstance()
{ {
return Plugin.Condition[ConditionFlag.BoundByDuty56]; return Plugin.Condition[ConditionFlag.BoundByDuty56];
} }
internal bool TryOpenAdventurerPlate(ulong playerId) internal static bool TryOpenAdventurerPlate(ulong playerId)
{ {
try try
{ {
@@ -251,7 +227,7 @@ internal unsafe class GameFunctions : IDisposable
} }
} }
internal void ClickNoviceNetworkButton() internal static void ClickNoviceNetworkButton()
{ {
var agent = Framework.Instance()->GetUiModule()->GetAgentModule()->GetAgentByInternalId(AgentId.ChatLog); var agent = Framework.Instance()->GetUiModule()->GetAgentModule()->GetAgentByInternalId(AgentId.ChatLog);
// case 3 // case 3
+10 -73
View File
@@ -1,109 +1,46 @@
using ChatTwo.Util; using ChatTwo.Util;
using Dalamud.Utility.Signatures;
using FFXIVClientStructs.FFXIV.Client.System.Framework;
using FFXIVClientStructs.FFXIV.Client.UI.Agent; using FFXIVClientStructs.FFXIV.Client.UI.Agent;
using FFXIVClientStructs.FFXIV.Client.UI.Info; using FFXIVClientStructs.FFXIV.Client.UI.Info;
using FFXIVClientStructs.FFXIV.Component.GUI;
namespace ChatTwo.GameFunctions; namespace ChatTwo.GameFunctions;
internal sealed unsafe class Party internal sealed unsafe class Party
{ {
// TODO: Replace all hooks with CS after https://github.com/aers/FFXIVClientStructs/pull/872 got merged internal static void InviteSameWorld(string name, ushort world, ulong contentId)
[Signature("E8 ?? ?? ?? ?? 33 C0 EB 51", Fallibility = Fallibility.Fallible)]
private readonly delegate* unmanaged<IntPtr, ulong, byte*, ushort, byte> InviteToPartyNative = null!;
[Signature("48 83 EC 38 41 B1 09", Fallibility = Fallibility.Fallible)]
private readonly delegate* unmanaged<IntPtr, ulong, ushort, byte> InviteToPartyContentIdNative = null!;
[Signature("E8 ?? ?? ?? ?? E9 ?? ?? ?? ?? 48 8B 83 ?? ?? ?? ?? 48 85 C0 74 62", Fallibility = Fallibility.Fallible)]
private readonly delegate* unmanaged<IntPtr, ulong, byte> InviteToPartyInInstanceNative = null!;
[Signature("E8 ?? ?? ?? ?? E9 ?? ?? ?? ?? 49 8B 56 20", Fallibility = Fallibility.Fallible)]
private readonly delegate* unmanaged<AgentInterface*, byte*, ushort, ulong, void> PromoteNative = null!;
[Signature("E8 ?? ?? ?? ?? EB 66 49 8B 4E 20", Fallibility = Fallibility.Fallible)]
private readonly delegate* unmanaged<AgentInterface*, byte*, ushort, ulong, void> KickNative = null!;
private Plugin Plugin { get; }
internal Party(Plugin plugin)
{ {
Plugin = plugin;
Plugin.GameInteropProvider.InitializeFromAttributes(this);
}
internal void InviteSameWorld(string name, ushort world, ulong contentId)
{
if (InviteToPartyNative == null)
return;
// 6.11: 214A55
var a1 = Plugin.Functions.GetInfoProxyByIndex(InfoProxyId.PartyInvite);
// this only works if target is on the same world // this only works if target is on the same world
fixed (byte* namePtr = name.ToTerminatedBytes()) { fixed (byte* namePtr = name.ToTerminatedBytes()) {
InviteToPartyNative(a1, contentId, namePtr, world); InfoProxyPartyInvite.Instance()->InviteToParty(contentId, namePtr, world);
} }
} }
internal void InviteOtherWorld(ulong contentId) internal static void InviteOtherWorld(ulong contentId)
{ {
if (InviteToPartyContentIdNative == null)
return;
// 6.11: 214A55
var a1 = Plugin.Functions.GetInfoProxyByIndex(InfoProxyId.PartyInvite);
// third param is world, but it requires a specific world // third param is world, but it requires a specific world
// if they're not on that world, it will fail // if they're not on that world, it will fail
// pass 0 and it will work on any world EXCEPT for the world the // pass 0 and it will work on any world EXCEPT for the world the
// current player is on // current player is on
if (contentId != 0) if (contentId != 0)
InviteToPartyContentIdNative(a1, contentId, 0); InfoProxyPartyInvite.Instance()->InviteToPartyContentId(contentId, 0);
} }
internal void InviteInInstance(ulong contentId) internal static void InviteInInstance(ulong contentId)
{ {
if (InviteToPartyInInstanceNative == null)
return;
// 6.11: 214A55
var a1 = Plugin.Functions.GetInfoProxyByIndex(InfoProxyId.PartyInvite);
// third param is world, but it requires a specific world
// if they're not on that world, it will fail
// pass 0 and it will work on any world EXCEPT for the world the
// current player is on
if (contentId != 0) if (contentId != 0)
InviteToPartyInInstanceNative(a1, contentId); InfoProxyPartyInvite.Instance()->InviteToPartyInInstance(contentId);
} }
internal void Kick(string name, ulong contentId) internal static void Kick(string name, ulong contentId)
{ {
if (KickNative == null)
return;
var agent = Framework.Instance()->GetUiModule()->GetAgentModule()->GetAgentByInternalId(AgentId.SocialPartyMember);
if (agent == null)
return;
fixed (byte* namePtr = name.ToTerminatedBytes()) { fixed (byte* namePtr = name.ToTerminatedBytes()) {
KickNative(agent, namePtr, 0, contentId); AgentPartyMember.Instance()->Kick(namePtr, 0, contentId);
} }
} }
internal void Promote(string name, ulong contentId) internal static void Promote(string name, ulong contentId)
{ {
if (PromoteNative == null)
return;
var agent = Framework.Instance()->GetUiModule()->GetAgentModule()->GetAgentByInternalId(AgentId.SocialPartyMember);
if (agent == null)
return;
fixed (byte* namePtr = name.ToTerminatedBytes()) { fixed (byte* namePtr = name.ToTerminatedBytes()) {
PromoteNative(agent, namePtr, 0, contentId); AgentPartyMember.Instance()->Promote(namePtr, 0, contentId);
} }
} }
} }
+6 -8
View File
@@ -8,7 +8,6 @@ using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Hooking; using Dalamud.Hooking;
using Dalamud.Interface.Internal.Notifications; using Dalamud.Interface.Internal.Notifications;
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using Dalamud.Utility.Signatures;
using FFXIVClientStructs.FFXIV.Client.UI.Misc; using FFXIVClientStructs.FFXIV.Client.UI.Misc;
using Lumina.Excel.GeneratedSheets; using Lumina.Excel.GeneratedSheets;
@@ -36,11 +35,9 @@ internal class MessageManager : IAsyncDisposable
private readonly Thread PendingMessageThread; private readonly Thread PendingMessageThread;
private readonly CancellationTokenSource PendingThreadCancellationToken = new(); private readonly CancellationTokenSource PendingThreadCancellationToken = new();
// TODO: replace with CS version // TODO Replace with delegate in API X
private unsafe delegate void ContentIdResolverDelegate(RaptureLogModule* agent, ulong contentId, int messageIndex, ushort worldId, ushort chatType);
[Signature("4C 8B D1 48 8B 89 ?? ?? ?? ?? 48 85 C9", DetourName = nameof(ContentIdResolver))]
private Hook<ContentIdResolverDelegate>? ContentIdResolverHook { get; init; } private Hook<ContentIdResolverDelegate>? ContentIdResolverHook { get; init; }
private unsafe delegate void ContentIdResolverDelegate(RaptureLogModule* agent, ulong contentId, int messageIndex, ushort worldId, ushort chatType);
internal ulong CurrentContentId internal ulong CurrentContentId
{ {
@@ -51,17 +48,18 @@ internal class MessageManager : IAsyncDisposable
} }
} }
internal MessageManager(Plugin plugin) internal unsafe MessageManager(Plugin plugin)
{ {
Plugin = plugin; Plugin = plugin;
Plugin.GameInteropProvider.InitializeFromAttributes(this);
Store = new MessageStore(DatabasePath()); Store = new MessageStore(DatabasePath());
PendingMessageThread = new Thread(() => ProcessPendingMessages(PendingThreadCancellationToken.Token)); PendingMessageThread = new Thread(() => ProcessPendingMessages(PendingThreadCancellationToken.Token));
PendingMessageThread.Start(); PendingMessageThread.Start();
ContentIdResolverHook?.Enable(); ContentIdResolverHook = Plugin.GameInteropProvider.HookFromAddress<ContentIdResolverDelegate>(RaptureLogModule.Addresses.AddMsgSourceEntry.Value, ContentIdResolver);
ContentIdResolverHook.Enable();
Plugin.ChatGui.ChatMessageUnhandled += ChatMessage; Plugin.ChatGui.ChatMessageUnhandled += ChatMessage;
Plugin.Framework.Update += OnFrameworkUpdate; Plugin.Framework.Update += OnFrameworkUpdate;
Plugin.ClientState.Logout += Logout; Plugin.ClientState.Logout += Logout;
+19 -19
View File
@@ -388,13 +388,13 @@ public sealed class PayloadHandler {
if (pf.LinkType == DalamudPartyFinderPayload.PartyFinderLinkType.PartyFinderNotification) if (pf.LinkType == DalamudPartyFinderPayload.PartyFinderLinkType.PartyFinderNotification)
GameFunctions.GameFunctions.OpenPartyFinder(); GameFunctions.GameFunctions.OpenPartyFinder();
else else
LogWindow.Plugin.Functions.OpenPartyFinder(pf.ListingId); GameFunctions.GameFunctions.OpenPartyFinder(pf.ListingId);
break; break;
case ChatTwoPartyFinderPayload pf: case ChatTwoPartyFinderPayload pf:
LogWindow.Plugin.Functions.OpenPartyFinder(pf.Id); GameFunctions.GameFunctions.OpenPartyFinder(pf.Id);
break; break;
case AchievementPayload achievement: case AchievementPayload achievement:
LogWindow.Plugin.Functions.OpenAchievement(achievement.Id); GameFunctions.GameFunctions.OpenAchievement(achievement.Id);
break; break;
case RawPayload raw: case RawPayload raw:
if (Equals(raw, ChunkUtil.PeriodicRecruitmentLink)) if (Equals(raw, ChunkUtil.PeriodicRecruitmentLink))
@@ -473,21 +473,21 @@ public sealed class PayloadHandler {
if (item.EquipSlotCategory.Row != 0) if (item.EquipSlotCategory.Row != 0)
{ {
if (ImGui.Selectable(Language.Context_TryOn)) if (ImGui.Selectable(Language.Context_TryOn))
LogWindow.Plugin.Functions.Context.TryOn(realItemId, 0); GameFunctions.Context.TryOn(realItemId, 0);
if (ImGui.Selectable(Language.Context_ItemComparison)) if (ImGui.Selectable(Language.Context_ItemComparison))
LogWindow.Plugin.Functions.Context.OpenItemComparison(realItemId); GameFunctions.Context.OpenItemComparison(realItemId);
} }
if (item.ItemSearchCategory.Value?.Category == 3) if (item.ItemSearchCategory.Value?.Category == 3)
if (ImGui.Selectable(Language.Context_SearchRecipes)) if (ImGui.Selectable(Language.Context_SearchRecipes))
LogWindow.Plugin.Functions.Context.SearchForRecipesUsingItem(payload.ItemId); GameFunctions.Context.SearchForRecipesUsingItem(payload.ItemId);
if (ImGui.Selectable(Language.Context_SearchForItem)) if (ImGui.Selectable(Language.Context_SearchForItem))
LogWindow.Plugin.Functions.Context.SearchForItem(realItemId); GameFunctions.Context.SearchForItem(realItemId);
if (ImGui.Selectable(Language.Context_Link)) if (ImGui.Selectable(Language.Context_Link))
LogWindow.Plugin.Functions.Context.LinkItem(realItemId); GameFunctions.Context.LinkItem(realItemId);
if (ImGui.Selectable(Language.Context_CopyItemName)) if (ImGui.Selectable(Language.Context_CopyItemName))
ImGui.SetClipboardText(name.TextValue); ImGui.SetClipboardText(name.TextValue);
@@ -511,7 +511,7 @@ public sealed class PayloadHandler {
var realItemId = payload.RawItemId; var realItemId = payload.RawItemId;
if (ImGui.Selectable(Language.Context_Link)) if (ImGui.Selectable(Language.Context_Link))
LogWindow.Plugin.Functions.Context.LinkItem(realItemId); GameFunctions.Context.LinkItem(realItemId);
if (ImGui.Selectable(Language.Context_CopyItemName)) if (ImGui.Selectable(Language.Context_CopyItemName))
ImGui.SetClipboardText(name.TextValue); ImGui.SetClipboardText(name.TextValue);
@@ -555,7 +555,7 @@ public sealed class PayloadHandler {
} }
else if (validContentId) else if (validContentId)
{ {
LogWindow.Plugin.Functions.Chat.SetEurekaTellChannel(player.PlayerName, world.Name.ToString(), (ushort) world.RowId, chunk.Message!.ContentId, 0, 0); LogWindow.Plugin.Functions.Chat.SetEurekaTellChannel(player.PlayerName, world.Name.ToString(), (ushort) world.RowId, chunk.Message!.ContentId, 0, false);
} }
LogWindow.Activate = true; LogWindow.Activate = true;
@@ -568,7 +568,7 @@ public sealed class PayloadHandler {
var isLeader = party.Length == 0 || Plugin.ClientState.LocalContentId == leader; var isLeader = party.Length == 0 || Plugin.ClientState.LocalContentId == leader;
var member = party.FirstOrDefault(member => member.Name.TextValue == player.PlayerName && member.World.Id == world.RowId); var member = party.FirstOrDefault(member => member.Name.TextValue == player.PlayerName && member.World.Id == world.RowId);
var isInParty = member != default; var isInParty = member != default;
var inInstance = LogWindow.Plugin.Functions.IsInInstance(); var inInstance = GameFunctions.GameFunctions.IsInInstance();
var inPartyInstance = TerritorySheet.GetRow(Plugin.ClientState.TerritoryType)?.TerritoryIntendedUse is (41 or 47 or 48 or 52 or 53); var inPartyInstance = TerritorySheet.GetRow(Plugin.ClientState.TerritoryType)?.TerritoryIntendedUse is (41 or 47 or 48 or 52 or 53);
if (isLeader) if (isLeader)
{ {
@@ -577,15 +577,15 @@ public sealed class PayloadHandler {
if (inInstance && inPartyInstance) if (inInstance && inPartyInstance)
{ {
if (validContentId && ImGui.Selectable(Language.Context_InviteToParty)) if (validContentId && ImGui.Selectable(Language.Context_InviteToParty))
LogWindow.Plugin.Functions.Party.InviteInInstance(chunk.Message!.ContentId); GameFunctions.Party.InviteInInstance(chunk.Message!.ContentId);
} }
else if (!inInstance && ImGui.BeginMenu(Language.Context_InviteToParty)) else if (!inInstance && ImGui.BeginMenu(Language.Context_InviteToParty))
{ {
if (ImGui.Selectable(Language.Context_InviteToParty_SameWorld)) if (ImGui.Selectable(Language.Context_InviteToParty_SameWorld))
LogWindow.Plugin.Functions.Party.InviteSameWorld(player.PlayerName, (ushort) world.RowId, chunk.Message?.ContentId ?? 0); GameFunctions.Party.InviteSameWorld(player.PlayerName, (ushort) world.RowId, chunk.Message?.ContentId ?? 0);
if (validContentId && ImGui.Selectable(Language.Context_InviteToParty_DifferentWorld)) if (validContentId && ImGui.Selectable(Language.Context_InviteToParty_DifferentWorld))
LogWindow.Plugin.Functions.Party.InviteOtherWorld(chunk.Message!.ContentId); GameFunctions.Party.InviteOtherWorld(chunk.Message!.ContentId);
ImGui.EndMenu(); ImGui.EndMenu();
} }
@@ -594,10 +594,10 @@ public sealed class PayloadHandler {
if (isInParty && member != null && (!inInstance || (inInstance && inPartyInstance))) if (isInParty && member != null && (!inInstance || (inInstance && inPartyInstance)))
{ {
if (ImGui.Selectable(Language.Context_Promote)) if (ImGui.Selectable(Language.Context_Promote))
LogWindow.Plugin.Functions.Party.Promote(player.PlayerName, (ulong) member.ContentId); GameFunctions.Party.Promote(player.PlayerName, (ulong) member.ContentId);
if (ImGui.Selectable(Language.Context_KickFromParty)) if (ImGui.Selectable(Language.Context_KickFromParty))
LogWindow.Plugin.Functions.Party.Kick(player.PlayerName, (ulong) member.ContentId); GameFunctions.Party.Kick(player.PlayerName, (ulong) member.ContentId);
} }
} }
@@ -608,8 +608,8 @@ public sealed class PayloadHandler {
if (ImGui.Selectable(Language.Context_AddToBlacklist)) if (ImGui.Selectable(Language.Context_AddToBlacklist))
LogWindow.Plugin.Functions.AddToBlacklist(player.PlayerName, (ushort) world.RowId); LogWindow.Plugin.Functions.AddToBlacklist(player.PlayerName, (ushort) world.RowId);
if (LogWindow.Plugin.Functions.IsMentor() && ImGui.Selectable(Language.Context_InviteToNoviceNetwork)) if (GameFunctions.GameFunctions.IsMentor() && ImGui.Selectable(Language.Context_InviteToNoviceNetwork))
LogWindow.Plugin.Functions.Context.InviteToNoviceNetwork(player.PlayerName, (ushort) world.RowId); GameFunctions.Context.InviteToNoviceNetwork(player.PlayerName, (ushort) world.RowId);
} }
var inputChannel = chunk.Message?.Code.Type.ToInputChannel(); var inputChannel = chunk.Message?.Code.Type.ToInputChannel();
@@ -623,7 +623,7 @@ public sealed class PayloadHandler {
Plugin.TargetManager.Target = obj; Plugin.TargetManager.Target = obj;
if (validContentId && ImGui.Selectable(Language.Context_AdventurerPlate)) if (validContentId && ImGui.Selectable(Language.Context_AdventurerPlate))
if (!LogWindow.Plugin.Functions.TryOpenAdventurerPlate(chunk.Message!.ContentId)) if (!GameFunctions.GameFunctions.TryOpenAdventurerPlate(chunk.Message!.ContentId))
WrapperUtil.AddNotification(Language.Context_AdventurerPlateError, NotificationType.Warning); WrapperUtil.AddNotification(Language.Context_AdventurerPlateError, NotificationType.Warning);
// View Party Finder 0x2E // View Party Finder 0x2E
+5 -5
View File
@@ -191,12 +191,12 @@ public sealed class ChatLogWindow : Window
if (info.Channel is InputChannel.Linkshell1 && info.Rotate != RotateMode.None) if (info.Channel is InputChannel.Linkshell1 && info.Rotate != RotateMode.None)
{ {
var idx = Plugin.Functions.Chat.RotateLinkshellHistory(mode); var idx = GameFunctions.Chat.RotateLinkshellHistory(mode);
TempChannel = info.Channel.Value + (uint) idx; TempChannel = info.Channel.Value + (uint) idx;
} }
else if (info.Channel is InputChannel.CrossLinkshell1 && info.Rotate != RotateMode.None) else if (info.Channel is InputChannel.CrossLinkshell1 && info.Rotate != RotateMode.None)
{ {
var idx = Plugin.Functions.Chat.RotateCrossLinkshellHistory(mode); var idx = GameFunctions.Chat.RotateCrossLinkshellHistory(mode);
TempChannel = info.Channel.Value + (uint) idx; TempChannel = info.Channel.Value + (uint) idx;
} }
} }
@@ -665,7 +665,7 @@ 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 showNovice = Plugin.Config.ShowNoviceNetwork && GameFunctions.GameFunctions.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();
@@ -788,7 +788,7 @@ public sealed class ChatLogWindow : Window
ImGui.SameLine(); ImGui.SameLine();
if (ImGuiUtil.IconButton(FontAwesomeIcon.Leaf)) if (ImGuiUtil.IconButton(FontAwesomeIcon.Leaf))
Plugin.Functions.ClickNoviceNetworkButton(); GameFunctions.GameFunctions.ClickNoviceNetworkButton();
} }
internal void SetChannel(InputChannel? channel) internal void SetChannel(InputChannel? channel)
@@ -814,7 +814,7 @@ public sealed class ChatLogWindow : Window
return; return;
} }
Plugin.Functions.Chat.SetChannel(channel.Value); GameFunctions.Chat.SetChannel(channel.Value);
} }
private void SendChatBox(Tab? activeTab) private void SendChatBox(Tab? activeTab)