From c91a044d4635b86381e5dea05c38a356b684b638 Mon Sep 17 00:00:00 2001 From: David Chavez Date: Tue, 28 Feb 2023 11:20:43 +0100 Subject: [PATCH] [GameInteractor] Introduce GameFrameUpdate Hook and Migrate Cheats (#2554) --- .../game-interactor/GameInteractor.h | 1 + .../game-interactor/GameInteractor_Hooks.cpp | 4 + .../game-interactor/GameInteractor_Hooks.h | 1 + soh/soh/Enhancements/mods.cpp | 179 +++++++++++++++++- soh/src/code/game.c | 136 +------------ 5 files changed, 184 insertions(+), 137 deletions(-) diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor.h b/soh/soh/Enhancements/game-interactor/GameInteractor.h index 1379339f1..a3e2b4c5c 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor.h @@ -86,6 +86,7 @@ public: DEFINE_HOOK(OnLoadGame, void(int32_t fileNum)); DEFINE_HOOK(OnExitGame, void(int32_t fileNum)); + DEFINE_HOOK(OnGameFrameUpdate, void()); DEFINE_HOOK(OnReceiveItem, void(u8 item)); DEFINE_HOOK(OnSceneInit, void(s16 sceneNum)); DEFINE_HOOK(OnPlayerUpdate, void()); diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp index 9405f201e..2ed4758e9 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp @@ -14,6 +14,10 @@ void GameInteractor_ExecuteOnExitGame(int32_t fileNum) { GameInteractor::Instance->ExecuteHooks(fileNum); } +void GameInteractor_ExecuteOnGameFrameUpdate() { + GameInteractor::Instance->ExecuteHooks(); +} + void GameInteractor_ExecuteOnReceiveItemHooks(u8 item) { GameInteractor::Instance->ExecuteHooks(item); } diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h index 302102552..658ca05d4 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h @@ -3,6 +3,7 @@ // MARK: - Gameplay extern "C" void GameInteractor_ExecuteOnLoadGame(int32_t fileNum); extern "C" void GameInteractor_ExecuteOnExitGame(int32_t fileNum); +extern "C" void GameInteractor_ExecuteOnGameFrameUpdate(); extern "C" void GameInteractor_ExecuteOnReceiveItemHooks(u8 item); extern "C" void GameInteractor_ExecuteOnSceneInit(s16 sceneNum); extern "C" void GameInteractor_ExecuteOnPlayerUpdate(); diff --git a/soh/soh/Enhancements/mods.cpp b/soh/soh/Enhancements/mods.cpp index c5aecd397..66f2d169e 100644 --- a/soh/soh/Enhancements/mods.cpp +++ b/soh/soh/Enhancements/mods.cpp @@ -4,6 +4,8 @@ extern "C" { #include +#include "macros.h" +#include "variables.h" extern SaveContext gSaveContext; extern PlayState* gPlayState; extern void Play_PerformSave(PlayState* play); @@ -11,9 +13,170 @@ extern s32 Health_ChangeBy(PlayState* play, s16 healthChange); extern void Rupees_ChangeBy(s16 rupeeChange); } -void RegisterAutoSaveOnReceiveItemHook() { - GameInteractor::Instance->RegisterGameHook([](u8 item) { +void RegisterInfiniteMoney() { + GameInteractor::Instance->RegisterGameHook([]() { + if (CVarGetInteger("gInfiniteMoney", 0) != 0) { + if (gSaveContext.rupees < CUR_CAPACITY(UPG_WALLET)) { + gSaveContext.rupees = CUR_CAPACITY(UPG_WALLET); + } + } + }); +} +void RegisterInfiniteHealth() { + GameInteractor::Instance->RegisterGameHook([]() { + if (CVarGetInteger("gInfiniteHealth", 0) != 0) { + if (gSaveContext.health < gSaveContext.healthCapacity) { + gSaveContext.health = gSaveContext.healthCapacity; + } + } + }); +} + +void RegisterInfiniteAmmo() { + GameInteractor::Instance->RegisterGameHook([]() { + if (CVarGetInteger("gInfiniteAmmo", 0) != 0) { + // Deku Sticks + if (AMMO(ITEM_STICK) < CUR_CAPACITY(UPG_STICKS)) { + AMMO(ITEM_STICK) = CUR_CAPACITY(UPG_STICKS); + } + + // Deku Nuts + if (AMMO(ITEM_NUT) < CUR_CAPACITY(UPG_NUTS)) { + AMMO(ITEM_NUT) = CUR_CAPACITY(UPG_NUTS); + } + + // Bombs + if (AMMO(ITEM_BOMB) < CUR_CAPACITY(UPG_BOMB_BAG)) { + AMMO(ITEM_BOMB) = CUR_CAPACITY(UPG_BOMB_BAG); + } + + // Fairy Bow (Ammo) + if (AMMO(ITEM_BOW) < CUR_CAPACITY(UPG_QUIVER)) { + AMMO(ITEM_BOW) = CUR_CAPACITY(UPG_QUIVER); + } + + // Fairy Slingshot (Ammo) + if (AMMO(ITEM_SLINGSHOT) < CUR_CAPACITY(UPG_BULLET_BAG)) { + AMMO(ITEM_SLINGSHOT) = CUR_CAPACITY(UPG_BULLET_BAG); + } + + // Bombchus (max: 50, no upgrades) + if (INV_CONTENT(ITEM_BOMBCHU) == ITEM_BOMBCHU && AMMO(ITEM_BOMBCHU) < 50) { + AMMO(ITEM_BOMBCHU) = 50; + } + } + }); +} + +void RegisterInfiniteMagic() { + GameInteractor::Instance->RegisterGameHook([]() { + if (CVarGetInteger("gInfiniteMagic", 0) != 0) { + if (gSaveContext.isMagicAcquired && gSaveContext.magic != (gSaveContext.isDoubleMagicAcquired + 1) * 0x30) { + gSaveContext.magic = (gSaveContext.isDoubleMagicAcquired + 1) * 0x30; + } + } + }); +} + +void RegisterInfiniteNayrusLove() { + GameInteractor::Instance->RegisterGameHook([]() { + if (CVarGetInteger("gInfiniteNayru", 0) != 0) { + gSaveContext.nayrusLoveTimer = 0x44B; + } + }); +} + +void RegisterMoonJumpOnL() { + GameInteractor::Instance->RegisterGameHook([]() { + if (CVarGetInteger("gMoonJumpOnL", 0) != 0) { + if (gPlayState) { + Player* player = GET_PLAYER(gPlayState); + + if (CHECK_BTN_ANY(gPlayState->state.input[0].cur.button, BTN_L)) { + player->actor.velocity.y = 6.34375f; + } + } + } + }); +} + + +void RegisterInfiniteISG() { + GameInteractor::Instance->RegisterGameHook([]() { + if (CVarGetInteger("gEzISG", 0) != 0) { + if (gPlayState) { + Player* player = GET_PLAYER(gPlayState); + player->swordState = 1; + } + } + }); +} + +void RegisterUnrestrictedItems() { + GameInteractor::Instance->RegisterGameHook([]() { + if (CVarGetInteger("gNoRestrictItems", 0) != 0) { + if (gPlayState) { + u8 sunsBackup = gPlayState->interfaceCtx.restrictions.sunsSong; + memset(&gPlayState->interfaceCtx.restrictions, 0, sizeof(gPlayState->interfaceCtx.restrictions)); + gPlayState->interfaceCtx.restrictions.sunsSong = sunsBackup; + } + } + }); +} + +void RegisterFreezeTime() { + GameInteractor::Instance->RegisterGameHook([]() { + if (CVarGetInteger("gFreezeTime", 0) != 0) { + if (CVarGetInteger("gPrevTime", -1) == -1) { + CVarSetInteger("gPrevTime", gSaveContext.dayTime); + } + + int32_t prevTime = CVarGetInteger("gPrevTime", gSaveContext.dayTime); + gSaveContext.dayTime = prevTime; + } else { + CVarSetInteger("gPrevTime", -1); + } + }); +} + +/// Switches Link's age and respawns him at the last entrance he entered. +void RegisterSwitchAge() { + bool warped = false; + Vec3f playerPos; + int16_t playerYaw; + + GameInteractor::Instance->RegisterGameHook([&warped, &playerPos, &playerYaw]() { + if (CVarGetInteger("gSwitchAge", 0) != 0) { + CVarSetInteger("gSwitchAge", 0); + if (gPlayState) { + playerPos = GET_PLAYER(gPlayState)->actor.world.pos; + playerYaw = GET_PLAYER(gPlayState)->actor.shape.rot.y; + gPlayState->nextEntranceIndex = gSaveContext.entranceIndex; + gPlayState->sceneLoadFlag = 0x14; + gPlayState->fadeTransition = 11; + gSaveContext.nextTransitionType = 11; + warped = true; + if (gPlayState->linkAgeOnLoad == 1) { + gPlayState->linkAgeOnLoad = 0; + } else { + gPlayState->linkAgeOnLoad = 1; + } + } + } + + if (gPlayState) { + if (warped && gPlayState->sceneLoadFlag != 0x0014 && gSaveContext.nextTransitionType == 255) { + GET_PLAYER(gPlayState)->actor.shape.rot.y = playerYaw; + GET_PLAYER(gPlayState)->actor.world.pos = playerPos; + warped = false; + } + } + }); +} + +void RegisterAutoSave() { + GameInteractor::Instance->RegisterGameHook([](u8 item) { // Don't autosave immediately after buying items from shops to prevent getting them for free! // Don't autosave in the Chamber of Sages since resuming from that map breaks the game // Don't autosave during the Ganon fight when picking up the Master Sword @@ -99,6 +262,16 @@ void RegisterRupeeDash() { } void InitMods() { + RegisterInfiniteMoney(); + RegisterInfiniteHealth(); + RegisterInfiniteAmmo(); + RegisterInfiniteMagic(); + RegisterInfiniteNayrusLove(); + RegisterMoonJumpOnL(); + RegisterInfiniteISG(); + RegisterUnrestrictedItems(); + RegisterFreezeTime(); + RegisterSwitchAge(); RegisterRupeeDash(); - RegisterAutoSaveOnReceiveItemHook(); + RegisterAutoSave(); } diff --git a/soh/src/code/game.c b/soh/src/code/game.c index cdb36787e..546901d3e 100644 --- a/soh/src/code/game.c +++ b/soh/src/code/game.c @@ -1,6 +1,7 @@ #include #include "global.h" #include "vt.h" +#include "soh/Enhancements/game-interactor/GameInteractor.h" SpeedMeter D_801664D0; struct_801664F0 D_801664F0; @@ -9,10 +10,6 @@ VisMono sMonoColors; ViMode sViMode; FaultClient sGameFaultClient; u16 sLastButtonPressed; -int32_t warpTime = 0; -bool warped = false; -Vec3f playerPos; -int16_t playerYaw; // Forward declared, because this in a C++ header. int gfx_create_framebuffer(uint32_t width, uint32_t height); @@ -328,138 +325,9 @@ void GameState_Update(GameState* gameState) { func_800C49F4(gfxCtx); } - // ----------------------- - // Cheats hooks - // ----------------------- - - // Inf Money - if (CVarGetInteger("gInfiniteMoney", 0) != 0) { - if (gSaveContext.rupees < CUR_CAPACITY(UPG_WALLET)) { - gSaveContext.rupees = CUR_CAPACITY(UPG_WALLET); - } - } - - // Inf Health - if (CVarGetInteger("gInfiniteHealth", 0) != 0) { - if (gSaveContext.health < gSaveContext.healthCapacity) { - gSaveContext.health = gSaveContext.healthCapacity; - } - } - - // Inf Ammo - if (CVarGetInteger("gInfiniteAmmo", 0) != 0) { - // Deku Sticks - if (AMMO(ITEM_STICK) < CUR_CAPACITY(UPG_STICKS)) { - AMMO(ITEM_STICK) = CUR_CAPACITY(UPG_STICKS); - } - - // Deku Nuts - if (AMMO(ITEM_NUT) < CUR_CAPACITY(UPG_NUTS)) { - AMMO(ITEM_NUT) = CUR_CAPACITY(UPG_NUTS); - } - - // Bombs - if (AMMO(ITEM_BOMB) < CUR_CAPACITY(UPG_BOMB_BAG)) { - AMMO(ITEM_BOMB) = CUR_CAPACITY(UPG_BOMB_BAG); - } - - // Fairy Bow (Ammo) - if (AMMO(ITEM_BOW) < CUR_CAPACITY(UPG_QUIVER)) { - AMMO(ITEM_BOW) = CUR_CAPACITY(UPG_QUIVER); - } - - // Fairy Slingshot (Ammo) - if (AMMO(ITEM_SLINGSHOT) < CUR_CAPACITY(UPG_BULLET_BAG)) { - AMMO(ITEM_SLINGSHOT) = CUR_CAPACITY(UPG_BULLET_BAG); - } - - // Bombchus (max: 50, no upgrades) - if (INV_CONTENT(ITEM_BOMBCHU) == ITEM_BOMBCHU && AMMO(ITEM_BOMBCHU) < 50) { - AMMO(ITEM_BOMBCHU) = 50; - } - } - - // Inf Magic - if (CVarGetInteger("gInfiniteMagic", 0) != 0) { - if (gSaveContext.isMagicAcquired && gSaveContext.magic != (gSaveContext.isDoubleMagicAcquired + 1) * 0x30) { - gSaveContext.magic = (gSaveContext.isDoubleMagicAcquired + 1) * 0x30; - } - } - - // Inf Nayru's Love Timer - if (CVarGetInteger("gInfiniteNayru", 0) != 0) { - gSaveContext.nayrusLoveTimer = 0x44B; - } - - // Moon Jump On L - if (CVarGetInteger("gMoonJumpOnL", 0) != 0) { - if (gPlayState) { - Player* player = GET_PLAYER(gPlayState); - - if (CHECK_BTN_ANY(gPlayState->state.input[0].cur.button, BTN_L)) { - player->actor.velocity.y = 6.34375f; - } - } - } - - // Permanent infinite sword glitch (ISG) - if (CVarGetInteger("gEzISG", 0) != 0) { - if (gPlayState) { - Player* player = GET_PLAYER(gPlayState); - player->swordState = 1; - } - } - - // Unrestricted Items - if (CVarGetInteger("gNoRestrictItems", 0) != 0) { - if (gPlayState) { - u8 sunsBackup = gPlayState->interfaceCtx.restrictions.sunsSong; - memset(&gPlayState->interfaceCtx.restrictions, 0, sizeof(gPlayState->interfaceCtx.restrictions)); - gPlayState->interfaceCtx.restrictions.sunsSong = sunsBackup; - } - } - - // Freeze Time - if (CVarGetInteger("gFreezeTime", 0) != 0) { - if (CVarGetInteger("gPrevTime", -1) == -1) { - CVarSetInteger("gPrevTime", gSaveContext.dayTime); - } - - int32_t prevTime = CVarGetInteger("gPrevTime", gSaveContext.dayTime); - gSaveContext.dayTime = prevTime; - } else { - CVarSetInteger("gPrevTime", -1); - } - - //Switches Link's age and respawns him at the last entrance he entered. - if (CVarGetInteger("gSwitchAge", 0) != 0) { - CVarSetInteger("gSwitchAge", 0); - if (gPlayState) { - playerPos = GET_PLAYER(gPlayState)->actor.world.pos; - playerYaw = GET_PLAYER(gPlayState)->actor.shape.rot.y; - gPlayState->nextEntranceIndex = gSaveContext.entranceIndex; - gPlayState->sceneLoadFlag = 0x14; - gPlayState->fadeTransition = 11; - gSaveContext.nextTransitionType = 11; - warped = true; - if (gPlayState->linkAgeOnLoad == 1) { - gPlayState->linkAgeOnLoad = 0; - } else { - gPlayState->linkAgeOnLoad = 1; - } - } - } - - if (gPlayState) { - if (warped && gPlayState->sceneLoadFlag != 0x0014 && gSaveContext.nextTransitionType == 255) { - GET_PLAYER(gPlayState)->actor.shape.rot.y = playerYaw; - GET_PLAYER(gPlayState)->actor.world.pos = playerPos; - warped = false; - } - } - gSaveContext.language = CVarGetInteger("gLanguages", LANGUAGE_ENG); + GameInteractor_ExecuteOnGameFrameUpdate(); gameState->frames++; }