diff --git a/Glamourer/Api/GlamourerIpc.Revert.cs b/Glamourer/Api/GlamourerIpc.Revert.cs index 42c0000..37c057c 100644 --- a/Glamourer/Api/GlamourerIpc.Revert.cs +++ b/Glamourer/Api/GlamourerIpc.Revert.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Plugin; +using Glamourer.Events; using Penumbra.Api.Helpers; using Penumbra.GameData.Actors; @@ -31,9 +32,7 @@ public partial class GlamourerIpc foreach (var id in actors) { if (_stateManager.TryGetValue(id, out var state)) - { - _stateManager.ResetState(state, 0xDEADBEEF); - } + _stateManager.ResetState(state, StateChanged.Source.Ipc, 0xDEADBEEF); } } } diff --git a/Glamourer/Automation/AutoDesignApplier.cs b/Glamourer/Automation/AutoDesignApplier.cs index 5341551..56b9915 100644 --- a/Glamourer/Automation/AutoDesignApplier.cs +++ b/Glamourer/Automation/AutoDesignApplier.cs @@ -116,11 +116,12 @@ public class AutoDesignApplier : IDisposable break; case AutomationChanged.Type.ChangeIdentifier when set.Enabled: // Remove fixed state from the old identifiers assigned and the old enabled set, if any. - var (oldIds, _, oldSet) = ((ActorIdentifier[], ActorIdentifier, AutoDesignSet?)) bonusData!; + var (oldIds, _, oldSet) = ((ActorIdentifier[], ActorIdentifier, AutoDesignSet?))bonusData!; RemoveOld(oldIds); ApplyNew(set); // Does not need to disable oldSet because same identifiers. break; case AutomationChanged.Type.ToggleSet: // Does not need to disable old states because same identifiers. + case AutomationChanged.Type.ChangedBase: case AutomationChanged.Type.AddedDesign: case AutomationChanged.Type.MovedDesign: case AutomationChanged.Type.ChangedDesign: @@ -193,7 +194,9 @@ public class AutoDesignApplier : IDisposable EquipFlag totalEquipFlags = 0; CustomizeFlag totalCustomizeFlags = 0; byte totalMetaFlags = 0; - if (!respectManual) + if (set.BaseState == AutoDesignSet.Base.Game) + _state.ResetState(state, StateChanged.Source.Fixed); + else if (!respectManual) state.RemoveFixedDesignSources(); foreach (var design in set.Designs) { diff --git a/Glamourer/Automation/AutoDesignManager.cs b/Glamourer/Automation/AutoDesignManager.cs index e57d022..8aa8b50 100644 --- a/Glamourer/Automation/AutoDesignManager.cs +++ b/Glamourer/Automation/AutoDesignManager.cs @@ -209,6 +209,22 @@ public class AutoDesignManager : ISavable, IReadOnlyList _event.Invoke(AutomationChanged.Type.ToggleSet, set, oldEnabled); } + public void ChangeBaseState(int whichSet, AutoDesignSet.Base newBase) + { + if (whichSet >= _data.Count || whichSet < 0) + return; + + var set = _data[whichSet]; + if (newBase == set.BaseState) + return; + + var old = set.BaseState; + set.BaseState = newBase; + Save(); + Glamourer.Log.Debug($"Changed base state of set {whichSet + 1} from {old} to {newBase}."); + _event.Invoke(AutomationChanged.Type.ChangedBase, set, (old, newBase)); + } + public void AddDesign(AutoDesignSet set, Design design) { var newDesign = new AutoDesign() @@ -375,6 +391,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList var set = new AutoDesignSet(name, id) { Enabled = obj["Enabled"]?.ToObject() ?? false, + BaseState = obj["BaseState"]?.ToObject() ?? AutoDesignSet.Base.Current, }; if (set.Enabled) diff --git a/Glamourer/Automation/AutoDesignSet.cs b/Glamourer/Automation/AutoDesignSet.cs index d08aa46..2d62bd9 100644 --- a/Glamourer/Automation/AutoDesignSet.cs +++ b/Glamourer/Automation/AutoDesignSet.cs @@ -1,4 +1,7 @@ using System.Collections.Generic; +using System.Reflection.Metadata.Ecma335; +using Glamourer.Designs; +using Glamourer.State; using Newtonsoft.Json.Linq; using Penumbra.GameData.Actors; @@ -11,6 +14,7 @@ public class AutoDesignSet public string Name; public ActorIdentifier[] Identifiers; public bool Enabled; + public Base BaseState = Base.Current; public JObject Serialize() { @@ -23,6 +27,7 @@ public class AutoDesignSet ["Name"] = Name, ["Identifier"] = Identifiers[0].ToJson(), ["Enabled"] = Enabled, + ["BaseState"] = BaseState.ToString(), ["Designs"] = list, }; } @@ -37,4 +42,10 @@ public class AutoDesignSet Identifiers = identifiers; Designs = designs; } + + public enum Base : byte + { + Current, + Game, + } } diff --git a/Glamourer/Events/AutomationChanged.cs b/Glamourer/Events/AutomationChanged.cs index ce4de43..4a35e4b 100644 --- a/Glamourer/Events/AutomationChanged.cs +++ b/Glamourer/Events/AutomationChanged.cs @@ -35,6 +35,9 @@ public sealed class AutomationChanged : EventWrapper Toggle the enabled state of a given set. Additional data is the thus disabled other set, if any [AutoDesignSet?]. ToggleSet, + /// Change the used base state of a given set. Additional data is prior and new base. [(AutoDesignSet.Base, AutoDesignSet.Base)]. + ChangedBase, + /// Add a new associated design to a given set. Additional data is the index it got added at [int]. AddedDesign, diff --git a/Glamourer/Gui/Tabs/ActorTab/ActorPanel.cs b/Glamourer/Gui/Tabs/ActorTab/ActorPanel.cs index a67afd0..cde10a7 100644 --- a/Glamourer/Gui/Tabs/ActorTab/ActorPanel.cs +++ b/Glamourer/Gui/Tabs/ActorTab/ActorPanel.cs @@ -344,7 +344,7 @@ public class ActorPanel { if (ImGuiUtil.DrawDisabledButton("Revert to Game", Vector2.Zero, "Revert the character to its actual state in the game.", _state!.IsLocked)) - _stateManager.ResetState(_state!); + _stateManager.ResetState(_state!, StateChanged.Source.Manual); ImGui.SameLine(); if (ImGuiUtil.DrawDisabledButton("Reapply State", Vector2.Zero, "Try to reapply the configured state if something went wrong.", diff --git a/Glamourer/Gui/Tabs/AutomationTab/SetPanel.cs b/Glamourer/Gui/Tabs/AutomationTab/SetPanel.cs index 62ad2d2..d0305b9 100644 --- a/Glamourer/Gui/Tabs/AutomationTab/SetPanel.cs +++ b/Glamourer/Gui/Tabs/AutomationTab/SetPanel.cs @@ -73,8 +73,17 @@ public class SetPanel if (!child || !_selector.HasSelection) return; + var enabled = Selection.Enabled; + if (ImGui.Checkbox("Enabled", ref enabled)) + _manager.SetState(_selector.SelectionIndex, enabled); + + var useGame = _selector.Selection!.BaseState is AutoDesignSet.Base.Game; + if (ImGui.Checkbox("Use Game State as Base", ref useGame)) + _manager.ChangeBaseState(_selector.SelectionIndex, useGame ? AutoDesignSet.Base.Game : AutoDesignSet.Base.Current); + var name = _tempName ?? Selection.Name; var flags = _selector.IncognitoMode ? ImGuiInputTextFlags.ReadOnly | ImGuiInputTextFlags.Password : ImGuiInputTextFlags.None; + ImGui.SetNextItemWidth(220 * ImGuiHelpers.GlobalScale); if (ImGui.InputText("##Name", ref name, 128, flags)) _tempName = name; @@ -84,11 +93,6 @@ public class SetPanel _tempName = null; } - ImGui.SameLine(); - var enabled = Selection.Enabled; - if (ImGui.Checkbox("Enabled", ref enabled)) - _manager.SetState(_selector.SelectionIndex, enabled); - ImGui.Separator(); DrawIdentifierSelection(_selector.SelectionIndex); diff --git a/Glamourer/Gui/Tabs/DebugTab.cs b/Glamourer/Gui/Tabs/DebugTab.cs index 327e135..94245aa 100644 --- a/Glamourer/Gui/Tabs/DebugTab.cs +++ b/Glamourer/Gui/Tabs/DebugTab.cs @@ -1083,7 +1083,7 @@ public unsafe class DebugTab : ITab ImGuiUtil.DrawTableColumn(state.Identifier.ToString()); ImGui.TableNextColumn(); if (ImGui.Button("Reset")) - _state.ResetState(state); + _state.ResetState(state, StateChanged.Source.Manual); ImGui.TableNextRow(); diff --git a/Glamourer/Services/CommandService.cs b/Glamourer/Services/CommandService.cs index af9dab8..d98f5a7 100644 --- a/Glamourer/Services/CommandService.cs +++ b/Glamourer/Services/CommandService.cs @@ -150,7 +150,7 @@ public class CommandService : IDisposable return false; if (_stateManager.TryGetValue(identifier, out var state)) - _stateManager.ResetState(state); + _stateManager.ResetState(state, StateChanged.Source.Manual); return true; } diff --git a/Glamourer/State/StateApplier.cs b/Glamourer/State/StateApplier.cs index c826f1e..94fa700 100644 --- a/Glamourer/State/StateApplier.cs +++ b/Glamourer/State/StateApplier.cs @@ -50,7 +50,11 @@ public class StateApplier { var data = GetData(state); if (apply) + { + state.TempLock(); ForceRedraw(data); + } + return data; } @@ -58,7 +62,7 @@ public class StateApplier /// Change the customization values of actors either by applying them via update or redrawing, /// this depends on whether the changes include changes to Race, Gender, Body Type or Face. /// - public void ChangeCustomize(ActorData data, in Customize customize) + public void ChangeCustomize(ActorData data, in Customize customize, ActorState? state = null) { foreach (var actor in data.Objects) { @@ -68,9 +72,14 @@ public class StateApplier var flags = Customize.Compare(mdl.GetCustomize(), customize); if (!flags.RequiresRedraw() || !mdl.IsHuman) + { _changeCustomize.UpdateCustomize(mdl, customize.Data); + } else + { + state?.TempLock(); _penumbra.RedrawObject(actor, RedrawType.Redraw); + } } } @@ -79,7 +88,8 @@ public class StateApplier { var data = GetData(state); if (apply) - ChangeCustomize(data, state.ModelData.Customize); + ChangeCustomize(data, state.ModelData.Customize, state); + return data; } @@ -111,6 +121,7 @@ public class StateApplier var data = GetData(state); if (apply) ChangeArmor(data, slot, state.ModelData.Armor(slot), state.ModelData.IsHatVisible()); + return data; } @@ -145,6 +156,7 @@ public class StateApplier var data = GetData(state); if (apply) ChangeStain(data, slot, state.ModelData.Stain(slot)); + return data; } @@ -164,6 +176,7 @@ public class StateApplier var data = GetData(state); if (apply) ChangeWeapon(data, slot, state.ModelData.Item(slot), state.ModelData.Stain(slot)); + return data; } @@ -183,6 +196,7 @@ public class StateApplier var data = GetData(state); if (apply) ChangeMainhand(data, state.ModelData.Item(EquipSlot.MainHand), state.ModelData.Stain(EquipSlot.MainHand)); + return data; } diff --git a/Glamourer/State/StateManager.cs b/Glamourer/State/StateManager.cs index e6a5b63..485df94 100644 --- a/Glamourer/State/StateManager.cs +++ b/Glamourer/State/StateManager.cs @@ -399,7 +399,7 @@ public class StateManager : IReadOnlyDictionary return actors; } - public void ResetState(ActorState state, uint key = 0) + public void ResetState(ActorState state, StateChanged.Source source, uint key = 0) { if (!state.Unlock(key)) return; @@ -419,10 +419,10 @@ public class StateManager : IReadOnlyDictionary foreach (var type in Enum.GetValues()) state[type] = StateChanged.Source.Game; - var actors = ApplyAll(state, redraw); + var actors = source is StateChanged.Source.Manual or StateChanged.Source.Ipc ? ApplyAll(state, redraw) : ActorData.Invalid; Glamourer.Log.Verbose( $"Reset entire state of {state.Identifier.Incognito(null)} to game base. [Affecting {actors.ToLazyString("nothing")}.]"); - _event.Invoke(StateChanged.Type.Design, state[ActorState.MetaIndex.Wetness], state, actors, null); + _event.Invoke(StateChanged.Type.Design, StateChanged.Source.Manual, state, actors, null); } public void ReapplyState(Actor actor)