summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJordan Longstaff <JordanLongstaff@users.noreply.github.com>2026-07-24 12:30:08 -0400
committerGitHub <noreply@github.com>2026-07-24 16:30:08 +0000
commit5af79defb6c780f46d24b3bdf42e71911535fcbd (patch)
treeaded889da4d10320b5b99e787f995c056e32a19f
parent9d1e5d281100c2d3ab40541759041e2c912ae154 (diff)
Ingo Race Once: add option to skip both races (#6975)
-rw-r--r--soh/soh/Enhancements/Minigames/IngoRaceOnce.cpp20
-rw-r--r--soh/soh/Enhancements/enhancementTypes.h6
-rw-r--r--soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h8
-rw-r--r--soh/soh/SohGui/SohMenuEnhancements.cpp14
-rw-r--r--soh/src/overlays/actors/ovl_En_In/z_en_in.c5
5 files changed, 48 insertions, 5 deletions
diff --git a/soh/soh/Enhancements/Minigames/IngoRaceOnce.cpp b/soh/soh/Enhancements/Minigames/IngoRaceOnce.cpp
index 8f1ed554f..8ba9b9044 100644
--- a/soh/soh/Enhancements/Minigames/IngoRaceOnce.cpp
+++ b/soh/soh/Enhancements/Minigames/IngoRaceOnce.cpp
@@ -1,15 +1,31 @@
+#include "soh/Enhancements/enhancementTypes.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#include "soh/ShipInit.hpp"
extern "C" {
#include "functions.h"
+extern SaveContext gSaveContext;
+extern PlayState* gPlayState;
}
#define CVAR_INGO_RACE_ONCE_NAME CVAR_ENHANCEMENT("IngoRaceOnce")
-#define CVAR_INGO_RACE_ONCE_VALUE CVarGetInteger(CVAR_INGO_RACE_ONCE_NAME, 0)
+#define CVAR_INGO_RACE_ONCE_VALUE CVarGetInteger(CVAR_INGO_RACE_ONCE_NAME, INGO_RACE_TWICE)
static void RegisterIngoRaceOnce() {
- COND_VB_SHOULD(VB_LINK_WIN_EPONA, CVAR_INGO_RACE_ONCE_NAME, { *should = true; });
+ COND_VB_SHOULD(VB_RACE_INGO, CVAR_INGO_RACE_ONCE_VALUE == INGO_RACE_NONE, {
+ s32 entranceIndex = va_arg(args, s32);
+ if (entranceIndex == 2 && (gSaveContext.eventInf[0] & 0x10) == 0) {
+ gPlayState->nextEntranceIndex = ENTR_LON_LON_RANCH_7;
+ gSaveContext.eventInf[0] = (gSaveContext.eventInf[0] & ~0xF) | 0x8006;
+ gPlayState->transitionType = TRANS_TYPE_FADE_WHITE;
+ gPlayState->transitionTrigger = TRANS_TRIGGER_START;
+ gSaveContext.timerState = TIMER_STATE_OFF;
+ Environment_ForcePlaySequence(NA_BGM_INGO);
+ *should = false;
+ }
+ })
+
+ COND_VB_SHOULD(VB_LINK_WIN_EPONA, CVAR_INGO_RACE_ONCE_VALUE != INGO_RACE_TWICE, { *should = true; });
}
static RegisterShipInitFunc initFunc(RegisterIngoRaceOnce, { CVAR_INGO_RACE_ONCE_NAME });
diff --git a/soh/soh/Enhancements/enhancementTypes.h b/soh/soh/Enhancements/enhancementTypes.h
index 7b4b79a6f..7311a00ab 100644
--- a/soh/soh/Enhancements/enhancementTypes.h
+++ b/soh/soh/Enhancements/enhancementTypes.h
@@ -119,6 +119,12 @@ typedef enum {
} SleepingWaterfallType;
typedef enum {
+ INGO_RACE_TWICE,
+ INGO_RACE_ONCE,
+ INGO_RACE_NONE,
+} IngoRaceType;
+
+typedef enum {
RANDOMIZE_OFF,
RANDOMIZE_ON_NEW_SCENE,
RANDOMIZE_ON_RANDO_GEN_ONLY,
diff --git a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h
index 58c60ba67..f81b47c91 100644
--- a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h
+++ b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h
@@ -2282,6 +2282,14 @@ typedef enum {
// true
// ```
// #### `args`
+ // - `s32` (entrance index)
+ VB_RACE_INGO,
+
+ // #### `result`
+ // ```c
+ // true
+ // ```
+ // #### `args`
// - `*EnRd`
VB_REDEAD_GIBDO_FREEZE_LINK,
diff --git a/soh/soh/SohGui/SohMenuEnhancements.cpp b/soh/soh/SohGui/SohMenuEnhancements.cpp
index b9a134705..dccbb7237 100644
--- a/soh/soh/SohGui/SohMenuEnhancements.cpp
+++ b/soh/soh/SohGui/SohMenuEnhancements.cpp
@@ -55,6 +55,12 @@ static const std::map<int32_t, const char*> skipForcedDialogOptions = {
{ FORCED_DIALOG_SKIP_ALL, "All" },
};
+static const std::map<int32_t, const char*> ingoRaceOptions = {
+ { INGO_RACE_TWICE, "Twice" },
+ { INGO_RACE_ONCE, "Once" },
+ { INGO_RACE_NONE, "None" },
+};
+
static const std::map<int32_t, const char*> timeTravelOptions = {
{ TIME_TRAVEL_DISABLED, "Disabled" },
{ TIME_TRAVEL_OOT, "Ocarina of Time" },
@@ -1621,9 +1627,13 @@ void SohMenu::AddMenuEnhancements() {
.Options(CheckboxOptions().Tooltip("Amy's block pushing puzzle instantly solved."));
AddWidget(path, "Ingo's Race", WIDGET_SEPARATOR_TEXT);
- AddWidget(path, "Only Race Once", WIDGET_CVAR_CHECKBOX)
+ AddWidget(path, "Number of Races", WIDGET_CVAR_COMBOBOX)
.CVar(CVAR_ENHANCEMENT("IngoRaceOnce"))
- .Options(CheckboxOptions().Tooltip("Link only needs to race Ingo once to win Epona."));
+ .Options(ComboboxOptions()
+ .ComboMap(ingoRaceOptions)
+ .DefaultIndex(INGO_RACE_TWICE)
+ .Tooltip("Number of races Link must win against Ingo to earn Epona. "
+ "Only works if Link is riding Epona, not the other rideable horse."));
path.column = SECTION_COLUMN_3;
AddWidget(path, "Rupee Diving Game", WIDGET_SEPARATOR_TEXT);
diff --git a/soh/src/overlays/actors/ovl_En_In/z_en_in.c b/soh/src/overlays/actors/ovl_En_In/z_en_in.c
index 7986b042c..aeee316cb 100644
--- a/soh/src/overlays/actors/ovl_En_In/z_en_in.c
+++ b/soh/src/overlays/actors/ovl_En_In/z_en_in.c
@@ -2,6 +2,7 @@
#include "overlays/actors/ovl_En_Horse/z_en_horse.h"
#include "objects/object_in/object_in.h"
#include "soh/ResourceManagerHelpers.h"
+#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS (ACTOR_FLAG_ATTENTION_ENABLED | ACTOR_FLAG_FRIENDLY | ACTOR_FLAG_UPDATE_CULLING_DISABLED)
@@ -694,7 +695,9 @@ void func_80A7A568(EnIn* this, PlayState* play) {
phi_a2 = 0;
transitionType = TRANS_TYPE_CIRCLE(TCA_NORMAL, TCC_BLACK, TCS_FAST);
}
- func_80A79BAC(this, play, phi_a2, transitionType);
+ if (GameInteractor_Should(VB_RACE_INGO, true, phi_a2)) {
+ func_80A79BAC(this, play, phi_a2, transitionType);
+ }
play->msgCtx.stateTimer = 0;
gSaveContext.eventInf[0] = (gSaveContext.eventInf[0] & ~0x8000) | 0x8000;
play->msgCtx.msgMode = MSGMODE_TEXT_CLOSING;