From 75f278c945e9747cb3e79ccf67f5f2defc769a90 Mon Sep 17 00:00:00 2001 From: Zeffuro Date: Sun, 28 Dec 2025 09:14:20 +0100 Subject: [PATCH] Fix KTK, set up Lifecycles --- .../AddonLifecycles/InventoryLifecycles.cs | 25 ++ AetherBags/Hooks/InventoryHook.cs | 2 + AetherBags/Nodes/DragDropNode.cs | 398 +++++++++--------- AetherBags/Plugin.cs | 4 + KamiToolKit | 2 +- 5 files changed, 231 insertions(+), 200 deletions(-) create mode 100644 AetherBags/AddonLifecycles/InventoryLifecycles.cs diff --git a/AetherBags/AddonLifecycles/InventoryLifecycles.cs b/AetherBags/AddonLifecycles/InventoryLifecycles.cs new file mode 100644 index 0000000..996d94c --- /dev/null +++ b/AetherBags/AddonLifecycles/InventoryLifecycles.cs @@ -0,0 +1,25 @@ +using System; +using Dalamud.Game.Addon.Lifecycle; +using Dalamud.Game.Addon.Lifecycle.AddonArgTypes; + +namespace AetherBags.AddonLifecycles; + +public class InventoryLifecycles : IDisposable +{ + + public InventoryLifecycles() + { + Services.AddonLifecycle.RegisterListener(AddonEvent.PreOpen, ["Inventory", "InventoryLarge", "InventoryExpansion"], HandleInventorySetup); + Services.Logger.Verbose("InventoryLifecycles initialized"); + } + + private void HandleInventorySetup(AddonEvent type, AddonArgs args) + { + Services.Logger.Debug("HandleInventorySetup called"); + } + + public void Dispose() + { + Services.AddonLifecycle.UnregisterListener(AddonEvent.PreOpen, ["Inventory", "InventoryLarge", "InventoryExpansion"]); + } +} \ No newline at end of file diff --git a/AetherBags/Hooks/InventoryHook.cs b/AetherBags/Hooks/InventoryHook.cs index dd2fa48..de6a792 100644 --- a/AetherBags/Hooks/InventoryHook.cs +++ b/AetherBags/Hooks/InventoryHook.cs @@ -1,6 +1,8 @@ using System; using Dalamud.Hooking; using FFXIVClientStructs.FFXIV.Client.Game; +using FFXIVClientStructs.FFXIV.Client.UI; +using FFXIVClientStructs.FFXIV.Client.UI.Agent; namespace AetherBags.Hooks; diff --git a/AetherBags/Nodes/DragDropNode.cs b/AetherBags/Nodes/DragDropNode.cs index b7c6bba..4c0ec05 100644 --- a/AetherBags/Nodes/DragDropNode.cs +++ b/AetherBags/Nodes/DragDropNode.cs @@ -1,244 +1,244 @@ - using System; - using System.Numerics; - using AetherBags.Extensions; - using AetherBags.Interop; - using FFXIVClientStructs.FFXIV.Client.Enums; - using FFXIVClientStructs.FFXIV.Client.UI; - using FFXIVClientStructs.FFXIV.Client.UI.Agent; - using FFXIVClientStructs.FFXIV.Component.GUI; - using KamiToolKit.Classes; - using KamiToolKit.Classes.Timelines; - using KamiToolKit.Nodes; - using Lumina.Text.ReadOnly; +using System; +using System.Numerics; +using AetherBags.Extensions; +using AetherBags.Interop; +using FFXIVClientStructs.FFXIV.Client.Enums; +using FFXIVClientStructs.FFXIV.Client.UI; +using FFXIVClientStructs.FFXIV.Client.UI.Agent; +using FFXIVClientStructs.FFXIV.Component.GUI; +using KamiToolKit.Classes; +using KamiToolKit.Classes.Timelines; +using KamiToolKit.Nodes; +using Lumina.Text.ReadOnly; - namespace AetherBags.Nodes; +namespace AetherBags.Nodes; - public unsafe class DragDropNode : ComponentNode { +public unsafe class DragDropNode : ComponentNode { - // FIX: Manually expose the pointers that are 'internal' in KamiToolKit - // We access the raw AtkComponentNode* via 'this.ResNode' and cast from there. - private new AtkComponentDragDrop* Component => (AtkComponentDragDrop*)this.InternalComponentNode->Component; - private new AtkUldComponentDataDragDrop* Data => (AtkUldComponentDataDragDrop*)Component->UldManager.ComponentData; + // FIX: Manually expose the pointers that are 'internal' in KamiToolKit + // We access the raw AtkComponentNode* via 'this.ResNode' and cast from there. + private new AtkComponentDragDrop* Component => (AtkComponentDragDrop*)Node->Component; + private new AtkUldComponentDataDragDrop* Data => (AtkUldComponentDataDragDrop*)Component->UldManager.ComponentData; - public readonly ImageNode DragDropBackgroundNode; - public readonly IconNode IconNode; + public readonly ImageNode DragDropBackgroundNode; + public readonly IconNode IconNode; - public DragDropNode() { - SetInternalComponentType(ComponentType.DragDrop); + public DragDropNode() { + SetInternalComponentType(ComponentType.DragDrop); - DragDropBackgroundNode = new SimpleImageNode { - NodeId = 3, - Size = new Vector2(44.0f, 44.0f), - TexturePath = "ui/uld/DragTargetA.tex", - TextureCoordinates = new Vector2(0.0f, 0.0f), - TextureSize = new Vector2(44.0f, 44.0f), - WrapMode = WrapMode.Tile, - NodeFlags = NodeFlags.Visible | NodeFlags.Enabled | NodeFlags.EmitsEvents, - }; - DragDropBackgroundNode.AttachNode(this); + DragDropBackgroundNode = new SimpleImageNode { + NodeId = 3, + Size = new Vector2(44.0f, 44.0f), + TexturePath = "ui/uld/DragTargetA.tex", + TextureCoordinates = new Vector2(0.0f, 0.0f), + TextureSize = new Vector2(44.0f, 44.0f), + WrapMode = WrapMode.Tile, + NodeFlags = NodeFlags.Visible | NodeFlags.Enabled | NodeFlags.EmitsEvents, + }; + DragDropBackgroundNode.AttachNode(this); - IconNode = new IconNode { - NodeId = 2, - Size = new Vector2(44.0f, 48.0f), - NodeFlags = NodeFlags.Visible | NodeFlags.Enabled | NodeFlags.EmitsEvents, - }; - IconNode.AttachNode(this); + IconNode = new IconNode { + NodeId = 2, + Size = new Vector2(44.0f, 48.0f), + NodeFlags = NodeFlags.Visible | NodeFlags.Enabled | NodeFlags.EmitsEvents, + }; + IconNode.AttachNode(this); - LoadTimelines(); + LoadTimelines(); - Data->Nodes[0] = IconNode.NodeId; + Data->Nodes[0] = IconNode.NodeId; - AcceptedType = DragDropType.Everything; - Payload = new DragDropPayload(); + AcceptedType = DragDropType.Everything; + Payload = new DragDropPayload(); - // Use the fixed shadow struct for writing initial values if needed, - // though direct field access on the struct usually works for simple fields. - // However, to be safe with the VTable fix, we just set fields directly here - // as they are standard offsets, or use the pointer. - Component->AtkDragDropInterface.DragDropType = DragDropType.Everything; - Component->AtkDragDropInterface.DragDropReferenceIndex = 0; + // Use the fixed shadow struct for writing initial values if needed, + // though direct field access on the struct usually works for simple fields. + // However, to be safe with the VTable fix, we just set fields directly here + // as they are standard offsets, or use the pointer. + Component->AtkDragDropInterface.DragDropType = DragDropType.Everything; + Component->AtkDragDropInterface.DragDropReferenceIndex = 0; - InitializeComponentEvents(); + InitializeComponentEvents(); - AddEvent(AtkEventType.DragDropBegin, DragDropBeginHandler); - AddEvent(AtkEventType.DragDropInsert, DragDropInsertHandler); - AddEvent(AtkEventType.DragDropDiscard, DragDropDiscardHandler); - AddEvent(AtkEventType.DragDropClick, DragDropClickHandler); - AddEvent(AtkEventType.DragDropRollOver, DragDropRollOverHandler); - AddEvent(AtkEventType.DragDropRollOut, DragDropRollOutHandler); + AddEvent(AtkEventType.DragDropBegin, DragDropBeginHandler); + AddEvent(AtkEventType.DragDropInsert, DragDropInsertHandler); + AddEvent(AtkEventType.DragDropDiscard, DragDropDiscardHandler); + AddEvent(AtkEventType.DragDropClick, DragDropClickHandler); + AddEvent(AtkEventType.DragDropRollOver, DragDropRollOverHandler); + AddEvent(AtkEventType.DragDropRollOut, DragDropRollOutHandler); + } + + private bool IsDragDropEndRegistered { get; set; } + + public Action? OnBegin { get; set; } + public Action? OnEnd { get; set; } + public Action? OnPayloadAccepted { get; set; } + public Action? OnDiscard { get; set; } + public Action? OnClicked { get; set; } + public Action? OnRollOver { get; set; } + public Action? OnRollOut { get; set; } + + public DragDropPayload Payload { get; set; } + + public uint IconId { + get => IconNode.IconId; + set { + IconNode.IconId = value; + IconNode.IsVisible = value != 0; + } + } + + public bool IsIconDisabled { + get => IconNode.IsIconDisabled; + set => IconNode.IsIconDisabled = value; + } + + public int Quantity { + get => int.Parse(Component->GetQuantityText().ToString()); + set => Component->SetQuantity(value); + } + + public string QuantityString { + get => Component->GetQuantityText().ToString(); + set => Component->SetQuantityText(value); + } + + public DragDropType AcceptedType { + get => Component->AcceptedType; + set => Component->AcceptedType = value; + } + + public AtkDragDropInterface.SoundEffectSuppression SoundEffectSuppression { + get => Component->AtkDragDropInterface.DragDropSoundEffectSuppression; + set => Component->AtkDragDropInterface.DragDropSoundEffectSuppression = value; + } + + public bool IsDraggable { + get => !Component->Flags.HasFlag(DragDropFlag.Locked); + set { + if (value) { + Component->Flags &= ~DragDropFlag.Locked; } - - private bool IsDragDropEndRegistered { get; set; } - - public Action? OnBegin { get; set; } - public Action? OnEnd { get; set; } - public Action? OnPayloadAccepted { get; set; } - public Action? OnDiscard { get; set; } - public Action? OnClicked { get; set; } - public Action? OnRollOver { get; set; } - public Action? OnRollOut { get; set; } - - public DragDropPayload Payload { get; set; } - - public uint IconId { - get => IconNode.IconId; - set { - IconNode.IconId = value; - IconNode.IsVisible = value != 0; - } + else { + Component->Flags |= DragDropFlag.Locked; } + } + } - public bool IsIconDisabled { - get => IconNode.IsIconDisabled; - set => IconNode.IsIconDisabled = value; + public bool IsClickable { + get => Component->Flags.HasFlag(DragDropFlag.Clickable); + set { + if (value) { + Component->Flags |= DragDropFlag.Clickable; } - - public int Quantity { - get => int.Parse(Component->GetQuantityText().ToString()); - set => Component->SetQuantity(value); + else { + Component->Flags &= ~DragDropFlag.Clickable; } + } + } - public string QuantityString { - get => Component->GetQuantityText().ToString(); - set => Component->SetQuantityText(value); - } + private void DragDropBeginHandler(AtkEventListener* thisPtr, AtkEventType eventType, int eventParam, AtkEvent* atkEvent, AtkEventData* atkEventData) { + atkEvent->SetEventIsHandled(); - public DragDropType AcceptedType { - get => Component->AcceptedType; - set => Component->AcceptedType = value; - } + // FIX: Use extension method to write payload using fixed VTable + Payload.ToFixedInterface(atkEventData->DragDropData.DragDropInterface); - public AtkDragDropInterface.SoundEffectSuppression SoundEffectSuppression { - get => Component->AtkDragDropInterface.DragDropSoundEffectSuppression; - set => Component->AtkDragDropInterface.DragDropSoundEffectSuppression = value; - } + OnBegin?.Invoke(this); - public bool IsDraggable { - get => !Component->Flags.HasFlag(DragDropFlag.Locked); - set { - if (value) { - Component->Flags &= ~DragDropFlag.Locked; - } - else { - Component->Flags |= DragDropFlag.Locked; - } - } - } + if (!IsDragDropEndRegistered) { + AddEvent(AtkEventType.DragDropEnd, DragDropEndHandler); + IsDragDropEndRegistered = true; + } + } - public bool IsClickable { - get => Component->Flags.HasFlag(DragDropFlag.Clickable); - set { - if (value) { - Component->Flags |= DragDropFlag.Clickable; - } - else { - Component->Flags &= ~DragDropFlag.Clickable; - } - } - } + private void DragDropInsertHandler(AtkEventListener* thisPtr, AtkEventType eventType, int eventParam, AtkEvent* atkEvent, AtkEventData* atkEventData) { + atkEvent->SetEventIsHandled(); - private void DragDropBeginHandler(AtkEventListener* thisPtr, AtkEventType eventType, int eventParam, AtkEvent* atkEvent, AtkEventData* atkEventData) { - atkEvent->SetEventIsHandled(); + atkEvent->State.StateFlags |= AtkEventStateFlags.HasReturnFlags; + atkEvent->State.ReturnFlags = 1; - // FIX: Use extension method to write payload using fixed VTable - Payload.ToFixedInterface(atkEventData->DragDropData.DragDropInterface); + // FIX: Use extension method to read payload using fixed VTable + var payload = DragDropPayloadExtensions.FromFixedInterface(atkEventData->DragDropData.DragDropInterface); - OnBegin?.Invoke(this); + Payload.Clear(); + IconId = 0; - if (!IsDragDropEndRegistered) { - AddEvent(AtkEventType.DragDropEnd, DragDropEndHandler); - IsDragDropEndRegistered = true; - } - } + OnPayloadAccepted?.Invoke(this, payload); + } - private void DragDropInsertHandler(AtkEventListener* thisPtr, AtkEventType eventType, int eventParam, AtkEvent* atkEvent, AtkEventData* atkEventData) { - atkEvent->SetEventIsHandled(); + private void DragDropDiscardHandler(AtkEventListener* thisPtr, AtkEventType eventType, int eventParam, AtkEvent* atkEvent, AtkEventData* atkEventData) { + atkEvent->SetEventIsHandled(); - atkEvent->State.StateFlags |= AtkEventStateFlags.HasReturnFlags; - atkEvent->State.ReturnFlags = 1; + atkEvent->State.StateFlags |= AtkEventStateFlags.HasReturnFlags; + atkEvent->State.ReturnFlags = 1; - // FIX: Use extension method to read payload using fixed VTable - var payload = DragDropPayloadExtensions.FromFixedInterface(atkEventData->DragDropData.DragDropInterface); + OnDiscard?.Invoke(this); + } - Payload.Clear(); - IconId = 0; + private void DragDropEndHandler(AtkEventListener* thisPtr, AtkEventType eventType, int eventParam, AtkEvent* atkEvent, AtkEventData* atkEventData) { + atkEvent->SetEventIsHandled(); - OnPayloadAccepted?.Invoke(this, payload); - } + // FIX: Cast to shadow struct to call the correct GetPayloadContainer (Index 12) + var fixedInterface = (AtkDragDropInterfaceFixed*)atkEventData->DragDropData.DragDropInterface; + fixedInterface->GetPayloadContainer()->Clear(); - private void DragDropDiscardHandler(AtkEventListener* thisPtr, AtkEventType eventType, int eventParam, AtkEvent* atkEvent, AtkEventData* atkEventData) { - atkEvent->SetEventIsHandled(); + OnEnd?.Invoke(this); - atkEvent->State.StateFlags |= AtkEventStateFlags.HasReturnFlags; - atkEvent->State.ReturnFlags = 1; + if (IsDragDropEndRegistered) { + RemoveEvent(AtkEventType.DragDropEnd, DragDropEndHandler); + IsDragDropEndRegistered = false; + } + } - OnDiscard?.Invoke(this); - } + private void DragDropClickHandler(AtkEventListener* thisPtr, AtkEventType eventType, int eventParam, AtkEvent* atkEvent, AtkEventData* atkEventData) { + atkEvent->SetEventIsHandled(); - private void DragDropEndHandler(AtkEventListener* thisPtr, AtkEventType eventType, int eventParam, AtkEvent* atkEvent, AtkEventData* atkEventData) { - atkEvent->SetEventIsHandled(); + atkEvent->State.StateFlags |= AtkEventStateFlags.HasReturnFlags; + atkEvent->State.ReturnFlags = 1; - // FIX: Cast to shadow struct to call the correct GetPayloadContainer (Index 12) - var fixedInterface = (AtkDragDropInterfaceFixed*)atkEventData->DragDropData.DragDropInterface; - fixedInterface->GetPayloadContainer()->Clear(); + OnClicked?.Invoke(this); + } - OnEnd?.Invoke(this); + private void DragDropRollOverHandler() + => OnRollOver?.Invoke(this); - if (IsDragDropEndRegistered) { - RemoveEvent(AtkEventType.DragDropEnd, DragDropEndHandler); - IsDragDropEndRegistered = false; - } - } + private void DragDropRollOutHandler() + => OnRollOut?.Invoke(this); - private void DragDropClickHandler(AtkEventListener* thisPtr, AtkEventType eventType, int eventParam, AtkEvent* atkEvent, AtkEventData* atkEventData) { - atkEvent->SetEventIsHandled(); + public void Clear() { + Payload.Clear(); + IconId = 0; + } - atkEvent->State.StateFlags |= AtkEventStateFlags.HasReturnFlags; - atkEvent->State.ReturnFlags = 1; + public void ShowTooltip(AtkTooltipManager.AtkTooltipType type, ActionKind actionKind) { + if (AtkStage.Instance()->DragDropManager.IsDragging) return; - OnClicked?.Invoke(this); - } + // FIX: Explicitly use 'this.ResNode' and cast to (AtkResNode*) to avoid ambiguity with the class name + var addon = RaptureAtkUnitManager.Instance()->GetAddonByNode((AtkResNode*)this); + if (addon is null) return; - private void DragDropRollOverHandler() - => OnRollOver?.Invoke(this); + var tooltipArgs = new AtkTooltipManager.AtkTooltipArgs(); + tooltipArgs.Ctor(); + tooltipArgs.ActionArgs.Id = Payload.Int2; + tooltipArgs.ActionArgs.Kind = (DetailKind)actionKind; - private void DragDropRollOutHandler() - => OnRollOut?.Invoke(this); + AtkStage.Instance()->TooltipManager.ShowTooltip( + AtkTooltipManager.AtkTooltipType.Action, + addon->Id, + (AtkResNode*)this, // FIX: Explicit cast here as well + &tooltipArgs); + } - public void Clear() { - Payload.Clear(); - IconId = 0; - } - - public void ShowTooltip(AtkTooltipManager.AtkTooltipType type, ActionKind actionKind) { - if (AtkStage.Instance()->DragDropManager.IsDragging) return; - - // FIX: Explicitly use 'this.ResNode' and cast to (AtkResNode*) to avoid ambiguity with the class name - var addon = RaptureAtkUnitManager.Instance()->GetAddonByNode((AtkResNode*)this); - if (addon is null) return; - - var tooltipArgs = new AtkTooltipManager.AtkTooltipArgs(); - tooltipArgs.Ctor(); - tooltipArgs.ActionArgs.Id = Payload.Int2; - tooltipArgs.ActionArgs.Kind = (DetailKind)actionKind; - - AtkStage.Instance()->TooltipManager.ShowTooltip( - AtkTooltipManager.AtkTooltipType.Action, - addon->Id, - (AtkResNode*)this, // FIX: Explicit cast here as well - &tooltipArgs); - } - - private void LoadTimelines() { - AddTimeline(new TimelineBuilder() - .BeginFrameSet(1, 59) - .AddLabelPair(1, 10, 1) - .AddLabelPair(11, 19, 2) - .AddLabelPair(20, 29, 3) - .AddLabelPair(30, 39, 7) - .AddLabelPair(40, 49, 6) - .AddLabelPair(50, 59, 4) - .EndFrameSet() - .Build()); - } - } \ No newline at end of file + private void LoadTimelines() { + AddTimeline(new TimelineBuilder() + .BeginFrameSet(1, 59) + .AddLabelPair(1, 10, 1) + .AddLabelPair(11, 19, 2) + .AddLabelPair(20, 29, 3) + .AddLabelPair(30, 39, 7) + .AddLabelPair(40, 49, 6) + .AddLabelPair(50, 59, 4) + .EndFrameSet() + .Build()); + } +} \ No newline at end of file diff --git a/AetherBags/Plugin.cs b/AetherBags/Plugin.cs index 858226a..b22e51a 100644 --- a/AetherBags/Plugin.cs +++ b/AetherBags/Plugin.cs @@ -1,5 +1,6 @@ using System; using System.Numerics; +using AetherBags.AddonLifecycles; using AetherBags.Addons; using AetherBags.Helpers; using AetherBags.Hooks; @@ -16,6 +17,7 @@ public unsafe class Plugin : IDalamudPlugin private static string HelpDescription => "Opens your inventory."; private readonly InventoryHooks _inventoryHooks; + private readonly InventoryLifecycles _inventoryLifecycles; public Plugin(IDalamudPluginInterface pluginInterface) { @@ -64,6 +66,7 @@ public unsafe class Plugin : IDalamudPlugin } _inventoryHooks = new InventoryHooks(); + _inventoryLifecycles = new InventoryLifecycles(); } public void Dispose() @@ -82,6 +85,7 @@ public unsafe class Plugin : IDalamudPlugin KamiToolKitLibrary.Dispose(); _inventoryHooks.Dispose(); + _inventoryLifecycles.Dispose(); } private void OnCommand(string command, string args) diff --git a/KamiToolKit b/KamiToolKit index 9519b07..2122482 160000 --- a/KamiToolKit +++ b/KamiToolKit @@ -1 +1 @@ -Subproject commit 9519b07c8db287ef75b7153a5e97c24574e800f2 +Subproject commit 2122482f0dd453a74227965b4f0a6868866e21c1