From 9104c40d43e573daff06de91e2ecef5423ec2677 Mon Sep 17 00:00:00 2001 From: PhallenTree <168426989+PhallenTree@users.noreply.github.com> Date: Mon, 27 May 2024 16:07:13 +0100 Subject: [PATCH 1/8] Fixes Dancer triggering if the move's user didn't act (#4638) * Fixes Dancer triggering if the user flinched * Add check to make sure "Pokemon used move" message is kept * Address review --------- Co-authored-by: Fltp --- src/battle_script_commands.c | 15 +++++++- src/battle_util.c | 1 + test/battle/ability/dancer.c | 73 ++++++++++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+), 1 deletion(-) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index b2d9e8a152d0..459c6007f7c0 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -6259,8 +6259,19 @@ static void Cmd_moveend(void) case MOVEEND_DANCER: // Special case because it's so annoying if (gMovesInfo[gCurrentMove].danceMove) { - u8 battler, nextDancer = 0; + u32 battler, nextDancer = 0; + bool32 turnOnHitmarker = FALSE; + for (battler = 0; battler < MAX_BATTLERS_COUNT; battler++) + { + if (gSpecialStatuses[battler].dancerUsedMove) + { + // in case a battler fails to act on a Dancer-called move + turnOnHitmarker = TRUE; + break; + } + } + if (!(gBattleStruct->lastMoveFailed & gBitTable[gBattlerAttacker] || (!gSpecialStatuses[gBattlerAttacker].dancerUsedMove && gBattleStruct->bouncedMoveIsUsed))) @@ -6276,6 +6287,8 @@ static void Cmd_moveend(void) { if (GetBattlerAbility(battler) == ABILITY_DANCER && !gSpecialStatuses[battler].dancerUsedMove) { + if (turnOnHitmarker) + gHitMarker |= HITMARKER_ATTACKSTRING_PRINTED; if (!nextDancer || (gBattleMons[battler].speed < gBattleMons[nextDancer & 0x3].speed)) nextDancer = battler | 0x4; } diff --git a/src/battle_util.c b/src/battle_util.c index 1fe6facf1262..493b1667b8b0 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -5754,6 +5754,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 if (IsBattlerAlive(battler) && (gMovesInfo[gCurrentMove].danceMove) && !gSpecialStatuses[battler].dancerUsedMove + && (gHitMarker & HITMARKER_ATTACKSTRING_PRINTED) && gBattlerAttacker != battler) { // Set bit and save Dancer mon's original target diff --git a/test/battle/ability/dancer.c b/test/battle/ability/dancer.c index 34af26e75e3f..f54f7ab76ae7 100644 --- a/test/battle/ability/dancer.c +++ b/test/battle/ability/dancer.c @@ -53,3 +53,76 @@ DOUBLE_BATTLE_TEST("Dancer can copy Teeter Dance and confuse both opposing targe MESSAGE("Wynaut became confused!"); } } + +DOUBLE_BATTLE_TEST("Dancer triggers from slowest to fastest") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_DRAGON_DANCE].danceMove == TRUE); + PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_DANCER); Speed(10); } + PLAYER(SPECIES_WYNAUT) { Speed(50); } + OPPONENT(SPECIES_ORICORIO) { Ability(ABILITY_DANCER); Speed(20); } + OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_DANCER); Speed(3); } + } WHEN { + TURN { MOVE(playerRight, MOVE_DRAGON_DANCE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DANCE, playerRight); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight); + ABILITY_POPUP(opponentRight, ABILITY_DANCER); + ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DANCE, opponentRight); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight); + ABILITY_POPUP(playerLeft, ABILITY_DANCER); + ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DANCE, playerLeft); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft); + ABILITY_POPUP(opponentLeft, ABILITY_DANCER); + ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DANCE, opponentLeft); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft); + } +} + +SINGLE_BATTLE_TEST("Dancer doesn't trigger if the original user flinches") +{ + GIVEN { + ASSUME(MoveHasAdditionalEffectWithChance(MOVE_FAKE_OUT, MOVE_EFFECT_FLINCH, 100)); + ASSUME(gMovesInfo[MOVE_DRAGON_DANCE].danceMove == TRUE); + PLAYER(SPECIES_WOBBUFFET) + OPPONENT(SPECIES_ORICORIO) { Ability(ABILITY_DANCER); } + } WHEN { + TURN { MOVE(opponent, MOVE_FAKE_OUT); MOVE(player, MOVE_DRAGON_DANCE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_FAKE_OUT, opponent); + MESSAGE("Wobbuffet flinched!"); + NONE_OF { + ABILITY_POPUP(opponent, ABILITY_DANCER); + ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DANCE, opponent); + } + } +} + +DOUBLE_BATTLE_TEST("Dancer still triggers if another dancer flinches") +{ + GIVEN { + ASSUME(MoveHasAdditionalEffectWithChance(MOVE_FAKE_OUT, MOVE_EFFECT_FLINCH, 100)); + ASSUME(gMovesInfo[MOVE_DRAGON_DANCE].danceMove == TRUE); + PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_DANCER); Speed(10); } + PLAYER(SPECIES_WYNAUT) { Speed(5); } + OPPONENT(SPECIES_ORICORIO) { Ability(ABILITY_DANCER); Speed(20); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(3); } + } WHEN { + TURN { MOVE(opponentLeft, MOVE_FAKE_OUT, target: playerLeft); MOVE(playerRight, MOVE_DRAGON_DANCE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_FAKE_OUT, opponentLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DANCE, playerRight); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight); + ABILITY_POPUP(playerLeft, ABILITY_DANCER); + MESSAGE("Wobbuffet flinched!"); + NONE_OF { + MESSAGE("Wobbuffet used Dragon Dance!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DANCE, playerLeft); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft); + } + ABILITY_POPUP(opponentLeft, ABILITY_DANCER); + MESSAGE("Foe Oricorio used Dragon Dance!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DANCE, opponentLeft); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft); + } +} From df6fab7284ae6e7566deaf1ab15965883cf9698f Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Tue, 28 May 2024 10:29:37 +0200 Subject: [PATCH 2/8] Missing ignoreSubstitute flags (#4623) * Missing ignoreSubstitute flags * update defog flag --- include/pokemon.h | 1 + src/data/moves_info.h | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/include/pokemon.h b/include/pokemon.h index d37e5f4ae946..a4a7ad47a006 100644 --- a/include/pokemon.h +++ b/include/pokemon.h @@ -514,6 +514,7 @@ struct MoveInfo u32 parentalBondBanned:1; u32 skyBattleBanned:1; u32 sketchBanned:1; + u32 padding:5; // end of word u32 argument; diff --git a/src/data/moves_info.h b/src/data/moves_info.h index fa11b510e150..35672537a6da 100644 --- a/src/data/moves_info.h +++ b/src/data/moves_info.h @@ -6329,6 +6329,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .category = DAMAGE_CATEGORY_STATUS, .zMove = { .effect = Z_EFFECT_DEF_UP_1 }, .magicCoatAffected = B_UPDATED_MOVE_FLAGS >= GEN_5, + .ignoresSubstitute = TRUE, .contestEffect = CONTEST_EFFECT_MAKE_FOLLOWING_MONS_NERVOUS, .contestCategory = CONTEST_CATEGORY_TOUGH, .contestComboStarterId = 0, @@ -10411,7 +10412,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .priority = 0, .category = DAMAGE_CATEGORY_STATUS, .zMove = { .effect = Z_EFFECT_ACC_UP_1 }, - //.ignoresSubstitute = TRUE, + .ignoresSubstitute = B_UPDATED_MOVE_FLAGS == GEN_4, .magicCoatAffected = B_UPDATED_MOVE_FLAGS >= GEN_5, .contestEffect = CONTEST_EFFECT_WORSEN_CONDITION_OF_PREV_MONS, .contestCategory = CONTEST_CATEGORY_BEAUTY, @@ -14338,6 +14339,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .zMove = { .effect = Z_EFFECT_SPDEF_UP_2 }, .powderMove = TRUE, .magicCoatAffected = TRUE, + .ignoresSubstitute = TRUE, .contestEffect = CONTEST_EFFECT_DONT_EXCITE_AUDIENCE, .contestCategory = CONTEST_CATEGORY_SMART, .contestComboStarterId = 0, From e0499f8b3e0a1549043e1b9212e31836d5073e1d Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Tue, 28 May 2024 10:30:27 +0200 Subject: [PATCH 3/8] Fixes AI going for speed control even when faster (#4630) * Fixes AI going for speed control even when faster * Update src/battle_ai_util.c --- src/battle_ai_util.c | 62 +++++++++++--------------------------------- 1 file changed, 15 insertions(+), 47 deletions(-) diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 84bd85e3bb05..afcd1121e6ff 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -917,48 +917,21 @@ static u32 AI_GetEffectiveness(uq4_12_t multiplier) */ s32 AI_WhoStrikesFirst(u32 battlerAI, u32 battler2, u32 moveConsidered) { - u32 fasterAI = 0, fasterPlayer = 0, i; s8 prioAI = 0; s8 prioBattler2 = 0; - u16 *battler2Moves = GetMovesArray(battler2); - - // Check move priorities first. prioAI = GetMovePriority(battlerAI, moveConsidered); - for (i = 0; i < MAX_MON_MOVES; i++) - { - prioBattler2 = GetMovePriority(battler2, battler2Moves[i]); - if (battler2Moves[i] == MOVE_NONE || battler2Moves[i] == MOVE_UNAVAILABLE - || (prioBattler2 > prioAI && !CanIndexMoveFaintTarget(battler2, battlerAI, i , 2))) - continue; - if (prioAI > prioBattler2) - fasterAI++; - else if (prioBattler2 > prioAI) - fasterPlayer++; - } + if (prioAI > prioBattler2) + return AI_IS_FASTER; - if (fasterAI > fasterPlayer) - { + if (GetWhichBattlerFasterArgs(battlerAI, battler2, TRUE, + AI_DATA->abilities[battlerAI], AI_DATA->abilities[battler2], + AI_DATA->holdEffects[battlerAI], AI_DATA->holdEffects[battler2], + AI_DATA->speedStats[battlerAI], AI_DATA->speedStats[battler2], + prioAI, prioBattler2) == 1) return AI_IS_FASTER; - } - else if (fasterAI < fasterPlayer) - { - return AI_IS_SLOWER; - } else - { - if (prioAI > prioBattler2) - return AI_IS_FASTER; // if we didn't know any of battler 2's moves to compare priorities, assume they don't have a prio+ move - // Priorities are the same(at least comparing to moves the AI is aware of), decide by speed. - if (GetWhichBattlerFasterArgs(battlerAI, battler2, TRUE, - AI_DATA->abilities[battlerAI], AI_DATA->abilities[battler2], - AI_DATA->holdEffects[battlerAI], AI_DATA->holdEffects[battler2], - AI_DATA->speedStats[battlerAI], AI_DATA->speedStats[battler2], - prioAI, prioBattler2) == 1) - return AI_IS_FASTER; - else - return AI_IS_SLOWER; - } + return AI_IS_SLOWER; } // Check if target has means to faint ai mon. @@ -1657,19 +1630,14 @@ bool32 ShouldLowerDefense(u32 battlerAtk, u32 battlerDef, u32 defAbility) bool32 ShouldLowerSpeed(u32 battlerAtk, u32 battlerDef, u32 defAbility) { - if (AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered) - && (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_TRY_TO_FAINT) - && CanAIFaintTarget(battlerAtk, battlerDef, 0)) - return FALSE; // Don't bother lowering stats if can kill enemy. + if (defAbility == ABILITY_CONTRARY + || defAbility == ABILITY_CLEAR_BODY + || defAbility == ABILITY_FULL_METAL_BODY + || defAbility == ABILITY_WHITE_SMOKE + || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CLEAR_AMULET) + return FALSE; - if (!AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered) - && defAbility != ABILITY_CONTRARY - && defAbility != ABILITY_CLEAR_BODY - && defAbility != ABILITY_FULL_METAL_BODY - && defAbility != ABILITY_WHITE_SMOKE - && AI_DATA->holdEffects[battlerDef] != HOLD_EFFECT_CLEAR_AMULET) - return TRUE; - return FALSE; + return (!AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered)); } bool32 ShouldLowerSpAtk(u32 battlerAtk, u32 battlerDef, u32 defAbility) From 0570609ce63e1bdbddb8da01acfeb52c7aa2765b Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Tue, 28 May 2024 10:34:05 +0200 Subject: [PATCH 4/8] Small Ability Effect Move Block refactor (#4635) * Small Ability Effect Move Block refactor * combine tests --- data/battle_scripts_1.s | 2 +- src/battle_util.c | 72 +++++++++++++++++++++--------- test/battle/ability/dazzling.c | 52 +++++++++++++++++++++ test/battle/ability/good_as_gold.c | 70 +++++++++++++++++++++++++++++ 4 files changed, 174 insertions(+), 22 deletions(-) create mode 100644 test/battle/ability/dazzling.c create mode 100644 test/battle/ability/good_as_gold.c diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index a13aacea0cc7..a2184db6a600 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -8215,7 +8215,7 @@ BattleScript_DazzlingProtected:: attackstring ppreduce pause B_WAIT_TIME_SHORT - call BattleScript_AbilityPopUp + call BattleScript_AbilityPopUpScripting printstring STRINGID_POKEMONCANNOTUSEMOVE waitmessage B_WAIT_TIME_LONG goto BattleScript_MoveEnd diff --git a/src/battle_util.c b/src/battle_util.c index 493b1667b8b0..11cd2544d7ea 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -4977,30 +4977,69 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 case ABILITYEFFECT_MOVES_BLOCK: // 2 { u16 moveTarget = GetBattlerMoveTargetType(battler, move); - u16 battlerAbility = GetBattlerAbility(battler); - u16 targetAbility = GetBattlerAbility(gBattlerTarget); - if ((gLastUsedAbility == ABILITY_SOUNDPROOF && gMovesInfo[move].soundMove && !(moveTarget & MOVE_TARGET_USER)) - || (gLastUsedAbility == ABILITY_BULLETPROOF && gMovesInfo[move].ballisticMove)) + switch (gLastUsedAbility) + { + case ABILITY_SOUNDPROOF: + if (gMovesInfo[move].soundMove && !(moveTarget & MOVE_TARGET_USER)) + effect = 1; + break; + case ABILITY_BULLETPROOF: + if (gMovesInfo[move].ballisticMove) + effect = 1; + break; + case ABILITY_DAZZLING: + case ABILITY_QUEENLY_MAJESTY: + case ABILITY_ARMOR_TAIL: + if (GetChosenMovePriority(gBattlerAttacker) > 0 && GetBattlerSide(gBattlerAttacker) != GetBattlerSide(battler)) + effect = 2; + break; + case ABILITY_GOOD_AS_GOLD: + if (IS_MOVE_STATUS(gCurrentMove) + && !(moveTarget & MOVE_TARGET_USER) + && !(moveTarget & MOVE_TARGET_OPPONENTS_FIELD) + && !(moveTarget & MOVE_TARGET_ALL_BATTLERS)) + effect = 3; + break; + } + + if (!effect) + { + switch (GetBattlerAbility(BATTLE_PARTNER(battler))) + { + case ABILITY_DAZZLING: + case ABILITY_QUEENLY_MAJESTY: + case ABILITY_ARMOR_TAIL: + if (GetChosenMovePriority(gBattlerAttacker) > 0 && GetBattlerSide(gBattlerAttacker) != GetBattlerSide(battler)) + effect = 4; + break; + } + } + + if (effect == 1) { if (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS) gHitMarker |= HITMARKER_NO_PPDEDUCT; gBattlescriptCurrInstr = BattleScript_SoundproofProtected; - effect = 1; } - else if ((gLastUsedAbility == ABILITY_DAZZLING || gLastUsedAbility == ABILITY_QUEENLY_MAJESTY || gLastUsedAbility == ABILITY_ARMOR_TAIL || IsBattlerAlive(battler ^= BIT_FLANK)) - && (battlerAbility == ABILITY_DAZZLING || battlerAbility == ABILITY_QUEENLY_MAJESTY || battlerAbility == ABILITY_ARMOR_TAIL) - && GetChosenMovePriority(gBattlerAttacker) > 0 - && GetBattlerSide(gBattlerAttacker) != GetBattlerSide(battler)) + else if (effect == 2 || effect == 4) { + if (effect == 4) + gBattleScripting.battler = BATTLE_PARTNER(battler); + else + gBattleScripting.battler = battler; + if (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS) gHitMarker |= HITMARKER_NO_PPDEDUCT; gBattlescriptCurrInstr = BattleScript_DazzlingProtected; - effect = 1; + } + else if (effect == 3) + { + gBattlescriptCurrInstr = BattleScript_GoodAsGoldActivates; } else if (GetChosenMovePriority(gBattlerAttacker) > 0 - && BlocksPrankster(move, gBattlerAttacker, gBattlerTarget, TRUE) - && !(IS_MOVE_STATUS(move) && (targetAbility == ABILITY_MAGIC_BOUNCE || gProtectStructs[gBattlerTarget].bounceMove))) + && BlocksPrankster(move, gBattlerAttacker, gBattlerTarget, TRUE) + && !(IS_MOVE_STATUS(move) && (gLastUsedAbility == ABILITY_MAGIC_BOUNCE || gProtectStructs[gBattlerTarget].bounceMove))) { if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE) || !(moveTarget & (MOVE_TARGET_BOTH | MOVE_TARGET_FOES_AND_ALLY))) CancelMultiTurnMoves(gBattlerAttacker); // Don't cancel moves that can hit two targets bc one target might not be protected @@ -5008,15 +5047,6 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 gBattlescriptCurrInstr = BattleScript_DarkTypePreventsPrankster; effect = 1; } - else if (GetBattlerAbility(gBattlerTarget) == ABILITY_GOOD_AS_GOLD - && IS_MOVE_STATUS(gCurrentMove) - && !(moveTarget & MOVE_TARGET_USER) - && !(moveTarget & MOVE_TARGET_OPPONENTS_FIELD) - && !(moveTarget & MOVE_TARGET_ALL_BATTLERS)) - { - gBattlescriptCurrInstr = BattleScript_GoodAsGoldActivates; - effect = 1; - } break; } case ABILITYEFFECT_ABSORBING: // 3 diff --git a/test/battle/ability/dazzling.c b/test/battle/ability/dazzling.c new file mode 100644 index 000000000000..9eedb56a4957 --- /dev/null +++ b/test/battle/ability/dazzling.c @@ -0,0 +1,52 @@ +#include "global.h" +#include "test/battle.h" + + +ASSUMPTIONS +{ + ASSUME(gMovesInfo[MOVE_QUICK_ATTACK].priority > 0); +} + +DOUBLE_BATTLE_TEST("Dazzling, Queenly Majesty and Armor Tail protect the user from priority moves") +{ + u32 species, ability; + + PARAMETRIZE { species = SPECIES_BRUXISH; ability = ABILITY_DAZZLING; } + PARAMETRIZE { species = SPECIES_FARIGIRAF; ability = ABILITY_ARMOR_TAIL; } + PARAMETRIZE { species = SPECIES_TSAREENA; ability = ABILITY_QUEENLY_MAJESTY; } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(species) { Ability(ability); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(playerLeft, MOVE_QUICK_ATTACK, target: opponentLeft); } + } SCENE { + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_QUICK_ATTACK, opponentRight); + ABILITY_POPUP(opponentLeft, ability); + MESSAGE("Wobbuffet cannot use Quick Attack!"); + } +} + +DOUBLE_BATTLE_TEST("Dazzling, Queenly Majesty and Armor Tail protect users partner from priority moves") +{ + u32 species, ability; + + PARAMETRIZE { species = SPECIES_BRUXISH; ability = ABILITY_DAZZLING; } + PARAMETRIZE { species = SPECIES_FARIGIRAF; ability = ABILITY_ARMOR_TAIL; } + PARAMETRIZE { species = SPECIES_TSAREENA; ability = ABILITY_QUEENLY_MAJESTY; } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(species) { Ability(ability); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(playerLeft, MOVE_QUICK_ATTACK, target: opponentRight); } + } SCENE { + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_QUICK_ATTACK, opponentRight); + ABILITY_POPUP(opponentLeft, ability); + MESSAGE("Wobbuffet cannot use Quick Attack!"); + } +} diff --git a/test/battle/ability/good_as_gold.c b/test/battle/ability/good_as_gold.c new file mode 100644 index 000000000000..596bb6c4c7b7 --- /dev/null +++ b/test/battle/ability/good_as_gold.c @@ -0,0 +1,70 @@ +#include "global.h" +#include "test/battle.h" + + +SINGLE_BATTLE_TEST("Good as Gold protects from status moves") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_TOXIC].category == DAMAGE_CATEGORY_STATUS); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_GHOLDENGO) { Ability(ABILITY_GOOD_AS_GOLD); } + } WHEN { + TURN { MOVE(player, MOVE_TOXIC); } + } SCENE { + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_TOXIC, player); + ABILITY_POPUP(opponent, ABILITY_GOOD_AS_GOLD); + MESSAGE("It doesn't affect Foe Gholdengo…"); + } +} + +SINGLE_BATTLE_TEST("Good as Gold doesn't protect the user from it's own moves") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_NASTY_PLOT].category == DAMAGE_CATEGORY_STATUS); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_GHOLDENGO) { Ability(ABILITY_GOOD_AS_GOLD); } + } WHEN { + TURN { MOVE(opponent, MOVE_NASTY_PLOT); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_NASTY_PLOT, opponent); + NONE_OF { + ABILITY_POPUP(opponent, ABILITY_GOOD_AS_GOLD); + MESSAGE("It doesn't affect Foe Gholdengo…"); + } + } +} + +SINGLE_BATTLE_TEST("Good as Gold doesn't protect from moves that target the field") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_STEALTH_ROCK].category == DAMAGE_CATEGORY_STATUS); + ASSUME(gMovesInfo[MOVE_STEALTH_ROCK].target == MOVE_TARGET_OPPONENTS_FIELD); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_GHOLDENGO) { Ability(ABILITY_GOOD_AS_GOLD); } + } WHEN { + TURN { MOVE(player, MOVE_STEALTH_ROCK); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_STEALTH_ROCK, player); + NONE_OF { + ABILITY_POPUP(opponent, ABILITY_GOOD_AS_GOLD); + MESSAGE("It doesn't affect Foe Gholdengo…"); + } + } +} + +DOUBLE_BATTLE_TEST("Good as Gold protects from partner's status moves") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_HELPING_HAND].category == DAMAGE_CATEGORY_STATUS); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_GHOLDENGO) { Ability(ABILITY_GOOD_AS_GOLD); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponentRight, MOVE_HELPING_HAND); } + } SCENE { + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_HELPING_HAND, opponentRight); + ABILITY_POPUP(opponentLeft, ABILITY_GOOD_AS_GOLD); + MESSAGE("It doesn't affect Foe Gholdengo…"); + } +} From 84a7fd86d69fdddde8b2bf7eec6d174899e5be68 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Wed, 29 May 2024 09:56:59 +0200 Subject: [PATCH 5/8] #4635 follow up. Missing test comments (#4647) --- test/battle/ability/armor_tail.c | 4 ++++ test/battle/ability/queenly_majesty.c | 4 ++++ 2 files changed, 8 insertions(+) create mode 100644 test/battle/ability/armor_tail.c create mode 100644 test/battle/ability/queenly_majesty.c diff --git a/test/battle/ability/armor_tail.c b/test/battle/ability/armor_tail.c new file mode 100644 index 000000000000..ac2f7dbfcfb4 --- /dev/null +++ b/test/battle/ability/armor_tail.c @@ -0,0 +1,4 @@ +#include "global.h" +#include "test/battle.h" + +// Tests for Armor Tail are handled in test/battle/ability/dazzling.c diff --git a/test/battle/ability/queenly_majesty.c b/test/battle/ability/queenly_majesty.c new file mode 100644 index 000000000000..fcee95f6fa43 --- /dev/null +++ b/test/battle/ability/queenly_majesty.c @@ -0,0 +1,4 @@ +#include "global.h" +#include "test/battle.h" + +// Tests for Queenly Majesty are handled in test/battle/ability/dazzling.c From 1a59adcea808d091ce351db0876c10bc6b9471d1 Mon Sep 17 00:00:00 2001 From: PhallenTree <168426989+PhallenTree@users.noreply.github.com> Date: Thu, 30 May 2024 12:53:48 +0100 Subject: [PATCH 6/8] Fixes Burning Bulwark incorrect message and Grassy Glide granting priority to Max Move (#4659) * Fixes Burning Bulwark burning message and normalizes similar messages * Fixes Grassy Glide granting priority to Max Move used from that slot * Missed freeze message --- src/battle_main.c | 2 +- src/battle_message.c | 8 ++++---- src/battle_util.c | 2 ++ 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/battle_main.c b/src/battle_main.c index 0332214b9db3..3bf3a3e63809 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -5050,7 +5050,7 @@ s8 GetMovePriority(u32 battler, u16 move) gProtectStructs[battler].pranksterElevated = 1; priority++; } - else if (gMovesInfo[move].effect == EFFECT_GRASSY_GLIDE && gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN && IsBattlerGrounded(battler)) + else if (gMovesInfo[move].effect == EFFECT_GRASSY_GLIDE && gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN && IsBattlerGrounded(battler) && !IsDynamaxed(battler) && !(gBattleStruct->dynamax.toDynamax & gBitTable[battler])) { priority++; } diff --git a/src/battle_message.c b/src/battle_message.c index 0da0cba0e712..eecec7d2a4e2 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -91,7 +91,7 @@ static const u8 sText_CantEscape2[] = _("Can't escape!\p"); static const u8 sText_AttackerCantEscape[] = _("{B_ATK_NAME_WITH_PREFIX} can't escape!"); static const u8 sText_HitXTimes[] = _("Hit {B_BUFF1} time(s)!"); static const u8 sText_PkmnFellAsleep[] = _("{B_EFF_NAME_WITH_PREFIX}\nfell asleep!"); -static const u8 sText_PkmnMadeSleep[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX}'s {B_SCR_ACTIVE_ABILITY}\nmade {B_EFF_NAME_WITH_PREFIX} sleep!"); +static const u8 sText_PkmnMadeSleep[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX}'s {B_BUFF1}\nmade {B_EFF_NAME_WITH_PREFIX} sleep!"); static const u8 sText_PkmnAlreadyAsleep[] = _("{B_DEF_NAME_WITH_PREFIX} is\nalready asleep!"); static const u8 sText_PkmnAlreadyAsleep2[] = _("{B_ATK_NAME_WITH_PREFIX} is\nalready asleep!"); static const u8 sText_PkmnWasntAffected[] = _("{B_DEF_NAME_WITH_PREFIX}\nwasn't affected!"); @@ -103,12 +103,12 @@ static const u8 sText_PkmnBadlyPoisoned[] = _("{B_EFF_NAME_WITH_PREFIX} is badly static const u8 sText_PkmnEnergyDrained[] = _("{B_DEF_NAME_WITH_PREFIX} had its\nenergy drained!"); static const u8 sText_PkmnWasBurned[] = _("{B_EFF_NAME_WITH_PREFIX} was burned!"); static const u8 sText_PkmnGotFrostbite[] = _("{B_EFF_NAME_WITH_PREFIX} got frostbite!"); -static const u8 sText_PkmnBurnedBy[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX}'s {B_SCR_ACTIVE_ABILITY}\nburned {B_EFF_NAME_WITH_PREFIX}!"); +static const u8 sText_PkmnBurnedBy[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX}'s {B_BUFF1}\nburned {B_EFF_NAME_WITH_PREFIX}!"); static const u8 sText_PkmnHurtByBurn[] = _("{B_ATK_NAME_WITH_PREFIX} is hurt\nby its burn!"); static const u8 sText_PkmnHurtByFrostbite[] = _("{B_ATK_NAME_WITH_PREFIX} is hurt\nby its frostbite!"); static const u8 sText_PkmnAlreadyHasBurn[] = _("{B_DEF_NAME_WITH_PREFIX} already\nhas a burn."); static const u8 sText_PkmnWasFrozen[] = _("{B_EFF_NAME_WITH_PREFIX} was\nfrozen solid!"); -static const u8 sText_PkmnFrozenBy[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX}'s {B_SCR_ACTIVE_ABILITY}\nfroze {B_EFF_NAME_WITH_PREFIX} solid!"); +static const u8 sText_PkmnFrozenBy[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX}'s {B_BUFF1}\nfroze {B_EFF_NAME_WITH_PREFIX} solid!"); static const u8 sText_PkmnIsFrozen[] = _("{B_ATK_NAME_WITH_PREFIX} is\nfrozen solid!"); static const u8 sText_PkmnWasDefrosted[] = _("{B_DEF_NAME_WITH_PREFIX} was\ndefrosted!"); static const u8 sText_PkmnWasDefrosted2[] = _("{B_ATK_NAME_WITH_PREFIX} was\ndefrosted!"); @@ -117,7 +117,7 @@ static const u8 sText_PkmnFrostbiteHealed[] = _("{B_DEF_NAME_WITH_PREFIX}'s\nfro static const u8 sText_PkmnFrostbiteHealed2[] = _("{B_ATK_NAME_WITH_PREFIX}'s\nfrostbite was healed!"); static const u8 sText_PkmnFrostbiteHealedBy[] = _("{B_ATK_NAME_WITH_PREFIX}'s {B_CURRENT_MOVE}\nhealed its frostbite!"); static const u8 sText_PkmnWasParalyzed[] = _("{B_EFF_NAME_WITH_PREFIX} is paralyzed!\nIt may be unable to move!"); -static const u8 sText_PkmnWasParalyzedBy[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX}'s {B_SCR_ACTIVE_ABILITY}\nparalyzed {B_EFF_NAME_WITH_PREFIX}!\lIt may be unable to move!"); +static const u8 sText_PkmnWasParalyzedBy[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX}'s {B_BUFF1}\nparalyzed {B_EFF_NAME_WITH_PREFIX}!\lIt may be unable to move!"); static const u8 sText_PkmnIsParalyzed[] = _("{B_ATK_NAME_WITH_PREFIX} is paralyzed!\nIt can't move!"); static const u8 sText_PkmnIsAlreadyParalyzed[] = _("{B_DEF_NAME_WITH_PREFIX} is\nalready paralyzed!"); static const u8 sText_PkmnHealedParalysis[] = _("{B_DEF_NAME_WITH_PREFIX} was\nhealed of paralysis!"); diff --git a/src/battle_util.c b/src/battle_util.c index 11cd2544d7ea..905f78e01e35 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -5507,6 +5507,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 && RandomWeighted(RNG_STATIC, 2, 1)) { gBattleScripting.moveEffect = MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_PARALYSIS; + PREPARE_ABILITY_BUFFER(gBattleTextBuff1, gLastUsedAbility); BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_AbilityStatusEffect; gHitMarker |= HITMARKER_STATUS_ABILITY_EFFECT; @@ -5524,6 +5525,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 && RandomWeighted(RNG_FLAME_BODY, 2, 1)) { gBattleScripting.moveEffect = MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_BURN; + PREPARE_ABILITY_BUFFER(gBattleTextBuff1, gLastUsedAbility); BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_AbilityStatusEffect; gHitMarker |= HITMARKER_STATUS_ABILITY_EFFECT; From 317cf2e9336feaa133e794b230803468be6208b5 Mon Sep 17 00:00:00 2001 From: AgustinGDLV <103095241+AgustinGDLV@users.noreply.github.com> Date: Thu, 30 May 2024 13:15:26 -0700 Subject: [PATCH 7/8] updated Disguise to be a breakable ability (#4666) --- src/data/abilities.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/data/abilities.h b/src/data/abilities.h index a65d4e8c2499..8b81057b4ff4 100644 --- a/src/data/abilities.h +++ b/src/data/abilities.h @@ -1602,6 +1602,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] = .name = _("Disguise"), .description = COMPOUND_STRING("Decoy protects it once."), .aiRating = 8, + .breakable = TRUE, .cantBeCopied = TRUE, .cantBeSwapped = TRUE, .cantBeTraced = TRUE, From 13d4d29e1e3c32eb2d88c0c59c3953851095fcbd Mon Sep 17 00:00:00 2001 From: sneed <56992013+Sneed69@users.noreply.github.com> Date: Fri, 31 May 2024 13:19:07 +0300 Subject: [PATCH 8/8] Move SOLAR_POWER_HP_DROP label (#4675) --- src/battle_util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/battle_util.c b/src/battle_util.c index 905f78e01e35..08deb4fb5900 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -4903,10 +4903,10 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 BattleScriptPushCursorAndCallback(BattleScript_BadDreamsActivates); effect++; break; - SOLAR_POWER_HP_DROP: case ABILITY_SOLAR_POWER: if (IsBattlerWeatherAffected(battler, B_WEATHER_SUN)) { + SOLAR_POWER_HP_DROP: BattleScriptPushCursorAndCallback(BattleScript_SolarPowerActivates); gBattleMoveDamage = GetNonDynamaxMaxHP(battler) / 8; if (gBattleMoveDamage == 0)