From 728a4f1c80a711d2bd735de0d5a7f33c9fc3b162 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philip=20Dub=C3=A9?= <159546+serprex@users.noreply.github.com> Date: Sat, 20 Jun 2026 16:24:44 +0000 Subject: [PATCH] Complete rando starting items, make icon based like save editor (#6723) --- .../Enhancements/debugger/debugSaveEditor.cpp | 20 +- .../Enhancements/randomizer/3drando/hints.cpp | 5 +- .../randomizer/3drando/item_pool.cpp | 144 +++++--- .../randomizer/3drando/starting_inventory.cpp | 112 +++--- soh/soh/Enhancements/randomizer/option.cpp | 4 + soh/soh/Enhancements/randomizer/option.h | 7 + .../randomizerEnums/RandomizerMiscEnums.h | 7 - .../randomizerEnums/RandomizerOptions.h | 15 + .../randomizerEnums/RandomizerSettingKey.h | 34 ++ soh/soh/Enhancements/randomizer/savefile.cpp | 177 +++++++++ soh/soh/Enhancements/randomizer/settings.cpp | 173 ++++++--- soh/soh/SohGui/SohGui.hpp | 1 + soh/soh/SohGui/SohMenuRandomizer.cpp | 4 +- soh/soh/SohGui/SohMenuStartingItems.cpp | 336 ++++++++++++++++++ 14 files changed, 847 insertions(+), 192 deletions(-) create mode 100644 soh/soh/SohGui/SohMenuStartingItems.cpp diff --git a/soh/soh/Enhancements/debugger/debugSaveEditor.cpp b/soh/soh/Enhancements/debugger/debugSaveEditor.cpp index 2468b7db73..6a20efe805 100644 --- a/soh/soh/Enhancements/debugger/debugSaveEditor.cpp +++ b/soh/soh/Enhancements/debugger/debugSaveEditor.cpp @@ -1242,19 +1242,13 @@ void DrawUpgradeIcon(const std::string& categoryName, int32_t categoryId, const PushStyleButton(Colors::DarkGray); auto value = (size_t)CUR_UPG_VALUE(categoryId); uint8_t item = value < items.size() ? items[value] : ITEM_NONE; - if (item != ITEM_NONE) { - const ItemMapEntry& slotEntry = itemMapping[item]; - if (ImGui::ImageButton( - slotEntry.name.c_str(), - std::dynamic_pointer_cast(Ship::Context::GetRawInstance()->GetWindow()->GetGui()) - ->GetTextureByName(slotEntry.name), - ImVec2(IMAGE_SIZE, IMAGE_SIZE), ImVec2(0, 0), ImVec2(1, 1))) { - ImGui::OpenPopup(upgradePopupPicker); - } - } else { - if (ImGui::Button("##itemNone", ImVec2(IMAGE_SIZE, IMAGE_SIZE) + ImGui::GetStyle().FramePadding * 2)) { - ImGui::OpenPopup(upgradePopupPicker); - } + const ItemMapEntry& slotEntry = itemMapping[item]; + if (ImGui::ImageButton( + slotEntry.name.c_str(), + std::dynamic_pointer_cast(Ship::Context::GetRawInstance()->GetWindow()->GetGui()) + ->GetTextureByName(item != ITEM_NONE ? slotEntry.name : itemMapping[items[1]].nameFaded), + ImVec2(IMAGE_SIZE, IMAGE_SIZE), ImVec2(0, 0), ImVec2(1, 1))) { + ImGui::OpenPopup(upgradePopupPicker); } PopStyleButton(); Tooltip(categoryName.c_str()); diff --git a/soh/soh/Enhancements/randomizer/3drando/hints.cpp b/soh/soh/Enhancements/randomizer/3drando/hints.cpp index e8d9abaa2c..6330b1193e 100644 --- a/soh/soh/Enhancements/randomizer/3drando/hints.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/hints.cpp @@ -757,7 +757,10 @@ void CreateStaticItemHint(RandomizerHint hintKey, std::vector locations = FindItemsAndMarkHinted(items, hintChecks); - std::vector areas = {}; + locations.erase( + std::remove_if(locations.begin(), locations.end(), [](const auto rc) { return rc == RC_UNKNOWN_CHECK; })); + std::vector areas; + areas.reserve(locations.size()); for (auto loc : locations) { areas.push_back(ctx->GetItemLocation(loc)->GetRandomArea()); } diff --git a/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp b/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp index b1a956fadf..0dc1cb095c 100644 --- a/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp @@ -9,6 +9,7 @@ #include "spoiler_log.hpp" #include "soh/Enhancements/randomizer/Traps.h" #include "z64item.h" +#include #include std::vector itemPool = {}; @@ -157,26 +158,29 @@ void GenerateItemPool() { int reservedSlots = 0; // clang-format off - AddItemToPool(RG_BOOMERANG, 2, 1, 1, 1); - AddItemToPool(RG_LENS_OF_TRUTH, 2, 1, 1, 1); - AddItemToPool(RG_MEGATON_HAMMER, 2, 1, 1, 1); - AddItemToPool(RG_IRON_BOOTS, 2, 1, 1, 1); - AddItemToPool(RG_GORON_TUNIC, 2, 1, 1, 1); - AddItemToPool(RG_ZORA_TUNIC, 2, 1, 1, 1); - AddItemToPool(RG_HOVER_BOOTS, 2, 1, 1, 1); - AddItemToPool(RG_MIRROR_SHIELD, 2, 1, 1, 1); - AddItemToPool(RG_STONE_OF_AGONY, 2, 1, 1, 1); - AddItemToPool(RG_FIRE_ARROWS, 2, 1, 1, 1); - AddItemToPool(RG_ICE_ARROWS, 2, 1, 1, 1); - AddItemToPool(RG_LIGHT_ARROWS, 2, 1, 1, 1); - AddItemToPool(RG_DINS_FIRE, 2, 1, 1, 1); - AddItemToPool(RG_FARORES_WIND, 2, 1, 1, 0); - AddItemToPool(RG_NAYRUS_LOVE, 2, 1, 1, 0); + // Items the player can start with are removed from the pool so a started copy isn't also shuffled. + if (!ctx->GetOption(RSK_STARTING_BOOMERANG)) AddItemToPool(RG_BOOMERANG, 2, 1, 1, 1); + if (!ctx->GetOption(RSK_STARTING_LENS_OF_TRUTH)) AddItemToPool(RG_LENS_OF_TRUTH, 2, 1, 1, 1); + if (!ctx->GetOption(RSK_STARTING_MEGATON_HAMMER)) AddItemToPool(RG_MEGATON_HAMMER, 2, 1, 1, 1); + if (!ctx->GetOption(RSK_STARTING_IRON_BOOTS)) AddItemToPool(RG_IRON_BOOTS, 2, 1, 1, 1); + if (!ctx->GetOption(RSK_STARTING_GORON_TUNIC)) AddItemToPool(RG_GORON_TUNIC, 2, 1, 1, 1); + if (!ctx->GetOption(RSK_STARTING_ZORA_TUNIC)) AddItemToPool(RG_ZORA_TUNIC, 2, 1, 1, 1); + if (!ctx->GetOption(RSK_STARTING_HOVER_BOOTS)) AddItemToPool(RG_HOVER_BOOTS, 2, 1, 1, 1); + if (!ctx->GetOption(RSK_STARTING_MIRROR_SHIELD)) AddItemToPool(RG_MIRROR_SHIELD, 2, 1, 1, 1); + if (!ctx->GetOption(RSK_STARTING_STONE_OF_AGONY)) AddItemToPool(RG_STONE_OF_AGONY, 2, 1, 1, 1); + if (!ctx->GetOption(RSK_STARTING_FIRE_ARROWS)) AddItemToPool(RG_FIRE_ARROWS, 2, 1, 1, 1); + if (!ctx->GetOption(RSK_STARTING_ICE_ARROWS)) AddItemToPool(RG_ICE_ARROWS, 2, 1, 1, 1); + if (!ctx->GetOption(RSK_STARTING_LIGHT_ARROWS)) AddItemToPool(RG_LIGHT_ARROWS, 2, 1, 1, 1); + if (!ctx->GetOption(RSK_STARTING_DINS_FIRE)) AddItemToPool(RG_DINS_FIRE, 2, 1, 1, 1); + if (!ctx->GetOption(RSK_STARTING_FARORES_WIND)) AddItemToPool(RG_FARORES_WIND, 2, 1, 1, 0); + if (!ctx->GetOption(RSK_STARTING_NAYRUS_LOVE)) AddItemToPool(RG_NAYRUS_LOVE, 2, 1, 1, 0); AddItemToPool(RG_GREG_RUPEE, 1, 1, 1, 1); - AddItemToPool(RG_PROGRESSIVE_HOOKSHOT, 2, 2, 2, 2); - AddItemToPool(RG_HYLIAN_SHIELD, 1, 1, 1, 1); + AddFixedItemToPool(RG_PROGRESSIVE_HOOKSHOT, 2 - ctx->GetOption(RSK_STARTING_HOOKSHOT).Get()); + if (!ctx->GetOption(RSK_STARTING_HYLIAN_SHIELD)) AddItemToPool(RG_HYLIAN_SHIELD, 1, 1, 1, 1); AddItemToPool(RG_DOUBLE_DEFENSE, 2, 1, 0, 0); - AddItemToPool(RG_BIGGORON_SWORD, 2, 1, 1, 0); + if (ctx->GetOption(RSK_STARTING_BIGGORON_SWORD).IsNot(RO_STARTING_BGS_BIGGORON_SWORD)) { + AddItemToPool(RG_BIGGORON_SWORD, 2, 1, 1, 0); + } bool isScrubs = ctx->GetOption(RSK_SHUFFLE_SCRUBS).Is(RO_SCRUBS_ALL); AddFixedItemToPool(RG_DEKU_SHIELD, isScrubs ? 1 : 2); AddFixedItemToPool(RG_RECOVERY_HEART, isScrubs ? 6 : 11); @@ -199,30 +203,37 @@ void GenerateItemPool() { } } + // Progressive items the player starts with are removed from the pool, subtracting the starting + // tier from each count (clamped to 0 so smaller pools don't underflow). int infiniteProgressive = ctx->GetOption(RSK_INFINITE_UPGRADES).Is(RO_INF_UPGRADES_PROGRESSIVE) ? 1 : 0; - AddItemToPool(RG_PROGRESSIVE_BOW, 4 + infiniteProgressive, - 3 + infiniteProgressive, - 2 + infiniteProgressive, - 1 + infiniteProgressive); - AddItemToPool(RG_PROGRESSIVE_SLINGSHOT, 4 + infiniteProgressive, - 3 + infiniteProgressive, - 2 + infiniteProgressive, - 1 + infiniteProgressive); - AddItemToPool(RG_PROGRESSIVE_BOMB_BAG, 4 + infiniteProgressive, - 3 + infiniteProgressive, - 2 + infiniteProgressive, - 1 + infiniteProgressive); - AddItemToPool(RG_PROGRESSIVE_MAGIC_METER, 3 + infiniteProgressive, - 2 + infiniteProgressive, - 1 + infiniteProgressive, - 1 + infiniteProgressive); + int startBow = ctx->GetOption(RSK_STARTING_BOW).Get(); + AddItemToPool(RG_PROGRESSIVE_BOW, std::max(0, 4 + infiniteProgressive - startBow), + std::max(0, 3 + infiniteProgressive - startBow), + std::max(0, 2 + infiniteProgressive - startBow), + std::max(0, 1 + infiniteProgressive - startBow)); + int startSlingshot = ctx->GetOption(RSK_STARTING_SLINGSHOT).Get(); + AddItemToPool(RG_PROGRESSIVE_SLINGSHOT, std::max(0, 4 + infiniteProgressive - startSlingshot), + std::max(0, 3 + infiniteProgressive - startSlingshot), + std::max(0, 2 + infiniteProgressive - startSlingshot), + std::max(0, 1 + infiniteProgressive - startSlingshot)); + int startBombBag = ctx->GetOption(RSK_STARTING_BOMB_BAG).Get(); + AddItemToPool(RG_PROGRESSIVE_BOMB_BAG, std::max(0, 4 + infiniteProgressive - startBombBag), + std::max(0, 3 + infiniteProgressive - startBombBag), + std::max(0, 2 + infiniteProgressive - startBombBag), + std::max(0, 1 + infiniteProgressive - startBombBag)); + int startMagic = ctx->GetOption(RSK_STARTING_MAGIC_METER).Get(); + AddItemToPool(RG_PROGRESSIVE_MAGIC_METER, std::max(0, 3 + infiniteProgressive - startMagic), + std::max(0, 2 + infiniteProgressive - startMagic), + std::max(0, 1 + infiniteProgressive - startMagic), + std::max(0, 1 + infiniteProgressive - startMagic)); //clang-format on int extraWallets =(ctx->GetOption(RSK_SHUFFLE_CHILD_WALLET) ? 1 : 0) + (ctx->GetOption(RSK_INCLUDE_TYCOON_WALLET) ? 1 : 0); - AddItemToPool(RG_PROGRESSIVE_WALLET, 3 + infiniteProgressive + extraWallets, - 2 + infiniteProgressive + extraWallets, - 2 + infiniteProgressive + extraWallets, - 2 + infiniteProgressive + extraWallets); + int startWallet = ctx->GetOption(RSK_STARTING_WALLET).Get(); + AddItemToPool(RG_PROGRESSIVE_WALLET, std::max(0, 3 + infiniteProgressive + extraWallets - startWallet), + std::max(0, 2 + infiniteProgressive + extraWallets - startWallet), + std::max(0, 2 + infiniteProgressive + extraWallets - startWallet), + std::max(0, 2 + infiniteProgressive + extraWallets - startWallet)); int stickShuffle = ctx->GetOption(RSK_SHUFFLE_DEKU_STICK_BAG) ? 1 : 0; AddItemToPool(RG_PROGRESSIVE_STICK_UPGRADE, 3 + infiniteProgressive + stickShuffle, @@ -236,13 +247,17 @@ void GenerateItemPool() { 1 + infiniteProgressive + nutShuffle, 0 + infiniteProgressive + nutShuffle); + int startBombchu = ctx->GetOption(RSK_STARTING_BOMBCHU_BAG).Get(); if (ctx->GetOption(RSK_BOMBCHU_BAG).Is(RO_BOMBCHU_BAG_SINGLE)) { - AddItemToPool(RG_PROGRESSIVE_BOMBCHU_BAG, 6, 5, 3, 1); + // Single mode has only one bag; starting with it removes one copy from the pool. + int startSingle = startBombchu > 0 ? 1 : 0; + AddItemToPool(RG_PROGRESSIVE_BOMBCHU_BAG, std::max(0, 6 - startSingle), std::max(0, 5 - startSingle), + std::max(0, 3 - startSingle), std::max(0, 1 - startSingle)); } else if (ctx->GetOption(RSK_BOMBCHU_BAG).Is(RO_BOMBCHU_BAG_PROGRESSIVE)) { - AddItemToPool(RG_PROGRESSIVE_BOMBCHU_BAG, 4 + infiniteProgressive, - 3 + infiniteProgressive, - 2 + infiniteProgressive, - 1 + infiniteProgressive); + AddItemToPool(RG_PROGRESSIVE_BOMBCHU_BAG, std::max(0, 4 + infiniteProgressive - startBombchu), + std::max(0, 3 + infiniteProgressive - startBombchu), + std::max(0, 2 + infiniteProgressive - startBombchu), + std::max(0, 1 + infiniteProgressive - startBombchu)); } else { AddItemToPool(RG_BOMBCHU_20, 2, 1, 0, 0); AddItemToPool(RG_BOMBCHU_10, 3, 3, 2, 0); @@ -353,7 +368,9 @@ void GenerateItemPool() { } if (ctx->GetOption(RSK_SHUFFLE_WEIRD_EGG)) { - AddItemToPool(RG_WEIRD_EGG, 2, 1, 1, 1); + if (!ctx->GetOption(RSK_STARTING_WEIRD_EGG)) { + AddItemToPool(RG_WEIRD_EGG, 2, 1, 1, 1); + } } else { ctx->PlaceItemInLocation(RC_HC_MALON_EGG, RG_WEIRD_EGG, false, true); } @@ -390,7 +407,7 @@ void GenerateItemPool() { AddItemToPool(RG_KEATON_MASK, 2, 1, 1, 1); AddItemToPool(RG_SKULL_MASK, 2, 1, 1, 1); AddItemToPool(RG_SPOOKY_MASK, 2, 1, 1, 1); - AddItemToPool(RG_BUNNY_HOOD, 2, 1, 1, 1); + if (!ctx->GetOption(RSK_STARTING_BUNNY_HOOD)) AddItemToPool(RG_BUNNY_HOOD, 2, 1, 1, 1); AddItemToPool(RG_GORON_MASK, 2, 1, 1, 1); AddItemToPool(RG_ZORA_MASK, 2, 1, 1, 1); AddItemToPool(RG_GERUDO_MASK, 2, 1, 1, 1); @@ -402,9 +419,13 @@ void GenerateItemPool() { } int bronzeScale = ctx->GetOption(RSK_SHUFFLE_SWIM) ? 1 : 0; - AddItemToPool(RG_PROGRESSIVE_SCALE, 3 + bronzeScale, 2 + bronzeScale, 2 + bronzeScale, 2 + bronzeScale); + int startScale = ctx->GetOption(RSK_STARTING_SCALE).Get(); + AddItemToPool(RG_PROGRESSIVE_SCALE, std::max(0, 3 + bronzeScale - startScale), std::max(0, 2 + bronzeScale - startScale), + std::max(0, 2 + bronzeScale - startScale), std::max(0, 2 + bronzeScale - startScale)); int powerBracelet = ctx->GetOption(RSK_SHUFFLE_GRAB) ? 1 : 0; - AddItemToPool(RG_PROGRESSIVE_STRENGTH, 4 + powerBracelet, 3 + powerBracelet, 3 + powerBracelet, 3 + powerBracelet); + int startStrength = ctx->GetOption(RSK_STARTING_STRENGTH).Get(); + AddItemToPool(RG_PROGRESSIVE_STRENGTH, std::max(0, 4 + powerBracelet - startStrength), std::max(0, 3 + powerBracelet - startStrength), + std::max(0, 3 + powerBracelet - startStrength), std::max(0, 3 + powerBracelet - startStrength)); if (ctx->GetOption(RSK_SHUFFLE_CLIMB)) { AddItemToPool(RG_CLIMB, 2, 1, 1, 1); @@ -494,7 +515,8 @@ void GenerateItemPool() { if (ctx->GetOption(RSK_SHUFFLE_MERCHANTS).Is(RO_SHUFFLE_MERCHANTS_ALL_BUT_BEANS) || ctx->GetOption(RSK_SHUFFLE_MERCHANTS).Is(RO_SHUFFLE_MERCHANTS_ALL)) { - if (/*!ProgressiveGoronSword TODO: Implement Progressive Goron Sword*/ true) { + if (/*!ProgressiveGoronSword TODO: Implement Progressive Goron Sword*/ + ctx->GetOption(RSK_STARTING_BIGGORON_SWORD).Is(RO_STARTING_BGS_OFF)) { AddFixedItemToPool(RG_GIANTS_KNIFE, 1); } if (ctx->GetOption(RSK_BOMBCHU_BAG).Is(RO_BOMBCHU_BAG_SINGLE)) { @@ -533,7 +555,9 @@ void GenerateItemPool() { AddItemToPool(RG_EYEBALL_FROG, 2, 1, 1, 1); AddItemToPool(RG_EYEDROPS, 2, 1, 1, 1); } - AddItemToPool(RG_CLAIM_CHECK, 2, 1, 1, 1); + if (!ctx->GetOption(RSK_STARTING_CLAIM_CHECK)) { + AddItemToPool(RG_CLAIM_CHECK, 2, 1, 1, 1); + } if (ctx->GetOption(RSK_SHUFFLE_CHEST_MINIGAME).Is(RO_CHEST_GAME_SINGLE_KEYS)) { AddItemToPool(RG_TREASURE_GAME_SMALL_KEY, 7, 6, 6, 6); @@ -646,7 +670,9 @@ void GenerateItemPool() { // Gerudo Membership Card if (ctx->GetOption(RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD)) { - AddItemToPool(RG_GERUDO_MEMBERSHIP_CARD, 2, 1, 1, 1); + if (!ctx->GetOption(RSK_STARTING_GERUDO_CARD)) { + AddItemToPool(RG_GERUDO_MEMBERSHIP_CARD, 2, 1, 1, 1); + } } else { ctx->PlaceItemInLocation(RC_TH_FREED_CARPENTERS, RG_GERUDO_MEMBERSHIP_CARD, false, true); } @@ -829,15 +855,25 @@ void GenerateItemPool() { // Add 4 total bottles uint8_t bottleCount = 4; if (ctx->GetOption(RSK_ZORAS_FOUNTAIN).IsNot(RO_ZF_OPEN)) { - AddFixedItemToPool(RG_RUTOS_LETTER); - bottleCount--; + // When the letter is started with, a normal bottle takes its pool slot instead. + if (ctx->GetOption(RSK_STARTING_BOTTLE_1).IsNot(RO_STARTING_BOTTLE_RUTOS_LETTER)) { + AddFixedItemToPool(RG_RUTOS_LETTER); + bottleCount--; + } } + // Bottles the player starts with are removed from the pool. + for (RandomizerSettingKey bottleKey : + { RSK_STARTING_BOTTLE_1, RSK_STARTING_BOTTLE_2, RSK_STARTING_BOTTLE_3, RSK_STARTING_BOTTLE_4 }) { + if (bottleCount > 0 && ctx->GetOption(bottleKey).IsNot(RO_STARTING_BOTTLE_OFF)) { + bottleCount--; + } + } + if ((ctx->GetOption(RSK_SHUFFLE_MERCHANTS).Is(RO_SHUFFLE_MERCHANTS_ALL_BUT_BEANS) || - ctx->GetOption(RSK_SHUFFLE_MERCHANTS).Is(RO_SHUFFLE_MERCHANTS_ALL))) { + ctx->GetOption(RSK_SHUFFLE_MERCHANTS).Is(RO_SHUFFLE_MERCHANTS_ALL)) && bottleCount > 0) { AddFixedItemToPool(RG_BOTTLE_WITH_BLUE_POTION); bottleCount--; } - ctx->possibleIceTrapModels.insert(RG_EMPTY_BOTTLE); // ice traps reroll this into a random normal bottle in Rando::Traps::GetTrapTrickModel for (uint8_t i = 0; i < bottleCount; i++) { AddFixedItemToPool(RandomElement(Rando::StaticData::normalBottles), 1, false); diff --git a/soh/soh/Enhancements/randomizer/3drando/starting_inventory.cpp b/soh/soh/Enhancements/randomizer/3drando/starting_inventory.cpp index ded9523a1e..a7fe551020 100644 --- a/soh/soh/Enhancements/randomizer/3drando/starting_inventory.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/starting_inventory.cpp @@ -60,8 +60,8 @@ void GenerateStartingInventory() { AddItemToInventory(RG_GANONS_CASTLE_BOSS_KEY); } - if (ctx->GetOption(RSK_GERUDO_FORTRESS).Is(RO_GF_CARPENTERS_FREE) && - !ctx->GetOption(RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD)) { + if (ctx->GetOption(RSK_STARTING_GERUDO_CARD) || (ctx->GetOption(RSK_GERUDO_FORTRESS).Is(RO_GF_CARPENTERS_FREE) && + !ctx->GetOption(RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD))) { AddItemToInventory(RG_GERUDO_MEMBERSHIP_CARD); } @@ -72,46 +72,51 @@ void GenerateStartingInventory() { // TODO: Uncomment when these options are implemented. // AddItemToInventory(RG_PROGRESSIVE_STICK_UPGRADE, StartingStickCapacity.Value()); // AddItemToInventory(RG_PROGRESSIVE_NUT_UPGRADE, StartingNutCapacity.Value()); - // AddItemToInventory(RG_PROGRESSIVE_BOMB_BAG, StartingBombBag.Value()); - // AddItemToInventory((BombchuBag ? RG_PROGRESSIVE_BOMBCHU_BAG : RG_BOMBCHU_20), - // StartingBombchus.Value()); AddItemToInventory(RG_PROGRESSIVE_BOW, StartingBow.Value()); - // AddItemToInventory(RG_FIRE_ARROWS, StartingFireArrows.Value()); - // AddItemToInventory(RG_ICE_ARROWS, StartingIceArrows.Value()); - // AddItemToInventory(RG_LIGHT_ARROWS, StartingLightArrows.Value()); - // AddItemToInventory(RG_DINS_FIRE, StartingDinsFire.Value()); - // AddItemToInventory(RG_FARORES_WIND, StartingFaroresWind.Value()); - // AddItemToInventory(RG_NAYRUS_LOVE, StartingNayrusLove.Value()); - // AddItemToInventory(RG_PROGRESSIVE_SLINGSHOT, StartingSlingshot.Value()); - // AddItemToInventory(RG_BOOMERANG, StartingBoomerang.Value()); - // AddItemToInventory(RG_LENS_OF_TRUTH, StartingLensOfTruth.Value()); - // AddItemToInventory(RG_MAGIC_BEAN_PACK, StartingMagicBean.Value()); - // AddItemToInventory(RG_MEGATON_HAMMER, StartingMegatonHammer.Value()); - // AddItemToInventory(RG_PROGRESSIVE_HOOKSHOT, StartingHookshot.Value()); - // AddItemToInventory(RG_IRON_BOOTS, StartingIronBoots.Value()); - // AddItemToInventory(RG_HOVER_BOOTS, StartingHoverBoots.Value()); - // For starting bottles, we need to check if they are a big poe and add that if so - // since a big poe bottle is not logically equivalent to an empty bottle. - // if (StartingBottle1.Value() == STARTINGBOTTLE_BIG_POE) { - // AddItemToInventory(RG_BOTTLE_WITH_BIG_POE, 1); - // } else if (StartingBottle1.Value()) { - // AddItemToInventory(RG_EMPTY_BOTTLE, 1); - // } - // if (StartingBottle2.Value() == STARTINGBOTTLE_BIG_POE) { - // AddItemToInventory(RG_BOTTLE_WITH_BIG_POE, 1); - // } else if (StartingBottle2.Value()) { - // AddItemToInventory(RG_EMPTY_BOTTLE, 1); - // } - // if (StartingBottle3.Value() == STARTINGBOTTLE_BIG_POE) { - // AddItemToInventory(RG_BOTTLE_WITH_BIG_POE, 1); - // } else if (StartingBottle3.Value()) { - // AddItemToInventory(RG_EMPTY_BOTTLE, 1); - // } - // if (StartingBottle4.Value() == STARTINGBOTTLE_BIG_POE) { - // AddItemToInventory(RG_BOTTLE_WITH_BIG_POE, 1); - // } else if (StartingBottle4.Value()) { - // AddItemToInventory(RG_EMPTY_BOTTLE, 1); - // } - // AddItemToInventory(RG_RUTOS_LETTER, StartingRutoBottle.Value()); + AddItemToInventory(RG_PROGRESSIVE_BOMB_BAG, ctx->GetOption(RSK_STARTING_BOMB_BAG).Get()); + AddItemToInventory(RG_PROGRESSIVE_BOW, ctx->GetOption(RSK_STARTING_BOW).Get()); + AddItemToInventory(RG_PROGRESSIVE_SLINGSHOT, ctx->GetOption(RSK_STARTING_SLINGSHOT).Get()); + AddItemToInventory(RG_BOOMERANG, ctx->GetOption(RSK_STARTING_BOOMERANG) ? 1 : 0); + AddItemToInventory(RG_LENS_OF_TRUTH, ctx->GetOption(RSK_STARTING_LENS_OF_TRUTH) ? 1 : 0); + AddItemToInventory(RG_PROGRESSIVE_HOOKSHOT, ctx->GetOption(RSK_STARTING_HOOKSHOT).Get()); + // Bombchu bags only exist when the bombchu bag setting enables them; a single bag is one item, + // progressive bags are added per starting tier. + if (ctx->GetOption(RSK_BOMBCHU_BAG).Is(RO_BOMBCHU_BAG_SINGLE)) { + AddItemToInventory(RG_PROGRESSIVE_BOMBCHU_BAG, ctx->GetOption(RSK_STARTING_BOMBCHU_BAG).Get() ? 1 : 0); + } else if (ctx->GetOption(RSK_BOMBCHU_BAG).Is(RO_BOMBCHU_BAG_PROGRESSIVE)) { + AddItemToInventory(RG_PROGRESSIVE_BOMBCHU_BAG, ctx->GetOption(RSK_STARTING_BOMBCHU_BAG).Get()); + } + AddItemToInventory(RG_MEGATON_HAMMER, ctx->GetOption(RSK_STARTING_MEGATON_HAMMER) ? 1 : 0); + AddItemToInventory(RG_IRON_BOOTS, ctx->GetOption(RSK_STARTING_IRON_BOOTS) ? 1 : 0); + AddItemToInventory(RG_HOVER_BOOTS, ctx->GetOption(RSK_STARTING_HOVER_BOOTS) ? 1 : 0); + AddItemToInventory(RG_DINS_FIRE, ctx->GetOption(RSK_STARTING_DINS_FIRE) ? 1 : 0); + AddItemToInventory(RG_FARORES_WIND, ctx->GetOption(RSK_STARTING_FARORES_WIND) ? 1 : 0); + AddItemToInventory(RG_NAYRUS_LOVE, ctx->GetOption(RSK_STARTING_NAYRUS_LOVE) ? 1 : 0); + AddItemToInventory(RG_FIRE_ARROWS, ctx->GetOption(RSK_STARTING_FIRE_ARROWS) ? 1 : 0); + AddItemToInventory(RG_ICE_ARROWS, ctx->GetOption(RSK_STARTING_ICE_ARROWS) ? 1 : 0); + AddItemToInventory(RG_LIGHT_ARROWS, ctx->GetOption(RSK_STARTING_LIGHT_ARROWS) ? 1 : 0); + AddItemToInventory(RG_HYLIAN_SHIELD, ctx->GetOption(RSK_STARTING_HYLIAN_SHIELD) ? 1 : 0); + AddItemToInventory(RG_MIRROR_SHIELD, ctx->GetOption(RSK_STARTING_MIRROR_SHIELD) ? 1 : 0); + AddItemToInventory(RG_GORON_TUNIC, ctx->GetOption(RSK_STARTING_GORON_TUNIC) ? 1 : 0); + AddItemToInventory(RG_ZORA_TUNIC, ctx->GetOption(RSK_STARTING_ZORA_TUNIC) ? 1 : 0); + AddItemToInventory(RG_STONE_OF_AGONY, ctx->GetOption(RSK_STARTING_STONE_OF_AGONY) ? 1 : 0); + // A big poe bottle is not logically equivalent to an empty bottle, so it's a distinct item. + for (RandomizerSettingKey bottleKey : + { RSK_STARTING_BOTTLE_1, RSK_STARTING_BOTTLE_2, RSK_STARTING_BOTTLE_3, RSK_STARTING_BOTTLE_4 }) { + uint8_t bottle = ctx->GetOption(bottleKey).Get(); + if (bottle == RO_STARTING_BOTTLE_BIG_POE) { + AddItemToInventory(RG_BOTTLE_WITH_BIG_POE); + } else if (bottle == RO_STARTING_BOTTLE_RUTOS_LETTER) { + AddItemToInventory(RG_RUTOS_LETTER); + } else if (bottle != RO_STARTING_BOTTLE_OFF) { + AddItemToInventory(RG_EMPTY_BOTTLE); + } + } + // The weird egg only exists as an item when it's shuffled; vanilla gives it through the cutscene. + if (ctx->GetOption(RSK_SHUFFLE_WEIRD_EGG)) { + AddItemToInventory(RG_WEIRD_EGG, ctx->GetOption(RSK_STARTING_WEIRD_EGG) ? 1 : 0); + } + AddItemToInventory(RG_BUNNY_HOOD, ctx->GetOption(RSK_STARTING_BUNNY_HOOD) ? 1 : 0); + AddItemToInventory(RG_CLAIM_CHECK, ctx->GetOption(RSK_STARTING_CLAIM_CHECK) ? 1 : 0); AddItemToInventory(RG_PROGRESSIVE_OCARINA, ctx->GetOption(RSK_STARTING_OCARINA).Get()); AddItemToInventory(RG_ZELDAS_LULLABY, ctx->GetOption(RSK_STARTING_ZELDAS_LULLABY) ? 1 : 0); AddItemToInventory(RG_EPONAS_SONG, ctx->GetOption(RSK_STARTING_EPONAS_SONG) ? 1 : 0); @@ -127,23 +132,16 @@ void GenerateStartingInventory() { AddItemToInventory(RG_PRELUDE_OF_LIGHT, ctx->GetOption(RSK_STARTING_PRELUDE_OF_LIGHT) ? 1 : 0); AddItemToInventory(RG_KOKIRI_SWORD, ctx->GetOption(RSK_STARTING_KOKIRI_SWORD) ? 1 : 0); AddItemToInventory(RG_MAGIC_BEAN_PACK, ctx->GetOption(RSK_STARTING_BEANS) ? 1 : 0); - // if (ProgressiveGoronSword) { - // AddItemToInventory(RG_PROGRESSIVE_GORONSWORD, StartingBiggoronSword.Value()); - // } else { - // AddItemToInventory(RG_GIANTS_KNIFE, (StartingBiggoronSword.Is(STARTINGBGS_GIANTS_KNIFE)) ? 1 : 0); - // AddItemToInventory(RG_BIGGORON_SWORD, (StartingBiggoronSword.Is(STARTINGBGS_BIGGORON_SWORD)) ? 1 : 0); - // } + AddItemToInventory(RG_GIANTS_KNIFE, + ctx->GetOption(RSK_STARTING_BIGGORON_SWORD).Is(RO_STARTING_BGS_GIANTS_KNIFE) ? 1 : 0); + AddItemToInventory(RG_BIGGORON_SWORD, + ctx->GetOption(RSK_STARTING_BIGGORON_SWORD).Is(RO_STARTING_BGS_BIGGORON_SWORD) ? 1 : 0); AddItemToInventory(RG_MASTER_SWORD, ctx->GetOption(RSK_STARTING_MASTER_SWORD) ? 1 : 0); AddItemToInventory(RG_DEKU_SHIELD, ctx->GetOption(RSK_STARTING_DEKU_SHIELD) ? 1 : 0); - // AddItemToInventory(RG_HYLIAN_SHIELD, StartingHylianShield.Value()); - // AddItemToInventory(RG_MIRROR_SHIELD, StartingMirrorShield.Value()); - // AddItemToInventory(RG_GORON_TUNIC, StartingGoronTunic.Value()); - // AddItemToInventory(RG_ZORA_TUNIC, StartingZoraTunic.Value()); - // AddItemToInventory(RG_PROGRESSIVE_MAGIC_METER, StartingMagicMeter.Value()); - // AddItemToInventory(RG_PROGRESSIVE_STRENGTH, StartingStrength.Value()); - // AddItemToInventory(RG_PROGRESSIVE_SCALE, StartingScale.Value()); - // AddItemToInventory(RG_PROGRESSIVE_WALLET, StartingWallet.Value()); - // AddItemToInventory(RG_STONE_OF_AGONY, StartingShardOfAgony.Value()); + AddItemToInventory(RG_PROGRESSIVE_MAGIC_METER, ctx->GetOption(RSK_STARTING_MAGIC_METER).Get()); + AddItemToInventory(RG_PROGRESSIVE_STRENGTH, ctx->GetOption(RSK_STARTING_STRENGTH).Get()); + AddItemToInventory(RG_PROGRESSIVE_SCALE, ctx->GetOption(RSK_STARTING_SCALE).Get()); + AddItemToInventory(RG_PROGRESSIVE_WALLET, ctx->GetOption(RSK_STARTING_WALLET).Get()); // AddItemToInventory(RG_DOUBLE_DEFENSE, StartingDoubleDefense.Value()); // AddItemToInventory(RG_KOKIRI_EMERALD, StartingKokiriEmerald.Value()); // AddItemToInventory(RG_GORON_RUBY, StartingGoronRuby.Value()); diff --git a/soh/soh/Enhancements/randomizer/option.cpp b/soh/soh/Enhancements/randomizer/option.cpp index 8ae4f1b3b6..3a712b5e6b 100644 --- a/soh/soh/Enhancements/randomizer/option.cpp +++ b/soh/soh/Enhancements/randomizer/option.cpp @@ -94,6 +94,10 @@ uint8_t Option::GetOptionIndex() const { return CVarGetInteger(cvarName.c_str(), defaultOption); } +uint8_t Option::GetMenuOptionDefault() const { + return defaultOption; +} + const std::string& Option::GetOptionText(size_t index) const { return options[index]; } diff --git a/soh/soh/Enhancements/randomizer/option.h b/soh/soh/Enhancements/randomizer/option.h index a915821d47..2188aeed7f 100644 --- a/soh/soh/Enhancements/randomizer/option.h +++ b/soh/soh/Enhancements/randomizer/option.h @@ -225,6 +225,13 @@ class Option { */ uint8_t GetOptionIndex() const; + /** + * @brief Get the default menu index for this Option. + * + * @return uint8_t + */ + uint8_t GetMenuOptionDefault() const; + /** * @brief Set the rando context index for this Option. * diff --git a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerMiscEnums.h b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerMiscEnums.h index 6eb2bb4e08..8e3b39a452 100644 --- a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerMiscEnums.h +++ b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerMiscEnums.h @@ -334,13 +334,6 @@ RANDO_ENUM_ITEM(RSG_MENU_SECTION_HINTS) RANDO_ENUM_ITEM(RSG_MENU_SECTION_TRAPS) RANDO_ENUM_ITEM(RSG_MENU_COLUMN_STATIC_HINTS) RANDO_ENUM_ITEM(RSG_MENU_SECTION_STATIC_HINTS) -RANDO_ENUM_ITEM(RSG_MENU_SIDEBAR_STARTING_ITEMS) -RANDO_ENUM_ITEM(RSG_MENU_COLUMN_STARTING_EQUIPMENT) -RANDO_ENUM_ITEM(RSG_MENU_SECTION_STARTING_EQUIPS) -RANDO_ENUM_ITEM(RSG_MENU_SECTION_STARTING_ITEMS) -RANDO_ENUM_ITEM(RSG_MENU_COLUMN_STARTING_SONGS) -RANDO_ENUM_ITEM(RSG_MENU_SECTION_NORMAL_SONGS) -RANDO_ENUM_ITEM(RSG_MENU_SECTION_WARP_SONGS) RANDO_ENUM_ITEM(RSG_OPEN) RANDO_ENUM_ITEM(RSG_WORLD) RANDO_ENUM_ITEM(RSG_SHUFFLE) diff --git a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerOptions.h b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerOptions.h index e19e34a599..96e802bf85 100644 --- a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerOptions.h +++ b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerOptions.h @@ -294,6 +294,21 @@ RANDO_ENUM_ITEM(RO_STARTING_OCARINA_FAIRY) RANDO_ENUM_ITEM(RO_STARTING_OCARINA_TIME) RANDO_ENUM_END(RandoOptionStartingOcarina) +// Starting Bottle Settings (off, empty bottle, bottle with big poe, ruto's letter (bottle 1 only)) +RANDO_ENUM_BEGIN(RandoOptionStartingBottle) +RANDO_ENUM_ITEM(RO_STARTING_BOTTLE_OFF) +RANDO_ENUM_ITEM(RO_STARTING_BOTTLE_EMPTY) +RANDO_ENUM_ITEM(RO_STARTING_BOTTLE_BIG_POE) +RANDO_ENUM_ITEM(RO_STARTING_BOTTLE_RUTOS_LETTER) +RANDO_ENUM_END(RandoOptionStartingBottle) + +// Starting Biggoron's Sword Settings (off, giant's knife, biggoron's sword) +RANDO_ENUM_BEGIN(RandoOptionStartingBiggoronSword) +RANDO_ENUM_ITEM(RO_STARTING_BGS_OFF) +RANDO_ENUM_ITEM(RO_STARTING_BGS_GIANTS_KNIFE) +RANDO_ENUM_ITEM(RO_STARTING_BGS_BIGGORON_SWORD) +RANDO_ENUM_END(RandoOptionStartingBiggoronSword) + // Mask Quest Settings (vanilla, completed, shuffle) RANDO_ENUM_BEGIN(RandoOptionMaskQuest) RANDO_ENUM_ITEM(RO_MASK_QUEST_VANILLA) diff --git a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerSettingKey.h b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerSettingKey.h index 764fa916ed..6f6ea57ee7 100644 --- a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerSettingKey.h +++ b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerSettingKey.h @@ -144,6 +144,39 @@ RANDO_ENUM_ITEM(RSK_SKIP_CHILD_ZELDA) RANDO_ENUM_ITEM(RSK_STARTING_STICKS) RANDO_ENUM_ITEM(RSK_STARTING_NUTS) RANDO_ENUM_ITEM(RSK_STARTING_BEANS) +RANDO_ENUM_ITEM(RSK_STARTING_MEGATON_HAMMER) +RANDO_ENUM_ITEM(RSK_STARTING_BOOMERANG) +RANDO_ENUM_ITEM(RSK_STARTING_LENS_OF_TRUTH) +RANDO_ENUM_ITEM(RSK_STARTING_DINS_FIRE) +RANDO_ENUM_ITEM(RSK_STARTING_FARORES_WIND) +RANDO_ENUM_ITEM(RSK_STARTING_NAYRUS_LOVE) +RANDO_ENUM_ITEM(RSK_STARTING_FIRE_ARROWS) +RANDO_ENUM_ITEM(RSK_STARTING_ICE_ARROWS) +RANDO_ENUM_ITEM(RSK_STARTING_LIGHT_ARROWS) +RANDO_ENUM_ITEM(RSK_STARTING_IRON_BOOTS) +RANDO_ENUM_ITEM(RSK_STARTING_HOVER_BOOTS) +RANDO_ENUM_ITEM(RSK_STARTING_HYLIAN_SHIELD) +RANDO_ENUM_ITEM(RSK_STARTING_MIRROR_SHIELD) +RANDO_ENUM_ITEM(RSK_STARTING_GORON_TUNIC) +RANDO_ENUM_ITEM(RSK_STARTING_ZORA_TUNIC) +RANDO_ENUM_ITEM(RSK_STARTING_STONE_OF_AGONY) +RANDO_ENUM_ITEM(RSK_STARTING_HOOKSHOT) +RANDO_ENUM_ITEM(RSK_STARTING_BOW) +RANDO_ENUM_ITEM(RSK_STARTING_SLINGSHOT) +RANDO_ENUM_ITEM(RSK_STARTING_BOMB_BAG) +RANDO_ENUM_ITEM(RSK_STARTING_STRENGTH) +RANDO_ENUM_ITEM(RSK_STARTING_SCALE) +RANDO_ENUM_ITEM(RSK_STARTING_WALLET) +RANDO_ENUM_ITEM(RSK_STARTING_MAGIC_METER) +RANDO_ENUM_ITEM(RSK_STARTING_BOMBCHU_BAG) +RANDO_ENUM_ITEM(RSK_STARTING_BOTTLE_1) +RANDO_ENUM_ITEM(RSK_STARTING_BOTTLE_2) +RANDO_ENUM_ITEM(RSK_STARTING_BOTTLE_3) +RANDO_ENUM_ITEM(RSK_STARTING_BOTTLE_4) +RANDO_ENUM_ITEM(RSK_STARTING_WEIRD_EGG) +RANDO_ENUM_ITEM(RSK_STARTING_CLAIM_CHECK) +RANDO_ENUM_ITEM(RSK_STARTING_GERUDO_CARD) +RANDO_ENUM_ITEM(RSK_STARTING_BIGGORON_SWORD) RANDO_ENUM_ITEM(RSK_FULL_WALLETS) RANDO_ENUM_ITEM(RSK_SHUFFLE_CHEST_MINIGAME) RANDO_ENUM_ITEM(RSK_BIG_POE_COUNT) @@ -256,6 +289,7 @@ RANDO_ENUM_ITEM(RSK_SHUFFLE_SIGNS) RANDO_ENUM_ITEM(RSK_ROCS_FEATHER) RANDO_ENUM_ITEM(RSK_SHUFFLE_ICICLES) RANDO_ENUM_ITEM(RSK_SHUFFLE_RED_ICE) +RANDO_ENUM_ITEM(RSK_STARTING_BUNNY_HOOD) RANDO_ENUM_ITEM(RSK_MAX) RANDO_ENUM_END(RandomizerSettingKey) diff --git a/soh/soh/Enhancements/randomizer/savefile.cpp b/soh/soh/Enhancements/randomizer/savefile.cpp index a420abda02..15d644c902 100644 --- a/soh/soh/Enhancements/randomizer/savefile.cpp +++ b/soh/soh/Enhancements/randomizer/savefile.cpp @@ -125,6 +125,38 @@ void SetStartingItems() { Item_Give(NULL, ITEM_SWORD_KOKIRI); if (Randomizer_GetSettingValue(RSK_STARTING_DEKU_SHIELD)) Item_Give(NULL, ITEM_SHIELD_DEKU); + if (Randomizer_GetSettingValue(RSK_STARTING_HYLIAN_SHIELD)) + Item_Give(NULL, ITEM_SHIELD_HYLIAN); + if (Randomizer_GetSettingValue(RSK_STARTING_MIRROR_SHIELD)) + Item_Give(NULL, ITEM_SHIELD_MIRROR); + if (Randomizer_GetSettingValue(RSK_STARTING_GORON_TUNIC)) + Item_Give(NULL, ITEM_TUNIC_GORON); + if (Randomizer_GetSettingValue(RSK_STARTING_ZORA_TUNIC)) + Item_Give(NULL, ITEM_TUNIC_ZORA); + if (Randomizer_GetSettingValue(RSK_STARTING_IRON_BOOTS)) + Item_Give(NULL, ITEM_BOOTS_IRON); + if (Randomizer_GetSettingValue(RSK_STARTING_HOVER_BOOTS)) + Item_Give(NULL, ITEM_BOOTS_HOVER); + if (Randomizer_GetSettingValue(RSK_STARTING_MEGATON_HAMMER)) + Item_Give(NULL, ITEM_HAMMER); + if (Randomizer_GetSettingValue(RSK_STARTING_BOOMERANG)) + Item_Give(NULL, ITEM_BOOMERANG); + if (Randomizer_GetSettingValue(RSK_STARTING_LENS_OF_TRUTH)) + Item_Give(NULL, ITEM_LENS); + if (Randomizer_GetSettingValue(RSK_STARTING_DINS_FIRE)) + Item_Give(NULL, ITEM_DINS_FIRE); + if (Randomizer_GetSettingValue(RSK_STARTING_FARORES_WIND)) + Item_Give(NULL, ITEM_FARORES_WIND); + if (Randomizer_GetSettingValue(RSK_STARTING_NAYRUS_LOVE)) + Item_Give(NULL, ITEM_NAYRUS_LOVE); + if (Randomizer_GetSettingValue(RSK_STARTING_FIRE_ARROWS)) + Item_Give(NULL, ITEM_ARROW_FIRE); + if (Randomizer_GetSettingValue(RSK_STARTING_ICE_ARROWS)) + Item_Give(NULL, ITEM_ARROW_ICE); + if (Randomizer_GetSettingValue(RSK_STARTING_LIGHT_ARROWS)) + Item_Give(NULL, ITEM_ARROW_LIGHT); + if (Randomizer_GetSettingValue(RSK_STARTING_STONE_OF_AGONY)) + Item_Give(NULL, ITEM_STONE_OF_AGONY); // Songs if (Randomizer_GetSettingValue(RSK_STARTING_ZELDAS_LULLABY)) @@ -182,6 +214,151 @@ void SetStartingItems() { } } + // Tiered/progressive starting items. The upgrade items are given cumulatively where Item_Give + // only sets the base inventory content on the first tier. + switch (Randomizer_GetSettingValue(RSK_STARTING_HOOKSHOT)) { + case 2: + Item_Give(NULL, ITEM_LONGSHOT); + break; + case 1: + Item_Give(NULL, ITEM_HOOKSHOT); + break; + } + + uint8_t startBow = Randomizer_GetSettingValue(RSK_STARTING_BOW); + if (startBow >= 1) + Item_Give(NULL, ITEM_QUIVER_30); + if (startBow >= 2) + Item_Give(NULL, ITEM_QUIVER_40); + if (startBow >= 3) + Item_Give(NULL, ITEM_QUIVER_50); + + uint8_t startSlingshot = Randomizer_GetSettingValue(RSK_STARTING_SLINGSHOT); + if (startSlingshot >= 1) + Item_Give(NULL, ITEM_SLINGSHOT); + if (startSlingshot >= 2) + Item_Give(NULL, ITEM_BULLET_BAG_40); + if (startSlingshot >= 3) + Item_Give(NULL, ITEM_BULLET_BAG_50); + + uint8_t startBombBag = Randomizer_GetSettingValue(RSK_STARTING_BOMB_BAG); + if (startBombBag >= 1) + Item_Give(NULL, ITEM_BOMB_BAG_20); + if (startBombBag >= 2) + Item_Give(NULL, ITEM_BOMB_BAG_30); + if (startBombBag >= 3) + Item_Give(NULL, ITEM_BOMB_BAG_40); + + switch (Randomizer_GetSettingValue(RSK_STARTING_STRENGTH)) { + case 3: + Item_Give(NULL, ITEM_GAUNTLETS_GOLD); + break; + case 2: + Item_Give(NULL, ITEM_GAUNTLETS_SILVER); + break; + case 1: + Item_Give(NULL, ITEM_BRACELET); + break; + } + + switch (Randomizer_GetSettingValue(RSK_STARTING_SCALE)) { + case 2: + Item_Give(NULL, ITEM_SCALE_GOLDEN); + break; + case 1: + Item_Give(NULL, ITEM_SCALE_SILVER); + break; + } + + switch (Randomizer_GetSettingValue(RSK_STARTING_WALLET)) { + case 2: + Item_Give(NULL, ITEM_WALLET_GIANT); + break; + case 1: + Item_Give(NULL, ITEM_WALLET_ADULT); + break; + } + + uint8_t startMagic = Randomizer_GetSettingValue(RSK_STARTING_MAGIC_METER); + if (startMagic > 0) { + gSaveContext.isMagicAcquired = true; + gSaveContext.isDoubleMagicAcquired = startMagic >= 2; + gSaveContext.magicLevel = startMagic; + gSaveContext.magicCapacity = startMagic * MAGIC_NORMAL_METER; + gSaveContext.magic = gSaveContext.magicCapacity; + } + + uint8_t startBombchu = Randomizer_GetSettingValue(RSK_STARTING_BOMBCHU_BAG); + if (startBombchu > 0) { + uint8_t bombchuMode = Randomizer_GetSettingValue(RSK_BOMBCHU_BAG); + if (bombchuMode == RO_BOMBCHU_BAG_SINGLE) { + INV_CONTENT(ITEM_BOMBCHU) = ITEM_BOMBCHU; + AMMO(ITEM_BOMBCHU) = 20; + } else if (bombchuMode == RO_BOMBCHU_BAG_PROGRESSIVE) { + static const uint8_t bombchuCapacities[] = { 0, 20, 30, 50 }; + gSaveContext.ship.quest.data.randomizer.bombchuUpgradeLevel = startBombchu; + INV_CONTENT(ITEM_BOMBCHU) = ITEM_BOMBCHU; + AMMO(ITEM_BOMBCHU) = bombchuCapacities[startBombchu]; + } + } + + // Big poe bottles first: Item_Give for a bottled content fills the first empty-bottle + // slot, so each poe is paired with the bottle given right before it. Ruto's Letter fills + // an empty inventory slot on its own. The remaining plain empty bottles follow. + uint8_t emptyBottles = 0; + for (RandomizerSettingKey bottleKey : + { RSK_STARTING_BOTTLE_1, RSK_STARTING_BOTTLE_2, RSK_STARTING_BOTTLE_3, RSK_STARTING_BOTTLE_4 }) { + uint8_t bottle = Randomizer_GetSettingValue(bottleKey); + switch (bottle) { + case RO_STARTING_BOTTLE_OFF: + break; + case RO_STARTING_BOTTLE_EMPTY: + emptyBottles++; + break; + case RO_STARTING_BOTTLE_BIG_POE: + Item_Give(NULL, ITEM_BOTTLE); + Item_Give(NULL, ITEM_BIG_POE); + break; + case RO_STARTING_BOTTLE_RUTOS_LETTER: + Item_Give(NULL, ITEM_LETTER_RUTO); + break; + default: + SPDLOG_ERROR("[SetStartingItems] Unhandled value for bottleKey {}: {}", (int)bottleKey, bottle); + assert(false); + break; + } + } + for (uint8_t i = 0; i < emptyBottles; i++) { + Item_Give(NULL, ITEM_BOTTLE); + } + + if (Randomizer_GetSettingValue(RSK_STARTING_WEIRD_EGG) && Randomizer_GetSettingValue(RSK_SHUFFLE_WEIRD_EGG)) { + Item_Give(NULL, ITEM_WEIRD_EGG); + } + if (Randomizer_GetSettingValue(RSK_STARTING_CLAIM_CHECK)) { + Item_Give(NULL, ITEM_CLAIM_CHECK); + } + if (Randomizer_GetSettingValue(RSK_STARTING_GERUDO_CARD)) { + Item_Give(NULL, ITEM_GERUDO_CARD); + } + + if (Randomizer_GetSettingValue(RSK_STARTING_BUNNY_HOOD)) { + Flags_SetRandomizerInf(RAND_INF_CHILD_TRADES_HAS_MASK_BUNNY); + if (INV_CONTENT(ITEM_TRADE_CHILD) == ITEM_NONE) { + INV_CONTENT(ITEM_TRADE_CHILD) = ITEM_MASK_BUNNY; + } + } + + // Giant's Knife and Biggoron's Sword share an item slot, bgsFlag marks unbreakable + switch (Randomizer_GetSettingValue(RSK_STARTING_BIGGORON_SWORD)) { + case RO_STARTING_BGS_BIGGORON_SWORD: + gSaveContext.bgsFlag = true; + [[fallthrough]]; + case RO_STARTING_BGS_GIANTS_KNIFE: + Item_Give(NULL, ITEM_SWORD_BGS); + break; + } + if (Randomizer_GetSettingValue(RSK_FULL_WALLETS)) { GiveLinkRupees(9001); } diff --git a/soh/soh/Enhancements/randomizer/settings.cpp b/soh/soh/Enhancements/randomizer/settings.cpp index af347bef07..78988f6359 100644 --- a/soh/soh/Enhancements/randomizer/settings.cpp +++ b/soh/soh/Enhancements/randomizer/settings.cpp @@ -839,9 +839,25 @@ void Settings::CreateOptions() { }); 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_CALLBACK(RSK_SHUFFLE_SWIM, { + if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleSwim"), 0)) { + CVarSetInteger(CVAR_RANDOMIZER_SETTING("StartingScale"), 0); + mOptions[RSK_STARTING_SCALE].Disable("Disabled because Shuffle Swim is on."); + } else { + mOptions[RSK_STARTING_SCALE].Enable(); + } + }); 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_CALLBACK(RSK_SHUFFLE_GRAB, { + if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleGrab"), 0)) { + CVarSetInteger(CVAR_RANDOMIZER_SETTING("StartingStrength"), 0); + mOptions[RSK_STARTING_STRENGTH].Disable("Disabled because Shuffle Grab is on."); + } else { + mOptions[RSK_STARTING_STRENGTH].Enable(); + } + }); 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]); @@ -1328,6 +1344,40 @@ void Settings::CreateOptions() { 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_STARTING_MEGATON_HAMMER, "Start with Megaton Hammer", CVAR_RANDOMIZER_SETTING("StartingMegatonHammer")); + OPT_BOOL(RSK_STARTING_BOOMERANG, "Start with Boomerang", CVAR_RANDOMIZER_SETTING("StartingBoomerang")); + OPT_BOOL(RSK_STARTING_LENS_OF_TRUTH, "Start with Lens of Truth", CVAR_RANDOMIZER_SETTING("StartingLensOfTruth")); + OPT_BOOL(RSK_STARTING_DINS_FIRE, "Start with Din's Fire", CVAR_RANDOMIZER_SETTING("StartingDinsFire")); + OPT_BOOL(RSK_STARTING_FARORES_WIND, "Start with Farore's Wind", CVAR_RANDOMIZER_SETTING("StartingFaroresWind")); + OPT_BOOL(RSK_STARTING_NAYRUS_LOVE, "Start with Nayru's Love", CVAR_RANDOMIZER_SETTING("StartingNayrusLove")); + OPT_BOOL(RSK_STARTING_FIRE_ARROWS, "Start with Fire Arrows", CVAR_RANDOMIZER_SETTING("StartingFireArrows")); + OPT_BOOL(RSK_STARTING_ICE_ARROWS, "Start with Ice Arrows", CVAR_RANDOMIZER_SETTING("StartingIceArrows")); + OPT_BOOL(RSK_STARTING_LIGHT_ARROWS, "Start with Light Arrows", CVAR_RANDOMIZER_SETTING("StartingLightArrows")); + OPT_BOOL(RSK_STARTING_IRON_BOOTS, "Start with Iron Boots", CVAR_RANDOMIZER_SETTING("StartingIronBoots")); + OPT_BOOL(RSK_STARTING_HOVER_BOOTS, "Start with Hover Boots", CVAR_RANDOMIZER_SETTING("StartingHoverBoots")); + OPT_BOOL(RSK_STARTING_HYLIAN_SHIELD, "Start with Hylian Shield", CVAR_RANDOMIZER_SETTING("StartingHylianShield")); + OPT_BOOL(RSK_STARTING_MIRROR_SHIELD, "Start with Mirror Shield", CVAR_RANDOMIZER_SETTING("StartingMirrorShield")); + OPT_BOOL(RSK_STARTING_GORON_TUNIC, "Start with Goron Tunic", CVAR_RANDOMIZER_SETTING("StartingGoronTunic")); + OPT_BOOL(RSK_STARTING_ZORA_TUNIC, "Start with Zora Tunic", CVAR_RANDOMIZER_SETTING("StartingZoraTunic")); + OPT_BOOL(RSK_STARTING_STONE_OF_AGONY, "Start with Stone of Agony", CVAR_RANDOMIZER_SETTING("StartingStoneOfAgony")); + OPT_U8(RSK_STARTING_HOOKSHOT, "Start with Hookshot", {"Off", "Hookshot", "Longshot"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("StartingHookshot"), "", WIDGET_CVAR_COMBOBOX, 0); + OPT_U8(RSK_STARTING_BOW, "Start with Bow", {"Off", "Bow (Quiver 30)", "Bow (Quiver 40)", "Bow (Quiver 50)"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("StartingBow"), "", WIDGET_CVAR_COMBOBOX, 0); + OPT_U8(RSK_STARTING_SLINGSHOT, "Start with Slingshot", {"Off", "Slingshot (30)", "Slingshot (40)", "Slingshot (50)"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("StartingSlingshot"), "", WIDGET_CVAR_COMBOBOX, 0); + OPT_U8(RSK_STARTING_BOMB_BAG, "Start with Bomb Bag", {"Off", "Bomb Bag (20)", "Bomb Bag (30)", "Bomb Bag (40)"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("StartingBombBag"), "", WIDGET_CVAR_COMBOBOX, 0); + OPT_U8(RSK_STARTING_STRENGTH, "Start with Strength Upgrade", {"Off", "Goron's Bracelet", "Silver Gauntlets", "Golden Gauntlets"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("StartingStrength"), "", WIDGET_CVAR_COMBOBOX, 0); + OPT_U8(RSK_STARTING_SCALE, "Start with Diving Scale", {"Off", "Silver Scale", "Golden Scale"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("StartingScale"), "", WIDGET_CVAR_COMBOBOX, 0); + OPT_U8(RSK_STARTING_WALLET, "Start with Wallet Upgrade", {"Off", "Adult's Wallet", "Giant's Wallet"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("StartingWallet"), "", WIDGET_CVAR_COMBOBOX, 0); + OPT_U8(RSK_STARTING_MAGIC_METER, "Start with Magic Meter", {"Off", "Single Magic", "Double Magic"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("StartingMagicMeter"), "", WIDGET_CVAR_COMBOBOX, 0); + OPT_U8(RSK_STARTING_BOMBCHU_BAG, "Start with Bombchu Bag", {"Off", "Bombchu Bag (20)", "Bombchu Bag (30)", "Bombchu Bag (50)"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("StartingBombchuBag"), "", WIDGET_CVAR_COMBOBOX, 0); + OPT_U8(RSK_STARTING_BOTTLE_1, "Starting Bottle 1", {"Off", "Empty Bottle", "Bottle with Big Poe", "Ruto's Letter"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("StartingBottle1"), "", WIDGET_CVAR_COMBOBOX, 0); + OPT_U8(RSK_STARTING_BOTTLE_2, "Starting Bottle 2", {"Off", "Empty Bottle", "Bottle with Big Poe"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("StartingBottle2"), "", WIDGET_CVAR_COMBOBOX, 0); + OPT_U8(RSK_STARTING_BOTTLE_3, "Starting Bottle 3", {"Off", "Empty Bottle", "Bottle with Big Poe"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("StartingBottle3"), "", WIDGET_CVAR_COMBOBOX, 0); + OPT_U8(RSK_STARTING_BOTTLE_4, "Starting Bottle 4", {"Off", "Empty Bottle", "Bottle with Big Poe"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("StartingBottle4"), "", WIDGET_CVAR_COMBOBOX, 0); + OPT_BOOL(RSK_STARTING_WEIRD_EGG, "Start with Weird Egg", CVAR_RANDOMIZER_SETTING("StartingWeirdEgg")); + OPT_BOOL(RSK_STARTING_CLAIM_CHECK, "Start with Claim Check", CVAR_RANDOMIZER_SETTING("StartingClaimCheck")); + OPT_BOOL(RSK_STARTING_GERUDO_CARD, "Start with Gerudo Card", CVAR_RANDOMIZER_SETTING("StartingGerudoCard")); + OPT_BOOL(RSK_STARTING_BUNNY_HOOD, "Start with Bunny Hood", CVAR_RANDOMIZER_SETTING("StartingBunnyHood")); + OPT_U8(RSK_STARTING_BIGGORON_SWORD, "Start with Biggoron's Sword", {"Off", "Giant's Knife", "Biggoron's Sword"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("StartingBiggoronSword"), "", WIDGET_CVAR_COMBOBOX, 0); 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); @@ -2028,62 +2078,6 @@ void Settings::CreateOptions() { &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{ - &mOptionGroups[RSG_MENU_SECTION_STARTING_EQUIPS], - &mOptionGroups[RSG_MENU_SECTION_STARTING_ITEMS], - }, - WidgetContainerType::COLUMN); - mOptionGroups[RSG_MENU_SECTION_NORMAL_SONGS] = OptionGroup::SubGroup("Normal Songs", - { - &mOptions[RSK_STARTING_ZELDAS_LULLABY], - &mOptions[RSK_STARTING_EPONAS_SONG], - &mOptions[RSK_STARTING_SARIAS_SONG], - &mOptions[RSK_STARTING_SUNS_SONG], - &mOptions[RSK_STARTING_SONG_OF_TIME], - &mOptions[RSK_STARTING_SONG_OF_STORMS], - }, - WidgetContainerType::SECTION); - mOptionGroups[RSG_MENU_SECTION_WARP_SONGS] = OptionGroup::SubGroup("Warp Songs", - { - &mOptions[RSK_STARTING_MINUET_OF_FOREST], - &mOptions[RSK_STARTING_BOLERO_OF_FIRE], - &mOptions[RSK_STARTING_SERENADE_OF_WATER], - &mOptions[RSK_STARTING_REQUIEM_OF_SPIRIT], - &mOptions[RSK_STARTING_NOCTURNE_OF_SHADOW], - &mOptions[RSK_STARTING_PRELUDE_OF_LIGHT], - }, - WidgetContainerType::SECTION); - mOptionGroups[RSG_MENU_COLUMN_STARTING_SONGS] = - OptionGroup::SubGroup("", - std::initializer_list{ - &mOptionGroups[RSG_MENU_SECTION_NORMAL_SONGS], - &mOptionGroups[RSG_MENU_SECTION_WARP_SONGS], - }, - WidgetContainerType::COLUMN); - mOptionGroups[RSG_MENU_SIDEBAR_STARTING_ITEMS] = - OptionGroup::SubGroup("Starting Items", - std::initializer_list{ - &mOptionGroups[RSG_MENU_COLUMN_STARTING_EQUIPMENT], - &mOptionGroups[RSG_MENU_COLUMN_STARTING_SONGS], - }, - WidgetContainerType::TABLE); mOptionGroups[RSG_OPEN] = OptionGroup("Open Settings", { &mOptions[RSK_FOREST], &mOptions[RSK_KAK_GATE], @@ -2253,8 +2247,25 @@ void Settings::CreateOptions() { &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] }); + OptionGroup::SubGroup("Items", { &mOptions[RSK_STARTING_OCARINA], &mOptions[RSK_STARTING_KOKIRI_SWORD], + &mOptions[RSK_STARTING_MASTER_SWORD], &mOptions[RSK_STARTING_DEKU_SHIELD], + &mOptions[RSK_STARTING_HYLIAN_SHIELD], &mOptions[RSK_STARTING_MIRROR_SHIELD], + &mOptions[RSK_STARTING_GORON_TUNIC], &mOptions[RSK_STARTING_ZORA_TUNIC], + &mOptions[RSK_STARTING_IRON_BOOTS], &mOptions[RSK_STARTING_HOVER_BOOTS], + &mOptions[RSK_STARTING_MEGATON_HAMMER], &mOptions[RSK_STARTING_BOOMERANG], + &mOptions[RSK_STARTING_LENS_OF_TRUTH], &mOptions[RSK_STARTING_DINS_FIRE], + &mOptions[RSK_STARTING_FARORES_WIND], &mOptions[RSK_STARTING_NAYRUS_LOVE], + &mOptions[RSK_STARTING_FIRE_ARROWS], &mOptions[RSK_STARTING_ICE_ARROWS], + &mOptions[RSK_STARTING_LIGHT_ARROWS], &mOptions[RSK_STARTING_STONE_OF_AGONY], + &mOptions[RSK_STARTING_HOOKSHOT], &mOptions[RSK_STARTING_BOW], + &mOptions[RSK_STARTING_SLINGSHOT], &mOptions[RSK_STARTING_BOMB_BAG], + &mOptions[RSK_STARTING_STRENGTH], &mOptions[RSK_STARTING_SCALE], + &mOptions[RSK_STARTING_WALLET], &mOptions[RSK_STARTING_MAGIC_METER], + &mOptions[RSK_STARTING_BOMBCHU_BAG], &mOptions[RSK_STARTING_BOTTLE_1], + &mOptions[RSK_STARTING_BOTTLE_2], &mOptions[RSK_STARTING_BOTTLE_3], + &mOptions[RSK_STARTING_BOTTLE_4], &mOptions[RSK_STARTING_WEIRD_EGG], + &mOptions[RSK_STARTING_CLAIM_CHECK], &mOptions[RSK_STARTING_GERUDO_CARD], + &mOptions[RSK_STARTING_BIGGORON_SWORD], &mOptions[RSK_STARTING_BUNNY_HOOD] }); mOptionGroups[RSG_STARTING_SONGS] = OptionGroup::SubGroup("Ocarina Songs", { &mOptions[RSK_STARTING_ZELDAS_LULLABY], @@ -2263,7 +2274,6 @@ void Settings::CreateOptions() { &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], @@ -2601,6 +2611,17 @@ void Context::FinalizeSettings(const std::set& excludedLocation if (mOptions[RSK_SHUFFLE_DEKU_NUT_BAG]) { mOptions[RSK_STARTING_NUTS].Set(false); } + if (mOptions[RSK_SHUFFLE_SWIM]) { + mOptions[RSK_STARTING_SCALE].Set(0); + } + if (mOptions[RSK_SHUFFLE_GRAB]) { + mOptions[RSK_STARTING_STRENGTH].Set(0); + } + + if (mOptions[RSK_ZORAS_FOUNTAIN].IsNot(RO_ZF_OPEN) && + mOptions[RSK_STARTING_BOTTLE_1].IsNot(RO_STARTING_BOTTLE_RUTOS_LETTER)) { + mOptions[RSK_STARTING_BOTTLE_4].Set(RO_STARTING_BOTTLE_OFF); + } // RANDOTODO implement chest shuffle with keysanity // ShuffleChestMinigame.Set(cvarSettings[RSK_SHUFFLE_CHEST_MINIGAME]); @@ -2957,6 +2978,40 @@ void Settings::RandomizeAllSettings() { case RSK_STARTING_REQUIEM_OF_SPIRIT: case RSK_STARTING_NOCTURNE_OF_SHADOW: case RSK_STARTING_PRELUDE_OF_LIGHT: + case RSK_STARTING_MEGATON_HAMMER: + case RSK_STARTING_BOOMERANG: + case RSK_STARTING_LENS_OF_TRUTH: + case RSK_STARTING_DINS_FIRE: + case RSK_STARTING_FARORES_WIND: + case RSK_STARTING_NAYRUS_LOVE: + case RSK_STARTING_FIRE_ARROWS: + case RSK_STARTING_ICE_ARROWS: + case RSK_STARTING_LIGHT_ARROWS: + case RSK_STARTING_IRON_BOOTS: + case RSK_STARTING_HOVER_BOOTS: + case RSK_STARTING_HYLIAN_SHIELD: + case RSK_STARTING_MIRROR_SHIELD: + case RSK_STARTING_GORON_TUNIC: + case RSK_STARTING_ZORA_TUNIC: + case RSK_STARTING_STONE_OF_AGONY: + case RSK_STARTING_HOOKSHOT: + case RSK_STARTING_BOW: + case RSK_STARTING_SLINGSHOT: + case RSK_STARTING_BOMB_BAG: + case RSK_STARTING_STRENGTH: + case RSK_STARTING_SCALE: + case RSK_STARTING_WALLET: + case RSK_STARTING_MAGIC_METER: + case RSK_STARTING_BOMBCHU_BAG: + case RSK_STARTING_BOTTLE_1: + case RSK_STARTING_BOTTLE_2: + case RSK_STARTING_BOTTLE_3: + case RSK_STARTING_BOTTLE_4: + case RSK_STARTING_WEIRD_EGG: + case RSK_STARTING_CLAIM_CHECK: + case RSK_STARTING_GERUDO_CARD: + case RSK_STARTING_BIGGORON_SWORD: + case RSK_STARTING_BUNNY_HOOD: continue; default: break; diff --git a/soh/soh/SohGui/SohGui.hpp b/soh/soh/SohGui/SohGui.hpp index bc2d5fec68..bfaee2d6e8 100644 --- a/soh/soh/SohGui/SohGui.hpp +++ b/soh/soh/SohGui/SohGui.hpp @@ -45,6 +45,7 @@ void ShowRandomizerSettingsMenu(); void ShowEscMenu(); UIWidgets::Colors GetMenuThemeColor(); std::shared_ptr GetSohMenu(); +void DrawStartingItemsMenu(WidgetInfo& info); } // namespace SohGui #define THEME_COLOR SohGui::GetMenuThemeColor() diff --git a/soh/soh/SohGui/SohMenuRandomizer.cpp b/soh/soh/SohGui/SohMenuRandomizer.cpp index a51a4fbca4..44272400bd 100644 --- a/soh/soh/SohGui/SohMenuRandomizer.cpp +++ b/soh/soh/SohGui/SohMenuRandomizer.cpp @@ -691,7 +691,9 @@ void SohMenu::AddMenuRandomizer() { randoSettings->GetOptionGroup(RSG_MENU_SIDEBAR_DUNGEONS).AddWidgets(path); randoSettings->GetOptionGroup(RSG_MENU_SIDEBAR_SHUFFLES).AddWidgets(path); randoSettings->GetOptionGroup(RSG_MENU_SIDEBAR_HINTS_TRAPS).AddWidgets(path); - randoSettings->GetOptionGroup(RSG_MENU_SIDEBAR_STARTING_ITEMS).AddWidgets(path); + path.sidebarName = "Starting Items"; + AddSidebarEntry("Randomizer", path.sidebarName, 1); + AddWidget(path, "Starting Items", WIDGET_CUSTOM).CustomFunction(DrawStartingItemsMenu); path.sidebarName = "Locations"; AddSidebarEntry("Randomizer", path.sidebarName, 1); AddWidget(path, "Excluded Locations", WIDGET_CUSTOM).CustomFunction(DrawLocationsMenu); diff --git a/soh/soh/SohGui/SohMenuStartingItems.cpp b/soh/soh/SohGui/SohMenuStartingItems.cpp new file mode 100644 index 0000000000..f2c5c4960d --- /dev/null +++ b/soh/soh/SohGui/SohMenuStartingItems.cpp @@ -0,0 +1,336 @@ +#include "SohMenu.h" +#include "soh/SohGui/SohGui.hpp" +#include "soh/SohGui/UIWidgets.hpp" +#include "soh/SohGui/ImGuiUtils.h" +#include "soh/OTRGlobals.h" +#include "soh/cvar_prefixes.h" +#include "soh/Enhancements/game-interactor/GameInteractor.h" +#include "soh/Enhancements/randomizer/settings.h" + +#include +#include +#include + +extern "C" { +#include "variables.h" +#include "z64.h" +} + +namespace SohGui { + +using namespace UIWidgets; + +static constexpr float kIconSize = 48.0f; +static const ImVec2 kSongSize = ImVec2(32.0f, 48.0f); + +static std::shared_ptr GetFast3dGui() { + return std::dynamic_pointer_cast(Ship::Context::GetRawInstance()->GetWindow()->GetGui()); +} + +static void SaveStartingItemCVars() { + Ship::Context::GetRawInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); +} + +// Toggleable item icon (lit when the starting CVar is set, faded when not), mirroring +// DrawQuestItemButton in the save editor. The CVar name and tooltip come from the Option itself. +static void StartingItemToggle(RandomizerSettingKey rsk, uint32_t itemId, bool forceFaded = false) { + Rando::Option& option = Rando::Settings::GetInstance()->GetOption(rsk); + const char* cvar = option.GetCVarName().c_str(); + const ItemMapEntry& entry = itemMapping[itemId]; + bool on = !forceFaded && CVarGetInteger(cvar, 0) != 0; + + ImGui::PushID(static_cast(rsk)); + PushStyleButton(Colors::DarkGray); + if (ImGui::ImageButton(entry.name.c_str(), GetFast3dGui()->GetTextureByName(on ? entry.name : entry.nameFaded), + ImVec2(kIconSize, kIconSize), ImVec2(0, 0), ImVec2(1, 1))) { + CVarSetInteger(cvar, on ? 0 : 1); + SaveStartingItemCVars(); + } + PopStyleButton(); + Tooltip(option.GetName().c_str()); + ImGui::PopID(); +} + +// Tiered item icon with a popup picker (None + one icon per tier), mirroring DrawUpgradeIcon. +// `itemIds[i]` is the icon shown for CVar value `i + 1`; value 0 shows itemIds[0] faded. +static void StartingItemTiered(RandomizerSettingKey rsk, const std::vector& itemIds) { + Rando::Option& option = Rando::Settings::GetInstance()->GetOption(rsk); + const char* cvar = option.GetCVarName().c_str(); + int32_t value = CVarGetInteger(cvar, 0); + + ImGui::PushID(static_cast(rsk)); + const ItemMapEntry& displayEntry = itemMapping[itemIds[value > 0 ? value - 1 : 0]]; + bool lit = value > 0; + PushStyleButton(Colors::DarkGray); + if (ImGui::ImageButton(displayEntry.name.c_str(), + GetFast3dGui()->GetTextureByName(lit ? displayEntry.name : displayEntry.nameFaded), + ImVec2(kIconSize, kIconSize), ImVec2(0, 0), ImVec2(1, 1))) { + ImGui::OpenPopup("tieredPicker"); + } + PopStyleButton(); + Tooltip(option.GetName().c_str()); + + if (ImGui::BeginPopup("tieredPicker")) { + PushStyleButton(Colors::DarkGray); + if (ImGui::Button("##none", ImVec2(kIconSize, kIconSize) + ImGui::GetStyle().FramePadding * 2)) { + CVarSetInteger(cvar, 0); + SaveStartingItemCVars(); + ImGui::CloseCurrentPopup(); + } + PopStyleButton(); + Tooltip("None"); + for (size_t i = 0; i < itemIds.size(); i++) { + ImGui::SameLine(); + const ItemMapEntry& entry = itemMapping[itemIds[i]]; + PushStyleButton(Colors::DarkGray); + if (ImGui::ImageButton(entry.name.c_str(), GetFast3dGui()->GetTextureByName(entry.name), + ImVec2(kIconSize, kIconSize), ImVec2(0, 0), ImVec2(1, 1))) { + CVarSetInteger(cvar, static_cast(i) + 1); + SaveStartingItemCVars(); + ImGui::CloseCurrentPopup(); + } + PopStyleButton(); + Tooltip(option.GetOptionText(i + 1).c_str()); + } + ImGui::EndPopup(); + } + ImGui::PopID(); +} + +// Toggleable 32x48 song icon, mirroring the songMapping loop in DrawQuestStatusTab. +static void StartingSongToggle(RandomizerSettingKey rsk, QuestItem song) { + Rando::Option& option = Rando::Settings::GetInstance()->GetOption(rsk); + const char* cvar = option.GetCVarName().c_str(); + const SongMapEntry& entry = songMapping[song]; + bool on = CVarGetInteger(cvar, 0) != 0; + + ImGui::PushID(static_cast(rsk)); + PushStyleButton(Colors::DarkGray); + if (ImGui::ImageButton(entry.name.c_str(), GetFast3dGui()->GetTextureByName(on ? entry.name : entry.nameFaded), + kSongSize, ImVec2(0, 0), ImVec2(1, 1))) { + CVarSetInteger(cvar, on ? 0 : 1); + SaveStartingItemCVars(); + } + PopStyleButton(); + Tooltip(option.GetName().c_str()); + ImGui::PopID(); +} + +// Item icon followed by a count slider, like the ammo rows in DrawInventoryTab. The slider +// mirrors the Option's own semantics: the CVar stores the option index and the displayed +// number comes from the option text (matching how WIDGET_CVAR_SLIDER_INT renders these). +static void StartingItemCount(RandomizerSettingKey rsk, uint32_t itemId) { + Rando::Option& option = Rando::Settings::GetInstance()->GetOption(rsk); + const char* cvar = option.GetCVarName().c_str(); + const ItemMapEntry& entry = itemMapping[itemId]; + + ImGui::PushID(static_cast(rsk)); + ImGui::Image(GetFast3dGui()->GetTextureByName(entry.name), ImVec2(kIconSize, kIconSize)); + ImGui::SameLine(); + int32_t value = CVarGetInteger(cvar, option.GetMenuOptionDefault()); + CVarSliderInt(option.GetName().c_str(), cvar, + IntSliderOptions() + .Color(THEME_COLOR) + .Min(0) + .Max(static_cast(option.GetOptionCount()) - 1) + .DefaultValue(option.GetMenuOptionDefault()) + .Format(option.GetOptionText(value).c_str()) + .LabelPosition(LabelPositions::Near)); + ImGui::PopID(); +} + +// Plain combobox bound to an Option's CVar, with option text pulled from the Option. +static void StartingItemCombobox(RandomizerSettingKey rsk) { + Rando::Option& option = Rando::Settings::GetInstance()->GetOption(rsk); + std::vector entries; + for (size_t i = 0; i < option.GetOptionCount(); i++) { + entries.push_back(option.GetOptionText(i).c_str()); + } + CVarCombobox(option.GetName().c_str(), option.GetCVarName().c_str(), entries, + ComboboxOptions().Color(THEME_COLOR).LabelPosition(LabelPositions::Above)); +} + +void DrawStartingItemsMenu(WidgetInfo& info) { + bool generating = CVarGetInteger(CVAR_GENERAL("RandoGenerating"), 0); + bool disableEditingRandoSettings = generating || CVarGetInteger(CVAR_GENERAL("OnFileSelectNameEntry"), 0); + ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0) || disableEditingRandoSettings); + + ImGui::SeparatorText("Equipment"); + StartingItemToggle(RSK_STARTING_KOKIRI_SWORD, ITEM_SWORD_KOKIRI); + ImGui::SameLine(); + StartingItemToggle(RSK_STARTING_MASTER_SWORD, ITEM_SWORD_MASTER); + ImGui::SameLine(); + StartingItemTiered(RSK_STARTING_BIGGORON_SWORD, { ITEM_SWORD_KNIFE, ITEM_SWORD_BGS }); + ImGui::SameLine(); + StartingItemToggle(RSK_STARTING_DEKU_SHIELD, ITEM_SHIELD_DEKU); + ImGui::SameLine(); + StartingItemToggle(RSK_STARTING_HYLIAN_SHIELD, ITEM_SHIELD_HYLIAN); + ImGui::SameLine(); + StartingItemToggle(RSK_STARTING_MIRROR_SHIELD, ITEM_SHIELD_MIRROR); + + StartingItemToggle(RSK_STARTING_GORON_TUNIC, ITEM_TUNIC_GORON); + ImGui::SameLine(); + StartingItemToggle(RSK_STARTING_ZORA_TUNIC, ITEM_TUNIC_ZORA); + ImGui::SameLine(); + StartingItemToggle(RSK_STARTING_IRON_BOOTS, ITEM_BOOTS_IRON); + ImGui::SameLine(); + StartingItemToggle(RSK_STARTING_HOVER_BOOTS, ITEM_BOOTS_HOVER); + ImGui::SameLine(); + StartingItemToggle(RSK_STARTING_STONE_OF_AGONY, ITEM_STONE_OF_AGONY); + ImGui::SameLine(); + StartingItemToggle(RSK_STARTING_GERUDO_CARD, ITEM_GERUDO_CARD); + + // Starting Strength/Scale have no effect when Grab/Swim are shuffled; the generator + // force-disables them (settings.cpp), so gray them out to match. + bool grabShuffled = + CVarGetInteger(Rando::Settings::GetInstance()->GetOption(RSK_SHUFFLE_GRAB).GetCVarName().c_str(), 0) != 0; + ImGui::BeginDisabled(grabShuffled); + StartingItemTiered(RSK_STARTING_STRENGTH, { ITEM_BRACELET, ITEM_GAUNTLETS_SILVER, ITEM_GAUNTLETS_GOLD }); + ImGui::EndDisabled(); + if (grabShuffled && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) { + ImGui::SetTooltip("Disabled because Shuffle Grab is on."); + } + ImGui::SameLine(); + bool swimShuffled = + CVarGetInteger(Rando::Settings::GetInstance()->GetOption(RSK_SHUFFLE_SWIM).GetCVarName().c_str(), 0) != 0; + ImGui::BeginDisabled(swimShuffled); + StartingItemTiered(RSK_STARTING_SCALE, { ITEM_SCALE_SILVER, ITEM_SCALE_GOLDEN }); + ImGui::EndDisabled(); + if (swimShuffled && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) { + ImGui::SetTooltip("Disabled because Shuffle Swim is on."); + } + ImGui::SameLine(); + StartingItemTiered(RSK_STARTING_MAGIC_METER, { ITEM_MAGIC_SMALL, ITEM_MAGIC_LARGE }); + ImGui::SameLine(); + StartingItemTiered(RSK_STARTING_WALLET, { ITEM_WALLET_ADULT, ITEM_WALLET_GIANT }); + + ImGui::SeparatorText("Items"); + bool stickBagShuffled = + CVarGetInteger(Rando::Settings::GetInstance()->GetOption(RSK_SHUFFLE_DEKU_STICK_BAG).GetCVarName().c_str(), + 0) != 0; + ImGui::BeginDisabled(stickBagShuffled); + StartingItemToggle(RSK_STARTING_STICKS, ITEM_STICK, stickBagShuffled); + ImGui::EndDisabled(); + if (stickBagShuffled && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) { + ImGui::SetTooltip("Disabled because the Deku Stick Bag is being shuffled."); + } + ImGui::SameLine(); + bool nutBagShuffled = + CVarGetInteger(Rando::Settings::GetInstance()->GetOption(RSK_SHUFFLE_DEKU_NUT_BAG).GetCVarName().c_str(), 0) != + 0; + ImGui::BeginDisabled(nutBagShuffled); + StartingItemToggle(RSK_STARTING_NUTS, ITEM_NUT, nutBagShuffled); + ImGui::EndDisabled(); + if (nutBagShuffled && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) { + ImGui::SetTooltip("Disabled because the Deku Nut Bag is being shuffled."); + } + ImGui::SameLine(); + StartingItemTiered(RSK_STARTING_BOMB_BAG, { ITEM_BOMB_BAG_20, ITEM_BOMB_BAG_30, ITEM_BOMB_BAG_40 }); + ImGui::SameLine(); + StartingItemTiered(RSK_STARTING_BOW, { ITEM_QUIVER_30, ITEM_QUIVER_40, ITEM_QUIVER_50 }); + ImGui::SameLine(); + StartingItemToggle(RSK_STARTING_FIRE_ARROWS, ITEM_ARROW_FIRE); + ImGui::SameLine(); + StartingItemToggle(RSK_STARTING_DINS_FIRE, ITEM_DINS_FIRE); + + StartingItemTiered(RSK_STARTING_SLINGSHOT, { ITEM_BULLET_BAG_30, ITEM_BULLET_BAG_40, ITEM_BULLET_BAG_50 }); + ImGui::SameLine(); + StartingItemTiered(RSK_STARTING_OCARINA, { ITEM_OCARINA_FAIRY, ITEM_OCARINA_TIME }); + ImGui::SameLine(); + // The bombchu bag start option only makes sense when the bombchu bag setting enables bombchu bags; + // single-bag mode is a simple toggle, progressive mode is tiered, and None disables it entirely. + { + Rando::Option& bombchuOption = Rando::Settings::GetInstance()->GetOption(RSK_BOMBCHU_BAG); + int32_t bombchuMode = CVarGetInteger(bombchuOption.GetCVarName().c_str(), RO_BOMBCHU_BAG_NONE); + if (bombchuMode == RO_BOMBCHU_BAG_PROGRESSIVE) { + StartingItemTiered(RSK_STARTING_BOMBCHU_BAG, { ITEM_BOMBCHU, ITEM_BOMBCHU, ITEM_BOMBCHU }); + } else if (bombchuMode == RO_BOMBCHU_BAG_SINGLE) { + StartingItemToggle(RSK_STARTING_BOMBCHU_BAG, ITEM_BOMBCHU); + } else { + ImGui::BeginDisabled(); + StartingItemToggle(RSK_STARTING_BOMBCHU_BAG, ITEM_BOMBCHU, true); + ImGui::EndDisabled(); + if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) { + ImGui::SetTooltip("Enable the Bombchu Bag setting to start with bombchus."); + } + } + } + ImGui::SameLine(); + StartingItemTiered(RSK_STARTING_HOOKSHOT, { ITEM_HOOKSHOT, ITEM_LONGSHOT }); + ImGui::SameLine(); + StartingItemToggle(RSK_STARTING_ICE_ARROWS, ITEM_ARROW_ICE); + ImGui::SameLine(); + StartingItemToggle(RSK_STARTING_FARORES_WIND, ITEM_FARORES_WIND); + + StartingItemToggle(RSK_STARTING_BOOMERANG, ITEM_BOOMERANG); + ImGui::SameLine(); + StartingItemToggle(RSK_STARTING_LENS_OF_TRUTH, ITEM_LENS); + ImGui::SameLine(); + StartingItemToggle(RSK_STARTING_BEANS, ITEM_BEAN); + ImGui::SameLine(); + StartingItemToggle(RSK_STARTING_MEGATON_HAMMER, ITEM_HAMMER); + ImGui::SameLine(); + StartingItemToggle(RSK_STARTING_LIGHT_ARROWS, ITEM_ARROW_LIGHT); + ImGui::SameLine(); + StartingItemToggle(RSK_STARTING_NAYRUS_LOVE, ITEM_NAYRUS_LOVE); + + StartingItemTiered(RSK_STARTING_BOTTLE_1, { ITEM_BOTTLE, ITEM_BIG_POE, ITEM_LETTER_RUTO }); + ImGui::SameLine(); + StartingItemTiered(RSK_STARTING_BOTTLE_2, { ITEM_BOTTLE, ITEM_BIG_POE }); + ImGui::SameLine(); + StartingItemTiered(RSK_STARTING_BOTTLE_3, { ITEM_BOTTLE, ITEM_BIG_POE }); + ImGui::SameLine(); + StartingItemTiered(RSK_STARTING_BOTTLE_4, { ITEM_BOTTLE, ITEM_BIG_POE }); + ImGui::SameLine(); + StartingItemToggle(RSK_STARTING_CLAIM_CHECK, ITEM_CLAIM_CHECK); + ImGui::SameLine(); + bool weirdEggShuffled = + CVarGetInteger(Rando::Settings::GetInstance()->GetOption(RSK_SHUFFLE_WEIRD_EGG).GetCVarName().c_str(), 0) != 0; + ImGui::BeginDisabled(!weirdEggShuffled); + StartingItemToggle(RSK_STARTING_WEIRD_EGG, ITEM_WEIRD_EGG, !weirdEggShuffled); + ImGui::EndDisabled(); + if (!weirdEggShuffled && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) { + ImGui::SetTooltip("Enable Shuffle Weird Egg to start with the Weird Egg."); + } + ImGui::SameLine(); + StartingItemToggle(RSK_STARTING_BUNNY_HOOD, ITEM_MASK_BUNNY); + + ImGui::SeparatorText("Songs"); + StartingSongToggle(RSK_STARTING_ZELDAS_LULLABY, QUEST_SONG_LULLABY); + ImGui::SameLine(); + StartingSongToggle(RSK_STARTING_EPONAS_SONG, QUEST_SONG_EPONA); + ImGui::SameLine(); + StartingSongToggle(RSK_STARTING_SARIAS_SONG, QUEST_SONG_SARIA); + ImGui::SameLine(); + StartingSongToggle(RSK_STARTING_SUNS_SONG, QUEST_SONG_SUN); + ImGui::SameLine(); + StartingSongToggle(RSK_STARTING_SONG_OF_TIME, QUEST_SONG_TIME); + ImGui::SameLine(); + StartingSongToggle(RSK_STARTING_SONG_OF_STORMS, QUEST_SONG_STORMS); + + StartingSongToggle(RSK_STARTING_MINUET_OF_FOREST, QUEST_SONG_MINUET); + ImGui::SameLine(); + StartingSongToggle(RSK_STARTING_BOLERO_OF_FIRE, QUEST_SONG_BOLERO); + ImGui::SameLine(); + StartingSongToggle(RSK_STARTING_SERENADE_OF_WATER, QUEST_SONG_SERENADE); + ImGui::SameLine(); + StartingSongToggle(RSK_STARTING_REQUIEM_OF_SPIRIT, QUEST_SONG_REQUIEM); + ImGui::SameLine(); + StartingSongToggle(RSK_STARTING_NOCTURNE_OF_SHADOW, QUEST_SONG_NOCTURNE); + ImGui::SameLine(); + StartingSongToggle(RSK_STARTING_PRELUDE_OF_LIGHT, QUEST_SONG_PRELUDE); + + ImGui::SeparatorText("Other"); + StartingItemCombobox(RSK_LINKS_POCKET); + // The reward type only applies when Link's Pocket grants a dungeon reward. + if (CVarGetInteger(Rando::Settings::GetInstance()->GetOption(RSK_LINKS_POCKET).GetCVarName().c_str(), + RO_LINKS_POCKET_DUNGEON_REWARD) == RO_LINKS_POCKET_DUNGEON_REWARD) { + StartingItemCombobox(RSK_LINKS_POCKET_REWARD); + } + StartingItemCount(RSK_STARTING_SKULLTULA_TOKEN, ITEM_SKULL_TOKEN); + StartingItemCount(RSK_STARTING_HEARTS, ITEM_HEART_CONTAINER); + + ImGui::EndDisabled(); +} + +} // namespace SohGui