Add more mod association and modded utility.

This commit is contained in:
Ottermandias 2025-02-13 16:32:23 +01:00
parent ab2a3f5bd9
commit d56c2db547
9 changed files with 277 additions and 90 deletions

View file

@ -17,7 +17,6 @@ using OtterGui;
using OtterGui.Classes; using OtterGui.Classes;
using OtterGui.Raii; using OtterGui.Raii;
using OtterGui.Text; using OtterGui.Text;
using OtterGuiInternal.Structs;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using static Glamourer.Gui.Tabs.HeaderDrawer; using static Glamourer.Gui.Tabs.HeaderDrawer;

View file

@ -15,7 +15,7 @@ namespace Glamourer.Gui.Tabs.DesignTab;
public class ModAssociationsTab(PenumbraService penumbra, DesignFileSystemSelector selector, DesignManager manager, Configuration config) public class ModAssociationsTab(PenumbraService penumbra, DesignFileSystemSelector selector, DesignManager manager, Configuration config)
{ {
private readonly ModCombo _modCombo = new(penumbra, Glamourer.Log); private readonly ModCombo _modCombo = new(penumbra, Glamourer.Log, selector);
private (Mod, ModSettings)[]? _copy; private (Mod, ModSettings)[]? _copy;
public void Draw() public void Draw()

View file

@ -4,19 +4,18 @@ using ImGuiNET;
using OtterGui.Classes; using OtterGui.Classes;
using OtterGui.Log; using OtterGui.Log;
using OtterGui.Raii; using OtterGui.Raii;
using OtterGui.Text;
using OtterGui.Widgets; using OtterGui.Widgets;
namespace Glamourer.Gui.Tabs.DesignTab; namespace Glamourer.Gui.Tabs.DesignTab;
public sealed class ModCombo : FilterComboCache<(Mod Mod, ModSettings Settings)> public sealed class ModCombo : FilterComboCache<(Mod Mod, ModSettings Settings, int Count)>
{ {
public ModCombo(PenumbraService penumbra, Logger log) public ModCombo(PenumbraService penumbra, Logger log, DesignFileSystemSelector selector)
: base(penumbra.GetMods, MouseWheelType.None, log) : base(() => penumbra.GetMods(selector.Selected?.FilteredItemNames.ToArray() ?? []), MouseWheelType.None, log)
{ => SearchByParts = false;
SearchByParts = false;
}
protected override string ToString((Mod Mod, ModSettings Settings) obj) protected override string ToString((Mod Mod, ModSettings Settings, int Count) obj)
=> obj.Mod.Name; => obj.Mod.Name;
protected override bool IsVisible(int globalIndex, LowerString filter) protected override bool IsVisible(int globalIndex, LowerString filter)
@ -24,36 +23,45 @@ public sealed class ModCombo : FilterComboCache<(Mod Mod, ModSettings Settings)>
protected override bool DrawSelectable(int globalIdx, bool selected) protected override bool DrawSelectable(int globalIdx, bool selected)
{ {
using var id = ImRaii.PushId(globalIdx); using var id = ImUtf8.PushId(globalIdx);
var (mod, settings) = Items[globalIdx]; var (mod, settings, count) = Items[globalIdx];
bool ret; bool ret;
using (var color = ImRaii.PushColor(ImGuiCol.Text, ImGui.GetColorU32(ImGuiCol.TextDisabled), !settings.Enabled)) var color = settings.Enabled
? count > 0
? ColorId.ContainsItemsEnabled.Value()
: ImGui.GetColorU32(ImGuiCol.Text)
: count > 0
? ColorId.ContainsItemsDisabled.Value()
: ImGui.GetColorU32(ImGuiCol.TextDisabled);
using (ImRaii.PushColor(ImGuiCol.Text, color))
{ {
ret = ImGui.Selectable(mod.Name, selected); ret = ImUtf8.Selectable(mod.Name, selected);
} }
if (ImGui.IsItemHovered()) if (ImGui.IsItemHovered())
{ {
using var style = ImRaii.PushStyle(ImGuiStyleVar.PopupBorderSize, 2 * ImGuiHelpers.GlobalScale); using var style = ImRaii.PushStyle(ImGuiStyleVar.PopupBorderSize, 2 * ImGuiHelpers.GlobalScale);
using var tt = ImRaii.Tooltip(); using var tt = ImUtf8.Tooltip();
var namesDifferent = mod.Name != mod.DirectoryName; var namesDifferent = mod.Name != mod.DirectoryName;
ImGui.Dummy(new Vector2(300 * ImGuiHelpers.GlobalScale, 0)); ImGui.Dummy(new Vector2(300 * ImGuiHelpers.GlobalScale, 0));
using (var group = ImRaii.Group()) using (ImUtf8.Group())
{ {
if (namesDifferent) if (namesDifferent)
ImGui.TextUnformatted("Directory Name"); ImUtf8.Text("Directory Name"u8);
ImGui.TextUnformatted("Enabled"); ImUtf8.Text("Enabled"u8);
ImGui.TextUnformatted("Priority"); ImUtf8.Text("Priority"u8);
ImUtf8.Text("Affected Design Items"u8);
DrawSettingsLeft(settings); DrawSettingsLeft(settings);
} }
ImGui.SameLine(Math.Max(ImGui.GetItemRectSize().X + 3 * ImGui.GetStyle().ItemSpacing.X, 150 * ImGuiHelpers.GlobalScale)); ImGui.SameLine(Math.Max(ImGui.GetItemRectSize().X + 3 * ImGui.GetStyle().ItemSpacing.X, 150 * ImGuiHelpers.GlobalScale));
using (var group = ImRaii.Group()) using (ImUtf8.Group())
{ {
if (namesDifferent) if (namesDifferent)
ImGui.TextUnformatted(mod.DirectoryName); ImUtf8.Text(mod.DirectoryName);
ImGui.TextUnformatted(settings.Enabled.ToString()); ImUtf8.Text($"{settings.Enabled}");
ImGui.TextUnformatted(settings.Priority.ToString()); ImUtf8.Text($"{settings.Priority}");
ImUtf8.Text($"{count}");
DrawSettingsRight(settings); DrawSettingsRight(settings);
} }
} }
@ -65,7 +73,7 @@ public sealed class ModCombo : FilterComboCache<(Mod Mod, ModSettings Settings)>
{ {
foreach (var setting in settings.Settings) foreach (var setting in settings.Settings)
{ {
ImGui.TextUnformatted(setting.Key); ImUtf8.Text(setting.Key);
for (var i = 1; i < setting.Value.Count; ++i) for (var i = 1; i < setting.Value.Count; ++i)
ImGui.NewLine(); ImGui.NewLine();
} }
@ -76,10 +84,10 @@ public sealed class ModCombo : FilterComboCache<(Mod Mod, ModSettings Settings)>
foreach (var setting in settings.Settings) foreach (var setting in settings.Settings)
{ {
if (setting.Value.Count == 0) if (setting.Value.Count == 0)
ImGui.TextUnformatted("<None Enabled>"); ImUtf8.Text("<None Enabled>"u8);
else else
foreach (var option in setting.Value) foreach (var option in setting.Value)
ImGui.TextUnformatted(option); ImUtf8.Text(option);
} }
} }
} }

View file

@ -5,11 +5,15 @@ using ImGuiNET;
using OtterGui; using OtterGui;
using OtterGui.Raii; using OtterGui.Raii;
using OtterGui.Text; using OtterGui.Text;
using static Glamourer.Gui.Tabs.HeaderDrawer;
namespace Glamourer.Gui.Tabs.DesignTab; namespace Glamourer.Gui.Tabs.DesignTab;
public class MultiDesignPanel(DesignFileSystemSelector selector, DesignManager editor, DesignColors colors) public class MultiDesignPanel(DesignFileSystemSelector selector, DesignManager editor, DesignColors colors, Configuration config)
{ {
private readonly Button[] _leftButtons = [];
private readonly Button[] _rightButtons = [new IncognitoButton(config.Ephemeral)];
private readonly DesignColorCombo _colorCombo = new(colors, true); private readonly DesignColorCombo _colorCombo = new(colors, true);
public void Draw() public void Draw()
@ -17,8 +21,12 @@ public class MultiDesignPanel(DesignFileSystemSelector selector, DesignManager e
if (selector.SelectedPaths.Count == 0) if (selector.SelectedPaths.Count == 0)
return; return;
var width = ImGuiHelpers.ScaledVector2(145, 0); HeaderDrawer.Draw(string.Empty, 0, ImGui.GetColorU32(ImGuiCol.FrameBg), _leftButtons, _rightButtons);
ImGui.NewLine(); using var child = ImUtf8.Child("##MultiPanel"u8, default, true);
if (!child)
return;
var width = ImGuiHelpers.ScaledVector2(145, 0);
var treeNodePos = ImGui.GetCursorPos(); var treeNodePos = ImGui.GetCursorPos();
_numDesigns = DrawDesignList(); _numDesigns = DrawDesignList();
DrawCounts(treeNodePos); DrawCounts(treeNodePos);
@ -135,7 +143,7 @@ public class MultiDesignPanel(DesignFileSystemSelector selector, DesignManager e
{ {
ImUtf8.TextFrameAligned("Multi Tagger:"u8); ImUtf8.TextFrameAligned("Multi Tagger:"u8);
ImGui.SameLine(); ImGui.SameLine();
var offset = ImGui.GetItemRectSize().X; var offset = ImGui.GetItemRectSize().X + ImGui.GetStyle().WindowPadding.X;
ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X - 2 * (width.X + ImGui.GetStyle().ItemSpacing.X)); ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X - 2 * (width.X + ImGui.GetStyle().ItemSpacing.X));
ImUtf8.InputText("##tag"u8, ref _tag, "Tag Name..."u8); ImUtf8.InputText("##tag"u8, ref _tag, "Tag Name..."u8);

View file

@ -1,8 +1,8 @@
using Dalamud.Game.Text.SeStringHandling; using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Interface.Utility; using Dalamud.Interface.Utility;
using Glamourer.Designs;
using Glamourer.GameData; using Glamourer.GameData;
using Glamourer.Interop; using Glamourer.Interop;
using Glamourer.Interop.Penumbra;
using Glamourer.Services; using Glamourer.Services;
using Glamourer.Unlocks; using Glamourer.Unlocks;
using ImGuiNET; using ImGuiNET;
@ -23,7 +23,8 @@ public class UnlockOverview(
TextureService textures, TextureService textures,
CodeService codes, CodeService codes,
JobService jobs, JobService jobs,
FavoriteManager favorites) FavoriteManager favorites,
PenumbraService penumbra)
{ {
private static readonly Vector4 UnavailableTint = new(0.3f, 0.3f, 0.3f, 1.0f); private static readonly Vector4 UnavailableTint = new(0.3f, 0.3f, 0.3f, 1.0f);
@ -32,6 +33,9 @@ public class UnlockOverview(
private Gender _selected3 = Gender.Unknown; private Gender _selected3 = Gender.Unknown;
private BonusItemFlag _selected4 = BonusItemFlag.Unknown; private BonusItemFlag _selected4 = BonusItemFlag.Unknown;
private uint _favoriteColor;
private uint _moddedColor;
private void DrawSelector() private void DrawSelector()
{ {
using var child = ImRaii.Child("Selector", new Vector2(200 * ImGuiHelpers.GlobalScale, -1), true); using var child = ImRaii.Child("Selector", new Vector2(200 * ImGuiHelpers.GlobalScale, -1), true);
@ -90,6 +94,9 @@ public class UnlockOverview(
if (!child) if (!child)
return; return;
_moddedColor = ColorId.ModdedItemMarker.Value();
_favoriteColor = ColorId.FavoriteStarOn.Value();
if (_selected1 is not FullEquipType.Unknown) if (_selected1 is not FullEquipType.Unknown)
DrawItems(); DrawItems();
else if (_selected2 is not SubRace.Unknown && _selected3 is not Gender.Unknown) else if (_selected2 is not SubRace.Unknown && _selected3 is not Gender.Unknown)
@ -120,7 +127,7 @@ public class UnlockOverview(
unlocked || codes.Enabled(CodeService.CodeFlag.Shirts) ? Vector4.One : UnavailableTint); unlocked || codes.Enabled(CodeService.CodeFlag.Shirts) ? Vector4.One : UnavailableTint);
if (favorites.Contains(_selected3, _selected2, customize.Index, customize.Value)) if (favorites.Contains(_selected3, _selected2, customize.Index, customize.Value))
ImGui.GetWindowDrawList().AddRect(ImGui.GetItemRectMin(), ImGui.GetItemRectMax(), ColorId.FavoriteStarOn.Value(), ImGui.GetWindowDrawList().AddRect(ImGui.GetItemRectMin(), ImGui.GetItemRectMax(), _favoriteColor,
12 * ImGuiHelpers.GlobalScale, ImDrawFlags.RoundCornersAll, 6 * ImGuiHelpers.GlobalScale); 12 * ImGuiHelpers.GlobalScale, ImDrawFlags.RoundCornersAll, 6 * ImGuiHelpers.GlobalScale);
if (hasIcon && ImGui.IsItemHovered()) if (hasIcon && ImGui.IsItemHovered())
@ -192,9 +199,11 @@ public class UnlockOverview(
ImGui.Image(icon, iconSize, Vector2.Zero, Vector2.One, ImGui.Image(icon, iconSize, Vector2.Zero, Vector2.One,
unlocked || codes.Enabled(CodeService.CodeFlag.Shirts) ? Vector4.One : UnavailableTint); unlocked || codes.Enabled(CodeService.CodeFlag.Shirts) ? Vector4.One : UnavailableTint);
if (favorites.Contains(item)) if (favorites.Contains(item))
ImGui.GetWindowDrawList().AddRect(ImGui.GetItemRectMin(), ImGui.GetItemRectMax(), ColorId.FavoriteStarOn.Value(), ImGui.GetWindowDrawList().AddRect(ImGui.GetItemRectMin(), ImGui.GetItemRectMax(), _favoriteColor,
2 * ImGuiHelpers.GlobalScale, ImDrawFlags.RoundCornersAll, 4 * ImGuiHelpers.GlobalScale); 2 * ImGuiHelpers.GlobalScale, ImDrawFlags.RoundCornersAll, 4 * ImGuiHelpers.GlobalScale);
var mods = DrawModdedMarker(item, iconSize);
// TODO handle clicking // TODO handle clicking
if (ImGui.IsItemHovered()) if (ImGui.IsItemHovered())
{ {
@ -206,9 +215,10 @@ public class UnlockOverview(
ImUtf8.Text($"{item.Id.Id}"); ImUtf8.Text($"{item.Id.Id}");
ImUtf8.Text($"{item.PrimaryId.Id}-{item.Variant.Id}"); ImUtf8.Text($"{item.PrimaryId.Id}-{item.Variant.Id}");
// TODO // TODO
ImUtf8.Text("Always Unlocked"); // : $"Unlocked on {time:g}" : "Not Unlocked."); ImUtf8.Text("Always Unlocked"u8); // : $"Unlocked on {time:g}" : "Not Unlocked.");
// TODO // TODO
//tooltip.CreateTooltip(item, string.Empty, false); //tooltip.CreateTooltip(item, string.Empty, false);
DrawModTooltip(mods);
} }
} }
} }
@ -263,6 +273,8 @@ public class UnlockOverview(
ImGui.GetWindowDrawList().AddRect(ImGui.GetItemRectMin(), ImGui.GetItemRectMax(), ColorId.FavoriteStarOn.Value(), ImGui.GetWindowDrawList().AddRect(ImGui.GetItemRectMin(), ImGui.GetItemRectMax(), ColorId.FavoriteStarOn.Value(),
2 * ImGuiHelpers.GlobalScale, ImDrawFlags.RoundCornersAll, 4 * ImGuiHelpers.GlobalScale); 2 * ImGuiHelpers.GlobalScale, ImDrawFlags.RoundCornersAll, 4 * ImGuiHelpers.GlobalScale);
var mods = DrawModdedMarker(item, iconSize);
if (ImGui.IsItemClicked()) if (ImGui.IsItemClicked())
Glamourer.Messager.Chat.Print(new SeStringBuilder().AddItemLink(item.ItemId.Id, false).BuiltString); Glamourer.Messager.Chat.Print(new SeStringBuilder().AddItemLink(item.ItemId.Id, false).BuiltString);
@ -306,6 +318,7 @@ public class UnlockOverview(
ImGui.TextUnformatted("Tradable"); ImGui.TextUnformatted("Tradable");
if (item.Flags.HasFlag(ItemFlags.IsCrestWorthy)) if (item.Flags.HasFlag(ItemFlags.IsCrestWorthy))
ImGui.TextUnformatted("Can apply Crest"); ImGui.TextUnformatted("Can apply Crest");
DrawModTooltip(mods);
tooltip.CreateTooltip(item, string.Empty, false); tooltip.CreateTooltip(item, string.Empty, false);
} }
} }
@ -316,4 +329,36 @@ public class UnlockOverview(
private static int IconsPerRow(float iconWidth, float iconSpacing) private static int IconsPerRow(float iconWidth, float iconSpacing)
=> (int)(ImGui.GetContentRegionAvail().X / (iconWidth + iconSpacing)); => (int)(ImGui.GetContentRegionAvail().X / (iconWidth + iconSpacing));
[MethodImpl(MethodImplOptions.AggressiveOptimization | MethodImplOptions.AggressiveInlining)]
private (string ModDirectory, string ModName)[] DrawModdedMarker(in EquipItem item, Vector2 iconSize)
{
var mods = penumbra.CheckCurrentChangedItem(item.Name);
if (mods.Length == 0)
return mods;
var center = ImGui.GetItemRectMin() + new Vector2(iconSize.X * 0.85f, iconSize.Y * 0.15f);
ImGui.GetWindowDrawList().AddCircleFilled(center, iconSize.X * 0.1f, _moddedColor);
ImGui.GetWindowDrawList().AddCircle(center, iconSize.X * 0.1f, 0xFF000000);
return mods;
}
[MethodImpl(MethodImplOptions.AggressiveOptimization | MethodImplOptions.AggressiveInlining)]
private void DrawModTooltip((string ModDirectory, string ModName)[] mods)
{
switch (mods.Length)
{
case 0: return;
case 1:
ImUtf8.Text("Modded by: "u8, _moddedColor);
ImGui.SameLine(0, 0);
ImUtf8.Text(mods[0].ModName);
return;
default:
ImUtf8.Text("Modded by:"u8, _moddedColor);
foreach (var (_, mod) in mods)
ImUtf8.BulletText(mod);
return;
}
}
} }

View file

@ -3,6 +3,7 @@ using Dalamud.Interface;
using Dalamud.Interface.Utility; using Dalamud.Interface.Utility;
using Glamourer.Events; using Glamourer.Events;
using Glamourer.Interop; using Glamourer.Interop;
using Glamourer.Interop.Penumbra;
using Glamourer.Services; using Glamourer.Services;
using Glamourer.Unlocks; using Glamourer.Unlocks;
using ImGuiNET; using ImGuiNET;
@ -17,12 +18,16 @@ namespace Glamourer.Gui.Tabs.UnlocksTab;
public class UnlockTable : Table<EquipItem>, IDisposable public class UnlockTable : Table<EquipItem>, IDisposable
{ {
private readonly ObjectUnlocked _event; private readonly ObjectUnlocked _event;
private readonly PenumbraService _penumbra;
private Guid _lastCurrentCollection = Guid.Empty;
public UnlockTable(ItemManager items, TextureService textures, ItemUnlockManager itemUnlocks, public UnlockTable(ItemManager items, TextureService textures, ItemUnlockManager itemUnlocks,
PenumbraChangedItemTooltip tooltip, ObjectUnlocked @event, JobService jobs, FavoriteManager favorites) PenumbraChangedItemTooltip tooltip, ObjectUnlocked @event, JobService jobs, FavoriteManager favorites, PenumbraService penumbra)
: base("ItemUnlockTable", new ItemList(items), : base("ItemUnlockTable", new ItemList(items),
new FavoriteColumn(favorites, @event) { Label = "F" }, new FavoriteColumn(favorites, @event) { Label = "F" },
new ModdedColumn(penumbra) { Label = "M" },
new NameColumn(textures, tooltip) { Label = "Item Name..." }, new NameColumn(textures, tooltip) { Label = "Item Name..." },
new SlotColumn { Label = "Equip Slot" }, new SlotColumn { Label = "Equip Slot" },
new TypeColumn { Label = "Item Type..." }, new TypeColumn { Label = "Item Type..." },
@ -36,14 +41,40 @@ public class UnlockTable : Table<EquipItem>, IDisposable
new TradableColumn { Label = "Trade" } new TradableColumn { Label = "Trade" }
) )
{ {
_event = @event; _event = @event;
Sortable = true; _penumbra = penumbra;
Flags |= ImGuiTableFlags.Hideable | ImGuiTableFlags.Reorderable | ImGuiTableFlags.Resizable; Sortable = true;
Flags |= ImGuiTableFlags.Hideable | ImGuiTableFlags.Reorderable | ImGuiTableFlags.Resizable;
_event.Subscribe(OnObjectUnlock, ObjectUnlocked.Priority.UnlockTable); _event.Subscribe(OnObjectUnlock, ObjectUnlocked.Priority.UnlockTable);
_penumbra.ModSettingChanged += OnModSettingsChanged;
}
private void OnModSettingsChanged(Penumbra.Api.Enums.ModSettingChange type, Guid collection, string mod, bool inherited)
{
if (collection != _lastCurrentCollection)
return;
FilterDirty = true;
SortDirty = true;
}
protected override void PreDraw()
{
var lastCurrentCollection = _penumbra.CurrentCollection.Id;
if (_lastCurrentCollection != lastCurrentCollection)
{
_lastCurrentCollection = lastCurrentCollection;
FilterDirty = true;
SortDirty = true;
}
} }
public void Dispose() public void Dispose()
=> _event.Unsubscribe(OnObjectUnlock); {
_event.Unsubscribe(OnObjectUnlock);
_penumbra.ModSettingChanged -= OnModSettingsChanged;
}
private sealed class FavoriteColumn : YesNoColumn<EquipItem> private sealed class FavoriteColumn : YesNoColumn<EquipItem>
{ {
@ -77,6 +108,66 @@ public class UnlockTable : Table<EquipItem>, IDisposable
=> _favorites.Contains(rhs).CompareTo(_favorites.Contains(lhs)); => _favorites.Contains(rhs).CompareTo(_favorites.Contains(lhs));
} }
private sealed class ModdedColumn : YesNoColumn<EquipItem>
{
public override float Width
=> ImGui.GetFrameHeightWithSpacing();
private readonly PenumbraService _penumbra;
private readonly Dictionary<CustomItemId, int> _compareCache = [];
public ModdedColumn(PenumbraService penumbra)
{
_penumbra = penumbra;
Flags |= ImGuiTableColumnFlags.NoResize;
}
public override void PostSort()
{
_compareCache.Clear();
}
public override void DrawColumn(EquipItem item, int idx)
{
var value = _penumbra.CheckCurrentChangedItem(item.Name);
if (value.Length == 0)
return;
using (ImRaii.PushFont(UiBuilder.IconFont))
{
using var color = ImRaii.PushColor(ImGuiCol.Text, ColorId.ModdedItemMarker.Value());
ImGuiUtil.Center(FontAwesomeIcon.Circle.ToIconString());
}
if (ImGui.IsItemHovered())
{
using var tt = ImUtf8.Tooltip();
foreach (var (_, mod) in value)
ImUtf8.BulletText(mod);
}
}
public override bool FilterFunc(EquipItem item)
=> FilterValue.HasFlag(_penumbra.CheckCurrentChangedItem(item.Name).Length > 0 ? YesNoFlag.Yes : YesNoFlag.No);
public override int Compare(EquipItem lhs, EquipItem rhs)
{
if (!_compareCache.TryGetValue(lhs.Id, out var lhsCount))
{
lhsCount = _penumbra.CheckCurrentChangedItem(lhs.Name).Length;
_compareCache[lhs.Id] = lhsCount;
}
if (!_compareCache.TryGetValue(rhs.Id, out var rhsCount))
{
rhsCount = _penumbra.CheckCurrentChangedItem(rhs.Name).Length;
_compareCache[rhs.Id] = rhsCount;
}
return lhsCount.CompareTo(rhsCount);
}
}
private sealed class NameColumn : ColumnString<EquipItem> private sealed class NameColumn : ColumnString<EquipItem>
{ {
private readonly TextureService _textures; private readonly TextureService _textures;
@ -317,7 +408,6 @@ public class UnlockTable : Table<EquipItem>, IDisposable
{ } { }
} }
private sealed class JobColumn : ColumnFlags<JobFlag, EquipItem> private sealed class JobColumn : ColumnFlags<JobFlag, EquipItem>
{ {
public override float Width public override float Width
@ -415,7 +505,6 @@ public class UnlockTable : Table<EquipItem>, IDisposable
} }
} }
private sealed class DyableColumn : ColumnFlags<DyableColumn.Dyable, EquipItem> private sealed class DyableColumn : ColumnFlags<DyableColumn.Dyable, EquipItem>
{ {
[Flags] [Flags]

View file

@ -38,6 +38,7 @@ public class PenumbraService : IDisposable
public const int RequiredPenumbraFeatureVersionTemp = 4; public const int RequiredPenumbraFeatureVersionTemp = 4;
public const int RequiredPenumbraFeatureVersionTemp2 = 5; public const int RequiredPenumbraFeatureVersionTemp2 = 5;
public const int RequiredPenumbraFeatureVersionTemp3 = 6; public const int RequiredPenumbraFeatureVersionTemp3 = 6;
public const int RequiredPenumbraFeatureVersionTemp4 = 7;
private const int Key = -1610; private const int Key = -1610;
@ -49,30 +50,33 @@ public class PenumbraService : IDisposable
private readonly EventSubscriber<nint, Guid, nint> _createdCharacterBase; private readonly EventSubscriber<nint, Guid, nint> _createdCharacterBase;
private readonly EventSubscriber<ModSettingChange, Guid, string, bool> _modSettingChanged; private readonly EventSubscriber<ModSettingChange, Guid, string, bool> _modSettingChanged;
private global::Penumbra.Api.IpcSubscribers.GetCollectionsByIdentifier? _collectionByIdentifier; private global::Penumbra.Api.IpcSubscribers.GetCollectionsByIdentifier? _collectionByIdentifier;
private global::Penumbra.Api.IpcSubscribers.GetCollections? _collections; private global::Penumbra.Api.IpcSubscribers.GetCollections? _collections;
private global::Penumbra.Api.IpcSubscribers.RedrawObject? _redraw; private global::Penumbra.Api.IpcSubscribers.RedrawObject? _redraw;
private global::Penumbra.Api.IpcSubscribers.GetDrawObjectInfo? _drawObjectInfo; private global::Penumbra.Api.IpcSubscribers.GetDrawObjectInfo? _drawObjectInfo;
private global::Penumbra.Api.IpcSubscribers.GetCutsceneParentIndex? _cutsceneParent; private global::Penumbra.Api.IpcSubscribers.GetCutsceneParentIndex? _cutsceneParent;
private global::Penumbra.Api.IpcSubscribers.GetCollectionForObject? _objectCollection; private global::Penumbra.Api.IpcSubscribers.GetCollectionForObject? _objectCollection;
private global::Penumbra.Api.IpcSubscribers.GetModList? _getMods; private global::Penumbra.Api.IpcSubscribers.GetModList? _getMods;
private global::Penumbra.Api.IpcSubscribers.GetCollection? _currentCollection; private global::Penumbra.Api.IpcSubscribers.GetCollection? _currentCollection;
private global::Penumbra.Api.IpcSubscribers.GetCurrentModSettingsWithTemp? _getCurrentSettingsWithTemp; private global::Penumbra.Api.IpcSubscribers.GetCurrentModSettingsWithTemp? _getCurrentSettingsWithTemp;
private global::Penumbra.Api.IpcSubscribers.GetCurrentModSettings? _getCurrentSettings; private global::Penumbra.Api.IpcSubscribers.GetCurrentModSettings? _getCurrentSettings;
private global::Penumbra.Api.IpcSubscribers.GetAllModSettings? _getAllSettings; private global::Penumbra.Api.IpcSubscribers.GetAllModSettings? _getAllSettings;
private global::Penumbra.Api.IpcSubscribers.TryInheritMod? _inheritMod; private global::Penumbra.Api.IpcSubscribers.TryInheritMod? _inheritMod;
private global::Penumbra.Api.IpcSubscribers.TrySetMod? _setMod; private global::Penumbra.Api.IpcSubscribers.TrySetMod? _setMod;
private global::Penumbra.Api.IpcSubscribers.TrySetModPriority? _setModPriority; private global::Penumbra.Api.IpcSubscribers.TrySetModPriority? _setModPriority;
private global::Penumbra.Api.IpcSubscribers.TrySetModSetting? _setModSetting; private global::Penumbra.Api.IpcSubscribers.TrySetModSetting? _setModSetting;
private global::Penumbra.Api.IpcSubscribers.TrySetModSettings? _setModSettings; private global::Penumbra.Api.IpcSubscribers.TrySetModSettings? _setModSettings;
private global::Penumbra.Api.IpcSubscribers.SetTemporaryModSettings? _setTemporaryModSettings; private global::Penumbra.Api.IpcSubscribers.SetTemporaryModSettings? _setTemporaryModSettings;
private global::Penumbra.Api.IpcSubscribers.SetTemporaryModSettingsPlayer? _setTemporaryModSettingsPlayer; private global::Penumbra.Api.IpcSubscribers.SetTemporaryModSettingsPlayer? _setTemporaryModSettingsPlayer;
private global::Penumbra.Api.IpcSubscribers.RemoveTemporaryModSettings? _removeTemporaryModSettings; private global::Penumbra.Api.IpcSubscribers.RemoveTemporaryModSettings? _removeTemporaryModSettings;
private global::Penumbra.Api.IpcSubscribers.RemoveTemporaryModSettingsPlayer? _removeTemporaryModSettingsPlayer; private global::Penumbra.Api.IpcSubscribers.RemoveTemporaryModSettingsPlayer? _removeTemporaryModSettingsPlayer;
private global::Penumbra.Api.IpcSubscribers.RemoveAllTemporaryModSettings? _removeAllTemporaryModSettings; private global::Penumbra.Api.IpcSubscribers.RemoveAllTemporaryModSettings? _removeAllTemporaryModSettings;
private global::Penumbra.Api.IpcSubscribers.RemoveAllTemporaryModSettingsPlayer? _removeAllTemporaryModSettingsPlayer; private global::Penumbra.Api.IpcSubscribers.RemoveAllTemporaryModSettingsPlayer? _removeAllTemporaryModSettingsPlayer;
private global::Penumbra.Api.IpcSubscribers.QueryTemporaryModSettings? _queryTemporaryModSettings; private global::Penumbra.Api.IpcSubscribers.QueryTemporaryModSettings? _queryTemporaryModSettings;
private global::Penumbra.Api.IpcSubscribers.OpenMainWindow? _openModPage; private global::Penumbra.Api.IpcSubscribers.OpenMainWindow? _openModPage;
private global::Penumbra.Api.IpcSubscribers.GetChangedItems? _getChangedItems;
private IReadOnlyList<(string ModDirectory, IReadOnlyDictionary<string, object?> ChangedItems)>? _changedItems;
private Func<string, (string ModDirectory, string ModName)[]>? _checkCurrentChangedItems;
private readonly IDisposable _initializedEvent; private readonly IDisposable _initializedEvent;
private readonly IDisposable _disposedEvent; private readonly IDisposable _disposedEvent;
@ -195,37 +199,58 @@ public class PenumbraService : IDisposable
return ret[0]; return ret[0];
} }
public IReadOnlyList<(Mod Mod, ModSettings Settings)> GetMods() public IReadOnlyList<(Mod Mod, ModSettings Settings, int Count)> GetMods(IReadOnlyList<string> data)
{ {
if (!Available) if (!Available)
return []; return [];
try try
{ {
var allMods = _getMods!.Invoke(); var allMods = _getMods!.Invoke();
var collection = _currentCollection!.Invoke(ApiCollectionType.Current); var currentCollection = _currentCollection!.Invoke(ApiCollectionType.Current);
if (_getAllSettings != null) var withSettings = WithSettings(allMods, currentCollection!.Value.Id);
var withCounts = WithCounts(withSettings, allMods.Count);
return OrderList(withCounts, allMods.Count);
IEnumerable<(Mod Mod, ModSettings Settings)> WithSettings(Dictionary<string, string> mods, Guid collection)
{ {
var allSettings = _getAllSettings.Invoke(collection!.Value.Id, false, false, Key); if (_getAllSettings != null)
if (allSettings.Item1 is PenumbraApiEc.Success) {
return allMods.Select(m => (new Mod(m.Value, m.Key), var allSettings = _getAllSettings.Invoke(collection, false, false, Key);
if (allSettings.Item1 is PenumbraApiEc.Success)
return mods.Select(m => (new Mod(m.Value, m.Key),
allSettings.Item2!.TryGetValue(m.Key, out var s) allSettings.Item2!.TryGetValue(m.Key, out var s)
? new ModSettings(s.Item3, s.Item2, s.Item1, s.Item4 && s.Item5, false) ? new ModSettings(s.Item3, s.Item2, s.Item1, s is { Item4: true, Item5: true }, false)
: ModSettings.Empty)) : ModSettings.Empty));
.OrderByDescending(p => p.Item2.Enabled) }
.ThenBy(p => p.Item1.Name)
.ThenBy(p => p.Item1.DirectoryName) return mods.Select(m => (new Mod(m.Value, m.Key), GetSettings(collection, m.Key, m.Value, out _)));
.ThenByDescending(p => p.Item2.Priority)
.ToList();
} }
return allMods IEnumerable<(Mod Mod, ModSettings Settings, int Count)> WithCounts(IEnumerable<(Mod Mod, ModSettings Settings)> mods, int count)
.Select(m => (new Mod(m.Value, m.Key), GetSettings(collection!.Value.Id, m.Key, m.Value, out _))) {
.OrderByDescending(p => p.Item2.Enabled) if (_changedItems != null && _changedItems.Count == count)
.ThenBy(p => p.Item1.Name) return mods.Select((m, idx) => (m.Mod, m.Settings, CountItems(_changedItems[idx].ChangedItems, data)));
.ThenBy(p => p.Item1.DirectoryName)
.ThenByDescending(p => p.Item2.Priority) return mods.Select(p => (p.Item1, p.Item2, CountItems(_getChangedItems!.Invoke(p.Item1.DirectoryName, p.Item1.Name), data)));
.ToList();
static int CountItems(IReadOnlyDictionary<string, object?> dict, IReadOnlyList<string> data)
=> data.Count(dict.ContainsKey);
}
static IReadOnlyList<(Mod Mod, ModSettings Settings, int Count)> OrderList(
IEnumerable<(Mod Mod, ModSettings Settings, int Count)> enumerable, int count)
{
var array = new (Mod Mod, ModSettings Settings, int Count)[count];
var i = 0;
foreach (var t in enumerable.OrderByDescending(p => p.Item2.Enabled)
.ThenByDescending(p => p.Item3)
.ThenBy(p => p.Item1.Name)
.ThenBy(p => p.Item1.DirectoryName)
.ThenByDescending(p => p.Item2.Priority))
array[i++] = t;
return array;
}
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -289,6 +314,9 @@ public class PenumbraService : IDisposable
RemoveAllTemporarySettings(collection.Key); RemoveAllTemporarySettings(collection.Key);
} }
public (string ModDirectory, string ModName)[] CheckCurrentChangedItem(string changedItem)
=> _checkCurrentChangedItems?.Invoke(changedItem) ?? [];
private void SetModTemporary(StringBuilder sb, Mod mod, ModSettings settings, Guid collection, ObjectIndex? index) private void SetModTemporary(StringBuilder sb, Mod mod, ModSettings settings, Guid collection, ObjectIndex? index)
{ {
var ex = settings.Remove var ex = settings.Remove
@ -476,6 +504,7 @@ public class PenumbraService : IDisposable
_setModSetting = new global::Penumbra.Api.IpcSubscribers.TrySetModSetting(_pluginInterface); _setModSetting = new global::Penumbra.Api.IpcSubscribers.TrySetModSetting(_pluginInterface);
_setModSettings = new global::Penumbra.Api.IpcSubscribers.TrySetModSettings(_pluginInterface); _setModSettings = new global::Penumbra.Api.IpcSubscribers.TrySetModSettings(_pluginInterface);
_openModPage = new global::Penumbra.Api.IpcSubscribers.OpenMainWindow(_pluginInterface); _openModPage = new global::Penumbra.Api.IpcSubscribers.OpenMainWindow(_pluginInterface);
_getChangedItems = new global::Penumbra.Api.IpcSubscribers.GetChangedItems(_pluginInterface);
if (CurrentMinor >= RequiredPenumbraFeatureVersionTemp) if (CurrentMinor >= RequiredPenumbraFeatureVersionTemp)
{ {
_setTemporaryModSettings = new global::Penumbra.Api.IpcSubscribers.SetTemporaryModSettings(_pluginInterface); _setTemporaryModSettings = new global::Penumbra.Api.IpcSubscribers.SetTemporaryModSettings(_pluginInterface);
@ -488,10 +517,16 @@ public class PenumbraService : IDisposable
if (CurrentMinor >= RequiredPenumbraFeatureVersionTemp2) if (CurrentMinor >= RequiredPenumbraFeatureVersionTemp2)
{ {
_queryTemporaryModSettings = new global::Penumbra.Api.IpcSubscribers.QueryTemporaryModSettings(_pluginInterface); _queryTemporaryModSettings = new global::Penumbra.Api.IpcSubscribers.QueryTemporaryModSettings(_pluginInterface);
if (CurrentMinor >= RequiredPenumbraFeatureVersionTemp2) if (CurrentMinor >= RequiredPenumbraFeatureVersionTemp3)
{ {
_getCurrentSettingsWithTemp = new global::Penumbra.Api.IpcSubscribers.GetCurrentModSettingsWithTemp(_pluginInterface); _getCurrentSettingsWithTemp = new global::Penumbra.Api.IpcSubscribers.GetCurrentModSettingsWithTemp(_pluginInterface);
_getAllSettings = new global::Penumbra.Api.IpcSubscribers.GetAllModSettings(_pluginInterface); _getAllSettings = new global::Penumbra.Api.IpcSubscribers.GetAllModSettings(_pluginInterface);
if (CurrentMinor >= RequiredPenumbraFeatureVersionTemp4)
{
_changedItems = new global::Penumbra.Api.IpcSubscribers.GetChangedItemAdapterList(_pluginInterface).Invoke();
_checkCurrentChangedItems =
new global::Penumbra.Api.IpcSubscribers.CheckCurrentChangedItemFunc(_pluginInterface).Invoke();
}
} }
} }
} }
@ -541,6 +576,9 @@ public class PenumbraService : IDisposable
_removeAllTemporaryModSettings = null; _removeAllTemporaryModSettings = null;
_removeAllTemporaryModSettingsPlayer = null; _removeAllTemporaryModSettingsPlayer = null;
_queryTemporaryModSettings = null; _queryTemporaryModSettings = null;
_getChangedItems = null;
_changedItems = null;
_checkCurrentChangedItems = null;
Available = false; Available = false;
Glamourer.Log.Debug("Glamourer detached from Penumbra."); Glamourer.Log.Debug("Glamourer detached from Penumbra.");
} }

@ -1 +1 @@
Subproject commit 3c1260c9833303c2d33d12d6f77dc2b1afea3f34 Subproject commit 0b6085ce720ffb7c78cf42d4e51861f34db27744

@ -1 +1 @@
Subproject commit c67809057fac73a0fd407e3ad567f0aa6bc0bc37 Subproject commit 70f046830cc7cd35b3480b12b7efe94182477fbb