From 35d17b8b0b5eaa6eb30e5c3ec16e68894dd40245 Mon Sep 17 00:00:00 2001 From: xxAtrain223 Date: Mon, 31 Mar 2025 00:18:42 -0500 Subject: [PATCH 1/4] Available Randomizer Checks (#5091) * Calculate accessible checks and show them in the check tracker. * Added added DoorUnlocked checks to the Fire Temple doors. This is needed when calculating Accessible checks while playing and using small keys. Otherwise checks will appear unaccessible because Keys have been used. * Changed RecalculateAccessibleChecks to use the logic ReachabilitySearch. * Reverted DoorUnlocked changes. * Added recalculate_accessible_checks debug console command. * Skip CanBuy check when calculating accessible checks, it spoils that some items can't be bought with the current wallet. * Set RAND_INF_ZELDAS_LETTER. * Don't add locations to the pool when calculating accessible checks. * Save and Load randomizer trick options. * Get the number of used small keys. * Added check if bean was planted. * Show number of Accessible checks in each Area. * Accounted for missing RG_POCKET_CUCCO in logic HasItem and ApplyItemEffect. * Show accessible checks via an icon. * Check shop price against current wallet. * Recalculate Accessible Checks in CheckTrackerLoadGame instead of LoadFile. * RecalculateAccessibleChecks on save. * Don't unset Zelda's Letter. * Added Only Show Available Checks checkbox. * Rename Accessible Checks to Available Checks. * Added option to Enable Available Checks. * Remove debug console to recalculate available checks, this can now be done by toggling Enable Available Checks in the settings. * Remove extra requiredTrial. * Default "Hide unshuffled shop item checks" unchecked. * Updated GetSmallKeyCount to return int8_t. * Default AmmoDrop to true until the setting is implemented. * Fixed Sleeping Waterfall timesaver. * Updated Only Show Available Checks to let Show Hidden Items reveal hidden available checks. * Revert "Don't unset Zelda's Letter." This reverts commit 4b5903940f18a20b69620f3741760fd19ac3876f. * Set RAND_INF_ZELDAS_LETTER. * Revert "Fixed Sleeping Waterfall timesaver." This reverts commit 9b62fd5297cf5bf778ba26bd593c4728c9038be8. * Added isMagicAcquired to Logic::HasItem. * GetDungeonSmallKeyDoors return static emptyVector. * Comment out AmmoCanDrop in ResetLogic. * Added location_access CanBuy todo. * Reverted Get and Set SmallKeyCount to use uint8_t and handled -1 keys. * Added todo for MQ Water GetUsedSmallKeyCount. --- .../Enhancements/debugger/debugSaveEditor.h | 3 + .../debugger/performanceTimer.cpp | 4 + .../Enhancements/debugger/performanceTimer.h | 2 + .../Enhancements/randomizer/3drando/fill.cpp | 20 +- .../Enhancements/randomizer/3drando/fill.hpp | 4 +- .../Enhancements/randomizer/hook_handlers.cpp | 9 + .../Enhancements/randomizer/item_location.cpp | 9 + .../Enhancements/randomizer/item_location.h | 3 + .../randomizer/location_access.cpp | 78 ++++++- .../Enhancements/randomizer/location_access.h | 2 +- soh/soh/Enhancements/randomizer/logic.cpp | 122 ++++++++++- soh/soh/Enhancements/randomizer/logic.h | 2 +- .../randomizer/randomizer_check_tracker.cpp | 194 ++++++++++++++++-- .../randomizer/randomizer_check_tracker.h | 1 + soh/soh/Enhancements/randomizer/savefile.cpp | 3 +- soh/soh/SaveManager.cpp | 12 +- 16 files changed, 432 insertions(+), 36 deletions(-) diff --git a/soh/soh/Enhancements/debugger/debugSaveEditor.h b/soh/soh/Enhancements/debugger/debugSaveEditor.h index b99931a6e..04e01917d 100644 --- a/soh/soh/Enhancements/debugger/debugSaveEditor.h +++ b/soh/soh/Enhancements/debugger/debugSaveEditor.h @@ -1164,6 +1164,9 @@ const std::vector flagTables = { { RAND_INF_COLOSSUS_GREAT_FAIRY_REWARD, "RAND_INF_COLOSSUS_GREAT_FAIRY_REWARD" }, { RAND_INF_OGC_GREAT_FAIRY_REWARD, "RAND_INF_OGC_GREAT_FAIRY_REWARD" }, + { RAND_INF_ZELDAS_LETTER, "RAND_INF_ZELDAS_LETTER" }, + { RAND_INF_WEIRD_EGG, "RAND_INF_WEIRD_EGG" }, + { RAND_INF_KF_SOUTH_GRASS_WEST_RUPEE, "RAND_INF_KF_SOUTH_GRASS_WEST_RUPEE" }, { RAND_INF_KF_NORTH_GRASS_WEST_RUPEE, "RAND_INF_KF_NORTH_GRASS_WEST_RUPEE" }, { RAND_INF_KF_NORTH_GRASS_EAST_RUPEE, "RAND_INF_KF_NORTH_GRASS_EAST_RUPEE" }, diff --git a/soh/soh/Enhancements/debugger/performanceTimer.cpp b/soh/soh/Enhancements/debugger/performanceTimer.cpp index 69ffc6925..54e1cb983 100644 --- a/soh/soh/Enhancements/debugger/performanceTimer.cpp +++ b/soh/soh/Enhancements/debugger/performanceTimer.cpp @@ -12,6 +12,10 @@ std::chrono::duration GetPerformanceTimer(TimerID timer){ return totalTimes[timer]; } +void ResetPerformanceTimer(TimerID timer) { + totalTimes[timer] = {}; +} + void ResetPerformanceTimers(){ totalTimes = {}; } \ No newline at end of file diff --git a/soh/soh/Enhancements/debugger/performanceTimer.h b/soh/soh/Enhancements/debugger/performanceTimer.h index f4bc8f017..b94ab5f6d 100644 --- a/soh/soh/Enhancements/debugger/performanceTimer.h +++ b/soh/soh/Enhancements/debugger/performanceTimer.h @@ -25,12 +25,14 @@ typedef enum { PT_TOD_ACCESS, PT_ENTRANCE_LOGIC, PT_LOCATION_LOGIC, + PT_RECALCULATE_AVAILABLE_CHECKS, PT_MAX } TimerID; void StartPerformanceTimer(TimerID timer); void StopPerformanceTimer(TimerID timer); std::chrono::duration GetPerformanceTimer(TimerID timer); +void ResetPerformanceTimer(TimerID timer); void ResetPerformanceTimers(); static std::array, PT_MAX> totalTimes = {}; static std::array timeStarted = {}; diff --git a/soh/soh/Enhancements/randomizer/3drando/fill.cpp b/soh/soh/Enhancements/randomizer/3drando/fill.cpp index 56675d41d..356bc507b 100644 --- a/soh/soh/Enhancements/randomizer/3drando/fill.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/fill.cpp @@ -402,7 +402,13 @@ bool AddCheckToLogic(LocationAccess& locPair, GetAccessibleLocationsStruct& gals Rando::ItemLocation* location = ctx->GetItemLocation(loc); RandomizerGet locItem = location->GetPlacedRandomizerGet(); - if (!location->IsAddedToPool() && locPair.ConditionsMet(parentRegion)) { + if (!location->IsAddedToPool() && locPair.ConditionsMet(parentRegion, gals.calculatingAvailableChecks)) { + if (gals.calculatingAvailableChecks) { + gals.accessibleLocations.push_back(loc); + StopPerformanceTimer(PT_LOCATION_LOGIC); + return false; + } + location->AddToPool(); if (locItem == RG_NONE) { @@ -498,19 +504,23 @@ void ProcessRegion(Region* region, GetAccessibleLocationsStruct& gals, Randomize } // Return any of the targetLocations that are accessible in logic -std::vector ReachabilitySearch(const std::vector& targetLocations, RandomizerGet ignore /* = RG_NONE*/) { +std::vector ReachabilitySearch(const std::vector& targetLocations, RandomizerGet ignore /* = RG_NONE*/, bool calculatingAvailableChecks /* = false */) { auto ctx = Rando::Context::GetInstance(); GetAccessibleLocationsStruct gals(0); - ResetLogic(ctx, gals, true); + gals.calculatingAvailableChecks = calculatingAvailableChecks; + ResetLogic(ctx, gals, !calculatingAvailableChecks); do { gals.InitLoop(); for (size_t i = 0; i < gals.regionPool.size(); i++) { ProcessRegion(RegionTable(gals.regionPool[i]), gals, ignore); } } while (gals.logicUpdated); - erase_if(gals.accessibleLocations, [&targetLocations, ctx](RandomizerCheck loc){ + erase_if(gals.accessibleLocations, [&targetLocations, ctx, calculatingAvailableChecks](RandomizerCheck loc) { + if (ctx->GetItemLocation(loc)->GetPlacedRandomizerGet() != RG_NONE && !calculatingAvailableChecks) { + return false; + } for (RandomizerCheck allowedLocation : targetLocations) { - if (loc == allowedLocation || ctx->GetItemLocation(loc)->GetPlacedRandomizerGet() != RG_NONE) { + if (loc == allowedLocation) { return false; } } diff --git a/soh/soh/Enhancements/randomizer/3drando/fill.hpp b/soh/soh/Enhancements/randomizer/3drando/fill.hpp index 106af7aab..bb013cc13 100644 --- a/soh/soh/Enhancements/randomizer/3drando/fill.hpp +++ b/soh/soh/Enhancements/randomizer/3drando/fill.hpp @@ -34,6 +34,8 @@ struct GetAccessibleLocationsStruct { std::vector itemSphere; std::list entranceSphere; + bool calculatingAvailableChecks = false; + GetAccessibleLocationsStruct(int _maxGsCount){ regionPool = {RR_ROOT}; gsCount = 0; @@ -62,7 +64,7 @@ std::vector GetEmptyLocations(std::vector allo void ProcessRegion(Region* region, GetAccessibleLocationsStruct& gals, RandomizerGet ignore = RG_NONE, bool stopOnBeatable = false, bool addToPlaythrough = false); -std::vector ReachabilitySearch(const std::vector& allowedLocations, RandomizerGet ignore=RG_NONE); +std::vector ReachabilitySearch(const std::vector& allowedLocations, RandomizerGet ignore=RG_NONE, bool calculatingAvailableChecks=false); void GeneratePlaythrough(); diff --git a/soh/soh/Enhancements/randomizer/hook_handlers.cpp b/soh/soh/Enhancements/randomizer/hook_handlers.cpp index c3500edc0..a3088aabc 100644 --- a/soh/soh/Enhancements/randomizer/hook_handlers.cpp +++ b/soh/soh/Enhancements/randomizer/hook_handlers.cpp @@ -236,6 +236,14 @@ void RandomizerOnFlagSetHandler(int16_t flagType, int16_t flag) { Flags_UnsetRandomizerInf(RAND_INF_CHILD_TRADES_HAS_CHICKEN); } + if (flagType == FLAG_EVENT_CHECK_INF && flag == EVENTCHKINF_OBTAINED_ZELDAS_LETTER) { + Flags_SetRandomizerInf(RAND_INF_ZELDAS_LETTER); + } + + if (flagType == FLAG_EVENT_CHECK_INF && flag == EVENTCHKINF_OBTAINED_POCKET_EGG) { + Flags_SetRandomizerInf(RAND_INF_WEIRD_EGG); + } + RandomizerCheck rc = GetRandomizerCheckFromFlag(flagType, flag); if (rc == RC_UNKNOWN_CHECK) return; @@ -351,6 +359,7 @@ void RandomizerOnItemReceiveHandler(GetItemEntry receivedItemEntry) { loc->SetCheckStatus(RCSHOW_COLLECTED); CheckTracker::SpoilAreaFromCheck(randomizerQueuedCheck); CheckTracker::RecalculateAllAreaTotals(); + CheckTracker::RecalculateAvailableChecks(); SaveManager::Instance->SaveSection(gSaveContext.fileNum, SECTION_ID_TRACKER_DATA, true); randomizerQueuedCheck = RC_UNKNOWN_CHECK; randomizerQueuedItemEntry = GET_ITEM_NONE; diff --git a/soh/soh/Enhancements/randomizer/item_location.cpp b/soh/soh/Enhancements/randomizer/item_location.cpp index 00e0366ab..cfdfb4204 100644 --- a/soh/soh/Enhancements/randomizer/item_location.cpp +++ b/soh/soh/Enhancements/randomizer/item_location.cpp @@ -228,5 +228,14 @@ void ItemLocation::ResetVariables() { areas = {}; status = RCSHOW_UNCHECKED; isSkipped = false; + isAvailable = false; +} + +bool ItemLocation::IsAvailable() const { + return isAvailable; +} + +void ItemLocation::SetAvailable(bool isAvailable_) { + isAvailable = isAvailable_; } } \ No newline at end of file diff --git a/soh/soh/Enhancements/randomizer/item_location.h b/soh/soh/Enhancements/randomizer/item_location.h index 142ac1c0b..e60d358eb 100644 --- a/soh/soh/Enhancements/randomizer/item_location.h +++ b/soh/soh/Enhancements/randomizer/item_location.h @@ -56,6 +56,8 @@ class ItemLocation { bool IsFoolishCandidate() const; void SetBarrenCandidate(); void ResetVariables(); + bool IsAvailable() const; + void SetAvailable(bool isAvailable_); private: RandomizerCheck rc; @@ -76,5 +78,6 @@ class ItemLocation { bool barrenCandidate = false; RandomizerCheckStatus status = RCSHOW_UNCHECKED; bool isSkipped = false; + bool isAvailable = false; }; } // namespace Rando \ No newline at end of file diff --git a/soh/soh/Enhancements/randomizer/location_access.cpp b/soh/soh/Enhancements/randomizer/location_access.cpp index 33abc3ce8..e2781e9fb 100644 --- a/soh/soh/Enhancements/randomizer/location_access.cpp +++ b/soh/soh/Enhancements/randomizer/location_access.cpp @@ -11,6 +11,11 @@ #include +extern "C" { + extern SaveContext gSaveContext; + extern PlayState* gPlayState; +} + //generic grotto event list std::vector grottoEvents; @@ -27,7 +32,7 @@ bool LocationAccess::CheckConditionAtAgeTime(bool& age, bool& time) const { return GetConditionsMet(); } -bool LocationAccess::ConditionsMet(Region* parentRegion) const { +bool LocationAccess::ConditionsMet(Region* parentRegion, bool calculatingAvailableChecks) const { //WARNING enterance validation can run this after resetting the access for sphere 0 validation //When refactoring ToD access, either fix the above or do not assume that we //have any access at all just because this is being run @@ -41,8 +46,8 @@ bool LocationAccess::ConditionsMet(Region* parentRegion) const { ) { conditionsMet = true; } - - return conditionsMet && CanBuy(); + + return conditionsMet && (calculatingAvailableChecks || CanBuy()); // TODO: run CanBuy when price is known due to settings } bool LocationAccess::CanBuy() const { @@ -224,8 +229,73 @@ bool MQSpiritSharedBrokenWallRoom(const RandomizerRegion region, ConditionFn con return areaTable[region].MQSpiritShared(condition, true, anyAge); } +bool BeanPlanted(const RandomizerRegion region) { + // swchFlag found using the Actor Viewer to get the Obj_Bean parameters & 0x3F + // not tested with multiple OTRs, but can be automated similarly to GetDungeonSmallKeyDoors + SceneID sceneID; + uint8_t swchFlag; + switch (region) { + case RR_ZORAS_RIVER: + sceneID = SceneID::SCENE_ZORAS_RIVER; + swchFlag = 3; + break; + case RR_THE_GRAVEYARD: + sceneID = SceneID::SCENE_GRAVEYARD; + swchFlag = 3; + break; + case RR_KOKIRI_FOREST: + sceneID = SceneID::SCENE_KOKIRI_FOREST; + swchFlag = 9; + break; + case RR_THE_LOST_WOODS: + sceneID = SceneID::SCENE_LOST_WOODS; + swchFlag = 4; + break; + case RR_LW_BEYOND_MIDO: + sceneID = SceneID::SCENE_LOST_WOODS; + swchFlag = 18; + break; + case RR_DEATH_MOUNTAIN_TRAIL: + sceneID = SceneID::SCENE_DEATH_MOUNTAIN_TRAIL; + swchFlag = 6; + break; + case RR_LAKE_HYLIA: + sceneID = SceneID::SCENE_LAKE_HYLIA; + swchFlag = 1; + break; + case RR_GERUDO_VALLEY: + sceneID = SceneID::SCENE_GERUDO_VALLEY; + swchFlag = 3; + break; + case RR_DMC_CENTRAL_LOCAL: + sceneID = SceneID::SCENE_DEATH_MOUNTAIN_CRATER; + swchFlag = 3; + break; + case RR_DESERT_COLOSSUS: + sceneID = SceneID::SCENE_DESERT_COLOSSUS; + swchFlag = 24; + break; + default: + sceneID = SCENE_ID_MAX; + swchFlag = 0; + break; + } + + // Get the swch value for the scene + uint32_t swch; + if (gPlayState != nullptr && gPlayState->sceneNum == sceneID) { + swch = gPlayState->actorCtx.flags.swch; + } else if (sceneID != SCENE_ID_MAX) { + swch = gSaveContext.sceneFlags[sceneID].swch; + } else { + swch = 0; + } + + return swch >> swchFlag & 1; +} + bool CanPlantBean(const RandomizerRegion region) { - return areaTable[region].CanPlantBeanCheck(); + return areaTable[region].CanPlantBeanCheck() || BeanPlanted(region); } bool BothAges(const RandomizerRegion region) { diff --git a/soh/soh/Enhancements/randomizer/location_access.h b/soh/soh/Enhancements/randomizer/location_access.h index 986d08d6a..ad7061963 100644 --- a/soh/soh/Enhancements/randomizer/location_access.h +++ b/soh/soh/Enhancements/randomizer/location_access.h @@ -75,7 +75,7 @@ class LocationAccess { bool CheckConditionAtAgeTime(bool& age, bool& time) const; - bool ConditionsMet(Region* parentRegion) const; + bool ConditionsMet(Region* parentRegion, bool calculatingAvailableChecks) const; RandomizerCheck GetLocation() const { return location; diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index 836a817c4..43ff3ce56 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -13,6 +13,11 @@ #include "macros.h" #include "variables.h" #include +#include "StringHelper.h" +#include "soh/resource/type/Scene.h" +#include "soh/resource/type/scenecommand/SetTransitionActorList.h" +#include "src/overlays/actors/ovl_En_Door/z_en_door.h" +#include "src/overlays/actors/ovl_Door_Shutter/z_door_shutter.h" namespace Rando { @@ -87,7 +92,7 @@ namespace Rando { case RG_BOMB_BAG: return CurrentUpgrade(UPG_BOMB_BAG); case RG_MAGIC_SINGLE: - return GetSaveContext()->magicLevel >= 1; + return GetSaveContext()->magicLevel >= 1 || GetSaveContext()->isMagicAcquired; // Songs case RG_ZELDAS_LULLABY: case RG_EPONAS_SONG: @@ -217,6 +222,7 @@ namespace Rando { case RG_GOLDEN_SCALE: return CurrentUpgrade(UPG_SCALE) >= 2; case RG_POCKET_EGG: + return CheckRandoInf(RAND_INF_ADULT_TRADES_HAS_POCKET_EGG); case RG_COJIRO: case RG_ODD_MUSHROOM: case RG_ODD_POTION: @@ -226,7 +232,7 @@ namespace Rando { case RG_EYEBALL_FROG: case RG_EYEDROPS: case RG_CLAIM_CHECK: - return CheckRandoInf(itemName - RG_POCKET_EGG + RAND_INF_ADULT_TRADES_HAS_POCKET_EGG); + return CheckRandoInf(itemName - RG_COJIRO + RAND_INF_ADULT_TRADES_HAS_COJIRO); case RG_BOTTLE_WITH_BIG_POE: case RG_BOTTLE_WITH_BLUE_FIRE: case RG_BOTTLE_WITH_BLUE_POTION: @@ -1509,6 +1515,8 @@ namespace Rando { mSaveContext->isDoubleDefenseAcquired = state; break; case RG_POCKET_EGG: + SetRandoInf(RAND_INF_ADULT_TRADES_HAS_POCKET_EGG, state); + break; case RG_COJIRO: case RG_ODD_MUSHROOM: case RG_ODD_POTION: @@ -1518,7 +1526,7 @@ namespace Rando { case RG_EYEBALL_FROG: case RG_EYEDROPS: case RG_CLAIM_CHECK: - SetRandoInf(randoGet - RG_POCKET_EGG + RAND_INF_ADULT_TRADES_HAS_POCKET_EGG, state); + SetRandoInf(randoGet - RG_COJIRO + RAND_INF_ADULT_TRADES_HAS_COJIRO, state); break; case RG_PROGRESSIVE_HOOKSHOT: { @@ -2096,8 +2104,111 @@ namespace Rando { } } + std::unordered_map SceneToDungeon = { + { SceneID::SCENE_DEKU_TREE, DungeonKey::DEKU_TREE }, + { SceneID::SCENE_DODONGOS_CAVERN, DungeonKey::DODONGOS_CAVERN }, + { SceneID::SCENE_JABU_JABU, DungeonKey::JABU_JABUS_BELLY }, + { SceneID::SCENE_FOREST_TEMPLE, DungeonKey::FOREST_TEMPLE }, + { SceneID::SCENE_FIRE_TEMPLE, DungeonKey::FIRE_TEMPLE }, + { SceneID::SCENE_WATER_TEMPLE, DungeonKey::WATER_TEMPLE }, + { SceneID::SCENE_SPIRIT_TEMPLE, DungeonKey::SPIRIT_TEMPLE }, + { SceneID::SCENE_SHADOW_TEMPLE, DungeonKey::SHADOW_TEMPLE }, + { SceneID::SCENE_BOTTOM_OF_THE_WELL, DungeonKey::BOTTOM_OF_THE_WELL }, + { SceneID::SCENE_ICE_CAVERN, DungeonKey::ICE_CAVERN }, + { SceneID::SCENE_GERUDO_TRAINING_GROUND, DungeonKey::GERUDO_TRAINING_GROUND }, + { SceneID::SCENE_INSIDE_GANONS_CASTLE, DungeonKey::GANONS_CASTLE }, + }; + + // Get the swch bit positions for the dungeon + const std::vector& GetDungeonSmallKeyDoors(SceneID sceneId) { + static const std::vector emptyVector; + auto foundDungeon = SceneToDungeon.find(static_cast(sceneId)); + if (foundDungeon == SceneToDungeon.end()) { + return emptyVector; + } + + bool masterQuest = Rando::Context::GetInstance()->GetDungeon(foundDungeon->second)->IsMQ(); + + // Create a unique key for the dungeon and master quest + uint8_t key = sceneId | (masterQuest << 7); + + static std::unordered_map> dungeonSmallKeyDoors; + auto foundEntry = dungeonSmallKeyDoors.find(key); + if (foundEntry != dungeonSmallKeyDoors.end()) { + return foundEntry->second; + } + dungeonSmallKeyDoors[key] = {}; + + // Get the scene path + SceneTableEntry* sceneTableEntry = &gSceneTable[sceneId]; + std::string scenePath = StringHelper::Sprintf("scenes/%s/%s/%s", masterQuest ? "mq" : "nonmq", + sceneTableEntry->sceneFile.fileName, sceneTableEntry->sceneFile.fileName); + + // Load the scene + std::shared_ptr scene = std::dynamic_pointer_cast( + Ship::Context::GetInstance()->GetResourceManager()->LoadResource(scenePath)); + if (scene == nullptr) { + return emptyVector; + } + + // Find the SetTransitionActorList command + std::shared_ptr transitionActorListCommand = nullptr; + for (auto& command : scene->commands) { + if (command->cmdId == SOH::SceneCommandID::SetTransitionActorList) { + transitionActorListCommand = std::dynamic_pointer_cast(command); + break; + } + } + if (transitionActorListCommand == nullptr) { + return emptyVector; + } + + // Find the bit position for the small key doors + for (auto& transitionActor : transitionActorListCommand->transitionActorList) { + if (transitionActor.id == ACTOR_EN_DOOR) { + uint8_t doorType = (transitionActor.params >> 7) & 7; + if (doorType == DOOR_LOCKED) { + dungeonSmallKeyDoors[key].emplace_back(transitionActor.params & 0x3F); + } + } else if (transitionActor.id == ACTOR_DOOR_SHUTTER) { + uint8_t doorType = (transitionActor.params >> 7) & 15; + if (doorType == SHUTTER_BACK_LOCKED || doorType == SHUTTER_BOSS || doorType == SHUTTER_KEY_LOCKED) { + dungeonSmallKeyDoors[key].emplace_back(transitionActor.params & 0x3F); + } + } + } + + return dungeonSmallKeyDoors[key]; + } + + int8_t GetUsedSmallKeyCount(SceneID sceneId) { + const auto& smallKeyDoors = GetDungeonSmallKeyDoors(sceneId); + + // Get the swch value for the scene + uint32_t swch; + if (gPlayState != nullptr && gPlayState->sceneNum == sceneId) { + swch = gPlayState->actorCtx.flags.swch; + } else { + swch = gSaveContext.sceneFlags[sceneId].swch; + } + + // Count the number of small keys doors unlocked + int8_t unlockedSmallKeyDoors = 0; + for (auto& smallKeyDoor : smallKeyDoors) { + unlockedSmallKeyDoors += swch >> smallKeyDoor & 1; + } + + // RANDOTODO: Account for MQ Water trick that causes the basement lock to unlock when the player clears the stalfos pit. + return unlockedSmallKeyDoors; + } + uint8_t Logic::GetSmallKeyCount(uint32_t dungeonIndex) { - return mSaveContext->inventory.dungeonKeys[dungeonIndex]; + int8_t dungeonKeys = mSaveContext->inventory.dungeonKeys[dungeonIndex]; + if (dungeonKeys == -1) { + // never got keys, so can't have used keys + return 0; + } + return dungeonKeys + GetUsedSmallKeyCount(SceneID(dungeonIndex)); } void Logic::SetSmallKeyCount(uint32_t dungeonIndex, uint8_t count) { @@ -2172,7 +2283,8 @@ namespace Rando { IsKeysanity = ctx->GetOption(RSK_KEYSANITY).Is(RO_DUNGEON_ITEM_LOC_ANYWHERE) || ctx->GetOption(RSK_KEYSANITY).Is(RO_DUNGEON_ITEM_LOC_ANYWHERE) || ctx->GetOption(RSK_KEYSANITY).Is(RO_DUNGEON_ITEM_LOC_ANYWHERE); - AmmoCanDrop = /*AmmoDrops.IsNot(AMMODROPS_NONE) TODO: AmmoDrop setting*/ true; + + //AmmoCanDrop = /*AmmoDrops.IsNot(AMMODROPS_NONE)*/ false; TODO: AmmoDrop setting //Child item logic SkullMask = false; diff --git a/soh/soh/Enhancements/randomizer/logic.h b/soh/soh/Enhancements/randomizer/logic.h index 2f68017e2..5b0922d6e 100644 --- a/soh/soh/Enhancements/randomizer/logic.h +++ b/soh/soh/Enhancements/randomizer/logic.h @@ -90,7 +90,7 @@ class Logic { bool FairyPot = false; bool FreeFairies = false; bool FairyPond = false; - bool AmmoCanDrop = false; + bool AmmoCanDrop = true; uint8_t PieceOfHeart = 0; uint8_t HeartContainer = 0; diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp index 80aeead5c..270bdbb7f 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp @@ -9,7 +9,10 @@ #include "soh/SohGui/UIWidgets.hpp" #include "soh/SohGui/SohGui.hpp" #include "dungeon.h" +#include "entrance.h" #include "location_access.h" +#include "3drando/fill.hpp" +#include "soh/Enhancements/debugger/performanceTimer.h" #include #include @@ -84,7 +87,7 @@ bool fishsanityAgeSplit; bool initialized; bool doAreaScroll; bool previousShowHidden = false; -bool hideShopUnshuffledChecks = true; +bool hideShopUnshuffledChecks = false; bool alwaysShowGS = false; std::map startingShopItem = { { SCENE_KOKIRI_SHOP, RC_KF_SHOP_ITEM_1 }, @@ -132,8 +135,10 @@ bool areasFullyChecked[RCAREA_INVALID]; u32 areasSpoiled = 0; bool showVOrMQ; s8 areaChecksGotten[RCAREA_INVALID]; //| "Kokiri Forest (4/9)" +s8 areaChecksAvailable[RCAREA_INVALID]; s8 areaCheckTotals[RCAREA_INVALID]; uint16_t totalChecks = 0; +uint16_t totalChecksAvailable = 0; uint16_t totalChecksGotten = 0; bool optCollapseAll; // A bool that will collapse all checks once bool optExpandAll; // A bool that will expand all checks once @@ -166,6 +171,8 @@ bool hideCollected = false; bool showHidden = true; bool mystery = false; bool showLogicTooltip = false; +bool enableAvailableChecks = false; +bool onlyShowAvailable = false; SceneID DungeonSceneLookupByArea(RandomizerCheckArea area) { switch (area) { @@ -235,10 +242,12 @@ void TrySetAreas() { void CalculateTotals() { totalChecks = 0; + totalChecksAvailable = 0; totalChecksGotten = 0; for (uint8_t i = 0; i < RCAREA_INVALID; i++) { totalChecks += areaCheckTotals[i]; + totalChecksAvailable += areaChecksAvailable[i]; totalChecksGotten += areaChecksGotten[i]; } } @@ -251,17 +260,43 @@ uint16_t GetTotalChecksGotten() { return totalChecksGotten; } +bool IsCheckHidden(RandomizerCheck rc) { + Rando::ItemLocation* itemLocation = OTRGlobals::Instance->gRandoContext->GetItemLocation(rc); + RandomizerCheckStatus status = itemLocation->GetCheckStatus(); + bool available = itemLocation->IsAvailable(); + bool skipped = itemLocation->GetIsSkipped(); + bool obtained = itemLocation->HasObtained(); + bool seen = status == RCSHOW_SEEN || status == RCSHOW_IDENTIFIED; + bool scummed = status == RCSHOW_SCUMMED; + bool unchecked = status == RCSHOW_UNCHECKED; + + return !showHidden && ( + (skipped && hideSkipped) || + (seen && hideSeen) || + (scummed && hideScummed) || + (unchecked && hideUnchecked) + ); +} + void RecalculateAreaTotals(RandomizerCheckArea rcArea) { areaChecksGotten[rcArea] = 0; + areaChecksAvailable[rcArea] = 0; areaCheckTotals[rcArea] = 0; for (auto rc : checksByArea.at(rcArea)) { if (!IsVisibleInCheckTracker(rc)) { continue; } areaCheckTotals[rcArea]++; - if (OTRGlobals::Instance->gRandoContext->GetItemLocation(rc)->GetIsSkipped() || OTRGlobals::Instance->gRandoContext->GetItemLocation(rc)->HasObtained()) { + + Rando::ItemLocation* itemLoc = OTRGlobals::Instance->gRandoContext->GetItemLocation(rc); + + if (itemLoc->GetIsSkipped() || itemLoc->HasObtained()) { areaChecksGotten[rcArea]++; } + + if (itemLoc->IsAvailable() && !IsCheckHidden(rc)) { + areaChecksAvailable[rcArea]++; + } } CalculateTotals(); } @@ -308,6 +343,7 @@ void SetCheckCollected(RandomizerCheck rc) { if (IsVisibleInCheckTracker(rc)) { if (!OTRGlobals::Instance->gRandoContext->GetItemLocation(rc)->GetIsSkipped()) { areaChecksGotten[loc->GetArea()]++; + areaChecksAvailable[loc->GetArea()]--; } else { OTRGlobals::Instance->gRandoContext->GetItemLocation(rc)->SetIsSkipped(false); } @@ -424,10 +460,12 @@ void ClearAreaChecksAndTotals() { for (auto& [rcArea, vec] : checksByArea) { vec.clear(); areaChecksGotten[rcArea] = 0; + areaChecksAvailable[rcArea] = 0; areaCheckTotals[rcArea] = 0; } totalChecks = 0; totalChecksGotten = 0; + totalChecksAvailable = 0; } void SetShopSeen(uint32_t sceneNum, bool prices) { @@ -469,6 +507,9 @@ void CheckTrackerLoadGame(int32_t fileNum) { if (loc->GetCheckStatus() == RCSHOW_SAVED || loc->GetIsSkipped()) { areaChecksGotten[entry2->GetArea()]++; } + if (loc->IsAvailable()) { + areaChecksAvailable[entry2->GetArea()]++; + } } if (areaChecksGotten[entry2->GetArea()] != 0 || RandomizerCheckObjects::AreaIsOverworld(entry2->GetArea()) || @@ -524,6 +565,7 @@ void CheckTrackerLoadGame(int32_t fileNum) { UpdateAllOrdering(); UpdateInventoryChecks(); UpdateFilters(); + RecalculateAvailableChecks(); } void CheckTrackerShopSlotChange(uint8_t cursorSlot, int16_t basePrice) { @@ -539,6 +581,7 @@ void CheckTrackerShopSlotChange(uint8_t cursorSlot, int16_t basePrice) { if (status == RCSHOW_SEEN) { OTRGlobals::Instance->gRandoContext->GetItemLocation(slot)->SetCheckStatus(RCSHOW_IDENTIFIED); SaveManager::Instance->SaveSection(gSaveContext.fileNum, sectionId, true); + RecalculateAvailableChecks(); } } @@ -812,6 +855,9 @@ void SaveTrackerData(SaveContext* saveContext, int sectionID, bool fullSave) { void SaveFile(SaveContext* saveContext, int sectionID, bool fullSave) { SaveTrackerData(saveContext, sectionID, fullSave); + if (fullSave) { + RecalculateAvailableChecks(); + } } void LoadFile() { @@ -882,6 +928,8 @@ void CheckTrackerWindow::DrawElement() { showHidden = CVarGetInteger(CVAR_TRACKER_CHECK("ShowHidden"), 0); mystery = CVarGetInteger(CVAR_RANDOMIZER_ENHANCEMENT("MysteriousShuffle"), 0); showLogicTooltip = CVarGetInteger(CVAR_TRACKER_CHECK("ShowLogic"), 0); + enableAvailableChecks = CVarGetInteger(CVAR_TRACKER_CHECK("EnableAvailableChecks"), 0); + onlyShowAvailable = CVarGetInteger(CVAR_TRACKER_CHECK("OnlyShowAvailable"), 0); hideShopUnshuffledChecks = CVarGetInteger(CVAR_TRACKER_CHECK("HideUnshuffledShopChecks"), 0); alwaysShowGS = CVarGetInteger(CVAR_TRACKER_CHECK("AlwaysShowGSLocs"), 0); @@ -932,9 +980,21 @@ void CheckTrackerWindow::DrawElement() { ImGui::TableNextRow(0, headerHeight); ImGui::TableNextColumn(); - UIWidgets::CVarCheckbox( - "Show Hidden Items", CVAR_TRACKER_CHECK("ShowHidden"), UIWidgets::CheckboxOptions({{ .tooltip = "When active, items will show hidden checks by default when updated to this state." }}) - .Color(THEME_COLOR)); + if (UIWidgets::CVarCheckbox( + "Show Hidden Items", CVAR_TRACKER_CHECK("ShowHidden"), UIWidgets::CheckboxOptions({{.tooltip = "When active, items will show hidden checks by default when updated to this state." }}) + .Color(THEME_COLOR))) { + doAreaScroll = true; + showHidden = CVarGetInteger(CVAR_TRACKER_CHECK("ShowHidden"), 0); + RecalculateAllAreaTotals(); + } + if (enableAvailableChecks) { + if (UIWidgets::CVarCheckbox( + "Only Show Available Checks", CVAR_TRACKER_CHECK("OnlyShowAvailable"), UIWidgets::CheckboxOptions({{ .tooltip = "When active, unavailable checks will be hidden." }}) + .Color(THEME_COLOR))) { + doAreaScroll = true; + RecalculateAllAreaTotals(); + } + } UIWidgets::PaddedSeparator(); if (UIWidgets::Button("Expand All", UIWidgets::ButtonOptions().Color(THEME_COLOR).Size(UIWidgets::Sizes::Inline))) { optCollapseAll = false; @@ -960,7 +1020,13 @@ void CheckTrackerWindow::DrawElement() { ImGui::Separator(); - ImGui::Text("Total Checks: %d / %d", totalChecksGotten, totalChecks); + std::ostringstream totalChecksSS; + totalChecksSS << "Total Checks: "; + if (enableAvailableChecks) { + totalChecksSS << totalChecksAvailable << " Available / "; + } + totalChecksSS << totalChecksGotten << " Checked / " << totalChecks << " Total"; + ImGui::Text(totalChecksSS.str().c_str()); UIWidgets::PaddedSeparator(); @@ -1012,7 +1078,8 @@ void CheckTrackerWindow::DrawElement() { doAreaScroll = true; } if ((shouldHideFilteredAreas && filterAreasHidden[rcArea]) || - (!showHidden && ((hideComplete && thisAreaFullyChecked) || (hideIncomplete && !thisAreaFullyChecked))) + (!showHidden && ((hideComplete && thisAreaFullyChecked) || (hideIncomplete && !thisAreaFullyChecked))) || + (enableAvailableChecks && onlyShowAvailable && areaChecksAvailable[rcArea] == 0) ) { doDraw = false; } else { @@ -1051,14 +1118,27 @@ void CheckTrackerWindow::DrawElement() { isThisAreaSpoiled = IsAreaSpoiled(rcArea) || mqSpoilers; if (isThisAreaSpoiled) { - if (showVOrMQ && RandomizerCheckObjects::AreaIsDungeon(rcArea)) { - if (OTRGlobals::Instance->gRandoContext->GetDungeons()->GetDungeonFromScene(DungeonSceneLookupByArea(rcArea))->IsMQ()) - ImGui::Text("(%d/%d) - MQ", areaChecksGotten[rcArea], areaCheckTotals[rcArea]); - else - ImGui::Text("(%d/%d) - Vanilla", areaChecksGotten[rcArea], areaCheckTotals[rcArea]); - } else { - ImGui::Text("(%d/%d)", areaChecksGotten[rcArea], areaCheckTotals[rcArea]); + std::ostringstream areaTotalsSS; + std::ostringstream areaTotalsTooltipSS; + + areaTotalsSS << "("; + if (enableAvailableChecks) { + areaTotalsSS << static_cast(areaChecksAvailable[rcArea]) << " / "; + areaTotalsTooltipSS << "Available / "; } + areaTotalsSS << static_cast(areaChecksGotten[rcArea]) << " / " << static_cast(areaCheckTotals[rcArea]) << ")"; + areaTotalsTooltipSS << "Checked / Total"; + + if (showVOrMQ && RandomizerCheckObjects::AreaIsDungeon(rcArea)) { + if (OTRGlobals::Instance->gRandoContext->GetDungeons()->GetDungeonFromScene(DungeonSceneLookupByArea(rcArea))->IsMQ()) { + areaTotalsSS << " - MQ"; + } else { + areaTotalsSS << " - Vanilla"; + } + } + + ImGui::Text(areaTotalsSS.str().c_str()); + UIWidgets::Tooltip(areaTotalsTooltipSS.str().c_str()); } else { ImGui::Text("???"); } @@ -1561,6 +1641,12 @@ void DrawLocation(RandomizerCheck rc) { Rando::ItemLocation* itemLoc = OTRGlobals::Instance->gRandoContext->GetItemLocation(rc); RandomizerCheckStatus status = itemLoc->GetCheckStatus(); bool skipped = itemLoc->GetIsSkipped(); + bool available = itemLoc->IsAvailable(); + + if (enableAvailableChecks && onlyShowAvailable && !available) { + return; + } + if (status == RCSHOW_COLLECTED) { if (!showHidden && hideCollected) { return; @@ -1637,10 +1723,18 @@ void DrawLocation(RandomizerCheck rc) { OTRGlobals::Instance->gRandoContext->GetItemLocation(rc)->SetIsSkipped(false); areaChecksGotten[loc->GetArea()]--; totalChecksGotten--; + if (available) { + areaChecksAvailable[loc->GetArea()]++; + totalChecksAvailable++; + } } else { OTRGlobals::Instance->gRandoContext->GetItemLocation(rc)->SetIsSkipped(true); areaChecksGotten[loc->GetArea()]++; totalChecksGotten++; + if (available) { + areaChecksAvailable[loc->GetArea()]--; + totalChecksAvailable--; + } } UpdateOrdering(loc->GetArea()); UpdateInventoryChecks(); @@ -1654,7 +1748,19 @@ void DrawLocation(RandomizerCheck rc) { ImGui::SameLine(); //Draw - ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(mainColor.r / 255.0f, mainColor.g / 255.0f, mainColor.b / 255.0f, mainColor.a / 255.0f)); + ImVec4 styleColor(mainColor.r / 255.0f, mainColor.g / 255.0f, mainColor.b / 255.0f, mainColor.a / 255.0f); + if (enableAvailableChecks) { + if (itemLoc->HasObtained()) { + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0, 0, 0, 0)); + } else { + ImGui::PushStyleColor(ImGuiCol_Text, styleColor); + } + ImGui::Text("%s", available ? ICON_FA_UNLOCK : ICON_FA_LOCK); + ImGui::PopStyleColor(); + ImGui::SameLine(); + } + + ImGui::PushStyleColor(ImGuiCol_Text, styleColor); ImGui::Text("%s", txt.c_str()); ImGui::PopStyleColor(); @@ -1726,7 +1832,7 @@ void DrawLocation(RandomizerCheck rc) { if (conditionStr != "true") { UIWidgets::Tooltip(conditionStr.c_str()); } - return; + break; } } } @@ -1804,6 +1910,55 @@ void ImGuiDrawTwoColorPickerSection(const char* text, const char* cvarMainName, UIWidgets::PopStyleCombobox(); } +void RecalculateAvailableChecks() { + if (!enableAvailableChecks) { + return; + } + + ResetPerformanceTimer(PT_RECALCULATE_AVAILABLE_CHECKS); + StartPerformanceTimer(PT_RECALCULATE_AVAILABLE_CHECKS); + + std::vector targetLocations; + targetLocations.reserve(RR_MAX); + for (auto& location : Rando::StaticData::GetLocationTable()) { + RandomizerCheck rc = location.GetRandomizerCheck(); + Rando::ItemLocation* itemLocation = OTRGlobals::Instance->gRandoContext->GetItemLocation(rc); + itemLocation->SetAvailable(false); + if (!itemLocation->HasObtained()) { + targetLocations.emplace_back(rc); + } + } + + std::vector availableChecks = ReachabilitySearch(targetLocations, RG_NONE, true); + for (auto& rc : availableChecks) { + const auto& location = Rando::StaticData::GetLocation(rc); + const auto& itemLocation = OTRGlobals::Instance->gRandoContext->GetItemLocation(rc); + if (location->GetRCType() == RCTYPE_SHOP && itemLocation->GetCheckStatus() == RCSHOW_IDENTIFIED) { + if (CanBuyAnother(rc)) { + itemLocation->SetAvailable(true); + } + } else { + itemLocation->SetAvailable(true); + } + } + + totalChecksAvailable = 0; + for (auto& [rcArea, vec] : checksByArea) { + areaChecksAvailable[rcArea] = 0; + for (auto& rc : vec) { + Rando::ItemLocation* itemLocation = OTRGlobals::Instance->gRandoContext->GetItemLocation(rc); + if (itemLocation->IsAvailable() && IsVisibleInCheckTracker(rc) && !IsCheckHidden(rc)) { + areaChecksAvailable[rcArea]++; + } + } + totalChecksAvailable += areaChecksAvailable[rcArea]; + } + + StopPerformanceTimer(PT_RECALCULATE_AVAILABLE_CHECKS); + SPDLOG_INFO("Recalculate Available Checks Time: {}ms", GetPerformanceTimer(PT_RECALCULATE_AVAILABLE_CHECKS).count()); +} + + void CheckTrackerWindow::Draw() { if (!IsVisible()) { return; @@ -1858,7 +2013,7 @@ void CheckTrackerSettingsWindow::DrawElement() { .Tooltip("If enabled, Vanilla/MQ dungeons will show on the tracker immediately. Otherwise, Vanilla/MQ dungeon locations must be unlocked.").Color(THEME_COLOR)); if (UIWidgets::CVarCheckbox("Hide unshuffled shop item checks", CVAR_TRACKER_CHECK("HideUnshuffledShopChecks"), UIWidgets::CheckboxOptions().Tooltip("If enabled, will prevent the tracker from displaying slots with non-shop-item shuffles.").Color(THEME_COLOR))) { - hideShopUnshuffledChecks = !hideShopUnshuffledChecks; + hideShopUnshuffledChecks = CVarGetInteger(CVAR_TRACKER_CHECK("HideUnshuffledShopChecks"), 0); UpdateFilters(); } if (UIWidgets::CVarCheckbox("Always show gold skulltulas", CVAR_TRACKER_CHECK("AlwaysShowGSLocs"), @@ -1868,6 +2023,11 @@ void CheckTrackerSettingsWindow::DrawElement() { } UIWidgets::CVarCheckbox("Show Logic", CVAR_TRACKER_CHECK("ShowLogic"), UIWidgets::CheckboxOptions().Tooltip("If enabled, will show a check's logic when hovering over it.").Color(THEME_COLOR)); + if (UIWidgets::CVarCheckbox("Enable Available Checks", CVAR_TRACKER_CHECK("EnableAvailableChecks"), + UIWidgets::CheckboxOptions().Tooltip("If enabled, will show the checks that are available to be collected with your current progress.").Color(THEME_COLOR))) { + enableAvailableChecks = CVarGetInteger(CVAR_TRACKER_CHECK("EnableAvailableChecks"), 0); + RecalculateAvailableChecks(); + } // Filtering settings UIWidgets::PaddedSeparator(); diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.h b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.h index c5bcaa074..962da88e3 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.h +++ b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.h @@ -61,4 +61,5 @@ void UpdateAllOrdering(); void UpdateAllAreas(); void RecalculateAllAreaTotals(); void SpoilAreaFromCheck(RandomizerCheck rc); +void RecalculateAvailableChecks(); } // namespace CheckTracker diff --git a/soh/soh/Enhancements/randomizer/savefile.cpp b/soh/soh/Enhancements/randomizer/savefile.cpp index f43fecf9e..049b87f65 100644 --- a/soh/soh/Enhancements/randomizer/savefile.cpp +++ b/soh/soh/Enhancements/randomizer/savefile.cpp @@ -306,11 +306,13 @@ extern "C" void Randomizer_InitSaveFile() { // Malon/Talon back at ranch. Flags_SetEventChkInf(EVENTCHKINF_OBTAINED_POCKET_EGG); + Flags_SetRandomizerInf(RAND_INF_WEIRD_EGG); Flags_SetEventChkInf(EVENTCHKINF_TALON_WOKEN_IN_CASTLE); Flags_SetEventChkInf(EVENTCHKINF_TALON_RETURNED_FROM_CASTLE); // Set "Got Zelda's Letter" flag. Also ensures Saria is back at SFM. Flags_SetEventChkInf(EVENTCHKINF_OBTAINED_ZELDAS_LETTER); + Flags_SetRandomizerInf(RAND_INF_ZELDAS_LETTER); Flags_SetRandomizerInf(RAND_INF_CHILD_TRADES_HAS_LETTER_ZELDA); // Got item from Impa. @@ -321,7 +323,6 @@ extern "C" void Randomizer_InitSaveFile() { // Set this at the end to ensure we always start with the letter. // This is for the off chance, we got the Weird Egg from Impa (which should never happen). INV_CONTENT(ITEM_LETTER_ZELDA) = ITEM_LETTER_ZELDA; - Flags_SetRandomizerInf(RAND_INF_CHILD_TRADES_HAS_LETTER_ZELDA); } if (Randomizer_GetSettingValue(RSK_SHUFFLE_MASTER_SWORD) && startingAge == RO_AGE_ADULT) { diff --git a/soh/soh/SaveManager.cpp b/soh/soh/SaveManager.cpp index 6bde466ea..06a986a20 100644 --- a/soh/soh/SaveManager.cpp +++ b/soh/soh/SaveManager.cpp @@ -459,10 +459,16 @@ void SaveManager::LoadRandomizerVersion3() { }); randoContext->GetTrials()->SkipAll(); - SaveManager::Instance->LoadArray("requiredTrials", randoContext->GetOption(RSK_TRIAL_COUNT).Get() + 1, [&](size_t i) { + SaveManager::Instance->LoadArray("requiredTrials", randoContext->GetOption(RSK_TRIAL_COUNT).Get(), [&](size_t i) { size_t trialId; SaveManager::Instance->LoadData("", trialId); randoContext->GetTrial(trialId)->SetAsRequired(); + }); + + SaveManager::Instance->LoadArray("trickOptions", RT_MAX, [&](size_t i) { + uint8_t value = 0; + SaveManager::Instance->LoadData("", value); + randoContext->GetTrickOption(RandomizerTrick(i)).Set(value); }); } @@ -596,6 +602,10 @@ void SaveManager::SaveRandomizer(SaveContext* saveContext, int sectionID, bool f SaveManager::Instance->SaveData("", i); } }); + + SaveManager::Instance->SaveArray("trickOptions", RT_MAX, [&](size_t i) { + SaveManager::Instance->SaveData("", randoContext->GetTrickOption(RandomizerTrick(i)).Get()); + }); } // Init() here is an extension of InitSram, and thus not truly an initializer for SaveManager itself. don't put any class initialization stuff here From eb17f2e99d67d72ad51e59c2dffc292363aa80e5 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Mon, 31 Mar 2025 20:15:41 +0100 Subject: [PATCH 2/4] Lock mask select behind zelda's letter in rando (#5267) * Lock mask select behind letter in rando * change to rand inf --- soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_item.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_item.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_item.c index 9096271c1..25f0b3632 100644 --- a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_item.c +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_item.c @@ -317,7 +317,8 @@ void KaleidoScope_HandleItemCycleExtras(PlayState* play, u8 slot, bool canCycle, bool CanMaskSelect() { if (IS_RANDO) { - return CVarGetInteger(CVAR_ENHANCEMENT("MaskSelect"), 0) /* || Randomizer_GetSettingValue(RSK_SHUFFLE_CHILD_TRADE) */; + return CVarGetInteger(CVAR_ENHANCEMENT("MaskSelect"), 0) + && Flags_GetRandomizerInf(RAND_INF_ZELDAS_LETTER);/* || Randomizer_GetSettingValue(RSK_SHUFFLE_CHILD_TRADE) */ } // only allow mask select when: From 05219cbbf78bfbe77ccb4128de49f6beec858c38 Mon Sep 17 00:00:00 2001 From: Christopher Leggett Date: Mon, 31 Mar 2025 15:28:48 -0400 Subject: [PATCH 3/4] Prevent GameplayStats and TimeDisplay from getting reset on ship json drag and drop (#5269) --- CMake/soh-cvars.cmake | 4 +++ .../Enhancements/TimeDisplay/TimeDisplay.cpp | 12 ++++---- soh/soh/Enhancements/gameplaystats.cpp | 28 +++++++++---------- soh/soh/Enhancements/gameplaystats.h | 2 +- soh/soh/SohGui/SohMenuEnhancements.cpp | 4 +-- soh/soh/config/ConfigMigrators.h | 8 ++---- soh/soh/cvar_prefixes.h | 4 ++- soh/src/code/z_parameter.c | 2 +- 8 files changed, 33 insertions(+), 31 deletions(-) diff --git a/CMake/soh-cvars.cmake b/CMake/soh-cvars.cmake index e5ff8a384..227cfb2bb 100644 --- a/CMake/soh-cvars.cmake +++ b/CMake/soh-cvars.cmake @@ -10,6 +10,8 @@ set(CVAR_PREFIX_TRACKER "gTrackers") set(CVAR_PREFIX_DEVELOPER_TOOLS "gDeveloperTools") set(CVAR_PREFIX_GENERAL "gGeneral") set(CVAR_PREFIX_REMOTE "gRemote") +set(CVAR_PREFIX_GAMEPLAY_STATS "gGameplayStats") +set(CVAR_PREFIX_TIME_DISPLAY "gTimeDisplay") add_compile_definitions( CVAR_PREFIX_RANDOMIZER_ENHANCEMENT="${CVAR_PREFIX_RANDOMIZER_ENHANCEMENT}" CVAR_PREFIX_RANDOMIZER_SETTING="${CVAR_PREFIX_RANDOMIZER_SETTING}" @@ -23,4 +25,6 @@ add_compile_definitions( CVAR_PREFIX_DEVELOPER_TOOLS="${CVAR_PREFIX_DEVELOPER_TOOLS}" CVAR_PREFIX_GENERAL="${CVAR_PREFIX_GENERAL}" CVAR_PREFIX_REMOTE="${CVAR_PREFIX_REMOTE}" + CVAR_PREFIX_GAMEPLAY_STATS="${CVAR_PREFIX_GAMEPLAY_STATS}" + CVAR_PREFIX_TIME_DISPLAY="${CVAR_PREFIX_TIME_DISPLAY}" ) \ No newline at end of file diff --git a/soh/soh/Enhancements/TimeDisplay/TimeDisplay.cpp b/soh/soh/Enhancements/TimeDisplay/TimeDisplay.cpp index 80d048d68..4b374ed8e 100644 --- a/soh/soh/Enhancements/TimeDisplay/TimeDisplay.cpp +++ b/soh/soh/Enhancements/TimeDisplay/TimeDisplay.cpp @@ -37,10 +37,10 @@ const static std::vector> digitList = { }; const std::vector timeDisplayList = { - { DISPLAY_IN_GAME_TIMER, "Display Gameplay Timer", CVAR_ENHANCEMENT("TimeDisplay.Timers.InGameTimer") }, - { DISPLAY_TIME_OF_DAY, "Display Time of Day", CVAR_ENHANCEMENT("TimeDisplay.Timers.TimeofDay") }, - { DISPLAY_CONDITIONAL_TIMER, "Display Conditional Timer", CVAR_ENHANCEMENT("TimeDisplay.Timers.HotWater") }, - { DISPLAY_NAVI_TIMER, "Display Navi Timer", CVAR_ENHANCEMENT("TimeDisplay.Timers.NaviTimer") } + { DISPLAY_IN_GAME_TIMER, "Display Gameplay Timer", CVAR_TIME_DISPLAY("Timers.InGameTimer") }, + { DISPLAY_TIME_OF_DAY, "Display Time of Day", CVAR_TIME_DISPLAY("Timers.TimeofDay") }, + { DISPLAY_CONDITIONAL_TIMER, "Display Conditional Timer", CVAR_TIME_DISPLAY("Timers.HotWater") }, + { DISPLAY_NAVI_TIMER, "Display Navi Timer", CVAR_TIME_DISPLAY("Timers.NaviTimer") } }; static std::vector activeTimers; @@ -227,11 +227,11 @@ void TimeDisplayWindow::Draw() { } void TimeDisplayInitSettings() { - fontScale = CVarGetFloat(CVAR_ENHANCEMENT("TimeDisplay.FontScale"), 1.0f); + fontScale = CVarGetFloat(CVAR_TIME_DISPLAY("FontScale"), 1.0f); if (fontScale < 1.0f) { fontScale = 1.0f; } - if (CVarGetInteger(CVAR_ENHANCEMENT("TimeDisplay.ShowWindowBG"), 0)) { + if (CVarGetInteger(CVAR_TIME_DISPLAY("ShowWindowBG"), 0)) { windowBG = ImVec4(0, 0, 0, 0); } else { windowBG = ImVec4(0, 0, 0, 0.5f); diff --git a/soh/soh/Enhancements/gameplaystats.cpp b/soh/soh/Enhancements/gameplaystats.cpp index e939fa18c..347bfe982 100644 --- a/soh/soh/Enhancements/gameplaystats.cpp +++ b/soh/soh/Enhancements/gameplaystats.cpp @@ -391,7 +391,7 @@ void GameplayStatsRow(const char* label, const std::string& value, ImVec4 color } bool compareTimestampInfoByTime(const TimestampInfo& a, const TimestampInfo& b) { - return CVarGetInteger(CVAR_ENHANCEMENT("GameplayStats.ReverseTimestamps"), 0) ? a.time > b.time : a.time < b.time; + return CVarGetInteger(CVAR_GAMEPLAY_STATS("ReverseTimestamps"), 0) ? a.time > b.time : a.time < b.time; } const char* ResolveSceneID(int sceneID, int roomID){ @@ -452,13 +452,13 @@ void DrawGameplayStatsHeader() { } else { GameplayStatsRow("Total Game Time:", formatTimestampGameplayStat(GAMEPLAYSTAT_TOTAL_TIME), gSaveContext.ship.stats.gameComplete ? COLOR_GREEN : COLOR_WHITE); } - if (CVarGetInteger(CVAR_ENHANCEMENT("GameplayStats.ShowAdditionalTimers"), 0)) { // !Only display total game time + if (CVarGetInteger(CVAR_GAMEPLAY_STATS("ShowAdditionalTimers"), 0)) { // !Only display total game time GameplayStatsRow("Gameplay Time:", formatTimestampGameplayStat(gSaveContext.ship.stats.playTimer / 2), COLOR_GREY); GameplayStatsRow("Pause Menu Time:", formatTimestampGameplayStat(gSaveContext.ship.stats.pauseTimer / 3), COLOR_GREY); GameplayStatsRow("Time in scene:", formatTimestampGameplayStat(gSaveContext.ship.stats.sceneTimer / 2), COLOR_LIGHT_BLUE); GameplayStatsRow("Time in room:", formatTimestampGameplayStat(gSaveContext.ship.stats.roomTimer / 2), COLOR_LIGHT_BLUE); } - if (gPlayState != NULL && CVarGetInteger(CVAR_ENHANCEMENT("GameplayStats.ShowDebugInfo"), 0)) { // && display debug info + if (gPlayState != NULL && CVarGetInteger(CVAR_GAMEPLAY_STATS("ShowDebugInfo"), 0)) { // && display debug info GameplayStatsRow("play->sceneNum:", formatHexGameplayStat(gPlayState->sceneNum), COLOR_YELLOW); GameplayStatsRow("gSaveContext.entranceIndex:", formatHexGameplayStat(gSaveContext.entranceIndex), COLOR_YELLOW); GameplayStatsRow("gSaveContext.cutsceneIndex:", formatHexOnlyGameplayStat(gSaveContext.cutsceneIndex), COLOR_YELLOW); @@ -576,13 +576,13 @@ void DrawGameplayStatsBreakdownTab() { for (int i = 0; i < gSaveContext.ship.stats.tsIdx; i++) { std::string sceneName = ResolveSceneID(gSaveContext.ship.stats.sceneTimestamps[i].scene, gSaveContext.ship.stats.sceneTimestamps[i].room); std::string name; - if (CVarGetInteger(CVAR_ENHANCEMENT("GameplayStats.RoomBreakdown"), 0) && gSaveContext.ship.stats.sceneTimestamps[i].scene != SCENE_GROTTOS) { + if (CVarGetInteger(CVAR_GAMEPLAY_STATS("RoomBreakdown"), 0) && gSaveContext.ship.stats.sceneTimestamps[i].scene != SCENE_GROTTOS) { name = fmt::format("{:s} Room {:d}", sceneName, gSaveContext.ship.stats.sceneTimestamps[i].room); } else { name = sceneName; } strcpy(sceneTimestampDisplay[i].name, name.c_str()); - sceneTimestampDisplay[i].time = CVarGetInteger(CVAR_ENHANCEMENT("GameplayStats.RoomBreakdown"), 0) ? + sceneTimestampDisplay[i].time = CVarGetInteger(CVAR_GAMEPLAY_STATS("RoomBreakdown"), 0) ? gSaveContext.ship.stats.sceneTimestamps[i].roomTime : gSaveContext.ship.stats.sceneTimestamps[i].sceneTime; sceneTimestampDisplay[i].color = COLOR_GREY; sceneTimestampDisplay[i].isRoom = gSaveContext.ship.stats.sceneTimestamps[i].isRoom; @@ -593,13 +593,13 @@ void DrawGameplayStatsBreakdownTab() { ImGui::TableSetupColumn("stat", ImGuiTableColumnFlags_WidthStretch); for (int i = 0; i < gSaveContext.ship.stats.tsIdx; i++) { TimestampInfo tsInfo = sceneTimestampDisplay[i]; - bool canShow = !tsInfo.isRoom || CVarGetInteger(CVAR_ENHANCEMENT("GameplayStats.RoomBreakdown"), 0); + bool canShow = !tsInfo.isRoom || CVarGetInteger(CVAR_GAMEPLAY_STATS("RoomBreakdown"), 0); if (tsInfo.time > 0 && strnlen(tsInfo.name, 40) > 1 && canShow) { GameplayStatsRow(tsInfo.name, formatTimestampGameplayStat(tsInfo.time), tsInfo.color); } } std::string toPass; - if (CVarGetInteger(CVAR_ENHANCEMENT("GameplayStats.RoomBreakdown"), 0) && gSaveContext.ship.stats.sceneNum != SCENE_GROTTOS) { + if (CVarGetInteger(CVAR_GAMEPLAY_STATS("RoomBreakdown"), 0) && gSaveContext.ship.stats.sceneNum != SCENE_GROTTOS) { toPass = fmt::format("{:s} Room {:d}", ResolveSceneID(gSaveContext.ship.stats.sceneNum, gSaveContext.ship.stats.roomNum), gSaveContext.ship.stats.roomNum); } else { toPass = ResolveSceneID(gSaveContext.ship.stats.sceneNum, gSaveContext.ship.stats.roomNum); @@ -610,27 +610,27 @@ void DrawGameplayStatsBreakdownTab() { } void DrawGameplayStatsOptionsTab() { - UIWidgets::CVarCheckbox("Show in-game total timer", CVAR_ENHANCEMENT("GameplayStats.ShowIngameTimer"), + UIWidgets::CVarCheckbox("Show in-game total timer", CVAR_GAMEPLAY_STATS("ShowIngameTimer"), UIWidgets::CheckboxOptions() .Tooltip("Keep track of the timer as an in-game HUD element. The position of the " "timer can be changed in the Cosmetics Editor.") .Color(THEME_COLOR)); - UIWidgets::CVarCheckbox("Show latest timestamps on top", CVAR_ENHANCEMENT("GameplayStats.ReverseTimestamps"), + UIWidgets::CVarCheckbox("Show latest timestamps on top", CVAR_GAMEPLAY_STATS("ReverseTimestamps"), UIWidgets::CheckboxOptions().Color(THEME_COLOR)); - UIWidgets::CVarCheckbox("Room Breakdown", CVAR_ENHANCEMENT("GameplayStats.RoomBreakdown"), + UIWidgets::CVarCheckbox("Room Breakdown", CVAR_GAMEPLAY_STATS("RoomBreakdown"), UIWidgets::CheckboxOptions() .Tooltip("Allows a more in-depth perspective of time spent in a certain map.") .Color(THEME_COLOR)); - UIWidgets::CVarCheckbox("RTA Timing on new files", CVAR_ENHANCEMENT("GameplayStats.RTATiming"), + UIWidgets::CVarCheckbox("RTA Timing on new files", CVAR_GAMEPLAY_STATS("RTATiming"), UIWidgets::CheckboxOptions() .Tooltip("Timestamps are relative to starting timestamp rather than in game time, " "usually necessary for races/speedruns.\n\n" "Starting timestamp is on first non-C-up input after intro cutscene.\n\n" "NOTE: THIS NEEDS TO BE SET BEFORE CREATING A FILE TO TAKE EFFECT") .Color(THEME_COLOR)); - UIWidgets::CVarCheckbox("Show additional detail timers", CVAR_ENHANCEMENT("GameplayStats.ShowAdditionalTimers"), + UIWidgets::CVarCheckbox("Show additional detail timers", CVAR_GAMEPLAY_STATS("ShowAdditionalTimers"), UIWidgets::CheckboxOptions().Color(THEME_COLOR)); - UIWidgets::CVarCheckbox("Show Debug Info", CVAR_ENHANCEMENT("GameplayStats.ShowDebugInfo"), + UIWidgets::CVarCheckbox("Show Debug Info", CVAR_GAMEPLAY_STATS("ShowDebugInfo"), UIWidgets::CheckboxOptions().Color(THEME_COLOR)); } @@ -671,7 +671,7 @@ void InitStats(bool isDebug) { for (int dungeon = 0; dungeon < ARRAY_COUNT(gSaveContext.ship.stats.dungeonKeys); dungeon++) { gSaveContext.ship.stats.dungeonKeys[dungeon] = isDebug ? 8 : 0; } - gSaveContext.ship.stats.rtaTiming = CVarGetInteger(CVAR_ENHANCEMENT("GameplayStats.RTATiming"), 0); + gSaveContext.ship.stats.rtaTiming = CVarGetInteger(CVAR_GAMEPLAY_STATS("RTATiming"), 0); gSaveContext.ship.stats.fileCreatedAt = 0; gSaveContext.ship.stats.playTimer = 0; gSaveContext.ship.stats.pauseTimer = 0; diff --git a/soh/soh/Enhancements/gameplaystats.h b/soh/soh/Enhancements/gameplaystats.h index 81190c01e..b4d140f1f 100644 --- a/soh/soh/Enhancements/gameplaystats.h +++ b/soh/soh/Enhancements/gameplaystats.h @@ -27,7 +27,7 @@ extern "C" { : gSaveContext.ship.stats.itemTimestamp[TIMESTAMP_TRIFORCE_COMPLETED])) \ :\ (gSaveContext.ship.stats.playTimer / 2 + gSaveContext.ship.stats.pauseTimer / 3)) -#define CURRENT_MODE_TIMER (CVarGetInteger(CVAR_ENHANCEMENT("GameplayStats.RoomBreakdown"), 0) ?\ +#define CURRENT_MODE_TIMER (CVarGetInteger(CVAR_GAMEPLAY_STATS("RoomBreakdown"), 0) ?\ gSaveContext.ship.stats.roomTimer :\ gSaveContext.ship.stats.sceneTimer) diff --git a/soh/soh/SohGui/SohMenuEnhancements.cpp b/soh/soh/SohGui/SohMenuEnhancements.cpp index cc104b87d..febaa0a4a 100644 --- a/soh/soh/SohGui/SohMenuEnhancements.cpp +++ b/soh/soh/SohGui/SohMenuEnhancements.cpp @@ -1775,7 +1775,7 @@ void SohMenu::AddMenuEnhancements() { .WindowName("Additional Timers") .Options(WindowButtonOptions().Tooltip("Enables the separate Additional Timers Window.")); AddWidget(path, "Font Scale: %.2fx", WIDGET_CVAR_SLIDER_FLOAT) - .CVar(CVAR_ENHANCEMENT("TimeDisplay.FontScale")) + .CVar(CVAR_TIME_DISPLAY("FontScale")) .Callback([](WidgetInfo& info) { TimeDisplayInitSettings(); }) @@ -1786,7 +1786,7 @@ void SohMenu::AddMenuEnhancements() { .Format("%.2fx") ); AddWidget(path, "Hide Background", WIDGET_CVAR_CHECKBOX) - .CVar(CVAR_ENHANCEMENT("TimeDisplay.ShowWindowBG")) + .CVar(CVAR_TIME_DISPLAY("ShowWindowBG")) .Callback([](WidgetInfo& info) { TimeDisplayInitSettings(); }); diff --git a/soh/soh/config/ConfigMigrators.h b/soh/soh/config/ConfigMigrators.h index f3c663f66..021cab88e 100644 --- a/soh/soh/config/ConfigMigrators.h +++ b/soh/soh/config/ConfigMigrators.h @@ -299,12 +299,8 @@ namespace SOH { { MigrationAction::Rename, "gUniformLR", "gEnhancements.FixMenuLR" }, { MigrationAction::Rename, "gVisualAgony", "gEnhancements.VisualAgony" }, { MigrationAction::Rename, "gVoidDamageMul", "gEnhancements.VoidDamageMult" }, - { MigrationAction::Rename, "gGameplayStats.ShowAdditionalTimers", "gEnhancements.GameplayStats.ShowAdditionalTimers" }, - { MigrationAction::Rename, "gGameplayStats.ShowDebugInfo", "gEnhancements.GameplayStats.ShowDebugInfo" }, - { MigrationAction::Rename, "gGameplayStats.RoomBreakdown", "gEnhancements.GameplayStats.RoomBreakdown" }, - { MigrationAction::Rename, "gGameplayStats.ShowIngameTimer", "gEnhancements.GameplayStats.ShowInGameTimer" }, - { MigrationAction::Rename, "gGameplayStats.TimestampsReverse", "gEnhancements.GameplayStats.ReverseTimestamps" }, - { MigrationAction::Rename, "gGameplayStats.RTATiming", "gEnhancements.GameplayStats.RTATiming" }, + { MigrationAction::Rename, "gGameplayStats.ShowIngameTimer", "gGameplayStats.ShowInGameTimer" }, + { MigrationAction::Rename, "gGameplayStats.TimestampsReverse", "gGameplayStats.ReverseTimestamps" }, { MigrationAction::Rename, "gMirroredWorld", "gEnhancements.MirroredWorld" }, { MigrationAction::Rename, "gBetaQuestWorld", "gCheats.BetaQuestWorld" }, { MigrationAction::Rename, "gBombTimerMultiplier", "gCheats.BombTimerMultiplier" }, diff --git a/soh/soh/cvar_prefixes.h b/soh/soh/cvar_prefixes.h index 4b8d9f93a..375833cb1 100644 --- a/soh/soh/cvar_prefixes.h +++ b/soh/soh/cvar_prefixes.h @@ -14,4 +14,6 @@ #define CVAR_GENERAL(var) CVAR_PREFIX_GENERAL "." var #define CVAR_REMOTE(var) CVAR_PREFIX_REMOTE "." var #define CVAR_REMOTE_CROWD_CONTROL(var) CVAR_REMOTE("CrowdControl." var) -#define CVAR_REMOTE_SAIL(var) CVAR_REMOTE("Sail." var) \ No newline at end of file +#define CVAR_REMOTE_SAIL(var) CVAR_REMOTE("Sail." var) +#define CVAR_GAMEPLAY_STATS(var) CVAR_PREFIX_GAMEPLAY_STATS "." var +#define CVAR_TIME_DISPLAY(var) CVAR_PREFIX_TIME_DISPLAY "." var \ No newline at end of file diff --git a/soh/src/code/z_parameter.c b/soh/src/code/z_parameter.c index a221e570f..5060a0ab9 100644 --- a/soh/src/code/z_parameter.c +++ b/soh/src/code/z_parameter.c @@ -6123,7 +6123,7 @@ void Interface_DrawTotalGameplayTimer(PlayState* play) { // Draw timer based on the Gameplay Stats total time. if ((IS_BOSS_RUSH && gSaveContext.ship.quest.data.bossRush.options[BR_OPTIONS_TIMER] == BR_CHOICE_TIMER_YES) || - (CVarGetInteger(CVAR_ENHANCEMENT("GameplayStats.ShowIngameTimer"), 0) && gSaveContext.fileNum >= 0 && gSaveContext.fileNum <= 2)) { + (CVarGetInteger(CVAR_GAMEPLAY_STATS("ShowIngameTimer"), 0) && gSaveContext.fileNum >= 0 && gSaveContext.fileNum <= 2)) { s32 X_Margins_Timer = 0; if (CVarGetInteger(CVAR_COSMETIC("HUD.IGT.UseMargins"), 0) != 0) { From 2bde8fbe5db48969b47324b86c39272d3e335c2d Mon Sep 17 00:00:00 2001 From: xxAtrain223 Date: Mon, 31 Mar 2025 23:18:07 -0500 Subject: [PATCH 4/4] Use GetDungeonFromScene (#5271) * Use GetDungeonFromScene. * Remove SceneToDungeon map. --- soh/soh/Enhancements/randomizer/logic.cpp | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index 43ff3ce56..a567cd789 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -2104,30 +2104,16 @@ namespace Rando { } } - std::unordered_map SceneToDungeon = { - { SceneID::SCENE_DEKU_TREE, DungeonKey::DEKU_TREE }, - { SceneID::SCENE_DODONGOS_CAVERN, DungeonKey::DODONGOS_CAVERN }, - { SceneID::SCENE_JABU_JABU, DungeonKey::JABU_JABUS_BELLY }, - { SceneID::SCENE_FOREST_TEMPLE, DungeonKey::FOREST_TEMPLE }, - { SceneID::SCENE_FIRE_TEMPLE, DungeonKey::FIRE_TEMPLE }, - { SceneID::SCENE_WATER_TEMPLE, DungeonKey::WATER_TEMPLE }, - { SceneID::SCENE_SPIRIT_TEMPLE, DungeonKey::SPIRIT_TEMPLE }, - { SceneID::SCENE_SHADOW_TEMPLE, DungeonKey::SHADOW_TEMPLE }, - { SceneID::SCENE_BOTTOM_OF_THE_WELL, DungeonKey::BOTTOM_OF_THE_WELL }, - { SceneID::SCENE_ICE_CAVERN, DungeonKey::ICE_CAVERN }, - { SceneID::SCENE_GERUDO_TRAINING_GROUND, DungeonKey::GERUDO_TRAINING_GROUND }, - { SceneID::SCENE_INSIDE_GANONS_CASTLE, DungeonKey::GANONS_CASTLE }, - }; - // Get the swch bit positions for the dungeon const std::vector& GetDungeonSmallKeyDoors(SceneID sceneId) { static const std::vector emptyVector; - auto foundDungeon = SceneToDungeon.find(static_cast(sceneId)); - if (foundDungeon == SceneToDungeon.end()) { + + auto dungeonInfo = Rando::Context::GetInstance()->GetDungeons()->GetDungeonFromScene(sceneId); + if (dungeonInfo == nullptr) { return emptyVector; } - bool masterQuest = Rando::Context::GetInstance()->GetDungeon(foundDungeon->second)->IsMQ(); + bool masterQuest = dungeonInfo->IsMQ(); // Create a unique key for the dungeon and master quest uint8_t key = sceneId | (masterQuest << 7);