diff --git a/mm/2s2h/BenGui/BenMenuBar.cpp b/mm/2s2h/BenGui/BenMenuBar.cpp index ba2b746e0..afdb52871 100644 --- a/mm/2s2h/BenGui/BenMenuBar.cpp +++ b/mm/2s2h/BenGui/BenMenuBar.cpp @@ -653,6 +653,9 @@ void DrawEnhancementsMenu() { { .tooltip = "Enables using the Dpad for Ocarina playback." }); UIWidgets::CVarCheckbox("Prevent Dropped Ocarina Inputs", "gEnhancements.Playback.NoDropOcarinaInput", { .tooltip = "Prevent dropping inputs when playing the ocarina quickly" }); + UIWidgets::CVarCheckbox("Pause Owl Warp", "gEnhancements.Songs.PauseOwlWarp", + { .tooltip = "Allows the player to use the pause menu map to owl warp instead of " + "having to play the Song of Soaring." }); ImGui::EndMenu(); } diff --git a/mm/2s2h/Enhancements/Enhancements.cpp b/mm/2s2h/Enhancements/Enhancements.cpp index 0f0867f8a..1590e8a67 100644 --- a/mm/2s2h/Enhancements/Enhancements.cpp +++ b/mm/2s2h/Enhancements/Enhancements.cpp @@ -48,6 +48,7 @@ void InitEnhancements() { // Songs RegisterEnableSunsSong(); + RegisterPauseOwlWarp(); // Restorations RegisterPowerCrouchStab(); diff --git a/mm/2s2h/Enhancements/Enhancements.h b/mm/2s2h/Enhancements/Enhancements.h index dbdc7364a..da9a3a2bb 100644 --- a/mm/2s2h/Enhancements/Enhancements.h +++ b/mm/2s2h/Enhancements/Enhancements.h @@ -28,6 +28,7 @@ #include "Graphics/PlayAsKafei.h" #include "Player/Player.h" #include "Songs/EnableSunsSong.h" +#include "Songs/PauseOwlWarp.h" #include "Saving/SavingEnhancements.h" #include "Graphics/DisableBlackBars.h" #include "Modes/TimeMovesWhenYouMove.h" diff --git a/mm/2s2h/Enhancements/Songs/PauseOwlWarp.cpp b/mm/2s2h/Enhancements/Songs/PauseOwlWarp.cpp new file mode 100644 index 000000000..a86c1b943 --- /dev/null +++ b/mm/2s2h/Enhancements/Songs/PauseOwlWarp.cpp @@ -0,0 +1,222 @@ +#include "PauseOwlWarp.h" +#include +#include "Enhancements/GameInteractor/GameInteractor.h" + +extern "C" { +#include "overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope.h" + +extern f32 sPauseMenuVerticalOffset; +extern u16 sCursorPointsToOcarinaModes[]; +extern u16 sOwlWarpPauseItems[]; +extern u16 D_80AF343C[]; +extern s16 sInDungeonScene; +} + +extern "C" bool PauseOwlWarp_IsOwlWarpEnabled() { + return CVarGetInteger("gEnhancements.Songs.PauseOwlWarp", 0) && CHECK_QUEST_ITEM(QUEST_SONG_SOARING) && + gSaveContext.save.saveInfo.playerData.owlActivationFlags != 0 && + gPlayState->pauseCtx.debugEditor == DEBUG_EDITOR_NONE; +} + +static bool sIsConfirming = false; +static u16 sStickAdjTimer = 0; + +void ResetMessageContext() { + gPlayState->msgCtx.msgLength = 0; + gPlayState->msgCtx.msgMode = MSGMODE_NONE; +} + +void ClosePauseMenu(PauseContext* pauseCtx) { + Interface_SetAButtonDoAction(gPlayState, DO_ACTION_NONE); + Audio_PlaySfx_PauseMenuOpenOrClose(SFX_PAUSE_MENU_CLOSE); + gPlayState->msgCtx.ocarinaMode = OCARINA_MODE_END; + gSaveContext.prevHudVisibility = HUD_VISIBILITY_ALL; + sIsConfirming = false; + Message_CloseTextbox(gPlayState); + pauseCtx->state = PAUSE_STATE_UNPAUSE_SETUP; +} + +void HandleConfirmingState(PauseContext* pauseCtx, Input* input) { + if (Message_ShouldAdvance(gPlayState)) { + ResetMessageContext(); + sIsConfirming = false; + if (gPlayState->msgCtx.choiceIndex == 0) { // Yes + Interface_SetAButtonDoAction(gPlayState, DO_ACTION_NONE); + Audio_PlaySfx_PauseMenuOpenOrClose(SFX_PAUSE_MENU_CLOSE); + gPlayState->msgCtx.ocarinaMode = sCursorPointsToOcarinaModes[pauseCtx->cursorPoint[PAUSE_WORLD_MAP]]; + Audio_PlaySfx(NA_SE_SY_DECIDE); + Message_CloseTextbox(gPlayState); + sPauseMenuVerticalOffset = -6240.0f; + pauseCtx->state = PAUSE_STATE_UNPAUSE_SETUP; + gPlayState->nextEntrance = D_80AF343C[pauseCtx->cursorPoint[PAUSE_WORLD_MAP]]; + gPlayState->transitionTrigger = TRANS_TRIGGER_START; + gPlayState->transitionType = TRANS_TYPE_FADE_WHITE; + } else { // No + Interface_SetAButtonDoAction(gPlayState, DO_ACTION_WARP); + Audio_PlaySfx(NA_SE_SY_MESSAGE_PASS); + Message_CloseTextbox(gPlayState); + } + } else if (CHECK_BTN_ALL(input->press.button, BTN_B)) { + ResetMessageContext(); + sIsConfirming = false; + Audio_PlaySfx(NA_SE_SY_MESSAGE_PASS); + Message_CloseTextbox(gPlayState); + } else if (CHECK_BTN_ALL(input->press.button, BTN_START)) { + ClosePauseMenu(pauseCtx); + } +} + +void UpdateCursorForOwlWarpPoints(PauseContext* pauseCtx) { + if (pauseCtx->cursorSpecialPos == 0) { + if (pauseCtx->stickAdjX > 30) { + pauseCtx->cursorShrinkRate = 4.0f; + sStickAdjTimer = 0; + do { + pauseCtx->cursorPoint[PAUSE_WORLD_MAP]++; + if (pauseCtx->cursorPoint[PAUSE_WORLD_MAP] > OWL_WARP_STONE_TOWER) { + KaleidoScope_MoveCursorToSpecialPos(gPlayState, PAUSE_CURSOR_PAGE_RIGHT); + return; + } + } while (!pauseCtx->worldMapPoints[pauseCtx->cursorPoint[PAUSE_WORLD_MAP]]); + if (pauseCtx->worldMapPoints[pauseCtx->cursorPoint[PAUSE_WORLD_MAP]]) { + pauseCtx->cursorItem[PAUSE_MAP] = + sOwlWarpPauseItems[pauseCtx->cursorPoint[PAUSE_WORLD_MAP]] - ITEM_MAP_POINT_GREAT_BAY; + pauseCtx->cursorSlot[PAUSE_MAP] = 31 + pauseCtx->cursorPoint[PAUSE_WORLD_MAP]; + Audio_PlaySfx(NA_SE_SY_CURSOR); + } + } else if (pauseCtx->stickAdjX < -30) { + pauseCtx->cursorShrinkRate = 4.0f; + sStickAdjTimer = 0; + do { + pauseCtx->cursorPoint[PAUSE_WORLD_MAP]--; + if (pauseCtx->cursorPoint[PAUSE_WORLD_MAP] < OWL_WARP_GREAT_BAY_COAST) { + KaleidoScope_MoveCursorToSpecialPos(gPlayState, PAUSE_CURSOR_PAGE_LEFT); + return; + } + } while (!pauseCtx->worldMapPoints[pauseCtx->cursorPoint[PAUSE_WORLD_MAP]]); + if (pauseCtx->worldMapPoints[pauseCtx->cursorPoint[PAUSE_WORLD_MAP]]) { + pauseCtx->cursorItem[PAUSE_MAP] = + sOwlWarpPauseItems[pauseCtx->cursorPoint[PAUSE_WORLD_MAP]] - ITEM_MAP_POINT_GREAT_BAY; + pauseCtx->cursorSlot[PAUSE_MAP] = 31 + pauseCtx->cursorPoint[PAUSE_WORLD_MAP]; + Audio_PlaySfx(NA_SE_SY_CURSOR); + } + } else { + sStickAdjTimer++; + } + } else if (pauseCtx->cursorSpecialPos == PAUSE_CURSOR_PAGE_LEFT && pauseCtx->stickAdjX > 30) { + KaleidoScope_MoveCursorFromSpecialPos(gPlayState); + for (int i = OWL_WARP_GREAT_BAY_COAST; i <= OWL_WARP_STONE_TOWER; i++) { + if (pauseCtx->worldMapPoints[i]) { + pauseCtx->cursorPoint[PAUSE_WORLD_MAP] = i; + pauseCtx->cursorItem[PAUSE_MAP] = + sOwlWarpPauseItems[pauseCtx->cursorPoint[PAUSE_WORLD_MAP]] - ITEM_MAP_POINT_GREAT_BAY; + pauseCtx->cursorSlot[PAUSE_MAP] = 31 + pauseCtx->cursorPoint[PAUSE_WORLD_MAP]; + KaleidoScope_UpdateWorldMapCursor(gPlayState); + KaleidoScope_UpdateNamePanel(gPlayState); + return; + } + } + KaleidoScope_MoveCursorToSpecialPos(gPlayState, PAUSE_CURSOR_PAGE_RIGHT); + } else if (pauseCtx->cursorSpecialPos == PAUSE_CURSOR_PAGE_RIGHT && pauseCtx->stickAdjX < -30) { + KaleidoScope_MoveCursorFromSpecialPos(gPlayState); + for (int i = OWL_WARP_STONE_TOWER; i >= OWL_WARP_GREAT_BAY_COAST; i--) { + if (pauseCtx->worldMapPoints[i]) { + pauseCtx->cursorPoint[PAUSE_WORLD_MAP] = i; + pauseCtx->cursorItem[PAUSE_MAP] = + sOwlWarpPauseItems[pauseCtx->cursorPoint[PAUSE_WORLD_MAP]] - ITEM_MAP_POINT_GREAT_BAY; + pauseCtx->cursorSlot[PAUSE_MAP] = 31 + pauseCtx->cursorPoint[PAUSE_WORLD_MAP]; + KaleidoScope_UpdateWorldMapCursor(gPlayState); + KaleidoScope_UpdateNamePanel(gPlayState); + return; + } + } + KaleidoScope_MoveCursorToSpecialPos(gPlayState, PAUSE_CURSOR_PAGE_LEFT); + } +} + +void HandlePauseOwlWarp(PauseContext* pauseCtx) { + // Initialize worldMapPoints based on owl activation flags + for (int i = OWL_WARP_STONE_TOWER; i >= OWL_WARP_GREAT_BAY_COAST; i--) { + pauseCtx->worldMapPoints[i] = (gSaveContext.save.saveInfo.playerData.owlActivationFlags >> i) & 1; + } + + // Special condition for when only the 15th owl statue is activated + if (gSaveContext.save.saveInfo.playerData.owlActivationFlags == (1 << 15)) { + for (int i = REGION_GREAT_BAY; i < REGION_MAX; i++) { + if ((gSaveContext.save.saveInfo.regionsVisited >> i) & 1) { + switch (i) { + case REGION_GREAT_BAY: + pauseCtx->worldMapPoints[OWL_WARP_GREAT_BAY_COAST] = true; + break; + case REGION_ZORA_HALL: + pauseCtx->worldMapPoints[OWL_WARP_ZORA_CAPE] = true; + break; + case REGION_ROMANI_RANCH: + pauseCtx->worldMapPoints[OWL_WARP_SNOWHEAD] = true; + break; + case REGION_DEKU_PALACE: + pauseCtx->worldMapPoints[OWL_WARP_MOUNTAIN_VILLAGE] = true; + break; + case REGION_WOODFALL: + pauseCtx->worldMapPoints[OWL_WARP_CLOCK_TOWN] = true; + break; + case REGION_CLOCK_TOWN: + pauseCtx->worldMapPoints[OWL_WARP_MILK_ROAD] = true; + break; + case REGION_SNOWHEAD: + pauseCtx->worldMapPoints[OWL_WARP_WOODFALL] = true; + break; + case REGION_IKANA_GRAVEYARD: + pauseCtx->worldMapPoints[OWL_WARP_SOUTHERN_SWAMP] = true; + break; + case REGION_IKANA_CANYON: + pauseCtx->worldMapPoints[OWL_WARP_IKANA_CANYON] = true; + break; + case REGION_GORON_VILLAGE: + pauseCtx->worldMapPoints[OWL_WARP_STONE_TOWER] = true; + break; + } + } + } + } + + // Ensure cursor starts at an activated point + if (!pauseCtx->worldMapPoints[pauseCtx->cursorPoint[PAUSE_WORLD_MAP]]) { + for (int i = OWL_WARP_GREAT_BAY_COAST; i <= OWL_WARP_STONE_TOWER; i++) { + if (pauseCtx->worldMapPoints[i]) { + pauseCtx->cursorPoint[PAUSE_WORLD_MAP] = i; + break; + } + } + } + + Player* player = GET_PLAYER(gPlayState); + Input* input = &gPlayState->state.input[0]; + + if ((pauseCtx->state == PAUSE_STATE_MAIN && pauseCtx->pageIndex == PAUSE_MAP && pauseCtx->mapPageRoll == 0) || + sIsConfirming) { + if (CHECK_BTN_ALL(input->press.button, BTN_START) || CHECK_BTN_ALL(input->press.button, BTN_B)) { + ClosePauseMenu(pauseCtx); + } else if (CHECK_BTN_ALL(input->press.button, BTN_A) && !sIsConfirming) { + if (pauseCtx->cursorSpecialPos == 0 && pauseCtx->worldMapPoints[pauseCtx->cursorPoint[PAUSE_WORLD_MAP]]) { + Audio_PlaySfx(NA_SE_SY_DECIDE); + Message_StartTextbox(gPlayState, 0x1B93, NULL); + sIsConfirming = true; + } + } else if (sIsConfirming) { + HandleConfirmingState(pauseCtx, input); + } else { + KaleidoScope_UpdateOwlWarpNamePanel(gPlayState); + } + + UpdateCursorForOwlWarpPoints(pauseCtx); + } +} + +void RegisterPauseOwlWarp() { + GameInteractor::Instance->RegisterGameHook([](PauseContext* pauseCtx) { + if (!sInDungeonScene && PauseOwlWarp_IsOwlWarpEnabled() && CHECK_QUEST_ITEM(QUEST_SONG_SOARING)) { + HandlePauseOwlWarp(pauseCtx); + } + }); +} \ No newline at end of file diff --git a/mm/2s2h/Enhancements/Songs/PauseOwlWarp.h b/mm/2s2h/Enhancements/Songs/PauseOwlWarp.h new file mode 100644 index 000000000..b6adfeab9 --- /dev/null +++ b/mm/2s2h/Enhancements/Songs/PauseOwlWarp.h @@ -0,0 +1,15 @@ +#ifndef PAUSE_OWL_WARP_H +#define PAUSE_OWL_WARP_H + +#ifdef __cplusplus +extern "C" { +#endif + +void RegisterPauseOwlWarp(void); +bool PauseOwlWarp_IsOwlWarpEnabled(void); + +#ifdef __cplusplus +} +#endif + +#endif // PAUSE_OWL_WARP_H \ No newline at end of file diff --git a/mm/CMakeLists.txt b/mm/CMakeLists.txt index b03c9ff59..1f4a1f728 100644 --- a/mm/CMakeLists.txt +++ b/mm/CMakeLists.txt @@ -523,6 +523,7 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang|AppleClang") $<$: #-Werror-implicit-function-declaration -Wno-incompatible-pointer-types + -Wno-int-conversion > $<$:-fpermissive> $<$: diff --git a/mm/include/functions.h b/mm/include/functions.h index 62ac3cb60..2388f8ca4 100644 --- a/mm/include/functions.h +++ b/mm/include/functions.h @@ -1328,6 +1328,8 @@ void osContGetReadData(OSContPad* pad); // #region 2S2H [Port] Previously unavailable functions, made available for porting void PadMgr_ThreadEntry(); void Heaps_Alloc(void); +void KaleidoScope_UpdateOwlWarpNamePanel(PlayState* play); +void KaleidoScope_UpdateNamePanel(PlayState* play); // #endregion // #region 2S2H [Port] New methods added for porting void AudioSeq_SetPortVolumeScale(u8 seqPlayerIndex, f32 volume); diff --git a/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_map.c b/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_map.c index d0e23c839..ffa599dde 100644 --- a/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_map.c +++ b/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_map.c @@ -13,6 +13,8 @@ #include "BenPort.h" +#include "2s2h/Enhancements/Songs/PauseOwlWarp.h" + // 2S2H [Port] (and line 26) don't do pointer math and access the list of digits directly. extern const char* sCounterTextures[]; @@ -675,7 +677,7 @@ void KaleidoScope_DrawWorldMap(PlayState* play) { Gfx_SetupDL42_Opa(play->state.gfxCtx); - if (!IS_PAUSE_STATE_OWLWARP) { + if (!IS_PAUSE_STATE_OWLWARP && !PauseOwlWarp_IsOwlWarpEnabled()) { // Browsing the world map regions on the pause menu gDPLoadTextureBlock(POLY_OPA_DISP++, gWorldMapDotTex, G_IM_FMT_IA, G_IM_SIZ_8b, 8, 8, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, @@ -846,7 +848,7 @@ void KaleidoScope_UpdateWorldMapCursor(PlayState* play) { s16 oldCursorPoint; if ((pauseCtx->state == PAUSE_STATE_MAIN) && (pauseCtx->mainState == PAUSE_MAIN_STATE_IDLE) && - (pauseCtx->pageIndex == PAUSE_MAP)) { + (pauseCtx->pageIndex == PAUSE_MAP) && !PauseOwlWarp_IsOwlWarpEnabled()) { pauseCtx->cursorColorSet = PAUSE_CURSOR_COLOR_SET_WHITE; oldCursorPoint = pauseCtx->cursorPoint[PAUSE_WORLD_MAP]; diff --git a/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope_NES.c b/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope_NES.c index 5446f710c..79a7d2ef6 100644 --- a/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope_NES.c +++ b/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope_NES.c @@ -24,6 +24,8 @@ #include "2s2h/Enhancements/GameInteractor/GameInteractor.h" +#include "2s2h/Enhancements/Songs/PauseOwlWarp.h" + // Page Textures (Background of Page): // Broken up into multiple textures. // Numbered by column/row. @@ -2901,7 +2903,7 @@ void KaleidoScope_UpdateCursorSize(PlayState* play) { case PAUSE_MAP: if (!sInDungeonScene) { - if (IS_PAUSE_STATE_OWLWARP) { + if (IS_PAUSE_STATE_OWLWARP || PauseOwlWarp_IsOwlWarpEnabled()) { pauseCtx->cursorX = sOwlWarpWorldMapCursorsX[pauseCtx->cursorPoint[PAUSE_WORLD_MAP]]; pauseCtx->cursorY = sOwlWarpWorldMapCursorsY[pauseCtx->cursorPoint[PAUSE_WORLD_MAP]]; } else {