From 00fbb2686b864dcfe91bb9e67415b9059fc53a55 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Mon, 16 Sep 2024 22:54:06 +0200 Subject: [PATCH] Add option to apply only attributes from IMC group. --- Penumbra/Meta/ImcChecker.cs | 25 +++++++--------- Penumbra/Mods/Editor/ModMetaEditor.cs | 9 +++--- Penumbra/Mods/Groups/ImcModGroup.cs | 30 +++++++++++++------ .../Manager/OptionEditor/ImcModGroupEditor.cs | 10 +++++++ Penumbra/Mods/ModCreator.cs | 7 ++--- .../UI/AdvancedWindow/Meta/ImcMetaDrawer.cs | 4 +-- Penumbra/UI/ModsTab/Groups/AddGroupDrawer.cs | 6 ++-- .../ModsTab/Groups/ImcModGroupEditDrawer.cs | 12 ++++++-- 8 files changed, 63 insertions(+), 40 deletions(-) diff --git a/Penumbra/Meta/ImcChecker.cs b/Penumbra/Meta/ImcChecker.cs index 4e3ff11b..a415c9b0 100644 --- a/Penumbra/Meta/ImcChecker.cs +++ b/Penumbra/Meta/ImcChecker.cs @@ -6,9 +6,9 @@ namespace Penumbra.Meta; public class ImcChecker { - private static readonly Dictionary VariantCounts = []; - private static MetaFileManager? _dataManager; - + private static readonly Dictionary VariantCounts = []; + private static MetaFileManager? _dataManager; + private static readonly ConcurrentDictionary GlobalCachedDefaultEntries = []; public static int GetVariantCount(ImcIdentifier identifier) { @@ -26,23 +26,20 @@ public class ImcChecker public readonly record struct CachedEntry(ImcEntry Entry, bool FileExists, bool VariantExists); - private readonly Dictionary _cachedDefaultEntries = new(); - private readonly MetaFileManager _metaFileManager; - public ImcChecker(MetaFileManager metaFileManager) - { - _metaFileManager = metaFileManager; - _dataManager = metaFileManager; - } + => _dataManager = metaFileManager; - public CachedEntry GetDefaultEntry(ImcIdentifier identifier, bool storeCache) + public static CachedEntry GetDefaultEntry(ImcIdentifier identifier, bool storeCache) { - if (_cachedDefaultEntries.TryGetValue(identifier, out var entry)) + if (GlobalCachedDefaultEntries.TryGetValue(identifier, out var entry)) return entry; + if (_dataManager == null) + return new CachedEntry(default, false, false); + try { - var e = ImcFile.GetDefault(_metaFileManager, identifier.GamePath(), identifier.EquipSlot, identifier.Variant, out var entryExists); + var e = ImcFile.GetDefault(_dataManager, identifier.GamePath(), identifier.EquipSlot, identifier.Variant, out var entryExists); entry = new CachedEntry(e, true, entryExists); } catch (Exception) @@ -51,7 +48,7 @@ public class ImcChecker } if (storeCache) - _cachedDefaultEntries.Add(identifier, entry); + GlobalCachedDefaultEntries.TryAdd(identifier, entry); return entry; } diff --git a/Penumbra/Mods/Editor/ModMetaEditor.cs b/Penumbra/Mods/Editor/ModMetaEditor.cs index 07a54391..64c585ea 100644 --- a/Penumbra/Mods/Editor/ModMetaEditor.cs +++ b/Penumbra/Mods/Editor/ModMetaEditor.cs @@ -10,8 +10,7 @@ namespace Penumbra.Mods.Editor; public class ModMetaEditor( ModGroupEditor groupEditor, - MetaFileManager metaFileManager, - ImcChecker imcChecker) : MetaDictionary, IService + MetaFileManager metaFileManager) : MetaDictionary, IService { public sealed class OtherOptionData : HashSet { @@ -67,14 +66,14 @@ public class ModMetaEditor( Changes = false; } - public static bool DeleteDefaultValues(MetaFileManager metaFileManager, ImcChecker imcChecker, MetaDictionary dict) + public static bool DeleteDefaultValues(MetaFileManager metaFileManager, MetaDictionary dict) { var clone = dict.Clone(); dict.Clear(); var count = 0; foreach (var (key, value) in clone.Imc) { - var defaultEntry = imcChecker.GetDefaultEntry(key, false); + var defaultEntry = ImcChecker.GetDefaultEntry(key, false); if (!defaultEntry.Entry.Equals(value)) { dict.TryAdd(key, value); @@ -164,7 +163,7 @@ public class ModMetaEditor( } public void DeleteDefaultValues() - => Changes = DeleteDefaultValues(metaFileManager, imcChecker, this); + => Changes = DeleteDefaultValues(metaFileManager, this); public void Apply(IModDataContainer container) { diff --git a/Penumbra/Mods/Groups/ImcModGroup.cs b/Penumbra/Mods/Groups/ImcModGroup.cs index f8b4b2ef..2a1854ed 100644 --- a/Penumbra/Mods/Groups/ImcModGroup.cs +++ b/Penumbra/Mods/Groups/ImcModGroup.cs @@ -35,6 +35,7 @@ public class ImcModGroup(Mod mod) : IModGroup public ImcIdentifier Identifier; public ImcEntry DefaultEntry; public bool AllVariants; + public bool OnlyAttributes; public FullPath? FindBestMatch(Utf8GamePath gamePath) @@ -97,28 +98,36 @@ public class ImcModGroup(Mod mod) : IModGroup public IModGroupEditDrawer EditDrawer(ModGroupEditDrawer editDrawer) => new ImcModGroupEditDrawer(editDrawer, this); - public ImcEntry GetEntry(ushort mask) - => DefaultEntry with { AttributeMask = mask }; + private ImcEntry GetEntry(Variant variant, ushort mask) + { + if (!OnlyAttributes) + return DefaultEntry with { AttributeMask = mask }; + + var defaultEntry = ImcChecker.GetDefaultEntry(Identifier with { Variant = variant }, true); + if (defaultEntry.VariantExists) + return defaultEntry.Entry with { AttributeMask = mask }; + + return DefaultEntry with { AttributeMask = mask }; + } public void AddData(Setting setting, Dictionary redirections, MetaDictionary manipulations) { if (IsDisabled(setting)) return; - var mask = GetCurrentMask(setting); - var entry = GetEntry(mask); + var mask = GetCurrentMask(setting); if (AllVariants) { var count = ImcChecker.GetVariantCount(Identifier); if (count == 0) - manipulations.TryAdd(Identifier, entry); + manipulations.TryAdd(Identifier, GetEntry(Identifier.Variant, mask)); else for (var i = 0; i <= count; ++i) - manipulations.TryAdd(Identifier with { Variant = (Variant)i }, entry); + manipulations.TryAdd(Identifier with { Variant = (Variant)i }, GetEntry((Variant)i, mask)); } else { - manipulations.TryAdd(Identifier, entry); + manipulations.TryAdd(Identifier, GetEntry(Identifier.Variant, mask)); } } @@ -138,6 +147,8 @@ public class ImcModGroup(Mod mod) : IModGroup serializer.Serialize(jWriter, DefaultEntry); jWriter.WritePropertyName(nameof(AllVariants)); jWriter.WriteValue(AllVariants); + jWriter.WritePropertyName(nameof(OnlyAttributes)); + jWriter.WriteValue(OnlyAttributes); jWriter.WritePropertyName("Options"); jWriter.WriteStartArray(); foreach (var option in OptionData) @@ -170,8 +181,9 @@ public class ImcModGroup(Mod mod) : IModGroup var identifier = ImcIdentifier.FromJson(json[nameof(Identifier)] as JObject); var ret = new ImcModGroup(mod) { - DefaultEntry = json[nameof(DefaultEntry)]?.ToObject() ?? new ImcEntry(), - AllVariants = json[nameof(AllVariants)]?.ToObject() ?? false, + DefaultEntry = json[nameof(DefaultEntry)]?.ToObject() ?? new ImcEntry(), + AllVariants = json[nameof(AllVariants)]?.ToObject() ?? false, + OnlyAttributes = json[nameof(OnlyAttributes)]?.ToObject() ?? false, }; if (!ModSaveGroup.ReadJsonBase(json, ret)) return null; diff --git a/Penumbra/Mods/Manager/OptionEditor/ImcModGroupEditor.cs b/Penumbra/Mods/Manager/OptionEditor/ImcModGroupEditor.cs index 515f6ff4..dc94c881 100644 --- a/Penumbra/Mods/Manager/OptionEditor/ImcModGroupEditor.cs +++ b/Penumbra/Mods/Manager/OptionEditor/ImcModGroupEditor.cs @@ -89,6 +89,16 @@ public sealed class ImcModGroupEditor(CommunicatorService communicator, SaveServ Communicator.ModOptionChanged.Invoke(ModOptionChangeType.OptionMetaChanged, group.Mod, group, null, null, -1); } + public void ChangeOnlyAttributes(ImcModGroup group, bool onlyAttributes, SaveType saveType = SaveType.Queue) + { + if (group.OnlyAttributes == onlyAttributes) + return; + + group.OnlyAttributes = onlyAttributes; + SaveService.Save(saveType, new ModSaveGroup(group, Config.ReplaceNonAsciiOnImport)); + Communicator.ModOptionChanged.Invoke(ModOptionChangeType.OptionMetaChanged, group.Mod, group, null, null, -1); + } + public void ChangeCanBeDisabled(ImcModGroup group, bool canBeDisabled, SaveType saveType = SaveType.Queue) { if (group.CanBeDisabled == canBeDisabled) diff --git a/Penumbra/Mods/ModCreator.cs b/Penumbra/Mods/ModCreator.cs index fe027ca4..1af9c1db 100644 --- a/Penumbra/Mods/ModCreator.cs +++ b/Penumbra/Mods/ModCreator.cs @@ -25,8 +25,7 @@ public partial class ModCreator( Configuration config, ModDataEditor dataEditor, MetaFileManager metaFileManager, - GamePathParser gamePathParser, - ImcChecker imcChecker) : IService + GamePathParser gamePathParser) : IService { public readonly Configuration Config = config; @@ -86,7 +85,7 @@ public partial class ModCreator( { foreach (var container in mod.AllDataContainers) { - if (ModMetaEditor.DeleteDefaultValues(metaFileManager, imcChecker, container.Manipulations)) + if (ModMetaEditor.DeleteDefaultValues(metaFileManager, container.Manipulations)) saveService.ImmediateSaveSync(new ModSaveGroup(container, Config.ReplaceNonAsciiOnImport)); } } @@ -235,7 +234,7 @@ public partial class ModCreator( DeleteDeleteList(deleteList, delete); var changes = oldSize < option.Manipulations.Count; if (deleteDefault && !Config.KeepDefaultMetaChanges) - changes |= ModMetaEditor.DeleteDefaultValues(metaFileManager, imcChecker, option.Manipulations); + changes |= ModMetaEditor.DeleteDefaultValues(metaFileManager, option.Manipulations); return (changes, deleteList); } diff --git a/Penumbra/UI/AdvancedWindow/Meta/ImcMetaDrawer.cs b/Penumbra/UI/AdvancedWindow/Meta/ImcMetaDrawer.cs index 53c61292..c8310cf7 100644 --- a/Penumbra/UI/AdvancedWindow/Meta/ImcMetaDrawer.cs +++ b/Penumbra/UI/AdvancedWindow/Meta/ImcMetaDrawer.cs @@ -30,7 +30,7 @@ public sealed class ImcMetaDrawer(ModMetaEditor editor, MetaFileManager metaFile } private void UpdateEntry() - => (Entry, _fileExists, _) = MetaFiles.ImcChecker.GetDefaultEntry(Identifier, true); + => (Entry, _fileExists, _) = ImcChecker.GetDefaultEntry(Identifier, true); protected override void DrawNew() { @@ -54,7 +54,7 @@ public sealed class ImcMetaDrawer(ModMetaEditor editor, MetaFileManager metaFile DrawMetaButtons(identifier, entry); DrawIdentifier(identifier); - var defaultEntry = MetaFiles.ImcChecker.GetDefaultEntry(identifier, true).Entry; + var defaultEntry = ImcChecker.GetDefaultEntry(identifier, true).Entry; if (DrawEntry(defaultEntry, ref entry, true)) Editor.Changes |= Editor.Update(identifier, entry); } diff --git a/Penumbra/UI/ModsTab/Groups/AddGroupDrawer.cs b/Penumbra/UI/ModsTab/Groups/AddGroupDrawer.cs index 689571f3..c30239bc 100644 --- a/Penumbra/UI/ModsTab/Groups/AddGroupDrawer.cs +++ b/Penumbra/UI/ModsTab/Groups/AddGroupDrawer.cs @@ -24,13 +24,11 @@ public class AddGroupDrawer : IUiService private bool _imcFileExists; private bool _entryExists; private bool _entryInvalid; - private readonly ImcChecker _imcChecker; private readonly ModManager _modManager; - public AddGroupDrawer(ModManager modManager, ImcChecker imcChecker) + public AddGroupDrawer(ModManager modManager) { _modManager = modManager; - _imcChecker = imcChecker; UpdateEntry(); } @@ -142,7 +140,7 @@ public class AddGroupDrawer : IUiService private void UpdateEntry() { - (_defaultEntry, _imcFileExists, _entryExists) = _imcChecker.GetDefaultEntry(_imcIdentifier, false); + (_defaultEntry, _imcFileExists, _entryExists) = ImcChecker.GetDefaultEntry(_imcIdentifier, false); _entryInvalid = !_imcIdentifier.Validate() || _defaultEntry.MaterialId == 0 || !_entryExists; } } diff --git a/Penumbra/UI/ModsTab/Groups/ImcModGroupEditDrawer.cs b/Penumbra/UI/ModsTab/Groups/ImcModGroupEditDrawer.cs index 4ab1c6aa..786bb8ff 100644 --- a/Penumbra/UI/ModsTab/Groups/ImcModGroupEditDrawer.cs +++ b/Penumbra/UI/ModsTab/Groups/ImcModGroupEditDrawer.cs @@ -6,6 +6,7 @@ using OtterGui.Text; using OtterGui.Text.Widget; using OtterGuiInternal.Utility; using Penumbra.GameData.Structs; +using Penumbra.Meta; using Penumbra.Mods.Groups; using Penumbra.Mods.Manager.OptionEditor; using Penumbra.Mods.SubMods; @@ -18,18 +19,25 @@ public readonly struct ImcModGroupEditDrawer(ModGroupEditDrawer editor, ImcModGr public void Draw() { var identifier = group.Identifier; - var defaultEntry = editor.ImcChecker.GetDefaultEntry(identifier, true).Entry; + var defaultEntry = ImcChecker.GetDefaultEntry(identifier, true).Entry; var entry = group.DefaultEntry; var changes = false; - var width = editor.AvailableWidth.X - ImUtf8.ItemInnerSpacing.X - ImUtf8.CalcTextSize("All Variants"u8).X; + var width = editor.AvailableWidth.X - 3 * ImUtf8.ItemInnerSpacing.X - ImUtf8.ItemSpacing.X - ImUtf8.CalcTextSize("All Variants"u8).X - ImUtf8.CalcTextSize("Only Attributes"u8).X - 2 * ImUtf8.FrameHeight; ImUtf8.TextFramed(identifier.ToString(), 0, new Vector2(width, 0), borderColor: ImGui.GetColorU32(ImGuiCol.Border)); + ImUtf8.SameLineInner(); var allVariants = group.AllVariants; if (ImUtf8.Checkbox("All Variants"u8, ref allVariants)) editor.ModManager.OptionEditor.ImcEditor.ChangeAllVariants(group, allVariants); ImUtf8.HoverTooltip("Make this group overwrite all corresponding variants for this identifier, not just the one specified."u8); + ImGui.SameLine(); + var onlyAttributes = group.OnlyAttributes; + if (ImUtf8.Checkbox("Only Attributes"u8, ref onlyAttributes)) + editor.ModManager.OptionEditor.ImcEditor.ChangeOnlyAttributes(group, onlyAttributes); + ImUtf8.HoverTooltip("Only overwrite the attribute flags and take all the other values from the game's default entry instead of the one configured here.\n\nMainly useful if used with All Variants to keep the material IDs for each variant."u8); + using (ImUtf8.Group()) { ImUtf8.TextFrameAligned("Material ID"u8);