AI refactor for weather-setting, terrain-setting, Trick Room behaviors; doubles-focused (#7319)
This commit is contained in:
parent
2f6e1ea6d3
commit
1b996fbb8e
18
include/battle_ai_field_statuses.h
Normal file
18
include/battle_ai_field_statuses.h
Normal file
@ -0,0 +1,18 @@
|
||||
#ifndef GUARD_BATTLE_AI_FIELD_STATUSES_H
|
||||
#define GUARD_BATTLE_AI_FIELD_STATUSES_H
|
||||
|
||||
#include "battle_ai_main.h"
|
||||
#include "battle_ai_util.h"
|
||||
|
||||
enum FieldEffectOutcome
|
||||
{
|
||||
FIELD_EFFECT_POSITIVE,
|
||||
FIELD_EFFECT_NEUTRAL,
|
||||
FIELD_EFFECT_NEGATIVE,
|
||||
FIELD_EFFECT_BLOCKED,
|
||||
};
|
||||
|
||||
bool32 WeatherChecker(u32 battler, u32 weather, enum FieldEffectOutcome desiredResult);
|
||||
bool32 FieldStatusChecker(u32 battler, u32 fieldStatus, enum FieldEffectOutcome desiredResult);
|
||||
|
||||
#endif //GUARD_BATTLE_AI_FIELD_STATUSES_H
|
||||
@ -2,6 +2,7 @@
|
||||
#define GUARD_BATTLE_AI_UTIL_H
|
||||
|
||||
#include "battle_ai_main.h"
|
||||
#include "battle_ai_field_statuses.h"
|
||||
|
||||
#define FOE(battler) ((BATTLE_OPPOSITE(battler)) & BIT_SIDE)
|
||||
|
||||
@ -159,9 +160,12 @@ bool32 HasMoveWithCategory(u32 battler, enum DamageCategory category);
|
||||
bool32 HasMoveWithType(u32 battler, u32 type);
|
||||
bool32 HasMoveWithEffect(u32 battlerId, enum BattleMoveEffects moveEffect);
|
||||
bool32 HasBattlerSideMoveWithEffect(u32 battler, u32 effect);
|
||||
bool32 HasBattlerSideUsedMoveWithEffect(u32 battler, u32 effect);
|
||||
bool32 HasNonVolatileMoveEffect(u32 battlerId, u32 effect);
|
||||
bool32 IsPowerBasedOnStatus(u32 battlerId, enum BattleMoveEffects effect, u32 argument);
|
||||
bool32 HasMoveWithAdditionalEffect(u32 battlerId, u32 moveEffect);
|
||||
bool32 HasBattlerSideMoveWithAdditionalEffect(u32 battler, u32 moveEffect);
|
||||
bool32 HasBattlerSideUsedMoveWithAdditionalEffect(u32 battler, u32 moveEffect);
|
||||
bool32 HasMoveWithCriticalHitChance(u32 battlerId);
|
||||
bool32 HasMoveWithMoveEffectExcept(u32 battlerId, u32 moveEffect, enum BattleMoveEffects exception);
|
||||
bool32 HasMoveThatLowersOwnStats(u32 battlerId);
|
||||
@ -175,11 +179,10 @@ bool32 IsHazardMove(u32 move);
|
||||
bool32 IsTwoTurnNotSemiInvulnerableMove(u32 battlerAtk, u32 move);
|
||||
bool32 IsBattlerDamagedByStatus(u32 battler);
|
||||
s32 ProtectChecks(u32 battlerAtk, u32 battlerDef, u32 move, u32 predictedMove);
|
||||
bool32 ShouldSetSandstorm(u32 battler, u32 ability, enum ItemHoldEffect holdEffect);
|
||||
bool32 ShouldSetHail(u32 battler, u32 ability, enum ItemHoldEffect holdEffect);
|
||||
bool32 ShouldSetSnow(u32 battler, u32 ability, enum ItemHoldEffect holdEffect);
|
||||
bool32 ShouldSetRain(u32 battlerAtk, u32 ability, enum ItemHoldEffect holdEffect);
|
||||
bool32 ShouldSetSun(u32 battlerAtk, u32 atkAbility, enum ItemHoldEffect holdEffect);
|
||||
bool32 ShouldSetWeather(u32 battler, u32 weather);
|
||||
bool32 ShouldClearWeather(u32 battler, u32 weather);
|
||||
bool32 ShouldSetFieldStatus(u32 battler, u32 fieldStatus);
|
||||
bool32 ShouldClearFieldStatus(u32 battler, u32 fieldStatus);
|
||||
bool32 HasSleepMoveWithLowAccuracy(u32 battlerAtk, u32 battlerDef);
|
||||
bool32 IsHealingMove(u32 move);
|
||||
bool32 HasHealingEffect(u32 battler);
|
||||
|
||||
@ -89,10 +89,13 @@
|
||||
// AI_FLAG_SMART_SWITCHING settings
|
||||
#define SMART_SWITCHING_OMNISCIENT FALSE // AI will use omniscience for switching calcs, regardless of omniscience setting otherwise
|
||||
|
||||
// AI's acceptable number of hits to KO the partner via friendly fire in a double battle.
|
||||
#define FRIENDLY_FIRE_RISKY_THRESHOLD 2
|
||||
#define FRIENDLY_FIRE_NORMAL_THRESHOLD 3
|
||||
#define FRIENDLY_FIRE_CONSERVATIVE_THRESHOLD 4
|
||||
// Configurations specifically for AI_FLAG_DOUBLE_BATTLE.
|
||||
#define FRIENDLY_FIRE_RISKY_THRESHOLD 2 // AI_FLAG_RISKY acceptable number of hits to KO the partner via friendly fire
|
||||
#define FRIENDLY_FIRE_NORMAL_THRESHOLD 3 // typical acceptable number of hits to KO the partner via friendly fire
|
||||
#define FRIENDLY_FIRE_CONSERVATIVE_THRESHOLD 4 // AI_FLAG_CONSERVATIVE acceptable number of hits to KO the partner via friendly fire
|
||||
// Counterplay on the assumption of opponents Protecting.
|
||||
#define DOUBLE_TRICK_ROOM_ON_LAST_TURN_CHANCE 35 // both pokemon use Trick Room on turn Trick Room expires in the hopes both opponents used Protect to stall, getting a free refresh on the timer
|
||||
#define TAILWIND_IN_TRICK_ROOM_CHANCE 35 // use Tailwind on turn Trick Room expires in the hopes both opponents used Protect to stall
|
||||
|
||||
// AI's desired stat changes for Guard Split and Power Split, treated as %
|
||||
#define GUARD_SPLIT_ALLY_PERCENTAGE 200
|
||||
|
||||
@ -387,6 +387,9 @@ enum BattleWeather
|
||||
#define B_WEATHER_STRONG_WINDS (1 << BATTLE_WEATHER_STRONG_WINDS)
|
||||
|
||||
#define B_WEATHER_ANY (B_WEATHER_RAIN | B_WEATHER_SANDSTORM | B_WEATHER_SUN | B_WEATHER_HAIL | B_WEATHER_STRONG_WINDS | B_WEATHER_SNOW | B_WEATHER_FOG)
|
||||
#define B_WEATHER_DAMAGING_ANY (B_WEATHER_HAIL | B_WEATHER_SANDSTORM)
|
||||
#define B_WEATHER_ICY_ANY (B_WEATHER_HAIL | B_WEATHER_SNOW)
|
||||
#define B_WEATHER_LOW_LIGHT (B_WEATHER_FOG | B_WEATHER_ICY_ANY | B_WEATHER_RAIN | B_WEATHER_SANDSTORM)
|
||||
#define B_WEATHER_PRIMAL_ANY (B_WEATHER_RAIN_PRIMAL | B_WEATHER_SUN_PRIMAL | B_WEATHER_STRONG_WINDS)
|
||||
|
||||
enum __attribute__((packed)) MoveEffects
|
||||
|
||||
@ -209,6 +209,8 @@ enum RandomTag
|
||||
RNG_AI_ASSUME_STATUS_MEDIUM_ODDS,
|
||||
RNG_AI_ASSUME_STATUS_LOW_ODDS,
|
||||
RNG_AI_ASSUME_ALL_STATUS,
|
||||
RNG_AI_REFRESH_TRICK_ROOM_ON_LAST_TURN,
|
||||
RNG_AI_APPLY_TAILWIND_ON_LAST_TURN_OF_TRICK_ROOM,
|
||||
};
|
||||
|
||||
#define RandomWeighted(tag, ...) \
|
||||
|
||||
454
src/battle_ai_field_statuses.c
Normal file
454
src/battle_ai_field_statuses.c
Normal file
@ -0,0 +1,454 @@
|
||||
#include "global.h"
|
||||
#include "battle_z_move.h"
|
||||
#include "malloc.h"
|
||||
#include "battle.h"
|
||||
#include "battle_anim.h"
|
||||
#include "battle_ai_field_statuses.h"
|
||||
#include "battle_ai_util.h"
|
||||
#include "battle_ai_main.h"
|
||||
#include "battle_ai_switch_items.h"
|
||||
#include "battle_factory.h"
|
||||
#include "battle_setup.h"
|
||||
#include "event_data.h"
|
||||
#include "data.h"
|
||||
#include "item.h"
|
||||
#include "move.h"
|
||||
#include "pokemon.h"
|
||||
#include "random.h"
|
||||
#include "recorded_battle.h"
|
||||
#include "util.h"
|
||||
#include "constants/abilities.h"
|
||||
#include "constants/battle_ai.h"
|
||||
#include "constants/battle_move_effects.h"
|
||||
#include "constants/hold_effects.h"
|
||||
#include "constants/moves.h"
|
||||
#include "constants/items.h"
|
||||
|
||||
static bool32 DoesAbilityBenefitFromWeather(u32 ability, u32 weather);
|
||||
static bool32 DoesAbilityBenefitFromFieldStatus(u32 ability, u32 fieldStatus);
|
||||
// A move is light sensitive if it is boosted by Sunny Day and weakened by low light weathers.
|
||||
static bool32 IsLightSensitiveMove(u32 move);
|
||||
static bool32 HasLightSensitiveMove(u32 battler);
|
||||
// The following functions all feed into WeatherChecker, which is then called by ShouldSetWeather and ShouldClearWeather.
|
||||
// BenefitsFrom functions all return FIELD_EFFECT_POSITIVE if the weather or field effect is good to have in place from the perspective of the battler, FIELD_EFFECT_NEUTRAL if it is neither good nor bad, and FIELD_EFFECT_NEGATIVE if it is bad.
|
||||
// The purpose of WeatherChecker and FieldStatusChecker is to cleanly homogenize the logic that's the same with all of them, and to more easily apply single battle logic to double battles.
|
||||
// ShouldSetWeather and ShouldClearWeather are looking for a positive or negative result respectively, and check the entire side.
|
||||
// If one pokemon has a positive result and the other has a negative result, it defaults to the opinion of the battler that may change the weather or field status.
|
||||
static enum FieldEffectOutcome BenefitsFromSun(u32 battler);
|
||||
static enum FieldEffectOutcome BenefitsFromSandstorm(u32 battler);
|
||||
static enum FieldEffectOutcome BenefitsFromHailOrSnow(u32 battler, u32 weather);
|
||||
static enum FieldEffectOutcome BenefitsFromRain(u32 battler);
|
||||
// The following functions all feed into FieldStatusChecker, which is then called by ShouldSetFieldStatus and ShouldClearFieldStatus.
|
||||
// They work approximately the same as the weather functions.
|
||||
static enum FieldEffectOutcome BenefitsFromElectricTerrain(u32 battler);
|
||||
static enum FieldEffectOutcome BenefitsFromGrassyTerrain(u32 battler);
|
||||
static enum FieldEffectOutcome BenefitsFromMistyTerrain(u32 battler);
|
||||
static enum FieldEffectOutcome BenefitsFromPsychicTerrain(u32 battler);
|
||||
static enum FieldEffectOutcome BenefitsFromTrickRoom(u32 battler);
|
||||
|
||||
bool32 WeatherChecker(u32 battler, u32 weather, enum FieldEffectOutcome desiredResult)
|
||||
{
|
||||
if (IsWeatherActive(B_WEATHER_PRIMAL_ANY) != WEATHER_INACTIVE)
|
||||
return (FIELD_EFFECT_BLOCKED == desiredResult);
|
||||
|
||||
enum FieldEffectOutcome result = FIELD_EFFECT_NEUTRAL;
|
||||
enum FieldEffectOutcome firstResult = FIELD_EFFECT_NEUTRAL;
|
||||
|
||||
u32 i;
|
||||
u32 battlersOnSide = 1;
|
||||
|
||||
if (IsDoubleBattle() && IsBattlerAlive(BATTLE_PARTNER(battler)))
|
||||
battlersOnSide = 2;
|
||||
|
||||
for (i = 0; i < battlersOnSide; i++)
|
||||
{
|
||||
if (weather & B_WEATHER_RAIN)
|
||||
result = BenefitsFromRain(battler);
|
||||
else if (weather & B_WEATHER_SUN)
|
||||
result = BenefitsFromSun(battler);
|
||||
else if (weather & B_WEATHER_SANDSTORM)
|
||||
result = BenefitsFromSandstorm(battler);
|
||||
else if (weather & B_WEATHER_ICY_ANY)
|
||||
result = BenefitsFromHailOrSnow(battler, weather);
|
||||
|
||||
battler = BATTLE_PARTNER(battler);
|
||||
|
||||
if (result != FIELD_EFFECT_NEUTRAL)
|
||||
{
|
||||
if (weather & B_WEATHER_DAMAGING_ANY && i == 0 && battlersOnSide == 2)
|
||||
firstResult = result;
|
||||
}
|
||||
}
|
||||
if (firstResult != FIELD_EFFECT_NEUTRAL)
|
||||
return (firstResult == result) && (result == desiredResult);
|
||||
return (result == desiredResult);
|
||||
}
|
||||
|
||||
bool32 FieldStatusChecker(u32 battler, u32 fieldStatus, enum FieldEffectOutcome desiredResult)
|
||||
{
|
||||
enum FieldEffectOutcome result = FIELD_EFFECT_NEUTRAL;
|
||||
enum FieldEffectOutcome firstResult = FIELD_EFFECT_NEUTRAL;
|
||||
u32 i;
|
||||
|
||||
u32 battlersOnSide = 1;
|
||||
|
||||
if (IsDoubleBattle() && IsBattlerAlive(BATTLE_PARTNER(battler)))
|
||||
battlersOnSide = 2;
|
||||
|
||||
for (i = 0; i < battlersOnSide; i++)
|
||||
{
|
||||
// terrains
|
||||
if (fieldStatus & STATUS_FIELD_ELECTRIC_TERRAIN)
|
||||
result = BenefitsFromElectricTerrain(battler);
|
||||
if (fieldStatus & STATUS_FIELD_GRASSY_TERRAIN)
|
||||
result = BenefitsFromGrassyTerrain(battler);
|
||||
if (fieldStatus & STATUS_FIELD_MISTY_TERRAIN)
|
||||
result = BenefitsFromMistyTerrain(battler);
|
||||
if (fieldStatus & STATUS_FIELD_PSYCHIC_TERRAIN)
|
||||
result = BenefitsFromPsychicTerrain(battler);
|
||||
|
||||
// other field statuses
|
||||
if (fieldStatus & STATUS_FIELD_TRICK_ROOM)
|
||||
result = BenefitsFromTrickRoom(battler);
|
||||
|
||||
battler = BATTLE_PARTNER(battler);
|
||||
|
||||
if (result != FIELD_EFFECT_NEUTRAL)
|
||||
{
|
||||
// Trick room wants both pokemon to agree, not just one
|
||||
if (fieldStatus & STATUS_FIELD_TRICK_ROOM && i == 0 && battlersOnSide == 2)
|
||||
firstResult = result;
|
||||
}
|
||||
}
|
||||
if (firstResult != FIELD_EFFECT_NEUTRAL)
|
||||
return (firstResult == result) && (result == desiredResult);
|
||||
return (result == desiredResult);
|
||||
}
|
||||
|
||||
static bool32 DoesAbilityBenefitFromWeather(u32 ability, u32 weather)
|
||||
{
|
||||
switch (ability)
|
||||
{
|
||||
case ABILITY_FORECAST:
|
||||
return (weather & (B_WEATHER_RAIN | B_WEATHER_SUN | B_WEATHER_ICY_ANY));
|
||||
case ABILITY_MAGIC_GUARD:
|
||||
case ABILITY_OVERCOAT:
|
||||
return (weather & B_WEATHER_DAMAGING_ANY);
|
||||
case ABILITY_SAND_FORCE:
|
||||
case ABILITY_SAND_RUSH:
|
||||
case ABILITY_SAND_VEIL:
|
||||
return (weather & B_WEATHER_SANDSTORM);
|
||||
case ABILITY_ICE_BODY:
|
||||
case ABILITY_ICE_FACE:
|
||||
case ABILITY_SNOW_CLOAK:
|
||||
return (weather & B_WEATHER_ICY_ANY);
|
||||
case ABILITY_SLUSH_RUSH:
|
||||
return (weather & B_WEATHER_SNOW);
|
||||
case ABILITY_DRY_SKIN:
|
||||
case ABILITY_HYDRATION:
|
||||
case ABILITY_RAIN_DISH:
|
||||
case ABILITY_SWIFT_SWIM:
|
||||
return (weather & B_WEATHER_RAIN);
|
||||
case ABILITY_CHLOROPHYLL:
|
||||
case ABILITY_FLOWER_GIFT:
|
||||
case ABILITY_HARVEST:
|
||||
case ABILITY_LEAF_GUARD:
|
||||
case ABILITY_ORICHALCUM_PULSE:
|
||||
case ABILITY_PROTOSYNTHESIS:
|
||||
case ABILITY_SOLAR_POWER:
|
||||
return (weather & B_WEATHER_SUN);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static bool32 DoesAbilityBenefitFromFieldStatus(u32 ability, u32 fieldStatus)
|
||||
{
|
||||
switch (ability)
|
||||
{
|
||||
case ABILITY_MIMICRY:
|
||||
return (fieldStatus & STATUS_FIELD_TERRAIN_ANY);
|
||||
case ABILITY_HADRON_ENGINE:
|
||||
case ABILITY_QUARK_DRIVE:
|
||||
case ABILITY_SURGE_SURFER:
|
||||
return (fieldStatus & STATUS_FIELD_ELECTRIC_TERRAIN);
|
||||
case ABILITY_GRASS_PELT:
|
||||
return (fieldStatus & STATUS_FIELD_GRASSY_TERRAIN);
|
||||
// no abilities inherently benefit from Misty or Psychic Terrains
|
||||
// return (fieldStatus & STATUS_FIELD_MISTY_TERRAIN);
|
||||
// return (fieldStatus & STATUS_FIELD_PSYCHIC_TERRAIN);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static bool32 IsLightSensitiveMove(u32 move)
|
||||
{
|
||||
switch (GetMoveEffect(move))
|
||||
{
|
||||
case EFFECT_SOLAR_BEAM:
|
||||
case EFFECT_MORNING_SUN:
|
||||
case EFFECT_SYNTHESIS:
|
||||
case EFFECT_MOONLIGHT:
|
||||
case EFFECT_GROWTH:
|
||||
return TRUE;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static bool32 HasLightSensitiveMove(u32 battler)
|
||||
{
|
||||
s32 i;
|
||||
u16 *moves = GetMovesArray(battler);
|
||||
|
||||
for (i = 0; i < MAX_MON_MOVES; i++)
|
||||
{
|
||||
if (moves[i] != MOVE_NONE && moves[i] != MOVE_UNAVAILABLE && IsLightSensitiveMove(moves[i]))
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Sun
|
||||
// Utility Umbrella does NOT block Ancient Pokemon from their stat boosts.
|
||||
static enum FieldEffectOutcome BenefitsFromSun(u32 battler)
|
||||
{
|
||||
u32 ability = gAiLogicData->abilities[battler];
|
||||
|
||||
if (gAiLogicData->holdEffects[battler] == HOLD_EFFECT_UTILITY_UMBRELLA)
|
||||
{
|
||||
if (ability == ABILITY_ORICHALCUM_PULSE || ability == ABILITY_PROTOSYNTHESIS)
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
else
|
||||
return FIELD_EFFECT_NEUTRAL;
|
||||
}
|
||||
|
||||
if (DoesAbilityBenefitFromWeather(ability, B_WEATHER_SUN)
|
||||
|| HasLightSensitiveMove(battler)
|
||||
|| HasDamagingMoveOfType(battler, TYPE_FIRE)
|
||||
|| HasBattlerSideMoveWithEffect(battler, EFFECT_HYDRO_STEAM))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
|
||||
if (HasMoveWithFlag(battler, MoveHas50AccuracyInSun) || HasDamagingMoveOfType(battler, TYPE_WATER) || gAiLogicData->abilities[battler] == ABILITY_DRY_SKIN)
|
||||
return FIELD_EFFECT_NEGATIVE;
|
||||
|
||||
return FIELD_EFFECT_NEUTRAL;
|
||||
}
|
||||
|
||||
// Sandstorm
|
||||
static enum FieldEffectOutcome BenefitsFromSandstorm(u32 battler)
|
||||
{
|
||||
if (DoesAbilityBenefitFromWeather(gAiLogicData->abilities[battler], B_WEATHER_SANDSTORM)
|
||||
|| IS_BATTLER_OF_TYPE(battler, TYPE_ROCK)
|
||||
|| HasBattlerSideMoveWithEffect(battler, EFFECT_SHORE_UP))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
|
||||
if (gAiLogicData->holdEffects[battler] == HOLD_EFFECT_SAFETY_GOGGLES || IS_BATTLER_ANY_TYPE(battler, TYPE_ROCK, TYPE_GROUND, TYPE_STEEL))
|
||||
{
|
||||
if (!(IS_BATTLER_ANY_TYPE(FOE(battler), TYPE_ROCK, TYPE_GROUND, TYPE_STEEL))
|
||||
|| gAiLogicData->holdEffects[FOE(battler)] == HOLD_EFFECT_SAFETY_GOGGLES
|
||||
|| DoesAbilityBenefitFromWeather(gAiLogicData->abilities[FOE(battler)], B_WEATHER_SANDSTORM))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
else
|
||||
return FIELD_EFFECT_NEUTRAL;
|
||||
}
|
||||
|
||||
return FIELD_EFFECT_NEGATIVE;
|
||||
}
|
||||
|
||||
// Hail or Snow
|
||||
static enum FieldEffectOutcome BenefitsFromHailOrSnow(u32 battler, u32 weather)
|
||||
{
|
||||
if (DoesAbilityBenefitFromWeather(gAiLogicData->abilities[battler], weather)
|
||||
|| IS_BATTLER_OF_TYPE(battler, TYPE_ICE)
|
||||
|| HasMoveWithFlag(battler, MoveAlwaysHitsInHailSnow)
|
||||
|| HasBattlerSideMoveWithEffect(battler, EFFECT_AURORA_VEIL))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
|
||||
if ((weather & B_WEATHER_DAMAGING_ANY) && gAiLogicData->holdEffects[battler] != HOLD_EFFECT_SAFETY_GOGGLES)
|
||||
return FIELD_EFFECT_NEGATIVE;
|
||||
|
||||
if (HasLightSensitiveMove(battler))
|
||||
return FIELD_EFFECT_NEGATIVE;
|
||||
|
||||
if (HasMoveWithFlag(FOE(battler), MoveAlwaysHitsInHailSnow))
|
||||
return FIELD_EFFECT_NEGATIVE;
|
||||
|
||||
return FIELD_EFFECT_NEUTRAL;
|
||||
}
|
||||
|
||||
// Rain
|
||||
static enum FieldEffectOutcome BenefitsFromRain(u32 battler)
|
||||
{
|
||||
if (gAiLogicData->holdEffects[battler] == HOLD_EFFECT_UTILITY_UMBRELLA)
|
||||
return FIELD_EFFECT_NEUTRAL;
|
||||
|
||||
if (DoesAbilityBenefitFromWeather(gAiLogicData->abilities[battler], B_WEATHER_RAIN)
|
||||
|| HasMoveWithFlag(battler, MoveAlwaysHitsInRain)
|
||||
|| HasDamagingMoveOfType(battler, TYPE_WATER))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
|
||||
if (HasLightSensitiveMove(battler) || HasDamagingMoveOfType(battler, TYPE_FIRE))
|
||||
return FIELD_EFFECT_NEGATIVE;
|
||||
|
||||
if (HasMoveWithFlag(FOE(battler), MoveAlwaysHitsInRain))
|
||||
return FIELD_EFFECT_NEGATIVE;
|
||||
|
||||
return FIELD_EFFECT_NEUTRAL;
|
||||
}
|
||||
|
||||
//TODO: when is electric terrain bad?
|
||||
static enum FieldEffectOutcome BenefitsFromElectricTerrain(u32 battler)
|
||||
{
|
||||
if (DoesAbilityBenefitFromFieldStatus(gAiLogicData->abilities[battler], STATUS_FIELD_ELECTRIC_TERRAIN))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
|
||||
if (HasMoveWithEffect(battler, EFFECT_RISING_VOLTAGE))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
|
||||
if (HasMoveWithEffect(FOE(battler), EFFECT_REST) && IsBattlerGrounded(FOE(battler)))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
|
||||
bool32 grounded = IsBattlerGrounded(battler);
|
||||
if (grounded && HasBattlerSideUsedMoveWithAdditionalEffect(FOE(battler), MOVE_EFFECT_SLEEP))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
|
||||
if (grounded && ((gBattleMons[battler].status1 & STATUS1_SLEEP)
|
||||
|| (gStatuses3[battler] & STATUS3_YAWN)
|
||||
|| HasDamagingMoveOfType(battler, TYPE_ELECTRIC)))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
|
||||
return FIELD_EFFECT_NEUTRAL;
|
||||
}
|
||||
|
||||
//TODO: when is grassy terrain bad?
|
||||
static enum FieldEffectOutcome BenefitsFromGrassyTerrain(u32 battler)
|
||||
{
|
||||
if (DoesAbilityBenefitFromFieldStatus(gAiLogicData->abilities[battler], STATUS_FIELD_GRASSY_TERRAIN))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
|
||||
if (HasBattlerSideMoveWithEffect(battler, EFFECT_GRASSY_GLIDE))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
if (HasBattlerSideUsedMoveWithAdditionalEffect(battler, MOVE_EFFECT_FLORAL_HEALING))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
|
||||
bool32 grounded = IsBattlerGrounded(battler);
|
||||
|
||||
// Weaken spamming Earthquake, Magnitude, and Bulldoze.
|
||||
if (grounded && (HasBattlerSideUsedMoveWithEffect(FOE(battler), EFFECT_EARTHQUAKE)
|
||||
|| HasBattlerSideUsedMoveWithEffect(FOE(battler), EFFECT_MAGNITUDE)))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
|
||||
if (grounded && HasDamagingMoveOfType(battler, TYPE_GRASS))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
|
||||
return FIELD_EFFECT_NEUTRAL;
|
||||
}
|
||||
|
||||
//TODO: when is misty terrain bad?
|
||||
static enum FieldEffectOutcome BenefitsFromMistyTerrain(u32 battler)
|
||||
{
|
||||
if (DoesAbilityBenefitFromFieldStatus(gAiLogicData->abilities[battler], STATUS_FIELD_MISTY_TERRAIN))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
|
||||
if (HasBattlerSideMoveWithEffect(battler, EFFECT_MISTY_EXPLOSION))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
|
||||
bool32 grounded = IsBattlerGrounded(battler);
|
||||
bool32 allyGrounded = FALSE;
|
||||
if (IsDoubleBattle() && IsBattlerAlive(BATTLE_PARTNER(battler)))
|
||||
allyGrounded = IsBattlerGrounded(BATTLE_PARTNER(battler));
|
||||
|
||||
if (HasMoveWithEffect(FOE(battler), EFFECT_REST) && IsBattlerGrounded(FOE(battler)))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
|
||||
// harass dragons
|
||||
if ((grounded || allyGrounded)
|
||||
&& (HasDamagingMoveOfType(FOE(battler), TYPE_DRAGON) || HasDamagingMoveOfType(BATTLE_PARTNER(FOE(battler)), TYPE_DRAGON)))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
|
||||
if ((grounded || allyGrounded) && HasBattlerSideUsedMoveWithAdditionalEffect(FOE(battler), MOVE_EFFECT_SLEEP))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
|
||||
if (grounded && ((gBattleMons[battler].status1 & STATUS1_SLEEP)
|
||||
|| (gStatuses3[battler] & STATUS3_YAWN)))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
|
||||
return FIELD_EFFECT_NEUTRAL;
|
||||
}
|
||||
|
||||
//TODO: when is Psychic Terrain negative?
|
||||
static enum FieldEffectOutcome BenefitsFromPsychicTerrain(u32 battler)
|
||||
{
|
||||
if (DoesAbilityBenefitFromFieldStatus(gAiLogicData->abilities[battler], STATUS_FIELD_PSYCHIC_TERRAIN))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
|
||||
if (HasBattlerSideMoveWithEffect(battler, EFFECT_EXPANDING_FORCE))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
|
||||
bool32 grounded = IsBattlerGrounded(battler);
|
||||
bool32 allyGrounded = FALSE;
|
||||
if (IsDoubleBattle() && IsBattlerAlive(BATTLE_PARTNER(battler)))
|
||||
allyGrounded = IsBattlerGrounded(BATTLE_PARTNER(battler));
|
||||
|
||||
// don't bother if we're not grounded
|
||||
if (grounded || allyGrounded)
|
||||
{
|
||||
// harass priority
|
||||
if (HasBattlerSideAbility(FOE(battler), ABILITY_GALE_WINGS, gAiLogicData)
|
||||
|| HasBattlerSideAbility(FOE(battler), ABILITY_TRIAGE, gAiLogicData)
|
||||
|| HasBattlerSideAbility(FOE(battler), ABILITY_PRANKSTER, gAiLogicData))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
}
|
||||
|
||||
if (grounded && (HasDamagingMoveOfType(battler, TYPE_PSYCHIC)))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
|
||||
if (HasBattlerSideAbility(battler, ABILITY_GALE_WINGS, gAiLogicData)
|
||||
|| HasBattlerSideAbility(battler, ABILITY_TRIAGE, gAiLogicData)
|
||||
|| HasBattlerSideAbility(battler, ABILITY_PRANKSTER, gAiLogicData))
|
||||
return FIELD_EFFECT_NEGATIVE;
|
||||
|
||||
return FIELD_EFFECT_NEUTRAL;
|
||||
}
|
||||
|
||||
static enum FieldEffectOutcome BenefitsFromTrickRoom(u32 battler)
|
||||
{
|
||||
// If we're in singles, we literally only care about speed.
|
||||
if (!IsDoubleBattle())
|
||||
{
|
||||
if (GetBattlerSideSpeedAverage(battler) < GetBattlerSideSpeedAverage(FOE(battler)))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
// If we tie, we shouldn't change trick room state.
|
||||
else if (GetBattlerSideSpeedAverage(battler) == GetBattlerSideSpeedAverage(FOE(battler)))
|
||||
return FIELD_EFFECT_NEUTRAL;
|
||||
else
|
||||
return FIELD_EFFECT_NEGATIVE;
|
||||
}
|
||||
|
||||
// First checking if we have enough priority for one pokemon to disregard Trick Room entirely.
|
||||
if (!(gFieldStatuses & STATUS_FIELD_PSYCHIC_TERRAIN))
|
||||
{
|
||||
u16* aiMoves = GetMovesArray(battler);
|
||||
for (int i = 0; i < MAX_MON_MOVES; i++)
|
||||
{
|
||||
u16 move = aiMoves[i];
|
||||
if (GetBattleMovePriority(battler, gAiLogicData->abilities[battler], move) > 0 && !(GetMovePriority(move) > 0 && IsBattleMoveStatus(move)))
|
||||
{
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we are faster or tie, we don't want trick room.
|
||||
if ((gAiLogicData->speedStats[battler] >= gAiLogicData->speedStats[FOE(battler)]) || (gAiLogicData->speedStats[battler] >= gAiLogicData->speedStats[BATTLE_PARTNER(FOE(battler))]))
|
||||
return FIELD_EFFECT_NEGATIVE;
|
||||
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
}
|
||||
|
||||
|
||||
@ -1698,7 +1698,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
case EFFECT_AURORA_VEIL:
|
||||
if (gSideStatuses[GetBattlerSide(battlerAtk)] & SIDE_STATUS_AURORA_VEIL
|
||||
|| PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, aiData->partnerMove)
|
||||
|| !(weather & (B_WEATHER_HAIL | B_WEATHER_SNOW)))
|
||||
|| !(weather & (B_WEATHER_ICY_ANY)))
|
||||
ADJUST_SCORE(-10);
|
||||
break;
|
||||
case EFFECT_SHEER_COLD:
|
||||
@ -1912,18 +1912,10 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
ADJUST_SCORE(-8);
|
||||
break;
|
||||
case EFFECT_HAIL:
|
||||
if (weather & (B_WEATHER_HAIL | B_WEATHER_PRIMAL_ANY)
|
||||
|| IsMoveEffectWeather(aiData->partnerMove))
|
||||
ADJUST_SCORE(-8);
|
||||
else if (weather & B_WEATHER_SNOW)
|
||||
ADJUST_SCORE(-2); // mainly to prevent looping between hail and snow
|
||||
break;
|
||||
case EFFECT_SNOWSCAPE:
|
||||
if (weather & (B_WEATHER_SNOW | B_WEATHER_PRIMAL_ANY)
|
||||
if (weather & (B_WEATHER_ICY_ANY | B_WEATHER_PRIMAL_ANY)
|
||||
|| IsMoveEffectWeather(aiData->partnerMove))
|
||||
ADJUST_SCORE(-8);
|
||||
else if (weather & B_WEATHER_HAIL)
|
||||
ADJUST_SCORE(-2); // mainly to prevent looping between hail and snow
|
||||
break;
|
||||
case EFFECT_ATTRACT:
|
||||
if (!AI_CanBeInfatuated(battlerAtk, battlerDef, aiData->abilities[battlerDef]))
|
||||
@ -1957,10 +1949,8 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
case EFFECT_CHILLY_RECEPTION:
|
||||
if (CountUsablePartyMons(battlerAtk) == 0)
|
||||
ADJUST_SCORE(-10);
|
||||
else if (weather & (B_WEATHER_SNOW | B_WEATHER_PRIMAL_ANY) || IsMoveEffectWeather(aiData->partnerMove))
|
||||
else if (weather & (B_WEATHER_ICY_ANY | B_WEATHER_PRIMAL_ANY) || IsMoveEffectWeather(aiData->partnerMove))
|
||||
ADJUST_SCORE(-8);
|
||||
else if (weather & B_WEATHER_HAIL)
|
||||
ADJUST_SCORE(-2); // mainly to prevent looping between hail and snow
|
||||
break;
|
||||
case EFFECT_BELLY_DRUM:
|
||||
case EFFECT_FILLET_AWAY:
|
||||
@ -2143,12 +2133,12 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
case EFFECT_MORNING_SUN:
|
||||
case EFFECT_SYNTHESIS:
|
||||
case EFFECT_MOONLIGHT:
|
||||
if ((AI_GetWeather() & (B_WEATHER_RAIN | B_WEATHER_SANDSTORM | B_WEATHER_HAIL | B_WEATHER_SNOW | B_WEATHER_FOG)))
|
||||
ADJUST_SCORE(-3);
|
||||
else if (AI_BattlerAtMaxHp(battlerAtk))
|
||||
if (AI_BattlerAtMaxHp(battlerAtk))
|
||||
ADJUST_SCORE(-10);
|
||||
else if (aiData->hpPercents[battlerAtk] >= 90)
|
||||
ADJUST_SCORE(-9); //No point in healing, but should at least do it if nothing better
|
||||
else if ((AI_GetWeather() & (B_WEATHER_LOW_LIGHT)))
|
||||
ADJUST_SCORE(-3);
|
||||
break;
|
||||
case EFFECT_PURIFY:
|
||||
if (!(gBattleMons[battlerDef].status1 & STATUS1_ANY))
|
||||
@ -2535,6 +2525,10 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
if (PartnerMoveEffectIsTerrain(BATTLE_PARTNER(battlerAtk), aiData->partnerMove) || gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN)
|
||||
ADJUST_SCORE(-10);
|
||||
break;
|
||||
case EFFECT_STEEL_ROLLER:
|
||||
if (!(gFieldStatuses & STATUS_FIELD_TERRAIN_ANY))
|
||||
ADJUST_SCORE(-10);
|
||||
break;
|
||||
case EFFECT_PLEDGE:
|
||||
if (isDoubleBattle && gBattleMons[BATTLE_PARTNER(battlerAtk)].hp > 0)
|
||||
{
|
||||
@ -2551,20 +2545,21 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
case EFFECT_TRICK_ROOM:
|
||||
if (PartnerMoveEffectIs(BATTLE_PARTNER(battlerAtk), aiData->partnerMove, EFFECT_TRICK_ROOM))
|
||||
{
|
||||
ADJUST_SCORE(-10);
|
||||
// This only happens if the ally already rolled on double trick room on final turn.
|
||||
// Both Pokemon use Trick Room on the final turn of Trick Room to anticipate both opponents Protecting to stall out.
|
||||
if (gFieldTimers.trickRoomTimer == gBattleTurnCounter)
|
||||
ADJUST_SCORE(PERFECT_EFFECT);
|
||||
else
|
||||
ADJUST_SCORE(-10);
|
||||
}
|
||||
else if (!(gAiThinkingStruct->aiFlags[battlerAtk] & AI_FLAG_POWERFUL_STATUS))
|
||||
{
|
||||
if (gFieldStatuses & STATUS_FIELD_TRICK_ROOM) // Trick Room Up
|
||||
{
|
||||
if (GetBattlerSideSpeedAverage(battlerAtk) < GetBattlerSideSpeedAverage(battlerDef)) // Attacker side slower than target side
|
||||
ADJUST_SCORE(-10); // Keep the Trick Room up
|
||||
}
|
||||
else
|
||||
{
|
||||
if (GetBattlerSideSpeedAverage(battlerAtk) >= GetBattlerSideSpeedAverage(battlerDef)) // Attacker side faster than target side
|
||||
ADJUST_SCORE(-10); // Keep the Trick Room down
|
||||
}
|
||||
// Don't set a trick room you don't benefit from.
|
||||
if (!(gFieldStatuses & STATUS_FIELD_TRICK_ROOM) && !ShouldSetFieldStatus(battlerAtk, STATUS_FIELD_TRICK_ROOM))
|
||||
ADJUST_SCORE(-10);
|
||||
// Don't unset a trick room that doesn't harm you unless it's about to expire.
|
||||
else if ((gFieldStatuses & STATUS_FIELD_TRICK_ROOM) && gFieldTimers.trickRoomTimer != gBattleTurnCounter && !ShouldClearFieldStatus(battlerAtk, STATUS_FIELD_TRICK_ROOM))
|
||||
ADJUST_SCORE(-10);
|
||||
}
|
||||
break;
|
||||
case EFFECT_MAGIC_ROOM:
|
||||
@ -2759,7 +2754,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
case EFFECT_TAILWIND:
|
||||
if (gSideStatuses[GetBattlerSide(battlerAtk)] & SIDE_STATUS_TAILWIND
|
||||
|| PartnerMoveEffectIs(BATTLE_PARTNER(battlerAtk), aiData->partnerMove, EFFECT_TAILWIND)
|
||||
|| gFieldStatuses & STATUS_FIELD_TRICK_ROOM)
|
||||
|| (gFieldStatuses & STATUS_FIELD_TRICK_ROOM && gFieldTimers.trickRoomTimer != gBattleTurnCounter))
|
||||
ADJUST_SCORE(-10);
|
||||
break;
|
||||
case EFFECT_LUCKY_CHANT:
|
||||
@ -2900,7 +2895,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
// Don't use user-target moves ie. Swords Dance, with exceptions
|
||||
if ((moveTarget & MOVE_TARGET_USER)
|
||||
&& moveEffect != EFFECT_DESTINY_BOND && moveEffect != EFFECT_WISH && moveEffect != EFFECT_HEALING_WISH
|
||||
&& !(moveEffect == EFFECT_AURORA_VEIL && (AI_GetWeather() & (B_WEATHER_SNOW | B_WEATHER_HAIL))))
|
||||
&& !(moveEffect == EFFECT_AURORA_VEIL && (AI_GetWeather() & B_WEATHER_ICY_ANY)))
|
||||
ADJUST_SCORE(-30);
|
||||
// Don't use a status move if the mon is the last one in the party, has no good switchin, or is trapped
|
||||
else if (GetBattleMoveCategory(move) == DAMAGE_CATEGORY_STATUS
|
||||
@ -3000,11 +2995,11 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
ADJUST_SCORE(-10);
|
||||
break;
|
||||
case EFFECT_AFTER_YOU:
|
||||
if (effect == EFFECT_TRICK_ROOM)
|
||||
if (effect == EFFECT_TRICK_ROOM && !(gFieldStatuses & STATUS_FIELD_TRICK_ROOM) && ShouldSetFieldStatus(battlerAtk, STATUS_FIELD_TRICK_ROOM))
|
||||
ADJUST_SCORE(DECENT_EFFECT);
|
||||
break;
|
||||
case EFFECT_TRICK_ROOM:
|
||||
if (effect == EFFECT_AFTER_YOU)
|
||||
if (effect == EFFECT_AFTER_YOU && !(gFieldStatuses & STATUS_FIELD_TRICK_ROOM) && ShouldSetFieldStatus(battlerAtk, STATUS_FIELD_TRICK_ROOM))
|
||||
ADJUST_SCORE(DECENT_EFFECT);
|
||||
break;
|
||||
default:
|
||||
@ -3039,7 +3034,7 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
break;
|
||||
case EFFECT_MAGNET_RISE:
|
||||
if (IsBattlerGrounded(battlerAtk)
|
||||
&& (HasMove(battlerAtkPartner, MOVE_EARTHQUAKE) || HasMove(battlerAtkPartner, MOVE_MAGNITUDE))
|
||||
&& (HasMoveWithEffect(battlerAtkPartner, EFFECT_EARTHQUAKE) || HasMoveWithEffect(battlerAtkPartner, EFFECT_MAGNITUDE))
|
||||
&& (AI_GetMoveEffectiveness(MOVE_EARTHQUAKE, battlerAtk, battlerAtkPartner) != UQ_4_12(0.0))) // Doesn't resist ground move
|
||||
{
|
||||
RETURN_SCORE_PLUS(DECENT_EFFECT); // partner has earthquake or magnitude -> good idea to use magnet rise
|
||||
@ -3063,38 +3058,20 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
// consider global move effects
|
||||
switch (effect)
|
||||
{
|
||||
case EFFECT_SANDSTORM:
|
||||
if (ShouldSetSandstorm(battlerAtkPartner, atkPartnerAbility, atkPartnerHoldEffect))
|
||||
{
|
||||
RETURN_SCORE_PLUS(WEAK_EFFECT); // our partner benefits from sandstorm
|
||||
}
|
||||
// Both Pokemon use Trick Room on the final turn of Trick Room to anticipate both opponents Protecting to stall out.
|
||||
// This unsets Trick Room and resets it with a full timer.
|
||||
case EFFECT_TRICK_ROOM:
|
||||
if (gFieldStatuses & STATUS_FIELD_TRICK_ROOM && gFieldTimers.trickRoomTimer == gBattleTurnCounter
|
||||
&& ShouldSetFieldStatus(battlerAtk, STATUS_FIELD_TRICK_ROOM)
|
||||
&& HasMoveWithEffect(battlerAtkPartner, MOVE_TRICK_ROOM)
|
||||
&& RandomPercentage(RNG_AI_REFRESH_TRICK_ROOM_ON_LAST_TURN, DOUBLE_TRICK_ROOM_ON_LAST_TURN_CHANCE))
|
||||
ADJUST_SCORE(PERFECT_EFFECT);
|
||||
break;
|
||||
case EFFECT_RAIN_DANCE:
|
||||
if (ShouldSetRain(battlerAtkPartner, atkPartnerAbility, atkPartnerHoldEffect))
|
||||
{
|
||||
RETURN_SCORE_PLUS(WEAK_EFFECT); // our partner benefits from rain
|
||||
}
|
||||
break;
|
||||
case EFFECT_SUNNY_DAY:
|
||||
if (ShouldSetSun(battlerAtkPartner, atkPartnerAbility, atkPartnerHoldEffect))
|
||||
{
|
||||
RETURN_SCORE_PLUS(WEAK_EFFECT); // our partner benefits from sun
|
||||
}
|
||||
break;
|
||||
case EFFECT_HAIL:
|
||||
if (IsBattlerAlive(battlerAtkPartner)
|
||||
&& ShouldSetHail(battlerAtkPartner, atkPartnerAbility, atkPartnerHoldEffect))
|
||||
{
|
||||
RETURN_SCORE_PLUS(DECENT_EFFECT); // our partner benefits from hail
|
||||
}
|
||||
break;
|
||||
case EFFECT_SNOWSCAPE:
|
||||
case EFFECT_CHILLY_RECEPTION:
|
||||
if (IsBattlerAlive(battlerAtkPartner)
|
||||
&& ShouldSetSnow(battlerAtkPartner, atkPartnerAbility, atkPartnerHoldEffect))
|
||||
{
|
||||
RETURN_SCORE_PLUS(DECENT_EFFECT); // our partner benefits from snow
|
||||
}
|
||||
case EFFECT_TAILWIND:
|
||||
// Anticipate both opponents protecting to stall out Trick Room, and apply Tailwind.
|
||||
if (gFieldStatuses & STATUS_FIELD_TRICK_ROOM && gFieldTimers.trickRoomTimer == gBattleTurnCounter
|
||||
&& RandomPercentage(RNG_AI_APPLY_TAILWIND_ON_LAST_TURN_OF_TRICK_ROOM, TAILWIND_IN_TRICK_ROOM_CHANCE))
|
||||
ADJUST_SCORE(BEST_EFFECT);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -4355,9 +4332,12 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
|
||||
ADJUST_SCORE(GOOD_EFFECT);
|
||||
break;
|
||||
case EFFECT_SANDSTORM:
|
||||
if (ShouldSetSandstorm(battlerAtk, aiData->abilities[battlerAtk], aiData->holdEffects[battlerAtk]))
|
||||
if (ShouldSetWeather(battlerAtk, B_WEATHER_SANDSTORM))
|
||||
{
|
||||
ADJUST_SCORE(DECENT_EFFECT);
|
||||
|
||||
if (HasBattlerSideMoveWithEffect(battlerAtk, EFFECT_WEATHER_BALL))
|
||||
ADJUST_SCORE(WEAK_EFFECT);
|
||||
if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_SMOOTH_ROCK)
|
||||
ADJUST_SCORE(WEAK_EFFECT);
|
||||
if (HasMoveWithEffect(battlerDef, EFFECT_MORNING_SUN)
|
||||
@ -4367,12 +4347,14 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
|
||||
}
|
||||
break;
|
||||
case EFFECT_HAIL:
|
||||
if (ShouldSetHail(battlerAtk, aiData->abilities[battlerAtk], aiData->holdEffects[battlerAtk]))
|
||||
if (ShouldSetWeather(battlerAtk, B_WEATHER_HAIL))
|
||||
{
|
||||
ADJUST_SCORE(DECENT_EFFECT);
|
||||
|
||||
if (HasBattlerSideMoveWithEffect(battlerAtk, EFFECT_AURORA_VEIL) && ShouldSetScreen(battlerAtk, battlerDef, EFFECT_AURORA_VEIL))
|
||||
ADJUST_SCORE(GOOD_EFFECT);
|
||||
|
||||
ADJUST_SCORE(DECENT_EFFECT);
|
||||
if (HasBattlerSideMoveWithEffect(battlerAtk, EFFECT_WEATHER_BALL))
|
||||
ADJUST_SCORE(WEAK_EFFECT);
|
||||
if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_ICY_ROCK)
|
||||
ADJUST_SCORE(WEAK_EFFECT);
|
||||
if (HasMoveWithEffect(battlerDef, EFFECT_MORNING_SUN)
|
||||
@ -4382,12 +4364,14 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
|
||||
}
|
||||
break;
|
||||
case EFFECT_SNOWSCAPE:
|
||||
if (ShouldSetSnow(battlerAtk, aiData->abilities[battlerAtk], aiData->holdEffects[battlerAtk]))
|
||||
if (ShouldSetWeather(battlerAtk, B_WEATHER_SNOW))
|
||||
{
|
||||
ADJUST_SCORE(DECENT_EFFECT);
|
||||
|
||||
if (HasBattlerSideMoveWithEffect(battlerAtk, EFFECT_AURORA_VEIL) && ShouldSetScreen(battlerAtk, battlerDef, EFFECT_AURORA_VEIL))
|
||||
ADJUST_SCORE(GOOD_EFFECT);
|
||||
|
||||
ADJUST_SCORE(DECENT_EFFECT);
|
||||
if (HasBattlerSideMoveWithEffect(battlerAtk, EFFECT_WEATHER_BALL))
|
||||
ADJUST_SCORE(WEAK_EFFECT);
|
||||
if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_ICY_ROCK)
|
||||
ADJUST_SCORE(WEAK_EFFECT);
|
||||
if (HasMoveWithEffect(battlerDef, EFFECT_MORNING_SUN)
|
||||
@ -4397,27 +4381,33 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
|
||||
}
|
||||
break;
|
||||
case EFFECT_RAIN_DANCE:
|
||||
if (ShouldSetRain(battlerAtk, aiData->abilities[battlerAtk], aiData->holdEffects[battlerAtk]))
|
||||
if (ShouldSetWeather(battlerAtk, B_WEATHER_RAIN))
|
||||
{
|
||||
ADJUST_SCORE(DECENT_EFFECT);
|
||||
|
||||
if (HasBattlerSideMoveWithEffect(battlerAtk, EFFECT_WEATHER_BALL))
|
||||
ADJUST_SCORE(WEAK_EFFECT);
|
||||
if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_DAMP_ROCK)
|
||||
ADJUST_SCORE(WEAK_EFFECT);
|
||||
if (HasMoveWithEffect(battlerDef, EFFECT_MORNING_SUN)
|
||||
|| HasMoveWithEffect(battlerDef, EFFECT_SYNTHESIS)
|
||||
|| HasMoveWithEffect(battlerDef, EFFECT_SOLAR_BEAM)
|
||||
|| HasMoveWithEffect(battlerDef, EFFECT_MOONLIGHT))
|
||||
if (HasBattlerSideMoveWithEffect(battlerDef, EFFECT_MORNING_SUN)
|
||||
|| HasBattlerSideMoveWithEffect(battlerDef, EFFECT_SYNTHESIS)
|
||||
|| HasBattlerSideMoveWithEffect(battlerDef, EFFECT_SOLAR_BEAM)
|
||||
|| HasBattlerSideMoveWithEffect(battlerDef, EFFECT_MOONLIGHT))
|
||||
ADJUST_SCORE(WEAK_EFFECT);
|
||||
if (HasMoveWithType(battlerDef, TYPE_FIRE) || HasMoveWithType(BATTLE_PARTNER(battlerDef), TYPE_FIRE))
|
||||
if (HasDamagingMoveOfType(battlerDef, TYPE_FIRE) || HasDamagingMoveOfType(BATTLE_PARTNER(battlerDef), TYPE_FIRE))
|
||||
ADJUST_SCORE(WEAK_EFFECT);
|
||||
}
|
||||
break;
|
||||
case EFFECT_SUNNY_DAY:
|
||||
if (ShouldSetSun(battlerAtk, aiData->abilities[battlerAtk], aiData->holdEffects[battlerAtk]))
|
||||
if (ShouldSetWeather(battlerAtk, B_WEATHER_SUN))
|
||||
{
|
||||
ADJUST_SCORE(DECENT_EFFECT);
|
||||
|
||||
if (HasBattlerSideMoveWithEffect(battlerAtk, EFFECT_WEATHER_BALL))
|
||||
ADJUST_SCORE(WEAK_EFFECT);
|
||||
if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_HEAT_ROCK)
|
||||
ADJUST_SCORE(WEAK_EFFECT);
|
||||
if (HasMoveWithType(battlerDef, TYPE_WATER) || HasMoveWithType(BATTLE_PARTNER(battlerDef), TYPE_WATER))
|
||||
if (HasDamagingMoveOfType(battlerDef, TYPE_WATER) || HasDamagingMoveOfType(BATTLE_PARTNER(battlerDef), TYPE_WATER))
|
||||
ADJUST_SCORE(WEAK_EFFECT);
|
||||
if (HasMoveWithFlag(battlerDef, MoveHas50AccuracyInSun) || HasMoveWithFlag(BATTLE_PARTNER(battlerDef), MoveHas50AccuracyInSun))
|
||||
ADJUST_SCORE(WEAK_EFFECT);
|
||||
@ -4883,14 +4873,58 @@ case EFFECT_GUARD_SPLIT:
|
||||
ADJUST_SCORE(WORST_EFFECT);
|
||||
}
|
||||
case EFFECT_ELECTRIC_TERRAIN:
|
||||
case EFFECT_MISTY_TERRAIN:
|
||||
if (gStatuses3[battlerAtk] & STATUS3_YAWN && IsBattlerGrounded(battlerAtk))
|
||||
ADJUST_SCORE(BEST_EFFECT);
|
||||
case EFFECT_GRASSY_TERRAIN:
|
||||
case EFFECT_PSYCHIC_TERRAIN:
|
||||
ADJUST_SCORE(GOOD_EFFECT);
|
||||
if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_TERRAIN_EXTENDER)
|
||||
if (ShouldSetFieldStatus(battlerAtk, STATUS_FIELD_ELECTRIC_TERRAIN))
|
||||
{
|
||||
ADJUST_SCORE(GOOD_EFFECT);
|
||||
if (gStatuses3[battlerAtk] & STATUS3_YAWN && IsBattlerGrounded(battlerAtk))
|
||||
ADJUST_SCORE(BEST_EFFECT);
|
||||
if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_TERRAIN_EXTENDER || HasBattlerSideMoveWithEffect(battlerAtk, EFFECT_TERRAIN_PULSE))
|
||||
ADJUST_SCORE(WEAK_EFFECT);
|
||||
}
|
||||
break;
|
||||
case EFFECT_MISTY_TERRAIN:
|
||||
if (ShouldSetFieldStatus(battlerAtk, STATUS_FIELD_MISTY_TERRAIN))
|
||||
{
|
||||
ADJUST_SCORE(GOOD_EFFECT);
|
||||
if (gStatuses3[battlerAtk] & STATUS3_YAWN && IsBattlerGrounded(battlerAtk))
|
||||
ADJUST_SCORE(BEST_EFFECT);
|
||||
if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_TERRAIN_EXTENDER || HasBattlerSideMoveWithEffect(battlerAtk, EFFECT_TERRAIN_PULSE))
|
||||
ADJUST_SCORE(WEAK_EFFECT);
|
||||
}
|
||||
break;
|
||||
case EFFECT_GRASSY_TERRAIN:
|
||||
if (ShouldSetFieldStatus(battlerAtk, STATUS_FIELD_GRASSY_TERRAIN))
|
||||
{
|
||||
ADJUST_SCORE(GOOD_EFFECT);
|
||||
if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_TERRAIN_EXTENDER || HasBattlerSideMoveWithEffect(battlerAtk, EFFECT_TERRAIN_PULSE))
|
||||
ADJUST_SCORE(WEAK_EFFECT);
|
||||
}
|
||||
break;
|
||||
case EFFECT_PSYCHIC_TERRAIN:
|
||||
if (ShouldSetFieldStatus(battlerAtk, STATUS_FIELD_PSYCHIC_TERRAIN))
|
||||
{
|
||||
ADJUST_SCORE(GOOD_EFFECT);
|
||||
if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_TERRAIN_EXTENDER || HasBattlerSideMoveWithEffect(battlerAtk, EFFECT_TERRAIN_PULSE))
|
||||
ADJUST_SCORE(WEAK_EFFECT);
|
||||
}
|
||||
break;
|
||||
case EFFECT_STEEL_ROLLER:
|
||||
{
|
||||
u32 terrain = gFieldStatuses & STATUS_FIELD_TERRAIN_ANY;
|
||||
if (ShouldClearFieldStatus(battlerAtk, terrain))
|
||||
ADJUST_SCORE(GOOD_EFFECT);
|
||||
if (ShouldSetFieldStatus(battlerDef, terrain))
|
||||
ADJUST_SCORE(DECENT_EFFECT);
|
||||
}
|
||||
break;
|
||||
case EFFECT_ICE_SPINNER:
|
||||
{
|
||||
u32 terrain = gFieldStatuses & STATUS_FIELD_TERRAIN_ANY;
|
||||
if (ShouldClearFieldStatus(battlerAtk, terrain))
|
||||
ADJUST_SCORE(GOOD_EFFECT);
|
||||
if (ShouldSetFieldStatus(battlerDef, terrain))
|
||||
ADJUST_SCORE(DECENT_EFFECT);
|
||||
}
|
||||
break;
|
||||
case EFFECT_PLEDGE:
|
||||
if (isDoubleBattle && HasMoveWithEffect(BATTLE_PARTNER(battlerAtk), EFFECT_PLEDGE))
|
||||
@ -4899,9 +4933,10 @@ case EFFECT_GUARD_SPLIT:
|
||||
case EFFECT_TRICK_ROOM:
|
||||
if (!(gAiThinkingStruct->aiFlags[battlerAtk] & AI_FLAG_POWERFUL_STATUS))
|
||||
{
|
||||
if (!(gFieldStatuses & STATUS_FIELD_TRICK_ROOM) && GetBattlerSideSpeedAverage(battlerAtk) < GetBattlerSideSpeedAverage(battlerDef))
|
||||
if (!(gFieldStatuses & STATUS_FIELD_TRICK_ROOM) && ShouldSetFieldStatus(battlerAtk, STATUS_FIELD_TRICK_ROOM))
|
||||
ADJUST_SCORE(GOOD_EFFECT);
|
||||
else if ((gFieldStatuses & STATUS_FIELD_TRICK_ROOM) && GetBattlerSideSpeedAverage(battlerAtk) >= GetBattlerSideSpeedAverage(battlerDef))
|
||||
// Don't unset it on last turn.
|
||||
else if (gFieldStatuses & STATUS_FIELD_TRICK_ROOM && gFieldTimers.trickRoomTimer != gBattleTurnCounter && ShouldClearFieldStatus(battlerAtk, STATUS_FIELD_TRICK_ROOM))
|
||||
ADJUST_SCORE(GOOD_EFFECT);
|
||||
}
|
||||
break;
|
||||
@ -5929,11 +5964,8 @@ static s32 AI_PowerfulStatus(u32 battlerAtk, u32 battlerDef, u32 move, s32 score
|
||||
ADJUST_SCORE(POWERFUL_STATUS_MOVE);
|
||||
break;
|
||||
case EFFECT_HAIL:
|
||||
if (IsWeatherActive(B_WEATHER_HAIL | B_WEATHER_PRIMAL_ANY) == WEATHER_INACTIVE)
|
||||
ADJUST_SCORE(POWERFUL_STATUS_MOVE);
|
||||
break;
|
||||
case EFFECT_SNOWSCAPE:
|
||||
if (IsWeatherActive(B_WEATHER_SNOW | B_WEATHER_PRIMAL_ANY) == WEATHER_INACTIVE)
|
||||
if (IsWeatherActive(B_WEATHER_ICY_ANY | B_WEATHER_PRIMAL_ANY) == WEATHER_INACTIVE)
|
||||
ADJUST_SCORE(POWERFUL_STATUS_MOVE);
|
||||
break;
|
||||
default:
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
#include "malloc.h"
|
||||
#include "battle.h"
|
||||
#include "battle_anim.h"
|
||||
#include "battle_ai_field_statuses.h"
|
||||
#include "battle_ai_util.h"
|
||||
#include "battle_ai_main.h"
|
||||
#include "battle_ai_switch_items.h"
|
||||
@ -1887,7 +1888,7 @@ bool32 IsMoveEncouragedToHit(u32 battlerAtk, u32 battlerDef, u32 move)
|
||||
|
||||
if ((weather & B_WEATHER_RAIN) && MoveAlwaysHitsInRain(move))
|
||||
return TRUE;
|
||||
if ((weather & (B_WEATHER_HAIL | B_WEATHER_SNOW)) && MoveAlwaysHitsInHailSnow(move))
|
||||
if ((weather & B_WEATHER_ICY_ANY) && MoveAlwaysHitsInHailSnow(move))
|
||||
return TRUE;
|
||||
if (B_MINIMIZE_DMG_ACC >= GEN_6 && (gStatuses3[battlerDef] & STATUS3_MINIMIZED) && MoveIncreasesPowerToMinimizedTargets(move))
|
||||
return TRUE;
|
||||
@ -1929,110 +1930,24 @@ bool32 ShouldTryOHKO(u32 battlerAtk, u32 battlerDef, u32 atkAbility, u32 defAbil
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
bool32 ShouldSetSandstorm(u32 battler, u32 ability, enum ItemHoldEffect holdEffect)
|
||||
bool32 ShouldSetWeather(u32 battler, u32 weather)
|
||||
{
|
||||
if (IsWeatherActive(B_WEATHER_SANDSTORM | B_WEATHER_PRIMAL_ANY) != WEATHER_INACTIVE)
|
||||
return FALSE;
|
||||
|
||||
if (ability == ABILITY_SAND_VEIL
|
||||
|| ability == ABILITY_SAND_RUSH
|
||||
|| ability == ABILITY_SAND_FORCE
|
||||
|| ability == ABILITY_OVERCOAT
|
||||
|| ability == ABILITY_MAGIC_GUARD
|
||||
|| holdEffect == HOLD_EFFECT_SAFETY_GOGGLES
|
||||
|| IS_BATTLER_ANY_TYPE(battler, TYPE_ROCK, TYPE_GROUND, TYPE_STEEL)
|
||||
|| HasMoveWithEffect(battler, EFFECT_SHORE_UP)
|
||||
|| HasMoveWithEffect(battler, EFFECT_WEATHER_BALL))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
return WeatherChecker(battler, weather, FIELD_EFFECT_POSITIVE);
|
||||
}
|
||||
|
||||
bool32 ShouldSetHail(u32 battler, u32 ability, enum ItemHoldEffect holdEffect)
|
||||
bool32 ShouldClearWeather(u32 battler, u32 weather)
|
||||
{
|
||||
if (IsWeatherActive(B_WEATHER_HAIL | B_WEATHER_SNOW | B_WEATHER_PRIMAL_ANY) != WEATHER_INACTIVE)
|
||||
return FALSE;
|
||||
|
||||
if (ability == ABILITY_SNOW_CLOAK
|
||||
|| ability == ABILITY_ICE_BODY
|
||||
|| ability == ABILITY_FORECAST
|
||||
|| ability == ABILITY_SLUSH_RUSH
|
||||
|| ability == ABILITY_MAGIC_GUARD
|
||||
|| ability == ABILITY_OVERCOAT
|
||||
|| holdEffect == HOLD_EFFECT_SAFETY_GOGGLES
|
||||
|| IS_BATTLER_OF_TYPE(battler, TYPE_ICE)
|
||||
|| HasMoveWithFlag(battler, MoveAlwaysHitsInHailSnow)
|
||||
|| HasMoveWithEffect(battler, EFFECT_AURORA_VEIL)
|
||||
|| HasMoveWithEffect(battler, EFFECT_WEATHER_BALL))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
return WeatherChecker(battler, weather, FIELD_EFFECT_NEGATIVE);
|
||||
}
|
||||
|
||||
bool32 ShouldSetRain(u32 battlerAtk, u32 atkAbility, enum ItemHoldEffect holdEffect)
|
||||
bool32 ShouldSetFieldStatus(u32 battler, u32 fieldStatus)
|
||||
{
|
||||
if (IsWeatherActive(B_WEATHER_RAIN | B_WEATHER_PRIMAL_ANY) != WEATHER_INACTIVE)
|
||||
return FALSE;
|
||||
|
||||
if (holdEffect != HOLD_EFFECT_UTILITY_UMBRELLA
|
||||
&& (atkAbility == ABILITY_SWIFT_SWIM
|
||||
|| atkAbility == ABILITY_FORECAST
|
||||
|| atkAbility == ABILITY_HYDRATION
|
||||
|| atkAbility == ABILITY_RAIN_DISH
|
||||
|| atkAbility == ABILITY_DRY_SKIN
|
||||
|| HasMoveWithFlag(battlerAtk, MoveAlwaysHitsInRain)
|
||||
|| HasMoveWithEffect(battlerAtk, EFFECT_WEATHER_BALL)
|
||||
|| HasMoveWithType(battlerAtk, TYPE_WATER)))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
return FieldStatusChecker(battler, fieldStatus, FIELD_EFFECT_POSITIVE);
|
||||
}
|
||||
|
||||
bool32 ShouldSetSun(u32 battlerAtk, u32 atkAbility, enum ItemHoldEffect holdEffect)
|
||||
bool32 ShouldClearFieldStatus(u32 battler, u32 fieldStatus)
|
||||
{
|
||||
if (IsWeatherActive(B_WEATHER_SUN | B_WEATHER_PRIMAL_ANY) != WEATHER_INACTIVE)
|
||||
return FALSE;
|
||||
|
||||
if (holdEffect != HOLD_EFFECT_UTILITY_UMBRELLA
|
||||
&& (atkAbility == ABILITY_CHLOROPHYLL
|
||||
|| atkAbility == ABILITY_FLOWER_GIFT
|
||||
|| atkAbility == ABILITY_FORECAST
|
||||
|| atkAbility == ABILITY_LEAF_GUARD
|
||||
|| atkAbility == ABILITY_SOLAR_POWER
|
||||
|| atkAbility == ABILITY_HARVEST
|
||||
|| HasMoveWithEffect(battlerAtk, EFFECT_SOLAR_BEAM)
|
||||
|| HasMoveWithEffect(battlerAtk, EFFECT_MORNING_SUN)
|
||||
|| HasMoveWithEffect(battlerAtk, EFFECT_SYNTHESIS)
|
||||
|| HasMoveWithEffect(battlerAtk, EFFECT_MOONLIGHT)
|
||||
|| HasMoveWithEffect(battlerAtk, EFFECT_WEATHER_BALL)
|
||||
|| HasMoveWithEffect(battlerAtk, EFFECT_GROWTH)
|
||||
|| HasMoveWithType(battlerAtk, TYPE_FIRE)))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
bool32 ShouldSetSnow(u32 battler, u32 ability, enum ItemHoldEffect holdEffect)
|
||||
{
|
||||
if (IsWeatherActive(B_WEATHER_SNOW | B_WEATHER_HAIL | B_WEATHER_PRIMAL_ANY) != WEATHER_INACTIVE)
|
||||
return FALSE;
|
||||
|
||||
if (ability == ABILITY_SNOW_CLOAK
|
||||
|| ability == ABILITY_ICE_BODY
|
||||
|| ability == ABILITY_FORECAST
|
||||
|| ability == ABILITY_SLUSH_RUSH
|
||||
|| IS_BATTLER_OF_TYPE(battler, TYPE_ICE)
|
||||
|| HasMoveWithFlag(battler, MoveAlwaysHitsInHailSnow)
|
||||
|| HasMoveWithEffect(battler, EFFECT_AURORA_VEIL)
|
||||
|| HasMoveWithEffect(battler, EFFECT_WEATHER_BALL))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
return FieldStatusChecker(battler, fieldStatus, FIELD_EFFECT_NEGATIVE);
|
||||
}
|
||||
|
||||
bool32 IsBattlerDamagedByStatus(u32 battler)
|
||||
@ -3816,7 +3731,7 @@ bool32 ShouldSetScreen(u32 battlerAtk, u32 battlerDef, enum BattleMoveEffects mo
|
||||
{
|
||||
case EFFECT_AURORA_VEIL:
|
||||
// Use only in Hail and only if AI doesn't already have Reflect, Light Screen or Aurora Veil itself active.
|
||||
if ((AI_GetWeather() & (B_WEATHER_HAIL | B_WEATHER_SNOW))
|
||||
if ((AI_GetWeather() & (B_WEATHER_ICY_ANY))
|
||||
&& !(gSideStatuses[atkSide] & (SIDE_STATUS_REFLECT | SIDE_STATUS_LIGHTSCREEN | SIDE_STATUS_AURORA_VEIL)))
|
||||
return TRUE;
|
||||
break;
|
||||
|
||||
@ -431,7 +431,7 @@ AI_DOUBLE_BATTLE_TEST("AI prioritizes Skill Swapping Contrary to allied mons tha
|
||||
// Sandstorm is omitted on purpose.
|
||||
// Tornadus is currently not willing to set up Sandstorm for its ally, but the actual purpose of this test is to demonstrate that Tornadus or Whimsicott will perform standard VGC openers.
|
||||
// Rain Dance, Sunny Day, and Snowscape are the actually important ones; setting up a good Sandstorm test + functionality is less important and will be done in later PRs.
|
||||
AI_DOUBLE_BATTLE_TEST("AI will set up weather for its ally")
|
||||
AI_DOUBLE_BATTLE_TEST("AI sets up weather for its ally")
|
||||
{
|
||||
u32 goodWeather, badWeather, weatherTrigger;
|
||||
u64 aiFlags = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT;
|
||||
@ -440,7 +440,7 @@ AI_DOUBLE_BATTLE_TEST("AI will set up weather for its ally")
|
||||
PARAMETRIZE { goodWeather = MOVE_RAIN_DANCE; badWeather = MOVE_SUNNY_DAY; weatherTrigger = MOVE_THUNDER; }
|
||||
PARAMETRIZE { goodWeather = MOVE_HAIL; badWeather = MOVE_SUNNY_DAY; weatherTrigger = MOVE_BLIZZARD; }
|
||||
PARAMETRIZE { goodWeather = MOVE_SNOWSCAPE; badWeather = MOVE_SUNNY_DAY; weatherTrigger = MOVE_BLIZZARD; }
|
||||
// PARAMETRIZE { goodWeather = MOVE_SANDSTORM; badWeather = MOVE_SUNNY_DAY; weatherTrigger = MOVE_SHORE_UP; }
|
||||
PARAMETRIZE { goodWeather = MOVE_SANDSTORM; badWeather = MOVE_SUNNY_DAY; weatherTrigger = MOVE_SHORE_UP; }
|
||||
PARAMETRIZE { aiFlags |= AI_FLAG_OMNISCIENT | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_PP_STALL_PREVENTION;
|
||||
goodWeather = MOVE_SUNNY_DAY; badWeather = MOVE_RAIN_DANCE; weatherTrigger = MOVE_SOLAR_BEAM; }
|
||||
PARAMETRIZE { aiFlags |= AI_FLAG_OMNISCIENT | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_PP_STALL_PREVENTION;
|
||||
@ -449,20 +449,49 @@ AI_DOUBLE_BATTLE_TEST("AI will set up weather for its ally")
|
||||
goodWeather = MOVE_HAIL; badWeather = MOVE_SUNNY_DAY; weatherTrigger = MOVE_BLIZZARD; }
|
||||
PARAMETRIZE { aiFlags |= AI_FLAG_OMNISCIENT | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_PP_STALL_PREVENTION;
|
||||
goodWeather = MOVE_SNOWSCAPE; badWeather = MOVE_SUNNY_DAY; weatherTrigger = MOVE_BLIZZARD; }
|
||||
// PARAMETRIZE { aiFlags |= AI_FLAG_OMNISCIENT | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_PP_STALL_PREVENTION;
|
||||
// goodWeather = MOVE_SANDSTORM; badWeather = MOVE_SUNNY_DAY; weatherTrigger = MOVE_SHORE_UP; }
|
||||
PARAMETRIZE { aiFlags |= AI_FLAG_OMNISCIENT | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_PP_STALL_PREVENTION;
|
||||
goodWeather = MOVE_SANDSTORM; badWeather = MOVE_SUNNY_DAY; weatherTrigger = MOVE_SHORE_UP; }
|
||||
|
||||
GIVEN {
|
||||
AI_FLAGS(aiFlags);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_TORNADUS) { Ability(ABILITY_PRANKSTER); Moves(goodWeather, badWeather, MOVE_RETURN, MOVE_TAUNT); }
|
||||
OPPONENT(SPECIES_TORNADUS) { Item(ITEM_SAFETY_GOGGLES); Ability(ABILITY_PRANKSTER); Moves(goodWeather, badWeather, MOVE_RETURN, MOVE_TAUNT); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Moves(weatherTrigger, MOVE_EARTH_POWER); }
|
||||
} WHEN {
|
||||
TURN { EXPECT_MOVE(opponentLeft, goodWeather); }
|
||||
}
|
||||
}
|
||||
|
||||
AI_DOUBLE_BATTLE_TEST("AI sets up terrain for its ally")
|
||||
{
|
||||
u32 goodTerrain, badTerrain, terrainTrigger;
|
||||
u64 aiFlags = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT;
|
||||
|
||||
PARAMETRIZE { goodTerrain = MOVE_ELECTRIC_TERRAIN; badTerrain = MOVE_PSYCHIC_TERRAIN; terrainTrigger = MOVE_RISING_VOLTAGE; }
|
||||
PARAMETRIZE { goodTerrain = MOVE_GRASSY_TERRAIN; badTerrain = MOVE_PSYCHIC_TERRAIN; terrainTrigger = MOVE_GRASSY_GLIDE; }
|
||||
PARAMETRIZE { goodTerrain = MOVE_MISTY_TERRAIN; badTerrain = MOVE_PSYCHIC_TERRAIN; terrainTrigger = MOVE_MISTY_EXPLOSION; }
|
||||
PARAMETRIZE { goodTerrain = MOVE_PSYCHIC_TERRAIN; badTerrain = MOVE_ELECTRIC_TERRAIN; terrainTrigger = MOVE_EXPANDING_FORCE; }
|
||||
PARAMETRIZE { aiFlags |= AI_FLAG_OMNISCIENT | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_PP_STALL_PREVENTION;
|
||||
goodTerrain = MOVE_ELECTRIC_TERRAIN; badTerrain = MOVE_PSYCHIC_TERRAIN; terrainTrigger = MOVE_RISING_VOLTAGE; }
|
||||
PARAMETRIZE { aiFlags |= AI_FLAG_OMNISCIENT | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_PP_STALL_PREVENTION;
|
||||
goodTerrain = MOVE_GRASSY_TERRAIN; badTerrain = MOVE_PSYCHIC_TERRAIN; terrainTrigger = MOVE_GRASSY_GLIDE; }
|
||||
PARAMETRIZE { aiFlags |= AI_FLAG_OMNISCIENT | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_PP_STALL_PREVENTION;
|
||||
goodTerrain = MOVE_MISTY_TERRAIN; badTerrain = MOVE_PSYCHIC_TERRAIN; terrainTrigger = MOVE_MISTY_EXPLOSION; }
|
||||
PARAMETRIZE { aiFlags |= AI_FLAG_OMNISCIENT | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_PP_STALL_PREVENTION;
|
||||
goodTerrain = MOVE_PSYCHIC_TERRAIN; badTerrain = MOVE_ELECTRIC_TERRAIN; terrainTrigger = MOVE_EXPANDING_FORCE; }
|
||||
|
||||
GIVEN {
|
||||
AI_FLAGS(aiFlags);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Moves(goodTerrain, badTerrain, MOVE_RETURN, MOVE_TAUNT); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Moves(terrainTrigger, MOVE_EARTH_POWER); }
|
||||
} WHEN {
|
||||
TURN { EXPECT_MOVE(opponentLeft, goodTerrain); }
|
||||
}
|
||||
}
|
||||
|
||||
AI_DOUBLE_BATTLE_TEST("AI uses After You to set up Trick Room")
|
||||
{
|
||||
u32 move;
|
||||
@ -476,7 +505,7 @@ AI_DOUBLE_BATTLE_TEST("AI uses After You to set up Trick Room")
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_DOUBLE_BATTLE);
|
||||
PLAYER(SPECIES_WOBBUFFET) { Speed(4); }
|
||||
PLAYER(SPECIES_WOBBUFFET) { Speed(4); }
|
||||
OPPONENT(SPECIES_INDEEDEE_M) { Speed(5); Moves(MOVE_AFTER_YOU, MOVE_PSYCHIC); }
|
||||
OPPONENT(SPECIES_COMFEY) { Ability(ABILITY_TRIAGE); Speed(5); Moves(MOVE_AFTER_YOU, MOVE_DRAINING_KISS); }
|
||||
OPPONENT(SPECIES_CLEFAIRY) { Speed(3); Moves(move, MOVE_PSYCHIC); }
|
||||
} WHEN {
|
||||
if (move == MOVE_TRICK_ROOM)
|
||||
@ -486,7 +515,32 @@ AI_DOUBLE_BATTLE_TEST("AI uses After You to set up Trick Room")
|
||||
}
|
||||
}
|
||||
|
||||
AI_DOUBLE_BATTLE_TEST("AI uses Trick Room intelligently")
|
||||
{
|
||||
u32 move, ability, speed;
|
||||
|
||||
PARAMETRIZE { move = MOVE_DRAINING_KISS; ability = ABILITY_SYNCHRONIZE; speed = 4; }
|
||||
PARAMETRIZE { move = MOVE_DAZZLING_GLEAM; ability = ABILITY_SYNCHRONIZE; speed = 4; }
|
||||
PARAMETRIZE { move = MOVE_DRAINING_KISS; ability = ABILITY_PSYCHIC_SURGE; speed = 4; }
|
||||
PARAMETRIZE { move = MOVE_DRAINING_KISS; ability = ABILITY_SYNCHRONIZE; speed = 2; }
|
||||
PARAMETRIZE { move = MOVE_DAZZLING_GLEAM; ability = ABILITY_SYNCHRONIZE; speed = 2; }
|
||||
PARAMETRIZE { move = MOVE_DRAINING_KISS; ability = ABILITY_PSYCHIC_SURGE; speed = 2; }
|
||||
|
||||
GIVEN {
|
||||
ASSUME(GetMoveEffect(MOVE_AFTER_YOU) == EFFECT_AFTER_YOU);
|
||||
ASSUME(GetMoveEffect(MOVE_TRICK_ROOM) == EFFECT_TRICK_ROOM);
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_DOUBLE_BATTLE);
|
||||
PLAYER(SPECIES_WOBBUFFET) { Speed(4); }
|
||||
PLAYER(SPECIES_WOBBUFFET) { Speed(speed); }
|
||||
OPPONENT(SPECIES_COMFEY) { Ability(ABILITY_TRIAGE); Speed(5); Moves(move); }
|
||||
OPPONENT(SPECIES_INDEEDEE) { Ability(ability); Speed(3); Moves(MOVE_TRICK_ROOM, MOVE_PSYCHIC); }
|
||||
} WHEN {
|
||||
if (move == MOVE_DRAINING_KISS && ability != ABILITY_PSYCHIC_SURGE && speed > 3)
|
||||
TURN { EXPECT_MOVE(opponentRight, MOVE_TRICK_ROOM); }
|
||||
else
|
||||
TURN { NOT_EXPECT_MOVE(opponentRight, MOVE_TRICK_ROOM); }
|
||||
}
|
||||
}
|
||||
AI_DOUBLE_BATTLE_TEST("AI uses Guard Split to improve its stats")
|
||||
{
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user