From 7e829a078048700f0746b73cc681353f4934e147 Mon Sep 17 00:00:00 2001 From: Christopher Leggett Date: Thu, 4 Dec 2025 15:14:32 +0000 Subject: [PATCH] Add in Progressive Bombchu Bags (#5836) * Adds new Progressive Bombchu Bags option Also changes existing code to account for Bombchu Bags becoming a drop down with 3 values instead of a checkbox * Handle the new lower capacities for ammo refills * Handle what happens when receiving a bombchu bag * Remove the trick name of Progressive Bombchu Bag Since, you know, that's an actual thing now. * Implements Bombchu Bag handling next to Progressive Bombchu Handling * clang-formatting * Add extra bag for plentiful items * Address review comment * Move bombchu upgrade level to gSaveContext and add save/load * Use logic's saveContext for correct logic calculations * Remove RG_PROGRESSIVE_BOMBCHU * Fix Bombchu Refill obtainability * Don't add normal chus to the pool if chu bags are on. * cmake-format * Properly reset bombchuUpgradeLevel on savefile init * Fix error with va_arg on linux * Fixes small error in the Bombchu Bag description * Fix bug with bombchu obtainability * clang-format * Fix infinite bombchu text * fix clang-format --- soh/include/z64save.h | 1 + .../vanilla-behavior/GIVanillaBehavior.h | 22 ++++++++ .../Enhancements/randomizer/3drando/fill.cpp | 2 +- .../randomizer/3drando/item_pool.cpp | 22 +++++--- .../Enhancements/randomizer/3drando/shops.cpp | 4 +- .../randomizer/3drando/starting_inventory.cpp | 4 +- soh/soh/Enhancements/randomizer/Bombchus.cpp | 51 ++++++++++++++++++ .../Enhancements/randomizer/Plandomizer.cpp | 2 +- .../Enhancements/randomizer/ShuffleCrates.cpp | 4 +- .../Enhancements/randomizer/ShuffleTrees.cpp | 2 +- soh/soh/Enhancements/randomizer/context.cpp | 53 +++++++++++++++++++ soh/soh/Enhancements/randomizer/context.h | 2 + soh/soh/Enhancements/randomizer/draw.cpp | 2 +- .../Enhancements/randomizer/hook_handlers.cpp | 3 +- soh/soh/Enhancements/randomizer/item.cpp | 33 ++++++++---- soh/soh/Enhancements/randomizer/item_list.cpp | 5 +- soh/soh/Enhancements/randomizer/logic.cpp | 9 ++-- .../randomizer/option_descriptions.cpp | 18 ++++--- .../Enhancements/randomizer/randomizer.cpp | 52 +++++++++++------- .../Enhancements/randomizer/randomizerTypes.h | 5 +- soh/soh/Enhancements/randomizer/savefile.cpp | 3 ++ soh/soh/Enhancements/randomizer/settings.cpp | 2 +- soh/soh/SaveManager.cpp | 2 + soh/src/code/z_en_item00.c | 2 +- soh/src/code/z_parameter.c | 20 ++++--- soh/src/overlays/actors/ovl_En_Box/z_en_box.c | 2 +- .../overlays/actors/ovl_En_GirlA/z_en_girla.c | 12 ++--- .../misc/ovl_kaleido_scope/z_kaleido_item.c | 2 +- 28 files changed, 260 insertions(+), 81 deletions(-) create mode 100644 soh/soh/Enhancements/randomizer/Bombchus.cpp diff --git a/soh/include/z64save.h b/soh/include/z64save.h index c20bf2034..0abfe32d8 100644 --- a/soh/include/z64save.h +++ b/soh/include/z64save.h @@ -164,6 +164,7 @@ typedef struct { typedef struct ShipRandomizerSaveContextData { u8 triforcePiecesCollected; + u8 bombchuUpgradeLevel; } ShipRandomizerSaveContextData; typedef struct ShipBossRushSaveContextData { diff --git a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h index 62da37bcb..f604c9c79 100644 --- a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h +++ b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h @@ -2336,6 +2336,28 @@ typedef enum { // #### `result` // ```c + // false + // ``` + // #### `args` + // - *EnGirlACanBuyResult + VB_CAN_BUY_BOMBCHUS, + + // #### `result` + // ```c + // true + // ``` + // #### `args` + // - None + VB_CHECK_BOMBCHU_CAPACITY, + + // #### `result` + // ```c + // false + // ``` + // #### `args` + // - int16_t + VB_COLOR_AMMO_GREEN, + // (this->collider.base.acFlags & AC_HIT) && !Player_InCsMode(play) && // (player->meleeWeaponAnimation == 22 || player->meleeWeaponAnimation == 23) // ``` diff --git a/soh/soh/Enhancements/randomizer/3drando/fill.cpp b/soh/soh/Enhancements/randomizer/3drando/fill.cpp index 99deb9b70..63a5f96ee 100644 --- a/soh/soh/Enhancements/randomizer/3drando/fill.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/fill.cpp @@ -316,7 +316,7 @@ std::vector GetAllEmptyLocations() { } bool IsBombchus(RandomizerGet item, bool includeShops = false) { - return (item >= RG_BOMBCHU_5 && item <= RG_BOMBCHU_20) || item == RG_PROGRESSIVE_BOMBCHUS || + return (item >= RG_BOMBCHU_5 && item <= RG_BOMBCHU_20) || item == RG_PROGRESSIVE_BOMBCHU_BAG || (includeShops && (item == RG_BUY_BOMBCHUS_10 || item == RG_BUY_BOMBCHUS_20)); } diff --git a/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp b/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp index ea6123b24..ec90b0619 100644 --- a/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp @@ -408,7 +408,7 @@ static void PlaceItemsForType(RandomizerCheckType rctype, bool overworldActive, } static void SetScarceItemPool() { - ReplaceMaxItem(RG_PROGRESSIVE_BOMBCHUS, 3); + 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); @@ -424,7 +424,7 @@ static void SetScarceItemPool() { static void SetMinimalItemPool() { auto ctx = Rando::Context::GetInstance(); - ReplaceMaxItem(RG_PROGRESSIVE_BOMBCHUS, 1); + ReplaceMaxItem(RG_PROGRESSIVE_BOMBCHU_BAG, 1); ReplaceMaxItem(RG_BOMBCHU_5, 1); ReplaceMaxItem(RG_BOMBCHU_10, 0); ReplaceMaxItem(RG_BOMBCHU_20, 0); @@ -653,6 +653,9 @@ void GenerateItemPool() { 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 (ctx->GetOption(RSK_SHUFFLE_MERCHANTS).Is(RO_SHUFFLE_MERCHANTS_BEANS_ONLY) || @@ -671,9 +674,9 @@ void GenerateItemPool() { if (/*!ProgressiveGoronSword TODO: Implement Progressive Goron Sword*/ true) { AddItemToMainPool(RG_GIANTS_KNIFE); } - if (ctx->GetOption(RSK_BOMBCHU_BAG)) { - AddItemToMainPool(RG_PROGRESSIVE_BOMBCHUS); - } else { + if (ctx->GetOption(RSK_BOMBCHU_BAG).Is(RO_BOMBCHU_BAG_SINGLE)) { + AddItemToMainPool(RG_PROGRESSIVE_BOMBCHU_BAG); + } else if (ctx->GetOption(RSK_BOMBCHU_BAG).Is(RO_BOMBCHU_BAG_NONE)) { AddItemToMainPool(RG_BOMBCHU_10); } } else { @@ -785,8 +788,13 @@ void GenerateItemPool() { AddItemToMainPool(RG_PROGRESSIVE_NUT_UPGRADE); } - if (ctx->GetOption(RSK_BOMBCHU_BAG)) { - AddItemToMainPool(RG_PROGRESSIVE_BOMBCHUS, 5); + 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); diff --git a/soh/soh/Enhancements/randomizer/3drando/shops.cpp b/soh/soh/Enhancements/randomizer/3drando/shops.cpp index 0a6ff6ac9..06fe5790f 100644 --- a/soh/soh/Enhancements/randomizer/3drando/shops.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/shops.cpp @@ -563,14 +563,12 @@ void InitTrickNames() { Text{ "Progressive Rod Capacity", "Capacité de tiges (prog.)", "Stock-Kapazität (prog.)" }, // "Mayor capacidad de cetros deku" }; - trickNameTable[RG_PROGRESSIVE_BOMBCHUS] = { + trickNameTable[RG_PROGRESSIVE_BOMBCHU_BAG] = { Text{ "Progressive Bomblings", "Bombinsectes (prog.)", "Bombenmäuse (prog.)€" }, // "Bombinsectos progresivos" Text{ "Progressive Sentrobe Bombs", "Bombe de Sphérodrone (prog.)", "Rokopterbomben (prog.)€" }, // "Bomba de helicobot progresivo" Text{ "Progressive Bomb-ombs", "Bombe Soldat (prog.)", "Bob-omb (prog.)" }, // "Soldado bomba progresivo" Text{ "Progressive Missiles", "Missiles (prog.)", "Missiles (prog.)€" }, // "Misiles progresivos" - Text{ "Progressive Bombchu Bag", "Sac à Bombchu (prog.)", - "Krabbelminenbeutel (prog.)" }, // "Bombachus progresivos" }; trickNameTable[RG_PROGRESSIVE_MAGIC_METER] = { Text{ "Progressive Stamina Meter", "Jauge d'endurance (prog.)", diff --git a/soh/soh/Enhancements/randomizer/3drando/starting_inventory.cpp b/soh/soh/Enhancements/randomizer/3drando/starting_inventory.cpp index dda37b18a..f09e2f5c0 100644 --- a/soh/soh/Enhancements/randomizer/3drando/starting_inventory.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/starting_inventory.cpp @@ -73,8 +73,8 @@ void GenerateStartingInventory() { // AddItemToInventory(RG_PROGRESSIVE_STICK_UPGRADE, StartingStickCapacity.Value()); // AddItemToInventory(RG_PROGRESSIVE_NUT_UPGRADE, StartingNutCapacity.Value()); // AddItemToInventory(RG_PROGRESSIVE_BOMB_BAG, StartingBombBag.Value()); - // AddItemToInventory((BombchuBag ? RG_PROGRESSIVE_BOMBCHUS : RG_BOMBCHU_20), StartingBombchus.Value()); - // AddItemToInventory(RG_PROGRESSIVE_BOW, StartingBow.Value()); + // AddItemToInventory((BombchuBag ? RG_PROGRESSIVE_BOMBCHU_BAG : RG_BOMBCHU_20), + // StartingBombchus.Value()); AddItemToInventory(RG_PROGRESSIVE_BOW, StartingBow.Value()); // AddItemToInventory(RG_FIRE_ARROWS, StartingFireArrows.Value()); // AddItemToInventory(RG_ICE_ARROWS, StartingIceArrows.Value()); // AddItemToInventory(RG_LIGHT_ARROWS, StartingLightArrows.Value()); diff --git a/soh/soh/Enhancements/randomizer/Bombchus.cpp b/soh/soh/Enhancements/randomizer/Bombchus.cpp new file mode 100644 index 000000000..22a2aee84 --- /dev/null +++ b/soh/soh/Enhancements/randomizer/Bombchus.cpp @@ -0,0 +1,51 @@ +#include +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" +#include "soh/ShipInit.hpp" +#include "src/overlays/actors/ovl_En_GirlA/z_en_girla.h" + +extern "C" { +#include "z64save.h" +} + +void RegisterVBOverrides() { + bool shouldRegister = IS_RANDO && RAND_GET_OPTION(RSK_BOMBCHU_BAG) == RO_BOMBCHU_BAG_PROGRESSIVE; + COND_VB_SHOULD(VB_CAN_BUY_BOMBCHUS, IS_RANDO, { + EnGirlACanBuyResult* canBuy = va_arg(args, EnGirlACanBuyResult*); + u8 bombchuBag = RAND_GET_OPTION(RSK_BOMBCHU_BAG); + // When in rando, don't allow buying bombchus when the player doesn't have required explosives + // If bombchus are in logic, the player needs to have bombchus; otherwise they need a bomb bag + if ((!bombchuBag && CUR_CAPACITY(UPG_BOMB_BAG) == 0) || + (bombchuBag && INV_CONTENT(ITEM_BOMBCHU) == ITEM_NONE)) { + *canBuy = CANBUY_RESULT_CANT_GET_NOW; + *should = true; + } + int8_t capacity = 50; + if (bombchuBag == RO_BOMBCHU_BAG_PROGRESSIVE) { + capacity = OTRGlobals::Instance->gRandoContext->GetBombchuCapacity(); + } + if (AMMO(ITEM_BOMBCHU) >= capacity) { + *canBuy = CANBUY_RESULT_CANT_GET_NOW; + *should = true; + } + }); + + COND_VB_SHOULD(VB_CHECK_BOMBCHU_CAPACITY, shouldRegister, { + *should = false; + uint8_t capacity = OTRGlobals::Instance->gRandoContext->GetBombchuCapacity(); + if (AMMO(ITEM_BOMBCHU) > capacity) { + AMMO(ITEM_BOMBCHU) = capacity; + } + }); + + COND_VB_SHOULD(VB_COLOR_AMMO_GREEN, shouldRegister, { + int16_t i = va_arg(args, int); + if (i == ITEM_BOMBCHU) { + uint8_t capacity = OTRGlobals::Instance->gRandoContext->GetBombchuCapacity(); + if (AMMO(i) == capacity) { + *should = true; + } + } + }); +} + +static RegisterShipInitFunc initHooks(RegisterVBOverrides, { "IS_RANDO" }); \ No newline at end of file diff --git a/soh/soh/Enhancements/randomizer/Plandomizer.cpp b/soh/soh/Enhancements/randomizer/Plandomizer.cpp index 06bb69003..3cea3949f 100644 --- a/soh/soh/Enhancements/randomizer/Plandomizer.cpp +++ b/soh/soh/Enhancements/randomizer/Plandomizer.cpp @@ -135,7 +135,7 @@ std::unordered_map itemImageMap = { { RG_PROGRESSIVE_SCALE, "ITEM_SCALE_SILVER" }, { RG_PROGRESSIVE_NUT_UPGRADE, "ITEM_NUT" }, { RG_PROGRESSIVE_STICK_UPGRADE, "ITEM_STICK" }, - { RG_PROGRESSIVE_BOMBCHUS, "ITEM_BOMBCHU" }, + { RG_PROGRESSIVE_BOMBCHU_BAG, "ITEM_BOMBCHU" }, { RG_PROGRESSIVE_MAGIC_METER, "ITEM_MAGIC_SMALL" }, { RG_MAGIC_SINGLE, "ITEM_MAGIC_SMALL" }, { RG_MAGIC_DOUBLE, "ITEM_MAGIC_LARGE" }, diff --git a/soh/soh/Enhancements/randomizer/ShuffleCrates.cpp b/soh/soh/Enhancements/randomizer/ShuffleCrates.cpp index 007c12bc0..1762824d9 100644 --- a/soh/soh/Enhancements/randomizer/ShuffleCrates.cpp +++ b/soh/soh/Enhancements/randomizer/ShuffleCrates.cpp @@ -44,7 +44,7 @@ extern "C" void ObjKibako2_RandomizerDraw(Actor* thisx, PlayState* play) { // If they have bombchus, don't consider the bombchu item major if (INV_CONTENT(ITEM_BOMBCHU) == ITEM_BOMBCHU && - ((crateItem.modIndex == MOD_RANDOMIZER && crateItem.getItemId == RG_PROGRESSIVE_BOMBCHUS) || + ((crateItem.modIndex == MOD_RANDOMIZER && crateItem.getItemId == RG_PROGRESSIVE_BOMBCHU_BAG) || (crateItem.modIndex == MOD_NONE && (crateItem.getItemId == GI_BOMBCHUS_5 || crateItem.getItemId == GI_BOMBCHUS_10 || crateItem.getItemId == GI_BOMBCHUS_20)))) { @@ -118,7 +118,7 @@ extern "C" void ObjKibako_RandomizerDraw(Actor* thisx, PlayState* play) { // If they have bombchus, don't consider the bombchu item major if (INV_CONTENT(ITEM_BOMBCHU) == ITEM_BOMBCHU && - ((smallCrateItem.modIndex == MOD_RANDOMIZER && smallCrateItem.getItemId == RG_PROGRESSIVE_BOMBCHUS) || + ((smallCrateItem.modIndex == MOD_RANDOMIZER && smallCrateItem.getItemId == RG_PROGRESSIVE_BOMBCHU_BAG) || (smallCrateItem.modIndex == MOD_NONE && (smallCrateItem.getItemId == GI_BOMBCHUS_5 || smallCrateItem.getItemId == GI_BOMBCHUS_10 || smallCrateItem.getItemId == GI_BOMBCHUS_20)))) { diff --git a/soh/soh/Enhancements/randomizer/ShuffleTrees.cpp b/soh/soh/Enhancements/randomizer/ShuffleTrees.cpp index 14cb9c7b1..7355fc505 100644 --- a/soh/soh/Enhancements/randomizer/ShuffleTrees.cpp +++ b/soh/soh/Enhancements/randomizer/ShuffleTrees.cpp @@ -69,7 +69,7 @@ extern "C" void EnWood02_RandomizerDraw(Actor* thisx, PlayState* play) { // If they have bombchus, don't consider the bombchu item major if (INV_CONTENT(ITEM_BOMBCHU) == ITEM_BOMBCHU && - ((smallCrateItem.modIndex == MOD_RANDOMIZER && smallCrateItem.getItemId == RG_PROGRESSIVE_BOMBCHUS) || + ((smallCrateItem.modIndex == MOD_RANDOMIZER && smallCrateItem.getItemId == RG_PROGRESSIVE_BOMBCHU_BAG) || (smallCrateItem.modIndex == MOD_NONE && (smallCrateItem.getItemId == GI_BOMBCHUS_5 || smallCrateItem.getItemId == GI_BOMBCHUS_10 || smallCrateItem.getItemId == GI_BOMBCHUS_20)))) { diff --git a/soh/soh/Enhancements/randomizer/context.cpp b/soh/soh/Enhancements/randomizer/context.cpp index ef6808625..f821c8ff7 100644 --- a/soh/soh/Enhancements/randomizer/context.cpp +++ b/soh/soh/Enhancements/randomizer/context.cpp @@ -16,6 +16,9 @@ #include #include +extern "C" { +#include +} namespace Rando { std::weak_ptr Context::mContext; @@ -551,6 +554,56 @@ void Context::SetHash(std::string hash) { mHash = std::move(hash); } +uint8_t Context::GetBombchuCapacity() { + switch (mLogic->GetSaveContext()->ship.quest.data.randomizer.bombchuUpgradeLevel) { + case 0: + return 0; + case 1: + return 20; + case 2: + return 30; + case 3: + return 50; + default: + return 0; + } +} + +void Context::HandleGetBombchuBag() { + if (GetOption(RSK_BOMBCHU_BAG).Is(RO_BOMBCHU_BAG_SINGLE)) { + if (INV_CONTENT(ITEM_BOMBCHU) == ITEM_NONE) { + INV_CONTENT(ITEM_BOMBCHU) = ITEM_BOMBCHU; + AMMO(ITEM_BOMBCHU) = 20; + } else if (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_INFINITE_UPGRADES)) { + Flags_SetRandomizerInf(RAND_INF_HAS_INFINITE_BOMBCHUS); + } else { + AMMO(ITEM_BOMBCHU) += 10; + if (AMMO(ITEM_BOMBCHU) > 50) { + AMMO(ITEM_BOMBCHU) = 50; + } + } + return; + } + switch (mLogic->GetSaveContext()->ship.quest.data.randomizer.bombchuUpgradeLevel) { + case 0: + case 1: + case 2: + mLogic->GetSaveContext()->ship.quest.data.randomizer.bombchuUpgradeLevel++; + if (INV_CONTENT(ITEM_BOMBCHU) == ITEM_NONE) { + INV_CONTENT(ITEM_BOMBCHU) = ITEM_BOMBCHU; + } else if (GetOption(RSK_INFINITE_UPGRADES).Is(RO_INF_UPGRADES_CONDENSED_PROGRESSIVE)) { + Flags_SetRandomizerInf(RAND_INF_HAS_INFINITE_BOMBCHUS); + } + AMMO(ITEM_BOMBCHU) = GetBombchuCapacity(); + return; + case 3: + if (GetOption(RSK_INFINITE_UPGRADES).IsNot(RO_INF_UPGRADES_OFF)) { + Flags_SetRandomizerInf(RAND_INF_HAS_INFINITE_BOMBCHUS); + } + return; + } +} + const std::string& Context::GetSeedString() const { return mSeedString; } diff --git a/soh/soh/Enhancements/randomizer/context.h b/soh/soh/Enhancements/randomizer/context.h index 968947756..bd20817f5 100644 --- a/soh/soh/Enhancements/randomizer/context.h +++ b/soh/soh/Enhancements/randomizer/context.h @@ -174,6 +174,8 @@ class Context { * @param hash */ void SetHash(std::string hash); + uint8_t GetBombchuCapacity(); + void HandleGetBombchuBag(); private: static std::weak_ptr mContext; diff --git a/soh/soh/Enhancements/randomizer/draw.cpp b/soh/soh/Enhancements/randomizer/draw.cpp index b17718b65..7d266d78b 100644 --- a/soh/soh/Enhancements/randomizer/draw.cpp +++ b/soh/soh/Enhancements/randomizer/draw.cpp @@ -1196,7 +1196,7 @@ extern "C" void Randomizer_DrawBombchuBag(PlayState* play, GetItemEntry* getItem } extern "C" void Randomizer_DrawBombchuBagInLogic(PlayState* play, GetItemEntry* getItemEntry) { - if (IS_RANDO && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_BOMBCHU_BAG)) { + if (IS_RANDO && OTRGlobals::Instance->gRandoContext->GetOption(RSK_BOMBCHU_BAG).IsNot(RO_BOMBCHU_BAG_NONE)) { Randomizer_DrawBombchuBag(play, getItemEntry); } else { OPEN_DISPS(play->state.gfxCtx); diff --git a/soh/soh/Enhancements/randomizer/hook_handlers.cpp b/soh/soh/Enhancements/randomizer/hook_handlers.cpp index 95a20083a..fcadbb534 100644 --- a/soh/soh/Enhancements/randomizer/hook_handlers.cpp +++ b/soh/soh/Enhancements/randomizer/hook_handlers.cpp @@ -1183,7 +1183,8 @@ void RandomizerOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_l break; } case VB_GIVE_BOMBCHUS_FROM_CARPET_SALESMAN: { - *should = RAND_GET_OPTION(RSK_BOMBCHU_BAG) == false || INV_CONTENT(ITEM_BOMBCHU) == ITEM_BOMBCHU; + *should = + RAND_GET_OPTION(RSK_BOMBCHU_BAG) == RO_BOMBCHU_BAG_NONE || INV_CONTENT(ITEM_BOMBCHU) == ITEM_BOMBCHU; break; } case VB_CHECK_RANDO_PRICE_OF_MEDIGORON: { diff --git a/soh/soh/Enhancements/randomizer/item.cpp b/soh/soh/Enhancements/randomizer/item.cpp index 5207b9734..abe824572 100644 --- a/soh/soh/Enhancements/randomizer/item.cpp +++ b/soh/soh/Enhancements/randomizer/item.cpp @@ -93,7 +93,7 @@ uint16_t Item::GetPrice() const { } std::shared_ptr Item::GetGIEntry() const { // NOLINT(*-no-recursion) - if (giEntry != nullptr) { + if (giEntry != nullptr && giEntry->itemId != RG_PROGRESSIVE_BOMBCHU_BAG) { return giEntry; } std::shared_ptr ctx = Rando::Context::GetInstance(); @@ -355,19 +355,34 @@ std::shared_ptr Item::GetGIEntry() const { // NOLINT(*-no-recursio case RG_PROGRESSIVE_GORONSWORD: // todo progressive? actual = RG_BIGGORON_SWORD; break; - case RG_PROGRESSIVE_BOMBCHUS: - if (logic->CurrentInventory(ITEM_BOMBCHU) == ITEM_NONE) { - actual = RG_BOMBCHU_BAG; - } else if (infiniteUpgrades != RO_INF_UPGRADES_OFF) { - actual = RG_BOMBCHU_INF; - } else { - actual = RG_BOMBCHU_10; + case RG_PROGRESSIVE_BOMBCHU_BAG: + if (OTRGlobals::Instance->gRandoContext->GetOption(RSK_BOMBCHU_BAG).Is(RO_BOMBCHU_BAG_SINGLE)) { + if (logic->CurrentInventory(ITEM_BOMBCHU) != ITEM_NONE) { + if (infiniteUpgrades != RO_INF_UPGRADES_OFF) { + actual = RG_BOMBCHU_INF; + } else { + actual = RG_BOMBCHU_10; + } + } + } else if (OTRGlobals::Instance->gRandoContext->GetOption(RSK_BOMBCHU_BAG).Is(RO_BOMBCHU_BAG_PROGRESSIVE)) { + if (logic->CurrentInventory(ITEM_BOMBCHU) != ITEM_NONE) { + if (infiniteUpgrades == RO_INF_UPGRADES_CONDENSED_PROGRESSIVE) { + actual = RG_BOMBCHU_INF; + } else if (infiniteUpgrades == RO_INF_UPGRADES_PROGRESSIVE) { + if (logic->GetSaveContext()->ship.quest.data.randomizer.bombchuUpgradeLevel >= 3) { + actual = RG_BOMBCHU_INF; + } + } + } } break; default: actual = RG_NONE; break; } + if (giEntry != nullptr && actual == RG_NONE) { + return giEntry; + } return StaticData::RetrieveItem(actual).GetGIEntry(); } @@ -414,7 +429,7 @@ bool Item::IsMajorItem() const { } if ((randomizerGet == RG_BOMBCHU_5 || randomizerGet == RG_BOMBCHU_10 || randomizerGet == RG_BOMBCHU_20) && - !ctx->GetOption(RSK_BOMBCHU_BAG)) { + ctx->GetOption(RSK_BOMBCHU_BAG).Is(RO_BOMBCHU_BAG_NONE)) { return false; } diff --git a/soh/soh/Enhancements/randomizer/item_list.cpp b/soh/soh/Enhancements/randomizer/item_list.cpp index 3f6535c83..9aa9b1400 100644 --- a/soh/soh/Enhancements/randomizer/item_list.cpp +++ b/soh/soh/Enhancements/randomizer/item_list.cpp @@ -68,7 +68,6 @@ void Rando::StaticData::InitItemTable() { itemTable[RG_PROGRESSIVE_SCALE] = Item(RG_PROGRESSIVE_SCALE, Text{ "Progressive Scale", "Écaille (prog.)", "Zora-Schuppe (prog.)" }, ITEMTYPE_ITEM, 0x86, true, LOGIC_PROGRESSIVE_SCALE, RHT_PROGRESSIVE_SCALE, ITEM_CATEGORY_MAJOR, true); itemTable[RG_PROGRESSIVE_NUT_UPGRADE] = Item(RG_PROGRESSIVE_NUT_UPGRADE, Text{ "Progressive Nut Capacity", "Capacité de Noix (prog.)", "Nuß-Kapazität (prog.)" }, ITEMTYPE_ITEM, 0x87, true, LOGIC_PROGRESSIVE_NUT_BAG, RHT_PROGRESSIVE_NUT_UPGRADE, ITEM_CATEGORY_MAJOR, true); itemTable[RG_PROGRESSIVE_STICK_UPGRADE] = Item(RG_PROGRESSIVE_STICK_UPGRADE, Text{ "Progressive Stick Capacity", "Capacité de Bâtons (prog.)", "Stab-Kapazität (prog.)" }, ITEMTYPE_ITEM, 0x88, true, LOGIC_PROGRESSIVE_STICK_BAG, RHT_PROGRESSIVE_STICK_UPGRADE, ITEM_CATEGORY_MAJOR, true); - itemTable[RG_PROGRESSIVE_BOMBCHUS] = Item(RG_PROGRESSIVE_BOMBCHUS, Text{ "Progressive Bombchu", "Missiles (prog.)", "Krabbelminen (prog.)" }, ITEMTYPE_ITEM, 0x89, true, LOGIC_BOMBCHUS, RHT_PROGRESSIVE_BOMBCHUS, ITEM_CATEGORY_MAJOR, true); itemTable[RG_PROGRESSIVE_MAGIC_METER] = Item(RG_PROGRESSIVE_MAGIC_METER, Text{ "Progressive Magic Meter", "Jauge de Magie (prog.)", "Magische Kraft (prog.)" }, ITEMTYPE_ITEM, 0x8A, true, LOGIC_PROGRESSIVE_MAGIC, RHT_PROGRESSIVE_MAGIC_METER, ITEM_CATEGORY_MAJOR, true); itemTable[RG_PROGRESSIVE_OCARINA] = Item(RG_PROGRESSIVE_OCARINA, Text{ "Progressive Ocarina", "Ocarina (prog.)", "Okarina (prog.)" }, ITEMTYPE_ITEM, 0x8B, true, LOGIC_PROGRESSIVE_OCARINA, RHT_PROGRESSIVE_OCARINA, ITEM_CATEGORY_MAJOR, true); itemTable[RG_PROGRESSIVE_GORONSWORD] = Item(RG_PROGRESSIVE_GORONSWORD, Text{ "Progressive Goron Sword", "Épée Goron (prog.)", "Goronen-Schwert (prog.)" }, ITEMTYPE_ITEM, 0xD4, true, LOGIC_PROGRESSIVE_GIANT_KNIFE, RHT_PROGRESSIVE_GORONSWORD, ITEM_CATEGORY_MAJOR, true); @@ -352,8 +351,8 @@ void Rando::StaticData::InitItemTable() { itemTable[RG_BRONZE_SCALE] = Item(RG_BRONZE_SCALE, Text{ "Bronze Scale", "Écaille de Bronze", "Bronzene Schuppe" }, ITEMTYPE_ITEM, GI_SCALE_SILVER, true, LOGIC_PROGRESSIVE_WALLET, RHT_BRONZE_SCALE, RG_BRONZE_SCALE, OBJECT_GI_SCALE, GID_SCALE_SILVER, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER); itemTable[RG_BRONZE_SCALE].SetCustomDrawFunc(Randomizer_DrawBronzeScale); - itemTable[RG_BOMBCHU_BAG] = Item(RG_BOMBCHU_BAG, Text{ "Bombchu Bag", "Sac de Missiles Teigneux", "Krabbelminentasche" }, ITEMTYPE_ITEM, RG_BOMBCHU_BAG, true, LOGIC_BOMBCHUS, RHT_BOMBCHU_BAG, RG_BOMBCHU_BAG, OBJECT_GI_BOMB_2, GID_BOMBCHU, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER); - itemTable[RG_BOMBCHU_BAG].SetCustomDrawFunc(Randomizer_DrawBombchuBag); + itemTable[RG_PROGRESSIVE_BOMBCHU_BAG] = Item(RG_PROGRESSIVE_BOMBCHU_BAG, Text{ "Bombchu Bag", "Sac de Missiles Teigneux", "Krabbelminentasche" }, ITEMTYPE_ITEM, RG_PROGRESSIVE_BOMBCHU_BAG, true, LOGIC_BOMBCHUS, RHT_BOMBCHU_BAG, RG_PROGRESSIVE_BOMBCHU_BAG, OBJECT_GI_BOMB_2, GID_BOMBCHU, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER); + itemTable[RG_PROGRESSIVE_BOMBCHU_BAG].SetCustomDrawFunc(Randomizer_DrawBombchuBag); itemTable[RG_QUIVER_INF] = Item(RG_QUIVER_INF, Text{ "Infinite Quiver", "Carquois Infini", "Unendlicher Köcher" }, ITEMTYPE_ITEM, RG_QUIVER_INF, true, LOGIC_PROGRESSIVE_BOW, RHT_QUIVER_INF, RG_QUIVER_INF, OBJECT_GI_ARROWCASE, GID_QUIVER_50, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_LESSER, MOD_RANDOMIZER); itemTable[RG_BOMB_BAG_INF] = Item(RG_BOMB_BAG_INF, Text{ "Infinite Bomb Bag", "Sac de Bombes Infini", "Unendliche Bombentasche" }, ITEMTYPE_ITEM, RG_BOMB_BAG_INF, true, LOGIC_PROGRESSIVE_BOMB_BAG, RHT_BOMB_BAG_INF, RG_BOMB_BAG_INF, OBJECT_GI_BOMBPOUCH, GID_BOMB_BAG_40, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_LESSER, MOD_RANDOMIZER); diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index 6c359d529..b831d4c27 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -52,7 +52,7 @@ bool Logic::HasItem(RandomizerGet itemName) { return CheckInventory(ITEM_ARROW_ICE, true); case RG_LIGHT_ARROWS: return CheckInventory(ITEM_ARROW_LIGHT, true); - case RG_PROGRESSIVE_BOMBCHUS: + case RG_PROGRESSIVE_BOMBCHU_BAG: case RG_BOMBCHU_5: case RG_BOMBCHU_10: case RG_BOMBCHU_20: @@ -332,7 +332,7 @@ bool Logic::CanUse(RandomizerGet itemName) { case RG_PROGRESSIVE_BOMB_BAG: case RG_BOMB_BAG: return true; // AmmoCanDrop || Get(LOGIC_BUY_BOMB) - case RG_PROGRESSIVE_BOMBCHUS: + case RG_PROGRESSIVE_BOMBCHU_BAG: case RG_BOMBCHU_5: case RG_BOMBCHU_10: case RG_BOMBCHU_20: @@ -1129,7 +1129,8 @@ bool Logic::CanAttack() { } bool Logic::BombchusEnabled() { - return ctx->GetOption(RSK_BOMBCHU_BAG) ? CheckInventory(ITEM_BOMBCHU, true) : HasItem(RG_BOMB_BAG); + return ctx->GetOption(RSK_BOMBCHU_BAG).IsNot(RO_BOMBCHU_BAG_NONE) ? CheckInventory(ITEM_BOMBCHU, true) + : HasItem(RG_BOMB_BAG); } // TODO: Implement Ammo Drop Setting in place of bombchu drops @@ -1686,7 +1687,7 @@ void Logic::ApplyItemEffect(Item& item, bool state) { } SetUpgrade(UPG_STICKS, newLevel); } break; - case RG_PROGRESSIVE_BOMBCHUS: { + case RG_PROGRESSIVE_BOMBCHU_BAG: { auto realGI = item.GetGIEntry(); if (realGI->itemId == RG_BOMBCHU_INF && realGI->modIndex == MOD_RANDOMIZER) { SetRandoInf(RAND_INF_HAS_INFINITE_BOMBCHUS, true); diff --git a/soh/soh/Enhancements/randomizer/option_descriptions.cpp b/soh/soh/Enhancements/randomizer/option_descriptions.cpp index c544d3b29..1f6b71a88 100644 --- a/soh/soh/Enhancements/randomizer/option_descriptions.cpp +++ b/soh/soh/Enhancements/randomizer/option_descriptions.cpp @@ -738,13 +738,17 @@ void Settings::CreateOptionDescriptions() { "Reading the mask shop sign will tell you rewards from showing masks at the Deku Theatre."; mOptionDescriptions[RSK_FULL_WALLETS] = "Start with a full wallet. All wallet upgrades come filled with rupees."; mOptionDescriptions[RSK_BOMBCHU_BAG] = - "Bombchus require their own bag to be found before use. Without this setting, any Bombchu requirement " - "is filled by Bomb Bag + a renewable source of Bombchus.\n" - "\n" - "The first Bombchu you find be a Bag containing 20 chus, and subsequent packs will have 10." - "Once found, they can be replenished at shops selling refills, Bombchu Bowling and the carpet merchant.\n" - "\n" - "Bombchu Bowling is opened by obtaining the Bombchu Bag."; + "None - Bombchus have vanilla behavior, any Bombchu requirement is filled by Bomb Bag + a renewable source of " + "Bombchus.\n\n" + "Single Bag - Bombchus require their own bag to be found before use. 5 of them are added to the pool " + "(6 if the Carpet Merchant is shuffled). The first Bombchu Bag you find will be a Bag containing 20 chus, " + "and subsequent bags will be replaced with Bombchu Ammo refills. Once found, they can be replenished at " + "shops selling refills, Bombchu Bowling and the carpet merchant. Bombchu Bowling is opened by obtaining " + "the Bombchu Bag.\n\n" + "Progressive Bags - 3 Bombchu Bags are added to the pool, the first one will unlock Bombchus with a capacity " + "of 20. The second one will upgrade this capacity to 30, and the final one will upgrade the capacity to the " + "usual 50.\n\n" + "Bombchu Bowling is opened by obtaining the first Bombchu bag."; mOptionDescriptions[RSK_ENABLE_BOMBCHU_DROPS] = "Once you obtain a Bombchu Bag, refills will sometimes replace " "Bomb drops that would spawn." "\n" diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index cf30a53b3..de17327f1 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -921,9 +921,36 @@ ItemObtainability Randomizer::GetItemObtainabilityFromRandomizerGet(RandomizerGe case RG_BOMBCHU_20: case RG_BUY_BOMBCHUS_10: case RG_BUY_BOMBCHUS_20: - case RG_PROGRESSIVE_BOMBCHUS: // RANDOTODO Do we want bombchu refills to exist seperatly from bombchu bags? If - // so, this needs changing. - return CAN_OBTAIN; + return OTRGlobals::Instance->gRandoContext->GetOption(RSK_BOMBCHU_BAG).Is(RO_BOMBCHU_BAG_NONE) + ? CAN_OBTAIN + : (INV_CONTENT(ITEM_BOMBCHU) != ITEM_NONE ? CAN_OBTAIN : CANT_OBTAIN_NEED_UPGRADE); + case RG_PROGRESSIVE_BOMBCHU_BAG: // RANDOTODO Do we want bombchu refills to exist seperatly from bombchu bags? + // If so, this needs changing. + switch (OTRGlobals::Instance->gRandoContext->GetOption(RSK_BOMBCHU_BAG).Get()) { + case RO_BOMBCHU_BAG_NONE: + return CANT_OBTAIN_MISC; + case RO_BOMBCHU_BAG_SINGLE: + return INV_CONTENT(ITEM_BOMBCHU) == ITEM_BOMBCHU + ? (infiniteUpgrades != RO_INF_UPGRADES_OFF ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE) + : CAN_OBTAIN; + case RO_BOMBCHU_BAG_PROGRESSIVE: + if (Flags_GetRandomizerInf(RAND_INF_HAS_INFINITE_BOMBCHUS)) { + return CANT_OBTAIN_ALREADY_HAVE; + } else { + switch (gSaveContext.ship.quest.data.randomizer.bombchuUpgradeLevel) { + case 0: + case 1: + return CAN_OBTAIN; + case 2: + return infiniteUpgrades == RO_INF_UPGRADES_CONDENSED_PROGRESSIVE + ? CANT_OBTAIN_ALREADY_HAVE + : CAN_OBTAIN; + case 3: + return infiniteUpgrades == RO_INF_UPGRADES_PROGRESSIVE ? CAN_OBTAIN + : CANT_OBTAIN_ALREADY_HAVE; + } + } + } case RG_PROGRESSIVE_HOOKSHOT: switch (INV_CONTENT(ITEM_HOOKSHOT)) { case ITEM_NONE: @@ -5815,7 +5842,7 @@ void Randomizer::CreateCustomMessages() { GIMESSAGE(RG_FISHING_POLE, ITEM_FISHING_POLE, "You found a lost %rFishing Pole%w!&Time to hit the pond!", "Du hast eine verlorene %rAngelrute%w&gefunden!&Zeit, im Teich&zu angeln!", "Vous obtenez une %rCanne à pêche%w&perdue!&Il est temps d'aller à %gl'étang%w!"), - GIMESSAGE(RG_BOMBCHU_BAG, ITEM_BOMBCHU, "You found the %rBombchu Bag%w!", + GIMESSAGE(RG_PROGRESSIVE_BOMBCHU_BAG, ITEM_BOMBCHU, "You found the %rBombchu Bag%w!", "Du hast die %rKrabbelminentasche%w&gefunden!", "Vous obtenez un %rSac de Missiles&Teigneux%w!"), GIMESSAGE( RG_BOMB_BAG_INF, ITEM_BOMB_BAG_40, "You got an %rInfinite Bomb Bag%w!&Now you have %yinfinite bombs%w!", @@ -5958,7 +5985,7 @@ void Randomizer_GameplayStats_SetTimestamp(uint16_t item) { } // Count any bombchu pack as bombchus - if ((item >= RG_BOMBCHU_5 && item <= RG_BOMBCHU_20) || item == RG_PROGRESSIVE_BOMBCHUS) { + if ((item >= RG_BOMBCHU_5 && item <= RG_BOMBCHU_20) || item == RG_PROGRESSIVE_BOMBCHU_BAG) { if (gSaveContext.ship.stats.itemTimestamp[ITEM_BOMBCHU] = 0) { gSaveContext.ship.stats.itemTimestamp[ITEM_BOMBCHU] = time; } @@ -6273,19 +6300,8 @@ extern "C" u16 Randomizer_Item_Give(PlayState* play, GetItemEntry giEntry) { } break; - case RG_PROGRESSIVE_BOMBCHUS: - case RG_BOMBCHU_BAG: - if (INV_CONTENT(ITEM_BOMBCHU) == ITEM_NONE) { - INV_CONTENT(ITEM_BOMBCHU) = ITEM_BOMBCHU; - AMMO(ITEM_BOMBCHU) = 20; - } else if (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_INFINITE_UPGRADES)) { - Flags_SetRandomizerInf(RAND_INF_HAS_INFINITE_BOMBCHUS); - } else { - AMMO(ITEM_BOMBCHU) += 10; - if (AMMO(ITEM_BOMBCHU) > 50) { - AMMO(ITEM_BOMBCHU) = 50; - } - } + case RG_PROGRESSIVE_BOMBCHU_BAG: + OTRGlobals::Instance->gRandoContext->HandleGetBombchuBag(); break; case RG_MASTER_SWORD: if (!CHECK_OWNED_EQUIP(EQUIP_TYPE_SWORD, EQUIP_INV_SWORD_MASTER)) { diff --git a/soh/soh/Enhancements/randomizer/randomizerTypes.h b/soh/soh/Enhancements/randomizer/randomizerTypes.h index ec8e05d05..02bffe53e 100644 --- a/soh/soh/Enhancements/randomizer/randomizerTypes.h +++ b/soh/soh/Enhancements/randomizer/randomizerTypes.h @@ -4105,7 +4105,6 @@ typedef enum { RG_PROGRESSIVE_SCALE, RG_PROGRESSIVE_NUT_UPGRADE, RG_PROGRESSIVE_STICK_UPGRADE, - RG_PROGRESSIVE_BOMBCHUS, RG_PROGRESSIVE_MAGIC_METER, RG_MAGIC_SINGLE, // Added for refactor of GetItemEntries RG_MAGIC_DOUBLE, // Added for refactor of GetItemEntries @@ -4274,7 +4273,7 @@ typedef enum { RG_TYCOON_WALLET, RG_BRONZE_SCALE, RG_CHILD_WALLET, - RG_BOMBCHU_BAG, + RG_PROGRESSIVE_BOMBCHU_BAG, RG_QUIVER_INF, RG_BOMB_BAG_INF, RG_BULLET_BAG_INF, @@ -6377,6 +6376,8 @@ typedef enum { RO_AMMO_DROPS_ON, } RandoOptionAmmoDrops; +typedef enum { RO_BOMBCHU_BAG_NONE, RO_BOMBCHU_BAG_SINGLE, RO_BOMBCHU_BAG_PROGRESSIVE } RandoOptionBombchuBag; + typedef enum { RO_BOSS_SOULS_OFF, RO_BOSS_SOULS_ON, diff --git a/soh/soh/Enhancements/randomizer/savefile.cpp b/soh/soh/Enhancements/randomizer/savefile.cpp index 106cecb11..5e52d48ef 100644 --- a/soh/soh/Enhancements/randomizer/savefile.cpp +++ b/soh/soh/Enhancements/randomizer/savefile.cpp @@ -232,6 +232,9 @@ extern "C" void Randomizer_InitSaveFile() { // Reset triforce pieces collected. gSaveContext.ship.quest.data.randomizer.triforcePiecesCollected = 0; + // Reset Bombchu Bag Upgrade + gSaveContext.ship.quest.data.randomizer.bombchuUpgradeLevel = 0; + SetStartingItems(); // Set Cutscene flags and texts to skip them. diff --git a/soh/soh/Enhancements/randomizer/settings.cpp b/soh/soh/Enhancements/randomizer/settings.cpp index 68bed88c8..265066a7e 100644 --- a/soh/soh/Enhancements/randomizer/settings.cpp +++ b/soh/soh/Enhancements/randomizer/settings.cpp @@ -157,7 +157,7 @@ void Settings::CreateOptions() { OPT_BOOL(RSK_MIX_THIEVES_HIDEOUT_ENTRANCES, "Mix Thieves' Hideout", CVAR_RANDOMIZER_SETTING("MixThievesHideout"), mOptionDescriptions[RSK_MIX_THIEVES_HIDEOUT_ENTRANCES]); OPT_BOOL(RSK_MIX_GROTTO_ENTRANCES, "Mix Grottos", CVAR_RANDOMIZER_SETTING("MixGrottos"), mOptionDescriptions[RSK_MIX_GROTTO_ENTRANCES]); OPT_BOOL(RSK_DECOUPLED_ENTRANCES, "Decouple Entrances", CVAR_RANDOMIZER_SETTING("DecoupleEntrances"), mOptionDescriptions[RSK_DECOUPLED_ENTRANCES]); - OPT_BOOL(RSK_BOMBCHU_BAG, "Bombchu Bag", CVAR_RANDOMIZER_SETTING("BombchuBag"), mOptionDescriptions[RSK_BOMBCHU_BAG]); + OPT_U8(RSK_BOMBCHU_BAG, "Bombchu Bag", {"None", "Single Bag", "Progressive Bags"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("BombchuBag"), mOptionDescriptions[RSK_BOMBCHU_BAG], WidgetType::Combobox, RO_BOMBCHU_BAG_NONE); OPT_U8(RSK_ENABLE_BOMBCHU_DROPS, "Bombchu Drops", {"No", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("EnableBombchuDrops"), mOptionDescriptions[RSK_ENABLE_BOMBCHU_DROPS], WidgetType::Combobox, RO_AMMO_DROPS_ON); // TODO: AmmoDrops and/or HeartDropRefill, combine with/separate Ammo Drops from Bombchu Drops? OPT_BOOL(RSK_TRIFORCE_HUNT, "Triforce Hunt", CVAR_RANDOMIZER_SETTING("TriforceHunt"), mOptionDescriptions[RSK_TRIFORCE_HUNT], IMFLAG_NONE); diff --git a/soh/soh/SaveManager.cpp b/soh/soh/SaveManager.cpp index b05770c54..9317f8cbc 100644 --- a/soh/soh/SaveManager.cpp +++ b/soh/soh/SaveManager.cpp @@ -229,6 +229,7 @@ void SaveManager::LoadRandomizer() { SaveManager::Instance->LoadData("triforcePiecesCollected", gSaveContext.ship.quest.data.randomizer.triforcePiecesCollected); + SaveManager::Instance->LoadData("bombchuUpgradeLevel", gSaveContext.ship.quest.data.randomizer.bombchuUpgradeLevel); SaveManager::Instance->LoadData("pendingIceTrapCount", gSaveContext.ship.pendingIceTrapCount); @@ -382,6 +383,7 @@ void SaveManager::SaveRandomizer(SaveContext* saveContext, int sectionID, bool f SaveManager::Instance->SaveData("triforcePiecesCollected", saveContext->ship.quest.data.randomizer.triforcePiecesCollected); + SaveManager::Instance->SaveData("bombchuUpgradeLevel", saveContext->ship.quest.data.randomizer.bombchuUpgradeLevel); SaveManager::Instance->SaveData("pendingIceTrapCount", saveContext->ship.pendingIceTrapCount); diff --git a/soh/src/code/z_en_item00.c b/soh/src/code/z_en_item00.c index 28459ba5e..7fc89ee83 100644 --- a/soh/src/code/z_en_item00.c +++ b/soh/src/code/z_en_item00.c @@ -1259,7 +1259,7 @@ void EnItem00_CustomItemsParticles(Actor* Parent, PlayState* play, GetItemEntry case RG_DOUBLE_DEFENSE: colorIndex = PARTICLE_WHITE; break; - case RG_PROGRESSIVE_BOMBCHUS: + case RG_PROGRESSIVE_BOMBCHU_BAG: colorIndex = PARTICLE_DARK_BLUE; break; case RG_BOTTLE_WITH_FAIRY: diff --git a/soh/src/code/z_parameter.c b/soh/src/code/z_parameter.c index 84cea3264..3b1d91f05 100644 --- a/soh/src/code/z_parameter.c +++ b/soh/src/code/z_parameter.c @@ -2211,8 +2211,10 @@ u8 Item_Give(PlayState* play, u8 item) { AMMO(ITEM_BOMBCHU) = 10; } else { AMMO(ITEM_BOMBCHU) += 10; - if (AMMO(ITEM_BOMBCHU) > 50) { - AMMO(ITEM_BOMBCHU) = 50; + if (GameInteractor_Should(VB_CHECK_BOMBCHU_CAPACITY, true)) { + if (AMMO(ITEM_BOMBCHU) > 50) { + AMMO(ITEM_BOMBCHU) = 50; + } } } return Return_Item(item, MOD_NONE, ITEM_NONE); @@ -2222,8 +2224,10 @@ u8 Item_Give(PlayState* play, u8 item) { AMMO(ITEM_BOMBCHU) += sAmmoRefillCounts[item - ITEM_BOMBCHUS_5 + 8]; } else { AMMO(ITEM_BOMBCHU) += sAmmoRefillCounts[item - ITEM_BOMBCHUS_5 + 8]; - if (AMMO(ITEM_BOMBCHU) > 50) { - AMMO(ITEM_BOMBCHU) = 50; + if (GameInteractor_Should(VB_CHECK_BOMBCHU_CAPACITY, true)) { + if (AMMO(ITEM_BOMBCHU) > 50) { + AMMO(ITEM_BOMBCHU) = 50; + } } } return Return_Item(item, MOD_NONE, ITEM_NONE); @@ -2996,8 +3000,10 @@ void Inventory_ChangeAmmo(s16 item, s16 ammoChange) { } else if (item == ITEM_BOMBCHU) { AMMO(ITEM_BOMBCHU) += ammoChange; - if (AMMO(ITEM_BOMBCHU) >= 50) { - AMMO(ITEM_BOMBCHU) = 50; + if (GameInteractor_Should(VB_CHECK_BOMBCHU_CAPACITY, true)) { + if (AMMO(ITEM_BOMBCHU) > 50) { + AMMO(ITEM_BOMBCHU) = 50; + } } else if (AMMO(ITEM_BOMBCHU) < 0) { AMMO(ITEM_BOMBCHU) = 0; } @@ -4908,7 +4914,7 @@ void Interface_DrawAmmoCount(PlayState* play, s16 button, s16 alpha) { ((i == ITEM_SLINGSHOT) && (AMMO(i) == CUR_CAPACITY(UPG_BULLET_BAG))) || ((i == ITEM_STICK) && (AMMO(i) == CUR_CAPACITY(UPG_STICKS))) || ((i == ITEM_NUT) && (AMMO(i) == CUR_CAPACITY(UPG_NUTS))) || ((i == ITEM_BOMBCHU) && (ammo == 50)) || - ((i == ITEM_BEAN) && (ammo == 15))) { + ((i == ITEM_BEAN) && (ammo == 15)) || GameInteractor_Should(VB_COLOR_AMMO_GREEN, false, i)) { gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 120, 255, 0, alpha); } diff --git a/soh/src/overlays/actors/ovl_En_Box/z_en_box.c b/soh/src/overlays/actors/ovl_En_Box/z_en_box.c index f52b4a458..92ad93fb1 100644 --- a/soh/src/overlays/actors/ovl_En_Box/z_en_box.c +++ b/soh/src/overlays/actors/ovl_En_Box/z_en_box.c @@ -591,7 +591,7 @@ void EnBox_UpdateSizeAndTexture(EnBox* this, PlayState* play) { // If they have bombchus, don't consider the bombchu item major if (INV_CONTENT(ITEM_BOMBCHU) == ITEM_BOMBCHU && ((this->getItemEntry.modIndex == MOD_RANDOMIZER && - this->getItemEntry.getItemId == RG_PROGRESSIVE_BOMBCHUS) || + this->getItemEntry.getItemId == RG_PROGRESSIVE_BOMBCHU_BAG) || (this->getItemEntry.modIndex == MOD_NONE && (this->getItemEntry.getItemId == GI_BOMBCHUS_5 || this->getItemEntry.getItemId == GI_BOMBCHUS_10 || this->getItemEntry.getItemId == GI_BOMBCHUS_20)))) { diff --git a/soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.c b/soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.c index 277677360..a8924cf11 100644 --- a/soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.c +++ b/soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.c @@ -762,15 +762,11 @@ s32 EnGirlA_CanBuy_Unk20(PlayState* play, EnGirlA* this) { } s32 EnGirlA_CanBuy_Bombchus(PlayState* play, EnGirlA* this) { - // When in rando, don't allow buying bombchus when the player doesn't have required explosives - // If bombchus are in logic, the player needs to have bombchus; otherwise they need a bomb bag - if (IS_RANDO) { - u8 bombchuBag = Randomizer_GetSettingValue(RSK_BOMBCHU_BAG); - if ((!bombchuBag && CUR_CAPACITY(UPG_BOMB_BAG) == 0) || - (bombchuBag && INV_CONTENT(ITEM_BOMBCHU) == ITEM_NONE)) { - return CANBUY_RESULT_CANT_GET_NOW; - } + s32 canBuy; + if (GameInteractor_Should(VB_CAN_BUY_BOMBCHUS, false, &canBuy)) { + return canBuy; } + if (AMMO(ITEM_BOMBCHU) >= 50) { return CANBUY_RESULT_CANT_GET_NOW; } diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_item.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_item.c index 7f0cfcc7a..dad520a0b 100644 --- a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_item.c +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_item.c @@ -61,7 +61,7 @@ void KaleidoScope_DrawAmmoCount(PauseContext* pauseCtx, GraphicsContext* gfxCtx, (item == ITEM_SLINGSHOT && AMMO(item) == CUR_CAPACITY(UPG_BULLET_BAG)) || (item == ITEM_STICK && AMMO(item) == CUR_CAPACITY(UPG_STICKS)) || (item == ITEM_NUT && AMMO(item) == CUR_CAPACITY(UPG_NUTS)) || (item == ITEM_BOMBCHU && ammo == 50) || - (item == ITEM_BEAN && ammo == 15)) { + (item == ITEM_BEAN && ammo == 15) || GameInteractor_Should(VB_COLOR_AMMO_GREEN, false, item)) { gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 120, 255, 0, pauseCtx->alpha); } }