From 7595a5a65e86ad6a61d7bd642f5907f624a7d725 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Wed, 17 Jul 2024 17:45:29 +0100 Subject: [PATCH] fix selection only and make MQ dungeon settings set themselves more smartley (#4232) --- .../randomizer/3drando/random.hpp | 1 + soh/soh/Enhancements/randomizer/dungeon.cpp | 34 ++-- soh/soh/Enhancements/randomizer/dungeon.h | 7 +- .../randomizer/option_descriptions.cpp | 3 +- .../randomizer/randomizer_check_objects.cpp | 10 +- soh/soh/Enhancements/randomizer/settings.cpp | 179 +++++++++++------- 6 files changed, 149 insertions(+), 85 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/3drando/random.hpp b/soh/soh/Enhancements/randomizer/3drando/random.hpp index c8ccf0f50..623c396a1 100644 --- a/soh/soh/Enhancements/randomizer/3drando/random.hpp +++ b/soh/soh/Enhancements/randomizer/3drando/random.hpp @@ -30,6 +30,7 @@ const auto& RandomElement(const Container& container) { } //Shuffle items within a vector or array +//RANDOTODO There's probably a more efficient way to do what this does. template void Shuffle(std::vector& vector) { for (std::size_t i = 0; i + 1 < vector.size(); i++) diff --git a/soh/soh/Enhancements/randomizer/dungeon.cpp b/soh/soh/Enhancements/randomizer/dungeon.cpp index 8453c938b..0f799b036 100644 --- a/soh/soh/Enhancements/randomizer/dungeon.cpp +++ b/soh/soh/Enhancements/randomizer/dungeon.cpp @@ -8,10 +8,12 @@ namespace Rando { DungeonInfo::DungeonInfo(std::string name_, const RandomizerHintTextKey hintKey_, const RandomizerGet map_, const RandomizerGet compass_, const RandomizerGet smallKey_, const RandomizerGet keyRing_, const RandomizerGet bossKey_, RandomizerArea area_, const uint8_t vanillaKeyCount_, const uint8_t mqKeyCount_, + const RandomizerSettingKey mqSetting_, std::vector vanillaLocations_, std::vector mqLocations_, std::vector sharedLocations_, std::vector bossRoomLocations_) : name(std::move(name_)), hintKey(hintKey_), map(map_), compass(compass_), smallKey(smallKey_), keyRing(keyRing_), bossKey(bossKey_), area(area_), vanillaKeyCount(vanillaKeyCount_), mqKeyCount(mqKeyCount_), + mqSetting(mqSetting_), vanillaLocations(std::move(vanillaLocations_)), mqLocations(std::move(mqLocations_)), sharedLocations(std::move(sharedLocations_)), bossRoomLocations(std::move(bossRoomLocations_)) { } @@ -83,6 +85,14 @@ RandomizerGet DungeonInfo::GetBossKey() const { return bossKey; } +RandomizerSettingKey DungeonInfo::GetMQSetting() const { + return mqSetting; +} + +void DungeonInfo::SetDungeonKnown(bool known) { + isDungeonModeKnown = known; +} + void DungeonInfo::PlaceVanillaMap() const { if (map == RG_NONE) { return; @@ -152,7 +162,7 @@ std::vector DungeonInfo::GetEveryLocation() const { Dungeons::Dungeons() { dungeonList[DEKU_TREE] = - DungeonInfo("Deku Tree", RHT_DEKU_TREE, RG_DEKU_TREE_MAP, RG_DEKU_TREE_COMPASS, RG_NONE, RG_NONE, RG_NONE, RA_DEKU_TREE, 0, 0, + DungeonInfo("Deku Tree", RHT_DEKU_TREE, RG_DEKU_TREE_MAP, RG_DEKU_TREE_COMPASS, RG_NONE, RG_NONE, RG_NONE, RA_DEKU_TREE, 0, 0, RSK_MQ_DEKU_TREE, { // Vanilla Locations RC_DEKU_TREE_MAP_CHEST, @@ -188,7 +198,7 @@ Dungeons::Dungeons() { RC_QUEEN_GOHMA, }); dungeonList[DODONGOS_CAVERN] = DungeonInfo("Dodongo's Cavern", RHT_DODONGOS_CAVERN, RG_DODONGOS_CAVERN_MAP, - RG_DODONGOS_CAVERN_COMPASS, RG_NONE, RG_NONE, RG_NONE, RA_DODONGOS_CAVERN, 0, 0, + RG_DODONGOS_CAVERN_COMPASS, RG_NONE, RG_NONE, RG_NONE, RA_DODONGOS_CAVERN, 0, 0, RSK_MQ_DODONGOS_CAVERN, { // Vanilla Locations RC_DODONGOS_CAVERN_MAP_CHEST, @@ -232,7 +242,7 @@ Dungeons::Dungeons() { RC_KING_DODONGO, }); dungeonList[JABU_JABUS_BELLY] = DungeonInfo("Jabu Jabu's Belly", RHT_JABU_JABUS_BELLY, RG_JABU_JABUS_BELLY_MAP, - RG_JABU_JABUS_BELLY_COMPASS, RG_NONE, RG_NONE, RG_NONE, RA_JABU_JABUS_BELLY, 0, 0, + RG_JABU_JABUS_BELLY_COMPASS, RG_NONE, RG_NONE, RG_NONE, RA_JABU_JABUS_BELLY, 0, 0, RSK_MQ_JABU_JABU, { // Vanilla Locations RC_JABU_JABUS_BELLY_MAP_CHEST, @@ -271,7 +281,7 @@ Dungeons::Dungeons() { }); dungeonList[FOREST_TEMPLE] = DungeonInfo("Forest Temple", RHT_FOREST_TEMPLE, RG_FOREST_TEMPLE_MAP, RG_FOREST_TEMPLE_COMPASS, - RG_FOREST_TEMPLE_SMALL_KEY, RG_FOREST_TEMPLE_KEY_RING, RG_FOREST_TEMPLE_BOSS_KEY, RA_FOREST_TEMPLE, 5, 6, + RG_FOREST_TEMPLE_SMALL_KEY, RG_FOREST_TEMPLE_KEY_RING, RG_FOREST_TEMPLE_BOSS_KEY, RA_FOREST_TEMPLE, 5, 6, RSK_MQ_FOREST_TEMPLE, { // Vanilla Locations RC_FOREST_TEMPLE_FIRST_ROOM_CHEST, @@ -321,7 +331,7 @@ Dungeons::Dungeons() { }); dungeonList[FIRE_TEMPLE] = DungeonInfo("Fire Temple", RHT_FIRE_TEMPLE, RG_FIRE_TEMPLE_MAP, RG_FIRE_TEMPLE_COMPASS, - RG_FIRE_TEMPLE_SMALL_KEY, RG_FIRE_TEMPLE_KEY_RING, RG_FIRE_TEMPLE_BOSS_KEY, RA_FIRE_TEMPLE, 8, 5, + RG_FIRE_TEMPLE_SMALL_KEY, RG_FIRE_TEMPLE_KEY_RING, RG_FIRE_TEMPLE_BOSS_KEY, RA_FIRE_TEMPLE, 8, 5, RSK_MQ_FIRE_TEMPLE, { // Vanilla Locations RC_FIRE_TEMPLE_NEAR_BOSS_CHEST, @@ -372,7 +382,7 @@ Dungeons::Dungeons() { }); dungeonList[WATER_TEMPLE] = DungeonInfo("Water Temple", RHT_WATER_TEMPLE, RG_WATER_TEMPLE_MAP, RG_WATER_TEMPLE_COMPASS, - RG_WATER_TEMPLE_SMALL_KEY, RG_WATER_TEMPLE_KEY_RING, RG_WATER_TEMPLE_BOSS_KEY, RA_WATER_TEMPLE, 6, 2, + RG_WATER_TEMPLE_SMALL_KEY, RG_WATER_TEMPLE_KEY_RING, RG_WATER_TEMPLE_BOSS_KEY, RA_WATER_TEMPLE, 6, 2, RSK_MQ_WATER_TEMPLE, { // Vanilla Locations RC_WATER_TEMPLE_MAP_CHEST, @@ -413,7 +423,7 @@ Dungeons::Dungeons() { }); dungeonList[SPIRIT_TEMPLE] = DungeonInfo("Spirit Temple", RHT_SPIRIT_TEMPLE, RG_SPIRIT_TEMPLE_MAP, RG_SPIRIT_TEMPLE_COMPASS, - RG_SPIRIT_TEMPLE_SMALL_KEY, RG_SPIRIT_TEMPLE_KEY_RING, RG_SPIRIT_TEMPLE_BOSS_KEY, RA_SPIRIT_TEMPLE, 5, 7, + RG_SPIRIT_TEMPLE_SMALL_KEY, RG_SPIRIT_TEMPLE_KEY_RING, RG_SPIRIT_TEMPLE_BOSS_KEY, RA_SPIRIT_TEMPLE, 5, 7, RSK_MQ_SPIRIT_TEMPLE, { // Vanilla Locations RC_SPIRIT_TEMPLE_CHILD_BRIDGE_CHEST, @@ -479,7 +489,7 @@ Dungeons::Dungeons() { }); dungeonList[SHADOW_TEMPLE] = DungeonInfo("Shadow Temple", RHT_SHADOW_TEMPLE, RG_SHADOW_TEMPLE_MAP, RG_SHADOW_TEMPLE_COMPASS, - RG_SHADOW_TEMPLE_SMALL_KEY, RG_SHADOW_TEMPLE_KEY_RING, RG_SHADOW_TEMPLE_BOSS_KEY, RA_SHADOW_TEMPLE, 5, 6, + RG_SHADOW_TEMPLE_SMALL_KEY, RG_SHADOW_TEMPLE_KEY_RING, RG_SHADOW_TEMPLE_BOSS_KEY, RA_SHADOW_TEMPLE, 5, 6, RSK_MQ_SHADOW_TEMPLE, { // Vanilla Locations RC_SHADOW_TEMPLE_MAP_CHEST, @@ -541,7 +551,7 @@ Dungeons::Dungeons() { }); dungeonList[BOTTOM_OF_THE_WELL] = DungeonInfo( "Bottom of the Well", RHT_BOTTOM_OF_THE_WELL, RG_BOTTOM_OF_THE_WELL_MAP, RG_BOTTOM_OF_THE_WELL_COMPASS, - RG_BOTTOM_OF_THE_WELL_SMALL_KEY, RG_BOTTOM_OF_THE_WELL_KEY_RING, RG_NONE, RA_BOTTOM_OF_THE_WELL, 3, 2, + RG_BOTTOM_OF_THE_WELL_SMALL_KEY, RG_BOTTOM_OF_THE_WELL_KEY_RING, RG_NONE, RA_BOTTOM_OF_THE_WELL, 3, 2, RSK_MQ_BOTTOM_OF_THE_WELL, { // Vanilla Locations RC_BOTTOM_OF_THE_WELL_FRONT_LEFT_FAKE_WALL_CHEST, @@ -575,7 +585,7 @@ Dungeons::Dungeons() { }, {}, {}); dungeonList[ICE_CAVERN] = DungeonInfo("Ice Cavern", RHT_ICE_CAVERN, RG_ICE_CAVERN_MAP, RG_ICE_CAVERN_COMPASS, - RG_NONE, RG_NONE, RG_NONE, RA_ICE_CAVERN, 0, 0, + RG_NONE, RG_NONE, RG_NONE, RA_ICE_CAVERN, 0, 0, RSK_MQ_ICE_CAVERN, { // Vanilla Locations RC_ICE_CAVERN_MAP_CHEST, @@ -603,7 +613,7 @@ Dungeons::Dungeons() { {}); dungeonList[GERUDO_TRAINING_GROUNDS] = DungeonInfo("Gerudo Training Grounds", RHT_GERUDO_TRAINING_GROUND, RG_NONE, RG_NONE, - RG_GERUDO_TRAINING_GROUNDS_SMALL_KEY, RG_GERUDO_TRAINING_GROUNDS_KEY_RING, RG_NONE, RA_GERUDO_TRAINING_GROUND, 9, 3, + RG_GERUDO_TRAINING_GROUNDS_SMALL_KEY, RG_GERUDO_TRAINING_GROUNDS_KEY_RING, RG_NONE, RA_GERUDO_TRAINING_GROUND, 9, 3, RSK_MQ_GTG, { // Vanilla Locations RC_GERUDO_TRAINING_GROUND_LOBBY_LEFT_CHEST, @@ -652,7 +662,7 @@ Dungeons::Dungeons() { {}, {}); dungeonList[GANONS_CASTLE] = DungeonInfo("Ganon's Castle", RHT_GANONS_CASTLE, RG_NONE, RG_NONE, RG_GANONS_CASTLE_SMALL_KEY, - RG_GANONS_CASTLE_KEY_RING, RG_GANONS_CASTLE_BOSS_KEY, RA_GANONS_CASTLE, 2, 3, + RG_GANONS_CASTLE_KEY_RING, RG_GANONS_CASTLE_BOSS_KEY, RA_GANONS_CASTLE, 2, 3, RSK_MQ_GANONS_CASTLE, { // Vanilla Locations RC_GANONS_CASTLE_FOREST_TRIAL_CHEST, diff --git a/soh/soh/Enhancements/randomizer/dungeon.h b/soh/soh/Enhancements/randomizer/dungeon.h index a2338441d..b9176f707 100644 --- a/soh/soh/Enhancements/randomizer/dungeon.h +++ b/soh/soh/Enhancements/randomizer/dungeon.h @@ -12,7 +12,8 @@ class DungeonInfo { public: DungeonInfo(std::string name_, RandomizerHintTextKey hintKey_, RandomizerGet map_, RandomizerGet compass_, RandomizerGet smallKey_, RandomizerGet keyRing_, RandomizerGet bossKey_, RandomizerArea area_, - uint8_t vanillaKeyCount_, uint8_t mqKeyCount_, std::vector vanillaLocations_, + uint8_t vanillaKeyCount_, uint8_t mqKeyCount_, RandomizerSettingKey mqSetting_, + std::vector vanillaLocations_, std::vector mqLocations_, std::vector sharedLocations_, std::vector bossRoomLocations_); DungeonInfo(); @@ -34,6 +35,8 @@ class DungeonInfo { RandomizerGet GetMap() const; RandomizerGet GetCompass() const; RandomizerGet GetBossKey() const; + RandomizerSettingKey GetMQSetting() const; + void SetDungeonKnown(bool known); void PlaceVanillaMap() const; void PlaceVanillaCompass() const; void PlaceVanillaBossKey() const; @@ -50,6 +53,8 @@ class DungeonInfo { RandomizerGet smallKey; RandomizerGet keyRing; RandomizerGet bossKey; + RandomizerSettingKey mqSetting; + bool isDungeonModeKnown = true; uint8_t vanillaKeyCount{}; uint8_t mqKeyCount{}; bool masterQuest = false; diff --git a/soh/soh/Enhancements/randomizer/option_descriptions.cpp b/soh/soh/Enhancements/randomizer/option_descriptions.cpp index 3e91715c4..ad5cd4e09 100644 --- a/soh/soh/Enhancements/randomizer/option_descriptions.cpp +++ b/soh/soh/Enhancements/randomizer/option_descriptions.cpp @@ -98,7 +98,8 @@ void Settings::CreateOptionDescriptions() { "\n" "Random Number - A Random number and set of dungeons will be their Master Quest varieties.\n" "\n" - "Selection Only - Specify which dungeons are Vanilla or Master Quest."; + "Selection Only - Specify which dungeons are Vanilla, Master Quest or a 50/50 between the two.\n" + "Differs from Random Number in that they are rolled individually, making the exact total a bell curve."; mOptionDescriptions[RSK_MQ_DUNGEON_SET] = "Choose specific Dungeons to be Master Quest or Vanilla.\n" "\n" diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_objects.cpp b/soh/soh/Enhancements/randomizer/randomizer_check_objects.cpp index d6fc3fefd..ddedd5b53 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_check_objects.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_check_objects.cpp @@ -112,12 +112,12 @@ void RandomizerCheckObjects::UpdateImGuiVisibility() { (location.GetRandomizerCheck() != RC_UNKNOWN_CHECK) && (!RandomizerCheckObjects::AreaIsDungeon(location.GetArea()) || location.GetQuest() == RCQUEST_BOTH || location.GetQuest() == RCQUEST_MQ && - ((CVarGetInteger(CVAR_RANDOMIZER_SETTING("MqDungeons"), RO_MQ_DUNGEONS_NONE) == RO_MQ_DUNGEONS_SET_NUMBER && - (CVarGetInteger(CVAR_RANDOMIZER_SETTING("MqDungeonCount"), 12) > 0) || // at least one MQ dungeon - CVarGetInteger(CVAR_RANDOMIZER_SETTING("MqDungeons"), RO_MQ_DUNGEONS_NONE) == RO_MQ_DUNGEONS_RANDOM_NUMBER)) || + ((CVarGetInteger(CVAR_RANDOMIZER_SETTING("MQDungeons"), RO_MQ_DUNGEONS_NONE) == RO_MQ_DUNGEONS_SET_NUMBER && + (CVarGetInteger(CVAR_RANDOMIZER_SETTING("MQDungeonCount"), 12) > 0) || // at least one MQ dungeon + CVarGetInteger(CVAR_RANDOMIZER_SETTING("MQDungeons"), RO_MQ_DUNGEONS_NONE) == RO_MQ_DUNGEONS_RANDOM_NUMBER)) || location.GetQuest() == RCQUEST_VANILLA && - (CVarGetInteger(CVAR_RANDOMIZER_SETTING("MqDungeons"), RO_MQ_DUNGEONS_NONE) != RO_MQ_DUNGEONS_SET_NUMBER || - CVarGetInteger(CVAR_RANDOMIZER_SETTING("MqDungeonCount"), 12) < 12) // at least one vanilla dungeon + (CVarGetInteger(CVAR_RANDOMIZER_SETTING("MQDungeons"), RO_MQ_DUNGEONS_NONE) != RO_MQ_DUNGEONS_SET_NUMBER || + CVarGetInteger(CVAR_RANDOMIZER_SETTING("MQDungeonCount"), 12) < 12) // at least one vanilla dungeon ) && (location.GetRCType() != RCTYPE_SHOP || CVarGetInteger(CVAR_RANDOMIZER_SETTING("Shopsanity"), RO_SHOPSANITY_OFF) > RO_SHOPSANITY_ZERO_ITEMS) && diff --git a/soh/soh/Enhancements/randomizer/settings.cpp b/soh/soh/Enhancements/randomizer/settings.cpp index 745593ef5..80007b564 100644 --- a/soh/soh/Enhancements/randomizer/settings.cpp +++ b/soh/soh/Enhancements/randomizer/settings.cpp @@ -1486,7 +1486,7 @@ void Settings::UpdateOptionProperties() { } else { // If any MQ Options are available, show the MQ Dungeon Randomization Combobox mOptions[RSK_MQ_DUNGEON_RANDOM].Unhide(); - switch(CVarGetInteger(CVAR_RANDOMIZER_SETTING("MqDungeons"), RO_MQ_DUNGEONS_NONE)) { + switch(CVarGetInteger(CVAR_RANDOMIZER_SETTING("MQDungeons"), RO_MQ_DUNGEONS_NONE)) { // If No MQ Dungeons, add a separator after the combobx and hide // the count slider and the toggle for individual dungeon selections. case RO_MQ_DUNGEONS_NONE: @@ -1504,17 +1504,22 @@ void Settings::UpdateOptionProperties() { // else if random number or selection only, remove the separator and only show // the individual dungeon selection toggle. case RO_MQ_DUNGEONS_RANDOM_NUMBER: - case RO_MQ_DUNGEONS_SELECTION: mOptions[RSK_MQ_DUNGEON_RANDOM].RemoveFlag(IMFLAG_SEPARATOR_BOTTOM); mOptions[RSK_MQ_DUNGEON_COUNT].Hide(); mOptions[RSK_MQ_DUNGEON_SET].Unhide(); break; + case RO_MQ_DUNGEONS_SELECTION: + mOptions[RSK_MQ_DUNGEON_RANDOM].RemoveFlag(IMFLAG_SEPARATOR_BOTTOM); + mOptions[RSK_MQ_DUNGEON_COUNT].Hide(); + mOptions[RSK_MQ_DUNGEON_SET].Hide(); + break; default: break; } // Controls whether or not to show the selectors for individual dungeons. - if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("MqDungeons"), RO_MQ_DUNGEONS_NONE) != RO_MQ_DUNGEONS_NONE && - CVarGetInteger(CVAR_RANDOMIZER_SETTING("MqDungeonsSelection"), RO_GENERIC_OFF) == RO_GENERIC_ON) { + if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("MQDungeons"), RO_MQ_DUNGEONS_NONE) != RO_MQ_DUNGEONS_NONE && + (CVarGetInteger(CVAR_RANDOMIZER_SETTING("MQDungeonsSelection"), RO_GENERIC_OFF) == RO_GENERIC_ON || + CVarGetInteger(CVAR_RANDOMIZER_SETTING("MQDungeons"), RO_MQ_DUNGEONS_NONE) == RO_MQ_DUNGEONS_SELECTION)) { // if showing the dungeon selectors, remove the separator after the Set Dungeons checkbox. mOptions[RSK_MQ_DUNGEON_SET].RemoveFlag(IMFLAG_SEPARATOR_BOTTOM); mOptions[RSK_MQ_DEKU_TREE].Unhide(); @@ -1546,12 +1551,7 @@ void Settings::UpdateOptionProperties() { mOptions[RSK_MQ_GANONS_CASTLE].Hide(); } } - // Disable interaction with Set Dungeons checkbox if MQ Dungeon Randomization is set to Selection Only. - if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("MqDungeons"), RO_MQ_DUNGEONS_NONE) == RO_MQ_DUNGEONS_SELECTION) { - mOptions[RSK_MQ_DUNGEON_SET].Disable("This option is force-enabled because Master Quest Dungeons is set to Selection Only", UIWidgets::CheckboxGraphics::Checkmark); - } else { - mOptions[RSK_MQ_DUNGEON_SET].Enable(); - } + // Show mixed entrance pool options if mixed entrance pools are enabled at all. if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("MixedEntrances"), RO_GENERIC_OFF)) { mOptions[RSK_MIXED_ENTRANCE_POOLS].RemoveFlag(IMFLAG_SEPARATOR_BOTTOM); @@ -1871,74 +1871,121 @@ void Settings::FinalizeSettings(const std::set& excludedLocatio // RANDOTODO implement chest shuffle with keysanity // ShuffleChestMinigame.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_CHEST_MINIGAME]); mOptions[RSK_SHUFFLE_CHEST_MINIGAME].SetSelectedIndex(RO_CHEST_GAME_OFF); + //TODO: RandomizeAllSettings(true) when implementing the ability to randomize the options themselves. std::array dungeons = ctx->GetDungeons()->GetDungeonList(); - std::array dungeonModesKnown{}; - const std::vector dungeonOptions = { - &mOptions[RSK_MQ_DEKU_TREE], - &mOptions[RSK_MQ_DODONGOS_CAVERN], - &mOptions[RSK_MQ_JABU_JABU], - &mOptions[RSK_MQ_FOREST_TEMPLE], - &mOptions[RSK_MQ_FIRE_TEMPLE], - &mOptions[RSK_MQ_WATER_TEMPLE], - &mOptions[RSK_MQ_SPIRIT_TEMPLE], - &mOptions[RSK_MQ_SHADOW_TEMPLE], - &mOptions[RSK_MQ_BOTTOM_OF_THE_WELL], - &mOptions[RSK_MQ_ICE_CAVERN], - &mOptions[RSK_MQ_GTG], - &mOptions[RSK_MQ_GANONS_CASTLE] - }; - auto mqSet = mOptions[RSK_MQ_DUNGEON_COUNT].Value(); - if (mOptions[RSK_MQ_DUNGEON_RANDOM].Is(RO_MQ_DUNGEONS_SELECTION)) { - mqSet = 0; + + //reset the MQ vars + for (auto dungeon: dungeons) { + dungeon->ClearMQ(); + dungeon->SetDungeonKnown(true); } - std::vector randMQOption = {}; - if (mOptions[RSK_MQ_DUNGEON_SET]) { - uint8_t dungeonCount = 0; - for (size_t i = 0; i < dungeons.size(); i++) { - dungeons[i]->ClearMQ(); - dungeonModesKnown[i] = true; - switch (dungeonOptions[i]->Value()) { - case 1: - dungeons[i]->SetMQ(); - dungeonCount++; + //if it's selection mode, process the selection directly + if (mOptions[RSK_MQ_DUNGEON_RANDOM].Value() == RO_MQ_DUNGEONS_SELECTION){ + mOptions[RSK_MQ_DUNGEON_SET].SetSelectedIndex(RO_GENERIC_ON); + //How many dungeons are set to MQ in selection + uint8_t mqSet = 0; + for (auto dungeon: dungeons) { + switch (mOptions[dungeon->GetMQSetting()].Value()) { + case RO_MQ_SET_MQ: + dungeon->SetMQ(); + mqSet += 1; break; - case 2: - randMQOption.push_back(i); - dungeonModesKnown[i] = false; + case RO_MQ_SET_RANDOM: + //50% per dungeon, rolled seperatly so people can either have a linear distribtuion + //or a bell curve for the number of MQ dungeons per seed. + if (Random(0,2)){ + dungeon->SetMQ(); + mqSet += 1; + } + dungeon->SetDungeonKnown(false); break; default: break; } } - Shuffle(randMQOption); - if (mOptions[RSK_MQ_DUNGEON_RANDOM].Is(RO_MQ_DUNGEONS_RANDOM_NUMBER)) { - mqSet = dungeonCount + Random(0, static_cast(randMQOption.size()) + 1); - } - for (uint8_t i = 0; dungeonCount < mqSet; i++) { - if (i > randMQOption.size()) { - // This can happen if the amount of MQ Dungeons is specifically - // set to a higher number than the amount of Dungeons specifically set to MQ or Random, - // break out of the loop and just have fewer MQ dungeons than the Set Count. - break; + //override the dungeons set with the ones set by selection, so it's accurate for anything that wants to know MQ dungeon count + mOptions[RSK_MQ_DUNGEON_COUNT].SetSelectedIndex(mqSet); + //handling set number and random number together + } else if (mOptions[RSK_MQ_DUNGEON_RANDOM].Value() != RO_MQ_DUNGEONS_NONE){ + // so we don't have to call this repeatedly + uint8_t mqCount = mOptions[RSK_MQ_DUNGEON_COUNT].Value(); + //How many dungeons are set to MQ in selection + uint8_t mqSet = 0; + //the number of random + uint8_t mqToSet = 0; + //store the dungeons to randomly decide between. we use the id instead of a dungeon object to avoid a lot of casting. + std::vector randMQOption = {}; + //if dungeons have been preset, process them + if (mOptions[RSK_MQ_DUNGEON_SET]){ + for (size_t i = 0; i < dungeons.size(); i++) { + switch (mOptions[dungeons[i]->GetMQSetting()].Value()) { + case RO_MQ_SET_MQ: + dungeons[i]->SetMQ(); + mqSet += 1; + break; + case RO_MQ_SET_RANDOM: + randMQOption.push_back(i); + dungeons[i]->SetDungeonKnown(false); + break; + default: + break; + } + } + //otherwise, every dungeon is possible + } else { + //if the count is fixed to 12, we know everything is MQ, so can skip some setps and do not set Known + if (mOptions[RSK_MQ_DUNGEON_RANDOM].Value() == RO_MQ_DUNGEONS_SET_NUMBER && + mqCount == 12) { + randMQOption = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; + for (auto dungeon: dungeons) { + mOptions[dungeon->GetMQSetting()].SetSelectedIndex(RO_MQ_SET_MQ); + } + //if it's fixed to zero, set it to None instead. the rest is processed after + } else if (mOptions[RSK_MQ_DUNGEON_RANDOM].Value() == RO_MQ_DUNGEONS_SET_NUMBER && + mqCount == 0){ + mOptions[RSK_MQ_DUNGEON_RANDOM].SetSelectedIndex(RO_MQ_DUNGEONS_NONE); + //otherwise, make everything a possibility and unknown + } else { + for (size_t i = 0; i < dungeons.size(); i++) { + randMQOption.push_back(i); + dungeons[i]->SetDungeonKnown(false); + mOptions[dungeons[i]->GetMQSetting()].SetSelectedIndex(RO_MQ_SET_RANDOM); + } } - dungeons[randMQOption[i]]->SetMQ(); - dungeonCount++; } - } else { - Shuffle(dungeons); - for (const auto dungeon : dungeons) { - dungeon->ClearMQ(); + //if there's no random options, we can skip this + if (randMQOption.size() > 0){ + //Figure out how many dungeons to select, rolling the random number if needed + if (mOptions[RSK_MQ_DUNGEON_RANDOM].Is(RO_MQ_DUNGEONS_RANDOM_NUMBER)){ + mqToSet = Random(0, static_cast(randMQOption.size()) + 1); + } else if (mqCount > mqSet) { + mqToSet = std::min(mqCount - mqSet, static_cast(randMQOption.size())); + } + //we only need to shuffle if we're not using them all + if (mqToSet <= static_cast(randMQOption.size()) && mqToSet > 0) { + Shuffle(randMQOption); + } + for (uint8_t i = 0; i < mqToSet; i++){ + dungeons[randMQOption[i]]->SetMQ(); + } + } else { + //if there's no random options, check if we can collapse the setting into None or Selection + if (mqSet == 0){ + mOptions[RSK_MQ_DUNGEON_RANDOM].SetSelectedIndex(RO_MQ_DUNGEONS_NONE); + } else { + mOptions[RSK_MQ_DUNGEON_RANDOM].SetSelectedIndex(RO_MQ_DUNGEONS_SELECTION); + } } - const bool allDungeonModesKnown = mqSet == 0 || mqSet == dungeons.size(); - for (bool & i : dungeonModesKnown) { - i = allDungeonModesKnown; - } - if (mOptions[RSK_MQ_DUNGEON_RANDOM].Is(RO_MQ_DUNGEONS_RANDOM_NUMBER)) { - mqSet = Random(0, 13); - } - for (uint8_t i = 0; i < mqSet; i++) { - dungeons[i]->SetMQ(); + //reset the value set based on what was actually set + mOptions[RSK_MQ_DUNGEON_COUNT].SetSelectedIndex(mqToSet + mqSet); + } + //Not an if else as other settings can become None in processing + if (mOptions[RSK_MQ_DUNGEON_RANDOM].Value() == RO_MQ_DUNGEONS_NONE) { + mOptions[RSK_MQ_DUNGEON_SET].SetSelectedIndex(RO_GENERIC_OFF); + mOptions[RSK_MQ_DUNGEON_COUNT].SetSelectedIndex(0); + for (auto dungeon: dungeons) { + mOptions[dungeon->GetMQSetting()].SetSelectedIndex(RO_MQ_SET_VANILLA); } }