From d70d6ff1b3c859f6ed988e66648f953c3e376e53 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Wed, 19 Jul 2023 18:02:40 +0200 Subject: [PATCH] Add Revert-Design for automated designs. --- Glamourer/Automation/AutoDesign.cs | 23 +++++++- Glamourer/Automation/AutoDesignApplier.cs | 44 +++++++------- Glamourer/Automation/AutoDesignManager.cs | 58 ++++++++++--------- Glamourer/Designs/Design.cs | 1 - .../Gui/Tabs/AutomationTab/DesignCombo.cs | 36 ++++++++---- Glamourer/Gui/Tabs/DebugTab.cs | 2 +- 6 files changed, 98 insertions(+), 66 deletions(-) diff --git a/Glamourer/Automation/AutoDesign.cs b/Glamourer/Automation/AutoDesign.cs index 18deaf9..2004652 100644 --- a/Glamourer/Automation/AutoDesign.cs +++ b/Glamourer/Automation/AutoDesign.cs @@ -2,6 +2,7 @@ using Glamourer.Customization; using Glamourer.Designs; using Glamourer.Interop.Structs; +using Glamourer.State; using Glamourer.Structs; using Newtonsoft.Json.Linq; @@ -9,6 +10,8 @@ namespace Glamourer.Automation; public class AutoDesign { + public const string RevertName = "Revert"; + [Flags] public enum Type : byte { @@ -21,10 +24,19 @@ public class AutoDesign All = Armor | Accessories | Customizations | Weapons | Stains, } - public Design Design = null!; + public Design? Design; public JobGroup Jobs; public Type ApplicationType; + public string Name(bool incognito) + => Revert ? RevertName : incognito ? Design!.Incognito : Design!.Name.Text; + + public ref DesignData GetDesignData(ActorState state) + => ref Design == null ? ref state.BaseData : ref Design.DesignData; + + public bool Revert + => Design == null; + public AutoDesign Clone() => new() { @@ -39,7 +51,7 @@ public class AutoDesign public JObject Serialize() => new() { - ["Design"] = Design.Identifier.ToString(), + ["Design"] = Design?.Identifier.ToString(), ["ApplicationType"] = (uint)ApplicationType, ["Conditions"] = CreateConditionObject(), }; @@ -59,7 +71,12 @@ public class AutoDesign | (ApplicationType.HasFlag(Type.Accessories) ? AccessoryFlags : 0) | (ApplicationType.HasFlag(Type.Stains) ? StainFlags : 0); var customizeFlags = ApplicationType.HasFlag(Type.Customizations) ? CustomizeFlagExtensions.All : 0; - return (equipFlags & Design.ApplyEquip, customizeFlags & Design.ApplyCustomize, + + if (Revert) + return (equipFlags, customizeFlags, ApplicationType.HasFlag(Type.Armor), ApplicationType.HasFlag(Type.Armor), + ApplicationType.HasFlag(Type.Weapons), ApplicationType.HasFlag(Type.Customizations)); + + return (equipFlags & Design!.ApplyEquip, customizeFlags & Design.ApplyCustomize, ApplicationType.HasFlag(Type.Armor) && Design.DoApplyHatVisible(), ApplicationType.HasFlag(Type.Armor) && Design.DoApplyVisorToggle(), ApplicationType.HasFlag(Type.Weapons) && Design.DoApplyWeaponVisible(), diff --git a/Glamourer/Automation/AutoDesignApplier.cs b/Glamourer/Automation/AutoDesignApplier.cs index 56b9915..2a494bc 100644 --- a/Glamourer/Automation/AutoDesignApplier.cs +++ b/Glamourer/Automation/AutoDesignApplier.cs @@ -11,7 +11,6 @@ using Glamourer.Structs; using Glamourer.Unlocks; using Penumbra.GameData.Actors; using Penumbra.GameData.Enums; -using static System.Runtime.InteropServices.JavaScript.JSType; namespace Glamourer.Automation; @@ -206,13 +205,16 @@ public class AutoDesignApplier : IDisposable if (design.ApplicationType is 0) continue; - if (actor.AsCharacter->CharacterData.ModelCharaId != design.Design.DesignData.ModelId) + ref var data = ref design.GetDesignData(state); + var source = design.Revert ? StateChanged.Source.Game : StateChanged.Source.Fixed; + + if (actor.AsCharacter->CharacterData.ModelCharaId != data.ModelId) continue; var (equipFlags, customizeFlags, applyHat, applyVisor, applyWeapon, applyWet) = design.ApplyWhat(); - Reduce(state, in design.Design.DesignData, applyHat, applyVisor, applyWeapon, applyWet, ref totalMetaFlags, respectManual); - Reduce(state, in design.Design.DesignData, customizeFlags, ref totalCustomizeFlags, respectManual); - Reduce(state, in design.Design.DesignData, equipFlags, ref totalEquipFlags, respectManual); + Reduce(state, data, applyHat, applyVisor, applyWeapon, applyWet, ref totalMetaFlags, respectManual, source); + Reduce(state, data, customizeFlags, ref totalCustomizeFlags, respectManual, source); + Reduce(state, data, equipFlags, ref totalEquipFlags, respectManual, source); } } @@ -229,7 +231,7 @@ public class AutoDesignApplier : IDisposable return _manager.EnabledSets.TryGetValue(identifier, out set); } - private void Reduce(ActorState state, in DesignData design, EquipFlag equipFlags, ref EquipFlag totalEquipFlags, bool respectManual) + private void Reduce(ActorState state, in DesignData design, EquipFlag equipFlags, ref EquipFlag totalEquipFlags, bool respectManual, StateChanged.Source source) { equipFlags &= ~totalEquipFlags; if (equipFlags == 0) @@ -244,7 +246,7 @@ public class AutoDesignApplier : IDisposable if (!_config.UnlockedItemMode || _itemUnlocks.IsUnlocked(item.ItemId, out _)) { if (!respectManual || state[slot, false] is not StateChanged.Source.Manual) - _state.ChangeItem(state, slot, item, StateChanged.Source.Fixed); + _state.ChangeItem(state, slot, item, source); totalEquipFlags |= flag; } } @@ -253,7 +255,7 @@ public class AutoDesignApplier : IDisposable if (equipFlags.HasFlag(stainFlag)) { if (!respectManual || state[slot, true] is not StateChanged.Source.Manual) - _state.ChangeStain(state, slot, design.Stain(slot), StateChanged.Source.Fixed); + _state.ChangeStain(state, slot, design.Stain(slot), source); totalEquipFlags |= stainFlag; } } @@ -265,7 +267,7 @@ public class AutoDesignApplier : IDisposable && (!_config.UnlockedItemMode || _itemUnlocks.IsUnlocked(item.ItemId, out _))) { if (!respectManual || state[EquipSlot.MainHand, false] is not StateChanged.Source.Manual) - _state.ChangeItem(state, EquipSlot.MainHand, item, StateChanged.Source.Fixed); + _state.ChangeItem(state, EquipSlot.MainHand, item, source); totalEquipFlags |= EquipFlag.Mainhand; } } @@ -277,7 +279,7 @@ public class AutoDesignApplier : IDisposable && (!_config.UnlockedItemMode || _itemUnlocks.IsUnlocked(item.ItemId, out _))) { if (!respectManual || state[EquipSlot.OffHand, false] is not StateChanged.Source.Manual) - _state.ChangeItem(state, EquipSlot.OffHand, item, StateChanged.Source.Fixed); + _state.ChangeItem(state, EquipSlot.OffHand, item, source); totalEquipFlags |= EquipFlag.Offhand; } } @@ -285,20 +287,20 @@ public class AutoDesignApplier : IDisposable if (equipFlags.HasFlag(EquipFlag.MainhandStain)) { if (!respectManual || state[EquipSlot.MainHand, true] is not StateChanged.Source.Manual) - _state.ChangeStain(state, EquipSlot.MainHand, design.Stain(EquipSlot.MainHand), StateChanged.Source.Fixed); + _state.ChangeStain(state, EquipSlot.MainHand, design.Stain(EquipSlot.MainHand), source); totalEquipFlags |= EquipFlag.MainhandStain; } if (equipFlags.HasFlag(EquipFlag.OffhandStain)) { if (!respectManual || state[EquipSlot.OffHand, true] is not StateChanged.Source.Manual) - _state.ChangeStain(state, EquipSlot.OffHand, design.Stain(EquipSlot.OffHand), StateChanged.Source.Fixed); + _state.ChangeStain(state, EquipSlot.OffHand, design.Stain(EquipSlot.OffHand), source); totalEquipFlags |= EquipFlag.OffhandStain; } } private void Reduce(ActorState state, in DesignData design, CustomizeFlag customizeFlags, ref CustomizeFlag totalCustomizeFlags, - bool respectManual) + bool respectManual, StateChanged.Source source) { customizeFlags &= ~totalCustomizeFlags; if (customizeFlags == 0) @@ -328,12 +330,12 @@ public class AutoDesignApplier : IDisposable } if (fixFlags != 0) - _state.ChangeCustomize(state, customize, fixFlags, StateChanged.Source.Fixed); + _state.ChangeCustomize(state, customize, fixFlags, source); if (customizeFlags.HasFlag(CustomizeFlag.Face)) { if (!respectManual || state[CustomizeIndex.Face] is not StateChanged.Source.Manual) - _state.ChangeCustomize(state, CustomizeIndex.Face, design.Customize.Face, StateChanged.Source.Fixed); + _state.ChangeCustomize(state, CustomizeIndex.Face, design.Customize.Face, source); customizeFlags &= ~CustomizeFlag.Face; totalCustomizeFlags |= CustomizeFlag.Face; } @@ -351,40 +353,40 @@ public class AutoDesignApplier : IDisposable && (!_config.UnlockedItemMode || _customizeUnlocks.IsUnlocked(data.Value, out _))) { if (!respectManual || state[index] is not StateChanged.Source.Manual) - _state.ChangeCustomize(state, index, value, StateChanged.Source.Fixed); + _state.ChangeCustomize(state, index, value, source); totalCustomizeFlags |= flag; } } } private void Reduce(ActorState state, in DesignData design, bool applyHat, bool applyVisor, bool applyWeapon, bool applyWet, - ref byte totalMetaFlags, bool respectManual) + ref byte totalMetaFlags, bool respectManual, StateChanged.Source source) { if (applyHat && (totalMetaFlags & 0x01) == 0) { if (!respectManual || state[ActorState.MetaIndex.HatState] is not StateChanged.Source.Manual) - _state.ChangeHatState(state, design.IsHatVisible(), StateChanged.Source.Fixed); + _state.ChangeHatState(state, design.IsHatVisible(), source); totalMetaFlags |= 0x01; } if (applyVisor && (totalMetaFlags & 0x02) == 0) { if (!respectManual || state[ActorState.MetaIndex.VisorState] is not StateChanged.Source.Manual) - _state.ChangeVisorState(state, design.IsVisorToggled(), StateChanged.Source.Fixed); + _state.ChangeVisorState(state, design.IsVisorToggled(), source); totalMetaFlags |= 0x02; } if (applyWeapon && (totalMetaFlags & 0x04) == 0) { if (!respectManual || state[ActorState.MetaIndex.WeaponState] is not StateChanged.Source.Manual) - _state.ChangeWeaponState(state, design.IsWeaponVisible(), StateChanged.Source.Fixed); + _state.ChangeWeaponState(state, design.IsWeaponVisible(), source); totalMetaFlags |= 0x04; } if (applyWet && (totalMetaFlags & 0x08) == 0) { if (!respectManual || state[ActorState.MetaIndex.Wetness] is not StateChanged.Source.Manual) - _state.ChangeWetness(state, design.IsWet(), StateChanged.Source.Fixed); + _state.ChangeWetness(state, design.IsWet(), source); totalMetaFlags |= 0x08; } } diff --git a/Glamourer/Automation/AutoDesignManager.cs b/Glamourer/Automation/AutoDesignManager.cs index 8aa8b50..bdccb1c 100644 --- a/Glamourer/Automation/AutoDesignManager.cs +++ b/Glamourer/Automation/AutoDesignManager.cs @@ -6,7 +6,6 @@ using System.Linq; using System.Text.RegularExpressions; using Dalamud.Game.ClientState.Objects.Enums; using Dalamud.Interface.Internal.Notifications; -using Dalamud.Utility; using Glamourer.Designs; using Glamourer.Events; using Glamourer.Interop; @@ -185,7 +184,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList return; set.Enabled = value; - AutoDesignSet? oldEnabled = null; + AutoDesignSet? oldEnabled; if (value) { if (_enabled.Remove(set.Identifiers[0], out oldEnabled)) @@ -225,7 +224,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList _event.Invoke(AutomationChanged.Type.ChangedBase, set, (old, newBase)); } - public void AddDesign(AutoDesignSet set, Design design) + public void AddDesign(AutoDesignSet set, Design? design) { var newDesign = new AutoDesign() { @@ -235,7 +234,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList }; set.Designs.Add(newDesign); Save(); - Glamourer.Log.Debug($"Added new associated design {design.Identifier} as design {set.Designs.Count} to design set."); + Glamourer.Log.Debug($"Added new associated design {design?.Identifier.ToString() ?? "Reverter"} as design {set.Designs.Count} to design set."); _event.Invoke(AutomationChanged.Type.AddedDesign, set, set.Designs.Count - 1); } @@ -260,20 +259,20 @@ public class AutoDesignManager : ISavable, IReadOnlyList _event.Invoke(AutomationChanged.Type.MovedDesign, set, (from, to)); } - public void ChangeDesign(AutoDesignSet set, int which, Design newDesign) + public void ChangeDesign(AutoDesignSet set, int which, Design? newDesign) { if (which >= set.Designs.Count || which < 0) return; var design = set.Designs[which]; - if (design.Design.Identifier == newDesign.Identifier) + if (design.Design?.Identifier == newDesign?.Identifier) return; var old = design.Design; design.Design = newDesign; Save(); Glamourer.Log.Debug( - $"Changed linked design from {old.Identifier} to {newDesign.Identifier} for associated design {which + 1} in design set."); + $"Changed linked design from {old?.Identifier.ToString() ?? "Reverter"} to {newDesign?.Identifier.ToString() ?? "Reverter"} for associated design {which + 1} in design set."); _event.Invoke(AutomationChanged.Type.ChangedDesign, set, (which, old, newDesign)); } @@ -390,7 +389,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList var set = new AutoDesignSet(name, id) { - Enabled = obj["Enabled"]?.ToObject() ?? false, + Enabled = obj["Enabled"]?.ToObject() ?? false, BaseState = obj["BaseState"]?.ToObject() ?? AutoDesignSet.Base.Current, }; @@ -425,30 +424,33 @@ public class AutoDesignManager : ISavable, IReadOnlyList private AutoDesign? ToDesignObject(JObject jObj) { - var designIdentifier = jObj["Design"]?.ToObject(); - if (designIdentifier.IsNullOrEmpty()) + var designIdentifier = jObj["Design"]?.ToObject(); + Design? design = null; + // designIdentifier == null means Revert-Design. + if (designIdentifier != null) { - Glamourer.Chat.NotificationMessage("Error parsing automatically applied design: No design specified."); - return null; + if (designIdentifier.Length == 0) + { + Glamourer.Chat.NotificationMessage("Error parsing automatically applied design: No design specified."); + return null; + } + + if (!Guid.TryParse(designIdentifier, out var guid)) + { + Glamourer.Chat.NotificationMessage($"Error parsing automatically applied design: {designIdentifier} is not a valid GUID."); + + return null; + } + + design = _designs.Designs.FirstOrDefault(d => d.Identifier == guid); + if (design == null) + { + Glamourer.Chat.NotificationMessage($"Error parsing automatically applied design: The specified design {guid} does not exist."); + return null; + } } - - if (!Guid.TryParse(designIdentifier, out var guid)) - { - Glamourer.Chat.NotificationMessage($"Error parsing automatically applied design: {designIdentifier} is not a valid GUID."); - - return null; - } - - var design = _designs.Designs.FirstOrDefault(d => d.Identifier == guid); - if (design == null) - { - Glamourer.Chat.NotificationMessage($"Error parsing automatically applied design: The specified design {guid} does not exist."); - return null; - } - var applicationType = (AutoDesign.Type)(jObj["ApplicationType"]?.ToObject() ?? 0); - var ret = new AutoDesign() { Design = design, diff --git a/Glamourer/Designs/Design.cs b/Glamourer/Designs/Design.cs index 38a63ec..6bb5fbf 100644 --- a/Glamourer/Designs/Design.cs +++ b/Glamourer/Designs/Design.cs @@ -14,7 +14,6 @@ namespace Glamourer.Designs; public sealed class Design : DesignBase, ISavable { #region Data - internal Design(ItemManager items) : base(items) { } diff --git a/Glamourer/Gui/Tabs/AutomationTab/DesignCombo.cs b/Glamourer/Gui/Tabs/AutomationTab/DesignCombo.cs index 65316b8..32a76f0 100644 --- a/Glamourer/Gui/Tabs/AutomationTab/DesignCombo.cs +++ b/Glamourer/Gui/Tabs/AutomationTab/DesignCombo.cs @@ -2,6 +2,7 @@ using Glamourer.Automation; using Glamourer.Designs; using Glamourer.Events; +using Glamourer.Services; using ImGuiNET; using OtterGui; using OtterGui.Raii; @@ -11,30 +12,34 @@ namespace Glamourer.Gui.Tabs.AutomationTab; public sealed class DesignCombo : FilterComboCache { + public const int RevertDesignIndex = -1228; + public readonly Design RevertDesign; + private readonly AutoDesignManager _manager; private readonly DesignFileSystem _fileSystem; private readonly TabSelected _tabSelected; - public DesignCombo(AutoDesignManager manager, DesignManager designs, DesignFileSystem fileSystem, TabSelected tabSelected) - : base(() => designs.Designs.OrderBy(d => d.Name).ToList()) + public DesignCombo(AutoDesignManager manager, DesignManager designs, DesignFileSystem fileSystem, TabSelected tabSelected, + ItemManager items) + : base(() => designs.Designs.OrderBy(d => d.Name).Prepend(CreateRevertDesign(items)).ToList()) { _manager = manager; _fileSystem = fileSystem; _tabSelected = tabSelected; + RevertDesign = Items[0]; } protected override bool DrawSelectable(int globalIdx, bool selected) { var ret = base.DrawSelectable(globalIdx, selected); - if (_fileSystem.FindLeaf(Items[globalIdx], out var leaf)) + if (ImGui.IsItemHovered() && _fileSystem.FindLeaf(Items[globalIdx], out var leaf)) { var fullName = leaf.FullName(); if (!fullName.StartsWith(Items[globalIdx].Name)) { - using var color = ImRaii.PushColor(ImGuiCol.Text, ImGui.GetColorU32(ImGuiCol.TextDisabled)); - ImGui.SameLine(); - ImGuiUtil.RightAlign(fullName); + using var tt = ImRaii.Tooltip(); + ImGui.TextUnformatted(fullName); } } @@ -43,20 +48,20 @@ public sealed class DesignCombo : FilterComboCache public void Draw(AutoDesignSet set, AutoDesign? design, int autoDesignIndex, bool incognito) { - CurrentSelection = design?.Design ?? (Items.Count > 0 ? Items[0] : null); - CurrentSelectionIdx = design?.Design.Index ?? (Items.Count > 0 ? 0 : -1); - var name = (incognito ? CurrentSelection?.Incognito : CurrentSelection?.Name.Text) ?? string.Empty; + CurrentSelection = design?.Design ?? RevertDesign; + CurrentSelectionIdx = (design?.Design?.Index ?? -1) + 1; + var name = design?.Name(incognito) ?? string.Empty; if (Draw("##design", name, string.Empty, ImGui.GetContentRegionAvail().X, ImGui.GetTextLineHeight()) && CurrentSelection != null) { if (autoDesignIndex >= 0) - _manager.ChangeDesign(set, autoDesignIndex, CurrentSelection); + _manager.ChangeDesign(set, autoDesignIndex, CurrentSelection == RevertDesign ? null : CurrentSelection); else - _manager.AddDesign(set, CurrentSelection); + _manager.AddDesign(set, CurrentSelection == RevertDesign ? null : CurrentSelection); } - if (design != null) + if (design?.Design != null) { if (ImGui.IsItemClicked(ImGuiMouseButton.Right) && ImGui.GetIO().KeyCtrl) _tabSelected.Invoke(MainWindow.TabType.Designs, design.Design); @@ -66,4 +71,11 @@ public sealed class DesignCombo : FilterComboCache protected override string ToString(Design obj) => obj.Name.Text; + + private static Design CreateRevertDesign(ItemManager items) + => new(items) + { + Index = RevertDesignIndex, + Name = AutoDesign.RevertName, + }; } diff --git a/Glamourer/Gui/Tabs/DebugTab.cs b/Glamourer/Gui/Tabs/DebugTab.cs index 94245aa..23e19f2 100644 --- a/Glamourer/Gui/Tabs/DebugTab.cs +++ b/Glamourer/Gui/Tabs/DebugTab.cs @@ -1362,7 +1362,7 @@ public unsafe class DebugTab : ITab foreach (var (design, designIdx) in set.Designs.WithIndex()) { - ImGuiUtil.DrawTableColumn($"{design.Design.Name} ({designIdx})"); + ImGuiUtil.DrawTableColumn($"{design.Name(false)} ({designIdx})"); ImGuiUtil.DrawTableColumn($"{design.ApplicationType} {design.Jobs.Name}"); } }