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/CrashHandlerExt.cpp b/soh/soh/CrashHandlerExt.cpp index cc9c7b1ee..3ca821460 100644 --- a/soh/soh/CrashHandlerExt.cpp +++ b/soh/soh/CrashHandlerExt.cpp @@ -19,7 +19,7 @@ static std::array sCatToStrArray{ "SWITCH", "BG", "PLAYER", "EXPLOSIVE", "NPC", "ENEMY", "PROP", "ITEMACTION", "MISC", "BOSS", "DOOR", "CHEST", }; -#define DEFINE_SCENE(_1, _2, enumName, _4, _5, _6) #enumName +#define DEFINE_SCENE(_1, _2, enumName, _4, _5, _6) #enumName, static std::array sSceneIdToStrArray{ #include "tables/scene_table.h" diff --git a/soh/soh/Enhancements/BonkDamage.cpp b/soh/soh/Enhancements/BonkDamage.cpp new file mode 100644 index 000000000..e11f46b78 --- /dev/null +++ b/soh/soh/Enhancements/BonkDamage.cpp @@ -0,0 +1,51 @@ +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" +#include "soh/ShipInit.hpp" +#include "./enhancementTypes.h" + +extern "C" { +#include "functions.h" +#include "macros.h" +extern PlayState* gPlayState; +extern SaveContext gSaveContext; +} + +void RegisterBonkDamage() { + COND_HOOK(OnPlayerBonk, CVarGetInteger(CVAR_ENHANCEMENT("BonkDamageMult"), BONK_DAMAGE_NONE) != BONK_DAMAGE_NONE, + [] { + uint16_t bonkDamage = 0; + switch (CVarGetInteger(CVAR_ENHANCEMENT("BonkDamageMult"), BONK_DAMAGE_NONE)) { + case BONK_DAMAGE_NONE: + return; + case BONK_DAMAGE_OHKO: + gSaveContext.health = 0; + return; + case BONK_DAMAGE_QUARTER_HEART: + bonkDamage = 4; + break; + case BONK_DAMAGE_HALF_HEART: + bonkDamage = 8; + break; + case BONK_DAMAGE_1_HEART: + bonkDamage = 16; + break; + case BONK_DAMAGE_2_HEARTS: + bonkDamage = 32; + break; + case BONK_DAMAGE_4_HEARTS: + bonkDamage = 64; + break; + case BONK_DAMAGE_8_HEARTS: + bonkDamage = 128; + break; + default: + break; + } + + Health_ChangeBy(gPlayState, -bonkDamage); + // Set invincibility to make Link flash red as a visual damage indicator. + Player* player = GET_PLAYER(gPlayState); + player->invincibilityTimer = 28; + }); +} + +static RegisterShipInitFunc registerBonkDamage(RegisterBonkDamage, { CVAR_ENHANCEMENT("BonkDamageMult") }); diff --git a/soh/soh/Enhancements/audio/AudioCollection.cpp b/soh/soh/Enhancements/audio/AudioCollection.cpp index d3f69dad8..a30d3f086 100644 --- a/soh/soh/Enhancements/audio/AudioCollection.cpp +++ b/soh/soh/Enhancements/audio/AudioCollection.cpp @@ -2,6 +2,7 @@ #include "sequence.h" #include "sfx.h" #include "soh/cvar_prefixes.h" +#include "soh/Notification/Notification.h" #include #include #include @@ -458,3 +459,11 @@ extern "C" bool AudioCollection_HasSequenceNum(uint16_t seqId) { extern "C" size_t AudioCollection_SequenceMapSize() { return AudioCollection::Instance->SequenceMapSize(); } + +extern "C" void AudioCollection_EmitSongNameNotification(s32 seqId) { + const char* sequenceName = AudioCollection_GetSequenceName(seqId); + if (sequenceName != NULL) { + Notification::Emit({ .message = "Currently playing: " + std::string(sequenceName), + .remainingTime = (float)CVarGetInteger(CVAR_AUDIO("SeqNameOverlayDuration"), 5) }); + } +} \ No newline at end of file diff --git a/soh/soh/Enhancements/audio/AudioCollection.h b/soh/soh/Enhancements/audio/AudioCollection.h index 2cdb02e21..51a6902cf 100644 --- a/soh/soh/Enhancements/audio/AudioCollection.h +++ b/soh/soh/Enhancements/audio/AudioCollection.h @@ -73,4 +73,5 @@ void AudioCollection_AddToCollection(char* otrPath, uint16_t seqNum); const char* AudioCollection_GetSequenceName(uint16_t seqId); bool AudioCollection_HasSequenceNum(uint16_t seqId); size_t AudioCollection_SequenceMapSize(); +void AudioCollection_EmitSongNameNotification(s32 seqId); #endif \ No newline at end of file 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 #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,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; +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; + }); - 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); + // Remove bushes, used in Gohma's arena + COND_ID_HOOK(OnActorInit, ACTOR_EN_KUSA, IS_BOSS_RUSH, [](void* actorPtr) { Actor_Kill((Actor*)actorPtr); }); - onVanillaBehaviorHook = 0; - onSceneInitHook = 0; - onActorInitHook = 0; - onBossDefeatHook = 0; - onActorUpdate = 0; + // 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); }); - if (!IS_BOSS_RUSH) - return; + // 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); }); - 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); + 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/enhancementTypes.h b/soh/soh/Enhancements/enhancementTypes.h index c1b91cffa..bd025115b 100644 --- a/soh/soh/Enhancements/enhancementTypes.h +++ b/soh/soh/Enhancements/enhancementTypes.h @@ -112,7 +112,9 @@ typedef enum { typedef enum { TIME_TRAVEL_DISABLED, TIME_TRAVEL_OOT, + TIME_TRAVEL_OOT_MS, TIME_TRAVEL_ANY, + TIME_TRAVEL_ANY_MS } TimeTravelType; typedef enum { diff --git a/soh/soh/Enhancements/game-interactor/GameInteractionEffect.cpp b/soh/soh/Enhancements/game-interactor/GameInteractionEffect.cpp index 03667dd86..76e026206 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractionEffect.cpp +++ b/soh/soh/Enhancements/game-interactor/GameInteractionEffect.cpp @@ -50,7 +50,7 @@ namespace GameInteractionEffect { // MARK: - Flags GameInteractionEffectQueryResult SetSceneFlag::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded()) { + if (!GameInteractor::IsSaveLoaded(true)) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } @@ -62,7 +62,7 @@ void SetSceneFlag::_Apply() { } GameInteractionEffectQueryResult UnsetSceneFlag::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded()) { + if (!GameInteractor::IsSaveLoaded(true)) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } @@ -74,7 +74,7 @@ void UnsetSceneFlag::_Apply() { } GameInteractionEffectQueryResult SetFlag::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded()) { + if (!GameInteractor::IsSaveLoaded(true)) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } @@ -86,7 +86,7 @@ void SetFlag::_Apply() { } GameInteractionEffectQueryResult UnsetFlag::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded()) { + if (!GameInteractor::IsSaveLoaded(true)) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } @@ -99,7 +99,7 @@ void UnsetFlag::_Apply() { // MARK: - ModifyHeartContainers GameInteractionEffectQueryResult ModifyHeartContainers::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded()) { + if (!GameInteractor::IsSaveLoaded(true)) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else if ((parameters[0] > 0 && (gSaveContext.healthCapacity + (parameters[0] * 0x10) > 0x140)) || (parameters[0] < 0 && (gSaveContext.healthCapacity + (parameters[0] * 0x10) < 0x10))) { @@ -115,7 +115,7 @@ void ModifyHeartContainers::_Apply() { // MARK: - FillMagic GameInteractionEffectQueryResult FillMagic::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded()) { + if (!GameInteractor::IsSaveLoaded(true)) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else if (!gSaveContext.isMagicAcquired || gSaveContext.magic >= ((gSaveContext.isDoubleMagicAcquired + 1) * 48)) { return GameInteractionEffectQueryResult::NotPossible; @@ -129,7 +129,7 @@ void FillMagic::_Apply() { // MARK: - EmptyMagic GameInteractionEffectQueryResult EmptyMagic::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded()) { + if (!GameInteractor::IsSaveLoaded(true)) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else if (!gSaveContext.isMagicAcquired || gSaveContext.magic <= 0) { return GameInteractionEffectQueryResult::NotPossible; @@ -143,7 +143,7 @@ void EmptyMagic::_Apply() { // MARK: - ModifyRupees GameInteractionEffectQueryResult ModifyRupees::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded()) { + if (!GameInteractor::IsSaveLoaded(true)) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else if ((parameters[0] < 0 && gSaveContext.rupees <= 0) || (parameters[0] > 0 && gSaveContext.rupees >= CUR_CAPACITY(UPG_WALLET))) { @@ -158,7 +158,7 @@ void ModifyRupees::_Apply() { // MARK: - NoUI GameInteractionEffectQueryResult NoUI::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -173,7 +173,7 @@ void NoUI::_Remove() { // MARK: - ModifyGravity GameInteractionEffectQueryResult ModifyGravity::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -188,7 +188,7 @@ void ModifyGravity::_Remove() { // MARK: - ModifyHealth GameInteractionEffectQueryResult ModifyHealth::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded()) { + if (!GameInteractor::IsSaveLoaded(true)) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else if ((parameters[0] > 0 && gSaveContext.health == gSaveContext.healthCapacity) || (parameters[0] < 0 && (gSaveContext.health + (16 * parameters[0]) <= 0))) { @@ -204,7 +204,7 @@ void ModifyHealth::_Apply() { // MARK: - SetPlayerHealth GameInteractionEffectQueryResult SetPlayerHealth::CanBeApplied() { Player* player = GET_PLAYER(gPlayState); - if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -217,7 +217,7 @@ void SetPlayerHealth::_Apply() { // MARK: - FreezePlayer GameInteractionEffectQueryResult FreezePlayer::CanBeApplied() { Player* player = GET_PLAYER(gPlayState); - if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused() || !PlayerGrounded(player)) { + if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused() || !PlayerGrounded(player)) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -230,7 +230,7 @@ void FreezePlayer::_Apply() { // MARK: - BurnPlayer GameInteractionEffectQueryResult BurnPlayer::CanBeApplied() { Player* player = GET_PLAYER(gPlayState); - if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused() || !PlayerGrounded(player)) { + if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused() || !PlayerGrounded(player)) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -243,7 +243,7 @@ void BurnPlayer::_Apply() { // MARK: - ElectrocutePlayer GameInteractionEffectQueryResult ElectrocutePlayer::CanBeApplied() { Player* player = GET_PLAYER(gPlayState); - if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused() || !PlayerGrounded(player)) { + if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused() || !PlayerGrounded(player)) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -256,7 +256,7 @@ void ElectrocutePlayer::_Apply() { // MARK: - KnockbackPlayer GameInteractionEffectQueryResult KnockbackPlayer::CanBeApplied() { Player* player = GET_PLAYER(gPlayState); - if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused() || + if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused() || player->stateFlags2 & PLAYER_STATE2_CRAWLING) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { @@ -269,7 +269,7 @@ void KnockbackPlayer::_Apply() { // MARK: - ModifyLinkSize GameInteractionEffectQueryResult ModifyLinkSize::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -284,7 +284,7 @@ void ModifyLinkSize::_Remove() { // MARK: - InvisibleLink GameInteractionEffectQueryResult InvisibleLink::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -299,7 +299,7 @@ void InvisibleLink::_Remove() { // MARK: - PacifistMode GameInteractionEffectQueryResult PacifistMode::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -314,7 +314,7 @@ void PacifistMode::_Remove() { // MARK: - DisableZTargeting GameInteractionEffectQueryResult DisableZTargeting::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -329,7 +329,7 @@ void DisableZTargeting::_Remove() { // MARK: - WeatherRainstorm GameInteractionEffectQueryResult WeatherRainstorm::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -344,7 +344,7 @@ void WeatherRainstorm::_Remove() { // MARK: - ReverseControls GameInteractionEffectQueryResult ReverseControls::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded()) { + if (!GameInteractor::IsSaveLoaded(true)) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -359,7 +359,7 @@ void ReverseControls::_Remove() { // MARK: - ForceEquipBoots GameInteractionEffectQueryResult ForceEquipBoots::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -374,7 +374,7 @@ void ForceEquipBoots::_Remove() { // MARK: - ModifyMovementSpeedMultiplier GameInteractionEffectQueryResult ModifyMovementSpeedMultiplier::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -393,7 +393,7 @@ void ModifyMovementSpeedMultiplier::_Remove() { // MARK: - OneHitKO GameInteractionEffectQueryResult OneHitKO::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -408,7 +408,7 @@ void OneHitKO::_Remove() { // MARK: - ModifyDefenseModifier GameInteractionEffectQueryResult ModifyDefenseModifier::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -423,7 +423,7 @@ void ModifyDefenseModifier::_Remove() { // MARK: - GiveOrTakeShield GameInteractionEffectQueryResult GiveOrTakeShield::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else if ((parameters[0] > 0 && ((gBitFlags[parameters[0] - ITEM_SHIELD_DEKU] << gEquipShifts[EQUIP_TYPE_SHIELD]) & gSaveContext.inventory.equipment)) || @@ -441,7 +441,7 @@ void GiveOrTakeShield::_Apply() { // MARK: - TeleportPlayer GameInteractionEffectQueryResult TeleportPlayer::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -453,7 +453,7 @@ void TeleportPlayer::_Apply() { // MARK: - ClearAssignedButtons GameInteractionEffectQueryResult ClearAssignedButtons::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded()) { + if (!GameInteractor::IsSaveLoaded(true)) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -465,7 +465,7 @@ void ClearAssignedButtons::_Apply() { // MARK: - SetTimeOfDay GameInteractionEffectQueryResult SetTimeOfDay::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded()) { + if (!GameInteractor::IsSaveLoaded(true)) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -477,7 +477,7 @@ void SetTimeOfDay::_Apply() { // MARK: - SetCollisionViewer GameInteractionEffectQueryResult SetCollisionViewer::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -492,7 +492,7 @@ void SetCollisionViewer::_Remove() { // MARK: - RandomizeCosmetics GameInteractionEffectQueryResult RandomizeCosmetics::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded()) { + if (!GameInteractor::IsSaveLoaded(true)) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -504,7 +504,7 @@ void RandomizeCosmetics::_Apply() { // MARK: - PressButton GameInteractionEffectQueryResult PressButton::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded()) { + if (!GameInteractor::IsSaveLoaded(true)) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -516,7 +516,7 @@ void PressButton::_Apply() { // MARK: - PressRandomButton GameInteractionEffectQueryResult PressRandomButton::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded()) { + if (!GameInteractor::IsSaveLoaded(true)) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -528,7 +528,7 @@ void PressRandomButton::_Apply() { // MARK: - AddOrTakeAmmo GameInteractionEffectQueryResult AddOrTakeAmmo::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded()) { + if (!GameInteractor::IsSaveLoaded(true)) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else if (!GameInteractor::CanAddOrTakeAmmo(parameters[0], parameters[1])) { return GameInteractionEffectQueryResult::NotPossible; @@ -542,7 +542,7 @@ void AddOrTakeAmmo::_Apply() { // MARK: - RandomBombFuseTimer GameInteractionEffectQueryResult RandomBombFuseTimer::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -557,7 +557,7 @@ void RandomBombFuseTimer::_Remove() { // MARK: - DisableLedgeGrabs GameInteractionEffectQueryResult DisableLedgeGrabs::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -572,7 +572,7 @@ void DisableLedgeGrabs::_Remove() { // MARK: - RandomWind GameInteractionEffectQueryResult RandomWind::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -587,7 +587,7 @@ void RandomWind::_Remove() { // MARK: - RandomBonks GameInteractionEffectQueryResult RandomBonks::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -602,7 +602,7 @@ void RandomBonks::_Remove() { // MARK: - PlayerInvincibility GameInteractionEffectQueryResult PlayerInvincibility::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -617,7 +617,7 @@ void PlayerInvincibility::_Remove() { // MARK: - SlipperyFloor GameInteractionEffectQueryResult SlipperyFloor::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused()) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; @@ -632,7 +632,7 @@ void SlipperyFloor::_Remove() { // MARK: - SpawnEnemyWithOffset GameInteractionEffectQueryResult SpawnEnemyWithOffset::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded()) { + if (!GameInteractor::IsSaveLoaded(true)) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } return GameInteractor::RawAction::SpawnEnemyWithOffset(parameters[0], parameters[1]); @@ -644,7 +644,7 @@ void SpawnEnemyWithOffset::_Apply() { // MARK: - SpawnActor GameInteractionEffectQueryResult SpawnActor::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded()) { + if (!GameInteractor::IsSaveLoaded(true)) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } return GameInteractor::RawAction::SpawnActor(parameters[0], parameters[1]); 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/gameplaystats.h b/soh/soh/Enhancements/gameplaystats.h index 1f89cb3a4..1d60b5dfc 100644 --- a/soh/soh/Enhancements/gameplaystats.h +++ b/soh/soh/Enhancements/gameplaystats.h @@ -51,9 +51,48 @@ typedef enum { /* 0xA9 */ TIMESTAMP_DEFEAT_GANON, // z_boss_ganon2.c /* 0xA9 */ TIMESTAMP_BOSSRUSH_FINISH, // z_boss_ganon2.c /* 0xAA */ TIMESTAMP_FOUND_GREG, // z_parameter.c - /* 0xAA */ TIMESTAMP_TRIFORCE_COMPLETED, // z_parameter.c - /* 0xAB */ TIMESTAMP_MAX - + /* 0xAB */ TIMESTAMP_TRIFORCE_COMPLETED, // z_parameter.c + /* 0xAC */ TIMESTAMP_FOUND_GOHMA_SOUL, + /* 0xAD */ TIMESTAMP_FOUND_KING_DODONGO_SOUL, + /* 0xAE */ TIMESTAMP_FOUND_BARINADE_SOUL, + /* 0xAF */ TIMESTAMP_FOUND_PHANTOM_GANON_SOUL, + /* 0xB0 */ TIMESTAMP_FOUND_VOLVAGIA_SOUL, + /* 0xB1 */ TIMESTAMP_FOUND_MORPHA_SOUL, + /* 0xB2 */ TIMESTAMP_FOUND_BONGO_BONGO_SOUL, + /* 0xB3 */ TIMESTAMP_FOUND_TWINROVA_SOUL, + /* 0xB5 */ TIMESTAMP_FOUND_GANON_SOUL, + /* 0xB6 */ TIMESTAMP_FOUND_BRONZE_SCALE, + /* 0xB7 */ TIMESTAMP_FOUND_OCARINA_A_BUTTON, + /* 0xB8 */ TIMESTAMP_FOUND_OCARINA_C_UP_BUTTON, + /* 0xB9 */ TIMESTAMP_FOUND_OCARINA_C_DOWN_BUTTON, + /* 0xBA */ TIMESTAMP_FOUND_OCARINA_C_LEFT_BUTTON, + /* 0xBB */ TIMESTAMP_FOUND_OCARINA_C_RIGHT_BUTTON, + /* 0xBC */ TIMESTAMP_FOUND_FISHING_POLE, + /* 0xBD */ TIMESTAMP_FOUND_GUARD_HOUSE_KEY, + /* 0xBE */ TIMESTAMP_FOUND_MARKET_BAZAAR_KEY, + /* 0xBF */ TIMESTAMP_FOUND_MARKET_POTION_SHOP_KEY, + /* 0xC0 */ TIMESTAMP_FOUND_MASK_SHOP_KEY, + /* 0xC1 */ TIMESTAMP_FOUND_MARKET_SHOOTING_GALLERY_KEY, + /* 0xC2 */ TIMESTAMP_FOUND_BOMBCHU_BOWLING_KEY, + /* 0xC3 */ TIMESTAMP_FOUND_TREASURE_CHEST_GAME_BUILDING_KEY, + /* 0xC4 */ TIMESTAMP_FOUND_BOMBCHU_SHOP_KEY, + /* 0xC5 */ TIMESTAMP_FOUND_RICHARDS_HOUSE_KEY, + /* 0xC6 */ TIMESTAMP_FOUND_ALLEY_HOUSE_KEY, + /* 0xC7 */ TIMESTAMP_FOUND_KAK_BAZAAR_KEY, + /* 0xC8 */ TIMESTAMP_FOUND_KAK_POTION_SHOP_KEY, + /* 0xC9 */ TIMESTAMP_FOUND_BOSS_HOUSE_KEY, + /* 0xCA */ TIMESTAMP_FOUND_GRANNYS_POTION_SHOP_KEY, + /* 0xCB */ TIMESTAMP_FOUND_SKULLTULA_HOUSE_KEY, + /* 0xCC */ TIMESTAMP_FOUND_IMPAS_HOUSE_KEY, + /* 0xCD */ TIMESTAMP_FOUND_WINDMILL_KEY, + /* 0xCE */ TIMESTAMP_FOUND_KAK_SHOOTING_GALLERY_KEY, + /* 0xCF */ TIMESTAMP_FOUND_DAMPES_HUT_KEY, + /* 0xD0 */ TIMESTAMP_FOUND_TALONS_HOUSE_KEY, + /* 0xD1 */ TIMESTAMP_FOUND_STABLES_KEY, + /* 0xD2 */ TIMESTAMP_FOUND_BACK_TOWER_KEY, + /* 0xD3 */ TIMESTAMP_FOUND_HYLIA_LAB_KEY, + /* 0xD4 */ TIMESTAMP_FOUND_FISHING_HOLE_KEY, + /* 0xD5 */ TIMESTAMP_MAX } GameplayStatTimestamp; typedef enum { diff --git a/soh/soh/Enhancements/mods.cpp b/soh/soh/Enhancements/mods.cpp index 6c547db08..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" @@ -49,7 +48,6 @@ extern "C" { extern SaveContext gSaveContext; extern PlayState* gPlayState; -extern void Overlay_DisplayText(float duration, const char* text); } // GreyScaleEndDlist @@ -131,10 +129,27 @@ void RegisterOcarinaTimeTravel() { bool notNearAnySource = !nearbyTimeBlockEmpty && !nearbyTimeBlock && !nearbyOcarinaSpot && !nearbyDoorOfTime && !nearbyFrogs && !nearbyGossipStone; bool hasOcarinaOfTime = (INV_CONTENT(ITEM_OCARINA_TIME) == ITEM_OCARINA_TIME); - bool doesntNeedOcarinaOfTime = CVarGetInteger(CVAR_ENHANCEMENT("TimeTravel"), 0) == 2; bool hasMasterSword = CHECK_OWNED_EQUIP(EQUIP_TYPE_SWORD, EQUIP_INV_SWORD_MASTER); - // TODO: Once Swordless Adult is fixed: Remove the Master Sword check - if (justPlayedSoT && notNearAnySource && (hasOcarinaOfTime || doesntNeedOcarinaOfTime) && hasMasterSword) { + int timeTravelSetting = CVarGetInteger(CVAR_ENHANCEMENT("TimeTravel"), 0); + bool meetsTimeTravelRequirements = false; + + switch (timeTravelSetting) { + case TIME_TRAVEL_ANY: + meetsTimeTravelRequirements = true; + break; + case TIME_TRAVEL_ANY_MS: + meetsTimeTravelRequirements = hasMasterSword; + break; + case TIME_TRAVEL_OOT_MS: + meetsTimeTravelRequirements = hasMasterSword && hasOcarinaOfTime; + break; + case TIME_TRAVEL_OOT: + default: + meetsTimeTravelRequirements = hasOcarinaOfTime; + break; + } + + if (justPlayedSoT && notNearAnySource && meetsTimeTravelRequirements) { SwitchAge(); } }); @@ -283,49 +298,6 @@ void UpdateHyperEnemiesState() { } } -void RegisterBonkDamage() { - GameInteractor::Instance->RegisterGameHook([]() { - uint8_t bonkOption = CVarGetInteger(CVAR_ENHANCEMENT("BonkDamageMult"), BONK_DAMAGE_NONE); - if (bonkOption == BONK_DAMAGE_NONE) { - return; - } - - if (bonkOption == BONK_DAMAGE_OHKO) { - gSaveContext.health = 0; - return; - } - - uint16_t bonkDamage = 0; - switch (bonkOption) { - case BONK_DAMAGE_QUARTER_HEART: - bonkDamage = 4; - break; - case BONK_DAMAGE_HALF_HEART: - bonkDamage = 8; - break; - case BONK_DAMAGE_1_HEART: - bonkDamage = 16; - break; - case BONK_DAMAGE_2_HEARTS: - bonkDamage = 32; - break; - case BONK_DAMAGE_4_HEARTS: - bonkDamage = 64; - break; - case BONK_DAMAGE_8_HEARTS: - bonkDamage = 128; - break; - default: - break; - } - - Health_ChangeBy(gPlayState, -bonkDamage); - // Set invincibility to make Link flash red as a visual damage indicator. - Player* player = GET_PLAYER(gPlayState); - player->invincibilityTimer = 28; - }); -} - void UpdateDirtPathFixState(int32_t sceneNum) { switch (sceneNum) { case SCENE_HYRULE_FIELD: @@ -957,7 +929,6 @@ void RegisterCustomSkeletons() { } void InitMods() { - BossRush_RegisterHooks(); RandomizerRegisterHooks(); TimeSaverRegisterHooks(); RegisterTTS(); @@ -966,7 +937,6 @@ void InitMods() { RegisterDeleteFileOnDeath(); RegisterHyperBosses(); UpdateHyperEnemiesState(); - RegisterBonkDamage(); RegisterMenuPathFix(); RegisterMirrorModeHandler(); RegisterResetNaviTimer(); diff --git a/soh/soh/Enhancements/randomizer/ShuffleCrates.cpp b/soh/soh/Enhancements/randomizer/ShuffleCrates.cpp index 53a65df22..6962b290d 100644 --- a/soh/soh/Enhancements/randomizer/ShuffleCrates.cpp +++ b/soh/soh/Enhancements/randomizer/ShuffleCrates.cpp @@ -336,7 +336,7 @@ void Rando::StaticData::RegisterCrateLocations() { locationTable[RC_GF_SOUTHMOST_CENTER_CRATE] = Location::Crate(RC_GF_SOUTHMOST_CENTER_CRATE, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_GERUDOS_FORTRESS, TWO_ACTOR_PARAMS(315, -1534), "Southmost Center Crate", RHT_CRATE_GERUDOS_FORTRESS, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GF_SOUTHMOST_CENTER_CRATE)); locationTable[RC_GF_MID_SOUTH_CENTER_CRATE] = Location::Crate(RC_GF_MID_SOUTH_CENTER_CRATE, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_GERUDOS_FORTRESS, TWO_ACTOR_PARAMS(315, -1594), "Middle South Center Crate", RHT_CRATE_GERUDOS_FORTRESS, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GF_MID_SOUTH_CENTER_CRATE)); locationTable[RC_GF_MID_NORTH_CENTER_CRATE] = Location::Crate(RC_GF_MID_NORTH_CENTER_CRATE, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_GERUDOS_FORTRESS, TWO_ACTOR_PARAMS(310, -1782), "Middle North Center Crate", RHT_CRATE_GERUDOS_FORTRESS, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GF_MID_NORTH_CENTER_CRATE)); - locationTable[RR_GF_NORTHMOST_CENTER_CRATE] = Location::Crate(RR_GF_NORTHMOST_CENTER_CRATE, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_GERUDOS_FORTRESS, TWO_ACTOR_PARAMS(310, -1842), "Northmost Center Crate", RHT_CRATE_GERUDOS_FORTRESS, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GF_NORTHMOST_CENTER_CRATE)); + locationTable[RC_GF_NORTHMOST_CENTER_CRATE] = Location::Crate(RC_GF_NORTHMOST_CENTER_CRATE, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_GERUDOS_FORTRESS, TWO_ACTOR_PARAMS(310, -1842), "Northmost Center Crate", RHT_CRATE_GERUDOS_FORTRESS, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GF_NORTHMOST_CENTER_CRATE)); locationTable[RC_GF_OUTSKIRTS_NE_CRATE] = Location::Crate(RC_GF_OUTSKIRTS_NE_CRATE, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_GERUDOS_FORTRESS, TWO_ACTOR_PARAMS(-60, -2210), "Outskirts Northeast Crate", RHT_CRATE_GERUDOS_FORTRESS, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GF_OUTSKIRTS_NE_CRATE)); locationTable[RC_GF_OUTSKIRTS_NW_CRATE] = Location::Crate(RC_GF_OUTSKIRTS_NW_CRATE, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_GERUDOS_FORTRESS, TWO_ACTOR_PARAMS(-120, -2210), "Outskirts Northwest Crate", RHT_CRATE_GERUDOS_FORTRESS, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GF_OUTSKIRTS_NW_CRATE)); locationTable[RC_GF_HBA_RANGE_CRATE_2] = Location::Crate(RC_GF_HBA_RANGE_CRATE_2, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_GERUDOS_FORTRESS, TWO_ACTOR_PARAMS(4090, -1780), "Horseback Archery Range Crate 2", RHT_CRATE_GERUDOS_FORTRESS, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GF_HBA_RANGE_CRATE_2)); diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/gerudo_fortress.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/gerudo_fortress.cpp index 3929faf86..17e28d5eb 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/gerudo_fortress.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/gerudo_fortress.cpp @@ -30,7 +30,7 @@ void RegionTable_Init_GerudoFortress() { LOCATION(RC_GF_SOUTHMOST_CENTER_CRATE, logic->CanBreakCrates()), LOCATION(RC_GF_MID_SOUTH_CENTER_CRATE, logic->CanBreakCrates()), LOCATION(RC_GF_MID_NORTH_CENTER_CRATE, logic->CanBreakCrates()), - LOCATION(RR_GF_NORTHMOST_CENTER_CRATE, logic->CanBreakCrates()), + LOCATION(RC_GF_NORTHMOST_CENTER_CRATE, logic->CanBreakCrates()), }, { //Exits Entrance(RR_TH_1_TORCH_CELL, []{return true;}), diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index 5f9027d82..b8a4820d8 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -2332,7 +2332,7 @@ std::map rcToRandomizerInf = { RAND_INF_GF_MID_NORTH_CENTER_CRATE, }, { - RR_GF_NORTHMOST_CENTER_CRATE, + RC_GF_NORTHMOST_CENTER_CRATE, RAND_INF_GF_NORTHMOST_CENTER_CRATE, }, { @@ -5783,6 +5783,53 @@ void RandomizerSettingsWindow::InitElement() { mSettings->UpdateOptionProperties(); } +static std::unordered_map randomizerGetToStatsTimeStamp = { + { RG_GOHMA_SOUL, TIMESTAMP_FOUND_GOHMA_SOUL }, + { RG_KING_DODONGO_SOUL, TIMESTAMP_FOUND_KING_DODONGO_SOUL }, + { RG_BARINADE_SOUL, TIMESTAMP_FOUND_BARINADE_SOUL }, + { RG_PHANTOM_GANON_SOUL, TIMESTAMP_FOUND_PHANTOM_GANON_SOUL }, + { RG_VOLVAGIA_SOUL, TIMESTAMP_FOUND_VOLVAGIA_SOUL }, + { RG_MORPHA_SOUL, TIMESTAMP_FOUND_MORPHA_SOUL }, + { RG_BONGO_BONGO_SOUL, TIMESTAMP_FOUND_BONGO_BONGO_SOUL }, + { RG_TWINROVA_SOUL, TIMESTAMP_FOUND_TWINROVA_SOUL }, + { RG_GANON_SOUL, TIMESTAMP_FOUND_GANON_SOUL }, + + { RG_BRONZE_SCALE, TIMESTAMP_FOUND_BRONZE_SCALE }, + + { RG_OCARINA_A_BUTTON, TIMESTAMP_FOUND_OCARINA_A_BUTTON }, + { RG_OCARINA_C_UP_BUTTON, TIMESTAMP_FOUND_OCARINA_C_UP_BUTTON }, + { RG_OCARINA_C_DOWN_BUTTON, TIMESTAMP_FOUND_OCARINA_C_DOWN_BUTTON }, + { RG_OCARINA_C_LEFT_BUTTON, TIMESTAMP_FOUND_OCARINA_C_LEFT_BUTTON }, + { RG_OCARINA_C_RIGHT_BUTTON, TIMESTAMP_FOUND_OCARINA_C_RIGHT_BUTTON }, + + { RG_FISHING_POLE, TIMESTAMP_FOUND_FISHING_POLE }, + + { RG_GUARD_HOUSE_KEY, TIMESTAMP_FOUND_GUARD_HOUSE_KEY }, + { RG_MARKET_BAZAAR_KEY, TIMESTAMP_FOUND_MARKET_BAZAAR_KEY }, + { RG_MARKET_POTION_SHOP_KEY, TIMESTAMP_FOUND_MARKET_POTION_SHOP_KEY }, + { RG_MASK_SHOP_KEY, TIMESTAMP_FOUND_MASK_SHOP_KEY }, + { RG_MARKET_SHOOTING_GALLERY_KEY, TIMESTAMP_FOUND_MARKET_SHOOTING_GALLERY_KEY }, + { RG_BOMBCHU_BOWLING_KEY, TIMESTAMP_FOUND_BOMBCHU_BOWLING_KEY }, + { RG_TREASURE_CHEST_GAME_BUILDING_KEY, TIMESTAMP_FOUND_TREASURE_CHEST_GAME_BUILDING_KEY }, + { RG_BOMBCHU_SHOP_KEY, TIMESTAMP_FOUND_BOMBCHU_SHOP_KEY }, + { RG_RICHARDS_HOUSE_KEY, TIMESTAMP_FOUND_RICHARDS_HOUSE_KEY }, + { RG_ALLEY_HOUSE_KEY, TIMESTAMP_FOUND_ALLEY_HOUSE_KEY }, + { RG_KAK_BAZAAR_KEY, TIMESTAMP_FOUND_KAK_BAZAAR_KEY }, + { RG_KAK_POTION_SHOP_KEY, TIMESTAMP_FOUND_KAK_POTION_SHOP_KEY }, + { RG_BOSS_HOUSE_KEY, TIMESTAMP_FOUND_BOSS_HOUSE_KEY }, + { RG_GRANNYS_POTION_SHOP_KEY, TIMESTAMP_FOUND_GRANNYS_POTION_SHOP_KEY }, + { RG_SKULLTULA_HOUSE_KEY, TIMESTAMP_FOUND_SKULLTULA_HOUSE_KEY }, + { RG_IMPAS_HOUSE_KEY, TIMESTAMP_FOUND_IMPAS_HOUSE_KEY }, + { RG_WINDMILL_KEY, TIMESTAMP_FOUND_WINDMILL_KEY }, + { RG_KAK_SHOOTING_GALLERY_KEY, TIMESTAMP_FOUND_KAK_SHOOTING_GALLERY_KEY }, + { RG_DAMPES_HUT_KEY, TIMESTAMP_FOUND_DAMPES_HUT_KEY }, + { RG_TALONS_HOUSE_KEY, TIMESTAMP_FOUND_TALONS_HOUSE_KEY }, + { RG_STABLES_KEY, TIMESTAMP_FOUND_STABLES_KEY }, + { RG_BACK_TOWER_KEY, TIMESTAMP_FOUND_BACK_TOWER_KEY }, + { RG_HYLIA_LAB_KEY, TIMESTAMP_FOUND_HYLIA_LAB_KEY }, + { RG_FISHING_HOLE_KEY, TIMESTAMP_FOUND_FISHING_HOLE_KEY }, +}; + // Gameplay stat tracking: Update time the item was acquired // (special cases for rando items) void Randomizer_GameplayStats_SetTimestamp(uint16_t item) { @@ -5797,6 +5844,12 @@ void Randomizer_GameplayStats_SetTimestamp(uint16_t item) { // Use ITEM_KEY_BOSS to timestamp Ganon's boss key if (item == RG_GANONS_CASTLE_BOSS_KEY) { gSaveContext.ship.stats.itemTimestamp[ITEM_KEY_BOSS] = time; + return; + } + + if (randomizerGetToStatsTimeStamp.contains((RandomizerGet)item)) { + gSaveContext.ship.stats.itemTimestamp[randomizerGetToStatsTimeStamp[(RandomizerGet)item]] = time; + return; } // Count any bottled item as a bottle @@ -5806,6 +5859,7 @@ void Randomizer_GameplayStats_SetTimestamp(uint16_t item) { } return; } + // Count any bombchu pack as bombchus if ((item >= RG_BOMBCHU_5 && item <= RG_BOMBCHU_20) || item == RG_PROGRESSIVE_BOMBCHUS) { if (gSaveContext.ship.stats.itemTimestamp[ITEM_BOMBCHU] = 0) { @@ -5813,11 +5867,15 @@ void Randomizer_GameplayStats_SetTimestamp(uint16_t item) { } return; } + if (item == RG_MAGIC_SINGLE) { gSaveContext.ship.stats.itemTimestamp[ITEM_SINGLE_MAGIC] = time; + return; } + if (item == RG_DOUBLE_DEFENSE) { gSaveContext.ship.stats.itemTimestamp[ITEM_DOUBLE_DEFENSE] = time; + return; } } diff --git a/soh/soh/Enhancements/randomizer/randomizerTypes.h b/soh/soh/Enhancements/randomizer/randomizerTypes.h index 15a2fbd37..c2b9cbbdd 100644 --- a/soh/soh/Enhancements/randomizer/randomizerTypes.h +++ b/soh/soh/Enhancements/randomizer/randomizerTypes.h @@ -2396,7 +2396,7 @@ typedef enum { RC_GF_SOUTHMOST_CENTER_CRATE, RC_GF_MID_SOUTH_CENTER_CRATE, RC_GF_MID_NORTH_CENTER_CRATE, - RR_GF_NORTHMOST_CENTER_CRATE, + RC_GF_NORTHMOST_CENTER_CRATE, RC_GF_OUTSKIRTS_NE_CRATE, RC_GF_OUTSKIRTS_NW_CRATE, RC_GF_HBA_RANGE_CRATE_1, diff --git a/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp index fcace1017..04bc783d9 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp @@ -449,7 +449,8 @@ ItemTrackerNumbers GetItemCurrentAndMax(ItemTrackerItem item) { result.currentCapacity = IS_RANDO && !Flags_GetRandomizerInf(RAND_INF_HAS_WALLET) ? 0 : CUR_CAPACITY(UPG_WALLET); result.maxCapacity = - OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_INCLUDE_TYCOON_WALLET) ? 999 : 500; + IS_RANDO && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_INCLUDE_TYCOON_WALLET) ? 999 + : 500; result.currentAmmo = gSaveContext.rupees; break; case ITEM_BOMBCHU: @@ -502,7 +503,28 @@ ItemTrackerNumbers GetItemCurrentAndMax(ItemTrackerItem item) { result.maxCapacity = GERUDO_TRAINING_GROUND_SMALL_KEY_MAX; break; case SCENE_THIEVES_HIDEOUT: - result.maxCapacity = GERUDO_FORTRESS_SMALL_KEY_MAX; + if (IS_RANDO) { + switch (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_GERUDO_FORTRESS)) { + case RO_GF_CARPENTERS_NORMAL: + result.maxCapacity = GERUDO_FORTRESS_SMALL_KEY_MAX; + break; + case RO_GF_CARPENTERS_FAST: + result.maxCapacity = 1; + break; + case RO_GF_CARPENTERS_FREE: + result.maxCapacity = 0; + break; + default: + result.maxCapacity = 0; + SPDLOG_ERROR( + "Invalid value for RSK_GERUDO_FORTRESS: " + + OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_GERUDO_FORTRESS)); + assert(false); + break; + } + } else { + result.maxCapacity = GERUDO_FORTRESS_SMALL_KEY_MAX; + } break; case SCENE_INSIDE_GANONS_CASTLE: result.maxCapacity = GANONS_CASTLE_SMALL_KEY_MAX; diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index 5a506aaa2..0c6cd0114 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -2600,15 +2600,6 @@ extern "C" int CustomMessage_RetrieveIfExists(PlayState* play) { return false; } -extern "C" void Overlay_DisplayText(float duration, const char* text) { - Ship::Context::GetInstance()->GetWindow()->GetGui()->GetGameOverlay()->TextDrawNotification(duration, true, text); -} - -extern "C" void Overlay_DisplayText_Seconds(int seconds, const char* text) { - float duration = seconds * OTRGlobals::Instance->GetInterpolationFPS() * 0.05; - Ship::Context::GetInstance()->GetWindow()->GetGui()->GetGameOverlay()->TextDrawNotification(duration, true, text); -} - extern "C" void EntranceTracker_SetCurrentGrottoID(s16 entranceIndex) { SetCurrentGrottoIDForTracker(entranceIndex); } diff --git a/soh/soh/OTRGlobals.h b/soh/soh/OTRGlobals.h index 80b243863..2b644fe82 100644 --- a/soh/soh/OTRGlobals.h +++ b/soh/soh/OTRGlobals.h @@ -156,8 +156,6 @@ void Randomizer_SetSpoilerLoaded(bool spoilerLoaded); uint8_t Randomizer_GenerateRandomizer(); void Randomizer_ShowRandomizerMenu(); int CustomMessage_RetrieveIfExists(PlayState* play); -void Overlay_DisplayText(float duration, const char* text); -void Overlay_DisplayText_Seconds(int seconds, const char* text); GetItemEntry ItemTable_Retrieve(int16_t getItemID); GetItemEntry ItemTable_RetrieveEntry(s16 modIndex, s16 getItemID); void EntranceTracker_SetCurrentGrottoID(s16 entranceIndex); diff --git a/soh/soh/SohGui/SohMenuEnhancements.cpp b/soh/soh/SohGui/SohMenuEnhancements.cpp index bdc6f5344..488aeb720 100644 --- a/soh/soh/SohGui/SohMenuEnhancements.cpp +++ b/soh/soh/SohGui/SohMenuEnhancements.cpp @@ -56,7 +56,9 @@ static const std::unordered_map chestStyleMatchesContentsO static const std::unordered_map timeTravelOptions = { { TIME_TRAVEL_DISABLED, "Disabled" }, { TIME_TRAVEL_OOT, "Ocarina of Time" }, + { TIME_TRAVEL_OOT_MS, "Ocarina of Time + Master Sword" }, { TIME_TRAVEL_ANY, "Any Ocarina" }, + { TIME_TRAVEL_ANY_MS, "Any Ocarina + Master Sword" }, }; static const std::unordered_map sleepingWaterfallOptions = { @@ -783,9 +785,9 @@ void SohMenu::AddMenuEnhancements() { .Tooltip("Allows Link to freely change age by playing the Song of Time.\n" "Time Blocks can still be used properly.\n\n" "Requirements:\n" - " - Obtained the Ocarina of Time (depends on selection)\n" " - Obtained the Song of Time\n" - " - Obtained the Master Sword\n" + " - Obtained the Ocarina of Time (depends on selection)\n" + " - Obtained the Master Sword (depends on selection)\n" " - Not within range of a Time Block\n" " - Not within range of Ocarina Playing spots")); diff --git a/soh/src/code/audio_load.c b/soh/src/code/audio_load.c index 81f67f9c0..52efa4016 100644 --- a/soh/src/code/audio_load.c +++ b/soh/src/code/audio_load.c @@ -640,10 +640,8 @@ s32 AudioLoad_SyncInitSeqPlayerInternal(s32 playerIdx, s32 seqId, s32 arg2) { if (gPlayState != NULL) { previousSceneNum = gPlayState->sceneNum; } - const char* sequenceName = AudioCollection_GetSequenceName(seqId); - if (sequenceName != NULL) { - Overlay_DisplayText_Seconds(CVarGetInteger(CVAR_AUDIO("SeqNameOverlayDuration"), 5), sequenceName); - } + + AudioCollection_EmitSongNameNotification(seqId); } } 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;