#include "random.h" #include #include #include "debug.h" #include "platform_compat.h" #include "scripts.h" #include "sfall_config.h" namespace fallout { static int _roll_reset_(); static int randomTranslateRoll(int delta, int criticalSuccessModifier); static int getRandom(int max); static int randomInt32(); static void randomSeedPrerandomInternal(int seed); static unsigned int randomGetSeed(); static void randomValidatePrerandom(); // 0x50D4BA static const double dbl_50D4BA = 36.42; // 0x50D4C2 static const double dbl_50D4C2 = 4000; // 0x51C694 static int _iy = 0; // 0x6648D0 static int _iv[32]; // 0x664950 static int _idum; // 0x4A2FE0 void randomInit() { unsigned int randomSeed = randomGetSeed(); srand(randomSeed); int pseudorandomSeed = randomInt32(); randomSeedPrerandomInternal(pseudorandomSeed); randomValidatePrerandom(); } // Note: Collapsed. // // 0x4A2FFC static int _roll_reset_() { return 0; } // NOTE: Uncollapsed 0x4A2FFC. void randomReset() { _roll_reset_(); } // NOTE: Uncollapsed 0x4A2FFC. void randomExit() { _roll_reset_(); } // NOTE: Uncollapsed 0x4A2FFC. int randomSave(File* stream) { return _roll_reset_(); } // NOTE: Uncollapsed 0x4A2FFC. int randomLoad(File* stream) { return _roll_reset_(); } // Rolls d% against [difficulty]. // // 0x4A3000 int randomRoll(int difficulty, int criticalSuccessModifier, int* howMuchPtr) { int delta = difficulty - randomBetween(1, 100); int result = randomTranslateRoll(delta, criticalSuccessModifier); if (howMuchPtr != NULL) { *howMuchPtr = delta; } return result; } // Translates raw d% result into [Roll] constants, possibly upgrading to // criticals (starting from day 2). // // 0x4A3030 static int randomTranslateRoll(int delta, int criticalSuccessModifier) { int gameTime = gameTimeGetTime(); // SFALL: Remove criticals time limits. bool criticalsTimeLimitsRemoved = false; configGetBool(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_REMOVE_CRITICALS_TIME_LIMITS_KEY, &criticalsTimeLimitsRemoved); int roll; if (delta < 0) { roll = ROLL_FAILURE; if (criticalsTimeLimitsRemoved || (gameTime / GAME_TIME_TICKS_PER_DAY) >= 1) { // 10% to become critical failure. if (randomBetween(1, 100) <= -delta / 10) { roll = ROLL_CRITICAL_FAILURE; } } } else { roll = ROLL_SUCCESS; if (criticalsTimeLimitsRemoved || (gameTime / GAME_TIME_TICKS_PER_DAY) >= 1) { // 10% + modifier to become critical success. if (randomBetween(1, 100) <= delta / 10 + criticalSuccessModifier) { roll = ROLL_CRITICAL_SUCCESS; } } } return roll; } // 0x4A30C0 int randomBetween(int min, int max) { int result; if (min <= max) { result = min + getRandom(max - min + 1); } else { result = max + getRandom(min - max + 1); } if (result < min || result > max) { debugPrint("Random number %d is not in range %d to %d", result, min, max); result = min; } return result; } // 0x4A30FC static int getRandom(int max) { int v1 = 16807 * (_idum % 127773) - 2836 * (_idum / 127773); if (v1 < 0) { v1 += 0x7FFFFFFF; } if (v1 < 0) { v1 += 0x7FFFFFFF; } int v2 = _iy & 0x1F; int v3 = _iv[v2]; _iv[v2] = v1; _iy = v3; _idum = v1; return v3 % max; } // 0x4A31A0 void randomSeedPrerandom(int seed) { if (seed == -1) { // NOTE: Uninline. seed = randomInt32(); } randomSeedPrerandomInternal(seed); } // 0x4A31C4 static int randomInt32() { int high = rand() << 16; int low = rand(); return (high + low) & INT_MAX; } // 0x4A31E0 static void randomSeedPrerandomInternal(int seed) { int num = seed; if (num < 1) { num = 1; } for (int index = 40; index > 0; index--) { num = 16807 * (num % 127773) - 2836 * (num / 127773); if (num < 0) { num &= INT_MAX; } if (index < 32) { _iv[index] = num; } } _iy = _iv[0]; _idum = num; } // Provides seed for random number generator. // // 0x4A3258 static unsigned int randomGetSeed() { return compat_timeGetTime(); } // 0x4A3264 static void randomValidatePrerandom() { int results[25]; for (int index = 0; index < 25; index++) { results[index] = 0; } for (int attempt = 0; attempt < 100000; attempt++) { int value = randomBetween(1, 25); if (value - 1 < 0) { debugPrint("I made a negative number %d\n", value - 1); } results[value - 1]++; } double v1 = 0.0; for (int index = 0; index < 25; index++) { double v2 = ((double)results[index] - dbl_50D4C2) * ((double)results[index] - dbl_50D4C2) / dbl_50D4C2; v1 += v2; } debugPrint("Chi squared is %f, P = %f at 0.05\n", v1, dbl_50D4C2); if (v1 < dbl_50D4BA) { debugPrint("Sequence is random, 95%% confidence.\n"); } else { debugPrint("Warning! Sequence is not random, 95%% confidence.\n"); } } } // namespace fallout