From 7b01a7bf2eb9b9d7dfb29285ffcab98569f675f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philip=20Dub=C3=A9?= <159546+serprex@users.noreply.github.com> Date: Fri, 12 Jun 2026 05:05:37 +0000 Subject: [PATCH] Early Granny's Potion Shop (#6693) ZFG thinks it makes more sense not requiring Claim Check, so add option --- .../Enhancements/randomizer/SeedContext.cpp | 30 ------------- soh/soh/Enhancements/randomizer/SeedContext.h | 1 - .../Enhancements/randomizer/hook_handlers.cpp | 4 +- .../location_access/overworld/kakariko.cpp | 6 ++- soh/soh/Enhancements/randomizer/logic.cpp | 44 ------------------- soh/soh/Enhancements/randomizer/logic.h | 1 - .../randomizer/option_descriptions.cpp | 28 +++++++----- .../randomizerEnums/RandomizerSettingKey.h | 1 + soh/soh/Enhancements/randomizer/settings.cpp | 9 ++++ 9 files changed, 32 insertions(+), 92 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/SeedContext.cpp b/soh/soh/Enhancements/randomizer/SeedContext.cpp index 71bbd84d2b..5a2eb77857 100644 --- a/soh/soh/Enhancements/randomizer/SeedContext.cpp +++ b/soh/soh/Enhancements/randomizer/SeedContext.cpp @@ -34,36 +34,6 @@ Context::Context() { mLogic = std::make_shared(); mTrials = std::make_shared(); mFishsanity = std::make_shared(); - VanillaLogicDefaults = { - // RANDOTODO check what this does - &mOptions[RSK_LINKS_POCKET], - &mOptions[RSK_SHUFFLE_DUNGEON_REWARDS], - &mOptions[RSK_SHUFFLE_SONGS], - &mOptions[RSK_SHOPSANITY], - &mOptions[RSK_SHOPSANITY_COUNT], - &mOptions[RSK_SHOPSANITY_PRICES], - &mOptions[RSK_SHOPSANITY_PRICES_AFFORDABLE], - &mOptions[RSK_FISHSANITY], - &mOptions[RSK_FISHSANITY_POND_COUNT], - &mOptions[RSK_FISHSANITY_AGE_SPLIT], - &mOptions[RSK_SHUFFLE_SCRUBS], - &mOptions[RSK_SHUFFLE_BEEHIVES], - &mOptions[RSK_SHUFFLE_COWS], - &mOptions[RSK_SHUFFLE_POTS], - &mOptions[RSK_SHUFFLE_CRATES], - &mOptions[RSK_SHUFFLE_ROCKS], - &mOptions[RSK_SHUFFLE_BOULDERS], - &mOptions[RSK_SHUFFLE_FREESTANDING], - &mOptions[RSK_SHUFFLE_MERCHANTS], - &mOptions[RSK_SHUFFLE_FROG_SONG_RUPEES], - &mOptions[RSK_SHUFFLE_ADULT_TRADE], - &mOptions[RSK_SHUFFLE_100_GS_REWARD], - &mOptions[RSK_SHUFFLE_FOUNTAIN_FAIRIES], - &mOptions[RSK_SHUFFLE_STONE_FAIRIES], - &mOptions[RSK_SHUFFLE_BEAN_FAIRIES], - &mOptions[RSK_SHUFFLE_SONG_FAIRIES], - &mOptions[RSK_GOSSIP_STONE_HINTS], - }; } RandomizerArea Context::GetAreaFromString(std::string str) { diff --git a/soh/soh/Enhancements/randomizer/SeedContext.h b/soh/soh/Enhancements/randomizer/SeedContext.h index 46b117616c..a1d5adb261 100644 --- a/soh/soh/Enhancements/randomizer/SeedContext.h +++ b/soh/soh/Enhancements/randomizer/SeedContext.h @@ -128,7 +128,6 @@ class Context { std::vector everyPossibleLocation = {}; std::set possibleIceTrapModels = {}; std::unordered_map iceTrapModels = {}; - std::vector VanillaLogicDefaults = {}; std::array hashIconIndexes = {}; bool playthroughBeatable = false; bool allLocationsReachable = false; diff --git a/soh/soh/Enhancements/randomizer/hook_handlers.cpp b/soh/soh/Enhancements/randomizer/hook_handlers.cpp index b5a18bde1a..2cae55af02 100644 --- a/soh/soh/Enhancements/randomizer/hook_handlers.cpp +++ b/soh/soh/Enhancements/randomizer/hook_handlers.cpp @@ -688,8 +688,8 @@ u8 EnDs_RandoCanGetGrannyItem() { !Flags_GetRandomizerInf(RAND_INF_MERCHANTS_GRANNYS_SHOP) && // Traded odd mushroom when adult trade is on ((RAND_GET_OPTION(RSK_SHUFFLE_ADULT_TRADE) && Flags_GetItemGetInf(ITEMGETINF_30)) || - // Found claim check when adult trade is off - (!RAND_GET_OPTION(RSK_SHUFFLE_ADULT_TRADE) && INV_CONTENT(ITEM_CLAIM_CHECK) == ITEM_CLAIM_CHECK)); + (!RAND_GET_OPTION(RSK_SHUFFLE_ADULT_TRADE) && + (RAND_GET_OPTION(RSK_EARLY_GRANNYS_SHOP) || INV_CONTENT(ITEM_CLAIM_CHECK) == ITEM_CLAIM_CHECK))); } u8 EnJs_RandoCanGetCarpetMerchantItem() { diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/kakariko.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/kakariko.cpp index e4aeb949ad..641dff6e25 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/kakariko.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/kakariko.cpp @@ -257,8 +257,10 @@ void RegionTable_Init_Kakariko() { }, { //Locations LOCATION(RC_KAK_TRADE_ODD_MUSHROOM, logic->IsAdult && logic->CanUse(RG_ODD_MUSHROOM)), - LOCATION(RC_KAK_GRANNYS_SHOP, logic->IsAdult && logic->HasItem(RG_SPEAK_HYLIAN) && - (logic->CanUse(RG_ODD_MUSHROOM) || logic->TradeQuestStep(RG_ODD_MUSHROOM)) && GetCheckPrice() <= GetWalletCapacity()), + LOCATION(RC_KAK_GRANNYS_SHOP, logic->IsAdult && logic->HasItem(RG_SPEAK_HYLIAN) && + (logic->CanUse(RG_ODD_MUSHROOM) || (ctx->GetOption(RSK_SHUFFLE_ADULT_TRADE).Is(RO_GENERIC_OFF) && + (logic->HasItem(RG_CLAIM_CHECK) || ctx->GetOption(RSK_EARLY_GRANNYS_SHOP))) && + GetCheckPrice() <= GetWalletCapacity())), }, { // Exits ENTRANCE(RR_KAK_BACKYARD, true), diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index 9090703f9d..a8ccec4fe9 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -1498,50 +1498,6 @@ bool Logic::SunlightArrows() { return ctx->GetOption(RSK_SUNLIGHT_ARROWS) && CanUse(RG_LIGHT_ARROWS); } -// Is this best off signaling what you have already traded, or what step you are currently on? -bool Logic::TradeQuestStep(RandomizerGet rg) { - if (ctx->GetOption(RSK_SHUFFLE_ADULT_TRADE)) { - return false; // This does not apply when we are shuffling trade items - } - bool hasState = false; - // Falling through each case to test each possibility - switch (rg) { - case RG_POCKET_EGG: - hasState = hasState || HasItem(RG_POCKET_EGG); - [[fallthrough]]; - case RG_COJIRO: - hasState = hasState || HasItem(RG_COJIRO); - [[fallthrough]]; - case RG_ODD_MUSHROOM: - hasState = hasState || HasItem(RG_ODD_MUSHROOM); - [[fallthrough]]; - case RG_ODD_POTION: - hasState = hasState || HasItem(RG_ODD_POTION); - [[fallthrough]]; - case RG_POACHERS_SAW: - hasState = hasState || HasItem(RG_POACHERS_SAW); - [[fallthrough]]; - case RG_BROKEN_SWORD: - hasState = hasState || HasItem(RG_BROKEN_SWORD); - [[fallthrough]]; - case RG_PRESCRIPTION: - hasState = hasState || HasItem(RG_PRESCRIPTION); - [[fallthrough]]; - case RG_EYEDROPS: - hasState = hasState || HasItem(RG_EYEDROPS); - [[fallthrough]]; - case RG_CLAIM_CHECK: - hasState = hasState || HasItem(RG_CLAIM_CHECK); - break; - default: - SPDLOG_ERROR("TradeQuestStep reached `return false;`. Missing case for RandomizerGet of {}", - static_cast(rg)); - assert(false); - return false; - } - return hasState; -} - bool Logic::CanStandingShield() { return CanUse(RG_MIRROR_SHIELD) || (IsAdult && HasItem(RG_HYLIAN_SHIELD)) || CanUse(RG_DEKU_SHIELD); } diff --git a/soh/soh/Enhancements/randomizer/logic.h b/soh/soh/Enhancements/randomizer/logic.h index 04f6b5232d..91d019e76b 100644 --- a/soh/soh/Enhancements/randomizer/logic.h +++ b/soh/soh/Enhancements/randomizer/logic.h @@ -113,7 +113,6 @@ class Logic { bool HasFireSource(); bool HasFireSourceWithTorch(); bool SunlightArrows(); - bool TradeQuestStep(RandomizerGet rg); bool CanStandingShield(); bool CanShield(); bool CanUseProjectile(); diff --git a/soh/soh/Enhancements/randomizer/option_descriptions.cpp b/soh/soh/Enhancements/randomizer/option_descriptions.cpp index 8efc31f49e..cc9a780086 100644 --- a/soh/soh/Enhancements/randomizer/option_descriptions.cpp +++ b/soh/soh/Enhancements/randomizer/option_descriptions.cpp @@ -50,18 +50,17 @@ void Settings::CreateOptionDescriptions() { "Choose which age Link will start as.\n\n" "Starting as adult means you start with the Master Sword in your inventory.\n" "The child option is forcefully set if it would conflict with other options."; - mOptionDescriptions[RSK_GERUDO_FORTRESS] = - "Sets the state of the carpenters captured by Gerudo " - "in Gerudo Fortress, and with it the number of guards that spawn.\n" - "\n" - "Normal - All 4 carpenters are required to be saved.\n" - "\n" - "Fast - Only the bottom left carpenter requires rescuing.\n" - "\n" - "Free - The bridge is repaired from the start, and Nabooru cannot spawn.\n" - "If the Gerudo Membership Card isn't shuffled, you start with it.\n" - "\n" - "Only \"Normal\" is compatible with Gerudo Fortress Key Rings."; + mOptionDescriptions[RSK_GERUDO_FORTRESS] = "Sets the state of the carpenters captured by Gerudo " + "in Gerudo Fortress, and with it the number of guards that spawn.\n" + "\n" + "Normal - All 4 carpenters are required to be saved.\n" + "\n" + "Fast - Only the bottom left carpenter requires rescuing.\n" + "\n" + "Free - Bridge is repaired from start, and Nabooru cannot spawn.\n" + "If the Gerudo Membership Card isn't shuffled, you start with it.\n" + "\n" + "Only \"Normal\" is compatible with Gerudo Fortress Key Rings."; mOptionDescriptions[RSK_RAINBOW_BRIDGE] = "Alters the requirements to open the bridge to Ganon's Castle.\n" "\n" @@ -521,6 +520,11 @@ void Settings::CreateOptionDescriptions() { "D-pad.\n" "\n" "If disabled, only the Claim Check will be found in the pool."; + mOptionDescriptions[RSK_EARLY_GRANNYS_SHOP] = + "Makes Granny's Potion Shop available from start, rather than requiring Claim Check to be found first.\n" + "\n" + "This only applies when Shuffle Adult Trade is disabled. With Shuffle Adult " + "Trade enabled, Granny still requires trading the Odd Mushroom as usual."; mOptionDescriptions[RSK_SHUFFLE_100_GS_REWARD] = "Shuffle the item the cursed rich man in the House of Skulltula gives when you " "have collected all 100 Gold Skulltula Tokens.\n" diff --git a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerSettingKey.h b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerSettingKey.h index 3095055b55..915fb6e230 100644 --- a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerSettingKey.h +++ b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerSettingKey.h @@ -151,6 +151,7 @@ RANDO_ENUM_ITEM(RSK_MASK_QUEST) RANDO_ENUM_ITEM(RSK_SKIP_SCARECROWS_SONG) RANDO_ENUM_ITEM(RSK_SKIP_PLANTING_BEANS) RANDO_ENUM_ITEM(RSK_SKULLS_SUNS_SONG) +RANDO_ENUM_ITEM(RSK_EARLY_GRANNYS_SHOP) RANDO_ENUM_ITEM(RSK_SHUFFLE_ADULT_TRADE) RANDO_ENUM_ITEM(RSK_SHUFFLE_MERCHANTS) RANDO_ENUM_ITEM(RSK_MERCHANT_PRICES) diff --git a/soh/soh/Enhancements/randomizer/settings.cpp b/soh/soh/Enhancements/randomizer/settings.cpp index 29b028002d..e0b8c2f394 100644 --- a/soh/soh/Enhancements/randomizer/settings.cpp +++ b/soh/soh/Enhancements/randomizer/settings.cpp @@ -1027,6 +1027,13 @@ void Settings::CreateOptions() { OPT_BOOL(RSK_SHUFFLE_BEGGAR, "Shuffle Beggar", CVAR_RANDOMIZER_SETTING("ShuffleBeggar"), mOptionDescriptions[RSK_SHUFFLE_BEGGAR]); OPT_BOOL(RSK_SHUFFLE_FROG_SONG_RUPEES, "Shuffle Frog Song Rupees", CVAR_RANDOMIZER_SETTING("ShuffleFrogSongRupees"), mOptionDescriptions[RSK_SHUFFLE_FROG_SONG_RUPEES]); OPT_BOOL(RSK_SHUFFLE_ADULT_TRADE, "Shuffle Adult Trade", CVAR_RANDOMIZER_SETTING("ShuffleAdultTrade"), mOptionDescriptions[RSK_SHUFFLE_ADULT_TRADE]); + OPT_CALLBACK(RSK_SHUFFLE_ADULT_TRADE, { + if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleAdultTrade"), RO_GENERIC_OFF)) { + mOptions[RSK_EARLY_GRANNYS_SHOP].Disable("This has no effect when Shuffle Adult Trade is on."); + } else { + mOptions[RSK_EARLY_GRANNYS_SHOP].Enable(); + } + }); OPT_U8(RSK_SHUFFLE_CHEST_MINIGAME, "Shuffle Chest Minigame", {"Off", "On (Separate)", "On (Pack)"}); OPT_BOOL(RSK_SHUFFLE_100_GS_REWARD, "Shuffle 100 GS Reward", CVAR_RANDOMIZER_SETTING("Shuffle100GSReward"), mOptionDescriptions[RSK_SHUFFLE_100_GS_REWARD], IMFLAG_SEPARATOR_BOTTOM, WIDGET_CVAR_CHECKBOX, RO_GENERIC_OFF); OPT_CALLBACK(RSK_SHUFFLE_100_GS_REWARD, { @@ -1250,6 +1257,7 @@ void Settings::CreateOptions() { mOptions[RSK_SKIP_CHILD_STEALTH].Enable(); } }); + OPT_BOOL(RSK_EARLY_GRANNYS_SHOP, "Early Granny's Potion Shop", CVAR_RANDOMIZER_SETTING("EarlyGrannysShop"), mOptionDescriptions[RSK_EARLY_GRANNYS_SHOP]); OPT_BOOL(RSK_SKIP_EPONA_RACE, "Skip Epona Race", {"Don't Skip", "Skip"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("SkipEponaRace"), mOptionDescriptions[RSK_SKIP_EPONA_RACE], WIDGET_CVAR_CHECKBOX, RO_GENERIC_DONT_SKIP); OPT_BOOL(RSK_SKIP_SCARECROWS_SONG, "Skip Scarecrow's Song", CVAR_RANDOMIZER_SETTING("SkipScarecrowsSong"), mOptionDescriptions[RSK_SKIP_SCARECROWS_SONG]); OPT_BOOL(RSK_SKIP_PLANTING_BEANS, "Skip Planting Beans", CVAR_RANDOMIZER_SETTING("SkipPlantingBeans"), mOptionDescriptions[RSK_SKIP_PLANTING_BEANS]); @@ -1747,6 +1755,7 @@ void Settings::CreateOptions() { &mOptions[RSK_SKIP_CHILD_ZELDA], &mOptions[RSK_MASK_QUEST], &mOptions[RSK_SKIP_CHILD_STEALTH], + &mOptions[RSK_EARLY_GRANNYS_SHOP], &mOptions[RSK_SKIP_PLANTING_BEANS], &mOptions[RSK_SKIP_EPONA_RACE], &mOptions[RSK_SKIP_SCARECROWS_SONG],