diff --git a/PlayerTags/Configuration/GameConfig/GameConfigHelper.cs b/PlayerTags/Configuration/GameConfig/GameConfigHelper.cs index f84a58d..1ed8621 100644 --- a/PlayerTags/Configuration/GameConfig/GameConfigHelper.cs +++ b/PlayerTags/Configuration/GameConfig/GameConfigHelper.cs @@ -1,48 +1,41 @@ using FFXIVClientStructs.FFXIV.Client.UI.Misc; -using Lumina.Excel.GeneratedSheets; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -namespace PlayerTags.Configuration.GameConfig +namespace PlayerTags.Configuration.GameConfig; + +public class GameConfigHelper { - public class GameConfigHelper + private static GameConfigHelper instance = null; + private unsafe static ConfigModule* configModule = null; + + public static GameConfigHelper Instance { - private static GameConfigHelper instance = null; - private unsafe static ConfigModule* configModule = null; - - public static GameConfigHelper Instance + get { - get - { - instance ??= new GameConfigHelper(); - return instance; - } - } - - private GameConfigHelper() - { - unsafe - { - configModule = ConfigModule.Instance(); - } - } - - private uint? GetIntValue(ConfigOption option) - { - if (PluginServices.GameConfig.UiConfig.TryGetUInt(nameof(ConfigOption.LogNameType), out var value)) - return value; - return null; - } - - public LogNameType? GetLogNameType() - { - uint? value = GetIntValue(ConfigOption.LogNameType); - if (value != null) - return (LogNameType)value; - return null; + instance ??= new GameConfigHelper(); + return instance; } } + + private GameConfigHelper() + { + unsafe + { + configModule = ConfigModule.Instance(); + } + } + + private uint? GetIntValue(ConfigOption option) + { + if (PluginServices.GameConfig.UiConfig.TryGetUInt(nameof(ConfigOption.LogNameType), out var value)) + return value; + return null; + } + + public LogNameType? GetLogNameType() + { + uint? value = GetIntValue(ConfigOption.LogNameType); + if (value != null) + return (LogNameType)value; + return null; + } } diff --git a/PlayerTags/Configuration/GameConfig/LogNameType.cs b/PlayerTags/Configuration/GameConfig/LogNameType.cs index cc75bdf..f73b199 100644 --- a/PlayerTags/Configuration/GameConfig/LogNameType.cs +++ b/PlayerTags/Configuration/GameConfig/LogNameType.cs @@ -1,16 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +namespace PlayerTags.Configuration.GameConfig; -namespace PlayerTags.Configuration.GameConfig +public enum LogNameType : uint { - public enum LogNameType : uint - { - FullName, - LastNameShorted, - FirstNameShorted, - Initials - } + FullName, + LastNameShorted, + FirstNameShorted, + Initials } diff --git a/PlayerTags/Configuration/PluginConfiguration.cs b/PlayerTags/Configuration/PluginConfiguration.cs index b016823..369dc97 100644 --- a/PlayerTags/Configuration/PluginConfiguration.cs +++ b/PlayerTags/Configuration/PluginConfiguration.cs @@ -9,344 +9,341 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Reflection.Metadata.Ecma335; -using System.Runtime.CompilerServices; -namespace PlayerTags.Configuration +namespace PlayerTags.Configuration; + +[Serializable] +public class PluginConfiguration : IPluginConfiguration { - [Serializable] - public class PluginConfiguration : IPluginConfiguration + 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; + public bool EnabledGlobal = true; + + [JsonProperty("GeneralOptionsV2")] + public Dictionary GeneralOptions = new() { - private const int DEFAULT_CONFIG_VERSION = 1; + { ActivityType.None, new GeneralOptionsClass() }, + { ActivityType.PveDuty, new GeneralOptionsClass() }, + { ActivityType.PvpDuty, new GeneralOptionsClass() } + }; - [JsonProperty] - public int RootVersion { get; private set; } = DEFAULT_CONFIG_VERSION; - public int Version { get; set; } = DEFAULT_CONFIG_VERSION; - public bool IsVisible = false; - public bool EnabledGlobal = true; + public DefaultPluginDataTemplate DefaultPluginDataTemplate = DefaultPluginDataTemplate.None; + public StatusIconPriorizerSettings StatusIconPriorizerSettings = new(true); + public bool MoveStatusIconToNameplateTextIfPossible = true; + 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 IsPlayersTabPartyVisible = true; + public bool IsPlayersTabAllianceVisible = true; + public bool IsPlayersTabEnemiesVisible = true; + public bool IsPlayersTabOthersVisible = false; + public bool IsGeneralOptionsAllTheSameEnabled = true; - [JsonProperty("GeneralOptionsV2")] - public Dictionary GeneralOptions = new() + [JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)] + public Dictionary AllTagsChanges = []; + + [JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)] + public Dictionary AllRoleTagsChanges = []; + + [JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)] + public Dictionary> RoleTagsChanges = []; + + [JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)] + public Dictionary> DpsRoleTagsChanges = []; + + [JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)] + public Dictionary> RangedDpsRoleTagsChanges = []; + + [JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)] + public Dictionary> LandHandRoleTagsChanges = []; + + [JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)] + public Dictionary> JobTagsChanges = []; + + [JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)] + public Dictionary AllCustomTagsChanges = []; + + [JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)] + public List> CustomTagsChanges = []; + + [JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)] + public List Identities = []; + + #region Obsulate Properties + + [Obsolete] + [JsonProperty("GeneralOptions")] + private Dictionary GeneralOptionsV1 + { + set { - { ActivityType.None, new GeneralOptionsClass() }, - { ActivityType.PveDuty, new GeneralOptionsClass() }, - { ActivityType.PvpDuty, new GeneralOptionsClass() } + GeneralOptions.Clear(); + foreach (var kvp in value) + GeneralOptions.Add((ActivityType)kvp.Key, kvp.Value); + } + } + + [JsonProperty("NameplateFreeCompanyVisibility"), Obsolete] + private NameplateFreeCompanyVisibility NameplateFreeCompanyVisibilityV1 + { + set + { + foreach (var key in GeneralOptions.Keys) + GeneralOptions[key].NameplateFreeCompanyVisibility = value; + } + } + [JsonProperty("NameplateTitleVisibility"), Obsolete] + public NameplateTitleVisibility NameplateTitleVisibilityV1 + { + set + { + foreach (var key in GeneralOptions.Keys) + GeneralOptions[key].NameplateTitleVisibility = value; + } + } + [JsonProperty("NameplateTitlePosition"), Obsolete] + public NameplateTitlePosition NameplateTitlePositionV1 + { + set + { + foreach (var key in GeneralOptions.Keys) + GeneralOptions[key].NameplateTitlePosition = value; + } + } + + [JsonProperty("IsApplyTagsToAllChatMessagesEnabled"), Obsolete] + private bool IsApplyTagsToAllChatMessagesEnabledV1 + { + set + { + foreach (var key in GeneralOptions.Keys) + GeneralOptions[key].IsApplyTagsToAllChatMessagesEnabled = value; + } + } + + #endregion + + public event System.Action? Saved; + + public void Save(PluginData pluginData) + { + AllTagsChanges = pluginData.AllTags.GetChanges(pluginData.Default.AllTags.GetChanges()); + AllRoleTagsChanges = pluginData.AllRoleTags.GetChanges(pluginData.Default.AllRoleTags.GetChanges()); + + RoleTagsChanges = []; + foreach ((var role, var roleTag) in pluginData.RoleTags) + { + Dictionary? defaultChanges = []; + if (pluginData.Default.RoleTags.TryGetValue(role, out var defaultTag)) + { + defaultChanges = defaultTag.GetChanges(); + } + + var changes = roleTag.GetChanges(defaultChanges); + if (changes.Any()) + { + RoleTagsChanges[role] = changes; + } + } + + DpsRoleTagsChanges = []; + foreach ((var dpsRole, var dpsRoleTag) in pluginData.DpsRoleTags) + { + Dictionary? defaultChanges = []; + if (pluginData.Default.DpsRoleTags.TryGetValue(dpsRole, out var defaultTag)) + { + defaultChanges = defaultTag.GetChanges(); + } + + var changes = dpsRoleTag.GetChanges(defaultChanges); + if (changes.Any()) + { + DpsRoleTagsChanges[dpsRole] = changes; + } + } + + RangedDpsRoleTagsChanges = []; + foreach ((var rangedDpsRole, var rangedDpsRoleTag) in pluginData.RangedDpsRoleTags) + { + Dictionary? defaultChanges = []; + if (pluginData.Default.RangedDpsRoleTags.TryGetValue(rangedDpsRole, out var defaultTag)) + { + defaultChanges = defaultTag.GetChanges(); + } + + var changes = rangedDpsRoleTag.GetChanges(defaultChanges); + if (changes.Any()) + { + RangedDpsRoleTagsChanges[rangedDpsRole] = changes; + } + } + + LandHandRoleTagsChanges = []; + foreach ((var landHandRole, var landHandRoleTag) in pluginData.LandHandRoleTags) + { + Dictionary? defaultChanges = []; + if (pluginData.Default.LandHandRoleTags.TryGetValue(landHandRole, out var defaultTag)) + { + defaultChanges = defaultTag.GetChanges(); + } + + var changes = landHandRoleTag.GetChanges(defaultChanges); + if (changes.Any()) + { + LandHandRoleTagsChanges[landHandRole] = changes; + } + } + + JobTagsChanges = []; + foreach ((var jobAbbreviation, var jobTag) in pluginData.JobTags) + { + Dictionary? defaultChanges = []; + if (pluginData.Default.JobTags.TryGetValue(jobAbbreviation, out var defaultTag)) + { + defaultChanges = defaultTag.GetChanges(); + } + + var changes = jobTag.GetChanges(defaultChanges); + if (changes.Any()) + { + JobTagsChanges[jobAbbreviation] = changes; + } + } + + AllCustomTagsChanges = pluginData.AllCustomTags.GetChanges(pluginData.Default.AllCustomTags.GetChanges()); + + CustomTagsChanges = []; + foreach (var customTag in pluginData.CustomTags) + { + CustomTagsChanges.Add(customTag.GetChanges()); + } + + Identities = pluginData.Identities; + + 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); + } + + private static void BackupPluginConfig() + { + var configFilePath = GetConfigFilePath(); + var configFilePathOld = Path.ChangeExtension(configFilePath, ".old" + Path.GetExtension(configFilePath)); + File.Copy(configFilePath, configFilePathOld, true); + } + + 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(); + } + + if (config is PluginConfiguration pluginConfig) + { + if (PluginConfigFix(pluginConfig)) + { + BackupPluginConfig(); + pluginConfig.SavePluginConfig(); + } + } + + 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, }; - public DefaultPluginDataTemplate DefaultPluginDataTemplate = DefaultPluginDataTemplate.None; - public StatusIconPriorizerSettings StatusIconPriorizerSettings = new(true); - public bool MoveStatusIconToNameplateTextIfPossible = true; - 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 IsPlayersTabPartyVisible = true; - public bool IsPlayersTabAllianceVisible = true; - public bool IsPlayersTabEnemiesVisible = true; - public bool IsPlayersTabOthersVisible = false; - public bool IsGeneralOptionsAllTheSameEnabled = true; + jsonSettings.Converters.Add(new StringEnumConverter()); - [JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)] - public Dictionary AllTagsChanges = new Dictionary(); - - [JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)] - public Dictionary AllRoleTagsChanges = new Dictionary(); - - [JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)] - public Dictionary> RoleTagsChanges = new Dictionary>(); - - [JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)] - public Dictionary> DpsRoleTagsChanges = new Dictionary>(); - - [JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)] - public Dictionary> RangedDpsRoleTagsChanges = new Dictionary>(); - - [JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)] - public Dictionary> LandHandRoleTagsChanges = new Dictionary>(); - - [JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)] - public Dictionary> JobTagsChanges = new Dictionary>(); - - [JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)] - public Dictionary AllCustomTagsChanges = new Dictionary(); - - [JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)] - public List> CustomTagsChanges = new List>(); - - [JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)] - public List Identities = new List(); - - #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 - { - set - { - foreach (var key in GeneralOptions.Keys) - GeneralOptions[key].NameplateFreeCompanyVisibility = value; - } - } - [JsonProperty("NameplateTitleVisibility"), Obsolete] - public NameplateTitleVisibility NameplateTitleVisibilityV1 - { - set - { - foreach (var key in GeneralOptions.Keys) - GeneralOptions[key].NameplateTitleVisibility = value; - } - } - [JsonProperty("NameplateTitlePosition"), Obsolete] - public NameplateTitlePosition NameplateTitlePositionV1 - { - set - { - foreach (var key in GeneralOptions.Keys) - GeneralOptions[key].NameplateTitlePosition = value; - } - } - - [JsonProperty("IsApplyTagsToAllChatMessagesEnabled"), Obsolete] - private bool IsApplyTagsToAllChatMessagesEnabledV1 - { - set - { - foreach (var key in GeneralOptions.Keys) - GeneralOptions[key].IsApplyTagsToAllChatMessagesEnabled = value; - } - } - - #endregion - - public event System.Action? Saved; - - public void Save(PluginData pluginData) - { - AllTagsChanges = pluginData.AllTags.GetChanges(pluginData.Default.AllTags.GetChanges()); - AllRoleTagsChanges = pluginData.AllRoleTags.GetChanges(pluginData.Default.AllRoleTags.GetChanges()); - - RoleTagsChanges = new Dictionary>(); - foreach ((var role, var roleTag) in pluginData.RoleTags) - { - Dictionary? defaultChanges = new Dictionary(); - if (pluginData.Default.RoleTags.TryGetValue(role, out var defaultTag)) - { - defaultChanges = defaultTag.GetChanges(); - } - - var changes = roleTag.GetChanges(defaultChanges); - if (changes.Any()) - { - RoleTagsChanges[role] = changes; - } - } - - DpsRoleTagsChanges = new Dictionary>(); - foreach ((var dpsRole, var dpsRoleTag) in pluginData.DpsRoleTags) - { - Dictionary? defaultChanges = new Dictionary(); - if (pluginData.Default.DpsRoleTags.TryGetValue(dpsRole, out var defaultTag)) - { - defaultChanges = defaultTag.GetChanges(); - } - - var changes = dpsRoleTag.GetChanges(defaultChanges); - if (changes.Any()) - { - DpsRoleTagsChanges[dpsRole] = changes; - } - } - - RangedDpsRoleTagsChanges = new Dictionary>(); - foreach ((var rangedDpsRole, var rangedDpsRoleTag) in pluginData.RangedDpsRoleTags) - { - Dictionary? defaultChanges = new Dictionary(); - if (pluginData.Default.RangedDpsRoleTags.TryGetValue(rangedDpsRole, out var defaultTag)) - { - defaultChanges = defaultTag.GetChanges(); - } - - var changes = rangedDpsRoleTag.GetChanges(defaultChanges); - if (changes.Any()) - { - RangedDpsRoleTagsChanges[rangedDpsRole] = changes; - } - } - - LandHandRoleTagsChanges = new Dictionary>(); - foreach ((var landHandRole, var landHandRoleTag) in pluginData.LandHandRoleTags) - { - Dictionary? defaultChanges = new Dictionary(); - if (pluginData.Default.LandHandRoleTags.TryGetValue(landHandRole, out var defaultTag)) - { - defaultChanges = defaultTag.GetChanges(); - } - - var changes = landHandRoleTag.GetChanges(defaultChanges); - if (changes.Any()) - { - LandHandRoleTagsChanges[landHandRole] = changes; - } - } - - JobTagsChanges = new Dictionary>(); - foreach ((var jobAbbreviation, var jobTag) in pluginData.JobTags) - { - Dictionary? defaultChanges = new Dictionary(); - if (pluginData.Default.JobTags.TryGetValue(jobAbbreviation, out var defaultTag)) - { - defaultChanges = defaultTag.GetChanges(); - } - - var changes = jobTag.GetChanges(defaultChanges); - if (changes.Any()) - { - JobTagsChanges[jobAbbreviation] = changes; - } - } - - AllCustomTagsChanges = pluginData.AllCustomTags.GetChanges(pluginData.Default.AllCustomTags.GetChanges()); - - CustomTagsChanges = new List>(); - foreach (var customTag in pluginData.CustomTags) - { - CustomTagsChanges.Add(customTag.GetChanges()); - } - - Identities = pluginData.Identities; - - 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); - } - - private static void BackupPluginConfig() - { - var configFilePath = GetConfigFilePath(); - var configFilePathOld = Path.ChangeExtension(configFilePath, ".old" + Path.GetExtension(configFilePath)); - File.Copy(configFilePath, configFilePathOld, true); - } - - 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(); - } - - if (config is PluginConfiguration pluginConfig) - { - if (PluginConfigFix(pluginConfig)) - { - BackupPluginConfig(); - pluginConfig.SavePluginConfig(); - } - } - - 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; - } - - private static bool PluginConfigFix(PluginConfiguration config) - { - bool hasFixes = false; - - // Patch 6.4 - Disable all Job & Role specific colors & prefix - // Not used yet, but keeping it there, just for the case, - //if (config.Version <= 1) - //{ - // void fixTags(Dictionary dic) - // { - // foreach (var change in config.AllRoleTagsChanges.ToArray()) - // { - // var key = change.Key; - // if (key == nameof(Tag.IsTextVisibleInChat) || - // key == nameof(Tag.IsTextVisibleInNameplates) || - // key == nameof(Tag.IsRoleIconVisibleInChat) || - // key == nameof(Tag.IsRoleIconVisibleInNameplates) || - // key == nameof(Tag.IsTextColorAppliedToNameplateName) || - // key == nameof(Tag.IsTextColorAppliedToChatName) || - // key == nameof(Tag.IsJobIconVisibleInNameplates)) - // { - // var data = change.Value; - // data.Behavior = InheritableBehavior.Disabled; - // } - // } - // } - - // // "All Roles" tag changes - // fixTags(config.AllRoleTagsChanges); - - // // Role tags changes - // foreach (var kvp in config.RoleTagsChanges) - // fixTags(kvp.Value); - - // // Job tags changes - // foreach (var kvp in config.JobTagsChanges) - // fixTags(kvp.Value); - - // hasFixes = true; - //} - - return hasFixes; - } + return jsonSettings; } - public class GeneralOptionsClass + private static bool PluginConfigFix(PluginConfiguration config) { - public NameplateFreeCompanyVisibility NameplateFreeCompanyVisibility = NameplateFreeCompanyVisibility.Default; - public NameplateTitleVisibility NameplateTitleVisibility = NameplateTitleVisibility.WhenHasTags; - public NameplateTitlePosition NameplateTitlePosition = NameplateTitlePosition.AlwaysAboveName; - public DeadPlayerHandling NameplateDeadPlayerHandling = DeadPlayerHandling.Include; - public bool IsApplyTagsToAllChatMessagesEnabled = true; + bool hasFixes = false; + + // Patch 6.4 - Disable all Job & Role specific colors & prefix + // Not used yet, but keeping it there, just for the case, + //if (config.Version <= 1) + //{ + // void fixTags(Dictionary dic) + // { + // foreach (var change in config.AllRoleTagsChanges.ToArray()) + // { + // var key = change.Key; + // if (key == nameof(Tag.IsTextVisibleInChat) || + // key == nameof(Tag.IsTextVisibleInNameplates) || + // key == nameof(Tag.IsRoleIconVisibleInChat) || + // key == nameof(Tag.IsRoleIconVisibleInNameplates) || + // key == nameof(Tag.IsTextColorAppliedToNameplateName) || + // key == nameof(Tag.IsTextColorAppliedToChatName) || + // key == nameof(Tag.IsJobIconVisibleInNameplates)) + // { + // var data = change.Value; + // data.Behavior = InheritableBehavior.Disabled; + // } + // } + // } + + // // "All Roles" tag changes + // fixTags(config.AllRoleTagsChanges); + + // // Role tags changes + // foreach (var kvp in config.RoleTagsChanges) + // fixTags(kvp.Value); + + // // Job tags changes + // foreach (var kvp in config.JobTagsChanges) + // fixTags(kvp.Value); + + // hasFixes = true; + //} + + return hasFixes; } } + +public class GeneralOptionsClass +{ + 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/Configuration/PluginConfigurationUI.cs b/PlayerTags/Configuration/PluginConfigurationUI.cs index 05fe348..9e2a68e 100644 --- a/PlayerTags/Configuration/PluginConfigurationUI.cs +++ b/PlayerTags/Configuration/PluginConfigurationUI.cs @@ -1,19 +1,13 @@ -using Dalamud.Configuration; -using Dalamud.Game.ClientState.Objects.SubKinds; -using Dalamud.Game.Text; +using Dalamud.Game.Text; using Dalamud.Game.Text.SeStringHandling; using Dalamud.Interface; using Dalamud.Interface.Utility; -using Dalamud.Logging; -using FFXIVClientStructs.FFXIV.Client.Game.UI; -using FFXIVClientStructs.Havok; 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 Pilz.Dalamud.Tools; using PlayerTags.Data; using PlayerTags.Inheritables; using PlayerTags.PluginStrings; @@ -22,1300 +16,518 @@ using System; using System.Collections.Generic; using System.Linq; using System.Numerics; -using System.Threading.Tasks; -using System.Transactions; -namespace PlayerTags.Configuration +namespace PlayerTags.Configuration; + +public class PluginConfigurationUI { - public class PluginConfigurationUI + private struct PlayerInfo { - private struct PlayerInfo + public PlayerContext PlayerContext; + public float Proximity; + } + + private PluginConfiguration m_PluginConfiguration; + private PluginData m_PluginData; + private PropertyProxy propertyProxy; + + private InheritableValue? m_ColorPickerPopupDataContext; + private Dictionary inheritableTEnumProxies = []; + + public PluginConfigurationUI(PluginConfiguration config, PluginData pluginData) + { + m_PluginConfiguration = config; + m_PluginData = pluginData; + propertyProxy = new PropertyProxy(config); + } + + private static float ScalePoints(float input) => input * ImGuiHelpers.GlobalScale; + + public void Draw() + { + if (m_PluginConfiguration == null || !m_PluginConfiguration.IsVisible) { - public PlayerContext PlayerContext; - public float Proximity; + return; } - private PluginConfiguration m_PluginConfiguration; - private PluginData m_PluginData; - private PropertyProxy propertyProxy; + ImGui.SetNextWindowSize(ImGuiHelpers.ScaledVector2(600, 500), ImGuiCond.FirstUseEver); - private InheritableValue? m_ColorPickerPopupDataContext; - private Dictionary inheritableTEnumProxies = new(); - - public PluginConfigurationUI(PluginConfiguration config, PluginData pluginData) + if (ImGui.Begin(Strings.Loc_Static_PluginName, ref m_PluginConfiguration.IsVisible)) { - m_PluginConfiguration = config; - m_PluginData = pluginData; - propertyProxy = new PropertyProxy(config); - } + propertyProxy.LoadData(); - private static float ScalePoints(float input) => input * ImGuiHelpers.GlobalScale; + ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(1, 0.8f, 0.5f, 1)); + ImGui.TextWrapped(Strings.Loc_Static_WarningMessage); + ImGui.PopStyleColor(); - public void Draw() - { - if (m_PluginConfiguration == null || !m_PluginConfiguration.IsVisible) + ImGui.Spacing(); + ImGui.Spacing(); + if (ImGui.BeginTabBar("MainTabs")) { - return; - } - - ImGui.SetNextWindowSize(ImGuiHelpers.ScaledVector2(600, 500), ImGuiCond.FirstUseEver); - - if (ImGui.Begin(Strings.Loc_Static_PluginName, ref m_PluginConfiguration.IsVisible)) - { - propertyProxy.LoadData(); - - ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(1, 0.8f, 0.5f, 1)); - ImGui.TextWrapped(Strings.Loc_Static_WarningMessage); - ImGui.PopStyleColor(); - - ImGui.Spacing(); - ImGui.Spacing(); - if (ImGui.BeginTabBar("MainTabs")) + if (ImGui.BeginTabItem(Strings.Loc_Static_General)) { - if (ImGui.BeginTabItem(Strings.Loc_Static_General)) - { - ImGui.Spacing(); - ImGui.Spacing(); - DrawCheckbox(nameof(m_PluginConfiguration.IsCustomTagsContextMenuEnabled), true, ref m_PluginConfiguration.IsCustomTagsContextMenuEnabled, () => SaveSettings()); + ImGui.Spacing(); + ImGui.Spacing(); + DrawCheckbox(nameof(m_PluginConfiguration.IsCustomTagsContextMenuEnabled), true, ref m_PluginConfiguration.IsCustomTagsContextMenuEnabled, () => SaveSettings()); - ImGui.Spacing(); - ImGui.Spacing(); - DrawHeading(Strings.Loc_Static_CurrentActivityProfile); - DrawComboBox(true, true, false, ref propertyProxy.CurrentActivityContext, () => SaveSettings(false)); + ImGui.Spacing(); + ImGui.Spacing(); + DrawHeading(Strings.Loc_Static_CurrentActivityProfile); + DrawComboBox(true, true, false, ref propertyProxy.CurrentActivityContext, () => SaveSettings(false)); - ImGui.Spacing(); - ImGui.Spacing(); - DrawHeading(Strings.Loc_Static_Nameplates); - 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(); + DrawHeading(Strings.Loc_Static_Nameplates); + 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(); - DrawHeading(Strings.Loc_Static_Chat); - //DrawCheckbox(nameof(propertyProxy.IsLinkSelfInChatEnabled), true, ref propertyProxy.IsLinkSelfInChatEnabled, () => SaveSettings(true)); - DrawCheckbox(nameof(propertyProxy.IsApplyTagsToAllChatMessagesEnabled), true, ref propertyProxy.IsApplyTagsToAllChatMessagesEnabled, () => SaveSettings(true)); + ImGui.Spacing(); + ImGui.Spacing(); + DrawHeading(Strings.Loc_Static_Chat); + //DrawCheckbox(nameof(propertyProxy.IsLinkSelfInChatEnabled), true, ref propertyProxy.IsLinkSelfInChatEnabled, () => SaveSettings(true)); + DrawCheckbox(nameof(propertyProxy.IsApplyTagsToAllChatMessagesEnabled), true, ref propertyProxy.IsApplyTagsToAllChatMessagesEnabled, () => SaveSettings(true)); - ImGui.Spacing(); - ImGui.Spacing(); - DrawHeading(Strings.Loc_Static_OtherExperimental); - DrawCheckbox(nameof(m_PluginConfiguration.IsPlayerNameRandomlyGenerated), true, ref m_PluginConfiguration.IsPlayerNameRandomlyGenerated, () => SaveSettings()); + ImGui.Spacing(); + ImGui.Spacing(); + DrawHeading(Strings.Loc_Static_OtherExperimental); + DrawCheckbox(nameof(m_PluginConfiguration.IsPlayerNameRandomlyGenerated), true, ref m_PluginConfiguration.IsPlayerNameRandomlyGenerated, () => SaveSettings()); - ImGui.EndTabItem(); - } - - if (ImGui.BeginTabItem(Strings.Loc_Static_Tags)) - { - 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); - - ImGui.Spacing(); - ImGui.Spacing(); - ImGui.BeginGroup(); - DrawTree(m_PluginData.AllTags); - ImGui.EndGroup(); - - ImGui.NextColumn(); - var selectedTag = m_PluginData.AllTags.Descendents.SingleOrDefault(descendent => descendent.IsSelected.Value); - if (selectedTag != null) - { - DrawControls(selectedTag); - } - - ImGui.EndGroup(); - ImGui.Columns(1); - - ImGui.EndTabItem(); - } - - if (ImGui.BeginTabItem(Strings.Loc_Static_QuickTag)) - { - ImGui.Spacing(); - ImGui.Spacing(); - DrawCheckbox(nameof(m_PluginConfiguration.IsPlayersTabOrderedByProximity), true, ref m_PluginConfiguration.IsPlayersTabOrderedByProximity, () => SaveSettings()); - DrawCheckbox(nameof(m_PluginConfiguration.IsPlayersTabSelfVisible), true, ref m_PluginConfiguration.IsPlayersTabSelfVisible, () => SaveSettings()); - DrawCheckbox(nameof(m_PluginConfiguration.IsPlayersTabFriendsVisible), true, ref m_PluginConfiguration.IsPlayersTabFriendsVisible, () => SaveSettings()); - DrawCheckbox(nameof(m_PluginConfiguration.IsPlayersTabPartyVisible), true, ref m_PluginConfiguration.IsPlayersTabPartyVisible, () => SaveSettings()); - DrawCheckbox(nameof(m_PluginConfiguration.IsPlayersTabAllianceVisible), true, ref m_PluginConfiguration.IsPlayersTabAllianceVisible, () => SaveSettings()); - DrawCheckbox(nameof(m_PluginConfiguration.IsPlayersTabEnemiesVisible), true, ref m_PluginConfiguration.IsPlayersTabEnemiesVisible, () => SaveSettings()); - DrawCheckbox(nameof(m_PluginConfiguration.IsPlayersTabOthersVisible), true, ref m_PluginConfiguration.IsPlayersTabOthersVisible, () => SaveSettings()); - - ImGui.Spacing(); - ImGui.Spacing(); - if (ImGui.BeginTable("##PlayersTable", 1 + m_PluginData.CustomTags.Count)) - { - ImGui.TableHeader(Strings.Loc_Static_PlayerName); - ImGui.TableSetupColumn(Strings.Loc_Static_PlayerName); - ImGui.NextColumn(); - foreach (var customTag in m_PluginData.CustomTags) - { - ImGui.TableHeader(customTag.Text.InheritedValue); - ImGui.TableSetupColumn(customTag.Text.InheritedValue); - ImGui.NextColumn(); - } - ImGui.TableHeadersRow(); - - if (PluginServices.ClientState.LocalPlayer != null) - { - Dictionary playerNameContexts = PluginServices.ObjectTable - .Where(gameObject => gameObject is PlayerCharacter) - .Select(gameObject => gameObject as PlayerCharacter) - .ToDictionary( - playerCharacter => m_PluginData.GetIdentity(playerCharacter!), - playerCharacter => new PlayerInfo() - { - PlayerContext = PlayerContextHelper.GetPlayerContext(playerCharacter!), - Proximity = (playerCharacter!.Position - PluginServices.ClientState.LocalPlayer.Position).Length() - }); - - // Include party members that aren't in the game object list - foreach (var partyMember in PluginServices.PartyList) - { - var partyMemberIdentity = m_PluginData.GetIdentity(partyMember); - - if (!playerNameContexts.ContainsKey(partyMemberIdentity)) - { - playerNameContexts[partyMemberIdentity] = new PlayerInfo() - { - PlayerContext = PlayerContext.Party, - Proximity = int.MaxValue - }; - } - } - - var filteredPlayerNameContexts = playerNameContexts.Where(player => PlayerContextHelper.GetIsVisible(player.Value.PlayerContext, - m_PluginConfiguration.IsPlayersTabSelfVisible, - m_PluginConfiguration.IsPlayersTabFriendsVisible, - m_PluginConfiguration.IsPlayersTabPartyVisible, - m_PluginConfiguration.IsPlayersTabAllianceVisible, - m_PluginConfiguration.IsPlayersTabEnemiesVisible, - m_PluginConfiguration.IsPlayersTabOthersVisible)); - - var orderedPlayerNameContexts = filteredPlayerNameContexts.OrderBy(player => player.Key); - if (m_PluginConfiguration.IsPlayersTabOrderedByProximity) - { - orderedPlayerNameContexts = filteredPlayerNameContexts.OrderBy(player => player.Value.Proximity); - } - - int rowIndex = 0; - foreach (var player in orderedPlayerNameContexts) - { - DrawQuickAddRow(player.Key, rowIndex); - ++rowIndex; - } - } - - ImGui.EndTable(); - } - - ImGui.EndTabItem(); - } - - if (ImGui.BeginTabItem(Strings.Loc_Static_TaggedPlayers)) - { - ImGui.Spacing(); - ImGui.Spacing(); - if (ImGui.BeginTable("##TaggedPlayersTable", 1 + m_PluginData.CustomTags.Count)) - { - ImGui.TableHeader(Strings.Loc_Static_PlayerName); - ImGui.TableSetupColumn(Strings.Loc_Static_PlayerName); - ImGui.NextColumn(); - foreach (var customTag in m_PluginData.CustomTags) - { - ImGui.TableHeader(customTag.Text.InheritedValue); - ImGui.TableSetupColumn(customTag.Text.InheritedValue); - ImGui.NextColumn(); - } - ImGui.TableHeadersRow(); - - int rowIndex = 0; - foreach (var identity in m_PluginData.Identities.ToArray()) - { - DrawQuickAddRow(identity, rowIndex); - ++rowIndex; - } - - if (PluginServices.ObjectTable.Length == 0 && PluginServices.ClientState.LocalPlayer != null) - { - DrawQuickAddRow(m_PluginData.GetIdentity(PluginServices.ClientState.LocalPlayer), 0); - } - - ImGui.EndTable(); - } - - 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(); - }); - - DrawCheckbox(nameof(PluginConfiguration.MoveStatusIconToNameplateTextIfPossible), true, ref m_PluginConfiguration.MoveStatusIconToNameplateTextIfPossible, () => SaveSettings()); - - if (isPriorizerEnabled) - { - var statusIcons = Enum.GetValues(); - - ImGui.Spacing(); - ImGui.Spacing(); - - if (ImGui.Button(Strings.Loc_StatusIconPriorizer_ResetToDefault)) - { - m_PluginConfiguration.StatusIconPriorizerSettings.ResetToDefault(); - SaveSettings(); - } - if (ImGui.IsItemHovered()) - ImGui.SetTooltip(Strings.Loc_StatusIconPriorizer_ResetToDefault_Description); - - ImGui.SameLine(); - - if (ImGui.Button(Strings.Loc_StatusIconPriorizer_ResetToEmpty)) - { - m_PluginConfiguration.StatusIconPriorizerSettings.ResetToEmpty(); - SaveSettings(); - } - 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); - - 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(); - }); - } - } - - if (ImGui.IsItemHovered()) - ImGui.SetTooltip(Localizer.GetString(conditionSetName, true)); - - ImGui.Spacing(); - } - } - - ImGui.EndTabItem(); - } - - ImGui.EndTabBar(); + ImGui.EndTabItem(); } - ImGui.End(); + if (ImGui.BeginTabItem(Strings.Loc_Static_Tags)) + { + 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); + + ImGui.Spacing(); + ImGui.Spacing(); + ImGui.BeginGroup(); + DrawTree(m_PluginData.AllTags); + ImGui.EndGroup(); + + ImGui.NextColumn(); + var selectedTag = m_PluginData.AllTags.Descendents.SingleOrDefault(descendent => descendent.IsSelected.Value); + if (selectedTag != null) + { + DrawControls(selectedTag); + } + + ImGui.EndGroup(); + ImGui.Columns(1); + + ImGui.EndTabItem(); + } + + if (ImGui.BeginTabItem(Strings.Loc_Static_QuickTag)) + { + ImGui.Spacing(); + ImGui.Spacing(); + DrawCheckbox(nameof(m_PluginConfiguration.IsPlayersTabOrderedByProximity), true, ref m_PluginConfiguration.IsPlayersTabOrderedByProximity, () => SaveSettings()); + DrawCheckbox(nameof(m_PluginConfiguration.IsPlayersTabSelfVisible), true, ref m_PluginConfiguration.IsPlayersTabSelfVisible, () => SaveSettings()); + DrawCheckbox(nameof(m_PluginConfiguration.IsPlayersTabFriendsVisible), true, ref m_PluginConfiguration.IsPlayersTabFriendsVisible, () => SaveSettings()); + DrawCheckbox(nameof(m_PluginConfiguration.IsPlayersTabPartyVisible), true, ref m_PluginConfiguration.IsPlayersTabPartyVisible, () => SaveSettings()); + DrawCheckbox(nameof(m_PluginConfiguration.IsPlayersTabAllianceVisible), true, ref m_PluginConfiguration.IsPlayersTabAllianceVisible, () => SaveSettings()); + DrawCheckbox(nameof(m_PluginConfiguration.IsPlayersTabEnemiesVisible), true, ref m_PluginConfiguration.IsPlayersTabEnemiesVisible, () => SaveSettings()); + DrawCheckbox(nameof(m_PluginConfiguration.IsPlayersTabOthersVisible), true, ref m_PluginConfiguration.IsPlayersTabOthersVisible, () => SaveSettings()); + + ImGui.Spacing(); + ImGui.Spacing(); + if (ImGui.BeginTable("##PlayersTable", 1 + m_PluginData.CustomTags.Count)) + { + ImGui.TableHeader(Strings.Loc_Static_PlayerName); + ImGui.TableSetupColumn(Strings.Loc_Static_PlayerName); + ImGui.NextColumn(); + foreach (var customTag in m_PluginData.CustomTags) + { + ImGui.TableHeader(customTag.Text.InheritedValue); + ImGui.TableSetupColumn(customTag.Text.InheritedValue); + ImGui.NextColumn(); + } + ImGui.TableHeadersRow(); + + if (PluginServices.ClientState.LocalPlayer != null) + { + Dictionary playerNameContexts = PluginServices.ObjectTable + .Where(gameObject => gameObject is PlayerCharacter) + .Select(gameObject => gameObject as PlayerCharacter) + .ToDictionary( + playerCharacter => m_PluginData.GetIdentity(playerCharacter!), + playerCharacter => new PlayerInfo() + { + PlayerContext = PlayerContextHelper.GetPlayerContext(playerCharacter!), + Proximity = (playerCharacter!.Position - PluginServices.ClientState.LocalPlayer.Position).Length() + }); + + // Include party members that aren't in the game object list + foreach (var partyMember in PluginServices.PartyList) + { + var partyMemberIdentity = m_PluginData.GetIdentity(partyMember); + + if (!playerNameContexts.ContainsKey(partyMemberIdentity)) + { + playerNameContexts[partyMemberIdentity] = new PlayerInfo() + { + PlayerContext = PlayerContext.Party, + Proximity = int.MaxValue + }; + } + } + + var filteredPlayerNameContexts = playerNameContexts.Where(player => PlayerContextHelper.GetIsVisible(player.Value.PlayerContext, + m_PluginConfiguration.IsPlayersTabSelfVisible, + m_PluginConfiguration.IsPlayersTabFriendsVisible, + m_PluginConfiguration.IsPlayersTabPartyVisible, + m_PluginConfiguration.IsPlayersTabAllianceVisible, + m_PluginConfiguration.IsPlayersTabEnemiesVisible, + m_PluginConfiguration.IsPlayersTabOthersVisible)); + + var orderedPlayerNameContexts = filteredPlayerNameContexts.OrderBy(player => player.Key); + if (m_PluginConfiguration.IsPlayersTabOrderedByProximity) + { + orderedPlayerNameContexts = filteredPlayerNameContexts.OrderBy(player => player.Value.Proximity); + } + + int rowIndex = 0; + foreach (var player in orderedPlayerNameContexts) + { + DrawQuickAddRow(player.Key, rowIndex); + ++rowIndex; + } + } + + ImGui.EndTable(); + } + + ImGui.EndTabItem(); + } + + if (ImGui.BeginTabItem(Strings.Loc_Static_TaggedPlayers)) + { + ImGui.Spacing(); + ImGui.Spacing(); + if (ImGui.BeginTable("##TaggedPlayersTable", 1 + m_PluginData.CustomTags.Count)) + { + ImGui.TableHeader(Strings.Loc_Static_PlayerName); + ImGui.TableSetupColumn(Strings.Loc_Static_PlayerName); + ImGui.NextColumn(); + foreach (var customTag in m_PluginData.CustomTags) + { + ImGui.TableHeader(customTag.Text.InheritedValue); + ImGui.TableSetupColumn(customTag.Text.InheritedValue); + ImGui.NextColumn(); + } + ImGui.TableHeadersRow(); + + int rowIndex = 0; + foreach (var identity in m_PluginData.Identities.ToArray()) + { + DrawQuickAddRow(identity, rowIndex); + ++rowIndex; + } + + if (PluginServices.ObjectTable.Length == 0 && PluginServices.ClientState.LocalPlayer != null) + { + DrawQuickAddRow(m_PluginData.GetIdentity(PluginServices.ClientState.LocalPlayer), 0); + } + + ImGui.EndTable(); + } + + 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(); + }); + + DrawCheckbox(nameof(PluginConfiguration.MoveStatusIconToNameplateTextIfPossible), true, ref m_PluginConfiguration.MoveStatusIconToNameplateTextIfPossible, () => SaveSettings()); + + if (isPriorizerEnabled) + { + var statusIcons = Enum.GetValues(); + + ImGui.Spacing(); + ImGui.Spacing(); + + if (ImGui.Button(Strings.Loc_StatusIconPriorizer_ResetToDefault)) + { + m_PluginConfiguration.StatusIconPriorizerSettings.ResetToDefault(); + SaveSettings(); + } + if (ImGui.IsItemHovered()) + ImGui.SetTooltip(Strings.Loc_StatusIconPriorizer_ResetToDefault_Description); + + ImGui.SameLine(); + + if (ImGui.Button(Strings.Loc_StatusIconPriorizer_ResetToEmpty)) + { + m_PluginConfiguration.StatusIconPriorizerSettings.ResetToEmpty(); + SaveSettings(); + } + 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); + + 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(); + }); + } + } + + if (ImGui.IsItemHovered()) + ImGui.SetTooltip(Localizer.GetString(conditionSetName, true)); + + ImGui.Spacing(); + } + } + + ImGui.EndTabItem(); + } + + ImGui.EndTabBar(); } - if (!m_PluginConfiguration.IsVisible) - { - SaveSettings(); - } + ImGui.End(); } - void DrawQuickAddRow(Identity identity, int rowIndex) + if (!m_PluginConfiguration.IsVisible) { - ImGui.PushID(identity.ToString()); + SaveSettings(); + } + } - ImGui.TableNextRow(); + void DrawQuickAddRow(Identity identity, int rowIndex) + { + ImGui.PushID(identity.ToString()); - if (rowIndex % 2 != 0) - { - var backgroundColor = ImGui.ColorConvertFloat4ToU32(new Vector4(1, 1, 1, 0.05f)); - ImGui.TableSetBgColor(ImGuiTableBgTarget.RowBg1, backgroundColor); - } + ImGui.TableNextRow(); + + if (rowIndex % 2 != 0) + { + var backgroundColor = ImGui.ColorConvertFloat4ToU32(new Vector4(1, 1, 1, 0.05f)); + ImGui.TableSetBgColor(ImGuiTableBgTarget.RowBg1, backgroundColor); + } + + ImGui.TableNextColumn(); + + ImGui.AlignTextToFramePadding(); + ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, new Vector2(4, 0)); + ImGui.Text(identity.Name); + if (identity.WorldId != null) + { + ImGui.SameLine(); + ImGui.TextColored(new Vector4(1, 1, 1, 0.25f), $"@{identity.WorldName}"); + } + ImGui.PopStyleVar(); + + foreach (Tag customTag in m_PluginData.CustomTags) + { + ImGui.PushID(customTag.GetHashCode().ToString()); ImGui.TableNextColumn(); - ImGui.AlignTextToFramePadding(); - ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, new Vector2(4, 0)); - ImGui.Text(identity.Name); - if (identity.WorldId != null) + bool isTagAssigned = identity.CustomTagIds.Contains(customTag.CustomId.Value); + + DrawSimpleCheckbox(string.Format(Strings.Loc_Static_Format_AddTagToPlayer, customTag.Text.InheritedValue, identity.Name), ref isTagAssigned, () => { - ImGui.SameLine(); - ImGui.TextColored(new Vector4(1, 1, 1, 0.25f), $"@{identity.WorldName}"); - } - ImGui.PopStyleVar(); - - foreach (Tag customTag in m_PluginData.CustomTags) - { - ImGui.PushID(customTag.GetHashCode().ToString()); - - ImGui.TableNextColumn(); - - bool isTagAssigned = identity.CustomTagIds.Contains(customTag.CustomId.Value); - - DrawSimpleCheckbox(string.Format(Strings.Loc_Static_Format_AddTagToPlayer, customTag.Text.InheritedValue, identity.Name), ref isTagAssigned, () => + if (isTagAssigned) { - if (isTagAssigned) - { - m_PluginData.AddCustomTagToIdentity(customTag, identity); - SaveSettings(); - } - else - { - m_PluginData.RemoveCustomTagFromIdentity(customTag, identity); - SaveSettings(); - } - }); - - ImGui.PopID(); - } - - ImGui.PopID(); - } - - public string GetTreeItemName(Tag tag) - { - string itemName = tag.Name.Value; - if (m_PluginData.CustomTags.Contains(tag)) - { - if (!string.IsNullOrWhiteSpace(tag.Text.InheritedValue)) - { - itemName = tag.Text.InheritedValue; + m_PluginData.AddCustomTagToIdentity(customTag, identity); + SaveSettings(); } else { - itemName = Strings.Loc_Static_NoText; - } - } - - return itemName; - } - - public void Select(Tag tag) - { - foreach (var descendent in m_PluginData.AllTags.Descendents) - { - descendent.IsSelected.Value = false; - } - - tag.IsSelected.Value = true; - SaveSettings(); - } - - public void DrawTree(Tag tag) - { - ImGui.PushID(tag.GetHashCode().ToString()); - - // Build the tree node flags - ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags.OpenOnArrow | ImGuiTreeNodeFlags.NoTreePushOnOpen | ImGuiTreeNodeFlags.SpanFullWidth | ImGuiTreeNodeFlags.AllowItemOverlap | ImGuiTreeNodeFlags.FramePadding; - - if (!tag.Children.Any()) - { - flags |= ImGuiTreeNodeFlags.Leaf; - } - - if (tag.IsSelected.Value) - { - flags |= ImGuiTreeNodeFlags.Selected; - } - - if (tag.IsExpanded.Value) - { - flags |= ImGuiTreeNodeFlags.DefaultOpen; - } - - // Render the tree node - var beforeItemPos = ImGui.GetCursorScreenPos(); - ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, new Vector2(0, 3)); - ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, new Vector2(0, 0)); - if (tag.TextColor.InheritedValue != null) - { - ImGui.PushStyleColor(ImGuiCol.Text, UIColorHelper.ToColor(tag.TextColor.InheritedValue.Value)); - } - bool isOpened = ImGui.TreeNodeEx($"{GetTreeItemName(tag)}###{tag.GetHashCode()}", flags); - if (tag.TextColor.InheritedValue != null) - { - ImGui.PopStyleColor(); - } - ImGui.PopStyleVar(); - ImGui.PopStyleVar(); - var afterItemPos = ImGui.GetCursorScreenPos(); - - // Don't allow expanding the item to select the item - // Don't allow clicking on add/remove buttons to select the item - var hard23 = ScalePoints(23); - var hard2 = ScalePoints(2); - var deadzoneTopLeft = new Vector2(beforeItemPos.X + ImGui.GetContentRegionAvail().X - hard23, beforeItemPos.Y - hard2); - var deadzoneBottomRight = new Vector2(beforeItemPos.X + ImGui.GetContentRegionAvail().X + hard2, afterItemPos.Y + hard2); - if (!ImGui.IsItemToggledOpen() && !ImGui.IsMouseHoveringRect(deadzoneTopLeft, deadzoneBottomRight) && ImGui.IsItemClicked()) - { - Select(tag); - } - - // Render the custom tag button - if (tag == m_PluginData.AllCustomTags) - { - ImGui.SameLine(); - ImGui.PushStyleColor(ImGuiCol.Button, new Vector4(0.1f, 0.3f, 0.1f, 1)); - ImGui.PushStyleColor(ImGuiCol.ButtonHovered, new Vector4(0.2f, 0.6f, 0.2f, 1)); - ImGui.PushStyleColor(ImGuiCol.ButtonActive, new Vector4(0.2f, 0.6f, 0.2f, 1)); - ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, new Vector2(0, 0)); - ImGui.PushStyleVar(ImGuiStyleVar.ItemInnerSpacing, new Vector2(0, 0)); - ImGui.PushFont(UiBuilder.IconFont); - ImGui.SetCursorPosX(ImGui.GetCursorPos().X + ImGui.GetContentRegionAvail().X - hard23); - if (ImGui.Button(FontAwesomeIcon.Plus.ToIconString())) - { - var newTag = new Tag(new LocalizedPluginString(nameof(PluginData.CustomTags))) - { - IsExpanded = true, - Text = Strings.Loc_Static_NewTag, - CustomId = Guid.NewGuid() - }; - - m_PluginData.CustomTags.Add(newTag); - newTag.Parent = m_PluginData.AllCustomTags; - - Select(newTag); - } - ImGui.PopFont(); - ImGui.PopStyleVar(); - ImGui.PopStyleVar(); - ImGui.PopStyleColor(); - ImGui.PopStyleColor(); - ImGui.PopStyleColor(); - - if (ImGui.IsItemHovered()) - { - ImGui.SetTooltip(Strings.Loc_Static_AddCustomTag_Description); - } - } - - - // Render the remove custom tag button - if (m_PluginData.CustomTags.Contains(tag)) - { - ImGui.SameLine(); - ImGui.PushStyleColor(ImGuiCol.Button, new Vector4(0.3f, 0.1f, 0.1f, 1)); - ImGui.PushStyleColor(ImGuiCol.ButtonHovered, new Vector4(0.6f, 0.2f, 0.2f, 1)); - ImGui.PushStyleColor(ImGuiCol.ButtonActive, new Vector4(0.6f, 0.2f, 0.2f, 1)); - ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, new Vector2(0, 0)); - ImGui.PushStyleVar(ImGuiStyleVar.ItemInnerSpacing, new Vector2(0, 0)); - ImGui.PushFont(UiBuilder.IconFont); - ImGui.SetCursorPosX(ImGui.GetCursorPos().X + ImGui.GetContentRegionAvail().X - hard23); - if (ImGui.Button(FontAwesomeIcon.TrashAlt.ToIconString())) - { - m_PluginData.RemoveCustomTagFromIdentities(tag); - m_PluginData.AllCustomTags.Children.Remove(tag); - m_PluginData.CustomTags.Remove(tag); + m_PluginData.RemoveCustomTagFromIdentity(customTag, identity); SaveSettings(); - - Select(m_PluginData.AllCustomTags); } - ImGui.PopFont(); - ImGui.PopStyleVar(); - ImGui.PopStyleVar(); - ImGui.PopStyleColor(); - ImGui.PopStyleColor(); - ImGui.PopStyleColor(); - - if (ImGui.IsItemHovered()) - { - ImGui.SetTooltip(Strings.Loc_Static_RemoveCustomTag_Description); - } - } - - - // Save the node expansion state - if (isOpened && tag.Children.Any() && !tag.IsExpanded.Value) - { - tag.IsExpanded.Value = true; - SaveSettings(); - } - else if (!isOpened && tag.IsExpanded.Value) - { - tag.IsExpanded.Value = false; - SaveSettings(); - } - - - - // Render the child nodes - if (isOpened) - { - ImGui.TreePush(); - foreach (var childTag in tag.Children.OrderBy(child => GetTreeItemName(child)).ToArray()) - { - DrawTree(childTag); - } - ImGui.TreePop(); - } - + }); ImGui.PopID(); } - public void DrawControls(Tag tag) - { - var hard23 = ScalePoints(23); - ImGui.PushID(tag.GetHashCode().ToString()); + ImGui.PopID(); + } - // Render the add property override button and popup - if (ImGui.IsPopupOpen("AddPopup")) + public string GetTreeItemName(Tag tag) + { + string itemName = tag.Name.Value; + if (m_PluginData.CustomTags.Contains(tag)) + { + if (!string.IsNullOrWhiteSpace(tag.Text.InheritedValue)) { - ImGui.PushStyleColor(ImGuiCol.Button, new Vector4(0.2f, 0.6f, 0.2f, 1)); + itemName = tag.Text.InheritedValue; } else { - ImGui.PushStyleColor(ImGuiCol.Button, new Vector4(0.1f, 0.3f, 0.1f, 1)); + itemName = Strings.Loc_Static_NoText; } + } + + return itemName; + } + + public void Select(Tag tag) + { + foreach (var descendent in m_PluginData.AllTags.Descendents) + { + descendent.IsSelected.Value = false; + } + + tag.IsSelected.Value = true; + SaveSettings(); + } + + public void DrawTree(Tag tag) + { + ImGui.PushID(tag.GetHashCode().ToString()); + + // Build the tree node flags + ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags.OpenOnArrow | ImGuiTreeNodeFlags.NoTreePushOnOpen | ImGuiTreeNodeFlags.SpanFullWidth | ImGuiTreeNodeFlags.AllowItemOverlap | ImGuiTreeNodeFlags.FramePadding; + + if (!tag.Children.Any()) + { + flags |= ImGuiTreeNodeFlags.Leaf; + } + + if (tag.IsSelected.Value) + { + flags |= ImGuiTreeNodeFlags.Selected; + } + + if (tag.IsExpanded.Value) + { + flags |= ImGuiTreeNodeFlags.DefaultOpen; + } + + // Render the tree node + var beforeItemPos = ImGui.GetCursorScreenPos(); + ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, new Vector2(0, 3)); + ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, new Vector2(0, 0)); + if (tag.TextColor.InheritedValue != null) + { + ImGui.PushStyleColor(ImGuiCol.Text, UIColorHelper.ToColor(tag.TextColor.InheritedValue.Value)); + } + bool isOpened = ImGui.TreeNodeEx($"{GetTreeItemName(tag)}###{tag.GetHashCode()}", flags); + if (tag.TextColor.InheritedValue != null) + { + ImGui.PopStyleColor(); + } + ImGui.PopStyleVar(); + ImGui.PopStyleVar(); + var afterItemPos = ImGui.GetCursorScreenPos(); + + // Don't allow expanding the item to select the item + // Don't allow clicking on add/remove buttons to select the item + var hard23 = ScalePoints(23); + var hard2 = ScalePoints(2); + var deadzoneTopLeft = new Vector2(beforeItemPos.X + ImGui.GetContentRegionAvail().X - hard23, beforeItemPos.Y - hard2); + var deadzoneBottomRight = new Vector2(beforeItemPos.X + ImGui.GetContentRegionAvail().X + hard2, afterItemPos.Y + hard2); + if (!ImGui.IsItemToggledOpen() && !ImGui.IsMouseHoveringRect(deadzoneTopLeft, deadzoneBottomRight) && ImGui.IsItemClicked()) + { + Select(tag); + } + + // Render the custom tag button + if (tag == m_PluginData.AllCustomTags) + { + ImGui.SameLine(); + ImGui.PushStyleColor(ImGuiCol.Button, new Vector4(0.1f, 0.3f, 0.1f, 1)); ImGui.PushStyleColor(ImGuiCol.ButtonHovered, new Vector4(0.2f, 0.6f, 0.2f, 1)); ImGui.PushStyleColor(ImGuiCol.ButtonActive, new Vector4(0.2f, 0.6f, 0.2f, 1)); - + ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, new Vector2(0, 0)); + ImGui.PushStyleVar(ImGuiStyleVar.ItemInnerSpacing, new Vector2(0, 0)); ImGui.PushFont(UiBuilder.IconFont); - if (ImGui.Button(FontAwesomeIcon.Plus.ToIconString(), new Vector2(hard23, hard23))) + ImGui.SetCursorPosX(ImGui.GetCursorPos().X + ImGui.GetContentRegionAvail().X - hard23); + if (ImGui.Button(FontAwesomeIcon.Plus.ToIconString())) { - ImGui.OpenPopup("AddPopup"); + var newTag = new Tag(new LocalizedPluginString(nameof(PluginData.CustomTags))) + { + IsExpanded = true, + Text = Strings.Loc_Static_NewTag, + CustomId = Guid.NewGuid() + }; + + m_PluginData.CustomTags.Add(newTag); + newTag.Parent = m_PluginData.AllCustomTags; + + Select(newTag); } ImGui.PopFont(); + ImGui.PopStyleVar(); + ImGui.PopStyleVar(); ImGui.PopStyleColor(); ImGui.PopStyleColor(); ImGui.PopStyleColor(); - bool wasPaddingConsumed = false; - ImGui.PushStyleVar(ImGuiStyleVar.WindowPadding, ImGuiHelpers.ScaledVector2(0, 5)); - ImGui.SetNextWindowPos(ImGui.GetCursorScreenPos() - ImGuiHelpers.ScaledVector2(0, 4)); - if (ImGui.BeginPopup("AddPopup")) - { - wasPaddingConsumed = true; - ImGui.PopStyleVar(); - - ImGui.PushStyleColor(ImGuiCol.FrameBg, new Vector4(0f, 0f, 0f, 0)); - if (ImGui.BeginListBox("###SelectableInheritables")) - { - IEnumerable> inheritables1 = tag.Inheritables.Where(inheritable => inheritable.Value.Behavior == InheritableBehavior.Inherit); - var selectedInheritables1 = inheritables1 - .Select(inheritable => - { - string? categoryId = null; - - var field = tag.GetType().GetField(inheritable.Key); - if (field != null) - { - var inheritableCategory = field.GetCustomAttributes(typeof(InheritableCategoryAttribute), false).Cast().FirstOrDefault(); - if (inheritableCategory != null) - { - categoryId = inheritableCategory.CategoryId; - } - } - - return new - { - Inheritable = inheritable, - CategoryId = categoryId, - LocalizedName = Localizer.GetString(inheritable.Key, false) - }; - }).Where(inheritable => inheritable.CategoryId != null); - - var inheritableGroups1 = selectedInheritables1.GroupBy(inheritable => inheritable.CategoryId); - - foreach (var inheritableGroup in inheritableGroups1) - { - if (inheritableGroup.Key != null) - { - ImGui.PushID(inheritableGroup.Key); - DrawHeading(Localizer.GetString(inheritableGroup.Key, false)); - } - - foreach (var selectedInheritable in inheritableGroup) - { - bool isSelected = false; - if (ImGui.Selectable(selectedInheritable.LocalizedName, isSelected)) - { - selectedInheritable.Inheritable.Value.Behavior = InheritableBehavior.Enabled; - - if (selectedInheritable.Inheritable.Value is InheritableValue inheritableBool) - { - inheritableBool.Value = true; - } - - SaveSettings(); - ImGui.CloseCurrentPopup(); - } - - if (isSelected) - { - ImGui.SetItemDefaultFocus(); - } - - if (ImGui.IsItemHovered()) - { - ImGui.SetTooltip(Localizer.GetString(selectedInheritable.Inheritable.Key, true)); - } - } - - if (inheritableGroup.Key != null) - { - ImGui.PopID(); - } - } - - ImGui.EndListBox(); - } - ImGui.PopStyleColor(); - ImGui.EndPopup(); - } - if (!wasPaddingConsumed) - { - ImGui.PopStyleVar(); - } - if (ImGui.IsItemHovered()) { - ImGui.SetTooltip(Strings.Loc_Static_AddPropertyOverride_Description); + ImGui.SetTooltip(Strings.Loc_Static_AddCustomTag_Description); } - - if (tag.HasDefaults) - { - ImGui.SameLine(); - ImGui.PushFont(UiBuilder.IconFont); - ImGui.SetCursorPosX(ImGui.GetCursorPos().X + ImGui.GetContentRegionAvail().X - hard23); - if (ImGui.Button(FontAwesomeIcon.Recycle.ToIconString())) - { - tag.SetDefaults(); - } - ImGui.PopFont(); - if (ImGui.IsItemHovered()) - { - ImGui.SetTooltip(Strings.Loc_Static_ResetDefault_Description); - } - } - - // Render all the property overrides, and optionally allow the inherited properties to be rendered - IEnumerable> inheritables = tag.Inheritables; - if (!m_PluginConfiguration.IsShowInheritedPropertiesEnabled) - { - inheritables = inheritables.Where(inheritable => inheritable.Value.Behavior != InheritableBehavior.Inherit); - } - - var selectedInheritables = inheritables - .Select(inheritable => - { - string? categoryId = null; - - var field = tag.GetType().GetField(inheritable.Key); - if (field != null) - { - var inheritableCategory = field.GetCustomAttributes(typeof(InheritableCategoryAttribute), false).Cast().FirstOrDefault(); - if (inheritableCategory != null) - { - categoryId = inheritableCategory.CategoryId; - } - } - - return new - { - Inheritable = inheritable, - CategoryId = categoryId, - LocalizedName = Localizer.GetString(inheritable.Key, false) - }; - }).Where(inheritable => inheritable.CategoryId != null); - - var inheritableGroups = selectedInheritables.GroupBy(inheritable => inheritable.CategoryId); - - foreach (var inheritableGroup in inheritableGroups) - { - ImGui.Spacing(); - ImGui.Spacing(); - if (inheritableGroup.Key != null) - { - DrawHeading(Localizer.GetString(inheritableGroup.Key, false)); - } - - foreach (var selectedInheritable in inheritableGroup) - { - if (selectedInheritable.Inheritable.Value is InheritableValue inheritableBool) - { - DrawInheritable(selectedInheritable.Inheritable.Key, inheritableBool); - } - else if (selectedInheritable.Inheritable.Value is InheritableValue inheritableUshort) - { - DrawInheritable(selectedInheritable.Inheritable.Key, inheritableUshort); - } - else if (selectedInheritable.Inheritable.Value is InheritableValue inheritableBitmapFontIcon) - { - DrawInheritable(selectedInheritable.Inheritable.Key, false, true, inheritableBitmapFontIcon); - } - else if (selectedInheritable.Inheritable.Value is InheritableValue inheritableTagPosition) - { - DrawInheritable(selectedInheritable.Inheritable.Key, true, false, inheritableTagPosition); - } - else if (selectedInheritable.Inheritable.Value is InheritableValue inheritableNameplateElement) - { - DrawInheritable(selectedInheritable.Inheritable.Key, true, false, inheritableNameplateElement); - } - else if (selectedInheritable.Inheritable.Value is InheritableValue inheritableFreeCompanyVisibility) - { - DrawInheritable(selectedInheritable.Inheritable.Key, true, false, inheritableFreeCompanyVisibility); - } - else if (selectedInheritable.Inheritable.Value is InheritableValue inheritableNameplateTitleVisibility) - { - DrawInheritable(selectedInheritable.Inheritable.Key, true, false, inheritableNameplateTitleVisibility); - } - else if (selectedInheritable.Inheritable.Value is InheritableValue inheritableNameplateTitlePosition) - { - 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> inheritableXivChatType) - { - DrawMultiselect(selectedInheritable.Inheritable.Key, inheritableXivChatType); - } - else if (selectedInheritable.Inheritable.Value is InheritableReference inheritableString) - { - DrawInheritable(selectedInheritable.Inheritable.Key, inheritableString); - } - else - { - PluginServices.PluginLog.Warning($"Rendering for inheritable option not implemented: {selectedInheritable.Inheritable.Key}"); - } - } - } - - - ImGui.PopID(); } - private void DrawRemovePropertyOverrideButton(IInheritable inheritable) + + // Render the remove custom tag button + if (m_PluginData.CustomTags.Contains(tag)) { - ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, new Vector2(0, 0)); + ImGui.SameLine(); ImGui.PushStyleColor(ImGuiCol.Button, new Vector4(0.3f, 0.1f, 0.1f, 1)); ImGui.PushStyleColor(ImGuiCol.ButtonHovered, new Vector4(0.6f, 0.2f, 0.2f, 1)); ImGui.PushStyleColor(ImGuiCol.ButtonActive, new Vector4(0.6f, 0.2f, 0.2f, 1)); - ImGui.PushFont(UiBuilder.IconFont); - if (ImGui.Button(FontAwesomeIcon.TrashAlt.ToIconString(), ImGuiHelpers.ScaledVector2(23))) - { - inheritable.Behavior = InheritableBehavior.Inherit; - SaveSettings(); - } - ImGui.PopFont(); - ImGui.PopStyleColor(); - ImGui.PopStyleColor(); - ImGui.PopStyleColor(); - ImGui.PopStyleVar(); - if (ImGui.IsItemHovered()) - { - ImGui.SetTooltip(Strings.Loc_Static_RemovePropertyOverride_Description); - } - } - - private void DrawInheritable(string localizedStringName, InheritableValue inheritable) - { - bool isDisabled = inheritable.Behavior == InheritableBehavior.Inherit; - if (isDisabled) - { - ImGui.PushStyleVar(ImGuiStyleVar.Alpha, 0.25f); - } - - ImGui.BeginGroup(); - ImGui.BeginChild(inheritable.GetHashCode().ToString(), ImGuiHelpers.ScaledVector2(0, 50)); - - ImGui.Text(Localizer.GetString(localizedStringName, false)); - if (ImGui.IsItemHovered()) - { - ImGui.PushStyleVar(ImGuiStyleVar.Alpha, 1f); - ImGui.SetTooltip(Localizer.GetString(localizedStringName, true)); - ImGui.PopStyleVar(); - } - - if (isDisabled) - { - ImGui.SameLine(); - ImGui.Text(Strings.Loc_Static_Inherited); - } - - if (isDisabled) - { - bool value = inheritable.InheritedValue != null ? inheritable.InheritedValue.Value : false; - DrawCheckbox("IsEnabled", false, ref value, () => - { - }); - } - else - { - DrawCheckbox("IsEnabled", false, ref inheritable.Value, () => - { - SaveSettings(); - }); - - ImGui.SameLine(); - DrawRemovePropertyOverrideButton(inheritable); - } - - ImGui.EndChild(); - ImGui.EndGroup(); - - if (isDisabled) - { - ImGui.PopStyleVar(); - } - } - - private void DrawInheritable(string localizedStringName, bool shouldLocalizeNames, bool shouldOrderNames, InheritableValue inheritable) - where TEnum : struct, Enum - { - bool isDisabled = inheritable.Behavior == InheritableBehavior.Inherit; - if (isDisabled) - { - ImGui.PushStyleVar(ImGuiStyleVar.Alpha, 0.25f); - } - - ImGui.BeginGroup(); - ImGui.BeginChild(inheritable.GetHashCode().ToString(), ImGuiHelpers.ScaledVector2(0, 50)); - - ImGui.Text(Localizer.GetString(localizedStringName, false)); - if (ImGui.IsItemHovered()) - { - ImGui.PushStyleVar(ImGuiStyleVar.Alpha, 1f); - ImGui.SetTooltip(Localizer.GetString(localizedStringName, true)); - ImGui.PopStyleVar(); - } - - if (isDisabled) - { - ImGui.SameLine(); - ImGui.Text(Strings.Loc_Static_Inherited); - } - - if (isDisabled) - { - bool isEnabled = inheritable.InheritedValue != null; - DrawCheckbox("IsEnabled", false, ref isEnabled, () => { }); - - if (isEnabled) - { - ImGui.SameLine(); - ImGui.SetNextItemWidth(ScalePoints(200)); - ImGui.BeginChild(inheritable.GetHashCode().ToString(), ImGuiHelpers.ScaledVector2(200, 0)); - TEnum value = inheritable.InheritedValue != null ? inheritable.InheritedValue.Value : default(TEnum); - DrawComboBox(false, shouldLocalizeNames, shouldOrderNames, ref value, () => { }); - ImGui.EndChild(); - } - } - else - { - bool isEnabled = inheritable.Behavior == InheritableBehavior.Enabled; - DrawCheckbox("IsEnabled", false, ref isEnabled, () => - { - if (isEnabled) - { - inheritable.Behavior = InheritableBehavior.Enabled; - } - else - { - inheritable.Behavior = InheritableBehavior.Disabled; - } - SaveSettings(); - }); - - if (isEnabled) - { - ImGui.SameLine(); - ImGui.SetNextItemWidth(ScalePoints(200)); - ImGui.BeginChild(inheritable.GetHashCode().ToString(), ImGuiHelpers.ScaledVector2(200, 0)); - DrawComboBox(false, shouldLocalizeNames, shouldOrderNames, ref inheritable.Value, () => { SaveSettings(); }); - ImGui.EndChild(); - } - - ImGui.SameLine(); - DrawRemovePropertyOverrideButton(inheritable); - } - - ImGui.EndChild(); - ImGui.EndGroup(); - - if (isDisabled) - { - ImGui.PopStyleVar(); - } - } - - private void DrawInheritable(string localizedStringName, InheritableValue inheritable) - { - bool isDisabled = inheritable.Behavior == InheritableBehavior.Inherit; - if (isDisabled) - { - ImGui.PushStyleVar(ImGuiStyleVar.Alpha, 0.25f); - } - - ImGui.BeginGroup(); - ImGui.BeginChild(inheritable.GetHashCode().ToString(), ImGuiHelpers.ScaledVector2(180, 50)); - - ImGui.Text(Localizer.GetString(localizedStringName, false)); - if (ImGui.IsItemHovered()) - { - ImGui.PushStyleVar(ImGuiStyleVar.Alpha, 1f); - ImGui.SetTooltip(Localizer.GetString(localizedStringName, true)); - ImGui.PopStyleVar(); - } - - if (isDisabled) - { - ImGui.SameLine(); - ImGui.Text(Strings.Loc_Static_Inherited); - } - - if (isDisabled) - { - bool isEnabled = inheritable.InheritedValue != null; - DrawCheckbox("IsEnabled", false, ref isEnabled, () => { }); - - if (isEnabled) - { - ImGui.SameLine(); - ushort value = inheritable.InheritedValue != null ? inheritable.InheritedValue.Value : default(ushort); - DrawColorButton(value.ToString(), UIColorHelper.ToColor(value), () => { }); - } - } - else - { - bool isEnabled = inheritable.Behavior == InheritableBehavior.Enabled; - DrawCheckbox("IsEnabled", false, ref isEnabled, () => - { - if (isEnabled) - { - inheritable.Behavior = InheritableBehavior.Enabled; - } - else - { - inheritable.Behavior = InheritableBehavior.Disabled; - } - SaveSettings(); - }); - - if (isEnabled) - { - ImGui.SameLine(); - DrawColorButton( - inheritable.Value.ToString(), - UIColorHelper.ToColor(inheritable.Value), - () => - { - m_ColorPickerPopupDataContext = inheritable; - ImGui.OpenPopup("ColorPickerPopup"); - }); - } - - bool wasStyleConsumed = false; - ImGui.SetNextWindowPos(ImGui.GetCursorScreenPos() + ImGuiHelpers.ScaledVector2(31, 0)); - ImGui.PushStyleVar(ImGuiStyleVar.WindowPadding, new Vector2(0, 0)); - if (ImGui.BeginPopup("ColorPickerPopup")) - { - wasStyleConsumed = true; - ImGui.PopStyleVar(); - - DrawUIColorPicker( - (UIColor value) => - { - if (m_ColorPickerPopupDataContext != null) - { - m_ColorPickerPopupDataContext.Value = (ushort)value.RowId; - m_ColorPickerPopupDataContext = null; - SaveSettings(); - } - - ImGui.CloseCurrentPopup(); - }); - - ImGui.EndPopup(); - } - if (!wasStyleConsumed) - { - ImGui.PopStyleVar(); - } - - ImGui.SameLine(); - DrawRemovePropertyOverrideButton(inheritable); - } - - ImGui.EndChild(); - ImGui.EndGroup(); - - if (isDisabled) - { - ImGui.PopStyleVar(); - } - } - - private void DrawInheritable(string localizedStringName, InheritableReference inheritable) - { - bool isDisabled = inheritable.Behavior == InheritableBehavior.Inherit; - if (isDisabled) - { - ImGui.PushStyleVar(ImGuiStyleVar.Alpha, 0.25f); - } - - ImGui.BeginGroup(); - ImGui.BeginChild(inheritable.GetHashCode().ToString(), ImGuiHelpers.ScaledVector2(0, 50)); - - ImGui.Text(Localizer.GetString(localizedStringName, false)); - if (ImGui.IsItemHovered()) - { - ImGui.PushStyleVar(ImGuiStyleVar.Alpha, 1f); - ImGui.SetTooltip(Localizer.GetString(localizedStringName, true)); - ImGui.PopStyleVar(); - } - - if (isDisabled) - { - ImGui.SameLine(); - ImGui.Text(Strings.Loc_Static_Inherited); - } - - if (isDisabled) - { - bool isEnabled = inheritable.InheritedValue != null; - DrawCheckbox("IsEnabled", false, ref isEnabled, () => - { - }); - - if (isEnabled) - { - ImGui.SameLine(); - ImGui.SetNextItemWidth(ScalePoints(200)); - ImGui.BeginChild(inheritable.GetHashCode().ToString(), ImGuiHelpers.ScaledVector2(200, 0)); - string value = inheritable.Value; - DrawTextBox(localizedStringName, ref value, () => { }); - ImGui.EndChild(); - } - } - else - { - bool isEnabled = inheritable.Behavior == InheritableBehavior.Enabled; - DrawCheckbox("IsEnabled", false, ref isEnabled, () => - { - if (isEnabled) - { - inheritable.Behavior = InheritableBehavior.Enabled; - } - else - { - inheritable.Behavior = InheritableBehavior.Disabled; - } - SaveSettings(); - }); - - if (isEnabled) - { - ImGui.SameLine(); - ImGui.SetNextItemWidth(ScalePoints(200)); - ImGui.BeginChild(inheritable.GetHashCode().ToString(), ImGuiHelpers.ScaledVector2(200, 0)); - DrawTextBox(localizedStringName, ref inheritable.Value, () => { SaveSettings(); }); - ImGui.EndChild(); - } - - ImGui.SameLine(); - DrawRemovePropertyOverrideButton(inheritable); - } - - ImGui.EndChild(); - ImGui.EndGroup(); - - if (isDisabled) - { - ImGui.PopStyleVar(); - } - } - - private void DrawHeading(string label) - { - 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; - List 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, false), ref isDisabled ? ref tempval : ref entry.Enabled); - - if (ImGui.IsItemHovered()) - ImGui.SetTooltip(Localizer.GetString(entryName, true)); - - if (isClicked && !isDisabled) - { - var newList = proxyKey.ToList(); - proxy.ApplyTo(newList); - inheritable.Value = newList; - SaveSettings(); - } - } - } - - if (isDisabled) - ImGui.PopStyleVar(); - } - - 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(); - - ImGui.SetNextItemWidth(ImGui.CalcItemWidth()); - if (ImGui.BeginCombo($"###{currentValue.GetType().Name}", currentDisplayName)) - { - var displayNames = Enum.GetValues(typeof(TEnum)).Cast() - .Select(value => new { Value = value, DisplayName = shouldLocalizeNames ? Localizer.GetString(value, false) : value.ToString() }); - - if (shouldOrderNames) - { - displayNames = displayNames.OrderBy(displayEnum => displayEnum.DisplayName); - } - - foreach (var orderedDisplayName in displayNames) - { - bool isSelected = orderedDisplayName.Value.Equals(currentValue); - if (ImGui.Selectable(orderedDisplayName.DisplayName, isSelected)) - { - currentValue = orderedDisplayName.Value; - changed(); - } - - if (isSelected) - { - ImGui.SetItemDefaultFocus(); - } - - if (ImGui.IsItemHovered() && shouldLocalizeNames) - { - ImGui.SetTooltip(Localizer.GetString(orderedDisplayName.Value, true)); - } - } - - ImGui.EndCombo(); - } - - if (ImGui.IsItemHovered() && shouldLocalizeNames) - { - if (showToolTipToLabel) - ImGui.SetTooltip(Localizer.GetString(typeof(TEnum).Name, true)); - else - ImGui.SetTooltip(Localizer.GetString(currentValue, true)); - } - } - - private void DrawCheckbox(string localizedStringName, bool hasLabel, ref bool isChecked, System.Action changed) - { - if (ImGui.Checkbox(hasLabel ? Localizer.GetString(localizedStringName, false) : $"###{Localizer.GetString(localizedStringName, false)}", ref isChecked)) - { - changed(); - } - - if (ImGui.IsItemHovered()) - { - ImGui.SetTooltip(Localizer.GetString(localizedStringName, true)); - } - } - - private void DrawSimpleCheckbox(string localizedString, ref bool isChecked, System.Action changed) - { - if (ImGui.Checkbox($"###{localizedString}", ref isChecked)) - { - changed(); - } - - if (ImGui.IsItemHovered()) - { - ImGui.SetTooltip(localizedString); - } - } - - private void DrawColorButton(string colorId, Vector4 color, System.Action clicked) - { - ImGui.PushStyleColor(ImGuiCol.Button, color); - ImGui.PushStyleColor(ImGuiCol.ButtonHovered, color); - ImGui.PushStyleColor(ImGuiCol.ButtonActive, color); ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, new Vector2(0, 0)); ImGui.PushStyleVar(ImGuiStyleVar.ItemInnerSpacing, new Vector2(0, 0)); - ImGui.PushStyleVar(ImGuiStyleVar.FrameRounding, 0); - if (ImGui.Button($"###{colorId}", ImGuiHelpers.ScaledVector2(23))) + ImGui.PushFont(UiBuilder.IconFont); + ImGui.SetCursorPosX(ImGui.GetCursorPos().X + ImGui.GetContentRegionAvail().X - hard23); + if (ImGui.Button(FontAwesomeIcon.TrashAlt.ToIconString())) { - clicked(); + m_PluginData.RemoveCustomTagFromIdentities(tag); + m_PluginData.AllCustomTags.Children.Remove(tag); + m_PluginData.CustomTags.Remove(tag); + SaveSettings(); + + Select(m_PluginData.AllCustomTags); } - ImGui.PopStyleVar(); + ImGui.PopFont(); ImGui.PopStyleVar(); ImGui.PopStyleVar(); ImGui.PopStyleColor(); @@ -1324,167 +536,945 @@ namespace PlayerTags.Configuration if (ImGui.IsItemHovered()) { - ImGui.SetTooltip(colorId); + ImGui.SetTooltip(Strings.Loc_Static_RemoveCustomTag_Description); } } - private void DrawTextBox(string localizedStringName, ref string text, System.Action changed) + + // Save the node expansion state + if (isOpened && tag.Children.Any() && !tag.IsExpanded.Value) { - ImGui.SetNextItemWidth(ImGui.CalcItemWidth()); - - var oldText = text; - ImGui.InputText($"###{localizedStringName}", ref text, 1024); - if (text != oldText) - { - changed(); - } - - if (ImGui.IsItemHovered()) - { - ImGui.SetTooltip(Localizer.GetString(localizedStringName, true)); - } + tag.IsExpanded.Value = true; + SaveSettings(); + } + else if (!isOpened && tag.IsExpanded.Value) + { + tag.IsExpanded.Value = false; + SaveSettings(); } - private void DrawUIColorPicker(System.Action colorSelected) - { - const int columnCount = 12; - int currentColumn = 0; - foreach (var uiColor in UIColorHelper.UIColors) + + // Render the child nodes + if (isOpened) + { + ImGui.TreePush(); + foreach (var childTag in tag.Children.OrderBy(child => GetTreeItemName(child)).ToArray()) { - if (currentColumn % columnCount != 0) + DrawTree(childTag); + } + ImGui.TreePop(); + } + + + ImGui.PopID(); + } + + public void DrawControls(Tag tag) + { + var hard23 = ScalePoints(23); + ImGui.PushID(tag.GetHashCode().ToString()); + + // Render the add property override button and popup + if (ImGui.IsPopupOpen("AddPopup")) + { + ImGui.PushStyleColor(ImGuiCol.Button, new Vector4(0.2f, 0.6f, 0.2f, 1)); + } + else + { + ImGui.PushStyleColor(ImGuiCol.Button, new Vector4(0.1f, 0.3f, 0.1f, 1)); + } + ImGui.PushStyleColor(ImGuiCol.ButtonHovered, new Vector4(0.2f, 0.6f, 0.2f, 1)); + ImGui.PushStyleColor(ImGuiCol.ButtonActive, new Vector4(0.2f, 0.6f, 0.2f, 1)); + + ImGui.PushFont(UiBuilder.IconFont); + if (ImGui.Button(FontAwesomeIcon.Plus.ToIconString(), new Vector2(hard23, hard23))) + { + ImGui.OpenPopup("AddPopup"); + } + ImGui.PopFont(); + ImGui.PopStyleColor(); + ImGui.PopStyleColor(); + ImGui.PopStyleColor(); + + bool wasPaddingConsumed = false; + ImGui.PushStyleVar(ImGuiStyleVar.WindowPadding, ImGuiHelpers.ScaledVector2(0, 5)); + ImGui.SetNextWindowPos(ImGui.GetCursorScreenPos() - ImGuiHelpers.ScaledVector2(0, 4)); + if (ImGui.BeginPopup("AddPopup")) + { + wasPaddingConsumed = true; + ImGui.PopStyleVar(); + + ImGui.PushStyleColor(ImGuiCol.FrameBg, new Vector4(0f, 0f, 0f, 0)); + if (ImGui.BeginListBox("###SelectableInheritables")) + { + IEnumerable> inheritables1 = tag.Inheritables.Where(inheritable => inheritable.Value.Behavior == InheritableBehavior.Inherit); + var selectedInheritables1 = inheritables1 + .Select(inheritable => + { + string? categoryId = null; + + var field = tag.GetType().GetField(inheritable.Key); + if (field != null) + { + var inheritableCategory = field.GetCustomAttributes(typeof(InheritableCategoryAttribute), false).Cast().FirstOrDefault(); + if (inheritableCategory != null) + { + categoryId = inheritableCategory.CategoryId; + } + } + + return new + { + Inheritable = inheritable, + CategoryId = categoryId, + LocalizedName = Localizer.GetString(inheritable.Key, false) + }; + }).Where(inheritable => inheritable.CategoryId != null); + + var inheritableGroups1 = selectedInheritables1.GroupBy(inheritable => inheritable.CategoryId); + + foreach (var inheritableGroup in inheritableGroups1) { - ImGui.SameLine(0, 0); + if (inheritableGroup.Key != null) + { + ImGui.PushID(inheritableGroup.Key); + DrawHeading(Localizer.GetString(inheritableGroup.Key, false)); + } + + foreach (var selectedInheritable in inheritableGroup) + { + bool isSelected = false; + if (ImGui.Selectable(selectedInheritable.LocalizedName, isSelected)) + { + selectedInheritable.Inheritable.Value.Behavior = InheritableBehavior.Enabled; + + if (selectedInheritable.Inheritable.Value is InheritableValue inheritableBool) + { + inheritableBool.Value = true; + } + + SaveSettings(); + ImGui.CloseCurrentPopup(); + } + + if (isSelected) + { + ImGui.SetItemDefaultFocus(); + } + + if (ImGui.IsItemHovered()) + { + ImGui.SetTooltip(Localizer.GetString(selectedInheritable.Inheritable.Key, true)); + } + } + + if (inheritableGroup.Key != null) + { + ImGui.PopID(); + } } - DrawColorButton(uiColor.RowId.ToString(), UIColorHelper.ToColor(uiColor), () => { colorSelected(uiColor); }); - currentColumn++; + ImGui.EndListBox(); + } + ImGui.PopStyleColor(); + ImGui.EndPopup(); + } + if (!wasPaddingConsumed) + { + ImGui.PopStyleVar(); + } + + if (ImGui.IsItemHovered()) + { + ImGui.SetTooltip(Strings.Loc_Static_AddPropertyOverride_Description); + } + + if (tag.HasDefaults) + { + ImGui.SameLine(); + ImGui.PushFont(UiBuilder.IconFont); + ImGui.SetCursorPosX(ImGui.GetCursorPos().X + ImGui.GetContentRegionAvail().X - hard23); + if (ImGui.Button(FontAwesomeIcon.Recycle.ToIconString())) + { + tag.SetDefaults(); + } + ImGui.PopFont(); + if (ImGui.IsItemHovered()) + { + ImGui.SetTooltip(Strings.Loc_Static_ResetDefault_Description); } } - private void SaveSettings(bool saveProxy = false) + // Render all the property overrides, and optionally allow the inherited properties to be rendered + IEnumerable> inheritables = tag.Inheritables; + if (!m_PluginConfiguration.IsShowInheritedPropertiesEnabled) { - if (saveProxy) - propertyProxy.SaveData(); - m_PluginConfiguration.Save(m_PluginData); + inheritables = inheritables.Where(inheritable => inheritable.Value.Behavior != InheritableBehavior.Inherit); } - private class PropertyProxy - { - private PluginConfiguration pluginConfig; - - public ActivityContextSelection CurrentActivityContext; - public NameplateFreeCompanyVisibility NameplateFreeCompanyVisibility; - public NameplateTitleVisibility NameplateTitleVisibility; - public NameplateTitlePosition NameplateTitlePosition; - public DeadPlayerHandling NameplateDeadPlayerHandling; - public bool IsApplyTagsToAllChatMessagesEnabled; - - public PropertyProxy(PluginConfiguration config) + var selectedInheritables = inheritables + .Select(inheritable => { - pluginConfig = config; - CurrentActivityContext = config.IsGeneralOptionsAllTheSameEnabled ? ActivityContextSelection.All : ActivityContextSelection.None; - } + string? categoryId = null; - public void LoadData() - { - var currentActivityContext = GetActivityContext(CurrentActivityContext); - 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; - } - - public void SaveData() - { - if (CurrentActivityContext == ActivityContextSelection.All) + var field = tag.GetType().GetField(inheritable.Key); + if (field != null) { - pluginConfig.IsGeneralOptionsAllTheSameEnabled = true; - foreach (var key in pluginConfig.GeneralOptions.Keys) - applyChanges(key); + var inheritableCategory = field.GetCustomAttributes(typeof(InheritableCategoryAttribute), false).Cast().FirstOrDefault(); + if (inheritableCategory != null) + { + categoryId = inheritableCategory.CategoryId; + } + } + + return new + { + Inheritable = inheritable, + CategoryId = categoryId, + LocalizedName = Localizer.GetString(inheritable.Key, false) + }; + }).Where(inheritable => inheritable.CategoryId != null); + + var inheritableGroups = selectedInheritables.GroupBy(inheritable => inheritable.CategoryId); + + foreach (var inheritableGroup in inheritableGroups) + { + ImGui.Spacing(); + ImGui.Spacing(); + if (inheritableGroup.Key != null) + { + DrawHeading(Localizer.GetString(inheritableGroup.Key, false)); + } + + foreach (var selectedInheritable in inheritableGroup) + { + if (selectedInheritable.Inheritable.Value is InheritableValue inheritableBool) + { + DrawInheritable(selectedInheritable.Inheritable.Key, inheritableBool); + } + else if (selectedInheritable.Inheritable.Value is InheritableValue inheritableUshort) + { + DrawInheritable(selectedInheritable.Inheritable.Key, inheritableUshort); + } + else if (selectedInheritable.Inheritable.Value is InheritableValue inheritableBitmapFontIcon) + { + DrawInheritable(selectedInheritable.Inheritable.Key, false, true, inheritableBitmapFontIcon); + } + else if (selectedInheritable.Inheritable.Value is InheritableValue inheritableTagPosition) + { + DrawInheritable(selectedInheritable.Inheritable.Key, true, false, inheritableTagPosition); + } + else if (selectedInheritable.Inheritable.Value is InheritableValue inheritableNameplateElement) + { + DrawInheritable(selectedInheritable.Inheritable.Key, true, false, inheritableNameplateElement); + } + else if (selectedInheritable.Inheritable.Value is InheritableValue inheritableFreeCompanyVisibility) + { + DrawInheritable(selectedInheritable.Inheritable.Key, true, false, inheritableFreeCompanyVisibility); + } + else if (selectedInheritable.Inheritable.Value is InheritableValue inheritableNameplateTitleVisibility) + { + DrawInheritable(selectedInheritable.Inheritable.Key, true, false, inheritableNameplateTitleVisibility); + } + else if (selectedInheritable.Inheritable.Value is InheritableValue inheritableNameplateTitlePosition) + { + 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> inheritableXivChatType) + { + DrawMultiselect(selectedInheritable.Inheritable.Key, inheritableXivChatType); + } + else if (selectedInheritable.Inheritable.Value is InheritableReference inheritableString) + { + DrawInheritable(selectedInheritable.Inheritable.Key, inheritableString); } else { - pluginConfig.IsGeneralOptionsAllTheSameEnabled = false; - applyChanges(GetActivityContext(CurrentActivityContext)); + PluginServices.PluginLog.Warning($"Rendering for inheritable option not implemented: {selectedInheritable.Inheritable.Key}"); } - - void applyChanges(ActivityType key) - { - 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; - } - } - - private ActivityType GetActivityContext(ActivityContextSelection selection) - { - ActivityType result; - - switch (selection) - { - case ActivityContextSelection.PveDuty: - result = ActivityType.PveDuty; - break; - case ActivityContextSelection.PvpDuty: - result = ActivityType.PvpDuty; - break; - case ActivityContextSelection.All: - case ActivityContextSelection.None: - default: - result = ActivityType.None; - break; - } - - return result; } } - private enum ActivityContextSelection + + ImGui.PopID(); + } + + private void DrawRemovePropertyOverrideButton(IInheritable inheritable) + { + ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, new Vector2(0, 0)); + ImGui.PushStyleColor(ImGuiCol.Button, new Vector4(0.3f, 0.1f, 0.1f, 1)); + ImGui.PushStyleColor(ImGuiCol.ButtonHovered, new Vector4(0.6f, 0.2f, 0.2f, 1)); + ImGui.PushStyleColor(ImGuiCol.ButtonActive, new Vector4(0.6f, 0.2f, 0.2f, 1)); + ImGui.PushFont(UiBuilder.IconFont); + if (ImGui.Button(FontAwesomeIcon.TrashAlt.ToIconString(), ImGuiHelpers.ScaledVector2(23))) { - All, - None, - PveDuty, - PvpDuty + inheritable.Behavior = InheritableBehavior.Inherit; + SaveSettings(); + } + ImGui.PopFont(); + ImGui.PopStyleColor(); + ImGui.PopStyleColor(); + ImGui.PopStyleColor(); + ImGui.PopStyleVar(); + if (ImGui.IsItemHovered()) + { + ImGui.SetTooltip(Strings.Loc_Static_RemovePropertyOverride_Description); + } + } + + private void DrawInheritable(string localizedStringName, InheritableValue inheritable) + { + bool isDisabled = inheritable.Behavior == InheritableBehavior.Inherit; + if (isDisabled) + { + ImGui.PushStyleVar(ImGuiStyleVar.Alpha, 0.25f); } - private class EnumMultiselectProxy where TEnum : Enum - { - public List Entries { get; } = new(); + ImGui.BeginGroup(); + ImGui.BeginChild(inheritable.GetHashCode().ToString(), ImGuiHelpers.ScaledVector2(0, 50)); - public EnumMultiselectProxy(List target) + ImGui.Text(Localizer.GetString(localizedStringName, false)); + if (ImGui.IsItemHovered()) + { + ImGui.PushStyleVar(ImGuiStyleVar.Alpha, 1f); + ImGui.SetTooltip(Localizer.GetString(localizedStringName, true)); + ImGui.PopStyleVar(); + } + + if (isDisabled) + { + ImGui.SameLine(); + ImGui.Text(Strings.Loc_Static_Inherited); + } + + if (isDisabled) + { + bool value = inheritable.InheritedValue != null ? inheritable.InheritedValue.Value : false; + DrawCheckbox("IsEnabled", false, ref value, () => { - foreach (TEnum value in Enum.GetValues(typeof(TEnum))) - Entries.Add(new(value, target.Contains(value))); + }); + } + else + { + DrawCheckbox("IsEnabled", false, ref inheritable.Value, () => + { + SaveSettings(); + }); + + ImGui.SameLine(); + DrawRemovePropertyOverrideButton(inheritable); + } + + ImGui.EndChild(); + ImGui.EndGroup(); + + if (isDisabled) + { + ImGui.PopStyleVar(); + } + } + + private void DrawInheritable(string localizedStringName, bool shouldLocalizeNames, bool shouldOrderNames, InheritableValue inheritable) + where TEnum : struct, Enum + { + bool isDisabled = inheritable.Behavior == InheritableBehavior.Inherit; + if (isDisabled) + { + ImGui.PushStyleVar(ImGuiStyleVar.Alpha, 0.25f); + } + + ImGui.BeginGroup(); + ImGui.BeginChild(inheritable.GetHashCode().ToString(), ImGuiHelpers.ScaledVector2(0, 50)); + + ImGui.Text(Localizer.GetString(localizedStringName, false)); + if (ImGui.IsItemHovered()) + { + ImGui.PushStyleVar(ImGuiStyleVar.Alpha, 1f); + ImGui.SetTooltip(Localizer.GetString(localizedStringName, true)); + ImGui.PopStyleVar(); + } + + if (isDisabled) + { + ImGui.SameLine(); + ImGui.Text(Strings.Loc_Static_Inherited); + } + + if (isDisabled) + { + bool isEnabled = inheritable.InheritedValue != null; + DrawCheckbox("IsEnabled", false, ref isEnabled, () => { }); + + if (isEnabled) + { + ImGui.SameLine(); + ImGui.SetNextItemWidth(ScalePoints(200)); + ImGui.BeginChild(inheritable.GetHashCode().ToString(), ImGuiHelpers.ScaledVector2(200, 0)); + TEnum value = inheritable.InheritedValue != null ? inheritable.InheritedValue.Value : default(TEnum); + DrawComboBox(false, shouldLocalizeNames, shouldOrderNames, ref value, () => { }); + ImGui.EndChild(); + } + } + else + { + bool isEnabled = inheritable.Behavior == InheritableBehavior.Enabled; + DrawCheckbox("IsEnabled", false, ref isEnabled, () => + { + if (isEnabled) + { + inheritable.Behavior = InheritableBehavior.Enabled; + } + else + { + inheritable.Behavior = InheritableBehavior.Disabled; + } + SaveSettings(); + }); + + if (isEnabled) + { + ImGui.SameLine(); + ImGui.SetNextItemWidth(ScalePoints(200)); + ImGui.BeginChild(inheritable.GetHashCode().ToString(), ImGuiHelpers.ScaledVector2(200, 0)); + DrawComboBox(false, shouldLocalizeNames, shouldOrderNames, ref inheritable.Value, () => { SaveSettings(); }); + ImGui.EndChild(); } - public void ApplyTo(List target) + ImGui.SameLine(); + DrawRemovePropertyOverrideButton(inheritable); + } + + ImGui.EndChild(); + ImGui.EndGroup(); + + if (isDisabled) + { + ImGui.PopStyleVar(); + } + } + + private void DrawInheritable(string localizedStringName, InheritableValue inheritable) + { + bool isDisabled = inheritable.Behavior == InheritableBehavior.Inherit; + if (isDisabled) + { + ImGui.PushStyleVar(ImGuiStyleVar.Alpha, 0.25f); + } + + ImGui.BeginGroup(); + ImGui.BeginChild(inheritable.GetHashCode().ToString(), ImGuiHelpers.ScaledVector2(180, 50)); + + ImGui.Text(Localizer.GetString(localizedStringName, false)); + if (ImGui.IsItemHovered()) + { + ImGui.PushStyleVar(ImGuiStyleVar.Alpha, 1f); + ImGui.SetTooltip(Localizer.GetString(localizedStringName, true)); + ImGui.PopStyleVar(); + } + + if (isDisabled) + { + ImGui.SameLine(); + ImGui.Text(Strings.Loc_Static_Inherited); + } + + if (isDisabled) + { + bool isEnabled = inheritable.InheritedValue != null; + DrawCheckbox("IsEnabled", false, ref isEnabled, () => { }); + + if (isEnabled) { - foreach (var entry in Entries) + ImGui.SameLine(); + ushort value = inheritable.InheritedValue != null ? inheritable.InheritedValue.Value : default(ushort); + DrawColorButton(value.ToString(), UIColorHelper.ToColor(value), () => { }); + } + } + else + { + bool isEnabled = inheritable.Behavior == InheritableBehavior.Enabled; + DrawCheckbox("IsEnabled", false, ref isEnabled, () => + { + if (isEnabled) { - if (entry.Enabled) + inheritable.Behavior = InheritableBehavior.Enabled; + } + else + { + inheritable.Behavior = InheritableBehavior.Disabled; + } + SaveSettings(); + }); + + if (isEnabled) + { + ImGui.SameLine(); + DrawColorButton( + inheritable.Value.ToString(), + UIColorHelper.ToColor(inheritable.Value), + () => { - if (!target.Contains(entry.Value)) - target.Add(entry.Value); - } - else if (target.Contains(entry.Value)) - target.Remove(entry.Value); + m_ColorPickerPopupDataContext = inheritable; + ImGui.OpenPopup("ColorPickerPopup"); + }); + } + + bool wasStyleConsumed = false; + ImGui.SetNextWindowPos(ImGui.GetCursorScreenPos() + ImGuiHelpers.ScaledVector2(31, 0)); + ImGui.PushStyleVar(ImGuiStyleVar.WindowPadding, new Vector2(0, 0)); + if (ImGui.BeginPopup("ColorPickerPopup")) + { + wasStyleConsumed = true; + ImGui.PopStyleVar(); + + DrawUIColorPicker( + (UIColor value) => + { + if (m_ColorPickerPopupDataContext != null) + { + m_ColorPickerPopupDataContext.Value = (ushort)value.RowId; + m_ColorPickerPopupDataContext = null; + SaveSettings(); + } + + ImGui.CloseCurrentPopup(); + }); + + ImGui.EndPopup(); + } + if (!wasStyleConsumed) + { + ImGui.PopStyleVar(); + } + + ImGui.SameLine(); + DrawRemovePropertyOverrideButton(inheritable); + } + + ImGui.EndChild(); + ImGui.EndGroup(); + + if (isDisabled) + { + ImGui.PopStyleVar(); + } + } + + private void DrawInheritable(string localizedStringName, InheritableReference inheritable) + { + bool isDisabled = inheritable.Behavior == InheritableBehavior.Inherit; + if (isDisabled) + { + ImGui.PushStyleVar(ImGuiStyleVar.Alpha, 0.25f); + } + + ImGui.BeginGroup(); + ImGui.BeginChild(inheritable.GetHashCode().ToString(), ImGuiHelpers.ScaledVector2(0, 50)); + + ImGui.Text(Localizer.GetString(localizedStringName, false)); + if (ImGui.IsItemHovered()) + { + ImGui.PushStyleVar(ImGuiStyleVar.Alpha, 1f); + ImGui.SetTooltip(Localizer.GetString(localizedStringName, true)); + ImGui.PopStyleVar(); + } + + if (isDisabled) + { + ImGui.SameLine(); + ImGui.Text(Strings.Loc_Static_Inherited); + } + + if (isDisabled) + { + bool isEnabled = inheritable.InheritedValue != null; + DrawCheckbox("IsEnabled", false, ref isEnabled, () => + { + }); + + if (isEnabled) + { + ImGui.SameLine(); + ImGui.SetNextItemWidth(ScalePoints(200)); + ImGui.BeginChild(inheritable.GetHashCode().ToString(), ImGuiHelpers.ScaledVector2(200, 0)); + string value = inheritable.Value; + DrawTextBox(localizedStringName, ref value, () => { }); + ImGui.EndChild(); + } + } + else + { + bool isEnabled = inheritable.Behavior == InheritableBehavior.Enabled; + DrawCheckbox("IsEnabled", false, ref isEnabled, () => + { + if (isEnabled) + { + inheritable.Behavior = InheritableBehavior.Enabled; + } + else + { + inheritable.Behavior = InheritableBehavior.Disabled; + } + SaveSettings(); + }); + + if (isEnabled) + { + ImGui.SameLine(); + ImGui.SetNextItemWidth(ScalePoints(200)); + ImGui.BeginChild(inheritable.GetHashCode().ToString(), ImGuiHelpers.ScaledVector2(200, 0)); + DrawTextBox(localizedStringName, ref inheritable.Value, () => { SaveSettings(); }); + ImGui.EndChild(); + } + + ImGui.SameLine(); + DrawRemovePropertyOverrideButton(inheritable); + } + + ImGui.EndChild(); + ImGui.EndGroup(); + + if (isDisabled) + { + ImGui.PopStyleVar(); + } + } + + private void DrawHeading(string label) + { + 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; + List proxyKey = isDisabled ? inheritable.InheritedValue : inheritable.Value; + + if (isDisabled) + proxyKey = inheritable.InheritedValue; + 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, false), ref isDisabled ? ref tempval : ref entry.Enabled); + + if (ImGui.IsItemHovered()) + ImGui.SetTooltip(Localizer.GetString(entryName, true)); + + if (isClicked && !isDisabled) + { + var newList = proxyKey.ToList(); + proxy.ApplyTo(newList); + inheritable.Value = newList; + SaveSettings(); + } + } + } + + if (isDisabled) + ImGui.PopStyleVar(); + } + + 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(); + + ImGui.SetNextItemWidth(ImGui.CalcItemWidth()); + if (ImGui.BeginCombo($"###{currentValue.GetType().Name}", currentDisplayName)) + { + var displayNames = Enum.GetValues(typeof(TEnum)).Cast() + .Select(value => new { Value = value, DisplayName = shouldLocalizeNames ? Localizer.GetString(value, false) : value.ToString() }); + + if (shouldOrderNames) + { + displayNames = displayNames.OrderBy(displayEnum => displayEnum.DisplayName); + } + + foreach (var orderedDisplayName in displayNames) + { + bool isSelected = orderedDisplayName.Value.Equals(currentValue); + if (ImGui.Selectable(orderedDisplayName.DisplayName, isSelected)) + { + currentValue = orderedDisplayName.Value; + changed(); + } + + if (isSelected) + { + ImGui.SetItemDefaultFocus(); + } + + if (ImGui.IsItemHovered() && shouldLocalizeNames) + { + ImGui.SetTooltip(Localizer.GetString(orderedDisplayName.Value, true)); } } - public class Entry - { - public TEnum Value { get; set; } - public bool Enabled; + ImGui.EndCombo(); + } - public Entry(TEnum value, bool enabled) + if (ImGui.IsItemHovered() && shouldLocalizeNames) + { + if (showToolTipToLabel) + ImGui.SetTooltip(Localizer.GetString(typeof(TEnum).Name, true)); + else + ImGui.SetTooltip(Localizer.GetString(currentValue, true)); + } + } + + private void DrawCheckbox(string localizedStringName, bool hasLabel, ref bool isChecked, System.Action changed) + { + if (ImGui.Checkbox(hasLabel ? Localizer.GetString(localizedStringName, false) : $"###{Localizer.GetString(localizedStringName, false)}", ref isChecked)) + { + changed(); + } + + if (ImGui.IsItemHovered()) + { + ImGui.SetTooltip(Localizer.GetString(localizedStringName, true)); + } + } + + private void DrawSimpleCheckbox(string localizedString, ref bool isChecked, System.Action changed) + { + if (ImGui.Checkbox($"###{localizedString}", ref isChecked)) + { + changed(); + } + + if (ImGui.IsItemHovered()) + { + ImGui.SetTooltip(localizedString); + } + } + + private void DrawColorButton(string colorId, Vector4 color, System.Action clicked) + { + ImGui.PushStyleColor(ImGuiCol.Button, color); + ImGui.PushStyleColor(ImGuiCol.ButtonHovered, color); + ImGui.PushStyleColor(ImGuiCol.ButtonActive, color); + ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, new Vector2(0, 0)); + ImGui.PushStyleVar(ImGuiStyleVar.ItemInnerSpacing, new Vector2(0, 0)); + ImGui.PushStyleVar(ImGuiStyleVar.FrameRounding, 0); + if (ImGui.Button($"###{colorId}", ImGuiHelpers.ScaledVector2(23))) + { + clicked(); + } + ImGui.PopStyleVar(); + ImGui.PopStyleVar(); + ImGui.PopStyleVar(); + ImGui.PopStyleColor(); + ImGui.PopStyleColor(); + ImGui.PopStyleColor(); + + if (ImGui.IsItemHovered()) + { + ImGui.SetTooltip(colorId); + } + } + + private void DrawTextBox(string localizedStringName, ref string text, System.Action changed) + { + ImGui.SetNextItemWidth(ImGui.CalcItemWidth()); + + var oldText = text; + ImGui.InputText($"###{localizedStringName}", ref text, 1024); + if (text != oldText) + { + changed(); + } + + if (ImGui.IsItemHovered()) + { + ImGui.SetTooltip(Localizer.GetString(localizedStringName, true)); + } + } + + private void DrawUIColorPicker(System.Action colorSelected) + { + const int columnCount = 12; + + int currentColumn = 0; + foreach (var uiColor in UIColorHelper.UIColors) + { + if (currentColumn % columnCount != 0) + { + ImGui.SameLine(0, 0); + } + + DrawColorButton(uiColor.RowId.ToString(), UIColorHelper.ToColor(uiColor), () => { colorSelected(uiColor); }); + currentColumn++; + } + } + + private void SaveSettings(bool saveProxy = false) + { + if (saveProxy) + propertyProxy.SaveData(); + m_PluginConfiguration.Save(m_PluginData); + } + + private class PropertyProxy + { + private PluginConfiguration pluginConfig; + + public ActivityContextSelection CurrentActivityContext; + public NameplateFreeCompanyVisibility NameplateFreeCompanyVisibility; + public NameplateTitleVisibility NameplateTitleVisibility; + public NameplateTitlePosition NameplateTitlePosition; + public DeadPlayerHandling NameplateDeadPlayerHandling; + public bool IsApplyTagsToAllChatMessagesEnabled; + + public PropertyProxy(PluginConfiguration config) + { + pluginConfig = config; + CurrentActivityContext = config.IsGeneralOptionsAllTheSameEnabled ? ActivityContextSelection.All : ActivityContextSelection.None; + } + + public void LoadData() + { + var currentActivityContext = GetActivityContext(CurrentActivityContext); + 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; + } + + public void SaveData() + { + if (CurrentActivityContext == ActivityContextSelection.All) + { + pluginConfig.IsGeneralOptionsAllTheSameEnabled = true; + foreach (var key in pluginConfig.GeneralOptions.Keys) + applyChanges(key); + } + else + { + pluginConfig.IsGeneralOptionsAllTheSameEnabled = false; + applyChanges(GetActivityContext(CurrentActivityContext)); + } + + void applyChanges(ActivityType key) + { + 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; + } + } + + private ActivityType GetActivityContext(ActivityContextSelection selection) + { + ActivityType result; + + switch (selection) + { + case ActivityContextSelection.PveDuty: + result = ActivityType.PveDuty; + break; + case ActivityContextSelection.PvpDuty: + result = ActivityType.PvpDuty; + break; + case ActivityContextSelection.All: + case ActivityContextSelection.None: + default: + result = ActivityType.None; + break; + } + + return result; + } + } + + private enum ActivityContextSelection + { + All, + None, + PveDuty, + PvpDuty + } + + private class EnumMultiselectProxy where TEnum : Enum + { + public List Entries { get; } = []; + + public EnumMultiselectProxy(List target) + { + foreach (TEnum value in Enum.GetValues(typeof(TEnum))) + Entries.Add(new(value, target.Contains(value))); + } + + public void ApplyTo(List target) + { + foreach (var entry in Entries) + { + if (entry.Enabled) { - Value = value; - Enabled = 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/ActivityContext.cs b/PlayerTags/Data/ActivityContext.cs index ec78386..b7de0d4 100644 --- a/PlayerTags/Data/ActivityContext.cs +++ b/PlayerTags/Data/ActivityContext.cs @@ -1,15 +1,14 @@ using Newtonsoft.Json; using System; -namespace PlayerTags.Data +namespace PlayerTags.Data; + +[Obsolete] +[Flags] +[JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] +public enum ActivityContext { - [Obsolete] - [Flags] - [JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] - public enum ActivityContext - { - None = 0x0, - PveDuty = 0x1, - PvpDuty = 0x2, - } + None = 0x0, + PveDuty = 0x1, + PvpDuty = 0x2, } diff --git a/PlayerTags/Data/ActivityContextHelper.cs b/PlayerTags/Data/ActivityContextHelper.cs index cf82c52..d878c04 100644 --- a/PlayerTags/Data/ActivityContextHelper.cs +++ b/PlayerTags/Data/ActivityContextHelper.cs @@ -1,23 +1,22 @@ using Pilz.Dalamud.ActivityContexts; -namespace PlayerTags.Data +namespace PlayerTags.Data; + +public static class ActivityContextHelper { - public static class ActivityContextHelper + public static bool GetIsVisible(ActivityType playerContext, bool desiredPveDutyVisibility, bool desiredPvpDutyVisibility, bool desiredOthersVisibility) { - public static bool GetIsVisible(ActivityType playerContext, bool desiredPveDutyVisibility, bool desiredPvpDutyVisibility, bool desiredOthersVisibility) - { - bool isVisible = false; + bool isVisible = false; - if (playerContext.HasFlag(ActivityType.PveDuty)) - isVisible |= desiredPveDutyVisibility; + if (playerContext.HasFlag(ActivityType.PveDuty)) + isVisible |= desiredPveDutyVisibility; - if (playerContext.HasFlag(ActivityType.PvpDuty)) - isVisible |= desiredPvpDutyVisibility; + if (playerContext.HasFlag(ActivityType.PvpDuty)) + isVisible |= desiredPvpDutyVisibility; - if (playerContext == ActivityType.None) - isVisible |= desiredOthersVisibility; + if (playerContext == ActivityType.None) + isVisible |= desiredOthersVisibility; - return isVisible; - } + return isVisible; } } diff --git a/PlayerTags/Data/DeadPlayerHandling.cs b/PlayerTags/Data/DeadPlayerHandling.cs index 9fd6883..29fe218 100644 --- a/PlayerTags/Data/DeadPlayerHandling.cs +++ b/PlayerTags/Data/DeadPlayerHandling.cs @@ -1,15 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +namespace PlayerTags.Data; -namespace PlayerTags.Data +public enum DeadPlayerHandling { - public enum DeadPlayerHandling - { - Ignore, - Include, - GrayOut - } + Ignore, + Include, + GrayOut } diff --git a/PlayerTags/Data/DefaultPluginData.cs b/PlayerTags/Data/DefaultPluginData.cs index d84f8c8..445ec71 100644 --- a/PlayerTags/Data/DefaultPluginData.cs +++ b/PlayerTags/Data/DefaultPluginData.cs @@ -5,487 +5,486 @@ using System; using System.Collections.Generic; using System.Linq; -namespace PlayerTags.Data +namespace PlayerTags.Data; + +public class DefaultPluginData { - public class DefaultPluginData + public Tag AllTags { get; private set; } + + 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; private set; } + + public DefaultPluginData(DefaultPluginDataTemplate template) { - public Tag AllTags { get; private set; } + SetupTemplate(template); + } - 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; } + private void SetupTemplate(DefaultPluginDataTemplate template) + { + Clear(); - public Tag AllCustomTags { get; private set; } - - public DefaultPluginData(DefaultPluginDataTemplate template) + switch (template) { - SetupTemplate(template); + case DefaultPluginDataTemplate.None: + SetupTemplateNone(); + break; + case DefaultPluginDataTemplate.Basic: + SetupTemplateBasic(); + break; + case DefaultPluginDataTemplate.Simple: + SetupTemplateSimple(); + break; + case DefaultPluginDataTemplate.Full: + SetupTemplateFull(); + break; } - private void SetupTemplate(DefaultPluginDataTemplate template) + SetupJobTags(); + } + + private void Clear() + { + RoleTags = []; + DpsRoleTags = []; + RangedDpsRoleTags = []; + LandHandRoleTags = []; + } + + private void SetupTemplateNone() + { + AllTags = new Tag() { - Clear(); + IsSelected = true, + IsExpanded = true, + }; - 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() + AllRoleTags = new Tag() { - RoleTags = new Dictionary(); - DpsRoleTags = new Dictionary(); - RangedDpsRoleTags = new Dictionary(); - LandHandRoleTags = new Dictionary(); - } + IsSelected = false, + IsExpanded = true, + }; - private void SetupTemplateNone() + RoleTags[Role.LandHand] = new Tag() { - AllTags = new Tag() - { - IsSelected = true, - IsExpanded = true, - }; + IsSelected = false, + IsExpanded = false, + }; - 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() + RoleTags[Role.Tank] = new Tag() { - AllTags = new Tag() - { - IsSelected = true, - IsExpanded = true, + IsSelected = false, + IsExpanded = false, + }; - TagPositionInChat = TagPosition.Before, - InsertBehindNumberPrefixInChat = true, - TagPositionInNameplates = TagPosition.Replace, - TagTargetInNameplates = NameplateElement.Title, - - TargetChatTypes = new List(Enum.GetValues()), - TargetChatTypesIncludeUndefined = 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, - 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() + RoleTags[Role.Healer] = new Tag() { - AllTags = new Tag() - { - IsSelected = true, - IsExpanded = true, - TagPositionInChat = TagPosition.Before, - InsertBehindNumberPrefixInChat = true, - TagPositionInNameplates = TagPosition.Replace, - TagTargetInNameplates = NameplateElement.Title, - IsTextItalic = true, + IsSelected = false, + IsExpanded = false, + }; - IsVisibleInOverworld = true, - IsVisibleInPveDuties = true, - IsVisibleInPvpDuties = true, - - IsVisibleForSelf = true, - IsVisibleForFriendPlayers = true, - IsVisibleForPartyPlayers = true, - IsVisibleForAlliancePlayers = true, - IsVisibleForEnemyPlayers = true, - IsVisibleForOtherPlayers = true, - - TargetChatTypes = new List(Enum.GetValues()), - TargetChatTypesIncludeUndefined = true, - }; - - AllRoleTags = new Tag() - { - IsSelected = false, - IsExpanded = true, - IsRoleIconVisibleInChat = true, - IsTextVisibleInChat = true, - IsRoleIconVisibleInNameplates = true, - IsTextVisibleInNameplates = true, - IsTextColorAppliedToChatName = 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 SetupTemplateFull() + RoleTags[Role.Dps] = new Tag() { - AllTags = new Tag() - { - IsSelected = true, - IsExpanded = true, - TagPositionInChat = TagPosition.Before, - InsertBehindNumberPrefixInChat = true, - TagPositionInNameplates = TagPosition.Replace, - TagTargetInNameplates = NameplateElement.Title, - IsTextItalic = true, + IsSelected = false, + IsExpanded = true, + }; - IsVisibleInOverworld = true, - IsVisibleInPveDuties = true, - IsVisibleInPvpDuties = true, - - IsVisibleForSelf = true, - IsVisibleForFriendPlayers = true, - IsVisibleForPartyPlayers = true, - IsVisibleForAlliancePlayers = true, - IsVisibleForEnemyPlayers = true, - IsVisibleForOtherPlayers = true, - - TargetChatTypes = new List(Enum.GetValues()), - TargetChatTypesIncludeUndefined = true, - }; - - 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() + DpsRoleTags[DpsRole.Melee] = new Tag() { - JobTags = new Dictionary(); + IsSelected = false, + IsExpanded = false, + }; - var classJobs = PluginServices.DataManager.GetExcelSheet(); - if (classJobs != null) + 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()), + TargetChatTypesIncludeUndefined = 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, + 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() + { + 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()), + TargetChatTypesIncludeUndefined = true, + }; + + AllRoleTags = new Tag() + { + IsSelected = false, + IsExpanded = true, + IsRoleIconVisibleInChat = true, + IsTextVisibleInChat = true, + IsRoleIconVisibleInNameplates = true, + IsTextVisibleInNameplates = true, + IsTextColorAppliedToChatName = 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 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()), + TargetChatTypesIncludeUndefined = true, + }; + + 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 = []; + + var classJobs = PluginServices.DataManager.GetExcelSheet(); + if (classJobs != null) + { + foreach ((var role, var roleTagChanges) in RoleTags) { - foreach ((var role, var roleTagChanges) in RoleTags) + foreach (var classJob in classJobs.Where(classJob => RoleHelper.RolesByRoleId[classJob.Role] == role && !string.IsNullOrEmpty(classJob.Abbreviation.RawString))) { - foreach (var classJob in classJobs.Where(classJob => RoleHelper.RolesByRoleId[classJob.Role] == role && !string.IsNullOrEmpty(classJob.Abbreviation.RawString))) + if (!JobTags.ContainsKey(classJob.Abbreviation.RawString)) { - if (!JobTags.ContainsKey(classJob.Abbreviation.RawString)) + JobTags[classJob.Abbreviation.RawString] = new Tag() { - JobTags[classJob.Abbreviation.RawString] = new Tag() - { - IsSelected = false, - IsExpanded = false, - Text = classJob.Abbreviation.RawString, - }; - } + IsSelected = false, + IsExpanded = false, + Text = classJob.Abbreviation.RawString, + }; } } } diff --git a/PlayerTags/Data/DefaultPluginDataTemplate.cs b/PlayerTags/Data/DefaultPluginDataTemplate.cs index 98211ea..0c90d59 100644 --- a/PlayerTags/Data/DefaultPluginDataTemplate.cs +++ b/PlayerTags/Data/DefaultPluginDataTemplate.cs @@ -1,16 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +namespace PlayerTags.Data; -namespace PlayerTags.Data +public enum DefaultPluginDataTemplate { - public enum DefaultPluginDataTemplate - { - None, - Basic, - Simple, - Full - } + None, + Basic, + Simple, + Full } diff --git a/PlayerTags/Data/DpsRole.cs b/PlayerTags/Data/DpsRole.cs index 2559921..f9f7fb8 100644 --- a/PlayerTags/Data/DpsRole.cs +++ b/PlayerTags/Data/DpsRole.cs @@ -1,8 +1,7 @@ -namespace PlayerTags.Data +namespace PlayerTags.Data; + +public enum DpsRole { - public enum DpsRole - { - Melee, - Ranged - } + Melee, + Ranged } diff --git a/PlayerTags/Data/Identity.cs b/PlayerTags/Data/Identity.cs index 2ef5444..9f0f560 100644 --- a/PlayerTags/Data/Identity.cs +++ b/PlayerTags/Data/Identity.cs @@ -2,100 +2,99 @@ using System; using System.Collections.Generic; -namespace PlayerTags.Data +namespace PlayerTags.Data; + +public class Identity : IComparable, IEquatable { - public class Identity : IComparable, IEquatable + public string Name { get; init; } + public uint? WorldId { get; set; } = null; + public List CustomTagIds { get; init; } = []; + + [JsonIgnore] + public string? WorldName => WorldHelper.GetWorldName(WorldId); + + public Identity(string name) { - public string Name { get; init; } - public uint? WorldId { get; set; } = null; - public List CustomTagIds { get; init; } = new List(); + Name = name; + } - [JsonIgnore] - public string? WorldName => WorldHelper.GetWorldName(WorldId); + public override string ToString() + { + string str = Name; - public Identity(string name) + if (WorldId != null) { - Name = name; + str += $"@{WorldName}"; } - public override string ToString() + return str; + } + + public int CompareTo(Identity? other) + { + string? otherToString = null; + if (!(other is null)) { - string str = Name; - - if (WorldId != null) - { - str += $"@{WorldName}"; - } - - return str; + otherToString = other.ToString(); } - public int CompareTo(Identity? other) - { - string? otherToString = null; - if (!(other is null)) - { - otherToString = other.ToString(); - } + return ToString().CompareTo(otherToString); + } - return ToString().CompareTo(otherToString); + public override bool Equals(object? obj) + { + return obj is Identity identity && Equals(identity); + } + + public bool Equals(Identity? obj) + { + if (obj is null) + { + return false; } - public override bool Equals(object? obj) + return this == obj; + } + + public static bool operator ==(Identity? first, Identity? second) + { + if (ReferenceEquals(first, second)) { - return obj is Identity identity && Equals(identity); + return true; } - public bool Equals(Identity? obj) + if (first is null && second is null) { - if (obj is null) - { - return false; - } - - return this == obj; + return true; } - public static bool operator ==(Identity? first, Identity? second) + if (first is null || second is null) { - if (ReferenceEquals(first, second)) - { - return true; - } - - if (first is null && second is null) - { - return true; - } - - if (first is null || second is null) - { - return false; - } - - bool areNamesEqual = first.Name.ToLower().Trim() == second.Name.ToLower().Trim(); - - // If one of the worlds are null then it's technically equal as it could be promoted to the identity that does have a world - bool areWorldsEqual = first.WorldId == null || second.WorldId == null || first.WorldId == second.WorldId; - - return areNamesEqual && areWorldsEqual; + return false; } - public static bool operator !=(Identity? first, Identity? second) + bool areNamesEqual = first.Name.ToLower().Trim() == second.Name.ToLower().Trim(); + + // If one of the worlds are null then it's technically equal as it could be promoted to the identity that does have a world + bool areWorldsEqual = first.WorldId == null || second.WorldId == null || first.WorldId == second.WorldId; + + return areNamesEqual && areWorldsEqual; + } + + public static bool operator !=(Identity? first, Identity? second) + { + return !(first == second); + } + + public override int GetHashCode() + { + var hashCode = Name.GetHashCode(); + + if (WorldName != null) { - return !(first == second); + hashCode *= 17 ^ WorldName.GetHashCode(); } - public override int GetHashCode() - { - var hashCode = Name.GetHashCode(); - - if (WorldName != null) - { - hashCode *= 17 ^ WorldName.GetHashCode(); - } - - return hashCode; - } + return hashCode; } } diff --git a/PlayerTags/Data/InheritableCategoryAttribute.cs b/PlayerTags/Data/InheritableCategoryAttribute.cs index bc0e629..8ef035d 100644 --- a/PlayerTags/Data/InheritableCategoryAttribute.cs +++ b/PlayerTags/Data/InheritableCategoryAttribute.cs @@ -1,14 +1,13 @@ using System; -namespace PlayerTags.Data -{ - public class InheritableCategoryAttribute : Attribute - { - public string CategoryId { get; private set; } +namespace PlayerTags.Data; - public InheritableCategoryAttribute(string categoryId) - { - CategoryId = categoryId; - } +public class InheritableCategoryAttribute : Attribute +{ + public string CategoryId { get; private set; } + + public InheritableCategoryAttribute(string categoryId) + { + CategoryId = categoryId; } } diff --git a/PlayerTags/Data/LandHandRole.cs b/PlayerTags/Data/LandHandRole.cs index 9c257ac..2d93bc2 100644 --- a/PlayerTags/Data/LandHandRole.cs +++ b/PlayerTags/Data/LandHandRole.cs @@ -1,8 +1,7 @@ -namespace PlayerTags.Data +namespace PlayerTags.Data; + +public enum LandHandRole { - public enum LandHandRole - { - Land, - Hand - } + Land, + Hand } diff --git a/PlayerTags/Data/NameplateElement.cs b/PlayerTags/Data/NameplateElement.cs index 8a10b25..153524b 100644 --- a/PlayerTags/Data/NameplateElement.cs +++ b/PlayerTags/Data/NameplateElement.cs @@ -1,9 +1,8 @@ -namespace PlayerTags.Data +namespace PlayerTags.Data; + +public enum NameplateElement { - public enum NameplateElement - { - Name, - Title, - FreeCompany - } + Name, + Title, + FreeCompany } diff --git a/PlayerTags/Data/NameplateFreeCompanyVisibility.cs b/PlayerTags/Data/NameplateFreeCompanyVisibility.cs index 6308f8c..3c005b5 100644 --- a/PlayerTags/Data/NameplateFreeCompanyVisibility.cs +++ b/PlayerTags/Data/NameplateFreeCompanyVisibility.cs @@ -1,11 +1,10 @@ using Newtonsoft.Json; -namespace PlayerTags.Data +namespace PlayerTags.Data; + +[JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] +public enum NameplateFreeCompanyVisibility { - [JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] - public enum NameplateFreeCompanyVisibility - { - Default, - Never - } + Default, + Never } diff --git a/PlayerTags/Data/NameplateTitlePosition.cs b/PlayerTags/Data/NameplateTitlePosition.cs index 08052d8..5c332bb 100644 --- a/PlayerTags/Data/NameplateTitlePosition.cs +++ b/PlayerTags/Data/NameplateTitlePosition.cs @@ -1,12 +1,11 @@ using Newtonsoft.Json; -namespace PlayerTags.Data +namespace PlayerTags.Data; + +[JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] +public enum NameplateTitlePosition { - [JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] - public enum NameplateTitlePosition - { - Default, - AlwaysAboveName, - AlwaysBelowName - } + Default, + AlwaysAboveName, + AlwaysBelowName } diff --git a/PlayerTags/Data/NameplateTitleVisibility.cs b/PlayerTags/Data/NameplateTitleVisibility.cs index 3914b47..a6a20ba 100644 --- a/PlayerTags/Data/NameplateTitleVisibility.cs +++ b/PlayerTags/Data/NameplateTitleVisibility.cs @@ -1,13 +1,12 @@ using Newtonsoft.Json; -namespace PlayerTags.Data +namespace PlayerTags.Data; + +[JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] +public enum NameplateTitleVisibility { - [JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] - public enum NameplateTitleVisibility - { - Default, - Always, - Never, - WhenHasTags - } + Default, + Always, + Never, + WhenHasTags } diff --git a/PlayerTags/Data/PlayerContext.cs b/PlayerTags/Data/PlayerContext.cs index 4c848ea..ea8225b 100644 --- a/PlayerTags/Data/PlayerContext.cs +++ b/PlayerTags/Data/PlayerContext.cs @@ -1,12 +1,11 @@ -namespace PlayerTags.Data +namespace PlayerTags.Data; + +public enum PlayerContext { - public enum PlayerContext - { - None = 0x0, - Self = 0x1, - Party = 0x2, - Alliance = 0x4, - Enemy = 0x8, - Friend = 0x10 - } + None = 0x0, + Self = 0x1, + Party = 0x2, + Alliance = 0x4, + Enemy = 0x8, + Friend = 0x10 } diff --git a/PlayerTags/Data/PlayerContextHelper.cs b/PlayerTags/Data/PlayerContextHelper.cs index d27d715..c4cc2e0 100644 --- a/PlayerTags/Data/PlayerContextHelper.cs +++ b/PlayerTags/Data/PlayerContextHelper.cs @@ -1,81 +1,79 @@ using Dalamud.Game.ClientState.Objects.Enums; -using Dalamud.Game.ClientState.Objects.SubKinds; -namespace PlayerTags.Data +namespace PlayerTags.Data; + +public static class PlayerContextHelper { - public static class PlayerContextHelper + public static PlayerContext GetPlayerContext(PlayerCharacter playerCharacter) { - public static PlayerContext GetPlayerContext(PlayerCharacter playerCharacter) + PlayerContext playerContext = PlayerContext.None; + + if (PluginServices.ClientState.LocalPlayer == playerCharacter) { - PlayerContext playerContext = PlayerContext.None; - - if (PluginServices.ClientState.LocalPlayer == playerCharacter) - { - playerContext |= PlayerContext.Self; - } - - if (playerCharacter.StatusFlags.HasFlag(StatusFlags.Friend)) - { - playerContext |= PlayerContext.Friend; - } - - if (playerCharacter.StatusFlags.HasFlag(StatusFlags.PartyMember)) - { - playerContext |= PlayerContext.Party; - } - - if (playerCharacter.StatusFlags.HasFlag(StatusFlags.AllianceMember)) - { - playerContext |= PlayerContext.Alliance; - } - - if (playerCharacter.StatusFlags.HasFlag(StatusFlags.Hostile)) - { - playerContext |= PlayerContext.Enemy; - } - - return playerContext; + playerContext |= PlayerContext.Self; } - public static bool GetIsVisible(PlayerContext playerContext, bool desiredSelfVisibility, bool desiredFriendsVisibility, bool desiredPartyVisibility, bool desiredAllianceVisibility, bool desiredEnemiesVisibility, bool desiredOthersVisibility) + if (playerCharacter.StatusFlags.HasFlag(StatusFlags.Friend)) { - if (playerContext.HasFlag(PlayerContext.Self)) - { - return desiredSelfVisibility; - } - - bool isVisible = false; - if (playerContext.HasFlag(PlayerContext.Friend)) - { - isVisible |= desiredFriendsVisibility; - } - - if (playerContext.HasFlag(PlayerContext.Party)) - { - isVisible |= desiredPartyVisibility; - } - - if (!playerContext.HasFlag(PlayerContext.Party) && playerContext.HasFlag(PlayerContext.Alliance)) - { - isVisible |= desiredAllianceVisibility; - } - - if (playerContext.HasFlag(PlayerContext.Enemy)) - { - isVisible |= desiredEnemiesVisibility; - } - - if (playerContext == PlayerContext.None) - { - isVisible |= desiredOthersVisibility; - } - - return isVisible; + playerContext |= PlayerContext.Friend; } - public static bool GetIsVisible(PlayerCharacter playerCharacter, bool desiredSelfVisibility, bool desiredFriendsVisibility, bool desiredPartyVisibility, bool desiredAllianceVisibility, bool desiredEnemiesVisibility, bool desiredOthersVisibility) + if (playerCharacter.StatusFlags.HasFlag(StatusFlags.PartyMember)) { - return GetIsVisible(GetPlayerContext(playerCharacter), desiredSelfVisibility, desiredFriendsVisibility, desiredPartyVisibility, desiredAllianceVisibility, desiredEnemiesVisibility, desiredOthersVisibility); + playerContext |= PlayerContext.Party; } + + if (playerCharacter.StatusFlags.HasFlag(StatusFlags.AllianceMember)) + { + playerContext |= PlayerContext.Alliance; + } + + if (playerCharacter.StatusFlags.HasFlag(StatusFlags.Hostile)) + { + playerContext |= PlayerContext.Enemy; + } + + return playerContext; + } + + public static bool GetIsVisible(PlayerContext playerContext, bool desiredSelfVisibility, bool desiredFriendsVisibility, bool desiredPartyVisibility, bool desiredAllianceVisibility, bool desiredEnemiesVisibility, bool desiredOthersVisibility) + { + if (playerContext.HasFlag(PlayerContext.Self)) + { + return desiredSelfVisibility; + } + + bool isVisible = false; + if (playerContext.HasFlag(PlayerContext.Friend)) + { + isVisible |= desiredFriendsVisibility; + } + + if (playerContext.HasFlag(PlayerContext.Party)) + { + isVisible |= desiredPartyVisibility; + } + + if (!playerContext.HasFlag(PlayerContext.Party) && playerContext.HasFlag(PlayerContext.Alliance)) + { + isVisible |= desiredAllianceVisibility; + } + + if (playerContext.HasFlag(PlayerContext.Enemy)) + { + isVisible |= desiredEnemiesVisibility; + } + + if (playerContext == PlayerContext.None) + { + isVisible |= desiredOthersVisibility; + } + + return isVisible; + } + + public static bool GetIsVisible(PlayerCharacter playerCharacter, bool desiredSelfVisibility, bool desiredFriendsVisibility, bool desiredPartyVisibility, bool desiredAllianceVisibility, bool desiredEnemiesVisibility, bool desiredOthersVisibility) + { + return GetIsVisible(GetPlayerContext(playerCharacter), desiredSelfVisibility, desiredFriendsVisibility, desiredPartyVisibility, desiredAllianceVisibility, desiredEnemiesVisibility, desiredOthersVisibility); } } diff --git a/PlayerTags/Data/PluginData.cs b/PlayerTags/Data/PluginData.cs index cd9c2c8..c3a97dc 100644 --- a/PlayerTags/Data/PluginData.cs +++ b/PlayerTags/Data/PluginData.cs @@ -1,307 +1,302 @@ -using Dalamud.ContextMenu; -using Dalamud.Game.ClientState.Objects.SubKinds; -using Dalamud.Game.ClientState.Party; -using Dalamud.Game.Text.SeStringHandling.Payloads; -using Dalamud.Logging; +using Dalamud.Game.Text.SeStringHandling.Payloads; using PlayerTags.Configuration; using PlayerTags.PluginStrings; using System; using System.Collections.Generic; using System.Linq; -namespace PlayerTags.Data +namespace PlayerTags.Data; + +public class PluginData { - public class PluginData + public DefaultPluginData Default; + public Tag AllTags; + public Tag AllRoleTags; + public Dictionary RoleTags; + public Dictionary DpsRoleTags; + public Dictionary RangedDpsRoleTags; + public Dictionary LandHandRoleTags; + public Dictionary JobTags; + public Tag AllCustomTags; + public List CustomTags; + public List Identities; + + private PluginConfiguration pluginConfiguration; + + public PluginData(PluginConfiguration pluginConfiguration) { - public DefaultPluginData Default; - public Tag AllTags; - public Tag AllRoleTags; - public Dictionary RoleTags; - public Dictionary DpsRoleTags; - public Dictionary RangedDpsRoleTags; - public Dictionary LandHandRoleTags; - public Dictionary JobTags; - public Tag AllCustomTags; - public List CustomTags; - public List Identities; + this.pluginConfiguration = pluginConfiguration; + ReloadDefault(); + } - private PluginConfiguration pluginConfiguration; + public void ReloadDefault() + { + Default = new DefaultPluginData(pluginConfiguration.DefaultPluginDataTemplate); - public PluginData(PluginConfiguration pluginConfiguration) + // Set the default changes and saved changes + AllTags = new Tag(new LocalizedPluginString(nameof(AllTags)), Default.AllTags); + AllTags.SetChanges(pluginConfiguration.AllTagsChanges); + + AllRoleTags = new Tag(new LocalizedPluginString(nameof(AllRoleTags)), Default.AllRoleTags); + AllRoleTags.SetChanges(pluginConfiguration.AllRoleTagsChanges); + + RoleTags = []; + foreach (var role in Enum.GetValues()) { - this.pluginConfiguration = pluginConfiguration; - ReloadDefault(); + if (Default.RoleTags.TryGetValue(role, out var defaultTag)) + { + RoleTags[role] = new Tag(new LocalizedPluginString(Localizer.GetName(role)), defaultTag); + if (pluginConfiguration.RoleTagsChanges.TryGetValue(role, out var savedChanges)) + { + RoleTags[role].SetChanges(savedChanges); + } + } } - public void ReloadDefault() + DpsRoleTags = []; + foreach (var dpsRole in Enum.GetValues()) { - Default = new DefaultPluginData(pluginConfiguration.DefaultPluginDataTemplate); - - // Set the default changes and saved changes - AllTags = new Tag(new LocalizedPluginString(nameof(AllTags)), Default.AllTags); - AllTags.SetChanges(pluginConfiguration.AllTagsChanges); - - AllRoleTags = new Tag(new LocalizedPluginString(nameof(AllRoleTags)), Default.AllRoleTags); - AllRoleTags.SetChanges(pluginConfiguration.AllRoleTagsChanges); - - RoleTags = new Dictionary(); - foreach (var role in Enum.GetValues()) + if (Default.DpsRoleTags.TryGetValue(dpsRole, out var defaultTag)) { - if (Default.RoleTags.TryGetValue(role, out var defaultTag)) + DpsRoleTags[dpsRole] = new Tag(new LocalizedPluginString(Localizer.GetName(dpsRole)), defaultTag); + if (pluginConfiguration.DpsRoleTagsChanges.TryGetValue(dpsRole, out var savedChanges)) { - RoleTags[role] = new Tag(new LocalizedPluginString(Localizer.GetName(role)), defaultTag); - if (pluginConfiguration.RoleTagsChanges.TryGetValue(role, out var savedChanges)) - { - RoleTags[role].SetChanges(savedChanges); - } + DpsRoleTags[dpsRole].SetChanges(savedChanges); } } + } - DpsRoleTags = new Dictionary(); - foreach (var dpsRole in Enum.GetValues()) + RangedDpsRoleTags = []; + foreach (var rangedDpsRole in Enum.GetValues()) + { + if (Default.RangedDpsRoleTags.TryGetValue(rangedDpsRole, out var defaultTag)) { - if (Default.DpsRoleTags.TryGetValue(dpsRole, out var defaultTag)) + RangedDpsRoleTags[rangedDpsRole] = new Tag(new LocalizedPluginString(Localizer.GetName(rangedDpsRole)), defaultTag); + if (pluginConfiguration.RangedDpsRoleTagsChanges.TryGetValue(rangedDpsRole, out var savedChanges)) { - DpsRoleTags[dpsRole] = new Tag(new LocalizedPluginString(Localizer.GetName(dpsRole)), defaultTag); - if (pluginConfiguration.DpsRoleTagsChanges.TryGetValue(dpsRole, out var savedChanges)) - { - DpsRoleTags[dpsRole].SetChanges(savedChanges); - } + RangedDpsRoleTags[rangedDpsRole].SetChanges(savedChanges); } } + } - RangedDpsRoleTags = new Dictionary(); - foreach (var rangedDpsRole in Enum.GetValues()) + LandHandRoleTags = []; + foreach (var landHandRole in Enum.GetValues()) + { + if (Default.LandHandRoleTags.TryGetValue(landHandRole, out var defaultChanges)) { - if (Default.RangedDpsRoleTags.TryGetValue(rangedDpsRole, out var defaultTag)) + LandHandRoleTags[landHandRole] = new Tag(new LocalizedPluginString(Localizer.GetName(landHandRole)), defaultChanges); + if (pluginConfiguration.LandHandRoleTagsChanges.TryGetValue(landHandRole, out var savedChanges)) { - RangedDpsRoleTags[rangedDpsRole] = new Tag(new LocalizedPluginString(Localizer.GetName(rangedDpsRole)), defaultTag); - if (pluginConfiguration.RangedDpsRoleTagsChanges.TryGetValue(rangedDpsRole, out var savedChanges)) - { - RangedDpsRoleTags[rangedDpsRole].SetChanges(savedChanges); - } + LandHandRoleTags[landHandRole].SetChanges(savedChanges); } } + } - LandHandRoleTags = new Dictionary(); - foreach (var landHandRole in Enum.GetValues()) + JobTags = []; + foreach ((var jobAbbreviation, var role) in RoleHelper.RolesByJobAbbreviation) + { + if (Default.JobTags.TryGetValue(jobAbbreviation, out var defaultChanges)) { - if (Default.LandHandRoleTags.TryGetValue(landHandRole, out var defaultChanges)) + JobTags[jobAbbreviation] = new Tag(new LiteralPluginString(jobAbbreviation), defaultChanges); + if (pluginConfiguration.JobTagsChanges.TryGetValue(jobAbbreviation, out var savedChanges)) { - LandHandRoleTags[landHandRole] = new Tag(new LocalizedPluginString(Localizer.GetName(landHandRole)), defaultChanges); - if (pluginConfiguration.LandHandRoleTagsChanges.TryGetValue(landHandRole, out var savedChanges)) - { - LandHandRoleTags[landHandRole].SetChanges(savedChanges); - } + JobTags[jobAbbreviation].SetChanges(savedChanges); } } + } - JobTags = new Dictionary(); - foreach ((var jobAbbreviation, var role) in RoleHelper.RolesByJobAbbreviation) + AllCustomTags = new Tag(new LocalizedPluginString(nameof(AllCustomTags)), Default.AllCustomTags); + AllCustomTags.SetChanges(pluginConfiguration.AllCustomTagsChanges); + + CustomTags = []; + foreach (var savedChanges in pluginConfiguration.CustomTagsChanges) + { + var tag = new Tag(new LocalizedPluginString(nameof(CustomTags))); + tag.SetChanges(savedChanges); + CustomTags.Add(tag); + } + + // Set up the inheritance heirarchy + AllRoleTags.Parent = AllTags; + foreach ((var role, var roleTag) in RoleTags) + { + roleTag.Parent = AllRoleTags; + + if (role == Role.Dps) { - if (Default.JobTags.TryGetValue(jobAbbreviation, out var defaultChanges)) + foreach ((var dpsRole, var dpsRoleTag) in DpsRoleTags) { - JobTags[jobAbbreviation] = new Tag(new LiteralPluginString(jobAbbreviation), defaultChanges); - if (pluginConfiguration.JobTagsChanges.TryGetValue(jobAbbreviation, out var savedChanges)) + dpsRoleTag.Parent = roleTag; + + if (dpsRole == DpsRole.Ranged) { - JobTags[jobAbbreviation].SetChanges(savedChanges); - } - } - } - - AllCustomTags = new Tag(new LocalizedPluginString(nameof(AllCustomTags)), Default.AllCustomTags); - AllCustomTags.SetChanges(pluginConfiguration.AllCustomTagsChanges); - - CustomTags = new List(); - foreach (var savedChanges in pluginConfiguration.CustomTagsChanges) - { - var tag = new Tag(new LocalizedPluginString(nameof(CustomTags))); - tag.SetChanges(savedChanges); - CustomTags.Add(tag); - } - - // Set up the inheritance heirarchy - AllRoleTags.Parent = AllTags; - foreach ((var role, var roleTag) in RoleTags) - { - roleTag.Parent = AllRoleTags; - - if (role == Role.Dps) - { - foreach ((var dpsRole, var dpsRoleTag) in DpsRoleTags) - { - dpsRoleTag.Parent = roleTag; - - if (dpsRole == DpsRole.Ranged) + foreach ((var rangedDpsRole, var rangedDpsRoleTag) in RangedDpsRoleTags) { - foreach ((var rangedDpsRole, var rangedDpsRoleTag) in RangedDpsRoleTags) - { - rangedDpsRoleTag.Parent = dpsRoleTag; - } + rangedDpsRoleTag.Parent = dpsRoleTag; } } } - else if (role == Role.LandHand) + } + else if (role == Role.LandHand) + { + foreach ((var landHandRole, var landHandRoleTag) in LandHandRoleTags) { - foreach ((var landHandRole, var landHandRoleTag) in LandHandRoleTags) - { - landHandRoleTag.Parent = roleTag; - } + landHandRoleTag.Parent = roleTag; } } + } - foreach ((var jobAbbreviation, var jobTag) in JobTags) + foreach ((var jobAbbreviation, var jobTag) in JobTags) + { + if (RoleHelper.RolesByJobAbbreviation.TryGetValue(jobAbbreviation, out var role)) { - if (RoleHelper.RolesByJobAbbreviation.TryGetValue(jobAbbreviation, out var role)) + if (RoleHelper.DpsRolesByJobAbbreviation.TryGetValue(jobAbbreviation, out var dpsRole)) { - if (RoleHelper.DpsRolesByJobAbbreviation.TryGetValue(jobAbbreviation, out var dpsRole)) + if (RoleHelper.RangedDpsRolesByJobAbbreviation.TryGetValue(jobAbbreviation, out var rangedDpsRole)) { - if (RoleHelper.RangedDpsRolesByJobAbbreviation.TryGetValue(jobAbbreviation, out var rangedDpsRole)) - { - jobTag.Parent = RangedDpsRoleTags[rangedDpsRole]; - } - else - { - jobTag.Parent = DpsRoleTags[dpsRole]; - } - } - else if (RoleHelper.LandHandRolesByJobAbbreviation.TryGetValue(jobAbbreviation, out var landHandRole)) - { - jobTag.Parent = LandHandRoleTags[landHandRole]; + jobTag.Parent = RangedDpsRoleTags[rangedDpsRole]; } else { - jobTag.Parent = RoleTags[RoleHelper.RolesByJobAbbreviation[jobAbbreviation]]; + jobTag.Parent = DpsRoleTags[dpsRole]; } } + else if (RoleHelper.LandHandRolesByJobAbbreviation.TryGetValue(jobAbbreviation, out var landHandRole)) + { + jobTag.Parent = LandHandRoleTags[landHandRole]; + } + else + { + jobTag.Parent = RoleTags[RoleHelper.RolesByJobAbbreviation[jobAbbreviation]]; + } } + } - AllCustomTags.Parent = AllTags; - foreach (var tag in CustomTags) + AllCustomTags.Parent = AllTags; + foreach (var tag in CustomTags) + { + tag.Parent = AllCustomTags; + } + + Identities = pluginConfiguration.Identities; + + // Migrate old custom tag identity assignments + bool customTagsMigrated = false; + foreach (var customTag in CustomTags) + { + if (customTag.CustomId.Value == Guid.Empty) { - tag.Parent = AllCustomTags; + customTag.CustomId.Behavior = Inheritables.InheritableBehavior.Enabled; + customTag.CustomId.Value = Guid.NewGuid(); + customTagsMigrated = true; } - Identities = pluginConfiguration.Identities; - - // Migrate old custom tag identity assignments - bool customTagsMigrated = false; - foreach (var customTag in CustomTags) + foreach (string identityToAddTo in customTag.IdentitiesToAddTo) { - if (customTag.CustomId.Value == Guid.Empty) + Identity? identity = Identities.FirstOrDefault(identity => identity.Name.ToLower() == identityToAddTo.ToLower()); + if (identity == null) { - customTag.CustomId.Behavior = Inheritables.InheritableBehavior.Enabled; - customTag.CustomId.Value = Guid.NewGuid(); - customTagsMigrated = true; + identity = new Identity(identityToAddTo); + Identities.Add(identity); } - foreach (string identityToAddTo in customTag.IdentitiesToAddTo) + if (identity != null) { - Identity? identity = Identities.FirstOrDefault(identity => identity.Name.ToLower() == identityToAddTo.ToLower()); - if (identity == null) - { - identity = new Identity(identityToAddTo); - Identities.Add(identity); - } - - if (identity != null) - { - identity.CustomTagIds.Add(customTag.CustomId.Value); - customTagsMigrated = true; - } - } - - if (customTag.GameObjectNamesToApplyTo.Behavior != Inheritables.InheritableBehavior.Inherit) - { - customTag.GameObjectNamesToApplyTo.Behavior = Inheritables.InheritableBehavior.Inherit; - customTag.GameObjectNamesToApplyTo.Value = ""; + identity.CustomTagIds.Add(customTag.CustomId.Value); customTagsMigrated = true; } } - if (customTagsMigrated) + if (customTag.GameObjectNamesToApplyTo.Behavior != Inheritables.InheritableBehavior.Inherit) { - pluginConfiguration.Save(this); + customTag.GameObjectNamesToApplyTo.Behavior = Inheritables.InheritableBehavior.Inherit; + customTag.GameObjectNamesToApplyTo.Value = ""; + customTagsMigrated = true; } } - public void AddCustomTagToIdentity(Tag customTag, Identity identity) + if (customTagsMigrated) { - if (!identity.CustomTagIds.Contains(customTag.CustomId.Value)) - { - identity.CustomTagIds.Add(customTag.CustomId.Value); - } - - if (!Identities.Contains(identity)) - { - Identities.Add(identity); - } - } - - public void RemoveCustomTagFromIdentity(Tag customTag, Identity identity) - { - identity.CustomTagIds.Remove(customTag.CustomId.Value); - - if (!identity.CustomTagIds.Any()) - { - Identities.Remove(identity); - } - } - - public void RemoveCustomTagFromIdentities(Tag customTag) - { - foreach (var identity in Identities.ToArray()) - { - RemoveCustomTagFromIdentity(customTag, identity); - } - } - - public Identity GetIdentity(string name, uint? worldId) - { - foreach (var identity in Identities) - { - if (identity.Name.ToLower().Trim() == name.ToLower().Trim()) - { - if (identity.WorldId == null && worldId != null) - { - identity.WorldId = worldId; - pluginConfiguration.Save(this); - } - - return identity; - } - } - - return new Identity(name) - { - WorldId = worldId - }; - } - - public Identity? GetIdentity(GameObjectContextMenuOpenArgs contextMenuOpenedArgs) - { - if (string.IsNullOrEmpty(contextMenuOpenedArgs.Text?.TextValue) - || contextMenuOpenedArgs.ObjectWorld == 0 - || contextMenuOpenedArgs.ObjectWorld == 65535) - { - return null; - } - return GetIdentity(contextMenuOpenedArgs.Text?.TextValue ?? string.Empty, contextMenuOpenedArgs.ObjectWorld); - } - - public Identity GetIdentity(PlayerCharacter playerCharacter) - { - return GetIdentity(playerCharacter.Name.TextValue, playerCharacter.HomeWorld.Id); - } - - public Identity GetIdentity(PartyMember partyMember) - { - return GetIdentity(partyMember.Name.TextValue, partyMember.World.Id); - } - - public Identity GetIdentity(PlayerPayload playerPayload) - { - return GetIdentity(playerPayload.PlayerName, playerPayload.World.RowId); + pluginConfiguration.Save(this); } } + + public void AddCustomTagToIdentity(Tag customTag, Identity identity) + { + if (!identity.CustomTagIds.Contains(customTag.CustomId.Value)) + { + identity.CustomTagIds.Add(customTag.CustomId.Value); + } + + if (!Identities.Contains(identity)) + { + Identities.Add(identity); + } + } + + public void RemoveCustomTagFromIdentity(Tag customTag, Identity identity) + { + identity.CustomTagIds.Remove(customTag.CustomId.Value); + + if (!identity.CustomTagIds.Any()) + { + Identities.Remove(identity); + } + } + + public void RemoveCustomTagFromIdentities(Tag customTag) + { + foreach (var identity in Identities.ToArray()) + { + RemoveCustomTagFromIdentity(customTag, identity); + } + } + + public Identity GetIdentity(string name, uint? worldId) + { + foreach (var identity in Identities) + { + if (identity.Name.ToLower().Trim() == name.ToLower().Trim()) + { + if (identity.WorldId == null && worldId != null) + { + identity.WorldId = worldId; + pluginConfiguration.Save(this); + } + + return identity; + } + } + + return new Identity(name) + { + WorldId = worldId + }; + } + + public Identity? GetIdentity(GameObjectContextMenuOpenArgs contextMenuOpenedArgs) + { + if (string.IsNullOrEmpty(contextMenuOpenedArgs.Text?.TextValue) + || contextMenuOpenedArgs.ObjectWorld == 0 + || contextMenuOpenedArgs.ObjectWorld == 65535) + { + return null; + } + return GetIdentity(contextMenuOpenedArgs.Text?.TextValue ?? string.Empty, contextMenuOpenedArgs.ObjectWorld); + } + + public Identity GetIdentity(PlayerCharacter playerCharacter) + { + return GetIdentity(playerCharacter.Name.TextValue, playerCharacter.HomeWorld.Id); + } + + public Identity GetIdentity(PartyMember partyMember) + { + return GetIdentity(partyMember.Name.TextValue, partyMember.World.Id); + } + + public Identity GetIdentity(PlayerPayload playerPayload) + { + return GetIdentity(playerPayload.PlayerName, playerPayload.World.RowId); + } } diff --git a/PlayerTags/Data/RangedDpsRole.cs b/PlayerTags/Data/RangedDpsRole.cs index eb47702..50a31a7 100644 --- a/PlayerTags/Data/RangedDpsRole.cs +++ b/PlayerTags/Data/RangedDpsRole.cs @@ -1,8 +1,7 @@ -namespace PlayerTags.Data +namespace PlayerTags.Data; + +public enum RangedDpsRole { - public enum RangedDpsRole - { - Magical, - Physical, - } + Magical, + Physical, } diff --git a/PlayerTags/Data/Role.cs b/PlayerTags/Data/Role.cs index a9ac88e..1cee8a1 100644 --- a/PlayerTags/Data/Role.cs +++ b/PlayerTags/Data/Role.cs @@ -1,10 +1,9 @@ -namespace PlayerTags.Data +namespace PlayerTags.Data; + +public enum Role { - public enum Role - { - LandHand, - Tank, - Healer, - Dps, - } + LandHand, + Tank, + Healer, + Dps, } diff --git a/PlayerTags/Data/RoleHelper.cs b/PlayerTags/Data/RoleHelper.cs index c34d714..7b0ce7e 100644 --- a/PlayerTags/Data/RoleHelper.cs +++ b/PlayerTags/Data/RoleHelper.cs @@ -2,152 +2,151 @@ using System.Collections.Generic; using System.Linq; -namespace PlayerTags.Data +namespace PlayerTags.Data; + +public static class RoleHelper { - public static class RoleHelper + public static Dictionary RolesByRoleId { get; } = new Dictionary() { - public static Dictionary RolesByRoleId { get; } = new Dictionary() - { - { 0, Role.LandHand }, - { 1, Role.Tank }, - { 2, Role.Dps }, - { 3, Role.Dps }, - { 4, Role.Healer }, - }; + { 0, Role.LandHand }, + { 1, Role.Tank }, + { 2, Role.Dps }, + { 3, Role.Dps }, + { 4, Role.Healer }, + }; - public static Dictionary DpsRolesByRoleId { get; } = new Dictionary() - { - { 2, DpsRole.Melee }, - { 3, DpsRole.Ranged }, - }; + public static Dictionary DpsRolesByRoleId { get; } = new Dictionary() + { + { 2, DpsRole.Melee }, + { 3, DpsRole.Ranged }, + }; - public static Dictionary RangedDpsRolesByPrimaryStat { get; } = new Dictionary() - { - { 4, RangedDpsRole.Magical }, - { 2, RangedDpsRole.Physical }, - }; + public static Dictionary RangedDpsRolesByPrimaryStat { get; } = new Dictionary() + { + { 4, RangedDpsRole.Magical }, + { 2, RangedDpsRole.Physical }, + }; - private static Dictionary? s_RolesByJobAbbreviation = null; - public static Dictionary RolesByJobAbbreviation + private static Dictionary? s_RolesByJobAbbreviation = null; + public static Dictionary RolesByJobAbbreviation + { + get { - get + if (s_RolesByJobAbbreviation == null) { - if (s_RolesByJobAbbreviation == null) - { - s_RolesByJobAbbreviation = new Dictionary(); + s_RolesByJobAbbreviation = []; - var classJobs = PluginServices.DataManager.GetExcelSheet(); - if (classJobs != null) + var classJobs = PluginServices.DataManager.GetExcelSheet(); + if (classJobs != null) + { + foreach (var classJob in classJobs.Where(classJob => !string.IsNullOrEmpty(classJob.Abbreviation.RawString))) { - foreach (var classJob in classJobs.Where(classJob => !string.IsNullOrEmpty(classJob.Abbreviation.RawString))) + if (RolesByRoleId.TryGetValue(classJob.Role, out var role)) { - if (RolesByRoleId.TryGetValue(classJob.Role, out var role)) + s_RolesByJobAbbreviation[classJob.Abbreviation] = role; + } + } + } + } + + return s_RolesByJobAbbreviation; + } + } + + private static Dictionary? s_DpsRolesByJobAbbreviation = null; + public static Dictionary DpsRolesByJobAbbreviation + { + get + { + if (s_DpsRolesByJobAbbreviation == null) + { + s_DpsRolesByJobAbbreviation = []; + + var classJobs = PluginServices.DataManager.GetExcelSheet(); + if (classJobs != null) + { + foreach (var classJob in classJobs.Where(classJob => !string.IsNullOrEmpty(classJob.Abbreviation.RawString))) + { + if (DpsRolesByRoleId.TryGetValue(classJob.Role, out var dpsRole)) + { + s_DpsRolesByJobAbbreviation[classJob.Abbreviation] = dpsRole; + } + } + } + } + + return s_DpsRolesByJobAbbreviation; + } + } + + private static Dictionary? s_RangedDpsRolesByJobAbbreviation = null; + public static Dictionary RangedDpsRolesByJobAbbreviation + { + get + { + if (s_RangedDpsRolesByJobAbbreviation == null) + { + s_RangedDpsRolesByJobAbbreviation = []; + + var classJobs = PluginServices.DataManager.GetExcelSheet(); + if (classJobs != null) + { + foreach (var classJob in classJobs.Where(classJob => !string.IsNullOrEmpty(classJob.Abbreviation.RawString))) + { + if (DpsRolesByJobAbbreviation.TryGetValue(classJob.Abbreviation, out var dpsRole) && dpsRole == DpsRole.Ranged) + { + if (RangedDpsRolesByPrimaryStat.TryGetValue(classJob.PrimaryStat, out var rangedDPSRole)) { - s_RolesByJobAbbreviation[classJob.Abbreviation] = role; + s_RangedDpsRolesByJobAbbreviation[classJob.Abbreviation] = rangedDPSRole; } } } } - - return s_RolesByJobAbbreviation; } + + return s_RangedDpsRolesByJobAbbreviation; } + } - private static Dictionary? s_DpsRolesByJobAbbreviation = null; - public static Dictionary DpsRolesByJobAbbreviation + private static Dictionary? s_LandHandRolesByJobAbbreviation = null; + public static Dictionary LandHandRolesByJobAbbreviation + { + get { - get + if (s_LandHandRolesByJobAbbreviation == null) { - if (s_DpsRolesByJobAbbreviation == null) + s_LandHandRolesByJobAbbreviation = []; + + var classJobs = PluginServices.DataManager.GetExcelSheet(); + var gatheringSubCategories = PluginServices.DataManager.GetExcelSheet(); + if (classJobs != null && gatheringSubCategories != null) { - s_DpsRolesByJobAbbreviation = new Dictionary(); + var gatheringJobAbbreviations = gatheringSubCategories + .Select(gatheringSubCategory => gatheringSubCategory.ClassJob.Value) + .Where(classJob => classJob != null) + .Select(classJob => classJob!.Abbreviation).Distinct(); - var classJobs = PluginServices.DataManager.GetExcelSheet(); - if (classJobs != null) + foreach (var classJob in classJobs.Where(classJob => !string.IsNullOrEmpty(classJob.Abbreviation.RawString))) { - foreach (var classJob in classJobs.Where(classJob => !string.IsNullOrEmpty(classJob.Abbreviation.RawString))) + if (RolesByRoleId.TryGetValue(classJob.Role, out var role)) { - if (DpsRolesByRoleId.TryGetValue(classJob.Role, out var dpsRole)) + if (role == Role.LandHand) { - s_DpsRolesByJobAbbreviation[classJob.Abbreviation] = dpsRole; - } - } - } - } - - return s_DpsRolesByJobAbbreviation; - } - } - - private static Dictionary? s_RangedDpsRolesByJobAbbreviation = null; - public static Dictionary RangedDpsRolesByJobAbbreviation - { - get - { - if (s_RangedDpsRolesByJobAbbreviation == null) - { - s_RangedDpsRolesByJobAbbreviation = new Dictionary(); - - var classJobs = PluginServices.DataManager.GetExcelSheet(); - if (classJobs != null) - { - foreach (var classJob in classJobs.Where(classJob => !string.IsNullOrEmpty(classJob.Abbreviation.RawString))) - { - if (DpsRolesByJobAbbreviation.TryGetValue(classJob.Abbreviation, out var dpsRole) && dpsRole == DpsRole.Ranged) - { - if (RangedDpsRolesByPrimaryStat.TryGetValue(classJob.PrimaryStat, out var rangedDPSRole)) + if (gatheringJobAbbreviations.Contains(classJob.Abbreviation)) { - s_RangedDpsRolesByJobAbbreviation[classJob.Abbreviation] = rangedDPSRole; + s_LandHandRolesByJobAbbreviation[classJob.Abbreviation] = LandHandRole.Land; + } + else + { + s_LandHandRolesByJobAbbreviation[classJob.Abbreviation] = LandHandRole.Hand; } } } } } - - return s_RangedDpsRolesByJobAbbreviation; } - } - private static Dictionary? s_LandHandRolesByJobAbbreviation = null; - public static Dictionary LandHandRolesByJobAbbreviation - { - get - { - if (s_LandHandRolesByJobAbbreviation == null) - { - s_LandHandRolesByJobAbbreviation = new Dictionary(); - - var classJobs = PluginServices.DataManager.GetExcelSheet(); - var gatheringSubCategories = PluginServices.DataManager.GetExcelSheet(); - if (classJobs != null && gatheringSubCategories != null) - { - var gatheringJobAbbreviations = gatheringSubCategories - .Select(gatheringSubCategory => gatheringSubCategory.ClassJob.Value) - .Where(classJob => classJob != null) - .Select(classJob => classJob!.Abbreviation).Distinct(); - - foreach (var classJob in classJobs.Where(classJob => !string.IsNullOrEmpty(classJob.Abbreviation.RawString))) - { - if (RolesByRoleId.TryGetValue(classJob.Role, out var role)) - { - if (role == Role.LandHand) - { - if (gatheringJobAbbreviations.Contains(classJob.Abbreviation)) - { - s_LandHandRolesByJobAbbreviation[classJob.Abbreviation] = LandHandRole.Land; - } - else - { - s_LandHandRolesByJobAbbreviation[classJob.Abbreviation] = LandHandRole.Hand; - } - } - } - } - } - } - - return s_LandHandRolesByJobAbbreviation; - } + return s_LandHandRolesByJobAbbreviation; } } } diff --git a/PlayerTags/Data/Tag.cs b/PlayerTags/Data/Tag.cs index 1244c1c..4e76888 100644 --- a/PlayerTags/Data/Tag.cs +++ b/PlayerTags/Data/Tag.cs @@ -1,330 +1,327 @@ 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; using System; using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; -namespace PlayerTags.Data +namespace PlayerTags.Data; + +public class Tag { - public class Tag + public IPluginString Name { get; init; } + + [JsonProperty("Parent")] + private Tag? m_Parent = null; + + [JsonIgnore] + public Tag? Parent { - public IPluginString Name { get; init; } - - [JsonProperty("Parent")] - private Tag? m_Parent = null; - - [JsonIgnore] - public Tag? Parent + get => m_Parent; + set { - get => m_Parent; - set + if (m_Parent != value) { - if (m_Parent != value) + if (m_Parent != null) { - if (m_Parent != null) + if (m_Parent.Children.Contains(this)) { - if (m_Parent.Children.Contains(this)) - { - m_Parent.Children.Remove(this); - } + m_Parent.Children.Remove(this); } + } - m_Parent = value; - if (m_Parent != null) + m_Parent = value; + if (m_Parent != null) + { + m_Parent.Children.Add(this); + foreach ((var name, IInheritable inheritable) in Inheritables) { - m_Parent.Children.Add(this); - foreach ((var name, IInheritable inheritable) in Inheritables) - { - inheritable.Parent = m_Parent.Inheritables[name]; - } + inheritable.Parent = m_Parent.Inheritables[name]; } } } } + } - public List Children { get; } = new List(); + public List Children { get; } = []; - [JsonIgnore] - public IEnumerable Descendents + [JsonIgnore] + public IEnumerable Descendents + { + get { - get + IEnumerable descendents = Children.Prepend(this); + + foreach (var child in Children) { - IEnumerable descendents = Children.Prepend(this); - - foreach (var child in Children) - { - descendents = descendents.Union(child.Descendents); - } - - return descendents.Distinct(); + descendents = descendents.Union(child.Descendents); } + + return descendents.Distinct(); } + } - [JsonIgnore] - private Dictionary? m_Inheritables = null; - [JsonIgnore] - public Dictionary Inheritables + [JsonIgnore] + private Dictionary? m_Inheritables = null; + [JsonIgnore] + public Dictionary Inheritables + { + get { - get + if (m_Inheritables == null) { - if (m_Inheritables == null) - { - m_Inheritables = new Dictionary(); + m_Inheritables = []; - var inheritableFields = GetType().GetFields().Where(field => typeof(IInheritable).IsAssignableFrom(field.FieldType)); - foreach (var inheritableField in inheritableFields) + var inheritableFields = GetType().GetFields().Where(field => typeof(IInheritable).IsAssignableFrom(field.FieldType)); + foreach (var inheritableField in inheritableFields) + { + IInheritable? inheritable = inheritableField.GetValue(this) as IInheritable; + if (inheritable != null) { - IInheritable? inheritable = inheritableField.GetValue(this) as IInheritable; - if (inheritable != null) - { - Inheritables[inheritableField.Name] = inheritable; - } + Inheritables[inheritableField.Name] = inheritable; } } - - return m_Inheritables!; } + + return m_Inheritables!; } + } - public InheritableValue IsSelected = new InheritableValue(false) + public InheritableValue IsSelected = new(false) + { + Behavior = InheritableBehavior.Enabled + }; + + public InheritableValue IsExpanded = new(false) + { + Behavior = InheritableBehavior.Enabled + }; + + // Deprecated + public InheritableReference GameObjectNamesToApplyTo = new(""); + + public InheritableValue CustomId = new(Guid.Empty); + + [JsonProperty, Obsolete] + private InheritableValue IsIconVisibleInChat + { + set => IsRoleIconVisibleInChat = value; + } + + [JsonProperty, Obsolete] + private InheritableValue IsIconVisibleInNameplate + { + set => IsRoleIconVisibleInNameplates = value; + } + + [InheritableCategory("IconCategory")] + public InheritableValue Icon = new(BitmapFontIcon.Aethernet); + [InheritableCategory("IconCategory")] + public InheritableValue IsRoleIconVisibleInChat = new(false); + [InheritableCategory("IconCategory")] + public InheritableValue IsRoleIconVisibleInNameplates = new(false); + [InheritableCategory("IconCategory")] + public InheritableValue IsJobIconVisibleInNameplates = new(false); + [InheritableCategory("IconCategory")] + public InheritableValue JobIconSet = new(JobIconSetName.Framed); + + [InheritableCategory("TextCategory")] + public InheritableReference Text = new(""); + [InheritableCategory("TextCategory")] + public InheritableValue TextColor = new(6); + [InheritableCategory("TextCategory")] + public InheritableValue TextGlowColor = new(6); + [InheritableCategory("TextCategory")] + public InheritableValue IsTextItalic = new(false); + [InheritableCategory("TextCategory")] + public InheritableValue IsTextVisibleInChat = new(false); + [InheritableCategory("TextCategory")] + public InheritableValue IsTextVisibleInNameplates = new(false); + [InheritableCategory("TextCategory")] + public InheritableValue IsTextColorAppliedToChatName = new(false); + [InheritableCategory("TextCategory")] + public InheritableValue IsTextColorAppliedToNameplateName = new(false); + [InheritableCategory("TextCategory")] + public InheritableValue IsTextColorAppliedToNameplateTitle = new(false); + [InheritableCategory("TextCategory")] + public InheritableValue IsTextColorAppliedToNameplateFreeCompany = new(false); + + //[InheritableCategory("NameplateCategory")] + //public InheritableValue NameplateFreeCompanyVisibility = new InheritableValue(Data.NameplateFreeCompanyVisibility.Default); + //[InheritableCategory("NameplateCategory")] + //public InheritableValue NameplateTitleVisibility = new InheritableValue(Data.NameplateTitleVisibility.Default); + //[InheritableCategory("NameplateCategory")] + //public InheritableValue NameplateTitlePosition = new InheritableValue(Data.NameplateTitlePosition.Default); + + [InheritableCategory("PositionCategory")] + public InheritableValue TagPositionInChat = new(TagPosition.Before); + [InheritableCategory("PositionCategory")] + public InheritableValue InsertBehindNumberPrefixInChat = new(true); + [InheritableCategory("PositionCategory")] + public InheritableValue TagPositionInNameplates = new(TagPosition.Before); + [InheritableCategory("PositionCategory")] + public InheritableValue TagTargetInNameplates = new(NameplateElement.Name); + + [InheritableCategory("ActivityCategory")] + public InheritableValue IsVisibleInPveDuties = new(false); + [InheritableCategory("ActivityCategory")] + public InheritableValue IsVisibleInPvpDuties = new(false); + [InheritableCategory("ActivityCategory")] + public InheritableValue IsVisibleInOverworld = new(false); + + [InheritableCategory("PlayerCategory")] + public InheritableValue IsVisibleForSelf = new(false); + [InheritableCategory("PlayerCategory")] + public InheritableValue IsVisibleForFriendPlayers = new(false); + [InheritableCategory("PlayerCategory")] + public InheritableValue IsVisibleForPartyPlayers = new(false); + [InheritableCategory("PlayerCategory")] + public InheritableValue IsVisibleForAlliancePlayers = new(false); + [InheritableCategory("PlayerCategory")] + public InheritableValue IsVisibleForEnemyPlayers = new(false); + [InheritableCategory("PlayerCategory")] + public InheritableValue IsVisibleForOtherPlayers = new(false); + + [InheritableCategory("ChatFeatureCategory")] + public InheritableReference> TargetChatTypes = new(new List(Enum.GetValues())); + [InheritableCategory("ChatFeatureCategory")] + public InheritableValue TargetChatTypesIncludeUndefined = new(true); + + [JsonIgnore] + public string[] IdentitiesToAddTo + { + get { - Behavior = InheritableBehavior.Enabled - }; - - public InheritableValue IsExpanded = new InheritableValue(false) - { - Behavior = InheritableBehavior.Enabled - }; - - // Deprecated - public InheritableReference GameObjectNamesToApplyTo = new InheritableReference(""); - - 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")] - public InheritableValue IsRoleIconVisibleInChat = new InheritableValue(false); - [InheritableCategory("IconCategory")] - 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(""); - [InheritableCategory("TextCategory")] - public InheritableValue TextColor = new InheritableValue(6); - [InheritableCategory("TextCategory")] - public InheritableValue TextGlowColor = new InheritableValue(6); - [InheritableCategory("TextCategory")] - public InheritableValue IsTextItalic = new InheritableValue(false); - [InheritableCategory("TextCategory")] - public InheritableValue IsTextVisibleInChat = new InheritableValue(false); - [InheritableCategory("TextCategory")] - public InheritableValue IsTextVisibleInNameplates = new InheritableValue(false); - [InheritableCategory("TextCategory")] - public InheritableValue IsTextColorAppliedToChatName = new InheritableValue(false); - [InheritableCategory("TextCategory")] - public InheritableValue IsTextColorAppliedToNameplateName = new InheritableValue(false); - [InheritableCategory("TextCategory")] - public InheritableValue IsTextColorAppliedToNameplateTitle = new InheritableValue(false); - [InheritableCategory("TextCategory")] - public InheritableValue IsTextColorAppliedToNameplateFreeCompany = new InheritableValue(false); - - //[InheritableCategory("NameplateCategory")] - //public InheritableValue NameplateFreeCompanyVisibility = new InheritableValue(Data.NameplateFreeCompanyVisibility.Default); - //[InheritableCategory("NameplateCategory")] - //public InheritableValue NameplateTitleVisibility = new InheritableValue(Data.NameplateTitleVisibility.Default); - //[InheritableCategory("NameplateCategory")] - //public InheritableValue NameplateTitlePosition = new InheritableValue(Data.NameplateTitlePosition.Default); - - [InheritableCategory("PositionCategory")] - public InheritableValue TagPositionInChat = new InheritableValue(TagPosition.Before); - [InheritableCategory("PositionCategory")] - public InheritableValue InsertBehindNumberPrefixInChat = new InheritableValue(true); - [InheritableCategory("PositionCategory")] - public InheritableValue TagPositionInNameplates = new InheritableValue(TagPosition.Before); - [InheritableCategory("PositionCategory")] - public InheritableValue TagTargetInNameplates = new InheritableValue(NameplateElement.Name); - - [InheritableCategory("ActivityCategory")] - public InheritableValue IsVisibleInPveDuties = new InheritableValue(false); - [InheritableCategory("ActivityCategory")] - public InheritableValue IsVisibleInPvpDuties = new InheritableValue(false); - [InheritableCategory("ActivityCategory")] - public InheritableValue IsVisibleInOverworld = new InheritableValue(false); - - [InheritableCategory("PlayerCategory")] - public InheritableValue IsVisibleForSelf = new InheritableValue(false); - [InheritableCategory("PlayerCategory")] - public InheritableValue IsVisibleForFriendPlayers = new InheritableValue(false); - [InheritableCategory("PlayerCategory")] - public InheritableValue IsVisibleForPartyPlayers = new InheritableValue(false); - [InheritableCategory("PlayerCategory")] - public InheritableValue IsVisibleForAlliancePlayers = new InheritableValue(false); - [InheritableCategory("PlayerCategory")] - public InheritableValue IsVisibleForEnemyPlayers = new InheritableValue(false); - [InheritableCategory("PlayerCategory")] - public InheritableValue IsVisibleForOtherPlayers = new InheritableValue(false); - - [InheritableCategory("ChatFeatureCategory")] - public InheritableReference> TargetChatTypes = new(new List(Enum.GetValues())); - [InheritableCategory("ChatFeatureCategory")] - public InheritableValue TargetChatTypesIncludeUndefined = new(true); - - [JsonIgnore] - public string[] IdentitiesToAddTo - { - get + if (GameObjectNamesToApplyTo == null || GameObjectNamesToApplyTo.InheritedValue == null) { - if (GameObjectNamesToApplyTo == null || GameObjectNamesToApplyTo.InheritedValue == null) - { - return new string[] { }; - } - - return GameObjectNamesToApplyTo.InheritedValue.Split(';', ',').Where(item => !string.IsNullOrEmpty(item)).Select(item => item.Trim()).ToArray(); + return new string[] { }; } + + return GameObjectNamesToApplyTo.InheritedValue.Split(';', ',').Where(item => !string.IsNullOrEmpty(item)).Select(item => item.Trim()).ToArray(); } + } - private Tag? m_Defaults; - [JsonIgnore] - public bool HasDefaults + private Tag? m_Defaults; + [JsonIgnore] + public bool HasDefaults + { + get { return m_Defaults != null; } + } + + public Tag() + { + Name = new LiteralPluginString(""); + m_Defaults = null; + } + + public Tag(IPluginString name) + { + Name = name; + m_Defaults = null; + } + + public Tag(IPluginString name, Tag defaults) + { + Name = name; + m_Defaults = defaults; + SetChanges(defaults.GetChanges()); + } + + public Dictionary GetChanges(Dictionary? defaultChanges = null) + { + Dictionary changes = []; + + foreach ((var name, var inheritable) in Inheritables) { - get { return m_Defaults != null; } - } - - public Tag() - { - Name = new LiteralPluginString(""); - m_Defaults = null; - } - - public Tag(IPluginString name) - { - Name = name; - m_Defaults = null; - } - - public Tag(IPluginString name, Tag defaults) - { - Name = name; - m_Defaults = defaults; - SetChanges(defaults.GetChanges()); - } - - public Dictionary GetChanges(Dictionary? defaultChanges = null) - { - Dictionary changes = new Dictionary(); - - foreach ((var name, var inheritable) in Inheritables) + // If there's a default for this name, only set the value if it's different from the default + if (defaultChanges != null && defaultChanges.TryGetValue(name, out var defaultInheritableData)) { - // If there's a default for this name, only set the value if it's different from the default - if (defaultChanges != null && defaultChanges.TryGetValue(name, out var defaultInheritableData)) - { - var inheritableData = inheritable.GetData(); - if (inheritableData.Behavior != defaultInheritableData.Behavior || - !EqualsInheritableData(inheritableData, defaultInheritableData)) - { - changes[name] = inheritable.GetData(); - } - } - // If there's no default, then only set the value if it's not inherited - else if (inheritable.Behavior != InheritableBehavior.Inherit) + var inheritableData = inheritable.GetData(); + if (inheritableData.Behavior != defaultInheritableData.Behavior || + !EqualsInheritableData(inheritableData, defaultInheritableData)) { changes[name] = inheritable.GetData(); } } - - 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; - } - - private static readonly Dictionary ObsulteInheritableStringMap = new() - { - { "IsIconVisibleInChat", nameof(IsRoleIconVisibleInChat) }, - { "IsIconVisibleInNameplate", nameof(IsRoleIconVisibleInNameplates) }, - { "IsIconVisibleInNameplates", nameof(IsRoleIconVisibleInNameplates) } - }; - private static string FixObsuleteInheritableStringName(string name) - { - if (ObsulteInheritableStringMap.ContainsKey(name)) - return ObsulteInheritableStringMap[name]; - else - return name; - } - - public void SetChanges(IEnumerable> changes) - { - foreach ((var name, var inheritableData) in changes) - { - var namefixed = FixObsuleteInheritableStringName(name); - Inheritables[namefixed].SetData(inheritableData); - } - } - - private Dictionary GetAllAsChanges() - { - Dictionary changes = new Dictionary(); - - foreach ((var name, var inheritable) in Inheritables) + // If there's no default, then only set the value if it's not inherited + else if (inheritable.Behavior != InheritableBehavior.Inherit) { changes[name] = inheritable.GetData(); } - - return changes; } - public void SetDefaults() + 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 (m_Defaults != null) - { - // Exclude IsSelected and IsExpanded for UX purposes - SetChanges(m_Defaults.GetAllAsChanges().Where(change => change.Key != nameof(IsSelected) && change.Key != nameof(IsExpanded))); - } + if (!list1[i].Equals(list2[i])) + return false; + } + + return true; + } + + private static readonly Dictionary ObsulteInheritableStringMap = new() + { + { "IsIconVisibleInChat", nameof(IsRoleIconVisibleInChat) }, + { "IsIconVisibleInNameplate", nameof(IsRoleIconVisibleInNameplates) }, + { "IsIconVisibleInNameplates", nameof(IsRoleIconVisibleInNameplates) } + }; + private static string FixObsuleteInheritableStringName(string name) + { + if (ObsulteInheritableStringMap.ContainsKey(name)) + return ObsulteInheritableStringMap[name]; + else + return name; + } + + public void SetChanges(IEnumerable> changes) + { + foreach ((var name, var inheritableData) in changes) + { + var namefixed = FixObsuleteInheritableStringName(name); + Inheritables[namefixed].SetData(inheritableData); + } + } + + private Dictionary GetAllAsChanges() + { + Dictionary changes = []; + + foreach ((var name, var inheritable) in Inheritables) + { + changes[name] = inheritable.GetData(); + } + + return changes; + } + + public void SetDefaults() + { + if (m_Defaults != null) + { + // Exclude IsSelected and IsExpanded for UX purposes + SetChanges(m_Defaults.GetAllAsChanges().Where(change => change.Key != nameof(IsSelected) && change.Key != nameof(IsExpanded))); } } } diff --git a/PlayerTags/Data/TagPosition.cs b/PlayerTags/Data/TagPosition.cs index 4dc027f..ad1c63f 100644 --- a/PlayerTags/Data/TagPosition.cs +++ b/PlayerTags/Data/TagPosition.cs @@ -1,9 +1,8 @@ -namespace PlayerTags.Data +namespace PlayerTags.Data; + +public enum TagPosition { - public enum TagPosition - { - Before, - After, - Replace - } + Before, + After, + Replace } diff --git a/PlayerTags/Data/TagTarget.cs b/PlayerTags/Data/TagTarget.cs index c26d362..ede90df 100644 --- a/PlayerTags/Data/TagTarget.cs +++ b/PlayerTags/Data/TagTarget.cs @@ -1,8 +1,7 @@ - namespace PlayerTags.Data +namespace PlayerTags.Data; + +public enum TagTarget { - public enum TagTarget - { - Chat, - Nameplate - } + Chat, + Nameplate } diff --git a/PlayerTags/Data/WorldHelper.cs b/PlayerTags/Data/WorldHelper.cs index 57e8c3c..319df5d 100644 --- a/PlayerTags/Data/WorldHelper.cs +++ b/PlayerTags/Data/WorldHelper.cs @@ -1,41 +1,40 @@ using Lumina.Excel.GeneratedSheets; using System.Collections.Generic; -namespace PlayerTags.Data -{ - public static class WorldHelper - { - private static Dictionary? s_WorldNames = null; - public static Dictionary WorldNames - { - get - { - if (s_WorldNames == null) - { - s_WorldNames = new Dictionary(); +namespace PlayerTags.Data; - var worlds = PluginServices.DataManager.GetExcelSheet(); - if (worlds != null) +public static class WorldHelper +{ + private static Dictionary? s_WorldNames = null; + public static Dictionary WorldNames + { + get + { + if (s_WorldNames == null) + { + s_WorldNames = []; + + var worlds = PluginServices.DataManager.GetExcelSheet(); + if (worlds != null) + { + foreach (var world in worlds) { - foreach (var world in worlds) - { - s_WorldNames[world.RowId] = world.Name; - } + s_WorldNames[world.RowId] = world.Name; } } - - return s_WorldNames; - } - } - - public static string? GetWorldName(uint? worldId) - { - if (worldId != null && WorldNames.TryGetValue(worldId.Value, out var name)) - { - return name; } - return null; + return s_WorldNames; } } + + public static string? GetWorldName(uint? worldId) + { + if (worldId != null && WorldNames.TryGetValue(worldId.Value, out var name)) + { + return name; + } + + return null; + } } diff --git a/PlayerTags/Extensions.cs b/PlayerTags/Extensions.cs index 964b8e5..de2bc51 100644 --- a/PlayerTags/Extensions.cs +++ b/PlayerTags/Extensions.cs @@ -1,40 +1,35 @@ using Dalamud.Game.Text.SeStringHandling; -using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -namespace PlayerTags +namespace PlayerTags; + +internal static class Extensions { - internal 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) { - /// - /// 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); - } + 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) + /// + /// 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++) { - for (int i = 0; i < payloads.Count; i++) + if (ReferenceEquals(payloads[i], payload)) { - if (ReferenceEquals(payloads[i], payload)) - { - payloads.RemoveAt(i); - break; - } + payloads.RemoveAt(i); + break; } } } diff --git a/PlayerTags/Features/ChatTagTargetFeature.cs b/PlayerTags/Features/ChatTagTargetFeature.cs index 67d63d2..89ecb6f 100644 --- a/PlayerTags/Features/ChatTagTargetFeature.cs +++ b/PlayerTags/Features/ChatTagTargetFeature.cs @@ -1,437 +1,428 @@ -using Dalamud.Game.ClientState.Objects.SubKinds; -using Dalamud.Game.ClientState.Objects.Types; -using Dalamud.Game.Text; +using Dalamud.Game.Text; 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; -using PlayerTags.Inheritables; using System; using System.Collections.Generic; -using System.Data; using System.Linq; -using Action = System.Action; -namespace PlayerTags.Features +namespace PlayerTags.Features; + +/// +/// A feature that adds tags to chat messages. +/// +public class ChatTagTargetFeature : TagTargetFeature { /// - /// A feature that adds tags to chat messages. + /// A match found within a string. /// - public class ChatTagTargetFeature : TagTargetFeature + private class StringMatch { /// - /// A match found within a string. + /// The string that the match was found in. /// - private class StringMatch + public SeString SeString { get; init; } + + public List DisplayTextPayloads { get; init; } = []; + + /// + /// The matching game object if one exists + /// + public GameObject? GameObject { get; init; } + + /// + /// A matching player payload if one exists. + /// + public PlayerPayload? PlayerPayload { get; init; } + + public RawPayload LinkTerminatorPayload { get; init; } + + public Payload? PlayerNamePayload { - /// - /// The string that the match was found in. - /// - public SeString SeString { get; init; } - - public List DisplayTextPayloads { get; init; } = new(); - - /// - /// The matching game object if one exists - /// - public GameObject? GameObject { get; init; } - - /// - /// A matching player payload if one exists. - /// - public PlayerPayload? PlayerPayload { get; init; } - - public RawPayload LinkTerminatorPayload { get; init; } - - public Payload? PlayerNamePayload + get { - get - { - Payload textPayload = null; - string textMatch = GetMatchTextInternal(); - string textMatchShort = BuildPlayername(textMatch); + Payload textPayload = null; + string textMatch = GetMatchTextInternal(); + string textMatchShort = BuildPlayername(textMatch); - textPayload = DisplayTextPayloads.FirstOrDefault(n => n is TextPayload textPayload && (textPayload.Text.Contains(textMatch) || ((!string.IsNullOrEmpty(textMatchShort)) && textPayload.Text.Contains(textMatchShort)))); - textPayload ??= PlayerPayload; - textPayload ??= DisplayTextPayloads.FirstOrDefault(); + textPayload = DisplayTextPayloads.FirstOrDefault(n => n is TextPayload textPayload && (textPayload.Text.Contains(textMatch) || ((!string.IsNullOrEmpty(textMatchShort)) && textPayload.Text.Contains(textMatchShort)))); + textPayload ??= PlayerPayload; + textPayload ??= DisplayTextPayloads.FirstOrDefault(); - return textPayload; - } - } - - public bool IsLocalPlayer - { - get - { - return GetMatchTextInternal() == PluginServices.ClientState.LocalPlayer.Name.TextValue; - } - } - - public StringMatch(SeString seString) - { - SeString = seString; - } - - private string GetMatchTextInternal() - { - if (GameObject != null) - return GameObject.Name.TextValue; - else if (PlayerPayload != null) - return PlayerPayload.PlayerName; - else - return SeString.TextValue; - } - - /// - /// Gets the matches text. - /// - /// The match text. - public string GetMatchText() - { - var playerNamePayload = PlayerNamePayload; - if (playerNamePayload is PlayerPayload pp) - return pp.PlayerName; - else if (playerNamePayload is TextPayload tp) - return tp.Text; - else - return SeString.TextValue; + return textPayload; } } - public ChatTagTargetFeature(PluginConfiguration pluginConfiguration, PluginData pluginData) : base(pluginConfiguration, pluginData) + public bool IsLocalPlayer { - PluginServices.ChatGui.ChatMessage += Chat_ChatMessage; - } - - public override void Dispose() - { - PluginServices.ChatGui.ChatMessage -= Chat_ChatMessage; - base.Dispose(); - } - - private void Chat_ChatMessage(XivChatType type, uint senderId, ref SeString sender, ref SeString message, ref bool isHandled) - { - if (EnableGlobal && pluginConfiguration.GeneralOptions[ActivityContextManager.CurrentActivityContext.ActivityType].IsApplyTagsToAllChatMessagesEnabled) + get { - AddTagsToChat(sender, type, true); - AddTagsToChat(message, type, false); + return GetMatchTextInternal() == PluginServices.ClientState.LocalPlayer.Name.TextValue; } } - protected override bool IsIconVisible(Tag tag) + public StringMatch(SeString seString) { - if (tag.IsRoleIconVisibleInChat.InheritedValue != null) - { - return tag.IsRoleIconVisibleInChat.InheritedValue.Value; - } - - return false; + SeString = seString; } - protected override bool IsTextVisible(Tag tag) + private string GetMatchTextInternal() { - if (tag.IsTextVisibleInChat.InheritedValue != null) - { - return tag.IsTextVisibleInChat.InheritedValue.Value; - } - - return false; + if (GameObject != null) + return GameObject.Name.TextValue; + else if (PlayerPayload != null) + return PlayerPayload.PlayerName; + else + return SeString.TextValue; } /// - /// Searches the given string for game object matches. + /// Gets the matches text. /// - /// The string to search. - /// A list of matched game objects. - private List GetStringMatches(SeString seString) + /// The match text. + public string GetMatchText() { - List stringMatches = new(); - Stack curPlayerPayload = new(); - Stack> curRefPayloads = new(); - var defaultRawPayload = RawPayload.LinkTerminator.Data; + var playerNamePayload = PlayerNamePayload; + if (playerNamePayload is PlayerPayload pp) + return pp.PlayerName; + else if (playerNamePayload is TextPayload tp) + return tp.Text; + else + return SeString.TextValue; + } + } - foreach (var payload in seString.Payloads) + public ChatTagTargetFeature(PluginConfiguration pluginConfiguration, PluginData pluginData) : base(pluginConfiguration, pluginData) + { + PluginServices.ChatGui.ChatMessage += Chat_ChatMessage; + } + + public override void Dispose() + { + PluginServices.ChatGui.ChatMessage -= Chat_ChatMessage; + base.Dispose(); + } + + private void Chat_ChatMessage(XivChatType type, uint senderId, ref SeString sender, ref SeString message, ref bool isHandled) + { + if (EnableGlobal && pluginConfiguration.GeneralOptions[ActivityContextManager.CurrentActivityContext.ActivityType].IsApplyTagsToAllChatMessagesEnabled) + { + AddTagsToChat(sender, type, true); + AddTagsToChat(message, type, false); + } + } + + protected override bool IsIconVisible(Tag tag) + { + if (tag.IsRoleIconVisibleInChat.InheritedValue != null) + { + return tag.IsRoleIconVisibleInChat.InheritedValue.Value; + } + + return false; + } + + protected override bool IsTextVisible(Tag tag) + { + if (tag.IsTextVisibleInChat.InheritedValue != null) + { + return tag.IsTextVisibleInChat.InheritedValue.Value; + } + + return false; + } + + /// + /// Searches the given string for game object matches. + /// + /// The string to search. + /// A list of matched game objects. + private List GetStringMatches(SeString seString) + { + List stringMatches = []; + Stack curPlayerPayload = new(); + Stack> curRefPayloads = new(); + var defaultRawPayload = RawPayload.LinkTerminator.Data; + + foreach (var payload in seString.Payloads) + { + + if (payload is PlayerPayload playerPayload) { + curPlayerPayload.Push(playerPayload); + curRefPayloads.Push([]); + } + else if (payload is RawPayload rawPayload) + { + if (defaultRawPayload.SequenceEqual(rawPayload.Data)) + finishCurrentMatch(rawPayload); + } + else + { + if (curRefPayloads.TryPeek(out List result)) + result.Add(payload); + } + } + // Finally finish, if not closed by RawPayload + finishCurrentMatch(null); + + void finishCurrentMatch(RawPayload linkTerminatorPayload) + { + if (curPlayerPayload.TryPop(out PlayerPayload playerPayload)) + { + var gameObject = PluginServices.ObjectTable.FirstOrDefault(gameObject => gameObject.Name.TextValue == playerPayload.PlayerName); + var stringMatch = new StringMatch(seString) + { + GameObject = gameObject, + PlayerPayload = playerPayload, + LinkTerminatorPayload = linkTerminatorPayload, + DisplayTextPayloads = curRefPayloads.Pop() + }; + stringMatches.Add(stringMatch); + } + } + + return stringMatches; + } + + private void SplitOffPartyNumberPrefix(SeString sender, XivChatType type) + { + if (type == XivChatType.Party || type == XivChatType.Alliance) + { + PlayerPayload lastPlayerPayload = null; + foreach (var payload in sender.Payloads.ToArray()) + { if (payload is PlayerPayload playerPayload) + lastPlayerPayload = playerPayload; + else if (payload is TextPayload playerNamePayload && lastPlayerPayload != null) { - curPlayerPayload.Push(playerPayload); - curRefPayloads.Push(new List()); - } - else if (payload is RawPayload rawPayload) - { - if (defaultRawPayload.SequenceEqual(rawPayload.Data)) - finishCurrentMatch(rawPayload); - } - else - { - if (curRefPayloads.TryPeek(out List result)) - result.Add(payload); - } - } + // Get position of player name in payload + var indexOfPlayerName = playerNamePayload.Text.IndexOf(BuildPlayername(lastPlayerPayload.PlayerName)); - // Finally finish, if not closed by RawPayload - finishCurrentMatch(null); + if (indexOfPlayerName == -1) + indexOfPlayerName = playerNamePayload.Text.IndexOf(lastPlayerPayload.PlayerName); - void finishCurrentMatch(RawPayload linkTerminatorPayload) - { - if (curPlayerPayload.TryPop(out PlayerPayload playerPayload)) - { - var gameObject = PluginServices.ObjectTable.FirstOrDefault(gameObject => gameObject.Name.TextValue == playerPayload.PlayerName); - var stringMatch = new StringMatch(seString) + if (indexOfPlayerName > 0) { - GameObject = gameObject, - PlayerPayload = playerPayload, - LinkTerminatorPayload = linkTerminatorPayload, - DisplayTextPayloads = curRefPayloads.Pop() - }; - stringMatches.Add(stringMatch); - } - } + // Split off the name from the prefix number + var prefixPayload = new TextPayload(playerNamePayload.Text[..indexOfPlayerName]); + playerNamePayload.Text = playerNamePayload.Text[indexOfPlayerName..]; - return stringMatches; - } - - private void SplitOffPartyNumberPrefix(SeString sender, XivChatType type) - { - if (type == XivChatType.Party || type == XivChatType.Alliance) - { - PlayerPayload lastPlayerPayload = null; - foreach (var payload in sender.Payloads.ToArray()) - { - if (payload is PlayerPayload playerPayload) - lastPlayerPayload = playerPayload; - else if (payload is TextPayload playerNamePayload && lastPlayerPayload != null) - { - // Get position of player name in payload - var indexOfPlayerName = playerNamePayload.Text.IndexOf(BuildPlayername(lastPlayerPayload.PlayerName)); - - if (indexOfPlayerName == -1) - indexOfPlayerName = playerNamePayload.Text.IndexOf(lastPlayerPayload.PlayerName); - - if (indexOfPlayerName > 0) - { - // Split off the name from the prefix number - var prefixPayload = new TextPayload(playerNamePayload.Text[..indexOfPlayerName]); - playerNamePayload.Text = playerNamePayload.Text[indexOfPlayerName..]; - - // Add prefix number before the player name payload - var playerNamePayloadIndex = sender.Payloads.IndexOf(playerNamePayload); - sender.Payloads.Insert(playerNamePayloadIndex, prefixPayload); - } + // Add prefix number before the player name payload + var playerNamePayloadIndex = sender.Payloads.IndexOf(playerNamePayload); + sender.Payloads.Insert(playerNamePayloadIndex, prefixPayload); } } } } - - private void ParsePayloadsForOwnPlayer(SeString seString, XivChatType chatType, bool isSender) - { - if (PluginServices.ClientState.LocalPlayer != null) - { - foreach (var payload in seString.Payloads.ToArray()) - { - if (payload is TextPayload textPayload) - { - List playerTextPayloads = new List(); - - var playerName = PluginServices.ClientState.LocalPlayer.Name.TextValue; - var playerNameShorted = BuildPlayername(playerName); - - if (textPayload.Text == playerName || textPayload.Text == playerNameShorted) - { - playerTextPayloads.Add(textPayload); - } - else - { - var usedPlayerName = chatType == XivChatType.Party || chatType == XivChatType.Alliance ? playerNameShorted : playerName; - var textMatchIndex = textPayload.Text.IndexOf(usedPlayerName); - - while (textMatchIndex >= 0) - { - var textPayloadIndex = seString.Payloads.IndexOf(payload); - - // Chop text to the left and insert it as a new payload - if (textMatchIndex > 0) - { - // Add the content before the player - seString.Payloads.Insert(textPayloadIndex++, new TextPayload(textPayload.Text.Substring(0, textMatchIndex))); - - // Remove from the chopped text from the original payload - textPayload.Text = textPayload.Text.Substring(textMatchIndex, textPayload.Text.Length - textMatchIndex); - } - - // This is the last reference to the local player in this payload - if (textPayload.Text.Length == usedPlayerName.Length) - { - playerTextPayloads.Add(textPayload); - break; - } - - // Create the new name payload and add it - var playerTextPayload = new TextPayload(usedPlayerName); - playerTextPayloads.Add(playerTextPayload); - seString.Payloads.Insert(textPayloadIndex, playerTextPayload); - - // Remove from the chopped text from the original payload - textPayload.Text = textPayload.Text.Substring(usedPlayerName.Length); - - textMatchIndex = textPayload.Text.IndexOf(usedPlayerName); - } - } - - foreach (var playerTextPayload in playerTextPayloads) - { - // Fix displaying of abbreviated own player name as the game does this after the chat message handler - playerTextPayload.Text = BuildPlayername(playerTextPayload.Text); - - var playerPayload = new PlayerPayload(playerName, PluginServices.ClientState.LocalPlayer.HomeWorld.Id); - int playerPayloadIndex = seString.Payloads.IndexOf(playerTextPayload); - var hasNumberPrefix = isSender && (chatType == XivChatType.Party || chatType == XivChatType.Alliance); - - // Ensure to include the group number prefix within the player link - if (hasNumberPrefix) - playerPayloadIndex--; - - // Add the Player Link Payload - seString.Payloads.Insert(playerPayloadIndex++, playerPayload); - - // Same as above, but reverse - if (hasNumberPrefix) - playerPayloadIndex++; - - // Add the Link Terminator to end the Player Link. This should be done behind the Text Payload (display text). - // Normally used to end PlayerPayload linking. But for the own player it has no affect. Anyway, use it, just because. Maybe it's needed in the future somewhere else. - seString.Payloads.Insert(++playerPayloadIndex, RawPayload.LinkTerminator); - - // I M P O R T A N T N O T I C E: - // The PlayerPayload is now just temporary. We keep the TextPayload. - // The PayerPayload gets removed at the ChatTagTargetFeature at the end and the TextPayload will be keeped there. - } - } - } - } - } - - /// - /// Adds all configured tags to a chat message. - /// - /// The message to change. - private void AddTagsToChat(SeString message, XivChatType chatType, bool isSender) - { - // Parse Payloads for local player to be able to work with in the following code - ParsePayloadsForOwnPlayer(message, chatType, isSender); - - // Split out the party/alliance number from the PlayerPayload - if (isSender) - SplitOffPartyNumberPrefix(message, chatType); - - var stringMatches = GetStringMatches(message); - foreach (var stringMatch in stringMatches) - { - StringChanges stringChanges = new(); - - bool isTagEnabled(Tag tag) - => tag.TagPositionInChat.InheritedValue != null && tag.TargetChatTypes.InheritedValue != null && - (tag.TargetChatTypes.InheritedValue.Contains(chatType) || (!Enum.IsDefined(chatType) && (tag.TargetChatTypesIncludeUndefined?.InheritedValue ?? false))); - - if (stringMatch.GameObject is PlayerCharacter playerCharacter) - { - // Add the job tag - if (playerCharacter.ClassJob.GameData != null && pluginData.JobTags.TryGetValue(playerCharacter.ClassJob.GameData.Abbreviation, out var jobTag)) - { - if (isTagEnabled(jobTag)) - { - var payloads = GetPayloads(jobTag, stringMatch.GameObject); - if (payloads.Any()) - { - var insertBehindNumberPrefix = jobTag.InsertBehindNumberPrefixInChat?.Value ?? true; - addPayloadChanges(jobTag, payloads); - } - } - } - - // Add randomly generated name tag payload - if (pluginConfiguration.IsPlayerNameRandomlyGenerated) - { - var playerName = stringMatch.GetMatchText(); - if (playerName != null) - { - var generatedName = BuildPlayername(RandomNameGenerator.Generate(playerName)); - if (generatedName != null) - { - AddPayloadChanges(StringPosition.Replace, Enumerable.Empty().Append(new TextPayload(generatedName)), stringChanges, false); - } - } - } - } - - // Add custom tags - if (stringMatch.PlayerPayload != null) - { - Identity identity = pluginData.GetIdentity(stringMatch.PlayerPayload); - foreach (var customTagId in identity.CustomTagIds) - { - var customTag = pluginData.CustomTags.FirstOrDefault(tag => tag.CustomId.Value == customTagId); - if (customTag != null) - { - if (isTagEnabled(customTag)) - { - var customTagPayloads = GetPayloads(customTag, stringMatch.GameObject); - if (customTagPayloads.Any()) - { - var insertBehindNumberPrefix = customTag.InsertBehindNumberPrefixInChat?.Value ?? true; - addPayloadChanges(customTag, customTagPayloads); - } - } - } - } - } - - void addPayloadChanges(Tag tag, IEnumerable payloads) - { - var insertBehindNumberPrefix = tag.InsertBehindNumberPrefixInChat?.Value ?? true; - var insertPositionInChat = tag.TagPositionInChat.InheritedValue.Value; - AddPayloadChanges((StringPosition)insertPositionInChat, payloads, stringChanges, insertBehindNumberPrefix); - } - - // An additional step to apply text color to additional locations - if (stringMatch.PlayerPayload != null && stringMatch.DisplayTextPayloads.Any()) - { - Identity identity = pluginData.GetIdentity(stringMatch.PlayerPayload); - - if (stringMatch.GameObject is PlayerCharacter playerCharacter1) - { - if (playerCharacter1.ClassJob.GameData != null && pluginData.JobTags.TryGetValue(playerCharacter1.ClassJob.GameData.Abbreviation, out var jobTag) && isTagEnabled(jobTag)) - applyTextFormatting(jobTag); - } - - foreach (var customTagId in identity.CustomTagIds) - { - var customTag = pluginData.CustomTags.FirstOrDefault(tag => tag.CustomId.Value == customTagId); - if (customTag != null && isTagEnabled(customTag)) - applyTextFormatting(customTag); - } - - void applyTextFormatting(Tag tag) - => ApplyTextFormatting(stringMatch.GameObject, tag, new[] { message }, new[] { tag.IsTextColorAppliedToChatName }, stringMatch.DisplayTextPayloads); - } - - // Finally apply the all the changes to the message - ApplyStringChanges(message, stringChanges, stringMatch.DisplayTextPayloads, stringMatch.PlayerNamePayload); - - // Remove PlayerPayload and LinkTerminator if it's your own character (they just got added temporary) - if (stringMatch.IsLocalPlayer) - { - if (stringMatch.PlayerPayload != null) - message.Remove(stringMatch.PlayerPayload); - if (stringMatch.LinkTerminatorPayload != null) - message.Remove(stringMatch.LinkTerminatorPayload); - } - } - } + } + + private void ParsePayloadsForOwnPlayer(SeString seString, XivChatType chatType, bool isSender) + { + if (PluginServices.ClientState.LocalPlayer != null) + { + foreach (var payload in seString.Payloads.ToArray()) + { + if (payload is TextPayload textPayload) + { + List playerTextPayloads = []; + + var playerName = PluginServices.ClientState.LocalPlayer.Name.TextValue; + var playerNameShorted = BuildPlayername(playerName); + + if (textPayload.Text == playerName || textPayload.Text == playerNameShorted) + { + playerTextPayloads.Add(textPayload); + } + else + { + var usedPlayerName = chatType == XivChatType.Party || chatType == XivChatType.Alliance ? playerNameShorted : playerName; + var textMatchIndex = textPayload.Text.IndexOf(usedPlayerName); + + while (textMatchIndex >= 0) + { + var textPayloadIndex = seString.Payloads.IndexOf(payload); + + // Chop text to the left and insert it as a new payload + if (textMatchIndex > 0) + { + // Add the content before the player + seString.Payloads.Insert(textPayloadIndex++, new TextPayload(textPayload.Text.Substring(0, textMatchIndex))); + + // Remove from the chopped text from the original payload + textPayload.Text = textPayload.Text.Substring(textMatchIndex, textPayload.Text.Length - textMatchIndex); + } + + // This is the last reference to the local player in this payload + if (textPayload.Text.Length == usedPlayerName.Length) + { + playerTextPayloads.Add(textPayload); + break; + } + + // Create the new name payload and add it + var playerTextPayload = new TextPayload(usedPlayerName); + playerTextPayloads.Add(playerTextPayload); + seString.Payloads.Insert(textPayloadIndex, playerTextPayload); + + // Remove from the chopped text from the original payload + textPayload.Text = textPayload.Text.Substring(usedPlayerName.Length); + + textMatchIndex = textPayload.Text.IndexOf(usedPlayerName); + } + } + + foreach (var playerTextPayload in playerTextPayloads) + { + // Fix displaying of abbreviated own player name as the game does this after the chat message handler + playerTextPayload.Text = BuildPlayername(playerTextPayload.Text); + + var playerPayload = new PlayerPayload(playerName, PluginServices.ClientState.LocalPlayer.HomeWorld.Id); + int playerPayloadIndex = seString.Payloads.IndexOf(playerTextPayload); + var hasNumberPrefix = isSender && (chatType == XivChatType.Party || chatType == XivChatType.Alliance); + + // Ensure to include the group number prefix within the player link + if (hasNumberPrefix) + playerPayloadIndex--; + + // Add the Player Link Payload + seString.Payloads.Insert(playerPayloadIndex++, playerPayload); + + // Same as above, but reverse + if (hasNumberPrefix) + playerPayloadIndex++; + + // Add the Link Terminator to end the Player Link. This should be done behind the Text Payload (display text). + // Normally used to end PlayerPayload linking. But for the own player it has no affect. Anyway, use it, just because. Maybe it's needed in the future somewhere else. + seString.Payloads.Insert(++playerPayloadIndex, RawPayload.LinkTerminator); + + // I M P O R T A N T N O T I C E: + // The PlayerPayload is now just temporary. We keep the TextPayload. + // The PayerPayload gets removed at the ChatTagTargetFeature at the end and the TextPayload will be keeped there. + } + } + } + } + } + + /// + /// Adds all configured tags to a chat message. + /// + /// The message to change. + private void AddTagsToChat(SeString message, XivChatType chatType, bool isSender) + { + // Parse Payloads for local player to be able to work with in the following code + ParsePayloadsForOwnPlayer(message, chatType, isSender); + + // Split out the party/alliance number from the PlayerPayload + if (isSender) + SplitOffPartyNumberPrefix(message, chatType); + + var stringMatches = GetStringMatches(message); + foreach (var stringMatch in stringMatches) + { + StringChanges stringChanges = new(); + + bool isTagEnabled(Tag tag) + => tag.TagPositionInChat.InheritedValue != null && tag.TargetChatTypes.InheritedValue != null && + (tag.TargetChatTypes.InheritedValue.Contains(chatType) || (!Enum.IsDefined(chatType) && (tag.TargetChatTypesIncludeUndefined?.InheritedValue ?? false))); + + if (stringMatch.GameObject is PlayerCharacter playerCharacter) + { + // Add the job tag + if (playerCharacter.ClassJob.GameData != null && pluginData.JobTags.TryGetValue(playerCharacter.ClassJob.GameData.Abbreviation, out var jobTag)) + { + if (isTagEnabled(jobTag)) + { + var payloads = GetPayloads(jobTag, stringMatch.GameObject); + if (payloads.Any()) + { + var insertBehindNumberPrefix = jobTag.InsertBehindNumberPrefixInChat?.Value ?? true; + addPayloadChanges(jobTag, payloads); + } + } + } + + // Add randomly generated name tag payload + if (pluginConfiguration.IsPlayerNameRandomlyGenerated) + { + var playerName = stringMatch.GetMatchText(); + if (playerName != null) + { + var generatedName = BuildPlayername(RandomNameGenerator.Generate(playerName)); + if (generatedName != null) + { + AddPayloadChanges(StringPosition.Replace, Enumerable.Empty().Append(new TextPayload(generatedName)), stringChanges, false); + } + } + } + } + + // Add custom tags + if (stringMatch.PlayerPayload != null) + { + Identity identity = pluginData.GetIdentity(stringMatch.PlayerPayload); + foreach (var customTagId in identity.CustomTagIds) + { + var customTag = pluginData.CustomTags.FirstOrDefault(tag => tag.CustomId.Value == customTagId); + if (customTag != null) + { + if (isTagEnabled(customTag)) + { + var customTagPayloads = GetPayloads(customTag, stringMatch.GameObject); + if (customTagPayloads.Any()) + { + var insertBehindNumberPrefix = customTag.InsertBehindNumberPrefixInChat?.Value ?? true; + addPayloadChanges(customTag, customTagPayloads); + } + } + } + } + } + + void addPayloadChanges(Tag tag, IEnumerable payloads) + { + var insertBehindNumberPrefix = tag.InsertBehindNumberPrefixInChat?.Value ?? true; + var insertPositionInChat = tag.TagPositionInChat.InheritedValue.Value; + AddPayloadChanges((StringPosition)insertPositionInChat, payloads, stringChanges, insertBehindNumberPrefix); + } + + // An additional step to apply text color to additional locations + if (stringMatch.PlayerPayload != null && stringMatch.DisplayTextPayloads.Any()) + { + Identity identity = pluginData.GetIdentity(stringMatch.PlayerPayload); + + if (stringMatch.GameObject is PlayerCharacter playerCharacter1) + { + if (playerCharacter1.ClassJob.GameData != null && pluginData.JobTags.TryGetValue(playerCharacter1.ClassJob.GameData.Abbreviation, out var jobTag) && isTagEnabled(jobTag)) + applyTextFormatting(jobTag); + } + + foreach (var customTagId in identity.CustomTagIds) + { + var customTag = pluginData.CustomTags.FirstOrDefault(tag => tag.CustomId.Value == customTagId); + if (customTag != null && isTagEnabled(customTag)) + applyTextFormatting(customTag); + } + + void applyTextFormatting(Tag tag) + => ApplyTextFormatting(stringMatch.GameObject, tag, new[] { message }, new[] { tag.IsTextColorAppliedToChatName }, stringMatch.DisplayTextPayloads); + } + + // Finally apply the all the changes to the message + ApplyStringChanges(message, stringChanges, stringMatch.DisplayTextPayloads, stringMatch.PlayerNamePayload); + + // Remove PlayerPayload and LinkTerminator if it's your own character (they just got added temporary) + if (stringMatch.IsLocalPlayer) + { + if (stringMatch.PlayerPayload != null) + message.Remove(stringMatch.PlayerPayload); + if (stringMatch.LinkTerminatorPayload != null) + message.Remove(stringMatch.LinkTerminatorPayload); + } + } } } diff --git a/PlayerTags/Features/CustomTagsContextMenuFeature.cs b/PlayerTags/Features/CustomTagsContextMenuFeature.cs index 2309cc9..596c5d6 100644 --- a/PlayerTags/Features/CustomTagsContextMenuFeature.cs +++ b/PlayerTags/Features/CustomTagsContextMenuFeature.cs @@ -1,7 +1,4 @@ -using Dalamud.ContextMenu; -using Dalamud.Game.Text.SeStringHandling; -using Dalamud.Logging; -using Dalamud.Plugin; +using Dalamud.Plugin; using PlayerTags.Configuration; using PlayerTags.Data; using PlayerTags.Resources; @@ -9,88 +6,87 @@ using System; using System.Collections.Generic; using System.Linq; -namespace PlayerTags.Features +namespace PlayerTags.Features; + +/// +/// A feature that adds options for the management of custom tags to context menus. +/// +public class CustomTagsContextMenuFeature : FeatureBase, IDisposable { - /// - /// A feature that adds options for the management of custom tags to context menus. - /// - public class CustomTagsContextMenuFeature : FeatureBase, IDisposable + private readonly string[] supportedAddonNames = + [ + null, + "_PartyList", + "ChatLog", + "ContactList", + "ContentMemberList", + "CrossWorldLinkshell", + "FreeCompany", + "FriendList", + "LookingForGroup", + "LinkShell", + "PartyMemberList", + "SocialList", + ]; + + private DalamudContextMenu? m_ContextMenu; + + public CustomTagsContextMenuFeature(PluginConfiguration pluginConfiguration, PluginData pluginData, IDalamudPluginInterface pluginInterface) : base(pluginConfiguration, pluginData) { - private string?[] SupportedAddonNames = new string?[] - { - null, - "_PartyList", - "ChatLog", - "ContactList", - "ContentMemberList", - "CrossWorldLinkshell", - "FreeCompany", - "FriendList", - "LookingForGroup", - "LinkShell", - "PartyMemberList", - "SocialList", - }; + m_ContextMenu = new DalamudContextMenu(pluginInterface); + m_ContextMenu.OnOpenGameObjectContextMenu += ContextMenuHooks_ContextMenuOpened; + } - private DalamudContextMenu? m_ContextMenu; - - public CustomTagsContextMenuFeature(PluginConfiguration pluginConfiguration, PluginData pluginData, IDalamudPluginInterface pluginInterface) : base(pluginConfiguration, pluginData) + public void Dispose() + { + if (m_ContextMenu != null) { - m_ContextMenu = new DalamudContextMenu(pluginInterface); - m_ContextMenu.OnOpenGameObjectContextMenu += ContextMenuHooks_ContextMenuOpened; + m_ContextMenu.OnOpenGameObjectContextMenu -= ContextMenuHooks_ContextMenuOpened; + ((IDisposable)m_ContextMenu).Dispose(); + m_ContextMenu = null; + } + } + + private void ContextMenuHooks_ContextMenuOpened(GameObjectContextMenuOpenArgs contextMenuOpenedArgs) + { + if (!EnableGlobal || !pluginConfiguration.IsCustomTagsContextMenuEnabled + || !supportedAddonNames.Contains(contextMenuOpenedArgs.ParentAddonName)) + { + return; } - public void Dispose() + Identity? identity = pluginData.GetIdentity(contextMenuOpenedArgs); + if (identity != null) { - if (m_ContextMenu != null) + var allTags = new Dictionary(); + foreach (var customTag in pluginData.CustomTags) { - m_ContextMenu.OnOpenGameObjectContextMenu -= ContextMenuHooks_ContextMenuOpened; - ((IDisposable)m_ContextMenu).Dispose(); - m_ContextMenu = null; - } - } - - private void ContextMenuHooks_ContextMenuOpened(GameObjectContextMenuOpenArgs contextMenuOpenedArgs) - { - if (!EnableGlobal || !pluginConfiguration.IsCustomTagsContextMenuEnabled - || !SupportedAddonNames.Contains(contextMenuOpenedArgs.ParentAddonName)) - { - return; + var isAdded = identity.CustomTagIds.Contains(customTag.CustomId.Value); + allTags.Add(customTag, isAdded); } - Identity? identity = pluginData.GetIdentity(contextMenuOpenedArgs); - if (identity != null) + var sortedTags = allTags.OrderBy(n => n.Value); + foreach (var tag in sortedTags) { - var allTags = new Dictionary(); - foreach (var customTag in pluginData.CustomTags) - { - var isAdded = identity.CustomTagIds.Contains(customTag.CustomId.Value); - allTags.Add(customTag, isAdded); - } - - var sortedTags = allTags.OrderBy(n => n.Value); - foreach (var tag in sortedTags) - { - string menuItemText; - if (tag.Value) - menuItemText = Strings.Loc_Static_ContextMenu_RemoveTag; - else - menuItemText = Strings.Loc_Static_ContextMenu_AddTag; - menuItemText = string.Format(menuItemText, tag.Key.Text.Value); + string menuItemText; + if (tag.Value) + menuItemText = Strings.Loc_Static_ContextMenu_RemoveTag; + else + menuItemText = Strings.Loc_Static_ContextMenu_AddTag; + menuItemText = string.Format(menuItemText, tag.Key.Text.Value); - contextMenuOpenedArgs.AddCustomItem( - new GameObjectContextMenuItem(menuItemText, openedEventArgs => - { - if (tag.Value) - pluginData.RemoveCustomTagFromIdentity(tag.Key, identity); - else - pluginData.AddCustomTagToIdentity(tag.Key, identity); - pluginConfiguration.Save(pluginData); - }) - { - IsSubMenu = false - }); - } + contextMenuOpenedArgs.AddCustomItem( + new GameObjectContextMenuItem(menuItemText, openedEventArgs => + { + if (tag.Value) + pluginData.RemoveCustomTagFromIdentity(tag.Key, identity); + else + pluginData.AddCustomTagToIdentity(tag.Key, identity); + pluginConfiguration.Save(pluginData); + }) + { + IsSubMenu = false + }); } } } diff --git a/PlayerTags/Features/FeatureBase.cs b/PlayerTags/Features/FeatureBase.cs index f49cce7..9d3f2c1 100644 --- a/PlayerTags/Features/FeatureBase.cs +++ b/PlayerTags/Features/FeatureBase.cs @@ -1,24 +1,18 @@ using PlayerTags.Configuration; using PlayerTags.Data; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -namespace PlayerTags.Features +namespace PlayerTags.Features; + +public class FeatureBase { - public class FeatureBase + protected readonly PluginConfiguration pluginConfiguration; + protected readonly PluginData pluginData; + + public virtual bool EnableGlobal => pluginConfiguration.EnabledGlobal; + + protected FeatureBase(PluginConfiguration pluginConfiguration, PluginData pluginData) { - protected readonly PluginConfiguration pluginConfiguration; - protected readonly PluginData pluginData; - - public virtual bool EnableGlobal => pluginConfiguration.EnabledGlobal; - - protected FeatureBase(PluginConfiguration pluginConfiguration, PluginData pluginData) - { - this.pluginConfiguration = pluginConfiguration; - this.pluginData = pluginData; - } + this.pluginConfiguration = pluginConfiguration; + this.pluginData = pluginData; } } diff --git a/PlayerTags/Features/NameplateTagTargetFeature.cs b/PlayerTags/Features/NameplateTagTargetFeature.cs index 0d763fd..1533e4c 100644 --- a/PlayerTags/Features/NameplateTagTargetFeature.cs +++ b/PlayerTags/Features/NameplateTagTargetFeature.cs @@ -1,274 +1,266 @@ -using Dalamud.Game.ClientState.Objects.SubKinds; -using Dalamud.Game.ClientState.Objects.Types; -using Dalamud.Game.Text.SeStringHandling; +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; using Pilz.Dalamud.Tools.Strings; using PlayerTags.Configuration; using PlayerTags.Data; using PlayerTags.GameInterface.Nameplates; -using PlayerTags.Inheritables; using System; using System.Collections.Generic; using System.Linq; -namespace PlayerTags.Features +namespace PlayerTags.Features; + +/// +/// A feature that adds tags to nameplates. +/// +public class NameplateTagTargetFeature : TagTargetFeature { - /// - /// A feature that adds tags to nameplates. - /// - public class NameplateTagTargetFeature : TagTargetFeature + private readonly StatusIconPriorizer statusiconPriorizer; + private readonly JobIconSets jobIconSets = new(); + private Nameplate? m_Nameplate; + + public NameplateTagTargetFeature(PluginConfiguration pluginConfiguration, PluginData pluginData) : base(pluginConfiguration, pluginData) { - private readonly StatusIconPriorizer statusiconPriorizer; - private readonly JobIconSets jobIconSets = new(); - private Nameplate? m_Nameplate; + statusiconPriorizer = new(pluginConfiguration.StatusIconPriorizerSettings); - public NameplateTagTargetFeature(PluginConfiguration pluginConfiguration, PluginData pluginData) : base(pluginConfiguration, pluginData) + 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(); + } + + private void Hook() + { + if (m_Nameplate == null) { - statusiconPriorizer = new(pluginConfiguration.StatusIconPriorizerSettings); + m_Nameplate = new(); - 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(); - } - - private void Hook() - { - if (m_Nameplate == null) - { - m_Nameplate = new(); - - if (!m_Nameplate.IsValid) - m_Nameplate = null; - - if (m_Nameplate != null) - m_Nameplate.PlayerNameplateUpdated += Nameplate_PlayerNameplateUpdated; - } - } - - private void Unhook() - { - if (m_Nameplate != null) - { - m_Nameplate.PlayerNameplateUpdated -= Nameplate_PlayerNameplateUpdated; - m_Nameplate.Dispose(); + if (!m_Nameplate.IsValid) m_Nameplate = null; - } - } - private void ClientState_Login() - { - Hook(); - } - - private void ClientState_Logout() - { - Unhook(); - } - - protected override bool IsIconVisible(Tag tag) - { - if (tag.IsRoleIconVisibleInNameplates.InheritedValue != null) - return tag.IsRoleIconVisibleInNameplates.InheritedValue.Value; - return false; - } - - protected override bool IsTextVisible(Tag tag) - { - if (tag.IsTextVisibleInNameplates.InheritedValue != null) - return tag.IsTextVisibleInNameplates.InheritedValue.Value; - return false; - } - - private unsafe void Nameplate_PlayerNameplateUpdated(PlayerNameplateUpdatedArgs args) - { - if (!EnableGlobal) return; - - var beforeTitleBytes = args.Title.Encode(); - var iconID = args.IconId; - var generalOptions = pluginConfiguration.GeneralOptions[ActivityContextManager.CurrentActivityContext.ActivityType]; - - AddTagsToNameplate(args.PlayerCharacter, args.Name, args.Title, args.FreeCompany, ref iconID, generalOptions); - - args.IconId = iconID; - - if (generalOptions.NameplateTitlePosition == NameplateTitlePosition.AlwaysAboveName) - args.IsTitleAboveName = true; - else if (generalOptions.NameplateTitlePosition == NameplateTitlePosition.AlwaysBelowName) - args.IsTitleAboveName = false; - - if (generalOptions.NameplateTitleVisibility == NameplateTitleVisibility.Always) - args.IsTitleVisible = true; - else if (generalOptions.NameplateTitleVisibility == NameplateTitleVisibility.Never) - args.IsTitleVisible = false; - else if (generalOptions.NameplateTitleVisibility == NameplateTitleVisibility.WhenHasTags) - { - bool hasTitleChanged = !beforeTitleBytes.SequenceEqual(args.Title.Encode()); - args.IsTitleVisible = hasTitleChanged; - } - - if (generalOptions.NameplateFreeCompanyVisibility == NameplateFreeCompanyVisibility.Never) - args.FreeCompany.Payloads.Clear(); - } - - /// - /// Adds the given payload changes to the specified locations. - /// - /// The nameplate element of the changes. - /// 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, NameplateChanges nameplateChanges, bool forceUsingSingleAnchorPayload) - { - if (payloadChanges.Any()) - { - var changes = nameplateChanges.GetChanges((NameplateElements)nameplateElement); - AddPayloadChanges((StringPosition)tagPosition, payloadChanges, changes, forceUsingSingleAnchorPayload); - } - } - - 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. - /// - /// The game object context. - /// 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, GeneralOptionsClass generalOptions) - { - var playerCharacter = gameObject as PlayerCharacter; - int? newStatusIcon = null; - NameplateChanges nameplateChanges = GenerateEmptyNameplateChanges(name, title, freeCompany); - - if (playerCharacter != null && (!playerCharacter.IsDead || generalOptions.NameplateDeadPlayerHandling != DeadPlayerHandling.Ignore)) - { - var classJob = playerCharacter.ClassJob; - var classJobGameData = classJob?.GameData; - - // Add the job tags - if (classJobGameData != null && pluginData.JobTags.TryGetValue(classJobGameData.Abbreviation, out var jobTag)) - { - if (jobTag.TagTargetInNameplates.InheritedValue != null && jobTag.TagPositionInNameplates.InheritedValue != null) - checkTag(jobTag); - } - - // Add the randomly generated name tag payload - if (pluginConfiguration.IsPlayerNameRandomlyGenerated) - { - var characterName = playerCharacter.Name.TextValue; - if (characterName != null) - { - var generatedName = RandomNameGenerator.Generate(characterName); - if (generatedName != null) - AddPayloadChanges(NameplateElement.Name, TagPosition.Replace, Enumerable.Empty().Append(new TextPayload(generatedName)), nameplateChanges, false); - } - } - - // Add custom tags - Identity identity = pluginData.GetIdentity(playerCharacter); - foreach (var customTagId in identity.CustomTagIds) - { - var customTag = pluginData.CustomTags.FirstOrDefault(tag => tag.CustomId.Value == customTagId); - if (customTag != null) - checkTag(customTag); - } - - void checkTag(Tag tag) - { - if (tag.TagTargetInNameplates.InheritedValue != null && tag.TagPositionInNameplates.InheritedValue != null) - { - var payloads = GetPayloads(tag, gameObject); - if (payloads.Any()) - AddPayloadChanges(tag.TagTargetInNameplates.InheritedValue.Value, tag.TagPositionInNameplates.InheritedValue.Value, payloads, nameplateChanges, false); - } - if (IsTagVisible(tag, gameObject) && newStatusIcon == null && classJob != null && (tag.IsJobIconVisibleInNameplates?.InheritedValue ?? false)) - newStatusIcon = jobIconSets.GetJobIcon(tag.JobIconSet?.InheritedValue ?? 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, 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 (playerCharacter != null && (!playerCharacter.IsDead || generalOptions.NameplateDeadPlayerHandling == DeadPlayerHandling.Include)) - { - // An additional step to apply text color to additional locations - Identity identity = pluginData.GetIdentity(playerCharacter); - foreach (var customTagId in identity.CustomTagIds) - { - var customTag = pluginData.CustomTags.FirstOrDefault(tag => tag.CustomId.Value == customTagId); - if (customTag != null) - applyTextFormatting(customTag); - } - - if (playerCharacter.ClassJob.GameData != null && 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); - } - } - } - - 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)); - } - } - } - - protected void ApplyNameplateChanges(NameplateChanges nameplateChanges) - { - var props = new NameplateChangesProps - { - Changes = nameplateChanges - }; - NameplateUpdateFactory.ApplyNameplateChanges(props); + if (m_Nameplate != null) + m_Nameplate.PlayerNameplateUpdated += Nameplate_PlayerNameplateUpdated; } } + + private void Unhook() + { + if (m_Nameplate != null) + { + m_Nameplate.PlayerNameplateUpdated -= Nameplate_PlayerNameplateUpdated; + m_Nameplate.Dispose(); + m_Nameplate = null; + } + } + + private void ClientState_Login() + { + Hook(); + } + + private void ClientState_Logout() + { + Unhook(); + } + + protected override bool IsIconVisible(Tag tag) + { + if (tag.IsRoleIconVisibleInNameplates.InheritedValue != null) + return tag.IsRoleIconVisibleInNameplates.InheritedValue.Value; + return false; + } + + protected override bool IsTextVisible(Tag tag) + { + if (tag.IsTextVisibleInNameplates.InheritedValue != null) + return tag.IsTextVisibleInNameplates.InheritedValue.Value; + return false; + } + + private unsafe void Nameplate_PlayerNameplateUpdated(PlayerNameplateUpdatedArgs args) + { + if (!EnableGlobal) return; + + var beforeTitleBytes = args.Title.Encode(); + var iconID = args.IconId; + var generalOptions = pluginConfiguration.GeneralOptions[ActivityContextManager.CurrentActivityContext.ActivityType]; + + AddTagsToNameplate(args.PlayerCharacter, args.Name, args.Title, args.FreeCompany, ref iconID, generalOptions); + + args.IconId = iconID; + + if (generalOptions.NameplateTitlePosition == NameplateTitlePosition.AlwaysAboveName) + args.IsTitleAboveName = true; + else if (generalOptions.NameplateTitlePosition == NameplateTitlePosition.AlwaysBelowName) + args.IsTitleAboveName = false; + + if (generalOptions.NameplateTitleVisibility == NameplateTitleVisibility.Always) + args.IsTitleVisible = true; + else if (generalOptions.NameplateTitleVisibility == NameplateTitleVisibility.Never) + args.IsTitleVisible = false; + else if (generalOptions.NameplateTitleVisibility == NameplateTitleVisibility.WhenHasTags) + { + bool hasTitleChanged = !beforeTitleBytes.SequenceEqual(args.Title.Encode()); + args.IsTitleVisible = hasTitleChanged; + } + + if (generalOptions.NameplateFreeCompanyVisibility == NameplateFreeCompanyVisibility.Never) + args.FreeCompany.Payloads.Clear(); + } + + /// + /// Adds the given payload changes to the specified locations. + /// + /// The nameplate element of the changes. + /// 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, NameplateChanges nameplateChanges, bool forceUsingSingleAnchorPayload) + { + if (payloadChanges.Any()) + { + var changes = nameplateChanges.GetChanges((NameplateElements)nameplateElement); + AddPayloadChanges((StringPosition)tagPosition, payloadChanges, changes, forceUsingSingleAnchorPayload); + } + } + + 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. + /// + /// The game object context. + /// 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, GeneralOptionsClass generalOptions) + { + var playerCharacter = gameObject as PlayerCharacter; + int? newStatusIcon = null; + NameplateChanges nameplateChanges = GenerateEmptyNameplateChanges(name, title, freeCompany); + + if (playerCharacter != null && (!playerCharacter.IsDead || generalOptions.NameplateDeadPlayerHandling != DeadPlayerHandling.Ignore)) + { + var classJob = playerCharacter.ClassJob; + var classJobGameData = classJob?.GameData; + + // Add the job tags + if (classJobGameData != null && pluginData.JobTags.TryGetValue(classJobGameData.Abbreviation, out var jobTag)) + { + if (jobTag.TagTargetInNameplates.InheritedValue != null && jobTag.TagPositionInNameplates.InheritedValue != null) + checkTag(jobTag); + } + + // Add the randomly generated name tag payload + if (pluginConfiguration.IsPlayerNameRandomlyGenerated) + { + var characterName = playerCharacter.Name.TextValue; + if (characterName != null) + { + var generatedName = RandomNameGenerator.Generate(characterName); + if (generatedName != null) + AddPayloadChanges(NameplateElement.Name, TagPosition.Replace, Enumerable.Empty().Append(new TextPayload(generatedName)), nameplateChanges, false); + } + } + + // Add custom tags + Identity identity = pluginData.GetIdentity(playerCharacter); + foreach (var customTagId in identity.CustomTagIds) + { + var customTag = pluginData.CustomTags.FirstOrDefault(tag => tag.CustomId.Value == customTagId); + if (customTag != null) + checkTag(customTag); + } + + void checkTag(Tag tag) + { + if (tag.TagTargetInNameplates.InheritedValue != null && tag.TagPositionInNameplates.InheritedValue != null) + { + var payloads = GetPayloads(tag, gameObject); + if (payloads.Any()) + AddPayloadChanges(tag.TagTargetInNameplates.InheritedValue.Value, tag.TagPositionInNameplates.InheritedValue.Value, payloads, nameplateChanges, false); + } + if (IsTagVisible(tag, gameObject) && newStatusIcon == null && classJob != null && (tag.IsJobIconVisibleInNameplates?.InheritedValue ?? false)) + newStatusIcon = jobIconSets.GetJobIcon(tag.JobIconSet?.InheritedValue ?? 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, 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 (playerCharacter != null && (!playerCharacter.IsDead || generalOptions.NameplateDeadPlayerHandling == DeadPlayerHandling.Include)) + { + // An additional step to apply text color to additional locations + Identity identity = pluginData.GetIdentity(playerCharacter); + foreach (var customTagId in identity.CustomTagIds) + { + var customTag = pluginData.CustomTags.FirstOrDefault(tag => tag.CustomId.Value == customTagId); + if (customTag != null) + applyTextFormatting(customTag); + } + + if (playerCharacter.ClassJob.GameData != null && 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); + } + } + } + + 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)); + } + } + } + + 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 272aae5..ae591c9 100644 --- a/PlayerTags/Features/TagTargetFeature.cs +++ b/PlayerTags/Features/TagTargetFeature.cs @@ -1,9 +1,5 @@ -using Dalamud.Game.ClientState.Objects.SubKinds; -using Dalamud.Game.ClientState.Objects.Types; -using Dalamud.Game.Text.SeStringHandling; +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 Pilz.Dalamud.Tools.Strings; using PlayerTags.Configuration; @@ -13,283 +9,280 @@ using PlayerTags.Inheritables; using System; using System.Collections.Generic; using System.Linq; -using System.Xml.Linq; using GameObject = Dalamud.Game.ClientState.Objects.Types.GameObject; -namespace PlayerTags.Features +namespace PlayerTags.Features; + +/// +/// The base of a feature that adds tags to UI elements. +/// +public abstract class TagTargetFeature : FeatureBase, IDisposable { - /// - /// The base of a feature that adds tags to UI elements. - /// - public abstract class TagTargetFeature : FeatureBase, IDisposable + public ActivityContextManager ActivityContextManager { get; init; } + + protected TagTargetFeature(PluginConfiguration pluginConfiguration, PluginData pluginData) : base(pluginConfiguration, pluginData) { - public ActivityContextManager ActivityContextManager { get; init; } + ActivityContextManager = new(); + } - protected TagTargetFeature(PluginConfiguration pluginConfiguration, PluginData pluginData) : base(pluginConfiguration, pluginData) + public virtual void Dispose() + { + ActivityContextManager.Dispose(); + } + + protected abstract bool IsIconVisible(Tag tag); + + protected abstract bool IsTextVisible(Tag tag); + + protected bool IsTagVisible(Tag tag, GameObject? gameObject) + { + bool isVisibleForActivity = ActivityContextHelper.GetIsVisible(ActivityContextManager.CurrentActivityContext.ActivityType, + tag.IsVisibleInPveDuties.InheritedValue ?? false, + tag.IsVisibleInPvpDuties.InheritedValue ?? false, + tag.IsVisibleInOverworld.InheritedValue ?? false); + + if (!isVisibleForActivity) { - ActivityContextManager = new(); + return false; } - public virtual void Dispose() + if (gameObject is PlayerCharacter playerCharacter) { - ActivityContextManager.Dispose(); - } + bool isVisibleForPlayer = PlayerContextHelper.GetIsVisible(playerCharacter, + tag.IsVisibleForSelf.InheritedValue ?? false, + tag.IsVisibleForFriendPlayers.InheritedValue ?? false, + tag.IsVisibleForPartyPlayers.InheritedValue ?? false, + tag.IsVisibleForAlliancePlayers.InheritedValue ?? false, + tag.IsVisibleForEnemyPlayers.InheritedValue ?? false, + tag.IsVisibleForOtherPlayers.InheritedValue ?? false); - protected abstract bool IsIconVisible(Tag tag); - - protected abstract bool IsTextVisible(Tag tag); - - protected bool IsTagVisible(Tag tag, GameObject? gameObject) - { - bool isVisibleForActivity = ActivityContextHelper.GetIsVisible(ActivityContextManager.CurrentActivityContext.ActivityType, - tag.IsVisibleInPveDuties.InheritedValue ?? false, - tag.IsVisibleInPvpDuties.InheritedValue ?? false, - tag.IsVisibleInOverworld.InheritedValue ?? false); - - if (!isVisibleForActivity) + if (!isVisibleForPlayer) { return false; } - - if (gameObject is PlayerCharacter playerCharacter) - { - bool isVisibleForPlayer = PlayerContextHelper.GetIsVisible(playerCharacter, - tag.IsVisibleForSelf.InheritedValue ?? false, - tag.IsVisibleForFriendPlayers.InheritedValue ?? false, - tag.IsVisibleForPartyPlayers.InheritedValue ?? false, - tag.IsVisibleForAlliancePlayers.InheritedValue ?? false, - tag.IsVisibleForEnemyPlayers.InheritedValue ?? false, - tag.IsVisibleForOtherPlayers.InheritedValue ?? false); - - if (!isVisibleForPlayer) - { - return false; - } - } - - return true; } - /// - /// Gets the payloads for the given tag and game object depending on visibility conditions. - /// - /// The game object to get payloads for. - /// The tag config to get payloads for. - /// A list of payloads for the given tag. - protected Payload[] GetPayloads(Tag tag, GameObject? gameObject) - { - if (!IsTagVisible(tag, gameObject)) - { - return Array.Empty(); - } + return true; + } - return CreatePayloads(tag); + /// + /// Gets the payloads for the given tag and game object depending on visibility conditions. + /// + /// The game object to get payloads for. + /// The tag config to get payloads for. + /// A list of payloads for the given tag. + protected Payload[] GetPayloads(Tag tag, GameObject? gameObject) + { + if (!IsTagVisible(tag, gameObject)) + { + return Array.Empty(); } - /// - /// Creates payloads for the given tag. - /// - /// The tag to create payloads for. - /// The payloads for the given tag. - private Payload[] CreatePayloads(Tag tag) + return CreatePayloads(tag); + } + + /// + /// Creates payloads for the given tag. + /// + /// The tag to create payloads for. + /// The payloads for the given tag. + private Payload[] CreatePayloads(Tag tag) + { + List newPayloads = []; + + BitmapFontIcon? icon = null; + if (IsIconVisible(tag)) { - List newPayloads = new List(); - - BitmapFontIcon? icon = null; - if (IsIconVisible(tag)) - { - icon = tag.Icon.InheritedValue; - } - - if (icon != null && icon.Value != BitmapFontIcon.None) - { - newPayloads.Add(new IconPayload(icon.Value)); - } - - string? text = null; - if (IsTextVisible(tag)) - { - text = tag.Text.InheritedValue; - } - - if (!string.IsNullOrWhiteSpace(text)) - { - if (tag.IsTextItalic.InheritedValue != null && tag.IsTextItalic.InheritedValue.Value) - { - newPayloads.Add(new EmphasisItalicPayload(true)); - } - - if (tag.TextGlowColor.InheritedValue != null) - { - newPayloads.Add(new UIGlowPayload(tag.TextGlowColor.InheritedValue.Value)); - } - - if (tag.TextColor.InheritedValue != null) - { - newPayloads.Add(new UIForegroundPayload(tag.TextColor.InheritedValue.Value)); - } - - newPayloads.Add(new TextPayload(text)); - - if (tag.TextColor.InheritedValue != null) - { - newPayloads.Add(new UIForegroundPayload(0)); - } - - if (tag.TextGlowColor.InheritedValue != null) - { - newPayloads.Add(new UIGlowPayload(0)); - } - - if (tag.IsTextItalic.InheritedValue != null && tag.IsTextItalic.InheritedValue.Value) - { - newPayloads.Add(new EmphasisItalicPayload(false)); - } - } - - return newPayloads.ToArray(); + icon = tag.Icon.InheritedValue; } - protected static string BuildPlayername(string name) + if (icon != null && icon.Value != BitmapFontIcon.None) { - var logNameType = GameConfigHelper.Instance.GetLogNameType(); - var result = string.Empty; - - if (logNameType != null && !string.IsNullOrEmpty(name)) - { - var nameSplitted = name.Split(' '); - - if (nameSplitted.Length > 1) - { - var firstName = nameSplitted[0]; - var lastName = nameSplitted[1]; - - switch (logNameType) - { - case LogNameType.FullName: - result = $"{firstName} {lastName}"; - break; - case LogNameType.LastNameShorted: - result = $"{firstName} {lastName[..1]}."; - break; - case LogNameType.FirstNameShorted: - result = $"{firstName[..1]}. {lastName}"; - break; - case LogNameType.Initials: - result = $"{firstName[..1]}. {lastName[..1]}."; - break; - } - } - } - - if (string.IsNullOrEmpty(result)) - result = name; - - return result; + newPayloads.Add(new IconPayload(icon.Value)); } - /// - /// Adds the given payload changes to the dictionary. - /// - /// The position to add changes to. - /// The payloads to add. - /// The dictionary to add the changes to. - protected void AddPayloadChanges(StringPosition tagPosition, IEnumerable payloads, StringChanges stringChanges, bool forceUsingSingleAnchorPayload) + string? text = null; + if (IsTextVisible(tag)) { - if (payloads != null && payloads.Any() && stringChanges != null) + text = tag.Text.InheritedValue; + } + + if (!string.IsNullOrWhiteSpace(text)) + { + if (tag.IsTextItalic.InheritedValue != null && tag.IsTextItalic.InheritedValue.Value) { - var changes = stringChanges.GetChange(tagPosition); - changes.Payloads.AddRange(payloads); - changes.ForceUsingSingleAnchorPayload = forceUsingSingleAnchorPayload; + newPayloads.Add(new EmphasisItalicPayload(true)); + } + + if (tag.TextGlowColor.InheritedValue != null) + { + newPayloads.Add(new UIGlowPayload(tag.TextGlowColor.InheritedValue.Value)); + } + + if (tag.TextColor.InheritedValue != null) + { + newPayloads.Add(new UIForegroundPayload(tag.TextColor.InheritedValue.Value)); + } + + newPayloads.Add(new TextPayload(text)); + + if (tag.TextColor.InheritedValue != null) + { + newPayloads.Add(new UIForegroundPayload(0)); + } + + if (tag.TextGlowColor.InheritedValue != null) + { + newPayloads.Add(new UIGlowPayload(0)); + } + + if (tag.IsTextItalic.InheritedValue != null && tag.IsTextItalic.InheritedValue.Value) + { + newPayloads.Add(new EmphasisItalicPayload(false)); } } - /// - /// Applies changes to the given string. - /// - /// 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, StringChanges stringChanges, List anchorPayloads = null, Payload anchorReplacePayload = null) + return newPayloads.ToArray(); + } + + protected static string BuildPlayername(string name) + { + var logNameType = GameConfigHelper.Instance.GetLogNameType(); + var result = string.Empty; + + if (logNameType != null && !string.IsNullOrEmpty(name)) { - var props = new StringChangesProps + var nameSplitted = name.Split(' '); + + if (nameSplitted.Length > 1) { - Destination = seString, - AnchorPayload = anchorReplacePayload - }; + var firstName = nameSplitted[0]; + var lastName = nameSplitted[1]; - props.AnchorPayloads = anchorPayloads; - props.StringChanges = stringChanges; - - StringUpdateFactory.ApplyStringChanges(props); - } - - protected void ApplyTextFormatting(GameObject gameObject, Tag tag, SeString[] destStrings, InheritableValue[] textColorApplied, List preferedPayloads, ushort? overwriteTextColor = null) - { - if (IsTagVisible(tag, gameObject)) - { - for (int i = 0; i < destStrings.Length; i++) + switch (logNameType) { - var destString = destStrings[i]; - var isTextColorApplied = textColorApplied[i]; - applyTextColor(destString, isTextColorApplied, tag.TextColor); - //applyTextGlowColor(destString, isTextColorApplied, tag.TextGlowColor); - //applyTextItalicColor(destString, tag.IsTextItalic); // Disabled, because that is needed only for a few parts somewhere else. + case LogNameType.FullName: + result = $"{firstName} {lastName}"; + break; + case LogNameType.LastNameShorted: + result = $"{firstName} {lastName[..1]}."; + break; + case LogNameType.FirstNameShorted: + result = $"{firstName[..1]}. {lastName}"; + break; + case LogNameType.Initials: + result = $"{firstName[..1]}. {lastName[..1]}."; + break; } } + } - void applyTextColor(SeString destPayload, InheritableValue enableFlag, InheritableValue colorValue) + if (string.IsNullOrEmpty(result)) + result = name; + + return result; + } + + /// + /// Adds the given payload changes to the dictionary. + /// + /// The position to add changes to. + /// The payloads to add. + /// The dictionary to add the changes to. + protected void AddPayloadChanges(StringPosition tagPosition, IEnumerable payloads, StringChanges stringChanges, bool forceUsingSingleAnchorPayload) + { + if (payloads != null && payloads.Any() && stringChanges != null) + { + var changes = stringChanges.GetChange(tagPosition); + changes.Payloads.AddRange(payloads); + changes.ForceUsingSingleAnchorPayload = forceUsingSingleAnchorPayload; + } + } + + /// + /// Applies changes to the given string. + /// + /// 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, StringChanges stringChanges, List anchorPayloads = null, Payload anchorReplacePayload = null) + { + var props = new StringChangesProps + { + Destination = seString, + AnchorPayload = anchorReplacePayload, + AnchorPayloads = anchorPayloads, + StringChanges = stringChanges + }; + + StringUpdateFactory.ApplyStringChanges(props); + } + + protected void ApplyTextFormatting(GameObject gameObject, Tag tag, SeString[] destStrings, InheritableValue[] textColorApplied, List preferedPayloads, ushort? overwriteTextColor = null) + { + if (IsTagVisible(tag, gameObject)) + { + for (int i = 0; i < destStrings.Length; i++) { - var colorToUse = overwriteTextColor ?? colorValue?.InheritedValue; - if (shouldApplyFormattingPayloads(destPayload) - && enableFlag.InheritedValue != null - && enableFlag.InheritedValue.Value - && colorToUse != null) - applyTextFormattingPayloads(destPayload, new UIForegroundPayload(colorToUse.Value), new UIForegroundPayload(0)); + var destString = destStrings[i]; + var isTextColorApplied = textColorApplied[i]; + applyTextColor(destString, isTextColorApplied, tag.TextColor); + //applyTextGlowColor(destString, isTextColorApplied, tag.TextGlowColor); + //applyTextItalicColor(destString, tag.IsTextItalic); // Disabled, because that is needed only for a few parts somewhere else. } + } - //void applyTextGlowColor(SeString destPayload, InheritableValue enableFlag, InheritableValue colorValue) - //{ - // if (shouldApplyFormattingPayloads(destPayload) - // && enableFlag.InheritedValue != null - // && enableFlag.InheritedValue.Value - // && colorValue.InheritedValue != null) - // applyTextFormattingPayloads(destPayload, new UIGlowPayload(colorValue.InheritedValue.Value), new UIGlowPayload(0)); - //} + void applyTextColor(SeString destPayload, InheritableValue enableFlag, InheritableValue colorValue) + { + var colorToUse = overwriteTextColor ?? colorValue?.InheritedValue; + if (shouldApplyFormattingPayloads(destPayload) + && enableFlag.InheritedValue != null + && enableFlag.InheritedValue.Value + && colorToUse != null) + applyTextFormattingPayloads(destPayload, new UIForegroundPayload(colorToUse.Value), new UIForegroundPayload(0)); + } - //void applyTextItalicColor(SeString destPayload, InheritableValue italicValue) - //{ - // if (shouldApplyFormattingPayloads(destPayload) - // && italicValue.InheritedValue != null - // && italicValue.InheritedValue.Value) - // applyTextFormattingPayloads(destPayload, new EmphasisItalicPayload(true), new EmphasisItalicPayload(false)); - //} + //void applyTextGlowColor(SeString destPayload, InheritableValue enableFlag, InheritableValue colorValue) + //{ + // if (shouldApplyFormattingPayloads(destPayload) + // && enableFlag.InheritedValue != null + // && enableFlag.InheritedValue.Value + // && colorValue.InheritedValue != null) + // applyTextFormattingPayloads(destPayload, new UIGlowPayload(colorValue.InheritedValue.Value), new UIGlowPayload(0)); + //} - bool shouldApplyFormattingPayloads(SeString destPayload) - => destPayload.Payloads.Any(payload => payload is TextPayload || payload is PlayerPayload); + //void applyTextItalicColor(SeString destPayload, InheritableValue italicValue) + //{ + // if (shouldApplyFormattingPayloads(destPayload) + // && italicValue.InheritedValue != null + // && italicValue.InheritedValue.Value) + // applyTextFormattingPayloads(destPayload, new EmphasisItalicPayload(true), new EmphasisItalicPayload(false)); + //} - void applyTextFormattingPayloads(SeString destPayload, Payload startPayload, Payload endPayload) - { - if (preferedPayloads == null || !preferedPayloads.Any()) - applyTextFormattingPayloadToStartAndEnd(destPayload, startPayload, endPayload); - else - applyTextFormattingPayloadsToSpecificPosition(destPayload, startPayload, endPayload, preferedPayloads); - } - - void applyTextFormattingPayloadToStartAndEnd(SeString destPayload, Payload startPayload, Payload endPayload) - { - destPayload.Payloads.Insert(0, startPayload); - destPayload.Payloads.Add(endPayload); - } + bool shouldApplyFormattingPayloads(SeString destPayload) + => destPayload.Payloads.Any(payload => payload is TextPayload || payload is PlayerPayload); - void applyTextFormattingPayloadsToSpecificPosition(SeString destPayload, Payload startPayload, Payload endPayload, List preferedPayload) - { - int payloadStartIndex = destPayload.Payloads.IndexOf(preferedPayloads.First()); - destPayload.Payloads.Insert(payloadStartIndex, startPayload); + void applyTextFormattingPayloads(SeString destPayload, Payload startPayload, Payload endPayload) + { + if (preferedPayloads == null || !preferedPayloads.Any()) + applyTextFormattingPayloadToStartAndEnd(destPayload, startPayload, endPayload); + else + applyTextFormattingPayloadsToSpecificPosition(destPayload, startPayload, endPayload, preferedPayloads); + } - int payloadEndIndex = destPayload.Payloads.IndexOf(preferedPayloads.Last()); - destPayload.Payloads.Insert(payloadEndIndex + 1, endPayload); - } + void applyTextFormattingPayloadToStartAndEnd(SeString destPayload, Payload startPayload, Payload endPayload) + { + destPayload.Payloads.Insert(0, startPayload); + destPayload.Payloads.Add(endPayload); + } + + void applyTextFormattingPayloadsToSpecificPosition(SeString destPayload, Payload startPayload, Payload endPayload, List preferedPayload) + { + int payloadStartIndex = destPayload.Payloads.IndexOf(preferedPayloads.First()); + destPayload.Payloads.Insert(payloadStartIndex, startPayload); + + int payloadEndIndex = destPayload.Payloads.IndexOf(preferedPayloads.Last()); + destPayload.Payloads.Insert(payloadEndIndex + 1, endPayload); } } } diff --git a/PlayerTags/GameInterface/GameInterfaceHelper.cs b/PlayerTags/GameInterface/GameInterfaceHelper.cs index fe4d52d..2167ea9 100644 --- a/PlayerTags/GameInterface/GameInterfaceHelper.cs +++ b/PlayerTags/GameInterface/GameInterfaceHelper.cs @@ -4,142 +4,141 @@ using System; using System.Runtime.InteropServices; using System.Text; -namespace PlayerTags.GameInterface +namespace PlayerTags.GameInterface; + +public static class GameInterfaceHelper { - public static class GameInterfaceHelper + public static SeString ReadSeString(IntPtr ptr) { - public static SeString ReadSeString(IntPtr ptr) + if (ptr == IntPtr.Zero) { - 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) + if (TryReadStringBytes(ptr, out var bytes) && bytes != null) { - seString = null; - if (ptr == IntPtr.Zero) - { - return false; - } + return SeString.Parse(bytes); + } - if (TryReadStringBytes(ptr, out var bytes) && bytes != null) - { - seString = SeString.Parse(bytes); - return true; - } + return new SeString(); + } + public static bool TryReadSeString(IntPtr ptr, out SeString? seString) + { + seString = null; + if (ptr == IntPtr.Zero) + { return false; } - public static string? ReadString(IntPtr ptr) + if (TryReadStringBytes(ptr, out var bytes) && bytes != null) { - 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); - + seString = SeString.Parse(bytes); 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 false; + } - return pointer; + public static string? ReadString(IntPtr ptr) + { + if (ptr == IntPtr.Zero) + { + return null; } - public static IntPtr PluginAllocate(SeString seString) + if (TryReadStringBytes(ptr, out var bytes) && bytes != null) { - return PluginAllocate(seString.Encode()); + return Encoding.UTF8.GetString(bytes); } - public static void PluginFree(ref IntPtr ptr) + return null; + } + + public static bool TryReadString(IntPtr ptr, out string? str) + { + str = null; + if (ptr == IntPtr.Zero) { - Marshal.FreeHGlobal(ptr); - ptr = IntPtr.Zero; + return false; } - public static byte[] NullTerminate(this byte[] bytes) + if (TryReadStringBytes(ptr, out var bytes) && bytes != null) { - 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; + str = Encoding.UTF8.GetString(bytes); + return true; } - public static unsafe IntPtr GameUIAllocate(ulong size) + return false; + } + + public static bool TryReadStringBytes(IntPtr ptr, out byte[]? bytes) + { + bytes = null; + if (ptr == IntPtr.Zero) { - return (IntPtr)IMemorySpace.GetUISpace()->Malloc(size, 0); + return false; } - public static unsafe void GameFree(ref IntPtr ptr, ulong size) + var size = 0; + while (Marshal.ReadByte(ptr, size) != 0) { - if (ptr == IntPtr.Zero) - { - return; - } - - IMemorySpace.Free((void*)ptr, size); - ptr = IntPtr.Zero; + 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(ref IntPtr ptr) + { + Marshal.FreeHGlobal(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/PlayerTags/GameInterface/Nameplates/Nameplate.cs b/PlayerTags/GameInterface/Nameplates/Nameplate.cs index 8e90611..e681a6a 100644 --- a/PlayerTags/GameInterface/Nameplates/Nameplate.cs +++ b/PlayerTags/GameInterface/Nameplates/Nameplate.cs @@ -1,65 +1,55 @@ -using Dalamud.Game.ClientState.Objects.SubKinds; -using Dalamud.Game.ClientState.Objects.Types; -using Dalamud.Hooking; -using Dalamud.Logging; -using Dalamud.Utility.Signatures; -using FFXIVClientStructs.FFXIV.Client.UI; -using Pilz.Dalamud.Nameplates; +using Pilz.Dalamud.Nameplates; using System; -using System.Diagnostics.Tracing; -using System.Linq; -using System.Runtime.InteropServices; -namespace PlayerTags.GameInterface.Nameplates +namespace PlayerTags.GameInterface.Nameplates; + +/// +/// Provides an interface to modify nameplates. +/// +public class Nameplate : IDisposable { + public NameplateManager NameplateManager { get; init; } + /// - /// Provides an interface to modify nameplates. + /// Occurs when a player nameplate is updated by the game. /// - public class Nameplate : IDisposable + public event PlayerNameplateUpdatedDelegate? PlayerNameplateUpdated; + + /// + /// Whether the required hooks are in place and this instance is valid. + /// + public bool IsValid { - public NameplateManager NameplateManager { get; init; } + get => NameplateManager != null && NameplateManager.IsValid; + } - /// - /// Occurs when a player nameplate is updated by the game. - /// - public event PlayerNameplateUpdatedDelegate? PlayerNameplateUpdated; + public Nameplate() + { + NameplateManager = new(); + NameplateManager.Hooks.AddonNamePlate_SetPlayerNameManaged += Hooks_AddonNamePlate_SetPlayerNameManaged; + } - /// - /// Whether the required hooks are in place and this instance is valid. - /// - public bool IsValid + public void Dispose() + { + NameplateManager.Hooks.AddonNamePlate_SetPlayerNameManaged -= Hooks_AddonNamePlate_SetPlayerNameManaged; + NameplateManager.Dispose(); + } + + private void Hooks_AddonNamePlate_SetPlayerNameManaged(Pilz.Dalamud.Nameplates.EventArgs.AddonNamePlate_SetPlayerNameManagedEventArgs eventArgs) + { + try { - get => NameplateManager != null && NameplateManager.IsValid; - } + PlayerCharacter? playerCharacter = NameplateManager.GetNameplateGameObject(eventArgs.SafeNameplateObject); - public Nameplate() - { - NameplateManager = new(); - NameplateManager.Hooks.AddonNamePlate_SetPlayerNameManaged += Hooks_AddonNamePlate_SetPlayerNameManaged; - } - - public void Dispose() - { - NameplateManager.Hooks.AddonNamePlate_SetPlayerNameManaged -= Hooks_AddonNamePlate_SetPlayerNameManaged; - NameplateManager.Dispose(); - } - - private void Hooks_AddonNamePlate_SetPlayerNameManaged(Pilz.Dalamud.Nameplates.EventArgs.AddonNamePlate_SetPlayerNameManagedEventArgs eventArgs) - { - try + if (playerCharacter != null) { - PlayerCharacter? playerCharacter = NameplateManager.GetNameplateGameObject(eventArgs.SafeNameplateObject); - - if (playerCharacter != null) - { - var playerNameplateUpdatedArgs = new PlayerNameplateUpdatedArgs(playerCharacter, eventArgs); - PlayerNameplateUpdated?.Invoke(playerNameplateUpdatedArgs); - } - } - catch (Exception ex) - { - PluginServices.PluginLog.Error(ex, $"SetPlayerNameplateDetour"); + var playerNameplateUpdatedArgs = new PlayerNameplateUpdatedArgs(playerCharacter, eventArgs); + PlayerNameplateUpdated?.Invoke(playerNameplateUpdatedArgs); } } + catch (Exception ex) + { + PluginServices.PluginLog.Error(ex, $"SetPlayerNameplateDetour"); + } } } diff --git a/PlayerTags/GameInterface/Nameplates/PlayerNameplateUpdatedArgs.cs b/PlayerTags/GameInterface/Nameplates/PlayerNameplateUpdatedArgs.cs index 8621f90..1bc4170 100644 --- a/PlayerTags/GameInterface/Nameplates/PlayerNameplateUpdatedArgs.cs +++ b/PlayerTags/GameInterface/Nameplates/PlayerNameplateUpdatedArgs.cs @@ -1,52 +1,50 @@ -using Dalamud.Game.ClientState.Objects.SubKinds; -using Dalamud.Game.Text.SeStringHandling; +using Dalamud.Game.Text.SeStringHandling; using Pilz.Dalamud.Nameplates.EventArgs; -namespace PlayerTags.GameInterface.Nameplates +namespace PlayerTags.GameInterface.Nameplates; + +public class PlayerNameplateUpdatedArgs { - public class PlayerNameplateUpdatedArgs + private readonly AddonNamePlate_SetPlayerNameManagedEventArgs eventArgs; + + public PlayerCharacter PlayerCharacter { get; } + + public SeString Name { - private readonly AddonNamePlate_SetPlayerNameManagedEventArgs eventArgs; + get => eventArgs.Name; + } - public PlayerCharacter PlayerCharacter { get; } + public SeString Title + { + get => eventArgs.Title; + } - public SeString Name - { - get => eventArgs.Name; - } + public SeString FreeCompany + { + get => eventArgs.FreeCompany; + } - public SeString Title - { - get => eventArgs.Title; - } + public bool IsTitleVisible + { + get => eventArgs.IsTitleVisible; + set => eventArgs.IsTitleVisible = value; + } - public SeString FreeCompany - { - get => eventArgs.FreeCompany; - } + public bool IsTitleAboveName + { + get => eventArgs.IsTitleAboveName; + set => eventArgs.IsTitleAboveName = value; + } - public bool IsTitleVisible - { - get => eventArgs.IsTitleVisible; - set => eventArgs.IsTitleVisible = value; - } + public int IconId + { + get => eventArgs.IconID; + set => eventArgs.IconID = value; + } - public bool IsTitleAboveName - { - get => eventArgs.IsTitleAboveName; - set => eventArgs.IsTitleAboveName = value; - } - - public int IconId - { - get => eventArgs.IconID; - set => eventArgs.IconID = value; - } - - public PlayerNameplateUpdatedArgs(PlayerCharacter playerCharacter, AddonNamePlate_SetPlayerNameManagedEventArgs eventArgs) - { - PlayerCharacter = playerCharacter; - this.eventArgs = eventArgs; - } + public PlayerNameplateUpdatedArgs(PlayerCharacter playerCharacter, AddonNamePlate_SetPlayerNameManagedEventArgs eventArgs) + { + PlayerCharacter = playerCharacter; + this.eventArgs = eventArgs; } } diff --git a/PlayerTags/GameInterface/Nameplates/PlayerNameplateUpdatedDelegate.cs b/PlayerTags/GameInterface/Nameplates/PlayerNameplateUpdatedDelegate.cs index 0e38218..85630cb 100644 --- a/PlayerTags/GameInterface/Nameplates/PlayerNameplateUpdatedDelegate.cs +++ b/PlayerTags/GameInterface/Nameplates/PlayerNameplateUpdatedDelegate.cs @@ -1,4 +1,3 @@ -namespace PlayerTags.GameInterface.Nameplates -{ - public delegate void PlayerNameplateUpdatedDelegate(PlayerNameplateUpdatedArgs args); -} +namespace PlayerTags.GameInterface.Nameplates; + +public delegate void PlayerNameplateUpdatedDelegate(PlayerNameplateUpdatedArgs args); diff --git a/PlayerTags/Inheritables/IInheritable.cs b/PlayerTags/Inheritables/IInheritable.cs index f20611a..eb71c74 100644 --- a/PlayerTags/Inheritables/IInheritable.cs +++ b/PlayerTags/Inheritables/IInheritable.cs @@ -1,13 +1,12 @@ -namespace PlayerTags.Inheritables +namespace PlayerTags.Inheritables; + +public interface IInheritable { - public interface IInheritable - { - public IInheritable? Parent { get; set; } + public IInheritable? Parent { get; set; } - public InheritableBehavior Behavior { get; set; } + public InheritableBehavior Behavior { get; set; } - public abstract void SetData(InheritableData inheritableData); + public abstract void SetData(InheritableData inheritableData); - public abstract InheritableData GetData(); - } + public abstract InheritableData GetData(); } diff --git a/PlayerTags/Inheritables/InheritableBehavior.cs b/PlayerTags/Inheritables/InheritableBehavior.cs index 1691864..9843b96 100644 --- a/PlayerTags/Inheritables/InheritableBehavior.cs +++ b/PlayerTags/Inheritables/InheritableBehavior.cs @@ -1,12 +1,8 @@ -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; +namespace PlayerTags.Inheritables; -namespace PlayerTags.Inheritables +public enum InheritableBehavior { - public enum InheritableBehavior - { - Inherit, - Enabled, - Disabled - } + Inherit, + Enabled, + Disabled } diff --git a/PlayerTags/Inheritables/InheritableData.cs b/PlayerTags/Inheritables/InheritableData.cs index 9b95b85..b264e43 100644 --- a/PlayerTags/Inheritables/InheritableData.cs +++ b/PlayerTags/Inheritables/InheritableData.cs @@ -2,16 +2,15 @@ using Newtonsoft.Json.Converters; using System; -namespace PlayerTags.Inheritables -{ - [Serializable] - public struct InheritableData - { - [JsonConverter(typeof(StringEnumConverter))] - [JsonProperty("Behavior")] - public InheritableBehavior Behavior; +namespace PlayerTags.Inheritables; - [JsonProperty("Value")] - public object Value; - } +[Serializable] +public struct InheritableData +{ + [JsonConverter(typeof(StringEnumConverter))] + [JsonProperty("Behavior")] + public InheritableBehavior Behavior; + + [JsonProperty("Value")] + public object Value; } diff --git a/PlayerTags/Inheritables/InheritableReference.cs b/PlayerTags/Inheritables/InheritableReference.cs index cabcf62..cbc59ae 100644 --- a/PlayerTags/Inheritables/InheritableReference.cs +++ b/PlayerTags/Inheritables/InheritableReference.cs @@ -1,66 +1,63 @@ using Newtonsoft.Json; -using Newtonsoft.Json.Converters; -using System.Collections.Generic; -namespace PlayerTags.Inheritables +namespace PlayerTags.Inheritables; + +public class InheritableReference : IInheritable + where T : class { - public class InheritableReference : IInheritable - where T : class + public IInheritable? Parent { get; set; } + + public InheritableBehavior Behavior { get; set; } + + [JsonProperty] + public T Value; + + [JsonIgnore] + public T? InheritedValue { - public IInheritable? Parent { get; set; } - - public InheritableBehavior Behavior { get; set; } - - [JsonProperty] - public T Value; - - [JsonIgnore] - public T? InheritedValue + get { - get + IInheritable? current = this; + while (current != null) { - IInheritable? current = this; - while (current != null) + if (current.Behavior == InheritableBehavior.Enabled && current is InheritableReference currentOfSameType) { - if (current.Behavior == InheritableBehavior.Enabled && current is InheritableReference currentOfSameType) - { - return currentOfSameType.Value; - } - else if (current.Behavior == InheritableBehavior.Disabled) - { - return default; - } - - current = current.Parent; + return currentOfSameType.Value; + } + else if (current.Behavior == InheritableBehavior.Disabled) + { + return default; } - return default; + current = current.Parent; } - } - public static implicit operator InheritableReference(T value) => new InheritableReference(value) - { - Behavior = InheritableBehavior.Enabled - }; - - public InheritableReference(T value) - { - Value = value; - } - - public void SetData(InheritableData inheritableData) - { - Behavior = inheritableData.Behavior; - Value = (T)inheritableData.Value; - } - - public InheritableData GetData() - { - return new InheritableData - { - Behavior = Behavior, - Value = Value - }; + return default; } } + + public static implicit operator InheritableReference(T value) => new(value) + { + Behavior = InheritableBehavior.Enabled + }; + + public InheritableReference(T value) + { + Value = value; + } + + public void SetData(InheritableData inheritableData) + { + Behavior = inheritableData.Behavior; + Value = (T)inheritableData.Value; + } + + public InheritableData GetData() + { + return new InheritableData + { + Behavior = Behavior, + Value = Value + }; + } } diff --git a/PlayerTags/Inheritables/InheritableValue.cs b/PlayerTags/Inheritables/InheritableValue.cs index 4ac7422..4292c79 100644 --- a/PlayerTags/Inheritables/InheritableValue.cs +++ b/PlayerTags/Inheritables/InheritableValue.cs @@ -1,97 +1,95 @@ -using Dalamud.Logging; -using Newtonsoft.Json; +using Newtonsoft.Json; using System; -namespace PlayerTags.Inheritables +namespace PlayerTags.Inheritables; + +public class InheritableValue : IInheritable + where T : struct { - public class InheritableValue : IInheritable - where T : struct + public IInheritable? Parent { get; set; } + + public InheritableBehavior Behavior { get; set; } + + [JsonProperty] + public T Value; + + [JsonIgnore] + public T? InheritedValue { - public IInheritable? Parent { get; set; } - - public InheritableBehavior Behavior { get; set; } - - [JsonProperty] - public T Value; - - [JsonIgnore] - public T? InheritedValue + get { - get + IInheritable? current = this; + while (current != null) { - IInheritable? current = this; - while (current != null) + if (current.Behavior == InheritableBehavior.Enabled && current is InheritableValue currentOfSameType) { - if (current.Behavior == InheritableBehavior.Enabled && current is InheritableValue currentOfSameType) - { - return currentOfSameType.Value; - } - else if (current.Behavior == InheritableBehavior.Disabled) - { - return default; - } - - current = current.Parent; + return currentOfSameType.Value; + } + else if (current.Behavior == InheritableBehavior.Disabled) + { + return default; } - return default; + current = current.Parent; } + + return default; } + } - public static implicit operator InheritableValue(T value) => new InheritableValue(value) + public static implicit operator InheritableValue(T value) => new(value) + { + Behavior = InheritableBehavior.Enabled + }; + + public InheritableValue(T value) + { + Value = value; + } + + public void SetData(InheritableData inheritableData) + { + Behavior = inheritableData.Behavior; + + try { - Behavior = InheritableBehavior.Enabled - }; - - public InheritableValue(T value) - { - Value = value; - } - - public void SetData(InheritableData inheritableData) - { - Behavior = inheritableData.Behavior; - - try + if (typeof(T).IsEnum && inheritableData.Value != null) { - if (typeof(T).IsEnum && inheritableData.Value != null) + if (inheritableData.Value is string stringValue) { - if (inheritableData.Value is string stringValue) - { - Value = (T)Enum.Parse(typeof(T), stringValue); - } - else - { - Value = (T)Enum.ToObject(typeof(T), inheritableData.Value); - } - } - else if (inheritableData.Value == null) - { - // This should never happen - PluginServices.PluginLog.Error($"Expected value of type {Value.GetType()} but received null"); - } - else if (typeof(T) == typeof(Guid) && inheritableData.Value is string strValue) - { - Value = (T)(object)Guid.Parse(strValue); + Value = (T)Enum.Parse(typeof(T), stringValue); } else { - Value = (T)Convert.ChangeType(inheritableData.Value, typeof(T)); + Value = (T)Enum.ToObject(typeof(T), inheritableData.Value); } } - catch (Exception ex) + else if (inheritableData.Value == null) { - PluginServices.PluginLog.Error(ex, $"Failed to convert {inheritableData.Value.GetType()} value '{inheritableData.Value}' to {Value.GetType()}"); + // This should never happen + PluginServices.PluginLog.Error($"Expected value of type {Value.GetType()} but received null"); + } + else if (typeof(T) == typeof(Guid) && inheritableData.Value is string strValue) + { + Value = (T)(object)Guid.Parse(strValue); + } + else + { + Value = (T)Convert.ChangeType(inheritableData.Value, typeof(T)); } } - - public InheritableData GetData() + catch (Exception ex) { - return new InheritableData - { - Behavior = Behavior, - Value = Value - }; + PluginServices.PluginLog.Error(ex, $"Failed to convert {inheritableData.Value.GetType()} value '{inheritableData.Value}' to {Value.GetType()}"); } } + + public InheritableData GetData() + { + return new InheritableData + { + Behavior = Behavior, + Value = Value + }; + } } diff --git a/PlayerTags/Localizer.cs b/PlayerTags/Localizer.cs index 18c8a51..14b8688 100644 --- a/PlayerTags/Localizer.cs +++ b/PlayerTags/Localizer.cs @@ -1,62 +1,58 @@ -using Dalamud.Logging; -using Dalamud.Plugin; -using PlayerTags.Resources; +using PlayerTags.Resources; using System; -using System.ComponentModel; using System.Globalization; -namespace PlayerTags +namespace PlayerTags; + +public static class Localizer { - public static class Localizer + public static void SetLanguage(string langCode) { - public static void SetLanguage(string langCode) + SetLanguage(new CultureInfo(langCode)); + } + + public static void SetLanguage(CultureInfo cultureInfo) + { + Strings.Culture = cultureInfo; + } + + public static string GetName(TEnum value) + { + return $"{typeof(TEnum).Name}_{value}"; + } + + public static string GetString(bool isDescription) + where TEnum : Enum + { + return GetString(typeof(TEnum).Name, isDescription); + } + + public static string GetString(TEnum value, bool isDescription) + where TEnum : Enum + { + return GetString(GetName(value), isDescription); + } + + public static string GetString(string localizedStringName, bool isDescription) + { + string localizedStringId = $"Loc_{localizedStringName}"; + + if (isDescription) { - SetLanguage(new CultureInfo(langCode)); + localizedStringId += "_Description"; } - public static void SetLanguage(CultureInfo cultureInfo) - { - Strings.Culture = cultureInfo; - } + return GetString(localizedStringId); + } - public static string GetName(TEnum value) - { - return $"{typeof(TEnum).Name}_{value}"; - } + public static string GetString(string localizedStringId) + { + string? value = Strings.ResourceManager.GetString(localizedStringId, Strings.Culture); - public static string GetString(bool isDescription) - where TEnum : Enum - { - return GetString(typeof(TEnum).Name, isDescription); - } + if (value != null) + return value; - public static string GetString(TEnum value, bool isDescription) - where TEnum : Enum - { - return GetString(GetName(value), isDescription); - } - - public static string GetString(string localizedStringName, bool isDescription) - { - string localizedStringId = $"Loc_{localizedStringName}"; - - if (isDescription) - { - localizedStringId += "_Description"; - } - - return GetString(localizedStringId); - } - - public static string GetString(string localizedStringId) - { - string? value = Strings.ResourceManager.GetString(localizedStringId, Strings.Culture); - - if (value != null) - return value; - - PluginServices.PluginLog.Error($"Failed to get localized string for id {localizedStringId}"); - return localizedStringId; - } + PluginServices.PluginLog.Error($"Failed to get localized string for id {localizedStringId}"); + return localizedStringId; } } diff --git a/PlayerTags/MyPaths.cs b/PlayerTags/MyPaths.cs index b5b88a1..54540f9 100644 --- a/PlayerTags/MyPaths.cs +++ b/PlayerTags/MyPaths.cs @@ -1,33 +1,27 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.IO; -namespace PlayerTags +namespace PlayerTags; + +internal class MyPaths { - internal class MyPaths + private static string? _PluginDirectoryPath = null; + + public static string PluginDirectoryPath { - private static string? _PluginDirectoryPath = null; - - public static string PluginDirectoryPath + get { - get + if (_PluginDirectoryPath is null) { - if (_PluginDirectoryPath is null) - { - var path = Path.GetDirectoryName(PluginServices.DalamudPluginInterface.AssemblyLocation.FullName); - if (path is null) - _PluginDirectoryPath = string.Empty; - else - _PluginDirectoryPath = path; - } - return _PluginDirectoryPath; + var path = Path.GetDirectoryName(PluginServices.DalamudPluginInterface.AssemblyLocation.FullName); + if (path is null) + _PluginDirectoryPath = string.Empty; + else + _PluginDirectoryPath = path; } + return _PluginDirectoryPath; } - - public static string ResourcePath - => Path.Combine(PluginDirectoryPath, "Resources"); } + + public static string ResourcePath + => Path.Combine(PluginDirectoryPath, "Resources"); } diff --git a/PlayerTags/Plugin.cs b/PlayerTags/Plugin.cs index bcaaffc..c54b330 100644 --- a/PlayerTags/Plugin.cs +++ b/PlayerTags/Plugin.cs @@ -1,123 +1,113 @@ using Dalamud.Game.Command; -using Dalamud.Logging; using Dalamud.Plugin; -using Dalamud.Plugin.Internal; -using FFXIVClientStructs.FFXIV.Client.UI.Misc; -using Lumina.Excel.GeneratedSheets; using PlayerTags.Configuration; using PlayerTags.Data; using PlayerTags.Features; -using System; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Security.Cryptography; -namespace PlayerTags +namespace PlayerTags; + +public sealed class Plugin : IDalamudPlugin { - public sealed class Plugin : IDalamudPlugin + private const string c_CommandName = "/playertags"; + private const string c_SubCommandName_EnableGlobal = "enableglobal"; + private const string c_CommandArg_On = "on"; + private const string c_CommandArg_Off = "off"; + private const string c_CommandArg_toggle = "toggle"; + + private readonly PluginConfiguration pluginConfiguration = null; + private readonly PluginData pluginData = null; + private readonly PluginConfigurationUI pluginConfigurationUI = null; + + private readonly CustomTagsContextMenuFeature customTagsContextMenuFeature; + private readonly NameplateTagTargetFeature nameplatesTagTargetFeature; + private readonly ChatTagTargetFeature chatTagTargetFeature; + + public Plugin(IDalamudPluginInterface pluginInterface) { - private const string c_CommandName = "/playertags"; - private const string c_SubCommandName_EnableGlobal = "enableglobal"; - private const string c_CommandArg_On = "on"; - private const string c_CommandArg_Off = "off"; - private const string c_CommandArg_toggle = "toggle"; + PluginServices.Initialize(pluginInterface); + Pilz.Dalamud.PluginServices.Initialize(pluginInterface); - private readonly PluginConfiguration pluginConfiguration = null; - private readonly PluginData pluginData = null; - private readonly PluginConfigurationUI pluginConfigurationUI = null; + pluginConfiguration = PluginConfiguration.LoadPluginConfig() ?? new PluginConfiguration(); + pluginData = new PluginData(pluginConfiguration); + pluginConfigurationUI = new PluginConfigurationUI(pluginConfiguration, pluginData); - private readonly CustomTagsContextMenuFeature customTagsContextMenuFeature; - private readonly NameplateTagTargetFeature nameplatesTagTargetFeature; - private readonly ChatTagTargetFeature chatTagTargetFeature; + Localizer.SetLanguage(PluginServices.DalamudPluginInterface.UiLanguage); + PluginServices.DalamudPluginInterface.LanguageChanged += DalamudPluginInterface_LanguageChanged; - public Plugin(IDalamudPluginInterface pluginInterface) + PluginServices.DalamudPluginInterface.UiBuilder.Draw += UiBuilder_Draw; + PluginServices.DalamudPluginInterface.UiBuilder.OpenConfigUi += UiBuilder_OpenConfigUi; + PluginServices.CommandManager.AddHandler(c_CommandName, new CommandInfo(CommandManager_Handler) { - PluginServices.Initialize(pluginInterface); - Pilz.Dalamud.PluginServices.Initialize(pluginInterface); + HelpMessage = Resources.Strings.Loc_Command_playertags_v2 + }); + customTagsContextMenuFeature = new CustomTagsContextMenuFeature(pluginConfiguration, pluginData, pluginInterface); + nameplatesTagTargetFeature = new NameplateTagTargetFeature(pluginConfiguration, pluginData); + chatTagTargetFeature = new ChatTagTargetFeature(pluginConfiguration, pluginData); + } - pluginConfiguration = PluginConfiguration.LoadPluginConfig() ?? new PluginConfiguration(); - pluginData = new PluginData(pluginConfiguration); - pluginConfigurationUI = new PluginConfigurationUI(pluginConfiguration, pluginData); + public void Dispose() + { + chatTagTargetFeature.Dispose(); + nameplatesTagTargetFeature.Dispose(); + customTagsContextMenuFeature.Dispose(); + PluginServices.DalamudPluginInterface.LanguageChanged -= DalamudPluginInterface_LanguageChanged; + PluginServices.CommandManager.RemoveHandler(c_CommandName); + PluginServices.DalamudPluginInterface.UiBuilder.OpenConfigUi -= UiBuilder_OpenConfigUi; + PluginServices.DalamudPluginInterface.UiBuilder.Draw -= UiBuilder_Draw; + } - Localizer.SetLanguage(PluginServices.DalamudPluginInterface.UiLanguage); - PluginServices.DalamudPluginInterface.LanguageChanged += DalamudPluginInterface_LanguageChanged; + private void DalamudPluginInterface_LanguageChanged(string langCode) + { + Localizer.SetLanguage(langCode); + } - PluginServices.DalamudPluginInterface.UiBuilder.Draw += UiBuilder_Draw; - PluginServices.DalamudPluginInterface.UiBuilder.OpenConfigUi += UiBuilder_OpenConfigUi; - PluginServices.CommandManager.AddHandler(c_CommandName, new CommandInfo(CommandManager_Handler) - { - HelpMessage = Resources.Strings.Loc_Command_playertags_v2 - }); - customTagsContextMenuFeature = new CustomTagsContextMenuFeature(pluginConfiguration, pluginData, pluginInterface); - nameplatesTagTargetFeature = new NameplateTagTargetFeature(pluginConfiguration, pluginData); - chatTagTargetFeature = new ChatTagTargetFeature(pluginConfiguration, pluginData); - } - - public void Dispose() + private void CommandManager_Handler(string command, string arguments) + { + switch (command) { - chatTagTargetFeature.Dispose(); - nameplatesTagTargetFeature.Dispose(); - customTagsContextMenuFeature.Dispose(); - PluginServices.DalamudPluginInterface.LanguageChanged -= DalamudPluginInterface_LanguageChanged; - PluginServices.CommandManager.RemoveHandler(c_CommandName); - PluginServices.DalamudPluginInterface.UiBuilder.OpenConfigUi -= UiBuilder_OpenConfigUi; - PluginServices.DalamudPluginInterface.UiBuilder.Draw -= UiBuilder_Draw; - } - - private void DalamudPluginInterface_LanguageChanged(string langCode) - { - Localizer.SetLanguage(langCode); - } - - private void CommandManager_Handler(string command, string arguments) - { - switch (command) - { - case c_CommandName: - if (string.IsNullOrWhiteSpace(arguments)) - UiBuilder_OpenConfigUi(); - else + case c_CommandName: + if (string.IsNullOrWhiteSpace(arguments)) + UiBuilder_OpenConfigUi(); + else + { + var lowerArgs = arguments.ToLower().Split(' '); + if (lowerArgs.Length >= 1) { - var lowerArgs = arguments.ToLower().Split(' '); - if (lowerArgs.Length >= 1) + switch (lowerArgs[0]) { - switch (lowerArgs[0]) - { - case c_SubCommandName_EnableGlobal: - if (lowerArgs.Length >= 2) + case c_SubCommandName_EnableGlobal: + if (lowerArgs.Length >= 2) + { + switch (lowerArgs[0]) { - switch (lowerArgs[0]) - { - case c_CommandArg_On: - pluginConfiguration.EnabledGlobal = true; - break; - case c_CommandArg_Off: - pluginConfiguration.EnabledGlobal = false; - break; - case c_CommandArg_toggle: - pluginConfiguration.EnabledGlobal = !pluginConfiguration.EnabledGlobal; - break; - } + case c_CommandArg_On: + pluginConfiguration.EnabledGlobal = true; + break; + case c_CommandArg_Off: + pluginConfiguration.EnabledGlobal = false; + break; + case c_CommandArg_toggle: + pluginConfiguration.EnabledGlobal = !pluginConfiguration.EnabledGlobal; + break; } - break; - } + } + break; } } - break; - } - } - - private void UiBuilder_Draw() - { - if (pluginConfiguration.IsVisible) - pluginConfigurationUI.Draw(); - } - - private void UiBuilder_OpenConfigUi() - { - pluginConfiguration.IsVisible = true; - pluginConfiguration.Save(pluginData); + } + break; } } + + private void UiBuilder_Draw() + { + if (pluginConfiguration.IsVisible) + pluginConfigurationUI.Draw(); + } + + private void UiBuilder_OpenConfigUi() + { + pluginConfiguration.IsVisible = true; + pluginConfiguration.Save(pluginData); + } } diff --git a/PlayerTags/PluginServices.cs b/PlayerTags/PluginServices.cs index eabf001..1795005 100644 --- a/PlayerTags/PluginServices.cs +++ b/PlayerTags/PluginServices.cs @@ -1,34 +1,26 @@ -using Dalamud.Data; -using Dalamud.Game; -using Dalamud.Game.ClientState; -using Dalamud.Game.ClientState.Objects; -using Dalamud.Game.ClientState.Party; -using Dalamud.Game.Command; -using Dalamud.Game.Gui; -using Dalamud.IoC; +using Dalamud.IoC; using Dalamud.Plugin; using Dalamud.Plugin.Services; -namespace PlayerTags -{ - public class PluginServices - { - [PluginService] public static IDalamudPluginInterface DalamudPluginInterface { get; set; } = null!; - [PluginService] public static IPluginLog PluginLog { get; set; } = null; - [PluginService] public static IGameConfig GameConfig { get; set; } = null; - [PluginService] public static IChatGui ChatGui { get; set; } = null!; - [PluginService] public static IClientState ClientState { get; set; } = null!; - [PluginService] public static ICommandManager CommandManager { get; set; } = null!; - [PluginService] public static IDataManager DataManager { get; set; } = null!; - [PluginService] public static IFramework Framework { get; set; } = null!; - [PluginService] public static IGameGui GameGui { get; set; } = null!; - [PluginService] public static IObjectTable ObjectTable { get; set; } = null!; - [PluginService] public static IPartyList PartyList { get; set; } = null!; - [PluginService] public static IGameInteropProvider GameInteropProvider { get; set; } = null; +namespace PlayerTags; - public static void Initialize(IDalamudPluginInterface pluginInterface) - { - pluginInterface.Create(); - } +public class PluginServices +{ + [PluginService] public static IDalamudPluginInterface DalamudPluginInterface { get; set; } = null!; + [PluginService] public static IPluginLog PluginLog { get; set; } = null; + [PluginService] public static IGameConfig GameConfig { get; set; } = null; + [PluginService] public static IChatGui ChatGui { get; set; } = null!; + [PluginService] public static IClientState ClientState { get; set; } = null!; + [PluginService] public static ICommandManager CommandManager { get; set; } = null!; + [PluginService] public static IDataManager DataManager { get; set; } = null!; + [PluginService] public static IFramework Framework { get; set; } = null!; + [PluginService] public static IGameGui GameGui { get; set; } = null!; + [PluginService] public static IObjectTable ObjectTable { get; set; } = null!; + [PluginService] public static IPartyList PartyList { get; set; } = null!; + [PluginService] public static IGameInteropProvider GameInteropProvider { get; set; } = null; + + public static void Initialize(IDalamudPluginInterface pluginInterface) + { + pluginInterface.Create(); } } diff --git a/PlayerTags/PluginStrings/IPluginString.cs b/PlayerTags/PluginStrings/IPluginString.cs index b75aa87..0c4e20b 100644 --- a/PlayerTags/PluginStrings/IPluginString.cs +++ b/PlayerTags/PluginStrings/IPluginString.cs @@ -1,7 +1,6 @@ -namespace PlayerTags.PluginStrings +namespace PlayerTags.PluginStrings; + +public interface IPluginString { - public interface IPluginString - { - public string Value { get; } - } + public string Value { get; } } diff --git a/PlayerTags/PluginStrings/LiteralPluginString.cs b/PlayerTags/PluginStrings/LiteralPluginString.cs index 9c91db5..5e91483 100644 --- a/PlayerTags/PluginStrings/LiteralPluginString.cs +++ b/PlayerTags/PluginStrings/LiteralPluginString.cs @@ -1,18 +1,17 @@ -namespace PlayerTags.PluginStrings +namespace PlayerTags.PluginStrings; + +public class LiteralPluginString : IPluginString { - public class LiteralPluginString : IPluginString + private string m_Value; + public string Value => m_Value; + + public LiteralPluginString(string value) { - private string m_Value; - public string Value => m_Value; + m_Value = value; + } - public LiteralPluginString(string value) - { - m_Value = value; - } - - public override string ToString() - { - return Value; - } + public override string ToString() + { + return Value; } } diff --git a/PlayerTags/PluginStrings/LocalizedPluginString.cs b/PlayerTags/PluginStrings/LocalizedPluginString.cs index b8e01bc..c708565 100644 --- a/PlayerTags/PluginStrings/LocalizedPluginString.cs +++ b/PlayerTags/PluginStrings/LocalizedPluginString.cs @@ -1,18 +1,17 @@ -namespace PlayerTags.PluginStrings +namespace PlayerTags.PluginStrings; + +public class LocalizedPluginString : IPluginString { - public class LocalizedPluginString : IPluginString + public string Key { get; init; } + public string Value => Localizer.GetString(Key, false); + + public LocalizedPluginString(string key) { - public string Key { get; init; } - public string Value => Localizer.GetString(Key, false); + Key = key; + } - public LocalizedPluginString(string key) - { - Key = key; - } - - public override string ToString() - { - return Value; - } + public override string ToString() + { + return Value; } } diff --git a/PlayerTags/RandomNameGenerator.cs b/PlayerTags/RandomNameGenerator.cs index 430b025..e1ef7f8 100644 --- a/PlayerTags/RandomNameGenerator.cs +++ b/PlayerTags/RandomNameGenerator.cs @@ -1,116 +1,113 @@ -using Dalamud.Logging; -using System; +using System; using System.Globalization; using System.IO; -using System.Reflection; -namespace PlayerTags +namespace PlayerTags; + +/// +/// Generates names based on existing lists of words. +/// +public static class RandomNameGenerator { - /// - /// Generates names based on existing lists of words. - /// - public static class RandomNameGenerator + private static string[]? s_Adjectives; + private static string[] Adjectives { - private static string[]? s_Adjectives; - private static string[] Adjectives + get { - get + if (s_Adjectives == null) { - if (s_Adjectives == null) + try { - try - { - s_Adjectives = File.ReadAllLines(Path.Combine(MyPaths.ResourcePath, Resources.Paths.AdjectivesTxt)); - } - catch (Exception ex) - { - PluginServices.PluginLog.Error(ex, $"RandomNameGenerator failed to read adjectives"); - } + s_Adjectives = File.ReadAllLines(Path.Combine(MyPaths.ResourcePath, Resources.Paths.AdjectivesTxt)); } - - if (s_Adjectives != null) + catch (Exception ex) { - return s_Adjectives; + PluginServices.PluginLog.Error(ex, $"RandomNameGenerator failed to read adjectives"); } - - return new string[] { }; } + + if (s_Adjectives != null) + { + return s_Adjectives; + } + + return new string[] { }; + } + } + + private static string[]? s_Nouns; + private static string[] Nouns + { + get + { + if (s_Nouns == null) + { + try + { + s_Nouns = File.ReadAllLines(Path.Combine(MyPaths.ResourcePath, Resources.Paths.NounsTxt)); + } + catch (Exception ex) + { + PluginServices.PluginLog.Error(ex, $"RandomNameGenerator failed to read nouns"); + } + } + + if (s_Nouns != null) + { + return s_Nouns; + } + + return new string[] { }; + } + } + + /// + /// Generates a name for the given string. + /// + /// The string to generate a name for. + /// A generated name. + public static string? Generate(string str) + { + if (Adjectives == null || Nouns == null) + { + return null; } - private static string[]? s_Nouns; - private static string[] Nouns + int hash = GetDeterministicHashCode(str); + + // Use the seed as the hash so the same player always gets the same name + Random random = new(hash); + var adjective = Adjectives[random.Next(0, Adjectives.Length)]; + var noun = Nouns[random.Next(0, Nouns.Length)]; + var generatedName = $"{adjective} {noun}"; + + return CultureInfo.CurrentCulture.TextInfo.ToTitleCase(generatedName); + } + + /// + /// Gets a deterministic hash code for the given string. + /// + /// The string to hash. + /// A deterministic hash code. + private static int GetDeterministicHashCode(string str) + { + unchecked { - get + int hash1 = (5381 << 16) + 5381; + int hash2 = hash1; + + for (int index = 0; index < str.Length; index += 2) { - if (s_Nouns == null) + hash1 = ((hash1 << 5) + hash1) ^ str[index]; + if (index == str.Length - 1) { - try - { - s_Nouns = File.ReadAllLines(Path.Combine(MyPaths.ResourcePath, Resources.Paths.NounsTxt)); - } - catch (Exception ex) - { - PluginServices.PluginLog.Error(ex, $"RandomNameGenerator failed to read nouns"); - } + break; } - if (s_Nouns != null) - { - return s_Nouns; - } - - return new string[] { }; - } - } - - /// - /// Generates a name for the given string. - /// - /// The string to generate a name for. - /// A generated name. - public static string? Generate(string str) - { - if (Adjectives == null || Nouns == null) - { - return null; + hash2 = ((hash2 << 5) + hash2) ^ str[index + 1]; } - int hash = GetDeterministicHashCode(str); - - // Use the seed as the hash so the same player always gets the same name - Random random = new Random(hash); - var adjective = Adjectives[random.Next(0, Adjectives.Length)]; - var noun = Nouns[random.Next(0, Nouns.Length)]; - var generatedName = $"{adjective} {noun}"; - - return CultureInfo.CurrentCulture.TextInfo.ToTitleCase(generatedName); - } - - /// - /// Gets a deterministic hash code for the given string. - /// - /// The string to hash. - /// A deterministic hash code. - private static int GetDeterministicHashCode(string str) - { - unchecked - { - int hash1 = (5381 << 16) + 5381; - int hash2 = hash1; - - for (int index = 0; index < str.Length; index += 2) - { - hash1 = ((hash1 << 5) + hash1) ^ str[index]; - if (index == str.Length - 1) - { - break; - } - - hash2 = ((hash2 << 5) + hash2) ^ str[index + 1]; - } - - return hash1 + (hash2 * 1566083941); - } + return hash1 + (hash2 * 1566083941); } } } diff --git a/PlayerTags/UIColorHelper.cs b/PlayerTags/UIColorHelper.cs index 827c46b..a447993 100644 --- a/PlayerTags/UIColorHelper.cs +++ b/PlayerTags/UIColorHelper.cs @@ -5,105 +5,101 @@ using System.Collections.Generic; using System.Linq; using System.Numerics; -namespace PlayerTags +namespace PlayerTags; + +public static class UIColorHelper { - public static class UIColorHelper + private class UIColorComparer : IEqualityComparer { - private class UIColorComparer : IEqualityComparer + public bool Equals(UIColor? left, UIColor? right) { - public bool Equals(UIColor? left, UIColor? right) + if (left != null && right != null) { - if (left != null && right != null) - { - return left.UIForeground == right.UIForeground; - } - - return false; + return left.UIForeground == right.UIForeground; } - public int GetHashCode(UIColor obj) - { - return obj.UIForeground.GetHashCode(); - } + return false; } - private static UIColor[] s_UIColors = null!; - - public static IEnumerable UIColors + public int GetHashCode(UIColor obj) { - get - { - if (s_UIColors == null) - { - s_UIColors = CreateUIColors(); - } - - return s_UIColors; - } - } - - public static Vector4 ToColor(UIColor uiColor) - { - var uiColorBytes = BitConverter.GetBytes(uiColor.UIForeground); - return - new Vector4((float)uiColorBytes[3] / 255, - (float)uiColorBytes[2] / 255, - (float)uiColorBytes[1] / 255, - (float)uiColorBytes[0] / 255); - } - - public static Vector4 ToColor(ushort colorId) - { - foreach (var uiColor in UIColors) - { - if ((ushort)uiColor.RowId == colorId) - { - return ToColor(uiColor); - } - } - - return new Vector4(); - } - - private static UIColor[] CreateUIColors() - { - var uiColors = PluginServices.DataManager.GetExcelSheet(); - if (uiColors != null) - { - var filteredUIColors = new List(uiColors.Distinct(new UIColorComparer()).Where(uiColor => uiColor.UIForeground != 0 && uiColor.UIForeground != 255)); - - filteredUIColors.Sort((left, right) => - { - var leftColor = ToColor(left); - var rightColor = ToColor(right); - ImGui.ColorConvertRGBtoHSV(leftColor.X, leftColor.Y, leftColor.Z, out float leftHue, out float leftSaturation, out float leftValue); - ImGui.ColorConvertRGBtoHSV(rightColor.X, rightColor.Y, rightColor.Z, out float rightHue, out float rightSaturation, out float rightValue); - - var hueDifference = leftHue.CompareTo(rightHue); - if (hueDifference != 0) - { - return hueDifference; - } - - var valueDifference = leftValue.CompareTo(rightValue); - if (valueDifference != 0) - { - return valueDifference; - } - - var saturationDifference = leftSaturation.CompareTo(rightSaturation); - if (saturationDifference != 0) - { - return saturationDifference; - } - - return 0; - }); - - return filteredUIColors.ToArray(); - } - - return new UIColor[] { }; + return obj.UIForeground.GetHashCode(); } } + + private static UIColor[] s_UIColors = null!; + + public static IEnumerable UIColors + { + get + { + s_UIColors ??= CreateUIColors(); + + return s_UIColors; + } + } + + public static Vector4 ToColor(UIColor uiColor) + { + var uiColorBytes = BitConverter.GetBytes(uiColor.UIForeground); + return + new Vector4((float)uiColorBytes[3] / 255, + (float)uiColorBytes[2] / 255, + (float)uiColorBytes[1] / 255, + (float)uiColorBytes[0] / 255); + } + + public static Vector4 ToColor(ushort colorId) + { + foreach (var uiColor in UIColors) + { + if ((ushort)uiColor.RowId == colorId) + { + return ToColor(uiColor); + } + } + + return new Vector4(); + } + + private static UIColor[] CreateUIColors() + { + var uiColors = PluginServices.DataManager.GetExcelSheet(); + if (uiColors != null) + { + var filteredUIColors = new List(uiColors.Distinct(new UIColorComparer()).Where(uiColor => uiColor.UIForeground != 0 && uiColor.UIForeground != 255)); + + filteredUIColors.Sort((left, right) => + { + var leftColor = ToColor(left); + var rightColor = ToColor(right); + ImGui.ColorConvertRGBtoHSV(leftColor.X, leftColor.Y, leftColor.Z, out float leftHue, out float leftSaturation, out float leftValue); + ImGui.ColorConvertRGBtoHSV(rightColor.X, rightColor.Y, rightColor.Z, out float rightHue, out float rightSaturation, out float rightValue); + + var hueDifference = leftHue.CompareTo(rightHue); + if (hueDifference != 0) + { + return hueDifference; + } + + var valueDifference = leftValue.CompareTo(rightValue); + if (valueDifference != 0) + { + return valueDifference; + } + + var saturationDifference = leftSaturation.CompareTo(rightSaturation); + if (saturationDifference != 0) + { + return saturationDifference; + } + + return 0; + }); + + return filteredUIColors.ToArray(); + } + + return new UIColor[] { }; + } }