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 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);

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_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.

View file

@ -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;
}
}
}

View file

@ -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);

View file

@ -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]);
}

View file

@ -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;

View file

@ -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];