diff --git a/Pilz.Dalamud/ActivityContexts/ActivityType.cs b/Pilz.Dalamud/ActivityContexts/ActivityType.cs index 687433f..d82d5ab 100644 --- a/Pilz.Dalamud/ActivityContexts/ActivityType.cs +++ b/Pilz.Dalamud/ActivityContexts/ActivityType.cs @@ -11,8 +11,8 @@ namespace Pilz.Dalamud.ActivityContexts [JsonConverter(typeof(StringEnumConverter))] public enum ActivityType { - None, - PveDuty, - PvpDuty + None = 0x0, + PveDuty = 0x1, + PvpDuty = 0x2 } } diff --git a/Pilz.Dalamud/Nameplates/EventArgs/AddonNamePlate_SetPlayerNameManagedEventArgs.cs b/Pilz.Dalamud/Nameplates/EventArgs/AddonNamePlate_SetPlayerNameManagedEventArgs.cs index 7cb50a1..494bdd1 100644 --- a/Pilz.Dalamud/Nameplates/EventArgs/AddonNamePlate_SetPlayerNameManagedEventArgs.cs +++ b/Pilz.Dalamud/Nameplates/EventArgs/AddonNamePlate_SetPlayerNameManagedEventArgs.cs @@ -10,8 +10,11 @@ namespace Pilz.Dalamud.Nameplates.EventArgs { public class AddonNamePlate_SetPlayerNameManagedEventArgs : HookWithResultManagedBaseEventArgs { - private new AddonNamePlate_SetPlayerNameEventArgs OriginalEventArgs - => base.OriginalEventArgs as AddonNamePlate_SetPlayerNameEventArgs; + public new AddonNamePlate_SetPlayerNameEventArgs OriginalEventArgs + { + get => base.OriginalEventArgs as AddonNamePlate_SetPlayerNameEventArgs; + set => base.OriginalEventArgs = value; + } public SafeNameplateObject SafeNameplateObject { get; set; } public SeString Title { get; set; } diff --git a/Pilz.Dalamud/Nameplates/EventArgs/HookBaseEventArgs.cs b/Pilz.Dalamud/Nameplates/EventArgs/HookBaseEventArgs.cs index 1418566..2df854d 100644 --- a/Pilz.Dalamud/Nameplates/EventArgs/HookBaseEventArgs.cs +++ b/Pilz.Dalamud/Nameplates/EventArgs/HookBaseEventArgs.cs @@ -8,6 +8,11 @@ namespace Pilz.Dalamud.Nameplates.EventArgs { public abstract class HookBaseEventArgs { - public bool CallOriginal { get; set; } = true; + internal event Action CallOriginal; + + public void Original() + { + CallOriginal?.Invoke(); + } } } diff --git a/Pilz.Dalamud/Nameplates/EventArgs/HookWithResultBaseEventArgs.cs b/Pilz.Dalamud/Nameplates/EventArgs/HookWithResultBaseEventArgs.cs index 4805bb8..ad3c130 100644 --- a/Pilz.Dalamud/Nameplates/EventArgs/HookWithResultBaseEventArgs.cs +++ b/Pilz.Dalamud/Nameplates/EventArgs/HookWithResultBaseEventArgs.cs @@ -6,8 +6,16 @@ using System.Threading.Tasks; namespace Pilz.Dalamud.Nameplates.EventArgs { - public abstract class HookWithResultBaseEventArgs : HookBaseEventArgs + public abstract class HookWithResultBaseEventArgs { + internal event Func CallOriginal; + public TResult Result { get; set; } + + // Call Original based on the given properties + public TResult Original() + { + return CallOriginal.Invoke(); + } } } diff --git a/Pilz.Dalamud/Nameplates/Model/SafeNameplateObject.cs b/Pilz.Dalamud/Nameplates/Model/SafeNameplateObject.cs index 04aa2c6..6a39ba0 100644 --- a/Pilz.Dalamud/Nameplates/Model/SafeNameplateObject.cs +++ b/Pilz.Dalamud/Nameplates/Model/SafeNameplateObject.cs @@ -12,8 +12,8 @@ namespace Pilz.Dalamud.Nameplates.Model { public class SafeNameplateObject { - public readonly IntPtr Pointer; - public readonly AddonNamePlate.NamePlateObject Data; + public IntPtr Pointer { get; } + public AddonNamePlate.NamePlateObject Data { get; } private int _Index; private SafeNameplateInfo _NamePlateInfo; diff --git a/Pilz.Dalamud/Nameplates/NameplateHooks.cs b/Pilz.Dalamud/Nameplates/NameplateHooks.cs index 659705a..25aff71 100644 --- a/Pilz.Dalamud/Nameplates/NameplateHooks.cs +++ b/Pilz.Dalamud/Nameplates/NameplateHooks.cs @@ -8,6 +8,8 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using Pilz.Dalamud.Nameplates.Model; +using Lumina.Excel.GeneratedSheets; +using System.Xml.Linq; namespace Pilz.Dalamud.Nameplates { @@ -17,14 +19,14 @@ namespace Pilz.Dalamud.Nameplates /// Will be executed when the the Game wants to update the content of a nameplate with the details of the Player. /// public event AddonNamePlate_SetPlayerNameEventHandler AddonNamePlate_SetPlayerName; - public delegate IntPtr AddonNamePlate_SetPlayerNameEventHandler(AddonNamePlate_SetPlayerNameEventArgs eventArgs); + public delegate void AddonNamePlate_SetPlayerNameEventHandler(AddonNamePlate_SetPlayerNameEventArgs eventArgs); /// /// Will be executed when the the Game wants to update the content of a nameplate with the details of the Player. /// This will event acts on a higher level with SeString instead of IntPtr e.g. /// public event AddonNamePlate_SetPlayerNameManagedEventHandler AddonNamePlate_SetPlayerNameManaged; - public delegate IntPtr AddonNamePlate_SetPlayerNameManagedEventHandler(AddonNamePlate_SetPlayerNameManagedEventArgs eventArgs); + public delegate void AddonNamePlate_SetPlayerNameManagedEventHandler(AddonNamePlate_SetPlayerNameManagedEventArgs eventArgs); [Signature("48 89 5C 24 ?? 48 89 6C 24 ?? 56 57 41 54 41 56 41 57 48 83 EC 40 44 0F B6 E2", DetourName = nameof(SetPlayerNameplateDetour))] private readonly Hook? hook_AddonNamePlate_SetPlayerNameplateDetour = null; @@ -91,10 +93,6 @@ namespace Pilz.Dalamud.Nameplates if (IsHookEnabled(hook_AddonNamePlate_SetPlayerNameplateDetour)) { - var freeTitle = false; - var freeName = false; - var freeFreeCompany = false; - var eventArgs = new AddonNamePlate_SetPlayerNameEventArgs { PlayerNameplateObjectPtr = playerNameplateObjectPtr, @@ -106,11 +104,34 @@ namespace Pilz.Dalamud.Nameplates IconID = iconId }; + void callOriginal() + { + eventArgs.Result = eventArgs.Original(); + } + + // Add handler for the Original call + eventArgs.CallOriginal += () => + { + return hook_AddonNamePlate_SetPlayerNameplateDetour.Original( + eventArgs.PlayerNameplateObjectPtr, + eventArgs.IsTitleAboveName, + eventArgs.IsTitleVisible, + eventArgs.TitlePtr, + eventArgs.NamePtr, + eventArgs.FreeCompanyPtr, + eventArgs.IconID); + }; + // Invoke Event + var hasDefaultHookEvent = AddonNamePlate_SetPlayerName != null; AddonNamePlate_SetPlayerName?.Invoke(eventArgs); if (AddonNamePlate_SetPlayerNameManaged != null) { + var freeTitle = false; + var freeName = false; + var freeFreeCompany = false; + // Create NamePlateObject if possible var namePlateObj = new SafeNameplateObject(playerNameplateObjectPtr); @@ -155,28 +176,26 @@ namespace Pilz.Dalamud.Nameplates eventArgs.FreeCompanyPtr = GameInterfaceHelper.PluginAllocate(freeCompanyNewRaw); freeFreeCompany = true; } + + // Call Original as we changed something + callOriginal(); + + // Free memory + if (freeTitle) + GameInterfaceHelper.PluginFree(eventArgs.TitlePtr); + if (freeName) + GameInterfaceHelper.PluginFree(eventArgs.NamePtr); + if (freeFreeCompany) + GameInterfaceHelper.PluginFree(eventArgs.FreeCompanyPtr); + } + else if(!hasDefaultHookEvent) + { + // Call original in case of nothing get called, just to get secure it will not break the game when not calling it. + callOriginal(); } - // Call Original and set result - if (eventArgs.CallOriginal) - result = hook_AddonNamePlate_SetPlayerNameplateDetour.Original( - eventArgs.PlayerNameplateObjectPtr, - eventArgs.IsTitleAboveName, - eventArgs.IsTitleVisible, - eventArgs.TitlePtr, - eventArgs.NamePtr, - eventArgs.FreeCompanyPtr, - eventArgs.IconID); - else - result = eventArgs.Result; - - // Free memory - if (freeTitle) - GameInterfaceHelper.PluginFree(eventArgs.TitlePtr); - if (freeName) - GameInterfaceHelper.PluginFree(eventArgs.NamePtr); - if (freeFreeCompany) - GameInterfaceHelper.PluginFree(eventArgs.FreeCompanyPtr); + // Set result + result = eventArgs.Result; } return result; diff --git a/Pilz.Dalamud/Nameplates/NameplateManager.cs b/Pilz.Dalamud/Nameplates/NameplateManager.cs index 8861636..8fbd04a 100644 --- a/Pilz.Dalamud/Nameplates/NameplateManager.cs +++ b/Pilz.Dalamud/Nameplates/NameplateManager.cs @@ -1,6 +1,10 @@ using Dalamud.Hooking; using Pilz.Dalamud.Nameplates.EventArgs; using Dalamud.Utility.Signatures; +using FFXIVClientStructs.FFXIV.Client.UI; +using System.Runtime.InteropServices; +using Dalamud.Game.ClientState.Objects.Types; +using Pilz.Dalamud.Nameplates.Model; namespace Pilz.Dalamud.Nameplates { @@ -33,5 +37,47 @@ namespace Pilz.Dalamud.Nameplates { Hooks?.Dispose(); } + + public static T? GetNameplateGameObject(SafeNameplateObject namePlateObject) where T : GameObject + { + return GetNameplateGameObject(namePlateObject.Pointer); + } + + public static T? GetNameplateGameObject(IntPtr nameplateObjectPtr) where T : GameObject + { + // Get the nameplate object array + var nameplateAddonPtr = PluginServices.GameGui.GetAddonByName("NamePlate", 1); + var nameplateObjectArrayPtrPtr = nameplateAddonPtr + Marshal.OffsetOf(typeof(AddonNamePlate), nameof(AddonNamePlate.NamePlateObjectArray)).ToInt32(); + var nameplateObjectArrayPtr = Marshal.ReadIntPtr(nameplateObjectArrayPtrPtr); + if (nameplateObjectArrayPtr == IntPtr.Zero) + { + return null; + } + + // Determine the index of the nameplate object within the nameplate object array + var namePlateObjectSize = Marshal.SizeOf(typeof(AddonNamePlate.NamePlateObject)); + var namePlateObjectPtr0 = nameplateObjectArrayPtr + namePlateObjectSize * 0; + var namePlateIndex = (nameplateObjectPtr.ToInt64() - namePlateObjectPtr0.ToInt64()) / namePlateObjectSize; + if (namePlateIndex < 0 || namePlateIndex >= AddonNamePlate.NumNamePlateObjects) + { + return null; + } + + // Get the nameplate info array + IntPtr nameplateInfoArrayPtr = IntPtr.Zero; + unsafe + { + var framework = FFXIVClientStructs.FFXIV.Client.System.Framework.Framework.Instance(); + nameplateInfoArrayPtr = new IntPtr(&framework->GetUiModule()->GetRaptureAtkModule()->NamePlateInfoArray); + } + + // Get the nameplate info for the nameplate object + var namePlateInfoPtr = new IntPtr(nameplateInfoArrayPtr.ToInt64() + Marshal.SizeOf(typeof(RaptureAtkModule.NamePlateInfo)) * namePlateIndex); + RaptureAtkModule.NamePlateInfo namePlateInfo = Marshal.PtrToStructure(namePlateInfoPtr); + + // Return the object for its object id + var objectId = namePlateInfo.ObjectID.ObjectID; + return PluginServices.ObjectTable.SearchById(objectId) as T; + } } } diff --git a/Pilz.Dalamud/PluginServices.cs b/Pilz.Dalamud/PluginServices.cs index 3c90f40..8433392 100644 --- a/Pilz.Dalamud/PluginServices.cs +++ b/Pilz.Dalamud/PluginServices.cs @@ -1,5 +1,6 @@ using Dalamud.Data; using Dalamud.Game.ClientState; +using Dalamud.Game.ClientState.Objects; using Dalamud.Game.Gui; using Dalamud.IoC; using Dalamud.Plugin; @@ -11,16 +12,13 @@ using System.Threading.Tasks; namespace Pilz.Dalamud { - internal class PluginServices + public class PluginServices { - [PluginService] - public static GameGui GameGui { get; private set; } = null!; - [PluginService] - public static DalamudPluginInterface PluginInterface { get; private set; } = null!; - [PluginService] - public static ClientState ClientState { get; private set; } = null!; - [PluginService] - public static DataManager DataManager { get; private set; } = null!; + [PluginService] public static GameGui GameGui { get; private set; } = null!; + [PluginService] public static DalamudPluginInterface PluginInterface { get; private set; } = null!; + [PluginService] public static ClientState ClientState { get; private set; } = null!; + [PluginService] public static DataManager DataManager { get; private set; } = null!; + [PluginService] public static ObjectTable ObjectTable { get; private set; } = null!; public static void Initialize(DalamudPluginInterface dalamudPluginInterface) {