From ce6850acd348b7cbbd081e392e561809a4ecae60 Mon Sep 17 00:00:00 2001 From: jdflyer Date: Fri, 7 Aug 2026 15:27:23 -0700 Subject: [PATCH] Fix small issues, update example in modding.md to show scoped hooks --- docs/modding.md | 44 ++++++++++++++++++++++++++-------------- src/dusk/ui/menu_bar.cpp | 6 ++++-- src/m_Do/m_Do_main.cpp | 16 ++++++++++----- 3 files changed, 44 insertions(+), 22 deletions(-) diff --git a/docs/modding.md b/docs/modding.md index 7f5f7e4395..89a395ac5d 100644 --- a/docs/modding.md +++ b/docs/modding.md @@ -648,40 +648,54 @@ dusklight when their condition is met. Note: for any gamemode wishing to use the vanilla set of savefiles, use `gczelda2` as the save file name. ```cpp +// An example that shows registering a gamemode with function hooks that are scoped to the gamemode being active +IMPORT_SERVICE(LogService, svc_log); +IMPORT_SERVICE(HookService, svc_hook); IMPORT_SERVICE(GamemodeService, svc_gamemode); #define MY_GAMEMODE_ID "gamemodeid" -#define ONLY_GAMEMODE(ctx, id) \ - { \ - bool isGamemodeActive; \ - svc_gamemode->is_active(ctx, id, &isGamemodeActive); \ - if (!isGamemodeActive) { \ - return HOOK_CONTINUE; \ - } \ - } -#define ONLY_GAMEMODE_MYGAMEMODE ONLY_GAMEMODE(ctx, MY_GAMEMODE_ID) + static HookAction myFunctionHook(ModContext *ctx, void *args, void *, void *) { - // Note: normal function hooks will need to check if the gamemode is active - ONLY_GAMEMODE_MYGAMEMODE // A macro like this can make it easy - - // Code that runs when only the Gamemode is active + // If we wish to have this hook only run while the gamemode is registered, we need to hook the function from the + // gamemode's onActivatedFunction, and uninstall the hook during the onDeactivatedFunction. An example is given below return HOOK_CONTINUE; } +DEFINE_HOOK(fopAcM_createItem, CreateItem); + +void onGamemodeActivated() { + // Setup the gamemode, Add any hooks that are gamemode specific + // Overlay any files that are gamemode specific + ModResult result = mods::hook_add_pre(svc_hook, myFunctionHook); + if (result != MOD_OK) { + svc_log->error(mod_ctx, "failed to install hook to fopAcM_createItem"); + } +} + +void onGamemodeDeactivated() { + // Uninstall any hooks that are gamemode specific + // Remove overlays to any files that are gamemode specific + ModResult result = mods::hook_uninstall(); + if (result != MOD_OK) { + svc_log->error(mod_ctx, "failed to uninstall CreateItem hook"); + } +} + void onSaveLoaded() { // This function will be invoked by the game as a save is loaded } +// Register the gamemode when the mod is initialized const GamemodeDesc gamemodeDesc = { .gamemodeId = MY_GAMEMODE_ID, .fullName = "Gamemode Name", // The save name should be something that other gamemodes will not try to use, so appending your name to it // is reccomended .saveName = "my-unique-save-name_developer-name", - .onActivatedFunction = nullptr, // Called when the gamemode is selected on the prelaunch menu (or is launched) - .onDeactivatedFunction = nullptr, // Called when the gamemode is deselected on the prelaunch menu (or the mod is disabled) + .onActivatedFunction = onGamemodeActivated, // Called when the gamemode is selected on the prelaunch menu (or is launched) + .onDeactivatedFunction = onGamemodeDeactivated, // Called when the gamemode is deselected on the prelaunch menu (or the mod is disabled) .onPlayFunction = nullptr, // Called when "Play" is pressed on the prelaunch menu .onSaveLoadedFunction = onSaveLoaded, // Called when a save is loaded .onNewSaveFunction = nullptr, // Called after a new savefile is created diff --git a/src/dusk/ui/menu_bar.cpp b/src/dusk/ui/menu_bar.cpp index 28f51e1e6a..3b1c8f9b84 100644 --- a/src/dusk/ui/menu_bar.cpp +++ b/src/dusk/ui/menu_bar.cpp @@ -95,13 +95,15 @@ MenuBar::MenuBar() dismiss(modal); return; } + dismiss(modal); if (gamemode::getGamemodeManager().getRegisteredGamemodes().size() > 1) { // If we have gamemodes registered, show pre-launch on a menubar reset prelaunch_state().showPrelaunchOnReset = true; + Document::hide(true); + }else { + hide(false); } JUTGamePad::C3ButtonReset::sResetSwitchPushing = true; - dismiss(modal); - Document::hide(true); }, }, }, diff --git a/src/m_Do/m_Do_main.cpp b/src/m_Do/m_Do_main.cpp index 3632c38ab9..166ba25535 100644 --- a/src/m_Do/m_Do_main.cpp +++ b/src/m_Do/m_Do_main.cpp @@ -728,6 +728,8 @@ int game_main(int argc, char* argv[]) { saveConfigBeforePrelaunch = true; } + bool skipPreLaunchUI = dusk::getSettings().backend.skipPreLaunchUI.getValue(); + std::string dvd_path = dusk::getSettings().backend.isoPath; bool dvd_opened = false; if (parsed_arg_options.count("dvd")) { @@ -744,6 +746,7 @@ int game_main(int argc, char* argv[]) { dusk::DiscVerificationState::Unknown); dusk::config::save(); dusk::IsGameLaunched = true; + skipPreLaunchUI = true; } } else { DuskLog.warn("DVD image from command line failed validation: {}, opening prelaunch UI", dvd_path); @@ -751,8 +754,6 @@ int game_main(int argc, char* argv[]) { } } - bool skipPreLaunchUI = dusk::getSettings().backend.skipPreLaunchUI.getValue(); - // If we can't load right into the game, stop requesting to load a stage or save if (forcePreLaunchUI || dvd_path.empty()) { if (dusk::StageRequested.set) { @@ -907,9 +908,14 @@ int game_main(int argc, char* argv[]) { dusk::speedrun::registerSpeedrunGamemode(); } - if (skipPreLaunchUI == true && dusk::gamemode::getGamemodeManager().getRegisteredGamemodes().size() > 1 && dusk::getSettings().backend.skipPreLaunchUI.getValue()) { - // Force pre-launch if we have registered gamemodes that we need to choose from - dusk::ui::push_document(std::make_unique(), true); + if (skipPreLaunchUI == true) { + if (dusk::gamemode::getGamemodeManager().getRegisteredGamemodes().size() > 1 && dusk::getSettings().backend.skipPreLaunchUI.getValue()) { + // Force pre-launch if we have registered gamemodes that we need to choose from + dusk::ui::push_document(std::make_unique(), true); + } else { + // If we get back to prelaunch later, tell it that we've already started the game + dusk::ui::prelaunch_state().firstLaunch = false; + } } OSReport("Starting main01 (Game Loop)...\n");