mirror of
https://github.com/Ninjdai1/pokeemerald.git
synced 2025-01-18 17:34:20 +01:00
Added corner case logic for AI Switching.
1. Refactor Perish Song 2. Yawn 3. Secondary Damage (Leech Seed, Cursed etc). 4. Added Preliminary logic to help AI be smarter against semi-invulnerable Added AI logic regarding abilities that benefit from switching 1. Natural Cure 2. Regenerator
This commit is contained in:
parent
eb0cc91dce
commit
b95e450cb6
@ -105,6 +105,7 @@ bool8 HasNoMonsToSwitch(u8 battlerId, u8 r1, u8 r2);
|
||||
u8 TryWeatherFormChange(u8 battlerId);
|
||||
bool32 TryChangeBattleWeather(u8 battler, u32 weatherEnumId, bool32 viaAbility);
|
||||
u8 AbilityBattleEffects(u8 caseID, u8 battlerId, u16 ability, u8 special, u16 moveArg);
|
||||
bool32 IsNeutralizingGasOnField(void);
|
||||
u32 GetBattlerAbility(u8 battlerId);
|
||||
u32 IsAbilityOnSide(u32 battlerId, u32 ability);
|
||||
u32 IsAbilityOnOpposingSide(u32 battlerId, u32 ability);
|
||||
|
@ -1,10 +1,13 @@
|
||||
#include "global.h"
|
||||
#include "battle.h"
|
||||
#include "constants/battle_ai.h"
|
||||
#include "battle_ai_main.h"
|
||||
#include "battle_ai_util.h"
|
||||
#include "battle_util.h"
|
||||
#include "battle_anim.h"
|
||||
#include "battle_controllers.h"
|
||||
#include "battle_main.h"
|
||||
#include "constants/hold_effects.h"
|
||||
#include "battle_setup.h"
|
||||
#include "data.h"
|
||||
#include "pokemon.h"
|
||||
@ -19,6 +22,7 @@
|
||||
static bool8 HasSuperEffectiveMoveAgainstOpponents(bool8 noRng);
|
||||
static bool8 FindMonWithFlagsAndSuperEffective(u16 flags, u8 moduloPercent);
|
||||
static bool8 ShouldUseItem(void);
|
||||
static bool32 AiExpectsToFaintPlayer(void);
|
||||
static bool32 AI_ShouldHeal(u32 healAmount);
|
||||
static bool32 AI_OpponentCanFaintAiWithMod(u32 healAmount);
|
||||
|
||||
@ -56,21 +60,6 @@ static bool8 ShouldSwitchIfAllBadMoves(void)
|
||||
}
|
||||
}
|
||||
|
||||
static bool8 ShouldSwitchIfPerishSong(void)
|
||||
{
|
||||
if (gStatuses3[gActiveBattler] & STATUS3_PERISH_SONG
|
||||
&& gDisableStructs[gActiveBattler].perishSongTimer == 0)
|
||||
{
|
||||
*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) = PARTY_SIZE;
|
||||
BtlController_EmitTwoReturnValues(BUFFER_B, B_ACTION_SWITCH, 0);
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static bool8 ShouldSwitchIfWonderGuard(void)
|
||||
{
|
||||
u8 opposingPosition;
|
||||
@ -228,44 +217,177 @@ static bool8 FindMonThatAbsorbsOpponentsMove(void)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static bool8 ShouldSwitchIfNaturalCure(void)
|
||||
static bool8 ShouldSwitchIfGameStatePrompt(void)
|
||||
{
|
||||
if (!(gBattleMons[gActiveBattler].status1 & STATUS1_SLEEP))
|
||||
return FALSE;
|
||||
if (AI_GetAbility(gActiveBattler) != ABILITY_NATURAL_CURE)
|
||||
return FALSE;
|
||||
if (gBattleMons[gActiveBattler].hp < gBattleMons[gActiveBattler].maxHP / 2)
|
||||
return FALSE;
|
||||
bool8 switchMon = FALSE;
|
||||
u16 monAbility = AI_GetAbility(gActiveBattler);
|
||||
u16 holdEffect = AI_GetHoldEffect(gActiveBattler);
|
||||
u8 opposingPosition = BATTLE_OPPOSITE(GetBattlerPosition(gActiveBattler));
|
||||
u8 opposingBattler = GetBattlerAtPosition(opposingPosition);
|
||||
s32 moduloChance = 4; //25% Chance Default
|
||||
s32 chanceReducer = 1; //No Reduce default. Increase to reduce
|
||||
|
||||
if ((gLastLandedMoves[gActiveBattler] == MOVE_NONE
|
||||
|| gLastLandedMoves[gActiveBattler] == MOVE_UNAVAILABLE)
|
||||
&& Random() & 1)
|
||||
|
||||
if (AnyStatIsRaised(gActiveBattler))
|
||||
chanceReducer = 5; // Reduce switchout probability by factor of 5 if setup
|
||||
|
||||
//Perish Song
|
||||
if (gStatuses3[gActiveBattler] & STATUS3_PERISH_SONG
|
||||
&& gDisableStructs[gActiveBattler].perishSongTimer == 0
|
||||
&& monAbility != ABILITY_SOUNDPROOF)
|
||||
switchMon = TRUE;
|
||||
|
||||
if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_SMART_SWITCHING)
|
||||
{
|
||||
//Yawn
|
||||
if (gStatuses3[gActiveBattler] & STATUS3_YAWN
|
||||
&& AI_CanSleep(gActiveBattler, monAbility)
|
||||
&& gBattleMons[gActiveBattler].hp > gBattleMons[gActiveBattler].maxHP / 3)
|
||||
{
|
||||
switchMon = TRUE;
|
||||
|
||||
//ToDo:
|
||||
//Double Battles
|
||||
//Add logic checking to see if effected by yawn & ally wants to switch out to a pokemon that will set Misty or Electric Terrain
|
||||
|
||||
//Check if Active Pokemon can KO opponent instead of switching
|
||||
//Will still fall asleep, but take out opposing Pokemon first
|
||||
if (AiExpectsToFaintPlayer())
|
||||
switchMon = FALSE;
|
||||
|
||||
//Checks to see if active Pokemon can do something against sleep
|
||||
if (monAbility == (ABILITY_NATURAL_CURE | ABILITY_SHED_SKIN | ABILITY_EARLY_BIRD)
|
||||
|| holdEffect == (HOLD_EFFECT_CURE_SLP | HOLD_EFFECT_CURE_STATUS)
|
||||
|| HasMove(gActiveBattler, MOVE_SLEEP_TALK)
|
||||
|| (HasMoveEffect(gActiveBattler, MOVE_SNORE) && AI_GetTypeEffectiveness(MOVE_SNORE, gActiveBattler, opposingBattler) >= UQ_4_12(1.0))
|
||||
|| (IsBattlerGrounded(gActiveBattler)
|
||||
&& (HasMove(gActiveBattler, MOVE_MISTY_TERRAIN) || HasMove(gActiveBattler, MOVE_ELECTRIC_TERRAIN)))
|
||||
)
|
||||
switchMon = FALSE;
|
||||
|
||||
//Check if Active Pokemon evasion boosted and might be able to dodge until awake
|
||||
if (gBattleMons[gActiveBattler].statStages[STAT_EVASION] > (DEFAULT_STAT_STAGE + 3)
|
||||
&& AI_GetAbility(opposingBattler) != ABILITY_UNAWARE
|
||||
&& AI_GetAbility(opposingBattler) != ABILITY_KEEN_EYE
|
||||
&& !(gBattleMons[gActiveBattler].status2 & STATUS2_FORESIGHT)
|
||||
&& !(gStatuses3[gActiveBattler] & STATUS3_MIRACLE_EYED))
|
||||
switchMon = FALSE;
|
||||
|
||||
}
|
||||
|
||||
//Secondary Damage
|
||||
if (monAbility != ABILITY_MAGIC_GUARD
|
||||
&& !AiExpectsToFaintPlayer())
|
||||
{
|
||||
//Toxic
|
||||
moduloChance = 2; //50%
|
||||
if (gBattleMons[gActiveBattler].status1 & (STATUS1_TOXIC_COUNTER > 2)
|
||||
&& gBattleMons[gActiveBattler].hp >= (gBattleMons[gActiveBattler].maxHP / 3)
|
||||
&& (Random() % (moduloChance*chanceReducer)) == 0)
|
||||
switchMon = TRUE;
|
||||
|
||||
//Cursed
|
||||
moduloChance = 2; //50%
|
||||
if (gBattleMons[gActiveBattler].status2 & STATUS2_CURSED
|
||||
&& (Random() % (moduloChance*chanceReducer)) == 0)
|
||||
switchMon = TRUE;
|
||||
|
||||
//Nightmare
|
||||
moduloChance = 3; //33.3%
|
||||
if (gBattleMons[gActiveBattler].status1 & (STATUS1_SLEEP > 1) && gBattleMons[gActiveBattler].status2 & STATUS2_NIGHTMARE
|
||||
&& (Random() % (moduloChance*chanceReducer)) == 0)
|
||||
switchMon = TRUE;
|
||||
|
||||
//Leech Seed
|
||||
moduloChance = 4; //25%
|
||||
if (gStatuses3[gActiveBattler] & STATUS3_LEECHSEED
|
||||
&& (Random() % (moduloChance*chanceReducer)) == 0)
|
||||
switchMon = TRUE;
|
||||
}
|
||||
|
||||
//Infatuation
|
||||
if (gBattleMons[gActiveBattler].status2 & STATUS2_INFATUATION
|
||||
&& !AiExpectsToFaintPlayer())
|
||||
switchMon = TRUE;
|
||||
|
||||
//Todo
|
||||
//Pass Wish Heal
|
||||
|
||||
//Semi-Invulnerable
|
||||
if (gStatuses3[opposingBattler] & STATUS3_SEMI_INVULNERABLE)
|
||||
if (FindMonThatAbsorbsOpponentsMove()) //If find absorber default to switch
|
||||
switchMon = TRUE;
|
||||
if (!AI_OpponentCanFaintAiWithMod(0)
|
||||
&& AnyStatIsRaised(gActiveBattler))
|
||||
switchMon = FALSE;
|
||||
if (AiExpectsToFaintPlayer()
|
||||
&& GetAIChosenMove(gActiveBattler) == AI_IS_SLOWER
|
||||
&& !AI_OpponentCanFaintAiWithMod(0))
|
||||
switchMon = FALSE;
|
||||
}
|
||||
|
||||
|
||||
if (switchMon)
|
||||
{
|
||||
*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) = PARTY_SIZE;
|
||||
BtlController_EmitTwoReturnValues(BUFFER_B, B_ACTION_SWITCH, 0);
|
||||
return TRUE;
|
||||
}
|
||||
else if (gBattleMoves[gLastLandedMoves[gActiveBattler]].power == 0
|
||||
&& Random() & 1)
|
||||
{
|
||||
*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) = PARTY_SIZE;
|
||||
BtlController_EmitTwoReturnValues(BUFFER_B, B_ACTION_SWITCH, 0);
|
||||
return TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static bool8 ShouldSwitchIfAbilityBenefit(void)
|
||||
{
|
||||
s32 monToSwitchId;
|
||||
s32 moduloChance = 4; //25% Chance Default
|
||||
s32 chanceReducer = 1; //No Reduce default. Increase to reduce
|
||||
u8 battlerId = GetBattlerPosition(gActiveBattler);
|
||||
|
||||
if (AnyStatIsRaised(battlerId))
|
||||
chanceReducer = 5; // Reduce switchout probability by factor of 5 if setup
|
||||
|
||||
//Check if ability is blocked
|
||||
if (gStatuses3[gActiveBattler] & STATUS3_GASTRO_ACID
|
||||
||IsNeutralizingGasOnField())
|
||||
return FALSE;
|
||||
|
||||
switch(AI_GetAbility(gActiveBattler)) {
|
||||
case ABILITY_NATURAL_CURE:
|
||||
moduloChance = 4; //25%
|
||||
//Attempt to cure bad ailment
|
||||
if (gBattleMons[gActiveBattler].status1 & (STATUS1_SLEEP | STATUS1_FREEZE | STATUS1_TOXIC_POISON)
|
||||
&& GetMostSuitableMonToSwitchInto() != PARTY_SIZE)
|
||||
break;
|
||||
//Attempt to cure lesser ailment
|
||||
if ((gBattleMons[gActiveBattler].status1 & STATUS1_ANY)
|
||||
&& (gBattleMons[gActiveBattler].hp >= gBattleMons[gActiveBattler].maxHP / 2)
|
||||
&& GetMostSuitableMonToSwitchInto() != PARTY_SIZE
|
||||
&& Random() % (moduloChance*chanceReducer) == 0)
|
||||
break;
|
||||
|
||||
return FALSE;
|
||||
|
||||
case ABILITY_REGENERATOR:
|
||||
moduloChance = 2; //50%
|
||||
//Don't switch if ailment
|
||||
if (gBattleMons[gActiveBattler].status1 & STATUS1_ANY)
|
||||
return FALSE;
|
||||
if ((gBattleMons[gActiveBattler].hp <= ((gBattleMons[gActiveBattler].maxHP * 2) / 3))
|
||||
&& GetMostSuitableMonToSwitchInto() != PARTY_SIZE
|
||||
&& Random() % (moduloChance*chanceReducer) == 0)
|
||||
break;
|
||||
|
||||
return FALSE;
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (FindMonWithFlagsAndSuperEffective(MOVE_RESULT_DOESNT_AFFECT_FOE, 1))
|
||||
return TRUE;
|
||||
if (FindMonWithFlagsAndSuperEffective(MOVE_RESULT_NOT_VERY_EFFECTIVE, 1))
|
||||
return TRUE;
|
||||
*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) = PARTY_SIZE;
|
||||
BtlController_EmitTwoReturnValues(BUFFER_B, B_ACTION_SWITCH, 0);
|
||||
|
||||
if (Random() & 1)
|
||||
{
|
||||
*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) = PARTY_SIZE;
|
||||
BtlController_EmitTwoReturnValues(BUFFER_B, B_ACTION_SWITCH, 0);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static bool8 HasSuperEffectiveMoveAgainstOpponents(bool8 noRng)
|
||||
@ -489,13 +611,13 @@ bool32 ShouldSwitch(void)
|
||||
return FALSE;
|
||||
if (ShouldSwitchIfAllBadMoves())
|
||||
return TRUE;
|
||||
if (ShouldSwitchIfPerishSong())
|
||||
if (ShouldSwitchIfGameStatePrompt())
|
||||
return TRUE;
|
||||
if (ShouldSwitchIfWonderGuard())
|
||||
return TRUE;
|
||||
if (FindMonThatAbsorbsOpponentsMove())
|
||||
return TRUE;
|
||||
if (ShouldSwitchIfNaturalCure())
|
||||
if (ShouldSwitchIfAbilityBenefit())
|
||||
return TRUE;
|
||||
if (HasSuperEffectiveMoveAgainstOpponents(FALSE))
|
||||
return FALSE;
|
||||
|
Loading…
x
Reference in New Issue
Block a user