From 14a241ed3f7772ce69993ff9c4148ced8ef0bf58 Mon Sep 17 00:00:00 2001 From: Christopher Leggett Date: Fri, 2 Jan 2026 00:46:22 +0000 Subject: [PATCH] Integrate Randomizer Window into Modern Menu Properly (#6017) Also comes with a menu reorganization --- .../Enhancements/ExtraModes/MirroredWorld.cpp | 2 +- soh/soh/Enhancements/Presets/Presets.cpp | 4 +- .../TimeSavers/SkipCutscene/SkipIntro.cpp | 2 +- .../SkipCutscene/Story/SkipBlueWarp.cpp | 2 +- soh/soh/Enhancements/enemyrandomizer.cpp | 2 +- .../Enhancements/item-tables/ItemTableTypes.h | 1 + .../Enhancements/randomizer/3drando/fill.cpp | 2 +- .../Enhancements/randomizer/3drando/fill.hpp | 2 + .../randomizer/3drando/hint_list.cpp | 2 +- .../randomizer/3drando/item_pool.cpp | 2 +- .../randomizer/3drando/playthrough.hpp | 5 +- .../randomizer/3drando/rando_main.cpp | 2 +- .../randomizer/3drando/rando_main.hpp | 3 +- .../Enhancements/randomizer/3drando/shops.hpp | 2 +- .../randomizer/3drando/spoiler_log.cpp | 2 +- .../randomizer/3drando/starting_inventory.cpp | 2 +- .../{context.cpp => SeedContext.cpp} | 4 +- .../randomizer/{context.h => SeedContext.h} | 0 soh/soh/Enhancements/randomizer/dungeon.cpp | 2 +- soh/soh/Enhancements/randomizer/entrance.h | 3 +- soh/soh/Enhancements/randomizer/hint.cpp | 2 +- soh/soh/Enhancements/randomizer/item.cpp | 2 +- soh/soh/Enhancements/randomizer/item_list.cpp | 2 +- .../Enhancements/randomizer/item_location.cpp | 2 +- .../randomizer/location_access.cpp | 2 +- .../Enhancements/randomizer/location_access.h | 2 +- .../Enhancements/randomizer/location_list.cpp | 2 +- soh/soh/Enhancements/randomizer/logic.cpp | 2 +- soh/soh/Enhancements/randomizer/logic.h | 2 +- soh/soh/Enhancements/randomizer/option.cpp | 214 +- soh/soh/Enhancements/randomizer/option.h | 51 +- .../Enhancements/randomizer/randomizer.cpp | 671 +---- soh/soh/Enhancements/randomizer/randomizer.h | 3 +- .../Enhancements/randomizer/randomizerTypes.h | 57 +- .../randomizer/randomizer_check_objects.cpp | 2 +- .../randomizer/randomizer_check_objects.h | 3 +- .../randomizer/randomizer_check_tracker.h | 1 + .../Enhancements/randomizer/randomizer_inf.h | 2 +- .../randomizer/randomizer_item_tracker.h | 2 +- .../randomizer/randomizer_settings_window.h | 21 - soh/soh/Enhancements/randomizer/settings.cpp | 2373 +++++++++-------- soh/soh/Enhancements/randomizer/settings.h | 25 +- soh/soh/Enhancements/randomizer/static_data.h | 2 +- soh/soh/OTRGlobals.cpp | 4 +- soh/soh/SaveManager.cpp | 2 +- soh/soh/SohGui/SohGui.cpp | 9 +- soh/soh/SohGui/SohGui.hpp | 1 - soh/soh/SohGui/SohMenuBar.cpp | 1 - soh/soh/SohGui/SohMenuRandomizer.cpp | 617 ++++- soh/soh/SohGui/UIWidgets.hpp | 4 + 50 files changed, 2191 insertions(+), 1938 deletions(-) rename soh/soh/Enhancements/randomizer/{context.cpp => SeedContext.cpp} (99%) rename soh/soh/Enhancements/randomizer/{context.h => SeedContext.h} (100%) delete mode 100644 soh/soh/Enhancements/randomizer/randomizer_settings_window.h diff --git a/soh/soh/Enhancements/ExtraModes/MirroredWorld.cpp b/soh/soh/Enhancements/ExtraModes/MirroredWorld.cpp index 723a5f45e..98d9c2c0d 100644 --- a/soh/soh/Enhancements/ExtraModes/MirroredWorld.cpp +++ b/soh/soh/Enhancements/ExtraModes/MirroredWorld.cpp @@ -1,7 +1,7 @@ #include "soh/Enhancements/cosmetics/authenticGfxPatches.h" #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #include "soh/Enhancements/randomizer/3drando/random.hpp" -#include "soh/Enhancements/randomizer/context.h" +#include "soh/Enhancements/randomizer/SeedContext.h" #include "soh/Enhancements/enhancementTypes.h" #include "soh/ResourceManagerHelpers.h" #include "soh/ShipInit.hpp" diff --git a/soh/soh/Enhancements/Presets/Presets.cpp b/soh/soh/Enhancements/Presets/Presets.cpp index c75303045..d08859ae3 100644 --- a/soh/soh/Enhancements/Presets/Presets.cpp +++ b/soh/soh/Enhancements/Presets/Presets.cpp @@ -11,7 +11,6 @@ #include "soh/SohGui/MenuTypes.h" #include "soh/SohGui/SohMenu.h" #include "soh/SohGui/SohGui.hpp" -#include "soh/Enhancements/randomizer/randomizer_settings_window.h" #include "soh/Enhancements/randomizer/randomizer_check_tracker.h" #include "soh/Enhancements/randomizer/randomizer_entrance_tracker.h" #include "soh/Enhancements/randomizer/randomizer_item_tracker.h" @@ -20,7 +19,6 @@ namespace fs = std::filesystem; namespace SohGui { extern std::shared_ptr mSohMenu; -extern std::shared_ptr mRandomizerSettingsWindow; } // namespace SohGui struct PresetInfo { @@ -124,7 +122,7 @@ void applyPreset(std::string presetName, std::vector includeSecti } } if (i == PRESET_SECTION_RANDOMIZER) { - SohGui::mRandomizerSettingsWindow->SetNeedsUpdate(); + Rando::Settings::GetInstance()->UpdateAllOptions(); } } } diff --git a/soh/soh/Enhancements/TimeSavers/SkipCutscene/SkipIntro.cpp b/soh/soh/Enhancements/TimeSavers/SkipCutscene/SkipIntro.cpp index 26eae8097..d9e08b3da 100644 --- a/soh/soh/Enhancements/TimeSavers/SkipCutscene/SkipIntro.cpp +++ b/soh/soh/Enhancements/TimeSavers/SkipCutscene/SkipIntro.cpp @@ -1,5 +1,5 @@ #include "soh/Enhancements/game-interactor/GameInteractor.h" -#include "soh/Enhancements/randomizer/context.h" +#include "soh/Enhancements/randomizer/SeedContext.h" #include "soh/ShipInit.hpp" extern "C" { diff --git a/soh/soh/Enhancements/TimeSavers/SkipCutscene/Story/SkipBlueWarp.cpp b/soh/soh/Enhancements/TimeSavers/SkipCutscene/Story/SkipBlueWarp.cpp index ebd2348a3..49fb61329 100644 --- a/soh/soh/Enhancements/TimeSavers/SkipCutscene/Story/SkipBlueWarp.cpp +++ b/soh/soh/Enhancements/TimeSavers/SkipCutscene/Story/SkipBlueWarp.cpp @@ -1,6 +1,6 @@ #include "soh/Enhancements/game-interactor/GameInteractor.h" #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" -#include "soh/Enhancements/randomizer/context.h" +#include "soh/Enhancements/randomizer/SeedContext.h" #include "soh/ShipInit.hpp" extern "C" { diff --git a/soh/soh/Enhancements/enemyrandomizer.cpp b/soh/soh/Enhancements/enemyrandomizer.cpp index 52d35fbc5..aedc47643 100644 --- a/soh/soh/Enhancements/enemyrandomizer.cpp +++ b/soh/soh/Enhancements/enemyrandomizer.cpp @@ -2,7 +2,7 @@ #include "functions.h" #include "macros.h" #include "soh/Enhancements/randomizer/3drando/random.hpp" -#include "soh/Enhancements/randomizer/context.h" +#include "soh/Enhancements/randomizer/SeedContext.h" #include "soh/Enhancements/enhancementTypes.h" #include "variables.h" #include "soh/OTRGlobals.h" diff --git a/soh/soh/Enhancements/item-tables/ItemTableTypes.h b/soh/soh/Enhancements/item-tables/ItemTableTypes.h index df59d6770..46b4cbb5b 100644 --- a/soh/soh/Enhancements/item-tables/ItemTableTypes.h +++ b/soh/soh/Enhancements/item-tables/ItemTableTypes.h @@ -1,4 +1,5 @@ #pragma once + #ifdef __cplusplus #include #endif diff --git a/soh/soh/Enhancements/randomizer/3drando/fill.cpp b/soh/soh/Enhancements/randomizer/3drando/fill.cpp index 4f7f2d395..b6a564c73 100644 --- a/soh/soh/Enhancements/randomizer/3drando/fill.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/fill.cpp @@ -1,7 +1,7 @@ #include "fill.hpp" #include "../dungeon.h" -#include "../context.h" +#include "../SeedContext.h" #include "item_pool.hpp" #include "random.hpp" #include "spoiler_log.hpp" diff --git a/soh/soh/Enhancements/randomizer/3drando/fill.hpp b/soh/soh/Enhancements/randomizer/3drando/fill.hpp index 0e032985e..d66ef5d40 100644 --- a/soh/soh/Enhancements/randomizer/3drando/fill.hpp +++ b/soh/soh/Enhancements/randomizer/3drando/fill.hpp @@ -74,3 +74,5 @@ void GeneratePlaythrough(); bool CheckBeatable(RandomizerGet ignore = RG_NONE); void ValidateEntrances(bool checkOtherEntranceAccess); + +void ValidateEntrances(bool checkPoeCollectorAccess, bool checkOtherEntranceAccess); \ No newline at end of file diff --git a/soh/soh/Enhancements/randomizer/3drando/hint_list.cpp b/soh/soh/Enhancements/randomizer/3drando/hint_list.cpp index 598940082..47e71d026 100644 --- a/soh/soh/Enhancements/randomizer/3drando/hint_list.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/hint_list.cpp @@ -1,7 +1,7 @@ #include "custom_messages.hpp" #include "../randomizerTypes.h" -#include "../context.h" +#include "../SeedContext.h" #include "../static_data.h" using namespace CustomMessages; diff --git a/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp b/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp index e9260e02f..b37bd8325 100644 --- a/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp @@ -3,7 +3,7 @@ #include "../dungeon.h" #include "fill.hpp" #include "../static_data.h" -#include "../context.h" +#include "../SeedContext.h" #include "pool_functions.hpp" #include "random.hpp" #include "spoiler_log.hpp" diff --git a/soh/soh/Enhancements/randomizer/3drando/playthrough.hpp b/soh/soh/Enhancements/randomizer/3drando/playthrough.hpp index 52ad6a3ee..513c4b2fe 100644 --- a/soh/soh/Enhancements/randomizer/3drando/playthrough.hpp +++ b/soh/soh/Enhancements/randomizer/3drando/playthrough.hpp @@ -1,11 +1,12 @@ #pragma once + #include #include -#include "../context.h" +#include "../SeedContext.h" namespace Playthrough { int Playthrough_Init(uint32_t seed, std::set excludedLocations, std::set enabledTricks); int Playthrough_Repeat(std::set excludedLocations, std::set enabledTricks, int count = 1); -} // namespace Playthrough +} // namespace Playthrough \ No newline at end of file diff --git a/soh/soh/Enhancements/randomizer/3drando/rando_main.cpp b/soh/soh/Enhancements/randomizer/3drando/rando_main.cpp index 28f8537b4..76f26f912 100644 --- a/soh/soh/Enhancements/randomizer/3drando/rando_main.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/rando_main.cpp @@ -3,7 +3,7 @@ #include "../item_location.h" #include "../location_access.h" #include "rando_main.hpp" -#include "../context.h" +#include "../SeedContext.h" #include #include #include diff --git a/soh/soh/Enhancements/randomizer/3drando/rando_main.hpp b/soh/soh/Enhancements/randomizer/3drando/rando_main.hpp index b03dd8b88..77e98b2fd 100644 --- a/soh/soh/Enhancements/randomizer/3drando/rando_main.hpp +++ b/soh/soh/Enhancements/randomizer/3drando/rando_main.hpp @@ -1,8 +1,9 @@ #pragma once + #include "soh/Enhancements/randomizer/item.h" #include namespace RandoMain { void GenerateRando(std::set excludedLocations, std::set enabledTricks, std::string seedInput); -} +} \ No newline at end of file diff --git a/soh/soh/Enhancements/randomizer/3drando/shops.hpp b/soh/soh/Enhancements/randomizer/3drando/shops.hpp index 81c4a4358..4cf780e83 100644 --- a/soh/soh/Enhancements/randomizer/3drando/shops.hpp +++ b/soh/soh/Enhancements/randomizer/3drando/shops.hpp @@ -1,5 +1,5 @@ #pragma once -#include "../context.h" +#include "../SeedContext.h" #include #include diff --git a/soh/soh/Enhancements/randomizer/3drando/spoiler_log.cpp b/soh/soh/Enhancements/randomizer/3drando/spoiler_log.cpp index 85225e2b1..c959c1ab4 100644 --- a/soh/soh/Enhancements/randomizer/3drando/spoiler_log.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/spoiler_log.cpp @@ -160,7 +160,7 @@ std::string RemoveLineBreaks(std::string s) { static void WriteExcludedLocations() { auto ctx = Rando::Context::GetInstance(); - for (size_t i = 1; i < Rando::Settings::GetInstance()->GetExcludeLocationsOptions().size(); i++) { + for (size_t i = 0; i < Rando::Settings::GetInstance()->GetExcludeLocationsOptions().size() - 1; i++) { for (const auto& location : Rando::Settings::GetInstance()->GetExcludeLocationsOptions()[i]) { if (ctx->GetLocationOption(static_cast(location->GetKey())).Get() == RO_LOCATION_INCLUDE) { continue; diff --git a/soh/soh/Enhancements/randomizer/3drando/starting_inventory.cpp b/soh/soh/Enhancements/randomizer/3drando/starting_inventory.cpp index f09e2f5c0..45f0cfad5 100644 --- a/soh/soh/Enhancements/randomizer/3drando/starting_inventory.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/starting_inventory.cpp @@ -1,7 +1,7 @@ #include "starting_inventory.hpp" #include "../dungeon.h" -#include "../context.h" +#include "../SeedContext.h" #include "../logic.h" #include "pool_functions.hpp" #include "soh/Enhancements/randomizer/static_data.h" diff --git a/soh/soh/Enhancements/randomizer/context.cpp b/soh/soh/Enhancements/randomizer/SeedContext.cpp similarity index 99% rename from soh/soh/Enhancements/randomizer/context.cpp rename to soh/soh/Enhancements/randomizer/SeedContext.cpp index 7dff59ae6..054f92b2a 100644 --- a/soh/soh/Enhancements/randomizer/context.cpp +++ b/soh/soh/Enhancements/randomizer/SeedContext.cpp @@ -1,4 +1,4 @@ -#include "context.h" +#include "SeedContext.h" #include "static_data.h" #include "soh/OTRGlobals.h" #include "soh/Enhancements/item-tables/ItemTableManager.h" @@ -620,4 +620,4 @@ uint32_t Context::GetSeed() const { void Context::SetSeed(const uint32_t seed) { mFinalSeed = seed; } -} // namespace Rando +} // namespace Rando \ No newline at end of file diff --git a/soh/soh/Enhancements/randomizer/context.h b/soh/soh/Enhancements/randomizer/SeedContext.h similarity index 100% rename from soh/soh/Enhancements/randomizer/context.h rename to soh/soh/Enhancements/randomizer/SeedContext.h diff --git a/soh/soh/Enhancements/randomizer/dungeon.cpp b/soh/soh/Enhancements/randomizer/dungeon.cpp index 1eb88ed73..cf37b5f39 100644 --- a/soh/soh/Enhancements/randomizer/dungeon.cpp +++ b/soh/soh/Enhancements/randomizer/dungeon.cpp @@ -2,7 +2,7 @@ #include "3drando/pool_functions.hpp" #include "static_data.h" -#include "context.h" +#include "SeedContext.h" namespace Rando { DungeonInfo::DungeonInfo(std::string name_, const RandomizerHintTextKey hintKey_, const RandomizerGet map_, diff --git a/soh/soh/Enhancements/randomizer/entrance.h b/soh/soh/Enhancements/randomizer/entrance.h index a40ea031b..21afd19b3 100644 --- a/soh/soh/Enhancements/randomizer/entrance.h +++ b/soh/soh/Enhancements/randomizer/entrance.h @@ -1,4 +1,5 @@ #pragma once + #ifdef __cplusplus #include "randomizerTypes.h" @@ -157,4 +158,4 @@ extern "C" { EntranceOverride* Randomizer_GetEntranceOverrides(); #ifdef __cplusplus } -#endif +#endif \ No newline at end of file diff --git a/soh/soh/Enhancements/randomizer/hint.cpp b/soh/soh/Enhancements/randomizer/hint.cpp index 27ca4eb2a..f0ce3dd3b 100644 --- a/soh/soh/Enhancements/randomizer/hint.cpp +++ b/soh/soh/Enhancements/randomizer/hint.cpp @@ -1,7 +1,7 @@ #include "hint.h" #include "map" #include "string" -#include "context.h" +#include "SeedContext.h" #include #include "static_data.h" diff --git a/soh/soh/Enhancements/randomizer/item.cpp b/soh/soh/Enhancements/randomizer/item.cpp index abe824572..08539f9ff 100644 --- a/soh/soh/Enhancements/randomizer/item.cpp +++ b/soh/soh/Enhancements/randomizer/item.cpp @@ -1,7 +1,7 @@ #include "item.h" #include "item_location.h" -#include "context.h" +#include "SeedContext.h" #include "logic.h" #include "3drando/item_pool.hpp" #include "z64item.h" diff --git a/soh/soh/Enhancements/randomizer/item_list.cpp b/soh/soh/Enhancements/randomizer/item_list.cpp index 10c2eea91..fb071fcdf 100644 --- a/soh/soh/Enhancements/randomizer/item_list.cpp +++ b/soh/soh/Enhancements/randomizer/item_list.cpp @@ -1,5 +1,5 @@ #include "static_data.h" -#include "context.h" +#include "SeedContext.h" #include "logic.h" #include "z64object.h" #include "soh/Enhancements/custom-message/CustomMessageTypes.h" diff --git a/soh/soh/Enhancements/randomizer/item_location.cpp b/soh/soh/Enhancements/randomizer/item_location.cpp index b5325c604..5835a8685 100644 --- a/soh/soh/Enhancements/randomizer/item_location.cpp +++ b/soh/soh/Enhancements/randomizer/item_location.cpp @@ -1,5 +1,5 @@ #include "item_location.h" -#include "context.h" +#include "SeedContext.h" #include "logic.h" namespace Rando { diff --git a/soh/soh/Enhancements/randomizer/location_access.cpp b/soh/soh/Enhancements/randomizer/location_access.cpp index ea036542f..03f59220d 100644 --- a/soh/soh/Enhancements/randomizer/location_access.cpp +++ b/soh/soh/Enhancements/randomizer/location_access.cpp @@ -2,7 +2,7 @@ #include "soh/Enhancements/randomizer/dungeon.h" #include "soh/Enhancements/randomizer/static_data.h" -#include "soh/Enhancements/randomizer/context.h" +#include "soh/Enhancements/randomizer/SeedContext.h" #include "soh/Enhancements/randomizer/3drando/item_pool.hpp" #include "soh/Enhancements/randomizer/3drando/spoiler_log.hpp" #include "soh/Enhancements/randomizer/trial.h" diff --git a/soh/soh/Enhancements/randomizer/location_access.h b/soh/soh/Enhancements/randomizer/location_access.h index 33dddc1db..4bb1db613 100644 --- a/soh/soh/Enhancements/randomizer/location_access.h +++ b/soh/soh/Enhancements/randomizer/location_access.h @@ -6,7 +6,7 @@ #include #include "soh/Enhancements/randomizer/randomizerTypes.h" -#include "soh/Enhancements/randomizer/context.h" +#include "soh/Enhancements/randomizer/SeedContext.h" #include "soh/Enhancements/randomizer/logic.h" #define TIME_PASSES true diff --git a/soh/soh/Enhancements/randomizer/location_list.cpp b/soh/soh/Enhancements/randomizer/location_list.cpp index e3eebf757..0d81ac78b 100644 --- a/soh/soh/Enhancements/randomizer/location_list.cpp +++ b/soh/soh/Enhancements/randomizer/location_list.cpp @@ -1,6 +1,6 @@ #include "static_data.h" #include "z64save.h" -#include "context.h" +#include "SeedContext.h" #include "dungeon.h" std::array Rando::StaticData::locationTable; diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index 215e0dfbe..939fac070 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -7,7 +7,7 @@ #include "soh/OTRGlobals.h" #include "dungeon.h" -#include "context.h" +#include "SeedContext.h" #include "macros.h" #include "variables.h" #include diff --git a/soh/soh/Enhancements/randomizer/logic.h b/soh/soh/Enhancements/randomizer/logic.h index 0541072a6..858e2e580 100644 --- a/soh/soh/Enhancements/randomizer/logic.h +++ b/soh/soh/Enhancements/randomizer/logic.h @@ -1,7 +1,7 @@ #pragma once #include "randomizerTypes.h" -#include "context.h" +#include "SeedContext.h" #include namespace Rando { diff --git a/soh/soh/Enhancements/randomizer/option.cpp b/soh/soh/Enhancements/randomizer/option.cpp index bfdecd861..87b767355 100644 --- a/soh/soh/Enhancements/randomizer/option.cpp +++ b/soh/soh/Enhancements/randomizer/option.cpp @@ -2,34 +2,60 @@ #include "libultraship/bridge.h" #include #include +#include "soh/Enhancements/randomizer/settings.h" #include "soh/SohGui/SohGui.hpp" +#include "soh/SohGui/SohMenu.h" #include "soh/SohGui/UIWidgets.hpp" #include +namespace SohGui { +extern std::shared_ptr mSohMenu; +} + namespace Rando { Option Option::Bool(RandomizerSettingKey key_, std::string name_, std::vector options_, const OptionCategory category_, std::string cvarName_, std::string description_, - WidgetType widgetType_, const uint8_t defaultOption_, const bool defaultHidden_, int imFlags_) { - return { static_cast(key_), std::move(name_), std::move(options_), category_, std::move(cvarName_), - std::move(description_), widgetType_, defaultOption_, defaultHidden_, imFlags_ }; + WidgetType widgetType_, const uint8_t defaultOption_, const bool defaultHidden_, + WidgetFunc callback_, int imFlags_) { + return { static_cast(key_), + std::move(name_), + std::move(options_), + category_, + std::move(cvarName_), + std::move(description_), + widgetType_, + defaultOption_, + defaultHidden_, + callback_, + imFlags_ }; } Option Option::Bool(RandomizerSettingKey key_, std::string name_, std::string cvarName_, std::string description_, - const int imFlags_, const WidgetType widgetType_, const bool defaultOption_) { + const int imFlags_, const WidgetType widgetType_, const bool defaultOption_, WidgetFunc callback_) { return Option(key_, std::move(name_), { "Off", "On" }, OptionCategory::Setting, std::move(cvarName_), - std::move(description_), widgetType_, defaultOption_, false, imFlags_); + std::move(description_), widgetType_, defaultOption_, false, callback_, imFlags_); } Option Option::U8(RandomizerSettingKey key_, std::string name_, std::vector options_, const OptionCategory category_, std::string cvarName_, std::string description_, - WidgetType widgetType_, const uint8_t defaultOption_, const bool defaultHidden_, int imFlags_) { - return { static_cast(key_), std::move(name_), std::move(options_), category_, std::move(cvarName_), - std::move(description_), widgetType_, defaultOption_, defaultHidden_, imFlags_ }; + WidgetType widgetType_, const uint8_t defaultOption_, const bool defaultHidden_, WidgetFunc callback_, + int imFlags_) { + return { static_cast(key_), + std::move(name_), + std::move(options_), + category_, + std::move(cvarName_), + std::move(description_), + widgetType_, + defaultOption_, + defaultHidden_, + callback_, + imFlags_ }; } Option Option::LogicTrick(RandomizerTrick rt_, std::string name_) { return Option(rt_, std::move(name_), { "Disabled", "Enabled" }, OptionCategory::Setting, "", "", - WidgetType::Checkbox, 0, false, IMFLAG_NONE); + WIDGET_CVAR_CHECKBOX, 0, false, nullptr, IMFLAG_NONE); } OptionValue::OptionValue(uint8_t val) : mVal(val) { @@ -125,24 +151,6 @@ bool Option::IsCategory(const OptionCategory category) const { return category == this->category; } -bool Option::RenderImGui() { - bool changed = false; - ImGui::BeginGroup(); - switch (widgetType) { - case WidgetType::Checkbox: - changed = RenderCheckbox(); - break; - case WidgetType::Combobox: - changed = RenderCombobox(); - break; - case WidgetType::Slider: - changed = RenderSlider(); - break; - } - ImGui::EndGroup(); - return changed; -} - bool Option::HasFlag(const int imFlag_) const { return imFlag_ & imFlags; } @@ -180,12 +188,52 @@ void Option::SetContextIndexFromText(const std::string text) { Option::Option(size_t key_, std::string name_, std::vector options_, OptionCategory category_, std::string cvarName_, std::string description_, WidgetType widgetType_, uint8_t defaultOption_, - bool defaultHidden_, int imFlags_) + bool defaultHidden_, WidgetFunc callback_, int imFlags_) : key(key_), name(std::move(name_)), options(std::move(options_)), category(category_), cvarName(std::move(cvarName_)), description(std::move(description_)), widgetType(widgetType_), - defaultOption(defaultOption_), defaultHidden(defaultHidden_), imFlags(imFlags_) { + defaultOption(defaultOption_), defaultHidden(defaultHidden_), callback(callback_), imFlags(imFlags_) { contextSelection = defaultOption; hidden = defaultHidden; + for (int i = 0; i < options.size(); i++) { + optionsMap.emplace(i, options[i].c_str()); + } + UIWidgets::LabelPositions labelPosition; + switch (widgetType) { + case WIDGET_CVAR_CHECKBOX: + // labelPosition = UIWidgets::LabelPositions::Near; + // if (imFlags_ & IMFLAG_LABEL_INLINE) { + // labelPosition = UIWidgets::LabelPositions::Near; + // } + widgetOptions = std::make_shared( + UIWidgets::CheckboxOptions().DefaultValue(defaultOption).Tooltip(description.c_str())); + break; + case WIDGET_CVAR_COMBOBOX: + labelPosition = UIWidgets::LabelPositions::Above; + if (imFlags_ & IMFLAG_LABEL_INLINE) { + labelPosition = UIWidgets::LabelPositions::Near; + } + widgetOptions = std::make_shared(UIWidgets::ComboboxOptions() + .DefaultIndex(defaultOption) + .ComboMap(optionsMap) + .Tooltip(description.c_str()) + .LabelPosition(labelPosition)); + break; + case WIDGET_CVAR_SLIDER_INT: + labelPosition = UIWidgets::LabelPositions::Above; + if (imFlags_ & IMFLAG_LABEL_INLINE) { + labelPosition = UIWidgets::LabelPositions::Near; + } + widgetOptions = std::make_shared(UIWidgets::IntSliderOptions() + .DefaultValue(defaultOption) + .Tooltip(description.c_str()) + .Min(0) + .Max(options.size() - 1) + .Format(options[defaultOption].c_str()) + .LabelPosition(labelPosition)); + break; + default: + break; + } PopulateTextToNum(); } @@ -260,15 +308,49 @@ bool Option::RenderSlider() { return changed; } +void Option::AddWidget(WidgetPath& path) { + auto widget = SohGui::mSohMenu->AddWidget(path, name, widgetType) + .Callback(callback) + .PreFunc([this](WidgetInfo& info) { + info.isHidden = this->IsHidden(); + info.options->disabled = this->disabled; + info.options->disabledTooltip = this->disabledText.c_str(); + info.options->tooltip = this->description.c_str(); + if (info.type == WIDGET_CVAR_SLIDER_INT) { + UIWidgets::IntSliderOptions* sliderOpts = + (UIWidgets::IntSliderOptions*)info.options.get(); + sliderOpts->Format(this->GetOptionText(this->GetOptionIndex()).c_str()); + sliderOpts->Max(this->options.size() - 1); + } + }) + .CVar(cvarName.c_str()) + .Options(widgetOptions) + .SameLine(imFlags & IMFLAG_SAME_LINE); + widgetInfo = widget; + if (callback != nullptr) { + callback(widgetInfo); + } +} + void Option::PopulateTextToNum() { for (uint8_t count = 0; count < options.size(); count++) { optionsTextToVar[options[count]] = count; } } +void Option::SetCallback(WidgetFunc callback) { + this->callback = callback; +} + +void Option::RunCallback() { + if (callback != nullptr) { + this->callback(widgetInfo); + } +} + LocationOption::LocationOption(RandomizerCheck key_, const std::string& name_) - : Option(key_, name_, { "Included", "Excluded" }, OptionCategory::Setting, "", "", WidgetType::Checkbox, - RO_LOCATION_INCLUDE, false, IMFLAG_NONE) { + : Option(key_, name_, { "Included", "Excluded" }, OptionCategory::Setting, "", "", WIDGET_CVAR_CHECKBOX, + RO_LOCATION_INCLUDE, false, nullptr, IMFLAG_NONE) { } RandomizerCheck LocationOption::GetKey() const { @@ -278,7 +360,7 @@ RandomizerCheck LocationOption::GetKey() const { TrickOption::TrickOption(RandomizerTrick key_, const RandomizerCheckQuest quest_, const RandomizerArea area_, std::set tags_, const std::string& name_, std::string description_) : Option(key_, name_, { "Disabled", "Enabled" }, OptionCategory::Setting, "", std::move(description_), - WidgetType::Checkbox, 0, false, IMFLAG_NONE), + WIDGET_CVAR_CHECKBOX, 0, false, nullptr, IMFLAG_NONE), mQuest(quest_), mArea(area_), mTags(std::move(tags_)) { } @@ -365,75 +447,29 @@ void OptionGroup::Disable() { mDisabled = true; } -bool OptionGroup::RenderImGui() const { // NOLINT(*-no-recursion) - ImGuiWindow* window = ImGui::GetCurrentWindow(); - bool changed = false; - ImGui::BeginDisabled(mDisabled || CVarGetInteger(CVAR_SETTING("DisableChanges"), 0) || - CVarGetInteger(CVAR_GENERAL("RandoGenerating"), 0) || - CVarGetInteger(CVAR_GENERAL("OnFileSelectNameEntry"), 0)); +void OptionGroup::AddWidgets(WidgetPath& path) const { if (mContainerType == WidgetContainerType::TABLE) { - if (ImGui::BeginTable(mName.c_str(), static_cast(mSubGroups.size()), - ImGuiTableFlags_BordersH | ImGuiTableFlags_BordersV)) { - for (const auto column : mSubGroups) { - if (column->GetContainerType() == WidgetContainerType::COLUMN) { - ImGui::TableSetupColumn(column->GetName().c_str(), ImGuiTableColumnFlags_WidthStretch, 200.0f); - } - } - ImGui::PushItemFlag(ImGuiItemFlags_NoNav, true); - ImGui::TableNextRow(ImGuiTableRowFlags_Headers); - for (int i = 0; i < mSubGroups.size(); i++) { - ImGui::TableSetColumnIndex(i); - ImGui::TableHeader(mSubGroups[i]->GetName().c_str()); - if (!mSubGroups[i]->GetDescription().empty()) { - UIWidgets::Tooltip(mSubGroups[i]->GetDescription().c_str()); - } - } - ImGui::PopItemFlag(); - ImGui::TableNextRow(); - } + path.column = SECTION_COLUMN_1; + path.sidebarName = mName; + SohGui::mSohMenu->AddSidebarEntry("Randomizer", path.sidebarName, mSubGroups.size()); } - if (mContainerType == WidgetContainerType::SECTION && !mName.empty()) { - ImGui::SeparatorText(mName.c_str()); - if (!mDescription.empty()) { - UIWidgets::Tooltip(mDescription.c_str()); + if (mContainerType == WidgetContainerType::SECTION || mContainerType == WidgetContainerType::COLUMN) { + if (!mName.empty()) { + SohGui::mSohMenu->AddWidget(path, mName.c_str(), WIDGET_SEPARATOR_TEXT); } } - if (mContainerType == WidgetContainerType::COLUMN) { - ImGui::TableNextColumn(); - window->DC.CurrLineTextBaseOffset = 0.0f; - ImGui::BeginChild(mName.c_str(), ImVec2(0, -8)); - ImGui::PushItemWidth(-FLT_MIN); - } if (mContainsType == OptionGroupType::SUBGROUP) { for (const auto optionGroup : mSubGroups) { - if (optionGroup->RenderImGui()) { - changed = true; - } + optionGroup->AddWidgets(path); } } else { for (const auto option : mOptions) { - if (option->IsHidden()) { - continue; - } - if (option->HasFlag(IMFLAG_INDENT)) { - ImGui::Indent(); - } - // If any options changed, changed will end up being true - if (option->RenderImGui()) { - changed = true; - } - if (option->HasFlag(IMFLAG_UNINDENT)) { - ImGui::Unindent(); - } + option->AddWidget(path); } } if (mContainerType == WidgetContainerType::COLUMN) { - ImGui::EndChild(); + assert(path.column < 3); + path.column = static_cast(path.column + 1); } - if (mContainerType == WidgetContainerType::TABLE) { - ImGui::EndTable(); - } - ImGui::EndDisabled(); - return changed; } } // namespace Rando \ No newline at end of file diff --git a/soh/soh/Enhancements/randomizer/option.h b/soh/soh/Enhancements/randomizer/option.h index 55a06ed86..f4e814088 100644 --- a/soh/soh/Enhancements/randomizer/option.h +++ b/soh/soh/Enhancements/randomizer/option.h @@ -8,11 +8,11 @@ #include #include #include -#include #include #include "randomizerTypes.h" #include "tricks.h" +#include "soh/SohGui/MenuTypes.h" namespace Rando { enum ImGuiMenuFlags { @@ -21,6 +21,8 @@ enum ImGuiMenuFlags { IMFLAG_SEPARATOR_TOP = 1 << 1, /** Adds a padded separator above the widget. */ IMFLAG_INDENT = 1 << 2, /** Indents this widget and all proceeding widgets. */ IMFLAG_UNINDENT = 1 << 3, /** Unindents this widget and all proceeding widgets. */ + IMFLAG_SAME_LINE = 1 << 4, + IMFLAG_LABEL_INLINE = 1 << 5, }; /** @@ -33,16 +35,6 @@ enum class OptionCategory { for randomizing the values of other options. */ }; -/** - * @brief Controls how this option is rendered in the menu. - */ -enum class WidgetType { - Checkbox, /** Default for Bools, not compatible if options.size() > 2. */ - Combobox, /** Default for U8s, works with U8s and Bools. */ - Slider, /** Compatible with U8s. If constructed with NumOpts, consider using this. Technically can be used for Bool - or non-NumOpts options but it would be a bit weird semantically. */ -}; - class OptionValue { public: OptionValue() = default; @@ -127,8 +119,9 @@ class Option { static Option Bool(RandomizerSettingKey key_, std::string name_, std::vector options_ = { "Off", "On" }, OptionCategory category_ = OptionCategory::Setting, std::string cvarName_ = "", - std::string description_ = "", WidgetType widgetType_ = WidgetType::Checkbox, - uint8_t defaultOption_ = 0, bool defaultHidden_ = false, int imFlags_ = IMFLAG_SEPARATOR_BOTTOM); + std::string description_ = "", WidgetType widgetType_ = WIDGET_CVAR_CHECKBOX, + uint8_t defaultOption_ = 0, bool defaultHidden_ = false, WidgetFunc callback_ = nullptr, + int imFlags_ = IMFLAG_SEPARATOR_BOTTOM); /** * @brief Constructs a boolean option. This constructor was added later for convenience so that a cvarName @@ -150,7 +143,8 @@ class Option { */ static Option Bool(RandomizerSettingKey key_, std::string name_, std::string cvarName_, std::string description_ = "", int imFlags_ = IMFLAG_SEPARATOR_BOTTOM, - WidgetType widgetType_ = WidgetType::Checkbox, bool defaultOption_ = false); + WidgetType widgetType_ = WIDGET_CVAR_CHECKBOX, bool defaultOption_ = false, + WidgetFunc callback_ = nullptr); /** * @brief Constructs a U8 Option. @@ -175,8 +169,9 @@ class Option { */ static Option U8(RandomizerSettingKey key_, std::string name_, std::vector options_, OptionCategory category_ = OptionCategory::Setting, std::string cvarName_ = "", - std::string description_ = "", WidgetType widgetType_ = WidgetType::Combobox, - uint8_t defaultOption_ = 0, bool defaultHidden_ = false, int imFlags_ = IMFLAG_SEPARATOR_BOTTOM); + std::string description_ = "", WidgetType widgetType_ = WIDGET_CVAR_COMBOBOX, + uint8_t defaultOption_ = 0, bool defaultHidden_ = false, WidgetFunc callback_ = nullptr, + int imFlags_ = IMFLAG_SEPARATOR_BOTTOM); /** * @brief A convenience function for constructing the Option for a trick. @@ -299,13 +294,7 @@ class Option { void Disable(std::string text); bool IsCategory(OptionCategory category) const; - /** - * @brief Automatically renders a widget for this option in ImGui, based on the various - * properties of this Option. Typically, Bool options are rendered as Checkboxes and - * U8 options are rendered as Comboboxes, but this can be overridden during construction with - * the `widgetType` property. - */ - bool RenderImGui(); + void AddWidget(WidgetPath& path); bool HasFlag(int imFlag_) const; void AddFlag(int imFlag_); @@ -315,10 +304,13 @@ class Option { uint8_t GetValueFromText(std::string text); void SetContextIndexFromText(std::string text); + void SetCallback(WidgetFunc callback); + void RunCallback(); + protected: Option(size_t key_, std::string name_, std::vector options_, OptionCategory category_, std::string cvarName_, std::string description_, WidgetType widgetType_, uint8_t defaultOption_, - bool defaultHidden_, int imFlags_); + bool defaultHidden_, WidgetFunc callback_, int imFlags_); size_t key; private: @@ -334,13 +326,17 @@ class Option { OptionCategory category = OptionCategory::Setting; std::string cvarName; std::string description; - WidgetType widgetType = WidgetType::Checkbox; + WidgetType widgetType; uint8_t defaultOption = false; bool defaultHidden = false; int imFlags = IMFLAG_NONE; bool disabled = false; std::string disabledText; std::unordered_map optionsTextToVar = {}; + std::shared_ptr widgetOptions; + struct WidgetInfo widgetInfo; + WidgetFunc callback; + std::unordered_map optionsMap = {}; }; class LocationOption : public Option { @@ -515,10 +511,7 @@ class OptionGroup { const std::string& GetDescription() const; - /** - * @brief Renders all of the options contained within this `OptionGroup` in the ImGui menu. - */ - bool RenderImGui() const; + void AddWidgets(WidgetPath& path) const; void Disable(); void Enable(); diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index 0c5086b52..77e8c0b3c 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -31,7 +31,6 @@ #include "soh/SohGui/UIWidgets.hpp" #include "static_data.h" #include "soh/Enhancements/game-interactor/GameInteractor.h" -#include "randomizer_settings_window.h" #include "savefile.h" #include "entrance.h" #include "dungeon.h" @@ -4021,672 +4020,11 @@ bool GenerateRandomizer(std::string seed /*= ""*/) { static bool locationsTabOpen = false; static bool tricksTabOpen = false; -void RandomizerSettingsWindow::DrawElement() { - auto ctx = Rando::Context::GetInstance(); +void JoinRandoGenerationThread() { if (generated) { generated = 0; randoThread.join(); } - bool generating = CVarGetInteger(CVAR_GENERAL("RandoGenerating"), 0); - bool disableEditingRandoSettings = generating || CVarGetInteger(CVAR_GENERAL("OnFileSelectNameEntry"), 0); - - DrawPresetSelector({ PRESET_SECTION_RANDOMIZER }, "Randomizer", generating); - - // UIWidgets::Spacer(0); - UIWidgets::CVarCheckbox("Manual seed entry", CVAR_RANDOMIZER_SETTING("ManualSeedEntry"), - UIWidgets::CheckboxOptions().Color(THEME_COLOR)); - if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ManualSeedEntry"), 0)) { - UIWidgets::PushStyleInput(THEME_COLOR); - ImGui::InputText("##RandomizerSeed", seedString, MAX_SEED_STRING_SIZE, ImGuiInputTextFlags_CallbackCharFilter, - UIWidgets::TextFilters::FilterAlphaNum); - UIWidgets::Tooltip("Characters from a-z, A-Z, and 0-9 are supported.\n" - "Character limit is 1023, after which the seed will be truncated.\n"); - ImGui::SameLine(); - if (UIWidgets::Button( - ICON_FA_RANDOM, - UIWidgets::ButtonOptions() - .Size(UIWidgets::Sizes::Inline) - .Color(THEME_COLOR) - .Padding(ImVec2(10.f, 6.f)) - .Tooltip("Creates a new random seed value to be used when generating a randomizer"))) { - SohUtils::CopyStringToCharArray(seedString, std::to_string(rand() & 0xFFFFFFFF), MAX_SEED_STRING_SIZE); - } - ImGui::SameLine(); - if (UIWidgets::Button(ICON_FA_ERASER, UIWidgets::ButtonOptions() - .Size(UIWidgets::Sizes::Inline) - .Color(THEME_COLOR) - .Padding(ImVec2(10.f, 6.f)))) { - memset(seedString, 0, MAX_SEED_STRING_SIZE); - } - if (strnlen(seedString, MAX_SEED_STRING_SIZE) == 0) { - ImGui::SameLine(17.0f); - ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, 0.4f), "Leave blank for random seed"); - } - UIWidgets::PopStyleInput(); - } - - UIWidgets::Spacer(0); - UIWidgets::ButtonOptions options = UIWidgets::ButtonOptions().Size(ImVec2(250.f, 0.f)).Color(THEME_COLOR); - options.Disabled((gSaveContext.gameMode != GAMEMODE_FILE_SELECT) || GameInteractor::IsSaveLoaded()); - if (options.disabled) { - options.DisabledTooltip("Must be on File Select to generate a randomizer seed."); - } - if (UIWidgets::Button("Generate Randomizer", options)) { - ctx->SetSpoilerLoaded(false); - GenerateRandomizer(CVarGetInteger(CVAR_RANDOMIZER_SETTING("ManualSeedEntry"), 0) ? seedString : ""); - } - - ImGui::SameLine(); - if (!CVarGetInteger(CVAR_RANDOMIZER_SETTING("DontGenerateSpoiler"), 0)) { - std::string spoilerfilepath = CVarGetString(CVAR_GENERAL("SpoilerLog"), ""); - ImGui::Text("Spoiler File: %s", spoilerfilepath.c_str()); - } - - UIWidgets::Separator(true, true, 0.f, 0.f); - - ImGuiWindow* window = ImGui::GetCurrentWindow(); - static ImVec2 cellPadding(8.0f, 8.0f); - - UIWidgets::PushStyleTabs(THEME_COLOR); - if (ImGui::BeginTabBar("Randomizer Settings", ImGuiTabBarFlags_NoCloseWithMiddleMouseButton)) { - if (ImGui::BeginTabItem("World")) { - ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, cellPadding); - if (mSettings->GetOptionGroup(RSG_WORLD_IMGUI_TABLE).RenderImGui()) { - mNeedsUpdate = true; - } - ImGui::PopStyleVar(1); - ImGui::EndTabItem(); - } - - if (ImGui::BeginTabItem("Items")) { - ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, cellPadding); - if (mSettings->GetOptionGroup(RSG_ITEMS_IMGUI_TABLE).RenderImGui()) { - mNeedsUpdate = true; - } - ImGui::PopStyleVar(1); - ImGui::EndTabItem(); - } - - if (ImGui::BeginTabItem("Gameplay")) { - ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, cellPadding); - if (mSettings->GetOptionGroup(RSG_GAMEPLAY_IMGUI_TABLE).RenderImGui()) { - mNeedsUpdate = true; - } - ImGui::PopStyleVar(1); - ImGui::EndTabItem(); - } - - if (ImGui::BeginTabItem("Locations")) { - ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0) || disableEditingRandoSettings); - ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, cellPadding); - if (!locationsTabOpen) { - locationsTabOpen = true; - RandomizerCheckObjects::UpdateImGuiVisibility(); - // todo: this efficently when we build out cvar array support - std::stringstream excludedLocationStringStream( - CVarGetString(CVAR_RANDOMIZER_SETTING("ExcludedLocations"), "")); - std::string excludedLocationString; - excludedLocations.clear(); - while (getline(excludedLocationStringStream, excludedLocationString, ',')) { - excludedLocations.insert((RandomizerCheck)std::stoi(excludedLocationString)); - } - } - - if (ImGui::BeginTable("tableRandoLocations", 2, ImGuiTableFlags_BordersH | ImGuiTableFlags_BordersV)) { - ImGui::TableSetupColumn("Included", ImGuiTableColumnFlags_WidthStretch, 200.0f); - ImGui::TableSetupColumn("Excluded", ImGuiTableColumnFlags_WidthStretch, 200.0f); - ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); - ImGui::TableHeadersRow(); - ImGui::PopItemFlag(); - ImGui::TableNextRow(); - - // COLUMN 1 - INCLUDED LOCATIONS - ImGui::TableNextColumn(); - window->DC.CurrLineTextBaseOffset = 0.0f; - - static ImGuiTextFilter locationSearch; - UIWidgets::PushStyleInput(THEME_COLOR); - locationSearch.Draw(); - UIWidgets::PopStyleInput(); - - ImGui::BeginChild("ChildIncludedLocations", ImVec2(0, -8)); - for (auto& [rcArea, locations] : RandomizerCheckObjects::GetAllRCObjectsByArea()) { - bool hasItems = false; - for (RandomizerCheck rc : locations) { - if (ctx->GetItemLocation(rc)->IsVisible() && !excludedLocations.count(rc) && - locationSearch.PassFilter(Rando::StaticData::GetLocation(rc)->GetName().c_str())) { - - hasItems = true; - break; - } - } - - if (hasItems) { - ImGui::SetNextItemOpen(true, ImGuiCond_Once); - if (ImGui::TreeNode(RandomizerCheckObjects::GetRCAreaName(rcArea).c_str())) { - for (auto& location : locations) { - if (ctx->GetItemLocation(location)->IsVisible() && !excludedLocations.count(location) && - locationSearch.PassFilter( - Rando::StaticData::GetLocation(location)->GetName().c_str())) { - UIWidgets::PushStyleButton(THEME_COLOR, ImVec2(7.f, 5.f)); - if (ImGui::ArrowButton(std::to_string(location).c_str(), ImGuiDir_Right)) { - excludedLocations.insert(location); - // todo: this efficently when we build out cvar array support - std::string excludedLocationString = ""; - for (auto excludedLocationIt : excludedLocations) { - excludedLocationString += std::to_string(excludedLocationIt); - excludedLocationString += ","; - } - CVarSetString(CVAR_RANDOMIZER_SETTING("ExcludedLocations"), - excludedLocationString.c_str()); - Ship::Context::GetInstance() - ->GetWindow() - ->GetGui() - ->SaveConsoleVariablesNextFrame(); - } - UIWidgets::PopStyleButton(); - ImGui::SameLine(); - ImGui::Text("%s", Rando::StaticData::GetLocation(location)->GetShortName().c_str()); - } - } - ImGui::TreePop(); - } - } - } - ImGui::EndChild(); - - // COLUMN 2 - EXCLUDED LOCATIONS - ImGui::TableNextColumn(); - window->DC.CurrLineTextBaseOffset = 0.0f; - - ImGui::BeginChild("ChildExcludedLocations", ImVec2(0, -8)); - for (auto& [rcArea, locations] : RandomizerCheckObjects::GetAllRCObjectsByArea()) { - bool hasItems = false; - for (RandomizerCheck rc : locations) { - if (ctx->GetItemLocation(rc)->IsVisible() && excludedLocations.count(rc)) { - hasItems = true; - break; - } - } - - if (hasItems) { - ImGui::SetNextItemOpen(true, ImGuiCond_Once); - if (ImGui::TreeNode(RandomizerCheckObjects::GetRCAreaName(rcArea).c_str())) { - for (auto& location : locations) { - auto elfound = excludedLocations.find(location); - if (ctx->GetItemLocation(location)->IsVisible() && elfound != excludedLocations.end()) { - UIWidgets::PushStyleButton(THEME_COLOR, ImVec2(7.f, 5.f)); - if (ImGui::ArrowButton(std::to_string(location).c_str(), ImGuiDir_Left)) { - excludedLocations.erase(elfound); - // todo: this efficently when we build out cvar array support - std::string excludedLocationString = ""; - for (auto excludedLocationIt : excludedLocations) { - excludedLocationString += std::to_string(excludedLocationIt); - excludedLocationString += ","; - } - if (excludedLocationString == "") { - CVarClear(CVAR_RANDOMIZER_SETTING("ExcludedLocations")); - } else { - CVarSetString(CVAR_RANDOMIZER_SETTING("ExcludedLocations"), - excludedLocationString.c_str()); - } - Ship::Context::GetInstance() - ->GetWindow() - ->GetGui() - ->SaveConsoleVariablesNextFrame(); - } - UIWidgets::PopStyleButton(); - ImGui::SameLine(); - ImGui::Text("%s", Rando::StaticData::GetLocation(location)->GetShortName().c_str()); - } - } - ImGui::TreePop(); - } - } - } - ImGui::EndChild(); - - ImGui::EndTable(); - } - ImGui::PopStyleVar(1); - ImGui::EndTabItem(); - ImGui::EndDisabled(); - } else { - locationsTabOpen = false; - } - - if (ImGui::BeginTabItem("Tricks/Glitches")) { - if (!tricksTabOpen) { - tricksTabOpen = true; - // RandomizerTricks::UpdateImGuiVisibility(); - // todo: this efficently when we build out cvar array support - std::stringstream enabledTrickStringStream(CVarGetString(CVAR_RANDOMIZER_SETTING("EnabledTricks"), "")); - std::string enabledTrickString; - enabledTricks.clear(); - while (getline(enabledTrickStringStream, enabledTrickString, ',')) { - enabledTricks.insert((RandomizerTrick)std::stoi(enabledTrickString)); - } - std::stringstream enabledGlitchStringStream( - CVarGetString(CVAR_RANDOMIZER_SETTING("EnabledGlitches"), "")); - std::string enabledGlitchString; - enabledGlitches.clear(); - while (getline(enabledGlitchStringStream, enabledGlitchString, ',')) { - enabledGlitches.insert((RandomizerTrick)std::stoi(enabledGlitchString)); - } - } - - ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, cellPadding); - if (ImGui::BeginTable("tableRandoLogic", 1, ImGuiTableFlags_BordersH | ImGuiTableFlags_BordersV)) { - ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 200.0f); - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::PushItemWidth(170.0); - if (mSettings->GetOption(RSK_LOGIC_RULES).RenderImGui()) { - mNeedsUpdate = true; - } - // RANDOTODO: Implement Disalbling of Options for Vanilla Logic - if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("LogicRules"), RO_LOGIC_GLITCHLESS) == RO_LOGIC_GLITCHLESS) { - ImGui::SameLine(); - if (mSettings->GetOption(RSK_ALL_LOCATIONS_REACHABLE).RenderImGui()) { - mNeedsUpdate = true; - } - } - ImGui::PopItemWidth(); - ImGui::EndTable(); - } - - ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0) || disableEditingRandoSettings); - - // Tricks - static std::unordered_map areaTreeDisabled{ - { RA_NONE, true }, - { RA_KOKIRI_FOREST, true }, - { RA_THE_LOST_WOODS, true }, - { RA_SACRED_FOREST_MEADOW, true }, - { RA_HYRULE_FIELD, true }, - { RA_LAKE_HYLIA, true }, - { RA_GERUDO_VALLEY, true }, - { RA_GERUDO_FORTRESS, true }, - { RA_HAUNTED_WASTELAND, true }, - { RA_DESERT_COLOSSUS, true }, - { RA_THE_MARKET, true }, - { RA_HYRULE_CASTLE, true }, - { RA_KAKARIKO_VILLAGE, true }, - { RA_THE_GRAVEYARD, true }, - { RA_DEATH_MOUNTAIN_TRAIL, true }, - { RA_GORON_CITY, true }, - { RA_DEATH_MOUNTAIN_CRATER, true }, - { RA_ZORAS_RIVER, true }, - { RA_ZORAS_DOMAIN, true }, - { RA_ZORAS_FOUNTAIN, true }, - { RA_LON_LON_RANCH, true }, - { RA_DEKU_TREE, true }, - { RA_DODONGOS_CAVERN, true }, - { RA_JABU_JABUS_BELLY, true }, - { RA_FOREST_TEMPLE, true }, - { RA_FIRE_TEMPLE, true }, - { RA_WATER_TEMPLE, true }, - { RA_SPIRIT_TEMPLE, true }, - { RA_SHADOW_TEMPLE, true }, - { RA_BOTTOM_OF_THE_WELL, true }, - { RA_ICE_CAVERN, true }, - { RA_GERUDO_TRAINING_GROUND, true }, - { RA_GANONS_CASTLE, true }, - }; - static std::unordered_map areaTreeEnabled{ - { RA_NONE, true }, - { RA_KOKIRI_FOREST, true }, - { RA_THE_LOST_WOODS, true }, - { RA_SACRED_FOREST_MEADOW, true }, - { RA_HYRULE_FIELD, true }, - { RA_LAKE_HYLIA, true }, - { RA_GERUDO_VALLEY, true }, - { RA_GERUDO_FORTRESS, true }, - { RA_HAUNTED_WASTELAND, true }, - { RA_DESERT_COLOSSUS, true }, - { RA_THE_MARKET, true }, - { RA_HYRULE_CASTLE, true }, - { RA_KAKARIKO_VILLAGE, true }, - { RA_THE_GRAVEYARD, true }, - { RA_DEATH_MOUNTAIN_TRAIL, true }, - { RA_GORON_CITY, true }, - { RA_DEATH_MOUNTAIN_CRATER, true }, - { RA_ZORAS_RIVER, true }, - { RA_ZORAS_DOMAIN, true }, - { RA_ZORAS_FOUNTAIN, true }, - { RA_LON_LON_RANCH, true }, - { RA_DEKU_TREE, true }, - { RA_DODONGOS_CAVERN, true }, - { RA_JABU_JABUS_BELLY, true }, - { RA_FOREST_TEMPLE, true }, - { RA_FIRE_TEMPLE, true }, - { RA_WATER_TEMPLE, true }, - { RA_SPIRIT_TEMPLE, true }, - { RA_SHADOW_TEMPLE, true }, - { RA_BOTTOM_OF_THE_WELL, true }, - { RA_ICE_CAVERN, true }, - { RA_GERUDO_TRAINING_GROUND, true }, - { RA_GANONS_CASTLE, true }, - }; - - static std::map showTag{ - { Rando::Tricks::Tag::NOVICE, true }, { Rando::Tricks::Tag::INTERMEDIATE, true }, - { Rando::Tricks::Tag::ADVANCED, true }, { Rando::Tricks::Tag::EXPERT, true }, - { Rando::Tricks::Tag::EXTREME, true }, { Rando::Tricks::Tag::EXPERIMENTAL, true }, - { Rando::Tricks::Tag::GLITCH, false }, - }; - static ImGuiTextFilter trickSearch; - UIWidgets::PushStyleInput(THEME_COLOR); - trickSearch.Draw("Filter (inc,-exc)", 490.0f); - UIWidgets::PopStyleInput(); - if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("LogicRules"), RO_LOGIC_GLITCHLESS) != RO_LOGIC_NO_LOGIC) { - ImGui::SameLine(); - if (UIWidgets::Button("Disable All", - UIWidgets::ButtonOptions().Color(THEME_COLOR).Size(ImVec2(250.f, 0.f)))) { - for (int i = 0; i < RT_MAX; i++) { - auto etfound = enabledTricks.find(static_cast(i)); - if (etfound != enabledTricks.end()) { - enabledTricks.erase(etfound); - } - } - std::string enabledTrickString = ""; - for (auto enabledTrickIt : enabledTricks) { - enabledTrickString += std::to_string(enabledTrickIt); - enabledTrickString += ","; - } - CVarClear(CVAR_RANDOMIZER_SETTING("EnabledTricks")); - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); - } - ImGui::SameLine(); - if (UIWidgets::Button("Enable All", - UIWidgets::ButtonOptions().Color(THEME_COLOR).Size(ImVec2(250.f, 0.f)))) { - for (int i = 0; i < RT_MAX; i++) { - if (!enabledTricks.count(static_cast(i))) { - enabledTricks.insert(static_cast(i)); - } - } - std::string enabledTrickString = ""; - for (auto enabledTrickIt : enabledTricks) { - enabledTrickString += std::to_string(enabledTrickIt); - enabledTrickString += ","; - } - CVarSetString(CVAR_RANDOMIZER_SETTING("EnabledTricks"), enabledTrickString.c_str()); - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); - } - } - if (ImGui::BeginTable("trickTags", static_cast(showTag.size()), - ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings | - ImGuiTableFlags_Borders)) { - for (auto [rtTag, isShown] : showTag) { - ImGui::TableNextColumn(); - if (isShown) { - ImGui::PushStyleColor(ImGuiCol_Text, Rando::Tricks::GetTextColor(rtTag)); - } else { - ImGui::PushStyleColor(ImGuiCol_Text, { 1.0f, 1.0f, 1.0f, 1.0f }); - } - ImGui::PushStyleColor(ImGuiCol_Header, Rando::Tricks::GetTagColor(rtTag)); - ImGui::Selectable(Rando::Tricks::GetTagName(rtTag).c_str(), &showTag[rtTag]); - ImGui::PopStyleColor(2); - } - ImGui::EndTable(); - } - - if (ImGui::BeginTable("tableRandoTricks", 2, ImGuiTableFlags_BordersH | ImGuiTableFlags_BordersV)) { - ImGui::TableSetupColumn("Disabled Tricks", ImGuiTableColumnFlags_WidthStretch, 200.0f); - ImGui::TableSetupColumn("Enabled Tricks", ImGuiTableColumnFlags_WidthStretch, 200.0f); - ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); - ImGui::TableHeadersRow(); - ImGui::PopItemFlag(); - ImGui::TableNextRow(); - - if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("LogicRules"), RO_LOGIC_GLITCHLESS) != RO_LOGIC_NO_LOGIC) { - // COLUMN 1 - DISABLED TRICKS - ImGui::TableNextColumn(); - window->DC.CurrLineTextBaseOffset = 0.0f; - - if (UIWidgets::Button("Collapse All##disabled", - UIWidgets::ButtonOptions().Color(THEME_COLOR).Size(ImVec2(0.f, 0.f)))) { - for (int i = 0; i < RA_MAX; i++) { - areaTreeDisabled[static_cast(i)] = false; - } - } - ImGui::SameLine(); - if (UIWidgets::Button("Open All##disabled", - UIWidgets::ButtonOptions().Color(THEME_COLOR).Size(ImVec2(0.f, 0.f)))) { - for (int i = 0; i < RA_MAX; i++) { - areaTreeDisabled[static_cast(i)] = true; - } - } - ImGui::SameLine(); - if (UIWidgets::Button("Enable Visible", - UIWidgets::ButtonOptions().Color(THEME_COLOR).Size(ImVec2(0.f, 0.f)))) { - for (int i = 0; i < RT_MAX; i++) { - auto option = mSettings->GetTrickOption(static_cast(i)); - if (!enabledTricks.count(static_cast(i)) && - trickSearch.PassFilter(option.GetName().c_str()) && - areaTreeDisabled[option.GetArea()] && - Rando::Tricks::CheckTags(showTag, option.GetTags())) { - enabledTricks.insert(static_cast(i)); - } - } - std::string enabledTrickString = ""; - for (auto enabledTrickIt : enabledTricks) { - enabledTrickString += std::to_string(enabledTrickIt); - enabledTrickString += ","; - } - CVarSetString(CVAR_RANDOMIZER_SETTING("EnabledTricks"), enabledTrickString.c_str()); - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); - } - - ImGui::BeginChild("ChildTricksDisabled", ImVec2(0, -8), false, - ImGuiWindowFlags_HorizontalScrollbar); - - for (auto [area, trickIds] : mSettings->mTricksByArea) { - bool hasTricks = false; - for (auto rt : trickIds) { - auto option = mSettings->GetTrickOption(rt); - if (!option.IsHidden() && trickSearch.PassFilter(option.GetName().c_str()) && - !enabledTricks.count(rt) && Rando::Tricks::CheckTags(showTag, option.GetTags())) { - hasTricks = true; - break; - } - } - if (hasTricks) { - ImGui::TreeNodeSetOpen( - ImGui::GetID((Rando::Tricks::GetAreaName(area) + "##disabled").c_str()), - areaTreeDisabled[area]); - ImGui::SetNextItemOpen(true, ImGuiCond_Once); - if (ImGui::TreeNode((Rando::Tricks::GetAreaName(area) + "##disabled").c_str())) { - for (auto rt : trickIds) { - auto option = mSettings->GetTrickOption(rt); - if (!option.IsHidden() && trickSearch.PassFilter(option.GetName().c_str()) && - !enabledTricks.count(rt) && - Rando::Tricks::CheckTags(showTag, option.GetTags())) { - ImGui::TreeNodeSetOpen( - ImGui::GetID( - (Rando::Tricks::GetAreaName(option.GetArea()) + "##disabled").c_str()), - areaTreeDisabled[option.GetArea()]); - ImGui::SetNextItemOpen(true, ImGuiCond_Once); - UIWidgets::PushStyleButton(THEME_COLOR, ImVec2(7.f, 5.f)); - if (ImGui::ArrowButton(std::to_string(rt).c_str(), ImGuiDir_Right)) { - enabledTricks.insert(rt); - std::string enabledTrickString = ""; - for (auto enabledTrickIt : enabledTricks) { - enabledTrickString += std::to_string(enabledTrickIt); - enabledTrickString += ","; - } - CVarSetString(CVAR_RANDOMIZER_SETTING("EnabledTricks"), - enabledTrickString.c_str()); - Ship::Context::GetInstance() - ->GetWindow() - ->GetGui() - ->SaveConsoleVariablesNextFrame(); - } - UIWidgets::PopStyleButton(); - Rando::Tricks::DrawTagChips(option.GetTags(), option.GetName()); - ImGui::SameLine(); - ImGui::Text("%s", option.GetName().c_str()); - UIWidgets::Tooltip(option.GetDescription().c_str()); - } - } - areaTreeDisabled[area] = true; - ImGui::TreePop(); - } else { - areaTreeDisabled[area] = false; - } - } - } - ImGui::EndChild(); - - // COLUMN 2 - ENABLED TRICKS - ImGui::TableNextColumn(); - window->DC.CurrLineTextBaseOffset = 0.0f; - - if (UIWidgets::Button("Collapse All##enabled", - UIWidgets::ButtonOptions().Color(THEME_COLOR).Size(ImVec2(0.f, 0.f)))) { - for (int i = 0; i < RA_MAX; i++) { - areaTreeEnabled[static_cast(i)] = false; - } - } - ImGui::SameLine(); - if (UIWidgets::Button("Open All##enabled", - UIWidgets::ButtonOptions().Color(THEME_COLOR).Size(ImVec2(0.f, 0.f)))) { - for (int i = 0; i < RA_MAX; i++) { - areaTreeEnabled[static_cast(i)] = true; - } - } - ImGui::SameLine(); - if (UIWidgets::Button("Disable Visible", - UIWidgets::ButtonOptions().Color(THEME_COLOR).Size(ImVec2(0.f, 0.f)))) { - for (int i = 0; i < RT_MAX; i++) { - auto option = mSettings->GetTrickOption(static_cast(i)); - if (enabledTricks.count(static_cast(i)) && - trickSearch.PassFilter(option.GetName().c_str()) && areaTreeEnabled[option.GetArea()] && - Rando::Tricks::CheckTags(showTag, option.GetTags())) { - enabledTricks.erase(static_cast(i)); - } - } - std::string enabledTrickString = ""; - for (auto enabledTrickIt : enabledTricks) { - enabledTrickString += std::to_string(enabledTrickIt); - enabledTrickString += ","; - } - if (enabledTricks.size() == 0) { - CVarClear(CVAR_RANDOMIZER_SETTING("EnabledTricks")); - } else { - CVarSetString(CVAR_RANDOMIZER_SETTING("EnabledTricks"), enabledTrickString.c_str()); - } - Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); - } - - ImGui::BeginChild("ChildTricksEnabled", ImVec2(0, -8), false, ImGuiWindowFlags_HorizontalScrollbar); - - for (auto [area, trickIds] : mSettings->mTricksByArea) { - bool hasTricks = false; - for (auto rt : trickIds) { - auto option = mSettings->GetTrickOption(rt); - if (!option.IsHidden() && trickSearch.PassFilter(option.GetName().c_str()) && - enabledTricks.count(rt) && Rando::Tricks::CheckTags(showTag, option.GetTags())) { - hasTricks = true; - break; - } - } - if (hasTricks) { - ImGui::TreeNodeSetOpen( - ImGui::GetID((Rando::Tricks::GetAreaName(area) + "##enabled").c_str()), - areaTreeEnabled[area]); - ImGui::SetNextItemOpen(true, ImGuiCond_Once); - if (ImGui::TreeNode((Rando::Tricks::GetAreaName(area) + "##enabled").c_str())) { - for (auto rt : trickIds) { - auto option = mSettings->GetTrickOption(rt); - if (!option.IsHidden() && trickSearch.PassFilter(option.GetName().c_str()) && - enabledTricks.count(rt) && - Rando::Tricks::CheckTags(showTag, option.GetTags())) { - ImGui::TreeNodeSetOpen( - ImGui::GetID( - (Rando::Tricks::GetAreaName(option.GetArea()) + "##enabled").c_str()), - areaTreeEnabled[option.GetArea()]); - ImGui::SetNextItemOpen(true, ImGuiCond_Once); - UIWidgets::PushStyleButton(THEME_COLOR, ImVec2(7.f, 5.f)); - if (ImGui::ArrowButton(std::to_string(rt).c_str(), ImGuiDir_Left)) { - enabledTricks.erase(rt); - std::string enabledTrickString = ""; - for (auto enabledTrickIt : enabledTricks) { - enabledTrickString += std::to_string(enabledTrickIt); - enabledTrickString += ","; - } - if (enabledTrickString == "") { - CVarClear(CVAR_RANDOMIZER_SETTING("EnabledTricks")); - } else { - CVarSetString(CVAR_RANDOMIZER_SETTING("EnabledTricks"), - enabledTrickString.c_str()); - } - Ship::Context::GetInstance() - ->GetWindow() - ->GetGui() - ->SaveConsoleVariablesNextFrame(); - } - UIWidgets::PopStyleButton(); - Rando::Tricks::DrawTagChips(option.GetTags(), option.GetName()); - ImGui::SameLine(); - ImGui::Text("%s", option.GetName().c_str()); - UIWidgets::Tooltip(option.GetDescription().c_str()); - } - } - areaTreeEnabled[area] = true; - ImGui::TreePop(); - } else { - areaTreeEnabled[area] = false; - } - } - } - - ImGui::EndChild(); - } else { - ImGui::TableNextColumn(); - ImGui::BeginChild("ChildTricksDisabled", ImVec2(0, -8)); - ImGui::Text("Requires Logic Turned On."); - ImGui::EndChild(); - ImGui::TableNextColumn(); - ImGui::BeginChild("ChildTricksEnabled", ImVec2(0, -8)); - ImGui::Text("Requires Logic Turned On."); - ImGui::EndChild(); - } - ImGui::EndTable(); - } - ImGui::EndDisabled(); - ImGui::PopStyleVar(1); - ImGui::EndTabItem(); - } else { - tricksTabOpen = false; - } - - if (ImGui::BeginTabItem("Starting Inventory")) { - ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, cellPadding); - if (mSettings->GetOptionGroup(RSG_STARTING_INVENTORY_IMGUI_TABLE).RenderImGui()) { - mNeedsUpdate = true; - } - ImGui::PopStyleVar(1); - ImGui::EndTabItem(); - } - - ImGui::EndTabBar(); - } - UIWidgets::PopStyleTabs(); -} - -void RandomizerSettingsWindow::SetNeedsUpdate() { - mNeedsUpdate = true; -} - -void RandomizerSettingsWindow::UpdateElement() { - if (mNeedsUpdate) { - RandomizerCheckObjects::UpdateImGuiVisibility(); - mSettings->UpdateOptionProperties(); - locationsTabOpen = false; - tricksTabOpen = false; - mNeedsUpdate = false; - } } CustomMessage Randomizer::GetSheikMessage(s16 scene, u16 originalTextId) { @@ -5985,13 +5323,6 @@ class ExtendedVanillaTableInvalidItemIdException : public std::exception { } }; -void RandomizerSettingsWindow::InitElement() { - mSettings = Rando::Settings::GetInstance(); - Randomizer::CreateCustomMessages(); - seedString = (char*)calloc(MAX_SEED_STRING_SIZE, sizeof(char)); - mSettings->UpdateOptionProperties(); -} - static std::unordered_map randomizerGetToStatsTimeStamp = { { RG_GOHMA_SOUL, TIMESTAMP_FOUND_GOHMA_SOUL }, { RG_KING_DODONGO_SOUL, TIMESTAMP_FOUND_KING_DODONGO_SOUL }, diff --git a/soh/soh/Enhancements/randomizer/randomizer.h b/soh/soh/Enhancements/randomizer/randomizer.h index 54c85307b..4b4d96c3e 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.h +++ b/soh/soh/Enhancements/randomizer/randomizer.h @@ -7,7 +7,7 @@ #include #include "z64item.h" #include -#include "context.h" +#include "SeedContext.h" #include #include "soh/Enhancements/randomizer/randomizer_check_objects.h" #include "soh/Enhancements/randomizer/randomizer_check_tracker.h" @@ -82,6 +82,7 @@ extern "C" { #endif bool GenerateRandomizer(std::string seed = ""); +void JoinRandoGenerationThread(); #ifdef __cplusplus } diff --git a/soh/soh/Enhancements/randomizer/randomizerTypes.h b/soh/soh/Enhancements/randomizer/randomizerTypes.h index da421c792..6809c7172 100644 --- a/soh/soh/Enhancements/randomizer/randomizerTypes.h +++ b/soh/soh/Enhancements/randomizer/randomizerTypes.h @@ -6145,26 +6145,41 @@ typedef enum { RSG_EXCLUDES, RSG_TRICKS, RSG_GLITCHES, - RSG_AREA_ACCESS_IMGUI, - RSG_WORLD_IMGUI, - RSG_SHUFFLE_ENTRANCES_IMGUI, - RSG_WORLD_IMGUI_TABLE, - RSG_SHUFFLE_ITEMS_IMGUI, - RSG_SHUFFLE_NPCS_IMGUI, - RSG_SHUFFLE_DUNGEON_ITEMS_IMGUI, - RSG_ITEMS_IMGUI_TABLE, - RSG_TIMESAVERS_IMGUI, - RSG_ITEM_POOL_HINTS_IMGUI, - RSG_EXTRA_HINTS_IMGUI, - RSG_ITEM_POOL_HINTS_IMGUI_COLUMN, - RSG_ADDITIONAL_FEATURES_IMGUI, - RSG_GAMEPLAY_IMGUI_TABLE, - RSG_STARTING_EQUIPMENT_IMGUI, - RSG_STARTING_ITEMS_IMGUI, - RSG_STARTING_NORMAL_SONGS_IMGUI, - RSG_STARTING_WARP_SONGS_IMGUI, - RSG_STARTING_SONGS_IMGUI, - RSG_STARTING_INVENTORY_IMGUI_TABLE, + RSG_MENU_SIDEBAR_LOGIC_ACCESS, + RSG_MENU_COLUMN_LOGIC_WINCON, + RSG_MENU_SECTION_LOGIC, + RSG_MENU_SECTION_WINCON, + RSG_MENU_COLUMN_AREA_ACCESS, + RSG_MENU_SECTION_AREA_ACCESS, + RSG_MENU_SECTION_ENTRANCES, + RSG_MENU_COLUMN_ENTRANCES, + RSG_MENU_SIDEBAR_DUNGEONS, + RSG_MENU_COLUMN_DUNGEON_ITEMS, + RSG_MENU_SECTION_DUNGEON_ITEMS, + RSG_MENU_COLUMN_KEYRINGS, + RSG_MENU_SECTION_KEYRINGS, + RSG_MENU_COLUMN_MQ, + RSG_MENU_SECTION_MQ, + RSG_MENU_SIDEBAR_SHUFFLES, + RSG_MENU_COLUMN_BASIC_SHUFFLES, + RSG_MENU_SECTION_BASIC_SHUFFLES, + RSG_MENU_COLUMN_SHOP_SHUFFLES, + RSG_MENU_SECTION_SHOP_SHUFFLES, + RSG_MENU_COLUMN_ADDITIONAL_ITEMS, + RSG_MENU_SECTION_ADDITIONAL_ITEMS, + RSG_MENU_SIDEBAR_HINTS_TRAPS, + RSG_MENU_COLUMN_HINTS_TRAPS, + RSG_MENU_SECTION_HINTS, + RSG_MENU_SECTION_TRAPS, + RSG_MENU_COLUMN_STATIC_HINTS, + RSG_MENU_SECTION_STATIC_HINTS, + RSG_MENU_SIDEBAR_STARTING_ITEMS, + RSG_MENU_COLUMN_STARTING_EQUIPMENT, + RSG_MENU_SECTION_STARTING_EQUIPS, + RSG_MENU_SECTION_STARTING_ITEMS, + RSG_MENU_COLUMN_STARTING_SONGS, + RSG_MENU_SECTION_NORMAL_SONGS, + RSG_MENU_SECTION_WARP_SONGS, RSG_OPEN, RSG_WORLD, RSG_SHUFFLE, @@ -7050,4 +7065,4 @@ typedef enum { /* 0x1F */ GROTTO_GV_OCTOROK_OFFSET, /* 0x20 */ GROTTO_LW_DEKU_THEATRE_OFFSET, /* 0x21 */ GROTTO_OFFSET_MAX, -} GrottoEntranceOffsets; +} GrottoEntranceOffsets; \ No newline at end of file diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_objects.cpp b/soh/soh/Enhancements/randomizer/randomizer_check_objects.cpp index fa1e7158b..573a86090 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_check_objects.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_check_objects.cpp @@ -1,6 +1,6 @@ #include "randomizer_check_objects.h" #include "static_data.h" -#include "context.h" +#include "SeedContext.h" #include #include #include diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_objects.h b/soh/soh/Enhancements/randomizer/randomizer_check_objects.h index 7a7e605bb..f777b84d0 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_check_objects.h +++ b/soh/soh/Enhancements/randomizer/randomizer_check_objects.h @@ -1,4 +1,5 @@ #pragma once + #include "randomizerTypes.h" #include "z64actor_enum.h" #include "z64scene.h" @@ -14,4 +15,4 @@ std::map> GetAllRCObjectsByAre std::map GetAllRCAreaBySceneID(); RandomizerCheckArea GetRCAreaBySceneID(SceneID sceneId); void UpdateImGuiVisibility(); -} // namespace RandomizerCheckObjects +} // namespace RandomizerCheckObjects \ No newline at end of file diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.h b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.h index 38c38a39b..85def7e3b 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.h +++ b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.h @@ -1,4 +1,5 @@ #pragma once + #include #include "randomizerTypes.h" #include "randomizer_check_objects.h" diff --git a/soh/soh/Enhancements/randomizer/randomizer_inf.h b/soh/soh/Enhancements/randomizer/randomizer_inf.h index b2d8c21ea..7017ec738 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_inf.h +++ b/soh/soh/Enhancements/randomizer/randomizer_inf.h @@ -2099,4 +2099,4 @@ DEFINE_RAND_INF(RAND_INF_DEKU_TREE_QUEEN_GOHMA_GRASS_6) DEFINE_RAND_INF(RAND_INF_DEKU_TREE_QUEEN_GOHMA_GRASS_7) DEFINE_RAND_INF(RAND_INF_DEKU_TREE_QUEEN_GOHMA_GRASS_8) // End Grass -DEFINE_RAND_INF(RAND_INF_OBTAINED_RUTOS_LETTER) \ No newline at end of file +DEFINE_RAND_INF(RAND_INF_OBTAINED_RUTOS_LETTER) diff --git a/soh/soh/Enhancements/randomizer/randomizer_item_tracker.h b/soh/soh/Enhancements/randomizer/randomizer_item_tracker.h index 85a2b63a0..fab47c953 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_item_tracker.h +++ b/soh/soh/Enhancements/randomizer/randomizer_item_tracker.h @@ -66,4 +66,4 @@ class ItemTrackerWindow final : public Ship::GuiWindow { void InitElement() override; void DrawElement() override; void UpdateElement() override{}; -}; \ No newline at end of file +}; diff --git a/soh/soh/Enhancements/randomizer/randomizer_settings_window.h b/soh/soh/Enhancements/randomizer/randomizer_settings_window.h deleted file mode 100644 index 87d830e38..000000000 --- a/soh/soh/Enhancements/randomizer/randomizer_settings_window.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include - -namespace Rando { -class Settings; -} - -class RandomizerSettingsWindow final : public Ship::GuiWindow { - public: - using GuiWindow::GuiWindow; - - void InitElement() override; - void DrawElement() override; - void UpdateElement() override; - void SetNeedsUpdate(); - - private: - bool mNeedsUpdate = false; - std::shared_ptr mSettings; -}; diff --git a/soh/soh/Enhancements/randomizer/settings.cpp b/soh/soh/Enhancements/randomizer/settings.cpp index 1332fe4a5..5eb2642cf 100644 --- a/soh/soh/Enhancements/randomizer/settings.cpp +++ b/soh/soh/Enhancements/randomizer/settings.cpp @@ -1,4 +1,5 @@ #include "settings.h" +#include "soh/Enhancements/randomizer/randomizerTypes.h" #include "trial.h" #include "dungeon.h" @@ -111,45 +112,288 @@ Settings::Settings() : mExcludeLocationsOptionsAreas(RCAREA_INVALID) { #define OPT_U8(rsk, ...) mOptions[rsk] = Option::U8(rsk, __VA_ARGS__) #define OPT_BOOL(rsk, ...) mOptions[rsk] = Option::Bool(rsk, __VA_ARGS__) #define OPT_TRICK(rsk, ...) mTrickOptions[rsk] = TrickOption::LogicTrick(rsk, __VA_ARGS__) +// All callbacks will be called once when the widget is Added (on boot, essentially) and +// once when the widget is interacted with such that the value was changed. +#define OPT_CALLBACK(rsk, body) mOptions[rsk].SetCallback([this](WidgetInfo & info) body) +#define OPT_CALLBACK_FN(rsk, fn) mOptions[rsk].SetCallback(fn) + +void Settings::HandleMixedEntrancePoolsUI() { + bool dungeonShuffle = + CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleDungeonsEntrances"), RO_DUNGEON_ENTRANCE_SHUFFLE_OFF); + bool bossShuffle = + CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleBossEntrances"), RO_BOSS_ROOM_ENTRANCE_SHUFFLE_OFF); + bool overworldShuffle = CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleOverworldEntrances"), RO_GENERIC_OFF); + bool interiorShuffle = CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleInteriorsEntrances"), RO_GENERIC_OFF); + bool grottoShuffle = CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleGrottosEntrances"), RO_GENERIC_OFF); + bool thievesHideoutShuffle = + CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleThievesHideoutEntrances"), RO_GENERIC_OFF); + + // Hide Mixed Entrances option if 1 or no applicable entrance shuffles are visible + if (dungeonShuffle + bossShuffle + overworldShuffle + interiorShuffle + grottoShuffle + thievesHideoutShuffle <= + 1) { + mOptions[RSK_MIXED_ENTRANCE_POOLS].Hide(); + } else { + mOptions[RSK_MIXED_ENTRANCE_POOLS].Unhide(); + } +} + +void Settings::HandleStartingAgeUI() { + // Starting Age - Disabled under very specific conditions + // RANDOTODO: Fix so this is not disabled for No Logic. + if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("DoorOfTime"), RO_DOOROFTIME_CLOSED) == RO_DOOROFTIME_CLOSED && + CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleOcarinas"), RO_GENERIC_OFF) == + RO_GENERIC_OFF) /* closed door of time with ocarina shuffle off */ { + mOptions[RSK_STARTING_AGE].Disable("This option is disabled due to other options making the game unbeatable."); + } else { + mOptions[RSK_STARTING_AGE].Enable(); + } +} void Settings::CreateOptions() { CreateOptionDescriptions(); // clang-format off - OPT_U8(RSK_FOREST, "Closed Forest", {"On", "Deku Only", "Off"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ClosedForest"), mOptionDescriptions[RSK_FOREST], WidgetType::Combobox, RO_CLOSED_FOREST_ON); + OPT_U8(RSK_FOREST, "Closed Forest", {"On", "Deku Only", "Off"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ClosedForest"), mOptionDescriptions[RSK_FOREST], WIDGET_CVAR_COMBOBOX, RO_CLOSED_FOREST_ON); OPT_U8(RSK_KAK_GATE, "Kakariko Gate", {"Closed", "Open"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("KakarikoGate"), mOptionDescriptions[RSK_KAK_GATE]); - OPT_U8(RSK_DOOR_OF_TIME, "Door of Time", {"Closed", "Song only", "Open"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("DoorOfTime"), mOptionDescriptions[RSK_DOOR_OF_TIME], WidgetType::Combobox); + OPT_U8(RSK_DOOR_OF_TIME, "Door of Time", {"Closed", "Song only", "Open"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("DoorOfTime"), mOptionDescriptions[RSK_DOOR_OF_TIME], WIDGET_CVAR_COMBOBOX); + OPT_CALLBACK(RSK_DOOR_OF_TIME, { + HandleStartingAgeUI(); + }); OPT_U8(RSK_ZORAS_FOUNTAIN, "Zora's Fountain", {"Closed", "Closed as child", "Open"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ZorasFountain"), mOptionDescriptions[RSK_ZORAS_FOUNTAIN]); OPT_U8(RSK_SLEEPING_WATERFALL, "Sleeping Waterfall", {"Closed", "Open"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("SleepingWaterfall"), mOptionDescriptions[RSK_SLEEPING_WATERFALL]); OPT_U8(RSK_JABU_OPEN, "Jabu-Jabu", {"Closed", "Open"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("JabuJabu"), mOptionDescriptions[RSK_JABU_OPEN]); OPT_BOOL(RSK_LOCK_OVERWORLD_DOORS, "Lock Overworld Doors", CVAR_RANDOMIZER_SETTING("LockOverworldDoors"), mOptionDescriptions[RSK_LOCK_OVERWORLD_DOORS]); OPT_U8(RSK_GERUDO_FORTRESS, "Fortress Carpenters", {"Normal", "Fast", "Free"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("FortressCarpenters"), mOptionDescriptions[RSK_GERUDO_FORTRESS]); - OPT_U8(RSK_RAINBOW_BRIDGE, "Rainbow Bridge", {"Vanilla", "Always open", "Stones", "Medallions", "Dungeon rewards", "Dungeons", "Tokens", "Greg"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("RainbowBridge"), mOptionDescriptions[RSK_RAINBOW_BRIDGE], WidgetType::Combobox, RO_BRIDGE_VANILLA, false, IMFLAG_NONE); - OPT_U8(RSK_RAINBOW_BRIDGE_STONE_COUNT, "Bridge Stone Count", {NumOpts(0, 4)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("StoneCount"), "", WidgetType::Slider, 3, true); - OPT_U8(RSK_RAINBOW_BRIDGE_MEDALLION_COUNT, "Bridge Medallion Count", {NumOpts(0, 7)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MedallionCount"), "", WidgetType::Slider, 6, true); - OPT_U8(RSK_RAINBOW_BRIDGE_REWARD_COUNT, "Bridge Reward Count", {NumOpts(0, 10)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("RewardCount"), "", WidgetType::Slider, 9, true); - OPT_U8(RSK_RAINBOW_BRIDGE_DUNGEON_COUNT, "Bridge Dungeon Count", {NumOpts(0, 9)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("DungeonCount"), "", WidgetType::Slider, 8, true); - OPT_U8(RSK_RAINBOW_BRIDGE_TOKEN_COUNT, "Bridge Token Count", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("TokenCount"), "", WidgetType::Slider, 100, true); - OPT_U8(RSK_RAINBOW_BRIDGE_STONE_COUNT, "Bridge Stone Count", {NumOpts(0, 4)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("StoneCount"), "", WidgetType::Slider, 3, true); - OPT_U8(RSK_RAINBOW_BRIDGE_MEDALLION_COUNT, "Bridge Medallion Count", {NumOpts(0, 7)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MedallionCount"), "", WidgetType::Slider, 6, true); - OPT_U8(RSK_RAINBOW_BRIDGE_REWARD_COUNT, "Bridge Reward Count", {NumOpts(0, 10)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("RewardCount"), "", WidgetType::Slider, 9, true); - OPT_U8(RSK_RAINBOW_BRIDGE_DUNGEON_COUNT, "Bridge Dungeon Count", {NumOpts(0, 9)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("DungeonCount"), "", WidgetType::Slider, 8, true); - OPT_U8(RSK_RAINBOW_BRIDGE_TOKEN_COUNT, "Bridge Token Count", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("TokenCount"), "", WidgetType::Slider, 100, true); - OPT_U8(RSK_BRIDGE_OPTIONS, "Bridge Reward Options", {"Standard Rewards", "Greg as Reward", "Greg as Wildcard"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("BridgeRewardOptions"), mOptionDescriptions[RSK_BRIDGE_OPTIONS], WidgetType::Combobox, RO_BRIDGE_STANDARD_REWARD, false, IMFLAG_NONE); - OPT_U8(RSK_GANONS_TRIALS, "Ganon's Trials", {"Skip", "Set Number", "Random Number"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("GanonTrial"), mOptionDescriptions[RSK_GANONS_TRIALS], WidgetType::Combobox, RO_GANONS_TRIALS_SET_NUMBER); - OPT_U8(RSK_TRIAL_COUNT, "Ganon's Trials Count", {NumOpts(0, 6)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("GanonTrialCount"), mOptionDescriptions[RSK_TRIAL_COUNT], WidgetType::Slider, 6, true); - OPT_U8(RSK_STARTING_AGE, "Starting Age", {"Child", "Adult", "Random"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("StartingAge"), mOptionDescriptions[RSK_STARTING_AGE], WidgetType::Combobox, RO_AGE_CHILD); - OPT_U8(RSK_SELECTED_STARTING_AGE, "Selected Starting Age", {"Child", "Adult"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("SelectedStartingAge"), mOptionDescriptions[RSK_STARTING_AGE], WidgetType::Combobox, RO_AGE_CHILD); + OPT_CALLBACK(RSK_GERUDO_FORTRESS, { + const uint8_t maxKeyringCount = + (CVarGetInteger(CVAR_RANDOMIZER_SETTING("FortressCarpenters"), RO_GF_CARPENTERS_NORMAL) == + RO_GF_CARPENTERS_NORMAL && + CVarGetInteger(CVAR_RANDOMIZER_SETTING("GerudoKeys"), RO_GERUDO_KEYS_VANILLA) != RO_GERUDO_KEYS_VANILLA) + ? 9 + : 8; + if (mOptions[RSK_KEYRINGS_RANDOM_COUNT].GetOptionCount() != maxKeyringCount + 1) { + mOptions[RSK_KEYRINGS_RANDOM_COUNT].ChangeOptions(NumOpts(0, maxKeyringCount)); + } + if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("FortressCarpenters"), RO_GF_CARPENTERS_NORMAL) != + RO_GF_CARPENTERS_NORMAL || + CVarGetInteger(CVAR_RANDOMIZER_SETTING("GerudoKeys"), RO_GERUDO_KEYS_VANILLA) == RO_GERUDO_KEYS_VANILLA) { + mOptions[RSK_KEYRINGS_GERUDO_FORTRESS].Disable( + "Disabled because the currently selected Gerudo Fortress Carpenters\n" + "setting and/or Gerudo Fortress Keys setting is incompatible with\n" + "having a Gerudo Fortress Keyring."); + } else { + mOptions[RSK_KEYRINGS_GERUDO_FORTRESS].Enable(); + } + }); + OPT_U8(RSK_RAINBOW_BRIDGE, "Rainbow Bridge", {"Vanilla", "Always open", "Stones", "Medallions", "Dungeon rewards", "Dungeons", "Tokens", "Greg"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("RainbowBridge"), mOptionDescriptions[RSK_RAINBOW_BRIDGE], WIDGET_CVAR_COMBOBOX, RO_BRIDGE_VANILLA, false, nullptr, IMFLAG_NONE); + OPT_CALLBACK(RSK_RAINBOW_BRIDGE, { + mOptions[RSK_BRIDGE_OPTIONS].Hide(); + mOptions[RSK_RAINBOW_BRIDGE_STONE_COUNT].Hide(); + mOptions[RSK_RAINBOW_BRIDGE_MEDALLION_COUNT].Hide(); + mOptions[RSK_RAINBOW_BRIDGE_REWARD_COUNT].Hide(); + mOptions[RSK_RAINBOW_BRIDGE_DUNGEON_COUNT].Hide(); + mOptions[RSK_RAINBOW_BRIDGE_TOKEN_COUNT].Hide(); + switch (CVarGetInteger(CVAR_RANDOMIZER_SETTING("RainbowBridge"), RO_BRIDGE_VANILLA)) { + case RO_BRIDGE_STONES: + // Show Bridge Options and Stone Count slider + mOptions[RSK_RAINBOW_BRIDGE].RemoveFlag(IMFLAG_SEPARATOR_BOTTOM); + mOptions[RSK_BRIDGE_OPTIONS].Unhide(); + mOptions[RSK_RAINBOW_BRIDGE_STONE_COUNT].Unhide(); + break; + case RO_BRIDGE_MEDALLIONS: + // Show Bridge Options and Medallion Count Slider + mOptions[RSK_RAINBOW_BRIDGE].RemoveFlag(IMFLAG_SEPARATOR_BOTTOM); + mOptions[RSK_BRIDGE_OPTIONS].Unhide(); + mOptions[RSK_RAINBOW_BRIDGE_MEDALLION_COUNT].Unhide(); + break; + case RO_BRIDGE_DUNGEON_REWARDS: + // Show Bridge Options and Dungeon Reward Count Slider + mOptions[RSK_RAINBOW_BRIDGE].RemoveFlag(IMFLAG_SEPARATOR_BOTTOM); + mOptions[RSK_BRIDGE_OPTIONS].Unhide(); + mOptions[RSK_RAINBOW_BRIDGE_REWARD_COUNT].Unhide(); + break; + case RO_BRIDGE_DUNGEONS: + // Show Bridge Options and Dungeon Count Slider + mOptions[RSK_RAINBOW_BRIDGE].RemoveFlag(IMFLAG_SEPARATOR_BOTTOM); + mOptions[RSK_BRIDGE_OPTIONS].Unhide(); + mOptions[RSK_RAINBOW_BRIDGE_DUNGEON_COUNT].Unhide(); + break; + case RO_BRIDGE_TOKENS: + // Show token count slider (not bridge options) + mOptions[RSK_RAINBOW_BRIDGE].RemoveFlag(IMFLAG_SEPARATOR_BOTTOM); + mOptions[RSK_BRIDGE_OPTIONS].Hide(); + mOptions[RSK_RAINBOW_BRIDGE_TOKEN_COUNT].Unhide(); + break; + default: + break; + } + }); + OPT_U8(RSK_RAINBOW_BRIDGE_STONE_COUNT, "Bridge Stone Count", {NumOpts(0, 4)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("StoneCount"), "", WIDGET_CVAR_SLIDER_INT, 3, true); + OPT_U8(RSK_RAINBOW_BRIDGE_MEDALLION_COUNT, "Bridge Medallion Count", {NumOpts(0, 7)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MedallionCount"), "", WIDGET_CVAR_SLIDER_INT, 6, true); + OPT_U8(RSK_RAINBOW_BRIDGE_REWARD_COUNT, "Bridge Reward Count", {NumOpts(0, 10)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("RewardCount"), "", WIDGET_CVAR_SLIDER_INT, 9, true); + OPT_U8(RSK_RAINBOW_BRIDGE_DUNGEON_COUNT, "Bridge Dungeon Count", {NumOpts(0, 9)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("DungeonCount"), "", WIDGET_CVAR_SLIDER_INT, 8, true); + OPT_U8(RSK_RAINBOW_BRIDGE_TOKEN_COUNT, "Bridge Token Count", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("TokenCount"), "", WIDGET_CVAR_SLIDER_INT, 100, true); + OPT_U8(RSK_RAINBOW_BRIDGE_STONE_COUNT, "Bridge Stone Count", {NumOpts(0, 4)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("StoneCount"), "", WIDGET_CVAR_SLIDER_INT, 3, true); + OPT_U8(RSK_RAINBOW_BRIDGE_MEDALLION_COUNT, "Bridge Medallion Count", {NumOpts(0, 7)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MedallionCount"), "", WIDGET_CVAR_SLIDER_INT, 6, true); + OPT_U8(RSK_RAINBOW_BRIDGE_REWARD_COUNT, "Bridge Reward Count", {NumOpts(0, 10)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("RewardCount"), "", WIDGET_CVAR_SLIDER_INT, 9, true); + OPT_U8(RSK_RAINBOW_BRIDGE_DUNGEON_COUNT, "Bridge Dungeon Count", {NumOpts(0, 9)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("DungeonCount"), "", WIDGET_CVAR_SLIDER_INT, 8, true); + OPT_U8(RSK_RAINBOW_BRIDGE_TOKEN_COUNT, "Bridge Token Count", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("TokenCount"), "", WIDGET_CVAR_SLIDER_INT, 100, true); + OPT_U8(RSK_BRIDGE_OPTIONS, "Bridge Reward Options", {"Standard Rewards", "Greg as Reward", "Greg as Wildcard"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("BridgeRewardOptions"), mOptionDescriptions[RSK_BRIDGE_OPTIONS], WIDGET_CVAR_COMBOBOX, RO_BRIDGE_STANDARD_REWARD, false, nullptr, IMFLAG_NONE); + OPT_CALLBACK(RSK_BRIDGE_OPTIONS, { + const uint8_t bridgeOpt = CVarGetInteger(CVAR_RANDOMIZER_SETTING("BridgeRewardOptions"), RO_BRIDGE_STANDARD_REWARD); + if (bridgeOpt == RO_BRIDGE_GREG_REWARD) { + if (mOptions[RSK_RAINBOW_BRIDGE_STONE_COUNT].GetOptionCount() == 4) { + mOptions[RSK_RAINBOW_BRIDGE_STONE_COUNT].ChangeOptions(NumOpts(0, 4)); + } + if (mOptions[RSK_RAINBOW_BRIDGE_MEDALLION_COUNT].GetOptionCount() == 7) { + mOptions[RSK_RAINBOW_BRIDGE_MEDALLION_COUNT].ChangeOptions(NumOpts(0, 7)); + } + if (mOptions[RSK_RAINBOW_BRIDGE_REWARD_COUNT].GetOptionCount() == 10) { + mOptions[RSK_RAINBOW_BRIDGE_REWARD_COUNT].ChangeOptions(NumOpts(0, 10)); + } + if (mOptions[RSK_RAINBOW_BRIDGE_DUNGEON_COUNT].GetOptionCount() == 9) { + mOptions[RSK_RAINBOW_BRIDGE_DUNGEON_COUNT].ChangeOptions(NumOpts(0, 9)); + } + } else { + if (mOptions[RSK_RAINBOW_BRIDGE_STONE_COUNT].GetOptionCount() == 5) { + mOptions[RSK_RAINBOW_BRIDGE_STONE_COUNT].ChangeOptions(NumOpts(0, 3)); + } + if (mOptions[RSK_RAINBOW_BRIDGE_MEDALLION_COUNT].GetOptionCount() == 8) { + mOptions[RSK_RAINBOW_BRIDGE_MEDALLION_COUNT].ChangeOptions(NumOpts(0, 6)); + } + if (mOptions[RSK_RAINBOW_BRIDGE_REWARD_COUNT].GetOptionCount() == 11) { + mOptions[RSK_RAINBOW_BRIDGE_REWARD_COUNT].ChangeOptions(NumOpts(0, 9)); + } + if (mOptions[RSK_RAINBOW_BRIDGE_DUNGEON_COUNT].GetOptionCount() == 10) { + mOptions[RSK_RAINBOW_BRIDGE_DUNGEON_COUNT].ChangeOptions(NumOpts(0, 8)); + } + } + }); + OPT_U8(RSK_GANONS_TRIALS, "Ganon's Trials", {"Skip", "Set Number", "Random Number"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("GanonTrial"), mOptionDescriptions[RSK_GANONS_TRIALS], WIDGET_CVAR_COMBOBOX, RO_GANONS_TRIALS_SET_NUMBER); + OPT_CALLBACK(RSK_GANONS_TRIALS, { + // Only show the trial count slider if Trials is set to Set Number + if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("GanonTrial"), RO_GANONS_TRIALS_SET_NUMBER) == + RO_GANONS_TRIALS_SET_NUMBER) { + mOptions[RSK_TRIAL_COUNT].Unhide(); + } else { + mOptions[RSK_TRIAL_COUNT].Hide(); + } + }); + OPT_U8(RSK_TRIAL_COUNT, "Ganon's Trials Count", {NumOpts(0, 6)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("GanonTrialCount"), mOptionDescriptions[RSK_TRIAL_COUNT], WIDGET_CVAR_SLIDER_INT, 6, true); + OPT_U8(RSK_STARTING_AGE, "Starting Age", {"Child", "Adult", "Random"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("StartingAge"), mOptionDescriptions[RSK_STARTING_AGE], WIDGET_CVAR_COMBOBOX, RO_AGE_CHILD); + OPT_U8(RSK_SELECTED_STARTING_AGE, "Selected Starting Age", {"Child", "Adult"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("SelectedStartingAge"), mOptionDescriptions[RSK_STARTING_AGE], WIDGET_CVAR_COMBOBOX, RO_AGE_CHILD); OPT_BOOL(RSK_SHUFFLE_ENTRANCES, "Shuffle Entrances"); - OPT_U8(RSK_SHUFFLE_DUNGEON_ENTRANCES, "Dungeon Entrances", {"Off", "On", "On + Ganon"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleDungeonsEntrances"), mOptionDescriptions[RSK_SHUFFLE_DUNGEON_ENTRANCES], WidgetType::Combobox, RO_DUNGEON_ENTRANCE_SHUFFLE_OFF); - OPT_U8(RSK_SHUFFLE_BOSS_ENTRANCES, "Boss Entrances", {"Off", "Age Restricted", "Full"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleBossEntrances"), mOptionDescriptions[RSK_SHUFFLE_BOSS_ENTRANCES], WidgetType::Combobox, RO_BOSS_ROOM_ENTRANCE_SHUFFLE_OFF); + OPT_U8(RSK_SHUFFLE_DUNGEON_ENTRANCES, "Dungeon Entrances", {"Off", "On", "On + Ganon"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleDungeonsEntrances"), mOptionDescriptions[RSK_SHUFFLE_DUNGEON_ENTRANCES], WIDGET_CVAR_COMBOBOX, RO_DUNGEON_ENTRANCE_SHUFFLE_OFF); + OPT_CALLBACK(RSK_SHUFFLE_DUNGEON_ENTRANCES, { + if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleDungeonsEntrances"), RO_DUNGEON_ENTRANCE_SHUFFLE_OFF) == + RO_DUNGEON_ENTRANCE_SHUFFLE_OFF || + CVarGetInteger(CVAR_RANDOMIZER_SETTING("MixedEntrances"), RO_GENERIC_OFF) == RO_GENERIC_OFF) { + mOptions[RSK_MIX_DUNGEON_ENTRANCES].Hide(); + } else { + mOptions[RSK_MIX_DUNGEON_ENTRANCES].Unhide(); + } + }); + OPT_U8(RSK_SHUFFLE_BOSS_ENTRANCES, "Boss Entrances", {"Off", "Age Restricted", "Full"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleBossEntrances"), mOptionDescriptions[RSK_SHUFFLE_BOSS_ENTRANCES], WIDGET_CVAR_COMBOBOX, RO_BOSS_ROOM_ENTRANCE_SHUFFLE_OFF); + OPT_CALLBACK(RSK_SHUFFLE_BOSS_ENTRANCES, { + HandleMixedEntrancePoolsUI(); + + if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleBossEntrances"), RO_BOSS_ROOM_ENTRANCE_SHUFFLE_OFF) == + RO_BOSS_ROOM_ENTRANCE_SHUFFLE_OFF || + CVarGetInteger(CVAR_RANDOMIZER_SETTING("MixedEntrances"), RO_GENERIC_OFF) == RO_GENERIC_OFF) { + mOptions[RSK_MIX_BOSS_ENTRANCES].Hide(); + } else { + mOptions[RSK_MIX_BOSS_ENTRANCES].Unhide(); + } + }); OPT_BOOL(RSK_SHUFFLE_OVERWORLD_ENTRANCES, "Overworld Entrances", CVAR_RANDOMIZER_SETTING("ShuffleOverworldEntrances"), mOptionDescriptions[RSK_SHUFFLE_OVERWORLD_ENTRANCES]); - OPT_U8(RSK_SHUFFLE_INTERIOR_ENTRANCES, "Interior Entrances", {"Off", "Simple", "All"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleInteriorsEntrances"), mOptionDescriptions[RSK_SHUFFLE_INTERIOR_ENTRANCES], WidgetType::Combobox, RO_INTERIOR_ENTRANCE_SHUFFLE_OFF); + OPT_CALLBACK(RSK_SHUFFLE_OVERWORLD_ENTRANCES, { + HandleMixedEntrancePoolsUI(); + + if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleOverworldEntrances"), RO_GENERIC_OFF) == RO_GENERIC_OFF || + CVarGetInteger(CVAR_RANDOMIZER_SETTING("MixedEntrances"), RO_GENERIC_OFF) == RO_GENERIC_OFF) { + mOptions[RSK_MIX_OVERWORLD_ENTRANCES].Hide(); + } else { + mOptions[RSK_MIX_OVERWORLD_ENTRANCES].Unhide(); + } + }); + OPT_U8(RSK_SHUFFLE_INTERIOR_ENTRANCES, "Interior Entrances", {"Off", "Simple", "All"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleInteriorsEntrances"), mOptionDescriptions[RSK_SHUFFLE_INTERIOR_ENTRANCES], WIDGET_CVAR_COMBOBOX, RO_INTERIOR_ENTRANCE_SHUFFLE_OFF); + OPT_CALLBACK(RSK_SHUFFLE_INTERIOR_ENTRANCES, { + HandleMixedEntrancePoolsUI(); + + if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleInteriorsEntrances"), RO_GENERIC_OFF) == RO_GENERIC_OFF || + CVarGetInteger(CVAR_RANDOMIZER_SETTING("MixedEntrances"), RO_GENERIC_OFF) == RO_GENERIC_OFF) { + mOptions[RSK_MIX_INTERIOR_ENTRANCES].Hide(); + } else { + mOptions[RSK_MIX_INTERIOR_ENTRANCES].Unhide(); + } + }); OPT_BOOL(RSK_SHUFFLE_THIEVES_HIDEOUT_ENTRANCES, "Thieves' Hideout Entrances", CVAR_RANDOMIZER_SETTING("ShuffleThievesHideoutEntrances"), mOptionDescriptions[RSK_SHUFFLE_THIEVES_HIDEOUT_ENTRANCES]); + OPT_CALLBACK(RSK_SHUFFLE_THIEVES_HIDEOUT_ENTRANCES, { + HandleMixedEntrancePoolsUI(); + + if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleThievesHideoutEntrances"), RO_GENERIC_OFF) == + RO_GENERIC_OFF || + CVarGetInteger(CVAR_RANDOMIZER_SETTING("MixedEntrances"), RO_GENERIC_OFF) == RO_GENERIC_OFF) { + mOptions[RSK_MIX_THIEVES_HIDEOUT_ENTRANCES].Hide(); + } else { + mOptions[RSK_MIX_THIEVES_HIDEOUT_ENTRANCES].Unhide(); + } + }); OPT_BOOL(RSK_SHUFFLE_GROTTO_ENTRANCES, "Grottos Entrances", CVAR_RANDOMIZER_SETTING("ShuffleGrottosEntrances"), mOptionDescriptions[RSK_SHUFFLE_GROTTO_ENTRANCES]); + OPT_CALLBACK(RSK_SHUFFLE_GROTTO_ENTRANCES, { + HandleMixedEntrancePoolsUI(); + + if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleGrottosEntrances"), RO_GENERIC_OFF) == RO_GENERIC_OFF || + CVarGetInteger(CVAR_RANDOMIZER_SETTING("MixedEntrances"), RO_GENERIC_OFF) == RO_GENERIC_OFF) { + mOptions[RSK_MIX_GROTTO_ENTRANCES].Hide(); + } else { + mOptions[RSK_MIX_GROTTO_ENTRANCES].Unhide(); + } + }); OPT_BOOL(RSK_SHUFFLE_OWL_DROPS, "Owl Drops", CVAR_RANDOMIZER_SETTING("ShuffleOwlDrops"), mOptionDescriptions[RSK_SHUFFLE_OWL_DROPS]); OPT_BOOL(RSK_SHUFFLE_WARP_SONGS, "Warp Songs", CVAR_RANDOMIZER_SETTING("ShuffleWarpSongs"), mOptionDescriptions[RSK_SHUFFLE_WARP_SONGS]); + OPT_CALLBACK(RSK_SHUFFLE_WARP_SONGS, { + if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleWarpSongs"), RO_GENERIC_ON)) { + mOptions[RSK_WARP_SONG_HINTS].Enable(); + } else { + mOptions[RSK_WARP_SONG_HINTS].Disable("This option is disabled since warp song locations are not shuffled."); + } + }); OPT_BOOL(RSK_SHUFFLE_OVERWORLD_SPAWNS, "Overworld Spawns", CVAR_RANDOMIZER_SETTING("ShuffleOverworldSpawns"), mOptionDescriptions[RSK_SHUFFLE_OVERWORLD_SPAWNS]); OPT_BOOL(RSK_MIXED_ENTRANCE_POOLS, "Mixed Entrance Pools", CVAR_RANDOMIZER_SETTING("MixedEntrances"), mOptionDescriptions[RSK_MIXED_ENTRANCE_POOLS]); + OPT_CALLBACK(RSK_MIXED_ENTRANCE_POOLS, { + // Show mixed entrance pool options if mixed entrance pools are enabled, but only the ones that aren't off + if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("MixedEntrances"), RO_GENERIC_OFF) == RO_GENERIC_OFF || + mOptions[RSK_MIXED_ENTRANCE_POOLS].IsHidden()) { + mOptions[RSK_MIXED_ENTRANCE_POOLS].AddFlag(IMFLAG_SEPARATOR_BOTTOM); + mOptions[RSK_MIX_DUNGEON_ENTRANCES].Hide(); + mOptions[RSK_MIX_BOSS_ENTRANCES].Hide(); + mOptions[RSK_MIX_OVERWORLD_ENTRANCES].Hide(); + mOptions[RSK_MIX_INTERIOR_ENTRANCES].Hide(); + mOptions[RSK_MIX_THIEVES_HIDEOUT_ENTRANCES].Hide(); + mOptions[RSK_MIX_GROTTO_ENTRANCES].Hide(); + } else { + if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleDungeonsEntrances"), RO_DUNGEON_ENTRANCE_SHUFFLE_OFF) != + RO_DUNGEON_ENTRANCE_SHUFFLE_OFF) { + mOptions[RSK_MIX_DUNGEON_ENTRANCES].Unhide(); + } + if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleBossEntrances"), RO_BOSS_ROOM_ENTRANCE_SHUFFLE_OFF) != + RO_BOSS_ROOM_ENTRANCE_SHUFFLE_OFF) { + mOptions[RSK_MIX_BOSS_ENTRANCES].Unhide(); + } + if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleOverworldEntrances"), RO_GENERIC_OFF) != RO_GENERIC_OFF) { + mOptions[RSK_MIX_OVERWORLD_ENTRANCES].Unhide(); + } + if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleInteriorsEntrances"), RO_GENERIC_OFF) != RO_GENERIC_OFF) { + mOptions[RSK_MIX_INTERIOR_ENTRANCES].Unhide(); + } + if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleThievesHideoutEntrances"), RO_GENERIC_OFF) != RO_GENERIC_OFF) { + mOptions[RSK_MIX_THIEVES_HIDEOUT_ENTRANCES].Unhide(); + } + if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleGrottosEntrances"), RO_GENERIC_OFF) != RO_GENERIC_OFF) { + mOptions[RSK_MIX_GROTTO_ENTRANCES].Unhide(); + } + } + }); OPT_BOOL(RSK_MIX_DUNGEON_ENTRANCES, "Mix Dungeons", CVAR_RANDOMIZER_SETTING("MixDungeons"), mOptionDescriptions[RSK_MIX_DUNGEON_ENTRANCES], IMFLAG_NONE); OPT_BOOL(RSK_MIX_BOSS_ENTRANCES, "Mix Bosses", CVAR_RANDOMIZER_SETTING("MixBosses"), mOptionDescriptions[RSK_MIX_BOSS_ENTRANCES], IMFLAG_NONE); OPT_BOOL(RSK_MIX_OVERWORLD_ENTRANCES, "Mix Overworld", CVAR_RANDOMIZER_SETTING("MixOverworld"), mOptionDescriptions[RSK_MIX_OVERWORLD_ENTRANCES], IMFLAG_NONE); @@ -157,135 +401,806 @@ void Settings::CreateOptions() { OPT_BOOL(RSK_MIX_THIEVES_HIDEOUT_ENTRANCES, "Mix Thieves' Hideout", CVAR_RANDOMIZER_SETTING("MixThievesHideout"), mOptionDescriptions[RSK_MIX_THIEVES_HIDEOUT_ENTRANCES]); OPT_BOOL(RSK_MIX_GROTTO_ENTRANCES, "Mix Grottos", CVAR_RANDOMIZER_SETTING("MixGrottos"), mOptionDescriptions[RSK_MIX_GROTTO_ENTRANCES]); OPT_BOOL(RSK_DECOUPLED_ENTRANCES, "Decouple Entrances", CVAR_RANDOMIZER_SETTING("DecoupleEntrances"), mOptionDescriptions[RSK_DECOUPLED_ENTRANCES]); - OPT_U8(RSK_BOMBCHU_BAG, "Bombchu Bag", {"None", "Single Bag", "Progressive Bags"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("BombchuBag"), mOptionDescriptions[RSK_BOMBCHU_BAG], WidgetType::Combobox, RO_BOMBCHU_BAG_NONE); - OPT_U8(RSK_ENABLE_BOMBCHU_DROPS, "Bombchu Drops", {"No", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("EnableBombchuDrops"), mOptionDescriptions[RSK_ENABLE_BOMBCHU_DROPS], WidgetType::Combobox, RO_AMMO_DROPS_ON); + OPT_U8(RSK_BOMBCHU_BAG, "Bombchu Bag", {"None", "Single Bag", "Progressive Bags"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("BombchuBag"), mOptionDescriptions[RSK_BOMBCHU_BAG], WIDGET_CVAR_COMBOBOX, RO_BOMBCHU_BAG_NONE); + OPT_U8(RSK_ENABLE_BOMBCHU_DROPS, "Bombchu Drops", {"No", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("EnableBombchuDrops"), mOptionDescriptions[RSK_ENABLE_BOMBCHU_DROPS], WIDGET_CVAR_COMBOBOX, RO_AMMO_DROPS_ON); // TODO: AmmoDrops and/or HeartDropRefill, combine with/separate Ammo Drops from Bombchu Drops? OPT_BOOL(RSK_TRIFORCE_HUNT, "Triforce Hunt", CVAR_RANDOMIZER_SETTING("TriforceHunt"), mOptionDescriptions[RSK_TRIFORCE_HUNT], IMFLAG_NONE); - OPT_U8(RSK_TRIFORCE_HUNT_PIECES_TOTAL, "Triforce Hunt Total Pieces", {NumOpts(1, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("TriforceHuntTotalPieces"), mOptionDescriptions[RSK_TRIFORCE_HUNT_PIECES_TOTAL], WidgetType::Slider, 29, false, IMFLAG_NONE); - OPT_U8(RSK_TRIFORCE_HUNT_PIECES_REQUIRED, "Triforce Hunt Required Pieces", {NumOpts(1, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("TriforceHuntRequiredPieces"), mOptionDescriptions[RSK_TRIFORCE_HUNT_PIECES_REQUIRED], WidgetType::Slider, 19); - OPT_U8(RSK_MQ_DUNGEON_RANDOM, "MQ Dungeon Setting", {"None", "Set Number", "Random", "Selection Only"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MQDungeons"), mOptionDescriptions[RSK_MQ_DUNGEON_RANDOM], WidgetType::Combobox, RO_MQ_DUNGEONS_NONE, true, IMFLAG_NONE); - OPT_U8(RSK_MQ_DUNGEON_COUNT, "MQ Dungeon Count", {NumOpts(0, 12)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MQDungeonCount"), "", WidgetType::Slider, 12, true, IMFLAG_NONE); - OPT_BOOL(RSK_MQ_DUNGEON_SET, "Set Dungeon Quests", {"Off", "On"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MQDungeonsSelection"), mOptionDescriptions[RSK_MQ_DUNGEON_SET], WidgetType::Checkbox, false, false, IMFLAG_NONE); - OPT_U8(RSK_MQ_DEKU_TREE, "Deku Tree Quest", {"Vanilla", "Master Quest", "Random"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MQDungeonsDekuTree"), "", WidgetType::Combobox, RO_MQ_SET_VANILLA, false, IMFLAG_NONE); - OPT_U8(RSK_MQ_DODONGOS_CAVERN, "Dodongo's Cavern Quest", {"Vanilla", "Master Quest", "Random"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MQDungeonsDodongosCavern"), "", WidgetType::Combobox, RO_MQ_SET_VANILLA, false, IMFLAG_NONE); - OPT_U8(RSK_MQ_JABU_JABU, "Jabu-Jabu's Belly Quest", {"Vanilla", "Master Quest", "Random"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MQDungeonsJabuJabu"), "", WidgetType::Combobox, RO_MQ_SET_VANILLA, false, IMFLAG_NONE); - OPT_U8(RSK_MQ_FOREST_TEMPLE, "Forest Temple Quest", {"Vanilla", "Master Quest", "Random"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MQDungeonsForestTemple"), "", WidgetType::Combobox, RO_MQ_SET_VANILLA, false, IMFLAG_NONE); - OPT_U8(RSK_MQ_FIRE_TEMPLE, "Fire Temple Quest", {"Vanilla", "Master Quest", "Random"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MQDungeonsFireTemple"), "", WidgetType::Combobox, RO_MQ_SET_VANILLA, false, IMFLAG_NONE); - OPT_U8(RSK_MQ_WATER_TEMPLE, "Water Temple Quest", {"Vanilla", "Master Quest", "Random"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MQDungeonsWaterTemple"), "", WidgetType::Combobox, RO_MQ_SET_VANILLA, false, IMFLAG_NONE); - OPT_U8(RSK_MQ_SPIRIT_TEMPLE, "Spirit Temple Quest", {"Vanilla", "Master Quest", "Random"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MQDungeonsSpiritTemple"), "", WidgetType::Combobox, RO_MQ_SET_VANILLA, false, IMFLAG_NONE); - OPT_U8(RSK_MQ_SHADOW_TEMPLE, "Shadow Temple Quest", {"Vanilla", "Master Quest", "Random"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MQDungeonsShadowTemple"), "", WidgetType::Combobox, RO_MQ_SET_VANILLA, false, IMFLAG_NONE); - OPT_U8(RSK_MQ_BOTTOM_OF_THE_WELL, "Bottom of the Well Quest", {"Vanilla", "Master Quest", "Random"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MQDungeonsBottomOfTheWell"), "", WidgetType::Combobox, RO_MQ_SET_VANILLA, false, IMFLAG_NONE); - OPT_U8(RSK_MQ_ICE_CAVERN, "Ice Cavern Quest", {"Vanilla", "Master Quest", "Random"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MQDungeonsIceCavern"), "", WidgetType::Combobox, RO_MQ_SET_VANILLA, false, IMFLAG_NONE); - OPT_U8(RSK_MQ_GTG, "Gerudo Training Ground Quest", {"Vanilla", "Master Quest", "Random"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MQDungeonsGTG"), "", WidgetType::Combobox, RO_MQ_SET_VANILLA, false, IMFLAG_NONE); - OPT_U8(RSK_MQ_GANONS_CASTLE, "Ganon's Castle Quest", {"Vanilla", "Master Quest", "Random"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MQDungeonsGanonsCastle"), "", WidgetType::Combobox, RO_MQ_SET_VANILLA); - OPT_U8(RSK_SHUFFLE_DUNGEON_REWARDS, "Shuffle Dungeon Rewards", {"Vanilla", "End of Dungeons", "Any Dungeon", "Overworld", "Anywhere"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleDungeonReward"), mOptionDescriptions[RSK_SHUFFLE_DUNGEON_REWARDS], WidgetType::Combobox, RO_DUNGEON_REWARDS_END_OF_DUNGEON); - OPT_U8(RSK_LINKS_POCKET, "Link's Pocket", {"Dungeon Reward", "Advancement", "Anything", "Nothing"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LinksPocket"), "", WidgetType::Combobox, RO_LINKS_POCKET_DUNGEON_REWARD); - OPT_U8(RSK_SHUFFLE_SONGS, "Shuffle Songs", {"Off", "Song Locations", "Dungeon Rewards", "Anywhere"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleSongs"), mOptionDescriptions[RSK_SHUFFLE_SONGS], WidgetType::Combobox, RO_SONG_SHUFFLE_SONG_LOCATIONS); - OPT_U8(RSK_SHOPSANITY, "Shop Shuffle", {"Off", "Specific Count", "Random"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("Shopsanity"), mOptionDescriptions[RSK_SHOPSANITY], WidgetType::Combobox, RO_SHOPSANITY_OFF); - OPT_U8(RSK_SHOPSANITY_COUNT, "Shops Item Count", {NumOpts(0, 7/*8*/)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShopsanityCount"), mOptionDescriptions[RSK_SHOPSANITY_COUNT], WidgetType::Slider, 0, false, IMFLAG_NONE); - OPT_U8(RSK_SHOPSANITY_PRICES, "Shops Prices", {"Vanilla", "Cheap Balanced", "Balanced", "Fixed", "Range", "Set By Wallet"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShopsanityPrices"), mOptionDescriptions[RSK_SHOPSANITY_PRICES], WidgetType::Combobox, RO_PRICE_VANILLA, false, IMFLAG_NONE); - OPT_U8(RSK_SHOPSANITY_PRICES_FIXED_PRICE, "Shops Fixed Price", {NumOpts(0, 995, 5)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShopsanityFixedPrice"), mOptionDescriptions[RSK_SHOPSANITY_PRICES_FIXED_PRICE], WidgetType::Slider, 10, true); - OPT_U8(RSK_SHOPSANITY_PRICES_RANGE_1, "Shops Lower Bound", {NumOpts(0, 995, 5)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShopsanityPriceRange1"), mOptionDescriptions[RSK_SHOPSANITY_PRICES_RANGE_1], WidgetType::Slider, 10, true, IMFLAG_NONE); - OPT_U8(RSK_SHOPSANITY_PRICES_RANGE_2, "Shops Upper Bound", {NumOpts(0, 995, 5)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShopsanityPriceRange2"), mOptionDescriptions[RSK_SHOPSANITY_PRICES_RANGE_2], WidgetType::Slider, 100, true, IMFLAG_NONE); - OPT_U8(RSK_SHOPSANITY_PRICES_NO_WALLET_WEIGHT, "Shops No Wallet Weight", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShopsanityNoWalletWeight"), mOptionDescriptions[RSK_SHOPSANITY_PRICES_NO_WALLET_WEIGHT], WidgetType::Slider, 10, true, IMFLAG_NONE); - OPT_U8(RSK_SHOPSANITY_PRICES_CHILD_WALLET_WEIGHT, "Shops Child Wallet Weight", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShopsanityChildWalletWeight"), mOptionDescriptions[RSK_SHOPSANITY_PRICES_CHILD_WALLET_WEIGHT], WidgetType::Slider, 10, true, IMFLAG_NONE); - OPT_U8(RSK_SHOPSANITY_PRICES_ADULT_WALLET_WEIGHT, "Shops Adult Wallet Weight", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShopsanityAdultWalletWeight"), mOptionDescriptions[RSK_SHOPSANITY_PRICES_ADULT_WALLET_WEIGHT], WidgetType::Slider, 10, true, IMFLAG_NONE); - OPT_U8(RSK_SHOPSANITY_PRICES_GIANT_WALLET_WEIGHT, "Shops Giant Wallet Weight", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShopsanityGiantWalletWeight"), mOptionDescriptions[RSK_SHOPSANITY_PRICES_GIANT_WALLET_WEIGHT], WidgetType::Slider, 10, true, IMFLAG_NONE); - OPT_U8(RSK_SHOPSANITY_PRICES_TYCOON_WALLET_WEIGHT, "Shops Tycoon Wallet Weight", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShopsanityTycoonWalletWeight"), mOptionDescriptions[RSK_SHOPSANITY_PRICES_TYCOON_WALLET_WEIGHT], WidgetType::Slider, 10, true, IMFLAG_NONE); + OPT_CALLBACK(RSK_TRIFORCE_HUNT, { + // Remove the pieces required/total sliders and add a separator after Tirforce Hunt if Triforce Hunt is off + if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("TriforceHunt"), RO_GENERIC_OFF) == RO_GENERIC_OFF) { + mOptions[RSK_TRIFORCE_HUNT_PIECES_REQUIRED].Hide(); + mOptions[RSK_TRIFORCE_HUNT_PIECES_TOTAL].Hide(); + mOptions[RSK_GANONS_BOSS_KEY].Enable(); + } else { + mOptions[RSK_TRIFORCE_HUNT_PIECES_REQUIRED].Unhide(); + mOptions[RSK_TRIFORCE_HUNT_PIECES_TOTAL].Unhide(); + mOptions[RSK_GANONS_BOSS_KEY].Disable( + "This option is disabled because Triforce Hunt is enabled." + "Ganon's Boss key\nwill instead be given to you after Triforce Hunt completion."); + } + }); + OPT_U8(RSK_TRIFORCE_HUNT_PIECES_TOTAL, "Triforce Hunt Total Pieces", {NumOpts(1, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("TriforceHuntTotalPieces"), mOptionDescriptions[RSK_TRIFORCE_HUNT_PIECES_TOTAL], WIDGET_CVAR_SLIDER_INT, 29, false, nullptr, IMFLAG_NONE); + OPT_CALLBACK(RSK_TRIFORCE_HUNT_PIECES_TOTAL, { + // Update triforce pieces required to be capped at the current value for pieces total. + const uint8_t triforceTotal = CVarGetInteger(CVAR_RANDOMIZER_SETTING("TriforceHuntTotalPieces"), 30); + if (mOptions[RSK_TRIFORCE_HUNT_PIECES_REQUIRED].GetOptionCount() != triforceTotal + 1) { + mOptions[RSK_TRIFORCE_HUNT_PIECES_REQUIRED].ChangeOptions(NumOpts(1, triforceTotal + 1)); + } + }); + OPT_U8(RSK_TRIFORCE_HUNT_PIECES_REQUIRED, "Triforce Hunt Required Pieces", {NumOpts(1, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("TriforceHuntRequiredPieces"), mOptionDescriptions[RSK_TRIFORCE_HUNT_PIECES_REQUIRED], WIDGET_CVAR_SLIDER_INT, 19); + OPT_U8(RSK_MQ_DUNGEON_RANDOM, "MQ Dungeon Setting", {"None", "Set Number", "Random", "Selection Only"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MQDungeons"), mOptionDescriptions[RSK_MQ_DUNGEON_RANDOM], WIDGET_CVAR_COMBOBOX, RO_MQ_DUNGEONS_NONE, false, nullptr, IMFLAG_NONE); + OPT_CALLBACK(RSK_MQ_DUNGEON_RANDOM, { + 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: + mOptions[RSK_MQ_DUNGEON_COUNT].Hide(); + mOptions[RSK_MQ_DUNGEON_SET].Hide(); + break; + // If Set Number, remove the separator and show both the count slider and the + // individual dungeon selection toggle. + case RO_MQ_DUNGEONS_SET_NUMBER: + mOptions[RSK_MQ_DUNGEON_COUNT].Unhide(); + mOptions[RSK_MQ_DUNGEON_SET].Unhide(); + break; + // else if random number or selection only, remove the separator and only show + // the individual dungeon selection toggle. + case RO_MQ_DUNGEONS_RANDOM_NUMBER: + mOptions[RSK_MQ_DUNGEON_COUNT].Hide(); + mOptions[RSK_MQ_DUNGEON_SET].Unhide(); + break; + case RO_MQ_DUNGEONS_SELECTION: + 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 || + 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_DEKU_TREE].Unhide(); + mOptions[RSK_MQ_DODONGOS_CAVERN].Unhide(); + mOptions[RSK_MQ_JABU_JABU].Unhide(); + mOptions[RSK_MQ_FOREST_TEMPLE].Unhide(); + mOptions[RSK_MQ_FIRE_TEMPLE].Unhide(); + mOptions[RSK_MQ_WATER_TEMPLE].Unhide(); + mOptions[RSK_MQ_SPIRIT_TEMPLE].Unhide(); + mOptions[RSK_MQ_SHADOW_TEMPLE].Unhide(); + mOptions[RSK_MQ_BOTTOM_OF_THE_WELL].Unhide(); + mOptions[RSK_MQ_ICE_CAVERN].Unhide(); + mOptions[RSK_MQ_GTG].Unhide(); + mOptions[RSK_MQ_GANONS_CASTLE].Unhide(); + } else { + // If those are not shown, add a separator after the Set Dungeons checkbox. + mOptions[RSK_MQ_DEKU_TREE].Hide(); + mOptions[RSK_MQ_DODONGOS_CAVERN].Hide(); + mOptions[RSK_MQ_JABU_JABU].Hide(); + mOptions[RSK_MQ_FOREST_TEMPLE].Hide(); + mOptions[RSK_MQ_FIRE_TEMPLE].Hide(); + mOptions[RSK_MQ_WATER_TEMPLE].Hide(); + mOptions[RSK_MQ_SPIRIT_TEMPLE].Hide(); + mOptions[RSK_MQ_SHADOW_TEMPLE].Hide(); + mOptions[RSK_MQ_BOTTOM_OF_THE_WELL].Hide(); + mOptions[RSK_MQ_ICE_CAVERN].Hide(); + mOptions[RSK_MQ_GTG].Hide(); + mOptions[RSK_MQ_GANONS_CASTLE].Hide(); + } + }); + OPT_U8(RSK_MQ_DUNGEON_COUNT, "MQ Dungeon Count", {NumOpts(0, 12)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MQDungeonCount"), "", WIDGET_CVAR_SLIDER_INT, 12, true, nullptr, IMFLAG_NONE); + OPT_BOOL(RSK_MQ_DUNGEON_SET, "Set Dungeon Quests", {"Off", "On"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MQDungeonsSelection"), mOptionDescriptions[RSK_MQ_DUNGEON_SET], WIDGET_CVAR_CHECKBOX, false, false, nullptr, IMFLAG_NONE); + OPT_CALLBACK(RSK_MQ_DUNGEON_SET, { + // 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 || + 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_DEKU_TREE].Unhide(); + mOptions[RSK_MQ_DODONGOS_CAVERN].Unhide(); + mOptions[RSK_MQ_JABU_JABU].Unhide(); + mOptions[RSK_MQ_FOREST_TEMPLE].Unhide(); + mOptions[RSK_MQ_FIRE_TEMPLE].Unhide(); + mOptions[RSK_MQ_WATER_TEMPLE].Unhide(); + mOptions[RSK_MQ_SPIRIT_TEMPLE].Unhide(); + mOptions[RSK_MQ_SHADOW_TEMPLE].Unhide(); + mOptions[RSK_MQ_BOTTOM_OF_THE_WELL].Unhide(); + mOptions[RSK_MQ_ICE_CAVERN].Unhide(); + mOptions[RSK_MQ_GTG].Unhide(); + mOptions[RSK_MQ_GANONS_CASTLE].Unhide(); + } else { + // If those are not shown, add a separator after the Set Dungeons checkbox. + mOptions[RSK_MQ_DEKU_TREE].Hide(); + mOptions[RSK_MQ_DODONGOS_CAVERN].Hide(); + mOptions[RSK_MQ_JABU_JABU].Hide(); + mOptions[RSK_MQ_FOREST_TEMPLE].Hide(); + mOptions[RSK_MQ_FIRE_TEMPLE].Hide(); + mOptions[RSK_MQ_WATER_TEMPLE].Hide(); + mOptions[RSK_MQ_SPIRIT_TEMPLE].Hide(); + mOptions[RSK_MQ_SHADOW_TEMPLE].Hide(); + mOptions[RSK_MQ_BOTTOM_OF_THE_WELL].Hide(); + mOptions[RSK_MQ_ICE_CAVERN].Hide(); + mOptions[RSK_MQ_GTG].Hide(); + mOptions[RSK_MQ_GANONS_CASTLE].Hide(); + } + }); + OPT_U8(RSK_MQ_DEKU_TREE, "Deku Tree Quest", {"Vanilla", "Master Quest", "Random"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MQDungeonsDekuTree"), "", WIDGET_CVAR_COMBOBOX, RO_MQ_SET_VANILLA, false, nullptr, IMFLAG_NONE); + OPT_U8(RSK_MQ_DODONGOS_CAVERN, "Dodongo's Cavern Quest", {"Vanilla", "Master Quest", "Random"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MQDungeonsDodongosCavern"), "", WIDGET_CVAR_COMBOBOX, RO_MQ_SET_VANILLA, false, nullptr, IMFLAG_NONE); + OPT_U8(RSK_MQ_JABU_JABU, "Jabu-Jabu's Belly Quest", {"Vanilla", "Master Quest", "Random"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MQDungeonsJabuJabu"), "", WIDGET_CVAR_COMBOBOX, RO_MQ_SET_VANILLA, false, nullptr, IMFLAG_NONE); + OPT_U8(RSK_MQ_FOREST_TEMPLE, "Forest Temple Quest", {"Vanilla", "Master Quest", "Random"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MQDungeonsForestTemple"), "", WIDGET_CVAR_COMBOBOX, RO_MQ_SET_VANILLA, false, nullptr, IMFLAG_NONE); + OPT_U8(RSK_MQ_FIRE_TEMPLE, "Fire Temple Quest", {"Vanilla", "Master Quest", "Random"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MQDungeonsFireTemple"), "", WIDGET_CVAR_COMBOBOX, RO_MQ_SET_VANILLA, false, nullptr, IMFLAG_NONE); + OPT_U8(RSK_MQ_WATER_TEMPLE, "Water Temple Quest", {"Vanilla", "Master Quest", "Random"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MQDungeonsWaterTemple"), "", WIDGET_CVAR_COMBOBOX, RO_MQ_SET_VANILLA, false, nullptr, IMFLAG_NONE); + OPT_U8(RSK_MQ_SPIRIT_TEMPLE, "Spirit Temple Quest", {"Vanilla", "Master Quest", "Random"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MQDungeonsSpiritTemple"), "", WIDGET_CVAR_COMBOBOX, RO_MQ_SET_VANILLA, false, nullptr, IMFLAG_NONE); + OPT_U8(RSK_MQ_SHADOW_TEMPLE, "Shadow Temple Quest", {"Vanilla", "Master Quest", "Random"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MQDungeonsShadowTemple"), "", WIDGET_CVAR_COMBOBOX, RO_MQ_SET_VANILLA, false, nullptr, IMFLAG_NONE); + OPT_U8(RSK_MQ_BOTTOM_OF_THE_WELL, "Bottom of the Well Quest", {"Vanilla", "Master Quest", "Random"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MQDungeonsBottomOfTheWell"), "", WIDGET_CVAR_COMBOBOX, RO_MQ_SET_VANILLA, false, nullptr, IMFLAG_NONE); + OPT_U8(RSK_MQ_ICE_CAVERN, "Ice Cavern Quest", {"Vanilla", "Master Quest", "Random"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MQDungeonsIceCavern"), "", WIDGET_CVAR_COMBOBOX, RO_MQ_SET_VANILLA, false, nullptr, IMFLAG_NONE); + OPT_U8(RSK_MQ_GTG, "Gerudo Training Ground Quest", {"Vanilla", "Master Quest", "Random"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MQDungeonsGTG"), "", WIDGET_CVAR_COMBOBOX, RO_MQ_SET_VANILLA, false, nullptr, IMFLAG_NONE); + OPT_U8(RSK_MQ_GANONS_CASTLE, "Ganon's Castle Quest", {"Vanilla", "Master Quest", "Random"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MQDungeonsGanonsCastle"), "", WIDGET_CVAR_COMBOBOX, RO_MQ_SET_VANILLA); + OPT_U8(RSK_SHUFFLE_DUNGEON_REWARDS, "Shuffle Dungeon Rewards", {"Vanilla", "End of Dungeons", "Any Dungeon", "Overworld", "Anywhere"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleDungeonReward"), mOptionDescriptions[RSK_SHUFFLE_DUNGEON_REWARDS], WIDGET_CVAR_COMBOBOX, RO_DUNGEON_REWARDS_END_OF_DUNGEON); + OPT_CALLBACK(RSK_SHUFFLE_DUNGEON_REWARDS, { + // Link's Pocket - Disabled when Dungeon Rewards are shuffled to End of Dungeon + if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleDungeonReward"), RO_DUNGEON_REWARDS_END_OF_DUNGEON) == + RO_DUNGEON_REWARDS_END_OF_DUNGEON) { + mOptions[RSK_LINKS_POCKET].Disable( + "This option is disabled because \"Dungeon Rewards\" are shuffled to \"End of Dungeons\"."); + } else { + mOptions[RSK_LINKS_POCKET].Enable(); + } + }); + OPT_U8(RSK_LINKS_POCKET, "Link's Pocket", {"Dungeon Reward", "Advancement", "Anything", "Nothing"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LinksPocket"), "", WIDGET_CVAR_COMBOBOX, RO_LINKS_POCKET_DUNGEON_REWARD); + OPT_U8(RSK_SHUFFLE_SONGS, "Shuffle Songs", {"Off", "Song Locations", "Dungeon Rewards", "Anywhere"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleSongs"), mOptionDescriptions[RSK_SHUFFLE_SONGS], WIDGET_CVAR_COMBOBOX, RO_SONG_SHUFFLE_SONG_LOCATIONS); + OPT_U8(RSK_SHOPSANITY, "Shop Shuffle", {"Off", "Specific Count", "Random"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("Shopsanity"), mOptionDescriptions[RSK_SHOPSANITY], WIDGET_CVAR_COMBOBOX, RO_SHOPSANITY_OFF); + OPT_CALLBACK(RSK_SHOPSANITY, { + // Hide shopsanity prices if shopsanity is off or zero + switch (CVarGetInteger(CVAR_RANDOMIZER_SETTING("Shopsanity"), RO_SHOPSANITY_OFF)) { + case RO_SHOPSANITY_OFF: + mOptions[RSK_SHOPSANITY].AddFlag(IMFLAG_SEPARATOR_BOTTOM); + mOptions[RSK_SHOPSANITY_COUNT].Hide(); + mOptions[RSK_SHOPSANITY_COUNT].Hide(); + mOptions[RSK_SHOPSANITY_PRICES].Hide(); + mOptions[RSK_SHOPSANITY_PRICES_AFFORDABLE].Hide(); + mOptions[RSK_SHOPSANITY_PRICES_FIXED_PRICE].Hide(); + mOptions[RSK_SHOPSANITY_PRICES_RANGE_1].Hide(); + mOptions[RSK_SHOPSANITY_PRICES_RANGE_2].Hide(); + mOptions[RSK_SHOPSANITY_PRICES_NO_WALLET_WEIGHT].Hide(); + mOptions[RSK_SHOPSANITY_PRICES_CHILD_WALLET_WEIGHT].Hide(); + mOptions[RSK_SHOPSANITY_PRICES_ADULT_WALLET_WEIGHT].Hide(); + mOptions[RSK_SHOPSANITY_PRICES_GIANT_WALLET_WEIGHT].Hide(); + mOptions[RSK_SHOPSANITY_PRICES_TYCOON_WALLET_WEIGHT].Hide(); + break; + case RO_SHOPSANITY_SPECIFIC_COUNT: + mOptions[RSK_SHOPSANITY_COUNT].Unhide(); + HandleShopsanityPriceUI(); + break; + case RO_SHOPSANITY_RANDOM: + mOptions[RSK_SHOPSANITY_COUNT].Hide(); + HandleShopsanityPriceUI(); + break; + } + }); + OPT_U8(RSK_SHOPSANITY_COUNT, "Shops Item Count", {NumOpts(0, 7/*8*/)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShopsanityCount"), mOptionDescriptions[RSK_SHOPSANITY_COUNT], WIDGET_CVAR_SLIDER_INT, 0, false, nullptr, IMFLAG_NONE); + OPT_U8(RSK_SHOPSANITY_PRICES, "Shops Prices", {"Vanilla", "Cheap Balanced", "Balanced", "Fixed", "Range", "Set By Wallet"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShopsanityPrices"), mOptionDescriptions[RSK_SHOPSANITY_PRICES], WIDGET_CVAR_COMBOBOX, RO_PRICE_VANILLA, false, nullptr, IMFLAG_NONE); + OPT_U8(RSK_SHOPSANITY_PRICES_FIXED_PRICE, "Shops Fixed Price", {NumOpts(0, 995, 5)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShopsanityFixedPrice"), mOptionDescriptions[RSK_SHOPSANITY_PRICES_FIXED_PRICE], WIDGET_CVAR_SLIDER_INT, 10, true); + OPT_U8(RSK_SHOPSANITY_PRICES_RANGE_1, "Shops Lower Bound", {NumOpts(0, 995, 5)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShopsanityPriceRange1"), mOptionDescriptions[RSK_SHOPSANITY_PRICES_RANGE_1], WIDGET_CVAR_SLIDER_INT, 10, true, nullptr, IMFLAG_NONE); + OPT_U8(RSK_SHOPSANITY_PRICES_RANGE_2, "Shops Upper Bound", {NumOpts(0, 995, 5)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShopsanityPriceRange2"), mOptionDescriptions[RSK_SHOPSANITY_PRICES_RANGE_2], WIDGET_CVAR_SLIDER_INT, 100, true, nullptr, IMFLAG_NONE); + OPT_U8(RSK_SHOPSANITY_PRICES_NO_WALLET_WEIGHT, "Shops No Wallet Weight", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShopsanityNoWalletWeight"), mOptionDescriptions[RSK_SHOPSANITY_PRICES_NO_WALLET_WEIGHT], WIDGET_CVAR_SLIDER_INT, 10, true, nullptr, IMFLAG_NONE); + OPT_U8(RSK_SHOPSANITY_PRICES_CHILD_WALLET_WEIGHT, "Shops Child Wallet Weight", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShopsanityChildWalletWeight"), mOptionDescriptions[RSK_SHOPSANITY_PRICES_CHILD_WALLET_WEIGHT], WIDGET_CVAR_SLIDER_INT, 10, true, nullptr, IMFLAG_NONE); + OPT_U8(RSK_SHOPSANITY_PRICES_ADULT_WALLET_WEIGHT, "Shops Adult Wallet Weight", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShopsanityAdultWalletWeight"), mOptionDescriptions[RSK_SHOPSANITY_PRICES_ADULT_WALLET_WEIGHT], WIDGET_CVAR_SLIDER_INT, 10, true, nullptr, IMFLAG_NONE); + OPT_U8(RSK_SHOPSANITY_PRICES_GIANT_WALLET_WEIGHT, "Shops Giant Wallet Weight", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShopsanityGiantWalletWeight"), mOptionDescriptions[RSK_SHOPSANITY_PRICES_GIANT_WALLET_WEIGHT], WIDGET_CVAR_SLIDER_INT, 10, true, nullptr, IMFLAG_NONE); + OPT_U8(RSK_SHOPSANITY_PRICES_TYCOON_WALLET_WEIGHT, "Shops Tycoon Wallet Weight", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShopsanityTycoonWalletWeight"), mOptionDescriptions[RSK_SHOPSANITY_PRICES_TYCOON_WALLET_WEIGHT], WIDGET_CVAR_SLIDER_INT, 10, true, nullptr, IMFLAG_NONE); OPT_BOOL(RSK_SHOPSANITY_PRICES_AFFORDABLE, "Shops Affordable Prices", CVAR_RANDOMIZER_SETTING("ShopsanityPricesAffordable"), mOptionDescriptions[RSK_SHOPSANITY_PRICES_AFFORDABLE]); - OPT_U8(RSK_SHUFFLE_TOKENS, "Token Shuffle", {"Off", "Dungeons", "Overworld", "All Tokens"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleTokens"), mOptionDescriptions[RSK_SHUFFLE_TOKENS], WidgetType::Combobox, RO_TOKENSANITY_OFF); - OPT_U8(RSK_SHUFFLE_SCRUBS, "Scrubs Shuffle", {"Off", "One-Time Only", "All"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleScrubs"), mOptionDescriptions[RSK_SHUFFLE_SCRUBS], WidgetType::Combobox, RO_SCRUBS_OFF); - OPT_U8(RSK_SCRUBS_PRICES, "Scrubs Prices", {"Vanilla", "Cheap Balanced", "Balanced", "Fixed", "Range", "Set By Wallet"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ScrubsPrices"), mOptionDescriptions[RSK_SCRUBS_PRICES], WidgetType::Combobox, RO_PRICE_VANILLA, false, IMFLAG_NONE); - OPT_U8(RSK_SCRUBS_PRICES_FIXED_PRICE, "Scrubs Fixed Price", {NumOpts(0, 995, 5)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ScrubsFixedPrice"), mOptionDescriptions[RSK_SCRUBS_PRICES_FIXED_PRICE], WidgetType::Slider, 10, true); - OPT_U8(RSK_SCRUBS_PRICES_RANGE_1, "Scrubs Lower Bound", {NumOpts(0, 995, 5)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ScrubsPriceRange1"), mOptionDescriptions[RSK_SCRUBS_PRICES_RANGE_1], WidgetType::Slider, 10, true, IMFLAG_NONE); - OPT_U8(RSK_SCRUBS_PRICES_RANGE_2, "Scrubs Upper Bound", {NumOpts(0, 995, 5)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ScrubsPriceRange2"), mOptionDescriptions[RSK_SCRUBS_PRICES_RANGE_2], WidgetType::Slider, 100, true, IMFLAG_NONE); - OPT_U8(RSK_SCRUBS_PRICES_NO_WALLET_WEIGHT, "Scrubs No Wallet Weight", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ScrubsNoWalletWeight"), mOptionDescriptions[RSK_SCRUBS_PRICES_NO_WALLET_WEIGHT], WidgetType::Slider, 10, true, IMFLAG_NONE); - OPT_U8(RSK_SCRUBS_PRICES_CHILD_WALLET_WEIGHT, "Scrubs Child Wallet Weight", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ScrubsChildWalletWeight"), mOptionDescriptions[RSK_SCRUBS_PRICES_CHILD_WALLET_WEIGHT], WidgetType::Slider, 10, true, IMFLAG_NONE); - OPT_U8(RSK_SCRUBS_PRICES_ADULT_WALLET_WEIGHT, "Scrubs Adult Wallet Weight", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ScrubsAdultWalletWeight"), mOptionDescriptions[RSK_SCRUBS_PRICES_ADULT_WALLET_WEIGHT], WidgetType::Slider, 10, true, IMFLAG_NONE); - OPT_U8(RSK_SCRUBS_PRICES_GIANT_WALLET_WEIGHT, "Scrubs Giant Wallet Weight", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ScrubsGiantWalletWeight"), mOptionDescriptions[RSK_SCRUBS_PRICES_GIANT_WALLET_WEIGHT], WidgetType::Slider, 10, true, IMFLAG_NONE); - OPT_U8(RSK_SCRUBS_PRICES_TYCOON_WALLET_WEIGHT, "Scrubs Tycoon Wallet Weight", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ScrubsTycoonWalletWeight"), mOptionDescriptions[RSK_SCRUBS_PRICES_TYCOON_WALLET_WEIGHT], WidgetType::Slider, 10, true, IMFLAG_NONE); + OPT_U8(RSK_SHUFFLE_TOKENS, "Token Shuffle", {"Off", "Dungeons", "Overworld", "All Tokens"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleTokens"), mOptionDescriptions[RSK_SHUFFLE_TOKENS], WIDGET_CVAR_COMBOBOX, RO_TOKENSANITY_OFF); + OPT_U8(RSK_SHUFFLE_SCRUBS, "Scrubs Shuffle", {"Off", "One-Time Only", "All"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleScrubs"), mOptionDescriptions[RSK_SHUFFLE_SCRUBS], WIDGET_CVAR_COMBOBOX, RO_SCRUBS_OFF); + OPT_CALLBACK(RSK_SHUFFLE_SCRUBS, { + bool isTycoon = CVarGetInteger(CVAR_RANDOMIZER_SETTING("IncludeTycoonWallet"), RO_GENERIC_OFF); + switch (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleScrubs"), RO_SCRUBS_OFF)) { + case RO_SCRUBS_OFF: + mOptions[RSK_SCRUBS_PRICES].Hide(); + mOptions[RSK_SCRUBS_PRICES_AFFORDABLE].Hide(); + mOptions[RSK_SCRUBS_PRICES_FIXED_PRICE].Hide(); + mOptions[RSK_SCRUBS_PRICES_RANGE_1].Hide(); + mOptions[RSK_SCRUBS_PRICES_RANGE_2].Hide(); + mOptions[RSK_SCRUBS_PRICES_NO_WALLET_WEIGHT].Hide(); + mOptions[RSK_SCRUBS_PRICES_CHILD_WALLET_WEIGHT].Hide(); + mOptions[RSK_SCRUBS_PRICES_ADULT_WALLET_WEIGHT].Hide(); + mOptions[RSK_SCRUBS_PRICES_GIANT_WALLET_WEIGHT].Hide(); + mOptions[RSK_SCRUBS_PRICES_TYCOON_WALLET_WEIGHT].Hide(); + break; + default: + mOptions[RSK_SCRUBS_PRICES].Unhide(); + switch (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ScrubsPrices"), RO_PRICE_VANILLA)) { + case RO_PRICE_FIXED: + mOptions[RSK_SCRUBS_PRICES_FIXED_PRICE].Unhide(); + mOptions[RSK_SCRUBS_PRICES_RANGE_1].Hide(); + mOptions[RSK_SCRUBS_PRICES_RANGE_2].Hide(); + mOptions[RSK_SCRUBS_PRICES_NO_WALLET_WEIGHT].Hide(); + mOptions[RSK_SCRUBS_PRICES_CHILD_WALLET_WEIGHT].Hide(); + mOptions[RSK_SCRUBS_PRICES_ADULT_WALLET_WEIGHT].Hide(); + mOptions[RSK_SCRUBS_PRICES_GIANT_WALLET_WEIGHT].Hide(); + mOptions[RSK_SCRUBS_PRICES_TYCOON_WALLET_WEIGHT].Hide(); + if (isTycoon ? mOptions[RSK_SCRUBS_PRICES_FIXED_PRICE].GetOptionCount() == 501 + : mOptions[RSK_SCRUBS_PRICES_FIXED_PRICE].GetOptionCount() == 1000) { + mOptions[RSK_SCRUBS_PRICES_FIXED_PRICE].ChangeOptions(isTycoon ? NumOpts(0, 999) + : NumOpts(0, 500)); + } + mOptions[RSK_SCRUBS_PRICES_AFFORDABLE].Hide(); + break; + case RO_PRICE_RANGE: + mOptions[RSK_SCRUBS_PRICES_FIXED_PRICE].Hide(); + mOptions[RSK_SCRUBS_PRICES_RANGE_1].Unhide(); + mOptions[RSK_SCRUBS_PRICES_RANGE_2].Unhide(); + mOptions[RSK_SCRUBS_PRICES_NO_WALLET_WEIGHT].Hide(); + mOptions[RSK_SCRUBS_PRICES_CHILD_WALLET_WEIGHT].Hide(); + mOptions[RSK_SCRUBS_PRICES_ADULT_WALLET_WEIGHT].Hide(); + mOptions[RSK_SCRUBS_PRICES_GIANT_WALLET_WEIGHT].Hide(); + mOptions[RSK_SCRUBS_PRICES_TYCOON_WALLET_WEIGHT].Hide(); + if (isTycoon ? mOptions[RSK_SCRUBS_PRICES_RANGE_1].GetOptionCount() == 101 + : mOptions[RSK_SCRUBS_PRICES_RANGE_1].GetOptionCount() == 200) { + mOptions[RSK_SCRUBS_PRICES_RANGE_1].ChangeOptions(isTycoon ? NumOpts(0, 995, 5) + : NumOpts(0, 500, 5)); + mOptions[RSK_SCRUBS_PRICES_RANGE_2].ChangeOptions(isTycoon ? NumOpts(0, 995, 5) + : NumOpts(0, 500, 5)); + } + mOptions[RSK_SCRUBS_PRICES_AFFORDABLE].Unhide(); + break; + case RO_PRICE_SET_BY_WALLET: + mOptions[RSK_SCRUBS_PRICES_FIXED_PRICE].Hide(); + mOptions[RSK_SCRUBS_PRICES_RANGE_1].Hide(); + mOptions[RSK_SCRUBS_PRICES_RANGE_2].Hide(); + mOptions[RSK_SCRUBS_PRICES_NO_WALLET_WEIGHT].Unhide(); + mOptions[RSK_SCRUBS_PRICES_CHILD_WALLET_WEIGHT].Unhide(); + mOptions[RSK_SCRUBS_PRICES_ADULT_WALLET_WEIGHT].Unhide(); + mOptions[RSK_SCRUBS_PRICES_GIANT_WALLET_WEIGHT].Unhide(); + if (isTycoon) { + mOptions[RSK_SCRUBS_PRICES_TYCOON_WALLET_WEIGHT].Unhide(); + } else { + mOptions[RSK_SCRUBS_PRICES_TYCOON_WALLET_WEIGHT].Hide(); + } + mOptions[RSK_SCRUBS_PRICES_AFFORDABLE].Unhide(); + break; + default: + mOptions[RSK_SCRUBS_PRICES_FIXED_PRICE].Hide(); + mOptions[RSK_SCRUBS_PRICES_RANGE_1].Hide(); + mOptions[RSK_SCRUBS_PRICES_RANGE_2].Hide(); + mOptions[RSK_SCRUBS_PRICES_NO_WALLET_WEIGHT].Hide(); + mOptions[RSK_SCRUBS_PRICES_CHILD_WALLET_WEIGHT].Hide(); + mOptions[RSK_SCRUBS_PRICES_ADULT_WALLET_WEIGHT].Hide(); + mOptions[RSK_SCRUBS_PRICES_GIANT_WALLET_WEIGHT].Hide(); + mOptions[RSK_SCRUBS_PRICES_TYCOON_WALLET_WEIGHT].Hide(); + mOptions[RSK_SCRUBS_PRICES_AFFORDABLE].Unhide(); + break; + } + break; + } + }); + OPT_U8(RSK_SCRUBS_PRICES, "Scrubs Prices", {"Vanilla", "Cheap Balanced", "Balanced", "Fixed", "Range", "Set By Wallet"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ScrubsPrices"), mOptionDescriptions[RSK_SCRUBS_PRICES], WIDGET_CVAR_COMBOBOX, RO_PRICE_VANILLA, false, nullptr, IMFLAG_NONE); + OPT_CALLBACK(RSK_SCRUBS_PRICES, { + bool isTycoon = CVarGetInteger(CVAR_RANDOMIZER_SETTING("IncludeTycoonWallet"), RO_GENERIC_OFF); + switch (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ScrubsPrices"), RO_PRICE_VANILLA)) { + case RO_PRICE_FIXED: + mOptions[RSK_SCRUBS_PRICES_FIXED_PRICE].Unhide(); + mOptions[RSK_SCRUBS_PRICES_RANGE_1].Hide(); + mOptions[RSK_SCRUBS_PRICES_RANGE_2].Hide(); + mOptions[RSK_SCRUBS_PRICES_NO_WALLET_WEIGHT].Hide(); + mOptions[RSK_SCRUBS_PRICES_CHILD_WALLET_WEIGHT].Hide(); + mOptions[RSK_SCRUBS_PRICES_ADULT_WALLET_WEIGHT].Hide(); + mOptions[RSK_SCRUBS_PRICES_GIANT_WALLET_WEIGHT].Hide(); + mOptions[RSK_SCRUBS_PRICES_TYCOON_WALLET_WEIGHT].Hide(); + if (isTycoon ? mOptions[RSK_SCRUBS_PRICES_FIXED_PRICE].GetOptionCount() == 501 + : mOptions[RSK_SCRUBS_PRICES_FIXED_PRICE].GetOptionCount() == 1000) { + mOptions[RSK_SCRUBS_PRICES_FIXED_PRICE].ChangeOptions(isTycoon ? NumOpts(0, 999) + : NumOpts(0, 500)); + } + mOptions[RSK_SCRUBS_PRICES_AFFORDABLE].Hide(); + break; + case RO_PRICE_RANGE: + mOptions[RSK_SCRUBS_PRICES_FIXED_PRICE].Hide(); + mOptions[RSK_SCRUBS_PRICES_RANGE_1].Unhide(); + mOptions[RSK_SCRUBS_PRICES_RANGE_2].Unhide(); + mOptions[RSK_SCRUBS_PRICES_NO_WALLET_WEIGHT].Hide(); + mOptions[RSK_SCRUBS_PRICES_CHILD_WALLET_WEIGHT].Hide(); + mOptions[RSK_SCRUBS_PRICES_ADULT_WALLET_WEIGHT].Hide(); + mOptions[RSK_SCRUBS_PRICES_GIANT_WALLET_WEIGHT].Hide(); + mOptions[RSK_SCRUBS_PRICES_TYCOON_WALLET_WEIGHT].Hide(); + if (isTycoon ? mOptions[RSK_SCRUBS_PRICES_RANGE_1].GetOptionCount() == 101 + : mOptions[RSK_SCRUBS_PRICES_RANGE_1].GetOptionCount() == 200) { + mOptions[RSK_SCRUBS_PRICES_RANGE_1].ChangeOptions(isTycoon ? NumOpts(0, 995, 5) + : NumOpts(0, 500, 5)); + mOptions[RSK_SCRUBS_PRICES_RANGE_2].ChangeOptions(isTycoon ? NumOpts(0, 995, 5) + : NumOpts(0, 500, 5)); + } + mOptions[RSK_SCRUBS_PRICES_AFFORDABLE].Unhide(); + break; + case RO_PRICE_SET_BY_WALLET: + mOptions[RSK_SCRUBS_PRICES_FIXED_PRICE].Hide(); + mOptions[RSK_SCRUBS_PRICES_RANGE_1].Hide(); + mOptions[RSK_SCRUBS_PRICES_RANGE_2].Hide(); + mOptions[RSK_SCRUBS_PRICES_NO_WALLET_WEIGHT].Unhide(); + mOptions[RSK_SCRUBS_PRICES_CHILD_WALLET_WEIGHT].Unhide(); + mOptions[RSK_SCRUBS_PRICES_ADULT_WALLET_WEIGHT].Unhide(); + mOptions[RSK_SCRUBS_PRICES_GIANT_WALLET_WEIGHT].Unhide(); + if (isTycoon) { + mOptions[RSK_SCRUBS_PRICES_TYCOON_WALLET_WEIGHT].Unhide(); + } else { + mOptions[RSK_SCRUBS_PRICES_TYCOON_WALLET_WEIGHT].Hide(); + } + mOptions[RSK_SCRUBS_PRICES_AFFORDABLE].Unhide(); + break; + default: + mOptions[RSK_SCRUBS_PRICES_FIXED_PRICE].Hide(); + mOptions[RSK_SCRUBS_PRICES_RANGE_1].Hide(); + mOptions[RSK_SCRUBS_PRICES_RANGE_2].Hide(); + mOptions[RSK_SCRUBS_PRICES_NO_WALLET_WEIGHT].Hide(); + mOptions[RSK_SCRUBS_PRICES_CHILD_WALLET_WEIGHT].Hide(); + mOptions[RSK_SCRUBS_PRICES_ADULT_WALLET_WEIGHT].Hide(); + mOptions[RSK_SCRUBS_PRICES_GIANT_WALLET_WEIGHT].Hide(); + mOptions[RSK_SCRUBS_PRICES_TYCOON_WALLET_WEIGHT].Hide(); + mOptions[RSK_SCRUBS_PRICES_AFFORDABLE].Unhide(); + break; + } + }); + OPT_U8(RSK_SCRUBS_PRICES_FIXED_PRICE, "Scrubs Fixed Price", {NumOpts(0, 995, 5)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ScrubsFixedPrice"), mOptionDescriptions[RSK_SCRUBS_PRICES_FIXED_PRICE], WIDGET_CVAR_SLIDER_INT, 10, true); + OPT_U8(RSK_SCRUBS_PRICES_RANGE_1, "Scrubs Lower Bound", {NumOpts(0, 995, 5)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ScrubsPriceRange1"), mOptionDescriptions[RSK_SCRUBS_PRICES_RANGE_1], WIDGET_CVAR_SLIDER_INT, 10, true, nullptr, IMFLAG_NONE); + OPT_U8(RSK_SCRUBS_PRICES_RANGE_2, "Scrubs Upper Bound", {NumOpts(0, 995, 5)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ScrubsPriceRange2"), mOptionDescriptions[RSK_SCRUBS_PRICES_RANGE_2], WIDGET_CVAR_SLIDER_INT, 100, true, nullptr, IMFLAG_NONE); + OPT_U8(RSK_SCRUBS_PRICES_NO_WALLET_WEIGHT, "Scrubs No Wallet Weight", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ScrubsNoWalletWeight"), mOptionDescriptions[RSK_SCRUBS_PRICES_NO_WALLET_WEIGHT], WIDGET_CVAR_SLIDER_INT, 10, true, nullptr, IMFLAG_NONE); + OPT_U8(RSK_SCRUBS_PRICES_CHILD_WALLET_WEIGHT, "Scrubs Child Wallet Weight", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ScrubsChildWalletWeight"), mOptionDescriptions[RSK_SCRUBS_PRICES_CHILD_WALLET_WEIGHT], WIDGET_CVAR_SLIDER_INT, 10, true, nullptr, IMFLAG_NONE); + OPT_U8(RSK_SCRUBS_PRICES_ADULT_WALLET_WEIGHT, "Scrubs Adult Wallet Weight", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ScrubsAdultWalletWeight"), mOptionDescriptions[RSK_SCRUBS_PRICES_ADULT_WALLET_WEIGHT], WIDGET_CVAR_SLIDER_INT, 10, true, nullptr, IMFLAG_NONE); + OPT_U8(RSK_SCRUBS_PRICES_GIANT_WALLET_WEIGHT, "Scrubs Giant Wallet Weight", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ScrubsGiantWalletWeight"), mOptionDescriptions[RSK_SCRUBS_PRICES_GIANT_WALLET_WEIGHT], WIDGET_CVAR_SLIDER_INT, 10, true, nullptr, IMFLAG_NONE); + OPT_U8(RSK_SCRUBS_PRICES_TYCOON_WALLET_WEIGHT, "Scrubs Tycoon Wallet Weight", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ScrubsTycoonWalletWeight"), mOptionDescriptions[RSK_SCRUBS_PRICES_TYCOON_WALLET_WEIGHT], WIDGET_CVAR_SLIDER_INT, 10, true, nullptr, IMFLAG_NONE); OPT_BOOL(RSK_SCRUBS_PRICES_AFFORDABLE, "Scrubs Affordable Prices", CVAR_RANDOMIZER_SETTING("ScrubsPricesAffordable"), mOptionDescriptions[RSK_SCRUBS_PRICES_AFFORDABLE]); OPT_BOOL(RSK_SHUFFLE_BEEHIVES, "Shuffle Beehives", CVAR_RANDOMIZER_SETTING("ShuffleBeehives"), mOptionDescriptions[RSK_SHUFFLE_BEEHIVES]); + OPT_CALLBACK(RSK_SHUFFLE_BEEHIVES, { + if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleBeehives"), RO_GENERIC_OFF)) { + mOptions[RSK_SLINGBOW_BREAK_BEEHIVES].Enable(); + } else { + mOptions[RSK_SLINGBOW_BREAK_BEEHIVES].Disable( + "This option is disabled because Shuffle Beehives is not enabled."); + } + }); OPT_BOOL(RSK_SHUFFLE_COWS, "Shuffle Cows", CVAR_RANDOMIZER_SETTING("ShuffleCows"), mOptionDescriptions[RSK_SHUFFLE_COWS]); + OPT_CALLBACK(RSK_SHUFFLE_COWS, { + if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleCows"), RO_GENERIC_OFF)) { + mOptions[RSK_MALON_HINT].Enable(); + } else { + mOptions[RSK_MALON_HINT].Disable("Malon's hint points to a cow, so requires cows to be shuffled."); + } + }); OPT_BOOL(RSK_SHUFFLE_KOKIRI_SWORD, "Shuffle Kokiri Sword", CVAR_RANDOMIZER_SETTING("ShuffleKokiriSword"), mOptionDescriptions[RSK_SHUFFLE_KOKIRI_SWORD]); OPT_BOOL(RSK_SHUFFLE_MASTER_SWORD, "Shuffle Master Sword", CVAR_RANDOMIZER_SETTING("ShuffleMasterSword"), mOptionDescriptions[RSK_SHUFFLE_MASTER_SWORD]); OPT_BOOL(RSK_SHUFFLE_CHILD_WALLET, "Shuffle Child's Wallet", CVAR_RANDOMIZER_SETTING("ShuffleChildWallet"), mOptionDescriptions[RSK_SHUFFLE_CHILD_WALLET], IMFLAG_NONE); OPT_BOOL(RSK_INCLUDE_TYCOON_WALLET, "Include Tycoon Wallet", CVAR_RANDOMIZER_SETTING("IncludeTycoonWallet"), mOptionDescriptions[RSK_INCLUDE_TYCOON_WALLET]); OPT_BOOL(RSK_SHUFFLE_OCARINA, "Shuffle Ocarinas", CVAR_RANDOMIZER_SETTING("ShuffleOcarinas"), mOptionDescriptions[RSK_SHUFFLE_OCARINA]); + OPT_CALLBACK(RSK_SHUFFLE_OCARINA, { + HandleStartingAgeUI(); + }); OPT_BOOL(RSK_SHUFFLE_OCARINA_BUTTONS, "Shuffle Ocarina Buttons", CVAR_RANDOMIZER_SETTING("ShuffleOcarinaButtons"), mOptionDescriptions[RSK_SHUFFLE_OCARINA_BUTTONS]); OPT_BOOL(RSK_SHUFFLE_SWIM, "Shuffle Swim", CVAR_RANDOMIZER_SETTING("ShuffleSwim"), mOptionDescriptions[RSK_SHUFFLE_SWIM]); OPT_BOOL(RSK_SHUFFLE_WEIRD_EGG, "Shuffle Weird Egg", CVAR_RANDOMIZER_SETTING("ShuffleWeirdEgg"), mOptionDescriptions[RSK_SHUFFLE_WEIRD_EGG]); OPT_BOOL(RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD, "Shuffle Gerudo Membership Card", CVAR_RANDOMIZER_SETTING("ShuffleGerudoToken"), mOptionDescriptions[RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD]); - OPT_U8(RSK_SHUFFLE_POTS, "Shuffle Pots", {"Off", "Dungeons", "Overworld", "All Pots"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShufflePots"), mOptionDescriptions[RSK_SHUFFLE_POTS], WidgetType::Combobox, RO_SHUFFLE_POTS_OFF); - OPT_U8(RSK_SHUFFLE_GRASS, "Shuffle Grass", {"Off", "Dungeons", "Overworld", "All Grass/Bushes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleGrass"), mOptionDescriptions[RSK_SHUFFLE_GRASS], WidgetType::Combobox, RO_SHUFFLE_GRASS_OFF); - OPT_U8(RSK_SHUFFLE_CRATES, "Shuffle Crates", {"Off", "Dungeons", "Overworld", "All Crates"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleCrates"), mOptionDescriptions[RSK_SHUFFLE_CRATES], WidgetType::Combobox, RO_SHUFFLE_CRATES_OFF); + OPT_U8(RSK_SHUFFLE_POTS, "Shuffle Pots", {"Off", "Dungeons", "Overworld", "All Pots"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShufflePots"), mOptionDescriptions[RSK_SHUFFLE_POTS], WIDGET_CVAR_COMBOBOX, RO_SHUFFLE_POTS_OFF); + OPT_U8(RSK_SHUFFLE_GRASS, "Shuffle Grass", {"Off", "Dungeons", "Overworld", "All Grass/Bushes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleGrass"), mOptionDescriptions[RSK_SHUFFLE_GRASS], WIDGET_CVAR_COMBOBOX, RO_SHUFFLE_GRASS_OFF); + OPT_U8(RSK_SHUFFLE_CRATES, "Shuffle Crates", {"Off", "Dungeons", "Overworld", "All Crates"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleCrates"), mOptionDescriptions[RSK_SHUFFLE_CRATES], WIDGET_CVAR_COMBOBOX, RO_SHUFFLE_CRATES_OFF); OPT_BOOL(RSK_SHUFFLE_TREES, "Shuffle Trees", CVAR_RANDOMIZER_SETTING("ShuffleTrees"), mOptionDescriptions[RSK_SHUFFLE_TREES]); OPT_BOOL(RSK_SHUFFLE_BUSHES, "Shuffle Bushes", CVAR_RANDOMIZER_SETTING("ShuffleBushes"), mOptionDescriptions[RSK_SHUFFLE_BUSHES]); OPT_BOOL(RSK_SHUFFLE_FISHING_POLE, "Shuffle Fishing Pole", CVAR_RANDOMIZER_SETTING("ShuffleFishingPole"), mOptionDescriptions[RSK_SHUFFLE_FISHING_POLE]); - OPT_U8(RSK_SHUFFLE_MERCHANTS, "Shuffle Merchants", {"Off", "Bean Merchant Only", "All But Beans", "All"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleMerchants"), mOptionDescriptions[RSK_SHUFFLE_MERCHANTS], WidgetType::Combobox, RO_SHUFFLE_MERCHANTS_OFF, IMFLAG_NONE); - OPT_U8(RSK_MERCHANT_PRICES, "Merchant Prices", {"Vanilla", "Cheap Balanced", "Balanced", "Fixed", "Range", "Set By Wallet"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MerchantPrices"), mOptionDescriptions[RSK_MERCHANT_PRICES], WidgetType::Combobox, RO_PRICE_VANILLA, false, IMFLAG_NONE); - OPT_U8(RSK_MERCHANT_PRICES_FIXED_PRICE, "Merchant Fixed Price", {NumOpts(0, 995, 5)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MerchantFixedPrice"), mOptionDescriptions[RSK_MERCHANT_PRICES_FIXED_PRICE], WidgetType::Slider, 10, true); - OPT_U8(RSK_MERCHANT_PRICES_RANGE_1, "Merchant Lower Bound", {NumOpts(0, 995, 5)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MerchantPriceRange1"), mOptionDescriptions[RSK_MERCHANT_PRICES_RANGE_1], WidgetType::Slider, 10, true, IMFLAG_NONE); - OPT_U8(RSK_MERCHANT_PRICES_RANGE_2, "Merchant Upper Bound", {NumOpts(0, 995, 5)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MerchantPriceRange2"), mOptionDescriptions[RSK_MERCHANT_PRICES_RANGE_2], WidgetType::Slider, 100, true, IMFLAG_NONE); - OPT_U8(RSK_MERCHANT_PRICES_NO_WALLET_WEIGHT, "Merchant No Wallet Weight", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MerchantNoWalletWeight"), mOptionDescriptions[RSK_MERCHANT_PRICES_NO_WALLET_WEIGHT], WidgetType::Slider, 10, true, IMFLAG_NONE); - OPT_U8(RSK_MERCHANT_PRICES_CHILD_WALLET_WEIGHT, "Merchant Child Wallet Weight", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MerchantChildWalletWeight"), mOptionDescriptions[RSK_MERCHANT_PRICES_CHILD_WALLET_WEIGHT], WidgetType::Slider, 10, true, IMFLAG_NONE); - OPT_U8(RSK_MERCHANT_PRICES_ADULT_WALLET_WEIGHT, "Merchant Adult Wallet Weight", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MerchantAdultWalletWeight"), mOptionDescriptions[RSK_MERCHANT_PRICES_ADULT_WALLET_WEIGHT], WidgetType::Slider, 10, true, IMFLAG_NONE); - OPT_U8(RSK_MERCHANT_PRICES_GIANT_WALLET_WEIGHT, "Merchant Giant Wallet Weight", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MerchantGiantWalletWeight"), mOptionDescriptions[RSK_MERCHANT_PRICES_GIANT_WALLET_WEIGHT], WidgetType::Slider, 10, true, IMFLAG_NONE); - OPT_U8(RSK_MERCHANT_PRICES_TYCOON_WALLET_WEIGHT, "Merchant Tycoon Wallet Weight", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MerchantTycoonWalletWeight"), mOptionDescriptions[RSK_MERCHANT_PRICES_TYCOON_WALLET_WEIGHT], WidgetType::Slider, 10, true, IMFLAG_NONE); + OPT_CALLBACK(RSK_SHUFFLE_FISHING_POLE, { + // Disable fishing pole hint if the fishing pole is not shuffled + if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleFishingPole"), RO_GENERIC_OFF)) { + mOptions[RSK_FISHING_POLE_HINT].Enable(); + } else { + mOptions[RSK_FISHING_POLE_HINT].Disable("This option is disabled since the fishing pole is not shuffled."); + } + }); + OPT_U8(RSK_SHUFFLE_MERCHANTS, "Shuffle Merchants", {"Off", "Bean Merchant Only", "All But Beans", "All"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleMerchants"), mOptionDescriptions[RSK_SHUFFLE_MERCHANTS], WIDGET_CVAR_COMBOBOX, RO_SHUFFLE_MERCHANTS_OFF, IMFLAG_NONE); + OPT_CALLBACK(RSK_SHUFFLE_MERCHANTS, { + bool isTycoon = CVarGetInteger(CVAR_RANDOMIZER_SETTING("IncludeTycoonWallet"), RO_GENERIC_OFF); + switch (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleMerchants"), RO_SHUFFLE_MERCHANTS_OFF)) { + case RO_SHUFFLE_MERCHANTS_OFF: + mOptions[RSK_MERCHANT_PRICES].Hide(); + mOptions[RSK_MERCHANT_PRICES_AFFORDABLE].Hide(); + mOptions[RSK_MERCHANT_PRICES_FIXED_PRICE].Hide(); + mOptions[RSK_MERCHANT_PRICES_RANGE_1].Hide(); + mOptions[RSK_MERCHANT_PRICES_RANGE_2].Hide(); + mOptions[RSK_MERCHANT_PRICES_NO_WALLET_WEIGHT].Hide(); + mOptions[RSK_MERCHANT_PRICES_CHILD_WALLET_WEIGHT].Hide(); + mOptions[RSK_MERCHANT_PRICES_ADULT_WALLET_WEIGHT].Hide(); + mOptions[RSK_MERCHANT_PRICES_GIANT_WALLET_WEIGHT].Hide(); + mOptions[RSK_MERCHANT_PRICES_TYCOON_WALLET_WEIGHT].Hide(); + break; + default: + mOptions[RSK_MERCHANT_PRICES].Unhide(); + switch (CVarGetInteger(CVAR_RANDOMIZER_SETTING("MerchantPrices"), RO_PRICE_VANILLA)) { + case RO_PRICE_FIXED: + mOptions[RSK_MERCHANT_PRICES_FIXED_PRICE].Unhide(); + mOptions[RSK_MERCHANT_PRICES_RANGE_1].Hide(); + mOptions[RSK_MERCHANT_PRICES_RANGE_2].Hide(); + mOptions[RSK_MERCHANT_PRICES_NO_WALLET_WEIGHT].Hide(); + mOptions[RSK_MERCHANT_PRICES_CHILD_WALLET_WEIGHT].Hide(); + mOptions[RSK_MERCHANT_PRICES_ADULT_WALLET_WEIGHT].Hide(); + mOptions[RSK_MERCHANT_PRICES_GIANT_WALLET_WEIGHT].Hide(); + mOptions[RSK_MERCHANT_PRICES_TYCOON_WALLET_WEIGHT].Hide(); + if (isTycoon ? mOptions[RSK_MERCHANT_PRICES_FIXED_PRICE].GetOptionCount() == 501 + : mOptions[RSK_MERCHANT_PRICES_FIXED_PRICE].GetOptionCount() == 1000) { + mOptions[RSK_MERCHANT_PRICES_FIXED_PRICE].ChangeOptions(isTycoon ? NumOpts(0, 999) + : NumOpts(0, 500)); + } + mOptions[RSK_MERCHANT_PRICES_AFFORDABLE].Hide(); + break; + case RO_PRICE_RANGE: + mOptions[RSK_MERCHANT_PRICES_FIXED_PRICE].Hide(); + mOptions[RSK_MERCHANT_PRICES_RANGE_1].Unhide(); + mOptions[RSK_MERCHANT_PRICES_RANGE_2].Unhide(); + mOptions[RSK_MERCHANT_PRICES_NO_WALLET_WEIGHT].Hide(); + mOptions[RSK_MERCHANT_PRICES_CHILD_WALLET_WEIGHT].Hide(); + mOptions[RSK_MERCHANT_PRICES_ADULT_WALLET_WEIGHT].Hide(); + mOptions[RSK_MERCHANT_PRICES_GIANT_WALLET_WEIGHT].Hide(); + mOptions[RSK_MERCHANT_PRICES_TYCOON_WALLET_WEIGHT].Hide(); + if (isTycoon ? mOptions[RSK_MERCHANT_PRICES_RANGE_1].GetOptionCount() == 101 + : mOptions[RSK_MERCHANT_PRICES_RANGE_1].GetOptionCount() == 200) { + mOptions[RSK_MERCHANT_PRICES_RANGE_1].ChangeOptions(isTycoon ? NumOpts(0, 995, 5) + : NumOpts(0, 500, 5)); + mOptions[RSK_MERCHANT_PRICES_RANGE_2].ChangeOptions(isTycoon ? NumOpts(0, 995, 5) + : NumOpts(0, 500, 5)); + } + mOptions[RSK_MERCHANT_PRICES_AFFORDABLE].Unhide(); + break; + case RO_PRICE_SET_BY_WALLET: + mOptions[RSK_MERCHANT_PRICES_FIXED_PRICE].Hide(); + mOptions[RSK_MERCHANT_PRICES_RANGE_1].Hide(); + mOptions[RSK_MERCHANT_PRICES_RANGE_2].Hide(); + mOptions[RSK_MERCHANT_PRICES_NO_WALLET_WEIGHT].Unhide(); + mOptions[RSK_MERCHANT_PRICES_CHILD_WALLET_WEIGHT].Unhide(); + mOptions[RSK_MERCHANT_PRICES_ADULT_WALLET_WEIGHT].Unhide(); + mOptions[RSK_MERCHANT_PRICES_GIANT_WALLET_WEIGHT].Unhide(); + if (isTycoon) { + mOptions[RSK_MERCHANT_PRICES_TYCOON_WALLET_WEIGHT].Unhide(); + } else { + mOptions[RSK_MERCHANT_PRICES_TYCOON_WALLET_WEIGHT].Hide(); + } + mOptions[RSK_MERCHANT_PRICES_AFFORDABLE].Unhide(); + break; + default: + mOptions[RSK_MERCHANT_PRICES_FIXED_PRICE].Hide(); + mOptions[RSK_MERCHANT_PRICES_RANGE_1].Hide(); + mOptions[RSK_MERCHANT_PRICES_RANGE_2].Hide(); + mOptions[RSK_MERCHANT_PRICES_NO_WALLET_WEIGHT].Hide(); + mOptions[RSK_MERCHANT_PRICES_CHILD_WALLET_WEIGHT].Hide(); + mOptions[RSK_MERCHANT_PRICES_ADULT_WALLET_WEIGHT].Hide(); + mOptions[RSK_MERCHANT_PRICES_GIANT_WALLET_WEIGHT].Hide(); + mOptions[RSK_MERCHANT_PRICES_TYCOON_WALLET_WEIGHT].Hide(); + mOptions[RSK_MERCHANT_PRICES_AFFORDABLE].Unhide(); + break; + } + break; + } + }); + OPT_U8(RSK_MERCHANT_PRICES, "Merchant Prices", {"Vanilla", "Cheap Balanced", "Balanced", "Fixed", "Range", "Set By Wallet"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MerchantPrices"), mOptionDescriptions[RSK_MERCHANT_PRICES], WIDGET_CVAR_COMBOBOX, RO_PRICE_VANILLA, false, nullptr, IMFLAG_NONE); + OPT_CALLBACK(RSK_MERCHANT_PRICES, { + bool isTycoon = CVarGetInteger(CVAR_RANDOMIZER_SETTING("IncludeTycoonWallet"), RO_GENERIC_OFF); + switch (CVarGetInteger(CVAR_RANDOMIZER_SETTING("MerchantPrices"), RO_PRICE_VANILLA)) { + case RO_PRICE_FIXED: + mOptions[RSK_MERCHANT_PRICES_FIXED_PRICE].Unhide(); + mOptions[RSK_MERCHANT_PRICES_RANGE_1].Hide(); + mOptions[RSK_MERCHANT_PRICES_RANGE_2].Hide(); + mOptions[RSK_MERCHANT_PRICES_NO_WALLET_WEIGHT].Hide(); + mOptions[RSK_MERCHANT_PRICES_CHILD_WALLET_WEIGHT].Hide(); + mOptions[RSK_MERCHANT_PRICES_ADULT_WALLET_WEIGHT].Hide(); + mOptions[RSK_MERCHANT_PRICES_GIANT_WALLET_WEIGHT].Hide(); + mOptions[RSK_MERCHANT_PRICES_TYCOON_WALLET_WEIGHT].Hide(); + if (isTycoon ? mOptions[RSK_MERCHANT_PRICES_FIXED_PRICE].GetOptionCount() == 501 + : mOptions[RSK_MERCHANT_PRICES_FIXED_PRICE].GetOptionCount() == 1000) { + mOptions[RSK_MERCHANT_PRICES_FIXED_PRICE].ChangeOptions(isTycoon ? NumOpts(0, 999) + : NumOpts(0, 500)); + } + mOptions[RSK_MERCHANT_PRICES_AFFORDABLE].Hide(); + break; + case RO_PRICE_RANGE: + mOptions[RSK_MERCHANT_PRICES_FIXED_PRICE].Hide(); + mOptions[RSK_MERCHANT_PRICES_RANGE_1].Unhide(); + mOptions[RSK_MERCHANT_PRICES_RANGE_2].Unhide(); + mOptions[RSK_MERCHANT_PRICES_NO_WALLET_WEIGHT].Hide(); + mOptions[RSK_MERCHANT_PRICES_CHILD_WALLET_WEIGHT].Hide(); + mOptions[RSK_MERCHANT_PRICES_ADULT_WALLET_WEIGHT].Hide(); + mOptions[RSK_MERCHANT_PRICES_GIANT_WALLET_WEIGHT].Hide(); + mOptions[RSK_MERCHANT_PRICES_TYCOON_WALLET_WEIGHT].Hide(); + if (isTycoon ? mOptions[RSK_MERCHANT_PRICES_RANGE_1].GetOptionCount() == 101 + : mOptions[RSK_MERCHANT_PRICES_RANGE_1].GetOptionCount() == 200) { + mOptions[RSK_MERCHANT_PRICES_RANGE_1].ChangeOptions(isTycoon ? NumOpts(0, 995, 5) + : NumOpts(0, 500, 5)); + mOptions[RSK_MERCHANT_PRICES_RANGE_2].ChangeOptions(isTycoon ? NumOpts(0, 995, 5) + : NumOpts(0, 500, 5)); + } + mOptions[RSK_MERCHANT_PRICES_AFFORDABLE].Unhide(); + break; + case RO_PRICE_SET_BY_WALLET: + mOptions[RSK_MERCHANT_PRICES_FIXED_PRICE].Hide(); + mOptions[RSK_MERCHANT_PRICES_RANGE_1].Hide(); + mOptions[RSK_MERCHANT_PRICES_RANGE_2].Hide(); + mOptions[RSK_MERCHANT_PRICES_NO_WALLET_WEIGHT].Unhide(); + mOptions[RSK_MERCHANT_PRICES_CHILD_WALLET_WEIGHT].Unhide(); + mOptions[RSK_MERCHANT_PRICES_ADULT_WALLET_WEIGHT].Unhide(); + mOptions[RSK_MERCHANT_PRICES_GIANT_WALLET_WEIGHT].Unhide(); + if (isTycoon) { + mOptions[RSK_MERCHANT_PRICES_TYCOON_WALLET_WEIGHT].Unhide(); + } else { + mOptions[RSK_MERCHANT_PRICES_TYCOON_WALLET_WEIGHT].Hide(); + } + mOptions[RSK_MERCHANT_PRICES_AFFORDABLE].Unhide(); + break; + default: + mOptions[RSK_MERCHANT_PRICES_FIXED_PRICE].Hide(); + mOptions[RSK_MERCHANT_PRICES_RANGE_1].Hide(); + mOptions[RSK_MERCHANT_PRICES_RANGE_2].Hide(); + mOptions[RSK_MERCHANT_PRICES_NO_WALLET_WEIGHT].Hide(); + mOptions[RSK_MERCHANT_PRICES_CHILD_WALLET_WEIGHT].Hide(); + mOptions[RSK_MERCHANT_PRICES_ADULT_WALLET_WEIGHT].Hide(); + mOptions[RSK_MERCHANT_PRICES_GIANT_WALLET_WEIGHT].Hide(); + mOptions[RSK_MERCHANT_PRICES_TYCOON_WALLET_WEIGHT].Hide(); + mOptions[RSK_MERCHANT_PRICES_AFFORDABLE].Unhide(); + break; + } + }); + OPT_U8(RSK_MERCHANT_PRICES_FIXED_PRICE, "Merchant Fixed Price", {NumOpts(0, 995, 5)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MerchantFixedPrice"), mOptionDescriptions[RSK_MERCHANT_PRICES_FIXED_PRICE], WIDGET_CVAR_SLIDER_INT, 10, true); + OPT_U8(RSK_MERCHANT_PRICES_RANGE_1, "Merchant Lower Bound", {NumOpts(0, 995, 5)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MerchantPriceRange1"), mOptionDescriptions[RSK_MERCHANT_PRICES_RANGE_1], WIDGET_CVAR_SLIDER_INT, 10, true, nullptr, IMFLAG_NONE); + OPT_U8(RSK_MERCHANT_PRICES_RANGE_2, "Merchant Upper Bound", {NumOpts(0, 995, 5)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MerchantPriceRange2"), mOptionDescriptions[RSK_MERCHANT_PRICES_RANGE_2], WIDGET_CVAR_SLIDER_INT, 100, true, nullptr, IMFLAG_NONE); + OPT_U8(RSK_MERCHANT_PRICES_NO_WALLET_WEIGHT, "Merchant No Wallet Weight", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MerchantNoWalletWeight"), mOptionDescriptions[RSK_MERCHANT_PRICES_NO_WALLET_WEIGHT], WIDGET_CVAR_SLIDER_INT, 10, true, nullptr, IMFLAG_NONE); + OPT_U8(RSK_MERCHANT_PRICES_CHILD_WALLET_WEIGHT, "Merchant Child Wallet Weight", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MerchantChildWalletWeight"), mOptionDescriptions[RSK_MERCHANT_PRICES_CHILD_WALLET_WEIGHT], WIDGET_CVAR_SLIDER_INT, 10, true, nullptr, IMFLAG_NONE); + OPT_U8(RSK_MERCHANT_PRICES_ADULT_WALLET_WEIGHT, "Merchant Adult Wallet Weight", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MerchantAdultWalletWeight"), mOptionDescriptions[RSK_MERCHANT_PRICES_ADULT_WALLET_WEIGHT], WIDGET_CVAR_SLIDER_INT, 10, true, nullptr, IMFLAG_NONE); + OPT_U8(RSK_MERCHANT_PRICES_GIANT_WALLET_WEIGHT, "Merchant Giant Wallet Weight", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MerchantGiantWalletWeight"), mOptionDescriptions[RSK_MERCHANT_PRICES_GIANT_WALLET_WEIGHT], WIDGET_CVAR_SLIDER_INT, 10, true, nullptr, IMFLAG_NONE); + OPT_U8(RSK_MERCHANT_PRICES_TYCOON_WALLET_WEIGHT, "Merchant Tycoon Wallet Weight", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MerchantTycoonWalletWeight"), mOptionDescriptions[RSK_MERCHANT_PRICES_TYCOON_WALLET_WEIGHT], WIDGET_CVAR_SLIDER_INT, 10, true, nullptr, IMFLAG_NONE); OPT_BOOL(RSK_MERCHANT_PRICES_AFFORDABLE, "Merchant Affordable Prices", CVAR_RANDOMIZER_SETTING("MerchantPricesAffordable"), mOptionDescriptions[RSK_MERCHANT_PRICES_AFFORDABLE]); OPT_BOOL(RSK_SHUFFLE_FROG_SONG_RUPEES, "Shuffle Frog Song Rupees", CVAR_RANDOMIZER_SETTING("ShuffleFrogSongRupees"), mOptionDescriptions[RSK_SHUFFLE_FROG_SONG_RUPEES]); OPT_BOOL(RSK_SHUFFLE_ADULT_TRADE, "Shuffle Adult Trade", CVAR_RANDOMIZER_SETTING("ShuffleAdultTrade"), mOptionDescriptions[RSK_SHUFFLE_ADULT_TRADE]); OPT_U8(RSK_SHUFFLE_CHEST_MINIGAME, "Shuffle Chest Minigame", {"Off", "On (Separate)", "On (Pack)"}); - OPT_BOOL(RSK_SHUFFLE_100_GS_REWARD, "Shuffle 100 GS Reward", CVAR_RANDOMIZER_SETTING("Shuffle100GSReward"), mOptionDescriptions[RSK_SHUFFLE_100_GS_REWARD], IMFLAG_SEPARATOR_BOTTOM, WidgetType::Checkbox, RO_GENERIC_OFF); - OPT_BOOL(RSK_SHUFFLE_BEAN_SOULS, "Shuffle Bean Souls", CVAR_RANDOMIZER_SETTING("ShuffleBeanSouls"), mOptionDescriptions[RSK_SHUFFLE_BEAN_SOULS], IMFLAG_SEPARATOR_BOTTOM, WidgetType::Checkbox, RO_GENERIC_OFF); - OPT_U8(RSK_SHUFFLE_BOSS_SOULS, "Shuffle Boss Souls", {"Off", "On", "On + Ganon"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleBossSouls"), mOptionDescriptions[RSK_SHUFFLE_BOSS_SOULS], WidgetType::Combobox); - OPT_BOOL(RSK_SHUFFLE_DEKU_STICK_BAG, "Shuffle Deku Stick Bag", CVAR_RANDOMIZER_SETTING("ShuffleDekuStickBag"), mOptionDescriptions[RSK_SHUFFLE_DEKU_STICK_BAG], IMFLAG_SEPARATOR_BOTTOM, WidgetType::Checkbox, RO_GENERIC_OFF); - OPT_BOOL(RSK_SHUFFLE_DEKU_NUT_BAG, "Shuffle Deku Nut Bag", CVAR_RANDOMIZER_SETTING("ShuffleDekuNutBag"), mOptionDescriptions[RSK_SHUFFLE_DEKU_NUT_BAG], IMFLAG_SEPARATOR_BOTTOM, WidgetType::Checkbox, RO_GENERIC_OFF); - OPT_U8(RSK_SHUFFLE_FREESTANDING, "Shuffle Freestanding Items", {"Off", "Dungeons", "Overworld", "All Items"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleFreestanding"), mOptionDescriptions[RSK_SHUFFLE_FREESTANDING], WidgetType::Combobox, RO_SHUFFLE_FREESTANDING_OFF); - OPT_U8(RSK_FISHSANITY, "Fishsanity", {"Off", "Shuffle only Hyrule Loach", "Shuffle Fishing Pond", "Shuffle Overworld Fish", "Shuffle Both"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("Fishsanity"), mOptionDescriptions[RSK_FISHSANITY], WidgetType::Combobox, RO_FISHSANITY_OFF); - OPT_U8(RSK_FISHSANITY_POND_COUNT, "Pond Fish Count", {NumOpts(0,17,1)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("FishsanityPondCount"), mOptionDescriptions[RSK_FISHSANITY_POND_COUNT], WidgetType::Slider, 0, true, IMFLAG_NONE); + OPT_BOOL(RSK_SHUFFLE_100_GS_REWARD, "Shuffle 100 GS Reward", CVAR_RANDOMIZER_SETTING("Shuffle100GSReward"), mOptionDescriptions[RSK_SHUFFLE_100_GS_REWARD], IMFLAG_SEPARATOR_BOTTOM, WIDGET_CVAR_CHECKBOX, RO_GENERIC_OFF); + OPT_CALLBACK(RSK_SHUFFLE_100_GS_REWARD, { + if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("Shuffle100GSReward"), RO_GENERIC_OFF)) { + mOptions[RSK_KAK_100_SKULLS_HINT].Enable(); + } else { + mOptions[RSK_KAK_100_SKULLS_HINT].Disable("There is no point to hinting 100 skulls if it is not shuffled."); + } + }); + OPT_BOOL(RSK_SHUFFLE_BEAN_SOULS, "Shuffle Bean Souls", CVAR_RANDOMIZER_SETTING("ShuffleBeanSouls"), mOptionDescriptions[RSK_SHUFFLE_BEAN_SOULS], IMFLAG_SEPARATOR_BOTTOM, WIDGET_CVAR_CHECKBOX, RO_GENERIC_OFF); + OPT_U8(RSK_SHUFFLE_BOSS_SOULS, "Shuffle Boss Souls", {"Off", "On", "On + Ganon"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleBossSouls"), mOptionDescriptions[RSK_SHUFFLE_BOSS_SOULS], WIDGET_CVAR_COMBOBOX); + OPT_BOOL(RSK_SHUFFLE_DEKU_STICK_BAG, "Shuffle Deku Stick Bag", CVAR_RANDOMIZER_SETTING("ShuffleDekuStickBag"), mOptionDescriptions[RSK_SHUFFLE_DEKU_STICK_BAG], IMFLAG_SEPARATOR_BOTTOM, WIDGET_CVAR_CHECKBOX, RO_GENERIC_OFF); + OPT_CALLBACK(RSK_SHUFFLE_DEKU_STICK_BAG, { + if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleDekuStickBag"), 0)) { + mOptions[RSK_STARTING_STICKS].Disable("Disabled because Shuffle Deku Stick Bag is on."); + } else { + mOptions[RSK_STARTING_STICKS].Enable(); + } + }); + OPT_BOOL(RSK_SHUFFLE_DEKU_NUT_BAG, "Shuffle Deku Nut Bag", CVAR_RANDOMIZER_SETTING("ShuffleDekuNutBag"), mOptionDescriptions[RSK_SHUFFLE_DEKU_NUT_BAG], IMFLAG_SEPARATOR_BOTTOM, WIDGET_CVAR_CHECKBOX, RO_GENERIC_OFF); + OPT_CALLBACK(RSK_SHUFFLE_DEKU_NUT_BAG, { + if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleDekuNutBag"), 0)) { + mOptions[RSK_STARTING_NUTS].Disable("Disabled because Shuffle Deku Nut Bag is on."); + } else { + mOptions[RSK_STARTING_NUTS].Enable(); + } + }); + OPT_U8(RSK_SHUFFLE_FREESTANDING, "Shuffle Freestanding Items", {"Off", "Dungeons", "Overworld", "All Items"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleFreestanding"), mOptionDescriptions[RSK_SHUFFLE_FREESTANDING], WIDGET_CVAR_COMBOBOX, RO_SHUFFLE_FREESTANDING_OFF); + OPT_U8(RSK_FISHSANITY, "Fishsanity", {"Off", "Shuffle only Hyrule Loach", "Shuffle Fishing Pond", "Shuffle Overworld Fish", "Shuffle Both"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("Fishsanity"), mOptionDescriptions[RSK_FISHSANITY], WIDGET_CVAR_COMBOBOX, RO_FISHSANITY_OFF); + OPT_CALLBACK(RSK_FISHSANITY, { + // Hide fishing pond settings if we aren't shuffling the fishing pond + switch (CVarGetInteger(CVAR_RANDOMIZER_SETTING("Fishsanity"), RO_FISHSANITY_OFF)) { + case RO_FISHSANITY_POND: + case RO_FISHSANITY_BOTH: + mOptions[RSK_FISHSANITY_POND_COUNT].Unhide(); + mOptions[RSK_FISHSANITY_AGE_SPLIT].Unhide(); + break; + default: + mOptions[RSK_FISHSANITY_POND_COUNT].Hide(); + mOptions[RSK_FISHSANITY_AGE_SPLIT].Hide(); + } + if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("Fishsanity"), RO_FISHSANITY_OFF) == RO_FISHSANITY_HYRULE_LOACH) { + mOptions[RSK_LOACH_HINT].Enable(); + } else { + mOptions[RSK_LOACH_HINT].Disable( + "Loach hint is only avaliable with \"Fishsanity\" set to \"Shuffle only Hyrule Loach\"\nas that's the only " + "setting where you present the loach to the fishing pond owner."); + } + }); + OPT_U8(RSK_FISHSANITY_POND_COUNT, "Pond Fish Count", {NumOpts(0,17,1)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("FishsanityPondCount"), mOptionDescriptions[RSK_FISHSANITY_POND_COUNT], WIDGET_CVAR_SLIDER_INT, 0, true, nullptr, IMFLAG_NONE); OPT_BOOL(RSK_FISHSANITY_AGE_SPLIT, "Pond Age Split", CVAR_RANDOMIZER_SETTING("FishsanityAgeSplit"), mOptionDescriptions[RSK_FISHSANITY_AGE_SPLIT]); OPT_BOOL(RSK_SHUFFLE_FOUNTAIN_FAIRIES, "Shuffle Fairies in Fountains", CVAR_RANDOMIZER_SETTING("ShuffleFountainFairies"), mOptionDescriptions[RSK_SHUFFLE_FOUNTAIN_FAIRIES]); OPT_BOOL(RSK_SHUFFLE_STONE_FAIRIES, "Shuffle Gossip Stone Fairies", CVAR_RANDOMIZER_SETTING("ShuffleStoneFairies"), mOptionDescriptions[RSK_SHUFFLE_STONE_FAIRIES]); OPT_BOOL(RSK_SHUFFLE_BEAN_FAIRIES, "Shuffle Bean Fairies", CVAR_RANDOMIZER_SETTING("ShuffleBeanFairies"), mOptionDescriptions[RSK_SHUFFLE_BEAN_FAIRIES]); OPT_BOOL(RSK_SHUFFLE_SONG_FAIRIES, "Shuffle Fairy Spots", CVAR_RANDOMIZER_SETTING("ShuffleFairySpots"), mOptionDescriptions[RSK_SHUFFLE_SONG_FAIRIES]); - OPT_U8(RSK_SHUFFLE_MAPANDCOMPASS, "Maps/Compasses", {"Start With", "Vanilla", "Own Dungeon", "Any Dungeon", "Overworld", "Anywhere"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("StartingMapsCompasses"), mOptionDescriptions[RSK_SHUFFLE_MAPANDCOMPASS], WidgetType::Combobox, RO_DUNGEON_ITEM_LOC_OWN_DUNGEON); - OPT_U8(RSK_KEYSANITY, "Small Key Shuffle", {"Start With", "Vanilla", "Own Dungeon", "Any Dungeon", "Overworld", "Anywhere"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("Keysanity"), mOptionDescriptions[RSK_KEYSANITY], WidgetType::Combobox, RO_DUNGEON_ITEM_LOC_OWN_DUNGEON); - OPT_U8(RSK_GERUDO_KEYS, "Gerudo Fortress Keys", {"Vanilla", "Any Dungeon", "Overworld", "Anywhere"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("GerudoKeys"), mOptionDescriptions[RSK_GERUDO_KEYS], WidgetType::Combobox, RO_GERUDO_KEYS_VANILLA); - OPT_U8(RSK_BOSS_KEYSANITY, "Boss Key Shuffle", {"Start With", "Vanilla", "Own Dungeon", "Any Dungeon", "Overworld", "Anywhere"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("BossKeysanity"), mOptionDescriptions[RSK_BOSS_KEYSANITY], WidgetType::Combobox, RO_DUNGEON_ITEM_LOC_OWN_DUNGEON); - OPT_U8(RSK_GANONS_BOSS_KEY, "Ganon's Boss Key", {"Vanilla", "Own Dungeon", "Start With", "Any Dungeon", "Overworld", "Anywhere", "LACS-Vanilla", "LACS-Stones", "LACS-Medallions", "LACS-Rewards", "LACS-Dungeons", "LACS-Tokens", "100 GS Reward"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleGanonBossKey"), mOptionDescriptions[RSK_GANONS_BOSS_KEY], WidgetType::Combobox, RO_GANON_BOSS_KEY_VANILLA); - OPT_U8(RSK_LACS_STONE_COUNT, "GCBK Stone Count", {NumOpts(0, 4)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LacsStoneCount"), "", WidgetType::Slider, 3, true); - OPT_U8(RSK_LACS_MEDALLION_COUNT, "GCBK Medallion Count", {NumOpts(0, 7)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LacsMedallionCount"), "", WidgetType::Slider, 6, true); - OPT_U8(RSK_LACS_REWARD_COUNT, "GCBK Reward Count", {NumOpts(0, 10)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LacsRewardCount"), "", WidgetType::Slider, 9, true); - OPT_U8(RSK_LACS_DUNGEON_COUNT, "GCBK Dungeon Count", {NumOpts(0, 9)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LacsDungeonCount"), "", WidgetType::Slider, 8, true); - OPT_U8(RSK_LACS_TOKEN_COUNT, "GCBK Token Count", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LacsTokenCount"), "", WidgetType::Slider, 100, true); - OPT_U8(RSK_LACS_OPTIONS, "GCBK LACS Reward Options", {"Standard Reward", "Greg as Reward", "Greg as Wildcard"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LacsRewardOptions"), mOptionDescriptions[RSK_LACS_OPTIONS], WidgetType::Combobox, RO_LACS_STANDARD_REWARD); - OPT_U8(RSK_KEYRINGS, "Key Rings", {"Off", "Random", "Count", "Selection"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRings"), mOptionDescriptions[RSK_KEYRINGS], WidgetType::Combobox, RO_KEYRINGS_OFF); - OPT_U8(RSK_KEYRINGS_RANDOM_COUNT, "Keyring Dungeon Count", {NumOpts(0, 9)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsRandomCount"), "", WidgetType::Slider, 8); - OPT_U8(RSK_KEYRINGS_GERUDO_FORTRESS, "Gerudo Fortress Keyring", {"No", "Random", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsGerudoFortress"), "", WidgetType::Combobox, 0); - OPT_U8(RSK_KEYRINGS_FOREST_TEMPLE, "Forest Temple Keyring", {"No", "Random", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsForestTemple"), "", WidgetType::Combobox, 0); - OPT_U8(RSK_KEYRINGS_FIRE_TEMPLE, "Fire Temple Keyring", {"No", "Random", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsFireTemple"), "", WidgetType::Combobox, 0); - OPT_U8(RSK_KEYRINGS_WATER_TEMPLE, "Water Temple Keyring", {"No", "Random", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsWaterTemple"), "", WidgetType::Combobox, 0); - OPT_U8(RSK_KEYRINGS_SPIRIT_TEMPLE, "Spirit Temple Keyring", {"No", "Random", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsSpiritTemple"), "", WidgetType::Combobox, 0); - OPT_U8(RSK_KEYRINGS_SHADOW_TEMPLE, "Shadow Temple Keyring", {"No", "Random", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsShadowTemple"), "", WidgetType::Combobox, 0); - OPT_U8(RSK_KEYRINGS_BOTTOM_OF_THE_WELL, "Bottom of the Well Keyring", {"No", "Random", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsBottomOfTheWell"), "", WidgetType::Combobox, 0); - OPT_U8(RSK_KEYRINGS_GTG, "Gerudo Training Ground Keyring", {"No", "Random", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsGTG"), "", WidgetType::Combobox, 0); - OPT_U8(RSK_KEYRINGS_GANONS_CASTLE, "Ganon's Castle Keyring", {"No", "Random", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsGanonsCastle"), "", WidgetType::Combobox, 0); + OPT_U8(RSK_SHUFFLE_MAPANDCOMPASS, "Maps/Compasses", {"Start With", "Vanilla", "Own Dungeon", "Any Dungeon", "Overworld", "Anywhere"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("StartingMapsCompasses"), mOptionDescriptions[RSK_SHUFFLE_MAPANDCOMPASS], WIDGET_CVAR_COMBOBOX, RO_DUNGEON_ITEM_LOC_OWN_DUNGEON); + OPT_U8(RSK_KEYSANITY, "Small Key Shuffle", {"Start With", "Vanilla", "Own Dungeon", "Any Dungeon", "Overworld", "Anywhere"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("Keysanity"), mOptionDescriptions[RSK_KEYSANITY], WIDGET_CVAR_COMBOBOX, RO_DUNGEON_ITEM_LOC_OWN_DUNGEON); + OPT_U8(RSK_GERUDO_KEYS, "Gerudo Fortress Keys", {"Vanilla", "Any Dungeon", "Overworld", "Anywhere"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("GerudoKeys"), mOptionDescriptions[RSK_GERUDO_KEYS], WIDGET_CVAR_COMBOBOX, RO_GERUDO_KEYS_VANILLA); + OPT_CALLBACK(RSK_GERUDO_KEYS, { + const uint8_t maxKeyringCount = + (CVarGetInteger(CVAR_RANDOMIZER_SETTING("FortressCarpenters"), RO_GF_CARPENTERS_NORMAL) == + RO_GF_CARPENTERS_NORMAL && + CVarGetInteger(CVAR_RANDOMIZER_SETTING("GerudoKeys"), RO_GERUDO_KEYS_VANILLA) != RO_GERUDO_KEYS_VANILLA) + ? 9 + : 8; + if (mOptions[RSK_KEYRINGS_RANDOM_COUNT].GetOptionCount() != maxKeyringCount + 1) { + mOptions[RSK_KEYRINGS_RANDOM_COUNT].ChangeOptions(NumOpts(0, maxKeyringCount)); + } + if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("FortressCarpenters"), RO_GF_CARPENTERS_NORMAL) != + RO_GF_CARPENTERS_NORMAL || + CVarGetInteger(CVAR_RANDOMIZER_SETTING("GerudoKeys"), RO_GERUDO_KEYS_VANILLA) == RO_GERUDO_KEYS_VANILLA) { + mOptions[RSK_KEYRINGS_GERUDO_FORTRESS].Disable( + "Disabled because the currently selected Gerudo Fortress Carpenters\n" + "setting and/or Gerudo Fortress Keys setting is incompatible with\n" + "having a Gerudo Fortress Keyring."); + } else { + mOptions[RSK_KEYRINGS_GERUDO_FORTRESS].Enable(); + } + }); + OPT_U8(RSK_BOSS_KEYSANITY, "Boss Key Shuffle", {"Start With", "Vanilla", "Own Dungeon", "Any Dungeon", "Overworld", "Anywhere"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("BossKeysanity"), mOptionDescriptions[RSK_BOSS_KEYSANITY], WIDGET_CVAR_COMBOBOX, RO_DUNGEON_ITEM_LOC_OWN_DUNGEON); + OPT_U8(RSK_GANONS_BOSS_KEY, "Ganon's Boss Key", {"Vanilla", "Own Dungeon", "Start With", "Any Dungeon", "Overworld", "Anywhere", "LACS-Vanilla", "LACS-Stones", "LACS-Medallions", "LACS-Rewards", "LACS-Dungeons", "LACS-Tokens", "100 GS Reward"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleGanonBossKey"), mOptionDescriptions[RSK_GANONS_BOSS_KEY], WIDGET_CVAR_COMBOBOX, RO_GANON_BOSS_KEY_VANILLA); + OPT_CALLBACK(RSK_GANONS_BOSS_KEY, { + // Shuffle 100 GS Reward - Force-Enabled if Ganon's Boss Key is on the 100 GS Reward + if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleGanonBossKey"), RO_GANON_BOSS_KEY_VANILLA) == + RO_GANON_BOSS_KEY_KAK_TOKENS) { + mOptions[RSK_SHUFFLE_100_GS_REWARD].Disable( + "This option is force-enabled because \"Ganon's Boss Key\" is set to \"100 GS Reward\"."); + } else { + mOptions[RSK_SHUFFLE_100_GS_REWARD].Enable(); + } + mOptions[RSK_LACS_OPTIONS].Hide(); + mOptions[RSK_LACS_STONE_COUNT].Hide(); + mOptions[RSK_LACS_MEDALLION_COUNT].Hide(); + mOptions[RSK_LACS_REWARD_COUNT].Hide(); + mOptions[RSK_LACS_DUNGEON_COUNT].Hide(); + mOptions[RSK_LACS_TOKEN_COUNT].Hide(); + switch (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleGanonBossKey"), RO_GANON_BOSS_KEY_VANILLA)) { + case RO_GANON_BOSS_KEY_LACS_STONES: + mOptions[RSK_LACS_OPTIONS].Unhide(); + mOptions[RSK_LACS_STONE_COUNT].Unhide(); + break; + case RO_GANON_BOSS_KEY_LACS_MEDALLIONS: + mOptions[RSK_LACS_OPTIONS].Unhide(); + mOptions[RSK_LACS_MEDALLION_COUNT].Unhide(); + break; + case RO_GANON_BOSS_KEY_LACS_REWARDS: + mOptions[RSK_LACS_OPTIONS].Unhide(); + mOptions[RSK_LACS_REWARD_COUNT].Unhide(); + break; + case RO_GANON_BOSS_KEY_LACS_DUNGEONS: + mOptions[RSK_LACS_OPTIONS].Unhide(); + mOptions[RSK_LACS_DUNGEON_COUNT].Unhide(); + break; + case RO_GANON_BOSS_KEY_LACS_TOKENS: + mOptions[RSK_LACS_TOKEN_COUNT].Unhide(); + break; + } + }); + OPT_U8(RSK_LACS_STONE_COUNT, "GCBK Stone Count", {NumOpts(0, 4)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LacsStoneCount"), "", WIDGET_CVAR_SLIDER_INT, 3, true); + OPT_U8(RSK_LACS_MEDALLION_COUNT, "GCBK Medallion Count", {NumOpts(0, 7)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LacsMedallionCount"), "", WIDGET_CVAR_SLIDER_INT, 6, true); + OPT_U8(RSK_LACS_REWARD_COUNT, "GCBK Reward Count", {NumOpts(0, 10)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LacsRewardCount"), "", WIDGET_CVAR_SLIDER_INT, 9, true); + OPT_U8(RSK_LACS_DUNGEON_COUNT, "GCBK Dungeon Count", {NumOpts(0, 9)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LacsDungeonCount"), "", WIDGET_CVAR_SLIDER_INT, 8, true); + OPT_U8(RSK_LACS_TOKEN_COUNT, "GCBK Token Count", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LacsTokenCount"), "", WIDGET_CVAR_SLIDER_INT, 100, true); + OPT_U8(RSK_LACS_OPTIONS, "GCBK LACS Reward Options", {"Standard Reward", "Greg as Reward", "Greg as Wildcard"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LacsRewardOptions"), mOptionDescriptions[RSK_LACS_OPTIONS], WIDGET_CVAR_COMBOBOX, RO_LACS_STANDARD_REWARD); + OPT_CALLBACK(RSK_LACS_OPTIONS, { + const uint8_t lacsOpts = CVarGetInteger(CVAR_RANDOMIZER_SETTING("LacsRewardOptions"), RO_LACS_STANDARD_REWARD); + if (lacsOpts == RO_LACS_GREG_REWARD) { + if (mOptions[RSK_LACS_STONE_COUNT].GetOptionCount() == 4) { + mOptions[RSK_LACS_STONE_COUNT].ChangeOptions(NumOpts(0, 4)); + } + if (mOptions[RSK_LACS_MEDALLION_COUNT].GetOptionCount() == 7) { + mOptions[RSK_LACS_MEDALLION_COUNT].ChangeOptions(NumOpts(0, 7)); + } + if (mOptions[RSK_LACS_REWARD_COUNT].GetOptionCount() == 10) { + mOptions[RSK_LACS_REWARD_COUNT].ChangeOptions(NumOpts(0, 10)); + } + if (mOptions[RSK_LACS_DUNGEON_COUNT].GetOptionCount() == 9) { + mOptions[RSK_LACS_DUNGEON_COUNT].ChangeOptions(NumOpts(0, 9)); + } + } else { + if (mOptions[RSK_LACS_STONE_COUNT].GetOptionCount() == 5) { + mOptions[RSK_LACS_STONE_COUNT].ChangeOptions(NumOpts(0, 3)); + } + if (mOptions[RSK_LACS_MEDALLION_COUNT].GetOptionCount() == 8) { + mOptions[RSK_LACS_MEDALLION_COUNT].ChangeOptions(NumOpts(0, 6)); + } + if (mOptions[RSK_LACS_REWARD_COUNT].GetOptionCount() == 11) { + mOptions[RSK_LACS_REWARD_COUNT].ChangeOptions(NumOpts(0, 9)); + } + if (mOptions[RSK_LACS_DUNGEON_COUNT].GetOptionCount() == 10) { + mOptions[RSK_LACS_DUNGEON_COUNT].ChangeOptions(NumOpts(0, 8)); + } + } + }); + OPT_U8(RSK_KEYRINGS, "Key Rings", {"Off", "Random", "Count", "Selection"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRings"), mOptionDescriptions[RSK_KEYRINGS], WIDGET_CVAR_COMBOBOX, RO_KEYRINGS_OFF); + OPT_CALLBACK(RSK_KEYRINGS, { + switch (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleKeyRings"), RO_KEYRINGS_OFF)) { + case RO_KEYRINGS_COUNT: + // Show count slider. + mOptions[RSK_KEYRINGS_RANDOM_COUNT].Unhide(); + mOptions[RSK_KEYRINGS_GERUDO_FORTRESS].Hide(); + mOptions[RSK_KEYRINGS_FOREST_TEMPLE].Hide(); + mOptions[RSK_KEYRINGS_FIRE_TEMPLE].Hide(); + mOptions[RSK_KEYRINGS_WATER_TEMPLE].Hide(); + mOptions[RSK_KEYRINGS_SPIRIT_TEMPLE].Hide(); + mOptions[RSK_KEYRINGS_SHADOW_TEMPLE].Hide(); + mOptions[RSK_KEYRINGS_BOTTOM_OF_THE_WELL].Hide(); + mOptions[RSK_KEYRINGS_GTG].Hide(); + mOptions[RSK_KEYRINGS_GANONS_CASTLE].Hide(); + break; + case RO_KEYRINGS_SELECTION: + // Show checkboxes for each dungeon with keys. + mOptions[RSK_KEYRINGS_RANDOM_COUNT].Hide(); + mOptions[RSK_KEYRINGS_GERUDO_FORTRESS].Unhide(); + mOptions[RSK_KEYRINGS_FOREST_TEMPLE].Unhide(); + mOptions[RSK_KEYRINGS_FIRE_TEMPLE].Unhide(); + mOptions[RSK_KEYRINGS_WATER_TEMPLE].Unhide(); + mOptions[RSK_KEYRINGS_SPIRIT_TEMPLE].Unhide(); + mOptions[RSK_KEYRINGS_SHADOW_TEMPLE].Unhide(); + mOptions[RSK_KEYRINGS_BOTTOM_OF_THE_WELL].Unhide(); + mOptions[RSK_KEYRINGS_GTG].Unhide(); + mOptions[RSK_KEYRINGS_GANONS_CASTLE].Unhide(); + break; + default: + mOptions[RSK_KEYRINGS_RANDOM_COUNT].Hide(); + mOptions[RSK_KEYRINGS_GERUDO_FORTRESS].Hide(); + mOptions[RSK_KEYRINGS_FOREST_TEMPLE].Hide(); + mOptions[RSK_KEYRINGS_FIRE_TEMPLE].Hide(); + mOptions[RSK_KEYRINGS_WATER_TEMPLE].Hide(); + mOptions[RSK_KEYRINGS_SPIRIT_TEMPLE].Hide(); + mOptions[RSK_KEYRINGS_SHADOW_TEMPLE].Hide(); + mOptions[RSK_KEYRINGS_BOTTOM_OF_THE_WELL].Hide(); + mOptions[RSK_KEYRINGS_GTG].Hide(); + mOptions[RSK_KEYRINGS_GANONS_CASTLE].Hide(); + break; + } + }); + OPT_U8(RSK_KEYRINGS_RANDOM_COUNT, "Keyring Dungeon Count", {NumOpts(0, 9)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsRandomCount"), "", WIDGET_CVAR_SLIDER_INT, 8); + OPT_U8(RSK_KEYRINGS_GERUDO_FORTRESS, "Gerudo Fortress Keyring", {"No", "Random", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsGerudoFortress"), "", WIDGET_CVAR_COMBOBOX, 0); + OPT_U8(RSK_KEYRINGS_FOREST_TEMPLE, "Forest Temple Keyring", {"No", "Random", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsForestTemple"), "", WIDGET_CVAR_COMBOBOX, 0); + OPT_U8(RSK_KEYRINGS_FIRE_TEMPLE, "Fire Temple Keyring", {"No", "Random", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsFireTemple"), "", WIDGET_CVAR_COMBOBOX, 0); + OPT_U8(RSK_KEYRINGS_WATER_TEMPLE, "Water Temple Keyring", {"No", "Random", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsWaterTemple"), "", WIDGET_CVAR_COMBOBOX, 0); + OPT_U8(RSK_KEYRINGS_SPIRIT_TEMPLE, "Spirit Temple Keyring", {"No", "Random", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsSpiritTemple"), "", WIDGET_CVAR_COMBOBOX, 0); + OPT_U8(RSK_KEYRINGS_SHADOW_TEMPLE, "Shadow Temple Keyring", {"No", "Random", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsShadowTemple"), "", WIDGET_CVAR_COMBOBOX, 0); + OPT_U8(RSK_KEYRINGS_BOTTOM_OF_THE_WELL, "Bottom of the Well Keyring", {"No", "Random", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsBottomOfTheWell"), "", WIDGET_CVAR_COMBOBOX, 0); + OPT_U8(RSK_KEYRINGS_GTG, "Gerudo Training Ground Keyring", {"No", "Random", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsGTG"), "", WIDGET_CVAR_COMBOBOX, 0); + OPT_U8(RSK_KEYRINGS_GANONS_CASTLE, "Ganon's Castle Keyring", {"No", "Random", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsGanonsCastle"), "", WIDGET_CVAR_COMBOBOX, 0); //Dummied out due to redundancy with TimeSavers.SkipChildStealth until such a time that logic needs to consider child stealth e.g. because it's freestanding checks are added to freestanding shuffle. //To undo this dummying, readd this setting to an OptionGroup so it appears in the UI, then edit the timesaver check hooks to look at this, and the timesaver setting to lock itself as needed. - OPT_BOOL(RSK_SKIP_CHILD_STEALTH, "Skip Child Stealth", {"Don't Skip", "Skip"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("SkipChildStealth"), mOptionDescriptions[RSK_SKIP_CHILD_STEALTH], WidgetType::Checkbox, RO_GENERIC_DONT_SKIP); - OPT_BOOL(RSK_SKIP_CHILD_ZELDA, "Skip Child Zelda", {"Don't Skip", "Skip"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("SkipChildZelda"), mOptionDescriptions[RSK_SKIP_CHILD_ZELDA], WidgetType::Checkbox, RO_GENERIC_DONT_SKIP); - OPT_BOOL(RSK_SKIP_EPONA_RACE, "Skip Epona Race", {"Don't Skip", "Skip"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("SkipEponaRace"), mOptionDescriptions[RSK_SKIP_EPONA_RACE], WidgetType::Checkbox, RO_GENERIC_DONT_SKIP); + OPT_BOOL(RSK_SKIP_CHILD_STEALTH, "Skip Child Stealth", {"Don't Skip", "Skip"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("SkipChildStealth"), mOptionDescriptions[RSK_SKIP_CHILD_STEALTH], WIDGET_CVAR_CHECKBOX, RO_GENERIC_DONT_SKIP); + OPT_BOOL(RSK_SKIP_CHILD_ZELDA, "Skip Child Zelda", {"Don't Skip", "Skip"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("SkipChildZelda"), mOptionDescriptions[RSK_SKIP_CHILD_ZELDA], WIDGET_CVAR_CHECKBOX, RO_GENERIC_DONT_SKIP); + OPT_CALLBACK(RSK_SKIP_CHILD_ZELDA, { + // Shuffle Weird Egg - Disabled when Skip Child Zelda is active + if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("SkipChildZelda"), RO_GENERIC_DONT_SKIP)) { + mOptions[RSK_SHUFFLE_WEIRD_EGG].Disable("This option is disabled because \"Skip Child Zelda\" is enabled."); + mOptions[RSK_SKIP_CHILD_STEALTH].Disable("This option is disabled because \"Skip Child Zelda\" is enabled."); + } else { + mOptions[RSK_SHUFFLE_WEIRD_EGG].Enable(); + mOptions[RSK_SKIP_CHILD_STEALTH].Enable(); + } + }); + OPT_BOOL(RSK_SKIP_EPONA_RACE, "Skip Epona Race", {"Don't Skip", "Skip"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("SkipEponaRace"), mOptionDescriptions[RSK_SKIP_EPONA_RACE], WIDGET_CVAR_CHECKBOX, RO_GENERIC_DONT_SKIP); OPT_BOOL(RSK_SKIP_SCARECROWS_SONG, "Skip Scarecrow's Song", CVAR_RANDOMIZER_SETTING("SkipScarecrowsSong"), mOptionDescriptions[RSK_SKIP_SCARECROWS_SONG]); OPT_BOOL(RSK_SKIP_PLANTING_BEANS, "Skip Planting Beans", CVAR_RANDOMIZER_SETTING("SkipPlantingBeans"), mOptionDescriptions[RSK_SKIP_PLANTING_BEANS]); - OPT_U8(RSK_BIG_POE_COUNT, "Big Poe Target Count", {NumOpts(0, 10)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("BigPoeTargetCount"), mOptionDescriptions[RSK_BIG_POE_COUNT], WidgetType::Slider, 10); + OPT_U8(RSK_BIG_POE_COUNT, "Big Poe Target Count", {NumOpts(0, 10)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("BigPoeTargetCount"), mOptionDescriptions[RSK_BIG_POE_COUNT], WIDGET_CVAR_SLIDER_INT, 10); + OPT_CALLBACK(RSK_BIG_POE_COUNT, { + if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("BigPoeTargetCount"), 10) == 0) { + mOptions[RSK_BIG_POES_HINT].Disable("Poe Collector will just give you the item instead with 0 big poes."); + } else { + mOptions[RSK_BIG_POES_HINT].Enable(); + } + }); OPT_BOOL(RSK_COMPLETE_MASK_QUEST, "Complete Mask Quest", CVAR_RANDOMIZER_SETTING("CompleteMaskQuest"), mOptionDescriptions[RSK_COMPLETE_MASK_QUEST]); - OPT_U8(RSK_GOSSIP_STONE_HINTS, "Gossip Stone Hints", {"No Hints", "Need Nothing", "Mask of Truth", "Stone of Agony"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("GossipStoneHints"), mOptionDescriptions[RSK_GOSSIP_STONE_HINTS], WidgetType::Combobox, RO_GOSSIP_STONES_NEED_NOTHING, false, IMFLAG_NONE); - OPT_U8(RSK_HINT_CLARITY, "Hint Clarity", {"Obscure", "Ambiguous", "Clear"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("HintClarity"), mOptionDescriptions[RSK_HINT_CLARITY], WidgetType::Combobox, RO_HINT_CLARITY_CLEAR, true, IMFLAG_INDENT); - OPT_U8(RSK_HINT_DISTRIBUTION, "Hint Distribution", {"Useless", "Balanced", "Strong", "Very Strong"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("HintDistribution"), mOptionDescriptions[RSK_HINT_DISTRIBUTION], WidgetType::Combobox, RO_HINT_DIST_BALANCED, true, IMFLAG_UNINDENT); - OPT_BOOL(RSK_TOT_ALTAR_HINT, "ToT Altar Hint", {"Off", "On"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("AltarHint"), mOptionDescriptions[RSK_TOT_ALTAR_HINT], WidgetType::Checkbox, RO_GENERIC_ON, false, IMFLAG_INDENT); - OPT_BOOL(RSK_GANONDORF_HINT, "Ganondorf Hint", {"Off", "On"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("GanondorfHint"), mOptionDescriptions[RSK_GANONDORF_HINT], WidgetType::Checkbox, RO_GENERIC_ON, false, IMFLAG_NONE); - OPT_BOOL(RSK_SHEIK_LA_HINT, "Sheik Light Arrow Hint", {"Off", "On"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("SheikLAHint"), mOptionDescriptions[RSK_SHEIK_LA_HINT], WidgetType::Checkbox, RO_GENERIC_ON, false, IMFLAG_NONE); + OPT_U8(RSK_GOSSIP_STONE_HINTS, "Gossip Stone Hints", {"No Hints", "Need Nothing", "Mask of Truth", "Stone of Agony"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("GossipStoneHints"), mOptionDescriptions[RSK_GOSSIP_STONE_HINTS], WIDGET_CVAR_COMBOBOX, RO_GOSSIP_STONES_NEED_NOTHING, false, nullptr, IMFLAG_NONE); + OPT_CALLBACK(RSK_GOSSIP_STONE_HINTS, { + if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("GossipStoneHints"), RO_GOSSIP_STONES_NEED_NOTHING) == + RO_GOSSIP_STONES_NONE) { + mOptions[RSK_HINT_CLARITY].Hide(); + mOptions[RSK_HINT_DISTRIBUTION].Hide(); + } else { + mOptions[RSK_HINT_CLARITY].Unhide(); + mOptions[RSK_HINT_DISTRIBUTION].Unhide(); + } + }); + OPT_U8(RSK_HINT_CLARITY, "Hint Clarity", {"Obscure", "Ambiguous", "Clear"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("HintClarity"), mOptionDescriptions[RSK_HINT_CLARITY], WIDGET_CVAR_COMBOBOX, RO_HINT_CLARITY_CLEAR, true, nullptr, IMFLAG_INDENT); + OPT_U8(RSK_HINT_DISTRIBUTION, "Hint Distribution", {"Useless", "Balanced", "Strong", "Very Strong"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("HintDistribution"), mOptionDescriptions[RSK_HINT_DISTRIBUTION], WIDGET_CVAR_COMBOBOX, RO_HINT_DIST_BALANCED, true, nullptr, IMFLAG_UNINDENT); + OPT_BOOL(RSK_TOT_ALTAR_HINT, "ToT Altar Hint", {"Off", "On"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("AltarHint"), mOptionDescriptions[RSK_TOT_ALTAR_HINT], WIDGET_CVAR_CHECKBOX, RO_GENERIC_ON, false, nullptr, IMFLAG_INDENT); + OPT_BOOL(RSK_GANONDORF_HINT, "Ganondorf Hint", {"Off", "On"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("GanondorfHint"), mOptionDescriptions[RSK_GANONDORF_HINT], WIDGET_CVAR_CHECKBOX, RO_GENERIC_ON, false, nullptr, IMFLAG_NONE); + OPT_BOOL(RSK_SHEIK_LA_HINT, "Sheik Light Arrow Hint", {"Off", "On"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("SheikLAHint"), mOptionDescriptions[RSK_SHEIK_LA_HINT], WIDGET_CVAR_CHECKBOX, RO_GENERIC_ON, false, nullptr, IMFLAG_NONE); OPT_BOOL(RSK_DAMPES_DIARY_HINT, "Dampe's Diary Hint", CVAR_RANDOMIZER_SETTING("DampeHint"), mOptionDescriptions[RSK_DAMPES_DIARY_HINT], IMFLAG_NONE); OPT_BOOL(RSK_GREG_HINT, "Greg the Green Rupee Hint", CVAR_RANDOMIZER_SETTING("GregHint"), mOptionDescriptions[RSK_GREG_HINT], IMFLAG_NONE); OPT_BOOL(RSK_LOACH_HINT, "Hyrule Loach Hint", CVAR_RANDOMIZER_SETTING("LoachHint"), mOptionDescriptions[RSK_LOACH_HINT], IMFLAG_NONE); @@ -299,7 +1214,7 @@ void Settings::CreateOptions() { OPT_BOOL(RSK_CHICKENS_HINT, "Chickens Hint", CVAR_RANDOMIZER_SETTING("ChickensHint"), mOptionDescriptions[RSK_CHICKENS_HINT], IMFLAG_NONE); OPT_BOOL(RSK_MALON_HINT, "Malon Hint", CVAR_RANDOMIZER_SETTING("MalonHint"), mOptionDescriptions[RSK_MALON_HINT], IMFLAG_NONE); OPT_BOOL(RSK_HBA_HINT, "Horseback Archery Hint", CVAR_RANDOMIZER_SETTING("HBAHint"), mOptionDescriptions[RSK_HBA_HINT], IMFLAG_NONE); - OPT_BOOL(RSK_WARP_SONG_HINTS, "Warp Song Hints", CVAR_RANDOMIZER_SETTING("WarpSongText"), mOptionDescriptions[RSK_WARP_SONG_HINTS], IMFLAG_NONE, WidgetType::Checkbox, RO_GENERIC_ON); + OPT_BOOL(RSK_WARP_SONG_HINTS, "Warp Song Hints", CVAR_RANDOMIZER_SETTING("WarpSongText"), mOptionDescriptions[RSK_WARP_SONG_HINTS], IMFLAG_NONE, WIDGET_CVAR_CHECKBOX, RO_GENERIC_ON); OPT_BOOL(RSK_SCRUB_TEXT_HINT, "Scrub Hint Text", CVAR_RANDOMIZER_SETTING("ScrubText"), mOptionDescriptions[RSK_SCRUB_TEXT_HINT], IMFLAG_NONE); OPT_BOOL(RSK_MERCHANT_TEXT_HINT, "Merchant Hint Text", CVAR_RANDOMIZER_SETTING("MerchantText"), mOptionDescriptions[RSK_MERCHANT_TEXT_HINT], IMFLAG_NONE); OPT_BOOL(RSK_KAK_10_SKULLS_HINT, "10 GS Hint", CVAR_RANDOMIZER_SETTING("10GSHint"), mOptionDescriptions[RSK_KAK_10_SKULLS_HINT], IMFLAG_NONE); @@ -315,17 +1230,17 @@ void Settings::CreateOptions() { OPT_U8(RSK_INFINITE_UPGRADES, "Infinite Upgrades", {"Off", "Progressive", "Condensed Progressive"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("InfiniteUpgrades"), mOptionDescriptions[RSK_INFINITE_UPGRADES]); OPT_BOOL(RSK_SKELETON_KEY, "Skeleton Key", CVAR_RANDOMIZER_SETTING("SkeletonKey"), mOptionDescriptions[RSK_SKELETON_KEY]); OPT_BOOL(RSK_SLINGBOW_BREAK_BEEHIVES, "Slingshot/Bow Can Break Beehives", CVAR_RANDOMIZER_SETTING("SlingBowBeehives"), mOptionDescriptions[RSK_SLINGBOW_BREAK_BEEHIVES]); - OPT_U8(RSK_ITEM_POOL, "Item Pool", {"Plentiful", "Balanced", "Scarce", "Minimal"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ItemPool"), mOptionDescriptions[RSK_ITEM_POOL], WidgetType::Combobox, RO_ITEM_POOL_BALANCED); - OPT_U8(RSK_ICE_TRAPS, "Ice Traps", {"Off", "Normal", "Extra", "Mayhem", "Onslaught"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("IceTraps"), mOptionDescriptions[RSK_ICE_TRAPS], WidgetType::Combobox, RO_ICE_TRAPS_NORMAL); + OPT_U8(RSK_ITEM_POOL, "Item Pool", {"Plentiful", "Balanced", "Scarce", "Minimal"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ItemPool"), mOptionDescriptions[RSK_ITEM_POOL], WIDGET_CVAR_COMBOBOX, RO_ITEM_POOL_BALANCED); + OPT_U8(RSK_ICE_TRAPS, "Ice Traps", {"Off", "Normal", "Extra", "Mayhem", "Onslaught"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("IceTraps"), mOptionDescriptions[RSK_ICE_TRAPS], WIDGET_CVAR_COMBOBOX, RO_ICE_TRAPS_NORMAL); // TODO: Remove Double Defense, Progressive Goron Sword - OPT_U8(RSK_STARTING_OCARINA, "Start with Ocarina", {"Off", "Fairy Ocarina", "Ocarina of Time"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("StartingOcarina"), "", WidgetType::Combobox, RO_STARTING_OCARINA_OFF); + OPT_U8(RSK_STARTING_OCARINA, "Start with Ocarina", {"Off", "Fairy Ocarina", "Ocarina of Time"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("StartingOcarina"), "", WIDGET_CVAR_COMBOBOX, RO_STARTING_OCARINA_OFF); OPT_BOOL(RSK_STARTING_DEKU_SHIELD, "Start with Deku Shield", CVAR_RANDOMIZER_SETTING("StartingDekuShield")); OPT_BOOL(RSK_STARTING_KOKIRI_SWORD, "Start with Kokiri Sword", CVAR_RANDOMIZER_SETTING("StartingKokiriSword")); OPT_BOOL(RSK_STARTING_MASTER_SWORD, "Start with Master Sword", CVAR_RANDOMIZER_SETTING("StartingMasterSword")); - OPT_BOOL(RSK_STARTING_STICKS, "Start with Stick Ammo", {"No", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("StartingSticks"), "", WidgetType::Checkbox, RO_GENERIC_OFF); - OPT_BOOL(RSK_STARTING_NUTS, "Start with Nut Ammo", {"No", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("StartingNuts"), "", WidgetType::Checkbox, RO_GENERIC_OFF); - OPT_BOOL(RSK_STARTING_BEANS, "Start with Magic Beans", {"No", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("StartingBeans"), "", WidgetType::Checkbox, RO_GENERIC_OFF); - OPT_BOOL(RSK_FULL_WALLETS, "Full Wallets", {"No", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("FullWallets"), mOptionDescriptions[RSK_FULL_WALLETS], WidgetType::Checkbox, RO_GENERIC_OFF); + OPT_BOOL(RSK_STARTING_STICKS, "Start with Stick Ammo", {"No", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("StartingSticks"), "", WIDGET_CVAR_CHECKBOX, RO_GENERIC_OFF); + OPT_BOOL(RSK_STARTING_NUTS, "Start with Nut Ammo", {"No", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("StartingNuts"), "", WIDGET_CVAR_CHECKBOX, RO_GENERIC_OFF); + OPT_BOOL(RSK_STARTING_BEANS, "Start with Magic Beans", {"No", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("StartingBeans"), "", WIDGET_CVAR_CHECKBOX, RO_GENERIC_OFF); + OPT_BOOL(RSK_FULL_WALLETS, "Full Wallets", {"No", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("FullWallets"), mOptionDescriptions[RSK_FULL_WALLETS], WIDGET_CVAR_CHECKBOX, RO_GENERIC_OFF); OPT_BOOL(RSK_STARTING_ZELDAS_LULLABY, "Start with Zelda's Lullaby", CVAR_RANDOMIZER_SETTING("StartingZeldasLullaby"), "", IMFLAG_NONE); OPT_BOOL(RSK_STARTING_EPONAS_SONG, "Start with Epona's Song", CVAR_RANDOMIZER_SETTING("StartingEponasSong"), "", IMFLAG_NONE); OPT_BOOL(RSK_STARTING_SARIAS_SONG, "Start with Saria's Song", CVAR_RANDOMIZER_SETTING("StartingSariasSong"), "", IMFLAG_NONE); @@ -338,13 +1253,48 @@ void Settings::CreateOptions() { OPT_BOOL(RSK_STARTING_REQUIEM_OF_SPIRIT, "Start with Requiem of Spirit", CVAR_RANDOMIZER_SETTING("StartingRequiemOfSpirit"), "", IMFLAG_NONE); OPT_BOOL(RSK_STARTING_NOCTURNE_OF_SHADOW, "Start with Nocturne of Shadow", CVAR_RANDOMIZER_SETTING("StartingNocturneOfShadow"), "", IMFLAG_NONE); OPT_BOOL(RSK_STARTING_PRELUDE_OF_LIGHT, "Start with Prelude of Light", CVAR_RANDOMIZER_SETTING("StartingPreludeOfLight")); - OPT_U8(RSK_STARTING_SKULLTULA_TOKEN, "Gold Skulltula Tokens", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("StartingSkulltulaToken"), "", WidgetType::Slider); - OPT_U8(RSK_STARTING_HEARTS, "Starting Hearts", {NumOpts(1, 20)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("StartingHearts"), "", WidgetType::Slider, 2); + OPT_U8(RSK_STARTING_SKULLTULA_TOKEN, "Gold Skulltula Tokens", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("StartingSkulltulaToken"), "", WIDGET_CVAR_SLIDER_INT); + OPT_U8(RSK_STARTING_HEARTS, "Starting Hearts", {NumOpts(1, 20)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("StartingHearts"), "", WIDGET_CVAR_SLIDER_INT, 2); // TODO: Remainder of Starting Items - OPT_U8(RSK_LOGIC_RULES, "Logic", {"Glitchless", "No Logic"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LogicRules"), mOptionDescriptions[RSK_LOGIC_RULES], WidgetType::Combobox, RO_LOGIC_GLITCHLESS); - OPT_BOOL(RSK_ALL_LOCATIONS_REACHABLE, "All Locations Reachable", {"Off", "On"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("AllLocationsReachable"), mOptionDescriptions[RSK_ALL_LOCATIONS_REACHABLE], WidgetType::Checkbox, RO_GENERIC_ON); + OPT_U8(RSK_LOGIC_RULES, "Logic", {"Glitchless", "No Logic"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LogicRules"), mOptionDescriptions[RSK_LOGIC_RULES], WIDGET_CVAR_COMBOBOX, RO_LOGIC_GLITCHLESS, false, nullptr, IMFLAG_LABEL_INLINE); + OPT_BOOL(RSK_ALL_LOCATIONS_REACHABLE, "All Locations Reachable", {"Off", "On"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("AllLocationsReachable"), mOptionDescriptions[RSK_ALL_LOCATIONS_REACHABLE], WIDGET_CVAR_CHECKBOX, RO_GENERIC_ON, false, nullptr, IMFLAG_SAME_LINE); OPT_BOOL(RSK_SKULLS_SUNS_SONG, "Night Skulltula's Expect Sun's Song", CVAR_RANDOMIZER_SETTING("GsExpectSunsSong"), mOptionDescriptions[RSK_SKULLS_SUNS_SONG]); - OPT_U8(RSK_DAMAGE_MULTIPLIER, "Damage Multiplier", {"x1/2", "x1", "x2", "x4", "x8", "x16", "OHKO"}, OptionCategory::Setting, "", "", WidgetType::Slider, RO_DAMAGE_MULTIPLIER_DEFAULT); + OPT_U8(RSK_DAMAGE_MULTIPLIER, "Damage Multiplier", {"x1/2", "x1", "x2", "x4", "x8", "x16", "OHKO"}, OptionCategory::Setting, "", "", WIDGET_CVAR_SLIDER_INT, RO_DAMAGE_MULTIPLIER_DEFAULT); + // Don't show any MQ options if both quests aren't available + if (!(OTRGlobals::Instance->HasMasterQuest() && OTRGlobals::Instance->HasOriginal())) { + mOptions[RSK_MQ_DUNGEON_RANDOM].Disable("This Options has been disabled because only one type of OTR has been loaded"); + mOptions[RSK_MQ_DUNGEON_COUNT].Disable("This Options has been disabled because only one type of OTR has been loaded"); + mOptions[RSK_MQ_DUNGEON_SET].Disable("This Options has been disabled because only one type of OTR has been loaded"); + mOptions[RSK_MQ_DEKU_TREE].Disable("This Options has been disabled because only one type of OTR has been loaded"); + mOptions[RSK_MQ_DODONGOS_CAVERN].Disable("This Options has been disabled because only one type of OTR has been loaded"); + mOptions[RSK_MQ_JABU_JABU].Disable("This Options has been disabled because only one type of OTR has been loaded"); + mOptions[RSK_MQ_FOREST_TEMPLE].Disable("This Options has been disabled because only one type of OTR has been loaded"); + mOptions[RSK_MQ_FIRE_TEMPLE].Disable("This Options has been disabled because only one type of OTR has been loaded"); + mOptions[RSK_MQ_WATER_TEMPLE].Disable("This Options has been disabled because only one type of OTR has been loaded"); + mOptions[RSK_MQ_SPIRIT_TEMPLE].Disable("This Options has been disabled because only one type of OTR has been loaded"); + mOptions[RSK_MQ_SHADOW_TEMPLE].Disable("This Options has been disabled because only one type of OTR has been loaded"); + mOptions[RSK_MQ_BOTTOM_OF_THE_WELL].Disable("This Options has been disabled because only one type of OTR has been loaded"); + mOptions[RSK_MQ_ICE_CAVERN].Disable("This Options has been disabled because only one type of OTR has been loaded"); + mOptions[RSK_MQ_GTG].Disable("This Options has been disabled because only one type of OTR has been loaded"); + mOptions[RSK_MQ_GANONS_CASTLE].Disable("This Options has been disabled because only one type of OTR has been loaded"); + } else { + // If any MQ Options are available, show the MQ Dungeon Randomization Combobox + mOptions[RSK_MQ_DUNGEON_RANDOM].Enable(); + mOptions[RSK_MQ_DUNGEON_COUNT].Enable(); + mOptions[RSK_MQ_DUNGEON_SET].Enable(); + mOptions[RSK_MQ_DEKU_TREE].Enable(); + mOptions[RSK_MQ_DODONGOS_CAVERN].Enable(); + mOptions[RSK_MQ_JABU_JABU].Enable(); + mOptions[RSK_MQ_FOREST_TEMPLE].Enable(); + mOptions[RSK_MQ_FIRE_TEMPLE].Enable(); + mOptions[RSK_MQ_WATER_TEMPLE].Enable(); + mOptions[RSK_MQ_SPIRIT_TEMPLE].Enable(); + mOptions[RSK_MQ_SHADOW_TEMPLE].Enable(); + mOptions[RSK_MQ_BOTTOM_OF_THE_WELL].Enable(); + mOptions[RSK_MQ_ICE_CAVERN].Enable(); + mOptions[RSK_MQ_GTG].Enable(); + mOptions[RSK_MQ_GANONS_CASTLE].Enable(); + } // clang-format on StaticData::optionNameToEnum = PopulateOptionNameToEnum(); @@ -1218,50 +2168,63 @@ void Settings::CreateOptions() { } mOptionGroups[RSG_TRICKS] = OptionGroup::SubGroup("Logical Tricks", tricksOption); // TODO: Glitches - mOptionGroups[RSG_AREA_ACCESS_IMGUI] = OptionGroup::SubGroup("Area Access", - { - &mOptions[RSK_FOREST], - &mOptions[RSK_KAK_GATE], - &mOptions[RSK_DOOR_OF_TIME], - &mOptions[RSK_ZORAS_FOUNTAIN], - &mOptions[RSK_SLEEPING_WATERFALL], - &mOptions[RSK_JABU_OPEN], - &mOptions[RSK_LOCK_OVERWORLD_DOORS], - }, - WidgetContainerType::COLUMN); - mOptionGroups[RSG_WORLD_IMGUI] = OptionGroup::SubGroup("World Settings", - { &mOptions[RSK_STARTING_AGE], - &mOptions[RSK_GERUDO_FORTRESS], - &mOptions[RSK_RAINBOW_BRIDGE], - &mOptions[RSK_BRIDGE_OPTIONS], - &mOptions[RSK_RAINBOW_BRIDGE_STONE_COUNT], - &mOptions[RSK_RAINBOW_BRIDGE_MEDALLION_COUNT], - &mOptions[RSK_RAINBOW_BRIDGE_REWARD_COUNT], - &mOptions[RSK_RAINBOW_BRIDGE_DUNGEON_COUNT], - &mOptions[RSK_RAINBOW_BRIDGE_TOKEN_COUNT], - &mOptions[RSK_GANONS_TRIALS], - &mOptions[RSK_TRIAL_COUNT], - &mOptions[RSK_MQ_DUNGEON_RANDOM], - &mOptions[RSK_MQ_DUNGEON_COUNT], - &mOptions[RSK_MQ_DUNGEON_SET], - &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], - &mOptions[RSK_TRIFORCE_HUNT], - &mOptions[RSK_TRIFORCE_HUNT_PIECES_TOTAL], - &mOptions[RSK_TRIFORCE_HUNT_PIECES_REQUIRED] }, - WidgetContainerType::COLUMN); - mOptionGroups[RSG_SHUFFLE_ENTRANCES_IMGUI] = OptionGroup::SubGroup( - "Shuffle Entrances", + mOptionGroups[RSG_MENU_SECTION_LOGIC] = OptionGroup::SubGroup("Logic", + { + &mOptions[RSK_LOGIC_RULES], + &mOptions[RSK_ALL_LOCATIONS_REACHABLE], + &mOptions[RSK_STARTING_AGE], + &mOptions[RSK_SKULLS_SUNS_SONG], + &mOptions[RSK_BLUE_FIRE_ARROWS], + &mOptions[RSK_SUNLIGHT_ARROWS], + &mOptions[RSK_FULL_WALLETS], + &mOptions[RSK_SLINGBOW_BREAK_BEEHIVES], + &mOptions[RSK_SKIP_CHILD_ZELDA], + &mOptions[RSK_COMPLETE_MASK_QUEST], + &mOptions[RSK_SKIP_CHILD_STEALTH], + &mOptions[RSK_SKIP_PLANTING_BEANS], + &mOptions[RSK_SKIP_EPONA_RACE], + &mOptions[RSK_SKIP_SCARECROWS_SONG], + }, + WidgetContainerType::SECTION); + mOptionGroups[RSG_MENU_SECTION_WINCON] = OptionGroup::SubGroup( + "Win Condition", + { &mOptions[RSK_TRIFORCE_HUNT], &mOptions[RSK_TRIFORCE_HUNT_PIECES_TOTAL], + &mOptions[RSK_TRIFORCE_HUNT_PIECES_REQUIRED], &mOptions[RSK_GANONS_BOSS_KEY], &mOptions[RSK_LACS_OPTIONS], + &mOptions[RSK_LACS_MEDALLION_COUNT], &mOptions[RSK_LACS_STONE_COUNT], &mOptions[RSK_LACS_DUNGEON_COUNT], + &mOptions[RSK_LACS_REWARD_COUNT], &mOptions[RSK_LACS_TOKEN_COUNT] }, + WidgetContainerType::SECTION); + mOptionGroups[RSG_MENU_COLUMN_LOGIC_WINCON] = OptionGroup::SubGroup("", + std::initializer_list{ + &mOptionGroups[RSG_MENU_SECTION_LOGIC], + &mOptionGroups[RSG_MENU_SECTION_WINCON], + }, + WidgetContainerType::COLUMN); + mOptionGroups[RSG_MENU_SECTION_AREA_ACCESS] = + OptionGroup::SubGroup("Area Access", + { + &mOptions[RSK_FOREST], + &mOptions[RSK_KAK_GATE], + &mOptions[RSK_DOOR_OF_TIME], + &mOptions[RSK_ZORAS_FOUNTAIN], + &mOptions[RSK_SLEEPING_WATERFALL], + &mOptions[RSK_JABU_OPEN], + &mOptions[RSK_LOCK_OVERWORLD_DOORS], + &mOptions[RSK_GERUDO_FORTRESS], + &mOptions[RSK_RAINBOW_BRIDGE], + &mOptions[RSK_BRIDGE_OPTIONS], + &mOptions[RSK_RAINBOW_BRIDGE_STONE_COUNT], + &mOptions[RSK_RAINBOW_BRIDGE_MEDALLION_COUNT], + &mOptions[RSK_RAINBOW_BRIDGE_REWARD_COUNT], + &mOptions[RSK_RAINBOW_BRIDGE_DUNGEON_COUNT], + &mOptions[RSK_RAINBOW_BRIDGE_TOKEN_COUNT], + &mOptions[RSK_GANONS_TRIALS], + &mOptions[RSK_TRIAL_COUNT], + }, + WidgetContainerType::SECTION); + mOptionGroups[RSG_MENU_COLUMN_AREA_ACCESS] = + OptionGroup::SubGroup("", { &mOptionGroups[RSG_MENU_SECTION_AREA_ACCESS] }, WidgetContainerType::COLUMN); + mOptionGroups[RSG_MENU_SECTION_ENTRANCES] = OptionGroup::SubGroup( + "Entrances", { &mOptions[RSK_SHUFFLE_DUNGEON_ENTRANCES], &mOptions[RSK_SHUFFLE_BOSS_ENTRANCES], &mOptions[RSK_SHUFFLE_OVERWORLD_ENTRANCES], &mOptions[RSK_SHUFFLE_INTERIOR_ENTRANCES], &mOptions[RSK_SHUFFLE_THIEVES_HIDEOUT_ENTRANCES], &mOptions[RSK_SHUFFLE_GROTTO_ENTRANCES], @@ -1270,36 +2233,97 @@ void Settings::CreateOptions() { &mOptions[RSK_MIX_BOSS_ENTRANCES], &mOptions[RSK_MIX_OVERWORLD_ENTRANCES], &mOptions[RSK_MIX_INTERIOR_ENTRANCES], &mOptions[RSK_MIX_THIEVES_HIDEOUT_ENTRANCES], &mOptions[RSK_MIX_GROTTO_ENTRANCES] }, - WidgetContainerType::COLUMN); - mOptionGroups[RSG_WORLD_IMGUI_TABLE] = OptionGroup::SubGroup("World", - { - &mOptionGroups[RSG_AREA_ACCESS_IMGUI], - &mOptionGroups[RSG_WORLD_IMGUI], - &mOptionGroups[RSG_SHUFFLE_ENTRANCES_IMGUI], - }, - WidgetContainerType::TABLE); - mOptionGroups[RSG_SHUFFLE_ITEMS_IMGUI] = OptionGroup::SubGroup("Shuffle Items", - { - &mOptions[RSK_SHUFFLE_SONGS], - &mOptions[RSK_SHUFFLE_TOKENS], - &mOptions[RSK_SKULLS_SUNS_SONG], - &mOptions[RSK_SHUFFLE_KOKIRI_SWORD], - &mOptions[RSK_SHUFFLE_MASTER_SWORD], - &mOptions[RSK_SHUFFLE_CHILD_WALLET], - &mOptions[RSK_INCLUDE_TYCOON_WALLET], - &mOptions[RSK_SHUFFLE_OCARINA], - &mOptions[RSK_SHUFFLE_OCARINA_BUTTONS], - &mOptions[RSK_SHUFFLE_SWIM], - &mOptions[RSK_SHUFFLE_WEIRD_EGG], - &mOptions[RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD], - &mOptions[RSK_SHUFFLE_FISHING_POLE], - &mOptions[RSK_SHUFFLE_DEKU_STICK_BAG], - &mOptions[RSK_SHUFFLE_DEKU_NUT_BAG], - &mOptions[RSK_SHUFFLE_FREESTANDING], - }, - WidgetContainerType::COLUMN); - mOptionGroups[RSG_SHUFFLE_NPCS_IMGUI] = - OptionGroup::SubGroup("Shuffle NPCs & Merchants", + WidgetContainerType::SECTION); + mOptionGroups[RSG_MENU_COLUMN_ENTRANCES] = + OptionGroup::SubGroup("", { &mOptionGroups[RSG_MENU_SECTION_ENTRANCES] }, WidgetContainerType::COLUMN); + mOptionGroups[RSG_MENU_SIDEBAR_LOGIC_ACCESS] = + OptionGroup::SubGroup("Logic/Access", + std::initializer_list{ &mOptionGroups[RSG_MENU_COLUMN_LOGIC_WINCON], + &mOptionGroups[RSG_MENU_COLUMN_AREA_ACCESS], + &mOptionGroups[RSG_MENU_COLUMN_ENTRANCES] }, + WidgetContainerType::TABLE); + mOptionGroups[RSG_MENU_SECTION_DUNGEON_ITEMS] = OptionGroup::SubGroup("Dungeon Items", + { + &mOptions[RSK_SHUFFLE_MAPANDCOMPASS], + &mOptions[RSK_KEYSANITY], + &mOptions[RSK_BOSS_KEYSANITY], + &mOptions[RSK_SHUFFLE_DUNGEON_REWARDS], + &mOptions[RSK_GERUDO_KEYS], + &mOptions[RSK_SHUFFLE_BOSS_SOULS], + }, + WidgetContainerType::SECTION); + mOptionGroups[RSG_MENU_COLUMN_DUNGEON_ITEMS] = + OptionGroup::SubGroup("", { &mOptionGroups[RSG_MENU_SECTION_DUNGEON_ITEMS] }, WidgetContainerType::COLUMN); + mOptionGroups[RSG_MENU_SECTION_MQ] = OptionGroup::SubGroup("Master Quest", + { + &mOptions[RSK_MQ_DUNGEON_RANDOM], + &mOptions[RSK_MQ_DUNGEON_COUNT], + &mOptions[RSK_MQ_DUNGEON_SET], + &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], + }, + WidgetContainerType::SECTION); + mOptionGroups[RSG_MENU_COLUMN_MQ] = + OptionGroup::SubGroup("", { &mOptionGroups[RSG_MENU_SECTION_MQ] }, WidgetContainerType::COLUMN); + mOptionGroups[RSG_MENU_SECTION_KEYRINGS] = OptionGroup::SubGroup( + "Keyrings", + { &mOptions[RSK_KEYRINGS], &mOptions[RSK_KEYRINGS_RANDOM_COUNT], &mOptions[RSK_KEYRINGS_FOREST_TEMPLE], + &mOptions[RSK_KEYRINGS_FIRE_TEMPLE], &mOptions[RSK_KEYRINGS_WATER_TEMPLE], + &mOptions[RSK_KEYRINGS_SPIRIT_TEMPLE], &mOptions[RSK_KEYRINGS_SHADOW_TEMPLE], + &mOptions[RSK_KEYRINGS_BOTTOM_OF_THE_WELL], &mOptions[RSK_KEYRINGS_GTG], + &mOptions[RSK_KEYRINGS_GANONS_CASTLE], &mOptions[RSK_KEYRINGS_GERUDO_FORTRESS] }, + WidgetContainerType::SECTION); + mOptionGroups[RSG_MENU_COLUMN_KEYRINGS] = + OptionGroup::SubGroup("", { &mOptionGroups[RSG_MENU_SECTION_KEYRINGS] }, WidgetContainerType::COLUMN); + mOptionGroups[RSG_MENU_SIDEBAR_DUNGEONS] = OptionGroup::SubGroup("Dungeons", + std::initializer_list{ + &mOptionGroups[RSG_MENU_COLUMN_DUNGEON_ITEMS], + &mOptionGroups[RSG_MENU_COLUMN_KEYRINGS], + &mOptionGroups[RSG_MENU_COLUMN_MQ], + }, + WidgetContainerType::TABLE); + mOptionGroups[RSG_MENU_SECTION_BASIC_SHUFFLES] = + OptionGroup::SubGroup("Shuffle Items", + { + &mOptions[RSK_SHUFFLE_SONGS], + &mOptions[RSK_SHUFFLE_TOKENS], + &mOptions[RSK_SHUFFLE_KOKIRI_SWORD], + &mOptions[RSK_SHUFFLE_MASTER_SWORD], + &mOptions[RSK_SHUFFLE_OCARINA], + &mOptions[RSK_SHUFFLE_WEIRD_EGG], + &mOptions[RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD], + &mOptions[RSK_FISHSANITY], + &mOptions[RSK_FISHSANITY_POND_COUNT], + &mOptions[RSK_FISHSANITY_AGE_SPLIT], + &mOptions[RSK_SHUFFLE_FREESTANDING], + &mOptions[RSK_SHUFFLE_BEEHIVES], + &mOptions[RSK_SHUFFLE_COWS], + &mOptions[RSK_SHUFFLE_POTS], + &mOptions[RSK_SHUFFLE_CRATES], + &mOptions[RSK_SHUFFLE_TREES], + &mOptions[RSK_SHUFFLE_FROG_SONG_RUPEES], + &mOptions[RSK_SHUFFLE_ADULT_TRADE], + &mOptions[RSK_SHUFFLE_100_GS_REWARD], + &mOptions[RSK_SHUFFLE_FOUNTAIN_FAIRIES], + &mOptions[RSK_SHUFFLE_STONE_FAIRIES], + &mOptions[RSK_SHUFFLE_BEAN_FAIRIES], + &mOptions[RSK_SHUFFLE_SONG_FAIRIES], + }, + WidgetContainerType::SECTION); + mOptionGroups[RSG_MENU_COLUMN_BASIC_SHUFFLES] = + OptionGroup::SubGroup("", { &mOptionGroups[RSG_MENU_SECTION_BASIC_SHUFFLES] }, WidgetContainerType::COLUMN); + mOptionGroups[RSG_MENU_SECTION_SHOP_SHUFFLES] = + OptionGroup::SubGroup("Shuffle Shops & Merchants", { &mOptions[RSK_SHOPSANITY], &mOptions[RSK_SHOPSANITY_COUNT], @@ -1313,9 +2337,6 @@ void Settings::CreateOptions() { &mOptions[RSK_SHOPSANITY_PRICES_GIANT_WALLET_WEIGHT], &mOptions[RSK_SHOPSANITY_PRICES_TYCOON_WALLET_WEIGHT], &mOptions[RSK_SHOPSANITY_PRICES_AFFORDABLE], - &mOptions[RSK_FISHSANITY], - &mOptions[RSK_FISHSANITY_POND_COUNT], - &mOptions[RSK_FISHSANITY_AGE_SPLIT], &mOptions[RSK_SHUFFLE_SCRUBS], &mOptions[RSK_SCRUBS_PRICES], &mOptions[RSK_SCRUBS_PRICES_FIXED_PRICE], @@ -1327,12 +2348,6 @@ void Settings::CreateOptions() { &mOptions[RSK_SCRUBS_PRICES_GIANT_WALLET_WEIGHT], &mOptions[RSK_SCRUBS_PRICES_TYCOON_WALLET_WEIGHT], &mOptions[RSK_SCRUBS_PRICES_AFFORDABLE], - &mOptions[RSK_SHUFFLE_BEEHIVES], - &mOptions[RSK_SHUFFLE_COWS], - &mOptions[RSK_SHUFFLE_POTS], - &mOptions[RSK_SHUFFLE_CRATES], - &mOptions[RSK_SHUFFLE_TREES], - &mOptions[RSK_SHUFFLE_BUSHES], &mOptions[RSK_SHUFFLE_MERCHANTS], &mOptions[RSK_MERCHANT_PRICES], &mOptions[RSK_MERCHANT_PRICES_FIXED_PRICE], @@ -1344,69 +2359,55 @@ void Settings::CreateOptions() { &mOptions[RSK_MERCHANT_PRICES_GIANT_WALLET_WEIGHT], &mOptions[RSK_MERCHANT_PRICES_TYCOON_WALLET_WEIGHT], &mOptions[RSK_MERCHANT_PRICES_AFFORDABLE], - &mOptions[RSK_SHUFFLE_FROG_SONG_RUPEES], - &mOptions[RSK_SHUFFLE_ADULT_TRADE], - &mOptions[RSK_SHUFFLE_100_GS_REWARD], - &mOptions[RSK_SHUFFLE_BEAN_SOULS], - &mOptions[RSK_SHUFFLE_BOSS_SOULS], - &mOptions[RSK_SHUFFLE_FOUNTAIN_FAIRIES], - &mOptions[RSK_SHUFFLE_STONE_FAIRIES], - &mOptions[RSK_SHUFFLE_BEAN_FAIRIES], - &mOptions[RSK_SHUFFLE_SONG_FAIRIES], - &mOptions[RSK_SHUFFLE_GRASS], }, - WidgetContainerType::COLUMN); - mOptionGroups[RSG_SHUFFLE_DUNGEON_ITEMS_IMGUI] = - OptionGroup::SubGroup("Shuffle Dungeon Items", + WidgetContainerType::SECTION); + mOptionGroups[RSG_MENU_COLUMN_SHOP_SHUFFLES] = + OptionGroup::SubGroup("", { &mOptionGroups[RSG_MENU_SECTION_SHOP_SHUFFLES] }, WidgetContainerType::COLUMN); + mOptionGroups[RSG_MENU_SECTION_ADDITIONAL_ITEMS] = OptionGroup::SubGroup("Additional Items", + { + &mOptions[RSK_SHUFFLE_CHILD_WALLET], + &mOptions[RSK_INCLUDE_TYCOON_WALLET], + &mOptions[RSK_SHUFFLE_FISHING_POLE], + &mOptions[RSK_SHUFFLE_DEKU_STICK_BAG], + &mOptions[RSK_SHUFFLE_DEKU_NUT_BAG], + &mOptions[RSK_SHUFFLE_OCARINA_BUTTONS], + &mOptions[RSK_SHUFFLE_SWIM], + &mOptions[RSK_SHUFFLE_BEAN_SOULS], + &mOptions[RSK_BOMBCHU_BAG], + &mOptions[RSK_ENABLE_BOMBCHU_DROPS], + &mOptions[RSK_INFINITE_UPGRADES], + &mOptions[RSK_SKELETON_KEY], + }, + WidgetContainerType::SECTION); + mOptionGroups[RSG_MENU_COLUMN_ADDITIONAL_ITEMS] = + OptionGroup::SubGroup("", { &mOptionGroups[RSG_MENU_SECTION_ADDITIONAL_ITEMS] }, WidgetContainerType::COLUMN); + mOptionGroups[RSG_MENU_SIDEBAR_SHUFFLES] = + OptionGroup::SubGroup("Shuffles", { - &mOptions[RSK_SHUFFLE_DUNGEON_REWARDS], - &mOptions[RSK_SHUFFLE_MAPANDCOMPASS], - &mOptions[RSK_KEYSANITY], - &mOptions[RSK_GERUDO_KEYS], - &mOptions[RSK_BOSS_KEYSANITY], - &mOptions[RSK_GANONS_BOSS_KEY], - &mOptions[RSK_LACS_STONE_COUNT], - &mOptions[RSK_LACS_MEDALLION_COUNT], - &mOptions[RSK_LACS_DUNGEON_COUNT], - &mOptions[RSK_LACS_REWARD_COUNT], - &mOptions[RSK_LACS_TOKEN_COUNT], - &mOptions[RSK_LACS_OPTIONS], - &mOptions[RSK_KEYRINGS], - &mOptions[RSK_KEYRINGS_RANDOM_COUNT], - &mOptions[RSK_KEYRINGS_GERUDO_FORTRESS], - &mOptions[RSK_KEYRINGS_FOREST_TEMPLE], - &mOptions[RSK_KEYRINGS_FIRE_TEMPLE], - &mOptions[RSK_KEYRINGS_WATER_TEMPLE], - &mOptions[RSK_KEYRINGS_SPIRIT_TEMPLE], - &mOptions[RSK_KEYRINGS_SHADOW_TEMPLE], - &mOptions[RSK_KEYRINGS_BOTTOM_OF_THE_WELL], - &mOptions[RSK_KEYRINGS_GTG], - &mOptions[RSK_KEYRINGS_GANONS_CASTLE], + &mOptionGroups[RSG_MENU_COLUMN_BASIC_SHUFFLES], + &mOptionGroups[RSG_MENU_COLUMN_SHOP_SHUFFLES], + &mOptionGroups[RSG_MENU_COLUMN_ADDITIONAL_ITEMS], }, + WidgetContainerType::TABLE); + mOptionGroups[RSG_MENU_SECTION_HINTS] = OptionGroup::SubGroup("Hints", + { + &mOptions[RSK_GOSSIP_STONE_HINTS], + &mOptions[RSK_HINT_CLARITY], + &mOptions[RSK_HINT_DISTRIBUTION], + }, + WidgetContainerType::SECTION); + mOptionGroups[RSG_MENU_SECTION_TRAPS] = OptionGroup::SubGroup("Traps", + { + &mOptions[RSK_ICE_TRAPS], + }, + WidgetContainerType::SECTION); + mOptionGroups[RSG_MENU_COLUMN_HINTS_TRAPS] = + OptionGroup::SubGroup("", + std::initializer_list{ &mOptionGroups[RSG_MENU_SECTION_HINTS], + &mOptionGroups[RSG_MENU_SECTION_TRAPS] }, WidgetContainerType::COLUMN); - mOptionGroups[RSG_ITEMS_IMGUI_TABLE] = OptionGroup::SubGroup("Items", - { - &mOptionGroups[RSG_SHUFFLE_ITEMS_IMGUI], - &mOptionGroups[RSG_SHUFFLE_NPCS_IMGUI], - &mOptionGroups[RSG_SHUFFLE_DUNGEON_ITEMS_IMGUI], - }, - WidgetContainerType::TABLE); - mOptionGroups[RSG_TIMESAVERS_IMGUI] = OptionGroup::SubGroup( - "Timesavers", - { &mOptions[RSK_BIG_POE_COUNT], &mOptions[RSK_SKIP_CHILD_ZELDA], &mOptions[RSK_SKIP_EPONA_RACE], - &mOptions[RSK_COMPLETE_MASK_QUEST], &mOptions[RSK_SKIP_SCARECROWS_SONG], &mOptions[RSK_SKIP_PLANTING_BEANS] }, - WidgetContainerType::COLUMN); - mOptionGroups[RSG_ITEM_POOL_HINTS_IMGUI] = OptionGroup::SubGroup("", - { - &mOptions[RSK_ITEM_POOL], - &mOptions[RSK_ICE_TRAPS], - &mOptions[RSK_GOSSIP_STONE_HINTS], - &mOptions[RSK_HINT_CLARITY], - &mOptions[RSK_HINT_DISTRIBUTION], - }, - WidgetContainerType::SECTION); - mOptionGroups[RSG_EXTRA_HINTS_IMGUI] = OptionGroup::SubGroup( - "Extra Hints", + mOptionGroups[RSG_MENU_SECTION_STATIC_HINTS] = OptionGroup::SubGroup( + "Static Hints", { &mOptions[RSK_TOT_ALTAR_HINT], &mOptions[RSK_GANONDORF_HINT], &mOptions[RSK_SHEIK_LA_HINT], @@ -1434,71 +2435,70 @@ void Settings::CreateOptions() { &mOptions[RSK_KAK_100_SKULLS_HINT], &mOptions[RSK_MASK_SHOP_HINT] }, WidgetContainerType::SECTION, "This setting adds some hints at locations other than Gossip Stones."); - mOptionGroups[RSG_ITEM_POOL_HINTS_IMGUI_COLUMN] = - OptionGroup::SubGroup("Item Pool & Hints", + mOptionGroups[RSG_MENU_COLUMN_STATIC_HINTS] = + OptionGroup::SubGroup("", { &mOptionGroups[RSG_MENU_SECTION_STATIC_HINTS] }, WidgetContainerType::COLUMN); + mOptionGroups[RSG_MENU_SIDEBAR_HINTS_TRAPS] = + OptionGroup::SubGroup("Hints/Traps", std::initializer_list{ - &mOptionGroups[RSG_ITEM_POOL_HINTS_IMGUI], - &mOptionGroups[RSG_EXTRA_HINTS_IMGUI], + &mOptionGroups[RSG_MENU_COLUMN_HINTS_TRAPS], + &mOptionGroups[RSG_MENU_COLUMN_STATIC_HINTS], }, - WidgetContainerType::COLUMN); - mOptionGroups[RSG_ADDITIONAL_FEATURES_IMGUI] = OptionGroup::SubGroup("Additional Features", - { - &mOptions[RSK_FULL_WALLETS], - &mOptions[RSK_BOMBCHU_BAG], - &mOptions[RSK_ENABLE_BOMBCHU_DROPS], - &mOptions[RSK_BLUE_FIRE_ARROWS], - &mOptions[RSK_SUNLIGHT_ARROWS], - &mOptions[RSK_INFINITE_UPGRADES], - &mOptions[RSK_SKELETON_KEY], - &mOptions[RSK_SLINGBOW_BREAK_BEEHIVES], - }, - WidgetContainerType::COLUMN); - mOptionGroups[RSG_GAMEPLAY_IMGUI_TABLE] = - OptionGroup::SubGroup("Gameplay", - { &mOptionGroups[RSG_TIMESAVERS_IMGUI], &mOptionGroups[RSG_ITEM_POOL_HINTS_IMGUI_COLUMN], - &mOptionGroups[RSG_ADDITIONAL_FEATURES_IMGUI] }, WidgetContainerType::TABLE); - mOptionGroups[RSG_STARTING_EQUIPMENT_IMGUI] = - OptionGroup::SubGroup("Starting Equipment", + mOptionGroups[RSG_MENU_SECTION_STARTING_EQUIPS] = + OptionGroup::SubGroup("Equips", { &mOptions[RSK_LINKS_POCKET], &mOptions[RSK_STARTING_KOKIRI_SWORD], &mOptions[RSK_STARTING_MASTER_SWORD], &mOptions[RSK_STARTING_DEKU_SHIELD] }, - WidgetContainerType::COLUMN); - mOptionGroups[RSG_STARTING_ITEMS_IMGUI] = OptionGroup::SubGroup("Starting Items", - { - &mOptions[RSK_STARTING_OCARINA], - &mOptions[RSK_STARTING_STICKS], - &mOptions[RSK_STARTING_NUTS], - &mOptions[RSK_STARTING_BEANS], - &mOptions[RSK_STARTING_SKULLTULA_TOKEN], - &mOptions[RSK_STARTING_HEARTS], - }, - WidgetContainerType::COLUMN); - mOptionGroups[RSG_STARTING_NORMAL_SONGS_IMGUI] = OptionGroup::SubGroup("Normal Songs", + WidgetContainerType::SECTION); + mOptionGroups[RSG_MENU_SECTION_STARTING_ITEMS] = OptionGroup::SubGroup("Items", { - &mOptions[RSK_STARTING_ZELDAS_LULLABY], - &mOptions[RSK_STARTING_EPONAS_SONG], - &mOptions[RSK_STARTING_SARIAS_SONG], - &mOptions[RSK_STARTING_SUNS_SONG], - &mOptions[RSK_STARTING_SONG_OF_TIME], - &mOptions[RSK_STARTING_SONG_OF_STORMS], + &mOptions[RSK_STARTING_OCARINA], + &mOptions[RSK_STARTING_STICKS], + &mOptions[RSK_STARTING_NUTS], + &mOptions[RSK_STARTING_BEANS], + &mOptions[RSK_STARTING_SKULLTULA_TOKEN], + &mOptions[RSK_STARTING_HEARTS], }, WidgetContainerType::SECTION); - mOptionGroups[RSG_STARTING_WARP_SONGS_IMGUI] = - OptionGroup::SubGroup("Warp Songs", - { &mOptions[RSK_STARTING_MINUET_OF_FOREST], &mOptions[RSK_STARTING_BOLERO_OF_FIRE], - &mOptions[RSK_STARTING_SERENADE_OF_WATER], &mOptions[RSK_STARTING_REQUIEM_OF_SPIRIT], - &mOptions[RSK_STARTING_NOCTURNE_OF_SHADOW], &mOptions[RSK_STARTING_PRELUDE_OF_LIGHT] }, - WidgetContainerType::SECTION); - mOptionGroups[RSG_STARTING_SONGS_IMGUI] = OptionGroup::SubGroup("Starting Songs", - std::initializer_list({ - &mOptionGroups[RSG_STARTING_NORMAL_SONGS_IMGUI], - &mOptionGroups[RSG_STARTING_WARP_SONGS_IMGUI], - }), - WidgetContainerType::COLUMN); - mOptionGroups[RSG_STARTING_INVENTORY_IMGUI_TABLE] = - OptionGroup::SubGroup("Starting Inventory", - { &mOptionGroups[RSG_STARTING_EQUIPMENT_IMGUI], &mOptionGroups[RSG_STARTING_ITEMS_IMGUI], - &mOptionGroups[RSG_STARTING_SONGS_IMGUI] }, + mOptionGroups[RSG_MENU_COLUMN_STARTING_EQUIPMENT] = + OptionGroup::SubGroup("", + std::initializer_list{ + &mOptionGroups[RSG_MENU_SECTION_STARTING_EQUIPS], + &mOptionGroups[RSG_MENU_SECTION_STARTING_ITEMS], + }, + WidgetContainerType::COLUMN); + mOptionGroups[RSG_MENU_SECTION_NORMAL_SONGS] = OptionGroup::SubGroup("Normal Songs", + { + &mOptions[RSK_STARTING_ZELDAS_LULLABY], + &mOptions[RSK_STARTING_EPONAS_SONG], + &mOptions[RSK_STARTING_SARIAS_SONG], + &mOptions[RSK_STARTING_SUNS_SONG], + &mOptions[RSK_STARTING_SONG_OF_TIME], + &mOptions[RSK_STARTING_SONG_OF_STORMS], + }, + WidgetContainerType::SECTION); + mOptionGroups[RSG_MENU_SECTION_WARP_SONGS] = OptionGroup::SubGroup("Warp Songs", + { + &mOptions[RSK_STARTING_MINUET_OF_FOREST], + &mOptions[RSK_STARTING_BOLERO_OF_FIRE], + &mOptions[RSK_STARTING_SERENADE_OF_WATER], + &mOptions[RSK_STARTING_REQUIEM_OF_SPIRIT], + &mOptions[RSK_STARTING_NOCTURNE_OF_SHADOW], + &mOptions[RSK_STARTING_PRELUDE_OF_LIGHT], + }, + WidgetContainerType::SECTION); + mOptionGroups[RSG_MENU_COLUMN_STARTING_SONGS] = + OptionGroup::SubGroup("", + std::initializer_list{ + &mOptionGroups[RSG_MENU_SECTION_NORMAL_SONGS], + &mOptionGroups[RSG_MENU_SECTION_WARP_SONGS], + }, + WidgetContainerType::COLUMN); + mOptionGroups[RSG_MENU_SIDEBAR_STARTING_ITEMS] = + OptionGroup::SubGroup("Starting Items", + std::initializer_list{ + &mOptionGroups[RSG_MENU_COLUMN_STARTING_EQUIPMENT], + &mOptionGroups[RSG_MENU_COLUMN_STARTING_SONGS], + }, WidgetContainerType::TABLE); mOptionGroups[RSG_OPEN] = OptionGroup("Open Settings", { &mOptions[RSK_FOREST], @@ -1886,716 +2886,9 @@ const OptionGroup& Settings::GetOptionGroup(const RandomizerSettingGroupKey key) return mOptionGroups[key]; } -void Settings::UpdateOptionProperties() { - // Default to hiding bridge opts and the extra sliders. - mOptions[RSK_RAINBOW_BRIDGE].AddFlag(IMFLAG_SEPARATOR_BOTTOM); - mOptions[RSK_BRIDGE_OPTIONS].Hide(); - mOptions[RSK_RAINBOW_BRIDGE_STONE_COUNT].Hide(); - mOptions[RSK_RAINBOW_BRIDGE_MEDALLION_COUNT].Hide(); - mOptions[RSK_RAINBOW_BRIDGE_REWARD_COUNT].Hide(); - mOptions[RSK_RAINBOW_BRIDGE_DUNGEON_COUNT].Hide(); - mOptions[RSK_RAINBOW_BRIDGE_TOKEN_COUNT].Hide(); - mOptionGroups[RSG_AREA_ACCESS_IMGUI].Enable(); - // Starting Age - Disabled when Forest is set to Closed or under very specific conditions - if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("DoorOfTime"), RO_DOOROFTIME_CLOSED) == RO_DOOROFTIME_CLOSED && - CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleOcarinas"), RO_GENERIC_OFF) == - RO_GENERIC_OFF) /* closed door of time with ocarina shuffle off */ { - mOptions[RSK_STARTING_AGE].Disable("This option is disabled due to other options making the game unbeatable."); - } else { - mOptions[RSK_STARTING_AGE].Enable(); - } - mOptions[RSK_GERUDO_FORTRESS].Enable(); - mOptions[RSK_RAINBOW_BRIDGE].Enable(); - mOptions[RSK_BRIDGE_OPTIONS].Enable(); - mOptions[RSK_RAINBOW_BRIDGE_STONE_COUNT].Enable(); - mOptions[RSK_RAINBOW_BRIDGE_MEDALLION_COUNT].Enable(); - mOptions[RSK_RAINBOW_BRIDGE_REWARD_COUNT].Enable(); - mOptions[RSK_RAINBOW_BRIDGE_DUNGEON_COUNT].Enable(); - mOptions[RSK_RAINBOW_BRIDGE_TOKEN_COUNT].Enable(); - const uint8_t bridgeOpt = CVarGetInteger(CVAR_RANDOMIZER_SETTING("BridgeRewardOptions"), RO_BRIDGE_STANDARD_REWARD); - switch (CVarGetInteger(CVAR_RANDOMIZER_SETTING("RainbowBridge"), RO_BRIDGE_VANILLA)) { - case RO_BRIDGE_STONES: - // Show Bridge Options and Stone Count slider - mOptions[RSK_RAINBOW_BRIDGE].RemoveFlag(IMFLAG_SEPARATOR_BOTTOM); - mOptions[RSK_BRIDGE_OPTIONS].Unhide(); - mOptions[RSK_RAINBOW_BRIDGE_STONE_COUNT].Unhide(); - if (bridgeOpt == RO_BRIDGE_GREG_REWARD) { - if (mOptions[RSK_RAINBOW_BRIDGE_STONE_COUNT].GetOptionCount() == 4) { - mOptions[RSK_RAINBOW_BRIDGE_STONE_COUNT].ChangeOptions(NumOpts(0, 4)); - } - } else { - if (mOptions[RSK_RAINBOW_BRIDGE_STONE_COUNT].GetOptionCount() == 5) { - mOptions[RSK_RAINBOW_BRIDGE_STONE_COUNT].ChangeOptions(NumOpts(0, 3)); - } - } - break; - case RO_BRIDGE_MEDALLIONS: - // Show Bridge Options and Medallion Count Slider - mOptions[RSK_RAINBOW_BRIDGE].RemoveFlag(IMFLAG_SEPARATOR_BOTTOM); - mOptions[RSK_BRIDGE_OPTIONS].Unhide(); - mOptions[RSK_RAINBOW_BRIDGE_MEDALLION_COUNT].Unhide(); - if (bridgeOpt == RO_BRIDGE_GREG_REWARD) { - if (mOptions[RSK_RAINBOW_BRIDGE_MEDALLION_COUNT].GetOptionCount() == 7) { - mOptions[RSK_RAINBOW_BRIDGE_MEDALLION_COUNT].ChangeOptions(NumOpts(0, 7)); - } - } else { - if (mOptions[RSK_RAINBOW_BRIDGE_MEDALLION_COUNT].GetOptionCount() == 8) { - mOptions[RSK_RAINBOW_BRIDGE_MEDALLION_COUNT].ChangeOptions(NumOpts(0, 6)); - } - } - break; - case RO_BRIDGE_DUNGEON_REWARDS: - // Show Bridge Options and Dungeon Reward Count Slider - mOptions[RSK_RAINBOW_BRIDGE].RemoveFlag(IMFLAG_SEPARATOR_BOTTOM); - mOptions[RSK_BRIDGE_OPTIONS].Unhide(); - mOptions[RSK_RAINBOW_BRIDGE_REWARD_COUNT].Unhide(); - if (bridgeOpt == RO_BRIDGE_GREG_REWARD) { - if (mOptions[RSK_RAINBOW_BRIDGE_REWARD_COUNT].GetOptionCount() == 10) { - mOptions[RSK_RAINBOW_BRIDGE_REWARD_COUNT].ChangeOptions(NumOpts(0, 10)); - } - } else { - if (mOptions[RSK_RAINBOW_BRIDGE_REWARD_COUNT].GetOptionCount() == 11) { - mOptions[RSK_RAINBOW_BRIDGE_REWARD_COUNT].ChangeOptions(NumOpts(0, 9)); - } - } - break; - case RO_BRIDGE_DUNGEONS: - // Show Bridge Options and Dungeon Count Slider - mOptions[RSK_RAINBOW_BRIDGE].RemoveFlag(IMFLAG_SEPARATOR_BOTTOM); - mOptions[RSK_BRIDGE_OPTIONS].Unhide(); - mOptions[RSK_RAINBOW_BRIDGE_DUNGEON_COUNT].Unhide(); - if (bridgeOpt == RO_BRIDGE_GREG_REWARD) { - if (mOptions[RSK_RAINBOW_BRIDGE_DUNGEON_COUNT].GetOptionCount() == 9) { - mOptions[RSK_RAINBOW_BRIDGE_DUNGEON_COUNT].ChangeOptions(NumOpts(0, 9)); - } - } else { - if (mOptions[RSK_RAINBOW_BRIDGE_DUNGEON_COUNT].GetOptionCount() == 10) { - mOptions[RSK_RAINBOW_BRIDGE_DUNGEON_COUNT].ChangeOptions(NumOpts(0, 8)); - } - } - break; - case RO_BRIDGE_TOKENS: - // Show token count slider (not bridge options) - mOptions[RSK_RAINBOW_BRIDGE].RemoveFlag(IMFLAG_SEPARATOR_BOTTOM); - mOptions[RSK_BRIDGE_OPTIONS].Hide(); - mOptions[RSK_RAINBOW_BRIDGE_TOKEN_COUNT].Unhide(); - break; - default: - break; - } - mOptions[RSK_GANONS_TRIALS].Enable(); - mOptions[RSK_TRIAL_COUNT].Enable(); - // Only show the trial count slider if Trials is set to Set Number - if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("GanonTrial"), RO_GANONS_TRIALS_SET_NUMBER) == - RO_GANONS_TRIALS_SET_NUMBER) { - mOptions[RSK_GANONS_TRIALS].RemoveFlag(IMFLAG_SEPARATOR_BOTTOM); - mOptions[RSK_TRIAL_COUNT].Unhide(); - } else { - mOptions[RSK_GANONS_TRIALS].AddFlag(IMFLAG_SEPARATOR_BOTTOM); - mOptions[RSK_TRIAL_COUNT].Hide(); - } - mOptions[RSK_TRIFORCE_HUNT].Enable(); - mOptions[RSK_TRIFORCE_HUNT_PIECES_TOTAL].Enable(); - mOptions[RSK_TRIFORCE_HUNT_PIECES_REQUIRED].Enable(); - // Remove the pieces required/total sliders and add a separator after Tirforce Hunt if Triforce Hunt is off - if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("TriforceHunt"), RO_GENERIC_OFF) == RO_GENERIC_OFF) { - mOptions[RSK_TRIFORCE_HUNT_PIECES_REQUIRED].Hide(); - mOptions[RSK_TRIFORCE_HUNT_PIECES_TOTAL].Hide(); - mOptions[RSK_TRIFORCE_HUNT].AddFlag(IMFLAG_SEPARATOR_BOTTOM); - } else { - mOptions[RSK_TRIFORCE_HUNT_PIECES_REQUIRED].Unhide(); - mOptions[RSK_TRIFORCE_HUNT_PIECES_TOTAL].Unhide(); - mOptions[RSK_TRIFORCE_HUNT].RemoveFlag(IMFLAG_SEPARATOR_BOTTOM); - } - // Update triforce pieces required to be capped at the current value for pieces total. - const uint8_t triforceTotal = CVarGetInteger(CVAR_RANDOMIZER_SETTING("TriforceHuntTotalPieces"), 30); - if (mOptions[RSK_TRIFORCE_HUNT_PIECES_REQUIRED].GetOptionCount() != triforceTotal + 1) { - mOptions[RSK_TRIFORCE_HUNT_PIECES_REQUIRED].ChangeOptions(NumOpts(1, triforceTotal + 1)); - } - mOptionGroups[RSG_ITEMS_IMGUI_TABLE].Enable(); - mOptionGroups[RSG_GAMEPLAY_IMGUI_TABLE].Enable(); - // Link's Pocket - Disabled when Dungeon Rewards are shuffled to End of Dungeon - if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleDungeonReward"), RO_DUNGEON_REWARDS_END_OF_DUNGEON) == - RO_DUNGEON_REWARDS_END_OF_DUNGEON) { - mOptions[RSK_LINKS_POCKET].Disable( - "This option is disabled because \"Dungeon Rewards\" are shuffled to \"End of Dungeons\"."); - } else { - mOptions[RSK_LINKS_POCKET].Enable(); - } - mOptions[RSK_STARTING_OCARINA].Enable(); - - // Don't show any MQ options if both quests aren't available - if (!(OTRGlobals::Instance->HasMasterQuest() && OTRGlobals::Instance->HasOriginal())) { - mOptions[RSK_MQ_DUNGEON_RANDOM].Hide(); - mOptions[RSK_MQ_DUNGEON_COUNT].Hide(); - mOptions[RSK_MQ_DUNGEON_SET].Hide(); - mOptions[RSK_MQ_DEKU_TREE].Hide(); - mOptions[RSK_MQ_DODONGOS_CAVERN].Hide(); - mOptions[RSK_MQ_JABU_JABU].Hide(); - mOptions[RSK_MQ_FOREST_TEMPLE].Hide(); - mOptions[RSK_MQ_FIRE_TEMPLE].Hide(); - mOptions[RSK_MQ_WATER_TEMPLE].Hide(); - mOptions[RSK_MQ_SPIRIT_TEMPLE].Hide(); - mOptions[RSK_MQ_SHADOW_TEMPLE].Hide(); - mOptions[RSK_MQ_BOTTOM_OF_THE_WELL].Hide(); - mOptions[RSK_MQ_ICE_CAVERN].Hide(); - mOptions[RSK_MQ_GTG].Hide(); - mOptions[RSK_MQ_GANONS_CASTLE].Hide(); - } 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)) { - // 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: - mOptions[RSK_MQ_DUNGEON_RANDOM].AddFlag(IMFLAG_SEPARATOR_BOTTOM); - mOptions[RSK_MQ_DUNGEON_COUNT].Hide(); - mOptions[RSK_MQ_DUNGEON_SET].Hide(); - break; - // If Set Number, remove the separator and show both the count slider and the - // individual dungeon selection toggle. - case RO_MQ_DUNGEONS_SET_NUMBER: - mOptions[RSK_MQ_DUNGEON_RANDOM].RemoveFlag(IMFLAG_SEPARATOR_BOTTOM); - mOptions[RSK_MQ_DUNGEON_COUNT].Unhide(); - mOptions[RSK_MQ_DUNGEON_SET].Unhide(); - break; - // else if random number or selection only, remove the separator and only show - // the individual dungeon selection toggle. - case RO_MQ_DUNGEONS_RANDOM_NUMBER: - 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 || - 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(); - mOptions[RSK_MQ_DODONGOS_CAVERN].Unhide(); - mOptions[RSK_MQ_JABU_JABU].Unhide(); - mOptions[RSK_MQ_FOREST_TEMPLE].Unhide(); - mOptions[RSK_MQ_FIRE_TEMPLE].Unhide(); - mOptions[RSK_MQ_WATER_TEMPLE].Unhide(); - mOptions[RSK_MQ_SPIRIT_TEMPLE].Unhide(); - mOptions[RSK_MQ_SHADOW_TEMPLE].Unhide(); - mOptions[RSK_MQ_BOTTOM_OF_THE_WELL].Unhide(); - mOptions[RSK_MQ_ICE_CAVERN].Unhide(); - mOptions[RSK_MQ_GTG].Unhide(); - mOptions[RSK_MQ_GANONS_CASTLE].Unhide(); - } else { - // If those are not shown, add a separator after the Set Dungeons checkbox. - mOptions[RSK_MQ_DUNGEON_SET].AddFlag(IMFLAG_SEPARATOR_BOTTOM); - mOptions[RSK_MQ_DEKU_TREE].Hide(); - mOptions[RSK_MQ_DODONGOS_CAVERN].Hide(); - mOptions[RSK_MQ_JABU_JABU].Hide(); - mOptions[RSK_MQ_FOREST_TEMPLE].Hide(); - mOptions[RSK_MQ_FIRE_TEMPLE].Hide(); - mOptions[RSK_MQ_WATER_TEMPLE].Hide(); - mOptions[RSK_MQ_SPIRIT_TEMPLE].Hide(); - mOptions[RSK_MQ_SHADOW_TEMPLE].Hide(); - mOptions[RSK_MQ_BOTTOM_OF_THE_WELL].Hide(); - mOptions[RSK_MQ_ICE_CAVERN].Hide(); - mOptions[RSK_MQ_GTG].Hide(); - mOptions[RSK_MQ_GANONS_CASTLE].Hide(); - } - } - - bool dungeonShuffle = - CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleDungeonsEntrances"), RO_DUNGEON_ENTRANCE_SHUFFLE_OFF); - bool bossShuffle = - CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleBossEntrances"), RO_BOSS_ROOM_ENTRANCE_SHUFFLE_OFF); - bool overworldShuffle = CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleOverworldEntrances"), RO_GENERIC_OFF); - bool interiorShuffle = CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleInteriorsEntrances"), RO_GENERIC_OFF); - bool grottoShuffle = CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleGrottosEntrances"), RO_GENERIC_OFF); - - // Hide Mixed Entrances option if 1 or no applicable entrance shuffles are visible - if (dungeonShuffle + bossShuffle + overworldShuffle + interiorShuffle + grottoShuffle <= 1) { - mOptions[RSK_MIXED_ENTRANCE_POOLS].Hide(); - } else { - mOptions[RSK_MIXED_ENTRANCE_POOLS].Unhide(); - } - // Show mixed entrance pool options if mixed entrance pools are enabled, but only the ones that aren't off - if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("MixedEntrances"), RO_GENERIC_OFF) == RO_GENERIC_OFF || - mOptions[RSK_MIXED_ENTRANCE_POOLS].IsHidden()) { - mOptions[RSK_MIXED_ENTRANCE_POOLS].AddFlag(IMFLAG_SEPARATOR_BOTTOM); - mOptions[RSK_MIX_DUNGEON_ENTRANCES].Hide(); - mOptions[RSK_MIX_BOSS_ENTRANCES].Hide(); - mOptions[RSK_MIX_OVERWORLD_ENTRANCES].Hide(); - mOptions[RSK_MIX_INTERIOR_ENTRANCES].Hide(); - mOptions[RSK_MIX_THIEVES_HIDEOUT_ENTRANCES].Hide(); - mOptions[RSK_MIX_GROTTO_ENTRANCES].Hide(); - } else { - mOptions[RSK_MIXED_ENTRANCE_POOLS].RemoveFlag(IMFLAG_SEPARATOR_BOTTOM); - mOptions[RSK_MIX_DUNGEON_ENTRANCES].RemoveFlag(IMFLAG_SEPARATOR_BOTTOM); - mOptions[RSK_MIX_BOSS_ENTRANCES].RemoveFlag(IMFLAG_SEPARATOR_BOTTOM); - mOptions[RSK_MIX_OVERWORLD_ENTRANCES].RemoveFlag(IMFLAG_SEPARATOR_BOTTOM); - mOptions[RSK_MIX_INTERIOR_ENTRANCES].RemoveFlag(IMFLAG_SEPARATOR_BOTTOM); - mOptions[RSK_MIX_THIEVES_HIDEOUT_ENTRANCES].RemoveFlag(IMFLAG_SEPARATOR_BOTTOM); - mOptions[RSK_MIX_GROTTO_ENTRANCES].RemoveFlag(IMFLAG_SEPARATOR_BOTTOM); - RandomizerSettingKey lastKey = RSK_MIXED_ENTRANCE_POOLS; - if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleDungeonsEntrances"), RO_DUNGEON_ENTRANCE_SHUFFLE_OFF) == - RO_DUNGEON_ENTRANCE_SHUFFLE_OFF) { - mOptions[RSK_MIX_DUNGEON_ENTRANCES].Hide(); - } else { - mOptions[RSK_MIX_DUNGEON_ENTRANCES].Unhide(); - lastKey = RSK_MIX_DUNGEON_ENTRANCES; - } - if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleBossEntrances"), RO_BOSS_ROOM_ENTRANCE_SHUFFLE_OFF) == - RO_BOSS_ROOM_ENTRANCE_SHUFFLE_OFF) { - mOptions[RSK_MIX_BOSS_ENTRANCES].Hide(); - } else { - mOptions[RSK_MIX_BOSS_ENTRANCES].Unhide(); - lastKey = RSK_MIX_BOSS_ENTRANCES; - } - if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleOverworldEntrances"), RO_GENERIC_OFF) == RO_GENERIC_OFF) { - mOptions[RSK_MIX_OVERWORLD_ENTRANCES].Hide(); - } else { - mOptions[RSK_MIX_OVERWORLD_ENTRANCES].Unhide(); - lastKey = RSK_MIX_OVERWORLD_ENTRANCES; - } - if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleInteriorsEntrances"), RO_GENERIC_OFF) == RO_GENERIC_OFF) { - mOptions[RSK_MIX_INTERIOR_ENTRANCES].Hide(); - } else { - mOptions[RSK_MIX_INTERIOR_ENTRANCES].Unhide(); - lastKey = RSK_MIX_INTERIOR_ENTRANCES; - } - if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleThievesHideoutEntrances"), RO_GENERIC_OFF) == - RO_GENERIC_OFF) { - mOptions[RSK_MIX_THIEVES_HIDEOUT_ENTRANCES].Hide(); - } else { - mOptions[RSK_MIX_THIEVES_HIDEOUT_ENTRANCES].Unhide(); - lastKey = RSK_MIX_THIEVES_HIDEOUT_ENTRANCES; - } - if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleGrottosEntrances"), RO_GENERIC_OFF) == RO_GENERIC_OFF) { - mOptions[RSK_MIX_GROTTO_ENTRANCES].Hide(); - } else { - mOptions[RSK_MIX_GROTTO_ENTRANCES].Unhide(); - lastKey = RSK_MIX_GROTTO_ENTRANCES; - } - mOptions[lastKey].AddFlag(IMFLAG_SEPARATOR_BOTTOM); - } - - if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleDekuStickBag"), 0)) { - mOptions[RSK_STARTING_STICKS].Disable("Disabled because Shuffle Deku Stick Bag is on."); - } else { - mOptions[RSK_STARTING_STICKS].Enable(); - } - if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleDekuNutBag"), 0)) { - mOptions[RSK_STARTING_NUTS].Disable("Disabled because Shuffle Deku Nut Bag is on."); - } else { - mOptions[RSK_STARTING_NUTS].Enable(); - } - - // Shuffle Weird Egg - Disabled when Skip Child Zelda is active - if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("SkipChildZelda"), RO_GENERIC_DONT_SKIP)) { - mOptions[RSK_SHUFFLE_WEIRD_EGG].Disable("This option is disabled because \"Skip Child Zelda\" is enabled."); - } else { - mOptions[RSK_SHUFFLE_WEIRD_EGG].Enable(); - } - bool isTycoon = CVarGetInteger(CVAR_RANDOMIZER_SETTING("IncludeTycoonWallet"), RO_GENERIC_OFF); - // Hide shopsanity prices if shopsanity is off or zero - switch (CVarGetInteger(CVAR_RANDOMIZER_SETTING("Shopsanity"), RO_SHOPSANITY_OFF)) { - case RO_SHOPSANITY_OFF: - mOptions[RSK_SHOPSANITY].AddFlag(IMFLAG_SEPARATOR_BOTTOM); - mOptions[RSK_SHOPSANITY_COUNT].Hide(); - mOptions[RSK_SHOPSANITY_COUNT].Hide(); - mOptions[RSK_SHOPSANITY_PRICES].Hide(); - mOptions[RSK_SHOPSANITY_PRICES_AFFORDABLE].Hide(); - mOptions[RSK_SHOPSANITY_PRICES_FIXED_PRICE].Hide(); - mOptions[RSK_SHOPSANITY_PRICES_RANGE_1].Hide(); - mOptions[RSK_SHOPSANITY_PRICES_RANGE_2].Hide(); - mOptions[RSK_SHOPSANITY_PRICES_NO_WALLET_WEIGHT].Hide(); - mOptions[RSK_SHOPSANITY_PRICES_CHILD_WALLET_WEIGHT].Hide(); - mOptions[RSK_SHOPSANITY_PRICES_ADULT_WALLET_WEIGHT].Hide(); - mOptions[RSK_SHOPSANITY_PRICES_GIANT_WALLET_WEIGHT].Hide(); - mOptions[RSK_SHOPSANITY_PRICES_TYCOON_WALLET_WEIGHT].Hide(); - break; - case RO_SHOPSANITY_SPECIFIC_COUNT: - mOptions[RSK_SHOPSANITY_COUNT].Unhide(); - HandleShopsanityPriceUI(); - break; - case RO_SHOPSANITY_RANDOM: - mOptions[RSK_SHOPSANITY_COUNT].Hide(); - HandleShopsanityPriceUI(); - break; - } - switch (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleScrubs"), RO_SCRUBS_OFF)) { - case RO_SCRUBS_OFF: - mOptions[RSK_SHUFFLE_SCRUBS].AddFlag(IMFLAG_SEPARATOR_BOTTOM); - mOptions[RSK_SCRUBS_PRICES].Hide(); - mOptions[RSK_SCRUBS_PRICES_AFFORDABLE].Hide(); - mOptions[RSK_SCRUBS_PRICES_FIXED_PRICE].Hide(); - mOptions[RSK_SCRUBS_PRICES_RANGE_1].Hide(); - mOptions[RSK_SCRUBS_PRICES_RANGE_2].Hide(); - mOptions[RSK_SCRUBS_PRICES_NO_WALLET_WEIGHT].Hide(); - mOptions[RSK_SCRUBS_PRICES_CHILD_WALLET_WEIGHT].Hide(); - mOptions[RSK_SCRUBS_PRICES_ADULT_WALLET_WEIGHT].Hide(); - mOptions[RSK_SCRUBS_PRICES_GIANT_WALLET_WEIGHT].Hide(); - mOptions[RSK_SCRUBS_PRICES_TYCOON_WALLET_WEIGHT].Hide(); - break; - default: - mOptions[RSK_SHUFFLE_SCRUBS].RemoveFlag(IMFLAG_SEPARATOR_BOTTOM); - mOptions[RSK_SCRUBS_PRICES].Unhide(); - switch (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ScrubsPrices"), RO_PRICE_VANILLA)) { - case RO_PRICE_FIXED: - mOptions[RSK_SCRUBS_PRICES_FIXED_PRICE].Unhide(); - mOptions[RSK_SCRUBS_PRICES_RANGE_1].Hide(); - mOptions[RSK_SCRUBS_PRICES_RANGE_2].Hide(); - mOptions[RSK_SCRUBS_PRICES_NO_WALLET_WEIGHT].Hide(); - mOptions[RSK_SCRUBS_PRICES_CHILD_WALLET_WEIGHT].Hide(); - mOptions[RSK_SCRUBS_PRICES_ADULT_WALLET_WEIGHT].Hide(); - mOptions[RSK_SCRUBS_PRICES_GIANT_WALLET_WEIGHT].Hide(); - mOptions[RSK_SCRUBS_PRICES_TYCOON_WALLET_WEIGHT].Hide(); - if (isTycoon ? mOptions[RSK_SCRUBS_PRICES_FIXED_PRICE].GetOptionCount() == 501 - : mOptions[RSK_SCRUBS_PRICES_FIXED_PRICE].GetOptionCount() == 1000) { - mOptions[RSK_SCRUBS_PRICES_FIXED_PRICE].ChangeOptions(isTycoon ? NumOpts(0, 999) - : NumOpts(0, 500)); - } - mOptions[RSK_SCRUBS_PRICES_AFFORDABLE].Hide(); - break; - case RO_PRICE_RANGE: - mOptions[RSK_SCRUBS_PRICES_FIXED_PRICE].Hide(); - mOptions[RSK_SCRUBS_PRICES_RANGE_1].Unhide(); - mOptions[RSK_SCRUBS_PRICES_RANGE_2].Unhide(); - mOptions[RSK_SCRUBS_PRICES_NO_WALLET_WEIGHT].Hide(); - mOptions[RSK_SCRUBS_PRICES_CHILD_WALLET_WEIGHT].Hide(); - mOptions[RSK_SCRUBS_PRICES_ADULT_WALLET_WEIGHT].Hide(); - mOptions[RSK_SCRUBS_PRICES_GIANT_WALLET_WEIGHT].Hide(); - mOptions[RSK_SCRUBS_PRICES_TYCOON_WALLET_WEIGHT].Hide(); - if (isTycoon ? mOptions[RSK_SCRUBS_PRICES_RANGE_1].GetOptionCount() == 101 - : mOptions[RSK_SCRUBS_PRICES_RANGE_1].GetOptionCount() == 200) { - mOptions[RSK_SCRUBS_PRICES_RANGE_1].ChangeOptions(isTycoon ? NumOpts(0, 995, 5) - : NumOpts(0, 500, 5)); - mOptions[RSK_SCRUBS_PRICES_RANGE_2].ChangeOptions(isTycoon ? NumOpts(0, 995, 5) - : NumOpts(0, 500, 5)); - } - mOptions[RSK_SCRUBS_PRICES_AFFORDABLE].Unhide(); - break; - case RO_PRICE_SET_BY_WALLET: - mOptions[RSK_SCRUBS_PRICES_FIXED_PRICE].Hide(); - mOptions[RSK_SCRUBS_PRICES_RANGE_1].Hide(); - mOptions[RSK_SCRUBS_PRICES_RANGE_2].Hide(); - mOptions[RSK_SCRUBS_PRICES_NO_WALLET_WEIGHT].Unhide(); - mOptions[RSK_SCRUBS_PRICES_CHILD_WALLET_WEIGHT].Unhide(); - mOptions[RSK_SCRUBS_PRICES_ADULT_WALLET_WEIGHT].Unhide(); - mOptions[RSK_SCRUBS_PRICES_GIANT_WALLET_WEIGHT].Unhide(); - if (isTycoon) { - mOptions[RSK_SCRUBS_PRICES_TYCOON_WALLET_WEIGHT].Unhide(); - } else { - mOptions[RSK_SCRUBS_PRICES_TYCOON_WALLET_WEIGHT].Hide(); - } - mOptions[RSK_SCRUBS_PRICES_AFFORDABLE].Unhide(); - break; - default: - mOptions[RSK_SCRUBS_PRICES_FIXED_PRICE].Hide(); - mOptions[RSK_SCRUBS_PRICES_RANGE_1].Hide(); - mOptions[RSK_SCRUBS_PRICES_RANGE_2].Hide(); - mOptions[RSK_SCRUBS_PRICES_NO_WALLET_WEIGHT].Hide(); - mOptions[RSK_SCRUBS_PRICES_CHILD_WALLET_WEIGHT].Hide(); - mOptions[RSK_SCRUBS_PRICES_ADULT_WALLET_WEIGHT].Hide(); - mOptions[RSK_SCRUBS_PRICES_GIANT_WALLET_WEIGHT].Hide(); - mOptions[RSK_SCRUBS_PRICES_TYCOON_WALLET_WEIGHT].Hide(); - mOptions[RSK_SCRUBS_PRICES_AFFORDABLE].Unhide(); - break; - } - break; - } - switch (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleMerchants"), RO_SHUFFLE_MERCHANTS_OFF)) { - case RO_SHUFFLE_MERCHANTS_OFF: - mOptions[RSK_SHUFFLE_MERCHANTS].AddFlag(IMFLAG_SEPARATOR_BOTTOM); - mOptions[RSK_MERCHANT_PRICES].Hide(); - mOptions[RSK_MERCHANT_PRICES_AFFORDABLE].Hide(); - mOptions[RSK_MERCHANT_PRICES_FIXED_PRICE].Hide(); - mOptions[RSK_MERCHANT_PRICES_RANGE_1].Hide(); - mOptions[RSK_MERCHANT_PRICES_RANGE_2].Hide(); - mOptions[RSK_MERCHANT_PRICES_NO_WALLET_WEIGHT].Hide(); - mOptions[RSK_MERCHANT_PRICES_CHILD_WALLET_WEIGHT].Hide(); - mOptions[RSK_MERCHANT_PRICES_ADULT_WALLET_WEIGHT].Hide(); - mOptions[RSK_MERCHANT_PRICES_GIANT_WALLET_WEIGHT].Hide(); - mOptions[RSK_MERCHANT_PRICES_TYCOON_WALLET_WEIGHT].Hide(); - break; - default: - mOptions[RSK_SHUFFLE_MERCHANTS].RemoveFlag(IMFLAG_SEPARATOR_BOTTOM); - mOptions[RSK_MERCHANT_PRICES].Unhide(); - switch (CVarGetInteger(CVAR_RANDOMIZER_SETTING("MerchantPrices"), RO_PRICE_VANILLA)) { - case RO_PRICE_FIXED: - mOptions[RSK_MERCHANT_PRICES_FIXED_PRICE].Unhide(); - mOptions[RSK_MERCHANT_PRICES_RANGE_1].Hide(); - mOptions[RSK_MERCHANT_PRICES_RANGE_2].Hide(); - mOptions[RSK_MERCHANT_PRICES_NO_WALLET_WEIGHT].Hide(); - mOptions[RSK_MERCHANT_PRICES_CHILD_WALLET_WEIGHT].Hide(); - mOptions[RSK_MERCHANT_PRICES_ADULT_WALLET_WEIGHT].Hide(); - mOptions[RSK_MERCHANT_PRICES_GIANT_WALLET_WEIGHT].Hide(); - mOptions[RSK_MERCHANT_PRICES_TYCOON_WALLET_WEIGHT].Hide(); - if (isTycoon ? mOptions[RSK_MERCHANT_PRICES_FIXED_PRICE].GetOptionCount() == 501 - : mOptions[RSK_MERCHANT_PRICES_FIXED_PRICE].GetOptionCount() == 1000) { - mOptions[RSK_MERCHANT_PRICES_FIXED_PRICE].ChangeOptions(isTycoon ? NumOpts(0, 999) - : NumOpts(0, 500)); - } - mOptions[RSK_MERCHANT_PRICES_AFFORDABLE].Hide(); - break; - case RO_PRICE_RANGE: - mOptions[RSK_MERCHANT_PRICES_FIXED_PRICE].Hide(); - mOptions[RSK_MERCHANT_PRICES_RANGE_1].Unhide(); - mOptions[RSK_MERCHANT_PRICES_RANGE_2].Unhide(); - mOptions[RSK_MERCHANT_PRICES_NO_WALLET_WEIGHT].Hide(); - mOptions[RSK_MERCHANT_PRICES_CHILD_WALLET_WEIGHT].Hide(); - mOptions[RSK_MERCHANT_PRICES_ADULT_WALLET_WEIGHT].Hide(); - mOptions[RSK_MERCHANT_PRICES_GIANT_WALLET_WEIGHT].Hide(); - mOptions[RSK_MERCHANT_PRICES_TYCOON_WALLET_WEIGHT].Hide(); - if (isTycoon ? mOptions[RSK_MERCHANT_PRICES_RANGE_1].GetOptionCount() == 101 - : mOptions[RSK_MERCHANT_PRICES_RANGE_1].GetOptionCount() == 200) { - mOptions[RSK_MERCHANT_PRICES_RANGE_1].ChangeOptions(isTycoon ? NumOpts(0, 995, 5) - : NumOpts(0, 500, 5)); - mOptions[RSK_MERCHANT_PRICES_RANGE_2].ChangeOptions(isTycoon ? NumOpts(0, 995, 5) - : NumOpts(0, 500, 5)); - } - mOptions[RSK_MERCHANT_PRICES_AFFORDABLE].Unhide(); - break; - case RO_PRICE_SET_BY_WALLET: - mOptions[RSK_MERCHANT_PRICES_FIXED_PRICE].Hide(); - mOptions[RSK_MERCHANT_PRICES_RANGE_1].Hide(); - mOptions[RSK_MERCHANT_PRICES_RANGE_2].Hide(); - mOptions[RSK_MERCHANT_PRICES_NO_WALLET_WEIGHT].Unhide(); - mOptions[RSK_MERCHANT_PRICES_CHILD_WALLET_WEIGHT].Unhide(); - mOptions[RSK_MERCHANT_PRICES_ADULT_WALLET_WEIGHT].Unhide(); - mOptions[RSK_MERCHANT_PRICES_GIANT_WALLET_WEIGHT].Unhide(); - if (isTycoon) { - mOptions[RSK_MERCHANT_PRICES_TYCOON_WALLET_WEIGHT].Unhide(); - } else { - mOptions[RSK_MERCHANT_PRICES_TYCOON_WALLET_WEIGHT].Hide(); - } - mOptions[RSK_MERCHANT_PRICES_AFFORDABLE].Unhide(); - break; - default: - mOptions[RSK_MERCHANT_PRICES_FIXED_PRICE].Hide(); - mOptions[RSK_MERCHANT_PRICES_RANGE_1].Hide(); - mOptions[RSK_MERCHANT_PRICES_RANGE_2].Hide(); - mOptions[RSK_MERCHANT_PRICES_NO_WALLET_WEIGHT].Hide(); - mOptions[RSK_MERCHANT_PRICES_CHILD_WALLET_WEIGHT].Hide(); - mOptions[RSK_MERCHANT_PRICES_ADULT_WALLET_WEIGHT].Hide(); - mOptions[RSK_MERCHANT_PRICES_GIANT_WALLET_WEIGHT].Hide(); - mOptions[RSK_MERCHANT_PRICES_TYCOON_WALLET_WEIGHT].Hide(); - mOptions[RSK_MERCHANT_PRICES_AFFORDABLE].Unhide(); - break; - } - break; - } - // Hide fishing pond settings if we aren't shuffling the fishing pond - switch (CVarGetInteger(CVAR_RANDOMIZER_SETTING("Fishsanity"), RO_FISHSANITY_OFF)) { - case RO_FISHSANITY_POND: - case RO_FISHSANITY_BOTH: - mOptions[RSK_FISHSANITY].RemoveFlag(IMFLAG_SEPARATOR_BOTTOM); - mOptions[RSK_FISHSANITY_POND_COUNT].Unhide(); - mOptions[RSK_FISHSANITY_AGE_SPLIT].Unhide(); - break; - default: - mOptions[RSK_FISHSANITY].AddFlag(IMFLAG_SEPARATOR_BOTTOM); - mOptions[RSK_FISHSANITY_POND_COUNT].Hide(); - mOptions[RSK_FISHSANITY_AGE_SPLIT].Hide(); - } - // Disable fishing pole hint if the fishing pole is not shuffled - if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleFishingPole"), RO_GENERIC_OFF)) { - mOptions[RSK_FISHING_POLE_HINT].Enable(); - } else { - mOptions[RSK_FISHING_POLE_HINT].Disable("This option is disabled since the fishing pole is not shuffled."); - } - // Shuffle 100 GS Reward - Force-Enabled if Ganon's Boss Key is on the 100 GS Reward - if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleGanonBossKey"), RO_GANON_BOSS_KEY_VANILLA) == - RO_GANON_BOSS_KEY_KAK_TOKENS) { - mOptions[RSK_SHUFFLE_100_GS_REWARD].Disable( - "This option is force-enabled because \"Ganon's Boss Key\" is set to \"100 GS Reward\"."); - } else { - mOptions[RSK_SHUFFLE_100_GS_REWARD].Enable(); - } - // Default state for Keyrings GUI - mOptions[RSK_KEYRINGS].AddFlag(IMFLAG_SEPARATOR_BOTTOM); - mOptions[RSK_KEYRINGS_RANDOM_COUNT].Hide(); - mOptions[RSK_KEYRINGS_GERUDO_FORTRESS].Hide(); - mOptions[RSK_KEYRINGS_FOREST_TEMPLE].Hide(); - mOptions[RSK_KEYRINGS_FIRE_TEMPLE].Hide(); - mOptions[RSK_KEYRINGS_WATER_TEMPLE].Hide(); - mOptions[RSK_KEYRINGS_SPIRIT_TEMPLE].Hide(); - mOptions[RSK_KEYRINGS_SHADOW_TEMPLE].Hide(); - mOptions[RSK_KEYRINGS_BOTTOM_OF_THE_WELL].Hide(); - mOptions[RSK_KEYRINGS_GTG].Hide(); - mOptions[RSK_KEYRINGS_GANONS_CASTLE].Hide(); - switch (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleKeyRings"), RO_KEYRINGS_OFF)) { - case RO_KEYRINGS_COUNT: - // Show count slider. - mOptions[RSK_KEYRINGS].RemoveFlag(IMFLAG_SEPARATOR_BOTTOM); - mOptions[RSK_KEYRINGS_RANDOM_COUNT].Unhide(); - break; - case RO_KEYRINGS_SELECTION: - // Show checkboxes for each dungeon with keys. - mOptions[RSK_KEYRINGS].RemoveFlag(IMFLAG_SEPARATOR_BOTTOM); - mOptions[RSK_KEYRINGS_GERUDO_FORTRESS].Unhide(); - mOptions[RSK_KEYRINGS_FOREST_TEMPLE].Unhide(); - mOptions[RSK_KEYRINGS_FIRE_TEMPLE].Unhide(); - mOptions[RSK_KEYRINGS_WATER_TEMPLE].Unhide(); - mOptions[RSK_KEYRINGS_SPIRIT_TEMPLE].Unhide(); - mOptions[RSK_KEYRINGS_SHADOW_TEMPLE].Unhide(); - mOptions[RSK_KEYRINGS_BOTTOM_OF_THE_WELL].Unhide(); - mOptions[RSK_KEYRINGS_GTG].Unhide(); - mOptions[RSK_KEYRINGS_GANONS_CASTLE].Unhide(); - default: - break; - } - const uint8_t maxKeyringCount = - (CVarGetInteger(CVAR_RANDOMIZER_SETTING("FortressCarpenters"), RO_GF_CARPENTERS_NORMAL) == - RO_GF_CARPENTERS_NORMAL && - CVarGetInteger(CVAR_RANDOMIZER_SETTING("GerudoKeys"), RO_GERUDO_KEYS_VANILLA) != RO_GERUDO_KEYS_VANILLA) - ? 9 - : 8; - if (mOptions[RSK_KEYRINGS_RANDOM_COUNT].GetOptionCount() != maxKeyringCount + 1) { - mOptions[RSK_KEYRINGS_RANDOM_COUNT].ChangeOptions(NumOpts(0, maxKeyringCount)); - } - if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("FortressCarpenters"), RO_GF_CARPENTERS_NORMAL) != - RO_GF_CARPENTERS_NORMAL || - CVarGetInteger(CVAR_RANDOMIZER_SETTING("GerudoKeys"), RO_GERUDO_KEYS_VANILLA) == RO_GERUDO_KEYS_VANILLA) { - mOptions[RSK_KEYRINGS_GERUDO_FORTRESS].Disable( - "Disabled because the currently selected Gerudo Fortress Carpenters\n" - "setting and/or Gerudo Fortress Keys setting is incompatible with\n" - "having a Gerudo Fortress Keyring."); - } else { - mOptions[RSK_KEYRINGS_GERUDO_FORTRESS].Enable(); - } - if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("TriforceHunt"), RO_GENERIC_OFF)) { - mOptions[RSK_GANONS_BOSS_KEY].Disable( - "This option is disabled because Triforce Hunt is enabled." - "Ganon's Boss key\nwill instead be given to you after Triforce Hunt completion."); - } else { - mOptions[RSK_GANONS_BOSS_KEY].Enable(); - } - mOptions[RSK_GANONS_BOSS_KEY].RemoveFlag(IMFLAG_SEPARATOR_BOTTOM); - mOptions[RSK_LACS_OPTIONS].Hide(); - mOptions[RSK_LACS_STONE_COUNT].Hide(); - mOptions[RSK_LACS_MEDALLION_COUNT].Hide(); - mOptions[RSK_LACS_REWARD_COUNT].Hide(); - mOptions[RSK_LACS_DUNGEON_COUNT].Hide(); - mOptions[RSK_LACS_TOKEN_COUNT].Hide(); - const uint8_t lacsOpts = CVarGetInteger(CVAR_RANDOMIZER_SETTING("LacsRewardOptions"), RO_LACS_STANDARD_REWARD); - switch (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleGanonBossKey"), RO_GANON_BOSS_KEY_VANILLA)) { - case RO_GANON_BOSS_KEY_LACS_STONES: - mOptions[RSK_LACS_OPTIONS].Unhide(); - mOptions[RSK_LACS_STONE_COUNT].Unhide(); - if (lacsOpts == RO_LACS_GREG_REWARD) { - if (mOptions[RSK_LACS_STONE_COUNT].GetOptionCount() == 4) { - mOptions[RSK_LACS_STONE_COUNT].ChangeOptions(NumOpts(0, 4)); - } - } else { - if (mOptions[RSK_LACS_STONE_COUNT].GetOptionCount() == 5) { - mOptions[RSK_LACS_STONE_COUNT].ChangeOptions(NumOpts(0, 3)); - } - } - break; - case RO_GANON_BOSS_KEY_LACS_MEDALLIONS: - mOptions[RSK_LACS_OPTIONS].Unhide(); - mOptions[RSK_LACS_MEDALLION_COUNT].Unhide(); - if (lacsOpts == RO_LACS_GREG_REWARD) { - if (mOptions[RSK_LACS_MEDALLION_COUNT].GetOptionCount() == 7) { - mOptions[RSK_LACS_MEDALLION_COUNT].ChangeOptions(NumOpts(0, 7)); - } - } else { - if (mOptions[RSK_LACS_MEDALLION_COUNT].GetOptionCount() == 8) { - mOptions[RSK_LACS_MEDALLION_COUNT].ChangeOptions(NumOpts(0, 6)); - } - } - break; - case RO_GANON_BOSS_KEY_LACS_REWARDS: - mOptions[RSK_LACS_OPTIONS].Unhide(); - mOptions[RSK_LACS_REWARD_COUNT].Unhide(); - if (lacsOpts == RO_LACS_GREG_REWARD) { - if (mOptions[RSK_LACS_REWARD_COUNT].GetOptionCount() == 10) { - mOptions[RSK_LACS_REWARD_COUNT].ChangeOptions(NumOpts(0, 10)); - } - } else { - if (mOptions[RSK_LACS_REWARD_COUNT].GetOptionCount() == 11) { - mOptions[RSK_LACS_REWARD_COUNT].ChangeOptions(NumOpts(0, 9)); - } - } - break; - case RO_GANON_BOSS_KEY_LACS_DUNGEONS: - mOptions[RSK_LACS_OPTIONS].Unhide(); - mOptions[RSK_LACS_DUNGEON_COUNT].Unhide(); - if (lacsOpts == RO_LACS_GREG_REWARD) { - if (mOptions[RSK_LACS_DUNGEON_COUNT].GetOptionCount() == 9) { - mOptions[RSK_LACS_DUNGEON_COUNT].ChangeOptions(NumOpts(0, 9)); - } - } else { - if (mOptions[RSK_LACS_DUNGEON_COUNT].GetOptionCount() == 10) { - mOptions[RSK_LACS_DUNGEON_COUNT].ChangeOptions(NumOpts(0, 8)); - } - } - break; - case RO_GANON_BOSS_KEY_LACS_TOKENS: - mOptions[RSK_LACS_TOKEN_COUNT].Unhide(); - break; - default: - mOptions[RSK_GANONS_BOSS_KEY].AddFlag(IMFLAG_SEPARATOR_BOTTOM); - break; - } - // Skip Child Stealth - Disabled when Skip Child Zelda is active - if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("SkipChildZelda"), RO_GENERIC_DONT_SKIP)) { - mOptions[RSK_SKIP_CHILD_STEALTH].Disable("This option is disabled because \"Skip Child Zelda\" is enabled."); - } else { - mOptions[RSK_SKIP_CHILD_STEALTH].Enable(); - } - if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("GossipStoneHints"), RO_GOSSIP_STONES_NEED_NOTHING) == - RO_GOSSIP_STONES_NONE) { - mOptions[RSK_HINT_CLARITY].Hide(); - mOptions[RSK_HINT_DISTRIBUTION].Hide(); - } else { - mOptions[RSK_HINT_CLARITY].Unhide(); - mOptions[RSK_HINT_DISTRIBUTION].Unhide(); - } - - if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleWarpSongs"), RO_GENERIC_ON)) { - mOptions[RSK_WARP_SONG_HINTS].Enable(); - } else { - mOptions[RSK_WARP_SONG_HINTS].Disable("This option is disabled since warp song locations not shuffled."); - } - - if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleCows"), RO_GENERIC_OFF)) { - mOptions[RSK_MALON_HINT].Enable(); - } else { - mOptions[RSK_MALON_HINT].Disable("Malon's hint points to a cow, so requires cows to be shuffled."); - } - - if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("Shuffle100GSReward"), RO_GENERIC_OFF)) { - mOptions[RSK_KAK_100_SKULLS_HINT].Enable(); - } else { - mOptions[RSK_KAK_100_SKULLS_HINT].Disable("There is no point to hinting 100 skulls if it is not shuffled."); - } - - if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("Fishsanity"), RO_FISHSANITY_OFF) == RO_FISHSANITY_HYRULE_LOACH) { - mOptions[RSK_LOACH_HINT].Enable(); - } else { - mOptions[RSK_LOACH_HINT].Disable( - "Loach hint is only avaliable with \"Fishsanity\" set to \"Shuffle only Hyrule Loach\"\nas that's the only " - "setting where you present the loach to the fishing pond owner."); - } - - if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("BigPoeTargetCount"), 10) == 0) { - mOptions[RSK_BIG_POES_HINT].Disable("Poe Collector will just give you the item instead with 0 big poes."); - } else { - mOptions[RSK_BIG_POES_HINT].Enable(); - } - if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleBeehives"), RO_GENERIC_OFF)) { - mOptions[RSK_SLINGBOW_BREAK_BEEHIVES].Enable(); - } else { - mOptions[RSK_SLINGBOW_BREAK_BEEHIVES].Disable( - "This option is disabled because Shuffle Beehives is not enabled."); +void Settings::UpdateAllOptions() { + for (auto& option : mOptions) { + option.RunCallback(); } } diff --git a/soh/soh/Enhancements/randomizer/settings.h b/soh/soh/Enhancements/randomizer/settings.h index ed214471a..11b87029f 100644 --- a/soh/soh/Enhancements/randomizer/settings.h +++ b/soh/soh/Enhancements/randomizer/settings.h @@ -1,6 +1,6 @@ #pragma once -#include "context.h" +#include "SeedContext.h" #include "option.h" #include "randomizerTypes.h" #include "3drando/spoiler_log.hpp" @@ -20,6 +20,16 @@ class Settings { */ void HandleShopsanityPriceUI(); + /** + * @brief Hides or Unhides the UI of Mixed Entrance Pools + */ + void HandleMixedEntrancePoolsUI(); + + /** + * @brief UI Callback for handling UI state of Starting Age shuffle. + */ + void HandleStartingAgeUI(); + /** * @brief Creates the `Option` and `OptionGroup` objects. This happens after construction because certain * other events in the codebase need to happen before all of the `Option`s can be created. @@ -97,16 +107,11 @@ class Settings { const OptionGroup& GetOptionGroup(RandomizerSettingGroupKey key); /** - * @brief Updates various properties of options based on the value of other options. - * Used to update visibility, whether or not interaction is disabled, and what the - * actual option values are. Actually changing option values should be handled in - * `FinalizeSettings` - * - * For example, this function handles setting the maximum possible keyring count to 9 - * when Gerudo's Fortress options are set such that a keyring is possible for that - * dungeon. + * @brief Runs the Callback on every option, to ensure they are all + * hidden/unhidden and/or disabled/enabled properly after applying a + * preset or dropping a file. */ - void UpdateOptionProperties(); + void UpdateAllOptions(); /** * @brief Parse Options from a JSON file. diff --git a/soh/soh/Enhancements/randomizer/static_data.h b/soh/soh/Enhancements/randomizer/static_data.h index cd24667a0..b01d150ff 100644 --- a/soh/soh/Enhancements/randomizer/static_data.h +++ b/soh/soh/Enhancements/randomizer/static_data.h @@ -87,4 +87,4 @@ class StaticData { StaticData(); ~StaticData(); }; -} // namespace Rando \ No newline at end of file +} // namespace Rando diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index 9167d5e9c..a3e30bca4 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -427,7 +427,6 @@ void OTRGlobals::Initialize() { Rando::Settings::GetInstance()->AssignContext(gRandoContext); Rando::StaticData::InitItemTable(); // RANDOTODO make this not rely on context's logic so it can be initialised in // InitStaticData - Rando::Settings::GetInstance()->CreateOptions(); gRandomizer = std::make_shared(); hasMasterQuest = hasOriginal = false; @@ -2853,8 +2852,6 @@ bool SoH_HandleConfigDrop(char* filePath) { } } - Rando::Settings::GetInstance()->UpdateOptionProperties(); - auto gui = Ship::Context::GetInstance()->GetWindow()->GetGui(); gui->GetGuiWindow("Console")->Hide(); gui->GetGuiWindow("Actor Viewer")->Hide(); @@ -2866,6 +2863,7 @@ bool SoH_HandleConfigDrop(char* filePath) { Ship::Context::GetInstance()->GetWindow()->GetGui()->GetGuiWindow("Console")) ->ClearBindings(); + Rando::Settings::GetInstance()->UpdateAllOptions(); gui->SaveConsoleVariablesNextFrame(); ShipInit::Init("*"); diff --git a/soh/soh/SaveManager.cpp b/soh/soh/SaveManager.cpp index c945b491e..5059deade 100644 --- a/soh/soh/SaveManager.cpp +++ b/soh/soh/SaveManager.cpp @@ -1,7 +1,7 @@ #include "SaveManager.h" #include "OTRGlobals.h" #include "Enhancements/game-interactor/GameInteractor.h" -#include "Enhancements/randomizer/context.h" +#include "Enhancements/randomizer/SeedContext.h" #include "Enhancements/randomizer/entrance.h" #include "Enhancements/randomizer/dungeon.h" #include "Enhancements/randomizer/trial.h" diff --git a/soh/soh/SohGui/SohGui.cpp b/soh/soh/SohGui/SohGui.cpp index 172f923e4..c4d1d8dba 100644 --- a/soh/soh/SohGui/SohGui.cpp +++ b/soh/soh/SohGui/SohGui.cpp @@ -95,7 +95,6 @@ std::shared_ptr mItemTrackerSettingsWindow; std::shared_ptr mItemTrackerWindow; std::shared_ptr mTimeSplitWindow; std::shared_ptr mPlandomizerWindow; -std::shared_ptr mRandomizerSettingsWindow; std::shared_ptr mModalWindow; std::shared_ptr mNotificationWindow; std::shared_ptr mTimeDisplayWindow; @@ -191,9 +190,6 @@ void SetupGuiElements() { mItemTrackerSettingsWindow = std::make_shared(CVAR_WINDOW("ItemTrackerSettings"), "Item Tracker Settings", ImVec2(733, 472)); gui->AddGuiWindow(mItemTrackerSettingsWindow); - mRandomizerSettingsWindow = std::make_shared(CVAR_WINDOW("RandomizerSettings"), - "Randomizer Settings", ImVec2(920, 600)); - gui->AddGuiWindow(mRandomizerSettingsWindow); mTimeSplitWindow = std::make_shared(CVAR_WINDOW("TimeSplits"), "Time Splits", ImVec2(450, 660)); gui->AddGuiWindow(mTimeSplitWindow); mPlandomizerWindow = @@ -217,7 +213,6 @@ void Destroy() { mNotificationWindow = nullptr; mModalWindow = nullptr; - mRandomizerSettingsWindow = nullptr; mItemTrackerWindow = nullptr; mItemTrackerSettingsWindow = nullptr; mEntranceTrackerWindow = nullptr; @@ -253,6 +248,8 @@ void RegisterPopup(std::string title, std::string message, std::string button1, } void ShowRandomizerSettingsMenu() { - mRandomizerSettingsWindow->Show(); + CVarSetString(CVAR_SETTING("Menu.ActiveHeader"), "Randomizer"); + CVarSetString(CVAR_SETTING("Menu.RandomizerSidebarSection"), "General"); + mSohMenu->Show(); } } // namespace SohGui diff --git a/soh/soh/SohGui/SohGui.hpp b/soh/soh/SohGui/SohGui.hpp index d9b7bb644..3d218651b 100644 --- a/soh/soh/SohGui/SohGui.hpp +++ b/soh/soh/SohGui/SohGui.hpp @@ -26,7 +26,6 @@ #include "soh/Enhancements/randomizer/randomizer_check_tracker.h" #include "soh/Enhancements/randomizer/randomizer_entrance_tracker.h" #include "soh/Enhancements/randomizer/randomizer_item_tracker.h" -#include "soh/Enhancements/randomizer/randomizer_settings_window.h" #include "soh/Enhancements/timesplits/TimeSplits.h" #include "soh/Enhancements/randomizer/Plandomizer.h" #include "SohModals.h" diff --git a/soh/soh/SohGui/SohMenuBar.cpp b/soh/soh/SohGui/SohMenuBar.cpp index 3e5353a1f..ea5d522b0 100644 --- a/soh/soh/SohGui/SohMenuBar.cpp +++ b/soh/soh/SohGui/SohMenuBar.cpp @@ -34,7 +34,6 @@ #include "soh/Enhancements/randomizer/randomizer_check_tracker.h" #include "soh/Enhancements/randomizer/randomizer_entrance_tracker.h" #include "soh/Enhancements/randomizer/randomizer_item_tracker.h" -#include "soh/Enhancements/randomizer/randomizer_settings_window.h" #include "soh/Enhancements/enemyrandomizer.h" #include "soh/Enhancements/timesplits/TimeSplits.h" #include "soh/Enhancements/randomizer/Plandomizer.h" diff --git a/soh/soh/SohGui/SohMenuRandomizer.cpp b/soh/soh/SohGui/SohMenuRandomizer.cpp index ad688740a..59cd096c6 100644 --- a/soh/soh/SohGui/SohMenuRandomizer.cpp +++ b/soh/soh/SohGui/SohMenuRandomizer.cpp @@ -1,5 +1,8 @@ #include "SohMenu.h" +#include "soh/Enhancements/randomizer/randomizer.h" +#include "soh/Enhancements/randomizer/randomizerTypes.h" #include "soh/OTRGlobals.h" +#include "soh/SohGui/SohGui.hpp" namespace SohGui { @@ -12,23 +15,603 @@ static const std::unordered_map skipGetItemAnimationOption { SGIA_ALL, "All Items" }, }; +static bool locationsDirty = true; +static bool tricksDirty = true; +static char seedString[MAX_SEED_STRING_SIZE]; +static std::set excludedLocations; +static std::set enabledTricks; +static std::set enabledGlitches; + +void DrawLocationsMenu(WidgetInfo& info) { + auto ctx = OTRGlobals::Instance->gRandoContext; + static ImVec2 cellPadding(8.0f, 8.0f); + bool generating = CVarGetInteger(CVAR_GENERAL("RandoGenerating"), 0); + bool disableEditingRandoSettings = generating || CVarGetInteger(CVAR_GENERAL("OnFileSelectNameEntry"), 0); + ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0) || disableEditingRandoSettings); + ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, cellPadding); + if (locationsDirty) { + RandomizerCheckObjects::UpdateImGuiVisibility(); + // todo: this efficently when we build out cvar array support + std::stringstream excludedLocationStringStream(CVarGetString(CVAR_RANDOMIZER_SETTING("ExcludedLocations"), "")); + std::string excludedLocationString; + excludedLocations.clear(); + while (getline(excludedLocationStringStream, excludedLocationString, ',')) { + excludedLocations.insert((RandomizerCheck)std::stoi(excludedLocationString)); + } + locationsDirty = false; + } + + if (ImGui::BeginTable("tableRandoLocations", 2, ImGuiTableFlags_BordersH | ImGuiTableFlags_BordersV)) { + ImGui::TableSetupColumn("Included", ImGuiTableColumnFlags_WidthStretch, 200.0f); + ImGui::TableSetupColumn("Excluded", ImGuiTableColumnFlags_WidthStretch, 200.0f); + ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); + ImGui::TableHeadersRow(); + ImGui::PopItemFlag(); + ImGui::TableNextRow(); + + // COLUMN 1 - INCLUDED LOCATIONS + ImGui::TableNextColumn(); + // window->DC.CurrLineTextBaseOffset = 0.0f; + + static ImGuiTextFilter locationSearch; + UIWidgets::PushStyleInput(THEME_COLOR); + locationSearch.Draw(); + UIWidgets::PopStyleInput(); + + ImGui::BeginChild("ChildIncludedLocations", ImVec2(0, -8)); + for (auto& [rcArea, locations] : RandomizerCheckObjects::GetAllRCObjectsByArea()) { + bool hasItems = false; + for (RandomizerCheck rc : locations) { + if (ctx->GetItemLocation(rc)->IsVisible() && !excludedLocations.count(rc) && + locationSearch.PassFilter(Rando::StaticData::GetLocation(rc)->GetName().c_str())) { + + hasItems = true; + break; + } + } + + if (hasItems) { + ImGui::SetNextItemOpen(true, ImGuiCond_Once); + if (ImGui::TreeNode(RandomizerCheckObjects::GetRCAreaName(rcArea).c_str())) { + for (auto& location : locations) { + if (ctx->GetItemLocation(location)->IsVisible() && !excludedLocations.count(location) && + locationSearch.PassFilter(Rando::StaticData::GetLocation(location)->GetName().c_str())) { + UIWidgets::PushStyleButton(THEME_COLOR, ImVec2(7.f, 5.f)); + if (ImGui::ArrowButton(std::to_string(location).c_str(), ImGuiDir_Right)) { + excludedLocations.insert(location); + // todo: this efficently when we build out cvar array support + std::string excludedLocationString = ""; + for (auto excludedLocationIt : excludedLocations) { + excludedLocationString += std::to_string(excludedLocationIt); + excludedLocationString += ","; + } + CVarSetString(CVAR_RANDOMIZER_SETTING("ExcludedLocations"), + excludedLocationString.c_str()); + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); + locationsDirty = true; + } + UIWidgets::PopStyleButton(); + ImGui::SameLine(); + ImGui::Text("%s", Rando::StaticData::GetLocation(location)->GetShortName().c_str()); + } + } + ImGui::TreePop(); + } + } + } + ImGui::EndChild(); + + // COLUMN 2 - EXCLUDED LOCATIONS + ImGui::TableNextColumn(); + // window->DC.CurrLineTextBaseOffset = 0.0f; + + ImGui::BeginChild("ChildExcludedLocations", ImVec2(0, -8)); + for (auto& [rcArea, locations] : RandomizerCheckObjects::GetAllRCObjectsByArea()) { + bool hasItems = false; + for (RandomizerCheck rc : locations) { + if (ctx->GetItemLocation(rc)->IsVisible() && excludedLocations.count(rc)) { + hasItems = true; + break; + } + } + + if (hasItems) { + ImGui::SetNextItemOpen(true, ImGuiCond_Once); + if (ImGui::TreeNode(RandomizerCheckObjects::GetRCAreaName(rcArea).c_str())) { + for (auto& location : locations) { + auto elfound = excludedLocations.find(location); + if (ctx->GetItemLocation(location)->IsVisible() && elfound != excludedLocations.end()) { + UIWidgets::PushStyleButton(THEME_COLOR, ImVec2(7.f, 5.f)); + if (ImGui::ArrowButton(std::to_string(location).c_str(), ImGuiDir_Left)) { + excludedLocations.erase(elfound); + // todo: this efficently when we build out cvar array support + std::string excludedLocationString = ""; + for (auto excludedLocationIt : excludedLocations) { + excludedLocationString += std::to_string(excludedLocationIt); + excludedLocationString += ","; + } + if (excludedLocationString == "") { + CVarClear(CVAR_RANDOMIZER_SETTING("ExcludedLocations")); + } else { + CVarSetString(CVAR_RANDOMIZER_SETTING("ExcludedLocations"), + excludedLocationString.c_str()); + } + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); + locationsDirty = true; + } + UIWidgets::PopStyleButton(); + ImGui::SameLine(); + ImGui::Text("%s", Rando::StaticData::GetLocation(location)->GetShortName().c_str()); + } + } + ImGui::TreePop(); + } + } + } + ImGui::EndChild(); + + ImGui::EndTable(); + } + ImGui::PopStyleVar(1); + // ImGui::EndTabItem(); + ImGui::EndDisabled(); +} + +void DrawTricksMenu(WidgetInfo& info) { + auto ctx = OTRGlobals::Instance->gRandoContext; + auto randoSettings = Rando::Settings::GetInstance(); + static ImVec2 cellPadding(8.0f, 8.0f); + bool generating = CVarGetInteger(CVAR_GENERAL("RandoGenerating"), 0); + bool disableEditingRandoSettings = generating || CVarGetInteger(CVAR_GENERAL("OnFileSelectNameEntry"), 0); + if (tricksDirty) { + tricksDirty = false; + // RandomizerTricks::UpdateImGuiVisibility(); + // todo: this efficently when we build out cvar array support + std::stringstream enabledTrickStringStream(CVarGetString(CVAR_RANDOMIZER_SETTING("EnabledTricks"), "")); + std::string enabledTrickString; + enabledTricks.clear(); + while (getline(enabledTrickStringStream, enabledTrickString, ',')) { + enabledTricks.insert((RandomizerTrick)std::stoi(enabledTrickString)); + } + std::stringstream enabledGlitchStringStream(CVarGetString(CVAR_RANDOMIZER_SETTING("EnabledGlitches"), "")); + std::string enabledGlitchString; + enabledGlitches.clear(); + while (getline(enabledGlitchStringStream, enabledGlitchString, ',')) { + enabledGlitches.insert((RandomizerTrick)std::stoi(enabledGlitchString)); + } + } + + ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, cellPadding); + ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0) || disableEditingRandoSettings); + + // Tricks + static std::unordered_map areaTreeDisabled{ + { RA_NONE, true }, + { RA_KOKIRI_FOREST, true }, + { RA_THE_LOST_WOODS, true }, + { RA_SACRED_FOREST_MEADOW, true }, + { RA_HYRULE_FIELD, true }, + { RA_LAKE_HYLIA, true }, + { RA_GERUDO_VALLEY, true }, + { RA_GERUDO_FORTRESS, true }, + { RA_HAUNTED_WASTELAND, true }, + { RA_DESERT_COLOSSUS, true }, + { RA_THE_MARKET, true }, + { RA_HYRULE_CASTLE, true }, + { RA_KAKARIKO_VILLAGE, true }, + { RA_THE_GRAVEYARD, true }, + { RA_DEATH_MOUNTAIN_TRAIL, true }, + { RA_GORON_CITY, true }, + { RA_DEATH_MOUNTAIN_CRATER, true }, + { RA_ZORAS_RIVER, true }, + { RA_ZORAS_DOMAIN, true }, + { RA_ZORAS_FOUNTAIN, true }, + { RA_LON_LON_RANCH, true }, + { RA_DEKU_TREE, true }, + { RA_DODONGOS_CAVERN, true }, + { RA_JABU_JABUS_BELLY, true }, + { RA_FOREST_TEMPLE, true }, + { RA_FIRE_TEMPLE, true }, + { RA_WATER_TEMPLE, true }, + { RA_SPIRIT_TEMPLE, true }, + { RA_SHADOW_TEMPLE, true }, + { RA_BOTTOM_OF_THE_WELL, true }, + { RA_ICE_CAVERN, true }, + { RA_GERUDO_TRAINING_GROUND, true }, + { RA_GANONS_CASTLE, true }, + }; + static std::unordered_map areaTreeEnabled{ + { RA_NONE, true }, + { RA_KOKIRI_FOREST, true }, + { RA_THE_LOST_WOODS, true }, + { RA_SACRED_FOREST_MEADOW, true }, + { RA_HYRULE_FIELD, true }, + { RA_LAKE_HYLIA, true }, + { RA_GERUDO_VALLEY, true }, + { RA_GERUDO_FORTRESS, true }, + { RA_HAUNTED_WASTELAND, true }, + { RA_DESERT_COLOSSUS, true }, + { RA_THE_MARKET, true }, + { RA_HYRULE_CASTLE, true }, + { RA_KAKARIKO_VILLAGE, true }, + { RA_THE_GRAVEYARD, true }, + { RA_DEATH_MOUNTAIN_TRAIL, true }, + { RA_GORON_CITY, true }, + { RA_DEATH_MOUNTAIN_CRATER, true }, + { RA_ZORAS_RIVER, true }, + { RA_ZORAS_DOMAIN, true }, + { RA_ZORAS_FOUNTAIN, true }, + { RA_LON_LON_RANCH, true }, + { RA_DEKU_TREE, true }, + { RA_DODONGOS_CAVERN, true }, + { RA_JABU_JABUS_BELLY, true }, + { RA_FOREST_TEMPLE, true }, + { RA_FIRE_TEMPLE, true }, + { RA_WATER_TEMPLE, true }, + { RA_SPIRIT_TEMPLE, true }, + { RA_SHADOW_TEMPLE, true }, + { RA_BOTTOM_OF_THE_WELL, true }, + { RA_ICE_CAVERN, true }, + { RA_GERUDO_TRAINING_GROUND, true }, + { RA_GANONS_CASTLE, true }, + }; + + static std::map showTag{ + { Rando::Tricks::Tag::NOVICE, true }, { Rando::Tricks::Tag::INTERMEDIATE, true }, + { Rando::Tricks::Tag::ADVANCED, true }, { Rando::Tricks::Tag::EXPERT, true }, + { Rando::Tricks::Tag::EXTREME, true }, { Rando::Tricks::Tag::EXPERIMENTAL, true }, + { Rando::Tricks::Tag::GLITCH, false }, + }; + static ImGuiTextFilter trickSearch; + UIWidgets::PushStyleInput(THEME_COLOR); + trickSearch.Draw("Filter (inc,-exc)", 490.0f); + UIWidgets::PopStyleInput(); + if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("LogicRules"), RO_LOGIC_GLITCHLESS) != RO_LOGIC_NO_LOGIC) { + ImGui::SameLine(); + if (UIWidgets::Button("Disable All", UIWidgets::ButtonOptions().Color(THEME_COLOR).Size(ImVec2(250.f, 0.f)))) { + for (int i = 0; i < RT_MAX; i++) { + auto etfound = enabledTricks.find(static_cast(i)); + if (etfound != enabledTricks.end()) { + enabledTricks.erase(etfound); + } + } + std::string enabledTrickString = ""; + for (auto enabledTrickIt : enabledTricks) { + enabledTrickString += std::to_string(enabledTrickIt); + enabledTrickString += ","; + } + CVarClear(CVAR_RANDOMIZER_SETTING("EnabledTricks")); + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); + tricksDirty = true; + } + ImGui::SameLine(); + if (UIWidgets::Button("Enable All", UIWidgets::ButtonOptions().Color(THEME_COLOR).Size(ImVec2(250.f, 0.f)))) { + for (int i = 0; i < RT_MAX; i++) { + if (!enabledTricks.count(static_cast(i))) { + enabledTricks.insert(static_cast(i)); + } + } + std::string enabledTrickString = ""; + for (auto enabledTrickIt : enabledTricks) { + enabledTrickString += std::to_string(enabledTrickIt); + enabledTrickString += ","; + } + CVarSetString(CVAR_RANDOMIZER_SETTING("EnabledTricks"), enabledTrickString.c_str()); + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); + tricksDirty = true; + } + } + if (ImGui::BeginTable("trickTags", static_cast(showTag.size()), + ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_Borders)) { + for (auto [rtTag, isShown] : showTag) { + ImGui::TableNextColumn(); + if (isShown) { + ImGui::PushStyleColor(ImGuiCol_Text, Rando::Tricks::GetTextColor(rtTag)); + } else { + ImGui::PushStyleColor(ImGuiCol_Text, { 1.0f, 1.0f, 1.0f, 1.0f }); + } + ImGui::PushStyleColor(ImGuiCol_Header, Rando::Tricks::GetTagColor(rtTag)); + ImGui::Selectable(Rando::Tricks::GetTagName(rtTag).c_str(), &showTag[rtTag]); + ImGui::PopStyleColor(2); + } + ImGui::EndTable(); + } + + if (ImGui::BeginTable("tableRandoTricks", 2, ImGuiTableFlags_BordersH | ImGuiTableFlags_BordersV)) { + ImGui::TableSetupColumn("Disabled Tricks", ImGuiTableColumnFlags_WidthStretch, 200.0f); + ImGui::TableSetupColumn("Enabled Tricks", ImGuiTableColumnFlags_WidthStretch, 200.0f); + ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); + ImGui::TableHeadersRow(); + ImGui::PopItemFlag(); + ImGui::TableNextRow(); + + if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("LogicRules"), RO_LOGIC_GLITCHLESS) != RO_LOGIC_NO_LOGIC) { + // COLUMN 1 - DISABLED TRICKS + ImGui::TableNextColumn(); + // window->DC.CurrLineTextBaseOffset = 0.0f; + + if (UIWidgets::Button("Collapse All##disabled", + UIWidgets::ButtonOptions().Color(THEME_COLOR).Size(ImVec2(0.f, 0.f)))) { + for (int i = 0; i < RA_MAX; i++) { + areaTreeDisabled[static_cast(i)] = false; + } + } + ImGui::SameLine(); + if (UIWidgets::Button("Open All##disabled", + UIWidgets::ButtonOptions().Color(THEME_COLOR).Size(ImVec2(0.f, 0.f)))) { + for (int i = 0; i < RA_MAX; i++) { + areaTreeDisabled[static_cast(i)] = true; + } + } + ImGui::SameLine(); + if (UIWidgets::Button("Enable Visible", + UIWidgets::ButtonOptions().Color(THEME_COLOR).Size(ImVec2(0.f, 0.f)))) { + for (int i = 0; i < RT_MAX; i++) { + auto option = randoSettings->GetTrickOption(static_cast(i)); + if (!enabledTricks.count(static_cast(i)) && + trickSearch.PassFilter(option.GetName().c_str()) && areaTreeDisabled[option.GetArea()] && + Rando::Tricks::CheckTags(showTag, option.GetTags())) { + enabledTricks.insert(static_cast(i)); + } + } + std::string enabledTrickString = ""; + for (auto enabledTrickIt : enabledTricks) { + enabledTrickString += std::to_string(enabledTrickIt); + enabledTrickString += ","; + } + CVarSetString(CVAR_RANDOMIZER_SETTING("EnabledTricks"), enabledTrickString.c_str()); + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); + tricksDirty = true; + } + + ImGui::BeginChild("ChildTricksDisabled", ImVec2(0, -8), false, ImGuiWindowFlags_HorizontalScrollbar); + + for (auto [area, trickIds] : randoSettings->mTricksByArea) { + bool hasTricks = false; + for (auto rt : trickIds) { + auto option = randoSettings->GetTrickOption(rt); + if (!option.IsHidden() && trickSearch.PassFilter(option.GetName().c_str()) && + !enabledTricks.count(rt) && Rando::Tricks::CheckTags(showTag, option.GetTags())) { + hasTricks = true; + break; + } + } + if (hasTricks) { + ImGui::TreeNodeSetOpen(ImGui::GetID((Rando::Tricks::GetAreaName(area) + "##disabled").c_str()), + areaTreeDisabled[area]); + ImGui::SetNextItemOpen(true, ImGuiCond_Once); + if (ImGui::TreeNode((Rando::Tricks::GetAreaName(area) + "##disabled").c_str())) { + for (auto rt : trickIds) { + auto option = randoSettings->GetTrickOption(rt); + if (!option.IsHidden() && trickSearch.PassFilter(option.GetName().c_str()) && + !enabledTricks.count(rt) && Rando::Tricks::CheckTags(showTag, option.GetTags())) { + ImGui::TreeNodeSetOpen( + ImGui::GetID((Rando::Tricks::GetAreaName(option.GetArea()) + "##disabled").c_str()), + areaTreeDisabled[option.GetArea()]); + ImGui::SetNextItemOpen(true, ImGuiCond_Once); + UIWidgets::PushStyleButton(THEME_COLOR, ImVec2(7.f, 5.f)); + if (ImGui::ArrowButton(std::to_string(rt).c_str(), ImGuiDir_Right)) { + enabledTricks.insert(rt); + std::string enabledTrickString = ""; + for (auto enabledTrickIt : enabledTricks) { + enabledTrickString += std::to_string(enabledTrickIt); + enabledTrickString += ","; + } + CVarSetString(CVAR_RANDOMIZER_SETTING("EnabledTricks"), enabledTrickString.c_str()); + Ship::Context::GetInstance() + ->GetWindow() + ->GetGui() + ->SaveConsoleVariablesNextFrame(); + tricksDirty = true; + } + UIWidgets::PopStyleButton(); + Rando::Tricks::DrawTagChips(option.GetTags(), option.GetName()); + ImGui::SameLine(); + ImGui::Text("%s", option.GetName().c_str()); + UIWidgets::Tooltip(option.GetDescription().c_str()); + } + } + areaTreeDisabled[area] = true; + ImGui::TreePop(); + } else { + areaTreeDisabled[area] = false; + } + } + } + ImGui::EndChild(); + + // COLUMN 2 - ENABLED TRICKS + ImGui::TableNextColumn(); + // window->DC.CurrLineTextBaseOffset = 0.0f; + + if (UIWidgets::Button("Collapse All##enabled", + UIWidgets::ButtonOptions().Color(THEME_COLOR).Size(ImVec2(0.f, 0.f)))) { + for (int i = 0; i < RA_MAX; i++) { + areaTreeEnabled[static_cast(i)] = false; + } + } + ImGui::SameLine(); + if (UIWidgets::Button("Open All##enabled", + UIWidgets::ButtonOptions().Color(THEME_COLOR).Size(ImVec2(0.f, 0.f)))) { + for (int i = 0; i < RA_MAX; i++) { + areaTreeEnabled[static_cast(i)] = true; + } + } + ImGui::SameLine(); + if (UIWidgets::Button("Disable Visible", + UIWidgets::ButtonOptions().Color(THEME_COLOR).Size(ImVec2(0.f, 0.f)))) { + for (int i = 0; i < RT_MAX; i++) { + auto option = randoSettings->GetTrickOption(static_cast(i)); + if (enabledTricks.count(static_cast(i)) && + trickSearch.PassFilter(option.GetName().c_str()) && areaTreeEnabled[option.GetArea()] && + Rando::Tricks::CheckTags(showTag, option.GetTags())) { + enabledTricks.erase(static_cast(i)); + } + } + std::string enabledTrickString = ""; + for (auto enabledTrickIt : enabledTricks) { + enabledTrickString += std::to_string(enabledTrickIt); + enabledTrickString += ","; + } + if (enabledTricks.size() == 0) { + CVarClear(CVAR_RANDOMIZER_SETTING("EnabledTricks")); + } else { + CVarSetString(CVAR_RANDOMIZER_SETTING("EnabledTricks"), enabledTrickString.c_str()); + } + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); + tricksDirty = true; + } + + ImGui::BeginChild("ChildTricksEnabled", ImVec2(0, -8), false, ImGuiWindowFlags_HorizontalScrollbar); + + for (auto [area, trickIds] : randoSettings->mTricksByArea) { + bool hasTricks = false; + for (auto rt : trickIds) { + auto option = randoSettings->GetTrickOption(rt); + if (!option.IsHidden() && trickSearch.PassFilter(option.GetName().c_str()) && + enabledTricks.count(rt) && Rando::Tricks::CheckTags(showTag, option.GetTags())) { + hasTricks = true; + break; + } + } + if (hasTricks) { + ImGui::TreeNodeSetOpen(ImGui::GetID((Rando::Tricks::GetAreaName(area) + "##enabled").c_str()), + areaTreeEnabled[area]); + ImGui::SetNextItemOpen(true, ImGuiCond_Once); + if (ImGui::TreeNode((Rando::Tricks::GetAreaName(area) + "##enabled").c_str())) { + for (auto rt : trickIds) { + auto option = randoSettings->GetTrickOption(rt); + if (!option.IsHidden() && trickSearch.PassFilter(option.GetName().c_str()) && + enabledTricks.count(rt) && Rando::Tricks::CheckTags(showTag, option.GetTags())) { + ImGui::TreeNodeSetOpen( + ImGui::GetID((Rando::Tricks::GetAreaName(option.GetArea()) + "##enabled").c_str()), + areaTreeEnabled[option.GetArea()]); + ImGui::SetNextItemOpen(true, ImGuiCond_Once); + UIWidgets::PushStyleButton(THEME_COLOR, ImVec2(7.f, 5.f)); + if (ImGui::ArrowButton(std::to_string(rt).c_str(), ImGuiDir_Left)) { + enabledTricks.erase(rt); + std::string enabledTrickString = ""; + for (auto enabledTrickIt : enabledTricks) { + enabledTrickString += std::to_string(enabledTrickIt); + enabledTrickString += ","; + } + if (enabledTrickString == "") { + CVarClear(CVAR_RANDOMIZER_SETTING("EnabledTricks")); + } else { + CVarSetString(CVAR_RANDOMIZER_SETTING("EnabledTricks"), + enabledTrickString.c_str()); + } + Ship::Context::GetInstance() + ->GetWindow() + ->GetGui() + ->SaveConsoleVariablesNextFrame(); + tricksDirty = true; + } + UIWidgets::PopStyleButton(); + Rando::Tricks::DrawTagChips(option.GetTags(), option.GetName()); + ImGui::SameLine(); + ImGui::Text("%s", option.GetName().c_str()); + UIWidgets::Tooltip(option.GetDescription().c_str()); + } + } + areaTreeEnabled[area] = true; + ImGui::TreePop(); + } else { + areaTreeEnabled[area] = false; + } + } + } + + ImGui::EndChild(); + } else { + ImGui::TableNextColumn(); + ImGui::BeginChild("ChildTricksDisabled", ImVec2(0, -8)); + ImGui::Text("Requires Logic Turned On."); + ImGui::EndChild(); + ImGui::TableNextColumn(); + ImGui::BeginChild("ChildTricksEnabled", ImVec2(0, -8)); + ImGui::Text("Requires Logic Turned On."); + ImGui::EndChild(); + } + ImGui::EndTable(); + } + ImGui::EndDisabled(); + ImGui::PopStyleVar(1); +} + void SohMenu::AddMenuRandomizer() { + Randomizer::CreateCustomMessages(); // Add Randomizer Menu AddMenuEntry("Randomizer", CVAR_SETTING("Menu.RandomizerSidebarSection")); // Seed Settings - WidgetPath path = { "Randomizer", "Seed Settings", SECTION_COLUMN_1 }; - AddSidebarEntry("Randomizer", path.sidebarName, 1); - AddWidget(path, "Popout Randomizer Settings Window", WIDGET_WINDOW_BUTTON) - .CVar(CVAR_WINDOW("RandomizerSettings")) - .WindowName("Randomizer Settings") - .HideInSearch(true) - .Options(WindowButtonOptions().Tooltip("Enables the separate Randomizer Settings Window.")); + WidgetPath path = { "Randomizer", "General", SECTION_COLUMN_1 }; + AddSidebarEntry("Randomizer", path.sidebarName, 2); + AddWidget(path, + "Be sure to explore the Presets and Enhancements Menus for various Speedups and Quality of life changes!", + WIDGET_TEXT) + .Options(TextOptions().Color(UIWidgets::Colors::Gray)); + AddWidget(path, "Seed Entry", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Manual seed entry", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_RANDOMIZER_SETTING("ManualSeedEntry")) + .Options(CheckboxOptions().DefaultValue(true)); + AddWidget(path, "Seed", WIDGET_CUSTOM).CustomFunction([](WidgetInfo& info) { + if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ManualSeedEntry"), 0)) { + UIWidgets::PushStyleInput(THEME_COLOR); + ImGui::InputText("##RandomizerSeed", seedString, MAX_SEED_STRING_SIZE, + ImGuiInputTextFlags_CallbackCharFilter, UIWidgets::TextFilters::FilterAlphaNum); + UIWidgets::Tooltip("Characters from a-z, A-Z, and 0-9 are supported.\n" + "Character limit is 1023, after which the seed will be truncated.\n"); + ImGui::SameLine(); + if (UIWidgets::Button( + ICON_FA_RANDOM, + UIWidgets::ButtonOptions() + .Size(UIWidgets::Sizes::Inline) + .Color(THEME_COLOR) + .Padding(ImVec2(10.f, 6.f)) + .Tooltip("Creates a new random seed value to be used when generating a randomizer"))) { + SohUtils::CopyStringToCharArray(seedString, std::to_string(rand() & 0xFFFFFFFF), MAX_SEED_STRING_SIZE); + } + ImGui::SameLine(); + if (UIWidgets::Button(ICON_FA_ERASER, UIWidgets::ButtonOptions() + .Size(UIWidgets::Sizes::Inline) + .Color(THEME_COLOR) + .Padding(ImVec2(10.f, 6.f)))) { + memset(seedString, 0, MAX_SEED_STRING_SIZE); + } + if (strnlen(seedString, MAX_SEED_STRING_SIZE) == 0) { + ImGui::SameLine(17.0f); + ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, 0.4f), "Leave blank for random seed"); + } + UIWidgets::PopStyleInput(); + } + }); + AddWidget(path, "Generate Randomizer", WIDGET_BUTTON) + .Callback([](WidgetInfo& info) { + OTRGlobals::Instance->gRandoContext->SetSpoilerLoaded(false); + GenerateRandomizer(CVarGetInteger(CVAR_RANDOMIZER_SETTING("ManualSeedEntry"), 0) ? seedString : ""); + }) + .PreFunc([](WidgetInfo& info) { + info.options->Disabled((gSaveContext.gameMode != GAMEMODE_FILE_SELECT) || GameInteractor::IsSaveLoaded()); + }) + .Options(ButtonOptions() + .Size(ImVec2(250.f, 0.f)) + .DisabledTooltip("Must be on File Select to generate a randomizer seed.")); + AddWidget(path, "Spoiler File", WIDGET_CUSTOM) + .CustomFunction([](WidgetInfo& info) { + JoinRandoGenerationThread(); + if (!CVarGetInteger(CVAR_RANDOMIZER_SETTING("DontGenerateSpoiler"), 0)) { + std::string spoilerfilepath = CVarGetString(CVAR_GENERAL("SpoilerLog"), ""); + ImGui::Text("Spoiler File: %s", spoilerfilepath.c_str()); + } + }) + .SameLine(true); // Enhancements - path.sidebarName = "Enhancements"; - AddSidebarEntry("Randomizer", path.sidebarName, 3); - AddWidget(path, "Randomizer Enhancements", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Enhancements", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "These enhancements are only useful in the Randomizer mode but do not affect the randomizer logic.", + WIDGET_TEXT) + .Options(TextOptions().Color(UIWidgets::Colors::Gray)); AddWidget(path, "Rando-Relevant Navi Hints", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_RANDOMIZER_ENHANCEMENT("RandoRelevantNavi")) .Options(CheckboxOptions() @@ -98,6 +681,20 @@ void SohMenu::AddMenuRandomizer() { .Options(FloatSliderOptions().Min(5.0f).Max(15.0f).Format("%.2f").DefaultValue(10.0f).Tooltip( "The size of the item when it is picked up.")); + auto randoSettings = Rando::Settings::GetInstance(); + randoSettings->CreateOptions(); + randoSettings->GetOptionGroup(RSG_MENU_SIDEBAR_LOGIC_ACCESS).AddWidgets(path); + randoSettings->GetOptionGroup(RSG_MENU_SIDEBAR_DUNGEONS).AddWidgets(path); + randoSettings->GetOptionGroup(RSG_MENU_SIDEBAR_SHUFFLES).AddWidgets(path); + randoSettings->GetOptionGroup(RSG_MENU_SIDEBAR_HINTS_TRAPS).AddWidgets(path); + randoSettings->GetOptionGroup(RSG_MENU_SIDEBAR_STARTING_ITEMS).AddWidgets(path); + path.sidebarName = "Locations"; + AddSidebarEntry("Randomizer", path.sidebarName, 1); + AddWidget(path, "Excluded Locations", WIDGET_CUSTOM).CustomFunction(DrawLocationsMenu); + path.sidebarName = "Tricks/Glitches"; + AddSidebarEntry("Randomizer", path.sidebarName, 1); + AddWidget(path, "Tricks/Glitches", WIDGET_CUSTOM).CustomFunction(DrawTricksMenu); + // Plandomizer path.sidebarName = "Plandomizer"; AddSidebarEntry("Randomizer", path.sidebarName, 1); diff --git a/soh/soh/SohGui/UIWidgets.hpp b/soh/soh/SohGui/UIWidgets.hpp index 95590b1ce..ffc443352 100644 --- a/soh/soh/SohGui/UIWidgets.hpp +++ b/soh/soh/SohGui/UIWidgets.hpp @@ -160,6 +160,10 @@ struct ButtonOptions : WidgetOptions { color = color_; return *this; } + ButtonOptions& DisabledTooltip(const char* disabledTooltip_) { + WidgetOptions::disabledTooltip = disabledTooltip_; + return *this; + } }; struct ColorPickerOptions : WidgetOptions {