mirror of
https://github.com/Ottermandias/Glamourer.git
synced 2025-12-12 18:27:24 +01:00
Add option to apply mod associations with /glamour apply.
This commit is contained in:
parent
1a0a0f681f
commit
d62d7e352f
4 changed files with 82 additions and 20 deletions
|
|
@ -66,7 +66,7 @@ public sealed class Design : DesignBase, ISavable
|
||||||
["WriteProtected"] = WriteProtected(),
|
["WriteProtected"] = WriteProtected(),
|
||||||
["Equipment"] = SerializeEquipment(),
|
["Equipment"] = SerializeEquipment(),
|
||||||
["Customize"] = SerializeCustomize(),
|
["Customize"] = SerializeCustomize(),
|
||||||
["Parameters"] = SerializeParameters(),
|
["Parameters"] = SerializeParameters(),
|
||||||
["Mods"] = SerializeMods(),
|
["Mods"] = SerializeMods(),
|
||||||
};
|
};
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
||||||
|
|
@ -279,10 +279,10 @@ public class DesignBase
|
||||||
{
|
{
|
||||||
var ret = new JObject
|
var ret = new JObject
|
||||||
{
|
{
|
||||||
["FileVersion"] = FileVersion,
|
["FileVersion"] = FileVersion,
|
||||||
["Equipment"] = SerializeEquipment(),
|
["Equipment"] = SerializeEquipment(),
|
||||||
["Customize"] = SerializeCustomize(),
|
["Customize"] = SerializeCustomize(),
|
||||||
["Parameters"] = SerializeParameters(),
|
["Parameters"] = SerializeParameters(),
|
||||||
};
|
};
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -148,7 +148,8 @@ public unsafe class PenumbraService : IDisposable
|
||||||
public void OpenModPage(Mod mod)
|
public void OpenModPage(Mod mod)
|
||||||
{
|
{
|
||||||
if (_openModPage.Invoke(TabType.Mods, mod.DirectoryName, mod.Name) == PenumbraApiEc.ModMissing)
|
if (_openModPage.Invoke(TabType.Mods, mod.DirectoryName, mod.Name) == PenumbraApiEc.ModMissing)
|
||||||
Glamourer.Messager.NotificationMessage($"Could not open the mod {mod.Name}, no fitting mod was found in your Penumbra install.", NotificationType.Info, false);
|
Glamourer.Messager.NotificationMessage($"Could not open the mod {mod.Name}, no fitting mod was found in your Penumbra install.",
|
||||||
|
NotificationType.Info, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string CurrentCollection
|
public string CurrentCollection
|
||||||
|
|
@ -158,7 +159,7 @@ public unsafe class PenumbraService : IDisposable
|
||||||
/// Try to set all mod settings as desired. Only sets when the mod should be enabled.
|
/// Try to set all mod settings as desired. Only sets when the mod should be enabled.
|
||||||
/// If it is disabled, ignore all other settings.
|
/// If it is disabled, ignore all other settings.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string SetMod(Mod mod, ModSettings settings)
|
public string SetMod(Mod mod, ModSettings settings, string? collection = null)
|
||||||
{
|
{
|
||||||
if (!Available)
|
if (!Available)
|
||||||
return "Penumbra is not available.";
|
return "Penumbra is not available.";
|
||||||
|
|
@ -166,12 +167,13 @@ public unsafe class PenumbraService : IDisposable
|
||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var collection = _currentCollection.Invoke(ApiCollectionType.Current);
|
collection ??= _currentCollection.Invoke(ApiCollectionType.Current);
|
||||||
var ec = _setMod.Invoke(collection, mod.DirectoryName, mod.Name, settings.Enabled);
|
var ec = _setMod.Invoke(collection, mod.DirectoryName, mod.Name, settings.Enabled);
|
||||||
if (ec is PenumbraApiEc.ModMissing)
|
switch (ec)
|
||||||
return $"The mod {mod.Name} [{mod.DirectoryName}] could not be found.";
|
{
|
||||||
|
case PenumbraApiEc.ModMissing: return $"The mod {mod.Name} [{mod.DirectoryName}] could not be found.";
|
||||||
Debug.Assert(ec is not PenumbraApiEc.CollectionMissing, "Missing collection should not be possible.");
|
case PenumbraApiEc.CollectionMissing: return $"The collection {collection} could not be found.";
|
||||||
|
}
|
||||||
|
|
||||||
if (!settings.Enabled)
|
if (!settings.Enabled)
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
|
|
@ -216,13 +218,23 @@ public unsafe class PenumbraService : IDisposable
|
||||||
return valid ? name : string.Empty;
|
return valid ? name : string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary> Obtain the name of the collection currently assigned to the given actor. </summary>
|
||||||
|
public string GetActorCollection(Actor actor)
|
||||||
|
{
|
||||||
|
if (!Available)
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
var (valid, _, name) = _objectCollection.Invoke(actor.Index.Index);
|
||||||
|
return valid ? name : string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary> Obtain the game object corresponding to a draw object. </summary>
|
/// <summary> Obtain the game object corresponding to a draw object. </summary>
|
||||||
public Actor GameObjectFromDrawObject(Model drawObject)
|
public Actor GameObjectFromDrawObject(Model drawObject)
|
||||||
=> Available ? _drawObjectInfo.Invoke(drawObject.Address).Item1 : Actor.Null;
|
=> Available ? _drawObjectInfo.Invoke(drawObject.Address).Item1 : Actor.Null;
|
||||||
|
|
||||||
/// <summary> Obtain the parent of a cutscene actor if it is known. </summary>
|
/// <summary> Obtain the parent of a cutscene actor if it is known. </summary>
|
||||||
public short CutsceneParent(ushort idx)
|
public short CutsceneParent(ushort idx)
|
||||||
=> (short) (Available ? _cutsceneParent.Invoke(idx) : -1);
|
=> (short)(Available ? _cutsceneParent.Invoke(idx) : -1);
|
||||||
|
|
||||||
/// <summary> Try to redraw the given actor. </summary>
|
/// <summary> Try to redraw the given actor. </summary>
|
||||||
public void RedrawObject(Actor actor, RedrawType settings)
|
public void RedrawObject(Actor actor, RedrawType settings)
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,8 @@ using Glamourer.Events;
|
||||||
using Glamourer.GameData;
|
using Glamourer.GameData;
|
||||||
using Glamourer.Gui;
|
using Glamourer.Gui;
|
||||||
using Glamourer.Interop;
|
using Glamourer.Interop;
|
||||||
|
using Glamourer.Interop.Penumbra;
|
||||||
|
using Glamourer.Interop.Structs;
|
||||||
using Glamourer.State;
|
using Glamourer.State;
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
using OtterGui;
|
using OtterGui;
|
||||||
|
|
@ -36,10 +38,11 @@ public class CommandService : IDisposable
|
||||||
private readonly DesignConverter _converter;
|
private readonly DesignConverter _converter;
|
||||||
private readonly DesignFileSystem _designFileSystem;
|
private readonly DesignFileSystem _designFileSystem;
|
||||||
private readonly Configuration _config;
|
private readonly Configuration _config;
|
||||||
|
private readonly PenumbraService _penumbra;
|
||||||
|
|
||||||
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)
|
DesignFileSystem designFileSystem, AutoDesignManager autoDesignManager, Configuration config, PenumbraService penumbra)
|
||||||
{
|
{
|
||||||
_commands = commands;
|
_commands = commands;
|
||||||
_mainWindow = mainWindow;
|
_mainWindow = mainWindow;
|
||||||
|
|
@ -53,6 +56,7 @@ public class CommandService : IDisposable
|
||||||
_designFileSystem = designFileSystem;
|
_designFileSystem = designFileSystem;
|
||||||
_autoDesignManager = autoDesignManager;
|
_autoDesignManager = autoDesignManager;
|
||||||
_config = config;
|
_config = config;
|
||||||
|
_penumbra = penumbra;
|
||||||
|
|
||||||
_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,
|
||||||
|
|
@ -368,11 +372,14 @@ public class CommandService : IDisposable
|
||||||
private bool Apply(string arguments)
|
private bool Apply(string arguments)
|
||||||
{
|
{
|
||||||
var split = arguments.Split('|', 2, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
var split = arguments.Split('|', 2, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
||||||
if (split.Length != 2)
|
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, or Clipboard]")
|
||||||
.AddText(" | ")
|
.AddText(" | ")
|
||||||
.AddGreen("[Character Identifier]").BuiltString);
|
.AddGreen("[Character Identifier]")
|
||||||
|
.AddText("; ")
|
||||||
|
.AddBlue("<Apply Mods>")
|
||||||
|
.BuiltString);
|
||||||
_chat.Print(new SeStringBuilder()
|
_chat.Print(new SeStringBuilder()
|
||||||
.AddText(
|
.AddText(
|
||||||
" 》 The design name is case-insensitive. If multiple designs of that name up to case exist, the first one is chosen.")
|
" 》 The design name is case-insensitive. If multiple designs of that name up to case exist, the first one is chosen.")
|
||||||
|
|
@ -386,10 +393,27 @@ public class CommandService : IDisposable
|
||||||
.BuiltString);
|
.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()
|
||||||
|
.AddText(" 》 ").AddBlue("<Enable Mods>").AddText(" is optional and can be omitted (together with the ;), ").AddBlue("true")
|
||||||
|
.AddText(" or ").AddBlue("false").AddText(".").BuiltString);
|
||||||
|
_chat.Print(new SeStringBuilder().AddText("If ").AddBlue("true")
|
||||||
|
.AddText(", it will try to apply mod associations to the collection assigned to the identified character.").BuiltString);
|
||||||
PlayerIdentifierHelp(false, true);
|
PlayerIdentifierHelp(false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!GetDesign(split[0], out var design, true) || !IdentifierHandling(split[1], out var identifiers, false, true))
|
var split2 = split[1].Split(';', 2, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
||||||
|
|
||||||
|
var applyMods = split2.Length == 2
|
||||||
|
&& split2[1].ToLowerInvariant() switch
|
||||||
|
{
|
||||||
|
"true" => true,
|
||||||
|
"1" => true,
|
||||||
|
"t" => true,
|
||||||
|
"yes" => true,
|
||||||
|
"y" => true,
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
if (!GetDesign(split[0], out var design, true) || !IdentifierHandling(split2[0], out var identifiers, false, true))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
_objects.Update();
|
_objects.Update();
|
||||||
|
|
@ -405,7 +429,10 @@ public class CommandService : IDisposable
|
||||||
foreach (var actor in actors.Objects)
|
foreach (var actor in actors.Objects)
|
||||||
{
|
{
|
||||||
if (_stateManager.GetOrCreate(actor.GetIdentifier(_actors), actor, out var state))
|
if (_stateManager.GetOrCreate(actor.GetIdentifier(_actors), actor, out var state))
|
||||||
|
{
|
||||||
|
ApplyModSettings(design, actor, applyMods);
|
||||||
_stateManager.ApplyDesign(design, state, StateChanged.Source.Manual);
|
_stateManager.ApplyDesign(design, state, StateChanged.Source.Manual);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -413,6 +440,29 @@ public class CommandService : IDisposable
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ApplyModSettings(DesignBase design, Actor actor, bool applyMods)
|
||||||
|
{
|
||||||
|
if (!applyMods || design is not Design d)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var collection = _penumbra.GetActorCollection(actor);
|
||||||
|
if (collection.Length <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var appliedMods = 0;
|
||||||
|
foreach (var (mod, setting) in d.AssociatedMods)
|
||||||
|
{
|
||||||
|
var message = _penumbra.SetMod(mod, setting, collection);
|
||||||
|
if (message.Length > 0)
|
||||||
|
Glamourer.Messager.Chat.Print($"Error applying mod settings: {message}");
|
||||||
|
else
|
||||||
|
++appliedMods;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (appliedMods > 0)
|
||||||
|
Glamourer.Messager.Chat.Print($"Applied {appliedMods} mod settings to {collection}.");
|
||||||
|
}
|
||||||
|
|
||||||
private bool Delete(string argument)
|
private bool Delete(string argument)
|
||||||
{
|
{
|
||||||
if (argument.Length == 0)
|
if (argument.Length == 0)
|
||||||
|
|
@ -501,7 +551,8 @@ public class CommandService : IDisposable
|
||||||
&& _stateManager.GetOrCreate(identifier, data.Objects[0], out state)))
|
&& _stateManager.GetOrCreate(identifier, data.Objects[0], out state)))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var design = _converter.Convert(state, EquipFlagExtensions.All, CustomizeFlagExtensions.AllRelevant, CrestExtensions.All, CustomizeParameterExtensions.All);
|
var design = _converter.Convert(state, EquipFlagExtensions.All, CustomizeFlagExtensions.AllRelevant, CrestExtensions.All,
|
||||||
|
CustomizeParameterExtensions.All);
|
||||||
_designManager.CreateClone(design, split[0], true);
|
_designManager.CreateClone(design, split[0], true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -556,7 +607,6 @@ public class CommandService : IDisposable
|
||||||
_chat.Print(new SeStringBuilder().AddText("The token ").AddYellow(argument, true).AddText(" did not resolve to an existing design.")
|
_chat.Print(new SeStringBuilder().AddText("The token ").AddYellow(argument, true).AddText(" did not resolve to an existing design.")
|
||||||
.BuiltString);
|
.BuiltString);
|
||||||
return false;
|
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)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue