-
-
Notifications
You must be signed in to change notification settings - Fork 1
Link API v1
Are you a developer looking to fully integrate your mod into Horrible Menu's interface? Well, you found the right place! The following guide teaches you how to register the metadata and handle toggling for your options.
Let's start off by adding this mod as a dependency in your mod.json!
Tip
We recommend shortening include paths in your code by adding the the full path as a private include directory through your project's CMakeLists.txt file. The rest of the documentation will use this practice in its code samples.
target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/geode-deps/cubicstudios.horriblemenu/include)You can directly access the Horrible Menu mod menu API by including the API.h file in your code. Make sure to include the horrible namespace to directly access all needed classes and methods.
#include <horrible/API.h>
using namespace horrible;The purpose of this mod is primarily focused on its huge list of togglable troll options. To create the metadata for an option, create a new horrible::Option object pointer.
using namespace horrible;
auto myOption = Option::create("my-option"_spr);Tip
To avoid conflicts with this or other mods' option IDs, prefix your option IDs with you mod ID by appending _spr at the end of your string.
You should now have a new smart pointer to an option's metadata. Before proceeding, be sure to provide more information about your option with the following setter functions.
-
setName(std::string): Name of the option -
setDescription(std::string): Description of the option -
setCategory(std::string): Name of the category this option should be under -
setSillyTier(SillyTier): How silly the option is -
setDefaultToggleState(bool): Default toggle state for this option -
setOnline(bool): If the option requires an active internet connection to work properly -
setRequiresRestart(bool): If the option requires a game restart to take effect -
setSupportedPlatforms(std::vector<Platform>): Platforms that the option supports
Each of these setters has its respective getter in case you ever need to read the information you've stored in these objects.
The Option class was designed to allow a readable, syntax-sugary builder pattern to simplify the creation process.
auto myOption = Option::create("my-option"_spr)
->setName("My Very Cool Option")
->setDescription("A very detailed description about what this option does...")
->setCategory("My Stuff!")
->setSillyTier(SillyTier::Low);There are a few ways to register your option to Horrible Menu's option manager in your code. The example practices below assume you're creating each of your Option objects in their own respective source files.
This is the safest option, as HORRIBLE_REGISTER_OPTION expands into an $on_mod(Loaded) block which safely calls the register function for you once your mod finishes fully loading. It's recommended you call this right after creating your Option object globally.
static auto myOption = Option::create("my-option"_spr)
->setName("My Very Cool Option")
->setDescription("A very detailed description about what this option does...")
->setCategory("My Stuff!")
->setSillyTier(SillyTier::Low);
HORRIBLE_REGISTER_OPTION(myOption);In the same chain where you create and build your option, you can call autoRegister at the very end. This will register your option the moment it's created, during your mod's static initialization.
static auto myOption = Option::create("my-option"_spr)
->setName("My Very Cool Option")
->setDescription("A very detailed description about what this option does...")
->setCategory("My Stuff!")
->setSillyTier(SillyTier::Low)
->autoRegister();Note
Metadata such as category names can be cached at option registration for the entire game session to help improve performance. Calling autoRegister on your builder before you actually finish setting up your option's metadata can cause unexpected issues in UI!
Of course, all of these still call OptionManager::registerOption in the end, if you prefer registering your option during a different loading phase for your mod, feel free to manually call the register function yourself!
static auto myOption = Option::create("my-option"_spr)
->setName("My Very Cool Option")
->setDescription("A very detailed description about what this option does...")
->setCategory("My Stuff!")
->setSillyTier(SillyTier::Low);
$on_game(Loaded) {
OptionManager::get()->registerOption(myOption);
};So, your options should now be registered. Great! Now, we can move to actually handling them functionally by listening to when they're being toggled.
Before proceeding, it's best to familiarize yourself with the save data object this mod uses to store user data for each option.
struct HorribleOptionSave final {
bool enabled = false; // If the option is toggled on
bool pin = false; // If the option is pinned in the menu UI
bool viewed = false; // If the option has been used or viewed
};Tip
You'll likely only need to check for enabled, as the other two are reserved for the user interface, already handled by this mod. Here's the OptionManager method to check only for the toggle state.
bool enabled = OptionManager::get()->isEnabled("my-option"_spr);This API has a handy macro that allows you to automatically and safely register your modified classes' hooks to Horrible Menu's options manager, so you aren't the one managing them yourself.
HORRIBLE_DELEGATE_HOOKS(optID)This macro expands into a static void onModify() function inside your modified class that calls horrible::delegateHooks to register your hooks for automatic toggling.
[!INFO] By including this macro, all hooks in that modified class will be toggled on or off in realtime whenever the option for it is toggled by the player through the UI.
Option toggling can also be listened to via the provided thread-safe, global event class, horrible::OptionEvent - which you can listen for on any thread, with or without a filter.
Normally, you'd only want to filter through events that actually have to do with one option in some places. The event allows you to add ID for the option you would like to listen to specifically.
OptionEvent("my-option"_spr);If you need to listen for global events, which will involve other mods' options, simply provide no ID to make it listen for every single toggle.
OptionEvent();Listening to option events under a node is pretty straightforward.
this->addEventListener(
OptionEvent("my-option"_spr),
[](HorribleOptionSave data) {
log::info("{} is set to: {}", "my-option"_spr, data.enabled);
}); this->addEventListener(
OptionEvent(),
[](std::string_view id, HorribleOptionSave data) {
log::info("{} is set to: {}", id, data.enabled);
});There are a couple of utility functions you can use to conveniently listen for option toggle events globally.
Creates a listener for option toggles for one specific option.
using namespace horrible;
listenForHorribleOptionChanges(
"my-option"_spr,
[](HorribleOptionSave data) {
log::info("{} is set to: {}", "my-option"_spr, data.enabled);
}
);Creates a listener for any option's toggling.
using namespace horrible;
listenForAllHorribleOptionChanges(
[](std::string_view id, HorribleOptionSave data) {
log::info("{} is set to: {}", id, data.enabled);
}
);That should be all! Hopefully you got the gist of it! If not, feel free to join our Discord server and ask about anything that was potentially missing from here or that you did not fully understand. Happy modding!
Your favorite source of gaming disaster.