Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[GameInteractor] Introduce GameFrameUpdate Hook and Migrate Cheats #2554

Merged
merged 2 commits into from
Feb 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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