From c5835669fec5c804156943b5cd6e8da3a1bd6489 Mon Sep 17 00:00:00 2001 From: Chris Date: Tue, 30 Jun 2026 00:10:56 -0400 Subject: [PATCH] Hookifying Various Cheats and FixVineFall (#6827) --- .../Enhancements/Cheats/ClimbEverything.cpp | 10 +++ .../Cheats/FireproofDekuShield.cpp | 8 +++ .../Cheats/HookshotEverything.cpp | 8 +++ .../Cheats/Infinite/EponaBoost.cpp | 10 +++ soh/soh/Enhancements/Cheats/NoClip.cpp | 13 ++++ soh/soh/Enhancements/Fixes/FixVineFall.cpp | 72 +++++++++++++++++++ .../vanilla-behavior/GIVanillaBehavior.h | 67 +++++++++++++++++ soh/src/code/z_bgcheck.c | 7 +- soh/src/code/z_parameter.c | 2 +- .../overlays/actors/ovl_En_Horse/z_en_horse.c | 3 +- .../actors/ovl_player_actor/z_player.c | 57 ++------------- 11 files changed, 199 insertions(+), 58 deletions(-) create mode 100644 soh/soh/Enhancements/Cheats/ClimbEverything.cpp create mode 100644 soh/soh/Enhancements/Cheats/FireproofDekuShield.cpp create mode 100644 soh/soh/Enhancements/Cheats/HookshotEverything.cpp create mode 100644 soh/soh/Enhancements/Cheats/Infinite/EponaBoost.cpp create mode 100644 soh/soh/Enhancements/Cheats/NoClip.cpp create mode 100644 soh/soh/Enhancements/Fixes/FixVineFall.cpp diff --git a/soh/soh/Enhancements/Cheats/ClimbEverything.cpp b/soh/soh/Enhancements/Cheats/ClimbEverything.cpp new file mode 100644 index 0000000000..62614ac3b7 --- /dev/null +++ b/soh/soh/Enhancements/Cheats/ClimbEverything.cpp @@ -0,0 +1,10 @@ +#include "soh/Enhancements/game-interactor/GameInteractor.h" +#include "soh/ShipInit.hpp" + +static void RegisterClimbEverything() { + COND_VB_SHOULD(VB_SURFACE_IS_CLIMBABLE, CVarGetInteger(CVAR_CHEAT("ClimbEverything"), 0), { *should = true; }); + COND_VB_SHOULD(VB_SURFACE_ANGLE_IS_CLIMBABLE, CVarGetInteger(CVAR_CHEAT("ClimbEverything"), 0), + { *should = true; }); +} + +static RegisterShipInitFunc initFunc(RegisterClimbEverything, { CVAR_CHEAT("ClimbEverything") }); diff --git a/soh/soh/Enhancements/Cheats/FireproofDekuShield.cpp b/soh/soh/Enhancements/Cheats/FireproofDekuShield.cpp new file mode 100644 index 0000000000..4b8e4a135d --- /dev/null +++ b/soh/soh/Enhancements/Cheats/FireproofDekuShield.cpp @@ -0,0 +1,8 @@ +#include "soh/Enhancements/game-interactor/GameInteractor.h" +#include "soh/ShipInit.hpp" + +static void RegisterFireproofDekuShield() { + COND_VB_SHOULD(VB_BURN_SHIELD, CVarGetInteger(CVAR_CHEAT("FireproofDekuShield"), 0), { *should = false; }); +} + +static RegisterShipInitFunc initFunc(RegisterFireproofDekuShield, { CVAR_CHEAT("FireproofDekuShield") }); diff --git a/soh/soh/Enhancements/Cheats/HookshotEverything.cpp b/soh/soh/Enhancements/Cheats/HookshotEverything.cpp new file mode 100644 index 0000000000..be97c09565 --- /dev/null +++ b/soh/soh/Enhancements/Cheats/HookshotEverything.cpp @@ -0,0 +1,8 @@ +#include "soh/Enhancements/game-interactor/GameInteractor.h" +#include "soh/ShipInit.hpp" + +static void RegisterHookshotEverything() { + COND_VB_SHOULD(VB_SURFACE_IS_HOOKSHOT, CVarGetInteger(CVAR_CHEAT("HookshotEverything"), 0), { *should = true; }); +} + +static RegisterShipInitFunc initFunc(RegisterHookshotEverything, { CVAR_CHEAT("HookshotEverything") }); diff --git a/soh/soh/Enhancements/Cheats/Infinite/EponaBoost.cpp b/soh/soh/Enhancements/Cheats/Infinite/EponaBoost.cpp new file mode 100644 index 0000000000..0baa3e191f --- /dev/null +++ b/soh/soh/Enhancements/Cheats/Infinite/EponaBoost.cpp @@ -0,0 +1,10 @@ +#include "soh/Enhancements/game-interactor/GameInteractor.h" +#include "soh/ShipInit.hpp" + +static void RegisterInfiniteEponaBoost() { + COND_VB_SHOULD(VB_CONSUME_EPONA_BOOST, CVarGetInteger(CVAR_CHEAT("InfiniteEponaBoost"), 0), { *should = false; }); + COND_VB_SHOULD(VB_DRAW_EPONA_BOOST_CARROTS, CVarGetInteger(CVAR_CHEAT("InfiniteEponaBoost"), 0), + { *should = false; }); +} + +static RegisterShipInitFunc initFunc(RegisterInfiniteEponaBoost, { CVAR_CHEAT("InfiniteEponaBoost") }); diff --git a/soh/soh/Enhancements/Cheats/NoClip.cpp b/soh/soh/Enhancements/Cheats/NoClip.cpp new file mode 100644 index 0000000000..1ad6304772 --- /dev/null +++ b/soh/soh/Enhancements/Cheats/NoClip.cpp @@ -0,0 +1,13 @@ +#include "soh/Enhancements/game-interactor/GameInteractor.h" +#include "soh/ShipInit.hpp" + +static void RegisterNoClip() { + COND_VB_SHOULD(VB_PERFORM_WALL_COLLISION_CHECK, CVarGetInteger(CVAR_CHEAT("NoClip"), 0), { + Actor* actor = va_arg(args, Actor*); + if (actor != NULL && actor->id == ACTOR_PLAYER) { + *should = false; + } + }); +} + +static RegisterShipInitFunc initFunc(RegisterNoClip, { CVAR_CHEAT("NoClip") }); diff --git a/soh/soh/Enhancements/Fixes/FixVineFall.cpp b/soh/soh/Enhancements/Fixes/FixVineFall.cpp new file mode 100644 index 0000000000..f518916739 --- /dev/null +++ b/soh/soh/Enhancements/Fixes/FixVineFall.cpp @@ -0,0 +1,72 @@ +#include "soh/Enhancements/game-interactor/GameInteractor.h" +#include "soh/ShipInit.hpp" + +extern "C" { +#include "functions.h" +} + +static void RegisterFixVineFall() { + // conflicts arise from these two being enabled at once, and with ClimbEverything on, FixVineFall is redundant + // anyway + COND_VB_SHOULD( + VB_REVALIDATE_CLIMBED_WALL, + CVarGetInteger(CVAR_ENHANCEMENT("FixVineFall"), 0) && !CVarGetInteger(CVAR_CHEAT("ClimbEverything"), 0), { + PlayState* play = va_arg(args, PlayState*); + Player* player = va_arg(args, Player*); + u32* touchedWallFlags = va_arg(args, u32*); + s16* yawDiff = va_arg(args, s16*); + + /* This fixes the "started climbing a wall and then immediately fell off" bug. + * The main idea is if a climbing wall is detected, double-check that it will + * still be valid once climbing begins by doing a second raycast with a small + * margin to make sure it still hits a climbable poly. Then update the flags + * in touchedWallFlags again and proceed as normal. + */ + if (*touchedWallFlags & 8) { + Vec3f checkPosA; + Vec3f checkPosB; + Vec3f raycastResult; + CollisionPoly* wallPoly; + s32 wallBgId; + f32 yawCos; + f32 yawSin; + s32 hitWall; + + /* Angle the raycast slightly out towards the side based on the angle of + * attack the player takes coming at the climb wall. This is necessary because + * the player's XZ position actually wobbles very slightly while climbing + * due to small rounding errors in the sin/cos lookup tables. This wobble + * can cause wall checks while climbing to be slightly left or right of + * the wall check to start the climb. By adding this buffer it accounts for + * any possible wobble. The end result is the player has to be further than + * some epsilon distance from the edge of the climbing poly to actually + * start the climb. I divide it by 2 to make that epsilon slightly smaller, + * mainly for visuals. Using the full yawDiff leaves a noticeable gap on + * the edges that can't be climbed. But with the half distance it looks like + * the player is climbing right on the edge, and still works. + */ + yawCos = Math_CosS(player->actor.wallYaw - (*yawDiff / 2) + 0x8000); + yawSin = Math_SinS(player->actor.wallYaw - (*yawDiff / 2) + 0x8000); + checkPosA.x = player->actor.world.pos.x + (-20.0f * yawSin); + checkPosA.z = player->actor.world.pos.z + (-20.0f * yawCos); + checkPosB.x = player->actor.world.pos.x + (50.0f * yawSin); + checkPosB.z = player->actor.world.pos.z + (50.0f * yawCos); + checkPosB.y = checkPosA.y = player->actor.world.pos.y + 26.0f; + + hitWall = BgCheck_EntityLineTest1(&play->colCtx, &checkPosA, &checkPosB, &raycastResult, &wallPoly, + true, false, false, true, &wallBgId); + + if (hitWall) { + player->actor.wallPoly = wallPoly; + player->actor.wallBgId = wallBgId; + player->actor.wallYaw = Math_Atan2S(wallPoly->normal.z, wallPoly->normal.x); + *yawDiff = player->actor.shape.rot.y - (s16)(player->actor.wallYaw + 0x8000); + + *touchedWallFlags = func_80041DB8(&play->colCtx, player->actor.wallPoly, player->actor.wallBgId); + } + } + }); +} + +static RegisterShipInitFunc initFunc(RegisterFixVineFall, + { CVAR_ENHANCEMENT("FixVineFall"), CVAR_CHEAT("ClimbEverything") }); diff --git a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h index c130a65c66..6998709b66 100644 --- a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h +++ b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h @@ -285,6 +285,14 @@ typedef enum { // - `*EnPoField` VB_BOTTLE_BIG_POE, + // #### `result` + // ```c + // this->currentShield == PLAYER_SHIELD_DEKU + // ``` + // #### `args` + // - `*Player` + VB_BURN_SHIELD, + // #### `result` // ```c // true @@ -367,6 +375,14 @@ typedef enum { // - None VB_CLOSE_PAUSE_MENU, + // #### `result` + // ```c + // true + // ``` + // #### `args` + // - `*EnHorse` + VB_CONSUME_EPONA_BOOST, + // #### `result` // ```c // true @@ -612,6 +628,14 @@ typedef enum { // - `*int16_t` (item id) VB_DRAW_AMMO_COUNT, + // #### `result` + // ```c + // true + // ``` + // #### `args` + // - None + VB_DRAW_EPONA_BOOST_CARROTS, + // #### `args` // - `Player*` player // - `PlayState*` play @@ -1642,6 +1666,14 @@ typedef enum { // - `*EnOwl` VB_OWL_INTERACTION, + // #### `result` + // ```c + // true + // ``` + // #### `args` + // - `*Actor` + VB_PERFORM_WALL_COLLISION_CHECK, + // #### `result` // ```c // true @@ -2145,6 +2177,17 @@ typedef enum { // - `**Gfx` (`&POLY_OPA_DISP`) VB_RENDER_YES_ON_CONTINUE_PROMPT, + // #### `result` + // ```c + // true + // ``` + // #### `args` + // - `*PlayState` + // - `*Player` + // - `*u32` + // - `*s16` + VB_REVALIDATE_CLIMBED_WALL, + // #### `result` // ```c // true @@ -2488,6 +2531,30 @@ typedef enum { // - `*BgIceTurara` VB_STALAGMITE_DROP_ITEM, + // #### `result` + // ```c + // ABS(wallPoly->normal.y) < 600 + // ``` + // #### `args` + // - None + VB_SURFACE_ANGLE_IS_CLIMBABLE, + + // #### `result` + // ```c + // false + // ``` + // #### `args` + // - None + VB_SURFACE_IS_CLIMBABLE, + + // #### `result` + // ```c + // SurfaceType_GetData(colCtx, poly, bgId, 1) >> 17 & 1 + // ``` + // #### `args` + // - None + VB_SURFACE_IS_HOOKSHOT, + // #### `result` // ```c // varies, never set should to true diff --git a/soh/src/code/z_bgcheck.c b/soh/src/code/z_bgcheck.c index e98e66e44d..3cce952f5b 100644 --- a/soh/src/code/z_bgcheck.c +++ b/soh/src/code/z_bgcheck.c @@ -3,6 +3,7 @@ #include "soh/OTRGlobals.h" #include "soh/ResourceManagerHelpers.h" +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #include #define SS_NULL 0xFFFF @@ -1892,7 +1893,7 @@ s32 BgCheck_CheckWallImpl(CollisionContext* colCtx, u16 xpFlags, Vec3f* posResul s32 bgId2; f32 nx, ny, nz; // unit normal of polygon - if (CVarGetInteger(CVAR_CHEAT("NoClip"), 0) && actor != NULL && actor->id == ACTOR_PLAYER) { + if (!GameInteractor_Should(VB_PERFORM_WALL_COLLISION_CHECK, true, actor)) { return false; } @@ -4011,7 +4012,7 @@ u32 func_80041D94(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) { * SurfaceType Get Wall Flags */ s32 func_80041DB8(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) { - if (CVarGetInteger(CVAR_CHEAT("ClimbEverything"), 0) != 0) { + if (GameInteractor_Should(VB_SURFACE_IS_CLIMBABLE, false)) { return (1 << 3) | D_80119D90[func_80041D94(colCtx, poly, bgId)]; } else { return D_80119D90[func_80041D94(colCtx, poly, bgId)]; @@ -4108,7 +4109,7 @@ u32 SurfaceType_GetEcho(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) * SurfaceType Is Hookshot Surface */ u32 SurfaceType_IsHookshotSurface(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) { - return CVarGetInteger(CVAR_CHEAT("HookshotEverything"), 0) || SurfaceType_GetData(colCtx, poly, bgId, 1) >> 17 & 1; + return GameInteractor_Should(VB_SURFACE_IS_HOOKSHOT, SurfaceType_GetData(colCtx, poly, bgId, 1) >> 17 & 1); } /** diff --git a/soh/src/code/z_parameter.c b/soh/src/code/z_parameter.c index c378d15f48..9a63d8de5d 100644 --- a/soh/src/code/z_parameter.c +++ b/soh/src/code/z_parameter.c @@ -5776,7 +5776,7 @@ void Interface_Draw(PlayState* play) { if ((play->pauseCtx.state == 0) && (play->pauseCtx.debugState == 0)) { if (gSaveContext.minigameState != 1) { // Carrots rendering if the action corresponds to riding a horse - if (interfaceCtx->unk_1EE == 8 && !CVarGetInteger(CVAR_CHEAT("InfiniteEponaBoost"), 0)) { + if (interfaceCtx->unk_1EE == 8 && GameInteractor_Should(VB_DRAW_EPONA_BOOST_CARROTS, true)) { // Load Carrot Icon gDPLoadTextureBlock(OVERLAY_DISP++, gCarrotIconTex, G_IM_FMT_RGBA, G_IM_SIZ_32b, 16, 16, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, diff --git a/soh/src/overlays/actors/ovl_En_Horse/z_en_horse.c b/soh/src/overlays/actors/ovl_En_Horse/z_en_horse.c index 502b07301c..fe8bd8b83a 100644 --- a/soh/src/overlays/actors/ovl_En_Horse/z_en_horse.c +++ b/soh/src/overlays/actors/ovl_En_Horse/z_en_horse.c @@ -9,6 +9,7 @@ #include "objects/object_horse/object_horse.h" #include "objects/object_hni/object_hni.h" #include "scenes/overworld/spot09/spot09_scene.h" +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #include #define FLAGS ACTOR_FLAG_UPDATE_CULLING_DISABLED @@ -3332,7 +3333,7 @@ void EnHorse_CheckBoost(EnHorse* thisx, PlayState* play2) { this->stateFlags |= ENHORSE_BOOST; this->stateFlags |= ENHORSE_FIRST_BOOST_REGEN; this->stateFlags |= ENHORSE_FLAG_8; - if (!CVarGetInteger(CVAR_CHEAT("InfiniteEponaBoost"), 0)) { + if (GameInteractor_Should(VB_CONSUME_EPONA_BOOST, true, this)) { this->numBoosts--; } this->boostTimer = 0; diff --git a/soh/src/overlays/actors/ovl_player_actor/z_player.c b/soh/src/overlays/actors/ovl_player_actor/z_player.c index 171cb09ac6..b1d2f4c47d 100644 --- a/soh/src/overlays/actors/ovl_player_actor/z_player.c +++ b/soh/src/overlays/actors/ovl_player_actor/z_player.c @@ -4681,7 +4681,7 @@ int func_8083816C(s32 arg0) { } void func_8083819C(Player* this, PlayState* play) { - if (this->currentShield == PLAYER_SHIELD_DEKU && (CVarGetInteger(CVAR_CHEAT("FireproofDekuShield"), 0) == 0)) { + if (GameInteractor_Should(VB_BURN_SHIELD, this->currentShield == PLAYER_SHIELD_DEKU, this)) { Actor_Spawn(&play->actorCtx, play, ACTOR_ITEM_SHIELD, this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 1); Inventory_DeleteEquipment(play, EQUIP_TYPE_SHIELD); @@ -5705,7 +5705,7 @@ s32 func_8083A6AC(Player* this, PlayState* play) { if (BgCheck_EntityLineTest1(&play->colCtx, &this->actor.world.pos, &sp74, &sp68, &sp84, true, false, false, true, &sp80) && - ((ABS(sp84->normal.y) < 600) || (CVarGetInteger(CVAR_CHEAT("ClimbEverything"), 0) != 0))) { + GameInteractor_Should(VB_SURFACE_ANGLE_IS_CLIMBABLE, ABS(sp84->normal.y) < 600)) { f32 nx = COLPOLY_GET_NORMAL(sp84->normal.x); f32 ny = COLPOLY_GET_NORMAL(sp84->normal.y); f32 nz = COLPOLY_GET_NORMAL(sp84->normal.z); @@ -11305,56 +11305,7 @@ void Player_ProcessSceneCollision(PlayState* play, Player* this) { sTouchedWallFlags = func_80041DB8(&play->colCtx, this->actor.wallPoly, this->actor.wallBgId); - // conflicts arise from these two being enabled at once, and with ClimbEverything on, FixVineFall is redundant - // anyway - if (CVarGetInteger(CVAR_ENHANCEMENT("FixVineFall"), 0) && !CVarGetInteger(CVAR_CHEAT("ClimbEverything"), 0)) { - /* This fixes the "started climbing a wall and then immediately fell off" bug. - * The main idea is if a climbing wall is detected, double-check that it will - * still be valid once climbing begins by doing a second raycast with a small - * margin to make sure it still hits a climbable poly. Then update the flags - * in sTouchedWallFlags again and proceed as normal. - */ - if (sTouchedWallFlags & 8) { - Vec3f checkPosA; - Vec3f checkPosB; - f32 yawCos; - f32 yawSin; - s32 hitWall; - - /* Angle the raycast slightly out towards the side based on the angle of - * attack the player takes coming at the climb wall. This is necessary because - * the player's XZ position actually wobbles very slightly while climbing - * due to small rounding errors in the sin/cos lookup tables. This wobble - * can cause wall checks while climbing to be slightly left or right of - * the wall check to start the climb. By adding this buffer it accounts for - * any possible wobble. The end result is the player has to be further than - * some epsilon distance from the edge of the climbing poly to actually - * start the climb. I divide it by 2 to make that epsilon slightly smaller, - * mainly for visuals. Using the full yawDiff leaves a noticeable gap on - * the edges that can't be climbed. But with the half distance it looks like - * the player is climbing right on the edge, and still works. - */ - yawCos = Math_CosS(this->actor.wallYaw - (yawDiff / 2) + 0x8000); - yawSin = Math_SinS(this->actor.wallYaw - (yawDiff / 2) + 0x8000); - checkPosA.x = this->actor.world.pos.x + (-20.0f * yawSin); - checkPosA.z = this->actor.world.pos.z + (-20.0f * yawCos); - checkPosB.x = this->actor.world.pos.x + (50.0f * yawSin); - checkPosB.z = this->actor.world.pos.z + (50.0f * yawCos); - checkPosB.y = checkPosA.y = this->actor.world.pos.y + 26.0f; - - hitWall = BgCheck_EntityLineTest1(&play->colCtx, &checkPosA, &checkPosB, &sInteractWallCheckResult, - &wallPoly, true, false, false, true, &wallBgId); - - if (hitWall) { - this->actor.wallPoly = wallPoly; - this->actor.wallBgId = wallBgId; - this->actor.wallYaw = Math_Atan2S(wallPoly->normal.z, wallPoly->normal.x); - yawDiff = this->actor.shape.rot.y - (s16)(this->actor.wallYaw + 0x8000); - - sTouchedWallFlags = func_80041DB8(&play->colCtx, this->actor.wallPoly, this->actor.wallBgId); - } - } - } + GameInteractor_Should(VB_REVALIDATE_CLIMBED_WALL, true, play, this, &sTouchedWallFlags, &yawDiff); sShapeYawToTouchedWall = ABS(yawDiff); @@ -11380,7 +11331,7 @@ void Player_ProcessSceneCollision(PlayState* play, Player* this) { if ((this->actor.bgCheckFlags & 0x200) && (sShapeYawToTouchedWall < 0x3000)) { CollisionPoly* wallPoly = this->actor.wallPoly; - if (ABS(wallPoly->normal.y) < 600 || (CVarGetInteger(CVAR_CHEAT("ClimbEverything"), 0) != 0)) { + if (GameInteractor_Should(VB_SURFACE_ANGLE_IS_CLIMBABLE, ABS(wallPoly->normal.y) < 600)) { f32 wallPolyNormalX = COLPOLY_GET_NORMAL(wallPoly->normal.x); f32 wallPolyNormalY = COLPOLY_GET_NORMAL(wallPoly->normal.y); f32 wallPolyNormalZ = COLPOLY_GET_NORMAL(wallPoly->normal.z);