diff --git a/include/battle_ai_util.h b/include/battle_ai_util.h index c74cad2e780e..afcc04768cf5 100644 --- a/include/battle_ai_util.h +++ b/include/battle_ai_util.h @@ -71,7 +71,7 @@ u32 GetBattlerSideSpeedAverage(u32 battler); bool32 ShouldAbsorb(u32 battlerAtk, u32 battlerDef, u32 move, s32 damage); bool32 ShouldRecover(u32 battlerAtk, u32 battlerDef, u32 move, u32 healPercent); bool32 ShouldSetScreen(u32 battlerAtk, u32 battlerDef, u32 moveEffect); -enum AIPivot ShouldPivot(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 moveIndex); +enum AIPivot ShouldPivot(u32 battlerAtk, u32 battlerDef, u32 atkAbility, u32 defAbility, u32 move, u32 moveIndex); bool32 IsRecycleEncouragedItem(u32 item); bool32 ShouldRestoreHpBerry(u32 battlerAtk, u32 item); bool32 IsStatBoostingBerry(u32 item); diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 3c031de092c7..6f8e83d76307 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -3524,7 +3524,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move) case EFFECT_CHILLY_RECEPTION: if (!IsDoubleBattle()) { - switch (ShouldPivot(battlerAtk, battlerDef, aiData->abilities[battlerDef], move, movesetIndex)) + switch (ShouldPivot(battlerAtk, battlerDef, aiData->abilities[battlerAtk], aiData->abilities[battlerDef], move, movesetIndex)) { case DONT_PIVOT: ADJUST_SCORE(-10); // technically should go in CheckBadMove, but this is easier/less computationally demanding diff --git a/src/battle_ai_switch_items.c b/src/battle_ai_switch_items.c index 47f5cae7408e..e349dc6353a0 100644 --- a/src/battle_ai_switch_items.c +++ b/src/battle_ai_switch_items.c @@ -606,6 +606,13 @@ static bool32 ShouldSwitchIfAbilityBenefit(u32 battler) return FALSE; + case ABILITY_ZERO_TO_HERO: + //Zero to Hero only works on Palafin-Zero + if (gBattleMons[battler].species == SPECIES_PALAFIN_ZERO + && AI_DATA->mostSuitableMonId[battler] != PARTY_SIZE) + break; + + return FALSE; default: return FALSE; } diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index c033061dc9e6..1c2abbd66b9d 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -779,7 +779,7 @@ static bool32 AI_IsMoveEffectInPlus(u32 battlerAtk, u32 battlerDef, u32 move, s3 switch (gMovesInfo[move].effect) { case EFFECT_HIT_ESCAPE: - if (CountUsablePartyMons(battlerAtk) != 0 && ShouldPivot(battlerAtk, battlerDef, abilityDef, move, AI_THINKING_STRUCT->movesetIndex)) + if (CountUsablePartyMons(battlerAtk) != 0 && ShouldPivot(battlerAtk, battlerDef, abilityAtk, abilityDef, move, AI_THINKING_STRUCT->movesetIndex)) return TRUE; break; case EFFECT_FELL_STINGER: @@ -2692,13 +2692,19 @@ static bool32 PartyBattlerShouldAvoidHazards(u32 currBattler, u32 switchBattler) return FALSE; } -enum AIPivot ShouldPivot(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 moveIndex) +enum AIPivot ShouldPivot(u32 battlerAtk, u32 battlerDef, u32 atkAbility, u32 defAbility, u32 move, u32 moveIndex) { bool32 hasStatBoost = AnyUsefulStatIsRaised(battlerAtk) || gBattleMons[battlerDef].statStages[STAT_EVASION] >= 9; //Significant boost in evasion for any class u32 battlerToSwitch; battlerToSwitch = gBattleStruct->AI_monToSwitchIntoId[battlerAtk]; + // Palafin has a unique playstyle where it should pivot as soon as possible no matter what in order to transform. It can ignore any other checks to achieve this. + if (gBattleMons[battlerAtk].species == SPECIES_PALAFIN_ZERO + && atkAbility == ABILITY_ZERO_TO_HERO + && CountUsablePartyMons(battlerAtk) != 0) + return SHOULD_PIVOT; + if (PartyBattlerShouldAvoidHazards(battlerAtk, battlerToSwitch)) return DONT_PIVOT; @@ -4086,7 +4092,7 @@ void IncreaseSubstituteMoveScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 * } else if (gMovesInfo[move].effect == EFFECT_SHED_TAIL) // Shed Tail specific { - if ((ShouldPivot(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], move, AI_THINKING_STRUCT->movesetIndex)) + if ((ShouldPivot(battlerAtk, battlerDef, AI_DATA->abilities[battlerAtk], AI_DATA->abilities[battlerDef], move, AI_THINKING_STRUCT->movesetIndex)) && (HasAnyKnownMove(battlerDef) && (GetBestDmgFromBattler(battlerDef, battlerAtk) < gBattleMons[battlerAtk].maxHP / 2))) ADJUST_SCORE_PTR(BEST_EFFECT); } diff --git a/test/battle/ai/ai_switching.c b/test/battle/ai/ai_switching.c index 7f2368261d17..5684c3fbf2fb 100644 --- a/test/battle/ai/ai_switching.c +++ b/test/battle/ai/ai_switching.c @@ -909,3 +909,15 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: AI correctly handles abilities TURN { MOVE(player, MOVE_WATER_GUN); EXPECT_MOVE(opponent, MOVE_ABSORB); } } } + +AI_SINGLE_BATTLE_TEST("Switch AI: AI will switch out if Palafin-Zero isn't transformed yet") +{ + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); + PLAYER(SPECIES_FINIZEN); + OPPONENT(SPECIES_PALAFIN_ZERO); + OPPONENT(SPECIES_FINIZEN); + } WHEN { + TURN { MOVE(player, MOVE_CELEBRATE); EXPECT_SWITCH(opponent, 1); } + } +}