#include "settings.h"
#include "soh/Enhancements/randomizer/randomizerTypes.h"
#include "trial.h"
#include "dungeon.h"
#include "soh/ShipUtils.h"

#include "soh/OTRGlobals.h"

#include <spdlog/spdlog.h>

#include <libultraship/bridge/consolevariablebridge.h>
#include <libultraship/libultraship.h>

namespace Rando {
std::shared_ptr<Settings> Settings::mInstance;

std::vector<std::string> NumOpts(const int min, const int max, const int step = 1, const std::string& textBefore = {},
                                 const std::string& textAfter = {}) {
    std::vector<std::string> options;
    options.reserve((max - min) / step + 1);
    for (int i = min; i <= max; i += step) {
        options.push_back(textBefore + std::to_string(i) += textAfter);
    }
    return options;
}

std::vector<std::string> MultiVecOpts(const std::vector<std::vector<std::string>>& optionsVector) {
    size_t totalSize = 0;
    for (const auto& vector : optionsVector) {
        totalSize += vector.size();
    }
    std::vector<std::string> options;
    options.reserve(totalSize);
    for (const auto& vector : optionsVector) {
        for (const auto& op : vector) {
            options.push_back(op);
        }
    }
    return options;
}

void Settings::HandleShopsanityPriceUI() {
    bool isTycoon = CVarGetInteger(CVAR_RANDOMIZER_SETTING("IncludeTycoonWallet"), RO_GENERIC_OFF);
    mOptions[RSK_SHOPSANITY].RemoveFlag(IMFLAG_SEPARATOR_BOTTOM);
    mOptions[RSK_SHOPSANITY_PRICES].Unhide();
    switch (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShopsanityPrices"), RO_PRICE_VANILLA)) {
        case RO_PRICE_FIXED:
            mOptions[RSK_SHOPSANITY_PRICES_FIXED_PRICE].Unhide();
            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();
            if (isTycoon ? mOptions[RSK_SHOPSANITY_PRICES_FIXED_PRICE].GetOptionCount() == 501
                         : mOptions[RSK_SHOPSANITY_PRICES_FIXED_PRICE].GetOptionCount() == 1000) {
                mOptions[RSK_SHOPSANITY_PRICES_FIXED_PRICE].ChangeOptions(isTycoon ? NumOpts(0, 999) : NumOpts(0, 500));
            }
            mOptions[RSK_SHOPSANITY_PRICES_AFFORDABLE].Hide();
            break;
        case RO_PRICE_RANGE:
            mOptions[RSK_SHOPSANITY_PRICES_FIXED_PRICE].Hide();
            mOptions[RSK_SHOPSANITY_PRICES_RANGE_1].Unhide();
            mOptions[RSK_SHOPSANITY_PRICES_RANGE_2].Unhide();
            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();
            if (isTycoon ? mOptions[RSK_SHOPSANITY_PRICES_RANGE_1].GetOptionCount() == 101
                         : mOptions[RSK_SHOPSANITY_PRICES_RANGE_1].GetOptionCount() == 200) {
                mOptions[RSK_SHOPSANITY_PRICES_RANGE_1].ChangeOptions(isTycoon ? NumOpts(0, 995, 5)
                                                                               : NumOpts(0, 500, 5));
                mOptions[RSK_SHOPSANITY_PRICES_RANGE_2].ChangeOptions(isTycoon ? NumOpts(0, 995, 5)
                                                                               : NumOpts(0, 500, 5));
            }
            mOptions[RSK_SHOPSANITY_PRICES_AFFORDABLE].Unhide();
            break;
        case RO_PRICE_SET_BY_WALLET:
            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].Unhide();
            mOptions[RSK_SHOPSANITY_PRICES_CHILD_WALLET_WEIGHT].Unhide();
            mOptions[RSK_SHOPSANITY_PRICES_ADULT_WALLET_WEIGHT].Unhide();
            mOptions[RSK_SHOPSANITY_PRICES_GIANT_WALLET_WEIGHT].Unhide();
            if (isTycoon) {
                mOptions[RSK_SHOPSANITY_PRICES_TYCOON_WALLET_WEIGHT].Unhide();
            } else {
                mOptions[RSK_SHOPSANITY_PRICES_TYCOON_WALLET_WEIGHT].Hide();
            }
            mOptions[RSK_SHOPSANITY_PRICES_AFFORDABLE].Unhide();
            break;
        default:
            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();
            mOptions[RSK_SHOPSANITY_PRICES_AFFORDABLE].Unhide();
            break;
    }
}

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, ...) mTrickSettings[rsk] = TrickSetting::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 unless it's No Logic
    if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("LogicRules"), RO_LOGIC_GLITCHLESS) != RO_LOGIC_NO_LOGIC &&
        // If Closed DoT requires OoT then we can only start as child
        ((CVarGetInteger(CVAR_RANDOMIZER_SETTING("DoorOfTime"), RO_DOOROFTIME_CLOSED) == RO_DOOROFTIME_CLOSED &&
          CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleOcarinas"), RO_GENERIC_OFF) == RO_GENERIC_OFF) ||
         // If Forest is Closed, we cannot start as Adult unless there's a sphere 0 entrance shuffle in Kokiri forest,
         // or there's random spawns, as the player may saveload as child and get stuck.
         // Grottos only lead somewhere if decoupled
         (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ClosedForest"), RO_CLOSED_FOREST_ON) == RO_CLOSED_FOREST_ON &&
          CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleOverworldSpawns"), RO_GENERIC_OFF) == RO_GENERIC_OFF &&
          (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleGrottosEntrances"), RO_GENERIC_OFF) == RO_GENERIC_OFF ||
           CVarGetInteger(CVAR_RANDOMIZER_SETTING("DecoupleEntrances"), RO_GENERIC_OFF) == RO_GENERIC_OFF) &&
          CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleInteriorsEntrances"), RO_GENERIC_OFF) == RO_GENERIC_OFF &&
          CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleOverworldEntrances"), RO_GENERIC_OFF) == RO_GENERIC_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], WIDGET_CVAR_COMBOBOX, RO_CLOSED_FOREST_ON);
    OPT_CALLBACK(RSK_FOREST, {
        HandleStartingAgeUI();
    });
    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], 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_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_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_BOOL(RSK_MEDALLION_LOCKED_TRIALS, "Medallion Locked Trials", CVAR_RANDOMIZER_SETTING("MedallionLockedTrials"), mOptionDescriptions[RSK_MEDALLION_LOCKED_TRIALS]);
    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], 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) {
            mOptions[RSK_SHUFFLE_GANONS_TOWER_ENTRANCE].Hide();
        } else {
            mOptions[RSK_SHUFFLE_GANONS_TOWER_ENTRANCE].Unhide();
        }

        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_GANONS_TOWER_ENTRANCE, "Ganon's Tower Entrance", CVAR_RANDOMIZER_SETTING("ShuffleGanonTowerEntrance"), mOptionDescriptions[RSK_SHUFFLE_GANONS_TOWER_ENTRANCE]);
    OPT_BOOL(RSK_SHUFFLE_OVERWORLD_ENTRANCES, "Overworld Entrances", CVAR_RANDOMIZER_SETTING("ShuffleOverworldEntrances"), mOptionDescriptions[RSK_SHUFFLE_OVERWORLD_ENTRANCES]);
    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();
        }
        
        HandleStartingAgeUI();
    });
    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();
        }
        
        HandleStartingAgeUI();
    });
    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();
        }
        
        HandleStartingAgeUI();
    });
    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_CALLBACK(RSK_SHUFFLE_OVERWORLD_SPAWNS, {
        HandleStartingAgeUI();
    });
    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);
    OPT_BOOL(RSK_MIX_INTERIOR_ENTRANCES, "Mix Interiors", CVAR_RANDOMIZER_SETTING("MixInteriors"), mOptionDescriptions[RSK_MIX_INTERIOR_ENTRANCES], IMFLAG_NONE);
    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_CALLBACK(RSK_DECOUPLED_ENTRANCES, {
        HandleStartingAgeUI();
    });
    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_U8(RSK_TRIFORCE_HUNT, "Triforce Hunt", {"Off", "Win", "Ganon's Boss Key"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("TriforceHunt"), mOptionDescriptions[RSK_TRIFORCE_HUNT]);
    OPT_CALLBACK(RSK_TRIFORCE_HUNT, {
        // Remove the pieces required/total sliders and add a separator after Triforce Hunt if Triforce Hunt is off
        if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("TriforceHunt"), RO_TRIFORCE_HUNT_OFF) == RO_TRIFORCE_HUNT_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, MAX_MQ_DUNGEON_COUNT)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MQDungeonCount"), "", WIDGET_CVAR_SLIDER_INT, MAX_MQ_DUNGEON_COUNT, 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", "Own Dungeon", "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\".");
            mOptions[RSK_LINKS_POCKET_REWARD].Enable();
            mOptions[RSK_LINKS_POCKET_REWARD].Unhide();
        } else {
            if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleDungeonReward"), RO_DUNGEON_REWARDS_END_OF_DUNGEON) ==
                RO_DUNGEON_REWARDS_OWN_DUNGEON) {
                mOptions[RSK_LINKS_POCKET].Enable();
                mOptions[RSK_LINKS_POCKET_REWARD].Disable(
                    "As \"Link's Pocket\" is set to \"Dungeon Reward\" while \"Dungeon Rewards\" is set to \"Own Dungeon\", Link's Pocket will always have the Light Medallion");
            }else if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleDungeonReward"), RO_DUNGEON_REWARDS_END_OF_DUNGEON) ==
                RO_DUNGEON_REWARDS_VANILLA) {
                mOptions[RSK_LINKS_POCKET].Enable();
                mOptions[RSK_LINKS_POCKET_REWARD].Disable(
                    "As \"Link's Pocket\" is set to \"Dungeon Reward\" while \"Dungeon Rewards\" is set to \"Vanilla\", Link's Pocket will always have the Light Medallion");
            } else {
                mOptions[RSK_LINKS_POCKET].Enable();
                mOptions[RSK_LINKS_POCKET_REWARD].Enable();
            }
            if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("LinksPocket"), RO_LINKS_POCKET_DUNGEON_REWARD) == RO_LINKS_POCKET_DUNGEON_REWARD) {
                mOptions[RSK_LINKS_POCKET_REWARD].Unhide();
            } else {
                mOptions[RSK_LINKS_POCKET_REWARD].Hide();
            }  
        }
    });
    OPT_U8(RSK_LINKS_POCKET, "Link's Pocket", {"Dungeon Reward", "Advancement", "Anything", "Nothing"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LinksPocket"), mOptionDescriptions[RSK_LINKS_POCKET], WIDGET_CVAR_COMBOBOX, RO_LINKS_POCKET_DUNGEON_REWARD);
    OPT_CALLBACK(RSK_LINKS_POCKET, {
        // Only show the dungeon reward type if Link's Pocket is set to Dungeon Reward and Dungeon Rewards are not Vanilla, OR Dungeon Rewards are end of dungeon
        if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("LinksPocket"), RO_LINKS_POCKET_DUNGEON_REWARD) == RO_LINKS_POCKET_DUNGEON_REWARD || 
            CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleDungeonReward"), RO_DUNGEON_REWARDS_END_OF_DUNGEON) == RO_DUNGEON_REWARDS_END_OF_DUNGEON) {
            mOptions[RSK_LINKS_POCKET_REWARD].Unhide();
        } else {
            mOptions[RSK_LINKS_POCKET_REWARD].Hide();
        }
    });
    OPT_U8(RSK_LINKS_POCKET_REWARD, "Link's Pocket Reward Type", {"Any Reward", "Any Stone", "Any Medallion", "Light Medallion"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LinksPocketReward"), mOptionDescriptions[RSK_LINKS_POCKET_REWARD], WIDGET_CVAR_COMBOBOX, RO_LINKS_POCKET_ANY_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_CALLBACK(RSK_SHOPSANITY_PRICES, {
        HandleShopsanityPriceUI();
    });
    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], 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_CLIMB, "Shuffle Climb", CVAR_RANDOMIZER_SETTING("ShuffleClimb"), mOptionDescriptions[RSK_SHUFFLE_CLIMB]);
    OPT_BOOL(RSK_SHUFFLE_CRAWL, "Shuffle Crawl", CVAR_RANDOMIZER_SETTING("ShuffleCrawl"), mOptionDescriptions[RSK_SHUFFLE_CRAWL]);
    OPT_BOOL(RSK_SHUFFLE_GRAB, "Shuffle Grab", CVAR_RANDOMIZER_SETTING("ShuffleGrab"), mOptionDescriptions[RSK_SHUFFLE_GRAB]);
    OPT_BOOL(RSK_SHUFFLE_SPEAK, "Shuffle Jabber Nuts", CVAR_RANDOMIZER_SETTING("ShuffleSpeak"), mOptionDescriptions[RSK_SHUFFLE_SPEAK]);
    OPT_BOOL(RSK_SHUFFLE_OPEN_CHEST, "Shuffle Open Chest", CVAR_RANDOMIZER_SETTING("ShuffleOpenChest"), mOptionDescriptions[RSK_SHUFFLE_OPEN_CHEST]);
    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], WIDGET_CVAR_COMBOBOX, RO_SHUFFLE_POTS_OFF);
    OPT_U8(RSK_SHUFFLE_GRASS, "Shuffle Grass", {"Off", "Dungeons", "Overworld", "All Grass"}, 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_ROCKS, "Shuffle Rocks", CVAR_RANDOMIZER_SETTING("ShuffleRocks"), mOptionDescriptions[RSK_SHUFFLE_ROCKS]);
    OPT_U8(RSK_SHUFFLE_BOULDERS, "Shuffle Boulders", {"Off", "Dungeons", "Overworld", "All Boulders"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleBoulders"), mOptionDescriptions[RSK_SHUFFLE_BOULDERS], WIDGET_CVAR_COMBOBOX, RO_SHUFFLE_BOULDERS_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_ICICLES, "Shuffle Icicles", CVAR_RANDOMIZER_SETTING("ShuffleIcicles"), mOptionDescriptions[RSK_SHUFFLE_ICICLES]);
    OPT_BOOL(RSK_SHUFFLE_RED_ICE, "Shuffle Red Ice", CVAR_RANDOMIZER_SETTING("ShuffleRedIce"), mOptionDescriptions[RSK_SHUFFLE_RED_ICE]);
    OPT_U8(RSK_SHUFFLE_SIGNS, "Shuffle Signs", {"Off", "Dungeons", "Overworld", "All Signs"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleSigns"), mOptionDescriptions[RSK_SHUFFLE_SIGNS], WIDGET_CVAR_COMBOBOX, RO_SHUFFLE_SIGNS_OFF);
    OPT_BOOL(RSK_SHUFFLE_FISHING_POLE, "Shuffle Fishing Pole", CVAR_RANDOMIZER_SETTING("ShuffleFishingPole"), mOptionDescriptions[RSK_SHUFFLE_FISHING_POLE]);
    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_BEGGAR, "Shuffle Beggar", CVAR_RANDOMIZER_SETTING("ShuffleBeggar"), mOptionDescriptions[RSK_SHUFFLE_BEGGAR]);
    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, 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_SHUFFLE_WONDER_ITEMS, "Shuffle Wonder Items", {"Off", "Dungeons", "Overworld", "All Items"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleWonderItems"), mOptionDescriptions[RSK_SHUFFLE_WONDER_ITEMS], WIDGET_CVAR_COMBOBOX, RO_SHUFFLE_WONDER_ITEMS_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_BOOL(RSK_SHUFFLE_BUTTERFLY_FAIRIES, "Shuffle Butterfly Fairies", CVAR_RANDOMIZER_SETTING("ShuffleButterflyFairies"), mOptionDescriptions[RSK_SHUFFLE_BUTTERFLY_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], 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], 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], 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_U8(RSK_MASK_QUEST, "Mask Quest", {"Vanilla", "Completed", "Shuffle"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("CompleteMaskQuest"), mOptionDescriptions[RSK_MASK_QUEST], WIDGET_CVAR_COMBOBOX, 0);
    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_BOSS_KEY_HINT, "Boss Door Hints", CVAR_RANDOMIZER_SETTING("BossKeyHint"), mOptionDescriptions[RSK_BOSS_KEY_HINT], 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);
    OPT_BOOL(RSK_SARIA_HINT, "Saria's Hint", CVAR_RANDOMIZER_SETTING("SariaHint"), mOptionDescriptions[RSK_SARIA_HINT], IMFLAG_NONE);
    OPT_BOOL(RSK_MIDO_HINT, "Mido's Hint", CVAR_RANDOMIZER_SETTING("MidoHint"), mOptionDescriptions[RSK_MIDO_HINT], IMFLAG_NONE);
    OPT_BOOL(RSK_FISHING_POLE_HINT, "Fishing Pole Hint", CVAR_RANDOMIZER_SETTING("FishingPoleHint"), mOptionDescriptions[RSK_FISHING_POLE_HINT], IMFLAG_NONE);
    OPT_BOOL(RSK_FROGS_HINT, "Frog Ocarina Game Hint", CVAR_RANDOMIZER_SETTING("FrogsHint"), mOptionDescriptions[RSK_FROGS_HINT], IMFLAG_NONE);
    OPT_BOOL(RSK_OOT_HINT, "Ocarina of Time Hint", CVAR_RANDOMIZER_SETTING("OoTHint"), mOptionDescriptions[RSK_OOT_HINT], IMFLAG_NONE);
    OPT_BOOL(RSK_BIGGORON_HINT, "Biggoron's Hint", CVAR_RANDOMIZER_SETTING("BiggoronHint"), mOptionDescriptions[RSK_BIGGORON_HINT], IMFLAG_NONE);
    OPT_BOOL(RSK_BIG_POES_HINT, "Big Poes Hint", CVAR_RANDOMIZER_SETTING("BigPoesHint"), mOptionDescriptions[RSK_BIG_POES_HINT], IMFLAG_NONE);
    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, 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);
    OPT_BOOL(RSK_KAK_20_SKULLS_HINT, "20 GS Hint", CVAR_RANDOMIZER_SETTING("20GSHint"), mOptionDescriptions[RSK_KAK_20_SKULLS_HINT], IMFLAG_NONE);
    OPT_BOOL(RSK_KAK_30_SKULLS_HINT, "30 GS Hint", CVAR_RANDOMIZER_SETTING("30GSHint"), mOptionDescriptions[RSK_KAK_30_SKULLS_HINT], IMFLAG_NONE);
    OPT_BOOL(RSK_KAK_40_SKULLS_HINT, "40 GS Hint", CVAR_RANDOMIZER_SETTING("40GSHint"), mOptionDescriptions[RSK_KAK_40_SKULLS_HINT], IMFLAG_NONE);
    OPT_BOOL(RSK_KAK_50_SKULLS_HINT, "50 GS Hint", CVAR_RANDOMIZER_SETTING("50GSHint"), mOptionDescriptions[RSK_KAK_50_SKULLS_HINT], IMFLAG_NONE);
    OPT_BOOL(RSK_KAK_100_SKULLS_HINT, "100 GS Hint", CVAR_RANDOMIZER_SETTING("100GSHint"), mOptionDescriptions[RSK_KAK_100_SKULLS_HINT], IMFLAG_NONE);
    OPT_BOOL(RSK_MASK_SHOP_HINT, "Mask Shop Hint", CVAR_RANDOMIZER_SETTING("MaskShopHint"), mOptionDescriptions[RSK_MASK_SHOP_HINT]);
    // TODO: Compasses show rewards/woth, maps show dungeon mode
    OPT_BOOL(RSK_BLUE_FIRE_ARROWS, "Blue Fire Arrows", CVAR_RANDOMIZER_SETTING("BlueFireArrows"), mOptionDescriptions[RSK_BLUE_FIRE_ARROWS]);
    OPT_BOOL(RSK_SUNLIGHT_ARROWS, "Sunlight Arrows", CVAR_RANDOMIZER_SETTING("SunlightArrows"), mOptionDescriptions[RSK_SUNLIGHT_ARROWS]);
    OPT_BOOL(RSK_ROCS_FEATHER, "Roc's Feather", CVAR_RANDOMIZER_SETTING("RocsFeather"), mOptionDescriptions[RSK_ROCS_FEATHER]);
    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], WIDGET_CVAR_COMBOBOX, RO_ITEM_POOL_BALANCED);
    OPT_BOOL(RSK_BASE_ICE_TRAPS, "Base Ice Traps", CVAR_RANDOMIZER_SETTING("BaseIceTraps"), mOptionDescriptions[RSK_BASE_ICE_TRAPS], IMFLAG_NONE, WIDGET_CVAR_COMBOBOX, RO_GENERIC_ON);
    OPT_U8(RSK_ADDITIONAL_ICE_TRAPS, "Additional Ice Traps", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("AdditionalIceTraps"), mOptionDescriptions[RSK_ADDITIONAL_ICE_TRAPS], WIDGET_CVAR_SLIDER_INT, 0);
    OPT_U8(RSK_ICE_TRAP_PERCENT, "Ice Trap Percent", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("IceTrapPercent"), mOptionDescriptions[RSK_ICE_TRAP_PERCENT], WIDGET_CVAR_SLIDER_INT, 0);
    // 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"), "", 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"), "", 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);
    OPT_BOOL(RSK_STARTING_SUNS_SONG, "Start with Sun's Song", CVAR_RANDOMIZER_SETTING("StartingSunsSong"), "", IMFLAG_NONE);
    OPT_BOOL(RSK_STARTING_SONG_OF_TIME, "Start with Song of Time", CVAR_RANDOMIZER_SETTING("StartingSongOfTime"), "", IMFLAG_NONE);
    OPT_BOOL(RSK_STARTING_SONG_OF_STORMS, "Start with Song of Storms", CVAR_RANDOMIZER_SETTING("StartingSongOfStorms"), "", IMFLAG_NONE);
    OPT_BOOL(RSK_STARTING_MINUET_OF_FOREST, "Start with Minuet of Forest", CVAR_RANDOMIZER_SETTING("StartingMinuetOfForest"), "", IMFLAG_NONE);
    OPT_BOOL(RSK_STARTING_BOLERO_OF_FIRE, "Start with Bolero of Fire", CVAR_RANDOMIZER_SETTING("StartingBoleroOfFire"), "", IMFLAG_NONE);
    OPT_BOOL(RSK_STARTING_SERENADE_OF_WATER, "Start with Serenade of Water", CVAR_RANDOMIZER_SETTING("StartingSerenadeOfWater"), "", IMFLAG_NONE);
    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"), "", 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], WIDGET_CVAR_COMBOBOX, RO_LOGIC_GLITCHLESS, false, nullptr, IMFLAG_LABEL_INLINE);
    OPT_CALLBACK(RSK_LOGIC_RULES, {
        HandleStartingAgeUI();
    });
    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, "", "", 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 option has been disabled because only one type of O2R has been loaded");
        mOptions[RSK_MQ_DUNGEON_COUNT].Disable("This option has been disabled because only one type of O2R has been loaded");
        mOptions[RSK_MQ_DUNGEON_SET].Disable("This option has been disabled because only one type of O2R has been loaded");
        mOptions[RSK_MQ_DEKU_TREE].Disable("This option has been disabled because only one type of O2R has been loaded");
        mOptions[RSK_MQ_DODONGOS_CAVERN].Disable("This option has been disabled because only one type of O2R has been loaded");
        mOptions[RSK_MQ_JABU_JABU].Disable("This option has been disabled because only one type of O2R has been loaded");
        mOptions[RSK_MQ_FOREST_TEMPLE].Disable("This option has been disabled because only one type of O2R has been loaded");
        mOptions[RSK_MQ_FIRE_TEMPLE].Disable("This option has been disabled because only one type of O2R has been loaded");
        mOptions[RSK_MQ_WATER_TEMPLE].Disable("This option has been disabled because only one type of O2R has been loaded");
        mOptions[RSK_MQ_SPIRIT_TEMPLE].Disable("This option has been disabled because only one type of O2R has been loaded");
        mOptions[RSK_MQ_SHADOW_TEMPLE].Disable("This option has been disabled because only one type of O2R has been loaded");
        mOptions[RSK_MQ_BOTTOM_OF_THE_WELL].Disable("This option has been disabled because only one type of O2R has been loaded");
        mOptions[RSK_MQ_ICE_CAVERN].Disable("This option has been disabled because only one type of O2R has been loaded");
        mOptions[RSK_MQ_GTG].Disable("This option has been disabled because only one type of O2R has been loaded");
        mOptions[RSK_MQ_GANONS_CASTLE].Disable("This option has been disabled because only one type of O2R 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();

    mExcludeLocationsOptionsAreas.reserve(RCAREA_INVALID);

    // RANDOTODO sweep trick descriptions and make sure they match a post-refactor, post shuffles reality
    /* Common abbreviations in name tags
    - A: Adult
    - Blk: Block
    - Blu: Blue (Switch)
    - Bmb: Bombs
    - Bou: Boulder
    - C: Child
    - Clp: Clip
    - Col: Collision
    - Cuc: Cucoo
    - Crt: Crate
    - Diff: Difficult (Weapons)
    - Ent: Entrance
    - HB: Hover Boots
    - Jmp: Jump
    - Ldg: Ledge
    - LoT: Lens of Truth
    - Prj: Projectile
    - Rng: Boomerang
    - Sli: Slingshot
    - Skp: Skip
    - Swt: Switch
    - Tor: Torch
    Try to keep Name Tags less than 8 chars.
    */

    // the following are glitches and are currently disabled

    // OPT_TRICK(RT_ACUTE_ANGLE_CLIP, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::ADVANCED, Tricks::Tag::EXPERIMENTAL,
    // Tricks::Tag::GLITCH });

    // OPT_TRICK(RT_ADVANCED_CLIPS, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::ADVANCED, Tricks::Tag::EXPERIMENTAL,
    // Tricks::Tag::GLITCH });

    // OPT_TRICK(RT_BLANK_A, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::ADVANCED, Tricks::Tag::EXPERIMENTAL,
    // Tricks::Tag::GLITCH });

    // OPT_TRICK(RT_DOOM_JUMP, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::ADVANCED, Tricks::Tag::EXPERIMENTAL,
    // Tricks::Tag::GLITCH });

    // OPT_TRICK(RT_EPG, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::ADVANCED, Tricks::Tag::EXPERIMENTAL,
    // Tricks::Tag::GLITCH});

    // OPT_TRICK(RT_EQUIP_SWAP, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::ADVANCED, Tricks::Tag::EXPERIMENTAL,
    // Tricks::Tag::GLITCH });

    // OPT_TRICK(RT_EQUIP_SWAP_EXPECTS_DINS, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::ADVANCED, Tricks::Tag::EXPERIMENTAL,
    // Tricks::Tag::GLITCH });

    // OPT_TRICK(RT_FLAME_STORAGE, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::ADVANCED, Tricks::Tag::EXPERIMENTAL,
    // Tricks::Tag::GLITCH });

    // OPT_TRICK(RT_GROUND_CLIP, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::ADVANCED, Tricks::Tag::EXPERIMENTAL,
    // Tricks::Tag::GLITCH });

    // OPT_TRICK(RT_HESS, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::ADVANCED, Tricks::Tag::EXPERIMENTAL,
    // Tricks::Tag::GLITCH});

    // OPT_TRICK(RT_HOOKSHOT_CLIP, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::ADVANCED, Tricks::Tag::EXPERIMENTAL,
    // Tricks::Tag::GLITCH });

    // OPT_TRICK(RT_HOOKSHOT_JUMP, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::ADVANCED, Tricks::Tag::EXPERIMENTAL,
    // Tricks::Tag::GLITCH });

    // OPT_TRICK(RT_ISG, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::ADVANCED, Tricks::Tag::EXPERIMENTAL,
    // Tricks::Tag::GLITCH});

    OPT_TRICK(RT_VISIBLE_COLLISION, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::NOVICE }, "VisCol");
    OPT_TRICK(RT_GROTTOS_WITHOUT_AGONY, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::NOVICE }, "NoSoA");
    OPT_TRICK(RT_FEWER_TUNIC_REQUIREMENTS, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::INTERMEDIATE }, "FTR");
    OPT_TRICK(RT_UNINTUITIVE_JUMPS, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::NOVICE }, "UnJmp");
    OPT_TRICK(RT_FIRE_RINGS, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::INTERMEDIATE }, "FlaChst");

    // disabled for now, can't check for being able to use bunny hood & bunny hood speedup is currently completely
    // decoupled from rando

    // OPT_TRICK(RT_BUNNY_HOOD_JUMPS, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::ADVANCED });

    OPT_TRICK(RT_DAMAGE_BOOST_SIMPLE, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::ADVANCED, Tricks::Tag::EXPERIMENTAL },
              "SDmgBoo");
    OPT_TRICK(RT_HOVER_BOOST_SIMPLE, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::ADVANCED, Tricks::Tag::GLITCH }, "SHovBoo");
    OPT_TRICK(RT_BOMBCHU_BEEHIVES, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::NOVICE }, "ChuBee");
    OPT_TRICK(RT_HOOKSHOT_LADDERS, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::NOVICE }, "HSLad");
    OPT_TRICK(RT_BLUE_FIRE_MUD_WALLS, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::NOVICE }, "BluFire");
    OPT_TRICK(RT_OPEN_UNDERWATER_CHEST, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::NOVICE, Tricks::Tag::GLITCH }, "OpenUC");
    OPT_TRICK(RT_BOULDER_COLLISION, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::NOVICE, Tricks::Tag::GLITCH }, "BolCol");
    OPT_TRICK(RT_ITEM_EXTENSION, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::INTERMEDIATE }, "HSExt");
    OPT_TRICK(RT_BIG_SKULLTULA_PAUSE_LIFT, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::NOVICE, Tricks::Tag::GLITCH },
              "SkulPaus");
    OPT_TRICK(RT_GROUND_JUMP, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::NOVICE, Tricks::Tag::GLITCH }, "GrdJmp");
    OPT_TRICK(RT_GROUND_JUMP_HARD, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::INTERMEDIATE, Tricks::Tag::GLITCH },
              "HGrdJmp");
    OPT_TRICK(RT_SLIDE_JUMP, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::NOVICE }, "SldJmp");
    OPT_TRICK(RT_VOIDOUT_COLLECTION, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::NOVICE }, "VdCl");
    OPT_TRICK(RT_BOMB_DETONATION, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::NOVICE }, "BmbDet");
    OPT_TRICK(RT_KF_ADULT_GS, RCQUEST_BOTH, RA_KOKIRI_FOREST, { Tricks::Tag::NOVICE }, "KFGSHB");
    OPT_TRICK(RT_LW_BRIDGE, RCQUEST_BOTH, RA_THE_LOST_WOODS, { Tricks::Tag::EXPERT }, "LWBrgJmp");
    OPT_TRICK(RT_LW_MIDO_BACKFLIP, RCQUEST_BOTH, RA_THE_LOST_WOODS, { Tricks::Tag::NOVICE }, "MidoSkip");
    OPT_TRICK(RT_LOST_WOOD_NAVI_DIVE, RCQUEST_BOTH, RA_THE_LOST_WOODS, { Tricks::Tag::NOVICE, Tricks::Tag::GLITCH },
              "LWNaviD");
    OPT_TRICK(RT_LW_GS_BEAN, RCQUEST_BOTH, RA_THE_LOST_WOODS, { Tricks::Tag::INTERMEDIATE }, "LWGSHS");
    OPT_TRICK(RT_HC_STORMS_GS, RCQUEST_BOTH, RA_HYRULE_CASTLE, { Tricks::Tag::INTERMEDIATE }, "HCGrGSRng");
    OPT_TRICK(RT_HF_BIG_POE_WITHOUT_EPONA, RCQUEST_BOTH, RA_HYRULE_FIELD, { Tricks::Tag::NOVICE }, "PoeDiff");
    OPT_TRICK(RT_KAK_TOWER_GS, RCQUEST_BOTH, RA_KAKARIKO_VILLAGE, { Tricks::Tag::INTERMEDIATE }, "KakGSJS");
    OPT_TRICK(RT_KAK_CHILD_WINDMILL_POH, RCQUEST_BOTH, RA_KAKARIKO_VILLAGE, { Tricks::Tag::EXTREME }, "WndCJS");
    OPT_TRICK(RT_KAK_ROOFTOP_GS, RCQUEST_BOTH, RA_KAKARIKO_VILLAGE, { Tricks::Tag::ADVANCED }, "KakGSHB");
    OPT_TRICK(RT_GY_POH, RCQUEST_BOTH, RA_THE_GRAVEYARD, { Tricks::Tag::INTERMEDIATE }, "GYPoHRng");
    OPT_TRICK(RT_GY_CHILD_DAMPE_RACE_POH, RCQUEST_BOTH, RA_THE_GRAVEYARD, { Tricks::Tag::NOVICE }, "CDmpRace");
    OPT_TRICK(RT_GY_SHADOW_FIRE_ARROWS, RCQUEST_BOTH, RA_THE_GRAVEYARD, { Tricks::Tag::EXPERT }, "FAEntry");
    OPT_TRICK(RT_DMT_SHIELDLESS_CLIMB, RCQUEST_BOTH, RA_DEATH_MOUNTAIN_TRAIL, { Tricks::Tag::NOVICE }, "DMTCWoS");
    OPT_TRICK(RT_DMT_SOIL_GS, RCQUEST_BOTH, RA_DEATH_MOUNTAIN_TRAIL, { Tricks::Tag::INTERMEDIATE }, "DMTSoil");
    OPT_TRICK(RT_DMT_BOMBABLE, RCQUEST_BOTH, RA_DEATH_MOUNTAIN_TRAIL, { Tricks::Tag::INTERMEDIATE }, "DMTSTR");
    OPT_TRICK(RT_DMT_HOVERS_LOWER_GS, RCQUEST_BOTH, RA_DEATH_MOUNTAIN_TRAIL, { Tricks::Tag::ADVANCED }, "DMTGSHB");
    OPT_TRICK(RT_DMT_BEAN_LOWER_GS, RCQUEST_BOTH, RA_DEATH_MOUNTAIN_TRAIL, { Tricks::Tag::EXPERT }, "DMTGSMB");
    OPT_TRICK(RT_DMT_JS_LOWER_GS, RCQUEST_BOTH, RA_DEATH_MOUNTAIN_TRAIL, { Tricks::Tag::INTERMEDIATE }, "DMTGSJS");
    OPT_TRICK(RT_DMT_CLIMB_HOVERS, RCQUEST_BOTH, RA_DEATH_MOUNTAIN_TRAIL, { Tricks::Tag::ADVANCED }, "DMTBouHB");
    OPT_TRICK(RT_DMT_UPPER_GS, RCQUEST_BOTH, RA_DEATH_MOUNTAIN_TRAIL, { Tricks::Tag::NOVICE }, "DMTGSBF");

    // disabled for now, only applies when trade quest is not shuffled so there's a timer (currently not considered in
    // logic)

    // OPT_TRICK(RT_DMT_BOLERO_BIGGORON, RCQUEST_BOTH, RA_DEATH_MOUNTAIN_TRAIL, { Tricks::Tag::INTERMEDIATE });

    OPT_TRICK(RT_GC_POT, RCQUEST_BOTH, RA_GORON_CITY, { Tricks::Tag::ADVANCED }, "GorPotChu");
    OPT_TRICK(RT_GC_POT_STRENGTH, RCQUEST_BOTH, RA_GORON_CITY, { Tricks::Tag::INTERMEDIATE }, "GorPotStr");
    OPT_TRICK(RT_GC_ROLLING_STRENGTH, RCQUEST_BOTH, RA_GORON_CITY, { Tricks::Tag::INTERMEDIATE }, "GorStrC");
    OPT_TRICK(RT_GC_LEFTMOST, RCQUEST_BOTH, RA_GORON_CITY, { Tricks::Tag::ADVANCED }, "GCMazHB");
    OPT_TRICK(RT_GC_GROTTO, RCQUEST_BOTH, RA_GORON_CITY, { Tricks::Tag::ADVANCED }, "GorGroHS");
    OPT_TRICK(RT_GC_LINK_GORON_DINS, RCQUEST_BOTH, RA_GORON_CITY, { Tricks::Tag::NOVICE }, "GorDinA");
    OPT_TRICK(RT_DMC_HOVER_BEAN_POH, RCQUEST_BOTH, RA_DEATH_MOUNTAIN_CRATER, { Tricks::Tag::NOVICE }, "DMCHB");
    OPT_TRICK(RT_DMC_BOLERO_JUMP, RCQUEST_BOTH, RA_DEATH_MOUNTAIN_CRATER, { Tricks::Tag::EXTREME }, "DMCBolJump");
    OPT_TRICK(RT_DMC_BOULDER_JS, RCQUEST_BOTH, RA_DEATH_MOUNTAIN_CRATER, { Tricks::Tag::NOVICE }, "DMCHam");
    OPT_TRICK(RT_DMC_BOULDER_SKIP, RCQUEST_BOTH, RA_DEATH_MOUNTAIN_CRATER, { Tricks::Tag::INTERMEDIATE }, "DMCULJmp");
    OPT_TRICK(RT_ZR_LOWER, RCQUEST_BOTH, RA_ZORAS_RIVER, { Tricks::Tag::INTERMEDIATE }, "ZRLJmp");
    OPT_TRICK(RT_ZR_UPPER, RCQUEST_BOTH, RA_ZORAS_RIVER, { Tricks::Tag::INTERMEDIATE }, "ZRUJmp");
    OPT_TRICK(RT_ZR_HOVERS, RCQUEST_BOTH, RA_ZORAS_RIVER, { Tricks::Tag::NOVICE }, "ZRZDHB");
    OPT_TRICK(RT_ZR_CUCCO, RCQUEST_BOTH, RA_ZORAS_RIVER, { Tricks::Tag::NOVICE }, "ZRZDCuc");
    OPT_TRICK(RT_ZD_KING_ZORA_SKIP, RCQUEST_BOTH, RA_ZORAS_DOMAIN, { Tricks::Tag::INTERMEDIATE }, "Mweep");
    OPT_TRICK(RT_ZD_GS, RCQUEST_BOTH, RA_ZORAS_DOMAIN, { Tricks::Tag::INTERMEDIATE }, "ZDGS");
    OPT_TRICK(RT_ZF_GREAT_FAIRY_WITHOUT_EXPLOSIVES, RCQUEST_BOTH, RA_ZORAS_FOUNTAIN, { Tricks::Tag::NOVICE },
              "ZFGFStr2");
    OPT_TRICK(RT_LH_LAB_WALL_GS, RCQUEST_BOTH, RA_LAKE_HYLIA, { Tricks::Tag::NOVICE }, "LHGSJS");
    OPT_TRICK(RT_LH_LAB_DIVING, RCQUEST_BOTH, RA_LAKE_HYLIA, { Tricks::Tag::NOVICE }, "LabHS");
    OPT_TRICK(RT_LH_WATER_HOOKSHOT, RCQUEST_BOTH, RA_LAKE_HYLIA, { Tricks::Tag::INTERMEDIATE }, "WTEntHS");
    OPT_TRICK(RT_GV_CRATE_HOVERS, RCQUEST_BOTH, RA_GERUDO_VALLEY, { Tricks::Tag::INTERMEDIATE }, "GVPoHHB");
    OPT_TRICK(RT_GV_CHILD_TENT, RCQUEST_BOTH, RA_GERUDO_VALLEY, { Tricks::Tag::NOVICE }, "GVTent");
    OPT_TRICK(RT_GV_CHILD_CUCCO_JUMP, RCQUEST_BOTH, RA_GERUDO_VALLEY, { Tricks::Tag::INTERMEDIATE }, "GVCUC");
    OPT_TRICK(RT_GV_HOOKSHOT_BRIDGE, RCQUEST_BOTH, RA_GERUDO_VALLEY, { Tricks::Tag::ADVANCED }, "GVHSBrg");
    OPT_TRICK(RT_PASS_GUARDS_WITH_NOTHING, RCQUEST_BOTH, RA_GERUDO_FORTRESS, { Tricks::Tag::NOVICE }, "Guards");
    OPT_TRICK(RT_GF_WASTELAND_GATE_SIDEHOP_SKIP, RCQUEST_BOTH, RA_GERUDO_FORTRESS, { Tricks::Tag::NOVICE }, "GFHWC");
    OPT_TRICK(RT_GF_ADULT_SKIP_WASTELAND_GATE, RCQUEST_BOTH, RA_GERUDO_FORTRESS, { Tricks::Tag::INTERMEDIATE },
              "GFHWA");
    OPT_TRICK(RT_GF_WARRIOR_WITH_DIFFICULT_WEAPON, RCQUEST_BOTH, RA_GERUDO_FORTRESS, { Tricks::Tag::NOVICE }, "GWDiff");
    OPT_TRICK(RT_GF_LEDGE_CLIP_INTO_GTG, RCQUEST_BOTH, RA_GERUDO_FORTRESS, { Tricks::Tag::NOVICE, Tricks::Tag::GLITCH },
              "GTGLdgClp");

    // disabled for now, can't check for being able to use bunny hood & bunny hood speedup is currently completely
    // decoupled from rando

    // OPT_TRICK(RT_HW_BUNNY_CROSSING, RCQUEST_BOTH, RA_HAUNTED_WASTELAND, { Tricks::Tag::NOVICE });

    OPT_TRICK(RT_HW_CROSSING, RCQUEST_BOTH, RA_HAUNTED_WASTELAND, { Tricks::Tag::INTERMEDIATE }, "RvrSand");
    OPT_TRICK(RT_LENS_HW, RCQUEST_BOTH, RA_HAUNTED_WASTELAND, { Tricks::Tag::INTERMEDIATE }, "HWNoLoT");
    OPT_TRICK(RT_HW_REVERSE, RCQUEST_BOTH, RA_HAUNTED_WASTELAND, { Tricks::Tag::INTERMEDIATE }, "RevHW");
    OPT_TRICK(RT_COLOSSUS_GS, RCQUEST_BOTH, RA_DESERT_COLOSSUS, { Tricks::Tag::NOVICE }, "ColGSHS");
    OPT_TRICK(RT_DEKU_BASEMENT_GS, RCQUEST_VANILLA, RA_DEKU_TREE, { Tricks::Tag::NOVICE }, "DTGSJS");
    OPT_TRICK(RT_DEKU_B1_SKIP, RCQUEST_BOTH, RA_DEKU_TREE, { Tricks::Tag::INTERMEDIATE }, "B1Skip");
    OPT_TRICK(RT_DEKU_B1_BOW_WEBS, RCQUEST_VANILLA, RA_DEKU_TREE, { Tricks::Tag::NOVICE }, "DTWebBow");
    OPT_TRICK(RT_DEKU_B1_BACKFLIP_OVER_SPIKED_LOG, RCQUEST_VANILLA, RA_DEKU_TREE, { Tricks::Tag::NOVICE }, "DTLogBF");
    OPT_TRICK(RT_DEKU_MQ_COMPASS_GS, RCQUEST_MQ, RA_DEKU_TREE, { Tricks::Tag::NOVICE }, "DTGSHam");
    OPT_TRICK(RT_DEKU_MQ_LOG, RCQUEST_MQ, RA_DEKU_TREE, { Tricks::Tag::NOVICE }, "DTLogRol");
    OPT_TRICK(RT_DC_SCARECROW_GS, RCQUEST_VANILLA, RA_DODONGOS_CAVERN, { Tricks::Tag::NOVICE }, "DCArmos");
    OPT_TRICK(RT_DC_VINES_GS, RCQUEST_VANILLA, RA_DODONGOS_CAVERN, { Tricks::Tag::NOVICE }, "DCGSLS");
    OPT_TRICK(RT_DC_STAIRS_WITH_BOW, RCQUEST_VANILLA, RA_DODONGOS_CAVERN, { Tricks::Tag::NOVICE }, "DCStaBow");
    OPT_TRICK(RT_DC_SLINGSHOT_SKIP, RCQUEST_VANILLA, RA_DODONGOS_CAVERN, { Tricks::Tag::EXPERT }, "DCSliSkp");
    OPT_TRICK(RT_DC_SCRUB_ROOM, RCQUEST_VANILLA, RA_DODONGOS_CAVERN, { Tricks::Tag::NOVICE }, "DCSrbStr");
    OPT_TRICK(RT_DC_HAMMER_FLOOR, RCQUEST_BOTH, RA_DODONGOS_CAVERN, { Tricks::Tag::NOVICE }, "KDHamFl");
    OPT_TRICK(RT_DC_DODONGO_CHU, RCQUEST_BOTH, RA_DODONGOS_CAVERN, { Tricks::Tag::ADVANCED }, "KDChu");
    OPT_TRICK(RT_DC_MQ_STAIRS_WITH_ONLY_STRENGTH, RCQUEST_MQ, RA_DODONGOS_CAVERN, { Tricks::Tag::NOVICE }, "DCStaStr");
    OPT_TRICK(RT_DC_MQ_CHILD_BOMBS, RCQUEST_MQ, RA_DODONGOS_CAVERN, { Tricks::Tag::ADVANCED }, "DCLobyJS");
    OPT_TRICK(RT_DC_MQ_CHILD_EYES, RCQUEST_MQ, RA_DODONGOS_CAVERN, { Tricks::Tag::EXPERT }, "DCEyeStrC");
    OPT_TRICK(RT_DC_MQ_ADULT_EYES, RCQUEST_MQ, RA_DODONGOS_CAVERN, { Tricks::Tag::ADVANCED }, "DCEyeStrA");
    OPT_TRICK(RT_DC_EYES_CHU, RCQUEST_BOTH, RA_DODONGOS_CAVERN, { Tricks::Tag::ADVANCED }, "DCEyeChu");
    OPT_TRICK(RT_JABU_BOSS_HOVER, RCQUEST_VANILLA, RA_JABU_JABUS_BELLY, { Tricks::Tag::INTERMEDIATE }, "JbuBoxHB");
    OPT_TRICK(RT_JABU_NEAR_BOSS_RANGED, RCQUEST_BOTH, RA_JABU_JABUS_BELLY, { Tricks::Tag::NOVICE }, "JbuBosPrj");
    OPT_TRICK(RT_JABU_NEAR_BOSS_EXPLOSIVES, RCQUEST_VANILLA, RA_JABU_JABUS_BELLY, { Tricks::Tag::INTERMEDIATE },
              "JbuBosExp");
    OPT_TRICK(RT_JABU_B1_CUBE_HOVER, RCQUEST_VANILLA, RA_JABU_JABUS_BELLY, { Tricks::Tag::NOVICE }, "JbuJigHB");
    OPT_TRICK(RT_LENS_JABU_MQ, RCQUEST_MQ, RA_JABU_JABUS_BELLY, { Tricks::Tag::NOVICE }, "JbuLoT");
    OPT_TRICK(RT_JABU_MQ_RANG_JUMP, RCQUEST_MQ, RA_JABU_JABUS_BELLY, { Tricks::Tag::ADVANCED }, "JbuSwtRng");
    OPT_TRICK(RT_JABU_MQ_SOT_GS, RCQUEST_MQ, RA_JABU_JABUS_BELLY, { Tricks::Tag::INTERMEDIATE }, "JbuSoTRng");
    OPT_TRICK(RT_JABU_BARINADE_POTS, RCQUEST_BOTH, RA_JABU_JABUS_BELLY, { Tricks::Tag::ADVANCED }, "BariPot");
    OPT_TRICK(RT_LENS_BOTW, RCQUEST_VANILLA, RA_BOTTOM_OF_THE_WELL, { Tricks::Tag::NOVICE }, "BWLoT");
    OPT_TRICK(RT_BOTTOM_OF_THE_WELL_NAVI_DIVE, RCQUEST_BOTH, RA_BOTTOM_OF_THE_WELL,
              { Tricks::Tag::NOVICE, Tricks::Tag::GLITCH }, "KakNviD");
    OPT_TRICK(RT_BOTW_CHILD_DEADHAND, RCQUEST_BOTH, RA_BOTTOM_OF_THE_WELL, { Tricks::Tag::NOVICE }, "DHDiff");
    OPT_TRICK(RT_BOTW_BASEMENT, RCQUEST_VANILLA, RA_BOTTOM_OF_THE_WELL, { Tricks::Tag::NOVICE }, "BWBmbFl");
    // RANDOTODO with doorsanity, this can be relevant in Vanilla
    OPT_TRICK(RT_BOTW_PITS, RCQUEST_MQ, RA_BOTTOM_OF_THE_WELL, { Tricks::Tag::NOVICE }, "BWPitJmp");
    OPT_TRICK(RT_BOTW_MQ_DEADHAND_KEY, RCQUEST_MQ, RA_BOTTOM_OF_THE_WELL, { Tricks::Tag::NOVICE }, "BWKeyRng");
    OPT_TRICK(RT_FOREST_FIRST_GS, RCQUEST_VANILLA, RA_FOREST_TEMPLE, { Tricks::Tag::NOVICE }, "FT1stGS");
    OPT_TRICK(RT_FOREST_COURTYARD_EAST_GS, RCQUEST_VANILLA, RA_FOREST_TEMPLE, { Tricks::Tag::NOVICE }, "FTGSRng");
    OPT_TRICK(RT_FOREST_VINES, RCQUEST_BOTH, RA_FOREST_TEMPLE, { Tricks::Tag::NOVICE }, "FTVineHS");
    OPT_TRICK(RT_FOREST_COURTYARD_LEDGE, RCQUEST_BOTH, RA_FOREST_TEMPLE, { Tricks::Tag::NOVICE }, "FTLdgHB");
    OPT_TRICK(RT_FOREST_DOORFRAME, RCQUEST_BOTH, RA_FOREST_TEMPLE, { Tricks::Tag::ADVANCED }, "FTDoorHB");
    OPT_TRICK(RT_FOREST_OUTSIDE_BACKDOOR, RCQUEST_BOTH, RA_FOREST_TEMPLE, { Tricks::Tag::ADVANCED }, "FTBlkJS");
    OPT_TRICK(RT_FOREST_COURTYARD_HEARTS_BOOMERANG, RCQUEST_BOTH, RA_FOREST_TEMPLE, { Tricks::Tag::NOVICE },
              "FTHrtRng");
    OPT_TRICK(RT_FOREST_WELL_SWIM, RCQUEST_MQ, RA_FOREST_TEMPLE, { Tricks::Tag::NOVICE }, "FTSwim");
    OPT_TRICK(RT_FOREST_MQ_BLOCK_PUZZLE, RCQUEST_MQ, RA_FOREST_TEMPLE, { Tricks::Tag::NOVICE }, "FTBlkChu");
    // Child with hovers cannot do this from the lower floor, and must go to the upper floor which needs goron bracelet.
    // Adult can do this with hammer and KSword, But child cannot.
    OPT_TRICK(RT_FOREST_MQ_JS_HALLWAY_SWITCH, RCQUEST_MQ, RA_FOREST_TEMPLE, { Tricks::Tag::NOVICE }, "FTTwstJS");
    OPT_TRICK(RT_FOREST_MQ_HOOKSHOT_HALLWAY_SWITCH, RCQUEST_MQ, RA_FOREST_TEMPLE, { Tricks::Tag::INTERMEDIATE },
              "FTTwstHS");
    OPT_TRICK(RT_FOREST_MQ_RANG_HALLWAY_SWITCH, RCQUEST_MQ, RA_FOREST_TEMPLE, { Tricks::Tag::INTERMEDIATE },
              "FTTwstRng");
    OPT_TRICK(RT_FOREST_MQ_CHILD_DOORFRAME, RCQUEST_MQ, RA_FOREST_TEMPLE, { Tricks::Tag::NOVICE }, "FTDoorC");
    // Is also used in MQ logic, but has no practical effect there as of now
    OPT_TRICK(RT_FIRE_SOT, RCQUEST_VANILLA, RA_FIRE_TEMPLE, { Tricks::Tag::INTERMEDIATE }, "FISoTSkp");
    OPT_TRICK(RT_FIRE_STRENGTH, RCQUEST_VANILLA, RA_FIRE_TEMPLE, { Tricks::Tag::INTERMEDIATE }, "FIStrSkp");
    OPT_TRICK(RT_FIRE_SCARECROW, RCQUEST_VANILLA, RA_FIRE_TEMPLE, { Tricks::Tag::EXPERT }, "PixelShot");
    OPT_TRICK(RT_FIRE_SKIP_FLAME_WALLS, RCQUEST_VANILLA, RA_FIRE_TEMPLE, { Tricks::Tag::INTERMEDIATE }, "FIRWAL");
    OPT_TRICK(RT_FIRE_MQ_NEAR_BOSS, RCQUEST_MQ, RA_FIRE_TEMPLE, { Tricks::Tag::NOVICE }, "FICrtTor");
    OPT_TRICK(RT_FIRE_MQ_BLOCKED_CHEST, RCQUEST_MQ, RA_FIRE_TEMPLE, { Tricks::Tag::INTERMEDIATE }, "FIHSSkp");
    OPT_TRICK(RT_FIRE_MQ_BK_CHEST, RCQUEST_MQ, RA_FIRE_TEMPLE, { Tricks::Tag::INTERMEDIATE }, "FIBowSkp");
    OPT_TRICK(RT_FIRE_MQ_CLIMB, RCQUEST_MQ, RA_FIRE_TEMPLE, { Tricks::Tag::NOVICE }, "FIFirSkp");
    OPT_TRICK(RT_FIRE_MQ_MAZE_SIDE_ROOM, RCQUEST_MQ, RA_FIRE_TEMPLE, { Tricks::Tag::NOVICE }, "FIBoxSkp");
    OPT_TRICK(RT_FIRE_MQ_MAZE_HOVERS, RCQUEST_MQ, RA_FIRE_TEMPLE, { Tricks::Tag::NOVICE }, "FIMazHB");
    OPT_TRICK(RT_FIRE_MQ_MAZE_JUMP, RCQUEST_MQ, RA_FIRE_TEMPLE, { Tricks::Tag::INTERMEDIATE }, "FIMazJmp");
    OPT_TRICK(RT_FIRE_MQ_ABOVE_MAZE_GS, RCQUEST_MQ, RA_FIRE_TEMPLE, { Tricks::Tag::INTERMEDIATE }, "FIGSLS");
    OPT_TRICK(RT_WATER_LONGSHOT_TORCH, RCQUEST_VANILLA, RA_WATER_TEMPLE, { Tricks::Tag::NOVICE }, "WTTorLS");
    OPT_TRICK(RT_WATER_CRACKED_WALL_HOVERS, RCQUEST_VANILLA, RA_WATER_TEMPLE, { Tricks::Tag::NOVICE }, "WTCrkHB");
    OPT_TRICK(RT_WATER_CRACKED_WALL, RCQUEST_VANILLA, RA_WATER_TEMPLE, { Tricks::Tag::INTERMEDIATE }, "WTCrkJmp");
    OPT_TRICK(RT_WATER_BK_REGION, RCQUEST_VANILLA, RA_WATER_TEMPLE, { Tricks::Tag::INTERMEDIATE }, "WTBKHB");
    OPT_TRICK(RT_WATER_NORTH_BASEMENT_LEDGE_JUMP, RCQUEST_BOTH, RA_WATER_TEMPLE, { Tricks::Tag::INTERMEDIATE },
              "WTBolLdg");
    // Also used in MQ logic, but won't be relevent unless a way to enter tower without irons exists (likely a clip +
    // swim)
    OPT_TRICK(RT_WATER_FW_CENTRAL_GS, RCQUEST_VANILLA, RA_WATER_TEMPLE, { Tricks::Tag::NOVICE }, "WTGSFW");
    OPT_TRICK(RT_WATER_IRONS_CENTRAL_GS, RCQUEST_VANILLA, RA_WATER_TEMPLE, { Tricks::Tag::NOVICE }, "WTGSIB");
    OPT_TRICK(RT_WATER_CENTRAL_BOW, RCQUEST_VANILLA, RA_WATER_TEMPLE, { Tricks::Tag::ADVANCED }, "WTBowJmp");
    OPT_TRICK(RT_WATER_HOOKSHOT_FALLING_PLATFORM_GS, RCQUEST_VANILLA, RA_WATER_TEMPLE, { Tricks::Tag::NOVICE },
              "WTWfalHS");
    OPT_TRICK(RT_WATER_RANG_FALLING_PLATFORM_GS, RCQUEST_VANILLA, RA_WATER_TEMPLE, { Tricks::Tag::INTERMEDIATE },
              "WTWfalRng");
    OPT_TRICK(RT_WATER_RIVER_GS, RCQUEST_VANILLA, RA_WATER_TEMPLE, { Tricks::Tag::INTERMEDIATE }, "WTRvrLS");
    OPT_TRICK(RT_WATER_DRAGON_JUMP_DIVE, RCQUEST_BOTH, RA_WATER_TEMPLE, { Tricks::Tag::NOVICE }, "WTDrgJmp");
    OPT_TRICK(RT_WATER_ADULT_DRAGON, RCQUEST_VANILLA, RA_WATER_TEMPLE, { Tricks::Tag::NOVICE }, "WTDrgA");
    OPT_TRICK(RT_WATER_CHILD_DRAGON, RCQUEST_VANILLA, RA_WATER_TEMPLE, { Tricks::Tag::ADVANCED }, "WTDrgC");
    OPT_TRICK(RT_WATER_MQ_CENTRAL_PILLAR, RCQUEST_MQ, RA_WATER_TEMPLE, { Tricks::Tag::NOVICE }, "WTCntFA");
    OPT_TRICK(RT_WATER_IRON_BOOTS_LEDGE_GRAB, RCQUEST_BOTH, RA_WATER_TEMPLE,
              { Tricks::Tag::NOVICE, Tricks::Tag::GLITCH }, "IBSrfLG");
    OPT_TRICK(RT_WATER_INVISIBLE_HOOKSHOT_TARGET, RCQUEST_BOTH, RA_WATER_TEMPLE, { Tricks::Tag::NOVICE }, "WTTarg");
    OPT_TRICK(RT_WATER_MORPHA_WITHOUT_HOOKSHOT, RCQUEST_BOTH, RA_WATER_TEMPLE, { Tricks::Tag::EXTREME }, "MorphDiff");
    OPT_TRICK(RT_LENS_SHADOW, RCQUEST_VANILLA, RA_SHADOW_TEMPLE, { Tricks::Tag::NOVICE }, "STStLoT");
    OPT_TRICK(RT_LENS_SHADOW_PLATFORM, RCQUEST_VANILLA, RA_SHADOW_TEMPLE, { Tricks::Tag::NOVICE }, "STMvLot");
    OPT_TRICK(RT_LENS_BONGO, RCQUEST_BOTH, RA_SHADOW_TEMPLE, { Tricks::Tag::NOVICE }, "BNGLoT");
    OPT_TRICK(RT_SHADOW_UMBRELLA_HOVER, RCQUEST_BOTH, RA_SHADOW_TEMPLE, { Tricks::Tag::EXPERT }, "STUmbSkp");
    OPT_TRICK(RT_SHADOW_UMBRELLA_CLIP, RCQUEST_BOTH, RA_SHADOW_TEMPLE, { Tricks::Tag::NOVICE, Tricks::Tag::GLITCH },
              "STUmbClp");
    OPT_TRICK(RT_SHADOW_UMBRELLA_GS, RCQUEST_BOTH, RA_SHADOW_TEMPLE, { Tricks::Tag::EXPERT }, "STUmbHB");
    OPT_TRICK(RT_SHADOW_FREESTANDING_KEY, RCQUEST_VANILLA, RA_SHADOW_TEMPLE, { Tricks::Tag::NOVICE }, "STPotChu");
    OPT_TRICK(RT_SHADOW_STATUE, RCQUEST_BOTH, RA_SHADOW_TEMPLE, { Tricks::Tag::INTERMEDIATE }, "STStaChu");
    OPT_TRICK(RT_SHADOW_BONGO, RCQUEST_BOTH, RA_SHADOW_TEMPLE, { Tricks::Tag::INTERMEDIATE }, "BngNoPrg");
    OPT_TRICK(RT_LENS_SHADOW_MQ, RCQUEST_MQ, RA_SHADOW_TEMPLE, { Tricks::Tag::NOVICE }, "STMQStLoT");
    OPT_TRICK(RT_LENS_SHADOW_MQ_INVISIBLE_BLADES, RCQUEST_MQ, RA_SHADOW_TEMPLE, { Tricks::Tag::NOVICE }, "STBldLoT");
    OPT_TRICK(RT_LENS_SHADOW_MQ_PLATFORM, RCQUEST_MQ, RA_SHADOW_TEMPLE, { Tricks::Tag::NOVICE }, "STMQMvLot");
    OPT_TRICK(RT_LENS_SHADOW_MQ_DEADHAND, RCQUEST_MQ, RA_SHADOW_TEMPLE, { Tricks::Tag::NOVICE }, "STDHLoT");
    OPT_TRICK(RT_SHADOW_MQ_GAP, RCQUEST_MQ, RA_SHADOW_TEMPLE, { Tricks::Tag::INTERMEDIATE }, "STTSLS");
    OPT_TRICK(RT_SHADOW_MQ_INVISIBLE_BLADES, RCQUEST_MQ, RA_SHADOW_TEMPLE, { Tricks::Tag::INTERMEDIATE }, "STSoTSkp");
    OPT_TRICK(RT_SHADOW_MQ_HUGE_PIT, RCQUEST_MQ, RA_SHADOW_TEMPLE, { Tricks::Tag::INTERMEDIATE }, "STPitJmp");
    OPT_TRICK(RT_SHADOW_MQ_WINDY_WALKWAY, RCQUEST_MQ, RA_SHADOW_TEMPLE, { Tricks::Tag::INTERMEDIATE }, "STWindSkp");
    OPT_TRICK(RT_LENS_SPIRIT, RCQUEST_VANILLA, RA_SPIRIT_TEMPLE, { Tricks::Tag::NOVICE }, "SPLoT");
    OPT_TRICK(RT_SPIRIT_CHILD_CHU, RCQUEST_VANILLA, RA_SPIRIT_TEMPLE, { Tricks::Tag::NOVICE }, "SPBrgChu");
    OPT_TRICK(RT_SPIRIT_WEST_LEDGE, RCQUEST_BOTH, RA_SPIRIT_TEMPLE, { Tricks::Tag::NOVICE }, "SPWeRng");
    OPT_TRICK(RT_SPIRIT_LOWER_ADULT_SWITCH, RCQUEST_VANILLA, RA_SPIRIT_TEMPLE, { Tricks::Tag::ADVANCED }, "SPSwtBmb");
    OPT_TRICK(RT_SPIRIT_STATUE_JUMP, RCQUEST_BOTH, RA_SPIRIT_TEMPLE, { Tricks::Tag::INTERMEDIATE }, "SPHndJmp");

    // disabled since "Spirit Temple boss shortcuts" (pre-lowers the platform where you break the statues face) isn't a
    // setting in ship

    // OPT_TRICK(RT_SPIRIT_PLATFORM_HOOKSHOT, RCQUEST_VANILLA, RA_SPIRIT_TEMPLE, { Tricks::Tag::INTERMEDIATE });

    OPT_TRICK(RT_SPIRIT_MAP_CHEST, RCQUEST_VANILLA, RA_SPIRIT_TEMPLE, { Tricks::Tag::NOVICE }, "SPMapBow");
    OPT_TRICK(RT_SPIRIT_SUN_CHEST, RCQUEST_VANILLA, RA_SPIRIT_TEMPLE, { Tricks::Tag::ADVANCED }, "SPSUNBOW");
    OPT_TRICK(RT_SPIRIT_WALL, RCQUEST_VANILLA, RA_SPIRIT_TEMPLE, { Tricks::Tag::INTERMEDIATE }, "SPWall");
    OPT_TRICK(RT_LENS_SPIRIT_MQ, RCQUEST_MQ, RA_SPIRIT_TEMPLE, { Tricks::Tag::NOVICE }, "SPMQLoT");
    OPT_TRICK(RT_SPIRIT_MQ_SUN_BLOCK_SOT, RCQUEST_MQ, RA_SPIRIT_TEMPLE, { Tricks::Tag::INTERMEDIATE }, "SPBluSkp");
    OPT_TRICK(RT_SPIRIT_MQ_SUN_BLOCK_GS, RCQUEST_MQ, RA_SPIRIT_TEMPLE, { Tricks::Tag::INTERMEDIATE }, "SPBlkGS");
    OPT_TRICK(RT_SPIRIT_MQ_LOWER_ADULT, RCQUEST_MQ, RA_SPIRIT_TEMPLE, { Tricks::Tag::INTERMEDIATE }, "SPTorDin");
    OPT_TRICK(RT_SPIRIT_MQ_FROZEN_EYE, RCQUEST_MQ, RA_SPIRIT_TEMPLE, { Tricks::Tag::NOVICE }, "SPFEBow");
    OPT_TRICK(RT_ICE_STALAGMITE_CLIP, RCQUEST_BOTH, RA_ICE_CAVERN, { Tricks::Tag::NOVICE }, "StalClp");
    OPT_TRICK(RT_ICE_STALAGMITE_HOOKSHOT, RCQUEST_BOTH, RA_ICE_CAVERN, { Tricks::Tag::NOVICE }, "StalHS");
    OPT_TRICK(RT_ICE_BLOCK_GS, RCQUEST_VANILLA, RA_ICE_CAVERN, { Tricks::Tag::INTERMEDIATE }, "ICBlkHB");
    OPT_TRICK(RT_ICE_MQ_RED_ICE_GS, RCQUEST_MQ, RA_ICE_CAVERN, { Tricks::Tag::INTERMEDIATE }, "ICNoSoT");
    OPT_TRICK(RT_LENS_GTG, RCQUEST_VANILLA, RA_GERUDO_TRAINING_GROUND, { Tricks::Tag::NOVICE }, "GTGLoT");
    OPT_TRICK(RT_GTG_WITHOUT_HOOKSHOT, RCQUEST_VANILLA, RA_GERUDO_TRAINING_GROUND, { Tricks::Tag::INTERMEDIATE },
              "GTGNoHS");
    OPT_TRICK(RT_GTG_FAKE_WALL, RCQUEST_BOTH, RA_GERUDO_TRAINING_GROUND, { Tricks::Tag::NOVICE }, "GTGWallHB");
    OPT_TRICK(RT_GTG_LAVA_JUMP, RCQUEST_BOTH, RA_GERUDO_TRAINING_GROUND, { Tricks::Tag::INTERMEDIATE }, "GTGLavaJmp");
    OPT_TRICK(RT_GTG_STATUE_JUMP, RCQUEST_BOTH, RA_GERUDO_TRAINING_GROUND, { Tricks::Tag::INTERMEDIATE }, "GTGStJmp");
    OPT_TRICK(RT_LENS_GTG_MQ, RCQUEST_MQ, RA_GERUDO_TRAINING_GROUND, { Tricks::Tag::NOVICE }, "GTGMQLoT");
    OPT_TRICK(RT_GTG_MQ_WITH_HOOKSHOT, RCQUEST_MQ, RA_GERUDO_TRAINING_GROUND, { Tricks::Tag::NOVICE }, "GTGMQHS");
    OPT_TRICK(RT_GTG_MQ_WITHOUT_HOOKSHOT, RCQUEST_MQ, RA_GERUDO_TRAINING_GROUND, { Tricks::Tag::INTERMEDIATE },
              "GTGMQNoHS");
    OPT_TRICK(RT_LENS_GANON, RCQUEST_VANILLA, RA_GANONS_CASTLE, { Tricks::Tag::NOVICE }, "GCLoT");
    OPT_TRICK(RT_GANON_SPIRIT_TRIAL_HOOKSHOT, RCQUEST_VANILLA, RA_GANONS_CASTLE, { Tricks::Tag::NOVICE }, "GCNoHS");
    OPT_TRICK(RT_LENS_GANON_MQ, RCQUEST_MQ, RA_GANONS_CASTLE, { Tricks::Tag::NOVICE }, "GCMQLoT");
    OPT_TRICK(RT_GANON_MQ_FIRE_TRIAL, RCQUEST_MQ, RA_GANONS_CASTLE, { Tricks::Tag::ADVANCED }, "GCFTHS");
    OPT_TRICK(RT_GANON_MQ_SHADOW_TRIAL, RCQUEST_MQ, RA_GANONS_CASTLE, { Tricks::Tag::NOVICE }, "GCSTBow");
    OPT_TRICK(RT_GANON_MQ_LIGHT_TRIAL, RCQUEST_MQ, RA_GANONS_CASTLE, { Tricks::Tag::INTERMEDIATE }, "GCFirWal");

    for (auto trick : mTrickSettings) {
        if (trick.GetNameTag() != "") {
            if (StaticData::trickToEnum.contains(trick.GetNameTag())) {
                SPDLOG_ERROR("REPEATED TRICK NAME TAG " + trick.GetName());
                assert(false);
            } else {
                StaticData::trickToEnum[trick.GetNameTag()] = trick.GetKey();
            }
        }
    }

    mOptionGroups[RSG_LOGIC] = OptionGroup::SubGroup("Logic Options", {
                                                                          &mOptions[RSK_LOGIC_RULES],
                                                                          &mOptions[RSK_ALL_LOCATIONS_REACHABLE],
                                                                          &mOptions[RSK_SKULLS_SUNS_SONG],
                                                                          &mOptions[RSK_BIG_POE_COUNT],
                                                                      });
    // TODO: Exclude Locations Menus
    mTricksByArea.clear();
    std::vector<Option*> tricksOption;
    tricksOption.reserve(mTrickSettings.size());
    for (int i = 0; i < RT_MAX; i++) {
        auto trick = &mTrickSettings[i];
        if (!trick->GetName().empty()) {
            tricksOption.push_back(trick);
            mTrickNameToEnum[std::string(trick->GetName())] = static_cast<RandomizerTrick>(i);
            mTricksByArea[trick->GetArea()].push_back(static_cast<RandomizerTrick>(i));
        }
    }
    mOptionGroups[RSG_TRICKS] = OptionGroup::SubGroup("Logical Tricks", tricksOption);
    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_BIG_POE_COUNT],
                                                                      &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_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<OptionGroup*>{
                                                                            &mOptionGroups[RSG_ITEM_POOL],
                                                                            &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],
                                  &mOptions[RSK_MEDALLION_LOCKED_TRIALS],
                              },
                              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_GANONS_TOWER_ENTRANCE], &mOptions[RSK_SHUFFLE_OVERWORLD_ENTRANCES],
          &mOptions[RSK_SHUFFLE_INTERIOR_ENTRANCES], &mOptions[RSK_SHUFFLE_THIEVES_HIDEOUT_ENTRANCES],
          &mOptions[RSK_SHUFFLE_GROTTO_ENTRANCES], &mOptions[RSK_SHUFFLE_OWL_DROPS], &mOptions[RSK_SHUFFLE_WARP_SONGS],
          &mOptions[RSK_SHUFFLE_OVERWORLD_SPAWNS], &mOptions[RSK_DECOUPLED_ENTRANCES],
          &mOptions[RSK_MIXED_ENTRANCE_POOLS], &mOptions[RSK_MIX_DUNGEON_ENTRANCES], &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::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<OptionGroup*>{ &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<OptionGroup*>{
                                                                         &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_WONDER_ITEMS],
                                  &mOptions[RSK_SHUFFLE_BEEHIVES],
                                  &mOptions[RSK_SHUFFLE_COWS],
                                  &mOptions[RSK_SHUFFLE_POTS],
                                  &mOptions[RSK_SHUFFLE_GRASS],
                                  &mOptions[RSK_SHUFFLE_CRATES],
                                  &mOptions[RSK_SHUFFLE_BOULDERS],
                                  &mOptions[RSK_SHUFFLE_ROCKS],
                                  &mOptions[RSK_SHUFFLE_TREES],
                                  &mOptions[RSK_SHUFFLE_BUSHES],
                                  &mOptions[RSK_SHUFFLE_ICICLES],
                                  &mOptions[RSK_SHUFFLE_RED_ICE],
                                  &mOptions[RSK_SHUFFLE_SIGNS],
                                  &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],
                                  &mOptions[RSK_SHUFFLE_BUTTERFLY_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],
                                  &mOptions[RSK_SHOPSANITY_PRICES],
                                  &mOptions[RSK_SHOPSANITY_PRICES_FIXED_PRICE],
                                  &mOptions[RSK_SHOPSANITY_PRICES_RANGE_1],
                                  &mOptions[RSK_SHOPSANITY_PRICES_RANGE_2],
                                  &mOptions[RSK_SHOPSANITY_PRICES_NO_WALLET_WEIGHT],
                                  &mOptions[RSK_SHOPSANITY_PRICES_CHILD_WALLET_WEIGHT],
                                  &mOptions[RSK_SHOPSANITY_PRICES_ADULT_WALLET_WEIGHT],
                                  &mOptions[RSK_SHOPSANITY_PRICES_GIANT_WALLET_WEIGHT],
                                  &mOptions[RSK_SHOPSANITY_PRICES_TYCOON_WALLET_WEIGHT],
                                  &mOptions[RSK_SHOPSANITY_PRICES_AFFORDABLE],
                                  &mOptions[RSK_SHUFFLE_SCRUBS],
                                  &mOptions[RSK_SCRUBS_PRICES],
                                  &mOptions[RSK_SCRUBS_PRICES_FIXED_PRICE],
                                  &mOptions[RSK_SCRUBS_PRICES_RANGE_1],
                                  &mOptions[RSK_SCRUBS_PRICES_RANGE_2],
                                  &mOptions[RSK_SCRUBS_PRICES_NO_WALLET_WEIGHT],
                                  &mOptions[RSK_SCRUBS_PRICES_CHILD_WALLET_WEIGHT],
                                  &mOptions[RSK_SCRUBS_PRICES_ADULT_WALLET_WEIGHT],
                                  &mOptions[RSK_SCRUBS_PRICES_GIANT_WALLET_WEIGHT],
                                  &mOptions[RSK_SCRUBS_PRICES_TYCOON_WALLET_WEIGHT],
                                  &mOptions[RSK_SCRUBS_PRICES_AFFORDABLE],
                                  &mOptions[RSK_SHUFFLE_MERCHANTS],
                                  &mOptions[RSK_MERCHANT_PRICES],
                                  &mOptions[RSK_MERCHANT_PRICES_FIXED_PRICE],
                                  &mOptions[RSK_MERCHANT_PRICES_RANGE_1],
                                  &mOptions[RSK_MERCHANT_PRICES_RANGE_2],
                                  &mOptions[RSK_MERCHANT_PRICES_NO_WALLET_WEIGHT],
                                  &mOptions[RSK_MERCHANT_PRICES_CHILD_WALLET_WEIGHT],
                                  &mOptions[RSK_MERCHANT_PRICES_ADULT_WALLET_WEIGHT],
                                  &mOptions[RSK_MERCHANT_PRICES_GIANT_WALLET_WEIGHT],
                                  &mOptions[RSK_MERCHANT_PRICES_TYCOON_WALLET_WEIGHT],
                                  &mOptions[RSK_MERCHANT_PRICES_AFFORDABLE],
                                  &mOptions[RSK_SHUFFLE_BEGGAR],
                              },
                              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_GRAB],
                                                                                 &mOptions[RSK_SHUFFLE_CLIMB],
                                                                                 &mOptions[RSK_SHUFFLE_CRAWL],
                                                                                 &mOptions[RSK_SHUFFLE_SPEAK],
                                                                                 &mOptions[RSK_SHUFFLE_OPEN_CHEST],
                                                                                 &mOptions[RSK_SHUFFLE_BEAN_SOULS],
                                                                                 &mOptions[RSK_ROCS_FEATHER],
                                                                                 &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",
                              {
                                  &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_BASE_ICE_TRAPS],
                                                                      &mOptions[RSK_ADDITIONAL_ICE_TRAPS],
                                                                      &mOptions[RSK_ICE_TRAP_PERCENT],
                                                                  },
                                                                  WidgetContainerType::SECTION);
    mOptionGroups[RSG_MENU_COLUMN_HINTS_TRAPS] =
        OptionGroup::SubGroup("",
                              std::initializer_list<OptionGroup*>{ &mOptionGroups[RSG_MENU_SECTION_HINTS],
                                                                   &mOptionGroups[RSG_MENU_SECTION_TRAPS] },
                              WidgetContainerType::COLUMN);
    mOptionGroups[RSG_MENU_SECTION_STATIC_HINTS] = OptionGroup::SubGroup(
        "Static Hints", { &mOptions[RSK_TOT_ALTAR_HINT],     &mOptions[RSK_GANONDORF_HINT],
                          &mOptions[RSK_SHEIK_LA_HINT],      &mOptions[RSK_BOSS_KEY_HINT],
                          &mOptions[RSK_DAMPES_DIARY_HINT],  &mOptions[RSK_GREG_HINT],
                          &mOptions[RSK_LOACH_HINT],         &mOptions[RSK_SARIA_HINT],
                          &mOptions[RSK_MIDO_HINT],          &mOptions[RSK_FROGS_HINT],
                          &mOptions[RSK_OOT_HINT],           &mOptions[RSK_BIGGORON_HINT],
                          &mOptions[RSK_BIG_POES_HINT],      &mOptions[RSK_CHICKENS_HINT],
                          &mOptions[RSK_MALON_HINT],         &mOptions[RSK_HBA_HINT],
                          &mOptions[RSK_FISHING_POLE_HINT],  &mOptions[RSK_WARP_SONG_HINTS],
                          &mOptions[RSK_SCRUB_TEXT_HINT],    &mOptions[RSK_MERCHANT_TEXT_HINT],
                          &mOptions[RSK_KAK_10_SKULLS_HINT], &mOptions[RSK_KAK_20_SKULLS_HINT],
                          &mOptions[RSK_KAK_30_SKULLS_HINT], &mOptions[RSK_KAK_40_SKULLS_HINT],
                          &mOptions[RSK_KAK_50_SKULLS_HINT], &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_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<OptionGroup*>{
                                  &mOptionGroups[RSG_MENU_COLUMN_HINTS_TRAPS],
                                  &mOptionGroups[RSG_MENU_COLUMN_STATIC_HINTS],
                              },
                              WidgetContainerType::TABLE);
    mOptionGroups[RSG_MENU_SECTION_STARTING_EQUIPS] = OptionGroup::SubGroup(
        "Equips",
        { &mOptions[RSK_LINKS_POCKET], &mOptions[RSK_LINKS_POCKET_REWARD], &mOptions[RSK_STARTING_KOKIRI_SWORD],
          &mOptions[RSK_STARTING_MASTER_SWORD], &mOptions[RSK_STARTING_DEKU_SHIELD] },
        WidgetContainerType::SECTION);
    mOptionGroups[RSG_MENU_SECTION_STARTING_ITEMS] = OptionGroup::SubGroup("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::SECTION);
    mOptionGroups[RSG_MENU_COLUMN_STARTING_EQUIPMENT] =
        OptionGroup::SubGroup("",
                              std::initializer_list<OptionGroup*>{
                                  &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<OptionGroup*>{
                                  &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<OptionGroup*>{
                                  &mOptionGroups[RSG_MENU_COLUMN_STARTING_EQUIPMENT],
                                  &mOptionGroups[RSG_MENU_COLUMN_STARTING_SONGS],
                              },
                              WidgetContainerType::TABLE);
    mOptionGroups[RSG_OPEN] = OptionGroup("Open Settings", {
                                                               &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_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_BRIDGE_OPTIONS],
                                                               &mOptions[RSK_GANONS_TRIALS],
                                                               &mOptions[RSK_TRIAL_COUNT],
                                                               &mOptions[RSK_MEDALLION_LOCKED_TRIALS],
                                                           });
    mOptionGroups[RSG_WORLD] = OptionGroup("World Settings", {
                                                                 &mOptions[RSK_STARTING_AGE],
                                                                 &mOptions[RSK_SHUFFLE_ENTRANCES],
                                                                 &mOptions[RSK_SHUFFLE_DUNGEON_ENTRANCES],
                                                                 &mOptions[RSK_SHUFFLE_BOSS_ENTRANCES],
                                                                 &mOptions[RSK_SHUFFLE_GANONS_TOWER_ENTRANCE],
                                                                 &mOptions[RSK_SHUFFLE_OVERWORLD_ENTRANCES],
                                                                 &mOptions[RSK_SHUFFLE_INTERIOR_ENTRANCES],
                                                                 &mOptions[RSK_SHUFFLE_THIEVES_HIDEOUT_ENTRANCES],
                                                                 &mOptions[RSK_SHUFFLE_GROTTO_ENTRANCES],
                                                                 &mOptions[RSK_SHUFFLE_OWL_DROPS],
                                                                 &mOptions[RSK_SHUFFLE_WARP_SONGS],
                                                                 &mOptions[RSK_SHUFFLE_OVERWORLD_SPAWNS],
                                                                 &mOptions[RSK_MIXED_ENTRANCE_POOLS],
                                                                 &mOptions[RSK_MIX_DUNGEON_ENTRANCES],
                                                                 &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],
                                                                 &mOptions[RSK_DECOUPLED_ENTRANCES],
                                                                 &mOptions[RSK_BOMBCHU_BAG],
                                                                 &mOptions[RSK_ENABLE_BOMBCHU_DROPS],
                                                                 &mOptions[RSK_TRIFORCE_HUNT],
                                                                 &mOptions[RSK_TRIFORCE_HUNT_PIECES_TOTAL],
                                                                 &mOptions[RSK_TRIFORCE_HUNT_PIECES_REQUIRED],
                                                                 &mOptions[RSK_MQ_DUNGEON_RANDOM],
                                                                 &mOptions[RSK_MQ_DUNGEON_COUNT],
                                                                 &mOptions[RSK_MQ_DUNGEON_SET],
                                                             });
    mOptionGroups[RSG_SHUFFLE_DUNGEON_QUESTS] = OptionGroup::SubGroup(
        "Shuffle Dungeon Quest",
        { &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] });
    mOptionGroups[RSG_SHUFFLE] =
        OptionGroup("Shuffle Settings", {
                                            &mOptions[RSK_SHUFFLE_DUNGEON_REWARDS],
                                            &mOptions[RSK_LINKS_POCKET],
                                            &mOptions[RSK_SHUFFLE_SONGS],
                                            &mOptions[RSK_SHOPSANITY],
                                            &mOptions[RSK_SHOPSANITY_COUNT],
                                            &mOptions[RSK_SHOPSANITY_PRICES],
                                            &mOptions[RSK_SHOPSANITY_PRICES_FIXED_PRICE],
                                            &mOptions[RSK_SHOPSANITY_PRICES_RANGE_1],
                                            &mOptions[RSK_SHOPSANITY_PRICES_RANGE_2],
                                            &mOptions[RSK_SHOPSANITY_PRICES_NO_WALLET_WEIGHT],
                                            &mOptions[RSK_SHOPSANITY_PRICES_CHILD_WALLET_WEIGHT],
                                            &mOptions[RSK_SHOPSANITY_PRICES_ADULT_WALLET_WEIGHT],
                                            &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_FISHING_POLE],
                                            &mOptions[RSK_SHUFFLE_TOKENS],
                                            &mOptions[RSK_SHUFFLE_SCRUBS],
                                            &mOptions[RSK_SCRUBS_PRICES],
                                            &mOptions[RSK_SCRUBS_PRICES_FIXED_PRICE],
                                            &mOptions[RSK_SCRUBS_PRICES_RANGE_1],
                                            &mOptions[RSK_SCRUBS_PRICES_RANGE_2],
                                            &mOptions[RSK_SCRUBS_PRICES_NO_WALLET_WEIGHT],
                                            &mOptions[RSK_SCRUBS_PRICES_CHILD_WALLET_WEIGHT],
                                            &mOptions[RSK_SCRUBS_PRICES_ADULT_WALLET_WEIGHT],
                                            &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_GRASS],
                                            &mOptions[RSK_SHUFFLE_CRATES],
                                            &mOptions[RSK_SHUFFLE_BOULDERS],
                                            &mOptions[RSK_SHUFFLE_ROCKS],
                                            &mOptions[RSK_SHUFFLE_TREES],
                                            &mOptions[RSK_SHUFFLE_BUSHES],
                                            &mOptions[RSK_SHUFFLE_ICICLES],
                                            &mOptions[RSK_SHUFFLE_RED_ICE],
                                            &mOptions[RSK_SHUFFLE_SIGNS],
                                            &mOptions[RSK_SHUFFLE_KOKIRI_SWORD],
                                            &mOptions[RSK_SHUFFLE_OCARINA],
                                            &mOptions[RSK_SHUFFLE_OCARINA_BUTTONS],
                                            &mOptions[RSK_SHUFFLE_SWIM],
                                            &mOptions[RSK_SHUFFLE_GRAB],
                                            &mOptions[RSK_SHUFFLE_CLIMB],
                                            &mOptions[RSK_SHUFFLE_CRAWL],
                                            &mOptions[RSK_SHUFFLE_SPEAK],
                                            &mOptions[RSK_SHUFFLE_OPEN_CHEST],
                                            &mOptions[RSK_SHUFFLE_WEIRD_EGG],
                                            &mOptions[RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD],
                                            &mOptions[RSK_SHUFFLE_MERCHANTS],
                                            &mOptions[RSK_MERCHANT_PRICES],
                                            &mOptions[RSK_MERCHANT_PRICES_FIXED_PRICE],
                                            &mOptions[RSK_MERCHANT_PRICES_RANGE_1],
                                            &mOptions[RSK_MERCHANT_PRICES_RANGE_2],
                                            &mOptions[RSK_MERCHANT_PRICES_NO_WALLET_WEIGHT],
                                            &mOptions[RSK_MERCHANT_PRICES_CHILD_WALLET_WEIGHT],
                                            &mOptions[RSK_MERCHANT_PRICES_ADULT_WALLET_WEIGHT],
                                            &mOptions[RSK_MERCHANT_PRICES_GIANT_WALLET_WEIGHT],
                                            &mOptions[RSK_MERCHANT_PRICES_TYCOON_WALLET_WEIGHT],
                                            &mOptions[RSK_MERCHANT_PRICES_AFFORDABLE],
                                            &mOptions[RSK_SHUFFLE_BEGGAR],
                                            &mOptions[RSK_SHUFFLE_FROG_SONG_RUPEES],
                                            &mOptions[RSK_SHUFFLE_ADULT_TRADE],
                                            &mOptions[RSK_SHUFFLE_CHEST_MINIGAME],
                                            &mOptions[RSK_SHUFFLE_100_GS_REWARD],
                                            &mOptions[RSK_SHUFFLE_BEAN_SOULS],
                                            &mOptions[RSK_ROCS_FEATHER],
                                            &mOptions[RSK_SHUFFLE_BOSS_SOULS],
                                            &mOptions[RSK_SHUFFLE_DEKU_STICK_BAG],
                                            &mOptions[RSK_SHUFFLE_DEKU_NUT_BAG],
                                            &mOptions[RSK_SHUFFLE_FREESTANDING],
                                            &mOptions[RSK_SHUFFLE_WONDER_ITEMS],
                                            &mOptions[RSK_SHUFFLE_FOUNTAIN_FAIRIES],
                                            &mOptions[RSK_SHUFFLE_STONE_FAIRIES],
                                            &mOptions[RSK_SHUFFLE_BEAN_FAIRIES],
                                            &mOptions[RSK_SHUFFLE_SONG_FAIRIES],
                                            &mOptions[RSK_SHUFFLE_BUTTERFLY_FAIRIES],
                                        });
    mOptionGroups[RSG_SHUFFLE_DUNGEON_ITEMS] =
        OptionGroup("Shuffle Dungeon Items", {
                                                 &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_STARTING_ITEMS] =
        OptionGroup::SubGroup("Items", { &mOptions[RSK_STARTING_OCARINA], &mOptions[RSK_STARTING_KOKIRI_SWORD],
                                         &mOptions[RSK_STARTING_DEKU_SHIELD] });
    mOptionGroups[RSG_STARTING_SONGS] =
        OptionGroup::SubGroup("Ocarina 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],
                                                   &mOptions[RSK_STARTING_SONG_OF_TIME],
                                                   &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],
                                               });
    mOptionGroups[RSG_STARTING_OTHER] = OptionGroup::SubGroup("Other", {
                                                                           &mOptions[RSK_STARTING_STICKS],
                                                                           &mOptions[RSK_STARTING_NUTS],
                                                                           &mOptions[RSK_STARTING_BEANS],
                                                                           &mOptions[RSK_FULL_WALLETS],
                                                                           &mOptions[RSK_STARTING_SKULLTULA_TOKEN],
                                                                           &mOptions[RSK_STARTING_HEARTS],
                                                                       });
    mOptionGroups[RSG_STARTING_INVENTORY] = OptionGroup("Starting Inventory",
                                                        {
                                                            &mOptionGroups[RSG_STARTING_ITEMS],
                                                            &mOptionGroups[RSG_STARTING_SONGS],
                                                            &mOptionGroups[RSG_STARTING_OTHER],
                                                        },
                                                        OptionGroupType::DEFAULT);
    mOptionGroups[RSG_TIMESAVERS] = OptionGroup("Timesaver Settings", {
                                                                          &mOptions[RSK_SKIP_CHILD_ZELDA],
                                                                          &mOptions[RSK_SKIP_EPONA_RACE],
                                                                          &mOptions[RSK_SKIP_SCARECROWS_SONG],
                                                                          &mOptions[RSK_SKIP_PLANTING_BEANS],
                                                                          &mOptions[RSK_BIG_POE_COUNT],
                                                                      });
    mOptionGroups[RSG_MISC] = OptionGroup("Miscellaneous Settings",
                                          {
                                              &mOptions[RSK_GOSSIP_STONE_HINTS],
                                              &mOptions[RSK_HINT_CLARITY],
                                              &mOptions[RSK_HINT_DISTRIBUTION],
                                              &mOptions[RSK_TOT_ALTAR_HINT],
                                              &mOptions[RSK_GANONDORF_HINT],
                                              &mOptions[RSK_SHEIK_LA_HINT],
                                              &mOptions[RSK_BOSS_KEY_HINT],
                                              &mOptions[RSK_DAMPES_DIARY_HINT],
                                              &mOptions[RSK_GREG_HINT],
                                              &mOptions[RSK_LOACH_HINT],
                                              &mOptions[RSK_SARIA_HINT],
                                              &mOptions[RSK_MIDO_HINT],
                                              &mOptions[RSK_FROGS_HINT],
                                              &mOptions[RSK_OOT_HINT],
                                              &mOptions[RSK_WARP_SONG_HINTS],
                                              &mOptions[RSK_BIGGORON_HINT],
                                              &mOptions[RSK_BIG_POES_HINT],
                                              &mOptions[RSK_CHICKENS_HINT],
                                              &mOptions[RSK_MALON_HINT],
                                              &mOptions[RSK_HBA_HINT],
                                              &mOptions[RSK_KAK_10_SKULLS_HINT],
                                              &mOptions[RSK_KAK_20_SKULLS_HINT],
                                              &mOptions[RSK_KAK_30_SKULLS_HINT],
                                              &mOptions[RSK_KAK_40_SKULLS_HINT],
                                              &mOptions[RSK_KAK_50_SKULLS_HINT],
                                              &mOptions[RSK_KAK_100_SKULLS_HINT],
                                              &mOptions[RSK_MASK_SHOP_HINT],
                                              &mOptions[RSK_SCRUB_TEXT_HINT],
                                              &mOptions[RSK_MERCHANT_TEXT_HINT],
                                              &mOptions[RSK_FISHING_POLE_HINT],
                                              // TODO: Compasses show Reward/WOTH, Maps show Dungeon Mode, Starting Time
                                              &mOptions[RSK_DAMAGE_MULTIPLIER],
                                              &mOptions[RSK_BLUE_FIRE_ARROWS],
                                              &mOptions[RSK_SUNLIGHT_ARROWS],
                                              &mOptions[RSK_INFINITE_UPGRADES],
                                              &mOptions[RSK_SKELETON_KEY],
                                              &mOptions[RSK_SLINGBOW_BREAK_BEEHIVES],
                                          });
    mOptionGroups[RSG_ITEM_POOL] =
        OptionGroup("Item Pool Settings", std::initializer_list<Option*>({ &mOptions[RSK_ITEM_POOL] }));
    // TODO: Progressive Goron Sword, Remove Double Defense
    mOptionGroups[RSG_EXCLUDES_KOKIRI_FOREST] =
        OptionGroup::SubGroup("Kokiri Forest", mExcludeLocationsOptionsAreas[RCAREA_KOKIRI_FOREST]);
    mOptionGroups[RSG_EXCLUDES_LOST_WOODS] =
        OptionGroup::SubGroup("Lost Woods", mExcludeLocationsOptionsAreas[RCAREA_LOST_WOODS]);
    mOptionGroups[RSG_EXCLUDES_SACRED_FOREST_MEADOW] =
        OptionGroup::SubGroup("Sacred Forest Meadow", mExcludeLocationsOptionsAreas[RCAREA_SACRED_FOREST_MEADOW]);
    mOptionGroups[RSG_EXCLUDES_DEKU_TREE] =
        OptionGroup::SubGroup("Deku Tree", mExcludeLocationsOptionsAreas[RCAREA_DEKU_TREE]);
    mOptionGroups[RSG_EXCLUDES_FOREST_TEMPLE] =
        OptionGroup::SubGroup("Forest Temple", mExcludeLocationsOptionsAreas[RCAREA_FOREST_TEMPLE]);
    mOptionGroups[RSG_EXCLUDES_KAKARIKO_VILLAGE] =
        OptionGroup::SubGroup("Kakariko Village", mExcludeLocationsOptionsAreas[RCAREA_KAKARIKO_VILLAGE]);
    mOptionGroups[RSG_EXCLUDES_GRAVEYARD] =
        OptionGroup::SubGroup("Graveyard", mExcludeLocationsOptionsAreas[RCAREA_GRAVEYARD]);
    mOptionGroups[RSG_EXCLUDES_BOTTOM_OF_THE_WELL] =
        OptionGroup::SubGroup("Bottom of the Well", mExcludeLocationsOptionsAreas[RCAREA_BOTTOM_OF_THE_WELL]);
    mOptionGroups[RSG_EXCLUDES_SHADOW_TEMPLE] =
        OptionGroup::SubGroup("Shadow Temple", mExcludeLocationsOptionsAreas[RCAREA_SHADOW_TEMPLE]);
    mOptionGroups[RSG_EXCLUDES_DEATH_MOUNTAIN_TRAIL] =
        OptionGroup::SubGroup("Death Mountain Trail", mExcludeLocationsOptionsAreas[RCAREA_DEATH_MOUNTAIN_TRAIL]);
    mOptionGroups[RSG_EXCLUDES_DEATH_MOUNTAIN_CRATER] =
        OptionGroup::SubGroup("Death Mountain Crater", mExcludeLocationsOptionsAreas[RCAREA_DEATH_MOUNTAIN_CRATER]);
    mOptionGroups[RSG_EXCLUDES_GORON_CITY] =
        OptionGroup::SubGroup("Goron City", mExcludeLocationsOptionsAreas[RCAREA_GORON_CITY]);
    mOptionGroups[RSG_EXCLUDES_DODONGOS_CAVERN] =
        OptionGroup::SubGroup("Dodongo's Cavern", mExcludeLocationsOptionsAreas[RCAREA_DODONGOS_CAVERN]);
    mOptionGroups[RSG_EXCLUDES_FIRE_TEMPLE] =
        OptionGroup::SubGroup("Fire Temple", mExcludeLocationsOptionsAreas[RCAREA_FIRE_TEMPLE]);
    mOptionGroups[RSG_EXCLUDES_ZORAS_RIVER] =
        OptionGroup::SubGroup("Zora's River", mExcludeLocationsOptionsAreas[RCAREA_ZORAS_RIVER]);
    mOptionGroups[RSG_EXCLUDES_ZORAS_DOMAIN] =
        OptionGroup::SubGroup("Zora's Domain", mExcludeLocationsOptionsAreas[RCAREA_ZORAS_DOMAIN]);
    mOptionGroups[RSG_EXCLUDES_ZORAS_FOUNTAIN] =
        OptionGroup::SubGroup("Zora's Fountain", mExcludeLocationsOptionsAreas[RCAREA_ZORAS_FOUNTAIN]);
    mOptionGroups[RSG_EXCLUDES_JABU_JABU] =
        OptionGroup::SubGroup("Jabu Jabu's Belly", mExcludeLocationsOptionsAreas[RCAREA_JABU_JABUS_BELLY]);
    mOptionGroups[RSG_EXCLUDES_ICE_CAVERN] =
        OptionGroup::SubGroup("Ice Cavern", mExcludeLocationsOptionsAreas[RCAREA_ICE_CAVERN]);
    mOptionGroups[RSG_EXCLUDES_HYRULE_FIELD] =
        OptionGroup::SubGroup("Hyrule Field", mExcludeLocationsOptionsAreas[RCAREA_HYRULE_FIELD]);
    mOptionGroups[RSG_EXCLUDES_LON_LON_RANCH] =
        OptionGroup::SubGroup("Lon Lon Ranch", mExcludeLocationsOptionsAreas[RCAREA_LON_LON_RANCH]);
    mOptionGroups[RSG_EXCLUDES_LAKE_HYLIA] =
        OptionGroup::SubGroup("Lake Hylia", mExcludeLocationsOptionsAreas[RCAREA_LAKE_HYLIA]);
    mOptionGroups[RSG_EXCLUDES_WATER_TEMPLE] =
        OptionGroup::SubGroup("Water Temple", mExcludeLocationsOptionsAreas[RCAREA_WATER_TEMPLE]);
    mOptionGroups[RSG_EXCLUDES_GERUDO_VALLEY] =
        OptionGroup::SubGroup("Gerudo Valley", mExcludeLocationsOptionsAreas[RCAREA_GERUDO_VALLEY]);
    mOptionGroups[RSG_EXCLUDES_GERUDO_FORTRESS] =
        OptionGroup::SubGroup("Gerudo Fortress", mExcludeLocationsOptionsAreas[RCAREA_GERUDO_FORTRESS]);
    mOptionGroups[RSG_EXCLUDES_HAUNTED_WASTELAND] =
        OptionGroup::SubGroup("Haunted Wasteland", mExcludeLocationsOptionsAreas[RCAREA_WASTELAND]);
    mOptionGroups[RSG_EXCLUDES_DESERT_COLOSSUS] =
        OptionGroup::SubGroup("Desert Colossus", mExcludeLocationsOptionsAreas[RCAREA_DESERT_COLOSSUS]);
    mOptionGroups[RSG_EXCLUDES_GERUDO_TRAINING_GROUND] =
        OptionGroup::SubGroup("Gerudo Training Ground", mExcludeLocationsOptionsAreas[RCAREA_GERUDO_TRAINING_GROUND]);
    mOptionGroups[RSG_EXCLUDES_SPIRIT_TEMPLE] =
        OptionGroup::SubGroup("Spirit Temple", mExcludeLocationsOptionsAreas[RCAREA_SPIRIT_TEMPLE]);
    mOptionGroups[RSG_EXCLUDES_HYRULE_CASTLE] =
        OptionGroup::SubGroup("Hyrule Castle", mExcludeLocationsOptionsAreas[RCAREA_HYRULE_CASTLE]);
    mOptionGroups[RSG_EXCLUDES_MARKET] = OptionGroup::SubGroup("Market", mExcludeLocationsOptionsAreas[RCAREA_MARKET]);
    mOptionGroups[RSG_EXCLUDES_GANONS_CASTLE] =
        OptionGroup::SubGroup("Ganon's Castle", mExcludeLocationsOptionsAreas[RCAREA_GANONS_CASTLE]);
    mOptionGroups[RSG_EXCLUDES] =
        OptionGroup::SubGroup("Exclude Locations", {
                                                       &mOptionGroups[RSG_EXCLUDES_KOKIRI_FOREST],
                                                       &mOptionGroups[RSG_EXCLUDES_LOST_WOODS],
                                                       &mOptionGroups[RSG_EXCLUDES_SACRED_FOREST_MEADOW],
                                                       &mOptionGroups[RSG_EXCLUDES_DEKU_TREE],
                                                       &mOptionGroups[RSG_EXCLUDES_FOREST_TEMPLE],
                                                       &mOptionGroups[RSG_EXCLUDES_KAKARIKO_VILLAGE],
                                                       &mOptionGroups[RSG_EXCLUDES_GRAVEYARD],
                                                       &mOptionGroups[RSG_EXCLUDES_BOTTOM_OF_THE_WELL],
                                                       &mOptionGroups[RSG_EXCLUDES_SHADOW_TEMPLE],
                                                       &mOptionGroups[RSG_EXCLUDES_DEATH_MOUNTAIN_TRAIL],
                                                       &mOptionGroups[RSG_EXCLUDES_DEATH_MOUNTAIN_CRATER],
                                                       &mOptionGroups[RSG_EXCLUDES_GORON_CITY],
                                                       &mOptionGroups[RSG_EXCLUDES_DODONGOS_CAVERN],
                                                       &mOptionGroups[RSG_EXCLUDES_FIRE_TEMPLE],
                                                       &mOptionGroups[RSG_EXCLUDES_ZORAS_RIVER],
                                                       &mOptionGroups[RSG_EXCLUDES_ZORAS_DOMAIN],
                                                       &mOptionGroups[RSG_EXCLUDES_ZORAS_FOUNTAIN],
                                                       &mOptionGroups[RSG_EXCLUDES_JABU_JABU],
                                                       &mOptionGroups[RSG_EXCLUDES_ICE_CAVERN],
                                                       &mOptionGroups[RSG_EXCLUDES_HYRULE_FIELD],
                                                       &mOptionGroups[RSG_EXCLUDES_LON_LON_RANCH],
                                                       &mOptionGroups[RSG_EXCLUDES_LAKE_HYLIA],
                                                       &mOptionGroups[RSG_EXCLUDES_WATER_TEMPLE],
                                                       &mOptionGroups[RSG_EXCLUDES_GERUDO_VALLEY],
                                                       &mOptionGroups[RSG_EXCLUDES_GERUDO_FORTRESS],
                                                       &mOptionGroups[RSG_EXCLUDES_HAUNTED_WASTELAND],
                                                       &mOptionGroups[RSG_EXCLUDES_DESERT_COLOSSUS],
                                                       &mOptionGroups[RSG_EXCLUDES_GERUDO_TRAINING_GROUND],
                                                       &mOptionGroups[RSG_EXCLUDES_SPIRIT_TEMPLE],
                                                       &mOptionGroups[RSG_EXCLUDES_HYRULE_CASTLE],
                                                       &mOptionGroups[RSG_EXCLUDES_MARKET],
                                                       &mOptionGroups[RSG_EXCLUDES_GANONS_CASTLE],
                                                   });
    mOptionGroups[RSG_DETAILED_LOGIC] =
        OptionGroup("Detailed Logic Settings",
                    { &mOptionGroups[RSG_LOGIC], &mOptionGroups[RSG_TRICKS], &mOptionGroups[RSG_EXCLUDES] });
}

std::unordered_map<std::string, RandomizerSettingKey> Settings::PopulateOptionNameToEnum() {
    std::unordered_map<std::string, RandomizerSettingKey> output = {};
    for (size_t count = 0; count < RSK_MAX; count++) {
        output[mOptions[count].GetName()] = static_cast<RandomizerSettingKey>(count);
    }
    return output;
}

Option& Settings::GetOption(const RandomizerSettingKey key) {
    return mOptions[key];
}

TrickSetting& Settings::GetTrickSetting(const RandomizerTrick key) {
    return mTrickSettings[key];
}

int Settings::GetRandomizerTrickByName(const std::string& name) {
    const auto& it = mTrickNameToEnum.find(name);
    if (it == mTrickNameToEnum.end()) {
        return -1;
    }
    return it->second;
}

void Context::ResetTrickOptions() {
    for (int count = 0; count < RT_MAX; count++) {
        mTrickOptions[count].Set(0); // RANDOTODO this can probably be done better
    };
}

const std::array<Option, RSK_MAX>& Settings::GetAllOptions() const {
    return mOptions;
}

std::vector<Option*>& Settings::GetExcludeOptionsForArea(const RandomizerCheckArea area) {
    return mExcludeLocationsOptionsAreas[area];
}

const std::vector<std::vector<Option*>>& Settings::GetExcludeLocationsOptions() const {
    return mExcludeLocationsOptionsAreas;
}

const std::array<OptionGroup, RSG_MAX>& Settings::GetOptionGroups() {
    return mOptionGroups;
}

const OptionGroup& Settings::GetOptionGroup(const RandomizerSettingGroupKey key) {
    return mOptionGroups[key];
}

void Settings::UpdateAllOptions() {
    for (auto& option : mOptions) {
        option.RunCallback();
    }
}

void Context::FinalizeSettings(const std::set<RandomizerCheck>& excludedLocations,
                               const std::set<RandomizerTrick>& enabledTricks) {
    // if we skip child zelda, we start with zelda's letter, and malon starts
    // at the ranch, so we should *not* shuffle the weird egg
    if (mOptions[RSK_SKIP_CHILD_ZELDA]) {
        mOptions[RSK_SHUFFLE_WEIRD_EGG].Set(RO_GENERIC_OFF);
    }

    // With certain access settings, the seed is only beatable if Starting Age is set to Child.
    if (mOptions[RSK_LOGIC_RULES].IsNot(RO_LOGIC_NO_LOGIC) &&
        ((mOptions[RSK_DOOR_OF_TIME].Is(RO_DOOROFTIME_CLOSED) && !mOptions[RSK_SHUFFLE_OCARINA]) ||
         (mOptions[RSK_FOREST].Is(RO_CLOSED_FOREST_ON) && mOptions[RSK_SHUFFLE_OVERWORLD_SPAWNS].Is(RO_GENERIC_OFF) &&
          mOptions[RSK_SHUFFLE_OVERWORLD_ENTRANCES].Is(RO_GENERIC_OFF) &&
          mOptions[RSK_SHUFFLE_INTERIOR_ENTRANCES].Is(RO_GENERIC_OFF) &&
          (mOptions[RSK_SHUFFLE_GROTTO_ENTRANCES].Is(RO_GENERIC_OFF) &&
           mOptions[RSK_DECOUPLED_ENTRANCES].Is(RO_GENERIC_OFF))))) {
        mOptions[RSK_STARTING_AGE].Set(RO_AGE_CHILD);
    }

    // Force 100 GS Shuffle if that's where Ganon's Boss Key is
    if (mOptions[RSK_GANONS_BOSS_KEY].Is(RO_GANON_BOSS_KEY_KAK_TOKENS)) {
        mOptions[RSK_SHUFFLE_100_GS_REWARD].Set(1);
    }

    // If we only have MQ, set all dungeons to MQ
    if (OTRGlobals::Instance->HasMasterQuest() && !OTRGlobals::Instance->HasOriginal()) {
        mOptions[RSK_MQ_DUNGEON_RANDOM].Set(RO_MQ_DUNGEONS_SET_NUMBER);
        mOptions[RSK_MQ_DUNGEON_COUNT].Set(MAX_MQ_DUNGEON_COUNT);
        mOptions[RSK_MQ_DUNGEON_SET].Set(RO_GENERIC_OFF);
    }

    // If we don't have MQ, set all dungeons to Vanilla
    if (OTRGlobals::Instance->HasOriginal() && !OTRGlobals::Instance->HasMasterQuest()) {
        mOptions[RSK_MQ_DUNGEON_RANDOM].Set(RO_MQ_DUNGEONS_NONE);
    }

    if (mOptions[RSK_MQ_DUNGEON_RANDOM].Is(RO_MQ_DUNGEONS_NONE)) {
        mOptions[RSK_MQ_DUNGEON_COUNT].Set(0);
        mOptions[RSK_MQ_DUNGEON_SET].Set(RO_GENERIC_OFF);
    }

    // If any of the individual shuffle settings are on, turn on the main Shuffle Entrances option
    if (mOptions[RSK_SHUFFLE_DUNGEON_ENTRANCES].IsNot(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF) ||
        mOptions[RSK_SHUFFLE_BOSS_ENTRANCES].IsNot(RO_BOSS_ROOM_ENTRANCE_SHUFFLE_OFF) ||
        mOptions[RSK_SHUFFLE_OVERWORLD_ENTRANCES] ||
        mOptions[RSK_SHUFFLE_INTERIOR_ENTRANCES].IsNot(RO_INTERIOR_ENTRANCE_SHUFFLE_OFF) ||
        mOptions[RSK_SHUFFLE_THIEVES_HIDEOUT_ENTRANCES] || mOptions[RSK_SHUFFLE_GROTTO_ENTRANCES] ||
        mOptions[RSK_SHUFFLE_OWL_DROPS] || mOptions[RSK_SHUFFLE_WARP_SONGS] || mOptions[RSK_SHUFFLE_OVERWORLD_SPAWNS]) {
        mOptions[RSK_SHUFFLE_ENTRANCES].Set(RO_GENERIC_ON);
    } else {
        mOptions[RSK_SHUFFLE_ENTRANCES].Set(RO_GENERIC_OFF);
    }

    if (mOptions[RSK_SHUFFLE_BOSS_ENTRANCES].Is(RO_BOSS_ROOM_ENTRANCE_SHUFFLE_OFF)) {
        mOptions[RSK_SHUFFLE_GANONS_TOWER_ENTRANCE].Set(RO_GENERIC_OFF);
    }

    if (mOptions[RSK_SHUFFLE_DUNGEON_REWARDS].Is(RO_DUNGEON_REWARDS_END_OF_DUNGEON)) {
        mOptions[RSK_LINKS_POCKET].Set(RO_LINKS_POCKET_DUNGEON_REWARD);
    } else if (mOptions[RSK_SHUFFLE_DUNGEON_REWARDS].Is(RO_DUNGEON_REWARDS_OWN_DUNGEON) ||
               mOptions[RSK_SHUFFLE_DUNGEON_REWARDS].Is(RO_DUNGEON_REWARDS_VANILLA)) {
        mOptions[RSK_LINKS_POCKET_REWARD].Set(RO_LINKS_POCKET_LIGHT_MEDALLION);
    }

    if (mOptions[RSK_LINKS_POCKET].IsNot(RO_LINKS_POCKET_DUNGEON_REWARD)) {
        mOptions[RSK_LINKS_POCKET_REWARD].Set(RO_LINKS_POCKET_ANY_REWARD);
    }

    for (const auto locationKey : this->everyPossibleLocation) {
        if (const auto location = this->GetItemLocation(locationKey);
            excludedLocations.contains(location->GetRandomizerCheck())) {
            location->SetExcludedOption(1);
        } else {
            location->SetExcludedOption(0);
        }
    }
    // Tricks
    ResetTrickOptions();
    for (const auto randomizerTrick : enabledTricks) {
        mTrickOptions[randomizerTrick].Set(1);
    }
    if (!mOptions[RSK_SHUFFLE_KOKIRI_SWORD]) {
        if (mOptions[RSK_STARTING_KOKIRI_SWORD]) {
            this->GetItemLocation(RC_KF_KOKIRI_SWORD_CHEST)->SetExcludedOption(1);
        }
    }
    if (!mOptions[RSK_SHUFFLE_MASTER_SWORD]) {
        if (mOptions[RSK_STARTING_MASTER_SWORD]) {
            this->GetItemLocation(RC_TOT_MASTER_SWORD)->SetExcludedOption(1);
        }
    }
    if (!mOptions[RSK_SHUFFLE_OCARINA]) {
        if (mOptions[RSK_STARTING_OCARINA].IsNot(RO_STARTING_OCARINA_OFF)) {
            this->GetItemLocation(RC_LW_GIFT_FROM_SARIA)->SetExcludedOption(1);
            if (mOptions[RSK_STARTING_OCARINA].Is(RO_STARTING_OCARINA_TIME)) {
                this->GetItemLocation(RC_HF_OCARINA_OF_TIME_ITEM)->SetExcludedOption(1);
            }
        }
    }

    if (mOptions[RSK_SHUFFLE_DEKU_STICK_BAG]) {
        mOptions[RSK_STARTING_STICKS].Set(false);
    }
    if (mOptions[RSK_SHUFFLE_DEKU_NUT_BAG]) {
        mOptions[RSK_STARTING_NUTS].Set(false);
    }

    // RANDOTODO implement chest shuffle with keysanity
    // ShuffleChestMinigame.Set(cvarSettings[RSK_SHUFFLE_CHEST_MINIGAME]);
    mOptions[RSK_SHUFFLE_CHEST_MINIGAME].Set(RO_CHEST_GAME_OFF);

    // TODO: RandomizeAllSettings(true) when implementing the ability to randomize the options themselves.
    std::array<DungeonInfo*, 12> dungeons = this->GetDungeons()->GetDungeonList();

    // reset the MQ vars
    for (auto dungeon : dungeons) {
        dungeon->ClearMQ();
        dungeon->SetDungeonKnown(true);
    }
    // if it's selection mode, process the selection directly
    if (mOptions[RSK_MQ_DUNGEON_RANDOM].Is(RO_MQ_DUNGEONS_SELECTION)) {
        mOptions[RSK_MQ_DUNGEON_SET].Set(RO_GENERIC_ON);
        // How many dungeons are set to MQ in selection
        uint8_t mqSet = 0;
        for (auto dungeon : dungeons) {
            switch (mOptions[dungeon->GetMQSetting()].Get()) {
                case RO_MQ_SET_MQ:
                    dungeon->SetMQ();
                    mqSet += 1;
                    break;
                case RO_MQ_SET_RANDOM:
                    // 50% per dungeon, rolled separatly so people can either have a linear distribtuion
                    // or a bell curve for the number of MQ dungeons per seed.
                    if (ShipUtils::Random(0, 2)) {
                        dungeon->SetMQ();
                        mqSet += 1;
                    }
                    dungeon->SetDungeonKnown(false);
                    break;
                default:
                    break;
            }
        }
        // override the dungeons set with the ones set by selection, so it's accurate for anything that wants to know MQ
        // dungeon count
        mOptions[RSK_MQ_DUNGEON_COUNT].Set(mqSet);
        // handling set number and random number together
    } else if (mOptions[RSK_MQ_DUNGEON_RANDOM].IsNot(RO_MQ_DUNGEONS_NONE)) {
        // so we don't have to call this repeatedly
        uint8_t mqCount = mOptions[RSK_MQ_DUNGEON_COUNT].Get();
        // How many dungeons are set to MQ in selection
        uint8_t mqSet = 0;
        // the number of random
        uint8_t mqToSet = 0;
        // store the dungeons to randomly decide between. we use the id instead of a dungeon object to avoid a lot of
        // casting.
        std::vector<uint8_t> randMQOption = {};
        // if dungeons have been preset, process them
        if (mOptions[RSK_MQ_DUNGEON_SET]) {
            for (size_t i = 0; i < dungeons.size(); i++) {
                switch (mOptions[dungeons[i]->GetMQSetting()].Get()) {
                    case RO_MQ_SET_MQ:
                        dungeons[i]->SetMQ();
                        mqSet += 1;
                        break;
                    case RO_MQ_SET_RANDOM:
                        randMQOption.push_back(static_cast<uint8_t>(i));
                        dungeons[i]->SetDungeonKnown(false);
                        break;
                    default:
                        break;
                }
            }
            // otherwise, every dungeon is possible
        } else {
            // if count is MAX_MQ_DUNGEON_COUNT, we know everything is MQ, so can skip some setps and not set Known
            if (mOptions[RSK_MQ_DUNGEON_RANDOM].Is(RO_MQ_DUNGEONS_SET_NUMBER) && mqCount == MAX_MQ_DUNGEON_COUNT) {
                randMQOption.resize(MAX_MQ_DUNGEON_COUNT);
                for (int i = 0; i < MAX_MQ_DUNGEON_COUNT; i++) {
                    randMQOption[i] = i;
                }
                for (auto dungeon : dungeons) {
                    mOptions[dungeon->GetMQSetting()].Set(RO_MQ_SET_MQ);
                }
                // if it's fixed to zero, set it to None instead. the rest is processed after
            } else if (mOptions[RSK_MQ_DUNGEON_RANDOM].Is(RO_MQ_DUNGEONS_SET_NUMBER) && mqCount == 0) {
                mOptions[RSK_MQ_DUNGEON_RANDOM].Set(RO_MQ_DUNGEONS_NONE);
                // otherwise, make everything a possibility and unknown
            } else {
                for (size_t i = 0; i < dungeons.size(); i++) {
                    randMQOption.push_back(static_cast<uint8_t>(i));
                    dungeons[i]->SetDungeonKnown(false);
                    mOptions[dungeons[i]->GetMQSetting()].Set(RO_MQ_SET_RANDOM);
                }
            }
        }
        // if there's no random options, we can skip this
        if (randMQOption.size() > 0) {
            // Figure out how many dungeons to select, rolling the random number if needed
            if (mOptions[RSK_MQ_DUNGEON_RANDOM].Is(RO_MQ_DUNGEONS_RANDOM_NUMBER)) {
                mqToSet = ShipUtils::Random(0, static_cast<int>(randMQOption.size()) + 1);
            } else if (mqCount > mqSet) {
                mqToSet = std::min(mqCount - mqSet, static_cast<int>(randMQOption.size()));
            }
            // we only need to shuffle if we're not using them all
            if (mqToSet <= static_cast<int8_t>(randMQOption.size()) && mqToSet > 0) {
                ShipUtils::Shuffle(randMQOption);
            }
            for (uint8_t i = 0; i < mqToSet; i++) {
                dungeons[randMQOption[i]]->SetMQ();
            }
        } else {
            // if there's no random options, check if we can collapse the setting into None or Selection
            if (mqSet == 0) {
                mOptions[RSK_MQ_DUNGEON_RANDOM].Set(RO_MQ_DUNGEONS_NONE);
            } else {
                mOptions[RSK_MQ_DUNGEON_RANDOM].Set(RO_MQ_DUNGEONS_SELECTION);
            }
        }
        // reset the value set based on what was actually set
        mOptions[RSK_MQ_DUNGEON_COUNT].Set(mqToSet + mqSet);
    }
    // Not an if else as other settings can become None in processing
    if (mOptions[RSK_MQ_DUNGEON_RANDOM].Is(RO_MQ_DUNGEONS_NONE)) {
        mOptions[RSK_MQ_DUNGEON_SET].Set(RO_GENERIC_OFF);
        mOptions[RSK_MQ_DUNGEON_COUNT].Set(0);
        for (auto dungeon : dungeons) {
            mOptions[dungeon->GetMQSetting()].Set(RO_MQ_SET_VANILLA);
        }
    }

    // Set key ring for each dungeon
    for (const auto dungeon : dungeons) {
        dungeon->ClearKeyRing();
    }

    const std::vector<OptionValue*> keyRingOptions = {
        &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],
    };

    if (mOptions[RSK_KEYRINGS]) {
        // Random Key Rings
        auto keyrings = keyRingOptions;
        if (mOptions[RSK_GERUDO_FORTRESS].Is(RO_GF_CARPENTERS_NORMAL) &&
            mOptions[RSK_GERUDO_KEYS].IsNot(RO_GERUDO_KEYS_VANILLA)) {
            keyrings.push_back(&mOptions[RSK_KEYRINGS_GERUDO_FORTRESS]);
        } else {
            mOptions[RSK_KEYRINGS_GERUDO_FORTRESS].Set(RO_KEYRING_FOR_DUNGEON_OFF);
        }
        if (mOptions[RSK_KEYRINGS].Is(RO_KEYRINGS_RANDOM) || mOptions[RSK_KEYRINGS].Is(RO_KEYRINGS_COUNT)) {
            const uint32_t keyRingCount = mOptions[RSK_KEYRINGS].Is(RO_KEYRINGS_COUNT)
                                              ? mOptions[RSK_KEYRINGS_RANDOM_COUNT].Get()
                                              : ShipUtils::Random(0, static_cast<int>(keyrings.size()));
            ShipUtils::Shuffle(keyrings);
            for (size_t i = 0; i < keyRingCount; i++) {
                keyrings[i]->Set(RO_KEYRING_FOR_DUNGEON_ON);
            }
            for (size_t i = keyRingCount; i < keyrings.size(); i++) {
                keyrings[i]->Set(RO_KEYRING_FOR_DUNGEON_OFF);
            }
        }
        if (mOptions[RSK_KEYRINGS_BOTTOM_OF_THE_WELL].Is(RO_KEYRING_FOR_DUNGEON_ON) ||
            (mOptions[RSK_KEYRINGS_BOTTOM_OF_THE_WELL].Is(RO_KEYRING_FOR_DUNGEON_RANDOM) &&
             ShipUtils::Random(0, 2) == 0)) {
            this->GetDungeon(BOTTOM_OF_THE_WELL)->SetKeyRing();
        }
        if (mOptions[RSK_KEYRINGS_FOREST_TEMPLE].Is(RO_KEYRING_FOR_DUNGEON_ON) ||
            (mOptions[RSK_KEYRINGS_FOREST_TEMPLE].Is(RO_KEYRING_FOR_DUNGEON_RANDOM) && ShipUtils::Random(0, 2) == 0)) {
            this->GetDungeon(FOREST_TEMPLE)->SetKeyRing();
        }
        if (mOptions[RSK_KEYRINGS_FIRE_TEMPLE].Is(RO_KEYRING_FOR_DUNGEON_ON) ||
            (mOptions[RSK_KEYRINGS_FIRE_TEMPLE].Is(RO_KEYRING_FOR_DUNGEON_RANDOM) && ShipUtils::Random(0, 2) == 0)) {
            this->GetDungeon(FIRE_TEMPLE)->SetKeyRing();
        }
        if (mOptions[RSK_KEYRINGS_WATER_TEMPLE].Is(RO_KEYRING_FOR_DUNGEON_ON) ||
            (mOptions[RSK_KEYRINGS_WATER_TEMPLE].Is(RO_KEYRING_FOR_DUNGEON_RANDOM) && ShipUtils::Random(0, 2) == 0)) {
            this->GetDungeon(WATER_TEMPLE)->SetKeyRing();
        }
        if (mOptions[RSK_KEYRINGS_SPIRIT_TEMPLE].Is(RO_KEYRING_FOR_DUNGEON_ON) ||
            (mOptions[RSK_KEYRINGS_SPIRIT_TEMPLE].Is(RO_KEYRING_FOR_DUNGEON_RANDOM) && ShipUtils::Random(0, 2) == 0)) {
            this->GetDungeon(SPIRIT_TEMPLE)->SetKeyRing();
        }
        if (mOptions[RSK_KEYRINGS_SHADOW_TEMPLE].Is(RO_KEYRING_FOR_DUNGEON_ON) ||
            (mOptions[RSK_KEYRINGS_SHADOW_TEMPLE].Is(RO_KEYRING_FOR_DUNGEON_RANDOM) && ShipUtils::Random(0, 2) == 0)) {
            this->GetDungeon(SHADOW_TEMPLE)->SetKeyRing();
        }
        if (mOptions[RSK_KEYRINGS_GTG].Is(RO_KEYRING_FOR_DUNGEON_ON) ||
            (mOptions[RSK_KEYRINGS_GTG].Is(RO_KEYRING_FOR_DUNGEON_RANDOM) && ShipUtils::Random(0, 2) == 0)) {
            this->GetDungeon(GERUDO_TRAINING_GROUND)->SetKeyRing();
        }
        if (mOptions[RSK_KEYRINGS_GANONS_CASTLE].Is(RO_KEYRING_FOR_DUNGEON_ON) ||
            (mOptions[RSK_KEYRINGS_GANONS_CASTLE].Is(RO_KEYRING_FOR_DUNGEON_RANDOM) && ShipUtils::Random(0, 2) == 0)) {
            this->GetDungeon(GANONS_CASTLE)->SetKeyRing();
        }
    }

    auto trials = this->GetTrials()->GetTrialList();
    ShipUtils::Shuffle(trials);
    for (const auto trial : trials) {
        trial->SetAsSkipped();
    }
    if (mOptions[RSK_GANONS_TRIALS].Is(RO_GANONS_TRIALS_SKIP)) {
        mOptions[RSK_TRIAL_COUNT].Set(0);
    } else if (mOptions[RSK_GANONS_TRIALS].Is(RO_GANONS_TRIALS_RANDOM_NUMBER)) {
        mOptions[RSK_TRIAL_COUNT].Set(ShipUtils::Random(
            0, static_cast<int>(Rando::Settings::GetInstance()->GetOption(RSK_TRIAL_COUNT).GetOptionCount())));
    }
    for (uint8_t i = 0; i < mOptions[RSK_TRIAL_COUNT].Get(); i++) {
        trials[i]->SetAsRequired();
    }

    bool dungeonShuffle = !mOptions[RSK_SHUFFLE_DUNGEON_ENTRANCES].Is(RO_GENERIC_OFF);
    bool bossShuffle = !mOptions[RSK_SHUFFLE_BOSS_ENTRANCES].Is(RO_GENERIC_OFF);
    bool overworldShuffle = !mOptions[RSK_SHUFFLE_OVERWORLD_ENTRANCES].Is(RO_GENERIC_OFF);
    bool interiorShuffle = !mOptions[RSK_SHUFFLE_INTERIOR_ENTRANCES].Is(RO_INTERIOR_ENTRANCE_SHUFFLE_OFF);
    bool gerudoFortressShuffle = !mOptions[RSK_SHUFFLE_THIEVES_HIDEOUT_ENTRANCES].Is(RO_GENERIC_OFF);
    bool grottoShuffle = !mOptions[RSK_SHUFFLE_GROTTO_ENTRANCES].Is(RO_GENERIC_OFF);

    if (dungeonShuffle + bossShuffle + overworldShuffle + interiorShuffle + grottoShuffle <= 1) {
        mOptions[RSK_MIXED_ENTRANCE_POOLS].Set(RO_GENERIC_OFF);
    }

    if (!mOptions[RSK_MIXED_ENTRANCE_POOLS] || !dungeonShuffle) {
        mOptions[RSK_MIX_DUNGEON_ENTRANCES].Set(RO_GENERIC_OFF);
    }

    if (!mOptions[RSK_MIXED_ENTRANCE_POOLS] || !bossShuffle) {
        mOptions[RSK_MIX_BOSS_ENTRANCES].Set(RO_GENERIC_OFF);
    }

    if (!mOptions[RSK_MIXED_ENTRANCE_POOLS] || !overworldShuffle) {
        mOptions[RSK_MIX_OVERWORLD_ENTRANCES].Set(RO_GENERIC_OFF);
    }

    if (!mOptions[RSK_MIXED_ENTRANCE_POOLS] || !interiorShuffle) {
        mOptions[RSK_MIX_INTERIOR_ENTRANCES].Set(RO_GENERIC_OFF);
    }

    if (!mOptions[RSK_MIXED_ENTRANCE_POOLS] || !gerudoFortressShuffle) {
        mOptions[RSK_MIX_THIEVES_HIDEOUT_ENTRANCES].Set(RO_GENERIC_OFF);
    }

    if (!mOptions[RSK_MIXED_ENTRANCE_POOLS] || !grottoShuffle) {
        mOptions[RSK_MIX_GROTTO_ENTRANCES].Set(RO_GENERIC_OFF);
    }

    if (mOptions[RSK_STARTING_AGE].Is(RO_AGE_RANDOM)) {
        if (const uint32_t choice = ShipUtils::Random(0, 2); choice == 0) {
            mOptions[RSK_SELECTED_STARTING_AGE].Set(RO_AGE_CHILD);
        } else {
            mOptions[RSK_SELECTED_STARTING_AGE].Set(RO_AGE_ADULT);
        }
    } else {
        mOptions[RSK_SELECTED_STARTING_AGE].Set(mOptions[RSK_STARTING_AGE].Get());
    }

    // TODO: Random Starting Time

    if (mOptions[RSK_GANONS_BOSS_KEY].Is(RO_GANON_BOSS_KEY_LACS_STONES)) {
        mLACSCondition = RO_LACS_STONES;
    } else if (mOptions[RSK_GANONS_BOSS_KEY].Is(RO_GANON_BOSS_KEY_LACS_MEDALLIONS)) {
        mLACSCondition = RO_LACS_MEDALLIONS;
    } else if (mOptions[RSK_GANONS_BOSS_KEY].Is(RO_GANON_BOSS_KEY_LACS_REWARDS)) {
        mLACSCondition = RO_LACS_REWARDS;
    } else if (mOptions[RSK_GANONS_BOSS_KEY].Is(RO_GANON_BOSS_KEY_LACS_DUNGEONS)) {
        mLACSCondition = RO_LACS_DUNGEONS;
    } else if (mOptions[RSK_GANONS_BOSS_KEY].Is(RO_GANON_BOSS_KEY_LACS_TOKENS)) {
        mLACSCondition = RO_LACS_TOKENS;
    } else {
        mLACSCondition = RO_LACS_VANILLA;
    }

    if (!mOptions[RSK_SHUFFLE_WARP_SONGS]) {
        mOptions[RSK_WARP_SONG_HINTS].Set(RO_GENERIC_OFF);
    }

    if (!mOptions[RSK_SHUFFLE_COWS]) {
        mOptions[RSK_MALON_HINT].Set(RO_GENERIC_OFF);
    }

    if (!mOptions[RSK_SHUFFLE_100_GS_REWARD]) {
        mOptions[RSK_KAK_100_SKULLS_HINT].Set(RO_GENERIC_OFF);
    }

    if (!mOptions[RSK_SHUFFLE_FISHING_POLE]) {
        mOptions[RSK_FISHING_POLE_HINT].Set(RO_GENERIC_OFF);
    }

    if (mOptions[RSK_FISHSANITY].IsNot(RO_FISHSANITY_HYRULE_LOACH)) {
        mOptions[RSK_LOACH_HINT].Set(RO_GENERIC_OFF);
    }
}

void Settings::ParseJson(nlohmann::json spoilerFileJson) {
    mContext->SetSeedString(spoilerFileJson["seed"].get<std::string>());
    mContext->SetSeed(spoilerFileJson["finalSeed"].get<uint32_t>());
    nlohmann::json settingsJson = spoilerFileJson["settings"];
    for (auto it = settingsJson.begin(); it != settingsJson.end(); ++it) {
        // todo load into cvars for UI
        // RANDOTODO handle numeric value to options conversion better than brute force
        if (StaticData::optionNameToEnum.contains(it.key())) {
            const RandomizerSettingKey index = StaticData::optionNameToEnum[it.key()];
            mContext->GetOption(index).Set(mOptions[index].GetValueFromText(it.value()));
        }
    }

    nlohmann::json jsonExcludedLocations = spoilerFileJson["excludedLocations"];
    const auto ctx = Context::GetInstance();

    for (auto it = jsonExcludedLocations.begin(); it != jsonExcludedLocations.end(); ++it) {
        const RandomizerCheck rc = Rando::StaticData::locationNameToEnum[it.value()];
        ctx->GetItemLocation(rc)->SetExcludedOption(RO_GENERIC_ON);
    }

    nlohmann::json enabledTricksJson = spoilerFileJson["enabledTricks"];
    for (auto it = enabledTricksJson.begin(); it != enabledTricksJson.end(); ++it) {
        const RandomizerTrick rt = mTrickNameToEnum[it.value()];
        GetTrickSetting(rt).SetContextIndex(RO_GENERIC_ON);
    }
}

void Settings::AssignContext(std::shared_ptr<Context> ctx) {
    mContext = ctx;
}

void Settings::ClearContext() {
    mContext = nullptr;
}

void Settings::SetAllToContext() {
    for (int i = 0; i < RSK_MAX; i++) {
        mContext->GetOption(static_cast<RandomizerSettingKey>(i)).Set(mOptions[i].GetOptionIndex());
    }
    for (int i = 0; i < RT_MAX; i++) {
        mContext->GetTrickOption(static_cast<RandomizerTrick>(i)).Set(mTrickSettings[i].GetOptionIndex());
    }
    for (int i = 0; i < RC_MAX; i++) {
        mContext->GetItemLocation(i)->SetExcludedOption(
            StaticData::GetLocation(static_cast<RandomizerCheck>(i))->GetExcludedOption()->GetOptionIndex());
    }
}

void Settings::RandomizeAllSettings() {
    // Randomize all settings except tricks
    for (int i = 0; i < RSK_MAX; i++) {
        switch (static_cast<RandomizerSettingKey>(i)) {
            case RSK_STARTING_SKULLTULA_TOKEN:
            case RSK_STARTING_HEARTS:
            case RSK_STARTING_ZELDAS_LULLABY:
            case RSK_STARTING_EPONAS_SONG:
            case RSK_STARTING_SARIAS_SONG:
            case RSK_STARTING_SUNS_SONG:
            case RSK_STARTING_SONG_OF_TIME:
            case RSK_STARTING_SONG_OF_STORMS:
            case RSK_STARTING_MINUET_OF_FOREST:
            case RSK_STARTING_BOLERO_OF_FIRE:
            case RSK_STARTING_SERENADE_OF_WATER:
            case RSK_STARTING_REQUIEM_OF_SPIRIT:
            case RSK_STARTING_NOCTURNE_OF_SHADOW:
            case RSK_STARTING_PRELUDE_OF_LIGHT:
                continue;
            default:
                break;
        }

        auto key = static_cast<RandomizerSettingKey>(i);
        Option& option = mOptions[key];

        if (option.GetOptionCount() == 0) {
            continue;
        }

        uint8_t randomIndex = ShipUtils::Random(0, static_cast<uint32_t>(option.GetOptionCount()));

        option.SetContextIndex(randomIndex);
        if (!option.GetCVarName().empty()) {
            CVarSetInteger(option.GetCVarName().c_str(), randomIndex);
        }
        option.RunCallback();
    }

    Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame();
}

std::shared_ptr<Settings> Settings::GetInstance() {
    if (mInstance == nullptr) {
        mInstance = std::make_shared<Settings>();
    }
    return mInstance;
}
} // namespace Rando
