diff --git a/Glamourer/Gui/InterfaceActorPanel.cs b/Glamourer/Gui/InterfaceActorPanel.cs index 79e55b1..c50cbeb 100644 --- a/Glamourer/Gui/InterfaceActorPanel.cs +++ b/Glamourer/Gui/InterfaceActorPanel.cs @@ -106,7 +106,7 @@ namespace Glamourer.Gui save.Apply(player); if (_inGPose) save.Apply(fallback!); - _plugin.UpdateActors(player, fallback); + Glamourer.Penumbra.UpdateActors(player, fallback); } private void DrawApplyToTargetButton(CharacterSave save) @@ -122,7 +122,7 @@ namespace Glamourer.Gui save.Apply(player); if (fallBackActor != null) save.Apply(fallBackActor); - _plugin.UpdateActors(player, fallBackActor); + Glamourer.Penumbra.UpdateActors(player, fallBackActor); } private void SaveNewDesign(CharacterSave save) @@ -180,7 +180,7 @@ namespace Glamourer.Gui changes |= DrawMiscellaneous(_currentSave, _player); if (_player != null && changes) - _plugin.UpdateActors(_player); + Glamourer.Penumbra.UpdateActors(_player); ImGui.EndChild(); ImGui.EndGroup(); } diff --git a/Glamourer/Gui/InterfaceConfig.cs b/Glamourer/Gui/InterfaceConfig.cs index 48d2702..c1a2d3a 100644 --- a/Glamourer/Gui/InterfaceConfig.cs +++ b/Glamourer/Gui/InterfaceConfig.cs @@ -53,10 +53,9 @@ namespace Glamourer.Gui return; } - if (ImGui.Button(buttonLabel) && Glamourer.GetPenumbra()) + if (ImGui.Button(buttonLabel)) { - _plugin.UnregisterFunctions(); - _plugin.RegisterFunctions(); + Glamourer.Penumbra.Reattach(true); } if (ImGui.IsItemHovered()) @@ -88,12 +87,11 @@ namespace Glamourer.Gui cfg.AttachToPenumbra = v; if (v) { - if (Glamourer.GetPenumbra()) - _plugin.RegisterFunctions(); + Glamourer.Penumbra.Reattach(true); } else { - _plugin.UnregisterFunctions(); + Glamourer.Penumbra.Unattach(); } }); ImGui.SameLine(); diff --git a/Glamourer/Main.cs b/Glamourer/Main.cs index b17013d..5adb440 100644 --- a/Glamourer/Main.cs +++ b/Glamourer/Main.cs @@ -3,109 +3,32 @@ using System.Linq; using System.Reflection; using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Game.Command; -using Dalamud.Logging; using Dalamud.Plugin; using Glamourer.Customization; using Glamourer.Designs; using Glamourer.FileSystem; using Glamourer.Gui; using ImGuiNET; -using Penumbra.Api; using Penumbra.PlayerWatch; namespace Glamourer { public class Glamourer : IDalamudPlugin { - public const int RequiredPenumbraShareVersion = 3; - private const string HelpString = "[Copy|Apply|Save],[Name or PlaceHolder],"; public string Name => "Glamourer"; - public static GlamourerConfig Config = null!; - private Interface _interface = null!; - public static ICustomizationManager Customization = null!; - public DesignManager Designs = null!; - public IPlayerWatcher PlayerWatcher = null!; + public static GlamourerConfig Config = null!; + public static IPlayerWatcher PlayerWatcher = null!; + public static ICustomizationManager Customization = null!; + private readonly Interface _interface; + public DesignManager Designs; - public static string Version = string.Empty; - public static IPenumbraApi? Penumbra; - - private static void PenumbraTooltip(object? it) - { - if (it is Lumina.Excel.GeneratedSheets.Item) - ImGui.Text("Right click to apply to current Glamourer Set. [Glamourer]"); - } - - private void PenumbraRightClick(MouseButton button, object? it) - { - if (button != MouseButton.Right || it is not Lumina.Excel.GeneratedSheets.Item item) - return; - - var gPose = Dalamud.Objects[Interface.GPoseActorId] as Character; - var player = Dalamud.Objects[0] as Character; - var writeItem = new Item(item, string.Empty); - if (gPose != null) - { - writeItem.Write(gPose.Address); - UpdateActors(gPose, player); - } - else if (player != null) - { - writeItem.Write(player.Address); - UpdateActors(player); - } - } - - public void RegisterFunctions() - { - if (Penumbra == null || !Penumbra.Valid) - return; - - Penumbra!.ChangedItemTooltip += PenumbraTooltip; - Penumbra!.ChangedItemClicked += PenumbraRightClick; - } - - public void UnregisterFunctions() - { - if (Penumbra == null || !Penumbra.Valid) - return; - - Penumbra!.ChangedItemTooltip -= PenumbraTooltip; - Penumbra!.ChangedItemClicked -= PenumbraRightClick; - } - - internal static bool GetPenumbra() - { - try - { - var subscriber = Dalamud.PluginInterface.GetIpcSubscriber("Penumbra.Api"); - var penumbraApiBase = subscriber.InvokeFunc(); - if (penumbraApiBase.ApiVersion != RequiredPenumbraShareVersion) - { - PluginLog.Debug("Could not get Penumbra because API version {penumbraApiBase.ApiVersion} does not equal the required version {RequiredPenumbraShareVersion}."); - Penumbra = null; - return false; - } - - Penumbra = penumbraApiBase as IPenumbraApi; - } - catch (IpcNotReadyError ipc) - { - Penumbra = null; - PluginLog.Debug($"Could not get Penumbra because IPC not registered:\n{ipc}"); - } - catch (Exception e) - { - Penumbra = null; - PluginLog.Debug($"Could not get Penumbra for unknown reason:\n{e}"); - } - - return Penumbra != null; - } + public static string Version = string.Empty; + public static PenumbraAttach Penumbra = null!; public Glamourer(DalamudPluginInterface pluginInterface) { @@ -114,8 +37,7 @@ namespace Glamourer Config = GlamourerConfig.Load(); Customization = CustomizationManager.Create(Dalamud.PluginInterface, Dalamud.GameData, Dalamud.ClientState.ClientLanguage); Designs = new DesignManager(); - if (GetPenumbra() && Config.AttachToPenumbra) - RegisterFunctions(); + Penumbra = new PenumbraAttach(Config.AttachToPenumbra); PlayerWatcher = PlayerWatchFactory.Create(Dalamud.Framework, Dalamud.ClientState, Dalamud.Objects); Dalamud.Commands.AddHandler("/glamourer", new CommandInfo(OnGlamourer) @@ -177,7 +99,7 @@ namespace Glamourer save = d.Data; save?.Apply(actor); - UpdateActors(actor); + Penumbra.UpdateActors(actor); } public void SaveCommand(Character actor, string path) @@ -268,27 +190,11 @@ namespace Glamourer public void Dispose() { - PlayerWatcher?.Dispose(); - UnregisterFunctions(); - _interface?.Dispose(); + Penumbra.Dispose(); + PlayerWatcher.Dispose(); + _interface.Dispose(); Dalamud.Commands.RemoveHandler("/glamour"); Dalamud.Commands.RemoveHandler("/glamourer"); } - - // Update actors without triggering PlayerWatcher Events, - // then manually redraw using Penumbra. - public void UpdateActors(Character actor, Character? gPoseOriginalActor = null) - { - var newEquip = PlayerWatcher.UpdateActorWithoutEvent(actor); - Penumbra?.RedrawObject(actor, RedrawType.WithSettings); - - // Special case for carrying over changes to the gPose actor to the regular player actor, too. - if (gPoseOriginalActor == null) - return; - - newEquip.Write(gPoseOriginalActor.Address); - PlayerWatcher.UpdateActorWithoutEvent(gPoseOriginalActor); - Penumbra?.RedrawObject(gPoseOriginalActor, RedrawType.AfterGPoseWithSettings); - } } } diff --git a/Glamourer/PenumbraAttach.cs b/Glamourer/PenumbraAttach.cs new file mode 100644 index 0000000..58676ad --- /dev/null +++ b/Glamourer/PenumbraAttach.cs @@ -0,0 +1,140 @@ +using System; +using Dalamud.Game.ClientState.Objects.Types; +using Dalamud.Logging; +using Dalamud.Plugin; +using Glamourer.Gui; +using ImGuiNET; +using Penumbra.Api; +using Penumbra.PlayerWatch; + +namespace Glamourer +{ + public class PenumbraAttach : IDisposable + { + public const int RequiredPenumbraShareVersion = 3; + + private ICallGateSubscriber? TooltipSubscriber; + private ICallGateSubscriber? ClickSubscriber; + private ICallGateSubscriber? RedrawSubscriberName; + private ICallGateSubscriber? RedrawSubscriberObject; + + public PenumbraAttach(bool attach) + => Reattach(attach); + + public void Reattach(bool attach) + { + try + { + Unattach(); + + var versionSubscriber = Dalamud.PluginInterface.GetIpcSubscriber("Penumbra.ApiVersion"); + var version = versionSubscriber.InvokeFunc(); + if (version != RequiredPenumbraShareVersion) + throw new Exception($"Invalid Version {version}, required Version {RequiredPenumbraShareVersion}."); + + RedrawSubscriberName = Dalamud.PluginInterface.GetIpcSubscriber("Penumbra.RedrawObjectByName"); + RedrawSubscriberObject = Dalamud.PluginInterface.GetIpcSubscriber("Penumbra.RedrawObject"); + + if (!attach) + return; + + TooltipSubscriber = Dalamud.PluginInterface.GetIpcSubscriber("Penumbra.ChangedItemTooltip"); + ClickSubscriber = Dalamud.PluginInterface.GetIpcSubscriber("Penumbra.ChangedItemClick"); + TooltipSubscriber.Subscribe(PenumbraTooltip); + ClickSubscriber.Subscribe(PenumbraRightClickWrapper); + } + catch (Exception e) + { + PluginLog.Debug($"Could not attach to Penumbra:\n{e}"); + } + } + + public void Unattach() + { + TooltipSubscriber?.Unsubscribe(PenumbraTooltip); + ClickSubscriber?.Unsubscribe(PenumbraRightClickWrapper); + TooltipSubscriber = null; + ClickSubscriber = null; + RedrawSubscriberName = null; + RedrawSubscriberObject = null; + } + + public void Dispose() + => Unattach(); + + private static void PenumbraTooltip(object? it) + { + if (it is Lumina.Excel.GeneratedSheets.Item) + ImGui.Text("Right click to apply to current Glamourer Set. [Glamourer]"); + } + + private void PenumbraRightClick(MouseButton button, object? it) + { + if (button != MouseButton.Right || it is not Lumina.Excel.GeneratedSheets.Item item) + return; + + var gPose = Dalamud.Objects[Interface.GPoseActorId] as Character; + var player = Dalamud.Objects[0] as Character; + var writeItem = new Item(item, string.Empty); + if (gPose != null) + { + writeItem.Write(gPose.Address); + UpdateActors(gPose, player); + } + else if (player != null) + { + writeItem.Write(player.Address); + UpdateActors(player); + } + } + + private void PenumbraRightClickWrapper(int button, object? it) + => PenumbraRightClick((MouseButton) button, it); + + public void RedrawObject(GameObject actor, RedrawType settings, bool repeat) + { + if (RedrawSubscriberObject != null) + { + try + { + RedrawSubscriberObject.InvokeFunc(actor, (int) settings); + } + catch (Exception e) + { + if (repeat) + { + Reattach(Glamourer.Config.AttachToPenumbra); + RedrawObject(actor, settings, false); + } + else + { + PluginLog.Debug($"Failure redrawing actor:\n{e}"); + } + } + } + else if (repeat) + { + Reattach(Glamourer.Config.AttachToPenumbra); + RedrawObject(actor, settings, false); + } + else + PluginLog.Debug("Trying to redraw actor, but not attached to Penumbra."); + } + + // Update actors without triggering PlayerWatcher Events, + // then manually redraw using Penumbra. + public void UpdateActors(Character actor, Character? gPoseOriginalActor = null) + { + var newEquip = Glamourer.PlayerWatcher.UpdateActorWithoutEvent(actor); + RedrawObject(actor, RedrawType.WithSettings, true); + + // Special case for carrying over changes to the gPose actor to the regular player actor, too. + if (gPoseOriginalActor == null) + return; + + newEquip.Write(gPoseOriginalActor.Address); + Glamourer.PlayerWatcher.UpdateActorWithoutEvent(gPoseOriginalActor); + RedrawObject(gPoseOriginalActor, RedrawType.AfterGPoseWithSettings, false); + } + } +}