summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPepe20129 <72659707+Pepe20129@users.noreply.github.com>2025-08-13 21:08:32 +0200
committerGitHub <noreply@github.com>2025-08-13 12:08:32 -0700
commit7b4df9bdb2ebde2bdd6581c10ffdc1aea4ee1573 (patch)
tree6c805ed37b7a34523a9e47a5d620604da8ef5ec5
parentfadae4997740d6073d6ce01db2950c8a24b3cbb0 (diff)
Boss Rush cleanup (#5652)
* Reduce stuff in the `.h`s & use `COND_HOOK` + variants * Move some boss rush stuff from `z_file_choose.c` * clang * Update BossRush.cpp
-rw-r--r--soh/include/z64.h2
-rw-r--r--soh/include/z64save.h2
-rw-r--r--soh/soh/Enhancements/boss-rush/BossRush.cpp347
-rw-r--r--soh/soh/Enhancements/boss-rush/BossRush.h33
-rw-r--r--soh/soh/Enhancements/boss-rush/BossRushTypes.h91
-rw-r--r--soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h14
-rw-r--r--soh/soh/Enhancements/mods.cpp2
-rw-r--r--soh/src/code/z_parameter.c10
-rw-r--r--soh/src/overlays/actors/ovl_Door_Warp1/z_door_warp1.c8
-rw-r--r--soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c162
10 files changed, 342 insertions, 329 deletions
diff --git a/soh/include/z64.h b/soh/include/z64.h
index 79ecdabcd..70b877f30 100644
--- a/soh/include/z64.h
+++ b/soh/include/z64.h
@@ -1522,7 +1522,7 @@ typedef struct {
/* 0x34 */ s32 isEnabled;
} StickDirectionPrompt;
-typedef struct {
+typedef struct FileChooseContext {
/* 0x00000 */ GameState state;
/* 0x000A4 */ Vtx* windowVtx;
/* 0x000A8 */ u8* staticSegment;
diff --git a/soh/include/z64save.h b/soh/include/z64save.h
index 63a9b1d10..bf1342360 100644
--- a/soh/include/z64save.h
+++ b/soh/include/z64save.h
@@ -7,7 +7,7 @@
#include "soh/Enhancements/randomizer/randomizerTypes.h"
#include "soh/Enhancements/gameplaystats.h"
#include "soh/Enhancements/randomizer/randomizer_entrance.h"
-#include "soh/Enhancements/boss-rush/BossRushTypes.h"
+#include "soh/Enhancements/boss-rush/BossRush.h"
typedef enum {
/* 0x0 */ MAGIC_STATE_IDLE, // Regular gameplay
diff --git a/soh/soh/Enhancements/boss-rush/BossRush.cpp b/soh/soh/Enhancements/boss-rush/BossRush.cpp
index 979f0ad6a..af5c04dd2 100644
--- a/soh/soh/Enhancements/boss-rush/BossRush.cpp
+++ b/soh/soh/Enhancements/boss-rush/BossRush.cpp
@@ -2,6 +2,8 @@
#include "soh/OTRGlobals.h"
#include "soh/Enhancements/game-interactor/GameInteractor.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
+#include "soh_assets.h"
+#include "soh/frame_interpolation.h"
#include <array>
#include <string>
@@ -14,14 +16,87 @@ extern "C" {
#include "src/overlays/actors/ovl_Boss_Goma/z_boss_goma.h"
#include "src/overlays/actors/ovl_Boss_Mo/z_boss_mo.h"
#include "src/overlays/actors/ovl_Door_Warp1/z_door_warp1.h"
-extern PlayState* gPlayState;
-
-Gfx* KaleidoScope_QuadTextureIA8(Gfx* gfx, void* texture, s16 width, s16 height, u16 point);
+#include "src/overlays/gamestates/ovl_file_choose/file_choose.h"
+#include "objects/gameplay_keep/gameplay_keep.h"
#include "textures/icon_item_nes_static/icon_item_nes_static.h"
#include "textures/icon_item_ger_static/icon_item_ger_static.h"
#include "textures/icon_item_fra_static/icon_item_fra_static.h"
+
+extern PlayState* gPlayState;
+
+Gfx* KaleidoScope_QuadTextureIA8(Gfx* gfx, void* texture, s16 width, s16 height, u16 point);
+void FileChoose_UpdateStickDirectionPromptAnim(GameState* thisx);
+void FileChoose_DrawTextRec(GraphicsContext* gfxCtx, s32 r, s32 g, s32 b, s32 a, f32 x, f32 y, f32 z, s32 s, s32 t,
+ f32 dx, f32 dy);
}
+typedef enum {
+ BR_CHOICE_BOSSES_ALL,
+ BR_CHOICE_BOSSES_CHILD,
+ BR_CHOICE_BOSSES_ADULT,
+ BR_CHOICE_BOSSES_GANONDORF_GANON
+} BossRushBossesChoices;
+
+typedef enum {
+ BR_CHOICE_HEARTS_10,
+ BR_CHOICE_HEARTS_15,
+ BR_CHOICE_HEARTS_20,
+ BR_CHOICE_HEARTS_3,
+ BR_CHOICE_HEARTS_5,
+ BR_CHOICE_HEARTS_7
+} BossRushHeartsChoices;
+
+typedef enum {
+ BR_CHOICE_AMMO_LIMITED,
+ BR_CHOICE_AMMO_FULL,
+ BR_CHOICE_AMMO_MAXED,
+} BossRushAmmoChoices;
+
+typedef enum {
+ BR_CHOICE_HEAL_GANONDORF,
+ BR_CHOICE_HEAL_EVERYBOSS,
+ BR_CHOICE_HEAL_NEVER,
+} BossRushHealChoices;
+
+typedef enum {
+ BR_CHOICE_MAGIC_SINGLE,
+ BR_CHOICE_MAGIC_DOUBLE,
+} BossRushMagicChoices;
+
+typedef enum {
+ BR_CHOICE_BGS_NO,
+ BR_CHOICE_BGS_YES,
+} BossRushBgsChoices;
+
+typedef enum {
+ BR_CHOICE_BOTTLE_NO,
+ BR_CHOICE_BOTTLE_EMPTY,
+ BR_CHOICE_BOTTLE_FAIRY,
+ BR_CHOICE_BOTTLE_REDPOTION,
+ BR_CHOICE_BOTTLE_GREENPOTION,
+ BR_CHOICE_BOTTLE_BLUEPOTION
+} BossRushBottleChoices;
+
+typedef enum {
+ BR_CHOICE_LONGSHOT_NO,
+ BR_CHOICE_LONGSHOT_YES,
+} BossRushLongshotChoices;
+
+typedef enum {
+ BR_CHOICE_HOVERBOOTS_NO,
+ BR_CHOICE_HOVERBOOTS_YES,
+} BossRushHoverBootsChoices;
+
+typedef enum {
+ BR_CHOICE_BUNNYHOOD_NO,
+ BR_CHOICE_BUNNYHOOD_YES,
+} BossRushBunnyHoodChoices;
+
+typedef enum {
+ BR_CHOICE_TIMER_YES,
+ BR_CHOICE_TIMER_NO,
+} BossRushTimerChoices;
+
typedef struct BossRushSetting {
std::array<std::string, LANGUAGE_MAX> name;
std::vector<std::array<std::string, LANGUAGE_MAX>> choices;
@@ -114,6 +189,180 @@ u8 BossRush_GetSettingOptionsAmount(u8 optionIndex) {
return static_cast<u8>(BossRushOptions[optionIndex].choices.size());
}
+void FileChoose_UpdateBossRushMenu(GameState* gameState) {
+ static s8 sLastBossRushOptionIndex = -1;
+ static s8 sLastBossRushOptionValue = -1;
+
+ FileChoose_UpdateStickDirectionPromptAnim(gameState);
+ FileChooseContext* fileChooseContext = (FileChooseContext*)gameState;
+ Input* input = &fileChooseContext->state.input[0];
+ bool dpad = CVarGetInteger(CVAR_SETTING("DpadInText"), 0);
+
+ // Fade in elements after opening Boss Rush options menu
+ fileChooseContext->bossRushUIAlpha += 25;
+ if (fileChooseContext->bossRushUIAlpha > 255) {
+ fileChooseContext->bossRushUIAlpha = 255;
+ }
+
+ // Animate up/down arrows.
+ fileChooseContext->bossRushArrowOffset += 1;
+ if (fileChooseContext->bossRushArrowOffset >= 30) {
+ fileChooseContext->bossRushArrowOffset = 0;
+ }
+
+ // Move menu selection up or down.
+ if (ABS(fileChooseContext->stickRelY) > 30 || (dpad && CHECK_BTN_ANY(input->press.button, BTN_DDOWN | BTN_DUP))) {
+ // Move down
+ if (fileChooseContext->stickRelY < -30 || (dpad && CHECK_BTN_ANY(input->press.button, BTN_DDOWN))) {
+ // When selecting past the last option, cycle back to the first option.
+ if ((fileChooseContext->bossRushIndex + 1) > BR_OPTIONS_MAX - 1) {
+ fileChooseContext->bossRushIndex = 0;
+ fileChooseContext->bossRushOffset = 0;
+ } else {
+ fileChooseContext->bossRushIndex++;
+ // When last visible option is selected when moving down, offset the list down by one.
+ if (fileChooseContext->bossRushIndex - fileChooseContext->bossRushOffset >
+ BOSSRUSH_MAX_OPTIONS_ON_SCREEN - 1) {
+ fileChooseContext->bossRushOffset++;
+ }
+ }
+ } else if (fileChooseContext->stickRelY > 30 || (dpad && CHECK_BTN_ANY(input->press.button, BTN_DUP))) {
+ // When selecting past the first option, cycle back to the last option and offset the list to view it
+ // properly.
+ if ((fileChooseContext->bossRushIndex - 1) < 0) {
+ fileChooseContext->bossRushIndex = BR_OPTIONS_MAX - 1;
+ fileChooseContext->bossRushOffset =
+ fileChooseContext->bossRushIndex - BOSSRUSH_MAX_OPTIONS_ON_SCREEN + 1;
+ } else {
+ // When first visible option is selected when moving up, offset the list up by one.
+ if (fileChooseContext->bossRushIndex - fileChooseContext->bossRushOffset == 0) {
+ fileChooseContext->bossRushOffset--;
+ }
+ fileChooseContext->bossRushIndex--;
+ }
+ }
+
+ Audio_PlaySoundGeneral(NA_SE_SY_FSEL_CURSOR, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale,
+ &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb);
+ }
+
+ // Cycle through choices for currently selected option.
+ if (ABS(fileChooseContext->stickRelX) > 30 ||
+ (dpad && CHECK_BTN_ANY(input->press.button, BTN_DLEFT | BTN_DRIGHT))) {
+ if (fileChooseContext->stickRelX > 30 || (dpad && CHECK_BTN_ANY(input->press.button, BTN_DRIGHT))) {
+ // If exceeding the amount of choices for the selected option, cycle back to the first.
+ if ((gSaveContext.ship.quest.data.bossRush.options[fileChooseContext->bossRushIndex] + 1) ==
+ BossRush_GetSettingOptionsAmount(fileChooseContext->bossRushIndex)) {
+ gSaveContext.ship.quest.data.bossRush.options[fileChooseContext->bossRushIndex] = 0;
+ } else {
+ gSaveContext.ship.quest.data.bossRush.options[fileChooseContext->bossRushIndex]++;
+ }
+ } else if (fileChooseContext->stickRelX < -30 || (dpad && CHECK_BTN_ANY(input->press.button, BTN_DLEFT))) {
+ // If cycling back when already at the first choice for the selected option, cycle back to the last choice.
+ if ((gSaveContext.ship.quest.data.bossRush.options[fileChooseContext->bossRushIndex] - 1) < 0) {
+ gSaveContext.ship.quest.data.bossRush.options[fileChooseContext->bossRushIndex] =
+ BossRush_GetSettingOptionsAmount(fileChooseContext->bossRushIndex) - 1;
+ } else {
+ gSaveContext.ship.quest.data.bossRush.options[fileChooseContext->bossRushIndex]--;
+ }
+ }
+
+ Audio_PlaySoundGeneral(NA_SE_SY_FSEL_CURSOR, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale,
+ &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb);
+ }
+
+ if (sLastBossRushOptionIndex != fileChooseContext->bossRushIndex ||
+ sLastBossRushOptionValue != gSaveContext.ship.quest.data.bossRush.options[fileChooseContext->bossRushIndex]) {
+ GameInteractor_ExecuteOnUpdateFileBossRushOptionSelection(
+ fileChooseContext->bossRushIndex,
+ gSaveContext.ship.quest.data.bossRush.options[fileChooseContext->bossRushIndex]);
+ sLastBossRushOptionIndex = fileChooseContext->bossRushIndex;
+ sLastBossRushOptionValue = gSaveContext.ship.quest.data.bossRush.options[fileChooseContext->bossRushIndex];
+ }
+
+ if (CHECK_BTN_ALL(input->press.button, BTN_B)) {
+ fileChooseContext->configMode = CM_BOSS_RUSH_TO_QUEST;
+ return;
+ }
+
+ // Load into the game.
+ if (CHECK_BTN_ALL(input->press.button, BTN_START) || CHECK_BTN_ALL(input->press.button, BTN_A)) {
+ Audio_PlaySoundGeneral(NA_SE_SY_FSEL_DECIDE_L, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale,
+ &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb);
+ fileChooseContext->buttonIndex = 0xFE;
+ fileChooseContext->menuMode = FS_MENU_MODE_SELECT;
+ fileChooseContext->selectMode = SM_FADE_OUT;
+ fileChooseContext->prevConfigMode = fileChooseContext->configMode;
+ return;
+ }
+}
+
+void FileChoose_DrawBossRushMenuWindowContents(FileChooseContext* fileChooseContext) {
+ OPEN_DISPS(fileChooseContext->state.gfxCtx);
+
+ uint8_t language = (gSaveContext.language == LANGUAGE_JPN) ? LANGUAGE_ENG : gSaveContext.language;
+ uint8_t listOffset = fileChooseContext->bossRushOffset;
+ uint8_t textAlpha = fileChooseContext->bossRushUIAlpha;
+
+ // Draw arrows to indicate that the list can scroll up or down.
+ // Arrow up
+ if (listOffset > 0) {
+ uint16_t arrowUpX = 140;
+ uint16_t arrowUpY = 76 - (fileChooseContext->bossRushArrowOffset / 10);
+ gDPLoadTextureBlock(POLY_OPA_DISP++, gArrowUpTex, G_IM_FMT_IA, G_IM_SIZ_16b, 16, 16, 0,
+ G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD,
+ G_TX_NOLOD);
+ gSPWideTextureRectangle(POLY_OPA_DISP++, arrowUpX << 2, arrowUpY << 2, (arrowUpX + 8) << 2, (arrowUpY + 8) << 2,
+ G_TX_RENDERTILE, 0, 0, (1 << 11), (1 << 11));
+ }
+ // Arrow down
+ if (BR_OPTIONS_MAX - listOffset > BOSSRUSH_MAX_OPTIONS_ON_SCREEN) {
+ uint16_t arrowDownX = 140;
+ uint16_t arrowDownY = 181 + (fileChooseContext->bossRushArrowOffset / 10);
+ gDPLoadTextureBlock(POLY_OPA_DISP++, gArrowDownTex, G_IM_FMT_IA, G_IM_SIZ_16b, 16, 16, 0,
+ G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD,
+ G_TX_NOLOD);
+ gSPWideTextureRectangle(POLY_OPA_DISP++, arrowDownX << 2, arrowDownY << 2, (arrowDownX + 8) << 2,
+ (arrowDownY + 8) << 2, G_TX_RENDERTILE, 0, 0, (1 << 11), (1 << 11));
+ }
+
+ // Draw options. There's more options than what fits on the screen, so the visible options
+ // depend on the current offset of the list. Currently selected option pulses in
+ // color and has arrows surrounding the option.
+ for (uint8_t i = listOffset; i - listOffset < BOSSRUSH_MAX_OPTIONS_ON_SCREEN; i++) {
+ uint16_t textYOffset = (i - listOffset) * 16;
+
+ // Option name.
+ Interface_DrawTextLine(fileChooseContext->state.gfxCtx, (char*)BossRush_GetSettingName(i, language), 65,
+ (87 + textYOffset), 255, 255, 80, textAlpha, 0.8f, true);
+
+ // Selected choice for option.
+ uint16_t finalKerning = Interface_DrawTextLine(
+ fileChooseContext->state.gfxCtx,
+ (char*)BossRush_GetSettingChoiceName(i, gSaveContext.ship.quest.data.bossRush.options[i], language), 165,
+ (87 + textYOffset), 255, 255, 255, textAlpha, 0.8f, true);
+
+ // Draw arrows around selected option.
+ if (fileChooseContext->bossRushIndex == i) {
+ Gfx_SetupDL_39Opa(fileChooseContext->state.gfxCtx);
+ gDPSetCombineMode(POLY_OPA_DISP++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM);
+ gDPLoadTextureBlock(POLY_OPA_DISP++, gArrowCursorTex, G_IM_FMT_IA, G_IM_SIZ_8b, 16, 24, 0,
+ G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, 4, G_TX_NOMASK, G_TX_NOLOD,
+ G_TX_NOLOD);
+ FileChoose_DrawTextRec(fileChooseContext->state.gfxCtx, fileChooseContext->stickLeftPrompt.arrowColorR,
+ fileChooseContext->stickLeftPrompt.arrowColorG,
+ fileChooseContext->stickLeftPrompt.arrowColorB, textAlpha, 160, (92 + textYOffset),
+ 0.42f, 0, 0, -1.0f, 1.0f);
+ FileChoose_DrawTextRec(fileChooseContext->state.gfxCtx, fileChooseContext->stickRightPrompt.arrowColorR,
+ fileChooseContext->stickRightPrompt.arrowColorG,
+ fileChooseContext->stickRightPrompt.arrowColorB, textAlpha, (171 + finalKerning),
+ (92 + textYOffset), 0.42f, 0, 0, 1.0f, 1.0f);
+ }
+ }
+
+ CLOSE_DISPS(fileChooseContext->state.gfxCtx);
+}
+
void BossRush_SpawnBlueWarps(PlayState* play) {
// Spawn blue warps in Chamber of Sages based on what bosses have been defeated.
@@ -316,7 +565,7 @@ void BossRush_HandleCompleteBoss(PlayState* play) {
}
}
-void BossRush_InitSave() {
+extern "C" void BossRush_InitSave() {
// Set player name to Lonk for the few textboxes that show up during Boss Rush. Player can't input their own name.
std::array<char, 8> brPlayerName = { 21, 50, 49, 46, 62, 62, 62, 62 };
@@ -609,6 +858,17 @@ void BossRush_OnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li
*should = false;
break;
}
+ // Handle the heal on blue warp
+ case VB_BLUE_WARP_CONSIDER_ADULT_IN_RANGE: {
+ if (*should) {
+ BossRush_HandleBlueWarpHeal(gPlayState);
+ }
+ break;
+ }
+ case VB_SHOW_GAMEPLAY_TIMER: {
+ *should |= gSaveContext.ship.quest.data.bossRush.options[BR_OPTIONS_TIMER] == BR_CHOICE_TIMER_YES;
+ break;
+ }
// Prevent saving
case VB_BE_ABLE_TO_SAVE:
// Disable doors so the player can't leave the boss rooms backwards.
@@ -629,25 +889,6 @@ void BossRush_OnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li
va_end(args);
}
-void BossRush_OnActorInitHandler(void* actorRef) {
- Actor* actor = static_cast<Actor*>(actorRef);
-
- if (actor->id == ACTOR_DEMO_SA && gPlayState->sceneNum == SCENE_CHAMBER_OF_THE_SAGES) {
- BossRush_SpawnBlueWarps(gPlayState);
- Actor_Kill(actor);
- GET_PLAYER(gPlayState)->actor.world.rot.y = GET_PLAYER(gPlayState)->actor.shape.rot.y = 27306;
- return;
- }
-
- // Remove chests, mainly for the chest in King Dodongo's boss room.
- // Remove bushes, used in Gohma's arena.
- // Remove pots, used in Barinade's and Ganondorf's arenas.
- if (actor->id == ACTOR_EN_KUSA || actor->id == ACTOR_OBJ_TSUBO || actor->id == ACTOR_EN_BOX) {
- Actor_Kill(actor);
- return;
- }
-}
-
void BossRush_OnSceneInitHandler(s16 sceneNum) {
// Unpause the timer when the scene loaded isn't the Chamber of Sages.
if (sceneNum != SCENE_CHAMBER_OF_THE_SAGES) {
@@ -667,38 +908,32 @@ void BossRush_OnBlueWarpUpdate(void* actor) {
}
}
-void BossRush_RegisterHooks() {
- static u32 onVanillaBehaviorHook = 0;
- static u32 onSceneInitHook = 0;
- static u32 onActorInitHook = 0;
- static u32 onBossDefeatHook = 0;
- static u32 onActorUpdate = 0;
-
- GameInteractor::Instance->RegisterGameHook<GameInteractor::OnLoadGame>([](int32_t fileNum) {
- GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnVanillaBehavior>(onVanillaBehaviorHook);
- GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnSceneInit>(onSceneInitHook);
- GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnActorInit>(onActorInitHook);
- GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnBossDefeat>(onBossDefeatHook);
- GameInteractor::Instance->UnregisterGameHookForID<GameInteractor::OnActorUpdate>(onActorUpdate);
-
- onVanillaBehaviorHook = 0;
- onSceneInitHook = 0;
- onActorInitHook = 0;
- onBossDefeatHook = 0;
- onActorUpdate = 0;
-
- if (!IS_BOSS_RUSH)
- return;
-
- onVanillaBehaviorHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnVanillaBehavior>(
- BossRush_OnVanillaBehaviorHandler);
- onSceneInitHook =
- GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneInit>(BossRush_OnSceneInitHandler);
- onActorInitHook =
- GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorInit>(BossRush_OnActorInitHandler);
- onBossDefeatHook =
- GameInteractor::Instance->RegisterGameHook<GameInteractor::OnBossDefeat>(BossRush_OnBossDefeatHandler);
- onActorUpdate = GameInteractor::Instance->RegisterGameHookForID<GameInteractor::OnActorUpdate>(
- ACTOR_DOOR_WARP1, BossRush_OnBlueWarpUpdate);
+void RegisterBossRush() {
+ COND_HOOK(OnLoadGame, true, [](int32_t fileNum) {
+ COND_ID_HOOK(OnActorInit, ACTOR_DEMO_SA, IS_BOSS_RUSH, [](void* actorPtr) {
+ BossRush_SpawnBlueWarps(gPlayState);
+ Actor_Kill((Actor*)actorPtr);
+ GET_PLAYER(gPlayState)->actor.world.rot.y = 27306;
+ GET_PLAYER(gPlayState)->actor.shape.rot.y = 27306;
+ });
+
+ // Remove bushes, used in Gohma's arena
+ COND_ID_HOOK(OnActorInit, ACTOR_EN_KUSA, IS_BOSS_RUSH, [](void* actorPtr) { Actor_Kill((Actor*)actorPtr); });
+
+ // Remove pots, used in Barinade's and Ganondorf's arenas
+ COND_ID_HOOK(OnActorInit, ACTOR_OBJ_TSUBO, IS_BOSS_RUSH, [](void* actorPtr) { Actor_Kill((Actor*)actorPtr); });
+
+ // Remove chests, mainly for the chest in King Dodongo's boss room
+ COND_ID_HOOK(OnActorInit, ACTOR_EN_BOX, IS_BOSS_RUSH, [](void* actorPtr) { Actor_Kill((Actor*)actorPtr); });
+
+ COND_HOOK(OnVanillaBehavior, IS_BOSS_RUSH, BossRush_OnVanillaBehaviorHandler);
+
+ COND_HOOK(OnSceneInit, IS_BOSS_RUSH, BossRush_OnSceneInitHandler);
+
+ COND_HOOK(OnBossDefeat, IS_BOSS_RUSH, BossRush_OnBossDefeatHandler);
+
+ COND_ID_HOOK(OnActorUpdate, ACTOR_DOOR_WARP1, IS_BOSS_RUSH, BossRush_OnBlueWarpUpdate);
});
}
+
+static RegisterShipInitFunc initFunc(RegisterBossRush); \ No newline at end of file
diff --git a/soh/soh/Enhancements/boss-rush/BossRush.h b/soh/soh/Enhancements/boss-rush/BossRush.h
index 881d8d53f..4a47f4f5b 100644
--- a/soh/soh/Enhancements/boss-rush/BossRush.h
+++ b/soh/soh/Enhancements/boss-rush/BossRush.h
@@ -1,16 +1,41 @@
#pragma once
-#include "z64.h"
+#include <libultraship/libultra/types.h>
#ifdef __cplusplus
extern "C" {
#endif
-void BossRush_HandleBlueWarpHeal(PlayState* play);
-void BossRush_InitSave();
+struct GameState;
+struct FileChooseContext;
+
+void FileChoose_UpdateBossRushMenu(struct GameState* gameState);
+void FileChoose_DrawBossRushMenuWindowContents(struct FileChooseContext* fileChooseContext);
const char* BossRush_GetSettingName(u8 optionIndex, u8 language);
const char* BossRush_GetSettingChoiceName(u8 optionIndex, u8 choiceIndex, u8 language);
u8 BossRush_GetSettingOptionsAmount(u8 optionIndex);
-void BossRush_RegisterHooks();
#ifdef __cplusplus
};
#endif
+
+#define BOSSRUSH_MAX_OPTIONS_ON_SCREEN 6
+
+typedef enum {
+ BR_OPTIONS_BOSSES,
+ BR_OPTIONS_HEARTS,
+ BR_OPTIONS_AMMO,
+ BR_OPTIONS_HEAL,
+ BR_OPTIONS_HYPERBOSSES,
+ BR_OPTIONS_MAGIC,
+ BR_OPTIONS_BGS,
+ BR_OPTIONS_BOTTLE,
+ BR_OPTIONS_LONGSHOT,
+ BR_OPTIONS_HOVERBOOTS,
+ BR_OPTIONS_BUNNYHOOD,
+ BR_OPTIONS_TIMER,
+ BR_OPTIONS_MAX,
+} BossRushOptionEnums;
+
+typedef enum {
+ BR_CHOICE_HYPERBOSSES_NO,
+ BR_CHOICE_HYPERBOSSES_YES,
+} BossRushHyperBossesChoices; \ No newline at end of file
diff --git a/soh/soh/Enhancements/boss-rush/BossRushTypes.h b/soh/soh/Enhancements/boss-rush/BossRushTypes.h
deleted file mode 100644
index 973f1599f..000000000
--- a/soh/soh/Enhancements/boss-rush/BossRushTypes.h
+++ /dev/null
@@ -1,91 +0,0 @@
-#pragma once
-
-#define BOSSRUSH_MAX_OPTIONS_ON_SCREEN 6
-
-typedef enum {
- BR_OPTIONS_BOSSES,
- BR_OPTIONS_HEARTS,
- BR_OPTIONS_AMMO,
- BR_OPTIONS_HEAL,
- BR_OPTIONS_HYPERBOSSES,
- BR_OPTIONS_MAGIC,
- BR_OPTIONS_BGS,
- BR_OPTIONS_BOTTLE,
- BR_OPTIONS_LONGSHOT,
- BR_OPTIONS_HOVERBOOTS,
- BR_OPTIONS_BUNNYHOOD,
- BR_OPTIONS_TIMER,
- BR_OPTIONS_MAX,
-} BossRushOptionEnums;
-
-typedef enum {
- BR_CHOICE_BOSSES_ALL,
- BR_CHOICE_BOSSES_CHILD,
- BR_CHOICE_BOSSES_ADULT,
- BR_CHOICE_BOSSES_GANONDORF_GANON
-} BossRushBossesChoices;
-
-typedef enum {
- BR_CHOICE_HEARTS_10,
- BR_CHOICE_HEARTS_15,
- BR_CHOICE_HEARTS_20,
- BR_CHOICE_HEARTS_3,
- BR_CHOICE_HEARTS_5,
- BR_CHOICE_HEARTS_7
-} BossRushHeartsChoices;
-
-typedef enum {
- BR_CHOICE_AMMO_LIMITED,
- BR_CHOICE_AMMO_FULL,
- BR_CHOICE_AMMO_MAXED,
-} BossRushAmmoChoices;
-
-typedef enum {
- BR_CHOICE_HEAL_GANONDORF,
- BR_CHOICE_HEAL_EVERYBOSS,
- BR_CHOICE_HEAL_NEVER,
-} BossRushHealChoices;
-
-typedef enum {
- BR_CHOICE_HYPERBOSSES_NO,
- BR_CHOICE_HYPERBOSSES_YES,
-} BossRushHyperBossesChoices;
-
-typedef enum {
- BR_CHOICE_MAGIC_SINGLE,
- BR_CHOICE_MAGIC_DOUBLE,
-} BossRushMagicChoices;
-
-typedef enum {
- BR_CHOICE_BGS_NO,
- BR_CHOICE_BGS_YES,
-} BossRushBgsChoices;
-
-typedef enum {
- BR_CHOICE_BOTTLE_NO,
- BR_CHOICE_BOTTLE_EMPTY,
- BR_CHOICE_BOTTLE_FAIRY,
- BR_CHOICE_BOTTLE_REDPOTION,
- BR_CHOICE_BOTTLE_GREENPOTION,
- BR_CHOICE_BOTTLE_BLUEPOTION
-} BossRushBottleChoices;
-
-typedef enum {
- BR_CHOICE_LONGSHOT_NO,
- BR_CHOICE_LONGSHOT_YES,
-} BossRushLongshotChoices;
-
-typedef enum {
- BR_CHOICE_HOVERBOOTS_NO,
- BR_CHOICE_HOVERBOOTS_YES,
-} BossRushHoverBootsChoices;
-
-typedef enum {
- BR_CHOICE_BUNNYHOOD_NO,
- BR_CHOICE_BUNNYHOOD_YES,
-} BossRushBunnyHoodChoices;
-
-typedef enum {
- BR_CHOICE_TIMER_YES,
- BR_CHOICE_TIMER_NO,
-} BossRushTimerChoices;
diff --git a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h
index 0ba910cf6..09defd088 100644
--- a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h
+++ b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h
@@ -2225,6 +2225,20 @@ typedef enum {
// #### `result`
// ```c
+ // DoorWarp1_PlayerInRange(this, play)
+ // ```
+ // #### `args`
+ // - `*DoorWarp1`
+ VB_BLUE_WARP_CONSIDER_ADULT_IN_RANGE,
+
+ // #### `result`
+ // ```c
+ // (CVarGetInteger(CVAR_GAMEPLAY_STATS("ShowIngameTimer"), 0) && gSaveContext.fileNum >= 0 && gSaveContext.fileNum
+ // <= 2)
+ // ```
+ // #### `args`
+ // - `*PlayState`
+ VB_SHOW_GAMEPLAY_TIMER,
// (this->dyna.actor.params >> 5 & 0x7F) == GI_ICE_TRAP && this->actionFunc == EnBox_Open &&
// this->skelanime.curFrame > 45 && this->iceSmokeTimer < 100
// ```
diff --git a/soh/soh/Enhancements/mods.cpp b/soh/soh/Enhancements/mods.cpp
index f276e19d3..2f56cadd6 100644
--- a/soh/soh/Enhancements/mods.cpp
+++ b/soh/soh/Enhancements/mods.cpp
@@ -6,7 +6,6 @@
#include "soh/SaveManager.h"
#include "soh/ResourceManagerHelpers.h"
#include "soh/resource/type/Skeleton.h"
-#include "soh/Enhancements/boss-rush/BossRushTypes.h"
#include "soh/Enhancements/boss-rush/BossRush.h"
#include "soh/Enhancements/enhancementTypes.h"
#include "soh/Enhancements/randomizer/3drando/random.hpp"
@@ -930,7 +929,6 @@ void RegisterCustomSkeletons() {
}
void InitMods() {
- BossRush_RegisterHooks();
RandomizerRegisterHooks();
TimeSaverRegisterHooks();
RegisterTTS();
diff --git a/soh/src/code/z_parameter.c b/soh/src/code/z_parameter.c
index 0fc923070..f2713c8d6 100644
--- a/soh/src/code/z_parameter.c
+++ b/soh/src/code/z_parameter.c
@@ -8,7 +8,6 @@
#include "libultraship/bridge.h"
#include "soh/Enhancements/gameplaystats.h"
-#include "soh/Enhancements/boss-rush/BossRushTypes.h"
#include "soh/Enhancements/custom-message/CustomMessageInterfaceAddon.h"
#include "soh/Enhancements/cosmetics/cosmeticsTypes.h"
#include "soh/Enhancements/enhancementTypes.h"
@@ -6374,11 +6373,10 @@ void Interface_Draw(PlayState* play) {
void Interface_DrawTotalGameplayTimer(PlayState* play) {
// Draw timer based on the Gameplay Stats total time.
-
- if ((IS_BOSS_RUSH && gSaveContext.ship.quest.data.bossRush.options[BR_OPTIONS_TIMER] == BR_CHOICE_TIMER_YES) ||
- (CVarGetInteger(CVAR_GAMEPLAY_STATS("ShowIngameTimer"), 0) && gSaveContext.fileNum >= 0 &&
- gSaveContext.fileNum <= 2)) {
-
+ if (GameInteractor_Should(VB_SHOW_GAMEPLAY_TIMER,
+ CVarGetInteger(CVAR_GAMEPLAY_STATS("ShowIngameTimer"), 0) && gSaveContext.fileNum >= 0 &&
+ gSaveContext.fileNum <= 2,
+ play)) {
s32 X_Margins_Timer = 0;
if (CVarGetInteger(CVAR_COSMETIC("HUD.IGT.UseMargins"), 0) != 0) {
if (CVarGetInteger(CVAR_COSMETIC("HUD.IGT.PosType"), 0) == ORIGINAL_LOCATION) {
diff --git a/soh/src/overlays/actors/ovl_Door_Warp1/z_door_warp1.c b/soh/src/overlays/actors/ovl_Door_Warp1/z_door_warp1.c
index 54e9768b4..2e0cd458f 100644
--- a/soh/src/overlays/actors/ovl_Door_Warp1/z_door_warp1.c
+++ b/soh/src/overlays/actors/ovl_Door_Warp1/z_door_warp1.c
@@ -1,7 +1,6 @@
#include "z_door_warp1.h"
#include "objects/object_warp1/object_warp1.h"
#include "soh/Enhancements/randomizer/randomizer_entrance.h"
-#include "soh/Enhancements/boss-rush/BossRush.h"
#include "soh/OTRGlobals.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
@@ -693,12 +692,7 @@ void DoorWarp1_AdultWarpIdle(DoorWarp1* this, PlayState* play) {
Audio_PlayActorSound2(&this->actor, NA_SE_EV_WARP_HOLE - SFX_FLAG);
- if (DoorWarp1_PlayerInRange(this, play)) {
- // Heal player in Boss Rush
- if (IS_BOSS_RUSH) {
- BossRush_HandleBlueWarpHeal(play);
- }
-
+ if (GameInteractor_Should(VB_BLUE_WARP_CONSIDER_ADULT_IN_RANGE, DoorWarp1_PlayerInRange(this, play), this)) {
player = GET_PLAYER(play);
OnePointCutscene_Init(play, 0x25E8, 999, &this->actor, MAIN_CAM);
diff --git a/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c b/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c
index 8fa504748..9e58f5e31 100644
--- a/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c
+++ b/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c
@@ -1398,110 +1398,6 @@ void FileChoose_UpdateQuestMenu(GameState* thisx) {
}
}
-static s8 sLastBossRushOptionIndex = -1;
-static s8 sLastBossRushOptionValue = -1;
-
-void FileChoose_UpdateBossRushMenu(GameState* thisx) {
- FileChoose_UpdateStickDirectionPromptAnim(thisx);
- FileChooseContext* this = (FileChooseContext*)thisx;
- Input* input = &this->state.input[0];
- bool dpad = CVarGetInteger(CVAR_SETTING("DpadInText"), 0);
-
- // Fade in elements after opening Boss Rush options menu
- this->bossRushUIAlpha += 25;
- if (this->bossRushUIAlpha > 255) {
- this->bossRushUIAlpha = 255;
- }
-
- // Animate up/down arrows.
- this->bossRushArrowOffset += 1;
- if (this->bossRushArrowOffset >= 30) {
- this->bossRushArrowOffset = 0;
- }
-
- // Move menu selection up or down.
- if (ABS(this->stickRelY) > 30 || (dpad && CHECK_BTN_ANY(input->press.button, BTN_DDOWN | BTN_DUP))) {
- // Move down
- if (this->stickRelY < -30 || (dpad && CHECK_BTN_ANY(input->press.button, BTN_DDOWN))) {
- // When selecting past the last option, cycle back to the first option.
- if ((this->bossRushIndex + 1) > BR_OPTIONS_MAX - 1) {
- this->bossRushIndex = 0;
- this->bossRushOffset = 0;
- } else {
- this->bossRushIndex++;
- // When last visible option is selected when moving down, offset the list down by one.
- if (this->bossRushIndex - this->bossRushOffset > BOSSRUSH_MAX_OPTIONS_ON_SCREEN - 1) {
- this->bossRushOffset++;
- }
- }
- } else if (this->stickRelY > 30 || (dpad && CHECK_BTN_ANY(input->press.button, BTN_DUP))) {
- // When selecting past the first option, cycle back to the last option and offset the list to view it
- // properly.
- if ((this->bossRushIndex - 1) < 0) {
- this->bossRushIndex = BR_OPTIONS_MAX - 1;
- this->bossRushOffset = this->bossRushIndex - BOSSRUSH_MAX_OPTIONS_ON_SCREEN + 1;
- } else {
- // When first visible option is selected when moving up, offset the list up by one.
- if (this->bossRushIndex - this->bossRushOffset == 0) {
- this->bossRushOffset--;
- }
- this->bossRushIndex--;
- }
- }
-
- Audio_PlaySoundGeneral(NA_SE_SY_FSEL_CURSOR, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale,
- &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb);
- }
-
- // Cycle through choices for currently selected option.
- if (ABS(this->stickRelX) > 30 || (dpad && CHECK_BTN_ANY(input->press.button, BTN_DLEFT | BTN_DRIGHT))) {
- if (this->stickRelX > 30 || (dpad && CHECK_BTN_ANY(input->press.button, BTN_DRIGHT))) {
- // If exceeding the amount of choices for the selected option, cycle back to the first.
- if ((gSaveContext.ship.quest.data.bossRush.options[this->bossRushIndex] + 1) ==
- BossRush_GetSettingOptionsAmount(this->bossRushIndex)) {
- gSaveContext.ship.quest.data.bossRush.options[this->bossRushIndex] = 0;
- } else {
- gSaveContext.ship.quest.data.bossRush.options[this->bossRushIndex]++;
- }
- } else if (this->stickRelX < -30 || (dpad && CHECK_BTN_ANY(input->press.button, BTN_DLEFT))) {
- // If cycling back when already at the first choice for the selected option, cycle back to the last choice.
- if ((gSaveContext.ship.quest.data.bossRush.options[this->bossRushIndex] - 1) < 0) {
- gSaveContext.ship.quest.data.bossRush.options[this->bossRushIndex] =
- BossRush_GetSettingOptionsAmount(this->bossRushIndex) - 1;
- } else {
- gSaveContext.ship.quest.data.bossRush.options[this->bossRushIndex]--;
- }
- }
-
- Audio_PlaySoundGeneral(NA_SE_SY_FSEL_CURSOR, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale,
- &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb);
- }
-
- if (sLastBossRushOptionIndex != this->bossRushIndex ||
- sLastBossRushOptionValue != gSaveContext.ship.quest.data.bossRush.options[this->bossRushIndex]) {
- GameInteractor_ExecuteOnUpdateFileBossRushOptionSelection(
- this->bossRushIndex, gSaveContext.ship.quest.data.bossRush.options[this->bossRushIndex]);
- sLastBossRushOptionIndex = this->bossRushIndex;
- sLastBossRushOptionValue = gSaveContext.ship.quest.data.bossRush.options[this->bossRushIndex];
- }
-
- if (CHECK_BTN_ALL(input->press.button, BTN_B)) {
- this->configMode = CM_BOSS_RUSH_TO_QUEST;
- return;
- }
-
- // Load into the game.
- if (CHECK_BTN_ALL(input->press.button, BTN_START) || CHECK_BTN_ALL(input->press.button, BTN_A)) {
- Audio_PlaySoundGeneral(NA_SE_SY_FSEL_DECIDE_L, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale,
- &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb);
- this->buttonIndex = 0xFE;
- this->menuMode = FS_MENU_MODE_SELECT;
- this->selectMode = SM_FADE_OUT;
- this->prevConfigMode = this->configMode;
- return;
- }
-}
-
void FileChoose_UpdateRandomizerMenu(GameState* thisx) {
FileChoose_UpdateStickDirectionPromptAnim(thisx);
FileChooseContext* this = (FileChooseContext*)thisx;
@@ -2590,63 +2486,7 @@ void FileChoose_DrawWindowContents(GameState* thisx) {
break;
}
} else if (this->configMode == CM_BOSS_RUSH_MENU) {
- uint8_t language = (gSaveContext.language == LANGUAGE_JPN) ? LANGUAGE_ENG : gSaveContext.language;
- uint8_t listOffset = this->bossRushOffset;
- uint8_t textAlpha = this->bossRushUIAlpha;
-
- // Draw arrows to indicate that the list can scroll up or down.
- // Arrow up
- if (listOffset > 0) {
- uint16_t arrowUpX = 140;
- uint16_t arrowUpY = 76 - (this->bossRushArrowOffset / 10);
- gDPLoadTextureBlock(POLY_OPA_DISP++, gArrowUpTex, G_IM_FMT_IA, G_IM_SIZ_16b, 16, 16, 0,
- G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK,
- G_TX_NOLOD, G_TX_NOLOD);
- gSPWideTextureRectangle(POLY_OPA_DISP++, arrowUpX << 2, arrowUpY << 2, (arrowUpX + 8) << 2,
- (arrowUpY + 8) << 2, G_TX_RENDERTILE, 0, 0, (1 << 11), (1 << 11));
- }
- // Arrow down
- if (BR_OPTIONS_MAX - listOffset > BOSSRUSH_MAX_OPTIONS_ON_SCREEN) {
- uint16_t arrowDownX = 140;
- uint16_t arrowDownY = 181 + (this->bossRushArrowOffset / 10);
- gDPLoadTextureBlock(POLY_OPA_DISP++, gArrowDownTex, G_IM_FMT_IA, G_IM_SIZ_16b, 16, 16, 0,
- G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK,
- G_TX_NOLOD, G_TX_NOLOD);
- gSPWideTextureRectangle(POLY_OPA_DISP++, arrowDownX << 2, arrowDownY << 2, (arrowDownX + 8) << 2,
- (arrowDownY + 8) << 2, G_TX_RENDERTILE, 0, 0, (1 << 11), (1 << 11));
- }
-
- // Draw options. There's more options than what fits on the screen, so the visible options
- // depend on the current offset of the list. Currently selected option pulses in
- // color and has arrows surrounding the option.
- for (uint8_t i = listOffset; i - listOffset < BOSSRUSH_MAX_OPTIONS_ON_SCREEN; i++) {
- uint16_t textYOffset = (i - listOffset) * 16;
-
- // Option name.
- Interface_DrawTextLine(this->state.gfxCtx, BossRush_GetSettingName(i, language), 65, (87 + textYOffset),
- 255, 255, 80, textAlpha, 0.8f, true);
-
- // Selected choice for option.
- uint16_t finalKerning = Interface_DrawTextLine(
- this->state.gfxCtx,
- BossRush_GetSettingChoiceName(i, gSaveContext.ship.quest.data.bossRush.options[i], language), 165,
- (87 + textYOffset), 255, 255, 255, textAlpha, 0.8f, true);
-
- // Draw arrows around selected option.
- if (this->bossRushIndex == i) {
- Gfx_SetupDL_39Opa(this->state.gfxCtx);
- gDPSetCombineMode(POLY_OPA_DISP++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM);
- gDPLoadTextureBlock(POLY_OPA_DISP++, gArrowCursorTex, G_IM_FMT_IA, G_IM_SIZ_8b, 16, 24, 0,
- G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, 4, G_TX_NOMASK, G_TX_NOLOD,
- G_TX_NOLOD);
- FileChoose_DrawTextRec(this->state.gfxCtx, this->stickLeftPrompt.arrowColorR,
- this->stickLeftPrompt.arrowColorG, this->stickLeftPrompt.arrowColorB, textAlpha,
- 160, (92 + textYOffset), 0.42f, 0, 0, -1.0f, 1.0f);
- FileChoose_DrawTextRec(this->state.gfxCtx, this->stickRightPrompt.arrowColorR,
- this->stickRightPrompt.arrowColorG, this->stickRightPrompt.arrowColorB,
- textAlpha, (171 + finalKerning), (92 + textYOffset), 0.42f, 0, 0, 1.0f, 1.0f);
- }
- }
+ FileChoose_DrawBossRushMenuWindowContents(this);
} else if (this->configMode == CM_RANDOMIZER_SETTINGS_MENU) {
uint8_t language = (gSaveContext.language == LANGUAGE_JPN) ? LANGUAGE_ENG : gSaveContext.language;
uint8_t textAlpha = this->randomizerUIAlpha;