diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 21e7376cb7..70683fc420 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -1960,40 +1960,45 @@ bool32 IsAllyProtectingFromMove(u32 battlerAtk, u32 attackerMove, u32 allyMove) { return FALSE; } - else + + enum ProtectMethod protectMethod = GetMoveProtectMethod(allyMove); + + switch (protectMethod) { - enum ProtectMethod protectMethod = GetMoveProtectMethod(allyMove); - - if (protectMethod == PROTECT_QUICK_GUARD) + case PROTECT_CRAFTY_SHIELD: + if (!IsBattleMoveStatus(attackerMove)) { - u32 priority = GetBattleMovePriority(battlerAtk, gAiLogicData->abilities[battlerAtk], attackerMove); - return (priority > 0); + return FALSE; } - - if (IsBattleMoveStatus(attackerMove)) + else if (GetMoveEffect(attackerMove) == EFFECT_HOLD_HANDS) { - switch (protectMethod) - { - case PROTECT_NORMAL: - case PROTECT_CRAFTY_SHIELD: - case PROTECT_MAX_GUARD: - case PROTECT_WIDE_GUARD: - return TRUE; - - default: - return FALSE; - } + return TRUE; } else { - switch (protectMethod) - { - case PROTECT_CRAFTY_SHIELD: - return FALSE; - default: - return TRUE; - } + u32 moveTarget = GetBattlerMoveTargetType(battlerAtk, attackerMove); + return (GetBattlerSide(battlerAtk) != GetBattlerSide(BATTLE_PARTNER(battlerAtk)) + && moveTarget != MOVE_TARGET_OPPONENTS_FIELD + && moveTarget != MOVE_TARGET_ALL_BATTLERS); } + case PROTECT_WIDE_GUARD: + return IsSpreadMove(GetBattlerMoveTargetType(battlerAtk, attackerMove)); + case PROTECT_NORMAL: + case PROTECT_SPIKY_SHIELD: + case PROTECT_MAX_GUARD: + case PROTECT_BANEFUL_BUNKER: + case PROTECT_BURNING_BULWARK: + return TRUE; + case PROTECT_OBSTRUCT: + case PROTECT_SILK_TRAP: + case PROTECT_KINGS_SHIELD: + return !IsBattleMoveStatus(attackerMove); + case PROTECT_QUICK_GUARD: + return (GetChosenMovePriority(battlerAtk, gAiLogicData->abilities[battlerAtk]) > 0); + case PROTECT_MAT_BLOCK: + return !IsBattleMoveStatus(attackerMove); + default: + return FALSE; } } diff --git a/src/battle_util.c b/src/battle_util.c index b660c174ce..c209573d30 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -6635,6 +6635,26 @@ static inline bool32 IsSideProtected(u32 battler, enum ProtectMethod method) || gProtectStructs[BATTLE_PARTNER(battler)].protected == method; } +static bool32 IsCraftyShieldProtected(u32 battlerAtk, u32 battlerDef, u32 move) +{ + if (!IsBattleMoveStatus(move)) + return FALSE; + + if (!IsSideProtected(battlerDef, PROTECT_CRAFTY_SHIELD)) + return FALSE; + + if (GetMoveEffect(move) == EFFECT_HOLD_HANDS) + return TRUE; + + u32 moveTarget = GetBattlerMoveTargetType(battlerAtk, move); + if (!IsBattlerAlly(battlerAtk, battlerDef) + && moveTarget != MOVE_TARGET_OPPONENTS_FIELD + && moveTarget != MOVE_TARGET_ALL_BATTLERS) + return TRUE; + + return FALSE; +} + bool32 IsBattlerProtected(u32 battlerAtk, u32 battlerDef, u32 move) { if (gProtectStructs[battlerDef].protected == PROTECT_NONE @@ -6652,9 +6672,7 @@ bool32 IsBattlerProtected(u32 battlerAtk, u32 battlerDef, u32 move) bool32 isProtected = FALSE; - if (IsSideProtected(battlerDef, PROTECT_CRAFTY_SHIELD) - && IsBattleMoveStatus(move) - && GetMoveEffect(move) != EFFECT_COACHING) + if (IsCraftyShieldProtected(battlerAtk, battlerDef, move)) isProtected = TRUE; else if (MoveIgnoresProtect(move)) isProtected = FALSE; diff --git a/test/battle/move_effect/protect.c b/test/battle/move_effect/protect.c index 85546f2064..0f486c6405 100644 --- a/test/battle/move_effect/protect.c +++ b/test/battle/move_effect/protect.c @@ -528,7 +528,7 @@ DOUBLE_BATTLE_TEST("Protect: Quick Guard can not fail on consecutive turns (Gen6 } } -DOUBLE_BATTLE_TEST("Protect: Crafty Shield protects self and ally from status moves") +DOUBLE_BATTLE_TEST("Crafty Shield protects self and ally from opposing status moves") { u16 move = MOVE_NONE; struct BattlePokemon *targetOpponent = NULL; @@ -569,6 +569,72 @@ DOUBLE_BATTLE_TEST("Protect: Crafty Shield protects self and ally from status mo } } +DOUBLE_BATTLE_TEST("Crafty Shield does not protect against status moves used on the user's side") +{ + u32 move; + + PARAMETRIZE { move = MOVE_AROMATHERAPY; } + PARAMETRIZE { move = MOVE_ACUPRESSURE; } + + GIVEN { + ASSUME(GetMoveEffect(MOVE_AROMATHERAPY) == EFFECT_HEAL_BELL); + ASSUME(GetMoveEffect(MOVE_ACUPRESSURE) == EFFECT_ACUPRESSURE); + PLAYER(SPECIES_WOBBUFFET) { Speed(5); } + PLAYER(SPECIES_WOBBUFFET) { Speed(5); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(10); } + OPPONENT(SPECIES_WYNAUT) { Speed(5); Status1(STATUS1_BURN); } + } WHEN { + TURN { + MOVE(opponentLeft, MOVE_CRAFTY_SHIELD); + if (move == MOVE_ACUPRESSURE) + MOVE(opponentRight, move, target: opponentLeft); + else + MOVE(opponentRight, move); + } + TURN {} + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CRAFTY_SHIELD, opponentLeft); + if (move == MOVE_ACUPRESSURE) { + ANIMATION(ANIM_TYPE_MOVE, MOVE_ACUPRESSURE, opponentRight); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft); + } else { + ANIMATION(ANIM_TYPE_MOVE, MOVE_AROMATHERAPY, opponentRight); + STATUS_ICON(opponentRight, none: TRUE); + } + } +} + +DOUBLE_BATTLE_TEST("Crafty Shield does not protect against entry hazard moves") +{ + u32 move; + + PARAMETRIZE { move = MOVE_SPIKES; } + PARAMETRIZE { move = MOVE_STEALTH_ROCK; } + PARAMETRIZE { move = MOVE_TOXIC_SPIKES; } + PARAMETRIZE { move = MOVE_STICKY_WEB; } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponentLeft, MOVE_CRAFTY_SHIELD); MOVE(playerLeft, move, target: opponentLeft); } + TURN {} + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CRAFTY_SHIELD, opponentLeft); + if (move == MOVE_SPIKES) { + MESSAGE("Spikes were scattered on the ground all around the opposing team!"); + } else if (move == MOVE_TOXIC_SPIKES) { + MESSAGE("Poison spikes were scattered on the ground all around the opposing team!"); + } else if (move == MOVE_STEALTH_ROCK) { + MESSAGE("Pointed stones float in the air around the opposing team!"); + } else { + MESSAGE("A sticky web has been laid out on the ground around the opposing team!"); + } + } +} + SINGLE_BATTLE_TEST("Protect: Protect does not block Confide or Decorate") { u32 move; @@ -618,6 +684,11 @@ DOUBLE_BATTLE_TEST("Crafty Shield protects self and ally from Confide and Decora DOUBLE_BATTLE_TEST("Crafty Shield does not protect against moves that target all battlers") { + u32 move; + + PARAMETRIZE { move = MOVE_FLOWER_SHIELD; } + PARAMETRIZE { move = MOVE_PERISH_SONG; } + GIVEN { ASSUME(GetSpeciesType(SPECIES_TANGELA, 0) == TYPE_GRASS); ASSUME(GetSpeciesType(SPECIES_TANGROWTH, 0) == TYPE_GRASS); @@ -628,21 +699,29 @@ DOUBLE_BATTLE_TEST("Crafty Shield does not protect against moves that target all OPPONENT(SPECIES_SUNKERN); OPPONENT(SPECIES_SUNFLORA); } WHEN { - TURN { MOVE(opponentLeft, MOVE_CRAFTY_SHIELD); MOVE(opponentRight, MOVE_CELEBRATE); MOVE(playerLeft, MOVE_FLOWER_SHIELD); MOVE(playerRight, MOVE_CELEBRATE); } + TURN { MOVE(opponentLeft, MOVE_CRAFTY_SHIELD); MOVE(opponentRight, MOVE_CELEBRATE); MOVE(playerLeft, move); MOVE(playerRight, MOVE_CELEBRATE); } } SCENE { - MESSAGE("Tangela used Flower Shield!"); - ANIMATION(ANIM_TYPE_MOVE, MOVE_FLOWER_SHIELD, playerLeft); - ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft); - MESSAGE("Tangela's Defense rose!"); - ANIMATION(ANIM_TYPE_MOVE, MOVE_FLOWER_SHIELD, playerLeft); - ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft); - MESSAGE("The opposing Sunkern's Defense rose!"); - ANIMATION(ANIM_TYPE_MOVE, MOVE_FLOWER_SHIELD, playerLeft); - ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight); - MESSAGE("Tangrowth's Defense rose!"); - ANIMATION(ANIM_TYPE_MOVE, MOVE_FLOWER_SHIELD, playerLeft); - ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight); - MESSAGE("The opposing Sunflora's Defense rose!"); + if (move == MOVE_FLOWER_SHIELD) { + MESSAGE("Tangela used Flower Shield!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_FLOWER_SHIELD, playerLeft); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft); + MESSAGE("Tangela's Defense rose!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_FLOWER_SHIELD, playerLeft); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft); + MESSAGE("The opposing Sunkern's Defense rose!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_FLOWER_SHIELD, playerLeft); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight); + MESSAGE("Tangrowth's Defense rose!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_FLOWER_SHIELD, playerLeft); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight); + MESSAGE("The opposing Sunflora's Defense rose!"); + } else { + ANIMATION(ANIM_TYPE_MOVE, MOVE_PERISH_SONG, playerLeft); + NONE_OF { + MESSAGE("The opposing Sunkern protected itself!"); + MESSAGE("The opposing Sunflora protected itself!"); + } + } } }