diff --git a/soh/soh/Enhancements/presets.h b/soh/soh/Enhancements/presets.h index 65c30f737..dd5369d18 100644 --- a/soh/soh/Enhancements/presets.h +++ b/soh/soh/Enhancements/presets.h @@ -236,6 +236,8 @@ const std::vector randomizerCvars = { "gRandomizeRewardCount", "gRandomizeScrubText", "gRandomizeShopsanity", + "gRandomizeShopsanityPrices", + "gRandomizeShopsanityPricesAffordable", "gRandomizeShuffleAdultTrade", "gRandomizeShuffleBeans", "gRandomizeShuffleBossEntrances", @@ -690,6 +692,7 @@ const std::vector hellModePresetEntries = { PRESET_ENTRY_S32("gRandomizeMqDungeons", RO_MQ_DUNGEONS_RANDOM_NUMBER), PRESET_ENTRY_S32("gRandomizeRainbowBridge", RO_BRIDGE_DUNGEON_REWARDS), PRESET_ENTRY_S32("gRandomizeShopsanity", RO_SHOPSANITY_FOUR_ITEMS), + PRESET_ENTRY_S32("gRandomizeShopsanityPrices", RO_SHOPSANITY_PRICE_TYCOON), PRESET_ENTRY_S32("gRandomizeShuffleAdultTrade", 1), PRESET_ENTRY_S32("gRandomizeShuffleBeans", 1), PRESET_ENTRY_S32("gRandomizeShuffleCows", 1), diff --git a/soh/soh/Enhancements/randomizer/3drando/setting_descriptions.cpp b/soh/soh/Enhancements/randomizer/3drando/setting_descriptions.cpp index 200739991..ed857e82c 100644 --- a/soh/soh/Enhancements/randomizer/3drando/setting_descriptions.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/setting_descriptions.cpp @@ -358,6 +358,18 @@ string_view shopsFour = "Vanilla shop items will be shuffled amo string_view shopsRandom = "Vanilla shop items will be shuffled among\n" // "different shops, and each shop will contain\n" // "1-4 non-vanilla shop items."; // + // +/*------------------------------ // +| SHOPSANITY PRICES | // +------------------------------*/ // +string_view shopPriceBalanced = "Weighted randomization, max 300."; // +string_view shopPriceStarter = "True randomization, max 95"; // +string_view shopPriceAdult = "True randomization, max 200"; // +string_view shopPriceGiant = "True randomization, max 500"; // +string_view shopPriceTycoon = "True randomization, max 995"; // +string_view shopPriceAffordable = "Cap shop prices to affordable value just above" // + "the previous tier wallet's max value"; // + // /*------------------------------ // | TOKENSANITY | // ------------------------------*/ // diff --git a/soh/soh/Enhancements/randomizer/3drando/setting_descriptions.hpp b/soh/soh/Enhancements/randomizer/3drando/setting_descriptions.hpp index 2749fa985..fae3ee995 100644 --- a/soh/soh/Enhancements/randomizer/3drando/setting_descriptions.hpp +++ b/soh/soh/Enhancements/randomizer/3drando/setting_descriptions.hpp @@ -118,6 +118,13 @@ extern string_view shopsThree; extern string_view shopsFour; extern string_view shopsRandom; +extern string_view shopPriceBalanced; +extern string_view shopPriceStarter; +extern string_view shopPriceAdult; +extern string_view shopPriceGiant; +extern string_view shopPriceTycoon; +extern string_view shopPriceAffordable; + extern string_view tokensOff; extern string_view tokensDungeon; extern string_view tokensOverworld; diff --git a/soh/soh/Enhancements/randomizer/3drando/settings.cpp b/soh/soh/Enhancements/randomizer/3drando/settings.cpp index 632ed45c8..07e17f7e9 100644 --- a/soh/soh/Enhancements/randomizer/3drando/settings.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/settings.cpp @@ -179,8 +179,10 @@ namespace Settings { Option LinksPocketItem = Option::U8 ("Link's Pocket", {"Dungeon Reward", "Advancement", "Anything", "Nothing"}, {linksPocketDungeonReward, linksPocketAdvancement, linksPocketAnything, linksPocketNothing}); Option ShuffleSongs = Option::U8 ("Shuffle Songs", {"Song locations", "Dungeon rewards", "Anywhere"}, {songsSongLocations, songsDungeonRewards, songsAllLocations}); Option Shopsanity = Option::U8 ("Shopsanity", {"Off","0 Items","1 Item","2 Items","3 Items","4 Items","Random"}, {shopsOff, shopsZero, shopsOne, shopsTwo, shopsThree, shopsFour, shopsRandom}); - Option Tokensanity = Option::U8 ("Tokensanity", {"Off", "Dungeons", "Overworld", "All Tokens"}, {tokensOff, tokensDungeon, tokensOverworld, tokensAllTokens}); - Option Scrubsanity = Option::U8 ("Scrub Shuffle", {"Off", "Affordable", "Expensive", "Random Prices"}, {scrubsOff, scrubsAffordable, scrubsExpensive, scrubsRandomPrices}); + Option ShopsanityPrices = Option::U8 ("Shopsanity Prices", {"Balanced", "Starting Wallet", "Adult Wallet", "Giant's Wallet", "Tycoon's Wallet" }, {shopPriceBalanced, shopPriceStarter, shopPriceAdult, shopPriceGiant, shopPriceTycoon} ); + Option ShopsanityPricesAffordable = Option::Bool("Affordable Prices", {"Off", "On"}, {shopPriceAffordable} ); + Option Tokensanity = Option::U8 ("Tokensanity", {"Off", "Dungeons", "Overworld", "All Tokens"}, {tokensOff, tokensDungeon, tokensOverworld, tokensAllTokens}); + Option Scrubsanity = Option::U8 ("Scrub Shuffle", {"Off", "Affordable", "Expensive", "Random Prices"}, {scrubsOff, scrubsAffordable, scrubsExpensive, scrubsRandomPrices}); Option ShuffleCows = Option::Bool("Shuffle Cows", {"Off", "On"}, {shuffleCowsDesc}); Option ShuffleKokiriSword = Option::Bool("Shuffle Kokiri Sword", {"Off", "On"}, {kokiriSwordDesc}); Option ShuffleOcarinas = Option::Bool("Shuffle Ocarinas", {"Off", "On"}, {ocarinasDesc}); @@ -198,6 +200,8 @@ namespace Settings { &LinksPocketItem, &ShuffleSongs, &Shopsanity, + &ShopsanityPrices, + &ShopsanityPricesAffordable, &Tokensanity, &Scrubsanity, &ShuffleCows, @@ -2427,6 +2431,8 @@ namespace Settings { &ShuffleRewards, &ShuffleSongs, &Shopsanity, + &ShopsanityPrices, + &ShopsanityPricesAffordable, &Scrubsanity, &ShuffleCows, &ShuffleMagicBeans, @@ -2703,6 +2709,8 @@ namespace Settings { ShuffleSongs.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_SONGS]); Tokensanity.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_TOKENS]); Shopsanity.SetSelectedIndex(cvarSettings[RSK_SHOPSANITY]); + ShopsanityPrices.SetSelectedIndex(cvarSettings[RSK_SHOPSANITY_PRICES]); + ShopsanityPricesAffordable.SetSelectedIndex(cvarSettings[RSK_SHOPSANITY_PRICES_AFFORDABLE]); Scrubsanity.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_SCRUBS]); ShuffleCows.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_COWS]); ShuffleKokiriSword.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_KOKIRI_SWORD]); diff --git a/soh/soh/Enhancements/randomizer/3drando/settings.hpp b/soh/soh/Enhancements/randomizer/3drando/settings.hpp index 84851a3d6..a4f788c81 100644 --- a/soh/soh/Enhancements/randomizer/3drando/settings.hpp +++ b/soh/soh/Enhancements/randomizer/3drando/settings.hpp @@ -421,6 +421,7 @@ typedef struct { uint8_t tokensanity; uint8_t scrubsanity; uint8_t shopsanity; + uint8_t shopsanityPrices; uint8_t shuffleCows; uint8_t shuffleKokiriSword; uint8_t shuffleOcarinas; @@ -937,6 +938,8 @@ void UpdateSettings(std::unordered_map cvarSettin extern Option LinksPocketItem; extern Option ShuffleSongs; extern Option Shopsanity; + extern Option ShopsanityPrices; + extern Option ShopsanityPricesAffordable; extern Option Tokensanity; extern Option Scrubsanity; extern Option ShuffleCows; diff --git a/soh/soh/Enhancements/randomizer/3drando/shops.cpp b/soh/soh/Enhancements/randomizer/3drando/shops.cpp index 72aab2eb6..1b4dbb28d 100644 --- a/soh/soh/Enhancements/randomizer/3drando/shops.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/shops.cpp @@ -127,15 +127,56 @@ static constexpr std::array ShopPriceProbability= { 0.833208595, 0.849243398, 0.864579161, 0.879211177, 0.893112051, 0.906263928, 0.918639420, 0.930222611, 0.940985829, 0.950914731, 0.959992180, 0.968187000, 0.975495390, 0.981884488, 0.987344345, 0.991851853, 0.995389113, 0.997937921, 0.999481947, 1.000000000, }; + +std::map affordableCaps = { + {RO_SHOPSANITY_PRICE_STARTER, 10}, + {RO_SHOPSANITY_PRICE_ADULT, 105}, + {RO_SHOPSANITY_PRICE_GIANT, 205}, + {RO_SHOPSANITY_PRICE_TYCOON, 505}, +}; + +// If affordable option is on, cap items at affordable price just above the max of the previous wallet tier +int CapPriceAffordable(int value, int cap) { + if (Settings::ShopsanityPricesAffordable.Is(true) && value > cap) + return cap; + return value; +} + +// Generate random number from 5 to wallet max +int GetPriceFromMax(int max) { + int temp = Random(1, max) * 5; // random range of 1 - wallet max / 5, where wallet max is the highest it goes as a multiple of 5 + return CapPriceAffordable(temp, affordableCaps.find(Settings::ShopsanityPrices.Value())->second); +} + int GetRandomShopPrice() { - double random = RandomDouble(); //Randomly generated probability value - for (size_t i = 0; i < ShopPriceProbability.size(); i++) { - if (random < ShopPriceProbability[i]) { - //The randomly generated value has surpassed the total probability up to this point, so this is the generated price - return i * 5; //i in range [0, 59], output in range [0, 295] in increments of 5 + int max = 0; + + if(Settings::ShopsanityPrices.Is(RO_SHOPSANITY_PRICE_STARTER)) {// check for xx wallet setting and set max amount as method for + max = 19; // 95/5 // setting true randomization } - } - return -1; //Shouldn't happen + else if (Settings::ShopsanityPrices.Is(RO_SHOPSANITY_PRICE_ADULT)) { + max = 40; // 200/5 + } + else if (Settings::ShopsanityPrices.Is(RO_SHOPSANITY_PRICE_GIANT)) { + max = 100; // 500/5 + } + else if (Settings::ShopsanityPrices.Is(RO_SHOPSANITY_PRICE_TYCOON)) { + max = 199; // 995/5 + } + if (max != 0) { + return GetPriceFromMax(max); + } + // Balanced is default, so if all other known cases fail, fall back to Balanced + int price = 150; // JUST in case something fails with the randomization, return sane price for balanced + double random = RandomDouble(); //Randomly generated probability value + for (size_t i = 0; i < ShopPriceProbability.size(); i++) { + if (random < ShopPriceProbability[i]) { + //The randomly generated value has surpassed the total probability up to this point, so this is the generated price + price = i * 5; //i in range [0, 59], output in range [0, 295] in increments of 5 + break; + } + } + return price; } //Similar to above, beta distribution with alpha = 1, beta = 2, diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index 080c5d1df..4a5be61f5 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -221,6 +221,7 @@ std::unordered_map SpoilerfileSettingNameToEn { "Shuffle Settings:Link's Pocket", RSK_LINKS_POCKET}, { "Shuffle Settings:Shuffle Gerudo Card", RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD }, { "Shuffle Settings:Shopsanity", RSK_SHOPSANITY }, + { "Shuffle Settings:Shopsanity Prices", RSK_SHOPSANITY_PRICES }, { "Shuffle Settings:Scrub Shuffle", RSK_SHUFFLE_SCRUBS }, { "Shuffle Settings:Shuffle Cows", RSK_SHUFFLE_COWS }, { "Shuffle Settings:Tokensanity", RSK_SHUFFLE_TOKENS }, @@ -727,6 +728,18 @@ void Randomizer::ParseRandomizerSettingsFile(const char* spoilerFileName) { gSaveContext.randoSettings[index].value = RO_SHOPSANITY_RANDOM; } break; + case RSK_SHOPSANITY_PRICES: + if (it.value() == "Random") { + gSaveContext.randoSettings[index].value = RO_SHOPSANITY_PRICE_BALANCED; + } else if (it.value() == "Starter Wallet") { + gSaveContext.randoSettings[index].value = RO_SHOPSANITY_PRICE_STARTER; + } else if (it.value() == "Adult's Wallet") { + gSaveContext.randoSettings[index].value = RO_SHOPSANITY_PRICE_ADULT; + } else if (it.value() == "Giant's Wallet") { + gSaveContext.randoSettings[index].value = RO_SHOPSANITY_PRICE_GIANT; + } else if (it.value() == "Tycoon's Wallet") { + gSaveContext.randoSettings[index].value = RO_SHOPSANITY_PRICE_TYCOON; + } case RSK_SHUFFLE_SCRUBS: if(it.value() == "Off") { gSaveContext.randoSettings[index].value = RO_SCRUBS_OFF; @@ -789,6 +802,7 @@ void Randomizer::ParseRandomizerSettingsFile(const char* spoilerFileName) { case RSK_MIX_INTERIOR_ENTRANCES: case RSK_MIX_GROTTO_ENTRANCES: case RSK_DECOUPLED_ENTRANCES: + case RSK_SHOPSANITY_PRICES_AFFORDABLE: if(it.value() == "Off") { gSaveContext.randoSettings[index].value = RO_GENERIC_OFF; } else if(it.value() == "On") { @@ -2824,6 +2838,8 @@ void GenerateRandomizerImgui(std::string seed = "") { cvarSettings[RSK_SHUFFLE_SONGS] = CVarGetInteger("gRandomizeShuffleSongs", RO_SONG_SHUFFLE_SONG_LOCATIONS); cvarSettings[RSK_SHUFFLE_TOKENS] = CVarGetInteger("gRandomizeShuffleTokens", RO_TOKENSANITY_OFF); cvarSettings[RSK_SHOPSANITY] = CVarGetInteger("gRandomizeShopsanity", RO_SHOPSANITY_OFF); + cvarSettings[RSK_SHOPSANITY_PRICES] = CVarGetInteger("gRandomizeShopsanityPrices", RO_SHOPSANITY_PRICE_BALANCED); + cvarSettings[RSK_SHOPSANITY_PRICES_AFFORDABLE] = CVarGetInteger("gRandomizeShopsanityPricesAffordable", RO_SHOPSANITY_OFF); cvarSettings[RSK_SHUFFLE_SCRUBS] = CVarGetInteger("gRandomizeShuffleScrubs", RO_SCRUBS_OFF); cvarSettings[RSK_SHUFFLE_COWS] = CVarGetInteger("gRandomizeShuffleCows", 0); cvarSettings[RSK_SHUFFLE_ADULT_TRADE] = CVarGetInteger("gRandomizeShuffleAdultTrade", 0); @@ -3018,6 +3034,7 @@ void DrawRandoEditor(bool& open) { static const char* randoLinksPocket[4] = { "Dungeon Reward", "Advancement", "Anything", "Nothing" }; static const char* randoShuffleSongs[3] = { "Song Locations", "Dungeon Rewards", "Anywhere" }; static const char* randoShopsanity[7] = { "Off", "0 Items", "1 Item", "2 Items", "3 Items", "4 Items", "Random" }; + static const char* randoShopsanityPrices[6] = { "Balanced", "Starter Wallet", "Adult Wallet", "Giant's Wallet", "Tycoon's Wallet", "Affordable" }; static const char* randoTokensanity[4] = { "Off", "Dungeons", "Overworld", "All Tokens" }; static const char* randoShuffleScrubs[4] = { "Off", "Affordable", "Expensive", "Random Prices" }; static const char* randoShuffleMerchants[3] = { "Off", "On (no hints)", "On (with hints)" }; @@ -3635,6 +3652,28 @@ void DrawRandoEditor(bool& open) { ); UIWidgets::EnhancementCombobox("gRandomizeShopsanity", randoShopsanity, RO_SHOPSANITY_MAX, RO_SHOPSANITY_OFF); + // Shopsanity Prices + switch (CVarGetInteger("gRandomizeShopsanity", RO_SHOPSANITY_OFF)) { + case RO_SHOPSANITY_OFF: + case RO_SHOPSANITY_ZERO_ITEMS: // no need to show it if there aren't shop slots in the pool + break; + default: + ImGui::Text(Settings::ShopsanityPrices.GetName().c_str()); + UIWidgets::InsertHelpHoverText( + "Balanced - The default randomization. Shop prices for shopsanity items will range between 0 to 300 rupees, " + "with a bias towards values slightly below the middle of the range, in multiples of 5.\n " + "\n" + "X Wallet - Randomized between 5 and the wallet's max size, in multiples of 5" + ); + UIWidgets::EnhancementCombobox("gRandomizeShopsanityPrices", randoShopsanityPrices, RO_SHOPSANITY_PRICE_MAX, RO_SHOPSANITY_PRICE_BALANCED); + UIWidgets::EnhancementCheckbox(Settings::ShopsanityPricesAffordable.GetName().c_str(), "gRandomizeShopsanityPricesAffordable", + CVarGetInteger("gRandomizeShopsanityPrices", RO_SHOPSANITY_PRICE_BALANCED) == RO_SHOPSANITY_PRICE_BALANCED, + "This can only apply to a wallet range."); + UIWidgets::InsertHelpHoverText("Cap item prices to a value just above the previous tier wallet's max value.\n" + "Affordable caps: starter = 10, adult = 105, giant = 205, tycoon = 505\n" + "Use this to enable wallet tier locking, but make shop items not as expensive as they could be."); + } + UIWidgets::PaddedSeparator(); // Shuffle Scrubs diff --git a/soh/soh/Enhancements/randomizer/randomizerTypes.h b/soh/soh/Enhancements/randomizer/randomizerTypes.h index 1fc7c7f0e..1dde6f23a 100644 --- a/soh/soh/Enhancements/randomizer/randomizerTypes.h +++ b/soh/soh/Enhancements/randomizer/randomizerTypes.h @@ -1013,6 +1013,8 @@ typedef enum { RSK_SHUFFLE_SONGS, RSK_SHUFFLE_TOKENS, RSK_SHOPSANITY, + RSK_SHOPSANITY_PRICES, + RSK_SHOPSANITY_PRICES_AFFORDABLE, RSK_SHUFFLE_SCRUBS, RSK_SHUFFLE_COWS, RSK_SHUFFLE_WEIRD_EGG, @@ -1191,6 +1193,16 @@ typedef enum { RO_SHOPSANITY_MAX, } RandoOptionShopsanity; +//Shopsanity price ranges +typedef enum { + RO_SHOPSANITY_PRICE_BALANCED, //Balanced random from 0-300 + RO_SHOPSANITY_PRICE_STARTER, //Wallets are random within their range, in increments of 5 rupees + RO_SHOPSANITY_PRICE_ADULT, + RO_SHOPSANITY_PRICE_GIANT, + RO_SHOPSANITY_PRICE_TYCOON, + RO_SHOPSANITY_PRICE_MAX, +} RandoOptionShopsanityPrices; + //Scrubsanity settings (off, affordable, expensive, random) typedef enum { RO_SCRUBS_OFF,