diff --git a/Glamourer/Automation/AutoDesignApplier.cs b/Glamourer/Automation/AutoDesignApplier.cs index e506d9f..660acf4 100644 --- a/Glamourer/Automation/AutoDesignApplier.cs +++ b/Glamourer/Automation/AutoDesignApplier.cs @@ -293,6 +293,8 @@ public sealed class AutoDesignApplier : IDisposable set.Designs.Where(d => d.IsActive(actor)) .SelectMany(d => d.Design.AllLinks(newApplication).Select(l => (l.Design, l.Flags & d.Type, d.Jobs.Flags))), state.ModelData.Customize, state.BaseData, true, _config.AlwaysApplyAssociatedMods); + if (set.ResetTemporarySettings) + mergedDesign.ResetTemporarySettings = true; _state.ApplyDesign(state, mergedDesign, new ApplySettings(0, StateSource.Fixed, respectManual, fromJobChange, false, false, false)); forcedRedraw = mergedDesign.ForcedRedraw; diff --git a/Glamourer/Automation/AutoDesignManager.cs b/Glamourer/Automation/AutoDesignManager.cs index a219376..5d30de0 100644 --- a/Glamourer/Automation/AutoDesignManager.cs +++ b/Glamourer/Automation/AutoDesignManager.cs @@ -444,8 +444,9 @@ public class AutoDesignManager : ISavable, IReadOnlyList, IDispos var set = new AutoDesignSet(name, group) { - Enabled = obj["Enabled"]?.ToObject() ?? false, - BaseState = obj["BaseState"]?.ToObject() ?? AutoDesignSet.Base.Current, + Enabled = obj["Enabled"]?.ToObject() ?? false, + ResetTemporarySettings = obj["ResetTemporarySettings"]?.ToObject() ?? false, + BaseState = obj["BaseState"]?.ToObject() ?? AutoDesignSet.Base.Current, }; if (set.Enabled) @@ -602,9 +603,9 @@ public class AutoDesignManager : ISavable, IReadOnlyList, IDispos ? ActorIdentifier.RetainerType.Mannequin : ActorIdentifier.RetainerType.Bell).CreatePermanent(), ], - IdentifierType.Npc => CreateNpcs(_actors, identifier), + IdentifierType.Npc => CreateNpcs(_actors, identifier), IdentifierType.Owned => CreateNpcs(_actors, identifier), - _ => [], + _ => [], }; static ActorIdentifier[] CreateNpcs(ActorManager manager, ActorIdentifier identifier) diff --git a/Glamourer/Automation/AutoDesignSet.cs b/Glamourer/Automation/AutoDesignSet.cs index adaa355..f8987af 100644 --- a/Glamourer/Automation/AutoDesignSet.cs +++ b/Glamourer/Automation/AutoDesignSet.cs @@ -10,7 +10,8 @@ public class AutoDesignSet(string name, ActorIdentifier[] identifiers, List(other.AssociatedMods); - Links = Links.Clone(); + Tags = [.. other.Tags]; + Description = other.Description; + QuickDesign = other.QuickDesign; + ForcedRedraw = other.ForcedRedraw; + ResetAdvancedDyes = other.ResetAdvancedDyes; + ResetTemporarySettings = other.ResetTemporarySettings; + Color = other.Color; + AssociatedMods = new SortedList(other.AssociatedMods); + Links = Links.Clone(); } // Metadata public new const int FileVersion = 2; - public Guid Identifier { get; internal init; } - public DateTimeOffset CreationDate { get; internal init; } - public DateTimeOffset LastEdit { get; internal set; } - public LowerString Name { get; internal set; } = LowerString.Empty; - public string Description { get; internal set; } = string.Empty; - public string[] Tags { get; internal set; } = []; - public int Index { get; internal set; } - public bool ForcedRedraw { get; internal set; } - public bool ResetAdvancedDyes { get; internal set; } - public bool QuickDesign { get; internal set; } = true; - public string Color { get; internal set; } = string.Empty; - public SortedList AssociatedMods { get; private set; } = []; - public LinkContainer Links { get; private set; } = []; + public Guid Identifier { get; internal init; } + public DateTimeOffset CreationDate { get; internal init; } + public DateTimeOffset LastEdit { get; internal set; } + public LowerString Name { get; internal set; } = LowerString.Empty; + public string Description { get; internal set; } = string.Empty; + public string[] Tags { get; internal set; } = []; + public int Index { get; internal set; } + public bool ForcedRedraw { get; internal set; } + public bool ResetAdvancedDyes { get; internal set; } + public bool ResetTemporarySettings { get; internal set; } + public bool QuickDesign { get; internal set; } = true; + public string Color { get; internal set; } = string.Empty; + public SortedList AssociatedMods { get; private set; } = []; + public LinkContainer Links { get; private set; } = []; public string Incognito => Identifier.ToString()[..8]; @@ -100,25 +102,26 @@ public sealed class Design : DesignBase, ISavable, IDesignStandIn { var ret = new JObject() { - ["FileVersion"] = FileVersion, - ["Identifier"] = Identifier, - ["CreationDate"] = CreationDate, - ["LastEdit"] = LastEdit, - ["Name"] = Name.Text, - ["Description"] = Description, - ["ForcedRedraw"] = ForcedRedraw, - ["ResetAdvancedDyes"] = ResetAdvancedDyes, - ["Color"] = Color, - ["QuickDesign"] = QuickDesign, - ["Tags"] = JArray.FromObject(Tags), - ["WriteProtected"] = WriteProtected(), - ["Equipment"] = SerializeEquipment(), - ["Bonus"] = SerializeBonusItems(), - ["Customize"] = SerializeCustomize(), - ["Parameters"] = SerializeParameters(), - ["Materials"] = SerializeMaterials(), - ["Mods"] = SerializeMods(), - ["Links"] = Links.Serialize(), + ["FileVersion"] = FileVersion, + ["Identifier"] = Identifier, + ["CreationDate"] = CreationDate, + ["LastEdit"] = LastEdit, + ["Name"] = Name.Text, + ["Description"] = Description, + ["ForcedRedraw"] = ForcedRedraw, + ["ResetAdvancedDyes"] = ResetAdvancedDyes, + ["ResetTemporarySettings"] = ResetTemporarySettings, + ["Color"] = Color, + ["QuickDesign"] = QuickDesign, + ["Tags"] = JArray.FromObject(Tags), + ["WriteProtected"] = WriteProtected(), + ["Equipment"] = SerializeEquipment(), + ["Bonus"] = SerializeBonusItems(), + ["Customize"] = SerializeCustomize(), + ["Parameters"] = SerializeParameters(), + ["Materials"] = SerializeMaterials(), + ["Mods"] = SerializeMods(), + ["Links"] = Links.Serialize(), }; return ret; } @@ -250,9 +253,10 @@ public sealed class Design : DesignBase, ISavable, IDesignStandIn LoadParameters(json["Parameters"], design, design.Name); LoadMaterials(json["Materials"], design, design.Name); LoadLinks(linkLoader, json["Links"], design); - design.Color = json["Color"]?.ToObject() ?? string.Empty; - design.ForcedRedraw = json["ForcedRedraw"]?.ToObject() ?? false; - design.ResetAdvancedDyes = json["ResetAdvancedDyes"]?.ToObject() ?? false; + design.Color = json["Color"]?.ToObject() ?? string.Empty; + design.ForcedRedraw = json["ForcedRedraw"]?.ToObject() ?? false; + design.ResetAdvancedDyes = json["ResetAdvancedDyes"]?.ToObject() ?? false; + design.ResetTemporarySettings = json["ResetTemporarySettings"]?.ToObject() ?? false; return design; static string[] ParseTags(JObject json) @@ -278,12 +282,15 @@ public sealed class Design : DesignBase, ISavable, IDesignStandIn continue; } - var settingsDict = tok["Settings"]?.ToObject>>() ?? []; - var settings = new Dictionary>(settingsDict.Count); + var forceInherit = tok["Inherit"]?.ToObject() ?? false; + var removeSetting = tok["Remove"]?.ToObject() ?? false; + var settingsDict = tok["Settings"]?.ToObject>>() ?? []; + var settings = new Dictionary>(settingsDict.Count); foreach (var (key, value) in settingsDict) settings.Add(key, value); var priority = tok["Priority"]?.ToObject() ?? 0; - if (!design.AssociatedMods.TryAdd(new Mod(name, directory), new ModSettings(settings, priority, enabled.Value))) + if (!design.AssociatedMods.TryAdd(new Mod(name, directory), + new ModSettings(settings, priority, enabled.Value, forceInherit, removeSetting))) Glamourer.Messager.NotificationMessage("The loaded design contains a mod more than once, skipped.", NotificationType.Warning); } } diff --git a/Glamourer/Designs/DesignManager.cs b/Glamourer/Designs/DesignManager.cs index b889649..f931489 100644 --- a/Glamourer/Designs/DesignManager.cs +++ b/Glamourer/Designs/DesignManager.cs @@ -99,14 +99,15 @@ public sealed class DesignManager : DesignEditor var (actualName, path) = ParseName(name, handlePath); var design = new Design(Customizations, Items) { - CreationDate = DateTimeOffset.UtcNow, - LastEdit = DateTimeOffset.UtcNow, - Identifier = CreateNewGuid(), - Name = actualName, - Index = Designs.Count, - ForcedRedraw = Config.DefaultDesignSettings.AlwaysForceRedrawing, - ResetAdvancedDyes = Config.DefaultDesignSettings.ResetAdvancedDyes, - QuickDesign = Config.DefaultDesignSettings.ShowQuickDesignBar, + CreationDate = DateTimeOffset.UtcNow, + LastEdit = DateTimeOffset.UtcNow, + Identifier = CreateNewGuid(), + Name = actualName, + Index = Designs.Count, + ForcedRedraw = Config.DefaultDesignSettings.AlwaysForceRedrawing, + ResetAdvancedDyes = Config.DefaultDesignSettings.ResetAdvancedDyes, + QuickDesign = Config.DefaultDesignSettings.ShowQuickDesignBar, + ResetTemporarySettings = Config.DefaultDesignSettings.ResetTemporarySettings, }; Designs.Add(design); Glamourer.Log.Debug($"Added new design {design.Identifier}."); @@ -121,14 +122,15 @@ public sealed class DesignManager : DesignEditor var (actualName, path) = ParseName(name, handlePath); var design = new Design(clone) { - CreationDate = DateTimeOffset.UtcNow, - LastEdit = DateTimeOffset.UtcNow, - Identifier = CreateNewGuid(), - Name = actualName, - Index = Designs.Count, - ForcedRedraw = Config.DefaultDesignSettings.AlwaysForceRedrawing, - ResetAdvancedDyes = Config.DefaultDesignSettings.ResetAdvancedDyes, - QuickDesign = Config.DefaultDesignSettings.ShowQuickDesignBar, + CreationDate = DateTimeOffset.UtcNow, + LastEdit = DateTimeOffset.UtcNow, + Identifier = CreateNewGuid(), + Name = actualName, + Index = Designs.Count, + ForcedRedraw = Config.DefaultDesignSettings.AlwaysForceRedrawing, + ResetAdvancedDyes = Config.DefaultDesignSettings.ResetAdvancedDyes, + QuickDesign = Config.DefaultDesignSettings.ShowQuickDesignBar, + ResetTemporarySettings = Config.DefaultDesignSettings.ResetTemporarySettings, }; Designs.Add(design); @@ -144,11 +146,11 @@ public sealed class DesignManager : DesignEditor var (actualName, path) = ParseName(name, handlePath); var design = new Design(clone) { - CreationDate = DateTimeOffset.UtcNow, - LastEdit = DateTimeOffset.UtcNow, - Identifier = CreateNewGuid(), - Name = actualName, - Index = Designs.Count, + CreationDate = DateTimeOffset.UtcNow, + LastEdit = DateTimeOffset.UtcNow, + Identifier = CreateNewGuid(), + Name = actualName, + Index = Designs.Count, }; Designs.Add(design); Glamourer.Log.Debug( @@ -351,6 +353,17 @@ public sealed class DesignManager : DesignEditor DesignChanged.Invoke(DesignChanged.Type.ResetAdvancedDyes, design, null); } + public void ChangeResetTemporarySettings(Design design, bool resetTemporarySettings) + { + if (design.ResetTemporarySettings == resetTemporarySettings) + return; + + design.ResetTemporarySettings = resetTemporarySettings; + SaveService.QueueSave(design); + Glamourer.Log.Debug($"Set {design.Identifier} to {(resetTemporarySettings ? string.Empty : "not")} reset temporary settings."); + DesignChanged.Invoke(DesignChanged.Type.ResetTemporarySettings, design, null); + } + /// Change whether to apply a specific customize value. public void ChangeApplyCustomize(Design design, CustomizeIndex idx, bool value) { diff --git a/Glamourer/Designs/IDesignStandIn.cs b/Glamourer/Designs/IDesignStandIn.cs index 02baee4..d07acb9 100644 --- a/Glamourer/Designs/IDesignStandIn.cs +++ b/Glamourer/Designs/IDesignStandIn.cs @@ -26,5 +26,6 @@ public interface IDesignStandIn : IEquatable public bool ForcedRedraw { get; } - public bool ResetAdvancedDyes { get; } + public bool ResetAdvancedDyes { get; } + public bool ResetTemporarySettings { get; } } diff --git a/Glamourer/Designs/Links/DesignMerger.cs b/Glamourer/Designs/Links/DesignMerger.cs index e0e5d95..0284322 100644 --- a/Glamourer/Designs/Links/DesignMerger.cs +++ b/Glamourer/Designs/Links/DesignMerger.cs @@ -58,6 +58,8 @@ public class DesignMerger( ret.ForcedRedraw = true; if (design.ResetAdvancedDyes) ret.ResetAdvancedDyes = true; + if (design.ResetTemporarySettings) + ret.ResetTemporarySettings = true; } ApplyFixFlags(ret, fixFlags); diff --git a/Glamourer/Designs/Links/MergedDesign.cs b/Glamourer/Designs/Links/MergedDesign.cs index 9c9e079..3d81cda 100644 --- a/Glamourer/Designs/Links/MergedDesign.cs +++ b/Glamourer/Designs/Links/MergedDesign.cs @@ -101,4 +101,5 @@ public sealed class MergedDesign public StateSources Sources = new(); public bool ForcedRedraw; public bool ResetAdvancedDyes; + public bool ResetTemporarySettings; } diff --git a/Glamourer/Designs/Special/QuickSelectedDesign.cs b/Glamourer/Designs/Special/QuickSelectedDesign.cs index 31fb40f..740bb7f 100644 --- a/Glamourer/Designs/Special/QuickSelectedDesign.cs +++ b/Glamourer/Designs/Special/QuickSelectedDesign.cs @@ -56,4 +56,7 @@ public class QuickSelectedDesign(QuickDesignCombo combo) : IDesignStandIn, IServ public bool ResetAdvancedDyes => combo.Design?.ResetAdvancedDyes ?? false; + + public bool ResetTemporarySettings + => combo.Design?.ResetTemporarySettings ?? false; } diff --git a/Glamourer/Designs/Special/RandomDesign.cs b/Glamourer/Designs/Special/RandomDesign.cs index a54ffcf..844f203 100644 --- a/Glamourer/Designs/Special/RandomDesign.cs +++ b/Glamourer/Designs/Special/RandomDesign.cs @@ -92,8 +92,11 @@ public class RandomDesign(RandomDesignGenerator rng) : IDesignStandIn } public bool ForcedRedraw - => false; + => _currentDesign?.ForcedRedraw ?? false; public bool ResetAdvancedDyes - => false; + => _currentDesign?.ResetAdvancedDyes ?? false; + + public bool ResetTemporarySettings + => _currentDesign?.ResetTemporarySettings ?? false; } diff --git a/Glamourer/Designs/Special/RevertDesign.cs b/Glamourer/Designs/Special/RevertDesign.cs index 8704339..4caf7b6 100644 --- a/Glamourer/Designs/Special/RevertDesign.cs +++ b/Glamourer/Designs/Special/RevertDesign.cs @@ -48,4 +48,7 @@ public class RevertDesign : IDesignStandIn public bool ResetAdvancedDyes => true; + + public bool ResetTemporarySettings + => true; } diff --git a/Glamourer/Events/DesignChanged.cs b/Glamourer/Events/DesignChanged.cs index 8cd8f5c..04bb46a 100644 --- a/Glamourer/Events/DesignChanged.cs +++ b/Glamourer/Events/DesignChanged.cs @@ -93,6 +93,9 @@ public sealed class DesignChanged() /// An existing design had changed whether it always resets advanced dyes or not. ResetAdvancedDyes, + /// An existing design had changed whether it always resets all prior temporary settings or not. + ResetTemporarySettings, + /// An existing design changed whether a specific customization is applied. ApplyCustomize, diff --git a/Glamourer/Gui/Tabs/ActorTab/ActorPanel.cs b/Glamourer/Gui/Tabs/ActorTab/ActorPanel.cs index 2549240..265e1d9 100644 --- a/Glamourer/Gui/Tabs/ActorTab/ActorPanel.cs +++ b/Glamourer/Gui/Tabs/ActorTab/ActorPanel.cs @@ -159,6 +159,7 @@ public class ActorPanel return; ImGui.TableSetupScrollFreeze(0, 1); ImGui.TableNextColumn(); + ImGui.Dummy(Vector2.Zero); var transformationId = _actor.IsCharacter ? _actor.AsCharacter->CharacterData.TransformationId : 0; if (transformationId != 0) ImGuiUtil.DrawTextButton($"Currently transformed to Transformation {transformationId}.", diff --git a/Glamourer/Gui/Tabs/DesignTab/DesignDetailTab.cs b/Glamourer/Gui/Tabs/DesignTab/DesignDetailTab.cs index bd74772..1469deb 100644 --- a/Glamourer/Gui/Tabs/DesignTab/DesignDetailTab.cs +++ b/Glamourer/Gui/Tabs/DesignTab/DesignDetailTab.cs @@ -6,6 +6,7 @@ using ImGuiNET; using OtterGui; using OtterGui.Classes; using OtterGui.Raii; +using OtterGui.Text; using OtterGui.Widgets; namespace Glamourer.Gui.Tabs.DesignTab; @@ -41,7 +42,7 @@ public class DesignDetailTab public void Draw() { - using var h = ImRaii.CollapsingHeader("Design Details"); + using var h = ImUtf8.CollapsingHeaderId("Design Details"u8); if (!h) return; @@ -54,19 +55,19 @@ public class DesignDetailTab private void DrawDesignInfoTable() { using var style = ImRaii.PushStyle(ImGuiStyleVar.ButtonTextAlign, new Vector2(0, 0.5f)); - using var table = ImRaii.Table("Details", 2); + using var table = ImUtf8.Table("Details"u8, 2); if (!table) return; - ImGui.TableSetupColumn("Type", ImGuiTableColumnFlags.WidthFixed, ImGui.CalcTextSize("Reset Advanced Dyes").X); - ImGui.TableSetupColumn("Data", ImGuiTableColumnFlags.WidthStretch); + ImUtf8.TableSetupColumn("Type"u8, ImGuiTableColumnFlags.WidthFixed, ImUtf8.CalcTextSize("Reset Temporary Settings"u8).X); + ImUtf8.TableSetupColumn("Data"u8, ImGuiTableColumnFlags.WidthStretch); - ImGuiUtil.DrawFrameColumn("Design Name"); + ImUtf8.DrawFrameColumn("Design Name"u8); ImGui.TableNextColumn(); var width = new Vector2(ImGui.GetContentRegionAvail().X, 0); var name = _newName ?? _selector.Selected!.Name; ImGui.SetNextItemWidth(width.X); - if (ImGui.InputText("##Name", ref name, 128)) + if (ImUtf8.InputText("##Name"u8, ref name)) { _newName = name; _changeDesign = _selector.Selected; @@ -80,10 +81,10 @@ public class DesignDetailTab } var identifier = _selector.Selected!.Identifier.ToString(); - ImGuiUtil.DrawFrameColumn("Unique Identifier"); + ImUtf8.DrawFrameColumn("Unique Identifier"u8); ImGui.TableNextColumn(); var fileName = _saveService.FileNames.DesignFile(_selector.Selected!); - using (var mono = ImRaii.PushFont(UiBuilder.MonoFont)) + using (ImRaii.PushFont(UiBuilder.MonoFont)) { if (ImGui.Button(identifier, width)) try @@ -100,14 +101,14 @@ public class DesignDetailTab ImGui.SetClipboardText(identifier); } - ImGuiUtil.HoverTooltip( + ImUtf8.HoverTooltip( $"Open the file\n\t{fileName}\ncontaining this design in the .json-editor of your choice.\n\nRight-Click to copy identifier to clipboard."); - ImGuiUtil.DrawFrameColumn("Full Selector Path"); + ImUtf8.DrawFrameColumn("Full Selector Path"u8); ImGui.TableNextColumn(); var path = _newPath ?? _selector.SelectedLeaf!.FullName(); ImGui.SetNextItemWidth(width.X); - if (ImGui.InputText("##Path", ref path, 1024)) + if (ImUtf8.InputText("##Path"u8, ref path)) { _newPath = path; _changeLeaf = _selector.SelectedLeaf!; @@ -125,32 +126,42 @@ public class DesignDetailTab Glamourer.Messager.NotificationMessage(ex, ex.Message, "Could not rename or move design", NotificationType.Error); } - ImGuiUtil.DrawFrameColumn("Quick Design Bar"); + ImUtf8.DrawFrameColumn("Quick Design Bar"u8); ImGui.TableNextColumn(); - if (ImGui.RadioButton("Display##qdb", _selector.Selected.QuickDesign)) + if (ImUtf8.RadioButton("Display##qdb"u8, _selector.Selected.QuickDesign)) _manager.SetQuickDesign(_selector.Selected!, true); var hovered = ImGui.IsItemHovered(); ImGui.SameLine(); - if (ImGui.RadioButton("Hide##qdb", !_selector.Selected.QuickDesign)) + if (ImUtf8.RadioButton("Hide##qdb"u8, !_selector.Selected.QuickDesign)) _manager.SetQuickDesign(_selector.Selected!, false); if (hovered || ImGui.IsItemHovered()) - ImGui.SetTooltip("Display or hide this design in your quick design bar."); + { + using var tt = ImUtf8.Tooltip(); + ImUtf8.Text("Display or hide this design in your quick design bar."u8); + } var forceRedraw = _selector.Selected!.ForcedRedraw; - ImGuiUtil.DrawFrameColumn("Force Redrawing"); + ImUtf8.DrawFrameColumn("Force Redrawing"u8); ImGui.TableNextColumn(); - if (ImGui.Checkbox("##ForceRedraw", ref forceRedraw)) + if (ImUtf8.Checkbox("##ForceRedraw"u8, ref forceRedraw)) _manager.ChangeForcedRedraw(_selector.Selected!, forceRedraw); - ImGuiUtil.HoverTooltip("Set this design to always force a redraw when it is applied through any means."); + ImUtf8.HoverTooltip("Set this design to always force a redraw when it is applied through any means."u8); var resetAdvancedDyes = _selector.Selected!.ResetAdvancedDyes; - ImGuiUtil.DrawFrameColumn("Reset Advanced Dyes"); + ImUtf8.DrawFrameColumn("Reset Advanced Dyes"u8); ImGui.TableNextColumn(); - if (ImGui.Checkbox("##ResetAdvancedDyes", ref resetAdvancedDyes)) + if (ImUtf8.Checkbox("##ResetAdvancedDyes"u8, ref resetAdvancedDyes)) _manager.ChangeResetAdvancedDyes(_selector.Selected!, resetAdvancedDyes); - ImGuiUtil.HoverTooltip("Set this design to reset any previously applied advanced dyes when it is applied through any means."); + ImUtf8.HoverTooltip("Set this design to reset any previously applied advanced dyes when it is applied through any means."u8); - ImGuiUtil.DrawFrameColumn("Color"); + var resetTemporarySettings = _selector.Selected!.ResetTemporarySettings; + ImUtf8.DrawFrameColumn("Reset Temporary Settings"u8); + ImGui.TableNextColumn(); + if (ImUtf8.Checkbox("##ResetTemporarySettings"u8, ref resetTemporarySettings)) + _manager.ChangeResetTemporarySettings(_selector.Selected!, resetTemporarySettings); + ImUtf8.HoverTooltip("Set this design to reset any temporary settings previously applied to the associated collection when it is applied through any means."u8); + + ImUtf8.DrawFrameColumn("Color"u8); var colorName = _selector.Selected!.Color.Length == 0 ? DesignColors.AutomaticName : _selector.Selected!.Color; ImGui.TableNextColumn(); if (_colorCombo.Draw("##colorCombo", colorName, "Associate a color with this design.\n" @@ -178,18 +189,18 @@ public class DesignDetailTab var size = new Vector2(ImGui.GetFrameHeight()); using var font = ImRaii.PushFont(UiBuilder.IconFont); ImGuiUtil.DrawTextButton(FontAwesomeIcon.ExclamationCircle.ToIconString(), size, 0, _colors.MissingColor); - ImGuiUtil.HoverTooltip("The color associated with this design does not exist."); + ImUtf8.HoverTooltip("The color associated with this design does not exist."u8); } - ImGuiUtil.DrawFrameColumn("Creation Date"); + ImUtf8.DrawFrameColumn("Creation Date"u8); ImGui.TableNextColumn(); ImGuiUtil.DrawTextButton(_selector.Selected!.CreationDate.LocalDateTime.ToString("F"), width, 0); - ImGuiUtil.DrawFrameColumn("Last Update Date"); + ImUtf8.DrawFrameColumn("Last Update Date"u8); ImGui.TableNextColumn(); ImGuiUtil.DrawTextButton(_selector.Selected!.LastEdit.LocalDateTime.ToString("F"), width, 0); - ImGuiUtil.DrawFrameColumn("Tags"); + ImUtf8.DrawFrameColumn("Tags"u8); ImGui.TableNextColumn(); DrawTags(); } @@ -219,18 +230,18 @@ public class DesignDetailTab var size = new Vector2(ImGui.GetContentRegionAvail().X, 12 * ImGui.GetTextLineHeightWithSpacing()); if (!_editDescriptionMode) { - using (var textBox = ImRaii.ListBox("##desc", size)) + using (var textBox = ImUtf8.ListBox("##desc"u8, size)) { - ImGuiUtil.TextWrapped(desc); + ImUtf8.TextWrapped(desc); } - if (ImGui.Button("Edit Description")) + if (ImUtf8.Button("Edit Description"u8)) _editDescriptionMode = true; } else { var edit = _newDescription ?? desc; - if (ImGui.InputTextMultiline("##desc", ref edit, (uint)Math.Max(2000, 4 * edit.Length), size)) + if (ImUtf8.InputMultiLine("##desc"u8, ref edit, size)) _newDescription = edit; if (ImGui.IsItemDeactivatedAfterEdit()) @@ -239,7 +250,7 @@ public class DesignDetailTab _newDescription = null; } - if (ImGui.Button("Stop Editing")) + if (ImUtf8.Button("Stop Editing"u8)) _editDescriptionMode = false; } } diff --git a/Glamourer/Gui/Tabs/DesignTab/DesignPanel.cs b/Glamourer/Gui/Tabs/DesignTab/DesignPanel.cs index 26553a7..070ca1e 100644 --- a/Glamourer/Gui/Tabs/DesignTab/DesignPanel.cs +++ b/Glamourer/Gui/Tabs/DesignTab/DesignPanel.cs @@ -423,6 +423,7 @@ public class DesignPanel ImGui.TableNextColumn(); if (_selector.Selected == null) return; + ImGui.Dummy(Vector2.Zero); DrawButtonRow(); ImGui.TableNextColumn(); @@ -438,7 +439,6 @@ public class DesignPanel private void DrawButtonRow() { - ImGui.Dummy(Vector2.Zero); DrawApplyToSelf(); ImGui.SameLine(); DrawApplyToTarget(); diff --git a/Glamourer/Gui/Tabs/DesignTab/ModAssociationsTab.cs b/Glamourer/Gui/Tabs/DesignTab/ModAssociationsTab.cs index 1f1e1ad..20ddbe8 100644 --- a/Glamourer/Gui/Tabs/DesignTab/ModAssociationsTab.cs +++ b/Glamourer/Gui/Tabs/DesignTab/ModAssociationsTab.cs @@ -88,16 +88,18 @@ public class ModAssociationsTab(PenumbraService penumbra, DesignFileSystemSelect private void DrawTable() { - using var table = ImRaii.Table("Mods", 5, ImGuiTableFlags.RowBg); + using var table = ImUtf8.Table("Mods"u8, 7, ImGuiTableFlags.RowBg); if (!table) return; - ImGui.TableSetupColumn("##Buttons", ImGuiTableColumnFlags.WidthFixed, + ImUtf8.TableSetupColumn("##Buttons"u8, ImGuiTableColumnFlags.WidthFixed, ImGui.GetFrameHeight() * 3 + ImGui.GetStyle().ItemInnerSpacing.X * 2); - ImGui.TableSetupColumn("Mod Name", ImGuiTableColumnFlags.WidthStretch); - ImGui.TableSetupColumn("State", ImGuiTableColumnFlags.WidthFixed, ImGui.CalcTextSize("State").X); - ImGui.TableSetupColumn("Priority", ImGuiTableColumnFlags.WidthFixed, ImGui.CalcTextSize("Priority").X); - ImGui.TableSetupColumn("##Options", ImGuiTableColumnFlags.WidthFixed, ImGui.CalcTextSize("Applym").X); + ImUtf8.TableSetupColumn("Mod Name"u8, ImGuiTableColumnFlags.WidthStretch); + ImUtf8.TableSetupColumn("Remove"u8, ImGuiTableColumnFlags.WidthFixed, ImUtf8.CalcTextSize("Remove"u8).X); + ImUtf8.TableSetupColumn("Inherit"u8, ImGuiTableColumnFlags.WidthFixed, ImUtf8.CalcTextSize("Inherit"u8).X); + ImUtf8.TableSetupColumn("State"u8, ImGuiTableColumnFlags.WidthFixed, ImUtf8.CalcTextSize("State"u8).X); + ImUtf8.TableSetupColumn("Priority"u8, ImGuiTableColumnFlags.WidthFixed, ImUtf8.CalcTextSize("Priority"u8).X); + ImUtf8.TableSetupColumn("##Options"u8, ImGuiTableColumnFlags.WidthFixed, ImUtf8.CalcTextSize("Applym"u8).X); ImGui.TableHeadersRow(); Mod? removedMod = null; @@ -183,6 +185,17 @@ public class ModAssociationsTab(PenumbraService penumbra, DesignFileSystemSelect if (ImGui.IsItemHovered()) ImGui.SetTooltip($"Mod Directory: {mod.DirectoryName}\n\nClick to open mod page in Penumbra."); ImGui.TableNextColumn(); + var remove = settings.Remove; + if (TwoStateCheckbox.Instance.Draw("##Remove"u8, ref remove)) + updatedMod = (mod, settings with { Remove = remove }); + ImUtf8.HoverTooltip( + "Remove any temporary settings applied by Glamourer instead of applying the configured settings. Only works when using temporary settings, ignored otherwise."u8); + ImGui.TableNextColumn(); + var inherit = settings.ForceInherit; + if (TwoStateCheckbox.Instance.Draw("##Enabled"u8, ref inherit)) + updatedMod = (mod, settings with { ForceInherit = inherit }); + ImUtf8.HoverTooltip("Force the mod to inherit its settings from inherited collections."u8); + ImGui.TableNextColumn(); var enabled = settings.Enabled; if (TwoStateCheckbox.Instance.Draw("##Enabled"u8, ref enabled)) updatedMod = (mod, settings with { Enabled = enabled }); diff --git a/Glamourer/Gui/Tabs/SettingsTab/SettingsTab.cs b/Glamourer/Gui/Tabs/SettingsTab/SettingsTab.cs index c6d69fd..ab40a48 100644 --- a/Glamourer/Gui/Tabs/SettingsTab/SettingsTab.cs +++ b/Glamourer/Gui/Tabs/SettingsTab/SettingsTab.cs @@ -106,6 +106,9 @@ public class SettingsTab( + "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); + Checkbox("Use Temporary Mod Settings", + "Apply all settings as temporary settings so they will be reset when Glamourer or the game shut down.", config.UseTemporarySettings, + v => config.UseTemporarySettings = v); ImGui.NewLine(); } @@ -120,6 +123,8 @@ public class SettingsTab( config.DefaultDesignSettings.ResetAdvancedDyes, v => config.DefaultDesignSettings.ResetAdvancedDyes = v); Checkbox("Always Force Redraw", "Newly created designs will be configured to force character redraws on application by default.", config.DefaultDesignSettings.AlwaysForceRedrawing, v => config.DefaultDesignSettings.AlwaysForceRedrawing = v); + Checkbox("Reset Temporary Settings", "Newly created designs will be configured to clear all advanced settings applied by Glamourer to the collection by default.", + config.DefaultDesignSettings.ResetTemporarySettings, v => config.DefaultDesignSettings.ResetTemporarySettings = v); } private void DrawInterfaceSettings() diff --git a/Glamourer/Interop/Penumbra/ModSettingApplier.cs b/Glamourer/Interop/Penumbra/ModSettingApplier.cs index a6fe3e5..b804720 100644 --- a/Glamourer/Interop/Penumbra/ModSettingApplier.cs +++ b/Glamourer/Interop/Penumbra/ModSettingApplier.cs @@ -3,12 +3,15 @@ using Glamourer.Services; using Glamourer.State; using OtterGui.Services; using Penumbra.GameData.Interop; +using Penumbra.GameData.Structs; namespace Glamourer.Interop.Penumbra; public class ModSettingApplier(PenumbraService penumbra, Configuration config, ObjectManager objects, CollectionOverrideService overrides) : IService { + private readonly HashSet _collectionTracker = []; + public void HandleStateApplication(ActorState state, MergedDesign design) { if (!config.AlwaysApplyAssociatedMods || design.AssociatedMods.Count == 0) @@ -22,20 +25,20 @@ public class ModSettingApplier(PenumbraService penumbra, Configuration config, O return; } - var collections = new HashSet(); - + _collectionTracker.Clear(); foreach (var actor in data.Objects) { var (collection, _, overridden) = overrides.GetCollection(actor, state.Identifier); if (collection == Guid.Empty) continue; - if (!collections.Add(collection)) + if (!_collectionTracker.Add(collection)) continue; + var index = ResetOldSettings(collection, actor, design.ResetTemporarySettings); foreach (var (mod, setting) in design.AssociatedMods) { - var message = penumbra.SetMod(mod, setting, collection); + var message = penumbra.SetMod(mod, setting, collection, index); if (message.Length > 0) Glamourer.Log.Verbose($"[Mod Applier] Error applying mod settings: {message}"); else @@ -45,7 +48,8 @@ public class ModSettingApplier(PenumbraService penumbra, Configuration config, O } } - public (List Messages, int Applied, Guid Collection, string Name, bool Overridden) ApplyModSettings(IReadOnlyDictionary settings, Actor actor) + public (List Messages, int Applied, Guid Collection, string Name, bool Overridden) ApplyModSettings( + IReadOnlyDictionary settings, Actor actor, bool resetOther) { var (collection, name, overridden) = overrides.GetCollection(actor); if (collection == Guid.Empty) @@ -53,9 +57,11 @@ public class ModSettingApplier(PenumbraService penumbra, Configuration config, O var messages = new List(); var appliedMods = 0; + + var index = ResetOldSettings(collection, actor, resetOther); foreach (var (mod, setting) in settings) { - var message = penumbra.SetMod(mod, setting, collection); + var message = penumbra.SetMod(mod, setting, collection, index); if (message.Length > 0) messages.Add($"Error applying mod settings: {message}"); else @@ -64,4 +70,18 @@ public class ModSettingApplier(PenumbraService penumbra, Configuration config, O return (messages, appliedMods, collection, name, overridden); } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private ObjectIndex? ResetOldSettings(Guid collection, Actor actor, bool resetOther) + { + ObjectIndex? index = actor.Valid ? actor.Index : null; + if (!resetOther) + return index; + + if (index == null) + penumbra.RemoveAllTemporarySettings(collection); + else + penumbra.RemoveAllTemporarySettings(index.Value); + return index; + } } diff --git a/Glamourer/Interop/Penumbra/PenumbraService.cs b/Glamourer/Interop/Penumbra/PenumbraService.cs index 4f6c44a..868ce11 100644 --- a/Glamourer/Interop/Penumbra/PenumbraService.cs +++ b/Glamourer/Interop/Penumbra/PenumbraService.cs @@ -21,10 +21,10 @@ public readonly record struct Mod(string Name, string DirectoryName) : IComparab } } -public readonly record struct ModSettings(Dictionary> Settings, int Priority, bool Enabled) +public readonly record struct ModSettings(Dictionary> Settings, int Priority, bool Enabled, bool ForceInherit, bool Remove) { public ModSettings() - : this(new Dictionary>(), 0, false) + : this(new Dictionary>(), 0, false, false, false) { } public static ModSettings Empty @@ -33,30 +33,41 @@ public readonly record struct ModSettings(Dictionary> Setti public class PenumbraService : IDisposable { - public const int RequiredPenumbraBreakingVersion = 5; - public const int RequiredPenumbraFeatureVersion = 0; + public const int RequiredPenumbraBreakingVersion = 5; + public const int RequiredPenumbraFeatureVersion = 3; + public const int RequiredPenumbraFeatureVersionTemp = 4; - private readonly IDalamudPluginInterface _pluginInterface; + private const int Key = -1610; + + private readonly IDalamudPluginInterface _pluginInterface; + private readonly Configuration _config; private readonly EventSubscriber _tooltipSubscriber; private readonly EventSubscriber _clickSubscriber; private readonly EventSubscriber _creatingCharacterBase; private readonly EventSubscriber _createdCharacterBase; private readonly EventSubscriber _modSettingChanged; - private global::Penumbra.Api.IpcSubscribers.GetCollectionsByIdentifier? _collectionByIdentifier; - private global::Penumbra.Api.IpcSubscribers.GetCollections? _collections; - private global::Penumbra.Api.IpcSubscribers.RedrawObject? _redraw; - private global::Penumbra.Api.IpcSubscribers.GetDrawObjectInfo? _drawObjectInfo; - private global::Penumbra.Api.IpcSubscribers.GetCutsceneParentIndex? _cutsceneParent; - private global::Penumbra.Api.IpcSubscribers.GetCollectionForObject? _objectCollection; - private global::Penumbra.Api.IpcSubscribers.GetModList? _getMods; - private global::Penumbra.Api.IpcSubscribers.GetCollection? _currentCollection; - private global::Penumbra.Api.IpcSubscribers.GetCurrentModSettings? _getCurrentSettings; - private global::Penumbra.Api.IpcSubscribers.TrySetMod? _setMod; - private global::Penumbra.Api.IpcSubscribers.TrySetModPriority? _setModPriority; - private global::Penumbra.Api.IpcSubscribers.TrySetModSetting? _setModSetting; - private global::Penumbra.Api.IpcSubscribers.TrySetModSettings? _setModSettings; - private global::Penumbra.Api.IpcSubscribers.OpenMainWindow? _openModPage; + private global::Penumbra.Api.IpcSubscribers.GetCollectionsByIdentifier? _collectionByIdentifier; + private global::Penumbra.Api.IpcSubscribers.GetCollections? _collections; + private global::Penumbra.Api.IpcSubscribers.RedrawObject? _redraw; + private global::Penumbra.Api.IpcSubscribers.GetDrawObjectInfo? _drawObjectInfo; + private global::Penumbra.Api.IpcSubscribers.GetCutsceneParentIndex? _cutsceneParent; + private global::Penumbra.Api.IpcSubscribers.GetCollectionForObject? _objectCollection; + private global::Penumbra.Api.IpcSubscribers.GetModList? _getMods; + private global::Penumbra.Api.IpcSubscribers.GetCollection? _currentCollection; + private global::Penumbra.Api.IpcSubscribers.GetCurrentModSettings? _getCurrentSettings; + private global::Penumbra.Api.IpcSubscribers.TryInheritMod? _inheritMod; + private global::Penumbra.Api.IpcSubscribers.TrySetMod? _setMod; + private global::Penumbra.Api.IpcSubscribers.TrySetModPriority? _setModPriority; + private global::Penumbra.Api.IpcSubscribers.TrySetModSetting? _setModSetting; + private global::Penumbra.Api.IpcSubscribers.TrySetModSettings? _setModSettings; + private global::Penumbra.Api.IpcSubscribers.SetTemporaryModSettings? _setTemporaryModSettings; + private global::Penumbra.Api.IpcSubscribers.SetTemporaryModSettingsPlayer? _setTemporaryModSettingsPlayer; + private global::Penumbra.Api.IpcSubscribers.RemoveTemporaryModSettings? _removeTemporaryModSettings; + private global::Penumbra.Api.IpcSubscribers.RemoveTemporaryModSettingsPlayer? _removeTemporaryModSettingsPlayer; + private global::Penumbra.Api.IpcSubscribers.RemoveAllTemporaryModSettings? _removeAllTemporaryModSettings; + private global::Penumbra.Api.IpcSubscribers.RemoveAllTemporaryModSettingsPlayer? _removeAllTemporaryModSettingsPlayer; + private global::Penumbra.Api.IpcSubscribers.OpenMainWindow? _openModPage; private readonly IDisposable _initializedEvent; private readonly IDisposable _disposedEvent; @@ -68,10 +79,11 @@ public class PenumbraService : IDisposable public int CurrentMinor { get; private set; } public DateTime AttachTime { get; private set; } - public PenumbraService(IDalamudPluginInterface pi, PenumbraReloaded penumbraReloaded) + public PenumbraService(IDalamudPluginInterface pi, PenumbraReloaded penumbraReloaded, Configuration config) { _pluginInterface = pi; _penumbraReloaded = penumbraReloaded; + _config = config; _initializedEvent = global::Penumbra.Api.IpcSubscribers.Initialized.Subscriber(pi, Reattach); _disposedEvent = global::Penumbra.Api.IpcSubscribers.Disposed.Subscriber(pi, Unattach); _tooltipSubscriber = global::Penumbra.Api.IpcSubscribers.ChangedItemTooltip.Subscriber(pi); @@ -128,7 +140,7 @@ public class PenumbraService : IDisposable if (ec is not PenumbraApiEc.Success) return ModSettings.Empty; - return tuple.HasValue ? new ModSettings(tuple.Value.Item3, tuple.Value.Item2, tuple.Value.Item1) : ModSettings.Empty; + return tuple.HasValue ? new ModSettings(tuple.Value.Item3, tuple.Value.Item2, tuple.Value.Item1, false, false) : ModSettings.Empty; } catch (Exception ex) { @@ -164,7 +176,7 @@ public class PenumbraService : IDisposable .Select(t => (new Mod(t.Item2, t.Item1), !t.Item3.Item2.HasValue ? ModSettings.Empty - : new ModSettings(t.Item3.Item2!.Value.Item3, t.Item3.Item2!.Value.Item2, t.Item3.Item2!.Value.Item1))) + : new ModSettings(t.Item3.Item2!.Value.Item3, t.Item3.Item2!.Value.Item2, t.Item3.Item2!.Value.Item1, false, false))) .OrderByDescending(p => p.Item2.Enabled) .ThenBy(p => p.Item1.Name) .ThenBy(p => p.Item1.DirectoryName) @@ -195,7 +207,7 @@ public class PenumbraService : IDisposable /// Try to set all mod settings as desired. Only sets when the mod should be enabled. /// If it is disabled, ignore all other settings. /// - public string SetMod(Mod mod, ModSettings settings, Guid? collectionInput = null) + public string SetMod(Mod mod, ModSettings settings, Guid? collectionInput = null, ObjectIndex? index = null) { if (!Available) return "Penumbra is not available."; @@ -204,40 +216,10 @@ public class PenumbraService : IDisposable try { var collection = collectionInput ?? _currentCollection!.Invoke(ApiCollectionType.Current)!.Value.Id; - var ec = _setMod!.Invoke(collection, mod.DirectoryName, settings.Enabled); - switch (ec) - { - case PenumbraApiEc.ModMissing: return $"The mod {mod.Name} [{mod.DirectoryName}] could not be found."; - case PenumbraApiEc.CollectionMissing: return $"The collection {collection} could not be found."; - } - - if (!settings.Enabled) - return string.Empty; - - ec = _setModPriority!.Invoke(collection, mod.DirectoryName, settings.Priority); - Debug.Assert(ec is PenumbraApiEc.Success or PenumbraApiEc.NothingChanged, "Setting Priority should not be able to fail."); - - foreach (var (setting, list) in settings.Settings) - { - ec = list.Count == 1 - ? _setModSetting!.Invoke(collection, mod.DirectoryName, setting, list[0]) - : _setModSettings!.Invoke(collection, mod.DirectoryName, setting, list); - switch (ec) - { - case PenumbraApiEc.OptionGroupMissing: - sb.AppendLine($"Could not find the option group {setting} in mod {mod.Name}."); - break; - case PenumbraApiEc.OptionMissing: - sb.AppendLine($"Could not find all desired options in the option group {setting} in mod {mod.Name}."); - break; - case PenumbraApiEc.Success: - case PenumbraApiEc.NothingChanged: - break; - default: - sb.AppendLine($"Could not apply options in the option group {setting} in mod {mod.Name} for unknown reason {ec}."); - break; - } - } + if (_config.UseTemporarySettings && _setTemporaryModSettings != null) + SetModTemporary(sb, mod, settings, collection, index); + else + SetModPermanent(sb, mod, settings, collection); return sb.ToString(); } @@ -247,6 +229,103 @@ public class PenumbraService : IDisposable } } + public void RemoveAllTemporarySettings(Guid collection) + => _removeAllTemporaryModSettings?.Invoke(collection, Key); + + public void RemoveAllTemporarySettings(ObjectIndex index) + => _removeAllTemporaryModSettingsPlayer?.Invoke(index.Index, Key); + + public void ClearAllTemporarySettings() + { + if (!Available || _removeAllTemporaryModSettings == null) + return; + + var collections = _collections!.Invoke(); + foreach (var collection in collections) + RemoveAllTemporarySettings(collection.Key); + } + + private void SetModTemporary(StringBuilder sb, Mod mod, ModSettings settings, Guid collection, ObjectIndex? index) + { + var ex = settings.Remove + ? index.HasValue + ? _removeTemporaryModSettingsPlayer!.Invoke(index.Value.Index, mod.DirectoryName, Key) + : _removeTemporaryModSettings!.Invoke(collection, mod.DirectoryName, Key) + : index.HasValue + ? _setTemporaryModSettingsPlayer!.Invoke(index.Value.Index, mod.DirectoryName, settings.ForceInherit, settings.Enabled, + settings.Priority, + settings.Settings.ToDictionary(kvp => kvp.Key, kvp => (IReadOnlyList)kvp.Value), "Glamourer", Key) + : _setTemporaryModSettings!.Invoke(collection, mod.DirectoryName, settings.ForceInherit, settings.Enabled, settings.Priority, + settings.Settings.ToDictionary(kvp => kvp.Key, kvp => (IReadOnlyList)kvp.Value), "Glamourer", Key); + switch (ex) + { + case PenumbraApiEc.InvalidArgument: + sb.Append($"No actor with index {index!.Value.Index} could be identified."); + return; + case PenumbraApiEc.ModMissing: + sb.Append($"The mod {mod.Name} [{mod.DirectoryName}] could not be found."); + return; + case PenumbraApiEc.CollectionMissing: + sb.Append($"The collection {collection} could not be found."); + return; + case PenumbraApiEc.TemporarySettingImpossible: + sb.Append($"The collection {collection} can not have settings."); + return; + case PenumbraApiEc.TemporarySettingDisallowed: + sb.Append($"The mod {mod.Name} [{mod.DirectoryName}] already has temporary settings with a different key in {collection}."); + return; + case PenumbraApiEc.OptionGroupMissing: + case PenumbraApiEc.OptionMissing: + sb.Append($"The provided settings for {mod.Name} [{mod.DirectoryName}] did not correspond to its actual options."); + return; + } + } + + private void SetModPermanent(StringBuilder sb, Mod mod, ModSettings settings, Guid collection) + { + var ec = settings.ForceInherit + ? _inheritMod!.Invoke(collection, mod.DirectoryName, true) + : _setMod!.Invoke(collection, mod.DirectoryName, settings.Enabled); + switch (ec) + { + case PenumbraApiEc.ModMissing: + sb.Append($"The mod {mod.Name} [{mod.DirectoryName}] could not be found."); + return; + case PenumbraApiEc.CollectionMissing: + sb.Append($"The collection {collection} could not be found."); + return; + } + + if (settings.ForceInherit || !settings.Enabled) + return; + + ec = _setModPriority!.Invoke(collection, mod.DirectoryName, settings.Priority); + Debug.Assert(ec is PenumbraApiEc.Success or PenumbraApiEc.NothingChanged, "Setting Priority should not be able to fail."); + + foreach (var (setting, list) in settings.Settings) + { + ec = list.Count == 1 + ? _setModSetting!.Invoke(collection, mod.DirectoryName, setting, list[0]) + : _setModSettings!.Invoke(collection, mod.DirectoryName, setting, list); + switch (ec) + { + case PenumbraApiEc.OptionGroupMissing: + sb.AppendLine($"Could not find the option group {setting} in mod {mod.Name}."); + break; + case PenumbraApiEc.OptionMissing: + sb.AppendLine($"Could not find all desired options in the option group {setting} in mod {mod.Name}."); + break; + case PenumbraApiEc.Success: + case PenumbraApiEc.NothingChanged: + break; + default: + sb.AppendLine($"Could not apply options in the option group {setting} in mod {mod.Name} for unknown reason {ec}."); + break; + } + } + } + + /// Obtain the name of the collection currently assigned to the player. public Guid GetCurrentPlayerCollection() { @@ -347,12 +426,24 @@ public class PenumbraService : IDisposable _getMods = new global::Penumbra.Api.IpcSubscribers.GetModList(_pluginInterface); _currentCollection = new global::Penumbra.Api.IpcSubscribers.GetCollection(_pluginInterface); _getCurrentSettings = new global::Penumbra.Api.IpcSubscribers.GetCurrentModSettings(_pluginInterface); + _inheritMod = new global::Penumbra.Api.IpcSubscribers.TryInheritMod(_pluginInterface); _setMod = new global::Penumbra.Api.IpcSubscribers.TrySetMod(_pluginInterface); _setModPriority = new global::Penumbra.Api.IpcSubscribers.TrySetModPriority(_pluginInterface); _setModSetting = new global::Penumbra.Api.IpcSubscribers.TrySetModSetting(_pluginInterface); _setModSettings = new global::Penumbra.Api.IpcSubscribers.TrySetModSettings(_pluginInterface); _openModPage = new global::Penumbra.Api.IpcSubscribers.OpenMainWindow(_pluginInterface); - Available = true; + if (CurrentMinor >= RequiredPenumbraFeatureVersionTemp) + { + _setTemporaryModSettings = new global::Penumbra.Api.IpcSubscribers.SetTemporaryModSettings(_pluginInterface); + _setTemporaryModSettingsPlayer = new global::Penumbra.Api.IpcSubscribers.SetTemporaryModSettingsPlayer(_pluginInterface); + _removeTemporaryModSettings = new global::Penumbra.Api.IpcSubscribers.RemoveTemporaryModSettings(_pluginInterface); + _removeTemporaryModSettingsPlayer = new global::Penumbra.Api.IpcSubscribers.RemoveTemporaryModSettingsPlayer(_pluginInterface); + _removeAllTemporaryModSettings = new global::Penumbra.Api.IpcSubscribers.RemoveAllTemporaryModSettings(_pluginInterface); + _removeAllTemporaryModSettingsPlayer = + new global::Penumbra.Api.IpcSubscribers.RemoveAllTemporaryModSettingsPlayer(_pluginInterface); + } + + Available = true; _penumbraReloaded.Invoke(); Glamourer.Log.Debug("Glamourer attached to Penumbra."); } @@ -373,27 +464,35 @@ public class PenumbraService : IDisposable _modSettingChanged.Disable(); if (Available) { - _collectionByIdentifier = null; - _collections = null; - _redraw = null; - _drawObjectInfo = null; - _cutsceneParent = null; - _objectCollection = null; - _getMods = null; - _currentCollection = null; - _getCurrentSettings = null; - _setMod = null; - _setModPriority = null; - _setModSetting = null; - _setModSettings = null; - _openModPage = null; - Available = false; + _collectionByIdentifier = null; + _collections = null; + _redraw = null; + _drawObjectInfo = null; + _cutsceneParent = null; + _objectCollection = null; + _getMods = null; + _currentCollection = null; + _getCurrentSettings = null; + _inheritMod = null; + _setMod = null; + _setModPriority = null; + _setModSetting = null; + _setModSettings = null; + _openModPage = null; + _setTemporaryModSettings = null; + _setTemporaryModSettingsPlayer = null; + _removeTemporaryModSettings = null; + _removeTemporaryModSettingsPlayer = null; + _removeAllTemporaryModSettings = null; + _removeAllTemporaryModSettingsPlayer = null; + Available = false; Glamourer.Log.Debug("Glamourer detached from Penumbra."); } } public void Dispose() { + ClearAllTemporarySettings(); Unattach(); _tooltipSubscriber.Dispose(); _clickSubscriber.Dispose(); diff --git a/Glamourer/Services/CommandService.cs b/Glamourer/Services/CommandService.cs index cee4f57..10f68ee 100644 --- a/Glamourer/Services/CommandService.cs +++ b/Glamourer/Services/CommandService.cs @@ -691,7 +691,7 @@ public class CommandService : IDisposable, IApiService if (!applyMods || design is not Design d) return; - var (messages, appliedMods, _, name, overridden) = _modApplier.ApplyModSettings(d.AssociatedMods, actor); + var (messages, appliedMods, _, name, overridden) = _modApplier.ApplyModSettings(d.AssociatedMods, actor, d.ResetTemporarySettings); foreach (var message in messages) Glamourer.Messager.Chat.Print($"Error applying mod settings: {message}"); diff --git a/Penumbra.Api b/Penumbra.Api index 882b778..de0f281 160000 --- a/Penumbra.Api +++ b/Penumbra.Api @@ -1 +1 @@ -Subproject commit 882b778e78bb0806dd7d38e8b3670ff138a84a31 +Subproject commit de0f281fbf9d8d9d3aa8463a28025d54877cde8d