Gamemode service improvements. Disable achievements on custom gamemodes (for now)

This commit is contained in:
jdflyer
2026-08-18 00:33:15 -07:00
parent 85c2ddd8ff
commit c0d44d4a21
13 changed files with 99 additions and 76 deletions
+34 -42
View File
@@ -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<UiWindowHandle*>(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<UiWindowHandle*>(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<dFile_select_c *>(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 = {
+2 -1
View File
@@ -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
+11 -9
View File
@@ -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 {
+15 -4
View File
@@ -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;
}
}
+8
View File
@@ -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 <filesystem>
#include <algorithm>
@@ -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();
}
+4 -5
View File
@@ -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();
+8 -5
View File
@@ -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<void()> mOnPlayFunction;
std::function<void()> mOnSaveLoadedFunction;
std::function<void()> mOnNewSaveFunction;
std::function<bool(dFile_select_c* fileSelect)> mOnNewSaveSelectFunction;
std::function<void(bool* out_proceedToNameSelect, bool* out_returnToFileSelect)> mOnNewSaveSelectFunction;
std::function<void()> mOnGameResetFunction;
std::function<void()> 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();
+2 -1
View File
@@ -1,5 +1,6 @@
#include "dusk/settings.h"
#include "dusk/config.hpp"
#include "dusk/gamemode.hpp"
#include <aurora/aurora.h>
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 = {
+2 -2
View File
@@ -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() {
+4 -2
View File
@@ -2,11 +2,13 @@
#include <aurora/aurora.h>
#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
+5 -1
View File
@@ -63,7 +63,11 @@ MenuBar::MenuBar()
mTabBar->add_tab("Editor", [this] { push(std::make_unique<EditorWindow>()); });
}
mTabBar->add_tab("Achievements", [this] { push(std::make_unique<AchievementsWindow>()); });
// 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<AchievementsWindow>()); });
}
mTabBar->add_tab("Mods", [this] { push(std::make_unique<ModsWindow>()); });
for (auto& tab : mods::svc::ui_mod_menu_tabs()) {
mTabBar->add_tab(tab.label, std::move(tab.onSelected));
+1 -1
View File
@@ -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;
}
+3 -3
View File
@@ -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;
}