diff --git a/docs/modding.md b/docs/modding.md index 3c8a4908c1..ff62666aa1 100644 --- a/docs/modding.md +++ b/docs/modding.md @@ -699,7 +699,7 @@ const GamemodeDesc gamemodeDesc = { .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 - .onNewSaveSelectFunction = nullptr, // Called while the flow before the file name select is ran (see below) + .onNewSaveSelectFunction = nullptr, // Called during the flow before the file name select is ran (see below) .onGameResetFunction = nullptr, // Called when the game is reset .onTickFunction = nullptr, // Called every game tick while the gamemode is active }; @@ -710,55 +710,47 @@ Within the gamemode service, a gamemode can also request to load a UI for per-sa create a new file is pressed. ```cpp -#include "d/d_file_select.h" // Requires game feature IMPORT_SERVICE(GamemodeService, svc_gamemode); IMPORT_SERVICE(UiService, svc_ui); -bool proceedUI = false; -UiWindowHandle windowHandle; +void onNewSaveSelect(bool *out_proceedToNameSelect, bool *out_returnToFileSelect) { + static bool *proceedToNameSelect; + static bool *returnToFileSelect; + static UiWindowHandle windowHandle; -bool onNewSaveSelect(dFile_select_c *fileSelect) { - if (fileSelect->mGamemodeSaveStartBuildUi) { - proceedUI = false; - UiTabDesc tabs[1]{}; + // Used within callbacks as needed + proceedToNameSelect = out_proceedToNameSelect; + returnToFileSelect = out_returnToFileSelect; - tabs[0].struct_size = sizeof(UiTabDesc); - tabs[0].title = "Play"; - tabs[0].build = [](ModContext* ctx, UiWindowHandle, UiElementHandle leftPane, UiElementHandle rightPane, void*, ModError*) { - UiControlDesc desc = UI_CONTROL_DESC_INIT; - desc.kind = UI_CONTROL_BUTTON; - desc.label = "Play"; - desc.help_rml = "Play Button"; - desc.on_pressed = [](ModContext* ctx, void* userdata) { - proceedUI = true; - svc_ui->window_close(ctx, *static_cast(userdata)); - }; - desc.user_data = &windowHandle; - svc_ui->pane_add_control(mod_ctx, leftPane, &desc, nullptr); - return MOD_OK; + UiTabDesc tabs[1]{}; + + tabs[0].struct_size = sizeof(UiTabDesc); + tabs[0].title = "Play"; + tabs[0].build = [](ModContext* ctx, UiWindowHandle, UiElementHandle leftPane, UiElementHandle rightPane, void*, ModError*) { + UiControlDesc desc = UI_CONTROL_DESC_INIT; + desc.kind = UI_CONTROL_BUTTON; + desc.label = "Play"; + desc.help_rml = "Play Button"; + desc.on_pressed = [](ModContext* ctx, void* userdata) { + *proceedToNameSelect = true; + svc_ui->window_close(ctx, *static_cast(userdata)); }; + desc.user_data = &windowHandle; + svc_ui->pane_add_control(mod_ctx, leftPane, &desc, nullptr); + return MOD_OK; + }; - UiWindowDesc desc = UI_WINDOW_DESC_INIT; - desc.tabs = tabs; - desc.tab_count = 1; - desc.user_data = fileSelect; - desc.on_closed = [](ModContext *, UiWindowHandle, void *userdata) { - dFile_select_c *i_this = static_cast(userdata); + UiWindowDesc desc = UI_WINDOW_DESC_INIT; + desc.tabs = tabs; + desc.tab_count = 1; + desc.on_closed = [](ModContext *, UiWindowHandle, void *userdata) { + // If closing the window through backing out, return to file select + if (*proceedToNameSelect == false) { + *returnToFileSelect = true; + } + }; - // if closing the window through backing out, return to file select - if (!proceedUI) { - i_this->backToDataSelectMove(); - } - }; - - svc_ui->window_push(mod_ctx, &desc, &windowHandle); - } - - // When we hit play in the UI, return true (tells the file select that we can proceed) - if (proceedUI) { - return true; - } - return false; + svc_ui->window_push(mod_ctx, &desc, &windowHandle); } const GamemodeDesc gamemodeDesc = { diff --git a/include/d/d_file_select.h b/include/d/d_file_select.h index 0a0772e807..271784803c 100644 --- a/include/d/d_file_select.h +++ b/include/d/d_file_select.h @@ -426,7 +426,6 @@ public: nameMoveAnmInitSet(0xd29, 0xd1f); modoruTxtDispAnmInit(0); mDataSelProc = DATASELPROC_NAME_TO_DATA_SELECT_MOVE; - mGamemodeSaveStartBuildUi = true; } #endif void _draw(); @@ -742,6 +741,8 @@ public: #ifdef TARGET_PC dDlst_FileSelFade_c mFadeDlst; bool mGamemodeSaveStartBuildUi = true; + bool mGamemodeProceedToNameSelect = false; + bool mGamemodeReturnToFileSelect = false; #endif #if PLATFORM_WII || PLATFORM_SHIELD diff --git a/sdk/include/mods/svc/gamemode.h b/sdk/include/mods/svc/gamemode.h index 81d2417528..eab97f7188 100644 --- a/sdk/include/mods/svc/gamemode.h +++ b/sdk/include/mods/svc/gamemode.h @@ -10,15 +10,17 @@ typedef struct { const char* gamemodeId; const char* fullName; - const char saveName[32]; // GCI Filenames are limited to 31 characters - void (*onActivatedFunction)(); - void (*onDeactivatedFunction)(); - void (*onPlayFunction)(); - void (*onSaveLoadedFunction)(); - void (*onNewSaveFunction)(); - bool (*onNewSaveSelectFunction)(struct dFile_select_c* fileSelect); // Should return true when any custom options flows are finished - void (*onGameResetFunction)(); - void (*onTickFunction)(); + const char saveName[32]; // Should be unique. GCI Filenames are limited to 31 characters + void (*onActivatedFunction)(); // Called when the gamemode is selected + void (*onDeactivatedFunction)(); // Called when the gamemode is deselected + void (*onPlayFunction)(); // Called when play is pressed on the prelaunch menu + void (*onSaveLoadedFunction)(); // Called whenever a savefile is loaded + void (*onNewSaveFunction)(); // Called when a new save is created + void (*onNewSaveSelectFunction)(bool* out_proceedToNameSelect, + bool* out_returnToFileSelect); // Set out_proceedToNameSelect to true once any UI flows are + // completed + void (*onGameResetFunction)(); // Called when the game is reset + void (*onTickFunction)(); // Called on every tick } GamemodeDesc; typedef struct GamemodeService { diff --git a/src/d/d_file_select.cpp b/src/d/d_file_select.cpp index 3b83c455d2..a66a216f38 100644 --- a/src/d/d_file_select.cpp +++ b/src/d/d_file_select.cpp @@ -1318,18 +1318,24 @@ void dFile_select_c::selectDataNameMove() { const dusk::gamemode::Gamemode* gamemode = dusk::gamemode::getGamemodeManager().getCurrentGamemode(); if (gamemode) { if (isHeaderTxtChange == true && isFileRecScale == true && isModoruTxtDisp == true) { - bool gamemodeFlowClosed = gamemode->invokeOnNewSaveSelectFunction(this); - if (mGamemodeSaveStartBuildUi == true) { + if (mGamemodeSaveStartBuildUi) { + gamemode->invokeOnNewSaveSelectFunction(&mGamemodeProceedToNameSelect, &mGamemodeReturnToFileSelect); mGamemodeSaveStartBuildUi = false; } - if (!gamemodeFlowClosed) { + if (mGamemodeReturnToFileSelect) { + backToDataSelectMove(); + mGamemodeSaveStartBuildUi = true; + mGamemodeProceedToNameSelect = false; + mGamemodeReturnToFileSelect = false; + return; + } + if (!mGamemodeProceedToNameSelect) { return; } }else { return; } } - mGamemodeSaveStartBuildUi = true; #endif IF_DUSK(bool isNameMove = nameMoveAnm();) @@ -1337,6 +1343,11 @@ void dFile_select_c::selectDataNameMove() { if (isHeaderTxtChange == true && isFileRecScale == true && isNameMove == true && isModoruTxtDisp == true) { +#ifdef TARGET_PC + mGamemodeSaveStartBuildUi = true; + mGamemodeProceedToNameSelect = false; + mGamemodeReturnToFileSelect = false; +#endif mDataSelProc = DATASELPROC_NAME_INPUT_WAIT; } } diff --git a/src/dusk/achievements.cpp b/src/dusk/achievements.cpp index 6c4de43487..5589cf3434 100644 --- a/src/dusk/achievements.cpp +++ b/src/dusk/achievements.cpp @@ -20,6 +20,8 @@ #include "f_op/f_op_actor_mng.h" #include "f_pc/f_pc_name.h" #include "dusk/logging.h" +#include "dusk/gamemode.hpp" +#include "dusk/speedrun.h" #include #include @@ -1302,6 +1304,12 @@ void AchievementSystem::processEntry(Entry& e) { } void AchievementSystem::tick() { + // Until we implement an AchievementService, achievements will be unavailible in custom gamemodes + if (dusk::gamemode::getGamemodeManager().isCurrentGamemode(dusk::gamemode::kVanillaGamemodeId) == false + && dusk::gamemode::getGamemodeManager().isCurrentGamemode(dusk::speedrun::kSpeedrunGamemodeId) == false) { + m_signals.clear(); + return; + } if (!m_loaded) { load(); } diff --git a/src/dusk/gamemode.cpp b/src/dusk/gamemode.cpp index 2bfaf07e7e..0736541380 100644 --- a/src/dusk/gamemode.cpp +++ b/src/dusk/gamemode.cpp @@ -12,16 +12,15 @@ GamemodeManager g_GamemodeManager; aurora::Module DuskGamemodeLog("dusk::gamemode"); GamemodeManager::GamemodeManager() { - registerGamemode(Gamemode("vanilla","Vanilla","gczelda2")); - - mCurrentGamemodeId = "vanilla"; + registerGamemode(Gamemode(kVanillaGamemodeId,"Vanilla","gczelda2")); + mCurrentGamemodeId = kVanillaGamemodeId; } void GamemodeManager::setGamemodeToPrevious() { // Gets the value from the settings of the last played gamemode id and sets that to the current gamemode (if registered) GamemodeId id = dusk::getSettings().game.lastSelectedGamemodeId; if (mRegisteredGamemodes.find(id) == mRegisteredGamemodes.end()) { - setCurrentGamemode("vanilla"); + setCurrentGamemode(kVanillaGamemodeId); return; } setCurrentGamemode(id); @@ -60,7 +59,7 @@ void GamemodeManager::unregisterGamemode(const GamemodeId& gamemodeId) { // to reset the game back to title as vanilla; ui::prelaunch_state().showPrelaunchOnReset = true; JUTGamePad::C3ButtonReset::sResetSwitchPushing = true; - setCurrentGamemode("vanilla"); + setCurrentGamemode(kVanillaGamemodeId); } mRegisteredGamemodes.erase(it); dusk::ui::Prelaunch::rebuild_menu_buttons(); diff --git a/src/dusk/gamemode.hpp b/src/dusk/gamemode.hpp index 3f9324107a..e19301eb87 100644 --- a/src/dusk/gamemode.hpp +++ b/src/dusk/gamemode.hpp @@ -6,6 +6,8 @@ namespace dusk::gamemode { using GamemodeId = std::string; +constexpr const char* kVanillaGamemodeId = "vanilla"; + // This class holds the definition for the gamemode and various function pointers to call class Gamemode { public: @@ -52,11 +54,12 @@ public: } } - bool invokeOnNewSaveSelectFunction(dFile_select_c* fileSelect) const { + void invokeOnNewSaveSelectFunction(bool* out_proceedToNameSelect, bool* out_returnToFileSelect) const { if (mOnNewSaveSelectFunction) { - return mOnNewSaveSelectFunction(fileSelect); + mOnNewSaveSelectFunction(out_proceedToNameSelect, out_returnToFileSelect); + }else { + *out_proceedToNameSelect = true; } - return true; } void invokeOnGameResetFunction() const { @@ -76,7 +79,7 @@ public: std::function mOnPlayFunction; std::function mOnSaveLoadedFunction; std::function mOnNewSaveFunction; - std::function mOnNewSaveSelectFunction; + std::function mOnNewSaveSelectFunction; std::function mOnGameResetFunction; std::function mOnTickFunction; }; @@ -89,7 +92,7 @@ public: const Gamemode* getCurrentGamemode() const { const auto& it = mRegisteredGamemodes.find(mCurrentGamemodeId); - return it != mRegisteredGamemodes.end() ? &it->second : &mRegisteredGamemodes.at("vanilla"); + return it != mRegisteredGamemodes.end() ? &it->second : &mRegisteredGamemodes.at(kVanillaGamemodeId); } bool isCurrentGamemode(const GamemodeId& id) const { const Gamemode* gamemode = getCurrentGamemode(); diff --git a/src/dusk/settings.cpp b/src/dusk/settings.cpp index af3890a05e..1665fdb102 100644 --- a/src/dusk/settings.cpp +++ b/src/dusk/settings.cpp @@ -1,5 +1,6 @@ #include "dusk/settings.h" #include "dusk/config.hpp" +#include "dusk/gamemode.hpp" #include namespace dusk { @@ -157,7 +158,7 @@ UserSettings g_userSettings = { .removeQuestMapMarkers {"game.removeQuestMapMarkers", false}, .showInputViewer {"game.showInputViewer", false}, .showInputViewerGyro {"game.showInputViewerGyro", false}, - .lastSelectedGamemodeId {"game.lastSelectedGamemodeId", "vanilla"} + .lastSelectedGamemodeId {"game.lastSelectedGamemodeId", gamemode::kVanillaGamemodeId} }, .backend = { diff --git a/src/dusk/speedrun.cpp b/src/dusk/speedrun.cpp index 3cf0c6184b..076daeade6 100644 --- a/src/dusk/speedrun.cpp +++ b/src/dusk/speedrun.cpp @@ -22,7 +22,7 @@ static void onSpeedrunModeDeactive() { } void registerSpeedrunGamemode() { - dusk::gamemode::Gamemode speedrunGamemode(DUSK_SPEEDRUN_GAMEMODE_ID,"Speedrun","gczelda2-speedrun"); + dusk::gamemode::Gamemode speedrunGamemode(kSpeedrunGamemodeId,"Speedrun","gczelda2-speedrun"); speedrunGamemode.mOnSaveLoadedFunction = dusk::speedrun::start; speedrunGamemode.mOnActivatedFunction = onSpeedrunModeActive; speedrunGamemode.mOnDeactivatedFunction = onSpeedrunModeDeactive; @@ -32,7 +32,7 @@ void registerSpeedrunGamemode() { } void unregisterSpeedrunGamemode() { - dusk::gamemode::getGamemodeManager().unregisterGamemode(DUSK_SPEEDRUN_GAMEMODE_ID); + dusk::gamemode::getGamemodeManager().unregisterGamemode(kSpeedrunGamemodeId); } void resetForSpeedrunMode() { diff --git a/src/dusk/speedrun.h b/src/dusk/speedrun.h index de21a2092e..1a70167113 100644 --- a/src/dusk/speedrun.h +++ b/src/dusk/speedrun.h @@ -2,11 +2,13 @@ #include #include "dusk/gamemode.hpp" -#define DUSK_SPEEDRUN_GAMEMODE_ID "vanilla_speedrun" namespace dusk::speedrun { +constexpr const char* kSpeedrunGamemodeId = "vanilla_speedrun"; + struct SpeedrunInfo { + void startRun() { m_isRunStarted = true; m_rtaStartTimestamp = OSGetNativeTime(); @@ -51,7 +53,7 @@ void resetForSpeedrunMode(); void restoreFromSpeedrunMode(); inline bool isActive() { - return dusk::gamemode::getGamemodeManager().isCurrentGamemode(DUSK_SPEEDRUN_GAMEMODE_ID); + return dusk::gamemode::getGamemodeManager().isCurrentGamemode(kSpeedrunGamemodeId); } } // namespace dusk diff --git a/src/dusk/ui/menu_bar.cpp b/src/dusk/ui/menu_bar.cpp index 3b1c8f9b84..6ae31987dd 100644 --- a/src/dusk/ui/menu_bar.cpp +++ b/src/dusk/ui/menu_bar.cpp @@ -63,7 +63,11 @@ MenuBar::MenuBar() mTabBar->add_tab("Editor", [this] { push(std::make_unique()); }); } - mTabBar->add_tab("Achievements", [this] { push(std::make_unique()); }); + // Only allow us to access achievements if we are playing on a gamemode that uses them + if (dusk::gamemode::getGamemodeManager().isCurrentGamemode(dusk::gamemode::kVanillaGamemodeId) + || dusk::gamemode::getGamemodeManager().isCurrentGamemode(dusk::speedrun::kSpeedrunGamemodeId)) { + mTabBar->add_tab("Achievements", [this] { push(std::make_unique()); }); + } mTabBar->add_tab("Mods", [this] { push(std::make_unique()); }); for (auto& tab : mods::svc::ui_mod_menu_tabs()) { mTabBar->add_tab(tab.label, std::move(tab.onSelected)); diff --git a/src/dusk/ui/overlay.cpp b/src/dusk/ui/overlay.cpp index bbc23f6dd2..be95e99a4f 100644 --- a/src/dusk/ui/overlay.cpp +++ b/src/dusk/ui/overlay.cpp @@ -337,7 +337,7 @@ void Overlay::update() { rtaElapsedTime = speedrun::g_speedrunInfo.m_rtaTimer; } - if (speedrun::g_speedrunInfo.m_isRunStarted && !speedrun::g_speedrunInfo.m_isRunStarted) { + if (speedrun::g_speedrunInfo.m_isRunStarted && !speedrun::g_speedrunInfo.m_isPauseIGT) { speedrun::g_speedrunInfo.m_igtTimer = OSGetTime() - speedrun::g_speedrunInfo.m_igtStartTimestamp - speedrun::g_speedrunInfo.m_totalLoadTime; } diff --git a/src/dusk/ui/prelaunch.cpp b/src/dusk/ui/prelaunch.cpp index 887b0a0af1..27b3f29c89 100644 --- a/src/dusk/ui/prelaunch.cpp +++ b/src/dusk/ui/prelaunch.cpp @@ -769,7 +769,7 @@ static std::string get_playbutton_text() { const dusk::gamemode::Gamemode* currentGameMode = dusk::gamemode::getGamemodeManager().getCurrentGamemode(); if (currentGameMode != nullptr) { - return currentGameMode->getId() == "vanilla" ? "Play" : + return currentGameMode->getId() == dusk::gamemode::kVanillaGamemodeId ? "Play" : "Play " + currentGameMode->getFullName(); } return "Play"; @@ -880,14 +880,14 @@ void Prelaunch::build_menu_buttons() { gamemodeActions.push_back(dusk::ui::ModalAction{ .label = "Vanilla", .onPressed = [this](dusk::ui::Modal& modal) { mDoAud_seStartMenu(kSoundClick); - dusk::gamemode::getGamemodeManager().setCurrentGamemode("vanilla"); + dusk::gamemode::getGamemodeManager().setCurrentGamemode(dusk::gamemode::kVanillaGamemodeId); modal.pop(); update(); }}); for (const auto& [id, gamemode] : dusk::gamemode::getGamemodeManager().getRegisteredGamemodes()) { - if (id == "vanilla") { + if (id == dusk::gamemode::kVanillaGamemodeId) { // Force vanilla to the top continue; }