コード例 #1
0
ファイル: ContextMenu.cs プロジェクト: r1ft4469/Dalamud
        private unsafe void ContextMenuItemSelectedImplementation(AddonContextMenu *addonContextMenu, int selectedIndex)
        {
            if (this.currentContextMenuOpenedArgs == null || selectedIndex == -1)
            {
                this.currentContextMenuOpenedArgs   = null;
                this.selectedOpenSubContextMenuItem = null;
                return;
            }

            // Read the selected item directly from the game
            ContextMenuReaderWriter contextMenuReaderWriter = new ContextMenuReaderWriter(this.currentAgentContextInterface, addonContextMenu->AtkValuesCount, addonContextMenu->AtkValues);
            var gameContextMenuItems = contextMenuReaderWriter.Read();
            var gameSelectedItem     = gameContextMenuItems.ElementAtOrDefault(selectedIndex);

            // This should be impossible
            if (gameSelectedItem == null)
            {
                this.currentContextMenuOpenedArgs   = null;
                this.selectedOpenSubContextMenuItem = null;
                return;
            }

            // Match it with the items we already know about based on its name.
            // We can get into a state where we have a game item we don't recognize when another plugin has added one.
            var selectedItem = this.currentContextMenuOpenedArgs.Items.FirstOrDefault(item => item.Name.Encode().SequenceEqual(gameSelectedItem.Name.Encode()));

            this.selectedOpenSubContextMenuItem = null;
            if (selectedItem is CustomContextMenuItem customContextMenuItem)
            {
                try
                {
                    var customContextMenuItemSelectedArgs = new CustomContextMenuItemSelectedArgs(this.currentContextMenuOpenedArgs, customContextMenuItem);
                    customContextMenuItem.ItemSelected(customContextMenuItemSelectedArgs);
                }
                catch (Exception ex)
                {
                    PluginLog.LogError(ex, "ContextMenuItemSelectedImplementation");
                }
            }
            else if (selectedItem is OpenSubContextMenuItem openSubContextMenuItem)
            {
                this.selectedOpenSubContextMenuItem = openSubContextMenuItem;
            }

            this.currentContextMenuOpenedArgs = null;
        }
コード例 #2
0
ファイル: ContextMenu.cs プロジェクト: r1ft4469/Dalamud
        private unsafe bool SubContextMenuOpeningImplementation(AgentContext *agentContext)
        {
            if (this.openSubContextMenu == null || this.selectedOpenSubContextMenuItem == null)
            {
                return(false);
            }

            // The important things to make this work are:
            // 1. Allocate a temporary sub context menu title. The value doesn't matter, we'll set it later.
            // 2. Context menu item count must equal 1 to tell the game there is enough space for the "< Return" item.
            // 3. Atk value count must equal the index of the first context menu item.
            //    This is enough to keep the base data, but excludes the context menu item data.
            //    We want to exclude context menu item data in this function because the game sometimes includes garbage items which can cause problems.
            //    After this function, the game adds the "< Return" item, and THEN we add our own items after that.

            this.openSubContextMenu(agentContext);

            // Allocate a new 1 byte title. This is required for the game to render the titled context menu style.
            // The actual value doesn't matter at this point, we'll set it later.
            MemoryHelper.GameFree(ref this.currentSubContextMenuTitle, (ulong)IntPtr.Size);
            this.currentSubContextMenuTitle = MemoryHelper.GameAllocateUi(1);
            *(&(&agentContext->AgentContextInterface)->SubContextMenuTitle) = (byte *)this.currentSubContextMenuTitle;
            *(byte *)this.currentSubContextMenuTitle = 0;

            // Expect at least 1 context menu item.
            (&agentContext->Items->AtkValues)[0].UInt = 1;

            // Expect a title. This isn't needed by the game, it's needed by ContextMenuReaderWriter which uses this to check if it's a context menu
            (&agentContext->Items->AtkValues)[1].ChangeType(ValueType.String);

            (&agentContext->Items->AtkValues)[1].String = (byte *)0;

            ContextMenuReaderWriter contextMenuReaderWriter = new ContextMenuReaderWriter(&agentContext->AgentContextInterface, agentContext->Items->AtkValueCount, &agentContext->Items->AtkValues);

            *(&agentContext->Items->AtkValueCount) = (ushort)contextMenuReaderWriter.FirstContextMenuItemIndex;

            return(true);
        }
コード例 #3
0
ファイル: ContextMenu.cs プロジェクト: r1ft4469/Dalamud
        private unsafe void ContextMenuOpenedImplementation(AddonContextMenu *addonContextMenu, ref int atkValueCount, ref AtkValue *atkValues)
        {
            if (this.ContextMenuOpened == null ||
                this.currentAgentContextInterface == null)
            {
                return;
            }

            var contextMenuReaderWriter = new ContextMenuReaderWriter(this.currentAgentContextInterface, atkValueCount, atkValues);

            // Check for a title.
            string?title = null;

            if (this.selectedOpenSubContextMenuItem != null)
            {
                title = this.selectedOpenSubContextMenuItem.Name.TextValue;

                // Write the custom title
                var titleAtkValue = &atkValues[1];
                fixed(byte *titlePtr = this.selectedOpenSubContextMenuItem.Name.Encode().NullTerminate())
                {
                    titleAtkValue->SetString(titlePtr);
                }
            }
            else if (contextMenuReaderWriter.Title != null)
            {
                title = contextMenuReaderWriter.Title.TextValue;
            }

            // Determine which event to raise.
            var contextMenuOpenedDelegate = this.ContextMenuOpened;

            // this.selectedOpenSubContextMenuItem is OpenSubContextMenuItem openSubContextMenuItem
            if (this.selectedOpenSubContextMenuItem != null)
            {
                contextMenuOpenedDelegate = this.selectedOpenSubContextMenuItem.Opened;
            }

            // Get the existing items from the game.
            // TODO: For inventory sub context menus, we take only the last item -- the return item.
            // This is because we're doing a hack to spawn a Second Tier sub context menu and then appropriating it.
            var contextMenuItems = contextMenuReaderWriter.Read();

            if (IsInventoryContext(this.currentAgentContextInterface) && this.selectedOpenSubContextMenuItem != null)
            {
                contextMenuItems = contextMenuItems.TakeLast(1).ToArray();
            }

            var beforeHashCode = GetContextMenuItemsHashCode(contextMenuItems);

            // Raise the event and get the context menu changes.
            this.currentContextMenuOpenedArgs = this.NotifyContextMenuOpened(addonContextMenu, this.currentAgentContextInterface, title, contextMenuOpenedDelegate, contextMenuItems);
            if (this.currentContextMenuOpenedArgs == null)
            {
                return;
            }

            var afterHashCode = GetContextMenuItemsHashCode(this.currentContextMenuOpenedArgs.Items);

            PluginLog.Warning($"{beforeHashCode}={afterHashCode}");

            // Only write to memory if the items were actually changed.
            if (beforeHashCode != afterHashCode)
            {
                // Write the new changes.
                contextMenuReaderWriter.Write(this.currentContextMenuOpenedArgs.Items);

                // Update the addon.
                atkValueCount = *(&addonContextMenu->AtkValuesCount) = (ushort)contextMenuReaderWriter.AtkValueCount;
                atkValues     = *(&addonContextMenu->AtkValues) = contextMenuReaderWriter.AtkValues;
            }
        }