mirror of
https://github.com/rh-hideout/pokeemerald-expansion.git
synced 2026-04-26 02:14:22 -05:00
Switch AI sees stat, volatile, status, and HP changes on switchin in calcs (#9124)
This commit is contained in:
parent
070f31e384
commit
2c965d02b6
|
|
@ -322,7 +322,6 @@ bool32 IsBattlerPredictedToSwitch(enum BattlerId battler);
|
|||
enum Move GetIncomingMove(enum BattlerId battler, enum BattlerId opposingBattler, struct AiLogicData *aiData);
|
||||
enum Move GetIncomingMoveSpeedCheck(enum BattlerId battler, enum BattlerId opposingBattler, struct AiLogicData *aiData);
|
||||
bool32 AI_OpponentCanFaintAiWithMod(enum BattlerId battler, u32 healAmount);
|
||||
void SetBattlerFieldStatusForSwitchin(enum BattlerId battler);
|
||||
bool32 ShouldInstructPartner(enum BattlerId partner, enum Move move);
|
||||
bool32 CanMoveBeBouncedBack(enum BattlerId battler, enum Move move);
|
||||
|
||||
|
|
|
|||
|
|
@ -423,5 +423,6 @@ void TryResetConsecutiveUseCounter(enum BattlerId battler);
|
|||
void SetOrClearRageVolatile(void);
|
||||
enum BattlerId GetTargetBySlot(enum BattlerId battlerAtk, enum BattlerId battlerDef);
|
||||
bool32 IsNaturalEnemy(enum Species speciesAttacker, enum Species speciesTarget);
|
||||
enum Stat GetDownloadStat(enum BattlerId battler);
|
||||
|
||||
#endif // GUARD_BATTLE_UTIL_H
|
||||
|
|
|
|||
|
|
@ -35,11 +35,17 @@ struct IncomingHealInfo
|
|||
static bool32 CanUseSuperEffectiveMoveAgainstOpponents(enum BattlerId battler);
|
||||
static bool32 FindMonWithFlagsAndSuperEffective(enum BattlerId battler, u16 flags, u32 moduloPercent);
|
||||
static u32 GetSwitchinHazardsDamage(enum BattlerId battler);
|
||||
static u32 GetSwitchinSingleUseItemHealing(enum BattlerId battler, enum BattlerId opposingBattler, s32 currentHP);
|
||||
static bool32 AI_CanSwitchinAbilityTrapOpponent(enum Ability ability, enum BattlerId opposingBattler);
|
||||
static u32 GetBattlerTypeMatchup(enum BattlerId opposingBattler, enum BattlerId battler);
|
||||
static u32 GetSwitchinHitsToKO(s32 damageTaken, enum BattlerId battler, const struct IncomingHealInfo *healInfo, u32 originalHp);
|
||||
static void GetIncomingHealInfo(enum BattlerId battler, struct IncomingHealInfo *healInfo);
|
||||
static u32 GetWishHealAmountForBattler(enum BattlerId battler);
|
||||
static void SetBattlerStatusForSwitchin(enum BattlerId battler);
|
||||
static void SetBattlerStatStagesForSwitchin(enum BattlerId battler, enum BattlerId opposingBattler, u32 fieldStatus);
|
||||
static void SetBattlerHPChangeForSwitch(enum BattlerId battler, enum BattlerId opposingBattler);
|
||||
static void SetBattlerVolatilesForSwitchin(enum BattlerId battler, u32 weather, u32 fieldStatus);
|
||||
bool32 IsSwitchinTSpikesAffected(enum BattlerId battler);
|
||||
|
||||
static void InitializeSwitchinCandidate(enum BattlerId switchinBattler, u32 monIndex, struct Pokemon *mon)
|
||||
{
|
||||
|
|
@ -48,15 +54,19 @@ static void InitializeSwitchinCandidate(enum BattlerId switchinBattler, u32 monI
|
|||
// Setup switchin battler data
|
||||
gAiThinkingStruct->saved[switchinBattler].saved = TRUE;
|
||||
SetBattlerAiData(switchinBattler, gAiLogicData);
|
||||
SetBattlerFieldStatusForSwitchin(switchinBattler);
|
||||
u32 switchinWeather = AI_GetSwitchinWeather(switchinBattler);
|
||||
u32 switchinFieldStatus = AI_GetSwitchinFieldStatus(switchinBattler);
|
||||
SetBattlerVolatilesForSwitchin(switchinBattler, switchinWeather, switchinFieldStatus);
|
||||
SetBattlerStatusForSwitchin(switchinBattler);
|
||||
gBattlerPartyIndexes[switchinBattler] = monIndex;
|
||||
for (enum BattlerId battlerIndex = 0; battlerIndex < gBattlersCount; battlerIndex++)
|
||||
{
|
||||
if (switchinBattler == battlerIndex || !IsBattlerAlive(battlerIndex))
|
||||
continue;
|
||||
|
||||
CalcBattlerAiMovesData(gAiLogicData, switchinBattler, battlerIndex, AI_GetSwitchinWeather(switchinBattler), AI_GetSwitchinFieldStatus(switchinBattler));
|
||||
CalcBattlerAiMovesData(gAiLogicData, battlerIndex, switchinBattler, AI_GetSwitchinWeather(switchinBattler), AI_GetSwitchinFieldStatus(switchinBattler));
|
||||
SetBattlerStatStagesForSwitchin(switchinBattler, battlerIndex, switchinFieldStatus);
|
||||
SetBattlerHPChangeForSwitch(switchinBattler, battlerIndex);
|
||||
CalcBattlerAiMovesData(gAiLogicData, switchinBattler, battlerIndex, switchinWeather, switchinFieldStatus);
|
||||
CalcBattlerAiMovesData(gAiLogicData, battlerIndex, switchinBattler, switchinWeather, switchinFieldStatus);
|
||||
}
|
||||
|
||||
gBattlerPartyIndexes[switchinBattler] = storeCurrBattlerPartyIndex;
|
||||
|
|
@ -218,6 +228,35 @@ static bool32 AreStatsRaised(enum BattlerId battler)
|
|||
return (buffedStatsValue > STAY_IN_STATS_RAISED);
|
||||
}
|
||||
|
||||
bool32 IsSwitchinTSpikesAffected(enum BattlerId battler)
|
||||
{
|
||||
enum Ability ability = gAiLogicData->abilities[battler];
|
||||
enum HoldEffect heldItemEffect = gAiLogicData->holdEffects[battler];
|
||||
enum BattlerId opposingBattler = GetOppositeBattler(battler);
|
||||
bool32 ignoreItem = ((gFieldStatuses & STATUS_FIELD_MAGIC_ROOM) || ability == ABILITY_KLUTZ);
|
||||
if (gBattleMons[battler].status1 & STATUS1_ANY)
|
||||
return FALSE;
|
||||
if (IS_BATTLER_ANY_TYPE(battler, TYPE_POISON, TYPE_STEEL))
|
||||
return FALSE;
|
||||
if (ability == ABILITY_IMMUNITY || IsAbilityOnSide(battler, ABILITY_PASTEL_VEIL))
|
||||
return FALSE;
|
||||
if ((heldItemEffect == HOLD_EFFECT_HEAVY_DUTY_BOOTS || heldItemEffect == HOLD_EFFECT_CURE_PSN || heldItemEffect == HOLD_EFFECT_CURE_STATUS) && !ignoreItem)
|
||||
return FALSE;
|
||||
if (!AI_IsBattlerGrounded(battler))
|
||||
return FALSE;
|
||||
if (IsMistyTerrainAffected(battler, ability, heldItemEffect, gFieldStatuses))
|
||||
return FALSE;
|
||||
if (IsLeafGuardProtected(battler, ability))
|
||||
return FALSE;
|
||||
if (IsShieldsDownProtected(battler, ability))
|
||||
return FALSE;
|
||||
if (IsFlowerVeilProtected(battler))
|
||||
return FALSE;
|
||||
if (IsSafeguardProtected(opposingBattler, battler, gAiLogicData->abilities[opposingBattler]))
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static inline bool32 SetSwitchinAndSwitch(enum BattlerId battler, u32 switchinId)
|
||||
{
|
||||
gBattleStruct->AI_monToSwitchIntoId[battler] = switchinId;
|
||||
|
|
@ -1488,6 +1527,51 @@ bool32 IsSwitchinValid(enum BattlerId battler)
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
static u32 GetSwitchinSingleUseItemHealing(enum BattlerId battler, enum BattlerId opposingBattler, s32 currentHP)
|
||||
{
|
||||
enum Item aiItem = gAiLogicData->items[battler];
|
||||
u32 maxHP = gBattleMons[battler].maxHP;
|
||||
s32 itemHeal = 0;
|
||||
|
||||
// Check if we're at a single use healing item threshold
|
||||
if (currentHP <= 0
|
||||
|| gAiLogicData->abilities[battler] == ABILITY_KLUTZ
|
||||
|| (gAiLogicData->abilities[opposingBattler] == ABILITY_UNNERVE && GetItemPocket(aiItem) == POCKET_BERRIES))
|
||||
return itemHeal;
|
||||
|
||||
switch (GetItemHoldEffect(aiItem))
|
||||
{
|
||||
case HOLD_EFFECT_RESTORE_HP:
|
||||
if (currentHP < maxHP / 2)
|
||||
itemHeal = GetItemHoldEffectParam(aiItem);
|
||||
break;
|
||||
case HOLD_EFFECT_RESTORE_PCT_HP:
|
||||
if (currentHP < maxHP / 2)
|
||||
{
|
||||
itemHeal = maxHP / GetItemHoldEffectParam(aiItem);
|
||||
if (itemHeal == 0)
|
||||
itemHeal = 1;
|
||||
}
|
||||
break;
|
||||
case HOLD_EFFECT_CONFUSE_SPICY:
|
||||
case HOLD_EFFECT_CONFUSE_DRY:
|
||||
case HOLD_EFFECT_CONFUSE_SWEET:
|
||||
case HOLD_EFFECT_CONFUSE_BITTER:
|
||||
case HOLD_EFFECT_CONFUSE_SOUR:
|
||||
if (currentHP < maxHP / CONFUSE_BERRY_HP_FRACTION)
|
||||
{
|
||||
itemHeal = maxHP / GetItemHoldEffectParam(aiItem);
|
||||
if (itemHeal == 0)
|
||||
itemHeal = 1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return itemHeal;
|
||||
}
|
||||
|
||||
// Gets hazard damage
|
||||
static u32 GetSwitchinHazardsDamage(enum BattlerId battler)
|
||||
{
|
||||
|
|
@ -1681,7 +1765,6 @@ static u32 GetSwitchinRecurringDamage(enum BattlerId battler)
|
|||
static u32 GetSwitchinStatusDamage(enum BattlerId battler)
|
||||
{
|
||||
u8 tSpikesLayers = gSideTimers[GetBattlerSide(battler)].toxicSpikesAmount;
|
||||
enum HoldEffect heldItemEffect = gAiLogicData->holdEffects[battler];
|
||||
u32 status = gBattleMons[battler].status1;
|
||||
enum Ability ability = gAiLogicData->abilities[battler];
|
||||
u32 maxHP = gBattleMons[battler].maxHP;
|
||||
|
|
@ -1729,13 +1812,7 @@ static u32 GetSwitchinStatusDamage(enum BattlerId battler)
|
|||
|
||||
// Apply hypothetical poisoning from Toxic Spikes, which means the first turn of damage already added in GetSwitchinHazardsDamage
|
||||
// Do this last to skip one iteration of Poison / Toxic damage, and start counting Toxic damage one turn later.
|
||||
if (tSpikesLayers != 0 && (!IS_BATTLER_ANY_TYPE(battler, TYPE_POISON, TYPE_STEEL)
|
||||
&& ability != ABILITY_IMMUNITY && ability != ABILITY_POISON_HEAL && ability != ABILITY_PASTEL_VEIL
|
||||
&& status == 0
|
||||
&& !(heldItemEffect == HOLD_EFFECT_HEAVY_DUTY_BOOTS
|
||||
&& (((gFieldStatuses & STATUS_FIELD_MAGIC_ROOM) || ability == ABILITY_KLUTZ)))
|
||||
&& heldItemEffect != HOLD_EFFECT_CURE_PSN && heldItemEffect != HOLD_EFFECT_CURE_STATUS
|
||||
&& AI_IsBattlerGrounded(battler)))
|
||||
if (tSpikesLayers != 0 && IsSwitchinTSpikesAffected(battler))
|
||||
{
|
||||
if (tSpikesLayers == 1)
|
||||
{
|
||||
|
|
@ -1777,7 +1854,7 @@ static u32 GetSwitchinHitsToKO(s32 damageTaken, enum BattlerId battler, const st
|
|||
u32 statusDamage = GetSwitchinStatusDamage(battler);
|
||||
u32 hitsToKO = 0;
|
||||
u16 maxHP = gBattleMons[battler].maxHP, item = gAiLogicData->items[battler], heldItemEffect = GetItemHoldEffect(item);
|
||||
u8 weatherDuration = gBattleStruct->weatherDuration, holdEffectParam = GetItemHoldEffectParam(item);
|
||||
u8 weatherDuration = gBattleStruct->weatherDuration;
|
||||
enum BattlerId opposingBattler = GetOppositeBattler(battler);
|
||||
enum Ability opposingAbility = gAiLogicData->abilities[opposingBattler], ability = gAiLogicData->abilities[battler];
|
||||
bool32 usedSingleUseHealingItem = FALSE, opponentCanBreakMold = IsMoldBreakerTypeAbility(opposingBattler, opposingAbility);
|
||||
|
|
@ -1811,37 +1888,9 @@ static u32 GetSwitchinHitsToKO(s32 damageTaken, enum BattlerId battler, const st
|
|||
currentHP = currentHP - weatherImpact;
|
||||
|
||||
// Check if we're at a single use healing item threshold
|
||||
if (currentHP > 0 && gAiLogicData->abilities[battler] != ABILITY_KLUTZ && usedSingleUseHealingItem == FALSE
|
||||
&& !(opposingAbility == ABILITY_UNNERVE && GetItemPocket(item) == POCKET_BERRIES))
|
||||
if (usedSingleUseHealingItem == FALSE)
|
||||
{
|
||||
switch (heldItemEffect)
|
||||
{
|
||||
case HOLD_EFFECT_RESTORE_HP:
|
||||
if (currentHP < maxHP / 2)
|
||||
singleUseItemHeal = holdEffectParam;
|
||||
break;
|
||||
case HOLD_EFFECT_RESTORE_PCT_HP:
|
||||
if (currentHP < maxHP / 2)
|
||||
{
|
||||
singleUseItemHeal = maxHP / holdEffectParam;
|
||||
if (singleUseItemHeal == 0)
|
||||
singleUseItemHeal = 1;
|
||||
}
|
||||
break;
|
||||
case HOLD_EFFECT_CONFUSE_SPICY:
|
||||
case HOLD_EFFECT_CONFUSE_DRY:
|
||||
case HOLD_EFFECT_CONFUSE_SWEET:
|
||||
case HOLD_EFFECT_CONFUSE_BITTER:
|
||||
case HOLD_EFFECT_CONFUSE_SOUR:
|
||||
if (currentHP < maxHP / CONFUSE_BERRY_HP_FRACTION)
|
||||
{
|
||||
singleUseItemHeal = maxHP / holdEffectParam;
|
||||
if (singleUseItemHeal == 0)
|
||||
singleUseItemHeal = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
singleUseItemHeal = GetSwitchinSingleUseItemHealing(battler, opposingBattler, currentHP);
|
||||
// If we used one, apply it without overcapping our maxHP
|
||||
if (singleUseItemHeal > 0)
|
||||
{
|
||||
|
|
@ -2611,3 +2660,194 @@ u32 AI_SelectRevivalBlessingMon(enum BattlerId battler)
|
|||
|
||||
return bestMonId;
|
||||
}
|
||||
|
||||
static void SetBattlerStatusForSwitchin(enum BattlerId battler)
|
||||
{
|
||||
u32 tSpikesLayers = gSideTimers[GetBattlerSide(battler)].toxicSpikesAmount;
|
||||
if (tSpikesLayers != 0 && IsSwitchinTSpikesAffected(battler))
|
||||
{
|
||||
if (tSpikesLayers == 1)
|
||||
gBattleMons[battler].status1 = STATUS1_POISON;
|
||||
if (tSpikesLayers == 2)
|
||||
{
|
||||
gBattleMons[battler].status1 = STATUS1_TOXIC_POISON;
|
||||
gBattleMons[battler].status1 += STATUS1_TOXIC_TURN(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void SetBattlerStatStagesForSwitchin(enum BattlerId battler, enum BattlerId opposingBattler, u32 fieldStatus)
|
||||
{
|
||||
u32 aiAbility = gAiLogicData->abilities[battler];
|
||||
enum Item aiItem = gAiLogicData->items[battler];
|
||||
bool32 isStickyWebsAffected = (IsHazardOnSide(GetBattlerSide(battler), HAZARDS_STICKY_WEB) && IsBattlerAffectedByHazards(battler, GetItemHoldEffect(aiItem), FALSE) && IsBattlerGrounded(battler, gAiLogicData->abilities[battler], GetItemHoldEffect(aiItem)));
|
||||
bool32 opponentStatDrop = FALSE;
|
||||
|
||||
// Ability stat changes
|
||||
switch(aiAbility)
|
||||
{
|
||||
case ABILITY_INTREPID_SWORD:
|
||||
gBattleMons[battler].statStages[STAT_ATK] += 1;
|
||||
break;
|
||||
case ABILITY_DAUNTLESS_SHIELD:
|
||||
gBattleMons[battler].statStages[STAT_DEF] += 1;
|
||||
break;
|
||||
case ABILITY_SUPREME_OVERLORD:
|
||||
break;
|
||||
case ABILITY_DOWNLOAD:
|
||||
gBattleMons[battler].statStages[GetDownloadStat(battler)] += 1;
|
||||
break;
|
||||
case ABILITY_INTIMIDATE:
|
||||
if (CanLowerStat(battler, opposingBattler, gAiLogicData, STAT_ATK))
|
||||
{
|
||||
if (gAiLogicData->abilities[opposingBattler] == ABILITY_CONTRARY)
|
||||
{
|
||||
gBattleMons[opposingBattler].statStages[STAT_ATK] += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
opponentStatDrop = TRUE;
|
||||
gBattleMons[opposingBattler].statStages[STAT_ATK] -= 1;
|
||||
if (gAiLogicData->abilities[opposingBattler] == ABILITY_DEFIANT)
|
||||
gBattleMons[opposingBattler].statStages[STAT_ATK] += 2;
|
||||
if (gAiLogicData->abilities[opposingBattler] == ABILITY_COMPETITIVE)
|
||||
gBattleMons[opposingBattler].statStages[STAT_SPATK] += 2;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ABILITY_SUPERSWEET_SYRUP:
|
||||
if (CanLowerStat(battler, opposingBattler, gAiLogicData, STAT_EVASION))
|
||||
{
|
||||
if (gAiLogicData->abilities[opposingBattler] == ABILITY_CONTRARY)
|
||||
{
|
||||
gBattleMons[opposingBattler].statStages[STAT_EVASION] += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
opponentStatDrop = TRUE;
|
||||
gBattleMons[opposingBattler].statStages[STAT_EVASION] -= 1;
|
||||
if (gAiLogicData->abilities[opposingBattler] == ABILITY_DEFIANT)
|
||||
gBattleMons[opposingBattler].statStages[STAT_ATK] += 2;
|
||||
if (gAiLogicData->abilities[opposingBattler] == ABILITY_COMPETITIVE)
|
||||
gBattleMons[opposingBattler].statStages[STAT_SPATK] += 2;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ABILITY_WIND_RIDER:
|
||||
if (gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_TAILWIND)
|
||||
gBattleMons[battler].statStages[STAT_ATK] += 1;
|
||||
break;
|
||||
case ABILITY_DEFIANT:
|
||||
if (isStickyWebsAffected)
|
||||
gBattleMons[battler].statStages[STAT_ATK] += 2;
|
||||
break;
|
||||
case ABILITY_COMPETITIVE:
|
||||
if (isStickyWebsAffected)
|
||||
gBattleMons[battler].statStages[STAT_SPATK] += 2;
|
||||
break;
|
||||
case ABILITY_CONTRARY:
|
||||
if (isStickyWebsAffected)
|
||||
gBattleMons[battler].statStages[STAT_SPEED] += 1;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Item stat changes
|
||||
switch(GetItemHoldEffect(aiItem))
|
||||
{
|
||||
case HOLD_EFFECT_TERRAIN_SEED:
|
||||
u32 seedParam = GetItemHoldEffectParam(aiItem);
|
||||
if ((seedParam == HOLD_EFFECT_PARAM_ELECTRIC_TERRAIN && (fieldStatus & STATUS_FIELD_ELECTRIC_TERRAIN))
|
||||
|| (seedParam == HOLD_EFFECT_PARAM_GRASSY_TERRAIN && (fieldStatus & STATUS_FIELD_GRASSY_TERRAIN))
|
||||
|| (seedParam == HOLD_EFFECT_PARAM_MISTY_TERRAIN && (fieldStatus & STATUS_FIELD_MISTY_TERRAIN))
|
||||
|| (seedParam == HOLD_EFFECT_PARAM_PSYCHIC_TERRAIN && (fieldStatus & STATUS_FIELD_PSYCHIC_TERRAIN)))
|
||||
gBattleMons[battler].statStages[STAT_DEF] += 1;
|
||||
break;
|
||||
case HOLD_EFFECT_ATTACK_UP:
|
||||
if (HasEnoughHpToEatBerry(battler, aiAbility, GetItemHoldEffectParam(aiItem), aiItem))
|
||||
gBattleMons[battler].statStages[STAT_ATK] += 1;
|
||||
break;
|
||||
case HOLD_EFFECT_DEFENSE_UP:
|
||||
if (HasEnoughHpToEatBerry(battler, aiAbility, GetItemHoldEffectParam(aiItem), aiItem))
|
||||
gBattleMons[battler].statStages[STAT_DEF] += 1;
|
||||
break;
|
||||
case HOLD_EFFECT_SPEED_UP:
|
||||
if (HasEnoughHpToEatBerry(battler, aiAbility, GetItemHoldEffectParam(aiItem), aiItem))
|
||||
gBattleMons[battler].statStages[STAT_SPEED] += 1;
|
||||
break;
|
||||
case HOLD_EFFECT_SP_ATTACK_UP:
|
||||
if (HasEnoughHpToEatBerry(battler, aiAbility, GetItemHoldEffectParam(aiItem), aiItem))
|
||||
gBattleMons[battler].statStages[STAT_SPATK] += 1;
|
||||
break;
|
||||
case HOLD_EFFECT_SP_DEFENSE_UP:
|
||||
if (HasEnoughHpToEatBerry(battler, aiAbility, GetItemHoldEffectParam(aiItem), aiItem))
|
||||
gBattleMons[battler].statStages[STAT_SPDEF] += 1;
|
||||
break;
|
||||
case HOLD_EFFECT_ROOM_SERVICE:
|
||||
if (gFieldStatuses & STATUS_FIELD_TRICK_ROOM)
|
||||
gBattleMons[battler].statStages[STAT_SPEED] -= 1;
|
||||
case HOLD_EFFECT_MIRROR_HERB:
|
||||
if (opponentStatDrop && gAiLogicData->abilities[opposingBattler] == ABILITY_DEFIANT)
|
||||
gBattleMons[battler].statStages[STAT_ATK] += 2;
|
||||
if (opponentStatDrop && gAiLogicData->abilities[opposingBattler] == ABILITY_COMPETITIVE)
|
||||
gBattleMons[battler].statStages[STAT_SPATK] += 2;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Hazard stat changes
|
||||
if (isStickyWebsAffected && GetItemHoldEffect(aiItem) != HOLD_EFFECT_WHITE_HERB)
|
||||
gBattleMons[battler].statStages[STAT_SPEED] -= 1;
|
||||
}
|
||||
|
||||
static void SetBattlerHPChangeForSwitch(enum BattlerId battler, enum BattlerId opposingBattler)
|
||||
{
|
||||
s32 maxHP = gBattleMons[battler].maxHP;
|
||||
s32 currentHP = gBattleMons[battler].hp - GetSwitchinHazardsDamage(battler);
|
||||
s32 itemHeal = GetSwitchinSingleUseItemHealing(battler, opposingBattler, currentHP);
|
||||
|
||||
if (itemHeal > 0)
|
||||
{
|
||||
if ((currentHP + itemHeal) > maxHP)
|
||||
currentHP = maxHP;
|
||||
else
|
||||
currentHP = currentHP + itemHeal;
|
||||
}
|
||||
gBattleMons[battler].hp = currentHP;
|
||||
}
|
||||
|
||||
// Set potential field effect from ability for switch in
|
||||
static void SetBattlerVolatilesForSwitchin(enum BattlerId battler, u32 weather, u32 fieldStatus)
|
||||
{
|
||||
enum Item aiItem = gAiLogicData->items[battler];
|
||||
switch (gAiLogicData->abilities[battler])
|
||||
{
|
||||
case ABILITY_VESSEL_OF_RUIN:
|
||||
gBattleMons[battler].volatiles.vesselOfRuin = TRUE;
|
||||
break;
|
||||
case ABILITY_SWORD_OF_RUIN:
|
||||
gBattleMons[battler].volatiles.swordOfRuin = TRUE;
|
||||
break;
|
||||
case ABILITY_TABLETS_OF_RUIN:
|
||||
gBattleMons[battler].volatiles.tabletsOfRuin = TRUE;
|
||||
break;
|
||||
case ABILITY_BEADS_OF_RUIN:
|
||||
gBattleMons[battler].volatiles.beadsOfRuin = TRUE;
|
||||
break;
|
||||
case ABILITY_QUARK_DRIVE:
|
||||
if ((fieldStatus & STATUS_FIELD_ELECTRIC_TERRAIN) || GetItemHoldEffect(aiItem) == HOLD_EFFECT_BOOSTER_ENERGY)
|
||||
gBattleMons[battler].volatiles.boosterEnergyActivated = TRUE;
|
||||
break;
|
||||
case ABILITY_PROTOSYNTHESIS:
|
||||
if (((weather & B_WEATHER_SUN) && HasWeatherEffect()) || GetItemHoldEffect(aiItem) == HOLD_EFFECT_BOOSTER_ENERGY)
|
||||
gBattleMons[battler].volatiles.boosterEnergyActivated = TRUE;
|
||||
break;
|
||||
case ABILITY_WIND_POWER:
|
||||
if (gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_TAILWIND)
|
||||
gBattleMons[battler].volatiles.chargeTimer = 2;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1873,6 +1873,7 @@ u32 AI_GetSwitchinWeather(enum BattlerId battler)
|
|||
case ABILITY_DRIZZLE:
|
||||
return B_WEATHER_RAIN_NORMAL;
|
||||
case ABILITY_DROUGHT:
|
||||
case ABILITY_ORICHALCUM_PULSE:
|
||||
return B_WEATHER_SUN_NORMAL;
|
||||
case ABILITY_SAND_STREAM:
|
||||
return B_WEATHER_SANDSTORM;
|
||||
|
|
@ -4531,28 +4532,6 @@ void FreeRestoreAiLogicData(struct AiLogicData *savedAiLogicData)
|
|||
Free(savedAiLogicData);
|
||||
}
|
||||
|
||||
// Set potential field effect from ability for switch in
|
||||
void SetBattlerFieldStatusForSwitchin(enum BattlerId battler)
|
||||
{
|
||||
switch (gAiLogicData->abilities[battler])
|
||||
{
|
||||
case ABILITY_VESSEL_OF_RUIN:
|
||||
gBattleMons[battler].volatiles.vesselOfRuin = TRUE;
|
||||
break;
|
||||
case ABILITY_SWORD_OF_RUIN:
|
||||
gBattleMons[battler].volatiles.swordOfRuin = TRUE;
|
||||
break;
|
||||
case ABILITY_TABLETS_OF_RUIN:
|
||||
gBattleMons[battler].volatiles.tabletsOfRuin = TRUE;
|
||||
break;
|
||||
case ABILITY_BEADS_OF_RUIN:
|
||||
gBattleMons[battler].volatiles.beadsOfRuin = TRUE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// party logic
|
||||
s32 CountUsablePartyMons(enum BattlerId battlerId)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -3239,28 +3239,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, enum BattlerId battler, enum
|
|||
case ABILITY_DOWNLOAD:
|
||||
if (shouldAbilityTrigger)
|
||||
{
|
||||
enum Stat statId;
|
||||
enum BattlerId opposingBattler;
|
||||
u32 opposingDef = 0, opposingSpDef = 0;
|
||||
|
||||
opposingBattler = BATTLE_OPPOSITE(battler);
|
||||
for (i = 0; i < 2; opposingBattler ^= BIT_FLANK, i++)
|
||||
{
|
||||
if (IsBattlerAlive(opposingBattler))
|
||||
{
|
||||
opposingDef += gBattleMons[opposingBattler].defense
|
||||
* gStatStageRatios[gBattleMons[opposingBattler].statStages[STAT_DEF]][0]
|
||||
/ gStatStageRatios[gBattleMons[opposingBattler].statStages[STAT_DEF]][1];
|
||||
opposingSpDef += gBattleMons[opposingBattler].spDefense
|
||||
* gStatStageRatios[gBattleMons[opposingBattler].statStages[STAT_SPDEF]][0]
|
||||
/ gStatStageRatios[gBattleMons[opposingBattler].statStages[STAT_SPDEF]][1];
|
||||
}
|
||||
}
|
||||
|
||||
if (opposingDef < opposingSpDef)
|
||||
statId = STAT_ATK;
|
||||
else
|
||||
statId = STAT_SPATK;
|
||||
enum Stat statId = GetDownloadStat(battler);
|
||||
|
||||
if (CompareStat(battler, statId, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility))
|
||||
{
|
||||
|
|
@ -10939,3 +10918,28 @@ bool32 IsNaturalEnemy(enum Species speciesAttacker, enum Species speciesTarget)
|
|||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
enum Stat GetDownloadStat(enum BattlerId battler)
|
||||
{
|
||||
enum BattlerId opposingBattler;
|
||||
u32 opposingDef = 0, opposingSpDef = 0;
|
||||
|
||||
opposingBattler = BATTLE_OPPOSITE(battler);
|
||||
for (u32 i = 0; i < 2; opposingBattler ^= BIT_FLANK, i++)
|
||||
{
|
||||
if (IsBattlerAlive(opposingBattler))
|
||||
{
|
||||
opposingDef += gBattleMons[opposingBattler].defense
|
||||
* gStatStageRatios[gBattleMons[opposingBattler].statStages[STAT_DEF]][0]
|
||||
/ gStatStageRatios[gBattleMons[opposingBattler].statStages[STAT_DEF]][1];
|
||||
opposingSpDef += gBattleMons[opposingBattler].spDefense
|
||||
* gStatStageRatios[gBattleMons[opposingBattler].statStages[STAT_SPDEF]][0]
|
||||
/ gStatStageRatios[gBattleMons[opposingBattler].statStages[STAT_SPDEF]][1];
|
||||
}
|
||||
}
|
||||
|
||||
if (opposingDef < opposingSpDef)
|
||||
return STAT_ATK;
|
||||
else
|
||||
return STAT_SPATK;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2244,3 +2244,60 @@ AI_DOUBLE_BATTLE_TEST("AI can switch out both mons on the same turn in double ba
|
|||
TURN { EXPECT_SWITCH(opponentLeft, 2); EXPECT_SWITCH(opponentRight, 3); }
|
||||
}
|
||||
}
|
||||
|
||||
AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: AI sees stat stage changes on switchin when doing switching calcs")
|
||||
{
|
||||
GIVEN {
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_OMNISCIENT);
|
||||
PLAYER(SPECIES_LUXIO) { Speed(2); Ability(ABILITY_GUTS); Moves(MOVE_SCRATCH); }
|
||||
OPPONENT(SPECIES_ZIGZAGOON) { Speed(1); HP(1); MaxHP(100); Moves(MOVE_SCRATCH); }
|
||||
OPPONENT(SPECIES_LUXIO) { Speed(1); Ability(ABILITY_GUTS); Moves(MOVE_SCRATCH); }
|
||||
OPPONENT(SPECIES_LUXIO) { Speed(1); Ability(ABILITY_INTIMIDATE); Moves(MOVE_SCRATCH); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_SCRATCH); EXPECT_MOVE(opponent, MOVE_SCRATCH); EXPECT_SEND_OUT(opponent, 2); }
|
||||
}
|
||||
}
|
||||
|
||||
AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: AI sees status changes on switchin when doing switching calcs")
|
||||
{
|
||||
GIVEN {
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_OMNISCIENT);
|
||||
PLAYER(SPECIES_ZIGZAGOON) { Speed(2); HP(1); MaxHP(100); Moves(MOVE_TOXIC_SPIKES); }
|
||||
PLAYER(SPECIES_LUXIO) { Speed(2); Ability(ABILITY_GUTS); Moves(MOVE_SCRATCH); }
|
||||
OPPONENT(SPECIES_ZIGZAGOON) { Speed(1); HP(1); MaxHP(100); Moves(MOVE_SCRATCH); }
|
||||
OPPONENT(SPECIES_LUXIO) { Speed(1); Ability(ABILITY_INTIMIDATE); Moves(MOVE_SCRATCH); }
|
||||
OPPONENT(SPECIES_LUXIO) { Speed(1); Ability(ABILITY_GUTS); Moves(MOVE_SCRATCH); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_TOXIC_SPIKES); EXPECT_MOVE(opponent, MOVE_SCRATCH); SEND_OUT(player, 1); }
|
||||
TURN { MOVE(player, MOVE_SCRATCH); EXPECT_MOVE(opponent, MOVE_SCRATCH); EXPECT_SEND_OUT(opponent, 2); }
|
||||
}
|
||||
}
|
||||
|
||||
AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: AI sees volate changes on switchin when doing switching calcs")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(GetItemHoldEffect(ITEM_BOOSTER_ENERGY) == HOLD_EFFECT_BOOSTER_ENERGY);
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_OMNISCIENT);
|
||||
PLAYER(SPECIES_ROARING_MOON) { Speed(2); Moves(MOVE_SCRATCH); }
|
||||
OPPONENT(SPECIES_ZIGZAGOON) { Speed(1); HP(1); MaxHP(100); Moves(MOVE_SCRATCH); }
|
||||
OPPONENT(SPECIES_ROARING_MOON) { Speed(1); Moves(MOVE_SCRATCH); }
|
||||
OPPONENT(SPECIES_ROARING_MOON) { Speed(1); Moves(MOVE_SCRATCH); Item(ITEM_BOOSTER_ENERGY); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_SCRATCH); EXPECT_MOVE(opponent, MOVE_SCRATCH); EXPECT_SEND_OUT(opponent, 2); }
|
||||
}
|
||||
}
|
||||
|
||||
AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: AI sees HP changes on switchin when doing switchin calcs")
|
||||
{
|
||||
GIVEN {
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_OMNISCIENT);
|
||||
PLAYER(SPECIES_ZIGZAGOON) { Speed(2); HP(1); MaxHP(100); Moves(MOVE_SPIKES); }
|
||||
PLAYER(SPECIES_BLAZIKEN) { Speed(2); HP(130); MaxHP(400); Ability(ABILITY_SPEED_BOOST); Moves(MOVE_EMBER); }
|
||||
OPPONENT(SPECIES_ZIGZAGOON) { Speed(1); HP(1); MaxHP(100); Moves(MOVE_SCRATCH); }
|
||||
OPPONENT(SPECIES_BLAZIKEN) { Speed(1); HP(180); MaxHP(400); Ability(ABILITY_SPEED_BOOST); Moves(MOVE_EMBER); }
|
||||
OPPONENT(SPECIES_BLAZIKEN) { Speed(1); HP(180); MaxHP(400); Ability(ABILITY_BLAZE); Moves(MOVE_EMBER); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_SPIKES); EXPECT_MOVE(opponent, MOVE_SCRATCH); SEND_OUT(player, 1); }
|
||||
TURN { MOVE(player, MOVE_EMBER); EXPECT_MOVE(opponent, MOVE_SCRATCH); EXPECT_SEND_OUT(opponent, 2); }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user