From f868de066d7569d390e1d4df9708aee1e7fbc1d6 Mon Sep 17 00:00:00 2001 From: sneed <56992013+Sneed69@users.noreply.github.com> Date: Thu, 13 Jun 2024 17:40:53 +0300 Subject: [PATCH 01/14] fix passing hold effect instead of ability (#4789) --- src/battle_ai_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 2aa8f71fa7..28823822ce 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -3715,7 +3715,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move) ADJUST_SCORE(GOOD_EFFECT); break; case EFFECT_SANDSTORM: - if (ShouldSetSandstorm(battlerAtk, aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerAtk])) + if (ShouldSetSandstorm(battlerAtk, aiData->abilities[battlerAtk], aiData->holdEffects[battlerAtk])) { ADJUST_SCORE(DECENT_EFFECT); if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_SMOOTH_ROCK) From 84d13d0abf173fd59ce50188cf6b0c10f81f7510 Mon Sep 17 00:00:00 2001 From: DizzyEggg Date: Thu, 13 Jun 2024 21:30:28 +0200 Subject: [PATCH 02/14] Fix Smack Down anim + move anim tests (#4774) * Fix Smack Down anim + move anim tests * really agbcc * fix undefined reference * hopefully everything works --- data/battle_anim_scripts.s | 1 - include/test/battle.h | 3 +++ src/battle_anim.c | 8 ++++++-- test/battle/move_animations/smack_down.c | 25 ++++++++++++++++++++++++ 4 files changed, 34 insertions(+), 3 deletions(-) create mode 100644 test/battle/move_animations/smack_down.c diff --git a/data/battle_anim_scripts.s b/data/battle_anim_scripts.s index 61e5d94650..aa62732c7d 100644 --- a/data/battle_anim_scripts.s +++ b/data/battle_anim_scripts.s @@ -5168,7 +5168,6 @@ Move_SMACK_DOWN:: createvisualtask AnimTask_SmokescreenImpact, 0x8, 0x400, 0x1902 fadetobg BG_IN_AIR waitbgfadeout - createvisualtask AnimTask_StartSlidingBg, 5, 0x0, 0x0, 0x0, 0xffff createvisualtask AnimTask_SeismicTossBgAccelerateDownAtEnd, 3 goto SeismicTossWeak diff --git a/include/test/battle.h b/include/test/battle.h index b44186ed3d..514e3bca49 100644 --- a/include/test/battle.h +++ b/include/test/battle.h @@ -695,6 +695,7 @@ struct BattleTestData struct BattleTestRunnerState { u8 battlersCount; + bool8 forceMoveAnim; u16 parametersCount; // Valid only in BattleTest_Setup. u16 parameters; u16 runParameter; @@ -996,6 +997,8 @@ void SendOut(u32 sourceLine, struct BattlePokemon *, u32 partyIndex); #define NONE_OF for (OpenQueueGroup(__LINE__, QUEUE_GROUP_NONE_OF); gBattleTestRunnerState->data.queueGroupType != QUEUE_GROUP_NONE; CloseQueueGroup(__LINE__)) #define NOT NONE_OF +#define FORCE_MOVE_ANIM(set) gBattleTestRunnerState->forceMoveAnim = (set) + #define ABILITY_POPUP(battler, ...) QueueAbility(__LINE__, battler, (struct AbilityEventContext) { __VA_ARGS__ }) #define ANIMATION(type, id, ...) QueueAnimation(__LINE__, type, id, (struct AnimationEventContext) { __VA_ARGS__ }) #define HP_BAR(battler, ...) QueueHP(__LINE__, battler, (struct HPEventContext) { APPEND_TRUE(__VA_ARGS__) }) diff --git a/src/battle_anim.c b/src/battle_anim.c index af1a5a262a..77f16cd3d7 100644 --- a/src/battle_anim.c +++ b/src/battle_anim.c @@ -18,6 +18,7 @@ #include "sprite.h" #include "task.h" #include "test_runner.h" +#include "test/battle.h" #include "constants/battle_anim.h" #include "constants/moves.h" @@ -239,6 +240,9 @@ void LaunchBattleAnimation(u32 animType, u32 animId) TestRunner_Battle_RecordAnimation(animType, animId); // Play Transform and Ally Switch even in Headless as these move animations also change mon data. if (gTestRunnerHeadless + #if TESTING // Because gBattleTestRunnerState is not seen outside of test env. + && !gBattleTestRunnerState->forceMoveAnim + #endif // TESTING && !(animType == ANIM_TYPE_MOVE && (animId == MOVE_TRANSFORM || animId == MOVE_ALLY_SWITCH))) { gAnimScriptCallback = Nop; @@ -446,7 +450,7 @@ static u8 GetBattleAnimMoveTargets(u8 battlerArgIndex, u8 *targets) u32 i; u32 ignoredTgt = gBattlerAttacker; u32 target = GetBattlerMoveTargetType(gBattleAnimAttacker, gAnimMoveIndex); - + switch (battlerAnimId) { case ANIM_ATTACKER: @@ -458,7 +462,7 @@ static u8 GetBattleAnimMoveTargets(u8 battlerArgIndex, u8 *targets) ignoredTgt = gBattlerAttacker; break; } - + switch (target) { case MOVE_TARGET_FOES_AND_ALLY: diff --git a/test/battle/move_animations/smack_down.c b/test/battle/move_animations/smack_down.c new file mode 100644 index 0000000000..acd97e9505 --- /dev/null +++ b/test/battle/move_animations/smack_down.c @@ -0,0 +1,25 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Move Animation Test: Smack Down works when used 15 times in a row") +{ + u16 j, nTurns = 15; + FORCE_MOVE_ANIM(TRUE); + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + for (j = 0; j < nTurns; j++) + { + TURN { MOVE(player, MOVE_SMACK_DOWN); MOVE(opponent, MOVE_HELPING_HAND); } // Helping Hand, so there's no anim on the opponent's side. + } + } SCENE { + for (j = 0; j < nTurns; j++) + { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SMACK_DOWN, player); + } + } THEN { + FORCE_MOVE_ANIM(FALSE); + } +} From 9ec16f4eb63b02261e8de6bd1ff9509709937724 Mon Sep 17 00:00:00 2001 From: Pawkkie <61265402+Pawkkie@users.noreply.github.com> Date: Fri, 14 Jun 2024 03:08:21 -0400 Subject: [PATCH 03/14] Clear Body tests include Full Metal Body and White Smoke (#4797) --- test/battle/ability/clear_body.c | 276 +++++++++++++++++++------- test/battle/ability/full_metal_body.c | 38 ---- test/battle/ability/white_smoke.c | 39 ---- 3 files changed, 206 insertions(+), 147 deletions(-) delete mode 100644 test/battle/ability/full_metal_body.c delete mode 100644 test/battle/ability/white_smoke.c diff --git a/test/battle/ability/clear_body.c b/test/battle/ability/clear_body.c index 1117bac3d3..5dd4eff5db 100644 --- a/test/battle/ability/clear_body.c +++ b/test/battle/ability/clear_body.c @@ -1,15 +1,19 @@ #include "global.h" #include "test/battle.h" -SINGLE_BATTLE_TEST("Clear Body prevents intimidate") +SINGLE_BATTLE_TEST("Clear Body, Full Metal Body, and White Smoke prevent intimidate") { s16 turnOneHit; s16 turnTwoHit; + u32 species, ability; + PARAMETRIZE{ species = SPECIES_METANG; ability = ABILITY_CLEAR_BODY; } + PARAMETRIZE{ species = SPECIES_SOLGALEO; ability = ABILITY_FULL_METAL_BODY; } + PARAMETRIZE{ species = SPECIES_TORKOAL; ability = ABILITY_WHITE_SMOKE; } GIVEN { PLAYER(SPECIES_EKANS) { Ability(ABILITY_SHED_SKIN); }; PLAYER(SPECIES_EKANS) { Ability(ABILITY_INTIMIDATE); }; - OPPONENT(SPECIES_BELDUM) { Ability(ABILITY_CLEAR_BODY); }; + OPPONENT(species) { Ability(ability); }; } WHEN { TURN { MOVE(opponent, MOVE_TACKLE); } TURN { SWITCH(player, 1); MOVE(opponent, MOVE_TACKLE); } @@ -20,24 +24,38 @@ SINGLE_BATTLE_TEST("Clear Body prevents intimidate") NONE_OF { ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); } - ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY); - MESSAGE("Foe Beldum's Clear Body prevents stat loss!"); + ABILITY_POPUP(opponent, ability); + if (ability == ABILITY_FULL_METAL_BODY) + MESSAGE("Foe Solgaleo's Full Metal Body prevents stat loss!"); + else if (ability == ABILITY_WHITE_SMOKE) + MESSAGE("Foe Torkoal's White Smoke prevents stat loss!"); + else + MESSAGE("Foe Metang's Clear Body prevents stat loss!"); HP_BAR(player, captureDamage: &turnTwoHit); } THEN { EXPECT_EQ(turnOneHit, turnTwoHit); } } -SINGLE_BATTLE_TEST("Clear Body prevents stat stage reduction from moves") +SINGLE_BATTLE_TEST("Clear Body, Full Metal Body, and White Smoke prevent stat stage reduction from moves") { - u16 move; - PARAMETRIZE{ move = MOVE_GROWL; } - PARAMETRIZE{ move = MOVE_LEER; } - PARAMETRIZE{ move = MOVE_CONFIDE; } - PARAMETRIZE{ move = MOVE_FAKE_TEARS; } - PARAMETRIZE{ move = MOVE_SCARY_FACE; } - PARAMETRIZE{ move = MOVE_SWEET_SCENT; } - PARAMETRIZE{ move = MOVE_SAND_ATTACK; } + u16 move = MOVE_NONE; + u32 j, species = SPECIES_NONE, ability = ABILITY_NONE; + static const u16 statReductionMoves[] = { + MOVE_GROWL, + MOVE_LEER, + MOVE_CONFIDE, + MOVE_FAKE_TEARS, + MOVE_SCARY_FACE, + MOVE_SWEET_SCENT, + MOVE_SAND_ATTACK, + }; + for (j = 0; j < ARRAY_COUNT(statReductionMoves); j++) + { + PARAMETRIZE{ species = SPECIES_METANG; ability = ABILITY_CLEAR_BODY; move = statReductionMoves[j]; } + PARAMETRIZE{ species = SPECIES_SOLGALEO; ability = ABILITY_FULL_METAL_BODY; move = statReductionMoves[j]; } + PARAMETRIZE{ species = SPECIES_TORKOAL; ability = ABILITY_WHITE_SMOKE; move = statReductionMoves[j]; } + } GIVEN { ASSUME(gMovesInfo[MOVE_GROWL].effect == EFFECT_ATTACK_DOWN); @@ -48,7 +66,7 @@ SINGLE_BATTLE_TEST("Clear Body prevents stat stage reduction from moves") ASSUME(gMovesInfo[MOVE_SWEET_SCENT].effect == (B_UPDATED_MOVE_DATA >= GEN_6 ? EFFECT_EVASION_DOWN_2 : EFFECT_EVASION_DOWN)); ASSUME(gMovesInfo[MOVE_SAND_ATTACK].effect == EFFECT_ACCURACY_DOWN); PLAYER(SPECIES_WOBBUFFET) - OPPONENT(SPECIES_BELDUM) { Ability(ABILITY_CLEAR_BODY); } + OPPONENT(species) { Ability(ability); } } WHEN { TURN { MOVE(player, move); } } SCENE { @@ -56,18 +74,27 @@ SINGLE_BATTLE_TEST("Clear Body prevents stat stage reduction from moves") ANIMATION(ANIM_TYPE_MOVE, move, player); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); } - ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY); - MESSAGE("Foe Beldum's Clear Body prevents stat loss!"); + ABILITY_POPUP(opponent, ability); + if (ability == ABILITY_FULL_METAL_BODY) + MESSAGE("Foe Solgaleo's Full Metal Body prevents stat loss!"); + else if (ability == ABILITY_WHITE_SMOKE) + MESSAGE("Foe Torkoal's White Smoke prevents stat loss!"); + else + MESSAGE("Foe Metang's Clear Body prevents stat loss!"); } } -SINGLE_BATTLE_TEST("Clear Body prevents Sticky Web") +SINGLE_BATTLE_TEST("Clear Body, Full Metal Body, and White Smoke prevent Sticky Web effect on switchin") { + u32 species, ability; + PARAMETRIZE{ species = SPECIES_METANG; ability = ABILITY_CLEAR_BODY; } + PARAMETRIZE{ species = SPECIES_SOLGALEO; ability = ABILITY_FULL_METAL_BODY; } + PARAMETRIZE{ species = SPECIES_TORKOAL; ability = ABILITY_WHITE_SMOKE; } GIVEN { ASSUME(gMovesInfo[MOVE_STICKY_WEB].effect == EFFECT_STICKY_WEB); PLAYER(SPECIES_WOBBUFFET) OPPONENT(SPECIES_WOBBUFFET) - OPPONENT(SPECIES_BELDUM) { Ability(ABILITY_CLEAR_BODY); } + OPPONENT(species) { Ability(ability); } } WHEN { TURN { MOVE(player, MOVE_STICKY_WEB); } TURN { SWITCH(opponent, 1); } @@ -75,32 +102,43 @@ SINGLE_BATTLE_TEST("Clear Body prevents Sticky Web") NONE_OF { ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); } - ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY); - MESSAGE("Foe Beldum's Clear Body prevents stat loss!"); + ABILITY_POPUP(opponent, ability); + if (ability == ABILITY_FULL_METAL_BODY) + MESSAGE("Foe Solgaleo's Full Metal Body prevents stat loss!"); + else if (ability == ABILITY_WHITE_SMOKE) + MESSAGE("Foe Torkoal's White Smoke prevents stat loss!"); + else + MESSAGE("Foe Metang's Clear Body prevents stat loss!"); } } -SINGLE_BATTLE_TEST("Clear Body doesn't prevent stat stage reduction from moves used by the user") +SINGLE_BATTLE_TEST("Clear Body, Full Metal Body, and White Smoke don't prevent stat stage reduction from moves used by the user") { + u32 species, ability; + PARAMETRIZE{ species = SPECIES_METANG; ability = ABILITY_CLEAR_BODY; } + PARAMETRIZE{ species = SPECIES_SOLGALEO; ability = ABILITY_FULL_METAL_BODY; } + PARAMETRIZE{ species = SPECIES_TORKOAL; ability = ABILITY_WHITE_SMOKE; } GIVEN { ASSUME(MoveHasAdditionalEffectSelf(MOVE_SUPERPOWER, MOVE_EFFECT_ATK_DEF_DOWN) == TRUE); PLAYER(SPECIES_WOBBUFFET) - OPPONENT(SPECIES_BELDUM) { Ability(ABILITY_CLEAR_BODY); } + OPPONENT(species) { Ability(ability); } } WHEN { TURN { MOVE(opponent, MOVE_SUPERPOWER); } } SCENE { ANIMATION(ANIM_TYPE_MOVE, MOVE_SUPERPOWER, opponent); NONE_OF { - ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY); - MESSAGE("Foe Beldum's Clear Body prevents stat loss!"); + ABILITY_POPUP(opponent, ability); + MESSAGE("Foe Solgaleo's Full Metal Body prevents stat loss!"); + MESSAGE("Foe Torkoal's White Smoke prevents stat loss!"); + MESSAGE("Foe Metang's Clear Body prevents stat loss!"); } } } -SINGLE_BATTLE_TEST("Mold Breaker, Teravolt, and Turboblaze ignore Clear Body") +SINGLE_BATTLE_TEST("Mold Breaker, Teravolt, and Turboblaze ignore Clear Body and White Smoke, but not Full Metal Body") { - u32 j, k; - u16 ability = ABILITY_NONE; + u32 j, k, species = SPECIES_NONE, ability = ABILITY_NONE; + u16 breakerAbility = ABILITY_NONE; u16 move = ABILITY_NONE; static const u16 breakerAbilities[] = { ABILITY_MOLD_BREAKER, @@ -121,7 +159,9 @@ SINGLE_BATTLE_TEST("Mold Breaker, Teravolt, and Turboblaze ignore Clear Body") { for (k = 0; k < ARRAY_COUNT(breakerAbilities); k++) { - PARAMETRIZE{ move = statReductionMoves[j]; ability = breakerAbilities[k]; } + PARAMETRIZE{ species = SPECIES_METANG; ability = ABILITY_CLEAR_BODY; move = statReductionMoves[j]; breakerAbility = breakerAbilities[k]; } + PARAMETRIZE{ species = SPECIES_SOLGALEO; ability = ABILITY_FULL_METAL_BODY; move = statReductionMoves[j]; breakerAbility = breakerAbilities[k]; } + PARAMETRIZE{ species = SPECIES_TORKOAL; ability = ABILITY_WHITE_SMOKE; move = statReductionMoves[j]; breakerAbility = breakerAbilities[k]; } } } @@ -133,146 +173,242 @@ SINGLE_BATTLE_TEST("Mold Breaker, Teravolt, and Turboblaze ignore Clear Body") ASSUME(gMovesInfo[MOVE_SCARY_FACE].effect == EFFECT_SPEED_DOWN_2); ASSUME(gMovesInfo[MOVE_SWEET_SCENT].effect == (B_UPDATED_MOVE_DATA >= GEN_6 ? EFFECT_EVASION_DOWN_2 : EFFECT_EVASION_DOWN)); ASSUME(gMovesInfo[MOVE_SAND_ATTACK].effect == EFFECT_ACCURACY_DOWN); - PLAYER(SPECIES_WOBBUFFET) { Ability(ability); } - OPPONENT(SPECIES_BELDUM) { Ability(ABILITY_CLEAR_BODY); } + PLAYER(SPECIES_WOBBUFFET) { Ability(breakerAbility); } + OPPONENT(species) { Ability(ability); } } WHEN { TURN { MOVE(player, move); } } SCENE { - ANIMATION(ANIM_TYPE_MOVE, move, player); - NONE_OF { - ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY); - MESSAGE("Foe Beldum's Clear Body prevents stat loss!"); + if (ability == ABILITY_FULL_METAL_BODY){ // Full Metal Body can't be ignored by breaker abilities + NOT ANIMATION(ANIM_TYPE_MOVE, move, player); + ABILITY_POPUP(opponent, ability); + MESSAGE("Foe Solgaleo's Full Metal Body prevents stat loss!"); + } + else{ + ANIMATION(ANIM_TYPE_MOVE, move, player); + NONE_OF { + ABILITY_POPUP(opponent, ability); + MESSAGE("Foe Solgaleo's Full Metal Body prevents stat loss!"); + MESSAGE("Foe Torkoal's White Smoke prevents stat loss!"); + MESSAGE("Foe Metang's Clear Body prevents stat loss!"); + } } } } -SINGLE_BATTLE_TEST("Clear Body doesn't prevent Speed reduction from Iron Ball") +SINGLE_BATTLE_TEST("Clear Body, Full Metal Body, and White Smoke don't prevent Speed reduction from Iron Ball") { - u16 heldItem; - PARAMETRIZE{ heldItem = ITEM_NONE; } - PARAMETRIZE{ heldItem = ITEM_IRON_BALL; } + u32 j, species = SPECIES_NONE, ability = ABILITY_NONE; + u16 heldItem = ITEM_NONE; + static const u16 heldItems[] = { + ITEM_NONE, + ITEM_IRON_BALL, + }; + for (j = 0; j < ARRAY_COUNT(heldItems); j++) + { + PARAMETRIZE{ species = SPECIES_METANG; ability = ABILITY_CLEAR_BODY; heldItem = heldItems[j]; } + PARAMETRIZE{ species = SPECIES_SOLGALEO; ability = ABILITY_FULL_METAL_BODY; heldItem = heldItems[j]; } + PARAMETRIZE{ species = SPECIES_TORKOAL; ability = ABILITY_WHITE_SMOKE; heldItem = heldItems[j]; } + } GIVEN { ASSUME(gItemsInfo[ITEM_IRON_BALL].holdEffect == HOLD_EFFECT_IRON_BALL); PLAYER(SPECIES_WOBBUFFET) { Speed(4); } - OPPONENT(SPECIES_BELDUM) { Speed(6); Ability(ABILITY_CLEAR_BODY); Item(heldItem); } + OPPONENT(species) { Speed(6); Ability(ability); Item(heldItem); } } WHEN { TURN { } } SCENE { - NOT ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY); + NOT ABILITY_POPUP(opponent, ability); if (heldItem == ITEM_IRON_BALL) { MESSAGE("Wobbuffet used Celebrate!"); - MESSAGE("Foe Beldum used Celebrate!"); + if (ability == ABILITY_FULL_METAL_BODY) + MESSAGE("Foe Solgaleo used Celebrate!"); + else if (ability == ABILITY_WHITE_SMOKE) + MESSAGE("Foe Torkoal used Celebrate!"); + else + MESSAGE("Foe Metang used Celebrate!"); } else { - MESSAGE("Foe Beldum used Celebrate!"); + if (ability == ABILITY_FULL_METAL_BODY) + MESSAGE("Foe Solgaleo used Celebrate!"); + else if (ability == ABILITY_WHITE_SMOKE) + MESSAGE("Foe Torkoal used Celebrate!"); + else + MESSAGE("Foe Metang used Celebrate!"); MESSAGE("Wobbuffet used Celebrate!"); } } } -SINGLE_BATTLE_TEST("Clear Body doesn't prevent Speed reduction from paralysis") +SINGLE_BATTLE_TEST("Clear Body, Full Metal Body, and White Smoke don't prevent Speed reduction from paralysis") { + u32 species, ability; + + PARAMETRIZE{ species = SPECIES_METANG; ability = ABILITY_CLEAR_BODY; } + PARAMETRIZE{ species = SPECIES_SOLGALEO; ability = ABILITY_FULL_METAL_BODY; } + PARAMETRIZE{ species = SPECIES_TORKOAL; ability = ABILITY_WHITE_SMOKE; } + GIVEN { PLAYER(SPECIES_WOBBUFFET) { Speed(4); } - OPPONENT(SPECIES_BELDUM) { Speed(6); Ability(ABILITY_CLEAR_BODY); } + OPPONENT(species) { Speed(6); Ability(ability); } } WHEN { TURN { MOVE(player, MOVE_THUNDER_WAVE); } TURN { MOVE(player, MOVE_THUNDER_WAVE); } } SCENE { - MESSAGE("Foe Beldum used Celebrate!"); + if (ability == ABILITY_FULL_METAL_BODY) + MESSAGE("Foe Solgaleo used Celebrate!"); + else if (ability == ABILITY_WHITE_SMOKE) + MESSAGE("Foe Torkoal used Celebrate!"); + else + MESSAGE("Foe Metang used Celebrate!"); MESSAGE("Wobbuffet used Thunder Wave!"); ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDER_WAVE, player); - NOT ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY); + NOT ABILITY_POPUP(opponent, ability); MESSAGE("Wobbuffet used Thunder Wave!"); ONE_OF { - MESSAGE("Foe Beldum used Celebrate!"); - MESSAGE("Foe Beldum is paralyzed! It can't move!"); + MESSAGE("Foe Metang used Celebrate!"); + MESSAGE("Foe Metang is paralyzed! It can't move!"); + MESSAGE("Foe Solgaleo used Celebrate!"); + MESSAGE("Foe Solgaleo is paralyzed! It can't move!"); + MESSAGE("Foe Torkoal used Celebrate!"); + MESSAGE("Foe Torkoal is paralyzed! It can't move!"); } } } -SINGLE_BATTLE_TEST("Clear Body doesn't prevent Attack reduction from burn", s16 damage) +SINGLE_BATTLE_TEST("Clear Body, Full Metal Body, and White Smoke don't prevent Attack reduction from burn", s16 damage) { - bool32 burned; - PARAMETRIZE{ burned = FALSE; } - PARAMETRIZE{ burned = TRUE; } + bool32 burned = FALSE; + u32 species, ability; + PARAMETRIZE{ species = SPECIES_METANG; ability = ABILITY_CLEAR_BODY; burned = FALSE; } + PARAMETRIZE{ species = SPECIES_METANG; ability = ABILITY_CLEAR_BODY; burned = TRUE; } + PARAMETRIZE{ species = SPECIES_SOLGALEO; ability = ABILITY_FULL_METAL_BODY; burned = FALSE; } + PARAMETRIZE{ species = SPECIES_SOLGALEO; ability = ABILITY_FULL_METAL_BODY; burned = TRUE; } + PARAMETRIZE{ species = SPECIES_TORKOAL; ability = ABILITY_WHITE_SMOKE; burned = FALSE; } + PARAMETRIZE{ species = SPECIES_TORKOAL; ability = ABILITY_WHITE_SMOKE; burned = TRUE; } GIVEN { ASSUME(gMovesInfo[MOVE_TACKLE].category == DAMAGE_CATEGORY_PHYSICAL); PLAYER(SPECIES_WOBBUFFET) - OPPONENT(SPECIES_BELDUM) { Ability(ABILITY_CLEAR_BODY); if (burned) Status1(STATUS1_BURN); } + OPPONENT(species) { Ability(ability); if (burned) Status1(STATUS1_BURN); } } WHEN { TURN { MOVE(opponent, MOVE_TACKLE); } } SCENE { - NOT ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY); + NOT ABILITY_POPUP(opponent, ability); HP_BAR(player, captureDamage: &results[i].damage); } FINALLY { EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage); } } -SINGLE_BATTLE_TEST("Clear Body doesn't prevent receiving negative stat changes from Baton Pass") +SINGLE_BATTLE_TEST("Clear Body, Full Metal Body, and White Smoke don't prevent receiving negative stat changes from Baton Pass") { + u32 species, ability; + + PARAMETRIZE{ species = SPECIES_METANG; ability = ABILITY_CLEAR_BODY; } + PARAMETRIZE{ species = SPECIES_SOLGALEO; ability = ABILITY_FULL_METAL_BODY; } + PARAMETRIZE{ species = SPECIES_TORKOAL; ability = ABILITY_WHITE_SMOKE; } + GIVEN { ASSUME(gMovesInfo[MOVE_SCARY_FACE].effect == EFFECT_SPEED_DOWN_2); ASSUME(gMovesInfo[MOVE_BATON_PASS].effect == EFFECT_BATON_PASS); PLAYER(SPECIES_WOBBUFFET) { Speed(4); } OPPONENT(SPECIES_WOBBUFFET) { Speed(3); } - OPPONENT(SPECIES_BELDUM) { Speed(6); Ability(ABILITY_CLEAR_BODY); } + OPPONENT(species) { Speed(6); Ability(ability); } } WHEN { TURN { MOVE(player, MOVE_SCARY_FACE); MOVE(opponent, MOVE_BATON_PASS); SEND_OUT(opponent, 1); } TURN { MOVE(player, MOVE_SCARY_FACE); } } SCENE { MESSAGE("Wobbuffet used Scary Face!"); ANIMATION(ANIM_TYPE_MOVE, MOVE_SCARY_FACE, player); - ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY); - MESSAGE("Foe Beldum used Celebrate!"); + ABILITY_POPUP(opponent, ability); + if (ability == ABILITY_FULL_METAL_BODY) + MESSAGE("Foe Solgaleo used Celebrate!"); + else if (ability == ABILITY_WHITE_SMOKE) + MESSAGE("Foe Torkoal used Celebrate!"); + else + MESSAGE("Foe Metang used Celebrate!"); } } -SINGLE_BATTLE_TEST("Clear Body doesn't prevent Topsy-Turvy") +SINGLE_BATTLE_TEST("Clear Body, Full Metal Body, and White Smoke don't prevent Topsy-Turvy") { + u32 species, ability; + + PARAMETRIZE{ species = SPECIES_METANG; ability = ABILITY_CLEAR_BODY; } + PARAMETRIZE{ species = SPECIES_SOLGALEO; ability = ABILITY_FULL_METAL_BODY; } + PARAMETRIZE{ species = SPECIES_TORKOAL; ability = ABILITY_WHITE_SMOKE; } + GIVEN { ASSUME(gMovesInfo[MOVE_TOPSY_TURVY].effect == EFFECT_TOPSY_TURVY); ASSUME(gMovesInfo[MOVE_SCARY_FACE].effect == EFFECT_SPEED_DOWN_2); ASSUME(gMovesInfo[MOVE_BATON_PASS].effect == EFFECT_BATON_PASS); PLAYER(SPECIES_WOBBUFFET) { Speed(4); } OPPONENT(SPECIES_WOBBUFFET) { Speed(3); } - OPPONENT(SPECIES_BELDUM) { Speed(6); Ability(ABILITY_CLEAR_BODY); } + OPPONENT(species) { Speed(6); Ability(ability); } } WHEN { TURN { MOVE(player, MOVE_SCARY_FACE); MOVE(opponent, MOVE_BATON_PASS); SEND_OUT(opponent, 1); } TURN { MOVE(player, MOVE_TOPSY_TURVY); } TURN { MOVE(player, MOVE_SCARY_FACE); } } SCENE { MESSAGE("Wobbuffet used Topsy-Turvy!"); - NOT ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY); + NOT ABILITY_POPUP(opponent, ability); ANIMATION(ANIM_TYPE_MOVE, MOVE_TOPSY_TURVY, player); - MESSAGE("Foe Beldum used Celebrate!"); - MESSAGE("Foe Beldum used Celebrate!"); + if (ability == ABILITY_FULL_METAL_BODY) { + MESSAGE("Foe Solgaleo used Celebrate!"); + MESSAGE("Foe Solgaleo used Celebrate!"); + } + else if (ability == ABILITY_WHITE_SMOKE) { + MESSAGE("Foe Torkoal used Celebrate!"); + MESSAGE("Foe Torkoal used Celebrate!"); + } + else { + MESSAGE("Foe Metang used Celebrate!"); + MESSAGE("Foe Metang used Celebrate!"); + } MESSAGE("Wobbuffet used Scary Face!"); NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_SCARY_FACE, player); - ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY); + ABILITY_POPUP(opponent, ability); } } -SINGLE_BATTLE_TEST("Clear Body doesn't prevent Spectral Thief from resetting positive stat changes") +SINGLE_BATTLE_TEST("Clear Body, Full Metal Body, and White Smoke don't prevent Spectral Thief from resetting positive stat changes") { + u32 species, ability; + + PARAMETRIZE{ species = SPECIES_METANG; ability = ABILITY_CLEAR_BODY; } + PARAMETRIZE{ species = SPECIES_SOLGALEO; ability = ABILITY_FULL_METAL_BODY; } + PARAMETRIZE{ species = SPECIES_TORKOAL; ability = ABILITY_WHITE_SMOKE; } + GIVEN { ASSUME(MoveHasAdditionalEffect(MOVE_SPECTRAL_THIEF, MOVE_EFFECT_SPECTRAL_THIEF) == TRUE); ASSUME(gMovesInfo[MOVE_AGILITY].effect == EFFECT_SPEED_UP_2); PLAYER(SPECIES_WOBBUFFET) { Speed(4); } - OPPONENT(SPECIES_METANG) { Speed(5); Ability(ABILITY_CLEAR_BODY); } + OPPONENT(species) { Speed(5); Ability(ability); } } WHEN { TURN{ MOVE(opponent, MOVE_AGILITY); } TURN{ MOVE(player, MOVE_SPECTRAL_THIEF); } TURN{ } } SCENE { - MESSAGE("Foe Metang used Agility!"); + if (ability == ABILITY_FULL_METAL_BODY) + MESSAGE("Foe Solgaleo used Agility!"); + else if (ability == ABILITY_WHITE_SMOKE) + MESSAGE("Foe Torkoal used Agility!"); + else + MESSAGE("Foe Metang used Agility!"); ANIMATION(ANIM_TYPE_MOVE, MOVE_AGILITY, opponent); MESSAGE("Wobbuffet used Celebrate!"); - MESSAGE("Foe Metang used Celebrate!"); + if (ability == ABILITY_FULL_METAL_BODY) + MESSAGE("Foe Solgaleo used Celebrate!"); + else if (ability == ABILITY_WHITE_SMOKE) + MESSAGE("Foe Torkoal used Celebrate!"); + else + MESSAGE("Foe Metang used Celebrate!"); MESSAGE("Wobbuffet used SpectrlThief!"); ANIMATION(ANIM_TYPE_MOVE, MOVE_SPECTRAL_THIEF, player); - NOT ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY); + NOT ABILITY_POPUP(opponent, ability); MESSAGE("Wobbuffet used Celebrate!"); - MESSAGE("Foe Metang used Celebrate!"); + if (ability == ABILITY_FULL_METAL_BODY) + MESSAGE("Foe Solgaleo used Celebrate!"); + else if (ability == ABILITY_WHITE_SMOKE) + MESSAGE("Foe Torkoal used Celebrate!"); + else + MESSAGE("Foe Metang used Celebrate!"); } } diff --git a/test/battle/ability/full_metal_body.c b/test/battle/ability/full_metal_body.c deleted file mode 100644 index d00714d524..0000000000 --- a/test/battle/ability/full_metal_body.c +++ /dev/null @@ -1,38 +0,0 @@ -#include "global.h" -#include "test/battle.h" - -SINGLE_BATTLE_TEST("Full Metal Body prevents intimidate") -{ - s16 turnOneHit; - s16 turnTwoHit; - - GIVEN { - PLAYER(SPECIES_EKANS) { Ability(ABILITY_SHED_SKIN); }; - PLAYER(SPECIES_EKANS) { Ability(ABILITY_INTIMIDATE); }; - OPPONENT(SPECIES_SOLGALEO) { Ability(ABILITY_FULL_METAL_BODY); }; - } WHEN { - TURN { MOVE(opponent, MOVE_TACKLE); } - TURN { SWITCH(player, 1); MOVE(opponent, MOVE_TACKLE); } - - } SCENE { - HP_BAR(player, captureDamage: &turnOneHit); - ABILITY_POPUP(player, ABILITY_INTIMIDATE); - NONE_OF { ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); } - ABILITY_POPUP(opponent, ABILITY_FULL_METAL_BODY); - MESSAGE("Foe Solgaleo's Full Metal Body prevents stat loss!"); - HP_BAR(player, captureDamage: &turnTwoHit); - } THEN { - EXPECT_EQ(turnOneHit, turnTwoHit); - } -} - -TO_DO_BATTLE_TEST("Full Metal Body prevents stat stage reduction from moves"); // Growl, Leer, Confide, Fake Tears, Scary Face, Sweet Scent, Sand Attack (Attack, Defense, Sp. Attack, Sp. Defense, Speed, Evasion, Accuracy -TO_DO_BATTLE_TEST("Full Metal Body prevents Sticky Web"); -TO_DO_BATTLE_TEST("Full Metal Body doesn't prevent stat stage reduction from moves used by the user"); // e.g. Superpower -TO_DO_BATTLE_TEST("Full Metal Body doesn't prevent Speed reduction from Iron Ball"); -TO_DO_BATTLE_TEST("Full Metal Body doesn't prevent Speed reduction from paralysis"); -TO_DO_BATTLE_TEST("Full Metal Body doesn't prevent Attack reduction from burn"); -TO_DO_BATTLE_TEST("Full Metal Body doesn't prevent receiving negative stat changes from Baton Pass"); -TO_DO_BATTLE_TEST("Full Metal Body doesn't prevent Topsy-Turvy"); -TO_DO_BATTLE_TEST("Full Metal Body doesn't prevent Spectral Thief from resetting positive stat changes"); -TO_DO_BATTLE_TEST("Full Metal Body is ignored by Mold Breaker"); diff --git a/test/battle/ability/white_smoke.c b/test/battle/ability/white_smoke.c deleted file mode 100644 index 7bedbca242..0000000000 --- a/test/battle/ability/white_smoke.c +++ /dev/null @@ -1,39 +0,0 @@ -#include "global.h" -#include "test/battle.h" - -SINGLE_BATTLE_TEST("White Smoke prevents intimidate") -{ - s16 turnOneHit; - s16 turnTwoHit; - - GIVEN { - PLAYER(SPECIES_EKANS) { Ability(ABILITY_SHED_SKIN); }; - PLAYER(SPECIES_EKANS) { Ability(ABILITY_INTIMIDATE); }; - OPPONENT(SPECIES_TORKOAL) { Ability(ABILITY_WHITE_SMOKE); }; - } WHEN { - TURN { MOVE(opponent, MOVE_TACKLE); } - TURN { SWITCH(player, 1); MOVE(opponent, MOVE_TACKLE); } - - } SCENE { - HP_BAR(player, captureDamage: &turnOneHit); - ABILITY_POPUP(player, ABILITY_INTIMIDATE); - NONE_OF { ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); } - ABILITY_POPUP(opponent, ABILITY_WHITE_SMOKE); - MESSAGE("Foe Torkoal's White Smoke prevents stat loss!"); - HP_BAR(player, captureDamage: &turnTwoHit); - } THEN { - EXPECT_EQ(turnOneHit, turnTwoHit); - } -} - - -TO_DO_BATTLE_TEST("White Smoke prevents stat stage reduction from moves"); // Growl, Leer, Confide, Fake Tears, Scary Face, Sweet Scent, Sand Attack (Attack, Defense, Sp. Attack, Sp. Defense, Speed, Evasion, Accuracy -TO_DO_BATTLE_TEST("White Smoke prevents Sticky Web"); -TO_DO_BATTLE_TEST("White Smoke doesn't prevent stat stage reduction from moves used by the user"); // e.g. Superpower -TO_DO_BATTLE_TEST("White Smoke doesn't prevent Speed reduction from Iron Ball"); -TO_DO_BATTLE_TEST("White Smoke doesn't prevent Speed reduction from paralysis"); -TO_DO_BATTLE_TEST("White Smoke doesn't prevent Attack reduction from burn"); -TO_DO_BATTLE_TEST("White Smoke doesn't prevent receiving negative stat changes from Baton Pass"); -TO_DO_BATTLE_TEST("White Smoke doesn't prevent Topsy-Turvy"); -TO_DO_BATTLE_TEST("White Smoke doesn't prevent Spectral Thief from resetting positive stat changes"); -TO_DO_BATTLE_TEST("White Smoke is ignored by Mold Breaker"); From 4b1ff3ad7f0d32292ea094712768868da6c51991 Mon Sep 17 00:00:00 2001 From: DizzyEggg Date: Fri, 14 Jun 2024 09:31:21 +0200 Subject: [PATCH 04/14] fix starting terrain making all other terrains infinite (#4795) --- src/battle_script_commands.c | 4 +- src/battle_util.c | 2 +- test/battle/terrain/starting_terrain.c | 113 +++++++++++++++++++++++++ 3 files changed, 116 insertions(+), 3 deletions(-) create mode 100644 test/battle/terrain/starting_terrain.c diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 470229d14f..802e9bac56 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -16372,9 +16372,9 @@ void BS_SetRemoveTerrain(void) } else { - u16 atkHoldEffect = GetBattlerHoldEffect(gBattlerAttacker, TRUE); + u32 atkHoldEffect = GetBattlerHoldEffect(gBattlerAttacker, TRUE); - gFieldStatuses &= ~STATUS_FIELD_TERRAIN_ANY; + gFieldStatuses &= ~(STATUS_FIELD_TERRAIN_ANY | STATUS_FIELD_TERRAIN_PERMANENT); gFieldStatuses |= statusFlag; gFieldTimers.terrainTimer = (atkHoldEffect == HOLD_EFFECT_TERRAIN_EXTENDER) ? 8 : 5; gBattlescriptCurrInstr = cmd->nextInstr; diff --git a/src/battle_util.c b/src/battle_util.c index 9202db6f35..e65ba17d45 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -3886,7 +3886,7 @@ static bool32 TryChangeBattleTerrain(u32 battler, u32 statusFlag, u8 *timer) { if ((!(gFieldStatuses & statusFlag) && (!gBattleStruct->isSkyBattle))) { - gFieldStatuses &= ~(STATUS_FIELD_MISTY_TERRAIN | STATUS_FIELD_GRASSY_TERRAIN | STATUS_FIELD_ELECTRIC_TERRAIN | STATUS_FIELD_PSYCHIC_TERRAIN); + gFieldStatuses &= ~(STATUS_FIELD_TERRAIN_ANY | STATUS_FIELD_TERRAIN_PERMANENT); gFieldStatuses |= statusFlag; gDisableStructs[battler].terrainAbilityDone = FALSE; diff --git a/test/battle/terrain/starting_terrain.c b/test/battle/terrain/starting_terrain.c new file mode 100644 index 0000000000..37caa20bc7 --- /dev/null +++ b/test/battle/terrain/starting_terrain.c @@ -0,0 +1,113 @@ +#include "global.h" +#include "event_data.h" +#include "test/battle.h" + +#if B_VAR_STARTING_STATUS != 0 + +SINGLE_BATTLE_TEST("B_VAR_STARTING_STATUS starts a chosen terrain at the beginning of battle and lasts infinitely long") +{ + u16 terrain; + + PARAMETRIZE { terrain = STARTING_STATUS_GRASSY_TERRAIN; } + PARAMETRIZE { terrain = STARTING_STATUS_PSYCHIC_TERRAIN; } + PARAMETRIZE { terrain = STARTING_STATUS_MISTY_TERRAIN; } + PARAMETRIZE { terrain = STARTING_STATUS_ELECTRIC_TERRAIN; } + + VarSet(B_VAR_STARTING_STATUS, terrain); + VarSet(B_VAR_STARTING_STATUS_TIMER, 0); + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + // More than 5 turns + TURN { ; } + TURN { ; } + TURN { ; } + TURN { ; } + TURN { ; } + TURN { ; } + TURN { ; } + } SCENE { + switch (terrain) { + case STARTING_STATUS_GRASSY_TERRAIN: + MESSAGE("Grass grew to cover the battlefield!"); + break; + case STARTING_STATUS_PSYCHIC_TERRAIN: + MESSAGE("The battlefield got weird!"); + break; + case STARTING_STATUS_MISTY_TERRAIN: + MESSAGE("Mist swirled about the battlefield!"); + break; + case STARTING_STATUS_ELECTRIC_TERRAIN: + MESSAGE("An electric current runs across the battlefield!"); + break; + } + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_RESTORE_BG); + NONE_OF { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_RESTORE_BG); + MESSAGE("The weirdness disappeared from the battlefield."); + MESSAGE("The electricity disappeared from the battlefield."); + MESSAGE("The mist disappeared from the battlefield."); + MESSAGE("The grass disappeared from the battlefield."); + } + } THEN { + VarSet(B_VAR_STARTING_STATUS, 0); + } +} + +SINGLE_BATTLE_TEST("Terrain started after the one which started the battle lasts only 5 turns") +{ + bool32 viaMove; + + PARAMETRIZE { viaMove = TRUE; } + PARAMETRIZE { viaMove = FALSE; } + + VarSet(B_VAR_STARTING_STATUS, STARTING_STATUS_ELECTRIC_TERRAIN); + VarSet(B_VAR_STARTING_STATUS_TIMER, 0); + + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Ability(viaMove == TRUE ? ABILITY_SHADOW_TAG : ABILITY_GRASSY_SURGE); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + // More than 5 turns + TURN { MOVE(player, viaMove == TRUE ? MOVE_GRASSY_TERRAIN : MOVE_CELEBRATE); } + TURN { ; } + TURN { ; } + TURN { ; } + TURN { ; } + TURN { ; } + TURN { ; } + } SCENE { + // Electric Terrain at battle's start + MESSAGE("An electric current runs across the battlefield!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_RESTORE_BG); + // Player uses Grassy Terrain + if (viaMove) { + MESSAGE("Wobbuffet used GrssyTerrain!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_GRASSY_TERRAIN, player); + MESSAGE("Grass grew to cover the battlefield!"); + } else { + ABILITY_POPUP(player, ABILITY_GRASSY_SURGE); + MESSAGE("Grass grew to cover the battlefield!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_RESTORE_BG); + } + + // 5 turns + MESSAGE("Wobbuffet used Celebrate!"); + MESSAGE("Foe Wobbuffet used Celebrate!"); + + MESSAGE("Wobbuffet used Celebrate!"); + MESSAGE("Foe Wobbuffet used Celebrate!"); + + MESSAGE("Wobbuffet used Celebrate!"); + MESSAGE("Foe Wobbuffet used Celebrate!"); + + MESSAGE("The grass disappeared from the battlefield."); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_RESTORE_BG); + } THEN { + VarSet(B_VAR_STARTING_STATUS, 0); + } +} + +#endif // B_VAR_STARTING_STATUS From 1a4f277d6fe7c01797256e018f0269eba02fa635 Mon Sep 17 00:00:00 2001 From: DizzyEggg Date: Fri, 14 Jun 2024 09:43:10 +0200 Subject: [PATCH 05/14] Tests for Body Press + body press interaction with Wonder room (#4792) --- src/battle_util.c | 6 +- test/battle/move_effect/body_press.c | 96 +++++++++++++++++++++++++++- 2 files changed, 99 insertions(+), 3 deletions(-) diff --git a/src/battle_util.c b/src/battle_util.c index e65ba17d45..e97c9b3ee6 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -9218,7 +9218,11 @@ static inline u32 CalcAttackStat(u32 move, u32 battlerAtk, u32 battlerDef, u32 m else if (gMovesInfo[move].effect == EFFECT_BODY_PRESS) { atkStat = gBattleMons[battlerAtk].defense; - atkStage = gBattleMons[battlerAtk].statStages[STAT_DEF]; + // Edge case: Body Press used during Wonder Room. For some reason, it still uses Defense over Sp.Def, but uses Sp.Def stat changes + if (gFieldStatuses & STATUS_FIELD_WONDER_ROOM) + atkStage = gBattleMons[battlerAtk].statStages[STAT_SPDEF]; + else + atkStage = gBattleMons[battlerAtk].statStages[STAT_DEF]; } else { diff --git a/test/battle/move_effect/body_press.c b/test/battle/move_effect/body_press.c index e1bf54f4e8..d7dccf1428 100644 --- a/test/battle/move_effect/body_press.c +++ b/test/battle/move_effect/body_press.c @@ -1,8 +1,100 @@ #include "global.h" #include "test/battle.h" -TO_DO_BATTLE_TEST("Body Press's damage depends on the user's base Defense instead of its base Attack"); -TO_DO_BATTLE_TEST("Body Press's damage depends on the user's Defense stat stages"); +ASSUMPTIONS +{ + ASSUME(gMovesInfo[MOVE_BODY_PRESS].effect == EFFECT_BODY_PRESS); +} + +SINGLE_BATTLE_TEST("Body Press's damage depends on the user's base Defense instead of its base Attack", s16 damage) +{ + u32 def, atk; + PARAMETRIZE { def = 150; atk = 179; } // Atk is higher + PARAMETRIZE { atk = 150; def = 179; } // Atk is lower + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Attack(atk); Defense(def); } + } WHEN { + TURN { MOVE(opponent, MOVE_BODY_PRESS); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_BODY_PRESS, opponent); + HP_BAR(player, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_GT(results[1].damage, results[0].damage); + } +} + +SINGLE_BATTLE_TEST("Body Press's damage depends on the user's Defense and not Attack stat stages", s16 damage) +{ + u32 move; + + PARAMETRIZE { move = MOVE_IRON_DEFENSE; } + PARAMETRIZE { move = MOVE_SWORDS_DANCE; } + PARAMETRIZE { move = MOVE_CELEBRATE; } // Nothing, stats are default + GIVEN { + ASSUME(gMovesInfo[MOVE_IRON_DEFENSE].effect == EFFECT_DEFENSE_UP_2); + ASSUME(gMovesInfo[MOVE_SWORDS_DANCE].effect == EFFECT_ATTACK_UP_2); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Attack(150); Defense(150); } + } WHEN { + TURN { MOVE(opponent, move); } + TURN { MOVE(opponent, MOVE_BODY_PRESS); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, move, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BODY_PRESS, opponent); + HP_BAR(player, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_GT(results[0].damage, results[1].damage); + EXPECT_EQ(results[1].damage, results[2].damage); + } +} + +SINGLE_BATTLE_TEST("Body Press uses Defense Stat even in Wonder Room", s16 damage) +{ + u32 move; + + PARAMETRIZE { move = MOVE_WONDER_ROOM; } + PARAMETRIZE { move = MOVE_CELEBRATE; } + GIVEN { + ASSUME(gMovesInfo[MOVE_WONDER_ROOM].effect == EFFECT_WONDER_ROOM); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { SpDefense(50); Defense(150); } + } WHEN { + TURN { MOVE(opponent, move); } + TURN { MOVE(opponent, MOVE_BODY_PRESS); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, move, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BODY_PRESS, opponent); + HP_BAR(player, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_EQ(results[0].damage, results[1].damage); + } +} + +SINGLE_BATTLE_TEST("Body Press uses Special Defense stat Stages in Wonder Room", s16 damage) +{ + u32 move; + + PARAMETRIZE { move = MOVE_IRON_DEFENSE; } + PARAMETRIZE { move = MOVE_AMNESIA; } + PARAMETRIZE { move = MOVE_CELEBRATE; } // Nothing, stats are default + GIVEN { + ASSUME(gMovesInfo[MOVE_IRON_DEFENSE].effect == EFFECT_DEFENSE_UP_2); + ASSUME(gMovesInfo[MOVE_AMNESIA].effect == EFFECT_SPECIAL_DEFENSE_UP_2); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { SpDefense(150); Defense(150); } + } WHEN { + TURN { MOVE(opponent, move); MOVE(player, MOVE_WONDER_ROOM); } + TURN { MOVE(opponent, MOVE_BODY_PRESS); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, move, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BODY_PRESS, opponent); + HP_BAR(player, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_GT(results[1].damage, results[0].damage); + EXPECT_EQ(results[0].damage, results[2].damage); + } +} // Could be split into multiple tests or maybe to separate files based on the modifier? TO_DO_BATTLE_TEST("Body Press's damage is influenced by all other Attack modifiers that are not stat stages"); From c7224d9ca7c3592d61e6d092007ed42bfd6c0b7c Mon Sep 17 00:00:00 2001 From: DizzyEggg Date: Fri, 14 Jun 2024 10:24:57 +0200 Subject: [PATCH 06/14] Fix Relic Song transforming species other than Meloetta (#4799) --- src/battle_script_commands.c | 5 ++++- test/battle/move_effect/relic_song.c | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 802e9bac56..4648ceb4fb 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -16468,7 +16468,8 @@ void BS_TryRelicSong(void) { NATIVE_ARGS(); - if (GetBattlerAbility(gBattlerAttacker) != ABILITY_SHEER_FORCE && !(gBattleMons[gBattlerAttacker].status2 & STATUS2_TRANSFORMED)) + if (GetBattlerAbility(gBattlerAttacker) != ABILITY_SHEER_FORCE && !(gBattleMons[gBattlerAttacker].status2 & STATUS2_TRANSFORMED) + && (gBattleMons[gBattlerAttacker].species == SPECIES_MELOETTA_ARIA || gBattleMons[gBattlerAttacker].species == SPECIES_MELOETTA_PIROUETTE)) { if (gBattleMons[gBattlerAttacker].species == SPECIES_MELOETTA_ARIA) gBattleMons[gBattlerAttacker].species = SPECIES_MELOETTA_PIROUETTE; @@ -16479,7 +16480,9 @@ void BS_TryRelicSong(void) gBattlescriptCurrInstr = BattleScript_AttackerFormChangeMoveEffect; } else + { gBattlescriptCurrInstr = cmd->nextInstr; + } } void BS_SetPledge(void) diff --git a/test/battle/move_effect/relic_song.c b/test/battle/move_effect/relic_song.c index 52db4a3601..3ea405cc50 100644 --- a/test/battle/move_effect/relic_song.c +++ b/test/battle/move_effect/relic_song.c @@ -85,6 +85,25 @@ SINGLE_BATTLE_TEST("Relic Song transforms Meloetta if used successfully") } } +SINGLE_BATTLE_TEST("Relic Song does not transform Pokemon other than Meloetta") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_RELIC_SONG); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_RELIC_SONG, player); + HP_BAR(opponent); + NONE_OF { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, player); + MESSAGE("Wobbuffet transformed!"); + } + } THEN { + EXPECT_EQ(player->species, SPECIES_WOBBUFFET); + } +} + SINGLE_BATTLE_TEST("Relic Song transforms Meloetta twice if used successfully") { GIVEN { From 2716ec5b03aba52a4f27b6f6d06fee629b7fd9c6 Mon Sep 17 00:00:00 2001 From: DizzyEggg Date: Fri, 14 Jun 2024 12:46:03 +0200 Subject: [PATCH 07/14] Fix Pursuit not getting Choice-locked on switch-out (#4801) * Fix Pursuit not getting Choice-locked on switch-out * use label --- data/battle_scripts_1.s | 4 ++- src/battle_script_commands.c | 47 ++++++++++++++++--------------- test/battle/move_effect/pursuit.c | 21 ++++++++++++++ 3 files changed, 49 insertions(+), 23 deletions(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 93a59090d0..33b3095953 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -5739,6 +5739,7 @@ BattleScript_DoSwitchOut:: BattleScript_PursuitDmgOnSwitchOut:: pause B_WAIT_TIME_SHORT + orword gHitMarker, HITMARKER_OBEYS attackstring ppreduce critcalc @@ -5756,11 +5757,12 @@ BattleScript_PursuitDmgOnSwitchOut:: resultmessage waitmessage B_WAIT_TIME_LONG tryfaintmon BS_TARGET - moveendfromto MOVEEND_ABILITIES, MOVEEND_CHOICE_MOVE + moveendfromto MOVEEND_ABILITIES, MOVEEND_ATTACKER_INVISIBLE @ MOVEEND_CHOICE_MOVE has to be included jumpiffainted BS_TARGET, FALSE, BattleScript_PursuitDmgOnSwitchOutRet setbyte sGIVEEXP_STATE, 0 getexp BS_TARGET BattleScript_PursuitDmgOnSwitchOutRet: + bicword gHitMarker, HITMARKER_OBEYS return BattleScript_Pausex20:: diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 4648ceb4fb..83a078d5fa 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -5419,7 +5419,6 @@ static void Cmd_moveend(void) bool32 effect = FALSE; u32 moveType = 0; u32 holdEffectAtk = 0; - u16 *choicedMoveAtk = NULL; u32 endMode, endState; u32 originallyUsedMove; @@ -5432,7 +5431,6 @@ static void Cmd_moveend(void) endState = cmd->endState; holdEffectAtk = GetBattlerHoldEffect(gBattlerAttacker, TRUE); - choicedMoveAtk = &gBattleStruct->choicedMove[gBattlerAttacker]; GET_MOVE_TYPE(gCurrentMove, moveType); do @@ -5642,29 +5640,34 @@ static void Cmd_moveend(void) gBattleScripting.moveendState++; break; case MOVEEND_CHOICE_MOVE: // update choice band move - if (gHitMarker & HITMARKER_OBEYS - && (HOLD_EFFECT_CHOICE(holdEffectAtk) || GetBattlerAbility(gBattlerAttacker) == ABILITY_GORILLA_TACTICS) - && gChosenMove != MOVE_STRUGGLE - && (*choicedMoveAtk == MOVE_NONE || *choicedMoveAtk == MOVE_UNAVAILABLE)) { - if ((gMovesInfo[gChosenMove].effect == EFFECT_BATON_PASS - || gMovesInfo[gChosenMove].effect == EFFECT_HEALING_WISH) - && !(gMoveResultFlags & MOVE_RESULT_FAILED)) + u16 *choicedMoveAtk = &gBattleStruct->choicedMove[gBattlerAttacker]; + if (gHitMarker & HITMARKER_OBEYS + && (HOLD_EFFECT_CHOICE(holdEffectAtk) || GetBattlerAbility(gBattlerAttacker) == ABILITY_GORILLA_TACTICS) + && gChosenMove != MOVE_STRUGGLE + && (*choicedMoveAtk == MOVE_NONE || *choicedMoveAtk == MOVE_UNAVAILABLE)) { - gBattleScripting.moveendState++; - break; + if ((gMovesInfo[gChosenMove].effect == EFFECT_BATON_PASS + || gMovesInfo[gChosenMove].effect == EFFECT_HEALING_WISH) + && !(gMoveResultFlags & MOVE_RESULT_FAILED)) + { + gBattleScripting.moveendState++; + break; + } + *choicedMoveAtk = gChosenMove; } - *choicedMoveAtk = gChosenMove; + for (i = 0; i < MAX_MON_MOVES; i++) + { + if (gBattleMons[gBattlerAttacker].moves[i] == *choicedMoveAtk) + break; + } + if (i == MAX_MON_MOVES) + { + *choicedMoveAtk = MOVE_NONE; + } + gBattleScripting.moveendState++; + break; } - for (i = 0; i < MAX_MON_MOVES; i++) - { - if (gBattleMons[gBattlerAttacker].moves[i] == *choicedMoveAtk) - break; - } - if (i == MAX_MON_MOVES) - *choicedMoveAtk = MOVE_NONE; - gBattleScripting.moveendState++; - break; case MOVEEND_CHANGED_ITEMS: // changed held items for (i = 0; i < gBattlersCount; i++) { @@ -13504,7 +13507,7 @@ static void Cmd_jumpifnopursuitswitchdmg(void) gActionsByTurnOrder[i] = B_ACTION_TRY_FINISH; } - gCurrentMove = gChosenMoveByBattler[gBattlerTarget]; + gCurrentMove = gChosenMove = gChosenMoveByBattler[gBattlerTarget]; gCurrMovePos = gChosenMovePos = *(gBattleStruct->chosenMovePositions + gBattlerTarget); gBattlescriptCurrInstr = cmd->nextInstr; gBattleScripting.animTurn = 1; diff --git a/test/battle/move_effect/pursuit.c b/test/battle/move_effect/pursuit.c index 0b517d4228..ec2345dbdd 100644 --- a/test/battle/move_effect/pursuit.c +++ b/test/battle/move_effect/pursuit.c @@ -25,4 +25,25 @@ SINGLE_BATTLE_TEST("Pursuited mon correctly switches out after it got hit and ac } } +// Checked so that Pursuit has only 1 PP and it forces the player to use Struggle. +SINGLE_BATTLE_TEST("Pursuit becomes a locked move after being used on switch-out while holding a Choice Item") +{ + GIVEN { + ASSUME(gItemsInfo[ITEM_CHOICE_BAND].holdEffect == HOLD_EFFECT_CHOICE_BAND); + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_CHOICE_BAND); MovesWithPP({MOVE_PURSUIT, 1}, {MOVE_CELEBRATE, 10}, {MOVE_WATER_GUN, 10}, {MOVE_TACKLE, 10}); } + OPPONENT(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { SWITCH(opponent, 1); MOVE(player, MOVE_PURSUIT); } + TURN { FORCED_MOVE(player); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_PURSUIT, player); + HP_BAR(opponent); + MESSAGE("2 sent out Wobbuffet!"); + + MESSAGE("Wobbuffet used Struggle!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_STRUGGLE, player); + } +} + TO_DO_BATTLE_TEST("Baton Pass doesn't cause Pursuit to increase its power or priority"); From de7a4e2328eac620d6a5592f003f4ed1a87b4685 Mon Sep 17 00:00:00 2001 From: DizzyEggg Date: Fri, 14 Jun 2024 13:15:38 +0200 Subject: [PATCH 08/14] Fix Encore turn amount bug (#4802) * Encore 3 turns * remove duplicate --- src/battle_script_commands.c | 6 +++- test/battle/move_effect/encore.c | 62 ++++++++++++++++++++++++++++---- 2 files changed, 61 insertions(+), 7 deletions(-) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 83a078d5fa..2306cb4dd6 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -12800,7 +12800,11 @@ static void Cmd_trysetencore(void) { gDisableStructs[gBattlerTarget].encoredMove = gBattleMons[gBattlerTarget].moves[i]; gDisableStructs[gBattlerTarget].encoredMovePos = i; - gDisableStructs[gBattlerTarget].encoreTimer = 3; + // Encore always lasts 3 turns, but we need to account for a scenario where Encore changes the move during the same turn. + if (GetBattlerTurnOrderNum(gBattlerAttacker) > GetBattlerTurnOrderNum(gBattlerTarget)) + gDisableStructs[gBattlerTarget].encoreTimer = 4; + else + gDisableStructs[gBattlerTarget].encoreTimer = 3; gBattlescriptCurrInstr = cmd->nextInstr; } else diff --git a/test/battle/move_effect/encore.c b/test/battle/move_effect/encore.c index 670122dc37..db7f5eb042 100644 --- a/test/battle/move_effect/encore.c +++ b/test/battle/move_effect/encore.c @@ -6,40 +6,90 @@ ASSUMPTIONS ASSUME(gMovesInfo[MOVE_ENCORE].effect == EFFECT_ENCORE); } -SINGLE_BATTLE_TEST("Encore forces consecutive move uses for 2 turns for player") +SINGLE_BATTLE_TEST("Encore forces consecutive move uses for 3 turns for player: Encore used before move") { GIVEN { - PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET) { Speed(10); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(20); } + } WHEN { + TURN { MOVE(opponent, MOVE_CELEBRATE); MOVE(player, MOVE_CELEBRATE); } + TURN { MOVE(opponent, MOVE_ENCORE); MOVE(player, MOVE_CELEBRATE); } + // TURN { FORCED_MOVE(player); } + TURN { FORCED_MOVE(player); } + TURN { FORCED_MOVE(player); } + TURN { MOVE(player, MOVE_SPLASH); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ENCORE, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SPLASH, player); + } +} + +SINGLE_BATTLE_TEST("Encore forces consecutive move uses for 3 turns for player: Encore used after move") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Speed(20); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(10); } } WHEN { TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_ENCORE); } TURN { FORCED_MOVE(player); } TURN { FORCED_MOVE(player); } + TURN { FORCED_MOVE(player); } TURN { MOVE(player, MOVE_SPLASH); } } SCENE { ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player); ANIMATION(ANIM_TYPE_MOVE, MOVE_ENCORE, opponent); ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player); ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player); ANIMATION(ANIM_TYPE_MOVE, MOVE_SPLASH, player); } } -SINGLE_BATTLE_TEST("Encore forces consecutive move uses for 2 turns for opponent") +SINGLE_BATTLE_TEST("Encore forces consecutive move uses for 3 turns for opponent: Encore used before move") { GIVEN { - PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET) { Speed(20); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(10); } + } WHEN { + TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_CELEBRATE); } + TURN { MOVE(player, MOVE_ENCORE); MOVE(opponent, MOVE_CELEBRATE); } + // TURN { FORCED_MOVE(opponent); } + TURN { FORCED_MOVE(opponent); } + TURN { FORCED_MOVE(opponent); } + TURN { MOVE(opponent, MOVE_SPLASH); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ENCORE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SPLASH, opponent); + } +} + +SINGLE_BATTLE_TEST("Encore forces consecutive move uses for 3 turns for opponent: Encore used after move") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Speed(10); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(20); } } WHEN { TURN { MOVE(opponent, MOVE_CELEBRATE); MOVE(player, MOVE_ENCORE); } TURN { FORCED_MOVE(opponent); } TURN { FORCED_MOVE(opponent); } + TURN { FORCED_MOVE(opponent); } TURN { MOVE(opponent, MOVE_SPLASH); } } SCENE { ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent); ANIMATION(ANIM_TYPE_MOVE, MOVE_ENCORE, player); ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent); ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent); ANIMATION(ANIM_TYPE_MOVE, MOVE_SPLASH, opponent); } } From e18150fb3559c5c8ceeacc9d914f3bb0f4622496 Mon Sep 17 00:00:00 2001 From: sneed <56992013+Sneed69@users.noreply.github.com> Date: Sat, 15 Jun 2024 16:35:43 +0300 Subject: [PATCH 09/14] Change code to reflect that Shell Side Arm's default category is special (#4806) * swapped cat is physical * Update battle_util.c * Update src/battle_util.c Co-authored-by: Alex <93446519+AlexOn1ine@users.noreply.github.com> --------- Co-authored-by: Alex <93446519+AlexOn1ine@users.noreply.github.com> --- src/battle_ai_util.c | 2 +- src/battle_script_commands.c | 2 +- src/battle_util.c | 8 +++++--- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 515c28d85c..ea50b636a6 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -477,7 +477,7 @@ s32 AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u8 *typeEffectivenes } else if (gMovesInfo[move].effect == EFFECT_PHOTON_GEYSER) gBattleStruct->swapDamageCategory = (GetCategoryBasedOnStats(gBattlerAttacker) == DAMAGE_CATEGORY_PHYSICAL); - else if (move == MOVE_SHELL_SIDE_ARM && gBattleStruct->shellSideArmCategory[battlerAtk][battlerDef] == DAMAGE_CATEGORY_SPECIAL) + else if (move == MOVE_SHELL_SIDE_ARM && gBattleStruct->shellSideArmCategory[battlerAtk][battlerDef] == DAMAGE_CATEGORY_PHYSICAL) gBattleStruct->swapDamageCategory = TRUE; else if (gMovesInfo[move].effect == EFFECT_NATURE_POWER) move = GetNaturePowerMove(); diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 2306cb4dd6..d60e4b5524 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -2009,7 +2009,7 @@ static void Cmd_damagecalc(void) u8 moveType; GET_MOVE_TYPE(gCurrentMove, moveType); - if (gBattleStruct->shellSideArmCategory[gBattlerAttacker][gBattlerTarget] == DAMAGE_CATEGORY_SPECIAL && gCurrentMove == MOVE_SHELL_SIDE_ARM) + if (gBattleStruct->shellSideArmCategory[gBattlerAttacker][gBattlerTarget] == DAMAGE_CATEGORY_PHYSICAL && gCurrentMove == MOVE_SHELL_SIDE_ARM) gBattleStruct->swapDamageCategory = TRUE; gBattleMoveDamage = CalculateMoveDamage(gCurrentMove, gBattlerAttacker, gBattlerTarget, moveType, 0, gIsCriticalHit, TRUE, TRUE); gBattlescriptCurrInstr = cmd->nextInstr; diff --git a/src/battle_util.c b/src/battle_util.c index e97c9b3ee6..ad1648dbd8 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -8264,7 +8264,7 @@ bool32 IsMoveMakingContact(u32 move, u32 battlerAtk) if (!gMovesInfo[move].makesContact) { - if (move == MOVE_SHELL_SIDE_ARM && gBattleStruct->shellSideArmCategory[battlerAtk][gBattlerTarget] == DAMAGE_CATEGORY_SPECIAL) + if (move == MOVE_SHELL_SIDE_ARM && gBattleStruct->shellSideArmCategory[battlerAtk][gBattlerTarget] == DAMAGE_CATEGORY_PHYSICAL) return TRUE; else return FALSE; @@ -8304,7 +8304,7 @@ bool32 IsBattlerProtected(u32 battlerAtk, u32 battlerDef, u32 move) // Protective Pads doesn't stop Unseen Fist from bypassing Protect effects, so IsMoveMakingContact() isn't used here. // This means extra logic is needed to handle Shell Side Arm. if (GetBattlerAbility(gBattlerAttacker) == ABILITY_UNSEEN_FIST - && (gMovesInfo[move].makesContact || (move == MOVE_SHELL_SIDE_ARM && gBattleStruct->shellSideArmCategory[battlerAtk][battlerDef] == DAMAGE_CATEGORY_SPECIAL)) + && (gMovesInfo[move].makesContact || (move == MOVE_SHELL_SIDE_ARM && gBattleStruct->shellSideArmCategory[battlerAtk][battlerDef] == DAMAGE_CATEGORY_PHYSICAL)) && !gProtectStructs[battlerDef].maxGuarded) // Max Guard cannot be bypassed by Unseen Fist return FALSE; else if (gMovesInfo[move].ignoresProtect) @@ -11429,7 +11429,9 @@ void SetShellSideArmCategory(void) special = ((((2 * gBattleMons[battlerAtk].level / 5 + 2) * gMovesInfo[MOVE_SHELL_SIDE_ARM].power * attackerSpAtkStat) / targetSpDefStat) / 50); - if (((physical > special) || (physical == special && RandomPercentage(RNG_SHELL_SIDE_ARM, 50)))) + if ((physical > special) || (physical == special && RandomPercentage(RNG_SHELL_SIDE_ARM, 50))) + gBattleStruct->shellSideArmCategory[battlerAtk][battlerDef] = DAMAGE_CATEGORY_PHYSICAL; + else gBattleStruct->shellSideArmCategory[battlerAtk][battlerDef] = DAMAGE_CATEGORY_SPECIAL; } } From b8607fe3aaa28af75ec98407d36d10cb2955975e Mon Sep 17 00:00:00 2001 From: kittenchilly Date: Sat, 15 Jun 2024 17:31:29 -0500 Subject: [PATCH 10/14] Fix Poltchageist not always producing counterfeit offspring (#4812) --- src/daycare.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/daycare.c b/src/daycare.c index ae734b41a1..d007de359d 100644 --- a/src/daycare.c +++ b/src/daycare.c @@ -1078,12 +1078,14 @@ static u16 DetermineEggSpeciesAndParentSlots(struct DayCare *daycare, u8 *parent eggSpecies = SPECIES_ILLUMISE; else if (eggSpecies == SPECIES_MANAPHY) eggSpecies = SPECIES_PHIONE; - else if (eggSpecies == SPECIES_SINISTEA_ANTIQUE) - eggSpecies = SPECIES_SINISTEA_PHONY; else if (GET_BASE_SPECIES_ID(eggSpecies) == SPECIES_ROTOM) eggSpecies = SPECIES_ROTOM; else if (GET_BASE_SPECIES_ID(eggSpecies) == SPECIES_FURFROU) eggSpecies = SPECIES_FURFROU; + else if (eggSpecies == SPECIES_SINISTEA_ANTIQUE) + eggSpecies = SPECIES_SINISTEA_PHONY; + else if (eggSpecies == SPECIES_POLTCHAGEIST_ARTISAN) + eggSpecies = SPECIES_POLTCHAGEIST_COUNTERFEIT; // To avoid single-stage Totem Pokémon to breed more of themselves. else if (eggSpecies == SPECIES_MIMIKYU_TOTEM_DISGUISED) eggSpecies = SPECIES_MIMIKYU_DISGUISED; From 107cb96a9879fcb9c18d446ffe8ad673b69302dd Mon Sep 17 00:00:00 2001 From: sneed <56992013+Sneed69@users.noreply.github.com> Date: Sun, 16 Jun 2024 16:02:44 +0300 Subject: [PATCH 11/14] Tests for Anger Point and Moxie (#4811) * Create anger_point.c * Create moxie.c * Add extra test and animations * add another test --- test/battle/ability/anger_point.c | 73 ++++++++++++++++++ test/battle/ability/moxie.c | 121 ++++++++++++++++++++++++++++++ 2 files changed, 194 insertions(+) create mode 100644 test/battle/ability/anger_point.c create mode 100644 test/battle/ability/moxie.c diff --git a/test/battle/ability/anger_point.c b/test/battle/ability/anger_point.c new file mode 100644 index 0000000000..0b13b9df4b --- /dev/null +++ b/test/battle/ability/anger_point.c @@ -0,0 +1,73 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Anger Point raises Attack stage to maximum after receiving a critical hit") +{ + ASSUME(gMovesInfo[MOVE_FROST_BREATH].alwaysCriticalHit); + + GIVEN { + PLAYER(SPECIES_PRIMEAPE) { Ability(ABILITY_ANGER_POINT); } + OPPONENT(SPECIES_SNORUNT); + } WHEN { + TURN { MOVE(opponent, MOVE_FROST_BREATH); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_FROST_BREATH, opponent); + MESSAGE("A critical hit!"); + ABILITY_POPUP(player, ABILITY_ANGER_POINT); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Primeape's Anger Point maxed its Attack!"); + } THEN { + EXPECT_EQ(player->statStages[STAT_ATK], MAX_STAT_STAGE); + } +} + +SINGLE_BATTLE_TEST("Anger Point does not trigger when already at maximum Attack stage") +{ + ASSUME(gMovesInfo[MOVE_FROST_BREATH].alwaysCriticalHit); + ASSUME(gMovesInfo[MOVE_BELLY_DRUM].effect == EFFECT_BELLY_DRUM); + + GIVEN { + PLAYER(SPECIES_PRIMEAPE) { Ability(ABILITY_ANGER_POINT); Speed(2); } + OPPONENT(SPECIES_SNORUNT) { Speed(1); } + } WHEN { + TURN { MOVE(player, MOVE_BELLY_DRUM); MOVE(opponent, MOVE_FROST_BREATH); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_BELLY_DRUM, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Primeape cut its own HP and maximized ATTACK!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_FROST_BREATH, opponent); + MESSAGE("A critical hit!"); + NONE_OF { + ABILITY_POPUP(player, ABILITY_ANGER_POINT); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Primeape's Anger Point maxed its Attack!"); + } + } THEN { + EXPECT_EQ(player->statStages[STAT_ATK], MAX_STAT_STAGE); + } +} + +SINGLE_BATTLE_TEST("Anger Point does not trigger when a substitute takes the hit") +{ + ASSUME(gMovesInfo[MOVE_FROST_BREATH].alwaysCriticalHit); + ASSUME(gMovesInfo[MOVE_SUBSTITUTE].effect == EFFECT_SUBSTITUTE); + + GIVEN { + PLAYER(SPECIES_PRIMEAPE) { Ability(ABILITY_ANGER_POINT); Speed(2); } + OPPONENT(SPECIES_SNORUNT) { Speed(1); } + } WHEN { + TURN { MOVE(player, MOVE_SUBSTITUTE); MOVE(opponent, MOVE_FROST_BREATH); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SUBSTITUTE, player); + MESSAGE("Primeape made a SUBSTITUTE!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_FROST_BREATH, opponent); + MESSAGE("A critical hit!"); + NONE_OF { + ABILITY_POPUP(player, ABILITY_ANGER_POINT); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Primeape's Anger Point maxed its Attack!"); + } + } THEN { + EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE); + } +} diff --git a/test/battle/ability/moxie.c b/test/battle/ability/moxie.c new file mode 100644 index 0000000000..b60a11507a --- /dev/null +++ b/test/battle/ability/moxie.c @@ -0,0 +1,121 @@ +#include "global.h" +#include "test/battle.h" + +DOUBLE_BATTLE_TEST("Moxie raises Attack by one stage after directly causing a Pokemon to faint") +{ + ASSUME(gMovesInfo[MOVE_EARTHQUAKE].target == MOVE_TARGET_FOES_AND_ALLY); + + GIVEN { + PLAYER(SPECIES_SALAMENCE) { Ability(ABILITY_MOXIE); } + PLAYER(SPECIES_SNORUNT) { HP(1); } + OPPONENT(SPECIES_GLALIE) { HP(1); } + OPPONENT(SPECIES_ABRA) { HP(1); } + OPPONENT(SPECIES_ABRA); + } WHEN { + TURN { MOVE(playerLeft, MOVE_EARTHQUAKE); SEND_OUT(opponentLeft, 2); } + } SCENE { + int i; + + ANIMATION(ANIM_TYPE_MOVE, MOVE_EARTHQUAKE, playerLeft); + for (i = 0; i < 3; i++) { + ONE_OF { + MESSAGE("Snorunt fainted!"); + MESSAGE("Foe Glalie fainted!"); + MESSAGE("Foe Abra fainted!"); + } + ABILITY_POPUP(playerLeft, ABILITY_MOXIE); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft); + MESSAGE("Salamence's Moxie raised its Attack!"); + } + } THEN { + EXPECT_EQ(playerLeft->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 3); + } +} + +DOUBLE_BATTLE_TEST("Moxie does not trigger if Pokemon faint to indirect damage or damage from other Pokemon") +{ + GIVEN { + PLAYER(SPECIES_SALAMENCE) { Ability(ABILITY_MOXIE); } + PLAYER(SPECIES_SNORUNT) { HP(1); Status1(STATUS1_POISON); } + OPPONENT(SPECIES_GLALIE) { HP(1); Status1(STATUS1_BURN); } + OPPONENT(SPECIES_ABRA) { HP(1); } + OPPONENT(SPECIES_ABRA); + } WHEN { + TURN { MOVE(playerRight, MOVE_QUICK_ATTACK, target: opponentRight); SEND_OUT(opponentLeft, 2); } + } SCENE { + int i; + + ANIMATION(ANIM_TYPE_MOVE, MOVE_QUICK_ATTACK, playerRight); + for (i = 0; i < 3; i++) { + ONE_OF { + MESSAGE("Snorunt fainted!"); + MESSAGE("Foe Glalie fainted!"); + MESSAGE("Foe Abra fainted!"); + } + NONE_OF { + ABILITY_POPUP(playerLeft, ABILITY_MOXIE); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft); + MESSAGE("Salamence's Moxie raised its Attack!"); + } + } + } THEN { + EXPECT_EQ(playerLeft->statStages[STAT_ATK], DEFAULT_STAT_STAGE); + } +} + +SINGLE_BATTLE_TEST("Moxie does not trigger when already at maximum Attack stage") +{ + ASSUME(gMovesInfo[MOVE_BELLY_DRUM].effect == EFFECT_BELLY_DRUM); + + GIVEN { + PLAYER(SPECIES_SALAMENCE) { Ability(ABILITY_MOXIE); } + OPPONENT(SPECIES_SNORUNT) { HP(1); } + OPPONENT(SPECIES_SNORUNT); + } WHEN { + TURN { MOVE(player, MOVE_BELLY_DRUM); } + TURN { MOVE(player, MOVE_QUICK_ATTACK); SEND_OUT(opponent, 1); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_BELLY_DRUM, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Salamence cut its own HP and maximized ATTACK!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_QUICK_ATTACK, player); + MESSAGE("Foe Snorunt fainted!"); + NONE_OF { + ABILITY_POPUP(player, ABILITY_MOXIE); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Salamence's Moxie raised its Attack!"); + } + } THEN { + EXPECT_EQ(player->statStages[STAT_ATK], MAX_STAT_STAGE); + } +} + +DOUBLE_BATTLE_TEST("Moxie does not increase damage done by the same move that causes another Pokemon to faint") +{ + s16 damage[2]; + + ASSUME(gMovesInfo[MOVE_EARTHQUAKE].target == MOVE_TARGET_FOES_AND_ALLY); + + KNOWN_FAILING; // Requires simultaneous damage implementation + GIVEN { + PLAYER(SPECIES_SALAMENCE) { Ability(ABILITY_MOXIE); } + PLAYER(SPECIES_ABRA) { HP(1); } + OPPONENT(SPECIES_GLALIE); + OPPONENT(SPECIES_GLALIE); + OPPONENT(SPECIES_ABRA); + } WHEN { + TURN { MOVE(playerLeft, MOVE_EARTHQUAKE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_EARTHQUAKE, playerLeft); + HP_BAR(opponentLeft, captureDamage: &damage[0]); + HP_BAR(playerRight); + MESSAGE("Abra fainted!"); + ABILITY_POPUP(playerLeft, ABILITY_MOXIE); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft); + MESSAGE("Salamence's Moxie raised its Attack!"); + HP_BAR(opponentRight, captureDamage: &damage[1]); + } THEN { + EXPECT_EQ(playerLeft->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(damage[0], damage[1]); + } +} From e64a2f3e25e5a772e365949d204709a7e3af69a4 Mon Sep 17 00:00:00 2001 From: sneed <56992013+Sneed69@users.noreply.github.com> Date: Mon, 17 Jun 2024 12:44:47 +0300 Subject: [PATCH 12/14] Roamer's frostbite carries over between battles (#4822) --- include/global.h | 5 +++-- src/roamer.c | 17 +++++++---------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/include/global.h b/include/global.h index 7db040697d..027edc746b 100644 --- a/include/global.h +++ b/include/global.h @@ -597,14 +597,15 @@ struct Roamer /*0x08*/ u16 species; /*0x0A*/ u16 hp; /*0x0C*/ u8 level; - /*0x0D*/ u8 status; + /*0x0D*/ u8 statusA; /*0x0E*/ u8 cool; /*0x0F*/ u8 beauty; /*0x10*/ u8 cute; /*0x11*/ u8 smart; /*0x12*/ u8 tough; /*0x13*/ bool8 active; - /*0x14*/ u8 filler[0x8]; + /*0x14*/ u8 statusB; // Stores frostbite + /*0x14*/ u8 filler[0x7]; }; struct RamScriptData diff --git a/src/roamer.c b/src/roamer.c index e9dc72a993..daa70dcb94 100644 --- a/src/roamer.c +++ b/src/roamer.c @@ -90,7 +90,8 @@ static void CreateInitialRoamerMon(bool16 createLatios) CreateMon(&gEnemyParty[0], ROAMER->species, 40, USE_RANDOM_IVS, FALSE, 0, OT_ID_PLAYER_ID, 0); ROAMER->level = 40; - ROAMER->status = 0; + ROAMER->statusA = 0; + ROAMER->statusB = 0; ROAMER->active = TRUE; ROAMER->ivs = GetMonData(&gEnemyParty[0], MON_DATA_IVS); ROAMER->personality = GetMonData(&gEnemyParty[0], MON_DATA_PERSONALITY); @@ -193,18 +194,11 @@ bool8 IsRoamerAt(u8 mapGroup, u8 mapNum) void CreateRoamerMonInstance(void) { - u32 status; + u32 status = ROAMER->statusA + (ROAMER->statusB << 8); struct Pokemon *mon = &gEnemyParty[0]; ZeroEnemyPartyMons(); CreateMonWithIVsPersonality(mon, ROAMER->species, ROAMER->level, ROAMER->ivs, ROAMER->personality); -// The roamer's status field is u8, but SetMonData expects status to be u32, so will set the roamer's status -// using the status field and the following 3 bytes (cool, beauty, and cute). -#ifdef BUGFIX - status = ROAMER->status; SetMonData(mon, MON_DATA_STATUS, &status); -#else - SetMonData(mon, MON_DATA_STATUS, &ROAMER->status); -#endif SetMonData(mon, MON_DATA_HP, &ROAMER->hp); SetMonData(mon, MON_DATA_COOL, &ROAMER->cool); SetMonData(mon, MON_DATA_BEAUTY, &ROAMER->beauty); @@ -228,8 +222,11 @@ bool8 TryStartRoamerEncounter(void) void UpdateRoamerHPStatus(struct Pokemon *mon) { + u32 status = GetMonData(mon, MON_DATA_STATUS); + ROAMER->hp = GetMonData(mon, MON_DATA_HP); - ROAMER->status = GetMonData(mon, MON_DATA_STATUS); + ROAMER->statusA = status; + ROAMER->statusB = status >> 8; RoamerMoveToOtherLocationSet(); } From 203318b9a22d13bc1faf228a6727624226f68329 Mon Sep 17 00:00:00 2001 From: DizzyEggg Date: Mon, 17 Jun 2024 13:51:04 +0200 Subject: [PATCH 13/14] Get rid of hardcoded stat raise anim values (#4825) --- include/battle_anim.h | 8 ++++---- src/battle_util.c | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/include/battle_anim.h b/include/battle_anim.h index fafde10c5b..8e21f2f84d 100644 --- a/include/battle_anim.h +++ b/include/battle_anim.h @@ -203,10 +203,10 @@ u8 GetBattlerSpriteDefault_Y(u8 battlerId); u8 GetSubstituteSpriteDefault_Y(u8 battlerId); // battle_anim_status_effects.c -#define STAT_ANIM_PLUS1 MOVE_EFFECT_ATK_PLUS_1 - 1 -#define STAT_ANIM_PLUS2 MOVE_EFFECT_ATK_PLUS_2 - 1 -#define STAT_ANIM_MINUS1 MOVE_EFFECT_ATK_MINUS_1 - 1 -#define STAT_ANIM_MINUS2 MOVE_EFFECT_ATK_MINUS_2 - 1 +#define STAT_ANIM_PLUS1 (MOVE_EFFECT_ATK_PLUS_1 - 1) +#define STAT_ANIM_PLUS2 (MOVE_EFFECT_ATK_PLUS_2 - 1) +#define STAT_ANIM_MINUS1 (MOVE_EFFECT_ATK_MINUS_1 - 1) +#define STAT_ANIM_MINUS2 (MOVE_EFFECT_ATK_MINUS_2 - 1) #define STAT_ANIM_MULTIPLE_PLUS1 55 #define STAT_ANIM_MULTIPLE_PLUS2 56 #define STAT_ANIM_MULTIPLE_MINUS1 57 diff --git a/src/battle_util.c b/src/battle_util.c index ad1648dbd8..394e34d42e 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -6499,7 +6499,7 @@ static u8 StatRaiseBerry(u32 battler, u32 itemId, u32 statId, bool32 end2) else SET_STATCHANGER(statId, 1, FALSE); - gBattleScripting.animArg1 = 14 + statId; + gBattleScripting.animArg1 = STAT_ANIM_PLUS1 + statId; gBattleScripting.animArg2 = 0; if (end2) @@ -6550,7 +6550,7 @@ static u8 RandomStatRaiseBerry(u32 battler, u32 itemId, bool32 end2) else SET_STATCHANGER(i + 1, 2, FALSE); - gBattleScripting.animArg1 = 0x21 + i + 6; + gBattleScripting.animArg1 = STAT_ANIM_PLUS2 + i + 1; gBattleScripting.animArg2 = 0; if (end2) { @@ -6627,7 +6627,7 @@ static u8 DamagedStatBoostBerryEffect(u32 battler, u8 statId, u8 category) SET_STATCHANGER(statId, 1, FALSE); gBattleScripting.battler = battler; - gBattleScripting.animArg1 = 14 + statId; + gBattleScripting.animArg1 = STAT_ANIM_PLUS1 + statId; gBattleScripting.animArg2 = 0; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_BerryStatRaiseRet; @@ -6644,7 +6644,7 @@ u8 TryHandleSeed(u32 battler, u32 terrainFlag, u8 statId, u16 itemId, bool32 exe gLastUsedItem = itemId; // For surge abilities gEffectBattler = gBattleScripting.battler = battler; SET_STATCHANGER(statId, 1, FALSE); - gBattleScripting.animArg1 = 14 + statId; + gBattleScripting.animArg1 = STAT_ANIM_PLUS1 + statId; gBattleScripting.animArg2 = 0; if (execute) { @@ -7052,7 +7052,7 @@ static u8 ItemEffectMoveEnd(u32 battler, u16 holdEffect) } SET_STATCHANGER(STAT_ATK, 2, FALSE); - gBattleScripting.animArg1 = 14 + STAT_ATK; + gBattleScripting.animArg1 = STAT_ANIM_PLUS1 + STAT_ATK; gBattleScripting.animArg2 = 0; BattleScriptPushCursorAndCallback(BattleScript_BerserkGeneRet); @@ -7334,7 +7334,7 @@ u8 ItemBattleEffects(u8 caseID, u32 battler, bool32 moveTurn) } SET_STATCHANGER(STAT_ATK, 2, FALSE); - gBattleScripting.animArg1 = 14 + STAT_ATK; + gBattleScripting.animArg1 = STAT_ANIM_PLUS1 + STAT_ATK; gBattleScripting.animArg2 = 0; BattleScriptPushCursorAndCallback(BattleScript_BerserkGeneRet); @@ -7586,7 +7586,7 @@ u8 ItemBattleEffects(u8 caseID, u32 battler, bool32 moveTurn) } SET_STATCHANGER(STAT_ATK, 2, FALSE); - gBattleScripting.animArg1 = 14 + STAT_ATK; + gBattleScripting.animArg1 = STAT_ANIM_PLUS1 + STAT_ATK; gBattleScripting.animArg2 = 0; BattleScriptPushCursorAndCallback(BattleScript_BerserkGeneRet); @@ -11057,7 +11057,7 @@ bool32 TryRoomService(u32 battler) BufferStatChange(battler, STAT_SPEED, STRINGID_STATFELL); gEffectBattler = gBattleScripting.battler = battler; SET_STATCHANGER(STAT_SPEED, 1, TRUE); - gBattleScripting.animArg1 = 14 + STAT_SPEED; + gBattleScripting.animArg1 = STAT_ANIM_PLUS1 + STAT_SPEED; gBattleScripting.animArg2 = 0; gLastUsedItem = gBattleMons[battler].item; return TRUE; From 189d542520e57b9a09a223ba3ee99115f6726af2 Mon Sep 17 00:00:00 2001 From: Jaizu Date: Mon, 17 Jun 2024 21:44:52 +0200 Subject: [PATCH 14/14] Fix Steven partner battle post-battle script (#2001) --- data/maps/MossdeepCity_SpaceCenter_2F/scripts.inc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/data/maps/MossdeepCity_SpaceCenter_2F/scripts.inc b/data/maps/MossdeepCity_SpaceCenter_2F/scripts.inc index 4a22bf8eea..c82a33e78d 100644 --- a/data/maps/MossdeepCity_SpaceCenter_2F/scripts.inc +++ b/data/maps/MossdeepCity_SpaceCenter_2F/scripts.inc @@ -320,6 +320,9 @@ MossdeepCity_SpaceCenter_2F_EventScript_DefeatedMaxieTabitha:: setobjectmovementtype LOCALID_SCIENTIST, MOVEMENT_TYPE_WANDER_AROUND addobject LOCALID_SCIENTIST fadescreen FADE_FROM_BLACK +#ifdef BUGFIX + releaseall +#endif end MossdeepCity_SpaceCenter_2F_EventScript_StevenFacePlayer::