summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorReppan <72985260+Jepvid@users.noreply.github.com>2026-06-28 15:41:59 +0200
committerGitHub <noreply@github.com>2026-06-28 13:41:59 +0000
commita629af2e9f1942e4914d0be27f207e572b40eae4 (patch)
tree617253bb0a865b75f68d8e865ea76452f9ce54e0
parent8d2d5c98287884407085830fbba417d17b6c7623 (diff)
[Enhancement] Improved Roll (#6604)
-rw-r--r--soh/soh/Enhancements/TimeSavers/ImprovedRoll.cpp36
-rw-r--r--soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h21
-rw-r--r--soh/soh/SohGui/SohMenuEnhancements.cpp10
-rw-r--r--soh/src/overlays/actors/ovl_player_actor/z_player.c5
4 files changed, 72 insertions, 0 deletions
diff --git a/soh/soh/Enhancements/TimeSavers/ImprovedRoll.cpp b/soh/soh/Enhancements/TimeSavers/ImprovedRoll.cpp
new file mode 100644
index 000000000..3c3edf54d
--- /dev/null
+++ b/soh/soh/Enhancements/TimeSavers/ImprovedRoll.cpp
@@ -0,0 +1,36 @@
+#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
+#include "soh/ShipInit.hpp"
+#include "global.h"
+
+extern "C" {
+void Player_SetupRoll(Player* player, PlayState* play);
+}
+
+#define CVAR_ROLL_CHAIN CVAR_ENHANCEMENT("ImprovedRoll")
+#define CVAR_ROLL_STEER CVAR_ENHANCEMENT("ImprovedRollSteering")
+
+void ImprovedRoll_Register() {
+ COND_VB_SHOULD(VB_PLAYER_ROLL_CHAIN, CVarGetInteger(CVAR_ROLL_CHAIN, 0), {
+ Player* player = va_arg(args, Player*);
+ PlayState* play = va_arg(args, PlayState*);
+ Input* controlInput = va_arg(args, Input*);
+ s32 floorType = va_arg(args, s32);
+ if ((player->skelAnime.curFrame >= 15.0f) && CHECK_BTN_ALL(controlInput->press.button, BTN_A) &&
+ (floorType != 7)) {
+ Player_SetupRoll(player, play);
+ *should = true;
+ }
+ });
+
+ COND_VB_SHOULD(VB_PLAYER_ROLL_STEER, CVarGetInteger(CVAR_ROLL_CHAIN, 0) && CVarGetInteger(CVAR_ROLL_STEER, 0), {
+ Player* player = va_arg(args, Player*);
+ PlayState* play = va_arg(args, PlayState*);
+ s16 yawTarget = (s16)va_arg(args, int);
+ if (!CHECK_BTN_ALL(play->state.input[0].cur.button, BTN_Z)) {
+ Math_ScaledStepToS(&player->actor.shape.rot.y, yawTarget, 0x200);
+ }
+ *should = false;
+ });
+}
+
+static RegisterShipInitFunc initFunc(ImprovedRoll_Register, { CVAR_ROLL_CHAIN, CVAR_ROLL_STEER });
diff --git a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h
index d6624b9c7..6e3f9e4a1 100644
--- a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h
+++ b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h
@@ -2030,6 +2030,27 @@ typedef enum {
// #### `result`
// ```c
+ // false
+ // ```
+ // #### `args`
+ // - `*Player`
+ // - `*PlayState`
+ // - `*Input` (sControlInput)
+ // - `s32` (sFloorType)
+ VB_PLAYER_ROLL_CHAIN,
+
+ // #### `result`
+ // ```c
+ // false
+ // ```
+ // #### `args`
+ // - `*Player`
+ // - `*PlayState`
+ // - `s16 yawTarget` (stick world-space yaw, promoted to int in va_list)
+ VB_PLAYER_ROLL_STEER,
+
+ // #### `result`
+ // ```c
// item == ITEM_SAW
// ```
// #### `args`
diff --git a/soh/soh/SohGui/SohMenuEnhancements.cpp b/soh/soh/SohGui/SohMenuEnhancements.cpp
index dc04eebac..afdb28730 100644
--- a/soh/soh/SohGui/SohMenuEnhancements.cpp
+++ b/soh/soh/SohGui/SohMenuEnhancements.cpp
@@ -462,6 +462,16 @@ void SohMenu::AddMenuEnhancements() {
.CVar(CVAR_ENHANCEMENT("FastChests"))
.Options(CheckboxOptions().Tooltip("Makes Link always kick the chest to open it, instead of doing the longer "
"chest opening animation for major items."));
+ AddWidget(path, "Improved Roll", WIDGET_CVAR_CHECKBOX)
+ .CVar(CVAR_ENHANCEMENT("ImprovedRoll"))
+ .Options(CheckboxOptions().Tooltip(
+ "Allows Link to chain a new roll by pressing A during a roll, maintaining maximum roll speed."));
+ AddWidget(path, "Improved Roll Steering", WIDGET_CVAR_CHECKBOX)
+ .CVar(CVAR_ENHANCEMENT("ImprovedRollSteering"))
+ .PreFunc([](WidgetInfo& info) { info.isHidden = !CVarGetInteger(CVAR_ENHANCEMENT("ImprovedRoll"), 0); })
+ .Options(CheckboxOptions().Tooltip(
+ "Allows slight directional steering with the control stick while rolling. "
+ "Steering is automatically disabled while Z is held, preserving Z-target roll glitch setups."));
AddWidget(path, "Skip Water Take Breath Animation", WIDGET_CVAR_CHECKBOX)
.CVar(CVAR_ENHANCEMENT("SkipSwimDeepEndAnim"))
.Options(CheckboxOptions().Tooltip("Skips Link's taking breath animation after coming up from water. "
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 9b6712e32..171cb09ac 100644
--- a/soh/src/overlays/actors/ovl_player_actor/z_player.c
+++ b/soh/src/overlays/actors/ovl_player_actor/z_player.c
@@ -9834,6 +9834,10 @@ void Player_Action_Roll(Player* this, PlayState* play) {
}
}
+ if (GameInteractor_Should(VB_PLAYER_ROLL_CHAIN, false, this, play, sControlInput, sFloorType)) {
+ return;
+ }
+
if ((this->skelAnime.curFrame < 15.0f) || !Player_ActionHandler_7(this, play)) {
if (this->skelAnime.curFrame >= 20.0f) {
func_8083A060(this, play);
@@ -9852,6 +9856,7 @@ void Player_Action_Roll(Player* this, PlayState* play) {
speedTarget = 3.0f;
}
+ GameInteractor_Should(VB_PLAYER_ROLL_STEER, false, this, play, yawTarget);
func_8083DF68(this, speedTarget, this->actor.shape.rot.y);
if (func_8084269C(play, this)) {