diff options
Diffstat (limited to 'soh')
42 files changed, 207 insertions, 48 deletions
diff --git a/soh/assets/custom/lang/en_US.json b/soh/assets/custom/lang/en_US.json index f018fda10..4e9bf0c42 100644 --- a/soh/assets/custom/lang/en_US.json +++ b/soh/assets/custom/lang/en_US.json @@ -472,6 +472,13 @@ "When disabled, \"Start with Zelda's Letter\" skips child Zelda: you also get the item Impa would give." ] }, + "shuffle_scarecrows_song": { + "name": "Shuffle Scarecrow's Song", + "description": [ + "Shuffles Scarecrow's Song into the item pool. Teaching Bonooru a song as child, then playing it ", + "back to him as adult, gives a random item instead." + ] + }, "shuffle_gerudo_membership_card": { "name": "Shuffle Gerudo Membership Card", "description": [ @@ -1168,14 +1175,6 @@ "name": "Shuffle Masks", "description": "The Happy Mask Shop never opens, masks are shuffled with the rest of the items." }, - "skip_scarecrows_song": { - "name": "Skip Scarecrow's Song", - "description": [ - "Start with the ability to summon Pierre the Scarecrow. Pulling out an Ocarina in the usual locations will ", - "automatically summon him.\n", - "With \"Shuffle Ocarina Buttons\" enabled, you'll need at least two Ocarina buttons to summon him." - ] - }, "skip_planting_beans": { "name": "Skip Planting Beans", "description": [ @@ -1826,6 +1825,9 @@ "starting_zeldas_letter": { "name": "Start with Zelda's Letter" }, + "starting_scarecrows_song": { + "name": "Start with Scarecrow's Song" + }, "starting_claim_check": { "name": "Start with Claim Check" }, diff --git a/soh/assets/custom/presets/Rando Seed Settings - Advanced.json b/soh/assets/custom/presets/Rando Seed Settings - Advanced.json index 88cad1fe5..2ce182484 100644 --- a/soh/assets/custom/presets/Rando Seed Settings - Advanced.json +++ b/soh/assets/custom/presets/Rando Seed Settings - Advanced.json @@ -56,9 +56,9 @@ "ShuffleTokens": 3, "ShuffleWeirdEgg": 2, "SkipEponaRace": 1, - "SkipScarecrowsSong": 1, "StartingAge": 2, "StartingMapsCompasses": 0, + "StartingScarecrowsSong": 1, "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 945ce0f80..22e0eaaf9 100644 --- a/soh/assets/custom/presets/Rando Seed Settings - Beginner.json +++ b/soh/assets/custom/presets/Rando Seed Settings - Beginner.json @@ -36,11 +36,11 @@ "ShuffleOcarinas": 1, "ShuffleWeirdEgg": 2, "SkipEponaRace": 1, - "SkipScarecrowsSong": 1, "StartingKokiriSword": 1, "StartingBunnyHood": 1, "StartingMapsCompasses": 0, "StartingOcarina": 1, + "StartingScarecrowsSong": 1, "StartingZeldasLetter": 1, "SunlightArrows": 1, "ZorasFountain": 1 diff --git a/soh/assets/custom/presets/Rando Seed Settings - Standard.json b/soh/assets/custom/presets/Rando Seed Settings - Standard.json index 2ebfe4f95..d97aaeac9 100644 --- a/soh/assets/custom/presets/Rando Seed Settings - Standard.json +++ b/soh/assets/custom/presets/Rando Seed Settings - Standard.json @@ -49,9 +49,9 @@ "ShuffleTokens": 3, "ShuffleWeirdEgg": 2, "SkipEponaRace": 1, - "SkipScarecrowsSong": 1, "StartingMapsCompasses": 0, "StartingOcarina": 1, + "StartingScarecrowsSong": 1, "StartingZeldasLetter": 1, "SunlightArrows": 1, "ZorasFountain": 1 diff --git a/soh/soh/Enhancements/TimeSavers/timesaver_hook_handlers.cpp b/soh/soh/Enhancements/TimeSavers/timesaver_hook_handlers.cpp index 7b3f150df..5ad585885 100644 --- a/soh/soh/Enhancements/TimeSavers/timesaver_hook_handlers.cpp +++ b/soh/soh/Enhancements/TimeSavers/timesaver_hook_handlers.cpp @@ -896,6 +896,9 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li break; } case VB_SKIP_SCARECROWS_SONG: { + if (IS_RANDO && RAND_GET_OPTION(RSK_SHUFFLE_SCARECROWS_SONG)) { + break; + } if (gPlayState->msgCtx.msgMode == MSGMODE_OCARINA_PLAYING && CVarGetInteger(CVAR_ENHANCEMENT("InstantScarecrow"), 0) && gSaveContext.scarecrowSpawnSongSet) { *should = true; diff --git a/soh/soh/Enhancements/debugger/debugSaveEditor.cpp b/soh/soh/Enhancements/debugger/debugSaveEditor.cpp index 35d8e5d92..36da8b07e 100644 --- a/soh/soh/Enhancements/debugger/debugSaveEditor.cpp +++ b/soh/soh/Enhancements/debugger/debugSaveEditor.cpp @@ -2132,6 +2132,7 @@ void DrawEquipmentTab() { randomizer.GetRandoSettingValue(RSK_SHUFFLE_OPEN_CHEST) || randomizer.GetRandoSettingValue(RSK_SHUFFLE_SPEAK) || randomizer.GetRandoSettingValue(RSK_SHUFFLE_OCARINA_BUTTONS) || + randomizer.GetRandoSettingValue(RSK_SHUFFLE_SCARECROWS_SONG) || randomizer.GetRandoSettingValue(RSK_ROCS_FEATHER); if (bombchuProgressive || triforceHunt || anyAbilityShuffle) { @@ -2216,6 +2217,8 @@ void DrawEquipmentTab() { static_cast<RandomizerInf>(RAND_INF_HAS_OCARINA_A + i)); } } + if (randomizer.GetRandoSettingValue(RSK_SHUFFLE_SCARECROWS_SONG)) + add(RG_SCARECROWS_SONG, RAND_INF_HAS_SCARECROWS_SONG); if (randomizer.GetRandoSettingValue(RSK_ROCS_FEATHER)) add(RG_ROCS_FEATHER, RAND_INF_OBTAINED_ROCS_FEATHER); if (randomizer.GetRandoSettingValue(RSK_SHUFFLE_FISHING_POLE)) diff --git a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h index 19f9bfb9e..478045108 100644 --- a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h +++ b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h @@ -1265,6 +1265,14 @@ typedef enum { // #### `result` // ```c + // !Flags_GetEventChkInf(EVENTCHKINF_PLAYED_SONG_FOR_SCARECROW_AS_ADULT) + // ``` + // #### `args` + // - None + VB_GIVE_ITEM_FROM_SCARECROW, + + // #### `result` + // ```c // true // ``` // #### `args` diff --git a/soh/soh/Enhancements/gameplaystats.cpp b/soh/soh/Enhancements/gameplaystats.cpp index 8824dfa2a..d814025b0 100644 --- a/soh/soh/Enhancements/gameplaystats.cpp +++ b/soh/soh/Enhancements/gameplaystats.cpp @@ -905,6 +905,7 @@ void SetupDisplayNames() { strcpy(itemTimestampDisplayName[TIMESTAMP_FOUND_ZR_BEAN_SOUL], "ZR Bean Soul: "); strcpy(itemTimestampDisplayName[TIMESTAMP_FOUND_SKELETON_KEY], "Skeleton Key: "); strcpy(itemTimestampDisplayName[TIMESTAMP_FOUND_ROCS_FEATHER], "Roc's Feather: "); + strcpy(itemTimestampDisplayName[TIMESTAMP_FOUND_SCARECROWS_SONG], "Scarecrow's Song: "); // clang-format on } diff --git a/soh/soh/Enhancements/gameplaystats.h b/soh/soh/Enhancements/gameplaystats.h index 414ffe689..bf9d651a6 100644 --- a/soh/soh/Enhancements/gameplaystats.h +++ b/soh/soh/Enhancements/gameplaystats.h @@ -121,7 +121,8 @@ typedef enum { /* 0xED */ TIMESTAMP_FOUND_SKELETON_KEY, /* 0xEE */ TIMESTAMP_FOUND_ROCS_FEATHER, /* 0xEF */ TIMESTAMP_TIMESPLITS_COMPLETED, - /* 0xF0 */ TIMESTAMP_MAX + /* 0xF0 */ TIMESTAMP_FOUND_SCARECROWS_SONG, + /* 0xF1 */ TIMESTAMP_MAX } GameplayStatTimestamp; typedef enum { diff --git a/soh/soh/Enhancements/kaleido.cpp b/soh/soh/Enhancements/kaleido.cpp index affeca7f5..ee66375f8 100644 --- a/soh/soh/Enhancements/kaleido.cpp +++ b/soh/soh/Enhancements/kaleido.cpp @@ -154,6 +154,11 @@ Kaleido::Kaleido() { if (ctx->GetOption(RSK_SHUFFLE_OCARINA_BUTTONS)) { mEntries.push_back(std::make_shared<KaleidoEntryOcarinaButtons>()); } + if (ctx->GetOption(RSK_SHUFFLE_SCARECROWS_SONG)) { + mEntries.push_back(std::make_shared<KaleidoEntryIconFlag>( + gSongNoteTex, G_IM_FMT_IA, G_IM_SIZ_8b, 16, 24, Color_RGBA8{ 40, 160, 40, 255 }, + FlagType::FLAG_RANDOMIZER_INF, RAND_INF_HAS_SCARECROWS_SONG, "Scarecrow's Song")); + } if (ctx->GetOption(RSK_SHUFFLE_BOSS_SOULS).IsNot(RO_BOSS_SOULS_OFF)) { static const char* bossSoulNames[] = { "Gohma's Soul", "King Dodongo's Soul", "Barinade's Soul", "Phantom Ganon's Soul", diff --git a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_overworld.cpp b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_overworld.cpp index e9d5a567a..d837e9e5a 100644 --- a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_overworld.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_overworld.cpp @@ -512,6 +512,10 @@ void StaticData::HintTable_Init_Exclude_Overworld() { /*french*/ "Selon moi, le #trésor au fond du lac# est #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ Según dicen, el #tesoro hundido del lago# se trata de #[[1]]#. + hintTextTable[RHT_LH_SCARECROWS_SONG] = HintText(CustomMessage("They say that #teaching a scarecrow a tune# is rewarded with #[[1]]#.", + /*german*/ TODO_TRANSLATE, + /*french*/ TODO_TRANSLATE, {QM_RED, QM_GREEN})); + hintTextTable[RHT_TH_FREED_CARPENTERS] = HintText(CustomMessage("They say that #rescuing captured carpenters# is rewarded with #[[1]]#.", /*german*/ "Man erzählt sich, daß das #Retten gefangener Zimmerleute# #[[1]]# einbrächte.", /*french*/ "Selon moi, #secourir les charpentiers capturés# assure #[[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 371163634..2a1df3385 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 @@ -2090,6 +2090,12 @@ void StaticData::HintTable_Init_Item() { 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_SCARECROWS_SONG] = HintText(CustomMessage("Scarecrow's Song", /*german*/"das Vogelscheuchenlied", /*french*/"le chant de l'épouvantail"), + { + CustomMessage("a song", /*german*/"ein Lied", /*french*/"un chant") + }, { + CustomMessage("a scarecrow's memory", /*german*/"eine Vogelscheuchen-Erinnerung", /*french*/"un souvenir d'épouvantail")}); + 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/item_pool.cpp b/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp index f7f56b9ef..53059dd80 100644 --- a/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp @@ -387,6 +387,10 @@ void GenerateItemPool() { AddItemToPool(RG_ZELDAS_LETTER, 2, 1, 1, 1); } + if (ctx->GetOption(RSK_SHUFFLE_SCARECROWS_SONG) && !ctx->GetOption(RSK_STARTING_SCARECROWS_SONG)) { + AddItemToPool(RG_SCARECROWS_SONG, 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 a873834a2..11e3527ad 100644 --- a/soh/soh/Enhancements/randomizer/3drando/starting_inventory.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/starting_inventory.cpp @@ -120,6 +120,7 @@ void GenerateStartingInventory() { if (ctx->GetOption(RSK_SHUFFLE_ZELDAS_LETTER)) { AddItemToInventory(RG_ZELDAS_LETTER, ctx->GetOption(RSK_STARTING_ZELDAS_LETTER) ? 1 : 0); } + AddItemToInventory(RG_SCARECROWS_SONG, ctx->GetOption(RSK_STARTING_SCARECROWS_SONG) ? 1 : 0); AddItemToInventory(RG_KEATON_MASK, ctx->GetOption(RSK_STARTING_KEATON_MASK) ? 1 : 0); AddItemToInventory(RG_SKULL_MASK, ctx->GetOption(RSK_STARTING_SKULL_MASK) ? 1 : 0); AddItemToInventory(RG_SPOOKY_MASK, ctx->GetOption(RSK_STARTING_SPOOKY_MASK) ? 1 : 0); diff --git a/soh/soh/Enhancements/randomizer/Plandomizer.cpp b/soh/soh/Enhancements/randomizer/Plandomizer.cpp index 73631e8a2..ebc5a8638 100644 --- a/soh/soh/Enhancements/randomizer/Plandomizer.cpp +++ b/soh/soh/Enhancements/randomizer/Plandomizer.cpp @@ -415,6 +415,12 @@ void PlandomizerItemImageCorrection(Rando::Item randoItem) { textureID = gui->GetTextureByName("ITEM_OCARINA_TIME"); } + if (randoItem.GetRandomizerGet() == RG_SCARECROWS_SONG) { + textureID = gui->GetTextureByName("RG_SCARECROWS_SONG"); + imageSize = ImVec2(24.0f, 32.0f); + imagePadding = 6.0f; + } + if (textureID == 0) { textureID = gui->GetTextureByName(itemMapping[randoItem.GetGIEntry()->itemId].name); } diff --git a/soh/soh/Enhancements/randomizer/RCToRandInf.cpp b/soh/soh/Enhancements/randomizer/RCToRandInf.cpp index 2e9077ba0..a579d6eb8 100644 --- a/soh/soh/Enhancements/randomizer/RCToRandInf.cpp +++ b/soh/soh/Enhancements/randomizer/RCToRandInf.cpp @@ -159,6 +159,7 @@ std::map<RandomizerCheck, RandomizerInf> rcToRandomizerInf = { { RC_LH_ADULT_FISHING, RAND_INF_ADULT_FISHING }, { RC_MARKET_10_BIG_POES, RAND_INF_10_BIG_POES }, { RC_KAK_100_GOLD_SKULLTULA_REWARD, RAND_INF_KAK_100_GOLD_SKULLTULA_REWARD }, + { RC_LH_SCARECROWS_SONG, RAND_INF_LH_SCARECROWS_SONG }, { RC_KF_STORMS_GROTTO_BEEHIVE_LEFT, RAND_INF_BEEHIVE_KF_STORMS_GROTTO_LEFT }, { RC_KF_STORMS_GROTTO_BEEHIVE_RIGHT, RAND_INF_BEEHIVE_KF_STORMS_GROTTO_RIGHT }, { RC_LW_NEAR_SHORTCUTS_GROTTO_BEEHIVE_LEFT, RAND_INF_BEEHIVE_LW_NEAR_SHORTCUTS_GROTTO_LEFT }, diff --git a/soh/soh/Enhancements/randomizer/SeedContext.cpp b/soh/soh/Enhancements/randomizer/SeedContext.cpp index 2cd7837ef..9d3013509 100644 --- a/soh/soh/Enhancements/randomizer/SeedContext.cpp +++ b/soh/soh/Enhancements/randomizer/SeedContext.cpp @@ -172,6 +172,8 @@ 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.GetRandomizerCheck() == RC_LH_SCARECROWS_SONG && + mOptions[RSK_SHUFFLE_SCARECROWS_SONG].Is(RO_GENERIC_OFF)) || (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 diff --git a/soh/soh/Enhancements/randomizer/Traps.cpp b/soh/soh/Enhancements/randomizer/Traps.cpp index 9ab724d81..0654479db 100644 --- a/soh/soh/Enhancements/randomizer/Traps.cpp +++ b/soh/soh/Enhancements/randomizer/Traps.cpp @@ -982,6 +982,11 @@ static void InitTrickNames() { { Text{ "Floating Lure", "Floating Lure", "Schwimmer" }, Text{ "a ", "un ", "einen " } }, { Text{ "Fishing Reel", "Fishing Reel", "Angelschnur" }, Text{ "a ", "un ", "eine " } }, }; + trickNameTable[RG_SCARECROWS_SONG] = { + { Text{ "Pierre's Polka", "Polka de Pierre", "Pierres Polka" }, Text{ "", "la ", "" } }, + { Text{ "Crow's Song", "Chant du corbeau", "Krähenlied" }, Text{ "the ", "le ", "das " } }, + { Text{ "Bonooru's Ballad", "Ballade de Bonooru", "Bonoorus Ballade" }, Text{ "", "la ", "" } }, + }; trickNameTable[RG_SKELETON_KEY] = { // TODO_TRANSLATE { Text{ "Stalfos Key" }, Text{ "the " } }, { Text{ "Nightmare Key" }, Text{ "the " } }, diff --git a/soh/soh/Enhancements/randomizer/draw.cpp b/soh/soh/Enhancements/randomizer/draw.cpp index b65aab0c5..334f84e8e 100644 --- a/soh/soh/Enhancements/randomizer/draw.cpp +++ b/soh/soh/Enhancements/randomizer/draw.cpp @@ -14,6 +14,7 @@ extern "C" { #include "objects/gameplay_keep/gameplay_keep.h" #include "objects/object_gi_compass/object_gi_compass.h" #include "objects/object_gi_map/object_gi_map.h" +#include "objects/object_gi_melody/object_gi_melody.h" #include "objects/object_gi_hearts/object_gi_hearts.h" #include "objects/object_gi_rupy/object_gi_rupy.h" #include "objects/object_gi_scale/object_gi_scale.h" @@ -1221,6 +1222,25 @@ extern "C" void Randomizer_DrawFishingPoleGI(PlayState* play, GetItemEntry* getI CLOSE_DISPS(play->state.gfxCtx); } +extern "C" void Randomizer_DrawScarecrowsSong(PlayState* play, GetItemEntry* getItemEntry) { + OPEN_DISPS(play->state.gfxCtx); + + Gfx_SetupDL_25Xlu(play->state.gfxCtx); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(play->state.gfxCtx, (char*)__FILE__, __LINE__), + G_MTX_MODELVIEW | G_MTX_LOAD); + + // dark green note, distinct from the lighter Saria and Minuet greens + gDPSetGrayscaleColor(POLY_XLU_DISP++, 40, 160, 40, 255); + gSPGrayscale(POLY_XLU_DISP++, true); + + gSPDisplayList(POLY_XLU_DISP++, (Gfx*)gGiSongNoteDL); + + gSPGrayscale(POLY_XLU_DISP++, false); + + CLOSE_DISPS(play->state.gfxCtx); +} + extern "C" void Randomizer_DrawSkeletonKey(PlayState* play, GetItemEntry* getItemEntry) { OPEN_DISPS(play->state.gfxCtx); diff --git a/soh/soh/Enhancements/randomizer/draw.h b/soh/soh/Enhancements/randomizer/draw.h index 05958dc8f..739971098 100644 --- a/soh/soh/Enhancements/randomizer/draw.h +++ b/soh/soh/Enhancements/randomizer/draw.h @@ -27,6 +27,7 @@ void Randomizer_DrawJabberNut(PlayState* play, GetItemEntry* getItemEntry); void Randomizer_DrawOpenChest(PlayState* play, GetItemEntry* getItemEntry); void Randomizer_DrawSilverRupee(PlayState* play, GetItemEntry* getItemEntry); void Randomizer_DrawFishingPoleGI(PlayState* play, GetItemEntry* getItemEntry); +void Randomizer_DrawScarecrowsSong(PlayState* play, GetItemEntry* getItemEntry); void Randomizer_DrawSkeletonKey(PlayState* play, GetItemEntry* getItemEntry); void Randomizer_DrawMysteryItem(PlayState* play, GetItemEntry* getItemEntry); void Randomizer_DrawBombchuBag(PlayState* play, GetItemEntry* getItemEntry); diff --git a/soh/soh/Enhancements/randomizer/hook_handlers.cpp b/soh/soh/Enhancements/randomizer/hook_handlers.cpp index e21bf5583..e14498097 100644 --- a/soh/soh/Enhancements/randomizer/hook_handlers.cpp +++ b/soh/soh/Enhancements/randomizer/hook_handlers.cpp @@ -1932,9 +1932,18 @@ void RandomizerOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_l break; } - if (gPlayState->msgCtx.msgMode == MSGMODE_OCARINA_PLAYING && RAND_GET_OPTION(RSK_SKIP_SCARECROWS_SONG)) { + if (Flags_GetRandomizerInf(RAND_INF_HAS_SCARECROWS_SONG) && + gPlayState->msgCtx.msgMode == MSGMODE_OCARINA_PLAYING) { *should = true; - break; + } else if (RAND_GET_OPTION(RSK_SHUFFLE_SCARECROWS_SONG)) { + *should = false; + } + break; + } + case VB_GIVE_ITEM_FROM_SCARECROW: { + if (RAND_GET_OPTION(RSK_SHUFFLE_SCARECROWS_SONG)) { + Flags_SetRandomizerInf(RAND_INF_LH_SCARECROWS_SONG); + *should = false; } break; } diff --git a/soh/soh/Enhancements/randomizer/item_list.cpp b/soh/soh/Enhancements/randomizer/item_list.cpp index 34cac3e19..83b4c36ef 100644 --- a/soh/soh/Enhancements/randomizer/item_list.cpp +++ b/soh/soh/Enhancements/randomizer/item_list.cpp @@ -402,6 +402,8 @@ void Rando::StaticData::InitItemTable() { itemTable[RG_GANON_SOUL].SetCustomDrawFunc(Randomizer_DrawBossSoul); itemTable[RG_FISHING_POLE] = Item(RG_FISHING_POLE, Text{ "Fishing Pole", "Canne à Pêche", "Angelrute" }, ITEMTYPE_ITEM, RG_FISHING_POLE, true, LOGIC_FISHING_POLE, RHT_FISHING_POLE, RG_FISHING_POLE, OBJECT_GI_FISH, GID_FISHING_POLE, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER, {"the ", "la ", "das "}); itemTable[RG_FISHING_POLE].SetCustomDrawFunc(Randomizer_DrawFishingPoleGI); + itemTable[RG_SCARECROWS_SONG] = Item(RG_SCARECROWS_SONG, Text{ "Scarecrow's Song", "Chant de l'Épouvantail", "Vogelscheuchenlied" }, ITEMTYPE_ITEM, RG_SCARECROWS_SONG, true, LOGIC_NONE, RHT_SCARECROWS_SONG, RG_SCARECROWS_SONG, OBJECT_GI_MELODY, GID_SONG_GENERIC, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER, {"the ", "le ", "das "}); + itemTable[RG_SCARECROWS_SONG].SetCustomDrawFunc(Randomizer_DrawScarecrowsSong); itemTable[RG_OCARINA_A_BUTTON] = Item(RG_OCARINA_A_BUTTON, Text{ "Ocarina A Button", "Touche A de l'Ocarina", "Taste A der Okarina" }, ITEMTYPE_ITEM, GI_MAP, true, LOGIC_OCARINA_A_BUTTON, RHT_OCARINA_A_BUTTON, RG_OCARINA_A_BUTTON, OBJECT_GI_MAP, GID_STONE_OF_AGONY, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER, {"the ", "la ", "den "}); itemTable[RG_OCARINA_A_BUTTON].SetCustomDrawFunc(Randomizer_DrawOcarinaButton); diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/lake_hylia.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/lake_hylia.cpp index 29d102aca..db854e734 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/lake_hylia.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/lake_hylia.cpp @@ -15,6 +15,7 @@ void RegionTable_Init_LakeHylia() { }, { //Locations LOCATION(RC_LH_UNDERWATER_ITEM, logic->IsChild && logic->HasItem(RG_SILVER_SCALE)), + LOCATION(RC_LH_SCARECROWS_SONG, logic->Get(LOGIC_CHILD_SCARECROW) && logic->Get(LOGIC_ADULT_SCARECROW)), LOCATION(RC_LH_SUN, logic->IsAdult && ((logic->Get(LOGIC_WATER_TEMPLE_CLEAR) && logic->HasItem(RG_BRONZE_SCALE)) || logic->ReachDistantScarecrow()) && logic->CanUse(RG_FAIRY_BOW)), LOCATION(RC_LH_FREESTANDING_POH, logic->IsAdult && (logic->ReachScarecrow() || logic->BeanPlanted(LOGIC_PLANT_LAKE_HYLIA_BEAN)) && logic->CanAvoidEnemy(RE_GUAY, ED_CLOSE, false) && logic->HasItem(RG_CLIMB)), LOCATION(RC_LH_GS_BEAN_PATCH, logic->CanSpawnSoilSkull(RG_LAKE_HYLIA_BEAN_SOUL) && logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA)), diff --git a/soh/soh/Enhancements/randomizer/location_list.cpp b/soh/soh/Enhancements/randomizer/location_list.cpp index 0f6665067..94073a11a 100644 --- a/soh/soh/Enhancements/randomizer/location_list.cpp +++ b/soh/soh/Enhancements/randomizer/location_list.cpp @@ -187,6 +187,7 @@ void Rando::StaticData::InitLocationTable() { locationTable[RC_LH_LAB_DIVE] = Location::Base(RC_LH_LAB_DIVE, RCQUEST_BOTH, RCTYPE_STANDARD, ACTOR_ID_MAX, SCENE_LAKESIDE_LABORATORY, 0x00, "Lab Dive", RHT_LH_LAB_DIVE, RG_PIECE_OF_HEART, SpoilerCollectionCheck::ItemGetInf(16), true); locationTable[RC_LH_TRADE_FROG] = Location::Base(RC_LH_TRADE_FROG, RCQUEST_BOTH, RCTYPE_ADULT_TRADE, ACTOR_ID_MAX, SCENE_LAKESIDE_LABORATORY, 0x00, "Lab Trade Eyeball Frog", RHT_LH_TRADE_FROG, RG_EYEDROPS, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ADULT_TRADES_LH_TRADE_FROG), true); locationTable[RC_LH_UNDERWATER_ITEM] = Location::Base(RC_LH_UNDERWATER_ITEM, RCQUEST_BOTH, RCTYPE_STANDARD, ACTOR_ID_MAX, SCENE_LAKE_HYLIA, 0x00, "Underwater Item", RHT_LH_UNDERWATER_ITEM, RG_RUTOS_LETTER, SpoilerCollectionCheck::EventChkInf(EVENTCHKINF_OBTAINED_RUTOS_LETTER), true); + locationTable[RC_LH_SCARECROWS_SONG] = Location::Base(RC_LH_SCARECROWS_SONG, RCQUEST_BOTH, RCTYPE_STANDARD, ACTOR_ID_MAX, SCENE_LAKE_HYLIA, 0x00, "Scarecrow's Song", RHT_LH_SCARECROWS_SONG, RG_SCARECROWS_SONG, SpoilerCollectionCheck::RandomizerInf(RAND_INF_LH_SCARECROWS_SONG)); locationTable[RC_LH_SUN] = Location::Base(RC_LH_SUN, RCQUEST_BOTH, RCTYPE_STANDARD, ACTOR_ID_MAX, SCENE_LAKE_HYLIA, 0x00, "Sun", RHT_LH_SUN, RG_FIRE_ARROWS, SpoilerCollectionCheck::Chest(0x57, 0x1F), true); locationTable[RC_LH_FREESTANDING_POH] = Location::Collectable(RC_LH_FREESTANDING_POH, RCQUEST_BOTH, RCTYPE_STANDARD, ACTOR_EN_ITEM00, SCENE_LAKE_HYLIA, 7686, 0x1E, "Freestanding PoH", RHT_LH_FREESTANDING_POH, RG_PIECE_OF_HEART, true); locationTable[RC_LH_DEKU_SCRUB_GROTTO_LEFT] = Location::Base(RC_LH_DEKU_SCRUB_GROTTO_LEFT, RCQUEST_BOTH, RCTYPE_SCRUB, RCAREA_LAKE_HYLIA, ACTOR_EN_DNS, SCENE_GROTTOS, TWO_ACTOR_PARAMS(0x00, 0xEF), "Deku Scrub Grotto Left", RHT_LH_DEKU_SCRUB_GROTTO_LEFT, RG_BUY_DEKU_NUTS_5, SpoilerCollectionCheck::RandomizerInf(RAND_INF_SCRUBS_PURCHASED_LH_DEKU_SCRUB_GROTTO_LEFT), false, 20); diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index e4d429710..a9caab725 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -144,6 +144,7 @@ bool Logic::HasItem(RandomizerGet itemName) { case RG_POWER_BRACELET: case RG_CHILD_WALLET: case RG_FISHING_POLE: + case RG_SCARECROWS_SONG: case RG_BRONZE_SCALE: case RG_CLIMB: case RG_CRAWL: @@ -1605,8 +1606,8 @@ bool Logic::HookshotOrBoomerang() { } bool Logic::ScarecrowsSong() { - return (ctx->GetOption(RSK_SKIP_SCARECROWS_SONG) && HasItem(RG_FAIRY_OCARINA) && OcarinaButtons() >= 2) || - (Get(LOGIC_CHILD_SCARECROW) && Get(LOGIC_ADULT_SCARECROW)); + return (HasItem(RG_SCARECROWS_SONG) && HasItem(RG_FAIRY_OCARINA) && OcarinaButtons() >= 2) || + (!ctx->GetOption(RSK_SHUFFLE_SCARECROWS_SONG) && Get(LOGIC_CHILD_SCARECROW) && Get(LOGIC_ADULT_SCARECROW)); } bool Logic::BlueFire() { @@ -2029,6 +2030,7 @@ std::map<RandomizerGet, uint32_t> StaticData::RandoGetToRandInf = { { RG_SPEAK_KOKIRI, RAND_INF_CAN_SPEAK_KOKIRI }, { RG_SPEAK_ZORA, RAND_INF_CAN_SPEAK_ZORA }, { RG_FISHING_POLE, RAND_INF_FISHING_POLE_FOUND }, + { RG_SCARECROWS_SONG, RAND_INF_HAS_SCARECROWS_SONG }, { RG_ROCS_FEATHER, RAND_INF_OBTAINED_ROCS_FEATHER }, { RG_GUARD_HOUSE_KEY, RAND_INF_GUARD_HOUSE_KEY_OBTAINED }, { RG_MARKET_BAZAAR_KEY, RAND_INF_MARKET_BAZAAR_KEY_OBTAINED }, @@ -2501,6 +2503,7 @@ void Logic::ApplyItemEffect(Item& item, bool state) { case RG_SPEAK_KOKIRI: case RG_SPEAK_ZORA: case RG_FISHING_POLE: + case RG_SCARECROWS_SONG: case RG_GUARD_HOUSE_KEY: case RG_MARKET_BAZAAR_KEY: case RG_MARKET_POTION_SHOP_KEY: diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index 7ebe0b141..d6669cbe7 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -512,6 +512,8 @@ ItemObtainability Randomizer::GetItemObtainabilityFromRandomizerGet(RandomizerGe : (gSaveContext.magicLevel < 2 ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE); case RG_FISHING_POLE: return !Flags_GetRandomizerInf(RAND_INF_FISHING_POLE_FOUND) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE; + case RG_SCARECROWS_SONG: + return !Flags_GetRandomizerInf(RAND_INF_HAS_SCARECROWS_SONG) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE; // Songs case RG_ZELDAS_LULLABY: @@ -1091,6 +1093,8 @@ static std::unordered_map<RandomizerGet, GameplayStatTimestamp> randomizerGetToS { RG_SKELETON_KEY, TIMESTAMP_FOUND_SKELETON_KEY }, { RG_ROCS_FEATHER, TIMESTAMP_FOUND_ROCS_FEATHER }, + + { RG_SCARECROWS_SONG, TIMESTAMP_FOUND_SCARECROWS_SONG }, }; // Gameplay stat tracking: Update time the item was acquired diff --git a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerCheck.h b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerCheck.h index 674e3004c..884f8c68a 100644 --- a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerCheck.h +++ b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerCheck.h @@ -277,6 +277,7 @@ RANDO_ENUM_ITEM(RC_ZF_GS_ABOVE_THE_LOG) RANDO_ENUM_ITEM(RC_ZF_GS_TREE) RANDO_ENUM_ITEM(RC_ZF_GS_HIDDEN_CAVE) RANDO_ENUM_ITEM(RC_LH_UNDERWATER_ITEM) +RANDO_ENUM_ITEM(RC_LH_SCARECROWS_SONG) RANDO_ENUM_ITEM(RC_LH_CHILD_FISHING) RANDO_ENUM_ITEM(RC_LH_CHILD_FISH_1) RANDO_ENUM_ITEM(RC_LH_CHILD_FISH_2) diff --git a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerGet.h b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerGet.h index 4b31a2824..7d58a950e 100644 --- a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerGet.h +++ b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerGet.h @@ -255,6 +255,7 @@ RANDO_ENUM_ITEM(RG_OCARINA_C_LEFT_BUTTON) RANDO_ENUM_ITEM(RG_OCARINA_C_RIGHT_BUTTON) RANDO_ENUM_ITEM(RG_SKELETON_KEY) RANDO_ENUM_ITEM(RG_FISHING_POLE) +RANDO_ENUM_ITEM(RG_SCARECROWS_SONG) RANDO_ENUM_ITEM(RG_DEKU_STICK_BAG) RANDO_ENUM_ITEM(RG_DEKU_NUT_BAG) RANDO_ENUM_ITEM(RG_HINT) diff --git a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerHintTextKey.h b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerHintTextKey.h index b2de108d7..a17a71818 100644 --- a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerHintTextKey.h +++ b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerHintTextKey.h @@ -362,6 +362,7 @@ RANDO_ENUM_ITEM(RHT_ZF_GS_ABOVE_THE_LOG) RANDO_ENUM_ITEM(RHT_ZF_GS_TREE) RANDO_ENUM_ITEM(RHT_ZF_GS_HIDDEN_CAVE) RANDO_ENUM_ITEM(RHT_LH_UNDERWATER_ITEM) +RANDO_ENUM_ITEM(RHT_LH_SCARECROWS_SONG) RANDO_ENUM_ITEM(RHT_LH_CHILD_FISHING) RANDO_ENUM_ITEM(RHT_LH_ADULT_FISHING) RANDO_ENUM_ITEM(RHT_LH_POND_FISH) @@ -1079,6 +1080,7 @@ RANDO_ENUM_ITEM(RHT_CRAWL) RANDO_ENUM_ITEM(RHT_SPEAK) RANDO_ENUM_ITEM(RHT_OPEN_CHEST) RANDO_ENUM_ITEM(RHT_FISHING_POLE) +RANDO_ENUM_ITEM(RHT_SCARECROWS_SONG) RANDO_ENUM_ITEM(RHT_SKELETON_KEY) RANDO_ENUM_ITEM(RHT_SILVER_RUPEE_DODONGOS_CAVERN) RANDO_ENUM_ITEM(RHT_SILVER_RUPEE_SHADOW_TEMPLE) diff --git a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerInf.h b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerInf.h index c2994616d..76dadee6f 100644 --- a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerInf.h +++ b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerInf.h @@ -155,6 +155,8 @@ RANDO_ENUM_ITEM(RAND_INF_ADULT_TRADES_DMT_TRADE_CLAIM_CHECK) RANDO_ENUM_ITEM(RAND_INF_KAK_100_GOLD_SKULLTULA_REWARD) +RANDO_ENUM_ITEM(RAND_INF_LH_SCARECROWS_SONG) + RANDO_ENUM_ITEM(RAND_INF_GREG_FOUND) RANDO_ENUM_ITEM(RAND_INF_TOT_MASTER_SWORD) @@ -1499,6 +1501,7 @@ RANDO_ENUM_ITEM(RAND_INF_GROTTO_FISH_LW_NEAR_SHORTCUTS_GROTTO) RANDO_ENUM_ITEM(RAND_INF_GROTTO_FISH_KF_STORMS_GROTTO) RANDO_ENUM_ITEM(RAND_INF_FISHING_POLE_FOUND) +RANDO_ENUM_ITEM(RAND_INF_HAS_SCARECROWS_SONG) RANDO_ENUM_ITEM(RAND_INF_ZD_FISH_1) RANDO_ENUM_ITEM(RAND_INF_ZD_FISH_2) diff --git a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerSettingKey.h b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerSettingKey.h index 155ad6c64..7f13f4b49 100644 --- a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerSettingKey.h +++ b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerSettingKey.h @@ -93,6 +93,7 @@ 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_SCARECROWS_SONG) RANDO_ENUM_ITEM(RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD) RANDO_ENUM_ITEM(RSK_SHUFFLE_POTS) RANDO_ENUM_ITEM(RSK_SHUFFLE_CRATES) @@ -177,6 +178,7 @@ 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_SCARECROWS_SONG) RANDO_ENUM_ITEM(RSK_STARTING_CLAIM_CHECK) RANDO_ENUM_ITEM(RSK_STARTING_GERUDO_CARD) RANDO_ENUM_ITEM(RSK_STARTING_BIGGORON_SWORD) @@ -185,7 +187,6 @@ RANDO_ENUM_ITEM(RSK_SHUFFLE_CHEST_MINIGAME) RANDO_ENUM_ITEM(RSK_BIG_POE_COUNT) RANDO_ENUM_ITEM(RSK_SKIP_EPONA_RACE) RANDO_ENUM_ITEM(RSK_SHUFFLE_MASKS) -RANDO_ENUM_ITEM(RSK_SKIP_SCARECROWS_SONG) RANDO_ENUM_ITEM(RSK_SKIP_PLANTING_BEANS) RANDO_ENUM_ITEM(RSK_SKULLS_SUNS_SONG) RANDO_ENUM_ITEM(RSK_EARLY_GRANNYS_SHOP) diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_objects.cpp b/soh/soh/Enhancements/randomizer/randomizer_check_objects.cpp index e41c74c24..c6b9920d2 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_check_objects.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_check_objects.cpp @@ -157,6 +157,8 @@ void RandomizerCheckObjects::UpdateImGuiVisibility() { CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleOcarinas"), RO_GENERIC_NO)) && // ocarina locations (location.GetRandomizerCheck() != RC_HC_ZELDAS_LETTER || CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleZeldasLetter"), RO_GENERIC_NO)) && + (location.GetRandomizerCheck() != RC_LH_SCARECROWS_SONG || + CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleScarecrowsSong"), 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 diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp index 7368c6309..67ba9cc99 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp @@ -112,6 +112,7 @@ bool showBossKeysanity; bool showGanonBossKey; bool showOcarinas; bool show100SkullReward; +bool showScarecrowsSong; bool showLinksPocket; bool showChestMinigame; bool fortressFast; @@ -1653,6 +1654,8 @@ void LoadSettings() { 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; + showScarecrowsSong = IS_RANDO && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue( + RSK_SHUFFLE_SCARECROWS_SONG) == 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; @@ -1970,6 +1973,7 @@ bool IsCheckShuffled(RandomizerCheck rc) { (loc->GetRCType() != RCTYPE_BOSS_KEY || showBossKeysanity) && (loc->GetRCType() != RCTYPE_GANON_BOSS_KEY || showGanonBossKey) && (rc != RC_KAK_100_GOLD_SKULLTULA_REWARD || show100SkullReward) && + (rc != RC_LH_SCARECROWS_SONG || showScarecrowsSong) && (loc->GetRCType() != RCTYPE_GF_KEY && rc != RC_TH_FREED_CARPENTERS || (showGerudoCard && rc == RC_TH_FREED_CARPENTERS) || (fortressNormal && showGerudoFortressKeys && loc->GetRCType() == RCTYPE_GF_KEY) || diff --git a/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp index e0f2412b7..3962fe3c5 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp @@ -176,6 +176,10 @@ std::vector<ItemTrackerItem> openChestItems = { ITEM_TRACKER_RG(RG_OPEN_CHEST, "", 0, DrawItem), }; +std::vector<ItemTrackerItem> scarecrowsSongItems = { + ITEM_TRACKER_RG(RG_SCARECROWS_SONG, "", 0, DrawItem), +}; + std::vector<ItemTrackerItem> beanSoulItems = { ITEM_TRACKER_RG_CUSTOM(RG_DEATH_MOUNTAIN_CRATER_BEAN_SOUL, "DMC", ITEM_BEAN, 0, DrawItem), ITEM_TRACKER_RG_CUSTOM(RG_DEATH_MOUNTAIN_TRAIL_BEAN_SOUL, "DMT", ITEM_BEAN, 0, DrawItem), @@ -1198,6 +1202,9 @@ void DrawItem(ItemTrackerItem item) { case RG_OPEN_CHEST: itemName = "Open"; break; + case RG_SCARECROWS_SONG: + itemName = "Scarecrow's Song"; + break; } } else if (item.kind == ITEM_KIND_DUMMY) { if (item.id == ITEMTYPE_SILVER) { @@ -1622,6 +1629,9 @@ void UpdateVectors() { if (IS_RANDO && RAND_GET_OPTION(RSK_SHUFFLE_OPEN_CHEST)) { mainWindowItems.insert(mainWindowItems.end(), openChestItems.begin(), openChestItems.end()); } + if (IS_RANDO && RAND_GET_OPTION(RSK_SHUFFLE_SCARECROWS_SONG)) { + mainWindowItems.insert(mainWindowItems.end(), scarecrowsSongItems.begin(), scarecrowsSongItems.end()); + } // if we're adding greg to the misc window, // and misc isn't on the main window, diff --git a/soh/soh/Enhancements/randomizer/savefile.cpp b/soh/soh/Enhancements/randomizer/savefile.cpp index 2fb9f3060..130cbc1c0 100644 --- a/soh/soh/Enhancements/randomizer/savefile.cpp +++ b/soh/soh/Enhancements/randomizer/savefile.cpp @@ -344,6 +344,9 @@ void SetStartingItems() { Randomizer_GetSettingValue(RSK_SHUFFLE_ZELDAS_LETTER)) { Item_Give(NULL, ITEM_LETTER_ZELDA); } + if (Randomizer_GetSettingValue(RSK_STARTING_SCARECROWS_SONG)) { + Flags_SetRandomizerInf(RAND_INF_HAS_SCARECROWS_SONG); + } if (Randomizer_GetSettingValue(RSK_STARTING_CLAIM_CHECK)) { Item_Give(NULL, ITEM_CLAIM_CHECK); } diff --git a/soh/soh/Enhancements/randomizer/settings.cpp b/soh/soh/Enhancements/randomizer/settings.cpp index 23c6814ee..118bf10bc 100644 --- a/soh/soh/Enhancements/randomizer/settings.cpp +++ b/soh/soh/Enhancements/randomizer/settings.cpp @@ -858,6 +858,7 @@ void Settings::CreateOptions() { OPT_U8(RSK_SHUFFLE_OPEN_CHEST, {"Off", "On", "Progressive"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleOpenChest"), WIDGET_CVAR_COMBOBOX, RO_OPEN_CHEST_OFF); OPT_U8(RSK_SHUFFLE_WEIRD_EGG, {"Vanilla", "Shuffled", "Skip Waking Talon"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleWeirdEgg"), WIDGET_CVAR_COMBOBOX, RO_WEIRD_EGG_VANILLA); OPT_BOOL(RSK_SHUFFLE_ZELDAS_LETTER, CVAR_RANDOMIZER_SETTING("ShuffleZeldasLetter")); + OPT_BOOL(RSK_SHUFFLE_SCARECROWS_SONG, CVAR_RANDOMIZER_SETTING("ShuffleScarecrowsSong")); OPT_BOOL(RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD, CVAR_RANDOMIZER_SETTING("ShuffleGerudoToken")); OPT_U8(RSK_SHUFFLE_POTS, {"Off", "Dungeons", "Overworld", "All Pots"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShufflePots"), WIDGET_CVAR_COMBOBOX, RO_SHUFFLE_POTS_OFF); OPT_U8(RSK_SHUFFLE_GRASS, {"Off", "Dungeons", "Overworld", "All Grass"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleGrass"), WIDGET_CVAR_COMBOBOX, RO_SHUFFLE_GRASS_OFF); @@ -1341,7 +1342,6 @@ void Settings::CreateOptions() { OPT_BOOL(RSK_SKIP_CHILD_STEALTH, {"Don't Skip", "Skip"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("SkipChildStealth"), WIDGET_CVAR_CHECKBOX, RO_GENERIC_DONT_SKIP); OPT_BOOL(RSK_EARLY_GRANNYS_SHOP, CVAR_RANDOMIZER_SETTING("EarlyGrannysShop")); OPT_BOOL(RSK_SKIP_EPONA_RACE, {"Don't Skip", "Skip"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("SkipEponaRace"), WIDGET_CVAR_CHECKBOX, RO_GENERIC_DONT_SKIP); - OPT_BOOL(RSK_SKIP_SCARECROWS_SONG, CVAR_RANDOMIZER_SETTING("SkipScarecrowsSong")); OPT_BOOL(RSK_SKIP_PLANTING_BEANS, CVAR_RANDOMIZER_SETTING("SkipPlantingBeans")); OPT_U8(RSK_BIG_POE_COUNT, {NumOpts(0, 10)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("BigPoeTargetCount"), WIDGET_CVAR_SLIDER_INT, 10); OPT_CALLBACK(RSK_BIG_POE_COUNT, { @@ -1446,6 +1446,7 @@ void Settings::CreateOptions() { OPT_U8(RSK_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, CVAR_RANDOMIZER_SETTING("StartingWeirdEgg")); OPT_BOOL(RSK_STARTING_ZELDAS_LETTER, CVAR_RANDOMIZER_SETTING("StartingZeldasLetter")); + OPT_BOOL(RSK_STARTING_SCARECROWS_SONG, CVAR_RANDOMIZER_SETTING("StartingScarecrowsSong")); OPT_BOOL(RSK_STARTING_CLAIM_CHECK, CVAR_RANDOMIZER_SETTING("StartingClaimCheck")); OPT_BOOL(RSK_STARTING_GERUDO_CARD, CVAR_RANDOMIZER_SETTING("StartingGerudoCard")); OPT_BOOL(RSK_STARTING_KEATON_MASK, CVAR_RANDOMIZER_SETTING("StartingKeatonMask")); @@ -1886,7 +1887,6 @@ void Settings::CreateOptions() { &mOptions[RSK_EARLY_GRANNYS_SHOP], &mOptions[RSK_SKIP_PLANTING_BEANS], &mOptions[RSK_SKIP_EPONA_RACE], - &mOptions[RSK_SKIP_SCARECROWS_SONG], }, WidgetContainerType::SECTION); mOptionGroups[RSG_MENU_SECTION_WINCON] = OptionGroup::SubGroup("Win Condition", @@ -2022,24 +2022,43 @@ void Settings::CreateOptions() { mOptionGroups[RSG_MENU_SECTION_BASIC_SHUFFLES] = OptionGroup::SubGroup("Shuffle Items", { - &mOptions[RSK_SHUFFLE_SONGS], &mOptions[RSK_SHUFFLE_TOKENS], - &mOptions[RSK_SHUFFLE_KOKIRI_SWORD], &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_SHUFFLE_MASKS], &mOptions[RSK_FISHSANITY], - &mOptions[RSK_FISHSANITY_POND_COUNT], &mOptions[RSK_FISHSANITY_AGE_SPLIT], - &mOptions[RSK_SHUFFLE_FREESTANDING], &mOptions[RSK_SHUFFLE_WONDER_ITEMS], - &mOptions[RSK_SHUFFLE_SILVER], &mOptions[RSK_SHUFFLE_BEEHIVES], - &mOptions[RSK_SHUFFLE_COWS], &mOptions[RSK_SHUFFLE_POTS], - &mOptions[RSK_SHUFFLE_GRASS], &mOptions[RSK_SHUFFLE_CRATES], - &mOptions[RSK_SHUFFLE_BOULDERS], &mOptions[RSK_SHUFFLE_ROCKS], - &mOptions[RSK_SHUFFLE_TREES], &mOptions[RSK_SHUFFLE_BUSHES], - &mOptions[RSK_SHUFFLE_ICICLES], &mOptions[RSK_SHUFFLE_RED_ICE], - &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], &mOptions[RSK_SHUFFLE_BEAN_FAIRIES], - &mOptions[RSK_SHUFFLE_SONG_FAIRIES], &mOptions[RSK_SHUFFLE_BUTTERFLY_FAIRIES], + &mOptions[RSK_SHUFFLE_SONGS], + &mOptions[RSK_SHUFFLE_TOKENS], + &mOptions[RSK_SHUFFLE_KOKIRI_SWORD], + &mOptions[RSK_SHUFFLE_MASTER_SWORD], + &mOptions[RSK_SHUFFLE_OCARINA], + &mOptions[RSK_SHUFFLE_WEIRD_EGG], + &mOptions[RSK_SHUFFLE_ZELDAS_LETTER], + &mOptions[RSK_SHUFFLE_SCARECROWS_SONG], + &mOptions[RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD], + &mOptions[RSK_SHUFFLE_MASKS], + &mOptions[RSK_FISHSANITY], + &mOptions[RSK_FISHSANITY_POND_COUNT], + &mOptions[RSK_FISHSANITY_AGE_SPLIT], + &mOptions[RSK_SHUFFLE_FREESTANDING], + &mOptions[RSK_SHUFFLE_WONDER_ITEMS], + &mOptions[RSK_SHUFFLE_SILVER], + &mOptions[RSK_SHUFFLE_BEEHIVES], + &mOptions[RSK_SHUFFLE_COWS], + &mOptions[RSK_SHUFFLE_POTS], + &mOptions[RSK_SHUFFLE_GRASS], + &mOptions[RSK_SHUFFLE_CRATES], + &mOptions[RSK_SHUFFLE_BOULDERS], + &mOptions[RSK_SHUFFLE_ROCKS], + &mOptions[RSK_SHUFFLE_TREES], + &mOptions[RSK_SHUFFLE_BUSHES], + &mOptions[RSK_SHUFFLE_ICICLES], + &mOptions[RSK_SHUFFLE_RED_ICE], + &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], + &mOptions[RSK_SHUFFLE_BEAN_FAIRIES], + &mOptions[RSK_SHUFFLE_SONG_FAIRIES], + &mOptions[RSK_SHUFFLE_BUTTERFLY_FAIRIES], }, WidgetContainerType::SECTION); mOptionGroups[RSG_MENU_COLUMN_BASIC_SHUFFLES] = @@ -2277,6 +2296,7 @@ void Settings::CreateOptions() { &mOptions[RSK_SHUFFLE_OPEN_CHEST], &mOptions[RSK_SHUFFLE_WEIRD_EGG], &mOptions[RSK_SHUFFLE_ZELDAS_LETTER], + &mOptions[RSK_SHUFFLE_SCARECROWS_SONG], &mOptions[RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD], &mOptions[RSK_SHUFFLE_MASKS], &mOptions[RSK_SHUFFLE_MERCHANTS], @@ -2391,6 +2411,7 @@ void Settings::CreateOptions() { &mOptions[RSK_STARTING_REQUIEM_OF_SPIRIT], &mOptions[RSK_STARTING_NOCTURNE_OF_SHADOW], &mOptions[RSK_STARTING_PRELUDE_OF_LIGHT], + &mOptions[RSK_STARTING_SCARECROWS_SONG], }); mOptionGroups[RSG_STARTING_OTHER] = OptionGroup::SubGroup("Other", { &mOptions[RSK_STARTING_STICKS], @@ -2409,7 +2430,6 @@ void Settings::CreateOptions() { OptionGroupType::DEFAULT); mOptionGroups[RSG_TIMESAVERS] = OptionGroup("Timesaver Settings", { &mOptions[RSK_SKIP_EPONA_RACE], - &mOptions[RSK_SKIP_SCARECROWS_SONG], &mOptions[RSK_SKIP_PLANTING_BEANS], &mOptions[RSK_BIG_POE_COUNT], }); @@ -3159,6 +3179,7 @@ void Settings::RandomizeAllSettings() { case RSK_STARTING_BOTTLE_4: case RSK_STARTING_WEIRD_EGG: case RSK_STARTING_ZELDAS_LETTER: + case RSK_STARTING_SCARECROWS_SONG: case RSK_STARTING_CLAIM_CHECK: case RSK_STARTING_GERUDO_CARD: case RSK_STARTING_BIGGORON_SWORD: diff --git a/soh/soh/SohGui/ImGuiUtils.cpp b/soh/soh/SohGui/ImGuiUtils.cpp index a75b65c01..968f5eac8 100644 --- a/soh/soh/SohGui/ImGuiUtils.cpp +++ b/soh/soh/SohGui/ImGuiUtils.cpp @@ -250,6 +250,12 @@ void RegisterImGuiItemIcons() { gui->LoadGuiTexture("ITEM_RUPEE_SILVER", gRupeeCounterIconTex, "", silver); gui->LoadGuiTexture("ITEM_RUPEE_SILVER_Faded", gRupeeCounterIconTex, "", silverFaded); + ImVec4 scarecrow = ImVec4(40.0f / 255.0f, 160.0f / 255.0f, 40.0f / 255.0f, 1.0f); + ImVec4 scarecrowFaded = scarecrow; + scarecrowFaded.w = 0.3f; + gui->LoadGuiTexture("RG_SCARECROWS_SONG", gSongNoteTex, "", scarecrow); + gui->LoadGuiTexture("RG_SCARECROWS_SONG_Faded", gSongNoteTex, "", scarecrowFaded); + for (const auto& entry : questMapping) { gui->LoadGuiTexture(entry.second.name, entry.second.texturePath, "", ImVec4(1, 1, 1, 1)); gui->LoadGuiTexture(entry.second.nameFaded, entry.second.texturePath, "", ImVec4(1, 1, 1, 0.3f)); diff --git a/soh/soh/SohGui/SohMenuEnhancements.cpp b/soh/soh/SohGui/SohMenuEnhancements.cpp index 7d0579981..27c7f9f47 100644 --- a/soh/soh/SohGui/SohMenuEnhancements.cpp +++ b/soh/soh/SohGui/SohMenuEnhancements.cpp @@ -601,13 +601,15 @@ void SohMenu::AddMenuEnhancements() { .CVar(CVAR_ENHANCEMENT("InstantScarecrow")) .PreFunc([](WidgetInfo& info) { info.options->disabled = - IS_RANDO && OTRGlobals::Instance->gRandoContext->GetOption(RSK_SKIP_SCARECROWS_SONG); - info.options->disabledTooltip = "This setting is forcefully enabled because a randomized save " - "file with the option \"Skip Scarecrow's Song\" is currently loaded."; + IS_RANDO && (OTRGlobals::Instance->gRandoContext->GetOption(RSK_STARTING_SCARECROWS_SONG) || + OTRGlobals::Instance->gRandoContext->GetOption(RSK_SHUFFLE_SCARECROWS_SONG)); + info.options->disabledTooltip = + "This setting is controlled by the randomizer because a randomized save file with the option " + "\"Start with Scarecrow's Song\" or \"Shuffle Scarecrow's Song\" is currently loaded."; }) .Options(CheckboxOptions().Tooltip( "Pierre appears when an Ocarina is pulled out. Requires learning the Scarecrow's Song first.\n" - "Without the randomizer option \"Skip Scarecrow's Song\" enabled for a seed, this still requires you " + "Without the randomizer option \"Start with Scarecrow's Song\" enabled for a seed, this still requires you " "to teach the scarecrow the song as both ages before summoning.")); AddWidget(path, "Faster Rupee Accumulator", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("FasterRupeeAccumulator")) diff --git a/soh/soh/SohGui/SohMenuStartingItems.cpp b/soh/soh/SohGui/SohMenuStartingItems.cpp index 6a6101509..fcd1015ef 100644 --- a/soh/soh/SohGui/SohMenuStartingItems.cpp +++ b/soh/soh/SohGui/SohMenuStartingItems.cpp @@ -93,16 +93,15 @@ static void StartingItemTiered(RandomizerSettingKey rsk, const std::vector<uint3 } // Toggleable 32x48 song icon, mirroring the songMapping loop in DrawQuestStatusTab. -static void StartingSongToggle(RandomizerSettingKey rsk, QuestItem song) { +static void StartingSongToggle(RandomizerSettingKey rsk, const std::string& name, const std::string& nameFaded) { Rando::Option& option = Rando::Settings::GetInstance()->GetOption(rsk); const char* cvar = option.GetCVarName().c_str(); - const SongMapEntry& entry = songMapping[song]; bool on = CVarGetInteger(cvar, 0) != 0; ImGui::PushID(static_cast<int32_t>(rsk)); PushStyleButton(Colors::DarkGray); - if (ImGui::ImageButton(entry.name.c_str(), GetFast3dGui()->GetTextureByName(on ? entry.name : entry.nameFaded), - kSongSize, ImVec2(0, 0), ImVec2(1, 1))) { + if (ImGui::ImageButton(name.c_str(), GetFast3dGui()->GetTextureByName(on ? name : nameFaded), kSongSize, + ImVec2(0, 0), ImVec2(1, 1))) { CVarSetInteger(cvar, on ? 0 : 1); SaveStartingItemCVars(); } @@ -111,6 +110,11 @@ static void StartingSongToggle(RandomizerSettingKey rsk, QuestItem song) { ImGui::PopID(); } +static void StartingSongToggle(RandomizerSettingKey rsk, QuestItem song) { + const SongMapEntry& entry = songMapping[song]; + StartingSongToggle(rsk, entry.name, entry.nameFaded); +} + // Item icon followed by a count slider, like the ammo rows in DrawInventoryTab. The slider // mirrors the Option's own semantics: the CVar stores the option index and the displayed // number comes from the option text (matching how WIDGET_CVAR_SLIDER_INT renders these). @@ -352,6 +356,8 @@ void DrawStartingItemsMenu(WidgetInfo& info) { StartingSongToggle(RSK_STARTING_NOCTURNE_OF_SHADOW, QUEST_SONG_NOCTURNE); ImGui::SameLine(); StartingSongToggle(RSK_STARTING_PRELUDE_OF_LIGHT, QUEST_SONG_PRELUDE); + ImGui::SameLine(); + StartingSongToggle(RSK_STARTING_SCARECROWS_SONG, "RG_SCARECROWS_SONG", "RG_SCARECROWS_SONG_Faded"); ImGui::SeparatorText("Other"); StartingItemCombobox(RSK_LINKS_POCKET); diff --git a/soh/soh/config/ConfigUpdaters.cpp b/soh/soh/config/ConfigUpdaters.cpp index 2dbdfd764..4df227ab2 100644 --- a/soh/soh/config/ConfigUpdaters.cpp +++ b/soh/soh/config/ConfigUpdaters.cpp @@ -1459,6 +1459,7 @@ static const Migration version7Migrations[] = { { "gRandoEnhancements.CustomKeyModels", nullptr }, { "gRandoSettings.LogicRules", "gRandoSettings.NoLogic" }, { "gRandoSettings.AllLocationsReachable", "gRandoSettings.AllChecksReachable" }, + { "gRandoSettings.SkipScarecrowsSong", "gRandoSettings.StartingScarecrowsSong" }, { nullptr, nullptr }, }; diff --git a/soh/src/code/code_800EC960.c b/soh/src/code/code_800EC960.c index b8aa5c8bf..31fc49de4 100644 --- a/soh/src/code/code_800EC960.c +++ b/soh/src/code/code_800EC960.c @@ -2090,7 +2090,8 @@ void AudioOcarina_PlaybackSong(void) { } return; } else { - sPlaybackNoteTimer -= nextNoteTimerStep; + // SoH [Fix] several audio tasks can run between updates, don't wrap below zero + sPlaybackNoteTimer -= MIN(nextNoteTimerStep, sPlaybackNoteTimer); } // Update volume diff --git a/soh/src/overlays/actors/ovl_En_Kakasi3/z_en_kakasi3.c b/soh/src/overlays/actors/ovl_En_Kakasi3/z_en_kakasi3.c index 2f088e5f6..c1e692851 100644 --- a/soh/src/overlays/actors/ovl_En_Kakasi3/z_en_kakasi3.c +++ b/soh/src/overlays/actors/ovl_En_Kakasi3/z_en_kakasi3.c @@ -7,6 +7,7 @@ #include "z_en_kakasi3.h" #include "vt.h" #include "objects/object_ka/object_ka.h" +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #define FLAGS (ACTOR_FLAG_ATTENTION_ENABLED | ACTOR_FLAG_FRIENDLY | ACTOR_FLAG_UPDATE_DURING_OCARINA) @@ -387,7 +388,8 @@ void func_80A91A90(EnKakasi3* this, PlayState* play) { if (this->dialogState == Message_GetState(&play->msgCtx) && Message_ShouldAdvance(play)) { if (this->unk_195) { - if (!Flags_GetEventChkInf(EVENTCHKINF_PLAYED_SONG_FOR_SCARECROW_AS_ADULT)) { + if (GameInteractor_Should(VB_GIVE_ITEM_FROM_SCARECROW, + !Flags_GetEventChkInf(EVENTCHKINF_PLAYED_SONG_FOR_SCARECROW_AS_ADULT))) { Flags_SetEventChkInf(EVENTCHKINF_PLAYED_SONG_FOR_SCARECROW_AS_ADULT); } } |
