diff --git a/Penumbra/Collections/Cache/CollectionCache.cs b/Penumbra/Collections/Cache/CollectionCache.cs index ded1dc73..e2f20b46 100644 --- a/Penumbra/Collections/Cache/CollectionCache.cs +++ b/Penumbra/Collections/Cache/CollectionCache.cs @@ -6,6 +6,7 @@ using Penumbra.Communication; using Penumbra.Mods.Editor; using Penumbra.String.Classes; using Penumbra.Mods.Manager; +using Penumbra.Util; namespace Penumbra.Collections.Cache; @@ -252,8 +253,8 @@ public sealed class CollectionCache : IDisposable return mod.GetData(); var settings = _collection[mod.Index].Settings; - return settings is not { Enabled: true } - ? AppliedModData.Empty + return settings is not { Enabled: true } + ? AppliedModData.Empty : mod.GetData(settings); } @@ -439,9 +440,12 @@ public sealed class CollectionCache : IDisposable foreach (var (manip, mod) in Meta) { - ModCacheManager.ComputeChangedItems(identifier, items, manip); + identifier.MetaChangedItems(items, manip); AddItems(mod); } + + if (_manager.Config.HideMachinistOffhandFromChangedItems) + _changedItems.RemoveMachinistOffhands(); } catch (Exception e) { diff --git a/Penumbra/Collections/Cache/CollectionCacheManager.cs b/Penumbra/Collections/Cache/CollectionCacheManager.cs index fb9ee9a3..ca57c8b9 100644 --- a/Penumbra/Collections/Cache/CollectionCacheManager.cs +++ b/Penumbra/Collections/Cache/CollectionCacheManager.cs @@ -25,6 +25,7 @@ public class CollectionCacheManager : IDisposable private readonly ModStorage _modStorage; private readonly CollectionStorage _storage; private readonly ActiveCollections _active; + internal readonly Configuration Config; internal readonly ResolvedFileChanged ResolvedFileChanged; internal readonly MetaFileManager MetaFileManager; internal readonly ResourceLoader ResourceLoader; @@ -40,7 +41,8 @@ public class CollectionCacheManager : IDisposable => _storage.Where(c => c.HasCache); public CollectionCacheManager(FrameworkManager framework, CommunicatorService communicator, TempModManager tempMods, ModStorage modStorage, - MetaFileManager metaFileManager, ActiveCollections active, CollectionStorage storage, ResourceLoader resourceLoader) + MetaFileManager metaFileManager, ActiveCollections active, CollectionStorage storage, ResourceLoader resourceLoader, + Configuration config) { _framework = framework; _communicator = communicator; @@ -50,6 +52,7 @@ public class CollectionCacheManager : IDisposable _active = active; _storage = storage; ResourceLoader = resourceLoader; + Config = config; ResolvedFileChanged = _communicator.ResolvedFileChanged; if (!_active.Individuals.IsLoaded) @@ -260,7 +263,8 @@ public class CollectionCacheManager : IDisposable } /// Prepare Changes by removing mods from caches with collections or add or reload mods. - private void OnModOptionChange(ModOptionChangeType type, Mod mod, IModGroup? group, IModOption? option, IModDataContainer? container, int movedToIdx) + private void OnModOptionChange(ModOptionChangeType type, Mod mod, IModGroup? group, IModOption? option, IModDataContainer? container, + int movedToIdx) { if (type is ModOptionChangeType.PrepareChange) { diff --git a/Penumbra/Configuration.cs b/Penumbra/Configuration.cs index a065bc26..b81e84d8 100644 --- a/Penumbra/Configuration.cs +++ b/Penumbra/Configuration.cs @@ -41,18 +41,19 @@ public class Configuration : IPluginConfiguration, ISavable public bool HideUiWhenUiHidden { get; set; } = false; public bool UseDalamudUiTextureRedirection { get; set; } = true; - public bool UseCharacterCollectionInMainWindow { get; set; } = true; - public bool UseCharacterCollectionsInCards { get; set; } = true; - public bool UseCharacterCollectionInInspect { get; set; } = true; - public bool UseCharacterCollectionInTryOn { get; set; } = true; - public bool UseOwnerNameForCharacterCollection { get; set; } = true; - public bool UseNoModsInInspect { get; set; } = false; - public bool HideChangedItemFilters { get; set; } = false; - public bool ReplaceNonAsciiOnImport { get; set; } = false; - public bool HidePrioritiesInSelector { get; set; } = false; - public bool HideRedrawBar { get; set; } = false; - public RenameField ShowRename { get; set; } = RenameField.BothDataPrio; - public int OptionGroupCollapsibleMin { get; set; } = 5; + public bool UseCharacterCollectionInMainWindow { get; set; } = true; + public bool UseCharacterCollectionsInCards { get; set; } = true; + public bool UseCharacterCollectionInInspect { get; set; } = true; + public bool UseCharacterCollectionInTryOn { get; set; } = true; + public bool UseOwnerNameForCharacterCollection { get; set; } = true; + public bool UseNoModsInInspect { get; set; } = false; + public bool HideChangedItemFilters { get; set; } = false; + public bool ReplaceNonAsciiOnImport { get; set; } = false; + public bool HidePrioritiesInSelector { get; set; } = false; + public bool HideRedrawBar { get; set; } = false; + public bool HideMachinistOffhandFromChangedItems { get; set; } = true; + public RenameField ShowRename { get; set; } = RenameField.BothDataPrio; + public int OptionGroupCollapsibleMin { get; set; } = 5; public Vector2 MinimumSize = new(Constants.MinimumSizeX, Constants.MinimumSizeY); diff --git a/Penumbra/Mods/Groups/IModGroup.cs b/Penumbra/Mods/Groups/IModGroup.cs index 2ec60f7e..ab367532 100644 --- a/Penumbra/Mods/Groups/IModGroup.cs +++ b/Penumbra/Mods/Groups/IModGroup.cs @@ -1,5 +1,6 @@ using Newtonsoft.Json; using Penumbra.Api.Enums; +using Penumbra.GameData.Data; using Penumbra.Meta.Manipulations; using Penumbra.Mods.Settings; using Penumbra.Mods.SubMods; @@ -40,6 +41,7 @@ public interface IModGroup public int GetIndex(); public void AddData(Setting setting, Dictionary redirections, HashSet manipulations); + public void AddChangedItems(ObjectIdentification identifier, IDictionary changedItems); /// Ensure that a value is valid for a group. public Setting FixSetting(Setting setting); diff --git a/Penumbra/Mods/Groups/ImcModGroup.cs b/Penumbra/Mods/Groups/ImcModGroup.cs index 671d684f..173bf57e 100644 --- a/Penumbra/Mods/Groups/ImcModGroup.cs +++ b/Penumbra/Mods/Groups/ImcModGroup.cs @@ -3,12 +3,14 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; using OtterGui.Classes; using Penumbra.Api.Enums; +using Penumbra.GameData.Data; using Penumbra.GameData.Enums; using Penumbra.GameData.Structs; using Penumbra.Meta.Manipulations; using Penumbra.Mods.Settings; using Penumbra.Mods.SubMods; using Penumbra.String.Classes; +using Penumbra.Util; namespace Penumbra.Mods.Groups; @@ -119,6 +121,9 @@ public class ImcModGroup(Mod mod) : IModGroup manipulations.Add(imc); } + public void AddChangedItems(ObjectIdentification identifier, IDictionary changedItems) + => identifier.MetaChangedItems(changedItems, GetManip(0)); + public Setting FixSetting(Setting setting) => new(setting.Value & (((1ul << OptionData.Count) - 1) | (CanBeDisabled ? 1ul << DisabledIndex : 0))); diff --git a/Penumbra/Mods/Groups/MultiModGroup.cs b/Penumbra/Mods/Groups/MultiModGroup.cs index 7495c4b4..f587fc8f 100644 --- a/Penumbra/Mods/Groups/MultiModGroup.cs +++ b/Penumbra/Mods/Groups/MultiModGroup.cs @@ -4,10 +4,12 @@ using Newtonsoft.Json.Linq; using OtterGui; using OtterGui.Classes; using Penumbra.Api.Enums; +using Penumbra.GameData.Data; using Penumbra.Meta.Manipulations; using Penumbra.Mods.Settings; using Penumbra.Mods.SubMods; using Penumbra.String.Classes; +using Penumbra.Util; namespace Penumbra.Mods.Groups; @@ -114,6 +116,12 @@ public sealed class MultiModGroup(Mod mod) : IModGroup, ITexToolsGroup } } + public void AddChangedItems(ObjectIdentification identifier, IDictionary changedItems) + { + foreach (var container in DataContainers) + identifier.AddChangedItems(container, changedItems); + } + public void WriteJson(JsonTextWriter jWriter, JsonSerializer serializer, DirectoryInfo? basePath = null) { ModSaveGroup.WriteJsonBase(jWriter, this); diff --git a/Penumbra/Mods/Groups/SingleModGroup.cs b/Penumbra/Mods/Groups/SingleModGroup.cs index bc463c1e..7a551322 100644 --- a/Penumbra/Mods/Groups/SingleModGroup.cs +++ b/Penumbra/Mods/Groups/SingleModGroup.cs @@ -2,10 +2,12 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; using OtterGui; using Penumbra.Api.Enums; +using Penumbra.GameData.Data; using Penumbra.Meta.Manipulations; using Penumbra.Mods.Settings; using Penumbra.Mods.SubMods; using Penumbra.String.Classes; +using Penumbra.Util; namespace Penumbra.Mods.Groups; @@ -99,6 +101,12 @@ public sealed class SingleModGroup(Mod mod) : IModGroup, ITexToolsGroup OptionData[setting.AsIndex].AddDataTo(redirections, manipulations); } + public void AddChangedItems(ObjectIdentification identifier, IDictionary changedItems) + { + foreach (var container in DataContainers) + identifier.AddChangedItems(container, changedItems); + } + public Setting FixSetting(Setting setting) => OptionData.Count == 0 ? Setting.Zero : new Setting(Math.Min(setting.Value, (ulong)(OptionData.Count - 1))); diff --git a/Penumbra/Mods/Manager/ModCacheManager.cs b/Penumbra/Mods/Manager/ModCacheManager.cs index c6a723a0..8ab8cf33 100644 --- a/Penumbra/Mods/Manager/ModCacheManager.cs +++ b/Penumbra/Mods/Manager/ModCacheManager.cs @@ -1,26 +1,27 @@ using Penumbra.Communication; using Penumbra.GameData.Data; -using Penumbra.GameData.Enums; -using Penumbra.Meta.Manipulations; using Penumbra.Mods.Groups; using Penumbra.Mods.Manager.OptionEditor; using Penumbra.Mods.SubMods; using Penumbra.Services; +using Penumbra.Util; namespace Penumbra.Mods.Manager; public class ModCacheManager : IDisposable { + private readonly Configuration _config; private readonly CommunicatorService _communicator; private readonly ObjectIdentification _identifier; private readonly ModStorage _modManager; private bool _updatingItems = false; - public ModCacheManager(CommunicatorService communicator, ObjectIdentification identifier, ModStorage modStorage) + public ModCacheManager(CommunicatorService communicator, ObjectIdentification identifier, ModStorage modStorage, Configuration config) { _communicator = communicator; _identifier = identifier; _modManager = modStorage; + _config = config; _communicator.ModOptionChanged.Subscribe(OnModOptionChange, ModOptionChanged.Priority.ModCacheManager); _communicator.ModPathChanged.Subscribe(OnModPathChange, ModPathChanged.Priority.ModCacheManager); @@ -38,75 +39,8 @@ public class ModCacheManager : IDisposable _communicator.ModDiscoveryFinished.Unsubscribe(OnModDiscoveryFinished); } - /// Compute the items changed by a given meta manipulation and put them into the changedItems dictionary. - public static void ComputeChangedItems(ObjectIdentification identifier, IDictionary changedItems, MetaManipulation manip) - { - switch (manip.ManipulationType) - { - case MetaManipulation.Type.Imc: - switch (manip.Imc.ObjectType) - { - case ObjectType.Equipment: - case ObjectType.Accessory: - identifier.Identify(changedItems, - GamePaths.Equipment.Mtrl.Path(manip.Imc.PrimaryId, GenderRace.MidlanderMale, manip.Imc.EquipSlot, manip.Imc.Variant, - "a")); - break; - case ObjectType.Weapon: - identifier.Identify(changedItems, - GamePaths.Weapon.Mtrl.Path(manip.Imc.PrimaryId, manip.Imc.SecondaryId, manip.Imc.Variant, "a")); - break; - case ObjectType.DemiHuman: - identifier.Identify(changedItems, - GamePaths.DemiHuman.Mtrl.Path(manip.Imc.PrimaryId, manip.Imc.SecondaryId, manip.Imc.EquipSlot, manip.Imc.Variant, - "a")); - break; - case ObjectType.Monster: - identifier.Identify(changedItems, - GamePaths.Monster.Mtrl.Path(manip.Imc.PrimaryId, manip.Imc.SecondaryId, manip.Imc.Variant, "a")); - break; - } - - break; - case MetaManipulation.Type.Eqdp: - identifier.Identify(changedItems, - GamePaths.Equipment.Mdl.Path(manip.Eqdp.SetId, Names.CombinedRace(manip.Eqdp.Gender, manip.Eqdp.Race), manip.Eqdp.Slot)); - break; - case MetaManipulation.Type.Eqp: - identifier.Identify(changedItems, GamePaths.Equipment.Mdl.Path(manip.Eqp.SetId, GenderRace.MidlanderMale, manip.Eqp.Slot)); - break; - case MetaManipulation.Type.Est: - switch (manip.Est.Slot) - { - case EstManipulation.EstType.Hair: - changedItems.TryAdd($"Customization: {manip.Est.Race} {manip.Est.Gender} Hair (Hair) {manip.Est.SetId}", null); - break; - case EstManipulation.EstType.Face: - changedItems.TryAdd($"Customization: {manip.Est.Race} {manip.Est.Gender} Face (Face) {manip.Est.SetId}", null); - break; - case EstManipulation.EstType.Body: - identifier.Identify(changedItems, - GamePaths.Equipment.Mdl.Path(manip.Est.SetId, Names.CombinedRace(manip.Est.Gender, manip.Est.Race), - EquipSlot.Body)); - break; - case EstManipulation.EstType.Head: - identifier.Identify(changedItems, - GamePaths.Equipment.Mdl.Path(manip.Est.SetId, Names.CombinedRace(manip.Est.Gender, manip.Est.Race), - EquipSlot.Head)); - break; - } - - break; - case MetaManipulation.Type.Gmp: - identifier.Identify(changedItems, GamePaths.Equipment.Mdl.Path(manip.Gmp.SetId, GenderRace.MidlanderMale, EquipSlot.Head)); - break; - case MetaManipulation.Type.Rsp: - changedItems.TryAdd($"{manip.Rsp.SubRace.ToName()} {manip.Rsp.Attribute.ToFullString()}", null); - break; - } - } - - private void OnModOptionChange(ModOptionChangeType type, Mod mod, IModGroup? group, IModOption? option, IModDataContainer? container, int fromIdx) + private void OnModOptionChange(ModOptionChangeType type, Mod mod, IModGroup? group, IModOption? option, IModDataContainer? container, + int fromIdx) { switch (type) { @@ -194,16 +128,14 @@ public class ModCacheManager : IDisposable private void UpdateChangedItems(Mod mod) { - var changedItems = (SortedList)mod.ChangedItems; - changedItems.Clear(); - foreach (var gamePath in mod.AllDataContainers.SelectMany(m => m.Files.Keys.Concat(m.FileSwaps.Keys))) - _identifier.Identify(changedItems, gamePath.ToString()); + mod.ChangedItems.Clear(); - foreach (var manip in mod.AllDataContainers.SelectMany(m => m.Manipulations)) - ComputeChangedItems(_identifier, changedItems, manip); + _identifier.AddChangedItems(mod.Default, mod.ChangedItems); + foreach (var group in mod.Groups) + group.AddChangedItems(_identifier, mod.ChangedItems); - foreach(var imcGroup in mod.Groups.OfType()) - ComputeChangedItems(_identifier, changedItems, imcGroup.GetManip(0)); + if (_config.HideMachinistOffhandFromChangedItems) + mod.ChangedItems.RemoveMachinistOffhands(); mod.LowerChangedItemsString = string.Join("\0", mod.ChangedItems.Keys.Select(k => k.ToLowerInvariant())); } diff --git a/Penumbra/Mods/Manager/ModImportManager.cs b/Penumbra/Mods/Manager/ModImportManager.cs index 73571ea4..c99b7d0e 100644 --- a/Penumbra/Mods/Manager/ModImportManager.cs +++ b/Penumbra/Mods/Manager/ModImportManager.cs @@ -5,12 +5,8 @@ using Penumbra.Mods.Editor; namespace Penumbra.Mods.Manager; -public class ModImportManager : IDisposable +public class ModImportManager(ModManager modManager, Configuration config, ModEditor modEditor) : IDisposable { - private readonly ModManager _modManager; - private readonly Configuration _config; - private readonly ModEditor _modEditor; - private readonly ConcurrentQueue _modsToUnpack = new(); /// Mods need to be added thread-safely outside of iteration. @@ -26,13 +22,6 @@ public class ModImportManager : IDisposable => _modsToAdd; - public ModImportManager(ModManager modManager, Configuration config, ModEditor modEditor) - { - _modManager = modManager; - _config = config; - _modEditor = modEditor; - } - public void TryUnpacking() { if (Importing || !_modsToUnpack.TryDequeue(out var newMods)) @@ -51,7 +40,7 @@ public class ModImportManager : IDisposable if (files.Length == 0) return; - _import = new TexToolsImporter(files.Length, files, AddNewMod, _config, _modEditor, _modManager, _modEditor.Compactor); + _import = new TexToolsImporter(files.Length, files, AddNewMod, config, modEditor, modManager, modEditor.Compactor); } public bool Importing @@ -87,8 +76,8 @@ public class ModImportManager : IDisposable return false; } - _modManager.AddMod(directory); - mod = _modManager.LastOrDefault(); + modManager.AddMod(directory); + mod = modManager.LastOrDefault(); return mod != null && mod.ModPath == directory; } diff --git a/Penumbra/Mods/Mod.cs b/Penumbra/Mods/Mod.cs index 6f6eb8ce..783ef3e6 100644 --- a/Penumbra/Mods/Mod.cs +++ b/Penumbra/Mods/Mod.cs @@ -62,8 +62,8 @@ public sealed class Mod : IMod // Options - public readonly DefaultSubMod Default; - public readonly List Groups = []; + public readonly DefaultSubMod Default; + public readonly List Groups = []; public AppliedModData GetData(ModSettings? settings = null) { @@ -99,7 +99,7 @@ public sealed class Mod : IMod } // Cache - public readonly IReadOnlyDictionary ChangedItems = new SortedList(); + public readonly SortedList ChangedItems = new(); public string LowerChangedItemsString { get; internal set; } = string.Empty; public string AllTagsLower { get; internal set; } = string.Empty; diff --git a/Penumbra/UI/ModsTab/ModGroupEditDrawer.cs b/Penumbra/UI/ModsTab/ModGroupEditDrawer.cs index a94c25ea..4ef1577f 100644 --- a/Penumbra/UI/ModsTab/ModGroupEditDrawer.cs +++ b/Penumbra/UI/ModsTab/ModGroupEditDrawer.cs @@ -123,8 +123,7 @@ public sealed class ModGroupEditDrawer( ModManager modManager, Configuration config, FilenameService filenames, - DescriptionEditPopup descriptionPopup, - MetaFileManager metaManager) : IUiService + DescriptionEditPopup descriptionPopup) : IUiService { private static ReadOnlySpan DragDropLabel => "##DragOption"u8; diff --git a/Penumbra/UI/Tabs/SettingsTab.cs b/Penumbra/UI/Tabs/SettingsTab.cs index 439f7be4..30384538 100644 --- a/Penumbra/UI/Tabs/SettingsTab.cs +++ b/Penumbra/UI/Tabs/SettingsTab.cs @@ -428,6 +428,14 @@ public class SettingsTab : ITab _config.Ephemeral.Save(); } }); + Checkbox("Omit Machinist Offhands in Changed Items", + "Omits all Aetherotransformers (machinist offhands) in the changed items tabs because any change on them changes all of them at the moment.\n\n" + + "Changing this triggers a rediscovery of your mods so all changed items can be updated.", + _config.HideMachinistOffhandFromChangedItems, v => + { + _config.HideMachinistOffhandFromChangedItems = v; + _modManager.DiscoverMods(); + }); Checkbox("Hide Priority Numbers in Mod Selector", "Hides the bracketed non-zero priority numbers displayed in the mod selector when there is enough space for them.", _config.HidePrioritiesInSelector, v => _config.HidePrioritiesInSelector = v); diff --git a/Penumbra/Util/IdentifierExtensions.cs b/Penumbra/Util/IdentifierExtensions.cs new file mode 100644 index 00000000..392a5aba --- /dev/null +++ b/Penumbra/Util/IdentifierExtensions.cs @@ -0,0 +1,115 @@ +using OtterGui.Classes; +using Penumbra.GameData.Data; +using Penumbra.GameData.Enums; +using Penumbra.GameData.Structs; +using Penumbra.Meta.Manipulations; +using Penumbra.Mods.Editor; +using Penumbra.Mods.SubMods; + +namespace Penumbra.Util; + +public static class IdentifierExtensions +{ + /// Compute the items changed by a given meta manipulation and put them into the changedItems dictionary. + public static void MetaChangedItems(this ObjectIdentification identifier, IDictionary changedItems, + MetaManipulation manip) + { + switch (manip.ManipulationType) + { + case MetaManipulation.Type.Imc: + switch (manip.Imc.ObjectType) + { + case ObjectType.Equipment: + case ObjectType.Accessory: + identifier.Identify(changedItems, + GamePaths.Equipment.Mtrl.Path(manip.Imc.PrimaryId, GenderRace.MidlanderMale, manip.Imc.EquipSlot, manip.Imc.Variant, + "a")); + break; + case ObjectType.Weapon: + identifier.Identify(changedItems, + GamePaths.Weapon.Mtrl.Path(manip.Imc.PrimaryId, manip.Imc.SecondaryId, manip.Imc.Variant, "a")); + break; + case ObjectType.DemiHuman: + identifier.Identify(changedItems, + GamePaths.DemiHuman.Mtrl.Path(manip.Imc.PrimaryId, manip.Imc.SecondaryId, manip.Imc.EquipSlot, manip.Imc.Variant, + "a")); + break; + case ObjectType.Monster: + identifier.Identify(changedItems, + GamePaths.Monster.Mtrl.Path(manip.Imc.PrimaryId, manip.Imc.SecondaryId, manip.Imc.Variant, "a")); + break; + } + + break; + case MetaManipulation.Type.Eqdp: + identifier.Identify(changedItems, + GamePaths.Equipment.Mdl.Path(manip.Eqdp.SetId, Names.CombinedRace(manip.Eqdp.Gender, manip.Eqdp.Race), manip.Eqdp.Slot)); + break; + case MetaManipulation.Type.Eqp: + identifier.Identify(changedItems, GamePaths.Equipment.Mdl.Path(manip.Eqp.SetId, GenderRace.MidlanderMale, manip.Eqp.Slot)); + break; + case MetaManipulation.Type.Est: + switch (manip.Est.Slot) + { + case EstManipulation.EstType.Hair: + changedItems.TryAdd($"Customization: {manip.Est.Race} {manip.Est.Gender} Hair (Hair) {manip.Est.SetId}", null); + break; + case EstManipulation.EstType.Face: + changedItems.TryAdd($"Customization: {manip.Est.Race} {manip.Est.Gender} Face (Face) {manip.Est.SetId}", null); + break; + case EstManipulation.EstType.Body: + identifier.Identify(changedItems, + GamePaths.Equipment.Mdl.Path(manip.Est.SetId, Names.CombinedRace(manip.Est.Gender, manip.Est.Race), + EquipSlot.Body)); + break; + case EstManipulation.EstType.Head: + identifier.Identify(changedItems, + GamePaths.Equipment.Mdl.Path(manip.Est.SetId, Names.CombinedRace(manip.Est.Gender, manip.Est.Race), + EquipSlot.Head)); + break; + } + + break; + case MetaManipulation.Type.Gmp: + identifier.Identify(changedItems, GamePaths.Equipment.Mdl.Path(manip.Gmp.SetId, GenderRace.MidlanderMale, EquipSlot.Head)); + break; + case MetaManipulation.Type.Rsp: + changedItems.TryAdd($"{manip.Rsp.SubRace.ToName()} {manip.Rsp.Attribute.ToFullString()}", null); + break; + } + } + + public static void AddChangedItems(this ObjectIdentification identifier, IModDataContainer container, + IDictionary changedItems) + { + foreach (var gamePath in container.Files.Keys.Concat(container.FileSwaps.Keys)) + identifier.Identify(changedItems, gamePath.ToString()); + + foreach (var manip in container.Manipulations) + MetaChangedItems(identifier, changedItems, manip); + } + + public static void RemoveMachinistOffhands(this SortedList changedItems) + { + for (var i = 0; i < changedItems.Count; i++) + { + { + var value = changedItems.Values[i]; + if (value is EquipItem { Type: FullEquipType.GunOff }) + changedItems.RemoveAt(i--); + } + } + } + + public static void RemoveMachinistOffhands(this SortedList, object?)> changedItems) + { + for (var i = 0; i < changedItems.Count; i++) + { + { + var value = changedItems.Values[i].Item2; + if (value is EquipItem { Type: FullEquipType.GunOff }) + changedItems.RemoveAt(i--); + } + } + } +}