diff --git a/soh/soh/Enhancements/randomizer/3drando/fill.cpp b/soh/soh/Enhancements/randomizer/3drando/fill.cpp index 8536c1d17..7f424f758 100644 --- a/soh/soh/Enhancements/randomizer/3drando/fill.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/fill.cpp @@ -324,7 +324,12 @@ std::vector GetAccessibleLocations(const std::vectorGetEntranceShuffler()->HasNoRandomEntrances()) { + // Include bluewarps when unshuffled but dungeon or boss shuffle is on + if (mode == SearchMode::GeneratePlaythrough && + (exit.IsShuffled() || + (exit.GetType() == Rando::EntranceType::BlueWarp && + (ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES) || ctx->GetOption(RSK_SHUFFLE_BOSS_ENTRANCES)))) && + !exit.IsAddedToPool() && !ctx->GetEntranceShuffler()->HasNoRandomEntrances()) { entranceSphere.push_back(&exit); exit.AddToPool(); // Don't list a two-way coupled entrance from both directions diff --git a/soh/soh/Enhancements/randomizer/3drando/location_access.cpp b/soh/soh/Enhancements/randomizer/3drando/location_access.cpp index 6c131e8ba..7eb1436df 100644 --- a/soh/soh/Enhancements/randomizer/3drando/location_access.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/location_access.cpp @@ -501,7 +501,7 @@ Area* AreaTable(const RandomizerRegion areaKey) { //Retrieve all the shuffable entrances of a specific type std::vector GetShuffleableEntrances(Rando::EntranceType type, bool onlyPrimary /*= true*/) { std::vector entrancesToShuffle = {}; - for (RandomizerRegion area : Areas::GetAllAreas()) { + for (RandomizerRegion area : Areas::GetAllAreas()) { for (auto& exit: AreaTable(area)->exits) { if ((exit.GetType() == type || type == Rando::EntranceType::All) && (exit.IsPrimary() || !onlyPrimary) && exit.GetType() != Rando::EntranceType::None) { entrancesToShuffle.push_back(&exit); @@ -510,3 +510,16 @@ std::vector GetShuffleableEntrances(Rando::EntranceType type, } return entrancesToShuffle; } + +// Get the specific entrance by name +Rando::Entrance* GetEntrance(const std::string name) { + for (RandomizerRegion area : Areas::GetAllAreas()) { + for (auto& exit : AreaTable(area)->exits) { + if (exit.GetName() == name) { + return &exit; + } + } + } + + return nullptr; +} diff --git a/soh/soh/Enhancements/randomizer/3drando/location_access.hpp b/soh/soh/Enhancements/randomizer/3drando/location_access.hpp index 8d36e7eaf..09fc5209e 100644 --- a/soh/soh/Enhancements/randomizer/3drando/location_access.hpp +++ b/soh/soh/Enhancements/randomizer/3drando/location_access.hpp @@ -251,6 +251,7 @@ namespace Areas { void AreaTable_Init(); Area* AreaTable(const RandomizerRegion areaKey); std::vector GetShuffleableEntrances(Rando::EntranceType type, bool onlyPrimary = true); +Rando::Entrance* GetEntrance(const std::string name); // Overworld void AreaTable_Init_LostWoods(); diff --git a/soh/soh/Enhancements/randomizer/3drando/location_access/locacc_deku_tree.cpp b/soh/soh/Enhancements/randomizer/3drando/location_access/locacc_deku_tree.cpp index af71727d4..39654e63e 100644 --- a/soh/soh/Enhancements/randomizer/3drando/location_access/locacc_deku_tree.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/location_access/locacc_deku_tree.cpp @@ -270,5 +270,6 @@ void AreaTable_Init_DekuTree() { { // Exits Entrance(RR_DEKU_TREE_BOSS_ENTRYWAY, { [] { return true; } }), + Entrance(RR_KF_OUTSIDE_DEKU_TREE, { [] { return logic->DekuTreeClear; } }), }); } diff --git a/soh/soh/Enhancements/randomizer/3drando/location_access/locacc_dodongos_cavern.cpp b/soh/soh/Enhancements/randomizer/3drando/location_access/locacc_dodongos_cavern.cpp index 86850b38d..9c7202b68 100644 --- a/soh/soh/Enhancements/randomizer/3drando/location_access/locacc_dodongos_cavern.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/location_access/locacc_dodongos_cavern.cpp @@ -307,5 +307,6 @@ void AreaTable_Init_DodongosCavern() { { // Exits Entrance(RR_DODONGOS_CAVERN_BOSS_ENTRYWAY, { [] { return true; } }), + Entrance(RR_DEATH_MOUNTAIN_TRAIL, { [] { return logic->DodongosCavernClear; } }), }); } diff --git a/soh/soh/Enhancements/randomizer/3drando/location_access/locacc_fire_temple.cpp b/soh/soh/Enhancements/randomizer/3drando/location_access/locacc_fire_temple.cpp index bc7002b57..de814b314 100644 --- a/soh/soh/Enhancements/randomizer/3drando/location_access/locacc_fire_temple.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/location_access/locacc_fire_temple.cpp @@ -418,5 +418,6 @@ void AreaTable_Init_FireTemple() { { // Exits Entrance(RR_FIRE_TEMPLE_BOSS_ENTRYWAY, { [] { return false; } }), + Entrance(RR_DMC_CENTRAL_LOCAL, { [] { return logic->FireTempleClear; } }), }); } diff --git a/soh/soh/Enhancements/randomizer/3drando/location_access/locacc_forest_temple.cpp b/soh/soh/Enhancements/randomizer/3drando/location_access/locacc_forest_temple.cpp index 5f79f53cd..4d46b80dc 100644 --- a/soh/soh/Enhancements/randomizer/3drando/location_access/locacc_forest_temple.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/location_access/locacc_forest_temple.cpp @@ -431,5 +431,6 @@ void AreaTable_Init_ForestTemple() { { // Exits Entrance(RR_FOREST_TEMPLE_BOSS_ENTRYWAY, { [] { return false; } }), + Entrance(RR_SACRED_FOREST_MEADOW, { [] { return logic->ForestTempleClear; } }), }); } diff --git a/soh/soh/Enhancements/randomizer/3drando/location_access/locacc_jabujabus_belly.cpp b/soh/soh/Enhancements/randomizer/3drando/location_access/locacc_jabujabus_belly.cpp index c54911a09..2816dd43a 100644 --- a/soh/soh/Enhancements/randomizer/3drando/location_access/locacc_jabujabus_belly.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/location_access/locacc_jabujabus_belly.cpp @@ -177,17 +177,17 @@ void AreaTable_Init_JabuJabusBelly() { //Locations LocationAccess(RC_JABU_JABUS_BELLY_MQ_SECOND_ROOM_LOWER_CHEST, {[]{return true;}}), LocationAccess(RC_JABU_JABUS_BELLY_MQ_SECOND_ROOM_UPPER_CHEST, {[]{return (logic->IsAdult && (logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_HOOKSHOT))) || ChildCanAccess(RR_JABU_JABUS_BELLY_MQ_BOSS_AREA);}}), - LocationAccess(RC_JABU_JABUS_BELLY_MQ_COMPASS_CHEST, {[]{return true;}}), - LocationAccess(RC_JABU_JABUS_BELLY_MQ_BASEMENT_NEAR_VINES_CHEST, {[]{return true;}}), - LocationAccess(RC_JABU_JABUS_BELLY_MQ_BASEMENT_NEAR_SWITCHES_CHEST, {[]{return true;}}), + LocationAccess(RC_JABU_JABUS_BELLY_MQ_COMPASS_CHEST, {[]{return (logic->IsChild || logic->CanDive || logic->CanUse(RG_IRON_BOOTS) || randoCtx->GetTrickOption(RT_JABU_ALCOVE_JUMP_DIVE)) && (logic->CanUse(RG_FAIRY_SLINGSHOT) || logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT) || logic->HasBombchus || (randoCtx->GetTrickOption(RT_JABU_MQ_RANG_JUMP) && logic->CanUse(RG_BOOMERANG)));}}), + LocationAccess(RC_JABU_JABUS_BELLY_MQ_BASEMENT_NEAR_VINES_CHEST, {[]{return logic->CanUse(RG_FAIRY_SLINGSHOT);}}), + LocationAccess(RC_JABU_JABUS_BELLY_MQ_BASEMENT_NEAR_SWITCHES_CHEST, {[]{return logic->CanUse(RG_FAIRY_SLINGSHOT);}}), LocationAccess(RC_JABU_JABUS_BELLY_MQ_BOOMERANG_ROOM_SMALL_CHEST, {[]{return true;}}), - LocationAccess(RC_JABU_JABUS_BELLY_MQ_BOOMERANG_CHEST, {[]{return true;}}), + LocationAccess(RC_JABU_JABUS_BELLY_MQ_BOOMERANG_CHEST, {[]{return logic->CanUse(RG_KOKIRI_SWORD) || logic->CanUse(RG_MASTER_SWORD) || logic->CanUse(RG_BIGGORON_SWORD) || logic->CanUse(RG_MEGATON_HAMMER) || logic->CanUse(RG_FAIRY_SLINGSHOT) || logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_STICKS) || logic->Bombs;}}), LocationAccess(RC_JABU_JABUS_BELLY_MQ_GS_BOOMERANG_CHEST_ROOM, {[]{return logic->CanUse(RG_SONG_OF_TIME) || (randoCtx->GetTrickOption(RT_JABU_MQ_SOT_GS) && logic->IsChild && logic->CanUse(RG_BOOMERANG));}}), //Trick: logic->CanUse(RG_SONG_OF_TIME) || (LogicJabuMQSoTGS && logic->IsChild && logic->CanUse(RG_BOOMERANG)) }, { //Exits Entrance(RR_JABU_JABUS_BELLY_MQ_BEGINNING, {[]{return true;}}), - Entrance(RR_JABU_JABUS_BELLY_MQ_DEPTHS, {[]{return logic->HasExplosives && logic->IsChild && logic->CanUse(RG_BOOMERANG);}}), + Entrance(RR_JABU_JABUS_BELLY_MQ_DEPTHS, {[]{return logic->HasExplosives && logic->CanUse(RG_FAIRY_SLINGSHOT) && logic->CanUse(RG_BOOMERANG);}}), }); areaTable[RR_JABU_JABUS_BELLY_MQ_DEPTHS] = Area("Jabu Jabus Belly MQ Depths", "Jabu Jabus Belly", RA_JABU_JABUS_BELLY, NO_DAY_NIGHT_CYCLE, {}, { @@ -207,8 +207,8 @@ void AreaTable_Init_JabuJabusBelly() { }, { //Locations LocationAccess(RC_JABU_JABUS_BELLY_MQ_COW, {[]{return logic->CanUse(RG_EPONAS_SONG);}}), - LocationAccess(RC_JABU_JABUS_BELLY_MQ_NEAR_BOSS_CHEST, {[]{return true;}}), - LocationAccess(RC_JABU_JABUS_BELLY_MQ_GS_NEAR_BOSS, {[]{return true;}}), + LocationAccess(RC_JABU_JABUS_BELLY_MQ_NEAR_BOSS_CHEST, {[]{return logic->CanUse(RG_FAIRY_SLINGSHOT);}}), + LocationAccess(RC_JABU_JABUS_BELLY_MQ_GS_NEAR_BOSS, {[]{return logic->CanUse(RG_BOOMERANG) || (randoCtx->GetTrickOption(RT_JABU_NEAR_BOSS_RANGED) && logic->CanUse(RG_HOOKSHOT));}}), }, { //Exits Entrance(RR_JABU_JABUS_BELLY_MQ_MAIN, {[]{return true;}}), @@ -243,5 +243,6 @@ void AreaTable_Init_JabuJabusBelly() { { // Exits Entrance(RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY, { [] { return false; } }), + Entrance(RR_ZORAS_FOUNTAIN, { [] { return logic->JabuJabusBellyClear; } }), }); } diff --git a/soh/soh/Enhancements/randomizer/3drando/location_access/locacc_shadow_temple.cpp b/soh/soh/Enhancements/randomizer/3drando/location_access/locacc_shadow_temple.cpp index 9a692e5c5..c742c9c2d 100644 --- a/soh/soh/Enhancements/randomizer/3drando/location_access/locacc_shadow_temple.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/location_access/locacc_shadow_temple.cpp @@ -207,5 +207,6 @@ void AreaTable_Init_ShadowTemple() { { // Exits Entrance(RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, { [] { return false; } }), + Entrance(RR_GRAVEYARD_WARP_PAD_REGION, { [] { return logic->ShadowTempleClear; } }), }); } diff --git a/soh/soh/Enhancements/randomizer/3drando/location_access/locacc_spirit_temple.cpp b/soh/soh/Enhancements/randomizer/3drando/location_access/locacc_spirit_temple.cpp index 4c27f5123..c859aeb8d 100644 --- a/soh/soh/Enhancements/randomizer/3drando/location_access/locacc_spirit_temple.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/location_access/locacc_spirit_temple.cpp @@ -269,5 +269,6 @@ void AreaTable_Init_SpiritTemple() { { // Exits Entrance(RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, { [] { return false; } }), + Entrance(RR_DESERT_COLOSSUS, { [] { return logic->SpiritTempleClear; } }), }); } diff --git a/soh/soh/Enhancements/randomizer/3drando/location_access/locacc_water_temple.cpp b/soh/soh/Enhancements/randomizer/3drando/location_access/locacc_water_temple.cpp index 001984ee7..e9a0b8678 100644 --- a/soh/soh/Enhancements/randomizer/3drando/location_access/locacc_water_temple.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/location_access/locacc_water_temple.cpp @@ -329,5 +329,6 @@ void AreaTable_Init_WaterTemple() { { // Exits Entrance(RR_WATER_TEMPLE_BOSS_ENTRYWAY, { [] { return false; } }), + Entrance(RR_LAKE_HYLIA, { [] { return logic->WaterTempleClear; } }), }); } diff --git a/soh/soh/Enhancements/randomizer/3drando/spoiler_log.cpp b/soh/soh/Enhancements/randomizer/3drando/spoiler_log.cpp index 6dd1802a6..ba63e4641 100644 --- a/soh/soh/Enhancements/randomizer/3drando/spoiler_log.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/spoiler_log.cpp @@ -316,35 +316,36 @@ static void WriteLocation( static void WriteShuffledEntrance(std::string sphereString, Entrance* entrance) { int16_t originalIndex = entrance->GetIndex(); int16_t destinationIndex = -1; - int16_t originalBlueWarp = entrance->GetBlueWarp(); - int16_t replacementBlueWarp = -1; int16_t replacementIndex = entrance->GetReplacement()->GetIndex(); int16_t replacementDestinationIndex = -1; std::string name = entrance->GetName(); std::string text = entrance->GetConnectedRegion()->regionName + " from " + entrance->GetReplacement()->GetParentRegion()->regionName; - if (entrance->GetReverse() != nullptr && !entrance->IsDecoupled()) { + // Track the reverse destination, useful for savewarp handling + if (entrance->GetReverse() != nullptr) { destinationIndex = entrance->GetReverse()->GetIndex(); - replacementDestinationIndex = entrance->GetReplacement()->GetReverse()->GetIndex(); - replacementBlueWarp = entrance->GetReplacement()->GetReverse()->GetBlueWarp(); + // When decouple is off we track the replacement's reverse destination, useful for recording visited entrances + if (!entrance->IsDecoupled()) { + replacementDestinationIndex = entrance->GetReplacement()->GetReverse()->GetIndex(); + } } json entranceJson = json::object({ + {"type", entrance->GetType()}, {"index", originalIndex}, {"destination", destinationIndex}, - {"blueWarp", originalBlueWarp}, {"override", replacementIndex}, {"overrideDestination", replacementDestinationIndex}, }); jsonData["entrances"].push_back(entranceJson); - // When decoupled entrances is off, handle saving reverse entrances with blue warps + // When decoupled entrances is off, handle saving reverse entrances if (entrance->GetReverse() != nullptr && !entrance->IsDecoupled()) { json reverseEntranceJson = json::object({ + {"type", entrance->GetReverse()->GetType()}, {"index", replacementDestinationIndex}, {"destination", replacementIndex}, - {"blueWarp", replacementBlueWarp}, {"override", destinationIndex}, {"overrideDestination", originalIndex}, }); diff --git a/soh/soh/Enhancements/randomizer/randomizerTypes.h b/soh/soh/Enhancements/randomizer/randomizerTypes.h index a86dc82c4..91906d127 100644 --- a/soh/soh/Enhancements/randomizer/randomizerTypes.h +++ b/soh/soh/Enhancements/randomizer/randomizerTypes.h @@ -3643,6 +3643,7 @@ typedef enum { RSK_SHUFFLE_OVERWORLD_SPAWNS, RSK_MIXED_ENTRANCE_POOLS, RSK_MIX_DUNGEON_ENTRANCES, + RSK_MIX_BOSS_ENTRANCES, RSK_MIX_OVERWORLD_ENTRANCES, RSK_MIX_INTERIOR_ENTRANCES, RSK_MIX_GROTTO_ENTRANCES, diff --git a/soh/soh/Enhancements/randomizer/randomizer_entrance.c b/soh/soh/Enhancements/randomizer/randomizer_entrance.c index fc2c651af..ea7e99eea 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_entrance.c +++ b/soh/soh/Enhancements/randomizer/randomizer_entrance.c @@ -40,17 +40,12 @@ s16 dynamicExitList[] = { // Owl Flights : 0x492064 and 0x492080 static s16 entranceOverrideTable[ENTRANCE_TABLE_SIZE] = {0}; -// Boss scenes (normalize boss scene range to 0 on lookup) to the replaced dungeon scene it is connected to -static s16 dungeonBossSceneOverrides[SHUFFLEABLE_BOSS_COUNT] = {0}; +// Boss scenes (normalize boss scene range to 0 on lookup) mapped to save/death warp entrance +static s16 bossSceneSaveDeathWarps[SHUFFLEABLE_BOSS_COUNT] = {0}; static ActorEntry modifiedLinkActorEntry = {0}; EntranceInfo originalEntranceTable[ENTRANCE_TABLE_SIZE] = {0}; -typedef struct { - s16 blueWarp; - s16 destination; -} BlueWarpReplacement; - typedef struct { s16 entryway; s16 exit; @@ -62,40 +57,25 @@ typedef struct { } DungeonEntranceInfo; static DungeonEntranceInfo dungeons[] = { - //entryway exit, boss, reverse, bluewarp, dungeon scene, boss scene - { DEKU_TREE_ENTRANCE, ENTR_KOKIRI_FOREST_1, ENTR_DEKU_TREE_BOSS_0, ENTR_DEKU_TREE_1, ENTR_KOKIRI_FOREST_11, SCENE_DEKU_TREE, SCENE_DEKU_TREE_BOSS }, - { DODONGOS_CAVERN_ENTRANCE, ENTR_DEATH_MOUNTAIN_TRAIL_3, ENTR_DODONGOS_CAVERN_BOSS_0, ENTR_DODONGOS_CAVERN_1, ENTR_DEATH_MOUNTAIN_TRAIL_5, SCENE_DODONGOS_CAVERN, SCENE_DODONGOS_CAVERN_BOSS }, - { JABU_JABUS_BELLY_ENTRANCE, ENTR_ZORAS_FOUNTAIN_1, ENTR_JABU_JABU_BOSS_0, ENTR_JABU_JABU_1, ENTR_ZORAS_FOUNTAIN_0, SCENE_JABU_JABU, SCENE_JABU_JABU_BOSS }, - { FOREST_TEMPLE_ENTRANCE, ENTR_SACRED_FOREST_MEADOW_1, ENTR_FOREST_TEMPLE_BOSS_0, ENTR_FOREST_TEMPLE_1, ENTR_SACRED_FOREST_MEADOW_3, SCENE_FOREST_TEMPLE, SCENE_FOREST_TEMPLE_BOSS }, - { FIRE_TEMPLE_ENTRANCE, ENTR_DEATH_MOUNTAIN_CRATER_2, ENTR_FIRE_TEMPLE_BOSS_0, ENTR_FIRE_TEMPLE_1, ENTR_DEATH_MOUNTAIN_CRATER_5, SCENE_FIRE_TEMPLE, SCENE_FIRE_TEMPLE_BOSS }, - { WATER_TEMPLE_ENTRANCE, ENTR_LAKE_HYLIA_2, ENTR_WATER_TEMPLE_BOSS_0, ENTR_WATER_TEMPLE_1, ENTR_LAKE_HYLIA_9, SCENE_WATER_TEMPLE, SCENE_WATER_TEMPLE_BOSS }, - { SPIRIT_TEMPLE_ENTRANCE, ENTR_DESERT_COLOSSUS_1, ENTR_SPIRIT_TEMPLE_BOSS_0, ENTR_SPIRIT_TEMPLE_1, ENTR_DESERT_COLOSSUS_8, SCENE_SPIRIT_TEMPLE, SCENE_SPIRIT_TEMPLE_BOSS }, - { SHADOW_TEMPLE_ENTRANCE, ENTR_GRAVEYARD_1, ENTR_SHADOW_TEMPLE_BOSS_0, ENTR_SHADOW_TEMPLE_1, ENTR_GRAVEYARD_8, SCENE_SHADOW_TEMPLE, SCENE_SHADOW_TEMPLE_BOSS }, + //entryway exit, boss, reverse, bluewarp, dungeon scene, boss scene + { ENTR_DEKU_TREE_0, ENTR_KOKIRI_FOREST_1, ENTR_DEKU_TREE_BOSS_0, ENTR_DEKU_TREE_1, ENTR_KOKIRI_FOREST_11, SCENE_DEKU_TREE, SCENE_DEKU_TREE_BOSS }, + { ENTR_DODONGOS_CAVERN_0, ENTR_DEATH_MOUNTAIN_TRAIL_3, ENTR_DODONGOS_CAVERN_BOSS_0, ENTR_DODONGOS_CAVERN_1, ENTR_DEATH_MOUNTAIN_TRAIL_5, SCENE_DODONGOS_CAVERN, SCENE_DODONGOS_CAVERN_BOSS }, + { ENTR_JABU_JABU_0, ENTR_ZORAS_FOUNTAIN_1, ENTR_JABU_JABU_BOSS_0, ENTR_JABU_JABU_1, ENTR_ZORAS_FOUNTAIN_0, SCENE_JABU_JABU, SCENE_JABU_JABU_BOSS }, + { ENTR_FOREST_TEMPLE_0, ENTR_SACRED_FOREST_MEADOW_1, ENTR_FOREST_TEMPLE_BOSS_0, ENTR_FOREST_TEMPLE_1, ENTR_SACRED_FOREST_MEADOW_3, SCENE_FOREST_TEMPLE, SCENE_FOREST_TEMPLE_BOSS }, + { ENTR_FIRE_TEMPLE_0, ENTR_DEATH_MOUNTAIN_CRATER_2, ENTR_FIRE_TEMPLE_BOSS_0, ENTR_FIRE_TEMPLE_1, ENTR_DEATH_MOUNTAIN_CRATER_5, SCENE_FIRE_TEMPLE, SCENE_FIRE_TEMPLE_BOSS }, + { ENTR_WATER_TEMPLE_0, ENTR_LAKE_HYLIA_2, ENTR_WATER_TEMPLE_BOSS_0, ENTR_WATER_TEMPLE_1, ENTR_LAKE_HYLIA_9, SCENE_WATER_TEMPLE, SCENE_WATER_TEMPLE_BOSS }, + { ENTR_SPIRIT_TEMPLE_0, ENTR_DESERT_COLOSSUS_1, ENTR_SPIRIT_TEMPLE_BOSS_0, ENTR_SPIRIT_TEMPLE_1, ENTR_DESERT_COLOSSUS_8, SCENE_SPIRIT_TEMPLE, SCENE_SPIRIT_TEMPLE_BOSS }, + { ENTR_SHADOW_TEMPLE_0, ENTR_GRAVEYARD_1, ENTR_SHADOW_TEMPLE_BOSS_0, ENTR_SHADOW_TEMPLE_1, ENTR_GRAVEYARD_8, SCENE_SHADOW_TEMPLE, SCENE_SHADOW_TEMPLE_BOSS }, }; -//These variables store the new entrance indices for dungeons so that -//savewarping and game overs respawn players at the proper entrance. -//By default, these will be their vanilla values. -static s16 newDekuTreeEntrance = DEKU_TREE_ENTRANCE; -static s16 newDodongosCavernEntrance = DODONGOS_CAVERN_ENTRANCE; -static s16 newJabuJabusBellyEntrance = JABU_JABUS_BELLY_ENTRANCE; -static s16 newForestTempleEntrance = FOREST_TEMPLE_ENTRANCE; -static s16 newFireTempleEntrance = FIRE_TEMPLE_ENTRANCE; -static s16 newWaterTempleEntrance = WATER_TEMPLE_ENTRANCE; -static s16 newSpiritTempleEntrance = SPIRIT_TEMPLE_ENTRANCE; -static s16 newShadowTempleEntrance = SHADOW_TEMPLE_ENTRANCE; -static s16 newBottomOfTheWellEntrance = BOTTOM_OF_THE_WELL_ENTRANCE; -static s16 newGerudoTrainingGroundsEntrance = GERUDO_TRAINING_GROUNDS_ENTRANCE; -static s16 newIceCavernEntrance = ICE_CAVERN_ENTRANCE; - static s8 hasCopiedEntranceTable = 0; static s8 hasModifiedEntranceTable = 0; void Entrance_SetEntranceDiscovered(u16 entranceIndex, u8 isReversedEntrance); u8 Entrance_EntranceIsNull(EntranceOverride* entranceOverride) { - return entranceOverride->index == 0 && entranceOverride->destination == 0 && entranceOverride->blueWarp == 0 - && entranceOverride->override == 0 && entranceOverride->overrideDestination == 0; + return entranceOverride->index == 0 && entranceOverride->destination == 0 && entranceOverride->override == 0 && + entranceOverride->overrideDestination == 0; } static void Entrance_SeparateOGCFairyFountainExit(void) { @@ -114,6 +94,28 @@ static void Entrance_SeparateAdultSpawnAndPrelude() { } } +// Fix Adult dungeon blue warps as Child by assigning the child values for the warp pads +static void Entrance_ReplaceChildTempleWarps() { + if (Randomizer_GetSettingValue(RSK_SHUFFLE_DUNGEON_ENTRANCES) != RO_DUNGEON_ENTRANCE_SHUFFLE_OFF || + Randomizer_GetSettingValue(RSK_SHUFFLE_BOSS_ENTRANCES) != RO_BOSS_ROOM_ENTRANCE_SHUFFLE_OFF) { + // Forest Temple + gEntranceTable[ENTR_SACRED_FOREST_MEADOW_3] = gEntranceTable[ENTR_SACRED_FOREST_MEADOW_2]; + gEntranceTable[ENTR_SACRED_FOREST_MEADOW_3_1] = gEntranceTable[ENTR_SACRED_FOREST_MEADOW_2_1]; + // Fire Temple + gEntranceTable[ENTR_DEATH_MOUNTAIN_CRATER_5] = gEntranceTable[ENTR_DEATH_MOUNTAIN_CRATER_4]; + gEntranceTable[ENTR_DEATH_MOUNTAIN_CRATER_5_1] = gEntranceTable[ENTR_DEATH_MOUNTAIN_CRATER_4_1]; + // Water Temple + gEntranceTable[ENTR_LAKE_HYLIA_9] = gEntranceTable[ENTR_LAKE_HYLIA_8]; + gEntranceTable[ENTR_LAKE_HYLIA_9_1] = gEntranceTable[ENTR_LAKE_HYLIA_8_1]; + // Shadow Temple + gEntranceTable[ENTR_GRAVEYARD_8] = gEntranceTable[ENTR_GRAVEYARD_7]; + gEntranceTable[ENTR_GRAVEYARD_8_1] = gEntranceTable[ENTR_GRAVEYARD_7_1]; + // Spirit Temple + gEntranceTable[ENTR_DESERT_COLOSSUS_8] = gEntranceTable[ENTR_DESERT_COLOSSUS_5]; + gEntranceTable[ENTR_DESERT_COLOSSUS_8_1] = gEntranceTable[ENTR_DESERT_COLOSSUS_5_1]; + } +} + void Entrance_CopyOriginalEntranceTable(void) { if (!hasCopiedEntranceTable) { memcpy(originalEntranceTable, gEntranceTable, sizeof(EntranceInfo) * ENTRANCE_TABLE_SIZE); @@ -132,9 +134,6 @@ void Entrance_Init(void) { EntranceOverride* entranceOverrides = Randomizer_GetEntranceOverrides(); s32 index; - size_t blueWarpRemapIdx = 0; - BlueWarpReplacement bluewarps[SHUFFLEABLE_BOSS_COUNT] = {0}; - Entrance_CopyOriginalEntranceTable(); // Skip Child Stealth if given by settings @@ -151,6 +150,7 @@ void Entrance_Init(void) { Entrance_SeparateOGCFairyFountainExit(); Entrance_SeparateAdultSpawnAndPrelude(); + Entrance_ReplaceChildTempleWarps(); // Initialize the entrance override table with each index leading to itself. An // index referring to itself means that the entrance is not currently shuffled. @@ -158,9 +158,9 @@ void Entrance_Init(void) { entranceOverrideTable[i] = i; } - // Initialize all boss rooms connected to their vanilla dungeon + // Initialize all boss room save/death warps with their vanilla dungeon entryway for (s16 i = 1; i < SHUFFLEABLE_BOSS_COUNT; i++) { - dungeonBossSceneOverrides[i] = i; + bossSceneSaveDeathWarps[i] = dungeons[i].entryway; } // Initialize the grotto exit and load lists @@ -174,9 +174,30 @@ void Entrance_Init(void) { } s16 originalIndex = entranceOverrides[i].index; - s16 blueWarpIndex = entranceOverrides[i].blueWarp; + s16 originalDestination = entranceOverrides[i].destination; s16 overrideIndex = entranceOverrides[i].override; + int16_t bossScene = -1; + int16_t saveWarpEntrance = originalDestination; // Default save warp to the original return entrance + + // Search for boss room overrides and look for the matching save/death warp value to use + // If the boss room is in a dungeon, use the dungeons entryway as the save warp + // Otherwise use the "exit" value for the entrance that lead to the boss room + for (int j = 0; j <= SHUFFLEABLE_BOSS_COUNT; j++) { + if (overrideIndex == dungeons[j].bossDoor) { + bossScene = dungeons[j].bossScene; + } + + if (index == dungeons[j].bossDoor) { + saveWarpEntrance = dungeons[j].entryway; + } + } + + // Found a boss scene and a valid save/death warp value + if (bossScene != -1 && saveWarpEntrance != -1) { + bossSceneSaveDeathWarps[bossScene - SCENE_DEKU_TREE_BOSS] = saveWarpEntrance; + } + //Overwrite grotto related indices if (originalIndex >= ENTRANCE_RANDO_GROTTO_EXIT_START) { Grotto_SetExitOverride(originalIndex, overrideIndex); @@ -191,36 +212,6 @@ void Entrance_Init(void) { // Overwrite the indices which we want to shuffle, leaving the rest as they are entranceOverrideTable[originalIndex] = overrideIndex; - if (blueWarpIndex != 0) { - // When boss shuffle is enabled, we need to know what dungeon the boss room is connected to for - // death/save warping, and for the blue warp - if (Randomizer_GetSettingValue(RSK_SHUFFLE_BOSS_ENTRANCES) != RO_BOSS_ROOM_ENTRANCE_SHUFFLE_OFF) { - s16 bossScene = -1; - s16 replacedDungeonScene = -1; - s16 replacedDungeonExit = -1; - // Search for the boss scene and replaced blue warp exits - for (s16 j = 0; j <= SHUFFLEABLE_BOSS_COUNT; j++) { - if (blueWarpIndex == dungeons[j].blueWarp) { - bossScene = dungeons[j].bossScene; - } - if (overrideIndex == dungeons[j].bossDoorReverse) { - replacedDungeonScene = dungeons[j].scene; - replacedDungeonExit = dungeons[j].exit; - } - } - - // assign the boss scene override - if (bossScene != -1 && replacedDungeonScene != -1 && replacedDungeonExit != -1) { - dungeonBossSceneOverrides[bossScene - SCENE_DEKU_TREE_BOSS] = replacedDungeonScene; - bluewarps[blueWarpRemapIdx].blueWarp = blueWarpIndex; - bluewarps[blueWarpRemapIdx].destination = replacedDungeonExit; - blueWarpRemapIdx++; - } - } else { - entranceOverrideTable[blueWarpIndex] = overrideIndex; - } - } - //Override both land and water entrances for Hyrule Field -> ZR Front and vice versa if (originalIndex == ENTR_ZORAS_RIVER_0) { //Hyrule Field -> ZR Front land entrance entranceOverrideTable[ENTR_ZORAS_RIVER_3] = overrideIndex; @@ -229,14 +220,6 @@ void Entrance_Init(void) { } } - // If we have remapped blue warps from boss shuffle, handle setting those and grabbing the override for - // the replaced dungeons exit in the event that dungeon shuffle is also turned on - for (size_t i = 0; i < ARRAY_COUNT(bluewarps); i++) { - if (bluewarps[i].blueWarp != 0 && bluewarps[i].destination != 0) { - entranceOverrideTable[bluewarps[i].blueWarp] = Entrance_GetOverride(bluewarps[i].destination); - } - } - // Stop playing background music during shuffled entrance transitions // so that we don't get duplicated or overlapping music tracks if (Randomizer_GetSettingValue(RSK_SHUFFLE_OVERWORLD_ENTRANCES)) { @@ -330,40 +313,39 @@ void Entrance_SetGameOverEntrance(void) { s16 scene = gPlayState->sceneNum; - // When in a boss room and boss shuffle is on, get the connected dungeon's original boss room entrance - // then run the normal game over overrides on it + // When in a boss room and boss shuffle is on, use the boss scene to find the death warp entrance if (Randomizer_GetSettingValue(RSK_SHUFFLE_BOSS_ENTRANCES) != RO_BOSS_ROOM_ENTRANCE_SHUFFLE_OFF && scene >= SCENE_DEKU_TREE_BOSS && scene <= SCENE_SHADOW_TEMPLE_BOSS) { - // Normalize boss scene range to 0 on lookup - scene = dungeonBossSceneOverrides[scene - SCENE_DEKU_TREE_BOSS]; - gSaveContext.entranceIndex = dungeons[scene].bossDoor; + // Normalize boss scene range to 0 on lookup and handle for grotto entrances + gSaveContext.entranceIndex = Grotto_OverrideSpecialEntrance(bossSceneSaveDeathWarps[scene - SCENE_DEKU_TREE_BOSS]); + return; } //Set the current entrance depending on which entrance the player last came through switch (gSaveContext.entranceIndex) { case ENTR_DEKU_TREE_BOSS_0 : //Deku Tree Boss Room - gSaveContext.entranceIndex = newDekuTreeEntrance; + gSaveContext.entranceIndex = ENTR_DEKU_TREE_0; return; case ENTR_DODONGOS_CAVERN_BOSS_0 : //Dodongos Cavern Boss Room - gSaveContext.entranceIndex = newDodongosCavernEntrance; + gSaveContext.entranceIndex = ENTR_DODONGOS_CAVERN_0; return; case ENTR_JABU_JABU_BOSS_0 : //Jabu Jabus Belly Boss Room - gSaveContext.entranceIndex = newJabuJabusBellyEntrance; + gSaveContext.entranceIndex = ENTR_JABU_JABU_0; return; case ENTR_FOREST_TEMPLE_BOSS_0 : //Forest Temple Boss Room - gSaveContext.entranceIndex = newForestTempleEntrance; + gSaveContext.entranceIndex = ENTR_FOREST_TEMPLE_0; return; case ENTR_FIRE_TEMPLE_BOSS_0 : //Fire Temple Boss Room - gSaveContext.entranceIndex = newFireTempleEntrance; + gSaveContext.entranceIndex = ENTR_FIRE_TEMPLE_0; return; case ENTR_WATER_TEMPLE_BOSS_0 : //Water Temple Boss Room - gSaveContext.entranceIndex = newWaterTempleEntrance; + gSaveContext.entranceIndex = ENTR_WATER_TEMPLE_0; return; case ENTR_SPIRIT_TEMPLE_BOSS_0 : //Spirit Temple Boss Room - gSaveContext.entranceIndex = newSpiritTempleEntrance; + gSaveContext.entranceIndex = ENTR_SPIRIT_TEMPLE_0; return; case ENTR_SHADOW_TEMPLE_BOSS_0 : //Shadow Temple Boss Room - gSaveContext.entranceIndex = newShadowTempleEntrance; + gSaveContext.entranceIndex = ENTR_SHADOW_TEMPLE_0; return; case ENTR_GANONDORF_BOSS_0 : //Ganondorf Boss Room gSaveContext.entranceIndex = ENTR_GANONS_TOWER_0; // Inside Ganon's Castle -> Ganon's Tower Climb @@ -376,46 +358,46 @@ void Entrance_SetSavewarpEntrance(void) { s16 scene = gSaveContext.savedSceneNum; - // When in a boss room and boss shuffle is on, use the boss scene override to remap to its - // connected dungeon and use that for the final entrance + // When in a boss room and boss shuffle is on, use the boss scene to find the savewarp entrance if (Randomizer_GetSettingValue(RSK_SHUFFLE_BOSS_ENTRANCES) != RO_BOSS_ROOM_ENTRANCE_SHUFFLE_OFF && scene >= SCENE_DEKU_TREE_BOSS && scene <= SCENE_SHADOW_TEMPLE_BOSS) { - // Normalize boss scene range to 0 on lookup - scene = dungeonBossSceneOverrides[scene - SCENE_DEKU_TREE_BOSS]; + // Normalize boss scene range to 0 on lookup and handle for grotto entrances + gSaveContext.entranceIndex = Grotto_OverrideSpecialEntrance(bossSceneSaveDeathWarps[scene - SCENE_DEKU_TREE_BOSS]); + return; } if (scene == SCENE_DEKU_TREE || scene == SCENE_DEKU_TREE_BOSS) { - gSaveContext.entranceIndex = newDekuTreeEntrance; + gSaveContext.entranceIndex = ENTR_DEKU_TREE_0; } else if (scene == SCENE_DODONGOS_CAVERN || scene == SCENE_DODONGOS_CAVERN_BOSS) { - gSaveContext.entranceIndex = newDodongosCavernEntrance; + gSaveContext.entranceIndex = ENTR_DODONGOS_CAVERN_0; } else if (scene == SCENE_JABU_JABU || scene == SCENE_JABU_JABU_BOSS) { - gSaveContext.entranceIndex = newJabuJabusBellyEntrance; + gSaveContext.entranceIndex = ENTR_JABU_JABU_0; } else if (scene == SCENE_FOREST_TEMPLE || scene == SCENE_FOREST_TEMPLE_BOSS) { //Forest Temple Boss Room - gSaveContext.entranceIndex = newForestTempleEntrance; + gSaveContext.entranceIndex = ENTR_FOREST_TEMPLE_0; } else if (scene == SCENE_FIRE_TEMPLE || scene == SCENE_FIRE_TEMPLE_BOSS) { //Fire Temple Boss Room - gSaveContext.entranceIndex = newFireTempleEntrance; + gSaveContext.entranceIndex = ENTR_FIRE_TEMPLE_0; } else if (scene == SCENE_WATER_TEMPLE || scene == SCENE_WATER_TEMPLE_BOSS) { //Water Temple Boss Room - gSaveContext.entranceIndex = newWaterTempleEntrance; + gSaveContext.entranceIndex = ENTR_WATER_TEMPLE_0; } else if (scene == SCENE_SPIRIT_TEMPLE || scene == SCENE_SPIRIT_TEMPLE_BOSS) { //Spirit Temple Boss Room - gSaveContext.entranceIndex = newSpiritTempleEntrance; + gSaveContext.entranceIndex = ENTR_SPIRIT_TEMPLE_0; } else if (scene == SCENE_SHADOW_TEMPLE || scene == SCENE_SHADOW_TEMPLE_BOSS) { //Shadow Temple Boss Room - gSaveContext.entranceIndex = newShadowTempleEntrance; + gSaveContext.entranceIndex = ENTR_SHADOW_TEMPLE_0; } else if (scene == SCENE_BOTTOM_OF_THE_WELL) { // BOTW - gSaveContext.entranceIndex = newBottomOfTheWellEntrance; + gSaveContext.entranceIndex = ENTR_BOTTOM_OF_THE_WELL_0; } else if (scene == SCENE_GERUDO_TRAINING_GROUND) { // GTG - gSaveContext.entranceIndex = newGerudoTrainingGroundsEntrance; + gSaveContext.entranceIndex = ENTR_GERUDO_TRAINING_GROUND_0; } else if (scene == SCENE_ICE_CAVERN) { // Ice cavern - gSaveContext.entranceIndex = newIceCavernEntrance; + gSaveContext.entranceIndex = ENTR_ICE_CAVERN_0; } else if (scene == SCENE_INSIDE_GANONS_CASTLE) { - gSaveContext.entranceIndex = GANONS_CASTLE_ENTRANCE; + gSaveContext.entranceIndex = ENTR_INSIDE_GANONS_CASTLE_0; } else if (scene == SCENE_GANONS_TOWER || scene == SCENE_INSIDE_GANONS_CASTLE_COLLAPSE || scene == SCENE_GANONS_TOWER_COLLAPSE_INTERIOR || scene == SCENE_GANON_BOSS || scene == SCENE_GANONS_TOWER_COLLAPSE_EXTERIOR) { gSaveContext.entranceIndex = ENTR_GANONS_TOWER_0; // Inside Ganon's Castle -> Ganon's Tower Climb } else if (scene == SCENE_THIEVES_HIDEOUT) { // Theives hideout gSaveContext.entranceIndex = ENTR_THIEVES_HIDEOUT_0; // Gerudo Fortress -> Thieve's Hideout spawn 0 } else if (scene == SCENE_LINKS_HOUSE) { - gSaveContext.entranceIndex = Entrance_OverrideNextIndex(LINK_HOUSE_SAVEWARP_ENTRANCE); + gSaveContext.entranceIndex = Entrance_OverrideNextIndex(ENTR_LINKS_HOUSE_0); } else if (LINK_IS_CHILD) { - gSaveContext.entranceIndex = Entrance_OverrideNextIndex(LINK_HOUSE_SAVEWARP_ENTRANCE); // Child Overworld Spawn + gSaveContext.entranceIndex = Entrance_OverrideNextIndex(ENTR_LINKS_HOUSE_0); // Child Overworld Spawn } else { gSaveContext.entranceIndex = Entrance_OverrideNextIndex(ENTR_HYRULE_FIELD_10); // Adult Overworld Spawn (Normally 0x5F4 (ENTR_TEMPLE_OF_TIME_7), but 0x282 (ENTR_HYRULE_FIELD_10) has been repurposed to differentiate from Prelude which also uses 0x5F4) } @@ -496,7 +478,7 @@ void Entrance_OverrideBlueWarp(void) { void Entrance_OverrideCutsceneEntrance(u16 cutsceneCmd) { switch (cutsceneCmd) { case 24: // Dropping a fish for Jabu Jabu - gPlayState->nextEntranceIndex = Entrance_OverrideNextIndex(newJabuJabusBellyEntrance); + gPlayState->nextEntranceIndex = Entrance_OverrideNextIndex(ENTR_JABU_JABU_0); gPlayState->transitionTrigger = TRANS_TRIGGER_START; gPlayState->transitionType = TRANS_TYPE_FADE_BLACK; // In case Jabu's mouth leads to a grotto return diff --git a/soh/soh/Enhancements/randomizer/randomizer_entrance.h b/soh/soh/Enhancements/randomizer/randomizer_entrance.h index 10a485a94..8d8d3a678 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_entrance.h +++ b/soh/soh/Enhancements/randomizer/randomizer_entrance.h @@ -8,20 +8,6 @@ #define ENTRANCE_TABLE_SIZE ENTR_MAX -#define DEKU_TREE_ENTRANCE ENTR_DEKU_TREE_0 -#define DODONGOS_CAVERN_ENTRANCE ENTR_DODONGOS_CAVERN_0 -#define JABU_JABUS_BELLY_ENTRANCE ENTR_JABU_JABU_0 -#define FOREST_TEMPLE_ENTRANCE ENTR_FOREST_TEMPLE_0 -#define FIRE_TEMPLE_ENTRANCE ENTR_FIRE_TEMPLE_0 -#define WATER_TEMPLE_ENTRANCE ENTR_WATER_TEMPLE_0 -#define SPIRIT_TEMPLE_ENTRANCE ENTR_SPIRIT_TEMPLE_0 -#define SHADOW_TEMPLE_ENTRANCE ENTR_SHADOW_TEMPLE_0 -#define BOTTOM_OF_THE_WELL_ENTRANCE ENTR_BOTTOM_OF_THE_WELL_0 -#define GERUDO_TRAINING_GROUNDS_ENTRANCE ENTR_GERUDO_TRAINING_GROUND_0 -#define ICE_CAVERN_ENTRANCE ENTR_ICE_CAVERN_0 -#define GANONS_CASTLE_ENTRANCE ENTR_INSIDE_GANONS_CASTLE_0 -#define LINK_HOUSE_SAVEWARP_ENTRANCE ENTR_LINKS_HOUSE_0 - #define ENTRANCE_RANDO_GROTTO_LOAD_START 0x0700 #define ENTRANCE_RANDO_GROTTO_EXIT_START 0x0800 #define MAX_ENTRANCE_RANDO_USED_INDEX 0x0820 @@ -66,7 +52,7 @@ typedef enum { #define ENTRANCE_RANDO_GROTTO_LOAD(index) ENTRANCE_RANDO_GROTTO_LOAD_START + index #define ENTRANCE_RANDO_GROTTO_EXIT(index) ENTRANCE_RANDO_GROTTO_EXIT_START + index -#define ENTRANCE_OVERRIDES_MAX_COUNT 259 // 11 one-way entrances + 124 two-way entrances (x2) +#define ENTRANCE_OVERRIDES_MAX_COUNT 267 // 19 one-way entrances + 124 two-way entrances (x2) #define SHUFFLEABLE_BOSS_COUNT 8 #define SAVEFILE_ENTRANCES_DISCOVERED_IDX_COUNT 66 // Max entrance rando index is 0x0820, (2080 / 32 == 65) + 1 @@ -79,9 +65,9 @@ typedef enum { (((startTransType) << ENTRANCE_INFO_START_TRANS_TYPE_SHIFT) & ENTRANCE_INFO_START_TRANS_TYPE_MASK)) typedef struct { + uint16_t type; int16_t index; int16_t destination; - int16_t blueWarp; int16_t override; int16_t overrideDestination; } EntranceOverride; diff --git a/soh/soh/Enhancements/randomizer/randomizer_entrance_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_entrance_tracker.cpp index 5d563b939..90b21e975 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_entrance_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_entrance_tracker.cpp @@ -105,6 +105,7 @@ const EntranceData entranceData[] = { { ENTR_KOKIRI_FOREST_1, ENTR_DEKU_TREE_0, SINGLE_SCENE_INFO(SCENE_DEKU_TREE), "Deku Tree", "KF", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_DUNGEON, ""}, { ENTR_DEKU_TREE_BOSS_0, ENTR_DEKU_TREE_1, SINGLE_SCENE_INFO(SCENE_DEKU_TREE), "Deku Tree Boss Door", "Gohma", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_DUNGEON, "", 1}, { ENTR_DEKU_TREE_1, ENTR_DEKU_TREE_BOSS_0, SINGLE_SCENE_INFO(SCENE_DEKU_TREE_BOSS), "Gohma", "Deku Tree Boss Door", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_DUNGEON, "", 1}, + { ENTR_KOKIRI_FOREST_11, -1, SINGLE_SCENE_INFO(SCENE_DEKU_TREE_BOSS), "Gohma", "Deku Tree Blue Warp", ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_ONE_WAY, "bw", 1}, // Lost Woods { ENTR_KOKIRI_FOREST_2, ENTR_LOST_WOODS_9, SINGLE_SCENE_INFO(SCENE_LOST_WOODS), "Lost Woods Bridge", "KF", ENTRANCE_GROUP_LOST_WOODS, ENTRANCE_GROUP_KOKIRI_FOREST, ENTRANCE_TYPE_OVERWORLD, "lw"}, @@ -132,6 +133,7 @@ const EntranceData entranceData[] = { { ENTR_SACRED_FOREST_MEADOW_1, ENTR_FOREST_TEMPLE_0, SINGLE_SCENE_INFO(SCENE_FOREST_TEMPLE), "Forest Temple", "SFM", ENTRANCE_GROUP_SFM, ENTRANCE_GROUP_SFM, ENTRANCE_TYPE_DUNGEON}, { ENTR_FOREST_TEMPLE_BOSS_0, ENTR_FOREST_TEMPLE_1, SINGLE_SCENE_INFO(SCENE_FOREST_TEMPLE), "Forest Temple Boss Door", "Phantom Ganon", ENTRANCE_GROUP_SFM, ENTRANCE_GROUP_SFM, ENTRANCE_TYPE_DUNGEON, "", 1}, { ENTR_FOREST_TEMPLE_1, ENTR_FOREST_TEMPLE_BOSS_0, SINGLE_SCENE_INFO(SCENE_FOREST_TEMPLE_BOSS), "Phantom Ganon", "Forest Temple Boss Door", ENTRANCE_GROUP_SFM, ENTRANCE_GROUP_SFM, ENTRANCE_TYPE_DUNGEON, "", 1}, + { ENTR_SACRED_FOREST_MEADOW_3, -1, SINGLE_SCENE_INFO(SCENE_FOREST_TEMPLE_BOSS), "Phantom Ganon", "Forest Temple Blue Warp", ENTRANCE_GROUP_SFM, ENTRANCE_GROUP_SFM, ENTRANCE_TYPE_ONE_WAY, "bw", 1}, // Kakariko Village { ENTR_HYRULE_FIELD_1, ENTR_KAKARIKO_VILLAGE_0, SINGLE_SCENE_INFO(SCENE_KAKARIKO_VILLAGE), "Kakariko", "Hyrule Field", ENTRANCE_GROUP_KAKARIKO, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_OVERWORLD, "hf"}, @@ -180,6 +182,7 @@ const EntranceData entranceData[] = { { ENTR_GRAVEYARD_1, ENTR_SHADOW_TEMPLE_0, SINGLE_SCENE_INFO(SCENE_SHADOW_TEMPLE), "Shadow Temple", "Graveyard", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_DUNGEON}, { ENTR_SHADOW_TEMPLE_BOSS_0, ENTR_SHADOW_TEMPLE_1, SINGLE_SCENE_INFO(SCENE_SHADOW_TEMPLE), "Shadow Temple Boss Door", "Bongo-Bongo", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_DUNGEON, "", 1}, { ENTR_SHADOW_TEMPLE_1, ENTR_SHADOW_TEMPLE_BOSS_0, SINGLE_SCENE_INFO(SCENE_SHADOW_TEMPLE_BOSS), "Bongo-Bongo", "Shadow Temple Boss Door", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_DUNGEON, "", 1}, + { ENTR_GRAVEYARD_8, -1, SINGLE_SCENE_INFO(SCENE_SHADOW_TEMPLE_BOSS), "Bongo-Bongo", "Shadow Temple Blue Warp", ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_GROUP_GRAVEYARD, ENTRANCE_TYPE_ONE_WAY, "bw", 1}, // Death Mountain Trail { ENTR_GORON_CITY_0, ENTR_DEATH_MOUNTAIN_TRAIL_1, SINGLE_SCENE_INFO(SCENE_DEATH_MOUNTAIN_TRAIL), "DMT", "Goron City", ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROUP_GORON_CITY, ENTRANCE_TYPE_OVERWORLD, "gc"}, @@ -195,6 +198,7 @@ const EntranceData entranceData[] = { { ENTR_DEATH_MOUNTAIN_TRAIL_3, ENTR_DODONGOS_CAVERN_0, SINGLE_SCENE_INFO(SCENE_DODONGOS_CAVERN), "Dodongo's Cavern", "DMT", ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_TYPE_DUNGEON, "dc"}, { ENTR_DODONGOS_CAVERN_BOSS_0, ENTR_DODONGOS_CAVERN_1, SINGLE_SCENE_INFO(SCENE_DODONGOS_CAVERN), "Dodongo's Cavern Boss Door", "King Dodongo", ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_TYPE_DUNGEON, "dc", 1}, { ENTR_DODONGOS_CAVERN_1, ENTR_DODONGOS_CAVERN_BOSS_0, SINGLE_SCENE_INFO(SCENE_DODONGOS_CAVERN_BOSS), "King Dodongo", "Dodongo's Cavern Boss Door", ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_TYPE_DUNGEON, "dc", 1}, + { ENTR_DEATH_MOUNTAIN_TRAIL_5, -1, SINGLE_SCENE_INFO(SCENE_DODONGOS_CAVERN_BOSS), "King Dodongo", "Dodongo's Cavern Blue Warp", ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_TYPE_ONE_WAY, "dc,bw", 1}, // Death Mountain Crater { ENTR_GORON_CITY_1, ENTR_DEATH_MOUNTAIN_CRATER_1, SINGLE_SCENE_INFO(SCENE_DEATH_MOUNTAIN_CRATER), "DMC", "Goron City", ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_GROUP_GORON_CITY, ENTRANCE_TYPE_OVERWORLD, "gc"}, @@ -209,6 +213,7 @@ const EntranceData entranceData[] = { { ENTR_DEATH_MOUNTAIN_CRATER_2, ENTR_FIRE_TEMPLE_0, SINGLE_SCENE_INFO(SCENE_FIRE_TEMPLE), "Fire Temple", "DMC", ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_TYPE_DUNGEON}, { ENTR_FIRE_TEMPLE_BOSS_0, ENTR_FIRE_TEMPLE_1, SINGLE_SCENE_INFO(SCENE_FIRE_TEMPLE), "Fire Temple Boss Door", "Volvagia", ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_TYPE_DUNGEON, "", 1}, { ENTR_FIRE_TEMPLE_1, ENTR_FIRE_TEMPLE_BOSS_0, SINGLE_SCENE_INFO(SCENE_FIRE_TEMPLE_BOSS), "Volvagia", "Fire Temple Boss Door", ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_TYPE_DUNGEON, "", 1}, + { ENTR_DEATH_MOUNTAIN_CRATER_5, -1, SINGLE_SCENE_INFO(SCENE_FIRE_TEMPLE_BOSS), "Volvagia", "Fire Temple Blue Warp", ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_GROUP_DEATH_MOUNTAIN_CRATER, ENTRANCE_TYPE_ONE_WAY, "bw", 1}, // Goron City { ENTR_DEATH_MOUNTAIN_TRAIL_1, ENTR_GORON_CITY_0, SINGLE_SCENE_INFO(SCENE_GORON_CITY), "Goron City", "DMT", ENTRANCE_GROUP_GORON_CITY, ENTRANCE_GROUP_DEATH_MOUNTAIN_TRAIL, ENTRANCE_TYPE_OVERWORLD, "gc"}, @@ -248,6 +253,7 @@ const EntranceData entranceData[] = { { ENTR_ZORAS_FOUNTAIN_1, ENTR_JABU_JABU_0, SINGLE_SCENE_INFO(SCENE_JABU_JABU), "Jabu Jabu's Belly", "ZF", ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_TYPE_DUNGEON}, { ENTR_JABU_JABU_BOSS_0, ENTR_JABU_JABU_1, SINGLE_SCENE_INFO(SCENE_JABU_JABU), "Jabu Jabu's Belly Boss Door", "Barinade", ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_TYPE_DUNGEON, "", 1}, { ENTR_JABU_JABU_1, ENTR_JABU_JABU_BOSS_0, SINGLE_SCENE_INFO(SCENE_JABU_JABU_BOSS), "Barinade", "Jabu Jabu's Belly Boss Door", ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_TYPE_DUNGEON, "", 1}, + { ENTR_ZORAS_FOUNTAIN_0, -1, SINGLE_SCENE_INFO(SCENE_JABU_JABU_BOSS), "Barinade", "Jabu Jabu's Belly Blue Warp", ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_TYPE_ONE_WAY, "bw", 1}, { ENTR_ZORAS_FOUNTAIN_3, ENTR_ICE_CAVERN_0, SINGLE_SCENE_INFO(SCENE_ICE_CAVERN), "Ice Cavern", "ZF", ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_GROUP_ZORAS_FOUNTAIN, ENTRANCE_TYPE_DUNGEON}, // Hyrule Field @@ -299,6 +305,7 @@ const EntranceData entranceData[] = { { ENTR_LAKE_HYLIA_2, ENTR_WATER_TEMPLE_0, SINGLE_SCENE_INFO(SCENE_WATER_TEMPLE), "Water Temple", "Lake Hylia", ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_TYPE_DUNGEON, "lh"}, { ENTR_WATER_TEMPLE_BOSS_0, ENTR_WATER_TEMPLE_1, SINGLE_SCENE_INFO(SCENE_WATER_TEMPLE), "Water Temple Boss Door", "Morpha", ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_TYPE_DUNGEON, "lh", 1}, { ENTR_WATER_TEMPLE_1, ENTR_WATER_TEMPLE_BOSS_0, SINGLE_SCENE_INFO(SCENE_WATER_TEMPLE_BOSS), "Morpha", "Water Temple Boss Door", ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_TYPE_DUNGEON, "lh", 1}, + { ENTR_LAKE_HYLIA_9, -1, SINGLE_SCENE_INFO(SCENE_WATER_TEMPLE_BOSS), "Morpha", "Water Temple Blue Warp", ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_GROUP_LAKE_HYLIA, ENTRANCE_TYPE_ONE_WAY, "lh,bw", 1}, // Gerudo Area { ENTR_HYRULE_FIELD_5, ENTR_GERUDO_VALLEY_0, SINGLE_SCENE_INFO(SCENE_GERUDO_VALLEY), "GV", "Hyrule Field", ENTRANCE_GROUP_GERUDO_VALLEY, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_OVERWORLD, "hf"}, @@ -329,6 +336,7 @@ const EntranceData entranceData[] = { { ENTR_DESERT_COLOSSUS_1, ENTR_SPIRIT_TEMPLE_0, SINGLE_SCENE_INFO(SCENE_SPIRIT_TEMPLE), "Spirit Temple", "Desert Colossus", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_TYPE_DUNGEON, "dc"}, { ENTR_SPIRIT_TEMPLE_BOSS_0, ENTR_SPIRIT_TEMPLE_1, SINGLE_SCENE_INFO(SCENE_SPIRIT_TEMPLE), "Spirit Temple Boss Door", "Twinrova", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_TYPE_DUNGEON, "", 1}, { ENTR_SPIRIT_TEMPLE_1, ENTR_SPIRIT_TEMPLE_BOSS_0, SINGLE_SCENE_INFO(SCENE_SPIRIT_TEMPLE_BOSS), "Twinrova", "Spirit Temple Boss Door", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_TYPE_DUNGEON, "", 1}, + { ENTR_DESERT_COLOSSUS_8, -1, SINGLE_SCENE_INFO(SCENE_SPIRIT_TEMPLE_BOSS), "Twinrova", "Spirit Temple Blue Warp", ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_GROUP_HAUNTED_WASTELAND, ENTRANCE_TYPE_ONE_WAY, "bw", 1}, // Market { ENTR_HYRULE_FIELD_7, ENTR_MARKET_ENTRANCE_DAY_1, {SCENE_NO_SPAWN(SCENE_MARKET_ENTRANCE_DAY), SCENE_NO_SPAWN(SCENE_MARKET_ENTRANCE_NIGHT), SCENE_NO_SPAWN(SCENE_MARKET_ENTRANCE_RUINS)}, "Market Entrance", "Hyrule Field", ENTRANCE_GROUP_MARKET, ENTRANCE_GROUP_HYRULE_FIELD, ENTRANCE_TYPE_OVERWORLD, "hf"}, @@ -800,6 +808,11 @@ void EntranceTrackerWindow::DrawElement() { continue; } + // RANDOTODO: Only show blue warps if bluewarp shuffle is on + if (original->metaTag.ends_with("bw") || override->metaTag.ends_with("bw")) { + continue; + } + bool isDiscovered = IsEntranceDiscovered(entrance.index); bool showOriginal = (!destToggle ? CVarGetInteger("gEntranceTrackerShowTo", 0) : CVarGetInteger("gEntranceTrackerShowFrom", 0)) || isDiscovered; diff --git a/soh/soh/SaveManager.cpp b/soh/soh/SaveManager.cpp index daae87146..0fc3394c9 100644 --- a/soh/soh/SaveManager.cpp +++ b/soh/soh/SaveManager.cpp @@ -210,9 +210,9 @@ void SaveManager::LoadRandomizerVersion2() { auto entranceCtx = randoContext->GetEntranceShuffler(); SaveManager::Instance->LoadArray("entrances", ARRAY_COUNT(entranceCtx->entranceOverrides), [&](size_t i) { SaveManager::Instance->LoadStruct("", [&]() { + SaveManager::Instance->LoadData("type", entranceCtx->entranceOverrides[i].type); SaveManager::Instance->LoadData("index", entranceCtx->entranceOverrides[i].index); SaveManager::Instance->LoadData("destination", entranceCtx->entranceOverrides[i].destination); - SaveManager::Instance->LoadData("blueWarp", entranceCtx->entranceOverrides[i].blueWarp); SaveManager::Instance->LoadData("override", entranceCtx->entranceOverrides[i].override); SaveManager::Instance->LoadData("overrideDestination", entranceCtx->entranceOverrides[i].overrideDestination); }); @@ -455,9 +455,9 @@ void SaveManager::SaveRandomizer(SaveContext* saveContext, int sectionID, bool f auto entranceCtx = randoContext->GetEntranceShuffler(); SaveManager::Instance->SaveArray("entrances", ARRAY_COUNT(entranceCtx->entranceOverrides), [&](size_t i) { SaveManager::Instance->SaveStruct("", [&]() { + SaveManager::Instance->SaveData("type", entranceCtx->entranceOverrides[i].type); SaveManager::Instance->SaveData("index", entranceCtx->entranceOverrides[i].index); SaveManager::Instance->SaveData("destination", entranceCtx->entranceOverrides[i].destination); - SaveManager::Instance->SaveData("blueWarp", entranceCtx->entranceOverrides[i].blueWarp); SaveManager::Instance->SaveData("override", entranceCtx->entranceOverrides[i].override); SaveManager::Instance->SaveData("overrideDestination", entranceCtx->entranceOverrides[i].overrideDestination); }); diff --git a/soh/src/code/z_room.c b/soh/src/code/z_room.c index 772978f09..e92925c6a 100644 --- a/soh/src/code/z_room.c +++ b/soh/src/code/z_room.c @@ -575,6 +575,13 @@ u32 func_80096FE8(PlayState* play, RoomContext* roomCtx) { frontRoom = gSaveContext.respawnFlag > 0 ? ((void)0, gSaveContext.respawn[gSaveContext.respawnFlag - 1].roomIndex) : play->setupEntranceList[play->curSpawn].room; + + // In ER, override roomNum to load based on scene and spawn during scene init + if (IS_RANDO && gSaveContext.respawnFlag <= 0 && + Randomizer_GetSettingValue(RSK_SHUFFLE_ENTRANCES)) { + frontRoom = Entrance_OverrideSpawnSceneRoom(play->sceneNum, play->curSpawn, frontRoom); + } + func_8009728C(play, roomCtx, frontRoom); return maxRoomSize; @@ -583,12 +590,6 @@ u32 func_80096FE8(PlayState* play, RoomContext* roomCtx) { s32 func_8009728C(PlayState* play, RoomContext* roomCtx, s32 roomNum) { size_t size; - // In ER, override roomNum to load based on scene and spawn - if (IS_RANDO && gSaveContext.respawnFlag <= 0 && - Randomizer_GetSettingValue(RSK_SHUFFLE_ENTRANCES)) { - roomNum = Entrance_OverrideSpawnSceneRoom(play->sceneNum, play->curSpawn, roomNum); - } - return OTRfunc_8009728C(play, roomCtx, roomNum); if (roomCtx->status == 0) { diff --git a/soh/src/overlays/actors/ovl_player_actor/z_player.c b/soh/src/overlays/actors/ovl_player_actor/z_player.c index 7489fa8fa..b614166fa 100644 --- a/soh/src/overlays/actors/ovl_player_actor/z_player.c +++ b/soh/src/overlays/actors/ovl_player_actor/z_player.c @@ -4539,7 +4539,9 @@ s32 Player_HandleExitsAndVoids(PlayState* play, Player* this, CollisionPoly* pol Scene_SetTransitionForNextEntrance(play); } else { - if (SurfaceType_GetSlope(&play->colCtx, poly, bgId) == 2) { + // In Entrance rando, if our respawnFlag is set for a grotto return, we don't want the void out to happen + if (SurfaceType_GetSlope(&play->colCtx, poly, bgId) == 2 && + (!IS_RANDO || (Randomizer_GetSettingValue(RSK_SHUFFLE_ENTRANCES) && gSaveContext.respawnFlag != 2))) { gSaveContext.respawn[RESPAWN_MODE_DOWN].entranceIndex = play->nextEntranceIndex; Play_TriggerVoidOut(play); gSaveContext.respawnFlag = -2;