Fix KTK, set up Lifecycles

This commit is contained in:
Zeffuro
2025-12-28 09:14:20 +01:00
parent 9b7e99276e
commit 75f278c945
5 changed files with 231 additions and 200 deletions
@@ -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"]);
}
}
+2
View File
@@ -1,6 +1,8 @@
using System; using System;
using Dalamud.Hooking; using Dalamud.Hooking;
using FFXIVClientStructs.FFXIV.Client.Game; using FFXIVClientStructs.FFXIV.Client.Game;
using FFXIVClientStructs.FFXIV.Client.UI;
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
namespace AetherBags.Hooks; namespace AetherBags.Hooks;
+199 -199
View File
@@ -1,244 +1,244 @@
using System; using System;
using System.Numerics; using System.Numerics;
using AetherBags.Extensions; using AetherBags.Extensions;
using AetherBags.Interop; using AetherBags.Interop;
using FFXIVClientStructs.FFXIV.Client.Enums; using FFXIVClientStructs.FFXIV.Client.Enums;
using FFXIVClientStructs.FFXIV.Client.UI; using FFXIVClientStructs.FFXIV.Client.UI;
using FFXIVClientStructs.FFXIV.Client.UI.Agent; using FFXIVClientStructs.FFXIV.Client.UI.Agent;
using FFXIVClientStructs.FFXIV.Component.GUI; using FFXIVClientStructs.FFXIV.Component.GUI;
using KamiToolKit.Classes; using KamiToolKit.Classes;
using KamiToolKit.Classes.Timelines; using KamiToolKit.Classes.Timelines;
using KamiToolKit.Nodes; using KamiToolKit.Nodes;
using Lumina.Text.ReadOnly; using Lumina.Text.ReadOnly;
namespace AetherBags.Nodes; namespace AetherBags.Nodes;
public unsafe class DragDropNode : ComponentNode<AtkComponentDragDrop, AtkUldComponentDataDragDrop> { public unsafe class DragDropNode : ComponentNode<AtkComponentDragDrop, AtkUldComponentDataDragDrop> {
// FIX: Manually expose the pointers that are 'internal' in KamiToolKit // FIX: Manually expose the pointers that are 'internal' in KamiToolKit
// We access the raw AtkComponentNode* via 'this.ResNode' and cast from there. // We access the raw AtkComponentNode* via 'this.ResNode' and cast from there.
private new AtkComponentDragDrop* Component => (AtkComponentDragDrop*)this.InternalComponentNode->Component; private new AtkComponentDragDrop* Component => (AtkComponentDragDrop*)Node->Component;
private new AtkUldComponentDataDragDrop* Data => (AtkUldComponentDataDragDrop*)Component->UldManager.ComponentData; private new AtkUldComponentDataDragDrop* Data => (AtkUldComponentDataDragDrop*)Component->UldManager.ComponentData;
public readonly ImageNode DragDropBackgroundNode; public readonly ImageNode DragDropBackgroundNode;
public readonly IconNode IconNode; public readonly IconNode IconNode;
public DragDropNode() { public DragDropNode() {
SetInternalComponentType(ComponentType.DragDrop); SetInternalComponentType(ComponentType.DragDrop);
DragDropBackgroundNode = new SimpleImageNode { DragDropBackgroundNode = new SimpleImageNode {
NodeId = 3, NodeId = 3,
Size = new Vector2(44.0f, 44.0f), Size = new Vector2(44.0f, 44.0f),
TexturePath = "ui/uld/DragTargetA.tex", TexturePath = "ui/uld/DragTargetA.tex",
TextureCoordinates = new Vector2(0.0f, 0.0f), TextureCoordinates = new Vector2(0.0f, 0.0f),
TextureSize = new Vector2(44.0f, 44.0f), TextureSize = new Vector2(44.0f, 44.0f),
WrapMode = WrapMode.Tile, WrapMode = WrapMode.Tile,
NodeFlags = NodeFlags.Visible | NodeFlags.Enabled | NodeFlags.EmitsEvents, NodeFlags = NodeFlags.Visible | NodeFlags.Enabled | NodeFlags.EmitsEvents,
}; };
DragDropBackgroundNode.AttachNode(this); DragDropBackgroundNode.AttachNode(this);
IconNode = new IconNode { IconNode = new IconNode {
NodeId = 2, NodeId = 2,
Size = new Vector2(44.0f, 48.0f), Size = new Vector2(44.0f, 48.0f),
NodeFlags = NodeFlags.Visible | NodeFlags.Enabled | NodeFlags.EmitsEvents, NodeFlags = NodeFlags.Visible | NodeFlags.Enabled | NodeFlags.EmitsEvents,
}; };
IconNode.AttachNode(this); IconNode.AttachNode(this);
LoadTimelines(); LoadTimelines();
Data->Nodes[0] = IconNode.NodeId; Data->Nodes[0] = IconNode.NodeId;
AcceptedType = DragDropType.Everything; AcceptedType = DragDropType.Everything;
Payload = new DragDropPayload(); Payload = new DragDropPayload();
// Use the fixed shadow struct for writing initial values if needed, // Use the fixed shadow struct for writing initial values if needed,
// though direct field access on the struct usually works for simple fields. // 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 // However, to be safe with the VTable fix, we just set fields directly here
// as they are standard offsets, or use the pointer. // as they are standard offsets, or use the pointer.
Component->AtkDragDropInterface.DragDropType = DragDropType.Everything; Component->AtkDragDropInterface.DragDropType = DragDropType.Everything;
Component->AtkDragDropInterface.DragDropReferenceIndex = 0; Component->AtkDragDropInterface.DragDropReferenceIndex = 0;
InitializeComponentEvents(); InitializeComponentEvents();
AddEvent(AtkEventType.DragDropBegin, DragDropBeginHandler); AddEvent(AtkEventType.DragDropBegin, DragDropBeginHandler);
AddEvent(AtkEventType.DragDropInsert, DragDropInsertHandler); AddEvent(AtkEventType.DragDropInsert, DragDropInsertHandler);
AddEvent(AtkEventType.DragDropDiscard, DragDropDiscardHandler); AddEvent(AtkEventType.DragDropDiscard, DragDropDiscardHandler);
AddEvent(AtkEventType.DragDropClick, DragDropClickHandler); AddEvent(AtkEventType.DragDropClick, DragDropClickHandler);
AddEvent(AtkEventType.DragDropRollOver, DragDropRollOverHandler); AddEvent(AtkEventType.DragDropRollOver, DragDropRollOverHandler);
AddEvent(AtkEventType.DragDropRollOut, DragDropRollOutHandler); AddEvent(AtkEventType.DragDropRollOut, DragDropRollOutHandler);
}
private bool IsDragDropEndRegistered { get; set; }
public Action<DragDropNode>? OnBegin { get; set; }
public Action<DragDropNode>? OnEnd { get; set; }
public Action<DragDropNode, DragDropPayload>? OnPayloadAccepted { get; set; }
public Action<DragDropNode>? OnDiscard { get; set; }
public Action<DragDropNode>? OnClicked { get; set; }
public Action<DragDropNode>? OnRollOver { get; set; }
public Action<DragDropNode>? 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;
} }
else {
private bool IsDragDropEndRegistered { get; set; } Component->Flags |= DragDropFlag.Locked;
public Action<DragDropNode>? OnBegin { get; set; }
public Action<DragDropNode>? OnEnd { get; set; }
public Action<DragDropNode, DragDropPayload>? OnPayloadAccepted { get; set; }
public Action<DragDropNode>? OnDiscard { get; set; }
public Action<DragDropNode>? OnClicked { get; set; }
public Action<DragDropNode>? OnRollOver { get; set; }
public Action<DragDropNode>? 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 { public bool IsClickable {
get => IconNode.IsIconDisabled; get => Component->Flags.HasFlag(DragDropFlag.Clickable);
set => IconNode.IsIconDisabled = value; set {
if (value) {
Component->Flags |= DragDropFlag.Clickable;
} }
else {
public int Quantity { Component->Flags &= ~DragDropFlag.Clickable;
get => int.Parse(Component->GetQuantityText().ToString());
set => Component->SetQuantity(value);
} }
}
}
public string QuantityString { private void DragDropBeginHandler(AtkEventListener* thisPtr, AtkEventType eventType, int eventParam, AtkEvent* atkEvent, AtkEventData* atkEventData) {
get => Component->GetQuantityText().ToString(); atkEvent->SetEventIsHandled();
set => Component->SetQuantityText(value);
}
public DragDropType AcceptedType { // FIX: Use extension method to write payload using fixed VTable
get => Component->AcceptedType; Payload.ToFixedInterface(atkEventData->DragDropData.DragDropInterface);
set => Component->AcceptedType = value;
}
public AtkDragDropInterface.SoundEffectSuppression SoundEffectSuppression { OnBegin?.Invoke(this);
get => Component->AtkDragDropInterface.DragDropSoundEffectSuppression;
set => Component->AtkDragDropInterface.DragDropSoundEffectSuppression = value;
}
public bool IsDraggable { if (!IsDragDropEndRegistered) {
get => !Component->Flags.HasFlag(DragDropFlag.Locked); AddEvent(AtkEventType.DragDropEnd, DragDropEndHandler);
set { IsDragDropEndRegistered = true;
if (value) { }
Component->Flags &= ~DragDropFlag.Locked; }
}
else {
Component->Flags |= DragDropFlag.Locked;
}
}
}
public bool IsClickable { private void DragDropInsertHandler(AtkEventListener* thisPtr, AtkEventType eventType, int eventParam, AtkEvent* atkEvent, AtkEventData* atkEventData) {
get => Component->Flags.HasFlag(DragDropFlag.Clickable); atkEvent->SetEventIsHandled();
set {
if (value) {
Component->Flags |= DragDropFlag.Clickable;
}
else {
Component->Flags &= ~DragDropFlag.Clickable;
}
}
}
private void DragDropBeginHandler(AtkEventListener* thisPtr, AtkEventType eventType, int eventParam, AtkEvent* atkEvent, AtkEventData* atkEventData) { atkEvent->State.StateFlags |= AtkEventStateFlags.HasReturnFlags;
atkEvent->SetEventIsHandled(); atkEvent->State.ReturnFlags = 1;
// FIX: Use extension method to write payload using fixed VTable // FIX: Use extension method to read payload using fixed VTable
Payload.ToFixedInterface(atkEventData->DragDropData.DragDropInterface); var payload = DragDropPayloadExtensions.FromFixedInterface(atkEventData->DragDropData.DragDropInterface);
OnBegin?.Invoke(this); Payload.Clear();
IconId = 0;
if (!IsDragDropEndRegistered) { OnPayloadAccepted?.Invoke(this, payload);
AddEvent(AtkEventType.DragDropEnd, DragDropEndHandler); }
IsDragDropEndRegistered = true;
}
}
private void DragDropInsertHandler(AtkEventListener* thisPtr, AtkEventType eventType, int eventParam, AtkEvent* atkEvent, AtkEventData* atkEventData) { private void DragDropDiscardHandler(AtkEventListener* thisPtr, AtkEventType eventType, int eventParam, AtkEvent* atkEvent, AtkEventData* atkEventData) {
atkEvent->SetEventIsHandled(); atkEvent->SetEventIsHandled();
atkEvent->State.StateFlags |= AtkEventStateFlags.HasReturnFlags; atkEvent->State.StateFlags |= AtkEventStateFlags.HasReturnFlags;
atkEvent->State.ReturnFlags = 1; atkEvent->State.ReturnFlags = 1;
// FIX: Use extension method to read payload using fixed VTable OnDiscard?.Invoke(this);
var payload = DragDropPayloadExtensions.FromFixedInterface(atkEventData->DragDropData.DragDropInterface); }
Payload.Clear(); private void DragDropEndHandler(AtkEventListener* thisPtr, AtkEventType eventType, int eventParam, AtkEvent* atkEvent, AtkEventData* atkEventData) {
IconId = 0; 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) { OnEnd?.Invoke(this);
atkEvent->SetEventIsHandled();
atkEvent->State.StateFlags |= AtkEventStateFlags.HasReturnFlags; if (IsDragDropEndRegistered) {
atkEvent->State.ReturnFlags = 1; 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->State.StateFlags |= AtkEventStateFlags.HasReturnFlags;
atkEvent->SetEventIsHandled(); atkEvent->State.ReturnFlags = 1;
// FIX: Cast to shadow struct to call the correct GetPayloadContainer (Index 12) OnClicked?.Invoke(this);
var fixedInterface = (AtkDragDropInterfaceFixed*)atkEventData->DragDropData.DragDropInterface; }
fixedInterface->GetPayloadContainer()->Clear();
OnEnd?.Invoke(this); private void DragDropRollOverHandler()
=> OnRollOver?.Invoke(this);
if (IsDragDropEndRegistered) { private void DragDropRollOutHandler()
RemoveEvent(AtkEventType.DragDropEnd, DragDropEndHandler); => OnRollOut?.Invoke(this);
IsDragDropEndRegistered = false;
}
}
private void DragDropClickHandler(AtkEventListener* thisPtr, AtkEventType eventType, int eventParam, AtkEvent* atkEvent, AtkEventData* atkEventData) { public void Clear() {
atkEvent->SetEventIsHandled(); Payload.Clear();
IconId = 0;
}
atkEvent->State.StateFlags |= AtkEventStateFlags.HasReturnFlags; public void ShowTooltip(AtkTooltipManager.AtkTooltipType type, ActionKind actionKind) {
atkEvent->State.ReturnFlags = 1; 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() var tooltipArgs = new AtkTooltipManager.AtkTooltipArgs();
=> OnRollOver?.Invoke(this); tooltipArgs.Ctor();
tooltipArgs.ActionArgs.Id = Payload.Int2;
tooltipArgs.ActionArgs.Kind = (DetailKind)actionKind;
private void DragDropRollOutHandler() AtkStage.Instance()->TooltipManager.ShowTooltip(
=> OnRollOut?.Invoke(this); AtkTooltipManager.AtkTooltipType.Action,
addon->Id,
(AtkResNode*)this, // FIX: Explicit cast here as well
&tooltipArgs);
}
public void Clear() { private void LoadTimelines() {
Payload.Clear(); AddTimeline(new TimelineBuilder()
IconId = 0; .BeginFrameSet(1, 59)
} .AddLabelPair(1, 10, 1)
.AddLabelPair(11, 19, 2)
public void ShowTooltip(AtkTooltipManager.AtkTooltipType type, ActionKind actionKind) { .AddLabelPair(20, 29, 3)
if (AtkStage.Instance()->DragDropManager.IsDragging) return; .AddLabelPair(30, 39, 7)
.AddLabelPair(40, 49, 6)
// FIX: Explicitly use 'this.ResNode' and cast to (AtkResNode*) to avoid ambiguity with the class name .AddLabelPair(50, 59, 4)
var addon = RaptureAtkUnitManager.Instance()->GetAddonByNode((AtkResNode*)this); .EndFrameSet()
if (addon is null) return; .Build());
}
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());
}
}
+4
View File
@@ -1,5 +1,6 @@
using System; using System;
using System.Numerics; using System.Numerics;
using AetherBags.AddonLifecycles;
using AetherBags.Addons; using AetherBags.Addons;
using AetherBags.Helpers; using AetherBags.Helpers;
using AetherBags.Hooks; using AetherBags.Hooks;
@@ -16,6 +17,7 @@ public unsafe class Plugin : IDalamudPlugin
private static string HelpDescription => "Opens your inventory."; private static string HelpDescription => "Opens your inventory.";
private readonly InventoryHooks _inventoryHooks; private readonly InventoryHooks _inventoryHooks;
private readonly InventoryLifecycles _inventoryLifecycles;
public Plugin(IDalamudPluginInterface pluginInterface) public Plugin(IDalamudPluginInterface pluginInterface)
{ {
@@ -64,6 +66,7 @@ public unsafe class Plugin : IDalamudPlugin
} }
_inventoryHooks = new InventoryHooks(); _inventoryHooks = new InventoryHooks();
_inventoryLifecycles = new InventoryLifecycles();
} }
public void Dispose() public void Dispose()
@@ -82,6 +85,7 @@ public unsafe class Plugin : IDalamudPlugin
KamiToolKitLibrary.Dispose(); KamiToolKitLibrary.Dispose();
_inventoryHooks.Dispose(); _inventoryHooks.Dispose();
_inventoryLifecycles.Dispose();
} }
private void OnCommand(string command, string args) private void OnCommand(string command, string args)