From a93c9ef8b9a02d979a1df965faee320877eb46e3 Mon Sep 17 00:00:00 2001 From: r00telement <47005506+r00telement@users.noreply.github.com> Date: Tue, 11 Jan 2022 22:38:54 +0000 Subject: [PATCH] Context menu fixes - Allow opening of titled context menus we didn't create - Allow customization of the red button context menus --- .../GameInterface/ContextMenus/ContextMenu.cs | 19 +++++----- .../ContextMenus/ContextMenuOpenedArgs.cs | 5 +++ .../ContextMenus/ContextMenuReaderWriter.cs | 38 ++++++++++++------- .../Client/UI/Agent/AgentContextItemData.cs | 1 + .../ContextMenus/GameContextMenuItem.cs | 8 ++-- 5 files changed, 45 insertions(+), 26 deletions(-) diff --git a/PlayerTags/GameInterface/ContextMenus/ContextMenu.cs b/PlayerTags/GameInterface/ContextMenus/ContextMenu.cs index 8f93b4e..df599ed 100644 --- a/PlayerTags/GameInterface/ContextMenus/ContextMenu.cs +++ b/PlayerTags/GameInterface/ContextMenus/ContextMenu.cs @@ -314,7 +314,14 @@ namespace PlayerTags.GameInterface.ContextMenus // Read the context menu items from the game, then allow subscribers to modify them ContextMenuReaderWriter contextMenuReaderWriter = new ContextMenuReaderWriter(m_CurrentContextMenuAgent, atkValueCount, atkValues); - m_CurrentContextMenuOpenedArgs = NotifyContextMenuOpened(addon, m_CurrentContextMenuAgent, contextMenuOpenedDelegate, contextMenuReaderWriter.Read()); + + string? title = null; + if (contextMenuReaderWriter.Title != null) + { + title = contextMenuReaderWriter.Title.TextValue; + } + + m_CurrentContextMenuOpenedArgs = NotifyContextMenuOpened(addon, m_CurrentContextMenuAgent, title, contextMenuOpenedDelegate, contextMenuReaderWriter.Read()); if (m_CurrentContextMenuOpenedArgs == null) { return; @@ -413,17 +420,10 @@ namespace PlayerTags.GameInterface.ContextMenus private unsafe void SubContextMenuOpenedImplementation(IntPtr addon, ref int atkValueCount, ref AtkValue* atkValues) { - // For now, don't allow to modifying sub context menus unless we created them. - // TODO: May want to allow this. - if (m_CurrentSelectedItem == null) - { - return; - } - ContextMenuOpenedImplementation(addon, ref atkValueCount, ref atkValues); } - private unsafe ContextMenuOpenedArgs? NotifyContextMenuOpened(IntPtr addon, IntPtr agent, ContextMenuOpenedDelegate contextMenuOpenedDelegate, IEnumerable initialContextMenuItems) + private unsafe ContextMenuOpenedArgs? NotifyContextMenuOpened(IntPtr addon, IntPtr agent, string? title, ContextMenuOpenedDelegate contextMenuOpenedDelegate, IEnumerable initialContextMenuItems) { var parentAddonName = GetParentAddonName(addon); @@ -451,6 +451,7 @@ namespace PlayerTags.GameInterface.ContextMenus var contextMenuOpenedArgs = new ContextMenuOpenedArgs(addon, agent, parentAddonName, initialContextMenuItems) { + Title = title, ItemContext = itemContext, GameObjectContext = gameObjectContext }; diff --git a/PlayerTags/GameInterface/ContextMenus/ContextMenuOpenedArgs.cs b/PlayerTags/GameInterface/ContextMenus/ContextMenuOpenedArgs.cs index 59761a3..ed6dee6 100644 --- a/PlayerTags/GameInterface/ContextMenus/ContextMenuOpenedArgs.cs +++ b/PlayerTags/GameInterface/ContextMenus/ContextMenuOpenedArgs.cs @@ -24,6 +24,11 @@ namespace PlayerTags.GameInterface.ContextMenus /// public string? ParentAddonName { get; } + /// + /// The title of the context menu. + /// + public string? Title { get; init; } + /// /// The items in the context menu. /// diff --git a/PlayerTags/GameInterface/ContextMenus/ContextMenuReaderWriter.cs b/PlayerTags/GameInterface/ContextMenus/ContextMenuReaderWriter.cs index 21f8414..e7334ee 100644 --- a/PlayerTags/GameInterface/ContextMenus/ContextMenuReaderWriter.cs +++ b/PlayerTags/GameInterface/ContextMenus/ContextMenuReaderWriter.cs @@ -239,16 +239,22 @@ namespace PlayerTags.GameInterface.ContextMenus } // Get the action - byte* actions = null; + byte action = 0; if (IsInventoryContext) { - actions = &((AgentInventoryContext*)m_Agent)->Actions; + var actions = &((AgentInventoryContext*)m_Agent)->Actions; + action = *(actions + contextMenuItemAtkValueBaseIndex); + } + else if (StructLayout != null && StructLayout.Value == SubContextMenuStructLayout.Alternate) + { + var actions = &((AgentContext*)m_Agent)->ItemData->RedButtonActions; + action = (byte)*(actions + contextMenuItemIndex); } else { - actions = &((AgentContext*)m_Agent)->ItemData->Actions; - } - byte action = *(actions + contextMenuItemAtkValueBaseIndex); + var actions = &((AgentContext*)m_Agent)->ItemData->Actions; + action = *(actions + contextMenuItemAtkValueBaseIndex); + } // Get the has previous indicator flag var hasPreviousIndicatorFlagsAtkValue = &m_AtkValues[HasPreviousIndicatorFlagsIndex]; @@ -281,7 +287,7 @@ namespace PlayerTags.GameInterface.ContextMenus return gameContextMenuItems.ToArray(); } - public unsafe void Write(ContextMenuOpenedArgs contextMenuOpenedArgs, ContextMenuItem selectedContextMenuItem, AtkValueChangeTypeDelegate_Unmanaged atkValueChangeType, AtkValueSetStringDelegate_Unmanaged atkValueSetString) + public unsafe void Write(ContextMenuOpenedArgs contextMenuOpenedArgs, ContextMenuItem? selectedContextMenuItem, AtkValueChangeTypeDelegate_Unmanaged atkValueChangeType, AtkValueSetStringDelegate_Unmanaged atkValueSetString) { var newAtkValuesCount = FirstContextMenuItemIndex + (contextMenuOpenedArgs.ContextMenuItems.Count() * TotalDesiredAtkValuesPerContextMenuItem); @@ -358,7 +364,7 @@ namespace PlayerTags.GameInterface.ContextMenus byte action = 0; if (contextMenuItem is GameContextMenuItem gameContextMenuItem) { - action = gameContextMenuItem.ItemSelectedAction; + action = gameContextMenuItem.SelectedAction; } else if (contextMenuItem is CustomContextMenuItem customContextMenuItem) { @@ -375,7 +381,8 @@ namespace PlayerTags.GameInterface.ContextMenus { if (IsInventoryContext) { - action = 0x30; + // TODO: Fix inventory sub context menus + action = /*0x30*/ 0xff; } else { @@ -383,16 +390,21 @@ namespace PlayerTags.GameInterface.ContextMenus } } - byte* actions = null; if (IsInventoryContext) { - actions = &((AgentInventoryContext*)m_Agent)->Actions; + var actions = &((AgentInventoryContext*)m_Agent)->Actions; + *(actions + FirstContextMenuItemIndex + contextMenuItemIndex) = action; + } + else if (StructLayout != null && StructLayout.Value == SubContextMenuStructLayout.Alternate) + { + var actions = &((AgentContext*)m_Agent)->ItemData->RedButtonActions; + *(actions + contextMenuItemIndex) = action; } else { - actions = &((AgentContext*)m_Agent)->ItemData->Actions; + var actions = &((AgentContext*)m_Agent)->ItemData->Actions; + *(actions + FirstContextMenuItemIndex + contextMenuItemIndex) = action; } - *(actions + FirstContextMenuItemIndex + contextMenuItemIndex) = action; if (contextMenuItem.Indicator == ContextMenuItemIndicator.Previous) { @@ -436,7 +448,7 @@ namespace PlayerTags.GameInterface.ContextMenus object? value = null; if (atkValue->Type == FFXIVClientStructs.FFXIV.Component.GUI.ValueType.Int) { - value = atkValue->Int; + value = $"{atkValue->Int:X}"; } else if (atkValue->Type == FFXIVClientStructs.FFXIV.Component.GUI.ValueType.Bool) { diff --git a/PlayerTags/GameInterface/ContextMenus/FFXIVClientStructs/FFXIV/Client/UI/Agent/AgentContextItemData.cs b/PlayerTags/GameInterface/ContextMenus/FFXIVClientStructs/FFXIV/Client/UI/Agent/AgentContextItemData.cs index c6b34d1..501f62f 100644 --- a/PlayerTags/GameInterface/ContextMenus/FFXIVClientStructs/FFXIV/Client/UI/Agent/AgentContextItemData.cs +++ b/PlayerTags/GameInterface/ContextMenus/FFXIVClientStructs/FFXIV/Client/UI/Agent/AgentContextItemData.cs @@ -9,5 +9,6 @@ namespace FFXIVClientStructs.FFXIV.Client.UI.Agent [FieldOffset(0x0)] public ushort AtkValuesCount; [FieldOffset(0x8)] public AtkValue AtkValues; [FieldOffset(0x428)] public byte Actions; + [FieldOffset(0x598)] public ulong RedButtonActions; } } diff --git a/PlayerTags/GameInterface/ContextMenus/GameContextMenuItem.cs b/PlayerTags/GameInterface/ContextMenus/GameContextMenuItem.cs index a3edc3b..1f47895 100644 --- a/PlayerTags/GameInterface/ContextMenus/GameContextMenuItem.cs +++ b/PlayerTags/GameInterface/ContextMenus/GameContextMenuItem.cs @@ -10,17 +10,17 @@ namespace PlayerTags.GameInterface.ContextMenus /// /// The game action that will be handled when the item is selected. /// - public byte ItemSelectedAction { get; } + public byte SelectedAction { get; } /// /// Initializes a new instance of the class. /// /// The name of the item. - /// The game action that will be handled when the item is selected. - public GameContextMenuItem(SeString name, byte itemSelectedAction) + /// The game action that will be handled when the item is selected. + public GameContextMenuItem(SeString name, byte selectedAction) : base(name) { - ItemSelectedAction = itemSelectedAction; + SelectedAction = selectedAction; } } } \ No newline at end of file