mirror of
https://github.com/Ottermandias/Glamourer.git
synced 2025-12-12 18:27:24 +01:00
Add selection designs to chat commands.
This commit is contained in:
parent
70cf21cf57
commit
71e80740f6
2 changed files with 202 additions and 101 deletions
|
|
@ -6,6 +6,7 @@ using Glamourer.Designs;
|
||||||
using Glamourer.Designs.Special;
|
using Glamourer.Designs.Special;
|
||||||
using Glamourer.GameData;
|
using Glamourer.GameData;
|
||||||
using Glamourer.Gui;
|
using Glamourer.Gui;
|
||||||
|
using Glamourer.Gui.Tabs.DesignTab;
|
||||||
using Glamourer.Interop.Penumbra;
|
using Glamourer.Interop.Penumbra;
|
||||||
using Glamourer.State;
|
using Glamourer.State;
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
|
|
@ -22,31 +23,30 @@ namespace Glamourer.Services;
|
||||||
|
|
||||||
public class CommandService : IDisposable, IApiService
|
public class CommandService : IDisposable, IApiService
|
||||||
{
|
{
|
||||||
private const string RandomString = "random";
|
|
||||||
private const string MainCommandString = "/glamourer";
|
private const string MainCommandString = "/glamourer";
|
||||||
private const string ApplyCommandString = "/glamour";
|
private const string ApplyCommandString = "/glamour";
|
||||||
|
|
||||||
private readonly ICommandManager _commands;
|
private readonly ICommandManager _commands;
|
||||||
private readonly MainWindow _mainWindow;
|
private readonly MainWindow _mainWindow;
|
||||||
private readonly IChatGui _chat;
|
private readonly IChatGui _chat;
|
||||||
private readonly ActorManager _actors;
|
private readonly ActorManager _actors;
|
||||||
private readonly ObjectManager _objects;
|
private readonly ObjectManager _objects;
|
||||||
private readonly StateManager _stateManager;
|
private readonly StateManager _stateManager;
|
||||||
private readonly AutoDesignApplier _autoDesignApplier;
|
private readonly AutoDesignApplier _autoDesignApplier;
|
||||||
private readonly AutoDesignManager _autoDesignManager;
|
private readonly AutoDesignManager _autoDesignManager;
|
||||||
private readonly DesignManager _designManager;
|
private readonly Configuration _config;
|
||||||
private readonly DesignConverter _converter;
|
private readonly ModSettingApplier _modApplier;
|
||||||
private readonly DesignFileSystem _designFileSystem;
|
private readonly ItemManager _items;
|
||||||
private readonly Configuration _config;
|
private readonly CustomizeService _customizeService;
|
||||||
private readonly ModSettingApplier _modApplier;
|
private readonly DesignManager _designManager;
|
||||||
private readonly ItemManager _items;
|
private readonly DesignConverter _converter;
|
||||||
private readonly RandomDesignGenerator _randomDesign;
|
private readonly DesignResolver _resolver;
|
||||||
private readonly CustomizeService _customizeService;
|
|
||||||
|
|
||||||
public CommandService(ICommandManager commands, MainWindow mainWindow, IChatGui chat, ActorManager actors, ObjectManager objects,
|
public CommandService(ICommandManager commands, MainWindow mainWindow, IChatGui chat, ActorManager actors, ObjectManager objects,
|
||||||
AutoDesignApplier autoDesignApplier, StateManager stateManager, DesignManager designManager, DesignConverter converter,
|
AutoDesignApplier autoDesignApplier, StateManager stateManager, DesignManager designManager, DesignConverter converter,
|
||||||
DesignFileSystem designFileSystem, AutoDesignManager autoDesignManager, Configuration config, ModSettingApplier modApplier,
|
DesignFileSystem designFileSystem, AutoDesignManager autoDesignManager, Configuration config, ModSettingApplier modApplier,
|
||||||
ItemManager items, RandomDesignGenerator randomDesign, CustomizeService customizeService)
|
ItemManager items, RandomDesignGenerator randomDesign, CustomizeService customizeService, DesignFileSystemSelector designSelector,
|
||||||
|
QuickDesignCombo quickDesignCombo, DesignResolver resolver)
|
||||||
{
|
{
|
||||||
_commands = commands;
|
_commands = commands;
|
||||||
_mainWindow = mainWindow;
|
_mainWindow = mainWindow;
|
||||||
|
|
@ -57,13 +57,12 @@ public class CommandService : IDisposable, IApiService
|
||||||
_stateManager = stateManager;
|
_stateManager = stateManager;
|
||||||
_designManager = designManager;
|
_designManager = designManager;
|
||||||
_converter = converter;
|
_converter = converter;
|
||||||
_designFileSystem = designFileSystem;
|
|
||||||
_autoDesignManager = autoDesignManager;
|
_autoDesignManager = autoDesignManager;
|
||||||
_config = config;
|
_config = config;
|
||||||
_modApplier = modApplier;
|
_modApplier = modApplier;
|
||||||
_items = items;
|
_items = items;
|
||||||
_randomDesign = randomDesign;
|
|
||||||
_customizeService = customizeService;
|
_customizeService = customizeService;
|
||||||
|
_resolver = resolver;
|
||||||
|
|
||||||
_commands.AddHandler(MainCommandString, new CommandInfo(OnGlamourer) { HelpMessage = "Open or close the Glamourer window." });
|
_commands.AddHandler(MainCommandString, new CommandInfo(OnGlamourer) { HelpMessage = "Open or close the Glamourer window." });
|
||||||
_commands.AddHandler(ApplyCommandString,
|
_commands.AddHandler(ApplyCommandString,
|
||||||
|
|
@ -611,7 +610,7 @@ public class CommandService : IDisposable, IApiService
|
||||||
if (split.Length is not 2)
|
if (split.Length is not 2)
|
||||||
{
|
{
|
||||||
_chat.Print(new SeStringBuilder().AddText("Use with /glamour apply ")
|
_chat.Print(new SeStringBuilder().AddText("Use with /glamour apply ")
|
||||||
.AddYellow("[Design Name, Path or Identifier, Random, or Clipboard]")
|
.AddYellow("[Design Name, Path or Identifier, Quick, Selection, Random, or Clipboard]")
|
||||||
.AddText(" | ")
|
.AddText(" | ")
|
||||||
.AddGreen("[Character Identifier]")
|
.AddGreen("[Character Identifier]")
|
||||||
.AddText("; ")
|
.AddText("; ")
|
||||||
|
|
@ -628,6 +627,10 @@ public class CommandService : IDisposable, IApiService
|
||||||
_chat.Print(new SeStringBuilder()
|
_chat.Print(new SeStringBuilder()
|
||||||
.AddText(" 》 The design path is the folder path in the selector, with '/' as separators. It is also case-insensitive.")
|
.AddText(" 》 The design path is the folder path in the selector, with '/' as separators. It is also case-insensitive.")
|
||||||
.BuiltString);
|
.BuiltString);
|
||||||
|
_chat.Print(new SeStringBuilder()
|
||||||
|
.AddText(" 》 Quick will use the design currently selected in the Quick Design Bar, if any.").BuiltString);
|
||||||
|
_chat.Print(new SeStringBuilder()
|
||||||
|
.AddText(" 》 Selection will use the design currently selected in the main interfaces Designs tab, if any.").BuiltString);
|
||||||
_chat.Print(new SeStringBuilder()
|
_chat.Print(new SeStringBuilder()
|
||||||
.AddText(" 》 Clipboard as a single word will try to apply a design string currently in your clipboard.").BuiltString);
|
.AddText(" 》 Clipboard as a single word will try to apply a design string currently in your clipboard.").BuiltString);
|
||||||
_chat.Print(new SeStringBuilder()
|
_chat.Print(new SeStringBuilder()
|
||||||
|
|
@ -656,7 +659,7 @@ public class CommandService : IDisposable, IApiService
|
||||||
"y" => true,
|
"y" => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
if (!GetDesign(split[0], out var design, true) || !IdentifierHandling(split2[0], out var identifiers, false, true))
|
if (!_resolver.GetDesign(split[0], out var design, true) || !IdentifierHandling(split2[0], out var identifiers, false, true))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
_objects.Update();
|
_objects.Update();
|
||||||
|
|
@ -688,7 +691,7 @@ public class CommandService : IDisposable, IApiService
|
||||||
if (!applyMods || design is not Design d)
|
if (!applyMods || design is not Design d)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var (messages, appliedMods, collection, name, overridden) = _modApplier.ApplyModSettings(d.AssociatedMods, actor);
|
var (messages, appliedMods, _, name, overridden) = _modApplier.ApplyModSettings(d.AssociatedMods, actor);
|
||||||
|
|
||||||
foreach (var message in messages)
|
foreach (var message in messages)
|
||||||
Glamourer.Messager.Chat.Print($"Error applying mod settings: {message}");
|
Glamourer.Messager.Chat.Print($"Error applying mod settings: {message}");
|
||||||
|
|
@ -717,7 +720,7 @@ public class CommandService : IDisposable, IApiService
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!GetDesign(argument, out var designBase, false) || designBase is not Design d)
|
if (!_resolver.GetDesign(argument, out var designBase, false) || designBase is not Design d)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
_designManager.Delete(d);
|
_designManager.Delete(d);
|
||||||
|
|
@ -796,81 +799,6 @@ public class CommandService : IDisposable, IApiService
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool GetDesign(string argument, [NotNullWhen(true)] out DesignBase? design, bool allowSpecial)
|
|
||||||
{
|
|
||||||
design = null;
|
|
||||||
if (argument.Length == 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (allowSpecial)
|
|
||||||
{
|
|
||||||
if (string.Equals("clipboard", argument, StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
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 (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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Guid.TryParse(argument, out var guid))
|
|
||||||
{
|
|
||||||
design = _designManager.Designs.ByIdentifier(guid);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var lower = argument.ToLowerInvariant();
|
|
||||||
design = _designManager.Designs.FirstOrDefault(d
|
|
||||||
=> d.Name.Lower == lower || lower.Length > 3 && d.Identifier.ToString().StartsWith(lower));
|
|
||||||
if (design == null && _designFileSystem.Find(lower, out var child) && child is DesignFileSystem.Leaf leaf)
|
|
||||||
design = leaf.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (design != null)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
_chat.Print(new SeStringBuilder().AddText("The token ").AddYellow(argument, true).AddText(" did not resolve to an existing design.")
|
|
||||||
.BuiltString);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private unsafe bool IdentifierHandling(string argument, out ActorIdentifier[] identifiers, bool allowAnyWorld, bool allowIndex)
|
private unsafe bool IdentifierHandling(string argument, out ActorIdentifier[] identifiers, bool allowAnyWorld, bool allowIndex)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
@ -882,7 +810,7 @@ public class CommandService : IDisposable, IApiService
|
||||||
{
|
{
|
||||||
_chat.Print(new SeStringBuilder().AddText("The placeholder ").AddGreen(argument)
|
_chat.Print(new SeStringBuilder().AddText("The placeholder ").AddGreen(argument)
|
||||||
.AddText(" did not resolve to a game object with a valid identifier.").BuiltString);
|
.AddText(" did not resolve to a game object with a valid identifier.").BuiltString);
|
||||||
identifiers = Array.Empty<ActorIdentifier>();
|
identifiers = [];
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -913,7 +841,7 @@ public class CommandService : IDisposable, IApiService
|
||||||
_chat.Print(new SeStringBuilder().AddText("The argument ").AddRed(argument, true)
|
_chat.Print(new SeStringBuilder().AddText("The argument ").AddRed(argument, true)
|
||||||
.AddText($" could not be converted to an identifier. {e.Message}")
|
.AddText($" could not be converted to an identifier. {e.Message}")
|
||||||
.BuiltString);
|
.BuiltString);
|
||||||
identifiers = Array.Empty<ActorIdentifier>();
|
identifiers = [];
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
173
Glamourer/Services/DesignResolver.cs
Normal file
173
Glamourer/Services/DesignResolver.cs
Normal file
|
|
@ -0,0 +1,173 @@
|
||||||
|
using Dalamud.Game.Text.SeStringHandling;
|
||||||
|
using Dalamud.Plugin.Services;
|
||||||
|
using Glamourer.Designs;
|
||||||
|
using Glamourer.Designs.Special;
|
||||||
|
using Glamourer.Gui;
|
||||||
|
using Glamourer.Gui.Tabs.DesignTab;
|
||||||
|
using ImGuiNET;
|
||||||
|
using OtterGui.Services;
|
||||||
|
using OtterGui.Classes;
|
||||||
|
|
||||||
|
namespace Glamourer.Services;
|
||||||
|
|
||||||
|
public class DesignResolver(
|
||||||
|
DesignFileSystemSelector designSelector,
|
||||||
|
QuickDesignCombo quickDesignCombo,
|
||||||
|
DesignConverter converter,
|
||||||
|
DesignManager manager,
|
||||||
|
DesignFileSystem designFileSystem,
|
||||||
|
RandomDesignGenerator randomDesign,
|
||||||
|
IChatGui chat) : IService
|
||||||
|
{
|
||||||
|
private const string RandomString = "random";
|
||||||
|
|
||||||
|
public bool GetDesign(string argument, [NotNullWhen(true)] out DesignBase? design, bool allowSpecial)
|
||||||
|
{
|
||||||
|
if (GetDesign(argument, out design, out var error, out var message, allowSpecial))
|
||||||
|
{
|
||||||
|
if (message != null)
|
||||||
|
chat.Print(message);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error != null)
|
||||||
|
chat.Print(error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool GetDesign(string argument, [NotNullWhen(true)] out DesignBase? design, out SeString? error, out SeString? message,
|
||||||
|
bool allowSpecial)
|
||||||
|
{
|
||||||
|
design = null;
|
||||||
|
error = null;
|
||||||
|
message = null;
|
||||||
|
|
||||||
|
if (argument.Length == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (allowSpecial)
|
||||||
|
{
|
||||||
|
if (string.Equals("selection", argument, StringComparison.OrdinalIgnoreCase))
|
||||||
|
return GetSelectedDesign(ref design, ref error);
|
||||||
|
|
||||||
|
if (string.Equals("quick", argument, StringComparison.OrdinalIgnoreCase))
|
||||||
|
return GetQuickDesign(ref design, ref error);
|
||||||
|
|
||||||
|
if (string.Equals("clipboard", argument, StringComparison.OrdinalIgnoreCase))
|
||||||
|
return GetClipboardDesign(ref design, ref error);
|
||||||
|
|
||||||
|
if (argument.StartsWith(RandomString, StringComparison.OrdinalIgnoreCase))
|
||||||
|
return GetRandomDesign(argument, ref design, ref error, ref message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetStandardDesign(argument, ref design, ref error);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public bool GetSelectedDesign(ref DesignBase? design, ref SeString? error)
|
||||||
|
{
|
||||||
|
design = designSelector.Selected;
|
||||||
|
if (design != null)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
error = "You do not have selected any design in the Designs Tab.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public bool GetQuickDesign(ref DesignBase? design, ref SeString? error)
|
||||||
|
{
|
||||||
|
design = quickDesignCombo.Design as Design;
|
||||||
|
if (design != null)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
error = "You do not have selected any design in the Quick Design Bar.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public bool GetClipboardDesign(ref DesignBase? design, ref SeString? error)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var clipboardText = ImGui.GetClipboardText();
|
||||||
|
if (clipboardText.Length > 0)
|
||||||
|
design = converter.FromBase64(clipboardText, true, true, out _);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
|
||||||
|
if (design != null)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
error = "Your current clipboard did not contain a valid design string.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public bool GetRandomDesign(string argument, ref DesignBase? design, ref SeString? error, ref SeString? message)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (argument.Length == RandomString.Length)
|
||||||
|
design = randomDesign.Design();
|
||||||
|
else if (argument[RandomString.Length] == ':')
|
||||||
|
design = randomDesign.Design(argument[(RandomString.Length + 1)..]);
|
||||||
|
if (design == null)
|
||||||
|
{
|
||||||
|
error = "No design matched your restrictions.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
message = $"Chose random design {((Design)design).Name}.";
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
error = $"Error in the restriction string: {ex.Message}";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public bool GetStandardDesign(string argument, ref DesignBase? design, ref SeString? error)
|
||||||
|
{
|
||||||
|
// As Guid
|
||||||
|
if (Guid.TryParse(argument, out var guid))
|
||||||
|
{
|
||||||
|
design = manager.Designs.ByIdentifier(guid);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var lower = argument.ToLowerInvariant();
|
||||||
|
// Search for design by name and partial identifier.
|
||||||
|
design = manager.Designs.FirstOrDefault(MatchNameAndIdentifier(lower));
|
||||||
|
// Search for design by path, if nothing was found.
|
||||||
|
if (design == null && designFileSystem.Find(lower, out var child) && child is DesignFileSystem.Leaf leaf)
|
||||||
|
design = leaf.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (design != null)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
error = new SeStringBuilder().AddText("The token ").AddYellow(argument, true).AddText(" did not resolve to an existing design.")
|
||||||
|
.BuiltString;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static Func<Design, bool> MatchNameAndIdentifier(string lower)
|
||||||
|
{
|
||||||
|
// Check for names and identifiers, prefer names
|
||||||
|
if (lower.Length > 3)
|
||||||
|
return d => d.Name.Lower == lower || d.Identifier.ToString().StartsWith(lower);
|
||||||
|
|
||||||
|
// Check only for names.
|
||||||
|
return d => d.Name.Lower == lower;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue