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--);
+ }
+ }
+ }
+}