diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7990fe7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.vs/ +obj/ +bin/ +*.user \ No newline at end of file diff --git a/PlayerTags.sln b/PlayerTags.sln new file mode 100644 index 0000000..a1c7ff1 --- /dev/null +++ b/PlayerTags.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29709.97 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlayerTags", "PlayerTags\PlayerTags.csproj", "{13C812E9-0D42-4B95-8646-40EEBF30636F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {13C812E9-0D42-4B95-8646-40EEBF30636F}.Debug|x64.ActiveCfg = Debug|x64 + {13C812E9-0D42-4B95-8646-40EEBF30636F}.Debug|x64.Build.0 = Debug|x64 + {13C812E9-0D42-4B95-8646-40EEBF30636F}.Release|x64.ActiveCfg = Release|x64 + {13C812E9-0D42-4B95-8646-40EEBF30636F}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {B17E85B1-5F60-4440-9F9A-3DDE877E8CDF} + EndGlobalSection +EndGlobal diff --git a/PlayerTags/Config/CustomColorConfig.cs b/PlayerTags/Config/CustomColorConfig.cs new file mode 100644 index 0000000..f73595b --- /dev/null +++ b/PlayerTags/Config/CustomColorConfig.cs @@ -0,0 +1,7 @@ +namespace PlayerTags.Config +{ + public class CustomColorConfig + { + public ushort? Id = null; + } +} diff --git a/PlayerTags/Config/CustomTagConfig.cs b/PlayerTags/Config/CustomTagConfig.cs new file mode 100644 index 0000000..5b0990b --- /dev/null +++ b/PlayerTags/Config/CustomTagConfig.cs @@ -0,0 +1,26 @@ +using System; +using System.Linq; + +namespace PlayerTags.Config +{ + [Serializable] + public class CustomTagConfig : TagConfig + { + public CustomColorConfig CustomColor = new CustomColorConfig(); + public string Name = ""; + public string FormattedGameObjectNames = ""; + + private string[] GameObjectNames + { + get + { + return FormattedGameObjectNames.Split(';').Select(gameObjectName => gameObjectName.ToLower().Trim()).ToArray(); + } + } + + public bool IncludesGameObjectName(string gameObjectName) + { + return GameObjectNames.Contains(gameObjectName); + } + } +} diff --git a/PlayerTags/Config/FreeCompanyNameplateVisibility.cs b/PlayerTags/Config/FreeCompanyNameplateVisibility.cs new file mode 100644 index 0000000..927bcb7 --- /dev/null +++ b/PlayerTags/Config/FreeCompanyNameplateVisibility.cs @@ -0,0 +1,8 @@ +namespace PlayerTags.Config +{ + public enum FreeCompanyNameplateVisibility + { + Default, + Never + } +} diff --git a/PlayerTags/Config/JobOverrideConfig.cs b/PlayerTags/Config/JobOverrideConfig.cs new file mode 100644 index 0000000..3907614 --- /dev/null +++ b/PlayerTags/Config/JobOverrideConfig.cs @@ -0,0 +1,10 @@ +using System; + +namespace PlayerTags.Config +{ + [Serializable] + public class JobOverrideConfig + { + public CustomColorConfig CustomColor = new CustomColorConfig(); + } +} diff --git a/PlayerTags/Config/MainConfig.cs b/PlayerTags/Config/MainConfig.cs new file mode 100644 index 0000000..bc6067a --- /dev/null +++ b/PlayerTags/Config/MainConfig.cs @@ -0,0 +1,73 @@ +using Dalamud.Configuration; +using Dalamud.Data; +using Dalamud.Plugin; +using Lumina.Excel.GeneratedSheets; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace PlayerTags.Config +{ + [Serializable] + public class MainConfig : IPluginConfiguration + { + public static Dictionary RolesById { get; } = new Dictionary() + { + { 0, Role.LandHand }, + { 1, Role.Tank }, + { 2, Role.DPS }, + { 3, Role.DPS }, + { 4, Role.Healer }, + }; + + public int Version { get; set; } = 0; + + public FreeCompanyNameplateVisibility FreeCompanyVisibility = FreeCompanyNameplateVisibility.Default; + public TitleNameplateVisibility TitleVisibility = TitleNameplateVisibility.Default; + public TitleNameplatePosition TitlePosition = TitleNameplatePosition.Default; + + public RoleTagConfig RoleTag = new RoleTagConfig(); + + public List CustomTagConfigs = new List(); + + public bool IsPlayerNameRandomlyGenerated = false; + + [NonSerialized] + private DalamudPluginInterface? m_PluginInterface; + + public event System.Action? Saved; + + public void Initialize(DalamudPluginInterface pluginInterface, DataManager dataManager) + { + m_PluginInterface = pluginInterface; + + // Populate each role config with all of its jobs if they aren't already in it + foreach (var roleConfigPair in RoleTag.RoleOverrideConfigs) + { + var role = roleConfigPair.Key; + var roleConfig = roleConfigPair.Value; + + var classJobs = dataManager.GetExcelSheet(); + if (classJobs != null) + { + foreach (var classJob in classJobs.Where(classJob => RolesById[classJob.Role] == role)) + { + if (!roleConfig.JobOverrideConfigs.ContainsKey(classJob.Abbreviation)) + { + roleConfig.JobOverrideConfigs[classJob.Abbreviation] = new JobOverrideConfig(); + } + } + } + } + } + + public void Save() + { + if (m_PluginInterface != null) + { + m_PluginInterface.SavePluginConfig(this); + Saved?.Invoke(); + }; + } + } +} diff --git a/PlayerTags/Config/MainConfigUI.cs b/PlayerTags/Config/MainConfigUI.cs new file mode 100644 index 0000000..e692cad --- /dev/null +++ b/PlayerTags/Config/MainConfigUI.cs @@ -0,0 +1,403 @@ +using Dalamud.Interface.Components; +using Dalamud.Logging; +using ImGuiNET; +using Lumina.Excel.GeneratedSheets; +using PlayerTags.Resources; +using System; +using System.Linq; +using System.Numerics; + +namespace PlayerTags.Config +{ + public class MainConfigUI + { + private MainConfig m_Config; + + private bool m_OpenPopupRequested; + + private CustomColorConfig? m_ColorPickerPopupDataContext; + + private bool m_IsVisible = false; + public bool IsVisible + { + get { return m_IsVisible; } + set { m_IsVisible = value; } + } + + public MainConfigUI(MainConfig config) + { + m_Config = config; + } + + private string GetLocString(bool isDescription) + where TEnum : Enum + { + return GetLocString(typeof(TEnum).Name, isDescription); + } + + private string GetLocString(TEnum enumValue, bool isDescription) + where TEnum : Enum + { + return GetLocString($"{typeof(TEnum).Name}_{enumValue}", isDescription); + } + + private string GetLocString(string locStringId, bool isDescription) + { + string completeLocStringId = $"Loc_{locStringId}"; + + if (isDescription) + { + completeLocStringId += "_Description"; + } + + return GetLocString(completeLocStringId); + } + + private string GetLocString(string completeLocStringId) + { + string? value = Strings.ResourceManager.GetString(completeLocStringId, Strings.Culture); + if (value != null) + { + return value; + } + + PluginLog.Error($"Failed to get localized string for id {completeLocStringId}"); + return completeLocStringId; + } + + public void Draw() + { + if (m_Config == null || !IsVisible) + { + return; + } + + if (ImGui.Begin(Strings.Loc_Static_PluginName, ref m_IsVisible)) + { + if (ImGui.BeginTabBar("MainTabBar")) + { + if (ImGui.BeginTabItem(Strings.Loc_Static_General)) + { + DrawHeading(Strings.Loc_Static_Nameplates); + DrawEnumComboBox( + ref m_Config.FreeCompanyVisibility, + () => m_Config.Save()); + DrawEnumComboBox( + ref m_Config.TitleVisibility, + () => m_Config.Save()); + DrawEnumComboBox( + ref m_Config.TitlePosition, + () => m_Config.Save()); + DrawHeading(Strings.Loc_Static_Development); + DrawCheckbox( + nameof(m_Config.IsPlayerNameRandomlyGenerated), + ref m_Config.IsPlayerNameRandomlyGenerated, + () => m_Config.Save()); + + ImGui.EndTabItem(); + } + + if (ImGui.BeginTabItem(Strings.Loc_Static_RoleAndJobTags)) + { + DrawEnumComboBox( + ref m_Config.RoleTag.Format, + () => m_Config.Save()); + DrawTagConfig(m_Config.RoleTag); + + DrawHeading(Strings.Loc_Static_Roles); + if (ImGui.BeginTabBar("JobAndRolesTabBar")) + { + foreach (var rolePair in m_Config.RoleTag.RoleOverrideConfigs) + { + var role = rolePair.Key; + var roleConfig = rolePair.Value; + + if (ImGui.BeginTabItem(GetLocString($"{role.GetType().Name}_{role}", false))) + { + DrawCheckbox( + $"{roleConfig.GetType().Name}_{nameof(roleConfig.IsEnabled)}", + ref roleConfig.IsEnabled, + () => m_Config.Save()); + DrawTextBox( + $"{roleConfig.GetType().Name}_{nameof(roleConfig.Name)}", + ref roleConfig.Name, + () => m_Config.Save()); + DrawOptionalCustomColor( + $"{roleConfig.CustomColor.GetType().Name}_IsEnabled", + roleConfig.CustomColor.Id.ToString()!, + roleConfig.CustomColor); + DrawHeading(Strings.Loc_Static_Jobs); + foreach (var key in roleConfig.JobOverrideConfigs.Keys.OrderBy(key => key)) + { + if (string.IsNullOrEmpty(key)) + { + continue; + } + + JobOverrideConfig jobConfig = roleConfig.JobOverrideConfigs[key]; + + ImGui.Columns(2, "columns", false); + ImGui.SetColumnWidth(0, 42); + ImGui.Text(key); + ImGui.NextColumn(); + DrawOptionalCustomColor( + $"{roleConfig.CustomColor.GetType().Name}_IsEnabled", + key, + jobConfig.CustomColor); + + ImGui.Columns(); + } + + ImGui.EndTabItem(); + } + } + + ImGui.EndTabBar(); + } + + ImGui.EndTabItem(); + } + + if (ImGui.BeginTabItem(Strings.Loc_Static_CustomTags)) + { + if (ImGui.Button(Strings.Loc_Static_AddCustomTag)) + { + m_Config.CustomTagConfigs.Add(new CustomTagConfig()); + m_Config.Save(); + } + + if (!m_Config.CustomTagConfigs.Any()) + { + ImGui.Text(Strings.Loc_Static_NoCustomTagsAdded); + } + else + { + foreach (var customTagConfig in m_Config.CustomTagConfigs.ToArray()) + { + ImGui.PushID(customTagConfig.GetHashCode().ToString()); + ImGui.Separator(); + DrawTextBox( + $"{customTagConfig.GetType().Name}_{nameof(customTagConfig.Name)}", + ref customTagConfig.Name, + () => { m_Config.Save(); }); + DrawOptionalCustomColor( + $"{customTagConfig.CustomColor.GetType().Name}_IsEnabled", + customTagConfig.CustomColor.Id.ToString()!, + customTagConfig.CustomColor); + DrawTextBox( + $"{customTagConfig.GetType().Name}_{nameof(customTagConfig.FormattedGameObjectNames)}", + ref customTagConfig.FormattedGameObjectNames, + () => { m_Config.Save(); }); + DrawTagConfig(customTagConfig); + ImGui.Spacing(); + ImGui.PushStyleColor(ImGuiCol.Button, new Vector4(0.4f, 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)); + if (ImGui.Button(Strings.Loc_Static_RemoveCustomTag)) + { + m_Config.CustomTagConfigs.Remove(customTagConfig); + m_Config.Save(); + } + ImGui.PopStyleColor(); + ImGui.PopStyleColor(); + ImGui.PopStyleColor(); + ImGui.PopID(); + } + } + + ImGui.EndTabItem(); + } + + ImGui.EndTabBar(); + } + + if (m_OpenPopupRequested == true) + { + m_OpenPopupRequested = false; + ImGui.OpenPopup("ColorPickerPopup"); + } + + ImGui.SetNextWindowSize(new Vector2(400, 284)); + if (ImGui.BeginPopup("ColorPickerPopup")) + { + DrawUIColorPicker( + (UIColor value) => + { + if (m_ColorPickerPopupDataContext != null) + { + m_ColorPickerPopupDataContext.Id = (ushort)value.RowId; + m_ColorPickerPopupDataContext = null; + m_Config.Save(); + } + + ImGui.CloseCurrentPopup(); + }); + + ImGui.EndPopup(); + } + + ImGui.End(); + } + } + + private void DrawTagConfig(TagConfig tagConfig) + { + if (m_Config == null || !IsVisible) + { + return; + } + + DrawHeading(Strings.Loc_Static_ChatTag); + ImGui.PushID("Chat"); + DrawEnumComboBox( + ref tagConfig.ChatPosition, + () => m_Config.Save()); + ImGui.PopID(); + DrawHeading(Strings.Loc_Static_NameplateTag); + ImGui.PushID("Nameplate"); + DrawEnumComboBox( + ref tagConfig.NameplatePosition, + () => m_Config.Save()); + DrawEnumComboBox( + ref tagConfig.NameplateElement, + () => m_Config.Save()); + ImGui.PopID(); + } + + private void DrawOptionalCustomColor(string locStringId, string colorId, CustomColorConfig customColorConfig) + { + if (customColorConfig.Id.HasValue) + { + DrawColorButton( + colorId, + UIColorHelper.ToColor(customColorConfig.Id.Value), + () => + { + m_ColorPickerPopupDataContext = customColorConfig; + m_OpenPopupRequested = true; + }); + + ImGui.SameLine(); + } + + var isChecked = customColorConfig.Id != null; + DrawCheckbox( + locStringId, + ref isChecked, + () => + { + if (!isChecked) + { + customColorConfig.Id = null; + m_Config.Save(); + } + else + { + customColorConfig.Id = (ushort)UIColorHelper.UIColors.First().RowId; + m_Config.Save(); + } + }); + } + + private void DrawSeparator() + { + ImGui.Spacing(); + ImGui.Spacing(); + ImGui.Spacing(); + ImGui.Spacing(); + } + + private void DrawHeading(string label) + { + ImGui.TextColored(new Vector4(0.7f, 0.6f, 1f, 1f), label); + } + + private void DrawEnumComboBox(ref TEnum currentValue, System.Action changed) + where TEnum : Enum + { + ImGui.Text(GetLocString(false)); + ImGuiComponents.HelpMarker(GetLocString(true)); + + if (ImGui.BeginCombo($"###{currentValue.GetType().Name}", GetLocString(currentValue, false))) + { + foreach (string enumValueString in typeof(TEnum).GetEnumNames()) + { + TEnum enumValue = (TEnum)Enum.Parse(typeof(TEnum), enumValueString); + bool isSelected = enumValueString == currentValue.ToString(); + if (ImGui.Selectable($"{GetLocString(enumValue, false)}###{enumValueString}", isSelected)) + { + currentValue = (TEnum)Enum.Parse(typeof(TEnum), enumValueString); + ImGui.SetItemDefaultFocus(); + changed(); + } + + if (ImGui.IsItemHovered()) + { + ImGui.SetTooltip(GetLocString(enumValue, true)); + } + } + + ImGui.EndCombo(); + } + + if (ImGui.IsItemHovered()) + { + ImGui.SetTooltip(GetLocString(currentValue, true)); + } + } + + private void DrawCheckbox(string locStringId, ref bool isChecked, System.Action changed) + { + if (ImGui.Checkbox(GetLocString(locStringId, false), ref isChecked)) + { + changed(); + } + + if (ImGui.IsItemHovered()) + { + ImGui.SetTooltip(GetLocString(locStringId, true)); + } + } + + private void DrawColorButton(string colorId, Vector4 color, System.Action clicked) + { + if (ImGui.ColorButton(colorId, color)) + { + clicked(); + } + } + + private void DrawTextBox(string locStringId, ref string text, System.Action changed) + { + ImGui.Text(GetLocString(locStringId, false)); + ImGuiComponents.HelpMarker(GetLocString(locStringId, true)); + + var oldText = text; + ImGui.InputText($"###{locStringId}", ref text, 1024); + if (text != oldText) + { + changed(); + } + } + + private void DrawUIColorPicker(System.Action clicked) + { + ImGui.PushID(clicked.GetHashCode()); + ImGui.Columns(12, "columns", false); + foreach (var uiColor in UIColorHelper.UIColors) + { + DrawColorButton( + uiColor.RowId.ToString(), + UIColorHelper.ToColor(uiColor), + () => + { + clicked(uiColor); + }); + + ImGui.NextColumn(); + } + ImGui.Columns(); + ImGui.PopID(); + } + } +} diff --git a/PlayerTags/Config/NameplateElement.cs b/PlayerTags/Config/NameplateElement.cs new file mode 100644 index 0000000..c81600a --- /dev/null +++ b/PlayerTags/Config/NameplateElement.cs @@ -0,0 +1,9 @@ +namespace PlayerTags.Config +{ + public enum NameplateElement + { + Name, + Title, + FreeCompany + } +} diff --git a/PlayerTags/Config/Role.cs b/PlayerTags/Config/Role.cs new file mode 100644 index 0000000..c13e54a --- /dev/null +++ b/PlayerTags/Config/Role.cs @@ -0,0 +1,10 @@ +namespace PlayerTags.Config +{ + public enum Role + { + LandHand, + Tank, + Healer, + DPS + } +} diff --git a/PlayerTags/Config/RoleOverrideConfig.cs b/PlayerTags/Config/RoleOverrideConfig.cs new file mode 100644 index 0000000..3c08d4c --- /dev/null +++ b/PlayerTags/Config/RoleOverrideConfig.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; + +namespace PlayerTags.Config +{ + [Serializable] + public class RoleOverrideConfig + { + public bool IsEnabled = true; + public string Name = ""; + public CustomColorConfig CustomColor = new CustomColorConfig(); + public Dictionary JobOverrideConfigs = new Dictionary(); + } +} diff --git a/PlayerTags/Config/RoleTagConfig.cs b/PlayerTags/Config/RoleTagConfig.cs new file mode 100644 index 0000000..4e6a707 --- /dev/null +++ b/PlayerTags/Config/RoleTagConfig.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; + +namespace PlayerTags.Config +{ + [Serializable] + public class RoleTagConfig : TagConfig + { + public RoleTagFormat Format = RoleTagFormat.AbbreviatedJobName; + + public Dictionary RoleOverrideConfigs = new Dictionary() + { + { Role.LandHand, new RoleOverrideConfig() { IsEnabled = true, Name = "Land/Hand", CustomColor = new CustomColorConfig() { Id = 3 } } }, + { Role.Tank, new RoleOverrideConfig() { IsEnabled = true, Name = "Tank", CustomColor = new CustomColorConfig() { Id = 542 } } }, + { Role.Healer, new RoleOverrideConfig() { IsEnabled = true, Name = "Healer", CustomColor = new CustomColorConfig() { Id = 45 } } }, + { Role.DPS, new RoleOverrideConfig() { IsEnabled = true, Name = "DPS", CustomColor = new CustomColorConfig() { Id = 511 } } }, + }; + } +} diff --git a/PlayerTags/Config/RoleTagFormat.cs b/PlayerTags/Config/RoleTagFormat.cs new file mode 100644 index 0000000..5e945fe --- /dev/null +++ b/PlayerTags/Config/RoleTagFormat.cs @@ -0,0 +1,9 @@ +namespace PlayerTags.Config +{ + public enum RoleTagFormat + { + AbbreviatedJobName, + JobName, + RoleName + } +} diff --git a/PlayerTags/Config/StringPosition.cs b/PlayerTags/Config/StringPosition.cs new file mode 100644 index 0000000..d076c96 --- /dev/null +++ b/PlayerTags/Config/StringPosition.cs @@ -0,0 +1,10 @@ +namespace PlayerTags.Config +{ + public enum StringPosition + { + None, + Before, + After, + Replace + } +} diff --git a/PlayerTags/Config/TagConfig.cs b/PlayerTags/Config/TagConfig.cs new file mode 100644 index 0000000..bcbc1cb --- /dev/null +++ b/PlayerTags/Config/TagConfig.cs @@ -0,0 +1,14 @@ +using System; + +namespace PlayerTags.Config +{ + [Serializable] + public class TagConfig + { + public NameplateElement NameplateElement = NameplateElement.Name; + + public StringPosition NameplatePosition = StringPosition.Before; + + public StringPosition ChatPosition = StringPosition.Before; + } +} diff --git a/PlayerTags/Config/TitleNameplatePosition.cs b/PlayerTags/Config/TitleNameplatePosition.cs new file mode 100644 index 0000000..92cc6e6 --- /dev/null +++ b/PlayerTags/Config/TitleNameplatePosition.cs @@ -0,0 +1,9 @@ +namespace PlayerTags.Config +{ + public enum TitleNameplatePosition + { + Default, + AlwaysAboveName, + AlwaysBelowName + } +} diff --git a/PlayerTags/Config/TitleNameplateVisibility.cs b/PlayerTags/Config/TitleNameplateVisibility.cs new file mode 100644 index 0000000..5d2ee3b --- /dev/null +++ b/PlayerTags/Config/TitleNameplateVisibility.cs @@ -0,0 +1,10 @@ +namespace PlayerTags.Config +{ + public enum TitleNameplateVisibility + { + Default, + Always, + Never, + WhenHasTags + } +} diff --git a/PlayerTags/DalamudPackager.targets b/PlayerTags/DalamudPackager.targets new file mode 100644 index 0000000..d87598b --- /dev/null +++ b/PlayerTags/DalamudPackager.targets @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/PlayerTags/PlayerTags.csproj b/PlayerTags/PlayerTags.csproj new file mode 100644 index 0000000..d7e6f22 --- /dev/null +++ b/PlayerTags/PlayerTags.csproj @@ -0,0 +1,78 @@ + + + + r00telement + 1.0.0.0 + + + + net5.0-windows7.0 + x64 + enable + latest + true + false + false + $(AppData)\XIVLauncher\devPlugins\PlayerTags\ + + + + $(appdata)\XIVLauncher\addon\Hooks\dev\ + + + + + + $(DalamudLibPath)FFXIVClientStructs.dll + false + + + $(DalamudLibPath)Newtonsoft.Json.dll + false + + + $(DalamudLibPath)Dalamud.dll + false + + + $(DalamudLibPath)ImGui.NET.dll + false + + + $(DalamudLibPath)ImGuiScene.dll + false + + + $(DalamudLibPath)Lumina.dll + false + + + $(DalamudLibPath)Lumina.Excel.dll + false + + + + + + True + True + Strings.resx + + + + + + PublicResXFileCodeGenerator + Strings.Designer.cs + + + + + + PreserveNewest + + + PreserveNewest + + + diff --git a/PlayerTags/PlayerTags.json b/PlayerTags/PlayerTags.json new file mode 100644 index 0000000..9aac722 --- /dev/null +++ b/PlayerTags/PlayerTags.json @@ -0,0 +1,11 @@ +{ + "Author": "r00telement", + "Name": "Player Tags", + "Description": "See tags for players in nameplates and chat, such as their role and job, or custom tags for players you know.", + "RepoUrl": "https://github.com/r00telement/PlayerTags", + "IconUrl": "https://github.com/r00telement/PlayerTags/master/Resources/Chat_1.png", + "ImageUrls": [ + "https://github.com/r00telement/PlayerTags/master/Resources/Chat_1.png", + "https://github.com/r00telement/PlayerTags/master/Resources/Nameplates_1.png" + ] +} \ No newline at end of file diff --git a/PlayerTags/Plugin.cs b/PlayerTags/Plugin.cs new file mode 100644 index 0000000..dbb89ab --- /dev/null +++ b/PlayerTags/Plugin.cs @@ -0,0 +1,619 @@ +using Dalamud.Data; +using Dalamud.Game; +using Dalamud.Game.ClientState.Objects; +using Dalamud.Game.ClientState.Objects.Types; +using Dalamud.Game.Command; +using Dalamud.Game.Gui; +using Dalamud.Game.Text; +using Dalamud.Game.Text.SeStringHandling; +using Dalamud.Game.Text.SeStringHandling.Payloads; +using Dalamud.IoC; +using Dalamud.Logging; +using Dalamud.Plugin; +using PlayerTags.Config; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace PlayerTags +{ + public sealed class Plugin : IDalamudPlugin + { + public string Name => "Player Tags"; + + private const string c_CommandName = "/playertags"; + + [PluginService] + private static DalamudPluginInterface PluginInterface { get; set; } = null!; + + [PluginService] + private static Framework Framework { get; set; } = null!; + + [PluginService] + private static ChatGui ChatGui { get; set; } = null!; + + [PluginService] + private static GameGui GameGui { get; set; } = null!; + + [PluginService] + private static ObjectTable ObjectTable { get; set; } = null!; + + [PluginService] + private static DataManager DataManager { get; set; } = null!; + + [PluginService] + private static CommandManager CommandManager { get; set; } = null!; + + private MainConfig m_Config; + + private MainConfigUI m_ConfigUI; + + private Dictionary m_JobTagPayloads = new Dictionary(); + + private Dictionary m_CustomTagPayloads = new Dictionary(); + + private TextPayload m_SpaceTextPayload = new TextPayload($" "); + + private PluginHooks m_PluginHooks; + + private RandomNameGenerator? m_RandomNameGenerator = null; + + public Plugin() + { + UIColorHelper.Initialize(DataManager); + + m_Config = PluginInterface.GetPluginConfig() as MainConfig ?? new MainConfig(); + m_Config.Initialize(PluginInterface, DataManager); + m_Config.Saved += Configuration_Saved; + + m_ConfigUI = new MainConfigUI(m_Config); + + CommandManager.AddHandler(c_CommandName, new CommandInfo((string command, string arguments) => + { + m_ConfigUI.IsVisible = true; + }) + { + HelpMessage = "Shows the config" + }); + + PluginInterface.UiBuilder.Draw += UiBuilder_Draw; + PluginInterface.UiBuilder.OpenConfigUi += UiBuilder_OpenConfigUi; + + m_PluginHooks = new PluginHooks(Framework, ObjectTable, GameGui, SetNameplate); + + ChatGui.ChatMessage += Chat_ChatMessage; + + if (m_Config.IsPlayerNameRandomlyGenerated && m_RandomNameGenerator == null) + { + m_RandomNameGenerator = new RandomNameGenerator(); + } + } + + public void Dispose() + { + PluginInterface.UiBuilder.OpenConfigUi -= UiBuilder_OpenConfigUi; + CommandManager.RemoveHandler(c_CommandName); + ChatGui.ChatMessage -= Chat_ChatMessage; + m_Config.Saved -= Configuration_Saved; + m_PluginHooks?.Dispose(); + } + + private void Configuration_Saved() + { + // Invalidate the cached payloads so they get remade + m_JobTagPayloads.Clear(); + m_CustomTagPayloads.Clear(); + + if (m_Config.IsPlayerNameRandomlyGenerated && m_RandomNameGenerator == null) + { + m_RandomNameGenerator = new RandomNameGenerator(); + } + } + + private void UiBuilder_Draw() + { + m_ConfigUI.Draw(); + } + + private void UiBuilder_OpenConfigUi() + { + m_ConfigUI.IsVisible = true; + } + + private void Chat_ChatMessage(XivChatType type, uint senderId, ref SeString sender, ref SeString message, ref bool isHandled) + { + AddTagsToChat(sender, out _); + AddTagsToChat(message, out _); + } + + /// + /// Sets the strings on a nameplate. + /// + /// The game object context. + /// The name text. + /// The title text. + /// The free company text. + /// Whether the title is visible. + /// Whether the title is above the name or below it. + /// The icon id. + /// Whether the name was changed. + /// Whether the title was changed. + /// Whether the free company was changed. + private void SetNameplate(GameObject gameObject, SeString name, SeString title, SeString freeCompany, ref bool isTitleVisible, ref bool isTitleAboveName, ref int iconId, out bool isNameChanged, out bool isTitleChanged, out bool isFreeCompanyChanged) + { + AddTagsToNameplate(gameObject, name, title, freeCompany, out isNameChanged, out isTitleChanged, out isFreeCompanyChanged); + + if (m_Config.TitlePosition == TitleNameplatePosition.AlwaysAboveName) + { + isTitleAboveName = true; + } + else if (m_Config.TitlePosition == TitleNameplatePosition.AlwaysBelowName) + { + isTitleAboveName = false; + } + + if (m_Config.TitleVisibility == TitleNameplateVisibility.Default) + { + } + else if (m_Config.TitleVisibility == TitleNameplateVisibility.Always) + { + isTitleVisible = true; + } + else if (m_Config.TitleVisibility == TitleNameplateVisibility.Never) + { + isTitleVisible = false; + } + else if (m_Config.TitleVisibility == TitleNameplateVisibility.WhenHasTags) + { + isTitleVisible = isTitleChanged; + } + + if (m_Config.FreeCompanyVisibility == FreeCompanyNameplateVisibility.Default) + { + } + else if (m_Config.FreeCompanyVisibility == FreeCompanyNameplateVisibility.Never) + { + freeCompany.Payloads.Clear(); + isFreeCompanyChanged = true; + } + } + + /// + /// Gets the job tag payloads for the given character. If the payloads don't yet exist then they are created. + /// + /// The character to get job tag payloads for. + /// A list of job tag payloads for the given character. + private IEnumerable GetJobTagPayloads(Character character) + { + var roleId = character.ClassJob.GameData.Role; + var jobAbbreviation = character.ClassJob.GameData.Abbreviation; + var role = MainConfig.RolesById[roleId]; + + var roleConfig = m_Config.RoleTag.RoleOverrideConfigs[role]; + if (!roleConfig.IsEnabled) + { + return new Payload[] { }; + } + + if (m_JobTagPayloads.TryGetValue(jobAbbreviation, out var payloads)) + { + return payloads; + } + + string text = ""; + if (m_Config.RoleTag.Format == RoleTagFormat.AbbreviatedJobName) + { + text = character.ClassJob.GameData.Abbreviation; + } + else if (m_Config.RoleTag.Format == RoleTagFormat.JobName) + { + text = character.ClassJob.GameData.NameEnglish; + } + else if (m_Config.RoleTag.Format == RoleTagFormat.RoleName) + { + text = m_Config.RoleTag.RoleOverrideConfigs[role].Name; + } + + List newPayloads = new List(); + + // There will always be a text payload + newPayloads.Add(new TextPayload(text)); + + ushort? colorId = null; + + // Pick a color id if one is available + if (roleConfig.JobOverrideConfigs[jobAbbreviation].CustomColor.Id != null) + { + colorId = roleConfig.JobOverrideConfigs[jobAbbreviation].CustomColor.Id!.Value; + } + else if (roleConfig.CustomColor.Id != null) + { + colorId = roleConfig.CustomColor.Id.Value; + } + + // If we picked a color id, add the payloads for it + if (colorId != null) + { + newPayloads.Insert(0, new UIForegroundPayload(colorId.Value)); + newPayloads.Add(new UIForegroundPayload(0)); + } + + var newPayloadsArray = newPayloads.ToArray(); + m_JobTagPayloads[jobAbbreviation] = newPayloadsArray; + + return newPayloadsArray; + } + + /// + /// Gets the payloads for the given custom tag. If the payloads don't yet exist then they are created. + /// + /// The custom tag config to get payloads for. + /// A list of payloads for the given custom tag. + private IEnumerable GetCustomTagPayloads(CustomTagConfig customTagConfig) + { + if (m_CustomTagPayloads.TryGetValue(customTagConfig, out var payloads)) + { + return payloads; + } + + List newPayloads = new List(); + + // There will always be a text payload + newPayloads.Add(new TextPayload(customTagConfig.Name)); + + ushort? colorId = null; + + // Pick a color id if one is available + if (customTagConfig.CustomColor.Id != null) + { + colorId = customTagConfig.CustomColor.Id!.Value; + } + + // If we picked a color id, add the payloads for it + if (colorId != null) + { + newPayloads.Insert(0, new UIForegroundPayload(colorId.Value)); + newPayloads.Add(new UIForegroundPayload(0)); + } + + var newPayloadsArray = newPayloads.ToArray(); + m_CustomTagPayloads[customTagConfig] = newPayloadsArray; + + return newPayloadsArray; + } + + + /// + /// Adds an additional space text payload in between any existing text payloads. + /// + /// The payloads to add spaces between. + private void AddSpacesBetweenTextPayloads(List payloads) + { + var textPayloads = payloads.Where(payload => payload is TextPayload).ToList(); + foreach (var textPayload in textPayloads.Skip(1)) + { + var index = payloads.IndexOf(textPayload); + payloads.Insert(index, m_SpaceTextPayload); + } + } + + /// + /// A match found within a string. + /// + private class StringMatch + { + /// + /// The string that the match was found in. + /// + public SeString SeString { get; init; } + + /// + /// The matching text payload. + /// + public TextPayload TextPayload { get; init; } + + /// + /// The matching game object. + /// + public GameObject? GameObject { get; init; } + + public StringMatch(SeString seString, TextPayload textPayload, GameObject? gameObject = null) + { + SeString = seString; + TextPayload = textPayload; + GameObject = gameObject; + } + + /// + /// Gets the matches text. + /// + /// The match text. + public string GetMatchText() + { + if (GameObject != null) + { + return GameObject.Name.TextValue; + } + + return TextPayload.Text; + } + } + + /// + /// 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 = new List(); + + for (int payloadIndex = 0; payloadIndex < seString.Payloads.Count; ++payloadIndex) + { + var payload = seString.Payloads[payloadIndex]; + if (payload is PlayerPayload playerPayload) + { + var gameObject = ObjectTable.FirstOrDefault(gameObject => gameObject.Name.TextValue == playerPayload.PlayerName); + + // The next payload MUST be a text payload + if (payloadIndex + 1 < seString.Payloads.Count && seString.Payloads[payloadIndex + 1] is TextPayload textPayload) + { + var stringMatch = new StringMatch(seString, textPayload, gameObject); + stringMatches.Add(stringMatch); + + // Don't handle the text payload twice + payloadIndex++; + } + else + { + PluginLog.Error("Expected payload after player payload to be a text payload but it wasn't"); + } + } + + // If it's just a text payload then either a character NEEDS to exist for it, or it needs to be identified as a character by custom tag configs + else if (payload is TextPayload textPayload) + { + var gameObject = ObjectTable.FirstOrDefault(gameObject => gameObject.Name.TextValue == textPayload.Text); + var isIncludedInCustomTagConfig = m_Config.CustomTagConfigs.Any(customTagConfig => customTagConfig.IncludesGameObjectName(textPayload.Text)); + + if (gameObject != null || isIncludedInCustomTagConfig) + { + var stringMatch = new StringMatch(seString, textPayload, gameObject); + stringMatches.Add(stringMatch); + } + } + } + + return stringMatches; + } + + /// + /// Adds the given payload changes to the dictionary. + /// + /// The position of the string to add changes to. + /// The payloads to add. + /// The dictionary to add the changes to. + private void AddPayloadChanges(StringPosition stringPosition, IEnumerable payloads, Dictionary> stringChanges) + { + if (!payloads.Any()) + { + return; + } + + if (!stringChanges.Keys.Contains(stringPosition)) + { + stringChanges[stringPosition] = new List(); + } + + stringChanges[stringPosition].AddRange(payloads); + } + + /// + /// Adds the given payload changes to the dictionary. + /// + /// The nameplate element to add changes to. + /// The position of the string to add changes to. + /// The payloads to add. + /// The dictionary to add the changes to. + private void AddPayloadChanges(NameplateElement nameplateElement, StringPosition stringPosition, IEnumerable payloads, Dictionary>> nameplateChanges) + { + if (!payloads.Any()) + { + return; + } + + if (!nameplateChanges.Keys.Contains(nameplateElement)) + { + nameplateChanges[nameplateElement] = new Dictionary>(); + } + + AddPayloadChanges(stringPosition, payloads, nameplateChanges[nameplateElement]); + } + + /// + /// 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. + private void ApplyStringChanges(SeString seString, Dictionary> stringChanges, Payload? anchorPayload = null) + { + foreach ((var stringPosition, var payloads) in stringChanges) + { + if (!payloads.Any()) + { + continue; + } + + AddSpacesBetweenTextPayloads(payloads); + + if (stringPosition == StringPosition.Before) + { + if (anchorPayload != null) + { + var payloadIndex = seString.Payloads.IndexOf(anchorPayload); + seString.Payloads.InsertRange(payloadIndex, payloads.Append(m_SpaceTextPayload)); + } + else + { + seString.Payloads.InsertRange(0, payloads.Append(m_SpaceTextPayload)); + } + } + else if (stringPosition == StringPosition.After) + { + if (anchorPayload != null) + { + var payloadIndex = seString.Payloads.IndexOf(anchorPayload); + seString.Payloads.InsertRange(payloadIndex + 1, payloads.Prepend(m_SpaceTextPayload)); + } + else + { + seString.Payloads.AddRange(payloads.Prepend(m_SpaceTextPayload)); + } + } + else if (stringPosition == StringPosition.Replace) + { + if (anchorPayload != null) + { + var payloadIndex = seString.Payloads.IndexOf(anchorPayload); + seString.Payloads.InsertRange(payloadIndex, payloads); + seString.Payloads.Remove(anchorPayload); + } + else + { + seString.Payloads.Clear(); + seString.Payloads.AddRange(payloads); + } + } + } + } + + /// + /// Adds all configured 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. + /// Whether the name was changed. + /// Whether the title was changed. + /// Whether the free company was changed. + private void AddTagsToNameplate(GameObject gameObject, SeString name, SeString title, SeString freeCompany, out bool isNameChanged, out bool isTitleChanged, out bool isFreeCompanyChanged) + { + isNameChanged = false; + isTitleChanged = false; + isFreeCompanyChanged = false; + + Dictionary>> nameplateChanges = new Dictionary>>(); + + if (gameObject is Character character) + { + // Add the role tag payloads + if (m_Config.RoleTag.NameplatePosition != StringPosition.None) + { + AddPayloadChanges(m_Config.RoleTag.NameplateElement, m_Config.RoleTag.NameplatePosition, GetJobTagPayloads(character), nameplateChanges); + } + + // Add randomly generated name tag payload + if (m_Config.IsPlayerNameRandomlyGenerated && m_RandomNameGenerator != null) + { + var characterName = character.Name.TextValue; + if (characterName != null) + { + var generatedName = m_RandomNameGenerator.GetGeneratedName(characterName); + if (generatedName != null) + { + AddPayloadChanges(NameplateElement.Name, StringPosition.Replace, Enumerable.Empty().Append(new TextPayload(generatedName)), nameplateChanges); + } + } + } + } + + // Add the custom tag payloads + foreach (var customTagConfig in m_Config.CustomTagConfigs) + { + if (customTagConfig.NameplatePosition != StringPosition.None && customTagConfig.FormattedGameObjectNames.Split(',').Contains(gameObject.Name.TextValue)) + { + AddPayloadChanges(customTagConfig.NameplateElement, customTagConfig.NameplatePosition, GetCustomTagPayloads(customTagConfig), nameplateChanges); + } + } + + // Build the final strings out of the payloads + foreach ((var nameplateElement, var stringChanges) in nameplateChanges) + { + SeString? seString = null; + if (nameplateElement == NameplateElement.Name) + { + seString = name; + isNameChanged = true; + } + else if (nameplateElement == NameplateElement.Title) + { + seString = title; + isTitleChanged = true; + } + else if (nameplateElement == NameplateElement.FreeCompany) + { + seString = freeCompany; + isFreeCompanyChanged = true; + } + + if (seString != null) + { + ApplyStringChanges(seString, stringChanges); + } + } + } + + /// + /// Adds all configured tags to chat. + /// + /// The message to change. + /// Whether the message was changed. + private void AddTagsToChat(SeString message, out bool isMessageChanged) + { + isMessageChanged = false; + + var stringMatches = GetStringMatches(message); + foreach (var stringMatch in stringMatches) + { + Dictionary> stringChanges = new Dictionary>(); + + // The role tag payloads + if (stringMatch.GameObject is Character character) + { + if (m_Config.RoleTag.ChatPosition != StringPosition.None) + { + AddPayloadChanges(m_Config.RoleTag.ChatPosition, GetJobTagPayloads(character), stringChanges); + } + } + + // Add randomly generated name tag payload + if (m_Config.IsPlayerNameRandomlyGenerated && m_RandomNameGenerator != null) + { + var playerName = stringMatch.GetMatchText(); + if (playerName != null) + { + var generatedName = m_RandomNameGenerator.GetGeneratedName(playerName); + if (generatedName != null) + { + AddPayloadChanges(StringPosition.Replace, Enumerable.Empty().Append(new TextPayload(generatedName)), stringChanges); + } + } + } + + // Add the custom tag payloads + foreach (var customTagConfig in m_Config.CustomTagConfigs) + { + if (customTagConfig.IncludesGameObjectName(stringMatch.GetMatchText())) + { + AddPayloadChanges(customTagConfig.ChatPosition, GetCustomTagPayloads(customTagConfig), stringChanges); + } + } + + ApplyStringChanges(message, stringChanges, stringMatch.TextPayload); + isMessageChanged = true; + } + } + } +} diff --git a/PlayerTags/PluginHooks.cs b/PlayerTags/PluginHooks.cs new file mode 100644 index 0000000..01a9ece --- /dev/null +++ b/PlayerTags/PluginHooks.cs @@ -0,0 +1,238 @@ +using Dalamud.Game; +using Dalamud.Game.ClientState.Objects; +using Dalamud.Game.ClientState.Objects.Types; +using Dalamud.Game.Gui; +using Dalamud.Game.Text.SeStringHandling; +using Dalamud.Hooking; +using Dalamud.Logging; +using FFXIVClientStructs.FFXIV.Client.UI; +using System; +using System.Linq; +using System.Runtime.InteropServices; + +namespace PlayerTags +{ + public class PluginHooks : IDisposable + { + [UnmanagedFunctionPointer(CallingConvention.ThisCall, CharSet = CharSet.Ansi)] + private delegate IntPtr SetNameplateDelegate_Private(IntPtr nameplateObjectPtr, bool isTitleAboveName, bool isTitleVisible, IntPtr titlePtr, IntPtr namePtr, IntPtr freeCompanyPtr, int iconId); + + [UnmanagedFunctionPointer(CallingConvention.ThisCall, CharSet = CharSet.Ansi)] + private delegate IntPtr UIModule_GetRaptureAtkModuleDelegate_Private(IntPtr uiModule); + + [UnmanagedFunctionPointer(CallingConvention.ThisCall, CharSet = CharSet.Ansi)] + private delegate IntPtr Framework_GetUIModuleDelegate_Private(IntPtr framework); + + private class PluginAddressResolver : BaseAddressResolver + { + private const string SetNameplateSignature = "48 89 5C 24 ?? 48 89 6C 24 ?? 56 57 41 54 41 56 41 57 48 83 EC 40 44 0F B6 E2"; + internal IntPtr SetNameplatePtr; + + private const string Framework_GetUIModuleSignature = "E8 ?? ?? ?? ?? 48 8B C8 48 8B 10 FF 92 ?? ?? ?? ?? 48 8B C8 BA ?? ?? ?? ??"; + internal IntPtr Framework_GetUIModulePtr; + + protected override void Setup64Bit(SigScanner scanner) + { + SetNameplatePtr = scanner.ScanText(SetNameplateSignature); + Framework_GetUIModulePtr = scanner.ScanText(Framework_GetUIModuleSignature); + } + } + + private Framework m_Framework; + private ObjectTable m_ObjectTable; + private GameGui m_GameGui; + private SetNameplateDelegate m_SetNameplate; + + private PluginAddressResolver m_PluginAddressResolver; + private Hook m_SetNameplateHook; + private readonly Framework_GetUIModuleDelegate_Private m_GetUIModule; + private IntPtr? m_NameplateObjectArrayPtr; + private IntPtr? m_NameplateInfoArrayPtr; + + public PluginHooks(Framework framework, ObjectTable objectTable, GameGui gameGui, SetNameplateDelegate setNameplate) + { + m_Framework = framework; + m_ObjectTable = objectTable; + m_GameGui = gameGui; + m_SetNameplate = setNameplate; + + m_PluginAddressResolver = new PluginAddressResolver(); + m_PluginAddressResolver.Setup(); + + m_GetUIModule = Marshal.GetDelegateForFunctionPointer(m_PluginAddressResolver.Framework_GetUIModulePtr); + + m_SetNameplateHook = new Hook(m_PluginAddressResolver.SetNameplatePtr, new SetNameplateDelegate_Private(SetNameplateDetour)); + m_SetNameplateHook.Enable(); + } + + public void Dispose() + { + m_SetNameplateHook.Disable(); + } + + private IntPtr SetNameplateDetour(IntPtr nameplateObjectPtrOriginal, bool isTitleAboveNameOriginal, bool isTitleVisibleOriginal, IntPtr titlePtrOriginal, IntPtr namePtrOriginal, IntPtr freeCompanyPtrOriginal, int iconIdOriginal) + { + if (m_SetNameplate != null) + { + try + { + GameObject? gameObject = GetNameplateObject(nameplateObjectPtrOriginal); + if (gameObject != null) + { + SeString title = ReadSeString(titlePtrOriginal); + SeString name = ReadSeString(namePtrOriginal); + SeString freeCompany = ReadSeString(freeCompanyPtrOriginal); + bool isTitleVisible = isTitleVisibleOriginal; + bool isTitleAboveName = isTitleAboveNameOriginal; + int iconId = iconIdOriginal; + bool isTitleChanged; + bool isNameChanged; + bool isFreeCompanyChanged; + m_SetNameplate(gameObject, name, title, freeCompany, ref isTitleVisible, ref isTitleAboveName, ref iconId, out isNameChanged, out isTitleChanged, out isFreeCompanyChanged); + + IntPtr namePtr = namePtrOriginal; + if (isNameChanged) + { + namePtr = Allocate(name); + } + + IntPtr titlePtr = titlePtrOriginal; + if (isTitleChanged) + { + titlePtr = Allocate(title); + } + + IntPtr freeCompanyPtr = freeCompanyPtrOriginal; + if (isFreeCompanyChanged) + { + freeCompanyPtr = Allocate(freeCompany); + } + + var result = m_SetNameplateHook.Original(nameplateObjectPtrOriginal, isTitleAboveName, isTitleVisible, titlePtr, namePtr, freeCompanyPtr, iconId); + + if (isNameChanged) + { + Release(ref namePtr); + } + + if (isTitleChanged) + { + Release(ref titlePtr); + } + + if (isFreeCompanyChanged) + { + Release(ref freeCompanyPtr); + } + + return result; + } + } + catch (Exception ex) + { + PluginLog.Error(ex, $"SetNameplateDetour encountered a critical error"); + } + } + + return m_SetNameplateHook.Original(nameplateObjectPtrOriginal, isTitleAboveNameOriginal, isTitleVisibleOriginal, titlePtrOriginal, namePtrOriginal, freeCompanyPtrOriginal, iconIdOriginal); + } + + private static SeString ReadSeString(IntPtr stringPtr) + { + return SeString.Parse(ReadStringBytes(stringPtr)); + } + + private static byte[] ReadStringBytes(IntPtr stringPtr) + { + if (stringPtr == IntPtr.Zero) + { + return null!; + } + + var size = 0; + while (Marshal.ReadByte(stringPtr, size) != 0) + { + size++; + } + + var bytes = new byte[size]; + Marshal.Copy(stringPtr, bytes, 0, size); + return bytes; + } + + private static IntPtr Allocate(SeString seString) + { + var bytes = seString.Encode(); + IntPtr pointer = Marshal.AllocHGlobal(bytes.Length + 1); + Marshal.Copy(bytes, 0, pointer, bytes.Length); + Marshal.WriteByte(pointer, bytes.Length, 0); + return pointer; + } + + private static void Release(ref IntPtr ptr) + { + Marshal.FreeHGlobal(ptr); + ptr = IntPtr.Zero; + } + + private T? GetNameplateObject(IntPtr nameplateObjectPtr) + where T : GameObject + { + if (!m_NameplateInfoArrayPtr.HasValue) + { + // Get the nameplate object array + var namePlateAddonPtr = m_GameGui.GetAddonByName("NamePlate", 1); + var namePlateObjectArrayPtrPtr = namePlateAddonPtr + Marshal.OffsetOf(typeof(AddonNamePlate), nameof(AddonNamePlate.NamePlateObjectArray)).ToInt32(); + var nameplateObjectArrayPtr = Marshal.ReadIntPtr(namePlateObjectArrayPtrPtr); + if (nameplateObjectArrayPtr == IntPtr.Zero) + { + return null!; + } + + m_NameplateObjectArrayPtr = nameplateObjectArrayPtr; + + // Get the nameplate info + IntPtr raptureAtkModulePtr; + var frameworkPtr = m_Framework.Address.BaseAddress; + var uiModulePtr = m_GetUIModule(frameworkPtr); + unsafe + { + var uiModule = *(UIModule*)uiModulePtr; + var UIModule_GetRaptureAtkModuleAddress = new IntPtr(uiModule.vfunc[7]); + var GetRaptureAtkModule = Marshal.GetDelegateForFunctionPointer(UIModule_GetRaptureAtkModuleAddress); + raptureAtkModulePtr = GetRaptureAtkModule(uiModulePtr); + } + + if (raptureAtkModulePtr == IntPtr.Zero) + { + return null!; + } + + m_NameplateInfoArrayPtr = raptureAtkModulePtr + Marshal.OffsetOf(typeof(RaptureAtkModule), nameof(RaptureAtkModule.NamePlateInfoArray)).ToInt32(); + } + + // Determine the index of this nameplate + var namePlateObjectSize = Marshal.SizeOf(typeof(AddonNamePlate.NamePlateObject)); + var namePlateObjectPtr0 = m_NameplateObjectArrayPtr!.Value + namePlateObjectSize * 0; + var namePlateIndex = (nameplateObjectPtr.ToInt64() - namePlateObjectPtr0.ToInt64()) / namePlateObjectSize; + if (namePlateIndex < 0 || namePlateIndex >= 50) + { + return null!; + } + + var namePlateInfoPtr = new IntPtr(m_NameplateInfoArrayPtr.Value.ToInt64() + Marshal.SizeOf(typeof(RaptureAtkModule.NamePlateInfo)) * namePlateIndex); + RaptureAtkModule.NamePlateInfo namePlateInfo = Marshal.PtrToStructure(namePlateInfoPtr); + + // Get the player character for this nameplate info + var objectId = namePlateInfo.ObjectID.ObjectID; + + T? gameObject = m_ObjectTable.FirstOrDefault(obj => obj.ObjectId == objectId) as T; + if (gameObject == null) + { + return null!; + } + + return gameObject!; + } + } +} diff --git a/PlayerTags/RandomNameGenerator.cs b/PlayerTags/RandomNameGenerator.cs new file mode 100644 index 0000000..cd065f1 --- /dev/null +++ b/PlayerTags/RandomNameGenerator.cs @@ -0,0 +1,96 @@ +using Dalamud.Logging; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Reflection; + +namespace PlayerTags +{ + /// + /// Generates names based on an existing list of words. + /// + public class RandomNameGenerator + { + private const string c_AdjectivesPath = "Resources/Words/Adjectives.txt"; + private string[]? m_Adjectives; + + private const string c_NounsPath = "Resources/Words/Nouns.txt"; + private string[]? m_Nouns; + + private Dictionary m_GeneratedNames = new Dictionary(); + + private string? PluginDirectory + { + get { return Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); } + } + + public RandomNameGenerator() + { + try + { + m_Adjectives = File.ReadAllLines($"{PluginDirectory}/{c_AdjectivesPath}"); + m_Nouns = File.ReadAllLines($"{PluginDirectory}/{c_NounsPath}"); + } + catch (Exception ex) + { + PluginLog.Error(ex, $"RandomNameGenerator failed to create"); + } + } + + /// + /// Generates a name for the given string. + /// + /// The string to generate a name for. + /// A generated name. + public string? GetGeneratedName(string str) + { + if (m_Adjectives == null || m_Nouns == null) + { + return null; + } + + int hash = GetDeterministicHashCode(str); + + if (!m_GeneratedNames.ContainsKey(hash)) + { + // Use the seed as the hash so that player always gets the same name + Random random = new Random(hash); + var adjective = m_Adjectives[random.Next(0, m_Adjectives.Length)]; + var noun = m_Nouns[random.Next(0, m_Nouns.Length)]; + var generatedName = $"{adjective} {noun}"; + + m_GeneratedNames[hash] = CultureInfo.CurrentCulture.TextInfo.ToTitleCase(generatedName); + } + + return m_GeneratedNames[hash]; + } + + /// + /// 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); + } + } + } +} diff --git a/PlayerTags/Resources/Promo/Chat_1.png b/PlayerTags/Resources/Promo/Chat_1.png new file mode 100644 index 0000000..1022105 Binary files /dev/null and b/PlayerTags/Resources/Promo/Chat_1.png differ diff --git a/PlayerTags/Resources/Promo/Nameplates_1.png b/PlayerTags/Resources/Promo/Nameplates_1.png new file mode 100644 index 0000000..b9bdf66 Binary files /dev/null and b/PlayerTags/Resources/Promo/Nameplates_1.png differ diff --git a/PlayerTags/Resources/Strings.Designer.cs b/PlayerTags/Resources/Strings.Designer.cs new file mode 100644 index 0000000..4cecfcb --- /dev/null +++ b/PlayerTags/Resources/Strings.Designer.cs @@ -0,0 +1,828 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace PlayerTags.Resources { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class Strings { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Strings() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("PlayerTags.Resources.Strings", typeof(Strings).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Color. + /// + public static string Loc_CustomColorConfig_IsEnabled { + get { + return ResourceManager.GetString("Loc_CustomColorConfig_IsEnabled", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enable a custom color.. + /// + public static string Loc_CustomColorConfig_IsEnabled_Description { + get { + return ResourceManager.GetString("Loc_CustomColorConfig_IsEnabled_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Apply to game object names. + /// + public static string Loc_CustomTagConfig_FormattedGameObjectNames { + get { + return ResourceManager.GetString("Loc_CustomTagConfig_FormattedGameObjectNames", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to A list of game object names to apply the tag to, separated by commas. E.g. "Cloud Strife, Tifa Lockhart". + /// + public static string Loc_CustomTagConfig_FormattedGameObjectNames_Description { + get { + return ResourceManager.GetString("Loc_CustomTagConfig_FormattedGameObjectNames_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Name. + /// + public static string Loc_CustomTagConfig_Name { + get { + return ResourceManager.GetString("Loc_CustomTagConfig_Name", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The name of the tag to display.. + /// + public static string Loc_CustomTagConfig_Name_Description { + get { + return ResourceManager.GetString("Loc_CustomTagConfig_Name_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Free company visibility. + /// + public static string Loc_FreeCompanyNameplateVisibility { + get { + return ResourceManager.GetString("Loc_FreeCompanyNameplateVisibility", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Default. + /// + public static string Loc_FreeCompanyNameplateVisibility_Default { + get { + return ResourceManager.GetString("Loc_FreeCompanyNameplateVisibility_Default", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The free company on nameplates will only be visible when the character is in a free company.. + /// + public static string Loc_FreeCompanyNameplateVisibility_Default_Description { + get { + return ResourceManager.GetString("Loc_FreeCompanyNameplateVisibility_Default_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The visibility of the free company on nameplates.. + /// + public static string Loc_FreeCompanyNameplateVisibility_Description { + get { + return ResourceManager.GetString("Loc_FreeCompanyNameplateVisibility_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Never visible. + /// + public static string Loc_FreeCompanyNameplateVisibility_Never { + get { + return ResourceManager.GetString("Loc_FreeCompanyNameplateVisibility_Never", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The free company on nameplates will never be visible.. + /// + public static string Loc_FreeCompanyNameplateVisibility_Never_Description { + get { + return ResourceManager.GetString("Loc_FreeCompanyNameplateVisibility_Never_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Randomly generate player names. + /// + public static string Loc_IsPlayerNameRandomlyGenerated { + get { + return ResourceManager.GetString("Loc_IsPlayerNameRandomlyGenerated", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use tags to replace every player's name with a randomly generated one. Helpful for preserving anonymity when taking screenshots.. + /// + public static string Loc_IsPlayerNameRandomlyGenerated_Description { + get { + return ResourceManager.GetString("Loc_IsPlayerNameRandomlyGenerated_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Data element. + /// + public static string Loc_NameplateElement { + get { + return ResourceManager.GetString("Loc_NameplateElement", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to A data element on nameplates.. + /// + public static string Loc_NameplateElement_Description { + get { + return ResourceManager.GetString("Loc_NameplateElement_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Free company. + /// + public static string Loc_NameplateElement_FreeCompany { + get { + return ResourceManager.GetString("Loc_NameplateElement_FreeCompany", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The free company on nameplates.. + /// + public static string Loc_NameplateElement_FreeCompany_Description { + get { + return ResourceManager.GetString("Loc_NameplateElement_FreeCompany_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Name. + /// + public static string Loc_NameplateElement_Name { + get { + return ResourceManager.GetString("Loc_NameplateElement_Name", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The name on nameplates.. + /// + public static string Loc_NameplateElement_Name_Description { + get { + return ResourceManager.GetString("Loc_NameplateElement_Name_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Title. + /// + public static string Loc_NameplateElement_Title { + get { + return ResourceManager.GetString("Loc_NameplateElement_Title", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The title on nameplates.. + /// + public static string Loc_NameplateElement_Title_Description { + get { + return ResourceManager.GetString("Loc_NameplateElement_Title_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Role. + /// + public static string Loc_Role { + get { + return ResourceManager.GetString("Loc_Role", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to A character role.. + /// + public static string Loc_Role_Description { + get { + return ResourceManager.GetString("Loc_Role_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to DPS. + /// + public static string Loc_Role_DPS { + get { + return ResourceManager.GetString("Loc_Role_DPS", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The DPS role.. + /// + public static string Loc_Role_DPS_Description { + get { + return ResourceManager.GetString("Loc_Role_DPS_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Healer. + /// + public static string Loc_Role_Healer { + get { + return ResourceManager.GetString("Loc_Role_Healer", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The healer role.. + /// + public static string Loc_Role_Healer_Description { + get { + return ResourceManager.GetString("Loc_Role_Healer_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Land/Hand. + /// + public static string Loc_Role_LandHand { + get { + return ResourceManager.GetString("Loc_Role_LandHand", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The land/hand role.. + /// + public static string Loc_Role_LandHand_Description { + get { + return ResourceManager.GetString("Loc_Role_LandHand_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Tank. + /// + public static string Loc_Role_Tank { + get { + return ResourceManager.GetString("Loc_Role_Tank", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The tank role.. + /// + public static string Loc_Role_Tank_Description { + get { + return ResourceManager.GetString("Loc_Role_Tank_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enabled. + /// + public static string Loc_RoleOverrideConfig_IsEnabled { + get { + return ResourceManager.GetString("Loc_RoleOverrideConfig_IsEnabled", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enable the tag for this role.. + /// + public static string Loc_RoleOverrideConfig_IsEnabled_Description { + get { + return ResourceManager.GetString("Loc_RoleOverrideConfig_IsEnabled_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Name. + /// + public static string Loc_RoleOverrideConfig_Name { + get { + return ResourceManager.GetString("Loc_RoleOverrideConfig_Name", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The name to use for this role when the format is set to use role names.. + /// + public static string Loc_RoleOverrideConfig_Name_Description { + get { + return ResourceManager.GetString("Loc_RoleOverrideConfig_Name_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Tag format. + /// + public static string Loc_RoleTagFormat { + get { + return ResourceManager.GetString("Loc_RoleTagFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Abbreviated job name. + /// + public static string Loc_RoleTagFormat_AbbreviatedJobName { + get { + return ResourceManager.GetString("Loc_RoleTagFormat_AbbreviatedJobName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The tag will appear as an abbreviated job name, e.g. SAM, WHM, GNB.. + /// + public static string Loc_RoleTagFormat_AbbreviatedJobName_Description { + get { + return ResourceManager.GetString("Loc_RoleTagFormat_AbbreviatedJobName_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The format that the role tag should be displayed as.. + /// + public static string Loc_RoleTagFormat_Description { + get { + return ResourceManager.GetString("Loc_RoleTagFormat_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Job name. + /// + public static string Loc_RoleTagFormat_JobName { + get { + return ResourceManager.GetString("Loc_RoleTagFormat_JobName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The tag will appear as a job name in English, e.g. Samurai, White Mage, Gunbreaker.. + /// + public static string Loc_RoleTagFormat_JobName_Description { + get { + return ResourceManager.GetString("Loc_RoleTagFormat_JobName_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Role name. + /// + public static string Loc_RoleTagFormat_RoleName { + get { + return ResourceManager.GetString("Loc_RoleTagFormat_RoleName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The tag will appear as the configured role name, e.g. DPS, Healer, Tank.. + /// + public static string Loc_RoleTagFormat_RoleName_Description { + get { + return ResourceManager.GetString("Loc_RoleTagFormat_RoleName_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add custom tag. + /// + public static string Loc_Static_AddCustomTag { + get { + return ResourceManager.GetString("Loc_Static_AddCustomTag", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Chat tag. + /// + public static string Loc_Static_ChatTag { + get { + return ResourceManager.GetString("Loc_Static_ChatTag", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Custom tags. + /// + public static string Loc_Static_CustomTags { + get { + return ResourceManager.GetString("Loc_Static_CustomTags", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Development. + /// + public static string Loc_Static_Development { + get { + return ResourceManager.GetString("Loc_Static_Development", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to General. + /// + public static string Loc_Static_General { + get { + return ResourceManager.GetString("Loc_Static_General", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Jobs. + /// + public static string Loc_Static_Jobs { + get { + return ResourceManager.GetString("Loc_Static_Jobs", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Nameplates. + /// + public static string Loc_Static_Nameplates { + get { + return ResourceManager.GetString("Loc_Static_Nameplates", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Nameplate tag. + /// + public static string Loc_Static_NameplateTag { + get { + return ResourceManager.GetString("Loc_Static_NameplateTag", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No custom tags added.. + /// + public static string Loc_Static_NoCustomTagsAdded { + get { + return ResourceManager.GetString("Loc_Static_NoCustomTagsAdded", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Player Tags. + /// + public static string Loc_Static_PluginName { + get { + return ResourceManager.GetString("Loc_Static_PluginName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Remove custom tag. + /// + public static string Loc_Static_RemoveCustomTag { + get { + return ResourceManager.GetString("Loc_Static_RemoveCustomTag", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Role and job tags. + /// + public static string Loc_Static_RoleAndJobTags { + get { + return ResourceManager.GetString("Loc_Static_RoleAndJobTags", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Roles. + /// + public static string Loc_Static_Roles { + get { + return ResourceManager.GetString("Loc_Static_Roles", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to String position. + /// + public static string Loc_StringPosition { + get { + return ResourceManager.GetString("Loc_StringPosition", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to After. + /// + public static string Loc_StringPosition_After { + get { + return ResourceManager.GetString("Loc_StringPosition_After", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The tag will be positioned after the data element.. + /// + public static string Loc_StringPosition_After_Description { + get { + return ResourceManager.GetString("Loc_StringPosition_After_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Before. + /// + public static string Loc_StringPosition_Before { + get { + return ResourceManager.GetString("Loc_StringPosition_Before", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The tag will be positioned before the data element.. + /// + public static string Loc_StringPosition_Before_Description { + get { + return ResourceManager.GetString("Loc_StringPosition_Before_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The position in a string where tags will be displayed.. + /// + public static string Loc_StringPosition_Description { + get { + return ResourceManager.GetString("Loc_StringPosition_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to None. + /// + public static string Loc_StringPosition_None { + get { + return ResourceManager.GetString("Loc_StringPosition_None", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The tag will not be positioned in the string.. + /// + public static string Loc_StringPosition_None_Description { + get { + return ResourceManager.GetString("Loc_StringPosition_None_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Replace. + /// + public static string Loc_StringPosition_Replace { + get { + return ResourceManager.GetString("Loc_StringPosition_Replace", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The tag will replace its data element.. + /// + public static string Loc_StringPosition_Replace_Description { + get { + return ResourceManager.GetString("Loc_StringPosition_Replace_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Title position. + /// + public static string Loc_TitleNameplatePosition { + get { + return ResourceManager.GetString("Loc_TitleNameplatePosition", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Always above the name. + /// + public static string Loc_TitleNameplatePosition_AlwaysAboveName { + get { + return ResourceManager.GetString("Loc_TitleNameplatePosition_AlwaysAboveName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The title on nameplates will always be positioned above the name.. + /// + public static string Loc_TitleNameplatePosition_AlwaysAboveName_Description { + get { + return ResourceManager.GetString("Loc_TitleNameplatePosition_AlwaysAboveName_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Always below the name. + /// + public static string Loc_TitleNameplatePosition_AlwaysBelowName { + get { + return ResourceManager.GetString("Loc_TitleNameplatePosition_AlwaysBelowName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The title on nameplates will always be positioned below the name.. + /// + public static string Loc_TitleNameplatePosition_AlwaysBelowName_Description { + get { + return ResourceManager.GetString("Loc_TitleNameplatePosition_AlwaysBelowName_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Default. + /// + public static string Loc_TitleNameplatePosition_Default { + get { + return ResourceManager.GetString("Loc_TitleNameplatePosition_Default", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The title on nameplates will be positioned depending on the title.. + /// + public static string Loc_TitleNameplatePosition_Default_Description { + get { + return ResourceManager.GetString("Loc_TitleNameplatePosition_Default_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The position of the title on nameplates.. + /// + public static string Loc_TitleNameplatePosition_Description { + get { + return ResourceManager.GetString("Loc_TitleNameplatePosition_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Title visibility. + /// + public static string Loc_TitleNameplateVisibility { + get { + return ResourceManager.GetString("Loc_TitleNameplateVisibility", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Always. + /// + public static string Loc_TitleNameplateVisibility_Always { + get { + return ResourceManager.GetString("Loc_TitleNameplateVisibility_Always", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The title on nameplates will always be visible, even when the character does not have a title.. + /// + public static string Loc_TitleNameplateVisibility_Always_Description { + get { + return ResourceManager.GetString("Loc_TitleNameplateVisibility_Always_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Default. + /// + public static string Loc_TitleNameplateVisibility_Default { + get { + return ResourceManager.GetString("Loc_TitleNameplateVisibility_Default", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The title on nameplates will only be visible when the character has a title.. + /// + public static string Loc_TitleNameplateVisibility_Default_Description { + get { + return ResourceManager.GetString("Loc_TitleNameplateVisibility_Default_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The visibility of the title on nameplates.. + /// + public static string Loc_TitleNameplateVisibility_Description { + get { + return ResourceManager.GetString("Loc_TitleNameplateVisibility_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Never. + /// + public static string Loc_TitleNameplateVisibility_Never { + get { + return ResourceManager.GetString("Loc_TitleNameplateVisibility_Never", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The title on nameplates will never visible.. + /// + public static string Loc_TitleNameplateVisibility_Never_Description { + get { + return ResourceManager.GetString("Loc_TitleNameplateVisibility_Never_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to When it has tags. + /// + public static string Loc_TitleNameplateVisibility_WhenHasTags { + get { + return ResourceManager.GetString("Loc_TitleNameplateVisibility_WhenHasTags", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The title on nameplates will only be visible when it has tags.. + /// + public static string Loc_TitleNameplateVisibility_WhenHasTags_Description { + get { + return ResourceManager.GetString("Loc_TitleNameplateVisibility_WhenHasTags_Description", resourceCulture); + } + } + } +} diff --git a/PlayerTags/Resources/Strings.resx b/PlayerTags/Resources/Strings.resx new file mode 100644 index 0000000..00c1356 --- /dev/null +++ b/PlayerTags/Resources/Strings.resx @@ -0,0 +1,375 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Player Tags + + + General + + + Development + + + Custom tags + + + Add custom tag + + + Remove custom tag + + + No custom tags added. + + + Role and job tags + + + Roles + + + Jobs + + + Chat tag + + + Nameplate tag + + + Nameplates + + + Randomly generate player names + + + Use tags to replace every player's name with a randomly generated one. Helpful for preserving anonymity when taking screenshots. + + + Enabled + + + Enable the tag for this role. + + + Name + + + The name to use for this role when the format is set to use role names. + + + Color + + + Enable a custom color. + + + Name + + + The name of the tag to display. + + + Apply to game object names + + + A list of game object names to apply the tag to, separated by commas. E.g. "Cloud Strife, Tifa Lockhart" + + + Free company visibility + + + The visibility of the free company on nameplates. + + + Default + + + The free company on nameplates will only be visible when the character is in a free company. + + + Never visible + + + The free company on nameplates will never be visible. + + + Data element + + + A data element on nameplates. + + + Free company + + + The free company on nameplates. + + + Name + + + The name on nameplates. + + + Title + + + The title on nameplates. + + + Tag format + + + The format that the role tag should be displayed as. + + + Abbreviated job name + + + Job name + + + The tag will appear as an abbreviated job name, e.g. SAM, WHM, GNB. + + + The tag will appear as a job name in English, e.g. Samurai, White Mage, Gunbreaker. + + + Role name + + + The tag will appear as the configured role name, e.g. DPS, Healer, Tank. + + + Role + + + A character role. + + + DPS + + + The DPS role. + + + Healer + + + The healer role. + + + Land/Hand + + + The land/hand role. + + + Tank + + + The tank role. + + + String position + + + The position in a string where tags will be displayed. + + + After + + + The tag will be positioned after the data element. + + + Before + + + The tag will be positioned before the data element. + + + None + + + The tag will not be positioned in the string. + + + Replace + + + The tag will replace its data element. + + + Title position + + + The position of the title on nameplates. + + + Always above the name + + + The title on nameplates will always be positioned above the name. + + + Always below the name + + + The title on nameplates will always be positioned below the name. + + + Default + + + The title on nameplates will be positioned depending on the title. + + + Title visibility + + + The visibility of the title on nameplates. + + + Always + + + The title on nameplates will always be visible, even when the character does not have a title. + + + Default + + + The title on nameplates will only be visible when the character has a title. + + + Never + + + The title on nameplates will never visible. + + + When it has tags + + + The title on nameplates will only be visible when it has tags. + + \ No newline at end of file diff --git a/PlayerTags/Resources/Words/Adjectives.txt b/PlayerTags/Resources/Words/Adjectives.txt new file mode 100644 index 0000000..b517add --- /dev/null +++ b/PlayerTags/Resources/Words/Adjectives.txt @@ -0,0 +1,3060 @@ +able +abnormal +abominable +aboriginal +abrupt +absent +absolute +abstract +absurd +abundant +abusive +abysmal +academic +acceptable +accessible +according +accurate +accused +acidic +acoustic +acrobatic +action +active +actual +acute +addictive +additional +adept +adequate +administrative +adorable +adult +advanced +advantageous +adverse +aerial +aerobic +aerodynamic +affectionate +afflicted +affordable +afraid +after +afternoon +aged +ageless +agent +aggressive +agile +agitable +ago +agonizing +agreeable +agricultural +aimless +airborne +airline +airtight +airy +alert +algorithmic +alien +alive +alleged +allied +alluring +alone +alpha +alphabetical +alphanumeric +alright +alterior +alternative +altruistic +amateur +amazing +ambient +ambiguous +ambitious +amusing +anaemic +ancestral +ancient +androgenous +anecdotal +angelic +angry +angsty +angular +animal +animated +annihilated +annoying +annual +anonymous +another +antisocial +anxious +any +apart +apathetic +aphrodisiac +appalling +apparent +appealing +appetizing +applicable +appreciative +apprehensive +appropriate +arcane +archaic +argumentative +arid +aristocratic +armed +armless +aromatic +arrogant +artistic +arty +ascendant +ashamed +asinine +asleep +asphyxiated +aspiring +assertive +associated +associative +asthmatic +astonishing +astounding +astral +astute +athletic +atmospheric +atomic +atrocious +attentive +attractive +augmented +aural +auspicious +authoritarian +authoritative +authorized +automatic +available +average +aware +away +awesome +awful +awkward +background +backward +bacterial +bad +baffling +baggy +balanced +bald +balding +banal +barbaric +bare +barren +bashful +basic +batty +beaten +beautiful +becoming +bedridden +beefy +beginning +behavioral +belated +beloved +bemused +bendy +beneficial +benevolent +benign +berserk +best +beta +better +bewildered +bewitched +big +biggish +bigheaded +bigoted +binary +biological +bionic +birdbrained +bisexual +bitter +bizarre +black +blameless +blameworthy +bland +blank +blasphemous +bleached +bleak +blessed +blind +blissful +bloated +blocky +blonde +bloodshot +bloodthirsty +blooming +blotchy +blue +blunt +blushing +boastful +boiled +bold +bolstered +bony +bored +boring +born +both +bothersome +bottom +bouncy +boundless +boyish +braided +brainy +brash +bratty +brave +brazen +breaded +breakable +breathless +breathtaking +breezy +brief +bright +brilliant +brisk +broad +broken +brotherly +brown +brunette +brusque +brutal +brutish +bubbly +budget +bulbous +bulletproof +bumpy +buoyant +bureaucratic +burgundy +burning +burnt +bushy +busy +buttery +caffeinated +calm +calorific +camouflage +cancerous +candid +candied +canine +cannibalistic +capable +capital +captivating +capturable +cardboard +cardiac +carefree +careful +careless +caring +catastrophic +catchy +categorical +causable +caustic +cautious +celebrated +celebrative +celebratory +celestial +cellular +centered +central +centralized +certain +certified +chalky +challenging +chance +changeable +chaotic +character +characteristic +characterless +chargeable +charismatic +charming +charred +chatty +chauvinistic +cheap +checkered +cheeky +cheerful +cheerless +cheery +cheesy +chemical +chewable +chewy +childish +childless +childlike +childproof +chilled +chilly +chiselled +chocolatey +choice +choosey +choppy +chorded +chubby +chummy +chunky +churchless +circular +circulatory +circumstantial +citable +citric +civil +civilized +claimable +classic +classical +classified +classless +classy +claustrophobic +clean +cleanable +clear +clever +climactic +clingy +clinical +close +cloudy +clueless +cluttered +coarse +coastal +cocky +coercive +cognitive +coherent +cohesive +cold +collaborative +collapsible +collectable +colonial +colorful +colossal +combative +combustible +comely +comfortable +comfy +comical +commendable +commercial +commiserative +common +communal +communist +compact +compassionate +compatible +compensatory +competent +competitive +complacent +complete +complex +compliant +complicated +comprehensive +compulsive +compulsory +computable +concave +conceited +conceivable +conceptual +concerned +concise +conclusive +concurrent +condemnable +conditioned +confident +confidential +confined +confirmatory +confounded +confused +connect +conscious +consecutive +consensual +conservable +conservative +considerate +consistent +conspicuous +constant +constrained +constructible +consumable +contagious +containable +contaminated +content +contentious +contestable +contextual +contiguous +continental +continuous +contorted +contraceptive +contractible +contradictory +contrary +contrived +controlled +controversial +convenient +conventional +convergent +conversational +convertible +convincing +cookable +cool +cooperative +corelative +corner +coronary +correct +correctable +correctional +corrective +corrosive +corrupt +corruptible +cosmic +cosmological +costly +counterfeit +courageous +courteous +covert +cowardly +cramped +cranky +crap +crass +crazy +creamy +creative +credible +creepy +criminal +cringeworthy +critical +crude +crushing +crusty +crying +cuddly +culinary +cultish +cultural +cumbersome +cumulative +cunning +curable +curative +curious +current +cursed +cursory +curvy +customary +cute +cyberpunk +cynical +cystic +daft +dainty +damaged +damaging +damp +dangerous +dank +daring +dark +dashing +dastardly +daughter +daughterly +daydreamy +dazzling +deactivated +dead +deadly +dear +deathly +debatable +decadent +deceased +deceitful +decent +decisive +declinable +declining +decomposed +decrepit +dedicated +deductible +deep +defeated +defective +defenceless +defensive +defiable +defiant +definable +definite +definitive +deflated +deformed +defunct +degenerative +degraded +degrading +dehydrated +delayed +deliberate +delicate +delicious +delightful +delirious +delusional +demanding +demented +democratic +demonic +demonstrational +demonstrative +deniable +dense +dental +dependent +depictive +deportable +depraved +depressing +depressive +deprived +derelict +derivable +derogatory +descendible +descriptive +deserted +deserving +designer +desirable +desired +desperate +despicable +despisable +destined +destroyable +destructible +destructive +detachable +detailed +detainable +detectable +deteriorative +deterministic +detestable +detrimental +developing +devoted +devout +dexterous +diabetic +diabolic +diagnosable +diagonal +dietary +different +difficult +digestible +digestive +digital +dignified +dilapidated +diminished +diplomatic +direct +dirty +disabled +disadvantaged +disagreeable +disappointed +disappointing +disastrous +discernible +disciplinary +discolored +discreet +discriminatory +discussable +disdainful +diseased +disenfranchised +disgraceful +disguisable +disgusted +disgusting +dishonest +dishonorable +disillusioned +disingenuous +disinterested +dislikeable +disloyal +dismal +dismissible +dismissive +disobedient +disorganized +displeased +displeasing +displeasing +disposable +disproportional +disprovable +disputable +disqualifiable +disregardful +disreputable +disrespectful +disruptive +dissatisfactory +dissatisfied +dissected +dissectible +dissimilar +distant +distasteful +distinct +distinctive +distinguished +distorted +distracted +distractible +distributable +distrustful +disturbed +disturbing +divergent +diverse +divine +dizzy +docile +dogmatic +domestic +dominant +dominating +doomed +dopey +dormant +double +doubtful +downright +downtown +dozy +drab +draconian +draconic +drained +dramatic +drastic +dreadful +dreamy +dress +drinkable +dripping +droopy +drowsy +drunken +dry +dubious +due +dumb +durable +dutiful +dwarfish +dying +dynamic +dynastic +dysfunctional +dystopian +each +eager +earnest +earthy +eastern +easy +eccentric +eclectic +economic +economical +ecstatic +edgy +edible +educated +educational +eerie +effective +effeminate +efficient +effortless +egotistical +egregious +elaborate +elastic +elated +elderly +electoral +electrical +electrified +electronic +elegant +elemental +elementary +elevated +eligible +eliminable +elite +eloquent +elusive +embarrassed +embedded +embracive +emergency +emotional +emotionless +emotive +emotive +empathetic +empty +enchanted +endagered +endangered +endless +endurable +energetic +engaging +enhanced +enjoyable +enlightened +enormous +enough +enraged +enslaved +enthusiastic +entire +entitled +environmental +equal +equipable +equivalent +eradicable +erect +erectable +erectile +ergonomic +erosive +escapable +esoteric +essential +established +eternal +ethereal +ethical +ethnic +evadable +evasive +even +evening +eventful +everlasting +every +everyday +evident +evil +evolvable +exacerbated +exact +exaggerated +exalted +excellent +exceptional +excessive +exchangeable +excitable +excited +exciting +exclusionary +exclusive +excusable +executable +exemplary +exemptible +exhausted +exhaustible +exhaustive +exilable +existent +existential +existentialistic +exotic +expandable +expansive +expectant +expected +expensive +experienced +experimental +expert +explicable +exploding +exploitable +exploitative +exploitatory +explorable +explosive +exponential +exposed +express +expressible +exquisite +extended +extended +extendible +extensible +exterior +exterminable +external +extinct +extinguishable +extra +extraordinary +extreme +extroverted +exuberant +exultant +eyeable +fabulous +facial +faded +failed +faint +fair +faithful +faithless +fake +false +falsifiable +familiar +famished +famous +fancy +fantastic +far +fascinating +fascist +fashionable +fast +fat +fatal +fatherly +fathomable +faulty +favorite +fearless +fearsome +feasible +federal +feeble +feeling +feisty +fellow +female +feminine +feral +ferocious +fertile +festive +fibrous +fickle +fictional +fidgety +fiendish +fierce +fiery +fightable +figurable +fillable +filmable +filthy +final +financial +fine +finicky +firebreathing +fireproof +firm +first +fishy +fit +fizzy +flabby +flacid +flaky +flamboyant +flammable +flappy +flashy +flat +flattering +flavored +flavorful +flavorless +flavorsome +flawless +fleeting +fleshy +flexible +flighty +flimsy +flippant +flirtatious +flirty +floatable +floaty +floggable +floodable +floppy +floral +flourishing +flowery +fluent +fluffy +fluid +fluorescent +flying +foggy +foolish +forbidden +foreign +forensic +foreseeable +forfeitable +forgeable +forgetful +forgivable +forgotten +formal +former +formidable +forsaken +forthcoming +fortified +fortunate +forward +foulmouthed +foxy +fragile +fragrant +frail +framable +frantic +fraternal +freakish +freaky +freckled +freckly +free +freezable +frenzied +frequent +fresh +fretful +fried +friendly +frightened +frightening +frigid +frilly +frisky +frivolous +frizzy +front +frostbitten +frostbreathing +frosted +frosty +frothy +frozen +frugal +fruitful +fruity +fucked +full +fumbling +fun +functional +fundamental +fungal +funny +furious +furry +fussy +futile +future +futuristic +fuzzy +gainable +gallant +gangrenous +gargantuan +garnishable +gaseous +gassy +gatherable +gaunt +gay +gelatinous +general +generic +generous +genetic +genocidal +gentle +genuine +geometric +germless +germproof +ghastly +ghetto +ghostly +giant +giddy +gifted +gigantic +giggly +gimmicky +girly +glad +glamorous +glass +glassy +glazed +gleaming +gleeful +glistening +glittery +global +gloomy +glorious +glossy +glowing +gluey +gnarly +godforsaken +godless +godly +gold +golden +good +gooey +goofy +goosebumpy +goosepimply +gorgeous +gossipy +governable +grabbable +graceful +graceless +gracious +gradable +grainy +grand +grandiose +graphic +grassy +grateful +grave +gray +greased +greasy +great +greedy +green +gregarious +grieving +grievous +grilled +grim +grisly +groggy +groovy +gross +grotesque +grouchy +growable +growing +gruesome +grunt +guardable +guessable +guidable +guilty +gullible +gummy +gushy +gutsy +habitual +hairsplitting +hairy +half +hallowed +hallucinogenic +handcrafted +handheld +handmade +handsewn +handsome +handwoven +handwritten +handy +happy +hard +harmful +harmless +harsh +hasty +hated +hateful +haughty +haunted +hazardous +hazy +head +headless +headstrong +healthy +heartbreaking +heartbroken +heartless +heated +heavenly +heavy +heightened +heinous +helpful +helpless +herbal +heroic +hesitant +hidden +hideous +hieroglyphic +high +hilarious +hip +historic +historical +holiday +holistic +hollow +holographic +holy +homeless +homemade +homesick +homicidal +homoerotic +homosexual +honest +honorable +honorary +hopeful +horizontal +hormonal +horrendous +horrible +horrid +horrific +horrified +horrifying +horror +hostile +hot +hotheaded +hour +huge +human +humane +humble +humid +humiliated +humorous +hungry +hurried +hurtful +hydrated +hygienic +hygienic +hyperactive +hyperbolic +hypnotic +hypnotised +hypnotizing +hypoactive +hypothetical +hysterical +iconic +icy +ideal +idealistic +identical +ideological +idiotic +idle +ignitable +ignorant +ill +illegal +illegible +illicit +illiterate +illogical +illtempered +illustrious +imaginative +immaculate +immature +immediate +immense +imminent +immodest +immoral +immortal +immovable +immune +impatient +impeccable +impending +imperialistic +impertinent +impolite +important +impossible +impotent +impoverished +imprecise +impressed +impressive +improbable +improper +improved +improvised +impudent +impulsive +inaccurate +inadequate +inadvertent +inane +inappropriate +inartistic +inattentive +inbred +incendiary +incensed +incestuous +incident +incoherent +incompatible +incompetent +incomplete +incomprehensible +inconclusive +inconsequential +inconsiderate +inconspicuous +inconvenient +incorporated +increased +incredible +indecipherable +indecisive +indefinite +independent +indestructible +indifferent +indigenous +indiscreet +indistinct +indistinctive +individual +individualistic +indomitable +indoor +industrial +industrialized +inedible +ineffective +inefficient +inept +inevitable +inexpensive +inexperienced +infamous +infamous +infantile +infatuated +infectious +inferior +infertile +infinite +inflammable +inflatable +influential +informal +ingenious +ingenuous +inglorious +inherent +inherited +inhuman +inhumane +initial +injured +inky +inland +inner +innocent +innovative +inoperable +inquisitive +insane +insecure +insensitive +inside +insidious +insignificant +insincere +insipid +insistent +insolent +insomniac +inspirational +instant +institutional +instrumental +insufficient +insulted +insulting +insured +intact +intangible +integral +intellectual +intelligent +intelligible +intense +intensive +interactive +interested +interesting +interior +intermediate +internal +international +interplanetary +intestinal +intimate +intolerable +intolerant +intoxicated +intriguing +introverted +intrusive +invaluable +invasive +inverse +invincible +invisible +involuntary +involved +invulnerable +ironic +irrational +irregular +irrelevant +irresistible +irresponsible +irreversible +irritable +irritated +itchy +jagged +jealous +jellied +jobless +joint +jolly +joyous +judgmental +juicy +junior +juvenile +kamikaze +kaput +keen +kind +kinetic +kingly +kitchen +knitted +knotted +knowing +knowledgeable +known +knuckleheaded +laborious +lace +lacklustre +ladylike +lagging +lame +large +last +late +latter +laughable +lawful +lawless +lazy +leading +leafy +learned +least +leather +leathery +leery +left +legal +legendary +legible +legislative +leisurable +leisureless +lengthy +lesbian +less +lethal +lethargic +lexical +liable +liberal +liberated +liberating +lifeless +light +lightweight +likable +likely +limbless +limitless +limp +linear +linguistic +literal +literary +little +live +lively +livid +living +loathsome +local +logical +lonely +lonesome +long +loopy +loose +lopsided +lost +loud +lousy +lovable +loveless +lovely +loverless +lovesick +loving +low +lower +lowly +loyal +lubricated +lucid +lucky +ludicrous +lukewarm +luminescent +luminous +lumpy +lunar +luscious +lush +lustered +lustrous +luxurious +mad +maddening +magical +magnetic +magnificent +main +majestic +major +male +maleficent +malevolent +malicious +malignant +maligned +malleable +malnourished +maniacal +manic +manipulative +manly +marginal +marital +marked +married +marvellous +masculine +masochistic +massive +master +masterful +material +maternal +mathematical +matriarchal +matricidal +matrimonial +mature +maximum +meager +meagre +mean +meaningful +meaningless +meanspirited +measly +meaty +mechanical +medical +medicinal +medicore +medium +mega +megalomaniacal +melancholic +mellow +melodic +melodramatic +melting +menial +menstrual +mental +merciful +merry +messy +metallic +microbial +microbicidal +microscopic +middle +mighty +mild +militant +militaristic +milky +minced +mindful +mindless +mini +miniature +minimal +minimum +minor +miraculous +misandrous +miscellaneous +mischievous +miserable +misleading +misogynistic +misogynous +missing +mission +mistrustful +misty +misunderstood +mobile +moderate +modern +modest +moist +monetary +monogamous +monotonous +monstrous +monumental +moody +moral +morbid +moreish +moronic +mossy +mother +motherly +motionless +motor +mountable +mountain +mountainous +moving +much +muddy +muffled +multiplayer +multiple +multipurpose +mundane +murderous +murky +mushy +musical +mutated +mutual +muzzled +mysterious +mystical +mythic +mythical +naive +naked +narcissistic +narrow +nasty +national +native +natural +naughty +nauseating +nauseous +nautical +nearby +neat +necessary +needless +needy +negative +negligent +neither +nerdy +nervous +neurotic +neutral +neutralizing +new +next +nice +nightmarish +nihilistic +nimble +nocturnal +nominal +nonchalant +nonessential +nonexistent +nonstick +nonstop +normal +northern +nosy +notable +noteworthy +notorious +novel +novice +nuclear +nude +numb +numbing +numerical +numerous +nutritious +nylon +obedient +obese +objective +oblivious +obnoxious +obscene +obscure +observant +obsessive +obsidian +obsolete +obtuse +obtuse +obvious +occasional +occupational +oceanic +odd +odourless +offbeat +offended +offensive +official +oiled +oily +okay +old +older +olympic +ominous +omnipotent +omnipresent +omniscient +onerous +ongoing +only +oozy +open +opening +operable +operational +opinionated +opposite +oppressed +oppressive +optic +optical +optimal +optimistic +oral +orchestral +ordinary +organizational +orgasmic +oriental +original +ornamental +ornate +orthodox +ostentatious +ostracized +other +otherwise +outdoor +outer +outgoing +outraged +outrageous +outside +outstanding +over +overall +overconfident +overcooked +overjoyed +overpriced +overrated +overseas +overwhelming +overzealous +own +oxymoronic +pacified +pacifist +painful +painstaking +painted +pale +panicky +papery +parallel +paralysed +paralyzed +paralyzing +paranoid +paranormal +parasitic +parking +partial +particular +party +passionate +passive +past +patchy +paternal +pathetic +patient +patriarchal +patricidal +patriotic +patronizing +peaceful +peachy +peckish +peculiar +pedantic +penal +peppery +perfect +perilous +period +permanent +perpendicular +perpetual +perplexed +perseverant +personal +persuasive +pertinent +perverted +pesky +pessimistic +pestilent +petrified +petty +petulant +pharmaceutical +phenomenal +philanthropic +phlegmy +phony +photosensitive +physical +physiological +picky +picturesque +piercing +pitiful +plain +plane +planetary +plastic +platonic +plausible +playful +pleasable +pleasant +pleased +pleasing +pleasurable +plenty +plump +plus +poetic +pointless +polar +polished +polite +political +polluted +pompous +poor +popular +portable +portrayable +posh +positive +possessive +possible +postal +potent +potential +powdery +powerful +practical +pragmatic +preachy +precious +precise +predatory +predominant +preemptive +preferred +pregnant +prehistoric +preliminary +premature +premier +premium +preoccupied +prepared +prepubescent +present +presumptuous +pretend +pretentious +pretty +previous +priceless +pricey +prickly +primary +prime +primitive +princely +prior +prismatic +prissy +pristine +private +privatized +privileged +prize +probable +problematic +productive +professional +proficient +profitable +profound +progressive +progressive +prolific +promethean +prominent +promising +proof +proper +proportional +proposed +prosperous +protected +protective +proud +prudent +psychedelic +psychiatric +psychic +psychological +psychotic +pubescent +pubic +public +publicized +punctual +puny +pure +purple +putrid +quaint +qualified +queenly +quick +quiet +quintessential +rabid +racial +racist +radiant +radical +radioactive +ragged +rainy +random +rapid +rare +rash +rational +rational +raw +readied +ready +real +realistic +rear +rearrangeable +reasonable +rebellious +receivable +recent +receptive +recessive +rechargeable +reckless +reclaimable +reclinable +reclining +reclusive +recognized +recommendable +reconcilable +reconstructible +recordable +recoverable +recruitable +rectangular +red +redeemable +reduced +redundant +refillable +reflective +reflexive +reformable +refractable +refractive +refreshing +refundable +refusable +refutable +regainable +regal +regardable +regenerated +regenerative +regional +registered +registrable +regressive +regretful +regrettable +regulable +regular +rehearsable +reincarnated +reinforced +rejectable +rejoiceful +relapsable +relatable +relative +relaxative +relaxatory +relaxed +releasable +relegable +relevant +reliable +reliant +relievable +relieved +relishable +reluctant +remaining +remarkable +remittable +remorseful +remorseless +remote +removable +renderable +renegotiable +renewed +renounceable +renowned +reobtainable +repairable +repayable +repealable +repeatable +reponsible +reportable +reprehensible +representational +representative +repressed +repressible +repressive +reproachable +reproductive +reptilian +repulsive +reputable +required +resealable +resentful +reservable +reserved +resident +residential +resigned +resistant +resolvable +respectable +respected +respectful +respirable +responsible +responsive +restful +resting +restless +restorable +restorative +restored +restoring +restrainable +restrictive +resurrective +resuscitative +retail +retaliatory +retired +retiring +retouchable +retrievable +returnable +reunitable +reusable +revealable +revengeful +revered +reverent +reversible +reviewable +reviled +revisitable +revivable +revocable +revolting +revolutionary +rich +rideable +ridiculous +right +righteous +ripe +risky +ritualistic +rival +roastable +roasted +roasting +robotic +robust +rocky +roguish +rollable +romantic +roomy +rotatable +rotten +rough +round +rounded +routine +rowable +rowdy +royal +rubber +rubbery +rude +ruinable +ruinous +ruling +running +rusted +rustic +rusty +sacraficial +sacred +sacrilegious +sad +saddened +saddening +sadist +sadistic +safe +saintly +salaried +saline +salted +salty +same +sanded +sane +sarcastic +sassy +satiated +satirical +satisfactory +satisfied +savage +savings +savoury +savvy +scabby +scaled +scaly +scandalous +scared +scarred +scary +scathing +scatterbrained +scattered +scholarly +scientifc +scientific +scratchy +scrawny +screaming +scrummy +scrumptious +scummy +sea +seaborne +seafaring +seagoing +seared +seasick +seasonal +seaworthy +secluded +secondary +secret +secretive +secular +secure +seedy +segregated +seismic +select +selective +selfish +selfless +semantic +senatorial +senile +senior +senseless +sensible +sensitive +sensual +sensualist +sentient +sentimental +separate +septic +serene +serious +several +severe +sewable +sexual +sexy +shabby +shaded +shadowy +shady +shaggy +shakespearean +shallow +shameful +shameless +shapable +shapely +shared +sharp +shaven +sheepish +sheer +shelled +shifty +shimmery +shiny +shocked +shoddy +short +shortsighted +shot +shrewd +shrinkable +shy +sibling +sick +sickening +sickly +sightless +sightly +signal +significant +silent +silly +silver +similar +simple +sincere +sinful +single +sinister +sinking +sinless +sisterly +sizable +skeletal +skeptical +sketched +sketchy +skilful +skilled +skimpy +skinny +skipping +skyborne +sleeping +sleepless +sleepy +slender +slick +slight +slim +slimline +slimming +slimy +slippery +slithery +slobbery +sloppy +slothful +slow +sludgy +sluggish +slushy +sly +small +smart +smashable +smashed +smellable +smelly +smileless +smiling +smoggy +smoking +smoky +smooth +smug +snappish +snappy +snazzy +sneaking +sneaky +snide +snobbish +snooty +snotty +snug +snugging +soaked +soapy +sociable +social +soft +soggy +solemn +solid +solitary +soluble +sonic +sophisticated +soppy +sorrowless +sorry +soulful +soulless +sour +southern +spacious +spare +sparkling +sparse +spatial +special +specialist +specialized +specific +specified +specious +spectacular +specular +speedy +spellbound +spherical +spicy +spiky +spineless +spiny +spirited +spiritless +spiritual +spiteful +splendid +spoiled +spoken +spongy +spontaneous +spooky +sporadic +sportsmanlike +sporty +spotless +spotted +spotty +square +squeamish +squirrelish +squirrelly +squishy +stable +stagnant +stale +stampable +standard +standardisable +starry +starved +starving +static +statistical +status +statutory +steadfast +stealthy +steamy +steep +stellar +stereotyped +sterile +sterilized +sticky +still +stimulated +stimulating +stingy +stinky +stoic +stolen +stoned +stoppable +stormproof +stormy +straight +straightforward +strained +strange +strategic +streaky +street +streetsmart +strengthened +strenuous +stressed +stressful +stretchable +stretchy +strict +striking +stringent +striped +stripy +strong +stubborn +stuffed +stumpy +stupendous +stupid +stylish +suave +subatomic +subcultural +subdued +subjective +sublime +subliminal +submissive +subsequent +subservient +subsonic +substantial +subtle +successful +successive +succinct +succulent +sudden +sufficient +sugary +suicidal +suitable +sunlit +super +superb +superior +superlative +supernatural +superstitious +supplemental +supplementary +supportive +suppressive +supreme +surgical +surprised +surprising +surreal +susceptible +suspect +suspicious +sustainable +swallowable +swampy +swanky +sweaty +sweet +sweltering +swift +swimming +swirly +symbiotic +symbolic +symmetric +sympathetic +symphonic +synonymous +synthetic +syrupy +systematic +taboo +tacky +tactful +tactical +tailored +tainted +talented +talkative +tall +tame +tamed +tangible +tangled +tantalizing +targeted +tasteful +tasteless +tasty +tearful +teary +technical +tedious +teen +teenage +teensy +teeny +temperate +temporal +temporary +tempting +tender +tense +tenuous +terrible +terrified +terse +tertiary +textile +thankful +thankless +theatrical +theistic +theistical +thermal +thermodynamic +thermonuclear +thick +thin +think +thirsty +thorny +thorough +thoughtful +thoughtless +threatening +thriving +tidal +tight +timeless +timely +timely +timid +tinned +tinted +tiny +tippable +tired +tireless +tiresome +tolerant +top +total +touchy +tough +touristy +towering +toxic +traditional +tragic +training +traitorous +tranquil +transcendent +transient +translucent +transparent +trashy +traumatic +traumatized +treacherous +treasonous +tremendous +trendy +tribal +trick +tricksy +tricky +trifling +triumphant +trivial +trophied +tropical +troubled +troublesome +troubling +true +trustworthy +truthful +tubby +typical +tyrannical +tyrannicidal +uber +ugly +ultimate +ultrasonic +unable +unacceptable +unaccepted +unamused +unamusing +unapologetic +unappealing +unappeased +unappreciated +unapproachable +unarmed +unaroused +unassuming +unattractive +unauthorized +unavoidable +unaware +unbeatable +unbeaten +unbecoming +unbelievable +unbiased +uncanny +uncertain +unchanged +uncharitable +uncivilised +unclear +uncomfortable +uncommon +unconscious +unconstitutional +uncontrollable +uncooked +uncooperative +uncovered +uncreative +uncultured +uncurable +undamaged +undead +undecided +undefeated +undeniable +underage +underaverage +undercover +undereducated +underemployed +underground +underhanded +underpowered +underpriced +underqualified +understated +understood +underweight +undesirable +undesired +undiplomatic +undramatic +undrinkable +uneasy +uneconomic +uneconomical +uneducated +unemployed +unequal +unethical +unexpected +unexplained +unfair +unfaithful +unfavorable +unfit +unforgivable +unfortunate +unfriended +unhappy +unhealthy +unhelpful +unholy +unhygienic +unidentifiable +unidentified +unimaginable +unimaginative +unimportant +uninformed +uninspirable +uninspired +uninspiring +uninsured +uninterested +unique +unisex +united +universal +unjust +unkempt +unkind +unknown +unlawful +unliberated +unlikely +unlimited +unlit +unlovable +unloved +unlucky +unmarried +unmodified +unnecessary +unneeded +unnoticeable +unnoticed +unpaid +unparalleled +unplayable +unpleasant +unpleasurable +unpopular +unpredictable +unpurified +unqualified +unread +unreadable +unreal +unrealistic +unreasonable +unremarkable +unrestrained +unrestricted +unrestrictive +unrideable +unrivaled +unruly +unsafe +unsanitary +unsanitized +unscientific +unsecure +unshakable +unshaken +unsightly +unsinkable +unsophisticated +unspecific +unspecified +unspectacular +unstable +unstoppable +unsuccessful +unsuitable +unsuited +unsure +unsuspecting +unsustainable +unsweetened +unsympathetic +untalented +unthankful +unthinkable +untimely +untiring +untouchable +untrusting +unusual +unverifiable +unverified +unwanted +unwashed +unwelcome +unwieldable +unwilling +unwise +unyielding +upbeat +uplifted +upper +uppity +upright +upset +upstairs +upstanding +uptight +urban +urgent +usable +used +useful +useless +usual +utilizable +utopian +vacant +vaccinated +vague +valiant +valid +valuable +valued +vampiric +vanquishable +vapid +variable +varied +various +vast +vegan +vegetable +vegetarian +vegetative +veiny +venomous +verbose +verifiable +verified +vibrant +vicious +victimized +victorious +viewable +vigilant +vigorous +vile +villainous +violent +viral +virtuous +visible +visionary +visual +vital +vivid +vocational +volatile +volcanic +voluptuous +vulgar +vulnerable +wacky +waiting +wakeful +walking +wandering +wanted +warm +warmthless +wary +washable +wasteful +watchful +waterborne +waterbreathing +waterlogged +waterproof +watery +wavy +waxy +wayfaring +weak +weakened +weakhanded +wealthy +weaponized +wearable +weathered +weatherproof +webbed +webby +weedy +weekly +weeping +weepy +weighable +weighted +weightless +weighty +weird +western +wet +wheezy +whimsical +white +whole +wholesome +wicked +wide +widespread +wieldable +wifely +wild +wilful +willing +wily +windy +wine +winged +wingless +winning +winter +wintry +wired +wise +wishful +wispy +wistful +witless +wizardly +woeful +womanly +wonderful +wooden +wooly +wordy +working +worldwide +worried +worriless +worrisome +worrying +worse +worst +worth +worthless +worthwhile +worthy +wounded +wrapped +wretched +wrinkly +written +wrong +wrongful +yeasty +yellow +yielding +young +younger +youthful +yummy +zealous +zombified \ No newline at end of file diff --git a/PlayerTags/Resources/Words/Nouns.txt b/PlayerTags/Resources/Words/Nouns.txt new file mode 100644 index 0000000..03013e4 --- /dev/null +++ b/PlayerTags/Resources/Words/Nouns.txt @@ -0,0 +1,2642 @@ +abdomen +ability +abolition +abortion +abroad +absolution +abuse +access +accident +accommodation +account +accountant +acid +acknowledgment +acquisition +act +action +active +activity +actor +actress +adapter +addict +addiction +addition +address +adhesive +adjustment +administration +adrenaline +adult +adulthood +advance +advancement +advantage +adventure +advertisement +advertising +advice +affair +affect +aftermath +afternoon +aftershave +aftershock +afterthought +age +agency +agenda +agent +aggression +agreement +aid +air +airbag +airforce +airline +airplane +airport +alarm +alcohol +alcove +alert +alien +alley +alloy +alternative +altitude +ambassador +ambition +ambulance +amendment +amount +amputee +amulet +amusement +anal +analysis +analyst +anatomy +anecdote +anger +angle +anguish +animal +anime +ankle +anklet +annual +annulment +answer +antidote +antihero +antler +anus +anxiety +anything +apartment +ape +apex +aphrodisiac +apology +apparatus +apparel +appeal +appearance +appendage +appendix +applause +apple +appliance +application +appointment +approval +arbiter +archer +architect +architecture +area +argument +arithmetic +arm +armor +army +arrival +arrow +art +article +artisan +ashtray +aside +aspect +ass +assessment +assignment +assist +assistance +assistant +associate +association +assumption +astronomy +athlete +atmosphere +attachment +attack +attempt +attendant +attention +attic +attitude +attorney +attraction +audience +audio +author +authority +authorization +automaton +avalanche +average +award +awareness +baboon +baby +back +backdrop +background +backpack +badge +badger +bafflement +bag +bagel +baggage +bagpipe +baguette +bake +baker +bakery +balance +balcony +ball +ballet +balloon +banana +band +bandana +bang +bangle +banjo +bank +banker +banquette +banter +bar +barbeque +barn +base +baseball +basement +basement +basis +basket +bastard +bat +bath +bathrobe +bathroom +bathtub +battery +battle +battleship +battletoad +beach +beam +bean +beanie +beanstalk +bear +beard +beast +beat +beautiful +beauty +beaver +bed +bedroom +bee +beef +beer +beetle +beginner +beginning +behavior +beheading +belief +bell +bellend +belligerency +belly +belt +bench +bend +beneficiary +benefit +berry +bestseller +bet +beverage +beyond +bibliography +bicycle +bid +bidet +bike +bikini +bill +billboard +bin +binge +bingo +biology +bird +birth +birthday +bit +bite +bitter +bladder +blade +blame +blanket +blaster +blight +blind +blister +blizzard +block +blood +blossom +blouse +blow +blue +board +boat +body +bog +boiler +bomb +bomber +bone +bong +bongo +bonus +book +bookcase +booklet +booster +boot +booze +border +boss +bother +bottle +bottom +boulder +bowel +bowl +box +boxer +boy +boyfriend +bra +brace +bracelet +bracket +brain +brake +branch +brand +brandy +brawl +breach +bread +breadcrumb +break +breakfast +breakpoint +breast +breastplate +breath +breeze +bribery +brick +bridge +brief +brigade +brink +broccoli +brochure +brother +brush +bubble +bucket +buckle +buddy +budget +buffet +bug +building +bulldozer +bullet +bumper +bunch +bungalow +burger +burglar +burn +bus +bush +business +butcher +butter +button +buy +buyer +cabbage +cabin +cabinet +cable +cadet +cafe +cake +calculation +calculator +calendar +call +camera +camp +campaign +cancel +cancer +candidate +candle +candy +cannibal +cannon +canteen +canvas +cap +cape +capital +captain +caption +car +caravan +card +care +career +cargo +carnage +carpenter +carpet +carriage +carrier +carrot +carry +cart +cartoon +cartridge +case +cash +cashier +casserole +castle +cat +catacomb +catastrophe +catch +category +cathedral +cattle +cause +caution +cave +ceiling +celebration +cell +cellar +cemetery +center +certification +cesspool +chain +chair +chairman +chalice +challenge +champion +championship +chance +change +channel +chaos +chapel +chapter +character +charge +charity +chart +check +cheek +cheese +chef +chemical +chemistry +chest +chicken +child +childhood +chimp +chin +chin +chip +chocolate +chode +choice +christening +church +cigarette +cinema +circuit +circulation +circumference +citizenship +city +civility +civilization +claim +clap +class +classic +classroom +cleaner +cleavage +cleric +clerk +click +client +cliff +climate +climax +clock +closet +cloth +clothes +clothing +cloud +club +clue +coach +coast +coat +cock +code +coffee +coffin +coil +cold +collar +colleague +collection +college +colony +column +comb +combat +combination +combine +comedy +comet +comfort +comfortable +comic +command +comment +commerce +commercial +commission +committee +commotion +communication +communist +community +company +comparison +compartment +compassion +competition +competitor +complaint +complex +compliment +component +composer +composition +compost +comprehension +compulsion +computer +comrade +concept +concern +concert +conclusion +concoction +condition +conductor +confectionery +conference +confidence +confirmation +conflict +confusion +connection +consequence +consideration +consist +console +constant +constellation +construction +contact +content +contest +context +continent +contraceptive +contract +contraction +contraption +contribution +control +controversy +conversation +convert +cook +cookie +cooking +copy +copyright +corner +corporation +corpse +correspondent +corridor +corruption +cost +costume +cottage +cotton +cougar +council +councilor +counter +counterpart +country +county +couple +courage +course +court +cousin +cover +cow +crack +cracker +cradle +craft +cramp +crap +crash +crate +crayon +crazy +cream +creation +creative +creator +credit +crescendo +crew +crime +criminal +criteria +criticism +cross +crotch +crowd +crown +cry +cube +cucumber +cuddle +culture +cup +cupboard +cupcake +currency +curse +cursor +curtain +curve +cushion +customer +cut +cycle +cyclone +cylinder +dagger +damage +dance +dancer +dandruff +danger +dare +dark +dashboard +data +database +date +daughter +day +deadline +deal +dealer +death +debate +debt +decision +declination +decongestant +decryption +dedication +deep +defense +deficit +definition +deformation +degree +delay +deliverance +delivery +demand +democracy +demon +dentist +deodorant +department +departure +dependent +deployment +deposit +depression +depth +deputy +description +desert +design +designer +desire +desk +dessert +destiny +destroyer +destruction +detail +detainment +detective +detention +determination +developer +development +deviance +device +devil +diamond +dictionary +diet +difference +differential +difficulty +digestion +digger +dignity +dimension +diner +dinner +dinosaur +direction +director +dirt +disability +disadvantage +disagreement +disapproval +disarmament +disaster +discipline +disco +disconnection +discount +discovery +discrepancy +discrimination +discussion +disease +disembodiment +disengagement +disguise +disgust +dish +disk +disorder +dispenser +display +disposer +dispute +disruption +distance +distribution +distributor +district +distrust +disturbance +divide +divider +diving +divinity +division +divorce +dock +doctor +document +dog +dolphin +donkey +door +dot +double +doubt +doubter +downforce +downgrade +draft +drag +dragon +drain +drama +draw +drawer +drawing +dream +dress +dresser +drill +drink +drive +driver +driveway +drizzle +drop +droplet +drought +drug +drum +drummer +drunk +duckling +dumbass +dump +dungeon +dungeoneer +dust +duty +dwarf +ear +earplug +earring +earth +earthquake +ease +eat +eavesdropper +eclipse +economics +economy +edge +editor +editorial +education +effect +effective +efficiency +effort +egg +ejection +elastic +elder +election +element +elephant +elevator +elixir +elongation +embezzlement +emergence +emergency +emotion +empathy +emphasis +employee +employer +employment +empowerment +emulsion +encirclement +encounter +end +enema +enemy +energy +engagement +engine +engineer +engineering +enigma +enjoyment +enquiry +entanglement +entertainment +enthusiasm +entity +entrance +entry +environment +envy +epitome +equal +equipment +equivalent +erection +error +eruption +escape +essay +establishment +estate +estimate +estrogen +ethics +euphoria +evaluator +evening +event +eviction +evidence +evocation +evolution +exam +examination +examiner +example +exchange +excitement +exclamation +excuse +execution +executor +exercise +exertion +exhaust +exile +existence +exit +expansion +experience +expert +explanation +explosion +exposition +expression +extension +extent +extermination +external +extreme +eye +eyeball +eyebrow +face +facelift +facility +fact +factor +factory +faculty +fail +failure +fairy +faith +faker +fame +family +fan +fang +fanny +farm +farmer +farmer +fart +fat +father +fatigue +fault +favor +fear +feast +feather +feature +feces +fee +feedback +feel +feeling +felony +female +feminist +ferry +fertilizer +fetish +fetus +feud +fiction +fidget +field +fight +fighter +figure +figurine +file +fill +film +filth +final +finance +finding +finger +finish +fire +fireman +firewall +fish +fishing +fisting +fix +flake +flame +flange +flap +flash +flatulence +flavor +fledgling +flesh +flick +flight +fling +flock +flood +floor +flour +flow +flower +flu +fluid +flurry +flute +fly +foam +focus +fold +folder +following +font +food +foot +football +force +forecast +forehead +forest +forever +form +format +fort +fortnight +fortune +forum +foundation +fountain +fox +fractal +fracture +fragment +fragrance +frame +frankenstein +fraudster +freedom +freezer +freighter +frenzy +friction +friend +friendship +friendzone +front +frost +frown +fruit +frustration +fryer +fuel +fulfillment +fumble +fun +function +funeral +fur +furnace +furniture +future +gain +gallery +gambit +game +gap +garage +garbage +garden +gas +gasp +gate +gateway +gatherer +gauge +gear +gender +gene +general +genius +geology +geometry +geyser +ghost +giant +gift +gigantism +girl +girlfriend +girth +gland +glass +glider +glove +goal +god +gold +golf +gorilla +gossip +governance +government +governor +grab +grade +grain +grammar +grandfather +grandmother +graph +graphic +grass +grassland +gratitude +grease +green +greenhouse +grenade +grill +grocery +ground +group +growth +guarantee +guard +guardian +guerilla +guess +guest +guestbook +guidance +guide +guilt +guitar +gun +gutter +guy +guzzler +gym +gymnast +gymnastics +habit +hair +haircut +half +hall +hammer +hamster +hand +handicap +handle +hang +happiness +harbor +hardware +harm +harmony +hassle +hat +hate +hatred +head +healer +healslut +health +hearing +heart +heat +heater +heaven +hedge +hedgehog +height +helicopter +hell +helmet +help +herb +hero +heyday +hierarchy +high +highlight +highway +hill +hire +historian +history +hit +hive +hobbit +hobby +hold +hole +holiday +home +homework +honesty +honey +hood +hook +hope +horror +horse +hose +hospice +hospital +hospitality +host +hostel +hostess +hotel +hour +house +housework +housing +hovercraft +human +humidity +humor +humping +hunger +hunt +hunter +hurricane +hurry +husband +hydrant +hyperbole +hypothermia +ice +icebreaker +icecream +icicle +icon +idea +ideal +idealist +igloo +illegal +image +imagination +impact +impeachment +implement +importance +impression +imprisonment +improvement +impudence +impulse +inbox +incest +incident +income +increase +independence +independent +index +indication +indifference +individual +industry +infancy +infatuation +inflammation +inflation +influence +information +infusion +ingrate +initial +initiative +injection +injury +injustice +ink +inn +innocence +input +inquiry +inscription +insect +insemination +inside +insolence +inspection +inspector +instance +instruction +instrument +instrumentalist +instrumentation +insulation +insurance +insurgence +intelligence +intention +interaction +interior +interjection +internal +international +internet +interpreter +intervenor +intervention +interview +interviewer +intestine +intrigue +introduction +invention +inventor +inventory +investment +invite +invoice +iron +island +issue +item +jacket +jail +jam +jar +jealousy +jet +jewel +job +jockey +join +joint +joke +journey +judge +judgment +juggernaut +juice +juicer +jump +jumper +junk +jury +justice +kamikaze +keep +kerfuffle +key +kick +kid +kill +killer +kind +kindness +king +kiss +kitchen +kitten +knee +knife +knight +knob +knot +knowledge +knuckle +labor +laboratory +laborer +labrynth +ladder +lady +lake +lamp +land +landscape +language +lantern +lard +latency +latex +laugh +laughter +laundry +lava +law +lawn +lawsuit +lawyer +lay +layer +lead +leader +leadership +leading +leaf +league +leash +leather +lecture +leg +legend +leisure +lemon +length +leprosy +lesson +letter +lettuce +level +lever +library +license +lie +lier +life +lift +light +lighting +lightning +limit +line +lingerie +linguistics +link +lion +lip +lipstick +liquid +liquor +list +listen +literature +litigation +litter +liver +livestock +living +load +loan +lobotomy +local +location +lock +log +logic +loneliness +lord +loss +lotion +lounge +love +loyalty +lubricant +luck +luggage +lunch +lung +luttuce +machine +machinery +magazine +magic +magnet +maid +maiden +mail +mailbox +mailman +maintenance +major +maker +makeup +mall +mallet +man +management +manager +manufacturer +map +march +mark +market +marketing +marksman +marriage +marsh +mascara +mask +mass +massage +master +match +mate +material +mathematic +mathematics +matter +mattress +maybe +mayor +meal +meaning +measurement +meat +medallion +media +medicine +medium +meet +meeting +melody +melon +member +membership +membrane +memory +mention +menu +mercenary +mess +message +metal +meteor +method +microwave +midget +midnight +midwife +might +military +milk +mind +minimum +minion +minister +minor +minute +miracle +mirage +mirror +misandry +miscarriage +miscommunication +misfit +misogyny +misplacement +misreading +miss +missile +mission +mistake +misunderstanding +mix +mixer +mixture +moan +moan +moat +mobile +mode +model +modem +moderator +mole +moment +monastery +money +mongrel +monitor +monkey +month +mood +morality +morning +mortgage +motel +mother +motion +motor +mound +mount +mountain +mouse +mouse +mousse +mouth +move +mover +movie +mower +mud +mule +multimedia +murderer +muscle +museum +music +mustache +mutant +myth +nail +name +napkin +nation +native +nature +nazi +nebula +neck +necklace +negative +negotiation +nerve +net +network +news +newspaper +night +nightclub +noise +nonbeliever +nonconformist +nondisclosure +nonsense +noodle +nose +note +nothing +notice +notification +notoriety +nova +novel +nugget +number +nurse +nursery +nut +nutrition +oath +obedience +obesity +object +objection +objective +obligation +obscurity +observation +observatory +occasion +occupation +ocean +offense +offer +office +officer +ogre +oil +onion +onslaught +opening +opera +operation +opinion +opponent +opportunist +opportunity +opposite +optimist +option +oral +orange +orchestra +order +organ +organization +orgasm +original +ornament +outburst +outcome +outfit +output +outside +ovary +oven +overcharge +overclocking +overexertion +owner +pace +pacemaker +pack +package +packet +pad +paddle +page +pain +paint +painter +painting +paintwork +pair +pajama +pamphlet +pan +pancake +panda +panic +pansy +panther +pantry +paper +parachute +parachute +parade +paramedic +parasite +parcel +parent +park +parking +part +participant +partner +party +passage +passenger +passion +passport +past +pasta +paste +pastry +path +patience +patient +patriot +patrol +pattern +pause +pavement +pay +payment +peace +peak +pearl +peasant +pedal +pen +penalty +pendant +penetration +pension +pentagon +people +percentage +perception +performance +perfume +period +permission +permit +perpetrator +person +personality +perspective +pervert +pessimist +pest +petal +pharmacist +phase +philosopher +philosophy +phone +photo +photographer +phrase +physics +pianist +piano +pick +pickle +picture +pie +piece +pig +pigeon +pilgrimage +pillow +pimp +pimple +pin +pioneer +pipe +piracy +pitch +pizza +place +plan +plane +planet +plank +plant +plantation +planter +plaster +plastic +plate +platform +play +player +playground +playroom +pleasure +pleb +plenty +plunger +poem +poet +poetry +point +pokemon +poking +police +policy +politics +pollution +pool +pop +population +porn +portal +portfolio +position +possession +possibility +post +postage +postbox +poster +posture +pot +potato +potato +potential +potion +potty +pouch +poultry +pound +pounder +pounding +powder +power +practice +practitioner +precedent +preference +prejudice +prelude +premeditation +preoccupation +preparation +presence +present +presentation +president +press +pressure +pressurisation +pretense +price +pride +priest +primary +principle +print +printer +prior +priority +prison +private +privilege +prize +probation +problem +procedure +process +producer +product +production +profession +professional +professor +profile +profit +program +programmer +progress +project +proliferation +promise +promotion +prompt +pronunciation +proof +property +proposal +prosecution +prospect +protagonist +protection +protest +protocol +psychology +pube +puberty +public +publicity +publisher +pudding +puddle +punch +punishment +pupil +purchase +purpose +push +pusher +pyramid +quality +quantity +quarrel +quarter +queen +quest +question +quit +quote +rabbit +race +racism +racist +radiator +radio +raffle +rage +raider +railway +rain +rainbow +raise +randomization +range +rank +rate +ratio +reach +reaction +reading +reality +reason +receipt +reception +recipe +recognition +recommendation +record +recorder +recording +recover +recreation +recruit +red +redesign +rediscovery +reduction +reference +reflection +refrigerator +refund +region +register +regression +regret +regular +regulation +reject +relation +relationship +relative +relaxation +release +reliability +relief +religion +reminder +remote +removal +rendition +rent +repair +reparation +repeat +repellent +replacement +replication +reply +report +representative +republic +reputation +request +requirement +resale +research +reserve +resident +resistance +resolution +resolve +resort +resource +respect +response +responsibility +restaurant +result +retailer +return +reveal +revenant +revenue +review +revolution +reward +rhetoric +rhyme +rhythm +rice +rich +riddle +ride +ring +rip +risk +river +road +roast +robe +rock +rocket +rod +rodent +role +roll +romance +roof +room +root +rope +round +route +routine +rub +ruckus +ruin +rule +rush +sack +saddle +safe +safety +sail +sailor +salad +salary +sale +salesman +salt +sample +samurai +sand +sandwich +satellite +satire +satisfaction +sauce +sausage +save +savings +savior +scale +scanner +scarf +scavenger +scene +scenery +scent +schedule +schematic +scheme +schizophrenic +school +science +score +scramble +scratch +scream +screamer +screen +screw +screwdriver +script +scrub +sculpture +sea +search +season +seat +second +secret +secretary +section +sector +security +seed +selection +self +sensation +sense +sensitive +sentence +series +servant +serve +server +service +session +setting +sewer +sex +shack +shame +shape +share +shelter +shield +shift +ship +shirt +shit +shitter +shiver +shock +shoe +shoot +shooter +shop +shopping +shortage +shot +shoulder +shoulder +show +shower +sibling +side +sigh +sign +signal +signature +significance +silence +silver +similarity +simpleton +simulator +sin +sing +singer +single +sink +sister +site +sitting +situation +size +skeleton +skill +skin +skirt +skull +sky +skyscraper +slave +slayer +sleep +slice +slide +slime +slip +slope +slum +slut +smack +smasher +smell +smile +smoke +snob +snow +snowflake +snuggle +socialist +society +sock +software +soil +soldier +solid +solution +solvent +somersault +son +song +soulmate +sound +soup +source +space +spank +spankbank +spark +spartan +spasm +speaker +spear +special +specialist +species +spectacle +speech +speed +spell +spend +sperm +sphere +spirit +spiritual +spite +spitroast +splash +spliff +split +sport +spot +spotlight +spray +spread +spring +spy +square +squatter +squirrel +stable +stack +stadium +staff +stage +stair +stamina +stamp +stance +stand +standard +star +start +state +statement +station +statistic +status +stay +steak +steam +steamer +step +stick +stock +stomach +stone +stool +stop +storage +store +storm +story +stranger +strategy +stream +street +strength +stress +stretch +strife +strike +string +strip +stroke +structure +struggle +stud +student +studio +study +stuff +stupidity +style +subject +subordinate +subroutine +substance +subway +success +suck +suffocation +sugar +suggestion +suit +suitcase +sultan +summer +sun +superhero +supermarket +supply +support +supporter +suppression +surface +surgeon +surgery +surprise +survey +suspect +sustainment +swim +swimming +swimsuit +swing +switch +sword +symmetry +sympathy +syndicate +synergy +system +table +tackle +tail +tale +talk +tank +tap +target +task +tattoo +tavern +tax +taxation +taxi +tea +teach +teacher +teaching +team +technology +teenager +telephone +television +temperature +tendency +tennis +tension +term +territory +test +text +textbook +texture +theater +theme +theory +therapist +thermometer +thief +thigh +thing +thirst +thong +thought +thread +throat +throne +thumb +thunder +tiara +ticket +time +timeout +tip +titan +title +today +toe +toilet +tolerance +tomb +tomorrow +tone +tongue +tonight +tool +tooth +top +topic +tornado +total +touch +tough +tour +tourist +towel +tower +town +toy +track +trade +tradition +traffic +tragedy +train +trainer +training +transaction +transition +translation +transmission +transport +transportation +trap +trapdoor +trash +travel +treat +treatment +tree +tremor +trench +trial +tribunal +tribute +trick +trip +troll +trooper +trophy +trouble +truck +trumpet +trunk +trust +truth +try +tsunami +tube +tumor +tune +turn +turnover +twist +type +uncle +understanding +underwear +unemployment +union +unique +unit +universe +university +upper +uproar +upstairs +urine +usage +user +vacation +vacuum +validity +valley +valuable +value +vanity +variation +variety +vast +vegetable +vegetation +vehicle +vendetta +vengeance +verdict +version +vibration +vibrator +victim +video +village +violin +virus +vision +visitor +visor +visual +vitality +vodka +voice +volcano +volume +voyage +wait +waiter +waitress +walk +wall +wannabe +war +warlock +warmth +warning +wash +washer +waste +watch +watcher +watchman +water +wave +weakness +wealth +weapon +wear +weasel +weather +web +wedding +weed +week +weekend +weight +welcome +welfare +wench +whale +wheel +whimp +whirlpool +whirlwind +whisky +wholesale +wholesaler +wiener +wife +wilderness +wildlife +win +wind +window +wine +wing +wingman +winner +winter +wisdom +wish +witch +witness +woman +wonder +wood +word +work +workbench +worker +workshop +world +worry +worth +wound +wrap +wrestle +writer +writing +yank +yard +yawn +year +yeast +yellow +youth +zombie +zone +zoo \ No newline at end of file diff --git a/PlayerTags/SetNameplateDelegate.cs b/PlayerTags/SetNameplateDelegate.cs new file mode 100644 index 0000000..778615e --- /dev/null +++ b/PlayerTags/SetNameplateDelegate.cs @@ -0,0 +1,7 @@ +using Dalamud.Game.ClientState.Objects.Types; +using Dalamud.Game.Text.SeStringHandling; + +namespace PlayerTags +{ + public delegate void SetNameplateDelegate(GameObject gameObject, SeString name, SeString title, SeString freeCompany, ref bool isTitleVisible, ref bool isTitleAboveName, ref int iconId, out bool isNameChanged, out bool isTitleChanged, out bool isFreeCompanyChanged); +} diff --git a/PlayerTags/UIColorHelper.cs b/PlayerTags/UIColorHelper.cs new file mode 100644 index 0000000..b5f66db --- /dev/null +++ b/PlayerTags/UIColorHelper.cs @@ -0,0 +1,97 @@ +using Dalamud.Data; +using ImGuiNET; +using Lumina.Excel.GeneratedSheets; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; + +namespace PlayerTags +{ + public static class UIColorHelper + { + private class UIColorComparer : IEqualityComparer + { + public bool Equals(UIColor? left, UIColor? right) + { + if (left != null && right != null) + { + return left.UIForeground == right.UIForeground; + } + + return false; + } + + public int GetHashCode(UIColor obj) + { + return obj.UIForeground.GetHashCode(); + } + } + + private static UIColor[] s_UIColors = new UIColor[] { }; + + public static IEnumerable UIColors { get { return s_UIColors; } } + + public static void Initialize(DataManager dataManager) + { + var uiColors = 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; + }); + + s_UIColors = filteredUIColors.ToArray(); + } + } + + 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(); + } + } +}