Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 64 additions & 25 deletions soh/soh/Enhancements/audio/AudioEditor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -78,6 +77,14 @@ size_t AuthenticCountBySequenceType(SeqType type) {
}
}

static const std::unordered_map<int32_t, const char*> 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() {
Expand All @@ -100,9 +107,19 @@ void UpdateCurrentBGM(u16 seqKey, SeqType seqType) {
}
}

void RandomizeGroup(SeqType type) {
void RandomizeGroup(SeqType type, bool manual = true) {
std::vector<u16> 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<uint32_t>(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();
Expand Down Expand Up @@ -478,23 +495,37 @@ void DrawTypeChip(SeqType type, std::string sequenceName) {

void AudioEditorRegisterOnSceneInitHook() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneInit>([](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<GameInteractor::OnGenerationCompletion>([]() {
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<GameInteractor::OnLoadGame>([](int32_t fileNum) {
Copy link
Contributor

@serprex serprex Jan 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be using COND_HOOK, but I can make a follow up PR converting the other 2 functions to hooks too

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Want me to make a PR converting those functions to use COND_HOOK ? I was working on it before I realized this PR got merged lol.

if (CVarGetInteger(CVAR_AUDIO("RandomizeAudioGenModes"), 0) == RANDOMIZE_ON_FILE_LOAD ||
CVarGetInteger(CVAR_AUDIO("RandomizeAudioGenModes"), 0) == RANDOMIZE_ON_FILE_LOAD_SEEDED) {

AudioEditor_AutoRandomizeAll();
}
});
}

void AudioEditor::InitElement() {
AudioEditorRegisterOnSceneInitHook();
AudioEditorRegisterOnGenerationCompletionHook();
AudioEditorRegisterOnLoadGameHook();
}

void AudioEditor::DrawElement() {
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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"))
Expand Down
1 change: 1 addition & 0 deletions soh/soh/Enhancements/audio/AudioEditor.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
90 changes: 71 additions & 19 deletions soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -96,6 +99,14 @@ std::map<CosmeticGroup, const char*> groupLabels = {
{ COSMETICS_GROUP_MESSAGE, "Message" },
};

static const std::unordered_map<int32_t, const char*> 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;
Expand Down Expand Up @@ -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<uint32_t>(gSaveContext.ship.stats.fileCreatedAt));

randomColor = GetRandomValue(finalSeed);
} else {
randomColor = GetRandomValue();
}

Color_RGBA8 newColor;
newColor.r = static_cast<uint8_t>(randomColor.x * 255.0f);
newColor.g = static_cast<uint8_t>(randomColor.y * 255.0f);
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -2573,6 +2598,10 @@ void CosmeticsEditorWindow::DrawElement() {
UIWidgets::PopStyleTabs();
}

void RegisterOnGameFrameUpdateHook() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() { CosmeticsUpdateTick(); });
}

void CosmeticsEditorWindow::InitElement() {
// Convert the `current color` into the format that the ImGui color picker expects
for (auto& [id, cosmeticOption] : cosmeticOptions) {
Expand Down Expand Up @@ -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) &&
Expand Down Expand Up @@ -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);
}
Expand All @@ -2664,7 +2717,6 @@ void RegisterCosmeticWidgets() {
}

static RegisterShipInitFunc initFunc(RegisterCosmeticHooks, {
CVAR_COSMETIC("RandomizeAllOnNewScene"),
CVAR_COSMETIC("RandomizeAllOnRandoGen"),
CVAR_COSMETIC("RandomizeCosmeticsGenModes"),
});
static RegisterMenuInitFunc menuInitFunc(RegisterCosmeticWidgets);
1 change: 1 addition & 0 deletions soh/soh/Enhancements/cosmetics/CosmeticsEditor.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
8 changes: 8 additions & 0 deletions soh/soh/Enhancements/enhancementTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
15 changes: 15 additions & 0 deletions soh/soh/SohGui/UIWidgets.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Copy link
Contributor

@serprex serprex Jan 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to go with #5979 over using mersenne twister, which is a bad RNG

(tho that's blocked on making our current PCG code accessible for modular use)

but since this is copying code above, this'll do for now, & can be handled by #5979 itself

#endif
std::uniform_int_distribution<int> 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;
Expand Down
2 changes: 2 additions & 0 deletions soh/soh/SohGui/UIWidgets.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Loading