From 6706b49d1ce3a1e4bafbdb6ff896480dc1195664 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philip=20Dub=C3=A9?= <159546+serprex@users.noreply.github.com> Date: Fri, 7 Aug 2026 03:17:10 +0000 Subject: [PATCH] Shuffle Treasure Chest Game (#7020) --- .../cosmetics/CosmeticsEditor.cpp | 4 +- .../vanilla-behavior/GIVanillaBehavior.h | 45 ++++++ .../randomizer/3drando/hint_list.cpp | 8 + .../3drando/hint_list/hint_list_item.cpp | 40 ++--- .../randomizer/3drando/item_pool.cpp | 12 +- .../Enhancements/randomizer/SeedContext.cpp | 5 +- .../randomizer/ShuffleTreasureChestGame.cpp | 125 +++++++++++++++ soh/soh/Enhancements/randomizer/Traps.cpp | 14 ++ .../Enhancements/randomizer/hook_handlers.cpp | 47 ------ soh/soh/Enhancements/randomizer/item_list.cpp | 3 +- .../location_access/overworld/market.cpp | 23 +-- .../Enhancements/randomizer/location_list.cpp | 1 + soh/soh/Enhancements/randomizer/logic.cpp | 5 +- .../randomizer/option_descriptions.cpp | 19 ++- .../Enhancements/randomizer/randomizer.cpp | 15 +- .../randomizerEnums/RandomizerCheck.h | 1 + .../randomizerEnums/RandomizerHintTextKey.h | 1 + .../randomizerEnums/RandomizerInf.h | 1 + .../randomizerEnums/RandomizerOptions.h | 6 - .../randomizerEnums/RandomizerSettingKey.h | 1 + .../randomizer/randomizer_check_objects.cpp | 4 +- .../randomizer/randomizer_check_tracker.cpp | 147 ++++++++---------- soh/soh/Enhancements/randomizer/settings.cpp | 117 +++++++------- soh/soh/Enhancements/randomizer/settings.h | 5 + soh/src/overlays/actors/ovl_En_Box/z_en_box.c | 15 +- .../actors/ovl_En_Changer/z_en_changer.c | 3 +- .../ovl_En_Takara_Man/z_en_takara_man.c | 13 +- 27 files changed, 419 insertions(+), 261 deletions(-) create mode 100644 soh/soh/Enhancements/randomizer/ShuffleTreasureChestGame.cpp diff --git a/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp b/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp index 84cb37404b..ad1ba5cfbe 100644 --- a/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp +++ b/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp @@ -281,8 +281,8 @@ std::map cosmeticOptions = { COSMETIC_OPTION("Key.FortSmallEmblem", "Fortress Small Key Emblem",COSMETICS_GROUP_SMALL_KEYS, ColorRGBA8(255, 255, 255, 255), false, true, false), COSMETIC_OPTION("Key.GTGSmallBody", "GTG Small Key", COSMETICS_GROUP_SMALL_KEYS, ColorRGBA8(255, 255, 255, 255), false, true, false), COSMETIC_OPTION("Key.GTGSmallEmblem", "GTG Small Key Emblem", COSMETICS_GROUP_SMALL_KEYS, ColorRGBA8(221, 212, 60, 255), false, true, false), - //COSMETIC_OPTION("Key.ChestGameSmallBody", "Chest Game Key", COSMETICS_GROUP_SMALL_KEYS, ColorRGBA8(255, 255, 255, 255), false, true, false), - //COSMETIC_OPTION("Key.ChestGameEmblem", "Chest Game Key Emblem", COSMETICS_GROUP_SMALL_KEYS, ColorRGBA8(255, 0, 0, 255), false, true, false), + COSMETIC_OPTION("Key.ChestGameSmallBody", "Chest Game Key", COSMETICS_GROUP_SMALL_KEYS, ColorRGBA8(255, 255, 255, 255), false, true, false), + COSMETIC_OPTION("Key.ChestGameEmblem", "Chest Game Key Emblem", COSMETICS_GROUP_SMALL_KEYS, ColorRGBA8(255, 0, 0, 255), false, true, false), COSMETIC_OPTION("Key.Skeleton", "Skeleton Key", COSMETICS_GROUP_SMALL_KEYS, ColorRGBA8(255, 255, 170, 255), false, true, false), COSMETIC_OPTION("HUD.AButton", "A Button", COSMETICS_GROUP_HUD, ColorRGBA8( 90, 90, 255, 255), false, true, false), diff --git a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h index 8a7c833d90..3a213b8ae6 100644 --- a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h +++ b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h @@ -3639,6 +3639,51 @@ typedef enum { // a textbox can be rendered instead. Pause screen only, Game Over version left // intact. VB_DRAW_SAVE_MENU, + + // #### `result` + // ```c + // true + // ``` + // Whether the chest game shopkeeper wipes the scene's chest flags and keys on spawn. + // #### `args` + // - `*EnTakaraMan` + VB_TAKARA_MAN_RESET_CHESTS_AND_KEYS, + + // #### `result` + // ```c + // true + // ``` + // Whether the chest game shopkeeper hands over a small key after being paid. + // #### `args` + // - `*EnTakaraMan` + VB_TAKARA_MAN_OFFER_GET_ITEM, + + // #### `result` + // ```c + // Rand_ZeroFloat(1.99f) < 1.0f + // ``` + // Whether the chest game swaps which side of the room holds the key chest. + // #### `args` + // - `*EnChanger` + VB_EN_CHANGER_SWAP_CHESTS, + + // #### `result` + // ```c + // true + // ``` + // Whether opening a chest sets its treasure flag. + // #### `args` + // - `*EnBox` + VB_CHEST_SET_TREASURE_FLAG, + + // #### `result` + // ```c + // Flags_GetTreasure(play, this->dyna.actor.params & 0x1F) + // ``` + // Whether a chest counts as already opened. + // #### `args` + // - `*EnBox` + VB_CHEST_CONSIDER_CHEST_OPEN, } GIVanillaBehavior; #endif diff --git a/soh/soh/Enhancements/randomizer/3drando/hint_list.cpp b/soh/soh/Enhancements/randomizer/3drando/hint_list.cpp index c9580322aa..0590180dab 100644 --- a/soh/soh/Enhancements/randomizer/3drando/hint_list.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/hint_list.cpp @@ -460,6 +460,14 @@ void StaticData::HintTable_Init() { /*french*/ "Selon moi, le #neuvième ou dixième coffre à jeu# contient #[[1]]#.", {QM_RED, QM_GREEN})}); // /*spanish*/#el noveno o décimo cofre del azar# revela #[[1]]#. + hintTextTable[RHT_MARKET_TREASURE_CHEST_GAME_SHOPKEEPER] = HintText(CustomMessage("They say that the #chest game host# is holding on to #[[1]]#.", + /*german*/ "Man erzählt sich, daß der #Truhenlotterie-Veranstalter# #[[1]]# bereithielte.", + /*french*/ "Selon moi, l'#hôte de la Chasse-aux-Trésors# détient #[[1]]#.", {QM_RED, QM_GREEN}), + {}, + {CustomMessage("They say that #paying to play the chest game# grants #[[1]]#.", + /*german*/ "Man erzählt sich, daß #ein Spiel der Truhenlotterie# #[[1]]# gewähre.", + /*french*/ "Selon moi, #payer pour la Chasse-aux-Trésors# donne #[[1]]#.", {QM_RED, QM_GREEN})}); + hintTextTable[RHT_GF_HBA_1500_POINTS] = HintText(CustomMessage("They say that scoring 1500 in #horseback archery# grants #[[1]]#.", /*german*/ "Man erzählt sich, daß das Erzielen von 1.500 Punkten beim #Bogenschießen zu Pferde# #[[1]]# gewähre.", /*french*/ "Selon moi, obtenir 1500 points dans l'#archerie équestre# donne #[[1]]#.", {QM_RED, QM_GREEN}), diff --git a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_item.cpp b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_item.cpp index 920e40c42b..14a50275cd 100644 --- a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_item.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_item.cpp @@ -1376,10 +1376,10 @@ void StaticData::HintTable_Init_Item() { CustomMessage("a key to the universe", /*german*/"ein Schlüssel zum Universum", /*french*/"une clé de l'univers")}); // /*spanish*/una llave al universo - hintTextTable[RHT_FOREST_TEMPLE_KEY_RING] = HintText(CustomMessage("a Forest Temple Key Ring", /*german*/"das Schlüsselbund des Waldtempels", /*french*/"un trousseau de clés du Temple de la Forêt"), + hintTextTable[RHT_FOREST_TEMPLE_KEY_RING] = HintText(CustomMessage("a Forest Temple Keyring", /*german*/"das Schlüsselbund des Waldtempels", /*french*/"un trousseau de clés du Temple de la Forêt"), // /*spanish*/un llavero del Templo del Bosque { - CustomMessage("a key ring", /*german*/"ein Schlüsselbund", /*french*/"un trousseau de clés") + CustomMessage("a keyring", /*german*/"ein Schlüsselbund", /*french*/"un trousseau de clés") // /*spanish*/un llavero }, { CustomMessage("a sylvan toolbox for unlocking", /*german*/"eine waldige Werkzeugkiste zur Entriegelung", /*french*/"des anti-portes sylvestres"), @@ -1391,10 +1391,10 @@ void StaticData::HintTable_Init_Item() { CustomMessage("a sylvan skeleton key", /*german*/"ein waldiger Skelettschlüssel", /*french*/"des crochets à porte sylvestres")}); // /*spanish*/un anillo silvestre contra cerrojos - hintTextTable[RHT_FIRE_TEMPLE_KEY_RING] = HintText(CustomMessage("a Fire Temple Key Ring", /*german*/"das Schlüsselbund des Feuertempels", /*french*/"un trousseau de clés du Temple du Feu"), + hintTextTable[RHT_FIRE_TEMPLE_KEY_RING] = HintText(CustomMessage("a Fire Temple Keyring", /*german*/"das Schlüsselbund des Feuertempels", /*french*/"un trousseau de clés du Temple du Feu"), // /*spanish*/un llavero del Templo del Fuego { - CustomMessage("a key ring", /*german*/"ein Schlüsselbund", /*french*/"un trousseau de clés") + CustomMessage("a keyring", /*german*/"ein Schlüsselbund", /*french*/"un trousseau de clés") // /*spanish*/un llavero }, { CustomMessage("a molten toolbox for unlocking", /*german*/"eine geschmolzene Werkzeugkiste zur Entriegelung", /*french*/"des anti-portes fondus"), @@ -1406,10 +1406,10 @@ void StaticData::HintTable_Init_Item() { CustomMessage("a molten skeleton key", /*german*/"ein geschmolzener Skelettschlüssel", /*french*/"des crochets à porte fondus")}); // /*spanish*/un anillo fundido contra cerrojos - hintTextTable[RHT_WATER_TEMPLE_KEY_RING] = HintText(CustomMessage("a Water Temple Key Ring", /*german*/"das Schlüsselbund des Wassertempels", /*french*/"un trousseau de clés du Temple de l'Eau"), + hintTextTable[RHT_WATER_TEMPLE_KEY_RING] = HintText(CustomMessage("a Water Temple Keyring", /*german*/"das Schlüsselbund des Wassertempels", /*french*/"un trousseau de clés du Temple de l'Eau"), // /*spanish*/un llavero del Templo del Agua { - CustomMessage("a key ring", /*german*/"ein Schlüsselbund", /*french*/"un trousseau de clés") + CustomMessage("a keyring", /*german*/"ein Schlüsselbund", /*french*/"un trousseau de clés") // /*spanish*/un llavero }, { CustomMessage("a wet toolbox for unlocking", /*german*/"eine nasse Werkzeugkiste zur Entriegelung", /*french*/"des anti-portes humides"), @@ -1421,10 +1421,10 @@ void StaticData::HintTable_Init_Item() { CustomMessage("a wet skeleton key", /*german*/"ein nasser Skelettschlüssel", /*french*/"des crochets à porte humides")}); // /*spanish*/un anillo abisal contra cerrojos - hintTextTable[RHT_SPIRIT_TEMPLE_KEY_RING] = HintText(CustomMessage("a Spirit Temple Key Ring", /*german*/"das Schlüsselbund des Geistertempels", /*french*/"un trousseau de clés du Temple de l'Esprit"), + hintTextTable[RHT_SPIRIT_TEMPLE_KEY_RING] = HintText(CustomMessage("a Spirit Temple Keyring", /*german*/"das Schlüsselbund des Geistertempels", /*french*/"un trousseau de clés du Temple de l'Esprit"), // /*spanish*/un llavero del Templo del Espíritu { - CustomMessage("a key ring", /*german*/"ein Schlüsselbund", /*french*/"un trousseau de clés") + CustomMessage("a keyring", /*german*/"ein Schlüsselbund", /*french*/"un trousseau de clés") // /*spanish*/un llavero }, { CustomMessage("a sandy toolbox for unlocking", /*german*/"eine sandige Werkzeugkiste zur Entriegelung", /*french*/"des anti-portes sableux"), @@ -1436,10 +1436,10 @@ void StaticData::HintTable_Init_Item() { CustomMessage("a sandy skeleton key", /*german*/"ein sandiger Skelettschlüssel", /*french*/"des crochets à porte sableux")}); // /*spanish*/un anillo arenoso contra cerrojos - hintTextTable[RHT_SHADOW_TEMPLE_KEY_RING] = HintText(CustomMessage("a Shadow Temple Key Ring", /*german*/"das Schlüsselbund des Schattentempels", /*french*/"un trousseau de clés du Temple de l'Ombre"), + hintTextTable[RHT_SHADOW_TEMPLE_KEY_RING] = HintText(CustomMessage("a Shadow Temple Keyring", /*german*/"das Schlüsselbund des Schattentempels", /*french*/"un trousseau de clés du Temple de l'Ombre"), // /*spanish*/un llavero del Templo de las Sombras { - CustomMessage("a key ring", /*german*/"ein Schlüsselbund", /*french*/"un trousseau de clés") + CustomMessage("a keyring", /*german*/"ein Schlüsselbund", /*french*/"un trousseau de clés") // /*spanish*/un llavero }, { CustomMessage("a creepy toolbox for unlocking", /*german*/"eine gruselige Werkzeugkiste zur Entriegelung", /*french*/"des anti-portes sinistres"), @@ -1451,10 +1451,10 @@ void StaticData::HintTable_Init_Item() { CustomMessage("a creepy skeleton key", /*german*/"ein gruseliger Skelettschlüssel", /*french*/"des crochets à porte sinistres")}); // /*spanish*/un anillo tenebroso contra cerrojos - hintTextTable[RHT_GERUDO_TRAINING_GROUND_KEY_RING] = HintText(CustomMessage("a Gerudo Training Ground Key Ring", /*german*/"das Schlüsselbund der Gerudo-Trainingsarena", /*french*/"un trousseau de clés du Gymnase Gerudo"), + hintTextTable[RHT_GERUDO_TRAINING_GROUND_KEY_RING] = HintText(CustomMessage("a Gerudo Training Ground Keyring", /*german*/"das Schlüsselbund der Gerudo-Trainingsarena", /*french*/"un trousseau de clés du Gymnase Gerudo"), // /*spanish*/un llavero del Centro de Instrucción Gerudo { - CustomMessage("a key ring", /*german*/"ein Schlüsselbund", /*french*/"un trousseau de clés") + CustomMessage("a keyring", /*german*/"ein Schlüsselbund", /*french*/"un trousseau de clés") // /*spanish*/un llavero }, { CustomMessage("a labyrinthian toolbox for unlocking", /*german*/"eine verwirrende Werkzeugkiste zur Entriegelung", /*french*/"des anti-portes labyrinthiques"), @@ -1466,10 +1466,10 @@ void StaticData::HintTable_Init_Item() { CustomMessage("a labyrinthian skeleton key", /*german*/"ein verwirrender Skelettschlüssel", /*french*/"des crochets à porte labyrinthiques")}); // /*spanish*/un anillo laberíntico contra cerrojos - hintTextTable[RHT_GERUDO_FORTRESS_KEY_RING] = HintText(CustomMessage("a Gerudo Fortress Key Ring", /*german*/"das Schlüsselbund des Diebesverstecks", /*french*/"un trousseau de clés de la Repaire des Voleurs"), + hintTextTable[RHT_GERUDO_FORTRESS_KEY_RING] = HintText(CustomMessage("a Gerudo Fortress Keyring", /*german*/"das Schlüsselbund des Diebesverstecks", /*french*/"un trousseau de clés de la Repaire des Voleurs"), // /*spanish*/un llavero de la Fortaleza Gerudo { - CustomMessage("a key ring", /*german*/"ein Schlüsselbund", /*french*/"un trousseau de clés") + CustomMessage("a keyring", /*german*/"ein Schlüsselbund", /*french*/"un trousseau de clés") // /*spanish*/un llavero }, { CustomMessage("an imprisoned toolbox for unlocking", /*german*/"eine gefangene Werkzeugkiste zur Entriegelung", /*french*/"des anti-portes emprisonnés"), @@ -1481,10 +1481,10 @@ void StaticData::HintTable_Init_Item() { CustomMessage("an imprisoned skeleton key", /*german*/"ein gefangener Skelettschlüssel", /*french*/"des crochets à porte emprisonnés")}); // /*spanish*/un anillo enjaulado contra cerrojos - hintTextTable[RHT_BOTTOM_OF_THE_WELL_KEY_RING] = HintText(CustomMessage("a Bottom of the Well Key Ring", /*german*/"das Schlüsselbund vom Grund des Brunnens", /*french*/"un trousseau de clés du fond du Puits"), + hintTextTable[RHT_BOTTOM_OF_THE_WELL_KEY_RING] = HintText(CustomMessage("a Bottom of the Well Keyring", /*german*/"das Schlüsselbund vom Grund des Brunnens", /*french*/"un trousseau de clés du fond du Puits"), // /*spanish*/un llavero del Fondo del pozo { - CustomMessage("a key ring", /*german*/"ein Schlüsselbund", /*french*/"un trousseau de clés") + CustomMessage("a keyring", /*german*/"ein Schlüsselbund", /*french*/"un trousseau de clés") // /*spanish*/un llavero }, { CustomMessage("a moldy toolbox for unlocking", /*german*/"eine schimmelige Werkzeugkiste zur Entriegelung", /*french*/"des anti-portes moisis"), @@ -1496,10 +1496,10 @@ void StaticData::HintTable_Init_Item() { CustomMessage("a moldy skeleton key", /*german*/"ein schimmeliger Skelettschlüssel", /*french*/"des crochets à porte moisis")}); // /*spanish*/un anillo subterráneo contra cerrojos - hintTextTable[RHT_GANONS_CASTLE_KEY_RING] = HintText(CustomMessage("a Ganon's Castle Key Ring", /*german*/"das Schlüsselbund von Ganons Schloß", /*french*/"un trousseau de clés du Château de Ganon"), + hintTextTable[RHT_GANONS_CASTLE_KEY_RING] = HintText(CustomMessage("a Ganon's Castle Keyring", /*german*/"das Schlüsselbund von Ganons Schloß", /*french*/"un trousseau de clés du Château de Ganon"), // /*spanish*/un llavero del Castillo de Ganon { - CustomMessage("a key ring", /*german*/"ein Schlüsselbund", /*french*/"un trousseau de clés") + CustomMessage("a keyring", /*german*/"ein Schlüsselbund", /*french*/"un trousseau de clés") // /*spanish*/un llavero }, { CustomMessage("a final toolbox for unlocking", /*german*/"eine finale Werkzeugkiste zur Entriegelung", /*french*/"des anti-portes finaux"), @@ -1526,9 +1526,9 @@ void StaticData::HintTable_Init_Item() { CustomMessage("a gambler's lockpick", /*german*/"ein Zocker-Dietrich", /*french*/"un crochet à serrure de parieur")}); // /*spanish*/una apertura portentosa del juego de azar - hintTextTable[RHT_TREASURE_GAME_KEY_RING] = HintText(CustomMessage("a Chest Game Key Ring", /*german*/ "das Schlüsselbund der Truhenlotterie", /*french*/ "TRANSLATE THIS"), + hintTextTable[RHT_TREASURE_GAME_KEY_RING] = HintText(CustomMessage("a Chest Game Keyring", /*german*/ "das Schlüsselbund der Truhenlotterie", /*french*/ "TRANSLATE THIS"), { - CustomMessage("a key ring", /*german*/"ein Schlüsselbund", /*french*/"un trousseau de clés") + CustomMessage("a keyring", /*german*/"ein Schlüsselbund", /*french*/"un trousseau de clés") // /*spanish*/un llavero }, { CustomMessage("a gambler's toolbox for unlocking", /*german*/ "eine Zocker-Werkzeugkiste zur Entriegelung", /*french*/ "TRANSLATE THIS"), diff --git a/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp b/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp index f9cd7e60ca..1d2c7f443f 100644 --- a/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp @@ -571,10 +571,12 @@ void GenerateItemPool() { AddItemToPool(RG_CLAIM_CHECK, 2, 1, 1, 1); } - if (ctx->GetOption(RSK_SHUFFLE_CHEST_MINIGAME).Is(RO_CHEST_GAME_SINGLE_KEYS)) { - AddItemToPool(RG_TREASURE_GAME_SMALL_KEY, 7, 6, 6, 6); - } else if (ctx->GetOption(RSK_SHUFFLE_CHEST_MINIGAME).Is(RO_CHEST_GAME_PACK)) { - AddItemToPool(RG_TREASURE_GAME_KEY_RING, 2, 1, 1, 1); + if (ctx->GetOption(RSK_SHUFFLE_CHEST_MINIGAME)) { + if (ctx->GetOption(RSK_KEYRINGS_CHEST_GAME) && ctx->GetOption(RSK_KEYRINGS)) { + AddItemToPool(RG_TREASURE_GAME_KEY_RING, 2, 1, 1, 1); + } else { + AddItemToPool(RG_TREASURE_GAME_SMALL_KEY, 7, 6, 6, 6); + } } int tokensToAdd = 0; @@ -656,7 +658,7 @@ void GenerateItemPool() { 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 + // Only add keyring if 4 Fortress keys necessary if (ctx->GetOption(RSK_KEYRINGS_GERUDO_FORTRESS) && ctx->GetOption(RSK_KEYRINGS)) { AddItemToPool(RG_GERUDO_FORTRESS_KEY_RING, 2, 1, 1, 1); } else { diff --git a/soh/soh/Enhancements/randomizer/SeedContext.cpp b/soh/soh/Enhancements/randomizer/SeedContext.cpp index c4ca7b1140..387ec7e119 100644 --- a/soh/soh/Enhancements/randomizer/SeedContext.cpp +++ b/soh/soh/Enhancements/randomizer/SeedContext.cpp @@ -171,7 +171,7 @@ void Context::GenerateLocationPool() { mOptions[RSK_SHUFFLE_MASTER_SWORD].Is(RO_GENERIC_OFF)) || (location.GetRandomizerCheck() == RC_KAK_100_GOLD_SKULLTULA_REWARD && mOptions[RSK_SHUFFLE_100_GS_REWARD].Is(RO_GENERIC_OFF)) || - location.GetRCType() == RCTYPE_CHEST_GAME || // not supported yet + (location.GetRCType() == RCTYPE_CHEST_GAME && mOptions[RSK_SHUFFLE_CHEST_MINIGAME].Is(RO_GENERIC_OFF)) || location.GetRCType() == RCTYPE_STATIC_HINT || // can't have items location.GetRCType() == RCTYPE_GOSSIP_STONE || // can't have items (location.GetRCType() == RCTYPE_FROG_SONG && mOptions[RSK_SHUFFLE_FROG_SONG_RUPEES].Is(RO_GENERIC_OFF)) || @@ -273,8 +273,7 @@ void Context::AddExcludedOptions() { for (auto& loc : StaticData::GetLocationTable()) { // Checks of these types don't have items, skip them. if (loc.GetRandomizerCheck() == RC_UNKNOWN_CHECK || loc.GetRandomizerCheck() == RC_WINCON || - loc.GetRCType() == RCTYPE_CHEST_GAME || loc.GetRCType() == RCTYPE_STATIC_HINT || - loc.GetRCType() == RCTYPE_GOSSIP_STONE) { + loc.GetRCType() == RCTYPE_STATIC_HINT || loc.GetRCType() == RCTYPE_GOSSIP_STONE) { continue; } AddLocation(loc.GetRandomizerCheck(), &everyPossibleLocation); diff --git a/soh/soh/Enhancements/randomizer/ShuffleTreasureChestGame.cpp b/soh/soh/Enhancements/randomizer/ShuffleTreasureChestGame.cpp new file mode 100644 index 0000000000..8c145f27ab --- /dev/null +++ b/soh/soh/Enhancements/randomizer/ShuffleTreasureChestGame.cpp @@ -0,0 +1,125 @@ +#include +#include "soh/Enhancements/randomizer/randomizer.h" + +extern "C" { +#include "src/overlays/actors/ovl_Door_Shutter/z_door_shutter.h" +#include "src/overlays/actors/ovl_En_Changer/z_en_changer.h" +#include "src/overlays/actors/ovl_En_Takara_Man/z_en_takara_man.h" +extern PlayState* gPlayState; +} + +static bool GetChestGameRandInf(Actor* chest, RandomizerInf* randInf) { + if (gPlayState->sceneNum != SCENE_TREASURE_BOX_SHOP) { + return false; + } + + s32 treasureFlag = chest->params & 0x1F; + if (treasureFlag > 9) { + return false; + } + + *randInf = static_cast( + (treasureFlag & 1 ? RAND_INF_MARKET_TREASURE_CHEST_GAME_KEY_1 : RAND_INF_MARKET_TREASURE_CHEST_GAME_ITEM_1) + + treasureFlag / 2); + return true; +} + +void DoorShutter_RandomizerInit(void* actorRef) { + DoorShutter* door = static_cast(actorRef); + + if (gPlayState->sceneNum != SCENE_TREASURE_BOX_SHOP || door->doorType != SHUTTER_KEY_LOCKED) { + return; + } + + door->dyna.actor.params &= ~0x20; + + if (Flags_GetSwitch(gPlayState, door->dyna.actor.params & 0x3F)) { + door->unlockTimer = 0; + } +} + +void ItemEtcetera_RandomizerInit(void* actorRef) { + Actor* actor = static_cast(actorRef); + + if (gPlayState->sceneNum != SCENE_TREASURE_BOX_SHOP || ((actor->params >> 8) & 0x1F) > 9) { + return; + } + + // these only ever show the vanilla contents through the lens, so drop them + Actor_Kill(actor); +} + +extern "C" void EnChanger_RandomizerWait(EnChanger* thisx, PlayState* play) { +} + +void EnChanger_RandomizerInit(void* actorRef) { + if (gPlayState->sceneNum != SCENE_TREASURE_BOX_SHOP || gPlayState->roomCtx.curRoom.num >= 6) { + return; + } + + EnChanger* changer = static_cast(actorRef); + + // stop opening one chest from sealing the other and playing the reveal + changer->actionFunc = EnChanger_RandomizerWait; +} + +void EnTakaraMan_RandomizerInit(void* actorRef) { + if (Flags_GetRandomizerInf(RAND_INF_MARKET_TREASURE_CHEST_GAME_SHOPKEEPER)) { + EnTakaraMan* takaraMan = static_cast(actorRef); + // acting as if the game was already paid for stops him offering it again + takaraMan->unk_214 = 1; + } +} + +void EnBox_RandomizerInit(void* actorRef) { + Actor* chest = static_cast(actorRef); + RandomizerInf randInf; + + if (GetChestGameRandInf(chest, &randInf)) { + // the chests are see-through until the lens reveals them, but there is nothing left to reveal + chest->flags &= ~ACTOR_FLAG_REACT_TO_LENS; + } +} + +void RegisterShuffleTreasureChestGame() { + bool shouldRegister = IS_RANDO && RAND_GET_OPTION(RSK_SHUFFLE_CHEST_MINIGAME); + + // prevent wiping chest flags and key count on every visit + COND_VB_SHOULD(VB_TAKARA_MAN_RESET_CHESTS_AND_KEYS, shouldRegister, { *should = false; }); + + COND_VB_SHOULD(VB_TAKARA_MAN_OFFER_GET_ITEM, shouldRegister, { + Actor* takaraMan = va_arg(args, Actor*); + GetItemEntry getItemEntry = + Rando::Context::GetInstance()->GetFinalGIEntry(RC_MARKET_TREASURE_CHEST_GAME_SHOPKEEPER, true, GI_DOOR_KEY); + Flags_SetRandomizerInf(RAND_INF_MARKET_TREASURE_CHEST_GAME_SHOPKEEPER); + GiveItemEntryFromActor(takaraMan, gPlayState, getItemEntry, 2000.0f, 1000.0f); + *should = false; + }); + + COND_VB_SHOULD(VB_EN_CHANGER_SWAP_CHESTS, shouldRegister, { *should = false; }); + + COND_VB_SHOULD(VB_CHEST_SET_TREASURE_FLAG, shouldRegister, { + Actor* chest = va_arg(args, Actor*); + RandomizerInf randInf; + if (GetChestGameRandInf(chest, &randInf)) { + Flags_SetRandomizerInf(randInf); + *should = false; + } + }); + + COND_VB_SHOULD(VB_CHEST_CONSIDER_CHEST_OPEN, shouldRegister, { + Actor* chest = va_arg(args, Actor*); + RandomizerInf randInf; + if (GetChestGameRandInf(chest, &randInf)) { + *should = Flags_GetRandomizerInf(randInf); + } + }); + + COND_ID_HOOK(OnActorInit, ACTOR_DOOR_SHUTTER, shouldRegister, DoorShutter_RandomizerInit); + COND_ID_HOOK(OnActorInit, ACTOR_ITEM_ETCETERA, shouldRegister, ItemEtcetera_RandomizerInit); + COND_ID_HOOK(OnActorInit, ACTOR_EN_CHANGER, shouldRegister, EnChanger_RandomizerInit); + COND_ID_HOOK(OnActorInit, ACTOR_EN_BOX, shouldRegister, EnBox_RandomizerInit); + COND_ID_HOOK(OnActorInit, ACTOR_EN_TAKARA_MAN, shouldRegister, EnTakaraMan_RandomizerInit); +} + +static RegisterShipInitFunc registerShuffleTreasureChestGame(RegisterShuffleTreasureChestGame, { "IS_RANDO" }); diff --git a/soh/soh/Enhancements/randomizer/Traps.cpp b/soh/soh/Enhancements/randomizer/Traps.cpp index 71e639458b..6642e8334c 100644 --- a/soh/soh/Enhancements/randomizer/Traps.cpp +++ b/soh/soh/Enhancements/randomizer/Traps.cpp @@ -885,6 +885,13 @@ static void InitTrickNames() { Text{ "Vaati's Palace Small Key", "Vaati's Palace Small Key", "Vaati's Palace Small Key" }, }; + trickNameTable[RG_TREASURE_GAME_SMALL_KEY] = { + // TODO_TRANSALTE + Text{ "Chest Game Salmon Koi", "Chest Game Salmon Koi", "Chest Game Salmon Koi" }, + Text{ "Shell Game Small Key", "Shell Game Small Key", "Shell Game Small Key" }, + Text{ "Treasure Hunt Small Key", "Treasure Hunt Small Key", "Treasure Hunt Small Key" }, + }; + trickNameTable[RG_FOREST_TEMPLE_KEY_RING] = { // TODO_TRANSALTE Text{ "Wind Temple Key Ring", "Wind Temple Key Ring", "Wind Temple Key Ring" }, @@ -943,6 +950,13 @@ static void InitTrickNames() { Text{ "Vaati's Palace Key Ring", "Vaati's Palace Key Ring", "Vaati's Palace Key Ring" }, }; + trickNameTable[RG_TREASURE_GAME_KEY_RING] = { + // TODO_TRANSALTE + Text{ "Chest Game Koi Ray", "Chest Game Koi Ray", "Chest Game Koi Ray" }, + Text{ "Shell Game Key Ring", "Shell Game Key Ring", "Shell Game Key Ring" }, + Text{ "Treasure Hunt Key Ring", "Treasure Hunt Key Ring", "Treasure Hunt Key Ring" }, + }; + trickNameTable[RG_FOREST_TEMPLE_BOSS_KEY] = { // TODO_TRANSALTE Text{ "Wind Temple Boss Key", "Wind Temple Boss Key", "Wind Temple Boss Key" }, diff --git a/soh/soh/Enhancements/randomizer/hook_handlers.cpp b/soh/soh/Enhancements/randomizer/hook_handlers.cpp index d71876e5ae..5377d0622b 100644 --- a/soh/soh/Enhancements/randomizer/hook_handlers.cpp +++ b/soh/soh/Enhancements/randomizer/hook_handlers.cpp @@ -2137,53 +2137,6 @@ void RandomizerOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_l } void RandomizerOnSceneInitHandler(int16_t sceneNum) { - // Treasure Chest Game - // todo: for now we're just unsetting all of them, we will - // probably need to do something different when we implement shuffle - if (sceneNum == SCENE_TREASURE_BOX_SHOP) { - Flags_UnsetRandomizerInf(RAND_INF_MARKET_TREASURE_CHEST_GAME_ITEM_1); - Rando::Context::GetInstance() - ->GetItemLocation(RC_MARKET_TREASURE_CHEST_GAME_ITEM_1) - ->SetCheckStatus(RCSHOW_UNCHECKED); - Flags_UnsetRandomizerInf(RAND_INF_MARKET_TREASURE_CHEST_GAME_ITEM_2); - Rando::Context::GetInstance() - ->GetItemLocation(RC_MARKET_TREASURE_CHEST_GAME_ITEM_2) - ->SetCheckStatus(RCSHOW_UNCHECKED); - Flags_UnsetRandomizerInf(RAND_INF_MARKET_TREASURE_CHEST_GAME_ITEM_3); - Rando::Context::GetInstance() - ->GetItemLocation(RC_MARKET_TREASURE_CHEST_GAME_ITEM_3) - ->SetCheckStatus(RCSHOW_UNCHECKED); - Flags_UnsetRandomizerInf(RAND_INF_MARKET_TREASURE_CHEST_GAME_ITEM_4); - Rando::Context::GetInstance() - ->GetItemLocation(RC_MARKET_TREASURE_CHEST_GAME_ITEM_4) - ->SetCheckStatus(RCSHOW_UNCHECKED); - Flags_UnsetRandomizerInf(RAND_INF_MARKET_TREASURE_CHEST_GAME_ITEM_5); - Rando::Context::GetInstance() - ->GetItemLocation(RC_MARKET_TREASURE_CHEST_GAME_ITEM_5) - ->SetCheckStatus(RCSHOW_UNCHECKED); - Flags_UnsetRandomizerInf(RAND_INF_MARKET_TREASURE_CHEST_GAME_KEY_1); - Rando::Context::GetInstance() - ->GetItemLocation(RC_MARKET_TREASURE_CHEST_GAME_KEY_1) - ->SetCheckStatus(RCSHOW_UNCHECKED); - Flags_UnsetRandomizerInf(RAND_INF_MARKET_TREASURE_CHEST_GAME_KEY_2); - Rando::Context::GetInstance() - ->GetItemLocation(RC_MARKET_TREASURE_CHEST_GAME_KEY_2) - ->SetCheckStatus(RCSHOW_UNCHECKED); - Flags_UnsetRandomizerInf(RAND_INF_MARKET_TREASURE_CHEST_GAME_KEY_3); - Rando::Context::GetInstance() - ->GetItemLocation(RC_MARKET_TREASURE_CHEST_GAME_KEY_3) - ->SetCheckStatus(RCSHOW_UNCHECKED); - Flags_UnsetRandomizerInf(RAND_INF_MARKET_TREASURE_CHEST_GAME_KEY_4); - Rando::Context::GetInstance() - ->GetItemLocation(RC_MARKET_TREASURE_CHEST_GAME_KEY_4) - ->SetCheckStatus(RCSHOW_UNCHECKED); - Flags_UnsetRandomizerInf(RAND_INF_MARKET_TREASURE_CHEST_GAME_KEY_5); - Rando::Context::GetInstance() - ->GetItemLocation(RC_MARKET_TREASURE_CHEST_GAME_KEY_5) - ->SetCheckStatus(RCSHOW_UNCHECKED); - CheckTracker::RecalculateAllAreaTotals(); - } - // ENTRTODO: Move all entrance rando handling to a dedicated file if (RAND_GET_OPTION(RSK_SHUFFLE_ENTRANCES)) { // In ER, override roomNum to load based on scene and spawn during scene init diff --git a/soh/soh/Enhancements/randomizer/item_list.cpp b/soh/soh/Enhancements/randomizer/item_list.cpp index b5004410ac..c0129ba1a5 100644 --- a/soh/soh/Enhancements/randomizer/item_list.cpp +++ b/soh/soh/Enhancements/randomizer/item_list.cpp @@ -171,7 +171,8 @@ void Rando::StaticData::InitItemTable() { itemTable[RG_GERUDO_FORTRESS_SMALL_KEY].SetCustomDrawFunc(Randomizer_DrawSmallKey); itemTable[RG_GANONS_CASTLE_SMALL_KEY] = Item(RG_GANONS_CASTLE_SMALL_KEY, Text{ "Ganon's Castle Small Key", "Petite Clé du Château de Ganon", "Kleiner Schlüssel für Ganons Schloß" }, ITEMTYPE_SMALLKEY, 0xB7, true, LOGIC_GANONS_CASTLE_KEYS, RHT_GANONS_CASTLE_SMALL_KEY, RG_GANONS_CASTLE_SMALL_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER, {"a ", "une ", "einen "}, "%r").CustomIcon(gQuestIconSmallKeyTex, ICON_SIZE_24); itemTable[RG_GANONS_CASTLE_SMALL_KEY].SetCustomDrawFunc(Randomizer_DrawSmallKey); - itemTable[RG_TREASURE_GAME_SMALL_KEY] = Item(RG_TREASURE_GAME_SMALL_KEY, Text{ "Chest Game Small Key", "Petite Clé du jeu la Chasse-aux-Trésors", "Kleiner Schlüssel für das Truhenspiel" }, ITEMTYPE_SMALLKEY, GI_DOOR_KEY, true, LOGIC_TREASURE_GAME_KEYS, RHT_TREASURE_GAME_SMALL_KEY, ITEM_KEY_SMALL, OBJECT_GI_KEY, GID_KEY_SMALL, 0xF3, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_NONE, {"a ", "une ", "einen "}).CustomIcon(gQuestIconSmallKeyTex, ICON_SIZE_24); + itemTable[RG_TREASURE_GAME_SMALL_KEY] = Item(RG_TREASURE_GAME_SMALL_KEY, Text{ "Chest Game Small Key", "Petite Clé du jeu la Chasse-aux-Trésors", "Kleiner Schlüssel für das Truhenspiel" }, ITEMTYPE_SMALLKEY, GI_DOOR_KEY, true, LOGIC_TREASURE_GAME_KEYS, RHT_TREASURE_GAME_SMALL_KEY, RG_TREASURE_GAME_SMALL_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER, {"a ", "une ", "einen "}, "%r").CustomIcon(gQuestIconSmallKeyTex, ICON_SIZE_24); + itemTable[RG_TREASURE_GAME_SMALL_KEY].SetCustomDrawFunc(Randomizer_DrawSmallKey); itemTable[RG_GUARD_HOUSE_KEY] = Item(RG_GUARD_HOUSE_KEY, Text{ "Guard House Key", "Clé de la Maison des Gardes", "Schlüssel für das Haus der Wachen" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_GUARD_HOUSE_KEY, RHT_OVERWORLD_KEY, RG_GUARD_HOUSE_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER, {"the ", "la ", "den "}).CustomIcon(gQuestIconSmallKeyTex, ICON_SIZE_24); itemTable[RG_GUARD_HOUSE_KEY].SetCustomDrawFunc(Randomizer_DrawOverworldKey); itemTable[RG_MARKET_BAZAAR_KEY] = Item(RG_MARKET_BAZAAR_KEY, Text{ "Market Bazaar Key", "Clé du Bazar de la Place du Marché", "Schlüssel für den Basar des Marktes" }, ITEMTYPE_ITEM, GI_DOOR_KEY, true, LOGIC_MARKET_BAZAAR_KEY, RHT_OVERWORLD_KEY, RG_MARKET_BAZAAR_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY,MOD_RANDOMIZER, {"the ", "la ", "den "}).CustomIcon(gQuestIconSmallKeyTex, ICON_SIZE_24); diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/market.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/market.cpp index 460c5c232b..bb4e610d83 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/market.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/market.cpp @@ -205,17 +205,18 @@ void RegionTable_Init_Market() { areaTable[RR_MARKET_TREASURE_CHEST_GAME] = Region("Market Treasure Chest Game", SCENE_TREASURE_BOX_SHOP, {}, { //Locations LOCATION(RC_GREG_HINT, logic->HasItem(RG_CHILD_WALLET) && logic->HasItem(RG_SPEAK_HYLIAN)), - LOCATION(RC_MARKET_TREASURE_CHEST_GAME_REWARD, logic->HasItem(RG_CHILD_WALLET) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->CanOpenLargeChest() && ((logic->CanUse(RG_LENS_OF_TRUTH) && !ctx->GetOption(RSK_SHUFFLE_CHEST_MINIGAME)) || (ctx->GetOption(RSK_SHUFFLE_CHEST_MINIGAME).Is(RO_CHEST_GAME_SINGLE_KEYS) && logic->SmallKeys(SCENE_TREASURE_BOX_SHOP, 6)) || (ctx->GetOption(RSK_SHUFFLE_CHEST_MINIGAME).Is(RO_CHEST_GAME_PACK) && logic->SmallKeys(SCENE_TREASURE_BOX_SHOP, 1)))), - LOCATION(RC_MARKET_TREASURE_CHEST_GAME_KEY_1, logic->HasItem(RG_CHILD_WALLET) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->CanOpenLargeChest() && ((ctx->GetOption(RSK_SHUFFLE_CHEST_MINIGAME).Is(RO_CHEST_GAME_SINGLE_KEYS) && logic->SmallKeys(SCENE_TREASURE_BOX_SHOP, 1)) || (ctx->GetOption(RSK_SHUFFLE_CHEST_MINIGAME).Is(RO_CHEST_GAME_PACK) && logic->SmallKeys(SCENE_TREASURE_BOX_SHOP, 1)) || (logic->CanUse(RG_LENS_OF_TRUTH) && !ctx->GetOption(RSK_SHUFFLE_CHEST_MINIGAME)))), - LOCATION(RC_MARKET_TREASURE_CHEST_GAME_ITEM_1, logic->HasItem(RG_CHILD_WALLET) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->CanOpenLargeChest() && ((ctx->GetOption(RSK_SHUFFLE_CHEST_MINIGAME).Is(RO_CHEST_GAME_SINGLE_KEYS) && logic->SmallKeys(SCENE_TREASURE_BOX_SHOP, 1)) || (ctx->GetOption(RSK_SHUFFLE_CHEST_MINIGAME).Is(RO_CHEST_GAME_PACK) && logic->SmallKeys(SCENE_TREASURE_BOX_SHOP, 1)) || (logic->CanUse(RG_LENS_OF_TRUTH) && !ctx->GetOption(RSK_SHUFFLE_CHEST_MINIGAME)))), - LOCATION(RC_MARKET_TREASURE_CHEST_GAME_KEY_2, logic->HasItem(RG_CHILD_WALLET) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->CanOpenLargeChest() && ((ctx->GetOption(RSK_SHUFFLE_CHEST_MINIGAME).Is(RO_CHEST_GAME_SINGLE_KEYS) && logic->SmallKeys(SCENE_TREASURE_BOX_SHOP, 2)) || (ctx->GetOption(RSK_SHUFFLE_CHEST_MINIGAME).Is(RO_CHEST_GAME_PACK) && logic->SmallKeys(SCENE_TREASURE_BOX_SHOP, 1)) || (logic->CanUse(RG_LENS_OF_TRUTH) && !ctx->GetOption(RSK_SHUFFLE_CHEST_MINIGAME)))), - LOCATION(RC_MARKET_TREASURE_CHEST_GAME_ITEM_2, logic->HasItem(RG_CHILD_WALLET) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->CanOpenLargeChest() && ((ctx->GetOption(RSK_SHUFFLE_CHEST_MINIGAME).Is(RO_CHEST_GAME_SINGLE_KEYS) && logic->SmallKeys(SCENE_TREASURE_BOX_SHOP, 2)) || (ctx->GetOption(RSK_SHUFFLE_CHEST_MINIGAME).Is(RO_CHEST_GAME_PACK) && logic->SmallKeys(SCENE_TREASURE_BOX_SHOP, 1)) || (logic->CanUse(RG_LENS_OF_TRUTH) && !ctx->GetOption(RSK_SHUFFLE_CHEST_MINIGAME)))), - LOCATION(RC_MARKET_TREASURE_CHEST_GAME_KEY_3, logic->HasItem(RG_CHILD_WALLET) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->CanOpenLargeChest() && ((ctx->GetOption(RSK_SHUFFLE_CHEST_MINIGAME).Is(RO_CHEST_GAME_SINGLE_KEYS) && logic->SmallKeys(SCENE_TREASURE_BOX_SHOP, 3)) || (ctx->GetOption(RSK_SHUFFLE_CHEST_MINIGAME).Is(RO_CHEST_GAME_PACK) && logic->SmallKeys(SCENE_TREASURE_BOX_SHOP, 1)) || (logic->CanUse(RG_LENS_OF_TRUTH) && !ctx->GetOption(RSK_SHUFFLE_CHEST_MINIGAME)))), - LOCATION(RC_MARKET_TREASURE_CHEST_GAME_ITEM_3, logic->HasItem(RG_CHILD_WALLET) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->CanOpenLargeChest() && ((ctx->GetOption(RSK_SHUFFLE_CHEST_MINIGAME).Is(RO_CHEST_GAME_SINGLE_KEYS) && logic->SmallKeys(SCENE_TREASURE_BOX_SHOP, 3)) || (ctx->GetOption(RSK_SHUFFLE_CHEST_MINIGAME).Is(RO_CHEST_GAME_PACK) && logic->SmallKeys(SCENE_TREASURE_BOX_SHOP, 1)) || (logic->CanUse(RG_LENS_OF_TRUTH) && !ctx->GetOption(RSK_SHUFFLE_CHEST_MINIGAME)))), - LOCATION(RC_MARKET_TREASURE_CHEST_GAME_KEY_4, logic->HasItem(RG_CHILD_WALLET) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->CanOpenLargeChest() && ((ctx->GetOption(RSK_SHUFFLE_CHEST_MINIGAME).Is(RO_CHEST_GAME_SINGLE_KEYS) && logic->SmallKeys(SCENE_TREASURE_BOX_SHOP, 4)) || (ctx->GetOption(RSK_SHUFFLE_CHEST_MINIGAME).Is(RO_CHEST_GAME_PACK) && logic->SmallKeys(SCENE_TREASURE_BOX_SHOP, 1)) || (logic->CanUse(RG_LENS_OF_TRUTH) && !ctx->GetOption(RSK_SHUFFLE_CHEST_MINIGAME)))), - LOCATION(RC_MARKET_TREASURE_CHEST_GAME_ITEM_4, logic->HasItem(RG_CHILD_WALLET) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->CanOpenLargeChest() && ((ctx->GetOption(RSK_SHUFFLE_CHEST_MINIGAME).Is(RO_CHEST_GAME_SINGLE_KEYS) && logic->SmallKeys(SCENE_TREASURE_BOX_SHOP, 4)) || (ctx->GetOption(RSK_SHUFFLE_CHEST_MINIGAME).Is(RO_CHEST_GAME_PACK) && logic->SmallKeys(SCENE_TREASURE_BOX_SHOP, 1)) || (logic->CanUse(RG_LENS_OF_TRUTH) && !ctx->GetOption(RSK_SHUFFLE_CHEST_MINIGAME)))), - LOCATION(RC_MARKET_TREASURE_CHEST_GAME_KEY_5, logic->HasItem(RG_CHILD_WALLET) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->CanOpenLargeChest() && ((ctx->GetOption(RSK_SHUFFLE_CHEST_MINIGAME).Is(RO_CHEST_GAME_SINGLE_KEYS) && logic->SmallKeys(SCENE_TREASURE_BOX_SHOP, 5)) || (ctx->GetOption(RSK_SHUFFLE_CHEST_MINIGAME).Is(RO_CHEST_GAME_PACK) && logic->SmallKeys(SCENE_TREASURE_BOX_SHOP, 1)) || (logic->CanUse(RG_LENS_OF_TRUTH) && !ctx->GetOption(RSK_SHUFFLE_CHEST_MINIGAME)))), - LOCATION(RC_MARKET_TREASURE_CHEST_GAME_ITEM_5, logic->HasItem(RG_CHILD_WALLET) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->CanOpenLargeChest() && ((ctx->GetOption(RSK_SHUFFLE_CHEST_MINIGAME).Is(RO_CHEST_GAME_SINGLE_KEYS) && logic->SmallKeys(SCENE_TREASURE_BOX_SHOP, 5)) || (ctx->GetOption(RSK_SHUFFLE_CHEST_MINIGAME).Is(RO_CHEST_GAME_PACK) && logic->SmallKeys(SCENE_TREASURE_BOX_SHOP, 1)) || (logic->CanUse(RG_LENS_OF_TRUTH) && !ctx->GetOption(RSK_SHUFFLE_CHEST_MINIGAME)))), + LOCATION(RC_MARKET_TREASURE_CHEST_GAME_SHOPKEEPER, logic->HasItem(RG_CHILD_WALLET) && logic->HasItem(RG_SPEAK_HYLIAN) && ctx->GetOption(RSK_SHUFFLE_CHEST_MINIGAME)), + LOCATION(RC_MARKET_TREASURE_CHEST_GAME_KEY_1, logic->CanOpenLargeChest() && (ctx->GetOption(RSK_SHUFFLE_CHEST_MINIGAME) ? logic->SmallKeys(SCENE_TREASURE_BOX_SHOP, 1) : (logic->HasItem(RG_CHILD_WALLET) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->CanUse(RG_LENS_OF_TRUTH)))), + LOCATION(RC_MARKET_TREASURE_CHEST_GAME_ITEM_1, logic->CanOpenLargeChest() && (ctx->GetOption(RSK_SHUFFLE_CHEST_MINIGAME) ? logic->SmallKeys(SCENE_TREASURE_BOX_SHOP, 1) : (logic->HasItem(RG_CHILD_WALLET) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->CanUse(RG_LENS_OF_TRUTH)))), + LOCATION(RC_MARKET_TREASURE_CHEST_GAME_KEY_2, logic->CanOpenLargeChest() && (ctx->GetOption(RSK_SHUFFLE_CHEST_MINIGAME) ? logic->SmallKeys(SCENE_TREASURE_BOX_SHOP, 2) : (logic->HasItem(RG_CHILD_WALLET) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->CanUse(RG_LENS_OF_TRUTH)))), + LOCATION(RC_MARKET_TREASURE_CHEST_GAME_ITEM_2, logic->CanOpenLargeChest() && (ctx->GetOption(RSK_SHUFFLE_CHEST_MINIGAME) ? logic->SmallKeys(SCENE_TREASURE_BOX_SHOP, 2) : (logic->HasItem(RG_CHILD_WALLET) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->CanUse(RG_LENS_OF_TRUTH)))), + LOCATION(RC_MARKET_TREASURE_CHEST_GAME_KEY_3, logic->CanOpenLargeChest() && (ctx->GetOption(RSK_SHUFFLE_CHEST_MINIGAME) ? logic->SmallKeys(SCENE_TREASURE_BOX_SHOP, 3) : (logic->HasItem(RG_CHILD_WALLET) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->CanUse(RG_LENS_OF_TRUTH)))), + LOCATION(RC_MARKET_TREASURE_CHEST_GAME_ITEM_3, logic->CanOpenLargeChest() && (ctx->GetOption(RSK_SHUFFLE_CHEST_MINIGAME) ? logic->SmallKeys(SCENE_TREASURE_BOX_SHOP, 3) : (logic->HasItem(RG_CHILD_WALLET) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->CanUse(RG_LENS_OF_TRUTH)))), + LOCATION(RC_MARKET_TREASURE_CHEST_GAME_KEY_4, logic->CanOpenLargeChest() && (ctx->GetOption(RSK_SHUFFLE_CHEST_MINIGAME) ? logic->SmallKeys(SCENE_TREASURE_BOX_SHOP, 4) : (logic->HasItem(RG_CHILD_WALLET) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->CanUse(RG_LENS_OF_TRUTH)))), + LOCATION(RC_MARKET_TREASURE_CHEST_GAME_ITEM_4, logic->CanOpenLargeChest() && (ctx->GetOption(RSK_SHUFFLE_CHEST_MINIGAME) ? logic->SmallKeys(SCENE_TREASURE_BOX_SHOP, 4) : (logic->HasItem(RG_CHILD_WALLET) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->CanUse(RG_LENS_OF_TRUTH)))), + LOCATION(RC_MARKET_TREASURE_CHEST_GAME_KEY_5, logic->CanOpenLargeChest() && (ctx->GetOption(RSK_SHUFFLE_CHEST_MINIGAME) ? logic->SmallKeys(SCENE_TREASURE_BOX_SHOP, 5) : (logic->HasItem(RG_CHILD_WALLET) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->CanUse(RG_LENS_OF_TRUTH)))), + LOCATION(RC_MARKET_TREASURE_CHEST_GAME_ITEM_5, logic->CanOpenLargeChest() && (ctx->GetOption(RSK_SHUFFLE_CHEST_MINIGAME) ? logic->SmallKeys(SCENE_TREASURE_BOX_SHOP, 5) : (logic->HasItem(RG_CHILD_WALLET) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->CanUse(RG_LENS_OF_TRUTH)))), + LOCATION(RC_MARKET_TREASURE_CHEST_GAME_REWARD, logic->CanOpenLargeChest() && (ctx->GetOption(RSK_SHUFFLE_CHEST_MINIGAME) ? logic->SmallKeys(SCENE_TREASURE_BOX_SHOP, 6) : (logic->HasItem(RG_CHILD_WALLET) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->CanUse(RG_LENS_OF_TRUTH)))), }, { //Exits ENTRANCE(RR_THE_MARKET, true), diff --git a/soh/soh/Enhancements/randomizer/location_list.cpp b/soh/soh/Enhancements/randomizer/location_list.cpp index 605acf35fc..0f6665067b 100644 --- a/soh/soh/Enhancements/randomizer/location_list.cpp +++ b/soh/soh/Enhancements/randomizer/location_list.cpp @@ -233,6 +233,7 @@ void Rando::StaticData::InitLocationTable() { locationTable[RC_MARKET_TREASURE_CHEST_GAME_KEY_3] = Location::Chest(RC_MARKET_TREASURE_CHEST_GAME_KEY_3, RCQUEST_BOTH, RCTYPE_CHEST_GAME, ACTOR_EN_BOX, SCENE_TREASURE_BOX_SHOP, 0x00, "Chest Game Third Room Key", RHT_MARKET_TREASURE_CHEST_GAME_KEY_3, RG_TREASURE_GAME_SMALL_KEY, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MARKET_TREASURE_CHEST_GAME_KEY_3)); locationTable[RC_MARKET_TREASURE_CHEST_GAME_KEY_4] = Location::Chest(RC_MARKET_TREASURE_CHEST_GAME_KEY_4, RCQUEST_BOTH, RCTYPE_CHEST_GAME, ACTOR_EN_BOX, SCENE_TREASURE_BOX_SHOP, 0x00, "Chest Game Fourth Room Key", RHT_MARKET_TREASURE_CHEST_GAME_KEY_4, RG_TREASURE_GAME_SMALL_KEY, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MARKET_TREASURE_CHEST_GAME_KEY_4)); locationTable[RC_MARKET_TREASURE_CHEST_GAME_KEY_5] = Location::Chest(RC_MARKET_TREASURE_CHEST_GAME_KEY_5, RCQUEST_BOTH, RCTYPE_CHEST_GAME, ACTOR_EN_BOX, SCENE_TREASURE_BOX_SHOP, 0x00, "Chest Game Fifth Room Key", RHT_MARKET_TREASURE_CHEST_GAME_KEY_5, RG_TREASURE_GAME_SMALL_KEY, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MARKET_TREASURE_CHEST_GAME_KEY_5)); + locationTable[RC_MARKET_TREASURE_CHEST_GAME_SHOPKEEPER] = Location::Base(RC_MARKET_TREASURE_CHEST_GAME_SHOPKEEPER, RCQUEST_BOTH, RCTYPE_CHEST_GAME, ACTOR_EN_TAKARA_MAN, SCENE_TREASURE_BOX_SHOP, 0x00, "Chest Game Shopkeeper", RHT_MARKET_TREASURE_CHEST_GAME_SHOPKEEPER, RG_TREASURE_GAME_SMALL_KEY, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MARKET_TREASURE_CHEST_GAME_SHOPKEEPER)); // Hyrule Castle locationTable[RC_HC_MALON_EGG] = Location::Base(RC_HC_MALON_EGG, RCQUEST_BOTH, RCTYPE_STANDARD, ACTOR_ID_MAX, SCENE_HYRULE_CASTLE, 0x00, "Malon Egg", RHT_HC_MALON_EGG, RG_WEIRD_EGG, SpoilerCollectionCheck::EventChkInf(EVENTCHKINF_OBTAINED_POCKET_EGG), true); locationTable[RC_HC_ZELDAS_LETTER] = Location::Base(RC_HC_ZELDAS_LETTER, RCQUEST_BOTH, RCTYPE_STANDARD, ACTOR_ID_MAX, SCENE_CASTLE_COURTYARD_ZELDA, 0x00, "Zeldas Letter", RHT_HC_ZELDAS_LETTER, RG_ZELDAS_LETTER, SpoilerCollectionCheck::EventChkInf(EVENTCHKINF_OBTAINED_ZELDAS_LETTER), true); diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index fce010dc54..97ad226cfd 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -1987,7 +1987,8 @@ std::map Logic::RandoGetToDungeonScene = { { RG_SHADOW_TEMPLE_COMPASS, SCENE_SHADOW_TEMPLE }, { RG_BOTTOM_OF_THE_WELL_COMPASS, SCENE_BOTTOM_OF_THE_WELL }, { RG_ICE_CAVERN_COMPASS, SCENE_ICE_CAVERN }, - { RG_TREASURE_GAME_SMALL_KEY, SCENE_TREASURE_BOX_SHOP } + { RG_TREASURE_GAME_SMALL_KEY, SCENE_TREASURE_BOX_SHOP }, + { RG_TREASURE_GAME_KEY_RING, SCENE_TREASURE_BOX_SHOP } }; std::map Logic::RandoGetToQuestItem = { @@ -2485,7 +2486,7 @@ void Logic::ApplyItemEffect(Item& item, bool state) { case ITEMTYPE_FORTRESS_SMALLKEY: case ITEMTYPE_SMALLKEY: { auto randoGet = item.GetRandomizerGet(); - auto keyring = randoGet >= RG_FOREST_TEMPLE_KEY_RING && randoGet <= RG_GANONS_CASTLE_KEY_RING; + auto keyring = randoGet >= RG_FOREST_TEMPLE_KEY_RING && randoGet <= RG_TREASURE_GAME_KEY_RING; auto dungeonIndex = RandoGetToDungeonScene.find(randoGet)->second; auto count = GetSmallKeyCount(dungeonIndex); if (!state) { diff --git a/soh/soh/Enhancements/randomizer/option_descriptions.cpp b/soh/soh/Enhancements/randomizer/option_descriptions.cpp index 8e801ddc31..3079737adf 100644 --- a/soh/soh/Enhancements/randomizer/option_descriptions.cpp +++ b/soh/soh/Enhancements/randomizer/option_descriptions.cpp @@ -55,7 +55,7 @@ void Settings::CreateOptionDescriptions() { "Free - Bridge is repaired from start, and Nabooru cannot spawn.\n" "If the Gerudo Membership Card isn't shuffled, you start with it.\n" "\n" - "Only \"Normal\" is compatible with Gerudo Fortress Key Rings."; + "Only \"Normal\" is compatible with Gerudo Fortress Keyrings."; mOptionDescriptions[RSK_RAINBOW_BRIDGE] = "Alters the requirements to open the bridge to Ganon's Castle.\n" "\n" @@ -526,6 +526,13 @@ void Settings::CreateOptionDescriptions() { "D-pad.\n" "\n" "If disabled, only the Claim Check will be found in the pool."; + mOptionDescriptions[RSK_SHUFFLE_CHEST_MINIGAME] = + "Shuffles the contents of the Market Treasure Chest Game, including the item you pay " + "the host for. Both chests in every room can be opened, and the locked doors are opened " + "with keys found elsewhere instead of keys won in the game.\n" + "\n" + "Six Chest Game Small Keys are added to the pool, or a single keyring holding all six " + "if the Chest Minigame Keyring is selected under Keyrings."; mOptionDescriptions[RSK_EARLY_GRANNYS_SHOP] = "Makes Granny's Potion Shop available from start, rather than requiring Claim Check to be found first.\n" "\n" @@ -625,18 +632,18 @@ void Settings::CreateOptionDescriptions() { "\n" "Off - No dungeons will have their keys replaced with keyrings.\n" "\n" - "Random - A random amount of dungeons (0-8 or 9) will have their keys replaced with keyrings.\n" + "Random - A random amount of dungeons will have their keys replaced with keyrings.\n" "\n" "Count - A specified amount of randomly selected dungeons will have their keys replaced with keyrings.\n" "\n" "Selection - Hand select which dungeons will have their keys replaced with keyrings\n" "(can also be left as random, in which case each one will have a 50% chance of being a keyring).\n" "\n" - "Selecting key ring for dungeons will have no effect if Small Keys are set to Start With or Vanilla.\n" + "Selecting keyring for dungeons will have no effect if Small Keys are set to Start With or Vanilla.\n" "\n" - "If Gerudo Fortress Carpenters is set to Normal, and Gerudo Fortress Keys is set to anything " - "other than Vanilla, then the maximum amount of Key Rings that can be selected by Random or " - "Count will be 9. Otherwise, the maximum amount of Key Rings will be 8."; + "The maximum amount of Keyrings that can be selected by Random or Count is 8, plus one if " + "Gerudo Fortress Carpenters is set to Normal and Gerudo Fortress Keys is set to anything " + "other than Vanilla, plus one if Shuffle Chest Minigame is on."; mOptionDescriptions[RSK_GERUDO_KEYS] = "Vanilla - Thieves' Hideout Keys will appear in their vanilla locations.\n" "\n" "Any dungeon - Thieves' Hideout Keys can only appear inside of any dungon.\n" diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index cd3314158c..9fba15043c 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -715,7 +715,7 @@ Rando::Location* Randomizer::GetCheckObjectFromActor(s16 actorId, s16 sceneNum, specialRc = RC_MARKET_TREASURE_CHEST_GAME_REWARD; } - // todo: handle the itemetc part of this so drawing works when we implement shuffle + // the low bits of a game chest's params are its treasure flag, odd for the key chest if (actorId == ACTOR_EN_BOX) { bool isAKey = (actorParams & 0x60) == 0x20; if ((actorParams & 0xF) < 2) { @@ -1277,8 +1277,8 @@ extern "C" u16 Randomizer_Item_Give(PlayState* play, GetItemEntry giEntry) { } // dungeon items - if ((item >= RG_FOREST_TEMPLE_SMALL_KEY && item <= RG_GANONS_CASTLE_SMALL_KEY) || - (item >= RG_FOREST_TEMPLE_KEY_RING && item <= RG_GANONS_CASTLE_KEY_RING) || + if ((item >= RG_FOREST_TEMPLE_SMALL_KEY && item <= RG_TREASURE_GAME_SMALL_KEY) || + (item >= RG_FOREST_TEMPLE_KEY_RING && item <= RG_TREASURE_GAME_KEY_RING) || (item >= RG_FOREST_TEMPLE_BOSS_KEY && item <= RG_GANONS_CASTLE_BOSS_KEY) || (item >= RG_DEKU_TREE_MAP && item <= RG_ICE_CAVERN_MAP) || (item >= RG_DEKU_TREE_COMPASS && item <= RG_ICE_CAVERN_COMPASS)) { @@ -1366,11 +1366,16 @@ extern "C" u16 Randomizer_Item_Give(PlayState* play, GetItemEntry giEntry) { mapIndex = SCENE_INSIDE_GANONS_CASTLE; numOfKeysOnKeyring = GANONS_CASTLE_SMALL_KEY_MAX; break; + case RG_TREASURE_GAME_SMALL_KEY: + case RG_TREASURE_GAME_KEY_RING: + mapIndex = SCENE_TREASURE_BOX_SHOP; + numOfKeysOnKeyring = TREASURE_GAME_SMALL_KEY_MAX; + break; default: break; } - if ((item >= RG_FOREST_TEMPLE_SMALL_KEY) && (item <= RG_GANONS_CASTLE_SMALL_KEY)) { + if ((item >= RG_FOREST_TEMPLE_SMALL_KEY) && (item <= RG_TREASURE_GAME_SMALL_KEY)) { gSaveContext.ship.stats.dungeonKeys[mapIndex]++; if (gSaveContext.inventory.dungeonKeys[mapIndex] < 0) { gSaveContext.inventory.dungeonKeys[mapIndex] = 1; @@ -1380,7 +1385,7 @@ extern "C" u16 Randomizer_Item_Give(PlayState* play, GetItemEntry giEntry) { return Return_Item_Entry(giEntry, RG_NONE); } - if ((item >= RG_FOREST_TEMPLE_KEY_RING) && (item <= RG_GANONS_CASTLE_KEY_RING)) { + if ((item >= RG_FOREST_TEMPLE_KEY_RING) && (item <= RG_TREASURE_GAME_KEY_RING)) { gSaveContext.ship.stats.dungeonKeys[mapIndex] = numOfKeysOnKeyring; gSaveContext.inventory.dungeonKeys[mapIndex] = numOfKeysOnKeyring; return Return_Item_Entry(giEntry, RG_NONE); diff --git a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerCheck.h b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerCheck.h index f9ac7a73bc..d82755561c 100644 --- a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerCheck.h +++ b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerCheck.h @@ -102,6 +102,7 @@ RANDO_ENUM_ITEM(RC_MARKET_TREASURE_CHEST_GAME_KEY_2) RANDO_ENUM_ITEM(RC_MARKET_TREASURE_CHEST_GAME_KEY_3) RANDO_ENUM_ITEM(RC_MARKET_TREASURE_CHEST_GAME_KEY_4) RANDO_ENUM_ITEM(RC_MARKET_TREASURE_CHEST_GAME_KEY_5) +RANDO_ENUM_ITEM(RC_MARKET_TREASURE_CHEST_GAME_SHOPKEEPER) RANDO_ENUM_ITEM(RC_MARKET_GS_GUARD_HOUSE) RANDO_ENUM_ITEM(RC_MARKET_BAZAAR_ITEM_1) RANDO_ENUM_ITEM(RC_MARKET_BAZAAR_ITEM_2) diff --git a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerHintTextKey.h b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerHintTextKey.h index c5721b790a..b2de108d74 100644 --- a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerHintTextKey.h +++ b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerHintTextKey.h @@ -182,6 +182,7 @@ RANDO_ENUM_ITEM(RHT_MARKET_TREASURE_CHEST_GAME_KEY_2) RANDO_ENUM_ITEM(RHT_MARKET_TREASURE_CHEST_GAME_KEY_3) RANDO_ENUM_ITEM(RHT_MARKET_TREASURE_CHEST_GAME_KEY_4) RANDO_ENUM_ITEM(RHT_MARKET_TREASURE_CHEST_GAME_KEY_5) +RANDO_ENUM_ITEM(RHT_MARKET_TREASURE_CHEST_GAME_SHOPKEEPER) RANDO_ENUM_ITEM(RHT_MARKET_GS_GUARD_HOUSE) RANDO_ENUM_ITEM(RHT_MARKET_BAZAAR_ITEM_1) RANDO_ENUM_ITEM(RHT_MARKET_BAZAAR_ITEM_2) diff --git a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerInf.h b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerInf.h index 50ee49da5e..ea0c04f4b0 100644 --- a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerInf.h +++ b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerInf.h @@ -1539,6 +1539,7 @@ RANDO_ENUM_ITEM(RAND_INF_MARKET_TREASURE_CHEST_GAME_KEY_2) RANDO_ENUM_ITEM(RAND_INF_MARKET_TREASURE_CHEST_GAME_KEY_3) RANDO_ENUM_ITEM(RAND_INF_MARKET_TREASURE_CHEST_GAME_KEY_4) RANDO_ENUM_ITEM(RAND_INF_MARKET_TREASURE_CHEST_GAME_KEY_5) +RANDO_ENUM_ITEM(RAND_INF_MARKET_TREASURE_CHEST_GAME_SHOPKEEPER) RANDO_ENUM_ITEM(RAND_INF_TH_ITEM_FROM_LEADER_OF_FORTRESS) RANDO_ENUM_ITEM(RAND_INF_GF_GTG_GATE_PERMANENTLY_OPEN) diff --git a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerOptions.h b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerOptions.h index d81a554b34..6ed8fac87d 100644 --- a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerOptions.h +++ b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerOptions.h @@ -524,12 +524,6 @@ RANDO_ENUM_ITEM(RO_LOCATION_INCLUDE) RANDO_ENUM_ITEM(RO_LOCATION_EXCLUDE) RANDO_ENUM_END(RandoOptionLocationInclusion) -RANDO_ENUM_BEGIN(RandoOptionChestGame) -RANDO_ENUM_ITEM(RO_CHEST_GAME_OFF) -RANDO_ENUM_ITEM(RO_CHEST_GAME_SINGLE_KEYS) -RANDO_ENUM_ITEM(RO_CHEST_GAME_PACK) -RANDO_ENUM_END(RandoOptionChestGame) - RANDO_ENUM_BEGIN(RandoOptionMQSet) RANDO_ENUM_ITEM(RO_MQ_SET_VANILLA) RANDO_ENUM_ITEM(RO_MQ_SET_MQ) diff --git a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerSettingKey.h b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerSettingKey.h index a987d082b3..e8926f38e7 100644 --- a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerSettingKey.h +++ b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerSettingKey.h @@ -248,6 +248,7 @@ RANDO_ENUM_ITEM(RSK_KEYRINGS_SHADOW_TEMPLE) RANDO_ENUM_ITEM(RSK_KEYRINGS_BOTTOM_OF_THE_WELL) RANDO_ENUM_ITEM(RSK_KEYRINGS_GTG) RANDO_ENUM_ITEM(RSK_KEYRINGS_GANONS_CASTLE) +RANDO_ENUM_ITEM(RSK_KEYRINGS_CHEST_GAME) RANDO_ENUM_ITEM(RSK_SHUFFLE_ENTRANCES) RANDO_ENUM_ITEM(RSK_SHUFFLE_DUNGEON_ENTRANCES) RANDO_ENUM_ITEM(RSK_SHUFFLE_OVERWORLD_ENTRANCES) diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_objects.cpp b/soh/soh/Enhancements/randomizer/randomizer_check_objects.cpp index 0f587596e0..313c8fdae6 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_check_objects.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_check_objects.cpp @@ -160,8 +160,8 @@ void RandomizerCheckObjects::UpdateImGuiVisibility() { RCTYPE_GOSSIP_STONE) && // don't show gossip stones (maybe gossipsanity will be a thing eventually?) (location.GetRCType() != RCTYPE_STATIC_HINT) && // don't show static hints (location.GetRCType() != RCTYPE_LINKS_POCKET) && // links pocket can be set to nothing if needed - (location.GetRCType() != - RCTYPE_CHEST_GAME) && // don't show non final reward chest game checks until we support shuffling them + (location.GetRCType() != RCTYPE_CHEST_GAME || + CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleChestMinigame"), RO_GENERIC_OFF)) && (location.GetRCType() != RCTYPE_SKULL_TOKEN || (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleTokens"), RO_TOKENSANITY_OFF) == RO_TOKENSANITY_ALL) || ((CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleTokens"), RO_TOKENSANITY_OFF) == diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp index fd0c48d749..13bbe75ee9 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp @@ -109,6 +109,7 @@ bool showGanonBossKey; bool showOcarinas; bool show100SkullReward; bool showLinksPocket; +bool showChestMinigame; bool fortressFast; bool fortressNormal; @@ -1588,96 +1589,71 @@ void LoadSettings() { // If in vanilla, _try_ to show items that at least are needed for 100% showShops = - IS_RANDO ? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHOPSANITY) != RO_SHOPSANITY_OFF : false; - showBeans = IS_RANDO ? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_MERCHANTS) == - RO_SHUFFLE_MERCHANTS_BEANS_ONLY || - OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_MERCHANTS) == - RO_SHUFFLE_MERCHANTS_ALL - : true; + IS_RANDO && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHOPSANITY) != RO_SHOPSANITY_OFF; + showBeans = + !IS_RANDO || + OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_MERCHANTS) == + RO_SHUFFLE_MERCHANTS_BEANS_ONLY || + OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_MERCHANTS) == RO_SHUFFLE_MERCHANTS_ALL; showScrubs = - IS_RANDO ? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_SCRUBS) == RO_SCRUBS_ALL : false; + IS_RANDO && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_SCRUBS) == RO_SCRUBS_ALL; showMajorScrubs = - IS_RANDO ? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_SCRUBS) != RO_SCRUBS_OFF : false; - showMerchants = IS_RANDO ? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_MERCHANTS) == - RO_SHUFFLE_MERCHANTS_ALL_BUT_BEANS || - OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_MERCHANTS) == - RO_SHUFFLE_MERCHANTS_ALL - : true; - showSongs = IS_RANDO - ? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_SONGS) != RO_SONG_SHUFFLE_OFF - : false; - showBeehives = IS_RANDO - ? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_BEEHIVES) == RO_GENERIC_YES - : false; - showCows = - IS_RANDO ? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_COWS) == RO_GENERIC_YES : false; + IS_RANDO && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_SCRUBS) != RO_SCRUBS_OFF; + showMerchants = + !IS_RANDO || + OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_MERCHANTS) == + RO_SHUFFLE_MERCHANTS_ALL_BUT_BEANS || + OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_MERCHANTS) == RO_SHUFFLE_MERCHANTS_ALL; + showSongs = + IS_RANDO && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_SONGS) != RO_SONG_SHUFFLE_OFF; + showBeehives = + IS_RANDO && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_BEEHIVES) == RO_GENERIC_YES; + showCows = IS_RANDO && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_COWS) == RO_GENERIC_YES; showAdultTrade = - IS_RANDO ? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_ADULT_TRADE) == RO_GENERIC_YES - : true; - showKokiriSword = - IS_RANDO ? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_KOKIRI_SWORD) == RO_GENERIC_YES - : true; - showMasterSword = - IS_RANDO ? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_MASTER_SWORD) == RO_GENERIC_YES - : true; - showHyruleLoach = - IS_RANDO ? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_FISHSANITY) == RO_FISHSANITY_HYRULE_LOACH - : false; - showWeirdEgg = IS_RANDO ? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_WEIRD_EGG) == - RO_WEIRD_EGG_SHUFFLED - : true; - showZeldasLetter = - IS_RANDO ? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_ZELDAS_LETTER) == RO_GENERIC_YES - : true; - showGerudoCard = IS_RANDO ? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue( - RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD) == RO_GENERIC_YES - : true; - showFrogSongRupees = - IS_RANDO - ? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_FROG_SONG_RUPEES) == RO_GENERIC_YES - : false; - showFountainFairies = - IS_RANDO - ? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_FOUNTAIN_FAIRIES) == RO_GENERIC_YES - : false; - showStoneFairies = - IS_RANDO ? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_STONE_FAIRIES) == RO_GENERIC_YES - : false; + !IS_RANDO || OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_ADULT_TRADE) == RO_GENERIC_YES; + showKokiriSword = !IS_RANDO || OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_KOKIRI_SWORD) == + RO_GENERIC_YES; + showMasterSword = !IS_RANDO || OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_MASTER_SWORD) == + RO_GENERIC_YES; + showHyruleLoach = IS_RANDO && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_FISHSANITY) == + RO_FISHSANITY_HYRULE_LOACH; + showWeirdEgg = !IS_RANDO || OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_WEIRD_EGG) == + RO_WEIRD_EGG_SHUFFLED; + showZeldasLetter = !IS_RANDO || OTRGlobals::Instance->gRandomizer->GetRandoSettingValue( + RSK_SHUFFLE_ZELDAS_LETTER) == RO_GENERIC_YES; + showGerudoCard = !IS_RANDO || OTRGlobals::Instance->gRandomizer->GetRandoSettingValue( + RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD) == RO_GENERIC_YES; + showFrogSongRupees = IS_RANDO && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue( + RSK_SHUFFLE_FROG_SONG_RUPEES) == RO_GENERIC_YES; + showFountainFairies = IS_RANDO && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue( + RSK_SHUFFLE_FOUNTAIN_FAIRIES) == RO_GENERIC_YES; + showStoneFairies = IS_RANDO && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_STONE_FAIRIES) == + RO_GENERIC_YES; showBeanFairies = - IS_RANDO ? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_BEAN_FAIRIES) == RO_GENERIC_YES - : false; + IS_RANDO && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_BEAN_FAIRIES) == RO_GENERIC_YES; showSongFairies = - IS_RANDO ? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_SONG_FAIRIES) == RO_GENERIC_YES - : false; - showButterflyFairies = - IS_RANDO - ? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_BUTTERFLY_FAIRIES) == RO_GENERIC_YES - : false; - showStartingMapsCompasses = IS_RANDO ? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue( - RSK_SHUFFLE_MAPANDCOMPASS) != RO_DUNGEON_ITEM_LOC_VANILLA - : false; - showKeysanity = - IS_RANDO ? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_KEYSANITY) != RO_DUNGEON_ITEM_LOC_VANILLA - : false; - showBossKeysanity = IS_RANDO ? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_BOSS_KEYSANITY) != - RO_DUNGEON_ITEM_LOC_VANILLA - : false; + IS_RANDO && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_SONG_FAIRIES) == RO_GENERIC_YES; + showButterflyFairies = IS_RANDO && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue( + RSK_SHUFFLE_BUTTERFLY_FAIRIES) == RO_GENERIC_YES; + showStartingMapsCompasses = IS_RANDO && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue( + RSK_SHUFFLE_MAPANDCOMPASS) != RO_DUNGEON_ITEM_LOC_VANILLA; + showKeysanity = IS_RANDO && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_KEYSANITY) != + RO_DUNGEON_ITEM_LOC_VANILLA; + showBossKeysanity = IS_RANDO && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_BOSS_KEYSANITY) != + RO_DUNGEON_ITEM_LOC_VANILLA; showGerudoFortressKeys = - IS_RANDO ? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_GERUDO_KEYS) != RO_GERUDO_KEYS_VANILLA - : false; - showGanonBossKey = IS_RANDO ? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_GANONS_BOSS_KEY) != - RO_GANON_BOSS_KEY_VANILLA - : false; - showOcarinas = IS_RANDO - ? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_OCARINA) == RO_GENERIC_YES - : false; - show100SkullReward = - IS_RANDO ? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_100_GS_REWARD) == RO_GENERIC_YES - : false; - showLinksPocket = - IS_RANDO ? // don't show Link's Pocket if not randomizer, or if rando and pocket is disabled - OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_LINKS_POCKET) != RO_LINKS_POCKET_NOTHING - : false; + IS_RANDO && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_GERUDO_KEYS) != RO_GERUDO_KEYS_VANILLA; + showGanonBossKey = IS_RANDO && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_GANONS_BOSS_KEY) != + RO_GANON_BOSS_KEY_VANILLA; + showOcarinas = + IS_RANDO && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_OCARINA) == RO_GENERIC_YES; + show100SkullReward = IS_RANDO && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue( + RSK_SHUFFLE_100_GS_REWARD) == RO_GENERIC_YES; + // don't show Link's Pocket if not randomizer, or if rando and pocket is disabled + showLinksPocket = IS_RANDO && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_LINKS_POCKET) != + RO_LINKS_POCKET_NOTHING; + showChestMinigame = IS_RANDO && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue( + RSK_SHUFFLE_CHEST_MINIGAME) != RO_GENERIC_OFF; if (IS_RANDO) { switch (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_TOKENS)) { @@ -1918,8 +1894,7 @@ bool IsCheckShuffled(RandomizerCheck rc) { return (loc->GetArea() != RCAREA_INVALID) && // don't show Invalid locations (loc->GetRCType() != RCTYPE_GOSSIP_STONE) && // TODO: Don't show hints until tracker supports them (loc->GetRCType() != RCTYPE_STATIC_HINT) && // TODO: Don't show hints until tracker supports them - (loc->GetRCType() != RCTYPE_CHEST_GAME) && // don't show non final reward chest game checks until we - // support shuffling them + (loc->GetRCType() != RCTYPE_CHEST_GAME || showChestMinigame) && (rc != RC_HC_ZELDAS_LETTER || showZeldasLetter) && (rc != RC_LINKS_POCKET || showLinksPocket) && OTRGlobals::Instance->gRandoContext->IsQuestOfLocationActive(rc) && (loc->GetRCType() != RCTYPE_SHOP || diff --git a/soh/soh/Enhancements/randomizer/settings.cpp b/soh/soh/Enhancements/randomizer/settings.cpp index 5fe7d8ed13..3c96c31049 100644 --- a/soh/soh/Enhancements/randomizer/settings.cpp +++ b/soh/soh/Enhancements/randomizer/settings.cpp @@ -137,6 +137,32 @@ void Settings::HandleMixedEntrancePoolsUI() { } } +void Settings::HandleKeyringUI() { + const bool fortressKeyring = + CVarGetInteger(CVAR_RANDOMIZER_SETTING("FortressCarpenters"), RO_GF_CARPENTERS_NORMAL) == + RO_GF_CARPENTERS_NORMAL && + CVarGetInteger(CVAR_RANDOMIZER_SETTING("GerudoKeys"), RO_GERUDO_KEYS_VANILLA) != RO_GERUDO_KEYS_VANILLA; + const bool chestGameKeyring = + CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleChestMinigame"), RO_GENERIC_OFF) != RO_GENERIC_OFF; + const uint8_t maxKeyringCount = 8 + fortressKeyring + chestGameKeyring; + if (mOptions[RSK_KEYRINGS_RANDOM_COUNT].GetOptionCount() != maxKeyringCount + 1) { + mOptions[RSK_KEYRINGS_RANDOM_COUNT].ChangeOptions(NumOpts(0, maxKeyringCount)); + } + if (fortressKeyring) { + mOptions[RSK_KEYRINGS_GERUDO_FORTRESS].Enable(); + } else { + mOptions[RSK_KEYRINGS_GERUDO_FORTRESS].Disable( + "Disabled because the currently selected Gerudo Fortress Carpenters\n" + "setting and/or Gerudo Fortress Keys setting is incompatible with\n" + "having a Gerudo Fortress Keyring."); + } + if (chestGameKeyring) { + mOptions[RSK_KEYRINGS_CHEST_GAME].Enable(); + } else { + mOptions[RSK_KEYRINGS_CHEST_GAME].Disable("Disabled because Shuffle Chest Minigame is off."); + } +} + void Settings::HandleStartingAgeUI() { // Starting Age - Disabled under very specific conditions unless it's No Logic if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("LogicRules"), RO_LOGIC_GLITCHLESS) != RO_LOGIC_NO_LOGIC && @@ -175,25 +201,7 @@ void Settings::CreateOptions() { OPT_BOOL(RSK_LOCK_OVERWORLD_DOORS, "Lock Overworld Doors", CVAR_RANDOMIZER_SETTING("LockOverworldDoors"), mOptionDescriptions[RSK_LOCK_OVERWORLD_DOORS]); OPT_U8(RSK_GERUDO_FORTRESS, "Fortress Carpenters", {"Normal", "Fast", "Free"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("FortressCarpenters"), mOptionDescriptions[RSK_GERUDO_FORTRESS]); OPT_CALLBACK(RSK_GERUDO_FORTRESS, { - const uint8_t maxKeyringCount = - (CVarGetInteger(CVAR_RANDOMIZER_SETTING("FortressCarpenters"), RO_GF_CARPENTERS_NORMAL) == - RO_GF_CARPENTERS_NORMAL && - CVarGetInteger(CVAR_RANDOMIZER_SETTING("GerudoKeys"), RO_GERUDO_KEYS_VANILLA) != RO_GERUDO_KEYS_VANILLA) - ? 9 - : 8; - if (mOptions[RSK_KEYRINGS_RANDOM_COUNT].GetOptionCount() != maxKeyringCount + 1) { - mOptions[RSK_KEYRINGS_RANDOM_COUNT].ChangeOptions(NumOpts(0, maxKeyringCount)); - } - if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("FortressCarpenters"), RO_GF_CARPENTERS_NORMAL) != - RO_GF_CARPENTERS_NORMAL || - CVarGetInteger(CVAR_RANDOMIZER_SETTING("GerudoKeys"), RO_GERUDO_KEYS_VANILLA) == RO_GERUDO_KEYS_VANILLA) { - mOptions[RSK_KEYRINGS_GERUDO_FORTRESS].Disable( - "Disabled because the currently selected Gerudo Fortress Carpenters\n" - "setting and/or Gerudo Fortress Keys setting is incompatible with\n" - "having a Gerudo Fortress Keyring."); - } else { - mOptions[RSK_KEYRINGS_GERUDO_FORTRESS].Enable(); - } + HandleKeyringUI(); }); OPT_U8(RSK_RAINBOW_BRIDGE, "Rainbow Bridge", {"Vanilla", "Always open", "Stones", "Medallions", "Dungeon rewards", "Dungeons", "Tokens", "Triforce Pieces", "Greg"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("RainbowBridge"), mOptionDescriptions[RSK_RAINBOW_BRIDGE], WIDGET_CVAR_COMBOBOX, RO_BRIDGE_VANILLA, false, nullptr, IMFLAG_NONE); OPT_CALLBACK(RSK_RAINBOW_BRIDGE, { @@ -1042,7 +1050,10 @@ void Settings::CreateOptions() { mOptions[RSK_EARLY_GRANNYS_SHOP].Enable(); } }); - OPT_U8(RSK_SHUFFLE_CHEST_MINIGAME, "Shuffle Chest Minigame", {"Off", "On (Separate)", "On (Pack)"}); + OPT_BOOL(RSK_SHUFFLE_CHEST_MINIGAME, "Shuffle Chest Minigame", CVAR_RANDOMIZER_SETTING("ShuffleChestMinigame"), mOptionDescriptions[RSK_SHUFFLE_CHEST_MINIGAME]); + OPT_CALLBACK(RSK_SHUFFLE_CHEST_MINIGAME, { + HandleKeyringUI(); + }); OPT_BOOL(RSK_SHUFFLE_100_GS_REWARD, "Shuffle 100 GS Reward", CVAR_RANDOMIZER_SETTING("Shuffle100GSReward"), mOptionDescriptions[RSK_SHUFFLE_100_GS_REWARD], IMFLAG_SEPARATOR_BOTTOM, WIDGET_CVAR_CHECKBOX, RO_GENERIC_OFF); OPT_CALLBACK(RSK_SHUFFLE_100_GS_REWARD, { if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("Shuffle100GSReward"), RO_GENERIC_OFF)) { @@ -1104,25 +1115,7 @@ void Settings::CreateOptions() { OPT_U8(RSK_KEYSANITY, "Small Key Shuffle", {"Start With", "Vanilla", "Own Dungeon", "Any Dungeon", "Overworld", "Anywhere"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("Keysanity"), mOptionDescriptions[RSK_KEYSANITY], WIDGET_CVAR_COMBOBOX, RO_DUNGEON_ITEM_LOC_OWN_DUNGEON); OPT_U8(RSK_GERUDO_KEYS, "Gerudo Fortress Keys", {"Vanilla", "Any Dungeon", "Overworld", "Anywhere"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("GerudoKeys"), mOptionDescriptions[RSK_GERUDO_KEYS], WIDGET_CVAR_COMBOBOX, RO_GERUDO_KEYS_VANILLA); OPT_CALLBACK(RSK_GERUDO_KEYS, { - const uint8_t maxKeyringCount = - (CVarGetInteger(CVAR_RANDOMIZER_SETTING("FortressCarpenters"), RO_GF_CARPENTERS_NORMAL) == - RO_GF_CARPENTERS_NORMAL && - CVarGetInteger(CVAR_RANDOMIZER_SETTING("GerudoKeys"), RO_GERUDO_KEYS_VANILLA) != RO_GERUDO_KEYS_VANILLA) - ? 9 - : 8; - if (mOptions[RSK_KEYRINGS_RANDOM_COUNT].GetOptionCount() != maxKeyringCount + 1) { - mOptions[RSK_KEYRINGS_RANDOM_COUNT].ChangeOptions(NumOpts(0, maxKeyringCount)); - } - if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("FortressCarpenters"), RO_GF_CARPENTERS_NORMAL) != - RO_GF_CARPENTERS_NORMAL || - CVarGetInteger(CVAR_RANDOMIZER_SETTING("GerudoKeys"), RO_GERUDO_KEYS_VANILLA) == RO_GERUDO_KEYS_VANILLA) { - mOptions[RSK_KEYRINGS_GERUDO_FORTRESS].Disable( - "Disabled because the currently selected Gerudo Fortress Carpenters\n" - "setting and/or Gerudo Fortress Keys setting is incompatible with\n" - "having a Gerudo Fortress Keyring."); - } else { - mOptions[RSK_KEYRINGS_GERUDO_FORTRESS].Enable(); - } + HandleKeyringUI(); }); OPT_U8(RSK_BOSS_KEYSANITY, "Boss Key Shuffle", {"Start With", "Vanilla", "Own Dungeon", "Any Dungeon", "Overworld", "Anywhere"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("BossKeysanity"), mOptionDescriptions[RSK_BOSS_KEYSANITY], WIDGET_CVAR_COMBOBOX, RO_DUNGEON_ITEM_LOC_OWN_DUNGEON); OPT_U8(RSK_GANONS_BOSS_KEY, "Ganon's Boss Key", {"Vanilla", "Own Dungeon", "Start With", "Any Dungeon", "Overworld", "Anywhere", "Trigger-Stones", "Trigger-Medallions", "Trigger-Rewards", "Trigger-Dungeons", "Trigger-Tokens", "Trigger-Triforce Pieces"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleGanonBossKey"), mOptionDescriptions[RSK_GANONS_BOSS_KEY], WIDGET_CVAR_COMBOBOX, RO_GANON_BOSS_KEY_VANILLA); @@ -1290,7 +1283,7 @@ void Settings::CreateOptions() { mOptions[RSK_WINCON_DUNGEON_COUNT].ChangeOptions(NumOpts(0, 8)); } }); - OPT_U8(RSK_KEYRINGS, "Key Rings", {"Off", "Random", "Count", "Selection"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRings"), mOptionDescriptions[RSK_KEYRINGS], WIDGET_CVAR_COMBOBOX, RO_KEYRINGS_OFF); + OPT_U8(RSK_KEYRINGS, "Keyrings", {"Off", "Random", "Count", "Selection"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRings"), mOptionDescriptions[RSK_KEYRINGS], WIDGET_CVAR_COMBOBOX, RO_KEYRINGS_OFF); OPT_CALLBACK(RSK_KEYRINGS, { switch (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleKeyRings"), RO_KEYRINGS_OFF)) { case RO_KEYRINGS_COUNT: @@ -1305,6 +1298,7 @@ void Settings::CreateOptions() { mOptions[RSK_KEYRINGS_BOTTOM_OF_THE_WELL].Hide(); mOptions[RSK_KEYRINGS_GTG].Hide(); mOptions[RSK_KEYRINGS_GANONS_CASTLE].Hide(); + mOptions[RSK_KEYRINGS_CHEST_GAME].Hide(); break; case RO_KEYRINGS_SELECTION: // Show checkboxes for each dungeon with keys. @@ -1318,6 +1312,7 @@ void Settings::CreateOptions() { mOptions[RSK_KEYRINGS_BOTTOM_OF_THE_WELL].Unhide(); mOptions[RSK_KEYRINGS_GTG].Unhide(); mOptions[RSK_KEYRINGS_GANONS_CASTLE].Unhide(); + mOptions[RSK_KEYRINGS_CHEST_GAME].Unhide(); break; default: mOptions[RSK_KEYRINGS_RANDOM_COUNT].Hide(); @@ -1330,10 +1325,11 @@ void Settings::CreateOptions() { mOptions[RSK_KEYRINGS_BOTTOM_OF_THE_WELL].Hide(); mOptions[RSK_KEYRINGS_GTG].Hide(); mOptions[RSK_KEYRINGS_GANONS_CASTLE].Hide(); + mOptions[RSK_KEYRINGS_CHEST_GAME].Hide(); break; } }); - OPT_U8(RSK_KEYRINGS_RANDOM_COUNT, "Keyring Dungeon Count", {NumOpts(0, 9)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsRandomCount"), "", WIDGET_CVAR_SLIDER_INT, 8); + OPT_U8(RSK_KEYRINGS_RANDOM_COUNT, "Keyring Dungeon Count", {NumOpts(0, 10)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsRandomCount"), "", WIDGET_CVAR_SLIDER_INT, 8); OPT_U8(RSK_KEYRINGS_GERUDO_FORTRESS, "Gerudo Fortress Keyring", {"No", "Random", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsGerudoFortress"), "", WIDGET_CVAR_COMBOBOX, 0); OPT_U8(RSK_KEYRINGS_FOREST_TEMPLE, "Forest Temple Keyring", {"No", "Random", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsForestTemple"), "", WIDGET_CVAR_COMBOBOX, 0); OPT_U8(RSK_KEYRINGS_FIRE_TEMPLE, "Fire Temple Keyring", {"No", "Random", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsFireTemple"), "", WIDGET_CVAR_COMBOBOX, 0); @@ -1343,6 +1339,7 @@ void Settings::CreateOptions() { OPT_U8(RSK_KEYRINGS_BOTTOM_OF_THE_WELL, "Bottom of the Well Keyring", {"No", "Random", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsBottomOfTheWell"), "", WIDGET_CVAR_COMBOBOX, 0); OPT_U8(RSK_KEYRINGS_GTG, "Gerudo Training Ground Keyring", {"No", "Random", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsGTG"), "", WIDGET_CVAR_COMBOBOX, 0); OPT_U8(RSK_KEYRINGS_GANONS_CASTLE, "Ganon's Castle Keyring", {"No", "Random", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsGanonsCastle"), "", WIDGET_CVAR_COMBOBOX, 0); + OPT_U8(RSK_KEYRINGS_CHEST_GAME, "Chest Minigame Keyring", {"No", "Random", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsChestGame"), "", WIDGET_CVAR_COMBOBOX, 0); //Dummied out due to redundancy with TimeSavers.SkipChildStealth until such a time that logic needs to consider child stealth e.g. because it's freestanding checks are added to freestanding shuffle. //To undo this dummying, readd this setting to an OptionGroup so it appears in the UI, then edit the timesaver check hooks to look at this, and the timesaver setting to lock itself as needed. OPT_BOOL(RSK_SKIP_CHILD_STEALTH, "Skip Child Stealth", {"Don't Skip", "Skip"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("SkipChildStealth"), mOptionDescriptions[RSK_SKIP_CHILD_STEALTH], WIDGET_CVAR_CHECKBOX, RO_GENERIC_DONT_SKIP); @@ -1998,14 +1995,15 @@ void Settings::CreateOptions() { WidgetContainerType::SECTION); mOptionGroups[RSG_MENU_COLUMN_MQ] = OptionGroup::SubGroup("", { &mOptionGroups[RSG_MENU_SECTION_MQ] }, WidgetContainerType::COLUMN); - mOptionGroups[RSG_MENU_SECTION_KEYRINGS] = OptionGroup::SubGroup( - "Keyrings", - { &mOptions[RSK_KEYRINGS], &mOptions[RSK_KEYRINGS_RANDOM_COUNT], &mOptions[RSK_KEYRINGS_FOREST_TEMPLE], - &mOptions[RSK_KEYRINGS_FIRE_TEMPLE], &mOptions[RSK_KEYRINGS_WATER_TEMPLE], - &mOptions[RSK_KEYRINGS_SPIRIT_TEMPLE], &mOptions[RSK_KEYRINGS_SHADOW_TEMPLE], - &mOptions[RSK_KEYRINGS_BOTTOM_OF_THE_WELL], &mOptions[RSK_KEYRINGS_GTG], - &mOptions[RSK_KEYRINGS_GANONS_CASTLE], &mOptions[RSK_KEYRINGS_GERUDO_FORTRESS] }, - WidgetContainerType::SECTION); + mOptionGroups[RSG_MENU_SECTION_KEYRINGS] = + OptionGroup::SubGroup("Keyrings", + { &mOptions[RSK_KEYRINGS], &mOptions[RSK_KEYRINGS_RANDOM_COUNT], + &mOptions[RSK_KEYRINGS_FOREST_TEMPLE], &mOptions[RSK_KEYRINGS_FIRE_TEMPLE], + &mOptions[RSK_KEYRINGS_WATER_TEMPLE], &mOptions[RSK_KEYRINGS_SPIRIT_TEMPLE], + &mOptions[RSK_KEYRINGS_SHADOW_TEMPLE], &mOptions[RSK_KEYRINGS_BOTTOM_OF_THE_WELL], + &mOptions[RSK_KEYRINGS_GTG], &mOptions[RSK_KEYRINGS_GANONS_CASTLE], + &mOptions[RSK_KEYRINGS_GERUDO_FORTRESS], &mOptions[RSK_KEYRINGS_CHEST_GAME] }, + WidgetContainerType::SECTION); mOptionGroups[RSG_MENU_COLUMN_KEYRINGS] = OptionGroup::SubGroup("", { &mOptionGroups[RSG_MENU_SECTION_KEYRINGS] }, WidgetContainerType::COLUMN); mOptionGroups[RSG_MENU_SIDEBAR_DUNGEONS] = OptionGroup::SubGroup("Dungeons", @@ -2046,6 +2044,7 @@ void Settings::CreateOptions() { &mOptions[RSK_SHUFFLE_SIGNS], &mOptions[RSK_SHUFFLE_FROG_SONG_RUPEES], &mOptions[RSK_SHUFFLE_ADULT_TRADE], + &mOptions[RSK_SHUFFLE_CHEST_MINIGAME], &mOptions[RSK_SHUFFLE_100_GS_REWARD], &mOptions[RSK_SHUFFLE_FOUNTAIN_FAIRIES], &mOptions[RSK_SHUFFLE_STONE_FAIRIES], @@ -2359,6 +2358,7 @@ void Settings::CreateOptions() { &mOptions[RSK_KEYRINGS_BOTTOM_OF_THE_WELL], &mOptions[RSK_KEYRINGS_GTG], &mOptions[RSK_KEYRINGS_GANONS_CASTLE], + &mOptions[RSK_KEYRINGS_CHEST_GAME], }); mOptionGroups[RSG_STARTING_ITEMS] = OptionGroup::SubGroup("Items", { &mOptions[RSK_STARTING_OCARINA], &mOptions[RSK_STARTING_KOKIRI_SWORD], @@ -2729,10 +2729,6 @@ void Context::FinalizeSettings(const std::set& excludedLocation mOptions[RSK_STARTING_BOTTLE_4].Set(RO_STARTING_BOTTLE_OFF); } - // RANDOTODO implement chest shuffle with keysanity - // ShuffleChestMinigame.Set(cvarSettings[RSK_SHUFFLE_CHEST_MINIGAME]); - mOptions[RSK_SHUFFLE_CHEST_MINIGAME].Set(RO_CHEST_GAME_OFF); - // TODO: RandomizeAllSettings(true) when implementing the ability to randomize the options themselves. std::array dungeons = this->GetDungeons()->GetDungeonList(); @@ -2853,7 +2849,7 @@ void Context::FinalizeSettings(const std::set& excludedLocation } } - // Set key ring for each dungeon + // Set keyring for each dungeon for (const auto dungeon : dungeons) { dungeon->ClearKeyRing(); } @@ -2866,7 +2862,7 @@ void Context::FinalizeSettings(const std::set& excludedLocation }; if (mOptions[RSK_KEYRINGS]) { - // Random Key Rings + // Random Keyrings auto keyrings = keyRingOptions; if (mOptions[RSK_GERUDO_FORTRESS].Is(RO_GF_CARPENTERS_NORMAL) && mOptions[RSK_GERUDO_KEYS].IsNot(RO_GERUDO_KEYS_VANILLA)) { @@ -2874,6 +2870,11 @@ void Context::FinalizeSettings(const std::set& excludedLocation } else { mOptions[RSK_KEYRINGS_GERUDO_FORTRESS].Set(RO_KEYRING_FOR_DUNGEON_OFF); } + if (mOptions[RSK_SHUFFLE_CHEST_MINIGAME]) { + keyrings.push_back(&mOptions[RSK_KEYRINGS_CHEST_GAME]); + } else { + mOptions[RSK_KEYRINGS_CHEST_GAME].Set(RO_KEYRING_FOR_DUNGEON_OFF); + } if (mOptions[RSK_KEYRINGS].Is(RO_KEYRINGS_RANDOM) || mOptions[RSK_KEYRINGS].Is(RO_KEYRINGS_COUNT)) { const uint32_t keyRingCount = mOptions[RSK_KEYRINGS].Is(RO_KEYRINGS_COUNT) ? mOptions[RSK_KEYRINGS_RANDOM_COUNT].Get() @@ -2918,6 +2919,12 @@ void Context::FinalizeSettings(const std::set& excludedLocation (mOptions[RSK_KEYRINGS_GANONS_CASTLE].Is(RO_KEYRING_FOR_DUNGEON_RANDOM) && Random(0, 2) == 0)) { this->GetDungeon(GANONS_CASTLE)->SetKeyRing(); } + // Gerudo Fortress & the chest minigame have no DungeonInfo, so resolve their random rolls into the option + for (const auto keyring : { &mOptions[RSK_KEYRINGS_GERUDO_FORTRESS], &mOptions[RSK_KEYRINGS_CHEST_GAME] }) { + if (keyring->Is(RO_KEYRING_FOR_DUNGEON_RANDOM)) { + keyring->Set(Random(0, 2) == 0 ? RO_KEYRING_FOR_DUNGEON_ON : RO_KEYRING_FOR_DUNGEON_OFF); + } + } } auto trials = this->GetTrials()->GetTrialList(); diff --git a/soh/soh/Enhancements/randomizer/settings.h b/soh/soh/Enhancements/randomizer/settings.h index 6b63a0a390..fcd274c58a 100644 --- a/soh/soh/Enhancements/randomizer/settings.h +++ b/soh/soh/Enhancements/randomizer/settings.h @@ -22,6 +22,11 @@ class Settings { */ void HandleMixedEntrancePoolsUI(); + /** + * @brief Hides or Unhides the UI of the keyring options, and updates the max keyring count. + */ + void HandleKeyringUI(); + /** * @brief UI Callback for handling UI state of Starting Age shuffle. */ 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 9b341f462a..19c0f50e95 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 @@ -135,7 +135,8 @@ void EnBox_Init(Actor* thisx, PlayState* play2) { if (play) {} // helps the compiler store play2 into s1 - if (Flags_GetTreasure(play, this->dyna.actor.params & 0x1F)) { + if (GameInteractor_Should(VB_CHEST_CONSIDER_CHEST_OPEN, Flags_GetTreasure(play, this->dyna.actor.params & 0x1F), + this)) { this->alpha = 255; this->iceSmokeTimer = 100; EnBox_SetupAction(this, EnBox_Open); @@ -441,7 +442,9 @@ void EnBox_WaitOpen(EnBox* this, PlayState* play) { } } osSyncPrintf("Actor_Environment_Tbox_On() %d\n", this->dyna.actor.params & 0x1F); - Flags_SetTreasure(play, this->dyna.actor.params & 0x1F); + if (GameInteractor_Should(VB_CHEST_SET_TREASURE_FLAG, true, this)) { + Flags_SetTreasure(play, this->dyna.actor.params & 0x1F); + } } else { player = GET_PLAYER(play); Actor_WorldToActorCoords(&this->dyna.actor, &sp4C, &player->actor.world.pos); @@ -449,7 +452,8 @@ void EnBox_WaitOpen(EnBox* this, PlayState* play) { Player_IsFacingActor(&this->dyna.actor, 0x3000, play)) { Actor_OfferGetItemNearby(&this->dyna.actor, play, -(this->dyna.actor.params >> 5 & 0x7F)); } - if (Flags_GetTreasure(play, this->dyna.actor.params & 0x1F)) { + if (GameInteractor_Should(VB_CHEST_CONSIDER_CHEST_OPEN, Flags_GetTreasure(play, this->dyna.actor.params & 0x1F), + this)) { EnBox_SetupAction(this, EnBox_Open); } } @@ -576,9 +580,10 @@ void EnBox_UpdateTexture(EnBox* this, PlayState* play) { GetItemCategory getItemCategory; GetItemEntry chestItem = this->getItemEntry; + // Exclude treasure game chests except for the final room, unless the chest game is shuffled int isVanilla = !csmc || (requiresStoneAgony && !CHECK_QUEST_ITEM(QUEST_STONE_OF_AGONY)) || - (play->sceneNum == SCENE_TREASURE_BOX_SHOP && - this->dyna.actor.room != 6); // Exclude treasure game chests except for the final room + (play->sceneNum == SCENE_TREASURE_BOX_SHOP && this->dyna.actor.room != 6 && + !Randomizer_GetSettingValue(RSK_SHUFFLE_CHEST_MINIGAME)); if (!isVanilla) { getItemCategory = Randomizer_AdjustItemCategory(chestItem); diff --git a/soh/src/overlays/actors/ovl_En_Changer/z_en_changer.c b/soh/src/overlays/actors/ovl_En_Changer/z_en_changer.c index 5bbe60c0ac..617c24dd19 100644 --- a/soh/src/overlays/actors/ovl_En_Changer/z_en_changer.c +++ b/soh/src/overlays/actors/ovl_En_Changer/z_en_changer.c @@ -8,6 +8,7 @@ #include "vt.h" #include "overlays/actors/ovl_Item_Etcetera/z_item_etcetera.h" #include "overlays/actors/ovl_En_Ex_Item/z_en_ex_item.h" +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #define FLAGS 0 @@ -136,7 +137,7 @@ void EnChanger_Init(Actor* thisx, PlayState* play2) { this->rightChestGetItemId = GI_DOOR_KEY; rightChestItem = ITEM_ETC_KEY_SMALL_CHEST_GAME; - if (Rand_ZeroFloat(1.99f) < 1.0f) { + if (GameInteractor_Should(VB_EN_CHANGER_SWAP_CHESTS, Rand_ZeroFloat(1.99f) < 1.0f, this)) { rightChestParams = (sLoserGetItemIds[play->roomCtx.curRoom.num] << 5) | 0x4000; this->rightChestNum = new_var; this->rightChestGetItemId = sLoserGetItemIds[play->roomCtx.curRoom.num]; diff --git a/soh/src/overlays/actors/ovl_En_Takara_Man/z_en_takara_man.c b/soh/src/overlays/actors/ovl_En_Takara_Man/z_en_takara_man.c index 7121096387..7500a869df 100644 --- a/soh/src/overlays/actors/ovl_En_Takara_Man/z_en_takara_man.c +++ b/soh/src/overlays/actors/ovl_En_Takara_Man/z_en_takara_man.c @@ -10,6 +10,7 @@ #include "soh/OTRGlobals.h" #include "soh/ResourceManagerHelpers.h" #include "soh/Enhancements/savestate_serialize.h" +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #define FLAGS \ (ACTOR_FLAG_ATTENTION_ENABLED | ACTOR_FLAG_FRIENDLY | ACTOR_FLAG_UPDATE_CULLING_DISABLED | \ @@ -64,8 +65,10 @@ void EnTakaraMan_Init(Actor* thisx, PlayState* play) { osSyncPrintf("\n\n"); // "Bun! %x" (needs a better translation) osSyncPrintf(VT_FGCOL(PURPLE) "☆☆☆☆☆ ばぅん! ☆☆☆☆☆ %x\n" VT_RST, play->actorCtx.flags.chest); - play->actorCtx.flags.chest = 0; - gSaveContext.inventory.dungeonKeys[gSaveContext.mapIndex] = -1; + if (GameInteractor_Should(VB_TAKARA_MAN_RESET_CHESTS_AND_KEYS, true, this)) { + play->actorCtx.flags.chest = 0; + gSaveContext.inventory.dungeonKeys[gSaveContext.mapIndex] = -1; + } SkelAnime_InitFlex(play, &this->skelAnime, &object_ts_Skel_004FE0, &object_ts_Anim_000498, this->jointTable, this->morphTable, 10); thisx->focus.pos = thisx->world.pos; @@ -156,7 +159,9 @@ void func_80B17934(EnTakaraMan* this, PlayState* play) { Rupees_ChangeBy(-10); this->unk_214 = 1; this->actor.parent = NULL; - Actor_OfferGetItem(&this->actor, play, GI_DOOR_KEY, 2000.0f, 1000.0f); + if (GameInteractor_Should(VB_TAKARA_MAN_OFFER_GET_ITEM, true, this)) { + Actor_OfferGetItem(&this->actor, play, GI_DOOR_KEY, 2000.0f, 1000.0f); + } this->actionFunc = func_80B17A6C; } else { Message_CloseTextbox(play); @@ -180,7 +185,7 @@ void func_80B17934(EnTakaraMan* this, PlayState* play) { void func_80B17A6C(EnTakaraMan* this, PlayState* play) { if (Actor_HasParent(&this->actor, play)) { this->actionFunc = func_80B17AC4; - } else { + } else if (GameInteractor_Should(VB_TAKARA_MAN_OFFER_GET_ITEM, true, this)) { Actor_OfferGetItem(&this->actor, play, GI_DOOR_KEY, 2000.0f, 1000.0f); } }