mirror of
https://github.com/Ottermandias/Glamourer.git
synced 2025-12-12 18:27:24 +01:00
Add UI stuff to random designs.
This commit is contained in:
parent
2a01b328e1
commit
139508917b
18 changed files with 744 additions and 117 deletions
|
|
@ -1,4 +1,5 @@
|
|||
using Glamourer.Designs;
|
||||
using Glamourer.Designs.Special;
|
||||
using Glamourer.GameData;
|
||||
using Glamourer.Interop.Structs;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
|
@ -9,8 +10,6 @@ namespace Glamourer.Automation;
|
|||
|
||||
public class AutoDesign
|
||||
{
|
||||
public const string RevertName = "Revert";
|
||||
|
||||
public IDesignStandIn Design = new RevertDesign();
|
||||
public JobGroup Jobs;
|
||||
public ApplicationType Type;
|
||||
|
|
|
|||
|
|
@ -131,6 +131,7 @@ public sealed class AutoDesignApplier : IDisposable
|
|||
case AutomationChanged.Type.ChangedDesign:
|
||||
case AutomationChanged.Type.ChangedConditions:
|
||||
case AutomationChanged.Type.ChangedType:
|
||||
case AutomationChanged.Type.ChangedData:
|
||||
ApplyNew(set);
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using Dalamud.Game.ClientState.Objects.Enums;
|
||||
using Dalamud.Interface.Internal.Notifications;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Designs.Special;
|
||||
using Glamourer.Events;
|
||||
using Glamourer.Interop;
|
||||
using Glamourer.Services;
|
||||
|
|
@ -347,6 +348,20 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
|
|||
_event.Invoke(AutomationChanged.Type.ChangedType, set, (which, old, applicationType));
|
||||
}
|
||||
|
||||
public void ChangeData(AutoDesignSet set, int which, object data)
|
||||
{
|
||||
if (which >= set.Designs.Count || which < 0)
|
||||
return;
|
||||
|
||||
var design = set.Designs[which];
|
||||
if (!design.Design.ChangeData(data))
|
||||
return;
|
||||
|
||||
Save();
|
||||
Glamourer.Log.Debug($"Changed additional design data for associated design {which + 1} in design set.");
|
||||
_event.Invoke(AutomationChanged.Type.ChangedData, set, (which, data));
|
||||
}
|
||||
|
||||
public string ToFilename(FilenameService fileNames)
|
||||
=> fileNames.AutomationFile;
|
||||
|
||||
|
|
@ -499,6 +514,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
|
|||
|
||||
design = d;
|
||||
}
|
||||
|
||||
design.ParseData(jObj);
|
||||
|
||||
// ApplicationType is a migration from an older property name.
|
||||
|
|
|
|||
|
|
@ -81,6 +81,9 @@ public sealed class Design : DesignBase, ISavable, IDesignStandIn
|
|||
public void ParseData(JObject _)
|
||||
{ }
|
||||
|
||||
public bool ChangeData(object data)
|
||||
=> false;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Serialization
|
||||
|
|
|
|||
|
|
@ -20,4 +20,6 @@ public interface IDesignStandIn : IEquatable<IDesignStandIn>
|
|||
public void AddData(JObject jObj);
|
||||
|
||||
public void ParseData(JObject jObj);
|
||||
|
||||
public bool ChangeData(object data);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,91 +0,0 @@
|
|||
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<Design> 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<Func<string, string, string, bool>> 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<string, string, string, bool> 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<string, string, string, bool> CreatePredicateSlash(string input)
|
||||
{
|
||||
var value = input[1..].ToLowerInvariant();
|
||||
return (string nameLower, string identifier, string pathLower)
|
||||
=> pathLower.StartsWith(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@ using Glamourer.Interop.Material;
|
|||
using Glamourer.State;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Glamourer.Designs;
|
||||
namespace Glamourer.Designs.Special;
|
||||
|
||||
public class RandomDesign(RandomDesignGenerator rng) : IDesignStandIn
|
||||
{
|
||||
|
|
@ -11,14 +11,14 @@ public class RandomDesign(RandomDesignGenerator rng) : IDesignStandIn
|
|||
public const string ResolvedName = "Random";
|
||||
private Design? _currentDesign;
|
||||
|
||||
public string Restrictions { get; internal set; } = string.Empty;
|
||||
public IReadOnlyList<IDesignPredicate> Predicates { get; private set; } = [];
|
||||
|
||||
public string ResolveName(bool _)
|
||||
=> ResolvedName;
|
||||
|
||||
public ref readonly DesignData GetDesignData(in DesignData baseRef)
|
||||
{
|
||||
_currentDesign ??= rng.Design(Restrictions);
|
||||
_currentDesign ??= rng.Design(Predicates);
|
||||
if (_currentDesign == null)
|
||||
return ref baseRef;
|
||||
|
||||
|
|
@ -27,7 +27,7 @@ public class RandomDesign(RandomDesignGenerator rng) : IDesignStandIn
|
|||
|
||||
public IReadOnlyList<(uint, MaterialValueDesign)> GetMaterialData()
|
||||
{
|
||||
_currentDesign ??= rng.Design(Restrictions);
|
||||
_currentDesign ??= rng.Design(Predicates);
|
||||
if (_currentDesign == null)
|
||||
return [];
|
||||
|
||||
|
|
@ -38,7 +38,9 @@ public class RandomDesign(RandomDesignGenerator rng) : IDesignStandIn
|
|||
=> SerializedName;
|
||||
|
||||
public bool Equals(IDesignStandIn? other)
|
||||
=> other is RandomDesign r && string.Equals(r.Restrictions, Restrictions, StringComparison.OrdinalIgnoreCase);
|
||||
=> other is RandomDesign r
|
||||
&& string.Equals(RandomPredicate.GeneratePredicateString(r.Predicates), RandomPredicate.GeneratePredicateString(Predicates),
|
||||
StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
public StateSource AssociatedSource()
|
||||
=> StateSource.Manual;
|
||||
|
|
@ -47,7 +49,7 @@ public class RandomDesign(RandomDesignGenerator rng) : IDesignStandIn
|
|||
{
|
||||
get
|
||||
{
|
||||
_currentDesign = rng.Design(Restrictions);
|
||||
_currentDesign = rng.Design(Predicates);
|
||||
if (_currentDesign == null)
|
||||
yield break;
|
||||
|
||||
|
|
@ -58,12 +60,21 @@ public class RandomDesign(RandomDesignGenerator rng) : IDesignStandIn
|
|||
|
||||
public void AddData(JObject jObj)
|
||||
{
|
||||
jObj["Restrictions"] = Restrictions;
|
||||
jObj["Restrictions"] = RandomPredicate.GeneratePredicateString(Predicates);
|
||||
}
|
||||
|
||||
public void ParseData(JObject jObj)
|
||||
{
|
||||
var restrictions = jObj["Restrictions"]?.ToObject<string>() ?? string.Empty;
|
||||
Restrictions = restrictions;
|
||||
Predicates = RandomPredicate.GeneratePredicates(restrictions);
|
||||
}
|
||||
|
||||
public bool ChangeData(object data)
|
||||
{
|
||||
if (data is not List<IDesignPredicate> predicates)
|
||||
return false;
|
||||
|
||||
Predicates = predicates;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
37
Glamourer/Designs/Special/RandomDesignGenerator.cs
Normal file
37
Glamourer/Designs/Special/RandomDesignGenerator.cs
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
using OtterGui.Services;
|
||||
|
||||
namespace Glamourer.Designs.Special;
|
||||
|
||||
public class RandomDesignGenerator(DesignStorage designs, DesignFileSystem fileSystem) : IService
|
||||
{
|
||||
private readonly Random _rng = new();
|
||||
|
||||
public Design? Design(IReadOnlyList<Design> 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(IDesignPredicate predicate)
|
||||
=> Design(predicate.Get(designs, fileSystem).ToList());
|
||||
|
||||
public Design? Design(IReadOnlyList<IDesignPredicate> predicates)
|
||||
{
|
||||
if (predicates.Count == 0)
|
||||
return Design();
|
||||
if (predicates.Count == 1)
|
||||
return Design(predicates[0]);
|
||||
|
||||
return Design(IDesignPredicate.Get(predicates, designs, fileSystem).ToList());
|
||||
}
|
||||
|
||||
public Design? Design(string restrictions)
|
||||
=> Design(RandomPredicate.GeneratePredicates(restrictions));
|
||||
}
|
||||
163
Glamourer/Designs/Special/RandomPredicate.cs
Normal file
163
Glamourer/Designs/Special/RandomPredicate.cs
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
using OtterGui.Classes;
|
||||
|
||||
namespace Glamourer.Designs.Special;
|
||||
|
||||
public interface IDesignPredicate
|
||||
{
|
||||
public bool Invoke(Design design, string lowerName, string identifier, string lowerPath);
|
||||
|
||||
public bool Invoke((Design Design, string LowerName, string Identifier, string LowerPath) args)
|
||||
=> Invoke(args.Design, args.LowerName, args.Identifier, args.LowerPath);
|
||||
|
||||
public IEnumerable<Design> Get(IEnumerable<Design> designs, DesignFileSystem fileSystem)
|
||||
=> designs.Select(d => Transform(d, fileSystem))
|
||||
.Where(Invoke)
|
||||
.Select(t => t.Design);
|
||||
|
||||
public static IEnumerable<Design> Get(IReadOnlyList<IDesignPredicate> predicates, IEnumerable<Design> designs, DesignFileSystem fileSystem)
|
||||
=> predicates.Count > 0
|
||||
? designs.Select(d => Transform(d, fileSystem))
|
||||
.Where(t => predicates.Any(p => p.Invoke(t)))
|
||||
.Select(t => t.Design)
|
||||
: designs;
|
||||
|
||||
private static (Design Design, string LowerName, string Identifier, string LowerPath) Transform(Design d, DesignFileSystem fs)
|
||||
=> (d, d.Name.Lower, d.Identifier.ToString(), fs.FindLeaf(d, out var l) ? l.FullName().ToLowerInvariant() : string.Empty);
|
||||
}
|
||||
|
||||
public static class RandomPredicate
|
||||
{
|
||||
public readonly struct StartsWith(string value) : IDesignPredicate
|
||||
{
|
||||
public LowerString Value { get; } = value;
|
||||
|
||||
public bool Invoke(Design design, string lowerName, string identifier, string lowerPath)
|
||||
=> lowerPath.StartsWith(Value.Lower);
|
||||
|
||||
public override string ToString()
|
||||
=> $"/{Value.Text}";
|
||||
}
|
||||
|
||||
public readonly struct Contains(string value) : IDesignPredicate
|
||||
{
|
||||
public LowerString Value { get; } = value;
|
||||
|
||||
public bool Invoke(Design design, string lowerName, string identifier, string lowerPath)
|
||||
{
|
||||
if (lowerName.Contains(Value.Lower))
|
||||
return true;
|
||||
if (identifier.Contains(Value.Lower))
|
||||
return true;
|
||||
if (lowerPath.Contains(Value.Lower))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
=> Value.Text;
|
||||
}
|
||||
|
||||
public readonly struct Exact(Exact.Type type, string value) : IDesignPredicate
|
||||
{
|
||||
public enum Type : byte
|
||||
{
|
||||
Name,
|
||||
Path,
|
||||
Identifier,
|
||||
Tag,
|
||||
Color,
|
||||
}
|
||||
|
||||
public Type Which { get; } = type;
|
||||
public LowerString Value { get; } = value;
|
||||
|
||||
public bool Invoke(Design design, string lowerName, string identifier, string lowerPath)
|
||||
=> Which switch
|
||||
{
|
||||
Type.Name => lowerName == Value.Lower,
|
||||
Type.Path => lowerPath == Value.Lower,
|
||||
Type.Identifier => identifier == Value.Lower,
|
||||
Type.Tag => IsContained(Value, design.Tags),
|
||||
Type.Color => design.Color == Value,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
private static bool IsContained(LowerString value, IEnumerable<string> data)
|
||||
=> data.Any(t => t == value);
|
||||
|
||||
public override string ToString()
|
||||
=> $"\"{Which switch { Type.Name => 'n', Type.Identifier => 'i', Type.Path => 'p', Type.Tag => 't', Type.Color => 'c', _ => '?' }}?{Value.Text}\"";
|
||||
}
|
||||
|
||||
public static IDesignPredicate CreateSinglePredicate(string restriction)
|
||||
{
|
||||
switch (restriction[0])
|
||||
{
|
||||
case '/': return new StartsWith(restriction[1..]);
|
||||
case '"':
|
||||
var end = restriction.IndexOf('"', 1);
|
||||
if (end < 3)
|
||||
return new Contains(restriction);
|
||||
|
||||
switch (restriction[1], restriction[2])
|
||||
{
|
||||
case ('n', '?'):
|
||||
case ('N', '?'):
|
||||
return new Exact(Exact.Type.Name, restriction[3..end]);
|
||||
case ('p', '?'):
|
||||
case ('P', '?'):
|
||||
return new Exact(Exact.Type.Path, restriction[3..end]);
|
||||
case ('i', '?'):
|
||||
case ('I', '?'):
|
||||
return new Exact(Exact.Type.Identifier, restriction[3..end]);
|
||||
case ('t', '?'):
|
||||
case ('T', '?'):
|
||||
return new Exact(Exact.Type.Tag, restriction[3..end]);
|
||||
case ('c', '?'):
|
||||
case ('C', '?'):
|
||||
return new Exact(Exact.Type.Color, restriction[3..end]);
|
||||
default: return new Contains(restriction);
|
||||
}
|
||||
default: return new Contains(restriction);
|
||||
}
|
||||
}
|
||||
|
||||
public static List<IDesignPredicate> GeneratePredicates(string restrictions)
|
||||
{
|
||||
if (restrictions.Length == 0)
|
||||
return [];
|
||||
|
||||
List<IDesignPredicate> predicates = new(1);
|
||||
if (restrictions[0] is '{')
|
||||
{
|
||||
var end = restrictions.IndexOf('}');
|
||||
if (end == -1)
|
||||
{
|
||||
predicates.Add(CreateSinglePredicate(restrictions));
|
||||
}
|
||||
else
|
||||
{
|
||||
restrictions = restrictions[1..end];
|
||||
var split = restrictions.Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
||||
predicates.AddRange(split.Distinct().Select(CreateSinglePredicate));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
predicates.Add(CreateSinglePredicate(restrictions));
|
||||
}
|
||||
|
||||
return predicates;
|
||||
}
|
||||
|
||||
public static string GeneratePredicateString(IReadOnlyCollection<IDesignPredicate> predicates)
|
||||
{
|
||||
if (predicates.Count == 0)
|
||||
return string.Empty;
|
||||
if (predicates.Count == 1)
|
||||
return predicates.First()!.ToString()!;
|
||||
|
||||
return $"{{{string.Join("; ", predicates)}}}";
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@ using Glamourer.Interop.Material;
|
|||
using Glamourer.State;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Glamourer.Designs;
|
||||
namespace Glamourer.Designs.Special;
|
||||
|
||||
public class RevertDesign : IDesignStandIn
|
||||
{
|
||||
|
|
@ -38,4 +38,7 @@ public class RevertDesign : IDesignStandIn
|
|||
|
||||
public void ParseData(JObject jObj)
|
||||
{ }
|
||||
|
||||
public bool ChangeData(object data)
|
||||
=> false;
|
||||
}
|
||||
|
|
@ -46,7 +46,7 @@ public sealed class AutomationChanged()
|
|||
/// <summary> Move a given associated design in the list of a given set. Additional data is the index that got moved and the index it got moved to [(int, int)]. </summary>
|
||||
MovedDesign,
|
||||
|
||||
/// <summary> Change the linked design in an associated design for a given set. Additional data is the index of the changed associated design, the old linked design and the new linked design [(int, Design, Design)]. </summary>
|
||||
/// <summary> Change the linked design in an associated design for a given set. Additional data is the index of the changed associated design, the old linked design and the new linked design [(int, IDesignStandIn, IDesignStandIn)]. </summary>
|
||||
ChangedDesign,
|
||||
|
||||
/// <summary> Change the job condition in an associated design for a given set. Additional data is the index of the changed associated design, the old job group and the new job group [(int, JobGroup, JobGroup)]. </summary>
|
||||
|
|
@ -54,6 +54,9 @@ public sealed class AutomationChanged()
|
|||
|
||||
/// <summary> Change the application type in an associated design for a given set. Additional data is the index of the changed associated design, the old type and the new type. [(int, AutoDesign.Type, AutoDesign.Type)]. </summary>
|
||||
ChangedType,
|
||||
|
||||
/// <summary> Change the additional data for a specific design type. Additional data is the index of the changed associated design and the new data. [(int, object)] </summary>
|
||||
ChangedData,
|
||||
}
|
||||
|
||||
public enum Priority
|
||||
|
|
@ -62,6 +65,9 @@ public sealed class AutomationChanged()
|
|||
SetSelector = 0,
|
||||
|
||||
/// <seealso cref="AutoDesignApplier.OnAutomationChange"/>
|
||||
AutoDesignApplier,
|
||||
AutoDesignApplier = 0,
|
||||
|
||||
/// <seealso cref="Gui.Tabs.AutomationTab.RandomRestrictionDrawer.OnAutomationChange"/>
|
||||
RandomRestrictionDrawer = -1,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
using Dalamud.Interface.Utility.Raii;
|
||||
using Glamourer.Automation;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Designs.Special;
|
||||
using Glamourer.Events;
|
||||
using ImGuiNET;
|
||||
using OtterGui;
|
||||
|
|
@ -207,6 +208,42 @@ public sealed class LinkDesignCombo(
|
|||
.OrderBy(d => d.Item2),
|
||||
]);
|
||||
|
||||
public sealed class RandomDesignCombo(
|
||||
DesignManager designs,
|
||||
DesignFileSystem fileSystem,
|
||||
Logger log,
|
||||
DesignChanged designChanged,
|
||||
TabSelected tabSelected,
|
||||
EphemeralConfig config,
|
||||
DesignColors designColors)
|
||||
: DesignCombo(log, designChanged, tabSelected, config, designColors, () =>
|
||||
[
|
||||
.. designs.Designs
|
||||
.Select(d => new Tuple<IDesignStandIn, string>(d, fileSystem.FindLeaf(d, out var l) ? l.FullName() : string.Empty))
|
||||
.OrderBy(d => d.Item2),
|
||||
])
|
||||
{
|
||||
private Design? GetDesign(RandomPredicate.Exact exact)
|
||||
{
|
||||
return exact.Which switch
|
||||
{
|
||||
RandomPredicate.Exact.Type.Name => designs.Designs.FirstOrDefault(d => d.Name == exact.Value),
|
||||
RandomPredicate.Exact.Type.Path => fileSystem.Find(exact.Value.Text, out var c) && c is DesignFileSystem.Leaf l ? l.Value : null,
|
||||
RandomPredicate.Exact.Type.Identifier => designs.Designs.ByIdentifier(Guid.TryParse(exact.Value.Text, out var g) ? g : Guid.Empty),
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
public bool Draw(RandomPredicate.Exact exact, float width)
|
||||
{
|
||||
var design = GetDesign(exact);
|
||||
return Draw(design, design?.ResolveName(Incognito) ?? $"Not Found [{exact.Value.Text}]", width);
|
||||
}
|
||||
|
||||
public bool Draw(IDesignStandIn? design, float width)
|
||||
=> Draw(design, design?.ResolveName(Incognito) ?? string.Empty, width);
|
||||
}
|
||||
|
||||
public sealed class SpecialDesignCombo(
|
||||
DesignManager designs,
|
||||
DesignFileSystem fileSystem,
|
||||
|
|
|
|||
|
|
@ -158,16 +158,14 @@ public sealed unsafe class AdvancedDyePopup(
|
|||
if (config.KeepAdvancedDyesAttached)
|
||||
{
|
||||
var position = ImGui.GetWindowPos();
|
||||
position.X += ImGui.GetWindowSize().X;
|
||||
position.X += ImGui.GetWindowSize().X + ImGui.GetStyle().WindowPadding.X;
|
||||
ImGui.SetNextWindowPos(position);
|
||||
flags |= ImGuiWindowFlags.NoMove;
|
||||
}
|
||||
|
||||
var size = new Vector2(7 * ImGui.GetFrameHeight() + 3 * ImGui.GetStyle().ItemInnerSpacing.X + 300 * ImGuiHelpers.GlobalScale,
|
||||
18 * ImGui.GetFrameHeightWithSpacing() + ImGui.GetStyle().WindowPadding.Y + ImGui.GetStyle().ItemSpacing.Y);
|
||||
18 * ImGui.GetFrameHeightWithSpacing() + ImGui.GetStyle().WindowPadding.Y + 2 * ImGui.GetStyle().ItemSpacing.Y);
|
||||
ImGui.SetNextWindowSize(size);
|
||||
|
||||
|
||||
var window = ImGui.Begin("###Glamourer Advanced Dyes", flags);
|
||||
try
|
||||
{
|
||||
|
|
|
|||
432
Glamourer/Gui/Tabs/AutomationTab/RandomRestrictionDrawer.cs
Normal file
432
Glamourer/Gui/Tabs/AutomationTab/RandomRestrictionDrawer.cs
Normal file
|
|
@ -0,0 +1,432 @@
|
|||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.Utility;
|
||||
using Glamourer.Automation;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Designs.Special;
|
||||
using Glamourer.Events;
|
||||
using ImGuiNET;
|
||||
using OtterGui;
|
||||
using OtterGui.Raii;
|
||||
using OtterGui.Services;
|
||||
|
||||
namespace Glamourer.Gui.Tabs.AutomationTab;
|
||||
|
||||
public sealed class RandomRestrictionDrawer : IService, IDisposable
|
||||
{
|
||||
private AutoDesignSet? _set;
|
||||
private int _designIndex = -1;
|
||||
|
||||
private readonly AutomationChanged _automationChanged;
|
||||
private readonly Configuration _config;
|
||||
private readonly AutoDesignManager _autoDesignManager;
|
||||
private readonly RandomDesignCombo _randomDesignCombo;
|
||||
private readonly SetSelector _selector;
|
||||
private readonly DesignStorage _designs;
|
||||
private readonly DesignFileSystem _designFileSystem;
|
||||
|
||||
private string _newText = string.Empty;
|
||||
private string? _newDefinition;
|
||||
private Design? _newDesign = null;
|
||||
|
||||
public RandomRestrictionDrawer(AutomationChanged automationChanged, Configuration config, AutoDesignManager autoDesignManager,
|
||||
RandomDesignCombo randomDesignCombo, SetSelector selector, DesignFileSystem designFileSystem, DesignStorage designs)
|
||||
{
|
||||
_automationChanged = automationChanged;
|
||||
_config = config;
|
||||
_autoDesignManager = autoDesignManager;
|
||||
_randomDesignCombo = randomDesignCombo;
|
||||
_selector = selector;
|
||||
_designFileSystem = designFileSystem;
|
||||
_designs = designs;
|
||||
_automationChanged.Subscribe(OnAutomationChange, AutomationChanged.Priority.RandomRestrictionDrawer);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_automationChanged.Unsubscribe(OnAutomationChange);
|
||||
}
|
||||
|
||||
public void DrawButton(AutoDesignSet set, int designIndex)
|
||||
{
|
||||
var isOpen = set == _set && designIndex == _designIndex;
|
||||
using (var color = ImRaii.PushColor(ImGuiCol.Button, ImGui.GetColorU32(ImGuiCol.ButtonActive), isOpen)
|
||||
.Push(ImGuiCol.Text, ColorId.HeaderButtons.Value(), isOpen)
|
||||
.Push(ImGuiCol.Border, ColorId.HeaderButtons.Value(), isOpen))
|
||||
{
|
||||
using var frame = ImRaii.PushStyle(ImGuiStyleVar.FrameBorderSize, 2 * ImGuiHelpers.GlobalScale, isOpen);
|
||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Edit.ToIconString(), new Vector2(ImGui.GetFrameHeight()),
|
||||
string.Empty, false, true))
|
||||
{
|
||||
if (isOpen)
|
||||
Close();
|
||||
else
|
||||
Open(set, designIndex);
|
||||
}
|
||||
}
|
||||
|
||||
ImGuiUtil.HoverTooltip("Edit restrictions for this random design.");
|
||||
}
|
||||
|
||||
private void Open(AutoDesignSet set, int designIndex)
|
||||
{
|
||||
if (designIndex < 0 || designIndex >= set.Designs.Count)
|
||||
return;
|
||||
|
||||
var design = set.Designs[designIndex];
|
||||
if (design.Design is not RandomDesign)
|
||||
return;
|
||||
|
||||
_set = set;
|
||||
_designIndex = designIndex;
|
||||
}
|
||||
|
||||
private void Close()
|
||||
{
|
||||
_set = null;
|
||||
_designIndex = -1;
|
||||
}
|
||||
|
||||
public void Draw()
|
||||
{
|
||||
if (_set == null || _designIndex < 0 || _designIndex >= _set.Designs.Count)
|
||||
return;
|
||||
|
||||
if (_set != _selector.Selection)
|
||||
{
|
||||
Close();
|
||||
return;
|
||||
}
|
||||
|
||||
var design = _set.Designs[_designIndex];
|
||||
if (design.Design is not RandomDesign random)
|
||||
return;
|
||||
|
||||
DrawWindow(random);
|
||||
}
|
||||
|
||||
private void DrawWindow(RandomDesign random)
|
||||
{
|
||||
var flags = ImGuiWindowFlags.NoFocusOnAppearing
|
||||
| ImGuiWindowFlags.NoCollapse
|
||||
| ImGuiWindowFlags.NoResize;
|
||||
|
||||
// Set position to the right of the main window when attached
|
||||
// The downwards offset is implicit through child position.
|
||||
if (_config.KeepAdvancedDyesAttached)
|
||||
{
|
||||
var position = ImGui.GetWindowPos();
|
||||
position.X += ImGui.GetWindowSize().X + ImGui.GetStyle().WindowPadding.X;
|
||||
ImGui.SetNextWindowPos(position);
|
||||
flags |= ImGuiWindowFlags.NoMove;
|
||||
}
|
||||
|
||||
using var color = ImRaii.PushColor(ImGuiCol.TitleBgActive, ImGui.GetColorU32(ImGuiCol.TitleBg));
|
||||
|
||||
var size = new Vector2(7 * ImGui.GetFrameHeight() + 3 * ImGui.GetStyle().ItemInnerSpacing.X + 300 * ImGuiHelpers.GlobalScale,
|
||||
18 * ImGui.GetFrameHeightWithSpacing() + ImGui.GetStyle().WindowPadding.Y + ImGui.GetStyle().ItemSpacing.Y);
|
||||
ImGui.SetNextWindowSize(size);
|
||||
|
||||
var open = true;
|
||||
var window = ImGui.Begin($"{_set!.Name} #{_designIndex + 1:D2}###Glamourer Random Design", ref open, flags);
|
||||
try
|
||||
{
|
||||
if (window)
|
||||
DrawContent(random);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ImGui.End();
|
||||
}
|
||||
|
||||
if (!open)
|
||||
Close();
|
||||
}
|
||||
|
||||
private void DrawTable(RandomDesign random, List<IDesignPredicate> list)
|
||||
{
|
||||
using var table = ImRaii.Table("##table", 3);
|
||||
if (!table)
|
||||
return;
|
||||
|
||||
using var spacing = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, ImGui.GetStyle().ItemInnerSpacing);
|
||||
var buttonSize = new Vector2(ImGui.GetFrameHeight());
|
||||
var descWidth = ImGui.CalcTextSize("or that are set to the color").X;
|
||||
ImGui.TableSetupColumn("desc", ImGuiTableColumnFlags.WidthFixed, descWidth);
|
||||
ImGui.TableSetupColumn("input", ImGuiTableColumnFlags.WidthStretch);
|
||||
ImGui.TableSetupColumn("del", ImGuiTableColumnFlags.WidthFixed, buttonSize.X * 2 + ImGui.GetStyle().ItemInnerSpacing.X);
|
||||
|
||||
var orSize = ImGui.CalcTextSize("or ");
|
||||
for (var i = 0; i < random.Predicates.Count; ++i)
|
||||
{
|
||||
using var id = ImRaii.PushId(i);
|
||||
var predicate = random.Predicates[i];
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.AlignTextToFramePadding();
|
||||
if (i != 0)
|
||||
ImGui.TextUnformatted("or ");
|
||||
else
|
||||
ImGui.Dummy(orSize);
|
||||
ImGui.SameLine(0, 0);
|
||||
ImGui.AlignTextToFramePadding();
|
||||
switch (predicate)
|
||||
{
|
||||
case RandomPredicate.Contains contains:
|
||||
{
|
||||
ImGui.TextUnformatted("that contain");
|
||||
ImGui.TableNextColumn();
|
||||
var data = contains.Value.Text;
|
||||
ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X);
|
||||
if (ImGui.InputTextWithHint("##match", "Name, Path, or Identifier Contains...", ref data, 128))
|
||||
{
|
||||
if (data.Length == 0)
|
||||
list.RemoveAt(i);
|
||||
else
|
||||
list[i] = new RandomPredicate.Contains(data);
|
||||
_autoDesignManager.ChangeData(_set!, _designIndex, list);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case RandomPredicate.StartsWith startsWith:
|
||||
{
|
||||
ImGui.TextUnformatted("whose path starts with");
|
||||
ImGui.TableNextColumn();
|
||||
var data = startsWith.Value.Text;
|
||||
ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X);
|
||||
if (ImGui.InputTextWithHint("##startsWith", "Path Starts With...", ref data, 128))
|
||||
{
|
||||
if (data.Length == 0)
|
||||
list.RemoveAt(i);
|
||||
else
|
||||
list[i] = new RandomPredicate.StartsWith(data);
|
||||
_autoDesignManager.ChangeData(_set!, _designIndex, list);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case RandomPredicate.Exact { Which: RandomPredicate.Exact.Type.Tag } exact:
|
||||
{
|
||||
ImGui.TextUnformatted("that contain the tag");
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X);
|
||||
var data = exact.Value.Text;
|
||||
if (ImGui.InputTextWithHint("##color", "Contained tag...", ref data, 128))
|
||||
{
|
||||
if (data.Length == 0)
|
||||
list.RemoveAt(i);
|
||||
else
|
||||
list[i] = new RandomPredicate.Exact(RandomPredicate.Exact.Type.Tag, data);
|
||||
_autoDesignManager.ChangeData(_set!, _designIndex, list);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case RandomPredicate.Exact { Which: RandomPredicate.Exact.Type.Color } exact:
|
||||
{
|
||||
ImGui.TextUnformatted("that are set to the color");
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X);
|
||||
var data = exact.Value.Text;
|
||||
if (ImGui.InputTextWithHint("##color", "Assigned Color is...", ref data, 128))
|
||||
{
|
||||
if (data.Length == 0)
|
||||
list.RemoveAt(i);
|
||||
else
|
||||
list[i] = new RandomPredicate.Exact(RandomPredicate.Exact.Type.Color, data);
|
||||
_autoDesignManager.ChangeData(_set!, _designIndex, list);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case RandomPredicate.Exact exact:
|
||||
{
|
||||
ImGui.TextUnformatted("that are exactly");
|
||||
ImGui.TableNextColumn();
|
||||
if (_randomDesignCombo.Draw(exact, ImGui.GetContentRegionAvail().X) && _randomDesignCombo.Design is Design d)
|
||||
{
|
||||
list[i] = new RandomPredicate.Exact(RandomPredicate.Exact.Type.Identifier, d.Identifier.ToString());
|
||||
_autoDesignManager.ChangeData(_set!, _designIndex, list);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Trash.ToIconString(), buttonSize, "Delete this restriction.", false, true))
|
||||
{
|
||||
list.RemoveAt(i);
|
||||
_autoDesignManager.ChangeData(_set!, _designIndex, list);
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
DrawLookup(predicate, buttonSize);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawLookup(IDesignPredicate predicate, Vector2 buttonSize)
|
||||
{
|
||||
ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.MagnifyingGlassChart.ToIconString(), buttonSize, string.Empty, false, true);
|
||||
if (!ImGui.IsItemHovered())
|
||||
return;
|
||||
|
||||
var designs = predicate.Get(_designs, _designFileSystem);
|
||||
LookupTooltip(designs);
|
||||
}
|
||||
|
||||
private void LookupTooltip(IEnumerable<Design> designs)
|
||||
{
|
||||
using var _ = ImRaii.Tooltip();
|
||||
var tt = string.Join('\n', designs.Select(d => _designFileSystem.FindLeaf(d, out var l) ? l.FullName() : d.Name.Text).OrderBy(t => t));
|
||||
ImGui.TextUnformatted(tt.Length == 0
|
||||
? "Matches no currently existing designs."
|
||||
: "Matches the following designs:");
|
||||
ImGui.Separator();
|
||||
ImGui.TextUnformatted(tt);
|
||||
}
|
||||
|
||||
private void DrawNewButtons(List<IDesignPredicate> list)
|
||||
{
|
||||
ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X);
|
||||
ImGui.InputTextWithHint("##newText", "Add New Restriction...", ref _newText, 128);
|
||||
var spacing = ImGui.GetStyle().ItemInnerSpacing.X;
|
||||
var invalid = _newText.Length == 0;
|
||||
|
||||
var buttonSize = new Vector2((ImGui.GetContentRegionAvail().X - 3 * spacing) / 4, 0);
|
||||
var changed = ImGuiUtil.DrawDisabledButton("Starts With", buttonSize,
|
||||
"Add a new condition that design paths must start with the given text.", invalid)
|
||||
&& Add(new RandomPredicate.StartsWith(_newText));
|
||||
|
||||
ImGui.SameLine(0, spacing);
|
||||
changed |= ImGuiUtil.DrawDisabledButton("Contains", buttonSize,
|
||||
"Add a new condition that design paths, names or identifiers must contain the given text.", invalid)
|
||||
&& Add(new RandomPredicate.Contains(_newText));
|
||||
|
||||
ImGui.SameLine(0, spacing);
|
||||
changed |= ImGuiUtil.DrawDisabledButton("Has Tag", buttonSize,
|
||||
"Add a new condition that the design must contain the given tag.", invalid)
|
||||
&& Add(new RandomPredicate.Exact(RandomPredicate.Exact.Type.Tag, _newText));
|
||||
|
||||
ImGui.SameLine(0, spacing);
|
||||
changed |= ImGuiUtil.DrawDisabledButton("Assigned Color", buttonSize,
|
||||
"Add a new condition that the design must be assigned to the given color.", invalid)
|
||||
&& Add(new RandomPredicate.Exact(RandomPredicate.Exact.Type.Color, _newText));
|
||||
|
||||
if (_randomDesignCombo.Draw(_newDesign, ImGui.GetContentRegionAvail().X - spacing - buttonSize.X))
|
||||
_newDesign = _randomDesignCombo.CurrentSelection?.Item1 as Design;
|
||||
ImGui.SameLine(0, spacing);
|
||||
if (ImGuiUtil.DrawDisabledButton("Exact Design", buttonSize, "Add a single, specific design.", _newDesign == null))
|
||||
{
|
||||
Add(new RandomPredicate.Exact(RandomPredicate.Exact.Type.Identifier, _newDesign!.Identifier.ToString()));
|
||||
changed = true;
|
||||
_newDesign = null;
|
||||
}
|
||||
|
||||
if (changed)
|
||||
_autoDesignManager.ChangeData(_set!, _designIndex, list);
|
||||
|
||||
return;
|
||||
|
||||
bool Add(IDesignPredicate predicate)
|
||||
{
|
||||
list.Add(predicate);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawManualInput(IReadOnlyList<IDesignPredicate> list)
|
||||
{
|
||||
ImGui.Dummy(Vector2.Zero);
|
||||
ImGui.Separator();
|
||||
ImGui.Dummy(Vector2.Zero);
|
||||
DrawTotalPreview(list);
|
||||
var currentDefinition = RandomPredicate.GeneratePredicateString(list);
|
||||
var definition = _newDefinition ?? currentDefinition;
|
||||
definition = definition.Replace(";", ";\n\t").Replace("{", "{\n\t").Replace("}", "\n}");
|
||||
var lines = definition.Count(c => c is '\n');
|
||||
if (ImGui.InputTextMultiline("##definition", ref definition, 2000,
|
||||
new Vector2(ImGui.GetContentRegionAvail().X, (lines + 1) * ImGui.GetTextLineHeight() + ImGui.GetFrameHeight()),
|
||||
ImGuiInputTextFlags.CtrlEnterForNewLine))
|
||||
_newDefinition = definition;
|
||||
if (ImGui.IsItemDeactivatedAfterEdit() && _newDefinition != null && _newDefinition != currentDefinition)
|
||||
{
|
||||
var predicates = RandomPredicate.GeneratePredicates(_newDefinition.Replace("\n", string.Empty).Replace("\t", string.Empty));
|
||||
_autoDesignManager.ChangeData(_set!, _designIndex, predicates);
|
||||
_newDefinition = null;
|
||||
}
|
||||
|
||||
if (ImGui.Button("Copy to Clipboard Without Line Breaks", new Vector2(ImGui.GetContentRegionAvail().X, 0)))
|
||||
{
|
||||
try
|
||||
{
|
||||
ImGui.SetClipboardText(currentDefinition);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawTotalPreview(IReadOnlyList<IDesignPredicate> list)
|
||||
{
|
||||
var designs = IDesignPredicate.Get(list, _designs, _designFileSystem).ToList();
|
||||
var button = designs.Count > 0
|
||||
? $"All Restrictions Combined Match {designs.Count} Designs"
|
||||
: "None of the Restrictions Matches Any Designs";
|
||||
ImGuiUtil.DrawDisabledButton(button, new Vector2(ImGui.GetContentRegionAvail().X, 0),
|
||||
string.Empty, false, false);
|
||||
if (ImGui.IsItemHovered())
|
||||
LookupTooltip(designs);
|
||||
}
|
||||
|
||||
private void DrawContent(RandomDesign random)
|
||||
{
|
||||
ImGui.SetCursorPosY(ImGui.GetCursorPosY() - ImGui.GetStyle().WindowPadding.Y + ImGuiHelpers.GlobalScale);
|
||||
ImGui.Separator();
|
||||
ImGui.Dummy(Vector2.Zero);
|
||||
|
||||
var list = random.Predicates.ToList();
|
||||
if (list.Count == 0)
|
||||
{
|
||||
ImGui.TextUnformatted("No Restrictions Set. Selects among all existing Designs.");
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui.TextUnformatted("Select among designs...");
|
||||
DrawTable(random, list);
|
||||
}
|
||||
|
||||
ImGui.Dummy(Vector2.Zero);
|
||||
ImGui.Separator();
|
||||
ImGui.Dummy(Vector2.Zero);
|
||||
|
||||
DrawNewButtons(list);
|
||||
DrawManualInput(list);
|
||||
}
|
||||
|
||||
private void OnAutomationChange(AutomationChanged.Type type, AutoDesignSet? set, object? data)
|
||||
{
|
||||
if (set != _set || _set == null)
|
||||
return;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case AutomationChanged.Type.DeletedSet:
|
||||
case AutomationChanged.Type.DeletedDesign when data is int index && _designIndex == index:
|
||||
Close();
|
||||
break;
|
||||
case AutomationChanged.Type.MovedDesign when data is (int from, int to):
|
||||
if (_designIndex == from)
|
||||
_designIndex = to;
|
||||
else if (_designIndex < from && _designIndex > to)
|
||||
_designIndex++;
|
||||
else if (_designIndex > to && _designIndex < from)
|
||||
_designIndex--;
|
||||
break;
|
||||
case AutomationChanged.Type.ChangedDesign when data is (int index, IDesignStandIn _, IDesignStandIn _) && index == _designIndex:
|
||||
Close();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
using Dalamud.Interface.Utility;
|
||||
using Glamourer.Automation;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Designs.Special;
|
||||
using Glamourer.Interop;
|
||||
using Glamourer.Services;
|
||||
using Glamourer.Unlocks;
|
||||
|
|
@ -25,7 +26,8 @@ public class SetPanel(
|
|||
CustomizeUnlockManager _customizeUnlocks,
|
||||
CustomizeService _customizations,
|
||||
IdentifierDrawer _identifierDrawer,
|
||||
Configuration _config)
|
||||
Configuration _config,
|
||||
RandomRestrictionDrawer _randomDrawer)
|
||||
{
|
||||
private readonly JobGroupCombo _jobGroupCombo = new(_manager, _jobs, Glamourer.Log);
|
||||
|
||||
|
|
@ -115,6 +117,7 @@ public class SetPanel(
|
|||
ImGui.Separator();
|
||||
ImGui.Dummy(Vector2.Zero);
|
||||
DrawDesignTable();
|
||||
_randomDrawer.Draw();
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -190,6 +193,7 @@ public class SetPanel(
|
|||
ImGui.Selectable($"#{idx + 1:D2}");
|
||||
DrawDragDrop(Selection, idx);
|
||||
ImGui.TableNextColumn();
|
||||
DrawRandomEditing(Selection, design, idx);
|
||||
_designCombo.Draw(Selection, design, idx);
|
||||
DrawDragDrop(Selection, idx);
|
||||
if (singleRow)
|
||||
|
|
@ -257,6 +261,15 @@ public class SetPanel(
|
|||
}
|
||||
}
|
||||
|
||||
private void DrawRandomEditing(AutoDesignSet set, AutoDesign design, int designIdx)
|
||||
{
|
||||
if (design.Design is not RandomDesign)
|
||||
return;
|
||||
|
||||
_randomDrawer.DrawButton(set, designIdx);
|
||||
ImGui.SameLine(0, ImGui.GetStyle().ItemInnerSpacing.X);
|
||||
}
|
||||
|
||||
private void DrawWarnings(AutoDesign design)
|
||||
{
|
||||
if (design.Design is not DesignBase)
|
||||
|
|
@ -266,7 +279,7 @@ public class SetPanel(
|
|||
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))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -290,7 +290,7 @@ public class SetSelector : IDisposable
|
|||
id = _actors.CreatePlayer(ByteString.FromSpanUnsafe("New Design"u8, true, false, true), ushort.MaxValue);
|
||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Plus.ToIconString(), size,
|
||||
$"Create a new Automatic Design Set for {id}. The associated player can be changed later.", !id.IsValid, true))
|
||||
_manager.AddDesignSet("New Design", id);
|
||||
_manager.AddDesignSet("New Automation Set", id);
|
||||
}
|
||||
|
||||
private void DuplicateSetButton(Vector2 size)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ using Dalamud.Game.Text.SeStringHandling;
|
|||
using Dalamud.Plugin.Services;
|
||||
using Glamourer.Automation;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Designs.Special;
|
||||
using Glamourer.Gui;
|
||||
using Glamourer.Interop;
|
||||
using Glamourer.Interop.Penumbra;
|
||||
|
|
@ -473,12 +474,7 @@ public class CommandService : IDisposable
|
|||
_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);
|
||||
.AddText(" 》 ").AddYellow("Random").AddText(" supports many restrictions, see the Restriction Builder when adding a Random design to Automations for valid strings.").BuiltString);
|
||||
_chat.Print(new SeStringBuilder()
|
||||
.AddText(" 》 ").AddBlue("<Enable Mods>").AddText(" is optional and can be omitted (together with the ;), ").AddBlue("true")
|
||||
.AddText(" or ").AddBlue("false").AddText(".").BuiltString);
|
||||
|
|
|
|||
|
|
@ -149,6 +149,7 @@ public static class ServiceManagerA
|
|||
.AddSingleton<DesignTab>()
|
||||
.AddSingleton<QuickDesignCombo>()
|
||||
.AddSingleton<LinkDesignCombo>()
|
||||
.AddSingleton<RandomDesignCombo>()
|
||||
.AddSingleton<SpecialDesignCombo>()
|
||||
.AddSingleton<ModAssociationsTab>()
|
||||
.AddSingleton<DesignDetailTab>()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue