diff --git a/include/battle.h b/include/battle.h index 7185bf340c..c41698fd5f 100644 --- a/include/battle.h +++ b/include/battle.h @@ -15,6 +15,7 @@ #include "pokeball.h" #include "battle_debug.h" #include "battle_dynamax.h" +#include "random.h" // for rng_value_t // Used to exclude moves learned temporarily by Transform or Mimic #define MOVE_IS_PERMANENT(battler, moveSlot) \ @@ -580,6 +581,13 @@ struct LostItem u16 stolen:1; }; +#if HQ_RANDOM == TRUE +struct BattleVideo { + u32 battleTypeFlags; + rng_value_t rngSeed; +}; +#endif + struct BattleStruct { u8 turnEffectsTracker; @@ -651,7 +659,12 @@ struct BattleStruct u16 lastTakenMoveFrom[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT]; // a 2-D array [target][attacker] union { struct LinkBattlerHeader linkBattlerHeader; + + #if HQ_RANDOM == FALSE u32 battleVideo[2]; + #else + struct BattleVideo battleVideo; + #endif } multiBuffer; u8 wishPerishSongState; u8 wishPerishSongBattlerId; diff --git a/include/config.h b/include/config.h index 8a6e990a5d..f4be812643 100644 --- a/include/config.h +++ b/include/config.h @@ -79,5 +79,6 @@ #define EXPANSION_INTRO TRUE // If TRUE, a custom RHH intro will play after the vanilla copyright screen. #define POKEDEX_PLUS_HGSS FALSE // If TRUE, enables the custom HGSS style Pokedex. #define SUMMARY_SCREEN_NATURE_COLORS TRUE // If TRUE, nature-based stat boosts and reductions will be red and blue in the summary screen. +#define HQ_RANDOM TRUE // If TRUE, replaces the default RNG with an implementation of SFC32 RNG. May break code that relies on RNG. #endif // GUARD_CONFIG_H diff --git a/include/contest.h b/include/contest.h index 21e2530c21..7a67fecd55 100644 --- a/include/contest.h +++ b/include/contest.h @@ -3,6 +3,7 @@ #include "palette.h" #include "constants/contest.h" +#include "random.h" // for rng_value_t enum { @@ -327,7 +328,7 @@ extern struct ContestResources *gContestResources; extern struct ContestWinner gCurContestWinner; extern u8 gCurContestWinnerIsForArtist; extern u8 gCurContestWinnerSaveIdx; -extern u32 gContestRngValue; +extern rng_value_t gContestRngValue; // contest.c void ResetLinkContestBoolean(void); diff --git a/include/random.h b/include/random.h index c3d6067efa..a13b28674f 100644 --- a/include/random.h +++ b/include/random.h @@ -1,8 +1,74 @@ #ifndef GUARD_RANDOM_H #define GUARD_RANDOM_H -extern u32 gRngValue; -extern u32 gRng2Value; +// The number 1103515245 comes from the example implementation of rand and srand +// in the ISO C standard. +#define ISO_RANDOMIZE1(val)(1103515245 * (val) + 24691) +#define ISO_RANDOMIZE2(val)(1103515245 * (val) + 12345) + +/* Some functions have been added to support HQ_RANDOM. +* +* If using HQ_RANDOM, you cannot call Random() in interrupt handlers safely. +* AdvanceRandom() is provided to handle burning numbers in the VBlank handler +* if you choose to do that, and can be used regardless of HQ_RANDOM setting. +* If you need to use random numbers in the VBlank handler, a local state +* should be used instead. +* +* LocalRandom(*val) allows you to have local random states that are the same +* type as the global states regardless of HQ_RANDOM setting, which is useful +* if you want to be able to set them from or assign them to gRngValue. +* +* Random2_32() was added to HQ_RANDOM because the output of the generator is +* always 32 bits and Random()/Random2() are just wrappers in that mode. It is +* also available in non-HQ mode for consistency. +*/ + +#if HQ_RANDOM == TRUE +struct Sfc32State { + u32 a; + u32 b; + u32 c; + u32 ctr; +}; + +typedef struct Sfc32State rng_value_t; + +#define RNG_VALUE_EMPTY {} + +// Calling this function directly is discouraged. +// Use LocalRandom() instead. +static inline u32 _SFC32_Next(struct Sfc32State *state) +{ + const u32 result = state->a + state->b + state->ctr++; + state->a = state->b ^ (state->b >> 9); + state->b = state->c * 9; + state->c = result + ((state->c << 21) | (state->c >> 11)); + return result; +} + +static inline u16 LocalRandom(rng_value_t *val) +{ + return _SFC32_Next(val) >> 16; +} + +u32 Random32(void); +u32 Random2_32(void); + +static inline u16 Random(void) +{ + return Random32() >> 16; +} + +static inline u16 Random2(void) +{ + return Random2_32() >> 16; +} + +void AdvanceRandom(void); +#else +typedef u32 rng_value_t; + +#define RNG_VALUE_EMPTY 0 //Returns a 16-bit pseudorandom number u16 Random(void); @@ -10,11 +76,23 @@ u16 Random2(void); //Returns a 32-bit pseudorandom number #define Random32() (Random() | (Random() << 16)) +#define Random2_32() (Random2() | (Random2() << 16)) -// The number 1103515245 comes from the example implementation of rand and srand -// in the ISO C standard. -#define ISO_RANDOMIZE1(val)(1103515245 * (val) + 24691) -#define ISO_RANDOMIZE2(val)(1103515245 * (val) + 12345) +static inline u16 LocalRandom(rng_value_t *val) +{ + *val = ISO_RANDOMIZE1(*val); + return *val >> 16; +} + +static inline void AdvanceRandom(void) +{ + Random(); +} + +#endif + +extern rng_value_t gRngValue; +extern rng_value_t gRng2Value; //Sets the initial seed value of the pseudorandom number generator void SeedRng(u16 seed); diff --git a/include/recorded_battle.h b/include/recorded_battle.h index fcbe9495fd..c64a665b74 100644 --- a/include/recorded_battle.h +++ b/include/recorded_battle.h @@ -2,6 +2,7 @@ #define GUARD_RECORDED_BATTLE_H #include "constants/battle.h" +#include "random.h" #define BATTLER_RECORD_SIZE 664 @@ -13,7 +14,7 @@ struct RecordedBattleSave u8 playersGender[MAX_BATTLERS_COUNT]; u32 playersTrainerId[MAX_BATTLERS_COUNT]; u8 playersLanguage[MAX_BATTLERS_COUNT]; - u32 rngSeed; + rng_value_t rngSeed; u32 battleFlags; u8 playersBattlers[MAX_BATTLERS_COUNT]; u16 opponentA; @@ -49,8 +50,8 @@ enum RECORDED_ITEM_MOVE, }; -extern u32 gRecordedBattleRngSeed; -extern u32 gBattlePalaceMoveSelectionRngValue; +extern rng_value_t gRecordedBattleRngSeed; +extern rng_value_t gBattlePalaceMoveSelectionRngValue; extern u8 gRecordedBattleMultiplayerId; #define B_RECORD_MODE_RECORDING 1 diff --git a/include/test/battle.h b/include/test/battle.h index 32908df9c5..ee41032102 100644 --- a/include/test/battle.h +++ b/include/test/battle.h @@ -855,7 +855,7 @@ void ClearFlagAfterTest(void); void OpenPokemon(u32 sourceLine, u32 side, u32 species); void ClosePokemon(u32 sourceLine); -void RNGSeed_(u32 sourceLine, u32 seed); +void RNGSeed_(u32 sourceLine, rng_value_t seed); void AIFlags_(u32 sourceLine, u32 flags); void AILogScores(u32 sourceLine); void Gender_(u32 sourceLine, u32 gender); diff --git a/src/battle_controller_link_opponent.c b/src/battle_controller_link_opponent.c index b89c2263f3..c9197bc5df 100644 --- a/src/battle_controller_link_opponent.c +++ b/src/battle_controller_link_opponent.c @@ -28,6 +28,7 @@ #include "constants/songs.h" #include "constants/trainers.h" #include "recorded_battle.h" +#include "random.h" static void LinkOpponentHandleLoadMonSprite(u32 battler); static void LinkOpponentHandleSwitchInAnim(u32 battler); diff --git a/src/battle_controller_link_partner.c b/src/battle_controller_link_partner.c index 58bbe676f0..e5fb49bebf 100644 --- a/src/battle_controller_link_partner.c +++ b/src/battle_controller_link_partner.c @@ -28,6 +28,7 @@ #include "constants/songs.h" #include "constants/trainers.h" #include "recorded_battle.h" +#include "random.h" static void LinkPartnerHandleLoadMonSprite(u32 battler); static void LinkPartnerHandleSwitchInAnim(u32 battler); diff --git a/src/battle_main.c b/src/battle_main.c index dd313a41de..6a1d6eb6ab 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -1713,9 +1713,16 @@ static void CB2_HandleStartMultiBattle(void) case 8: if (IsLinkTaskFinished()) { + #if HQ_RANDOM == TRUE + struct BattleVideo *ptr = &gBattleStruct->multiBuffer.battleVideo; + ptr->battleTypeFlags = gBattleTypeFlags; + ptr->rngSeed = gRecordedBattleRngSeed; + #else u32 *ptr = gBattleStruct->multiBuffer.battleVideo; ptr[0] = gBattleTypeFlags; ptr[1] = gRecordedBattleRngSeed; // UB: overwrites berry data + #endif + SendBlock(BitmaskAllOtherLinkPlayers(), ptr, sizeof(gBattleStruct->multiBuffer.battleVideo)); gBattleCommunication[MULTIUSE_STATE]++; } @@ -2053,7 +2060,7 @@ void VBlankCB_Battle(void) { // Change gRngSeed every vblank unless the battle could be recorded. if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_FRONTIER | BATTLE_TYPE_RECORDED))) - Random(); + AdvanceRandom(); SetGpuReg(REG_OFFSET_BG0HOFS, gBattle_BG0_X); SetGpuReg(REG_OFFSET_BG0VOFS, gBattle_BG0_Y); diff --git a/src/contest.c b/src/contest.c index 8f452b0921..d4cc73677c 100644 --- a/src/contest.c +++ b/src/contest.c @@ -358,7 +358,7 @@ EWRAM_DATA bool8 gCurContestWinnerIsForArtist = 0; EWRAM_DATA u8 gCurContestWinnerSaveIdx = 0; // IWRAM common vars. -u32 gContestRngValue; +rng_value_t gContestRngValue; extern const u8 gText_LinkStandby4[]; extern const u8 gText_BDot[]; @@ -1709,7 +1709,7 @@ static void Task_AppealSetup(u8 taskId) if (++gTasks[taskId].data[0] > 19) { eContest.turnNumber = 0; - eContest.unusedRng = gRngValue; + eContest.unusedRng = 0; if ((gLinkContestFlags & LINK_CONTEST_FLAG_IS_LINK) && IsPlayerLinkLeader()) { s32 i; @@ -6109,4 +6109,3 @@ void StripPlayerAndMonNamesForLinkContest(struct ContestPokemon *mon, s32 langua name[PLAYER_NAME_LENGTH] = EOS; } } - diff --git a/src/contest_util.c b/src/contest_util.c index 3bd1c28293..32588a8c00 100644 --- a/src/contest_util.c +++ b/src/contest_util.c @@ -2655,8 +2655,7 @@ void GenerateContestRand(void) if (gLinkContestFlags & LINK_CONTEST_FLAG_IS_LINK) { - gContestRngValue = ISO_RANDOMIZE1(gContestRngValue); - random = gContestRngValue >> 16; + random = LocalRandom(&gContestRngValue); result = &gSpecialVar_Result; } else @@ -2669,8 +2668,7 @@ void GenerateContestRand(void) u16 GetContestRand(void) { - gContestRngValue = ISO_RANDOMIZE1(gContestRngValue); - return gContestRngValue >> 16; + return LocalRandom(&gContestRngValue); } bool8 LinkContestWaitForConnection(void) diff --git a/src/debug.c b/src/debug.c index 1ed438598b..c1853af4cf 100644 --- a/src/debug.c +++ b/src/debug.c @@ -2144,7 +2144,7 @@ static void DebugAction_Util_Player_Gender(u8 taskId) static void DebugAction_Util_Player_Id(u8 taskId) { - u32 trainerId = ((Random() << 16) | Random()); + u32 trainerId = Random32(); SetTrainerId(trainerId, gSaveBlock2Ptr->playerTrainerId); Debug_DestroyMenu_Full(taskId); ScriptContext_Enable(); diff --git a/src/load_save.c b/src/load_save.c index 4f706180c4..d42b71a4a9 100644 --- a/src/load_save.c +++ b/src/load_save.c @@ -127,7 +127,7 @@ void MoveSaveBlocks_ResetHeap(void) gMain.vblankCallback = vblankCB; // create a new encryption key - encryptionKey = (Random() << 16) + (Random()); + encryptionKey = Random32(); ApplyNewEncryptionKeyToAllEncryptedData(encryptionKey); gSaveBlock2Ptr->encryptionKey = encryptionKey; } diff --git a/src/lottery_corner.c b/src/lottery_corner.c index f19e9f7b7b..d06934ca51 100644 --- a/src/lottery_corner.c +++ b/src/lottery_corner.c @@ -23,9 +23,7 @@ static u8 GetMatchingDigits(u16, u16); void ResetLotteryCorner(void) { - u16 rand = Random(); - - SetLotteryNumber((Random() << 16) | rand); + SetLotteryNumber(Random32()); VarSet(VAR_POKELOT_PRIZE_ITEM, 0); } diff --git a/src/main.c b/src/main.c index d850bc27bc..537af7191b 100644 --- a/src/main.c +++ b/src/main.c @@ -373,7 +373,7 @@ static void VBlankIntr(void) TryReceiveLinkBattleData(); if (!gMain.inBattle || !(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_FRONTIER | BATTLE_TYPE_RECORDED))) - Random(); + AdvanceRandom(); UpdateWirelessStatusIndicatorSprite(); diff --git a/src/random.c b/src/random.c index 6a462004b4..db82334aa9 100644 --- a/src/random.c +++ b/src/random.c @@ -4,12 +4,108 @@ #include #endif -EWRAM_DATA static u8 sUnknown = 0; -EWRAM_DATA static u32 sRandCount = 0; - // IWRAM common -u32 gRngValue; -u32 gRng2Value; +rng_value_t gRngValue; +rng_value_t gRng2Value; + +#if HQ_RANDOM == TRUE + +EWRAM_DATA static volatile bool8 sRngLoopUnlocked; + +// Streams allow generators seeded the same to have separate outputs. +#define STREAM1 1 +#define STREAM2 29 + +// A variant of SFC32 that lets you change the stream. +// stream can be any odd number. +static inline u32 _SFC32_Next_Stream(struct Sfc32State *state, const u8 stream) +{ + const u32 result = state->a + state->b + state->ctr; + state->ctr += stream; + state->a = state->b ^ (state->b >> 9); + state->b = state->c * 9; + state->c = result + ((state->c << 21) | (state->c >> 11)); + return result; +} + +static void SFC32_Seed(struct Sfc32State *state, u16 seed, u8 stream) +{ + u32 i; + state->a = state->b = 0; + state->c = seed; + state->ctr = stream; + for(i = 0; i < 16; i++) + { + _SFC32_Next_Stream(state, stream); + } +} + +/*This ASM implementation uses some shortcuts and is generally faster on the GBA. +* It's not necessarily faster if inlined, or on other platforms. */ +u32 NAKED Random32(void) +{ + asm(".thumb\n\ + push {r4, r5, r6}\n\ + mov r6, #11\n\ + ldr r5, =gRngValue\n\ + ldmia r5!, {r1, r2, r3, r4}\n\ + @ e (result) = a + b + d++\n\ + add r1, r1, r2\n\ + add r0, r1, r4\n\ + add r4, r4, #" STR(STREAM1) "\n\ + @ a = b ^ (b >> 9)\n\ + lsr r1, r2, #9\n\ + eor r1, r1, r2\n\ + @ b = c + (c << 3) [c * 9]\n\ + lsl r2, r3, #3\n\ + add r2, r2, r3\n\ + @ c = rol(c, 21) + e\n\ + ror r3, r3, r6\n\ + add r3, r3, r0\n\ + sub r5, r5, #16\n\ + stmia r5!, {r1, r2, r3, r4}\n\ + pop {r4, r5, r6}\n\ + bx lr\n\ + .ltorg" + ); +} + +u32 Random2_32(void) +{ + return _SFC32_Next_Stream(&gRng2Value, STREAM2); +} + +void SeedRng(u16 seed) +{ + struct Sfc32State state; + SFC32_Seed(&state, seed, STREAM1); + + sRngLoopUnlocked = FALSE; + gRngValue = state; + sRngLoopUnlocked = TRUE; +} + +void SeedRng2(u16 seed) +{ + SFC32_Seed(&gRng2Value, seed, STREAM2); +} + +void AdvanceRandom(void) +{ + if (sRngLoopUnlocked == TRUE) + Random32(); +} + +#define LOOP_RANDOM_START \ + struct Sfc32State *const state = &gRngValue; \ + sRngLoopUnlocked = FALSE; + +#define LOOP_RANDOM_END sRngLoopUnlocked = TRUE; + +#define LOOP_RANDOM ((u16)(_SFC32_Next(state) >> 16)) + +#else +EWRAM_DATA static u32 sRandCount = 0; u16 Random(void) { @@ -21,7 +117,6 @@ u16 Random(void) void SeedRng(u16 seed) { gRngValue = seed; - sUnknown = 0; } void SeedRng2(u16 seed) @@ -35,15 +130,24 @@ u16 Random2(void) return gRng2Value >> 16; } +#define LOOP_RANDOM_START +#define LOOP_RANDOM_END + +#define LOOP_RANDOM (Random()) + +#endif + #define SHUFFLE_IMPL \ u32 tmp; \ + LOOP_RANDOM_START; \ --n; \ while (n > 1) \ { \ - int j = (Random() * (n+1)) >> 16; \ + int j = (LOOP_RANDOM * (n+1)) >> 16; \ SWAP(data[n], data[j], tmp); \ --n; \ - } + } \ + LOOP_RANDOM_END void Shuffle8(void *data_, size_t n) { @@ -66,15 +170,19 @@ void Shuffle32(void *data_, size_t n) void ShuffleN(void *data, size_t n, size_t size) { void *tmp = alloca(size); + LOOP_RANDOM_START; --n; + while (n > 1) { - int j = (Random() * (n+1)) >> 16; + int j = (LOOP_RANDOM * (n+1)) >> 16; memcpy(tmp, (u8 *)data + n*size, size); // tmp = data[n]; memcpy((u8 *)data + n*size, (u8 *)data + j*size, size); // data[n] = data[j]; memcpy((u8 *)data + j*size, tmp, size); // data[j] = tmp; --n; } + + LOOP_RANDOM_END; } __attribute__((weak, alias("RandomUniformDefault"))) @@ -96,12 +204,14 @@ u32 RandomUniformDefault(enum RandomTag tag, u32 lo, u32 hi) u32 RandomUniformExceptDefault(enum RandomTag tag, u32 lo, u32 hi, bool32 (*reject)(u32)) { + LOOP_RANDOM_START; while (TRUE) { - u32 n = RandomUniformDefault(tag, lo, hi); + u32 n = lo + (((hi - lo + 1) * LOOP_RANDOM) >> 16); if (!reject(n)) return n; } + LOOP_RANDOM_END; } u32 RandomWeightedArrayDefault(enum RandomTag tag, u32 sum, u32 n, const u8 *weights) diff --git a/src/recorded_battle.c b/src/recorded_battle.c index 4d36ce12b2..1ba62b2837 100644 --- a/src/recorded_battle.c +++ b/src/recorded_battle.c @@ -33,8 +33,8 @@ struct PlayerInfo // Save data using TryWriteSpecialSaveSector is allowed to exceed SECTOR_DATA_SIZE (up to the counter field) STATIC_ASSERT(sizeof(struct RecordedBattleSave) <= SECTOR_COUNTER_OFFSET, RecordedBattleSaveFreeSpace); -EWRAM_DATA u32 gRecordedBattleRngSeed = 0; -EWRAM_DATA u32 gBattlePalaceMoveSelectionRngValue = 0; +EWRAM_DATA rng_value_t gRecordedBattleRngSeed = RNG_VALUE_EMPTY; +EWRAM_DATA rng_value_t gBattlePalaceMoveSelectionRngValue = RNG_VALUE_EMPTY; EWRAM_DATA static u8 sBattleRecords[MAX_BATTLERS_COUNT][BATTLER_RECORD_SIZE] = {0}; EWRAM_DATA static u16 sBattlerRecordSizes[MAX_BATTLERS_COUNT] = {0}; EWRAM_DATA static u16 sBattlerPrevRecordSizes[MAX_BATTLERS_COUNT] = {0}; diff --git a/src/time_events.c b/src/time_events.c index cec6a44c34..1c8a12a237 100644 --- a/src/time_events.c +++ b/src/time_events.c @@ -25,7 +25,7 @@ static void SetMirageRnd(u32 rnd) // unused void InitMirageRnd(void) { - SetMirageRnd((Random() << 16) | Random()); + SetMirageRnd(Random32()); } void UpdateMirageRnd(u16 days) diff --git a/test/random.c b/test/random.c index 845f24f102..0232ff1547 100644 --- a/test/random.c +++ b/test/random.c @@ -17,7 +17,7 @@ error = 0; \ for (i = 0; i < ARRAY_COUNT(indexSum); i++) \ error += abs(3584 - indexSum[i]); \ - EXPECT_LT(error, (int)(28672 * 0.025)); + EXPECT_LT(error, (int)(28672 * 0.03)); TEST("Shuffle randomizes the array [Shuffle8]") { @@ -196,6 +196,13 @@ TEST("RandomElement generates a uniform distribution") TEST("RandomUniform mul-based faster than mod-based (compile-time)") { + #if HQ_RANDOM == TRUE + const u32 expectedMulSum = 6; + const u32 expectedModSum = 4; + #else + const u32 expectedMulSum = 3; + const u32 expectedModSum = 4; + #endif struct Benchmark mulBenchmark, modBenchmark; u32 mulSum = 0, modSum = 0; @@ -221,12 +228,19 @@ TEST("RandomUniform mul-based faster than mod-based (compile-time)") // These numbers are different because multiplication and modulus // have subtly different biases (so subtle that it's irrelevant for // our purposes). - EXPECT_EQ(mulSum, 3); - EXPECT_EQ(modSum, 4); + EXPECT_EQ(mulSum, expectedMulSum); + EXPECT_EQ(modSum, expectedModSum); } TEST("RandomUniform mul-based faster than mod-based (run-time)") { + #if HQ_RANDOM == TRUE + const u32 expectedMulSum = 289; + const u32 expectedModSum = 205; + #else + const u32 expectedMulSum = 232; + const u32 expectedModSum = 249; + #endif u32 i; struct Benchmark mulBenchmark, modBenchmark; u32 mulSum = 0, modSum = 0; @@ -246,6 +260,30 @@ TEST("RandomUniform mul-based faster than mod-based (run-time)") EXPECT_FASTER(mulBenchmark, modBenchmark); // Reference mulSum/modSum to prevent optimization. - EXPECT_EQ(mulSum, 232); - EXPECT_EQ(modSum, 249); + EXPECT_EQ(mulSum, expectedMulSum); + EXPECT_EQ(modSum, expectedModSum); } + +#if HQ_RANDOM == TRUE +TEST("Thumb and C SFC32 implementations produce the same results") +{ + u32 thumbSum; + u32 cSum; + int i; + rng_value_t localState; + + thumbSum = 0; + cSum = 0; + + SeedRng(0); + localState = gRngValue; + + for(i = 0; i < 32; i++) + { + thumbSum += Random32(); + cSum += _SFC32_Next(&localState); + } + + EXPECT_EQ(thumbSum, cSum); +} +#endif \ No newline at end of file diff --git a/test/test_runner_battle.c b/test/test_runner_battle.c index d34e1fc96c..5e42057955 100644 --- a/test/test_runner_battle.c +++ b/test/test_runner_battle.c @@ -33,8 +33,20 @@ #define STATE gBattleTestRunnerState #define DATA gBattleTestRunnerState->data -#define RNG_SEED_DEFAULT 0x00000000 +#if HQ_RANDOM == TRUE +#define RNG_SEED_DEFAULT {0, 0, 0, 0} +static inline bool32 RngSeedNotDefault(const rng_value_t *seed) +{ + return (seed->a | seed->b | seed->c | seed->ctr) != 0; +} +#else +#define RNG_SEED_DEFAULT 0x00000000 +static inline bool32 RngSeedNotDefault(const rng_value_t *seed) +{ + return *seed != RNG_SEED_DEFAULT; +} +#endif #undef Q_4_12 #define Q_4_12(n) (s32)((n) * 4096) @@ -257,11 +269,12 @@ static void BattleTest_Run(void *data) s32 i; u32 requiredPlayerPartySize; u32 requiredOpponentPartySize; + const rng_value_t defaultSeed = RNG_SEED_DEFAULT; const struct BattleTest *test = data; memset(&DATA, 0, sizeof(DATA)); - DATA.recordedBattle.rngSeed = RNG_SEED_DEFAULT; + DATA.recordedBattle.rngSeed = defaultSeed; DATA.recordedBattle.textSpeed = OPTIONS_TEXT_SPEED_FAST; // Set battle flags and opponent ids. switch (test->type) @@ -1349,6 +1362,20 @@ static void CB2_BattleTest_NextParameter(void) } } +static inline rng_value_t MakeRngValue(const u16 seed) +{ + #if HQ_RANDOM == TRUE + int i; + rng_value_t result = {0, 0, seed, 1}; + for (i = 0; i < 16; i++) + { + _SFC32_Next(&result); + } + return result; + #else + return ISO_RANDOMIZE1(seed); + #endif +} static void CB2_BattleTest_NextTrial(void) { ClearFlagAfterTest(); @@ -1373,7 +1400,7 @@ static void CB2_BattleTest_NextTrial(void) { PrintTestName(); gTestRunnerState.result = TEST_RESULT_PASS; - DATA.recordedBattle.rngSeed = ISO_RANDOMIZE1(STATE->runTrial); + DATA.recordedBattle.rngSeed = MakeRngValue(STATE->runTrial); DATA.queuedEvent = 0; DATA.lastActionTurn = 0; SetVariablesForRecordedBattle(&DATA.recordedBattle); @@ -1446,16 +1473,17 @@ void Randomly(u32 sourceLine, u32 passes, u32 trials, struct RandomlyContext ctx } else { - INVALID_IF(DATA.recordedBattle.rngSeed != RNG_SEED_DEFAULT, "RNG seed already set"); + const rng_value_t defaultSeed = RNG_SEED_DEFAULT; + INVALID_IF(RngSeedNotDefault(&DATA.recordedBattle.rngSeed), "RNG seed already set"); STATE->trials = 50; STATE->trialRatio = Q_4_12(1) / STATE->trials; - DATA.recordedBattle.rngSeed = 0; + DATA.recordedBattle.rngSeed = defaultSeed; } } -void RNGSeed_(u32 sourceLine, u32 seed) +void RNGSeed_(u32 sourceLine, rng_value_t seed) { - INVALID_IF(DATA.recordedBattle.rngSeed != RNG_SEED_DEFAULT, "RNG seed already set"); + INVALID_IF(RngSeedNotDefault(&DATA.recordedBattle.rngSeed), "RNG seed already set"); DATA.recordedBattle.rngSeed = seed; }