From c650199909e2cd7bd47654cdd4f16c453ac9dca6 Mon Sep 17 00:00:00 2001 From: Eduardo Quezada Date: Sun, 20 Apr 2025 16:36:27 -0400 Subject: [PATCH 01/10] Added missing `gMovesInfo` encapsulation (#6656) --- include/move.h | 27 ++++++++++++++---------- src/apprentice.c | 2 +- src/battle_ai_util.c | 4 ++-- src/battle_script_commands.c | 6 +++--- test/battle/ability/color_change.c | 4 ++-- test/battle/ability/intimidate.c | 2 +- test/battle/ability/neutralizing_gas.c | 10 ++++----- test/battle/ability/thermal_exchange.c | 10 ++++----- test/battle/ai/ai.c | 4 ++-- test/battle/hold_effect/shell_bell.c | 2 +- test/battle/move_effect/dream_eater.c | 10 ++++----- test/battle/move_effect/follow_me.c | 4 ++-- test/battle/move_effect/instruct.c | 10 ++++----- test/battle/move_effect/laser_focus.c | 2 +- test/battle/move_effect/recoil_if_miss.c | 2 +- test/battle/move_effect/rototiller.c | 6 +++--- test/battle/move_effect/sleep_talk.c | 8 +++---- test/battle/move_effect/snore.c | 4 ++-- test/battle/move_effect/spectral_thief.c | 8 +++---- test/battle/move_flags/recoil.c | 2 +- test/test_runner_battle.c | 18 ++++++++-------- 21 files changed, 75 insertions(+), 70 deletions(-) diff --git a/include/move.h b/include/move.h index 5537840703..743889850e 100644 --- a/include/move.h +++ b/include/move.h @@ -137,7 +137,7 @@ struct MoveInfo const u8 *battleAnimScript; }; -extern const struct MoveInfo gMovesInfo[]; +extern const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL]; extern const u8 gNotDoneYetDescription[]; extern const struct BattleMoveEffect gBattleMoveEffects[]; @@ -154,19 +154,19 @@ static inline const u8 *GetMoveName(u32 moveId) return gMovesInfo[SanitizeMoveId(moveId)].name; } -static inline const u8 *GetMoveDescription(u32 moveId) -{ - moveId = SanitizeMoveId(moveId); - if (gMovesInfo[moveId].effect == EFFECT_PLACEHOLDER) - return gNotDoneYetDescription; - return gMovesInfo[moveId].description; -} - static inline u32 GetMoveEffect(u32 moveId) { return gMovesInfo[SanitizeMoveId(moveId)].effect; } +static inline const u8 *GetMoveDescription(u32 moveId) +{ + moveId = SanitizeMoveId(moveId); + if (GetMoveEffect(moveId) == EFFECT_PLACEHOLDER) + return gNotDoneYetDescription; + return gMovesInfo[moveId].description; +} + static inline u32 GetMoveType(u32 moveId) { return gMovesInfo[SanitizeMoveId(moveId)].type; @@ -437,6 +437,11 @@ static inline bool32 IsMoveSketchBanned(u32 moveId) return gMovesInfo[SanitizeMoveId(moveId)].sketchBanned; } +static inline bool32 IsValidApprenticeMove(u32 moveId) +{ + return gMovesInfo[SanitizeMoveId(moveId)].validApprenticeMove; +} + static inline u32 GetMoveTwoTurnAttackStringId(u32 moveId) { return gMovesInfo[SanitizeMoveId(moveId)].argument.twoTurnAttack.stringId; @@ -529,12 +534,12 @@ static inline const u8 *GetMoveAnimationScript(u32 moveId) static inline const u8 *GetMoveBattleScript(u32 moveId) { moveId = SanitizeMoveId(moveId); - if (gBattleMoveEffects[gMovesInfo[moveId].effect].battleScript == NULL) + if (gBattleMoveEffects[GetMoveEffect(moveId)].battleScript == NULL) { DebugPrintfLevel(MGBA_LOG_WARN, "No effect for moveId=%u", moveId); return gBattleMoveEffects[EFFECT_PLACEHOLDER].battleScript; } - return gBattleMoveEffects[gMovesInfo[moveId].effect].battleScript; + return gBattleMoveEffects[GetMoveEffect(moveId)].battleScript; } #endif // GUARD_MOVES_H diff --git a/src/apprentice.c b/src/apprentice.c index 7769dde6e3..7dcca7b28b 100644 --- a/src/apprentice.c +++ b/src/apprentice.c @@ -411,7 +411,7 @@ static u16 GetRandomAlternateMove(u8 monId) if (TrySetMove(monId, moveId)) { - if (gMovesInfo[moveId].validApprenticeMove) + if (IsValidApprenticeMove(moveId)) break; i++; } diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index ca94655476..6478950a85 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -1465,7 +1465,7 @@ bool32 IsConfusionMoveEffect(u32 moveEffect) bool32 IsHazardMove(u32 move) { // Hazard setting moves like Stealth Rock, Spikes, etc. - u32 i, moveEffect = gMovesInfo[move].effect; + u32 i, moveEffect = GetMoveEffect(move); switch (moveEffect) { case EFFECT_SPIKES: @@ -1491,7 +1491,7 @@ bool32 IsHazardMove(u32 move) bool32 IsHazardClearingMove(u32 move) { // Hazard clearing effects like Rapid Spin, Tidy Up, etc. - u32 i, moveEffect = gMovesInfo[move].effect; + u32 i, moveEffect = GetMoveEffect(move); switch (moveEffect) { case EFFECT_RAPID_SPIN: diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 4e090e3257..8b7e2702fd 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -1198,7 +1198,7 @@ static void Cmd_attackcanceler(void) if (AbilityBattleEffects(ABILITYEFFECT_MOVES_BLOCK, gBattlerTarget, 0, 0, 0)) return; - if (gMovesInfo[gCurrentMove].effect == EFFECT_PARALYZE && AbilityBattleEffects(ABILITYEFFECT_ABSORBING, gBattlerTarget, 0, 0, gCurrentMove)) + if (GetMoveEffect(gCurrentMove) == EFFECT_PARALYZE && AbilityBattleEffects(ABILITYEFFECT_ABSORBING, gBattlerTarget, 0, 0, gCurrentMove)) return; if (IsMovePowderBlocked(gBattlerAttacker, gBattlerTarget, gCurrentMove)) return; @@ -2423,7 +2423,7 @@ static void Cmd_attackanimation(void) } // handle special move animations - if (gMovesInfo[gCurrentMove].effect == EFFECT_EXPANDING_FORCE && moveTarget & MOVE_TARGET_BOTH && CountAliveMonsInBattle(BATTLE_ALIVE_SIDE, BATTLE_OPPOSITE(gBattlerAttacker) > 1)) + if (GetMoveEffect(gCurrentMove) == EFFECT_EXPANDING_FORCE && moveTarget & MOVE_TARGET_BOTH && CountAliveMonsInBattle(BATTLE_ALIVE_SIDE, BATTLE_OPPOSITE(gBattlerAttacker) > 1)) gBattleScripting.animTurn = 1; if (!(moveResultFlags & MOVE_RESULT_NO_EFFECT)) @@ -6475,7 +6475,7 @@ static void Cmd_moveend(void) gBattleScripting.moveendState++; break; } - else if (gMovesInfo[gCurrentMove].effect == EFFECT_RECOIL_IF_MISS + else if (GetMoveEffect(gCurrentMove) == EFFECT_RECOIL_IF_MISS && (!IsBattlerTurnDamaged(gBattlerTarget) || gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT) && !gBattleStruct->noTargetPresent && IsBattlerAlive(gBattlerAttacker)) diff --git a/test/battle/ability/color_change.c b/test/battle/ability/color_change.c index cc0a3e2472..42932a7d58 100644 --- a/test/battle/ability/color_change.c +++ b/test/battle/ability/color_change.c @@ -5,7 +5,7 @@ SINGLE_BATTLE_TEST("Color Change changes the type of a Pokemon being hit by a mo { GIVEN { ASSUME(gSpeciesInfo[SPECIES_KECLEON].types[0] != TYPE_PSYCHIC && gSpeciesInfo[SPECIES_KECLEON].types[1] != TYPE_PSYCHIC); - ASSUME(gMovesInfo[MOVE_PSYWAVE].type == TYPE_PSYCHIC); + ASSUME(GetMoveType(MOVE_PSYWAVE) == TYPE_PSYCHIC); PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_KECLEON) { Ability(ABILITY_COLOR_CHANGE); } } WHEN { @@ -21,7 +21,7 @@ SINGLE_BATTLE_TEST("Color Change does not change the type when hit by a move tha { GIVEN { ASSUME(gSpeciesInfo[SPECIES_KECLEON].types[0] == TYPE_NORMAL || gSpeciesInfo[SPECIES_KECLEON].types[1] == TYPE_NORMAL); - ASSUME(gMovesInfo[MOVE_TACKLE].type == TYPE_NORMAL); + ASSUME(GetMoveType(MOVE_TACKLE) == TYPE_NORMAL); PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_KECLEON) { Ability(ABILITY_COLOR_CHANGE); } } WHEN { diff --git a/test/battle/ability/intimidate.c b/test/battle/ability/intimidate.c index 61b53f42e1..dd1f761dee 100644 --- a/test/battle/ability/intimidate.c +++ b/test/battle/ability/intimidate.c @@ -379,7 +379,7 @@ DOUBLE_BATTLE_TEST("Intimidate will correctly decrease the attack of the second SINGLE_BATTLE_TEST("Intimdate does not lose timing after mega evolution and switch out by a hit escape move") { GIVEN { - ASSUME(gMovesInfo[MOVE_U_TURN].effect == EFFECT_HIT_ESCAPE); + ASSUME(GetMoveEffect(MOVE_U_TURN) == EFFECT_HIT_ESCAPE); PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_MANECTRIC) { Item(ITEM_MANECTITE); } OPPONENT(SPECIES_ARBOK) { Ability(ABILITY_INTIMIDATE); } diff --git a/test/battle/ability/neutralizing_gas.c b/test/battle/ability/neutralizing_gas.c index ef83483d32..ae11c458f7 100644 --- a/test/battle/ability/neutralizing_gas.c +++ b/test/battle/ability/neutralizing_gas.c @@ -51,7 +51,7 @@ DOUBLE_BATTLE_TEST("Neutralizing Gas prevents ally's switch-in ability from acti DOUBLE_BATTLE_TEST("Neutralizing Gas ignores all battlers' ability effects") { GIVEN { - ASSUME(gMovesInfo[MOVE_SURF].target == MOVE_TARGET_FOES_AND_ALLY); + ASSUME(GetMoveTarget(MOVE_SURF) == MOVE_TARGET_FOES_AND_ALLY); PLAYER(SPECIES_WEEZING) { Ability(ABILITY_NEUTRALIZING_GAS); } PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_TELEPATHY); } OPPONENT(SPECIES_LANTURN) { Ability(ABILITY_WATER_ABSORB); } @@ -87,7 +87,7 @@ SINGLE_BATTLE_TEST("Neutralizing Gas ignores multipliers from attacker's ability PARAMETRIZE { ability = ABILITY_NEUTRALIZING_GAS; } PARAMETRIZE { ability = ABILITY_LEVITATE; } GIVEN { - ASSUME(gMovesInfo[MOVE_TACKLE].category == DAMAGE_CATEGORY_PHYSICAL); + ASSUME(GetMoveCategory(MOVE_TACKLE) == DAMAGE_CATEGORY_PHYSICAL); PLAYER(SPECIES_WEEZING) { Ability(ability); } OPPONENT(SPECIES_AZUMARILL) { Ability(ABILITY_HUGE_POWER); } } WHEN { @@ -106,8 +106,8 @@ SINGLE_BATTLE_TEST("Neutralizing Gas ignores multipliers from target's ability", PARAMETRIZE { ability = ABILITY_NEUTRALIZING_GAS; } PARAMETRIZE { ability = ABILITY_LEVITATE; } GIVEN { - ASSUME(gMovesInfo[MOVE_TACKLE].makesContact == TRUE); - ASSUME(gMovesInfo[MOVE_TACKLE].type == TYPE_NORMAL); + ASSUME(MoveMakesContact(MOVE_TACKLE) == TRUE); + ASSUME(GetMoveType(MOVE_TACKLE) == TYPE_NORMAL); PLAYER(SPECIES_WEEZING) { Ability(ability); } OPPONENT(SPECIES_BEWEAR) { Ability(ABILITY_FLUFFY); } } WHEN { @@ -146,7 +146,7 @@ DOUBLE_BATTLE_TEST("Neutralizing Gas ignores multipliers from ally's ability", s PARAMETRIZE { ability = ABILITY_NEUTRALIZING_GAS; } PARAMETRIZE { ability = ABILITY_LEVITATE; } GIVEN { - ASSUME(gMovesInfo[MOVE_TACKLE].category == DAMAGE_CATEGORY_PHYSICAL); + ASSUME(GetMoveCategory(MOVE_TACKLE) == DAMAGE_CATEGORY_PHYSICAL); PLAYER(SPECIES_WEEZING) { Ability(ability); } PLAYER(SPECIES_WO_CHIEN) { Ability(ABILITY_TABLETS_OF_RUIN); } OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_TELEPATHY); } diff --git a/test/battle/ability/thermal_exchange.c b/test/battle/ability/thermal_exchange.c index 57f4256d7a..7484fa207c 100644 --- a/test/battle/ability/thermal_exchange.c +++ b/test/battle/ability/thermal_exchange.c @@ -4,7 +4,7 @@ SINGLE_BATTLE_TEST("Thermal Exchange makes Will-O-Wisp fail") { GIVEN { - ASSUME(gMovesInfo[MOVE_WILL_O_WISP].effect == EFFECT_WILL_O_WISP); + ASSUME(GetMoveEffect(MOVE_WILL_O_WISP) == EFFECT_WILL_O_WISP); PLAYER(SPECIES_BAXCALIBUR) { Ability(ABILITY_THERMAL_EXCHANGE); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { @@ -37,8 +37,8 @@ SINGLE_BATTLE_TEST("Thermal Exchange prevents the user from getting burned when SINGLE_BATTLE_TEST("Thermal Exchange cures burns when acquired") { GIVEN { - ASSUME(gMovesInfo[MOVE_WILL_O_WISP].effect == EFFECT_WILL_O_WISP); - ASSUME(gMovesInfo[MOVE_SKILL_SWAP].effect == EFFECT_SKILL_SWAP); + ASSUME(GetMoveEffect(MOVE_WILL_O_WISP) == EFFECT_WILL_O_WISP); + ASSUME(GetMoveEffect(MOVE_SKILL_SWAP) == EFFECT_SKILL_SWAP); PLAYER(SPECIES_BAXCALIBUR) { Ability(ABILITY_THERMAL_EXCHANGE); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { @@ -56,7 +56,7 @@ SINGLE_BATTLE_TEST("Thermal Exchange cures burns when acquired") SINGLE_BATTLE_TEST("Thermal Exchange burn prevention can be bypassed with Mold Breaker but is cured after") { GIVEN { - ASSUME(gMovesInfo[MOVE_WILL_O_WISP].effect == EFFECT_WILL_O_WISP); + ASSUME(GetMoveEffect(MOVE_WILL_O_WISP) == EFFECT_WILL_O_WISP); PLAYER(SPECIES_BAXCALIBUR) { Ability(ABILITY_THERMAL_EXCHANGE); } OPPONENT(SPECIES_RAMPARDOS) { Ability(ABILITY_MOLD_BREAKER); } } WHEN { @@ -73,7 +73,7 @@ SINGLE_BATTLE_TEST("Thermal Exchange burn prevention can be bypassed with Mold B SINGLE_BATTLE_TEST("Thermal Exchange boosts attack if hit by a damaging fire type move") { GIVEN { - ASSUME(gMovesInfo[MOVE_EMBER].type == TYPE_FIRE); + ASSUME(GetMoveType(MOVE_EMBER) == TYPE_FIRE); PLAYER(SPECIES_BAXCALIBUR) { Ability(ABILITY_THERMAL_EXCHANGE); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { diff --git a/test/battle/ai/ai.c b/test/battle/ai/ai.c index d11f146fc1..6f56e608b3 100644 --- a/test/battle/ai/ai.c +++ b/test/battle/ai/ai.c @@ -835,8 +835,8 @@ AI_SINGLE_BATTLE_TEST("AI stays choice locked into moves in spite of the player' AI_SINGLE_BATTLE_TEST("AI won't use Sucker Punch if it expects a move of the same priority bracket and the opponent is faster") { GIVEN { - ASSUME(gMovesInfo[MOVE_QUICK_ATTACK].priority == 1); - ASSUME(gMovesInfo[MOVE_SUCKER_PUNCH].priority == 1); + ASSUME(GetMovePriority(MOVE_QUICK_ATTACK) == 1); + ASSUME(GetMovePriority(MOVE_SUCKER_PUNCH) == 1); AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); PLAYER(SPECIES_WOBBUFFET) { Speed(300); Moves(MOVE_QUICK_ATTACK); } OPPONENT(SPECIES_WOBBUFFET) { Speed(100); Moves(MOVE_SUCKER_PUNCH, MOVE_TACKLE); } diff --git a/test/battle/hold_effect/shell_bell.c b/test/battle/hold_effect/shell_bell.c index f669c55081..8e91011865 100644 --- a/test/battle/hold_effect/shell_bell.c +++ b/test/battle/hold_effect/shell_bell.c @@ -208,7 +208,7 @@ DOUBLE_BATTLE_TEST("Shell Bell heals accumulated damage for spread moves") const u16 maxHp = 200; const u16 initHp = 1; GIVEN { - ASSUME(gMovesInfo[MOVE_DISCHARGE].target == MOVE_TARGET_FOES_AND_ALLY); + ASSUME(GetMoveTarget(MOVE_DISCHARGE) == MOVE_TARGET_FOES_AND_ALLY); PLAYER(SPECIES_ARIADOS) { MaxHP(maxHp); HP(initHp); Item(ITEM_SHELL_BELL); } PLAYER(SPECIES_WOBBUFFET) {} OPPONENT(SPECIES_GYARADOS) {} diff --git a/test/battle/move_effect/dream_eater.c b/test/battle/move_effect/dream_eater.c index 67b2ed5af5..caa365a17a 100644 --- a/test/battle/move_effect/dream_eater.c +++ b/test/battle/move_effect/dream_eater.c @@ -77,9 +77,9 @@ SINGLE_BATTLE_TEST("Dream Eater works on targets with Comatose") SINGLE_BATTLE_TEST("Dream Eater fails if the target is behind a Substitute (Gen 1-4)") { GIVEN { - ASSUME(gMovesInfo[MOVE_YAWN].effect == EFFECT_YAWN); - ASSUME(gMovesInfo[MOVE_SUBSTITUTE].effect == EFFECT_SUBSTITUTE); - ASSUME(!gMovesInfo[MOVE_DREAM_EATER].ignoresSubstitute); + ASSUME(GetMoveEffect(MOVE_YAWN) == EFFECT_YAWN); + ASSUME(GetMoveEffect(MOVE_SUBSTITUTE) == EFFECT_SUBSTITUTE); + ASSUME(!MoveIgnoresSubstitute(MOVE_DREAM_EATER)); PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); } WHEN { @@ -97,8 +97,8 @@ SINGLE_BATTLE_TEST("Dream Eater works if the target is behind a Substitute (Gen s16 damage; s16 healed; GIVEN { - ASSUME(gMovesInfo[MOVE_YAWN].effect == EFFECT_YAWN); - ASSUME(gMovesInfo[MOVE_SUBSTITUTE].effect == EFFECT_SUBSTITUTE); + ASSUME(GetMoveEffect(MOVE_YAWN) == EFFECT_YAWN); + ASSUME(GetMoveEffect(MOVE_SUBSTITUTE) == EFFECT_SUBSTITUTE); PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET) { HP(1); } } WHEN { diff --git a/test/battle/move_effect/follow_me.c b/test/battle/move_effect/follow_me.c index fe7b96207d..f24629f141 100644 --- a/test/battle/move_effect/follow_me.c +++ b/test/battle/move_effect/follow_me.c @@ -3,8 +3,8 @@ ASSUMPTIONS { - ASSUME(gMovesInfo[MOVE_FOLLOW_ME].effect == EFFECT_FOLLOW_ME); - ASSUME(gMovesInfo[MOVE_SPOTLIGHT].effect == EFFECT_FOLLOW_ME); + ASSUME(GetMoveEffect(MOVE_FOLLOW_ME) == EFFECT_FOLLOW_ME); + ASSUME(GetMoveEffect(MOVE_SPOTLIGHT) == EFFECT_FOLLOW_ME); } DOUBLE_BATTLE_TEST("Follow Me redirects single target moves used by opponents to user") diff --git a/test/battle/move_effect/instruct.c b/test/battle/move_effect/instruct.c index 85f56d4c4b..e61540b9c5 100644 --- a/test/battle/move_effect/instruct.c +++ b/test/battle/move_effect/instruct.c @@ -249,8 +249,8 @@ DOUBLE_BATTLE_TEST("Instructed move will be redirected by Follow Me after instru PARAMETRIZE { moveTarget = opponentLeft; } PARAMETRIZE { moveTarget = opponentRight; } GIVEN { - ASSUME(gMovesInfo[MOVE_FOLLOW_ME].effect == EFFECT_FOLLOW_ME); - ASSUME(gMovesInfo[MOVE_SKILL_SWAP].effect == EFFECT_SKILL_SWAP); + ASSUME(GetMoveEffect(MOVE_FOLLOW_ME) == EFFECT_FOLLOW_ME); + ASSUME(GetMoveEffect(MOVE_SKILL_SWAP) == EFFECT_SKILL_SWAP); PLAYER(SPECIES_DURALUDON) { Ability(ABILITY_STALWART); } PLAYER(SPECIES_DURALUDON) { Ability(ABILITY_STALWART); } OPPONENT(SPECIES_WOBBUFFET); @@ -279,9 +279,9 @@ DOUBLE_BATTLE_TEST("Instructed move will be redirected by Rage Powder after inst PARAMETRIZE { moveTarget = opponentLeft; } PARAMETRIZE { moveTarget = opponentRight; } GIVEN { - ASSUME(gMovesInfo[MOVE_RAGE_POWDER].effect == EFFECT_FOLLOW_ME); - ASSUME(gMovesInfo[MOVE_RAGE_POWDER].powderMove == TRUE); - ASSUME(gMovesInfo[MOVE_SOAK].effect == EFFECT_SOAK); + ASSUME(GetMoveEffect(MOVE_RAGE_POWDER) == EFFECT_FOLLOW_ME); + ASSUME(IsPowderMove(MOVE_RAGE_POWDER) == TRUE); + ASSUME(GetMoveEffect(MOVE_SOAK) == EFFECT_SOAK); PLAYER(SPECIES_TREECKO); PLAYER(SPECIES_SCEPTILE); OPPONENT(SPECIES_WOBBUFFET); diff --git a/test/battle/move_effect/laser_focus.c b/test/battle/move_effect/laser_focus.c index c486a3cbee..17922d3231 100644 --- a/test/battle/move_effect/laser_focus.c +++ b/test/battle/move_effect/laser_focus.c @@ -3,7 +3,7 @@ ASSUMPTIONS { - ASSUME(gMovesInfo[MOVE_LASER_FOCUS].effect == EFFECT_LASER_FOCUS); + ASSUME(GetMoveEffect(MOVE_LASER_FOCUS) == EFFECT_LASER_FOCUS); } SINGLE_BATTLE_TEST("Laser Focus causes the user's move used on the next turn to result in a Critical Hit") diff --git a/test/battle/move_effect/recoil_if_miss.c b/test/battle/move_effect/recoil_if_miss.c index 0b65eb8541..a5dbfef178 100644 --- a/test/battle/move_effect/recoil_if_miss.c +++ b/test/battle/move_effect/recoil_if_miss.c @@ -102,7 +102,7 @@ SINGLE_BATTLE_TEST("Recoil if miss: Jump Kick's recoil happens after Spiky Shiel SINGLE_BATTLE_TEST("Recoil if miss: Jump Kick recoil happens after Spiky Shield damage") { GIVEN { - ASSUME(!gMovesInfo[MOVE_JUMP_KICK].ignoresProtect); + ASSUME(!MoveIgnoresProtect(MOVE_JUMP_KICK)); PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); } WHEN { diff --git a/test/battle/move_effect/rototiller.c b/test/battle/move_effect/rototiller.c index 32ae7da75f..34b62a2666 100644 --- a/test/battle/move_effect/rototiller.c +++ b/test/battle/move_effect/rototiller.c @@ -3,7 +3,7 @@ ASSUMPTIONS { - ASSUME(gMovesInfo[MOVE_ROTOTILLER].effect == EFFECT_ROTOTILLER); + ASSUME(GetMoveEffect(MOVE_ROTOTILLER) == EFFECT_ROTOTILLER); } DOUBLE_BATTLE_TEST("Rototiller boosts Attack and Special Attack of all Grass types on the field") @@ -55,7 +55,7 @@ SINGLE_BATTLE_TEST("Rototiller doesn't affect pokemon that are semi-invulnerable { GIVEN { ASSUME(gSpeciesInfo[SPECIES_TANGELA].types[0] == TYPE_GRASS); - ASSUME(gMovesInfo[MOVE_DIG].effect == EFFECT_SEMI_INVULNERABLE); + ASSUME(GetMoveEffect(MOVE_DIG) == EFFECT_SEMI_INVULNERABLE); PLAYER(SPECIES_TANGELA); OPPONENT(SPECIES_TANGELA); } WHEN { @@ -79,7 +79,7 @@ SINGLE_BATTLE_TEST("Rototiller fails if the only valid target is semi-invulnerab ASSUME(gSpeciesInfo[SPECIES_TANGELA].types[0] == TYPE_GRASS); ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].types[0] != TYPE_GRASS); ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].types[1] != TYPE_GRASS); - ASSUME(gMovesInfo[MOVE_DIG].effect == EFFECT_SEMI_INVULNERABLE); + ASSUME(GetMoveEffect(MOVE_DIG) == EFFECT_SEMI_INVULNERABLE); PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_TANGELA); } WHEN { diff --git a/test/battle/move_effect/sleep_talk.c b/test/battle/move_effect/sleep_talk.c index b00f89e582..fd246f47aa 100644 --- a/test/battle/move_effect/sleep_talk.c +++ b/test/battle/move_effect/sleep_talk.c @@ -94,8 +94,8 @@ SINGLE_BATTLE_TEST("Sleep Talk can use moves while choiced into Sleep Talk") SINGLE_BATTLE_TEST("Sleep Talk fails if user is taunted") { GIVEN { - ASSUME(gMovesInfo[MOVE_TAUNT].effect == EFFECT_TAUNT); - ASSUME(gMovesInfo[MOVE_SLEEP_TALK].category == DAMAGE_CATEGORY_STATUS); + ASSUME(GetMoveEffect(MOVE_TAUNT) == EFFECT_TAUNT); + ASSUME(GetMoveCategory(MOVE_SLEEP_TALK) == DAMAGE_CATEGORY_STATUS); PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_SLEEP); Moves(MOVE_SLEEP_TALK, MOVE_TACKLE, MOVE_FLY, MOVE_DIG); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { @@ -113,7 +113,7 @@ DOUBLE_BATTLE_TEST("Sleep Talk calls move and that move may be redirected by Lig { PASSES_RANDOMLY(1, 2, RNG_RANDOM_TARGET); GIVEN { - ASSUME(gMovesInfo[MOVE_SPARK].type == TYPE_ELECTRIC); + ASSUME(GetMoveType(MOVE_SPARK) == TYPE_ELECTRIC); PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_SLEEP); Moves(MOVE_SLEEP_TALK, MOVE_SPARK, MOVE_FLY, MOVE_DIG); } PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); @@ -132,7 +132,7 @@ DOUBLE_BATTLE_TEST("Sleep Talk calls move and that move may be redirected by Sto { PASSES_RANDOMLY(1, 2, RNG_RANDOM_TARGET); GIVEN { - ASSUME(gMovesInfo[MOVE_WATER_GUN].type == TYPE_WATER); + ASSUME(GetMoveType(MOVE_WATER_GUN) == TYPE_WATER); PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_SLEEP); Moves(MOVE_SLEEP_TALK, MOVE_WATER_GUN, MOVE_FLY, MOVE_DIG); } PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); diff --git a/test/battle/move_effect/snore.c b/test/battle/move_effect/snore.c index d83e35b16c..410406a65b 100644 --- a/test/battle/move_effect/snore.c +++ b/test/battle/move_effect/snore.c @@ -3,7 +3,7 @@ ASSUMPTIONS { - ASSUME(gMovesInfo[MOVE_SNORE].effect == EFFECT_SNORE); + ASSUME(GetMoveEffect(MOVE_SNORE) == EFFECT_SNORE); } SINGLE_BATTLE_TEST("Snore fails if not asleep") @@ -47,7 +47,7 @@ SINGLE_BATTLE_TEST("Snore fails if user is throat chopped") { GIVEN { ASSUME(MoveHasAdditionalEffect(MOVE_THROAT_CHOP, MOVE_EFFECT_THROAT_CHOP)); - ASSUME(gMovesInfo[MOVE_SNORE].soundMove == TRUE); + ASSUME(IsSoundMove(MOVE_SNORE) == TRUE); PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_SLEEP); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { diff --git a/test/battle/move_effect/spectral_thief.c b/test/battle/move_effect/spectral_thief.c index 7bb5d5a536..885df9f5e3 100644 --- a/test/battle/move_effect/spectral_thief.c +++ b/test/battle/move_effect/spectral_thief.c @@ -8,8 +8,8 @@ SINGLE_BATTLE_TEST("Spectral Thief steals opponents boost before attacking", s16 PARAMETRIZE { move = MOVE_SWORDS_DANCE; } GIVEN { - ASSUME(gMovesInfo[MOVE_SWORDS_DANCE].effect == EFFECT_ATTACK_UP_2); - ASSUME(gMovesInfo[MOVE_SPECTRAL_THIEF].effect == EFFECT_SPECTRAL_THIEF); + ASSUME(GetMoveEffect(MOVE_SWORDS_DANCE) == EFFECT_ATTACK_UP_2); + ASSUME(GetMoveEffect(MOVE_SPECTRAL_THIEF) == EFFECT_SPECTRAL_THIEF); PLAYER(SPECIES_REGIROCK); OPPONENT(SPECIES_WOBBUFFET); } WHEN { @@ -32,8 +32,8 @@ SINGLE_BATTLE_TEST("Spectral Thief steals opponents boost before attacking", s16 SINGLE_BATTLE_TEST("Spectral Thief can't steal opponent's boost if target is immune") { GIVEN { - ASSUME(gMovesInfo[MOVE_SWORDS_DANCE].effect == EFFECT_ATTACK_UP_2); - ASSUME(gMovesInfo[MOVE_SPECTRAL_THIEF].effect == EFFECT_SPECTRAL_THIEF); + ASSUME(GetMoveEffect(MOVE_SWORDS_DANCE) == EFFECT_ATTACK_UP_2); + ASSUME(GetMoveEffect(MOVE_SPECTRAL_THIEF) == EFFECT_SPECTRAL_THIEF); PLAYER(SPECIES_MEOWTH); OPPONENT(SPECIES_WOBBUFFET); } WHEN { diff --git a/test/battle/move_flags/recoil.c b/test/battle/move_flags/recoil.c index 5a8f3be576..0293a07a80 100644 --- a/test/battle/move_flags/recoil.c +++ b/test/battle/move_flags/recoil.c @@ -87,7 +87,7 @@ SINGLE_BATTLE_TEST("Flare Blitz deals 33% of recoil damage to the user and can b SINGLE_BATTLE_TEST("Flare Blitz is absorbed by Flash Fire and no recoil damage is dealt") { GIVEN { - ASSUME(gMovesInfo[MOVE_FLARE_BLITZ].recoil > 0); + ASSUME(GetMoveRecoil(MOVE_FLARE_BLITZ) > 0); PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_VULPIX) { Ability(ABILITY_FLASH_FIRE); }; } WHEN { diff --git a/test/test_runner_battle.c b/test/test_runner_battle.c index 399cb8a852..806dcf1956 100644 --- a/test/test_runner_battle.c +++ b/test/test_runner_battle.c @@ -2034,16 +2034,16 @@ s32 MoveGetTarget(s32 battlerId, u32 moveId, struct MoveContext *ctx, u32 source } else { - const struct MoveInfo *move = &gMovesInfo[moveId]; - if (move->target == MOVE_TARGET_RANDOM - || move->target == MOVE_TARGET_BOTH - || move->target == MOVE_TARGET_DEPENDS - || move->target == MOVE_TARGET_FOES_AND_ALLY - || move->target == MOVE_TARGET_OPPONENTS_FIELD) + u32 moveTarget = GetMoveTarget(moveId); + if (moveTarget == MOVE_TARGET_RANDOM + || moveTarget == MOVE_TARGET_BOTH + || moveTarget == MOVE_TARGET_DEPENDS + || moveTarget == MOVE_TARGET_FOES_AND_ALLY + || moveTarget == MOVE_TARGET_OPPONENTS_FIELD) { target = BATTLE_OPPOSITE(battlerId); } - else if (move->target == MOVE_TARGET_SELECTED || move->target == MOVE_TARGET_OPPONENT) + else if (moveTarget == MOVE_TARGET_SELECTED || moveTarget == MOVE_TARGET_OPPONENT) { // In AI Doubles not specified target allows any target for EXPECT_MOVE. if (GetBattleTest()->type != BATTLE_TEST_AI_DOUBLES) @@ -2053,11 +2053,11 @@ s32 MoveGetTarget(s32 battlerId, u32 moveId, struct MoveContext *ctx, u32 source target = BATTLE_OPPOSITE(battlerId); } - else if (move->target == MOVE_TARGET_USER || move->target == MOVE_TARGET_ALL_BATTLERS) + else if (moveTarget == MOVE_TARGET_USER || moveTarget == MOVE_TARGET_ALL_BATTLERS) { target = battlerId; } - else if (move->target == MOVE_TARGET_ALLY) + else if (moveTarget == MOVE_TARGET_ALLY) { target = BATTLE_PARTNER(battlerId); } From 5c02abb101324b734280d8b84e3939788a892c47 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Sun, 20 Apr 2025 22:36:50 +0200 Subject: [PATCH 02/10] Test for Freeze vs Scald thaw (#6658) Co-authored-by: Bassoonian --- test/battle/status1/freeze.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/battle/status1/freeze.c b/test/battle/status1/freeze.c index f363ab9e35..12104438ac 100644 --- a/test/battle/status1/freeze.c +++ b/test/battle/status1/freeze.c @@ -61,3 +61,20 @@ SINGLE_BATTLE_TEST("Freeze isn't thawed if opponent is asleep during thawing att } } } + +SINGLE_BATTLE_TEST("Freeze isn't thawed if opponent is asleep during thawing attack when using Scald") +{ + PASSES_RANDOMLY(80, 100, RNG_FROZEN); + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_FREEZE); } + OPPONENT(SPECIES_WOBBUFFET) { Status1(STATUS1_SLEEP); } + } WHEN { + TURN { MOVE(opponent, MOVE_SCALD); MOVE(player, MOVE_CELEBRATE); } + } SCENE { + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCALD, opponent); + MESSAGE("Wobbuffet thawed out!"); + STATUS_ICON(player, none: TRUE); + } + } +} From fedbce510b30239099b25cb966ebfcab3d5dd568 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Sun, 20 Apr 2025 22:49:18 +0200 Subject: [PATCH 03/10] Fixes commander activation on a fainted mon (#6632) Co-authored-by: Bassoonian --- src/battle_script_commands.c | 1 + src/battle_util.c | 2 ++ test/battle/ability/commander.c | 34 +++++++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 8b7e2702fd..0c45e291ed 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -6708,6 +6708,7 @@ static void Cmd_moveend(void) && IsBattlerTurnDamaged(gBattlerTarget) && IsBattlerAlive(gBattlerTarget) && IsBattlerAlive(gBattlerAttacker) + && !(gStatuses3[BATTLE_PARTNER(gBattlerTarget)] & STATUS3_COMMANDER) && gSpecialStatuses[gBattlerAttacker].parentalBondState != PARENTAL_BOND_1ST_HIT) { u32 targetAbility = GetBattlerAbility(gBattlerTarget); diff --git a/src/battle_util.c b/src/battle_util.c index d38135dbb2..359c88e2b4 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -5363,6 +5363,8 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 case ABILITY_COMMANDER: partner = BATTLE_PARTNER(battler); if (!gSpecialStatuses[battler].switchInAbilityDone + && IsBattlerAlive(partner) + && IsBattlerAlive(battler) && gBattleStruct->commanderActive[partner] == SPECIES_NONE && gBattleMons[partner].species == SPECIES_DONDOZO && GET_BASE_SPECIES_ID(GetMonData(GetPartyBattlerData(battler), MON_DATA_SPECIES)) == SPECIES_TATSUGIRI) diff --git a/test/battle/ability/commander.c b/test/battle/ability/commander.c index d4b31f23b8..a50eba7b8c 100644 --- a/test/battle/ability/commander.c +++ b/test/battle/ability/commander.c @@ -421,3 +421,37 @@ DOUBLE_BATTLE_TEST("Commander Tatsugiri does not get hit by Dragon Darts when co NOT HP_BAR(playerLeft); } } + +DOUBLE_BATTLE_TEST("Commander will not activate if Dondozo fainted right before Tatsugiri came in") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_DONDOZO) { HP(1); } + PLAYER(SPECIES_TATSUGIRI) { Ability(ABILITY_COMMANDER); } + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponentRight, MOVE_TACKLE, target: playerRight); MOVE(playerLeft, MOVE_SHED_TAIL); SEND_OUT(playerLeft, 2); SEND_OUT(playerRight, 3); } + } SCENE { + NOT ABILITY_POPUP(playerLeft, ABILITY_COMMANDER); + } +} + +DOUBLE_BATTLE_TEST("Commander prevent Dondozo from switch out by Dragon Tail") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_DRAGON_TAIL) == EFFECT_HIT_SWITCH_TARGET); + PLAYER(SPECIES_DONDOZO); + PLAYER(SPECIES_TATSUGIRI) { Ability(ABILITY_COMMANDER); } + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponentLeft, MOVE_DRAGON_TAIL, target: playerLeft); } + } SCENE { + ABILITY_POPUP(playerRight, ABILITY_COMMANDER); + ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_TAIL, opponentLeft); + NOT MESSAGE("Wobbuffet was dragged out!"); + } +} From 86969f991962b847940c43d34c71f51d479d3958 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Sun, 20 Apr 2025 22:49:52 +0200 Subject: [PATCH 04/10] Remove HitmarkerSwapAttackerTarget (#6627) Co-authored-by: Bassoonian --- data/battle_scripts_1.s | 1 - include/constants/battle.h | 8 ++++---- src/battle_script_commands.c | 15 +++++---------- src/battle_util.c | 5 ++--- 4 files changed, 11 insertions(+), 18 deletions(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 4f17840393..9a44235816 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -7102,7 +7102,6 @@ BattleScript_MoveUsedIsAsleep:: goto BattleScript_MoveEnd BattleScript_MoveUsedWokeUp:: - bicword gHitMarker, HITMARKER_WAKE_UP_CLEAR printfromtable gWokeUpStringIds waitmessage B_WAIT_TIME_LONG updatestatusicon BS_ATTACKER diff --git a/include/constants/battle.h b/include/constants/battle.h index ce1cf0e948..de7bbdfe92 100644 --- a/include/constants/battle.h +++ b/include/constants/battle.h @@ -196,7 +196,7 @@ #define STATUS4_SYRUP_BOMB (1 << 5) #define STATUS4_GLAIVE_RUSH (1 << 6) -#define HITMARKER_WAKE_UP_CLEAR (1 << 4) // Cleared when waking up. Never set or checked. +#define HITMARKER_UNUSED_1 (1 << 4) #define HITMARKER_IGNORE_BIDE (1 << 5) #define HITMARKER_DESTINYBOND (1 << 6) #define HITMARKER_NO_ANIMATIONS (1 << 7) // set from battleSceneOff. Never changed during battle @@ -204,16 +204,16 @@ #define HITMARKER_NO_ATTACKSTRING (1 << 9) #define HITMARKER_ATTACKSTRING_PRINTED (1 << 10) #define HITMARKER_NO_PPDEDUCT (1 << 11) -#define HITMARKER_SWAP_ATTACKER_TARGET (1 << 12) +#define HITMARKER_UNUSED_2 (1 << 12) #define HITMARKER_STATUS_ABILITY_EFFECT (1 << 13) #define HITMARKER_SYNCHRONISE_EFFECT (1 << 14) #define HITMARKER_RUN (1 << 15) #define HITMARKER_IGNORE_DISGUISE (1 << 16) #define HITMARKER_DISABLE_ANIMATION (1 << 17) // disable animations during battle scripts, e.g. for Bug Bite -// 3 free spots because of change in handling of UNDERGROUND/UNDERWATER/ON AIR +#define HITMARKER_UNUSED_3 (1 << 18) #define HITMARKER_UNABLE_TO_USE_MOVE (1 << 19) #define HITMARKER_PASSIVE_DAMAGE (1 << 20) -#define HITMARKER_DISOBEDIENT_MOVE (1 << 21) +#define HITMARKER_UNUSED_4 (1 << 21) #define HITMARKER_PLAYER_FAINTED (1 << 22) #define HITMARKER_ALLOW_NO_PP (1 << 23) #define HITMARKER_GRUDGE (1 << 24) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 0c45e291ed..069ffb1737 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -6842,12 +6842,13 @@ static void Cmd_moveend(void) } } - if (gHitMarker & HITMARKER_SWAP_ATTACKER_TARGET) + // After swapattackerwithtarget is used for snatch the correct battlers have to be restored so data is stored correctly + if (gBattleStruct->snatchedMoveIsUsed) { - u8 temp; + u32 temp; SWAP(gBattlerAttacker, gBattlerTarget, temp); - gHitMarker &= ~HITMARKER_SWAP_ATTACKER_TARGET; } + if (!gSpecialStatuses[gBattlerAttacker].dancerUsedMove) { gDisableStructs[gBattlerAttacker].usedMoves |= 1u << gCurrMovePos; @@ -8827,14 +8828,8 @@ static void Cmd_swapattackerwithtarget(void) { CMD_ARGS(); - u8 temp; + u32 temp; SWAP(gBattlerAttacker, gBattlerTarget, temp); - - if (gHitMarker & HITMARKER_SWAP_ATTACKER_TARGET) - gHitMarker &= ~HITMARKER_SWAP_ATTACKER_TARGET; - else - gHitMarker |= HITMARKER_SWAP_ATTACKER_TARGET; - gBattlescriptCurrInstr = cmd->nextInstr; } diff --git a/src/battle_util.c b/src/battle_util.c index 359c88e2b4..05f5b83602 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -787,7 +787,7 @@ void HandleAction_NothingIsFainted(void) gCurrentActionFuncId = gActionsByTurnOrder[gCurrentTurnActionNumber]; gHitMarker &= ~(HITMARKER_DESTINYBOND | HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_ATTACKSTRING_PRINTED | HITMARKER_NO_PPDEDUCT | HITMARKER_STATUS_ABILITY_EFFECT | HITMARKER_PASSIVE_DAMAGE - | HITMARKER_OBEYS | HITMARKER_WAKE_UP_CLEAR | HITMARKER_SYNCHRONISE_EFFECT + | HITMARKER_OBEYS | HITMARKER_SYNCHRONISE_EFFECT | HITMARKER_CHARGING | HITMARKER_NEVER_SET); } @@ -801,7 +801,7 @@ void HandleAction_ActionFinished(void) SpecialStatusesClear(); gHitMarker &= ~(HITMARKER_DESTINYBOND | HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_ATTACKSTRING_PRINTED | HITMARKER_NO_PPDEDUCT | HITMARKER_STATUS_ABILITY_EFFECT | HITMARKER_PASSIVE_DAMAGE - | HITMARKER_OBEYS | HITMARKER_WAKE_UP_CLEAR | HITMARKER_SYNCHRONISE_EFFECT + | HITMARKER_OBEYS | HITMARKER_SYNCHRONISE_EFFECT | HITMARKER_CHARGING | HITMARKER_NEVER_SET | HITMARKER_IGNORE_DISGUISE); // check if Stellar type boost should be used up @@ -3309,7 +3309,6 @@ static void CancellerObedience(u32 *effect) SetAtkCancellerForCalledMove(); gBattlescriptCurrInstr = BattleScript_IgnoresAndUsesRandomMove; gBattlerTarget = GetBattleMoveTarget(gCalledMove, NO_TARGET_OVERRIDE); - gHitMarker |= HITMARKER_DISOBEDIENT_MOVE; gHitMarker |= HITMARKER_OBEYS; break; } From 0069ec1f0c1c096caa09ccc2aa12a366bb067b8f Mon Sep 17 00:00:00 2001 From: cawtds <38510667+cawtds@users.noreply.github.com> Date: Mon, 21 Apr 2025 11:01:01 +0200 Subject: [PATCH 05/10] Fix captivate-oblivious interaction and related test (#6653) --- asm/macros/battle_script.inc | 2 +- data/battle_scripts_1.s | 10 ++++++---- src/battle_script_commands.c | 22 ++++++++++++++++------ test/battle/ability/oblivious.c | 2 +- 4 files changed, 24 insertions(+), 12 deletions(-) diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index e386c389c9..2af5c1fcc8 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -1331,7 +1331,7 @@ .byte \stat .endm - .macro jumpifoppositegenders jumpInstr:req + .macro jumpifcaptivateaffected jumpInstr:req .byte 0xfc .4byte \jumpInstr .endm diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 9a44235816..5631934ed1 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -2743,11 +2743,13 @@ BattleScript_EffectRoost:: BattleScript_EffectCaptivate:: setstatchanger STAT_SPATK, 2, TRUE attackcanceler - jumpifsubstituteblocks BattleScript_FailedFromAtkString - jumpifoppositegenders BattleScript_CaptivateCheckAcc - goto BattleScript_FailedFromAtkString + attackstring + ppreduce + jumpifsubstituteblocks BattleScript_ButItFailed + jumpifcaptivateaffected BattleScript_CaptivateCheckAcc + goto BattleScript_ButItFailed BattleScript_CaptivateCheckAcc: - accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE + accuracycheck BattleScript_MoveMissedPause, ACC_CURR_MOVE goto BattleScript_StatDownFromAttackString BattleScript_EffectHealBlock:: diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 069ffb1737..f743879fc6 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -599,7 +599,7 @@ static void Cmd_trainerslideout(void); static void Cmd_settelekinesis(void); static void Cmd_swapstatstages(void); static void Cmd_averagestats(void); -static void Cmd_jumpifoppositegenders(void); +static void Cmd_jumpifcaptivateaffected(void); static void Cmd_unused(void); static void Cmd_tryworryseed(void); static void Cmd_callnative(void); @@ -836,8 +836,8 @@ void (* const gBattleScriptingCommandsTable[])(void) = Cmd_jumpifhasnohp, //0xE3 Cmd_jumpifnotcurrentmoveargtype, //0xE4 Cmd_pickup, //0xE5 - Cmd_unused_0xE6, //0xE6 - Cmd_unused_0xE7, //0xE7 + Cmd_unused_0xE6, //0xE6 + Cmd_unused_0xE7, //0xE7 Cmd_settypebasedhalvers, //0xE8 Cmd_jumpifsubstituteblocks, //0xE9 Cmd_tryrecycleitem, //0xEA @@ -858,7 +858,7 @@ void (* const gBattleScriptingCommandsTable[])(void) = Cmd_settelekinesis, //0xF9 Cmd_swapstatstages, //0xFA Cmd_averagestats, //0xFB - Cmd_jumpifoppositegenders, //0xFC + Cmd_jumpifcaptivateaffected, //0xFC Cmd_unused, //0xFD Cmd_tryworryseed, //0xFE Cmd_callnative, //0xFF @@ -16665,14 +16665,24 @@ static void Cmd_averagestats(void) gBattlescriptCurrInstr = cmd->nextInstr; } -static void Cmd_jumpifoppositegenders(void) +static void Cmd_jumpifcaptivateaffected(void) { CMD_ARGS(const u8 *jumpInstr); - if (AreBattlersOfOppositeGender(gBattlerAttacker, gBattlerTarget)) + if (GetBattlerAbility(gBattlerTarget) == ABILITY_OBLIVIOUS) + { + gBattlescriptCurrInstr = BattleScript_NotAffectedAbilityPopUp; + gLastUsedAbility = ABILITY_OBLIVIOUS; + RecordAbilityBattle(gBattlerTarget, ABILITY_OBLIVIOUS); + } + else if (AreBattlersOfOppositeGender(gBattlerAttacker, gBattlerTarget)) + { gBattlescriptCurrInstr = cmd->jumpInstr; + } else + { gBattlescriptCurrInstr = cmd->nextInstr; + } } static void Cmd_unused(void) diff --git a/test/battle/ability/oblivious.c b/test/battle/ability/oblivious.c index 3ac979a271..a787ec6578 100644 --- a/test/battle/ability/oblivious.c +++ b/test/battle/ability/oblivious.c @@ -23,7 +23,7 @@ SINGLE_BATTLE_TEST("Oblivious prevents Captivate") PLAYER(SPECIES_SLOWPOKE) { Ability(ABILITY_OBLIVIOUS); Gender(MON_MALE); } OPPONENT(SPECIES_WOBBUFFET) { Gender(MON_FEMALE); } } WHEN { - TURN { MOVE(opponent, MOVE_ATTRACT); } + TURN { MOVE(opponent, MOVE_CAPTIVATE); } } SCENE { ABILITY_POPUP(player, ABILITY_OBLIVIOUS); NONE_OF { ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); } From c4edfd29887ba1769b0780a5434b86db5f608706 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Mon, 21 Apr 2025 13:53:04 +0200 Subject: [PATCH 06/10] Fix gBattlerTarget OOB for Gravity and Perish Song (#6662) --- data/battle_scripts_1.s | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 5631934ed1..2c82112b1a 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -2713,6 +2713,7 @@ BattleScript_EffectGravity:: attackstring ppreduce setgravity BattleScript_ButItFailed + savetarget attackanimation waitanimation BattleScript_EffectGravitySuccess:: @@ -2730,6 +2731,7 @@ BattleScript_GravityLoopDrop: BattleScript_GravityLoopEnd: moveendcase MOVEEND_TARGET_VISIBLE jumpifnexttargetvalid BattleScript_GravityLoop + restoretarget goto BattleScript_MoveEnd BattleScript_EffectRoost:: @@ -4213,6 +4215,7 @@ BattleScript_EffectPerishSong:: attackstring ppreduce trysetperishsong BattleScript_ButItFailed + savetarget attackanimation waitanimation printstring STRINGID_FAINTINTHREE @@ -4224,6 +4227,7 @@ BattleScript_PerishSongLoop:: BattleScript_PerishSongLoopIncrement:: addbyte gBattlerTarget, 1 jumpifbytenotequal gBattlerTarget, gBattlersCount, BattleScript_PerishSongLoop + restoretarget goto BattleScript_MoveEnd BattleScript_PerishSongBlocked:: From 4e7fa359ef2fc54c46cb6ea1ec8aca5a18143937 Mon Sep 17 00:00:00 2001 From: Eduardo Quezada Date: Mon, 21 Apr 2025 08:48:49 -0400 Subject: [PATCH 07/10] Fixed LGPE move test fail with `B_UPDATED_MOVE_DATA` to `GEN_7` (#6657) --- test/battle/move_effect_secondary/haze.c | 5 ++++- test/battle/move_effect_secondary/leech_seed.c | 5 ++++- test/battle/move_effect_secondary/light_screen.c | 5 ++++- test/battle/move_effect_secondary/reflect.c | 5 ++++- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/test/battle/move_effect_secondary/haze.c b/test/battle/move_effect_secondary/haze.c index c3831f0768..c6a0f2cab1 100644 --- a/test/battle/move_effect_secondary/haze.c +++ b/test/battle/move_effect_secondary/haze.c @@ -16,7 +16,10 @@ SINGLE_BATTLE_TEST("Freeze Frost restores stat changes when it was succesful") PLAYER(SPECIES_WYNAUT); OPPONENT(SPECIES_WOBBUFFET); } WHEN { - TURN { MOVE(player, MOVE_FREEZY_FROST, hit: moveSuccess); } + TURN { + MOVE(opponent, MOVE_SAND_ATTACK); + MOVE(player, MOVE_FREEZY_FROST, hit: moveSuccess); + } } SCENE { if (moveSuccess == TRUE) { diff --git a/test/battle/move_effect_secondary/leech_seed.c b/test/battle/move_effect_secondary/leech_seed.c index c5a8db57cc..b7eef45ed4 100644 --- a/test/battle/move_effect_secondary/leech_seed.c +++ b/test/battle/move_effect_secondary/leech_seed.c @@ -26,7 +26,10 @@ SINGLE_BATTLE_TEST("Sappy Seed is not going to seed the target if it fails") PLAYER(SPECIES_WYNAUT); OPPONENT(SPECIES_WOBBUFFET); } WHEN { - TURN { MOVE(player, MOVE_SAPPY_SEED, hit: FALSE); } + TURN { + MOVE(opponent, MOVE_SAND_ATTACK); + MOVE(player, MOVE_SAPPY_SEED, hit: FALSE); + } } SCENE { NONE_OF { ANIMATION(ANIM_TYPE_MOVE, MOVE_SAPPY_SEED, player); diff --git a/test/battle/move_effect_secondary/light_screen.c b/test/battle/move_effect_secondary/light_screen.c index 244e469893..e834e6055f 100644 --- a/test/battle/move_effect_secondary/light_screen.c +++ b/test/battle/move_effect_secondary/light_screen.c @@ -16,7 +16,10 @@ SINGLE_BATTLE_TEST("Glitzy Glow sets up Light Screen when it was succesful") PLAYER(SPECIES_WYNAUT); OPPONENT(SPECIES_WOBBUFFET); } WHEN { - TURN { MOVE(player, MOVE_GLITZY_GLOW, hit: moveSuccess); } + TURN { + MOVE(opponent, MOVE_SAND_ATTACK); + MOVE(player, MOVE_GLITZY_GLOW, hit: moveSuccess); + } } SCENE { if (moveSuccess == TRUE) { diff --git a/test/battle/move_effect_secondary/reflect.c b/test/battle/move_effect_secondary/reflect.c index 6a0dda06d8..0b1dbf7fd3 100644 --- a/test/battle/move_effect_secondary/reflect.c +++ b/test/battle/move_effect_secondary/reflect.c @@ -16,7 +16,10 @@ SINGLE_BATTLE_TEST("Baddy Bad sets up Reflect when it was succesful") PLAYER(SPECIES_WYNAUT); OPPONENT(SPECIES_WOBBUFFET); } WHEN { - TURN { MOVE(player, MOVE_BADDY_BAD, hit: moveSuccess); } + TURN { + MOVE(opponent, MOVE_SAND_ATTACK); + MOVE(player, MOVE_BADDY_BAD, hit: moveSuccess); + } } SCENE { if (moveSuccess == TRUE) { From d220459a016983541b9536b7d0ce56974e17bc0b Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Mon, 21 Apr 2025 17:33:29 +0200 Subject: [PATCH 08/10] Fixes Intimidate / Eject Pack interaction (#6645) Co-authored-by: Bassoonian --- asm/macros/battle_script.inc | 8 +++ data/battle_scripts_1.s | 15 ++--- include/battle.h | 3 +- include/battle_scripts.h | 2 + src/battle_main.c | 3 +- src/battle_script_commands.c | 91 +++++++++++++++++++++++++++- src/battle_util.c | 5 +- test/battle/ability/intimidate.c | 49 ++++++++++++++- test/battle/hold_effect/eject_pack.c | 71 ++++++++++++++++++++++ 9 files changed, 227 insertions(+), 20 deletions(-) diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index 2af5c1fcc8..425ba1cac9 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -1367,6 +1367,10 @@ callnative BS_RestoreAttacker .endm + .macro jumpifintimidateabilityprevented + callnative BS_JumpIfIntimidateAbilityPrevented + .endm + .macro metalburstdamagecalculator failInstr:req callnative BS_CalcMetalBurstDmg .4byte \failInstr @@ -1383,6 +1387,10 @@ .byte \battler .endm + .macro tryintimidatejectpack + callnative BS_TryIntimidatEjectpack + .endm + .macro allyswitchswapbattlers callnative BS_AllySwitchSwapBattler .endm diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 2c82112b1a..a4f13be0b9 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -7735,13 +7735,7 @@ BattleScript_IntimidateLoop: jumpiftargetally BattleScript_IntimidateLoopIncrement jumpifabsent BS_TARGET, BattleScript_IntimidateLoopIncrement jumpifstatus2 BS_TARGET, STATUS2_SUBSTITUTE, BattleScript_IntimidateLoopIncrement -.if B_UPDATED_INTIMIDATE >= GEN_8 @These abilties specifically prevent just intimidate, without blocking stat decreases - jumpifability BS_TARGET, ABILITY_INNER_FOCUS, BattleScript_IntimidatePrevented - jumpifability BS_TARGET, ABILITY_SCRAPPY, BattleScript_IntimidatePrevented - jumpifability BS_TARGET, ABILITY_OWN_TEMPO, BattleScript_IntimidatePrevented - jumpifability BS_TARGET, ABILITY_OBLIVIOUS, BattleScript_IntimidatePrevented -.endif - jumpifability BS_TARGET, ABILITY_GUARD_DOG, BattleScript_IntimidateInReverse + jumpifintimidateabilityprevented BattleScript_IntimidateEffect: copybyte sBATTLER, gBattlerAttacker setstatchanger STAT_ATK, 1, TRUE @@ -7766,9 +7760,10 @@ BattleScript_IntimidateLoopIncrement: destroyabilitypopup restoretarget pause B_WAIT_TIME_MED + tryintimidatejectpack end3 -BattleScript_IntimidatePrevented: +BattleScript_IntimidatePrevented:: copybyte sBATTLER, gBattlerTarget call BattleScript_AbilityPopUp printstring STRINGID_PKMNPREVENTSSTATLOSSWITH @@ -7788,7 +7783,7 @@ BattleScript_IntimidateContrary_WontIncrease: printstring STRINGID_TARGETSTATWONTGOHIGHER goto BattleScript_IntimidateEffect_WaitString -BattleScript_IntimidateInReverse: +BattleScript_IntimidateInReverse:: copybyte sBATTLER, gBattlerTarget call BattleScript_AbilityPopUpTarget pause B_WAIT_TIME_SHORT @@ -9533,7 +9528,7 @@ BattleScript_EjectButtonActivates:: printstring STRINGID_EJECTBUTTONACTIVATE waitmessage B_WAIT_TIME_LONG removeitem BS_SCRIPTING - undodynamax BS_SCRIPTING + undodynamax BS_SCRIPTING makeinvisible BS_SCRIPTING openpartyscreen BS_SCRIPTING, BattleScript_EjectButtonEnd copybyte sSAVED_BATTLER, sBATTLER diff --git a/include/battle.h b/include/battle.h index 823e4813e9..1189ada4ea 100644 --- a/include/battle.h +++ b/include/battle.h @@ -183,7 +183,8 @@ struct ProtectStruct u16 eatMirrorHerb:1; u16 activateOpportunist:2; // 2 - to copy stats. 1 - stats copied (do not repeat). 0 - no stats to copy u16 usedAllySwitch:1; - u16 padding:2; + u16 lashOutAffected:1; + u16 padding:1; // End of 16-bit bitfield u32 physicalDmg; u32 specialDmg; diff --git a/include/battle_scripts.h b/include/battle_scripts.h index d11d2b814e..20ea1ccd49 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -170,6 +170,8 @@ extern const u8 BattleScript_RainDishActivates[]; extern const u8 BattleScript_SandstreamActivates[]; extern const u8 BattleScript_ShedSkinActivates[]; extern const u8 BattleScript_IntimidateActivates[]; +extern const u8 BattleScript_IntimidatePrevented[]; +extern const u8 BattleScript_IntimidateInReverse[]; extern const u8 BattleScript_DroughtActivates[]; extern const u8 BattleScript_TookAttack[]; extern const u8 BattleScript_SturdyPreventsOHKO[]; diff --git a/src/battle_main.c b/src/battle_main.c index 4834e26796..02cd58172d 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -3751,6 +3751,7 @@ static void TryDoEventsBeforeFirstTurn(void) { for (i = 0; i < gBattlersCount; i++) { + gBattleStruct->monToSwitchIntoId[i] = PARTY_SIZE; // Included here because switches can happen before during set ups (eg. eject pack) struct Pokemon *party = GetBattlerParty(i); struct Pokemon *mon = &party[gBattlerPartyIndexes[i]]; if (!IsBattlerAlive(i) || gBattleMons[i].species == SPECIES_NONE || GetMonData(mon, MON_DATA_IS_EGG)) @@ -4846,7 +4847,6 @@ s8 GetBattleMovePriority(u32 battler, u16 move) return priority; } -// Function for AI with variables provided as arguments to speed the computation time s32 GetWhichBattlerFasterArgs(u32 battler1, u32 battler2, bool32 ignoreChosenMoves, u32 ability1, u32 ability2, u32 holdEffectBattler1, u32 holdEffectBattler2, u32 speedBattler1, u32 speedBattler2, s32 priority1, s32 priority2) { @@ -5135,6 +5135,7 @@ static void TurnValuesCleanUp(bool8 var0) gSpecialStatuses[i].parentalBondState = PARENTAL_BOND_OFF; gBattleStruct->battlerState[i].usedEjectItem = FALSE; + gProtectStructs[i].lashOutAffected = FALSE; } gSideStatuses[B_SIDE_PLAYER] &= ~(SIDE_STATUS_QUICK_GUARD | SIDE_STATUS_WIDE_GUARD | SIDE_STATUS_CRAFTY_SHIELD | SIDE_STATUS_MAT_BLOCK); diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index f743879fc6..915cb16758 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -7149,6 +7149,9 @@ static void Cmd_moveend(void) if (numEjectPackBattlers > 1) SortBattlersBySpeed(battlers, FALSE); + for (i = 0; i < gBattlersCount; i++) + gProtectStructs[i].statFell = FALSE; // restore for every possible eject pack battler + for (i = 0; i < gBattlersCount; i++) { u32 battler = battlers[i]; @@ -7167,7 +7170,6 @@ static void Cmd_moveend(void) BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_EjectPackActivates; AI_DATA->ejectPackSwitch = TRUE; - gProtectStructs[battler].statFell = FALSE; break; // Only the fastest Eject item activates } } @@ -12588,7 +12590,8 @@ static u32 ChangeStatBuffs(s8 statValue, u32 statId, u32 flags, const u8 *BS_ptr } else { - gProtectStructs[battler].statFell = TRUE; // Eject pack, lash out + gProtectStructs[battler].statFell = TRUE; + gProtectStructs[battler].lashOutAffected = TRUE; gBattleCommunication[MULTISTRING_CHOOSER] = (gBattlerTarget == battler); // B_MSG_ATTACKER_STAT_FELL or B_MSG_DEFENDER_STAT_FELL } } @@ -18811,3 +18814,87 @@ void BS_SetSteelsurge(void) gBattlescriptCurrInstr = cmd->nextInstr; } } + +void BS_JumpIfIntimidateAbilityPrevented(void) +{ + NATIVE_ARGS(); + + u32 hasAbility = FALSE; + u32 ability = GetBattlerAbility(gBattlerTarget); + + switch (ability) + { + case ABILITY_INNER_FOCUS: + case ABILITY_SCRAPPY: + case ABILITY_OWN_TEMPO: + case ABILITY_OBLIVIOUS: + if (B_UPDATED_INTIMIDATE >= GEN_8) + { + hasAbility = TRUE; + gBattlescriptCurrInstr = BattleScript_IntimidatePrevented; + } + else + { + gBattlescriptCurrInstr = cmd->nextInstr; + } + break; + case ABILITY_GUARD_DOG: + hasAbility = TRUE; + gBattlescriptCurrInstr = BattleScript_IntimidateInReverse; + break; + default: + gBattlescriptCurrInstr = cmd->nextInstr; + break; + } + + if (hasAbility) + { + gLastUsedAbility = ability; + gBattlerAbility = gBattlerTarget; + RecordAbilityBattle(gBattlerTarget, gLastUsedAbility); + } +} + +void BS_TryIntimidatEjectpack(void) +{ + NATIVE_ARGS(); + + u32 affectedBattler = 0xFF; + u32 battler = BATTLE_OPPOSITE(gBattlerAttacker); + u32 partnerBattler = BATTLE_PARTNER(battler); + + bool32 ejectPackBattler = CanEjectPackTrigger(gBattlerAttacker, battler, MOVE_NONE); + bool32 ejectPackPartnerBattler = CanEjectPackTrigger(gBattlerAttacker, partnerBattler, MOVE_NONE); + + if (ejectPackBattler && ejectPackPartnerBattler) + { + u32 battlerSpeed = GetBattlerTotalSpeedStat(battler); + u32 partnerbattlerSpeed = GetBattlerTotalSpeedStat(partnerBattler); + + if (battlerSpeed >= partnerbattlerSpeed) + affectedBattler = battler; + else + affectedBattler = partnerBattler; + } + else if (ejectPackBattler) + { + affectedBattler = battler; + } + else if (ejectPackPartnerBattler) + { + affectedBattler = partnerBattler; + } + + gBattlescriptCurrInstr = cmd->nextInstr; + if (affectedBattler != 0xFF) + { + gProtectStructs[battler].statFell = FALSE; + gProtectStructs[partnerBattler].statFell = FALSE; + AI_DATA->ejectPackSwitch = TRUE; + gBattleScripting.battler = affectedBattler; + gLastUsedItem = gBattleMons[affectedBattler].item; + RecordItemEffectBattle(affectedBattler, HOLD_EFFECT_EJECT_PACK); + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_EjectPackActivate_Ret; + } +} diff --git a/src/battle_util.c b/src/battle_util.c index 05f5b83602..b1e479f06b 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -8449,9 +8449,6 @@ u32 ItemBattleEffects(enum ItemCaseId caseID, u32 battler, bool32 moveTurn) gBattlescriptCurrInstr = BattleScript_WhiteHerbRet; } break; - case HOLD_EFFECT_EJECT_PACK: - effect = TryEjectPack(battler, ITEMEFFECT_ON_SWITCH_IN); - break; } break; } @@ -9229,7 +9226,7 @@ static inline u32 CalcMoveBasePower(struct DamageCalculationData *damageCalcData basePower *= 2; break; case EFFECT_LASH_OUT: - if (gProtectStructs[battlerAtk].statFell) + if (gProtectStructs[battlerAtk].lashOutAffected) basePower *= 2; break; case EFFECT_EXPLOSION: diff --git a/test/battle/ability/intimidate.c b/test/battle/ability/intimidate.c index dd1f761dee..e5571b23fc 100644 --- a/test/battle/ability/intimidate.c +++ b/test/battle/ability/intimidate.c @@ -104,7 +104,7 @@ DOUBLE_BATTLE_TEST("Intimidate doesn't activate on an empty field in a double ba } } -SINGLE_BATTLE_TEST("Intimidate and Eject Button force the opponent to Attack") +SINGLE_BATTLE_TEST("Intimidate and Eject Button don't force the opponent to Attack") { GIVEN { ASSUME(gItemsInfo[ITEM_EJECT_BUTTON].holdEffect == HOLD_EFFECT_EJECT_BUTTON); @@ -365,6 +365,7 @@ DOUBLE_BATTLE_TEST("Intimidate will correctly decrease the attack of the second } SCENE { ABILITY_POPUP(opponentLeft, ABILITY_INTIMIDATE); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerLeft); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerLeft); ABILITY_POPUP(playerLeft, ABILITY_PROTOSYNTHESIS); @@ -372,7 +373,6 @@ DOUBLE_BATTLE_TEST("Intimidate will correctly decrease the attack of the second ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight); } - ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight); } } @@ -390,3 +390,48 @@ SINGLE_BATTLE_TEST("Intimdate does not lose timing after mega evolution and swit ABILITY_POPUP(opponent, ABILITY_INTIMIDATE); } } + +DOUBLE_BATTLE_TEST("Intimidate drop down both opposing atk before eject pack has the chance to activate") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_EJECT_PACK); } + PLAYER(SPECIES_WYNAUT); + PLAYER(SPECIES_EKANS) { Ability(ABILITY_INTIMIDATE); } + OPPONENT(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_EKANS) { Ability(ABILITY_INTIMIDATE); } + } WHEN { + TURN { SWITCH(opponentLeft, 2); SEND_OUT(playerLeft, 2); } + } SCENE { + ABILITY_POPUP(opponentLeft, ABILITY_INTIMIDATE); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerLeft); + ABILITY_POPUP(playerLeft, ABILITY_INTIMIDATE); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight); + } +} + +DOUBLE_BATTLE_TEST("Intimidate will not miss timing for competitive") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_EJECT_PACK); } + PLAYER(SPECIES_MILOTIC) { Ability(ABILITY_COMPETITIVE); } + PLAYER(SPECIES_EKANS) { Ability(ABILITY_INTIMIDATE); } + OPPONENT(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_EKANS) { Ability(ABILITY_INTIMIDATE); } + } WHEN { + TURN { SWITCH(opponentLeft, 2); SEND_OUT(playerLeft, 2); } + } SCENE { + ABILITY_POPUP(opponentLeft, ABILITY_INTIMIDATE); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight); + ABILITY_POPUP(playerRight, ABILITY_COMPETITIVE); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerLeft); + ABILITY_POPUP(playerLeft, ABILITY_INTIMIDATE); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight); + } +} diff --git a/test/battle/hold_effect/eject_pack.c b/test/battle/hold_effect/eject_pack.c index da053ec5e6..0a21362a99 100644 --- a/test/battle/hold_effect/eject_pack.c +++ b/test/battle/hold_effect/eject_pack.c @@ -160,3 +160,74 @@ SINGLE_BATTLE_TEST("Eject Pack will miss timing to switch out user if Eject Butt EXPECT(opponent->species == SPECIES_WYNAUT); } } + +DOUBLE_BATTLE_TEST("Eject Pack: Only the fastest Eject Pack will activate after intimidate") +{ + u32 speed; + + PARAMETRIZE { speed = 1; } + PARAMETRIZE { speed = 11; } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Speed(10); Item(ITEM_EJECT_PACK); } + PLAYER(SPECIES_WYNAUT) { Speed(speed); Item(ITEM_EJECT_PACK); } + PLAYER(SPECIES_WOBBUFFET) { Speed(3); } + OPPONENT(SPECIES_WYNAUT) { Speed(4); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(5); } + OPPONENT(SPECIES_EKANS) { Speed(6); Ability(ABILITY_INTIMIDATE); } + } WHEN { + TURN { + SWITCH(opponentLeft, 2); + if (speed == 11) + SEND_OUT(playerRight, 2); + else + SEND_OUT(playerLeft, 2); + } + } SCENE { + ABILITY_POPUP(opponentLeft, ABILITY_INTIMIDATE); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight); + if (speed == 11) { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerRight); + NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerLeft); + } else { + NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerRight); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerLeft); + } + } +} + +DOUBLE_BATTLE_TEST("Eject Pack: Only the fastest Eject Pack will activate after a move stat drop") +{ + u32 speed; + + PARAMETRIZE { speed = 1; } + PARAMETRIZE { speed = 11; } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Speed(10); Item(ITEM_EJECT_PACK); } + PLAYER(SPECIES_WYNAUT) { Speed(speed); Item(ITEM_EJECT_PACK); } + PLAYER(SPECIES_WOBBUFFET) { Speed(3); } + OPPONENT(SPECIES_WYNAUT) { Speed(4); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(5); } + } WHEN { + TURN { + MOVE(opponentLeft, MOVE_BUBBLE); + if (speed == 11) + SEND_OUT(playerRight, 2); + else + SEND_OUT(playerLeft, 2); + } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_BUBBLE, opponentLeft); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight); + if (speed == 11) { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerRight); + NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerLeft); + } else { + NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerRight); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerLeft); + } + } +} From 007d7e712e19a1a740d3d2b318a360991eaa4d3d Mon Sep 17 00:00:00 2001 From: PhallenTree <168426989+PhallenTree@users.noreply.github.com> Date: Mon, 21 Apr 2025 18:14:53 +0100 Subject: [PATCH 09/10] Fixes Neutralizing Gas activating again after switch-in (#6667) --- src/battle_script_commands.c | 1 + test/battle/ability/teraform_zero.c | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 915cb16758..225646ee29 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -8179,6 +8179,7 @@ static bool32 DoSwitchInEffectsForBattler(u32 battler) { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWITCHIN_NEUTRALIZING_GAS; gSpecialStatuses[battler].announceNeutralizingGas = TRUE; + gDisableStructs[battler].neutralizingGas = TRUE; gBattlerAbility = battler; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_SwitchInAbilityMsgRet; diff --git a/test/battle/ability/teraform_zero.c b/test/battle/ability/teraform_zero.c index 09ce921931..96186f2047 100644 --- a/test/battle/ability/teraform_zero.c +++ b/test/battle/ability/teraform_zero.c @@ -84,7 +84,6 @@ SINGLE_BATTLE_TEST("Teraform Zero cannot be copied") DOUBLE_BATTLE_TEST("Teraform Zero shouldn't cause Neutralizing Gas to show it's popup when trying to activate") { - KNOWN_FAILING; // #5010 GIVEN { PLAYER(SPECIES_TERAPAGOS_TERASTAL); PLAYER(SPECIES_ABSOL) {Ability(ABILITY_PRESSURE); } @@ -95,8 +94,8 @@ DOUBLE_BATTLE_TEST("Teraform Zero shouldn't cause Neutralizing Gas to show it's TURN { SWITCH(playerRight, 2); MOVE(playerLeft, MOVE_CELEBRATE, gimmick: GIMMICK_TERA); } } SCENE { MESSAGE("Terapagos is storing energy!"); - MESSAGE("Terapagos terastalized into the Stellar type!"); + MESSAGE("Terapagos terastallized into the Stellar type!"); NOT ABILITY_POPUP(playerRight, ABILITY_NEUTRALIZING_GAS); - MESSAGE("Terapagos used Celebreate!"); + MESSAGE("Terapagos used Celebrate!"); } } From 72839fc002e805d4f38986c8b80e83814f31c742 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Tue, 22 Apr 2025 00:38:39 +0200 Subject: [PATCH 10/10] Fixes Bug Bite eaten berry not ignoring Unnerve (#6666) --- src/battle_main.c | 3 +++ src/battle_util.c | 3 +++ test/battle/move_effect_secondary/bug_bite.c | 15 +++++++++++++++ 3 files changed, 21 insertions(+) diff --git a/src/battle_main.c b/src/battle_main.c index 02cd58172d..85194a0d54 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -5106,6 +5106,9 @@ static void TurnValuesCleanUp(bool8 var0) gProtectStructs[i].spikyShielded = FALSE; gProtectStructs[i].kingsShielded = FALSE; gProtectStructs[i].banefulBunkered = FALSE; + gProtectStructs[i].obstructed = FALSE; + gProtectStructs[i].silkTrapped = FALSE; + gProtectStructs[i].burningBulwarked = FALSE; gProtectStructs[i].quash = FALSE; gProtectStructs[i].usedCustapBerry = FALSE; gProtectStructs[i].quickDraw = FALSE; diff --git a/src/battle_util.c b/src/battle_util.c index b1e479f06b..d9e64c969c 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -7234,6 +7234,9 @@ static u32 ItemHealHp(u32 battler, u32 itemId, enum ItemCaseId caseID, bool32 pe static bool32 UnnerveOn(u32 battler, u32 itemId) { + if (gBattleScripting.overrideBerryRequirements > 0) // Berries that aren't eaten naturally ignore unnerve + return FALSE; + if (ItemId_GetPocket(itemId) == POCKET_BERRIES && IsUnnerveAbilityOnOpposingSide(battler)) return TRUE; return FALSE; diff --git a/test/battle/move_effect_secondary/bug_bite.c b/test/battle/move_effect_secondary/bug_bite.c index e086941a5e..70e0966caa 100644 --- a/test/battle/move_effect_secondary/bug_bite.c +++ b/test/battle/move_effect_secondary/bug_bite.c @@ -133,3 +133,18 @@ SINGLE_BATTLE_TEST("Tanga Berry activates before Bug Bite") EXPECT_EQ(player->item, ITEM_NONE); } } + +SINGLE_BATTLE_TEST("Bug Bite ignores Unnerve") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { HP(1); } + OPPONENT(SPECIES_TYRANITAR) { Ability(ABILITY_UNNERVE); Item(ITEM_ORAN_BERRY); } + } WHEN { + TURN { MOVE(player, MOVE_BUG_BITE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_BUG_BITE, player); + HP_BAR(player); + } THEN { + EXPECT_EQ(opponent->item, ITEM_NONE); + } +}