From ef1c60de67a1af3d945efe421c925fb0ea62e026 Mon Sep 17 00:00:00 2001 From: Martin Griffin Date: Sun, 15 Oct 2023 18:53:11 +0100 Subject: [PATCH 1/7] Buffer 'GBA: '-prefixed messages TODO: Detect illegal opcodes in the test runner because mgba-rom-test evaluates them very slowly, making the TIMEOUT mechanism effectively realtime. --- tools/mgba-rom-test-hydra/main.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/tools/mgba-rom-test-hydra/main.c b/tools/mgba-rom-test-hydra/main.c index 123d8a2552..c2189ac0df 100644 --- a/tools/mgba-rom-test-hydra/main.c +++ b/tools/mgba-rom-test-hydra/main.c @@ -2,9 +2,9 @@ * parses the output to display human-readable progress. * * Output lines starting with "GBA Debug: :" are parsed as commands to - * Hydra, other output lines starting with "GBA Debug: " are parsed as - * output from the current test, and any other lines are parsed as - * output from the mgba-rom-test process itself. + * Hydra, other output lines starting with "GBA Debug: " or with "GBA: " + * are parsed as output from the current test, and any other lines are + * parsed as output from the mgba-rom-test process itself. * * COMMANDS * N: Sets the test name to the remainder of the line. @@ -75,10 +75,17 @@ static void handle_read(int i, struct Runner *runner) { eol++; size_t n = eol - sol; - if (runner->input_buffer_size >= strlen("GBA Debug: ") - && !strncmp(sol, "GBA Debug: ", strlen("GBA Debug: "))) + char *soc; + if (runner->input_buffer_size >= strlen("GBA: ") + && !strncmp(sol, "GBA: ", strlen("GBA: "))) { - char *soc = sol + strlen("GBA Debug: "); + soc = sol + strlen("GBA: "); + goto buffer_output; + } + else if (runner->input_buffer_size >= strlen("GBA Debug: ") + && !strncmp(sol, "GBA Debug: ", strlen("GBA Debug: "))) + { + soc = sol + strlen("GBA Debug: "); if (soc[0] == ':') { switch (soc[1]) From 6e7e7fdafa21654bca58d9b68d739201c0b66746 Mon Sep 17 00:00:00 2001 From: Martin Griffin Date: Mon, 23 Oct 2023 07:03:45 +0100 Subject: [PATCH 2/7] make check MODERN=1 in a separate build directory --- Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Makefile b/Makefile index 80ee5303d3..5326383023 100644 --- a/Makefile +++ b/Makefile @@ -85,7 +85,11 @@ ELF = $(ROM:.gba=.elf) MAP = $(ROM:.gba=.map) SYM = $(ROM:.gba=.sym) +ifeq ($(MODERN),0) TEST_OBJ_DIR_NAME := build/test +else +TEST_OBJ_DIR_NAME := build/modern-test +endif TESTELF = $(ROM:.gba=-test.elf) HEADLESSELF = $(ROM:.gba=-test-headless.elf) From b0b6e1604264722f63807c3649d996164565884f Mon Sep 17 00:00:00 2001 From: Martin Griffin Date: Mon, 23 Oct 2023 06:57:57 +0100 Subject: [PATCH 3/7] Separate ASSIGN_TEST from NEXT_TEST Silences a warning about an invalid pointer when building with modern. --- include/test/test.h | 1 + test/test_runner.c | 28 +++++++++++++++++++++------- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/include/test/test.h b/include/test/test.h index 5b3ab8af0c..e9c920bd0b 100644 --- a/include/test/test.h +++ b/include/test/test.h @@ -47,6 +47,7 @@ struct TestRunnerState u8 expectedResult; bool8 expectLeaks:1; bool8 inBenchmark:1; + bool8 tearDown:1; u32 timeoutSeconds; }; diff --git a/test/test_runner.c b/test/test_runner.c index 0ae4f0a185..ddfe23b3ec 100644 --- a/test/test_runner.c +++ b/test/test_runner.c @@ -56,9 +56,10 @@ static bool32 PrefixMatch(const char *pattern, const char *string) enum { STATE_INIT, - STATE_NEXT_TEST, + STATE_ASSIGN_TEST, STATE_RUN_TEST, STATE_REPORT_RESULT, + STATE_NEXT_TEST, STATE_EXIT, }; @@ -152,17 +153,15 @@ void CB2_TestRunner(void) } else { - gTestRunnerState.state = STATE_NEXT_TEST; - gTestRunnerState.test = __start_tests - 1; + gTestRunnerState.state = STATE_ASSIGN_TEST; + gTestRunnerState.test = __start_tests; } gTestRunnerState.exitCode = 0; gTestRunnerState.skipFilename = NULL; break; - case STATE_NEXT_TEST: - gTestRunnerState.test++; - + case STATE_ASSIGN_TEST: if (gTestRunnerState.test == __stop_tests) { gTestRunnerState.state = STATE_EXIT; @@ -172,6 +171,7 @@ void CB2_TestRunner(void) if (gTestRunnerState.test->runner != &gAssumptionsRunner && !PrefixMatch(gTestRunnerArgv, gTestRunnerState.test->name)) { + gTestRunnerState.state = STATE_NEXT_TEST; return; } @@ -191,6 +191,8 @@ void CB2_TestRunner(void) sCurrentTest.address = (uintptr_t)gTestRunnerState.test; sCurrentTest.state = CURRENT_TEST_STATE_ESTIMATE; + // If AssignCostToRunner fails, we want to report the failure. + gTestRunnerState.state = STATE_REPORT_RESULT; if (AssignCostToRunner() == gTestRunnerI) gTestRunnerState.state = STATE_RUN_TEST; else @@ -204,7 +206,10 @@ void CB2_TestRunner(void) SeedRng(0); SeedRng2(0); if (gTestRunnerState.test->runner->setUp) + { gTestRunnerState.test->runner->setUp(gTestRunnerState.test->data); + gTestRunnerState.tearDown = TRUE; + } // NOTE: Assumes that the compiler interns __FILE__. if (gTestRunnerState.skipFilename == gTestRunnerState.test->filename) // Assumption fails for tests in this file. { @@ -216,13 +221,17 @@ void CB2_TestRunner(void) gTestRunnerState.test->runner->run(gTestRunnerState.test->data); } break; + case STATE_REPORT_RESULT: REG_TM2CNT_H = 0; gTestRunnerState.state = STATE_NEXT_TEST; - if (gTestRunnerState.test->runner->tearDown) + if (gTestRunnerState.tearDown && gTestRunnerState.test->runner->tearDown) + { gTestRunnerState.test->runner->tearDown(gTestRunnerState.test->data); + gTestRunnerState.tearDown = FALSE; + } if (gTestRunnerState.result == TEST_RESULT_PASS && !gTestRunnerState.expectLeaks) @@ -342,6 +351,11 @@ void CB2_TestRunner(void) break; + case STATE_NEXT_TEST: + gTestRunnerState.state = STATE_ASSIGN_TEST; + gTestRunnerState.test++; + break; + case STATE_EXIT: MgbaExit_(gTestRunnerState.exitCode); break; From cd59e055c24dd18e4fe9294ea5e53b723e898d17 Mon Sep 17 00:00:00 2001 From: LOuroboros Date: Mon, 23 Oct 2023 06:08:36 -0300 Subject: [PATCH 4/7] Made Reflect Type handle 3rd types (#3303) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Made Reflect Type handle 3rd types Misc: -Turned VARIOUS_TRY_REFLECT_TYPE into a callnative (BS_TryReflectType) -Introduced a macro to to check for typeless Pokémon (Pokémon who have Mystery in all 3 type slots) in battle. -Made the new BS_TryReflectType take into account the forms for Arceus and Silvally, rather than just their default form. --- asm/macros/battle_script.inc | 10 +- include/battle.h | 4 + src/battle_script_commands.c | 77 +++++----- test/battle/move_effect/reflect_type.c | 186 +++++++++++++++++++++++++ 4 files changed, 241 insertions(+), 36 deletions(-) create mode 100644 test/battle/move_effect/reflect_type.c diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index 866831c462..c875729b85 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -1456,6 +1456,11 @@ .4byte \jumpInstr .endm + .macro tryreflecttype failInstr:req + callnative BS_TryReflectType + .4byte \failInstr + .endm + @ various command changed to more readable macros .macro cancelmultiturnmoves battler:req various \battler, VARIOUS_CANCEL_MULTI_TURN_MOVES @@ -1686,11 +1691,6 @@ .4byte \failInstr .endm - .macro tryreflecttype failInstr:req - various BS_ATTACKER, VARIOUS_TRY_REFLECT_TYPE - .4byte \failInstr - .endm - .macro trysoak failInstr:req various BS_ATTACKER, VARIOUS_TRY_SOAK .4byte \failInstr diff --git a/include/battle.h b/include/battle.h index 037259fc5b..95886b7e13 100644 --- a/include/battle.h +++ b/include/battle.h @@ -724,12 +724,16 @@ STATIC_ASSERT(sizeof(((struct BattleStruct *)0)->palaceFlags) * 8 >= MAX_BATTLER #define BATTLER_DAMAGED(battlerId) ((gSpecialStatuses[battlerId].physicalDmg != 0 || gSpecialStatuses[battlerId].specialDmg != 0)) #define IS_BATTLER_OF_TYPE(battlerId, type)((GetBattlerType(battlerId, 0) == type || GetBattlerType(battlerId, 1) == type || (GetBattlerType(battlerId, 2) != TYPE_MYSTERY && GetBattlerType(battlerId, 2) == type))) + +#define IS_BATTLER_TYPELESS(battlerId)(GetBattlerType(battlerId, 0) == TYPE_MYSTERY && GetBattlerType(battlerId, 1) == TYPE_MYSTERY && GetBattlerType(battlerId, 2) == TYPE_MYSTERY) + #define SET_BATTLER_TYPE(battlerId, type) \ { \ gBattleMons[battlerId].type1 = type; \ gBattleMons[battlerId].type2 = type; \ gBattleMons[battlerId].type3 = TYPE_MYSTERY; \ } + #define RESTORE_BATTLER_TYPE(battlerId) \ { \ gBattleMons[battlerId].type1 = gSpeciesInfo[gBattleMons[battlerId].species].types[0]; \ diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index e9b7ba941b..c886178ec0 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -9409,37 +9409,6 @@ static void Cmd_various(void) } return; } - case VARIOUS_TRY_REFLECT_TYPE: - { - VARIOUS_ARGS(const u8 *failInstr); - if (gBattleMons[gBattlerTarget].species == SPECIES_ARCEUS || gBattleMons[gBattlerTarget].species == SPECIES_SILVALLY) - { - gBattlescriptCurrInstr = cmd->failInstr; - } - else if (GetBattlerType(gBattlerTarget, 0) == TYPE_MYSTERY && GetBattlerType(gBattlerTarget, 1) != TYPE_MYSTERY) - { - gBattleMons[gBattlerAttacker].type1 = GetBattlerType(gBattlerTarget, 1); - gBattleMons[gBattlerAttacker].type2 = GetBattlerType(gBattlerTarget, 1); - gBattlescriptCurrInstr = cmd->nextInstr; - } - else if (GetBattlerType(gBattlerTarget, 0) != TYPE_MYSTERY && GetBattlerType(gBattlerTarget, 1) == TYPE_MYSTERY) - { - gBattleMons[gBattlerAttacker].type1 = GetBattlerType(gBattlerTarget, 0); - gBattleMons[gBattlerAttacker].type2 = GetBattlerType(gBattlerTarget, 0); - gBattlescriptCurrInstr = cmd->nextInstr; - } - else if (GetBattlerType(gBattlerTarget, 0) == TYPE_MYSTERY && GetBattlerType(gBattlerTarget, 1) == TYPE_MYSTERY) - { - gBattlescriptCurrInstr = cmd->failInstr; - } - else - { - gBattleMons[gBattlerAttacker].type1 = GetBattlerType(gBattlerTarget, 0); - gBattleMons[gBattlerAttacker].type2 = GetBattlerType(gBattlerTarget, 1); - gBattlescriptCurrInstr = cmd->nextInstr; - } - return; - } case VARIOUS_TRY_SOAK: { VARIOUS_ARGS(const u8 *failInstr); @@ -16331,3 +16300,49 @@ void BS_JumpIfTerrainAffected(void) else gBattlescriptCurrInstr = cmd->nextInstr; } + +void BS_TryReflectType(void) +{ + NATIVE_ARGS(const u8 *failInstr); + u16 targetBaseSpecies = GET_BASE_SPECIES_ID(gBattleMons[gBattlerTarget].species); + u8 targetType1 = GetBattlerType(gBattlerTarget, 0); + u8 targetType2 = GetBattlerType(gBattlerTarget, 1); + u8 targetType3 = GetBattlerType(gBattlerTarget, 2); + + if (targetBaseSpecies == SPECIES_ARCEUS || targetBaseSpecies == SPECIES_SILVALLY) + { + gBattlescriptCurrInstr = cmd->failInstr; + } + else if (IS_BATTLER_TYPELESS(gBattlerTarget)) + { + gBattlescriptCurrInstr = cmd->failInstr; + } + else if (targetType1 == TYPE_MYSTERY && targetType2 == TYPE_MYSTERY && targetType3 != TYPE_MYSTERY) + { + gBattleMons[gBattlerAttacker].type1 = TYPE_NORMAL; + gBattleMons[gBattlerAttacker].type2 = TYPE_NORMAL; + gBattleMons[gBattlerAttacker].type3 = targetType3; + gBattlescriptCurrInstr = cmd->nextInstr; + } + else if (targetType1 == TYPE_MYSTERY && targetType2 != TYPE_MYSTERY) + { + gBattleMons[gBattlerAttacker].type1 = targetType2; + gBattleMons[gBattlerAttacker].type2 = targetType2; + gBattleMons[gBattlerAttacker].type3 = targetType3; + gBattlescriptCurrInstr = cmd->nextInstr; + } + else if (targetType1 != TYPE_MYSTERY && targetType2 == TYPE_MYSTERY) + { + gBattleMons[gBattlerAttacker].type1 = targetType1; + gBattleMons[gBattlerAttacker].type2 = targetType1; + gBattleMons[gBattlerAttacker].type3 = targetType3; + gBattlescriptCurrInstr = cmd->nextInstr; + } + else + { + gBattleMons[gBattlerAttacker].type1 = targetType1; + gBattleMons[gBattlerAttacker].type2 = targetType2; + gBattleMons[gBattlerAttacker].type3 = targetType3; + gBattlescriptCurrInstr = cmd->nextInstr; + } +} diff --git a/test/battle/move_effect/reflect_type.c b/test/battle/move_effect/reflect_type.c new file mode 100644 index 0000000000..e873516034 --- /dev/null +++ b/test/battle/move_effect/reflect_type.c @@ -0,0 +1,186 @@ +#include "global.h" +#include "test/battle.h" + +TO_DO_BATTLE_TEST("Reflect Type fails if the user is Terastallized"); +TO_DO_BATTLE_TEST("Reflect Type succeeds against a Terastallized target and copies its Tera type"); + +SINGLE_BATTLE_TEST("Reflect Type does not affect any of Arceus' forms") +{ + u32 j; + static const u16 sArceusFormSpeciesIdTable[] = { + SPECIES_ARCEUS, + SPECIES_ARCEUS_FIGHTING, + SPECIES_ARCEUS_FLYING, + SPECIES_ARCEUS_POISON, + SPECIES_ARCEUS_GROUND, + SPECIES_ARCEUS_ROCK, + SPECIES_ARCEUS_BUG, + SPECIES_ARCEUS_GHOST, + SPECIES_ARCEUS_STEEL, + SPECIES_ARCEUS_FIRE, + SPECIES_ARCEUS_WATER, + SPECIES_ARCEUS_GRASS, + SPECIES_ARCEUS_ELECTRIC, + SPECIES_ARCEUS_PSYCHIC, + SPECIES_ARCEUS_ICE, + SPECIES_ARCEUS_DRAGON, + SPECIES_ARCEUS_DARK, + SPECIES_ARCEUS_FAIRY, + }; + u16 species = SPECIES_NONE; + + for (j = 0; j < ARRAY_COUNT(sArceusFormSpeciesIdTable); j++) + { + PARAMETRIZE { species = sArceusFormSpeciesIdTable[j]; } + } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(species); + } WHEN { + TURN { MOVE(player, MOVE_REFLECT_TYPE); } + } SCENE { + MESSAGE("Wobbuffet used Reflect Type!"); + MESSAGE("But it failed!"); + } +} + +SINGLE_BATTLE_TEST("Reflect Type does not affect any of Silvally's forms") +{ + u32 j; + static const u16 sSilvallyFormSpeciesIdTable[] = { + SPECIES_SILVALLY, + SPECIES_SILVALLY_FIGHTING, + SPECIES_SILVALLY_FLYING, + SPECIES_SILVALLY_POISON, + SPECIES_SILVALLY_GROUND, + SPECIES_SILVALLY_ROCK, + SPECIES_SILVALLY_BUG, + SPECIES_SILVALLY_GHOST, + SPECIES_SILVALLY_STEEL, + SPECIES_SILVALLY_FIRE, + SPECIES_SILVALLY_WATER, + SPECIES_SILVALLY_GRASS, + SPECIES_SILVALLY_ELECTRIC, + SPECIES_SILVALLY_PSYCHIC, + SPECIES_SILVALLY_ICE, + SPECIES_SILVALLY_DRAGON, + SPECIES_SILVALLY_DARK, + SPECIES_SILVALLY_FAIRY, + }; + u16 species = SPECIES_NONE; + + for (j = 0; j < ARRAY_COUNT(sSilvallyFormSpeciesIdTable); j++) + { + PARAMETRIZE { species = sSilvallyFormSpeciesIdTable[j]; } + } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(species); + } WHEN { + TURN { MOVE(player, MOVE_REFLECT_TYPE); } + } SCENE { + MESSAGE("Wobbuffet used Reflect Type!"); + MESSAGE("But it failed!"); + } +} + +SINGLE_BATTLE_TEST("Reflect Type does not affect Pokémon with no types") +{ + ASSUME(gSpeciesInfo[SPECIES_ARCANINE].types[0] == TYPE_FIRE); + ASSUME(gSpeciesInfo[SPECIES_ARCANINE].types[1] == TYPE_FIRE); + ASSUME(gSpeciesInfo[SPECIES_POLIWRATH].types[0] == TYPE_WATER); + ASSUME(gSpeciesInfo[SPECIES_POLIWRATH].types[1] == TYPE_FIGHTING); + GIVEN { + PLAYER(SPECIES_ARCANINE); + OPPONENT(SPECIES_POLIWRATH); + } WHEN { + TURN { MOVE(player, MOVE_BURN_UP); MOVE(opponent, MOVE_REFLECT_TYPE); } + } SCENE { + MESSAGE("Arcanine used Burn Up!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BURN_UP, player); + HP_BAR(opponent); + MESSAGE("Arcanine burned itself out!"); + MESSAGE("Foe Poliwrath used Reflect Type!"); + MESSAGE("But it failed!"); + } +} + +SINGLE_BATTLE_TEST("Reflect Type copies a target's dual types") +{ + ASSUME(gSpeciesInfo[SPECIES_ARCANINE].types[0] == TYPE_FIRE); + ASSUME(gSpeciesInfo[SPECIES_ARCANINE].types[1] == TYPE_FIRE); + ASSUME(gSpeciesInfo[SPECIES_POLIWRATH].types[0] == TYPE_WATER); + ASSUME(gSpeciesInfo[SPECIES_POLIWRATH].types[1] == TYPE_FIGHTING); + GIVEN { + PLAYER(SPECIES_ARCANINE); + OPPONENT(SPECIES_POLIWRATH); + } WHEN { + TURN { MOVE(player, MOVE_REFLECT_TYPE); } + } SCENE { + MESSAGE("Arcanine used Reflect Type!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_REFLECT_TYPE, player); + MESSAGE("Arcanine's type changed to match the Foe Poliwrath's!"); + } THEN { + EXPECT_EQ(player->type1, TYPE_WATER); + EXPECT_EQ(player->type2, TYPE_FIGHTING); + EXPECT_EQ(player->type3, TYPE_MYSTERY); + } +} + +SINGLE_BATTLE_TEST("Reflect Type copies a target's pure type") +{ + ASSUME(gSpeciesInfo[SPECIES_ARCANINE].types[0] == TYPE_FIRE); + ASSUME(gSpeciesInfo[SPECIES_ARCANINE].types[1] == TYPE_FIRE); + ASSUME(gSpeciesInfo[SPECIES_SUDOWOODO].types[0] == TYPE_ROCK); + ASSUME(gSpeciesInfo[SPECIES_SUDOWOODO].types[1] == TYPE_ROCK); + GIVEN { + PLAYER(SPECIES_ARCANINE); + OPPONENT(SPECIES_SUDOWOODO); + } WHEN { + TURN { MOVE(player, MOVE_REFLECT_TYPE); } + } SCENE { + MESSAGE("Arcanine used Reflect Type!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_REFLECT_TYPE, player); + MESSAGE("Arcanine's type changed to match the Foe Sudowoodo's!"); + } THEN { + EXPECT_EQ(player->type1, TYPE_ROCK); + EXPECT_EQ(player->type2, TYPE_ROCK); + EXPECT_EQ(player->type3, TYPE_MYSTERY); + } +} + +SINGLE_BATTLE_TEST("Reflect Type defaults to Normal type for the user's type1 and type2 if the target only has a 3rd type") +{ + ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].types[0] == TYPE_PSYCHIC); + ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].types[1] == TYPE_PSYCHIC); + ASSUME(gSpeciesInfo[SPECIES_ARCANINE].types[0] == TYPE_FIRE); + ASSUME(gSpeciesInfo[SPECIES_ARCANINE].types[1] == TYPE_FIRE); + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_ARCANINE); + } WHEN { + TURN { MOVE(opponent, MOVE_BURN_UP); } + TURN { MOVE(player, MOVE_FORESTS_CURSE); } + TURN { MOVE(player, MOVE_REFLECT_TYPE); } + } SCENE { + // Turn 1 + MESSAGE("Foe Arcanine used Burn Up!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BURN_UP, opponent); + HP_BAR(player); + MESSAGE("Foe Arcanine burned itself out!"); + // Turn 2 + MESSAGE("Wobbuffet used Forest'sCurs!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_FORESTS_CURSE, player); + MESSAGE("Grass type was added to Foe Arcanine!"); + // Turn 3 + MESSAGE("Wobbuffet used Reflect Type!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_REFLECT_TYPE, player); + MESSAGE("Wobbuffet's type changed to match the Foe Arcanine's!"); + } THEN { + EXPECT_EQ(player->type1, TYPE_NORMAL); + EXPECT_EQ(player->type2, TYPE_NORMAL); + EXPECT_EQ(player->type3, TYPE_GRASS); + } +} From 5fd36a8e390c7b3d72ee470846bc0a91754425bd Mon Sep 17 00:00:00 2001 From: Martin Griffin Date: Mon, 23 Oct 2023 13:37:40 +0100 Subject: [PATCH 5/7] Fix ModifyPersonalityForNature (#3452) --- src/battle_main.c | 2 +- test/battle/trainer_control.c | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/battle_main.c b/src/battle_main.c index 037d9a857f..514b69fd68 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -1896,7 +1896,7 @@ static u32 GeneratePartyHash(const struct Trainer *trainer, u32 i) void ModifyPersonalityForNature(u32 *personality, u32 newNature) { u32 nature = GetNatureFromPersonality(*personality); - s32 diff = abs(nature - newNature); + s32 diff = abs((s32)nature - (s32)newNature); s32 sign = (nature > newNature) ? 1 : -1; if (diff > NUM_NATURES / 2) { diff --git a/test/battle/trainer_control.c b/test/battle/trainer_control.c index a0e94dbbec..99bea0e8cc 100644 --- a/test/battle/trainer_control.c +++ b/test/battle/trainer_control.c @@ -4,6 +4,7 @@ #include "battle_main.h" #include "data.h" #include "malloc.h" +#include "random.h" #include "string_util.h" #include "constants/item.h" #include "constants/abilities.h" @@ -119,3 +120,17 @@ TEST("CreateNPCTrainerPartyForTrainer generates different personalities for diff EXPECT(testParty[0].box.personality != testParty[1].box.personality); Free(testParty); } + +TEST("ModifyPersonalityForNature can set any nature") +{ + u32 personality, nature, j, k; + for (j = 0; j < 64; j++) + { + for (k = 0; k < NUM_NATURES; k++) + { + PARAMETRIZE { personality = Random32(); nature = k; } + } + } + ModifyPersonalityForNature(&personality, nature); + EXPECT_EQ(GetNatureFromPersonality(personality), nature); +} From 5592706a71170cb8064de999ff5f82ba0b0b6f47 Mon Sep 17 00:00:00 2001 From: Bassoonian Date: Mon, 23 Oct 2023 17:59:07 +0200 Subject: [PATCH 6/7] Fix Synchronize devnote + Gen9 behaviour --- include/config/battle.h | 2 +- src/wild_encounter.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/include/config/battle.h b/include/config/battle.h index aee79daee5..5ce2e4f9f8 100644 --- a/include/config/battle.h +++ b/include/config/battle.h @@ -115,7 +115,7 @@ #define B_SHADOW_TAG_ESCAPE GEN_LATEST // In Gen4+, if both sides have a Pokémon with Shadow Tag, all battlers can escape. Before, neither side could escape this situation. #define B_MOODY_ACC_EVASION GEN_LATEST // In Gen8, Moody CANNOT raise Accuracy and Evasion anymore. #define B_FLASH_FIRE_FROZEN GEN_LATEST // In Gen5+, Flash Fire can trigger even when frozen, when it couldn't before. -#define B_SYNCHRONIZE_NATURE GEN_LATEST // In Gen8, if a Pokémon with Synchronize is leading the party, it's 100% guaranteed that wild Pokémon will have the same ability, as opposed to 50% previously. +#define B_SYNCHRONIZE_NATURE GEN_LATEST // In Gen8, if a Pokémon with Synchronize is leading the party, it's 100% guaranteed that wild Pokémon will have the same Nature, as opposed to 50% previously. In Gen9, it has no out-of-battle effect. #define B_SYNCHRONIZE_TOXIC GEN_LATEST // In Gen5+, if a Pokémon with Synchronize is badly poisoned, the opponent will also become badly poisoned. Previously, the opponent would become regular poisoned. #define B_UPDATED_INTIMIDATE GEN_LATEST // In Gen8, Intimidate doesn't work on opponents with the Inner Focus, Scrappy, Own Tempo or Oblivious abilities. It also activates Rattled. #define B_OBLIVIOUS_TAUNT GEN_LATEST // In Gen6+, Pokémon with Oblivious can't be taunted. diff --git a/src/wild_encounter.c b/src/wild_encounter.c index 6016e08e69..5ea88f4c4a 100644 --- a/src/wild_encounter.c +++ b/src/wild_encounter.c @@ -415,6 +415,7 @@ static u8 PickWildMonNature(void) } } } +#if B_SYNCHRONIZE_NATURE < GEN_9 // check synchronize for a pokemon with the same ability if (!GetMonData(&gPlayerParty[0], MON_DATA_SANITY_IS_EGG) && GetMonAbility(&gPlayerParty[0]) == ABILITY_SYNCHRONIZE @@ -425,6 +426,7 @@ static u8 PickWildMonNature(void) { return GetMonData(&gPlayerParty[0], MON_DATA_PERSONALITY) % NUM_NATURES; } +#endif // random nature return Random() % NUM_NATURES; From bac135c6e8a82601c2add44918310454f49bbc10 Mon Sep 17 00:00:00 2001 From: Frank DeBlasio <35279583+fdeblasio@users.noreply.github.com> Date: Mon, 23 Oct 2023 16:03:31 -0400 Subject: [PATCH 7/7] Gave Razor Fang EVO_HELD_ITEM type/fieldUseFunc properties (#3456) --- src/data/items.h | 4 ++-- src/data/pokemon/item_effects.h | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/data/items.h b/src/data/items.h index 6990e7cded..ec7e22a608 100644 --- a/src/data/items.h +++ b/src/data/items.h @@ -6045,8 +6045,8 @@ const struct Item gItems[] = .holdEffectParam = 10, .description = sRazorFangDesc, .pocket = POCKET_ITEMS, - .type = ITEM_USE_BAG_MENU, - .fieldUseFunc = ItemUseOutOfBattle_CannotUse, + .type = EVO_HELD_ITEM_TYPE, + .fieldUseFunc = EVO_HELD_ITEM_FIELD_FUNC, .flingPower = 30, }, diff --git a/src/data/pokemon/item_effects.h b/src/data/pokemon/item_effects.h index d7482d5043..dc2e4f755d 100644 --- a/src/data/pokemon/item_effects.h +++ b/src/data/pokemon/item_effects.h @@ -566,6 +566,7 @@ const u8 *const gItemEffectTable[ITEMS_COUNT] = [ITEM_METAL_COAT] = gItemEffect_EvoItem, [ITEM_KINGS_ROCK] = gItemEffect_EvoItem, [ITEM_RAZOR_CLAW] = gItemEffect_EvoItem, + [ITEM_RAZOR_FANG] = gItemEffect_EvoItem, [ITEM_AUSPICIOUS_ARMOR] = gItemEffect_EvoItem, [ITEM_MALICIOUS_ARMOR] = gItemEffect_EvoItem, [ITEM_SCROLL_OF_DARKNESS] = gItemEffect_EvoItem,