Editing Menus

From PMDOWiki

This guide does not apply to the Settings Menu. Please refer to its specific page if that's what you are looking for.

As of 0.8.3, a new service callback has been added to the game, called AddMenu. This callback can be used by modders to edit menus on the fly right before they appear on screen. This guide exists as a way to explain how this callback works and how to use it.

Prerequisites

This guide assumes that the user is capable of writing Services and Creating Menus. Please refer to these pages if anything is unclear. The user is also expected to be able to navigate the RogueEssence repository, specifically looking into its Menu folder and its subfolders.

With the advent of 0.8.3, the callback AddMenu has been added to the game. This message is sent to services just before a menu is displayed so that modders can add, remove or modify its elements at their heart's content. What can be edited and how depends on the menu itself. Feel free to check the Menu Editing Reference page if you ever need more details on a specific function.

There are three broader types of Menus. Each of them has one Scriptable counterpart and they are InteractableMenu, ChoiceMenu and MultiPageMenu. Being able to differentiate between them is key to know how to interact with the menu you're looking to edit. Furthermore, we will need the menu's Label. A Label is a string property that serves to identify a menu. Lucky for us, the game already gives us everything we need to know by just opening the F1 overlay and hovering over the menu itself with your mouse. Here's the OthersMenu, for example:

This is already everything we need. We can now start working on our changes.

How to edit a menu

Say that we want to reproduce what the menu_tools service does in the base game. Since we already have our menu type and our label, we can start writing our AddMenu callback. The AddMenu event message contains an argument, so you will need to pass that argument on to your callback like this:
med:Subscribe("MenuTools", EngineServiceEvents.AddMenu, function(_, args) self:OnAddMenu(args[0]) end )

Now we write the function. Here's what menu_tools does:

function MenuTools:OnAddMenu(menu)
    if RogueEssence.GameManager.Instance.CurrentScene == RogueEssence.Dungeon.DungeonScene.Instance
            and menu:HasLabel() and menu.Label == "OTHERS_MENU" then
        local index = menu:GetChoiceIndexByLabel("OTH_SETTINGS")
        if index <0 then index = math.min(1, menu.Choices.Count) end
        local choices = menu:ExportChoices()
        choices:Insert(index, RogueEssence.Menu.MenuTextChoice("OTH_RECRUIT", RogueEssence.StringKey("MENU_RECRUITMENT"):ToLocal(), function () _MENU:AddMenu(RecruitmentListMenu:new().menu, false) end))
        menu:ImportChoices(choices)
    end
end

Now, what does this all mean? Let's analyze it one step at a time.

First things first, this new option must only appear while inside a dungeon, so the first part of the if statement makes sure that's the case. We must then check if the menu that has been opened has a label, and, if it does, check if it's the label we're looking for.

Now that we know that this is the right menu, we want to add our new option just above Settings. Since menu elements can also have labels, we ask the menu for the index of its OTH_SETTINGS option and save that value. The function then checks if the number is valid, and defaults to slot 1 if it isn't, or even 0 if the menu is empty for some reason. If you ever need an option's label, you can either hover it while displaying the F1 overlay or look them up inside of the ILabeled.cs file.

Finally, we apply the change: We first export the current list of choices, then add our new MenuTextChoice in the spot we chose and then load our edited list using ImportChoices.

And it's done! The game will now display the Recruitment Search option every time we open the Others menu while we're inside a dungeon.

This process can be applied to any menu that has a label. The only exception is SettingsMenu, that has its own page describing how to interact with it.