Mod SDK: Gamemode Service, Change Speedrun mode to a gamemode, Allow prelaunch to be restarted

This commit is contained in:
jdflyer
2026-07-19 00:50:49 -07:00
parent 1bae8a5e6a
commit 61a4ab3117
38 changed files with 974 additions and 250 deletions
+47
View File
@@ -470,6 +470,53 @@ if (svc_camera->get_camera(mod_ctx, game_view, &camera) == MOD_OK) {
first in-game frame. Projection matrices match the renderer's WebGPU clip convention and renderer depth convention
(reversed-Z by default).
### 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.
```cpp
IMPORT_SERVICE(GamemodeService, svc_gamemode);
void onSaveLoaded() {
// This function will be invoked by the game as a save is loaded
}
#define ONLY_GAMEMODE(ctx, id) \
{ \
bool isGamemodeActive; \
svc_gamemode->is_active(ctx, id, &isGamemodeActive); \
if (!isGamemodeActive) { \
return; \
} \
}
static HookAction myFunctionHook(ModContext *ctx, void *args, void *, void *) {
// Note: normal function hooks will need to check if the gamemode is active
ONLY_GAMEMODE(ctx,"id"); // A macro like this can make it easy
}
const GamemodeDesc gamemodeDesc = {
.gamemodeId = "my-unique-gamemode-id",
.fullName = "Gamemode Name",
.saveName = "my-unique-save-name",
.onActivatedFunction = nullptr,
.onDeactivatedFunction = nullptr,
.onPlayFunction = nullptr,
.onSaveLoadedFunction = onSaveLoaded,
.onNewSaveFunction = nullptr,
.onGameResetFunction = nullptr,
.onTickFunction = nullptr,
};
svc_gamemode->register_gamemode(mod_ctx, &gamemodeDesc);
```
---
## Hooking Game Functions
+2
View File
@@ -1441,6 +1441,7 @@ set(DUSK_FILES
src/dusk/file_select.hpp
src/dusk/frame_interpolation.cpp
src/dusk/game_clock.cpp
src/dusk/gamemode.cpp
src/dusk/gamepad_color.cpp
src/dusk/globals.cpp
src/dusk/gyro.cpp
@@ -1498,6 +1499,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/mouse.cpp
src/dusk/scope_guard.hpp
src/dusk/settings.cpp
+20
View File
@@ -112,6 +112,11 @@ public:
mSerialNo = serial_no;
}
#ifdef TARGET_PC
void setFileName(const std::string& fileName);
const char* getFileName();
#endif
/* 0x0000 */ u8 mData[SAVEFILE_SIZE];
/* 0x1FBC */ u8 mChannel;
/* 0x1FBD */ u8 mCopyToPos;
@@ -124,6 +129,11 @@ public:
/* 0x1FEC */ s32 mNandState;
/* 0x1FF0 */ u64 mSerialNo;
/* 0x1FF8 */ u32 mDataVersion;
#ifdef TARGET_PC
bool mInitialized;
std::string mFileName;
#endif
}; // Size: 0x2000
STATIC_ASSERT(sizeof(mDoMemCd_Ctrl_c) == 8192);
@@ -230,4 +240,14 @@ inline s32 mDoMemCd_checkNANDFile() {
}
#endif
#ifdef TARGET_PC
inline void mDoMemCd_SetFileName(const std::string& fileName) {
g_mDoMemCd_control.setFileName(fileName);
}
inline const char* mDoMemCd_GetFileName() {
return g_mDoMemCd_control.getFileName();
}
#endif
#endif /* M_DO_M_DO_MEMCARD_H */
+10
View File
@@ -519,3 +519,13 @@ progress.verification-progress-bar {
flex: 0 0 auto;
padding-top: 4dp;
}
.modal-actions-vertical {
flex-direction: column;
align-items: stretch;
}
.modal-actions-vertical button.modal-btn {
flex: 0 0 auto;
width: 100%;
}
+39
View File
@@ -0,0 +1,39 @@
#pragma once
#include <mods/api.h>
#include <mods/svc/config.h>
#define GAMEMODE_SERVICE_ID "dev.twilitrealm.dusklight.gamemode"
#define GAMEMODE_SERVICE_MAJOR 1u
#define GAMEMODE_SERVICE_MINOR 0u
typedef struct {
const char* gamemodeId;
const char* fullName;
const char* saveName;
void (*onActivatedFunction)();
void (*onDeactivatedFunction)();
void (*onPlayFunction)();
void (*onSaveLoadedFunction)();
void (*onNewSaveFunction)();
void (*onGameResetFunction)();
void (*onTickFunction)();
} GamemodeDesc;
typedef struct GamemodeService {
ServiceHeader header;
ModResult (*register_gamemode)(ModContext* ctx, const GamemodeDesc* desc);
ModResult (*unregister_gamemode)(ModContext* ctx, const char* id);
ModResult (*is_active)(ModContext* ctx, const char* gamemodeId, bool* out_active);
} GamemodeService;
#ifdef __cplusplus
#include "mods/service.hpp"
template <>
struct mods::ServiceTraits<GamemodeService> {
static constexpr const char* id = GAMEMODE_SERVICE_ID;
static constexpr uint16_t major_version = GAMEMODE_SERVICE_MAJOR;
static constexpr uint16_t minor_version = GAMEMODE_SERVICE_MINOR;
};
#endif
+6 -3
View File
@@ -23,9 +23,12 @@
#include "d/actor/d_a_npc_tkc.h"
#include <cstring>
#ifdef TARGET_PC
#include "dusk/imgui/ImGuiConsole.hpp"
#include "dusk/settings.h"
#include "dusk/speedrun.h"
#include "dusk/gamemode.hpp"
#endif
BOOL daAlink_c::checkEventRun() const {
return dComIfGp_event_runCheck() || checkPlayerDemoMode();
@@ -4009,9 +4012,9 @@ int daAlink_c::procGanonFinishInit() {
onEndResetFlg1(ERFLG1_SHIELD_BACKBONE);
#if TARGET_PC
if (dusk::getSettings().game.speedrunMode) {
if (dusk::m_speedrunInfo.m_isRunStarted) {
dusk::m_speedrunInfo.stopRun();
if (dusk::speedrun::isActive()) {
if (dusk::speedrun::g_speedrunInfo.m_isRunStarted) {
dusk::speedrun::g_speedrunInfo.stopRun();
}
}
#endif
+10 -8
View File
@@ -9,11 +9,15 @@
#include "JSystem/J2DGraph/J2DScreen.h"
#include "JSystem/J2DGraph/J2DTextBox.h"
#include "d/d_msg_string.h"
#include "m_Do/m_Do_controller_pad.h"
#ifdef TARGET_PC
#include <dusk/autosave.h>
#include "dusk/livesplit.h"
#include "dusk/imgui/ImGuiConsole.hpp"
#include "dusk/speedrun.h"
#include "m_Do/m_Do_controller_pad.h"
#include <dusk/autosave.h>
#include "dusk/gamemode.hpp"
#endif
dBrightCheck_c::dBrightCheck_c(JKRArchive* i_archive) {
mArchive = i_archive;
@@ -143,12 +147,10 @@ void dBrightCheck_c::modeMove() {
if (mDoCPd_c::getTrigA(PAD_1) || mDoCPd_c::getTrigStart(PAD_1)) {
mDoAud_seStart(Z2SE_ENTER_GAME, NULL, 0, 0);
#ifdef TARGET_PC
if (dusk::getSettings().game.speedrunMode && !dusk::getSettings().game.hideTvSettingsScreen) {
// start a new run if a run isn't already in progress
if (!dusk::m_speedrunInfo.m_isRunStarted) {
dusk::resetForSpeedrunMode();
dusk::m_speedrunInfo.startRun();
dusk::speedrun::start();
if (!dusk::getSettings().game.hideTvSettingsScreen) {
const dusk::gamemode::Gamemode* gamemode = dusk::gamemode::getGamemodeManager().getCurrentGamemode();
if (gamemode) {
gamemode->mOnSaveLoadedFunction();
}
}
+14 -12
View File
@@ -9,11 +9,6 @@
#include "d/d_com_inf_game.h"
#include "d/d_meter2_info.h"
#include "d/d_s_name.h"
#include "dusk/imgui/ImGuiConsole.hpp"
#include "dusk/livesplit.h"
#include "dusk/memory.h"
#include "dusk/speedrun.h"
#include "dusk/settings.h"
#include "f_op/f_op_overlap_mng.h"
#include "f_op/f_op_scene_mng.h"
#include "m_Do/m_Do_Reset.h"
@@ -21,7 +16,16 @@
#include "m_Do/m_Do_machine.h"
#include "m_Do/m_Do_main.h"
#include "m_Do/m_Do_mtx.h"
#include <dusk/autosave.h>
#ifdef TARGET_PC
#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"
#endif
#if TARGET_PC
#define SHOW_TV_SETTINGS_SCREEN (this->mShowTvSettingsScreen)
@@ -418,12 +422,10 @@ void dScnName_c::changeGameScene() {
dComIfGs_setRestartRoomParam(0);
#if TARGET_PC
if (dusk::getSettings().game.speedrunMode && dusk::getSettings().game.hideTvSettingsScreen) {
// start a new run on file load if a run isn't already in progress
if (!dusk::m_speedrunInfo.m_isRunStarted) {
dusk::resetForSpeedrunMode();
dusk::m_speedrunInfo.startRun();
dusk::speedrun::start();
if (dusk::getSettings().game.hideTvSettingsScreen) {
const dusk::gamemode::Gamemode* gamemode = dusk::gamemode::getGamemodeManager().getCurrentGamemode();
if (gamemode) {
gamemode->mOnSaveLoadedFunction();
}
}
+8
View File
@@ -29,6 +29,7 @@
#if TARGET_PC
#include "dusk/settings.h"
#include "dusk/gamemode.hpp"
#include <f_ap/f_ap_game.h>
#include "helpers/string.hpp"
@@ -1531,6 +1532,13 @@ void dSv_save_c::init() {
mEvent.init();
mMiniGame.init();
#ifdef TARGET_PC
const dusk::gamemode::Gamemode* gamemode = dusk::gamemode::getGamemodeManager().getCurrentGamemode();
if (gamemode) {
gamemode->mOnNewSaveFunction();
}
#endif
}
dSv_memory2_c* dSv_save_c::getSave2(int i_stage2No) {
+6
View File
@@ -509,6 +509,12 @@ BOOL OSJoinThread(OSThread* thread, void** val) {
*(s32*)val = (s32)(intptr_t)thread->val;
}
thread->state = 0;
{
std::lock_guard mapLock(GetThreadDataMutex());
GetThreadDataMap().erase(thread);
}
sActiveThreadCount--;
return 1;
}
return 0;
+93
View File
@@ -0,0 +1,93 @@
#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("vanilla","Vanilla","gczelda2"));
mCurrentGamemodeId = "vanilla";
}
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");
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("vanilla");
}
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->mOnDeactivatedFunction();
}
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->mOnActivatedFunction();
}
}
}; // namespace dusk::gamemode
+69
View File
@@ -0,0 +1,69 @@
#pragma once
#include <functional>
#include <map>
namespace dusk::gamemode {
using GamemodeId = std::string;
// This class holds the definition for the gamemode and various function pointers to call
class Gamemode {
public:
Gamemode(const GamemodeId& id, const std::string& fullName, const std::string& saveName) {
mId = id;
mFullName = fullName;
mSaveName = saveName;
}
const GamemodeId& getId() const { return mId; }
const std::string& getFullName() const { return mFullName; }
const std::string& getSaveName() const { return mSaveName; }
GamemodeId mId;
std::string mFullName;
std::string mSaveName;
std::function<void()> mOnActivatedFunction = gamemodeStub;
std::function<void()> mOnDeactivatedFunction = gamemodeStub;
std::function<void()> mOnPlayFunction = gamemodeStub;
std::function<void()> mOnSaveLoadedFunction = gamemodeStub;
std::function<void()> mOnNewSaveFunction = gamemodeStub;
std::function<void()> mOnGameResetFunction = gamemodeStub;
std::function<void()> mOnTickFunction = gamemodeStub;
private:
static void gamemodeStub() {}
};
class GamemodeManager {
public:
GamemodeManager();
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("vanilla");
}
bool isCurrentGamemode(const GamemodeId& id) const {
const Gamemode* gamemode = getCurrentGamemode();
if (gamemode && gamemode->getId() == id) {
return true;
}
return false;
}
void setCurrentGamemode(const GamemodeId& id);
void setGamemodeToPrevious();
std::map<GamemodeId, Gamemode>& getRegisteredGamemodes() { return mRegisteredGamemodes; }
private:
GamemodeId mCurrentGamemodeId;
std::map<GamemodeId, Gamemode> mRegisteredGamemodes;
};
extern GamemodeManager g_GamemodeManager;
inline GamemodeManager& getGamemodeManager() {
return g_GamemodeManager;
}
}; // namespace dusk::gamemode
+3 -2
View File
@@ -22,6 +22,7 @@
#include "dusk/main.h"
#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 "m_Do/m_Do_controller_pad.h"
@@ -285,7 +286,7 @@ namespace dusk {
if (dusk::IsGameLaunched && !m_isLaunchInitialized) {
m_isLaunchInitialized = true;
if (getSettings().game.speedrunMode && getSettings().game.liveSplitEnabled) {
if (dusk::speedrun::isActive() && getSettings().game.liveSplitEnabled) {
dusk::speedrun::connectLiveSplit();
}
}
@@ -357,7 +358,7 @@ namespace dusk {
m_menuTools.ShowInputViewer();
if (dusk::IsGameLaunched && !dusk::getSettings().game.speedrunMode) {
if (dusk::IsGameLaunched && !dusk::speedrun::isActive()) {
m_menuTools.ShowDebugOverlay();
m_menuTools.ShowCameraOverlay();
m_menuTools.ShowProcessManager();
+3 -2
View File
@@ -14,6 +14,7 @@
#include "d/d_com_inf_game.h"
#include "dusk/data.hpp"
#include "dusk/dusk.h"
#include "dusk/speedrun.h"
#include "dusk/main.h"
#include "dusk/os.h"
#include "m_Do/m_Do_main.h"
@@ -38,7 +39,7 @@ namespace dusk {
ImGui::BeginDisabled();
}
ImGui::BeginDisabled(getSettings().game.speedrunMode);
ImGui::BeginDisabled(dusk::speedrun::isActive());
ImGui::MenuItem("Save Editor", hotkeys::SHOW_SAVE_EDITOR, &m_showSaveEditor);
ImGui::MenuItem("State Share", hotkeys::SHOW_STATE_SHARE, &m_showStateShare);
@@ -60,7 +61,7 @@ namespace dusk {
}
if (ImGui::BeginMenu("Debug")) {
ImGui::BeginDisabled(getSettings().game.speedrunMode);
ImGui::BeginDisabled(dusk::speedrun::isActive());
bool developmentMode = mDoMain::developmentMode == 1;
if (ImGui::Checkbox("Development Mode", &developmentMode)) {
+1
View File
@@ -2,6 +2,7 @@
#define DUSK_ISO_VALIDATE_HPP
#include <atomic>
#include "dusk/settings.h"
namespace dusk::iso {
struct KnownDisc;
+73 -63
View File
@@ -1,46 +1,48 @@
#if _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
using socket_t = SOCKET;
static void closeSocket(socket_t s) {
LINGER li{1, 0};
setsockopt(s, SOL_SOCKET, SO_LINGER, reinterpret_cast<const char*>(&li), sizeof(li));
closesocket(s);
}
static int socketError(socket_t s) {
int err = 0; int len = sizeof(err);
getsockopt(s, SOL_SOCKET, SO_ERROR, reinterpret_cast<char*>(&err), &len);
return err;
}
static constexpr int kSendFlags = 0;
#include <winsock2.h>
#include <ws2tcpip.h>
using socket_t = SOCKET;
static void closeSocket(socket_t s) {
LINGER li{1, 0};
setsockopt(s, SOL_SOCKET, SO_LINGER, reinterpret_cast<const char*>(&li), sizeof(li));
closesocket(s);
}
static int socketError(socket_t s) {
int err = 0;
int len = sizeof(err);
getsockopt(s, SOL_SOCKET, SO_ERROR, reinterpret_cast<char*>(&err), &len);
return err;
}
static constexpr int kSendFlags = 0;
#else
#include <sys/socket.h>
#include <sys/select.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
using socket_t = int;
static void closeSocket(socket_t s) {
struct linger li{1, 0};
setsockopt(s, SOL_SOCKET, SO_LINGER, &li, sizeof(li));
close(s);
}
static int socketError(socket_t s) {
int err = 0; socklen_t len = sizeof(err);
getsockopt(s, SOL_SOCKET, SO_ERROR, &err, &len);
return err;
}
#ifndef INVALID_SOCKET
#define INVALID_SOCKET -1
#endif
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <unistd.h>
using socket_t = int;
static void closeSocket(socket_t s) {
struct linger li{1, 0};
setsockopt(s, SOL_SOCKET, SO_LINGER, &li, sizeof(li));
close(s);
}
static int socketError(socket_t s) {
int err = 0;
socklen_t len = sizeof(err);
getsockopt(s, SOL_SOCKET, SO_ERROR, &err, &len);
return err;
}
#ifndef INVALID_SOCKET
#define INVALID_SOCKET -1
#endif
#if defined(__APPLE__)
static constexpr int kSendFlags = 0;
#else
static constexpr int kSendFlags = MSG_NOSIGNAL;
#endif
#if defined(__APPLE__)
static constexpr int kSendFlags = 0;
#else
static constexpr int kSendFlags = MSG_NOSIGNAL;
#endif
#endif
#include <cstdio>
@@ -49,18 +51,18 @@
namespace dusk::speedrun {
static bool running = false;
static bool startPending = false;
static uint64_t frameCount = 0;
static socket_t sock = INVALID_SOCKET;
static bool wasLoading = false;
static bool connected = false;
static bool connectPending = false;
static bool disconnectPending = false;
static uint32_t idleProbeCounter = 0;
static uint32_t reconnectCounter = 0;
static char storedHost[64] = "127.0.0.1";
static int storedPort = 16834;
static bool running = false;
static bool startPending = false;
static uint64_t frameCount = 0;
static socket_t sock = INVALID_SOCKET;
static bool wasLoading = false;
static bool connected = false;
static bool connectPending = false;
static bool disconnectPending = false;
static uint32_t idleProbeCounter = 0;
static uint32_t reconnectCounter = 0;
static char storedHost[64] = "127.0.0.1";
static int storedPort = 16834;
static void sendCmd(const char* cmd) {
if (sock == INVALID_SOCKET) {
@@ -122,9 +124,11 @@ void onGameFrame() {
}
void start() {
if (running) {
if (g_speedrunInfo.m_isRunStarted || running) {
return;
}
resetForSpeedrunMode();
g_speedrunInfo.startRun();
running = true;
startPending = true;
@@ -214,8 +218,16 @@ void disconnectLiveSplit() {
connected = connectPending = disconnectPending = false;
}
bool consumeConnectedEvent() { bool v = connectPending; connectPending = false; return v; }
bool consumeDisconnectedEvent() { bool v = disconnectPending; disconnectPending = false; return v; }
bool consumeConnectedEvent() {
bool v = connectPending;
connectPending = false;
return v;
}
bool consumeDisconnectedEvent() {
bool v = disconnectPending;
disconnectPending = false;
return v;
}
void updateLiveSplit() {
if (sock == INVALID_SOCKET) {
@@ -267,7 +279,8 @@ void updateLiveSplit() {
#else
|| (r < 0 && errno != EAGAIN && errno != EWOULDBLOCK)
#endif
) {
)
{
if (connected) {
disconnectPending = true;
}
@@ -280,15 +293,12 @@ void updateLiveSplit() {
return;
}
const uint64_t totalMs = frameCount * 1000 / 30;
const uint64_t totalMs = frameCount * 1000 / 30;
const uint64_t totalSec = totalMs / 1000;
char cmd[32];
snprintf(cmd, sizeof(cmd), "setgametime %u:%02u:%02u.%03u",
static_cast<uint32_t>(totalSec / 3600),
static_cast<uint32_t>((totalSec / 60) % 60),
static_cast<uint32_t>(totalSec % 60),
static_cast<uint32_t>(totalMs % 1000)
);
static_cast<uint32_t>(totalSec / 3600), static_cast<uint32_t>((totalSec / 60) % 60),
static_cast<uint32_t>(totalSec % 60), static_cast<uint32_t>(totalMs % 1000));
sendCmd(cmd);
}
@@ -299,4 +309,4 @@ void shutdown() {
#endif
}
}
} // namespace dusk::speedrun
+2
View File
@@ -1,6 +1,8 @@
#pragma once
#include <cstdint>
#include "dusk/gamemode.hpp"
#include "dusk/speedrun.h"
namespace dusk::speedrun {
void onGameFrame();
-3
View File
@@ -1,7 +1,6 @@
#pragma once
#include <filesystem>
namespace dusk {
extern bool IsRunning;
@@ -21,8 +20,6 @@ struct StageRequest {
};
extern StageRequest StageRequested;
#if defined(__ANDROID__) || (defined(TARGET_OS_IOS) && TARGET_OS_IOS) || \
(defined(TARGET_OS_TV) && TARGET_OS_TV)
inline constexpr bool SupportsProcessRestart = false;
+144
View File
@@ -0,0 +1,144 @@
#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) {
return id + "_" + ctx->mod->metadata.id;
}
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;
if (!desc->saveName) {
Log.warn("Attempted to register gamemode {} with a null save name! Defaulting to: (gczelda2)",id);
saveName = "gczelda2";
}else{
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->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) {
dusk::gamemode::getGamemodeManager().unregisterGamemode(get_mod_gamemode_id(ctx,id));
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_gamemode = gamemode_impl::register_gamemode,
.unregister_gamemode = 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,
.modDetached = gamemode_impl::gamemode_remove_mod,
};
} // namespace dusk::mods::svc
+1
View File
@@ -210,6 +210,7 @@ void ModLoader::init_services() {
&svc::g_gameModule,
&svc::g_cameraModule,
&svc::g_gfxModule,
&svc::g_gamemodeModule,
})
{
svc::register_module(*module);
+1
View File
@@ -72,5 +72,6 @@ extern const ServiceModule g_uiModule;
extern const ServiceModule g_gameModule;
extern const ServiceModule g_cameraModule;
extern const ServiceModule g_gfxModule;
extern const ServiceModule g_gamemodeModule;
} // namespace dusk::mods::svc
+3 -1
View File
@@ -154,7 +154,8 @@ UserSettings g_userSettings = {
.recordingMode {"game.recordingMode", false},
.removeQuestMapMarkers {"game.removeQuestMapMarkers", false},
.showInputViewer {"game.showInputViewer", false},
.showInputViewerGyro {"game.showInputViewerGyro", false}
.showInputViewerGyro {"game.showInputViewerGyro", false},
.lastSelectedGamemodeId {"game.lastSelectedGamemodeId", "vanilla"}
},
.backend = {
@@ -302,6 +303,7 @@ void registerSettings() {
Register(g_userSettings.game.removeQuestMapMarkers);
Register(g_userSettings.game.showInputViewer);
Register(g_userSettings.game.showInputViewerGyro);
Register(g_userSettings.game.lastSelectedGamemodeId);
Register(g_userSettings.game.fastSpinner);
Register(g_userSettings.game.infiniteHearts);
Register(g_userSettings.game.infiniteArrows);
+2
View File
@@ -282,6 +282,8 @@ struct UserSettings {
ConfigVar<bool> removeQuestMapMarkers;
ConfigVar<bool> showInputViewer;
ConfigVar<bool> showInputViewerGyro;
ConfigVar<std::string> lastSelectedGamemodeId;
} game;
struct {
+29 -2
View File
@@ -3,10 +3,37 @@
#include "dusk/config.hpp"
#include "m_Do/m_Do_main.h"
#include <aurora/aurora.h>
#include "dusk/gamemode.hpp"
#include "dusk/livesplit.h"
namespace dusk {
namespace dusk::speedrun {
SpeedrunInfo m_speedrunInfo;
SpeedrunInfo g_speedrunInfo;
static void onSpeedrunModeActive() {
resetForSpeedrunMode();
}
static void onSpeedrunModeDeactive() {
restoreFromSpeedrunMode();
if (getSettings().game.liveSplitEnabled) {
speedrun::disconnectLiveSplit();
}
}
void registerSpeedrunGamemode() {
dusk::gamemode::Gamemode speedrunGamemode(DUSK_SPEEDRUN_GAMEMODE_ID,"Speedrun","gczelda2-speedrun");
speedrunGamemode.mOnSaveLoadedFunction = dusk::speedrun::start;
speedrunGamemode.mOnActivatedFunction = onSpeedrunModeActive;
speedrunGamemode.mOnDeactivatedFunction = onSpeedrunModeDeactive;
speedrunGamemode.mOnTickFunction = dusk::speedrun::onGameFrame;
dusk::gamemode::getGamemodeManager().registerGamemode(speedrunGamemode);
}
void unregisterSpeedrunGamemode() {
dusk::gamemode::getGamemodeManager().unregisterGamemode(DUSK_SPEEDRUN_GAMEMODE_ID);
}
void resetForSpeedrunMode() {
mDoMain::developmentMode = -1;
+11 -2
View File
@@ -1,7 +1,10 @@
#pragma once
#include <aurora/aurora.h>
#include "dusk/gamemode.hpp"
namespace dusk {
#define DUSK_SPEEDRUN_GAMEMODE_ID "vanilla_speedrun"
namespace dusk::speedrun {
struct SpeedrunInfo {
void startRun() {
@@ -34,9 +37,15 @@ struct SpeedrunInfo {
OSTime m_igtTimer = 0;
};
extern SpeedrunInfo m_speedrunInfo;
extern SpeedrunInfo g_speedrunInfo;
void registerSpeedrunGamemode();
void unregisterSpeedrunGamemode();
void resetForSpeedrunMode();
void restoreFromSpeedrunMode();
inline bool isActive() {
return dusk::gamemode::getGamemodeManager().isCurrentGamemode(DUSK_SPEEDRUN_GAMEMODE_ID);
}
} // namespace dusk
+6 -3
View File
@@ -8,6 +8,7 @@
#include "achievements.hpp"
#include "aurora/rmlui.hpp"
#include "dusk/livesplit.h"
#include "dusk/gamemode.hpp"
#include "dusk/main.h"
#include "dusk/mods/svc/ui.hpp"
#include "dusk/settings.h"
@@ -20,6 +21,7 @@
#include "mods_window.hpp"
#include "settings.hpp"
#include "ui.hpp"
#include "dusk/ui/prelaunch.hpp"
#include "warp.hpp"
#include "window.hpp"
@@ -93,9 +95,10 @@ MenuBar::MenuBar()
dismiss(modal);
return;
}
prelaunch_state().showPrelaunchOnReset = true;
JUTGamePad::C3ButtonReset::sResetSwitchPushing = true;
dismiss(modal);
hide(false);
Document::hide(true);
},
},
},
@@ -134,11 +137,11 @@ MenuBar::MenuBar()
}));
});
if (getSettings().game.speedrunMode) {
if (dusk::speedrun::isActive()) {
mTabBar->add_tab("Reset Timer", [this] {
mTabBar->set_active_tab(-1);
mDoAud_seStartMenu(kSoundClick);
m_speedrunInfo.reset();
dusk::speedrun::g_speedrunInfo.reset();
if (getSettings().game.liveSplitEnabled) {
dusk::speedrun::reset();
}
+8 -2
View File
@@ -25,6 +25,10 @@ Modal::Modal(Props props) : WindowSmall("modal", "modal-dialog"), mProps(std::mo
auto* actions = append(mDialog, "div");
actions->SetClass("modal-actions", true);
if (props.isVertical) {
actions->SetClass("modal-actions-vertical", true);
}else{
}
for (auto& action : mProps.actions) {
add_action(std::move(action));
@@ -87,9 +91,11 @@ bool Modal::handle_nav_command(Rml::Event& event, NavCommand cmd) {
}
int direction = 0;
if (cmd == NavCommand::Left) {
NavCommand prevCommand = mProps.isVertical ? NavCommand::Up : NavCommand::Left;
NavCommand nextCommand = mProps.isVertical ? NavCommand::Down : NavCommand::Right;
if (cmd == prevCommand) {
direction = -1;
} else if (cmd == NavCommand::Right) {
} else if (cmd == nextCommand) {
direction = 1;
} else {
return false;
+1
View File
@@ -20,6 +20,7 @@ public:
std::function<void(Modal&)> onDismiss;
Rml::String variant;
Rml::String icon = "";
bool isVertical;
};
explicit Modal(Props props);
+13 -13
View File
@@ -289,7 +289,7 @@ void Overlay::update() {
update_pipeline_progress();
#if !(defined(__ANDROID__) || (defined(__APPLE__) && TARGET_OS_IOS && !TARGET_OS_MACCATALYST))
if (getSettings().game.speedrunMode && getSettings().game.liveSplitEnabled) {
if (dusk::speedrun::isActive() && getSettings().game.liveSplitEnabled) {
dusk::speedrun::updateLiveSplit();
if (dusk::speedrun::consumeConnectedEvent()) {
push_toast({.title = "LiveSplit connected", .duration = std::chrono::seconds(3)});
@@ -301,33 +301,33 @@ void Overlay::update() {
#endif
if (mSpeedrunTimer != nullptr && mSpeedrunRta != nullptr && mSpeedrunIgt != nullptr) {
if (getSettings().game.speedrunMode) {
if (dusk::speedrun::isActive()) {
// L+R+A+Start to reset timer
if (mDoCPd_c::getHoldL(PAD_1) && mDoCPd_c::getHoldR(PAD_1) &&
mDoCPd_c::getHoldA(PAD_1) && mDoCPd_c::getTrigZ(PAD_1))
{
m_speedrunInfo.reset();
dusk::speedrun::g_speedrunInfo.reset();
}
// L+R+A+Y to manually stop timer
if (mDoCPd_c::getHoldL(PAD_1) && mDoCPd_c::getHoldR(PAD_1) &&
mDoCPd_c::getHoldA(PAD_1) && mDoCPd_c::getTrigY(PAD_1))
{
if (m_speedrunInfo.m_isRunStarted) {
m_speedrunInfo.m_endTimestamp = OSGetTime() - m_speedrunInfo.m_startTimestamp;
m_speedrunInfo.m_isRunStarted = false;
if (dusk::speedrun::g_speedrunInfo.m_isRunStarted) {
dusk::speedrun::g_speedrunInfo.m_endTimestamp = OSGetTime() - dusk::speedrun::g_speedrunInfo.m_startTimestamp;
dusk::speedrun::g_speedrunInfo.m_isRunStarted = false;
}
}
OSTime elapsedTime = 0;
if (m_speedrunInfo.m_isRunStarted) {
elapsedTime = OSGetTime() - m_speedrunInfo.m_startTimestamp;
} else if (m_speedrunInfo.m_endTimestamp != 0) {
elapsedTime = m_speedrunInfo.m_endTimestamp;
if (dusk::speedrun::g_speedrunInfo.m_isRunStarted) {
elapsedTime = OSGetTime() - dusk::speedrun::g_speedrunInfo.m_startTimestamp;
} else if (dusk::speedrun::g_speedrunInfo.m_endTimestamp != 0) {
elapsedTime = dusk::speedrun::g_speedrunInfo.m_endTimestamp;
}
if (!m_speedrunInfo.m_isPauseIGT) {
m_speedrunInfo.m_igtTimer = elapsedTime - m_speedrunInfo.m_totalLoadTime;
if (!dusk::speedrun::g_speedrunInfo.m_isPauseIGT) {
dusk::speedrun::g_speedrunInfo.m_igtTimer = elapsedTime - dusk::speedrun::g_speedrunInfo.m_totalLoadTime;
}
mSpeedrunTimer->SetAttribute("open", "");
@@ -340,7 +340,7 @@ void Overlay::update() {
}
mSpeedrunIgt->SetInnerRML(
escape(fmt::format("IGT {}", FormatTime(m_speedrunInfo.m_igtTimer))));
escape(fmt::format("IGT {}", FormatTime(dusk::speedrun::g_speedrunInfo.m_igtTimer))));
} else {
mSpeedrunTimer->RemoveAttribute("open");
}
+155 -53
View File
@@ -3,9 +3,11 @@
#include "dusk/config.hpp"
#include "dusk/data.hpp"
#include "dusk/file_select.hpp"
#include "dusk/gamemode.hpp"
#include "dusk/iso_validate.hpp"
#include "dusk/main.h"
#include "dusk/settings.h"
#include "dusk/ui/menu_bar.hpp"
#include "dusk/update_check.hpp"
#include "modal.hpp"
#include "mods_window.hpp"
@@ -685,61 +687,42 @@ void try_apply_mirrored_layout(Rml::Element* body) {
body->SetClass("mirrored", getSettings().game.enableMirrorMode.getValue());
}
Prelaunch::Prelaunch()
: Document(kDocumentSource, false, DocumentScope::Prelaunch),
mRoot(mDocument->GetElementById("root")) {
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;
}
}
}
static std::string getPlayButtonText() {
auto& state = prelaunch_state();
const bool activeDiscLoaded = !state.activeDiscPath.empty();
if (activeDiscLoaded == false) {
return "Select Disc Image";
}
std::string playText;
const dusk::gamemode::Gamemode* currentGameMode =
dusk::gamemode::getGamemodeManager().getCurrentGamemode();
if (currentGameMode != nullptr) {
return currentGameMode->getId() == "vanilla" ? "Play" :
"Play " + currentGameMode->getFullName();
}
return "Play";
}
Prelaunch::Prelaunch() : Document(kDocumentSource, false, DocumentScope::Prelaunch) {
mRoot = mDocument->GetElementById("root");
ensure_initialized();
begin_update_check();
if (auto* menuList = mDocument->GetElementById("menu-list")) {
auto& state = prelaunch_state();
const bool activeDiscLoaded = !state.activeDiscPath.empty();
mMenuButtons.push_back(
std::make_unique<Button>(menuList, activeDiscLoaded ? "Play" : "Select Disc Image"));
mMenuButtons.back()->on_pressed([this] {
if (prelaunch_state().activeDiscPath.empty()) {
open_iso_picker();
return;
}
mDoAud_seStartMenu(kSoundPlay);
show_menu_notification();
if (getSettings().audio.menuSounds) {
JAISoundHandle* handle = g_mEnvSeMgr.field_0x144.getHandle();
if (*handle) {
(*handle)->stop(60);
(*handle)->releaseHandle();
}
}
if (g_mDoMemCd_control.mCardCommand == mDoMemCd_Ctrl_c::Command_e::COMM_NONE_e) {
mDoMemCd_ThdInit();
}
IsGameLaunched = true;
pop();
});
apply_intro_animation(mMenuButtons.back()->root(), "delay-1");
mMenuButtons.push_back(std::make_unique<Button>(menuList, "Settings"));
mMenuButtons.back()->on_pressed([this] {
mRestartSuppressed = false;
push(std::make_unique<SettingsWindow>(true));
});
apply_intro_animation(mMenuButtons.back()->root(), "delay-2");
mMenuButtons.push_back(std::make_unique<Button>(menuList, "Mods"));
mMenuButtons.back()->on_pressed([this] {
mRestartSuppressed = false;
push(std::make_unique<ModsWindow>());
});
apply_intro_animation(mMenuButtons.back()->root(), "delay-3");
mMenuButtons.push_back(std::make_unique<Button>(menuList, "Quit"));
mMenuButtons.back()->on_pressed([] { IsRunning = false; });
apply_intro_animation(mMenuButtons.back()->root(), "delay-4");
}
build_menu_buttons();
mDiscStatus = mDocument->GetElementById("disc-status");
mDiscDetail = mDocument->GetElementById("disc-version");
@@ -777,6 +760,125 @@ Prelaunch::Prelaunch()
});
}
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();
mMenuButtons.push_back(std::make_unique<Button>(menuList, getPlayButtonText()));
mMenuButtons.back()->on_pressed([this] {
if (prelaunch_state().activeDiscPath.empty()) {
open_iso_picker();
return;
}
mDoAud_seStartMenu(kSoundPlay);
show_menu_notification();
if (getSettings().audio.menuSounds) {
JAISoundHandle* handle = g_mEnvSeMgr.field_0x144.getHandle();
if (*handle) {
(*handle)->stop(60);
(*handle)->releaseHandle();
}
}
if (g_mDoMemCd_control.mCardCommand == mDoMemCd_Ctrl_c::Command_e::COMM_NONE_e) {
mDoMemCd_ThdInit();
}
prelaunch_state().firstLaunch = false;
const dusk::gamemode::Gamemode* gamemode =
dusk::gamemode::getGamemodeManager().getCurrentGamemode();
if (gamemode) {
gamemode->mOnPlayFunction();
}
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);
}
});
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"));
mMenuButtons.back()->on_pressed([this] {
std::vector<ModalAction> gamemodeActions;
gamemodeActions.push_back(dusk::ui::ModalAction{
.label = "Vanilla", .onPressed = [this](dusk::ui::Modal& modal) {
mDoAud_seStartMenu(kSoundClick);
dusk::gamemode::getGamemodeManager().setCurrentGamemode("vanilla");
modal.pop();
update();
}});
for (const auto& [id, gamemode] :
dusk::gamemode::getGamemodeManager().getRegisteredGamemodes())
{
if (id == "vanilla") {
// Force vanilla to the top
continue;
}
gamemodeActions.push_back(dusk::ui::ModalAction{.label = gamemode.getFullName(),
.onPressed = [this, id](dusk::ui::Modal& modal) {
mDoAud_seStartMenu(kSoundClick);
dusk::gamemode::getGamemodeManager().setCurrentGamemode(id);
modal.pop();
update();
}});
}
mRestartSuppressed = false;
push(std::make_unique<dusk::ui::Modal>(dusk::ui::Modal::Props{
.title = "Play Type",
.bodyRml = "What mode would you like to play?",
.actions = gamemodeActions,
.onDismiss =
[this](dusk::ui::Modal& modal) {
mDoAud_seStartMenu(kSoundWindowClose);
modal.pop();
},
.icon = "question-mark",
.isVertical = true,
}));
});
apply_intro_animation(mMenuButtons.back()->root(), "delay-1");
}
mMenuButtons.push_back(std::make_unique<Button>(menuList, "Settings"));
mMenuButtons.back()->on_pressed([this] {
mRestartSuppressed = false;
bool showPrelaunchSettings = prelaunch_state().firstLaunch;
push(std::make_unique<SettingsWindow>(showPrelaunchSettings));
});
apply_intro_animation(mMenuButtons.back()->root(), "delay-2");
mMenuButtons.push_back(std::make_unique<Button>(menuList, "Mods"));
mMenuButtons.back()->on_pressed([this] {
mRestartSuppressed = false;
push(std::make_unique<ModsWindow>());
});
apply_intro_animation(mMenuButtons.back()->root(), "delay-3");
mMenuButtons.push_back(std::make_unique<Button>(menuList, "Quit"));
mMenuButtons.back()->on_pressed([] { IsRunning = false; });
apply_intro_animation(mMenuButtons.back()->root(), "delay-4");
}
}
void Prelaunch::show() {
Document::show();
mDocument->SetAttribute("open", "");
@@ -856,7 +958,7 @@ void Prelaunch::update() {
}
if (!mMenuButtons.empty()) {
mMenuButtons[0]->set_text(activeDiscLoaded ? "Play" : "Select Disc Image");
mMenuButtons[0]->set_text(getPlayButtonText());
}
const auto discStatusLabel = mDiscStatus->GetElementById("disc-status-label");
+7 -1
View File
@@ -3,6 +3,7 @@
#include "button.hpp"
#include "document.hpp"
#include "dusk/iso_validate.hpp"
#include "dusk/settings.h"
#include <memory>
#include <string>
@@ -13,6 +14,7 @@ namespace dusk::ui {
class Prelaunch : public Document {
public:
Prelaunch();
void build_menu_buttons();
void show() override;
void hide(bool close) override;
@@ -21,13 +23,15 @@ public:
bool visible() const override;
bool obscures_game() const override { return true; }
static void rebuild_menu_buttons();
protected:
bool handle_nav_command(Rml::Event& event, NavCommand cmd) override;
private:
bool mEntranceAnimationStarted = false;
bool mRestartSuppressed = false;
std::vector<std::unique_ptr<Button> > mMenuButtons;
std::vector<std::unique_ptr<Button>> mMenuButtons;
Rml::Element* mRoot = nullptr;
Rml::Element* mDiscStatus = nullptr;
Rml::Element* mDiscDetail = nullptr;
@@ -42,6 +46,7 @@ class PrelaunchOptions;
struct PrelaunchState {
bool initialized = false;
bool firstLaunch = true;
std::string configuredDiscPath;
bool configuredDiscCanLaunch = false;
iso::DiscInfo configuredDiscInfo{};
@@ -55,6 +60,7 @@ struct PrelaunchState {
std::string pendingDiscPath;
iso::DiscInfo pendingDiscInfo{};
iso::ValidationError pendingDiscValidation = iso::ValidationError::Unknown;
bool showPrelaunchOnReset = false;
};
PrelaunchState& prelaunch_state() noexcept;
+13 -13
View File
@@ -375,7 +375,7 @@ void add_speedrun_disabled_option(Pane& leftPane, Pane& rightPane, ConfigVar<boo
config_bool_select(leftPane, rightPane, var, {
.key = key,
.helpText = helpText,
.isDisabled = [] { return getSettings().game.speedrunMode.getValue(); },
.isDisabled = [] { return dusk::speedrun::isActive(); },
});
}
@@ -692,7 +692,7 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
{
.key = "Pause on Focus Lost",
.helpText = "Pause the game when window focus is lost.",
.isDisabled = [] { return IsMobile || getSettings().game.speedrunMode; },
.isDisabled = [] { return IsMobile || dusk::speedrun::isActive(); },
});
leftPane.register_control(
leftPane.add_select_button({
@@ -1053,7 +1053,7 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
leftPane.add_section("Tools");
addOption("Turbo Key", getSettings().game.enableTurboKeybind,
"Hold Tab to increase game speed by up to 4x.",
[] { return getSettings().game.speedrunMode.getValue(); });
[] { return dusk::speedrun::isActive(); });
addOption("Reset Key (" + Rml::String{hotkeys::DO_RESET} + ")",
getSettings().game.enableResetKeybind,
"Press " + Rml::String{hotkeys::DO_RESET} + " to reset the game.");
@@ -1166,7 +1166,7 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
getSettings().game.damageMultiplier.setValue(value);
config::save();
},
.isDisabled = [] { return getSettings().game.speedrunMode.getValue(); },
.isDisabled = [] { return dusk::speedrun::isActive(); },
.isModified =
[] {
return getSettings().game.damageMultiplier.getValue() !=
@@ -1225,14 +1225,14 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
.helpText =
"Enables speedrunning options while restricting certain gameplay modifiers.",
.onChange =
[](bool enabled) {
[this](bool enabled) {
if (enabled) {
resetForSpeedrunMode();
dusk::speedrun::registerSpeedrunGamemode();
} else {
restoreFromSpeedrunMode();
if (getSettings().game.liveSplitEnabled) {
speedrun::disconnectLiveSplit();
if (dusk::speedrun::isActive()) {
pop();
}
dusk::speedrun::unregisterSpeedrunGamemode();
}
MenuBar::rebuild();
},
@@ -1250,13 +1250,13 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
speedrun::disconnectLiveSplit();
}
},
.isDisabled = [] { return IsMobile || !getSettings().game.speedrunMode; },
.isDisabled = [] { return IsMobile || !dusk::speedrun::isActive(); },
});
config_bool_select(leftPane, rightPane, getSettings().game.showSpeedrunRTATimer,
{
.key = "Show RTA",
.helpText = "Display the RTA timer. IGT is always visible.",
.isDisabled = [] { return !getSettings().game.speedrunMode; },
.isDisabled = [] { return !dusk::speedrun::isActive(); },
});
});
@@ -1305,7 +1305,7 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
[] {
return kMagicArmorModes[static_cast<u8>(getSettings().game.armorRupeeDrain.getValue())];
},
.isDisabled = [] { return getSettings().game.speedrunMode.getValue(); },
.isDisabled = [] { return dusk::speedrun::isActive(); },
.isModified =
[] {
return getSettings().game.armorRupeeDrain.getValue() !=
@@ -1469,7 +1469,7 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
"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(); },
.isDisabled = [] { return getSettings().game.speedrunMode.getValue(); },
.isDisabled = [] { return dusk::speedrun::isActive(); },
});
config_bool_select(leftPane, rightPane, getSettings().game.showInputViewer,
{
+2
View File
@@ -10,6 +10,8 @@
#include "nav_types.hpp"
#include "Z2AudioLib/Z2SeMgr.h"
namespace dusk::ui {
class Document;
+5 -1
View File
@@ -31,6 +31,7 @@
#include <dusk/gamepad_color.h>
#include <dusk/autosave.h>
#include "dusk/menu_pointer.h"
#include "dusk/gamemode.hpp"
#endif
fapGm_HIO_c::fapGm_HIO_c() {
@@ -845,7 +846,10 @@ void fapGm_Execute() {
cCt_Counter(0);
#ifdef TARGET_PC
dusk::speedrun::onGameFrame();
const dusk::gamemode::Gamemode* gamemode = dusk::gamemode::getGamemodeManager().getCurrentGamemode();
if (gamemode) {
gamemode->mOnTickFunction();
}
dusk::AchievementSystem::get().tick();
dusk::menu_pointer::end_game_frame();
#endif
+11 -8
View File
@@ -7,7 +7,10 @@
#include "f_op/f_op_overlap_req.h"
#include "f_pc/f_pc_manager.h"
#ifdef TARGET_PC
#include "dusk/speedrun.h"
#include "dusk/gamemode.hpp"
#endif
void fopOvlpReq_SetPeektime(overlap_request_class*, u16);
@@ -20,11 +23,11 @@ static int fopOvlpReq_phase_Done(overlap_request_class* i_overlapReq) {
i_overlapReq->field_0xc = 0;
#if TARGET_PC
if (dusk::getSettings().game.speedrunMode) {
if (dusk::m_speedrunInfo.m_isRunStarted) {
dusk::m_speedrunInfo.m_isPauseIGT = false;
dusk::m_speedrunInfo.m_totalLoadTime += OSGetTime() - dusk::m_speedrunInfo.m_loadStartTimestamp;
dusk::m_speedrunInfo.m_loadStartTimestamp = OSGetTime();
if (dusk::speedrun::isActive()) {
if (dusk::speedrun::g_speedrunInfo.m_isRunStarted) {
dusk::speedrun::g_speedrunInfo.m_isPauseIGT = false;
dusk::speedrun::g_speedrunInfo.m_totalLoadTime += OSGetTime() - dusk::speedrun::g_speedrunInfo.m_loadStartTimestamp;
dusk::speedrun::g_speedrunInfo.m_loadStartTimestamp = OSGetTime();
}
}
#endif
@@ -95,9 +98,9 @@ static int fopOvlpReq_phase_Create(overlap_request_class* i_overlapReq) {
fpcM_Create(i_overlapReq->procname, NULL, NULL);
#if TARGET_PC
if (dusk::m_speedrunInfo.m_isRunStarted) {
dusk::m_speedrunInfo.m_isPauseIGT = true;
dusk::m_speedrunInfo.m_loadStartTimestamp = OSGetTime();
if (dusk::speedrun::isActive() && dusk::speedrun::g_speedrunInfo.m_isRunStarted) {
dusk::speedrun::g_speedrunInfo.m_isPauseIGT = true;
dusk::speedrun::g_speedrunInfo.m_loadStartTimestamp = OSGetTime();
}
#endif
+104 -48
View File
@@ -3,35 +3,35 @@
* Memory Card Control
*/
#include <card.h>
#include "m_Do/m_Do_MemCard.h"
#include <card.h>
#include "JSystem/JKernel/JKRAssertHeap.h"
#include "m_Do/m_Do_ext.h"
#include "dusk/main.h"
#include "dusk/os.h"
#include "dusk/version.hpp"
#include "m_Do/m_Do_MemCardRWmng.h"
#include "m_Do/m_Do_Reset.h"
#include "m_Do/m_Do_ext.h"
#include "os_report.h"
#include "dusk/os.h"
#include "dusk/main.h"
#include "dusk/version.hpp"
#if PLATFORM_WII || PLATFORM_SHIELD
#include <cstring>
#include <revolution/nand.h>
#include <revolution/sc.h>
#include <cstring>
#endif
#define SLOT_A 0
#define CHECKSPACE_RESULT_READY 0
#define CHECKSPACE_RESULT_READY 0
#define CHECKSPACE_RESULT_INSSPACE 1
#define CHECKSPACE_RESULT_NOENT 2
#define CHECKSPACE_RESULT_ERROR 3
#define CHECKSPACE_RESULT_NOENT 2
#define CHECKSPACE_RESULT_ERROR 3
#if PLATFORM_WII
s32 my_CARDOpen(s32 chan, const char* fileName, CARDFileInfo* fileInfo) {
CARDStat stat;
DVDDiskID* diskID = DVDGetCurrentDiskID();
for (int i = 0; i < CARD_MAX_FILE; i++) {
s32 ret = CARDGetStatus(chan, i, &stat);
if (ret == CARD_RESULT_READY) {
@@ -57,10 +57,10 @@ s32 my_CARDOpen(s32 chan, const char* fileName, CARDFileInfo* fileInfo) {
#endif
#if PLATFORM_WII
#define NAND_OPEN NANDSafeOpen
#define NAND_OPEN NANDSafeOpen
#define NAND_CLOSE NANDSafeClose
#elif PLATFORM_SHIELD
#define NAND_OPEN NANDSimpleSafeOpen
#define NAND_OPEN NANDSimpleSafeOpen
#define NAND_CLOSE NANDSimpleSafeClose
#endif
@@ -77,15 +77,20 @@ static u8 MemCardStack[STACK_SIZE];
static OSThread MemCardThread;
void mDoMemCd_Ctrl_c::ThdInit() {
#if !PLATFORM_SHIELD
#ifdef TARGET_PC
if (mInitialized) {
return;
}
CARDSetLoadType((CARDFileType)dusk::getSettings().backend.cardFileType.getValue());
#endif
#if !PLATFORM_SHIELD
char version[5] = {};
char maker[3] = {};
std::memcpy(version, dusk::version::getDiskID().gameName, 4);
std::memcpy(maker, dusk::version::getDiskID().company, 2);
CARDInit(version, maker);
#endif
#endif
mCopyToPos = 0;
mProbeStat = 2;
@@ -93,6 +98,7 @@ void mDoMemCd_Ctrl_c::ThdInit() {
#if TARGET_PC
mCardCommand = COMM_ATTACH_e;
mInitialized = true;
#else
mCardCommand = COMM_NONE_e;
#endif
@@ -101,8 +107,9 @@ void mDoMemCd_Ctrl_c::ThdInit() {
OSInitMutex(&mMutex);
OSInitCond(&mCond);
OSCreateThread(&MemCardThread, (void*(*)(void*))mDoMemCd_main, NULL, MemCardStack + sizeof(MemCardStack),
sizeof(MemCardStack), OSGetThreadPriority(OSGetCurrentThread()) + 1, 1);
OSCreateThread(&MemCardThread, (void* (*)(void*))mDoMemCd_main, NULL,
MemCardStack + sizeof(MemCardStack), sizeof(MemCardStack),
OSGetThreadPriority(OSGetCurrentThread()) + 1, 1);
OSResumeThread(&MemCardThread);
// "Memory Card Thread Init\n"
@@ -112,23 +119,26 @@ void mDoMemCd_Ctrl_c::ThdInit() {
void mDoMemCd_Ctrl_c::main() {
do {
OSLockMutex(&mMutex);
while (mCardCommand == COMM_NONE_e
#ifdef TARGET_PC
&& !dusk::IsShuttingDown
#endif
) {
bool shutdownThread = dusk::IsShuttingDown;
while (mCardCommand == COMM_NONE_e && !shutdownThread) {
OSWaitCond(&mCond, &mMutex);
shutdownThread = dusk::IsShuttingDown;
}
OSUnlockMutex(&mMutex);
#ifdef TARGET_PC
if (dusk::IsShuttingDown) {
if (shutdownThread) {
break;
}
#else
while (mCardCommand == COMM_NONE_e) {
OSWaitCond(&mCond, &mMutex);
}
OSUnlockMutex(&mMutex);
#endif
switch (mCardCommand) {
#if PLATFORM_GCN || PLATFORM_WII
#if PLATFORM_GCN || PLATFORM_WII
case COMM_RESTORE_e:
restore();
break;
@@ -144,16 +154,16 @@ void mDoMemCd_Ctrl_c::main() {
case COMM_DETACH_e:
detach();
break;
#elif PLATFORM_SHIELD
#elif PLATFORM_SHIELD
case COMM_RESTORE_e:
case COMM_STORE_e:
case COMM_FORMAT_e:
case COMM_ATTACH_e:
case COMM_DETACH_e:
break;
#endif
#endif
#if PLATFORM_WII || PLATFORM_SHIELD
#if PLATFORM_WII || PLATFORM_SHIELD
case COMM_RESTORE_NAND_e:
restoreNAND();
break;
@@ -163,7 +173,7 @@ void mDoMemCd_Ctrl_c::main() {
case COMM_STORE_SETUP_NAND_e:
storeSetUpNAND();
break;
#endif
#endif
}
OSLockMutex(&mMutex);
@@ -180,7 +190,7 @@ void mDoMemCd_Ctrl_c::update() {
OSUnlockMutex(&mMutex);
OSSignalCond(&mCond);
} else if (getStatus(0) != 14) {
#if PLATFORM_GCN || PLATFORM_WII
#if PLATFORM_GCN || PLATFORM_WII
if (CARDProbe(SLOT_A) && getStatus(0) == 0) {
OSLockMutex(&mMutex);
mProbeStat = 0;
@@ -196,7 +206,7 @@ void mDoMemCd_Ctrl_c::update() {
OSUnlockMutex(&mMutex);
OSSignalCond(&mCond);
}
#endif
#endif
}
}
@@ -214,7 +224,12 @@ void mDoMemCd_Ctrl_c::restore() {
CARDFileInfo file;
field_0x1fc8 = 0;
s32 ret = CARD_OPEN(mChannel, "gczelda2", &file);
#ifdef TARGET_PC
const char* fileName = getFileName();
#else
const char* fileName = "gczelda2";
#endif
s32 ret = CARD_OPEN(mChannel, fileName, &file);
OS_REPORT("\x1b[43;30mCret=%d\n\x1b[m", ret);
if (ret == CARD_RESULT_READY) {
s32 ret2 = mDoMemCdRWm_Restore(&file, this, sizeof(mData));
@@ -271,12 +286,18 @@ void mDoMemCd_Ctrl_c::store() {
s32 ret;
field_0x1fc8 = 0;
#ifdef TARGET_PC
const char* fileName = getFileName();
#else
const char* fileName = "gczelda2";
#endif
if (mCardState == CARD_STATE_NO_FILE_e) {
#if PLATFORM_GCN
ret = CARDCreate(mChannel, "gczelda2", CARD_FILE_SIZE, &file);
#else
#if PLATFORM_GCN
ret = CARDCreate(mChannel, fileName, CARD_FILE_SIZE, &file);
#else
ret = CARDCreate(mChannel, "zeldaTp.dat", CARD_FILE_SIZE, &file);
#endif
#endif
if (ret == CARD_RESULT_READY || ret == CARD_RESULT_EXIST) {
mCardState = CARD_STATE_READY_e;
} else {
@@ -285,7 +306,7 @@ void mDoMemCd_Ctrl_c::store() {
}
if (mCardState == CARD_STATE_READY_e) {
ret = CARD_OPEN(mChannel, "gczelda2", &file);
ret = CARD_OPEN(mChannel, fileName, &file);
if (ret == CARD_RESULT_READY) {
ret = mDoMemCdRWm_Store(&file, this, sizeof(mData));
if (ret != CARD_RESULT_READY) {
@@ -527,7 +548,13 @@ s32 mDoMemCd_Ctrl_c::mount() {
s32 mDoMemCd_Ctrl_c::loadfile() {
CARDFileInfo file;
s32 ret = CARD_OPEN(mChannel, "gczelda2", &file);
#ifdef TARGET_PC
const char* fileName = getFileName();
#else
const char* fileName = "gczelda2";
#endif
s32 ret = CARD_OPEN(mChannel, fileName, &file);
if (ret == CARD_RESULT_READY) {
CARDClose(&file);
return TRUE;
@@ -543,7 +570,7 @@ s32 mDoMemCd_Ctrl_c::checkspace() {
if (result != CARD_RESULT_READY) {
setCardState(result);
return CHECKSPACE_RESULT_ERROR;
return CHECKSPACE_RESULT_ERROR;
}
if (bytesNotUsed < CARD_FILE_SIZE) {
@@ -553,7 +580,7 @@ s32 mDoMemCd_Ctrl_c::checkspace() {
if (filesNotUsed < 1) {
return CHECKSPACE_RESULT_NOENT;
}
return CHECKSPACE_RESULT_READY;
}
@@ -596,7 +623,7 @@ void mDoMemCd_Ctrl_c::restoreNAND() {
NANDFileInfo file;
s32 ret, ret2;
field_0x1fc8 = 0;
field_0x1fc8 = 0;
ret = NANDOpen("zeldaTp.dat", &file, NAND_ACCESS_RW);
OS_REPORT("\x1b[43;30mCret=%d\n\x1b[m", ret);
@@ -668,7 +695,8 @@ void mDoMemCd_Ctrl_c::storeNAND() {
ret = NANDCreate("banner.bin", NAND_PERM_RUSR | NAND_PERM_WUSR | NAND_PERM_RGRP, 0);
printf("NAND bannerFile Create ret:%d\n", ret);
if (ret == NAND_RESULT_OK || ret == NAND_RESULT_EXISTS) {
ret = NAND_OPEN("banner.bin", &file, NAND_ACCESS_RW, l_safeCopyBuf, sizeof(l_safeCopyBuf));
ret = NAND_OPEN(
"banner.bin", &file, NAND_ACCESS_RW, l_safeCopyBuf, sizeof(l_safeCopyBuf));
if (ret == NAND_RESULT_OK) {
ret = mDoMemCdRWm_StoreBannerNAND(&file);
if (ret == NAND_RESULT_OK) {
@@ -686,7 +714,8 @@ void mDoMemCd_Ctrl_c::storeNAND() {
}
if (ret == NAND_RESULT_OK) {
ret = NAND_OPEN("zeldaTp.dat", &file, NAND_ACCESS_RW, l_safeCopyBuf, sizeof(l_safeCopyBuf));
ret = NAND_OPEN(
"zeldaTp.dat", &file, NAND_ACCESS_RW, l_safeCopyBuf, sizeof(l_safeCopyBuf));
if (ret == NAND_RESULT_OK) {
ret = mDoMemCdRWm_StoreNAND(&file, this, sizeof(mData));
if (ret == NAND_RESULT_OK) {
@@ -732,10 +761,11 @@ s32 mDoMemCd_Ctrl_c::SaveSyncNAND() {
void mDoMemCd_Ctrl_c::storeSetUpNAND() {
field_0x1fc8 = 0;
while ((int)SCCheckStatus() != 0) {}
#if PLATFORM_WII
while ((int)SCCheckStatus() != 0) {
}
#if PLATFORM_WII
if (!SCFlush()) {
mNandState = NAND_STATE_WRITE_e;
printf("== 本体設定Write OK ==\n");
@@ -743,9 +773,9 @@ void mDoMemCd_Ctrl_c::storeSetUpNAND() {
mNandState = NAND_STATE_FATAL_ERROR_e;
printf("== 本体設定Write ERR ==\n");
}
#else
#else
mNandState = NAND_STATE_WRITE_e;
#endif
#endif
field_0x1fc8 = 1;
}
@@ -866,7 +896,7 @@ s32 mDoMemCd_Ctrl_c::checkspaceNAND() {
s32 result = NANDCheck(3, 2, &answer);
if (result != NAND_RESULT_OK) {
setNandState(result);
return CHECKSPACE_RESULT_ERROR;
return CHECKSPACE_RESULT_ERROR;
}
if (answer == 0) {
@@ -876,11 +906,37 @@ s32 mDoMemCd_Ctrl_c::checkspaceNAND() {
} else if (answer & 10) {
ret = 2;
}
return ret;
}
#endif
#ifdef TARGET_PC
void mDoMemCd_Ctrl_c::setFileName(const std::string& fileName) {
if (mInitialized == false) {
mFileName = fileName;
} else {
OSLockMutex(&mMutex);
mFileName = fileName;
OSUnlockMutex(&mMutex);
}
}
const char* mDoMemCd_Ctrl_c::getFileName() {
const char* fileName = "gczelda2";
if (mInitialized) {
OSLockMutex(&mMutex);
}
if (!mFileName.empty()) {
fileName = mFileName.c_str();
}
if (mInitialized) {
OSUnlockMutex(&mMutex);
}
return fileName;
}
#endif
DUSK_GAME_DATA mDoMemCd_Ctrl_c g_mDoMemCd_control;
static int mDoMemCd_main(void*) {
+41 -4
View File
@@ -4,21 +4,27 @@
*/
#include "m_Do/m_Do_Reset.h"
#include <gx.h>
#include "JSystem/JAudio2/JASDvdThread.h"
#include "JSystem/JUtility/JUTGamePad.h"
#include "JSystem/JUtility/JUTXfb.h"
#include "SSystem/SComponent/c_API_controller_pad.h"
#include <gx.h>
#include "m_Do/m_Do_audio.h"
#include "m_Do/m_Do_DVDError.h"
#include "m_Do/m_Do_ext.h"
#include "m_Do/m_Do_MemCard.h"
#include "m_Do/m_Do_audio.h"
#include "m_Do/m_Do_ext.h"
#if !PLATFORM_GCN
#include <revolution/os.h>
#endif
#include "os_report.h"
#ifdef TARGET_PC
#include "dusk/ui/prelaunch.hpp"
#include "dusk/ui/menu_bar.hpp"
#include "dusk/gamemode.hpp"
#endif
static void my_OSCancelAlarmAll() {}
static void destroyVideo() {
@@ -104,6 +110,13 @@ 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->mOnGameResetFunction();
}
#endif
if (mDoRst::isReset()) {
return;
}
@@ -129,7 +142,8 @@ void mDoRst_resetCallBack(int port, void*) {
#else
DVDCommandBlock block;
block.userData = (void*)-1;
while (DVDCheckDiskAsync(&block, checkDiskCallback));
while (DVDCheckDiskAsync(&block, checkDiskCallback))
;
do {
check = (int)block.userData;
} while (check == -1);
@@ -141,6 +155,29 @@ 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 || dusk::gamemode::getGamemodeManager().getRegisteredGamemodes().size() == 1) {
return;
}
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* 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
}
void mDoRst_shutdownCallBack() {
+11 -6
View File
@@ -642,12 +642,6 @@ int game_main(int argc, char* argv[]) {
auroraInfo = aurora_initialize(argc, argv, &config);
}
// Apply after aurora_initialize: speedrun mode mutates cvars whose change callbacks push
// values into aurora.
if (dusk::getSettings().game.speedrunMode) {
dusk::resetForSpeedrunMode();
}
#ifdef DUSK_DISCORD
if (dusk::getSettings().game.enableDiscordPresence) {
dusk::discord::initialize();
@@ -889,6 +883,17 @@ int game_main(int argc, char* argv[]) {
DuskLog.info("Initializing mods...");
dusk::mods::ModLoader::instance().init();
// Apply after aurora_initialize: speedrun mode mutates cvars whose change callbacks push
// values into aurora.
if (dusk::getSettings().game.speedrunMode) {
dusk::speedrun::registerSpeedrunGamemode();
}
if (skipPreLaunchUI == true && dusk::gamemode::getGamemodeManager().getRegisteredGamemodes().size() > 1 && dusk::getSettings().backend.skipPreLaunchUI.getValue()) {
// Force pre-launch if we have registered gamemodes that we need to choose from
dusk::ui::push_document(std::make_unique<dusk::ui::Prelaunch>(), true);
}
OSReport("Starting main01 (Game Loop)...\n");
main01();