From d10043a69a01e45d84b03b19d4e1b1a5b5ef774c Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Tue, 30 Jan 2024 18:30:51 +0100 Subject: [PATCH] Add toggle for always applying mod associations. --- Glamourer/Automation/AutoDesignApplier.cs | 2 +- Glamourer/Gui/Tabs/SettingsTab.cs | 2 +- .../Interop/Penumbra/ModSettingApplier.cs | 67 +++++++++++++++++++ Glamourer/Services/CommandService.cs | 21 ++---- Glamourer/State/StateEditor.cs | 7 +- Glamourer/State/StateManager.cs | 6 +- 6 files changed, 84 insertions(+), 21 deletions(-) create mode 100644 Glamourer/Interop/Penumbra/ModSettingApplier.cs diff --git a/Glamourer/Automation/AutoDesignApplier.cs b/Glamourer/Automation/AutoDesignApplier.cs index dd327b3..65116db 100644 --- a/Glamourer/Automation/AutoDesignApplier.cs +++ b/Glamourer/Automation/AutoDesignApplier.cs @@ -264,7 +264,7 @@ public sealed class AutoDesignApplier : IDisposable var mergedDesign = _designMerger.Merge( set.Designs.Where(d => d.IsActive(actor)).SelectMany(d => d.Design?.AllLinks.Select(l => (l.Design, l.Flags & d.Type)) ?? [(d.Design, d.Type)]), - state.ModelData, true, false); + state.ModelData, true, _config.AlwaysApplyAssociatedMods); _state.ApplyDesign(state, mergedDesign, new ApplySettings(0, StateSource.Fixed, respectManual, fromJobChange, false)); } diff --git a/Glamourer/Gui/Tabs/SettingsTab.cs b/Glamourer/Gui/Tabs/SettingsTab.cs index ef75245..11490df 100644 --- a/Glamourer/Gui/Tabs/SettingsTab.cs +++ b/Glamourer/Gui/Tabs/SettingsTab.cs @@ -89,12 +89,12 @@ public class SettingsTab( Checkbox("Enable Advanced Customization Options", "Enable the display and editing of advanced customization options like arbitrary colors.", config.UseAdvancedParameters, paletteChecker.SetAdvancedParameters); + PaletteImportButton(); Checkbox("Always Apply Associated Mods", "Whenever a design is applied to a character (including via automation), Glamourer will try to apply its associated mod settings to the collection currently associated with that character, if it is available.\n\n" + "Glamourer will NOT revert these applied settings automatically. This may mess up your collection and configuration.\n\n" + "If you enable this setting, you are aware that any resulting misconfiguration is your own fault.", config.AlwaysApplyAssociatedMods, v => config.AlwaysApplyAssociatedMods = v); - PaletteImportButton(); ImGui.NewLine(); } diff --git a/Glamourer/Interop/Penumbra/ModSettingApplier.cs b/Glamourer/Interop/Penumbra/ModSettingApplier.cs new file mode 100644 index 0000000..01ea4d7 --- /dev/null +++ b/Glamourer/Interop/Penumbra/ModSettingApplier.cs @@ -0,0 +1,67 @@ +using Glamourer.Designs.Links; +using Glamourer.Interop.Structs; +using Glamourer.State; +using OtterGui.Services; + +namespace Glamourer.Interop.Penumbra; + +public class ModSettingApplier(PenumbraService penumbra, Configuration config, ObjectManager objects) : IService +{ + public void HandleStateApplication(ActorState state, MergedDesign design) + { + if (!config.AlwaysApplyAssociatedMods || design.AssociatedMods.Count == 0) + return; + + objects.Update(); + if (!objects.TryGetValue(state.Identifier, out var data)) + { + Glamourer.Log.Verbose( + $"[Mod Applier] No mod settings applied because no actor for {state.Identifier} could be found to associate collection."); + return; + } + + var collections = new HashSet(); + + foreach (var actor in data.Objects) + { + var collection = penumbra.GetActorCollection(actor); + if (collection.Length == 0) + { + Glamourer.Log.Verbose($"[Mod Applier] Could not obtain associated collection for {actor.Utf8Name}."); + continue; + } + + if (!collections.Add(collection)) + continue; + + foreach (var (mod, setting) in design.AssociatedMods) + { + var message = penumbra.SetMod(mod, setting, collection); + if (message.Length > 0) + Glamourer.Log.Verbose($"[Mod Applier] Error applying mod settings: {message}"); + else + Glamourer.Log.Verbose($"[Mod Applier] Set mod settings for {mod.DirectoryName} in {collection}."); + } + } + } + + public (List Messages, int Applied, string Collection) ApplyModSettings(IReadOnlyDictionary settings, Actor actor) + { + var collection = penumbra.GetActorCollection(actor); + if (collection.Length <= 0) + return ([$"Could not obtain associated collection for {actor.Utf8Name}."], 0, string.Empty); + + var messages = new List(); + var appliedMods = 0; + foreach (var (mod, setting) in settings) + { + var message = penumbra.SetMod(mod, setting, collection); + if (message.Length > 0) + messages.Add($"Error applying mod settings: {message}"); + else + ++appliedMods; + } + + return (messages, appliedMods, collection); + } +} diff --git a/Glamourer/Services/CommandService.cs b/Glamourer/Services/CommandService.cs index 895ad2f..2f3e938 100644 --- a/Glamourer/Services/CommandService.cs +++ b/Glamourer/Services/CommandService.cs @@ -33,11 +33,11 @@ public class CommandService : IDisposable private readonly DesignConverter _converter; private readonly DesignFileSystem _designFileSystem; private readonly Configuration _config; - private readonly PenumbraService _penumbra; + private readonly ModSettingApplier _modApplier; public CommandService(ICommandManager commands, MainWindow mainWindow, IChatGui chat, ActorManager actors, ObjectManager objects, AutoDesignApplier autoDesignApplier, StateManager stateManager, DesignManager designManager, DesignConverter converter, - DesignFileSystem designFileSystem, AutoDesignManager autoDesignManager, Configuration config, PenumbraService penumbra) + DesignFileSystem designFileSystem, AutoDesignManager autoDesignManager, Configuration config, ModSettingApplier modApplier) { _commands = commands; _mainWindow = mainWindow; @@ -51,7 +51,7 @@ public class CommandService : IDisposable _designFileSystem = designFileSystem; _autoDesignManager = autoDesignManager; _config = config; - _penumbra = penumbra; + _modApplier = modApplier; _commands.AddHandler(MainCommandString, new CommandInfo(OnGlamourer) { HelpMessage = "Open or close the Glamourer window." }); _commands.AddHandler(ApplyCommandString, @@ -440,19 +440,10 @@ public class CommandService : IDisposable if (!applyMods || design is not Design d) return; - var collection = _penumbra.GetActorCollection(actor); - if (collection.Length <= 0) - return; + var (messages, appliedMods, collection) = _modApplier.ApplyModSettings(d.AssociatedMods, actor); - var appliedMods = 0; - foreach (var (mod, setting) in d.AssociatedMods) - { - var message = _penumbra.SetMod(mod, setting, collection); - if (message.Length > 0) - Glamourer.Messager.Chat.Print($"Error applying mod settings: {message}"); - else - ++appliedMods; - } + foreach (var message in messages) + Glamourer.Messager.Chat.Print($"Error applying mod settings: {message}"); if (appliedMods > 0) Glamourer.Messager.Chat.Print($"Applied {appliedMods} mod settings to {collection}."); diff --git a/Glamourer/State/StateEditor.cs b/Glamourer/State/StateEditor.cs index 48b74ab..a6606e6 100644 --- a/Glamourer/State/StateEditor.cs +++ b/Glamourer/State/StateEditor.cs @@ -2,6 +2,7 @@ using Glamourer.Designs; using Glamourer.Designs.Links; using Glamourer.Events; using Glamourer.GameData; +using Glamourer.Interop.Penumbra; using Glamourer.Interop.Structs; using Glamourer.Services; using Penumbra.GameData.Enums; @@ -16,7 +17,8 @@ public class StateEditor( JobChangeState jobChange, Configuration config, ItemManager items, - DesignMerger merger) : IDesignEditor + DesignMerger merger, + ModSettingApplier modApplier) : IDesignEditor { protected readonly InternalStateEditor Editor = editor; protected readonly StateApplier Applier = applier; @@ -181,6 +183,7 @@ public class StateEditor( public void ApplyDesign(object data, MergedDesign mergedDesign, ApplySettings settings) { var state = (ActorState)data; + modApplier.HandleStateApplication(state, mergedDesign); if (!Editor.ChangeModelId(state, mergedDesign.Design.DesignData.ModelId, mergedDesign.Design.DesignData.Customize, mergedDesign.Design.GetDesignDataRef().GetEquipmentPtr(), settings.Source, out var oldModelId, settings.Key)) return; @@ -294,7 +297,7 @@ public class StateEditor( public void ApplyDesign(object data, DesignBase design, ApplySettings settings) { var merged = settings.MergeLinks && design is Design d - ? merger.Merge(d.AllLinks, ((ActorState)data).ModelData, false, false) + ? merger.Merge(d.AllLinks, ((ActorState)data).ModelData, false, Config.AlwaysApplyAssociatedMods) : new MergedDesign(design); ApplyDesign(data, merged, settings with diff --git a/Glamourer/State/StateManager.cs b/Glamourer/State/StateManager.cs index e6ed657..32d1237 100644 --- a/Glamourer/State/StateManager.cs +++ b/Glamourer/State/StateManager.cs @@ -4,6 +4,7 @@ using Glamourer.Designs.Links; using Glamourer.Events; using Glamourer.GameData; using Glamourer.Interop; +using Glamourer.Interop.Penumbra; using Glamourer.Interop.Structs; using Glamourer.Services; using Penumbra.GameData.Actors; @@ -23,8 +24,9 @@ public sealed class StateManager( IClientState _clientState, Configuration config, JobChangeState jobChange, - DesignMerger merger) - : StateEditor(editor, applier, @event, jobChange, config, items, merger), IReadOnlyDictionary + DesignMerger merger, + ModSettingApplier modApplier) + : StateEditor(editor, applier, @event, jobChange, config, items, merger, modApplier), IReadOnlyDictionary { private readonly Dictionary _states = [];