summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris <f1ana@users.noreply.github.com>2026-06-30 00:10:56 -0400
committerGitHub <noreply@github.com>2026-06-30 04:10:56 +0000
commitc5835669fec5c804156943b5cd6e8da3a1bd6489 (patch)
tree353450b5ef3612ff1befd1375582a621ad05f26e
parent1fd3a467cdbfc125935f8816d53b7b82421bd80a (diff)
Hookifying Various Cheats and FixVineFall (#6827)
-rw-r--r--soh/soh/Enhancements/Cheats/ClimbEverything.cpp10
-rw-r--r--soh/soh/Enhancements/Cheats/FireproofDekuShield.cpp8
-rw-r--r--soh/soh/Enhancements/Cheats/HookshotEverything.cpp8
-rw-r--r--soh/soh/Enhancements/Cheats/Infinite/EponaBoost.cpp10
-rw-r--r--soh/soh/Enhancements/Cheats/NoClip.cpp13
-rw-r--r--soh/soh/Enhancements/Fixes/FixVineFall.cpp72
-rw-r--r--soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h67
-rw-r--r--soh/src/code/z_bgcheck.c7
-rw-r--r--soh/src/code/z_parameter.c2
-rw-r--r--soh/src/overlays/actors/ovl_En_Horse/z_en_horse.c3
-rw-r--r--soh/src/overlays/actors/ovl_player_actor/z_player.c57
11 files changed, 199 insertions, 58 deletions
diff --git a/soh/soh/Enhancements/Cheats/ClimbEverything.cpp b/soh/soh/Enhancements/Cheats/ClimbEverything.cpp
new file mode 100644
index 000000000..62614ac3b
--- /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 000000000..4b8e4a135
--- /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 000000000..be97c0956
--- /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 000000000..0baa3e191
--- /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 000000000..1ad630477
--- /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 000000000..f51891673
--- /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 c130a65c6..6998709b6 100644
--- a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h
+++ b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h
@@ -287,6 +287,14 @@ typedef enum {
// #### `result`
// ```c
+ // this->currentShield == PLAYER_SHIELD_DEKU
+ // ```
+ // #### `args`
+ // - `*Player`
+ VB_BURN_SHIELD,
+
+ // #### `result`
+ // ```c
// true
// ```
// #### `args`
@@ -372,6 +380,14 @@ typedef enum {
// true
// ```
// #### `args`
+ // - `*EnHorse`
+ VB_CONSUME_EPONA_BOOST,
+
+ // #### `result`
+ // ```c
+ // true
+ // ```
+ // #### `args`
// - `*EnDoor`
VB_CONSUME_SMALL_KEY,
@@ -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
@@ -1647,6 +1671,14 @@ typedef enum {
// true
// ```
// #### `args`
+ // - `*Actor`
+ VB_PERFORM_WALL_COLLISION_CHECK,
+
+ // #### `result`
+ // ```c
+ // true
+ // ```
+ // #### `args`
// - `*BossGanondrof`
VB_PHANTOM_GANON_DEATH_SCENE,
@@ -2150,6 +2182,17 @@ typedef enum {
// true
// ```
// #### `args`
+ // - `*PlayState`
+ // - `*Player`
+ // - `*u32`
+ // - `*s16`
+ VB_REVALIDATE_CLIMBED_WALL,
+
+ // #### `result`
+ // ```c
+ // true
+ // ```
+ // #### `args`
// - None
VB_REVERT_SPOILING_ITEMS,
@@ -2490,6 +2533,30 @@ typedef enum {
// #### `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
// ```
// #### `args`
diff --git a/soh/src/code/z_bgcheck.c b/soh/src/code/z_bgcheck.c
index e98e66e44..3cce952f5 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 <assert.h>
#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 c378d15f4..9a63d8de5 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 502b07301..fe8bd8b83 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 <assert.h>
#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 171cb09ac..b1d2f4c47 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);