From 44d351698b8a92ff02b5d9be36a18ff0fb60648a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philip=20Dub=C3=A9?= Date: Sat, 17 Jan 2026 05:00:03 +0000 Subject: [PATCH] Shuffle Masks (#5536) Future improvements can build on this, giving masks abilities, or adding checks for trading mask to someone --- .../cosmetics/FileSelectMoreInfo.cpp | 7 +- .../3drando/hint_list/hint_list_item.cpp | 9 ++ .../Enhancements/randomizer/3drando/hints.cpp | 2 +- .../randomizer/3drando/item_pool.cpp | 11 +++ .../Enhancements/randomizer/3drando/shops.cpp | 50 ++++++++++ .../Enhancements/randomizer/hook_handlers.cpp | 8 +- soh/soh/Enhancements/randomizer/item_list.cpp | 11 ++- .../dungeons/spirit_temple.cpp | 2 +- .../location_access/overworld/lost_woods.cpp | 4 +- .../location_access/overworld/market.cpp | 8 +- soh/soh/Enhancements/randomizer/logic.cpp | 46 ++++++++- .../randomizer/option_descriptions.cpp | 9 +- .../Enhancements/randomizer/randomizer.cpp | 98 +++++++++---------- .../Enhancements/randomizer/randomizerTypes.h | 25 ++++- soh/soh/Enhancements/randomizer/savefile.cpp | 2 +- soh/soh/Enhancements/randomizer/settings.cpp | 5 +- soh/src/code/z_parameter.c | 2 +- .../misc/ovl_kaleido_scope/z_kaleido_item.c | 6 +- 18 files changed, 225 insertions(+), 80 deletions(-) diff --git a/soh/soh/Enhancements/cosmetics/FileSelectMoreInfo.cpp b/soh/soh/Enhancements/cosmetics/FileSelectMoreInfo.cpp index 3051b565a..048a27cf9 100644 --- a/soh/soh/Enhancements/cosmetics/FileSelectMoreInfo.cpp +++ b/soh/soh/Enhancements/cosmetics/FileSelectMoreInfo.cpp @@ -385,12 +385,7 @@ static bool ShouldRenderItem(s16 fileIndex, u8 item) { return false; } - if (item == ITEM_MASK_KEATON && (HasItem(fileIndex, ITEM_WEIRD_EGG) || HasItem(fileIndex, ITEM_CHICKEN) || - HasItem(fileIndex, ITEM_LETTER_ZELDA) || HasItem(fileIndex, ITEM_MASK_SKULL) || - HasItem(fileIndex, ITEM_MASK_SPOOKY) || HasItem(fileIndex, ITEM_MASK_BUNNY) || - HasItem(fileIndex, ITEM_MASK_GORON) || HasItem(fileIndex, ITEM_MASK_ZORA) || - HasItem(fileIndex, ITEM_MASK_GERUDO) || HasItem(fileIndex, ITEM_MASK_TRUTH) || - HasItem(fileIndex, ITEM_SOLD_OUT))) { + if (item == ITEM_MASK_KEATON && !HasItem(fileIndex, ITEM_MASK_KEATON)) { return false; } 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 b77b5b26e..01e015ed3 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 @@ -2081,6 +2081,15 @@ void StaticData::HintTable_Init_Item() { CustomMessage("a rightward tone", /*german*/"ein rechtsseitiger Ton", /*french*/"une tonalité vers la droite")}); // /*spanish*/un tono hacia la derecha + hintTextTable[RHT_MASK_KEATON] = HintText(CustomMessage("a keaton mask", /*german*/"!!!", /*french*/"le Masque du Renard"), {CustomMessage("a mask", /*german*/"!!!", /*french*/"un masque")}); + hintTextTable[RHT_MASK_SKULL] = HintText(CustomMessage("a skull mask", /*german*/"!!!", /*french*/"le Masque de Mort"), {CustomMessage("a mask", /*german*/"!!!", /*french*/"un masque")}); + hintTextTable[RHT_MASK_SPOOKY] = HintText(CustomMessage("a spooky mask", /*german*/"!!!", /*french*/"le Masque d'Effroi"), {CustomMessage("a mask", /*german*/"!!!", /*french*/"un masque")}); + hintTextTable[RHT_MASK_BUNNY] = HintText(CustomMessage("a bunny hood", /*german*/"!!!", /*french*/"le Masque du Lapin"), {CustomMessage("a mask", /*german*/"!!!", /*french*/"un masque")}); + hintTextTable[RHT_MASK_GORON] = HintText(CustomMessage("a goron mask", /*german*/"!!!", /*french*/"le Masque de Goron"), {CustomMessage("a mask", /*german*/"!!!", /*french*/"un masque")}); + hintTextTable[RHT_MASK_ZORA] = HintText(CustomMessage("a zora mask", /*german*/"!!!", /*french*/"le Masque de Zora"), {CustomMessage("a mask", /*german*/"!!!", /*french*/"un masque")}); + hintTextTable[RHT_MASK_GERUDO] = HintText(CustomMessage("a gerudo mask", /*german*/"!!!", /*french*/"le Masque de Gerudo"), {CustomMessage("a mask", /*german*/"!!!", /*french*/"un masque")}); + hintTextTable[RHT_MASK_TRUTH] = HintText(CustomMessage("a mask of truth", /*german*/"!!!", /*french*/"le Masque de Vérité"), {CustomMessage("a mask", /*german*/"!!!", /*french*/"un masque")}); + hintTextTable[RHT_FISHING_POLE] = HintText(CustomMessage("a fishing pole", /*german*/"eine Angelrute", /*french*/"une canne à pêche"), // /*spanish*/caña de pescar { diff --git a/soh/soh/Enhancements/randomizer/3drando/hints.cpp b/soh/soh/Enhancements/randomizer/3drando/hints.cpp index 33c8335fc..16de9e0b8 100644 --- a/soh/soh/Enhancements/randomizer/3drando/hints.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/hints.cpp @@ -279,7 +279,7 @@ std::vector>> conditionalAlways std::make_pair(RC_DEKU_THEATER_MASK_OF_TRUTH, []() { auto ctx = Rando::Context::GetInstance(); - return !ctx->GetOption(RSK_MASK_SHOP_HINT) && !ctx->GetOption(RSK_COMPLETE_MASK_QUEST); + return !ctx->GetOption(RSK_MASK_SHOP_HINT) && !ctx->GetOption(RSK_MASK_QUEST); }), std::make_pair(RC_SONG_FROM_OCARINA_OF_TIME, []() { diff --git a/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp b/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp index 2baa64e3c..548a85a6b 100644 --- a/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp @@ -385,6 +385,17 @@ void GenerateItemPool() { AddFixedItemToPool(RG_SKELETON_KEY, 1); } + if (ctx->GetOption(RSK_MASK_QUEST).Is(RO_MASK_QUEST_SHUFFLE)) { + AddItemToPool(RG_KEATON_MASK, 2, 1, 1, 1); + AddItemToPool(RG_SKULL_MASK, 2, 1, 1, 1); + AddItemToPool(RG_SPOOKY_MASK, 2, 1, 1, 1); + AddItemToPool(RG_BUNNY_HOOD, 2, 1, 1, 1); + AddItemToPool(RG_GORON_MASK, 2, 1, 1, 1); + AddItemToPool(RG_ZORA_MASK, 2, 1, 1, 1); + AddItemToPool(RG_GERUDO_MASK, 2, 1, 1, 1); + AddItemToPool(RG_MASK_OF_TRUTH, 2, 1, 1, 1); + } + if (ctx->GetOption(RSK_ROCS_FEATHER)) { AddItemToPool(RG_ROCS_FEATHER, 2, 1, 1, 1); } diff --git a/soh/soh/Enhancements/randomizer/3drando/shops.cpp b/soh/soh/Enhancements/randomizer/3drando/shops.cpp index 1f34968db..834aea3da 100644 --- a/soh/soh/Enhancements/randomizer/3drando/shops.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/shops.cpp @@ -1206,6 +1206,56 @@ void InitTrickNames() { Text{ "Ganondorf's Key", "Ganondorf's Key", "Ganondorf's Key" }, }; + trickNameTable[RG_KEATON_MASK] = { + // TODO_TRANSLATE + Text{ "Korok Mask" }, + Text{ "Lynel Mask" }, + Text{ "Cucco Mask" }, + Text{ "Remlit Mask" }, + }; + trickNameTable[RG_SKULL_MASK] = { + // TODO_TRANSLATE + Text{ "Darknut Mask" }, + Text{ "Stalfos Mask" }, + Text{ "Captain's Hat" }, + }; + trickNameTable[RG_SPOOKY_MASK] = { + // TODO_TRANSLATE + Text{ "Gibdo Mask" }, + Text{ "Garo's Mask" }, + Text{ "Redead mask" }, + }; + trickNameTable[RG_BUNNY_HOOD] = { + // TODO_TRANSLATE + Text{ "Bunny Mask" }, + Text{ "Bremen Mask" }, + Text{ "Rabbit Hood" }, + }; + trickNameTable[RG_MASK_OF_TRUTH] = { + // TODO_TRANSLATE + Text{ "Feirce Diety Mask" }, + Text{ "Majora's Mask" }, + Text{ "Hero's Charm" }, + }; + trickNameTable[RG_GORON_MASK] = { + // TODO_TRANSLATE + Text{ "Stone Mask" }, + Text{ "Darmani's Mask" }, + Text{ "Goron Garb" }, + }; + trickNameTable[RG_ZORA_MASK] = { + // TODO_TRANSLATE + Text{ "Zora Costume" }, + Text{ "Don Gero's Mask" }, + Text{ "Mikau's Mask" }, + }; + trickNameTable[RG_GERUDO_MASK] = { + // TODO_TRANSLATE + Text{ "Great Fairy Mask" }, + Text{ "Romani's Mask" }, + Text{ "Gerudo Veil" }, + }; + trickNameTable[RG_GUARD_HOUSE_KEY] = { // TODO_TRANSLATE Text{ "Pot Room Key", "Pot Room Key", "Pot Room Key" }, diff --git a/soh/soh/Enhancements/randomizer/hook_handlers.cpp b/soh/soh/Enhancements/randomizer/hook_handlers.cpp index 902eb71dc..6be1b8cf4 100644 --- a/soh/soh/Enhancements/randomizer/hook_handlers.cpp +++ b/soh/soh/Enhancements/randomizer/hook_handlers.cpp @@ -25,6 +25,7 @@ extern "C" { #include "src/overlays/actors/ovl_Bg_Treemouth/z_bg_treemouth.h" #include "src/overlays/actors/ovl_Bg_Jya_Bigmirror/z_bg_jya_bigmirror.h" #include "src/overlays/actors/ovl_En_Si/z_en_si.h" +#include "src/overlays/actors/ovl_En_Ossan/z_en_ossan.h" #include "src/overlays/actors/ovl_En_Shopnuts/z_en_shopnuts.h" #include "src/overlays/actors/ovl_En_Dns/z_en_dns.h" #include "src/overlays/actors/ovl_En_Gb/z_en_gb.h" @@ -369,7 +370,7 @@ void RandomizerOnPlayerUpdateForRCQueueHandler() { iceTrapScale = 0.0f; randomizerQueuedCheck = rc; randomizerQueuedItemEntry = getItemEntry; - SPDLOG_INFO("Queueing Item mod {} item {} from RC {}", getItemEntry.modIndex, getItemEntry.itemId, + SPDLOG_INFO("Queuing Item mod {} item {} from RC {}", getItemEntry.modIndex, getItemEntry.itemId, static_cast(rc)); if ( // Skipping ItemGet animation incompatible with checks that require closing a text box to finish @@ -2195,6 +2196,11 @@ void RandomizerOnActorInitHandler(void* actorRef) { } } + if (actor->id == ACTOR_EN_OSSAN && actor->params == OSSAN_TYPE_MASK && + RAND_GET_OPTION(RSK_MASK_QUEST) == RO_MASK_QUEST_SHUFFLE) { + Actor_Kill(actor); + } + if (actor->id == ACTOR_BG_TREEMOUTH && LINK_IS_ADULT && RAND_GET_OPTION(RSK_SHUFFLE_DUNGEON_ENTRANCES) != RO_DUNGEON_ENTRANCE_SHUFFLE_OFF && (RAND_GET_OPTION(RSK_FOREST) == RO_CLOSED_FOREST_OFF || diff --git a/soh/soh/Enhancements/randomizer/item_list.cpp b/soh/soh/Enhancements/randomizer/item_list.cpp index b433d4479..22fa37dfd 100644 --- a/soh/soh/Enhancements/randomizer/item_list.cpp +++ b/soh/soh/Enhancements/randomizer/item_list.cpp @@ -337,7 +337,16 @@ void Rando::StaticData::InitItemTable() { itemTable[RG_OCARINA_C_RIGHT_BUTTON] = Item(RG_OCARINA_C_RIGHT_BUTTON, Text{ "Ocarina C Right Button", "Touche C-Droit de l'Ocarina", "Taste C-Rechts der Okarina" }, ITEMTYPE_ITEM, GI_MAP, true, LOGIC_OCARINA_C_RIGHT_BUTTON, RHT_OCARINA_C_RIGHT_BUTTON, RG_OCARINA_C_RIGHT_BUTTON, OBJECT_GI_MAP, GID_STONE_OF_AGONY, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER, {"the ", "den ", "le "}); itemTable[RG_OCARINA_C_RIGHT_BUTTON].SetCustomDrawFunc(Randomizer_DrawOcarinaButton); - itemTable[RG_BRONZE_SCALE] = Item(RG_BRONZE_SCALE, Text{ "Bronze Scale", "Écaille de Bronze", "Bronzene Schuppe" }, ITEMTYPE_ITEM, GI_SCALE_SILVER, true, LOGIC_PROGRESSIVE_SCALE, 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, {"the ", "die ", "le "}); + itemTable[RG_KEATON_MASK] = Item(RG_KEATON_MASK, Text{ "Keaton Mask", "Masque du Renard", TODO_TRANSLATE }, ITEMTYPE_ITEM, RG_KEATON_MASK, true, LOGIC_NONE, RHT_MASK_KEATON, RG_KEATON_MASK, OBJECT_GI_KI_TAN_MASK, GID_MASK_KEATON, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER); + itemTable[RG_SKULL_MASK] = Item(RG_SKULL_MASK, Text{ "Skull Mask", "Masque de Mort", TODO_TRANSLATE }, ITEMTYPE_ITEM, RG_SKULL_MASK, true, LOGIC_NONE, RHT_MASK_SKULL, RG_SKULL_MASK, OBJECT_GI_SKJ_MASK, GID_MASK_SKULL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER); + itemTable[RG_SPOOKY_MASK] = Item(RG_SPOOKY_MASK, Text{ "Spooky Mask", "Masque d'Effroi", TODO_TRANSLATE }, ITEMTYPE_ITEM, RG_SPOOKY_MASK, true, LOGIC_NONE, RHT_MASK_SPOOKY, RG_SPOOKY_MASK, OBJECT_GI_REDEAD_MASK, GID_MASK_SPOOKY, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER); + itemTable[RG_BUNNY_HOOD] = Item(RG_BUNNY_HOOD, Text{ "Bunny Hood", "Masque du Lapin", TODO_TRANSLATE }, ITEMTYPE_ITEM, RG_BUNNY_HOOD, true, LOGIC_NONE, RHT_MASK_BUNNY, RG_BUNNY_HOOD, OBJECT_GI_RABIT_MASK, GID_MASK_BUNNY, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER); + itemTable[RG_GORON_MASK] = Item(RG_GORON_MASK, Text{ "Goron Mask", "Masque de Goron", TODO_TRANSLATE }, ITEMTYPE_ITEM, RG_GORON_MASK, true, LOGIC_NONE, RHT_MASK_GORON, RG_GORON_MASK, OBJECT_GI_GOLONMASK, GID_MASK_GORON, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER); + itemTable[RG_ZORA_MASK] = Item(RG_ZORA_MASK, Text{ "Zora Mask", "Masque de Zora", TODO_TRANSLATE }, ITEMTYPE_ITEM, RG_ZORA_MASK, true, LOGIC_NONE, RHT_MASK_ZORA, RG_ZORA_MASK, OBJECT_GI_ZORAMASK, GID_MASK_ZORA, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER); + itemTable[RG_GERUDO_MASK] = Item(RG_GERUDO_MASK, Text{ "Gerudo Mask", "Masque de Gerudo", TODO_TRANSLATE }, ITEMTYPE_ITEM, RG_GERUDO_MASK, true, LOGIC_NONE, RHT_MASK_GERUDO, RG_GERUDO_MASK, OBJECT_GI_GERUDOMASK, GID_MASK_GERUDO, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER); + itemTable[RG_MASK_OF_TRUTH] = Item(RG_MASK_OF_TRUTH, Text{ "Mask of Truth", "Masque de Vérité", TODO_TRANSLATE }, ITEMTYPE_ITEM, RG_MASK_OF_TRUTH, true, LOGIC_NONE, RHT_MASK_TRUTH, RG_MASK_OF_TRUTH, OBJECT_GI_TRUTH_MASK, GID_MASK_TRUTH, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER); + + itemTable[RG_BRONZE_SCALE] = Item(RG_BRONZE_SCALE, Text{ "Bronze Scale", "Écaille de Bronze", "Bronzene Schuppe" }, ITEMTYPE_ITEM, GI_SCALE_SILVER, true, LOGIC_PROGRESSIVE_SCALE, 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, {"the ", "die ", "le "}); itemTable[RG_BRONZE_SCALE].SetCustomDrawFunc(Randomizer_DrawBronzeScale); itemTable[RG_CRAWL] = Item(RG_CRAWL, Text{ "Crawl", "Ramper", "Kriechen" }, ITEMTYPE_ITEM, GI_SHIELD_DEKU, true, LOGIC_NONE, RHT_CRAWL, RG_CRAWL, OBJECT_GI_SHIELD_1, GID_SHIELD_DEKU, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER); diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp index cb3a97cfe..f3e842d1a 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp @@ -1038,7 +1038,7 @@ void RegionTable_Init_SpiritTemple() { //Exits Entrance(RR_SPIRIT_TEMPLE_MQ_BEAMOS_PITS, []{return true;}), //technically we only need to avoid them, but the sheer height and the moving walls makes getting to the top after only stunning them very difficult/impossible - Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_WALL_UPPER, []{return /*(*/logic->CanKillEnemy(RE_KEESE)/*|| CanUse(RG_SKULL_MASK)) && CanClimbHigh()*/;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_WALL_UPPER, []{return /*(*/logic->CanKillEnemy(RE_KEESE) || logic->CanUse(RG_SKULL_MASK)/*) && CanClimbHigh()*/;}), }); areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_WALL_UPPER] = Region("Spirit Temple MQ Big Wall Upper", SCENE_SPIRIT_TEMPLE, { diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/lost_woods.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/lost_woods.cpp index 186a3895e..cfeeff380 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/lost_woods.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/lost_woods.cpp @@ -110,8 +110,8 @@ void RegionTable_Init_LostWoods() { areaTable[RR_DEKU_THEATER] = Region("Deku Theater", SCENE_GROTTOS, {}, { //Locations - LOCATION(RC_DEKU_THEATER_SKULL_MASK, logic->IsChild && logic->Get(LOGIC_BORROW_SKULL_MASK)), - LOCATION(RC_DEKU_THEATER_MASK_OF_TRUTH, logic->IsChild && logic->Get(LOGIC_BORROW_RIGHT_MASKS)), + LOCATION(RC_DEKU_THEATER_SKULL_MASK, logic->CanUse(RG_SKULL_MASK)), + LOCATION(RC_DEKU_THEATER_MASK_OF_TRUTH, logic->CanUse(RG_MASK_OF_TRUTH)), }, { //Exits Entrance(RR_LW_BEYOND_MIDO, []{return true;}), diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/market.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/market.cpp index f06a83074..b656ecb37 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/market.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/market.cpp @@ -143,10 +143,10 @@ void RegionTable_Init_Market() { //If it is forced on/a setting, a copy of these events should be added to root //it also doesn't need you to open kak gate, but that might be best treated as a bug EventAccess(LOGIC_CAN_BORROW_MASKS, []{return logic->HasItem(RG_ZELDAS_LETTER) && logic->Get(LOGIC_KAKARIKO_GATE_OPEN);}), - EventAccess(LOGIC_BORROW_SKULL_MASK, []{return ctx->GetOption(RSK_COMPLETE_MASK_QUEST) && logic->Get(LOGIC_CAN_BORROW_MASKS);}), - EventAccess(LOGIC_BORROW_SPOOKY_MASK, []{return ctx->GetOption(RSK_COMPLETE_MASK_QUEST) && logic->Get(LOGIC_CAN_BORROW_MASKS);}), - EventAccess(LOGIC_BORROW_BUNNY_HOOD, []{return ctx->GetOption(RSK_COMPLETE_MASK_QUEST) && logic->Get(LOGIC_CAN_BORROW_MASKS);}), - EventAccess(LOGIC_BORROW_RIGHT_MASKS, []{return ctx->GetOption(RSK_COMPLETE_MASK_QUEST) && logic->Get(LOGIC_CAN_BORROW_MASKS);}), + EventAccess(LOGIC_BORROW_SKULL_MASK, []{return ctx->GetOption(RSK_MASK_QUEST).Is(RO_MASK_QUEST_COMPLETED) && logic->Get(LOGIC_CAN_BORROW_MASKS);}), + EventAccess(LOGIC_BORROW_SPOOKY_MASK, []{return ctx->GetOption(RSK_MASK_QUEST).Is(RO_MASK_QUEST_COMPLETED) && logic->Get(LOGIC_CAN_BORROW_MASKS);}), + EventAccess(LOGIC_BORROW_BUNNY_HOOD, []{return ctx->GetOption(RSK_MASK_QUEST).Is(RO_MASK_QUEST_COMPLETED) && logic->Get(LOGIC_CAN_BORROW_MASKS);}), + EventAccess(LOGIC_BORROW_RIGHT_MASKS, []{return ctx->GetOption(RSK_MASK_QUEST).Is(RO_MASK_QUEST_COMPLETED) && logic->Get(LOGIC_CAN_BORROW_MASKS);}), }, { //Locations LOCATION(RC_MASK_SHOP_HINT, true), diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index 00f65a7d6..d708f0e4a 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -120,6 +120,31 @@ bool Logic::HasItem(RandomizerGet itemName) { return CheckQuestItem(RandoGetToQuestItem.at(itemName)); case RG_DOUBLE_DEFENSE: return GetSaveContext()->isDoubleDefenseAcquired; + // Masks + case RG_SKULL_MASK: + switch (ctx->GetOption(RSK_MASK_QUEST).Get()) { + case RO_MASK_QUEST_VANILLA: + return Get(LOGIC_BORROW_SKULL_MASK); + case RO_MASK_QUEST_COMPLETED: + return HasItem(RG_ZELDAS_LETTER) && Get(LOGIC_KAKARIKO_GATE_OPEN); + case RO_MASK_QUEST_SHUFFLE: + return CheckRandoInf(RAND_INF_CHILD_TRADES_HAS_MASK_SKULL); + default: + assert(false); + return false; + } + case RG_MASK_OF_TRUTH: + switch (ctx->GetOption(RSK_MASK_QUEST).Get()) { + case RO_MASK_QUEST_VANILLA: + return Get(LOGIC_BORROW_RIGHT_MASKS); + case RO_MASK_QUEST_COMPLETED: + return HasItem(RG_ZELDAS_LETTER) && Get(LOGIC_KAKARIKO_GATE_OPEN); + case RO_MASK_QUEST_SHUFFLE: + return CheckRandoInf(RAND_INF_CHILD_TRADES_HAS_MASK_TRUTH); + default: + assert(false); + return false; + } case RG_FISHING_POLE: case RG_ZELDAS_LETTER: case RG_WEIRD_EGG: @@ -349,6 +374,9 @@ bool Logic::CanUse(RandomizerGet itemName) { return IsChild; case RG_MAGIC_BEAN: return IsChild; + case RG_SKULL_MASK: + case RG_MASK_OF_TRUTH: + return IsChild; // Songs case RG_ZELDAS_LULLABY: @@ -973,7 +1001,7 @@ bool Logic::CanAvoidEnemy(RandomizerEnemy enemy, bool grounded, uint8_t quantity case RE_KEESE: case RE_FIRE_KEESE: case RE_GUAY: - return CanUse(RG_NUTS); + return CanUse(RG_NUTS) || CanUse(RG_SKULL_MASK); case RE_BLUE_BUBBLE: // RANDOTODO Trick to use shield hylian shield as child to stun these guys return !grounded || CanUse(RG_NUTS) || HookshotOrBoomerang() || CanStandingShield(); @@ -1564,6 +1592,14 @@ std::map Logic::RandoGetToRandInf = { { RG_OCARINA_C_DOWN_BUTTON, RAND_INF_HAS_OCARINA_C_DOWN }, { RG_OCARINA_C_LEFT_BUTTON, RAND_INF_HAS_OCARINA_C_LEFT }, { RG_OCARINA_C_RIGHT_BUTTON, RAND_INF_HAS_OCARINA_C_RIGHT }, + { RG_KEATON_MASK, RAND_INF_CHILD_TRADES_HAS_MASK_KEATON }, + { RG_SKULL_MASK, RAND_INF_CHILD_TRADES_HAS_MASK_SKULL }, + { RG_SPOOKY_MASK, RAND_INF_CHILD_TRADES_HAS_MASK_SPOOKY }, + { RG_BUNNY_HOOD, RAND_INF_CHILD_TRADES_HAS_MASK_BUNNY }, + { RG_GORON_MASK, RAND_INF_CHILD_TRADES_HAS_MASK_GORON }, + { RG_ZORA_MASK, RAND_INF_CHILD_TRADES_HAS_MASK_ZORA }, + { RG_GERUDO_MASK, RAND_INF_CHILD_TRADES_HAS_MASK_GERUDO }, + { RG_MASK_OF_TRUTH, RAND_INF_CHILD_TRADES_HAS_MASK_TRUTH }, { RG_SKELETON_KEY, RAND_INF_HAS_SKELETON_KEY }, { RG_GREG_RUPEE, RAND_INF_GREG_FOUND }, { RG_FISHING_POLE, RAND_INF_FISHING_POLE_FOUND }, @@ -1946,6 +1982,14 @@ void Logic::ApplyItemEffect(Item& item, bool state) { case RG_OCARINA_C_DOWN_BUTTON: case RG_OCARINA_C_LEFT_BUTTON: case RG_OCARINA_C_RIGHT_BUTTON: + case RG_KEATON_MASK: + case RG_SKULL_MASK: + case RG_SPOOKY_MASK: + case RG_BUNNY_HOOD: + case RG_GORON_MASK: + case RG_ZORA_MASK: + case RG_GERUDO_MASK: + case RG_MASK_OF_TRUTH: case RG_GREG_RUPEE: case RG_FISHING_POLE: case RG_GUARD_HOUSE_KEY: diff --git a/soh/soh/Enhancements/randomizer/option_descriptions.cpp b/soh/soh/Enhancements/randomizer/option_descriptions.cpp index 3e5e76c19..347a294e0 100644 --- a/soh/soh/Enhancements/randomizer/option_descriptions.cpp +++ b/soh/soh/Enhancements/randomizer/option_descriptions.cpp @@ -626,8 +626,13 @@ void Settings::CreateOptionDescriptions() { "Start with Zelda's Letter and the item Impa would normally give you and skip the sequence up " "until after meeting Zelda. Disables the ability to shuffle Weird Egg."; mOptionDescriptions[RSK_SKIP_EPONA_RACE] = "Epona can be summoned with Epona's Song without needing to race Ingo."; - mOptionDescriptions[RSK_COMPLETE_MASK_QUEST] = - "Once the Happy Mask Shop is opened, all masks will be available to be borrowed."; + mOptionDescriptions[RSK_MASK_QUEST] = + "How masks are acquired.\n" + "Vanilla - Mask trade quest.\n" + "\n" + "Completed - Once the Happy Mask Shop is opened, all masks will be available to be borrowed.\n" + "\n" + "Shuffle - Happy Mask Shop never opens, masks are shuffled with rest of items."; mOptionDescriptions[RSK_SKIP_SCARECROWS_SONG] = "Start with the ability to summon Pierre the Scarecrow. Pulling out an Ocarina in the usual locations will " "automatically summon him.\n" diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index 140c3e38f..2e8dd0852 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -120,46 +120,42 @@ std::unordered_map spoilerFileDungeonToScene = { { "Ganon's Castle", SCENE_INSIDE_GANONS_CASTLE } }; -std::unordered_map getItemIdToItemId = { - { GI_BOW, ITEM_BOW }, - { GI_ARROW_FIRE, ITEM_ARROW_FIRE }, - { GI_DINS_FIRE, ITEM_DINS_FIRE }, - { GI_SLINGSHOT, ITEM_SLINGSHOT }, - { GI_OCARINA_FAIRY, ITEM_OCARINA_FAIRY }, - { GI_OCARINA_OOT, ITEM_OCARINA_TIME }, - { GI_HOOKSHOT, ITEM_HOOKSHOT }, - { GI_LONGSHOT, ITEM_LONGSHOT }, - { GI_ARROW_ICE, ITEM_ARROW_ICE }, - { GI_FARORES_WIND, ITEM_FARORES_WIND }, - { GI_BOOMERANG, ITEM_BOOMERANG }, - { GI_LENS, ITEM_LENS }, - { GI_HAMMER, ITEM_HAMMER }, - { GI_ARROW_LIGHT, ITEM_ARROW_LIGHT }, - { GI_NAYRUS_LOVE, ITEM_NAYRUS_LOVE }, - { GI_BOTTLE, ITEM_BOTTLE }, - { GI_POTION_RED, ITEM_POTION_RED }, - { GI_POTION_GREEN, ITEM_POTION_GREEN }, - { GI_POTION_BLUE, ITEM_POTION_BLUE }, - { GI_FAIRY, ITEM_FAIRY }, - { GI_FISH, ITEM_FISH }, - { GI_MILK_BOTTLE, ITEM_MILK_BOTTLE }, - { GI_LETTER_RUTO, ITEM_LETTER_RUTO }, - { GI_BLUE_FIRE, ITEM_BLUE_FIRE }, - { GI_BUGS, ITEM_BUG }, - { GI_BIG_POE, ITEM_BIG_POE }, - { GI_POE, ITEM_POE }, - { GI_WEIRD_EGG, ITEM_WEIRD_EGG }, - { GI_LETTER_ZELDA, ITEM_LETTER_ZELDA }, - { GI_POCKET_EGG, ITEM_POCKET_EGG }, - { GI_COJIRO, ITEM_COJIRO }, - { GI_ODD_MUSHROOM, ITEM_ODD_MUSHROOM }, - { GI_ODD_POTION, ITEM_ODD_POTION }, - { GI_SAW, ITEM_SAW }, - { GI_SWORD_BROKEN, ITEM_SWORD_BROKEN }, - { GI_PRESCRIPTION, ITEM_PRESCRIPTION }, - { GI_FROG, ITEM_FROG }, - { GI_EYEDROPS, ITEM_EYEDROPS }, - { GI_CLAIM_CHECK, ITEM_CLAIM_CHECK }, +// used for items that only set a rand inf when obtained +std::map randomizerGetToRandInf = { + { RG_FISHING_POLE, RAND_INF_FISHING_POLE_FOUND }, + { RG_BRONZE_SCALE, RAND_INF_CAN_SWIM }, + { RG_QUIVER_INF, RAND_INF_HAS_INFINITE_QUIVER }, + { RG_BOMB_BAG_INF, RAND_INF_HAS_INFINITE_BOMB_BAG }, + { RG_BULLET_BAG_INF, RAND_INF_HAS_INFINITE_BULLET_BAG }, + { RG_STICK_UPGRADE_INF, RAND_INF_HAS_INFINITE_STICK_UPGRADE }, + { RG_NUT_UPGRADE_INF, RAND_INF_HAS_INFINITE_NUT_UPGRADE }, + { RG_MAGIC_INF, RAND_INF_HAS_INFINITE_MAGIC_METER }, + { RG_BOMBCHU_INF, RAND_INF_HAS_INFINITE_BOMBCHUS }, + { RG_WALLET_INF, RAND_INF_HAS_INFINITE_MONEY }, + { RG_OCARINA_A_BUTTON, RAND_INF_HAS_OCARINA_A }, + { RG_OCARINA_C_UP_BUTTON, RAND_INF_HAS_OCARINA_C_UP }, + { RG_OCARINA_C_DOWN_BUTTON, RAND_INF_HAS_OCARINA_C_DOWN }, + { RG_OCARINA_C_LEFT_BUTTON, RAND_INF_HAS_OCARINA_C_LEFT }, + { RG_OCARINA_C_RIGHT_BUTTON, RAND_INF_HAS_OCARINA_C_RIGHT }, + { RG_DEATH_MOUNTAIN_CRATER_BEAN_SOUL, RAND_INF_DEATH_MOUNTAIN_CRATER_BEAN_SOUL }, + { RG_DEATH_MOUNTAIN_TRAIL_BEAN_SOUL, RAND_INF_DEATH_MOUNTAIN_TRAIL_BEAN_SOUL }, + { RG_DESERT_COLOSSUS_BEAN_SOUL, RAND_INF_DESERT_COLOSSUS_BEAN_SOUL }, + { RG_GERUDO_VALLEY_BEAN_SOUL, RAND_INF_GERUDO_VALLEY_BEAN_SOUL }, + { RG_GRAVEYARD_BEAN_SOUL, RAND_INF_GRAVEYARD_BEAN_SOUL }, + { RG_KOKIRI_FOREST_BEAN_SOUL, RAND_INF_KOKIRI_FOREST_BEAN_SOUL }, + { RG_LAKE_HYLIA_BEAN_SOUL, RAND_INF_LAKE_HYLIA_BEAN_SOUL }, + { RG_LOST_WOODS_BRIDGE_BEAN_SOUL, RAND_INF_LOST_WOODS_BRIDGE_BEAN_SOUL }, + { RG_LOST_WOODS_BEAN_SOUL, RAND_INF_LOST_WOODS_BEAN_SOUL }, + { RG_ZORAS_RIVER_BEAN_SOUL, RAND_INF_ZORAS_RIVER_BEAN_SOUL }, + { RG_GOHMA_SOUL, RAND_INF_GOHMA_SOUL }, + { RG_KING_DODONGO_SOUL, RAND_INF_KING_DODONGO_SOUL }, + { RG_BARINADE_SOUL, RAND_INF_BARINADE_SOUL }, + { RG_PHANTOM_GANON_SOUL, RAND_INF_PHANTOM_GANON_SOUL }, + { RG_VOLVAGIA_SOUL, RAND_INF_VOLVAGIA_SOUL }, + { RG_MORPHA_SOUL, RAND_INF_MORPHA_SOUL }, + { RG_BONGO_BONGO_SOUL, RAND_INF_BONGO_BONGO_SOUL }, + { RG_TWINROVA_SOUL, RAND_INF_TWINROVA_SOUL }, + { RG_GANON_SOUL, RAND_INF_GANON_SOUL }, }; #ifdef _MSC_VER @@ -299,6 +295,10 @@ ItemObtainability Randomizer::GetItemObtainabilityFromRandomizerCheck(Randomizer } ItemObtainability Randomizer::GetItemObtainabilityFromRandomizerGet(RandomizerGet randoGet) { + if (randomizerGetToRandInf.find(randoGet) != randomizerGetToRandInf.end()) { + return Flags_GetRandomizerInf(randomizerGetToRandInf.find(randoGet)->second) ? CANT_OBTAIN_ALREADY_HAVE + : CAN_OBTAIN; + } // This is needed since Plentiful item pool also adds a third progressive wallet // but we should not get Tycoon's Wallet from it if it is off. @@ -702,18 +702,6 @@ ItemObtainability Randomizer::GetItemObtainabilityFromRandomizerGet(RandomizerGe case RG_LIGHT_MEDALLION: return !CHECK_QUEST_ITEM(QUEST_MEDALLION_LIGHT) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE; - // Ocarina Buttons - case RG_OCARINA_A_BUTTON: - return Flags_GetRandomizerInf(RAND_INF_HAS_OCARINA_A) ? CANT_OBTAIN_ALREADY_HAVE : CAN_OBTAIN; - case RG_OCARINA_C_LEFT_BUTTON: - return Flags_GetRandomizerInf(RAND_INF_HAS_OCARINA_C_LEFT) ? CANT_OBTAIN_ALREADY_HAVE : CAN_OBTAIN; - case RG_OCARINA_C_RIGHT_BUTTON: - return Flags_GetRandomizerInf(RAND_INF_HAS_OCARINA_C_RIGHT) ? CANT_OBTAIN_ALREADY_HAVE : CAN_OBTAIN; - case RG_OCARINA_C_UP_BUTTON: - return Flags_GetRandomizerInf(RAND_INF_HAS_OCARINA_C_UP) ? CANT_OBTAIN_ALREADY_HAVE : CAN_OBTAIN; - case RG_OCARINA_C_DOWN_BUTTON: - return Flags_GetRandomizerInf(RAND_INF_HAS_OCARINA_C_DOWN) ? CANT_OBTAIN_ALREADY_HAVE : CAN_OBTAIN; - case RG_RECOVERY_HEART: case RG_GREEN_RUPEE: case RG_GREG_RUPEE: @@ -3887,6 +3875,12 @@ extern "C" u16 Randomizer_Item_Give(PlayState* play, GetItemEntry giEntry) { Flags_SetRandomizerInf( (RandomizerInf)((int)RAND_INF_GUARD_HOUSE_UNLOCKED + ((item - RG_GUARD_HOUSE_KEY) * 2) + 1)); return Return_Item_Entry(giEntry, RG_NONE); + } else if (item >= RG_KEATON_MASK && item <= RG_MASK_OF_TRUTH) { + Flags_SetRandomizerInf((RandomizerInf)((int)RAND_INF_CHILD_TRADES_HAS_MASK_KEATON + (item - RG_KEATON_MASK))); + if (INV_CONTENT(ITEM_TRADE_CHILD) == ITEM_NONE) { + INV_CONTENT(ITEM_TRADE_CHILD) = (int)ITEM_MASK_KEATON + (item - RG_KEATON_MASK); + } + return Return_Item_Entry(giEntry, RG_NONE); } switch (item) { diff --git a/soh/soh/Enhancements/randomizer/randomizerTypes.h b/soh/soh/Enhancements/randomizer/randomizerTypes.h index 8e6dbefbd..e8fa81ff6 100644 --- a/soh/soh/Enhancements/randomizer/randomizerTypes.h +++ b/soh/soh/Enhancements/randomizer/randomizerTypes.h @@ -4641,6 +4641,14 @@ typedef enum { RG_DEKU_STICK_CAPACITY_30, RG_HOOKSHOT, RG_LONGSHOT, + RG_KEATON_MASK, + RG_SKULL_MASK, + RG_SPOOKY_MASK, + RG_BUNNY_HOOD, + RG_GORON_MASK, + RG_ZORA_MASK, + RG_GERUDO_MASK, + RG_MASK_OF_TRUTH, // Overworld keys RG_GUARD_HOUSE_KEY, @@ -5806,6 +5814,14 @@ typedef enum { RHT_OCARINA_C_DOWN_BUTTON, RHT_OCARINA_C_LEFT_BUTTON, RHT_OCARINA_C_RIGHT_BUTTON, + RHT_MASK_KEATON, + RHT_MASK_SKULL, + RHT_MASK_SPOOKY, + RHT_MASK_BUNNY, + RHT_MASK_GORON, + RHT_MASK_ZORA, + RHT_MASK_GERUDO, + RHT_MASK_TRUTH, RHT_BRONZE_SCALE, RHT_CRAWL, RHT_OPEN_CHEST, @@ -6508,7 +6524,7 @@ typedef enum { RSK_SHUFFLE_CHEST_MINIGAME, RSK_BIG_POE_COUNT, RSK_SKIP_EPONA_RACE, - RSK_COMPLETE_MASK_QUEST, + RSK_MASK_QUEST, RSK_SKIP_SCARECROWS_SONG, RSK_SKIP_PLANTING_BEANS, RSK_SKULLS_SUNS_SONG, @@ -6887,6 +6903,13 @@ typedef enum { RO_STARTING_OCARINA_TIME, } RandoOptionStartingOcarina; +// Mask Quest Settings (vanilla, completed, shuffle) +typedef enum { + RO_MASK_QUEST_VANILLA, + RO_MASK_QUEST_COMPLETED, + RO_MASK_QUEST_SHUFFLE, +} RandoOptionMaskQuest; + // Item Pool Settings typedef enum { RO_ITEM_POOL_PLENTIFUL, diff --git a/soh/soh/Enhancements/randomizer/savefile.cpp b/soh/soh/Enhancements/randomizer/savefile.cpp index 34a46a5f4..75bd94ec6 100644 --- a/soh/soh/Enhancements/randomizer/savefile.cpp +++ b/soh/soh/Enhancements/randomizer/savefile.cpp @@ -469,7 +469,7 @@ extern "C" void Randomizer_InitSaveFile() { } // complete mask quest - if (Randomizer_GetSettingValue(RSK_COMPLETE_MASK_QUEST)) { + if (Randomizer_GetSettingValue(RSK_MASK_QUEST) == RO_MASK_QUEST_COMPLETED) { Flags_SetInfTable(INFTABLE_GATE_GUARD_PUT_ON_KEATON_MASK); Flags_SetEventChkInf(EVENTCHKINF_PAID_BACK_BUNNY_HOOD_FEE); diff --git a/soh/soh/Enhancements/randomizer/settings.cpp b/soh/soh/Enhancements/randomizer/settings.cpp index 7ab293b90..e8bfd545a 100644 --- a/soh/soh/Enhancements/randomizer/settings.cpp +++ b/soh/soh/Enhancements/randomizer/settings.cpp @@ -1188,7 +1188,7 @@ void Settings::CreateOptions() { mOptions[RSK_BIG_POES_HINT].Enable(); } }); - OPT_BOOL(RSK_COMPLETE_MASK_QUEST, "Complete Mask Quest", CVAR_RANDOMIZER_SETTING("CompleteMaskQuest"), mOptionDescriptions[RSK_COMPLETE_MASK_QUEST]); + OPT_U8(RSK_MASK_QUEST, "Mask Quest", {"Vanilla", "Completed", "Shuffle"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("CompleteMaskQuest"), mOptionDescriptions[RSK_MASK_QUEST], WIDGET_CVAR_COMBOBOX, 0); OPT_U8(RSK_GOSSIP_STONE_HINTS, "Gossip Stone Hints", {"No Hints", "Need Nothing", "Mask of Truth", "Stone of Agony"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("GossipStoneHints"), mOptionDescriptions[RSK_GOSSIP_STONE_HINTS], WIDGET_CVAR_COMBOBOX, RO_GOSSIP_STONES_NEED_NOTHING, false, nullptr, IMFLAG_NONE); OPT_CALLBACK(RSK_GOSSIP_STONE_HINTS, { if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("GossipStoneHints"), RO_GOSSIP_STONES_NEED_NOTHING) == @@ -2193,7 +2193,7 @@ void Settings::CreateOptions() { &mOptions[RSK_FULL_WALLETS], &mOptions[RSK_SLINGBOW_BREAK_BEEHIVES], &mOptions[RSK_SKIP_CHILD_ZELDA], - &mOptions[RSK_COMPLETE_MASK_QUEST], + &mOptions[RSK_MASK_QUEST], &mOptions[RSK_SKIP_CHILD_STEALTH], &mOptions[RSK_SKIP_PLANTING_BEANS], &mOptions[RSK_SKIP_EPONA_RACE], @@ -2704,7 +2704,6 @@ void Settings::CreateOptions() { &mOptions[RSK_SKIP_SCARECROWS_SONG], &mOptions[RSK_SKIP_PLANTING_BEANS], &mOptions[RSK_BIG_POE_COUNT], - &mOptions[RSK_COMPLETE_MASK_QUEST], }); mOptionGroups[RSG_MISC] = OptionGroup("Miscellaneous Settings", { diff --git a/soh/src/code/z_parameter.c b/soh/src/code/z_parameter.c index 86a72456f..049f0eaf5 100644 --- a/soh/src/code/z_parameter.c +++ b/soh/src/code/z_parameter.c @@ -2120,7 +2120,7 @@ u8 Item_Give(PlayState* play, u8 item) { } } } - // update the adult/child equips when rando'd (accounting for equp swapped hookshot as child) + // update the adult/child equips when rando'd (accounting for equip swapped hookshot as child) if (IS_RANDO && LINK_IS_CHILD) { for (i = 1; i < ARRAY_COUNT(gSaveContext.adultEquips.buttonItems); i++) { if (gSaveContext.adultEquips.buttonItems[i] == ITEM_HOOKSHOT) { 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 1bf210630..f57930de0 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 @@ -324,9 +324,9 @@ void KaleidoScope_HandleItemCycleExtras(PlayState* play, u8 slot, bool canCycle, bool CanMaskSelect() { if (IS_RANDO) { - return CVarGetInteger(CVAR_ENHANCEMENT("MaskSelect"), 0) && - Flags_GetRandomizerInf( - RAND_INF_ZELDAS_LETTER); /* || Randomizer_GetSettingValue(RSK_SHUFFLE_CHILD_TRADE) */ + return (CVarGetInteger(CVAR_ENHANCEMENT("MaskSelect"), 0) && Flags_GetRandomizerInf(RAND_INF_ZELDAS_LETTER) && + Flags_GetInfTable(INFTABLE_SHOWED_ZELDAS_LETTER_TO_GATE_GUARD)) || + Randomizer_GetSettingValue(RSK_MASK_QUEST) == RO_MASK_QUEST_SHUFFLE; } // only allow mask select when: