From 1cf0e2f70eb1932178f2d75a9dc006b871ccf6c3 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Wed, 28 Feb 2024 15:48:24 +0100 Subject: [PATCH] Add some new things, rework Revert-handling. --- Glamourer/Automation/ApplicationType.cs | 10 +- Glamourer/Automation/AutoDesign.cs | 20 +-- Glamourer/Automation/AutoDesignApplier.cs | 2 +- Glamourer/Automation/AutoDesignManager.cs | 60 ++++---- Glamourer/Automation/AutoDesignSet.cs | 2 +- Glamourer/Designs/Design.cs | 36 ++++- Glamourer/Designs/DesignColors.cs | 5 +- Glamourer/Designs/IDesignStandIn.cs | 23 ++++ Glamourer/Designs/Links/DesignMerger.cs | 21 +-- Glamourer/Designs/RandomDesign.cs | 69 ++++++++++ Glamourer/Designs/RandomDesignGenerator.cs | 91 +++++++++++++ Glamourer/Designs/RevertDesign.cs | 41 ++++++ Glamourer/Events/DesignChanged.cs | 2 +- Glamourer/Gui/DesignCombo.cs | 128 ++++++++---------- Glamourer/Gui/DesignQuickBar.cs | 8 +- Glamourer/Gui/Tabs/AutomationTab/SetPanel.cs | 14 +- .../Gui/Tabs/DebugTab/AutoDesignPanel.cs | 2 +- .../Gui/Tabs/DesignTab/DesignLinkDrawer.cs | 19 +-- Glamourer/Services/CommandService.cs | 101 +++++++++----- Glamourer/Services/ServiceManager.cs | 2 +- Penumbra.GameData | 2 +- 21 files changed, 477 insertions(+), 181 deletions(-) create mode 100644 Glamourer/Designs/IDesignStandIn.cs create mode 100644 Glamourer/Designs/RandomDesign.cs create mode 100644 Glamourer/Designs/RandomDesignGenerator.cs create mode 100644 Glamourer/Designs/RevertDesign.cs diff --git a/Glamourer/Automation/ApplicationType.cs b/Glamourer/Automation/ApplicationType.cs index 03e3a2d..12dac50 100644 --- a/Glamourer/Automation/ApplicationType.cs +++ b/Glamourer/Automation/ApplicationType.cs @@ -18,18 +18,18 @@ public enum ApplicationType : byte public static class ApplicationTypeExtensions { - public static readonly IReadOnlyList<(ApplicationType, string)> Types = new[] - { + public static readonly IReadOnlyList<(ApplicationType, string)> Types = + [ (ApplicationType.Customizations, "Apply all customization changes that are enabled in this design and that are valid in a fixed design and for the given race and gender."), (ApplicationType.Armor, "Apply all armor piece changes that are enabled in this design and that are valid in a fixed design."), (ApplicationType.Accessories, "Apply all accessory changes that are enabled in this design and that are valid in a fixed design."), (ApplicationType.GearCustomization, "Apply all dye and crest changes that are enabled in this design."), (ApplicationType.Weapons, "Apply all weapon changes that are enabled in this design and that are valid with the current weapon worn."), - }; + ]; public static (EquipFlag Equip, CustomizeFlag Customize, CrestFlag Crest, CustomizeParameterFlag Parameters, MetaFlag Meta) ApplyWhat( - this ApplicationType type, DesignBase? design) + this ApplicationType type, IDesignStandIn designStandIn) { var equipFlags = (type.HasFlag(ApplicationType.Weapons) ? WeaponFlags : 0) | (type.HasFlag(ApplicationType.Armor) ? ArmorFlags : 0) @@ -42,7 +42,7 @@ public static class ApplicationTypeExtensions | (type.HasFlag(ApplicationType.Weapons) ? MetaFlag.WeaponState : 0) | (type.HasFlag(ApplicationType.Customizations) ? MetaFlag.Wetness : 0); - if (design == null) + if (designStandIn is not DesignBase design) return (equipFlags, customizeFlags, crestFlag, parameterFlags, metaFlag); return (equipFlags & design!.ApplyEquip, customizeFlags & design.ApplyCustomize, crestFlag & design.ApplyCrest, diff --git a/Glamourer/Automation/AutoDesign.cs b/Glamourer/Automation/AutoDesign.cs index 50704f4..2a0ab7e 100644 --- a/Glamourer/Automation/AutoDesign.cs +++ b/Glamourer/Automation/AutoDesign.cs @@ -1,7 +1,6 @@ using Glamourer.Designs; using Glamourer.GameData; using Glamourer.Interop.Structs; -using Glamourer.State; using Newtonsoft.Json.Linq; using Penumbra.GameData.Enums; using Penumbra.GameData.Structs; @@ -12,20 +11,11 @@ public class AutoDesign { public const string RevertName = "Revert"; - public Design? Design; + public IDesignStandIn Design = new RevertDesign(); public JobGroup Jobs; public ApplicationType Type; public short GearsetIndex = -1; - public string Name(bool incognito) - => Revert ? RevertName : incognito ? Design!.Incognito : Design!.Name.Text; - - public ref readonly DesignData GetDesignData(ActorState state) - => ref Design == null ? ref state.BaseData : ref Design.DesignData; - - public bool Revert - => Design == null; - public AutoDesign Clone() => new() { @@ -50,12 +40,16 @@ public class AutoDesign } public JObject Serialize() - => new() + { + var ret = new JObject { - ["Design"] = Design?.Identifier.ToString(), + ["Design"] = Design.SerializeName(), ["Type"] = (uint)Type, ["Conditions"] = CreateConditionObject(), }; + Design.AddData(ret); + return ret; + } private JObject CreateConditionObject() { diff --git a/Glamourer/Automation/AutoDesignApplier.cs b/Glamourer/Automation/AutoDesignApplier.cs index b815376..332f9ee 100644 --- a/Glamourer/Automation/AutoDesignApplier.cs +++ b/Glamourer/Automation/AutoDesignApplier.cs @@ -263,7 +263,7 @@ public sealed class AutoDesignApplier : IDisposable return; 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)]), + set.Designs.Where(d => d.IsActive(actor)).SelectMany(d => d.Design.AllLinks.Select(l => (l.Design, l.Flags & d.Type))), state.ModelData.Customize, state.BaseData, true, _config.AlwaysApplyAssociatedMods); _state.ApplyDesign(state, mergedDesign, new ApplySettings(0, StateSource.Fixed, respectManual, fromJobChange, false, false, set.BaseState is AutoDesignSet.Base.Game)); } diff --git a/Glamourer/Automation/AutoDesignManager.cs b/Glamourer/Automation/AutoDesignManager.cs index e45b8e0..710336e 100644 --- a/Glamourer/Automation/AutoDesignManager.cs +++ b/Glamourer/Automation/AutoDesignManager.cs @@ -21,11 +21,12 @@ public class AutoDesignManager : ISavable, IReadOnlyList, IDispos private readonly SaveService _saveService; - private readonly JobService _jobs; - private readonly DesignManager _designs; - private readonly ActorManager _actors; - private readonly AutomationChanged _event; - private readonly DesignChanged _designEvent; + private readonly JobService _jobs; + private readonly DesignManager _designs; + private readonly ActorManager _actors; + private readonly AutomationChanged _event; + private readonly DesignChanged _designEvent; + private readonly RandomDesignGenerator _randomDesigns; private readonly List _data = []; private readonly Dictionary _enabled = []; @@ -34,14 +35,15 @@ public class AutoDesignManager : ISavable, IReadOnlyList, IDispos => _enabled; public AutoDesignManager(JobService jobs, ActorManager actors, SaveService saveService, DesignManager designs, AutomationChanged @event, - FixedDesignMigrator migrator, DesignFileSystem fileSystem, DesignChanged designEvent) + FixedDesignMigrator migrator, DesignFileSystem fileSystem, DesignChanged designEvent, RandomDesignGenerator randomDesigns) { - _jobs = jobs; - _actors = actors; - _saveService = saveService; - _designs = designs; - _event = @event; - _designEvent = designEvent; + _jobs = jobs; + _actors = actors; + _saveService = saveService; + _designs = designs; + _event = @event; + _designEvent = designEvent; + _randomDesigns = randomDesigns; _designEvent.Subscribe(OnDesignChange, DesignChanged.Priority.AutoDesignManager); Load(); migrator.ConsumeMigratedData(_actors, fileSystem, this); @@ -227,7 +229,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList, IDispos _event.Invoke(AutomationChanged.Type.ChangedBase, set, (old, newBase)); } - public void AddDesign(AutoDesignSet set, Design? design) + public void AddDesign(AutoDesignSet set, IDesignStandIn design) { var newDesign = new AutoDesign() { @@ -238,7 +240,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList, IDispos set.Designs.Add(newDesign); Save(); Glamourer.Log.Debug( - $"Added new associated design {design?.Identifier.ToString() ?? "Reverter"} as design {set.Designs.Count} to design set."); + $"Added new associated design {design.ResolveName(true)} as design {set.Designs.Count} to design set."); _event.Invoke(AutomationChanged.Type.AddedDesign, set, set.Designs.Count - 1); } @@ -278,20 +280,20 @@ public class AutoDesignManager : ISavable, IReadOnlyList, IDispos _event.Invoke(AutomationChanged.Type.MovedDesign, set, (from, to)); } - public void ChangeDesign(AutoDesignSet set, int which, Design? newDesign) + public void ChangeDesign(AutoDesignSet set, int which, IDesignStandIn newDesign) { if (which >= set.Designs.Count || which < 0) return; var design = set.Designs[which]; - if (design.Design?.Identifier == newDesign?.Identifier) + if (design.Design.Equals(newDesign)) return; var old = design.Design; design.Design = newDesign; Save(); Glamourer.Log.Debug( - $"Changed linked design from {old?.Identifier.ToString() ?? "Reverter"} to {newDesign?.Identifier.ToString() ?? "Reverter"} for associated design {which + 1} in design set."); + $"Changed linked design from {old.ResolveName(true)} to {newDesign.ResolveName(true)} for associated design {which + 1} in design set."); _event.Invoke(AutomationChanged.Type.ChangedDesign, set, (which, old, newDesign)); } @@ -450,8 +452,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList, IDispos continue; } - var design = ToDesignObject(set.Name, j); - if (design != null) + if (ToDesignObject(set.Name, j) is { } design) set.Designs.Add(design); } } @@ -459,10 +460,18 @@ public class AutoDesignManager : ISavable, IReadOnlyList, IDispos private AutoDesign? ToDesignObject(string setName, JObject jObj) { - var designIdentifier = jObj["Design"]?.ToObject(); - Design? design = null; - // designIdentifier == null means Revert-Design. - if (designIdentifier != null) + var designIdentifier = jObj["Design"]?.ToObject(); + IDesignStandIn? design; + // designIdentifier == null means Revert-Design for backwards compatibility + if (designIdentifier is null or RevertDesign.SerializedName) + { + design = new RevertDesign(); + } + else if (designIdentifier is RandomDesign.SerializedName) + { + design = new RandomDesign(_randomDesigns); + } + else { if (designIdentifier.Length == 0) { @@ -480,14 +489,17 @@ public class AutoDesignManager : ISavable, IReadOnlyList, IDispos return null; } - if (!_designs.Designs.TryGetValue(guid, out design)) + if (!_designs.Designs.TryGetValue(guid, out var d)) { Glamourer.Messager.NotificationMessage( $"Error parsing automatically applied design for set {setName}: The specified design {guid} does not exist.", NotificationType.Warning); return null; } + + design = d; } + design.ParseData(jObj); // ApplicationType is a migration from an older property name. var applicationType = (ApplicationType)(jObj["Type"]?.ToObject() ?? jObj["ApplicationType"]?.ToObject() ?? 0); diff --git a/Glamourer/Automation/AutoDesignSet.cs b/Glamourer/Automation/AutoDesignSet.cs index 29a0e2e..adaa355 100644 --- a/Glamourer/Automation/AutoDesignSet.cs +++ b/Glamourer/Automation/AutoDesignSet.cs @@ -29,7 +29,7 @@ public class AutoDesignSet(string name, ActorIdentifier[] identifiers, List()) + : this(name, identifiers, []) { } public enum Base : byte diff --git a/Glamourer/Designs/Design.cs b/Glamourer/Designs/Design.cs index 4e1e72e..e01c09e 100644 --- a/Glamourer/Designs/Design.cs +++ b/Glamourer/Designs/Design.cs @@ -1,15 +1,17 @@ using Dalamud.Interface.Internal.Notifications; using Glamourer.Automation; using Glamourer.Designs.Links; +using Glamourer.Interop.Material; using Glamourer.Interop.Penumbra; using Glamourer.Services; +using Glamourer.State; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using OtterGui.Classes; namespace Glamourer.Designs; -public sealed class Design : DesignBase, ISavable +public sealed class Design : DesignBase, ISavable, IDesignStandIn { #region Data @@ -48,8 +50,36 @@ public sealed class Design : DesignBase, ISavable public string Incognito => Identifier.ToString()[..8]; - public IEnumerable<(DesignBase? Design, ApplicationType Flags)> AllLinks - => LinkContainer.GetAllLinks(this).Select(t => ((DesignBase?)t.Link.Link, t.Link.Type)); + public IEnumerable<(IDesignStandIn Design, ApplicationType Flags)> AllLinks + => LinkContainer.GetAllLinks(this).Select(t => ((IDesignStandIn)t.Link.Link, t.Link.Type)); + + #endregion + + #region IDesignStandIn + + public string ResolveName(bool incognito) + => incognito ? Incognito : Name.Text; + + public string SerializeName() + => Identifier.ToString(); + + public ref readonly DesignData GetDesignData(in DesignData baseData) + => ref GetDesignDataRef(); + + public IReadOnlyList<(uint, MaterialValueDesign)> GetMaterialData() + => Materials; + + public bool Equals(IDesignStandIn? other) + => other is Design d && d.Identifier == Identifier; + + public StateSource AssociatedSource() + => StateSource.Manual; + + public void AddData(JObject _) + { } + + public void ParseData(JObject _) + { } #endregion diff --git a/Glamourer/Designs/DesignColors.cs b/Glamourer/Designs/DesignColors.cs index bd192be..8bc5539 100644 --- a/Glamourer/Designs/DesignColors.cs +++ b/Glamourer/Designs/DesignColors.cs @@ -149,8 +149,11 @@ public class DesignColors : ISavable, IReadOnlyDictionary Load(); } - public uint GetColor(Design design) + public uint GetColor(Design? design) { + if (design == null) + return ColorId.NormalDesign.Value(); + if (design.Color.Length == 0) return AutoColor(design); diff --git a/Glamourer/Designs/IDesignStandIn.cs b/Glamourer/Designs/IDesignStandIn.cs new file mode 100644 index 0000000..33122e2 --- /dev/null +++ b/Glamourer/Designs/IDesignStandIn.cs @@ -0,0 +1,23 @@ +using Glamourer.Automation; +using Glamourer.Interop.Material; +using Glamourer.State; +using Newtonsoft.Json.Linq; + +namespace Glamourer.Designs; + +public interface IDesignStandIn : IEquatable +{ + public string ResolveName(bool incognito); + public ref readonly DesignData GetDesignData(in DesignData baseRef); + + public IReadOnlyList<(uint, MaterialValueDesign)> GetMaterialData(); + + public string SerializeName(); + public StateSource AssociatedSource(); + + public IEnumerable<(IDesignStandIn Design, ApplicationType Flags)> AllLinks { get; } + + public void AddData(JObject jObj); + + public void ParseData(JObject jObj); +} diff --git a/Glamourer/Designs/Links/DesignMerger.cs b/Glamourer/Designs/Links/DesignMerger.cs index 6e0d7ba..ebe8aba 100644 --- a/Glamourer/Designs/Links/DesignMerger.cs +++ b/Glamourer/Designs/Links/DesignMerger.cs @@ -17,13 +17,15 @@ public class DesignMerger( ItemUnlockManager _itemUnlocks, CustomizeUnlockManager _customizeUnlocks) : IService { - public MergedDesign Merge(LinkContainer designs, in CustomizeArray currentCustomize, in DesignData baseRef, bool respectOwnership, bool modAssociations) - => Merge(designs.Select(d => ((DesignBase?) d.Link, d.Type)), currentCustomize, baseRef, respectOwnership, modAssociations); + public MergedDesign Merge(LinkContainer designs, in CustomizeArray currentCustomize, in DesignData baseRef, bool respectOwnership, + bool modAssociations) + => Merge(designs.Select(d => ((IDesignStandIn)d.Link, d.Type)), currentCustomize, baseRef, respectOwnership, modAssociations); - public MergedDesign Merge(IEnumerable<(DesignBase?, ApplicationType)> designs, in CustomizeArray currentCustomize, in DesignData baseRef, bool respectOwnership, + public MergedDesign Merge(IEnumerable<(IDesignStandIn, ApplicationType)> designs, in CustomizeArray currentCustomize, in DesignData baseRef, + bool respectOwnership, bool modAssociations) { - var ret = new MergedDesign(designManager); + var ret = new MergedDesign(designManager); ret.Design.SetCustomize(_customize, currentCustomize); CustomizeFlag fixFlags = 0; respectOwnership &= _config.UnlockedItemMode; @@ -32,8 +34,8 @@ public class DesignMerger( if (type is 0) continue; - ref readonly var data = ref design == null ? ref baseRef : ref design.GetDesignDataRef(); - var source = design == null ? StateSource.Game : StateSource.Manual; + ref readonly var data = ref design.GetDesignData(baseRef); + var source = design.AssociatedSource(); if (!data.IsHuman) continue; @@ -56,10 +58,11 @@ public class DesignMerger( } - private static void ReduceMaterials(DesignBase? design, MergedDesign ret) + private static void ReduceMaterials(IDesignStandIn designStandIn, MergedDesign ret) { - if (design == null) + if (designStandIn is not DesignBase design) return; + var materials = ret.Design.GetMaterialDataRef(); foreach (var (key, value) in design.Materials.Where(p => p.Item2.Enabled)) materials.TryAddValue(MaterialValueIndex.FromKey(key), value); @@ -243,7 +246,7 @@ public class DesignMerger( ret.Sources[CustomizeIndex.Face] = source; } - var set = _customize.Manager.GetSet(customize.Clan, customize.Gender); + var set = _customize.Manager.GetSet(customize.Clan, customize.Gender); var face = customize.Face; foreach (var index in Enum.GetValues()) { diff --git a/Glamourer/Designs/RandomDesign.cs b/Glamourer/Designs/RandomDesign.cs new file mode 100644 index 0000000..3b9ba2a --- /dev/null +++ b/Glamourer/Designs/RandomDesign.cs @@ -0,0 +1,69 @@ +using Glamourer.Automation; +using Glamourer.Interop.Material; +using Glamourer.State; +using Newtonsoft.Json.Linq; + +namespace Glamourer.Designs; + +public class RandomDesign(RandomDesignGenerator rng) : IDesignStandIn +{ + public const string SerializedName = "//Random"; + public const string ResolvedName = "Random"; + private Design? _currentDesign; + + public string Restrictions { get; internal set; } = string.Empty; + + public string ResolveName(bool _) + => ResolvedName; + + public ref readonly DesignData GetDesignData(in DesignData baseRef) + { + _currentDesign ??= rng.Design(Restrictions); + if (_currentDesign == null) + return ref baseRef; + + return ref _currentDesign.GetDesignDataRef(); + } + + public IReadOnlyList<(uint, MaterialValueDesign)> GetMaterialData() + { + _currentDesign ??= rng.Design(Restrictions); + if (_currentDesign == null) + return []; + + return _currentDesign.Materials; + } + + public string SerializeName() + => SerializedName; + + public bool Equals(IDesignStandIn? other) + => other is RandomDesign r && string.Equals(r.Restrictions, Restrictions, StringComparison.OrdinalIgnoreCase); + + public StateSource AssociatedSource() + => StateSource.Manual; + + public IEnumerable<(IDesignStandIn Design, ApplicationType Flags)> AllLinks + { + get + { + _currentDesign = rng.Design(Restrictions); + if (_currentDesign == null) + yield break; + + foreach (var (link, type) in _currentDesign.AllLinks) + yield return (link, type); + } + } + + public void AddData(JObject jObj) + { + jObj["Restrictions"] = Restrictions; + } + + public void ParseData(JObject jObj) + { + var restrictions = jObj["Restrictions"]?.ToObject() ?? string.Empty; + Restrictions = restrictions; + } +} diff --git a/Glamourer/Designs/RandomDesignGenerator.cs b/Glamourer/Designs/RandomDesignGenerator.cs new file mode 100644 index 0000000..4b8f13d --- /dev/null +++ b/Glamourer/Designs/RandomDesignGenerator.cs @@ -0,0 +1,91 @@ +using OtterGui; +using OtterGui.Services; +using System; + +namespace Glamourer.Designs; + +public class RandomDesignGenerator(DesignStorage designs, DesignFileSystem fileSystem) : IService +{ + private readonly Random _rng = new(); + + public Design? Design(IReadOnlyList localDesigns) + { + if (localDesigns.Count == 0) + return null; + + var idx = _rng.Next(0, localDesigns.Count - 1); + Glamourer.Log.Verbose($"[Random Design] Chose design {idx} out of {localDesigns.Count}: {localDesigns[idx].Incognito}."); + return localDesigns[idx]; + } + + public Design? Design() + => Design(designs); + + public Design? Design(string restrictions) + { + if (restrictions.Length == 0) + return Design(designs); + + List> predicates = []; + + switch (restrictions[0]) + { + case '{': + var end = restrictions.IndexOf('}'); + if (end == -1) + throw new ArgumentException($"The restriction group '{restrictions}' is not properly terminated."); + + restrictions = restrictions[1..end]; + var split = restrictions.Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + foreach (var item in split.Distinct()) + predicates.Add(item[0] == '/' ? CreatePredicateSlash(item) : CreatePredicate(item)); + break; + case '/': + predicates.Add(CreatePredicateSlash(restrictions)); + break; + default: + predicates.Add(CreatePredicate(restrictions)); + break; + } + + if (predicates.Count == 1) + { + var p = predicates[0]; + return Design(designs.Select(Transform).Where(t => p(t.NameLower, t.Identifier, t.PathLower)).Select(t => t.Design).ToList()); + } + + return Design(designs.Select(Transform).Where(t => predicates.Any(p => p(t.NameLower, t.Identifier, t.PathLower))).Select(t => t.Design) + .ToList()); + + (Design Design, string NameLower, string Identifier, string PathLower) Transform(Design design) + { + var name = design.Name.Lower; + var identifier = design.Identifier.ToString(); + var path = fileSystem.FindLeaf(design, out var leaf) ? leaf.FullName().ToLowerInvariant() : string.Empty; + return (design, name, identifier, path); + } + + Func CreatePredicate(string input) + { + var value = input.ToLowerInvariant(); + return (string nameLower, string identifier, string pathLower) => + { + if (nameLower.Contains(value)) + return true; + if (identifier.Contains(value)) + return true; + if (pathLower.Contains(value)) + return true; + + return false; + }; + } + + Func CreatePredicateSlash(string input) + { + var value = input[1..].ToLowerInvariant(); + return (string nameLower, string identifier, string pathLower) + => pathLower.StartsWith(value); + } + } +} diff --git a/Glamourer/Designs/RevertDesign.cs b/Glamourer/Designs/RevertDesign.cs new file mode 100644 index 0000000..28a490e --- /dev/null +++ b/Glamourer/Designs/RevertDesign.cs @@ -0,0 +1,41 @@ +using Glamourer.Automation; +using Glamourer.Interop.Material; +using Glamourer.State; +using Newtonsoft.Json.Linq; + +namespace Glamourer.Designs; + +public class RevertDesign : IDesignStandIn +{ + public const string SerializedName = "//Revert"; + public const string ResolvedName = "Revert"; + + public string ResolveName(bool _) + => ResolvedName; + + public ref readonly DesignData GetDesignData(in DesignData baseRef) + => ref baseRef; + + public IReadOnlyList<(uint, MaterialValueDesign)> GetMaterialData() + => []; + + public string SerializeName() + => SerializedName; + + public bool Equals(IDesignStandIn? other) + => other is RevertDesign; + + public StateSource AssociatedSource() + => StateSource.Game; + + public IEnumerable<(IDesignStandIn Design, ApplicationType Flags)> AllLinks + { + get { yield return (this, ApplicationType.All); } + } + + public void AddData(JObject jObj) + { } + + public void ParseData(JObject jObj) + { } +} diff --git a/Glamourer/Events/DesignChanged.cs b/Glamourer/Events/DesignChanged.cs index 70e9aa6..cd51f6d 100644 --- a/Glamourer/Events/DesignChanged.cs +++ b/Glamourer/Events/DesignChanged.cs @@ -122,7 +122,7 @@ public sealed class DesignChanged() /// DesignFileSystemSelector = -1, - /// + /// DesignCombo = -2, } } diff --git a/Glamourer/Gui/DesignCombo.cs b/Glamourer/Gui/DesignCombo.cs index d25ce66..f497f79 100644 --- a/Glamourer/Gui/DesignCombo.cs +++ b/Glamourer/Gui/DesignCombo.cs @@ -3,26 +3,24 @@ using Dalamud.Interface.Utility.Raii; using Glamourer.Automation; using Glamourer.Designs; using Glamourer.Events; -using Glamourer.Services; using ImGuiNET; using OtterGui; using OtterGui.Classes; using OtterGui.Log; using OtterGui.Widgets; -using Penumbra.GameData.Enums; namespace Glamourer.Gui; -public abstract class DesignComboBase : FilterComboCache>, IDisposable +public abstract class DesignComboBase : FilterComboCache>, IDisposable { private readonly EphemeralConfig _config; private readonly DesignChanged _designChanged; private readonly DesignColors _designColors; protected readonly TabSelected TabSelected; protected float InnerWidth; - private Design? _currentDesign; + private IDesignStandIn? _currentDesign; - protected DesignComboBase(Func>> generator, Logger log, DesignChanged designChanged, + protected DesignComboBase(Func>> generator, Logger log, DesignChanged designChanged, TabSelected tabSelected, EphemeralConfig config, DesignColors designColors) : base(generator, MouseWheelType.Control, log) { @@ -46,28 +44,34 @@ public abstract class DesignComboBase : FilterComboCache>, { var (design, path) = Items[globalIdx]; bool ret; - using (var color = ImRaii.PushColor(ImGuiCol.Text, _designColors.GetColor(design))) + if (design is Design realDesign) + { + using var color = ImRaii.PushColor(ImGuiCol.Text, _designColors.GetColor(realDesign)); + ret = base.DrawSelectable(globalIdx, selected); + + if (path.Length > 0 && realDesign.Name != path) + { + var start = ImGui.GetItemRectMin(); + var pos = start.X + ImGui.CalcTextSize(realDesign.Name).X; + var maxSize = ImGui.GetWindowPos().X + ImGui.GetWindowContentRegionMax().X; + var remainingSpace = maxSize - pos; + var requiredSize = ImGui.CalcTextSize(path).X + ImGui.GetStyle().ItemInnerSpacing.X; + var offset = remainingSpace - requiredSize; + if (ImGui.GetScrollMaxY() == 0) + offset -= ImGui.GetStyle().ItemInnerSpacing.X; + + if (offset < ImGui.GetStyle().ItemSpacing.X) + ImGuiUtil.HoverTooltip(path); + else + ImGui.GetWindowDrawList().AddText(start with { X = pos + offset }, + ImGui.GetColorU32(ImGuiCol.TextDisabled), path); + } + } + else { ret = base.DrawSelectable(globalIdx, selected); } - if (path.Length > 0 && design.Name != path) - { - var start = ImGui.GetItemRectMin(); - var pos = start.X + ImGui.CalcTextSize(design.Name).X; - var maxSize = ImGui.GetWindowPos().X + ImGui.GetWindowContentRegionMax().X; - var remainingSpace = maxSize - pos; - var requiredSize = ImGui.CalcTextSize(path).X + ImGui.GetStyle().ItemInnerSpacing.X; - var offset = remainingSpace - requiredSize; - if (ImGui.GetScrollMaxY() == 0) - offset -= ImGui.GetStyle().ItemInnerSpacing.X; - - if (offset < ImGui.GetStyle().ItemSpacing.X) - ImGuiUtil.HoverTooltip(path); - else - ImGui.GetWindowDrawList().AddText(start with { X = pos + offset }, - ImGui.GetColorU32(ImGuiCol.TextDisabled), path); - } return ret; } @@ -79,22 +83,22 @@ public abstract class DesignComboBase : FilterComboCache>, return CurrentSelectionIdx; } - protected bool Draw(Design? currentDesign, string? label, float width) + protected bool Draw(IDesignStandIn? currentDesign, string? label, float width) { _currentDesign = currentDesign; InnerWidth = 400 * ImGuiHelpers.GlobalScale; var name = label ?? "Select Design Here..."; bool ret; - using (_ = currentDesign != null ? ImRaii.PushColor(ImGuiCol.Text, _designColors.GetColor(currentDesign)) : null) + using (_ = currentDesign != null ? ImRaii.PushColor(ImGuiCol.Text, _designColors.GetColor(currentDesign as Design)) : null) { ret = Draw("##design", name, string.Empty, width, ImGui.GetTextLineHeightWithSpacing()) && CurrentSelection != null; } - if (currentDesign != null) + if (currentDesign is Design design) { if (ImGui.IsItemClicked(ImGuiMouseButton.Right) && ImGui.GetIO().KeyCtrl) - TabSelected.Invoke(MainWindow.TabType.Designs, currentDesign); + TabSelected.Invoke(MainWindow.TabType.Designs, design); ImGuiUtil.HoverTooltip("Control + Right-Click to move to design."); } @@ -102,8 +106,8 @@ public abstract class DesignComboBase : FilterComboCache>, return ret; } - protected override string ToString(Tuple obj) - => obj.Item1.Name.Text; + protected override string ToString(Tuple obj) + => obj.Item1.ResolveName(Incognito); protected override float GetFilterWidth() => InnerWidth - 2 * ImGui.GetStyle().FramePadding.X; @@ -111,7 +115,7 @@ public abstract class DesignComboBase : FilterComboCache>, protected override bool IsVisible(int globalIndex, LowerString filter) { var (design, path) = Items[globalIndex]; - return filter.IsContained(path) || design.Name.Lower.Contains(filter.Lower); + return filter.IsContained(path) || filter.IsContained(design.ResolveName(false)); } private void OnDesignChange(DesignChanged.Type type, Design design, object? data = null) @@ -151,7 +155,7 @@ public abstract class DesignComboBase : FilterComboCache>, public abstract class DesignCombo : DesignComboBase { protected DesignCombo(Logger log, DesignChanged designChanged, TabSelected tabSelected, - EphemeralConfig config, DesignColors designColors, Func>> generator) + EphemeralConfig config, DesignColors designColors, Func>> generator) : base(generator, log, designChanged, tabSelected, config, designColors) { if (Items.Count == 0) @@ -162,11 +166,11 @@ public abstract class DesignCombo : DesignComboBase base.Cleanup(); } - public Design? Design + public IDesignStandIn? Design => CurrentSelection?.Item1; public void Draw(float width) - => Draw(Design, (Incognito ? Design?.Incognito : Design?.Name.Text) ?? string.Empty, width); + => Draw(Design, Design?.ResolveName(Incognito) ?? string.Empty, width); } public sealed class QuickDesignCombo : DesignCombo @@ -182,7 +186,7 @@ public sealed class QuickDesignCombo : DesignCombo [ .. designs.Designs .Where(d => d.QuickDesign) - .Select(d => new Tuple(d, fileSystem.FindLeaf(d, out var l) ? l.FullName() : string.Empty)) + .Select(d => new Tuple(d, fileSystem.FindLeaf(d, out var l) ? l.FullName() : string.Empty)) .OrderBy(d => d.Item2), ]) => AllowMouseWheel = MouseWheelType.Unmodified; @@ -199,51 +203,35 @@ public sealed class LinkDesignCombo( : DesignCombo(log, designChanged, tabSelected, config, designColors, () => [ .. designs.Designs - .Select(d => new Tuple(d, fileSystem.FindLeaf(d, out var l) ? l.FullName() : string.Empty)) + .Select(d => new Tuple(d, fileSystem.FindLeaf(d, out var l) ? l.FullName() : string.Empty)) .OrderBy(d => d.Item2), ]); -public sealed class RevertDesignCombo : DesignComboBase +public sealed class SpecialDesignCombo( + DesignManager designs, + DesignFileSystem fileSystem, + TabSelected tabSelected, + DesignColors designColors, + Logger log, + DesignChanged designChanged, + AutoDesignManager autoDesignManager, + EphemeralConfig config, + RandomDesignGenerator rng) + : DesignComboBase(() => designs.Designs + .Select(d => new Tuple(d, fileSystem.FindLeaf(d, out var l) ? l.FullName() : string.Empty)) + .OrderBy(d => d.Item2) + .Prepend(new Tuple(new RandomDesign(rng), string.Empty)) + .Prepend(new Tuple(new RevertDesign(), string.Empty)) + .ToList(), log, designChanged, tabSelected, config, designColors) { - public const int RevertDesignIndex = -1228; - public readonly Design RevertDesign; - private readonly AutoDesignManager _autoDesignManager; - - public RevertDesignCombo(DesignManager designs, DesignFileSystem fileSystem, TabSelected tabSelected, DesignColors designColors, - ItemManager items, CustomizeService customize, Logger log, DesignChanged designChanged, AutoDesignManager autoDesignManager, - EphemeralConfig config) - : this(designs, fileSystem, tabSelected, designColors, CreateRevertDesign(customize, items), log, designChanged, autoDesignManager, - config) - { } - - private RevertDesignCombo(DesignManager designs, DesignFileSystem fileSystem, TabSelected tabSelected, DesignColors designColors, - Design revertDesign, Logger log, DesignChanged designChanged, AutoDesignManager autoDesignManager, EphemeralConfig config) - : base(() => designs.Designs - .Select(d => new Tuple(d, fileSystem.FindLeaf(d, out var l) ? l.FullName() : string.Empty)) - .OrderBy(d => d.Item2) - .Prepend(new Tuple(revertDesign, string.Empty)) - .ToList(), log, designChanged, tabSelected, config, designColors) - { - RevertDesign = revertDesign; - _autoDesignManager = autoDesignManager; - } - public void Draw(AutoDesignSet set, AutoDesign? design, int autoDesignIndex) { - if (!Draw(design?.Design, design?.Name(Incognito), ImGui.GetContentRegionAvail().X)) + if (!Draw(design?.Design, design?.Design.ResolveName(Incognito), ImGui.GetContentRegionAvail().X)) return; if (autoDesignIndex >= 0) - _autoDesignManager.ChangeDesign(set, autoDesignIndex, CurrentSelection!.Item1 == RevertDesign ? null : CurrentSelection!.Item1); + autoDesignManager.ChangeDesign(set, autoDesignIndex, CurrentSelection!.Item1); else - _autoDesignManager.AddDesign(set, CurrentSelection!.Item1 == RevertDesign ? null : CurrentSelection!.Item1); + autoDesignManager.AddDesign(set, CurrentSelection!.Item1); } - - private static Design CreateRevertDesign(CustomizeService customize, ItemManager items) - => new(customize, items) - { - Index = RevertDesignIndex, - Name = AutoDesign.RevertName, - ApplyCustomize = CustomizeFlagExtensions.AllRelevant, - }; } diff --git a/Glamourer/Gui/DesignQuickBar.cs b/Glamourer/Gui/DesignQuickBar.cs index 58bdb24..b602ee8 100644 --- a/Glamourer/Gui/DesignQuickBar.cs +++ b/Glamourer/Gui/DesignQuickBar.cs @@ -123,7 +123,7 @@ public sealed class DesignQuickBar : Window, IDisposable private void DrawApplyButton(Vector2 size) { - var design = _designCombo.Design; + var design = _designCombo.Design as Design; var available = 0; var tooltip = string.Empty; if (design == null) @@ -135,7 +135,7 @@ public sealed class DesignQuickBar : Window, IDisposable if (_playerIdentifier.IsValid && _playerData.Valid) { available |= 1; - tooltip = $"Left-Click: Apply {(_config.Ephemeral.IncognitoMode ? design.Incognito : design.Name)} to yourself."; + tooltip = $"Left-Click: Apply {design.ResolveName(_config.Ephemeral.IncognitoMode)} to yourself."; } if (_targetIdentifier.IsValid && _targetData.Valid) @@ -143,7 +143,7 @@ public sealed class DesignQuickBar : Window, IDisposable if (available != 0) tooltip += '\n'; available |= 2; - tooltip += $"Right-Click: Apply {(_config.Ephemeral.IncognitoMode ? design.Incognito : design.Name)} to {_targetIdentifier}."; + tooltip += $"Right-Click: Apply {design.ResolveName(_config.Ephemeral.IncognitoMode)} to {_targetIdentifier}."; } if (available == 0) @@ -157,7 +157,7 @@ public sealed class DesignQuickBar : Window, IDisposable if (state == null && !_stateManager.GetOrCreate(id, data.Objects[0], out state)) { - Glamourer.Messager.NotificationMessage($"Could not apply {design!.Incognito} to {id.Incognito(null)}: Failed to create state."); + Glamourer.Messager.NotificationMessage($"Could not apply {design!.ResolveName(true)} to {id.Incognito(null)}: Failed to create state."); return; } diff --git a/Glamourer/Gui/Tabs/AutomationTab/SetPanel.cs b/Glamourer/Gui/Tabs/AutomationTab/SetPanel.cs index 2f4e1d8..19d6fc5 100644 --- a/Glamourer/Gui/Tabs/AutomationTab/SetPanel.cs +++ b/Glamourer/Gui/Tabs/AutomationTab/SetPanel.cs @@ -1,6 +1,7 @@ using Dalamud.Interface; using Dalamud.Interface.Utility; using Glamourer.Automation; +using Glamourer.Designs; using Glamourer.Interop; using Glamourer.Services; using Glamourer.Unlocks; @@ -20,7 +21,7 @@ public class SetPanel( AutoDesignManager _manager, JobService _jobs, ItemUnlockManager _itemUnlocks, - RevertDesignCombo _designCombo, + SpecialDesignCombo _designCombo, CustomizeUnlockManager _customizeUnlocks, CustomizeService _customizations, IdentifierDrawer _identifierDrawer, @@ -258,21 +259,22 @@ public class SetPanel( private void DrawWarnings(AutoDesign design) { - if (design.Revert) + if (design.Design is not DesignBase) return; var size = new Vector2(ImGui.GetFrameHeight()); size.X += ImGuiHelpers.GlobalScale; var (equipFlags, customizeFlags, _, _, _) = design.ApplyWhat(); - var sb = new StringBuilder(); + var sb = new StringBuilder(); + var designData = design.Design.GetDesignData(default); foreach (var slot in EquipSlotExtensions.EqdpSlots.Append(EquipSlot.MainHand).Append(EquipSlot.OffHand)) { var flag = slot.ToFlag(); if (!equipFlags.HasFlag(flag)) continue; - var item = design.Design!.DesignData.Item(slot); + var item = designData.Item(slot); if (!_itemUnlocks.IsUnlocked(item.Id, out _)) sb.AppendLine($"{item.Name} in {slot.ToName()} slot is not unlocked. Consider obtaining it via gameplay means!"); } @@ -286,8 +288,8 @@ public class SetPanel( sb.Clear(); var sb2 = new StringBuilder(); - var customize = design.Design!.DesignData.Customize; - if (!design.Design.DesignData.IsHuman) + var customize = designData.Customize; + if (!designData.IsHuman) sb.AppendLine("The base model id can not be changed automatically to something non-human."); var set = _customizations.Manager.GetSet(customize.Clan, customize.Gender); diff --git a/Glamourer/Gui/Tabs/DebugTab/AutoDesignPanel.cs b/Glamourer/Gui/Tabs/DebugTab/AutoDesignPanel.cs index 6dd7f6e..98b7d9e 100644 --- a/Glamourer/Gui/Tabs/DebugTab/AutoDesignPanel.cs +++ b/Glamourer/Gui/Tabs/DebugTab/AutoDesignPanel.cs @@ -41,7 +41,7 @@ public class AutoDesignPanel(AutoDesignManager _autoDesignManager) : IGameDataDr foreach (var (design, designIdx) in set.Designs.WithIndex()) { - ImGuiUtil.DrawTableColumn($"{design.Name(false)} ({designIdx})"); + ImGuiUtil.DrawTableColumn($"{design.Design.ResolveName(false)} ({designIdx})"); ImGuiUtil.DrawTableColumn($"{design.Type} {design.Jobs.Name}"); } } diff --git a/Glamourer/Gui/Tabs/DesignTab/DesignLinkDrawer.cs b/Glamourer/Gui/Tabs/DesignTab/DesignLinkDrawer.cs index 32800f5..f9fcaef 100644 --- a/Glamourer/Gui/Tabs/DesignTab/DesignLinkDrawer.cs +++ b/Glamourer/Gui/Tabs/DesignTab/DesignLinkDrawer.cs @@ -152,32 +152,33 @@ public class DesignLinkDrawer(DesignLinkManager _linkManager, DesignFileSystemSe ImGui.TableNextColumn(); string ttBefore, ttAfter; bool canAddBefore, canAddAfter; - if (_combo.Design == null) + var design = _combo.Design as Design; + if (design == null) { ttAfter = ttBefore = "Select a design first."; canAddBefore = canAddAfter = false; } else { - canAddBefore = LinkContainer.CanAddLink(_selector.Selected!, _combo.Design, LinkOrder.Before, out var error); + canAddBefore = LinkContainer.CanAddLink(_selector.Selected!, design, LinkOrder.Before, out var error); ttBefore = canAddBefore - ? $"Add a link at the top of the list to {_combo.Design.Name}." - : $"Can not add a link to {_combo.Design.Name}:\n{error}"; - canAddAfter = LinkContainer.CanAddLink(_selector.Selected!, _combo.Design, LinkOrder.After, out error); + ? $"Add a link at the top of the list to {design.Name}." + : $"Can not add a link to {design.Name}:\n{error}"; + canAddAfter = LinkContainer.CanAddLink(_selector.Selected!, design, LinkOrder.After, out error); ttAfter = canAddAfter - ? $"Add a link at the bottom of the list to {_combo.Design.Name}." - : $"Can not add a link to {_combo.Design.Name}:\n{error}"; + ? $"Add a link at the bottom of the list to {design.Name}." + : $"Can not add a link to {design.Name}:\n{error}"; } if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.ArrowCircleUp.ToIconString(), buttonSize, ttBefore, !canAddBefore, true)) { - _linkManager.AddDesignLink(_selector.Selected!, _combo.Design!, LinkOrder.Before); + _linkManager.AddDesignLink(_selector.Selected!, design!, LinkOrder.Before); _linkManager.MoveDesignLink(_selector.Selected!, _selector.Selected!.Links.Before.Count - 1, LinkOrder.Before, 0, LinkOrder.Before); } ImGui.SameLine(); if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.ArrowCircleDown.ToIconString(), buttonSize, ttAfter, !canAddAfter, true)) - _linkManager.AddDesignLink(_selector.Selected!, _combo.Design!, LinkOrder.After); + _linkManager.AddDesignLink(_selector.Selected!, design!, LinkOrder.After); } private void DrawDragDrop(Design design, LinkOrder order, int index) diff --git a/Glamourer/Services/CommandService.cs b/Glamourer/Services/CommandService.cs index 287fca0..3ca684e 100644 --- a/Glamourer/Services/CommandService.cs +++ b/Glamourer/Services/CommandService.cs @@ -19,28 +19,30 @@ namespace Glamourer.Services; public class CommandService : IDisposable { + private const string RandomString = "random"; private const string MainCommandString = "/glamourer"; private const string ApplyCommandString = "/glamour"; - private readonly ICommandManager _commands; - private readonly MainWindow _mainWindow; - private readonly IChatGui _chat; - private readonly ActorManager _actors; - private readonly ObjectManager _objects; - private readonly StateManager _stateManager; - private readonly AutoDesignApplier _autoDesignApplier; - private readonly AutoDesignManager _autoDesignManager; - private readonly DesignManager _designManager; - private readonly DesignConverter _converter; - private readonly DesignFileSystem _designFileSystem; - private readonly Configuration _config; - private readonly ModSettingApplier _modApplier; - private readonly ItemManager _items; + private readonly ICommandManager _commands; + private readonly MainWindow _mainWindow; + private readonly IChatGui _chat; + private readonly ActorManager _actors; + private readonly ObjectManager _objects; + private readonly StateManager _stateManager; + private readonly AutoDesignApplier _autoDesignApplier; + private readonly AutoDesignManager _autoDesignManager; + private readonly DesignManager _designManager; + private readonly DesignConverter _converter; + private readonly DesignFileSystem _designFileSystem; + private readonly Configuration _config; + private readonly ModSettingApplier _modApplier; + private readonly ItemManager _items; + private readonly RandomDesignGenerator _randomDesign; 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, ModSettingApplier modApplier, - ItemManager items) + ItemManager items, RandomDesignGenerator randomDesign) { _commands = commands; _mainWindow = mainWindow; @@ -56,6 +58,7 @@ public class CommandService : IDisposable _config = config; _modApplier = modApplier; _items = items; + _randomDesign = randomDesign; _commands.AddHandler(MainCommandString, new CommandInfo(OnGlamourer) { HelpMessage = "Open or close the Glamourer window." }); _commands.AddHandler(ApplyCommandString, @@ -394,7 +397,8 @@ public class CommandService : IDisposable if (_items.ItemData.Primary.TryGetValue(id, out var main)) items[0] = main; } - else if (_items.ItemData.Primary.FindFirst(pair => string.Equals(pair.Value.Name, split[0], StringComparison.OrdinalIgnoreCase), out var i)) + else if (_items.ItemData.Primary.FindFirst(pair => string.Equals(pair.Value.Name, split[0], StringComparison.OrdinalIgnoreCase), + out var i)) { items[0] = i.Value; } @@ -448,7 +452,8 @@ public class CommandService : IDisposable var split = arguments.Split('|', 2, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); if (split.Length is not 2) { - _chat.Print(new SeStringBuilder().AddText("Use with /glamour apply ").AddYellow("[Design Name, Path or Identifier, or Clipboard]") + _chat.Print(new SeStringBuilder().AddText("Use with /glamour apply ") + .AddYellow("[Design Name, Path or Identifier, Random, or Clipboard]") .AddText(" | ") .AddGreen("[Character Identifier]") .AddText("; ") @@ -467,6 +472,13 @@ public class CommandService : IDisposable .BuiltString); _chat.Print(new SeStringBuilder() .AddText(" 》 Clipboard as a single word will try to apply a design string currently in your clipboard.").BuiltString); + _chat.Print(new SeStringBuilder() + .AddText(" 》 ").AddYellow("Random").AddText(" supports the following restrictions:").BuiltString); + _chat.Print(new SeStringBuilder() + .AddText(" 》》》 ").AddYellow("Random").AddText(", choosing a random design out of all your designs.").BuiltString); + _chat.Print(new SeStringBuilder().AddText(" 》》》 ").AddYellow("Random:{List of [text] or /[text]}").AddText(", containing a list of restrictions within swirly braces, separated by semicolons.").BuiltString); + _chat.Print(new SeStringBuilder().AddText(" 》》》 ").AddYellow("Random:[text]").AddText(", choosing a random design where the path, name or identifier contains 'text' (no brackets).").BuiltString); + _chat.Print(new SeStringBuilder().AddText(" 》》》 ").AddYellow("Random:/[text]").AddText(", choosing a random design where the path starts with 'text' (no brackets).").BuiltString); _chat.Print(new SeStringBuilder() .AddText(" 》 ").AddBlue("").AddText(" is optional and can be omitted (together with the ;), ").AddBlue("true") .AddText(" or ").AddBlue("false").AddText(".").BuiltString); @@ -628,30 +640,57 @@ public class CommandService : IDisposable return false; } - private bool GetDesign(string argument, [NotNullWhen(true)] out DesignBase? design, bool allowClipboard) + private bool GetDesign(string argument, [NotNullWhen(true)] out DesignBase? design, bool allowSpecial) { design = null; if (argument.Length == 0) return false; - if (allowClipboard && string.Equals("clipboard", argument, StringComparison.OrdinalIgnoreCase)) + if (allowSpecial) { - try + if (string.Equals("clipboard", argument, StringComparison.OrdinalIgnoreCase)) { - var clipboardText = ImGui.GetClipboardText(); - if (clipboardText.Length > 0) - design = _converter.FromBase64(clipboardText, true, true, out _); - } - catch - { - // ignored + try + { + var clipboardText = ImGui.GetClipboardText(); + if (clipboardText.Length > 0) + design = _converter.FromBase64(clipboardText, true, true, out _); + } + catch + { + // ignored + } + + if (design != null) + return true; + + _chat.Print(new SeStringBuilder().AddText("Your current clipboard did not contain a valid design string.").BuiltString); + return false; } - if (design != null) + if (argument.StartsWith(RandomString, StringComparison.OrdinalIgnoreCase)) + { + try + { + if (argument.Length == RandomString.Length) + design = _randomDesign.Design(); + else if (argument[RandomString.Length] == ':') + design = _randomDesign.Design(argument[(RandomString.Length + 1)..]); + if (design == null) + { + _chat.Print(new SeStringBuilder().AddText("No design matched your restrictions.").BuiltString); + return false; + } + _chat.Print($"Chose random design {((Design)design).Name}."); + } + catch (Exception ex) + { + _chat.Print(new SeStringBuilder().AddText($"Error in the restriction string: {ex.Message}").BuiltString); + return false; + } + return true; - - _chat.Print(new SeStringBuilder().AddText("Your current clipboard did not contain a valid design string.").BuiltString); - return false; + } } if (Guid.TryParse(argument, out var guid)) diff --git a/Glamourer/Services/ServiceManager.cs b/Glamourer/Services/ServiceManager.cs index aca333e..2d3507d 100644 --- a/Glamourer/Services/ServiceManager.cs +++ b/Glamourer/Services/ServiceManager.cs @@ -149,7 +149,7 @@ public static class ServiceManagerA .AddSingleton() .AddSingleton() .AddSingleton() - .AddSingleton() + .AddSingleton() .AddSingleton() .AddSingleton() .AddSingleton() diff --git a/Penumbra.GameData b/Penumbra.GameData index 44021b9..d0db2f1 160000 --- a/Penumbra.GameData +++ b/Penumbra.GameData @@ -1 +1 @@ -Subproject commit 44021b93e6901c84b739bbf4d1c6350f4486cdbf +Subproject commit d0db2f1fbc3ce26d0756da5118157e5fc723c62f