Add option to apply only attributes from IMC group.

This commit is contained in:
Ottermandias 2024-09-16 22:54:06 +02:00
parent ac1ea124d9
commit 00fbb2686b
8 changed files with 63 additions and 40 deletions

View file

@ -6,9 +6,9 @@ namespace Penumbra.Meta;
public class ImcChecker public class ImcChecker
{ {
private static readonly Dictionary<ImcIdentifier, int> VariantCounts = []; private static readonly Dictionary<ImcIdentifier, int> VariantCounts = [];
private static MetaFileManager? _dataManager; private static MetaFileManager? _dataManager;
private static readonly ConcurrentDictionary<ImcIdentifier, CachedEntry> GlobalCachedDefaultEntries = [];
public static int GetVariantCount(ImcIdentifier identifier) public static int GetVariantCount(ImcIdentifier identifier)
{ {
@ -26,23 +26,20 @@ public class ImcChecker
public readonly record struct CachedEntry(ImcEntry Entry, bool FileExists, bool VariantExists); public readonly record struct CachedEntry(ImcEntry Entry, bool FileExists, bool VariantExists);
private readonly Dictionary<ImcIdentifier, CachedEntry> _cachedDefaultEntries = new();
private readonly MetaFileManager _metaFileManager;
public ImcChecker(MetaFileManager metaFileManager) public ImcChecker(MetaFileManager metaFileManager)
{ => _dataManager = metaFileManager;
_metaFileManager = 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; return entry;
if (_dataManager == null)
return new CachedEntry(default, false, false);
try 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); entry = new CachedEntry(e, true, entryExists);
} }
catch (Exception) catch (Exception)
@ -51,7 +48,7 @@ public class ImcChecker
} }
if (storeCache) if (storeCache)
_cachedDefaultEntries.Add(identifier, entry); GlobalCachedDefaultEntries.TryAdd(identifier, entry);
return entry; return entry;
} }

View file

@ -10,8 +10,7 @@ namespace Penumbra.Mods.Editor;
public class ModMetaEditor( public class ModMetaEditor(
ModGroupEditor groupEditor, ModGroupEditor groupEditor,
MetaFileManager metaFileManager, MetaFileManager metaFileManager) : MetaDictionary, IService
ImcChecker imcChecker) : MetaDictionary, IService
{ {
public sealed class OtherOptionData : HashSet<string> public sealed class OtherOptionData : HashSet<string>
{ {
@ -67,14 +66,14 @@ public class ModMetaEditor(
Changes = false; Changes = false;
} }
public static bool DeleteDefaultValues(MetaFileManager metaFileManager, ImcChecker imcChecker, MetaDictionary dict) public static bool DeleteDefaultValues(MetaFileManager metaFileManager, MetaDictionary dict)
{ {
var clone = dict.Clone(); var clone = dict.Clone();
dict.Clear(); dict.Clear();
var count = 0; var count = 0;
foreach (var (key, value) in clone.Imc) foreach (var (key, value) in clone.Imc)
{ {
var defaultEntry = imcChecker.GetDefaultEntry(key, false); var defaultEntry = ImcChecker.GetDefaultEntry(key, false);
if (!defaultEntry.Entry.Equals(value)) if (!defaultEntry.Entry.Equals(value))
{ {
dict.TryAdd(key, value); dict.TryAdd(key, value);
@ -164,7 +163,7 @@ public class ModMetaEditor(
} }
public void DeleteDefaultValues() public void DeleteDefaultValues()
=> Changes = DeleteDefaultValues(metaFileManager, imcChecker, this); => Changes = DeleteDefaultValues(metaFileManager, this);
public void Apply(IModDataContainer container) public void Apply(IModDataContainer container)
{ {

View file

@ -35,6 +35,7 @@ public class ImcModGroup(Mod mod) : IModGroup
public ImcIdentifier Identifier; public ImcIdentifier Identifier;
public ImcEntry DefaultEntry; public ImcEntry DefaultEntry;
public bool AllVariants; public bool AllVariants;
public bool OnlyAttributes;
public FullPath? FindBestMatch(Utf8GamePath gamePath) public FullPath? FindBestMatch(Utf8GamePath gamePath)
@ -97,28 +98,36 @@ public class ImcModGroup(Mod mod) : IModGroup
public IModGroupEditDrawer EditDrawer(ModGroupEditDrawer editDrawer) public IModGroupEditDrawer EditDrawer(ModGroupEditDrawer editDrawer)
=> new ImcModGroupEditDrawer(editDrawer, this); => new ImcModGroupEditDrawer(editDrawer, this);
public ImcEntry GetEntry(ushort mask) private ImcEntry GetEntry(Variant variant, ushort mask)
=> DefaultEntry with { AttributeMask = 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<Utf8GamePath, FullPath> redirections, MetaDictionary manipulations) public void AddData(Setting setting, Dictionary<Utf8GamePath, FullPath> redirections, MetaDictionary manipulations)
{ {
if (IsDisabled(setting)) if (IsDisabled(setting))
return; return;
var mask = GetCurrentMask(setting); var mask = GetCurrentMask(setting);
var entry = GetEntry(mask);
if (AllVariants) if (AllVariants)
{ {
var count = ImcChecker.GetVariantCount(Identifier); var count = ImcChecker.GetVariantCount(Identifier);
if (count == 0) if (count == 0)
manipulations.TryAdd(Identifier, entry); manipulations.TryAdd(Identifier, GetEntry(Identifier.Variant, mask));
else else
for (var i = 0; i <= count; ++i) 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 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); serializer.Serialize(jWriter, DefaultEntry);
jWriter.WritePropertyName(nameof(AllVariants)); jWriter.WritePropertyName(nameof(AllVariants));
jWriter.WriteValue(AllVariants); jWriter.WriteValue(AllVariants);
jWriter.WritePropertyName(nameof(OnlyAttributes));
jWriter.WriteValue(OnlyAttributes);
jWriter.WritePropertyName("Options"); jWriter.WritePropertyName("Options");
jWriter.WriteStartArray(); jWriter.WriteStartArray();
foreach (var option in OptionData) 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 identifier = ImcIdentifier.FromJson(json[nameof(Identifier)] as JObject);
var ret = new ImcModGroup(mod) var ret = new ImcModGroup(mod)
{ {
DefaultEntry = json[nameof(DefaultEntry)]?.ToObject<ImcEntry>() ?? new ImcEntry(), DefaultEntry = json[nameof(DefaultEntry)]?.ToObject<ImcEntry>() ?? new ImcEntry(),
AllVariants = json[nameof(AllVariants)]?.ToObject<bool>() ?? false, AllVariants = json[nameof(AllVariants)]?.ToObject<bool>() ?? false,
OnlyAttributes = json[nameof(OnlyAttributes)]?.ToObject<bool>() ?? false,
}; };
if (!ModSaveGroup.ReadJsonBase(json, ret)) if (!ModSaveGroup.ReadJsonBase(json, ret))
return null; return null;

View file

@ -89,6 +89,16 @@ public sealed class ImcModGroupEditor(CommunicatorService communicator, SaveServ
Communicator.ModOptionChanged.Invoke(ModOptionChangeType.OptionMetaChanged, group.Mod, group, null, null, -1); 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) public void ChangeCanBeDisabled(ImcModGroup group, bool canBeDisabled, SaveType saveType = SaveType.Queue)
{ {
if (group.CanBeDisabled == canBeDisabled) if (group.CanBeDisabled == canBeDisabled)

View file

@ -25,8 +25,7 @@ public partial class ModCreator(
Configuration config, Configuration config,
ModDataEditor dataEditor, ModDataEditor dataEditor,
MetaFileManager metaFileManager, MetaFileManager metaFileManager,
GamePathParser gamePathParser, GamePathParser gamePathParser) : IService
ImcChecker imcChecker) : IService
{ {
public readonly Configuration Config = config; public readonly Configuration Config = config;
@ -86,7 +85,7 @@ public partial class ModCreator(
{ {
foreach (var container in mod.AllDataContainers) 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)); saveService.ImmediateSaveSync(new ModSaveGroup(container, Config.ReplaceNonAsciiOnImport));
} }
} }
@ -235,7 +234,7 @@ public partial class ModCreator(
DeleteDeleteList(deleteList, delete); DeleteDeleteList(deleteList, delete);
var changes = oldSize < option.Manipulations.Count; var changes = oldSize < option.Manipulations.Count;
if (deleteDefault && !Config.KeepDefaultMetaChanges) if (deleteDefault && !Config.KeepDefaultMetaChanges)
changes |= ModMetaEditor.DeleteDefaultValues(metaFileManager, imcChecker, option.Manipulations); changes |= ModMetaEditor.DeleteDefaultValues(metaFileManager, option.Manipulations);
return (changes, deleteList); return (changes, deleteList);
} }

View file

@ -30,7 +30,7 @@ public sealed class ImcMetaDrawer(ModMetaEditor editor, MetaFileManager metaFile
} }
private void UpdateEntry() private void UpdateEntry()
=> (Entry, _fileExists, _) = MetaFiles.ImcChecker.GetDefaultEntry(Identifier, true); => (Entry, _fileExists, _) = ImcChecker.GetDefaultEntry(Identifier, true);
protected override void DrawNew() protected override void DrawNew()
{ {
@ -54,7 +54,7 @@ public sealed class ImcMetaDrawer(ModMetaEditor editor, MetaFileManager metaFile
DrawMetaButtons(identifier, entry); DrawMetaButtons(identifier, entry);
DrawIdentifier(identifier); DrawIdentifier(identifier);
var defaultEntry = MetaFiles.ImcChecker.GetDefaultEntry(identifier, true).Entry; var defaultEntry = ImcChecker.GetDefaultEntry(identifier, true).Entry;
if (DrawEntry(defaultEntry, ref entry, true)) if (DrawEntry(defaultEntry, ref entry, true))
Editor.Changes |= Editor.Update(identifier, entry); Editor.Changes |= Editor.Update(identifier, entry);
} }

View file

@ -24,13 +24,11 @@ public class AddGroupDrawer : IUiService
private bool _imcFileExists; private bool _imcFileExists;
private bool _entryExists; private bool _entryExists;
private bool _entryInvalid; private bool _entryInvalid;
private readonly ImcChecker _imcChecker;
private readonly ModManager _modManager; private readonly ModManager _modManager;
public AddGroupDrawer(ModManager modManager, ImcChecker imcChecker) public AddGroupDrawer(ModManager modManager)
{ {
_modManager = modManager; _modManager = modManager;
_imcChecker = imcChecker;
UpdateEntry(); UpdateEntry();
} }
@ -142,7 +140,7 @@ public class AddGroupDrawer : IUiService
private void UpdateEntry() private void UpdateEntry()
{ {
(_defaultEntry, _imcFileExists, _entryExists) = _imcChecker.GetDefaultEntry(_imcIdentifier, false); (_defaultEntry, _imcFileExists, _entryExists) = ImcChecker.GetDefaultEntry(_imcIdentifier, false);
_entryInvalid = !_imcIdentifier.Validate() || _defaultEntry.MaterialId == 0 || !_entryExists; _entryInvalid = !_imcIdentifier.Validate() || _defaultEntry.MaterialId == 0 || !_entryExists;
} }
} }

View file

@ -6,6 +6,7 @@ using OtterGui.Text;
using OtterGui.Text.Widget; using OtterGui.Text.Widget;
using OtterGuiInternal.Utility; using OtterGuiInternal.Utility;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;
using Penumbra.Meta;
using Penumbra.Mods.Groups; using Penumbra.Mods.Groups;
using Penumbra.Mods.Manager.OptionEditor; using Penumbra.Mods.Manager.OptionEditor;
using Penumbra.Mods.SubMods; using Penumbra.Mods.SubMods;
@ -18,18 +19,25 @@ public readonly struct ImcModGroupEditDrawer(ModGroupEditDrawer editor, ImcModGr
public void Draw() public void Draw()
{ {
var identifier = group.Identifier; var identifier = group.Identifier;
var defaultEntry = editor.ImcChecker.GetDefaultEntry(identifier, true).Entry; var defaultEntry = ImcChecker.GetDefaultEntry(identifier, true).Entry;
var entry = group.DefaultEntry; var entry = group.DefaultEntry;
var changes = false; 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.TextFramed(identifier.ToString(), 0, new Vector2(width, 0), borderColor: ImGui.GetColorU32(ImGuiCol.Border));
ImUtf8.SameLineInner(); ImUtf8.SameLineInner();
var allVariants = group.AllVariants; var allVariants = group.AllVariants;
if (ImUtf8.Checkbox("All Variants"u8, ref allVariants)) if (ImUtf8.Checkbox("All Variants"u8, ref allVariants))
editor.ModManager.OptionEditor.ImcEditor.ChangeAllVariants(group, 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); 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()) using (ImUtf8.Group())
{ {
ImUtf8.TextFrameAligned("Material ID"u8); ImUtf8.TextFrameAligned("Material ID"u8);