Complete rando starting items, make icon based like save editor (#6723)

This commit is contained in:
Philip Dubé
2026-06-20 16:24:44 +00:00
committed by GitHub
parent c642e8c4d3
commit 728a4f1c80
14 changed files with 847 additions and 192 deletions
@@ -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<Fast::Fast3dGui>(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<Fast::Fast3dGui>(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());
@@ -757,7 +757,10 @@ void CreateStaticItemHint(RandomizerHint hintKey, std::vector<RandomizerHintText
// RANDOTODO choose area in case there are multiple
auto ctx = Rando::Context::GetInstance();
std::vector<RandomizerCheck> locations = FindItemsAndMarkHinted(items, hintChecks);
std::vector<RandomizerArea> areas = {};
locations.erase(
std::remove_if(locations.begin(), locations.end(), [](const auto rc) { return rc == RC_UNKNOWN_CHECK; }));
std::vector<RandomizerArea> areas;
areas.reserve(locations.size());
for (auto loc : locations) {
areas.push_back(ctx->GetItemLocation(loc)->GetRandomArea());
}
@@ -9,6 +9,7 @@
#include "spoiler_log.hpp"
#include "soh/Enhancements/randomizer/Traps.h"
#include "z64item.h"
#include <algorithm>
#include <spdlog/spdlog.h>
std::vector<RandomizerGet> 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);
@@ -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<uint8_t>());
// AddItemToInventory(RG_PROGRESSIVE_NUT_UPGRADE, StartingNutCapacity.Value<uint8_t>());
// AddItemToInventory(RG_PROGRESSIVE_BOMB_BAG, StartingBombBag.Value<uint8_t>());
// AddItemToInventory((BombchuBag ? RG_PROGRESSIVE_BOMBCHU_BAG : RG_BOMBCHU_20),
// StartingBombchus.Value<uint8_t>()); AddItemToInventory(RG_PROGRESSIVE_BOW, StartingBow.Value<uint8_t>());
// AddItemToInventory(RG_FIRE_ARROWS, StartingFireArrows.Value<uint8_t>());
// AddItemToInventory(RG_ICE_ARROWS, StartingIceArrows.Value<uint8_t>());
// AddItemToInventory(RG_LIGHT_ARROWS, StartingLightArrows.Value<uint8_t>());
// AddItemToInventory(RG_DINS_FIRE, StartingDinsFire.Value<uint8_t>());
// AddItemToInventory(RG_FARORES_WIND, StartingFaroresWind.Value<uint8_t>());
// AddItemToInventory(RG_NAYRUS_LOVE, StartingNayrusLove.Value<uint8_t>());
// AddItemToInventory(RG_PROGRESSIVE_SLINGSHOT, StartingSlingshot.Value<uint8_t>());
// AddItemToInventory(RG_BOOMERANG, StartingBoomerang.Value<uint8_t>());
// AddItemToInventory(RG_LENS_OF_TRUTH, StartingLensOfTruth.Value<uint8_t>());
// AddItemToInventory(RG_MAGIC_BEAN_PACK, StartingMagicBean.Value<uint8_t>());
// AddItemToInventory(RG_MEGATON_HAMMER, StartingMegatonHammer.Value<uint8_t>());
// AddItemToInventory(RG_PROGRESSIVE_HOOKSHOT, StartingHookshot.Value<uint8_t>());
// AddItemToInventory(RG_IRON_BOOTS, StartingIronBoots.Value<uint8_t>());
// AddItemToInventory(RG_HOVER_BOOTS, StartingHoverBoots.Value<uint8_t>());
// 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<uint8_t>() == STARTINGBOTTLE_BIG_POE) {
// AddItemToInventory(RG_BOTTLE_WITH_BIG_POE, 1);
// } else if (StartingBottle1.Value<uint8_t>()) {
// AddItemToInventory(RG_EMPTY_BOTTLE, 1);
// }
// if (StartingBottle2.Value<uint8_t>() == STARTINGBOTTLE_BIG_POE) {
// AddItemToInventory(RG_BOTTLE_WITH_BIG_POE, 1);
// } else if (StartingBottle2.Value<uint8_t>()) {
// AddItemToInventory(RG_EMPTY_BOTTLE, 1);
// }
// if (StartingBottle3.Value<uint8_t>() == STARTINGBOTTLE_BIG_POE) {
// AddItemToInventory(RG_BOTTLE_WITH_BIG_POE, 1);
// } else if (StartingBottle3.Value<uint8_t>()) {
// AddItemToInventory(RG_EMPTY_BOTTLE, 1);
// }
// if (StartingBottle4.Value<uint8_t>() == STARTINGBOTTLE_BIG_POE) {
// AddItemToInventory(RG_BOTTLE_WITH_BIG_POE, 1);
// } else if (StartingBottle4.Value<uint8_t>()) {
// AddItemToInventory(RG_EMPTY_BOTTLE, 1);
// }
// AddItemToInventory(RG_RUTOS_LETTER, StartingRutoBottle.Value<uint8_t>());
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<uint8_t>());
// } 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<uint8_t>());
// AddItemToInventory(RG_MIRROR_SHIELD, StartingMirrorShield.Value<uint8_t>());
// AddItemToInventory(RG_GORON_TUNIC, StartingGoronTunic.Value<uint8_t>());
// AddItemToInventory(RG_ZORA_TUNIC, StartingZoraTunic.Value<uint8_t>());
// AddItemToInventory(RG_PROGRESSIVE_MAGIC_METER, StartingMagicMeter.Value<uint8_t>());
// AddItemToInventory(RG_PROGRESSIVE_STRENGTH, StartingStrength.Value<uint8_t>());
// AddItemToInventory(RG_PROGRESSIVE_SCALE, StartingScale.Value<uint8_t>());
// AddItemToInventory(RG_PROGRESSIVE_WALLET, StartingWallet.Value<uint8_t>());
// AddItemToInventory(RG_STONE_OF_AGONY, StartingShardOfAgony.Value<uint8_t>());
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<uint8_t>());
// AddItemToInventory(RG_KOKIRI_EMERALD, StartingKokiriEmerald.Value<uint8_t>());
// AddItemToInventory(RG_GORON_RUBY, StartingGoronRuby.Value<uint8_t>());
@@ -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];
}
+7
View File
@@ -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.
*
@@ -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)
@@ -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)
@@ -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)
@@ -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);
}
+114 -59
View File
@@ -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<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],
@@ -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<RandomizerCheck>& 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;
+1
View File
@@ -45,6 +45,7 @@ void ShowRandomizerSettingsMenu();
void ShowEscMenu();
UIWidgets::Colors GetMenuThemeColor();
std::shared_ptr<SohMenu> GetSohMenu();
void DrawStartingItemsMenu(WidgetInfo& info);
} // namespace SohGui
#define THEME_COLOR SohGui::GetMenuThemeColor()
+3 -1
View File
@@ -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);
+336
View File
@@ -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 <vector>
#include <fast/Fast3dGui.h>
#include <libultraship/libultraship.h>
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<Fast::Fast3dGui> GetFast3dGui() {
return std::dynamic_pointer_cast<Fast::Fast3dGui>(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<int32_t>(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<uint32_t>& 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<int32_t>(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<int32_t>(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<int32_t>(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<int32_t>(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<int32_t>(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<const char*> 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