From 74ddb9e7c69278d680ded1be5993e463296e3bed Mon Sep 17 00:00:00 2001 From: Pilzinsel64 Date: Sat, 5 Nov 2022 15:17:26 +0100 Subject: [PATCH 01/36] integrate new ActivityContext --- PlayerTags.sln | 10 +++- .../Configuration/PluginConfiguration.cs | 22 ++++++-- .../Configuration/PluginConfigurationUI.cs | 13 ++--- PlayerTags/Data/ActivityContext.cs | 1 + PlayerTags/Data/ActivityContextHelper.cs | 18 +++---- PlayerTags/Data/ActivityContextManager.cs | 50 ------------------- PlayerTags/Features/ChatTagTargetFeature.cs | 2 +- .../Features/NameplateTagTargetFeature.cs | 2 +- PlayerTags/Features/TagTargetFeature.cs | 3 +- PlayerTags/PlayerTags.csproj | 1 + PlayerTags/packages.lock.json | 3 ++ 11 files changed, 49 insertions(+), 76 deletions(-) delete mode 100644 PlayerTags/Data/ActivityContextManager.cs diff --git a/PlayerTags.sln b/PlayerTags.sln index c058c5e..0219690 100644 --- a/PlayerTags.sln +++ b/PlayerTags.sln @@ -1,10 +1,12 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29709.97 +# Visual Studio Version 17 +VisualStudioVersion = 17.3.32929.385 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PlayerTags", "PlayerTags\PlayerTags.csproj", "{13C812E9-0D42-4B95-8646-40EEBF30636F}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Pilz.Dalamud", "..\Pilz.Dalamud\Pilz.Dalamud\Pilz.Dalamud.csproj", "{A92D2FFC-FDB8-4F28-B5DD-4A1B3EB0B1BB}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -15,6 +17,10 @@ Global {13C812E9-0D42-4B95-8646-40EEBF30636F}.Debug|x64.Build.0 = Debug|x64 {13C812E9-0D42-4B95-8646-40EEBF30636F}.Release|x64.ActiveCfg = Release|x64 {13C812E9-0D42-4B95-8646-40EEBF30636F}.Release|x64.Build.0 = Release|x64 + {A92D2FFC-FDB8-4F28-B5DD-4A1B3EB0B1BB}.Debug|x64.ActiveCfg = Debug|x64 + {A92D2FFC-FDB8-4F28-B5DD-4A1B3EB0B1BB}.Debug|x64.Build.0 = Debug|x64 + {A92D2FFC-FDB8-4F28-B5DD-4A1B3EB0B1BB}.Release|x64.ActiveCfg = Release|x64 + {A92D2FFC-FDB8-4F28-B5DD-4A1B3EB0B1BB}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/PlayerTags/Configuration/PluginConfiguration.cs b/PlayerTags/Configuration/PluginConfiguration.cs index 245930d..7c66f13 100644 --- a/PlayerTags/Configuration/PluginConfiguration.cs +++ b/PlayerTags/Configuration/PluginConfiguration.cs @@ -1,5 +1,6 @@ using Dalamud.Configuration; using Newtonsoft.Json; +using Pilz.Dalamud.ActivityContexts; using PlayerTags.Data; using PlayerTags.Inheritables; using System; @@ -19,11 +20,24 @@ namespace PlayerTags.Configuration private const NameplateTitlePosition DefaultNameplateTitlePosition = Data.NameplateTitlePosition.AlwaysAboveName; private const bool DefaultIsApplyTagsToAllChatMessagesEnabled = true; - public Dictionary GeneralOptions = new Dictionary() + [Obsolete] + [JsonProperty("GeneralOptions")] + private Dictionary GeneralOptionsV1 { - { ActivityContext.None, new GeneralOptionsClass() }, - { ActivityContext.PveDuty, new GeneralOptionsClass() }, - { ActivityContext.PvpDuty, new GeneralOptionsClass() } + set + { + GeneralOptions.Clear(); + foreach (var kvp in value) + GeneralOptions.Add((ActivityType)kvp.Key, kvp.Value); + } + } + + [JsonProperty("GeneralOptionsV2")] + public Dictionary GeneralOptions = new() + { + { ActivityType.None, new GeneralOptionsClass() }, + { ActivityType.PveDuty, new GeneralOptionsClass() }, + { ActivityType.PvpDuty, new GeneralOptionsClass() } }; public bool IsPlayerNameRandomlyGenerated = false; diff --git a/PlayerTags/Configuration/PluginConfigurationUI.cs b/PlayerTags/Configuration/PluginConfigurationUI.cs index 078c69a..9f2f923 100644 --- a/PlayerTags/Configuration/PluginConfigurationUI.cs +++ b/PlayerTags/Configuration/PluginConfigurationUI.cs @@ -5,6 +5,7 @@ using Dalamud.Interface; using Dalamud.Logging; using ImGuiNET; using Lumina.Excel.GeneratedSheets; +using Pilz.Dalamud.ActivityContexts; using PlayerTags.Data; using PlayerTags.Inheritables; using PlayerTags.PluginStrings; @@ -1233,7 +1234,7 @@ namespace PlayerTags.Configuration applyChanges(GetActivityContext(CurrentActivityContext)); } - void applyChanges(ActivityContext key) + void applyChanges(ActivityType key) { pluginConfig.GeneralOptions[key].NameplateFreeCompanyVisibility = NameplateFreeCompanyVisibility; pluginConfig.GeneralOptions[key].NameplateTitleVisibility = NameplateTitleVisibility; @@ -1242,22 +1243,22 @@ namespace PlayerTags.Configuration } } - private ActivityContext GetActivityContext(ActivityContextSelection selection) + private ActivityType GetActivityContext(ActivityContextSelection selection) { - ActivityContext result; + ActivityType result; switch (selection) { case ActivityContextSelection.PveDuty: - result = ActivityContext.PveDuty; + result = ActivityType.PveDuty; break; case ActivityContextSelection.PvpDuty: - result = ActivityContext.PvpDuty; + result = ActivityType.PvpDuty; break; case ActivityContextSelection.All: case ActivityContextSelection.None: default: - result = ActivityContext.None; + result = ActivityType.None; break; } diff --git a/PlayerTags/Data/ActivityContext.cs b/PlayerTags/Data/ActivityContext.cs index 6b5ef7c..ec78386 100644 --- a/PlayerTags/Data/ActivityContext.cs +++ b/PlayerTags/Data/ActivityContext.cs @@ -3,6 +3,7 @@ using System; namespace PlayerTags.Data { + [Obsolete] [Flags] [JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] public enum ActivityContext diff --git a/PlayerTags/Data/ActivityContextHelper.cs b/PlayerTags/Data/ActivityContextHelper.cs index c843b33..cf82c52 100644 --- a/PlayerTags/Data/ActivityContextHelper.cs +++ b/PlayerTags/Data/ActivityContextHelper.cs @@ -1,25 +1,21 @@ -namespace PlayerTags.Data +using Pilz.Dalamud.ActivityContexts; + +namespace PlayerTags.Data { public static class ActivityContextHelper { - public static bool GetIsVisible(ActivityContext playerContext, bool desiredPveDutyVisibility, bool desiredPvpDutyVisibility, bool desiredOthersVisibility) + public static bool GetIsVisible(ActivityType playerContext, bool desiredPveDutyVisibility, bool desiredPvpDutyVisibility, bool desiredOthersVisibility) { bool isVisible = false; - if (playerContext.HasFlag(ActivityContext.PveDuty)) - { + if (playerContext.HasFlag(ActivityType.PveDuty)) isVisible |= desiredPveDutyVisibility; - } - if (playerContext.HasFlag(ActivityContext.PvpDuty)) - { + if (playerContext.HasFlag(ActivityType.PvpDuty)) isVisible |= desiredPvpDutyVisibility; - } - if (playerContext == ActivityContext.None) - { + if (playerContext == ActivityType.None) isVisible |= desiredOthersVisibility; - } return isVisible; } diff --git a/PlayerTags/Data/ActivityContextManager.cs b/PlayerTags/Data/ActivityContextManager.cs deleted file mode 100644 index 2225db0..0000000 --- a/PlayerTags/Data/ActivityContextManager.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Dalamud.Plugin; -using Lumina.Excel.GeneratedSheets; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace PlayerTags.Data -{ - public class ActivityContextManager : IDisposable - { - private ActivityContext m_CurrentActivityContext; - - public ActivityContext CurrentActivityContext => m_CurrentActivityContext; - - public ActivityContextManager() - { - m_CurrentActivityContext = ActivityContext.None; - PluginServices.ClientState.TerritoryChanged += ClientState_TerritoryChanged; - } - - private void ClientState_TerritoryChanged(object? sender, ushort e) - { - m_CurrentActivityContext = ActivityContext.None; - - var contentFinderConditionsSheet = PluginServices.DataManager.GameData.GetExcelSheet(); - if (contentFinderConditionsSheet != null) - { - var foundContentFinderCondition = contentFinderConditionsSheet.FirstOrDefault(contentFinderCondition => contentFinderCondition.TerritoryType.Row == PluginServices.ClientState.TerritoryType); - if (foundContentFinderCondition != null) - { - if (foundContentFinderCondition.PvP) - { - m_CurrentActivityContext = ActivityContext.PvpDuty; - } - else - { - m_CurrentActivityContext = ActivityContext.PveDuty; - } - } - } - } - - public void Dispose() - { - PluginServices.ClientState.TerritoryChanged -= ClientState_TerritoryChanged; - } - } -} diff --git a/PlayerTags/Features/ChatTagTargetFeature.cs b/PlayerTags/Features/ChatTagTargetFeature.cs index a3518b3..22d1560 100644 --- a/PlayerTags/Features/ChatTagTargetFeature.cs +++ b/PlayerTags/Features/ChatTagTargetFeature.cs @@ -120,7 +120,7 @@ namespace PlayerTags.Features private void Chat_ChatMessage(XivChatType type, uint senderId, ref SeString sender, ref SeString message, ref bool isHandled) { - if (m_PluginConfiguration.GeneralOptions[ActivityContextManager.CurrentActivityContext].IsApplyTagsToAllChatMessagesEnabled || Enum.IsDefined(type)) + if (m_PluginConfiguration.GeneralOptions[ActivityContextManager.CurrentActivityContext.ActivityType].IsApplyTagsToAllChatMessagesEnabled || Enum.IsDefined(type)) { AddTagsToChat(sender, type, true); AddTagsToChat(message, type, false); diff --git a/PlayerTags/Features/NameplateTagTargetFeature.cs b/PlayerTags/Features/NameplateTagTargetFeature.cs index 1bd6379..9e30923 100644 --- a/PlayerTags/Features/NameplateTagTargetFeature.cs +++ b/PlayerTags/Features/NameplateTagTargetFeature.cs @@ -101,7 +101,7 @@ namespace PlayerTags.Features { var beforeTitleBytes = args.Title.Encode(); AddTagsToNameplate(args.PlayerCharacter, args.Name, args.Title, args.FreeCompany); - var generalOptions = m_PluginConfiguration.GeneralOptions[ActivityContextManager.CurrentActivityContext]; + var generalOptions = m_PluginConfiguration.GeneralOptions[ActivityContextManager.CurrentActivityContext.ActivityType]; if (generalOptions.NameplateTitlePosition == NameplateTitlePosition.AlwaysAboveName) args.IsTitleAboveName = true; diff --git a/PlayerTags/Features/TagTargetFeature.cs b/PlayerTags/Features/TagTargetFeature.cs index eb2be97..ae5d457 100644 --- a/PlayerTags/Features/TagTargetFeature.cs +++ b/PlayerTags/Features/TagTargetFeature.cs @@ -4,6 +4,7 @@ using Dalamud.Game.Text.SeStringHandling; using Dalamud.Game.Text.SeStringHandling.Payloads; using FFXIVClientStructs.FFXIV.Client.Game.Object; using Lumina.Excel.GeneratedSheets; +using Pilz.Dalamud.ActivityContexts; using PlayerTags.Configuration.GameConfig; using PlayerTags.Data; using PlayerTags.Inheritables; @@ -44,7 +45,7 @@ namespace PlayerTags.Features protected bool IsTagVisible(Tag tag, GameObject? gameObject) { - bool isVisibleForActivity = ActivityContextHelper.GetIsVisible(ActivityContextManager.CurrentActivityContext, + bool isVisibleForActivity = ActivityContextHelper.GetIsVisible(ActivityContextManager.CurrentActivityContext.ActivityType, tag.IsVisibleInPveDuties.InheritedValue ?? false, tag.IsVisibleInPvpDuties.InheritedValue ?? false, tag.IsVisibleInOverworld.InheritedValue ?? false); diff --git a/PlayerTags/PlayerTags.csproj b/PlayerTags/PlayerTags.csproj index f1a8fd5..b16c921 100644 --- a/PlayerTags/PlayerTags.csproj +++ b/PlayerTags/PlayerTags.csproj @@ -26,6 +26,7 @@ + $(DalamudLibPath)FFXIVClientStructs.dll false diff --git a/PlayerTags/packages.lock.json b/PlayerTags/packages.lock.json index abf5900..d25458d 100644 --- a/PlayerTags/packages.lock.json +++ b/PlayerTags/packages.lock.json @@ -13,6 +13,9 @@ "requested": "[2.1.8, )", "resolved": "2.1.8", "contentHash": "YqagNXs9InxmqkXzq7kLveImxnodkBEicAhydMXVp7dFjC7xb76U6zGgAax4/BWIWfZeWzr5DJyQSev31kj81A==" + }, + "pilz.dalamud": { + "type": "Project" } } } From 1b672513ae95924eece10c5614d27734b6efc2a8 Mon Sep 17 00:00:00 2001 From: Pilzinsel64 Date: Sat, 5 Nov 2022 15:19:40 +0100 Subject: [PATCH 02/36] fix merge conflicts --- PlayerTags/Configuration/PluginConfiguration.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PlayerTags/Configuration/PluginConfiguration.cs b/PlayerTags/Configuration/PluginConfiguration.cs index 6c1b5c6..7c66f13 100644 --- a/PlayerTags/Configuration/PluginConfiguration.cs +++ b/PlayerTags/Configuration/PluginConfiguration.cs @@ -33,7 +33,7 @@ namespace PlayerTags.Configuration } [JsonProperty("GeneralOptionsV2")] - public Dictionary GeneralOptions = new Dictionary() + public Dictionary GeneralOptions = new() { { ActivityType.None, new GeneralOptionsClass() }, { ActivityType.PveDuty, new GeneralOptionsClass() }, From d38d974aa62dd31a92c7e6a9ea3be095daa24b08 Mon Sep 17 00:00:00 2001 From: Pilzinsel64 Date: Sat, 5 Nov 2022 15:27:10 +0100 Subject: [PATCH 03/36] initialize Pilz.Dalamud lib --- PlayerTags/Plugin.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/PlayerTags/Plugin.cs b/PlayerTags/Plugin.cs index 293885d..97efc53 100644 --- a/PlayerTags/Plugin.cs +++ b/PlayerTags/Plugin.cs @@ -28,6 +28,7 @@ namespace PlayerTags public Plugin(DalamudPluginInterface pluginInterface) { PluginServices.Initialize(pluginInterface); + Pilz.Dalamud.PluginServices.Initialize(pluginInterface); m_PluginConfiguration = PluginServices.DalamudPluginInterface.GetPluginConfig() as PluginConfiguration ?? new PluginConfiguration(); m_PluginData = new PluginData(m_PluginConfiguration); From 6644b1edb823069fa4e0e3b2e66c69c191c298ac Mon Sep 17 00:00:00 2001 From: Pilzinsel64 Date: Sat, 5 Nov 2022 20:27:23 +0100 Subject: [PATCH 04/36] some work --- .../Configuration/PluginConfiguration.cs | 5 - .../GameInterface/Nameplates/Nameplate.cs | 127 ++---------------- .../Nameplates/PlayerNameplateUpdatedArgs.cs | 45 +++++-- 3 files changed, 44 insertions(+), 133 deletions(-) diff --git a/PlayerTags/Configuration/PluginConfiguration.cs b/PlayerTags/Configuration/PluginConfiguration.cs index 7c66f13..3c5761b 100644 --- a/PlayerTags/Configuration/PluginConfiguration.cs +++ b/PlayerTags/Configuration/PluginConfiguration.cs @@ -15,11 +15,6 @@ namespace PlayerTags.Configuration public int Version { get; set; } = 0; public bool IsVisible = false; - private const NameplateFreeCompanyVisibility DefaultNameplateFreeCompanyVisibility = Data.NameplateFreeCompanyVisibility.Default; - private const NameplateTitleVisibility DefaultNameplateTitleVisibility = Data.NameplateTitleVisibility.WhenHasTags; - private const NameplateTitlePosition DefaultNameplateTitlePosition = Data.NameplateTitlePosition.AlwaysAboveName; - private const bool DefaultIsApplyTagsToAllChatMessagesEnabled = true; - [Obsolete] [JsonProperty("GeneralOptions")] private Dictionary GeneralOptionsV1 diff --git a/PlayerTags/GameInterface/Nameplates/Nameplate.cs b/PlayerTags/GameInterface/Nameplates/Nameplate.cs index f2bbb38..62c5350 100644 --- a/PlayerTags/GameInterface/Nameplates/Nameplate.cs +++ b/PlayerTags/GameInterface/Nameplates/Nameplate.cs @@ -4,7 +4,9 @@ using Dalamud.Hooking; using Dalamud.Logging; using Dalamud.Utility.Signatures; using FFXIVClientStructs.FFXIV.Client.UI; +using Pilz.Dalamud.Nameplates; using System; +using System.Diagnostics.Tracing; using System.Linq; using System.Runtime.InteropServices; @@ -15,9 +17,7 @@ namespace PlayerTags.GameInterface.Nameplates /// public class Nameplate : IDisposable { - [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; - private unsafe delegate IntPtr AddonNamePlate_SetPlayerNameplateDetour(IntPtr playerNameplateObjectPtr, bool isTitleAboveName, bool isTitleVisible, IntPtr titlePtr, IntPtr namePtr, IntPtr freeCompanyPtr, int iconId); + public NameplateManager NameplateManager { get; init; } /// /// Occurs when a player nameplate is updated by the game. @@ -29,140 +29,37 @@ namespace PlayerTags.GameInterface.Nameplates /// public bool IsValid { - get - { - return hook_AddonNamePlate_SetPlayerNameplateDetour != null - && hook_AddonNamePlate_SetPlayerNameplateDetour.IsEnabled; - } + get => NameplateManager != null && NameplateManager.IsValid; } public Nameplate() { - SignatureHelper.Initialise(this); - hook_AddonNamePlate_SetPlayerNameplateDetour?.Enable(); + NameplateManager = new(); + NameplateManager.Hooks.AddonNamePlate_SetPlayerNameManaged += Hooks_AddonNamePlate_SetPlayerNameManaged; } public void Dispose() { - hook_AddonNamePlate_SetPlayerNameplateDetour?.Disable(); + NameplateManager.Hooks.AddonNamePlate_SetPlayerNameManaged -= Hooks_AddonNamePlate_SetPlayerNameManaged; + NameplateManager.Dispose(); } - private IntPtr SetPlayerNameplateDetour(IntPtr playerNameplateObjectPtr, bool isTitleAboveName, bool isTitleVisible, IntPtr titlePtr, IntPtr namePtr, IntPtr freeCompanyPtr, int iconId) + private void Hooks_AddonNamePlate_SetPlayerNameManaged(Pilz.Dalamud.Nameplates.EventArgs.AddonNamePlate_SetPlayerNameManagedEventArgs eventArgs) { - if (hook_AddonNamePlate_SetPlayerNameplateDetour == null) - { - return IntPtr.Zero; - } - try { - PlayerCharacter? playerCharacter = GetNameplateGameObject(playerNameplateObjectPtr); + PlayerCharacter? playerCharacter = NameplateManager.GetNameplateGameObject(eventArgs.SafeNameplateObject); + if (playerCharacter != null) { - PlayerNameplateUpdatedArgs playerNameplateUpdatedArgs = new PlayerNameplateUpdatedArgs( - playerCharacter, - GameInterfaceHelper.ReadSeString(namePtr), - GameInterfaceHelper.ReadSeString(titlePtr), - GameInterfaceHelper.ReadSeString(freeCompanyPtr), - isTitleVisible, - isTitleAboveName, - iconId); - - byte[] beforeNameBytes = playerNameplateUpdatedArgs.Name.Encode(); - byte[] beforeTitleBytes = playerNameplateUpdatedArgs.Title.Encode(); - byte[] beforeFreeCompanyBytes = playerNameplateUpdatedArgs.FreeCompany.Encode(); - + var playerNameplateUpdatedArgs = new PlayerNameplateUpdatedArgs(playerCharacter, eventArgs); PlayerNameplateUpdated?.Invoke(playerNameplateUpdatedArgs); - - byte[] afterNameBytes = playerNameplateUpdatedArgs.Name.Encode(); - byte[] afterTitleBytes = playerNameplateUpdatedArgs.Title.Encode(); - byte[] afterFreeCompanyBytes = playerNameplateUpdatedArgs.FreeCompany.Encode(); - - IntPtr newNamePtr = namePtr; - bool hasNameChanged = !beforeNameBytes.SequenceEqual(afterNameBytes); - if (hasNameChanged) - { - newNamePtr = GameInterfaceHelper.PluginAllocate(afterNameBytes); - } - - IntPtr newTitlePtr = titlePtr; - bool hasTitleChanged = !beforeTitleBytes.SequenceEqual(afterTitleBytes); - if (hasTitleChanged) - { - newTitlePtr = GameInterfaceHelper.PluginAllocate(afterTitleBytes); - } - - IntPtr newFreeCompanyPtr = freeCompanyPtr; - bool hasFreeCompanyChanged = !beforeFreeCompanyBytes.SequenceEqual(afterFreeCompanyBytes); - if (hasFreeCompanyChanged) - { - newFreeCompanyPtr = GameInterfaceHelper.PluginAllocate(afterFreeCompanyBytes); - } - - var result = hook_AddonNamePlate_SetPlayerNameplateDetour.Original(playerNameplateObjectPtr, playerNameplateUpdatedArgs.IsTitleAboveName, playerNameplateUpdatedArgs.IsTitleVisible, newTitlePtr, newNamePtr, newFreeCompanyPtr, playerNameplateUpdatedArgs.IconId); - - if (hasNameChanged) - { - GameInterfaceHelper.PluginFree(ref newNamePtr); - } - - if (hasTitleChanged) - { - GameInterfaceHelper.PluginFree(ref newTitlePtr); - } - - if (hasFreeCompanyChanged) - { - GameInterfaceHelper.PluginFree(ref newFreeCompanyPtr); - } - - return result; } } catch (Exception ex) { PluginLog.Error(ex, $"SetPlayerNameplateDetour"); } - - return hook_AddonNamePlate_SetPlayerNameplateDetour.Original(playerNameplateObjectPtr, isTitleAboveName, isTitleVisible, titlePtr, namePtr, freeCompanyPtr, iconId); - } - - private 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/PlayerTags/GameInterface/Nameplates/PlayerNameplateUpdatedArgs.cs b/PlayerTags/GameInterface/Nameplates/PlayerNameplateUpdatedArgs.cs index 517410a..8621f90 100644 --- a/PlayerTags/GameInterface/Nameplates/PlayerNameplateUpdatedArgs.cs +++ b/PlayerTags/GameInterface/Nameplates/PlayerNameplateUpdatedArgs.cs @@ -1,33 +1,52 @@ using Dalamud.Game.ClientState.Objects.SubKinds; using Dalamud.Game.Text.SeStringHandling; +using Pilz.Dalamud.Nameplates.EventArgs; namespace PlayerTags.GameInterface.Nameplates { public class PlayerNameplateUpdatedArgs { + private readonly AddonNamePlate_SetPlayerNameManagedEventArgs eventArgs; + public PlayerCharacter PlayerCharacter { get; } - public SeString Name { get; } + public SeString Name + { + get => eventArgs.Name; + } - public SeString Title { get; } + public SeString Title + { + get => eventArgs.Title; + } - public SeString FreeCompany { get; } + public SeString FreeCompany + { + get => eventArgs.FreeCompany; + } - public bool IsTitleVisible { get; set; } + public bool IsTitleVisible + { + get => eventArgs.IsTitleVisible; + set => eventArgs.IsTitleVisible = value; + } - public bool IsTitleAboveName { get; set; } + public bool IsTitleAboveName + { + get => eventArgs.IsTitleAboveName; + set => eventArgs.IsTitleAboveName = value; + } - public int IconId { get; set; } + public int IconId + { + get => eventArgs.IconID; + set => eventArgs.IconID = value; + } - public PlayerNameplateUpdatedArgs(PlayerCharacter playerCharacter, SeString name, SeString title, SeString freeCompany, bool isTitleVisible, bool isTitleAboveName, int iconId) + public PlayerNameplateUpdatedArgs(PlayerCharacter playerCharacter, AddonNamePlate_SetPlayerNameManagedEventArgs eventArgs) { PlayerCharacter = playerCharacter; - Name = name; - Title = title; - FreeCompany = freeCompany; - IsTitleVisible = isTitleVisible; - IsTitleAboveName = isTitleAboveName; - IconId = iconId; + this.eventArgs = eventArgs; } } } From 4025887ac136dbd826efad61fdcbcf538c348548 Mon Sep 17 00:00:00 2001 From: Pilzinsel64 Date: Sat, 5 Nov 2022 21:15:14 +0100 Subject: [PATCH 05/36] some more teststing code --- PlayerTags/Plugin.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/PlayerTags/Plugin.cs b/PlayerTags/Plugin.cs index 97efc53..f71fca2 100644 --- a/PlayerTags/Plugin.cs +++ b/PlayerTags/Plugin.cs @@ -28,7 +28,12 @@ namespace PlayerTags public Plugin(DalamudPluginInterface pluginInterface) { PluginServices.Initialize(pluginInterface); - Pilz.Dalamud.PluginServices.Initialize(pluginInterface); + Pilz.Dalamud.PluginServices.PluginInterface = pluginInterface; + Pilz.Dalamud.PluginServices.ClientState = PluginServices.ClientState; + Pilz.Dalamud.PluginServices.DataManager = PluginServices.DataManager; + Pilz.Dalamud.PluginServices.GameGui = PluginServices.GameGui; + Pilz.Dalamud.PluginServices.ObjectTable = PluginServices.ObjectTable; + //Pilz.Dalamud.PluginServices.Initialize(pluginInterface); m_PluginConfiguration = PluginServices.DalamudPluginInterface.GetPluginConfig() as PluginConfiguration ?? new PluginConfiguration(); m_PluginData = new PluginData(m_PluginConfiguration); From 1629b973620e658c16e1e91238d0138cc66a01d1 Mon Sep 17 00:00:00 2001 From: Pilzinsel64 Date: Sat, 5 Nov 2022 21:44:59 +0100 Subject: [PATCH 06/36] use pluginservices initialise again --- PlayerTags/Plugin.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/PlayerTags/Plugin.cs b/PlayerTags/Plugin.cs index f71fca2..97efc53 100644 --- a/PlayerTags/Plugin.cs +++ b/PlayerTags/Plugin.cs @@ -28,12 +28,7 @@ namespace PlayerTags public Plugin(DalamudPluginInterface pluginInterface) { PluginServices.Initialize(pluginInterface); - Pilz.Dalamud.PluginServices.PluginInterface = pluginInterface; - Pilz.Dalamud.PluginServices.ClientState = PluginServices.ClientState; - Pilz.Dalamud.PluginServices.DataManager = PluginServices.DataManager; - Pilz.Dalamud.PluginServices.GameGui = PluginServices.GameGui; - Pilz.Dalamud.PluginServices.ObjectTable = PluginServices.ObjectTable; - //Pilz.Dalamud.PluginServices.Initialize(pluginInterface); + Pilz.Dalamud.PluginServices.Initialize(pluginInterface); m_PluginConfiguration = PluginServices.DalamudPluginInterface.GetPluginConfig() as PluginConfiguration ?? new PluginConfiguration(); m_PluginData = new PluginData(m_PluginConfiguration); From c4baba304e4d7585cc6950d00d1742647db6fb20 Mon Sep 17 00:00:00 2001 From: Pilzinsel64 Date: Sat, 5 Nov 2022 22:52:44 +0100 Subject: [PATCH 07/36] integrate new StringUpdateFactory and NameplateUpdateFactory --- PlayerTags/Features/ChatTagTargetFeature.cs | 7 +- .../Features/NameplateTagTargetFeature.cs | 45 +++-- PlayerTags/Features/TagTargetFeature.cs | 154 ++---------------- 3 files changed, 35 insertions(+), 171 deletions(-) diff --git a/PlayerTags/Features/ChatTagTargetFeature.cs b/PlayerTags/Features/ChatTagTargetFeature.cs index 22d1560..9e25e90 100644 --- a/PlayerTags/Features/ChatTagTargetFeature.cs +++ b/PlayerTags/Features/ChatTagTargetFeature.cs @@ -5,6 +5,7 @@ using Dalamud.Game.Text.SeStringHandling; using Dalamud.Game.Text.SeStringHandling.Payloads; using FFXIVClientStructs.FFXIV.Client.UI.Misc; using Lumina.Excel.GeneratedSheets; +using Pilz.Dalamud.Tools.Strings; using PlayerTags.Configuration; using PlayerTags.Configuration.GameConfig; using PlayerTags.Data; @@ -337,7 +338,7 @@ namespace PlayerTags.Features var stringMatches = GetStringMatches(message); foreach (var stringMatch in stringMatches) { - Dictionary stringChanges = new Dictionary(); + StringChanges stringChanges = new(); if (stringMatch.GameObject is PlayerCharacter playerCharacter) { @@ -364,7 +365,7 @@ namespace PlayerTags.Features var generatedName = BuildPlayername(RandomNameGenerator.Generate(playerName)); if (generatedName != null) { - AddPayloadChanges(TagPosition.Replace, Enumerable.Empty().Append(new TextPayload(generatedName)), stringChanges, false); + AddPayloadChanges(StringPosition.Replace, Enumerable.Empty().Append(new TextPayload(generatedName)), stringChanges, false); } } } @@ -396,7 +397,7 @@ namespace PlayerTags.Features { var insertBehindNumberPrefix = tag.InsertBehindNumberPrefixInChat?.Value ?? true; var insertPositionInChat = tag.TagPositionInChat.InheritedValue.Value; - AddPayloadChanges(insertPositionInChat, payloads, stringChanges, insertBehindNumberPrefix); + AddPayloadChanges((StringPosition)insertPositionInChat, payloads, stringChanges, insertBehindNumberPrefix); } // An additional step to apply text color to additional locations diff --git a/PlayerTags/Features/NameplateTagTargetFeature.cs b/PlayerTags/Features/NameplateTagTargetFeature.cs index 9e30923..869e968 100644 --- a/PlayerTags/Features/NameplateTagTargetFeature.cs +++ b/PlayerTags/Features/NameplateTagTargetFeature.cs @@ -3,6 +3,8 @@ using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Game.Text.SeStringHandling; using Dalamud.Game.Text.SeStringHandling.Payloads; using Lumina.Excel.GeneratedSheets; +using Pilz.Dalamud.Nameplates.Tools; +using Pilz.Dalamud.Tools.Strings; using PlayerTags.Configuration; using PlayerTags.Data; using PlayerTags.GameInterface.Nameplates; @@ -129,19 +131,13 @@ namespace PlayerTags.Features /// The position of the changes. /// The payload changes to add. /// The dictionary to add changes to. - private void AddPayloadChanges(NameplateElement nameplateElement, TagPosition tagPosition, IEnumerable payloadChanges, Dictionary> nameplateChanges, bool forceUsingSingleAnchorPayload) + private void AddPayloadChanges(NameplateElement nameplateElement, TagPosition tagPosition, IEnumerable payloadChanges, NameplateChanges nameplateChanges, bool forceUsingSingleAnchorPayload) { - if (!payloadChanges.Any()) + if (payloadChanges.Any()) { - return; + var changes = nameplateChanges.GetChanges((NameplateElements)nameplateElement); + AddPayloadChanges((StringPosition)tagPosition, payloadChanges, changes, forceUsingSingleAnchorPayload); } - - if (!nameplateChanges.Keys.Contains(nameplateElement)) - { - nameplateChanges[nameplateElement] = new(); - } - - AddPayloadChanges(tagPosition, payloadChanges, nameplateChanges[nameplateElement], forceUsingSingleAnchorPayload); } /// @@ -153,7 +149,10 @@ namespace PlayerTags.Features /// The free company text to change. private void AddTagsToNameplate(GameObject gameObject, SeString name, SeString title, SeString freeCompany) { - Dictionary> nameplateChanges = new(); + NameplateChanges nameplateChanges = new(); + nameplateChanges.GetProps(NameplateElements.Name).Destination = name; + nameplateChanges.GetProps(NameplateElements.Title).Destination = title; + nameplateChanges.GetProps(NameplateElements.FreeCompany).Destination = freeCompany; if (gameObject is PlayerCharacter playerCharacter) { @@ -197,20 +196,7 @@ namespace PlayerTags.Features } // Build the final strings out of the payloads - foreach ((var nameplateElement, var stringChanges) in nameplateChanges) - { - SeString? seString = null; - - if (nameplateElement == NameplateElement.Name) - seString = name; - else if (nameplateElement == NameplateElement.Title) - seString = title; - else if (nameplateElement == NameplateElement.FreeCompany) - seString = freeCompany; - - if (seString != null) - ApplyStringChanges(seString, stringChanges); - } + ApplyNameplateChanges(nameplateChanges); if (gameObject is PlayerCharacter playerCharacter1) { @@ -234,5 +220,14 @@ namespace PlayerTags.Features } } } + + protected void ApplyNameplateChanges(NameplateChanges nameplateChanges) + { + var props = new NameplateChangesProps + { + Changes = nameplateChanges + }; + NameplateUpdateFactory.ApplyNameplateChanges(props); + } } } diff --git a/PlayerTags/Features/TagTargetFeature.cs b/PlayerTags/Features/TagTargetFeature.cs index ae5d457..7773da5 100644 --- a/PlayerTags/Features/TagTargetFeature.cs +++ b/PlayerTags/Features/TagTargetFeature.cs @@ -5,6 +5,7 @@ using Dalamud.Game.Text.SeStringHandling.Payloads; using FFXIVClientStructs.FFXIV.Client.Game.Object; using Lumina.Excel.GeneratedSheets; using Pilz.Dalamud.ActivityContexts; +using Pilz.Dalamud.Tools.Strings; using PlayerTags.Configuration.GameConfig; using PlayerTags.Data; using PlayerTags.Inheritables; @@ -21,12 +22,6 @@ namespace PlayerTags.Features /// public abstract class TagTargetFeature : IDisposable { - protected class StringChanges - { - public List Payloads { get; init; } = new(); - public bool ForceUsingSingleAnchorPayload { get; set; } = false; - } - public ActivityContextManager ActivityContextManager { get; init; } public TagTargetFeature() @@ -154,68 +149,6 @@ namespace PlayerTags.Features return newPayloads.ToArray(); } - /// - /// Adds an additional space text payload in between any existing text payloads. If there is an icon payload between two text payloads then the space is skipped. - /// Also adds an extra space to the beginning or end depending on the tag position and whether the most significant payload in either direction is a text payload. - /// In spirit, this is to ensure there is always a space between 2 text payloads, including between these payloads and the target payload. - /// - /// The payloads to add spaces between. - private void AddSpacesBetweenTextPayloads(List payloads, TagPosition tagPosition) - { - if (payloads == null) - { - return; - } - - if (!payloads.Any()) - { - return; - } - - List indicesToInsertSpacesAt = new List(); - int lastTextPayloadIndex = -1; - foreach (var payload in payloads.Reverse()) - { - if (payload is IconPayload iconPayload) - { - lastTextPayloadIndex = -1; - } - else if (payload is TextPayload textPayload) - { - if (lastTextPayloadIndex != -1) - { - indicesToInsertSpacesAt.Add(payloads.IndexOf(textPayload) + 1); - } - - lastTextPayloadIndex = payloads.IndexOf(textPayload); - } - } - - foreach (var indexToInsertSpaceAt in indicesToInsertSpacesAt) - { - payloads.Insert(indexToInsertSpaceAt, new TextPayload($" ")); - } - - // Decide whether to add a space to the end - if (tagPosition == TagPosition.Before) - { - var significantPayloads = payloads.Where(payload => payload is TextPayload || payload is IconPayload); - if (significantPayloads.Last() is TextPayload) - { - payloads.Add(new TextPayload($" ")); - } - } - // Decide whether to add a space to the beginning - else if (tagPosition == TagPosition.After) - { - var significantPayloads = payloads.Where(payload => payload is TextPayload || payload is IconPayload); - if (significantPayloads.First() is TextPayload) - { - payloads.Insert(0, new TextPayload($" ")); - } - } - } - protected static string BuildPlayername(string name) { var logNameType = GameConfigHelper.Instance.GetLogNameType(); @@ -260,14 +193,11 @@ namespace PlayerTags.Features /// The position to add changes to. /// The payloads to add. /// The dictionary to add the changes to. - protected void AddPayloadChanges(TagPosition tagPosition, IEnumerable payloads, Dictionary stringChanges, bool forceUsingSingleAnchorPayload) + protected void AddPayloadChanges(StringPosition tagPosition, IEnumerable payloads, StringChanges stringChanges, bool forceUsingSingleAnchorPayload) { if (payloads != null && payloads.Any() && stringChanges != null) { - if (!stringChanges.Keys.Contains(tagPosition)) - stringChanges[tagPosition] = new(); - - var changes = stringChanges[tagPosition]; + var changes = stringChanges.GetChange(tagPosition); changes.Payloads.AddRange(payloads); changes.ForceUsingSingleAnchorPayload = forceUsingSingleAnchorPayload; } @@ -279,80 +209,18 @@ namespace PlayerTags.Features /// The string to apply changes to. /// The changes to apply. /// The payload in the string that changes should be anchored to. If there is no anchor, the changes will be applied to the entire string. - protected void ApplyStringChanges(SeString seString, Dictionary stringChanges, List anchorPayloads = null, Payload anchorReplacePayload = null) + protected void ApplyStringChanges(SeString seString, StringChanges stringChanges, List anchorPayloads = null, Payload anchorReplacePayload = null) { - if (stringChanges.Count == 0) + var props = new StringChangesProps { - return; - } + Destination = seString, + AnchorPayload = anchorReplacePayload + }; - List tagPositionsOrdered = new List(); - // If there's no anchor payload, do replaces first so that befores and afters are based on the replaced data - if (anchorPayloads == null || !anchorPayloads.Any()) - { - tagPositionsOrdered.Add(TagPosition.Replace); - } + props.AnchorPayloads = anchorPayloads; + props.StringChanges = stringChanges; - tagPositionsOrdered.Add(TagPosition.Before); - tagPositionsOrdered.Add(TagPosition.After); - - // If there is an anchor payload, do replaces last so that we still know which payload needs to be removed - if (anchorPayloads != null && anchorPayloads.Any()) - { - tagPositionsOrdered.Add(TagPosition.Replace); - } - - foreach (var tagPosition in tagPositionsOrdered) - { - if (stringChanges.TryGetValue(tagPosition, out var payloads) && payloads.Payloads.Any()) - { - AddSpacesBetweenTextPayloads(stringChanges[tagPosition].Payloads, tagPosition); - if (tagPosition == TagPosition.Before) - { - Payload anchorFirst = payloads.ForceUsingSingleAnchorPayload ? anchorReplacePayload : anchorPayloads?.FirstOrDefault(); - - if (anchorFirst != null) - { - var anchorPayloadIndex = seString.Payloads.IndexOf(anchorFirst); - seString.Payloads.InsertRange(anchorPayloadIndex, payloads.Payloads); - } - else - { - seString.Payloads.InsertRange(0, payloads.Payloads); - } - } - else if (tagPosition == TagPosition.After) - { - Payload anchorLast = payloads.ForceUsingSingleAnchorPayload? anchorReplacePayload : anchorPayloads?.LastOrDefault(); - - if (anchorLast != null) - { - var anchorPayloadIndex = seString.Payloads.IndexOf(anchorLast); - seString.Payloads.InsertRange(anchorPayloadIndex + 1, payloads.Payloads); - } - else - { - seString.Payloads.AddRange(payloads.Payloads); - } - } - else if (tagPosition == TagPosition.Replace) - { - Payload anchorReplace = anchorReplacePayload; - - if (anchorReplace != null) - { - var anchorPayloadIndex = seString.Payloads.IndexOf(anchorReplace); - seString.Payloads.InsertRange(anchorPayloadIndex, payloads.Payloads); - seString.Remove(anchorReplace); - } - else - { - seString.Payloads.Clear(); - seString.Payloads.AddRange(payloads.Payloads); - } - } - } - } + StringUpdateFactory.ApplyStringChanges(props); } protected void ApplyTextFormatting(GameObject gameObject, Tag tag, SeString[] destStrings, InheritableValue[] textColorApplied, List preferedPayloads) From 1e3380cbd112f9e7f7e494a6540715eb3bb7a6a7 Mon Sep 17 00:00:00 2001 From: Pilzinsel64 Date: Sun, 6 Nov 2022 11:32:02 +0100 Subject: [PATCH 08/36] implement job icon feature --- .../Configuration/PluginConfiguration.cs | 4 +- .../Configuration/PluginConfigurationUI.cs | 5 ++ PlayerTags/Data/DefaultPluginData.cs | 4 +- PlayerTags/Data/Tag.cs | 9 ++- PlayerTags/Features/ChatTagTargetFeature.cs | 4 +- .../Features/NameplateTagTargetFeature.cs | 37 +++++++-- PlayerTags/Resources/Strings.Designer.cs | 75 +++++++++++++++---- PlayerTags/Resources/Strings.resx | 27 +++++-- 8 files changed, 130 insertions(+), 35 deletions(-) diff --git a/PlayerTags/Configuration/PluginConfiguration.cs b/PlayerTags/Configuration/PluginConfiguration.cs index 3c5761b..bc340e4 100644 --- a/PlayerTags/Configuration/PluginConfiguration.cs +++ b/PlayerTags/Configuration/PluginConfiguration.cs @@ -1,6 +1,7 @@ using Dalamud.Configuration; using Newtonsoft.Json; using Pilz.Dalamud.ActivityContexts; +using Pilz.Dalamud.Nameplates.Tools; using PlayerTags.Data; using PlayerTags.Inheritables; using System; @@ -35,12 +36,13 @@ namespace PlayerTags.Configuration { ActivityType.PvpDuty, new GeneralOptionsClass() } }; + public StatusIconPriorizerSettings StatusIconPriorizerSettings = new(); public bool IsPlayerNameRandomlyGenerated = false; public bool IsCustomTagsContextMenuEnabled = true; public bool IsShowInheritedPropertiesEnabled = true; public bool IsPlayersTabOrderedByProximity = false; public bool IsPlayersTabSelfVisible = true; - public bool IsPlayersTabFriendsVisible = true; + public bool IsPlayersTabFriendsVisible = true; public bool IsPlayersTabPartyVisible = true; public bool IsPlayersTabAllianceVisible = true; public bool IsPlayersTabEnemiesVisible = true; diff --git a/PlayerTags/Configuration/PluginConfigurationUI.cs b/PlayerTags/Configuration/PluginConfigurationUI.cs index 9f2f923..d012e00 100644 --- a/PlayerTags/Configuration/PluginConfigurationUI.cs +++ b/PlayerTags/Configuration/PluginConfigurationUI.cs @@ -6,6 +6,7 @@ using Dalamud.Logging; using ImGuiNET; using Lumina.Excel.GeneratedSheets; using Pilz.Dalamud.ActivityContexts; +using Pilz.Dalamud.Icons; using PlayerTags.Data; using PlayerTags.Inheritables; using PlayerTags.PluginStrings; @@ -692,6 +693,10 @@ namespace PlayerTags.Configuration { DrawInheritable(selectedInheritable.Inheritable.Key, true, false, inheritableNameplateTitlePosition); } + else if (selectedInheritable.Inheritable.Value is InheritableValue inheritableJobIconSetName) + { + DrawInheritable(selectedInheritable.Inheritable.Key, false, false, inheritableJobIconSetName); + } else if (selectedInheritable.Inheritable.Value is InheritableReference inheritableString) { DrawInheritable(selectedInheritable.Inheritable.Key, inheritableString); diff --git a/PlayerTags/Data/DefaultPluginData.cs b/PlayerTags/Data/DefaultPluginData.cs index 143fc64..48263ae 100644 --- a/PlayerTags/Data/DefaultPluginData.cs +++ b/PlayerTags/Data/DefaultPluginData.cs @@ -50,9 +50,9 @@ namespace PlayerTags.Data { IsSelected = false, IsExpanded = true, - IsIconVisibleInChat = true, + IsRoleIconVisibleInChat = true, IsTextVisibleInChat = true, - IsIconVisibleInNameplates = true, + IsRoleIconVisibleInNameplates = true, IsTextVisibleInNameplates = true, IsTextColorAppliedToChatName = true, }; diff --git a/PlayerTags/Data/Tag.cs b/PlayerTags/Data/Tag.cs index 51e9d0a..e32fe59 100644 --- a/PlayerTags/Data/Tag.cs +++ b/PlayerTags/Data/Tag.cs @@ -1,4 +1,5 @@ using Dalamud.Game.Text.SeStringHandling; +using Pilz.Dalamud.Icons; using PlayerTags.Inheritables; using PlayerTags.PluginStrings; using System; @@ -99,9 +100,13 @@ namespace PlayerTags.Data [InheritableCategory("IconCategory")] public InheritableValue Icon = new InheritableValue(BitmapFontIcon.Aethernet); [InheritableCategory("IconCategory")] - public InheritableValue IsIconVisibleInChat = new InheritableValue(false); + public InheritableValue IsRoleIconVisibleInChat = new InheritableValue(false); [InheritableCategory("IconCategory")] - public InheritableValue IsIconVisibleInNameplates = new InheritableValue(false); + public InheritableValue IsRoleIconVisibleInNameplates = new InheritableValue(false); + [InheritableCategory("IconCategory")] + public InheritableValue IsJobIconVisibleInNameplates = new InheritableValue(false); + [InheritableCategory("IconCategory")] + public InheritableValue JobIconSet = new InheritableValue(JobIconSetName.Framed); [InheritableCategory("TextCategory")] public InheritableReference Text = new InheritableReference(""); diff --git a/PlayerTags/Features/ChatTagTargetFeature.cs b/PlayerTags/Features/ChatTagTargetFeature.cs index 9e25e90..4239425 100644 --- a/PlayerTags/Features/ChatTagTargetFeature.cs +++ b/PlayerTags/Features/ChatTagTargetFeature.cs @@ -130,9 +130,9 @@ namespace PlayerTags.Features protected override bool IsIconVisible(Tag tag) { - if (tag.IsIconVisibleInChat.InheritedValue != null) + if (tag.IsRoleIconVisibleInChat.InheritedValue != null) { - return tag.IsIconVisibleInChat.InheritedValue.Value; + return tag.IsRoleIconVisibleInChat.InheritedValue.Value; } return false; diff --git a/PlayerTags/Features/NameplateTagTargetFeature.cs b/PlayerTags/Features/NameplateTagTargetFeature.cs index 869e968..c73205f 100644 --- a/PlayerTags/Features/NameplateTagTargetFeature.cs +++ b/PlayerTags/Features/NameplateTagTargetFeature.cs @@ -3,6 +3,7 @@ using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Game.Text.SeStringHandling; using Dalamud.Game.Text.SeStringHandling.Payloads; using Lumina.Excel.GeneratedSheets; +using Pilz.Dalamud.Icons; using Pilz.Dalamud.Nameplates.Tools; using Pilz.Dalamud.Tools.Strings; using PlayerTags.Configuration; @@ -20,25 +21,31 @@ namespace PlayerTags.Features /// public class NameplateTagTargetFeature : TagTargetFeature { - private PluginConfiguration m_PluginConfiguration; - private PluginData m_PluginData; + private readonly PluginConfiguration m_PluginConfiguration; + private readonly PluginData m_PluginData; + private readonly StatusIconPriorizer statusiconPriorizer; + private readonly JobIconSets jobIconSets = new(); private Nameplate? m_Nameplate; public NameplateTagTargetFeature(PluginConfiguration pluginConfiguration, PluginData pluginData) { m_PluginConfiguration = pluginConfiguration; m_PluginData = pluginData; + statusiconPriorizer = new(pluginConfiguration.StatusIconPriorizerSettings); PluginServices.ClientState.Login += ClientState_Login; PluginServices.ClientState.Logout += ClientState_Logout; + Hook(); } public override void Dispose() { Unhook(); + PluginServices.ClientState.Logout -= ClientState_Logout; PluginServices.ClientState.Login -= ClientState_Login; + base.Dispose(); } @@ -81,9 +88,9 @@ namespace PlayerTags.Features protected override bool IsIconVisible(Tag tag) { - if (tag.IsIconVisibleInNameplates.InheritedValue != null) + if (tag.IsRoleIconVisibleInNameplates.InheritedValue != null) { - return tag.IsIconVisibleInNameplates.InheritedValue.Value; + return tag.IsRoleIconVisibleInNameplates.InheritedValue.Value; } return false; @@ -102,9 +109,12 @@ namespace PlayerTags.Features private void Nameplate_PlayerNameplateUpdated(PlayerNameplateUpdatedArgs args) { var beforeTitleBytes = args.Title.Encode(); - AddTagsToNameplate(args.PlayerCharacter, args.Name, args.Title, args.FreeCompany); + var iconID = args.IconId; var generalOptions = m_PluginConfiguration.GeneralOptions[ActivityContextManager.CurrentActivityContext.ActivityType]; + AddTagsToNameplate(args.PlayerCharacter, args.Name, args.Title, args.FreeCompany, ref iconID); + args.IconId = iconID; + if (generalOptions.NameplateTitlePosition == NameplateTitlePosition.AlwaysAboveName) args.IsTitleAboveName = true; else if (generalOptions.NameplateTitlePosition == NameplateTitlePosition.AlwaysBelowName) @@ -147,8 +157,9 @@ namespace PlayerTags.Features /// The name text to change. /// The title text to change. /// The free company text to change. - private void AddTagsToNameplate(GameObject gameObject, SeString name, SeString title, SeString freeCompany) + private void AddTagsToNameplate(GameObject gameObject, SeString name, SeString title, SeString freeCompany, ref int statusIcon) { + int? newStatusIcon = null; NameplateChanges nameplateChanges = new(); nameplateChanges.GetProps(NameplateElements.Name).Destination = name; nameplateChanges.GetProps(NameplateElements.Title).Destination = title; @@ -156,8 +167,11 @@ namespace PlayerTags.Features if (gameObject is PlayerCharacter playerCharacter) { + var classJob = playerCharacter.ClassJob; + var classJobGameData = classJob?.GameData; + // Add the job tags - if (playerCharacter.ClassJob.GameData != null && m_PluginData.JobTags.TryGetValue(playerCharacter.ClassJob.GameData.Abbreviation, out var jobTag)) + if (classJobGameData != null && m_PluginData.JobTags.TryGetValue(classJobGameData.Abbreviation, out var jobTag)) { if (jobTag.TagTargetInNameplates.InheritedValue != null && jobTag.TagPositionInNameplates.InheritedValue != null) checkTag(jobTag); @@ -192,9 +206,18 @@ namespace PlayerTags.Features if (payloads.Any()) AddPayloadChanges(tag.TagTargetInNameplates.InheritedValue.Value, tag.TagPositionInNameplates.InheritedValue.Value, payloads, nameplateChanges, false); } + if (newStatusIcon == null && classJob != null && (tag.IsJobIconVisibleInNameplates?.InheritedValue ?? false)) + newStatusIcon = jobIconSets.GetJobIcon(JobIconSetName.Framed, classJob.Id); } } + // Apply new status icon + if (newStatusIcon != null) + { + var change = nameplateChanges.GetChange(NameplateElements.Name, StringPosition.Before); + NameplateUpdateFactory.ApplyStatusIconWithPrio(ref statusIcon, (int)newStatusIcon, change, ActivityContextManager.CurrentActivityContext, statusiconPriorizer); + } + // Build the final strings out of the payloads ApplyNameplateChanges(nameplateChanges); diff --git a/PlayerTags/Resources/Strings.Designer.cs b/PlayerTags/Resources/Strings.Designer.cs index ceb3809..a7e101f 100644 --- a/PlayerTags/Resources/Strings.Designer.cs +++ b/PlayerTags/Resources/Strings.Designer.cs @@ -358,29 +358,20 @@ namespace PlayerTags.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Whether the icon will be shown in chat. ähnelt. + /// Sucht eine lokalisierte Zeichenfolge, die Show job icon in nameplates ähnelt. /// - public static string Loc_IsIconVisibleInChat_Description { + public static string Loc_IsJobIconVisibleInNameplates { get { - return ResourceManager.GetString("Loc_IsIconVisibleInChat_Description", resourceCulture); + return ResourceManager.GetString("Loc_IsJobIconVisibleInNameplates", resourceCulture); } } /// - /// Sucht eine lokalisierte Zeichenfolge, die Show in nameplates ähnelt. + /// Sucht eine lokalisierte Zeichenfolge, die Whether the job icon will be shown in nameplates. ähnelt. /// - public static string Loc_IsIconVisibleInNameplates { + public static string Loc_IsJobIconVisibleInNameplates_Description { get { - return ResourceManager.GetString("Loc_IsIconVisibleInNameplates", resourceCulture); - } - } - - /// - /// Sucht eine lokalisierte Zeichenfolge, die Whether the icon will be shown in nameplates. ähnelt. - /// - public static string Loc_IsIconVisibleInNameplates_Description { - get { - return ResourceManager.GetString("Loc_IsIconVisibleInNameplates_Description", resourceCulture); + return ResourceManager.GetString("Loc_IsJobIconVisibleInNameplates_Description", resourceCulture); } } @@ -546,6 +537,42 @@ namespace PlayerTags.Resources { } } + /// + /// Sucht eine lokalisierte Zeichenfolge, die Whether the role icon will be shown in chat. ähnelt. + /// + public static string Loc_IsRoleIconVisibleInChat_Description { + get { + return ResourceManager.GetString("Loc_IsRoleIconVisibleInChat_Description", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Show role icon in nameplates ähnelt. + /// + public static string Loc_IsRoleIconVisibleInNameplates { + get { + return ResourceManager.GetString("Loc_IsRoleIconVisibleInNameplates", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Whether the role icon will be shown in nameplates. ähnelt. + /// + public static string Loc_IsRoleIconVisibleInNameplates_Description { + get { + return ResourceManager.GetString("Loc_IsRoleIconVisibleInNameplates_Description", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Show role icon in nameplate ähnelt. + /// + public static string Loc_IsRoleJobIconVisibleInNameplates { + get { + return ResourceManager.GetString("Loc_IsRoleJobIconVisibleInNameplates", resourceCulture); + } + } + /// /// Sucht eine lokalisierte Zeichenfolge, die Selected ähnelt. /// @@ -861,6 +888,24 @@ namespace PlayerTags.Resources { } } + /// + /// Sucht eine lokalisierte Zeichenfolge, die Job icon set ähnelt. + /// + public static string Loc_JobIconSet { + get { + return ResourceManager.GetString("Loc_JobIconSet", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die The icon set to use for displaying the job icon. You can also choose the role icon set to display the role icon instead. ähnelt. + /// + public static string Loc_JobIconSet_Description { + get { + return ResourceManager.GetString("Loc_JobIconSet_Description", resourceCulture); + } + } + /// /// Sucht eine lokalisierte Zeichenfolge, die Hand ähnelt. /// diff --git a/PlayerTags/Resources/Strings.resx b/PlayerTags/Resources/Strings.resx index c272ddc..c5639a9 100644 --- a/PlayerTags/Resources/Strings.resx +++ b/PlayerTags/Resources/Strings.resx @@ -354,14 +354,14 @@ Show in chat - - Whether the icon will be shown in chat. + + Whether the role icon will be shown in chat. - - Show in nameplates + + Show role icon in nameplates - - Whether the icon will be shown in nameplates. + + Whether the role icon will be shown in nameplates. Text @@ -642,4 +642,19 @@ Chat + + Show role icon in nameplate + + + Show job icon in nameplates + + + Whether the job icon will be shown in nameplates. + + + Job icon set + + + The icon set to use for displaying the job icon. You can also choose the role icon set to display the role icon instead. + \ No newline at end of file From 9c927d91f3f79560e7080b1a62c2ffed3bac4675 Mon Sep 17 00:00:00 2001 From: Pilzinsel64 Date: Tue, 8 Nov 2022 07:42:58 +0100 Subject: [PATCH 09/36] set some json attributes for eventually changes of configuraiton in the future --- .../Configuration/PluginConfiguration.cs | 28 ++++++++++--------- PlayerTags/Data/Tag.cs | 9 ++++++ .../Inheritables/InheritableReference.cs | 6 +++- PlayerTags/Inheritables/InheritableValue.cs | 3 ++ 4 files changed, 32 insertions(+), 14 deletions(-) diff --git a/PlayerTags/Configuration/PluginConfiguration.cs b/PlayerTags/Configuration/PluginConfiguration.cs index bc340e4..0bc50ac 100644 --- a/PlayerTags/Configuration/PluginConfiguration.cs +++ b/PlayerTags/Configuration/PluginConfiguration.cs @@ -13,21 +13,9 @@ namespace PlayerTags.Configuration [Serializable] public class PluginConfiguration : IPluginConfiguration { - public int Version { get; set; } = 0; + public int Version { get; set; } = 1; public bool IsVisible = false; - [Obsolete] - [JsonProperty("GeneralOptions")] - private Dictionary GeneralOptionsV1 - { - set - { - GeneralOptions.Clear(); - foreach (var kvp in value) - GeneralOptions.Add((ActivityType)kvp.Key, kvp.Value); - } - } - [JsonProperty("GeneralOptionsV2")] public Dictionary GeneralOptions = new() { @@ -49,6 +37,8 @@ namespace PlayerTags.Configuration public bool IsPlayersTabOthersVisible = false; public bool IsGeneralOptionsAllTheSameEnabled = true; + public Tag AllTags = null; + [JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)] public Dictionary AllTagsChanges = new Dictionary(); @@ -81,6 +71,18 @@ namespace PlayerTags.Configuration #region Obsulate Properties + [Obsolete] + [JsonProperty("GeneralOptions")] + private Dictionary GeneralOptionsV1 + { + set + { + GeneralOptions.Clear(); + foreach (var kvp in value) + GeneralOptions.Add((ActivityType)kvp.Key, kvp.Value); + } + } + [JsonProperty("NameplateFreeCompanyVisibility"), Obsolete] private NameplateFreeCompanyVisibility NameplateFreeCompanyVisibilityV1 { diff --git a/PlayerTags/Data/Tag.cs b/PlayerTags/Data/Tag.cs index e32fe59..5f427e6 100644 --- a/PlayerTags/Data/Tag.cs +++ b/PlayerTags/Data/Tag.cs @@ -1,4 +1,5 @@ using Dalamud.Game.Text.SeStringHandling; +using Newtonsoft.Json; using Pilz.Dalamud.Icons; using PlayerTags.Inheritables; using PlayerTags.PluginStrings; @@ -12,7 +13,10 @@ namespace PlayerTags.Data { public IPluginString Name { get; init; } + [JsonProperty("Parent")] private Tag? m_Parent = null; + + [JsonIgnore] public Tag? Parent { get => m_Parent; @@ -43,6 +47,7 @@ namespace PlayerTags.Data public List Children { get; } = new List(); + [JsonIgnore] public IEnumerable Descendents { get @@ -58,7 +63,9 @@ namespace PlayerTags.Data } } + [JsonIgnore] private Dictionary? m_Inheritables = null; + [JsonIgnore] public Dictionary Inheritables { get @@ -165,6 +172,7 @@ namespace PlayerTags.Data [InheritableCategory("PlayerCategory")] public InheritableValue IsVisibleForOtherPlayers = new InheritableValue(false); + [JsonIgnore] public string[] IdentitiesToAddTo { get @@ -179,6 +187,7 @@ namespace PlayerTags.Data } private Tag? m_Defaults; + [JsonIgnore] public bool HasDefaults { get { return m_Defaults != null; } diff --git a/PlayerTags/Inheritables/InheritableReference.cs b/PlayerTags/Inheritables/InheritableReference.cs index abc101f..ea3c5e2 100644 --- a/PlayerTags/Inheritables/InheritableReference.cs +++ b/PlayerTags/Inheritables/InheritableReference.cs @@ -1,4 +1,6 @@ -namespace PlayerTags.Inheritables +using Newtonsoft.Json; + +namespace PlayerTags.Inheritables { public class InheritableReference : IInheritable where T : class @@ -7,8 +9,10 @@ public InheritableBehavior Behavior { get; set; } + [JsonProperty] public T Value; + [JsonIgnore] public T? InheritedValue { get diff --git a/PlayerTags/Inheritables/InheritableValue.cs b/PlayerTags/Inheritables/InheritableValue.cs index e49e304..f17e55a 100644 --- a/PlayerTags/Inheritables/InheritableValue.cs +++ b/PlayerTags/Inheritables/InheritableValue.cs @@ -1,4 +1,5 @@ using Dalamud.Logging; +using Newtonsoft.Json; using System; namespace PlayerTags.Inheritables @@ -10,8 +11,10 @@ namespace PlayerTags.Inheritables public InheritableBehavior Behavior { get; set; } + [JsonProperty] public T Value; + [JsonIgnore] public T? InheritedValue { get From 2770797ada2b91c2d4319f062123f7980fc2df4c Mon Sep 17 00:00:00 2001 From: Pilzinsel64 Date: Tue, 8 Nov 2022 07:52:11 +0100 Subject: [PATCH 10/36] remove currently unneeded configuration --- PlayerTags/Configuration/PluginConfiguration.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/PlayerTags/Configuration/PluginConfiguration.cs b/PlayerTags/Configuration/PluginConfiguration.cs index 0bc50ac..34cb5cf 100644 --- a/PlayerTags/Configuration/PluginConfiguration.cs +++ b/PlayerTags/Configuration/PluginConfiguration.cs @@ -37,8 +37,6 @@ namespace PlayerTags.Configuration public bool IsPlayersTabOthersVisible = false; public bool IsGeneralOptionsAllTheSameEnabled = true; - public Tag AllTags = null; - [JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)] public Dictionary AllTagsChanges = new Dictionary(); From c2b2fa86a4dff4235d5f4f5ccbaaa5614da2ae82 Mon Sep 17 00:00:00 2001 From: Pilzinsel64 Date: Tue, 8 Nov 2022 07:57:21 +0100 Subject: [PATCH 11/36] maybe fix wrong(?) operator -> Chat messages should not be processed anymore when disabled --- PlayerTags/Features/ChatTagTargetFeature.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PlayerTags/Features/ChatTagTargetFeature.cs b/PlayerTags/Features/ChatTagTargetFeature.cs index 4239425..cfb29da 100644 --- a/PlayerTags/Features/ChatTagTargetFeature.cs +++ b/PlayerTags/Features/ChatTagTargetFeature.cs @@ -121,7 +121,7 @@ namespace PlayerTags.Features private void Chat_ChatMessage(XivChatType type, uint senderId, ref SeString sender, ref SeString message, ref bool isHandled) { - if (m_PluginConfiguration.GeneralOptions[ActivityContextManager.CurrentActivityContext.ActivityType].IsApplyTagsToAllChatMessagesEnabled || Enum.IsDefined(type)) + if (m_PluginConfiguration.GeneralOptions[ActivityContextManager.CurrentActivityContext.ActivityType].IsApplyTagsToAllChatMessagesEnabled && Enum.IsDefined(type)) { AddTagsToChat(sender, type, true); AddTagsToChat(message, type, false); From 5501d1ecc47a331ad28c40e9bfaa9d6d49bf46fc Mon Sep 17 00:00:00 2001 From: Pilzinsel64 Date: Wed, 9 Nov 2022 12:57:23 +0100 Subject: [PATCH 12/36] add settings for chat target types (broken state yet) --- PlayerTags.sln | 6 - .../Configuration/PluginConfiguration.cs | 61 +++++++++- .../Configuration/PluginConfigurationUI.cs | 107 ++++++++++++++++++ PlayerTags/Data/DefaultPluginData.cs | 6 +- PlayerTags/Data/Tag.cs | 7 +- PlayerTags/Data/XivChatTypeList.cs | 56 +++++++++ PlayerTags/Features/ChatTagTargetFeature.cs | 2 +- PlayerTags/GeneralConverter.cs | 30 ----- .../Inheritables/InheritableBehavior.cs | 5 +- PlayerTags/Inheritables/InheritableData.cs | 1 - .../Inheritables/InheritableReference.cs | 1 + PlayerTags/PlayerTags.csproj | 2 +- PlayerTags/Plugin.cs | 3 +- PlayerTags/Resources/Strings.Designer.cs | 27 +++++ PlayerTags/Resources/Strings.resx | 9 ++ PlayerTags/packages.lock.json | 7 +- 16 files changed, 282 insertions(+), 48 deletions(-) create mode 100644 PlayerTags/Data/XivChatTypeList.cs delete mode 100644 PlayerTags/GeneralConverter.cs diff --git a/PlayerTags.sln b/PlayerTags.sln index 0219690..d0f9aff 100644 --- a/PlayerTags.sln +++ b/PlayerTags.sln @@ -5,8 +5,6 @@ VisualStudioVersion = 17.3.32929.385 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PlayerTags", "PlayerTags\PlayerTags.csproj", "{13C812E9-0D42-4B95-8646-40EEBF30636F}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Pilz.Dalamud", "..\Pilz.Dalamud\Pilz.Dalamud\Pilz.Dalamud.csproj", "{A92D2FFC-FDB8-4F28-B5DD-4A1B3EB0B1BB}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -17,10 +15,6 @@ Global {13C812E9-0D42-4B95-8646-40EEBF30636F}.Debug|x64.Build.0 = Debug|x64 {13C812E9-0D42-4B95-8646-40EEBF30636F}.Release|x64.ActiveCfg = Release|x64 {13C812E9-0D42-4B95-8646-40EEBF30636F}.Release|x64.Build.0 = Release|x64 - {A92D2FFC-FDB8-4F28-B5DD-4A1B3EB0B1BB}.Debug|x64.ActiveCfg = Debug|x64 - {A92D2FFC-FDB8-4F28-B5DD-4A1B3EB0B1BB}.Debug|x64.Build.0 = Debug|x64 - {A92D2FFC-FDB8-4F28-B5DD-4A1B3EB0B1BB}.Release|x64.ActiveCfg = Release|x64 - {A92D2FFC-FDB8-4F28-B5DD-4A1B3EB0B1BB}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/PlayerTags/Configuration/PluginConfiguration.cs b/PlayerTags/Configuration/PluginConfiguration.cs index 34cb5cf..6d3a2a9 100644 --- a/PlayerTags/Configuration/PluginConfiguration.cs +++ b/PlayerTags/Configuration/PluginConfiguration.cs @@ -1,19 +1,27 @@ using Dalamud.Configuration; using Newtonsoft.Json; +using Newtonsoft.Json.Converters; using Pilz.Dalamud.ActivityContexts; using Pilz.Dalamud.Nameplates.Tools; using PlayerTags.Data; using PlayerTags.Inheritables; using System; using System.Collections.Generic; +using System.IO; using System.Linq; +using System.Reflection.Metadata.Ecma335; +using System.Runtime.CompilerServices; namespace PlayerTags.Configuration { [Serializable] public class PluginConfiguration : IPluginConfiguration { - public int Version { get; set; } = 1; + private const int DEFAULT_CONFIG_VERSION = 1; + + [JsonProperty] + public int RootVersion { get; private set; } = DEFAULT_CONFIG_VERSION; + public int Version { get; set; } = DEFAULT_CONFIG_VERSION; public bool IsVisible = false; [JsonProperty("GeneralOptionsV2")] @@ -24,7 +32,7 @@ namespace PlayerTags.Configuration { ActivityType.PvpDuty, new GeneralOptionsClass() } }; - public StatusIconPriorizerSettings StatusIconPriorizerSettings = new(); + public StatusIconPriorizerSettings StatusIconPriorizerSettings = new(true); public bool IsPlayerNameRandomlyGenerated = false; public bool IsCustomTagsContextMenuEnabled = true; public bool IsShowInheritedPropertiesEnabled = true; @@ -218,9 +226,56 @@ namespace PlayerTags.Configuration Identities = pluginData.Identities; - PluginServices.DalamudPluginInterface.SavePluginConfig(this); + SavePluginConfig(); + Saved?.Invoke(); } + + private void SavePluginConfig() + { + Version = DEFAULT_CONFIG_VERSION; + var configFilePath = GetConfigFilePath(); + var configFileContent = JsonConvert.SerializeObject(this, Formatting.Indented, GetJsonSettings()); + File.WriteAllText(configFilePath, configFileContent); + } + + public static PluginConfiguration LoadPluginConfig() + { + var configFilePath = GetConfigFilePath(); + object config = null; + + if (File.Exists(configFilePath)) + { + var configFileContent = File.ReadAllText(configFilePath); + config = JsonConvert.DeserializeObject(configFileContent, GetJsonSettings()); + } + else + { + // Try loading the old settings, if possible + configFilePath = PluginServices.DalamudPluginInterface.ConfigFile.FullName; + config = PluginServices.DalamudPluginInterface.GetPluginConfig(); + } + + return config as PluginConfiguration; + } + + private static string GetConfigFilePath() + { + return Path.Combine(PluginServices.DalamudPluginInterface.ConfigDirectory.FullName, "Config.json"); + } + + private static JsonSerializerSettings GetJsonSettings() + { + var jsonSettings = new JsonSerializerSettings + { + TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple, + TypeNameHandling = TypeNameHandling.Auto, + }; + + jsonSettings.Converters.Add(new StringEnumConverter()); + + return jsonSettings; + } } public class GeneralOptionsClass diff --git a/PlayerTags/Configuration/PluginConfigurationUI.cs b/PlayerTags/Configuration/PluginConfigurationUI.cs index d012e00..9f09e0f 100644 --- a/PlayerTags/Configuration/PluginConfigurationUI.cs +++ b/PlayerTags/Configuration/PluginConfigurationUI.cs @@ -1,8 +1,11 @@ using Dalamud.Configuration; using Dalamud.Game.ClientState.Objects.SubKinds; +using Dalamud.Game.Text; using Dalamud.Game.Text.SeStringHandling; using Dalamud.Interface; using Dalamud.Logging; +using FFXIVClientStructs.FFXIV.Client.Game.UI; +using FFXIVClientStructs.Havok; using ImGuiNET; using Lumina.Excel.GeneratedSheets; using Pilz.Dalamud.ActivityContexts; @@ -15,6 +18,8 @@ using System; using System.Collections.Generic; using System.Linq; using System.Numerics; +using System.Threading.Tasks; +using System.Transactions; namespace PlayerTags.Configuration { @@ -31,6 +36,7 @@ namespace PlayerTags.Configuration private PropertyProxy propertyProxy; private InheritableValue? m_ColorPickerPopupDataContext; + private Dictionary inheritableTEnumProxies = new(); public PluginConfigurationUI(PluginConfiguration config, PluginData pluginData) { @@ -697,6 +703,10 @@ namespace PlayerTags.Configuration { DrawInheritable(selectedInheritable.Inheritable.Key, false, false, inheritableJobIconSetName); } + else if (selectedInheritable.Inheritable.Value is InheritableReference> inheritableXivChatType) + { + DrawMultiselect(selectedInheritable.Inheritable.Key, inheritableXivChatType); + } else if (selectedInheritable.Inheritable.Value is InheritableReference inheritableString) { DrawInheritable(selectedInheritable.Inheritable.Key, inheritableString); @@ -1058,6 +1068,66 @@ namespace PlayerTags.Configuration ImGui.TextColored(new Vector4(0.7f, 0.6f, 1f, 1f), label); } + private void DrawMultiselect(string localizedStringName, InheritableReference> inheritable) where TEnum : Enum + { + bool isDisabled = inheritable.Behavior == InheritableBehavior.Inherit; + EnumList proxyKey = isDisabled ? inheritable.InheritedValue : inheritable.Value; + + if (isDisabled) + proxyKey = inheritable.InheritedValue; + if (proxyKey == null) + proxyKey = inheritable.Value; + + if (isDisabled) + ImGui.PushStyleVar(ImGuiStyleVar.Alpha, 0.25f); + + var isExpanded = ImGui.CollapsingHeader(Localizer.GetString(localizedStringName, false)); + + if (ImGui.IsItemHovered()) + ImGui.SetTooltip(Localizer.GetString(localizedStringName, true)); + + if (isDisabled) + { + ImGui.SameLine(); + ImGui.Text(Strings.Loc_Static_Inherited); + } + else + DrawRemovePropertyOverrideButton(inheritable); + + if (isExpanded) + { + bool isClicked = false; + var typeofEnum = typeof(TEnum); + EnumMultiselectProxy proxy; + + if (inheritableTEnumProxies.ContainsKey(proxyKey)) + proxy = inheritableTEnumProxies[proxyKey] as EnumMultiselectProxy; + else + { + proxy = new EnumMultiselectProxy(proxyKey); + inheritableTEnumProxies.Add(proxyKey, proxy); + } + + foreach (var entry in proxy.Entries) + { + var entryName = Enum.GetName(typeofEnum, entry.Value); + var tempval = entry.Enabled; + isClicked = ImGui.Checkbox(Localizer.GetString(entryName), ref isDisabled ? ref tempval : ref entry.Enabled); + if (ImGui.IsItemHovered()) + ImGui.SetTooltip(Localizer.GetString(entryName, true)); + } + + if (!isDisabled && isClicked) + { + proxy.ApplyTo(proxyKey); + SaveSettings(); + } + } + + if (isDisabled) + ImGui.PopStyleVar(); + } + private void DrawComboBox(bool isLabelVisible, bool shouldLocalizeNames, bool shouldOrderNames, ref TEnum currentValue, System.Action changed) where TEnum : Enum { @@ -1278,5 +1348,42 @@ namespace PlayerTags.Configuration PveDuty, PvpDuty } + + private class EnumMultiselectProxy where TEnum : Enum + { + public List Entries { get; } = new(); + + public EnumMultiselectProxy(EnumList target) + { + foreach (TEnum value in Enum.GetValues(typeof(TEnum))) + Entries.Add(new(value, target.Contains(value))); + } + + public void ApplyTo(EnumList target) + { + foreach (var entry in Entries) + { + if (entry.Enabled) + { + if (!target.Contains(entry.Value)) + target.Add(entry.Value); + } + else if (target.Contains(entry.Value)) + target.Remove(entry.Value); + } + } + + public class Entry + { + public TEnum Value { get; set; } + public bool Enabled; + + public Entry(TEnum value, bool enabled) + { + Value = value; + Enabled = enabled; + } + } + } } } diff --git a/PlayerTags/Data/DefaultPluginData.cs b/PlayerTags/Data/DefaultPluginData.cs index 48263ae..71a43ea 100644 --- a/PlayerTags/Data/DefaultPluginData.cs +++ b/PlayerTags/Data/DefaultPluginData.cs @@ -1,5 +1,7 @@ -using Dalamud.Game.Text.SeStringHandling; +using Dalamud.Game.Text; +using Dalamud.Game.Text.SeStringHandling; using Lumina.Excel.GeneratedSheets; +using System; using System.Collections.Generic; using System.Linq; @@ -44,6 +46,8 @@ namespace PlayerTags.Data IsVisibleForAlliancePlayers = true, IsVisibleForEnemyPlayers = true, IsVisibleForOtherPlayers = true, + + TargetChatTypes = new EnumList(Enum.GetValues()), }; AllRoleTags = new Tag() diff --git a/PlayerTags/Data/Tag.cs b/PlayerTags/Data/Tag.cs index 5f427e6..6338078 100644 --- a/PlayerTags/Data/Tag.cs +++ b/PlayerTags/Data/Tag.cs @@ -1,5 +1,7 @@ -using Dalamud.Game.Text.SeStringHandling; +using Dalamud.Game.Text; +using Dalamud.Game.Text.SeStringHandling; using Newtonsoft.Json; +using Newtonsoft.Json.Converters; using Pilz.Dalamud.Icons; using PlayerTags.Inheritables; using PlayerTags.PluginStrings; @@ -172,6 +174,9 @@ namespace PlayerTags.Data [InheritableCategory("PlayerCategory")] public InheritableValue IsVisibleForOtherPlayers = new InheritableValue(false); + [InheritableCategory("ChatFeatureCategory")] + public InheritableReference> TargetChatTypes = new(new EnumList(Enum.GetValues())); + [JsonIgnore] public string[] IdentitiesToAddTo { diff --git a/PlayerTags/Data/XivChatTypeList.cs b/PlayerTags/Data/XivChatTypeList.cs new file mode 100644 index 0000000..e05fdb8 --- /dev/null +++ b/PlayerTags/Data/XivChatTypeList.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PlayerTags.Data +{ + public class EnumList : List where TEnum : Enum + { + public EnumList() : base() + { + } + + public EnumList(IEnumerable collection) : base(collection) + { + } + + //// this is first one '==' + //public static bool operator ==(EnumList obj1, EnumList obj2) + //{ + + //} + + //// this is second one '!=' + //public static bool operator !=(EnumList obj1, EnumList obj2) + //{ + // return !(obj1 == obj2); + //} + + public override bool Equals(object? obj) + { + var obj1 = this; + var obj2 = obj as EnumList; + + if (obj1 is not null && obj2 is not null) + { + if (obj1.Count != obj2.Count) + return false; + + for (int i = 0; i < obj1.Count; i++) + { + if (!obj1[i]?.Equals(obj2[i]) ?? true) + return false; + } + } + + return true; + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + } +} diff --git a/PlayerTags/Features/ChatTagTargetFeature.cs b/PlayerTags/Features/ChatTagTargetFeature.cs index cfb29da..57d9da8 100644 --- a/PlayerTags/Features/ChatTagTargetFeature.cs +++ b/PlayerTags/Features/ChatTagTargetFeature.cs @@ -345,7 +345,7 @@ namespace PlayerTags.Features // Add the job tag if (playerCharacter.ClassJob.GameData != null && m_PluginData.JobTags.TryGetValue(playerCharacter.ClassJob.GameData.Abbreviation, out var jobTag)) { - if (jobTag.TagPositionInChat.InheritedValue != null) + if (jobTag.TagPositionInChat.InheritedValue != null && jobTag.TargetChatTypes.InheritedValue != null && jobTag.TargetChatTypes.Value.Contains(chatType)) { var payloads = GetPayloads(jobTag, stringMatch.GameObject); if (payloads.Any()) diff --git a/PlayerTags/GeneralConverter.cs b/PlayerTags/GeneralConverter.cs deleted file mode 100644 index eca30aa..0000000 --- a/PlayerTags/GeneralConverter.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Newtonsoft.Json; -using System; - -namespace PlayerTags -{ - public class GeneralConverter : JsonConverter - { - public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) - { - if (value != null && value.GetType().IsEnum) - { - writer.WriteValue(Enum.GetName(value.GetType(), value)); - } - else - { - writer.WriteValue(value); - } - } - - public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) - { - return reader.Value; - } - - public override bool CanConvert(Type objectType) - { - return true; - } - } -} diff --git a/PlayerTags/Inheritables/InheritableBehavior.cs b/PlayerTags/Inheritables/InheritableBehavior.cs index 752c2e1..1691864 100644 --- a/PlayerTags/Inheritables/InheritableBehavior.cs +++ b/PlayerTags/Inheritables/InheritableBehavior.cs @@ -1,4 +1,7 @@ -namespace PlayerTags.Inheritables +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace PlayerTags.Inheritables { public enum InheritableBehavior { diff --git a/PlayerTags/Inheritables/InheritableData.cs b/PlayerTags/Inheritables/InheritableData.cs index 88d8d9b..9b95b85 100644 --- a/PlayerTags/Inheritables/InheritableData.cs +++ b/PlayerTags/Inheritables/InheritableData.cs @@ -12,7 +12,6 @@ namespace PlayerTags.Inheritables public InheritableBehavior Behavior; [JsonProperty("Value")] - [JsonConverter(typeof(GeneralConverter))] public object Value; } } diff --git a/PlayerTags/Inheritables/InheritableReference.cs b/PlayerTags/Inheritables/InheritableReference.cs index ea3c5e2..21bab63 100644 --- a/PlayerTags/Inheritables/InheritableReference.cs +++ b/PlayerTags/Inheritables/InheritableReference.cs @@ -1,4 +1,5 @@ using Newtonsoft.Json; +using Newtonsoft.Json.Converters; namespace PlayerTags.Inheritables { diff --git a/PlayerTags/PlayerTags.csproj b/PlayerTags/PlayerTags.csproj index b16c921..352f895 100644 --- a/PlayerTags/PlayerTags.csproj +++ b/PlayerTags/PlayerTags.csproj @@ -26,7 +26,7 @@ - + $(DalamudLibPath)FFXIVClientStructs.dll false diff --git a/PlayerTags/Plugin.cs b/PlayerTags/Plugin.cs index 97efc53..a1d6d1a 100644 --- a/PlayerTags/Plugin.cs +++ b/PlayerTags/Plugin.cs @@ -1,4 +1,5 @@ using Dalamud.Game.Command; +using Dalamud.Logging; using Dalamud.Plugin; using Dalamud.Plugin.Internal; using FFXIVClientStructs.FFXIV.Client.UI.Misc; @@ -30,7 +31,7 @@ namespace PlayerTags PluginServices.Initialize(pluginInterface); Pilz.Dalamud.PluginServices.Initialize(pluginInterface); - m_PluginConfiguration = PluginServices.DalamudPluginInterface.GetPluginConfig() as PluginConfiguration ?? new PluginConfiguration(); + m_PluginConfiguration = PluginConfiguration.LoadPluginConfig() ?? new PluginConfiguration(); m_PluginData = new PluginData(m_PluginConfiguration); m_PluginConfigurationUI = new PluginConfigurationUI(m_PluginConfiguration, m_PluginData); diff --git a/PlayerTags/Resources/Strings.Designer.cs b/PlayerTags/Resources/Strings.Designer.cs index a7e101f..7b45161 100644 --- a/PlayerTags/Resources/Strings.Designer.cs +++ b/PlayerTags/Resources/Strings.Designer.cs @@ -177,6 +177,15 @@ namespace PlayerTags.Resources { } } + /// + /// Sucht eine lokalisierte Zeichenfolge, die Advanced Chat Options ähnelt. + /// + public static string Loc_ChatFeatureCategory { + get { + return ResourceManager.GetString("Loc_ChatFeatureCategory", resourceCulture); + } + } + /// /// Sucht eine lokalisierte Zeichenfolge, die Custom id ähnelt. /// @@ -1617,6 +1626,24 @@ namespace PlayerTags.Resources { } } + /// + /// Sucht eine lokalisierte Zeichenfolge, die Target Chat Types ähnelt. + /// + public static string Loc_TargetChatTypes { + get { + return ResourceManager.GetString("Loc_TargetChatTypes", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Defines for which chat type the chat features of this tag should be enabled for. ähnelt. + /// + public static string Loc_TargetChatTypes_Description { + get { + return ResourceManager.GetString("Loc_TargetChatTypes_Description", resourceCulture); + } + } + /// /// Sucht eine lokalisierte Zeichenfolge, die Text ähnelt. /// diff --git a/PlayerTags/Resources/Strings.resx b/PlayerTags/Resources/Strings.resx index c5639a9..f5d3dda 100644 --- a/PlayerTags/Resources/Strings.resx +++ b/PlayerTags/Resources/Strings.resx @@ -657,4 +657,13 @@ The icon set to use for displaying the job icon. You can also choose the role icon set to display the role icon instead. + + Advanced Chat Options + + + Target Chat Types + + + Defines for which chat type the chat features of this tag should be enabled for. + \ No newline at end of file diff --git a/PlayerTags/packages.lock.json b/PlayerTags/packages.lock.json index d25458d..fc36ed7 100644 --- a/PlayerTags/packages.lock.json +++ b/PlayerTags/packages.lock.json @@ -14,8 +14,11 @@ "resolved": "2.1.8", "contentHash": "YqagNXs9InxmqkXzq7kLveImxnodkBEicAhydMXVp7dFjC7xb76U6zGgAax4/BWIWfZeWzr5DJyQSev31kj81A==" }, - "pilz.dalamud": { - "type": "Project" + "Pilz.Dalamud": { + "type": "Direct", + "requested": "[0.1.0, )", + "resolved": "0.1.0", + "contentHash": "n22WXrCzA+EAQHi4ve4PgTZYDzr0f0qsQ/1ApBBQjoCKJcwFox2rO692/2davRWiUQiNv9B56ikcnHocLp75tQ==" } } } From fb4ffd3f2236ede632681c4ad02c6390136e7a81 Mon Sep 17 00:00:00 2001 From: Pilzinsel64 Date: Wed, 9 Nov 2022 15:56:05 +0100 Subject: [PATCH 13/36] fix settings finally --- .../Configuration/PluginConfigurationUI.cs | 24 ++++---- PlayerTags/Data/DefaultPluginData.cs | 2 +- PlayerTags/Data/Tag.cs | 30 +++++++++- PlayerTags/Data/XivChatTypeList.cs | 56 ------------------- .../Inheritables/InheritableReference.cs | 1 + 5 files changed, 44 insertions(+), 69 deletions(-) delete mode 100644 PlayerTags/Data/XivChatTypeList.cs diff --git a/PlayerTags/Configuration/PluginConfigurationUI.cs b/PlayerTags/Configuration/PluginConfigurationUI.cs index 9f09e0f..aba314f 100644 --- a/PlayerTags/Configuration/PluginConfigurationUI.cs +++ b/PlayerTags/Configuration/PluginConfigurationUI.cs @@ -703,7 +703,7 @@ namespace PlayerTags.Configuration { DrawInheritable(selectedInheritable.Inheritable.Key, false, false, inheritableJobIconSetName); } - else if (selectedInheritable.Inheritable.Value is InheritableReference> inheritableXivChatType) + else if (selectedInheritable.Inheritable.Value is InheritableReference> inheritableXivChatType) { DrawMultiselect(selectedInheritable.Inheritable.Key, inheritableXivChatType); } @@ -1068,10 +1068,10 @@ namespace PlayerTags.Configuration ImGui.TextColored(new Vector4(0.7f, 0.6f, 1f, 1f), label); } - private void DrawMultiselect(string localizedStringName, InheritableReference> inheritable) where TEnum : Enum + private void DrawMultiselect(string localizedStringName, InheritableReference> inheritable) where TEnum : Enum { bool isDisabled = inheritable.Behavior == InheritableBehavior.Inherit; - EnumList proxyKey = isDisabled ? inheritable.InheritedValue : inheritable.Value; + List proxyKey = isDisabled ? inheritable.InheritedValue : inheritable.Value; if (isDisabled) proxyKey = inheritable.InheritedValue; @@ -1112,15 +1112,19 @@ namespace PlayerTags.Configuration { var entryName = Enum.GetName(typeofEnum, entry.Value); var tempval = entry.Enabled; + isClicked = ImGui.Checkbox(Localizer.GetString(entryName), ref isDisabled ? ref tempval : ref entry.Enabled); + if (ImGui.IsItemHovered()) ImGui.SetTooltip(Localizer.GetString(entryName, true)); - } - if (!isDisabled && isClicked) - { - proxy.ApplyTo(proxyKey); - SaveSettings(); + if (isClicked && !isDisabled) + { + var newList = proxyKey.ToList(); + proxy.ApplyTo(newList); + inheritable.Value = newList; + SaveSettings(); + } } } @@ -1353,13 +1357,13 @@ namespace PlayerTags.Configuration { public List Entries { get; } = new(); - public EnumMultiselectProxy(EnumList target) + public EnumMultiselectProxy(List target) { foreach (TEnum value in Enum.GetValues(typeof(TEnum))) Entries.Add(new(value, target.Contains(value))); } - public void ApplyTo(EnumList target) + public void ApplyTo(List target) { foreach (var entry in Entries) { diff --git a/PlayerTags/Data/DefaultPluginData.cs b/PlayerTags/Data/DefaultPluginData.cs index 71a43ea..1ee8fb9 100644 --- a/PlayerTags/Data/DefaultPluginData.cs +++ b/PlayerTags/Data/DefaultPluginData.cs @@ -47,7 +47,7 @@ namespace PlayerTags.Data IsVisibleForEnemyPlayers = true, IsVisibleForOtherPlayers = true, - TargetChatTypes = new EnumList(Enum.GetValues()), + TargetChatTypes = new List(Enum.GetValues()), }; AllRoleTags = new Tag() diff --git a/PlayerTags/Data/Tag.cs b/PlayerTags/Data/Tag.cs index 6338078..a168890 100644 --- a/PlayerTags/Data/Tag.cs +++ b/PlayerTags/Data/Tag.cs @@ -8,6 +8,7 @@ using PlayerTags.PluginStrings; using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; namespace PlayerTags.Data { @@ -175,7 +176,7 @@ namespace PlayerTags.Data public InheritableValue IsVisibleForOtherPlayers = new InheritableValue(false); [InheritableCategory("ChatFeatureCategory")] - public InheritableReference> TargetChatTypes = new(new EnumList(Enum.GetValues())); + public InheritableReference> TargetChatTypes = new(new List(Enum.GetValues())); [JsonIgnore] public string[] IdentitiesToAddTo @@ -228,7 +229,7 @@ namespace PlayerTags.Data { var inheritableData = inheritable.GetData(); if (inheritableData.Behavior != defaultInheritableData.Behavior || - !inheritableData.Value.Equals(defaultInheritableData.Value)) + !EqualsInheritableData(inheritableData, defaultInheritableData)) { changes[name] = inheritable.GetData(); } @@ -243,6 +244,31 @@ namespace PlayerTags.Data return changes; } + private static bool EqualsInheritableData(InheritableData data1, InheritableData data2) + { + if (data1.Value is List) + return EqualsInheritableDataListXivChatType(data1, data2); + else + return data1.Value.Equals(data2.Value); + } + + private static bool EqualsInheritableDataListXivChatType(InheritableData data1, InheritableData data2) + { + var list1 = data1.Value as List; + var list2 = data2.Value as List; + + if (list1 is null || list2 is null || list1.Count != list2.Count) + return false; + + for (int i = 0; i < list1.Count; i++) + { + if (!list1[i].Equals(list2[i])) + return false; + } + + return true; + } + public void SetChanges(IEnumerable> changes) { foreach ((var name, var inheritableData) in changes) diff --git a/PlayerTags/Data/XivChatTypeList.cs b/PlayerTags/Data/XivChatTypeList.cs deleted file mode 100644 index e05fdb8..0000000 --- a/PlayerTags/Data/XivChatTypeList.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace PlayerTags.Data -{ - public class EnumList : List where TEnum : Enum - { - public EnumList() : base() - { - } - - public EnumList(IEnumerable collection) : base(collection) - { - } - - //// this is first one '==' - //public static bool operator ==(EnumList obj1, EnumList obj2) - //{ - - //} - - //// this is second one '!=' - //public static bool operator !=(EnumList obj1, EnumList obj2) - //{ - // return !(obj1 == obj2); - //} - - public override bool Equals(object? obj) - { - var obj1 = this; - var obj2 = obj as EnumList; - - if (obj1 is not null && obj2 is not null) - { - if (obj1.Count != obj2.Count) - return false; - - for (int i = 0; i < obj1.Count; i++) - { - if (!obj1[i]?.Equals(obj2[i]) ?? true) - return false; - } - } - - return true; - } - - public override int GetHashCode() - { - return base.GetHashCode(); - } - } -} diff --git a/PlayerTags/Inheritables/InheritableReference.cs b/PlayerTags/Inheritables/InheritableReference.cs index 21bab63..cabcf62 100644 --- a/PlayerTags/Inheritables/InheritableReference.cs +++ b/PlayerTags/Inheritables/InheritableReference.cs @@ -1,5 +1,6 @@ using Newtonsoft.Json; using Newtonsoft.Json.Converters; +using System.Collections.Generic; namespace PlayerTags.Inheritables { From 5ee2ad66292bda05fe9b4c0e49b37563c76b878d Mon Sep 17 00:00:00 2001 From: Pilzinsel64 Date: Thu, 10 Nov 2022 09:59:09 +0100 Subject: [PATCH 14/36] add settings ui for status icon priorizer settings --- .../Configuration/PluginConfigurationUI.cs | 75 +++++ PlayerTags/Resources/Strings.Designer.cs | 262 ++++++++++++++++++ PlayerTags/Resources/Strings.resx | 88 ++++++ 3 files changed, 425 insertions(+) diff --git a/PlayerTags/Configuration/PluginConfigurationUI.cs b/PlayerTags/Configuration/PluginConfigurationUI.cs index aba314f..7c67cb8 100644 --- a/PlayerTags/Configuration/PluginConfigurationUI.cs +++ b/PlayerTags/Configuration/PluginConfigurationUI.cs @@ -10,6 +10,8 @@ using ImGuiNET; using Lumina.Excel.GeneratedSheets; using Pilz.Dalamud.ActivityContexts; using Pilz.Dalamud.Icons; +using Pilz.Dalamud.Nameplates.Model; +using Pilz.Dalamud.Nameplates.Tools; using PlayerTags.Data; using PlayerTags.Inheritables; using PlayerTags.PluginStrings; @@ -246,6 +248,79 @@ namespace PlayerTags.Configuration ImGui.EndTabItem(); } + if (ImGui.BeginTabItem(Strings.Loc_Static_StatusIconPrioList)) + { + ImGui.Spacing(); + ImGui.Spacing(); + + var isPriorizerEnabled = m_PluginConfiguration.StatusIconPriorizerSettings.UsePriorizedIcons; + DrawCheckbox(nameof(StatusIconPriorizerSettings.UsePriorizedIcons), true, ref isPriorizerEnabled, () => + { + m_PluginConfiguration.StatusIconPriorizerSettings.UsePriorizedIcons = isPriorizerEnabled; + SaveSettings(); + }); + + if (isPriorizerEnabled) + { + var statusIcons = Enum.GetValues(); + + ImGui.Spacing(); + ImGui.Spacing(); + + if (ImGui.Button(Strings.Loc_StatusIconPriorizer_ResetToDefault)) + { + m_PluginConfiguration.StatusIconPriorizerSettings.ResetToDefault(); + SaveSettings(); + } + else if (ImGui.IsItemHovered()) + ImGui.SetTooltip(Strings.Loc_StatusIconPriorizer_ResetToDefault_Description); + + ImGui.SameLine(); + + if (ImGui.Button(Strings.Loc_StatusIconPriorizer_ResetToEmpty)) + { + m_PluginConfiguration.StatusIconPriorizerSettings.ResetToEmpty(); + SaveSettings(); + } + else if (ImGui.IsItemHovered()) + ImGui.SetTooltip(Strings.Loc_StatusIconPriorizer_ResetToEmpty_Description); + + ImGui.Spacing(); + ImGui.Spacing(); + + foreach (var conditionSetName in Enum.GetValues()) + { + if (ImGui.CollapsingHeader(Localizer.GetString(conditionSetName, false))) + { + var conditionSet = m_PluginConfiguration.StatusIconPriorizerSettings.GetConditionSet(conditionSetName); + + if (ImGui.IsItemHovered()) + ImGui.SetTooltip(Localizer.GetString(conditionSetName, true)); + + foreach (var statusIcon in statusIcons) + { + var isChecked = conditionSet.Contains(statusIcon); + DrawCheckbox(Localizer.GetName(statusIcon), true, ref isChecked, () => + { + if (isChecked) + { + if (!conditionSet.Contains(statusIcon)) + conditionSet.Add(statusIcon); + } + else if (conditionSet.Contains(statusIcon)) + conditionSet.Remove(statusIcon); + SaveSettings(); + }); + } + } + + ImGui.Spacing(); + } + } + + ImGui.EndTabItem(); + } + ImGui.EndTabBar(); } diff --git a/PlayerTags/Resources/Strings.Designer.cs b/PlayerTags/Resources/Strings.Designer.cs index 7b45161..b09f035 100644 --- a/PlayerTags/Resources/Strings.Designer.cs +++ b/PlayerTags/Resources/Strings.Designer.cs @@ -1491,6 +1491,15 @@ namespace PlayerTags.Resources { } } + /// + /// Sucht eine lokalisierte Zeichenfolge, die Status Icon Priorizer ähnelt. + /// + public static string Loc_Static_StatusIconPrioList { + get { + return ResourceManager.GetString("Loc_Static_StatusIconPrioList", resourceCulture); + } + } + /// /// Sucht eine lokalisierte Zeichenfolge, die Tagged Players ähnelt. /// @@ -1518,6 +1527,240 @@ namespace PlayerTags.Resources { } } + /// + /// Sucht eine lokalisierte Zeichenfolge, die Reset to default ähnelt. + /// + public static string Loc_StatusIconPriorizer_ResetToDefault { + get { + return ResourceManager.GetString("Loc_StatusIconPriorizer_ResetToDefault", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Resets all condition sets to the default settings ähnelt. + /// + public static string Loc_StatusIconPriorizer_ResetToDefault_Description { + get { + return ResourceManager.GetString("Loc_StatusIconPriorizer_ResetToDefault_Description", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Reset to empty ähnelt. + /// + public static string Loc_StatusIconPriorizer_ResetToEmpty { + get { + return ResourceManager.GetString("Loc_StatusIconPriorizer_ResetToEmpty", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Clears all condition sets to an empty collection. None status icons will be priorized. ähnelt. + /// + public static string Loc_StatusIconPriorizer_ResetToEmpty_Description { + get { + return ResourceManager.GetString("Loc_StatusIconPriorizer_ResetToEmpty_Description", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die In Duty ähnelt. + /// + public static string Loc_StatusIconPriorizerConditionSets_InDuty { + get { + return ResourceManager.GetString("Loc_StatusIconPriorizerConditionSets_InDuty", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Status icons that should get priorized within duties. ähnelt. + /// + public static string Loc_StatusIconPriorizerConditionSets_InDuty_Description { + get { + return ResourceManager.GetString("Loc_StatusIconPriorizerConditionSets_InDuty_Description", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die In Foray ähnelt. + /// + public static string Loc_StatusIconPriorizerConditionSets_InForay { + get { + return ResourceManager.GetString("Loc_StatusIconPriorizerConditionSets_InForay", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Status icons that should get priorized within foraies. ähnelt. + /// + public static string Loc_StatusIconPriorizerConditionSets_InForay_Description { + get { + return ResourceManager.GetString("Loc_StatusIconPriorizerConditionSets_InForay_Description", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Overworld ähnelt. + /// + public static string Loc_StatusIconPriorizerConditionSets_Overworld { + get { + return ResourceManager.GetString("Loc_StatusIconPriorizerConditionSets_Overworld", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Status icons that should get priorized at overworld. ähnelt. + /// + public static string Loc_StatusIconPriorizerConditionSets_Overworld_Description { + get { + return ResourceManager.GetString("Loc_StatusIconPriorizerConditionSets_Overworld_Description", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Busy ähnelt. + /// + public static string Loc_StatusIcons_Busy { + get { + return ResourceManager.GetString("Loc_StatusIcons_Busy", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Disconnecting ähnelt. + /// + public static string Loc_StatusIcons_Disconnecting { + get { + return ResourceManager.GetString("Loc_StatusIcons_Disconnecting", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Duty Finder ähnelt. + /// + public static string Loc_StatusIcons_DutyFinder { + get { + return ResourceManager.GetString("Loc_StatusIcons_DutyFinder", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Group Pose ähnelt. + /// + public static string Loc_StatusIcons_GroupPose { + get { + return ResourceManager.GetString("Loc_StatusIcons_GroupPose", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Idle ähnelt. + /// + public static string Loc_StatusIcons_Idle { + get { + return ResourceManager.GetString("Loc_StatusIcons_Idle", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die In Duty ähnelt. + /// + public static string Loc_StatusIcons_InDuty { + get { + return ResourceManager.GetString("Loc_StatusIcons_InDuty", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Mentor ähnelt. + /// + public static string Loc_StatusIcons_Mentor { + get { + return ResourceManager.GetString("Loc_StatusIcons_Mentor", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Mentor Crafting ähnelt. + /// + public static string Loc_StatusIcons_MentorCrafting { + get { + return ResourceManager.GetString("Loc_StatusIcons_MentorCrafting", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Mentor PvE ähnelt. + /// + public static string Loc_StatusIcons_MentorPvE { + get { + return ResourceManager.GetString("Loc_StatusIcons_MentorPvE", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Mentor PvP ähnelt. + /// + public static string Loc_StatusIcons_MentorPvP { + get { + return ResourceManager.GetString("Loc_StatusIcons_MentorPvP", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die New Adventurer ähnelt. + /// + public static string Loc_StatusIcons_NewAdventurer { + get { + return ResourceManager.GetString("Loc_StatusIcons_NewAdventurer", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Party Leader ähnelt. + /// + public static string Loc_StatusIcons_PartyLeader { + get { + return ResourceManager.GetString("Loc_StatusIcons_PartyLeader", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Party Member ähnelt. + /// + public static string Loc_StatusIcons_PartyMember { + get { + return ResourceManager.GetString("Loc_StatusIcons_PartyMember", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Returner ähnelt. + /// + public static string Loc_StatusIcons_Returner { + get { + return ResourceManager.GetString("Loc_StatusIcons_Returner", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Role Playing ähnelt. + /// + public static string Loc_StatusIcons_RolePlaying { + get { + return ResourceManager.GetString("Loc_StatusIcons_RolePlaying", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Viewing Cutscene ähnelt. + /// + public static string Loc_StatusIcons_ViewingCutscene { + get { + return ResourceManager.GetString("Loc_StatusIcons_ViewingCutscene", resourceCulture); + } + } + /// /// Sucht eine lokalisierte Zeichenfolge, die After ähnelt. /// @@ -1706,5 +1949,24 @@ namespace PlayerTags.Resources { return ResourceManager.GetString("Loc_TextGlowColor_Description", resourceCulture); } } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Use priorized icons ähnelt. + /// + public static string Loc_UsePriorizedIcons { + get { + return ResourceManager.GetString("Loc_UsePriorizedIcons", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die This option checked will force a set of status icons to be priorized over job icons, if you enabled job icons. + ///When disabled, only the disconnected status icon is priorized. ähnelt. + /// + public static string Loc_UsePriorizedIcons_Description { + get { + return ResourceManager.GetString("Loc_UsePriorizedIcons_Description", resourceCulture); + } + } } } diff --git a/PlayerTags/Resources/Strings.resx b/PlayerTags/Resources/Strings.resx index f5d3dda..9fed405 100644 --- a/PlayerTags/Resources/Strings.resx +++ b/PlayerTags/Resources/Strings.resx @@ -666,4 +666,92 @@ Defines for which chat type the chat features of this tag should be enabled for. + + Status Icon Priorizer + + + In Duty + + + Status icons that should get priorized within duties. + + + In Foray + + + Status icons that should get priorized within foraies. + + + Overworld + + + Status icons that should get priorized at overworld. + + + Reset to default + + + Resets all condition sets to the default settings + + + Reset to empty + + + Clears all condition sets to an empty collection. None status icons will be priorized. + + + Use priorized icons + + + This option checked will force a set of status icons to be priorized over job icons, if you enabled job icons. +When disabled, only the disconnected status icon is priorized. + + + Busy + + + Disconnecting + + + Duty Finder + + + Group Pose + + + Idle + + + In Duty + + + Mentor + + + Mentor Crafting + + + Mentor PvE + + + Mentor PvP + + + New Adventurer + + + Party Leader + + + Party Member + + + Returner + + + Role Playing + + + Viewing Cutscene + \ No newline at end of file From 1d69893890d1bd632daf1699dead5ebf7e4b6aa3 Mon Sep 17 00:00:00 2001 From: Pilzinsel64 Date: Thu, 10 Nov 2022 10:30:19 +0100 Subject: [PATCH 15/36] implement Default Plugin Data Templates --- .../Configuration/PluginConfiguration.cs | 1 + PlayerTags/Data/DefaultPluginData.cs | 375 ++++++++++++++++-- PlayerTags/Data/DefaultPluginDataTemplate.cs | 16 + PlayerTags/Data/PluginData.cs | 2 +- 4 files changed, 368 insertions(+), 26 deletions(-) create mode 100644 PlayerTags/Data/DefaultPluginDataTemplate.cs diff --git a/PlayerTags/Configuration/PluginConfiguration.cs b/PlayerTags/Configuration/PluginConfiguration.cs index 6d3a2a9..5340178 100644 --- a/PlayerTags/Configuration/PluginConfiguration.cs +++ b/PlayerTags/Configuration/PluginConfiguration.cs @@ -32,6 +32,7 @@ namespace PlayerTags.Configuration { ActivityType.PvpDuty, new GeneralOptionsClass() } }; + public DefaultPluginDataTemplate DefaultPluginDataTemplate = DefaultPluginDataTemplate.Simple; public StatusIconPriorizerSettings StatusIconPriorizerSettings = new(true); public bool IsPlayerNameRandomlyGenerated = false; public bool IsCustomTagsContextMenuEnabled = true; diff --git a/PlayerTags/Data/DefaultPluginData.cs b/PlayerTags/Data/DefaultPluginData.cs index 1ee8fb9..e7365ba 100644 --- a/PlayerTags/Data/DefaultPluginData.cs +++ b/PlayerTags/Data/DefaultPluginData.cs @@ -9,18 +9,232 @@ namespace PlayerTags.Data { public class DefaultPluginData { - public Tag AllTags { get; } + public Tag AllTags { get; private set; } - public Tag AllRoleTags { get; } - public Dictionary RoleTags { get; } - public Dictionary DpsRoleTags { get; } - public Dictionary RangedDpsRoleTags { get; } - public Dictionary LandHandRoleTags { get; } - public Dictionary JobTags { get; } + public Tag AllRoleTags { get; private set; } + public Dictionary RoleTags { get; private set; } + public Dictionary DpsRoleTags { get; private set; } + public Dictionary RangedDpsRoleTags { get; private set; } + public Dictionary LandHandRoleTags { get; private set; } + public Dictionary JobTags { get; private set; } - public Tag AllCustomTags { get; } + public Tag AllCustomTags { get; private set; } - public DefaultPluginData() + public DefaultPluginData(DefaultPluginDataTemplate template) + { + SetupTemplate(template); + } + + private void SetupTemplate(DefaultPluginDataTemplate template) + { + Clear(); + + switch(template) + { + case DefaultPluginDataTemplate.None: + SetupTemplateNone(); + break; + case DefaultPluginDataTemplate.Basic: + SetupTemplateBasic(); + break; + case DefaultPluginDataTemplate.Simple: + SetupTemplateSimple(); + break; + case DefaultPluginDataTemplate.Full: + SetupTemplateFull(); + break; + } + + SetupJobTags(); + } + + private void Clear() + { + + RoleTags = new Dictionary(); + DpsRoleTags = new Dictionary(); + RangedDpsRoleTags = new Dictionary(); + LandHandRoleTags = new Dictionary(); + } + + private void SetupTemplateNone() + { + AllTags = new Tag() + { + IsSelected = true, + IsExpanded = true, + }; + + AllRoleTags = new Tag() + { + IsSelected = false, + IsExpanded = true, + }; + + RoleTags[Role.LandHand] = new Tag() + { + IsSelected = false, + IsExpanded = false, + }; + + RoleTags[Role.Tank] = new Tag() + { + IsSelected = false, + IsExpanded = false, + }; + + RoleTags[Role.Healer] = new Tag() + { + IsSelected = false, + IsExpanded = false, + }; + + RoleTags[Role.Dps] = new Tag() + { + IsSelected = false, + IsExpanded = true, + }; + + DpsRoleTags[DpsRole.Melee] = new Tag() + { + IsSelected = false, + IsExpanded = false, + }; + + DpsRoleTags[DpsRole.Ranged] = new Tag() + { + IsSelected = false, + IsExpanded = true, + }; + + RangedDpsRoleTags[RangedDpsRole.Magical] = new Tag() + { + IsSelected = false, + IsExpanded = false, + }; + + RangedDpsRoleTags[RangedDpsRole.Physical] = new Tag() + { + IsSelected = false, + IsExpanded = false, + }; + + LandHandRoleTags[LandHandRole.Land] = new Tag() + { + IsSelected = false, + IsExpanded = false, + }; + + LandHandRoleTags[LandHandRole.Hand] = new Tag() + { + IsSelected = false, + IsExpanded = false, + }; + + AllCustomTags = new Tag() + { + IsSelected = false, + IsExpanded = true, + }; + } + + private void SetupTemplateBasic() + { + AllTags = new Tag() + { + IsSelected = true, + IsExpanded = true, + + TagPositionInChat = TagPosition.Before, + InsertBehindNumberPrefixInChat = true, + TagPositionInNameplates = TagPosition.Replace, + TagTargetInNameplates = NameplateElement.Title, + + TargetChatTypes = new List(Enum.GetValues()), + }; + + AllRoleTags = new Tag() + { + IsSelected = false, + IsExpanded = true, + }; + + RoleTags[Role.LandHand] = new Tag() + { + IsSelected = false, + IsExpanded = false + }; + + RoleTags[Role.Tank] = new Tag() + { + IsSelected = false, + IsExpanded = false, + Icon = BitmapFontIcon.Tank, + TextColor = 546, + }; + + RoleTags[Role.Healer] = new Tag() + { + IsSelected = false, + IsExpanded = false, + Icon = BitmapFontIcon.Healer, + TextColor = 43, + }; + + RoleTags[Role.Dps] = new Tag() + { + IsSelected = false, + IsExpanded = true, + Icon = BitmapFontIcon.DPS, + TextColor = 508, + }; + + DpsRoleTags[DpsRole.Melee] = new Tag() + { + IsSelected = false, + IsExpanded = false, + }; + + DpsRoleTags[DpsRole.Ranged] = new Tag() + { + IsSelected = false, + IsExpanded = true, + }; + + RangedDpsRoleTags[RangedDpsRole.Magical] = new Tag() + { + IsSelected = false, + IsExpanded = false, + }; + + RangedDpsRoleTags[RangedDpsRole.Physical] = new Tag() + { + IsSelected = false, + IsExpanded = false, + }; + + LandHandRoleTags[LandHandRole.Land] = new Tag() + { + IsSelected = false, + IsExpanded = false, + }; + + LandHandRoleTags[LandHandRole.Hand] = new Tag() + { + IsSelected = false, + IsExpanded = false, + }; + + AllCustomTags = new Tag() + { + IsSelected = false, + IsExpanded = true, + IsTextVisibleInChat = true, + IsTextVisibleInNameplates = true, + }; + } + + private void SetupTemplateSimple() { AllTags = new Tag() { @@ -36,10 +250,6 @@ namespace PlayerTags.Data IsVisibleInPveDuties = true, IsVisibleInPvpDuties = true, - //NameplateFreeCompanyVisibility = NameplateFreeCompanyVisibility.Never, - //NameplateTitleVisibility = NameplateTitleVisibility.Always, - //NameplateTitlePosition = NameplateTitlePosition.AlwaysAboveName, - IsVisibleForSelf = true, IsVisibleForFriendPlayers = true, IsVisibleForPartyPlayers = true, @@ -61,7 +271,6 @@ namespace PlayerTags.Data IsTextColorAppliedToChatName = true, }; - RoleTags = new Dictionary(); RoleTags[Role.LandHand] = new Tag() { IsSelected = false, @@ -94,7 +303,6 @@ namespace PlayerTags.Data TextColor = 508, }; - DpsRoleTags = new Dictionary(); DpsRoleTags[DpsRole.Melee] = new Tag() { IsSelected = false, @@ -107,7 +315,6 @@ namespace PlayerTags.Data IsExpanded = true, }; - RangedDpsRoleTags = new Dictionary(); RangedDpsRoleTags[RangedDpsRole.Magical] = new Tag() { IsSelected = false, @@ -120,7 +327,6 @@ namespace PlayerTags.Data IsExpanded = false, }; - LandHandRoleTags = new Dictionary(); LandHandRoleTags[LandHandRole.Land] = new Tag() { IsSelected = false, @@ -133,6 +339,133 @@ namespace PlayerTags.Data IsExpanded = false, }; + AllCustomTags = new Tag() + { + IsSelected = false, + IsExpanded = true, + IsTextVisibleInChat = true, + IsTextVisibleInNameplates = true, + }; + } + + private void SetupTemplateFull() + { + AllTags = new Tag() + { + IsSelected = true, + IsExpanded = true, + TagPositionInChat = TagPosition.Before, + InsertBehindNumberPrefixInChat = true, + TagPositionInNameplates = TagPosition.Replace, + TagTargetInNameplates = NameplateElement.Title, + IsTextItalic = true, + + IsVisibleInOverworld = true, + IsVisibleInPveDuties = true, + IsVisibleInPvpDuties = true, + + IsVisibleForSelf = true, + IsVisibleForFriendPlayers = true, + IsVisibleForPartyPlayers = true, + IsVisibleForAlliancePlayers = true, + IsVisibleForEnemyPlayers = true, + IsVisibleForOtherPlayers = true, + + TargetChatTypes = new List(Enum.GetValues()), + }; + + AllRoleTags = new Tag() + { + IsSelected = false, + IsExpanded = true, + IsRoleIconVisibleInChat = true, + IsTextVisibleInChat = true, + IsRoleIconVisibleInNameplates = true, + IsTextVisibleInNameplates = true, + IsTextColorAppliedToNameplateName = true, + IsTextColorAppliedToChatName = true, + IsJobIconVisibleInNameplates = true, + }; + + RoleTags[Role.LandHand] = new Tag() + { + IsSelected = false, + IsExpanded = false, + Icon = BitmapFontIcon.Crafter, + TextColor = 3, + }; + + RoleTags[Role.Tank] = new Tag() + { + IsSelected = false, + IsExpanded = false, + Icon = BitmapFontIcon.Tank, + TextColor = 546, + }; + + RoleTags[Role.Healer] = new Tag() + { + IsSelected = false, + IsExpanded = false, + Icon = BitmapFontIcon.Healer, + TextColor = 43, + }; + + RoleTags[Role.Dps] = new Tag() + { + IsSelected = false, + IsExpanded = true, + Icon = BitmapFontIcon.DPS, + TextColor = 508, + }; + + DpsRoleTags[DpsRole.Melee] = new Tag() + { + IsSelected = false, + IsExpanded = false, + }; + + DpsRoleTags[DpsRole.Ranged] = new Tag() + { + IsSelected = false, + IsExpanded = true, + }; + + RangedDpsRoleTags[RangedDpsRole.Magical] = new Tag() + { + IsSelected = false, + IsExpanded = false, + }; + + RangedDpsRoleTags[RangedDpsRole.Physical] = new Tag() + { + IsSelected = false, + IsExpanded = false, + }; + + LandHandRoleTags[LandHandRole.Land] = new Tag() + { + IsSelected = false, + IsExpanded = false, + }; + + LandHandRoleTags[LandHandRole.Hand] = new Tag() + { + IsSelected = false, + IsExpanded = false, + }; + + AllCustomTags = new Tag() + { + IsSelected = false, + IsExpanded = true, + IsTextVisibleInChat = true, + IsTextVisibleInNameplates = true, + }; + } + + private void SetupJobTags() + { JobTags = new Dictionary(); var classJobs = PluginServices.DataManager.GetExcelSheet(); @@ -154,14 +487,6 @@ namespace PlayerTags.Data } } } - - AllCustomTags = new Tag() - { - IsSelected = false, - IsExpanded = true, - IsTextVisibleInChat = true, - IsTextVisibleInNameplates = true, - }; } } } diff --git a/PlayerTags/Data/DefaultPluginDataTemplate.cs b/PlayerTags/Data/DefaultPluginDataTemplate.cs new file mode 100644 index 0000000..98211ea --- /dev/null +++ b/PlayerTags/Data/DefaultPluginDataTemplate.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PlayerTags.Data +{ + public enum DefaultPluginDataTemplate + { + None, + Basic, + Simple, + Full + } +} diff --git a/PlayerTags/Data/PluginData.cs b/PlayerTags/Data/PluginData.cs index 50df87d..fb45206 100644 --- a/PlayerTags/Data/PluginData.cs +++ b/PlayerTags/Data/PluginData.cs @@ -31,7 +31,7 @@ namespace PlayerTags.Data { m_PluginConfiguration = pluginConfiguration; - Default = new DefaultPluginData(); + Default = new DefaultPluginData(pluginConfiguration.DefaultPluginDataTemplate); // Set the default changes and saved changes AllTags = new Tag(new LocalizedPluginString(nameof(AllTags)), Default.AllTags); From c8a3f752f40ec2b9bc554fec41363159b0c77a90 Mon Sep 17 00:00:00 2001 From: Pilzinsel64 Date: Thu, 10 Nov 2022 12:34:18 +0100 Subject: [PATCH 16/36] implement UI for default plugin data template --- .../Configuration/PluginConfigurationUI.cs | 24 +++-- PlayerTags/Data/PluginData.cs | 10 +- PlayerTags/Resources/Strings.Designer.cs | 98 +++++++++++++++++++ PlayerTags/Resources/Strings.resx | 38 +++++++ 4 files changed, 160 insertions(+), 10 deletions(-) diff --git a/PlayerTags/Configuration/PluginConfigurationUI.cs b/PlayerTags/Configuration/PluginConfigurationUI.cs index 7c67cb8..7dfa20a 100644 --- a/PlayerTags/Configuration/PluginConfigurationUI.cs +++ b/PlayerTags/Configuration/PluginConfigurationUI.cs @@ -107,6 +107,11 @@ namespace PlayerTags.Configuration { ImGui.Spacing(); ImGui.Spacing(); + DrawComboBox(true, true, false, ref m_PluginConfiguration.DefaultPluginDataTemplate, () => + { + m_PluginData.ReloadDefault(); + SaveSettings(); + }, true, true); DrawCheckbox(nameof(m_PluginConfiguration.IsShowInheritedPropertiesEnabled), true, ref m_PluginConfiguration.IsShowInheritedPropertiesEnabled, () => SaveSettings()); ImGui.BeginGroup(); ImGui.Columns(2); @@ -272,7 +277,7 @@ namespace PlayerTags.Configuration m_PluginConfiguration.StatusIconPriorizerSettings.ResetToDefault(); SaveSettings(); } - else if (ImGui.IsItemHovered()) + if (ImGui.IsItemHovered()) ImGui.SetTooltip(Strings.Loc_StatusIconPriorizer_ResetToDefault_Description); ImGui.SameLine(); @@ -282,7 +287,7 @@ namespace PlayerTags.Configuration m_PluginConfiguration.StatusIconPriorizerSettings.ResetToEmpty(); SaveSettings(); } - else if (ImGui.IsItemHovered()) + if (ImGui.IsItemHovered()) ImGui.SetTooltip(Strings.Loc_StatusIconPriorizer_ResetToEmpty_Description); ImGui.Spacing(); @@ -294,9 +299,6 @@ namespace PlayerTags.Configuration { var conditionSet = m_PluginConfiguration.StatusIconPriorizerSettings.GetConditionSet(conditionSetName); - if (ImGui.IsItemHovered()) - ImGui.SetTooltip(Localizer.GetString(conditionSetName, true)); - foreach (var statusIcon in statusIcons) { var isChecked = conditionSet.Contains(statusIcon); @@ -314,6 +316,9 @@ namespace PlayerTags.Configuration } } + if (ImGui.IsItemHovered()) + ImGui.SetTooltip(Localizer.GetString(conditionSetName, true)); + ImGui.Spacing(); } } @@ -1207,12 +1212,14 @@ namespace PlayerTags.Configuration ImGui.PopStyleVar(); } - private void DrawComboBox(bool isLabelVisible, bool shouldLocalizeNames, bool shouldOrderNames, ref TEnum currentValue, System.Action changed) + private void DrawComboBox(bool isLabelVisible, bool shouldLocalizeNames, bool shouldOrderNames, ref TEnum currentValue, System.Action changed, bool showToolTipToLabel = false, bool showLabelInSameLine = false) where TEnum : Enum { if (isLabelVisible) { ImGui.Text(Localizer.GetString(false)); + if (showLabelInSameLine) + ImGui.SameLine(); } var currentDisplayName = shouldLocalizeNames ? Localizer.GetString(currentValue, false) : currentValue.ToString(); @@ -1253,7 +1260,10 @@ namespace PlayerTags.Configuration if (ImGui.IsItemHovered() && shouldLocalizeNames) { - ImGui.SetTooltip(Localizer.GetString(currentValue, true)); + if (showToolTipToLabel) + ImGui.SetTooltip(Localizer.GetString(typeof(TEnum).Name, true)); + else + ImGui.SetTooltip(Localizer.GetString(currentValue, true)); } } diff --git a/PlayerTags/Data/PluginData.cs b/PlayerTags/Data/PluginData.cs index fb45206..c8b5481 100644 --- a/PlayerTags/Data/PluginData.cs +++ b/PlayerTags/Data/PluginData.cs @@ -25,12 +25,16 @@ namespace PlayerTags.Data public List CustomTags; public List Identities; - private PluginConfiguration m_PluginConfiguration; + private PluginConfiguration pluginConfiguration; public PluginData(PluginConfiguration pluginConfiguration) { - m_PluginConfiguration = pluginConfiguration; + this.pluginConfiguration = pluginConfiguration; + ReloadDefault(); + } + public void ReloadDefault() + { Default = new DefaultPluginData(pluginConfiguration.DefaultPluginDataTemplate); // Set the default changes and saved changes @@ -261,7 +265,7 @@ namespace PlayerTags.Data if (identity.WorldId == null && worldId != null) { identity.WorldId = worldId; - m_PluginConfiguration.Save(this); + pluginConfiguration.Save(this); return identity; } diff --git a/PlayerTags/Resources/Strings.Designer.cs b/PlayerTags/Resources/Strings.Designer.cs index b09f035..b2f08bf 100644 --- a/PlayerTags/Resources/Strings.Designer.cs +++ b/PlayerTags/Resources/Strings.Designer.cs @@ -204,6 +204,104 @@ namespace PlayerTags.Resources { } } + /// + /// Sucht eine lokalisierte Zeichenfolge, die Template ähnelt. + /// + public static string Loc_DefaultPluginDataTemplate { + get { + return ResourceManager.GetString("Loc_DefaultPluginDataTemplate", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Basic ähnelt. + /// + public static string Loc_DefaultPluginDataTemplate_Basic { + get { + return ResourceManager.GetString("Loc_DefaultPluginDataTemplate_Basic", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Same as Empty, but includes a very basic pre-confiuration for formatting and coloring. + ///Can also be used if you want to make your own cofiguration but want basic formatting. ähnelt. + /// + public static string Loc_DefaultPluginDataTemplate_Basic_Description { + get { + return ResourceManager.GetString("Loc_DefaultPluginDataTemplate_Basic_Description", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Here you can choose the template for all the settings below. + ///The template is used as basic set of properties. Every change you are making is what get saved. But all options from this template will not be saved. + ///This helpes you by not needing to overwrite so much properties - or by doing your completely own configuration without a template. + /// + ///Warning: + ///Changing this can cause properties to be resetted. You may loose a part of your configuration. + ///After you changed this, ensure everything is setted up how [Rest der Zeichenfolge wurde abgeschnitten]"; ähnelt. + /// + public static string Loc_DefaultPluginDataTemplate_Description { + get { + return ResourceManager.GetString("Loc_DefaultPluginDataTemplate_Description", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Full ähnelt. + /// + public static string Loc_DefaultPluginDataTemplate_Full { + get { + return ResourceManager.GetString("Loc_DefaultPluginDataTemplate_Full", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Show the job tag with color and also color the player name element. ähnelt. + /// + public static string Loc_DefaultPluginDataTemplate_Full_Description { + get { + return ResourceManager.GetString("Loc_DefaultPluginDataTemplate_Full_Description", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Empty ähnelt. + /// + public static string Loc_DefaultPluginDataTemplate_None { + get { + return ResourceManager.GetString("Loc_DefaultPluginDataTemplate_None", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die No single configuration made. This is an completley empty template. + ///Use this if you want to to have every option under your control or just want to make only a few configurations. ähnelt. + /// + public static string Loc_DefaultPluginDataTemplate_None_Description { + get { + return ResourceManager.GetString("Loc_DefaultPluginDataTemplate_None_Description", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Simple ähnelt. + /// + public static string Loc_DefaultPluginDataTemplate_Simple { + get { + return ResourceManager.GetString("Loc_DefaultPluginDataTemplate_Simple", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Shows the job tag with color and the role icon by replacing the title. ähnelt. + /// + public static string Loc_DefaultPluginDataTemplate_Simple_Description { + get { + return ResourceManager.GetString("Loc_DefaultPluginDataTemplate_Simple_Description", resourceCulture); + } + } + /// /// Sucht eine lokalisierte Zeichenfolge, die Melee ähnelt. /// diff --git a/PlayerTags/Resources/Strings.resx b/PlayerTags/Resources/Strings.resx index 9fed405..6629cd8 100644 --- a/PlayerTags/Resources/Strings.resx +++ b/PlayerTags/Resources/Strings.resx @@ -754,4 +754,42 @@ When disabled, only the disconnected status icon is priorized. Viewing Cutscene + + Template + + + Basic + + + Same as Empty, but includes a very basic pre-confiuration for formatting and coloring. +Can also be used if you want to make your own cofiguration but want basic formatting. + + + Here you can choose the template for all the settings below. +The template is used as basic set of properties. Every change you are making is what get saved. But all options from this template will not be saved. +This helpes you by not needing to overwrite so much properties - or by doing your completely own configuration without a template. + +Warning: +Changing this can cause properties to be resetted. You may loose a part of your configuration. +After you changed this, ensure everything is setted up how you like it. + + + Full + + + Show the job tag with color and also color the player name element. + + + Empty + + + No single configuration made. This is an completley empty template. +Use this if you want to to have every option under your control or just want to make only a few configurations. + + + Simple + + + Shows the job tag with color and the role icon by replacing the title. + \ No newline at end of file From e17b884e0875e1d54a8108409c8bc8db6d00f9e7 Mon Sep 17 00:00:00 2001 From: Pilzinsel64 Date: Thu, 10 Nov 2022 15:03:10 +0100 Subject: [PATCH 17/36] v1.8 --- PlayerTags/PlayerTags.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PlayerTags/PlayerTags.csproj b/PlayerTags/PlayerTags.csproj index 352f895..90b7b1d 100644 --- a/PlayerTags/PlayerTags.csproj +++ b/PlayerTags/PlayerTags.csproj @@ -1,7 +1,7 @@  r00telement;Pilzinsel64 - 1.7.5.1 + 1.8.0.0 From 3e347e2bcafe91fcfd2571b62d8aff66bbe5c37d Mon Sep 17 00:00:00 2001 From: Pilzinsel64 Date: Sun, 13 Nov 2022 08:43:35 +0100 Subject: [PATCH 18/36] put Pilz.Dalamud and replace NuGet package with --- .../ActivityContexts/ActivityContext.cs | 25 +++ .../ActivityContextManager.cs | 85 ++++++++ Pilz.Dalamud/ActivityContexts/ActivityType.cs | 18 ++ Pilz.Dalamud/ActivityContexts/ZoneType.cs | 20 ++ Pilz.Dalamud/Extensions.cs | 41 ++++ Pilz.Dalamud/GameInterfaceHelper.cs | 153 +++++++++++++ Pilz.Dalamud/Icons/JobIconSet.cs | 27 +++ Pilz.Dalamud/Icons/JobIconSetName.cs | 24 +++ Pilz.Dalamud/Icons/JobIconSets.cs | 128 +++++++++++ .../AddonNamePlate_SetPlayerNameEventArgs.cs | 19 ++ ...NamePlate_SetPlayerNameManagedEventArgs.cs | 42 ++++ .../Nameplates/EventArgs/HookBaseEventArgs.cs | 18 ++ .../EventArgs/HookManagedBaseEventArgs.cs | 13 ++ .../EventArgs/HookWithResultBaseEventArgs.cs | 21 ++ .../HookWithResultManagedBaseEventArgs.cs | 13 ++ .../Nameplates/Model/SafeAddonNameplate.cs | 43 ++++ .../Nameplates/Model/SafeNameplateInfo.cs | 57 +++++ .../Nameplates/Model/SafeNameplateObject.cs | 128 +++++++++++ Pilz.Dalamud/Nameplates/Model/StatusIcons.cs | 31 +++ Pilz.Dalamud/Nameplates/NameplateHooks.cs | 203 ++++++++++++++++++ Pilz.Dalamud/Nameplates/NameplateManager.cs | 83 +++++++ .../Nameplates/Tools/NameplateChanges.cs | 51 +++++ .../Nameplates/Tools/NameplateChangesProps.cs | 16 ++ .../Nameplates/Tools/NameplateElements.cs | 15 ++ .../Tools/NameplateUpdateFactory.cs | 48 +++++ .../Nameplates/Tools/StatusIconPriorizer.cs | 67 ++++++ .../Tools/StatusIconPriorizerConditionSets.cs | 15 ++ .../Tools/StatusIconPriorizerSettings.cs | 92 ++++++++ Pilz.Dalamud/Pilz.Dalamud.csproj | 63 ++++++ Pilz.Dalamud/PluginServices.cs | 28 +++ Pilz.Dalamud/Tools/StatusIconFontConverter.cs | 41 ++++ Pilz.Dalamud/Tools/Strings/StringChange.cs | 24 +++ Pilz.Dalamud/Tools/Strings/StringChanges.cs | 40 ++++ .../Tools/Strings/StringChangesProps.cs | 31 +++ Pilz.Dalamud/Tools/Strings/StringPosition.cs | 15 ++ .../Tools/Strings/StringUpdateFactory.cs | 132 ++++++++++++ Pilz.Dalamud/XivApi.cs | 39 ++++ Pilz.Dalamud/packages.lock.json | 6 + PlayerTags.sln | 6 + PlayerTags/PlayerTags.csproj | 2 +- PlayerTags/packages.lock.json | 7 +- 41 files changed, 1924 insertions(+), 6 deletions(-) create mode 100644 Pilz.Dalamud/ActivityContexts/ActivityContext.cs create mode 100644 Pilz.Dalamud/ActivityContexts/ActivityContextManager.cs create mode 100644 Pilz.Dalamud/ActivityContexts/ActivityType.cs create mode 100644 Pilz.Dalamud/ActivityContexts/ZoneType.cs create mode 100644 Pilz.Dalamud/Extensions.cs create mode 100644 Pilz.Dalamud/GameInterfaceHelper.cs create mode 100644 Pilz.Dalamud/Icons/JobIconSet.cs create mode 100644 Pilz.Dalamud/Icons/JobIconSetName.cs create mode 100644 Pilz.Dalamud/Icons/JobIconSets.cs create mode 100644 Pilz.Dalamud/Nameplates/EventArgs/AddonNamePlate_SetPlayerNameEventArgs.cs create mode 100644 Pilz.Dalamud/Nameplates/EventArgs/AddonNamePlate_SetPlayerNameManagedEventArgs.cs create mode 100644 Pilz.Dalamud/Nameplates/EventArgs/HookBaseEventArgs.cs create mode 100644 Pilz.Dalamud/Nameplates/EventArgs/HookManagedBaseEventArgs.cs create mode 100644 Pilz.Dalamud/Nameplates/EventArgs/HookWithResultBaseEventArgs.cs create mode 100644 Pilz.Dalamud/Nameplates/EventArgs/HookWithResultManagedBaseEventArgs.cs create mode 100644 Pilz.Dalamud/Nameplates/Model/SafeAddonNameplate.cs create mode 100644 Pilz.Dalamud/Nameplates/Model/SafeNameplateInfo.cs create mode 100644 Pilz.Dalamud/Nameplates/Model/SafeNameplateObject.cs create mode 100644 Pilz.Dalamud/Nameplates/Model/StatusIcons.cs create mode 100644 Pilz.Dalamud/Nameplates/NameplateHooks.cs create mode 100644 Pilz.Dalamud/Nameplates/NameplateManager.cs create mode 100644 Pilz.Dalamud/Nameplates/Tools/NameplateChanges.cs create mode 100644 Pilz.Dalamud/Nameplates/Tools/NameplateChangesProps.cs create mode 100644 Pilz.Dalamud/Nameplates/Tools/NameplateElements.cs create mode 100644 Pilz.Dalamud/Nameplates/Tools/NameplateUpdateFactory.cs create mode 100644 Pilz.Dalamud/Nameplates/Tools/StatusIconPriorizer.cs create mode 100644 Pilz.Dalamud/Nameplates/Tools/StatusIconPriorizerConditionSets.cs create mode 100644 Pilz.Dalamud/Nameplates/Tools/StatusIconPriorizerSettings.cs create mode 100644 Pilz.Dalamud/Pilz.Dalamud.csproj create mode 100644 Pilz.Dalamud/PluginServices.cs create mode 100644 Pilz.Dalamud/Tools/StatusIconFontConverter.cs create mode 100644 Pilz.Dalamud/Tools/Strings/StringChange.cs create mode 100644 Pilz.Dalamud/Tools/Strings/StringChanges.cs create mode 100644 Pilz.Dalamud/Tools/Strings/StringChangesProps.cs create mode 100644 Pilz.Dalamud/Tools/Strings/StringPosition.cs create mode 100644 Pilz.Dalamud/Tools/Strings/StringUpdateFactory.cs create mode 100644 Pilz.Dalamud/XivApi.cs create mode 100644 Pilz.Dalamud/packages.lock.json diff --git a/Pilz.Dalamud/ActivityContexts/ActivityContext.cs b/Pilz.Dalamud/ActivityContexts/ActivityContext.cs new file mode 100644 index 0000000..ae9fd79 --- /dev/null +++ b/Pilz.Dalamud/ActivityContexts/ActivityContext.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Pilz.Dalamud.ActivityContexts +{ + public class ActivityContext + { + public ActivityType ActivityType { get; init; } + public ZoneType ZoneType { get; init; } + + public ActivityContext(ActivityType activityType, ZoneType zoneType) + { + ActivityType = activityType; + ZoneType = zoneType; + } + + public bool IsInDuty + { + get => ZoneType != ZoneType.Overworld; + } + } +} diff --git a/Pilz.Dalamud/ActivityContexts/ActivityContextManager.cs b/Pilz.Dalamud/ActivityContexts/ActivityContextManager.cs new file mode 100644 index 0000000..bf218d6 --- /dev/null +++ b/Pilz.Dalamud/ActivityContexts/ActivityContextManager.cs @@ -0,0 +1,85 @@ +using Dalamud.Logging; +using Lumina.Excel; +using Lumina.Excel.GeneratedSheets; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Pilz.Dalamud.ActivityContexts +{ + public class ActivityContextManager : IDisposable + { + public delegate void ActivityContextChangedEventHandler(ActivityContextManager sender, ActivityContext activityContext); + public event ActivityContextChangedEventHandler ActivityContextChanged; + + private readonly ExcelSheet contentFinderConditionsSheet; + + public ActivityContext CurrentActivityContext { get; protected set; } + + public ActivityContextManager() + { + // Get condition sheet + contentFinderConditionsSheet = PluginServices.DataManager.GameData.GetExcelSheet(); + + // Checks current territory type (if enabled/installed during a dutiy e.g.) + CheckCurrentTerritory(); + + // Enable event for automatic checks + PluginServices.ClientState.TerritoryChanged += ClientState_TerritoryChanged; + } + + public void Dispose() + { + PluginServices.ClientState.TerritoryChanged -= ClientState_TerritoryChanged; + } + + private void ClientState_TerritoryChanged(object? sender, ushort e) + { + CheckCurrentTerritory(); + } + + private void CheckCurrentTerritory() + { + var content = contentFinderConditionsSheet.FirstOrDefault(c => c.TerritoryType.Row == PluginServices.ClientState.TerritoryType); + ActivityType newActivityContext; + ZoneType newZoneType; + + if (content == null) + { + // No content found, so we must be on the overworld + newActivityContext = ActivityType.None; + newZoneType = ZoneType.Overworld; + } + else + { + // Check for ActivityContext + if (content.PvP) + newActivityContext = ActivityType.PvpDuty; + else + newActivityContext = ActivityType.PveDuty; + + // Find correct member type + var memberType = content.ContentMemberType.Row; + if (content.RowId == 16 || content.RowId == 15) + memberType = 2; // Praetorium and Castrum Meridianum + else if (content.RowId == 735 || content.RowId == 778) + memberType = 127; // Bozja + + // Check for ZoneType + newZoneType = memberType switch + { + 2 => ZoneType.Dungeon, + 3 => ZoneType.Raid, + 4 => ZoneType.AllianceRaid, + 127 => ZoneType.Foray, + _ => ZoneType.Dungeon, + }; + } + + CurrentActivityContext = new(newActivityContext, newZoneType); + ActivityContextChanged?.Invoke(this, CurrentActivityContext); + } + } +} diff --git a/Pilz.Dalamud/ActivityContexts/ActivityType.cs b/Pilz.Dalamud/ActivityContexts/ActivityType.cs new file mode 100644 index 0000000..d82d5ab --- /dev/null +++ b/Pilz.Dalamud/ActivityContexts/ActivityType.cs @@ -0,0 +1,18 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Pilz.Dalamud.ActivityContexts +{ + [JsonConverter(typeof(StringEnumConverter))] + public enum ActivityType + { + None = 0x0, + PveDuty = 0x1, + PvpDuty = 0x2 + } +} diff --git a/Pilz.Dalamud/ActivityContexts/ZoneType.cs b/Pilz.Dalamud/ActivityContexts/ZoneType.cs new file mode 100644 index 0000000..6800511 --- /dev/null +++ b/Pilz.Dalamud/ActivityContexts/ZoneType.cs @@ -0,0 +1,20 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Pilz.Dalamud.ActivityContexts +{ + [JsonConverter(typeof(StringEnumConverter))] + public enum ZoneType + { + Overworld, + Dungeon, + Raid, + AllianceRaid, + Foray + } +} diff --git a/Pilz.Dalamud/Extensions.cs b/Pilz.Dalamud/Extensions.cs new file mode 100644 index 0000000..08008a4 --- /dev/null +++ b/Pilz.Dalamud/Extensions.cs @@ -0,0 +1,41 @@ +using Dalamud.Game.Text.SeStringHandling; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Pilz.Dalamud +{ + public static class Extensions + { + /// + /// Removes a Payload from a given SeString. + /// Using SeString.Payloads.Remove() does not use the reference to compare for some reason. Tis is a workaround. + /// + /// + /// + public static void Remove(this SeString seString, Payload payload) + { + Remove(seString.Payloads, payload); + } + + /// + /// Removes a Payload from a given list. + /// Using List.Remove() does not use the reference to compare for some reason. Tis is a workaround. + /// + /// + /// + public static void Remove(this List payloads, Payload payload) + { + for (int i = 0; i < payloads.Count; i++) + { + if (ReferenceEquals(payloads[i], payload)) + { + payloads.RemoveAt(i); + break; + } + } + } + } +} diff --git a/Pilz.Dalamud/GameInterfaceHelper.cs b/Pilz.Dalamud/GameInterfaceHelper.cs new file mode 100644 index 0000000..f786f13 --- /dev/null +++ b/Pilz.Dalamud/GameInterfaceHelper.cs @@ -0,0 +1,153 @@ +using Dalamud.Game.Text.SeStringHandling; +using FFXIVClientStructs.FFXIV.Client.System.Memory; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace Pilz.Dalamud +{ + public static class GameInterfaceHelper + { + public static SeString ReadSeString(IntPtr ptr) + { + if (ptr == IntPtr.Zero) + { + return new SeString(); + } + + if (TryReadStringBytes(ptr, out var bytes) && bytes != null) + { + return SeString.Parse(bytes); + } + + return new SeString(); + } + + public static bool TryReadSeString(IntPtr ptr, out SeString? seString) + { + seString = null; + if (ptr == IntPtr.Zero) + { + return false; + } + + if (TryReadStringBytes(ptr, out var bytes) && bytes != null) + { + seString = SeString.Parse(bytes); + return true; + } + + return false; + } + + public static string? ReadString(IntPtr ptr) + { + if (ptr == IntPtr.Zero) + { + return null; + } + + if (TryReadStringBytes(ptr, out var bytes) && bytes != null) + { + return Encoding.UTF8.GetString(bytes); + } + + return null; + } + + public static bool TryReadString(IntPtr ptr, out string? str) + { + str = null; + if (ptr == IntPtr.Zero) + { + return false; + } + + if (TryReadStringBytes(ptr, out var bytes) && bytes != null) + { + str = Encoding.UTF8.GetString(bytes); + return true; + } + + return false; + } + + public static bool TryReadStringBytes(IntPtr ptr, out byte[]? bytes) + { + bytes = null; + if (ptr == IntPtr.Zero) + { + return false; + } + + var size = 0; + while (Marshal.ReadByte(ptr, size) != 0) + { + size++; + } + + bytes = new byte[size]; + Marshal.Copy(ptr, bytes, 0, size); + + return true; + } + + public static IntPtr PluginAllocate(byte[] bytes) + { + IntPtr pointer = Marshal.AllocHGlobal(bytes.Length + 1); + Marshal.Copy(bytes, 0, pointer, bytes.Length); + Marshal.WriteByte(pointer, bytes.Length, 0); + + return pointer; + } + + public static IntPtr PluginAllocate(SeString seString) + { + return PluginAllocate(seString.Encode()); + } + + public static void PluginFree(IntPtr ptr) + { + Marshal.FreeHGlobal(ptr); + } + + public static void PluginFree(ref IntPtr ptr) + { + PluginFree(ptr); + ptr = IntPtr.Zero; + } + + public static byte[] NullTerminate(this byte[] bytes) + { + if (bytes.Length == 0 || bytes[bytes.Length - 1] != 0) + { + var newBytes = new byte[bytes.Length + 1]; + Array.Copy(bytes, newBytes, bytes.Length); + newBytes[^1] = 0; + + return newBytes; + } + + return bytes; + } + + public static unsafe IntPtr GameUIAllocate(ulong size) + { + return (IntPtr)IMemorySpace.GetUISpace()->Malloc(size, 0); + } + + public static unsafe void GameFree(ref IntPtr ptr, ulong size) + { + if (ptr == IntPtr.Zero) + { + return; + } + + IMemorySpace.Free((void*)ptr, size); + ptr = IntPtr.Zero; + } + } +} diff --git a/Pilz.Dalamud/Icons/JobIconSet.cs b/Pilz.Dalamud/Icons/JobIconSet.cs new file mode 100644 index 0000000..db7acc0 --- /dev/null +++ b/Pilz.Dalamud/Icons/JobIconSet.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using System.Threading.Tasks; + +namespace Pilz.Dalamud.Icons +{ + public class JobIconSet + { + private readonly int[] icons; + + public float IconScale { get; init; } + + public JobIconSet(int[] icons, float iconScale) + { + this.icons = icons; + IconScale = iconScale; + } + + public int GetIcon(uint jobID) + { + return icons[jobID - 1]; + } + } +} diff --git a/Pilz.Dalamud/Icons/JobIconSetName.cs b/Pilz.Dalamud/Icons/JobIconSetName.cs new file mode 100644 index 0000000..7d19a2c --- /dev/null +++ b/Pilz.Dalamud/Icons/JobIconSetName.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Pilz.Dalamud.Icons +{ + public enum JobIconSetName + { + Gold, + Framed, + Glowing, + Blue, + Red, + Purple, + Black, + Yellow, + Orange, + Green, + Grey, + Role + } +} diff --git a/Pilz.Dalamud/Icons/JobIconSets.cs b/Pilz.Dalamud/Icons/JobIconSets.cs new file mode 100644 index 0000000..376b7f5 --- /dev/null +++ b/Pilz.Dalamud/Icons/JobIconSets.cs @@ -0,0 +1,128 @@ +using Lumina.Excel.GeneratedSheets; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Pilz.Dalamud.Icons +{ + public class JobIconSets + { + private readonly Dictionary iconSets = new(); + + public JobIconSets() + { + Add(JobIconSetName.Gold, new[] + { + 62001, 62002, 62003, 62004, 62005, 62006, 62007, 62008, 62009, 62010, + 62011, 62012, 62013, 62014, 62015, 62016, 62017, 62018, 62019, 62020, + 62021, 62022, 62023, 62024, 62025, 62026, 62027, 62028, 62029, 62030, + 62031, 62032, 62033, 62034, 62035, 62036, 62037, 62038, 62039, 62040 + }, 1); + + Add(JobIconSetName.Framed, new[] + { + 62101, 62102, 62103, 62104, 62105, 62106, 62107, 62108, 62109, 62110, + 62111, 62112, 62113, 62114, 62115, 62116, 62117, 62118, 62119, 62120, + 62121, 62122, 62123, 62124, 62125, 62126, 62127, 62128, 62129, 62130, + 62131, 62132, 62133, 62134, 62135, 62136, 62137, 62138, 62139, 62140 + }); + + Add(JobIconSetName.Glowing, new[] + { + 62301, 62302, 62303, 62304, 62305, 62306, 62307, 62310, 62311, 62312, + 62313, 62314, 62315, 62316, 62317, 62318, 62319, 62320, 62401, 62402, + 62403, 62404, 62405, 62406, 62407, 62308, 62408, 62409, 62309, 62410, + 62411, 62412, 62413, 62414, 62415, 62416, 62417, 62418, 62419, 62420 + }); + + Add(JobIconSetName.Grey, new[] + { + 91022, 91023, 91024, 91025, 91026, 91028, 91029, 91031, 91032, 91033, + 91034, 91035, 91036, 91037, 91038, 91039, 91040, 91041, 91079, 91080, + 91081, 91082, 91083, 91084, 91085, 91030, 91086, 91087, 91121, 91122, + 91125, 91123, 91124, 91127, 91128, 91129, 91130, 91131, 91132, 91133 + }, 2); + + Add(JobIconSetName.Black, new[] + { + 91522, 91523, 91524, 91525, 91526, 91528, 91529, 91531, 91532, 91533, + 91534, 91535, 91536, 91537, 91538, 91539, 91540, 91541, 91579, 91580, + 91581, 91582, 91583, 91584, 91585, 91530, 91586, 91587, 91621, 91622, + 91625, 91623, 91624, 91627, 91628, 91629, 91630, 91631, 91632, 91633 + }, 2); + + Add(JobIconSetName.Yellow, new[] + { + 92022, 92023, 92024, 92025, 92026, 92028, 92029, 92031, 92032, 92033, + 92034, 92035, 92036, 92037, 92038, 92039, 92040, 92041, 92079, 92080, + 92081, 92082, 92083, 92084, 92085, 92030, 92086, 92087, 92121, 92122, + 92125, 92123, 92124, 92127, 92128, 92129, 92130, 92131, 92132, 92133 + }, 2); + + Add(JobIconSetName.Orange, new[] + { + 92522, 92523, 92524, 92525, 92526, 92528, 92529, 92531, 92532, 92533, + 92534, 92535, 92536, 92537, 92538, 92539, 92540, 92541, 92579, 92580, + 92581, 92582, 92583, 92584, 92585, 92530, 92586, 92587, 92621, 92622, + 92625, 92623, 92624, 92627, 92628, 92629, 92630, 92631, 92632, 92633 + }, 2); + + Add(JobIconSetName.Red, new[] + { + 93022, 93023, 93024, 93025, 93026, 93028, 93029, 93031, 93032, 93033, + 93034, 93035, 93036, 93037, 93038, 93039, 93040, 93041, 93079, 93080, + 93081, 93082, 93083, 93084, 93085, 93030, 93086, 93087, 93121, 93122, + 93125, 93123, 93124, 93127, 93128, 93129, 93130, 93131, 93132, 93133 + }, 2); + + Add(JobIconSetName.Purple, new[] + { + 93522, 93523, 93524, 93525, 93526, 93528, 93529, 93531, 93532, 93533, + 93534, 93535, 93536, 93537, 93538, 93539, 93540, 93541, 93579, 93580, + 93581, 93582, 93583, 93584, 93585, 93530, 93586, 93587, 93621, 93622, + 93625, 93623, 93624, 93627, 93628, 93629, 93630, 93631, 93632, 93633 + }, 2); + + Add(JobIconSetName.Blue, new[] + { + 94022, 94023, 94024, 94025, 94026, 94028, 94029, 94031, 94032, 94033, + 94034, 94035, 94036, 94037, 94038, 94039, 94040, 94041, 94079, 94080, + 94081, 94082, 94083, 94084, 94085, 94030, 94086, 94087, 94121, 94122, + 94125, 94123, 94124, 94127, 94128, 94129, 94130, 94131, 94132, 94133 + }, 2); + + Add(JobIconSetName.Green, new[] + { + 94522, 94523, 94524, 94525, 94526, 94528, 94529, 94531, 94532, 94533, + 94534, 94535, 94536, 94537, 94538, 94539, 94540, 94541, 94579, 94580, + 94581, 94582, 94583, 94584, 94585, 94530, 94586, 94587, 94621, 94622, + 94625, 94623, 94624, 94627, 94628, 94629, 94630, 94631, 94632, 94633 + }, 2); + + Add(JobIconSetName.Role, new[] + { + 62581, 62584, 62581, 62584, 62586, 62582, 62502, 62502, 62503, 62504, + 62505, 62506, 62507, 62508, 62509, 62510, 62511, 62512, 62581, 62584, + 62581, 62584, 62586, 62582, 62587, 62587, 62587, 62582, 62584, 62584, + 62586, 62581, 62582, 62584, 62587, 62587, 62581, 62586, 62584, 62582 + }); + } + + private void Add(JobIconSetName id, int[] icons, float scale = 1f) + { + iconSets[id] = new JobIconSet(icons, scale); + } + + public int GetJobIcon(JobIconSetName set, uint jobId) + { + return iconSets[set].GetIcon(jobId); + } + + public float GetJobIconSale(JobIconSetName set) + { + return iconSets[set].IconScale; + } + } +} diff --git a/Pilz.Dalamud/Nameplates/EventArgs/AddonNamePlate_SetPlayerNameEventArgs.cs b/Pilz.Dalamud/Nameplates/EventArgs/AddonNamePlate_SetPlayerNameEventArgs.cs new file mode 100644 index 0000000..3a67e2b --- /dev/null +++ b/Pilz.Dalamud/Nameplates/EventArgs/AddonNamePlate_SetPlayerNameEventArgs.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Pilz.Dalamud.Nameplates.EventArgs +{ + public class AddonNamePlate_SetPlayerNameEventArgs : HookWithResultBaseEventArgs + { + public IntPtr PlayerNameplateObjectPtr { get; set; } + public IntPtr TitlePtr { get; set; } + public IntPtr NamePtr { get; set; } + public IntPtr FreeCompanyPtr { get; set; } + public bool IsTitleAboveName { get; set; } + public bool IsTitleVisible { get; set; } + public int IconID { get; set; } + } +} diff --git a/Pilz.Dalamud/Nameplates/EventArgs/AddonNamePlate_SetPlayerNameManagedEventArgs.cs b/Pilz.Dalamud/Nameplates/EventArgs/AddonNamePlate_SetPlayerNameManagedEventArgs.cs new file mode 100644 index 0000000..494bdd1 --- /dev/null +++ b/Pilz.Dalamud/Nameplates/EventArgs/AddonNamePlate_SetPlayerNameManagedEventArgs.cs @@ -0,0 +1,42 @@ +using Dalamud.Game.Text.SeStringHandling; +using Pilz.Dalamud.Nameplates.Model; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Pilz.Dalamud.Nameplates.EventArgs +{ + public class AddonNamePlate_SetPlayerNameManagedEventArgs : HookWithResultManagedBaseEventArgs + { + 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; } + public SeString Name { get; set; } + public SeString FreeCompany { get; set; } + + public bool IsTitleAboveName + { + get => OriginalEventArgs.IsTitleAboveName; + set => OriginalEventArgs.IsTitleAboveName = value; + } + + public bool IsTitleVisible + { + get => OriginalEventArgs.IsTitleVisible; + set => OriginalEventArgs.IsTitleVisible = value; + } + + public int IconID + { + get => OriginalEventArgs.IconID; + set => OriginalEventArgs.IconID = value; + } + } +} diff --git a/Pilz.Dalamud/Nameplates/EventArgs/HookBaseEventArgs.cs b/Pilz.Dalamud/Nameplates/EventArgs/HookBaseEventArgs.cs new file mode 100644 index 0000000..2df854d --- /dev/null +++ b/Pilz.Dalamud/Nameplates/EventArgs/HookBaseEventArgs.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Pilz.Dalamud.Nameplates.EventArgs +{ + public abstract class HookBaseEventArgs + { + internal event Action CallOriginal; + + public void Original() + { + CallOriginal?.Invoke(); + } + } +} diff --git a/Pilz.Dalamud/Nameplates/EventArgs/HookManagedBaseEventArgs.cs b/Pilz.Dalamud/Nameplates/EventArgs/HookManagedBaseEventArgs.cs new file mode 100644 index 0000000..71df630 --- /dev/null +++ b/Pilz.Dalamud/Nameplates/EventArgs/HookManagedBaseEventArgs.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Pilz.Dalamud.Nameplates.EventArgs +{ + public abstract class HookManagedBaseEventArgs + { + public HookBaseEventArgs OriginalEventArgs { get; internal set; } + } +} diff --git a/Pilz.Dalamud/Nameplates/EventArgs/HookWithResultBaseEventArgs.cs b/Pilz.Dalamud/Nameplates/EventArgs/HookWithResultBaseEventArgs.cs new file mode 100644 index 0000000..ad3c130 --- /dev/null +++ b/Pilz.Dalamud/Nameplates/EventArgs/HookWithResultBaseEventArgs.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Pilz.Dalamud.Nameplates.EventArgs +{ + 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/EventArgs/HookWithResultManagedBaseEventArgs.cs b/Pilz.Dalamud/Nameplates/EventArgs/HookWithResultManagedBaseEventArgs.cs new file mode 100644 index 0000000..59993fd --- /dev/null +++ b/Pilz.Dalamud/Nameplates/EventArgs/HookWithResultManagedBaseEventArgs.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Pilz.Dalamud.Nameplates.EventArgs +{ + public abstract class HookWithResultManagedBaseEventArgs + { + public HookWithResultBaseEventArgs OriginalEventArgs { get; internal set; } + } +} diff --git a/Pilz.Dalamud/Nameplates/Model/SafeAddonNameplate.cs b/Pilz.Dalamud/Nameplates/Model/SafeAddonNameplate.cs new file mode 100644 index 0000000..2b8736b --- /dev/null +++ b/Pilz.Dalamud/Nameplates/Model/SafeAddonNameplate.cs @@ -0,0 +1,43 @@ +using Dalamud.Logging; +using Dalamud.Plugin; +using FFXIVClientStructs.FFXIV.Client.UI; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace Pilz.Dalamud.Nameplates.Model +{ + public class SafeAddonNameplate + { + private readonly DalamudPluginInterface Interface; + + public IntPtr Pointer => PluginServices.GameGui.GetAddonByName("NamePlate", 1); + + public SafeAddonNameplate(DalamudPluginInterface pluginInterface) + { + Interface = pluginInterface; + } + + public unsafe SafeNameplateObject GetNamePlateObject(int index) + { + SafeNameplateObject result = null; + + if (Pointer != IntPtr.Zero) + { + var npObjectArrayPtrPtr = Pointer + Marshal.OffsetOf(typeof(AddonNamePlate), nameof(AddonNamePlate.NamePlateObjectArray)).ToInt32(); + var npObjectArrayPtr = Marshal.ReadIntPtr(npObjectArrayPtrPtr); + + if (npObjectArrayPtr != IntPtr.Zero) + { + var npObjectPtr = npObjectArrayPtr + Marshal.SizeOf(typeof(AddonNamePlate.NamePlateObject)) * index; + result = new(npObjectPtr, index); + } + } + + return result; + } + } +} diff --git a/Pilz.Dalamud/Nameplates/Model/SafeNameplateInfo.cs b/Pilz.Dalamud/Nameplates/Model/SafeNameplateInfo.cs new file mode 100644 index 0000000..5076d63 --- /dev/null +++ b/Pilz.Dalamud/Nameplates/Model/SafeNameplateInfo.cs @@ -0,0 +1,57 @@ +using FFXIVClientStructs.FFXIV.Client.System.String; +using FFXIVClientStructs.FFXIV.Client.UI; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace Pilz.Dalamud.Nameplates.Model +{ + public class SafeNameplateInfo + { + public readonly IntPtr Pointer; + public readonly RaptureAtkModule.NamePlateInfo Data; + + public SafeNameplateInfo(IntPtr pointer) + { + Pointer = pointer; + Data = Marshal.PtrToStructure(Pointer); + } + + internal IntPtr NameAddress => GetStringPtr(nameof(RaptureAtkModule.NamePlateInfo.Name)); + internal IntPtr FcNameAddress => GetStringPtr(nameof(RaptureAtkModule.NamePlateInfo.FcName)); + internal IntPtr TitleAddress => GetStringPtr(nameof(RaptureAtkModule.NamePlateInfo.Title)); + internal IntPtr DisplayTitleAddress => GetStringPtr(nameof(RaptureAtkModule.NamePlateInfo.DisplayTitle)); + internal IntPtr LevelTextAddress => GetStringPtr(nameof(RaptureAtkModule.NamePlateInfo.LevelText)); + + public string Name => GetString(NameAddress); + public string FcName => GetString(FcNameAddress); + public string Title => GetString(TitleAddress); + public string DisplayTitle => GetString(DisplayTitleAddress); + public string LevelText => GetString(LevelTextAddress); + + //public bool IsPlayerCharacter() => XivApi.IsPlayerCharacter(Data.ObjectID.ObjectID); + + //public bool IsPartyMember() => XivApi.IsPartyMember(Data.ObjectID.ObjectID); + + //public bool IsAllianceMember() => XivApi.IsAllianceMember(Data.ObjectID.ObjectID); + + //public uint GetJobID() => GetJobId(Data.ObjectID.ObjectID); + + private unsafe IntPtr GetStringPtr(string name) + { + var namePtr = Pointer + Marshal.OffsetOf(typeof(RaptureAtkModule.NamePlateInfo), name).ToInt32(); + var stringPtrPtr = namePtr + Marshal.OffsetOf(typeof(Utf8String), nameof(Utf8String.StringPtr)).ToInt32(); + var stringPtr = Marshal.ReadIntPtr(stringPtrPtr); + return stringPtr; + } + + private string GetString(IntPtr stringPtr) + { + return Marshal.PtrToStringUTF8(stringPtr); + } + } +} diff --git a/Pilz.Dalamud/Nameplates/Model/SafeNameplateObject.cs b/Pilz.Dalamud/Nameplates/Model/SafeNameplateObject.cs new file mode 100644 index 0000000..6a39ba0 --- /dev/null +++ b/Pilz.Dalamud/Nameplates/Model/SafeNameplateObject.cs @@ -0,0 +1,128 @@ +using Dalamud.Logging; +using FFXIVClientStructs.FFXIV.Client.UI; +using FFXIVClientStructs.FFXIV.Component.GUI; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace Pilz.Dalamud.Nameplates.Model +{ + public class SafeNameplateObject + { + public IntPtr Pointer { get; } + public AddonNamePlate.NamePlateObject Data { get; } + + private int _Index; + private SafeNameplateInfo _NamePlateInfo; + + public SafeNameplateObject(IntPtr pointer, int index = -1) + { + Pointer = pointer; + Data = Marshal.PtrToStructure(pointer); + _Index = index; + } + + public int Index + { + get + { + int result = _Index; + + if (_Index == -1) + { + var addon = XivApi.GetSafeAddonNamePlate(); + var npObject0 = addon.GetNamePlateObject(0); + + if (npObject0 == null) + result = -1; // NamePlateObject0 was null + else + { + var npObjectBase = npObject0.Pointer; + var npObjectSize = Marshal.SizeOf(typeof(AddonNamePlate.NamePlateObject)); + var index = (Pointer.ToInt64() - npObjectBase.ToInt64()) / npObjectSize; + + if (index < 0 || index >= 50) + result = -2; // NamePlateObject index was out of bounds + else + result = _Index = (int)index; + } + } + + return result; + } + } + + public SafeNameplateInfo NamePlateInfo + { + get + { + SafeNameplateInfo result = null; + + if (_NamePlateInfo != null) + { + var rapturePtr = XivApi.RaptureAtkModulePtr; + + if (rapturePtr != IntPtr.Zero) + { + var npInfoArrayPtr = rapturePtr + Marshal.OffsetOf(typeof(RaptureAtkModule), nameof(RaptureAtkModule.NamePlateInfoArray)).ToInt32(); + var npInfoPtr = npInfoArrayPtr + Marshal.SizeOf(typeof(RaptureAtkModule.NamePlateInfo)) * Index; + result = _NamePlateInfo = new SafeNameplateInfo(npInfoPtr); + } + } + + return result; + } + } + + #region Getters + + public unsafe IntPtr IconImageNodeAddress => Marshal.ReadIntPtr(Pointer + Marshal.OffsetOf(typeof(AddonNamePlate.NamePlateObject), nameof(AddonNamePlate.NamePlateObject.IconImageNode)).ToInt32()); + public unsafe IntPtr NameNodeAddress => Marshal.ReadIntPtr(Pointer + Marshal.OffsetOf(typeof(AddonNamePlate.NamePlateObject), nameof(AddonNamePlate.NamePlateObject.NameText)).ToInt32()); + + public AtkImageNode IconImageNode => Marshal.PtrToStructure(IconImageNodeAddress); + public AtkTextNode NameTextNode => Marshal.PtrToStructure(NameNodeAddress); + + #endregion + + public unsafe bool IsVisible => Data.IsVisible; + public unsafe bool IsLocalPlayer => Data.IsLocalPlayer; + public bool IsPlayer => Data.NameplateKind == 0; + + //public void SetIconScale(float scale, bool force = false) + //{ + // if (force || IconImageNode.AtkResNode.ScaleX != scale || IconImageNode.AtkResNode.ScaleY != scale) + // { + // Instance.SetNodeScale(IconImageNodeAddress, scale, scale); + // } + //} + + //public void SetNameScale(float scale, bool force = false) + //{ + // if (force || NameTextNode.AtkResNode.ScaleX != scale || NameTextNode.AtkResNode.ScaleY != scale) + // { + // Instance.SetNodeScale(NameNodeAddress, scale, scale); + // } + //} + + //public unsafe void SetName(IntPtr ptr) + //{ + // NameTextNode.SetText("aaa"); + //} + + //public void SetIcon(int icon) + //{ + // IconImageNode.LoadIconTexture(icon, 1); + //} + + public void SetIconPosition(short x, short y) + { + var iconXAdjustPtr = Pointer + Marshal.OffsetOf(typeof(AddonNamePlate.NamePlateObject), nameof(AddonNamePlate.NamePlateObject.IconXAdjust)).ToInt32(); + var iconYAdjustPtr = Pointer + Marshal.OffsetOf(typeof(AddonNamePlate.NamePlateObject), nameof(AddonNamePlate.NamePlateObject.IconYAdjust)).ToInt32(); + Marshal.WriteInt16(iconXAdjustPtr, x); + Marshal.WriteInt16(iconYAdjustPtr, y); + } + } +} diff --git a/Pilz.Dalamud/Nameplates/Model/StatusIcons.cs b/Pilz.Dalamud/Nameplates/Model/StatusIcons.cs new file mode 100644 index 0000000..89d7448 --- /dev/null +++ b/Pilz.Dalamud/Nameplates/Model/StatusIcons.cs @@ -0,0 +1,31 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Pilz.Dalamud.Nameplates.Model +{ + [JsonConverter(typeof(StringEnumConverter))] + public enum StatusIcons + { + Disconnecting = 061503, + InDuty = 061506, + ViewingCutscene = 061508, + Busy = 061509, + Idle = 061511, + DutyFinder = 061517, + PartyLeader = 061521, + PartyMember = 061522, + RolePlaying = 061545, + GroupPose = 061546, + NewAdventurer = 061523, + Mentor = 061540, + MentorPvE = 061542, + MentorCrafting = 061543, + MentorPvP = 061544, + Returner = 061547, + } +} diff --git a/Pilz.Dalamud/Nameplates/NameplateHooks.cs b/Pilz.Dalamud/Nameplates/NameplateHooks.cs new file mode 100644 index 0000000..1d28eb4 --- /dev/null +++ b/Pilz.Dalamud/Nameplates/NameplateHooks.cs @@ -0,0 +1,203 @@ +using Dalamud.Hooking; +using Pilz.Dalamud.Nameplates.EventArgs; +using Dalamud.Utility.Signatures; +using ImGuiNET; +using System; +using System.Collections.Generic; +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 +{ + public class NameplateHooks : IDisposable + { + /// + /// 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 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 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; + private unsafe delegate IntPtr AddonNamePlate_SetPlayerNameplateDetour(IntPtr playerNameplateObjectPtr, bool isTitleAboveName, bool isTitleVisible, IntPtr titlePtr, IntPtr namePtr, IntPtr freeCompanyPtr, int iconId); + + /// + /// Defines if all hooks are enabled. If this is false, then there might be something wrong or the class already has been disposed. + /// + public bool IsValid + { + get + { + var isValid = true; + + isValid &= IsHookEnabled(hook_AddonNamePlate_SetPlayerNameplateDetour); + + return isValid; + } + } + + /// + /// Create a new instance of NAmeplateHooks and automatically initialize and enable all Hooks. + /// + public NameplateHooks() + { + SignatureHelper.Initialise(this); + } + + ~NameplateHooks() + { + Dispose(); + } + + public void Dispose() + { + Unhook(); + } + + /// + /// Initialize and enable all Hooks. + /// + internal void Initialize() + { + hook_AddonNamePlate_SetPlayerNameplateDetour?.Enable(); + } + + /// + /// Disable all Hooks. + /// + internal void Unhook() + { + hook_AddonNamePlate_SetPlayerNameplateDetour?.Disable(); + } + + private static bool IsHookEnabled(Hook hook) where T : Delegate + { + return hook != null && hook.IsEnabled; + } + + private IntPtr SetPlayerNameplateDetour(IntPtr playerNameplateObjectPtr, bool isTitleAboveName, bool isTitleVisible, IntPtr titlePtr, IntPtr namePtr, IntPtr freeCompanyPtr, int iconId) + { + var result = IntPtr.Zero; + + if (IsHookEnabled(hook_AddonNamePlate_SetPlayerNameplateDetour)) + { + var eventArgs = new AddonNamePlate_SetPlayerNameEventArgs + { + PlayerNameplateObjectPtr = playerNameplateObjectPtr, + TitlePtr = titlePtr, + NamePtr = namePtr, + FreeCompanyPtr = freeCompanyPtr, + IsTitleAboveName = isTitleAboveName, + IsTitleVisible = isTitleVisible, + 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); + + // Create new event + var managedEventArgs = new AddonNamePlate_SetPlayerNameManagedEventArgs + { + OriginalEventArgs = eventArgs, + SafeNameplateObject = namePlateObj, + Title = GameInterfaceHelper.ReadSeString(eventArgs.TitlePtr), + Name = GameInterfaceHelper.ReadSeString(eventArgs.NamePtr), + FreeCompany = GameInterfaceHelper.ReadSeString(eventArgs.FreeCompanyPtr) + }; + + // Get raw string content + var titleRaw = managedEventArgs.Title.Encode(); + var nameRaw = managedEventArgs.Name.Encode(); + var freeCompanyRaw = managedEventArgs.FreeCompany.Encode(); + + // Invoke Managed Event + AddonNamePlate_SetPlayerNameManaged.Invoke(managedEventArgs); + + // Get new Title string ontent + var titleNewRaw = managedEventArgs.Title.Encode(); + if (!titleRaw.SequenceEqual(titleNewRaw)) + { + eventArgs.TitlePtr = GameInterfaceHelper.PluginAllocate(titleNewRaw); + freeTitle = true; + } + + // Get new Name string ontent + var nameNewRaw = managedEventArgs.Name.Encode(); + if (!nameRaw.SequenceEqual(nameNewRaw)) + { + eventArgs.NamePtr = GameInterfaceHelper.PluginAllocate(nameNewRaw); + freeName = true; + } + + // Get new Free Company string ontent + var freeCompanyNewRaw = managedEventArgs.FreeCompany.Encode(); + if (!freeCompanyRaw.SequenceEqual(freeCompanyNewRaw)) + { + 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(); + } + + // Set result + result = eventArgs.Result; + } + + return result; + } + } +} diff --git a/Pilz.Dalamud/Nameplates/NameplateManager.cs b/Pilz.Dalamud/Nameplates/NameplateManager.cs new file mode 100644 index 0000000..8fbd04a --- /dev/null +++ b/Pilz.Dalamud/Nameplates/NameplateManager.cs @@ -0,0 +1,83 @@ +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 +{ + public class NameplateManager : IDisposable + { + /// + /// Provides events that you can hook to. + /// + public NameplateHooks Hooks { get; init; } = new(); + + /// + /// Defines if all hooks are enabled and the NameplateManager is ready to go. If this is false, then there might be something wrong or something already has been disposed. + /// + public bool IsValid => Hooks.IsValid; + + /// + /// Creates a new instance of the NameplateManager. + /// + public NameplateManager() + { + Hooks.Initialize(); + } + + ~NameplateManager() + { + Dispose(); + } + + public void Dispose() + { + 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/Nameplates/Tools/NameplateChanges.cs b/Pilz.Dalamud/Nameplates/Tools/NameplateChanges.cs new file mode 100644 index 0000000..b33c372 --- /dev/null +++ b/Pilz.Dalamud/Nameplates/Tools/NameplateChanges.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Pilz.Dalamud.Tools.Strings; + +namespace Pilz.Dalamud.Nameplates.Tools +{ + public class NameplateChanges + { + private readonly Dictionary changes = new(); + + public NameplateChanges() + { + changes.Add(NameplateElements.Title, new()); + changes.Add(NameplateElements.Name, new()); + changes.Add(NameplateElements.FreeCompany, new()); + } + + /// + /// Gets the properties with the changes of an element of your choice where you can add your payloads to a change and setup some options. + /// + /// The position of your choice. + /// + public StringChangesProps GetProps(NameplateElements element) + { + return changes[element]; + } + + /// + /// Gets the changes of an element of your choice where you can add your payloads to a change. + /// + /// The position of your choice. + /// + public StringChanges GetChanges(NameplateElements element) + { + return GetProps(element).StringChanges; + } + + /// + /// Gets a change of the position of the element of your choice where you can add your payloads. + /// + /// The position of your choice. + /// + public StringChange GetChange(NameplateElements element, StringPosition position) + { + return GetChanges(element).GetChange(position); + } + } +} diff --git a/Pilz.Dalamud/Nameplates/Tools/NameplateChangesProps.cs b/Pilz.Dalamud/Nameplates/Tools/NameplateChangesProps.cs new file mode 100644 index 0000000..56f7017 --- /dev/null +++ b/Pilz.Dalamud/Nameplates/Tools/NameplateChangesProps.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Pilz.Dalamud.Nameplates.Tools +{ + public class NameplateChangesProps + { + /// + /// All the changes to the nameplate that should be made. + /// + public NameplateChanges Changes { get; set; } + } +} diff --git a/Pilz.Dalamud/Nameplates/Tools/NameplateElements.cs b/Pilz.Dalamud/Nameplates/Tools/NameplateElements.cs new file mode 100644 index 0000000..0a15cf9 --- /dev/null +++ b/Pilz.Dalamud/Nameplates/Tools/NameplateElements.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Pilz.Dalamud.Nameplates.Tools +{ + public enum NameplateElements + { + Name, + Title, + FreeCompany + } +} diff --git a/Pilz.Dalamud/Nameplates/Tools/NameplateUpdateFactory.cs b/Pilz.Dalamud/Nameplates/Tools/NameplateUpdateFactory.cs new file mode 100644 index 0000000..b04f947 --- /dev/null +++ b/Pilz.Dalamud/Nameplates/Tools/NameplateUpdateFactory.cs @@ -0,0 +1,48 @@ +using Dalamud.Game.Text.SeStringHandling; +using Dalamud.Game.Text.SeStringHandling.Payloads; +using Pilz.Dalamud.ActivityContexts; +using Pilz.Dalamud.Nameplates.Model; +using Pilz.Dalamud.Tools; +using Pilz.Dalamud.Tools.Strings; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Pilz.Dalamud.Nameplates.Tools +{ + public static class NameplateUpdateFactory + { + public static void ApplyNameplateChanges(NameplateChangesProps props) + { + foreach (NameplateElements element in Enum.GetValues(typeof(NameplateElements))) + { + var change = props.Changes.GetProps(element); + StringUpdateFactory.ApplyStringChanges(change); + } + } + + public static bool ApplyStatusIconWithPrio(ref int statusIcon, int newStatusIcon, StringChange stringChange, ActivityContext activityContext, StatusIconPriorizer priorizer) + { + var isPrio = priorizer.IsPriorityIcon(statusIcon, activityContext); + + if (!isPrio) + { + var fontIcon = StatusIconFontConverter.GetBitmapFontIconFromStatusIcon((StatusIcons)statusIcon); + + if (fontIcon != null) + { + // Set new font icon as string change + var iconPayload = new IconPayload(fontIcon.Value); + stringChange.Payloads.Insert(0, iconPayload); + + // Use new status icon as status icon + statusIcon = newStatusIcon; + } + } + + return isPrio; + } + } +} diff --git a/Pilz.Dalamud/Nameplates/Tools/StatusIconPriorizer.cs b/Pilz.Dalamud/Nameplates/Tools/StatusIconPriorizer.cs new file mode 100644 index 0000000..6d0c6fa --- /dev/null +++ b/Pilz.Dalamud/Nameplates/Tools/StatusIconPriorizer.cs @@ -0,0 +1,67 @@ +using Lumina.Excel.GeneratedSheets; +using Pilz.Dalamud.ActivityContexts; +using Pilz.Dalamud.Nameplates.Model; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Pilz.Dalamud.Nameplates.Tools +{ + public class StatusIconPriorizer + { + private static StatusIconPriorizerSettings DefaultSettings { get; } = new(); + public StatusIconPriorizerSettings Settings { get; init; } + + public StatusIconPriorizer() : this(DefaultSettings) + { + } + + public StatusIconPriorizer(StatusIconPriorizerSettings settings) + { + Settings = settings; + } + + /// + /// Check for an icon that should take priority over the job icon, + /// taking into account whether or not the player is in a duty. + /// + /// The incoming icon id that is being overwritten by the plugin. + /// The icon id that should be used. + /// Whether a priority icon was found. + public bool IsPriorityIcon(int iconId, ActivityContext activityContext) + { + bool isPrioIcon; + + if (!Settings.UsePriorizedIcons && iconId != (int)StatusIcons.Disconnecting && iconId != (int)StatusIcons.Disconnecting + 50) + isPrioIcon = false; + else + { + // Select which set of priority icons to use based on whether we're in a duty + // In the future, there can be a third list used when in combat + var priorityIcons = GetPriorityIcons(activityContext); + + // Determine whether the incoming icon should take priority over the job icon + // Check the id plus 50 as that's an alternately sized version + isPrioIcon = priorityIcons.Contains(iconId) || priorityIcons.Contains(iconId + 50); + } + + return isPrioIcon; + } + + private IEnumerable GetPriorityIcons(ActivityContext activityContext) + { + StatusIconPriorizerConditionSets set; + + if (activityContext.ZoneType == ZoneType.Foray) + set = StatusIconPriorizerConditionSets.InForay; + else if (activityContext.IsInDuty) + set = StatusIconPriorizerConditionSets.InDuty; + else + set = StatusIconPriorizerConditionSets.Overworld; + + return Settings.GetConditionSet(set).Select(n => (int)n); + } + } +} diff --git a/Pilz.Dalamud/Nameplates/Tools/StatusIconPriorizerConditionSets.cs b/Pilz.Dalamud/Nameplates/Tools/StatusIconPriorizerConditionSets.cs new file mode 100644 index 0000000..6c78cc7 --- /dev/null +++ b/Pilz.Dalamud/Nameplates/Tools/StatusIconPriorizerConditionSets.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Pilz.Dalamud.Nameplates.Tools +{ + public enum StatusIconPriorizerConditionSets + { + Overworld, + InDuty, + InForay + } +} diff --git a/Pilz.Dalamud/Nameplates/Tools/StatusIconPriorizerSettings.cs b/Pilz.Dalamud/Nameplates/Tools/StatusIconPriorizerSettings.cs new file mode 100644 index 0000000..1222e6c --- /dev/null +++ b/Pilz.Dalamud/Nameplates/Tools/StatusIconPriorizerSettings.cs @@ -0,0 +1,92 @@ +using Newtonsoft.Json; +using Pilz.Dalamud.ActivityContexts; +using Pilz.Dalamud.Nameplates.Model; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Pilz.Dalamud.Nameplates.Tools +{ + public class StatusIconPriorizerSettings + { + [JsonProperty("IconConditionSets")] + private Dictionary> iconConditionSets = new(); + public bool UsePriorizedIcons { get; set; } = true; + + [JsonConstructor] + private StatusIconPriorizerSettings(JsonConstructorAttribute dummy) + { + } + + public StatusIconPriorizerSettings() : this(false) + { + } + + public StatusIconPriorizerSettings(bool fillWithDefaultSettings) + { + foreach (StatusIconPriorizerConditionSets set in Enum.GetValues(typeof(StatusIconPriorizerConditionSets))) + iconConditionSets.Add(set, new List()); + + if (fillWithDefaultSettings) + FillWithDefaultSettings(); + } + + public List GetConditionSet(StatusIconPriorizerConditionSets set) + { + return iconConditionSets[set]; + } + + public void ResetToEmpty() + { + foreach (var kvp in iconConditionSets) + kvp.Value.Clear(); + } + + public void ResetToDefault() + { + ResetToEmpty(); + FillWithDefaultSettings(); + } + + private void FillWithDefaultSettings() + { + var setOverworld = GetConditionSet(StatusIconPriorizerConditionSets.Overworld); + setOverworld.AddRange(new[] + { + StatusIcons.Disconnecting, // Disconnecting + StatusIcons.InDuty, // In Duty + StatusIcons.ViewingCutscene, // Viewing Cutscene + StatusIcons.Busy, // Busy + StatusIcons.Idle, // Idle + StatusIcons.DutyFinder, // Duty Finder + StatusIcons.PartyLeader, // Party Leader + StatusIcons.PartyMember, // Party Member + StatusIcons.RolePlaying, // Role Playing + StatusIcons.GroupPose, // Group Pose + }); + + var setInDuty = GetConditionSet(StatusIconPriorizerConditionSets.InDuty); + setInDuty.AddRange(new[] + { + StatusIcons.Disconnecting, // Disconnecting + StatusIcons.ViewingCutscene, // Viewing Cutscene + StatusIcons.Idle, // Idle + StatusIcons.GroupPose, // Group Pose + }); + + var setInForay = GetConditionSet(StatusIconPriorizerConditionSets.InForay); + setInForay.AddRange(new[] + { + // This allows you to see which players don't have a party + StatusIcons.InDuty, // In Duty + + StatusIcons.Disconnecting, // Disconnecting + StatusIcons.ViewingCutscene, // Viewing Cutscene + StatusIcons.Idle, // Idle + StatusIcons.GroupPose, // Group Pose + }); + } + } +} diff --git a/Pilz.Dalamud/Pilz.Dalamud.csproj b/Pilz.Dalamud/Pilz.Dalamud.csproj new file mode 100644 index 0000000..540e7f5 --- /dev/null +++ b/Pilz.Dalamud/Pilz.Dalamud.csproj @@ -0,0 +1,63 @@ + + + + net6.0-windows + enable + annotations + true + latest + false + false + x64 + + + + true + true + + + + $(appdata)\XIVLauncher\addon\Hooks\dev\ + False + Pilzinsel64 + https://github.com/Pilzinsel64/Pilz.Dalamud + https://github.com/Pilzinsel64/Pilz.Dalamud + git + 0.1.1.0 + 0.1.1.0 + 0.1.1 + True + + + + + $(DalamudLibPath)FFXIVClientStructs.dll + false + + + $(DalamudLibPath)Newtonsoft.Json.dll + false + + + $(DalamudLibPath)Dalamud.dll + false + + + $(DalamudLibPath)ImGui.NET.dll + false + + + $(DalamudLibPath)ImGuiScene.dll + false + + + $(DalamudLibPath)Lumina.dll + false + + + $(DalamudLibPath)Lumina.Excel.dll + false + + + + diff --git a/Pilz.Dalamud/PluginServices.cs b/Pilz.Dalamud/PluginServices.cs new file mode 100644 index 0000000..37302ed --- /dev/null +++ b/Pilz.Dalamud/PluginServices.cs @@ -0,0 +1,28 @@ +using Dalamud.Data; +using Dalamud.Game.ClientState; +using Dalamud.Game.ClientState.Objects; +using Dalamud.Game.Gui; +using Dalamud.IoC; +using Dalamud.Plugin; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Pilz.Dalamud +{ + public class PluginServices + { + [PluginService] public static GameGui GameGui { get; set; } = null; + [PluginService] public static DalamudPluginInterface PluginInterface { get; set; } = null; + [PluginService] public static ClientState ClientState { get; set; } = null; + [PluginService] public static DataManager DataManager { get; set; } = null; + [PluginService] public static ObjectTable ObjectTable { get; set; } = null; + + public static void Initialize(DalamudPluginInterface dalamudPluginInterface) + { + dalamudPluginInterface.Create(); + } + } +} diff --git a/Pilz.Dalamud/Tools/StatusIconFontConverter.cs b/Pilz.Dalamud/Tools/StatusIconFontConverter.cs new file mode 100644 index 0000000..d5ecff2 --- /dev/null +++ b/Pilz.Dalamud/Tools/StatusIconFontConverter.cs @@ -0,0 +1,41 @@ +using Dalamud.Game.Text.SeStringHandling; +using Pilz.Dalamud.Nameplates.Model; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Pilz.Dalamud.Tools +{ + public static class StatusIconFontConverter + { + public static StatusIcons? GetStatusIconFromBitmapFontIcon(BitmapFontIcon fontIcon) + { + return fontIcon switch + { + BitmapFontIcon.NewAdventurer => StatusIcons.NewAdventurer, + BitmapFontIcon.Mentor => StatusIcons.Mentor, + BitmapFontIcon.MentorPvE => StatusIcons.MentorPvE, + BitmapFontIcon.MentorCrafting => StatusIcons.MentorCrafting, + BitmapFontIcon.MentorPvP => StatusIcons.MentorPvP, + BitmapFontIcon.Returner => StatusIcons.Returner, + _ => null + }; + } + + public static BitmapFontIcon? GetBitmapFontIconFromStatusIcon(StatusIcons icon) + { + return icon switch + { + StatusIcons.NewAdventurer => BitmapFontIcon.NewAdventurer, + StatusIcons.Mentor => BitmapFontIcon.Mentor, + StatusIcons.MentorPvE => BitmapFontIcon.MentorPvE, + StatusIcons.MentorCrafting => BitmapFontIcon.MentorCrafting, + StatusIcons.MentorPvP => BitmapFontIcon.MentorPvP, + StatusIcons.Returner => BitmapFontIcon.Returner, + _ => null + }; + } + } +} diff --git a/Pilz.Dalamud/Tools/Strings/StringChange.cs b/Pilz.Dalamud/Tools/Strings/StringChange.cs new file mode 100644 index 0000000..f6a019e --- /dev/null +++ b/Pilz.Dalamud/Tools/Strings/StringChange.cs @@ -0,0 +1,24 @@ +using Dalamud.Game.Text.SeStringHandling; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Pilz.Dalamud.Tools.Strings +{ + public class StringChange + { + /// + /// The payloads to use for inserting/replacing. + /// + public List Payloads { get; init; } = new(); + + /// + /// Defines if only one anchor payload should be used, if using anchor payloads. + /// With this true the single anchor payload will be used in StringUpdateFactory instead of the anchor payload list. + /// Not needed to be true for the most cases. + /// + public bool ForceUsingSingleAnchorPayload { get; set; } = false; + } +} diff --git a/Pilz.Dalamud/Tools/Strings/StringChanges.cs b/Pilz.Dalamud/Tools/Strings/StringChanges.cs new file mode 100644 index 0000000..1ae85c0 --- /dev/null +++ b/Pilz.Dalamud/Tools/Strings/StringChanges.cs @@ -0,0 +1,40 @@ +using Dalamud.Game.Text.SeStringHandling; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Pilz.Dalamud.Tools.Strings +{ + public class StringChanges + { + private readonly Dictionary changes = new(); + + public StringChanges() + { + changes.Add(StringPosition.Before, new StringChange()); + changes.Add(StringPosition.After, new StringChange()); + changes.Add(StringPosition.Replace, new StringChange()); + } + + /// + /// Gets a change of the position of your choice where you can add your payloads. + /// + /// The position of your choice. + /// + public StringChange GetChange(StringPosition position) + { + return changes[position]; + } + + /// + /// Checks if there is any string change listed. + /// + /// + public bool Any() + { + return changes.Sum(n => n.Value.Payloads.Count) != 0; + } + } +} diff --git a/Pilz.Dalamud/Tools/Strings/StringChangesProps.cs b/Pilz.Dalamud/Tools/Strings/StringChangesProps.cs new file mode 100644 index 0000000..d0cd253 --- /dev/null +++ b/Pilz.Dalamud/Tools/Strings/StringChangesProps.cs @@ -0,0 +1,31 @@ +using Dalamud.Game.ClientState.Objects.Types; +using Dalamud.Game.Text.SeStringHandling; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Pilz.Dalamud.Tools.Strings +{ + public class StringChangesProps + { + /// + /// The string where the changes should be applied. + /// + public SeString Destination { get; set; } + /// + /// The changes that should be applied to the destination. + /// + public StringChanges StringChanges { get; set; } = new(); + /// + /// Payloads to use as anchor where the changes should be applied to. + /// + public List AnchorPayloads { get; set; } = new(); + /// + /// A single payload to use as anchor where the changes should be applied to. + /// This property will only be used if StringChange.ForceSingleAnchorPayload is true. + /// + public Payload AnchorPayload { get; set; } + } +} diff --git a/Pilz.Dalamud/Tools/Strings/StringPosition.cs b/Pilz.Dalamud/Tools/Strings/StringPosition.cs new file mode 100644 index 0000000..46a05db --- /dev/null +++ b/Pilz.Dalamud/Tools/Strings/StringPosition.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Pilz.Dalamud.Tools.Strings +{ + public enum StringPosition + { + Before, + After, + Replace + } +} diff --git a/Pilz.Dalamud/Tools/Strings/StringUpdateFactory.cs b/Pilz.Dalamud/Tools/Strings/StringUpdateFactory.cs new file mode 100644 index 0000000..ab3423a --- /dev/null +++ b/Pilz.Dalamud/Tools/Strings/StringUpdateFactory.cs @@ -0,0 +1,132 @@ +using Dalamud.Game.Text.SeStringHandling; +using Dalamud.Game.Text.SeStringHandling.Payloads; +using Lumina.Text; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Pilz.Dalamud.Tools.Strings +{ + public static class StringUpdateFactory + { + public static void ApplyStringChanges(StringChangesProps props) + { + if (props.StringChanges != null && props.StringChanges.Any()) + { + var seString = props.Destination; + List stringPositionsOrdered = GetOrderedStringPositions(props); + + foreach (var stringPosition in stringPositionsOrdered) + { + var stringChange = props.StringChanges.GetChange(stringPosition); + if (stringChange != null && stringChange.Payloads.Any()) + { + AddSpacesBetweenTextPayloads(stringChange.Payloads, stringPosition); + + if (stringPosition == StringPosition.Before) + { + Payload anchorFirst = stringChange.ForceUsingSingleAnchorPayload ? props.AnchorPayload : props.AnchorPayloads?.FirstOrDefault(); + + if (anchorFirst != null) + { + var anchorPayloadIndex = seString.Payloads.IndexOf(anchorFirst); + seString.Payloads.InsertRange(anchorPayloadIndex, stringChange.Payloads); + } + else + seString.Payloads.InsertRange(0, stringChange.Payloads); + } + else if (stringPosition == StringPosition.After) + { + Payload anchorLast = stringChange.ForceUsingSingleAnchorPayload ? props.AnchorPayload : props.AnchorPayloads?.LastOrDefault(); + + if (anchorLast != null) + { + var anchorPayloadIndex = seString.Payloads.IndexOf(anchorLast); + seString.Payloads.InsertRange(anchorPayloadIndex + 1, stringChange.Payloads); + } + else + seString.Payloads.AddRange(stringChange.Payloads); + } + else if (stringPosition == StringPosition.Replace) + { + Payload anchorReplace = props.AnchorPayload; + + if (anchorReplace != null) + { + var anchorPayloadIndex = seString.Payloads.IndexOf(anchorReplace); + seString.Payloads.InsertRange(anchorPayloadIndex, stringChange.Payloads); + seString.Remove(anchorReplace); + } + else + { + seString.Payloads.Clear(); + seString.Payloads.AddRange(stringChange.Payloads); + } + } + } + } + } + } + + private static void AddSpacesBetweenTextPayloads(List payloads, StringPosition tagPosition) + { + if (payloads != null && payloads.Any()) + { + var indicesToInsertSpacesAt = new List(); + var lastTextPayloadIndex = -1; + + static TextPayload getNewTextPayload() => new(" "); + + foreach (var payload in payloads.Reverse()) + { + if (payload is IconPayload iconPayload) + lastTextPayloadIndex = -1; + else if (payload is TextPayload textPayload) + { + if (lastTextPayloadIndex != -1) + indicesToInsertSpacesAt.Add(payloads.IndexOf(textPayload) + 1); + lastTextPayloadIndex = payloads.IndexOf(textPayload); + } + } + + foreach (var indexToInsertSpaceAt in indicesToInsertSpacesAt) + payloads.Insert(indexToInsertSpaceAt, getNewTextPayload()); + + // Decide whether to add a space to the end + if (tagPosition == StringPosition.Before) + { + var significantPayloads = payloads.Where(payload => payload is TextPayload || payload is IconPayload); + if (significantPayloads.Last() is TextPayload) + payloads.Add(getNewTextPayload()); + } + // Decide whether to add a space to the beginning + else if (tagPosition == StringPosition.After) + { + var significantPayloads = payloads.Where(payload => payload is TextPayload || payload is IconPayload); + if (significantPayloads.First() is TextPayload) + payloads.Insert(0, getNewTextPayload()); + } + } + } + + private static List GetOrderedStringPositions(StringChangesProps props) + { + var tagPositionsOrdered = new List(); + + // If there's no anchor payload, do replaces first so that befores and afters are based on the replaced data + if (props.AnchorPayloads == null || !props.AnchorPayloads.Any()) + tagPositionsOrdered.Add(StringPosition.Replace); + + tagPositionsOrdered.Add(StringPosition.Before); + tagPositionsOrdered.Add(StringPosition.After); + + // If there is an anchor payload, do replaces last so that we still know which payload needs to be removed + if (props.AnchorPayloads != null && props.AnchorPayloads.Any()) + tagPositionsOrdered.Add(StringPosition.Replace); + + return tagPositionsOrdered; + } + } +} diff --git a/Pilz.Dalamud/XivApi.cs b/Pilz.Dalamud/XivApi.cs new file mode 100644 index 0000000..5d2a35c --- /dev/null +++ b/Pilz.Dalamud/XivApi.cs @@ -0,0 +1,39 @@ +using FFXIVClientStructs.FFXIV.Client.System.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Pilz.Dalamud.Nameplates.Model; + +namespace Pilz.Dalamud +{ + public class XivApi + { + private static IntPtr _RaptureAtkModulePtr = IntPtr.Zero; + + public static IntPtr RaptureAtkModulePtr + { + get + { + if (_RaptureAtkModulePtr == IntPtr.Zero) + { + unsafe + { + var framework = Framework.Instance(); + var uiModule = framework->GetUiModule(); + + _RaptureAtkModulePtr = new IntPtr(uiModule->GetRaptureAtkModule()); + } + } + + return _RaptureAtkModulePtr; + } + } + + public static SafeAddonNameplate GetSafeAddonNamePlate() + { + return new(PluginServices.PluginInterface); + } + } +} diff --git a/Pilz.Dalamud/packages.lock.json b/Pilz.Dalamud/packages.lock.json new file mode 100644 index 0000000..8ee9f35 --- /dev/null +++ b/Pilz.Dalamud/packages.lock.json @@ -0,0 +1,6 @@ +{ + "version": 1, + "dependencies": { + "net6.0-windows7.0": {} + } +} \ No newline at end of file diff --git a/PlayerTags.sln b/PlayerTags.sln index d0f9aff..72c2a81 100644 --- a/PlayerTags.sln +++ b/PlayerTags.sln @@ -5,6 +5,8 @@ VisualStudioVersion = 17.3.32929.385 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PlayerTags", "PlayerTags\PlayerTags.csproj", "{13C812E9-0D42-4B95-8646-40EEBF30636F}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Pilz.Dalamud", "Pilz.Dalamud\Pilz.Dalamud.csproj", "{D0362D71-E77F-4739-80BE-CD4454188B8F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -15,6 +17,10 @@ Global {13C812E9-0D42-4B95-8646-40EEBF30636F}.Debug|x64.Build.0 = Debug|x64 {13C812E9-0D42-4B95-8646-40EEBF30636F}.Release|x64.ActiveCfg = Release|x64 {13C812E9-0D42-4B95-8646-40EEBF30636F}.Release|x64.Build.0 = Release|x64 + {D0362D71-E77F-4739-80BE-CD4454188B8F}.Debug|x64.ActiveCfg = Debug|x64 + {D0362D71-E77F-4739-80BE-CD4454188B8F}.Debug|x64.Build.0 = Debug|x64 + {D0362D71-E77F-4739-80BE-CD4454188B8F}.Release|x64.ActiveCfg = Release|x64 + {D0362D71-E77F-4739-80BE-CD4454188B8F}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/PlayerTags/PlayerTags.csproj b/PlayerTags/PlayerTags.csproj index 90b7b1d..55a6599 100644 --- a/PlayerTags/PlayerTags.csproj +++ b/PlayerTags/PlayerTags.csproj @@ -26,7 +26,7 @@ - + $(DalamudLibPath)FFXIVClientStructs.dll false diff --git a/PlayerTags/packages.lock.json b/PlayerTags/packages.lock.json index fc36ed7..d25458d 100644 --- a/PlayerTags/packages.lock.json +++ b/PlayerTags/packages.lock.json @@ -14,11 +14,8 @@ "resolved": "2.1.8", "contentHash": "YqagNXs9InxmqkXzq7kLveImxnodkBEicAhydMXVp7dFjC7xb76U6zGgAax4/BWIWfZeWzr5DJyQSev31kj81A==" }, - "Pilz.Dalamud": { - "type": "Direct", - "requested": "[0.1.0, )", - "resolved": "0.1.0", - "contentHash": "n22WXrCzA+EAQHi4ve4PgTZYDzr0f0qsQ/1ApBBQjoCKJcwFox2rO692/2davRWiUQiNv9B56ikcnHocLp75tQ==" + "pilz.dalamud": { + "type": "Project" } } } From 1014fcc9f3b76464fe4f42f890176eb4f11331dc Mon Sep 17 00:00:00 2001 From: Pilzinsel64 Date: Sun, 13 Nov 2022 21:16:45 +0100 Subject: [PATCH 19/36] handle depricated properties for "IsIconVisibleInChat" and "IsIconVisibleInNameplate" --- PlayerTags/Data/Tag.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/PlayerTags/Data/Tag.cs b/PlayerTags/Data/Tag.cs index a168890..9774806 100644 --- a/PlayerTags/Data/Tag.cs +++ b/PlayerTags/Data/Tag.cs @@ -107,6 +107,18 @@ namespace PlayerTags.Data public InheritableValue CustomId = new InheritableValue(Guid.Empty); + [JsonProperty, Obsolete] + private InheritableValue IsIconVisibleInChat + { + set => IsRoleIconVisibleInChat = value; + } + + [JsonProperty, Obsolete] + private InheritableValue IsIconVisibleInNameplate + { + set => IsRoleIconVisibleInNameplates = value; + } + [InheritableCategory("IconCategory")] public InheritableValue Icon = new InheritableValue(BitmapFontIcon.Aethernet); [InheritableCategory("IconCategory")] From 972f9e88196bb59fb2b26baadd9fa74ed2665b2e Mon Sep 17 00:00:00 2001 From: Pilzinsel64 Date: Sun, 13 Nov 2022 21:17:19 +0100 Subject: [PATCH 20/36] change version to v1.8.0.1 --- PlayerTags/PlayerTags.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PlayerTags/PlayerTags.csproj b/PlayerTags/PlayerTags.csproj index 55a6599..dad6506 100644 --- a/PlayerTags/PlayerTags.csproj +++ b/PlayerTags/PlayerTags.csproj @@ -1,7 +1,7 @@  r00telement;Pilzinsel64 - 1.8.0.0 + 1.8.0.1 From 3849644e04164f9276ff0c209ff2b94446b683fa Mon Sep 17 00:00:00 2001 From: Pilzinsel64 Date: Tue, 15 Nov 2022 19:56:58 +0100 Subject: [PATCH 21/36] add dead player handling (backend) --- .../Configuration/PluginConfiguration.cs | 2 +- PlayerTags/Data/DeadPlayerHandling.cs | 15 +++++ .../Features/NameplateTagTargetFeature.cs | 63 +++++++++++++++++-- 3 files changed, 73 insertions(+), 7 deletions(-) create mode 100644 PlayerTags/Data/DeadPlayerHandling.cs diff --git a/PlayerTags/Configuration/PluginConfiguration.cs b/PlayerTags/Configuration/PluginConfiguration.cs index 5340178..a0aabeb 100644 --- a/PlayerTags/Configuration/PluginConfiguration.cs +++ b/PlayerTags/Configuration/PluginConfiguration.cs @@ -284,7 +284,7 @@ namespace PlayerTags.Configuration public NameplateFreeCompanyVisibility NameplateFreeCompanyVisibility = NameplateFreeCompanyVisibility.Default; public NameplateTitleVisibility NameplateTitleVisibility = NameplateTitleVisibility.WhenHasTags; public NameplateTitlePosition NameplateTitlePosition = NameplateTitlePosition.AlwaysAboveName; - + public DeadPlayerHandling NameplateDeadPlayerHandling = DeadPlayerHandling.Include; public bool IsApplyTagsToAllChatMessagesEnabled = true; } } diff --git a/PlayerTags/Data/DeadPlayerHandling.cs b/PlayerTags/Data/DeadPlayerHandling.cs new file mode 100644 index 0000000..9fd6883 --- /dev/null +++ b/PlayerTags/Data/DeadPlayerHandling.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PlayerTags.Data +{ + public enum DeadPlayerHandling + { + Ignore, + Include, + GrayOut + } +} diff --git a/PlayerTags/Features/NameplateTagTargetFeature.cs b/PlayerTags/Features/NameplateTagTargetFeature.cs index c73205f..32f10dc 100644 --- a/PlayerTags/Features/NameplateTagTargetFeature.cs +++ b/PlayerTags/Features/NameplateTagTargetFeature.cs @@ -2,6 +2,9 @@ using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Game.Text.SeStringHandling; using Dalamud.Game.Text.SeStringHandling.Payloads; +using FFXIVClientStructs.FFXIV.Client.Game.Character; +using FFXIVClientStructs.FFXIV.Client.System.Memory; +using FFXIVClientStructs.FFXIV.Component.GUI; using Lumina.Excel.GeneratedSheets; using Pilz.Dalamud.Icons; using Pilz.Dalamud.Nameplates.Tools; @@ -106,13 +109,37 @@ namespace PlayerTags.Features return false; } - private void Nameplate_PlayerNameplateUpdated(PlayerNameplateUpdatedArgs args) + private unsafe void Nameplate_PlayerNameplateUpdated(PlayerNameplateUpdatedArgs args) { var beforeTitleBytes = args.Title.Encode(); var iconID = args.IconId; var generalOptions = m_PluginConfiguration.GeneralOptions[ActivityContextManager.CurrentActivityContext.ActivityType]; + var applyTags = false; + var grayOut = false; + + if (args.PlayerCharacter != null) + { + if (args.PlayerCharacter.IsDead) + { + switch (generalOptions.NameplateDeadPlayerHandling) + { + case DeadPlayerHandling.Include: + applyTags = true; + break; + case DeadPlayerHandling.GrayOut: + grayOut = true; + break; + } + } + else + applyTags = true; + } + + if (applyTags) + AddTagsToNameplate(args.PlayerCharacter, args.Name, args.Title, args.FreeCompany, ref iconID); + else if(grayOut) + GrayOutNameplate(args.PlayerCharacter, args.Name, args.Title, args.FreeCompany, ref iconID); - AddTagsToNameplate(args.PlayerCharacter, args.Name, args.Title, args.FreeCompany, ref iconID); args.IconId = iconID; if (generalOptions.NameplateTitlePosition == NameplateTitlePosition.AlwaysAboveName) @@ -150,6 +177,17 @@ namespace PlayerTags.Features } } + private NameplateChanges GenerateEmptyNameplateChanges(SeString name, SeString title, SeString freeCompany) + { + NameplateChanges nameplateChanges = new(); + + nameplateChanges.GetProps(NameplateElements.Name).Destination = name; + nameplateChanges.GetProps(NameplateElements.Title).Destination = title; + nameplateChanges.GetProps(NameplateElements.FreeCompany).Destination = freeCompany; + + return nameplateChanges; + } + /// /// Adds tags to the nameplate of a game object. /// @@ -160,10 +198,7 @@ namespace PlayerTags.Features private void AddTagsToNameplate(GameObject gameObject, SeString name, SeString title, SeString freeCompany, ref int statusIcon) { int? newStatusIcon = null; - NameplateChanges nameplateChanges = new(); - nameplateChanges.GetProps(NameplateElements.Name).Destination = name; - nameplateChanges.GetProps(NameplateElements.Title).Destination = title; - nameplateChanges.GetProps(NameplateElements.FreeCompany).Destination = freeCompany; + NameplateChanges nameplateChanges = GenerateEmptyNameplateChanges(name, title, freeCompany); if (gameObject is PlayerCharacter playerCharacter) { @@ -244,6 +279,22 @@ namespace PlayerTags.Features } } + protected void GrayOutNameplate(GameObject gameObject, SeString name, SeString title, SeString freeCompany, ref int statusIcon) + { + if (gameObject is PlayerCharacter playerCharacter) + { + NameplateChanges nameplateChanges = GenerateEmptyNameplateChanges(name, title, freeCompany); + + foreach (NameplateElements element in Enum.GetValues()) + { + nameplateChanges.GetChange(element, StringPosition.Before).Payloads.Add(new UIForegroundPayload(3)); + nameplateChanges.GetChange(element, StringPosition.After).Payloads.Add(new UIForegroundPayload(0)); + } + + ApplyNameplateChanges(nameplateChanges); + } + } + protected void ApplyNameplateChanges(NameplateChanges nameplateChanges) { var props = new NameplateChangesProps From 2fa6d6b94fd6545e0ce84fee43e91b92d2cbe5ef Mon Sep 17 00:00:00 2001 From: Pilzinsel64 Date: Tue, 15 Nov 2022 20:00:02 +0100 Subject: [PATCH 22/36] fix not using icon set from tag --- PlayerTags/Features/NameplateTagTargetFeature.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PlayerTags/Features/NameplateTagTargetFeature.cs b/PlayerTags/Features/NameplateTagTargetFeature.cs index 32f10dc..e2a185d 100644 --- a/PlayerTags/Features/NameplateTagTargetFeature.cs +++ b/PlayerTags/Features/NameplateTagTargetFeature.cs @@ -242,7 +242,7 @@ namespace PlayerTags.Features AddPayloadChanges(tag.TagTargetInNameplates.InheritedValue.Value, tag.TagPositionInNameplates.InheritedValue.Value, payloads, nameplateChanges, false); } if (newStatusIcon == null && classJob != null && (tag.IsJobIconVisibleInNameplates?.InheritedValue ?? false)) - newStatusIcon = jobIconSets.GetJobIcon(JobIconSetName.Framed, classJob.Id); + newStatusIcon = jobIconSets.GetJobIcon(tag.JobIconSet?.InheritedValue ?? JobIconSetName.Framed, classJob.Id); } } From 4764a354397cb7849361177d4f40cbcb774837a0 Mon Sep 17 00:00:00 2001 From: Pilzinsel64 Date: Tue, 15 Nov 2022 20:01:27 +0100 Subject: [PATCH 23/36] fix status icon is visible if tag is not --- PlayerTags/Features/NameplateTagTargetFeature.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PlayerTags/Features/NameplateTagTargetFeature.cs b/PlayerTags/Features/NameplateTagTargetFeature.cs index e2a185d..2970a8c 100644 --- a/PlayerTags/Features/NameplateTagTargetFeature.cs +++ b/PlayerTags/Features/NameplateTagTargetFeature.cs @@ -241,7 +241,7 @@ namespace PlayerTags.Features if (payloads.Any()) AddPayloadChanges(tag.TagTargetInNameplates.InheritedValue.Value, tag.TagPositionInNameplates.InheritedValue.Value, payloads, nameplateChanges, false); } - if (newStatusIcon == null && classJob != null && (tag.IsJobIconVisibleInNameplates?.InheritedValue ?? false)) + if (IsTagVisible(tag, gameObject) && newStatusIcon == null && classJob != null && (tag.IsJobIconVisibleInNameplates?.InheritedValue ?? false)) newStatusIcon = jobIconSets.GetJobIcon(tag.JobIconSet?.InheritedValue ?? JobIconSetName.Framed, classJob.Id); } } From 649c75afff212d54dd473949aa9f0f9f6b15adff Mon Sep 17 00:00:00 2001 From: Pilzinsel64 Date: Tue, 15 Nov 2022 20:09:35 +0100 Subject: [PATCH 24/36] fix status icon priorizing doesn't work --- PlayerTags/Features/NameplateTagTargetFeature.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/PlayerTags/Features/NameplateTagTargetFeature.cs b/PlayerTags/Features/NameplateTagTargetFeature.cs index 2970a8c..044d0ac 100644 --- a/PlayerTags/Features/NameplateTagTargetFeature.cs +++ b/PlayerTags/Features/NameplateTagTargetFeature.cs @@ -250,7 +250,8 @@ namespace PlayerTags.Features if (newStatusIcon != null) { var change = nameplateChanges.GetChange(NameplateElements.Name, StringPosition.Before); - NameplateUpdateFactory.ApplyStatusIconWithPrio(ref statusIcon, (int)newStatusIcon, change, ActivityContextManager.CurrentActivityContext, statusiconPriorizer); + if (NameplateUpdateFactory.ApplyStatusIconWithPrio(ref statusIcon, (int)newStatusIcon, change, ActivityContextManager.CurrentActivityContext, statusiconPriorizer)) + statusIcon = (int)newStatusIcon; } // Build the final strings out of the payloads From 5eaa7d147adb021016f584252365d1bb5eccf3ff Mon Sep 17 00:00:00 2001 From: Pilzinsel64 Date: Tue, 15 Nov 2022 20:20:02 +0100 Subject: [PATCH 25/36] add ui for dead player handling --- .../Configuration/PluginConfigurationUI.cs | 4 ++ PlayerTags/Resources/Strings.Designer.cs | 63 +++++++++++++++++++ PlayerTags/Resources/Strings.resx | 21 +++++++ 3 files changed, 88 insertions(+) diff --git a/PlayerTags/Configuration/PluginConfigurationUI.cs b/PlayerTags/Configuration/PluginConfigurationUI.cs index 7dfa20a..a0577e6 100644 --- a/PlayerTags/Configuration/PluginConfigurationUI.cs +++ b/PlayerTags/Configuration/PluginConfigurationUI.cs @@ -87,6 +87,7 @@ namespace PlayerTags.Configuration DrawComboBox(true, true, false, ref propertyProxy.NameplateFreeCompanyVisibility, () => SaveSettings(true)); DrawComboBox(true, true, false, ref propertyProxy.NameplateTitleVisibility, () => SaveSettings(true)); DrawComboBox(true, true, false, ref propertyProxy.NameplateTitlePosition, () => SaveSettings(true)); + DrawComboBox(true, true, false, ref propertyProxy.NameplateDeadPlayerHandling, () => SaveSettings(true)); ImGui.Spacing(); ImGui.Spacing(); @@ -1367,6 +1368,7 @@ namespace PlayerTags.Configuration public NameplateFreeCompanyVisibility NameplateFreeCompanyVisibility; public NameplateTitleVisibility NameplateTitleVisibility; public NameplateTitlePosition NameplateTitlePosition; + public DeadPlayerHandling NameplateDeadPlayerHandling; public bool IsApplyTagsToAllChatMessagesEnabled; public PropertyProxy(PluginConfiguration config) @@ -1381,6 +1383,7 @@ namespace PlayerTags.Configuration NameplateFreeCompanyVisibility = pluginConfig.GeneralOptions[currentActivityContext].NameplateFreeCompanyVisibility; NameplateTitleVisibility = pluginConfig.GeneralOptions[currentActivityContext].NameplateTitleVisibility; NameplateTitlePosition = pluginConfig.GeneralOptions[currentActivityContext].NameplateTitlePosition; + NameplateDeadPlayerHandling = pluginConfig.GeneralOptions[currentActivityContext].NameplateDeadPlayerHandling; IsApplyTagsToAllChatMessagesEnabled = pluginConfig.GeneralOptions[currentActivityContext].IsApplyTagsToAllChatMessagesEnabled; } @@ -1403,6 +1406,7 @@ namespace PlayerTags.Configuration pluginConfig.GeneralOptions[key].NameplateFreeCompanyVisibility = NameplateFreeCompanyVisibility; pluginConfig.GeneralOptions[key].NameplateTitleVisibility = NameplateTitleVisibility; pluginConfig.GeneralOptions[key].NameplateTitlePosition = NameplateTitlePosition; + pluginConfig.GeneralOptions[key].NameplateDeadPlayerHandling = NameplateDeadPlayerHandling; pluginConfig.GeneralOptions[key].IsApplyTagsToAllChatMessagesEnabled = IsApplyTagsToAllChatMessagesEnabled; } } diff --git a/PlayerTags/Resources/Strings.Designer.cs b/PlayerTags/Resources/Strings.Designer.cs index b2f08bf..9d24be5 100644 --- a/PlayerTags/Resources/Strings.Designer.cs +++ b/PlayerTags/Resources/Strings.Designer.cs @@ -204,6 +204,69 @@ namespace PlayerTags.Resources { } } + /// + /// Sucht eine lokalisierte Zeichenfolge, die Handling for dead players ähnelt. + /// + public static string Loc_DeadPlayerHandling { + get { + return ResourceManager.GetString("Loc_DeadPlayerHandling", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Gray out ähnelt. + /// + public static string Loc_DeadPlayerHandling_GrayOut { + get { + return ResourceManager.GetString("Loc_DeadPlayerHandling_GrayOut", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Gray out the nameplate of dead players and don't apply any tag for. ähnelt. + /// + public static string Loc_DeadPlayerHandling_GrayOut_Description { + get { + return ResourceManager.GetString("Loc_DeadPlayerHandling_GrayOut_Description", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Ignore ähnelt. + /// + public static string Loc_DeadPlayerHandling_Ignore { + get { + return ResourceManager.GetString("Loc_DeadPlayerHandling_Ignore", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Don't process dead players and don't apply any tag for. ähnelt. + /// + public static string Loc_DeadPlayerHandling_Ignore_Description { + get { + return ResourceManager.GetString("Loc_DeadPlayerHandling_Ignore_Description", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Include ähnelt. + /// + public static string Loc_DeadPlayerHandling_Include { + get { + return ResourceManager.GetString("Loc_DeadPlayerHandling_Include", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Handle dead players as they are alive. No difference between dead and alive players. ähnelt. + /// + public static string Loc_DeadPlayerHandling_Include_Description { + get { + return ResourceManager.GetString("Loc_DeadPlayerHandling_Include_Description", resourceCulture); + } + } + /// /// Sucht eine lokalisierte Zeichenfolge, die Template ähnelt. /// diff --git a/PlayerTags/Resources/Strings.resx b/PlayerTags/Resources/Strings.resx index 6629cd8..5763f92 100644 --- a/PlayerTags/Resources/Strings.resx +++ b/PlayerTags/Resources/Strings.resx @@ -792,4 +792,25 @@ Use this if you want to to have every option under your control or just want to Shows the job tag with color and the role icon by replacing the title. + + Gray out + + + Ignore + + + Include + + + Handling for dead players + + + Gray out the nameplate of dead players and don't apply any tag for. + + + Don't process dead players and don't apply any tag for. + + + Handle dead players as they are alive. No difference between dead and alive players. + \ No newline at end of file From 6ec3a1a97077e305f5c95f79294f860366d15206 Mon Sep 17 00:00:00 2001 From: Pilzinsel64 Date: Tue, 15 Nov 2022 20:22:47 +0100 Subject: [PATCH 26/36] version 1.8.0.2 --- PlayerTags/PlayerTags.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PlayerTags/PlayerTags.csproj b/PlayerTags/PlayerTags.csproj index dad6506..a07f0c2 100644 --- a/PlayerTags/PlayerTags.csproj +++ b/PlayerTags/PlayerTags.csproj @@ -1,7 +1,7 @@  r00telement;Pilzinsel64 - 1.8.0.1 + 1.8.0.2 From 8573555af79eb976a547b0cbba45c98d2860ac0f Mon Sep 17 00:00:00 2001 From: Pilzinsel64 Date: Tue, 15 Nov 2022 21:06:50 +0100 Subject: [PATCH 27/36] finally fix status icon priorizing & add new option --- .../Tools/NameplateUpdateFactory.cs | 22 +++++++++++-------- .../Tools/StatusIconPriorizerSettings.cs | 18 +++++++++++++++ .../Configuration/PluginConfiguration.cs | 1 + .../Configuration/PluginConfigurationUI.cs | 2 ++ .../Features/NameplateTagTargetFeature.cs | 7 +++--- PlayerTags/Resources/Strings.Designer.cs | 18 +++++++++++++++ PlayerTags/Resources/Strings.resx | 6 +++++ 7 files changed, 61 insertions(+), 13 deletions(-) diff --git a/Pilz.Dalamud/Nameplates/Tools/NameplateUpdateFactory.cs b/Pilz.Dalamud/Nameplates/Tools/NameplateUpdateFactory.cs index b04f947..ebc9518 100644 --- a/Pilz.Dalamud/Nameplates/Tools/NameplateUpdateFactory.cs +++ b/Pilz.Dalamud/Nameplates/Tools/NameplateUpdateFactory.cs @@ -23,26 +23,30 @@ namespace Pilz.Dalamud.Nameplates.Tools } } - public static bool ApplyStatusIconWithPrio(ref int statusIcon, int newStatusIcon, StringChange stringChange, ActivityContext activityContext, StatusIconPriorizer priorizer) + public static bool ApplyStatusIconWithPrio(ref int statusIcon, int newStatusIcon, StringChange stringChange, ActivityContext activityContext, StatusIconPriorizer priorizer, bool moveIconToNameplateIfPossible) { - var isPrio = priorizer.IsPriorityIcon(statusIcon, activityContext); - - if (!isPrio) + bool? isPrio = null; + var fontIcon = StatusIconFontConverter.GetBitmapFontIconFromStatusIcon((StatusIcons)statusIcon); + + if (moveIconToNameplateIfPossible) { - var fontIcon = StatusIconFontConverter.GetBitmapFontIconFromStatusIcon((StatusIcons)statusIcon); - if (fontIcon != null) { // Set new font icon as string change var iconPayload = new IconPayload(fontIcon.Value); stringChange.Payloads.Insert(0, iconPayload); - // Use new status icon as status icon - statusIcon = newStatusIcon; + // If we moved it, we don't need it as icon anymore, yay :D + isPrio = false; } } - return isPrio; + isPrio ??= priorizer.IsPriorityIcon(statusIcon, activityContext); + + if (!isPrio.Value) + statusIcon = newStatusIcon; + + return isPrio.Value; } } } diff --git a/Pilz.Dalamud/Nameplates/Tools/StatusIconPriorizerSettings.cs b/Pilz.Dalamud/Nameplates/Tools/StatusIconPriorizerSettings.cs index 1222e6c..a5bfe34 100644 --- a/Pilz.Dalamud/Nameplates/Tools/StatusIconPriorizerSettings.cs +++ b/Pilz.Dalamud/Nameplates/Tools/StatusIconPriorizerSettings.cs @@ -65,6 +65,12 @@ namespace Pilz.Dalamud.Nameplates.Tools StatusIcons.PartyMember, // Party Member StatusIcons.RolePlaying, // Role Playing StatusIcons.GroupPose, // Group Pose + StatusIcons.Mentor, + StatusIcons.MentorCrafting, + StatusIcons.MentorPvE, + StatusIcons.MentorPvP, + StatusIcons.Returner, + StatusIcons.NewAdventurer, }); var setInDuty = GetConditionSet(StatusIconPriorizerConditionSets.InDuty); @@ -74,6 +80,12 @@ namespace Pilz.Dalamud.Nameplates.Tools StatusIcons.ViewingCutscene, // Viewing Cutscene StatusIcons.Idle, // Idle StatusIcons.GroupPose, // Group Pose + StatusIcons.Mentor, + StatusIcons.MentorCrafting, + StatusIcons.MentorPvE, + StatusIcons.MentorPvP, + StatusIcons.Returner, + StatusIcons.NewAdventurer, }); var setInForay = GetConditionSet(StatusIconPriorizerConditionSets.InForay); @@ -86,6 +98,12 @@ namespace Pilz.Dalamud.Nameplates.Tools StatusIcons.ViewingCutscene, // Viewing Cutscene StatusIcons.Idle, // Idle StatusIcons.GroupPose, // Group Pose + StatusIcons.Mentor, + StatusIcons.MentorCrafting, + StatusIcons.MentorPvE, + StatusIcons.MentorPvP, + StatusIcons.Returner, + StatusIcons.NewAdventurer, }); } } diff --git a/PlayerTags/Configuration/PluginConfiguration.cs b/PlayerTags/Configuration/PluginConfiguration.cs index a0aabeb..e04432f 100644 --- a/PlayerTags/Configuration/PluginConfiguration.cs +++ b/PlayerTags/Configuration/PluginConfiguration.cs @@ -34,6 +34,7 @@ namespace PlayerTags.Configuration public DefaultPluginDataTemplate DefaultPluginDataTemplate = DefaultPluginDataTemplate.Simple; public StatusIconPriorizerSettings StatusIconPriorizerSettings = new(true); + public bool MoveStatusIconToNameplateTextIfPossible = true; public bool IsPlayerNameRandomlyGenerated = false; public bool IsCustomTagsContextMenuEnabled = true; public bool IsShowInheritedPropertiesEnabled = true; diff --git a/PlayerTags/Configuration/PluginConfigurationUI.cs b/PlayerTags/Configuration/PluginConfigurationUI.cs index a0577e6..cbc334e 100644 --- a/PlayerTags/Configuration/PluginConfigurationUI.cs +++ b/PlayerTags/Configuration/PluginConfigurationUI.cs @@ -266,6 +266,8 @@ namespace PlayerTags.Configuration SaveSettings(); }); + DrawCheckbox(nameof(PluginConfiguration.MoveStatusIconToNameplateTextIfPossible), true, ref m_PluginConfiguration.MoveStatusIconToNameplateTextIfPossible, () => SaveSettings()); + if (isPriorizerEnabled) { var statusIcons = Enum.GetValues(); diff --git a/PlayerTags/Features/NameplateTagTargetFeature.cs b/PlayerTags/Features/NameplateTagTargetFeature.cs index 044d0ac..09786f4 100644 --- a/PlayerTags/Features/NameplateTagTargetFeature.cs +++ b/PlayerTags/Features/NameplateTagTargetFeature.cs @@ -136,7 +136,7 @@ namespace PlayerTags.Features } if (applyTags) - AddTagsToNameplate(args.PlayerCharacter, args.Name, args.Title, args.FreeCompany, ref iconID); + AddTagsToNameplate(args.PlayerCharacter, args.Name, args.Title, args.FreeCompany, ref iconID, generalOptions); else if(grayOut) GrayOutNameplate(args.PlayerCharacter, args.Name, args.Title, args.FreeCompany, ref iconID); @@ -195,7 +195,7 @@ namespace PlayerTags.Features /// The name text to change. /// The title text to change. /// The free company text to change. - private void AddTagsToNameplate(GameObject gameObject, SeString name, SeString title, SeString freeCompany, ref int statusIcon) + private void AddTagsToNameplate(GameObject gameObject, SeString name, SeString title, SeString freeCompany, ref int statusIcon, GeneralOptionsClass generalOptions) { int? newStatusIcon = null; NameplateChanges nameplateChanges = GenerateEmptyNameplateChanges(name, title, freeCompany); @@ -250,8 +250,7 @@ namespace PlayerTags.Features if (newStatusIcon != null) { var change = nameplateChanges.GetChange(NameplateElements.Name, StringPosition.Before); - if (NameplateUpdateFactory.ApplyStatusIconWithPrio(ref statusIcon, (int)newStatusIcon, change, ActivityContextManager.CurrentActivityContext, statusiconPriorizer)) - statusIcon = (int)newStatusIcon; + NameplateUpdateFactory.ApplyStatusIconWithPrio(ref statusIcon, (int)newStatusIcon, change, ActivityContextManager.CurrentActivityContext, statusiconPriorizer, m_PluginConfiguration.MoveStatusIconToNameplateTextIfPossible); } // Build the final strings out of the payloads diff --git a/PlayerTags/Resources/Strings.Designer.cs b/PlayerTags/Resources/Strings.Designer.cs index 9d24be5..01912cb 100644 --- a/PlayerTags/Resources/Strings.Designer.cs +++ b/PlayerTags/Resources/Strings.Designer.cs @@ -1094,6 +1094,24 @@ namespace PlayerTags.Resources { } } + /// + /// Sucht eine lokalisierte Zeichenfolge, die Move Status Icon to Nameplate if possible ähnelt. + /// + public static string Loc_MoveStatusIconToNameplateTextIfPossible { + get { + return ResourceManager.GetString("Loc_MoveStatusIconToNameplateTextIfPossible", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die If the current status icon is available as Font Icon then move it to the Player Name text in the Nameplate, so there is place for another icon to use, e.g. the job icon. ähnelt. + /// + public static string Loc_MoveStatusIconToNameplateTextIfPossible_Description { + get { + return ResourceManager.GetString("Loc_MoveStatusIconToNameplateTextIfPossible_Description", resourceCulture); + } + } + /// /// Sucht eine lokalisierte Zeichenfolge, die Nameplate properties ähnelt. /// diff --git a/PlayerTags/Resources/Strings.resx b/PlayerTags/Resources/Strings.resx index 5763f92..2d9c1a8 100644 --- a/PlayerTags/Resources/Strings.resx +++ b/PlayerTags/Resources/Strings.resx @@ -813,4 +813,10 @@ Use this if you want to to have every option under your control or just want to Handle dead players as they are alive. No difference between dead and alive players. + + Move Status Icon to Nameplate if possible + + + If the current status icon is available as Font Icon then move it to the Player Name text in the Nameplate, so there is place for another icon to use, e.g. the job icon. + \ No newline at end of file From dc365bfcb5a3c1fd54c23abcb2f91ecf421e2277 Mon Sep 17 00:00:00 2001 From: Pilzinsel64 Date: Tue, 15 Nov 2022 21:09:36 +0100 Subject: [PATCH 28/36] v1.8.0.3 --- PlayerTags/PlayerTags.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PlayerTags/PlayerTags.csproj b/PlayerTags/PlayerTags.csproj index a07f0c2..aef4a18 100644 --- a/PlayerTags/PlayerTags.csproj +++ b/PlayerTags/PlayerTags.csproj @@ -1,7 +1,7 @@  r00telement;Pilzinsel64 - 1.8.0.2 + 1.8.0.3 From 17fff4449bf20ee6ec8bdcb5e0fb51edeb1caebe Mon Sep 17 00:00:00 2001 From: Pilzinsel64 Date: Tue, 15 Nov 2022 21:09:42 +0100 Subject: [PATCH 29/36] fix naming --- PlayerTags/Resources/Strings.Designer.cs | 2 +- PlayerTags/Resources/Strings.resx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/PlayerTags/Resources/Strings.Designer.cs b/PlayerTags/Resources/Strings.Designer.cs index 01912cb..94f850b 100644 --- a/PlayerTags/Resources/Strings.Designer.cs +++ b/PlayerTags/Resources/Strings.Designer.cs @@ -1095,7 +1095,7 @@ namespace PlayerTags.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Move Status Icon to Nameplate if possible ähnelt. + /// Sucht eine lokalisierte Zeichenfolge, die Move Status Icon to Nameplate text if possible ähnelt. /// public static string Loc_MoveStatusIconToNameplateTextIfPossible { get { diff --git a/PlayerTags/Resources/Strings.resx b/PlayerTags/Resources/Strings.resx index 2d9c1a8..6f9fd7b 100644 --- a/PlayerTags/Resources/Strings.resx +++ b/PlayerTags/Resources/Strings.resx @@ -814,7 +814,7 @@ Use this if you want to to have every option under your control or just want to Handle dead players as they are alive. No difference between dead and alive players. - Move Status Icon to Nameplate if possible + Move Status Icon to Nameplate text if possible If the current status icon is available as Font Icon then move it to the Player Name text in the Nameplate, so there is place for another icon to use, e.g. the job icon. From 14bb07f7abbd1ffcdec6fd75a36140941838623e Mon Sep 17 00:00:00 2001 From: Pilzinsel64 Date: Wed, 16 Nov 2022 07:30:21 +0100 Subject: [PATCH 30/36] use gray color at text formatting applying --- .../Features/NameplateTagTargetFeature.cs | 52 ++++--------------- PlayerTags/Features/TagTargetFeature.cs | 7 +-- PlayerTags/Resources/Strings.Designer.cs | 2 +- PlayerTags/Resources/Strings.resx | 2 +- 4 files changed, 16 insertions(+), 47 deletions(-) diff --git a/PlayerTags/Features/NameplateTagTargetFeature.cs b/PlayerTags/Features/NameplateTagTargetFeature.cs index 09786f4..7b02576 100644 --- a/PlayerTags/Features/NameplateTagTargetFeature.cs +++ b/PlayerTags/Features/NameplateTagTargetFeature.cs @@ -114,31 +114,8 @@ namespace PlayerTags.Features var beforeTitleBytes = args.Title.Encode(); var iconID = args.IconId; var generalOptions = m_PluginConfiguration.GeneralOptions[ActivityContextManager.CurrentActivityContext.ActivityType]; - var applyTags = false; - var grayOut = false; - if (args.PlayerCharacter != null) - { - if (args.PlayerCharacter.IsDead) - { - switch (generalOptions.NameplateDeadPlayerHandling) - { - case DeadPlayerHandling.Include: - applyTags = true; - break; - case DeadPlayerHandling.GrayOut: - grayOut = true; - break; - } - } - else - applyTags = true; - } - - if (applyTags) - AddTagsToNameplate(args.PlayerCharacter, args.Name, args.Title, args.FreeCompany, ref iconID, generalOptions); - else if(grayOut) - GrayOutNameplate(args.PlayerCharacter, args.Name, args.Title, args.FreeCompany, ref iconID); + AddTagsToNameplate(args.PlayerCharacter, args.Name, args.Title, args.FreeCompany, ref iconID, generalOptions); args.IconId = iconID; @@ -200,7 +177,7 @@ namespace PlayerTags.Features int? newStatusIcon = null; NameplateChanges nameplateChanges = GenerateEmptyNameplateChanges(name, title, freeCompany); - if (gameObject is PlayerCharacter playerCharacter) + if (gameObject is PlayerCharacter playerCharacter && (!playerCharacter.IsDead || generalOptions.NameplateDeadPlayerHandling != DeadPlayerHandling.Ignore)) { var classJob = playerCharacter.ClassJob; var classJobGameData = classJob?.GameData; @@ -258,6 +235,12 @@ namespace PlayerTags.Features if (gameObject is PlayerCharacter playerCharacter1) { + ushort? colorOverwrite = null; + + // Use gray color if player is dead + if (playerCharacter1.IsDead && generalOptions.NameplateDeadPlayerHandling == DeadPlayerHandling.GrayOut) + colorOverwrite = 3; + // An additional step to apply text color to additional locations Identity identity = m_PluginData.GetIdentity(playerCharacter1); foreach (var customTagId in identity.CustomTagIds) @@ -274,27 +257,12 @@ namespace PlayerTags.Features { var destStrings = new[] { name, title, freeCompany }; var isTextColorApplied = new[] { tag.IsTextColorAppliedToNameplateName, tag.IsTextColorAppliedToNameplateTitle, tag.IsTextColorAppliedToNameplateFreeCompany }; - ApplyTextFormatting(gameObject, tag, new[] { name, title, freeCompany }, isTextColorApplied, null); + ApplyTextFormatting(gameObject, tag, new[] { name, title, freeCompany }, isTextColorApplied, null, + overwriteTextColor: colorOverwrite); } } } - protected void GrayOutNameplate(GameObject gameObject, SeString name, SeString title, SeString freeCompany, ref int statusIcon) - { - if (gameObject is PlayerCharacter playerCharacter) - { - NameplateChanges nameplateChanges = GenerateEmptyNameplateChanges(name, title, freeCompany); - - foreach (NameplateElements element in Enum.GetValues()) - { - nameplateChanges.GetChange(element, StringPosition.Before).Payloads.Add(new UIForegroundPayload(3)); - nameplateChanges.GetChange(element, StringPosition.After).Payloads.Add(new UIForegroundPayload(0)); - } - - ApplyNameplateChanges(nameplateChanges); - } - } - protected void ApplyNameplateChanges(NameplateChanges nameplateChanges) { var props = new NameplateChangesProps diff --git a/PlayerTags/Features/TagTargetFeature.cs b/PlayerTags/Features/TagTargetFeature.cs index 7773da5..a0e6c0b 100644 --- a/PlayerTags/Features/TagTargetFeature.cs +++ b/PlayerTags/Features/TagTargetFeature.cs @@ -223,7 +223,7 @@ namespace PlayerTags.Features StringUpdateFactory.ApplyStringChanges(props); } - protected void ApplyTextFormatting(GameObject gameObject, Tag tag, SeString[] destStrings, InheritableValue[] textColorApplied, List preferedPayloads) + protected void ApplyTextFormatting(GameObject gameObject, Tag tag, SeString[] destStrings, InheritableValue[] textColorApplied, List preferedPayloads, ushort? overwriteTextColor = null) { if (IsTagVisible(tag, gameObject)) { @@ -239,11 +239,12 @@ namespace PlayerTags.Features void applyTextColor(SeString destPayload, InheritableValue enableFlag, InheritableValue colorValue) { + var colorToUse = overwriteTextColor ?? colorValue?.InheritedValue; if (shouldApplyFormattingPayloads(destPayload) && enableFlag.InheritedValue != null && enableFlag.InheritedValue.Value - && colorValue.InheritedValue != null) - applyTextFormattingPayloads(destPayload, new UIForegroundPayload(colorValue.InheritedValue.Value), new UIForegroundPayload(0)); + && colorToUse != null) + applyTextFormattingPayloads(destPayload, new UIForegroundPayload(colorToUse.Value), new UIForegroundPayload(0)); } //void applyTextGlowColor(SeString destPayload, InheritableValue enableFlag, InheritableValue colorValue) diff --git a/PlayerTags/Resources/Strings.Designer.cs b/PlayerTags/Resources/Strings.Designer.cs index 94f850b..e0808c2 100644 --- a/PlayerTags/Resources/Strings.Designer.cs +++ b/PlayerTags/Resources/Strings.Designer.cs @@ -223,7 +223,7 @@ namespace PlayerTags.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Gray out the nameplate of dead players and don't apply any tag for. ähnelt. + /// Sucht eine lokalisierte Zeichenfolge, die Apply any tag but gray out the nameplate. ähnelt. /// public static string Loc_DeadPlayerHandling_GrayOut_Description { get { diff --git a/PlayerTags/Resources/Strings.resx b/PlayerTags/Resources/Strings.resx index 6f9fd7b..43bbdae 100644 --- a/PlayerTags/Resources/Strings.resx +++ b/PlayerTags/Resources/Strings.resx @@ -805,7 +805,7 @@ Use this if you want to to have every option under your control or just want to Handling for dead players - Gray out the nameplate of dead players and don't apply any tag for. + Apply any tag but gray out the nameplate. Don't process dead players and don't apply any tag for. From 164f611516521cd5017f07c069793975439b5a2b Mon Sep 17 00:00:00 2001 From: Pilzinsel64 Date: Wed, 16 Nov 2022 07:30:21 +0100 Subject: [PATCH 31/36] use gray color at text formatting applying --- .../Features/NameplateTagTargetFeature.cs | 54 ++++--------------- PlayerTags/Features/TagTargetFeature.cs | 7 +-- PlayerTags/Resources/Strings.Designer.cs | 2 +- PlayerTags/Resources/Strings.resx | 2 +- 4 files changed, 17 insertions(+), 48 deletions(-) diff --git a/PlayerTags/Features/NameplateTagTargetFeature.cs b/PlayerTags/Features/NameplateTagTargetFeature.cs index 09786f4..c196c57 100644 --- a/PlayerTags/Features/NameplateTagTargetFeature.cs +++ b/PlayerTags/Features/NameplateTagTargetFeature.cs @@ -114,31 +114,8 @@ namespace PlayerTags.Features var beforeTitleBytes = args.Title.Encode(); var iconID = args.IconId; var generalOptions = m_PluginConfiguration.GeneralOptions[ActivityContextManager.CurrentActivityContext.ActivityType]; - var applyTags = false; - var grayOut = false; - if (args.PlayerCharacter != null) - { - if (args.PlayerCharacter.IsDead) - { - switch (generalOptions.NameplateDeadPlayerHandling) - { - case DeadPlayerHandling.Include: - applyTags = true; - break; - case DeadPlayerHandling.GrayOut: - grayOut = true; - break; - } - } - else - applyTags = true; - } - - if (applyTags) - AddTagsToNameplate(args.PlayerCharacter, args.Name, args.Title, args.FreeCompany, ref iconID, generalOptions); - else if(grayOut) - GrayOutNameplate(args.PlayerCharacter, args.Name, args.Title, args.FreeCompany, ref iconID); + AddTagsToNameplate(args.PlayerCharacter, args.Name, args.Title, args.FreeCompany, ref iconID, generalOptions); args.IconId = iconID; @@ -200,7 +177,7 @@ namespace PlayerTags.Features int? newStatusIcon = null; NameplateChanges nameplateChanges = GenerateEmptyNameplateChanges(name, title, freeCompany); - if (gameObject is PlayerCharacter playerCharacter) + if (gameObject is PlayerCharacter playerCharacter && (!playerCharacter.IsDead || generalOptions.NameplateDeadPlayerHandling != DeadPlayerHandling.Ignore)) { var classJob = playerCharacter.ClassJob; var classJobGameData = classJob?.GameData; @@ -256,8 +233,14 @@ namespace PlayerTags.Features // Build the final strings out of the payloads ApplyNameplateChanges(nameplateChanges); - if (gameObject is PlayerCharacter playerCharacter1) + if (gameObject is PlayerCharacter playerCharacter1 && (!playerCharacter1.IsDead || generalOptions.NameplateDeadPlayerHandling != DeadPlayerHandling.Ignore)) { + ushort? colorOverwrite = null; + + // Use gray color if player is dead + if (playerCharacter1.IsDead && generalOptions.NameplateDeadPlayerHandling == DeadPlayerHandling.GrayOut) + colorOverwrite = 3; + // An additional step to apply text color to additional locations Identity identity = m_PluginData.GetIdentity(playerCharacter1); foreach (var customTagId in identity.CustomTagIds) @@ -274,27 +257,12 @@ namespace PlayerTags.Features { var destStrings = new[] { name, title, freeCompany }; var isTextColorApplied = new[] { tag.IsTextColorAppliedToNameplateName, tag.IsTextColorAppliedToNameplateTitle, tag.IsTextColorAppliedToNameplateFreeCompany }; - ApplyTextFormatting(gameObject, tag, new[] { name, title, freeCompany }, isTextColorApplied, null); + ApplyTextFormatting(gameObject, tag, new[] { name, title, freeCompany }, isTextColorApplied, null, + overwriteTextColor: colorOverwrite); } } } - protected void GrayOutNameplate(GameObject gameObject, SeString name, SeString title, SeString freeCompany, ref int statusIcon) - { - if (gameObject is PlayerCharacter playerCharacter) - { - NameplateChanges nameplateChanges = GenerateEmptyNameplateChanges(name, title, freeCompany); - - foreach (NameplateElements element in Enum.GetValues()) - { - nameplateChanges.GetChange(element, StringPosition.Before).Payloads.Add(new UIForegroundPayload(3)); - nameplateChanges.GetChange(element, StringPosition.After).Payloads.Add(new UIForegroundPayload(0)); - } - - ApplyNameplateChanges(nameplateChanges); - } - } - protected void ApplyNameplateChanges(NameplateChanges nameplateChanges) { var props = new NameplateChangesProps diff --git a/PlayerTags/Features/TagTargetFeature.cs b/PlayerTags/Features/TagTargetFeature.cs index 7773da5..a0e6c0b 100644 --- a/PlayerTags/Features/TagTargetFeature.cs +++ b/PlayerTags/Features/TagTargetFeature.cs @@ -223,7 +223,7 @@ namespace PlayerTags.Features StringUpdateFactory.ApplyStringChanges(props); } - protected void ApplyTextFormatting(GameObject gameObject, Tag tag, SeString[] destStrings, InheritableValue[] textColorApplied, List preferedPayloads) + protected void ApplyTextFormatting(GameObject gameObject, Tag tag, SeString[] destStrings, InheritableValue[] textColorApplied, List preferedPayloads, ushort? overwriteTextColor = null) { if (IsTagVisible(tag, gameObject)) { @@ -239,11 +239,12 @@ namespace PlayerTags.Features void applyTextColor(SeString destPayload, InheritableValue enableFlag, InheritableValue colorValue) { + var colorToUse = overwriteTextColor ?? colorValue?.InheritedValue; if (shouldApplyFormattingPayloads(destPayload) && enableFlag.InheritedValue != null && enableFlag.InheritedValue.Value - && colorValue.InheritedValue != null) - applyTextFormattingPayloads(destPayload, new UIForegroundPayload(colorValue.InheritedValue.Value), new UIForegroundPayload(0)); + && colorToUse != null) + applyTextFormattingPayloads(destPayload, new UIForegroundPayload(colorToUse.Value), new UIForegroundPayload(0)); } //void applyTextGlowColor(SeString destPayload, InheritableValue enableFlag, InheritableValue colorValue) diff --git a/PlayerTags/Resources/Strings.Designer.cs b/PlayerTags/Resources/Strings.Designer.cs index 94f850b..e0808c2 100644 --- a/PlayerTags/Resources/Strings.Designer.cs +++ b/PlayerTags/Resources/Strings.Designer.cs @@ -223,7 +223,7 @@ namespace PlayerTags.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Gray out the nameplate of dead players and don't apply any tag for. ähnelt. + /// Sucht eine lokalisierte Zeichenfolge, die Apply any tag but gray out the nameplate. ähnelt. /// public static string Loc_DeadPlayerHandling_GrayOut_Description { get { diff --git a/PlayerTags/Resources/Strings.resx b/PlayerTags/Resources/Strings.resx index 6f9fd7b..43bbdae 100644 --- a/PlayerTags/Resources/Strings.resx +++ b/PlayerTags/Resources/Strings.resx @@ -805,7 +805,7 @@ Use this if you want to to have every option under your control or just want to Handling for dead players - Gray out the nameplate of dead players and don't apply any tag for. + Apply any tag but gray out the nameplate. Don't process dead players and don't apply any tag for. From cce0f957489377429675ecb0fae71f3f7c23105c Mon Sep 17 00:00:00 2001 From: Pilzinsel64 Date: Thu, 17 Nov 2022 19:35:38 +0100 Subject: [PATCH 32/36] v1.8.0.4 testing --- PlayerTags/PlayerTags.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PlayerTags/PlayerTags.csproj b/PlayerTags/PlayerTags.csproj index aef4a18..7b4f443 100644 --- a/PlayerTags/PlayerTags.csproj +++ b/PlayerTags/PlayerTags.csproj @@ -1,7 +1,7 @@  r00telement;Pilzinsel64 - 1.8.0.3 + 1.8.0.4 From 103a826599bba16dca93ff6a6a15bd561ff876d7 Mon Sep 17 00:00:00 2001 From: Pilzinsel64 Date: Thu, 17 Nov 2022 19:52:04 +0100 Subject: [PATCH 33/36] optimize dead player handling a bit --- .../Features/NameplateTagTargetFeature.cs | 34 ++++++++++++------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/PlayerTags/Features/NameplateTagTargetFeature.cs b/PlayerTags/Features/NameplateTagTargetFeature.cs index c196c57..37f4d83 100644 --- a/PlayerTags/Features/NameplateTagTargetFeature.cs +++ b/PlayerTags/Features/NameplateTagTargetFeature.cs @@ -174,10 +174,11 @@ namespace PlayerTags.Features /// The free company text to change. private void AddTagsToNameplate(GameObject gameObject, SeString name, SeString title, SeString freeCompany, ref int statusIcon, GeneralOptionsClass generalOptions) { + var playerCharacter = gameObject as PlayerCharacter; int? newStatusIcon = null; NameplateChanges nameplateChanges = GenerateEmptyNameplateChanges(name, title, freeCompany); - if (gameObject is PlayerCharacter playerCharacter && (!playerCharacter.IsDead || generalOptions.NameplateDeadPlayerHandling != DeadPlayerHandling.Ignore)) + if (playerCharacter != null && (!playerCharacter.IsDead || generalOptions.NameplateDeadPlayerHandling != DeadPlayerHandling.Ignore)) { var classJob = playerCharacter.ClassJob; var classJobGameData = classJob?.GameData; @@ -230,19 +231,17 @@ namespace PlayerTags.Features NameplateUpdateFactory.ApplyStatusIconWithPrio(ref statusIcon, (int)newStatusIcon, change, ActivityContextManager.CurrentActivityContext, statusiconPriorizer, m_PluginConfiguration.MoveStatusIconToNameplateTextIfPossible); } + // Gray out the nameplate + if (playerCharacter != null && playerCharacter.IsDead && generalOptions.NameplateDeadPlayerHandling == DeadPlayerHandling.GrayOut) + GrayOutNameplate(gameObject, nameplateChanges); + // Build the final strings out of the payloads ApplyNameplateChanges(nameplateChanges); - if (gameObject is PlayerCharacter playerCharacter1 && (!playerCharacter1.IsDead || generalOptions.NameplateDeadPlayerHandling != DeadPlayerHandling.Ignore)) + if (playerCharacter != null && (!playerCharacter.IsDead || generalOptions.NameplateDeadPlayerHandling == DeadPlayerHandling.Include)) { - ushort? colorOverwrite = null; - - // Use gray color if player is dead - if (playerCharacter1.IsDead && generalOptions.NameplateDeadPlayerHandling == DeadPlayerHandling.GrayOut) - colorOverwrite = 3; - // An additional step to apply text color to additional locations - Identity identity = m_PluginData.GetIdentity(playerCharacter1); + Identity identity = m_PluginData.GetIdentity(playerCharacter); foreach (var customTagId in identity.CustomTagIds) { var customTag = m_PluginData.CustomTags.FirstOrDefault(tag => tag.CustomId.Value == customTagId); @@ -250,15 +249,26 @@ namespace PlayerTags.Features applyTextFormatting(customTag); } - if (playerCharacter1.ClassJob.GameData != null && m_PluginData.JobTags.TryGetValue(playerCharacter1.ClassJob.GameData.Abbreviation, out var jobTag)) + if (playerCharacter.ClassJob.GameData != null && m_PluginData.JobTags.TryGetValue(playerCharacter.ClassJob.GameData.Abbreviation, out var jobTag)) applyTextFormatting(jobTag); void applyTextFormatting(Tag tag) { var destStrings = new[] { name, title, freeCompany }; var isTextColorApplied = new[] { tag.IsTextColorAppliedToNameplateName, tag.IsTextColorAppliedToNameplateTitle, tag.IsTextColorAppliedToNameplateFreeCompany }; - ApplyTextFormatting(gameObject, tag, new[] { name, title, freeCompany }, isTextColorApplied, null, - overwriteTextColor: colorOverwrite); + ApplyTextFormatting(gameObject, tag, new[] { name, title, freeCompany }, isTextColorApplied, null); + } + } + } + + private void GrayOutNameplate(GameObject gameObject, NameplateChanges nameplateChanges) + { + if (gameObject is PlayerCharacter playerCharacter) + { + foreach (NameplateElements element in Enum.GetValues()) + { + nameplateChanges.GetChange(element, StringPosition.Before).Payloads.Add(new UIForegroundPayload(3)); + nameplateChanges.GetChange(element, StringPosition.After).Payloads.Add(new UIForegroundPayload(0)); } } } From cfbe84d28fe26dcd3a36791b73b9677b50a05e4b Mon Sep 17 00:00:00 2001 From: Pilzinsel64 Date: Thu, 17 Nov 2022 19:53:02 +0100 Subject: [PATCH 34/36] v1.8.0.5 Testing --- PlayerTags/PlayerTags.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PlayerTags/PlayerTags.csproj b/PlayerTags/PlayerTags.csproj index 7b4f443..e32c71f 100644 --- a/PlayerTags/PlayerTags.csproj +++ b/PlayerTags/PlayerTags.csproj @@ -1,7 +1,7 @@  r00telement;Pilzinsel64 - 1.8.0.4 + 1.8.0.5 From dcecc832eea2957813adf4680eda4404addfeaa3 Mon Sep 17 00:00:00 2001 From: Pilzinsel64 Date: Thu, 17 Nov 2022 20:06:38 +0100 Subject: [PATCH 35/36] update translation files --- PlayerTags/Resources/Strings.de.resx | 9 -------- PlayerTags/Resources/Strings.es-ES.resx | 30 ------------------------- PlayerTags/Resources/Strings.fr.resx | 30 ------------------------- 3 files changed, 69 deletions(-) diff --git a/PlayerTags/Resources/Strings.de.resx b/PlayerTags/Resources/Strings.de.resx index 96de658..fbf466b 100644 --- a/PlayerTags/Resources/Strings.de.resx +++ b/PlayerTags/Resources/Strings.de.resx @@ -354,15 +354,6 @@ Zeige im Chat - - Bestimmt, ob das Symbol im Chat dargestellt wird. - - - Zeige in der Namensanzeige - - - Bestimmt, ob das Symbol in der Namensanzeige dargestellt wird. - Text diff --git a/PlayerTags/Resources/Strings.es-ES.resx b/PlayerTags/Resources/Strings.es-ES.resx index 35cfbd2..b2c166b 100644 --- a/PlayerTags/Resources/Strings.es-ES.resx +++ b/PlayerTags/Resources/Strings.es-ES.resx @@ -354,15 +354,6 @@ Mostrar en el chat - - Si el icono se mostrará o no dentro del chat. - - - Mostrar en placas de nombre - - - Si el icono se mostrará o no en las placas de nombre. - Texto @@ -621,25 +612,4 @@ Otros (Experimental) - - Insert behind group number prefix in chat - - - If enabled, the Tag and Icon will get insert behind the Group/Alliance number prefix in Chat instead of before. - - - Nameplate properties - - - Defines whenever the free company element of the nameplate should be visible or not. - - - Defines the position for the title element of the nameplate. Should it be above or below the name? You can decide! - - - Defines whenever the title element of the nameplate should be visible or not. - - - Chat - \ No newline at end of file diff --git a/PlayerTags/Resources/Strings.fr.resx b/PlayerTags/Resources/Strings.fr.resx index a894726..12643c9 100644 --- a/PlayerTags/Resources/Strings.fr.resx +++ b/PlayerTags/Resources/Strings.fr.resx @@ -354,15 +354,6 @@ Afficher dans le chat - - Si l'icône doit être affichée dans le chat. - - - Afficher dans les plaques de noms - - - Si l'icône doit être affichée dans les plaques de noms. - Texte @@ -621,25 +612,4 @@ Autre (expérimental) - - Insert behind group number prefix in chat - - - If enabled, the Tag and Icon will get insert behind the Group/Alliance number prefix in Chat instead of before. - - - Nameplate properties - - - Defines whenever the free company element of the nameplate should be visible or not. - - - Defines the position for the title element of the nameplate. Should it be above or below the name? You can decide! - - - Defines whenever the title element of the nameplate should be visible or not. - - - Chat - \ No newline at end of file From 8f74555201b33a905bcb215c7afc4f5f838a3692 Mon Sep 17 00:00:00 2001 From: Pilzinsel64 Date: Sun, 20 Nov 2022 11:41:34 +0100 Subject: [PATCH 36/36] change version to v1.8.0.6 --- PlayerTags/PlayerTags.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PlayerTags/PlayerTags.csproj b/PlayerTags/PlayerTags.csproj index e32c71f..6311c54 100644 --- a/PlayerTags/PlayerTags.csproj +++ b/PlayerTags/PlayerTags.csproj @@ -1,7 +1,7 @@  r00telement;Pilzinsel64 - 1.8.0.5 + 1.8.0.6