diff --git a/soh/soh/Enhancements/audio/AudioEditor.cpp b/soh/soh/Enhancements/audio/AudioEditor.cpp index 51bc7f32ca2..1baf38abfb5 100644 --- a/soh/soh/Enhancements/audio/AudioEditor.cpp +++ b/soh/soh/Enhancements/audio/AudioEditor.cpp @@ -109,6 +109,8 @@ void UpdateCurrentBGM(u16 seqKey, SeqType seqType) { } } +static uint64_t seeded_audio_state = 0; + void RandomizeGroup(SeqType type, bool manual = true) { std::vector values; @@ -118,7 +120,7 @@ void RandomizeGroup(SeqType type, bool manual = true) { uint32_t finalSeed = type + (IS_RANDO ? Rando::Context::GetInstance()->GetSeed() : static_cast(gSaveContext.ship.stats.fileCreatedAt)); - Random_Init(finalSeed); + ShipUtils::RandInit(finalSeed, &seeded_audio_state); } } @@ -139,7 +141,7 @@ void RandomizeGroup(SeqType type, bool manual = true) { if (!values.size()) return; } - Shuffle(values); + ShipUtils::Shuffle(values, &seeded_audio_state); for (const auto& [seqId, seqData] : AudioCollection::Instance->GetAllSequences()) { const std::string cvarKey = AudioCollection::Instance->GetCvarKey(seqData.sfxKey); const std::string cvarLockKey = AudioCollection::Instance->GetCvarLockKey(seqData.sfxKey); diff --git a/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp b/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp index b8cb0c35d81..49e74f69cf1 100644 --- a/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp +++ b/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp @@ -2104,6 +2104,8 @@ void ApplySideEffects(CosmeticOption& cosmeticOption) { } } +static uint64_t seeded_cosmetics_state = 0; + void RandomizeColor(CosmeticOption& cosmeticOption, bool manual = true) { ImVec4 randomColor; @@ -2115,7 +2117,7 @@ void RandomizeColor(CosmeticOption& cosmeticOption, bool manual = true) { (IS_RANDO ? Rando::Context::GetInstance()->GetSeed() : static_cast(gSaveContext.ship.stats.fileCreatedAt)); - randomColor = GetRandomValue(finalSeed); + randomColor = GetRandomValue(finalSeed, &seeded_cosmetics_state); } else { randomColor = GetRandomValue(); } diff --git a/soh/soh/Enhancements/randomizer/3drando/random.cpp b/soh/soh/Enhancements/randomizer/3drando/random.cpp index 4cac198183f..257fa0c1289 100644 --- a/soh/soh/Enhancements/randomizer/3drando/random.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/random.cpp @@ -5,51 +5,25 @@ #include static bool init = false; -static uint64_t state = 0; +uint64_t rando_state = 0; const uint64_t multiplier = 6364136223846793005ULL; const uint64_t increment = 11634580027462260723ULL; // Initialize with seed specified void Random_Init(uint64_t seed) { - init = true; - state = seed; + ShipUtils::RandInit(seed, &rando_state); } uint32_t next32() { - if (!init) { - // No seed given, get a random number from device to seed -#if !defined(__SWITCH__) && !defined(__WIIU__) - uint64_t seed = static_cast(std::random_device{}()); -#else - uint64_t seed = static_cast(std::hash{}(std::to_string(rand()))); -#endif - Random_Init(seed); - } - - state = state * multiplier + increment; - uint32_t xorshifted = static_cast(((state >> 18) ^ state) >> 27); - uint32_t rot = static_cast(state >> 59); - return std::rotr(xorshifted, rot); + return ShipUtils::next32(&rando_state); } // Returns a random integer in range [min, max-1] uint32_t Random(uint32_t min, uint32_t max) { - if (min == max) { - return min; - } - assert(max > min); - - uint32_t n = max - min; - uint32_t cutoff = UINT32_MAX - UINT32_MAX % static_cast(n); - for (;;) { - uint32_t r = next32(); - if (r <= cutoff) { - return min + r % n; - } - } + return ShipUtils::Random(min, max, &rando_state); } // Returns a random floating point number in [0.0, 1.0) double RandomDouble() { - return ldexp(next32(), -32); + return ShipUtils::RandomDouble(&rando_state); } diff --git a/soh/soh/Enhancements/randomizer/3drando/random.hpp b/soh/soh/Enhancements/randomizer/3drando/random.hpp index 51438947102..b08e7807f40 100644 --- a/soh/soh/Enhancements/randomizer/3drando/random.hpp +++ b/soh/soh/Enhancements/randomizer/3drando/random.hpp @@ -1,5 +1,6 @@ #pragma once +#include "soh/ShipUtils.h" #include #include #include @@ -7,37 +8,25 @@ #include #include +extern uint64_t rando_state; + void Random_Init(uint64_t seed); uint32_t Random(uint32_t min, uint32_t max); double RandomDouble(); // Get a random element from a vector or array template T RandomElement(std::vector& vector, bool erase) { - const auto idx = Random(0, static_cast(vector.size())); - const T selected = vector[idx]; - if (erase) { - vector.erase(vector.begin() + idx); - } - return selected; + return ShipUtils::RandomElement(vector, erase, &rando_state); } template auto& RandomElement(Container& container) { - return container[Random(0, static_cast(std::size(container)))]; + return ShipUtils::RandomElement(container, &rando_state); } template const auto& RandomElement(const Container& container) { - return container[Random(0, static_cast(std::size(container)))]; + return ShipUtils::RandomElement(container, &rando_state); } template const T RandomElementFromSet(const std::set& set) { - if (set.size() == 1) { - return *set.begin(); - } - uint32_t rand = Random(0, static_cast(set.size())); - auto it = set.begin(); - for (uint32_t i = 0; i < rand; i++) { - it++; - } - auto test = *it; - return *it; + return ShipUtils::RandomElementFromSet(set, &rando_state); } // Shuffle items within a vector or array diff --git a/soh/soh/ShipUtils.cpp b/soh/soh/ShipUtils.cpp index 1e914d03cfa..9fd4d1be619 100644 --- a/soh/soh/ShipUtils.cpp +++ b/soh/soh/ShipUtils.cpp @@ -1,5 +1,6 @@ #include "ShipUtils.h" #include +#include #include "soh_assets.h" extern "C" { @@ -96,3 +97,60 @@ extern "C" void* Ship_GetCharFontTexture(u8 character) { return (void*)fontTbl[adjustedChar]; } + +static bool rand_init = false; +uint64_t default_state = 0; +const uint64_t multiplier = 6364136223846793005ULL; +const uint64_t increment = 11634580027462260723ULL; + +// Initialize with seed specified +void ShipUtils::RandInit(uint64_t seed, uint64_t* state) { + rand_init = true; + if (state == nullptr) { + state = &default_state; + } + *state = seed; +} + +uint32_t ShipUtils::next32(uint64_t* state) { + if (state == nullptr) { + state = &default_state; + } + + if (!rand_init) { + // No seed given, get a random number from device to seed +#if !defined(__SWITCH__) && !defined(__WIIU__) + uint64_t seed = static_cast(std::random_device{}()); +#else + uint64_t seed = static_cast(rand()); +#endif + ShipUtils::RandInit(seed, state); + } + + *state = *state * multiplier + increment; + uint32_t xorshifted = static_cast(((*state >> 18) ^ *state) >> 27); + uint32_t rot = static_cast(*state >> 59); + return std::rotr(xorshifted, rot); +} + +// Returns a random integer in range [min, max-1] +uint32_t ShipUtils::Random(uint32_t min, uint32_t max, uint64_t* state) { + if (min == max) { + return min; + } + assert(max > min); + + uint32_t n = max - min; + uint32_t cutoff = UINT32_MAX - UINT32_MAX % static_cast(n); + for (;;) { + uint32_t r = next32(state); + if (r <= cutoff) { + return min + r % n; + } + } +} + +// Returns a random floating point number in [0.0, 1.0) +double ShipUtils::RandomDouble(uint64_t* state) { + return ldexp(next32(state), -32); +} diff --git a/soh/soh/ShipUtils.h b/soh/soh/ShipUtils.h index 85fc89cc538..87e72e3cdd3 100644 --- a/soh/soh/ShipUtils.h +++ b/soh/soh/ShipUtils.h @@ -26,6 +26,53 @@ void* Ship_GetCharFontTexture(u8 character); #ifdef __cplusplus } + +namespace ShipUtils { +void RandInit(uint64_t seed, uint64_t* state = nullptr); +uint32_t next32(uint64_t* state = nullptr); +uint32_t Random(uint32_t min, uint32_t max, uint64_t* state = nullptr); +double RandomDouble(uint64_t* state = nullptr); + +// Get a random element from a vector or array +template T RandomElement(std::vector& vector, bool erase, uint64_t* state = nullptr) { + const auto idx = Random(0, static_cast(vector.size()), state); + const T selected = vector[idx]; + if (erase) { + vector.erase(vector.begin() + idx); + } + return selected; +} +template auto& RandomElement(Container& container, uint64_t* state = nullptr) { + return container[Random(0, static_cast(std::size(container)), state)]; +} +template const auto& RandomElement(const Container& container, uint64_t* state = nullptr) { + return container[Random(0, static_cast(std::size(container)), state)]; +} + +template const T RandomElementFromSet(const std::set& set, uint64_t* state = nullptr) { + if (set.size() == 1) { + return *set.begin(); + } + uint32_t rand = Random(0, static_cast(set.size()), state); + auto it = set.begin(); + for (uint32_t i = 0; i < rand; i++) { + it++; + } + auto test = *it; + return *it; +} + +template void Shuffle(std::vector& vector, uint64_t* state = nullptr) { + for (size_t i = 0; i + 1 < vector.size(); i++) { + std::swap(vector[i], vector[Random(static_cast(i), static_cast(vector.size()), state)]); + } +} +template void Shuffle(std::array& arr, uint64_t* state = nullptr) { + for (size_t i = 0; i + 1 < arr.size(); i++) { + std::swap(arr[i], arr[Random(static_cast(i), static_cast(arr.size()), state)]); + } +} +} // namespace ShipUtils #endif #endif // SHIP_UTILS_H diff --git a/soh/soh/SohGui/UIWidgets.cpp b/soh/soh/SohGui/UIWidgets.cpp index 9a054735289..3fedea85d81 100644 --- a/soh/soh/SohGui/UIWidgets.cpp +++ b/soh/soh/SohGui/UIWidgets.cpp @@ -1154,34 +1154,19 @@ void DrawFlagArray8Mask(const std::string& name, uint8_t& flags, Colors color) { } // namespace UIWidgets ImVec4 GetRandomValue() { -#if !defined(__SWITCH__) && !defined(__WIIU__) - std::random_device rd; - std::mt19937 rng(rd()); -#else - size_t seed = std::hash{}(std::to_string(rand())); - 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; + NewColor.x = (float)ShipUtils::RandomDouble(); + NewColor.y = (float)ShipUtils::RandomDouble(); + NewColor.z = (float)ShipUtils::RandomDouble(); 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 GetRandomValue(uint32_t seed, uint64_t* state) { + ShipUtils::RandInit(seed, state); ImVec4 NewColor; - NewColor.x = (float)(dist(rng)) / 255.0f; - NewColor.y = (float)(dist(rng)) / 255.0f; - NewColor.z = (float)(dist(rng)) / 255.0f; + NewColor.x = (float)ShipUtils::RandomDouble(state); + NewColor.y = (float)ShipUtils::RandomDouble(state); + NewColor.z = (float)ShipUtils::RandomDouble(state); return NewColor; } diff --git a/soh/soh/SohGui/UIWidgets.hpp b/soh/soh/SohGui/UIWidgets.hpp index 8ff022870c7..dc116b95b6e 100644 --- a/soh/soh/SohGui/UIWidgets.hpp +++ b/soh/soh/SohGui/UIWidgets.hpp @@ -1052,7 +1052,7 @@ void InsertHelpHoverText(const char* text); } // namespace UIWidgets ImVec4 GetRandomValue(); -ImVec4 GetRandomValue(uint32_t seed); +ImVec4 GetRandomValue(uint32_t seed, uint64_t* state = nullptr); Color_RGBA8 RGBA8FromVec(ImVec4 vec); ImVec4 VecFromRGBA8(Color_RGBA8 color);