bombchu bowling setting: first item (#7070)

desirable for speedrun settings that want less variance. also use for moving rando logic into hooks
This commit is contained in:
Philip Dubé
2026-08-14 22:24:03 +00:00
committed by GitHub
parent afcfe4f7bf
commit ca0a17cfe7
7 changed files with 118 additions and 44 deletions
@@ -1,8 +1,10 @@
#include "soh/Enhancements/enhancementTypes.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#include "soh/ShipInit.hpp"
extern "C" {
#include "functions.h"
#include "src/overlays/actors/ovl_En_Bom_Bowl_Man/z_en_bom_bowl_man.h"
extern PlayState* gPlayState;
}
@@ -19,6 +21,9 @@ static constexpr s32 CUCCO_BOWLING_AMMO_DEFAULT = 10;
#define CVAR_BOWLING_AMMO_NAME CVAR_ENHANCEMENT("BombchuBowlingAmmo")
#define CVAR_BOWLING_AMMO_VALUE CVarGetInteger(CVAR_BOWLING_AMMO_NAME, CUCCO_BOWLING_AMMO_DEFAULT)
#define CVAR_BOWLING_FIRST_PRIZE_NAME CVAR_ENHANCEMENT("BombchuBowlingFirstPrize")
#define CVAR_BOWLING_FIRST_PRIZE_VALUE CVarGetInteger(CVAR_BOWLING_FIRST_PRIZE_NAME, BOWLING_FIRST_PRIZE_RANDOM)
static constexpr f32 CUCCO_SEARCH_Z = -520.0f;
typedef enum {
@@ -88,8 +93,20 @@ static void RegisterBombchuBowlingAmmo() {
});
}
static void RegisterBombchuBowlingFirstPrize() {
COND_VB_SHOULD(VB_SET_BOMBCHU_BOWLING_PRIZE_SELECT,
!IS_RANDO && CVAR_BOWLING_VALUE && (CVAR_BOWLING_FIRST_PRIZE_VALUE != BOWLING_FIRST_PRIZE_RANDOM), {
EnBomBowlMan* bowlMan = va_arg(args, EnBomBowlMan*);
s16 prizeSelect = CVAR_BOWLING_FIRST_PRIZE_VALUE - 1;
bowlMan->prizeSelect = prizeSelect;
*should = false;
});
}
static RegisterShipInitFunc initFunc_SmallCucco(RegisterBombchuBowlingNoSmallCucco,
{ CVAR_BOWLING_NAME, CVAR_CUCCO_SMALL_NAME });
static RegisterShipInitFunc initFunc_BigCucco(RegisterBombchuBowlingNoBigCucco,
{ CVAR_BOWLING_NAME, CVAR_CUCCO_BIG_NAME });
static RegisterShipInitFunc initFunc_Ammo(RegisterBombchuBowlingAmmo, { CVAR_BOWLING_NAME, CVAR_BOWLING_AMMO_NAME });
static RegisterShipInitFunc initFunc_FirstPrize(RegisterBombchuBowlingFirstPrize,
{ CVAR_BOWLING_NAME, CVAR_BOWLING_FIRST_PRIZE_NAME, "IS_RANDO" });
+10
View File
@@ -124,6 +124,16 @@ typedef enum {
INGO_RACE_NONE,
} IngoRaceType;
// The Bombchu Bowling prize cycle always runs in this order, only the entry it starts on is random
typedef enum {
BOWLING_FIRST_PRIZE_RANDOM,
BOWLING_FIRST_PRIZE_BOMB_BAG,
BOWLING_FIRST_PRIZE_PURPLE_RUPEE,
BOWLING_FIRST_PRIZE_BOMBCHUS,
BOWLING_FIRST_PRIZE_HEART_PIECE,
BOWLING_FIRST_PRIZE_BOMBS,
} BombchuBowlingFirstPrize;
typedef enum {
RANDOMIZE_OFF,
RANDOMIZE_ON_NEW_SCENE,
@@ -2477,6 +2477,23 @@ typedef enum {
// - `None`
VB_SET_BOMBCHU_BOWLING_AMMO,
// #### `result`
// ```c
// true
// ```
// #### `args`
// - `*EnBomBowlMan`
// - `s16*` (prize to show, an `ExItemType`)
VB_SET_BOMBCHU_BOWLING_PRIZE,
// #### `result`
// ```c
// true
// ```
// #### `args`
// - `*EnBomBowlMan`
VB_SET_BOMBCHU_BOWLING_PRIZE_SELECT,
// #### `result`
// ```c
// true
@@ -1,7 +1,6 @@
#pragma once
#include "randomizerTypes.h"
#include "z64save.h"
#include "item_override.h"
#include "hint.h"
#include "fishsanity.h"
@@ -45,6 +45,7 @@ extern "C" {
#include "src/overlays/actors/ovl_En_Box/z_en_box.h"
#include "src/overlays/actors/ovl_En_Skj/z_en_skj.h"
#include "src/overlays/actors/ovl_En_Hy/z_en_hy.h"
#include "src/overlays/actors/ovl_En_Bom_Bowl_Man/z_en_bom_bowl_man.h"
#include "src/overlays/actors/ovl_En_Bom_Bowl_Pit/z_en_bom_bowl_pit.h"
#include "src/overlays/actors/ovl_En_Ge1/z_en_ge1.h"
#include "src/overlays/actors/ovl_En_Ge2/z_en_ge2.h"
@@ -1940,6 +1941,36 @@ void RandomizerOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_l
*should = INV_CONTENT((RAND_GET_OPTION(RSK_BOMBCHU_BAG) ? ITEM_BOMBCHU : ITEM_BOMB)) != ITEM_NONE;
break;
}
case VB_SET_BOMBCHU_BOWLING_PRIZE_SELECT: {
EnBomBowlMan* bowlMan = va_arg(args, EnBomBowlMan*);
bowlMan->prizeSelect = 0;
*should = false;
break;
}
case VB_SET_BOMBCHU_BOWLING_PRIZE: {
EnBomBowlMan* bowlMan = va_arg(args, EnBomBowlMan*);
s16* prize = va_arg(args, s16*);
switch (bowlMan->prizeSelect) {
case 0:
*prize = Flags_GetItemGetInf(ITEMGETINF_11) ? EXITEM_PURPLE_RUPEE_BOWLING : EXITEM_BOMB_BAG_BOWLING;
break;
case 1:
*prize =
Flags_GetItemGetInf(ITEMGETINF_12) ? EXITEM_PURPLE_RUPEE_BOWLING : EXITEM_HEART_PIECE_BOWLING;
break;
case 2:
*prize = EXITEM_BOMBCHUS_BOWLING;
break;
case 3:
*prize = EXITEM_PURPLE_RUPEE_BOWLING;
break;
case 4:
*prize = EXITEM_BOMBS_BOWLING;
break;
}
*should = false;
break;
}
case VB_SHOULD_CHECK_FOR_FISHING_RECORD: {
f32 sFishOnHandLength = *va_arg(args, f32*);
*should = *should || ShouldGiveFishingPrize(sFishOnHandLength);
+18 -1
View File
@@ -5,13 +5,14 @@
#include <soh/OTRGlobals.h>
#include <soh/Enhancements/cosmetics/authenticGfxPatches.h>
#include <soh/Enhancements/TimeDisplay/TimeDisplay.h>
#include "soh/Enhancements/randomizer/randomizer.h"
#include "soh/Enhancements/Restorations/GetItemManipulation.h"
#include "soh/Enhancements/randomizer/SeedContext.h"
#include <ship/Context.h>
extern "C" {
#include "functions.h"
#include "variables.h"
#include "macros.h"
extern PlayState* gPlayState;
}
@@ -87,6 +88,15 @@ static const std::map<int32_t, const char*> sleepingWaterfallOptions = {
{ WATERFALL_NEVER, "Never" },
};
static const std::map<int32_t, const char*> bombchuBowlingFirstPrizeOptions = {
{ BOWLING_FIRST_PRIZE_RANDOM, "Random" },
{ BOWLING_FIRST_PRIZE_BOMB_BAG, "Bomb Bag" },
{ BOWLING_FIRST_PRIZE_PURPLE_RUPEE, "Purple Rupee" },
{ BOWLING_FIRST_PRIZE_BOMBCHUS, "Bombchus" },
{ BOWLING_FIRST_PRIZE_HEART_PIECE, "Heart Piece" },
{ BOWLING_FIRST_PRIZE_BOMBS, "Bombs" },
};
static const std::map<int32_t, const char*> allPowers = {
{ DAMAGE_VANILLA, "Vanilla (1x)" }, { DAMAGE_DOUBLE, "Double (2x)" },
{ DAMAGE_QUADRUPLE, "Quadruple (4x)" }, { DAMAGE_OCTUPLE, "Octuple (8x)" },
@@ -1524,6 +1534,13 @@ void SohMenu::AddMenuEnhancements() {
.DefaultValue(10)
.Format("%d bombchus")
.Tooltip("The number of Bombchus available at the start of the Bombchu Bowling minigame."));
AddWidget(path, "First Prize", WIDGET_CVAR_COMBOBOX)
.CVar(CVAR_ENHANCEMENT("BombchuBowlingFirstPrize"))
.PreFunc(bombchuBowlingDisabledFunc)
.Options(ComboboxOptions()
.ComboMap(bombchuBowlingFirstPrizeOptions)
.DefaultIndex(BOWLING_FIRST_PRIZE_RANDOM)
.Tooltip("The prize the cycle starts on."));
AddWidget(path, "Horseback Archery", WIDGET_SEPARATOR_TEXT);
AddWidget(path, "Customize Behavior##HBA", WIDGET_CVAR_CHECKBOX)
.CVar(CVAR_ENHANCEMENT("CustomizeHorsebackArchery"))
@@ -3,8 +3,6 @@
#include "overlays/actors/ovl_En_Syateki_Niw/z_en_syateki_niw.h"
#include "overlays/actors/ovl_En_Ex_Item/z_en_ex_item.h"
#include "objects/object_bg/object_bg.h"
#include "soh/OTRGlobals.h"
#include "soh/Enhancements/game-interactor/GameInteractor.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS \
@@ -88,7 +86,9 @@ void EnBomBowlMan_Init(Actor* thisx, PlayState* play2) {
}
}
this->prizeSelect = IS_RANDO ? 0 : (s16)Rand_ZeroFloat(4.99f);
if (GameInteractor_Should(VB_SET_BOMBCHU_BOWLING_PRIZE_SELECT, true, this)) {
this->prizeSelect = (s16)Rand_ZeroFloat(4.99f);
}
this->actor.targetMode = 1;
this->actionFunc = EnBomBowMan_SetupWaitAsleep;
}
@@ -174,17 +174,9 @@ void EnBomBowMan_CheckBeatenDC(EnBomBowlMan* this, PlayState* play) {
this->eyeMode = CHU_GIRL_EYES_AWAKE;
this->blinkTimer = (s16)Rand_ZeroFloat(60.0f) + 20;
bool bombchuBowlingClosed;
if (IS_RANDO) {
// when rando'd, check if we have bombchus if chus are in logic
// and check if we have a bomb bag if chus aren't in logic
u8 explosive = Randomizer_GetSettingValue(RSK_BOMBCHU_BAG) ? ITEM_BOMBCHU : ITEM_BOMB;
bombchuBowlingClosed = (INV_CONTENT(explosive) == ITEM_NONE);
} else {
// if not rando'd, check if we have beaten Dodongo's Cavern
bombchuBowlingClosed = !((Flags_GetEventChkInf(EVENTCHKINF_USED_DODONGOS_CAVERN_BLUE_WARP)) || BREG(2));
}
if (bombchuBowlingClosed) {
// Check for beaten Dodongo's Cavern
if (!GameInteractor_Should(VB_BE_ABLE_TO_PLAY_BOMBCHU_BOWLING,
(Flags_GetEventChkInf(EVENTCHKINF_USED_DODONGOS_CAVERN_BLUE_WARP)) || BREG(2))) {
this->actionFunc = EnBomBowMan_WaitNotBeatenDC;
} else {
this->actor.textId = 0x18;
@@ -407,39 +399,30 @@ void EnBomBowMan_ChooseShowPrize(EnBomBowlMan* this, PlayState* play) {
SkelAnime_Update(&this->skelAnime);
if (this->prizeRevealTimer == 0) {
switch (this->prizeSelect) {
case 0:
prizeTemp = EXITEM_BOMB_BAG_BOWLING;
if (Flags_GetItemGetInf(ITEMGETINF_11)) {
if (GameInteractor_Should(VB_SET_BOMBCHU_BOWLING_PRIZE, true, this, &prizeTemp)) {
switch (this->prizeSelect) {
case 0:
prizeTemp = EXITEM_BOMB_BAG_BOWLING;
if (Flags_GetItemGetInf(ITEMGETINF_11)) {
prizeTemp = EXITEM_PURPLE_RUPEE_BOWLING;
}
break;
case 1:
prizeTemp = EXITEM_PURPLE_RUPEE_BOWLING;
}
break;
case 1:
if (!IS_RANDO) {
prizeTemp = EXITEM_PURPLE_RUPEE_BOWLING;
} else {
break;
case 2:
prizeTemp = EXITEM_BOMBCHUS_BOWLING;
break;
case 3:
prizeTemp = EXITEM_HEART_PIECE_BOWLING;
if (Flags_GetItemGetInf(ITEMGETINF_12)) {
prizeTemp = EXITEM_PURPLE_RUPEE_BOWLING;
}
}
break;
case 2:
prizeTemp = EXITEM_BOMBCHUS_BOWLING;
break;
case 3:
if (!IS_RANDO) {
prizeTemp = EXITEM_HEART_PIECE_BOWLING;
if (Flags_GetItemGetInf(ITEMGETINF_12)) {
prizeTemp = EXITEM_PURPLE_RUPEE_BOWLING;
}
} else {
prizeTemp = EXITEM_PURPLE_RUPEE_BOWLING;
}
break;
case 4:
prizeTemp = EXITEM_BOMBS_BOWLING;
break;
break;
case 4:
prizeTemp = EXITEM_BOMBS_BOWLING;
break;
}
}
this->prizeIndex = prizeTemp;