diff --git a/soh/soh/Enhancements/randomizer/3drando/fill.cpp b/soh/soh/Enhancements/randomizer/3drando/fill.cpp index b6a564c73..0bbe37bae 100644 --- a/soh/soh/Enhancements/randomizer/3drando/fill.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/fill.cpp @@ -60,29 +60,6 @@ PriceSettingsStruct merchantPrices = { RSK_MERCHANT_PRICES_AFFORDABLE, }; -static void RemoveStartingItemsFromPool() { - for (RandomizerGet startingItem : StartingInventory) { - for (size_t i = 0; i < ItemPool.size(); i++) { - if (startingItem == RG_BIGGORON_SWORD) { - if (ItemPool[i] == RG_GIANTS_KNIFE || ItemPool[i] == RG_BIGGORON_SWORD) { - ItemPool[i] = GetJunkItem(); - } - continue; - } else if (startingItem == ItemPool[i] || (Rando::StaticData::RetrieveItem(startingItem).IsBottleItem() && - Rando::StaticData::RetrieveItem(ItemPool[i]).IsBottleItem())) { - if (AdditionalHeartContainers > 0 && - (startingItem == RG_PIECE_OF_HEART || startingItem == RG_TREASURE_GAME_HEART)) { - ItemPool[i] = RG_HEART_CONTAINER; - AdditionalHeartContainers--; - } else { - ItemPool[i] = GetJunkItem(); - } - break; - } - } - } -} - static void PropagateTimeTravel(GetAccessibleLocationsStruct& gals, RandomizerGet ignore = RG_NONE, bool stopOnBeatable = false, bool addToPlaythrough = false) { // special check for temple of time @@ -163,7 +140,7 @@ static void ValidateOtherEntrance(GetAccessibleLocationsStruct& gals) { // Apply all items that are necessary for checking all location access static void ApplyAllAdvancmentItems() { std::vector itemsToPlace = - FilterFromPool(ItemPool, [](const auto i) { return Rando::StaticData::RetrieveItem(i).IsAdvancement(); }); + FilterFromPool(itemPool, [](const auto i) { return Rando::StaticData::RetrieveItem(i).IsAdvancement(); }); for (RandomizerGet unplacedItem : itemsToPlace) { Rando::StaticData::RetrieveItem(unplacedItem).ApplyEffect(); } @@ -321,7 +298,7 @@ bool IsBombchus(RandomizerGet item, bool includeShops = false) { } bool IsBeatableWithout(RandomizerCheck excludedCheck, bool replaceItem, - RandomizerGet ignore = RG_NONE) { // RANDOTODO make excludCheck an ItemLocation + RandomizerGet ignore = RG_NONE) { // RANDOTODO make excludedCheck an ItemLocation auto ctx = Rando::Context::GetInstance(); RandomizerGet copy = ctx->GetItemLocation(excludedCheck)->GetPlacedRandomizerGet(); // Copy out item ctx->GetItemLocation(excludedCheck)->SetPlacedItem(RG_NONE); // Write in empty item @@ -890,7 +867,7 @@ static void AssumedFill(const std::vector& items, const std::vect // copy all not yet placed advancement items so that we can apply their effects for the fill algorithm std::vector itemsToNotPlace = - FilterFromPool(ItemPool, [](const auto i) { return Rando::StaticData::RetrieveItem(i).IsAdvancement(); }); + FilterFromPool(itemPool, [](const auto i) { return Rando::StaticData::RetrieveItem(i).IsAdvancement(); }); // shuffle the order of items to place Shuffle(itemsToPlace); @@ -978,7 +955,7 @@ static void RandomizeDungeonRewards() { if (ctx->GetOption(RSK_SHUFFLE_DUNGEON_REWARDS).Is(RO_DUNGEON_REWARDS_END_OF_DUNGEON) || ctx->GetOption(RSK_SHUFFLE_DUNGEON_REWARDS).Is(RO_DUNGEON_REWARDS_VANILLA)) { // get stones and medallions - std::vector rewards = FilterAndEraseFromPool(ItemPool, [](const auto i) { + std::vector rewards = FilterAndEraseFromPool(itemPool, [](const auto i) { return Rando::StaticData::RetrieveItem(i).GetItemType() == ITEMTYPE_DUNGEONREWARD; }); @@ -1000,7 +977,7 @@ static void RandomizeDungeonRewards() { } } else if (ctx->GetOption(RSK_LINKS_POCKET).Is(RO_LINKS_POCKET_DUNGEON_REWARD)) { // get 1 stone/medallion - std::vector rewards = FilterFromPool(ItemPool, [](const auto i) { + std::vector rewards = FilterFromPool(itemPool, [](const auto i) { return Rando::StaticData::RetrieveItem(i).GetItemType() == ITEMTYPE_DUNGEONREWARD; }); // If there are no remaining stones/medallions, then Link's pocket won't get one @@ -1014,7 +991,7 @@ static void RandomizeDungeonRewards() { // baseOffset]; ctx->PlaceItemInLocation(RC_LINKS_POCKET, startingReward); // erase the stone/medallion from the Item Pool - FilterAndEraseFromPool(ItemPool, [startingReward](const RandomizerGet i) { return i == startingReward; }); + FilterAndEraseFromPool(itemPool, [startingReward](const RandomizerGet i) { return i == startingReward; }); } } @@ -1059,7 +1036,7 @@ static void RandomizeOwnDungeon(const Rando::DungeonInfo* dungeon) { // Add specific items that need be randomized within this dungeon if (ctx->GetOption(RSK_KEYSANITY).Is(RO_DUNGEON_ITEM_LOC_OWN_DUNGEON) && dungeon->GetSmallKey() != RG_NONE) { std::vector dungeonSmallKeys = - FilterAndEraseFromPool(ItemPool, [dungeon](const RandomizerGet i) { + FilterAndEraseFromPool(itemPool, [dungeon](const RandomizerGet i) { return (i == dungeon->GetSmallKey()) || (i == dungeon->GetKeyRing()); }); AddElementsToPool(dungeonItems, dungeonSmallKeys); @@ -1070,7 +1047,7 @@ static void RandomizeOwnDungeon(const Rando::DungeonInfo* dungeon) { (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_OWN_DUNGEON) && dungeon->GetBossKey() == RG_GANONS_CASTLE_BOSS_KEY)) { auto dungeonBossKey = - FilterAndEraseFromPool(ItemPool, [dungeon](const RandomizerGet i) { return i == dungeon->GetBossKey(); }); + FilterAndEraseFromPool(itemPool, [dungeon](const RandomizerGet i) { return i == dungeon->GetBossKey(); }); AddElementsToPool(dungeonItems, dungeonBossKey); } @@ -1080,7 +1057,7 @@ static void RandomizeOwnDungeon(const Rando::DungeonInfo* dungeon) { // randomize map and compass separately since they're not progressive if (ctx->GetOption(RSK_SHUFFLE_MAPANDCOMPASS).Is(RO_DUNGEON_ITEM_LOC_OWN_DUNGEON) && dungeon->GetMap() != RG_NONE && dungeon->GetCompass() != RG_NONE) { - auto dungeonMapAndCompass = FilterAndEraseFromPool(ItemPool, [dungeon](const RandomizerGet i) { + auto dungeonMapAndCompass = FilterAndEraseFromPool(itemPool, [dungeon](const RandomizerGet i) { return i == dungeon->GetMap() || i == dungeon->GetCompass(); }); AssumedFill(dungeonMapAndCompass, dungeonLocations); @@ -1107,12 +1084,12 @@ static void RandomizeDungeonItems() { for (auto dungeon : ctx->GetDungeons()->GetDungeonList()) { if (ctx->GetOption(RSK_KEYSANITY).Is(RO_DUNGEON_ITEM_LOC_ANY_DUNGEON)) { - auto dungeonKeys = FilterAndEraseFromPool(ItemPool, [dungeon](const RandomizerGet i) { + auto dungeonKeys = FilterAndEraseFromPool(itemPool, [dungeon](const RandomizerGet i) { return (i == dungeon->GetSmallKey()) || (i == dungeon->GetKeyRing()); }); AddElementsToPool(anyDungeonItems, dungeonKeys); } else if (ctx->GetOption(RSK_KEYSANITY).Is(RO_DUNGEON_ITEM_LOC_OVERWORLD)) { - auto dungeonKeys = FilterAndEraseFromPool(ItemPool, [dungeon](const RandomizerGet i) { + auto dungeonKeys = FilterAndEraseFromPool(itemPool, [dungeon](const RandomizerGet i) { return (i == dungeon->GetSmallKey()) || (i == dungeon->GetKeyRing()); }); AddElementsToPool(overworldItems, dungeonKeys); @@ -1121,45 +1098,45 @@ static void RandomizeDungeonItems() { if (ctx->GetOption(RSK_BOSS_KEYSANITY).Is(RO_DUNGEON_ITEM_LOC_ANY_DUNGEON) && dungeon->GetBossKey() != RG_GANONS_CASTLE_BOSS_KEY) { auto bossKey = FilterAndEraseFromPool( - ItemPool, [dungeon](const RandomizerGet i) { return i == dungeon->GetBossKey(); }); + itemPool, [dungeon](const RandomizerGet i) { return i == dungeon->GetBossKey(); }); AddElementsToPool(anyDungeonItems, bossKey); } else if (ctx->GetOption(RSK_BOSS_KEYSANITY).Is(RO_DUNGEON_ITEM_LOC_OVERWORLD) && dungeon->GetBossKey() != RG_GANONS_CASTLE_BOSS_KEY) { auto bossKey = FilterAndEraseFromPool( - ItemPool, [dungeon](const RandomizerGet i) { return i == dungeon->GetBossKey(); }); + itemPool, [dungeon](const RandomizerGet i) { return i == dungeon->GetBossKey(); }); AddElementsToPool(overworldItems, bossKey); } if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_ANY_DUNGEON)) { auto ganonBossKey = - FilterAndEraseFromPool(ItemPool, [](const auto i) { return i == RG_GANONS_CASTLE_BOSS_KEY; }); + FilterAndEraseFromPool(itemPool, [](const auto i) { return i == RG_GANONS_CASTLE_BOSS_KEY; }); AddElementsToPool(anyDungeonItems, ganonBossKey); } else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_OVERWORLD)) { auto ganonBossKey = - FilterAndEraseFromPool(ItemPool, [](const auto i) { return i == RG_GANONS_CASTLE_BOSS_KEY; }); + FilterAndEraseFromPool(itemPool, [](const auto i) { return i == RG_GANONS_CASTLE_BOSS_KEY; }); AddElementsToPool(overworldItems, ganonBossKey); } } if (ctx->GetOption(RSK_GERUDO_KEYS).Is(RO_GERUDO_KEYS_ANY_DUNGEON)) { - auto gerudoKeys = FilterAndEraseFromPool(ItemPool, [](const auto i) { + auto gerudoKeys = FilterAndEraseFromPool(itemPool, [](const auto i) { return i == RG_GERUDO_FORTRESS_SMALL_KEY || i == RG_GERUDO_FORTRESS_KEY_RING; }); AddElementsToPool(anyDungeonItems, gerudoKeys); } else if (ctx->GetOption(RSK_GERUDO_KEYS).Is(RO_GERUDO_KEYS_OVERWORLD)) { - auto gerudoKeys = FilterAndEraseFromPool(ItemPool, [](const auto i) { + auto gerudoKeys = FilterAndEraseFromPool(itemPool, [](const auto i) { return i == RG_GERUDO_FORTRESS_SMALL_KEY || i == RG_GERUDO_FORTRESS_KEY_RING; }); AddElementsToPool(overworldItems, gerudoKeys); } if (ctx->GetOption(RSK_SHUFFLE_DUNGEON_REWARDS).Is(RO_DUNGEON_REWARDS_ANY_DUNGEON)) { - auto rewards = FilterAndEraseFromPool(ItemPool, [](const auto i) { + auto rewards = FilterAndEraseFromPool(itemPool, [](const auto i) { return Rando::StaticData::RetrieveItem(i).GetItemType() == ITEMTYPE_DUNGEONREWARD; }); AddElementsToPool(anyDungeonItems, rewards); } else if (ctx->GetOption(RSK_SHUFFLE_DUNGEON_REWARDS).Is(RO_DUNGEON_REWARDS_OVERWORLD)) { - auto rewards = FilterAndEraseFromPool(ItemPool, [](const auto i) { + auto rewards = FilterAndEraseFromPool(itemPool, [](const auto i) { return Rando::StaticData::RetrieveItem(i).GetItemType() == ITEMTYPE_DUNGEONREWARD; }); AddElementsToPool(overworldItems, rewards); @@ -1172,12 +1149,12 @@ static void RandomizeDungeonItems() { // Randomize maps and compasses after since they're not advancement items for (auto dungeon : ctx->GetDungeons()->GetDungeonList()) { if (ctx->GetOption(RSK_SHUFFLE_MAPANDCOMPASS).Is(RO_DUNGEON_ITEM_LOC_ANY_DUNGEON)) { - auto mapAndCompassItems = FilterAndEraseFromPool(ItemPool, [dungeon](const RandomizerGet i) { + auto mapAndCompassItems = FilterAndEraseFromPool(itemPool, [dungeon](const RandomizerGet i) { return i == dungeon->GetMap() || i == dungeon->GetCompass(); }); AssumedFill(mapAndCompassItems, anyDungeonLocations, true); } else if (ctx->GetOption(RSK_SHUFFLE_MAPANDCOMPASS).Is(RO_DUNGEON_ITEM_LOC_OVERWORLD)) { - auto mapAndCompassItems = FilterAndEraseFromPool(ItemPool, [dungeon](const RandomizerGet i) { + auto mapAndCompassItems = FilterAndEraseFromPool(itemPool, [dungeon](const RandomizerGet i) { return i == dungeon->GetMap() || i == dungeon->GetCompass(); }); AssumedFill(mapAndCompassItems, ctx->overworldLocations, true); @@ -1189,14 +1166,14 @@ static void RandomizeLinksPocket() { auto ctx = Rando::Context::GetInstance(); if (ctx->GetOption(RSK_LINKS_POCKET).Is(RO_LINKS_POCKET_ADVANCEMENT)) { // Get all the advancement items don't include tokens - std::vector advancementItems = FilterAndEraseFromPool(ItemPool, [](const auto i) { + std::vector advancementItems = FilterAndEraseFromPool(itemPool, [](const auto i) { return Rando::StaticData::RetrieveItem(i).IsAdvancement() && Rando::StaticData::RetrieveItem(i).GetItemType() != ITEMTYPE_TOKEN; }); // select a random one RandomizerGet startingItem = RandomElement(advancementItems, true); // add the others back - AddElementsToPool(ItemPool, advancementItems); + AddElementsToPool(itemPool, advancementItems); ctx->PlaceItemInLocation(RC_LINKS_POCKET, startingItem); } else if (ctx->GetOption(RSK_LINKS_POCKET).Is(RO_LINKS_POCKET_NOTHING)) { @@ -1248,13 +1225,12 @@ int Fill() { ctx->GenerateLocationPool(); GenerateItemPool(); GenerateStartingInventory(); - RemoveStartingItemsFromPool(); FillExcludedLocations(); - // Temporarily add shop items to the ItemPool so that entrance randomization + // Temporarily add shop items to the itemPool so that entrance randomization // can validate the world using deku/hylian shields StartPerformanceTimer(PT_ENTRANCE_SHUFFLE); - AddElementsToPool(ItemPool, GetMinVanillaShopItems(8)); // assume worst case shopsanity 7 + AddElementsToPool(itemPool, GetMinVanillaShopItems(8)); // assume worst case shopsanity 7 if (ctx->GetOption(RSK_SHUFFLE_ENTRANCES)) { SPDLOG_INFO("Shuffling Entrances..."); if (ctx->GetEntranceShuffler()->ShuffleAllEntrances() == ENTRANCE_SHUFFLE_FAILURE) { @@ -1266,7 +1242,7 @@ int Fill() { } SetAreas(); // erase temporary shop items - FilterAndEraseFromPool(ItemPool, [](const auto item) { + FilterAndEraseFromPool(itemPool, [](const auto item) { return Rando::StaticData::RetrieveItem(item).GetItemType() == ITEMTYPE_SHOP; }); StopPerformanceTimer(PT_ENTRANCE_SHUFFLE); @@ -1378,7 +1354,7 @@ int Fill() { if (ctx->GetOption(RSK_SHUFFLE_SONGS).IsNot(RO_SONG_SHUFFLE_ANYWHERE) && ctx->GetOption(RSK_SHUFFLE_SONGS).IsNot(RO_SONG_SHUFFLE_OFF)) { // Get each song - std::vector songs = FilterAndEraseFromPool(ItemPool, [](const auto i) { + std::vector songs = FilterAndEraseFromPool(itemPool, [](const auto i) { return Rando::StaticData::RetrieveItem(i).GetItemType() == ITEMTYPE_SONG; }); @@ -1411,14 +1387,14 @@ int Fill() { SPDLOG_INFO("Shuffling Advancement Items"); // Then place the rest of the advancement items std::vector remainingAdvancementItems = FilterAndEraseFromPool( - ItemPool, [](const auto i) { return Rando::StaticData::RetrieveItem(i).IsAdvancement(); }); + itemPool, [](const auto i) { return Rando::StaticData::RetrieveItem(i).IsAdvancement(); }); AssumedFill(remainingAdvancementItems, ctx->allLocations, true); StopPerformanceTimer(PT_ADVANCEMENT_ITEMS); StartPerformanceTimer(PT_REMAINING_ITEMS); // Fast fill for the rest of the pool SPDLOG_INFO("Shuffling Remaining Items"); - std::vector remainingPool = FilterAndEraseFromPool(ItemPool, [](const auto i) { return true; }); + std::vector remainingPool = FilterAndEraseFromPool(itemPool, [](const auto i) { return true; }); FastFill(remainingPool, GetAllEmptyLocations(), false); StopPerformanceTimer(PT_REMAINING_ITEMS); diff --git a/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp b/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp index 5e02c004f..710869d81 100644 --- a/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp @@ -10,340 +10,79 @@ #include "z64item.h" #include -std::vector ItemPool = {}; -std::vector PendingJunkPool = {}; -const std::array dungeonRewards = { - RG_KOKIRI_EMERALD, RG_GORON_RUBY, RG_ZORA_SAPPHIRE, RG_FOREST_MEDALLION, RG_FIRE_MEDALLION, - RG_WATER_MEDALLION, RG_SPIRIT_MEDALLION, RG_SHADOW_MEDALLION, RG_LIGHT_MEDALLION, +std::vector itemPool = {}; +std::vector lesserPool = {}; +std::vector plentifulPool = {}; +std::vector junkPool = {}; +const std::array JunkPoolItems = { + RG_BOMBS_5, RG_BOMBS_10, RG_BOMBS_20, RG_DEKU_NUTS_5, RG_DEKU_STICK_1, RG_DEKU_SEEDS_30, RG_RECOVERY_HEART, + RG_ARROWS_5, RG_ARROWS_10, RG_ARROWS_30, RG_BLUE_RUPEE, RG_RED_RUPEE, RG_DEKU_NUTS_10, }; -const std::array JunkPoolItems = { - RG_BOMBS_5, RG_BOMBS_10, RG_BOMBS_20, RG_DEKU_NUTS_5, RG_DEKU_STICK_1, RG_DEKU_SEEDS_30, - RG_RECOVERY_HEART, RG_ARROWS_5, RG_ARROWS_10, RG_ARROWS_30, RG_BLUE_RUPEE, RG_RED_RUPEE, - RG_PURPLE_RUPEE, RG_HUGE_RUPEE, RG_DEKU_NUTS_10, RG_ICE_TRAP, -}; -const std::array alwaysItems = { - RG_BIGGORON_SWORD, - RG_BOOMERANG, - RG_LENS_OF_TRUTH, - RG_MEGATON_HAMMER, - RG_IRON_BOOTS, - RG_GORON_TUNIC, - RG_ZORA_TUNIC, - RG_HOVER_BOOTS, - RG_MIRROR_SHIELD, - RG_STONE_OF_AGONY, - RG_FIRE_ARROWS, - RG_ICE_ARROWS, - RG_LIGHT_ARROWS, - RG_DINS_FIRE, - RG_FARORES_WIND, - RG_NAYRUS_LOVE, - RG_GREG_RUPEE, - RG_PROGRESSIVE_HOOKSHOT, // 2 progressive hookshots - RG_PROGRESSIVE_HOOKSHOT, - RG_DEKU_SHIELD, - RG_HYLIAN_SHIELD, - RG_PROGRESSIVE_STRENGTH, // 3 progressive strength upgrades - RG_PROGRESSIVE_STRENGTH, - RG_PROGRESSIVE_STRENGTH, - RG_PROGRESSIVE_SCALE, // 2 progressive scales - RG_PROGRESSIVE_SCALE, - RG_PROGRESSIVE_BOW, // 3 progressive Bows - RG_PROGRESSIVE_BOW, - RG_PROGRESSIVE_BOW, - RG_PROGRESSIVE_SLINGSHOT, // 3 progressive bullet bags - RG_PROGRESSIVE_SLINGSHOT, - RG_PROGRESSIVE_SLINGSHOT, - RG_PROGRESSIVE_BOMB_BAG, // 3 progressive bomb bags - RG_PROGRESSIVE_BOMB_BAG, - RG_PROGRESSIVE_BOMB_BAG, - RG_PROGRESSIVE_WALLET, // 2 progressive wallets - RG_PROGRESSIVE_WALLET, - RG_PROGRESSIVE_MAGIC_METER, // 2 progressive magic meters - RG_PROGRESSIVE_MAGIC_METER, - RG_DOUBLE_DEFENSE, - RG_PROGRESSIVE_STICK_UPGRADE, // 2 stick upgrades - RG_PROGRESSIVE_STICK_UPGRADE, - RG_PROGRESSIVE_NUT_UPGRADE, // 2 nut upgrades - RG_PROGRESSIVE_NUT_UPGRADE, - RG_RECOVERY_HEART, // 6 recovery hearts - RG_RECOVERY_HEART, - RG_RECOVERY_HEART, - RG_RECOVERY_HEART, - RG_RECOVERY_HEART, - RG_RECOVERY_HEART, - RG_BOMBS_5, // 2 - RG_BOMBS_5, - RG_BOMBS_10, - RG_BOMBS_20, - RG_ARROWS_5, - RG_ARROWS_10, // 5 - RG_ARROWS_10, - RG_ARROWS_10, - RG_TREASURE_GAME_HEART, -}; -const std::array easyItems = { - RG_BIGGORON_SWORD, - RG_KOKIRI_SWORD, - RG_MASTER_SWORD, - RG_BOOMERANG, - RG_LENS_OF_TRUTH, - RG_MEGATON_HAMMER, - RG_IRON_BOOTS, - RG_GORON_TUNIC, - RG_ZORA_TUNIC, - RG_HOVER_BOOTS, - RG_MIRROR_SHIELD, - RG_FIRE_ARROWS, - RG_LIGHT_ARROWS, - RG_DINS_FIRE, - RG_PROGRESSIVE_HOOKSHOT, - RG_PROGRESSIVE_STRENGTH, - RG_PROGRESSIVE_SCALE, - RG_PROGRESSIVE_WALLET, - RG_PROGRESSIVE_MAGIC_METER, - RG_PROGRESSIVE_STICK_UPGRADE, - RG_PROGRESSIVE_NUT_UPGRADE, - RG_PROGRESSIVE_BOW, - RG_PROGRESSIVE_SLINGSHOT, - RG_PROGRESSIVE_BOMB_BAG, - RG_DOUBLE_DEFENSE, - RG_HEART_CONTAINER, // 16 Heart Containers - RG_HEART_CONTAINER, - RG_HEART_CONTAINER, - RG_HEART_CONTAINER, - RG_HEART_CONTAINER, - RG_HEART_CONTAINER, - RG_HEART_CONTAINER, - RG_HEART_CONTAINER, - RG_HEART_CONTAINER, - RG_HEART_CONTAINER, - RG_HEART_CONTAINER, - RG_HEART_CONTAINER, - RG_HEART_CONTAINER, - RG_HEART_CONTAINER, - RG_HEART_CONTAINER, - RG_HEART_CONTAINER, - RG_PIECE_OF_HEART, // 3 heart pieces - RG_PIECE_OF_HEART, - RG_PIECE_OF_HEART, -}; -const std::array normalItems = { - // 35 pieces of heart - RG_PIECE_OF_HEART, - RG_PIECE_OF_HEART, - RG_PIECE_OF_HEART, - RG_PIECE_OF_HEART, - RG_PIECE_OF_HEART, - RG_PIECE_OF_HEART, - RG_PIECE_OF_HEART, - RG_PIECE_OF_HEART, - RG_PIECE_OF_HEART, - RG_PIECE_OF_HEART, - RG_PIECE_OF_HEART, - RG_PIECE_OF_HEART, - RG_PIECE_OF_HEART, - RG_PIECE_OF_HEART, - RG_PIECE_OF_HEART, - RG_PIECE_OF_HEART, - RG_PIECE_OF_HEART, - RG_PIECE_OF_HEART, - RG_PIECE_OF_HEART, - RG_PIECE_OF_HEART, - RG_PIECE_OF_HEART, - RG_PIECE_OF_HEART, - RG_PIECE_OF_HEART, - RG_PIECE_OF_HEART, - RG_PIECE_OF_HEART, - RG_PIECE_OF_HEART, - RG_PIECE_OF_HEART, - RG_PIECE_OF_HEART, - RG_PIECE_OF_HEART, - RG_PIECE_OF_HEART, - RG_PIECE_OF_HEART, - RG_PIECE_OF_HEART, - RG_PIECE_OF_HEART, - RG_PIECE_OF_HEART, - RG_PIECE_OF_HEART, - // 8 heart containers - RG_HEART_CONTAINER, - RG_HEART_CONTAINER, - RG_HEART_CONTAINER, - RG_HEART_CONTAINER, - RG_HEART_CONTAINER, - RG_HEART_CONTAINER, - RG_HEART_CONTAINER, - RG_HEART_CONTAINER, -}; -const std::array DT_Vanilla = { - RG_RECOVERY_HEART, - RG_RECOVERY_HEART, -}; -const std::array DT_MQ = { - RG_DEKU_SHIELD, - RG_DEKU_SHIELD, - RG_PURPLE_RUPEE, -}; -const std::array DC_Vanilla = { - RG_RED_RUPEE, -}; -const std::array DC_MQ = { - RG_HYLIAN_SHIELD, - RG_BLUE_RUPEE, -}; -const std::array JB_MQ = { - RG_DEKU_NUTS_5, RG_DEKU_NUTS_5, RG_DEKU_NUTS_5, RG_DEKU_NUTS_5, RG_RECOVERY_HEART, RG_DEKU_SHIELD, RG_DEKU_STICK_1, -}; -const std::array FoT_Vanilla = { - RG_RECOVERY_HEART, - RG_ARROWS_10, - RG_ARROWS_30, -}; -const std::array FoT_MQ = { - RG_ARROWS_5, -}; -const std::array FiT_Vanilla = { - RG_HUGE_RUPEE, -}; -const std::array FiT_MQ = { - RG_BOMBS_20, - RG_HYLIAN_SHIELD, -}; -const std::array SpT_Vanilla = { - RG_DEKU_SHIELD, - RG_DEKU_SHIELD, - RG_RECOVERY_HEART, - RG_BOMBS_20, -}; -const std::array SpT_MQ = { - RG_PURPLE_RUPEE, - RG_PURPLE_RUPEE, - RG_ARROWS_30, -}; -const std::array ShT_Vanilla = { - RG_ARROWS_30, -}; -const std::array ShT_MQ = { - RG_ARROWS_5, - RG_ARROWS_5, - RG_RED_RUPEE, -}; -const std::array BW_Vanilla = { - RG_RECOVERY_HEART, RG_BOMBS_10, RG_HUGE_RUPEE, RG_DEKU_NUTS_5, RG_DEKU_NUTS_10, RG_DEKU_SHIELD, RG_HYLIAN_SHIELD, -}; -const std::array GTG_Vanilla = { - RG_ARROWS_30, - RG_ARROWS_30, - RG_ARROWS_30, - RG_HUGE_RUPEE, -}; -const std::array GTG_MQ = { - RG_TREASURE_GAME_GREEN_RUPEE, RG_TREASURE_GAME_GREEN_RUPEE, RG_ARROWS_10, RG_GREEN_RUPEE, RG_PURPLE_RUPEE, -}; -const std::array GC_Vanilla = { - RG_BLUE_RUPEE, - RG_BLUE_RUPEE, - RG_BLUE_RUPEE, - RG_ARROWS_30, -}; -const std::array GC_MQ = { - RG_ARROWS_10, RG_ARROWS_10, RG_BOMBS_5, RG_RED_RUPEE, RG_RECOVERY_HEART, -}; -const std::array normalBottles = { - RG_EMPTY_BOTTLE, - RG_BOTTLE_WITH_MILK, - RG_BOTTLE_WITH_RED_POTION, - RG_BOTTLE_WITH_GREEN_POTION, - RG_BOTTLE_WITH_BLUE_POTION, - RG_BOTTLE_WITH_FAIRY, - RG_BOTTLE_WITH_FISH, - RG_BOTTLE_WITH_BUGS, - RG_BOTTLE_WITH_POE, - RG_BOTTLE_WITH_BIG_POE, - RG_BOTTLE_WITH_BLUE_FIRE, -}; -const std::array normalRupees = { - RG_BLUE_RUPEE, RG_BLUE_RUPEE, RG_BLUE_RUPEE, RG_BLUE_RUPEE, RG_BLUE_RUPEE, RG_BLUE_RUPEE, - RG_BLUE_RUPEE, RG_BLUE_RUPEE, RG_BLUE_RUPEE, RG_BLUE_RUPEE, RG_BLUE_RUPEE, RG_BLUE_RUPEE, - RG_BLUE_RUPEE, RG_RED_RUPEE, RG_RED_RUPEE, RG_RED_RUPEE, RG_RED_RUPEE, RG_RED_RUPEE, - RG_PURPLE_RUPEE, RG_PURPLE_RUPEE, RG_PURPLE_RUPEE, RG_PURPLE_RUPEE, RG_PURPLE_RUPEE, RG_PURPLE_RUPEE, - RG_PURPLE_RUPEE, RG_HUGE_RUPEE, RG_HUGE_RUPEE, RG_HUGE_RUPEE, -}; -const std::array shopsanityRupees = { - RG_BLUE_RUPEE, RG_BLUE_RUPEE, RG_RED_RUPEE, RG_RED_RUPEE, RG_RED_RUPEE, RG_RED_RUPEE, - RG_RED_RUPEE, RG_RED_RUPEE, RG_RED_RUPEE, RG_RED_RUPEE, RG_RED_RUPEE, RG_RED_RUPEE, - RG_PURPLE_RUPEE, RG_PURPLE_RUPEE, RG_PURPLE_RUPEE, RG_PURPLE_RUPEE, RG_PURPLE_RUPEE, RG_PURPLE_RUPEE, - RG_PURPLE_RUPEE, RG_PURPLE_RUPEE, RG_PURPLE_RUPEE, RG_PURPLE_RUPEE, RG_HUGE_RUPEE, RG_HUGE_RUPEE, - RG_HUGE_RUPEE, RG_HUGE_RUPEE, RG_HUGE_RUPEE, RG_HUGE_RUPEE, -}; -const std::array dekuScrubItems = { - RG_DEKU_NUTS_5, RG_DEKU_NUTS_5, RG_DEKU_NUTS_5, RG_DEKU_NUTS_5, RG_DEKU_NUTS_5, - RG_DEKU_STICK_1, RG_BOMBS_5, RG_BOMBS_5, RG_BOMBS_5, RG_BOMBS_5, - RG_BOMBS_5, RG_RECOVERY_HEART, RG_RECOVERY_HEART, RG_RECOVERY_HEART, RG_RECOVERY_HEART, - RG_BLUE_RUPEE, RG_BLUE_RUPEE, RG_BLUE_RUPEE, RG_BLUE_RUPEE, -}; -const std::array songList = { - RG_ZELDAS_LULLABY, RG_EPONAS_SONG, RG_SUNS_SONG, RG_SARIAS_SONG, - RG_SONG_OF_TIME, RG_SONG_OF_STORMS, RG_MINUET_OF_FOREST, RG_PRELUDE_OF_LIGHT, - RG_BOLERO_OF_FIRE, RG_SERENADE_OF_WATER, RG_NOCTURNE_OF_SHADOW, RG_REQUIEM_OF_SPIRIT, -}; -const std::array tradeItems = { - RG_POCKET_EGG, - // RG_POCKET_CUCCO, - RG_COJIRO, - RG_ODD_MUSHROOM, - RG_POACHERS_SAW, - RG_BROKEN_SWORD, - RG_PRESCRIPTION, - RG_EYEBALL_FROG, - RG_EYEDROPS, - RG_CLAIM_CHECK, +// RANDOTODO should probably check the same thing as check matches contents at some point +const std::map*> poolForItem = { + { RG_BOMBS_5, &junkPool }, { RG_BOMBS_10, &junkPool }, { RG_BOMBS_20, &junkPool }, + { RG_DEKU_NUTS_5, &junkPool }, { RG_DEKU_STICK_1, &junkPool }, { RG_DEKU_SEEDS_30, &junkPool }, + { RG_RECOVERY_HEART, &junkPool }, { RG_ARROWS_5, &junkPool }, { RG_ARROWS_10, &junkPool }, + { RG_ARROWS_30, &junkPool }, { RG_GREEN_RUPEE, &junkPool }, { RG_BLUE_RUPEE, &junkPool }, + { RG_RED_RUPEE, &junkPool }, { RG_DEKU_NUTS_10, &junkPool }, { RG_TREASURE_GAME_GREEN_RUPEE, &junkPool }, + { RG_PURPLE_RUPEE, &lesserPool }, { RG_HUGE_RUPEE, &lesserPool }, { RG_DEKU_SHIELD, &lesserPool }, + { RG_HYLIAN_SHIELD, &lesserPool }, { RG_BOMBCHU_5, &lesserPool }, { RG_BOMBCHU_10, &lesserPool }, + { RG_BOMBCHU_20, &lesserPool } }; -void AddItemToPool(std::vector& pool, RandomizerGet item, size_t count /*= 1*/) { - pool.insert(pool.end(), count, item); +void AddItemToPool(RandomizerGet item, int plentifulCount, size_t balancedCount, size_t scarceCount = 1, + size_t minimalCount = 1, bool iceTrapModel = true) { + int count = balancedCount; + switch (ctx->GetOption(RSK_ITEM_POOL).Get()) { + case RO_ITEM_POOL_SCARCE: + count = scarceCount; + break; + case RO_ITEM_POOL_MINIMAL: + count = minimalCount; + break; + default: + break; + } + if (!poolForItem.contains(item)) { + itemPool.insert(itemPool.end(), count, item); + if (ctx->GetOption(RSK_ITEM_POOL).Is(RO_ITEM_POOL_PLENTIFUL)) { + plentifulPool.insert(plentifulPool.end(), plentifulCount - count, item); + } + if (iceTrapModel && count > 0 && item != RG_ICE_TRAP) { + ctx->possibleIceTrapModels.insert(item); + } + } else { + poolForItem.at(item)->insert(poolForItem.at(item)->end(), count, item); + } } -template static void AddItemsToPool(std::vector& toPool, const FromPool& fromPool) { - AddElementsToPool(toPool, fromPool); -} - -static void AddItemToMainPool(const RandomizerGet item, size_t count = 1) { - ItemPool.insert(ItemPool.end(), count, item); -} - -static void AddRandomBottle(std::vector& bottlePool) { - AddItemToMainPool(RandomElement(bottlePool, true)); +void AddFixedItemToPool(RandomizerGet item, int count = 1, bool iceTrapModel = true) { + if (!poolForItem.contains(item)) { + itemPool.insert(itemPool.end(), count, item); + if (iceTrapModel && count > 0 && item != RG_ICE_TRAP) { + ctx->possibleIceTrapModels.insert(item); + } + } else { + poolForItem.at(item)->insert(poolForItem.at(item)->end(), count, item); + } } RandomizerGet GetJunkItem() { auto ctx = Rando::Context::GetInstance(); - if (ctx->GetOption(RSK_ICE_TRAPS).Is(RO_ICE_TRAPS_MAYHEM) || - ctx->GetOption(RSK_ICE_TRAPS).Is(RO_ICE_TRAPS_ONSLAUGHT)) { + if (ctx->GetOption(RSK_ICE_TRAP_PERCENT).IsNot(0) && + (ctx->GetOption(RSK_ICE_TRAP_PERCENT).Is(100) || Random(0, 100) < ctx->GetOption(RSK_ICE_TRAP_PERCENT).Get())) { return RG_ICE_TRAP; - } else if (ctx->GetOption(RSK_ICE_TRAPS).Is(RO_ICE_TRAPS_EXTRA)) { - return RandomElement(JunkPoolItems); } - // Ice Trap is the last item in JunkPoolItems, so subtract 1 to never hit that index - uint8_t idx = Random(0, static_cast(JunkPoolItems.size()) - 1); - return JunkPoolItems[idx]; -} - -static RandomizerGet GetPendingJunkItem() { - if (PendingJunkPool.empty()) { - return GetJunkItem(); - } - - return RandomElement(PendingJunkPool, true); + return RandomElement(JunkPoolItems); } // Replace junk items in the pool with pending junk static void ReplaceMaxItem(const RandomizerGet itemToReplace, int max) { int itemCount = 0; - for (size_t i = 0; i < ItemPool.size(); i++) { - if (ItemPool[i] == itemToReplace) { + for (size_t i = 0; i < itemPool.size(); i++) { + if (itemPool[i] == itemToReplace) { if (itemCount >= max) { - ItemPool[i] = RG_NONE; + itemPool[i] = RG_NONE; } itemCount++; } @@ -372,7 +111,7 @@ static void PlaceVanillaBossKeys() { } } -static void PlaceItemsForType(RandomizerCheckType rctype, bool overworldActive, bool dungeonActive) { +static void PlaceItemsForType(RandomizerCheckType rctype, bool overworldActive = true, bool dungeonActive = true) { if (!(overworldActive || dungeonActive)) { return; } @@ -382,14 +121,14 @@ static void PlaceItemsForType(RandomizerCheckType rctype, bool overworldActive, // If item is in the overworld and shuffled, add its item to the pool if (loc->IsOverworld()) { if (overworldActive) { - AddItemToMainPool(loc->GetVanillaItem()); + AddFixedItemToPool(loc->GetVanillaItem(), 1, false); } } else { if (dungeonActive) { // If the same in MQ and vanilla, add. RandomizerCheckQuest currentQuest = loc->GetQuest(); if (currentQuest == RCQUEST_BOTH) { - AddItemToMainPool(loc->GetVanillaItem()); + AddFixedItemToPool(loc->GetVanillaItem(), 1, false); } else { // Check if current item's dungeon is vanilla or MQ, and only add if quest corresponds to it. SceneID itemScene = loc->GetScene(); @@ -398,7 +137,7 @@ static void PlaceItemsForType(RandomizerCheckType rctype, bool overworldActive, bool isMQ = ctx->GetDungeon(itemScene)->IsMQ(); if ((isMQ && currentQuest == RCQUEST_MQ) || (!isMQ && currentQuest == RCQUEST_VANILLA)) { - AddItemToMainPool(loc->GetVanillaItem()); + AddFixedItemToPool(loc->GetVanillaItem(), 1, false); } } } @@ -407,111 +146,181 @@ static void PlaceItemsForType(RandomizerCheckType rctype, bool overworldActive, } } -static void SetScarceItemPool() { - ReplaceMaxItem(RG_PROGRESSIVE_BOMBCHU_BAG, ctx->GetOption(RSK_BOMBCHU_BAG).Is(RO_BOMBCHU_BAG_SINGLE) ? 3 : 2); - ReplaceMaxItem(RG_BOMBCHU_5, 1); - ReplaceMaxItem(RG_BOMBCHU_10, 2); - ReplaceMaxItem(RG_BOMBCHU_20, 0); - ReplaceMaxItem(RG_PROGRESSIVE_MAGIC_METER, 1); - ReplaceMaxItem(RG_DOUBLE_DEFENSE, 0); - ReplaceMaxItem(RG_PROGRESSIVE_STICK_UPGRADE, ctx->GetOption(RSK_SHUFFLE_DEKU_STICK_BAG) ? 2 : 1); - ReplaceMaxItem(RG_PROGRESSIVE_NUT_UPGRADE, ctx->GetOption(RSK_SHUFFLE_DEKU_NUT_BAG) ? 2 : 1); - ReplaceMaxItem(RG_PROGRESSIVE_BOW, 2); - ReplaceMaxItem(RG_PROGRESSIVE_SLINGSHOT, 2); - ReplaceMaxItem(RG_PROGRESSIVE_BOMB_BAG, 2); - ReplaceMaxItem(RG_HEART_CONTAINER, 0); -} - -static void SetMinimalItemPool() { - auto ctx = Rando::Context::GetInstance(); - ReplaceMaxItem(RG_PROGRESSIVE_BOMBCHU_BAG, 1); - ReplaceMaxItem(RG_BOMBCHU_5, 1); - ReplaceMaxItem(RG_BOMBCHU_10, 0); - ReplaceMaxItem(RG_BOMBCHU_20, 0); - ReplaceMaxItem(RG_NAYRUS_LOVE, 0); - ReplaceMaxItem(RG_PROGRESSIVE_MAGIC_METER, 1); - ReplaceMaxItem(RG_DOUBLE_DEFENSE, 0); - ReplaceMaxItem(RG_PROGRESSIVE_STICK_UPGRADE, ctx->GetOption(RSK_SHUFFLE_DEKU_STICK_BAG) ? 1 : 0); - ReplaceMaxItem(RG_PROGRESSIVE_NUT_UPGRADE, ctx->GetOption(RSK_SHUFFLE_DEKU_NUT_BAG) ? 1 : 0); - ReplaceMaxItem(RG_PROGRESSIVE_BOW, 1); - ReplaceMaxItem(RG_PROGRESSIVE_SLINGSHOT, 1); - ReplaceMaxItem(RG_PROGRESSIVE_BOMB_BAG, 1); - ReplaceMaxItem(RG_PIECE_OF_HEART, 0); - // Need an extra heart container when starting with 1 heart to be able to reach 3 hearts - ReplaceMaxItem(RG_HEART_CONTAINER, (ctx->GetOption(RSK_STARTING_HEARTS).Get() == 18) ? 1 : 0); -} - void GenerateItemPool() { // RANDOTODO proper removal of items not in pool or logically relevant instead of dummy checks. auto ctx = Rando::Context::GetInstance(); - ItemPool.clear(); - PendingJunkPool.clear(); + itemPool.clear(); + junkPool.clear(); + plentifulPool.clear(); + lesserPool.clear(); + int reservedSlots = 0; - // Initialize ice trap models to always major items - ctx->possibleIceTrapModels = { - RG_MIRROR_SHIELD, - RG_BOOMERANG, - RG_LENS_OF_TRUTH, - RG_MEGATON_HAMMER, - RG_IRON_BOOTS, - RG_HOVER_BOOTS, - RG_STONE_OF_AGONY, - RG_DINS_FIRE, - RG_FARORES_WIND, - RG_NAYRUS_LOVE, - RG_FIRE_ARROWS, - RG_ICE_ARROWS, - RG_LIGHT_ARROWS, - RG_DOUBLE_DEFENSE, - RG_CLAIM_CHECK, - RG_PROGRESSIVE_HOOKSHOT, - RG_PROGRESSIVE_STRENGTH, - RG_PROGRESSIVE_BOMB_BAG, - RG_PROGRESSIVE_BOW, - RG_PROGRESSIVE_SLINGSHOT, - RG_PROGRESSIVE_WALLET, - RG_PROGRESSIVE_SCALE, - RG_PROGRESSIVE_MAGIC_METER, - }; - // Check song shuffle and dungeon reward shuffle just for ice traps - if (ctx->GetOption(RSK_SHUFFLE_SONGS).Is(RO_SONG_SHUFFLE_ANYWHERE)) { - // Push item ids for songs - ctx->possibleIceTrapModels.push_back(RG_ZELDAS_LULLABY); - ctx->possibleIceTrapModels.push_back(RG_EPONAS_SONG); - ctx->possibleIceTrapModels.push_back(RG_SARIAS_SONG); - ctx->possibleIceTrapModels.push_back(RG_SUNS_SONG); - ctx->possibleIceTrapModels.push_back(RG_SONG_OF_TIME); - ctx->possibleIceTrapModels.push_back(RG_SONG_OF_STORMS); - ctx->possibleIceTrapModels.push_back(RG_MINUET_OF_FOREST); - ctx->possibleIceTrapModels.push_back(RG_BOLERO_OF_FIRE); - ctx->possibleIceTrapModels.push_back(RG_SERENADE_OF_WATER); - ctx->possibleIceTrapModels.push_back(RG_REQUIEM_OF_SPIRIT); - ctx->possibleIceTrapModels.push_back(RG_NOCTURNE_OF_SHADOW); - ctx->possibleIceTrapModels.push_back(RG_PRELUDE_OF_LIGHT); + // 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_NAYRUS_LOVE, 2, 1, 0, 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); + AddItemToPool(RG_PROGRESSIVE_STRENGTH, 4, 3, 3, 3); + AddItemToPool(RG_DOUBLE_DEFENSE, 2, 1, 0, 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); + AddFixedItemToPool(RG_BOMBS_5, isScrubs ? 2 : 8); + AddFixedItemToPool(RG_DEKU_STICK_1, isScrubs ? 0 : 2); + AddFixedItemToPool(RG_BOMBS_10, 1); + AddFixedItemToPool(RG_BOMBS_20, 1); + AddFixedItemToPool(RG_ARROWS_5, 1); + AddFixedItemToPool(RG_ARROWS_10, 3); + + if (isScrubs) { + AddFixedItemToPool(RG_DEKU_NUTS_5, ctx->GetDungeon(Rando::JABU_JABUS_BELLY)->IsVanilla() ? 5 : 6); + // Scrubs which sell seeds or arrows sell it based on age, this randomly assigns them + for (uint8_t i = 0; i < 7; i++) { + if (Random(0, 2)) { + AddFixedItemToPool(RG_ARROWS_30); + } else { + AddFixedItemToPool(RG_DEKU_SEEDS_30); + } + } } - if (ctx->GetOption(RSK_SHUFFLE_DUNGEON_REWARDS).Is(RO_DUNGEON_REWARDS_ANYWHERE)) { - // Push item ids for dungeon rewards - ctx->possibleIceTrapModels.push_back(RG_KOKIRI_EMERALD); - ctx->possibleIceTrapModels.push_back(RG_GORON_RUBY); - ctx->possibleIceTrapModels.push_back(RG_ZORA_SAPPHIRE); - ctx->possibleIceTrapModels.push_back(RG_FOREST_MEDALLION); - ctx->possibleIceTrapModels.push_back(RG_FIRE_MEDALLION); - ctx->possibleIceTrapModels.push_back(RG_WATER_MEDALLION); - ctx->possibleIceTrapModels.push_back(RG_SPIRIT_MEDALLION); - ctx->possibleIceTrapModels.push_back(RG_SHADOW_MEDALLION); - ctx->possibleIceTrapModels.push_back(RG_LIGHT_MEDALLION); + + 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); + //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 stickShuffle = ctx->GetOption(RSK_SHUFFLE_DEKU_STICK_BAG) ? 1 : 0; + AddItemToPool(RG_PROGRESSIVE_STICK_UPGRADE, 3 + infiniteProgressive + stickShuffle, + 2 + infiniteProgressive + stickShuffle, + 1 + infiniteProgressive + stickShuffle, + 0 + infiniteProgressive + stickShuffle); + + int nutShuffle = ctx->GetOption(RSK_SHUFFLE_DEKU_NUT_BAG) ? 1 : 0; + AddItemToPool(RG_PROGRESSIVE_NUT_UPGRADE, 3 + infiniteProgressive + nutShuffle, + 2 + infiniteProgressive + nutShuffle, + 1 + infiniteProgressive + nutShuffle, + 0 + infiniteProgressive + nutShuffle); + + if (ctx->GetOption(RSK_BOMBCHU_BAG).Is(RO_BOMBCHU_BAG_SINGLE)) { + AddItemToPool(RG_PROGRESSIVE_BOMBCHU_BAG, 6, 5, 3, 1); + } 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); + } else { + AddItemToPool(RG_BOMBCHU_20, 2, 1, 0, 0); + AddItemToPool(RG_BOMBCHU_10, 3, 3, 2, 0); + AddItemToPool(RG_BOMBCHU_5, 1, 1, 1, 1); } + // add extra songs only if song shuffle is anywhere + if (ctx->GetOption(RSK_SHUFFLE_SONGS).IsNot(RO_SONG_SHUFFLE_OFF)) { + bool songAnywhere = ctx->GetOption(RSK_SHUFFLE_SONGS).Is(RO_SONG_SHUFFLE_ANYWHERE); + if (!ctx->GetOption(RSK_STARTING_ZELDAS_LULLABY).Get()) { + AddItemToPool(RG_ZELDAS_LULLABY, songAnywhere ? 2 : 1, 1, 1, 1, songAnywhere); + } + if (!ctx->GetOption(RSK_STARTING_EPONAS_SONG).Get()) { + AddItemToPool(RG_EPONAS_SONG, songAnywhere ? 2 : 1, 1, 1, 1, songAnywhere); + } + if (!ctx->GetOption(RSK_STARTING_SARIAS_SONG).Get()) { + AddItemToPool(RG_SARIAS_SONG, songAnywhere ? 2 : 1, 1, 1, 1, songAnywhere); + } + if (!ctx->GetOption(RSK_STARTING_SUNS_SONG).Get()) { + AddItemToPool(RG_SUNS_SONG, songAnywhere ? 2 : 1, 1, 1, 1, songAnywhere); + } + if (!ctx->GetOption(RSK_STARTING_SONG_OF_TIME).Get()) { + AddItemToPool(RG_SONG_OF_TIME, songAnywhere ? 2 : 1, 1, 1, 1, songAnywhere); + } + if (!ctx->GetOption(RSK_STARTING_SONG_OF_STORMS).Get()) { + AddItemToPool(RG_SONG_OF_STORMS, songAnywhere ? 2 : 1, 1, 1, 1, songAnywhere); + } + if (!ctx->GetOption(RSK_STARTING_MINUET_OF_FOREST).Get()) { + AddItemToPool(RG_MINUET_OF_FOREST, songAnywhere ? 2 : 1, 1, 1, 1, songAnywhere); + } + if (!ctx->GetOption(RSK_STARTING_BOLERO_OF_FIRE).Get()) { + AddItemToPool(RG_BOLERO_OF_FIRE, songAnywhere ? 2 : 1, 1, 1, 1, songAnywhere); + } + if (!ctx->GetOption(RSK_STARTING_SERENADE_OF_WATER).Get()) { + AddItemToPool(RG_SERENADE_OF_WATER, songAnywhere ? 2 : 1, 1, 1, 1, songAnywhere); + } + if (!ctx->GetOption(RSK_STARTING_REQUIEM_OF_SPIRIT).Get()) { + AddItemToPool(RG_REQUIEM_OF_SPIRIT, songAnywhere ? 2 : 1, 1, 1, 1, songAnywhere); + } + if (!ctx->GetOption(RSK_STARTING_NOCTURNE_OF_SHADOW).Get()) { + AddItemToPool(RG_NOCTURNE_OF_SHADOW, songAnywhere ? 2 : 1, 1, 1, 1, songAnywhere); + } + if (!ctx->GetOption(RSK_STARTING_PRELUDE_OF_LIGHT).Get()) { + AddItemToPool(RG_PRELUDE_OF_LIGHT, songAnywhere ? 2 : 1, 1, 1, 1, songAnywhere); + } + } else { + ctx->PlaceItemInLocation(RC_SHEIK_IN_FOREST, RG_MINUET_OF_FOREST, false, true); + ctx->PlaceItemInLocation(RC_SHEIK_IN_CRATER, RG_BOLERO_OF_FIRE, false, true); + ctx->PlaceItemInLocation(RC_SHEIK_IN_ICE_CAVERN, RG_SERENADE_OF_WATER, false, true); + ctx->PlaceItemInLocation(RC_SHEIK_AT_COLOSSUS, RG_REQUIEM_OF_SPIRIT, false, true); + ctx->PlaceItemInLocation(RC_SHEIK_IN_KAKARIKO, RG_NOCTURNE_OF_SHADOW, false, true); + ctx->PlaceItemInLocation(RC_SHEIK_AT_TEMPLE, RG_PRELUDE_OF_LIGHT, false, true); + ctx->PlaceItemInLocation(RC_SONG_FROM_IMPA, RG_ZELDAS_LULLABY, false, true); + ctx->PlaceItemInLocation(RC_SONG_FROM_MALON, RG_EPONAS_SONG, false, true); + ctx->PlaceItemInLocation(RC_SONG_FROM_SARIA, RG_SARIAS_SONG, false, true); + ctx->PlaceItemInLocation(RC_SONG_FROM_ROYAL_FAMILYS_TOMB, RG_SUNS_SONG, false, true); + ctx->PlaceItemInLocation(RC_SONG_FROM_OCARINA_OF_TIME, RG_SONG_OF_TIME, false, true); + ctx->PlaceItemInLocation(RC_SONG_FROM_WINDMILL, RG_SONG_OF_STORMS, false, true); + } + + bool rewardIceTraps = ctx->GetOption(RSK_SHUFFLE_DUNGEON_REWARDS).Get() >= RO_DUNGEON_REWARDS_ANY_DUNGEON; + AddFixedItemToPool(RG_KOKIRI_EMERALD, 1, rewardIceTraps); + AddFixedItemToPool(RG_GORON_RUBY, 1, rewardIceTraps); + AddFixedItemToPool(RG_ZORA_SAPPHIRE, 1, rewardIceTraps); + AddFixedItemToPool(RG_FOREST_MEDALLION, 1, rewardIceTraps); + AddFixedItemToPool(RG_FIRE_MEDALLION, 1, rewardIceTraps); + AddFixedItemToPool(RG_WATER_MEDALLION, 1, rewardIceTraps); + AddFixedItemToPool(RG_SPIRIT_MEDALLION, 1, rewardIceTraps); + AddFixedItemToPool(RG_SHADOW_MEDALLION, 1, rewardIceTraps); + AddFixedItemToPool(RG_LIGHT_MEDALLION, 1, rewardIceTraps); + if (ctx->GetOption(RSK_TRIFORCE_HUNT).IsNot(RO_TRIFORCE_HUNT_OFF)) { - ctx->possibleIceTrapModels.push_back(RG_TRIFORCE_PIECE); - AddItemToMainPool(RG_TRIFORCE_PIECE, (ctx->GetOption(RSK_TRIFORCE_HUNT_PIECES_TOTAL).Get() + 1)); + AddFixedItemToPool(RG_TRIFORCE_PIECE, ctx->GetOption(RSK_TRIFORCE_HUNT_PIECES_TOTAL).Get() + 1, false); switch (ctx->GetOption(RSK_TRIFORCE_HUNT).Get()) { case RO_TRIFORCE_HUNT_OFF: break; case RO_TRIFORCE_HUNT_WIN: ctx->PlaceItemInLocation(RC_TRIFORCE_COMPLETED, RG_TRIFORCE); // Win condition - ctx->PlaceItemInLocation(RC_GANON, GetJunkItem(), false, true); + ctx->PlaceItemInLocation(RC_GANON, RG_BLUE_RUPEE, false, true); break; case RO_TRIFORCE_HUNT_GBK: ctx->PlaceItemInLocation(RC_TRIFORCE_COMPLETED, RG_GANONS_CASTLE_BOSS_KEY); @@ -525,37 +334,33 @@ void GenerateItemPool() { // Fixed item locations ctx->PlaceItemInLocation(RC_HC_ZELDAS_LETTER, RG_ZELDAS_LETTER); - if (ctx->GetOption(RSK_SHUFFLE_KOKIRI_SWORD)) { - AddItemToMainPool(RG_KOKIRI_SWORD); - ctx->possibleIceTrapModels.push_back(RG_KOKIRI_SWORD); - } else { - if (!ctx->GetOption(RSK_STARTING_KOKIRI_SWORD)) { + if (!ctx->GetOption(RSK_STARTING_KOKIRI_SWORD)) { + if (ctx->GetOption(RSK_SHUFFLE_KOKIRI_SWORD)) { + AddItemToPool(RG_KOKIRI_SWORD, 2, 1, 1, 1); + } else { ctx->PlaceItemInLocation(RC_KF_KOKIRI_SWORD_CHEST, RG_KOKIRI_SWORD, false, true); } } - if (ctx->GetOption(RSK_SHUFFLE_MASTER_SWORD)) { - AddItemToMainPool(RG_MASTER_SWORD); - ctx->possibleIceTrapModels.push_back(RG_MASTER_SWORD); - } else { - if (!ctx->GetOption(RSK_STARTING_MASTER_SWORD)) { + if (!ctx->GetOption(RSK_STARTING_MASTER_SWORD)) { + if (ctx->GetOption(RSK_SHUFFLE_MASTER_SWORD)) { + AddItemToPool(RG_MASTER_SWORD, 2, 1, 1, 1); + } else { ctx->PlaceItemInLocation(RC_TOT_MASTER_SWORD, RG_MASTER_SWORD, false, true); } } if (ctx->GetOption(RSK_SHUFFLE_WEIRD_EGG)) { - AddItemToMainPool(RG_WEIRD_EGG); - ctx->possibleIceTrapModels.push_back(RG_WEIRD_EGG); + AddItemToPool(RG_WEIRD_EGG, 2, 1, 1, 1); } else { ctx->PlaceItemInLocation(RC_HC_MALON_EGG, RG_WEIRD_EGG, false, true); } if (ctx->GetOption(RSK_SHUFFLE_OCARINA)) { - AddItemToMainPool(RG_PROGRESSIVE_OCARINA, 2); - if (ctx->GetOption(RSK_ITEM_POOL).Is(RO_ITEM_POOL_PLENTIFUL)) { - AddItemToPool(PendingJunkPool, RG_PROGRESSIVE_OCARINA); + if (ctx->GetOption(RSK_STARTING_OCARINA).IsNot(RO_STARTING_OCARINA_TIME)) { + int baseOcarinas = ctx->GetOption(RSK_STARTING_OCARINA).Is(RO_STARTING_OCARINA_OFF) ? 2 : 1; + AddItemToPool(RG_PROGRESSIVE_OCARINA, baseOcarinas + 1, baseOcarinas, baseOcarinas, baseOcarinas); } - ctx->possibleIceTrapModels.push_back(RG_PROGRESSIVE_OCARINA); } else { if (ctx->GetOption(RSK_STARTING_OCARINA).Is(RO_STARTING_OCARINA_OFF)) { ctx->PlaceItemInLocation(RC_LW_GIFT_FROM_SARIA, RG_PROGRESSIVE_OCARINA, false, true); @@ -568,31 +373,22 @@ void GenerateItemPool() { } if (ctx->GetOption(RSK_SHUFFLE_OCARINA_BUTTONS)) { - AddItemToMainPool(RG_OCARINA_A_BUTTON); - AddItemToMainPool(RG_OCARINA_C_UP_BUTTON); - AddItemToMainPool(RG_OCARINA_C_DOWN_BUTTON); - AddItemToMainPool(RG_OCARINA_C_LEFT_BUTTON); - AddItemToMainPool(RG_OCARINA_C_RIGHT_BUTTON); - - ctx->possibleIceTrapModels.push_back(RG_OCARINA_A_BUTTON); - ctx->possibleIceTrapModels.push_back(RG_OCARINA_C_UP_BUTTON); - ctx->possibleIceTrapModels.push_back(RG_OCARINA_C_DOWN_BUTTON); - ctx->possibleIceTrapModels.push_back(RG_OCARINA_C_LEFT_BUTTON); - ctx->possibleIceTrapModels.push_back(RG_OCARINA_C_RIGHT_BUTTON); + AddItemToPool(RG_OCARINA_A_BUTTON, 2, 1, 1, 1); + AddItemToPool(RG_OCARINA_C_UP_BUTTON, 2, 1, 1, 1); + AddItemToPool(RG_OCARINA_C_DOWN_BUTTON, 2, 1, 1, 1); + AddItemToPool(RG_OCARINA_C_LEFT_BUTTON, 2, 1, 1, 1); + AddItemToPool(RG_OCARINA_C_RIGHT_BUTTON, 2, 1, 1, 1); } if (ctx->GetOption(RSK_SKELETON_KEY)) { - AddItemToMainPool(RG_SKELETON_KEY); + AddFixedItemToPool(RG_SKELETON_KEY, 1); } - if (ctx->GetOption(RSK_SHUFFLE_SWIM)) { - AddItemToMainPool(RG_PROGRESSIVE_SCALE); - } + int bronzeScale = ctx->GetOption(RSK_SHUFFLE_SWIM) ? 1 : 0; + AddItemToPool(RG_PROGRESSIVE_SCALE, 3 + bronzeScale, 2 + bronzeScale, 2 + bronzeScale, 2 + bronzeScale); if (ctx->GetOption(RSK_SHUFFLE_BEEHIVES)) { - // 32 total beehive locations - AddItemToPool(PendingJunkPool, RG_RED_RUPEE, 23); - AddItemToPool(PendingJunkPool, RG_BLUE_RUPEE, 9); + PlaceItemsForType(RCTYPE_BEEHIVE, true, true); } // Shuffle Pots @@ -619,81 +415,36 @@ void GenerateItemPool() { bool dungeonCratesActive = ctx->GetOption(RSK_SHUFFLE_CRATES).Is(RO_SHUFFLE_CRATES_DUNGEONS) || ctx->GetOption(RSK_SHUFFLE_CRATES).Is(RO_SHUFFLE_CRATES_ALL); PlaceItemsForType(RCTYPE_CRATE, overworldCratesActive, dungeonCratesActive); + PlaceItemsForType(RCTYPE_NLCRATE, ctx->GetOption(RSK_LOGIC_RULES).Is(RO_LOGIC_NO_LOGIC) && overworldCratesActive, + ctx->GetOption(RSK_LOGIC_RULES).Is(RO_LOGIC_NO_LOGIC) && dungeonCratesActive); PlaceItemsForType(RCTYPE_SMALL_CRATE, overworldCratesActive, dungeonCratesActive); - if (ctx->GetOption(RSK_LOGIC_RULES).Is(RO_LOGIC_NO_LOGIC)) { - PlaceItemsForType(RCTYPE_NLCRATE, overworldCratesActive, dungeonCratesActive); - } - auto fsMode = ctx->GetOption(RSK_FISHSANITY); - if (fsMode.IsNot(RO_FISHSANITY_OFF)) { - if (fsMode.Is(RO_FISHSANITY_POND) || fsMode.Is(RO_FISHSANITY_BOTH)) { - // 17 max child pond fish - uint8_t pondCt = ctx->GetOption(RSK_FISHSANITY_POND_COUNT).Get(); - for (uint8_t i = 0; i < pondCt; i++) { - AddItemToMainPool(GetJunkItem()); - } - - if (ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT)) { - // 16 max adult pond fish, have to reduce to 16 if every fish is enabled - if (pondCt > 16) - pondCt = 16; - for (uint8_t i = 0; i < pondCt; i++) { - AddItemToMainPool(GetJunkItem()); - } - } - } - // 9 grotto fish, 5 zora's domain fish - if (fsMode.Is(RO_FISHSANITY_OVERWORLD) || fsMode.Is(RO_FISHSANITY_BOTH)) { - for (uint8_t i = 0; i < Rando::StaticData::GetOverworldFishLocations().size(); i++) - AddItemToMainPool(GetJunkItem()); - } - - if (fsMode.Is(RO_FISHSANITY_HYRULE_LOACH)) { - AddItemToMainPool(RG_PURPLE_RUPEE); - } else { - ctx->PlaceItemInLocation(RC_LH_HYRULE_LOACH, RG_PURPLE_RUPEE, false, true); - } + if (ctx->GetOption(RSK_FISHSANITY).Is(RO_FISHSANITY_HYRULE_LOACH)) { + AddFixedItemToPool(RG_PURPLE_RUPEE, 1); + } else { + ctx->PlaceItemInLocation(RC_LH_HYRULE_LOACH, RG_PURPLE_RUPEE, false, true); } if (ctx->GetOption(RSK_SHUFFLE_FISHING_POLE)) { - AddItemToMainPool(RG_FISHING_POLE); - ctx->possibleIceTrapModels.push_back(RG_FISHING_POLE); + AddItemToPool(RG_FISHING_POLE, 2, 1, 1, 1); } - - if (ctx->GetOption(RSK_INFINITE_UPGRADES).Is(RO_INF_UPGRADES_PROGRESSIVE)) { - AddItemToMainPool(RG_PROGRESSIVE_BOMB_BAG); - AddItemToMainPool(RG_PROGRESSIVE_BOW); - AddItemToMainPool(RG_PROGRESSIVE_NUT_UPGRADE); - AddItemToMainPool(RG_PROGRESSIVE_SLINGSHOT); - AddItemToMainPool(RG_PROGRESSIVE_STICK_UPGRADE); - AddItemToMainPool(RG_PROGRESSIVE_MAGIC_METER); - AddItemToMainPool(RG_PROGRESSIVE_WALLET); - if (ctx->GetOption(RSK_BOMBCHU_BAG).Is(RO_BOMBCHU_BAG_PROGRESSIVE)) { - AddItemToMainPool(RG_PROGRESSIVE_BOMBCHU_BAG); - } - } - // if beans unshuffled, put on bean guy, otherwise if not starting with beans, add to pool if (ctx->GetOption(RSK_SHUFFLE_MERCHANTS).IsNot(RO_SHUFFLE_MERCHANTS_BEANS_ONLY) && ctx->GetOption(RSK_SHUFFLE_MERCHANTS).IsNot(RO_SHUFFLE_MERCHANTS_ALL)) { ctx->PlaceItemInLocation(RC_ZR_MAGIC_BEAN_SALESMAN, RG_MAGIC_BEAN, false, true); } else if (!ctx->GetOption(RSK_STARTING_BEANS)) { - AddItemToMainPool(RG_MAGIC_BEAN_PACK); - if (ctx->GetOption(RSK_ITEM_POOL).Is(RO_ITEM_POOL_PLENTIFUL)) { - AddItemToPool(PendingJunkPool, RG_MAGIC_BEAN_PACK); - } - ctx->possibleIceTrapModels.push_back(RG_MAGIC_BEAN_PACK); + AddItemToPool(RG_MAGIC_BEAN_PACK, 2, 1, 1, 1); } 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) { - AddItemToMainPool(RG_GIANTS_KNIFE); + AddFixedItemToPool(RG_GIANTS_KNIFE, 1); } if (ctx->GetOption(RSK_BOMBCHU_BAG).Is(RO_BOMBCHU_BAG_SINGLE)) { - AddItemToMainPool(RG_PROGRESSIVE_BOMBCHU_BAG); + AddFixedItemToPool(RG_PROGRESSIVE_BOMBCHU_BAG, 1); } else if (ctx->GetOption(RSK_BOMBCHU_BAG).Is(RO_BOMBCHU_BAG_NONE)) { - AddItemToMainPool(RG_BOMBCHU_10); + AddFixedItemToPool(RG_BOMBCHU_10, 1); } } else { ctx->PlaceItemInLocation(RC_KAK_GRANNYS_SHOP, RG_BLUE_POTION_REFILL, false, true); @@ -702,7 +453,7 @@ void GenerateItemPool() { } if (ctx->GetOption(RSK_SHUFFLE_FROG_SONG_RUPEES)) { - AddItemToMainPool(RG_PURPLE_RUPEE, 5); + AddFixedItemToPool(RG_PURPLE_RUPEE, 5); } else { ctx->PlaceItemInLocation(RC_ZR_FROGS_ZELDAS_LULLABY, RG_PURPLE_RUPEE, false, true); ctx->PlaceItemInLocation(RC_ZR_FROGS_EPONAS_SONG, RG_PURPLE_RUPEE, false, true); @@ -712,24 +463,25 @@ void GenerateItemPool() { } if (ctx->GetOption(RSK_SHUFFLE_ADULT_TRADE)) { - AddItemToMainPool(RG_POCKET_EGG); - AddItemToMainPool(RG_COJIRO); - AddItemToMainPool(RG_ODD_MUSHROOM); - AddItemToMainPool(RG_ODD_POTION); - AddItemToMainPool(RG_POACHERS_SAW); - AddItemToMainPool(RG_BROKEN_SWORD); - AddItemToMainPool(RG_PRESCRIPTION); - AddItemToMainPool(RG_EYEBALL_FROG); - AddItemToMainPool(RG_EYEDROPS); + AddItemToPool(RG_POCKET_EGG, 2, 1, 1, 1); + AddItemToPool(RG_COJIRO, 2, 1, 1, 1); + AddItemToPool(RG_ODD_MUSHROOM, 2, 1, 1, 1); + AddItemToPool(RG_ODD_POTION, 2, 1, 1, 1); + AddItemToPool(RG_POACHERS_SAW, 2, 1, 1, 1); + AddItemToPool(RG_BROKEN_SWORD, 2, 1, 1, 1); + AddItemToPool(RG_PRESCRIPTION, 2, 1, 1, 1); + AddItemToPool(RG_EYEBALL_FROG, 2, 1, 1, 1); + AddItemToPool(RG_EYEDROPS, 2, 1, 1, 1); } - AddItemToMainPool(RG_CLAIM_CHECK); + AddItemToPool(RG_CLAIM_CHECK, 2, 1, 1, 1); if (ctx->GetOption(RSK_SHUFFLE_CHEST_MINIGAME).Is(RO_CHEST_GAME_SINGLE_KEYS)) { - AddItemToMainPool(RG_TREASURE_GAME_SMALL_KEY, 6); // 6 individual keys + AddItemToPool(RG_TREASURE_GAME_SMALL_KEY, 7, 6, 6, 6); } else if (ctx->GetOption(RSK_SHUFFLE_CHEST_MINIGAME).Is(RO_CHEST_GAME_PACK)) { - AddItemToMainPool(RG_TREASURE_GAME_SMALL_KEY); // 1 key which will behave as a pack of 6 + AddItemToPool(RG_TREASURE_GAME_KEY_RING, 2, 1, 1, 1); } + int tokensToAdd = 0; if (ctx->GetOption(RSK_SHUFFLE_TOKENS).Is(RO_TOKENSANITY_OFF)) { for (RandomizerCheck loc : ctx->GetLocations(ctx->allLocations, RCTYPE_SKULL_TOKEN)) { ctx->PlaceItemInLocation(loc, RG_GOLD_SKULLTULA_TOKEN, false, true); @@ -739,7 +491,7 @@ void GenerateItemPool() { if (Rando::StaticData::GetLocation(loc)->IsOverworld()) { ctx->PlaceItemInLocation((RandomizerCheck)loc, RG_GOLD_SKULLTULA_TOKEN, false, true); } else { - AddItemToMainPool(RG_GOLD_SKULLTULA_TOKEN); + tokensToAdd++; } } } else if (ctx->GetOption(RSK_SHUFFLE_TOKENS).Is(RO_TOKENSANITY_OVERWORLD)) { @@ -747,130 +499,78 @@ void GenerateItemPool() { if (Rando::StaticData::GetLocation(loc)->IsDungeon()) { ctx->PlaceItemInLocation((RandomizerCheck)loc, RG_GOLD_SKULLTULA_TOKEN, false, true); } else { - AddItemToMainPool(RG_GOLD_SKULLTULA_TOKEN); + tokensToAdd++; } } } else { - AddItemToMainPool(RG_GOLD_SKULLTULA_TOKEN, 100); + tokensToAdd = 100; } if (ctx->GetOption(RSK_SHUFFLE_100_GS_REWARD)) { - if (ctx->GetOption(RSK_SHUFFLE_TOKENS).IsNot(RO_TOKENSANITY_OFF) && - ctx->GetOption(RSK_ITEM_POOL).Is(RO_ITEM_POOL_PLENTIFUL)) { - AddItemToPool(PendingJunkPool, RG_GOLD_SKULLTULA_TOKEN, 10); - } - AddItemToMainPool(RG_HUGE_RUPEE); + AddFixedItemToPool(RG_HUGE_RUPEE, 1); } else { ctx->PlaceItemInLocation(RC_KAK_100_GOLD_SKULLTULA_REWARD, RG_HUGE_RUPEE, false, true); } if (ctx->GetOption(RSK_SHUFFLE_BEAN_SOULS)) { - AddItemToMainPool(RG_DEATH_MOUNTAIN_CRATER_BEAN_SOUL); - AddItemToMainPool(RG_DEATH_MOUNTAIN_TRAIL_BEAN_SOUL); - AddItemToMainPool(RG_DESERT_COLOSSUS_BEAN_SOUL); - AddItemToMainPool(RG_GERUDO_VALLEY_BEAN_SOUL); - AddItemToMainPool(RG_GRAVEYARD_BEAN_SOUL); - AddItemToMainPool(RG_KOKIRI_FOREST_BEAN_SOUL); - AddItemToMainPool(RG_LAKE_HYLIA_BEAN_SOUL); - AddItemToMainPool(RG_LOST_WOODS_BRIDGE_BEAN_SOUL); - AddItemToMainPool(RG_LOST_WOODS_BEAN_SOUL); - AddItemToMainPool(RG_ZORAS_RIVER_BEAN_SOUL); + ctx->possibleIceTrapModels.insert(RG_DEATH_MOUNTAIN_CRATER_BEAN_SOUL); // ice traps reroll this into a random bean soul + AddItemToPool(RG_DEATH_MOUNTAIN_CRATER_BEAN_SOUL, 2, 1, 1, 1, false); + AddItemToPool(RG_DEATH_MOUNTAIN_TRAIL_BEAN_SOUL, 2, 1, 1, 1, false); + AddItemToPool(RG_DESERT_COLOSSUS_BEAN_SOUL, 2, 1, 1, 1, false); + AddItemToPool(RG_GERUDO_VALLEY_BEAN_SOUL, 2, 1, 1, 1, false); + AddItemToPool(RG_GRAVEYARD_BEAN_SOUL, 2, 1, 1, 1, false); + AddItemToPool(RG_KOKIRI_FOREST_BEAN_SOUL, 2, 1, 1, 1, false); + AddItemToPool(RG_LAKE_HYLIA_BEAN_SOUL, 2, 1, 1, 1, false); + AddItemToPool(RG_LOST_WOODS_BRIDGE_BEAN_SOUL, 2, 1, 1, 1, false); + AddItemToPool(RG_LOST_WOODS_BEAN_SOUL, 2, 1, 1, 1, false); + AddItemToPool(RG_ZORAS_RIVER_BEAN_SOUL, 2, 1, 1, 1, false); + } + + if (ctx->GetOption(RSK_SHUFFLE_TOKENS).IsNot(RO_TOKENSANITY_OFF) && + ctx->GetOption(RSK_ITEM_POOL).Is(RO_ITEM_POOL_PLENTIFUL)) { + tokensToAdd += 10; + } + + if (ctx->GetOption(RSK_STARTING_SKULLTULA_TOKEN).Get() < tokensToAdd) { + AddFixedItemToPool(RG_GOLD_SKULLTULA_TOKEN, tokensToAdd - ctx->GetOption(RSK_STARTING_SKULLTULA_TOKEN).Get()); } if (ctx->GetOption(RSK_SHUFFLE_BOSS_SOULS)) { - AddItemToMainPool(RG_GOHMA_SOUL); - AddItemToMainPool(RG_KING_DODONGO_SOUL); - AddItemToMainPool(RG_BARINADE_SOUL); - AddItemToMainPool(RG_PHANTOM_GANON_SOUL); - AddItemToMainPool(RG_VOLVAGIA_SOUL); - AddItemToMainPool(RG_MORPHA_SOUL); - AddItemToMainPool(RG_BONGO_BONGO_SOUL); - AddItemToMainPool(RG_TWINROVA_SOUL); - - ctx->possibleIceTrapModels.push_back(RG_GOHMA_SOUL); - ctx->possibleIceTrapModels.push_back(RG_KING_DODONGO_SOUL); - ctx->possibleIceTrapModels.push_back(RG_BARINADE_SOUL); - ctx->possibleIceTrapModels.push_back(RG_PHANTOM_GANON_SOUL); - ctx->possibleIceTrapModels.push_back(RG_VOLVAGIA_SOUL); - ctx->possibleIceTrapModels.push_back(RG_MORPHA_SOUL); - ctx->possibleIceTrapModels.push_back(RG_BONGO_BONGO_SOUL); - ctx->possibleIceTrapModels.push_back(RG_TWINROVA_SOUL); + AddItemToPool(RG_GOHMA_SOUL, 2, 1, 1, 1); + AddItemToPool(RG_KING_DODONGO_SOUL, 2, 1, 1, 1); + AddItemToPool(RG_BARINADE_SOUL, 2, 1, 1, 1); + AddItemToPool(RG_PHANTOM_GANON_SOUL, 2, 1, 1, 1); + AddItemToPool(RG_VOLVAGIA_SOUL, 2, 1, 1, 1); + AddItemToPool(RG_MORPHA_SOUL, 2, 1, 1, 1); + AddItemToPool(RG_BONGO_BONGO_SOUL, 2, 1, 1, 1); + AddItemToPool(RG_TWINROVA_SOUL, 2, 1, 1, 1); if (ctx->GetOption(RSK_SHUFFLE_BOSS_SOULS).Is(RO_BOSS_SOULS_ON_PLUS_GANON)) { - AddItemToMainPool(RG_GANON_SOUL); - ctx->possibleIceTrapModels.push_back(RG_GANON_SOUL); + AddItemToPool(RG_GANON_SOUL, 2, 1, 1, 1); } } - if (ctx->GetOption(RSK_SHUFFLE_CHILD_WALLET)) { - AddItemToMainPool(RG_PROGRESSIVE_WALLET); - } - - if (ctx->GetOption(RSK_INCLUDE_TYCOON_WALLET)) { - AddItemToMainPool(RG_PROGRESSIVE_WALLET); - } - - if (ctx->GetOption(RSK_SHUFFLE_DEKU_STICK_BAG)) { - AddItemToMainPool(RG_PROGRESSIVE_STICK_UPGRADE); - } - - if (ctx->GetOption(RSK_SHUFFLE_DEKU_NUT_BAG)) { - AddItemToMainPool(RG_PROGRESSIVE_NUT_UPGRADE); - } - - if (ctx->GetOption(RSK_BOMBCHU_BAG).Is(RO_BOMBCHU_BAG_SINGLE)) { - AddItemToMainPool(RG_PROGRESSIVE_BOMBCHU_BAG, 5); - } else if (ctx->GetOption(RSK_BOMBCHU_BAG).Is(RO_BOMBCHU_BAG_PROGRESSIVE)) { - AddItemToMainPool(RG_PROGRESSIVE_BOMBCHU_BAG, 3); - if (ctx->GetOption(RSK_ITEM_POOL).Is(RO_ITEM_POOL_PLENTIFUL)) { - AddItemToPool(PendingJunkPool, RG_PROGRESSIVE_BOMBCHU_BAG); - } - } else { - AddItemToMainPool(RG_BOMBCHU_5); - AddItemToMainPool(RG_BOMBCHU_10, 3); - AddItemToMainPool(RG_BOMBCHU_20); - } - - // Ice Traps - AddItemToMainPool(RG_ICE_TRAP); - if (ctx->GetDungeon(Rando::GERUDO_TRAINING_GROUND)->IsVanilla()) { - AddItemToMainPool(RG_ICE_TRAP); - } - if (ctx->GetDungeon(Rando::GANONS_CASTLE)->IsVanilla()) { - AddItemToMainPool(RG_ICE_TRAP, 4); - } - // Gerudo Fortress if (ctx->GetOption(RSK_GERUDO_FORTRESS).Is(RO_GF_CARPENTERS_FREE)) { ctx->PlaceItemInLocation(RC_TH_1_TORCH_CARPENTER, RG_RECOVERY_HEART, false, true); ctx->PlaceItemInLocation(RC_TH_DEAD_END_CARPENTER, RG_RECOVERY_HEART, false, true); ctx->PlaceItemInLocation(RC_TH_DOUBLE_CELL_CARPENTER, RG_RECOVERY_HEART, false, true); ctx->PlaceItemInLocation(RC_TH_STEEP_SLOPE_CARPENTER, RG_RECOVERY_HEART, false, true); + } else if (ctx->GetOption(RSK_GERUDO_KEYS).IsNot(RO_GERUDO_KEYS_VANILLA)) { if (ctx->GetOption(RSK_GERUDO_FORTRESS).Is(RO_GF_CARPENTERS_FAST)) { - AddItemToMainPool(RG_GERUDO_FORTRESS_SMALL_KEY); + AddItemToPool(RG_GERUDO_FORTRESS_SMALL_KEY, 2, 1, 1, 1); ctx->PlaceItemInLocation(RC_TH_DEAD_END_CARPENTER, RG_RECOVERY_HEART, false, true); ctx->PlaceItemInLocation(RC_TH_DOUBLE_CELL_CARPENTER, RG_RECOVERY_HEART, false, true); ctx->PlaceItemInLocation(RC_TH_STEEP_SLOPE_CARPENTER, RG_RECOVERY_HEART, false, true); } else { // Only add key ring if 4 Fortress keys necessary if (ctx->GetOption(RSK_KEYRINGS_GERUDO_FORTRESS) && ctx->GetOption(RSK_KEYRINGS)) { - AddItemToMainPool(RG_GERUDO_FORTRESS_KEY_RING); - // Add junk to make up for missing keys - for (uint8_t i = 0; i < 3; i++) { - AddItemToMainPool(GetJunkItem()); - } + AddItemToPool(RG_GERUDO_FORTRESS_KEY_RING, 2, 1, 1, 1); } else { - AddItemToMainPool(RG_GERUDO_FORTRESS_SMALL_KEY, 4); - } - } - if (ctx->GetOption(RSK_ITEM_POOL).Is(RO_ITEM_POOL_PLENTIFUL)) { - if (ctx->GetOption(RSK_KEYRINGS_GERUDO_FORTRESS) && - ctx->GetOption(RSK_GERUDO_FORTRESS).Is(RO_GF_CARPENTERS_NORMAL) && ctx->GetOption(RSK_KEYRINGS)) { - AddItemToPool(PendingJunkPool, RG_GERUDO_FORTRESS_KEY_RING); - } else { - AddItemToPool(PendingJunkPool, RG_GERUDO_FORTRESS_SMALL_KEY); + AddItemToPool(RG_GERUDO_FORTRESS_SMALL_KEY, 5, 4, 4, 4); } } + } else { if (ctx->GetOption(RSK_GERUDO_FORTRESS).Is(RO_GF_CARPENTERS_FAST)) { ctx->PlaceItemInLocation(RC_TH_1_TORCH_CARPENTER, RG_GERUDO_FORTRESS_SMALL_KEY, false, true); @@ -886,230 +586,95 @@ void GenerateItemPool() { } // Gerudo Membership Card - if (ctx->GetOption(RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD) && - ctx->GetOption(RSK_GERUDO_FORTRESS).IsNot(RO_GF_CARPENTERS_FREE)) { - AddItemToMainPool(RG_GERUDO_MEMBERSHIP_CARD); - ctx->possibleIceTrapModels.push_back(RG_GERUDO_MEMBERSHIP_CARD); - } else if (ctx->GetOption(RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD)) { - AddItemToPool(ItemPool, RG_GERUDO_MEMBERSHIP_CARD); - ctx->PlaceItemInLocation(RC_TH_FREED_CARPENTERS, RG_ICE_TRAP, false, true); + if (ctx->GetOption(RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD)) { + AddItemToPool(RG_GERUDO_MEMBERSHIP_CARD, 2, 1, 1, 1); + if (ctx->GetOption(RSK_GERUDO_FORTRESS).IsNot(RO_GF_CARPENTERS_FREE)) { + ctx->PlaceItemInLocation(RC_TH_FREED_CARPENTERS, RG_BLUE_RUPEE, false, true); + } } else { ctx->PlaceItemInLocation(RC_TH_FREED_CARPENTERS, RG_GERUDO_MEMBERSHIP_CARD, false, true); } // Keys + if (ctx->GetOption(RSK_LOCK_OVERWORLD_DOORS)) { + // only 1 is added to the ice trap pool, to avoid the pool being filled with them. + // a random one is chosen in CreateItemOverrides + AddItemToPool(RG_GUARD_HOUSE_KEY, 2, 1, 1, 1); + AddItemToPool(RG_MARKET_BAZAAR_KEY, 2, 1, 1, 1, false); + AddItemToPool(RG_MARKET_POTION_SHOP_KEY, 2, 1, 1, 1, false); + AddItemToPool(RG_MASK_SHOP_KEY, 2, 1, 1, 1, false); + AddItemToPool(RG_MARKET_SHOOTING_GALLERY_KEY, 2, 1, 1, 1, false); + AddItemToPool(RG_BOMBCHU_BOWLING_KEY, 2, 1, 1, 1, false); + AddItemToPool(RG_TREASURE_CHEST_GAME_BUILDING_KEY, 2, 1, 1, 1, false); + AddItemToPool(RG_BOMBCHU_SHOP_KEY, 2, 1, 1, 1, false); + AddItemToPool(RG_RICHARDS_HOUSE_KEY, 2, 1, 1, 1, false); + AddItemToPool(RG_ALLEY_HOUSE_KEY, 2, 1, 1, 1, false); + AddItemToPool(RG_KAK_BAZAAR_KEY, 2, 1, 1, 1, false); + AddItemToPool(RG_KAK_POTION_SHOP_KEY, 2, 1, 1, 1, false); + AddItemToPool(RG_BOSS_HOUSE_KEY, 2, 1, 1, 1, false); + AddItemToPool(RG_GRANNYS_POTION_SHOP_KEY, 2, 1, 1, 1, false); + AddItemToPool(RG_SKULLTULA_HOUSE_KEY, 2, 1, 1, 1, false); + AddItemToPool(RG_IMPAS_HOUSE_KEY, 2, 1, 1, 1, false); + AddItemToPool(RG_WINDMILL_KEY, 2, 1, 1, 1, false); + AddItemToPool(RG_KAK_SHOOTING_GALLERY_KEY, 2, 1, 1, 1, false); + AddItemToPool(RG_DAMPES_HUT_KEY, 2, 1, 1, 1, false); + AddItemToPool(RG_TALONS_HOUSE_KEY, 2, 1, 1, 1, false); + AddItemToPool(RG_STABLES_KEY, 2, 1, 1, 1, false); + AddItemToPool(RG_BACK_TOWER_KEY, 2, 1, 1, 1, false); + AddItemToPool(RG_HYLIA_LAB_KEY, 2, 1, 1, 1, false); + AddItemToPool(RG_FISHING_HOLE_KEY, 2, 1, 1, 1, false); + } - // For key rings, need to add as many junk items as "missing" keys - if (ctx->GetOption(RSK_KEYRINGS).IsNot(RO_KEYRINGS_OFF)) { - uint8_t ringJunkAmt = 0; + if (ctx->GetOption(RSK_KEYSANITY).Is(RO_DUNGEON_ITEM_LOC_VANILLA)) { + PlaceVanillaSmallKeys(); + } else if (ctx->GetOption(RSK_KEYSANITY).IsNot(RO_DUNGEON_ITEM_LOC_STARTWITH)) { for (auto dungeon : ctx->GetDungeons()->GetDungeonList()) { if (dungeon->HasKeyRing()) { - ringJunkAmt += dungeon->GetSmallKeyCount() - 1; + AddItemToPool(dungeon->GetKeyRing(), 2, 1, 1, 1); + } else if (dungeon->GetSmallKeyCount() > 0) { + int smallKeys = dungeon->GetSmallKeyCount(); + AddItemToPool(dungeon->GetSmallKey(), smallKeys + 1, smallKeys, smallKeys, smallKeys); } } - for (uint8_t i = 0; i < ringJunkAmt; i++) { - AddItemToMainPool(GetJunkItem()); - } } - if (ctx->GetOption(RSK_ITEM_POOL).Is(RO_ITEM_POOL_PLENTIFUL)) { - if (ctx->GetOption(RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD)) { - AddItemToPool(PendingJunkPool, RG_GERUDO_MEMBERSHIP_CARD); - } - - if (ctx->GetOption(RSK_SHUFFLE_FISHING_POLE)) { - AddItemToPool(PendingJunkPool, RG_FISHING_POLE); - } - - // Plentiful small keys - if (ctx->GetOption(RSK_KEYSANITY).Is(RO_DUNGEON_ITEM_LOC_ANYWHERE) || - ctx->GetOption(RSK_KEYSANITY).Is(RO_DUNGEON_ITEM_LOC_ANY_DUNGEON) || - ctx->GetOption(RSK_KEYSANITY).Is(RO_DUNGEON_ITEM_LOC_OVERWORLD)) { - if (ctx->GetDungeon(Rando::BOTTOM_OF_THE_WELL)->HasKeyRing()) { - AddItemToPool(PendingJunkPool, RG_BOTTOM_OF_THE_WELL_KEY_RING); - } else { - AddItemToPool(PendingJunkPool, RG_BOTTOM_OF_THE_WELL_SMALL_KEY); - } - if (ctx->GetDungeon(Rando::FOREST_TEMPLE)->HasKeyRing()) { - AddItemToPool(PendingJunkPool, RG_FOREST_TEMPLE_KEY_RING); - } else { - AddItemToPool(PendingJunkPool, RG_FOREST_TEMPLE_SMALL_KEY); - } - if (ctx->GetDungeon(Rando::FIRE_TEMPLE)->HasKeyRing()) { - AddItemToPool(PendingJunkPool, RG_FIRE_TEMPLE_KEY_RING); - } else { - AddItemToPool(PendingJunkPool, RG_FIRE_TEMPLE_SMALL_KEY); - } - if (ctx->GetDungeon(Rando::WATER_TEMPLE)->HasKeyRing()) { - AddItemToPool(PendingJunkPool, RG_WATER_TEMPLE_KEY_RING); - } else { - AddItemToPool(PendingJunkPool, RG_WATER_TEMPLE_SMALL_KEY); - } - if (ctx->GetDungeon(Rando::SPIRIT_TEMPLE)->HasKeyRing()) { - AddItemToPool(PendingJunkPool, RG_SPIRIT_TEMPLE_KEY_RING); - } else { - AddItemToPool(PendingJunkPool, RG_SPIRIT_TEMPLE_SMALL_KEY); - } - if (ctx->GetDungeon(Rando::SHADOW_TEMPLE)->HasKeyRing()) { - AddItemToPool(PendingJunkPool, RG_SHADOW_TEMPLE_KEY_RING); - } else { - AddItemToPool(PendingJunkPool, RG_SHADOW_TEMPLE_SMALL_KEY); - } - if (ctx->GetDungeon(Rando::GERUDO_TRAINING_GROUND)->HasKeyRing()) { - AddItemToPool(PendingJunkPool, RG_GERUDO_TRAINING_GROUND_KEY_RING); - } else { - AddItemToPool(PendingJunkPool, RG_GERUDO_TRAINING_GROUND_SMALL_KEY); - } - if (ctx->GetDungeon(Rando::GANONS_CASTLE)->HasKeyRing()) { - AddItemToPool(PendingJunkPool, RG_GANONS_CASTLE_KEY_RING); - } else { - AddItemToPool(PendingJunkPool, RG_GANONS_CASTLE_SMALL_KEY); - } - } - - if (ctx->GetOption(RSK_BOSS_KEYSANITY).Is(RO_DUNGEON_ITEM_LOC_ANYWHERE) || - ctx->GetOption(RSK_BOSS_KEYSANITY).Is(RO_DUNGEON_ITEM_LOC_ANY_DUNGEON) || - ctx->GetOption(RSK_BOSS_KEYSANITY).Is(RO_DUNGEON_ITEM_LOC_OVERWORLD)) { - AddItemToPool(PendingJunkPool, RG_FOREST_TEMPLE_BOSS_KEY); - AddItemToPool(PendingJunkPool, RG_FIRE_TEMPLE_BOSS_KEY); - AddItemToPool(PendingJunkPool, RG_WATER_TEMPLE_BOSS_KEY); - AddItemToPool(PendingJunkPool, RG_SPIRIT_TEMPLE_BOSS_KEY); - AddItemToPool(PendingJunkPool, RG_SHADOW_TEMPLE_BOSS_KEY); - } - - if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_ANYWHERE) || - ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_ANY_DUNGEON) || - ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_OVERWORLD)) { - AddItemToPool(PendingJunkPool, RG_GANONS_CASTLE_BOSS_KEY); - } + if (ctx->GetOption(RSK_BOSS_KEYSANITY).Is(RO_DUNGEON_ITEM_LOC_VANILLA)) { + PlaceVanillaBossKeys(); + } else if (ctx->GetOption(RSK_BOSS_KEYSANITY).IsNot(RO_DUNGEON_ITEM_LOC_STARTWITH)) { + AddItemToPool(RG_FOREST_TEMPLE_BOSS_KEY, 2, 1, 1, 1); + AddItemToPool(RG_FIRE_TEMPLE_BOSS_KEY, 2, 1, 1, 1); + AddItemToPool(RG_WATER_TEMPLE_BOSS_KEY, 2, 1, 1, 1); + AddItemToPool(RG_SPIRIT_TEMPLE_BOSS_KEY, 2, 1, 1, 1); + AddItemToPool(RG_SHADOW_TEMPLE_BOSS_KEY, 2, 1, 1, 1); } - if (ctx->GetOption(RSK_LOCK_OVERWORLD_DOORS)) { - AddItemToPool(ItemPool, RG_GUARD_HOUSE_KEY); - AddItemToPool(ItemPool, RG_MARKET_BAZAAR_KEY); - AddItemToPool(ItemPool, RG_MARKET_POTION_SHOP_KEY); - AddItemToPool(ItemPool, RG_MASK_SHOP_KEY); - AddItemToPool(ItemPool, RG_MARKET_SHOOTING_GALLERY_KEY); - AddItemToPool(ItemPool, RG_BOMBCHU_BOWLING_KEY); - AddItemToPool(ItemPool, RG_TREASURE_CHEST_GAME_BUILDING_KEY); - AddItemToPool(ItemPool, RG_BOMBCHU_SHOP_KEY); - AddItemToPool(ItemPool, RG_RICHARDS_HOUSE_KEY); - AddItemToPool(ItemPool, RG_ALLEY_HOUSE_KEY); - AddItemToPool(ItemPool, RG_KAK_BAZAAR_KEY); - AddItemToPool(ItemPool, RG_KAK_POTION_SHOP_KEY); - AddItemToPool(ItemPool, RG_BOSS_HOUSE_KEY); - AddItemToPool(ItemPool, RG_GRANNYS_POTION_SHOP_KEY); - AddItemToPool(ItemPool, RG_SKULLTULA_HOUSE_KEY); - AddItemToPool(ItemPool, RG_IMPAS_HOUSE_KEY); - AddItemToPool(ItemPool, RG_WINDMILL_KEY); - AddItemToPool(ItemPool, RG_KAK_SHOOTING_GALLERY_KEY); - AddItemToPool(ItemPool, RG_DAMPES_HUT_KEY); - AddItemToPool(ItemPool, RG_TALONS_HOUSE_KEY); - AddItemToPool(ItemPool, RG_STABLES_KEY); - AddItemToPool(ItemPool, RG_BACK_TOWER_KEY); - AddItemToPool(ItemPool, RG_HYLIA_LAB_KEY); - AddItemToPool(ItemPool, RG_FISHING_HOLE_KEY); + // Don't add GBK to the pool at all for Triforce Hunt or if we start with it. + if (!(ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_STARTWITH) || ctx->GetOption(RSK_TRIFORCE_HUNT))) { + if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_KAK_TOKENS)) { + ctx->PlaceItemInLocation(RC_KAK_100_GOLD_SKULLTULA_REWARD, RG_GANONS_CASTLE_BOSS_KEY); + } else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Get() >= RO_GANON_BOSS_KEY_LACS_VANILLA) { + ctx->PlaceItemInLocation(RC_TOT_LIGHT_ARROWS_CUTSCENE, RG_GANONS_CASTLE_BOSS_KEY); + } else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_VANILLA)) { + ctx->PlaceItemInLocation(RC_GANONS_TOWER_BOSS_KEY_CHEST, RG_GANONS_CASTLE_BOSS_KEY); + } else { + AddItemToPool(RG_GANONS_CASTLE_BOSS_KEY, 2, 1, 1, 1); + } } // Shopsanity if (ctx->GetOption(RSK_SHOPSANITY).Is(RO_SHOPSANITY_OFF) || (ctx->GetOption(RSK_SHOPSANITY).Is(RO_SHOPSANITY_SPECIFIC_COUNT) && ctx->GetOption(RSK_SHOPSANITY_COUNT).Is(RO_SHOPSANITY_COUNT_ZERO_ITEMS))) { - AddItemsToPool(ItemPool, normalRupees); + AddFixedItemToPool(RG_BLUE_RUPEE, 13); + AddFixedItemToPool(RG_RED_RUPEE, 5); + AddFixedItemToPool(RG_PURPLE_RUPEE, 7); + AddFixedItemToPool(RG_HUGE_RUPEE, 3); } else { - AddItemsToPool(ItemPool, shopsanityRupees); // Shopsanity gets extra large rupees - } - - // Shuffle Fountain Fairies - if (ctx->GetOption(RSK_SHUFFLE_FOUNTAIN_FAIRIES)) { - for (auto rc : Rando::StaticData::GetFountainFairyLocations()) { - AddItemToMainPool(GetJunkItem()); - } - // 8 extra for Ganon's Castle - int extra = 8; - for (int i = 0; i < extra; i++) { - AddItemToMainPool(GetJunkItem()); - } - } - - // Shuffle Gossip Stone Fairies - if (ctx->GetOption(RSK_SHUFFLE_STONE_FAIRIES)) { - for (auto rc : Rando::StaticData::GetStoneFairyLocations()) { - AddItemToMainPool(GetJunkItem()); - } - // 2 Dodongo's Cavern Gossip Stone - int extra = 2; - for (int i = 0; i < extra; i++) { - AddItemToMainPool(GetJunkItem()); - } - } - - // Shuffle Bean Fairies - if (ctx->GetOption(RSK_SHUFFLE_BEAN_FAIRIES)) { - for (auto rc : Rando::StaticData::GetBeanFairyLocations()) { - AddItemToMainPool(GetJunkItem()); - } - } - - // Shuffle Song Fairies - if (ctx->GetOption(RSK_SHUFFLE_SONG_FAIRIES)) { - for (auto rc : Rando::StaticData::GetSongFairyLocations()) { - AddItemToMainPool(GetJunkItem()); - } - // 3 Shadow Temple - int extra = 3; - extra += ctx->GetDungeon(Rando::FIRE_TEMPLE)->IsVanilla() ? 0 : 2; - extra += ctx->GetDungeon(Rando::WATER_TEMPLE)->IsVanilla() ? 0 : 3; - extra += ctx->GetDungeon(Rando::SPIRIT_TEMPLE)->IsVanilla() ? 2 : 1; - extra += ctx->GetDungeon(Rando::BOTTOM_OF_THE_WELL)->IsVanilla() ? 1 : 2; - extra += ctx->GetDungeon(Rando::ICE_CAVERN)->IsVanilla() ? 1 : 0; - extra += ctx->GetDungeon(Rando::GERUDO_TRAINING_GROUND)->IsVanilla() ? 1 : 0; - extra += ctx->GetDungeon(Rando::GANONS_CASTLE)->IsVanilla() ? 1 : 0; - for (int i = 0; i < extra; i++) { - AddItemToMainPool(GetJunkItem()); - } - } - - // Scrubsanity - if (ctx->GetOption(RSK_SHUFFLE_SCRUBS).Is(RO_SCRUBS_ALL)) { - // Deku Tree - if (ctx->GetDungeon(Rando::DEKU_TREE)->IsMQ()) { - AddItemToMainPool(RG_DEKU_SHIELD); - } - - // Dodongos Cavern - AddItemToMainPool(RG_DEKU_STICK_1); - AddItemToMainPool(RG_DEKU_SHIELD); - if (ctx->GetDungeon(Rando::DODONGOS_CAVERN)->IsMQ()) { - AddItemToMainPool(RG_RECOVERY_HEART); - } else { - AddItemToMainPool(RG_DEKU_NUTS_5); - } - - // Jabu Jabus Belly - if (ctx->GetDungeon(Rando::JABU_JABUS_BELLY)->IsVanilla()) { - AddItemToMainPool(RG_DEKU_NUTS_5); - } - - // Ganons Castle - AddItemToMainPool(RG_BOMBS_5); - AddItemToMainPool(RG_RECOVERY_HEART); - AddItemToMainPool(RG_BLUE_RUPEE); - if (ctx->GetDungeon(Rando::GANONS_CASTLE)->IsMQ()) { - AddItemToMainPool(RG_DEKU_NUTS_5); - } - - // Overworld Scrubs - AddItemsToPool(ItemPool, dekuScrubItems); - - // Scrubs which sell seeds or arrows sell it based on age, this randomly assigns them - for (uint8_t i = 0; i < 7; i++) { - if (Random(0, 3)) { - AddItemToMainPool(RG_ARROWS_30); - } else { - AddItemToMainPool(RG_DEKU_SEEDS_30); - } - } + // Shopsanity gets extra large rupees + AddFixedItemToPool(RG_BLUE_RUPEE, 2); + AddFixedItemToPool(RG_RED_RUPEE, 10); + AddFixedItemToPool(RG_PURPLE_RUPEE, 10); + AddFixedItemToPool(RG_HUGE_RUPEE, 6); } bool overworldFreeStandingActive = ctx->GetOption(RSK_SHUFFLE_FREESTANDING).Is(RO_SHUFFLE_FREESTANDING_OVERWORLD) || @@ -1118,102 +683,108 @@ void GenerateItemPool() { ctx->GetOption(RSK_SHUFFLE_FREESTANDING).Is(RO_SHUFFLE_FREESTANDING_ALL); PlaceItemsForType(RCTYPE_FREESTANDING, overworldFreeStandingActive, dungeonFreeStandingActive); - AddItemsToPool(ItemPool, alwaysItems); - AddItemsToPool(ItemPool, dungeonRewards); - // Dungeon pools if (ctx->GetDungeon(Rando::DEKU_TREE)->IsMQ()) { - AddItemsToPool(ItemPool, DT_MQ); + AddFixedItemToPool(RG_PURPLE_RUPEE); + if (ctx->GetOption(RSK_SHUFFLE_SCRUBS).Is(RO_SCRUBS_ALL)) { + AddFixedItemToPool(RG_DEKU_SHIELD, 3); + } else { + AddFixedItemToPool(RG_DEKU_SHIELD, 2); + } } else { - AddItemsToPool(ItemPool, DT_Vanilla); + AddFixedItemToPool(RG_RECOVERY_HEART, 2); } if (ctx->GetDungeon(Rando::DODONGOS_CAVERN)->IsMQ()) { - AddItemsToPool(ItemPool, DC_MQ); + AddFixedItemToPool(RG_HYLIAN_SHIELD); + AddFixedItemToPool(RG_BLUE_RUPEE); + if (ctx->GetOption(RSK_SHUFFLE_SCRUBS).Is(RO_SCRUBS_ALL)) { + AddFixedItemToPool(RG_RECOVERY_HEART); + } } else { - AddItemsToPool(ItemPool, DC_Vanilla); + AddFixedItemToPool(RG_RED_RUPEE); + if (ctx->GetOption(RSK_SHUFFLE_SCRUBS).Is(RO_SCRUBS_ALL)) { + AddFixedItemToPool(RG_DEKU_NUTS_5); + } } if (ctx->GetDungeon(Rando::JABU_JABUS_BELLY)->IsMQ()) { - AddItemsToPool(ItemPool, JB_MQ); + AddFixedItemToPool(RG_DEKU_NUTS_5, 4); + AddFixedItemToPool(RG_RECOVERY_HEART); + AddFixedItemToPool(RG_DEKU_STICK_1); + AddFixedItemToPool(RG_DEKU_SHIELD); } if (ctx->GetDungeon(Rando::FOREST_TEMPLE)->IsMQ()) { - AddItemsToPool(ItemPool, FoT_MQ); + AddFixedItemToPool(RG_ARROWS_5); } else { - AddItemsToPool(ItemPool, FoT_Vanilla); + AddFixedItemToPool(RG_RECOVERY_HEART); + AddFixedItemToPool(RG_ARROWS_10); + AddFixedItemToPool(RG_ARROWS_30); } if (ctx->GetDungeon(Rando::FIRE_TEMPLE)->IsMQ()) { - AddItemsToPool(ItemPool, FiT_MQ); + AddFixedItemToPool(RG_HYLIAN_SHIELD); + AddFixedItemToPool(RG_BOMBS_20); } else { - AddItemsToPool(ItemPool, FiT_Vanilla); + AddFixedItemToPool(RG_HUGE_RUPEE); } if (ctx->GetDungeon(Rando::SPIRIT_TEMPLE)->IsMQ()) { - AddItemsToPool(ItemPool, SpT_MQ); + AddFixedItemToPool(RG_PURPLE_RUPEE, 2); + AddFixedItemToPool(RG_ARROWS_30); } else { - AddItemsToPool(ItemPool, SpT_Vanilla); + AddFixedItemToPool(RG_DEKU_SHIELD, 2); + AddFixedItemToPool(RG_BOMBS_20); + AddFixedItemToPool(RG_RECOVERY_HEART, 2); } if (ctx->GetDungeon(Rando::SHADOW_TEMPLE)->IsMQ()) { - AddItemsToPool(ItemPool, ShT_MQ); + AddFixedItemToPool(RG_ARROWS_5, 2); + AddFixedItemToPool(RG_RED_RUPEE); } else { - AddItemsToPool(ItemPool, ShT_Vanilla); + AddFixedItemToPool(RG_ARROWS_30); } if (ctx->GetDungeon(Rando::BOTTOM_OF_THE_WELL)->IsVanilla()) { - AddItemsToPool(ItemPool, BW_Vanilla); + AddFixedItemToPool(RG_DEKU_NUTS_5); + AddFixedItemToPool(RG_DEKU_NUTS_10); + AddFixedItemToPool(RG_RECOVERY_HEART); + AddFixedItemToPool(RG_BOMBS_10); + AddFixedItemToPool(RG_DEKU_SHIELD); + AddFixedItemToPool(RG_HYLIAN_SHIELD); + AddFixedItemToPool(RG_HUGE_RUPEE); } if (ctx->GetDungeon(Rando::GERUDO_TRAINING_GROUND)->IsMQ()) { - AddItemsToPool(ItemPool, GTG_MQ); + AddFixedItemToPool(RG_TREASURE_GAME_GREEN_RUPEE, 2); + AddFixedItemToPool(RG_ARROWS_10); + AddFixedItemToPool(RG_GREEN_RUPEE); + AddFixedItemToPool(RG_PURPLE_RUPEE); } else { - AddItemsToPool(ItemPool, GTG_Vanilla); + AddFixedItemToPool(RG_HUGE_RUPEE); + AddFixedItemToPool(RG_ARROWS_30, 3); } if (ctx->GetDungeon(Rando::GANONS_CASTLE)->IsMQ()) { - AddItemsToPool(ItemPool, GC_MQ); + AddFixedItemToPool(RG_ARROWS_10, 2); + AddFixedItemToPool(RG_BOMBS_5); + AddFixedItemToPool(RG_RED_RUPEE); + AddFixedItemToPool(RG_RECOVERY_HEART); + if (ctx->GetOption(RSK_SHUFFLE_SCRUBS).Is(RO_SCRUBS_ALL)) { + AddFixedItemToPool(RG_DEKU_NUTS_5); + } } else { - AddItemsToPool(ItemPool, GC_Vanilla); - } - - uint8_t rutoBottles = 1; - if (ctx->GetOption(RSK_ZORAS_FOUNTAIN).Is(RO_ZF_OPEN)) { - rutoBottles = 0; + AddFixedItemToPool(RG_BLUE_RUPEE, 3); + AddFixedItemToPool(RG_ARROWS_30); } // Add 4 total bottles uint8_t bottleCount = 4; - std::vector bottles; - bottles.assign(normalBottles.begin(), normalBottles.end()); - ctx->possibleIceTrapModels.push_back(Rando::StaticData::RetrieveItem(RandomElement(bottles)) - .GetRandomizerGet()); // Get one random bottle type for ice traps - for (uint8_t i = 0; i < bottleCount; i++) { - if (i >= rutoBottles) { - if ((i == bottleCount - 1) && - (ctx->GetOption(RSK_SHUFFLE_MERCHANTS).Is(RO_SHUFFLE_MERCHANTS_ALL_BUT_BEANS) || - ctx->GetOption(RSK_SHUFFLE_MERCHANTS).Is(RO_SHUFFLE_MERCHANTS_ALL))) { - AddItemToMainPool(RG_BOTTLE_WITH_BLUE_POTION); - } else { - AddRandomBottle(bottles); - } - } else { - AddItemToMainPool(RG_RUTOS_LETTER); - } + if (ctx->GetOption(RSK_ZORAS_FOUNTAIN).IsNot(RO_ZF_OPEN)) { + AddFixedItemToPool(RG_RUTOS_LETTER); + bottleCount--; + } + if ((ctx->GetOption(RSK_SHUFFLE_MERCHANTS).Is(RO_SHUFFLE_MERCHANTS_ALL_BUT_BEANS) || + ctx->GetOption(RSK_SHUFFLE_MERCHANTS).Is(RO_SHUFFLE_MERCHANTS_ALL))) { + AddFixedItemToPool(RG_BOTTLE_WITH_BLUE_POTION); + bottleCount--; } - if (ctx->GetOption(RSK_SHUFFLE_SONGS).IsNot(RO_SONG_SHUFFLE_OFF)) { - AddItemsToPool(ItemPool, songList); - // add extra songs only if song shuffle is anywhere - if (ctx->GetOption(RSK_SHUFFLE_SONGS).Is(RO_SONG_SHUFFLE_ANYWHERE) && - ctx->GetOption(RSK_ITEM_POOL).Is(RO_ITEM_POOL_PLENTIFUL)) { - AddItemsToPool(PendingJunkPool, songList); - } - } else { - ctx->PlaceItemInLocation(RC_SHEIK_IN_FOREST, RG_MINUET_OF_FOREST, false, true); - ctx->PlaceItemInLocation(RC_SHEIK_IN_CRATER, RG_BOLERO_OF_FIRE, false, true); - ctx->PlaceItemInLocation(RC_SHEIK_IN_ICE_CAVERN, RG_SERENADE_OF_WATER, false, true); - ctx->PlaceItemInLocation(RC_SHEIK_AT_COLOSSUS, RG_REQUIEM_OF_SPIRIT, false, true); - ctx->PlaceItemInLocation(RC_SHEIK_IN_KAKARIKO, RG_NOCTURNE_OF_SHADOW, false, true); - ctx->PlaceItemInLocation(RC_SHEIK_AT_TEMPLE, RG_PRELUDE_OF_LIGHT, false, true); - ctx->PlaceItemInLocation(RC_SONG_FROM_IMPA, RG_ZELDAS_LULLABY, false, true); - ctx->PlaceItemInLocation(RC_SONG_FROM_MALON, RG_EPONAS_SONG, false, true); - ctx->PlaceItemInLocation(RC_SONG_FROM_SARIA, RG_SARIAS_SONG, false, true); - ctx->PlaceItemInLocation(RC_SONG_FROM_ROYAL_FAMILYS_TOMB, RG_SUNS_SONG, false, true); - ctx->PlaceItemInLocation(RC_SONG_FROM_OCARINA_OF_TIME, RG_SONG_OF_TIME, false, true); - ctx->PlaceItemInLocation(RC_SONG_FROM_WINDMILL, RG_SONG_OF_STORMS, false, true); + ctx->possibleIceTrapModels.insert(RG_EMPTY_BOTTLE); // ice traps reroll this into a random normal bottle + for (uint8_t i = 0; i < bottleCount; i++) { + AddFixedItemToPool(RandomElement(Rando::StaticData::normalBottles), 1, false); } /*For item pool generation, dungeon items are either placed in their vanilla @@ -1225,119 +796,117 @@ void GenerateItemPool() { if (ctx->GetOption(RSK_SHUFFLE_MAPANDCOMPASS).Is(RO_DUNGEON_ITEM_LOC_VANILLA)) { PlaceVanillaMapsAndCompasses(); - } else { + } else if (ctx->GetOption(RSK_SHUFFLE_MAPANDCOMPASS).IsNot(RO_DUNGEON_ITEM_LOC_STARTWITH)) { for (auto dungeon : ctx->GetDungeons()->GetDungeonList()) { if (dungeon->GetMap() != RG_NONE) { - AddItemToMainPool(dungeon->GetMap()); + AddFixedItemToPool(dungeon->GetMap(), false); } if (dungeon->GetCompass() != RG_NONE) { - AddItemToMainPool(dungeon->GetCompass()); + AddFixedItemToPool(dungeon->GetCompass(), false); } } } - if (ctx->GetOption(RSK_KEYSANITY).Is(RO_DUNGEON_ITEM_LOC_VANILLA)) { - PlaceVanillaSmallKeys(); - } else { - for (auto dungeon : ctx->GetDungeons()->GetDungeonList()) { - if (dungeon->HasKeyRing() && ctx->GetOption(RSK_KEYSANITY).IsNot(RO_DUNGEON_ITEM_LOC_STARTWITH)) { - AddItemToMainPool(dungeon->GetKeyRing()); - } else if (dungeon->GetSmallKeyCount() > 0) { - AddItemToMainPool(dungeon->GetSmallKey(), dungeon->GetSmallKeyCount()); - } - } + int maxHearts = 20; + switch (ctx->GetOption(RSK_ITEM_POOL).Get()) { + case RO_ITEM_POOL_PLENTIFUL: + case RO_ITEM_POOL_BALANCED: + break; + case RO_ITEM_POOL_SCARCE: + maxHearts = 12; + break; + case RO_ITEM_POOL_MINIMAL: + maxHearts = 3; + break; } - if (ctx->GetOption(RSK_BOSS_KEYSANITY).Is(RO_DUNGEON_ITEM_LOC_VANILLA)) { - PlaceVanillaBossKeys(); - } else { - AddItemToMainPool(RG_FOREST_TEMPLE_BOSS_KEY); - AddItemToMainPool(RG_FIRE_TEMPLE_BOSS_KEY); - AddItemToMainPool(RG_WATER_TEMPLE_BOSS_KEY); - AddItemToMainPool(RG_SPIRIT_TEMPLE_BOSS_KEY); - AddItemToMainPool(RG_SHADOW_TEMPLE_BOSS_KEY); - } - - if (!ctx->GetOption(RSK_TRIFORCE_HUNT) - .IsNot(RO_TRIFORCE_HUNT_OFF)) { // Don't add GBK to the pool at all for Triforce Hunt. - if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_KAK_TOKENS)) { - ctx->PlaceItemInLocation(RC_KAK_100_GOLD_SKULLTULA_REWARD, RG_GANONS_CASTLE_BOSS_KEY); - } else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Get() >= RO_GANON_BOSS_KEY_LACS_VANILLA) { - ctx->PlaceItemInLocation(RC_TOT_LIGHT_ARROWS_CUTSCENE, RG_GANONS_CASTLE_BOSS_KEY); - } else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_VANILLA)) { - ctx->PlaceItemInLocation(RC_GANONS_TOWER_BOSS_KEY_CHEST, RG_GANONS_CASTLE_BOSS_KEY); - } else { - AddItemToMainPool(RG_GANONS_CASTLE_BOSS_KEY); - } - } - - if (ctx->GetOption(RSK_ITEM_POOL).Is(RO_ITEM_POOL_PLENTIFUL)) { - AddItemsToPool(ItemPool, easyItems); - } else { - AddItemsToPool(ItemPool, normalItems); - } - - if (!ctx->GetOption(RSK_SHUFFLE_KOKIRI_SWORD)) { - ReplaceMaxItem(RG_KOKIRI_SWORD, 0); - } - - if (!ctx->GetOption(RSK_SHUFFLE_MASTER_SWORD)) { - ReplaceMaxItem(RG_MASTER_SWORD, 0); - } - - if (/*ProgressiveGoronSword TODO: Implement Setting*/ false) { - ReplaceMaxItem(RG_BIGGORON_SWORD, 0); - AddItemToMainPool(RG_PROGRESSIVE_GORONSWORD, 2); - ctx->possibleIceTrapModels.push_back(RG_PROGRESSIVE_GORONSWORD); - } else { - ctx->possibleIceTrapModels.push_back(RG_BIGGORON_SWORD); - } - - // Replace ice traps with junk from the pending junk pool if necessary - if (ctx->GetOption(RSK_ICE_TRAPS).Is(RO_ICE_TRAPS_OFF)) { - ReplaceMaxItem(RG_ICE_TRAP, 0); - } - // Replace all junk items with ice traps for onslaught mode - else if (ctx->GetOption(RSK_ICE_TRAPS).Is(RO_ICE_TRAPS_ONSLAUGHT)) { - PendingJunkPool.clear(); - for (uint8_t i = 0; i < JunkPoolItems.size() - 3; i++) { // -3 Omits Huge Rupees and Deku Nuts 10 - ReplaceMaxItem(JunkPoolItems[i], 0); - } - } - - if (ctx->GetOption(RSK_ITEM_POOL).Is(RO_ITEM_POOL_SCARCE)) { - SetScarceItemPool(); - } else if (ctx->GetOption(RSK_ITEM_POOL).Is(RO_ITEM_POOL_MINIMAL)) { - SetMinimalItemPool(); - } else if (/*RemoveDoubleDefense TODO: Implement setting*/ false) { - ReplaceMaxItem(RG_DOUBLE_DEFENSE, 0); - } - - std::erase(ItemPool, RG_NONE); - - if (ItemPool.size() < ctx->allLocations.size()) { - Shuffle(PendingJunkPool); - size_t junkNeeded = std::min(PendingJunkPool.size(), ctx->allLocations.size() - ItemPool.size()); - ItemPool.insert(ItemPool.end(), PendingJunkPool.begin(), PendingJunkPool.begin() + junkNeeded); - PendingJunkPool.erase(PendingJunkPool.begin(), PendingJunkPool.begin() + junkNeeded); - } else if (ItemPool.size() > ctx->allLocations.size()) { - // RANDOTODO: all junk should be put in PendingJunkPool so this is never needed - size_t remove = ItemPool.size() - ctx->allLocations.size(); - for (size_t i = 0; remove > 0 && i < ItemPool.size(); i++) { - for (size_t j = 0; j < JunkPoolItems.size(); j++) { - if (ItemPool[i] == JunkPoolItems[j]) { - ItemPool[i] = RG_NONE; - remove--; + int startingHearts = ctx->GetOption(RSK_STARTING_HEARTS).Get() + 1; + if (startingHearts < maxHearts) { + AddFixedItemToPool(RG_TREASURE_GAME_HEART, 1, false); + AddFixedItemToPool(RG_PIECE_OF_HEART, 3, false); + startingHearts++; + if (startingHearts < maxHearts) { + switch (ctx->GetOption(RSK_ITEM_POOL).Get()) { + case RO_ITEM_POOL_PLENTIFUL: + case RO_ITEM_POOL_MINIMAL: + AddFixedItemToPool(RG_HEART_CONTAINER, maxHearts - startingHearts, false); break; + case RO_ITEM_POOL_BALANCED: { + int heartsToPlace = maxHearts - startingHearts; + int halfHearts = maxHearts >> 2; + AddFixedItemToPool(RG_HEART_CONTAINER, heartsToPlace - halfHearts, false); + AddFixedItemToPool(RG_PIECE_OF_HEART, halfHearts * 4, false); + break; + } + case RO_ITEM_POOL_SCARCE: + AddFixedItemToPool(RG_PIECE_OF_HEART, (maxHearts - startingHearts) * 4, false); + break; + } + } + } + + std::erase(junkPool, RG_NONE); + std::erase(itemPool, RG_NONE); + std::erase(lesserPool, RG_NONE); + std::erase(plentifulPool, RG_NONE); + + size_t locCount = ctx->CountEmptyLocations(false); + assert(itemPool.size() <= locCount); + int iceTrapstoAdd = 0; + if (itemPool.size() + plentifulPool.size() < locCount) { + itemPool.insert(itemPool.end(), plentifulPool.begin(), plentifulPool.end()); + // Fixed Ice Traps + if (ctx->GetOption(RSK_BASE_ICE_TRAPS)) { + iceTrapstoAdd++; + if (ctx->GetDungeon(Rando::GERUDO_TRAINING_GROUND)->IsVanilla()) { + iceTrapstoAdd++; + } + if (ctx->GetDungeon(Rando::GANONS_CASTLE)->IsVanilla()) { + iceTrapstoAdd += 4; + } + } + iceTrapstoAdd += ctx->GetOption(RSK_ADDITIONAL_ICE_TRAPS).Get(); + AddFixedItemToPool(RG_ICE_TRAP, + itemPool.size() + iceTrapstoAdd < locCount ? iceTrapstoAdd : locCount - itemPool.size(), false); + if (itemPool.size() + lesserPool.size() < locCount) { + itemPool.insert(itemPool.end(), lesserPool.begin(), lesserPool.end()); + } else { + while (itemPool.size() < locCount) { + itemPool.insert(itemPool.end(), RandomElement(lesserPool, true)); + } + } + } else { + while (itemPool.size() < locCount) { + itemPool.insert(itemPool.end(), RandomElement(plentifulPool, true)); + } + } + + size_t junkToAdd = locCount - itemPool.size(); + iceTrapstoAdd = 0; + if (junkToAdd > 0) { + if (ctx->GetOption(RSK_ICE_TRAP_PERCENT).Is(100)) { + iceTrapstoAdd = junkToAdd; + } else if (ctx->GetOption(RSK_ICE_TRAP_PERCENT).Get() >= 0) { + for (int count = 0; count < junkToAdd; count++) { + if (Random(0, 101) < ctx->GetOption(RSK_ICE_TRAP_PERCENT).Get()) { + iceTrapstoAdd++; } } } - std::erase(ItemPool, RG_NONE); + AddFixedItemToPool(RG_ICE_TRAP, iceTrapstoAdd, false); + junkToAdd -= iceTrapstoAdd; + if (junkToAdd > junkPool.size()) { + itemPool.insert(itemPool.end(), junkPool.begin(), junkPool.end()); + while (itemPool.size() < locCount) { + itemPool.insert(itemPool.end(), RandomElement(JunkPoolItems)); + } + } else { + while (itemPool.size() < locCount) { + itemPool.insert(itemPool.end(), RandomElement(junkPool, true)); + } + } } - // RANDOTODO: Ideally this should be checking for equality, but that is not currently the case and has never been - // the case, and isn't even currently the case in the 3drando repo we inherited this from years ago, so it may - // be a large undertaking to fix. - assert(ItemPool.size() <= ctx->allLocations.size() || !"Item Pool larger than Location Pool"); + assert(itemPool.size() == locCount); } diff --git a/soh/soh/Enhancements/randomizer/3drando/item_pool.hpp b/soh/soh/Enhancements/randomizer/3drando/item_pool.hpp index a437099c8..01b9a2459 100644 --- a/soh/soh/Enhancements/randomizer/3drando/item_pool.hpp +++ b/soh/soh/Enhancements/randomizer/3drando/item_pool.hpp @@ -11,4 +11,4 @@ void AddItemToPool(std::vector& pool, const RandomizerGet item, s RandomizerGet GetJunkItem(); void GenerateItemPool(); -extern std::vector ItemPool; +extern std::vector itemPool; diff --git a/soh/soh/Enhancements/randomizer/3drando/shops.cpp b/soh/soh/Enhancements/randomizer/3drando/shops.cpp index 06fe5790f..970b05389 100644 --- a/soh/soh/Enhancements/randomizer/3drando/shops.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/shops.cpp @@ -27,7 +27,7 @@ PriceSettingsStruct::PriceSettingsStruct(RandomizerSettingKey _main, RandomizerS affordable = _affordable; } -static std::array, 0xF1> trickNameTable; // Table of trick names for ice traps +static std::array, RG_MAX> trickNameTable; // Table of trick names for ice traps bool initTrickNames = false; // Indicates if trick ice trap names have been initialized yet // Set vanilla shop item locations before potentially shuffling @@ -887,6 +887,56 @@ void InitTrickNames() { Text{ "Triforce Shard", "Éclat de Triforce", "Triforce-Fragment" }, // "Triforce Shard" Text{ "Shiny Rock", "Caillou Brillant", "glänzender Stein" }, // "Shiny Rock" }; + trickNameTable[RG_DEATH_MOUNTAIN_CRATER_BEAN_SOUL] = { + // TODO_TRANSLATE + Text{ "Volcano Seed Spirit" }, + Text{ "Bolero Sprout Platform" }, + }; + trickNameTable[RG_DEATH_MOUNTAIN_TRAIL_BEAN_SOUL] = { + // TODO_TRANSLATE + Text{ "Dodongo's Seed Spirit" }, + Text{ "Boulder Sprout Platform" }, + }; + trickNameTable[RG_DESERT_COLOSSUS_BEAN_SOUL] = { + // TODO_TRANSLATE + Text{ "Spirit Temple Seed Spirit" }, + Text{ "Colossus Arch Sprout Platform" }, + }; + trickNameTable[RG_GERUDO_VALLEY_BEAN_SOUL] = { + // TODO_TRANSLATE + Text{ "Waterfall Seed Spirit" }, + Text{ "Gerudo Cow Sprout Platform" }, + }; + trickNameTable[RG_GRAVEYARD_BEAN_SOUL] = { + // TODO_TRANSLATE + Text{ "GY Crate Seed Spirit" }, + Text{ "Dampe's Sprout Platform" }, + }; + trickNameTable[RG_KOKIRI_FOREST_BEAN_SOUL] = { + // TODO_TRANSLATE + Text{ "Rupee Ledge Seed Spirit" }, + Text{ "KF Shop Sprout Platform" }, + }; + trickNameTable[RG_LAKE_HYLIA_BEAN_SOUL] = { + // TODO_TRANSLATE + Text{ "Hylia Lab Seed Spirit" }, + Text{ "Fishing Sprout Platform" }, + }; + trickNameTable[RG_LOST_WOODS_BRIDGE_BEAN_SOUL] = { + // TODO_TRANSLATE + Text{ "LW Bridge Seed Spirit" }, + Text{ "Skull Kid Sprout Platform" }, + }; + trickNameTable[RG_LOST_WOODS_BEAN_SOUL] = { + // TODO_TRANSLATE + Text{ "Deku Theatre Seed Spirit" }, + Text{ "Deku Scrubs Sprout Platform" }, + }; + trickNameTable[RG_ZORAS_RIVER_BEAN_SOUL] = { + // TODO_TRANSLATE + Text{ "River Ride Seed Spirit" }, + Text{ "Bean Salesman Sprout Platform" }, + }; trickNameTable[RG_GOHMA_SOUL] = { Text{ "Spider Sense", "Sens de l'Araignée", "Spinnensinn" }, Text{ "Deku Spirit", "Parasite Mojo", "Deku Geist" }, @@ -937,6 +987,11 @@ void InitTrickNames() { Text{ "Floating Lure", "Floating Lure", "Schwimmer" }, Text{ "Fishing Reel", "Fishing Reel", "Angelschnur" }, }; + trickNameTable[RG_SKELETON_KEY] = { + // TODO_TRANSLATE + Text{ "Stalfos Key" }, Text{ "Nightmare Key" }, Text{ "Graveyard Key" }, + Text{ "King's Key" }, Text{ "Hero's Key" }, + }; trickNameTable[RG_OCARINA_A_BUTTON] = { Text{ "Ocarina J Button", "Touche Ha de l'Ocarina", "J-Taste der Okarina" }, Text{ "Ocarina Ayy Button", "Touche Ah de l'Ocarina", "A-Taste der Flöte" }, @@ -965,6 +1020,331 @@ void InitTrickNames() { Text{ "Overworld C Right Button", "Trou Droit de l'Ocarina", "C-Rechts-Taste der E-Gitarre" }, }; + trickNameTable[RG_GREG_RUPEE] = { + // TODO_TRANSALTE + Text{ "Morshu the Green Ruby", "Morshu the Green Ruby", "Morshu the Green Ruby" }, + Text{ "Geoffrey the Gray Rupoor", "Geoffrey the Gray Rupoor", "Geoffrey the Gray Rupoor" }, + Text{ "Validation Rupee", "Validation Rupee", "Validation Rupee" }, + Text{ "Gary, just Gary", "Gary, just Gary", "Gary, just Gary" }, + Text{ "Ike the Indigo Ice Trap", "Ike the Indigo Ice Trap", "Ike the Indigo Ice Trap" }, + }; + + trickNameTable[RG_FOREST_TEMPLE_SMALL_KEY] = { + // TODO_TRANSALTE + Text{ "Wind Temple Smol Key", "Wind Temple Smol Key", "Wind Temple Smol Key" }, + Text{ "Woodfall Temple Small Key", "Woodfall Temple Small Key", "Woodfall Temple Small Key" }, + Text{ "Skull Woods Small Key", "Skull Woods Small Key", "Skull Woods Small Key" }, + }; + trickNameTable[RG_FIRE_TEMPLE_SMALL_KEY] = { + // TODO_TRANSALTE + Text{ "Ice Cavern Small Keese", "Ice Cavern Small Keese", "Ice Cavern Small Keese" }, + Text{ "Goron Temple Small Key", "Goron Temple Small Key", "Goron Temple Small Key" }, + Text{ "Eldin Temple Salmon Koi", "Eldin Temple Salmon Koi", "Eldin Temple Salmon Koi" }, + }; + trickNameTable[RG_WATER_TEMPLE_SMALL_KEY] = { + // TODO_TRANSALTE + Text{ "Swamp Palace Small Keese", "Swamp Palace Small Keese", "Swamp Palace Small Keese" }, + Text{ "Great Bay Temple Small Key", "Great Bay Temple Small Key", "Great Bay Temple Small Key" }, + Text{ "Lakebed Temple Small Key", "Lakebed Temple Small Key", "Lakebed Temple Small Key" }, + }; + trickNameTable[RG_SPIRIT_TEMPLE_SMALL_KEY] = { + // TODO_TRANSALTE + Text{ "Light Temple Small Key", "Light Temple Small Key", "Light Temple Small Key" }, + Text{ "Lightning Temple Smol Key", "Lightning Temple Smol Key", "Lightning Temple Smol Key" }, + Text{ "Desert Palace Small Key", "Desert Palace Small Key", "Desert Palace Small Key" }, + Text{ "Stone Tower Small Keese", "Stone Tower Small Keese", "Stone Tower Small Keese" }, + }; + trickNameTable[RG_SHADOW_TEMPLE_SMALL_KEY] = { + // TODO_TRANSALTE + Text{ "Palace of Darkness Small Key", "Palace of Darkness Small Key", "Palace of Darkness Small Key" }, + Text{ "Shrine of Illusion Salmon Koi", "Shrine of Illusion Salmon Koi", "Shrine of Illusion Salmon Koi" }, + Text{ "Palace of Twilight Small Key", "Palace of Twilight Small Key", "Palace of Twilight Small Key" }, + }; + trickNameTable[RG_BOTTOM_OF_THE_WELL_SMALL_KEY] = { + // TODO_TRANSALTE + Text{ "Top of the Wall Small Key", "Top of the Wall Small Key", "Top of the Wall Small Key" }, + Text{ "Breath of the Wild Small Key", "Breath of the Wild Small Key", "Breath of the Wild Small Key" }, + Text{ "Beneath the Well Small Key", "Beneath the Well Small Key", "Beneath the Well Small Key" }, + }; + trickNameTable[RG_GERUDO_TRAINING_GROUND_SMALL_KEY] = { + // TODO_TRANSALTE + Text{ "Gerudo Sanctum Small Key", "Gerudo Sanctum Small Key", "Gerudo Sanctum Small Key" }, + Text{ "Lady's Lair Small Keese", "Lady's Lair Small Keese", "Lady's Lair Small Keese" }, + Text{ "Knight Acadamy Small Key", "Knight Acadamy Small Key", "Knight Acadamy Small Key" }, + }; + trickNameTable[RG_GERUDO_FORTRESS_SMALL_KEY] = { + // TODO_TRANSALTE + Text{ "Fortress of Winds Small Key", "Fortress of Winds Small Key", "Fortress of Winds Small Key" }, + Text{ "Thieve's Town Small Key", "Thieve's Town Small Key", "Thieve's Town Small Key" }, + Text{ "Fortress Centrum Small Key", "Fortress Centrum Small Key", "Fortress Centrum Small Key" }, + Text{ "Forsaken Fortress Smol Key", "Forsaken Fortress Smol Key", "Forsaken Fortress Smol Key" }, + Text{ "Pirate's Fortress Small Key", "Pirate's Fortress Small Key", "Pirate's Fortress Small Key" }, + }; + trickNameTable[RG_GANONS_CASTLE_SMALL_KEY] = { + // TODO_TRANSALTE + Text{ "Hyrule Castle Salmon Koi", "Hyrule Castle Salmon Koi", "Hyrule Castle Salmon Koi" }, + Text{ "Onox's Castle Small Key", "Onox's Castle Small Key", "Onox's Castle Small Key" }, + Text{ "Vaati's Palace Small Key", "Vaati's Palace Small Key", "Vaati's Palace Small Key" }, + }; + + trickNameTable[RG_FOREST_TEMPLE_KEY_RING] = { + // TODO_TRANSALTE + Text{ "Wind Temple Key Ring", "Wind Temple Key Ring", "Wind Temple Key Ring" }, + Text{ "Woodfall Temple Key Ring", "Woodfall Temple Key Ring", "Woodfall Temple Key Ring" }, + Text{ "Skull Woods Key Ring", "Skull Woods Key Ring", "Skull Woods Key Ring" }, + }; + trickNameTable[RG_FIRE_TEMPLE_KEY_RING] = { + // TODO_TRANSALTE + Text{ "Ice Cavern Keese Ring", "Ice Cavern Keese Ring", "Ice Cavern Keese Ring" }, + Text{ "Goron Temple Key Ring", "Goron Temple Key Ring", "Goron Temple Key Ring" }, + Text{ "Eldin Temple Koi Ray", "Eldin Temple Koi Ray", "Eldin Temple Koi Ray" }, + }; + trickNameTable[RG_WATER_TEMPLE_KEY_RING] = { + // TODO_TRANSALTE + Text{ "Swamp Palace Keese Ring", "Swamp Palace Keese Ring", "Swamp Palace Keese Ring" }, + Text{ "Great Bay Temple Key Ring", "Great Bay Temple Key Ring", "Great Bay Temple Key Ring" }, + Text{ "Lakebed Temple Key Ring", "Lakebed Temple Key Ring", "Lakebed Temple Key Ring" }, + }; + trickNameTable[RG_SPIRIT_TEMPLE_KEY_RING] = { + // TODO_TRANSALTE + Text{ "Light Temple Key Ring", "Light Temple Key Ring", "Light Temple Key Ring" }, + Text{ "Lightning Temple Key Ring", "Lightning Temple Key Ring", "Lightning Temple Key Ring" }, + Text{ "Desert Palace Key Ring", "Desert Palace Key Ring", "Desert Palace Key Ring" }, + Text{ "Stone Tower Keese Ring", "Stone Tower Keese Ring", "Stone Tower Keese Ring" }, + }; + trickNameTable[RG_SHADOW_TEMPLE_KEY_RING] = { + // TODO_TRANSALTE + Text{ "Palace of Darkness Key Ring", "Palace of Darkness Key Ring", "Palace of Darkness Key Ring" }, + Text{ "Shrine of Illusion Koi Ray", "Shrine of Illusion Koi Ray", "Shrine of Illusion Koi Ray" }, + Text{ "Palace of Twilight Key Ring", "Palace of Twilight Key Ring", "Palace of Twilight Key Ring" }, + }; + trickNameTable[RG_BOTTOM_OF_THE_WELL_KEY_RING] = { + // TODO_TRANSALTE + Text{ "Top of the Wall Key Ring", "Top of the Wall Key Ring", "Top of the Wall Key Ring" }, + Text{ "Breath of the Wild Key Ring", "Breath of the Wild Key Ring", "Breath of the Wild Key Ring" }, + Text{ "Beneath the Well Key Ring", "Beneath the Well Key Ring", "Beneath the Well Key Ring" }, + }; + trickNameTable[RG_GERUDO_TRAINING_GROUND_KEY_RING] = { + // TODO_TRANSALTE + Text{ "Gerudo Sanctum Key Ring", "Gerudo Sanctum Key Ring", "Gerudo Sanctum Key Ring" }, + Text{ "Lady's Lair Keese Ring", "Lady's Lair Keese Ring", "Lady's Lair Keese Ring" }, + Text{ "Knight Acadamy Key Ring", "Knight Acadamy Key Ring", "Knight Acadamy Key Ring" }, + }; + trickNameTable[RG_GERUDO_FORTRESS_KEY_RING] = { + // TODO_TRANSALTE + Text{ "Fortress of Winds Key Ring", "Fortress of Winds Key Ring", "Fortress of Winds Key Ring" }, + Text{ "Thieve's Town Key Ring", "Thieve's Town Key Ring", "Thieve's Town Key Ring" }, + Text{ "Fortress Centrum Key Ring", "Fortress Centrum Key Ring", "Fortress Centrum Key Ring" }, + Text{ "Forsaken Fortress Key Ring", "Forsaken Fortress Key Ring", "Forsaken Fortresse Key Ring" }, + Text{ "Pirate's Fortress Key Ring", "Pirate's Fortress Key Ring", "Pirate's Fortress Key Ring" }, + }; + trickNameTable[RG_GANONS_CASTLE_KEY_RING] = { + // TODO_TRANSALTE + Text{ "Hyrule Castle Koi Ray", "Hyrule Castle Koi Ray", "Hyrule Castle Koi Ray" }, + Text{ "Onox's Castle Key Ring", "Onox's Castle Key Ring", "Onox's Castle Key Ring" }, + Text{ "Vaati's Palace Key Ring", "Vaati's Palace Key Ring", "Vaati's Palace Key Ring" }, + }; + + trickNameTable[RG_FOREST_TEMPLE_BOSS_KEY] = { + // TODO_TRANSALTE + Text{ "Wind Temple Boss Key", "Wind Temple Boss Key", "Wind Temple Boss Key" }, + Text{ "Woodfall Temple Boss Key", "Woodfall Temple Boss Key", "Woodfall Temple Boss Key" }, + Text{ "Skull Woods Boss Key", "Skull Woods Boss Key", "Skull Woods Boss Key" }, + Text{ "Phantom Ganon's Key", "Phantom Ganon's Key", "Phantom Ganon's Key" }, + Text{ "Deku Tree's Boss Key", "Deku Tree's Boss Key", "Deku Tree's Boss Key" }, + }; + trickNameTable[RG_FIRE_TEMPLE_BOSS_KEY] = { + // TODO_TRANSALTE + Text{ "Ice Cavern Boss Keese", "Ice Cavern Boss Keese", "Ice Cavern Boss Keese" }, + Text{ "Goron Temple Boss Key", "Goron Temple Boss Key", "Goron Temple Boss Key" }, + Text{ "Eldin Temple Boss Koi", "Eldin Temple Boss Koi", "Eldin Temple Boss Koi" }, + Text{ "Volvagia's Key", "Volvagia's Key", "Volvagia's Key" }, + Text{ "Dodongo's Cavern Boss Key", "Dodongo's Cavern Boss Key", "Dodongo's Cavern Boss Key" }, + }; + trickNameTable[RG_WATER_TEMPLE_BOSS_KEY] = { + // TODO_TRANSALTE + Text{ "Swamp Palace Boss Keese", "Swamp Palace Boss Keese", "Swamp Palace Boss Keese" }, + Text{ "Great Bay Temple Boss Key", "Great Bay Temple Boss Key", "Great Bay Temple Boss Key" }, + Text{ "Lakebed Temple Boss Key", "Lakebed Temple Boss Key", "Lakebed Temple Boss Key" }, + Text{ "Morpha's Key", "Morpha's Key", "Morpha's Key" }, + Text{ "Jabu Jabu's Belly Boss Key", "Jabu Jabu's Belly Boss Key", "Jabu Jabu's Belly Boss Key" }, + }; + trickNameTable[RG_SPIRIT_TEMPLE_BOSS_KEY] = { + // TODO_TRANSALTE + Text{ "Light Temple Boss Key", "Light Temple Boss Key", "Light Temple Boss Key" }, + Text{ "Lightning Temple Boss Key", "Lightning Temple Boss Key", "Lightning Temple Boss Key" }, + Text{ "Desert Palace Boss Key", "Desert Palace Boss Key", "Desert Palace Boss Key" }, + Text{ "Stone Tower Boss Keese", "Stone Tower Boss Keese", "Stone Tower Boss Keese" }, + Text{ "Twinrova's Key", "Twinrova's Key", "Twinrova's Key" }, + }; + trickNameTable[RG_SHADOW_TEMPLE_BOSS_KEY] = { + // TODO_TRANSALTE + Text{ "Palace of Darkness Boss Key", "Palace of Darkness Boss Key", "Palace of Darkness Boss Key" }, + Text{ "Shrine of Illusion Bass Koi", "Shrine of Illusion Bass Koi", "Shrine of Illusion Bass Koi" }, + Text{ "Palace of Twilight Boss Key", "Palace of Twilight Boss Key", "Palace of Twilight Boss Key" }, + Text{ "Bongo Bongo's Key", "Bongo Bongo's Key", "Bongo Bongo's Key" }, + }; + trickNameTable[RG_GANONS_CASTLE_BOSS_KEY] = { + // TODO_TRANSALTE + Text{ "Hyrule Castle Bass Koi", "Hyrule Castle Bass Koi", "Hyrule Castle Bass Koi" }, + Text{ "Onox's Castle Boss Key", "Onox's Castle Boss Key", "Onox's Castle Boss Key" }, + Text{ "Vaati's Palace Boss Key", "Vaati's Palace Boss Key", "Vaati's Palace Boss Key" }, + Text{ "Ganondorf's Key", "Ganondorf's Key", "Ganondorf's Key" }, + }; + + trickNameTable[RG_GUARD_HOUSE_KEY] = { + // TODO_TRANSLATE + Text{ "Pot Room Key", "Pot Room Key", "Pot Room Key" }, + Text{ "Poe Shop Keese", "Poe Shop Keese", "Poe Shop Keese" }, + Text{ "Pot Collectors Club Key", "Pot Collectors Club Key", "Pot Collectors Club Key" }, + }; + trickNameTable[RG_MARKET_BAZAAR_KEY] = { + // TODO_TRANSLATE + Text{ "Malo Mart Key", "Malo Mart Key", "Malo Mart Key" }, + Text{ "Zora Shop Key", "Zora Shop Key", "Zora Shop Key" }, + Text{ "Goronu General Store Key", "Goronu General Store Key", "Goronu General Store Key" }, + Text{ "Chudly's Fine Goods Key", "Chudly's Fine Goods Key", "Chudly's Fine Goods Key" }, + }; + trickNameTable[RG_MARKET_POTION_SHOP_KEY] = { + // TODO_TRANSLATE + Text{ "Market Medicine Shop Koi", "Market Medicine Shop Koi", "Market Medicine Shop Koi" }, + Text{ "Market Pharmacy Key", "Market Pharmacy Key", "Market Pharmacy Key" }, + Text{ "Market Drug Store Keese", "Market Drug Store Keese", "Market Drug Store Keese" }, + }; + trickNameTable[RG_MASK_SHOP_KEY] = { + // TODO_TRANSLATE + Text{ "Masked Ship Koi", "Masked Ship Koi", "Masked Ship Koi" }, + Text{ "Madame Couture's Key", "Madame Couture's Key", "Madame Couture's Key" }, + Text{ "South Clock Town Key", "South Clock Town Key", "South Clock Town Key" }, + }; + trickNameTable[RG_MARKET_SHOOTING_GALLERY_KEY] = { + // TODO_TRANSLATE + Text{ "Swamp Shooting Gallery Key", "Swamp Shooting Gallery Key", "Swamp Shooting Gallery Key" }, + Text{ "Koume's Target Shooting Key", "Koume's Target Shooting Key", "Koume's Target Shooting Key" }, + Text{ "Pumpkin Pull Key", "Pumpkin Pull Key", "Pumpkin Pull Key" }, + }; + trickNameTable[RG_BOMBCHU_BOWLING_KEY] = { + // TODO_TRANSLATE + Text{ "Bombchu Gallery Key", "Bombchu Gallery Key", "Bombchu Gallery Key" }, + Text{ "Cucco Bowling Ally Key", "Cucco Bowling Ally Key", "Cucco Bowling Ally Key" }, + Text{ "Snowball Bowling Key", "Snowball Bowling Key", "Snowball Bowling Key" }, + Text{ "Bombsketball Key", "Bombsketball Key", "Bombsketball Key" }, + }; + trickNameTable[RG_TREASURE_CHEST_GAME_BUILDING_KEY] = { + // TODO_TRANSLATE + Text{ "Lucky Treasure Game Koi", "Lucky Treasure Game Koi", "Lucky Treasure Game Koi" }, + Text{ "1 in 32 Key", "1 in 32 Key", "1 in 32 Key" }, + Text{ "Fortune's Coice Key", "Fortune's Coice Key", "Fortune's Coice Key" }, + Text{ "Money Making Game Key", "Money Making Game Key", "Money Making Game Key" }, + Text{ "Trading Card Game Key", "Trading Card Game Key", "Trading Card Game Key" }, + }; + trickNameTable[RG_BOMBCHU_SHOP_KEY] = { + // TODO_TRANSLATE + Text{ "Curiosity Shop Key", "Curiosity Shop Key", "Curiosity Shop Key" }, + Text{ "Barnes Bomb Shop Key", "Barnes Bomb Shop Key", "Barnes Bomb Shop Key" }, + Text{ "Bomb Flower Shop Key", "Bomb Flower Shop Key", "Bomb Flower Shop Key" }, + }; + trickNameTable[RG_RICHARDS_HOUSE_KEY] = { + // TODO_TRANSLATE + Text{ "Stockwell's House Key", "Stockwell's House Key", "Stockwell's House Key" }, + Text{ "Blue Dog's House Key", "Blue Dog's House Key", "Blue Dog's House Key" }, + Text{ "Barkle's House Key", "Barkle's House Key", "Barkle's House Key" }, + Text{ "Chimimi's House Key", "Chimimi's House Key", "Chimimi's House Key" }, + }; + trickNameTable[RG_ALLEY_HOUSE_KEY] = { + // TODO_TRANSLATE + Text{ "Mido's House Key", "Mido's House Key", "Mido's House Key" }, + Text{ "Saria's House Key", "Saria's House Key", "Saria's House Key" }, + Text{ "@'s House Key", "@'s House Key", "@'s House Key" }, + }; + trickNameTable[RG_KAK_BAZAAR_KEY] = { + // TODO_TRANSLATE + Text{ "Skyloft Bazaar Key", "Skyloft Bazaar Key", "Skyloft Bazaar Key" }, + Text{ "Harlequin Bazaar Key", "Harlequin Bazaar Key", "Harlequin Bazaar Key" }, + Text{ "Kokiri Shop Key", "Kokiri Shop Key", "Kokiri Shop Key" }, + Text{ "Goron Shop Key", "Goron Shop Key", "Goron Shop Key" }, + }; + trickNameTable[RG_KAK_POTION_SHOP_KEY] = { + // TODO_TRANSLATE + Text{ "Kak Medicine Shop Keep", "Kak Medicine Shop Keep", "Kak Medicine Shop Keep" }, + Text{ "Kak Pharmacy Key", "Kak Pharmacy Key", "Kak Pharmacy Key" }, + Text{ "Kak Drug Store Keese", "Kak Drug Store Keese", "Kak Drug Store Keese" }, + }; + trickNameTable[RG_BOSS_HOUSE_KEY] = { + // TODO_TRANSLATE + Text{ "Kakariko Village Boss Key", "Kakariko Village Boss Key", "Kakariko Village Boss Key" }, + Text{ "Lord Kohga's House Key", "Lord Kohga's House Key", "Lord Kohga's House Key" }, + Text{ "Twinrova's House Key", "Twinrova's House Key", "Twinrova's House Key" }, + }; + trickNameTable[RG_GRANNYS_POTION_SHOP_KEY] = { + // TODO_TRANSLATE + Text{ "Grandpa's Potion Shop Key", "Grandpa's Potion Shop Key", "Grandpa's Potion Shop Key" }, + Text{ "Witch's Hut Key", "Witch's Hut Key", "Witch's Hut Key" }, + Text{ "Hags's Potion Shop Key", "Hags's Potion Shop Key", "Hags's Potion Shop Key" }, + Text{ "Syrup's Potion Shop Key", "Syrup's Potion Shop Key", "Syrup's Potion Shop Key" }, + }; + trickNameTable[RG_SKULLTULA_HOUSE_KEY] = { + // TODO_TRANSLATE + Text{ "Town Spider House Key", "Town Spider House Key", "Town Spider House Key" }, + Text{ "Jovani's House Key", "Jovani's House Key", "Jovani's House Key" }, + Text{ "Maiamai House Key", "Maiamai House Key", "Maiamai House Key" }, + }; + trickNameTable[RG_IMPAS_HOUSE_KEY] = { + // TODO_TRANSLATE + Text{ "Zelda's House Key", "Zelda's House Key", "Zelda's House Key" }, + Text{ "Sheik's House Key", "Sheik's House Key", "Sheik's House Key" }, + Text{ "Purah's House Key", "Purah's House Key", "Purah's House Key" }, + }; + trickNameTable[RG_WINDMILL_KEY] = { + // TODO_TRANSLATE + Text{ "Wind Switch Key", "Wind Switch Key", "Wind Switch Key" }, + Text{ "Weather Vane Key", "Weather Vane Key", "Weather Vane Key" }, + }; + trickNameTable[RG_KAK_SHOOTING_GALLERY_KEY] = { + // TODO_TRANSLATE + Text{ "Firing Range Key", "Firing Range Key", "Firing Range Key" }, + Text{ "Crossbow Training Key", "Crossbow Training Key", "Crossbow Training Key" }, + Text{ "Goron Target Range Key", "Goron Target Range Key", "Goron Target Range Key" }, + }; + trickNameTable[RG_DAMPES_HUT_KEY] = { + // TODO_TRANSLATE + Text{ "Dampe's Grave Key", "Dampe's Grave Key", "Dampe's Grave Key" }, + Text{ "Dampe Studio Key", "Dampe Studio Key", "Dampe Studio Key" }, + Text{ "Old Man's Cabin Key", "Old Man's Cabin Key", "Old Man's Cabin Key" }, + }; + trickNameTable[RG_TALONS_HOUSE_KEY] = { + // TODO_TRANSLATE + Text{ "Malon's House Koi", "Malon's House Koi", "Malon's House Koi" }, + Text{ "Ingo's House Keese", "Ingo's House Keese", "Ingo's House Keese" }, + Text{ "Mario's House Key", "Mario's House Key", "Mario's House Key" }, + }; + trickNameTable[RG_STABLES_KEY] = { + // TODO_TRANSLATE + Text{ "Corral Key", "Corral Key", "Corral Key" }, + Text{ "Foothill Stable Key", "Foothill Stable Key", "Foothill Stable Key" }, + Text{ "Goat Barn Key", "Goat Barn Key", "Goat Barn Key" }, + }; + trickNameTable[RG_BACK_TOWER_KEY] = { + // TODO_TRANSLATE + Text{ "Tower of Hera Key", "Tower of Hera Key", "Tower of Hera Key" }, + Text{ "Clock Tower Key", "Clock Tower Key", "Clock Tower Key" }, + Text{ "Tingle Tower Key", "Tingle Tower Key", "Tingle Tower Key" }, + Text{ "Skyview Tower Key", "Skyview Tower Key", "Skyview Tower Key" }, + Text{ "Sheikah Tower Key", "Sheikah Tower Key", "Sheikah Tower Key" }, + }; + trickNameTable[RG_HYLIA_LAB_KEY] = { + // TODO_TRANSLATE + Text{ "Marine Research Lab Key", "Marine Research Lab Key", "Marine Research Lab Key" }, + Text{ "Hateno Tech Lab Key", "Hateno Tech Lab Key", "Hateno Tech Lab Key" }, + Text{ "Tough Mango Lab Key", "Tough Mango Lab Key", "Tough Mango Lab Key" }, + }; + trickNameTable[RG_FISHING_HOLE_KEY] = { + // TODO_TRANSLATE + Text{ "Swamp Fishing Hole Key", "Swamp Fishing Hole Key", "Swamp Fishing Hole Key" }, + Text{ "Beaver Race Key", "Beaver Race Key", "Beaver Race Key" }, + Text{ "Squid-Hunt Key", "Squid-Hunt Key", "Squid-Hunt Key" }, + }; + /* //Names for individual upgrades, in case progressive names are replaced trickNameTable[GI_HOOKSHOT] = { @@ -1159,12 +1539,16 @@ void InitTrickNames() { } // Generate a fake name for the ice trap based on the item it's displayed as -Text GetIceTrapName(uint8_t id) { +Text GetIceTrapName(int id) { // If the trick names table has not been initialized, do so if (!initTrickNames) { InitTrickNames(); initTrickNames = true; } + if (trickNameTable[id].empty()) { + assert(false); + return Text{ "not an Ice Trap" }; + } // Randomly get the easy, medium, or hard name for the given item id return RandomElement(trickNameTable[id]); } diff --git a/soh/soh/Enhancements/randomizer/3drando/shops.hpp b/soh/soh/Enhancements/randomizer/3drando/shops.hpp index 4cf780e83..109ea6143 100644 --- a/soh/soh/Enhancements/randomizer/3drando/shops.hpp +++ b/soh/soh/Enhancements/randomizer/3drando/shops.hpp @@ -27,4 +27,4 @@ extern std::vector GetMinVanillaShopItems(int total_replaced); extern uint16_t GetRandomPrice(Rando::Location* loc, PriceSettingsStruct priceSettings); extern uint16_t GetCheapBalancedPrice(); extern int GetShopsanityReplaceAmount(); -extern Text GetIceTrapName(uint8_t id); +extern Text GetIceTrapName(int id); diff --git a/soh/soh/Enhancements/randomizer/3drando/starting_inventory.cpp b/soh/soh/Enhancements/randomizer/3drando/starting_inventory.cpp index a971ba6e5..e7f875bf1 100644 --- a/soh/soh/Enhancements/randomizer/3drando/starting_inventory.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/starting_inventory.cpp @@ -22,7 +22,6 @@ void GenerateStartingInventory() { if (dungeon->GetMap() != RG_NONE) { AddItemToInventory(dungeon->GetMap()); } - if (dungeon->GetCompass() != RG_NONE) { AddItemToInventory(dungeon->GetCompass()); } @@ -155,32 +154,6 @@ void GenerateStartingInventory() { // AddItemToInventory(RG_SHADOW_MEDALLION, StartingShadowMedallion.Value()); // AddItemToInventory(RG_LIGHT_MEDALLION, StartingLightMedallion.Value()); AddItemToInventory(RG_GOLD_SKULLTULA_TOKEN, ctx->GetOption(RSK_STARTING_SKULLTULA_TOKEN).Get()); - - int8_t hearts = ctx->GetOption(RSK_STARTING_HEARTS).Get() - 2; - AdditionalHeartContainers = 0; - if (hearts < 0) { - AddItemToInventory(RG_PIECE_OF_HEART, 4); - // Plentiful and minimal have less than 4 standard pieces of heart so also replace the winner heart - if (ctx->GetOption(RSK_ITEM_POOL).Get() == 0 || ctx->GetOption(RSK_ITEM_POOL).Get() == 3) { - AddItemToInventory(RG_TREASURE_GAME_HEART); - } - - AdditionalHeartContainers = 1 - hearts; - } else if (hearts > 0) { - // 16 containers in plentiful, 8 in balanced and 0 in the others - uint8_t maxContainers = 8 * std::max(0, 2 - ctx->GetOption(RSK_ITEM_POOL).Get()); - - if (hearts <= maxContainers) { - AddItemToInventory(RG_HEART_CONTAINER, hearts); - } else { - AddItemToInventory(RG_HEART_CONTAINER, maxContainers); - AddItemToInventory(RG_PIECE_OF_HEART, (hearts - maxContainers) * 4); - } - - if (hearts == 17) { - AddItemToInventory(RG_TREASURE_GAME_HEART); - } - } } bool StartingInventoryHasBottle() { diff --git a/soh/soh/Enhancements/randomizer/SeedContext.cpp b/soh/soh/Enhancements/randomizer/SeedContext.cpp index 054f92b2a..41a6cb02b 100644 --- a/soh/soh/Enhancements/randomizer/SeedContext.cpp +++ b/soh/soh/Enhancements/randomizer/SeedContext.cpp @@ -67,6 +67,14 @@ RandomizerArea Context::GetAreaFromString(std::string str) { return (RandomizerArea)StaticData::areaNameToEnum[str]; } +int Context::CountEmptyLocations(const bool countShops) { + auto ctx = Rando::Context::GetInstance(); + return count_if(allLocations.begin(), allLocations.end(), [ctx, countShops](const auto loc) { + return ctx->GetItemLocation(loc)->GetPlacedRandomizerGet() == RG_NONE && + (countShops || Rando::StaticData::GetLocation(loc)->GetRCType() != RCTYPE_SHOP); + }); +} + void Context::InitStaticData() { StaticData::HintTable_Init(); StaticData::trialNameToEnum = StaticData::PopulateTranslationMap(StaticData::trialData); @@ -329,7 +337,17 @@ void Context::CreateItemOverrides() { // If this is an ice trap, store the disguise model in iceTrapModels const auto itemLoc = GetItemLocation(locKey); if (itemLoc->GetPlacedRandomizerGet() == RG_ICE_TRAP) { - ItemOverride val(locKey, RandomElement(possibleIceTrapModels)); + RandomizerGet trickModel = RandomElementFromSet(possibleIceTrapModels); + if (trickModel == RG_EMPTY_BOTTLE) { + trickModel = RandomElement(StaticData::normalBottles); + } + if (trickModel == RG_GUARD_HOUSE_KEY) { + trickModel = RandomElement(StaticData::overworldKeys); + } + if (trickModel == RG_DEATH_MOUNTAIN_CRATER_BEAN_SOUL) { + trickModel = RandomElement(StaticData::beanSouls); + } + ItemOverride val(locKey, trickModel); iceTrapModels[locKey] = val.LooksLike(); val.SetTrickName(GetIceTrapName(val.LooksLike())); // If this is ice trap is in a shop, change the name based on what the model will look like diff --git a/soh/soh/Enhancements/randomizer/SeedContext.h b/soh/soh/Enhancements/randomizer/SeedContext.h index bd20817f5..659531da1 100644 --- a/soh/soh/Enhancements/randomizer/SeedContext.h +++ b/soh/soh/Enhancements/randomizer/SeedContext.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #define RAND_GET_OPTION(option) Rando::Context::GetInstance()->GetOption(option).Get() @@ -125,13 +126,14 @@ class Context { std::map overrides = {}; std::vector> playthroughLocations = {}; std::vector everyPossibleLocation = {}; - std::vector possibleIceTrapModels = {}; + std::set possibleIceTrapModels = {}; std::unordered_map iceTrapModels = {}; std::vector VanillaLogicDefaults = {}; std::array hashIconIndexes = {}; bool playthroughBeatable = false; bool allLocationsReachable = false; RandomizerArea GetAreaFromString(std::string str); + int CountEmptyLocations(bool countShops); /** * @brief Get the hash for the current seed. diff --git a/soh/soh/Enhancements/randomizer/entrance.cpp b/soh/soh/Enhancements/randomizer/entrance.cpp index b2d301988..f008c33a1 100644 --- a/soh/soh/Enhancements/randomizer/entrance.cpp +++ b/soh/soh/Enhancements/randomizer/entrance.cpp @@ -1074,7 +1074,7 @@ static std::array, 2> SplitEntrancesByRequirements(std::v logic->Reset(); // Apply the effects of all advancement items to search for entrance accessibility std::vector items = FilterFromPool( - ItemPool, [](const RandomizerGet i) { return Rando::StaticData::RetrieveItem(i).IsAdvancement(); }); + itemPool, [](const RandomizerGet i) { return Rando::StaticData::RetrieveItem(i).IsAdvancement(); }); for (RandomizerGet unplacedItem : items) { Rando::StaticData::RetrieveItem(unplacedItem).ApplyEffect(); } diff --git a/soh/soh/Enhancements/randomizer/location_access/root.cpp b/soh/soh/Enhancements/randomizer/location_access/root.cpp index 7c7e63540..b96c49ef5 100644 --- a/soh/soh/Enhancements/randomizer/location_access/root.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/root.cpp @@ -20,6 +20,8 @@ void RegionTable_Init_Root() { LOCATION(RC_TRIFORCE_COMPLETED, logic->GetSaveContext()->ship.quest.data.randomizer.triforcePiecesCollected >= ctx->GetOption(RSK_TRIFORCE_HUNT_PIECES_REQUIRED).Get() + 1;), LOCATION(RC_SARIA_SONG_HINT, logic->CanUse(RG_SARIAS_SONG)), LOCATION(RC_SONG_FROM_IMPA, (bool)ctx->GetOption(RSK_SKIP_CHILD_ZELDA)), + LOCATION(RC_HC_MALON_EGG, (bool)ctx->GetOption(RSK_SKIP_CHILD_ZELDA)), + LOCATION(RC_HC_ZELDAS_LETTER, (bool)ctx->GetOption(RSK_SKIP_CHILD_ZELDA)), LOCATION(RC_TOT_MASTER_SWORD, (bool)ctx->GetOption(RSK_SELECTED_STARTING_AGE).Is(RO_AGE_ADULT)), }, { //Exits diff --git a/soh/soh/Enhancements/randomizer/option_descriptions.cpp b/soh/soh/Enhancements/randomizer/option_descriptions.cpp index 4ea791619..ea559f7a8 100644 --- a/soh/soh/Enhancements/randomizer/option_descriptions.cpp +++ b/soh/soh/Enhancements/randomizer/option_descriptions.cpp @@ -641,18 +641,18 @@ void Settings::CreateOptionDescriptions() { "Scarce - Some excess items are removed, including health upgrades.\n" "\n" "Minimal - Most excess items are removed."; - mOptionDescriptions[RSK_ICE_TRAPS] = "Sets how many items are replaced by ice traps.\n" - "\n" - "Off - No ice traps.\n" - "\n" - "Normal - Only Ice Traps from the base item pool are shuffled in.\n" - "\n" - "Extra - Chance to replace added junk items with additional ice traps.\n" - "\n" - "Mayhem - All added junk items will be Ice Traps.\n" - "\n" - "Onslaught - All junk items will be replaced by Ice Traps, even those " - "in the base pool."; + mOptionDescriptions[RSK_BASE_ICE_TRAPS] = + "Sets if ice traps that exist in vanilla are shuffled into the item pool.\n" + "If this is on, 1 Trap will always be added to the pool,\n" + "an additional trap will be added if Gerudo Training Grounds\n" + "is NOT master quest,\n" + "and 4 more will be added if Ganon's Castle is NOT Master Quest."; + mOptionDescriptions[RSK_ADDITIONAL_ICE_TRAPS] = + "Sets how many more Ice Traps will be added to item pool,\n" + "assuming there is enough space after placing Progression Items.\n\n" + "You do not need to have base ice traps on for this setting to work."; + mOptionDescriptions[RSK_ICE_TRAP_PERCENT] = + "If set above 0, each Junk item has that chance of being replaced with an extra Ice Trap."; mOptionDescriptions[RSK_GOSSIP_STONE_HINTS] = "Allows Gossip Stones to provide hints on item locations. Hints mentioning " "\"Way of the Hero\" indicate a location that holds an item required to beat " diff --git a/soh/soh/Enhancements/randomizer/randomizerTypes.h b/soh/soh/Enhancements/randomizer/randomizerTypes.h index bac5fa2ee..bf75c9321 100644 --- a/soh/soh/Enhancements/randomizer/randomizerTypes.h +++ b/soh/soh/Enhancements/randomizer/randomizerTypes.h @@ -6453,7 +6453,9 @@ typedef enum { RSK_SHUFFLE_BUSHES, RSK_SHUFFLE_FROG_SONG_RUPEES, RSK_ITEM_POOL, - RSK_ICE_TRAPS, + RSK_BASE_ICE_TRAPS, + RSK_ADDITIONAL_ICE_TRAPS, + RSK_ICE_TRAP_PERCENT, RSK_GOSSIP_STONE_HINTS, RSK_TOT_ALTAR_HINT, RSK_GANONDORF_HINT, @@ -6885,13 +6887,7 @@ typedef enum { } RandoOptionItemPool; // Ice Trap Settings -typedef enum { - RO_ICE_TRAPS_OFF, - RO_ICE_TRAPS_NORMAL, - RO_ICE_TRAPS_EXTRA, - RO_ICE_TRAPS_MAYHEM, - RO_ICE_TRAPS_ONSLAUGHT, -} RandoOptionIceTraps; +typedef enum { RO_ICE_TRAPS_OFF, RO_ICE_TRAPS_NORMAL, RO_ICE_TRAPS_COUNT, RO_ICE_TRAPS_PERCENT } RandoOptionIceTraps; // Gossip Stone Hint Settings (no hints, needs nothing, // needs mask of truth, needs stone of agony) diff --git a/soh/soh/Enhancements/randomizer/savefile.cpp b/soh/soh/Enhancements/randomizer/savefile.cpp index 0bcfa1038..f130f10a3 100644 --- a/soh/soh/Enhancements/randomizer/savefile.cpp +++ b/soh/soh/Enhancements/randomizer/savefile.cpp @@ -363,6 +363,10 @@ extern "C" void Randomizer_InitSaveFile() { if (Randomizer_GetSettingValue(RSK_SKIP_CHILD_ZELDA)) { GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(RC_SONG_FROM_IMPA, (GetItemID)RG_ZELDAS_LULLABY); StartingItemGive(getItemEntry, RC_SONG_FROM_IMPA); + getItemEntry = Randomizer_GetItemFromKnownCheck(RC_HC_MALON_EGG, (GetItemID)RG_WEIRD_EGG); + StartingItemGive(getItemEntry, RC_HC_ZELDAS_LETTER); + getItemEntry = Randomizer_GetItemFromKnownCheck(RC_HC_ZELDAS_LETTER, (GetItemID)RG_ZELDAS_LETTER); + StartingItemGive(getItemEntry, RC_HC_MALON_EGG); // Malon/Talon back at ranch. Flags_SetEventChkInf(EVENTCHKINF_OBTAINED_POCKET_EGG); diff --git a/soh/soh/Enhancements/randomizer/settings.cpp b/soh/soh/Enhancements/randomizer/settings.cpp index ba2fdbda2..52334e851 100644 --- a/soh/soh/Enhancements/randomizer/settings.cpp +++ b/soh/soh/Enhancements/randomizer/settings.cpp @@ -1236,7 +1236,9 @@ void Settings::CreateOptions() { OPT_BOOL(RSK_SKELETON_KEY, "Skeleton Key", CVAR_RANDOMIZER_SETTING("SkeletonKey"), mOptionDescriptions[RSK_SKELETON_KEY]); OPT_BOOL(RSK_SLINGBOW_BREAK_BEEHIVES, "Slingshot/Bow Can Break Beehives", CVAR_RANDOMIZER_SETTING("SlingBowBeehives"), mOptionDescriptions[RSK_SLINGBOW_BREAK_BEEHIVES]); OPT_U8(RSK_ITEM_POOL, "Item Pool", {"Plentiful", "Balanced", "Scarce", "Minimal"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ItemPool"), mOptionDescriptions[RSK_ITEM_POOL], WIDGET_CVAR_COMBOBOX, RO_ITEM_POOL_BALANCED); - OPT_U8(RSK_ICE_TRAPS, "Ice Traps", {"Off", "Normal", "Extra", "Mayhem", "Onslaught"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("IceTraps"), mOptionDescriptions[RSK_ICE_TRAPS], WIDGET_CVAR_COMBOBOX, RO_ICE_TRAPS_NORMAL); + OPT_BOOL(RSK_BASE_ICE_TRAPS, "Base Ice Traps", CVAR_RANDOMIZER_SETTING("BaseIceTraps"), mOptionDescriptions[RSK_BASE_ICE_TRAPS], IMFLAG_NONE, WIDGET_CVAR_COMBOBOX, RO_GENERIC_ON); + OPT_U8(RSK_ADDITIONAL_ICE_TRAPS, "Additional Ice Traps", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("AdditionalIceTraps"), mOptionDescriptions[RSK_ADDITIONAL_ICE_TRAPS], WIDGET_CVAR_SLIDER_INT, 0); + OPT_U8(RSK_ICE_TRAP_PERCENT, "Ice Trap Percent", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("IceTrapPercent"), mOptionDescriptions[RSK_ICE_TRAP_PERCENT], WIDGET_CVAR_SLIDER_INT, 0); // TODO: Remove Double Defense, Progressive Goron Sword OPT_U8(RSK_STARTING_OCARINA, "Start with Ocarina", {"Off", "Fairy Ocarina", "Ocarina of Time"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("StartingOcarina"), "", WIDGET_CVAR_COMBOBOX, RO_STARTING_OCARINA_OFF); OPT_BOOL(RSK_STARTING_DEKU_SHIELD, "Start with Deku Shield", CVAR_RANDOMIZER_SETTING("StartingDekuShield")); @@ -2422,7 +2424,9 @@ void Settings::CreateOptions() { WidgetContainerType::SECTION); mOptionGroups[RSG_MENU_SECTION_TRAPS] = OptionGroup::SubGroup("Traps", { - &mOptions[RSK_ICE_TRAPS], + &mOptions[RSK_BASE_ICE_TRAPS], + &mOptions[RSK_ADDITIONAL_ICE_TRAPS], + &mOptions[RSK_ICE_TRAP_PERCENT], }, WidgetContainerType::SECTION); mOptionGroups[RSG_MENU_COLUMN_HINTS_TRAPS] = @@ -2744,8 +2748,8 @@ void Settings::CreateOptions() { &mOptions[RSK_SKELETON_KEY], &mOptions[RSK_SLINGBOW_BREAK_BEEHIVES], }); - mOptionGroups[RSG_ITEM_POOL] = OptionGroup( - "Item Pool Settings", std::initializer_list({ &mOptions[RSK_ITEM_POOL], &mOptions[RSK_ICE_TRAPS] })); + mOptionGroups[RSG_ITEM_POOL] = + OptionGroup("Item Pool Settings", std::initializer_list({ &mOptions[RSK_ITEM_POOL] })); // TODO: Progressive Goron Sword, Remove Double Defense mOptionGroups[RSG_EXCLUDES_KOKIRI_FOREST] = OptionGroup::SubGroup("Kokiri Forest", mExcludeLocationsOptionsAreas[RCAREA_KOKIRI_FOREST]); diff --git a/soh/soh/Enhancements/randomizer/static_data.cpp b/soh/soh/Enhancements/randomizer/static_data.cpp index 7c5c53cdf..028055ffd 100644 --- a/soh/soh/Enhancements/randomizer/static_data.cpp +++ b/soh/soh/Enhancements/randomizer/static_data.cpp @@ -317,4 +317,58 @@ std::unordered_map StaticData::grottoChestParamsToHint{ }; std::array StaticData::hintTextTable = {}; + +std::vector StaticData::normalBottles = { + RG_EMPTY_BOTTLE, + RG_BOTTLE_WITH_MILK, + RG_BOTTLE_WITH_RED_POTION, + RG_BOTTLE_WITH_GREEN_POTION, + RG_BOTTLE_WITH_BLUE_POTION, + RG_BOTTLE_WITH_FAIRY, + RG_BOTTLE_WITH_FISH, + RG_BOTTLE_WITH_BUGS, + RG_BOTTLE_WITH_POE, + RG_BOTTLE_WITH_BIG_POE, + RG_BOTTLE_WITH_BLUE_FIRE, +}; + +std::vector StaticData::beanSouls = { + RG_DEATH_MOUNTAIN_CRATER_BEAN_SOUL, + RG_DEATH_MOUNTAIN_TRAIL_BEAN_SOUL, + RG_DESERT_COLOSSUS_BEAN_SOUL, + RG_GERUDO_VALLEY_BEAN_SOUL, + RG_GRAVEYARD_BEAN_SOUL, + RG_KOKIRI_FOREST_BEAN_SOUL, + RG_LAKE_HYLIA_BEAN_SOUL, + RG_LOST_WOODS_BRIDGE_BEAN_SOUL, + RG_LOST_WOODS_BEAN_SOUL, + RG_ZORAS_RIVER_BEAN_SOUL, +}; + +std::vector StaticData::overworldKeys = { + RG_GUARD_HOUSE_KEY, + RG_MARKET_BAZAAR_KEY, + RG_MARKET_POTION_SHOP_KEY, + RG_MASK_SHOP_KEY, + RG_MARKET_SHOOTING_GALLERY_KEY, + RG_BOMBCHU_BOWLING_KEY, + RG_TREASURE_CHEST_GAME_BUILDING_KEY, + RG_BOMBCHU_SHOP_KEY, + RG_RICHARDS_HOUSE_KEY, + RG_ALLEY_HOUSE_KEY, + RG_KAK_BAZAAR_KEY, + RG_KAK_POTION_SHOP_KEY, + RG_BOSS_HOUSE_KEY, + RG_GRANNYS_POTION_SHOP_KEY, + RG_SKULLTULA_HOUSE_KEY, + RG_IMPAS_HOUSE_KEY, + RG_WINDMILL_KEY, + RG_KAK_SHOOTING_GALLERY_KEY, + RG_DAMPES_HUT_KEY, + RG_TALONS_HOUSE_KEY, + RG_STABLES_KEY, + RG_BACK_TOWER_KEY, + RG_HYLIA_LAB_KEY, + RG_FISHING_HOLE_KEY, +}; } // namespace Rando diff --git a/soh/soh/Enhancements/randomizer/static_data.h b/soh/soh/Enhancements/randomizer/static_data.h index b01d150ff..0b837179b 100644 --- a/soh/soh/Enhancements/randomizer/static_data.h +++ b/soh/soh/Enhancements/randomizer/static_data.h @@ -83,6 +83,9 @@ class StaticData { static std::unordered_map stoneParamsToHint; static std::unordered_map grottoChestParamsToHint; static std::array hintTextTable; + static std::vector normalBottles; + static std::vector beanSouls; + static std::vector overworldKeys; StaticData(); ~StaticData();