/* EQEmu: EQEmulator Copyright (C) 2001-2026 EQEmu Development Team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #pragma once #include #include #include #include #ifdef USE_SFMT19937 // only GCC supports, so lets turn it off #ifndef __GNUC__ #undef USE_SFMT19937 #endif #ifdef __clang__ #undef USE_SFMT19937 #endif #endif #ifdef USE_ADDITIVE_LFIB_PRNG #include "common/additive_lagged_fibonacci_engine.h" #elif defined(USE_SFMT19937) #include #endif /* This uses mt19937 (or other compatible engine) seeded with the std::random_device. The idea is to have this be * included as a member of another class so mocking out for testing is easier If you need to reseed random.Reseed() * Eventually this should be derived from an abstract base class */ namespace EQ { class Random { public: // AKA old MakeRandomInt int Int(int low, int high) { if (low > high) std::swap(low, high); // EQ uses biased int distribution, so I guess we can support it :P #ifdef BIASED_INT_DIST return low + m_gen() % (high - low + 1); #else return int_dist(m_gen, int_param_t(low, high)); // [low, high] #endif } // AKA old MakeRandomFloat double Real(double low, double high) { if (low > high) std::swap(low, high); return real_dist(m_gen, real_param_t(low, high)); // [low, high) } // example Roll(50) would have a 50% success rate // Roll(100) 100%, etc // valid values 0-100 (well, higher works too but ...) bool Roll(const int required) { return Int(0, 99) < required; } // valid values 0.0 - 1.0 bool Roll(const double required) { return Real(0.0, 1.0) <= required; } // same range as client's roll0 // This is their main high level RNG function int Roll0(int max) { if (max - 1 > 0) return Int(0, max - 1); return 0; } // std::shuffle requires a RNG engine passed to it, so lets provide a wrapper to use our engine template void Shuffle(RandomAccessIterator first, RandomAccessIterator last) { static_assert(std::is_same::iterator_category>::value, "EQ::Random::Shuffle requires random access iterators"); std::shuffle(first, last, m_gen); } void Reseed() { // We could do the seed_seq thing here too if we need better seeding // but that is mostly overkill for us, so just seed once std::random_device rd; m_gen.seed(rd()); } Random() { Reseed(); } static Random* Instance() { static Random instance; return &instance; } private: #ifndef BIASED_INT_DIST typedef std::uniform_int_distribution::param_type int_param_t; std::uniform_int_distribution int_dist; #endif typedef std::uniform_real_distribution::param_type real_param_t; std::uniform_real_distribution real_dist; // define USE_CUSTOM_PRNG_ENGINE to any supported random engine like: // #define USE_CUSTOM_PRNG_ENGINE std::default_random_engine #ifdef USE_ADDITIVE_LFIB_PRNG EQRand #elif defined(USE_SFMT19937) __gnu_cxx::sfmt19937 #elif defined(USE_CUSTOM_PRNG_ENGINE) USE_CUSTOM_PRNG_ENGINE #else std::mt19937 #endif m_gen; }; }