From 8499286899d2998185b0aa2c8423dc0b57d2038d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philip=20Dub=C3=A9?= <159546+serprex@users.noreply.github.com> Date: Fri, 12 Jun 2026 04:59:19 +0000 Subject: [PATCH] Only store grotto entrance for voiding out when grottos mixed, or in decoupled (#6721) Does some bookkeeping for shuffled spawns / warp songs / owl --- .../randomizer/randomizer_grotto.c | 36 +++++++++++++++---- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/randomizer_grotto.c b/soh/soh/Enhancements/randomizer/randomizer_grotto.c index 51576725d3..55185f5677 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_grotto.c +++ b/soh/soh/Enhancements/randomizer/randomizer_grotto.c @@ -95,6 +95,7 @@ static s16 grottoLoadList[NUM_GROTTOS] = { 0 }; static s8 grottoId = 0xFF; static s8 lastEntranceType = NOT_GROTTO; static bool overridingNextEntrance = false; +static bool grottoEnteredViaDoorAna = false; // Initialize both lists so that each index refers to itself. An index referring // to itself means that the entrance is not shuffled. Indices will be overwritten @@ -110,6 +111,13 @@ void Grotto_InitExitAndLoadLists(void) { grottoId = 0xFF; lastEntranceType = NOT_GROTTO; overridingNextEntrance = false; + grottoEnteredViaDoorAna = false; +} + +static bool Grotto_ShouldSetLastEntrance(void) { + return Randomizer_GetSettingValue(RSK_SHUFFLE_GROTTO_ENTRANCES) && + (Randomizer_GetSettingValue(RSK_MIX_GROTTO_ENTRANCES) || + Randomizer_GetSettingValue(RSK_DECOUPLED_ENTRANCES)); } void Grotto_SetExitOverride(s16 originalIndex, s16 overrideIndex) { @@ -193,9 +201,13 @@ s16 Grotto_OverrideSpecialEntrance(s16 nextEntranceIndex) { return nextEntranceIndex; } + // ENTR_RETURN_GROTTO means Link physically left a grotto. Any other way (warp song / owl / spawn) + // arrives as a concrete grotto-exit index. + bool grottoExit = nextEntranceIndex == ENTR_RETURN_GROTTO; + // If Link hits a grotto exit, load the entrance index from the grotto exit list // based on the current grotto ID - if (nextEntranceIndex == ENTR_RETURN_GROTTO) { + if (grottoExit) { Entrance_SetEntranceDiscovered(ENTRANCE_GROTTO_EXIT_START + grottoId, false); EntranceTracker_SetLastEntranceOverride(ENTRANCE_GROTTO_EXIT_START + grottoId); nextEntranceIndex = grottoExitList[grottoId]; @@ -207,17 +219,27 @@ s16 Grotto_OverrideSpecialEntrance(s16 nextEntranceIndex) { // Grotto Returns if (nextEntranceIndex >= ENTRANCE_GROTTO_EXIT_START && nextEntranceIndex < ENTRANCE_GROTTO_EXIT_START + NUM_GROTTOS) { - GrottoReturnInfo grotto = grottoReturnTable[grottoId]; - Grotto_SetupReturnInfo(grotto, RESPAWN_MODE_RETURN); - Grotto_SetupReturnInfo(grotto, RESPAWN_MODE_DOWN); + + // Normally grotto exit leaves pre-grotto respawn data that Door_Ana set on entry alone, + // so void-out returns to other last entrance. This is only when leaving grotto entered through + // Door_Ana (grottoExit && grottoEnteredViaDoorAna): only then does RESPAWN_MODE_RETURN hold + // valid pre-grotto data. A warp song or spawn shuffled onto a grotto load point enters without + // Door_Ana (so RETURN is stale), those must set up the grotto's own return data to position Link. + bool normalGrottoExit = !Grotto_ShouldSetLastEntrance() && grottoExit && grottoEnteredViaDoorAna; + + if (!normalGrottoExit) { + Grotto_SetupReturnInfo(grotto, RESPAWN_MODE_RETURN); + Grotto_SetupReturnInfo(grotto, RESPAWN_MODE_DOWN); + } // When the nextEntranceIndex is determined by a dynamic exit, // or set by Entrance_OverrideBlueWarp to mark a blue warp entrance, // we have to set the respawn information and nextEntranceIndex manually if (gPlayState != NULL && gPlayState->nextEntranceIndex != ENTR_LOAD_OPENING) { gSaveContext.respawnFlag = 2; - nextEntranceIndex = grotto.entranceIndex; + nextEntranceIndex = + normalGrottoExit ? gSaveContext.respawn[RESPAWN_MODE_RETURN].entranceIndex : grotto.entranceIndex; gPlayState->transitionType = TRANS_TYPE_FADE_WHITE; gSaveContext.nextTransitionType = TRANS_TYPE_FADE_WHITE; } else if (gPlayState == NULL) { // Handle spawn position when loading from a save file @@ -229,7 +251,7 @@ s16 Grotto_OverrideSpecialEntrance(s16 nextEntranceIndex) { nextEntranceIndex = ENTR_RETURN_GROTTO; } - lastEntranceType = GROTTO_RETURN; + lastEntranceType = normalGrottoExit ? NOT_GROTTO : GROTTO_RETURN; // Grotto Loads } else if (nextEntranceIndex >= ENTRANCE_GROTTO_LOAD_START && nextEntranceIndex < ENTRANCE_GROTTO_EXIT_START) { // Set the respawn data to load the correct grotto @@ -240,6 +262,7 @@ s16 Grotto_OverrideSpecialEntrance(s16 nextEntranceIndex) { EntranceTracker_SetCurrentGrottoID(grottoId); lastEntranceType = NOT_GROTTO; + grottoEnteredViaDoorAna = false; // Otherwise just unset the current grotto ID } else { grottoId = 0xFF; @@ -275,6 +298,7 @@ void Grotto_OverrideActorEntrance(Actor* thisx) { // Run the index through the special entrances override check lastEntranceType = GROTTO_LOAD; gPlayState->nextEntranceIndex = Grotto_OverrideSpecialEntrance(index); + grottoEnteredViaDoorAna = true; return; } }