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..688fecb1e 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 #include @@ -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 name; std::vector> choices; @@ -114,6 +189,180 @@ u8 BossRush_GetSettingOptionsAmount(u8 optionIndex) { return static_cast(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 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(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,37 @@ 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; +void RegisterBossRush() { + static bool registered = false; + if (registered) + return; + registered = true; - GameInteractor::Instance->RegisterGameHook([](int32_t fileNum) { - GameInteractor::Instance->UnregisterGameHook(onVanillaBehaviorHook); - GameInteractor::Instance->UnregisterGameHook(onSceneInitHook); - GameInteractor::Instance->UnregisterGameHook(onActorInitHook); - GameInteractor::Instance->UnregisterGameHook(onBossDefeatHook); - GameInteractor::Instance->UnregisterGameHookForID(onActorUpdate); + 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; + }); - onVanillaBehaviorHook = 0; - onSceneInitHook = 0; - onActorInitHook = 0; - onBossDefeatHook = 0; - onActorUpdate = 0; + // Remove bushes, used in Gohma's arena + COND_ID_HOOK(OnActorInit, ACTOR_EN_KUSA, IS_BOSS_RUSH, [](void* actorPtr) { Actor_Kill((Actor*)actorPtr); }); - if (!IS_BOSS_RUSH) - return; + // 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); }); - onVanillaBehaviorHook = GameInteractor::Instance->RegisterGameHook( - BossRush_OnVanillaBehaviorHandler); - onSceneInitHook = - GameInteractor::Instance->RegisterGameHook(BossRush_OnSceneInitHandler); - onActorInitHook = - GameInteractor::Instance->RegisterGameHook(BossRush_OnActorInitHandler); - onBossDefeatHook = - GameInteractor::Instance->RegisterGameHook(BossRush_OnBossDefeatHandler); - onActorUpdate = GameInteractor::Instance->RegisterGameHookForID( - ACTOR_DOOR_WARP1, BossRush_OnBlueWarpUpdate); + // 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 #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 186307aed..99bc57efe 100644 --- a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h +++ b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h @@ -2222,6 +2222,23 @@ typedef enum { // - `s32` limbCount // - `*Vec3s` frameTable VB_LOAD_PLAYER_ANIMATION_FRAME, + + // #### `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, } GIVanillaBehavior; #endif diff --git a/soh/soh/Enhancements/mods.cpp b/soh/soh/Enhancements/mods.cpp index 6c547db08..44f0bcd57 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" @@ -957,7 +956,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;