From 6d5503b3bc081331486831a44a379ef88a3d6cd6 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Sat, 9 Oct 2021 13:47:24 +0200 Subject: [PATCH] Allow multiple fixed designs for a single character to be active, and check jobs. Use the job-group with fewer entries if they are overlapping, and the first added if they have the same number of entries. --- Glamourer.GameData/GameData.cs | 50 ++++++++++++++++ Glamourer.GameData/Job.cs | 21 +++++++ Glamourer.GameData/JobGroup.cs | 50 ++++++++++++++++ Glamourer/Designs/FixedDesigns.cs | 83 ++++++++++++++++---------- Glamourer/GlamourerConfig.cs | 1 + Glamourer/Gui/InterfaceFixedDesigns.cs | 31 ++++++++-- 6 files changed, 201 insertions(+), 35 deletions(-) create mode 100644 Glamourer.GameData/Job.cs create mode 100644 Glamourer.GameData/JobGroup.cs diff --git a/Glamourer.GameData/GameData.cs b/Glamourer.GameData/GameData.cs index 9d255f1..2e53bfa 100644 --- a/Glamourer.GameData/GameData.cs +++ b/Glamourer.GameData/GameData.cs @@ -11,6 +11,8 @@ namespace Glamourer { private static Dictionary? _stains; private static Dictionary>? _itemsBySlot; + private static Dictionary? _jobs; + private static Dictionary? _jobGroups; private static SortedList? _models; public static IReadOnlyDictionary Models(DataManager dataManager) @@ -84,5 +86,53 @@ namespace Glamourer _itemsBySlot[EquipSlot.LFinger] = _itemsBySlot[EquipSlot.RFinger]; return _itemsBySlot; } + + public static IReadOnlyDictionary Jobs(DataManager dataManager) + { + if (_jobs != null) + return _jobs; + + var sheet = dataManager.GetExcelSheet()!; + _jobs = sheet.ToDictionary(j => (byte)j.RowId, j => new Job(j)); + return _jobs; + } + + public static IReadOnlyDictionary JobGroups(DataManager dataManager) + { + if (_jobGroups != null) + return _jobGroups; + + var sheet = dataManager.GetExcelSheet()!; + var jobs = dataManager.GetExcelSheet()!; + + static bool ValidIndex(uint idx) + { + if (idx > 0 && idx < 36) + return true; + + return idx switch + { + 91 => true, + 92 => true, + 96 => true, + 98 => true, + 99 => true, + 111 => true, + 112 => true, + 129 => true, + 149 => true, + 150 => true, + 156 => true, + 157 => true, + 158 => true, + 159 => true, + _ => false, + }; + } + + _jobGroups = sheet.Where(j => ValidIndex(j.RowId)) + .ToDictionary(j => (ushort) j.RowId, j => new JobGroup(j, jobs)); + return _jobGroups; + } } } diff --git a/Glamourer.GameData/Job.cs b/Glamourer.GameData/Job.cs new file mode 100644 index 0000000..01d3dd0 --- /dev/null +++ b/Glamourer.GameData/Job.cs @@ -0,0 +1,21 @@ +using Lumina.Excel.GeneratedSheets; + +namespace Glamourer +{ + public readonly struct Job + { + public readonly string Name; + public readonly string Abbreviation; + public readonly ClassJob Base; + + public uint Id + => Base.RowId; + + public Job(ClassJob job) + { + Base = job; + Name = job.Name.ToString(); + Abbreviation = job.Abbreviation.ToString(); + } + } +} diff --git a/Glamourer.GameData/JobGroup.cs b/Glamourer.GameData/JobGroup.cs new file mode 100644 index 0000000..83a1763 --- /dev/null +++ b/Glamourer.GameData/JobGroup.cs @@ -0,0 +1,50 @@ +using System; +using System.Diagnostics; +using System.Linq; +using Lumina.Excel; +using Lumina.Excel.GeneratedSheets; + +namespace Glamourer +{ + public readonly struct JobGroup + { + public readonly string Name; + private readonly ulong _flags; + public readonly int Count; + public readonly uint Id; + + public JobGroup(ClassJobCategory group, ExcelSheet jobs) + { + Count = 0; + _flags = 0ul; + Id = group.RowId; + Name = group.Name.ToString(); + + Debug.Assert(jobs.RowCount < 64); + foreach (var job in jobs) + { + var abbr = job.Abbreviation.ToString(); + if (!abbr.Any()) + continue; + + var prop = group.GetType().GetProperty(abbr); + Debug.Assert(prop != null); + + if (!(bool) prop.GetValue(group)!) + continue; + + ++Count; + _flags |= 1ul << (int) job.RowId; + } + } + + public bool Fits(Job job) + => Fits(job.Id); + + public bool Fits(uint jobId) + { + var flag = 1ul << (int)jobId; + return (flag & _flags) != 0; + } + } +} diff --git a/Glamourer/Designs/FixedDesigns.cs b/Glamourer/Designs/FixedDesigns.cs index 0a45a5c..9bcd849 100644 --- a/Glamourer/Designs/FixedDesigns.cs +++ b/Glamourer/Designs/FixedDesigns.cs @@ -13,44 +13,50 @@ namespace Glamourer.Designs { public class FixedDesign { - public string Name; - public Design Design; - public bool Enabled; + public string Name; + public JobGroup Jobs; + public Design Design; + public bool Enabled; public GlamourerConfig.FixedDesign ToSave() => new() { - Name = Name, - Path = Design.FullName(), - Enabled = Enabled, + Name = Name, + Path = Design.FullName(), + Enabled = Enabled, + JobGroups = Jobs.Id, }; - public FixedDesign(string name, Design design, bool enabled) + public FixedDesign(string name, Design design, bool enabled, JobGroup jobs) { Name = name; Design = design; Enabled = enabled; + Jobs = jobs; } } - public List Data; - public Dictionary EnabledDesigns; + public List Data; + public Dictionary> EnabledDesigns; + public readonly IReadOnlyDictionary JobGroups; public bool EnableDesign(FixedDesign design) { var changes = !design.Enabled; - if (EnabledDesigns.TryGetValue(design.Name, out var oldDesign)) - { - oldDesign.Enabled = false; - changes = true; - } - else + + if (!EnabledDesigns.TryGetValue(design.Name, out var designs)) { + EnabledDesigns[design.Name] = new List { design }; Glamourer.PlayerWatcher.AddPlayerToWatch(design.Name); + changes = true; + } + else if (!designs.Contains(design)) + { + designs.Add(design); + changes = true; } - EnabledDesigns[design.Name] = design; - design.Enabled = true; + design.Enabled = true; if (Dalamud.Objects.FirstOrDefault(o => o.ObjectKind == ObjectKind.Player && o.Name.ToString() == design.Name) is Character character) OnPlayerChange(character); @@ -63,15 +69,25 @@ namespace Glamourer.Designs return false; design.Enabled = false; - EnabledDesigns.Remove(design.Name); - Glamourer.PlayerWatcher.RemovePlayerFromWatch(design.Name); + if (!EnabledDesigns.TryGetValue(design.Name, out var designs)) + return false; + if (!designs.Remove(design)) + return false; + + if (designs.Count == 0) + { + EnabledDesigns.Remove(design.Name); + Glamourer.PlayerWatcher.RemovePlayerFromWatch(design.Name); + } + return true; } public FixedDesigns(DesignManager designs) { + JobGroups = GameData.JobGroups(Dalamud.GameData); Data = new List(Glamourer.Config.FixedDesigns.Count); - EnabledDesigns = new Dictionary(Glamourer.Config.FixedDesigns.Count); + EnabledDesigns = new Dictionary>(Glamourer.Config.FixedDesigns.Count); Glamourer.PlayerWatcher.PlayerChanged += OnPlayerChange; var changes = false; for (var i = 0; i < Glamourer.Config.FixedDesigns.Count; ++i) @@ -79,7 +95,9 @@ namespace Glamourer.Designs var save = Glamourer.Config.FixedDesigns[i]; if (designs.FileSystem.Find(save.Path, out var d) && d is Design design) { - Data.Add(new FixedDesign(save.Name, design, save.Enabled)); + if (!JobGroups.TryGetValue((ushort) save.JobGroups, out var jobGroup)) + jobGroup = JobGroups[1]; + Data.Add(new FixedDesign(save.Name, design, save.Enabled, jobGroup)); if (save.Enabled) changes |= EnableDesign(Data.Last()); } @@ -98,18 +116,23 @@ namespace Glamourer.Designs private void OnPlayerChange(Character character) { var name = character.Name.ToString(); - if (EnabledDesigns.TryGetValue(name, out var design)) - { - PluginLog.Debug("Redrawing {CharacterName} with {DesignName}.", name, design.Design.FullName()); - design.Design.Data.Apply(character); - Glamourer.PlayerWatcher.UpdatePlayerWithoutEvent(character); - Glamourer.Penumbra.RedrawObject(character, RedrawType.WithSettings, false); - } + if (!EnabledDesigns.TryGetValue(name, out var designs)) + return; + + var design = designs.OrderBy(d => d.Jobs.Count).FirstOrDefault(d => d.Jobs.Fits(character.ClassJob.Id)); + if (design == null) + return; + + PluginLog.Debug("Redrawing {CharacterName} with {DesignName} for job {JobGroup}.", name, design.Design.FullName(), + design.Jobs.Name); + design.Design.Data.Apply(character); + Glamourer.PlayerWatcher.UpdatePlayerWithoutEvent(character); + Glamourer.Penumbra.RedrawObject(character, RedrawType.WithSettings, false); } - public void Add(string name, Design design, bool enabled = false) + public void Add(string name, Design design, JobGroup group, bool enabled = false) { - Data.Add(new FixedDesign(name, design, enabled)); + Data.Add(new FixedDesign(name, design, enabled, group)); Glamourer.Config.FixedDesigns.Add(Data.Last().ToSave()); if (enabled) diff --git a/Glamourer/GlamourerConfig.cs b/Glamourer/GlamourerConfig.cs index 0e28efd..f8cce1c 100644 --- a/Glamourer/GlamourerConfig.cs +++ b/Glamourer/GlamourerConfig.cs @@ -9,6 +9,7 @@ namespace Glamourer { public string Name; public string Path; + public uint JobGroups; public bool Enabled; } diff --git a/Glamourer/Gui/InterfaceFixedDesigns.cs b/Glamourer/Gui/InterfaceFixedDesigns.cs index 24e520a..6348686 100644 --- a/Glamourer/Gui/InterfaceFixedDesigns.cs +++ b/Glamourer/Gui/InterfaceFixedDesigns.cs @@ -16,6 +16,7 @@ namespace Glamourer.Gui private List? _fullPathCache; private string _newFixCharacterName = string.Empty; private string _newFixDesignPath = string.Empty; + private JobGroup? _newFixDesignGroup; private Design? _newFixDesign; private int _fixDragDropIdx = -1; @@ -24,24 +25,28 @@ namespace Glamourer.Gui private void DrawFixedDesignsTab() { + _newFixDesignGroup ??= _plugin.FixedDesigns.JobGroups[1]; + using var raii = new ImGuiRaii(); if (!raii.Begin(() => ImGui.BeginTabItem("Fixed Designs"), ImGui.EndTabItem)) { - _fullPathCache = null; - _newFixDesign = null; - _newFixDesignPath = string.Empty; + _fullPathCache = null; + _newFixDesign = null; + _newFixDesignPath = string.Empty; + _newFixDesignGroup = _plugin.FixedDesigns.JobGroups[1]; return; } _fullPathCache ??= _plugin.FixedDesigns.Data.Select(d => d.Design.FullName()).ToList(); - raii.Begin(() => ImGui.BeginTable("##FixedTable", 3), ImGui.EndTable); + raii.Begin(() => ImGui.BeginTable("##FixedTable", 4), ImGui.EndTable); var buttonWidth = 23.5f * ImGuiHelpers.GlobalScale; ImGui.TableSetupColumn("##DeleteColumn", ImGuiTableColumnFlags.WidthFixed, 2 * buttonWidth); ImGui.TableSetupColumn("Character", ImGuiTableColumnFlags.WidthFixed, 200 * ImGuiHelpers.GlobalScale); + ImGui.TableSetupColumn("Jobs", ImGuiTableColumnFlags.WidthFixed, 175 * ImGuiHelpers.GlobalScale); ImGui.TableSetupColumn("Design", ImGuiTableColumnFlags.WidthStretch); ImGui.TableHeadersRow(); var xPos = 0f; @@ -92,6 +97,8 @@ namespace Glamourer.Gui ImGui.EndDragDropTarget(); } + ImGui.TableNextColumn(); + ImGui.Text(_plugin.FixedDesigns.Data[i].Jobs.Name); ImGui.TableNextColumn(); ImGui.Text(path); } @@ -110,16 +117,30 @@ namespace Glamourer.Gui else if (ImGui.Button($"{FontAwesomeIcon.Plus.ToIconChar()}##NewFix")) { _fullPathCache.Add(_newFixDesignPath); - _plugin.FixedDesigns.Add(_newFixCharacterName, _newFixDesign, false); + _plugin.FixedDesigns.Add(_newFixCharacterName, _newFixDesign, _newFixDesignGroup.Value, false); _newFixCharacterName = string.Empty; _newFixDesignPath = string.Empty; _newFixDesign = null; + _newFixDesignGroup = _plugin.FixedDesigns.JobGroups[1]; } raii.PopFonts(); ImGui.TableNextColumn(); ImGui.SetNextItemWidth(200 * ImGuiHelpers.GlobalScale); ImGui.InputTextWithHint("##NewFix", "Enter new Character", ref _newFixCharacterName, 32); + ImGui.TableNextColumn(); + ImGui.SetNextItemWidth(-1); + if (raii.Begin(() => ImGui.BeginCombo("##NewFixDesignGroup", _newFixDesignGroup.Value.Name), ImGui.EndCombo)) + { + foreach (var (id, group) in _plugin.FixedDesigns.JobGroups) + { + ImGui.SetNextItemWidth(-1); + if (ImGui.Selectable($"{group.Name}##NewFixDesignGroup", group.Name == _newFixDesignGroup.Value.Name)) + _newFixDesignGroup = group; + } + raii.End(); + } + ImGui.TableNextColumn(); ImGui.SetNextItemWidth(-1); if (!raii.Begin(() => ImGui.BeginCombo("##NewFixPath", _newFixDesignPath), ImGui.EndCombo))