diff --git a/Source/engine/random.cpp b/Source/engine/random.cpp index 9cc4bafa1..12c9f10dc 100644 --- a/Source/engine/random.cpp +++ b/Source/engine/random.cpp @@ -8,7 +8,7 @@ namespace devilution { uint32_t sglGameSeed; /** - * Specifies the increment used in the Borland C/C++ pseudo-random. + * Specifies the increment used in the Borland C/C++ pseudo-random number generator algorithm. */ const uint32_t RndInc = 1; @@ -17,44 +17,27 @@ const uint32_t RndInc = 1; */ const uint32_t RndMult = 0x015A4E35; -/** - * @brief Set the RNG seed - * @param s RNG seed - */ -void SetRndSeed(uint32_t s) +void SetRndSeed(uint32_t seed) { - sglGameSeed = s; + sglGameSeed = seed; } -/** - * @brief Advance the internal RNG seed and return the new value - * @return RNG seed - */ -int32_t AdvanceRndSeed() +uint32_t GetLCGEngineState() { - sglGameSeed = (RndMult * sglGameSeed) + RndInc; - return GetRndSeed(); + return sglGameSeed; } -/** - * @brief Get the current RNG seed - * @return RNG seed - */ int32_t GetRndSeed() { return abs(static_cast(sglGameSeed)); } -uint32_t GetLCGEngineState() +int32_t AdvanceRndSeed() { - return sglGameSeed; + sglGameSeed = (RndMult * sglGameSeed) + RndInc; + return GetRndSeed(); } -/** - * @brief Main RNG function - * @param v The upper limit for the return value - * @return A random number from 0 to (v-1) - */ int32_t GenerateRnd(int32_t v) { if (v <= 0) diff --git a/Source/engine/random.hpp b/Source/engine/random.hpp index b5c38d0af..73a219673 100644 --- a/Source/engine/random.hpp +++ b/Source/engine/random.hpp @@ -12,10 +12,50 @@ namespace devilution { -void SetRndSeed(uint32_t s); -int32_t AdvanceRndSeed(); -int32_t GetRndSeed(); +/** + * @brief Set the state of the RandomNumberEngine used by the base game to the specific seed + * @param seed New engine state + */ +void SetRndSeed(uint32_t seed); + +/** + * @brief Returns the current state of the RandomNumberEngine used by the base game + * + * This is only exposed to allow for debugging vanilla code and testing. Using this engine for new code is discouraged + * due to the poor randomness and bugs in the implementation that need to be retained for compatibility. + * + * @return The current engine state + */ uint32_t GetLCGEngineState(); + +/** + * @brief Generates a random non-negative integer (most of the time) using the vanilla RNG + * + * This advances the engine state then interprets the new engine state as a signed value and calls std::abs to try + * discard the high bit of the result. This usually returns a positive number but may very rarely return -2^31. + * + * This function is only used when the base game wants to store the seed used to generate an item or level, however + * as the returned value is transformed about 50% of values do not reflect the actual engine state. It would be more + * appropriate to use GetLCGEngineState() in these cases but that may break compatibility with the base game. + * + * @return A random number in the range [0,2^31) or -2^31 + */ +int32_t AdvanceRndSeed(); + +/** + * @brief Generates a random integer less than the given limit using the vanilla RNG + * + * If v is not a positive number this function returns 0 without calling the RNG. + * + * Limits between 32768 and 65534 should be avoided as a bug in vanilla means this function always returns a value + * less than 32768 for limits in that range. + * + * This can very rarely return a negative value in the range (-v, -1] due to the bug in AdvanceRndSeed() + * + * @see AdvanceRndSeed() + * @param v The upper limit for the return value + * @return A random number in the range [0, v) or rarely a negative value in (-v, -1] + */ int32_t GenerateRnd(int32_t v); } // namespace devilution