From 1ea7206073183e7f7424e02eceb44d4d9bf35300 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philip=20Dub=C3=A9?= <159546+serprex@users.noreply.github.com> Date: Sun, 19 Jul 2026 22:37:45 +0000 Subject: [PATCH] Shuffle Zelda's Letter (#6938) Removes RSK_SKIP_CHILD_ZELDA & RSK_KAK_GATE, these are managed by RSK_SHUFFLE_ZELDAS_LETTER & RSK_STARTING_ZELDAS_LETTER & RSK_SHUFFLE_WEIRD_EGG --- .../Rando Seed Settings - Advanced.json | 4 +- .../Rando Seed Settings - Beginner.json | 4 +- .../Rando Seed Settings - Hell Mode.json | 2 +- .../Rando Seed Settings - Standard.json | 4 +- .../Enhancements/randomizer/3drando/hints.cpp | 2 +- .../randomizer/3drando/item_pool.cpp | 10 +++- .../randomizer/3drando/starting_inventory.cpp | 6 ++- .../randomizer/ShuffleTradeItems.c | 1 + .../Enhancements/randomizer/hook_handlers.cpp | 4 +- soh/soh/Enhancements/randomizer/item_list.cpp | 4 +- .../overworld/castle_grounds.cpp | 17 +++++-- .../overworld/lon_lon_ranch.cpp | 4 +- .../location_access/overworld/market.cpp | 2 +- .../overworld/sacred_forest_meadow.cpp | 2 +- .../randomizer/location_access/root.cpp | 11 +++-- soh/soh/Enhancements/randomizer/logic.cpp | 4 +- .../randomizer/option_descriptions.cpp | 31 ++++++------- .../Enhancements/randomizer/randomizer.cpp | 26 ++++++++++- .../randomizer/randomizerEnums/LogicVal.h | 3 ++ .../randomizerEnums/RandomizerOptions.h | 13 +++--- .../randomizerEnums/RandomizerSettingKey.h | 4 +- .../randomizer/randomizer_check_objects.cpp | 6 ++- .../randomizer/randomizer_check_tracker.cpp | 11 +++-- soh/soh/Enhancements/randomizer/savefile.cpp | 46 ++++++++++--------- soh/soh/Enhancements/randomizer/settings.cpp | 34 ++++---------- soh/soh/SohGui/SohMenuStartingItems.cpp | 19 +++++++- soh/soh/config/ConfigUpdaters.cpp | 10 ++++ soh/src/code/z_parameter.c | 1 + .../ovl_Bg_Gate_Shutter/z_bg_gate_shutter.c | 3 +- .../overlays/actors/ovl_En_Ossan/z_en_ossan.c | 5 +- .../misc/ovl_kaleido_scope/z_kaleido_item.c | 4 +- 31 files changed, 180 insertions(+), 117 deletions(-) diff --git a/soh/assets/custom/presets/Rando Seed Settings - Advanced.json b/soh/assets/custom/presets/Rando Seed Settings - Advanced.json index 27b30656da..bb0962be0d 100644 --- a/soh/assets/custom/presets/Rando Seed Settings - Advanced.json +++ b/soh/assets/custom/presets/Rando Seed Settings - Advanced.json @@ -22,7 +22,6 @@ "GregHint": 1, "HBAHint": 1, "IncludeTycoonWallet": 1, - "KakarikoGate": 1, "Keysanity": 5, "GbkRewardCount": 8, "MalonHint": 1, @@ -55,11 +54,12 @@ "ShuffleSongs": 2, "ShuffleSwim": 1, "ShuffleTokens": 3, - "SkipChildZelda": 1, + "ShuffleWeirdEgg": 2, "SkipEponaRace": 1, "SkipScarecrowsSong": 1, "StartingAge": 2, "StartingMapsCompasses": 0, + "StartingZeldasLetter": 1, "SunlightArrows": 1 } } diff --git a/soh/assets/custom/presets/Rando Seed Settings - Beginner.json b/soh/assets/custom/presets/Rando Seed Settings - Beginner.json index 12abe7e48a..ef2fc3eafc 100644 --- a/soh/assets/custom/presets/Rando Seed Settings - Beginner.json +++ b/soh/assets/custom/presets/Rando Seed Settings - Beginner.json @@ -25,7 +25,6 @@ "GregHint": 1, "HBAHint": 1, "IncludeTycoonWallet": 1, - "KakarikoGate": 1, "Keysanity": 2, "GbkRewardCount": 6, "MalonHint": 1, @@ -35,12 +34,13 @@ "SheikLAHint": 0, "ShuffleGanonBossKey": 8, "ShuffleOcarinas": 1, - "SkipChildZelda": 1, + "ShuffleWeirdEgg": 2, "SkipEponaRace": 1, "SkipScarecrowsSong": 1, "StartingKokiriSword": 1, "StartingMapsCompasses": 0, "StartingOcarina": 1, + "StartingZeldasLetter": 1, "SunlightArrows": 1, "ZorasFountain": 1 } diff --git a/soh/assets/custom/presets/Rando Seed Settings - Hell Mode.json b/soh/assets/custom/presets/Rando Seed Settings - Hell Mode.json index 6bb0b8c527..cd506b3090 100644 --- a/soh/assets/custom/presets/Rando Seed Settings - Hell Mode.json +++ b/soh/assets/custom/presets/Rando Seed Settings - Hell Mode.json @@ -16,7 +16,6 @@ "FishsanityPondCount": 17, "GerudoKeys": 3, "IncludeTycoonWallet": 1, - "KakarikoGate": 1, "Keysanity": 5, "GbkRewardCount": 10, "GbkRewardOptions": 1, @@ -83,6 +82,7 @@ "ShuffleTokens": 3, "ShuffleWarpSongs": 1, "ShuffleWeirdEgg": 1, + "ShuffleZeldasLetter": 1, "StartingAge": 2, "StartingHearts": 0, "StartingMapsCompasses": 5, diff --git a/soh/assets/custom/presets/Rando Seed Settings - Standard.json b/soh/assets/custom/presets/Rando Seed Settings - Standard.json index dddd81ece8..b0647e10aa 100644 --- a/soh/assets/custom/presets/Rando Seed Settings - Standard.json +++ b/soh/assets/custom/presets/Rando Seed Settings - Standard.json @@ -25,7 +25,6 @@ "GregHint": 1, "HBAHint": 1, "IncludeTycoonWallet": 1, - "KakarikoGate": 1, "Keysanity": 5, "GbkRewardCount": 7, "MalonHint": 1, @@ -48,11 +47,12 @@ "ShuffleScrubs": 2, "ShuffleSongs": 2, "ShuffleTokens": 3, - "SkipChildZelda": 1, + "ShuffleWeirdEgg": 2, "SkipEponaRace": 1, "SkipScarecrowsSong": 1, "StartingMapsCompasses": 0, "StartingOcarina": 1, + "StartingZeldasLetter": 1, "SunlightArrows": 1, "ZorasFountain": 1 } diff --git a/soh/soh/Enhancements/randomizer/3drando/hints.cpp b/soh/soh/Enhancements/randomizer/3drando/hints.cpp index e0335a054a..f8f6ffdd40 100644 --- a/soh/soh/Enhancements/randomizer/3drando/hints.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/hints.cpp @@ -638,7 +638,7 @@ void CreateStoneHints() { std::vector distTable = hintSetting.distTable; // Apply impa's song exclusions when zelda is skipped - if (ctx->GetOption(RSK_SKIP_CHILD_ZELDA)) { + if (ctx->GetOption(RSK_STARTING_ZELDAS_LETTER) && !ctx->GetOption(RSK_SHUFFLE_ZELDAS_LETTER)) { ctx->GetItemLocation(RC_SONG_FROM_IMPA)->SetHintAccesible(); } if (ctx->GetOption(RSK_SELECTED_STARTING_AGE).Is(RO_AGE_ADULT) || !ctx->GetOption(RSK_SHUFFLE_MASTER_SWORD)) { diff --git a/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp b/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp index 07b57b6f76..4486895290 100644 --- a/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp @@ -331,7 +331,9 @@ void GenerateItemPool() { } // Fixed item locations - ctx->PlaceItemInLocation(RC_HC_ZELDAS_LETTER, RG_ZELDAS_LETTER); + if (!ctx->GetOption(RSK_SHUFFLE_ZELDAS_LETTER)) { + ctx->PlaceItemInLocation(RC_HC_ZELDAS_LETTER, RG_ZELDAS_LETTER); + } ctx->PlaceItemInLocation(RC_GANONS_BOSS_KEY, RG_BLUE_RUPEE); // placeholder, filled by setting ctx->PlaceItemInLocation(RC_GANON_SOUL, RG_BLUE_RUPEE); // placeholder, filled by setting ctx->PlaceItemInLocation(RC_WINCON, RG_BLUE_RUPEE); // placeholder, filled by setting @@ -364,7 +366,7 @@ void GenerateItemPool() { } } - if (ctx->GetOption(RSK_SHUFFLE_WEIRD_EGG)) { + if (ctx->GetOption(RSK_SHUFFLE_WEIRD_EGG).Is(RO_WEIRD_EGG_SHUFFLED)) { if (!ctx->GetOption(RSK_STARTING_WEIRD_EGG)) { AddItemToPool(RG_WEIRD_EGG, 2, 1, 1, 1); } @@ -372,6 +374,10 @@ void GenerateItemPool() { ctx->PlaceItemInLocation(RC_HC_MALON_EGG, RG_WEIRD_EGG, false, true); } + if (ctx->GetOption(RSK_SHUFFLE_ZELDAS_LETTER) && !ctx->GetOption(RSK_STARTING_ZELDAS_LETTER)) { + AddItemToPool(RG_ZELDAS_LETTER, 2, 1, 1, 1); + } + if (ctx->GetOption(RSK_SHUFFLE_OCARINA)) { if (ctx->GetOption(RSK_STARTING_OCARINA).IsNot(RO_STARTING_OCARINA_TIME)) { int baseOcarinas = ctx->GetOption(RSK_STARTING_OCARINA).Is(RO_STARTING_OCARINA_OFF) ? 2 : 1; diff --git a/soh/soh/Enhancements/randomizer/3drando/starting_inventory.cpp b/soh/soh/Enhancements/randomizer/3drando/starting_inventory.cpp index f3c023a6ff..65ff7a5136 100644 --- a/soh/soh/Enhancements/randomizer/3drando/starting_inventory.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/starting_inventory.cpp @@ -109,9 +109,13 @@ void GenerateStartingInventory() { } } // The weird egg only exists as an item when it's shuffled; vanilla gives it through the cutscene. - if (ctx->GetOption(RSK_SHUFFLE_WEIRD_EGG)) { + if (ctx->GetOption(RSK_SHUFFLE_WEIRD_EGG).Is(RO_WEIRD_EGG_SHUFFLED)) { AddItemToInventory(RG_WEIRD_EGG, ctx->GetOption(RSK_STARTING_WEIRD_EGG) ? 1 : 0); } + // Same for Zelda's Letter. + if (ctx->GetOption(RSK_SHUFFLE_ZELDAS_LETTER)) { + AddItemToInventory(RG_ZELDAS_LETTER, ctx->GetOption(RSK_STARTING_ZELDAS_LETTER) ? 1 : 0); + } AddItemToInventory(RG_BUNNY_HOOD, ctx->GetOption(RSK_STARTING_BUNNY_HOOD) ? 1 : 0); AddItemToInventory(RG_CLAIM_CHECK, ctx->GetOption(RSK_STARTING_CLAIM_CHECK) ? 1 : 0); AddItemToInventory(RG_PROGRESSIVE_OCARINA, ctx->GetOption(RSK_STARTING_OCARINA).Get()); diff --git a/soh/soh/Enhancements/randomizer/ShuffleTradeItems.c b/soh/soh/Enhancements/randomizer/ShuffleTradeItems.c index 8b43884e4b..17006ce850 100644 --- a/soh/soh/Enhancements/randomizer/ShuffleTradeItems.c +++ b/soh/soh/Enhancements/randomizer/ShuffleTradeItems.c @@ -1,3 +1,4 @@ +#include "ShuffleTradeItems.h" #include "functions.h" #include "variables.h" #include "macros.h" diff --git a/soh/soh/Enhancements/randomizer/hook_handlers.cpp b/soh/soh/Enhancements/randomizer/hook_handlers.cpp index c93851a7ef..15ac164ab1 100644 --- a/soh/soh/Enhancements/randomizer/hook_handlers.cpp +++ b/soh/soh/Enhancements/randomizer/hook_handlers.cpp @@ -294,11 +294,11 @@ void RandomizerOnFlagSetHandler(int16_t flagType, int16_t flag) { } if (flagType == FLAG_EVENT_CHECK_INF && flag == EVENTCHKINF_TALON_WOKEN_IN_CASTLE) { - // remove chicken as this is the only use for it Flags_UnsetRandomizerInf(RAND_INF_CHILD_TRADES_HAS_CHICKEN); } - if (flagType == FLAG_EVENT_CHECK_INF && flag == EVENTCHKINF_OBTAINED_ZELDAS_LETTER) { + if (flagType == FLAG_EVENT_CHECK_INF && flag == EVENTCHKINF_OBTAINED_ZELDAS_LETTER && + !RAND_GET_OPTION(RSK_SHUFFLE_ZELDAS_LETTER)) { Flags_SetRandomizerInf(RAND_INF_ZELDAS_LETTER); } diff --git a/soh/soh/Enhancements/randomizer/item_list.cpp b/soh/soh/Enhancements/randomizer/item_list.cpp index dd9c5aef3d..39663d611f 100644 --- a/soh/soh/Enhancements/randomizer/item_list.cpp +++ b/soh/soh/Enhancements/randomizer/item_list.cpp @@ -46,8 +46,8 @@ void Rando::StaticData::InitItemTable() { itemTable[RG_DOUBLE_DEFENSE] = Item(RG_DOUBLE_DEFENSE, Text{ "Double Defense", "Double Défense", "Doppelte Verteidigung" }, ITEMTYPE_ITEM, RG_DOUBLE_DEFENSE, true, LOGIC_DOUBLE_DEFENSE, RHT_DOUBLE_DEFENSE, RG_DOUBLE_DEFENSE, OBJECT_GI_HEARTS, GID_HEART_CONTAINER, 0xE9, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_HEALTH, MOD_RANDOMIZER, {"", "la ", ""}); itemTable[RG_DOUBLE_DEFENSE].SetCustomDrawFunc(Randomizer_DrawDoubleDefense); // Trade Quest Items - itemTable[RG_WEIRD_EGG] = Item(RG_WEIRD_EGG, Text{ "Weird Egg", "Oeuf Curieux", "Seltsames Ei" }, ITEMTYPE_ITEM, GI_WEIRD_EGG, true, LOGIC_WEIRD_EGG, RHT_WEIRD_EGG, ITEM_WEIRD_EGG, OBJECT_GI_EGG, GID_EGG, 0x9A, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, {"the ", "l'", "das "}); - itemTable[RG_ZELDAS_LETTER] = Item(RG_ZELDAS_LETTER, Text{ "Zelda's Letter", "Lettre de Zelda", "Zeldas Brief" }, ITEMTYPE_ITEM, GI_LETTER_ZELDA, true, LOGIC_ZELDAS_LETTER, RHT_ZELDAS_LETTER, ITEM_LETTER_ZELDA, OBJECT_GI_LETTER, GID_LETTER_ZELDA, 0x69, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, {"", "la ", ""}); + itemTable[RG_WEIRD_EGG] = Item(RG_WEIRD_EGG, Text{ "Weird Egg", "Oeuf Curieux", "Seltsames Ei" }, ITEMTYPE_ITEM, GI_WEIRD_EGG, true, LOGIC_WEIRD_EGG, RHT_WEIRD_EGG, ITEM_WEIRD_EGG, OBJECT_GI_EGG, GID_EGG, 0x9A, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER, {"the ", "l'", "das "}); + itemTable[RG_ZELDAS_LETTER] = Item(RG_ZELDAS_LETTER, Text{ "Zelda's Letter", "Lettre de Zelda", "Zeldas Brief" }, ITEMTYPE_ITEM, GI_LETTER_ZELDA, true, LOGIC_ZELDAS_LETTER, RHT_ZELDAS_LETTER, ITEM_LETTER_ZELDA, OBJECT_GI_LETTER, GID_LETTER_ZELDA, 0x69, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER, {"", "la ", ""}); itemTable[RG_POCKET_EGG] = Item(RG_POCKET_EGG, Text{ "Pocket Egg", "Oeuf de poche", "Ei" }, ITEMTYPE_ITEM, GI_POCKET_EGG, true, LOGIC_POCKET_EGG, RHT_POCKET_EGG, ITEM_POCKET_EGG, OBJECT_GI_EGG, GID_EGG, 0x01, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, {"the ", "l'", "das "}); itemTable[RG_COJIRO] = Item(RG_COJIRO, Text{ "Cojiro", "P'tit Poulet", "Henni" }, ITEMTYPE_ITEM, GI_COJIRO, true, LOGIC_COJIRO, RHT_COJIRO, ITEM_COJIRO, OBJECT_GI_NIWATORI, GID_COJIRO, 0x02, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE); itemTable[RG_ODD_MUSHROOM] = Item(RG_ODD_MUSHROOM, Text{ "Odd Mushroom", "Champigon Suspect", "Schimmelpilz" }, ITEMTYPE_ITEM, GI_ODD_MUSHROOM, true, LOGIC_ODD_MUSHROOM, RHT_ODD_MUSHROOM, ITEM_ODD_MUSHROOM, OBJECT_GI_MUSHROOM, GID_ODD_MUSHROOM, 0x03, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, {"the ", "le ", "den "}); diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/castle_grounds.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/castle_grounds.cpp index 6d7a0e3e12..372f993b62 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/castle_grounds.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/castle_grounds.cpp @@ -25,6 +25,7 @@ void RegionTable_Init_CastleGrounds() { //Events EVENT_ACCESS(LOGIC_FAIRY_ACCESS, logic->CallGossipFairy() || logic->CanUse(RG_STICKS)), EVENT_ACCESS(LOGIC_BUG_ACCESS, logic->HasItem(RG_POWER_BRACELET)), + EVENT_ACCESS(LOGIC_MALON_RETURNED_FROM_CASTLE, logic->Get(LOGIC_TALON_RETURNED_FROM_CASTLE) && logic->HasItem(RG_SPEAK_HYLIAN)), }, { //Locations LOCATION(RC_HC_MALON_EGG, logic->HasItem(RG_SPEAK_HYLIAN)), @@ -95,7 +96,10 @@ void RegionTable_Init_CastleGrounds() { ENTRANCE(RR_HC_MOAT, true), }); - areaTable[RR_HC_MOAT] = Region("Hyrule Castle Moat", SCENE_HYRULE_CASTLE, {}, { + areaTable[RR_HC_MOAT] = Region("Hyrule Castle Moat", SCENE_HYRULE_CASTLE, { + //Events + EVENT_ACCESS(LOGIC_TALON_RETURNED_FROM_CASTLE, logic->CanUse(RG_WEIRD_EGG) && logic->HasItem(RG_SPEAK_HYLIAN)), + }, { //Locations LOCATION(RC_HC_GRASS_1, logic->CanCutShrubs()), LOCATION(RC_HC_GRASS_2, logic->CanCutShrubs()), @@ -116,7 +120,7 @@ void RegionTable_Init_CastleGrounds() { //Exits ENTRANCE(RR_HC_GATE, true), ENTRANCE(RR_HC_STORMS_GROTTO, logic->CanOpenStormsGrotto()), - ENTRANCE(RR_HC_DRAIN_LEDGE, (logic->CanUse(RG_WEIRD_EGG) && logic->HasItem(RG_POWER_BRACELET) && logic->HasItem(RG_SPEAK_HYLIAN)) || + ENTRANCE(RR_HC_DRAIN_LEDGE, (logic->Get(LOGIC_TALON_RETURNED_FROM_CASTLE) && logic->HasItem(RG_POWER_BRACELET)) || (ctx->GetTrickOption(RT_DAMAGE_BOOST_SIMPLE) && logic->TakeDamage() && logic->HasExplosives() && logic->CanJumpslash())), }); @@ -126,10 +130,13 @@ void RegionTable_Init_CastleGrounds() { ENTRANCE(RR_HC_GARDEN, logic->CanUse(RG_CRAWL)), }); - areaTable[RR_HC_GARDEN] = Region("HC Garden", SCENE_CASTLE_COURTYARD_ZELDA, {}, { + areaTable[RR_HC_GARDEN] = Region("HC Garden", SCENE_CASTLE_COURTYARD_ZELDA, { + //Events + EVENT_ACCESS(LOGIC_MET_ZELDA, logic->HasItem(RG_SPEAK_HYLIAN)), + }, { //Locations - LOCATION(RC_HC_ZELDAS_LETTER, logic->HasItem(RG_SPEAK_HYLIAN)), - LOCATION(RC_SONG_FROM_IMPA, logic->HasItem(RG_SPEAK_HYLIAN)), + LOCATION(RC_HC_ZELDAS_LETTER, logic->Get(LOGIC_MET_ZELDA)), + LOCATION(RC_SONG_FROM_IMPA, logic->Get(LOGIC_MET_ZELDA)), LOCATION(RC_HC_WONDER_COURTYARD_RIGHT_WINDOW, logic->CanUse(RG_FAIRY_SLINGSHOT)), LOCATION(RC_HC_WONDER_COURTYARD_LEFT_WINDOW, logic->CanUse(RG_FAIRY_SLINGSHOT)), }, { diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/lon_lon_ranch.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/lon_lon_ranch.cpp index 622175717b..88975a2ece 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/lon_lon_ranch.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/lon_lon_ranch.cpp @@ -11,7 +11,7 @@ void RegionTable_Init_LonLonRanch() { EVENT_ACCESS(LOGIC_LINKS_COW, logic->HasItem(RG_CHILD_WALLET) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->CanUse(RG_EPONAS_SONG) && logic->IsAdult && logic->AtDay), }, { //Locations - LOCATION(RC_SONG_FROM_MALON, logic->IsChild && logic->HasItem(RG_ZELDAS_LETTER) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->HasItem(RG_FAIRY_OCARINA) && logic->AtDay), + LOCATION(RC_SONG_FROM_MALON, logic->IsChild && logic->Get(LOGIC_MALON_RETURNED_FROM_CASTLE) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->HasItem(RG_FAIRY_OCARINA) && logic->AtDay), LOCATION(RC_LLR_GS_TREE, logic->IsChild && logic->CanBonkTrees() && (logic->HasItem(RG_POWER_BRACELET) || logic->CanKillEnemy(RE_GOLD_SKULLTULA))), LOCATION(RC_LLR_GS_RAIN_SHED, logic->IsChild && logic->CanGetNightTimeGS() && (logic->HasItem(RG_POWER_BRACELET) || logic->CanKillEnemy(RE_GOLD_SKULLTULA))), LOCATION(RC_LLR_GS_HOUSE_WINDOW, logic->IsChild && logic->HookshotOrBoomerang() && logic->CanGetNightTimeGS()), @@ -38,7 +38,7 @@ void RegionTable_Init_LonLonRanch() { areaTable[RR_LLR_TALONS_HOUSE] = Region("LLR Talons House", SCENE_LON_LON_BUILDINGS, {}, { //Locations - LOCATION(RC_LLR_TALONS_CHICKENS, logic->HasItem(RG_CHILD_WALLET) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->IsChild && logic->AtDay && logic->HasItem(RG_ZELDAS_LETTER) && logic->HasItem(RG_POWER_BRACELET)), + LOCATION(RC_LLR_TALONS_CHICKENS, logic->HasItem(RG_CHILD_WALLET) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->IsChild && logic->AtDay && logic->Get(LOGIC_TALON_RETURNED_FROM_CASTLE) && logic->HasItem(RG_POWER_BRACELET)), LOCATION(RC_LLR_TALONS_HOUSE_POT_1, logic->CanBreakPots()), LOCATION(RC_LLR_TALONS_HOUSE_POT_2, logic->CanBreakPots()), LOCATION(RC_LLR_TALONS_HOUSE_POT_3, logic->CanBreakPots()), diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/market.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/market.cpp index 73b51e3c14..58d683bbbe 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/market.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/market.cpp @@ -152,7 +152,7 @@ void RegionTable_Init_Market() { //Currently, mask swap in menu doesn't need access to the mask shop //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 - EVENT_ACCESS(LOGIC_CAN_BORROW_MASKS, logic->HasItem(RG_ZELDAS_LETTER) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->Get(LOGIC_KAKARIKO_GATE_OPEN)), + EVENT_ACCESS(LOGIC_CAN_BORROW_MASKS, logic->HasItem(RG_SPEAK_HYLIAN) && logic->Get(LOGIC_KAKARIKO_GATE_OPEN)), EVENT_ACCESS(LOGIC_BORROW_SKULL_MASK, ctx->GetOption(RSK_MASK_QUEST).Is(RO_MASK_QUEST_COMPLETED) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->Get(LOGIC_CAN_BORROW_MASKS)), EVENT_ACCESS(LOGIC_BORROW_SPOOKY_MASK, ctx->GetOption(RSK_MASK_QUEST).Is(RO_MASK_QUEST_COMPLETED) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->Get(LOGIC_CAN_BORROW_MASKS)), EVENT_ACCESS(LOGIC_BORROW_BUNNY_HOOD, ctx->GetOption(RSK_MASK_QUEST).Is(RO_MASK_QUEST_COMPLETED) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->Get(LOGIC_CAN_BORROW_MASKS)), diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/sacred_forest_meadow.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/sacred_forest_meadow.cpp index fbda4879b9..eb7d375c35 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/sacred_forest_meadow.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/sacred_forest_meadow.cpp @@ -42,7 +42,7 @@ void RegionTable_Init_SacredForestMeadow() { EVENT_ACCESS(LOGIC_FAIRY_ACCESS, logic->CallGossipFairyExceptSuns()), }, { //Locations - LOCATION(RC_SONG_FROM_SARIA, logic->IsChild && logic->HasItem(RG_ZELDAS_LETTER)), + LOCATION(RC_SONG_FROM_SARIA, logic->IsChild && logic->Get(LOGIC_MET_ZELDA)), LOCATION(RC_SHEIK_IN_FOREST, logic->IsAdult), LOCATION(RC_SFM_SARIA_GOSSIP_STONE_FAIRY, logic->CallGossipFairyExceptSuns()), LOCATION(RC_SFM_SARIA_GOSSIP_STONE_FAIRY_BIG, logic->CanUse(RG_SONG_OF_STORMS)), diff --git a/soh/soh/Enhancements/randomizer/location_access/root.cpp b/soh/soh/Enhancements/randomizer/location_access/root.cpp index 72b591017e..0882495fcd 100644 --- a/soh/soh/Enhancements/randomizer/location_access/root.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/root.cpp @@ -7,7 +7,7 @@ void RegionTable_Init_Root() { // clang-format off areaTable[RR_ROOT] = Region("Root", SCENE_ID_MAX, TIME_DOESNT_PASS, {RA_LINKS_POCKET}, { //Events - EVENT_ACCESS(LOGIC_KAKARIKO_GATE_OPEN, ctx->GetOption(RSK_KAK_GATE).Is(RO_KAK_GATE_OPEN)), + EVENT_ACCESS(LOGIC_KAKARIKO_GATE_OPEN, (bool)ctx->GetOption(RSK_STARTING_ZELDAS_LETTER)), EVENT_ACCESS(LOGIC_TH_COULD_FREE_1_TORCH_CARPENTER, ctx->GetOption(RSK_GERUDO_FORTRESS).Is(RO_GF_CARPENTERS_FREE)), EVENT_ACCESS(LOGIC_TH_COULD_FREE_DOUBLE_CELL_CARPENTER, ctx->GetOption(RSK_GERUDO_FORTRESS).Is(RO_GF_CARPENTERS_FREE) || ctx->GetOption(RSK_GERUDO_FORTRESS).Is(RO_GF_CARPENTERS_FAST)), EVENT_ACCESS(LOGIC_TH_COULD_FREE_DEAD_END_CARPENTER, ctx->GetOption(RSK_GERUDO_FORTRESS).Is(RO_GF_CARPENTERS_FREE) || ctx->GetOption(RSK_GERUDO_FORTRESS).Is(RO_GF_CARPENTERS_FAST)), @@ -15,6 +15,9 @@ void RegionTable_Init_Root() { EVENT_ACCESS(LOGIC_TH_RESCUED_ALL_CARPENTERS, ctx->GetOption(RSK_GERUDO_FORTRESS).Is(RO_GF_CARPENTERS_FREE)), EVENT_ACCESS(LOGIC_FREED_EPONA, (bool)ctx->GetOption(RSK_SKIP_EPONA_RACE)), EVENT_ACCESS(LOGIC_SHOWED_MIDO_SWORD_AND_SHIELD, ctx->GetOption(RSK_FOREST).Is(RO_CLOSED_FOREST_OFF)), + EVENT_ACCESS(LOGIC_MET_ZELDA, ctx->GetOption(RSK_STARTING_ZELDAS_LETTER) && !ctx->GetOption(RSK_SHUFFLE_ZELDAS_LETTER)), + EVENT_ACCESS(LOGIC_MALON_RETURNED_FROM_CASTLE, ctx->GetOption(RSK_SHUFFLE_WEIRD_EGG).Is(RO_WEIRD_EGG_SKIP_TALON)), + EVENT_ACCESS(LOGIC_TALON_RETURNED_FROM_CASTLE, ctx->GetOption(RSK_SHUFFLE_WEIRD_EGG).Is(RO_WEIRD_EGG_SKIP_TALON)), }, { //Locations LOCATION(RC_LINKS_POCKET, true), @@ -22,9 +25,9 @@ void RegionTable_Init_Root() { LOCATION(RC_GANON_SOUL, logic->CanTriggerGanonsSoul()), LOCATION(RC_WINCON, logic->CanTriggerWincon()), LOCATION(RC_SARIA_SONG_HINT, logic->CanUse(RG_SARIAS_SONG)), - LOCATION(RC_SONG_FROM_IMPA, (bool)ctx->GetOption(RSK_SKIP_CHILD_ZELDA)), - LOCATION(RC_HC_MALON_EGG, (bool)ctx->GetOption(RSK_SKIP_CHILD_ZELDA)), - LOCATION(RC_HC_ZELDAS_LETTER, (bool)ctx->GetOption(RSK_SKIP_CHILD_ZELDA)), + LOCATION(RC_SONG_FROM_IMPA, ctx->GetOption(RSK_STARTING_ZELDAS_LETTER) && !ctx->GetOption(RSK_SHUFFLE_ZELDAS_LETTER)), + LOCATION(RC_HC_MALON_EGG, ctx->GetOption(RSK_SHUFFLE_WEIRD_EGG).Is(RO_WEIRD_EGG_SKIP_TALON)), + LOCATION(RC_HC_ZELDAS_LETTER, ctx->GetOption(RSK_STARTING_ZELDAS_LETTER) && !ctx->GetOption(RSK_SHUFFLE_ZELDAS_LETTER)), LOCATION(RC_TOT_MASTER_SWORD, (bool)ctx->GetOption(RSK_SELECTED_STARTING_AGE).Is(RO_AGE_ADULT)), }, { //Exits diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index ad51c75554..c00b4aac5f 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -122,7 +122,7 @@ bool Logic::HasItem(RandomizerGet itemName) { 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); + return Get(LOGIC_KAKARIKO_GATE_OPEN); case RO_MASK_QUEST_SHUFFLE: return CheckRandoInf(RAND_INF_CHILD_TRADES_HAS_MASK_SKULL); default: @@ -134,7 +134,7 @@ bool Logic::HasItem(RandomizerGet itemName) { 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); + return Get(LOGIC_KAKARIKO_GATE_OPEN); case RO_MASK_QUEST_SHUFFLE: return CheckRandoInf(RAND_INF_CHILD_TRADES_HAS_MASK_TRUTH); default: diff --git a/soh/soh/Enhancements/randomizer/option_descriptions.cpp b/soh/soh/Enhancements/randomizer/option_descriptions.cpp index e230906edf..6530386102 100644 --- a/soh/soh/Enhancements/randomizer/option_descriptions.cpp +++ b/soh/soh/Enhancements/randomizer/option_descriptions.cpp @@ -15,11 +15,6 @@ void Settings::CreateOptionDescriptions() { "\n" "Off - Mido no longer blocks the path to the Deku Tree. Kokiri " "boy no longer blocks the path out of the forest."; - mOptionDescriptions[RSK_KAK_GATE] = "Closed - The gate will remain closed until Zelda's Letter " - "is shown to the guard.\n" - "\n" - "Open - The gate is always open. The Happy Mask Shop " - "will open immediately after obtaining Zelda's Letter."; mOptionDescriptions[RSK_DOOR_OF_TIME] = "Closed - The Ocarina of Time, the Song of Time and all " "three Spiritual Stones are required to open the Door of Time.\n" "\n" @@ -274,15 +269,20 @@ void Settings::CreateOptionDescriptions() { "Shuffle ability to speak to NPCs. 6 jabbernuts will be shuffled:\nDeku, Gerudo, Goron, Hylian, Kokiri, " "Zora\nKaepora Gaebora speaks any language."; mOptionDescriptions[RSK_SHUFFLE_OPEN_CHEST] = "Shuffles the ability to open chests into the item pool."; - mOptionDescriptions[RSK_SHUFFLE_WEIRD_EGG] = "Shuffles the Weird Egg from Malon in to the item pool. Enabling " - "\"Skip Child Zelda\" disables this feature.\n" - "\n" - "The Weird Egg is required to unlock several events:\n" - " - Zelda's Lullaby from Impa\n" - " - Saria's Song in Sacred Forest Meadow\n" - " - Epona's Song and chicken minigame at Lon Lon Ranch\n" - " - Zelda's Letter for Kakariko gate (if set to closed)\n" - " - Happy Mask Shop sidequest\n"; + mOptionDescriptions[RSK_SHUFFLE_WEIRD_EGG] = + "Vanilla: Malon gives the Weird Egg at Hyrule Castle.\n" + "\n" + "Shuffled: shuffles Weird Egg into item pool.\n" + "\n" + "Skip Waking Talon: Talon already woken and back at Lon Lon Ranch with Malon."; + mOptionDescriptions[RSK_SHUFFLE_ZELDAS_LETTER] = + "Shuffles Zelda's Letter into the item pool, meeting Zelda gives a random item instead.\n" + "\n" + "Required to open Kakariko gate and Happy Mask Shop. Starting with letter starts with gate opened.\n" + "\n" + "Meeting Zelda still triggers Saria at Sacred Forest Meadow.\n" + "\n" + "When disabled, \"Start with Zelda's Letter\" skips child Zelda: you also get item Impa would give."; mOptionDescriptions[RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD] = "Shuffles the Gerudo Membership Card into the item pool.\n" "\n" @@ -685,9 +685,6 @@ void Settings::CreateOptionDescriptions() { mOptionDescriptions[RSK_BIG_POE_COUNT] = "The Poe collector will give a reward for turning in this many Big Poes."; mOptionDescriptions[RSK_SKIP_CHILD_STEALTH] = "The crawlspace into Hyrule Castle goes straight to Zelda, skipping the guards."; - mOptionDescriptions[RSK_SKIP_CHILD_ZELDA] = - "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_MASK_QUEST] = "How masks are acquired.\n" diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index 10a81015e6..bf2fe2ecd9 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -1094,6 +1094,10 @@ void Randomizer_GameplayStats_SetTimestamp(uint16_t item) { timestampItem = ITEM_DOUBLE_DEFENSE; } else if (item >= RG_KEATON_MASK && item <= RG_MASK_OF_TRUTH) { timestampItem = ITEM_MASK_KEATON + (item - RG_KEATON_MASK); + } else if (item == RG_WEIRD_EGG) { + timestampItem = ITEM_WEIRD_EGG; + } else if (item == RG_ZELDAS_LETTER) { + timestampItem = ITEM_LETTER_ZELDA; } else if (randomizerGetToStatsTimeStamp.contains((RandomizerGet)item)) { timestampItem = randomizerGetToStatsTimeStamp[(RandomizerGet)item]; } @@ -1105,6 +1109,16 @@ void Randomizer_GameplayStats_SetTimestamp(uint16_t item) { extern "C" u8 Return_Item_Entry(GetItemEntry itemEntry, u8 returnItem); +// The child trade slot can be displaced (e.g. chicken consumed waking Talon, +// letter shown to the guard), leaving an item there the player no longer owns. +static bool ChildTradeSlotOccupied() { + u8 slotItem = INV_CONTENT(ITEM_TRADE_CHILD); + if (slotItem < ITEM_WEIRD_EGG || slotItem > ITEM_MASK_TRUTH) { + return false; + } + return Flags_GetRandomizerInf((RandomizerInf)(slotItem - ITEM_WEIRD_EGG + RAND_INF_CHILD_TRADES_HAS_WEIRD_EGG)); +} + extern "C" u16 Randomizer_Item_Give(PlayState* play, GetItemEntry giEntry) { if (giEntry.modIndex != MOD_RANDOMIZER) { LUSLOG_WARN( @@ -1136,9 +1150,19 @@ extern "C" u16 Randomizer_Item_Give(PlayState* play, GetItemEntry giEntry) { gSaveContext.inventory.dungeonKeys[SCENE_THIEVES_HIDEOUT] = GERUDO_FORTRESS_SMALL_KEY_MAX; gSaveContext.inventory.dungeonKeys[SCENE_INSIDE_GANONS_CASTLE] = GANONS_CASTLE_SMALL_KEY_MAX; } else if (item >= RG_KEATON_MASK && item <= RG_MASK_OF_TRUTH) { - if (INV_CONTENT(ITEM_TRADE_CHILD) == ITEM_NONE) { + if (!ChildTradeSlotOccupied()) { INV_CONTENT(ITEM_TRADE_CHILD) = (int)ITEM_MASK_KEATON + (item - RG_KEATON_MASK); } + } else if (item == RG_WEIRD_EGG) { + Flags_SetRandomizerInf(RAND_INF_CHILD_TRADES_HAS_WEIRD_EGG); + if (!ChildTradeSlotOccupied()) { + INV_CONTENT(ITEM_TRADE_CHILD) = ITEM_WEIRD_EGG; + } + } else if (item == RG_ZELDAS_LETTER) { + Flags_SetRandomizerInf(RAND_INF_CHILD_TRADES_HAS_LETTER_ZELDA); + if (!ChildTradeSlotOccupied()) { + INV_CONTENT(ITEM_TRADE_CHILD) = ITEM_LETTER_ZELDA; + } } else if (item == RG_CHILD_WALLET && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_FULL_WALLETS)) { Rupees_ChangeBy(99); diff --git a/soh/soh/Enhancements/randomizer/randomizerEnums/LogicVal.h b/soh/soh/Enhancements/randomizer/randomizerEnums/LogicVal.h index bb8dc4e829..d49aa7e799 100644 --- a/soh/soh/Enhancements/randomizer/randomizerEnums/LogicVal.h +++ b/soh/soh/Enhancements/randomizer/randomizerEnums/LogicVal.h @@ -312,6 +312,9 @@ RANDO_ENUM_ITEM(LOGIC_SHADOW_MQ_SWITCH_ACROSS_CHASM) RANDO_ENUM_ITEM(LOGIC_SHADOW_MQ_EYE_SWITCH_ACROSS_CHASM) RANDO_ENUM_ITEM(LOGIC_WAKE_UP_ADULT_TALON) RANDO_ENUM_ITEM(LOGIC_KAKARIKO_GATE_OPEN) +RANDO_ENUM_ITEM(LOGIC_MET_ZELDA) +RANDO_ENUM_ITEM(LOGIC_MALON_RETURNED_FROM_CASTLE) +RANDO_ENUM_ITEM(LOGIC_TALON_RETURNED_FROM_CASTLE) RANDO_ENUM_ITEM(LOGIC_DELIVER_RUTOS_LETTER) RANDO_ENUM_ITEM(LOGIC_KING_ZORA_THAWED) RANDO_ENUM_ITEM(LOGIC_LINKS_COW) diff --git a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerOptions.h b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerOptions.h index cd0aeb6a03..ea255d0c46 100644 --- a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerOptions.h +++ b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerOptions.h @@ -77,12 +77,6 @@ RANDO_ENUM_ITEM(RO_GF_CARPENTERS_FAST) RANDO_ENUM_ITEM(RO_GF_CARPENTERS_FREE) RANDO_ENUM_END(RandoOptionGerudoFortress) -// Kakariko Gate settings (closed/open) -RANDO_ENUM_BEGIN(RandoOptionKakarikoGate) -RANDO_ENUM_ITEM(RO_KAK_GATE_CLOSED) -RANDO_ENUM_ITEM(RO_KAK_GATE_OPEN) -RANDO_ENUM_END(RandoOptionKakarikoGate) - // Rainbow Bridge settings (vanilla, always open, stones, medallions, dungeon rewards, dungeons, tokens) RANDO_ENUM_BEGIN(RandoOptionRainbowBridge) RANDO_ENUM_ITEM(RO_BRIDGE_VANILLA) @@ -313,6 +307,13 @@ RANDO_ENUM_ITEM(RO_SHUFFLE_MERCHANTS_ALL_BUT_BEANS) RANDO_ENUM_ITEM(RO_SHUFFLE_MERCHANTS_ALL) RANDO_ENUM_END(RandoOptionShuffleMerchants) +// Shuffle Weird Egg Settings (vanilla, shuffled, skip waking talon) +RANDO_ENUM_BEGIN(RandoOptionShuffleWeirdEgg) +RANDO_ENUM_ITEM(RO_WEIRD_EGG_VANILLA) +RANDO_ENUM_ITEM(RO_WEIRD_EGG_SHUFFLED) +RANDO_ENUM_ITEM(RO_WEIRD_EGG_SKIP_TALON) +RANDO_ENUM_END(RandoOptionShuffleWeirdEgg) + // Starting Ocarina Settings (off, fairy, oot) RANDO_ENUM_BEGIN(RandoOptionStartingOcarina) RANDO_ENUM_ITEM(RO_STARTING_OCARINA_OFF) diff --git a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerSettingKey.h b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerSettingKey.h index 06d2b8bd66..700a5afd74 100644 --- a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerSettingKey.h +++ b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerSettingKey.h @@ -15,7 +15,6 @@ RANDO_ENUM_BEGIN(RandomizerSettingKey) RANDO_ENUM_ITEM(RSK_NONE) RANDO_ENUM_ITEM(RSK_LOGIC_RULES) RANDO_ENUM_ITEM(RSK_FOREST) -RANDO_ENUM_ITEM(RSK_KAK_GATE) RANDO_ENUM_ITEM(RSK_DOOR_OF_TIME) RANDO_ENUM_ITEM(RSK_ZORAS_FOUNTAIN) RANDO_ENUM_ITEM(RSK_SLEEPING_WATERFALL) @@ -93,6 +92,7 @@ RANDO_ENUM_ITEM(RSK_SCRUBS_PRICES_AFFORDABLE) RANDO_ENUM_ITEM(RSK_SHUFFLE_BEEHIVES) RANDO_ENUM_ITEM(RSK_SHUFFLE_COWS) RANDO_ENUM_ITEM(RSK_SHUFFLE_WEIRD_EGG) +RANDO_ENUM_ITEM(RSK_SHUFFLE_ZELDAS_LETTER) RANDO_ENUM_ITEM(RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD) RANDO_ENUM_ITEM(RSK_SHUFFLE_POTS) RANDO_ENUM_ITEM(RSK_SHUFFLE_CRATES) @@ -142,7 +142,6 @@ RANDO_ENUM_ITEM(RSK_BOSS_KEYSANITY) RANDO_ENUM_ITEM(RSK_GANONS_BOSS_KEY) RANDO_ENUM_ITEM(RSK_GANONS_SOUL) RANDO_ENUM_ITEM(RSK_SKIP_CHILD_STEALTH) -RANDO_ENUM_ITEM(RSK_SKIP_CHILD_ZELDA) RANDO_ENUM_ITEM(RSK_STARTING_STICKS) RANDO_ENUM_ITEM(RSK_STARTING_NUTS) RANDO_ENUM_ITEM(RSK_STARTING_BEANS) @@ -176,6 +175,7 @@ RANDO_ENUM_ITEM(RSK_STARTING_BOTTLE_2) RANDO_ENUM_ITEM(RSK_STARTING_BOTTLE_3) RANDO_ENUM_ITEM(RSK_STARTING_BOTTLE_4) RANDO_ENUM_ITEM(RSK_STARTING_WEIRD_EGG) +RANDO_ENUM_ITEM(RSK_STARTING_ZELDAS_LETTER) RANDO_ENUM_ITEM(RSK_STARTING_CLAIM_CHECK) RANDO_ENUM_ITEM(RSK_STARTING_GERUDO_CARD) RANDO_ENUM_ITEM(RSK_STARTING_BIGGORON_SWORD) diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_objects.cpp b/soh/soh/Enhancements/randomizer/randomizer_check_objects.cpp index b90d8c62b2..44b9e75d3a 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_check_objects.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_check_objects.cpp @@ -154,7 +154,8 @@ void RandomizerCheckObjects::UpdateImGuiVisibility() { RO_DUNGEON_REWARDS_END_OF_DUNGEON) && // dungeon rewards end of dungeons (location.GetRCType() != RCTYPE_OCARINA || CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleOcarinas"), RO_GENERIC_NO)) && // ocarina locations - (location.GetRandomizerCheck() != RC_HC_ZELDAS_LETTER) && // don't show until we support shuffling letter + (location.GetRandomizerCheck() != RC_HC_ZELDAS_LETTER || + CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleZeldasLetter"), RO_GENERIC_NO)) && (location.GetRCType() != 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 @@ -215,7 +216,8 @@ void RandomizerCheckObjects::UpdateImGuiVisibility() { (location.GetRandomizerCheck() != RC_ZR_MAGIC_BEAN_SALESMAN || CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleMerchants"), RO_SHUFFLE_MERCHANTS_OFF) % 2) && (location.GetRandomizerCheck() != RC_HC_MALON_EGG || - CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleWeirdEgg"), RO_GENERIC_NO)) && + CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleWeirdEgg"), RO_WEIRD_EGG_VANILLA) == + RO_WEIRD_EGG_SHUFFLED) && (location.GetRCType() != RCTYPE_FROG_SONG || CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleFrogSongRupees"), RO_GENERIC_NO)) && (location.GetRCType() != RCTYPE_FOUNTAIN_FAIRY || diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp index 4991a861ce..30a23568ea 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp @@ -71,6 +71,7 @@ bool showKokiriSword; bool showMasterSword; bool showHyruleLoach; bool showWeirdEgg; +bool showZeldasLetter; bool showGerudoCard; bool showOverworldPots; bool showDungeonPots; @@ -1373,8 +1374,11 @@ void LoadSettings() { 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_GENERIC_YES + 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 @@ -1665,8 +1669,7 @@ bool IsCheckShuffled(RandomizerCheck rc) { (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 - (rc != RC_HC_ZELDAS_LETTER) && // don't show zeldas letter until we support shuffling it - (rc != RC_LINKS_POCKET || showLinksPocket) && + (rc != RC_HC_ZELDAS_LETTER || showZeldasLetter) && (rc != RC_LINKS_POCKET || showLinksPocket) && OTRGlobals::Instance->gRandoContext->IsQuestOfLocationActive(rc) && (loc->GetRCType() != RCTYPE_SHOP || (showShops && diff --git a/soh/soh/Enhancements/randomizer/savefile.cpp b/soh/soh/Enhancements/randomizer/savefile.cpp index 8b82b88340..a54a31c5b6 100644 --- a/soh/soh/Enhancements/randomizer/savefile.cpp +++ b/soh/soh/Enhancements/randomizer/savefile.cpp @@ -336,9 +336,14 @@ void SetStartingItems() { Item_Give(NULL, ITEM_BOTTLE); } - if (Randomizer_GetSettingValue(RSK_STARTING_WEIRD_EGG) && Randomizer_GetSettingValue(RSK_SHUFFLE_WEIRD_EGG)) { + if (Randomizer_GetSettingValue(RSK_STARTING_WEIRD_EGG) && + Randomizer_GetSettingValue(RSK_SHUFFLE_WEIRD_EGG) == RO_WEIRD_EGG_SHUFFLED) { Item_Give(NULL, ITEM_WEIRD_EGG); } + if (Randomizer_GetSettingValue(RSK_STARTING_ZELDAS_LETTER) && + Randomizer_GetSettingValue(RSK_SHUFFLE_ZELDAS_LETTER)) { + Item_Give(NULL, ITEM_LETTER_ZELDA); + } if (Randomizer_GetSettingValue(RSK_STARTING_CLAIM_CHECK)) { Item_Give(NULL, ITEM_CLAIM_CHECK); } @@ -571,33 +576,37 @@ extern "C" void Randomizer_InitSaveFile() { } } - if (Randomizer_GetSettingValue(RSK_SKIP_CHILD_ZELDA)) { - GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(RC_SONG_FROM_IMPA, (GetItemID)RG_ZELDAS_LULLABY); - StartingItemGive(getItemEntry, RC_SONG_FROM_IMPA); - getItemEntry = Randomizer_GetItemFromKnownCheck(RC_HC_MALON_EGG, (GetItemID)RG_WEIRD_EGG); - StartingItemGive(getItemEntry, RC_HC_MALON_EGG); - getItemEntry = Randomizer_GetItemFromKnownCheck(RC_HC_ZELDAS_LETTER, (GetItemID)RG_ZELDAS_LETTER); - StartingItemGive(getItemEntry, RC_HC_ZELDAS_LETTER); + // Skip Waking Talon: the egg already hatched and woke him, Malon/Talon start back at the ranch. + if (Randomizer_GetSettingValue(RSK_SHUFFLE_WEIRD_EGG) == RO_WEIRD_EGG_SKIP_TALON) { + OTRGlobals::Instance->gRandoContext->GetItemLocation(RC_HC_MALON_EGG)->SetCheckStatus(RCSHOW_SAVED); - // Malon/Talon back at ranch. Flags_SetEventChkInf(EVENTCHKINF_OBTAINED_POCKET_EGG); Flags_SetRandomizerInf(RAND_INF_WEIRD_EGG); Flags_SetEventChkInf(EVENTCHKINF_TALON_WOKEN_IN_CASTLE); Flags_SetEventChkInf(EVENTCHKINF_TALON_RETURNED_FROM_CASTLE); + } - // Set "Got Zelda's Letter" flag. Also ensures Saria is back at SFM. + // Starting with an unshuffled letter skips child Zelda. + if (Randomizer_GetSettingValue(RSK_STARTING_ZELDAS_LETTER) && + !Randomizer_GetSettingValue(RSK_SHUFFLE_ZELDAS_LETTER)) { + GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(RC_SONG_FROM_IMPA, (GetItemID)RG_ZELDAS_LULLABY); + StartingItemGive(getItemEntry, RC_SONG_FROM_IMPA); + getItemEntry = Randomizer_GetItemFromKnownCheck(RC_HC_ZELDAS_LETTER, (GetItemID)RG_ZELDAS_LETTER); + StartingItemGive(getItemEntry, RC_HC_ZELDAS_LETTER); + + // Set "Met Zelda" flag. Also ensures Saria is back at SFM. Flags_SetEventChkInf(EVENTCHKINF_OBTAINED_ZELDAS_LETTER); Flags_SetRandomizerInf(RAND_INF_ZELDAS_LETTER); - Flags_SetRandomizerInf(RAND_INF_CHILD_TRADES_HAS_LETTER_ZELDA); // Got item from Impa. Flags_SetEventChkInf(EVENTCHKINF_LEARNED_ZELDAS_LULLABY); + } - gSaveContext.sceneFlags[SCENE_HYRULE_CASTLE].swch |= (1 << 0x4); // Move milk crates in Hyrule Castle to moat. - - // Set this at the end to ensure we always start with the letter. - // This is for the off chance, we got the Weird Egg from Impa (which should never happen). - INV_CONTENT(ITEM_LETTER_ZELDA) = ITEM_LETTER_ZELDA; + // Starting with the letter opens the Kakariko gate, shuffled or not. + // The letter then has no use, so drop it from the trade cycle. + if (Randomizer_GetSettingValue(RSK_STARTING_ZELDAS_LETTER)) { + Flags_SetInfTable(INFTABLE_SHOWED_ZELDAS_LETTER_TO_GATE_GUARD); + Flags_UnsetRandomizerInf(RAND_INF_CHILD_TRADES_HAS_LETTER_ZELDA); } if (Randomizer_GetSettingValue(RSK_SHUFFLE_MASTER_SWORD) && startingAge == RO_AGE_ADULT) { @@ -631,11 +640,6 @@ extern "C" void Randomizer_InitSaveFile() { break; } - if (Randomizer_GetSettingValue(RSK_KAK_GATE) == RO_KAK_GATE_OPEN) { - Flags_SetInfTable(INFTABLE_SHOWED_ZELDAS_LETTER_TO_GATE_GUARD); - Flags_UnsetRandomizerInf(RAND_INF_CHILD_TRADES_HAS_LETTER_ZELDA); - } - if (Randomizer_GetSettingValue(RSK_GERUDO_FORTRESS) == RO_GF_CARPENTERS_FAST || Randomizer_GetSettingValue(RSK_GERUDO_FORTRESS) == RO_GF_CARPENTERS_FREE) { Flags_SetEventChkInf(EVENTCHKINF_CARPENTERS_FREE(1)); diff --git a/soh/soh/Enhancements/randomizer/settings.cpp b/soh/soh/Enhancements/randomizer/settings.cpp index fb59b2c370..eba220edad 100644 --- a/soh/soh/Enhancements/randomizer/settings.cpp +++ b/soh/soh/Enhancements/randomizer/settings.cpp @@ -165,7 +165,6 @@ void Settings::CreateOptions() { OPT_CALLBACK(RSK_FOREST, { HandleStartingAgeUI(); }); - OPT_U8(RSK_KAK_GATE, "Kakariko Gate", {"Closed", "Open"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("KakarikoGate"), mOptionDescriptions[RSK_KAK_GATE]); OPT_U8(RSK_DOOR_OF_TIME, "Door of Time", {"Closed", "Song only", "Open"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("DoorOfTime"), mOptionDescriptions[RSK_DOOR_OF_TIME], WIDGET_CVAR_COMBOBOX); OPT_CALLBACK(RSK_DOOR_OF_TIME, { HandleStartingAgeUI(); @@ -853,7 +852,8 @@ void Settings::CreateOptions() { }); OPT_BOOL(RSK_SHUFFLE_SPEAK, "Shuffle Jabber Nuts", CVAR_RANDOMIZER_SETTING("ShuffleSpeak"), mOptionDescriptions[RSK_SHUFFLE_SPEAK]); OPT_BOOL(RSK_SHUFFLE_OPEN_CHEST, "Shuffle Open Chest", CVAR_RANDOMIZER_SETTING("ShuffleOpenChest"), mOptionDescriptions[RSK_SHUFFLE_OPEN_CHEST]); - OPT_BOOL(RSK_SHUFFLE_WEIRD_EGG, "Shuffle Weird Egg", CVAR_RANDOMIZER_SETTING("ShuffleWeirdEgg"), mOptionDescriptions[RSK_SHUFFLE_WEIRD_EGG]); + OPT_U8(RSK_SHUFFLE_WEIRD_EGG, "Shuffle Weird Egg", {"Vanilla", "Shuffled", "Skip Waking Talon"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleWeirdEgg"), mOptionDescriptions[RSK_SHUFFLE_WEIRD_EGG], WIDGET_CVAR_COMBOBOX, RO_WEIRD_EGG_VANILLA); + OPT_BOOL(RSK_SHUFFLE_ZELDAS_LETTER, "Shuffle Zelda's Letter", CVAR_RANDOMIZER_SETTING("ShuffleZeldasLetter"), mOptionDescriptions[RSK_SHUFFLE_ZELDAS_LETTER]); OPT_BOOL(RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD, "Shuffle Gerudo Membership Card", CVAR_RANDOMIZER_SETTING("ShuffleGerudoToken"), mOptionDescriptions[RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD]); OPT_U8(RSK_SHUFFLE_POTS, "Shuffle Pots", {"Off", "Dungeons", "Overworld", "All Pots"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShufflePots"), mOptionDescriptions[RSK_SHUFFLE_POTS], WIDGET_CVAR_COMBOBOX, RO_SHUFFLE_POTS_OFF); OPT_U8(RSK_SHUFFLE_GRASS, "Shuffle Grass", {"Off", "Dungeons", "Overworld", "All Grass"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleGrass"), mOptionDescriptions[RSK_SHUFFLE_GRASS], WIDGET_CVAR_COMBOBOX, RO_SHUFFLE_GRASS_OFF); @@ -1345,17 +1345,6 @@ void Settings::CreateOptions() { //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); - OPT_BOOL(RSK_SKIP_CHILD_ZELDA, "Skip Child Zelda", {"Don't Skip", "Skip"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("SkipChildZelda"), mOptionDescriptions[RSK_SKIP_CHILD_ZELDA], WIDGET_CVAR_CHECKBOX, RO_GENERIC_DONT_SKIP); - OPT_CALLBACK(RSK_SKIP_CHILD_ZELDA, { - // Shuffle Weird Egg - Disabled when Skip Child Zelda is active - if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("SkipChildZelda"), RO_GENERIC_DONT_SKIP)) { - mOptions[RSK_SHUFFLE_WEIRD_EGG].Disable("This option is disabled because \"Skip Child Zelda\" is enabled."); - mOptions[RSK_SKIP_CHILD_STEALTH].Disable("This option is disabled because \"Skip Child Zelda\" is enabled."); - } else { - mOptions[RSK_SHUFFLE_WEIRD_EGG].Enable(); - mOptions[RSK_SKIP_CHILD_STEALTH].Enable(); - } - }); OPT_BOOL(RSK_EARLY_GRANNYS_SHOP, "Early Granny's Potion Shop", CVAR_RANDOMIZER_SETTING("EarlyGrannysShop"), mOptionDescriptions[RSK_EARLY_GRANNYS_SHOP]); OPT_BOOL(RSK_SKIP_EPONA_RACE, "Skip Epona Race", {"Don't Skip", "Skip"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("SkipEponaRace"), mOptionDescriptions[RSK_SKIP_EPONA_RACE], WIDGET_CVAR_CHECKBOX, RO_GENERIC_DONT_SKIP); OPT_BOOL(RSK_SKIP_SCARECROWS_SONG, "Skip Scarecrow's Song", CVAR_RANDOMIZER_SETTING("SkipScarecrowsSong"), mOptionDescriptions[RSK_SKIP_SCARECROWS_SONG]); @@ -1458,6 +1447,7 @@ void Settings::CreateOptions() { OPT_U8(RSK_STARTING_BOTTLE_3, "Starting Bottle 3", {"Off", "Empty Bottle", "Bottle with Big Poe"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("StartingBottle3"), "", WIDGET_CVAR_COMBOBOX, 0); OPT_U8(RSK_STARTING_BOTTLE_4, "Starting Bottle 4", {"Off", "Empty Bottle", "Bottle with Big Poe"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("StartingBottle4"), "", WIDGET_CVAR_COMBOBOX, 0); OPT_BOOL(RSK_STARTING_WEIRD_EGG, "Start with Weird Egg", CVAR_RANDOMIZER_SETTING("StartingWeirdEgg")); + OPT_BOOL(RSK_STARTING_ZELDAS_LETTER, "Start with Zelda's Letter", CVAR_RANDOMIZER_SETTING("StartingZeldasLetter")); OPT_BOOL(RSK_STARTING_CLAIM_CHECK, "Start with Claim Check", CVAR_RANDOMIZER_SETTING("StartingClaimCheck")); OPT_BOOL(RSK_STARTING_GERUDO_CARD, "Start with Gerudo Card", CVAR_RANDOMIZER_SETTING("StartingGerudoCard")); OPT_BOOL(RSK_STARTING_BUNNY_HOOD, "Start with Bunny Hood", CVAR_RANDOMIZER_SETTING("StartingBunnyHood")); @@ -1887,7 +1877,6 @@ void Settings::CreateOptions() { &mOptions[RSK_FULL_WALLETS], &mOptions[RSK_SLINGBOW_BREAK_BEEHIVES], &mOptions[RSK_SWORDLESS_EPONA_ITEMS], - &mOptions[RSK_SKIP_CHILD_ZELDA], &mOptions[RSK_MASK_QUEST], &mOptions[RSK_SKIP_CHILD_STEALTH], &mOptions[RSK_EARLY_GRANNYS_SHOP], @@ -1935,7 +1924,6 @@ void Settings::CreateOptions() { OptionGroup::SubGroup("Area Access", { &mOptions[RSK_FOREST], - &mOptions[RSK_KAK_GATE], &mOptions[RSK_DOOR_OF_TIME], &mOptions[RSK_ZORAS_FOUNTAIN], &mOptions[RSK_SLEEPING_WATERFALL], @@ -2035,6 +2023,7 @@ void Settings::CreateOptions() { &mOptions[RSK_SHUFFLE_MASTER_SWORD], &mOptions[RSK_SHUFFLE_OCARINA], &mOptions[RSK_SHUFFLE_WEIRD_EGG], + &mOptions[RSK_SHUFFLE_ZELDAS_LETTER], &mOptions[RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD], &mOptions[RSK_FISHSANITY], &mOptions[RSK_FISHSANITY_POND_COUNT], @@ -2186,7 +2175,6 @@ void Settings::CreateOptions() { WidgetContainerType::TABLE); mOptionGroups[RSG_OPEN] = OptionGroup("Open Settings", { &mOptions[RSK_FOREST], - &mOptions[RSK_KAK_GATE], &mOptions[RSK_DOOR_OF_TIME], &mOptions[RSK_ZORAS_FOUNTAIN], &mOptions[RSK_SLEEPING_WATERFALL], @@ -2296,6 +2284,7 @@ void Settings::CreateOptions() { &mOptions[RSK_SHUFFLE_SPEAK], &mOptions[RSK_SHUFFLE_OPEN_CHEST], &mOptions[RSK_SHUFFLE_WEIRD_EGG], + &mOptions[RSK_SHUFFLE_ZELDAS_LETTER], &mOptions[RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD], &mOptions[RSK_SHUFFLE_MERCHANTS], &mOptions[RSK_MERCHANT_PRICES], @@ -2386,8 +2375,9 @@ void Settings::CreateOptions() { &mOptions[RSK_STARTING_BOMBCHU_BAG], &mOptions[RSK_STARTING_BOTTLE_1], &mOptions[RSK_STARTING_BOTTLE_2], &mOptions[RSK_STARTING_BOTTLE_3], &mOptions[RSK_STARTING_BOTTLE_4], &mOptions[RSK_STARTING_WEIRD_EGG], - &mOptions[RSK_STARTING_CLAIM_CHECK], &mOptions[RSK_STARTING_GERUDO_CARD], - &mOptions[RSK_STARTING_BIGGORON_SWORD], &mOptions[RSK_STARTING_BUNNY_HOOD] }); + &mOptions[RSK_STARTING_ZELDAS_LETTER], &mOptions[RSK_STARTING_CLAIM_CHECK], + &mOptions[RSK_STARTING_GERUDO_CARD], &mOptions[RSK_STARTING_BIGGORON_SWORD], + &mOptions[RSK_STARTING_BUNNY_HOOD] }); mOptionGroups[RSG_STARTING_SONGS] = OptionGroup::SubGroup("Ocarina Songs", { &mOptions[RSK_STARTING_ZELDAS_LULLABY], @@ -2419,7 +2409,6 @@ void Settings::CreateOptions() { }, OptionGroupType::DEFAULT); mOptionGroups[RSG_TIMESAVERS] = OptionGroup("Timesaver Settings", { - &mOptions[RSK_SKIP_CHILD_ZELDA], &mOptions[RSK_SKIP_EPONA_RACE], &mOptions[RSK_SKIP_SCARECROWS_SONG], &mOptions[RSK_SKIP_PLANTING_BEANS], @@ -2629,12 +2618,6 @@ void Settings::UpdateAllOptions() { void Context::FinalizeSettings(const std::set& excludedLocations, const std::set& enabledTricks) { - // if we skip child zelda, we start with zelda's letter, and malon starts - // at the ranch, so we should *not* shuffle the weird egg - if (mOptions[RSK_SKIP_CHILD_ZELDA]) { - mOptions[RSK_SHUFFLE_WEIRD_EGG].Set(RO_GENERIC_OFF); - } - // With certain access settings, the seed is only beatable if Starting Age is set to Child. if (mOptions[RSK_LOGIC_RULES].IsNot(RO_LOGIC_NO_LOGIC) && ((mOptions[RSK_DOOR_OF_TIME].Is(RO_DOOROFTIME_CLOSED) && !mOptions[RSK_SHUFFLE_OCARINA]) || @@ -3164,6 +3147,7 @@ void Settings::RandomizeAllSettings() { case RSK_STARTING_BOTTLE_3: case RSK_STARTING_BOTTLE_4: case RSK_STARTING_WEIRD_EGG: + case RSK_STARTING_ZELDAS_LETTER: case RSK_STARTING_CLAIM_CHECK: case RSK_STARTING_GERUDO_CARD: case RSK_STARTING_BIGGORON_SWORD: diff --git a/soh/soh/SohGui/SohMenuStartingItems.cpp b/soh/soh/SohGui/SohMenuStartingItems.cpp index 50d6bc8fc3..caa60ed094 100644 --- a/soh/soh/SohGui/SohMenuStartingItems.cpp +++ b/soh/soh/SohGui/SohMenuStartingItems.cpp @@ -290,7 +290,8 @@ void DrawStartingItemsMenu(WidgetInfo& info) { StartingItemToggle(RSK_STARTING_CLAIM_CHECK, ITEM_CLAIM_CHECK); ImGui::SameLine(); bool weirdEggShuffled = - CVarGetInteger(Rando::Settings::GetInstance()->GetOption(RSK_SHUFFLE_WEIRD_EGG).GetCVarName().c_str(), 0) != 0; + CVarGetInteger(Rando::Settings::GetInstance()->GetOption(RSK_SHUFFLE_WEIRD_EGG).GetCVarName().c_str(), + RO_WEIRD_EGG_VANILLA) == RO_WEIRD_EGG_SHUFFLED; ImGui::BeginDisabled(!weirdEggShuffled); StartingItemToggle(RSK_STARTING_WEIRD_EGG, ITEM_WEIRD_EGG, !weirdEggShuffled); ImGui::EndDisabled(); @@ -298,6 +299,22 @@ void DrawStartingItemsMenu(WidgetInfo& info) { ImGui::SetTooltip("Enable Shuffle Weird Egg to start with the Weird Egg."); } ImGui::SameLine(); + bool zeldasLetterShuffled = + CVarGetInteger(Rando::Settings::GetInstance()->GetOption(RSK_SHUFFLE_ZELDAS_LETTER).GetCVarName().c_str(), 0) != + 0; + StartingItemToggle(RSK_STARTING_ZELDAS_LETTER, ITEM_LETTER_ZELDA); + if (ImGui::IsItemHovered()) { + if (zeldasLetterShuffled) { + ImGui::SetTooltip("Start with Zelda's Letter\n\n" + "The Kakariko gate starts open."); + } else { + ImGui::SetTooltip("Start with Zelda's Letter\n\n" + "The Kakariko gate starts open.\n" + "While the letter isn't shuffled this also skips child Zelda:\n" + "you get the item Impa would give and skip everything up to meeting Zelda."); + } + } + ImGui::SameLine(); StartingItemToggle(RSK_STARTING_BUNNY_HOOD, ITEM_MASK_BUNNY); ImGui::SeparatorText("Songs"); diff --git a/soh/soh/config/ConfigUpdaters.cpp b/soh/soh/config/ConfigUpdaters.cpp index 9034b187e1..996f064adc 100644 --- a/soh/soh/config/ConfigUpdaters.cpp +++ b/soh/soh/config/ConfigUpdaters.cpp @@ -1659,5 +1659,15 @@ void ConfigVersion7Updater::Update(Ship::Config* conf) { CVarSetInteger("gRandoSettings.ShuffleBossSouls", RO_BOSS_SOULS_ON); CVarSetInteger("gRandoSettings.ShuffleGanonsSoul", RO_GANONS_SOUL_ANYWHERE); } + + // Skip Child Zelda split into starting with unshuffled Zelda's Letter & Skip Waking Talon + if (CVarGetInteger("gRandoSettings.SkipChildZelda", 0)) { + CVarSetInteger("gRandoSettings.StartingZeldasLetter", 1); + CVarSetInteger("gRandoSettings.ShuffleWeirdEgg", RO_WEIRD_EGG_SKIP_TALON); + } + CVarClear("gRandoSettings.SkipChildZelda"); + + // Kakariko Gate setting removed; the gate opens when starting with an unshuffled letter + CVarClear("gRandoSettings.KakarikoGate"); } } // namespace SOH diff --git a/soh/src/code/z_parameter.c b/soh/src/code/z_parameter.c index 59318ac5b9..8196faef9d 100644 --- a/soh/src/code/z_parameter.c +++ b/soh/src/code/z_parameter.c @@ -2410,6 +2410,7 @@ u8 Item_Give(PlayState* play, u8 item) { if (item >= ITEM_POCKET_EGG) { Flags_SetRandomizerInf(item - ITEM_POCKET_EGG + RAND_INF_ADULT_TRADES_HAS_POCKET_EGG); } else if (item == ITEM_LETTER_ZELDA) { + Flags_SetRandomizerInf(RAND_INF_ZELDAS_LETTER); // don't care about zelda's letter if it's already been shown to the guard if (!Flags_GetInfTable(INFTABLE_SHOWED_ZELDAS_LETTER_TO_GATE_GUARD)) { Flags_SetRandomizerInf(RAND_INF_CHILD_TRADES_HAS_LETTER_ZELDA); diff --git a/soh/src/overlays/actors/ovl_Bg_Gate_Shutter/z_bg_gate_shutter.c b/soh/src/overlays/actors/ovl_Bg_Gate_Shutter/z_bg_gate_shutter.c index b3c38827fd..e3e52b2258 100644 --- a/soh/src/overlays/actors/ovl_Bg_Gate_Shutter/z_bg_gate_shutter.c +++ b/soh/src/overlays/actors/ovl_Bg_Gate_Shutter/z_bg_gate_shutter.c @@ -46,8 +46,7 @@ void BgGateShutter_Init(Actor* thisx, PlayState* play) { this->somePos.y = thisx->world.pos.y; this->somePos.z = thisx->world.pos.z; if (((Flags_GetInfTable(INFTABLE_SHOWED_ZELDAS_LETTER_TO_GATE_GUARD)) || - (!IS_RANDO && (Flags_GetEventChkInf(EVENTCHKINF_PULLED_MASTER_SWORD_FROM_PEDESTAL))) || - (IS_RANDO && (Randomizer_GetSettingValue(RSK_KAK_GATE) == RO_KAK_GATE_OPEN))) && + (!IS_RANDO && (Flags_GetEventChkInf(EVENTCHKINF_PULLED_MASTER_SWORD_FROM_PEDESTAL)))) && (play->sceneNum == SCENE_KAKARIKO_VILLAGE)) { thisx->world.pos.x = -89.0f; thisx->world.pos.z = -1375.0f; diff --git a/soh/src/overlays/actors/ovl_En_Ossan/z_en_ossan.c b/soh/src/overlays/actors/ovl_En_Ossan/z_en_ossan.c index 9138c5c1c2..c92e3c9957 100644 --- a/soh/src/overlays/actors/ovl_En_Ossan/z_en_ossan.c +++ b/soh/src/overlays/actors/ovl_En_Ossan/z_en_ossan.c @@ -606,10 +606,7 @@ void EnOssan_Init(Actor* thisx, PlayState* play) { } // If you haven't given Zelda's Letter to the Kakariko Guard - // or are rando'd and haven't gotten gotten the letter from zelda yet - if (this->actor.params == OSSAN_TYPE_MASK && - (!Flags_GetInfTable(INFTABLE_SHOWED_ZELDAS_LETTER_TO_GATE_GUARD) || - (IS_RANDO && !Flags_GetEventChkInf(EVENTCHKINF_OBTAINED_ZELDAS_LETTER)))) { + if (this->actor.params == OSSAN_TYPE_MASK && !Flags_GetInfTable(INFTABLE_SHOWED_ZELDAS_LETTER_TO_GATE_GUARD)) { Actor_Kill(&this->actor); return; } 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 16cfd8742d..a47334a24a 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 @@ -344,7 +344,7 @@ bool CanMaskSelect() { void KaleidoScope_HandleItemCycles(PlayState* play) { // handle the mask select KaleidoScope_HandleItemCycleExtras( - play, SLOT_TRADE_CHILD, CanMaskSelect(), + play, SLOT_TRADE_CHILD, IS_RANDO || CanMaskSelect(), IS_RANDO ? Randomizer_GetPrevChildTradeItem() : (INV_CONTENT(ITEM_TRADE_CHILD) <= ITEM_MASK_KEATON || INV_CONTENT(ITEM_TRADE_CHILD) > ITEM_MASK_TRUTH ? ITEM_MASK_TRUTH @@ -385,7 +385,7 @@ void KaleidoScope_HandleItemCycles(PlayState* play) { void KaleidoScope_DrawItemCycles(PlayState* play) { // draw the mask select KaleidoScope_DrawItemCycleExtras( - play, SLOT_TRADE_CHILD, CanMaskSelect(), + play, SLOT_TRADE_CHILD, IS_RANDO || CanMaskSelect(), IS_RANDO ? Randomizer_GetPrevChildTradeItem() : (INV_CONTENT(ITEM_TRADE_CHILD) <= ITEM_MASK_KEATON || INV_CONTENT(ITEM_TRADE_CHILD) > ITEM_MASK_TRUTH ? ITEM_MASK_TRUTH