mirror of
https://github.com/Ottermandias/Glamourer.git
synced 2025-12-12 18:27:24 +01:00
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.
This commit is contained in:
parent
9dafc65975
commit
6d5503b3bc
6 changed files with 201 additions and 35 deletions
|
|
@ -11,6 +11,8 @@ namespace Glamourer
|
||||||
{
|
{
|
||||||
private static Dictionary<byte, Stain>? _stains;
|
private static Dictionary<byte, Stain>? _stains;
|
||||||
private static Dictionary<EquipSlot, List<Item>>? _itemsBySlot;
|
private static Dictionary<EquipSlot, List<Item>>? _itemsBySlot;
|
||||||
|
private static Dictionary<byte, Job>? _jobs;
|
||||||
|
private static Dictionary<ushort, JobGroup>? _jobGroups;
|
||||||
private static SortedList<uint, ModelChara>? _models;
|
private static SortedList<uint, ModelChara>? _models;
|
||||||
|
|
||||||
public static IReadOnlyDictionary<uint, ModelChara> Models(DataManager dataManager)
|
public static IReadOnlyDictionary<uint, ModelChara> Models(DataManager dataManager)
|
||||||
|
|
@ -84,5 +86,53 @@ namespace Glamourer
|
||||||
_itemsBySlot[EquipSlot.LFinger] = _itemsBySlot[EquipSlot.RFinger];
|
_itemsBySlot[EquipSlot.LFinger] = _itemsBySlot[EquipSlot.RFinger];
|
||||||
return _itemsBySlot;
|
return _itemsBySlot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static IReadOnlyDictionary<byte, Job> Jobs(DataManager dataManager)
|
||||||
|
{
|
||||||
|
if (_jobs != null)
|
||||||
|
return _jobs;
|
||||||
|
|
||||||
|
var sheet = dataManager.GetExcelSheet<ClassJob>()!;
|
||||||
|
_jobs = sheet.ToDictionary(j => (byte)j.RowId, j => new Job(j));
|
||||||
|
return _jobs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IReadOnlyDictionary<ushort, JobGroup> JobGroups(DataManager dataManager)
|
||||||
|
{
|
||||||
|
if (_jobGroups != null)
|
||||||
|
return _jobGroups;
|
||||||
|
|
||||||
|
var sheet = dataManager.GetExcelSheet<ClassJobCategory>()!;
|
||||||
|
var jobs = dataManager.GetExcelSheet<ClassJob>()!;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
21
Glamourer.GameData/Job.cs
Normal file
21
Glamourer.GameData/Job.cs
Normal file
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
50
Glamourer.GameData/JobGroup.cs
Normal file
50
Glamourer.GameData/JobGroup.cs
Normal file
|
|
@ -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<ClassJob> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -13,44 +13,50 @@ namespace Glamourer.Designs
|
||||||
{
|
{
|
||||||
public class FixedDesign
|
public class FixedDesign
|
||||||
{
|
{
|
||||||
public string Name;
|
public string Name;
|
||||||
public Design Design;
|
public JobGroup Jobs;
|
||||||
public bool Enabled;
|
public Design Design;
|
||||||
|
public bool Enabled;
|
||||||
|
|
||||||
public GlamourerConfig.FixedDesign ToSave()
|
public GlamourerConfig.FixedDesign ToSave()
|
||||||
=> new()
|
=> new()
|
||||||
{
|
{
|
||||||
Name = Name,
|
Name = Name,
|
||||||
Path = Design.FullName(),
|
Path = Design.FullName(),
|
||||||
Enabled = Enabled,
|
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;
|
Name = name;
|
||||||
Design = design;
|
Design = design;
|
||||||
Enabled = enabled;
|
Enabled = enabled;
|
||||||
|
Jobs = jobs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<FixedDesign> Data;
|
public List<FixedDesign> Data;
|
||||||
public Dictionary<string, FixedDesign> EnabledDesigns;
|
public Dictionary<string, List<FixedDesign>> EnabledDesigns;
|
||||||
|
public readonly IReadOnlyDictionary<ushort, JobGroup> JobGroups;
|
||||||
|
|
||||||
public bool EnableDesign(FixedDesign design)
|
public bool EnableDesign(FixedDesign design)
|
||||||
{
|
{
|
||||||
var changes = !design.Enabled;
|
var changes = !design.Enabled;
|
||||||
if (EnabledDesigns.TryGetValue(design.Name, out var oldDesign))
|
|
||||||
{
|
if (!EnabledDesigns.TryGetValue(design.Name, out var designs))
|
||||||
oldDesign.Enabled = false;
|
|
||||||
changes = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
|
EnabledDesigns[design.Name] = new List<FixedDesign> { design };
|
||||||
Glamourer.PlayerWatcher.AddPlayerToWatch(design.Name);
|
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)
|
if (Dalamud.Objects.FirstOrDefault(o => o.ObjectKind == ObjectKind.Player && o.Name.ToString() == design.Name)
|
||||||
is Character character)
|
is Character character)
|
||||||
OnPlayerChange(character);
|
OnPlayerChange(character);
|
||||||
|
|
@ -63,15 +69,25 @@ namespace Glamourer.Designs
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
design.Enabled = false;
|
design.Enabled = false;
|
||||||
EnabledDesigns.Remove(design.Name);
|
if (!EnabledDesigns.TryGetValue(design.Name, out var designs))
|
||||||
Glamourer.PlayerWatcher.RemovePlayerFromWatch(design.Name);
|
return false;
|
||||||
|
if (!designs.Remove(design))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (designs.Count == 0)
|
||||||
|
{
|
||||||
|
EnabledDesigns.Remove(design.Name);
|
||||||
|
Glamourer.PlayerWatcher.RemovePlayerFromWatch(design.Name);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public FixedDesigns(DesignManager designs)
|
public FixedDesigns(DesignManager designs)
|
||||||
{
|
{
|
||||||
|
JobGroups = GameData.JobGroups(Dalamud.GameData);
|
||||||
Data = new List<FixedDesign>(Glamourer.Config.FixedDesigns.Count);
|
Data = new List<FixedDesign>(Glamourer.Config.FixedDesigns.Count);
|
||||||
EnabledDesigns = new Dictionary<string, FixedDesign>(Glamourer.Config.FixedDesigns.Count);
|
EnabledDesigns = new Dictionary<string, List<FixedDesign>>(Glamourer.Config.FixedDesigns.Count);
|
||||||
Glamourer.PlayerWatcher.PlayerChanged += OnPlayerChange;
|
Glamourer.PlayerWatcher.PlayerChanged += OnPlayerChange;
|
||||||
var changes = false;
|
var changes = false;
|
||||||
for (var i = 0; i < Glamourer.Config.FixedDesigns.Count; ++i)
|
for (var i = 0; i < Glamourer.Config.FixedDesigns.Count; ++i)
|
||||||
|
|
@ -79,7 +95,9 @@ namespace Glamourer.Designs
|
||||||
var save = Glamourer.Config.FixedDesigns[i];
|
var save = Glamourer.Config.FixedDesigns[i];
|
||||||
if (designs.FileSystem.Find(save.Path, out var d) && d is Design design)
|
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)
|
if (save.Enabled)
|
||||||
changes |= EnableDesign(Data.Last());
|
changes |= EnableDesign(Data.Last());
|
||||||
}
|
}
|
||||||
|
|
@ -98,18 +116,23 @@ namespace Glamourer.Designs
|
||||||
private void OnPlayerChange(Character character)
|
private void OnPlayerChange(Character character)
|
||||||
{
|
{
|
||||||
var name = character.Name.ToString();
|
var name = character.Name.ToString();
|
||||||
if (EnabledDesigns.TryGetValue(name, out var design))
|
if (!EnabledDesigns.TryGetValue(name, out var designs))
|
||||||
{
|
return;
|
||||||
PluginLog.Debug("Redrawing {CharacterName} with {DesignName}.", name, design.Design.FullName());
|
|
||||||
design.Design.Data.Apply(character);
|
var design = designs.OrderBy(d => d.Jobs.Count).FirstOrDefault(d => d.Jobs.Fits(character.ClassJob.Id));
|
||||||
Glamourer.PlayerWatcher.UpdatePlayerWithoutEvent(character);
|
if (design == null)
|
||||||
Glamourer.Penumbra.RedrawObject(character, RedrawType.WithSettings, false);
|
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());
|
Glamourer.Config.FixedDesigns.Add(Data.Last().ToSave());
|
||||||
|
|
||||||
if (enabled)
|
if (enabled)
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ namespace Glamourer
|
||||||
{
|
{
|
||||||
public string Name;
|
public string Name;
|
||||||
public string Path;
|
public string Path;
|
||||||
|
public uint JobGroups;
|
||||||
public bool Enabled;
|
public bool Enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ namespace Glamourer.Gui
|
||||||
private List<string>? _fullPathCache;
|
private List<string>? _fullPathCache;
|
||||||
private string _newFixCharacterName = string.Empty;
|
private string _newFixCharacterName = string.Empty;
|
||||||
private string _newFixDesignPath = string.Empty;
|
private string _newFixDesignPath = string.Empty;
|
||||||
|
private JobGroup? _newFixDesignGroup;
|
||||||
private Design? _newFixDesign;
|
private Design? _newFixDesign;
|
||||||
private int _fixDragDropIdx = -1;
|
private int _fixDragDropIdx = -1;
|
||||||
|
|
||||||
|
|
@ -24,24 +25,28 @@ namespace Glamourer.Gui
|
||||||
|
|
||||||
private void DrawFixedDesignsTab()
|
private void DrawFixedDesignsTab()
|
||||||
{
|
{
|
||||||
|
_newFixDesignGroup ??= _plugin.FixedDesigns.JobGroups[1];
|
||||||
|
|
||||||
using var raii = new ImGuiRaii();
|
using var raii = new ImGuiRaii();
|
||||||
if (!raii.Begin(() => ImGui.BeginTabItem("Fixed Designs"), ImGui.EndTabItem))
|
if (!raii.Begin(() => ImGui.BeginTabItem("Fixed Designs"), ImGui.EndTabItem))
|
||||||
{
|
{
|
||||||
_fullPathCache = null;
|
_fullPathCache = null;
|
||||||
_newFixDesign = null;
|
_newFixDesign = null;
|
||||||
_newFixDesignPath = string.Empty;
|
_newFixDesignPath = string.Empty;
|
||||||
|
_newFixDesignGroup = _plugin.FixedDesigns.JobGroups[1];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_fullPathCache ??= _plugin.FixedDesigns.Data.Select(d => d.Design.FullName()).ToList();
|
_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;
|
var buttonWidth = 23.5f * ImGuiHelpers.GlobalScale;
|
||||||
|
|
||||||
|
|
||||||
ImGui.TableSetupColumn("##DeleteColumn", ImGuiTableColumnFlags.WidthFixed, 2 * buttonWidth);
|
ImGui.TableSetupColumn("##DeleteColumn", ImGuiTableColumnFlags.WidthFixed, 2 * buttonWidth);
|
||||||
ImGui.TableSetupColumn("Character", ImGuiTableColumnFlags.WidthFixed, 200 * ImGuiHelpers.GlobalScale);
|
ImGui.TableSetupColumn("Character", ImGuiTableColumnFlags.WidthFixed, 200 * ImGuiHelpers.GlobalScale);
|
||||||
|
ImGui.TableSetupColumn("Jobs", ImGuiTableColumnFlags.WidthFixed, 175 * ImGuiHelpers.GlobalScale);
|
||||||
ImGui.TableSetupColumn("Design", ImGuiTableColumnFlags.WidthStretch);
|
ImGui.TableSetupColumn("Design", ImGuiTableColumnFlags.WidthStretch);
|
||||||
ImGui.TableHeadersRow();
|
ImGui.TableHeadersRow();
|
||||||
var xPos = 0f;
|
var xPos = 0f;
|
||||||
|
|
@ -92,6 +97,8 @@ namespace Glamourer.Gui
|
||||||
ImGui.EndDragDropTarget();
|
ImGui.EndDragDropTarget();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
ImGui.Text(_plugin.FixedDesigns.Data[i].Jobs.Name);
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
ImGui.Text(path);
|
ImGui.Text(path);
|
||||||
}
|
}
|
||||||
|
|
@ -110,16 +117,30 @@ namespace Glamourer.Gui
|
||||||
else if (ImGui.Button($"{FontAwesomeIcon.Plus.ToIconChar()}##NewFix"))
|
else if (ImGui.Button($"{FontAwesomeIcon.Plus.ToIconChar()}##NewFix"))
|
||||||
{
|
{
|
||||||
_fullPathCache.Add(_newFixDesignPath);
|
_fullPathCache.Add(_newFixDesignPath);
|
||||||
_plugin.FixedDesigns.Add(_newFixCharacterName, _newFixDesign, false);
|
_plugin.FixedDesigns.Add(_newFixCharacterName, _newFixDesign, _newFixDesignGroup.Value, false);
|
||||||
_newFixCharacterName = string.Empty;
|
_newFixCharacterName = string.Empty;
|
||||||
_newFixDesignPath = string.Empty;
|
_newFixDesignPath = string.Empty;
|
||||||
_newFixDesign = null;
|
_newFixDesign = null;
|
||||||
|
_newFixDesignGroup = _plugin.FixedDesigns.JobGroups[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
raii.PopFonts();
|
raii.PopFonts();
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
ImGui.SetNextItemWidth(200 * ImGuiHelpers.GlobalScale);
|
ImGui.SetNextItemWidth(200 * ImGuiHelpers.GlobalScale);
|
||||||
ImGui.InputTextWithHint("##NewFix", "Enter new Character", ref _newFixCharacterName, 32);
|
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.TableNextColumn();
|
||||||
ImGui.SetNextItemWidth(-1);
|
ImGui.SetNextItemWidth(-1);
|
||||||
if (!raii.Begin(() => ImGui.BeginCombo("##NewFixPath", _newFixDesignPath), ImGui.EndCombo))
|
if (!raii.Begin(() => ImGui.BeginCombo("##NewFixPath", _newFixDesignPath), ImGui.EndCombo))
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue