Fix AI trying to switch when it can't, also ghosts can escape

This commit is contained in:
DizzyEggg 2020-02-08 14:20:02 +01:00
parent 97eaf8b559
commit 14bc2f65f3
7 changed files with 66 additions and 83 deletions

View File

@ -81,6 +81,7 @@ u32 IsAbilityOnSide(u32 battlerId, u32 ability);
u32 IsAbilityOnOpposingSide(u32 battlerId, u32 ability); u32 IsAbilityOnOpposingSide(u32 battlerId, u32 ability);
u32 IsAbilityOnField(u32 ability); u32 IsAbilityOnField(u32 ability);
u32 IsAbilityOnFieldExcept(u32 battlerId, u32 ability); u32 IsAbilityOnFieldExcept(u32 battlerId, u32 ability);
u32 IsAbilityPreventingEscape(u32 battlerId);
void BattleScriptExecute(const u8* BS_ptr); void BattleScriptExecute(const u8* BS_ptr);
void BattleScriptPushCursorAndCallback(const u8* BS_ptr); void BattleScriptPushCursorAndCallback(const u8* BS_ptr);
u8 ItemBattleEffects(u8 caseID, u8 battlerId, bool8 moveTurn); u8 ItemBattleEffects(u8 caseID, u8 battlerId, bool8 moveTurn);

View File

@ -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_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_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_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 // 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. #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.

View File

@ -494,41 +494,44 @@ static u8 ChooseMoveOrAction_Singles(void)
return AI_CHOICE_WATCH; return AI_CHOICE_WATCH;
gActiveBattler = sBattler_AI; gActiveBattler = sBattler_AI;
// Consider switching if all moves are worthless to use. // If can switch.
if (AI_THINKING_STRUCT->aiFlags & (AI_SCRIPT_CHECK_VIABILITY | AI_SCRIPT_CHECK_BAD_MOVE | AI_SCRIPT_TRY_TO_FAINT | AI_SCRIPT_PREFER_BATON_PASS) if (CountUsablePartyMons(sBattler_AI) >= 1
&& CountUsablePartyMons(sBattler_AI) >= 1 && !IsAbilityPreventingEscape(sBattler_AI)
&& GetTotalBaseStat(gBattleMons[sBattler_AI].species) >= 310 // Mon is not weak. && !(gBattleMons[gActiveBattler].status2 & (STATUS2_WRAPPED | STATUS2_ESCAPE_PREVENTION))
&& gBattleMons[sBattler_AI].hp >= gBattleMons[sBattler_AI].maxHP / 2 && !(gStatuses3[gActiveBattler] & STATUS3_ROOTED)
&& !(gBattleTypeFlags & BATTLE_TYPE_PALACE)) && !(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; // Consider switching if all moves are worthless to use.
if (GetTotalBaseStat(gBattleMons[sBattler_AI].species) >= 310 // Mon is not weak.
for (i = 0; i < MAX_MON_MOVES; i++) && gBattleMons[sBattler_AI].hp >= gBattleMons[sBattler_AI].maxHP / 2)
{ {
if (AI_THINKING_STRUCT->score[i] > cap) s32 cap = AI_THINKING_STRUCT->aiFlags & (AI_SCRIPT_CHECK_VIABILITY) ? 95 : 93;
break; 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; if (GetMostSuitableMonToSwitchInto() != PARTY_SIZE)
return AI_CHOICE_SWITCH; {
} 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;
} }
} }

View File

@ -420,44 +420,34 @@ static bool8 FindMonWithFlagsAndSuperEffective(u16 flags, u8 moduloPercent)
static bool8 ShouldSwitch(void) static bool8 ShouldSwitch(void)
{ {
u8 battlerIn1, battlerIn2; u8 battlerIn1, battlerIn2;
u8 *activeBattlerPtr; // Needed to match.
s32 firstId; s32 firstId;
s32 lastId; // + 1 s32 lastId; // + 1
struct Pokemon *party; struct Pokemon *party;
s32 i; s32 i;
s32 availableToSwitch; s32 availableToSwitch;
if (gBattleMons[*(activeBattlerPtr = &gActiveBattler)].status2 & (STATUS2_WRAPPED | STATUS2_ESCAPE_PREVENTION)) if (gBattleMons[gActiveBattler].status2 & (STATUS2_WRAPPED | STATUS2_ESCAPE_PREVENTION))
return FALSE; return FALSE;
if (gStatuses3[gActiveBattler] & STATUS3_ROOTED) if (gStatuses3[gActiveBattler] & STATUS3_ROOTED)
return FALSE; return FALSE;
if (IsAbilityOnOpposingSide(gActiveBattler, ABILITY_SHADOW_TAG)) if (IsAbilityPreventingEscape(gActiveBattler))
return FALSE; 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) if (gBattleTypeFlags & BATTLE_TYPE_ARENA)
return FALSE; return FALSE;
availableToSwitch = 0; availableToSwitch = 0;
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
{ {
battlerIn1 = *activeBattlerPtr; battlerIn1 = gActiveBattler;
if (gAbsentBattlerFlags & gBitTable[GetBattlerAtPosition(GetBattlerPosition(*activeBattlerPtr) ^ BIT_FLANK)]) if (gAbsentBattlerFlags & gBitTable[GetBattlerAtPosition(GetBattlerPosition(gActiveBattler) ^ BIT_FLANK)])
battlerIn2 = *activeBattlerPtr; battlerIn2 = gActiveBattler;
else else
battlerIn2 = GetBattlerAtPosition(GetBattlerPosition(*activeBattlerPtr) ^ BIT_FLANK); battlerIn2 = GetBattlerAtPosition(GetBattlerPosition(gActiveBattler) ^ BIT_FLANK);
} }
else else
{ {
battlerIn1 = *activeBattlerPtr; battlerIn1 = gActiveBattler;
battlerIn2 = *activeBattlerPtr; battlerIn2 = gActiveBattler;
} }
GetAIPartyIndexes(gActiveBattler, &firstId, &lastId); GetAIPartyIndexes(gActiveBattler, &firstId, &lastId);

View File

@ -3652,9 +3652,7 @@ void BattleTurnPassed(void)
u8 IsRunningFromBattleImpossible(void) u8 IsRunningFromBattleImpossible(void)
{ {
u8 holdEffect; u32 holdEffect, i;
u8 side;
s32 i;
if (gBattleMons[gActiveBattler].item == ITEM_ENIGMA_BERRY) if (gBattleMons[gActiveBattler].item == ITEM_ENIGMA_BERRY)
holdEffect = gEnigmaBerries[gActiveBattler].holdEffect; holdEffect = gEnigmaBerries[gActiveBattler].holdEffect;
@ -3681,37 +3679,14 @@ u8 IsRunningFromBattleImpossible(void)
if (gBattleMons[gActiveBattler].ability == ABILITY_RUN_AWAY) if (gBattleMons[gActiveBattler].ability == ABILITY_RUN_AWAY)
return 0; return 0;
side = GetBattlerSide(gActiveBattler); if ((i = IsAbilityPreventingEscape(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))
{ {
gBattleScripting.battler = i - 1; gBattleScripting.battler = i - 1;
gLastUsedAbility = gBattleMons[i - 1].ability; gLastUsedAbility = gBattleMons[i - 1].ability;
gBattleCommunication[MULTISTRING_CHOOSER] = 2; gBattleCommunication[MULTISTRING_CHOOSER] = 2;
return 2; return 2;
} }
if ((gBattleMons[gActiveBattler].status2 & (STATUS2_ESCAPE_PREVENTION | STATUS2_WRAPPED)) if ((gBattleMons[gActiveBattler].status2 & (STATUS2_ESCAPE_PREVENTION | STATUS2_WRAPPED))
|| (gStatuses3[gActiveBattler] & STATUS3_ROOTED)) || (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]); BtlController_EmitChoosePokemon(0, PARTY_ACTION_CANT_SWITCH, PARTY_SIZE, ABILITY_NONE, gBattleStruct->field_60[gActiveBattler]);
} }
else if ((i = IsAbilityOnOpposingSide(gActiveBattler, ABILITY_SHADOW_TAG)) else if ((i = IsAbilityPreventingEscape(gActiveBattler)))
|| ((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)))
{ {
BtlController_EmitChoosePokemon(0, ((i - 1) << 4) | PARTY_ACTION_ABILITY_PREVENTS, PARTY_SIZE, gLastUsedAbility, gBattleStruct->field_60[gActiveBattler]); BtlController_EmitChoosePokemon(0, ((i - 1) << 4) | PARTY_ACTION_ABILITY_PREVENTS, PARTY_SIZE, gLastUsedAbility, gBattleStruct->field_60[gActiveBattler]);
} }

View File

@ -23,6 +23,7 @@
#include "field_weather.h" #include "field_weather.h"
#include "constants/abilities.h" #include "constants/abilities.h"
#include "constants/battle_anim.h" #include "constants/battle_anim.h"
#include "constants/battle_config.h"
#include "constants/battle_move_effects.h" #include "constants/battle_move_effects.h"
#include "constants/battle_script_commands.h" #include "constants/battle_script_commands.h"
#include "constants/battle_string_ids.h" #include "constants/battle_string_ids.h"
@ -3801,6 +3802,23 @@ u32 IsAbilityOnFieldExcept(u32 battlerId, u32 ability)
return 0; 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) void BattleScriptExecute(const u8 *BS_ptr)
{ {
gBattlescriptCurrInstr = BS_ptr; gBattlescriptCurrInstr = BS_ptr;

View File

@ -5510,7 +5510,7 @@ void AdjustFriendship(struct Pokemon *mon, u8 event)
if (friendship > 199) if (friendship > 199)
friendshipLevel++; friendshipLevel++;
if ((event != FRIENDSHIP_EVENT_WALKING || !(Random() & 1)) if ((event != FRIENDSHIP_EVENT_WALKING || !(Random() & 1))
&& (event != FRIENDSHIP_EVENT_LEAGUE_BATTLE || IS_LEAGUE_BATTLE)) && (event != FRIENDSHIP_EVENT_LEAGUE_BATTLE || IS_LEAGUE_BATTLE))
{ {
s8 mod = sFriendshipEventModifiers[event][friendshipLevel]; s8 mod = sFriendshipEventModifiers[event][friendshipLevel];