From 2c5762a0f287ee9de16eb8ed916275cf230fc60c Mon Sep 17 00:00:00 2001 From: Jordan Longstaff Date: Sat, 25 Jul 2026 19:07:07 -0400 Subject: [PATCH] [Enhancement] Dampe's Race awards both rewards at once (#6980) --- .../Fixes/DampeGoingBackwards.cpp | 19 ++++++++++ .../Minigames/DampeBothPrizes.cpp | 24 +++++++++++++ .../vanilla-behavior/GIVanillaBehavior.h | 16 +++++++++ .../Enhancements/randomizer/hook_handlers.cpp | 6 ++++ soh/soh/SohGui/SohMenuEnhancements.cpp | 7 ++++ .../actors/ovl_En_Po_Relay/z_en_po_relay.c | 35 ++++--------------- 6 files changed, 78 insertions(+), 29 deletions(-) create mode 100644 soh/soh/Enhancements/Fixes/DampeGoingBackwards.cpp create mode 100644 soh/soh/Enhancements/Minigames/DampeBothPrizes.cpp diff --git a/soh/soh/Enhancements/Fixes/DampeGoingBackwards.cpp b/soh/soh/Enhancements/Fixes/DampeGoingBackwards.cpp new file mode 100644 index 0000000000..be5b25a589 --- /dev/null +++ b/soh/soh/Enhancements/Fixes/DampeGoingBackwards.cpp @@ -0,0 +1,19 @@ +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" +#include "soh/ShipInit.hpp" + +extern "C" { +#include "functions.h" +#include "macros.h" +} + +#define CVAR_DAMPE_BACKWARDS_NAME CVAR_ENHANCEMENT("FixDampeGoingBackwards") +#define CVAR_DAMPE_BACKWARDS_VALUE CVarGetInteger(CVAR_DAMPE_BACKWARDS_NAME, 0) + +static void RegisterDampeGoingBackwardsFix() { + COND_VB_SHOULD(VB_DAMPE_GO_BACKWARDS, CVAR_DAMPE_BACKWARDS_VALUE, { + f32* speed = va_arg(args, f32*); + *speed = ABS(*speed); + }); +} + +static RegisterShipInitFunc initFunc(RegisterDampeGoingBackwardsFix, { CVAR_DAMPE_BACKWARDS_NAME }); diff --git a/soh/soh/Enhancements/Minigames/DampeBothPrizes.cpp b/soh/soh/Enhancements/Minigames/DampeBothPrizes.cpp new file mode 100644 index 0000000000..0b50f3eda0 --- /dev/null +++ b/soh/soh/Enhancements/Minigames/DampeBothPrizes.cpp @@ -0,0 +1,24 @@ +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" +#include "soh/ShipInit.hpp" + +extern "C" { +#include "functions.h" +#include "macros.h" +extern SaveContext gSaveContext; +extern PlayState* gPlayState; +} + +#define CVAR_DAMPE_BOTH_PRIZES_NAME CVAR_ENHANCEMENT("DampeBothPrizes") +#define CVAR_DAMPE_BOTH_PRIZES_VALUE CVarGetInteger(CVAR_DAMPE_BOTH_PRIZES_NAME, 0) + +static void RegisterDampeSecondPrize() { + COND_VB_SHOULD(VB_DAMPE_AWARD_SECOND_PRIZE, CVAR_DAMPE_BOTH_PRIZES_VALUE || IS_RANDO, { + if (!*should) { + Flags_SetTempClear(gPlayState, 4); + HIGH_SCORE(HS_DAMPE_RACE) = gSaveContext.timerSeconds; + *should = true; + } + }); +} + +static RegisterShipInitFunc initFunc(RegisterDampeSecondPrize, { CVAR_DAMPE_BOTH_PRIZES_NAME, "IS_RANDO" }); diff --git a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h index 2c02cfe94d..0ae138df4a 100644 --- a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h +++ b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h @@ -460,6 +460,14 @@ typedef enum { // - None VB_CRAWL_SPEED_INCREASE, + // #### `result` + // ```c + // this->hookshotSlotFull != 0 + // ``` + // #### `args` + // - None + VB_DAMPE_AWARD_SECOND_PRIZE, + // #### `result` // ```c // this->actionTimer == 0 && Rand_ZeroOne() < 0.03f @@ -468,6 +476,14 @@ typedef enum { // - `*EnPoRelay` VB_DAMPE_DROP_FLAME, + // #### `result` + // ```c + // true + // ``` + // #### `args` + // - `*f32` (speed + VB_DAMPE_GO_BACKWARDS, + // #### `result` // ```c // !Flags_GetItemGetInf(ITEMGETINF_1C) diff --git a/soh/soh/Enhancements/randomizer/hook_handlers.cpp b/soh/soh/Enhancements/randomizer/hook_handlers.cpp index c9b8de0fe3..75c315ca04 100644 --- a/soh/soh/Enhancements/randomizer/hook_handlers.cpp +++ b/soh/soh/Enhancements/randomizer/hook_handlers.cpp @@ -1536,6 +1536,12 @@ void RandomizerOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_l case VB_DEKU_THEATER_FINISH_GIVING_PRIZE: *should = true; break; + case VB_DAMPE_AWARD_SECOND_PRIZE: + if (!*should) { + Flags_SetTreasure(gPlayState, 0x1E); + *should = true; + } + break; case VB_FROGS_GO_TO_IDLE: { EnFr* enFr = va_arg(args, EnFr*); diff --git a/soh/soh/SohGui/SohMenuEnhancements.cpp b/soh/soh/SohGui/SohMenuEnhancements.cpp index e0dfbc5f89..5ded153733 100644 --- a/soh/soh/SohGui/SohMenuEnhancements.cpp +++ b/soh/soh/SohGui/SohMenuEnhancements.cpp @@ -1692,6 +1692,13 @@ void SohMenu::AddMenuEnhancements() { "fixing a vanilla bug. Always enabled in randomizer.")); AddWidget(path, "Multiple Prizes", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Dampe's Race", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("DampeBothPrizes")) + .PreFunc([](WidgetInfo& info) { + info.options->disabled = IS_RANDO && GameInteractor::IsSaveLoaded(true); + info.options->disabledTooltip = "This setting is forcefully enabled when you are playing a Randomizer."; + }) + .Options(CheckboxOptions().Tooltip("Dampe awards both prizes on the first race, not just the Hookshot.")); AddWidget(path, "Horseback Archery", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("HorsebackArcheryBothPrizes")) .Options(CheckboxOptions().Tooltip("Link can win both Horseback Archery prizes in one attempt")); diff --git a/soh/src/overlays/actors/ovl_En_Po_Relay/z_en_po_relay.c b/soh/src/overlays/actors/ovl_En_Po_Relay/z_en_po_relay.c index 4ede239434..bd719a044d 100644 --- a/soh/src/overlays/actors/ovl_En_Po_Relay/z_en_po_relay.c +++ b/soh/src/overlays/actors/ovl_En_Po_Relay/z_en_po_relay.c @@ -239,9 +239,7 @@ void EnPoRelay_Race(EnPoRelay* this, PlayState* play) { speed = 3.5f; } - if (CVarGetInteger(CVAR_ENHANCEMENT("FixDampeGoingBackwards"), false)) { - speed = ABS(speed); - } + GameInteractor_Should(VB_DAMPE_GO_BACKWARDS, true, &speed); multiplier = 250.0f - this->actor.xzDistToPlayer; multiplier = CLAMP_MIN(multiplier, 0.0f); @@ -338,42 +336,21 @@ void EnPoRelay_DisappearAndReward(EnPoRelay* this, PlayState* play) { } } if (Math_StepToF(&this->actor.scale.x, 0.0f, 0.001f) != 0) { - if (!IS_RANDO) { - if (this->hookshotSlotFull != 0) { - sp60.x = this->actor.world.pos.x; - sp60.y = this->actor.floorHeight; - sp60.z = this->actor.world.pos.z; - if (gSaveContext.timerSeconds < HIGH_SCORE(HS_DAMPE_RACE)) { - HIGH_SCORE(HS_DAMPE_RACE) = gSaveContext.timerSeconds; - } - if (Flags_GetCollectible(play, this->actor.params) == 0 && gSaveContext.timerSeconds <= 60) { - Item_DropCollectible2(play, &sp60, (this->actor.params << 8) + (0x4000 | ITEM00_HEART_PIECE)); - } else { - Actor_Spawn(&play->actorCtx, play, ACTOR_EN_ITEM00, sp60.x, sp60.y, sp60.z, 0, 0, 0, 2); - } - } else { - Flags_SetTempClear(play, 4); - HIGH_SCORE(HS_DAMPE_RACE) = gSaveContext.timerSeconds; - } - } else { + if (GameInteractor_Should(VB_DAMPE_AWARD_SECOND_PRIZE, this->hookshotSlotFull != 0)) { sp60.x = this->actor.world.pos.x; sp60.y = this->actor.floorHeight; sp60.z = this->actor.world.pos.z; - - if (this->hookshotSlotFull == 0) { - Flags_SetTempClear(play, 4); - Flags_SetTreasure(gPlayState, 0x1E); - HIGH_SCORE(HS_DAMPE_RACE) = gSaveContext.timerSeconds; - } - if (gSaveContext.timerSeconds < HIGH_SCORE(HS_DAMPE_RACE)) { HIGH_SCORE(HS_DAMPE_RACE) = gSaveContext.timerSeconds; } if (Flags_GetCollectible(play, this->actor.params) == 0 && gSaveContext.timerSeconds <= 60) { Item_DropCollectible2(play, &sp60, (this->actor.params << 8) + (0x4000 | ITEM00_HEART_PIECE)); - } else if (Flags_GetCollectible(play, this->actor.params) != 0) { + } else { Actor_Spawn(&play->actorCtx, play, ACTOR_EN_ITEM00, sp60.x, sp60.y, sp60.z, 0, 0, 0, 2); } + } else { + Flags_SetTempClear(play, 4); + HIGH_SCORE(HS_DAMPE_RACE) = gSaveContext.timerSeconds; } Actor_Kill(&this->actor); }