diff --git a/CMakeLists.txt b/CMakeLists.txt index f6593655..a9210ef0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -105,6 +105,10 @@ file(GLOB_RECURSE ALL_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/port/*.h" "src/port/*.c" "src/port/*.cpp" + "src/port/Enhancements/*.h" + "src/port/Enhancements/*.cpp" + "src/port/Enhancements/game-interactor/*.h" + "src/port/Enhancements/game-interactor/*.cpp" "src/port/importer/*.cpp" "src/port/importer/types/*.cpp" "text/*.c" diff --git a/src/game/level_update.c b/src/game/level_update.c index 19cea1ae..9cdcc24b 100644 --- a/src/game/level_update.c +++ b/src/game/level_update.c @@ -29,6 +29,8 @@ #include "course_table.h" #include "rumble_init.h" +#include "port/Enhancements/game-interactor/GameInteractor_Hooks.h" + #define PLAY_MODE_NORMAL 0 #define PLAY_MODE_PAUSED 2 #define PLAY_MODE_CHANGE_AREA 3 @@ -1153,6 +1155,9 @@ s32 update_level(void) { enable_background_sound(); } + // Updates only when in game, i.e. past the save select screen + GameInteractor_ExecuteOnGameFrameUpdate(); + return changeLevel; } diff --git a/src/game/mario.c b/src/game/mario.c index 7334741e..8224f5e0 100644 --- a/src/game/mario.c +++ b/src/game/mario.c @@ -35,6 +35,8 @@ #include "rumble_init.h" #include "animation_table.h" +#include "port/Enhancements/game-interactor/GameInteractor_Hooks.h" + u32 unused80339F10; u8 unused80339F1C[20]; @@ -1455,6 +1457,7 @@ void update_mario_health(struct MarioState *m) { if ((m->input & INPUT_IN_POISON_GAS) && !(m->action & ACT_FLAG_INTANGIBLE)) { if (!(m->flags & MARIO_METAL_CAP) && !gDebugLevelSelect) { m->health -= 4; + GameInteractor_ExecuteOnHealthChange(m->health); } } else { if ((m->action & ACT_FLAG_SWIMMING) && !(m->action & ACT_FLAG_INTANGIBLE)) { @@ -1465,8 +1468,10 @@ void update_mario_health(struct MarioState *m) { // If using the debug level select, do not lose any HP to water. if ((m->pos[1] >= (m->waterLevel - 140)) && !terrainIsSnow) { m->health += 0x1A; + GameInteractor_ExecuteOnHealthChange(m->health); } else if (!gDebugLevelSelect) { m->health -= (terrainIsSnow ? 3 : 1); + GameInteractor_ExecuteOnHealthChange(m->health); } } } @@ -1475,10 +1480,12 @@ void update_mario_health(struct MarioState *m) { if (m->healCounter > 0) { m->health += 0x40; m->healCounter--; + GameInteractor_ExecuteOnHealthChange(m->health); } if (m->hurtCounter > 0) { m->health -= 0x40; m->hurtCounter--; + GameInteractor_ExecuteOnHealthChange(m->health); } if (m->health > 0x880) { diff --git a/src/port/Engine.cpp b/src/port/Engine.cpp index f7ebbba4..2d6fee5b 100644 --- a/src/port/Engine.cpp +++ b/src/port/Engine.cpp @@ -9,6 +9,8 @@ #include "texts_table.h" #include "port/importer/DialogFactory.h" #include "port/importer/DictionaryFactory.h" +#include "port/Enhancements/game-interactor/GameInteractor.h" +#include "port/Enhancements/mods.h" #include #include @@ -21,6 +23,7 @@ extern "C" { } GameEngine* GameEngine::Instance; +GameInteractor* GameInteractor::Instance; GameEngine::GameEngine(){ std::vector OTRFiles; @@ -55,6 +58,8 @@ GameEngine::GameEngine(){ void GameEngine::Create(){ auto instance = GameEngine::Instance = new GameEngine(); + GameInteractor::Instance = new GameInteractor(); + InitMods(); GameUI::SetupGuiElements(); instance->AudioInit(); instance->LoadDictionary(); diff --git a/src/port/Enhancements/game-interactor/GameInteractor.cpp b/src/port/Enhancements/game-interactor/GameInteractor.cpp new file mode 100644 index 00000000..93854f3b --- /dev/null +++ b/src/port/Enhancements/game-interactor/GameInteractor.cpp @@ -0,0 +1,16 @@ +#include "GameInteractor.h" + +#include "game/level_update.h" + +extern int16_t sCurrPlayMode; + +// Taken from game/level_update.c +#define PLAY_MODE_NORMAL 0 +#define PLAY_MODE_PAUSED 2 +#define PLAY_MODE_CHANGE_AREA 3 +#define PLAY_MODE_CHANGE_LEVEL 4 +#define PLAY_MODE_FRAME_ADVANCE 5 + +bool GameInteractor::IsGameplayPaused() { + return sCurrPlayMode == PLAY_MODE_PAUSED; +} diff --git a/src/port/Enhancements/game-interactor/GameInteractor.h b/src/port/Enhancements/game-interactor/GameInteractor.h new file mode 100644 index 00000000..0124557a --- /dev/null +++ b/src/port/Enhancements/game-interactor/GameInteractor.h @@ -0,0 +1,55 @@ +#ifndef GameInteractor_h +#define GameInteractor_h + +#ifdef __cplusplus +extern "C" { +#endif +#ifdef __cplusplus +} +#endif + + +#ifdef __cplusplus + +#include +#include + +#define DEFINE_HOOK(name, type) \ + struct name { \ + typedef std::function fn; \ + } + +class GameInteractor { +public: + static GameInteractor* Instance; + + // Game State + class State { + + }; + + // Game Hooks + template struct RegisteredGameHooks { inline static std::vector functions; }; + template void RegisterGameHook(typename H::fn h) { RegisteredGameHooks::functions.push_back(h); } + template void ExecuteHooks(Args&&... args) { + for (auto& fn : RegisteredGameHooks::functions) { + fn(std::forward(args)...); + } + } + + DEFINE_HOOK(OnGameFrameUpdate, void()); + DEFINE_HOOK(OnHealthChange, void(int16_t health)); + + // Helpers + static bool IsGameplayPaused(); + + // Game Actions + class RawAction { + + }; + +}; + +#endif // __cplusplus + +#endif // GameInteractor_h diff --git a/src/port/Enhancements/game-interactor/GameInteractor_Hooks.cpp b/src/port/Enhancements/game-interactor/GameInteractor_Hooks.cpp new file mode 100644 index 00000000..285330c0 --- /dev/null +++ b/src/port/Enhancements/game-interactor/GameInteractor_Hooks.cpp @@ -0,0 +1,12 @@ +#include "GameInteractor_Hooks.h" + +// Gameplay + +// Updates only when in game, i.e. past the save select screen +void GameInteractor_ExecuteOnGameFrameUpdate() { + GameInteractor::Instance->ExecuteHooks(); +} + +void GameInteractor_ExecuteOnHealthChange(int16_t health) { + GameInteractor::Instance->ExecuteHooks(health); +} diff --git a/src/port/Enhancements/game-interactor/GameInteractor_Hooks.h b/src/port/Enhancements/game-interactor/GameInteractor_Hooks.h new file mode 100644 index 00000000..997ad1f4 --- /dev/null +++ b/src/port/Enhancements/game-interactor/GameInteractor_Hooks.h @@ -0,0 +1,13 @@ +#include "GameInteractor.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// Gameplay +void GameInteractor_ExecuteOnGameFrameUpdate(); +void GameInteractor_ExecuteOnHealthChange(int16_t health); + +#ifdef __cplusplus +} +#endif diff --git a/src/port/Enhancements/mods.cpp b/src/port/Enhancements/mods.cpp new file mode 100644 index 00000000..0da46ce9 --- /dev/null +++ b/src/port/Enhancements/mods.cpp @@ -0,0 +1,34 @@ +#include "mods.h" +#include +#include "game-interactor/GameInteractor.h" + +#include "game/level_update.h" +extern MarioState* gMarioState; + +#define MARIO_HEALTH_MAX 0x880 + +void RegisterInfiniteHealth() { + GameInteractor::Instance->RegisterGameHook([](int16_t health) { + if (!gMarioState) return; + + if (CVarGetInteger("gInfiniteHealth", 0) != 0) { + gMarioState->health = MARIO_HEALTH_MAX; + } + }); +} + +void RegisterInfiniteLives() { + // There may be a better hook that can be made, rather than doing this every frame + GameInteractor::Instance->RegisterGameHook([]() { + if (!gMarioState) return; + + if (CVarGetInteger("gInfiniteLives", 0) != 0) { + gMarioState->numLives = 100; + } + }); +} + +void InitMods() { + RegisterInfiniteHealth(); + RegisterInfiniteLives(); +} diff --git a/src/port/Enhancements/mods.h b/src/port/Enhancements/mods.h new file mode 100644 index 00000000..f56e68e7 --- /dev/null +++ b/src/port/Enhancements/mods.h @@ -0,0 +1,9 @@ +#ifdef __cplusplus +extern "C" { +#endif + +void InitMods(); + +#ifdef __cplusplus +} +#endif diff --git a/src/port/ui/ImguiUI.cpp b/src/port/ui/ImguiUI.cpp index cf0f3659..f3944e46 100644 --- a/src/port/ui/ImguiUI.cpp +++ b/src/port/ui/ImguiUI.cpp @@ -411,6 +411,16 @@ void DrawEnhancementsMenu() { } } +void DrawCheatsMenu() { + if (ImGui::BeginMenu("Cheats")) { + UIWidgets::PaddedEnhancementCheckbox("Infinite Health", "gInfiniteHealth", true, false); + UIWidgets::PaddedEnhancementCheckbox("Infinite Lives", "gInfiniteLives", true, false); + + + ImGui::EndMenu(); + } +} + void DrawDebugMenu() { if (ImGui::BeginMenu("Developer")) { @@ -448,7 +458,6 @@ void DrawDebugMenu() { } } - void GameMenuBar::DrawElement() { if(ImGui::BeginMenuBar()){ DrawMenuBarIcon(); @@ -468,6 +477,11 @@ void GameMenuBar::DrawElement() { DrawEnhancementsMenu(); ImGui::SetCursorPosY(0.0f); + + DrawCheatsMenu(); + + ImGui::SetCursorPosY(0.0f); + DrawDebugMenu(); ImGui::PopStyleVar(1);