mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-08-20 05:16:24 -04:00
Rework UI lifecycle, finish renaming & cleanup
This commit is contained in:
+23
-31
@@ -638,14 +638,9 @@ state for the current frame. Register and unregister using `register_camera_oper
|
||||
|
||||
### GameModeService (`mods/svc/gamemode.h`)
|
||||
|
||||
Allows a mod to register a gamemode that allows the game to designate one form of gameplay (named a gamemode). This
|
||||
is intended to allow large mods that change large amounts of game logic (such as a randomizer) to have explicit control
|
||||
over how the game will function at certain points. When a gamemode is registered via the service, it will add an entry
|
||||
to the pre-launch menu. When selected, the game will use a unique set of savefiles (designated by the `saveName` field)
|
||||
to store save data while the gamemode is active. Any function pointers registered with the gamemode will be called by
|
||||
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.
|
||||
Allows a mod to register a game mode with callbacks for key gameplay and save lifecycle events. Registered game modes
|
||||
appear in the prelaunch menu. Game modes may use a unique set of saves by configuring `saveName`; leave it empty to use
|
||||
the vanilla `gczelda2` save.
|
||||
|
||||
```cpp
|
||||
// An example that shows registering a gamemode with function hooks that are scoped to the gamemode being active
|
||||
@@ -653,18 +648,18 @@ IMPORT_SERVICE(LogService, svc_log);
|
||||
IMPORT_SERVICE(HookService, svc_hook);
|
||||
IMPORT_SERVICE(GameModeService, svc_gamemode);
|
||||
|
||||
#define MY_GAMEMODE_ID "gamemodeid"
|
||||
DEFINE_HOOK(fopAcM_createItem, CreateItem);
|
||||
|
||||
#define MY_GAME_MODE_ID "game-mode-id"
|
||||
|
||||
static HookAction myFunctionHook(ModContext *ctx, void *args, void *, void *) {
|
||||
// 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
|
||||
// gamemode's onActivatedFunction, and uninstall the hook during the onDeactivatedFunction. Example below.
|
||||
// Alternatively, check with `svc_gamemode->is_active(mod_ctx, MY_GAME_MODE_ID, &active) == MOD_OK && active`.
|
||||
|
||||
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
|
||||
@@ -676,7 +671,7 @@ void onGameModeActivated() {
|
||||
|
||||
void onGameModeDeactivated() {
|
||||
// Uninstall any hooks that are gamemode specific
|
||||
// Remove overlays to any files that are gamemode specific
|
||||
// Remove any file overlays that are gamemode specific
|
||||
ModResult result = mods::hook_uninstall<CreateItem>();
|
||||
if (result != MOD_OK) {
|
||||
svc_log->error(mod_ctx, "failed to uninstall CreateItem hook");
|
||||
@@ -687,27 +682,24 @@ 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. Note: it is limited to 31 characters long
|
||||
.saveName = "my-unique-save_developer-name",
|
||||
.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)
|
||||
// Register the game mode when the mod is initialized.
|
||||
const GameModeDesc gameModeDesc = {
|
||||
.gameModeId = MY_GAME_MODE_ID,
|
||||
.fullName = "My Game Mode",
|
||||
.saveName = "my-unique-save", // Custom save names should be unique, max 31 chars
|
||||
.onActivatedFunction = onGameModeActivated, // Called when the game mode is selected or launched
|
||||
.onDeactivatedFunction = onGameModeDeactivated, // Called when it is deselected 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
|
||||
.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
|
||||
.onTickFunction = nullptr, // Called every game tick while the game mode is active
|
||||
};
|
||||
svc_gamemode->register_gamemode(mod_ctx, &gamemodeDesc);
|
||||
svc_gamemode->register_game_mode(mod_ctx, &gameModeDesc);
|
||||
```
|
||||
|
||||
Within the gamemode service, a gamemode can also request to load a UI for per-save file settings when the button to
|
||||
create a new file is pressed.
|
||||
A game mode can also open UI for per-save settings when creating a new file.
|
||||
|
||||
```cpp
|
||||
IMPORT_SERVICE(GameModeService, svc_gamemode);
|
||||
@@ -753,13 +745,13 @@ void onNewSaveSelect(bool *out_proceedToNameSelect, bool *out_returnToFileSelect
|
||||
svc_ui->window_push(mod_ctx, &desc, &windowHandle);
|
||||
}
|
||||
|
||||
const GameModeDesc gamemodeDesc = {
|
||||
.gamemodeId = "my-gamemode-id",
|
||||
.fullName = "My GameMode",
|
||||
.saveName = "my-gamemode-save",
|
||||
const GameModeDesc gameModeDesc = {
|
||||
.gameModeId = "my-game-mode-id",
|
||||
.fullName = "My Game Mode",
|
||||
.saveName = "my-unique-save",
|
||||
.onNewSaveSelectFunction = onNewSaveSelect,
|
||||
};
|
||||
svc_gamemode->register_gamemode(mod_ctx, &gamemodeDesc);
|
||||
svc_gamemode->register_game_mode(mod_ctx, &gameModeDesc);
|
||||
|
||||
```
|
||||
|
||||
|
||||
+2
-2
@@ -1434,7 +1434,7 @@ set(DUSK_FILES
|
||||
src/dusk/extras.c
|
||||
src/dusk/frame_interpolation.cpp
|
||||
src/dusk/game_clock.cpp
|
||||
src/dusk/gamemode.cpp
|
||||
src/dusk/game_mode.cpp
|
||||
src/dusk/gamepad_color.cpp
|
||||
src/dusk/globals.cpp
|
||||
src/dusk/gyro.cpp
|
||||
@@ -1502,7 +1502,7 @@ set(DUSK_FILES
|
||||
src/dusk/mods/svc/texture.cpp
|
||||
src/dusk/mods/svc/ui.cpp
|
||||
src/dusk/mods/svc/ui.hpp
|
||||
src/dusk/mods/svc/gamemode.cpp
|
||||
src/dusk/mods/svc/game_mode.cpp
|
||||
src/dusk/mods/svc/window.cpp
|
||||
src/dusk/mods/svc/window.hpp
|
||||
src/dusk/mods/svc/save.cpp
|
||||
|
||||
@@ -8,13 +8,13 @@
|
||||
#define GAMEMODE_SERVICE_MINOR 0u
|
||||
|
||||
typedef struct {
|
||||
const char* gamemodeId;
|
||||
const char* gameModeId;
|
||||
const char* fullName;
|
||||
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
|
||||
const char saveName[32]; // Empty uses default (gczelda2); max 31 chars
|
||||
void (*onActivatedFunction)(); // Called when the game mode is selected
|
||||
void (*onDeactivatedFunction)(); // Called when the game mode is deselected
|
||||
void (*onPlayFunction)(); // Called when play is pressed on the prelaunch menu
|
||||
void (*onSaveLoadedFunction)(); // Called whenever a savefile is loaded
|
||||
void (*onSaveLoadedFunction)(); // Called whenever a save file 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
|
||||
@@ -27,7 +27,7 @@ typedef struct GameModeService {
|
||||
ServiceHeader header;
|
||||
ModResult (*register_game_mode)(ModContext* ctx, const GameModeDesc* desc);
|
||||
ModResult (*unregister_game_mode)(ModContext* ctx, const char* id);
|
||||
ModResult (*is_active)(ModContext* ctx, const char* gamemodeId, bool* out_active);
|
||||
ModResult (*is_active)(ModContext* ctx, const char* gameModeId, bool* out_active);
|
||||
} GameModeService;
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -24,10 +24,10 @@
|
||||
#include <cstring>
|
||||
|
||||
#ifdef TARGET_PC
|
||||
#include "dusk/game_mode.hpp"
|
||||
#include "dusk/imgui/ImGuiConsole.hpp"
|
||||
#include "dusk/settings.h"
|
||||
#include "dusk/speedrun.h"
|
||||
#include "dusk/gamemode.hpp"
|
||||
#include "dusk/version.hpp"
|
||||
#endif
|
||||
|
||||
|
||||
@@ -13,10 +13,10 @@
|
||||
|
||||
#ifdef TARGET_PC
|
||||
#include <dusk/autosave.h>
|
||||
#include "dusk/livesplit.h"
|
||||
#include "dusk/game_mode.hpp"
|
||||
#include "dusk/imgui/ImGuiConsole.hpp"
|
||||
#include "dusk/livesplit.h"
|
||||
#include "dusk/speedrun.h"
|
||||
#include "dusk/gamemode.hpp"
|
||||
#endif
|
||||
|
||||
#include "dusk/version.hpp"
|
||||
|
||||
+17
-13
@@ -26,10 +26,10 @@
|
||||
#include "dusk/version.hpp"
|
||||
|
||||
#if TARGET_PC
|
||||
#include "dusk/game_mode.hpp"
|
||||
#include "dusk/menu_pointer.h"
|
||||
#include "helpers/string.hpp"
|
||||
#include "dusk/mods/svc/save.hpp"
|
||||
#include "dusk/gamemode.hpp"
|
||||
#include "helpers/string.hpp"
|
||||
|
||||
namespace {
|
||||
constexpr u8 pointer_target(u8 group, u8 index) noexcept {
|
||||
@@ -1315,11 +1315,13 @@ void dFile_select_c::selectDataNameMove() {
|
||||
bool isModoruTxtDisp = modoruTxtDispAnm();
|
||||
|
||||
#ifdef TARGET_PC
|
||||
const dusk::gamemode::GameMode* gamemode = dusk::gamemode::getGameModeManager().getCurrentGameMode();
|
||||
if (gamemode) {
|
||||
if (isHeaderTxtChange == true && isFileRecScale == true && isModoruTxtDisp == true) {
|
||||
const dusk::gamemode::GameMode* gameMode =
|
||||
dusk::gamemode::getGameModeManager().getCurrentGameMode();
|
||||
if (gameMode) {
|
||||
if (isHeaderTxtChange == true && isFileRecScale == true && isModoruTxtDisp == true) {
|
||||
if (mGameModeSaveStartBuildUi) {
|
||||
gamemode->invokeOnNewSaveSelectFunction(&mGameModeProceedToNameSelect, &mGameModeReturnToFileSelect);
|
||||
gameMode->invokeOnNewSaveSelectFunction(
|
||||
&mGameModeProceedToNameSelect, &mGameModeReturnToFileSelect);
|
||||
mGameModeSaveStartBuildUi = false;
|
||||
}
|
||||
if (mGameModeReturnToFileSelect) {
|
||||
@@ -1430,9 +1432,10 @@ void dFile_select_c::menuSelectStart() {
|
||||
#if TARGET_PC
|
||||
dusk::mods::svc::save_slot_loaded(mSelectNum, &mSaveData[mSelectNum]);
|
||||
|
||||
const dusk::gamemode::GameMode* gamemode = dusk::gamemode::getGameModeManager().getCurrentGameMode();
|
||||
if (gamemode) {
|
||||
gamemode->invokeOnSaveLoadedFunction();
|
||||
const dusk::gamemode::GameMode* gameMode =
|
||||
dusk::gamemode::getGameModeManager().getCurrentGameMode();
|
||||
if (gameMode) {
|
||||
gameMode->invokeOnSaveLoadedFunction();
|
||||
}
|
||||
#endif
|
||||
} else if (mSelectMenuNum == 0) {
|
||||
@@ -1787,10 +1790,11 @@ void dFile_select_c::nameInput2() {
|
||||
mIsSelectEnd = true;
|
||||
#if TARGET_PC
|
||||
dusk::mods::svc::save_slot_new(mSelectNum);
|
||||
const dusk::gamemode::GameMode* gamemode = dusk::gamemode::getGameModeManager().getCurrentGameMode();
|
||||
if (gamemode) {
|
||||
gamemode->invokeOnNewSaveFunction();
|
||||
gamemode->invokeOnSaveLoadedFunction();
|
||||
const dusk::gamemode::GameMode* gameMode =
|
||||
dusk::gamemode::getGameModeManager().getCurrentGameMode();
|
||||
if (gameMode) {
|
||||
gameMode->invokeOnNewSaveFunction();
|
||||
gameMode->invokeOnSaveLoadedFunction();
|
||||
}
|
||||
#endif
|
||||
mDataSelProc = DATASELPROC_NEXT_MODE_WAIT;
|
||||
|
||||
+5
-4
@@ -24,11 +24,11 @@
|
||||
#include "JSystem/JUtility/JUTConsole.h"
|
||||
|
||||
#ifdef TARGET_PC
|
||||
#include "dusk/game_mode.hpp"
|
||||
#include "dusk/language.hpp"
|
||||
#include "dusk/logging.h"
|
||||
#include "dusk/main.h"
|
||||
#include "dusk/mods/svc/save.hpp"
|
||||
#include "dusk/gamemode.hpp"
|
||||
#include "dusk/version.hpp"
|
||||
#include "m_Do/m_Do_MemCard.h"
|
||||
#endif
|
||||
@@ -809,9 +809,10 @@ void dScnLogo_c::nextSceneChange() {
|
||||
if (status == 1) {
|
||||
dusk::mods::svc::save_slot_loaded(
|
||||
saveSlot, buf + saveSlot * SAVEDATA_SIZE);
|
||||
const dusk::gamemode::GameMode* gamemode = dusk::gamemode::getGameModeManager().getCurrentGameMode();
|
||||
if (gamemode) {
|
||||
gamemode->invokeOnSaveLoadedFunction();
|
||||
const dusk::gamemode::GameMode* gameMode =
|
||||
dusk::gamemode::getGameModeManager().getCurrentGameMode();
|
||||
if (gameMode) {
|
||||
gameMode->invokeOnSaveLoadedFunction();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -18,13 +18,13 @@
|
||||
#include "m_Do/m_Do_mtx.h"
|
||||
|
||||
#ifdef TARGET_PC
|
||||
#include "dusk/autosave.h"
|
||||
#include "dusk/game_mode.hpp"
|
||||
#include "dusk/imgui/ImGuiConsole.hpp"
|
||||
#include "dusk/livesplit.h"
|
||||
#include "dusk/memory.h"
|
||||
#include "dusk/speedrun.h"
|
||||
#include "dusk/settings.h"
|
||||
#include "dusk/autosave.h"
|
||||
#include "dusk/gamemode.hpp"
|
||||
#include "dusk/speedrun.h"
|
||||
#endif
|
||||
|
||||
#if TARGET_PC
|
||||
|
||||
+2
-2
@@ -28,9 +28,9 @@
|
||||
#endif
|
||||
|
||||
#if TARGET_PC
|
||||
#include "dusk/settings.h"
|
||||
#include "dusk/gamemode.hpp"
|
||||
#include <f_ap/f_ap_game.h>
|
||||
#include "dusk/game_mode.hpp"
|
||||
#include "dusk/settings.h"
|
||||
|
||||
#include "helpers/string.hpp"
|
||||
#define strcpy SafeStringCopy
|
||||
|
||||
+13
-14
@@ -1,27 +1,26 @@
|
||||
#include "dusk/achievements.h"
|
||||
#include "dusk/io.hpp"
|
||||
#include "dusk/main.h"
|
||||
#include "d/d_com_inf_game.h"
|
||||
#include "d/d_item_data.h"
|
||||
#include "d/d_map_path_fmap.h"
|
||||
#include "d/d_stage.h"
|
||||
#include "d/d_menu_fmap.h"
|
||||
#include "JSystem/JKernel/JKRArchive.h"
|
||||
#include "d/d_meter2_info.h"
|
||||
#include "d/actor/d_a_alink.h"
|
||||
#include "d/actor/d_a_ni.h"
|
||||
#include "d/actor/d_a_npc4.h"
|
||||
#include "d/actor/d_a_b_gnd.h"
|
||||
#include "d/actor/d_a_b_ob.h"
|
||||
#include "d/actor/d_a_ni.h"
|
||||
#include "d/actor/d_a_npc4.h"
|
||||
#include "d/actor/d_a_player.h"
|
||||
#include "d/d_com_inf_game.h"
|
||||
#include "d/d_demo.h"
|
||||
#include "d/d_item_data.h"
|
||||
#include "d/d_map_path_fmap.h"
|
||||
#include "d/d_menu_fmap.h"
|
||||
#include "d/d_meter2_info.h"
|
||||
#include "d/d_stage.h"
|
||||
#include "dusk/game_mode.hpp"
|
||||
#include "dusk/io.hpp"
|
||||
#include "dusk/logging.h"
|
||||
#include "dusk/main.h"
|
||||
#include "dusk/speedrun.h"
|
||||
#include "dusk/ui/ui.hpp"
|
||||
#include "f_pc/f_pc_name.h"
|
||||
#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>
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
#include "dusk/game_mode.hpp"
|
||||
#include "JSystem/JUtility/JUTGamePad.h"
|
||||
#include "aurora/lib/logging.hpp"
|
||||
#include "dusk/config.hpp"
|
||||
#include "dusk/ui/prelaunch.hpp"
|
||||
#include "m_Do/m_Do_MemCard.h"
|
||||
|
||||
namespace dusk::gamemode {
|
||||
namespace {
|
||||
aurora::Module Log("dusk::gamemode");
|
||||
}
|
||||
|
||||
GameModeManager g_GameModeManager;
|
||||
|
||||
GameModeManager::GameModeManager() {
|
||||
GameMode vanilla{kVanillaGameModeId, "Vanilla"};
|
||||
mRegisteredGameModes.emplace(vanilla.getId(), std::move(vanilla));
|
||||
mCurrentGameModeId = kVanillaGameModeId;
|
||||
}
|
||||
|
||||
void GameModeManager::setGameModeToPrevious() {
|
||||
// Restore the previously selected game mode if still registered.
|
||||
GameModeId id = getSettings().game.lastSelectedGameModeId;
|
||||
if (!mRegisteredGameModes.contains(id)) {
|
||||
setCurrentGameMode(kVanillaGameModeId);
|
||||
return;
|
||||
}
|
||||
setCurrentGameMode(id);
|
||||
}
|
||||
|
||||
void GameModeManager::registerGameMode(const GameMode& gameMode) {
|
||||
if (gameMode.getId().empty()) {
|
||||
Log.fatal("No game mode ID specified in GameModeManager::registerGameMode");
|
||||
}
|
||||
if (gameMode.getFullName().empty()) {
|
||||
Log.fatal("No display name specified for game mode {}", gameMode.getId());
|
||||
}
|
||||
|
||||
if (mRegisteredGameModes.contains(gameMode.getId())) {
|
||||
Log.warn("Attempting to re-register existing game mode {}", gameMode.getId());
|
||||
return;
|
||||
}
|
||||
|
||||
mRegisteredGameModes.emplace(gameMode.getId(), gameMode);
|
||||
ui::Prelaunch::refresh_menu_buttons();
|
||||
}
|
||||
|
||||
void GameModeManager::unregisterGameMode(const GameModeId& gameModeId) {
|
||||
const auto& it = mRegisteredGameModes.find(gameModeId);
|
||||
if (it == mRegisteredGameModes.end()) {
|
||||
Log.warn("Attempting to unregister unknown game mode {}", gameModeId);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mCurrentGameModeId == gameModeId) {
|
||||
// Reset to prelaunch before unloading callbacks belonging to the active mod.
|
||||
ui::prelaunch_state().returnToPrelaunchOnReset = true;
|
||||
JUTGamePad::C3ButtonReset::sResetSwitchPushing = true;
|
||||
setCurrentGameMode(kVanillaGameModeId);
|
||||
}
|
||||
mRegisteredGameModes.erase(it);
|
||||
ui::Prelaunch::refresh_menu_buttons();
|
||||
}
|
||||
|
||||
void GameModeManager::setCurrentGameMode(const GameModeId& id) {
|
||||
if (mCurrentGameModeId == id) {
|
||||
return;
|
||||
}
|
||||
if (!mRegisteredGameModes.contains(id)) {
|
||||
Log.warn("Attempting to configure unknown game mode {}", id);
|
||||
return;
|
||||
}
|
||||
const GameMode* currentGameMode = getCurrentGameMode();
|
||||
if (currentGameMode) {
|
||||
currentGameMode->invokeOnDeactivatedFunction();
|
||||
}
|
||||
mCurrentGameModeId = id;
|
||||
getSettings().game.lastSelectedGameModeId.setValue(id);
|
||||
config::save();
|
||||
|
||||
currentGameMode = getCurrentGameMode();
|
||||
if (currentGameMode) {
|
||||
mDoMemCd_SetFileName(currentGameMode->getSaveName());
|
||||
currentGameMode->invokeOnActivatedFunction();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace dusk::gamemode
|
||||
@@ -1,21 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "d/d_file_select.h"
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include "d/d_file_select.h"
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace dusk::gamemode {
|
||||
using GameModeId = std::string;
|
||||
|
||||
constexpr const char* kVanillaGameModeId = "vanilla";
|
||||
constexpr const char* kDefaultGameModeSaveName = "gczelda2";
|
||||
|
||||
// This class holds the definition for the gamemode and various function pointers to call
|
||||
// Holds a game mode definition and its lifecycle callbacks.
|
||||
class GameMode {
|
||||
public:
|
||||
GameMode(const GameModeId& id, const std::string& fullName, const std::string& saveName) {
|
||||
mId = id;
|
||||
mFullName = fullName;
|
||||
mSaveName = saveName;
|
||||
}
|
||||
GameMode(GameModeId id, std::string fullName, std::string saveName = {})
|
||||
: mId{std::move(id)}, mFullName{std::move(fullName)},
|
||||
mSaveName{saveName.empty() ? kDefaultGameModeSaveName : std::move(saveName)} {}
|
||||
const GameModeId& getId() const { return mId; }
|
||||
const std::string& getFullName() const { return mFullName; }
|
||||
const std::string& getSaveName() const { return mSaveName; }
|
||||
@@ -53,11 +56,12 @@ public:
|
||||
mOnNewSaveFunction();
|
||||
}
|
||||
}
|
||||
|
||||
void invokeOnNewSaveSelectFunction(bool* out_proceedToNameSelect, bool* out_returnToFileSelect) const {
|
||||
|
||||
void invokeOnNewSaveSelectFunction(
|
||||
bool* out_proceedToNameSelect, bool* out_returnToFileSelect) const {
|
||||
if (mOnNewSaveSelectFunction) {
|
||||
mOnNewSaveSelectFunction(out_proceedToNameSelect, out_returnToFileSelect);
|
||||
}else {
|
||||
} else {
|
||||
*out_proceedToNameSelect = true;
|
||||
}
|
||||
}
|
||||
@@ -79,7 +83,8 @@ public:
|
||||
std::function<void()> mOnPlayFunction;
|
||||
std::function<void()> mOnSaveLoadedFunction;
|
||||
std::function<void()> mOnNewSaveFunction;
|
||||
std::function<void(bool* out_proceedToNameSelect, bool* out_returnToFileSelect)> mOnNewSaveSelectFunction;
|
||||
std::function<void(bool* out_proceedToNameSelect, bool* out_returnToFileSelect)>
|
||||
mOnNewSaveSelectFunction;
|
||||
std::function<void()> mOnGameResetFunction;
|
||||
std::function<void()> mOnTickFunction;
|
||||
};
|
||||
@@ -87,16 +92,17 @@ public:
|
||||
class GameModeManager {
|
||||
public:
|
||||
GameModeManager();
|
||||
void registerGameMode(const GameMode& gamemode);
|
||||
void unregisterGameMode(const GameModeId& gamemodeId);
|
||||
void registerGameMode(const GameMode& gameMode);
|
||||
void unregisterGameMode(const GameModeId& gameModeId);
|
||||
|
||||
const GameMode* getCurrentGameMode() const {
|
||||
const auto& it = mRegisteredGameModes.find(mCurrentGameModeId);
|
||||
return it != mRegisteredGameModes.end() ? &it->second : &mRegisteredGameModes.at(kVanillaGameModeId);
|
||||
return it != mRegisteredGameModes.end() ? &it->second :
|
||||
&mRegisteredGameModes.at(kVanillaGameModeId);
|
||||
}
|
||||
bool isCurrentGameMode(const GameModeId& id) const {
|
||||
const GameMode* gamemode = getCurrentGameMode();
|
||||
if (gamemode && gamemode->getId() == id) {
|
||||
const GameMode* gameMode = getCurrentGameMode();
|
||||
if (gameMode && gameMode->getId() == id) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -104,7 +110,9 @@ public:
|
||||
void setCurrentGameMode(const GameModeId& id);
|
||||
void setGameModeToPrevious();
|
||||
|
||||
std::map<GameModeId, GameMode>& getRegisteredGameModes() { return mRegisteredGameModes; }
|
||||
const std::map<GameModeId, GameMode>& getRegisteredGameModes() const {
|
||||
return mRegisteredGameModes;
|
||||
}
|
||||
|
||||
private:
|
||||
GameModeId mCurrentGameModeId;
|
||||
@@ -117,4 +125,4 @@ inline GameModeManager& getGameModeManager() {
|
||||
return g_GameModeManager;
|
||||
}
|
||||
|
||||
}; // namespace dusk::gamemode
|
||||
} // namespace dusk::gamemode
|
||||
@@ -1,92 +0,0 @@
|
||||
#include "dusk/gamemode.hpp"
|
||||
#include "dusk/config.hpp"
|
||||
#include "JSystem/JUtility/JUTGamePad.h"
|
||||
#include "aurora/lib/logging.hpp"
|
||||
#include "m_Do/m_Do_MemCard.h"
|
||||
#include "dusk/ui/prelaunch.hpp"
|
||||
|
||||
namespace dusk::gamemode {
|
||||
|
||||
GameModeManager g_GameModeManager;
|
||||
|
||||
aurora::Module DuskGameModeLog("dusk::gamemode");
|
||||
|
||||
GameModeManager::GameModeManager() {
|
||||
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(kVanillaGameModeId);
|
||||
return;
|
||||
}
|
||||
setCurrentGameMode(id);
|
||||
}
|
||||
|
||||
void GameModeManager::registerGameMode(const GameMode& gamemode) {
|
||||
if (gamemode.getId().empty()) {
|
||||
DuskGameModeLog.fatal("No gamemode id specified in GameModeManager::registerGameMode!");
|
||||
}
|
||||
if (gamemode.getSaveName().empty()) {
|
||||
DuskGameModeLog.fatal("No save name provided for gamemode {}", gamemode.getId());
|
||||
}
|
||||
if (gamemode.getFullName().empty()) {
|
||||
DuskGameModeLog.fatal("No Name Specified for gamemode {}", gamemode.getId());
|
||||
}
|
||||
|
||||
if (mRegisteredGameModes.find(gamemode.getId()) != mRegisteredGameModes.end()) {
|
||||
DuskGameModeLog.warn("Attempting to register gamemode {} when it is already registered!", gamemode.getId());
|
||||
return;
|
||||
}
|
||||
|
||||
mRegisteredGameModes.emplace(gamemode.getId(),gamemode);
|
||||
dusk::ui::Prelaunch::rebuild_menu_buttons();
|
||||
}
|
||||
|
||||
void GameModeManager::unregisterGameMode(const GameModeId& gamemodeId) {
|
||||
const auto& it = mRegisteredGameModes.find(gamemodeId);
|
||||
if (it == mRegisteredGameModes.end()) {
|
||||
DuskGameModeLog.warn(
|
||||
"Attempting to unregister gamemode of id {} that isn't registered!", gamemodeId);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mCurrentGameModeId == gamemodeId) {
|
||||
// We need to be careful if we are unregistering a running gamemode, the easiest way is just
|
||||
// to reset the game back to title as vanilla;
|
||||
ui::prelaunch_state().showPrelaunchOnReset = true;
|
||||
JUTGamePad::C3ButtonReset::sResetSwitchPushing = true;
|
||||
setCurrentGameMode(kVanillaGameModeId);
|
||||
}
|
||||
mRegisteredGameModes.erase(it);
|
||||
dusk::ui::Prelaunch::rebuild_menu_buttons();
|
||||
}
|
||||
|
||||
void GameModeManager::setCurrentGameMode(const GameModeId& id) {
|
||||
if (mCurrentGameModeId == id) {
|
||||
return;
|
||||
}
|
||||
const GameMode* currentGameMode = getCurrentGameMode();
|
||||
if (currentGameMode) {
|
||||
currentGameMode->invokeOnDeactivatedFunction();
|
||||
}
|
||||
if (mRegisteredGameModes.find(id) == mRegisteredGameModes.end()) {
|
||||
DuskGameModeLog.warn("Attempting to set current game mode to {} when it hasn't been registered!", id);
|
||||
}
|
||||
|
||||
mCurrentGameModeId = id;
|
||||
dusk::getSettings().game.lastSelectedGameModeId.setValue(id);
|
||||
dusk::config::save();
|
||||
|
||||
currentGameMode = getCurrentGameMode();
|
||||
if (currentGameMode) {
|
||||
// Set the loaded save file to our gamemode's save name
|
||||
mDoMemCd_SetFileName(currentGameMode->mSaveName);
|
||||
currentGameMode->invokeOnActivatedFunction();
|
||||
}
|
||||
}
|
||||
|
||||
}; // namespace dusk::gamemode
|
||||
@@ -9,7 +9,6 @@
|
||||
#include "imgui.h"
|
||||
#include <imgui_internal.h>
|
||||
|
||||
#include "fmt/format.h"
|
||||
#include "ImGuiConsole.hpp"
|
||||
#include "ImGuiEngine.hpp"
|
||||
#include "JSystem/JUtility/JUTGamePad.h"
|
||||
@@ -19,14 +18,15 @@
|
||||
#include "dusk/data.hpp"
|
||||
#include "dusk/dusk.h"
|
||||
#include "dusk/frame_interpolation.h"
|
||||
#include "dusk/game_mode.hpp"
|
||||
#include "dusk/livesplit.h"
|
||||
#include "dusk/main.h"
|
||||
#include "dusk/presentation.hpp"
|
||||
#include "dusk/settings.h"
|
||||
#include "dusk/ui/ui.hpp"
|
||||
#include "dusk/gamemode.hpp"
|
||||
#include "f_pc/f_pc_manager.h"
|
||||
#include "f_pc/f_pc_name.h"
|
||||
#include "fmt/format.h"
|
||||
#include "m_Do/m_Do_controller_pad.h"
|
||||
#include "m_Do/m_Do_main.h"
|
||||
#include "tracy/Tracy.hpp"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include "dusk/gamemode.hpp"
|
||||
#include "dusk/game_mode.hpp"
|
||||
#include "dusk/speedrun.h"
|
||||
|
||||
namespace dusk::speedrun {
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
#include "mods/svc/game_mode.h"
|
||||
#include "dusk/game_mode.hpp"
|
||||
|
||||
#include "config.hpp"
|
||||
#include "registry.hpp"
|
||||
#include "slot_map.hpp"
|
||||
|
||||
#include "aurora/lib/logging.hpp"
|
||||
#include "dusk/mod_loader.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace dusk::mods::svc::game_mode_impl {
|
||||
namespace {
|
||||
|
||||
aurora::Module Log("dusk::mods::game_mode");
|
||||
|
||||
// Track which gamemodes are registered by which mods, allowing us to automatically unregister them
|
||||
std::unordered_map<std::string, std::vector<std::string>> s_gameModesByMod;
|
||||
|
||||
std::string get_mod_game_mode_id(ModContext* ctx, const std::string& id) {
|
||||
// Include the mod ID to prevent clashes and normalize to lowercase
|
||||
std::string fullId = id + "_" + ctx->mod->metadata.id;
|
||||
std::transform(fullId.begin(), fullId.end(), fullId.begin(),
|
||||
[](unsigned char c) { return std::tolower(c); });
|
||||
return fullId;
|
||||
}
|
||||
|
||||
void game_mode_remove_mod(LoadedMod& mod) {
|
||||
const auto it = s_gameModesByMod.find(mod.metadata.id);
|
||||
if (it != s_gameModesByMod.end()) {
|
||||
for (const auto& id : it->second) {
|
||||
gamemode::getGameModeManager().unregisterGameMode(id);
|
||||
}
|
||||
s_gameModesByMod.erase(it);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
ModResult register_game_mode(ModContext* ctx, const GameModeDesc* desc) {
|
||||
std::string id;
|
||||
if (!desc->gameModeId) {
|
||||
Log.error("Attempted to register a game mode with a null ID");
|
||||
return MOD_ERROR;
|
||||
}
|
||||
id = desc->gameModeId;
|
||||
if (id.empty()) {
|
||||
Log.error("Attempted to register a game mode with an empty ID");
|
||||
return MOD_ERROR;
|
||||
}
|
||||
id = get_mod_game_mode_id(ctx, id);
|
||||
|
||||
std::string fullName;
|
||||
if (!desc->fullName) {
|
||||
Log.warn("Game mode {} has no display name; using its ID", id);
|
||||
fullName = id;
|
||||
} else {
|
||||
fullName = desc->fullName;
|
||||
if (fullName.empty()) {
|
||||
Log.warn("Game mode {} has an empty display name; using its ID", id);
|
||||
fullName = id;
|
||||
}
|
||||
}
|
||||
|
||||
gamemode::GameMode mode{id, fullName, desc->saveName};
|
||||
if (desc->onActivatedFunction) {
|
||||
mode.mOnActivatedFunction = desc->onActivatedFunction;
|
||||
}
|
||||
if (desc->onDeactivatedFunction) {
|
||||
mode.mOnDeactivatedFunction = desc->onDeactivatedFunction;
|
||||
}
|
||||
if (desc->onPlayFunction) {
|
||||
mode.mOnPlayFunction = desc->onPlayFunction;
|
||||
}
|
||||
if (desc->onSaveLoadedFunction) {
|
||||
mode.mOnSaveLoadedFunction = desc->onSaveLoadedFunction;
|
||||
}
|
||||
if (desc->onNewSaveFunction) {
|
||||
mode.mOnNewSaveFunction = desc->onNewSaveFunction;
|
||||
}
|
||||
if (desc->onNewSaveSelectFunction) {
|
||||
mode.mOnNewSaveSelectFunction = desc->onNewSaveSelectFunction;
|
||||
}
|
||||
if (desc->onGameResetFunction) {
|
||||
mode.mOnGameResetFunction = desc->onGameResetFunction;
|
||||
}
|
||||
if (desc->onTickFunction) {
|
||||
mode.mOnTickFunction = desc->onTickFunction;
|
||||
}
|
||||
|
||||
gamemode::getGameModeManager().registerGameMode(mode);
|
||||
s_gameModesByMod[ctx->mod->metadata.id].push_back(id);
|
||||
return MOD_OK;
|
||||
}
|
||||
|
||||
ModResult unregister_game_mode(ModContext* ctx, const char* id) {
|
||||
std::string fullId = get_mod_game_mode_id(ctx, id);
|
||||
gamemode::getGameModeManager().unregisterGameMode(fullId);
|
||||
|
||||
// Remove the game mode from the service registered game modes map
|
||||
auto it = s_gameModesByMod.find(ctx->mod->metadata.id);
|
||||
if (it != s_gameModesByMod.end()) {
|
||||
std::erase(it->second, fullId);
|
||||
}
|
||||
return MOD_OK;
|
||||
}
|
||||
|
||||
ModResult is_active(ModContext* ctx, const char* gameModeId, bool* out_active) {
|
||||
*out_active =
|
||||
gamemode::getGameModeManager().isCurrentGameMode(get_mod_game_mode_id(ctx, gameModeId));
|
||||
return MOD_OK;
|
||||
}
|
||||
|
||||
} // namespace dusk::mods::svc::game_mode_impl
|
||||
|
||||
namespace dusk::mods::svc {
|
||||
namespace {
|
||||
|
||||
constexpr GameModeService s_gamemodeService{
|
||||
.header = SERVICE_HEADER(GameModeService, GAMEMODE_SERVICE_MAJOR, GAMEMODE_SERVICE_MINOR),
|
||||
.register_game_mode = game_mode_impl::register_game_mode,
|
||||
.unregister_game_mode = game_mode_impl::unregister_game_mode,
|
||||
.is_active = game_mode_impl::is_active,
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
constinit const ServiceModule g_gamemodeModule{
|
||||
.id = GAMEMODE_SERVICE_ID,
|
||||
.majorVersion = GAMEMODE_SERVICE_MAJOR,
|
||||
.minorVersion = GAMEMODE_SERVICE_MINOR,
|
||||
.service = &s_gamemodeService,
|
||||
.modDeactivating = game_mode_impl::game_mode_remove_mod,
|
||||
};
|
||||
|
||||
} // namespace dusk::mods::svc
|
||||
@@ -1,153 +0,0 @@
|
||||
#include "mods/svc/gamemode.h"
|
||||
#include "dusk/gamemode.hpp"
|
||||
|
||||
#include "config.hpp"
|
||||
#include "registry.hpp"
|
||||
#include "slot_map.hpp"
|
||||
|
||||
#include "aurora/lib/logging.hpp"
|
||||
#include "dusk/mod_loader.hpp"
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
|
||||
namespace dusk::mods::svc::gamemode_impl {
|
||||
namespace {
|
||||
|
||||
aurora::Module Log("dusk::mods::gamemode");
|
||||
|
||||
// These track which gamemodes are registered by which mods, allowing us to automatically unregister them
|
||||
std::unordered_map<std::string, std::vector<std::string>> s_gamemodesRegisteredToMods;
|
||||
|
||||
std::string get_mod_gamemode_id(ModContext* ctx, const std::string& id) {
|
||||
// Standardize the IDs to include the mod id (to prevent clashes) and set them as lowercase
|
||||
std::string fullId = id + "_" + ctx->mod->metadata.id;
|
||||
std::transform(fullId.begin(),fullId.end(),fullId.begin(),[](unsigned char c) {
|
||||
return std::tolower(c);
|
||||
});
|
||||
return fullId;
|
||||
}
|
||||
|
||||
void gamemode_remove_mod(LoadedMod& mod) {
|
||||
const auto it = s_gamemodesRegisteredToMods.find(mod.metadata.id);
|
||||
if (it != s_gamemodesRegisteredToMods.end()) {
|
||||
for (const auto& id : it->second) {
|
||||
dusk::gamemode::getGameModeManager().unregisterGameMode(id);
|
||||
}
|
||||
s_gamemodesRegisteredToMods.erase(it);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
|
||||
ModResult register_gamemode(ModContext* ctx, const GameModeDesc* desc) {
|
||||
std::string id;
|
||||
if (!desc->gamemodeId) {
|
||||
Log.error("Attempted to register a gamemode with a null id!");
|
||||
return MOD_ERROR;
|
||||
}
|
||||
id = desc->gamemodeId;
|
||||
if (id.empty()) {
|
||||
Log.error("Attempted to register a gamemode with an empty id!");
|
||||
return MOD_ERROR;
|
||||
}
|
||||
id = get_mod_gamemode_id(ctx, id); // Append the mod id to the end of the gamemode id to ensure they are unique
|
||||
|
||||
std::string fullName;
|
||||
if (!desc->fullName) {
|
||||
Log.warn("Attempted to register gamemode {} with a null full name! Defaulting to: ({})",id,id);
|
||||
fullName = id;
|
||||
}else{
|
||||
fullName = desc->fullName;
|
||||
if (fullName.empty()) {
|
||||
Log.warn("Attempted to register gamemode {} with an empty full name! Defaulting to: ({})",id,id);
|
||||
fullName = id;
|
||||
}
|
||||
}
|
||||
|
||||
std::string saveName = desc->saveName;
|
||||
if (saveName.empty()) {
|
||||
Log.warn("Attempted to register gamemode {} with an empty save name! Defaulting to: (gczelda2)",id);
|
||||
saveName = "gczelda2";
|
||||
}
|
||||
|
||||
dusk::gamemode::GameMode gamemode(id, fullName, saveName);
|
||||
|
||||
if (desc->onActivatedFunction) {
|
||||
gamemode.mOnActivatedFunction = desc->onActivatedFunction;
|
||||
}
|
||||
if (desc->onDeactivatedFunction) {
|
||||
gamemode.mOnDeactivatedFunction = desc->onDeactivatedFunction;
|
||||
}
|
||||
if (desc->onPlayFunction) {
|
||||
gamemode.mOnPlayFunction = desc->onPlayFunction;
|
||||
}
|
||||
if (desc->onSaveLoadedFunction) {
|
||||
gamemode.mOnSaveLoadedFunction = desc->onSaveLoadedFunction;
|
||||
}
|
||||
if (desc->onNewSaveFunction) {
|
||||
gamemode.mOnNewSaveFunction = desc->onNewSaveFunction;
|
||||
}
|
||||
if (desc->onNewSaveSelectFunction) {
|
||||
gamemode.mOnNewSaveSelectFunction = desc->onNewSaveSelectFunction;
|
||||
}
|
||||
if (desc->onGameResetFunction) {
|
||||
gamemode.mOnGameResetFunction = desc->onGameResetFunction;
|
||||
}
|
||||
if (desc->onTickFunction) {
|
||||
gamemode.mOnTickFunction = desc->onTickFunction;
|
||||
}
|
||||
|
||||
dusk::gamemode::getGameModeManager().registerGameMode(gamemode);
|
||||
|
||||
const auto it = s_gamemodesRegisteredToMods.find(ctx->mod->metadata.id);
|
||||
if (it == s_gamemodesRegisteredToMods.end()) {
|
||||
std::vector<std::string> registeredGameModes = {id};
|
||||
s_gamemodesRegisteredToMods.emplace(ctx->mod->metadata.id, registeredGameModes);
|
||||
}else {
|
||||
it->second.push_back(id);
|
||||
}
|
||||
|
||||
return MOD_OK;
|
||||
}
|
||||
|
||||
ModResult unregister_gamemode(ModContext* ctx, const char* id) {
|
||||
std::string fullId = get_mod_gamemode_id(ctx,id);
|
||||
dusk::gamemode::getGameModeManager().unregisterGameMode(fullId);
|
||||
|
||||
// Remove the gamemode from the service registered gamemodes map
|
||||
auto it = s_gamemodesRegisteredToMods.find(ctx->mod->metadata.id);
|
||||
if (it != s_gamemodesRegisteredToMods.end()) {
|
||||
std::erase(it->second, fullId);
|
||||
}
|
||||
return MOD_OK;
|
||||
}
|
||||
|
||||
ModResult is_active(ModContext* ctx, const char* gamemodeId, bool* out_active) {
|
||||
*out_active = dusk::gamemode::getGameModeManager().isCurrentGameMode(get_mod_gamemode_id(ctx,gamemodeId));
|
||||
return MOD_OK;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace dusk::mods::svc {
|
||||
namespace {
|
||||
|
||||
constexpr GameModeService s_gamemodeService{
|
||||
.header = SERVICE_HEADER(GameModeService, GAMEMODE_SERVICE_MAJOR, GAMEMODE_SERVICE_MINOR),
|
||||
.register_game_mode = gamemode_impl::register_gamemode,
|
||||
.unregister_game_mode = gamemode_impl::unregister_gamemode,
|
||||
.is_active = gamemode_impl::is_active
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
constinit const ServiceModule g_gamemodeModule{
|
||||
.id = GAMEMODE_SERVICE_ID,
|
||||
.majorVersion = GAMEMODE_SERVICE_MAJOR,
|
||||
.minorVersion = GAMEMODE_SERVICE_MINOR,
|
||||
.service = &s_gamemodeService,
|
||||
.modDeactivating = gamemode_impl::gamemode_remove_mod,
|
||||
};
|
||||
|
||||
} // namespace dusk::mods::svc
|
||||
@@ -424,7 +424,6 @@ public:
|
||||
}
|
||||
|
||||
void close() { pop(); }
|
||||
void force_close() { Document::hide(true); }
|
||||
|
||||
private:
|
||||
std::function<void()> m_onDestroyed;
|
||||
@@ -929,7 +928,7 @@ void ui_sync_menu_tabs() {
|
||||
}
|
||||
s_menuTabsDirty = false;
|
||||
if (aurora::rmlui::is_initialized()) {
|
||||
ui::MenuBar::rebuild();
|
||||
ui::MenuBar::refresh_tabs();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1017,14 +1016,14 @@ void ui_remove_mod(LoadedMod& mod) {
|
||||
case UiSlotKind::Window: {
|
||||
auto* window = static_cast<ui::ModWindow*>(slot.document);
|
||||
if (window != nullptr) {
|
||||
window->force_close();
|
||||
window->force_hide(true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case UiSlotKind::Dialog: {
|
||||
auto* dialog = static_cast<ModDialog*>(slot.document);
|
||||
if (dialog != nullptr) {
|
||||
dialog->force_close();
|
||||
dialog->force_hide(true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "dusk/settings.h"
|
||||
#include "dusk/config.hpp"
|
||||
#include "dusk/gamemode.hpp"
|
||||
#include <aurora/aurora.h>
|
||||
#include "dusk/config.hpp"
|
||||
#include "dusk/game_mode.hpp"
|
||||
|
||||
namespace dusk {
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#include "dusk/speedrun.h"
|
||||
#include "dusk/settings.h"
|
||||
#include "dusk/config.hpp"
|
||||
#include "m_Do/m_Do_main.h"
|
||||
#include <aurora/aurora.h>
|
||||
#include "dusk/gamemode.hpp"
|
||||
#include "dusk/config.hpp"
|
||||
#include "dusk/game_mode.hpp"
|
||||
#include "dusk/livesplit.h"
|
||||
#include "dusk/settings.h"
|
||||
#include "m_Do/m_Do_main.h"
|
||||
|
||||
namespace dusk::speedrun {
|
||||
|
||||
|
||||
+1
-2
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
#include <aurora/aurora.h>
|
||||
#include "dusk/gamemode.hpp"
|
||||
|
||||
#include "dusk/game_mode.hpp"
|
||||
|
||||
namespace dusk::speedrun {
|
||||
|
||||
|
||||
@@ -101,6 +101,15 @@ bool Document::focus() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Document::has_focus() const {
|
||||
if (mDocument == nullptr) {
|
||||
return false;
|
||||
}
|
||||
auto* context = mDocument->GetContext();
|
||||
const auto* focused = context != nullptr ? context->GetFocusElement() : nullptr;
|
||||
return focused != nullptr && focused->GetOwnerDocument() == mDocument;
|
||||
}
|
||||
|
||||
bool Document::set_document_styles(const Rml::String& rcss) {
|
||||
if (rcss.empty()) {
|
||||
mDocumentStyleSheets = nullptr;
|
||||
|
||||
@@ -20,6 +20,7 @@ public:
|
||||
virtual void hide(bool close);
|
||||
virtual void update();
|
||||
virtual bool focus();
|
||||
bool has_focus() const;
|
||||
virtual bool visible() const;
|
||||
virtual bool active() const;
|
||||
virtual bool obscures_game() const { return false; }
|
||||
@@ -64,6 +65,10 @@ public:
|
||||
hide(true);
|
||||
uncover_top_document();
|
||||
}
|
||||
void force_hide(bool close) {
|
||||
hide(close);
|
||||
Document::hide(close);
|
||||
}
|
||||
|
||||
bool closed() const { return mClosed; }
|
||||
|
||||
|
||||
+35
-32
@@ -7,12 +7,13 @@
|
||||
|
||||
#include "achievements.hpp"
|
||||
#include "aurora/rmlui.hpp"
|
||||
#include "dusk/game_mode.hpp"
|
||||
#include "dusk/livesplit.h"
|
||||
#include "dusk/gamemode.hpp"
|
||||
#include "dusk/main.h"
|
||||
#include "dusk/mods/svc/ui.hpp"
|
||||
#include "dusk/settings.h"
|
||||
#include "dusk/speedrun.h"
|
||||
#include "dusk/ui/prelaunch.hpp"
|
||||
#include "editor.hpp"
|
||||
#include "f_pc/f_pc_manager.h"
|
||||
#include "f_pc/f_pc_name.h"
|
||||
@@ -21,7 +22,6 @@
|
||||
#include "mods_window.hpp"
|
||||
#include "settings.hpp"
|
||||
#include "ui.hpp"
|
||||
#include "dusk/ui/prelaunch.hpp"
|
||||
#include "warp.hpp"
|
||||
#include "window.hpp"
|
||||
|
||||
@@ -56,6 +56,20 @@ MenuBar::MenuBar()
|
||||
},
|
||||
.autoSelect = false,
|
||||
});
|
||||
|
||||
// Hide document after transition completion
|
||||
listen(mRoot, Rml::EventId::Transitionend, [this](Rml::Event& event) {
|
||||
if (event.GetTargetElement() == mRoot && !mRoot->HasAttribute("open") &&
|
||||
Document::visible())
|
||||
{
|
||||
Document::hide(mPendingClose);
|
||||
}
|
||||
});
|
||||
|
||||
build_tabs();
|
||||
}
|
||||
|
||||
void MenuBar::build_tabs() {
|
||||
mTabBar->add_tab("Settings", [this] { push(std::make_unique<SettingsWindow>()); });
|
||||
|
||||
if (getSettings().backend.enableAdvancedSettings) {
|
||||
@@ -63,7 +77,7 @@ MenuBar::MenuBar()
|
||||
mTabBar->add_tab("Editor", [this] { push(std::make_unique<EditorWindow>()); });
|
||||
}
|
||||
|
||||
// Only allow us to access achievements if we are playing on a gamemode that uses them
|
||||
// Only allow us to access achievements if we are playing on a game mode 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>()); });
|
||||
@@ -101,12 +115,10 @@ MenuBar::MenuBar()
|
||||
}
|
||||
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);
|
||||
// If game modes are registered, return to prelaunch on reset.
|
||||
prelaunch_state().returnToPrelaunchOnReset = true;
|
||||
}
|
||||
hide(false);
|
||||
JUTGamePad::C3ButtonReset::sResetSwitchPushing = true;
|
||||
},
|
||||
},
|
||||
@@ -157,28 +169,19 @@ MenuBar::MenuBar()
|
||||
hide(false);
|
||||
});
|
||||
}
|
||||
|
||||
// Hide document after transition completion
|
||||
listen(mRoot, Rml::EventId::Transitionend, [this](Rml::Event& event) {
|
||||
if (event.GetTargetElement() == mRoot && !mRoot->HasAttribute("open") &&
|
||||
Document::visible())
|
||||
{
|
||||
Document::hide(mPendingClose);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void MenuBar::show() {
|
||||
Document::show();
|
||||
mRoot->SetAttribute("open", "");
|
||||
mTabBar->set_active_tab(-1);
|
||||
if (!mTabBar->focus_tab(mFocusedTabIndex)) {
|
||||
if (!mTabBar->focus_tab(mFocusedTabTitle)) {
|
||||
mTabBar->focus();
|
||||
}
|
||||
}
|
||||
|
||||
void MenuBar::hide(bool close) {
|
||||
mFocusedTabIndex = mTabBar->focused_tab_index();
|
||||
mFocusedTabTitle = mTabBar->focused_tab_title();
|
||||
mRoot->RemoveAttribute("open");
|
||||
if (close) {
|
||||
mPendingClose = true;
|
||||
@@ -248,19 +251,19 @@ bool MenuBar::focus() {
|
||||
return mTabBar->focus();
|
||||
}
|
||||
|
||||
void MenuBar::rebuild() {
|
||||
for (auto& doc : get_document_stack()) {
|
||||
if (auto* menuBar = dynamic_cast<MenuBar*>(doc.get())) {
|
||||
const bool wasVisible = menuBar->visible();
|
||||
auto next = std::make_unique<MenuBar>();
|
||||
next->mFocusedTabIndex = menuBar->mFocusedTabIndex;
|
||||
next->mWasVisible = menuBar->mWasVisible;
|
||||
doc = std::move(next);
|
||||
if (wasVisible) {
|
||||
doc->show();
|
||||
}
|
||||
break;
|
||||
}
|
||||
void MenuBar::refresh_tabs() {
|
||||
auto* menuBar = static_cast<MenuBar*>(find_document(DocumentScope::MenuBar));
|
||||
if (menuBar == nullptr) {
|
||||
return;
|
||||
}
|
||||
const auto focusedTitle = menuBar->mTabBar->focused_tab_title();
|
||||
if (!focusedTitle.empty()) {
|
||||
menuBar->mFocusedTabTitle = focusedTitle;
|
||||
}
|
||||
menuBar->mTabBar->clear_tabs();
|
||||
menuBar->build_tabs();
|
||||
if (menuBar->visible() && !menuBar->mTabBar->focus_tab(menuBar->mFocusedTabTitle)) {
|
||||
menuBar->mTabBar->focus();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,12 +21,13 @@ public:
|
||||
bool focus() override;
|
||||
bool visible() const override;
|
||||
|
||||
static void rebuild();
|
||||
static void refresh_tabs();
|
||||
|
||||
protected:
|
||||
bool handle_nav_command(Rml::Event& event, NavCommand cmd) override;
|
||||
|
||||
private:
|
||||
void build_tabs();
|
||||
void update_safe_area() noexcept;
|
||||
|
||||
Rml::Element* mRoot;
|
||||
@@ -34,7 +35,7 @@ private:
|
||||
std::unique_ptr<Button> mCloseButton;
|
||||
Insets mTabBarPadding;
|
||||
float mTopMargin = 0.f;
|
||||
int mFocusedTabIndex = -1;
|
||||
Rml::String mFocusedTabTitle;
|
||||
};
|
||||
|
||||
} // namespace dusk::ui
|
||||
|
||||
@@ -59,7 +59,6 @@ public:
|
||||
~ModWindow() override;
|
||||
|
||||
void update() override;
|
||||
void force_close() { Document::hide(true); }
|
||||
|
||||
private:
|
||||
Desc mDesc;
|
||||
|
||||
+48
-55
@@ -3,7 +3,7 @@
|
||||
#include "dusk/app_info.hpp"
|
||||
#include "dusk/config.hpp"
|
||||
#include "dusk/data.hpp"
|
||||
#include "dusk/gamemode.hpp"
|
||||
#include "dusk/game_mode.hpp"
|
||||
#include "dusk/iso_validate.hpp"
|
||||
#include "dusk/language.hpp"
|
||||
#include "dusk/main.h"
|
||||
@@ -745,18 +745,17 @@ void try_apply_mirrored_layout(Rml::Element* body) {
|
||||
body->SetClass("mirrored", getSettings().game.enableMirrorMode.getValue());
|
||||
}
|
||||
|
||||
void Prelaunch::rebuild_menu_buttons() {
|
||||
for (auto& doc : get_document_stack()) {
|
||||
if (auto* prelaunch = dynamic_cast<Prelaunch*>(doc.get())) {
|
||||
auto* menuList = prelaunch->mDocument->GetElementById("menu-list");
|
||||
while (menuList->GetNumChildren() > 0) {
|
||||
menuList->RemoveChild(menuList->GetChild(0));
|
||||
}
|
||||
prelaunch->mMenuButtons.clear();
|
||||
prelaunch->build_menu_buttons();
|
||||
break;
|
||||
}
|
||||
void Prelaunch::refresh_menu_buttons() {
|
||||
auto* prelaunch = static_cast<Prelaunch*>(find_document(DocumentScope::Prelaunch));
|
||||
if (prelaunch == nullptr) {
|
||||
return;
|
||||
}
|
||||
auto* menuList = prelaunch->mDocument->GetElementById("menu-list");
|
||||
while (menuList->GetNumChildren() > 0) {
|
||||
menuList->RemoveChild(menuList->GetChild(0));
|
||||
}
|
||||
prelaunch->mMenuButtons.clear();
|
||||
prelaunch->build_menu_buttons();
|
||||
}
|
||||
|
||||
static std::string get_playbutton_text() {
|
||||
@@ -766,10 +765,10 @@ static std::string get_playbutton_text() {
|
||||
return "Select Disc Image";
|
||||
}
|
||||
std::string playText;
|
||||
const dusk::gamemode::GameMode* currentGameMode =
|
||||
dusk::gamemode::getGameModeManager().getCurrentGameMode();
|
||||
const gamemode::GameMode* currentGameMode =
|
||||
gamemode::getGameModeManager().getCurrentGameMode();
|
||||
if (currentGameMode != nullptr) {
|
||||
return currentGameMode->getId() == dusk::gamemode::kVanillaGameModeId ? "Play" :
|
||||
return currentGameMode->getId() == gamemode::kVanillaGameModeId ? "Play" :
|
||||
"Play " + currentGameMode->getFullName();
|
||||
}
|
||||
return "Play";
|
||||
@@ -821,7 +820,7 @@ Prelaunch::Prelaunch() : Document(kDocumentSource, false, DocumentScope::Prelaun
|
||||
void Prelaunch::build_menu_buttons() {
|
||||
if (auto* menuList = mDocument->GetElementById("menu-list")) {
|
||||
// Set the gamemode to the last used before showing the play button
|
||||
dusk::gamemode::getGameModeManager().setGameModeToPrevious();
|
||||
gamemode::getGameModeManager().setGameModeToPrevious();
|
||||
|
||||
mMenuButtons.push_back(std::make_unique<Button>(menuList, get_playbutton_text()));
|
||||
mMenuButtons.back()->on_pressed([this] {
|
||||
@@ -846,66 +845,52 @@ void Prelaunch::build_menu_buttons() {
|
||||
}
|
||||
|
||||
prelaunch_state().firstLaunch = false;
|
||||
const dusk::gamemode::GameMode* gamemode =
|
||||
dusk::gamemode::getGameModeManager().getCurrentGameMode();
|
||||
if (gamemode) {
|
||||
gamemode->invokeOnPlayFunction();
|
||||
const gamemode::GameMode* gameMode =
|
||||
gamemode::getGameModeManager().getCurrentGameMode();
|
||||
if (gameMode) {
|
||||
gameMode->invokeOnPlayFunction();
|
||||
}
|
||||
|
||||
IsGameLaunched = true;
|
||||
pop();
|
||||
|
||||
// If we deleted the menubar on a previous reset, create it again here
|
||||
bool menuBarExists = false;
|
||||
for (auto& doc : dusk::ui::get_document_stack()) {
|
||||
if (auto* menubar = dynamic_cast<dusk::ui::MenuBar*>(doc.get())) {
|
||||
menuBarExists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (menuBarExists) {
|
||||
MenuBar::rebuild();
|
||||
}else{
|
||||
dusk::ui::push_document(std::make_unique<dusk::ui::MenuBar>(), false);
|
||||
}
|
||||
hide(true);
|
||||
MenuBar::refresh_tabs();
|
||||
});
|
||||
apply_intro_animation(mMenuButtons.back()->root(), "delay-1");
|
||||
|
||||
// If we have more gamemodes registered than the default vanilla, show the gamemode
|
||||
// selection
|
||||
if (dusk::gamemode::getGameModeManager().getRegisteredGameModes().size() > 1) {
|
||||
mMenuButtons.push_back(std::make_unique<Button>(menuList, "Select GameMode"));
|
||||
// Only show selection when game modes are registered (besides vanilla)
|
||||
if (gamemode::getGameModeManager().getRegisteredGameModes().size() > 1) {
|
||||
mMenuButtons.push_back(std::make_unique<Button>(menuList, "Select Game Mode"));
|
||||
mMenuButtons.back()->on_pressed([this] {
|
||||
std::vector<ModalAction> gamemodeActions;
|
||||
gamemodeActions.push_back(dusk::ui::ModalAction{
|
||||
.label = "Vanilla", .onPressed = [this](dusk::ui::Modal& modal) {
|
||||
std::vector<ModalAction> gameModeActions;
|
||||
gameModeActions.push_back(ModalAction{
|
||||
.label = "Vanilla", .onPressed = [this](Modal& modal) {
|
||||
mDoAud_seStartMenu(kSoundClick);
|
||||
dusk::gamemode::getGameModeManager().setCurrentGameMode(dusk::gamemode::kVanillaGameModeId);
|
||||
gamemode::getGameModeManager().setCurrentGameMode(gamemode::kVanillaGameModeId);
|
||||
modal.pop();
|
||||
update();
|
||||
}});
|
||||
for (const auto& [id, gamemode] :
|
||||
dusk::gamemode::getGameModeManager().getRegisteredGameModes())
|
||||
for (const auto& [id, gameMode] :
|
||||
gamemode::getGameModeManager().getRegisteredGameModes())
|
||||
{
|
||||
if (id == dusk::gamemode::kVanillaGameModeId) {
|
||||
if (id == gamemode::kVanillaGameModeId) {
|
||||
// Force vanilla to the top
|
||||
continue;
|
||||
}
|
||||
gamemodeActions.push_back(dusk::ui::ModalAction{.label = gamemode.getFullName(),
|
||||
.onPressed = [this, id](dusk::ui::Modal& modal) {
|
||||
gameModeActions.push_back(ModalAction{.label = gameMode.getFullName(),
|
||||
.onPressed = [this, id](Modal& modal) {
|
||||
mDoAud_seStartMenu(kSoundClick);
|
||||
dusk::gamemode::getGameModeManager().setCurrentGameMode(id);
|
||||
gamemode::getGameModeManager().setCurrentGameMode(id);
|
||||
modal.pop();
|
||||
update();
|
||||
}});
|
||||
}
|
||||
mRestartSuppressed = false;
|
||||
push(std::make_unique<dusk::ui::Modal>(dusk::ui::Modal::Props{
|
||||
push(std::make_unique<Modal>(Modal::Props{
|
||||
.title = "Play Type",
|
||||
.bodyRml = "What mode would you like to play?",
|
||||
.actions = gamemodeActions,
|
||||
.actions = gameModeActions,
|
||||
.onDismiss =
|
||||
[this](dusk::ui::Modal& modal) {
|
||||
[this](Modal& modal) {
|
||||
mDoAud_seStartMenu(kSoundWindowClose);
|
||||
modal.pop();
|
||||
},
|
||||
@@ -948,14 +933,14 @@ void Prelaunch::show() {
|
||||
modal.pop();
|
||||
};
|
||||
std::vector<ModalAction> actions;
|
||||
if constexpr (dusk::SupportsProcessRestart) {
|
||||
if constexpr (SupportsProcessRestart) {
|
||||
actions.push_back(ModalAction{
|
||||
.label = "Restart later",
|
||||
.onPressed = dismiss,
|
||||
});
|
||||
actions.push_back(ModalAction{
|
||||
.label = "Restart now",
|
||||
.onPressed = [](Modal&) { dusk::RequestRestart(); },
|
||||
.onPressed = [](Modal&) { RequestRestart(); },
|
||||
});
|
||||
} else {
|
||||
actions.push_back(ModalAction{
|
||||
@@ -966,7 +951,7 @@ void Prelaunch::show() {
|
||||
push(std::make_unique<Modal>(Modal::Props{
|
||||
.title = "Apply Options",
|
||||
.bodyRml =
|
||||
dusk::SupportsProcessRestart ?
|
||||
SupportsProcessRestart ?
|
||||
"A restart is required to apply selected options.<br/><br/>Restart now to "
|
||||
"apply them immediately?" :
|
||||
"A restart is required to apply selected options.<br/><br/>Close and reopen "
|
||||
@@ -1126,6 +1111,14 @@ void Prelaunch::update() {
|
||||
Document::update();
|
||||
}
|
||||
|
||||
void return_to_prelaunch() noexcept {
|
||||
close_documents_except(DocumentScope::MenuBar);
|
||||
if (auto* menuBar = find_document(DocumentScope::MenuBar)) {
|
||||
menuBar->force_hide(false);
|
||||
}
|
||||
push_document(std::make_unique<Prelaunch>(), true);
|
||||
}
|
||||
|
||||
bool Prelaunch::focus() {
|
||||
if (mMenuButtons.empty()) {
|
||||
return false;
|
||||
|
||||
@@ -23,7 +23,7 @@ public:
|
||||
bool visible() const override;
|
||||
bool obscures_game() const override { return true; }
|
||||
|
||||
static void rebuild_menu_buttons();
|
||||
static void refresh_menu_buttons();
|
||||
|
||||
protected:
|
||||
bool handle_nav_command(Rml::Event& event, NavCommand cmd) override;
|
||||
@@ -62,10 +62,11 @@ struct PrelaunchState {
|
||||
std::string pendingDiscPath;
|
||||
iso::DiscInfo pendingDiscInfo{};
|
||||
iso::ValidationError pendingDiscValidation = iso::ValidationError::Unknown;
|
||||
bool showPrelaunchOnReset = false;
|
||||
bool returnToPrelaunchOnReset = false;
|
||||
};
|
||||
|
||||
PrelaunchState& prelaunch_state() noexcept;
|
||||
void return_to_prelaunch() noexcept;
|
||||
void ensure_initialized() noexcept;
|
||||
void refresh_configured_disc_state() noexcept;
|
||||
void open_iso_picker() noexcept;
|
||||
|
||||
+14
-14
@@ -1246,7 +1246,7 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
|
||||
}
|
||||
dusk::speedrun::unregisterSpeedrunGameMode();
|
||||
}
|
||||
MenuBar::rebuild();
|
||||
MenuBar::refresh_tabs();
|
||||
},
|
||||
});
|
||||
config_bool_select(leftPane, rightPane, getSettings().game.liveSplitEnabled,
|
||||
@@ -1363,16 +1363,15 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
|
||||
"replacements, and other app data.");
|
||||
});
|
||||
#endif
|
||||
leftPane.register_control(
|
||||
leftPane.add_button("Restart To Main Menu").on_pressed([this] {
|
||||
mDoAud_seStartMenu(kSoundClick);
|
||||
pop();
|
||||
ui::prelaunch_state().showPrelaunchOnReset = true;
|
||||
JUTGamePad::C3ButtonReset::sResetSwitchPushing = true;
|
||||
}),
|
||||
leftPane.register_control(leftPane.add_button("Restart to Main Menu").on_pressed([this] {
|
||||
mDoAud_seStartMenu(kSoundClick);
|
||||
pop();
|
||||
ui::prelaunch_state().returnToPrelaunchOnReset = true;
|
||||
JUTGamePad::C3ButtonReset::sResetSwitchPushing = true;
|
||||
}),
|
||||
rightPane, [](Pane& pane) {
|
||||
pane.add_text(
|
||||
"Restart Dusklight to the pre-launch menu to change settings, gamemodes, or mods.");
|
||||
pane.add_text("Restart Dusklight to the pre-launch menu to change settings, game "
|
||||
"modes, or mods.");
|
||||
});
|
||||
leftPane.register_control(
|
||||
leftPane.add_select_button({
|
||||
@@ -1462,9 +1461,10 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
|
||||
config_bool_select(leftPane, rightPane, getSettings().backend.skipPreLaunchUI,
|
||||
{
|
||||
.key = "Skip Dusklight Main Menu",
|
||||
.helpText = "When starting Dusklight, skip the main menu and boot straight into the "
|
||||
"game if a disc image is available.<br/><br/>Note: If any mods register gamemodes, "
|
||||
"then this option will be ignored.",
|
||||
.helpText =
|
||||
"When starting Dusklight, skip the main menu and boot straight into the "
|
||||
"game if a disc image is available.<br/><br/>Note: If any mods register game "
|
||||
"modes, this option will be ignored.",
|
||||
});
|
||||
config_bool_select(leftPane, rightPane, getSettings().backend.checkForUpdates,
|
||||
{
|
||||
@@ -1493,7 +1493,7 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
|
||||
.helpText = "Show advanced settings and debugging tools with "
|
||||
"Shift+F1.<br/><br/><icon class=\"warning\"/> WARNING: Debugging tools "
|
||||
"can easily break your game. Do not use on a regular save!",
|
||||
.onChange = [](bool) { MenuBar::rebuild(); },
|
||||
.onChange = [](bool) { MenuBar::refresh_tabs(); },
|
||||
.isDisabled = [] { return dusk::speedrun::isActive(); },
|
||||
});
|
||||
config_bool_select(leftPane, rightPane, getSettings().game.showInputViewer,
|
||||
|
||||
+35
-13
@@ -94,14 +94,14 @@ TabBar::TabBar(Rml::Element* parent, Props props)
|
||||
bool TabBar::focus() {
|
||||
if (mProps.selectedTabIndex >= 0 && mProps.selectedTabIndex < mTabs.size()) {
|
||||
// Try to focus the currently selected tab
|
||||
if (mTabs[mProps.selectedTabIndex].button.focus()) {
|
||||
if (mTabs[mProps.selectedTabIndex].button->focus()) {
|
||||
mLastFocusedTabIndex = mProps.selectedTabIndex;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Otherwise, focus the first enabled tab
|
||||
for (int i = 0; i < static_cast<int>(mTabs.size()); ++i) {
|
||||
if (mTabs[i].button.focus()) {
|
||||
if (mTabs[i].button->focus()) {
|
||||
mLastFocusedTabIndex = i;
|
||||
return true;
|
||||
}
|
||||
@@ -115,8 +115,8 @@ void TabBar::add_tab(const Rml::String& title, TabCallback callback) {
|
||||
if (selected && callback) {
|
||||
callback();
|
||||
}
|
||||
auto& button = add_child<Button>(Button::Props{title}, "tab");
|
||||
button.on_nav_command([this, index](Rml::Event&, NavCommand cmd) {
|
||||
auto button = std::make_unique<Button>(mRoot, Button::Props{title}, "tab");
|
||||
button->on_nav_command([this, index](Rml::Event&, NavCommand cmd) {
|
||||
if (cmd == NavCommand::Confirm) {
|
||||
if (mProps.autoSelect) {
|
||||
mDoAud_seStartMenu(kSoundTabChanged);
|
||||
@@ -127,7 +127,7 @@ void TabBar::add_tab(const Rml::String& title, TabCallback callback) {
|
||||
return false;
|
||||
});
|
||||
if (selected) {
|
||||
button.set_selected(true);
|
||||
button->set_selected(true);
|
||||
}
|
||||
if (mEndSpacer != nullptr) {
|
||||
auto spacer = mRoot->RemoveChild(mEndSpacer);
|
||||
@@ -135,16 +135,26 @@ void TabBar::add_tab(const Rml::String& title, TabCallback callback) {
|
||||
}
|
||||
mTabs.emplace_back(Tab{
|
||||
.title = title,
|
||||
.button = button,
|
||||
.button = std::move(button),
|
||||
.callback = std::move(callback),
|
||||
});
|
||||
}
|
||||
|
||||
void TabBar::clear_tabs() {
|
||||
mProps.selectedTabIndex = -1;
|
||||
mLastFocusedTabIndex = -1;
|
||||
while (!mTabs.empty()) {
|
||||
auto* element = mTabs.back().button->root();
|
||||
mTabs.pop_back();
|
||||
mRoot->RemoveChild(element);
|
||||
}
|
||||
}
|
||||
|
||||
bool TabBar::set_active_tab(int index) {
|
||||
if (index == -1) {
|
||||
// Clear currently selected tab
|
||||
for (auto& tab : mTabs) {
|
||||
tab.button.set_selected(false);
|
||||
tab.button->set_selected(false);
|
||||
}
|
||||
mProps.selectedTabIndex = -1;
|
||||
return true;
|
||||
@@ -154,10 +164,10 @@ bool TabBar::set_active_tab(int index) {
|
||||
return false;
|
||||
}
|
||||
const auto& tab = mTabs[index];
|
||||
if (tab.button.focus()) {
|
||||
if (tab.button->focus()) {
|
||||
mLastFocusedTabIndex = index;
|
||||
for (int i = 0; i < static_cast<int>(mTabs.size()); ++i) {
|
||||
mTabs[i].button.set_selected(i == index);
|
||||
mTabs[i].button->set_selected(i == index);
|
||||
}
|
||||
mProps.selectedTabIndex = index;
|
||||
if (tab.callback) {
|
||||
@@ -177,24 +187,36 @@ void TabBar::refresh_active_tab() {
|
||||
}
|
||||
}
|
||||
|
||||
int TabBar::focused_tab_index() const {
|
||||
return mLastFocusedTabIndex;
|
||||
Rml::String TabBar::focused_tab_title() const {
|
||||
if (mLastFocusedTabIndex < 0 || mLastFocusedTabIndex >= static_cast<int>(mTabs.size())) {
|
||||
return {};
|
||||
}
|
||||
return mTabs[mLastFocusedTabIndex].title;
|
||||
}
|
||||
|
||||
bool TabBar::focus_tab(int index) {
|
||||
if (index < 0 || index >= mTabs.size() || index == mProps.selectedTabIndex) {
|
||||
return false;
|
||||
}
|
||||
if (mTabs[index].button.focus()) {
|
||||
if (mTabs[index].button->focus()) {
|
||||
mLastFocusedTabIndex = index;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TabBar::focus_tab(const Rml::String& title) {
|
||||
for (int i = 0; i < static_cast<int>(mTabs.size()); ++i) {
|
||||
if (mTabs[i].title == title) {
|
||||
return focus_tab(i);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int TabBar::tab_containing(Rml::Element* element) const {
|
||||
for (int i = 0; i < mTabs.size(); ++i) {
|
||||
if (mTabs[i].button.contains(element)) {
|
||||
if (mTabs[i].button->contains(element)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ using TabCallback = std::function<void()>;
|
||||
|
||||
struct Tab {
|
||||
Rml::String title;
|
||||
Button& button;
|
||||
std::unique_ptr<Button> button;
|
||||
TabCallback callback;
|
||||
};
|
||||
|
||||
@@ -27,10 +27,12 @@ public:
|
||||
bool focus() override;
|
||||
|
||||
void add_tab(const Rml::String& title, TabCallback callback);
|
||||
void clear_tabs();
|
||||
bool set_active_tab(int index);
|
||||
void refresh_active_tab();
|
||||
bool focus_tab(int index);
|
||||
int focused_tab_index() const;
|
||||
bool focus_tab(const Rml::String& title);
|
||||
Rml::String focused_tab_title() const;
|
||||
bool handle_nav_command(Rml::Event& event, NavCommand cmd);
|
||||
|
||||
private:
|
||||
|
||||
+21
-10
@@ -267,6 +267,24 @@ void uncover_top_document() noexcept {
|
||||
input::sync_input_block();
|
||||
}
|
||||
|
||||
Document* find_document(DocumentScope scope) noexcept {
|
||||
for (auto& doc : std::views::reverse(sDocumentStack)) {
|
||||
if (!doc->closed() && doc->scope() == scope) {
|
||||
return doc.get();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void close_documents_except(DocumentScope scope) noexcept {
|
||||
for (auto& doc : sDocumentStack) {
|
||||
if (!doc->closed() && doc->scope() != scope) {
|
||||
doc->force_hide(true);
|
||||
}
|
||||
}
|
||||
input::sync_input_block();
|
||||
}
|
||||
|
||||
bool any_document_visible() noexcept {
|
||||
return std::any_of(sDocumentStack.begin(), sDocumentStack.end(),
|
||||
[](const auto& doc) { return doc && doc->visible(); });
|
||||
@@ -330,13 +348,10 @@ void update() noexcept {
|
||||
sPassiveDocuments.erase(first, last);
|
||||
}
|
||||
|
||||
// If no documents have focus, explicitly focus the top one
|
||||
if (auto* context = aurora::rmlui::get_context();
|
||||
context != nullptr && (context->GetFocusElement() == nullptr ||
|
||||
context->GetFocusElement() == context->GetRootElement()))
|
||||
{
|
||||
// Keep focus on the highest active document.
|
||||
if (aurora::rmlui::get_context() != nullptr) {
|
||||
for (auto& doc : std::views::reverse(sDocumentStack)) {
|
||||
if (doc->active() && doc->focus()) {
|
||||
if (doc->active() && (doc->has_focus() || doc->focus())) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -462,10 +477,6 @@ void push_toast(Toast toast) noexcept {
|
||||
sToasts.push_back(std::move(toast));
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<Document>>& get_document_stack() noexcept {
|
||||
return sDocumentStack;
|
||||
}
|
||||
|
||||
std::deque<Toast>& get_toasts() noexcept {
|
||||
return sToasts;
|
||||
}
|
||||
|
||||
+2
-2
@@ -91,6 +91,8 @@ bool register_scoped_styles(DocumentScope scope, std::string id, const std::stri
|
||||
void unregister_scoped_styles(DocumentScope scope, std::string_view id) noexcept;
|
||||
void apply_scoped_styles(Document& doc) noexcept;
|
||||
void uncover_top_document() noexcept;
|
||||
Document* find_document(DocumentScope scope) noexcept;
|
||||
void close_documents_except(DocumentScope scope) noexcept;
|
||||
bool any_document_visible() noexcept;
|
||||
bool is_prelaunch_open() noexcept;
|
||||
bool game_obscured_below(const Document& doc) noexcept;
|
||||
@@ -104,8 +106,6 @@ Rml::Element* append_text(Rml::Element* parent, const Rml::String& text) noexcep
|
||||
NavCommand map_nav_event(const Rml::Event& event) noexcept;
|
||||
Insets safe_area_insets(Rml::Context* context) noexcept;
|
||||
|
||||
std::vector<std::unique_ptr<Document>>& get_document_stack() noexcept;
|
||||
|
||||
void push_toast(Toast toast) noexcept;
|
||||
std::deque<Toast>& get_toasts() noexcept;
|
||||
void show_menu_notification() noexcept;
|
||||
|
||||
@@ -27,11 +27,11 @@
|
||||
#include "m_Do/m_Do_main.h"
|
||||
|
||||
#if TARGET_PC
|
||||
#include "tracy/Tracy.hpp"
|
||||
#include <dusk/gamepad_color.h>
|
||||
#include <dusk/autosave.h>
|
||||
#include <dusk/gamepad_color.h>
|
||||
#include "dusk/game_mode.hpp"
|
||||
#include "dusk/menu_pointer.h"
|
||||
#include "dusk/gamemode.hpp"
|
||||
#include "tracy/Tracy.hpp"
|
||||
#endif
|
||||
|
||||
fapGm_HIO_c::fapGm_HIO_c() {
|
||||
@@ -846,9 +846,10 @@ void fapGm_Execute() {
|
||||
|
||||
cCt_Counter(0);
|
||||
#ifdef TARGET_PC
|
||||
const dusk::gamemode::GameMode* gamemode = dusk::gamemode::getGameModeManager().getCurrentGameMode();
|
||||
if (gamemode) {
|
||||
gamemode->invokeOnTickFunction();
|
||||
const dusk::gamemode::GameMode* gameMode =
|
||||
dusk::gamemode::getGameModeManager().getCurrentGameMode();
|
||||
if (gameMode) {
|
||||
gameMode->invokeOnTickFunction();
|
||||
}
|
||||
dusk::AchievementSystem::get().tick();
|
||||
dusk::menu_pointer::end_game_frame();
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
#include "f_pc/f_pc_manager.h"
|
||||
|
||||
#ifdef TARGET_PC
|
||||
#include "dusk/game_mode.hpp"
|
||||
#include "dusk/speedrun.h"
|
||||
#include "dusk/gamemode.hpp"
|
||||
#endif
|
||||
|
||||
void fopOvlpReq_SetPeektime(overlap_request_class*, u16);
|
||||
|
||||
+8
-30
@@ -20,10 +20,8 @@
|
||||
#include "os_report.h"
|
||||
|
||||
#ifdef TARGET_PC
|
||||
#include "dusk/game_mode.hpp"
|
||||
#include "dusk/ui/prelaunch.hpp"
|
||||
#include "dusk/ui/menu_bar.hpp"
|
||||
#include "dusk/ui/mods_window.hpp"
|
||||
#include "dusk/gamemode.hpp"
|
||||
#endif
|
||||
|
||||
static void my_OSCancelAlarmAll() {}
|
||||
@@ -112,9 +110,10 @@ void checkDiskCallback(s32 result, DVDCommandBlock* block) {
|
||||
|
||||
void mDoRst_resetCallBack(int port, void*) {
|
||||
#ifdef TARGET_PC
|
||||
const dusk::gamemode::GameMode* gamemode = dusk::gamemode::getGameModeManager().getCurrentGameMode();
|
||||
if (gamemode) {
|
||||
gamemode->invokeOnGameResetFunction();
|
||||
const dusk::gamemode::GameMode* gameMode =
|
||||
dusk::gamemode::getGameModeManager().getCurrentGameMode();
|
||||
if (gameMode) {
|
||||
gameMode->invokeOnGameResetFunction();
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -157,31 +156,10 @@ void mDoRst_resetCallBack(int port, void*) {
|
||||
}
|
||||
mDoRst::onReset();
|
||||
#ifdef TARGET_PC
|
||||
// Show pre-launch only if we have a registered gamemode and are resetting from the menubar
|
||||
if (dusk::ui::prelaunch_state().showPrelaunchOnReset == false) {
|
||||
return;
|
||||
if (dusk::ui::prelaunch_state().returnToPrelaunchOnReset) {
|
||||
dusk::ui::return_to_prelaunch();
|
||||
dusk::ui::prelaunch_state().returnToPrelaunchOnReset = false;
|
||||
}
|
||||
|
||||
bool prelaunchExists = false;
|
||||
for (auto& doc : dusk::ui::get_document_stack()) {
|
||||
if (auto* menubar = dynamic_cast<dusk::ui::MenuBar*>(doc.get())) {
|
||||
// Hide the menu bar
|
||||
menubar->Document::hide(true);
|
||||
}
|
||||
if (auto* modwindow = dynamic_cast<dusk::ui::ModsWindow*>(doc.get())) {
|
||||
// Hide the mod window (if we were disasbling or reloading a mod)
|
||||
modwindow->pop();
|
||||
}
|
||||
if (auto* prelaunch = dynamic_cast<dusk::ui::Prelaunch*>(doc.get())) {
|
||||
prelaunchExists = true;
|
||||
prelaunch->focus();
|
||||
}
|
||||
}
|
||||
if (prelaunchExists == false) {
|
||||
dusk::ui::Prelaunch& prelaunch = static_cast<dusk::ui::Prelaunch&>(dusk::ui::push_document(std::make_unique<dusk::ui::Prelaunch>(), true));
|
||||
prelaunch.focus();
|
||||
}
|
||||
dusk::ui::prelaunch_state().showPrelaunchOnReset = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user