diff --git a/include/battle_util.h b/include/battle_util.h index b7d572c79..92d798f7b 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -81,6 +81,7 @@ u32 IsAbilityOnSide(u32 battlerId, u32 ability); u32 IsAbilityOnOpposingSide(u32 battlerId, u32 ability); u32 IsAbilityOnField(u32 ability); u32 IsAbilityOnFieldExcept(u32 battlerId, u32 ability); +u32 IsAbilityPreventingEscape(u32 battlerId); void BattleScriptExecute(const u8* BS_ptr); void BattleScriptPushCursorAndCallback(const u8* BS_ptr); u8 ItemBattleEffects(u8 caseID, u8 battlerId, bool8 moveTurn); diff --git a/include/constants/battle_config.h b/include/constants/battle_config.h index 49a86c648..a5a9c8206 100644 --- a/include/constants/battle_config.h +++ b/include/constants/battle_config.h @@ -62,6 +62,7 @@ #define B_ABILITY_WEATHER GEN_6 // Up to gen5 - weather induced by abilities such as Drought or Drizzle lasted till the battle's end or weather change by a move. From Gen6 onwards, weather caused by abilities lasts the same amount of turns as induced from a move. #define B_GALE_WINGS GEN_6 // Gen7 requires full hp. #define B_STANCE_CHANGE_FAIL GEN_7 // In Gen7, Aegislash's form change does not happen, if the pokemon cannot use a move, because of confusion, paralysis, etc. In gen6, the form change occurs despite not being able to move. +#define B_GHOSTS_ESCAPE GEN_6 // From Gen6 onwards, ghosts can escape even when blocked by abilities such as Shadow Tag. // Other #define B_FAST_INTRO TRUE // If set to TRUE, battle intro texts print at the same time as animation of a pokemon, as opposing to waiting for the animation to end. diff --git a/src/battle_ai_script_commands.c b/src/battle_ai_script_commands.c index 4b3b00803..adcd0f234 100644 --- a/src/battle_ai_script_commands.c +++ b/src/battle_ai_script_commands.c @@ -494,41 +494,44 @@ static u8 ChooseMoveOrAction_Singles(void) return AI_CHOICE_WATCH; gActiveBattler = sBattler_AI; - // Consider switching if all moves are worthless to use. - if (AI_THINKING_STRUCT->aiFlags & (AI_SCRIPT_CHECK_VIABILITY | AI_SCRIPT_CHECK_BAD_MOVE | AI_SCRIPT_TRY_TO_FAINT | AI_SCRIPT_PREFER_BATON_PASS) - && CountUsablePartyMons(sBattler_AI) >= 1 - && GetTotalBaseStat(gBattleMons[sBattler_AI].species) >= 310 // Mon is not weak. - && gBattleMons[sBattler_AI].hp >= gBattleMons[sBattler_AI].maxHP / 2 - && !(gBattleTypeFlags & BATTLE_TYPE_PALACE)) + // If can switch. + if (CountUsablePartyMons(sBattler_AI) >= 1 + && !IsAbilityPreventingEscape(sBattler_AI) + && !(gBattleMons[gActiveBattler].status2 & (STATUS2_WRAPPED | STATUS2_ESCAPE_PREVENTION)) + && !(gStatuses3[gActiveBattler] & STATUS3_ROOTED) + && !(gBattleTypeFlags & (BATTLE_TYPE_ARENA | BATTLE_TYPE_PALACE)) + && AI_THINKING_STRUCT->aiFlags & (AI_SCRIPT_CHECK_VIABILITY | AI_SCRIPT_CHECK_BAD_MOVE | AI_SCRIPT_TRY_TO_FAINT | AI_SCRIPT_PREFER_BATON_PASS)) { - s32 cap = AI_THINKING_STRUCT->aiFlags & (AI_SCRIPT_CHECK_VIABILITY) ? 95 : 93; - - for (i = 0; i < MAX_MON_MOVES; i++) + // Consider switching if all moves are worthless to use. + if (GetTotalBaseStat(gBattleMons[sBattler_AI].species) >= 310 // Mon is not weak. + && gBattleMons[sBattler_AI].hp >= gBattleMons[sBattler_AI].maxHP / 2) { - if (AI_THINKING_STRUCT->score[i] > cap) - break; + s32 cap = AI_THINKING_STRUCT->aiFlags & (AI_SCRIPT_CHECK_VIABILITY) ? 95 : 93; + for (i = 0; i < MAX_MON_MOVES; i++) + { + if (AI_THINKING_STRUCT->score[i] > cap) + break; + } + + if (i == MAX_MON_MOVES && GetMostSuitableMonToSwitchInto() != PARTY_SIZE) + { + AI_THINKING_STRUCT->switchMon = TRUE; + return AI_CHOICE_SWITCH; + } } - if (i == MAX_MON_MOVES && GetMostSuitableMonToSwitchInto() != PARTY_SIZE) + // Consider switching if your mon with truant is bodied by Protect spam. + // Or is using a double turn semi invulnerable move(such as Fly) and is faster. + if (GetBattlerAbility(sBattler_AI) == ABILITY_TRUANT + && IsTruantMonVulnerable(sBattler_AI, gBattlerTarget) + && gDisableStructs[sBattler_AI].truantCounter + && gBattleMons[sBattler_AI].hp >= gBattleMons[sBattler_AI].maxHP / 2) { - AI_THINKING_STRUCT->switchMon = TRUE; - return AI_CHOICE_SWITCH; - } - } - - // Consider switching if your mon with truant is bodied by Protect spam. - // Or is using a double turn semi invulnerable move(such as Fly) and is faster. - if (GetBattlerAbility(sBattler_AI) == ABILITY_TRUANT - && IsTruantMonVulnerable(sBattler_AI, gBattlerTarget) - && gDisableStructs[sBattler_AI].truantCounter - && AI_THINKING_STRUCT->aiFlags & (AI_SCRIPT_CHECK_VIABILITY) - && gBattleMons[sBattler_AI].hp >= gBattleMons[sBattler_AI].maxHP / 2 - && CountUsablePartyMons(sBattler_AI) >= 1) - { - if (GetMostSuitableMonToSwitchInto() != PARTY_SIZE) - { - AI_THINKING_STRUCT->switchMon = TRUE; - return AI_CHOICE_SWITCH; + if (GetMostSuitableMonToSwitchInto() != PARTY_SIZE) + { + AI_THINKING_STRUCT->switchMon = TRUE; + return AI_CHOICE_SWITCH; + } } } diff --git a/src/battle_ai_switch_items.c b/src/battle_ai_switch_items.c index 77b1685d4..2eaf0e2e3 100644 --- a/src/battle_ai_switch_items.c +++ b/src/battle_ai_switch_items.c @@ -420,44 +420,34 @@ static bool8 FindMonWithFlagsAndSuperEffective(u16 flags, u8 moduloPercent) static bool8 ShouldSwitch(void) { u8 battlerIn1, battlerIn2; - u8 *activeBattlerPtr; // Needed to match. s32 firstId; s32 lastId; // + 1 struct Pokemon *party; s32 i; s32 availableToSwitch; - if (gBattleMons[*(activeBattlerPtr = &gActiveBattler)].status2 & (STATUS2_WRAPPED | STATUS2_ESCAPE_PREVENTION)) + if (gBattleMons[gActiveBattler].status2 & (STATUS2_WRAPPED | STATUS2_ESCAPE_PREVENTION)) return FALSE; if (gStatuses3[gActiveBattler] & STATUS3_ROOTED) return FALSE; - if (IsAbilityOnOpposingSide(gActiveBattler, ABILITY_SHADOW_TAG)) + if (IsAbilityPreventingEscape(gActiveBattler)) return FALSE; - if (IsAbilityOnOpposingSide(gActiveBattler, ABILITY_ARENA_TRAP)) // Misses the flying type and Levitate check. - return FALSE; - if (IsAbilityOnField(ABILITY_MAGNET_PULL)) - { - if (gBattleMons[gActiveBattler].type1 == TYPE_STEEL) - return FALSE; - if (gBattleMons[gActiveBattler].type2 == TYPE_STEEL) - return FALSE; - } if (gBattleTypeFlags & BATTLE_TYPE_ARENA) return FALSE; availableToSwitch = 0; if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) { - battlerIn1 = *activeBattlerPtr; - if (gAbsentBattlerFlags & gBitTable[GetBattlerAtPosition(GetBattlerPosition(*activeBattlerPtr) ^ BIT_FLANK)]) - battlerIn2 = *activeBattlerPtr; + battlerIn1 = gActiveBattler; + if (gAbsentBattlerFlags & gBitTable[GetBattlerAtPosition(GetBattlerPosition(gActiveBattler) ^ BIT_FLANK)]) + battlerIn2 = gActiveBattler; else - battlerIn2 = GetBattlerAtPosition(GetBattlerPosition(*activeBattlerPtr) ^ BIT_FLANK); + battlerIn2 = GetBattlerAtPosition(GetBattlerPosition(gActiveBattler) ^ BIT_FLANK); } else { - battlerIn1 = *activeBattlerPtr; - battlerIn2 = *activeBattlerPtr; + battlerIn1 = gActiveBattler; + battlerIn2 = gActiveBattler; } GetAIPartyIndexes(gActiveBattler, &firstId, &lastId); diff --git a/src/battle_main.c b/src/battle_main.c index 9840a56ac..1bd31360d 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -3652,9 +3652,7 @@ void BattleTurnPassed(void) u8 IsRunningFromBattleImpossible(void) { - u8 holdEffect; - u8 side; - s32 i; + u32 holdEffect, i; if (gBattleMons[gActiveBattler].item == ITEM_ENIGMA_BERRY) holdEffect = gEnigmaBerries[gActiveBattler].holdEffect; @@ -3681,37 +3679,14 @@ u8 IsRunningFromBattleImpossible(void) if (gBattleMons[gActiveBattler].ability == ABILITY_RUN_AWAY) return 0; - side = GetBattlerSide(gActiveBattler); - - for (i = 0; i < gBattlersCount; i++) - { - if (side != GetBattlerSide(i) - && gBattleMons[i].ability == ABILITY_SHADOW_TAG) - { - gBattleScripting.battler = i; - gLastUsedAbility = gBattleMons[i].ability; - gBattleCommunication[MULTISTRING_CHOOSER] = 2; - return 2; - } - if (side != GetBattlerSide(i) - && gBattleMons[gActiveBattler].ability != ABILITY_LEVITATE - && !IS_BATTLER_OF_TYPE(gActiveBattler, TYPE_FLYING) - && gBattleMons[i].ability == ABILITY_ARENA_TRAP) - { - gBattleScripting.battler = i; - gLastUsedAbility = gBattleMons[i].ability; - gBattleCommunication[MULTISTRING_CHOOSER] = 2; - return 2; - } - } - i = IsAbilityOnFieldExcept(gActiveBattler, ABILITY_MAGNET_PULL); - if (i != 0 && IS_BATTLER_OF_TYPE(gActiveBattler, TYPE_STEEL)) + if ((i = IsAbilityPreventingEscape(gActiveBattler))) { gBattleScripting.battler = i - 1; gLastUsedAbility = gBattleMons[i - 1].ability; gBattleCommunication[MULTISTRING_CHOOSER] = 2; return 2; } + if ((gBattleMons[gActiveBattler].status2 & (STATUS2_ESCAPE_PREVENTION | STATUS2_WRAPPED)) || (gStatuses3[gActiveBattler] & STATUS3_ROOTED)) { @@ -3895,12 +3870,7 @@ static void HandleTurnActionSelectionState(void) { BtlController_EmitChoosePokemon(0, PARTY_ACTION_CANT_SWITCH, PARTY_SIZE, ABILITY_NONE, gBattleStruct->field_60[gActiveBattler]); } - else if ((i = IsAbilityOnOpposingSide(gActiveBattler, ABILITY_SHADOW_TAG)) - || ((i = IsAbilityOnOpposingSide(gActiveBattler, ABILITY_ARENA_TRAP)) - && !IS_BATTLER_OF_TYPE(gActiveBattler, TYPE_FLYING) - && gBattleMons[gActiveBattler].ability != ABILITY_LEVITATE) - || ((i = IsAbilityOnFieldExcept(gActiveBattler, ABILITY_MAGNET_PULL)) - && IS_BATTLER_OF_TYPE(gActiveBattler, TYPE_STEEL))) + else if ((i = IsAbilityPreventingEscape(gActiveBattler))) { BtlController_EmitChoosePokemon(0, ((i - 1) << 4) | PARTY_ACTION_ABILITY_PREVENTS, PARTY_SIZE, gLastUsedAbility, gBattleStruct->field_60[gActiveBattler]); } diff --git a/src/battle_util.c b/src/battle_util.c index a432da904..cbcca73d7 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -23,6 +23,7 @@ #include "field_weather.h" #include "constants/abilities.h" #include "constants/battle_anim.h" +#include "constants/battle_config.h" #include "constants/battle_move_effects.h" #include "constants/battle_script_commands.h" #include "constants/battle_string_ids.h" @@ -3801,6 +3802,23 @@ u32 IsAbilityOnFieldExcept(u32 battlerId, u32 ability) return 0; } +u32 IsAbilityPreventingEscape(u32 battlerId) +{ + u32 id; + + if (B_GHOSTS_ESCAPE >= GEN_6 && IS_BATTLER_OF_TYPE(battlerId, TYPE_GHOST)) + return 0; + + if ((id = IsAbilityOnOpposingSide(battlerId, ABILITY_SHADOW_TAG)) && gBattleMons[battlerId].ability != ABILITY_SHADOW_TAG) + return id; + if ((id = IsAbilityOnOpposingSide(battlerId, ABILITY_ARENA_TRAP)) && IsBattlerGrounded(battlerId)) + return id; + if ((id = IsAbilityOnOpposingSide(battlerId, ABILITY_MAGNET_PULL)) && IS_BATTLER_OF_TYPE(battlerId, TYPE_STEEL)) + return id; + + return 0; +} + void BattleScriptExecute(const u8 *BS_ptr) { gBattlescriptCurrInstr = BS_ptr; diff --git a/src/pokemon.c b/src/pokemon.c index 6ea0d3337..2c0bd2fc0 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -5510,7 +5510,7 @@ void AdjustFriendship(struct Pokemon *mon, u8 event) if (friendship > 199) friendshipLevel++; - if ((event != FRIENDSHIP_EVENT_WALKING || !(Random() & 1)) + if ((event != FRIENDSHIP_EVENT_WALKING || !(Random() & 1)) && (event != FRIENDSHIP_EVENT_LEAGUE_BATTLE || IS_LEAGUE_BATTLE)) { s8 mod = sFriendshipEventModifiers[event][friendshipLevel];