mirror of
https://github.com/Ottermandias/Glamourer.git
synced 2025-12-12 18:27:24 +01:00
Rework API/IPC and use new Penumbra IPC.
This commit is contained in:
parent
9f276c7674
commit
0268546f63
46 changed files with 2270 additions and 1233 deletions
8
Glamourer/Api/Api/IGlamourerApi.cs
Normal file
8
Glamourer/Api/Api/IGlamourerApi.cs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
namespace Glamourer.Api.Api;
|
||||
|
||||
public interface IGlamourerApi : IGlamourerApiBase
|
||||
{
|
||||
public IGlamourerApiDesigns Designs { get; }
|
||||
public IGlamourerApiItems Items { get; }
|
||||
public IGlamourerApiState State { get; }
|
||||
}
|
||||
6
Glamourer/Api/Api/IGlamourerApiBase.cs
Normal file
6
Glamourer/Api/Api/IGlamourerApiBase.cs
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
namespace Glamourer.Api.Api;
|
||||
|
||||
public interface IGlamourerApiBase
|
||||
{
|
||||
public (int Major, int Minor) ApiVersion { get; }
|
||||
}
|
||||
12
Glamourer/Api/Api/IGlamourerApiDesigns.cs
Normal file
12
Glamourer/Api/Api/IGlamourerApiDesigns.cs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
using Glamourer.Api.Enums;
|
||||
|
||||
namespace Glamourer.Api.Api;
|
||||
|
||||
public interface IGlamourerApiDesigns
|
||||
{
|
||||
public Dictionary<Guid, string> GetDesignList();
|
||||
|
||||
public GlamourerApiEc ApplyDesign(Guid designId, int objectIndex, uint key, ApplyFlag flags);
|
||||
|
||||
public GlamourerApiEc ApplyDesignName(Guid designId, string objectName, uint key, ApplyFlag flags);
|
||||
}
|
||||
9
Glamourer/Api/Api/IGlamourerApiItems.cs
Normal file
9
Glamourer/Api/Api/IGlamourerApiItems.cs
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
using Glamourer.Api.Enums;
|
||||
|
||||
namespace Glamourer.Api.Api;
|
||||
|
||||
public interface IGlamourerApiItems
|
||||
{
|
||||
public GlamourerApiEc SetItem(int objectIndex, ApiEquipSlot apiSlot, ulong itemId, byte stain, uint key, ApplyFlag flags);
|
||||
public GlamourerApiEc SetItemName(string objectName, ApiEquipSlot slot, ulong itemId, byte stain, uint key, ApplyFlag flags);
|
||||
}
|
||||
28
Glamourer/Api/Api/IGlamourerApiState.cs
Normal file
28
Glamourer/Api/Api/IGlamourerApiState.cs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
using Glamourer.Api.Enums;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Glamourer.Api.Api;
|
||||
|
||||
public interface IGlamourerApiState
|
||||
{
|
||||
public (GlamourerApiEc, JObject?) GetState(int objectIndex, uint key);
|
||||
public (GlamourerApiEc, JObject?) GetStateName(string objectName, uint key);
|
||||
|
||||
public GlamourerApiEc ApplyState(object applyState, int objectIndex, uint key, ApplyFlag flags);
|
||||
|
||||
public GlamourerApiEc ApplyStateName(object state, string objectName, uint key, ApplyFlag flags);
|
||||
|
||||
public GlamourerApiEc RevertState(int objectIndex, uint key, ApplyFlag flags);
|
||||
public GlamourerApiEc RevertStateName(string objectName, uint key, ApplyFlag flags);
|
||||
|
||||
public GlamourerApiEc UnlockState(int objectIndex, uint key);
|
||||
public GlamourerApiEc UnlockStateName(string objectName, uint key);
|
||||
public int UnlockAll(uint key);
|
||||
|
||||
public GlamourerApiEc RevertToAutomation(int objectIndex, uint key, ApplyFlag flags);
|
||||
public GlamourerApiEc RevertToAutomationName(string objectName, uint key, ApplyFlag flags);
|
||||
|
||||
public event Action<nint>? StateChanged;
|
||||
|
||||
public event Action<bool>? GPoseChanged;
|
||||
}
|
||||
124
Glamourer/Api/ApiHelpers.cs
Normal file
124
Glamourer/Api/ApiHelpers.cs
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
using Glamourer.Api.Enums;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.GameData;
|
||||
using Glamourer.State;
|
||||
using OtterGui;
|
||||
using OtterGui.Log;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.GameData.Actors;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.String;
|
||||
using ObjectManager = Glamourer.Interop.ObjectManager;
|
||||
|
||||
namespace Glamourer.Api;
|
||||
|
||||
public class ApiHelpers(ObjectManager objects, StateManager stateManager, ActorManager actors) : IApiService
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
internal IEnumerable<ActorState> FindExistingStates(string actorName)
|
||||
{
|
||||
if (actorName.Length == 0 || !ByteString.FromString(actorName, out var byteString))
|
||||
yield break;
|
||||
|
||||
foreach (var state in stateManager.Values.Where(state
|
||||
=> state.Identifier.Type is IdentifierType.Player && state.Identifier.PlayerName == byteString))
|
||||
yield return state;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
internal GlamourerApiEc FindExistingState(int objectIndex, out ActorState? state)
|
||||
{
|
||||
var actor = objects[objectIndex];
|
||||
var identifier = actor.GetIdentifier(actors);
|
||||
if (!identifier.IsValid)
|
||||
{
|
||||
state = null;
|
||||
return GlamourerApiEc.ActorNotFound;
|
||||
}
|
||||
stateManager.TryGetValue(identifier, out state);
|
||||
return GlamourerApiEc.Success;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
internal ActorState? FindState(int objectIndex)
|
||||
{
|
||||
var actor = objects[objectIndex];
|
||||
var identifier = actor.GetIdentifier(actors);
|
||||
if (identifier.IsValid && stateManager.GetOrCreate(identifier, actor, out var state))
|
||||
return state;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
internal static DesignBase.FlagRestrictionResetter Restrict(DesignBase design, ApplyFlag flags)
|
||||
=> (flags & (ApplyFlag.Equipment | ApplyFlag.Customization)) switch
|
||||
{
|
||||
ApplyFlag.Equipment => design.TemporarilyRestrictApplication(EquipFlagExtensions.All, 0, CrestExtensions.All, 0),
|
||||
ApplyFlag.Customization => design.TemporarilyRestrictApplication(0, CustomizeFlagExtensions.All, 0,
|
||||
CustomizeParameterExtensions.All),
|
||||
ApplyFlag.Equipment | ApplyFlag.Customization => design.TemporarilyRestrictApplication(EquipFlagExtensions.All,
|
||||
CustomizeFlagExtensions.All, CrestExtensions.All, CustomizeParameterExtensions.All),
|
||||
_ => design.TemporarilyRestrictApplication(0, 0, 0, 0),
|
||||
};
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
internal static void Lock(ActorState state, uint key, ApplyFlag flags)
|
||||
{
|
||||
if ((flags & ApplyFlag.Lock) != 0 && key != 0)
|
||||
state.Lock(key);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
internal IEnumerable<ActorState> FindStates(string objectName)
|
||||
{
|
||||
if (objectName.Length == 0 || !ByteString.FromString(objectName, out var byteString))
|
||||
return [];
|
||||
|
||||
objects.Update();
|
||||
|
||||
return stateManager.Values.Where(state => state.Identifier.Type is IdentifierType.Player && state.Identifier.PlayerName == byteString)
|
||||
.Concat(objects.Identifiers
|
||||
.Where(kvp => kvp.Key is { IsValid: true, Type: IdentifierType.Player } && kvp.Key.PlayerName == byteString)
|
||||
.SelectWhere(kvp =>
|
||||
{
|
||||
if (stateManager.ContainsKey(kvp.Key))
|
||||
return (false, null);
|
||||
|
||||
var ret = stateManager.GetOrCreate(kvp.Key, kvp.Value.Objects[0], out var state);
|
||||
return (ret, state);
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
internal static GlamourerApiEc Return(GlamourerApiEc ec, LazyString args, [CallerMemberName] string name = "Unknown")
|
||||
{
|
||||
if (ec is GlamourerApiEc.Success or GlamourerApiEc.NothingDone)
|
||||
Glamourer.Log.Verbose($"[{name}] Called with {args}, returned {ec}.");
|
||||
else
|
||||
Glamourer.Log.Debug($"[{name}] Called with {args}, returned {ec}.");
|
||||
return ec;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
internal static LazyString Args(params object[] arguments)
|
||||
{
|
||||
if (arguments.Length == 0)
|
||||
return new LazyString(() => "no arguments");
|
||||
|
||||
return new LazyString(() =>
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
for (var i = 0; i < arguments.Length / 2; ++i)
|
||||
{
|
||||
sb.Append(arguments[2 * i]);
|
||||
sb.Append(" = ");
|
||||
sb.Append(arguments[2 * i + 1]);
|
||||
sb.Append(", ");
|
||||
}
|
||||
|
||||
return sb.ToString(0, sb.Length - 2);
|
||||
});
|
||||
}
|
||||
}
|
||||
69
Glamourer/Api/DesignsApi.cs
Normal file
69
Glamourer/Api/DesignsApi.cs
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
using Glamourer.Api.Api;
|
||||
using Glamourer.Api.Enums;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.State;
|
||||
using OtterGui.Services;
|
||||
|
||||
namespace Glamourer.Api;
|
||||
|
||||
public class DesignsApi(ApiHelpers helpers, DesignManager designs, StateManager stateManager) : IGlamourerApiDesigns, IApiService
|
||||
{
|
||||
public Dictionary<Guid, string> GetDesignList()
|
||||
=> designs.Designs.ToDictionary(d => d.Identifier, d => d.Name.Text);
|
||||
|
||||
public GlamourerApiEc ApplyDesign(Guid designId, int objectIndex, uint key, ApplyFlag flags)
|
||||
{
|
||||
var args = ApiHelpers.Args("Design", designId, "Index", objectIndex, "Key", key, "Flags", flags);
|
||||
var design = designs.Designs.ByIdentifier(designId);
|
||||
if (design == null)
|
||||
return ApiHelpers.Return(GlamourerApiEc.DesignNotFound, args);
|
||||
|
||||
if (helpers.FindState(objectIndex) is not { } state)
|
||||
return ApiHelpers.Return(GlamourerApiEc.ActorNotFound, args);
|
||||
|
||||
if (!state.CanUnlock(key))
|
||||
return ApiHelpers.Return(GlamourerApiEc.InvalidKey, args);
|
||||
|
||||
ApplyDesign(state, design, key, flags);
|
||||
ApiHelpers.Lock(state, key, flags);
|
||||
return ApiHelpers.Return(GlamourerApiEc.Success, args);
|
||||
}
|
||||
|
||||
private void ApplyDesign(ActorState state, Design design, uint key, ApplyFlag flags)
|
||||
{
|
||||
var once = (flags & ApplyFlag.Once) != 0;
|
||||
var settings = new ApplySettings(Source: once ? StateSource.IpcManual : StateSource.IpcFixed, Key: key, MergeLinks: true,
|
||||
ResetMaterials: !once && key != 0);
|
||||
|
||||
using var restrict = ApiHelpers.Restrict(design, flags);
|
||||
stateManager.ApplyDesign(state, design, settings);
|
||||
}
|
||||
|
||||
public GlamourerApiEc ApplyDesignName(Guid designId, string objectName, uint key, ApplyFlag flags)
|
||||
{
|
||||
var args = ApiHelpers.Args("Design", designId, "Name", objectName, "Key", key, "Flags", flags);
|
||||
var design = designs.Designs.ByIdentifier(designId);
|
||||
if (design == null)
|
||||
return ApiHelpers.Return(GlamourerApiEc.DesignNotFound, args);
|
||||
|
||||
var any = false;
|
||||
var anyUnlocked = false;
|
||||
foreach (var state in helpers.FindStates(objectName))
|
||||
{
|
||||
any = true;
|
||||
if (!state.CanUnlock(key))
|
||||
continue;
|
||||
|
||||
anyUnlocked = true;
|
||||
ApplyDesign(state, design, key, flags);
|
||||
ApiHelpers.Lock(state, key, flags);
|
||||
}
|
||||
|
||||
if (!any)
|
||||
return ApiHelpers.Return(GlamourerApiEc.ActorNotFound, args);
|
||||
if (!anyUnlocked)
|
||||
return ApiHelpers.Return(GlamourerApiEc.InvalidKey, args);
|
||||
|
||||
return ApiHelpers.Return(GlamourerApiEc.Success, args);
|
||||
}
|
||||
}
|
||||
34
Glamourer/Api/Enums/ApplyFlag.cs
Normal file
34
Glamourer/Api/Enums/ApplyFlag.cs
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
namespace Glamourer.Api.Enums;
|
||||
|
||||
[Flags]
|
||||
public enum ApplyFlag : ulong
|
||||
{
|
||||
Once = 0x01,
|
||||
Equipment = 0x02,
|
||||
Customization = 0x04,
|
||||
Lock = 0x08,
|
||||
}
|
||||
|
||||
public static class ApplyFlagEx
|
||||
{
|
||||
public const ApplyFlag DesignDefault = ApplyFlag.Once | ApplyFlag.Equipment | ApplyFlag.Customization;
|
||||
public const ApplyFlag StateDefault = ApplyFlag.Equipment | ApplyFlag.Customization | ApplyFlag.Lock;
|
||||
public const ApplyFlag RevertDefault = ApplyFlag.Equipment | ApplyFlag.Customization;
|
||||
}
|
||||
|
||||
public enum ApiEquipSlot : byte
|
||||
{
|
||||
Unknown = 0,
|
||||
MainHand = 1,
|
||||
OffHand = 2,
|
||||
Head = 3,
|
||||
Body = 4,
|
||||
Hands = 5,
|
||||
Legs = 7,
|
||||
Feet = 8,
|
||||
Ears = 9,
|
||||
Neck = 10,
|
||||
Wrists = 11,
|
||||
RFinger = 12,
|
||||
LFinger = 14, // Not officially existing, means "weapon could be equipped in either hand" for the game.
|
||||
}
|
||||
13
Glamourer/Api/Enums/GlamourerApiEc.cs
Normal file
13
Glamourer/Api/Enums/GlamourerApiEc.cs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
namespace Glamourer.Api.Enums;
|
||||
|
||||
public enum GlamourerApiEc
|
||||
{
|
||||
Success,
|
||||
ActorNotFound,
|
||||
ActorNotHuman,
|
||||
DesignNotFound,
|
||||
ItemInvalid,
|
||||
InvalidKey,
|
||||
InvalidState,
|
||||
NothingDone,
|
||||
}
|
||||
22
Glamourer/Api/GlamourerApi.cs
Normal file
22
Glamourer/Api/GlamourerApi.cs
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
using Glamourer.Api.Api;
|
||||
using OtterGui.Services;
|
||||
|
||||
namespace Glamourer.Api;
|
||||
|
||||
public class GlamourerApi(DesignsApi designs, StateApi state, ItemsApi items) : IGlamourerApi, IApiService
|
||||
{
|
||||
public const int CurrentApiVersionMajor = 1;
|
||||
public const int CurrentApiVersionMinor = 0;
|
||||
|
||||
public (int Major, int Minor) ApiVersion
|
||||
=> (CurrentApiVersionMajor, CurrentApiVersionMinor);
|
||||
|
||||
public IGlamourerApiDesigns Designs
|
||||
=> designs;
|
||||
|
||||
public IGlamourerApiItems Items
|
||||
=> items;
|
||||
|
||||
public IGlamourerApiState State
|
||||
=> state;
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
using Dalamud.Plugin;
|
||||
using Penumbra.Api.Helpers;
|
||||
|
||||
namespace Glamourer.Api;
|
||||
|
||||
public partial class GlamourerIpc
|
||||
{
|
||||
public const string LabelApiVersion = "Glamourer.ApiVersion";
|
||||
public const string LabelApiVersions = "Glamourer.ApiVersions";
|
||||
|
||||
private readonly FuncProvider<int> _apiVersionProvider;
|
||||
private readonly FuncProvider<(int Major, int Minor)> _apiVersionsProvider;
|
||||
|
||||
public static FuncSubscriber<int> ApiVersionSubscriber(DalamudPluginInterface pi)
|
||||
=> new(pi, LabelApiVersion);
|
||||
|
||||
public static FuncSubscriber<(int Major, int Minor)> ApiVersionsSubscriber(DalamudPluginInterface pi)
|
||||
=> new(pi, LabelApiVersions);
|
||||
|
||||
public int ApiVersion()
|
||||
=> CurrentApiVersionMajor;
|
||||
|
||||
public (int Major, int Minor) ApiVersions()
|
||||
=> (CurrentApiVersionMajor, CurrentApiVersionMinor);
|
||||
}
|
||||
|
|
@ -1,180 +0,0 @@
|
|||
using Dalamud.Game.ClientState.Objects.Types;
|
||||
using Dalamud.Plugin;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Interop.Structs;
|
||||
using Glamourer.State;
|
||||
using Penumbra.Api.Helpers;
|
||||
using Penumbra.GameData.Actors;
|
||||
|
||||
namespace Glamourer.Api;
|
||||
|
||||
public partial class GlamourerIpc
|
||||
{
|
||||
public const string LabelApplyAll = "Glamourer.ApplyAll";
|
||||
public const string LabelApplyAllOnce = "Glamourer.ApplyAllOnce";
|
||||
public const string LabelApplyAllToCharacter = "Glamourer.ApplyAllToCharacter";
|
||||
public const string LabelApplyAllOnceToCharacter = "Glamourer.ApplyAllOnceToCharacter";
|
||||
public const string LabelApplyOnlyEquipment = "Glamourer.ApplyOnlyEquipment";
|
||||
public const string LabelApplyOnlyEquipmentToCharacter = "Glamourer.ApplyOnlyEquipmentToCharacter";
|
||||
public const string LabelApplyOnlyCustomization = "Glamourer.ApplyOnlyCustomization";
|
||||
public const string LabelApplyOnlyCustomizationToCharacter = "Glamourer.ApplyOnlyCustomizationToCharacter";
|
||||
|
||||
public const string LabelApplyAllLock = "Glamourer.ApplyAllLock";
|
||||
public const string LabelApplyAllToCharacterLock = "Glamourer.ApplyAllToCharacterLock";
|
||||
public const string LabelApplyOnlyEquipmentLock = "Glamourer.ApplyOnlyEquipmentLock";
|
||||
public const string LabelApplyOnlyEquipmentToCharacterLock = "Glamourer.ApplyOnlyEquipmentToCharacterLock";
|
||||
public const string LabelApplyOnlyCustomizationLock = "Glamourer.ApplyOnlyCustomizationLock";
|
||||
public const string LabelApplyOnlyCustomizationToCharacterLock = "Glamourer.ApplyOnlyCustomizationToCharacterLock";
|
||||
|
||||
public const string LabelApplyByGuid = "Glamourer.ApplyByGuid";
|
||||
public const string LabelApplyByGuidOnce = "Glamourer.ApplyByGuidOnce";
|
||||
public const string LabelApplyByGuidToCharacter = "Glamourer.ApplyByGuidToCharacter";
|
||||
public const string LabelApplyByGuidOnceToCharacter = "Glamourer.ApplyByGuidOnceToCharacter";
|
||||
|
||||
private readonly ActionProvider<string, string> _applyAllProvider;
|
||||
private readonly ActionProvider<string, string> _applyAllOnceProvider;
|
||||
private readonly ActionProvider<string, Character?> _applyAllToCharacterProvider;
|
||||
private readonly ActionProvider<string, Character?> _applyAllOnceToCharacterProvider;
|
||||
private readonly ActionProvider<string, string> _applyOnlyEquipmentProvider;
|
||||
private readonly ActionProvider<string, Character?> _applyOnlyEquipmentToCharacterProvider;
|
||||
private readonly ActionProvider<string, string> _applyOnlyCustomizationProvider;
|
||||
private readonly ActionProvider<string, Character?> _applyOnlyCustomizationToCharacterProvider;
|
||||
|
||||
private readonly ActionProvider<string, string, uint> _applyAllProviderLock;
|
||||
private readonly ActionProvider<string, Character?, uint> _applyAllToCharacterProviderLock;
|
||||
private readonly ActionProvider<string, string, uint> _applyOnlyEquipmentProviderLock;
|
||||
private readonly ActionProvider<string, Character?, uint> _applyOnlyEquipmentToCharacterProviderLock;
|
||||
private readonly ActionProvider<string, string, uint> _applyOnlyCustomizationProviderLock;
|
||||
private readonly ActionProvider<string, Character?, uint> _applyOnlyCustomizationToCharacterProviderLock;
|
||||
|
||||
private readonly ActionProvider<Guid, string> _applyByGuidProvider;
|
||||
private readonly ActionProvider<Guid, string> _applyByGuidOnceProvider;
|
||||
private readonly ActionProvider<Guid, Character?> _applyByGuidToCharacterProvider;
|
||||
private readonly ActionProvider<Guid, Character?> _applyByGuidOnceToCharacterProvider;
|
||||
|
||||
public static ActionSubscriber<string, string> ApplyAllSubscriber(DalamudPluginInterface pi)
|
||||
=> new(pi, LabelApplyAll);
|
||||
|
||||
public static ActionSubscriber<string, string> ApplyAllOnceSubscriber(DalamudPluginInterface pi)
|
||||
=> new(pi, LabelApplyAllOnce);
|
||||
|
||||
public static ActionSubscriber<string, Character?> ApplyAllToCharacterSubscriber(DalamudPluginInterface pi)
|
||||
=> new(pi, LabelApplyAllToCharacter);
|
||||
|
||||
public static ActionSubscriber<string, Character?> ApplyAllOnceToCharacterSubscriber(DalamudPluginInterface pi)
|
||||
=> new(pi, LabelApplyAllOnceToCharacter);
|
||||
|
||||
public static ActionSubscriber<string, string> ApplyOnlyEquipmentSubscriber(DalamudPluginInterface pi)
|
||||
=> new(pi, LabelApplyOnlyEquipment);
|
||||
|
||||
public static ActionSubscriber<string, Character?> ApplyOnlyEquipmentToCharacterSubscriber(DalamudPluginInterface pi)
|
||||
=> new(pi, LabelApplyOnlyEquipmentToCharacter);
|
||||
|
||||
public static ActionSubscriber<string, string> ApplyOnlyCustomizationSubscriber(DalamudPluginInterface pi)
|
||||
=> new(pi, LabelApplyOnlyCustomization);
|
||||
|
||||
public static ActionSubscriber<string, Character?> ApplyOnlyCustomizationToCharacterSubscriber(DalamudPluginInterface pi)
|
||||
=> new(pi, LabelApplyOnlyCustomizationToCharacter);
|
||||
|
||||
public static ActionSubscriber<Guid, string> ApplyByGuidSubscriber(DalamudPluginInterface pi)
|
||||
=> new(pi, LabelApplyByGuid);
|
||||
|
||||
public static ActionSubscriber<Guid, string> ApplyByGuidOnceSubscriber(DalamudPluginInterface pi)
|
||||
=> new(pi, LabelApplyByGuidOnce);
|
||||
|
||||
public static ActionSubscriber<Guid, Character?> ApplyByGuidToCharacterSubscriber(DalamudPluginInterface pi)
|
||||
=> new(pi, LabelApplyByGuidToCharacter);
|
||||
|
||||
public static ActionSubscriber<Guid, Character?> ApplyByGuidOnceToCharacterSubscriber(DalamudPluginInterface pi)
|
||||
=> new(pi, LabelApplyByGuidOnceToCharacter);
|
||||
|
||||
public static ActionSubscriber<string, string, uint> ApplyAllLockSubscriber(DalamudPluginInterface pi)
|
||||
=> new(pi, LabelApplyAllLock);
|
||||
|
||||
public static ActionSubscriber<string, Character?, uint> ApplyAllToCharacterLockSubscriber(DalamudPluginInterface pi)
|
||||
=> new(pi, LabelApplyAllToCharacterLock);
|
||||
|
||||
public void ApplyAll(string base64, string characterName)
|
||||
=> ApplyDesign(_designConverter.FromBase64(base64, true, true, out var version), FindActors(characterName), version, 0);
|
||||
|
||||
public void ApplyAllOnce(string base64, string characterName)
|
||||
=> ApplyDesign(_designConverter.FromBase64(base64, true, true, out var version), FindActors(characterName), version, 0, true);
|
||||
|
||||
public void ApplyAllToCharacter(string base64, Character? character)
|
||||
=> ApplyDesign(_designConverter.FromBase64(base64, true, true, out var version), FindActors(character), version, 0);
|
||||
|
||||
public void ApplyAllOnceToCharacter(string base64, Character? character)
|
||||
=> ApplyDesign(_designConverter.FromBase64(base64, true, true, out var version), FindActors(character), version, 0, true);
|
||||
|
||||
public void ApplyOnlyEquipment(string base64, string characterName)
|
||||
=> ApplyDesign(_designConverter.FromBase64(base64, false, true, out var version), FindActors(characterName), version, 0);
|
||||
|
||||
public void ApplyOnlyEquipmentToCharacter(string base64, Character? character)
|
||||
=> ApplyDesign(_designConverter.FromBase64(base64, false, true, out var version), FindActors(character), version, 0);
|
||||
|
||||
public void ApplyOnlyCustomization(string base64, string characterName)
|
||||
=> ApplyDesign(_designConverter.FromBase64(base64, true, false, out var version), FindActors(characterName), version, 0);
|
||||
|
||||
public void ApplyOnlyCustomizationToCharacter(string base64, Character? character)
|
||||
=> ApplyDesign(_designConverter.FromBase64(base64, true, false, out var version), FindActors(character), version, 0);
|
||||
|
||||
|
||||
public void ApplyAllLock(string base64, string characterName, uint lockCode)
|
||||
=> ApplyDesign(_designConverter.FromBase64(base64, true, true, out var version), FindActors(characterName), version, lockCode);
|
||||
|
||||
public void ApplyAllToCharacterLock(string base64, Character? character, uint lockCode)
|
||||
=> ApplyDesign(_designConverter.FromBase64(base64, true, true, out var version), FindActors(character), version, lockCode);
|
||||
|
||||
public void ApplyOnlyEquipmentLock(string base64, string characterName, uint lockCode)
|
||||
=> ApplyDesign(_designConverter.FromBase64(base64, false, true, out var version), FindActors(characterName), version, lockCode);
|
||||
|
||||
public void ApplyOnlyEquipmentToCharacterLock(string base64, Character? character, uint lockCode)
|
||||
=> ApplyDesign(_designConverter.FromBase64(base64, false, true, out var version), FindActors(character), version, lockCode);
|
||||
|
||||
public void ApplyOnlyCustomizationLock(string base64, string characterName, uint lockCode)
|
||||
=> ApplyDesign(_designConverter.FromBase64(base64, true, false, out var version), FindActors(characterName), version, lockCode);
|
||||
|
||||
public void ApplyOnlyCustomizationToCharacterLock(string base64, Character? character, uint lockCode)
|
||||
=> ApplyDesign(_designConverter.FromBase64(base64, true, false, out var version), FindActors(character), version, lockCode);
|
||||
|
||||
|
||||
public void ApplyByGuid(Guid identifier, string characterName)
|
||||
=> ApplyDesignByGuid(identifier, FindActors(characterName), 0, false);
|
||||
|
||||
public void ApplyByGuidOnce(Guid identifier, string characterName)
|
||||
=> ApplyDesignByGuid(identifier, FindActors(characterName), 0, true);
|
||||
|
||||
public void ApplyByGuidToCharacter(Guid identifier, Character? character)
|
||||
=> ApplyDesignByGuid(identifier, FindActors(character), 0, false);
|
||||
|
||||
public void ApplyByGuidOnceToCharacter(Guid identifier, Character? character)
|
||||
=> ApplyDesignByGuid(identifier, FindActors(character), 0, true);
|
||||
|
||||
private void ApplyDesign(DesignBase? design, IEnumerable<ActorIdentifier> actors, byte version, uint lockCode, bool once = false)
|
||||
{
|
||||
if (design == null)
|
||||
return;
|
||||
|
||||
var hasModelId = version >= 3;
|
||||
_objects.Update();
|
||||
foreach (var id in actors)
|
||||
{
|
||||
if (!_stateManager.TryGetValue(id, out var state))
|
||||
{
|
||||
var data = _objects.TryGetValue(id, out var d) ? d : ActorData.Invalid;
|
||||
if (!data.Valid || !_stateManager.GetOrCreate(id, data.Objects[0], out state))
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((hasModelId || state.ModelData.ModelId == 0) && state.CanUnlock(lockCode))
|
||||
{
|
||||
_stateManager.ApplyDesign(state, design,
|
||||
new ApplySettings(Source: once ? StateSource.IpcManual : StateSource.IpcFixed, Key: lockCode, MergeLinks: true, ResetMaterials: !once && lockCode != 0));
|
||||
state.Lock(lockCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyDesignByGuid(Guid identifier, IEnumerable<ActorIdentifier> actors, uint lockCode, bool once)
|
||||
=> ApplyDesign(_designManager.Designs.ByIdentifier(identifier), actors, DesignConverter.Version, lockCode, once);
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
using Dalamud.Plugin;
|
||||
using Penumbra.Api.Helpers;
|
||||
|
||||
namespace Glamourer.Api;
|
||||
|
||||
public partial class GlamourerIpc
|
||||
{
|
||||
public const string LabelGetDesignList = "Glamourer.GetDesignList";
|
||||
|
||||
private readonly FuncProvider<(string Name, Guid Identifier)[]> _getDesignListProvider;
|
||||
|
||||
public static FuncSubscriber<(string Name, Guid Identifier)[]> GetDesignListSubscriber(DalamudPluginInterface pi)
|
||||
=> new(pi, LabelGetDesignList);
|
||||
|
||||
public (string Name, Guid Identifier)[] GetDesignList()
|
||||
=> _designManager.Designs.Select(x => (x.Name.Text, x.Identifier)).ToArray();
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
using Glamourer.Designs;
|
||||
using Glamourer.Events;
|
||||
using Glamourer.Interop.Structs;
|
||||
using Glamourer.State;
|
||||
using Penumbra.Api.Helpers;
|
||||
|
||||
namespace Glamourer.Api;
|
||||
|
||||
public partial class GlamourerIpc
|
||||
{
|
||||
public const string LabelStateChanged = "Glamourer.StateChanged";
|
||||
public const string LabelGPoseChanged = "Glamourer.GPoseChanged";
|
||||
|
||||
private readonly GPoseService _gPose;
|
||||
private readonly StateChanged _stateChangedEvent;
|
||||
private readonly EventProvider<StateChanged.Type, nint, Lazy<string>> _stateChangedProvider;
|
||||
private readonly EventProvider<bool> _gPoseChangedProvider;
|
||||
|
||||
private void OnStateChanged(StateChanged.Type type, StateSource source, ActorState state, ActorData actors, object? data = null)
|
||||
{
|
||||
foreach (var actor in actors.Objects)
|
||||
_stateChangedProvider.Invoke(type, actor.Address, new Lazy<string>(() => _designConverter.ShareBase64(state, ApplicationRules.AllButParameters(state))));
|
||||
}
|
||||
|
||||
private void OnGPoseChanged(bool value)
|
||||
=> _gPoseChangedProvider.Invoke(value);
|
||||
}
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
using Dalamud.Game.ClientState.Objects.Types;
|
||||
using Dalamud.Plugin;
|
||||
using Glamourer.Designs;
|
||||
using Penumbra.Api.Helpers;
|
||||
using Penumbra.GameData.Actors;
|
||||
|
||||
namespace Glamourer.Api;
|
||||
|
||||
public partial class GlamourerIpc
|
||||
{
|
||||
public const string LabelGetAllCustomization = "Glamourer.GetAllCustomization";
|
||||
public const string LabelGetAllCustomizationFromCharacter = "Glamourer.GetAllCustomizationFromCharacter";
|
||||
public const string LabelGetAllCustomizationLocked = "Glamourer.GetAllCustomizationLocked";
|
||||
public const string LabelGetAllCustomizationFromLockedCharacter = "Glamourer.GetAllCustomizationFromLockedCharacter";
|
||||
|
||||
private readonly FuncProvider<string, string?> _getAllCustomizationProvider;
|
||||
private readonly FuncProvider<string, uint, string?> _getAllCustomizationLockedProvider;
|
||||
private readonly FuncProvider<Character?, string?> _getAllCustomizationFromCharacterProvider;
|
||||
private readonly FuncProvider<Character?, uint, string?> _getAllCustomizationFromLockedCharacterProvider;
|
||||
|
||||
public static FuncSubscriber<string, string?> GetAllCustomizationSubscriber(DalamudPluginInterface pi)
|
||||
=> new(pi, LabelGetAllCustomization);
|
||||
|
||||
public static FuncSubscriber<Character?, string?> GetAllCustomizationFromCharacterSubscriber(DalamudPluginInterface pi)
|
||||
=> new(pi, LabelGetAllCustomizationFromCharacter);
|
||||
|
||||
public static FuncSubscriber<string, uint, string?> GetAllCustomizationLockedSubscriber(DalamudPluginInterface pi)
|
||||
=> new(pi, LabelGetAllCustomizationLocked);
|
||||
|
||||
public static FuncSubscriber<Character?, uint, string?> GetAllCustomizationFromLockedCharacterSubscriber(DalamudPluginInterface pi)
|
||||
=> new(pi, LabelGetAllCustomizationFromLockedCharacter);
|
||||
|
||||
public string? GetAllCustomization(string characterName)
|
||||
=> GetCustomization(FindActors(characterName), 0);
|
||||
|
||||
public string? GetAllCustomization(string characterName, uint lockCode)
|
||||
=> GetCustomization(FindActors(characterName), lockCode);
|
||||
|
||||
public string? GetAllCustomizationFromCharacter(Character? character)
|
||||
=> GetCustomization(FindActors(character), 0);
|
||||
|
||||
public string? GetAllCustomizationFromCharacter(Character? character, uint lockCode)
|
||||
=> GetCustomization(FindActors(character), lockCode);
|
||||
|
||||
private string? GetCustomization(IEnumerable<ActorIdentifier> actors, uint lockCode)
|
||||
{
|
||||
var actor = actors.FirstOrDefault(ActorIdentifier.Invalid);
|
||||
if (!actor.IsValid)
|
||||
return null;
|
||||
|
||||
if (!_stateManager.TryGetValue(actor, out var state))
|
||||
{
|
||||
_objects.Update();
|
||||
if (!_objects.TryGetValue(actor, out var data) || !data.Valid)
|
||||
return null;
|
||||
if (!_stateManager.GetOrCreate(actor, data.Objects[0], out state))
|
||||
return null;
|
||||
}
|
||||
if (!state.CanUnlock(lockCode))
|
||||
return null;
|
||||
|
||||
return _designConverter.ShareBase64(state, ApplicationRules.AllWithConfig(_config));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,135 +0,0 @@
|
|||
using Dalamud.Game.ClientState.Objects.Types;
|
||||
using Dalamud.Plugin;
|
||||
using Glamourer.State;
|
||||
using Penumbra.Api.Helpers;
|
||||
using Penumbra.GameData.Actors;
|
||||
|
||||
namespace Glamourer.Api;
|
||||
|
||||
public partial class GlamourerIpc
|
||||
{
|
||||
public const string LabelRevert = "Glamourer.Revert";
|
||||
public const string LabelRevertCharacter = "Glamourer.RevertCharacter";
|
||||
public const string LabelRevertLock = "Glamourer.RevertLock";
|
||||
public const string LabelRevertCharacterLock = "Glamourer.RevertCharacterLock";
|
||||
public const string LabelRevertToAutomation = "Glamourer.RevertToAutomation";
|
||||
public const string LabelRevertToAutomationCharacter = "Glamourer.RevertToAutomationCharacter";
|
||||
public const string LabelUnlock = "Glamourer.Unlock";
|
||||
public const string LabelUnlockName = "Glamourer.UnlockName";
|
||||
public const string LabelUnlockAll = "Glamourer.UnlockAll";
|
||||
|
||||
private readonly ActionProvider<string> _revertProvider;
|
||||
private readonly ActionProvider<Character?> _revertCharacterProvider;
|
||||
|
||||
private readonly ActionProvider<string, uint> _revertProviderLock;
|
||||
private readonly ActionProvider<Character?, uint> _revertCharacterProviderLock;
|
||||
|
||||
private readonly FuncProvider<string, uint, bool> _revertToAutomationProvider;
|
||||
private readonly FuncProvider<Character?, uint, bool> _revertToAutomationCharacterProvider;
|
||||
|
||||
private readonly FuncProvider<string, uint, bool> _unlockNameProvider;
|
||||
private readonly FuncProvider<Character?, uint, bool> _unlockProvider;
|
||||
|
||||
private readonly FuncProvider<uint, int> _unlockAllProvider;
|
||||
|
||||
public static ActionSubscriber<string> RevertSubscriber(DalamudPluginInterface pi)
|
||||
=> new(pi, LabelRevert);
|
||||
|
||||
public static ActionSubscriber<Character?> RevertCharacterSubscriber(DalamudPluginInterface pi)
|
||||
=> new(pi, LabelRevertCharacter);
|
||||
|
||||
public static ActionSubscriber<string> RevertLockSubscriber(DalamudPluginInterface pi)
|
||||
=> new(pi, LabelRevertLock);
|
||||
|
||||
public static ActionSubscriber<Character?> RevertCharacterLockSubscriber(DalamudPluginInterface pi)
|
||||
=> new(pi, LabelRevertCharacterLock);
|
||||
|
||||
public static FuncSubscriber<string, uint, bool> UnlockNameSubscriber(DalamudPluginInterface pi)
|
||||
=> new(pi, LabelUnlockName);
|
||||
|
||||
public static FuncSubscriber<Character?, uint, bool> UnlockSubscriber(DalamudPluginInterface pi)
|
||||
=> new(pi, LabelUnlock);
|
||||
|
||||
public static FuncSubscriber<uint, int> UnlockAllSubscriber(DalamudPluginInterface pi)
|
||||
=> new(pi, LabelUnlockAll);
|
||||
|
||||
public static FuncSubscriber<string, uint, bool> RevertToAutomationSubscriber(DalamudPluginInterface pi)
|
||||
=> new(pi, LabelRevertToAutomation);
|
||||
|
||||
public static FuncSubscriber<Character?, uint, bool> RevertToAutomationCharacterSubscriber(DalamudPluginInterface pi)
|
||||
=> new(pi, LabelRevertToAutomationCharacter);
|
||||
|
||||
public void Revert(string characterName)
|
||||
=> Revert(FindActorsRevert(characterName), 0);
|
||||
|
||||
public void RevertCharacter(Character? character)
|
||||
=> Revert(FindActors(character), 0);
|
||||
|
||||
public void RevertLock(string characterName, uint lockCode)
|
||||
=> Revert(FindActorsRevert(characterName), lockCode);
|
||||
|
||||
public void RevertCharacterLock(Character? character, uint lockCode)
|
||||
=> Revert(FindActors(character), lockCode);
|
||||
|
||||
public bool Unlock(string characterName, uint lockCode)
|
||||
=> Unlock(FindActorsRevert(characterName), lockCode);
|
||||
|
||||
public bool Unlock(Character? character, uint lockCode)
|
||||
=> Unlock(FindActors(character), lockCode);
|
||||
|
||||
public int UnlockAll(uint lockCode)
|
||||
{
|
||||
var count = 0;
|
||||
foreach (var state in _stateManager.Values)
|
||||
if (state.Unlock(lockCode))
|
||||
++count;
|
||||
return count;
|
||||
}
|
||||
|
||||
public bool RevertToAutomation(string characterName, uint lockCode)
|
||||
=> RevertToAutomation(FindActorsRevert(characterName), lockCode);
|
||||
|
||||
public bool RevertToAutomation(Character? character, uint lockCode)
|
||||
=> RevertToAutomation(FindActors(character), lockCode);
|
||||
|
||||
private void Revert(IEnumerable<ActorIdentifier> actors, uint lockCode)
|
||||
{
|
||||
foreach (var id in actors)
|
||||
{
|
||||
if (_stateManager.TryGetValue(id, out var state))
|
||||
_stateManager.ResetState(state, StateSource.IpcFixed, lockCode);
|
||||
}
|
||||
}
|
||||
|
||||
private bool Unlock(IEnumerable<ActorIdentifier> actors, uint lockCode)
|
||||
{
|
||||
var ret = false;
|
||||
foreach (var id in actors)
|
||||
{
|
||||
if (_stateManager.TryGetValue(id, out var state))
|
||||
ret |= state.Unlock(lockCode);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private bool RevertToAutomation(IEnumerable<ActorIdentifier> actors, uint lockCode)
|
||||
{
|
||||
var ret = false;
|
||||
foreach (var id in actors)
|
||||
{
|
||||
if (_stateManager.TryGetValue(id, out var state))
|
||||
{
|
||||
ret |= state.Unlock(lockCode);
|
||||
if (_objects.TryGetValue(id, out var data))
|
||||
foreach (var obj in data.Objects)
|
||||
{
|
||||
_autoDesignApplier.ReapplyAutomation(obj, state.Identifier, state, true);
|
||||
_stateManager.ReapplyState(obj, StateSource.IpcManual);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,105 +0,0 @@
|
|||
using Dalamud.Game.ClientState.Objects.Types;
|
||||
using Dalamud.Plugin;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Events;
|
||||
using Glamourer.Services;
|
||||
using Glamourer.State;
|
||||
using Penumbra.Api.Helpers;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
namespace Glamourer.Api;
|
||||
|
||||
public partial class GlamourerIpc
|
||||
{
|
||||
public enum GlamourerErrorCode
|
||||
{
|
||||
Success,
|
||||
ActorNotFound,
|
||||
ActorNotHuman,
|
||||
ItemInvalid,
|
||||
}
|
||||
|
||||
public const string LabelSetItem = "Glamourer.SetItem";
|
||||
public const string LabelSetItemOnce = "Glamourer.SetItemOnce";
|
||||
public const string LabelSetItemByActorName = "Glamourer.SetItemByActorName";
|
||||
public const string LabelSetItemOnceByActorName = "Glamourer.SetItemOnceByActorName";
|
||||
|
||||
|
||||
private readonly FuncProvider<Character?, byte, ulong, byte, uint, int> _setItemProvider;
|
||||
private readonly FuncProvider<Character?, byte, ulong, byte, uint, int> _setItemOnceProvider;
|
||||
private readonly FuncProvider<string, byte, ulong, byte, uint, int> _setItemByActorNameProvider;
|
||||
private readonly FuncProvider<string, byte, ulong, byte, uint, int> _setItemOnceByActorNameProvider;
|
||||
|
||||
public static FuncSubscriber<Character?, byte, ulong, byte, uint, int> SetItemSubscriber(DalamudPluginInterface pi)
|
||||
=> new(pi, LabelSetItem);
|
||||
|
||||
public static FuncSubscriber<Character?, byte, ulong, byte, uint, int> SetItemOnceSubscriber(DalamudPluginInterface pi)
|
||||
=> new(pi, LabelSetItemOnce);
|
||||
|
||||
public static FuncSubscriber<string, byte, ulong, byte, uint, int> SetItemByActorNameSubscriber(DalamudPluginInterface pi)
|
||||
=> new(pi, LabelSetItemByActorName);
|
||||
|
||||
public static FuncSubscriber<string, byte, ulong, byte, uint, int> SetItemOnceByActorNameSubscriber(DalamudPluginInterface pi)
|
||||
=> new(pi, LabelSetItemOnceByActorName);
|
||||
|
||||
private GlamourerErrorCode SetItem(Character? character, EquipSlot slot, CustomItemId itemId, StainId stainId, uint key, bool once)
|
||||
{
|
||||
if (itemId.Id == 0)
|
||||
itemId = ItemManager.NothingId(slot);
|
||||
|
||||
var item = _items.Resolve(slot, itemId);
|
||||
if (!item.Valid)
|
||||
return GlamourerErrorCode.ItemInvalid;
|
||||
|
||||
var identifier = _actors.FromObject(character, false, false, false);
|
||||
if (!identifier.IsValid)
|
||||
return GlamourerErrorCode.ActorNotFound;
|
||||
|
||||
if (!_stateManager.TryGetValue(identifier, out var state))
|
||||
{
|
||||
_objects.Update();
|
||||
var data = _objects[identifier];
|
||||
if (!data.Valid || !_stateManager.GetOrCreate(identifier, data.Objects[0], out state))
|
||||
return GlamourerErrorCode.ActorNotFound;
|
||||
}
|
||||
|
||||
if (!state.ModelData.IsHuman)
|
||||
return GlamourerErrorCode.ActorNotHuman;
|
||||
|
||||
_stateManager.ChangeEquip(state, slot, item, stainId,
|
||||
new ApplySettings(Source: once ? StateSource.IpcManual : StateSource.IpcFixed, Key: key));
|
||||
return GlamourerErrorCode.Success;
|
||||
}
|
||||
|
||||
private GlamourerErrorCode SetItemByActorName(string name, EquipSlot slot, CustomItemId itemId, StainId stainId, uint key, bool once)
|
||||
{
|
||||
if (itemId.Id == 0)
|
||||
itemId = ItemManager.NothingId(slot);
|
||||
|
||||
var item = _items.Resolve(slot, itemId);
|
||||
if (!item.Valid)
|
||||
return GlamourerErrorCode.ItemInvalid;
|
||||
|
||||
var found = false;
|
||||
_objects.Update();
|
||||
foreach (var identifier in FindActorsRevert(name).Distinct())
|
||||
{
|
||||
if (!_stateManager.TryGetValue(identifier, out var state))
|
||||
{
|
||||
var data = _objects[identifier];
|
||||
if (!data.Valid || !_stateManager.GetOrCreate(identifier, data.Objects[0], out state))
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!state.ModelData.IsHuman)
|
||||
return GlamourerErrorCode.ActorNotHuman;
|
||||
|
||||
_stateManager.ChangeEquip(state, slot, item, stainId,
|
||||
new ApplySettings(Source: once ? StateSource.IpcManual : StateSource.IpcFixed, Key: key));
|
||||
found = true;
|
||||
}
|
||||
|
||||
return found ? GlamourerErrorCode.Success : GlamourerErrorCode.ActorNotFound;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,196 +0,0 @@
|
|||
using Dalamud.Game.ClientState.Objects.Types;
|
||||
using Dalamud.Plugin;
|
||||
using Glamourer.Automation;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Events;
|
||||
using Glamourer.Interop;
|
||||
using Glamourer.Services;
|
||||
using Glamourer.State;
|
||||
using Penumbra.Api.Helpers;
|
||||
using Penumbra.GameData.Actors;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.String;
|
||||
|
||||
namespace Glamourer.Api;
|
||||
|
||||
public sealed partial class GlamourerIpc : IDisposable
|
||||
{
|
||||
public const int CurrentApiVersionMajor = 0;
|
||||
public const int CurrentApiVersionMinor = 5;
|
||||
|
||||
private readonly StateManager _stateManager;
|
||||
private readonly ObjectManager _objects;
|
||||
private readonly ActorManager _actors;
|
||||
private readonly DesignConverter _designConverter;
|
||||
private readonly AutoDesignApplier _autoDesignApplier;
|
||||
private readonly DesignManager _designManager;
|
||||
private readonly ItemManager _items;
|
||||
private readonly Configuration _config;
|
||||
|
||||
public GlamourerIpc(DalamudPluginInterface pi, StateManager stateManager, ObjectManager objects, ActorManager actors,
|
||||
DesignConverter designConverter, StateChanged stateChangedEvent, GPoseService gPose, AutoDesignApplier autoDesignApplier,
|
||||
DesignManager designManager, ItemManager items, Configuration config)
|
||||
{
|
||||
_stateManager = stateManager;
|
||||
_objects = objects;
|
||||
_actors = actors;
|
||||
_designConverter = designConverter;
|
||||
_autoDesignApplier = autoDesignApplier;
|
||||
_items = items;
|
||||
_config = config;
|
||||
_gPose = gPose;
|
||||
_stateChangedEvent = stateChangedEvent;
|
||||
_designManager = designManager;
|
||||
_apiVersionProvider = new FuncProvider<int>(pi, LabelApiVersion, ApiVersion);
|
||||
_apiVersionsProvider = new FuncProvider<(int Major, int Minor)>(pi, LabelApiVersions, ApiVersions);
|
||||
|
||||
_getAllCustomizationProvider = new FuncProvider<string, string?>(pi, LabelGetAllCustomization, GetAllCustomization);
|
||||
_getAllCustomizationFromCharacterProvider =
|
||||
new FuncProvider<Character?, string?>(pi, LabelGetAllCustomizationFromCharacter, GetAllCustomizationFromCharacter);
|
||||
_getAllCustomizationLockedProvider = new FuncProvider<string, uint, string?>(pi, LabelGetAllCustomizationLocked, GetAllCustomization);
|
||||
_getAllCustomizationFromLockedCharacterProvider =
|
||||
new FuncProvider<Character?, uint, string?>(pi, LabelGetAllCustomizationFromLockedCharacter, GetAllCustomizationFromCharacter);
|
||||
|
||||
_applyAllProvider = new ActionProvider<string, string>(pi, LabelApplyAll, ApplyAll);
|
||||
_applyAllOnceProvider = new ActionProvider<string, string>(pi, LabelApplyAllOnce, ApplyAllOnce);
|
||||
_applyAllToCharacterProvider = new ActionProvider<string, Character?>(pi, LabelApplyAllToCharacter, ApplyAllToCharacter);
|
||||
_applyAllOnceToCharacterProvider = new ActionProvider<string, Character?>(pi, LabelApplyAllOnceToCharacter, ApplyAllOnceToCharacter);
|
||||
_applyOnlyEquipmentProvider = new ActionProvider<string, string>(pi, LabelApplyOnlyEquipment, ApplyOnlyEquipment);
|
||||
_applyOnlyEquipmentToCharacterProvider =
|
||||
new ActionProvider<string, Character?>(pi, LabelApplyOnlyEquipmentToCharacter, ApplyOnlyEquipmentToCharacter);
|
||||
_applyOnlyCustomizationProvider = new ActionProvider<string, string>(pi, LabelApplyOnlyCustomization, ApplyOnlyCustomization);
|
||||
_applyOnlyCustomizationToCharacterProvider =
|
||||
new ActionProvider<string, Character?>(pi, LabelApplyOnlyCustomizationToCharacter, ApplyOnlyCustomizationToCharacter);
|
||||
|
||||
_applyAllProviderLock = new ActionProvider<string, string, uint>(pi, LabelApplyAllLock, ApplyAllLock);
|
||||
_applyAllToCharacterProviderLock =
|
||||
new ActionProvider<string, Character?, uint>(pi, LabelApplyAllToCharacterLock, ApplyAllToCharacterLock);
|
||||
_applyOnlyEquipmentProviderLock = new ActionProvider<string, string, uint>(pi, LabelApplyOnlyEquipmentLock, ApplyOnlyEquipmentLock);
|
||||
_applyOnlyEquipmentToCharacterProviderLock =
|
||||
new ActionProvider<string, Character?, uint>(pi, LabelApplyOnlyEquipmentToCharacterLock, ApplyOnlyEquipmentToCharacterLock);
|
||||
_applyOnlyCustomizationProviderLock =
|
||||
new ActionProvider<string, string, uint>(pi, LabelApplyOnlyCustomizationLock, ApplyOnlyCustomizationLock);
|
||||
_applyOnlyCustomizationToCharacterProviderLock =
|
||||
new ActionProvider<string, Character?, uint>(pi, LabelApplyOnlyCustomizationToCharacterLock, ApplyOnlyCustomizationToCharacterLock);
|
||||
|
||||
_applyByGuidProvider = new ActionProvider<Guid, string>(pi, LabelApplyByGuid, ApplyByGuid);
|
||||
_applyByGuidOnceProvider = new ActionProvider<Guid, string>(pi, LabelApplyByGuidOnce, ApplyByGuidOnce);
|
||||
_applyByGuidToCharacterProvider = new ActionProvider<Guid, Character?>(pi, LabelApplyByGuidToCharacter, ApplyByGuidToCharacter);
|
||||
_applyByGuidOnceToCharacterProvider =
|
||||
new ActionProvider<Guid, Character?>(pi, LabelApplyByGuidOnceToCharacter, ApplyByGuidOnceToCharacter);
|
||||
|
||||
_revertProvider = new ActionProvider<string>(pi, LabelRevert, Revert);
|
||||
_revertCharacterProvider = new ActionProvider<Character?>(pi, LabelRevertCharacter, RevertCharacter);
|
||||
_revertProviderLock = new ActionProvider<string, uint>(pi, LabelRevertLock, RevertLock);
|
||||
_revertCharacterProviderLock = new ActionProvider<Character?, uint>(pi, LabelRevertCharacterLock, RevertCharacterLock);
|
||||
_unlockNameProvider = new FuncProvider<string, uint, bool>(pi, LabelUnlockName, Unlock);
|
||||
_unlockProvider = new FuncProvider<Character?, uint, bool>(pi, LabelUnlock, Unlock);
|
||||
_unlockAllProvider = new FuncProvider<uint, int>(pi, LabelUnlockAll, UnlockAll);
|
||||
_revertToAutomationProvider = new FuncProvider<string, uint, bool>(pi, LabelRevertToAutomation, RevertToAutomation);
|
||||
_revertToAutomationCharacterProvider =
|
||||
new FuncProvider<Character?, uint, bool>(pi, LabelRevertToAutomationCharacter, RevertToAutomation);
|
||||
|
||||
_stateChangedProvider = new EventProvider<StateChanged.Type, nint, Lazy<string>>(pi, LabelStateChanged);
|
||||
_gPoseChangedProvider = new EventProvider<bool>(pi, LabelGPoseChanged);
|
||||
|
||||
_setItemProvider = new FuncProvider<Character?, byte, ulong, byte, uint, int>(pi, LabelSetItem,
|
||||
(idx, slot, item, stain, key) => (int)SetItem(idx, (EquipSlot)slot, item, stain, key, false));
|
||||
_setItemOnceProvider = new FuncProvider<Character?, byte, ulong, byte, uint, int>(pi, LabelSetItemOnce,
|
||||
(idx, slot, item, stain, key) => (int)SetItem(idx, (EquipSlot)slot, item, stain, key, true));
|
||||
|
||||
_setItemByActorNameProvider = new FuncProvider<string, byte, ulong, byte, uint, int>(pi, LabelSetItemByActorName,
|
||||
(name, slot, item, stain, key) => (int)SetItemByActorName(name, (EquipSlot)slot, item, stain, key, false));
|
||||
_setItemOnceByActorNameProvider = new FuncProvider<string, byte, ulong, byte, uint, int>(pi, LabelSetItemOnceByActorName,
|
||||
(name, slot, item, stain, key) => (int)SetItemByActorName(name, (EquipSlot)slot, item, stain, key, true));
|
||||
|
||||
_stateChangedEvent.Subscribe(OnStateChanged, StateChanged.Priority.GlamourerIpc);
|
||||
_gPose.Subscribe(OnGPoseChanged, GPoseService.Priority.GlamourerIpc);
|
||||
|
||||
_getDesignListProvider = new FuncProvider<(string Name, Guid Identifier)[]>(pi, LabelGetDesignList, GetDesignList);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_apiVersionProvider.Dispose();
|
||||
_apiVersionsProvider.Dispose();
|
||||
|
||||
_getAllCustomizationProvider.Dispose();
|
||||
_getAllCustomizationLockedProvider.Dispose();
|
||||
_getAllCustomizationFromCharacterProvider.Dispose();
|
||||
_getAllCustomizationFromLockedCharacterProvider.Dispose();
|
||||
|
||||
_applyAllProvider.Dispose();
|
||||
_applyAllOnceProvider.Dispose();
|
||||
_applyAllToCharacterProvider.Dispose();
|
||||
_applyAllOnceToCharacterProvider.Dispose();
|
||||
_applyOnlyEquipmentProvider.Dispose();
|
||||
_applyOnlyEquipmentToCharacterProvider.Dispose();
|
||||
_applyOnlyCustomizationProvider.Dispose();
|
||||
_applyOnlyCustomizationToCharacterProvider.Dispose();
|
||||
_applyAllProviderLock.Dispose();
|
||||
_applyAllToCharacterProviderLock.Dispose();
|
||||
_applyOnlyEquipmentProviderLock.Dispose();
|
||||
_applyOnlyEquipmentToCharacterProviderLock.Dispose();
|
||||
_applyOnlyCustomizationProviderLock.Dispose();
|
||||
_applyOnlyCustomizationToCharacterProviderLock.Dispose();
|
||||
|
||||
_applyByGuidProvider.Dispose();
|
||||
_applyByGuidOnceProvider.Dispose();
|
||||
_applyByGuidToCharacterProvider.Dispose();
|
||||
_applyByGuidOnceToCharacterProvider.Dispose();
|
||||
|
||||
_revertProvider.Dispose();
|
||||
_revertCharacterProvider.Dispose();
|
||||
_revertProviderLock.Dispose();
|
||||
_revertCharacterProviderLock.Dispose();
|
||||
_unlockNameProvider.Dispose();
|
||||
_unlockProvider.Dispose();
|
||||
_unlockAllProvider.Dispose();
|
||||
_revertToAutomationProvider.Dispose();
|
||||
_revertToAutomationCharacterProvider.Dispose();
|
||||
|
||||
_stateChangedEvent.Unsubscribe(OnStateChanged);
|
||||
_stateChangedProvider.Dispose();
|
||||
_gPose.Unsubscribe(OnGPoseChanged);
|
||||
_gPoseChangedProvider.Dispose();
|
||||
|
||||
_getDesignListProvider.Dispose();
|
||||
|
||||
_setItemProvider.Dispose();
|
||||
_setItemOnceProvider.Dispose();
|
||||
_setItemByActorNameProvider.Dispose();
|
||||
_setItemOnceByActorNameProvider.Dispose();
|
||||
}
|
||||
|
||||
private IEnumerable<ActorIdentifier> FindActors(string actorName)
|
||||
{
|
||||
if (actorName.Length == 0 || !ByteString.FromString(actorName, out var byteString))
|
||||
return [];
|
||||
|
||||
_objects.Update();
|
||||
return _objects.Keys.Where(i => i is { IsValid: true, Type: IdentifierType.Player } && i.PlayerName == byteString);
|
||||
}
|
||||
|
||||
private IEnumerable<ActorIdentifier> FindActorsRevert(string actorName)
|
||||
{
|
||||
if (actorName.Length == 0 || !ByteString.FromString(actorName, out var byteString))
|
||||
yield break;
|
||||
|
||||
_objects.Update();
|
||||
foreach (var id in _objects.Keys.Where(i => i is { IsValid: true, Type: IdentifierType.Player } && i.PlayerName == byteString)
|
||||
.Select(i => i))
|
||||
yield return id;
|
||||
|
||||
foreach (var id in _stateManager.Keys.Where(s => s.Type is IdentifierType.Player && s.PlayerName == byteString))
|
||||
yield return id;
|
||||
}
|
||||
|
||||
private IEnumerable<ActorIdentifier> FindActors(Character? character)
|
||||
{
|
||||
var id = _actors.FromObject(character, true, true, false);
|
||||
if (!id.IsValid)
|
||||
yield break;
|
||||
|
||||
yield return id;
|
||||
}
|
||||
}
|
||||
56
Glamourer/Api/IpcProviders.cs
Normal file
56
Glamourer/Api/IpcProviders.cs
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
using Dalamud.Plugin;
|
||||
using Glamourer.Api.Api;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.Api.Helpers;
|
||||
|
||||
namespace Glamourer.Api;
|
||||
|
||||
public sealed class IpcProviders : IDisposable, IApiService
|
||||
{
|
||||
private readonly List<IDisposable> _providers;
|
||||
|
||||
private readonly EventProvider _disposedProvider;
|
||||
private readonly EventProvider _initializedProvider;
|
||||
|
||||
public IpcProviders(DalamudPluginInterface pi, IGlamourerApi api)
|
||||
{
|
||||
_disposedProvider = IpcSubscribers.Disposed.Provider(pi);
|
||||
_initializedProvider = IpcSubscribers.Initialized.Provider(pi);
|
||||
_providers =
|
||||
[
|
||||
IpcSubscribers.ApiVersion.Provider(pi, api),
|
||||
|
||||
IpcSubscribers.GetDesignList.Provider(pi, api.Designs),
|
||||
IpcSubscribers.ApplyDesign.Provider(pi, api.Designs),
|
||||
IpcSubscribers.ApplyDesignName.Provider(pi, api.Designs),
|
||||
|
||||
IpcSubscribers.SetItem.Provider(pi, api.Items),
|
||||
IpcSubscribers.SetItemName.Provider(pi, api.Items),
|
||||
|
||||
IpcSubscribers.GetState.Provider(pi, api.State),
|
||||
IpcSubscribers.GetStateName.Provider(pi, api.State),
|
||||
IpcSubscribers.ApplyState.Provider(pi, api.State),
|
||||
IpcSubscribers.ApplyStateName.Provider(pi, api.State),
|
||||
IpcSubscribers.RevertState.Provider(pi, api.State),
|
||||
IpcSubscribers.RevertStateName.Provider(pi, api.State),
|
||||
IpcSubscribers.UnlockState.Provider(pi, api.State),
|
||||
IpcSubscribers.UnlockStateName.Provider(pi, api.State),
|
||||
IpcSubscribers.UnlockAll.Provider(pi, api.State),
|
||||
IpcSubscribers.RevertToAutomation.Provider(pi, api.State),
|
||||
IpcSubscribers.RevertToAutomationName.Provider(pi, api.State),
|
||||
IpcSubscribers.StateChanged.Provider(pi, api.State),
|
||||
IpcSubscribers.GPoseChanged.Provider(pi, api.State),
|
||||
];
|
||||
_initializedProvider.Invoke();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var provider in _providers)
|
||||
provider.Dispose();
|
||||
_providers.Clear();
|
||||
_initializedProvider.Dispose();
|
||||
_disposedProvider.Invoke();
|
||||
_disposedProvider.Dispose();
|
||||
}
|
||||
}
|
||||
52
Glamourer/Api/IpcSubscribers/Designs.cs
Normal file
52
Glamourer/Api/IpcSubscribers/Designs.cs
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
using Dalamud.Plugin;
|
||||
using Glamourer.Api.Api;
|
||||
using Glamourer.Api.Enums;
|
||||
using Penumbra.Api.Helpers;
|
||||
|
||||
namespace Glamourer.Api.IpcSubscribers;
|
||||
|
||||
/// <inheritdoc cref="IGlamourerApiDesigns.GetDesignList"/>
|
||||
public sealed class GetDesignList(DalamudPluginInterface pi)
|
||||
: FuncSubscriber<Dictionary<Guid, string>>(pi, Label)
|
||||
{
|
||||
/// <summary> The label. </summary>
|
||||
public const string Label = $"Glamourer.{nameof(GetDesignList)}";
|
||||
|
||||
/// <inheritdoc cref="IGlamourerApiDesigns.GetDesignList"/>
|
||||
public new Dictionary<Guid, string> Invoke()
|
||||
=> base.Invoke();
|
||||
|
||||
/// <summary> Create a provider. </summary>
|
||||
public static FuncProvider<Dictionary<Guid, string>> Provider(DalamudPluginInterface pi, IGlamourerApiDesigns api)
|
||||
=> new(pi, Label, api.GetDesignList);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IGlamourerApiDesigns.ApplyDesign"/>
|
||||
public sealed class ApplyDesign(DalamudPluginInterface pi) : FuncSubscriber<Guid, int, uint, ulong, int>(pi, Label)
|
||||
{
|
||||
/// <summary> The label. </summary>
|
||||
public const string Label = $"Glamourer.{nameof(ApplyDesign)}";
|
||||
|
||||
/// <inheritdoc cref="IGlamourerApiDesigns.ApplyDesign"/>
|
||||
public GlamourerApiEc Invoke(Guid designId, int objectIndex, uint key = 0, ApplyFlag flags = ApplyFlagEx.DesignDefault)
|
||||
=> (GlamourerApiEc)Invoke(designId, objectIndex, key, (ulong)flags);
|
||||
|
||||
/// <summary> Create a provider. </summary>
|
||||
public static FuncProvider<Guid, int, uint, ulong, int> Provider(DalamudPluginInterface pi, IGlamourerApiDesigns api)
|
||||
=> new(pi, Label, (a, b, c, d) => (int)api.ApplyDesign(a, b, c, (ApplyFlag)d));
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IGlamourerApiDesigns.ApplyDesignName"/>
|
||||
public sealed class ApplyDesignName(DalamudPluginInterface pi) : FuncSubscriber<Guid, string, uint, ulong, int>(pi, Label)
|
||||
{
|
||||
/// <summary> The label. </summary>
|
||||
public const string Label = $"Glamourer.{nameof(ApplyDesignName)}";
|
||||
|
||||
/// <inheritdoc cref="IGlamourerApiDesigns.ApplyDesignName"/>
|
||||
public GlamourerApiEc Invoke(Guid designId, string objectName, uint key = 0, ApplyFlag flags = ApplyFlagEx.DesignDefault)
|
||||
=> (GlamourerApiEc)Invoke(designId, objectName, key, (ulong)flags);
|
||||
|
||||
/// <summary> Create a provider. </summary>
|
||||
public static FuncProvider<Guid, string, uint, ulong, int> Provider(DalamudPluginInterface pi, IGlamourerApiDesigns api)
|
||||
=> new(pi, Label, (a, b, c, d) => (int)api.ApplyDesignName(a, b, c, (ApplyFlag)d));
|
||||
}
|
||||
39
Glamourer/Api/IpcSubscribers/Items.cs
Normal file
39
Glamourer/Api/IpcSubscribers/Items.cs
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
using Dalamud.Plugin;
|
||||
using Glamourer.Api.Api;
|
||||
using Glamourer.Api.Enums;
|
||||
using Penumbra.Api.Helpers;
|
||||
using Penumbra.GameData.Enums;
|
||||
|
||||
namespace Glamourer.Api.IpcSubscribers;
|
||||
|
||||
/// <inheritdoc cref="IGlamourerApiItems.SetItem"/>
|
||||
public sealed class SetItem(DalamudPluginInterface pi)
|
||||
: FuncSubscriber<int, byte, ulong, byte, uint, ulong, int>(pi, Label)
|
||||
{
|
||||
/// <summary> The label. </summary>
|
||||
public const string Label = $"Glamourer.{nameof(SetItem)}";
|
||||
|
||||
/// <inheritdoc cref="IGlamourerApiItems.SetItem"/>
|
||||
public GlamourerApiEc Invoke(int objectIndex, EquipSlot slot, ulong itemId, byte stain, uint key = 0, ApplyFlag flags = ApplyFlag.Once)
|
||||
=> (GlamourerApiEc)Invoke(objectIndex, (byte)slot, itemId, stain, key, (ulong)flags);
|
||||
|
||||
/// <summary> Create a provider. </summary>
|
||||
public static FuncProvider<int, byte, ulong, byte, uint, ulong, int> Provider(DalamudPluginInterface pi, IGlamourerApiItems api)
|
||||
=> new(pi, Label, (a, b, c, d, e, f) => (int)api.SetItem(a, (ApiEquipSlot)b, c, d, e, (ApplyFlag)f));
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IGlamourerApiItems.SetItemName"/>
|
||||
public sealed class SetItemName(DalamudPluginInterface pi)
|
||||
: FuncSubscriber<string, byte, ulong, byte, uint, ulong, int>(pi, Label)
|
||||
{
|
||||
/// <summary> The label. </summary>
|
||||
public const string Label = $"Glamourer.{nameof(SetItemName)}";
|
||||
|
||||
/// <inheritdoc cref="IGlamourerApiItems.SetItem"/>
|
||||
public GlamourerApiEc Invoke(string objectName, EquipSlot slot, ulong itemId, byte stain, uint key = 0, ApplyFlag flags = ApplyFlag.Once)
|
||||
=> (GlamourerApiEc)Invoke(objectName, (byte)slot, itemId, stain, key, (ulong)flags);
|
||||
|
||||
/// <summary> Create a provider. </summary>
|
||||
public static FuncProvider<string, byte, ulong, byte, uint, ulong, int> Provider(DalamudPluginInterface pi, IGlamourerApiItems api)
|
||||
=> new(pi, Label, (a, b, c, d, e, f) => (int)api.SetItemName(a, (ApiEquipSlot)b, c, d, e, (ApplyFlag)f));
|
||||
}
|
||||
51
Glamourer/Api/IpcSubscribers/PluginState.cs
Normal file
51
Glamourer/Api/IpcSubscribers/PluginState.cs
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
using Dalamud.Plugin;
|
||||
using Glamourer.Api.Api;
|
||||
using Penumbra.Api.Helpers;
|
||||
|
||||
namespace Glamourer.Api.IpcSubscribers;
|
||||
|
||||
/// <inheritdoc cref="IGlamourerApiBase.ApiVersion"/>
|
||||
public sealed class ApiVersion(DalamudPluginInterface pi)
|
||||
: FuncSubscriber<(int, int)>(pi, Label)
|
||||
{
|
||||
/// <summary> The label. </summary>
|
||||
public const string Label = $"Glamourer.{nameof(ApiVersion)}";
|
||||
|
||||
/// <inheritdoc cref="IGlamourerApiBase.ApiVersion"/>
|
||||
public new (int Major, int Minor) Invoke()
|
||||
=> base.Invoke();
|
||||
|
||||
/// <summary> Create a provider. </summary>
|
||||
public static FuncProvider<(int, int)> Provider(DalamudPluginInterface pi, IGlamourerApiBase api)
|
||||
=> new(pi, Label, () => api.ApiVersion);
|
||||
}
|
||||
|
||||
/// <summary> Triggered when the Glamourer API is initialized and ready. </summary>
|
||||
public static class Initialized
|
||||
{
|
||||
/// <summary> The label. </summary>
|
||||
public const string Label = $"Glamourer.{nameof(Initialized)}";
|
||||
|
||||
/// <summary> Create a new event subscriber. </summary>
|
||||
public static EventSubscriber Subscriber(DalamudPluginInterface pi, params Action[] actions)
|
||||
=> new(pi, Label, actions);
|
||||
|
||||
/// <summary> Create a provider. </summary>
|
||||
public static EventProvider Provider(DalamudPluginInterface pi)
|
||||
=> new(pi, Label);
|
||||
}
|
||||
|
||||
/// <summary> Triggered when the Glamourer API is fully disposed and unavailable. </summary>
|
||||
public static class Disposed
|
||||
{
|
||||
/// <summary> The label. </summary>
|
||||
public const string Label = $"Glamourer.{nameof(Disposed)}";
|
||||
|
||||
/// <summary> Create a new event subscriber. </summary>
|
||||
public static EventSubscriber Subscriber(DalamudPluginInterface pi, params Action[] actions)
|
||||
=> new(pi, Label, actions);
|
||||
|
||||
/// <summary> Create a provider. </summary>
|
||||
public static EventProvider Provider(DalamudPluginInterface pi)
|
||||
=> new(pi, Label);
|
||||
}
|
||||
235
Glamourer/Api/IpcSubscribers/State.cs
Normal file
235
Glamourer/Api/IpcSubscribers/State.cs
Normal file
|
|
@ -0,0 +1,235 @@
|
|||
using Dalamud.Plugin;
|
||||
using Glamourer.Api.Api;
|
||||
using Glamourer.Api.Enums;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Penumbra.Api.Helpers;
|
||||
|
||||
namespace Glamourer.Api.IpcSubscribers;
|
||||
|
||||
/// <inheritdoc cref="IGlamourerApiState.GetState"/>
|
||||
public sealed class GetState(DalamudPluginInterface pi)
|
||||
: FuncSubscriber<int, uint, (int, JObject?)>(pi, Label)
|
||||
{
|
||||
/// <summary> The label. </summary>
|
||||
public const string Label = $"Glamourer.{nameof(GetState)}";
|
||||
|
||||
/// <inheritdoc cref="IGlamourerApiState.GetState"/>
|
||||
public new (GlamourerApiEc, JObject?) Invoke(int objectIndex, uint key = 0)
|
||||
{
|
||||
var (ec, data) = base.Invoke(objectIndex, key);
|
||||
return ((GlamourerApiEc)ec, data);
|
||||
}
|
||||
|
||||
/// <summary> Create a provider. </summary>
|
||||
public static FuncProvider<int, uint, (int, JObject?)> Provider(DalamudPluginInterface pi, IGlamourerApiState api)
|
||||
=> new(pi, Label, (a, b) =>
|
||||
{
|
||||
var (ec, data) = api.GetState(a, b);
|
||||
return ((int)ec, data);
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IGlamourerApiState.GetStateName"/>
|
||||
public sealed class GetStateName(DalamudPluginInterface pi)
|
||||
: FuncSubscriber<string, uint, (int, JObject?)>(pi, Label)
|
||||
{
|
||||
/// <summary> The label. </summary>
|
||||
public const string Label = $"Glamourer.{nameof(GetStateName)}";
|
||||
|
||||
/// <inheritdoc cref="IGlamourerApiState.GetStateName"/>
|
||||
public new (GlamourerApiEc, JObject?) Invoke(string objectName, uint key = 0)
|
||||
{
|
||||
var (ec, data) = base.Invoke(objectName, key);
|
||||
return ((GlamourerApiEc)ec, data);
|
||||
}
|
||||
|
||||
/// <summary> Create a provider. </summary>
|
||||
public static FuncProvider<string, uint, (int, JObject?)> Provider(DalamudPluginInterface pi, IGlamourerApiState api)
|
||||
=> new(pi, Label, (i, k) =>
|
||||
{
|
||||
var (ec, data) = api.GetStateName(i, k);
|
||||
return ((int)ec, data);
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IGlamourerApiState.ApplyState"/>
|
||||
public sealed class ApplyState(DalamudPluginInterface pi)
|
||||
: FuncSubscriber<object, int, uint, ulong, int>(pi, Label)
|
||||
{
|
||||
/// <summary> The label. </summary>
|
||||
public const string Label = $"Glamourer.{nameof(ApplyState)}";
|
||||
|
||||
/// <inheritdoc cref="IGlamourerApiState.ApplyState"/>
|
||||
public GlamourerApiEc Invoke(JObject state, int objectIndex, uint key = 0, ApplyFlag flags = ApplyFlagEx.StateDefault)
|
||||
=> (GlamourerApiEc)Invoke(state, objectIndex, key, (ulong)flags);
|
||||
|
||||
/// <inheritdoc cref="IGlamourerApiState.ApplyState"/>
|
||||
public GlamourerApiEc Invoke(string base64State, int objectIndex, uint key = 0, ApplyFlag flags = ApplyFlagEx.StateDefault)
|
||||
=> (GlamourerApiEc)Invoke(base64State, objectIndex, key, (ulong)flags);
|
||||
|
||||
/// <summary> Create a provider. </summary>
|
||||
public static FuncProvider<object, int, uint, ulong, int> Provider(DalamudPluginInterface pi, IGlamourerApiState api)
|
||||
=> new(pi, Label, (a, b, c, d) => (int)api.ApplyState(a, b, c, (ApplyFlag)d));
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IGlamourerApiState.ApplyStateName"/>
|
||||
public sealed class ApplyStateName(DalamudPluginInterface pi)
|
||||
: FuncSubscriber<object, string, uint, ulong, int>(pi, Label)
|
||||
{
|
||||
/// <summary> The label. </summary>
|
||||
public const string Label = $"Glamourer.{nameof(ApplyStateName)}";
|
||||
|
||||
/// <inheritdoc cref="IGlamourerApiState.ApplyState"/>
|
||||
public GlamourerApiEc Invoke(JObject state, string objectName, uint key = 0, ApplyFlag flags = ApplyFlagEx.StateDefault)
|
||||
=> (GlamourerApiEc)Invoke(state, objectName, key, (ulong)flags);
|
||||
|
||||
/// <inheritdoc cref="IGlamourerApiState.ApplyState"/>
|
||||
public GlamourerApiEc Invoke(string base64State, string objectName, uint key = 0, ApplyFlag flags = ApplyFlagEx.StateDefault)
|
||||
=> (GlamourerApiEc)Invoke(base64State, objectName, key, (ulong)flags);
|
||||
|
||||
/// <summary> Create a provider. </summary>
|
||||
public static FuncProvider<object, string, uint, ulong, int> Provider(DalamudPluginInterface pi, IGlamourerApiState api)
|
||||
=> new(pi, Label, (a, b, c, d) => (int)api.ApplyStateName(a, b, c, (ApplyFlag)d));
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IGlamourerApiState.RevertState"/>
|
||||
public sealed class RevertState(DalamudPluginInterface pi)
|
||||
: FuncSubscriber<int, uint, ulong, int>(pi, Label)
|
||||
{
|
||||
/// <summary> The label. </summary>
|
||||
public const string Label = $"Glamourer.{nameof(RevertState)}";
|
||||
|
||||
/// <inheritdoc cref="IGlamourerApiState.RevertState"/>
|
||||
public GlamourerApiEc Invoke(int objectIndex, uint key = 0, ApplyFlag flags = ApplyFlagEx.RevertDefault)
|
||||
=> (GlamourerApiEc)Invoke(objectIndex, key, (ulong)flags);
|
||||
|
||||
/// <summary> Create a provider. </summary>
|
||||
public static FuncProvider<int, uint, ulong, int> Provider(DalamudPluginInterface pi, IGlamourerApiState api)
|
||||
=> new(pi, Label, (a, b, c) => (int)api.RevertState(a, b, (ApplyFlag)c));
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IGlamourerApiState.RevertStateName"/>
|
||||
public sealed class RevertStateName(DalamudPluginInterface pi)
|
||||
: FuncSubscriber<string, uint, ulong, int>(pi, Label)
|
||||
{
|
||||
/// <summary> The label. </summary>
|
||||
public const string Label = $"Glamourer.{nameof(RevertStateName)}";
|
||||
|
||||
/// <inheritdoc cref="IGlamourerApiState.RevertStateName"/>
|
||||
public GlamourerApiEc Invoke(string objectName, uint key = 0, ApplyFlag flags = ApplyFlagEx.RevertDefault)
|
||||
=> (GlamourerApiEc)Invoke(objectName, key, (ulong)flags);
|
||||
|
||||
/// <summary> Create a provider. </summary>
|
||||
public static FuncProvider<string, uint, ulong, int> Provider(DalamudPluginInterface pi, IGlamourerApiState api)
|
||||
=> new(pi, Label, (a, b, c) => (int)api.RevertStateName(a, b, (ApplyFlag)c));
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IGlamourerApiState.UnlockState"/>
|
||||
public sealed class UnlockState(DalamudPluginInterface pi)
|
||||
: FuncSubscriber<int, uint, int>(pi, Label)
|
||||
{
|
||||
/// <summary> The label. </summary>
|
||||
public const string Label = $"Glamourer.{nameof(UnlockState)}";
|
||||
|
||||
/// <inheritdoc cref="IGlamourerApiState.UnlockState"/>
|
||||
public new GlamourerApiEc Invoke(int objectIndex, uint key = 0)
|
||||
=> (GlamourerApiEc)base.Invoke(objectIndex, key);
|
||||
|
||||
/// <summary> Create a provider. </summary>
|
||||
public static FuncProvider<int, uint, int> Provider(DalamudPluginInterface pi, IGlamourerApiState api)
|
||||
=> new(pi, Label, (a, b) => (int)api.UnlockState(a, b));
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IGlamourerApiState.UnlockStateName"/>
|
||||
public sealed class UnlockStateName(DalamudPluginInterface pi)
|
||||
: FuncSubscriber<string, uint, int>(pi, Label)
|
||||
{
|
||||
/// <summary> The label. </summary>
|
||||
public const string Label = $"Glamourer.{nameof(UnlockStateName)}";
|
||||
|
||||
/// <inheritdoc cref="IGlamourerApiState.UnlockStateName"/>
|
||||
public new GlamourerApiEc Invoke(string objectName, uint key = 0)
|
||||
=> (GlamourerApiEc)base.Invoke(objectName, key);
|
||||
|
||||
/// <summary> Create a provider. </summary>
|
||||
public static FuncProvider<string, uint, int> Provider(DalamudPluginInterface pi, IGlamourerApiState api)
|
||||
=> new(pi, Label, (a, b) => (int)api.UnlockStateName(a, b));
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IGlamourerApiState.UnlockAll"/>
|
||||
public sealed class UnlockAll(DalamudPluginInterface pi)
|
||||
: FuncSubscriber<uint, int>(pi, Label)
|
||||
{
|
||||
/// <summary> The label. </summary>
|
||||
public const string Label = $"Glamourer.{nameof(UnlockAll)}";
|
||||
|
||||
/// <inheritdoc cref="IGlamourerApiState.UnlockAll"/>
|
||||
public new int Invoke(uint key)
|
||||
=> base.Invoke(key);
|
||||
|
||||
/// <summary> Create a provider. </summary>
|
||||
public static FuncProvider<uint, int> Provider(DalamudPluginInterface pi, IGlamourerApiState api)
|
||||
=> new(pi, Label, api.UnlockAll);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IGlamourerApiState.RevertToAutomation"/>
|
||||
public sealed class RevertToAutomation(DalamudPluginInterface pi)
|
||||
: FuncSubscriber<int, uint, ulong, int>(pi, Label)
|
||||
{
|
||||
/// <summary> The label. </summary>
|
||||
public const string Label = $"Glamourer.{nameof(RevertToAutomation)}";
|
||||
|
||||
/// <inheritdoc cref="IGlamourerApiState.RevertToAutomation"/>
|
||||
public GlamourerApiEc Invoke(int objectIndex, uint key = 0, ApplyFlag flags = ApplyFlagEx.RevertDefault)
|
||||
=> (GlamourerApiEc)Invoke(objectIndex, key, (ulong)flags);
|
||||
|
||||
/// <summary> Create a provider. </summary>
|
||||
public static FuncProvider<int, uint, ulong, int> Provider(DalamudPluginInterface pi, IGlamourerApiState api)
|
||||
=> new(pi, Label, (a, b, c) => (int)api.RevertToAutomation(a, b, (ApplyFlag)c));
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IGlamourerApiState.RevertToAutomationName"/>
|
||||
public sealed class RevertToAutomationName(DalamudPluginInterface pi)
|
||||
: FuncSubscriber<string, uint, ulong, int>(pi, Label)
|
||||
{
|
||||
/// <summary> The label. </summary>
|
||||
public const string Label = $"Glamourer.{nameof(RevertToAutomationName)}";
|
||||
|
||||
/// <inheritdoc cref="IGlamourerApiState.RevertToAutomationName"/>
|
||||
public GlamourerApiEc Invoke(string objectName, uint key = 0, ApplyFlag flags = ApplyFlagEx.RevertDefault)
|
||||
=> (GlamourerApiEc)Invoke(objectName, key, (ulong)flags);
|
||||
|
||||
/// <summary> Create a provider. </summary>
|
||||
public static FuncProvider<string, uint, ulong, int> Provider(DalamudPluginInterface pi, IGlamourerApiState api)
|
||||
=> new(pi, Label, (a, b, c) => (int)api.RevertToAutomationName(a, b, (ApplyFlag)c));
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IGlamourerApiState.StateChanged" />
|
||||
public static class StateChanged
|
||||
{
|
||||
/// <summary> The label. </summary>
|
||||
public const string Label = $"Penumbra.{nameof(StateChanged)}";
|
||||
|
||||
/// <summary> Create a new event subscriber. </summary>
|
||||
public static EventSubscriber<nint> Subscriber(DalamudPluginInterface pi, params Action<nint>[] actions)
|
||||
=> new(pi, Label, actions);
|
||||
|
||||
/// <summary> Create a provider. </summary>
|
||||
public static EventProvider<nint> Provider(DalamudPluginInterface pi, IGlamourerApiState api)
|
||||
=> new(pi, Label, (t => api.StateChanged += t, t => api.StateChanged -= t));
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IGlamourerApiState.GPoseChanged" />
|
||||
public static class GPoseChanged
|
||||
{
|
||||
/// <summary> The label. </summary>
|
||||
public const string Label = $"Penumbra.{nameof(GPoseChanged)}";
|
||||
|
||||
/// <summary> Create a new event subscriber. </summary>
|
||||
public static EventSubscriber<bool> Subscriber(DalamudPluginInterface pi, params Action<bool>[] actions)
|
||||
=> new(pi, Label, actions);
|
||||
|
||||
/// <summary> Create a provider. </summary>
|
||||
public static EventProvider<bool> Provider(DalamudPluginInterface pi, IGlamourerApiState api)
|
||||
=> new(pi, Label, (t => api.GPoseChanged += t, t => api.GPoseChanged -= t));
|
||||
}
|
||||
82
Glamourer/Api/ItemsApi.cs
Normal file
82
Glamourer/Api/ItemsApi.cs
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
using Glamourer.Api.Api;
|
||||
using Glamourer.Api.Enums;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Services;
|
||||
using Glamourer.State;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
namespace Glamourer.Api;
|
||||
|
||||
public class ItemsApi(ApiHelpers helpers, ItemManager itemManager, StateManager stateManager) : IGlamourerApiItems, IApiService
|
||||
{
|
||||
public GlamourerApiEc SetItem(int objectIndex, ApiEquipSlot slot, ulong itemId, byte stain, uint key, ApplyFlag flags)
|
||||
{
|
||||
var args = ApiHelpers.Args("Index", objectIndex, "Slot", slot, "ID", itemId, "Stain", stain, "Key", key, "Flags", flags);
|
||||
if (!ResolveItem(slot, itemId, out var item))
|
||||
return ApiHelpers.Return(GlamourerApiEc.ItemInvalid, args);
|
||||
|
||||
if (helpers.FindState(objectIndex) is not { } state)
|
||||
return ApiHelpers.Return(GlamourerApiEc.ActorNotFound, args);
|
||||
|
||||
if (!state.ModelData.IsHuman)
|
||||
return ApiHelpers.Return(GlamourerApiEc.ActorNotHuman, args);
|
||||
|
||||
if (!state.CanUnlock(key))
|
||||
return ApiHelpers.Return(GlamourerApiEc.InvalidKey, args);
|
||||
|
||||
var settings = new ApplySettings(Source: flags.HasFlag(ApplyFlag.Once) ? StateSource.IpcManual : StateSource.IpcFixed, Key: key);
|
||||
stateManager.ChangeEquip(state, (EquipSlot)slot, item, stain, settings);
|
||||
ApiHelpers.Lock(state, key, flags);
|
||||
return GlamourerApiEc.Success;
|
||||
}
|
||||
|
||||
public GlamourerApiEc SetItemName(string objectName, ApiEquipSlot slot, ulong itemId, byte stain, uint key, ApplyFlag flags)
|
||||
{
|
||||
var args = ApiHelpers.Args("Name", objectName, "Slot", slot, "ID", itemId, "Stain", stain, "Key", key, "Flags", flags);
|
||||
if (!ResolveItem(slot, itemId, out var item))
|
||||
return ApiHelpers.Return(GlamourerApiEc.ItemInvalid, args);
|
||||
|
||||
var settings = new ApplySettings(Source: flags.HasFlag(ApplyFlag.Once) ? StateSource.IpcManual : StateSource.IpcFixed, Key: key);
|
||||
var anyHuman = false;
|
||||
var anyFound = false;
|
||||
var anyUnlocked = false;
|
||||
foreach (var state in helpers.FindStates(objectName))
|
||||
{
|
||||
anyFound = true;
|
||||
if (!state.ModelData.IsHuman)
|
||||
continue;
|
||||
|
||||
anyHuman = true;
|
||||
if (!state.CanUnlock(key))
|
||||
continue;
|
||||
|
||||
anyUnlocked = true;
|
||||
stateManager.ChangeEquip(state, (EquipSlot)slot, item, stain, settings);
|
||||
ApiHelpers.Lock(state, key, flags);
|
||||
}
|
||||
|
||||
if (!anyFound)
|
||||
return ApiHelpers.Return(GlamourerApiEc.ActorNotFound, args);
|
||||
|
||||
if (!anyHuman)
|
||||
return ApiHelpers.Return(GlamourerApiEc.ActorNotHuman, args);
|
||||
|
||||
if (!anyUnlocked)
|
||||
return ApiHelpers.Return(GlamourerApiEc.InvalidKey, args);
|
||||
|
||||
return ApiHelpers.Return(GlamourerApiEc.Success, args);
|
||||
}
|
||||
|
||||
private bool ResolveItem(ApiEquipSlot apiSlot, ulong itemId, out EquipItem item)
|
||||
{
|
||||
var id = (CustomItemId)itemId;
|
||||
var slot = (EquipSlot)apiSlot;
|
||||
if (id.Id == 0)
|
||||
id = ItemManager.NothingId(slot);
|
||||
|
||||
item = itemManager.Resolve(slot, id);
|
||||
return item.Valid;
|
||||
}
|
||||
}
|
||||
328
Glamourer/Api/StateApi.cs
Normal file
328
Glamourer/Api/StateApi.cs
Normal file
|
|
@ -0,0 +1,328 @@
|
|||
using Glamourer.Api.Api;
|
||||
using Glamourer.Api.Enums;
|
||||
using Glamourer.Automation;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Events;
|
||||
using Glamourer.Interop.Structs;
|
||||
using Glamourer.State;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.GameData.Interop;
|
||||
using ObjectManager = Glamourer.Interop.ObjectManager;
|
||||
using StateChanged = Glamourer.Events.StateChanged;
|
||||
|
||||
namespace Glamourer.Api;
|
||||
|
||||
public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
|
||||
{
|
||||
private readonly ApiHelpers _helpers;
|
||||
private readonly StateManager _stateManager;
|
||||
private readonly DesignConverter _converter;
|
||||
private readonly Configuration _config;
|
||||
private readonly AutoDesignApplier _autoDesigns;
|
||||
private readonly ObjectManager _objects;
|
||||
private readonly StateChanged _stateChanged;
|
||||
private readonly GPoseService _gPose;
|
||||
|
||||
public StateApi(ApiHelpers helpers,
|
||||
StateManager stateManager,
|
||||
DesignConverter converter,
|
||||
Configuration config,
|
||||
AutoDesignApplier autoDesigns,
|
||||
ObjectManager objects,
|
||||
StateChanged stateChanged,
|
||||
GPoseService gPose)
|
||||
{
|
||||
_helpers = helpers;
|
||||
_stateManager = stateManager;
|
||||
_converter = converter;
|
||||
_config = config;
|
||||
_autoDesigns = autoDesigns;
|
||||
_objects = objects;
|
||||
_stateChanged = stateChanged;
|
||||
_gPose = gPose;
|
||||
_stateChanged.Subscribe(OnStateChange, Events.StateChanged.Priority.GlamourerIpc);
|
||||
_gPose.Subscribe(OnGPoseChange, GPoseService.Priority.GlamourerIpc);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_stateChanged.Unsubscribe(OnStateChange);
|
||||
_gPose.Unsubscribe(OnGPoseChange);
|
||||
}
|
||||
|
||||
public (GlamourerApiEc, JObject?) GetState(int objectIndex, uint key)
|
||||
=> Convert(_helpers.FindState(objectIndex), key);
|
||||
|
||||
public (GlamourerApiEc, JObject?) GetStateName(string objectName, uint key)
|
||||
=> Convert(_helpers.FindStates(objectName).FirstOrDefault(), key);
|
||||
|
||||
public GlamourerApiEc ApplyState(object applyState, int objectIndex, uint key, ApplyFlag flags)
|
||||
{
|
||||
var args = ApiHelpers.Args("Index", objectIndex, "Key", key, "Flags", flags);
|
||||
if (Convert(applyState, flags, out var version) is not { } design)
|
||||
return ApiHelpers.Return(GlamourerApiEc.InvalidState, args);
|
||||
|
||||
if (_helpers.FindState(objectIndex) is not { } state)
|
||||
return ApiHelpers.Return(GlamourerApiEc.ActorNotFound, args);
|
||||
|
||||
if (!state.CanUnlock(key))
|
||||
return ApiHelpers.Return(GlamourerApiEc.InvalidKey, args);
|
||||
|
||||
if (version < 3 && state.ModelData.ModelId != 0)
|
||||
return ApiHelpers.Return(GlamourerApiEc.ActorNotHuman, args);
|
||||
|
||||
ApplyDesign(state, design, key, flags);
|
||||
return ApiHelpers.Return(GlamourerApiEc.Success, args);
|
||||
}
|
||||
|
||||
public GlamourerApiEc ApplyStateName(object applyState, string objectName, uint key, ApplyFlag flags)
|
||||
{
|
||||
var args = ApiHelpers.Args("Name", objectName, "Key", key, "Flags", flags);
|
||||
if (Convert(applyState, flags, out var version) is not { } design)
|
||||
return ApiHelpers.Return(GlamourerApiEc.InvalidState, args);
|
||||
|
||||
var states = _helpers.FindExistingStates(objectName);
|
||||
|
||||
var any = false;
|
||||
var anyUnlocked = false;
|
||||
var anyHuman = false;
|
||||
foreach (var state in states)
|
||||
{
|
||||
any = true;
|
||||
if (!state.CanUnlock(key))
|
||||
continue;
|
||||
|
||||
anyUnlocked = true;
|
||||
if (version < 3 && state.ModelData.ModelId != 0)
|
||||
continue;
|
||||
|
||||
anyHuman = true;
|
||||
ApplyDesign(state, design, key, flags);
|
||||
}
|
||||
|
||||
if (any)
|
||||
ApiHelpers.Return(GlamourerApiEc.NothingDone, args);
|
||||
|
||||
if (!anyUnlocked)
|
||||
return ApiHelpers.Return(GlamourerApiEc.InvalidKey, args);
|
||||
|
||||
if (!anyHuman)
|
||||
return ApiHelpers.Return(GlamourerApiEc.ActorNotHuman, args);
|
||||
|
||||
return ApiHelpers.Return(GlamourerApiEc.Success, args);
|
||||
}
|
||||
|
||||
public GlamourerApiEc RevertState(int objectIndex, uint key, ApplyFlag flags)
|
||||
{
|
||||
var args = ApiHelpers.Args("Index", objectIndex, "Key", key, "Flags", flags);
|
||||
if (_helpers.FindExistingState(objectIndex, out var state) != GlamourerApiEc.Success)
|
||||
return ApiHelpers.Return(GlamourerApiEc.ActorNotFound, args);
|
||||
|
||||
if (state == null)
|
||||
return ApiHelpers.Return(GlamourerApiEc.NothingDone, args);
|
||||
|
||||
if (!state.CanUnlock(key))
|
||||
return ApiHelpers.Return(GlamourerApiEc.InvalidKey, args);
|
||||
|
||||
Revert(state, key, flags);
|
||||
return ApiHelpers.Return(GlamourerApiEc.Success, args);
|
||||
}
|
||||
|
||||
public GlamourerApiEc RevertStateName(string objectName, uint key, ApplyFlag flags)
|
||||
{
|
||||
var args = ApiHelpers.Args("Name", objectName, "Key", key, "Flags", flags);
|
||||
var states = _helpers.FindExistingStates(objectName);
|
||||
|
||||
var any = false;
|
||||
var anyUnlocked = false;
|
||||
foreach (var state in states)
|
||||
{
|
||||
any = true;
|
||||
if (!state.CanUnlock(key))
|
||||
continue;
|
||||
|
||||
anyUnlocked = true;
|
||||
Revert(state, key, flags);
|
||||
}
|
||||
|
||||
if (any)
|
||||
ApiHelpers.Return(GlamourerApiEc.NothingDone, args);
|
||||
|
||||
if (!anyUnlocked)
|
||||
return ApiHelpers.Return(GlamourerApiEc.InvalidKey, args);
|
||||
|
||||
return ApiHelpers.Return(GlamourerApiEc.Success, args);
|
||||
}
|
||||
|
||||
public GlamourerApiEc UnlockState(int objectIndex, uint key)
|
||||
{
|
||||
var args = ApiHelpers.Args("Index", objectIndex, "Key", key);
|
||||
if (_helpers.FindExistingState(objectIndex, out var state) != GlamourerApiEc.Success)
|
||||
return ApiHelpers.Return(GlamourerApiEc.ActorNotFound, args);
|
||||
|
||||
if (state == null)
|
||||
return ApiHelpers.Return(GlamourerApiEc.NothingDone, args);
|
||||
|
||||
if (!state.Unlock(key))
|
||||
return ApiHelpers.Return(GlamourerApiEc.InvalidKey, args);
|
||||
|
||||
return ApiHelpers.Return(GlamourerApiEc.Success, args);
|
||||
}
|
||||
|
||||
public GlamourerApiEc UnlockStateName(string objectName, uint key)
|
||||
{
|
||||
var args = ApiHelpers.Args("Name", objectName, "Key", key);
|
||||
var states = _helpers.FindExistingStates(objectName);
|
||||
|
||||
var any = false;
|
||||
var anyUnlocked = false;
|
||||
foreach (var state in states)
|
||||
{
|
||||
any = true;
|
||||
anyUnlocked |= state.Unlock(key);
|
||||
}
|
||||
|
||||
if (any)
|
||||
ApiHelpers.Return(GlamourerApiEc.NothingDone, args);
|
||||
|
||||
if (!anyUnlocked)
|
||||
return ApiHelpers.Return(GlamourerApiEc.InvalidKey, args);
|
||||
|
||||
return ApiHelpers.Return(GlamourerApiEc.Success, args);
|
||||
}
|
||||
|
||||
public int UnlockAll(uint key)
|
||||
=> _stateManager.Values.Count(state => state.Unlock(key));
|
||||
|
||||
public GlamourerApiEc RevertToAutomation(int objectIndex, uint key, ApplyFlag flags)
|
||||
{
|
||||
var args = ApiHelpers.Args("Index", objectIndex, "Key", key, "Flags", flags);
|
||||
if (_helpers.FindExistingState(objectIndex, out var state) != GlamourerApiEc.Success)
|
||||
return ApiHelpers.Return(GlamourerApiEc.ActorNotFound, args);
|
||||
|
||||
if (state == null)
|
||||
return ApiHelpers.Return(GlamourerApiEc.NothingDone, args);
|
||||
|
||||
if (!state.CanUnlock(key))
|
||||
return ApiHelpers.Return(GlamourerApiEc.InvalidKey, args);
|
||||
|
||||
RevertToAutomation(_objects[objectIndex], state, key, flags);
|
||||
return ApiHelpers.Return(GlamourerApiEc.Success, args);
|
||||
}
|
||||
|
||||
public GlamourerApiEc RevertToAutomationName(string objectName, uint key, ApplyFlag flags)
|
||||
{
|
||||
var args = ApiHelpers.Args("Name", objectName, "Key", key, "Flags", flags);
|
||||
var states = _helpers.FindExistingStates(objectName);
|
||||
|
||||
var any = false;
|
||||
var anyUnlocked = false;
|
||||
var anyReverted = false;
|
||||
foreach (var state in states)
|
||||
{
|
||||
any = true;
|
||||
if (!state.CanUnlock(key))
|
||||
continue;
|
||||
|
||||
anyUnlocked = true;
|
||||
anyReverted |= RevertToAutomation(state, key, flags) is GlamourerApiEc.Success;
|
||||
}
|
||||
|
||||
if (any)
|
||||
ApiHelpers.Return(GlamourerApiEc.NothingDone, args);
|
||||
|
||||
if (!anyReverted)
|
||||
ApiHelpers.Return(GlamourerApiEc.ActorNotFound, args);
|
||||
|
||||
if (!anyUnlocked)
|
||||
return ApiHelpers.Return(GlamourerApiEc.InvalidKey, args);
|
||||
|
||||
return ApiHelpers.Return(GlamourerApiEc.Success, args);
|
||||
}
|
||||
|
||||
public event Action<IntPtr>? StateChanged;
|
||||
public event Action<bool>? GPoseChanged;
|
||||
|
||||
private void ApplyDesign(ActorState state, DesignBase design, uint key, ApplyFlag flags)
|
||||
{
|
||||
var once = (flags & ApplyFlag.Once) != 0;
|
||||
var settings = new ApplySettings(Source: once ? StateSource.IpcManual : StateSource.IpcFixed, Key: key, MergeLinks: true,
|
||||
ResetMaterials: !once && key != 0);
|
||||
_stateManager.ApplyDesign(state, design, settings);
|
||||
ApiHelpers.Lock(state, key, flags);
|
||||
}
|
||||
|
||||
private void Revert(ActorState state, uint key, ApplyFlag flags)
|
||||
{
|
||||
var source = (flags & ApplyFlag.Once) != 0 ? StateSource.IpcManual : StateSource.IpcFixed;
|
||||
switch (flags & (ApplyFlag.Equipment | ApplyFlag.Customization))
|
||||
{
|
||||
case ApplyFlag.Equipment:
|
||||
_stateManager.ResetEquip(state, source, key);
|
||||
break;
|
||||
case ApplyFlag.Customization:
|
||||
_stateManager.ResetCustomize(state, source, key);
|
||||
break;
|
||||
case ApplyFlag.Equipment | ApplyFlag.Customization:
|
||||
_stateManager.ResetState(state, source, key);
|
||||
break;
|
||||
}
|
||||
|
||||
ApiHelpers.Lock(state, key, flags);
|
||||
}
|
||||
|
||||
private GlamourerApiEc RevertToAutomation(ActorState state, uint key, ApplyFlag flags)
|
||||
{
|
||||
_objects.Update();
|
||||
if (!_objects.TryGetValue(state.Identifier, out var actors) || !actors.Valid)
|
||||
return GlamourerApiEc.ActorNotFound;
|
||||
|
||||
foreach (var actor in actors.Objects)
|
||||
RevertToAutomation(actor, state, key, flags);
|
||||
|
||||
return GlamourerApiEc.Success;
|
||||
}
|
||||
|
||||
private void RevertToAutomation(Actor actor, ActorState state, uint key, ApplyFlag flags)
|
||||
{
|
||||
var source = (flags & ApplyFlag.Once) != 0 ? StateSource.IpcManual : StateSource.IpcFixed;
|
||||
_autoDesigns.ReapplyAutomation(actor, state.Identifier, state, true);
|
||||
_stateManager.ReapplyState(actor, state, source);
|
||||
ApiHelpers.Lock(state, key, flags);
|
||||
}
|
||||
|
||||
private (GlamourerApiEc, JObject?) Convert(ActorState? state, uint key)
|
||||
{
|
||||
if (state == null)
|
||||
return (GlamourerApiEc.ActorNotFound, null);
|
||||
|
||||
if (!state.CanUnlock(key))
|
||||
return (GlamourerApiEc.InvalidKey, null);
|
||||
|
||||
return (GlamourerApiEc.Success, _converter.ShareJObject(state, ApplicationRules.AllWithConfig(_config)));
|
||||
}
|
||||
|
||||
private DesignBase? Convert(object? state, ApplyFlag flags, out byte version)
|
||||
{
|
||||
version = DesignConverter.Version;
|
||||
return state switch
|
||||
{
|
||||
string s => _converter.FromBase64(s, (flags & ApplyFlag.Equipment) != 0, (flags & ApplyFlag.Customization) != 0, out version),
|
||||
JObject j => _converter.FromJObject(j, (flags & ApplyFlag.Equipment) != 0, (flags & ApplyFlag.Customization) != 0),
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
private void OnGPoseChange(bool gPose)
|
||||
=> GPoseChanged?.Invoke(gPose);
|
||||
|
||||
private void OnStateChange(StateChanged.Type _1, StateSource _2, ActorState _3, ActorData actors, object? _5)
|
||||
{
|
||||
if (StateChanged == null)
|
||||
return;
|
||||
|
||||
foreach (var actor in actors.Objects)
|
||||
StateChanged.Invoke(actor.Address);
|
||||
}
|
||||
}
|
||||
|
|
@ -199,8 +199,8 @@ public sealed class Design : DesignBase, ISavable, IDesignStandIn
|
|||
continue;
|
||||
}
|
||||
|
||||
var settingsDict = tok["Settings"]?.ToObject<Dictionary<string, string[]>>() ?? new Dictionary<string, string[]>();
|
||||
var settings = new SortedList<string, IList<string>>(settingsDict.Count);
|
||||
var settingsDict = tok["Settings"]?.ToObject<Dictionary<string, List<string>>>() ?? [];
|
||||
var settings = new Dictionary<string, List<string>>(settingsDict.Count);
|
||||
foreach (var (key, value) in settingsDict)
|
||||
settings.Add(key, value);
|
||||
var priority = tok["Priority"]?.ToObject<int>() ?? 0;
|
||||
|
|
|
|||
|
|
@ -34,10 +34,10 @@ public class DesignConverter(
|
|||
}
|
||||
|
||||
public string ShareBase64(Design design)
|
||||
=> ShareBase64(ShareJObject(design));
|
||||
=> ToBase64(ShareJObject(design));
|
||||
|
||||
public string ShareBase64(DesignBase design)
|
||||
=> ShareBase64(ShareJObject(design));
|
||||
=> ToBase64(ShareJObject(design));
|
||||
|
||||
public string ShareBase64(ActorState state, in ApplicationRules rules)
|
||||
=> ShareBase64(state.ModelData, state.Materials, rules);
|
||||
|
|
@ -45,7 +45,7 @@ public class DesignConverter(
|
|||
public string ShareBase64(in DesignData data, in StateMaterialManager materials, in ApplicationRules rules)
|
||||
{
|
||||
var design = Convert(data, materials, rules);
|
||||
return ShareBase64(ShareJObject(design));
|
||||
return ToBase64(ShareJObject(design));
|
||||
}
|
||||
|
||||
public DesignBase Convert(ActorState state, in ApplicationRules rules)
|
||||
|
|
@ -61,6 +61,37 @@ public class DesignConverter(
|
|||
return design;
|
||||
}
|
||||
|
||||
public DesignBase? FromJObject(JObject? jObject, bool customize, bool equip)
|
||||
{
|
||||
if (jObject == null)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
var ret = jObject["Identifier"] != null
|
||||
? Design.LoadDesign(_customize, _items, _linkLoader, jObject)
|
||||
: DesignBase.LoadDesignBase(_customize, _items, jObject);
|
||||
|
||||
ret.SetApplyMeta(MetaIndex.Wetness, customize);
|
||||
if (!customize)
|
||||
ret.ApplyCustomize = 0;
|
||||
|
||||
if (!equip)
|
||||
{
|
||||
ret.ApplyEquip = 0;
|
||||
ret.ApplyCrest = 0;
|
||||
ret.ApplyMeta &= ~(MetaFlag.HatState | MetaFlag.WeaponState | MetaFlag.VisorState);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Glamourer.Log.Warning($"Failure to parse JObject to design:\n{ex}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public DesignBase? FromBase64(string base64, bool customize, bool equip, out byte version)
|
||||
{
|
||||
DesignBase ret;
|
||||
|
|
@ -138,7 +169,7 @@ public class DesignConverter(
|
|||
return ret;
|
||||
}
|
||||
|
||||
private static string ShareBase64(JToken jObject)
|
||||
public static string ToBase64(JToken jObject)
|
||||
{
|
||||
var json = jObject.ToString(Formatting.None);
|
||||
var compressed = json.Compress(Version);
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ public class Glamourer : IDalamudPlugin
|
|||
_services.GetService<StateListener>(); // Initialize State Listener.
|
||||
_services.GetService<GlamourerWindowSystem>(); // initialize ui.
|
||||
_services.GetService<CommandService>(); // initialize commands.
|
||||
_services.GetService<GlamourerIpc>(); // initialize IPC.
|
||||
_services.GetService<IpcProviders>(); // initialize IPC.
|
||||
Log.Information($"Glamourer v{Version} loaded successfully.");
|
||||
}
|
||||
catch
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using ImGuiNET;
|
||||
using Glamourer.Gui.Tabs.DebugTab.IpcTester;
|
||||
using ImGuiNET;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using OtterGui.Raii;
|
||||
using Penumbra.GameData.Gui.Debug;
|
||||
|
|
|
|||
78
Glamourer/Gui/Tabs/DebugTab/IpcTester/DesignIpcTester.cs
Normal file
78
Glamourer/Gui/Tabs/DebugTab/IpcTester/DesignIpcTester.cs
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.Utility;
|
||||
using Dalamud.Plugin;
|
||||
using Glamourer.Api.Enums;
|
||||
using Glamourer.Api.IpcSubscribers;
|
||||
using ImGuiNET;
|
||||
using OtterGui;
|
||||
using OtterGui.Raii;
|
||||
using OtterGui.Services;
|
||||
|
||||
namespace Glamourer.Gui.Tabs.DebugTab.IpcTester;
|
||||
|
||||
public class DesignIpcTester(DalamudPluginInterface pluginInterface) : IUiService
|
||||
{
|
||||
private Dictionary<Guid, string> _designs = [];
|
||||
private int _gameObjectIndex;
|
||||
private string _gameObjectName = string.Empty;
|
||||
private uint _key;
|
||||
private ApplyFlag _flags = ApplyFlagEx.DesignDefault;
|
||||
private Guid? _design;
|
||||
private string _designText = string.Empty;
|
||||
private GlamourerApiEc _lastError;
|
||||
|
||||
public void Draw()
|
||||
{
|
||||
using var tree = ImRaii.TreeNode("Designs");
|
||||
if (!tree)
|
||||
return;
|
||||
|
||||
IpcTesterHelpers.IndexInput(ref _gameObjectIndex);
|
||||
IpcTesterHelpers.KeyInput(ref _key);
|
||||
IpcTesterHelpers.NameInput(ref _gameObjectName);
|
||||
ImGuiUtil.GuidInput("##identifier", "Design Identifier...", string.Empty, ref _design, ref _designText,
|
||||
ImGui.GetContentRegionAvail().X);
|
||||
IpcTesterHelpers.DrawFlagInput(ref _flags);
|
||||
|
||||
using var table = ImRaii.Table("##table", 2, ImGuiTableFlags.SizingFixedFit);
|
||||
|
||||
IpcTesterHelpers.DrawIntro("Last Error");
|
||||
ImGui.TextUnformatted(_lastError.ToString());
|
||||
|
||||
IpcTesterHelpers.DrawIntro(GetDesignList.Label);
|
||||
DrawDesignsPopup();
|
||||
if (ImGui.Button("Get##Designs"))
|
||||
{
|
||||
_designs = new GetDesignList(pluginInterface).Invoke();
|
||||
ImGui.OpenPopup("Designs");
|
||||
}
|
||||
|
||||
IpcTesterHelpers.DrawIntro(ApplyDesign.Label);
|
||||
if (ImGuiUtil.DrawDisabledButton("Apply##Idx", Vector2.Zero, string.Empty, !_design.HasValue))
|
||||
_lastError = new ApplyDesign(pluginInterface).Invoke(_design!.Value, _gameObjectIndex, _key, _flags);
|
||||
|
||||
IpcTesterHelpers.DrawIntro(ApplyDesignName.Label);
|
||||
if (ImGuiUtil.DrawDisabledButton("Apply##Name", Vector2.Zero, string.Empty, !_design.HasValue))
|
||||
_lastError = new ApplyDesignName(pluginInterface).Invoke(_design!.Value, _gameObjectName, _key, _flags);
|
||||
}
|
||||
|
||||
private void DrawDesignsPopup()
|
||||
{
|
||||
ImGui.SetNextWindowSize(ImGuiHelpers.ScaledVector2(500, 500));
|
||||
using var p = ImRaii.Popup("Designs");
|
||||
if (!p)
|
||||
return;
|
||||
|
||||
using var table = ImRaii.Table("Designs", 2, ImGuiTableFlags.SizingFixedFit);
|
||||
foreach (var (guid, name) in _designs)
|
||||
{
|
||||
ImGuiUtil.DrawTableColumn(name);
|
||||
using var f = ImRaii.PushFont(UiBuilder.MonoFont);
|
||||
ImGui.TableNextColumn();
|
||||
ImGuiUtil.CopyOnClickSelectable(guid.ToString());
|
||||
}
|
||||
|
||||
if (ImGui.Button("Close", -Vector2.UnitX) || !ImGui.IsWindowFocused())
|
||||
ImGui.CloseCurrentPopup();
|
||||
}
|
||||
}
|
||||
60
Glamourer/Gui/Tabs/DebugTab/IpcTester/IpcTesterHelpers.cs
Normal file
60
Glamourer/Gui/Tabs/DebugTab/IpcTester/IpcTesterHelpers.cs
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
using Glamourer.Api.Enums;
|
||||
using Glamourer.Designs;
|
||||
using ImGuiNET;
|
||||
using OtterGui;
|
||||
using static Penumbra.GameData.Files.ShpkFile;
|
||||
|
||||
namespace Glamourer.Gui.Tabs.DebugTab.IpcTester;
|
||||
|
||||
public static class IpcTesterHelpers
|
||||
{
|
||||
public static void DrawFlagInput(ref ApplyFlag flags)
|
||||
{
|
||||
var value = (flags & ApplyFlag.Once) != 0;
|
||||
if (ImGui.Checkbox("Apply Once", ref value))
|
||||
flags = value ? flags | ApplyFlag.Once : flags & ~ApplyFlag.Once;
|
||||
|
||||
ImGui.SameLine();
|
||||
value = (flags & ApplyFlag.Equipment) != 0;
|
||||
if (ImGui.Checkbox("Apply Equipment", ref value))
|
||||
flags = value ? flags | ApplyFlag.Equipment : flags & ~ApplyFlag.Equipment;
|
||||
|
||||
ImGui.SameLine();
|
||||
value = (flags & ApplyFlag.Customization) != 0;
|
||||
if (ImGui.Checkbox("Apply Customization", ref value))
|
||||
flags = value ? flags | ApplyFlag.Customization : flags & ~ApplyFlag.Customization;
|
||||
|
||||
ImGui.SameLine();
|
||||
value = (flags & ApplyFlag.Lock) != 0;
|
||||
if (ImGui.Checkbox("Lock Actor", ref value))
|
||||
flags = value ? flags | ApplyFlag.Lock : flags & ~ApplyFlag.Lock;
|
||||
}
|
||||
|
||||
public static void IndexInput(ref int index)
|
||||
{
|
||||
ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X / 2);
|
||||
ImGui.InputInt("Game Object Index", ref index, 0, 0);
|
||||
}
|
||||
|
||||
public static void KeyInput(ref uint key)
|
||||
{
|
||||
ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X / 2);
|
||||
var keyI = (int)key;
|
||||
if (ImGui.InputInt("Key", ref keyI, 0, 0))
|
||||
key = (uint)keyI;
|
||||
}
|
||||
|
||||
public static void NameInput(ref string name)
|
||||
{
|
||||
ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X);
|
||||
ImGui.InputTextWithHint("##gameObject", "Character Name...", ref name, 64);
|
||||
}
|
||||
|
||||
public static void DrawIntro(string intro)
|
||||
{
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.AlignTextToFramePadding();
|
||||
ImGui.TextUnformatted(intro);
|
||||
ImGui.TableNextColumn();
|
||||
}
|
||||
}
|
||||
271
Glamourer/Gui/Tabs/DebugTab/IpcTester/IpcTesterPanel.cs
Normal file
271
Glamourer/Gui/Tabs/DebugTab/IpcTester/IpcTesterPanel.cs
Normal file
|
|
@ -0,0 +1,271 @@
|
|||
using Dalamud.Plugin;
|
||||
using Dalamud.Plugin.Services;
|
||||
using FFXIVClientStructs.FFXIV.Client.System.Framework;
|
||||
using Glamourer.Api;
|
||||
using Glamourer.Api.Enums;
|
||||
using Glamourer.Api.IpcSubscribers;
|
||||
using Glamourer.Interop;
|
||||
using ImGuiNET;
|
||||
using OtterGui;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Gui;
|
||||
using Penumbra.GameData.Gui.Debug;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
namespace Glamourer.Gui.Tabs.DebugTab.IpcTester;
|
||||
|
||||
public class IpcTesterPanel(
|
||||
DalamudPluginInterface pluginInterface,
|
||||
DesignIpcTester designs,
|
||||
ItemsIpcTester items,
|
||||
StateIpcTester state,
|
||||
IFramework framework) : IGameDataDrawer
|
||||
{
|
||||
public string Label
|
||||
=> "IPC Tester";
|
||||
|
||||
public bool Disabled
|
||||
=> false;
|
||||
|
||||
private DateTime _lastUpdate;
|
||||
private bool _subscribed = false;
|
||||
|
||||
public void Draw()
|
||||
{
|
||||
try
|
||||
{
|
||||
_lastUpdate = framework.LastUpdateUTC.AddSeconds(1);
|
||||
Subscribe();
|
||||
ImGui.TextUnformatted(ApiVersion.Label);
|
||||
var (major, minor) = new ApiVersion(pluginInterface).Invoke();
|
||||
ImGui.SameLine();
|
||||
ImGui.TextUnformatted($"({major}.{minor:D4})");
|
||||
|
||||
designs.Draw();
|
||||
items.Draw();
|
||||
state.Draw();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Glamourer.Log.Error($"Error during IPC Tests:\n{e}");
|
||||
}
|
||||
//ImGui.InputInt("Game Object Index", ref _gameObjectIndex, 0, 0);
|
||||
//ImGui.InputTextWithHint("##gameObject", "Character Name...", ref _gameObjectName, 64);
|
||||
//ImGui.InputTextWithHint("##base64", "Design Base64...", ref _base64Apply, 2047);
|
||||
//ImGui.InputTextWithHint("##identifier", "Design identifier...", ref _designIdentifier, 36);
|
||||
//DrawItemInput();
|
||||
//using var table = ImRaii.Table("##ipc", 2, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg);
|
||||
//if (!table)
|
||||
// return;
|
||||
//
|
||||
//ImGuiUtil.DrawTableColumn();
|
||||
//ImGui.TableNextColumn();
|
||||
//var base64 = GlamourerIpc.GetAllCustomizationSubscriber(_pluginInterface).Invoke(_gameObjectName);
|
||||
//if (base64 != null)
|
||||
// ImGuiUtil.CopyOnClickSelectable(base64);
|
||||
//else
|
||||
// ImGui.TextUnformatted("Error");
|
||||
//
|
||||
//ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelGetAllCustomizationFromCharacter);
|
||||
//ImGui.TableNextColumn();
|
||||
//base64 = GlamourerIpc.GetAllCustomizationFromCharacterSubscriber(_pluginInterface)
|
||||
// .Invoke(_objectManager.GetDalamudCharacter(_gameObjectIndex));
|
||||
//if (base64 != null)
|
||||
// ImGuiUtil.CopyOnClickSelectable(base64);
|
||||
//else
|
||||
// ImGui.TextUnformatted("Error");
|
||||
//
|
||||
//ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelGetAllCustomizationFromLockedCharacter);
|
||||
//ImGui.TableNextColumn();
|
||||
//var base64Locked = GlamourerIpc.GetAllCustomizationFromLockedCharacterSubscriber(_pluginInterface)
|
||||
// .Invoke(_objectManager.GetDalamudCharacter(_gameObjectIndex), 1337);
|
||||
//if (base64Locked != null)
|
||||
// ImGuiUtil.CopyOnClickSelectable(base64Locked);
|
||||
//else
|
||||
// ImGui.TextUnformatted("Error");
|
||||
//
|
||||
//ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelRevert);
|
||||
//ImGui.TableNextColumn();
|
||||
//if (ImGui.Button("Revert##Name"))
|
||||
// GlamourerIpc.RevertSubscriber(_pluginInterface).Invoke(_gameObjectName);
|
||||
//
|
||||
//ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelRevertCharacter);
|
||||
//ImGui.TableNextColumn();
|
||||
//if (ImGui.Button("Revert##Character"))
|
||||
// GlamourerIpc.RevertCharacterSubscriber(_pluginInterface).Invoke(_objectManager.GetDalamudCharacter(_gameObjectIndex));
|
||||
//
|
||||
//ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelApplyAll);
|
||||
//ImGui.TableNextColumn();
|
||||
//if (ImGui.Button("Apply##AllName"))
|
||||
// GlamourerIpc.ApplyAllSubscriber(_pluginInterface).Invoke(_base64Apply, _gameObjectName);
|
||||
//
|
||||
//ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelApplyAllOnce);
|
||||
//ImGui.TableNextColumn();
|
||||
//if (ImGui.Button("Apply Once##AllName"))
|
||||
// GlamourerIpc.ApplyAllOnceSubscriber(_pluginInterface).Invoke(_base64Apply, _gameObjectName);
|
||||
//
|
||||
//ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelApplyAllToCharacter);
|
||||
//ImGui.TableNextColumn();
|
||||
//if (ImGui.Button("Apply##AllCharacter"))
|
||||
// GlamourerIpc.ApplyAllToCharacterSubscriber(_pluginInterface)
|
||||
// .Invoke(_base64Apply, _objectManager.GetDalamudCharacter(_gameObjectIndex));
|
||||
//
|
||||
//ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelApplyAllOnceToCharacter);
|
||||
//ImGui.TableNextColumn();
|
||||
//if (ImGui.Button("Apply Once##AllCharacter"))
|
||||
// GlamourerIpc.ApplyAllOnceToCharacterSubscriber(_pluginInterface)
|
||||
// .Invoke(_base64Apply, _objectManager.GetDalamudCharacter(_gameObjectIndex));
|
||||
//
|
||||
//ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelApplyOnlyEquipment);
|
||||
//ImGui.TableNextColumn();
|
||||
//if (ImGui.Button("Apply##EquipName"))
|
||||
// GlamourerIpc.ApplyOnlyEquipmentSubscriber(_pluginInterface).Invoke(_base64Apply, _gameObjectName);
|
||||
//
|
||||
//ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelApplyOnlyEquipmentToCharacter);
|
||||
//ImGui.TableNextColumn();
|
||||
//if (ImGui.Button("Apply##EquipCharacter"))
|
||||
// GlamourerIpc.ApplyOnlyEquipmentToCharacterSubscriber(_pluginInterface)
|
||||
// .Invoke(_base64Apply, _objectManager.GetDalamudCharacter(_gameObjectIndex));
|
||||
//
|
||||
//ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelApplyOnlyCustomization);
|
||||
//ImGui.TableNextColumn();
|
||||
//if (ImGui.Button("Apply##CustomizeName"))
|
||||
// GlamourerIpc.ApplyOnlyCustomizationSubscriber(_pluginInterface).Invoke(_base64Apply, _gameObjectName);
|
||||
//
|
||||
//ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelApplyOnlyCustomizationToCharacter);
|
||||
//ImGui.TableNextColumn();
|
||||
//if (ImGui.Button("Apply##CustomizeCharacter"))
|
||||
// GlamourerIpc.ApplyOnlyCustomizationToCharacterSubscriber(_pluginInterface)
|
||||
// .Invoke(_base64Apply, _objectManager.GetDalamudCharacter(_gameObjectIndex));
|
||||
//
|
||||
//ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelApplyByGuid);
|
||||
//ImGui.TableNextColumn();
|
||||
//if (ImGui.Button("Apply##ByGuidName") && Guid.TryParse(_designIdentifier, out var guid1))
|
||||
// GlamourerIpc.ApplyByGuidSubscriber(_pluginInterface).Invoke(guid1, _gameObjectName);
|
||||
//
|
||||
//ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelApplyByGuidOnce);
|
||||
//ImGui.TableNextColumn();
|
||||
//if (ImGui.Button("Apply Once##ByGuidName") && Guid.TryParse(_designIdentifier, out var guid1Once))
|
||||
// GlamourerIpc.ApplyByGuidOnceSubscriber(_pluginInterface).Invoke(guid1Once, _gameObjectName);
|
||||
//
|
||||
//ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelApplyByGuidToCharacter);
|
||||
//ImGui.TableNextColumn();
|
||||
//if (ImGui.Button("Apply##ByGuidCharacter") && Guid.TryParse(_designIdentifier, out var guid2))
|
||||
// GlamourerIpc.ApplyByGuidToCharacterSubscriber(_pluginInterface)
|
||||
// .Invoke(guid2, _objectManager.GetDalamudCharacter(_gameObjectIndex));
|
||||
//
|
||||
//ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelApplyByGuidOnceToCharacter);
|
||||
//ImGui.TableNextColumn();
|
||||
//if (ImGui.Button("Apply Once##ByGuidCharacter") && Guid.TryParse(_designIdentifier, out var guid2Once))
|
||||
// GlamourerIpc.ApplyByGuidOnceToCharacterSubscriber(_pluginInterface)
|
||||
// .Invoke(guid2Once, _objectManager.GetDalamudCharacter(_gameObjectIndex));
|
||||
//
|
||||
//ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelApplyAllLock);
|
||||
//ImGui.TableNextColumn();
|
||||
//if (ImGui.Button("Apply With Lock##CustomizeCharacter"))
|
||||
// GlamourerIpc.ApplyAllToCharacterLockSubscriber(_pluginInterface)
|
||||
// .Invoke(_base64Apply, _objectManager.GetDalamudCharacter(_gameObjectIndex), 1337);
|
||||
//
|
||||
//ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelUnlock);
|
||||
//ImGui.TableNextColumn();
|
||||
//if (ImGui.Button("Unlock##CustomizeCharacter"))
|
||||
// GlamourerIpc.UnlockSubscriber(_pluginInterface)
|
||||
// .Invoke(_objectManager.GetDalamudCharacter(_gameObjectIndex), 1337);
|
||||
//
|
||||
//ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelUnlockAll);
|
||||
//ImGui.TableNextColumn();
|
||||
//if (ImGui.Button("Unlock All##CustomizeCharacter"))
|
||||
// GlamourerIpc.UnlockAllSubscriber(_pluginInterface)
|
||||
// .Invoke(1337);
|
||||
//
|
||||
//ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelRevertToAutomation);
|
||||
//ImGui.TableNextColumn();
|
||||
//if (ImGui.Button("Revert##CustomizeCharacter"))
|
||||
// GlamourerIpc.RevertToAutomationCharacterSubscriber(_pluginInterface)
|
||||
// .Invoke(_objectManager.GetDalamudCharacter(_gameObjectIndex), 1337);
|
||||
//
|
||||
//
|
||||
//ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelGetDesignList);
|
||||
//ImGui.TableNextColumn();
|
||||
//var designList = GlamourerIpc.GetDesignListSubscriber(_pluginInterface)
|
||||
// .Invoke();
|
||||
//if (ImGui.Button($"Copy {designList.Length} Designs to Clipboard###CopyDesignList"))
|
||||
// ImGui.SetClipboardText(string.Join("\n", designList));
|
||||
//
|
||||
//ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelSetItem);
|
||||
//ImGui.TableNextColumn();
|
||||
//if (ImGui.Button("Set##SetItem"))
|
||||
// _setItemEc = (GlamourerApiEc)GlamourerIpc.SetItemSubscriber(_pluginInterface)
|
||||
// .Invoke(_objectManager.GetDalamudCharacter(_gameObjectIndex), (byte)_slot, _customItemId.Id, _stainId.Id, 1337);
|
||||
//if (_setItemEc != GlamourerApiEc.Success)
|
||||
//{
|
||||
// ImGui.SameLine();
|
||||
// ImGui.TextUnformatted(_setItemEc.ToString());
|
||||
//}
|
||||
//
|
||||
//ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelSetItemOnce);
|
||||
//ImGui.TableNextColumn();
|
||||
//if (ImGui.Button("Set Once##SetItem"))
|
||||
// _setItemOnceEc = (GlamourerApiEc)GlamourerIpc.SetItemOnceSubscriber(_pluginInterface)
|
||||
// .Invoke(_objectManager.GetDalamudCharacter(_gameObjectIndex), (byte)_slot, _customItemId.Id, _stainId.Id, 1337);
|
||||
//if (_setItemOnceEc != GlamourerApiEc.Success)
|
||||
//{
|
||||
// ImGui.SameLine();
|
||||
// ImGui.TextUnformatted(_setItemOnceEc.ToString());
|
||||
//}
|
||||
//
|
||||
//ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelSetItemByActorName);
|
||||
//ImGui.TableNextColumn();
|
||||
//if (ImGui.Button("Set##SetItemByActorName"))
|
||||
// _setItemByActorNameEc = (GlamourerApiEc)GlamourerIpc.SetItemByActorNameSubscriber(_pluginInterface)
|
||||
// .Invoke(_gameObjectName, (byte)_slot, _customItemId.Id, _stainId.Id, 1337);
|
||||
//if (_setItemByActorNameEc != GlamourerApiEc.Success)
|
||||
//{
|
||||
// ImGui.SameLine();
|
||||
// ImGui.TextUnformatted(_setItemByActorNameEc.ToString());
|
||||
//}
|
||||
//
|
||||
//ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelSetItemOnceByActorName);
|
||||
//ImGui.TableNextColumn();
|
||||
//if (ImGui.Button("Set Once##SetItemByActorName"))
|
||||
// _setItemOnceByActorNameEc = (GlamourerApiEc)GlamourerIpc.SetItemOnceByActorNameSubscriber(_pluginInterface)
|
||||
// .Invoke(_gameObjectName, (byte)_slot, _customItemId.Id, _stainId.Id, 1337);
|
||||
//if (_setItemOnceByActorNameEc != GlamourerApiEc.Success)
|
||||
//{
|
||||
// ImGui.SameLine();
|
||||
// ImGui.TextUnformatted(_setItemOnceByActorNameEc.ToString());
|
||||
//}
|
||||
}
|
||||
|
||||
private void Subscribe()
|
||||
{
|
||||
if (_subscribed)
|
||||
return;
|
||||
|
||||
Glamourer.Log.Debug("[IPCTester] Subscribed to IPC events for IPC tester.");
|
||||
state.GPoseChanged.Enable();
|
||||
state.StateChanged.Enable();
|
||||
framework.Update += CheckUnsubscribe;
|
||||
_subscribed = true;
|
||||
}
|
||||
|
||||
private void CheckUnsubscribe(IFramework framework1)
|
||||
{
|
||||
if (_lastUpdate > framework.LastUpdateUTC)
|
||||
return;
|
||||
|
||||
Unsubscribe();
|
||||
framework.Update -= CheckUnsubscribe;
|
||||
}
|
||||
|
||||
private void Unsubscribe()
|
||||
{
|
||||
if (!_subscribed)
|
||||
return;
|
||||
|
||||
Glamourer.Log.Debug("[IPCTester] Unsubscribed from IPC events for IPC tester.");
|
||||
_subscribed = false;
|
||||
state.GPoseChanged.Disable();
|
||||
state.StateChanged.Disable();
|
||||
}
|
||||
}
|
||||
66
Glamourer/Gui/Tabs/DebugTab/IpcTester/ItemsIpcTester.cs
Normal file
66
Glamourer/Gui/Tabs/DebugTab/IpcTester/ItemsIpcTester.cs
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
using Dalamud.Plugin;
|
||||
using Glamourer.Api.Enums;
|
||||
using Glamourer.Api.IpcSubscribers;
|
||||
using ImGuiNET;
|
||||
using OtterGui;
|
||||
using OtterGui.Raii;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Gui;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
namespace Glamourer.Gui.Tabs.DebugTab.IpcTester;
|
||||
|
||||
public class ItemsIpcTester(DalamudPluginInterface pluginInterface) : IUiService
|
||||
{
|
||||
private int _gameObjectIndex;
|
||||
private string _gameObjectName = string.Empty;
|
||||
private uint _key;
|
||||
private ApplyFlag _flags = ApplyFlagEx.DesignDefault;
|
||||
private CustomItemId _customItemId;
|
||||
private StainId _stainId;
|
||||
private EquipSlot _slot = EquipSlot.Head;
|
||||
private GlamourerApiEc _lastError;
|
||||
|
||||
public void Draw()
|
||||
{
|
||||
using var tree = ImRaii.TreeNode("Items");
|
||||
if (!tree)
|
||||
return;
|
||||
|
||||
IpcTesterHelpers.IndexInput(ref _gameObjectIndex);
|
||||
IpcTesterHelpers.KeyInput(ref _key);
|
||||
DrawItemInput();
|
||||
IpcTesterHelpers.NameInput(ref _gameObjectName);
|
||||
IpcTesterHelpers.DrawFlagInput(ref _flags);
|
||||
using var table = ImRaii.Table("##table", 2, ImGuiTableFlags.SizingFixedFit);
|
||||
|
||||
IpcTesterHelpers.DrawIntro("Last Error");
|
||||
ImGui.TextUnformatted(_lastError.ToString());
|
||||
|
||||
IpcTesterHelpers.DrawIntro(SetItem.Label);
|
||||
if (ImGui.Button("Set##Idx"))
|
||||
_lastError = new SetItem(pluginInterface).Invoke(_gameObjectIndex, _slot, _customItemId.Id, _stainId.Id, _key, _flags);
|
||||
|
||||
IpcTesterHelpers.DrawIntro(SetItemName.Label);
|
||||
if (ImGui.Button("Set##Name"))
|
||||
_lastError = new SetItemName(pluginInterface).Invoke(_gameObjectName, _slot, _customItemId.Id, _stainId.Id, _key, _flags);
|
||||
}
|
||||
|
||||
private void DrawItemInput()
|
||||
{
|
||||
var tmp = _customItemId.Id;
|
||||
var width = ImGui.GetContentRegionAvail().X / 2;
|
||||
ImGui.SetNextItemWidth(width);
|
||||
if (ImGuiUtil.InputUlong("Custom Item ID", ref tmp))
|
||||
_customItemId = tmp;
|
||||
EquipSlotCombo.Draw("Equip Slot", string.Empty, ref _slot, width);
|
||||
var value = (int)_stainId.Id;
|
||||
ImGui.SetNextItemWidth(width);
|
||||
if (ImGui.InputInt("Stain ID", ref value, 1, 3))
|
||||
{
|
||||
value = Math.Clamp(value, 0, byte.MaxValue);
|
||||
_stainId = (StainId)value;
|
||||
}
|
||||
}
|
||||
}
|
||||
184
Glamourer/Gui/Tabs/DebugTab/IpcTester/StateIpcTester.cs
Normal file
184
Glamourer/Gui/Tabs/DebugTab/IpcTester/StateIpcTester.cs
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.Utility;
|
||||
using Dalamud.Plugin;
|
||||
using Glamourer.Api.Enums;
|
||||
using Glamourer.Api.IpcSubscribers;
|
||||
using Glamourer.Designs;
|
||||
using ImGuiNET;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using OtterGui;
|
||||
using OtterGui.Raii;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.Api.Helpers;
|
||||
using Penumbra.GameData.Interop;
|
||||
using Penumbra.String;
|
||||
|
||||
namespace Glamourer.Gui.Tabs.DebugTab.IpcTester;
|
||||
|
||||
public class StateIpcTester : IUiService, IDisposable
|
||||
{
|
||||
private readonly DalamudPluginInterface _pluginInterface;
|
||||
|
||||
private int _gameObjectIndex;
|
||||
private string _gameObjectName = string.Empty;
|
||||
private uint _key;
|
||||
private ApplyFlag _flags = ApplyFlagEx.DesignDefault;
|
||||
private GlamourerApiEc _lastError;
|
||||
private JObject? _state;
|
||||
private string? _stateString;
|
||||
private string _base64State = string.Empty;
|
||||
|
||||
public readonly EventSubscriber<nint> StateChanged;
|
||||
private nint _lastStateChangeActor;
|
||||
private ByteString _lastStateChangeName = ByteString.Empty;
|
||||
private DateTime _lastStateChangeTime;
|
||||
|
||||
public readonly EventSubscriber<bool> GPoseChanged;
|
||||
private bool _lastGPoseChangeValue;
|
||||
private DateTime _lastGPoseChangeTime;
|
||||
|
||||
private int _numUnlocked;
|
||||
|
||||
public StateIpcTester(DalamudPluginInterface pluginInterface)
|
||||
{
|
||||
_pluginInterface = pluginInterface;
|
||||
StateChanged = Api.IpcSubscribers.StateChanged.Subscriber(_pluginInterface, OnStateChanged);
|
||||
GPoseChanged = Api.IpcSubscribers.GPoseChanged.Subscriber(_pluginInterface, OnGPoseChange);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
StateChanged.Dispose();
|
||||
GPoseChanged.Dispose();
|
||||
}
|
||||
|
||||
public void Draw()
|
||||
{
|
||||
using var tree = ImRaii.TreeNode("State");
|
||||
if (!tree)
|
||||
return;
|
||||
|
||||
IpcTesterHelpers.IndexInput(ref _gameObjectIndex);
|
||||
IpcTesterHelpers.KeyInput(ref _key);
|
||||
IpcTesterHelpers.NameInput(ref _gameObjectName);
|
||||
IpcTesterHelpers.DrawFlagInput(ref _flags);
|
||||
ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X);
|
||||
ImGui.InputTextWithHint("##base64", "Base 64 State...", ref _base64State, 2000);
|
||||
using var table = ImRaii.Table("##table", 2, ImGuiTableFlags.SizingFixedFit);
|
||||
|
||||
IpcTesterHelpers.DrawIntro("Last Error");
|
||||
ImGui.TextUnformatted(_lastError.ToString());
|
||||
IpcTesterHelpers.DrawIntro("Last State Change");
|
||||
PrintName();
|
||||
IpcTesterHelpers.DrawIntro("Last GPose Change");
|
||||
ImGui.TextUnformatted($"{_lastGPoseChangeValue} at {_lastGPoseChangeTime.ToLocalTime().TimeOfDay}");
|
||||
|
||||
|
||||
IpcTesterHelpers.DrawIntro(GetState.Label);
|
||||
DrawStatePopup();
|
||||
if (ImGui.Button("Get##Idx"))
|
||||
{
|
||||
(_lastError, _state) = new GetState(_pluginInterface).Invoke(_gameObjectIndex, _key);
|
||||
_stateString = _state?.ToString(Formatting.Indented) ?? "No State Available";
|
||||
ImGui.OpenPopup("State");
|
||||
}
|
||||
|
||||
IpcTesterHelpers.DrawIntro(GetStateName.Label);
|
||||
if (ImGui.Button("Get##Name"))
|
||||
{
|
||||
(_lastError, _state) = new GetStateName(_pluginInterface).Invoke(_gameObjectName, _key);
|
||||
_stateString = _state?.ToString(Formatting.Indented) ?? "No State Available";
|
||||
ImGui.OpenPopup("State");
|
||||
}
|
||||
|
||||
IpcTesterHelpers.DrawIntro(ApplyState.Label);
|
||||
if (ImGuiUtil.DrawDisabledButton("Apply Last##Idx", Vector2.Zero, string.Empty, _state == null))
|
||||
_lastError = new ApplyState(_pluginInterface).Invoke(_state!, _gameObjectIndex, _key, _flags);
|
||||
ImGui.SameLine();
|
||||
if (ImGui.Button("Apply Base64##Idx"))
|
||||
_lastError = new ApplyState(_pluginInterface).Invoke(_base64State, _gameObjectIndex, _key, _flags);
|
||||
|
||||
IpcTesterHelpers.DrawIntro(ApplyStateName.Label);
|
||||
if (ImGuiUtil.DrawDisabledButton("Apply Last##Name", Vector2.Zero, string.Empty, _state == null))
|
||||
_lastError = new ApplyStateName(_pluginInterface).Invoke(_state!, _gameObjectName, _key, _flags);
|
||||
ImGui.SameLine();
|
||||
if (ImGui.Button("Apply Base64##Name"))
|
||||
_lastError = new ApplyStateName(_pluginInterface).Invoke(_base64State, _gameObjectName, _key, _flags);
|
||||
|
||||
IpcTesterHelpers.DrawIntro(RevertState.Label);
|
||||
if (ImGui.Button("Revert##Idx"))
|
||||
_lastError = new RevertState(_pluginInterface).Invoke(_gameObjectIndex, _key, _flags);
|
||||
|
||||
IpcTesterHelpers.DrawIntro(RevertStateName.Label);
|
||||
if (ImGui.Button("Revert##Name"))
|
||||
_lastError = new RevertStateName(_pluginInterface).Invoke(_gameObjectName, _key, _flags);
|
||||
|
||||
IpcTesterHelpers.DrawIntro(UnlockState.Label);
|
||||
if (ImGui.Button("Unlock##Idx"))
|
||||
_lastError = new UnlockState(_pluginInterface).Invoke(_gameObjectIndex, _key);
|
||||
|
||||
IpcTesterHelpers.DrawIntro(UnlockStateName.Label);
|
||||
if (ImGui.Button("Unlock##Name"))
|
||||
_lastError = new UnlockStateName(_pluginInterface).Invoke(_gameObjectName, _key);
|
||||
|
||||
IpcTesterHelpers.DrawIntro(UnlockAll.Label);
|
||||
if (ImGui.Button("Unlock##All"))
|
||||
_numUnlocked = new UnlockAll(_pluginInterface).Invoke(_key);
|
||||
ImGui.SameLine();
|
||||
ImGui.TextUnformatted($"Unlocked {_numUnlocked}");
|
||||
|
||||
IpcTesterHelpers.DrawIntro(RevertToAutomation.Label);
|
||||
if (ImGui.Button("Revert##AutomationIdx"))
|
||||
_lastError = new RevertToAutomation(_pluginInterface).Invoke(_gameObjectIndex, _key, _flags);
|
||||
|
||||
IpcTesterHelpers.DrawIntro(RevertToAutomationName.Label);
|
||||
if (ImGui.Button("Revert##AutomationName"))
|
||||
_lastError = new RevertToAutomationName(_pluginInterface).Invoke(_gameObjectName, _key, _flags);
|
||||
}
|
||||
|
||||
private void DrawStatePopup()
|
||||
{
|
||||
ImGui.SetNextWindowSize(ImGuiHelpers.ScaledVector2(500, 500));
|
||||
using var p = ImRaii.Popup("State");
|
||||
if (!p)
|
||||
return;
|
||||
|
||||
if (ImGui.Button("Copy to Clipboard"))
|
||||
ImGui.SetClipboardText(_stateString);
|
||||
ImGui.SameLine();
|
||||
if (ImGui.Button("Copy as Base64") && _state != null)
|
||||
ImGui.SetClipboardText(DesignConverter.ToBase64(_state));
|
||||
using var font = ImRaii.PushFont(UiBuilder.MonoFont);
|
||||
ImGui.TextUnformatted(_stateString);
|
||||
|
||||
if (ImGui.Button("Close", -Vector2.UnitX) || !ImGui.IsWindowFocused())
|
||||
ImGui.CloseCurrentPopup();
|
||||
}
|
||||
|
||||
private unsafe void PrintName()
|
||||
{
|
||||
ImGuiNative.igTextUnformatted(_lastStateChangeName.Path, _lastStateChangeName.Path + _lastStateChangeName.Length);
|
||||
ImGui.SameLine();
|
||||
using (ImRaii.PushFont(UiBuilder.MonoFont))
|
||||
{
|
||||
ImGuiUtil.CopyOnClickSelectable($"0x{_lastStateChangeActor:X}");
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
ImGui.TextUnformatted($"at {_lastStateChangeTime.ToLocalTime().TimeOfDay}");
|
||||
}
|
||||
|
||||
private void OnStateChanged(nint actor)
|
||||
{
|
||||
_lastStateChangeActor = actor;
|
||||
_lastStateChangeTime = DateTime.UtcNow;
|
||||
_lastStateChangeName = actor != nint.Zero ? ((Actor)actor).Utf8Name.Clone() : ByteString.Empty;
|
||||
}
|
||||
|
||||
private void OnGPoseChange(bool value)
|
||||
{
|
||||
_lastGPoseChangeValue = value;
|
||||
_lastGPoseChangeTime = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,244 +0,0 @@
|
|||
using Dalamud.Plugin;
|
||||
using Glamourer.Api;
|
||||
using Glamourer.Interop;
|
||||
using ImGuiNET;
|
||||
using OtterGui;
|
||||
using OtterGui.Raii;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Gui;
|
||||
using Penumbra.GameData.Gui.Debug;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
namespace Glamourer.Gui.Tabs.DebugTab;
|
||||
|
||||
public class IpcTesterPanel(DalamudPluginInterface _pluginInterface, ObjectManager _objectManager) : IGameDataDrawer
|
||||
{
|
||||
public string Label
|
||||
=> "IPC Tester";
|
||||
|
||||
public bool Disabled
|
||||
=> false;
|
||||
|
||||
private int _gameObjectIndex;
|
||||
private CustomItemId _customItemId;
|
||||
private StainId _stainId;
|
||||
private EquipSlot _slot = EquipSlot.Head;
|
||||
private string _gameObjectName = string.Empty;
|
||||
private string _base64Apply = string.Empty;
|
||||
private string _designIdentifier = string.Empty;
|
||||
private GlamourerIpc.GlamourerErrorCode _setItemEc;
|
||||
private GlamourerIpc.GlamourerErrorCode _setItemOnceEc;
|
||||
private GlamourerIpc.GlamourerErrorCode _setItemByActorNameEc;
|
||||
private GlamourerIpc.GlamourerErrorCode _setItemOnceByActorNameEc;
|
||||
|
||||
public void Draw()
|
||||
{
|
||||
ImGui.InputInt("Game Object Index", ref _gameObjectIndex, 0, 0);
|
||||
ImGui.InputTextWithHint("##gameObject", "Character Name...", ref _gameObjectName, 64);
|
||||
ImGui.InputTextWithHint("##base64", "Design Base64...", ref _base64Apply, 2047);
|
||||
ImGui.InputTextWithHint("##identifier", "Design identifier...", ref _designIdentifier, 36);
|
||||
DrawItemInput();
|
||||
using var table = ImRaii.Table("##ipc", 2, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg);
|
||||
if (!table)
|
||||
return;
|
||||
|
||||
ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelApiVersions);
|
||||
var (major, minor) = GlamourerIpc.ApiVersionsSubscriber(_pluginInterface).Invoke();
|
||||
ImGuiUtil.DrawTableColumn($"({major}, {minor})");
|
||||
|
||||
ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelGetAllCustomization);
|
||||
ImGui.TableNextColumn();
|
||||
var base64 = GlamourerIpc.GetAllCustomizationSubscriber(_pluginInterface).Invoke(_gameObjectName);
|
||||
if (base64 != null)
|
||||
ImGuiUtil.CopyOnClickSelectable(base64);
|
||||
else
|
||||
ImGui.TextUnformatted("Error");
|
||||
|
||||
ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelGetAllCustomizationFromCharacter);
|
||||
ImGui.TableNextColumn();
|
||||
base64 = GlamourerIpc.GetAllCustomizationFromCharacterSubscriber(_pluginInterface)
|
||||
.Invoke(_objectManager.GetDalamudCharacter(_gameObjectIndex));
|
||||
if (base64 != null)
|
||||
ImGuiUtil.CopyOnClickSelectable(base64);
|
||||
else
|
||||
ImGui.TextUnformatted("Error");
|
||||
|
||||
ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelGetAllCustomizationFromLockedCharacter);
|
||||
ImGui.TableNextColumn();
|
||||
var base64Locked = GlamourerIpc.GetAllCustomizationFromLockedCharacterSubscriber(_pluginInterface).Invoke(_objectManager.GetDalamudCharacter(_gameObjectIndex), 1337);
|
||||
if (base64Locked != null)
|
||||
ImGuiUtil.CopyOnClickSelectable(base64Locked);
|
||||
else
|
||||
ImGui.TextUnformatted("Error");
|
||||
|
||||
ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelRevert);
|
||||
ImGui.TableNextColumn();
|
||||
if (ImGui.Button("Revert##Name"))
|
||||
GlamourerIpc.RevertSubscriber(_pluginInterface).Invoke(_gameObjectName);
|
||||
|
||||
ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelRevertCharacter);
|
||||
ImGui.TableNextColumn();
|
||||
if (ImGui.Button("Revert##Character"))
|
||||
GlamourerIpc.RevertCharacterSubscriber(_pluginInterface).Invoke(_objectManager.GetDalamudCharacter(_gameObjectIndex));
|
||||
|
||||
ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelApplyAll);
|
||||
ImGui.TableNextColumn();
|
||||
if (ImGui.Button("Apply##AllName"))
|
||||
GlamourerIpc.ApplyAllSubscriber(_pluginInterface).Invoke(_base64Apply, _gameObjectName);
|
||||
|
||||
ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelApplyAllOnce);
|
||||
ImGui.TableNextColumn();
|
||||
if (ImGui.Button("Apply Once##AllName"))
|
||||
GlamourerIpc.ApplyAllOnceSubscriber(_pluginInterface).Invoke(_base64Apply, _gameObjectName);
|
||||
|
||||
ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelApplyAllToCharacter);
|
||||
ImGui.TableNextColumn();
|
||||
if (ImGui.Button("Apply##AllCharacter"))
|
||||
GlamourerIpc.ApplyAllToCharacterSubscriber(_pluginInterface)
|
||||
.Invoke(_base64Apply, _objectManager.GetDalamudCharacter(_gameObjectIndex));
|
||||
|
||||
ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelApplyAllOnceToCharacter);
|
||||
ImGui.TableNextColumn();
|
||||
if (ImGui.Button("Apply Once##AllCharacter"))
|
||||
GlamourerIpc.ApplyAllOnceToCharacterSubscriber(_pluginInterface)
|
||||
.Invoke(_base64Apply, _objectManager.GetDalamudCharacter(_gameObjectIndex));
|
||||
|
||||
ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelApplyOnlyEquipment);
|
||||
ImGui.TableNextColumn();
|
||||
if (ImGui.Button("Apply##EquipName"))
|
||||
GlamourerIpc.ApplyOnlyEquipmentSubscriber(_pluginInterface).Invoke(_base64Apply, _gameObjectName);
|
||||
|
||||
ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelApplyOnlyEquipmentToCharacter);
|
||||
ImGui.TableNextColumn();
|
||||
if (ImGui.Button("Apply##EquipCharacter"))
|
||||
GlamourerIpc.ApplyOnlyEquipmentToCharacterSubscriber(_pluginInterface)
|
||||
.Invoke(_base64Apply, _objectManager.GetDalamudCharacter(_gameObjectIndex));
|
||||
|
||||
ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelApplyOnlyCustomization);
|
||||
ImGui.TableNextColumn();
|
||||
if (ImGui.Button("Apply##CustomizeName"))
|
||||
GlamourerIpc.ApplyOnlyCustomizationSubscriber(_pluginInterface).Invoke(_base64Apply, _gameObjectName);
|
||||
|
||||
ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelApplyOnlyCustomizationToCharacter);
|
||||
ImGui.TableNextColumn();
|
||||
if (ImGui.Button("Apply##CustomizeCharacter"))
|
||||
GlamourerIpc.ApplyOnlyCustomizationToCharacterSubscriber(_pluginInterface)
|
||||
.Invoke(_base64Apply, _objectManager.GetDalamudCharacter(_gameObjectIndex));
|
||||
|
||||
ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelApplyByGuid);
|
||||
ImGui.TableNextColumn();
|
||||
if (ImGui.Button("Apply##ByGuidName") && Guid.TryParse(_designIdentifier, out var guid1))
|
||||
GlamourerIpc.ApplyByGuidSubscriber(_pluginInterface).Invoke(guid1, _gameObjectName);
|
||||
|
||||
ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelApplyByGuidOnce);
|
||||
ImGui.TableNextColumn();
|
||||
if (ImGui.Button("Apply Once##ByGuidName") && Guid.TryParse(_designIdentifier, out var guid1Once))
|
||||
GlamourerIpc.ApplyByGuidOnceSubscriber(_pluginInterface).Invoke(guid1Once, _gameObjectName);
|
||||
|
||||
ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelApplyByGuidToCharacter);
|
||||
ImGui.TableNextColumn();
|
||||
if (ImGui.Button("Apply##ByGuidCharacter") && Guid.TryParse(_designIdentifier, out var guid2))
|
||||
GlamourerIpc.ApplyByGuidToCharacterSubscriber(_pluginInterface)
|
||||
.Invoke(guid2, _objectManager.GetDalamudCharacter(_gameObjectIndex));
|
||||
|
||||
ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelApplyByGuidOnceToCharacter);
|
||||
ImGui.TableNextColumn();
|
||||
if (ImGui.Button("Apply Once##ByGuidCharacter") && Guid.TryParse(_designIdentifier, out var guid2Once))
|
||||
GlamourerIpc.ApplyByGuidOnceToCharacterSubscriber(_pluginInterface)
|
||||
.Invoke(guid2Once, _objectManager.GetDalamudCharacter(_gameObjectIndex));
|
||||
|
||||
ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelApplyAllLock);
|
||||
ImGui.TableNextColumn();
|
||||
if (ImGui.Button("Apply With Lock##CustomizeCharacter"))
|
||||
GlamourerIpc.ApplyAllToCharacterLockSubscriber(_pluginInterface)
|
||||
.Invoke(_base64Apply, _objectManager.GetDalamudCharacter(_gameObjectIndex), 1337);
|
||||
|
||||
ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelUnlock);
|
||||
ImGui.TableNextColumn();
|
||||
if (ImGui.Button("Unlock##CustomizeCharacter"))
|
||||
GlamourerIpc.UnlockSubscriber(_pluginInterface)
|
||||
.Invoke(_objectManager.GetDalamudCharacter(_gameObjectIndex), 1337);
|
||||
|
||||
ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelUnlockAll);
|
||||
ImGui.TableNextColumn();
|
||||
if (ImGui.Button("Unlock All##CustomizeCharacter"))
|
||||
GlamourerIpc.UnlockAllSubscriber(_pluginInterface)
|
||||
.Invoke(1337);
|
||||
|
||||
ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelRevertToAutomation);
|
||||
ImGui.TableNextColumn();
|
||||
if (ImGui.Button("Revert##CustomizeCharacter"))
|
||||
GlamourerIpc.RevertToAutomationCharacterSubscriber(_pluginInterface)
|
||||
.Invoke(_objectManager.GetDalamudCharacter(_gameObjectIndex), 1337);
|
||||
|
||||
|
||||
ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelGetDesignList);
|
||||
ImGui.TableNextColumn();
|
||||
var designList = GlamourerIpc.GetDesignListSubscriber(_pluginInterface)
|
||||
.Invoke();
|
||||
if (ImGui.Button($"Copy {designList.Length} Designs to Clipboard###CopyDesignList"))
|
||||
ImGui.SetClipboardText(string.Join("\n", designList));
|
||||
|
||||
ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelSetItem);
|
||||
ImGui.TableNextColumn();
|
||||
if (ImGui.Button("Set##SetItem"))
|
||||
_setItemEc = (GlamourerIpc.GlamourerErrorCode)GlamourerIpc.SetItemSubscriber(_pluginInterface)
|
||||
.Invoke(_objectManager.GetDalamudCharacter(_gameObjectIndex), (byte)_slot, _customItemId.Id, _stainId.Id, 1337);
|
||||
if (_setItemEc != GlamourerIpc.GlamourerErrorCode.Success)
|
||||
{
|
||||
ImGui.SameLine();
|
||||
ImGui.TextUnformatted(_setItemEc.ToString());
|
||||
}
|
||||
|
||||
ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelSetItemOnce);
|
||||
ImGui.TableNextColumn();
|
||||
if (ImGui.Button("Set Once##SetItem"))
|
||||
_setItemOnceEc = (GlamourerIpc.GlamourerErrorCode)GlamourerIpc.SetItemOnceSubscriber(_pluginInterface)
|
||||
.Invoke(_objectManager.GetDalamudCharacter(_gameObjectIndex), (byte)_slot, _customItemId.Id, _stainId.Id, 1337);
|
||||
if (_setItemOnceEc != GlamourerIpc.GlamourerErrorCode.Success)
|
||||
{
|
||||
ImGui.SameLine();
|
||||
ImGui.TextUnformatted(_setItemOnceEc.ToString());
|
||||
}
|
||||
|
||||
ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelSetItemByActorName);
|
||||
ImGui.TableNextColumn();
|
||||
if (ImGui.Button("Set##SetItemByActorName"))
|
||||
_setItemByActorNameEc = (GlamourerIpc.GlamourerErrorCode)GlamourerIpc.SetItemByActorNameSubscriber(_pluginInterface)
|
||||
.Invoke(_gameObjectName, (byte)_slot, _customItemId.Id, _stainId.Id, 1337);
|
||||
if (_setItemByActorNameEc != GlamourerIpc.GlamourerErrorCode.Success)
|
||||
{
|
||||
ImGui.SameLine();
|
||||
ImGui.TextUnformatted(_setItemByActorNameEc.ToString());
|
||||
}
|
||||
|
||||
ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelSetItemOnceByActorName);
|
||||
ImGui.TableNextColumn();
|
||||
if (ImGui.Button("Set Once##SetItemByActorName"))
|
||||
_setItemOnceByActorNameEc = (GlamourerIpc.GlamourerErrorCode)GlamourerIpc.SetItemOnceByActorNameSubscriber(_pluginInterface)
|
||||
.Invoke(_gameObjectName, (byte)_slot, _customItemId.Id, _stainId.Id, 1337);
|
||||
if (_setItemOnceByActorNameEc != GlamourerIpc.GlamourerErrorCode.Success)
|
||||
{
|
||||
ImGui.SameLine();
|
||||
ImGui.TextUnformatted(_setItemOnceByActorNameEc.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawItemInput()
|
||||
{
|
||||
var tmp = _customItemId.Id;
|
||||
if (ImGuiUtil.InputUlong("Custom Item ID", ref tmp))
|
||||
_customItemId = tmp;
|
||||
var width = ImGui.GetContentRegionAvail().X;
|
||||
EquipSlotCombo.Draw("Equip Slot", string.Empty, ref _slot);
|
||||
var value = (int)_stainId.Id;
|
||||
ImGui.SameLine();
|
||||
width -= ImGui.GetContentRegionAvail().X;
|
||||
ImGui.SetNextItemWidth(width);
|
||||
if (ImGui.InputInt("Stain ID", ref value, 1, 3))
|
||||
{
|
||||
value = Math.Clamp(value, 0, byte.MaxValue);
|
||||
_stainId = (StainId)value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -11,20 +11,9 @@ using OtterGui.Raii;
|
|||
|
||||
namespace Glamourer.Gui.Tabs.DesignTab;
|
||||
|
||||
public class ModAssociationsTab
|
||||
public class ModAssociationsTab(PenumbraService penumbra, DesignFileSystemSelector selector, DesignManager manager)
|
||||
{
|
||||
private readonly PenumbraService _penumbra;
|
||||
private readonly DesignFileSystemSelector _selector;
|
||||
private readonly DesignManager _manager;
|
||||
private readonly ModCombo _modCombo;
|
||||
|
||||
public ModAssociationsTab(PenumbraService penumbra, DesignFileSystemSelector selector, DesignManager manager)
|
||||
{
|
||||
_penumbra = penumbra;
|
||||
_selector = selector;
|
||||
_manager = manager;
|
||||
_modCombo = new ModCombo(penumbra, Glamourer.Log);
|
||||
}
|
||||
private readonly ModCombo _modCombo = new(penumbra, Glamourer.Log);
|
||||
|
||||
public void Draw()
|
||||
{
|
||||
|
|
@ -43,25 +32,25 @@ public class ModAssociationsTab
|
|||
|
||||
private void DrawApplyAllButton()
|
||||
{
|
||||
var current = _penumbra.CurrentCollection;
|
||||
if (ImGuiUtil.DrawDisabledButton($"Try Applying All Associated Mods to {current}##applyAll",
|
||||
new Vector2(ImGui.GetContentRegionAvail().X, 0), string.Empty, current is "<Unavailable>"))
|
||||
var (id, name) = penumbra.CurrentCollection;
|
||||
if (ImGuiUtil.DrawDisabledButton($"Try Applying All Associated Mods to {name}##applyAll",
|
||||
new Vector2(ImGui.GetContentRegionAvail().X, 0), string.Empty, id == Guid.Empty))
|
||||
ApplyAll();
|
||||
}
|
||||
|
||||
public void DrawApplyButton()
|
||||
{
|
||||
var current = _penumbra.CurrentCollection;
|
||||
var (id, name) = penumbra.CurrentCollection;
|
||||
if (ImGuiUtil.DrawDisabledButton("Apply Mod Associations", Vector2.Zero,
|
||||
$"Try to apply all associated mod settings to Penumbras current collection {current}",
|
||||
_selector.Selected!.AssociatedMods.Count == 0 || current is "<Unavailable>"))
|
||||
$"Try to apply all associated mod settings to Penumbras current collection {name}",
|
||||
selector.Selected!.AssociatedMods.Count == 0 || id == Guid.Empty))
|
||||
ApplyAll();
|
||||
}
|
||||
|
||||
public void ApplyAll()
|
||||
{
|
||||
foreach (var (mod, settings) in _selector.Selected!.AssociatedMods)
|
||||
_penumbra.SetMod(mod, settings);
|
||||
foreach (var (mod, settings) in selector.Selected!.AssociatedMods)
|
||||
penumbra.SetMod(mod, settings);
|
||||
}
|
||||
|
||||
private void DrawTable()
|
||||
|
|
@ -81,7 +70,7 @@ public class ModAssociationsTab
|
|||
|
||||
Mod? removedMod = null;
|
||||
(Mod mod, ModSettings settings)? updatedMod = null;
|
||||
foreach (var ((mod, settings), idx) in _selector.Selected!.AssociatedMods.WithIndex())
|
||||
foreach (var ((mod, settings), idx) in selector.Selected!.AssociatedMods.WithIndex())
|
||||
{
|
||||
using var id = ImRaii.PushId(idx);
|
||||
DrawAssociatedModRow(mod, settings, out var removedModTmp, out var updatedModTmp);
|
||||
|
|
@ -94,10 +83,10 @@ public class ModAssociationsTab
|
|||
DrawNewModRow();
|
||||
|
||||
if (removedMod.HasValue)
|
||||
_manager.RemoveMod(_selector.Selected!, removedMod.Value);
|
||||
manager.RemoveMod(selector.Selected!, removedMod.Value);
|
||||
|
||||
if (updatedMod.HasValue)
|
||||
_manager.UpdateMod(_selector.Selected!, updatedMod.Value.mod, updatedMod.Value.settings);
|
||||
manager.UpdateMod(selector.Selected!, updatedMod.Value.mod, updatedMod.Value.settings);
|
||||
}
|
||||
|
||||
private void DrawAssociatedModRow(Mod mod, ModSettings settings, out Mod? removedMod, out (Mod, ModSettings)? updatedMod)
|
||||
|
|
@ -115,7 +104,7 @@ public class ModAssociationsTab
|
|||
|
||||
if (ImGui.IsItemHovered())
|
||||
{
|
||||
var newSettings = _penumbra.GetModSettings(mod);
|
||||
var newSettings = penumbra.GetModSettings(mod);
|
||||
if (ImGui.IsItemClicked())
|
||||
updatedMod = (mod, newSettings);
|
||||
|
||||
|
|
@ -151,7 +140,7 @@ public class ModAssociationsTab
|
|||
selected |= ImGui.Selectable($"{mod.DirectoryName}##directory");
|
||||
hovered |= ImGui.IsItemHovered();
|
||||
if (selected)
|
||||
_penumbra.OpenModPage(mod);
|
||||
penumbra.OpenModPage(mod);
|
||||
if (hovered)
|
||||
ImGui.SetTooltip("Click to open mod page in Penumbra.");
|
||||
ImGui.TableNextColumn();
|
||||
|
|
@ -164,9 +153,9 @@ public class ModAssociationsTab
|
|||
ImGuiUtil.RightAlign(settings.Priority.ToString());
|
||||
ImGui.TableNextColumn();
|
||||
if (ImGuiUtil.DrawDisabledButton("Try Applying", new Vector2(ImGui.GetContentRegionAvail().X, 0), string.Empty,
|
||||
!_penumbra.Available))
|
||||
!penumbra.Available))
|
||||
{
|
||||
var text = _penumbra.SetMod(mod, settings);
|
||||
var text = penumbra.SetMod(mod, settings);
|
||||
if (text.Length > 0)
|
||||
Glamourer.Messager.NotificationMessage(text, NotificationType.Warning, false);
|
||||
}
|
||||
|
|
@ -202,13 +191,13 @@ public class ModAssociationsTab
|
|||
ImGui.TableNextColumn();
|
||||
var tt = currentName.IsNullOrEmpty()
|
||||
? "Please select a mod first."
|
||||
: _selector.Selected!.AssociatedMods.ContainsKey(_modCombo.CurrentSelection.Mod)
|
||||
: selector.Selected!.AssociatedMods.ContainsKey(_modCombo.CurrentSelection.Mod)
|
||||
? "The design already contains an association with the selected mod."
|
||||
: string.Empty;
|
||||
|
||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Plus.ToIconString(), new Vector2(ImGui.GetFrameHeight()), tt, tt.Length > 0,
|
||||
true))
|
||||
_manager.AddMod(_selector.Selected!, _modCombo.CurrentSelection.Mod, _modCombo.CurrentSelection.Settings);
|
||||
manager.AddMod(selector.Selected!, _modCombo.CurrentSelection.Mod, _modCombo.CurrentSelection.Settings);
|
||||
ImGui.TableNextColumn();
|
||||
_modCombo.Draw("##new", currentName.IsNullOrEmpty() ? "Select new Mod..." : currentName, string.Empty,
|
||||
ImGui.GetContentRegionAvail().X, ImGui.GetTextLineHeight());
|
||||
|
|
|
|||
36
Glamourer/Gui/Tabs/SettingsTab/CollectionCombo.cs
Normal file
36
Glamourer/Gui/Tabs/SettingsTab/CollectionCombo.cs
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
using Dalamud.Interface;
|
||||
using Glamourer.Interop.Penumbra;
|
||||
using ImGuiNET;
|
||||
using OtterGui;
|
||||
using OtterGui.Log;
|
||||
using OtterGui.Raii;
|
||||
using OtterGui.Services;
|
||||
using OtterGui.Widgets;
|
||||
|
||||
namespace Glamourer.Gui.Tabs.SettingsTab;
|
||||
|
||||
public sealed class CollectionCombo(Configuration config, PenumbraService penumbra, Logger log)
|
||||
: FilterComboCache<(Guid Id, string IdShort, string Name)>(
|
||||
() => penumbra.GetCollections().Select(kvp => (kvp.Key, kvp.Key.ToString()[..8], kvp.Value)).ToArray(),
|
||||
MouseWheelType.Control, log), IUiService
|
||||
{
|
||||
protected override bool DrawSelectable(int globalIdx, bool selected)
|
||||
{
|
||||
var (_, idShort, name) = Items[globalIdx];
|
||||
if (config.Ephemeral.IncognitoMode)
|
||||
using (ImRaii.PushFont(UiBuilder.MonoFont))
|
||||
{
|
||||
return ImGui.Selectable(idShort);
|
||||
}
|
||||
|
||||
var ret = ImGui.Selectable(name, selected);
|
||||
ImGui.SameLine();
|
||||
using (ImRaii.PushFont(UiBuilder.MonoFont))
|
||||
{
|
||||
using var color = ImRaii.PushColor(ImGuiCol.Text, ImGui.GetColorU32(ImGuiCol.TextDisabled));
|
||||
ImGuiUtil.RightAlign($"({idShort})");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +1,12 @@
|
|||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.Components;
|
||||
using Dalamud.Interface.Style;
|
||||
using Glamourer.Interop;
|
||||
using Glamourer.Interop.Penumbra;
|
||||
using Glamourer.Services;
|
||||
using ImGuiNET;
|
||||
using OtterGui;
|
||||
using OtterGui.Raii;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.GameData.Actors;
|
||||
using ObjectManager = Glamourer.Interop.ObjectManager;
|
||||
|
||||
namespace Glamourer.Gui.Tabs.SettingsTab;
|
||||
|
||||
|
|
@ -15,13 +14,14 @@ public class CollectionOverrideDrawer(
|
|||
CollectionOverrideService collectionOverrides,
|
||||
Configuration config,
|
||||
ObjectManager objects,
|
||||
ActorManager actors) : IService
|
||||
ActorManager actors,
|
||||
PenumbraService penumbra,
|
||||
CollectionCombo combo) : IService
|
||||
{
|
||||
private string _newIdentifier = string.Empty;
|
||||
private ActorIdentifier[] _identifiers = [];
|
||||
private int _dragDropIndex = -1;
|
||||
private Exception? _exception;
|
||||
private string _collection = string.Empty;
|
||||
|
||||
public void Draw()
|
||||
{
|
||||
|
|
@ -32,31 +32,88 @@ public class CollectionOverrideDrawer(
|
|||
if (!header)
|
||||
return;
|
||||
|
||||
using var table = ImRaii.Table("table", 3, ImGuiTableFlags.RowBg);
|
||||
using var table = ImRaii.Table("table", 4, ImGuiTableFlags.RowBg);
|
||||
if (!table)
|
||||
return;
|
||||
|
||||
var buttonSize = new Vector2(ImGui.GetFrameHeight());
|
||||
ImGui.TableSetupColumn("buttons", ImGuiTableColumnFlags.WidthFixed, buttonSize.X);
|
||||
ImGui.TableSetupColumn("identifiers", ImGuiTableColumnFlags.WidthStretch, 0.6f);
|
||||
ImGui.TableSetupColumn("identifiers", ImGuiTableColumnFlags.WidthStretch, 0.35f);
|
||||
ImGui.TableSetupColumn("collections", ImGuiTableColumnFlags.WidthStretch, 0.4f);
|
||||
ImGui.TableSetupColumn("name", ImGuiTableColumnFlags.WidthStretch, 0.25f);
|
||||
|
||||
for (var i = 0; i < collectionOverrides.Overrides.Count; ++i)
|
||||
DrawCollectionRow(ref i, buttonSize);
|
||||
|
||||
DrawNewOverride(buttonSize);
|
||||
}
|
||||
|
||||
private void DrawCollectionRow(ref int idx, Vector2 buttonSize)
|
||||
{
|
||||
var (identifier, collection) = collectionOverrides.Overrides[i];
|
||||
using var id = ImRaii.PushId(i);
|
||||
using var id = ImRaii.PushId(idx);
|
||||
var (exists, actor, collection, name) = collectionOverrides.Fetch(idx);
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Trash.ToIconString(), buttonSize, "Delete this override.", false, true))
|
||||
collectionOverrides.DeleteOverride(i--);
|
||||
collectionOverrides.DeleteOverride(idx--);
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.Selectable(config.Ephemeral.IncognitoMode ? identifier.Incognito(null) : identifier.ToString());
|
||||
DrawActorIdentifier(idx, actor);
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
if (combo.Draw("##collection", name, $"Select the overriding collection. Current GUID:", ImGui.GetContentRegionAvail().X, ImGui.GetTextLineHeight()))
|
||||
{
|
||||
var (guid, _, newName) = combo.CurrentSelection;
|
||||
collectionOverrides.ChangeOverride(idx, guid, newName);
|
||||
}
|
||||
|
||||
if (ImGui.IsItemHovered())
|
||||
{
|
||||
using var tt = ImRaii.Tooltip();
|
||||
using var font = ImRaii.PushFont(UiBuilder.MonoFont);
|
||||
ImGui.TextUnformatted($" {collection}");
|
||||
}
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
DrawCollectionName(exists, collection, name);
|
||||
}
|
||||
|
||||
private void DrawCollectionName(bool exists, Guid collection, string name)
|
||||
{
|
||||
if (!exists)
|
||||
{
|
||||
ImGui.TextUnformatted("<Does not Exist>");
|
||||
if (!ImGui.IsItemHovered())
|
||||
return;
|
||||
|
||||
using var tt1 = ImRaii.Tooltip();
|
||||
ImGui.TextUnformatted($"The design {name} with the GUID");
|
||||
using (ImRaii.PushFont(UiBuilder.MonoFont))
|
||||
{
|
||||
ImGui.TextUnformatted($" {collection}");
|
||||
}
|
||||
|
||||
ImGui.TextUnformatted("does not exist in Penumbra.");
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui.TextUnformatted(config.Ephemeral.IncognitoMode ? collection.ToString()[..8] : name);
|
||||
if (!ImGui.IsItemHovered())
|
||||
return;
|
||||
|
||||
using var tt2 = ImRaii.Tooltip();
|
||||
using var f = ImRaii.PushFont(UiBuilder.MonoFont);
|
||||
ImGui.TextUnformatted(collection.ToString());
|
||||
}
|
||||
|
||||
private void DrawActorIdentifier(int idx, ActorIdentifier actor)
|
||||
{
|
||||
ImGui.Selectable(config.Ephemeral.IncognitoMode ? actor.Incognito(null) : actor.ToString());
|
||||
using (var target = ImRaii.DragDropTarget())
|
||||
{
|
||||
if (target.Success && ImGuiUtil.IsDropping("DraggingOverride"))
|
||||
{
|
||||
collectionOverrides.MoveOverride(_dragDropIndex, i);
|
||||
collectionOverrides.MoveOverride(_dragDropIndex, idx);
|
||||
_dragDropIndex = -1;
|
||||
}
|
||||
}
|
||||
|
|
@ -66,24 +123,22 @@ public class CollectionOverrideDrawer(
|
|||
if (source)
|
||||
{
|
||||
ImGui.SetDragDropPayload("DraggingOverride", nint.Zero, 0);
|
||||
ImGui.TextUnformatted($"Reordering Override #{i + 1}...");
|
||||
_dragDropIndex = i;
|
||||
ImGui.TextUnformatted($"Reordering Override #{idx + 1}...");
|
||||
_dragDropIndex = idx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawNewOverride(Vector2 buttonSize)
|
||||
{
|
||||
var (currentId, currentName) = penumbra.CurrentCollection;
|
||||
ImGui.TableNextColumn();
|
||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.PersonCirclePlus.ToIconString(), buttonSize, "Add override for current player.",
|
||||
!objects.Player.Valid && currentId != Guid.Empty, true))
|
||||
collectionOverrides.AddOverride([objects.PlayerData.Identifier], currentId, currentName);
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X);
|
||||
if (ImGui.InputText("##input", ref collection, 64) && collection.Length > 0)
|
||||
collectionOverrides.ChangeOverride(i, collection);
|
||||
}
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.PersonCirclePlus.ToIconString(), buttonSize, "Add override for current player.",
|
||||
!objects.Player.Valid, true))
|
||||
collectionOverrides.AddOverride([objects.PlayerData.Identifier], _collection.Length > 0 ? _collection : "TempCollection");
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X - ImGui.GetStyle().ItemInnerSpacing.X - buttonSize.X * 2);
|
||||
if (ImGui.InputTextWithHint("##newActor", "New Identifier...", ref _newIdentifier, 80))
|
||||
try
|
||||
{
|
||||
|
|
@ -101,9 +156,9 @@ public class CollectionOverrideDrawer(
|
|||
? "Please enter an identifier string first."
|
||||
: $"The identifier string {_newIdentifier} does not result in a valid identifier{(_exception == null ? "." : $":\n\n{_exception?.Message}")}";
|
||||
|
||||
ImGui.SameLine(0, ImGui.GetStyle().ItemInnerSpacing.X);
|
||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Plus.ToIconString(), buttonSize, tt, tt[0] is 'T', true))
|
||||
collectionOverrides.AddOverride(_identifiers, _collection.Length > 0 ? _collection : "TempCollection");
|
||||
ImGui.TableNextColumn();
|
||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Plus.ToIconString(), buttonSize, tt, tt[0] is not 'A', true))
|
||||
collectionOverrides.AddOverride(_identifiers, currentId, currentName);
|
||||
|
||||
ImGui.SameLine(0, ImGui.GetStyle().ItemInnerSpacing.X);
|
||||
using (ImRaii.PushFont(UiBuilder.IconFont))
|
||||
|
|
@ -114,9 +169,5 @@ public class CollectionOverrideDrawer(
|
|||
|
||||
if (ImGui.IsItemHovered())
|
||||
ActorIdentifierFactory.WriteUserStringTooltip(false);
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X);
|
||||
ImGui.InputTextWithHint("##collection", "Enter Collection...", ref _collection, 80);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,16 +22,13 @@ public class ModSettingApplier(PenumbraService penumbra, Configuration config, O
|
|||
return;
|
||||
}
|
||||
|
||||
var collections = new HashSet<string>();
|
||||
var collections = new HashSet<Guid>();
|
||||
|
||||
foreach (var actor in data.Objects)
|
||||
{
|
||||
var (collection, overridden) = overrides.GetCollection(actor, state.Identifier);
|
||||
if (collection.Length == 0)
|
||||
{
|
||||
Glamourer.Log.Verbose($"[Mod Applier] Could not obtain associated collection for {actor.Utf8Name}.");
|
||||
var (collection, _, overridden) = overrides.GetCollection(actor, state.Identifier);
|
||||
if (collection == Guid.Empty)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!collections.Add(collection))
|
||||
continue;
|
||||
|
|
@ -48,11 +45,11 @@ public class ModSettingApplier(PenumbraService penumbra, Configuration config, O
|
|||
}
|
||||
}
|
||||
|
||||
public (List<string> Messages, int Applied, string Collection, bool Overridden) ApplyModSettings(IReadOnlyDictionary<Mod, ModSettings> settings, Actor actor)
|
||||
public (List<string> Messages, int Applied, Guid Collection, string Name, bool Overridden) ApplyModSettings(IReadOnlyDictionary<Mod, ModSettings> settings, Actor actor)
|
||||
{
|
||||
var (collection, overridden) = overrides.GetCollection(actor);
|
||||
if (collection.Length <= 0)
|
||||
return ([$"Could not obtain associated collection for {actor.Utf8Name}."], 0, string.Empty, false);
|
||||
var (collection, name, overridden) = overrides.GetCollection(actor);
|
||||
if (collection == Guid.Empty)
|
||||
return ([$"{actor.Utf8Name} uses no mods."], 0, Guid.Empty, string.Empty, false);
|
||||
|
||||
var messages = new List<string>();
|
||||
var appliedMods = 0;
|
||||
|
|
@ -65,6 +62,6 @@ public class ModSettingApplier(PenumbraService penumbra, Configuration config, O
|
|||
++appliedMods;
|
||||
}
|
||||
|
||||
return (messages, appliedMods, collection, overridden);
|
||||
return (messages, appliedMods, collection, name, overridden);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ public class PenumbraAutoRedraw : IDisposable, IRequiredService
|
|||
}
|
||||
}
|
||||
|
||||
private void OnModSettingChange(ModSettingChange type, string name, string mod, bool inherited)
|
||||
private void OnModSettingChange(ModSettingChange type, Guid collectionId, string mod, bool inherited)
|
||||
{
|
||||
if (type is ModSettingChange.TemporaryMod)
|
||||
{
|
||||
|
|
@ -79,8 +79,8 @@ public class PenumbraAutoRedraw : IDisposable, IRequiredService
|
|||
if (!_objects.TryGetValue(id, out var actors) || !actors.Valid)
|
||||
continue;
|
||||
|
||||
var collection = _penumbra.GetActorCollection(actors.Objects[0]);
|
||||
if (collection != name)
|
||||
var collection = _penumbra.GetActorCollection(actors.Objects[0], out _);
|
||||
if (collection != collectionId)
|
||||
continue;
|
||||
|
||||
_actions.Enqueue((state, () =>
|
||||
|
|
@ -96,7 +96,7 @@ public class PenumbraAutoRedraw : IDisposable, IRequiredService
|
|||
{
|
||||
// Only update once per frame.
|
||||
var playerName = _penumbra.GetCurrentPlayerCollection();
|
||||
if (playerName != name)
|
||||
if (playerName != collectionId)
|
||||
return;
|
||||
|
||||
var currentFrame = _framework.LastUpdateUTC;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
using Dalamud.Plugin;
|
||||
using Glamourer.Events;
|
||||
using OtterGui.Classes;
|
||||
using Penumbra.Api;
|
||||
using Penumbra.Api.Enums;
|
||||
using Penumbra.Api.Helpers;
|
||||
using Penumbra.GameData.Interop;
|
||||
|
|
@ -10,8 +9,6 @@ using Penumbra.GameData.Structs;
|
|||
|
||||
namespace Glamourer.Interop.Penumbra;
|
||||
|
||||
using CurrentSettings = ValueTuple<PenumbraApiEc, (bool, int, IDictionary<string, IList<string>>, bool)?>;
|
||||
|
||||
public readonly record struct Mod(string Name, string DirectoryName) : IComparable<Mod>
|
||||
{
|
||||
public int CompareTo(Mod other)
|
||||
|
|
@ -24,10 +21,10 @@ public readonly record struct Mod(string Name, string DirectoryName) : IComparab
|
|||
}
|
||||
}
|
||||
|
||||
public readonly record struct ModSettings(IDictionary<string, IList<string>> Settings, int Priority, bool Enabled)
|
||||
public readonly record struct ModSettings(Dictionary<string, List<string>> Settings, int Priority, bool Enabled)
|
||||
{
|
||||
public ModSettings()
|
||||
: this(new Dictionary<string, IList<string>>(), 0, false)
|
||||
: this(new Dictionary<string, List<string>>(), 0, false)
|
||||
{ }
|
||||
|
||||
public static ModSettings Empty
|
||||
|
|
@ -36,30 +33,33 @@ public readonly record struct ModSettings(IDictionary<string, IList<string>> Set
|
|||
|
||||
public unsafe class PenumbraService : IDisposable
|
||||
{
|
||||
public const int RequiredPenumbraBreakingVersion = 4;
|
||||
public const int RequiredPenumbraFeatureVersion = 15;
|
||||
public const int RequiredPenumbraBreakingVersion = 5;
|
||||
public const int RequiredPenumbraFeatureVersion = 0;
|
||||
|
||||
private readonly DalamudPluginInterface _pluginInterface;
|
||||
private readonly EventSubscriber<ChangedItemType, uint> _tooltipSubscriber;
|
||||
private readonly EventSubscriber<MouseButton, ChangedItemType, uint> _clickSubscriber;
|
||||
private readonly EventSubscriber<nint, string, nint, nint, nint> _creatingCharacterBase;
|
||||
private readonly EventSubscriber<nint, string, nint> _createdCharacterBase;
|
||||
private readonly EventSubscriber<ModSettingChange, string, string, bool> _modSettingChanged;
|
||||
private ActionSubscriber<int, RedrawType> _redrawSubscriber;
|
||||
private FuncSubscriber<nint, (nint, string)> _drawObjectInfo;
|
||||
private FuncSubscriber<int, int> _cutsceneParent;
|
||||
private FuncSubscriber<int, (bool, bool, string)> _objectCollection;
|
||||
private FuncSubscriber<IList<(string, string)>> _getMods;
|
||||
private FuncSubscriber<ApiCollectionType, string> _currentCollection;
|
||||
private FuncSubscriber<string, string, string, bool, CurrentSettings> _getCurrentSettings;
|
||||
private FuncSubscriber<string, string, string, bool, PenumbraApiEc> _setMod;
|
||||
private FuncSubscriber<string, string, string, int, PenumbraApiEc> _setModPriority;
|
||||
private FuncSubscriber<string, string, string, string, string, PenumbraApiEc> _setModSetting;
|
||||
private FuncSubscriber<string, string, string, string, IReadOnlyList<string>, PenumbraApiEc> _setModSettings;
|
||||
private FuncSubscriber<TabType, string, string, PenumbraApiEc> _openModPage;
|
||||
private readonly EventSubscriber<nint, Guid, nint, nint, nint> _creatingCharacterBase;
|
||||
private readonly EventSubscriber<nint, Guid, nint> _createdCharacterBase;
|
||||
private readonly EventSubscriber<ModSettingChange, Guid, string, bool> _modSettingChanged;
|
||||
|
||||
private readonly EventSubscriber _initializedEvent;
|
||||
private readonly EventSubscriber _disposedEvent;
|
||||
private global::Penumbra.Api.IpcSubscribers.GetCollectionsByIdentifier? _collectionByIdentifier;
|
||||
private global::Penumbra.Api.IpcSubscribers.GetCollections? _collections;
|
||||
private global::Penumbra.Api.IpcSubscribers.RedrawObject? _redraw;
|
||||
private global::Penumbra.Api.IpcSubscribers.GetDrawObjectInfo? _drawObjectInfo;
|
||||
private global::Penumbra.Api.IpcSubscribers.GetCutsceneParentIndex? _cutsceneParent;
|
||||
private global::Penumbra.Api.IpcSubscribers.GetCollectionForObject? _objectCollection;
|
||||
private global::Penumbra.Api.IpcSubscribers.GetModList? _getMods;
|
||||
private global::Penumbra.Api.IpcSubscribers.GetCollection? _currentCollection;
|
||||
private global::Penumbra.Api.IpcSubscribers.GetCurrentModSettings? _getCurrentSettings;
|
||||
private global::Penumbra.Api.IpcSubscribers.TrySetMod? _setMod;
|
||||
private global::Penumbra.Api.IpcSubscribers.TrySetModPriority? _setModPriority;
|
||||
private global::Penumbra.Api.IpcSubscribers.TrySetModSetting? _setModSetting;
|
||||
private global::Penumbra.Api.IpcSubscribers.TrySetModSettings? _setModSettings;
|
||||
private global::Penumbra.Api.IpcSubscribers.OpenMainWindow? _openModPage;
|
||||
|
||||
private readonly IDisposable _initializedEvent;
|
||||
private readonly IDisposable _disposedEvent;
|
||||
|
||||
private readonly PenumbraReloaded _penumbraReloaded;
|
||||
|
||||
|
|
@ -69,13 +69,13 @@ public unsafe class PenumbraService : IDisposable
|
|||
{
|
||||
_pluginInterface = pi;
|
||||
_penumbraReloaded = penumbraReloaded;
|
||||
_initializedEvent = Ipc.Initialized.Subscriber(pi, Reattach);
|
||||
_disposedEvent = Ipc.Disposed.Subscriber(pi, Unattach);
|
||||
_tooltipSubscriber = Ipc.ChangedItemTooltip.Subscriber(pi);
|
||||
_clickSubscriber = Ipc.ChangedItemClick.Subscriber(pi);
|
||||
_createdCharacterBase = Ipc.CreatedCharacterBase.Subscriber(pi);
|
||||
_creatingCharacterBase = Ipc.CreatingCharacterBase.Subscriber(pi);
|
||||
_modSettingChanged = Ipc.ModSettingChanged.Subscriber(pi);
|
||||
_initializedEvent = global::Penumbra.Api.IpcSubscribers.Initialized.Subscriber(pi, Reattach);
|
||||
_disposedEvent = global::Penumbra.Api.IpcSubscribers.Disposed.Subscriber(pi, Unattach);
|
||||
_tooltipSubscriber = global::Penumbra.Api.IpcSubscribers.ChangedItemTooltip.Subscriber(pi);
|
||||
_clickSubscriber = global::Penumbra.Api.IpcSubscribers.ChangedItemClicked.Subscriber(pi);
|
||||
_createdCharacterBase = global::Penumbra.Api.IpcSubscribers.CreatedCharacterBase.Subscriber(pi);
|
||||
_creatingCharacterBase = global::Penumbra.Api.IpcSubscribers.CreatingCharacterBase.Subscriber(pi);
|
||||
_modSettingChanged = global::Penumbra.Api.IpcSubscribers.ModSettingChanged.Subscriber(pi);
|
||||
Reattach();
|
||||
}
|
||||
|
||||
|
|
@ -92,24 +92,27 @@ public unsafe class PenumbraService : IDisposable
|
|||
}
|
||||
|
||||
|
||||
public event Action<nint, string, nint, nint, nint> CreatingCharacterBase
|
||||
public event Action<nint, Guid, nint, nint, nint> CreatingCharacterBase
|
||||
{
|
||||
add => _creatingCharacterBase.Event += value;
|
||||
remove => _creatingCharacterBase.Event -= value;
|
||||
}
|
||||
|
||||
public event Action<nint, string, nint> CreatedCharacterBase
|
||||
public event Action<nint, Guid, nint> CreatedCharacterBase
|
||||
{
|
||||
add => _createdCharacterBase.Event += value;
|
||||
remove => _createdCharacterBase.Event -= value;
|
||||
}
|
||||
|
||||
public event Action<ModSettingChange, string, string, bool> ModSettingChanged
|
||||
public event Action<ModSettingChange, Guid, string, bool> ModSettingChanged
|
||||
{
|
||||
add => _modSettingChanged.Event += value;
|
||||
remove => _modSettingChanged.Event -= value;
|
||||
}
|
||||
|
||||
public Dictionary<Guid, string> GetCollections()
|
||||
=> Available ? _collections!.Invoke() : [];
|
||||
|
||||
public ModSettings GetModSettings(in Mod mod)
|
||||
{
|
||||
if (!Available)
|
||||
|
|
@ -117,8 +120,8 @@ public unsafe class PenumbraService : IDisposable
|
|||
|
||||
try
|
||||
{
|
||||
var collection = _currentCollection.Invoke(ApiCollectionType.Current);
|
||||
var (ec, tuple) = _getCurrentSettings.Invoke(collection, mod.DirectoryName, string.Empty, false);
|
||||
var collection = _currentCollection!.Invoke(ApiCollectionType.Current);
|
||||
var (ec, tuple) = _getCurrentSettings!.Invoke(collection!.Value.Id, mod.DirectoryName);
|
||||
if (ec is not PenumbraApiEc.Success)
|
||||
return ModSettings.Empty;
|
||||
|
||||
|
|
@ -131,6 +134,18 @@ public unsafe class PenumbraService : IDisposable
|
|||
}
|
||||
}
|
||||
|
||||
public (Guid Id, string Name)? CollectionByIdentifier(string identifier)
|
||||
{
|
||||
if (!Available)
|
||||
return null;
|
||||
|
||||
var ret = _collectionByIdentifier!.Invoke(identifier);
|
||||
if (ret.Count == 0)
|
||||
return null;
|
||||
|
||||
return ret[0];
|
||||
}
|
||||
|
||||
public IReadOnlyList<(Mod Mod, ModSettings Settings)> GetMods()
|
||||
{
|
||||
if (!Available)
|
||||
|
|
@ -138,10 +153,10 @@ public unsafe class PenumbraService : IDisposable
|
|||
|
||||
try
|
||||
{
|
||||
var allMods = _getMods.Invoke();
|
||||
var collection = _currentCollection.Invoke(ApiCollectionType.Current);
|
||||
var allMods = _getMods!.Invoke();
|
||||
var collection = _currentCollection!.Invoke(ApiCollectionType.Current);
|
||||
return allMods
|
||||
.Select(m => (m.Item1, m.Item2, _getCurrentSettings.Invoke(collection, m.Item1, m.Item2, false)))
|
||||
.Select(m => (m.Key, m.Value, _getCurrentSettings!.Invoke(collection!.Value.Id, m.Key)))
|
||||
.Where(t => t.Item3.Item1 is PenumbraApiEc.Success)
|
||||
.Select(t => (new Mod(t.Item2, t.Item1),
|
||||
!t.Item3.Item2.HasValue
|
||||
|
|
@ -162,19 +177,22 @@ public unsafe class PenumbraService : IDisposable
|
|||
|
||||
public void OpenModPage(Mod mod)
|
||||
{
|
||||
if (_openModPage.Invoke(TabType.Mods, mod.DirectoryName, mod.Name) == PenumbraApiEc.ModMissing)
|
||||
if (!Available)
|
||||
return;
|
||||
|
||||
if (_openModPage!.Invoke(TabType.Mods, mod.DirectoryName) == PenumbraApiEc.ModMissing)
|
||||
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
|
||||
=> Available ? _currentCollection.Invoke(ApiCollectionType.Current) : "<Unavailable>";
|
||||
public (Guid Id, string Name) CurrentCollection
|
||||
=> Available ? _currentCollection!.Invoke(ApiCollectionType.Current)!.Value : (Guid.Empty, "<Unavailable>");
|
||||
|
||||
/// <summary>
|
||||
/// Try to set all mod settings as desired. Only sets when the mod should be enabled.
|
||||
/// If it is disabled, ignore all other settings.
|
||||
/// </summary>
|
||||
public string SetMod(Mod mod, ModSettings settings, string? collection = null)
|
||||
public string SetMod(Mod mod, ModSettings settings, Guid? collectionInput = null)
|
||||
{
|
||||
if (!Available)
|
||||
return "Penumbra is not available.";
|
||||
|
|
@ -182,8 +200,8 @@ public unsafe class PenumbraService : IDisposable
|
|||
var sb = new StringBuilder();
|
||||
try
|
||||
{
|
||||
collection ??= _currentCollection.Invoke(ApiCollectionType.Current);
|
||||
var ec = _setMod.Invoke(collection, mod.DirectoryName, mod.Name, settings.Enabled);
|
||||
var collection = collectionInput ?? _currentCollection!.Invoke(ApiCollectionType.Current)!.Value.Id;
|
||||
var ec = _setMod!.Invoke(collection, mod.DirectoryName, settings.Enabled);
|
||||
switch (ec)
|
||||
{
|
||||
case PenumbraApiEc.ModMissing: return $"The mod {mod.Name} [{mod.DirectoryName}] could not be found.";
|
||||
|
|
@ -193,14 +211,14 @@ public unsafe class PenumbraService : IDisposable
|
|||
if (!settings.Enabled)
|
||||
return string.Empty;
|
||||
|
||||
ec = _setModPriority.Invoke(collection, mod.DirectoryName, mod.Name, settings.Priority);
|
||||
ec = _setModPriority!.Invoke(collection, mod.DirectoryName, settings.Priority);
|
||||
Debug.Assert(ec is PenumbraApiEc.Success or PenumbraApiEc.NothingChanged, "Setting Priority should not be able to fail.");
|
||||
|
||||
foreach (var (setting, list) in settings.Settings)
|
||||
{
|
||||
ec = list.Count == 1
|
||||
? _setModSetting.Invoke(collection, mod.DirectoryName, mod.Name, setting, list[0])
|
||||
: _setModSettings.Invoke(collection, mod.DirectoryName, mod.Name, setting, (IReadOnlyList<string>)list);
|
||||
? _setModSetting!.Invoke(collection, mod.DirectoryName, setting, list[0])
|
||||
: _setModSettings!.Invoke(collection, mod.DirectoryName, setting, list);
|
||||
switch (ec)
|
||||
{
|
||||
case PenumbraApiEc.OptionGroupMissing:
|
||||
|
|
@ -227,55 +245,54 @@ public unsafe class PenumbraService : IDisposable
|
|||
}
|
||||
|
||||
/// <summary> Obtain the name of the collection currently assigned to the player. </summary>
|
||||
public string GetCurrentPlayerCollection()
|
||||
public Guid GetCurrentPlayerCollection()
|
||||
{
|
||||
if (!Available)
|
||||
return string.Empty;
|
||||
return Guid.Empty;
|
||||
|
||||
var (valid, _, name) = _objectCollection.Invoke(0);
|
||||
return valid ? name : string.Empty;
|
||||
var (valid, _, (id, _)) = _objectCollection!.Invoke(0);
|
||||
return valid ? id : Guid.Empty;
|
||||
}
|
||||
|
||||
/// <summary> Obtain the name of the collection currently assigned to the given actor. </summary>
|
||||
public string GetActorCollection(Actor actor)
|
||||
public Guid GetActorCollection(Actor actor, out string name)
|
||||
{
|
||||
if (!Available)
|
||||
return string.Empty;
|
||||
{
|
||||
name = string.Empty;
|
||||
return Guid.Empty;
|
||||
}
|
||||
|
||||
var (valid, _, name) = _objectCollection.Invoke(actor.Index.Index);
|
||||
return valid ? name : string.Empty;
|
||||
(var valid, _, (var id, name)) = _objectCollection!.Invoke(actor.Index.Index);
|
||||
return valid ? id : Guid.Empty;
|
||||
}
|
||||
|
||||
/// <summary> Obtain the game object corresponding to a draw object. </summary>
|
||||
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>
|
||||
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>
|
||||
public void RedrawObject(Actor actor, RedrawType settings)
|
||||
{
|
||||
if (!actor || !Available)
|
||||
if (!actor)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
_redrawSubscriber.Invoke(actor.AsObject->ObjectIndex, settings);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Glamourer.Log.Debug($"Failure redrawing object:\n{e}");
|
||||
}
|
||||
RedrawObject(actor.Index, settings);
|
||||
}
|
||||
|
||||
/// <summary> Try to redraw the given actor. </summary>
|
||||
public void RedrawObject(ObjectIndex index, RedrawType settings)
|
||||
{
|
||||
if (!Available)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
_redrawSubscriber.Invoke(index.Index, settings);
|
||||
_redraw!.Invoke(index.Index, settings);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
|
@ -290,7 +307,7 @@ public unsafe class PenumbraService : IDisposable
|
|||
{
|
||||
Unattach();
|
||||
|
||||
var (breaking, feature) = Ipc.ApiVersions.Subscriber(_pluginInterface).Invoke();
|
||||
var (breaking, feature) = new global::Penumbra.Api.IpcSubscribers.ApiVersion(_pluginInterface).Invoke();
|
||||
if (breaking != RequiredPenumbraBreakingVersion || feature < RequiredPenumbraFeatureVersion)
|
||||
throw new Exception(
|
||||
$"Invalid Version {breaking}.{feature:D4}, required major Version {RequiredPenumbraBreakingVersion} with feature greater or equal to {RequiredPenumbraFeatureVersion}.");
|
||||
|
|
@ -300,24 +317,27 @@ public unsafe class PenumbraService : IDisposable
|
|||
_creatingCharacterBase.Enable();
|
||||
_createdCharacterBase.Enable();
|
||||
_modSettingChanged.Enable();
|
||||
_drawObjectInfo = Ipc.GetDrawObjectInfo.Subscriber(_pluginInterface);
|
||||
_cutsceneParent = Ipc.GetCutsceneParentIndex.Subscriber(_pluginInterface);
|
||||
_redrawSubscriber = Ipc.RedrawObjectByIndex.Subscriber(_pluginInterface);
|
||||
_objectCollection = Ipc.GetCollectionForObject.Subscriber(_pluginInterface);
|
||||
_getMods = Ipc.GetMods.Subscriber(_pluginInterface);
|
||||
_currentCollection = Ipc.GetCollectionForType.Subscriber(_pluginInterface);
|
||||
_getCurrentSettings = Ipc.GetCurrentModSettings.Subscriber(_pluginInterface);
|
||||
_setMod = Ipc.TrySetMod.Subscriber(_pluginInterface);
|
||||
_setModPriority = Ipc.TrySetModPriority.Subscriber(_pluginInterface);
|
||||
_setModSetting = Ipc.TrySetModSetting.Subscriber(_pluginInterface);
|
||||
_setModSettings = Ipc.TrySetModSettings.Subscriber(_pluginInterface);
|
||||
_openModPage = Ipc.OpenMainWindow.Subscriber(_pluginInterface);
|
||||
_collectionByIdentifier = new global::Penumbra.Api.IpcSubscribers.GetCollectionsByIdentifier(_pluginInterface);
|
||||
_collections = new global::Penumbra.Api.IpcSubscribers.GetCollections(_pluginInterface);
|
||||
_redraw = new global::Penumbra.Api.IpcSubscribers.RedrawObject(_pluginInterface);
|
||||
_drawObjectInfo = new global::Penumbra.Api.IpcSubscribers.GetDrawObjectInfo(_pluginInterface);
|
||||
_cutsceneParent = new global::Penumbra.Api.IpcSubscribers.GetCutsceneParentIndex(_pluginInterface);
|
||||
_objectCollection = new global::Penumbra.Api.IpcSubscribers.GetCollectionForObject(_pluginInterface);
|
||||
_getMods = new global::Penumbra.Api.IpcSubscribers.GetModList(_pluginInterface);
|
||||
_currentCollection = new global::Penumbra.Api.IpcSubscribers.GetCollection(_pluginInterface);
|
||||
_getCurrentSettings = new global::Penumbra.Api.IpcSubscribers.GetCurrentModSettings(_pluginInterface);
|
||||
_setMod = new global::Penumbra.Api.IpcSubscribers.TrySetMod(_pluginInterface);
|
||||
_setModPriority = new global::Penumbra.Api.IpcSubscribers.TrySetModPriority(_pluginInterface);
|
||||
_setModSetting = new global::Penumbra.Api.IpcSubscribers.TrySetModSetting(_pluginInterface);
|
||||
_setModSettings = new global::Penumbra.Api.IpcSubscribers.TrySetModSettings(_pluginInterface);
|
||||
_openModPage = new global::Penumbra.Api.IpcSubscribers.OpenMainWindow(_pluginInterface);
|
||||
Available = true;
|
||||
_penumbraReloaded.Invoke();
|
||||
Glamourer.Log.Debug("Glamourer attached to Penumbra.");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Unattach();
|
||||
Glamourer.Log.Debug($"Could not attach to Penumbra:\n{e}");
|
||||
}
|
||||
}
|
||||
|
|
@ -332,6 +352,20 @@ public unsafe class PenumbraService : IDisposable
|
|||
_modSettingChanged.Disable();
|
||||
if (Available)
|
||||
{
|
||||
_collectionByIdentifier = null;
|
||||
_collections = null;
|
||||
_redraw = null;
|
||||
_drawObjectInfo = null;
|
||||
_cutsceneParent = null;
|
||||
_objectCollection = null;
|
||||
_getMods = null;
|
||||
_currentCollection = null;
|
||||
_getCurrentSettings = null;
|
||||
_setMod = null;
|
||||
_setModPriority = null;
|
||||
_setModSetting = null;
|
||||
_setModSettings = null;
|
||||
_openModPage = null;
|
||||
Available = false;
|
||||
Glamourer.Log.Debug("Glamourer detached from Penumbra.");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
using Dalamud.Interface.Internal.Notifications;
|
||||
using Glamourer.Interop.Penumbra;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using OtterGui;
|
||||
using OtterGui.Classes;
|
||||
using OtterGui.Filesystem;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.GameData.Actors;
|
||||
|
|
@ -11,7 +13,7 @@ namespace Glamourer.Services;
|
|||
|
||||
public sealed class CollectionOverrideService : IService, ISavable
|
||||
{
|
||||
public const int Version = 1;
|
||||
public const int Version = 2;
|
||||
private readonly SaveService _saveService;
|
||||
private readonly ActorManager _actors;
|
||||
private readonly PenumbraService _penumbra;
|
||||
|
|
@ -24,48 +26,71 @@ public sealed class CollectionOverrideService : IService, ISavable
|
|||
Load();
|
||||
}
|
||||
|
||||
public unsafe (string Collection, bool Overriden) GetCollection(Actor actor, ActorIdentifier identifier = default)
|
||||
public unsafe (Guid CollectionId, string Display, bool Overriden) GetCollection(Actor actor, ActorIdentifier identifier = default)
|
||||
{
|
||||
if (!identifier.IsValid)
|
||||
identifier = _actors.FromObject(actor.AsObject, out _, true, true, true);
|
||||
|
||||
return _overrides.FindFirst(p => p.Actor.Matches(identifier), out var ret)
|
||||
? (ret.Collection, true)
|
||||
: (_penumbra.GetActorCollection(actor), false);
|
||||
? (ret.CollectionId, ret.DisplayName, true)
|
||||
: (_penumbra.GetActorCollection(actor, out var name), name, false);
|
||||
}
|
||||
|
||||
private readonly List<(ActorIdentifier Actor, string Collection)> _overrides = [];
|
||||
private readonly List<(ActorIdentifier Actor, Guid CollectionId, string DisplayName)> _overrides = [];
|
||||
|
||||
public IReadOnlyList<(ActorIdentifier Actor, string Collection)> Overrides
|
||||
public IReadOnlyList<(ActorIdentifier Actor, Guid CollectionId, string DisplayName)> Overrides
|
||||
=> _overrides;
|
||||
|
||||
public string ToFilename(FilenameService fileNames)
|
||||
=> fileNames.CollectionOverrideFile;
|
||||
|
||||
public void AddOverride(IEnumerable<ActorIdentifier> identifiers, string collection)
|
||||
public void AddOverride(IEnumerable<ActorIdentifier> identifiers, Guid collectionId, string displayName)
|
||||
{
|
||||
if (collection.Length == 0)
|
||||
if (collectionId == Guid.Empty)
|
||||
return;
|
||||
|
||||
foreach (var id in identifiers.Where(i => i.IsValid))
|
||||
{
|
||||
_overrides.Add((id, collection));
|
||||
Glamourer.Log.Debug($"Added collection override {id.Incognito(null)} -> {collection}.");
|
||||
_overrides.Add((id, collectionId, displayName));
|
||||
Glamourer.Log.Debug($"Added collection override {id.Incognito(null)} -> {collectionId}.");
|
||||
_saveService.QueueSave(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void ChangeOverride(int idx, string newCollection)
|
||||
public (bool Exists, ActorIdentifier Identifier, Guid CollectionId, string DisplayName) Fetch(int idx)
|
||||
{
|
||||
if (idx < 0 || idx >= _overrides.Count || newCollection.Length == 0)
|
||||
var (identifier, id, name) = _overrides[idx];
|
||||
var collection = _penumbra.CollectionByIdentifier(id.ToString());
|
||||
if (collection == null)
|
||||
return (false, identifier, id, name);
|
||||
|
||||
if (collection.Value.Name == name)
|
||||
return (true, identifier, id, name);
|
||||
|
||||
_overrides[idx] = (identifier, id, collection.Value.Name);
|
||||
Glamourer.Log.Debug($"Updated display name of collection override {idx + 1} ({id}).");
|
||||
_saveService.QueueSave(this);
|
||||
return (true, identifier, id, collection.Value.Name);
|
||||
}
|
||||
|
||||
public void ChangeOverride(int idx, Guid newCollectionId, string newDisplayName)
|
||||
{
|
||||
if (idx < 0 || idx >= _overrides.Count)
|
||||
return;
|
||||
|
||||
if (newCollectionId == Guid.Empty || newDisplayName.Length == 0)
|
||||
return;
|
||||
|
||||
var current = _overrides[idx];
|
||||
if (current.Collection == newCollection)
|
||||
if (current.CollectionId == newCollectionId)
|
||||
return;
|
||||
|
||||
_overrides[idx] = current with { Collection = newCollection };
|
||||
Glamourer.Log.Debug($"Changed collection override {idx + 1} from {current.Collection} to {newCollection}.");
|
||||
_overrides[idx] = current with
|
||||
{
|
||||
CollectionId = newCollectionId,
|
||||
DisplayName = newDisplayName,
|
||||
};
|
||||
Glamourer.Log.Debug($"Changed collection override {idx + 1} from {current.CollectionId} to {newCollectionId}.");
|
||||
_saveService.QueueSave(this);
|
||||
}
|
||||
|
||||
|
|
@ -102,6 +127,7 @@ public sealed class CollectionOverrideService : IService, ISavable
|
|||
switch (version)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
if (jObj["Overrides"] is not JArray array)
|
||||
{
|
||||
Glamourer.Log.Error($"Invalid format of collection override file, ignored.");
|
||||
|
|
@ -110,17 +136,50 @@ public sealed class CollectionOverrideService : IService, ISavable
|
|||
|
||||
foreach (var token in array.OfType<JObject>())
|
||||
{
|
||||
var collection = token["Collection"]?.ToObject<string>() ?? string.Empty;
|
||||
var collectionIdentifier = token["Collection"]?.ToObject<string>() ?? string.Empty;
|
||||
var identifier = _actors.FromJson(token);
|
||||
var displayName = token["DisplayName"]?.ToObject<string>() ?? collectionIdentifier;
|
||||
if (!identifier.IsValid)
|
||||
Glamourer.Log.Warning($"Invalid identifier for collection override with collection [{collection}], skipped.");
|
||||
else if (collection.Length == 0)
|
||||
{
|
||||
Glamourer.Log.Warning(
|
||||
$"Invalid identifier for collection override with collection [{token["Collection"]}], skipped.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!Guid.TryParse(collectionIdentifier, out var collectionId))
|
||||
{
|
||||
if (collectionIdentifier.Length == 0)
|
||||
{
|
||||
Glamourer.Log.Warning($"Empty collection override for identifier {identifier.Incognito(null)}, skipped.");
|
||||
else
|
||||
_overrides.Add((identifier, collection));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (version >= 2)
|
||||
{
|
||||
Glamourer.Log.Warning(
|
||||
$"Invalid collection override {collectionIdentifier} for identifier {identifier.Incognito(null)}, skipped.");
|
||||
continue;
|
||||
}
|
||||
|
||||
var collection = _penumbra.CollectionByIdentifier(collectionIdentifier);
|
||||
if (collection == null)
|
||||
{
|
||||
Glamourer.Messager.AddMessage(new Notification(
|
||||
$"The overridden collection for identifier {identifier.Incognito(null)} with name {collectionIdentifier} could not be found by Penumbra for migration.",
|
||||
NotificationType.Warning));
|
||||
continue;
|
||||
}
|
||||
|
||||
Glamourer.Log.Information($"Migrated collection {collectionIdentifier} to {collection.Value.Id}.");
|
||||
collectionId = collection.Value.Id;
|
||||
displayName = collection.Value.Name;
|
||||
}
|
||||
|
||||
_overrides.Add((identifier, collectionId, displayName));
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
Glamourer.Log.Error($"Invalid version {version} of collection override file, ignored.");
|
||||
return;
|
||||
|
|
@ -147,10 +206,11 @@ public sealed class CollectionOverrideService : IService, ISavable
|
|||
JArray SerializeOverrides()
|
||||
{
|
||||
var jArray = new JArray();
|
||||
foreach (var (actor, collection) in _overrides)
|
||||
foreach (var (actor, collection, displayName) in _overrides)
|
||||
{
|
||||
var obj = actor.ToJson();
|
||||
obj["Collection"] = collection;
|
||||
obj["DisplayName"] = displayName;
|
||||
jArray.Add(obj);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ using Glamourer.State;
|
|||
using ImGuiNET;
|
||||
using OtterGui;
|
||||
using OtterGui.Classes;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.GameData.Actors;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Interop;
|
||||
|
|
@ -18,7 +19,7 @@ using ObjectManager = Glamourer.Interop.ObjectManager;
|
|||
|
||||
namespace Glamourer.Services;
|
||||
|
||||
public class CommandService : IDisposable
|
||||
public class CommandService : IDisposable, IApiService
|
||||
{
|
||||
private const string RandomString = "random";
|
||||
private const string MainCommandString = "/glamourer";
|
||||
|
|
@ -534,14 +535,14 @@ public class CommandService : IDisposable
|
|||
if (!applyMods || design is not Design d)
|
||||
return;
|
||||
|
||||
var (messages, appliedMods, collection, overridden) = _modApplier.ApplyModSettings(d.AssociatedMods, actor);
|
||||
var (messages, appliedMods, collection, name, overridden) = _modApplier.ApplyModSettings(d.AssociatedMods, actor);
|
||||
|
||||
foreach (var message in messages)
|
||||
Glamourer.Messager.Chat.Print($"Error applying mod settings: {message}");
|
||||
|
||||
if (appliedMods > 0)
|
||||
Glamourer.Messager.Chat.Print(
|
||||
$"Applied {appliedMods} mod settings to {collection}{(overridden ? " (overridden by settings)" : string.Empty)}.");
|
||||
$"Applied {appliedMods} mod settings to {name}{(overridden ? " (overridden by settings)" : string.Empty)}.");
|
||||
}
|
||||
|
||||
private bool Delete(string argument)
|
||||
|
|
|
|||
|
|
@ -65,21 +65,27 @@ public class ItemManager
|
|||
if (itemId == SmallclothesId(slot))
|
||||
return SmallClothesItem(slot);
|
||||
|
||||
if (!itemId.IsItem || !ItemData.TryGetValue(itemId.Item, slot, out var item))
|
||||
if (!itemId.IsItem)
|
||||
{
|
||||
item = EquipItem.FromId(itemId);
|
||||
var item = EquipItem.FromId(itemId);
|
||||
item = slot is EquipSlot.MainHand or EquipSlot.OffHand
|
||||
? Identify(slot, item.PrimaryId, item.SecondaryId, item.Variant)
|
||||
: Identify(slot, item.PrimaryId, item.Variant);
|
||||
return item;
|
||||
}
|
||||
|
||||
else if (!ItemData.TryGetValue(itemId.Item, slot, out var item))
|
||||
{
|
||||
return EquipItem.FromId(itemId);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (item.Type.ToSlot() != slot)
|
||||
return new EquipItem(string.Intern($"Invalid #{itemId}"), itemId, item.IconId, item.PrimaryId, item.SecondaryId, item.Variant,
|
||||
0, 0, 0, 0);
|
||||
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
public EquipItem Resolve(FullEquipType type, ItemId itemId)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using Dalamud.Plugin;
|
||||
using Glamourer.Api;
|
||||
using Glamourer.Api.Api;
|
||||
using Glamourer.Automation;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Events;
|
||||
|
|
@ -43,12 +44,12 @@ public static class StaticServiceManager
|
|||
.AddData()
|
||||
.AddDesigns()
|
||||
.AddState()
|
||||
.AddUi()
|
||||
.AddApi();
|
||||
.AddUi();
|
||||
DalamudServices.AddServices(services, pi);
|
||||
services.AddIServices(typeof(EquipItem).Assembly);
|
||||
services.AddIServices(typeof(Glamourer).Assembly);
|
||||
services.AddIServices(typeof(ImRaii).Assembly);
|
||||
services.AddSingleton<IGlamourerApi>(p => p.GetRequiredService<GlamourerApi>());
|
||||
services.CreateProvider();
|
||||
return services;
|
||||
}
|
||||
|
|
@ -164,8 +165,4 @@ public static class StaticServiceManager
|
|||
.AddSingleton<DesignQuickBar>()
|
||||
.AddSingleton<DesignColorUi>()
|
||||
.AddSingleton<NpcCombo>();
|
||||
|
||||
private static ServiceManager AddApi(this ServiceManager services)
|
||||
=> services.AddSingleton<CommandService>()
|
||||
.AddSingleton<GlamourerIpc>();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ public class StateListener : IDisposable
|
|||
/// Weapons and meta flags are updated independently.
|
||||
/// We also need to apply fixed designs here.
|
||||
/// </summary>
|
||||
private unsafe void OnCreatingCharacterBase(nint actorPtr, string _, nint modelPtr, nint customizePtr, nint equipDataPtr)
|
||||
private unsafe void OnCreatingCharacterBase(nint actorPtr, Guid _, nint modelPtr, nint customizePtr, nint equipDataPtr)
|
||||
{
|
||||
var actor = (Actor)actorPtr;
|
||||
if (_condition[ConditionFlag.CreatingCharacter] && actor.Index >= ObjectIndex.CutsceneStart)
|
||||
|
|
@ -725,7 +725,7 @@ public class StateListener : IDisposable
|
|||
_changeCustomizeService.Unsubscribe(OnCustomizeChanged);
|
||||
}
|
||||
|
||||
private void OnCreatedCharacterBase(nint gameObject, string _, nint drawObject)
|
||||
private void OnCreatedCharacterBase(nint gameObject, Guid _, nint drawObject)
|
||||
{
|
||||
if (_condition[ConditionFlag.CreatingCharacter])
|
||||
return;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue