diff --git a/soh/soh/Enhancements/audio/AudioEditor.cpp b/soh/soh/Enhancements/audio/AudioEditor.cpp index fa07272183a..d951f5eb4d2 100644 --- a/soh/soh/Enhancements/audio/AudioEditor.cpp +++ b/soh/soh/Enhancements/audio/AudioEditor.cpp @@ -33,8 +33,7 @@ static WidgetInfo leadingMusic; static WidgetInfo displaySeqName; static WidgetInfo ovlDuration; static WidgetInfo voicePitch; -static WidgetInfo randoMusicOnSceneChange; -static WidgetInfo randomAudioOnSeedGen; +static WidgetInfo randomAudioGenModes; static WidgetInfo lowerOctaves; namespace SohGui { @@ -78,6 +77,14 @@ size_t AuthenticCountBySequenceType(SeqType type) { } } +static const std::unordered_map audioRandomizerModes = { + { RANDOMIZE_OFF, "Manual" }, + { RANDOMIZE_ON_NEW_SCENE, "On New Scene" }, + { RANDOMIZE_ON_RANDO_GEN_ONLY, "On Rando Gen Only" }, + { RANDOMIZE_ON_FILE_LOAD, "On File Load" }, + { RANDOMIZE_ON_FILE_LOAD_SEEDED, "On File Load (Seeded)" }, +}; + // Grabs the current BGM sequence ID and replays it // which will lookup the proper override, or reset back to vanilla void ReplayCurrentBGM() { @@ -100,9 +107,19 @@ void UpdateCurrentBGM(u16 seqKey, SeqType seqType) { } } -void RandomizeGroup(SeqType type) { +void RandomizeGroup(SeqType type, bool manual = true) { std::vector values; + if (!manual) { + if (CVarGetInteger(CVAR_AUDIO("RandomizeAudioGenModes"), 0) == RANDOMIZE_ON_FILE_LOAD_SEEDED || + CVarGetInteger(CVAR_AUDIO("RandomizeAudioGenModes"), 0) == RANDOMIZE_ON_RANDO_GEN_ONLY) { + + uint32_t finalSeed = type + (IS_RANDO ? Rando::Context::GetInstance()->GetSeed() + : static_cast(gSaveContext.ship.stats.fileCreatedAt)); + Random_Init(finalSeed); + } + } + // An empty IncludedSequences set means that the AudioEditor window has never been drawn if (AudioCollection::Instance->GetIncludedSequences().empty()) { AudioCollection::Instance->InitializeShufflePool(); @@ -478,16 +495,29 @@ void DrawTypeChip(SeqType type, std::string sequenceName) { void AudioEditorRegisterOnSceneInitHook() { GameInteractor::Instance->RegisterGameHook([](int16_t sceneNum) { - if (gSaveContext.gameMode != GAMEMODE_END_CREDITS && CVarGetInteger(CVAR_AUDIO("RandomizeAllOnNewScene"), 0)) { - AudioEditor_RandomizeAll(); + if (gSaveContext.gameMode != GAMEMODE_END_CREDITS && + CVarGetInteger(CVAR_AUDIO("RandomizeAudioGenModes"), 0) == RANDOMIZE_ON_NEW_SCENE) { + + AudioEditor_AutoRandomizeAll(); } }); } void AudioEditorRegisterOnGenerationCompletionHook() { GameInteractor::Instance->RegisterGameHook([]() { - if (CVarGetInteger(CVAR_AUDIO("RandomizeAllOnRandoGen"), 0)) { - AudioEditor_RandomizeAll(); + if (CVarGetInteger(CVAR_AUDIO("RandomizeAudioGenModes"), 0) == RANDOMIZE_ON_RANDO_GEN_ONLY) { + + AudioEditor_AutoRandomizeAll(); + } + }); +} + +void AudioEditorRegisterOnLoadGameHook() { + GameInteractor::Instance->RegisterGameHook([](int32_t fileNum) { + if (CVarGetInteger(CVAR_AUDIO("RandomizeAudioGenModes"), 0) == RANDOMIZE_ON_FILE_LOAD || + CVarGetInteger(CVAR_AUDIO("RandomizeAudioGenModes"), 0) == RANDOMIZE_ON_FILE_LOAD_SEEDED) { + + AudioEditor_AutoRandomizeAll(); } }); } @@ -495,6 +525,7 @@ void AudioEditorRegisterOnGenerationCompletionHook() { void AudioEditor::InitElement() { AudioEditorRegisterOnSceneInitHook(); AudioEditorRegisterOnGenerationCompletionHook(); + AudioEditorRegisterOnLoadGameHook(); } void AudioEditor::DrawElement() { @@ -556,8 +587,7 @@ void AudioEditor::DrawElement() { UIWidgets::ButtonOptions().Size(ImVec2(80, 36)).Padding(ImVec2(5.0f, 0.0f)))) { CVarSetFloat(CVAR_AUDIO("LinkVoiceFreqMultiplier"), 1.0f); } - SohGui::mSohMenu->MenuDrawItem(randoMusicOnSceneChange, ImGui::GetContentRegionAvail().x, THEME_COLOR); - SohGui::mSohMenu->MenuDrawItem(randomAudioOnSeedGen, ImGui::GetContentRegionAvail().x, THEME_COLOR); + SohGui::mSohMenu->MenuDrawItem(randomAudioGenModes, ImGui::GetContentRegionAvail().x, THEME_COLOR); SohGui::mSohMenu->MenuDrawItem(lowerOctaves, ImGui::GetContentRegionAvail().x, THEME_COLOR); } ImGui::EndChild(); @@ -774,6 +804,15 @@ void AudioEditor_RandomizeAll() { ReplayCurrentBGM(); } +void AudioEditor_AutoRandomizeAll() { + for (auto type : allTypes) { + RandomizeGroup(type, false); + } + + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); + ReplayCurrentBGM(); +} + void AudioEditor_RandomizeGroup(SeqType group) { RandomizeGroup(group); @@ -865,22 +904,22 @@ void RegisterAudioWidgets() { .Size(ImVec2(300.0f, 0.0f))); SohGui::mSohMenu->AddSearchWidget({ voicePitch, "Enhancements", "Audio Editor", "Audio Options" }); - randoMusicOnSceneChange = { .name = "Randomize All Music and Sound Effects on New Scene", - .type = WidgetType::WIDGET_CVAR_CHECKBOX }; - randoMusicOnSceneChange.CVar(CVAR_AUDIO("RandomizeAllOnNewScene")) - .Options(CheckboxOptions() - .Color(THEME_COLOR) - .Tooltip("Enables randomizing all unlocked music and sound effects when you enter a new scene.")); - SohGui::mSohMenu->AddSearchWidget({ randoMusicOnSceneChange, "Enhancements", "Audio Editor", "Audio Options" }); - - randomAudioOnSeedGen = { .name = "Randomize All Music and Sound Effects on Randomizer Generation", - .type = WidgetType::WIDGET_CVAR_CHECKBOX }; - randomAudioOnSeedGen.CVar(CVAR_AUDIO("RandomizeAllOnRandoGen")) - .Options(CheckboxOptions() - .Color(THEME_COLOR) - .Tooltip("Enables randomizing all unlocked music and sound effects when you generate a new " - "randomizer. Respects locks already in place.")); - SohGui::mSohMenu->AddSearchWidget({ randomAudioOnSeedGen, "Enhancements", "Audio Editor", "Audio Options" }); + randomAudioGenModes = { .name = "Automatically Randomize All Music and Sound Effects", + .type = WidgetType::WIDGET_CVAR_COMBOBOX }; + randomAudioGenModes.CVar(CVAR_AUDIO("RandomizeAudioGenModes")) + .Options( + ComboboxOptions() + .DefaultIndex(RANDOMIZE_OFF) + .ComboMap(audioRandomizerModes) + .Tooltip( + "Set when the music and sound effects is automaticly randomized:\n" + "- Manual: Manually randomize music or sound effects by pressing the 'Randomize all Groups' " + "button\n" + "- On New Scene : Randomizes when you enter a new scene.\n" + "- On Rando Gen Only: Randomizes only when you generate a new randomizer.\n" + "- On File Load: Randomizes on File Load.\n" + "- On File Load (Seeded): Randomizes on file load based on the current randomizer seed/file.\n")); + SohGui::mSohMenu->AddSearchWidget({ randomAudioGenModes, "Enhancements", "Audio Editor", "Audio Options" }); lowerOctaves = { .name = "Lower Octaves of Unplayable High Notes", .type = WidgetType::WIDGET_CVAR_CHECKBOX }; lowerOctaves.CVar(CVAR_AUDIO("ExperimentalOctaveDrop")) diff --git a/soh/soh/Enhancements/audio/AudioEditor.h b/soh/soh/Enhancements/audio/AudioEditor.h index ca3636d9a84..b05c7b88e2e 100644 --- a/soh/soh/Enhancements/audio/AudioEditor.h +++ b/soh/soh/Enhancements/audio/AudioEditor.h @@ -18,6 +18,7 @@ class AudioEditor final : public Ship::GuiWindow { }; void AudioEditor_RandomizeAll(); +void AudioEditor_AutoRandomizeAll(); void AudioEditor_RandomizeGroup(SeqType group); void AudioEditor_ResetAll(); void AudioEditor_ResetGroup(SeqType group); diff --git a/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp b/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp index 94222b0b715..3a475516bad 100644 --- a/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp +++ b/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp @@ -13,9 +13,11 @@ #include "soh/SohGui/SohGui.hpp" #include "soh/OTRGlobals.h" #include "soh/ResourceManagerHelpers.h" +#include "soh/Enhancements/enhancementTypes.h" extern "C" { #include "z64.h" +#include "z64save.h" #include "macros.h" #include "soh/cvar_prefixes.h" #include "objects/object_link_boy/object_link_boy.h" @@ -47,6 +49,7 @@ extern "C" { #include "objects/object_gi_rabit_mask/object_gi_rabit_mask.h" #include "overlays/ovl_Magic_Wind/ovl_Magic_Wind.h" +extern SaveContext gSaveContext; extern PlayState* gPlayState; void ResourceMgr_PatchGfxByName(const char* path, const char* patchName, int index, Gfx instruction); void ResourceMgr_PatchGfxCopyCommandByName(const char* path, const char* patchName, int destinationIndex, @@ -96,6 +99,14 @@ std::map groupLabels = { { COSMETICS_GROUP_MESSAGE, "Message" }, }; +static const std::unordered_map cosmeticsRandomizerModes = { + { RANDOMIZE_OFF, "Manual" }, + { RANDOMIZE_ON_NEW_SCENE, "On New Scene" }, + { RANDOMIZE_ON_RANDO_GEN_ONLY, "On Rando Gen Only" }, + { RANDOMIZE_ON_FILE_LOAD, "On File Load" }, + { RANDOMIZE_ON_FILE_LOAD_SEEDED, "On File Load (Seeded)" }, +}; + typedef struct { const char* cvar; const char* valuesCvar; @@ -2093,8 +2104,22 @@ void ApplySideEffects(CosmeticOption& cosmeticOption) { } } -void RandomizeColor(CosmeticOption& cosmeticOption) { - ImVec4 randomColor = GetRandomValue(); +void RandomizeColor(CosmeticOption& cosmeticOption, bool manual = true) { + ImVec4 randomColor; + + if (!manual && CVarGetInteger(CVAR_COSMETIC("RandomizeCosmeticsGenModes"), 0) == RANDOMIZE_ON_FILE_LOAD_SEEDED || + !manual && CVarGetInteger(CVAR_COSMETIC("RandomizeCosmeticsGenModes"), 0) == RANDOMIZE_ON_RANDO_GEN_ONLY) { + + uint32_t finalSeed = cosmeticOption.defaultColor.r + cosmeticOption.defaultColor.g + + cosmeticOption.defaultColor.b + cosmeticOption.defaultColor.a + + (IS_RANDO ? Rando::Context::GetInstance()->GetSeed() + : static_cast(gSaveContext.ship.stats.fileCreatedAt)); + + randomColor = GetRandomValue(finalSeed); + } else { + randomColor = GetRandomValue(); + } + Color_RGBA8 newColor; newColor.r = static_cast(randomColor.x * 255.0f); newColor.g = static_cast(randomColor.y * 255.0f); @@ -2372,17 +2397,17 @@ void CosmeticsEditorWindow::DrawElement() { .Step(0.01f) .Size(ImVec2(300.0f, 0.0f)) .Color(THEME_COLOR)); - ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0)); - UIWidgets::CVarCheckbox("Randomize All on New Scene", CVAR_COSMETIC("RandomizeAllOnNewScene"), - UIWidgets::CheckboxOptions() - .Color(THEME_COLOR) - .Tooltip("Enables randomizing all unlocked cosmetics when you enter a new scene.")); - ImGui::EndDisabled(); - UIWidgets::CVarCheckbox( - "Randomize All on Randomizer Generation", CVAR_COSMETIC("RandomizeAllOnRandoGen"), - UIWidgets::CheckboxOptions() + UIWidgets::CVarCombobox( + "Automatically Randomize All Cosmetics", CVAR_COSMETIC("RandomizeCosmeticsGenModes"), cosmeticsRandomizerModes, + UIWidgets::ComboboxOptions() + .DefaultIndex(RANDOMIZE_OFF) .Color(THEME_COLOR) - .Tooltip("Enables randomizing all unlocked cosmetics when you generate a new randomizer.")); + .Tooltip("Set when the cosmetics is automaticly randomized:\n" + "- Manual: Manually randomize cosmetics by pressing the 'Randomize all' button\n" + "- On New Scene : Randomizes when you enter a new scene.\n" + "- On Rando Gen Only: Randomizes only when you generate a new randomizer.\n" + "- On File Load: Randomizes on File Load.\n" + "- On File Load (Seeded): Randomizes on file load based on the current randomizer seed/file.\n")); UIWidgets::CVarCheckbox( "Advanced Mode", CVAR_COSMETIC("AdvancedMode"), UIWidgets::CheckboxOptions() @@ -2573,6 +2598,10 @@ void CosmeticsEditorWindow::DrawElement() { UIWidgets::PopStyleTabs(); } +void RegisterOnGameFrameUpdateHook() { + GameInteractor::Instance->RegisterGameHook([]() { CosmeticsUpdateTick(); }); +} + void CosmeticsEditorWindow::InitElement() { // Convert the `current color` into the format that the ImGui color picker expects for (auto& [id, cosmeticOption] : cosmeticOptions) { @@ -2602,6 +2631,18 @@ void CosmeticsEditor_RandomizeAll() { ApplyOrResetCustomGfxPatches(); } +void CosmeticsEditor_AutoRandomizeAll() { + for (auto& [id, cosmeticOption] : cosmeticOptions) { + if (!CVarGetInteger(cosmeticOption.lockedCvar, 0) && + (!cosmeticOption.advancedOption || CVarGetInteger(CVAR_COSMETIC("AdvancedMode"), 0))) { + RandomizeColor(cosmeticOption, false); + } + } + + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); + ApplyOrResetCustomGfxPatches(); +} + void CosmeticsEditor_RandomizeGroup(CosmeticGroup group) { for (auto& [id, cosmeticOption] : cosmeticOptions) { if (!CVarGetInteger(cosmeticOption.lockedCvar, 0) && @@ -2638,13 +2679,25 @@ void CosmeticsEditor_ResetGroup(CosmeticGroup group) { } void RegisterCosmeticHooks() { - COND_HOOK(OnSceneInit, CVarGetInteger(CVAR_COSMETIC("RandomizeAllOnNewScene"), 0), - [](s16 sceneNum) { CosmeticsEditor_RandomizeAll(); }); + COND_HOOK(OnGenerationCompletion, + CVarGetInteger(CVAR_COSMETIC("RandomizeCosmeticsGenModes"), RANDOMIZE_OFF) == RANDOMIZE_ON_RANDO_GEN_ONLY, + []() { CosmeticsEditor_AutoRandomizeAll(); }); + + COND_HOOK(OnLoadGame, CVarGetInteger(CVAR_COSMETIC("RandomizeCosmeticsGenModes"), RANDOMIZE_OFF) == RANDOMIZE_OFF, + [](s32 fileNum) { ApplyOrResetCustomGfxPatches(); }); + + COND_HOOK(OnLoadGame, + CVarGetInteger(CVAR_COSMETIC("RandomizeCosmeticsGenModes"), RANDOMIZE_OFF) == RANDOMIZE_ON_FILE_LOAD, + [](s32 fileNum) { CosmeticsEditor_AutoRandomizeAll(); }); - COND_HOOK(OnGenerationCompletion, CVarGetInteger(CVAR_COSMETIC("RandomizeAllOnRandoGen"), 0), - []() { CosmeticsEditor_RandomizeAll(); }); + COND_HOOK(OnLoadGame, + CVarGetInteger(CVAR_COSMETIC("RandomizeCosmeticsGenModes"), RANDOMIZE_OFF) == + RANDOMIZE_ON_FILE_LOAD_SEEDED, + [](s32 fileNum) { CosmeticsEditor_AutoRandomizeAll(); }); - COND_HOOK(OnLoadGame, true, [](int32_t fileNum) { ApplyOrResetCustomGfxPatches(); }); + COND_HOOK(OnSceneInit, + CVarGetInteger(CVAR_COSMETIC("RandomizeCosmeticsGenModes"), RANDOMIZE_OFF) == RANDOMIZE_ON_NEW_SCENE, + [](s16 sceneNum) { CosmeticsEditor_AutoRandomizeAll(); }); COND_HOOK(OnGameFrameUpdate, true, CosmeticsUpdateTick); } @@ -2664,7 +2717,6 @@ void RegisterCosmeticWidgets() { } static RegisterShipInitFunc initFunc(RegisterCosmeticHooks, { - CVAR_COSMETIC("RandomizeAllOnNewScene"), - CVAR_COSMETIC("RandomizeAllOnRandoGen"), + CVAR_COSMETIC("RandomizeCosmeticsGenModes"), }); static RegisterMenuInitFunc menuInitFunc(RegisterCosmeticWidgets); diff --git a/soh/soh/Enhancements/cosmetics/CosmeticsEditor.h b/soh/soh/Enhancements/cosmetics/CosmeticsEditor.h index e10583081d7..7da88bdbc6b 100644 --- a/soh/soh/Enhancements/cosmetics/CosmeticsEditor.h +++ b/soh/soh/Enhancements/cosmetics/CosmeticsEditor.h @@ -55,6 +55,7 @@ static ImGuiTableColumnFlags FlagsCell = ImGuiTableColumnFlags_WidthStretch | ImGuiTableColumnFlags_IndentEnable | ImGuiTableColumnFlags_NoSort; void CosmeticsEditor_RandomizeAll(); +void CosmeticsEditor_AutoRandomizeAll(); void CosmeticsEditor_RandomizeGroup(CosmeticGroup group); void CosmeticsEditor_ResetAll(); void CosmeticsEditor_ResetGroup(CosmeticGroup group); diff --git a/soh/soh/Enhancements/enhancementTypes.h b/soh/soh/Enhancements/enhancementTypes.h index bd025115b90..be0a3990f3a 100644 --- a/soh/soh/Enhancements/enhancementTypes.h +++ b/soh/soh/Enhancements/enhancementTypes.h @@ -123,4 +123,12 @@ typedef enum { WATERFALL_NEVER, } SleepingWaterfallType; +typedef enum { + RANDOMIZE_OFF, + RANDOMIZE_ON_NEW_SCENE, + RANDOMIZE_ON_RANDO_GEN_ONLY, + RANDOMIZE_ON_FILE_LOAD, + RANDOMIZE_ON_FILE_LOAD_SEEDED, +} RandomizeOnMode; + #endif diff --git a/soh/soh/SohGui/UIWidgets.cpp b/soh/soh/SohGui/UIWidgets.cpp index ca5b4be9b86..9a054735289 100644 --- a/soh/soh/SohGui/UIWidgets.cpp +++ b/soh/soh/SohGui/UIWidgets.cpp @@ -1170,6 +1170,21 @@ ImVec4 GetRandomValue() { return NewColor; } +ImVec4 GetRandomValue(uint32_t seed) { +#if !defined(__SWITCH__) && !defined(__WIIU__) + std::mt19937 rng(seed); +#else + std::mt19937_64 rng(seed); +#endif + std::uniform_int_distribution dist(0, 255 - 1); + + ImVec4 NewColor; + NewColor.x = (float)(dist(rng)) / 255.0f; + NewColor.y = (float)(dist(rng)) / 255.0f; + NewColor.z = (float)(dist(rng)) / 255.0f; + return NewColor; +} + Color_RGBA8 RGBA8FromVec(ImVec4 vec) { Color_RGBA8 color = { vec.x * 255, vec.y * 255, vec.z * 255, vec.w * 255 }; return color; diff --git a/soh/soh/SohGui/UIWidgets.hpp b/soh/soh/SohGui/UIWidgets.hpp index 95590b1ce5c..9dacf95fcc2 100644 --- a/soh/soh/SohGui/UIWidgets.hpp +++ b/soh/soh/SohGui/UIWidgets.hpp @@ -1047,7 +1047,9 @@ void DrawFlagArray8Mask(const std::string& name, uint8_t& flags, Colors color = void InsertHelpHoverText(const std::string& text); void InsertHelpHoverText(const char* text); } // namespace UIWidgets + ImVec4 GetRandomValue(); +ImVec4 GetRandomValue(uint32_t seed); Color_RGBA8 RGBA8FromVec(ImVec4 vec); ImVec4 VecFromRGBA8(Color_RGBA8 color);