Skip to content

Commit

Permalink
[GameInteractor] Introduce GameFrameUpdate Hook and Migrate Cheats (H…
Browse files Browse the repository at this point in the history
  • Loading branch information
dcvz authored Feb 28, 2023
1 parent afc61e6 commit c91a044
Show file tree
Hide file tree
Showing 5 changed files with 184 additions and 137 deletions.
1 change: 1 addition & 0 deletions soh/soh/Enhancements/game-interactor/GameInteractor.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ class GameInteractor {

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());
Expand Down
4 changes: 4 additions & 0 deletions soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ void GameInteractor_ExecuteOnExitGame(int32_t fileNum) {
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnExitGame>(fileNum);
}

void GameInteractor_ExecuteOnGameFrameUpdate() {
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnGameFrameUpdate>();
}

void GameInteractor_ExecuteOnReceiveItemHooks(u8 item) {
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnReceiveItem>(item);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
179 changes: 176 additions & 3 deletions soh/soh/Enhancements/mods.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,179 @@

extern "C" {
#include <z64.h>
#include "macros.h"
#include "variables.h"
extern SaveContext gSaveContext;
extern PlayState* gPlayState;
extern void Play_PerformSave(PlayState* play);
extern s32 Health_ChangeBy(PlayState* play, s16 healthChange);
extern void Rupees_ChangeBy(s16 rupeeChange);
}

void RegisterAutoSaveOnReceiveItemHook() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnReceiveItem>([](u8 item) {
void RegisterInfiniteMoney() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
if (CVarGetInteger("gInfiniteMoney", 0) != 0) {
if (gSaveContext.rupees < CUR_CAPACITY(UPG_WALLET)) {
gSaveContext.rupees = CUR_CAPACITY(UPG_WALLET);
}
}
});
}

void RegisterInfiniteHealth() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
if (CVarGetInteger("gInfiniteHealth", 0) != 0) {
if (gSaveContext.health < gSaveContext.healthCapacity) {
gSaveContext.health = gSaveContext.healthCapacity;
}
}
});
}

void RegisterInfiniteAmmo() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
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<GameInteractor::OnGameFrameUpdate>([]() {
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<GameInteractor::OnGameFrameUpdate>([]() {
if (CVarGetInteger("gInfiniteNayru", 0) != 0) {
gSaveContext.nayrusLoveTimer = 0x44B;
}
});
}

void RegisterMoonJumpOnL() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
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<GameInteractor::OnGameFrameUpdate>([]() {
if (CVarGetInteger("gEzISG", 0) != 0) {
if (gPlayState) {
Player* player = GET_PLAYER(gPlayState);
player->swordState = 1;
}
}
});
}

void RegisterUnrestrictedItems() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
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<GameInteractor::OnGameFrameUpdate>([]() {
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<GameInteractor::OnGameFrameUpdate>([&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<GameInteractor::OnReceiveItem>([](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
Expand Down Expand Up @@ -99,6 +262,16 @@ void RegisterRupeeDash() {
}

void InitMods() {
RegisterInfiniteMoney();
RegisterInfiniteHealth();
RegisterInfiniteAmmo();
RegisterInfiniteMagic();
RegisterInfiniteNayrusLove();
RegisterMoonJumpOnL();
RegisterInfiniteISG();
RegisterUnrestrictedItems();
RegisterFreezeTime();
RegisterSwitchAge();
RegisterRupeeDash();
RegisterAutoSaveOnReceiveItemHook();
RegisterAutoSave();
}
136 changes: 2 additions & 134 deletions soh/src/code/game.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include <string.h>
#include "global.h"
#include "vt.h"
#include "soh/Enhancements/game-interactor/GameInteractor.h"

SpeedMeter D_801664D0;
struct_801664F0 D_801664F0;
Expand All @@ -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);
Expand Down Expand Up @@ -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++;
}

Expand Down

0 comments on commit c91a044

Please sign in to comment.