diff --git a/PlayerTags/PlayerTags.json b/PlayerTags/PlayerTags.json index 8817f1e..19b295c 100644 --- a/PlayerTags/PlayerTags.json +++ b/PlayerTags/PlayerTags.json @@ -6,7 +6,6 @@ "IconUrl": "https://github.com/r00telement/PlayerTags/raw/main/PlayerTags/Resources/Promo/Icon.png", "ImageUrls": [ "https://github.com/r00telement/PlayerTags/raw/main/PlayerTags/Resources/Promo/Nameplates_1.png", - "https://github.com/r00telement/PlayerTags/raw/main/PlayerTags/Resources/Promo/Chat_1.png", - "https://github.com/r00telement/PlayerTags/raw/main/PlayerTags/Resources/Promo/Chat_2.png" + "https://github.com/r00telement/PlayerTags/raw/main/PlayerTags/Resources/Promo/Chat_1.png" ] } \ No newline at end of file diff --git a/PlayerTags/Plugin.cs b/PlayerTags/Plugin.cs index b8bde6c..e08ed23 100644 --- a/PlayerTags/Plugin.cs +++ b/PlayerTags/Plugin.cs @@ -2,6 +2,7 @@ using Dalamud.Game; using Dalamud.Game.ClientState; using Dalamud.Game.ClientState.Objects; +using Dalamud.Game.ClientState.Objects.SubKinds; using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Game.Command; using Dalamud.Game.Gui; @@ -104,7 +105,7 @@ namespace PlayerTags { if (m_PluginHooks == null) { - m_PluginHooks = new PluginHooks(Framework, ObjectTable, GameGui, SetNameplate); + m_PluginHooks = new PluginHooks(Framework, ObjectTable, GameGui, SetPlayerNameplate); } } @@ -146,7 +147,11 @@ namespace PlayerTags private void UiBuilder_Draw() { - m_PluginConfigurationUI.Draw(); + // Don't bother showing the config unless the player is in the world + if (ClientState.LocalPlayer != null) + { + m_PluginConfigurationUI.Draw(); + } } private void UiBuilder_OpenConfigUi() @@ -173,9 +178,9 @@ namespace PlayerTags /// Whether the name was changed. /// Whether the title was changed. /// Whether the free company was changed. - private void SetNameplate(GameObject gameObject, SeString name, SeString title, SeString freeCompany, ref bool isTitleVisible, ref bool isTitleAboveName, ref int iconId, out bool isNameChanged, out bool isTitleChanged, out bool isFreeCompanyChanged) + private void SetPlayerNameplate(PlayerCharacter playerCharacter, SeString name, SeString title, SeString freeCompany, ref bool isTitleVisible, ref bool isTitleAboveName, ref int iconId, out bool isNameChanged, out bool isTitleChanged, out bool isFreeCompanyChanged) { - AddTagsToNameplate(gameObject, name, title, freeCompany, out isNameChanged, out isTitleChanged, out isFreeCompanyChanged); + AddTagsToNameplate(playerCharacter, name, title, freeCompany, out isNameChanged, out isTitleChanged, out isFreeCompanyChanged); if (m_PluginConfiguration.NameplateTitlePosition == NameplateTitlePosition.AlwaysAboveName) { @@ -260,15 +265,15 @@ namespace PlayerTags newPayloads.Add(new TextPayload(text)); - if (tag.IsTextItalic.InheritedValue != null && tag.IsTextItalic.InheritedValue.Value) - { - newPayloads.Add(new EmphasisItalicPayload(false)); - } - if (tag.TextColor.InheritedValue != null) { newPayloads.Add(new UIForegroundPayload(0)); } + + if (tag.IsTextItalic.InheritedValue != null && tag.IsTextItalic.InheritedValue.Value) + { + newPayloads.Add(new EmphasisItalicPayload(false)); + } } return newPayloads.ToArray(); diff --git a/PlayerTags/PluginHooks.cs b/PlayerTags/PluginHooks.cs index 01a9ece..1e29cf8 100644 --- a/PlayerTags/PluginHooks.cs +++ b/PlayerTags/PluginHooks.cs @@ -1,5 +1,6 @@ using Dalamud.Game; using Dalamud.Game.ClientState.Objects; +using Dalamud.Game.ClientState.Objects.SubKinds; using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Game.Gui; using Dalamud.Game.Text.SeStringHandling; @@ -14,70 +15,54 @@ namespace PlayerTags { public class PluginHooks : IDisposable { - [UnmanagedFunctionPointer(CallingConvention.ThisCall, CharSet = CharSet.Ansi)] - private delegate IntPtr SetNameplateDelegate_Private(IntPtr nameplateObjectPtr, bool isTitleAboveName, bool isTitleVisible, IntPtr titlePtr, IntPtr namePtr, IntPtr freeCompanyPtr, int iconId); - - [UnmanagedFunctionPointer(CallingConvention.ThisCall, CharSet = CharSet.Ansi)] - private delegate IntPtr UIModule_GetRaptureAtkModuleDelegate_Private(IntPtr uiModule); - - [UnmanagedFunctionPointer(CallingConvention.ThisCall, CharSet = CharSet.Ansi)] - private delegate IntPtr Framework_GetUIModuleDelegate_Private(IntPtr framework); - private class PluginAddressResolver : BaseAddressResolver { private const string SetNameplateSignature = "48 89 5C 24 ?? 48 89 6C 24 ?? 56 57 41 54 41 56 41 57 48 83 EC 40 44 0F B6 E2"; internal IntPtr SetNameplatePtr; - private const string Framework_GetUIModuleSignature = "E8 ?? ?? ?? ?? 48 8B C8 48 8B 10 FF 92 ?? ?? ?? ?? 48 8B C8 BA ?? ?? ?? ??"; - internal IntPtr Framework_GetUIModulePtr; - protected override void Setup64Bit(SigScanner scanner) { SetNameplatePtr = scanner.ScanText(SetNameplateSignature); - Framework_GetUIModulePtr = scanner.ScanText(Framework_GetUIModuleSignature); } } + private delegate IntPtr SetPlayerNameplateDelegate_Unmanaged(IntPtr playerNameplateObjectPtr, bool isTitleAboveName, bool isTitleVisible, IntPtr titlePtr, IntPtr namePtr, IntPtr freeCompanyPtr, int iconId); + private Framework m_Framework; private ObjectTable m_ObjectTable; private GameGui m_GameGui; - private SetNameplateDelegate m_SetNameplate; + private SetPlayerNameplateDelegate m_SetPlayerNameplate; private PluginAddressResolver m_PluginAddressResolver; - private Hook m_SetNameplateHook; - private readonly Framework_GetUIModuleDelegate_Private m_GetUIModule; - private IntPtr? m_NameplateObjectArrayPtr; - private IntPtr? m_NameplateInfoArrayPtr; + private Hook m_SetPlayerNameplateHook; - public PluginHooks(Framework framework, ObjectTable objectTable, GameGui gameGui, SetNameplateDelegate setNameplate) + public PluginHooks(Framework framework, ObjectTable objectTable, GameGui gameGui, SetPlayerNameplateDelegate setPlayerNameplate) { m_Framework = framework; m_ObjectTable = objectTable; m_GameGui = gameGui; - m_SetNameplate = setNameplate; + m_SetPlayerNameplate = setPlayerNameplate; m_PluginAddressResolver = new PluginAddressResolver(); m_PluginAddressResolver.Setup(); - m_GetUIModule = Marshal.GetDelegateForFunctionPointer(m_PluginAddressResolver.Framework_GetUIModulePtr); - - m_SetNameplateHook = new Hook(m_PluginAddressResolver.SetNameplatePtr, new SetNameplateDelegate_Private(SetNameplateDetour)); - m_SetNameplateHook.Enable(); + m_SetPlayerNameplateHook = new Hook(m_PluginAddressResolver.SetNameplatePtr, new SetPlayerNameplateDelegate_Unmanaged(SetPlayerNameplateDetour)); + m_SetPlayerNameplateHook.Enable(); } public void Dispose() { - m_SetNameplateHook.Disable(); + m_SetPlayerNameplateHook.Disable(); } - private IntPtr SetNameplateDetour(IntPtr nameplateObjectPtrOriginal, bool isTitleAboveNameOriginal, bool isTitleVisibleOriginal, IntPtr titlePtrOriginal, IntPtr namePtrOriginal, IntPtr freeCompanyPtrOriginal, int iconIdOriginal) + private IntPtr SetPlayerNameplateDetour(IntPtr playerNameplateObjectPtrOriginal, bool isTitleAboveNameOriginal, bool isTitleVisibleOriginal, IntPtr titlePtrOriginal, IntPtr namePtrOriginal, IntPtr freeCompanyPtrOriginal, int iconIdOriginal) { - if (m_SetNameplate != null) + if (m_SetPlayerNameplate != null) { try { - GameObject? gameObject = GetNameplateObject(nameplateObjectPtrOriginal); - if (gameObject != null) + PlayerCharacter? playerCharacter = GetNameplateGameObject(playerNameplateObjectPtrOriginal); + if (playerCharacter != null) { SeString title = ReadSeString(titlePtrOriginal); SeString name = ReadSeString(namePtrOriginal); @@ -88,7 +73,7 @@ namespace PlayerTags bool isTitleChanged; bool isNameChanged; bool isFreeCompanyChanged; - m_SetNameplate(gameObject, name, title, freeCompany, ref isTitleVisible, ref isTitleAboveName, ref iconId, out isNameChanged, out isTitleChanged, out isFreeCompanyChanged); + m_SetPlayerNameplate(playerCharacter, name, title, freeCompany, ref isTitleVisible, ref isTitleAboveName, ref iconId, out isNameChanged, out isTitleChanged, out isFreeCompanyChanged); IntPtr namePtr = namePtrOriginal; if (isNameChanged) @@ -108,7 +93,7 @@ namespace PlayerTags freeCompanyPtr = Allocate(freeCompany); } - var result = m_SetNameplateHook.Original(nameplateObjectPtrOriginal, isTitleAboveName, isTitleVisible, titlePtr, namePtr, freeCompanyPtr, iconId); + var result = m_SetPlayerNameplateHook.Original(playerNameplateObjectPtrOriginal, isTitleAboveName, isTitleVisible, titlePtr, namePtr, freeCompanyPtr, iconId); if (isNameChanged) { @@ -134,7 +119,7 @@ namespace PlayerTags } } - return m_SetNameplateHook.Original(nameplateObjectPtrOriginal, isTitleAboveNameOriginal, isTitleVisibleOriginal, titlePtrOriginal, namePtrOriginal, freeCompanyPtrOriginal, iconIdOriginal); + return m_SetPlayerNameplateHook.Original(playerNameplateObjectPtrOriginal, isTitleAboveNameOriginal, isTitleVisibleOriginal, titlePtrOriginal, namePtrOriginal, freeCompanyPtrOriginal, iconIdOriginal); } private static SeString ReadSeString(IntPtr stringPtr) @@ -175,64 +160,43 @@ namespace PlayerTags ptr = IntPtr.Zero; } - private T? GetNameplateObject(IntPtr nameplateObjectPtr) + private T? GetNameplateGameObject(IntPtr nameplateObjectPtr) where T : GameObject { - if (!m_NameplateInfoArrayPtr.HasValue) + // Get the nameplate object array + var nameplateAddonPtr = m_GameGui.GetAddonByName("NamePlate", 1); + var nameplateObjectArrayPtrPtr = nameplateAddonPtr + Marshal.OffsetOf(typeof(AddonNamePlate), nameof(AddonNamePlate.NamePlateObjectArray)).ToInt32(); + var nameplateObjectArrayPtr = Marshal.ReadIntPtr(nameplateObjectArrayPtrPtr); + if (nameplateObjectArrayPtr == IntPtr.Zero) { - // Get the nameplate object array - var namePlateAddonPtr = m_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!; - } - - m_NameplateObjectArrayPtr = nameplateObjectArrayPtr; - - // Get the nameplate info - IntPtr raptureAtkModulePtr; - var frameworkPtr = m_Framework.Address.BaseAddress; - var uiModulePtr = m_GetUIModule(frameworkPtr); - unsafe - { - var uiModule = *(UIModule*)uiModulePtr; - var UIModule_GetRaptureAtkModuleAddress = new IntPtr(uiModule.vfunc[7]); - var GetRaptureAtkModule = Marshal.GetDelegateForFunctionPointer(UIModule_GetRaptureAtkModuleAddress); - raptureAtkModulePtr = GetRaptureAtkModule(uiModulePtr); - } - - if (raptureAtkModulePtr == IntPtr.Zero) - { - return null!; - } - - m_NameplateInfoArrayPtr = raptureAtkModulePtr + Marshal.OffsetOf(typeof(RaptureAtkModule), nameof(RaptureAtkModule.NamePlateInfoArray)).ToInt32(); + return null; } - // Determine the index of this nameplate + // Determine the index of the nameplate object within the nameplate object array var namePlateObjectSize = Marshal.SizeOf(typeof(AddonNamePlate.NamePlateObject)); - var namePlateObjectPtr0 = m_NameplateObjectArrayPtr!.Value + namePlateObjectSize * 0; + var namePlateObjectPtr0 = nameplateObjectArrayPtr + namePlateObjectSize * 0; var namePlateIndex = (nameplateObjectPtr.ToInt64() - namePlateObjectPtr0.ToInt64()) / namePlateObjectSize; if (namePlateIndex < 0 || namePlateIndex >= 50) { - return null!; + return null; } - var namePlateInfoPtr = new IntPtr(m_NameplateInfoArrayPtr.Value.ToInt64() + Marshal.SizeOf(typeof(RaptureAtkModule.NamePlateInfo)) * namePlateIndex); + // Get the nameplate info array + IntPtr nameplateInfoArrayPtr = IntPtr.Zero; + unsafe + { + var framework = FFXIVClientStructs.FFXIV.Client.System.Framework.Framework.Instance(); + var ui3DModule = framework->GetUiModule()->GetUI3DModule(); + 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); - // Get the player character for this nameplate info + // Return the object for its object id var objectId = namePlateInfo.ObjectID.ObjectID; - - T? gameObject = m_ObjectTable.FirstOrDefault(obj => obj.ObjectId == objectId) as T; - if (gameObject == null) - { - return null!; - } - - return gameObject!; + return m_ObjectTable.SearchById(objectId) as T; } } } diff --git a/PlayerTags/SetNameplateDelegate.cs b/PlayerTags/SetNameplateDelegate.cs deleted file mode 100644 index 778615e..0000000 --- a/PlayerTags/SetNameplateDelegate.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Dalamud.Game.ClientState.Objects.Types; -using Dalamud.Game.Text.SeStringHandling; - -namespace PlayerTags -{ - public delegate void SetNameplateDelegate(GameObject gameObject, SeString name, SeString title, SeString freeCompany, ref bool isTitleVisible, ref bool isTitleAboveName, ref int iconId, out bool isNameChanged, out bool isTitleChanged, out bool isFreeCompanyChanged); -} diff --git a/PlayerTags/SetPlayerNameplateDelegate.cs b/PlayerTags/SetPlayerNameplateDelegate.cs new file mode 100644 index 0000000..5e38761 --- /dev/null +++ b/PlayerTags/SetPlayerNameplateDelegate.cs @@ -0,0 +1,7 @@ +using Dalamud.Game.ClientState.Objects.SubKinds; +using Dalamud.Game.Text.SeStringHandling; + +namespace PlayerTags +{ + public delegate void SetPlayerNameplateDelegate(PlayerCharacter playerCharacter, SeString name, SeString title, SeString freeCompany, ref bool isTitleVisible, ref bool isTitleAboveName, ref int iconId, out bool isNameChanged, out bool isTitleChanged, out bool isFreeCompanyChanged); +}