diff --git a/HudSwap/Configuration.cs b/HudSwap/Configuration.cs index 857dc45..155a257 100644 --- a/HudSwap/Configuration.cs +++ b/HudSwap/Configuration.cs @@ -26,6 +26,11 @@ namespace HudSwap { public Guid craftingLayout = Guid.Empty; public Guid gatheringLayout = Guid.Empty; public Guid fishingLayout = Guid.Empty; + public Guid roleplayingLayout = Guid.Empty; + + public Dictionary JobLayouts { get; set; } = new Dictionary(); + public bool HighPriorityJobs { get; set; } = false; + public bool JobsCombatOnly { get; set; } = false; public Dictionary> Layouts { get; } = new Dictionary>(); diff --git a/HudSwap/HudSwap.csproj b/HudSwap/HudSwap.csproj index 04c70af..cfd6357 100644 --- a/HudSwap/HudSwap.csproj +++ b/HudSwap/HudSwap.csproj @@ -40,6 +40,14 @@ $(AppData)\XIVLauncher\addon\Hooks\ImGuiScene.dll + + False + $(AppData)\XIVLauncher\addon\Hooks\Lumina.dll + + + False + $(AppData)\XIVLauncher\addon\Hooks\Lumina.Generated.dll + @@ -61,4 +69,4 @@ - \ No newline at end of file + diff --git a/HudSwap/PluginUI.cs b/HudSwap/PluginUI.cs index 20cbd33..592d71f 100644 --- a/HudSwap/PluginUI.cs +++ b/HudSwap/PluginUI.cs @@ -2,13 +2,13 @@ using Dalamud.Game.ClientState.Actors.Types; using Dalamud.Plugin; using ImGuiNET; +using Lumina.Excel.GeneratedSheets; using System; using System.Collections.Generic; using System.Linq; using System.Numerics; using System.Runtime.InteropServices; -// TODO: Job swaps? // TODO: Zone swaps? namespace HudSwap { @@ -34,6 +34,8 @@ namespace HudSwap { private string renameName = ""; private Guid selectedLayout = Guid.Empty; + private string jobFilter = ""; + private static bool configErrorOpen = true; public static void ConfigError() { if (ImGui.Begin("HudSwap error", ref configErrorOpen)) { @@ -186,16 +188,85 @@ namespace HudSwap { ImGui.Text("These settings are ordered from highest priority to lowest priority.\nHigher priorities overwrite lower priorities when enabled."); ImGui.Spacing(); - ImGui.Columns(2); + if (ImGui.CollapsingHeader("Status layouts", ImGuiTreeNodeFlags.DefaultOpen)) { + if (ImGui.BeginChild("##layout-selections", new Vector2(0, 125))) { + ImGui.Columns(2); - this.LayoutBox("In combat", ref this.plugin.config.combatLayout, player); - this.LayoutBox("Weapon drawn", ref this.plugin.config.weaponDrawnLayout, player); - this.LayoutBox("In instance", ref this.plugin.config.instanceLayout, player); - this.LayoutBox("Crafting", ref this.plugin.config.craftingLayout, player); - this.LayoutBox("Gathering", ref this.plugin.config.gatheringLayout, player); - this.LayoutBox("Fishing", ref this.plugin.config.fishingLayout, player); + List textSizes = new List(); - ImGui.Columns(1); + this.LayoutBox("In combat", ref this.plugin.config.combatLayout, player, textSizes); + this.LayoutBox("Weapon drawn", ref this.plugin.config.weaponDrawnLayout, player, textSizes); + this.LayoutBox("In instance", ref this.plugin.config.instanceLayout, player, textSizes); + this.LayoutBox("Crafting", ref this.plugin.config.craftingLayout, player, textSizes); + this.LayoutBox("Gathering", ref this.plugin.config.gatheringLayout, player, textSizes); + this.LayoutBox("Fishing", ref this.plugin.config.fishingLayout, player, textSizes); + this.LayoutBox("Roleplaying", ref this.plugin.config.roleplayingLayout, player, textSizes); + + ImGui.SetColumnWidth(0, textSizes.Max() + ImGui.GetStyle().ItemSpacing.X * 2); + + ImGui.Columns(1); + ImGui.EndChild(); + } + } + + if (ImGui.CollapsingHeader("Job layouts")) { + if (ImGui.InputText("Filter", ref this.jobFilter, 50, ImGuiInputTextFlags.AutoSelectAll)) { + this.jobFilter = this.jobFilter.ToLower(); + } + + if (ImGui.BeginChild("##job-layout-selections", new Vector2(0, 125))) { + ImGui.Columns(2); + + List textSizes = new List(); + + var acceptableJobs = this.pi.Data.GetExcelSheet() + .Where(job => job.NameEnglish != "Adventurer") + .Where(job => this.jobFilter.Length == 0 || (job.NameEnglish.ToLower().Contains(this.jobFilter) || job.Abbreviation.ToLower().Contains(this.jobFilter))); + + foreach (ClassJob job in acceptableJobs) { + this.plugin.config.JobLayouts.TryGetValue(job.Abbreviation, out Guid layout); + + Guid oldLayout = layout; + + this.LayoutBox(job.NameEnglish, ref layout, player, textSizes); + + if (oldLayout != layout) { + this.plugin.config.JobLayouts[job.Abbreviation] = layout; + this.plugin.config.Save(); + if (this.plugin.config.SwapsEnabled) { + this.statuses.SetHudLayout(player, true); + } + } + } + + ImGui.SetColumnWidth(0, textSizes.DefaultIfEmpty(0).Max() + ImGui.GetStyle().ItemSpacing.X * 2); + + ImGui.Columns(1); + ImGui.EndChild(); + } + + bool combatOnlyJobs = this.plugin.config.JobsCombatOnly; + if (ImGui.Checkbox("Jobs only in combat/weapon drawn", ref combatOnlyJobs)) { + this.plugin.config.JobsCombatOnly = combatOnlyJobs; + this.plugin.config.Save(); + if (this.plugin.config.SwapsEnabled) { + this.statuses.SetHudLayout(player, true); + } + } + ImGui.SameLine(); + this.HelpMarker("Selecting this will make the HUD layout change for a job only when in combat or when your weapon is drawn."); + + bool highPriorityJobs = this.plugin.config.HighPriorityJobs; + if (ImGui.Checkbox("Jobs take priority over status", ref highPriorityJobs)) { + this.plugin.config.HighPriorityJobs = highPriorityJobs; + this.plugin.config.Save(); + if (this.plugin.config.SwapsEnabled) { + this.statuses.SetHudLayout(player, true); + } + } + ImGui.SameLine(); + this.HelpMarker("Selecting this will make job layouts always apply when on that job. If this is unselected, job layouts will only apply if the default layout was going to be used (or only in combat if the above checkbox is selected)."); + } ImGui.EndTabItem(); } @@ -219,8 +290,7 @@ namespace HudSwap { } private string LayoutNameOrDefault(Guid key) { - Tuple tuple; - if (this.plugin.config.Layouts.TryGetValue(key, out tuple)) { + if (this.plugin.config.Layouts.TryGetValue(key, out Tuple tuple)) { return tuple.Item1; } else { return ""; @@ -244,7 +314,8 @@ namespace HudSwap { } } - private void LayoutBox(string name, ref Guid layout, PlayerCharacter player) { + private void LayoutBox(string name, ref Guid layout, PlayerCharacter player, List textSizes) { + textSizes.Add(ImGui.CalcTextSize(name).X); ImGui.Text(name); ImGui.NextColumn(); if (ImGui.BeginCombo($"##{name}-layout", this.LayoutNameOrDefault(layout))) { @@ -283,10 +354,12 @@ namespace HudSwap { private readonly DalamudPluginInterface pi; private readonly bool[] condition = new bool[ORDER.Length]; + private ClassJob job; // Order: lowest to highest priority // For conditions that require custom logic, use ConditionFlag.None private static readonly ConditionFlag[] ORDER = { + ConditionFlag.RolePlaying, ConditionFlag.Fishing, ConditionFlag.Gathering, ConditionFlag.Crafting, @@ -322,6 +395,7 @@ namespace HudSwap { private Guid[] GetLayouts() { // These layouts must be in the same order as the flags in ORDER are defined Guid[] layouts = { + this.plugin.config.roleplayingLayout, this.plugin.config.fishingLayout, this.plugin.config.gatheringLayout, this.plugin.config.craftingLayout, @@ -343,6 +417,12 @@ namespace HudSwap { bool anyChanged = false; + ClassJob currentJob = this.pi.Data.GetExcelSheet().GetRow(player.ClassJob.Id); + if (this.job != null && this.job != currentJob) { + anyChanged = true; + } + this.job = currentJob; + for (int i = 0; i < ORDER.Length; i++) { ConditionFlag flag = ORDER[i]; if (flag == ConditionFlag.None) { @@ -362,11 +442,16 @@ namespace HudSwap { if (player == null) { return Guid.Empty; } - this.Update(player); - Guid layout = this.plugin.config.defaultLayout; + // get the job layout if there is one and check if jobs are high priority + if (this.plugin.config.JobLayouts.TryGetValue(this.job.Abbreviation, out Guid jobLayout) && this.plugin.config.HighPriorityJobs) { + return jobLayout; + } + + Guid layout = Guid.Empty; Guid[] layouts = this.GetLayouts(); + // check all status conditions and set layout as appropriate for (int i = 0; i < ORDER.Length; i++) { Guid flagLayout = layouts[i]; @@ -375,7 +460,20 @@ namespace HudSwap { } } - return layout; + // if a job layout is set for the current job + if (jobLayout != Guid.Empty) { + // if jobs are combat only and the player is either in combat or has their weapon drawn, use the job layout + if (this.plugin.config.JobsCombatOnly && (this.condition[5] || this.condition[6])) { + layout = jobLayout; + } + + // if the layout was going to be default, use job layout unless jobs are not combat only + if (!this.plugin.config.JobsCombatOnly && layout == Guid.Empty) { + layout = jobLayout; + } + } + + return layout == Guid.Empty ? this.plugin.config.defaultLayout : layout; } public void SetHudLayout(PlayerCharacter player, bool update = false) {