diff --git a/libultraship/libultraship/ImGuiImpl.cpp b/libultraship/libultraship/ImGuiImpl.cpp index f1f1fd12b..b7dd6e669 100644 --- a/libultraship/libultraship/ImGuiImpl.cpp +++ b/libultraship/libultraship/ImGuiImpl.cpp @@ -1393,19 +1393,43 @@ namespace SohImGui { for (const auto& category : windowCategories) { ImGui::SetCursorPosY(0.0f); - if (ImGui::BeginMenu(category.first.c_str())) { - for (const std::string& name : category.second) { - std::string varName(name); - varName.erase(std::remove_if(varName.begin(), varName.end(), [](unsigned char x) { return std::isspace(x); }), varName.end()); - std::string toggleName = "g" + varName + "Enabled"; + if (category.first != "Randomizer") { + if (ImGui::BeginMenu(category.first.c_str())) { + for (const std::string& name : category.second) { + std::string varName(name); + varName.erase(std::remove_if(varName.begin(), varName.end(), [](unsigned char x) { return std::isspace(x); }), varName.end()); + std::string toggleName = "g" + varName + "Enabled"; - EnhancementCheckbox(name.c_str(), toggleName.c_str()); - customWindows[name].enabled = CVar_GetS32(toggleName.c_str(), 0); + EnhancementCheckbox(name.c_str(), toggleName.c_str()); + customWindows[name].enabled = CVar_GetS32(toggleName.c_str(), 0); + } + ImGui::EndMenu(); } - ImGui::EndMenu(); } } + // Randomizer Menu + ImGui::SetCursorPosY(0.0f); + if (ImGui::BeginMenu("Randomizer")) + { + EnhancementCheckbox("Randomizer Settings", "gRandomizerSettingsEnabled"); + customWindows["Randomizer Settings"].enabled = CVar_GetS32("gRandomizerSettingsEnabled", 0); + EnhancementCheckbox("Item Tracker", "gItemTrackerEnabled"); + customWindows["Item Tracker"].enabled = CVar_GetS32("gItemTrackerEnabled", 0); + + ImGui::Separator(); + if (ImGui::BeginMenu("Rando Enhancements")) + { + EnhancementCheckbox("Quest Item Fanfares", "gRandoQuestItemFanfares"); + Tooltip( + "Play unique fanfares when obtaining quest items\n" + "(medallions/stones/songs). Note that these fanfares\n" + "are longer than usual." + ); + ImGui::EndMenu(); + } + ImGui::EndMenu(); + } ImGui::EndMenuBar(); } diff --git a/soh/soh/Enhancements/randomizer/3drando/settings.cpp b/soh/soh/Enhancements/randomizer/3drando/settings.cpp index d0997a640..6bf207069 100644 --- a/soh/soh/Enhancements/randomizer/3drando/settings.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/settings.cpp @@ -2463,6 +2463,17 @@ namespace Settings { //Function to set flags depending on settings void UpdateSettings(std::unordered_map cvarSettings) { + // RANDTODO: Switch this back once all logic options are implemented + // Logic.SetSelectedIndex(cvarSettings[RSK_LOGIC_RULES]); + switch (cvarSettings[RSK_LOGIC_RULES]) { + case 0: + Logic.SetSelectedIndex(0); + break; + case 1: + Logic.SetSelectedIndex(2); + break; + } + OpenForest.SetSelectedIndex(cvarSettings[RSK_FOREST]); OpenKakariko.SetSelectedIndex(cvarSettings[RSK_KAK_GATE]); ZorasFountain.SetSelectedIndex(cvarSettings[RSK_ZORAS_FOUNTAIN]); diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index 3b035b6ae..2fbe57785 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -3414,6 +3414,7 @@ void GenerateRandomizerImgui() { Game::SaveSettings(); std::unordered_map cvarSettings; + cvarSettings[RSK_LOGIC_RULES] = CVar_GetS32("gRandomizeLogicRules", 0); cvarSettings[RSK_FOREST] = CVar_GetS32("gRandomizeForest", 0); cvarSettings[RSK_KAK_GATE] = CVar_GetS32("gRandomizeKakarikoGate", 0); cvarSettings[RSK_DOOR_OF_TIME] = CVar_GetS32("gRandomizeDoorOfTime", 0); @@ -3497,6 +3498,9 @@ void DrawRandoEditor(bool& open) { } // Randomizer settings + // Logic Settings + const char* randoLogicRules[2] = { "Glitchless", "No logic"}; + // Open Settings const char* randoForest[3] = { "Closed", "Closed Deku", "Open" }; const char* randoKakarikoGate[2] = { "Closed", "Open" }; @@ -3745,6 +3749,20 @@ void DrawRandoEditor(bool& open) { if (CVar_GetS32("gRandomizer", 0) == 1 && ImGui::BeginTabBar("Randomizer Settings", ImGuiTabBarFlags_NoCloseWithMiddleMouseButton)) { if (ImGui::BeginTabItem("Main Rules")) { ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, cellPadding); + if (ImGui::BeginTable("tableRandoLogic", 1, ImGuiTableFlags_BordersH | ImGuiTableFlags_BordersV)) { + ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 200.0f); + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::PushItemWidth(170.0); + ImGui::Text("Logic Rules"); + InsertHelpHoverText("Glitchless - No glitches are required, but may require some minor tricks.\n" + "\n" + "No logic - Item placement is completely random. MAY BE IMPOSSIBLE TO BEAT." + ); + SohImGui::EnhancementCombobox("gRandomizeLogicRules", randoLogicRules, 2, 0); + ImGui::PopItemWidth(); + ImGui::EndTable(); + } if (ImGui::BeginTable("tableRandoMainRules", 3, ImGuiTableFlags_BordersH | ImGuiTableFlags_BordersV)) { ImGui::TableSetupColumn("Open Settings", ImGuiTableColumnFlags_WidthStretch, 200.0f); ImGui::TableSetupColumn("Shuffle Settings", ImGuiTableColumnFlags_WidthStretch, 200.0f); diff --git a/soh/soh/Enhancements/randomizer/randomizerTypes.h b/soh/soh/Enhancements/randomizer/randomizerTypes.h index 95b82127e..04e647783 100644 --- a/soh/soh/Enhancements/randomizer/randomizerTypes.h +++ b/soh/soh/Enhancements/randomizer/randomizerTypes.h @@ -952,6 +952,7 @@ typedef enum { typedef enum { RSK_NONE, + RSK_LOGIC_RULES, RSK_FOREST, RSK_KAK_GATE, RSK_DOOR_OF_TIME, diff --git a/soh/src/code/code_800EC960.c b/soh/src/code/code_800EC960.c index cab454b13..a0b08ff14 100644 --- a/soh/src/code/code_800EC960.c +++ b/soh/src/code/code_800EC960.c @@ -1246,6 +1246,7 @@ void Audio_StepFreqLerp(FreqLerp* lerp); void func_800F56A8(void); void Audio_PlayNatureAmbienceSequence(u8 natureAmbienceId); s32 Audio_SetGanonDistVol(u8 targetVol); +void Audio_PlayFanfare_Rando(ItemID getItemId); // Function originally not called, so repurposing for DPad input void func_800EC960(u8 dpad) { @@ -3889,6 +3890,49 @@ void Audio_ResetSfxChannelState(void) { sAudioCodeReverb = 0; } +// Function to play "get-item" fanfares according to the type of item obtained (used in rando) +// Longer fanfares for medallions/stones/songs are behind the Cvar +void Audio_PlayFanfare_Rando(ItemID getItemId) { + s32 temp1; + + if (((getItemId >= GI_RUPEE_GREEN) && (getItemId <= GI_RUPEE_RED)) || + ((getItemId >= GI_RUPEE_PURPLE) && (getItemId <= GI_RUPEE_GOLD)) || + ((getItemId >= GI_RUPEE_GREEN_LOSE) && (getItemId <= GI_RUPEE_PURPLE_LOSE)) || (getItemId == GI_HEART)) { + Audio_PlaySoundGeneral(NA_SE_SY_GET_BOXITEM, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } else { + if ((getItemId == GI_HEART_CONTAINER_2) || (getItemId == GI_HEART_CONTAINER) || + ((getItemId == GI_HEART_PIECE) && ((gSaveContext.inventory.questItems & 0xF0000000) == 0x40000000))) { + temp1 = NA_BGM_HEART_GET | 0x900; + } else { + temp1 = (getItemId == GI_HEART_PIECE) ? NA_BGM_SMALL_ITEM_GET : NA_BGM_ITEM_GET | 0x900; + } + // If we get a skulltula token or the "WINNER" heart, play "get small item" + if (getItemId == GI_SKULL_TOKEN || getItemId == GI_HEART_PIECE_WIN) { + temp1 = NA_BGM_SMALL_ITEM_GET | 0x900; + } + // But if the "WINNER" heart is the 4th heart piece collected, play "get heart container" + if (getItemId == GI_HEART_PIECE_WIN && ((gSaveContext.inventory.questItems & 0xF0000000) == 0x40000000)) { + temp1 = NA_BGM_HEART_GET | 0x900; + } + // If the setting is toggled on and we get special quest items (longer fanfares): + if (CVar_GetS32("gRandoQuestItemFanfares", 0) != 0) { + // If we get a medallion, play the "get a medallion" fanfare + if ((getItemId >= GI_MEDALLION_LIGHT) && (getItemId <= GI_MEDALLION_SPIRIT)) { + temp1 = NA_BGM_MEDALLION_GET | 0x900; + } + // If it's a Spiritual Stone, play the "get a spiritual stone" fanfare + if ((getItemId >= GI_STONE_KOKIRI) && (getItemId <= GI_STONE_ZORA)) { + temp1 = NA_BGM_SPIRITUAL_STONE | 0x900; + } + // If the item we're getting is a song, play the "learned a song" fanfare + if ((getItemId >= GI_ZELDAS_LULLABY) && (getItemId <= GI_PRELUDE_OF_LIGHT)) { + temp1 = NA_BGM_OCA_FAIRY_GET | 0x900; + } + } + Audio_PlayFanfare(temp1); + } +} + void func_800F3F3C(u8 arg0) { if (gSoundBankMuted[0] != 1) { Audio_StartSeq(SEQ_PLAYER_BGM_SUB, 0, NA_BGM_VARIOUS_SFX); diff --git a/soh/src/overlays/actors/ovl_En_Si/z_en_si.c b/soh/src/overlays/actors/ovl_En_Si/z_en_si.c index aa67f1a20..944a62850 100644 --- a/soh/src/overlays/actors/ovl_En_Si/z_en_si.c +++ b/soh/src/overlays/actors/ovl_En_Si/z_en_si.c @@ -115,7 +115,12 @@ void func_80AFB768(EnSi* this, GlobalContext* globalCtx) { player->actor.freezeTimer = 20; } Message_StartTextbox(globalCtx, textId, NULL); - Audio_PlayFanfare(NA_BGM_SMALL_ITEM_GET); + + if (gSaveContext.n64ddFlag) { + Audio_PlayFanfare_Rando(getItemId); + } else { + Audio_PlayFanfare(NA_BGM_SMALL_ITEM_GET); + } this->actionFunc = func_80AFB950; } else { Collider_UpdateCylinder(&this->actor, &this->collider); @@ -149,7 +154,11 @@ void func_80AFB89C(EnSi* this, GlobalContext* globalCtx) { Item_Give(globalCtx, giveItemId); } Message_StartTextbox(globalCtx, textId, NULL); - Audio_PlayFanfare(NA_BGM_SMALL_ITEM_GET); + if (gSaveContext.n64ddFlag) { + Audio_PlayFanfare_Rando(getItemId); + } else { + Audio_PlayFanfare(NA_BGM_SMALL_ITEM_GET); + } this->actionFunc = func_80AFB950; } } diff --git a/soh/src/overlays/actors/ovl_player_actor/z_player.c b/soh/src/overlays/actors/ovl_player_actor/z_player.c index 569ec2802..d7faa19cb 100644 --- a/soh/src/overlays/actors/ovl_player_actor/z_player.c +++ b/soh/src/overlays/actors/ovl_player_actor/z_player.c @@ -12684,7 +12684,11 @@ s32 func_8084DFF4(GlobalContext* globalCtx, Player* this) { Message_StartTextbox(globalCtx, giEntry->textId, &this->actor); Item_Give(globalCtx, giEntry->itemId); - if (((this->getItemId >= GI_RUPEE_GREEN) && (this->getItemId <= GI_RUPEE_RED)) || + // In rando the fanfares are handled by a function in code_800EC960.c + if (gSaveContext.n64ddFlag) { + Audio_PlayFanfare_Rando(this->getItemId); + } + else if (((this->getItemId >= GI_RUPEE_GREEN) && (this->getItemId <= GI_RUPEE_RED)) || ((this->getItemId >= GI_RUPEE_PURPLE) && (this->getItemId <= GI_RUPEE_GOLD)) || ((this->getItemId >= GI_RUPEE_GREEN_LOSE) && (this->getItemId <= GI_RUPEE_PURPLE_LOSE)) || (this->getItemId == GI_HEART)) {