diff options
| author | Garrett Cox <garrettjcox@gmail.com> | 2026-02-22 07:12:25 -0600 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-02-22 07:12:25 -0600 |
| commit | f8bf9bb3b66f467a8a433f8b204a1c575880c6d7 (patch) | |
| tree | 13522f00a4d2ca248786fd2524a467289d655c94 | |
| parent | 4a7af30687a57e26affe3c36546128d8f61351e0 (diff) | |
Make scroll to scene & only show current scene work for grottos (#1561)
| -rw-r--r-- | mm/2s2h/Rando/ActorBehavior/DmStk.cpp | 4 | ||||
| -rw-r--r-- | mm/2s2h/Rando/ActorBehavior/EnGs.cpp | 8 | ||||
| -rw-r--r-- | mm/2s2h/Rando/ActorBehavior/EnKgy.cpp | 8 | ||||
| -rw-r--r-- | mm/2s2h/Rando/ActorBehavior/EnTalk.cpp | 5 | ||||
| -rw-r--r-- | mm/2s2h/Rando/ActorBehavior/EnTimeTag.cpp | 4 | ||||
| -rw-r--r-- | mm/2s2h/Rando/ActorBehavior/EnZow.cpp | 4 | ||||
| -rw-r--r-- | mm/2s2h/Rando/CheckTracker/CheckTracker.cpp | 87 | ||||
| -rw-r--r-- | mm/2s2h/Rando/Logic/Logic.cpp | 75 | ||||
| -rw-r--r-- | mm/2s2h/Rando/Logic/Regions/East.cpp | 18 | ||||
| -rw-r--r-- | mm/2s2h/Rando/Logic/Regions/North.cpp | 28 | ||||
| -rw-r--r-- | mm/2s2h/Rando/Logic/Regions/South.cpp | 26 | ||||
| -rw-r--r-- | mm/2s2h/Rando/Logic/Regions/TerminaField.cpp | 50 | ||||
| -rw-r--r-- | mm/2s2h/Rando/Logic/Regions/West.cpp | 24 | ||||
| -rw-r--r-- | mm/2s2h/Rando/MiscBehavior/SariasSongHint.cpp | 2 | ||||
| -rw-r--r-- | mm/2s2h/Rando/StaticData/Checks.cpp | 68 | ||||
| -rw-r--r-- | mm/2s2h/Rando/StaticData/StaticData.h | 1 |
16 files changed, 268 insertions, 144 deletions
diff --git a/mm/2s2h/Rando/ActorBehavior/DmStk.cpp b/mm/2s2h/Rando/ActorBehavior/DmStk.cpp index 18797e4a0..f9ec1b38e 100644 --- a/mm/2s2h/Rando/ActorBehavior/DmStk.cpp +++ b/mm/2s2h/Rando/ActorBehavior/DmStk.cpp @@ -28,12 +28,12 @@ void ApplyOathHint(u16* textId, bool* loadFromMessageTable) { msg = "You think you can defeat me? The Giants are trapped and powerless to stop me. Even if they were free, " "they couldn't save you."; } else { - msg = "I can hear the Giants Melody coming from " + msg = "I can hear the Giants Melody echoing " "%y{{location}}%w. But it's too late! They can't help you now!"; } RandoCheckId randoCheckId = Rando::FindItemPlacement(RI_SONG_OATH); - CustomMessage::Replace(&msg, "{{location}}", Ship_GetSceneName(Rando::StaticData::Checks[randoCheckId].sceneId)); + CustomMessage::Replace(&msg, "{{location}}", Rando::StaticData::GetLocationNameForHint(randoCheckId, false)); CustomMessage::Entry entry = { .nextMessageID = (u16)0xFFFF, diff --git a/mm/2s2h/Rando/ActorBehavior/EnGs.cpp b/mm/2s2h/Rando/ActorBehavior/EnGs.cpp index a3db35aac..3fc96d787 100644 --- a/mm/2s2h/Rando/ActorBehavior/EnGs.cpp +++ b/mm/2s2h/Rando/ActorBehavior/EnGs.cpp @@ -92,12 +92,12 @@ void Rando::ActorBehavior::InitEnGsBehavior() { entry.autoFormat = false; auto& saveCheck = RANDO_SAVE_CHECKS[randoCheckId]; - entry.msg = "They say %g{{item}}%w is hidden at %y{{location}}%w."; + entry.msg = "They say %g{{item}}%w is hidden %y{{location}}%w."; CustomMessage::Replace(&entry.msg, "{{item}}", Rando::StaticData::GetItemName(saveCheck.randoItemId, true, randoCheckId)); CustomMessage::Replace(&entry.msg, "{{location}}", - Ship_GetSceneName(Rando::StaticData::Checks[randoCheckId].sceneId)); + Rando::StaticData::GetLocationNameForHint(randoCheckId, false)); // Replace colors before line break calculation CustomMessage::ReplaceColorChars(&entry.msg); @@ -141,12 +141,12 @@ void Rando::ActorBehavior::InitEnGsBehavior() { } else { RandoSaveCheck saveCheck = RANDO_SAVE_CHECKS[randoCheckId]; - entry.msg = "Wise choice... They say %g{{item}}%w is hidden at %y{{location}}%w."; + entry.msg = "Wise choice... They say %g{{item}}%w is hidden %y{{location}}%w."; CustomMessage::Replace(&entry.msg, "{{item}}", Rando::StaticData::GetItemName(saveCheck.randoItemId, true, randoCheckId)); CustomMessage::Replace(&entry.msg, "{{location}}", - Ship_GetSceneName(Rando::StaticData::Checks[randoCheckId].sceneId)); + Rando::StaticData::GetLocationNameForHint(randoCheckId, true)); gSaveContext.rupeeAccumulator -= cost; cost *= 2; diff --git a/mm/2s2h/Rando/ActorBehavior/EnKgy.cpp b/mm/2s2h/Rando/ActorBehavior/EnKgy.cpp index 341c67d48..f62e660c8 100644 --- a/mm/2s2h/Rando/ActorBehavior/EnKgy.cpp +++ b/mm/2s2h/Rando/ActorBehavior/EnKgy.cpp @@ -114,10 +114,10 @@ void Rando::ActorBehavior::InitEnKgyBehavior() { // "Gold dust is the prize for winning the Goron race in spring?" COND_ID_HOOK(OnOpenText, 0xc49, IS_RANDO, [](u16* textId, bool* loadFromMessageTable) { auto entry = CustomMessage::LoadVanillaMessageTableEntry(*textId); - entry.msg = "Huh? You say that gold dust can be found at %r{location}%w?\x19"; + entry.msg = "Huh? You say that gold dust can be found %r{location}%w?\x19"; RandoCheckId randoCheckId = Rando::FindItemPlacement(RI_BOTTLE_GOLD_DUST); CustomMessage::Replace(&entry.msg, "{location}", - Ship_GetSceneName(Rando::StaticData::Checks[randoCheckId].sceneId)); + Rando::StaticData::GetLocationNameForHint(randoCheckId, false)); CustomMessage::LoadCustomMessageIntoFont(entry); *loadFromMessageTable = false; @@ -126,14 +126,14 @@ void Rando::ActorBehavior::InitEnKgyBehavior() { // "Gold dust happens to be first prize at the racetrack" COND_ID_HOOK(OnOpenText, 0xc4b, IS_RANDO, [](u16* textId, bool* loadFromMessageTable) { auto entry = CustomMessage::LoadVanillaMessageTableEntry(*textId); - entry.msg = "Gold dust can be found at %p{location}%w.\x10"; + entry.msg = "Gold dust can be found %p{location}%w.\x10"; entry.msg += "Bring me that, and my %r{itemName}%w is all yours.\xE0"; RandoCheckId randoCheckId = Rando::FindItemPlacement(RI_BOTTLE_GOLD_DUST); CustomMessage::Replace( &entry.msg, "{itemName}", Rando::StaticData::Items[RANDO_SAVE_CHECKS[RC_MOUNTAIN_VILLAGE_SMITHY_GILDED_SWORD].randoItemId].name); CustomMessage::Replace(&entry.msg, "{location}", - Ship_GetSceneName(Rando::StaticData::Checks[randoCheckId].sceneId)); + Rando::StaticData::GetLocationNameForHint(randoCheckId, false)); CustomMessage::LoadCustomMessageIntoFont(entry); *loadFromMessageTable = false; diff --git a/mm/2s2h/Rando/ActorBehavior/EnTalk.cpp b/mm/2s2h/Rando/ActorBehavior/EnTalk.cpp index d95069ab6..49a721922 100644 --- a/mm/2s2h/Rando/ActorBehavior/EnTalk.cpp +++ b/mm/2s2h/Rando/ActorBehavior/EnTalk.cpp @@ -25,7 +25,7 @@ void ApplyRemainsHint(u16* textId, bool* loadFromMessageTable) { "for their remains."; } else { msg = " %g{{boss}}%w:\n" - "Last seen in near %y{{location}}%w."; + "Last seen %y{{location}}%w."; switch (remainsHintIndex) { case 1: @@ -48,8 +48,7 @@ void ApplyRemainsHint(u16* textId, bool* loadFromMessageTable) { icon = Rando::StaticData::GetIconForZMessage(randoItemId); RandoCheckId randoCheckId = Rando::FindItemPlacement(randoItemId); - CustomMessage::Replace(&msg, "{{location}}", - Ship_GetSceneName(Rando::StaticData::Checks[randoCheckId].sceneId)); + CustomMessage::Replace(&msg, "{{location}}", Rando::StaticData::GetLocationNameForHint(randoCheckId, false)); } CustomMessage::Entry entry = { diff --git a/mm/2s2h/Rando/ActorBehavior/EnTimeTag.cpp b/mm/2s2h/Rando/ActorBehavior/EnTimeTag.cpp index 273e6fdb2..dbf5ae238 100644 --- a/mm/2s2h/Rando/ActorBehavior/EnTimeTag.cpp +++ b/mm/2s2h/Rando/ActorBehavior/EnTimeTag.cpp @@ -21,11 +21,11 @@ void Rando::ActorBehavior::InitEnTimeTagBehavior() { if (RANDO_SAVE_OPTIONS[RO_HINTS_SONG_OF_SOARING]) { auto entry = CustomMessage::LoadVanillaMessageTableEntry(*textId); - entry.msg = "A note is carved into the stone...\x10What you seek lies in %y{{location}}%w."; + entry.msg = "A note is carved into the stone...\x10What you seek lies %y{{location}}%w."; RandoCheckId randoCheckId = Rando::FindItemPlacement(RI_SONG_SOARING); CustomMessage::Replace(&entry.msg, "{{location}}", - Ship_GetSceneName(Rando::StaticData::Checks[randoCheckId].sceneId)); + Rando::StaticData::GetLocationNameForHint(randoCheckId, false)); CustomMessage::LoadCustomMessageIntoFont(entry); *loadFromMessageTable = false; } diff --git a/mm/2s2h/Rando/ActorBehavior/EnZow.cpp b/mm/2s2h/Rando/ActorBehavior/EnZow.cpp index 4fa28e7bd..8fbdedc68 100644 --- a/mm/2s2h/Rando/ActorBehavior/EnZow.cpp +++ b/mm/2s2h/Rando/ActorBehavior/EnZow.cpp @@ -8,11 +8,11 @@ extern "C" { } void ApplyHookshotHint(u16* textId, bool* loadFromMessageTable) { - std::string msg = "I overheard those Pirates talk about treasure in " + std::string msg = "I overheard those Pirates talk about treasure " "%y{{location}}%w that can help you reach the unreachable!"; RandoCheckId randoCheckId = Rando::FindItemPlacement(RI_HOOKSHOT); - CustomMessage::Replace(&msg, "{{location}}", Ship_GetSceneName(Rando::StaticData::Checks[randoCheckId].sceneId)); + CustomMessage::Replace(&msg, "{{location}}", Rando::StaticData::GetLocationNameForHint(randoCheckId, false)); CustomMessage::Entry entry = { .nextMessageID = (u16)0xFFFF, diff --git a/mm/2s2h/Rando/CheckTracker/CheckTracker.cpp b/mm/2s2h/Rando/CheckTracker/CheckTracker.cpp index 6a35728a6..787e2692d 100644 --- a/mm/2s2h/Rando/CheckTracker/CheckTracker.cpp +++ b/mm/2s2h/Rando/CheckTracker/CheckTracker.cpp @@ -111,6 +111,7 @@ ImVec4 trackerBG = ImVec4{ 0, 0, 0, 0.5f }; std::map<SceneId, std::set<RandoCheckId>> sceneChecks; std::vector<SceneId> sortedSceneIds; std::unordered_map<RandoCheckId, std::string> accessLogicFuncs; +std::unordered_map<RandoRegionId, SceneId> regionParentSceneMap; std::set<RandoCheckType> checkTypeFiltersAvailable; std::set<RandoCheckType> checkTypeFilter; @@ -170,36 +171,36 @@ void DrawCheckTypeIcon(RandoCheckId randoCheckId) { checkType == RCTYPE_FREESTANDING ? ImVec4(0.78f, 1, 0.39f, 1) : ImVec4(1, 1, 1, 1), tintColor); } +static std::set<SceneId> scenesToCheckParent = { + SCENE_KAKUSIANA, SCENE_TAKARAYA, SCENE_BOWLING, SCENE_SONCHONOIE, SCENE_SYATEKI_MIZU, SCENE_YADOYA, + SCENE_MILK_BAR, SCENE_AYASHIISHOP, SCENE_YOUSEI_IZUMI, SCENE_DOUJOU, SCENE_8ITEMSHOP, SCENE_BOMYA, + SCENE_POSTHOUSE, SCENE_TAKARAKUJI, SCENE_SYATEKI_MORI, SCENE_MAP_SHOP, SCENE_WITCH_SHOP, SCENE_F01C, + SCENE_F01_B, SCENE_OMOYA, SCENE_GORONSHOP, SCENE_KAJIYA, SCENE_FISHERMAN, SCENE_LABO, + SCENE_BANDROOM, SCENE_TOUGITES, SCENE_MUSICHOUSE, +}; + +SceneId GetScrollTargetScene(s32 rawSceneId) { + SceneId sceneId = (SceneId)Play_GetOriginalSceneId(rawSceneId); + if (scenesToCheckParent.contains(sceneId)) { + RandoRegionId regionId = Rando::Logic::GetRegionIdFromEntrance(gSaveContext.save.entrance); + if (regionParentSceneMap.contains(regionId)) { + return regionParentSceneMap[regionId]; + } + } + return sceneId; +} + void initializeSceneChecks() { sceneChecks.clear(); + regionParentSceneMap.clear(); checkTypeFiltersAvailable.clear(); checkTypeFiltersAvailable.insert(RCTYPE_UNKNOWN); // Always include the unknown type - for (auto& [_, randoStaticCheck] : Rando::StaticData::Checks) { - RandoSaveCheck& randoSaveCheck = RANDO_SAVE_CHECKS[randoStaticCheck.randoCheckId]; - if (!randoSaveCheck.shuffled) { - continue; - } - - checkTypeFiltersAvailable.insert(randoStaticCheck.randoCheckType); - - // Skip Grotto Checks, they will be handled below - if (randoStaticCheck.sceneId == SCENE_KAKUSIANA) { - continue; - } - - // Skip Enemy Drops, they will be handled below - if (randoStaticCheck.randoCheckType == RCTYPE_ENEMY_DROP) { - continue; - } - - SceneId sceneId = (SceneId)Play_GetOriginalSceneId(randoStaticCheck.sceneId); - sceneChecks[sceneId].insert(randoStaticCheck.randoCheckId); - } - for (auto& [regionId, staticRegion] : Rando::Logic::Regions) { - // Handle grottos separately, their checks are assigned to the connected region - if (staticRegion.sceneId == SCENE_KAKUSIANA) { + SceneId sceneId = (SceneId)Play_GetOriginalSceneId(staticRegion.sceneId); + + // For some scenes we want to show them in their parent instead + if (scenesToCheckParent.contains(sceneId)) { RandoRegionId connectedRegionId = RR_MAX; if (regionId == RR_LONE_PEAK_SHRINE) { connectedRegionId = RR_LONE_PEAK_SHRINE; @@ -214,26 +215,17 @@ void initializeSceneChecks() { continue; } } - SceneId connectedSceneId = Rando::Logic::Regions[connectedRegionId].sceneId; - for (auto& [randoCheckId, _] : staticRegion.checks) { - RandoSaveCheck& randoSaveCheck = RANDO_SAVE_CHECKS[randoCheckId]; - if (!randoSaveCheck.shuffled) { - continue; - } - sceneChecks[connectedSceneId].insert(randoCheckId); - } - } else { - // Handle enemy drop seperately, they can be in multiple regions - for (auto& [randoCheckId, _] : staticRegion.checks) { - auto& randoStaticCheck = Rando::StaticData::Checks[randoCheckId]; - RandoSaveCheck& randoSaveCheck = RANDO_SAVE_CHECKS[randoCheckId]; - if (!randoSaveCheck.shuffled || randoStaticCheck.randoCheckType != RCTYPE_ENEMY_DROP) { - continue; - } - - SceneId sceneId = (SceneId)Play_GetOriginalSceneId(staticRegion.sceneId); - sceneChecks[sceneId].insert(randoCheckId); + sceneId = Rando::Logic::Regions[connectedRegionId].sceneId; + regionParentSceneMap[regionId] = sceneId; + } + for (auto& [randoCheckId, _] : staticRegion.checks) { + RandoSaveCheck& randoSaveCheck = RANDO_SAVE_CHECKS[randoCheckId]; + auto& randoStaticCheck = Rando::StaticData::Checks[randoCheckId]; + if (!randoSaveCheck.shuffled) { + continue; } + checkTypeFiltersAvailable.insert(randoStaticCheck.randoCheckType); + sceneChecks[sceneId].insert(randoCheckId); } } @@ -356,8 +348,7 @@ void CheckTrackerDrawNonLogicalList() { continue; } - if (CVAR_SHOW_CURRENT_SCENE && Play_GetOriginalSceneId(Rando::StaticData::Checks[checkId].sceneId) != - Play_GetOriginalSceneId(gPlayState->sceneId)) { + if (CVAR_SHOW_CURRENT_SCENE && sceneId != GetScrollTargetScene(gPlayState->sceneId)) { continue; } @@ -526,7 +517,7 @@ void CheckTrackerWindow::Draw() { if (!sCheckTrackerFilter.IsActive()) { if (UIWidgets::Button(ICON_FA_ARROWS_V, UIWidgets::ButtonOptions().Size(UIWidgets::Sizes::Inline))) { if (gPlayState) { - sScrollToTargetScene = Play_GetOriginalSceneId(gPlayState->sceneId); + sScrollToTargetScene = GetScrollTargetScene(gPlayState->sceneId); sScrollToTargetEntrance = gSaveContext.save.entrance; } } @@ -535,7 +526,7 @@ void CheckTrackerWindow::Draw() { if (UIWidgets::Button(ICON_FA_TIMES, UIWidgets::ButtonOptions().Size(UIWidgets::Sizes::Inline))) { sCheckTrackerFilter.Clear(); if (gPlayState) { - sScrollToTargetScene = Play_GetOriginalSceneId(gPlayState->sceneId); + sScrollToTargetScene = GetScrollTargetScene(gPlayState->sceneId); sScrollToTargetEntrance = gSaveContext.save.entrance; } } @@ -714,7 +705,7 @@ void Init() { } GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneInit>([](s8 sceneId, s8 spawnNum) { if (CVAR_SCROLL_TO_SCENE) { - sScrollToTargetScene = Play_GetOriginalSceneId(sceneId); + sScrollToTargetScene = GetScrollTargetScene(sceneId); sScrollToTargetEntrance = gSaveContext.save.entrance; } }); diff --git a/mm/2s2h/Rando/Logic/Logic.cpp b/mm/2s2h/Rando/Logic/Logic.cpp index 737e1cbcd..687af5f2d 100644 --- a/mm/2s2h/Rando/Logic/Logic.cpp +++ b/mm/2s2h/Rando/Logic/Logic.cpp @@ -11,6 +11,25 @@ std::map<RandoRegionId, RandoRegion> Regions = {}; uint64_t gCurrentRegionTime = 0; +// GROTTOS +// grottos are a bit weird, 15 of them share entrances with other grottos, so we have to check an extra field to +// determine which grotto we're in, and convert the entrance ID to an unused one that we store for logic purposes. +// ENTRANCE(GROTTOS, 17) = RR_TERMINA_FIELD_COW_GROTTO +// ENTRANCE(GROTTOS, 18) = RR_TERMINA_FIELD_PILLAR_GROTTO +// ENTRANCE(GROTTOS, 19) = RR_TERMINA_FIELD_TALL_GRASS_GROTTO +// ENTRANCE(GROTTOS, 20) = RR_ROAD_TO_SOUTHERN_SWAMP_GROTTO +// ENTRANCE(GROTTOS, 21) = RR_SOUTHERN_SWAMP_GROTTO +// ENTRANCE(GROTTOS, 22) = RR_WOODS_OF_MYSTERY_GROTTO +// ENTRANCE(GROTTOS, 23) = RR_MOUNTAIN_VILLAGE_TUNNEL_GROTTO +// ENTRANCE(GROTTOS, 24) = RR_PATH_TO_GORON_VILLAGE_RAMP_GROTTO +// ENTRANCE(GROTTOS, 25) = RR_PATH_TO_SNOWHEAD_GROTTO +// ENTRANCE(GROTTOS, 26) = RR_GREAT_BAY_COAST_FISHERMAN_GROTTO +// ENTRANCE(GROTTOS, 27) = RR_GREAT_BAY_COAST_COW_GROTTO +// ENTRANCE(GROTTOS, 28) = RR_ZORA_CAPE_GROTTO +// ENTRANCE(GROTTOS, 29) = RR_IKANA_CANYON_GROTTO +// ENTRANCE(GROTTOS, 30) = RR_IKANA_GRAVEYARD_GROTTO +// ENTRANCE(GROTTOS, 31) = RR_ROAD_TO_IKANA_GROTTO + RandoRegionId GetRegionIdFromEntrance(s32 entrance) { static std::map<s32, RandoRegionId> entranceToRegionId; if (entranceToRegionId.empty()) { @@ -27,8 +46,60 @@ RandoRegionId GetRegionIdFromEntrance(s32 entrance) { } } - if (entranceToRegionId.contains(entrance)) { - return entranceToRegionId[entrance]; + s32 modifiedEntrance = entrance; + if (entrance == ENTRANCE(GROTTOS, 4)) { + switch (gSaveContext.respawn[RESPAWN_MODE_DOWN].entrance) { + case ENTRANCE(TERMINA_FIELD, 0): + modifiedEntrance = gSaveContext.respawn[RESPAWN_MODE_UNK_3].data == 0x3F ? ENTRANCE(GROTTOS, 19) + : ENTRANCE(GROTTOS, 18); + break; + case ENTRANCE(ROAD_TO_SOUTHERN_SWAMP, 0): + modifiedEntrance = ENTRANCE(GROTTOS, 20); + break; + case ENTRANCE(SOUTHERN_SWAMP_POISONED, 0): + modifiedEntrance = ENTRANCE(GROTTOS, 21); + break; + case ENTRANCE(WOODS_OF_MYSTERY, 0): + modifiedEntrance = ENTRANCE(GROTTOS, 22); + break; + case ENTRANCE(MOUNTAIN_VILLAGE_SPRING, 0): + modifiedEntrance = ENTRANCE(GROTTOS, 23); + break; + case ENTRANCE(PATH_TO_GORON_VILLAGE_WINTER, 0): + modifiedEntrance = ENTRANCE(GROTTOS, 24); + break; + case ENTRANCE(PATH_TO_SNOWHEAD, 0): + modifiedEntrance = ENTRANCE(GROTTOS, 25); + break; + case ENTRANCE(GREAT_BAY_COAST, 0): + modifiedEntrance = ENTRANCE(GROTTOS, 26); + break; + case ENTRANCE(ZORA_CAPE, 0): + modifiedEntrance = ENTRANCE(GROTTOS, 28); + break; + case ENTRANCE(IKANA_CANYON, 0): + modifiedEntrance = ENTRANCE(GROTTOS, 29); + break; + case ENTRANCE(IKANA_GRAVEYARD, 0): + modifiedEntrance = ENTRANCE(GROTTOS, 30); + break; + case ENTRANCE(ROAD_TO_IKANA, 0): + modifiedEntrance = ENTRANCE(GROTTOS, 31); + break; + } + } else if (entrance == ENTRANCE(GROTTOS, 10)) { + switch (gSaveContext.respawn[RESPAWN_MODE_DOWN].entrance) { + case ENTRANCE(TERMINA_FIELD, 0): + modifiedEntrance = ENTRANCE(GROTTOS, 17); + break; + case ENTRANCE(GREAT_BAY_COAST, 0): + modifiedEntrance = ENTRANCE(GROTTOS, 27); + break; + } + } + + if (entranceToRegionId.contains(modifiedEntrance)) { + return entranceToRegionId[modifiedEntrance]; } return RR_MAX; diff --git a/mm/2s2h/Rando/Logic/Regions/East.cpp b/mm/2s2h/Rando/Logic/Regions/East.cpp index f4f975715..98c38e650 100644 --- a/mm/2s2h/Rando/Logic/Regions/East.cpp +++ b/mm/2s2h/Rando/Logic/Regions/East.cpp @@ -134,8 +134,8 @@ static RegisterShipInitFunc initFunc([]() { CHECK(RC_IKANA_CANYON_GROTTO_GRASS_14, true), CHECK(RC_ENEMY_DROP_MINI_BABA, CanKillEnemy(ACTOR_EN_KAREBABA)), }, - .connections = { - CONNECTION(RR_IKANA_CANYON_LOWER, true), // TODO: Grotto mapping + .exits = { // TO FROM + EXIT(ENTRANCE(IKANA_CANYON, 0), ENTRANCE(GROTTOS, 29), true), }, }; Regions[RR_IKANA_CANYON_LOWER] = RandoRegion{ .name = "Lower", .sceneId = SCENE_IKANA, @@ -147,6 +147,7 @@ static RegisterShipInitFunc initFunc([]() { CHECK(RC_ENEMY_DROP_GARO, CanKillEnemy(ACTOR_EN_JSO)), }, .exits = { // TO FROM + EXIT(ENTRANCE(GROTTOS, 29), ENTRANCE(IKANA_CANYON, 0), CAN_USE_ABILITY(SWIM)), EXIT(ENTRANCE(ROAD_TO_IKANA, 1), ENTRANCE(IKANA_CANYON, 0), true), /* TODO : Sakon's Hideout is heavily flag based so we should check for those. Consider what I have in here now to be loose placeholders. @@ -160,7 +161,6 @@ static RegisterShipInitFunc initFunc([]() { .connections = { // Octorok soul not needed; the player can also create ice platforms on the water itself. CONNECTION(RR_IKANA_CANYON_UPPER, HAS_ITEM(ITEM_HOOKSHOT) && CAN_USE_MAGIC_ARROW(ICE)), - CONNECTION(RR_IKANA_CANYON_GROTTO, CAN_USE_ABILITY(SWIM)), // TODO: Grotto mapping }, .events = { EVENT(RE_ACCESS_BLUE_POTION_REFILL, CUR_UPG_VALUE(UPG_WALLET) >= 1), @@ -213,8 +213,8 @@ static RegisterShipInitFunc initFunc([]() { CHECK(RC_IKANA_GRAVEYARD_GROTTO_GRASS_14, true), CHECK(RC_ENEMY_DROP_MINI_BABA, CanKillEnemy(ACTOR_EN_KAREBABA)), }, - .connections = { - CONNECTION(RR_IKANA_GRAVEYARD_LOWER, true), // TODO: Grotto mapping + .exits = { // TO FROM + EXIT(ENTRANCE(IKANA_GRAVEYARD, 0), ENTRANCE(GROTTOS, 30), true), }, }; Regions[RR_IKANA_GRAVEYARD_LOWER] = RandoRegion{ .name = "Lower", .sceneId = SCENE_BOTI, @@ -232,6 +232,7 @@ static RegisterShipInitFunc initFunc([]() { CHECK(RC_ENEMY_DROP_BAD_BAT, CanKillEnemy(ACTOR_EN_BAT) && IS_DAY()), // Day only }, .exits = { // TO FROM + EXIT(ENTRANCE(GROTTOS, 30), ENTRANCE(IKANA_GRAVEYARD, 0), CAN_USE_EXPLOSIVE), EXIT(ENTRANCE(ROAD_TO_IKANA, 2), ENTRANCE(IKANA_GRAVEYARD, 0), true), EXIT(ENTRANCE(DAMPES_HOUSE, 0), ONE_WAY_EXIT, HAS_ITEM(ITEM_MASK_CAPTAIN) && IS_NIGHT3()), // Day 3 hole EXIT(ENTRANCE(BENEATH_THE_GRAVERYARD, 0), ENTRANCE(IKANA_GRAVEYARD, 2), HAS_ITEM(ITEM_MASK_CAPTAIN) && IS_NIGHT2()), // Day 2 hole @@ -239,7 +240,6 @@ static RegisterShipInitFunc initFunc([]() { }, .connections = { CONNECTION(RR_IKANA_GRAVEYARD_UPPER, CAN_PLAY_SONG(SONATA)), - CONNECTION(RR_IKANA_GRAVEYARD_GROTTO, CAN_USE_EXPLOSIVE), // TODO: Grotto mapping }, .oneWayEntrances = { ENTRANCE(IKANA_GRAVEYARD, 4), // Exiting Dampe's house @@ -306,10 +306,10 @@ static RegisterShipInitFunc initFunc([]() { }, .exits = { // TO FROM EXIT(ENTRANCE(TERMINA_FIELD, 4), ENTRANCE(ROAD_TO_IKANA, 0), true), + EXIT(ENTRANCE(GROTTOS, 31), ENTRANCE(ROAD_TO_IKANA, 0), CAN_BE_GORON), }, .connections = { CONNECTION(RR_ROAD_TO_IKANA_BELOW_LEDGE, CAN_RIDE_EPONA), - CONNECTION(RR_ROAD_TO_IKANA_GROTTO, CAN_BE_GORON), // TODO: Grotto mapping }, }; Regions[RR_ROAD_TO_IKANA_GROTTO] = RandoRegion{ .name = "Road to Ikana Grotto", .sceneId = SCENE_KAKUSIANA, @@ -331,8 +331,8 @@ static RegisterShipInitFunc initFunc([]() { CHECK(RC_ROAD_TO_IKANA_GROTTO_GRASS_14, true), CHECK(RC_ENEMY_DROP_MINI_BABA, CanKillEnemy(ACTOR_EN_KAREBABA)), }, - .connections = { - CONNECTION(RR_ROAD_TO_IKANA_FIELD_SIDE, true), // TODO: Grotto mapping + .exits = { // TO FROM + EXIT(ENTRANCE(ROAD_TO_IKANA, 0), ENTRANCE(GROTTOS, 31), true), }, }; Regions[RR_SAKON_HIDEOUT] = RandoRegion{ .sceneId = SCENE_SECOM, diff --git a/mm/2s2h/Rando/Logic/Regions/North.cpp b/mm/2s2h/Rando/Logic/Regions/North.cpp index ad4599860..b7e38e41f 100644 --- a/mm/2s2h/Rando/Logic/Regions/North.cpp +++ b/mm/2s2h/Rando/Logic/Regions/North.cpp @@ -219,8 +219,8 @@ static RegisterShipInitFunc initFunc([]() { CHECK(RC_MOUNTAIN_VILLAGE_TUNNEL_GROTTO_GRASS_14, true), CHECK(RC_ENEMY_DROP_MINI_BABA, CanKillEnemy(ACTOR_EN_KAREBABA)), }, - .connections = { - CONNECTION(RR_MOUNTAIN_VILLAGE, true), // TODO: Grotto mapping + .exits = { // TO FROM + EXIT(ENTRANCE(MOUNTAIN_VILLAGE_WINTER, 0), ENTRANCE(GROTTOS, 23), true), }, }; Regions[RR_MOUNTAIN_VILLAGE] = RandoRegion{ .sceneId = SCENE_10YUKIYAMANOMURA, @@ -286,6 +286,7 @@ static RegisterShipInitFunc initFunc([]() { CHECK(RC_ENEMY_DROP_WOLFOS, CanKillEnemy(ACTOR_OBJ_SNOWBALL) && CanKillEnemy(ACTOR_EN_WF) && SECOND_DAY()), // Day 2 only }, .exits = { // TO FROM + EXIT(ENTRANCE(GROTTOS, 23), ENTRANCE(MOUNTAIN_VILLAGE_WINTER, 0), RANDO_EVENTS[RE_CLEARED_SNOWHEAD_TEMPLE]), EXIT(ENTRANCE(MOUNTAIN_SMITHY, 0), ENTRANCE(MOUNTAIN_VILLAGE_WINTER, 1), true), EXIT(ENTRANCE(PATH_TO_GORON_VILLAGE_WINTER, 0), ENTRANCE(MOUNTAIN_VILLAGE_WINTER, 2), true), // TODO: When it's spring you need goron mask or zora mask instead? @@ -293,9 +294,6 @@ static RegisterShipInitFunc initFunc([]() { EXIT(ENTRANCE(PATH_TO_SNOWHEAD, 0), ENTRANCE(MOUNTAIN_VILLAGE_WINTER, 4), true), EXIT(ENTRANCE(PATH_TO_MOUNTAIN_VILLAGE, 1), ENTRANCE(MOUNTAIN_VILLAGE_WINTER, 6), true), }, - .connections = { - CONNECTION(RR_MOUNTAIN_VILLAGE_TUNNEL_GROTTO, RANDO_EVENTS[RE_CLEARED_SNOWHEAD_TEMPLE]), // TODO: Grotto mapping - }, .oneWayEntrances = { ENTRANCE(MOUNTAIN_VILLAGE_WINTER, 8), // From Song of Soaring } @@ -305,7 +303,7 @@ static RegisterShipInitFunc initFunc([]() { CHECK(RC_TWIN_ISLANDS_FROZEN_GROTTO_CHEST, CAN_USE_EXPLOSIVE), }, .exits = { // TO FROM - EXIT(ENTRANCE(PATH_TO_GORON_VILLAGE_WINTER, 0), ENTRANCE(GROTTOS, 5), true), // TODO: Grotto mapping + EXIT(ENTRANCE(PATH_TO_GORON_VILLAGE_WINTER, 0), ENTRANCE(GROTTOS, 5), true), }, .events = { EVENT(RE_ACCESS_HOT_SPRING_WATER, true), @@ -330,8 +328,8 @@ static RegisterShipInitFunc initFunc([]() { CHECK(RC_TWIN_ISLANDS_RAMP_GROTTO_GRASS_14, true), CHECK(RC_ENEMY_DROP_MINI_BABA, CanKillEnemy(ACTOR_EN_KAREBABA)), }, - .connections = { - CONNECTION(RR_PATH_TO_GORON_VILLAGE, true), // TODO: Grotto mapping + .exits = { // TO FROM + EXIT(ENTRANCE(PATH_TO_GORON_VILLAGE_WINTER, 0), ENTRANCE(GROTTOS, 24), true), }, }; Regions[RR_PATH_TO_GORON_VILLAGE] = RandoRegion{ .sceneId = SCENE_17SETUGEN, @@ -389,16 +387,14 @@ static RegisterShipInitFunc initFunc([]() { CHECK(RC_ENEMY_DROP_SNAPPER, CanKillEnemy(ACTOR_OBJ_SNOWBALL) && CanKillEnemy(ACTOR_EN_KAME)), }, .exits = { // TO FROM - EXIT(ENTRANCE(GROTTOS, 5), ENTRANCE(PATH_TO_GORON_VILLAGE_WINTER, 0), RANDO_EVENTS[RE_CLEARED_SNOWHEAD_TEMPLE] || CAN_USE_MAGIC_ARROW(FIRE)), // TODO: Grotto mapping Hot spring + EXIT(ENTRANCE(GROTTOS, 5), ENTRANCE(PATH_TO_GORON_VILLAGE_WINTER, 0), RANDO_EVENTS[RE_CLEARED_SNOWHEAD_TEMPLE] || CAN_USE_MAGIC_ARROW(FIRE)), + // TODO: This can be reached in entrance rando if coming from Goron Racetrack + EXIT(ENTRANCE(GROTTOS, 24), ENTRANCE(PATH_TO_GORON_VILLAGE_WINTER, 0), CAN_USE_EXPLOSIVE && (CAN_BE_GORON || CAN_HOOK_SCARECROW)), EXIT(ENTRANCE(MOUNTAIN_VILLAGE_WINTER, 2), ENTRANCE(PATH_TO_GORON_VILLAGE_WINTER, 0), true), EXIT(ENTRANCE(GORON_VILLAGE_WINTER, 0), ENTRANCE(PATH_TO_GORON_VILLAGE_WINTER, 1), true), // This could also be opened by completing Medigoron's test without actually getting the Powder Keg as a item. Not sure what the flag for that is however. EXIT(ENTRANCE(GORON_RACETRACK, 0), ENTRANCE(PATH_TO_GORON_VILLAGE_WINTER, 2), HAS_ITEM(ITEM_POWDER_KEG) && CAN_BE_GORON), }, - .connections = { - // TODO: This can be reached in entrance rando if coming from Goron Racetrack - CONNECTION(RR_PATH_TO_GORON_VILLAGE_RAMP_GROTTO, CAN_USE_EXPLOSIVE && (CAN_BE_GORON || CAN_HOOK_SCARECROW)), // TODO: Grotto mapping - }, .events = { EVENT(RE_ACCESS_PICTOGRAPH_TINGLE, HAS_ITEM(ITEM_PICTOGRAPH_BOX)), }, @@ -468,8 +464,8 @@ static RegisterShipInitFunc initFunc([]() { CHECK(RC_PATH_TO_SNOWHEAD_GROTTO_GRASS_14, true), CHECK(RC_ENEMY_DROP_MINI_BABA, CanKillEnemy(ACTOR_EN_KAREBABA)), }, - .connections = { - CONNECTION(RR_PATH_TO_SNOWHEAD_UPPER, true), // TODO: Grotto mapping + .exits = { // TO FROM + EXIT(ENTRANCE(PATH_TO_SNOWHEAD, 0), ENTRANCE(GROTTOS, 25), true), }, }; Regions[RR_PATH_TO_SNOWHEAD_LOWER] = RandoRegion{ .sceneId = SCENE_14YUKIDAMANOMITI, @@ -509,10 +505,10 @@ static RegisterShipInitFunc initFunc([]() { }, .exits = { // TO FROM EXIT(ENTRANCE(SNOWHEAD, 0), ENTRANCE(PATH_TO_SNOWHEAD, 1), true), + EXIT(ENTRANCE(GROTTOS, 25), ENTRANCE(PATH_TO_SNOWHEAD, 0), CAN_USE_EXPLOSIVE), }, .connections = { CONNECTION(RR_PATH_TO_SNOWHEAD_MIDDLE, CAN_BE_GORON && HAS_MAGIC), - CONNECTION(RR_PATH_TO_SNOWHEAD_GROTTO, CAN_USE_EXPLOSIVE), // TODO: Grotto mapping }, }; Regions[RR_SNOWHEAD_GREAT_FAIRY_FOUNTAIN] = RandoRegion{ .sceneId = SCENE_YOUSEI_IZUMI, diff --git a/mm/2s2h/Rando/Logic/Regions/South.cpp b/mm/2s2h/Rando/Logic/Regions/South.cpp index 5e9a25499..c165e9765 100644 --- a/mm/2s2h/Rando/Logic/Regions/South.cpp +++ b/mm/2s2h/Rando/Logic/Regions/South.cpp @@ -73,7 +73,7 @@ static RegisterShipInitFunc initFunc([]() { // TODO: Bean salesman check }, .exits = { // TO FROM - EXIT(ENTRANCE(DEKU_PALACE, 9), ENTRANCE(GROTTOS, 12), true), // TODO: Grotto mapping + EXIT(ENTRANCE(DEKU_PALACE, 9), ENTRANCE(GROTTOS, 12), true), }, }; Regions[RR_DEKU_PALACE_INSIDE_LOWER] = RandoRegion{ .name = "Inside, Lower", .sceneId = SCENE_22DEKUCITY, @@ -106,7 +106,7 @@ static RegisterShipInitFunc initFunc([]() { }, .exits = { // TO FROM EXIT(ENTRANCE(DEKU_KINGS_CHAMBER, 0), ENTRANCE(DEKU_PALACE, 2), true), - EXIT(ENTRANCE(GROTTOS, 12), ENTRANCE(DEKU_PALACE, 9), true), // TODO: Grotto mapping + EXIT(ENTRANCE(GROTTOS, 12), ENTRANCE(DEKU_PALACE, 9), true), }, .connections = { CONNECTION(RR_DEKU_PALACE_OUTSIDE, true), @@ -222,8 +222,8 @@ static RegisterShipInitFunc initFunc([]() { CHECK(RC_ROAD_TO_SOUTHERN_SWAMP_GROTTO_GRASS_14, true), CHECK(RC_ENEMY_DROP_MINI_BABA, CanKillEnemy(ACTOR_EN_KAREBABA)), }, - .connections = { - CONNECTION(RR_ROAD_TO_SOUTHERN_SWAMP, true), // TODO: Grotto mapping + .exits = { // TO FROM + EXIT(ENTRANCE(ROAD_TO_SOUTHERN_SWAMP, 0), ENTRANCE(GROTTOS, 20), true), }, }; Regions[RR_ROAD_TO_SOUTHERN_SWAMP] = RandoRegion{ .sceneId = SCENE_24KEMONOMITI, @@ -264,12 +264,10 @@ static RegisterShipInitFunc initFunc([]() { }, .exits = { // TO FROM EXIT(ENTRANCE(TERMINA_FIELD, 1), ENTRANCE(ROAD_TO_SOUTHERN_SWAMP, 0), true), + EXIT(ENTRANCE(GROTTOS, 20), ENTRANCE(ROAD_TO_SOUTHERN_SWAMP, 0), true), EXIT(ENTRANCE(SOUTHERN_SWAMP_POISONED, 0), ENTRANCE(ROAD_TO_SOUTHERN_SWAMP, 1), true), EXIT(ENTRANCE(SWAMP_SHOOTING_GALLERY, 0), ENTRANCE(ROAD_TO_SOUTHERN_SWAMP, 2), BEFORE(TIME_NIGHT1_PM_10_00) || BETWEEN(TIME_DAY2_AM_06_00, TIME_NIGHT2_PM_10_00) || BETWEEN(TIME_DAY3_AM_06_00, TIME_NIGHT3_PM_10_00)), }, - .connections = { - CONNECTION(RR_ROAD_TO_SOUTHERN_SWAMP_GROTTO, true), // TODO: Grotto mapping - }, .events = { EVENT(RE_ACCESS_PICTOGRAPH_TINGLE, HAS_ITEM(ITEM_PICTOGRAPH_BOX)), EVENT(RE_ACCESS_SPRING_WATER, true), @@ -294,8 +292,8 @@ static RegisterShipInitFunc initFunc([]() { CHECK(RC_SOUTHERN_SWAMP_GROTTO_GRASS_14, true), CHECK(RC_ENEMY_DROP_MINI_BABA, CanKillEnemy(ACTOR_EN_KAREBABA)), }, - .connections = { - CONNECTION(RR_SOUTHERN_SWAMP_SOUTH, true), // TODO: Grotto mapping + .exits = { // TO FROM + EXIT(ENTRANCE(SOUTHERN_SWAMP_POISONED, 0), ENTRANCE(GROTTOS, 21), true), }, }; Regions[RR_SOUTHERN_SWAMP_NORTH] = RandoRegion{ .name = "North Tourist Section", .sceneId = SCENE_20SICHITAI, @@ -434,13 +432,13 @@ static RegisterShipInitFunc initFunc([]() { }; Regions[RR_SOUTHERN_SWAMP_SOUTH] = RandoRegion{ .name = "South Section", .sceneId = SCENE_20SICHITAI, .exits = { // TO FROM + EXIT(ENTRANCE(GROTTOS, 21), ENTRANCE(SOUTHERN_SWAMP_POISONED, 0), CAN_BE_DEKU || (RANDO_EVENTS[RE_CLEARED_WOODFALL_TEMPLE] && CAN_TRAVERSE_WAIST_DEEP_WATER)), EXIT(ENTRANCE(DEKU_PALACE, 0), ENTRANCE(SOUTHERN_SWAMP_POISONED, 3), true), EXIT(ENTRANCE(SWAMP_SPIDER_HOUSE, 0), ENTRANCE(SOUTHERN_SWAMP_POISONED, 8), CAN_LIGHT_TORCH_NEAR_ANOTHER && (CAN_BE_DEKU || (RANDO_EVENTS[RE_CLEARED_WOODFALL_TEMPLE] && (CAN_USE_ABILITY(SWIM) || CAN_BE_ZORA)))), }, .connections = { CONNECTION(RR_SOUTHERN_SWAMP_NORTH, CanGetPastBigOcto()), CONNECTION(RR_SOUTHERN_SWAMP_NEAR_FLOWERS, CanGetPastBigOctoWithoutBoat()), - CONNECTION(RR_SOUTHERN_SWAMP_GROTTO, CAN_BE_DEKU || (RANDO_EVENTS[RE_CLEARED_WOODFALL_TEMPLE] && CAN_TRAVERSE_WAIST_DEEP_WATER)), // TODO: Grotto mapping CONNECTION(RR_SOUTHERN_SWAMP_SOUTH_UPPER, RANDO_EVENTS[RE_CLEARED_WOODFALL_TEMPLE] && CAN_TRAVERSE_WAIST_DEEP_WATER), // Climbable vines, only in cleared swamp }, .events = { @@ -557,8 +555,8 @@ static RegisterShipInitFunc initFunc([]() { CHECK(RC_WOODS_OF_MYSTERY_GROTTO_GRASS_14, true), CHECK(RC_ENEMY_DROP_MINI_BABA, CanKillEnemy(ACTOR_EN_KAREBABA)), }, - .connections = { - CONNECTION(RR_WOODS_OF_MYSTERY, SECOND_DAY()), // TODO: Grotto mapping + .exits = { // TO FROM + EXIT(ENTRANCE(WOODS_OF_MYSTERY, 0), ENTRANCE(GROTTOS, 22), true), }, }; Regions[RR_WOODS_OF_MYSTERY] = RandoRegion{ .sceneId = SCENE_26SARUNOMORI, @@ -589,11 +587,9 @@ static RegisterShipInitFunc initFunc([]() { CHECK(RC_ENEMY_DROP_SNAPPER, CAN_BE_DEKU && CanKillEnemy(ACTOR_EN_KAME)), }, .exits = { // TO FROM + EXIT(ENTRANCE(GROTTOS, 22), ENTRANCE(WOODS_OF_MYSTERY, 0), SECOND_DAY()), EXIT(ENTRANCE(SOUTHERN_SWAMP_POISONED, 7), ENTRANCE(WOODS_OF_MYSTERY, 0), true), }, - .connections = { - CONNECTION(RR_WOODS_OF_MYSTERY_GROTTO, SECOND_DAY()), // TODO: Grotto mapping - }, .events = { EVENT(RE_SAVED_KOUME, HAS_BOTTLE && (CAN_ACCESS(RED_POTION_REFILL) || CAN_ACCESS(BLUE_POTION_REFILL))), }, diff --git a/mm/2s2h/Rando/Logic/Regions/TerminaField.cpp b/mm/2s2h/Rando/Logic/Regions/TerminaField.cpp index 2f02b917d..62fadf8c7 100644 --- a/mm/2s2h/Rando/Logic/Regions/TerminaField.cpp +++ b/mm/2s2h/Rando/Logic/Regions/TerminaField.cpp @@ -31,7 +31,7 @@ static RegisterShipInitFunc initFunc([]() { CHECK(RC_ENEMY_DROP_BIO_DEKU_BABA, CanKillEnemy(ACTOR_BOSS_05)), }, .exits = { // TO FROM - EXIT(ENTRANCE(TERMINA_FIELD, 0), ENTRANCE(GROTTOS, 11), true), // TODO: Grotto mapping + EXIT(ENTRANCE(TERMINA_FIELD, 0), ENTRANCE(GROTTOS, 11), true), }, }; Regions[RR_TERMINA_FIELD_COW_GROTTO] = RandoRegion{ .name = "Termina Field Cow", .sceneId = SCENE_KAKUSIANA, @@ -112,8 +112,8 @@ static RegisterShipInitFunc initFunc([]() { CHECK(RC_TERMINA_FIELD_COW_GROTTO_GRASS_72, true), CHECK(RC_ENEMY_DROP_GIANT_BEE, CAN_USE_PROJECTILE && CanKillEnemy(ACTOR_EN_BEE)), // In a beehive }, - .connections = { - CONNECTION(RR_TERMINA_FIELD, true), // TODO: Grotto mapping + .exits = { // TO FROM + EXIT(ENTRANCE(TERMINA_FIELD, 0), ENTRANCE(GROTTOS, 17), true), }, }; Regions[RR_TERMINA_FIELD_DODONGO_GROTTO] = RandoRegion{ .name = "Termina Field Dodongo", .sceneId = SCENE_KAKUSIANA, @@ -122,7 +122,7 @@ static RegisterShipInitFunc initFunc([]() { CHECK(RC_ENEMY_DROP_DODONGO, CanKillEnemy(ACTOR_EN_DODONGO)), }, .exits = { // TO FROM - EXIT(ENTRANCE(TERMINA_FIELD, 0), ENTRANCE(GROTTOS, 7), true), // TODO: Grotto mapping + EXIT(ENTRANCE(TERMINA_FIELD, 0), ENTRANCE(GROTTOS, 7), true), }, }; Regions[RR_TERMINA_FIELD_GOSSIP_STONE_GROTTO_1] = RandoRegion{ .name = "Termina Field Gossip Stone #1", .sceneId = SCENE_KAKUSIANA, @@ -131,7 +131,7 @@ static RegisterShipInitFunc initFunc([]() { CHECK(RC_ENEMY_DROP_SKULLWALLTULA, CanKillEnemy(ACTOR_EN_SW)), }, .exits = { // TO FROM - EXIT(ENTRANCE(TERMINA_FIELD, 0), ENTRANCE(GROTTOS, 1), true), // TODO: Grotto mapping + EXIT(ENTRANCE(TERMINA_FIELD, 0), ENTRANCE(GROTTOS, 1), true), }, .events = { // TODO: gSaveContext.save.saveInfo.unk_EA0 for gossip stone check @@ -139,7 +139,7 @@ static RegisterShipInitFunc initFunc([]() { }; Regions[RR_TERMINA_FIELD_GOSSIP_STONE_GROTTO_2] = RandoRegion{ .name = "Termina Field Gossip Stone #2", .sceneId = SCENE_KAKUSIANA, .exits = { // TO FROM - EXIT(ENTRANCE(TERMINA_FIELD, 0), ENTRANCE(GROTTOS, 3), true), // TODO: Grotto mapping + EXIT(ENTRANCE(TERMINA_FIELD, 0), ENTRANCE(GROTTOS, 3), true), }, .events = { // TODO: gSaveContext.save.saveInfo.unk_EA0 for gossip stone check @@ -156,7 +156,7 @@ static RegisterShipInitFunc initFunc([]() { CHECK(RC_ENEMY_DROP_GIANT_BEE, CAN_USE_PROJECTILE && CanKillEnemy(ACTOR_EN_BEE)), // In a beehive }, .exits = { // TO FROM - EXIT(ENTRANCE(TERMINA_FIELD, 0), ENTRANCE(GROTTOS, 0), true), // TODO: Grotto mapping + EXIT(ENTRANCE(TERMINA_FIELD, 0), ENTRANCE(GROTTOS, 0), true), }, .events = { // TODO: gSaveContext.save.saveInfo.unk_EA0 for gossip stone check @@ -171,7 +171,7 @@ static RegisterShipInitFunc initFunc([]() { CHECK(RC_TERMINA_FIELD_GOSSIP_STONE_GROTTO_4_GRASS_05, true), }, .exits = { // TO FROM - EXIT(ENTRANCE(TERMINA_FIELD, 0), ENTRANCE(GROTTOS, 2), true), // TODO: Grotto mapping + EXIT(ENTRANCE(TERMINA_FIELD, 0), ENTRANCE(GROTTOS, 2), true), }, .events = { // TODO: gSaveContext.save.saveInfo.unk_EA0 for gossip stone check @@ -195,7 +195,7 @@ static RegisterShipInitFunc initFunc([]() { CHECK(RC_ENEMY_DROP_PEAHAT, CanKillEnemy(ACTOR_EN_PEEHAT)), }, .exits = { // TO FROM - EXIT(ENTRANCE(TERMINA_FIELD, 0), ENTRANCE(GROTTOS, 13), true), // TODO: Grotto mapping + EXIT(ENTRANCE(TERMINA_FIELD, 0), ENTRANCE(GROTTOS, 13), true), }, }; Regions[RR_TERMINA_FIELD_PILLAR_GROTTO] = RandoRegion{ .name = "Termina Field Pillar", .sceneId = SCENE_KAKUSIANA, @@ -217,8 +217,8 @@ static RegisterShipInitFunc initFunc([]() { CHECK(RC_TERMINA_FIELD_PILLAR_GROTTO_GRASS_14, true), CHECK(RC_ENEMY_DROP_MINI_BABA, CanKillEnemy(ACTOR_EN_KAREBABA)), }, - .connections = { - CONNECTION(RR_TERMINA_FIELD, true), // TODO: Grotto mapping + .exits = { // TO FROM + EXIT(ENTRANCE(TERMINA_FIELD, 0), ENTRANCE(GROTTOS, 18), true), }, }; Regions[RR_TERMINA_FIELD_SCRUB_GROTTO] = RandoRegion{ .name = "Termina Field Scrub", .sceneId = SCENE_KAKUSIANA, @@ -228,7 +228,7 @@ static RegisterShipInitFunc initFunc([]() { CHECK(RC_TERMINA_FIELD_SCRUB_POT, true), }, .exits = { // TO FROM - EXIT(ENTRANCE(TERMINA_FIELD, 0), ENTRANCE(GROTTOS, 9), true), // TODO: Grotto mapping + EXIT(ENTRANCE(TERMINA_FIELD, 0), ENTRANCE(GROTTOS, 9), true), }, }; Regions[RR_TERMINA_FIELD_TALL_GRASS_GROTTO] = RandoRegion{ .name = "Termina Field Tall Grass", .sceneId = SCENE_KAKUSIANA, @@ -250,8 +250,8 @@ static RegisterShipInitFunc initFunc([]() { CHECK(RC_TERMINA_FIELD_TALL_GRASS_GROTTO_GRASS_14, true), CHECK(RC_ENEMY_DROP_MINI_BABA, CanKillEnemy(ACTOR_EN_KAREBABA)), }, - .connections = { - CONNECTION(RR_TERMINA_FIELD, true), // TODO: Grotto mapping + .exits = { // TO FROM + EXIT(ENTRANCE(TERMINA_FIELD, 0), ENTRANCE(GROTTOS, 19), true), }, }; Regions[RR_TERMINA_FIELD] = RandoRegion{ .sceneId = SCENE_00KEIKOKU, @@ -513,14 +513,17 @@ static RegisterShipInitFunc initFunc([]() { CHECK(RC_ENEMY_DROP_TAKKURI, CanKillEnemy(ACTOR_EN_THIEFBIRD)), }, .exits = { // TO FROM - EXIT(ENTRANCE(GROTTOS, 0), ENTRANCE(TERMINA_FIELD, 0), CAN_USE_EXPLOSIVE || CAN_BE_GORON), // TODO: Grotto mapping Gossip Stone #3 - EXIT(ENTRANCE(GROTTOS, 1), ENTRANCE(TERMINA_FIELD, 0), true), // TODO: Grotto mapping Gossip Stone #1 - EXIT(ENTRANCE(GROTTOS, 2), ENTRANCE(TERMINA_FIELD, 0), true), // TODO: Grotto mapping Gossip Stone #4 - EXIT(ENTRANCE(GROTTOS, 3), ENTRANCE(TERMINA_FIELD, 0), true), // TODO: Grotto mapping Gossip Stone #2 - EXIT(ENTRANCE(GROTTOS, 7), ENTRANCE(TERMINA_FIELD, 0), true), // TODO: Grotto mapping Dodongo - EXIT(ENTRANCE(GROTTOS, 9), ENTRANCE(TERMINA_FIELD, 0), true), // TODO: Grotto mapping Scrub - EXIT(ENTRANCE(GROTTOS, 11), ENTRANCE(TERMINA_FIELD, 0), CAN_USE_EXPLOSIVE || CAN_BE_GORON), // TODO: Grotto mapping bio baba grotto - EXIT(ENTRANCE(GROTTOS, 13), ENTRANCE(TERMINA_FIELD, 0), true), // TODO: Grotto mapping peahat grotto + EXIT(ENTRANCE(GROTTOS, 0), ENTRANCE(TERMINA_FIELD, 0), CAN_USE_EXPLOSIVE || CAN_BE_GORON), // Gossip Stone #3 + EXIT(ENTRANCE(GROTTOS, 1), ENTRANCE(TERMINA_FIELD, 0), true), // Gossip Stone #1 + EXIT(ENTRANCE(GROTTOS, 2), ENTRANCE(TERMINA_FIELD, 0), true), // Gossip Stone #4 + EXIT(ENTRANCE(GROTTOS, 3), ENTRANCE(TERMINA_FIELD, 0), true), // Gossip Stone #2 + EXIT(ENTRANCE(GROTTOS, 7), ENTRANCE(TERMINA_FIELD, 0), true), // Dodongo + EXIT(ENTRANCE(GROTTOS, 9), ENTRANCE(TERMINA_FIELD, 0), true), // Scrub + EXIT(ENTRANCE(GROTTOS, 11), ENTRANCE(TERMINA_FIELD, 0), CAN_USE_EXPLOSIVE || CAN_BE_GORON), // bio baba grotto + EXIT(ENTRANCE(GROTTOS, 13), ENTRANCE(TERMINA_FIELD, 0), true), // peahat grotto + EXIT(ENTRANCE(GROTTOS, 17), ENTRANCE(TERMINA_FIELD, 0), CAN_USE_EXPLOSIVE), // Cow grotto + EXIT(ENTRANCE(GROTTOS, 18), ENTRANCE(TERMINA_FIELD, 0), true), // Pillar grotto + EXIT(ENTRANCE(GROTTOS, 19), ENTRANCE(TERMINA_FIELD, 0), true), // Tall Grass grotto EXIT(ENTRANCE(WEST_CLOCK_TOWN, 0), ENTRANCE(TERMINA_FIELD, 0), true), EXIT(ENTRANCE(ROAD_TO_SOUTHERN_SWAMP, 0), ENTRANCE(TERMINA_FIELD, 1), true), EXIT(ENTRANCE(ROAD_TO_IKANA, 0), ENTRANCE(TERMINA_FIELD, 4), true), @@ -533,9 +536,6 @@ static RegisterShipInitFunc initFunc([]() { CONNECTION(RR_TERMINA_FIELD_BEFORE_PATH_TO_MOUNTAIN_VILLAGE, HAS_ITEM(ITEM_BOW)), CONNECTION(RR_TERMINA_FIELD_BEFORE_GREAT_BAY_COAST, CAN_PLAY_SONG(EPONA)), CONNECTION(RR_ASTRAL_OBSERVATORY_OUTSIDE, CAN_BE_DEKU), - CONNECTION(RR_TERMINA_FIELD_COW_GROTTO, CAN_USE_EXPLOSIVE), // TODO: Grotto mapping - CONNECTION(RR_TERMINA_FIELD_PILLAR_GROTTO, true), // TODO: Grotto mapping - CONNECTION(RR_TERMINA_FIELD_TALL_GRASS_GROTTO, true), // TODO: Grotto mapping }, }; }, {}); diff --git a/mm/2s2h/Rando/Logic/Regions/West.cpp b/mm/2s2h/Rando/Logic/Regions/West.cpp index a2725f8e8..6ff244133 100644 --- a/mm/2s2h/Rando/Logic/Regions/West.cpp +++ b/mm/2s2h/Rando/Logic/Regions/West.cpp @@ -94,9 +94,9 @@ static RegisterShipInitFunc initFunc([]() { CHECK(RC_GREAT_BAY_COAST_COW_GROTTO_GRASS_72, true), CHECK(RC_ENEMY_DROP_GIANT_BEE, CAN_USE_PROJECTILE && CanKillEnemy(ACTOR_EN_BEE)), // In a beehive }, - .connections = { - CONNECTION(RR_GREAT_BAY_COAST_CLIFFSIDE, true), // TODO: Grotto mapping - }, + .exits = { // TO FROM + EXIT(ENTRANCE(GREAT_BAY_COAST, 0), ENTRANCE(GROTTOS, 27), true), + } }; Regions[RR_GREAT_BAY_COAST_FISHERMAN_GROTTO] = RandoRegion{ .name = "Great Bay Coast Fisherman Grotto", .sceneId = SCENE_KAKUSIANA, .checks = { @@ -117,8 +117,8 @@ static RegisterShipInitFunc initFunc([]() { CHECK(RC_GREAT_BAY_COAST_FISHERMAN_GROTTO_GRASS_14, true), CHECK(RC_ENEMY_DROP_MINI_BABA, CanKillEnemy(ACTOR_EN_KAREBABA)), }, - .connections = { - CONNECTION(RR_GREAT_BAY_COAST, true), // TODO: Grotto mapping + .exits = { // TO FROM + EXIT(ENTRANCE(GREAT_BAY_COAST, 0), ENTRANCE(GROTTOS, 26), true), }, }; Regions[RR_GREAT_BAY_COAST] = RandoRegion{ .sceneId = SCENE_30GYOSON, @@ -144,10 +144,10 @@ static RegisterShipInitFunc initFunc([]() { EXIT(ENTRANCE(FISHERMANS_HUT, 0), ENTRANCE(GREAT_BAY_COAST, 4), true), EXIT(ENTRANCE(PIRATES_FORTRESS_EXTERIOR, 0), ENTRANCE(GREAT_BAY_COAST, 5), CAN_BE_ZORA && CAN_USE_ABILITY(SWIM)), EXIT(ENTRANCE(OCEANSIDE_SPIDER_HOUSE, 0), ENTRANCE(GREAT_BAY_COAST, 8), true), + EXIT(ENTRANCE(GROTTOS, 26), ENTRANCE(GREAT_BAY_COAST, 0), true), }, .connections = { - CONNECTION(RR_GREAT_BAY_COAST_CLIFFSIDE, CAN_USE_ABILITY(SWIM)), // TODO: Grotto mapping - CONNECTION(RR_GREAT_BAY_COAST_FISHERMAN_GROTTO, true), // TODO: Grotto mapping + CONNECTION(RR_GREAT_BAY_COAST_CLIFFSIDE, CAN_USE_ABILITY(SWIM)), CONNECTION(RR_GREAT_BAY_COAST_MARINE_LAB_EXTERIOR, CAN_USE_ABILITY(SWIM)), CONNECTION(RR_GREAT_BAY_COAST_PIRATE_LEDGE, CAN_USE_ABILITY(SWIM)), }, @@ -195,8 +195,10 @@ static RegisterShipInitFunc initFunc([]() { CHECK(RC_GREAT_BAY_COAST_LEDGE_POT_02, HAS_ITEM(ITEM_HOOKSHOT)), CHECK(RC_GREAT_BAY_COAST_LEDGE_POT_03, HAS_ITEM(ITEM_HOOKSHOT)), }, + .exits = { // TO FROM + EXIT(ENTRANCE(GROTTOS, 27), ENTRANCE(GREAT_BAY_COAST, 0), CAN_HOOK_SCARECROW && CAN_GROW_BEAN_PLANT), + }, .connections = { - CONNECTION(RR_GREAT_BAY_COAST_COW_GROTTO, CAN_HOOK_SCARECROW && CAN_GROW_BEAN_PLANT), // TODO: Grotto mapping CONNECTION(RR_GREAT_BAY_COAST, CAN_USE_ABILITY(SWIM)), CONNECTION(RR_GREAT_BAY_COAST_MINIGAME_PLATFORMS, RANDO_EVENTS[RE_CLEARED_GREAT_BAY_TEMPLE] && (HAS_ITEM(ITEM_HOOKSHOT) || CAN_USE_MAGIC_ARROW(ICE))), }, @@ -314,8 +316,8 @@ static RegisterShipInitFunc initFunc([]() { CHECK(RC_ZORA_CAPE_GROTTO_GRASS_14, true), CHECK(RC_ENEMY_DROP_MINI_BABA, CanKillEnemy(ACTOR_EN_KAREBABA)), }, - .connections = { - CONNECTION(RR_ZORA_CAPE, true), // TODO: Grotto mapping + .exits = { // TO FROM + EXIT(ENTRANCE(ZORA_CAPE, 0), ENTRANCE(GROTTOS, 28), true), }, }; Regions[RR_ZORA_CAPE_OUTSIDE_FAIRY_FOUNTAIN] = RandoRegion{ .sceneId = SCENE_31MISAKI, @@ -347,12 +349,12 @@ static RegisterShipInitFunc initFunc([]() { }, .exits = { // TO FROM EXIT(ENTRANCE(GREAT_BAY_COAST, 1), ENTRANCE(ZORA_CAPE, 0), true), + EXIT(ENTRANCE(GROTTOS, 28), ENTRANCE(ZORA_CAPE, 0), CAN_USE_EXPLOSIVE || CAN_BE_GORON), EXIT(ENTRANCE(ZORA_HALL, 0), ENTRANCE(ZORA_CAPE, 1), CAN_BE_ZORA && CAN_USE_ABILITY(SWIM)), EXIT(ENTRANCE(WATERFALL_RAPIDS, 0), ENTRANCE(ZORA_CAPE, 4), HAS_ITEM(ITEM_HOOKSHOT)), }, .connections = { CONNECTION(RR_ZORA_CAPE_BEFORE_GREAT_BAY_TEMPLE, CAN_BE_ZORA), - CONNECTION(RR_ZORA_CAPE_GROTTO, CAN_USE_EXPLOSIVE || CAN_BE_GORON), // TODO: Grotto mapping CONNECTION(RR_ZORA_CAPE_OUTSIDE_FAIRY_FOUNTAIN, HAS_ITEM(ITEM_HOOKSHOT)), } }; diff --git a/mm/2s2h/Rando/MiscBehavior/SariasSongHint.cpp b/mm/2s2h/Rando/MiscBehavior/SariasSongHint.cpp index c41cfbf3c..a85302580 100644 --- a/mm/2s2h/Rando/MiscBehavior/SariasSongHint.cpp +++ b/mm/2s2h/Rando/MiscBehavior/SariasSongHint.cpp @@ -120,7 +120,7 @@ void Rando::MiscBehavior::SariasSongHint() { "... You need my help?\x10 Alright but just this once. Search %y{{location}}%g, you will " "find what you need. Hurry now!"; CustomMessage::Replace(&entry.msg, "{{location}}", - Ship_GetSceneName(Rando::StaticData::Checks[randoCheckId].sceneId)); + Rando::StaticData::GetLocationNameForHint(randoCheckId, true)); Rando::RemoveItem(RI_SONG_SARIA); } diff --git a/mm/2s2h/Rando/StaticData/Checks.cpp b/mm/2s2h/Rando/StaticData/Checks.cpp index 74d625a89..b4123be1d 100644 --- a/mm/2s2h/Rando/StaticData/Checks.cpp +++ b/mm/2s2h/Rando/StaticData/Checks.cpp @@ -1,6 +1,9 @@ #include "StaticData.h" #include "ShipUtils.h" +#include "2s2h/CustomMessage/CustomMessage.h" +#include "2s2h/Rando/Logic/Logic.h" + extern "C" { s16 Play_GetOriginalSceneId(s16 sceneId); } @@ -2386,6 +2389,71 @@ void PopulateCheckNames() { } } +static std::set<SceneId> scenesToCheckParent = { + SCENE_KAKUSIANA, SCENE_TAKARAYA, SCENE_BOWLING, SCENE_SONCHONOIE, SCENE_SYATEKI_MIZU, SCENE_YADOYA, + SCENE_MILK_BAR, SCENE_AYASHIISHOP, SCENE_YOUSEI_IZUMI, SCENE_DOUJOU, SCENE_8ITEMSHOP, SCENE_BOMYA, + SCENE_POSTHOUSE, SCENE_TAKARAKUJI, SCENE_SYATEKI_MORI, SCENE_MAP_SHOP, SCENE_WITCH_SHOP, SCENE_F01C, + SCENE_F01_B, SCENE_OMOYA, SCENE_GORONSHOP, SCENE_KAJIYA, SCENE_FISHERMAN, SCENE_LABO, + SCENE_BANDROOM, SCENE_TOUGITES, SCENE_MUSICHOUSE, +}; +static std::unordered_map<RandoCheckId, std::string> checkLocationHints; + +std::string GetLocationNameForHint(RandoCheckId randoCheckId, bool exact) { + if (checkLocationHints.empty()) { + for (auto& [regionId, staticRegion] : Rando::Logic::Regions) { + SceneId sceneId = (SceneId)Play_GetOriginalSceneId(staticRegion.sceneId); + + // Handle grottos separately, their checks are assigned to the connected region + if (scenesToCheckParent.contains(sceneId)) { + RandoRegionId connectedRegionId = RR_MAX; + if (regionId == RR_LONE_PEAK_SHRINE) { + connectedRegionId = RR_LONE_PEAK_SHRINE; + } else { + for (auto& [entrance, _] : staticRegion.exits) { + connectedRegionId = Rando::Logic::GetRegionIdFromEntrance(entrance); + } + for (auto& [regionId, _] : staticRegion.connections) { + connectedRegionId = regionId; + } + if (connectedRegionId == RR_MAX) { + continue; + } + } + sceneId = Rando::Logic::Regions[connectedRegionId].sceneId; + } + for (auto& [regionRandoCheckId, _] : staticRegion.checks) { + checkLocationHints[regionRandoCheckId] = Ship_GetSceneName(sceneId); + // Remove some redundant info from the scene names to make hints cleaner + CustomMessage::Replace(&checkLocationHints[regionRandoCheckId], " (spring)", ""); + CustomMessage::Replace(&checkLocationHints[regionRandoCheckId], " (poison)", ""); + CustomMessage::Replace(&checkLocationHints[regionRandoCheckId], " (winter)", ""); + CustomMessage::Replace(&checkLocationHints[regionRandoCheckId], " (Clear)", ""); + CustomMessage::Replace(&checkLocationHints[regionRandoCheckId], " & Grottos", ""); + } + } + } + + if (randoCheckId == RC_UNKNOWN) { + return "in an Unknown Location"; + } + + auto& check = Checks[randoCheckId]; + + if (exact) { + return "at " + CheckNames[randoCheckId]; + } + + if (check.randoCheckType == RCTYPE_ENEMY_DROP) { + return "within a fomidable foe"; + } + + if (checkLocationHints.contains(randoCheckId)) { + return "in " + checkLocationHints[randoCheckId]; + } + + return "in an Unknown Location"; +} + } // namespace StaticData } // namespace Rando diff --git a/mm/2s2h/Rando/StaticData/StaticData.h b/mm/2s2h/Rando/StaticData/StaticData.h index 1bf25dec8..d67d88fb6 100644 --- a/mm/2s2h/Rando/StaticData/StaticData.h +++ b/mm/2s2h/Rando/StaticData/StaticData.h @@ -33,6 +33,7 @@ extern std::array<std::string, RC_MAX> CheckNames; RandoStaticCheck GetCheckFromFlag(FlagType flagType, s32 flag, s16 sceneId = SCENE_MAX); RandoCheckId GetCheckIdFromName(const char* name); void PopulateCheckNames(); +std::string GetLocationNameForHint(RandoCheckId randoCheckId, bool exact); struct RandoStaticItem { RandoItemId randoItemId; |
