From 15d1575c591b408c72423ffecd84dcc070529333 Mon Sep 17 00:00:00 2001 From: Anna Clemens Date: Sat, 29 Jan 2022 00:43:51 -0500 Subject: [PATCH] feat: handle tells --- ChatTwo/GameFunctions/Chat.cs | 34 +++++++++-- ChatTwo/GameFunctions/Types/TellReason.cs | 1 + ChatTwo/GameFunctions/Types/TellTarget.cs | 15 +++++ ChatTwo/Ui/ChatLog.cs | 70 ++++++++++++++++------- 4 files changed, 96 insertions(+), 24 deletions(-) create mode 100755 ChatTwo/GameFunctions/Types/TellTarget.cs diff --git a/ChatTwo/GameFunctions/Chat.cs b/ChatTwo/GameFunctions/Chat.cs index b2e7f43..62d5c52 100755 --- a/ChatTwo/GameFunctions/Chat.cs +++ b/ChatTwo/GameFunctions/Chat.cs @@ -36,6 +36,15 @@ internal sealed unsafe class Chat : IDisposable { [Signature("44 8B 89 ?? ?? ?? ?? 4C 8B C1")] private readonly delegate* unmanaged _getTellHistory = null!; + [Signature("E8 ?? ?? ?? ?? B8 ?? ?? ?? ?? 48 8D 4D 50")] + private readonly delegate* unmanaged _printTell = null!; + + [Signature("E8 ?? ?? ?? ?? 48 8D 4C 24 ?? E8 ?? ?? ?? ?? 48 8D 8C 24 ?? ?? ?? ?? E8 ?? ?? ?? ?? B0 01")] + private readonly delegate* unmanaged _sendTell = null!; + + [Signature("E8 ?? ?? ?? ?? F6 43 0A 40")] + private readonly delegate* unmanaged _getNetworkModule = null!; + // Hooks private delegate byte ChatLogRefreshDelegate(IntPtr log, ushort eventId, AtkValue* value); @@ -84,7 +93,7 @@ internal sealed unsafe class Chat : IDisposable { // Events - internal delegate void ChatActivatedEventDelegate(string? input, ChannelSwitchInfo info); + internal delegate void ChatActivatedEventDelegate(string? input, ChannelSwitchInfo info, TellReason? reason, TellTarget? target); internal event ChatActivatedEventDelegate? Activated; @@ -279,7 +288,7 @@ internal sealed unsafe class Chat : IDisposable { } try { - this.Activated?.Invoke(null, info); + this.Activated?.Invoke(null, info, TellReason.Reply, null); } catch (Exception ex) { PluginLog.LogError(ex, "Error in chat Activated event"); } @@ -315,7 +324,7 @@ internal sealed unsafe class Chat : IDisposable { } try { - this.Activated?.Invoke(eventInput, new ChannelSwitchInfo(null)); + this.Activated?.Invoke(eventInput, new ChannelSwitchInfo(null), null, null); } catch (Exception ex) { PluginLog.LogError(ex, "Error in chat Activated event"); } @@ -393,7 +402,12 @@ internal sealed unsafe class Chat : IDisposable { private byte SetChatLogTellTargetDetour(IntPtr a1, Utf8String* name, Utf8String* a3, ushort world, ulong contentId, ushort reason, byte a7) { if (name != null) { - PluginLog.Log($"{name->ToString()}@{world} ({contentId}) {(TellReason) reason}"); + try { + var target = new TellTarget(name->ToString(), world, contentId, (TellReason) reason); + this.Activated?.Invoke(null, new ChannelSwitchInfo(InputChannel.Tell), (TellReason) reason, target); + } catch (Exception ex) { + PluginLog.LogError(ex, "Error in chat Activated event"); + } } return this.SetChatLogTellTargetHook!.Original(a1, name, a3, world, contentId, reason, a7); @@ -487,4 +501,16 @@ internal sealed unsafe class Chat : IDisposable { return new TellHistoryInfo(name, world, contentId); } + + internal void SendTell(TellReason reason, ulong contentId, string name, ushort homeWorld, string message) { + var uName = Utf8String.FromString(name); + var uMessage = Utf8String.FromString(message); + + var networkModule = this._getNetworkModule(Framework.Instance()); + var a1 = *(IntPtr*) (networkModule + 8); + var logModule = Framework.Instance()->GetUiModule()->GetRaptureLogModule(); + + this._printTell(logModule, 33, uName, uMessage, contentId, homeWorld, 255, 0, 0); + this._sendTell(a1, contentId, homeWorld, uName, uMessage, (byte) reason, homeWorld); + } } diff --git a/ChatTwo/GameFunctions/Types/TellReason.cs b/ChatTwo/GameFunctions/Types/TellReason.cs index d8f9f83..fbdf022 100755 --- a/ChatTwo/GameFunctions/Types/TellReason.cs +++ b/ChatTwo/GameFunctions/Types/TellReason.cs @@ -3,5 +3,6 @@ namespace ChatTwo.GameFunctions.Types; internal enum TellReason { Direct = 0, PartyFinder = 1, + Reply = 2, Friend = 3, } diff --git a/ChatTwo/GameFunctions/Types/TellTarget.cs b/ChatTwo/GameFunctions/Types/TellTarget.cs new file mode 100755 index 0000000..8daa2dc --- /dev/null +++ b/ChatTwo/GameFunctions/Types/TellTarget.cs @@ -0,0 +1,15 @@ +namespace ChatTwo.GameFunctions.Types; + +internal sealed class TellTarget { + internal string Name { get; } + internal ushort World { get; } + internal ulong ContentId { get; } + internal TellReason Reason { get; } + + internal TellTarget(string name, ushort world, ulong contentId, TellReason reason) { + this.Name = name; + this.World = world; + this.ContentId = contentId; + this.Reason = reason; + } +} diff --git a/ChatTwo/Ui/ChatLog.cs b/ChatTwo/Ui/ChatLog.cs index e3f5383..9e923ac 100755 --- a/ChatTwo/Ui/ChatLog.cs +++ b/ChatTwo/Ui/ChatLog.cs @@ -4,6 +4,7 @@ using ChatTwo.GameFunctions.Types; using ChatTwo.Util; using Dalamud.Game.ClientState.Keys; using Dalamud.Game.Command; +using Dalamud.Game.Text.SeStringHandling; using Dalamud.Game.Text.SeStringHandling.Payloads; using Dalamud.Interface; using Dalamud.Logging; @@ -25,7 +26,7 @@ internal sealed class ChatLog : IUiComponent { private int _inputBacklogIdx = -1; private int _lastTab; private InputChannel? _tempChannel; - private (string, string, ulong)? _tellTarget; + private TellTarget? _tellTarget; private int _tellIdx; private PayloadHandler PayloadHandler { get; } @@ -52,7 +53,7 @@ internal sealed class ChatLog : IUiComponent { this.Ui.Plugin.CommandManager.RemoveHandler("/clearlog2"); } - private void Activated(string? input, ChannelSwitchInfo info) { + private void Activated(string? input, ChannelSwitchInfo info, TellReason? reason, TellTarget? target) { this.Activate = true; if (input != null && !this.Chat.Contains(input)) { this.Chat += input; @@ -67,18 +68,24 @@ internal sealed class ChatLog : IUiComponent { this._tempChannel = info.Channel.Value; } - if (info.Channel is InputChannel.Tell && info.Rotate != RotateMode.None) { - this._tellIdx = prevTemp != InputChannel.Tell - ? 0 - : info.Rotate == RotateMode.Reverse - ? -1 - : 1; + if (info.Channel is InputChannel.Tell) { + if (info.Rotate != RotateMode.None) { + this._tellIdx = prevTemp != InputChannel.Tell + ? 0 + : info.Rotate == RotateMode.Reverse + ? -1 + : 1; - var tellInfo = this.Ui.Plugin.Functions.Chat.GetTellHistoryInfo(this._tellIdx); - if (tellInfo != null) { - var world = this.Ui.Plugin.DataManager.GetExcelSheet()?.GetRow(tellInfo.World); - if (world != null) { - this._tellTarget = (tellInfo.Name, world.Name, tellInfo.ContentId); + var tellInfo = this.Ui.Plugin.Functions.Chat.GetTellHistoryInfo(this._tellIdx); + if (tellInfo != null && reason != null) { + this._tellTarget = new TellTarget(tellInfo.Name, (ushort) tellInfo.World, tellInfo.ContentId, reason.Value); + } + } else { + this._tellIdx = 0; + this._tellTarget = null; + + if (target != null) { + this._tellTarget = target; } } } else { @@ -211,7 +218,8 @@ internal sealed class ChatLog : IUiComponent { } try { - this.Activated(null, info); + TellReason? reason = info.Channel == InputChannel.Tell ? TellReason.Reply : null; + this.Activated(null, info, reason, null); } catch (Exception ex) { PluginLog.LogError(ex, "Error in chat Activated event"); } @@ -261,7 +269,17 @@ internal sealed class ChatLog : IUiComponent { try { if (this._tempChannel != null) { if (this._tellTarget != null) { - ImGui.TextUnformatted($"Tell {this._tellTarget.Value.Item1}@{this._tellTarget.Value.Item2}"); + var world = this.Ui.Plugin.DataManager.GetExcelSheet() + ?.GetRow(this._tellTarget.World) + ?.Name + ?.RawString ?? "???"; + + this.DrawChunks(new List { + new TextChunk(null, null, "Tell "), + new TextChunk(null, null, this._tellTarget.Name), + new IconChunk(null, null, BitmapFontIcon.CrossWorld), + new TextChunk(null, null, world), + }); } else { ImGui.TextUnformatted(this._tempChannel.Value.ToChatType().Name()); } @@ -333,12 +351,23 @@ internal sealed class ChatLog : IUiComponent { this.AddBacklog(trimmed); this._inputBacklogIdx = -1; - if (this._tempChannel != null) { - if (this._tellTarget != null) { - trimmed = $"{this._tempChannel.Value.Prefix()} {this._tellTarget.Value.Item1}@{this._tellTarget.Value.Item2} {trimmed}"; - } else { - trimmed = $"{this._tempChannel.Value.Prefix()} {trimmed}"; + if (this._tellTarget != null) { + var target = this._tellTarget; + var reason = target.Reason; + var world = this.Ui.Plugin.DataManager.GetExcelSheet()?.GetRow(target.World); + if (world is { IsPublic: true }) { + if (reason == TellReason.Reply && this.Ui.Plugin.Common.Functions.FriendList.List.Any(friend => friend.ContentId == target.ContentId)) { + reason = TellReason.Friend; + } + + this.Ui.Plugin.Functions.Chat.SendTell(reason, target.ContentId, target.Name, (ushort) world.RowId, trimmed); } + + goto Skip; + } + + if (this._tempChannel != null) { + trimmed = $"{this._tempChannel.Value.Prefix()} {trimmed}"; } else if (activeTab is { Channel: { } channel } && !trimmed.StartsWith('/')) { trimmed = $"{channel.Prefix()} {trimmed}"; } @@ -346,6 +375,7 @@ internal sealed class ChatLog : IUiComponent { this.Ui.Plugin.Common.Functions.Chat.SendMessage(trimmed); } + Skip: this.Chat = string.Empty; }