Open Inventory, different InventoryNotificationType
This commit is contained in:
@@ -1,6 +1,13 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using AetherBags.Configuration;
|
||||
using AetherBags.Inventory;
|
||||
using Dalamud.Game.Addon.Lifecycle;
|
||||
using Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
||||
using Dalamud.Game.NativeWrapper;
|
||||
using Dalamud.Utility;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
using Lumina.Text.ReadOnly;
|
||||
|
||||
namespace AetherBags.AddonLifecycles;
|
||||
|
||||
@@ -9,17 +16,49 @@ public class InventoryLifecycles : IDisposable
|
||||
|
||||
public InventoryLifecycles()
|
||||
{
|
||||
Services.AddonLifecycle.RegisterListener(AddonEvent.PreOpen, ["Inventory", "InventoryLarge", "InventoryExpansion"], HandleInventorySetup);
|
||||
Services.AddonLifecycle.RegisterListener(AddonEvent.PreRefresh, ["Inventory", "InventoryLarge", "InventoryExpansion"], PreRefreshHandler);
|
||||
Services.Logger.Verbose("InventoryLifecycles initialized");
|
||||
}
|
||||
|
||||
private void HandleInventorySetup(AddonEvent type, AddonArgs args)
|
||||
private unsafe void PreRefreshHandler(AddonEvent type, AddonArgs args)
|
||||
{
|
||||
Services.Logger.Debug("HandleInventorySetup called");
|
||||
if (args is not AddonRefreshArgs refreshArgs)
|
||||
return;
|
||||
|
||||
GeneralSettings config = System.Config.General;
|
||||
|
||||
Services.Logger.Debug("PreRefresh event for Inventory detected");
|
||||
|
||||
AtkValuePtr[] atkValues = refreshArgs.AtkValueEnumerable.ToArray();
|
||||
|
||||
if (atkValues.Length < 7) return;
|
||||
|
||||
AtkValue* value1 = (AtkValue*)atkValues[1].Address;
|
||||
AtkValue* value5 = (AtkValue*)atkValues[5].Address;
|
||||
AtkValue* value6 = (AtkValue*)atkValues[6].Address;
|
||||
|
||||
int openTitleId = value1->Int;
|
||||
ReadOnlySeString title = value5->String.AsReadOnlySeString();
|
||||
ReadOnlySeString upperTitle = value6->String.AsReadOnlySeString();
|
||||
|
||||
System.AddonInventoryWindow.SetNotification(new InventoryNotificationInfo(title, upperTitle));
|
||||
|
||||
if (config.HideGameInventory) refreshArgs.AtkValueCount = 0;
|
||||
if (config.OpenWithGameInventory)
|
||||
{
|
||||
if (openTitleId == 0)
|
||||
{
|
||||
System.AddonInventoryWindow.Toggle();
|
||||
}
|
||||
else
|
||||
{
|
||||
System.AddonInventoryWindow.Open();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Services.AddonLifecycle.UnregisterListener(AddonEvent.PreOpen, ["Inventory", "InventoryLarge", "InventoryExpansion"]);
|
||||
Services.AddonLifecycle.UnregisterListener(AddonEvent.PreRefresh, ["Inventory", "InventoryLarge", "InventoryExpansion"]);
|
||||
}
|
||||
}
|
||||
@@ -125,9 +125,6 @@ public class AddonInventoryWindow : NativeAddon
|
||||
RefreshCategoriesCore(doAutosize);
|
||||
}
|
||||
|
||||
InventoryNotificationType currentNotificationType = (InventoryNotificationType) AgentInventory.Instance()->OpenTitleId;
|
||||
if(currentNotificationType != _notificationNode.NotificationType) _notificationNode.NotificationType = currentNotificationType;
|
||||
|
||||
base.OnUpdate(addon);
|
||||
}
|
||||
|
||||
@@ -292,6 +289,14 @@ public class AddonInventoryWindow : NativeAddon
|
||||
private void ResizeWindow(float width, float height)
|
||||
=> ResizeWindow(width, height, recalcLayout: true);
|
||||
|
||||
public void SetNotification(InventoryNotificationInfo info)
|
||||
{
|
||||
Services.Framework.RunOnTick(() =>
|
||||
{
|
||||
if(IsOpen) _notificationNode.NotificationInfo = info;
|
||||
}, delayTicks: 1);
|
||||
}
|
||||
|
||||
protected override unsafe void OnFinalize(AtkUnitBase* addon)
|
||||
{
|
||||
Services.AddonLifecycle.UnregisterListener(OnInventoryUpdate);
|
||||
|
||||
@@ -11,6 +11,8 @@ public class GeneralSettings
|
||||
public int CompactLookahead { get; set; } = 24;
|
||||
public bool CompactPreferLargestFit { get; set; } = true;
|
||||
public bool CompactStableInsert { get; set; } = true;
|
||||
public bool OpenWithGameInventory { get; set; } = true;
|
||||
public bool HideGameInventory { get; set; } = false;
|
||||
}
|
||||
|
||||
public enum InventoryStackMode : byte
|
||||
|
||||
@@ -3,6 +3,7 @@ using Dalamud.Hooking;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
|
||||
namespace AetherBags.Hooks;
|
||||
|
||||
@@ -19,7 +20,14 @@ public sealed unsafe class InventoryHooks : IDisposable
|
||||
ushort dstSlot,
|
||||
bool unk);
|
||||
|
||||
private delegate void HandleInventoryEventDelegate(AgentInterface* eventInterface, AtkValue* atkValue, int valueCount);
|
||||
|
||||
private readonly Hook<MoveItemSlotDelegate>? _moveItemSlotHook;
|
||||
/*
|
||||
private readonly Hook<UIModule.Delegates.OpenInventory>? _openInventoryHook;
|
||||
private readonly Hook<HandleInventoryEventDelegate>? _handleInventoryEventHook;
|
||||
private readonly Hook<RaptureAtkModule.Delegates.OpenAddon>? _openAddonHook;
|
||||
*/
|
||||
|
||||
public InventoryHooks()
|
||||
{
|
||||
@@ -36,6 +44,47 @@ public sealed unsafe class InventoryHooks : IDisposable
|
||||
{
|
||||
Services.Logger.Error(e, "Failed to hook MoveItemSlot");
|
||||
}
|
||||
/*
|
||||
try
|
||||
{
|
||||
_openInventoryHook = Services.GameInteropProvider.HookFromAddress<UIModule.Delegates.OpenInventory>(
|
||||
UIModule.Instance()->VirtualTable->OpenInventory,
|
||||
OpenInventoryDetour);
|
||||
_openInventoryHook.Enable();
|
||||
|
||||
Services.Logger.Debug("OpenInventory hooked successfully.");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Services.Logger.Error(e, "Failed to hook OpenInventory");
|
||||
}
|
||||
try
|
||||
{
|
||||
_handleInventoryEventHook = Services.GameInteropProvider.HookFromSignature<HandleInventoryEventDelegate>(
|
||||
"E8 ?? ?? ?? ?? 48 8B 74 24 ?? 33 C0 ?? ?? 89 43",
|
||||
HandleInventoryEventDetour);
|
||||
_handleInventoryEventHook.Enable();
|
||||
|
||||
Services.Logger.Debug("HandleInventoryEvent hooked successfully.");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Services.Logger.Error(e, "Failed to hook HandleInventoryEvent");
|
||||
}
|
||||
try
|
||||
{
|
||||
_openAddonHook = Services.GameInteropProvider.HookFromAddress<RaptureAtkModule.Delegates.OpenAddon>(
|
||||
RaptureAtkModule.MemberFunctionPointers.OpenAddon,
|
||||
OpenAddonDetour);
|
||||
_openAddonHook.Enable();
|
||||
|
||||
Services.Logger.Debug("OpenAddon hooked successfully.");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Services.Logger.Error(e, "Failed to hook MoveItemSlot");
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
private int MoveItemSlotDetour(InventoryManager* manager,
|
||||
@@ -53,8 +102,39 @@ public sealed unsafe class InventoryHooks : IDisposable
|
||||
return _moveItemSlotHook!.Original(manager, srcType, srcSlot, dstType, dstSlot, unk);
|
||||
}
|
||||
|
||||
/*
|
||||
private void OpenInventoryDetour(UIModule* uiModule, byte type)
|
||||
{
|
||||
Services.Logger.Debug($"[OpenInventory Hook] Opening inventory of type {type}");
|
||||
_openInventoryHook?.Original(uiModule, type);
|
||||
}
|
||||
|
||||
private void HandleInventoryEventDetour(AgentInterface* eventInterface, AtkValue* atkValue, int valueCount)
|
||||
{
|
||||
for(int i = 0; i < valueCount; i++)
|
||||
{
|
||||
Services.Logger.Debug($"[HandleInventoryEvent Hook] AtkValue[{i}]: Type={atkValue[i].Type}, ToString: {atkValue[i].ToString()} ");
|
||||
}
|
||||
_handleInventoryEventHook?.Original(eventInterface, atkValue, valueCount);
|
||||
}
|
||||
|
||||
private ushort OpenAddonDetour(RaptureAtkModule* thisPtr, uint addonNameId, uint valueCount, AtkValue* values, AtkModuleInterface.AtkEventInterface* eventInterface, ulong eventKind, ushort parentAddonId, int depthLayer)
|
||||
{
|
||||
for(int i = 0; i < valueCount; i++)
|
||||
{
|
||||
Services.Logger.Debug($"[OpenAddon Hook] AtkValue[{i}]: ToString: {values[i].ToString()} ");
|
||||
}
|
||||
return _openAddonHook!.Original(thisPtr, addonNameId, valueCount, values, eventInterface, eventKind, parentAddonId, depthLayer);
|
||||
}
|
||||
*/
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_moveItemSlotHook?.Dispose();
|
||||
/*
|
||||
_openInventoryHook?.Dispose();
|
||||
_handleInventoryEventHook?.Dispose();
|
||||
_openAddonHook?.Dispose();
|
||||
*/
|
||||
}
|
||||
}
|
||||
@@ -56,8 +56,8 @@ public class InventoryNotificationState
|
||||
return notificationCache.GetValueOrDefault((InventoryNotificationType)openTitleId);
|
||||
}
|
||||
|
||||
public record InventoryNotificationInfo(ReadOnlySeString Title, ReadOnlySeString Message);
|
||||
}
|
||||
public record InventoryNotificationInfo(ReadOnlySeString Title, ReadOnlySeString Message);
|
||||
|
||||
public enum InventoryNotificationType : uint
|
||||
{
|
||||
|
||||
@@ -8,17 +8,15 @@ namespace AetherBags.Nodes.Color;
|
||||
|
||||
public class ColorInputRow : HorizontalListNode
|
||||
{
|
||||
private readonly GridNode _gridNode;
|
||||
private ColorPickerAddon? _colorPickerAddon;
|
||||
private readonly LabelTextNode _labelTextNode;
|
||||
private ColorPreviewButtonNode _colorPreview;
|
||||
private Vector4 _initialColor;
|
||||
private readonly ColorPreviewButtonNode _colorPreview;
|
||||
|
||||
public ColorInputRow()
|
||||
{
|
||||
InitializeColorPicker();
|
||||
|
||||
_initialColor = CurrentColor;
|
||||
var initialColor = CurrentColor;
|
||||
|
||||
_colorPreview = new ColorPreviewButtonNode
|
||||
{
|
||||
@@ -33,7 +31,7 @@ public class ColorInputRow : HorizontalListNode
|
||||
{
|
||||
CurrentColor = color;
|
||||
_colorPreview?.Color = color;
|
||||
_initialColor = color;
|
||||
initialColor = color;
|
||||
OnColorConfirmed?.Invoke(color);
|
||||
};
|
||||
_colorPickerAddon?.OnColorPreviewed = color =>
|
||||
@@ -41,7 +39,7 @@ public class ColorInputRow : HorizontalListNode
|
||||
_colorPreview?.Color = color;
|
||||
OnColorChange?.Invoke(color);
|
||||
};
|
||||
_colorPickerAddon?.OnColorCancelled = () => OnColorCanceled?.Invoke(_initialColor);
|
||||
_colorPickerAddon?.OnColorCancelled = () => OnColorCanceled?.Invoke(initialColor);
|
||||
}
|
||||
};
|
||||
_colorPreview.AttachNode(this);
|
||||
|
||||
@@ -50,7 +50,7 @@ public sealed class StringListEditorNode : VerticalListNode
|
||||
if (!string.IsNullOrWhiteSpace(value) && ! _list.Contains(value))
|
||||
{
|
||||
_list.Add(value);
|
||||
_addInput.String = "";
|
||||
_addInput?.String = "";
|
||||
RefreshItems();
|
||||
_onChanged?.Invoke();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using AetherBags.Configuration;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
using KamiToolKit.Nodes;
|
||||
|
||||
namespace AetherBags.Nodes.Configuration.General;
|
||||
|
||||
internal sealed class FunctionalConfigurationNode : TabbedVerticalListNode
|
||||
{
|
||||
private readonly CheckboxNode _hideDefaultBagsCheckboxNode;
|
||||
private readonly LabeledDropdownNode _stackDropDown;
|
||||
|
||||
public FunctionalConfigurationNode()
|
||||
{
|
||||
GeneralSettings config = System.Config.General;
|
||||
|
||||
var titleNode = new CategoryTextNode
|
||||
{
|
||||
Height = 18,
|
||||
String = "Functional Configuration",
|
||||
};
|
||||
AddNode(titleNode);
|
||||
|
||||
AddTab(1);
|
||||
|
||||
var showWithGameCheckBox = new CheckboxNode
|
||||
{
|
||||
Size = Size with { Y = 18 },
|
||||
IsVisible = true,
|
||||
String = "Auto-open with game inventory",
|
||||
IsChecked = config.OpenWithGameInventory,
|
||||
OnClick = isChecked =>
|
||||
{
|
||||
config.OpenWithGameInventory = isChecked;
|
||||
_hideDefaultBagsCheckboxNode?.IsEnabled = isChecked;
|
||||
}
|
||||
};
|
||||
AddNode(showWithGameCheckBox);
|
||||
|
||||
AddTab(1);
|
||||
_hideDefaultBagsCheckboxNode = new CheckboxNode
|
||||
{
|
||||
Size = Size with { Y = 18 },
|
||||
IsVisible = true,
|
||||
String = "Hide default inventory bags",
|
||||
IsEnabled = config.OpenWithGameInventory,
|
||||
IsChecked = config.HideGameInventory,
|
||||
OnClick = isChecked =>
|
||||
{
|
||||
config.HideGameInventory = isChecked;
|
||||
}
|
||||
};
|
||||
AddNode(_hideDefaultBagsCheckboxNode);
|
||||
SubtractTab(1);
|
||||
|
||||
_stackDropDown = new LabeledDropdownNode
|
||||
{
|
||||
Size = new Vector2(300, 20),
|
||||
IsEnabled = true,
|
||||
LabelText = "Stack Mode",
|
||||
LabelTextFlags = TextFlags.AutoAdjustNodeSize,
|
||||
Options = Enum.GetNames(typeof(InventoryStackMode)).ToList(),
|
||||
SelectedOption = config.StackMode.ToString(),
|
||||
OnOptionSelected = selected =>
|
||||
{
|
||||
if (Enum.TryParse<InventoryStackMode>(selected, out var parsed))
|
||||
{
|
||||
config.StackMode = parsed;
|
||||
System.AddonInventoryWindow.ManualInventoryRefresh();
|
||||
}
|
||||
}
|
||||
};
|
||||
AddNode(_stackDropDown);
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,6 @@ namespace AetherBags.Nodes.Configuration.General;
|
||||
public sealed class GeneralScrollingAreaNode : ScrollingAreaNode<VerticalListNode>
|
||||
{
|
||||
private readonly CheckboxNode _debugCheckboxNode = null!;
|
||||
private readonly LabeledDropdownNode _stackDropDown = null!;
|
||||
|
||||
public GeneralScrollingAreaNode()
|
||||
{
|
||||
@@ -19,24 +18,7 @@ public sealed class GeneralScrollingAreaNode : ScrollingAreaNode<VerticalListNod
|
||||
|
||||
ContentNode.ItemSpacing = 32;
|
||||
|
||||
_stackDropDown = new LabeledDropdownNode
|
||||
{
|
||||
Size = new Vector2(300, 20),
|
||||
IsEnabled = true,
|
||||
LabelText = "Stack Mode",
|
||||
LabelTextFlags = TextFlags.AutoAdjustNodeSize,
|
||||
Options = Enum.GetNames(typeof(InventoryStackMode)).ToList(),
|
||||
SelectedOption = config.StackMode.ToString(),
|
||||
OnOptionSelected = selected =>
|
||||
{
|
||||
if (Enum.TryParse<InventoryStackMode>(selected, out var parsed))
|
||||
{
|
||||
config.StackMode = parsed;
|
||||
System.AddonInventoryWindow.ManualInventoryRefresh();
|
||||
}
|
||||
}
|
||||
};
|
||||
ContentNode.AddNode(_stackDropDown);
|
||||
ContentNode.AddNode(new FunctionalConfigurationNode());
|
||||
|
||||
ContentNode.AddNode(new LayoutConfigurationNode());
|
||||
|
||||
|
||||
@@ -17,8 +17,8 @@ public unsafe class DragDropNode : ComponentNode<AtkComponentDragDrop, AtkUldCom
|
||||
|
||||
// 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;
|
||||
private AtkComponentDragDrop* Component => (AtkComponentDragDrop*)Node->Component;
|
||||
private AtkUldComponentDataDragDrop* Data => (AtkUldComponentDataDragDrop*)Component->UldManager.ComponentData;
|
||||
|
||||
public readonly ImageNode DragDropBackgroundNode;
|
||||
public readonly IconNode IconNode;
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using AetherBags.Inventory;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
using KamiToolKit;
|
||||
using KamiToolKit.Classes;
|
||||
using KamiToolKit.Classes.Timelines;
|
||||
using KamiToolKit.Nodes;
|
||||
using Lumina.Excel;
|
||||
using Lumina.Excel.Sheets;
|
||||
using Lumina.Text;
|
||||
using Lumina.Text.ReadOnly;
|
||||
|
||||
namespace AetherBags.Nodes.Inventory;
|
||||
|
||||
@@ -75,30 +69,25 @@ public sealed class InventoryNotificationNode : SimpleComponentNode
|
||||
messageTextNode.Size = Size with { Y = 16 };
|
||||
}
|
||||
|
||||
public InventoryNotificationType NotificationType
|
||||
public InventoryNotificationInfo NotificationInfo
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
if (value == InventoryNotificationType.None)
|
||||
|
||||
titleTextNode.SeString = value.Title;
|
||||
messageTextNode.SeString = value.Message;
|
||||
|
||||
if (value.Title.IsEmpty && value.Message.IsEmpty)
|
||||
{
|
||||
titleTextNode.String = string.Empty;
|
||||
messageTextNode.String = string.Empty;
|
||||
Timeline?.PlayAnimation(17); // Hide
|
||||
}
|
||||
else
|
||||
{
|
||||
var info = NotificationState.GetNotificationInfo((uint)value);
|
||||
if (info != null)
|
||||
{
|
||||
titleTextNode.SeString = info.Title;
|
||||
messageTextNode.SeString = info.Message;
|
||||
Timeline?.PlayAnimation(101); // Show
|
||||
}
|
||||
Timeline?.PlayAnimation(17);
|
||||
return;
|
||||
}
|
||||
|
||||
Timeline?.PlayAnimation(101);
|
||||
}
|
||||
} = InventoryNotificationType.None;
|
||||
} = new("sdsdsd", "sdsd");
|
||||
|
||||
// Future Zeff, this always goes on a parent
|
||||
private Timeline ParentLabels => new TimelineBuilder()
|
||||
|
||||
Reference in New Issue
Block a user