From c8780e2e12a0d5a19773b71fd76a794a615ebb55 Mon Sep 17 00:00:00 2001 From: Anna Date: Mon, 31 May 2021 01:03:27 -0400 Subject: [PATCH] feat(emotes): add /emoteid command --- .../Tools/Illegal/Emote/EmoteTool.cs | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/RoleplayersToolbox/Tools/Illegal/Emote/EmoteTool.cs b/RoleplayersToolbox/Tools/Illegal/Emote/EmoteTool.cs index df703db..fbcfe0b 100755 --- a/RoleplayersToolbox/Tools/Illegal/Emote/EmoteTool.cs +++ b/RoleplayersToolbox/Tools/Illegal/Emote/EmoteTool.cs @@ -1,6 +1,8 @@ #if ILLEGAL using System; +using System.Runtime.InteropServices; +using Dalamud.Game.Command; using Dalamud.Hooking; using ImGuiNET; @@ -8,27 +10,46 @@ namespace RoleplayersToolbox.Tools.Illegal.Emote { internal class EmoteTool : BaseTool, IDisposable { private static class Signatures { internal const string SetActionOnHotbar = "E8 ?? ?? ?? ?? 4C 39 6F 08"; + internal const string RunEmote = "E8 ?? ?? ?? ?? 40 84 ED 74 18"; + internal const string RunEmoteFirstArg = "48 8D 0D ?? ?? ?? ?? 0F 45 C5 88 44 24 31 E8"; + internal const string RunEmoteThirdArg = "48 8D 05 ?? ?? ?? ?? 33 DB 48 89 44 24 ?? B8"; } private delegate IntPtr SetActionOnHotbarDelegate(IntPtr a1, IntPtr a2, byte actionType, uint actionId); + private delegate byte RunEmoteDelegate(IntPtr a1, ushort emoteId, IntPtr a3); + public override string Name => "Emotes"; private Plugin Plugin { get; } private Hook? SetActionOnHotbarHook { get; } + private RunEmoteDelegate? RunEmoteFunction { get; } + private readonly IntPtr _runEmoteFirstArg; + private readonly IntPtr _runEmoteThirdArg; + private bool Custom { get; set; } private Emote? Emote { get; set; } internal EmoteTool(Plugin plugin) { this.Plugin = plugin; + this.Plugin.Interface.CommandManager.AddHandler("/emoteid", new CommandInfo(this.EmoteIdCommand)); + if (this.Plugin.Interface.TargetModuleScanner.TryScanText(Signatures.SetActionOnHotbar, out var setPtr)) { this.SetActionOnHotbarHook = new Hook(setPtr, new SetActionOnHotbarDelegate(this.SetActionOnHotbarDetour)); this.SetActionOnHotbarHook.Enable(); } + + if (this.Plugin.Interface.TargetModuleScanner.TryScanText(Signatures.RunEmote, out var runEmotePtr)) { + this.RunEmoteFunction = Marshal.GetDelegateForFunctionPointer(runEmotePtr); + } + + this.Plugin.Interface.TargetModuleScanner.TryGetStaticAddressFromSig(Signatures.RunEmoteFirstArg, out this._runEmoteFirstArg); + this.Plugin.Interface.TargetModuleScanner.TryGetStaticAddressFromSig(Signatures.RunEmoteThirdArg, out this._runEmoteThirdArg); } public void Dispose() { this.SetActionOnHotbarHook?.Dispose(); + this.Plugin.Interface.CommandManager.RemoveHandler("/emoteid"); } public override void DrawSettings(ref bool anyChanged) { @@ -62,6 +83,10 @@ namespace RoleplayersToolbox.Tools.Illegal.Emote { this.Custom = false; this.Emote = null; } + + ImGui.Separator(); + + ImGui.TextUnformatted("This tool also adds the /emoteid command, allowing you to run an emote based on its ID. This does NOT allow you to use emotes you do not have unlocked. For example, to use the sleep emote, run /emoteid 88."); } private IntPtr SetActionOnHotbarDetour(IntPtr a1, IntPtr a2, byte actionType, uint actionId) { @@ -74,6 +99,22 @@ namespace RoleplayersToolbox.Tools.Illegal.Emote { this.Emote = null; return this.SetActionOnHotbarHook!.Original(a1, a2, 6, (uint) emote); } + + private void EmoteIdCommand(string command, string arguments) { + if (ushort.TryParse(arguments, out var emoteId)) { + this.RunEmote(emoteId); + } + } + + private unsafe void RunEmote(ushort emoteId) { + if (this.RunEmoteFunction == null || this._runEmoteFirstArg == IntPtr.Zero || this._runEmoteThirdArg == IntPtr.Zero) { + return; + } + + fixed (void* thirdArg = &this._runEmoteThirdArg) { + this.RunEmoteFunction(this._runEmoteFirstArg, emoteId, (IntPtr) thirdArg); + } + } } }