Removes duplicate code in AI functions (#5457)

* Make functions CanAbilityAbsorbMove and CanAbilityBlockMove

* clean up

* Replace ai code with CanAbilityBlockMove / CanAbilityAbsorbMove

* Adds CanPartnerAbilityBlockMove

* Use switches instead of if-blocks

* solve potential bug in singles with spread attacks

* fix test

* revert other fix

* flash fire does not increase a stat

* left a comment for a future test and addition

* remove more checks

* comment was incorrect. it only applies to storm drain and lightning rod

* revert ndebug

* revert minor change

* Update src/battle_util.c
This commit is contained in:
Alex 2024-10-09 15:50:59 +02:00 committed by GitHub
parent 607c247ee0
commit 1454644376
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 230 additions and 219 deletions

View file

@ -378,6 +378,7 @@ struct AiLogicData
u8 ejectPackSwitch:1; // Tracks whether current switch out was from Eject Pack
u8 padding:5;
u8 shouldSwitch; // Stores result of ShouldSwitch, which decides whether a mon should be switched out
u8 aiCalcInProgress:1;
};
struct AI_ThinkingStruct
@ -716,7 +717,6 @@ struct BattleStruct
} multiBuffer;
u8 wishPerishSongState;
u8 wishPerishSongBattlerId;
u8 aiCalcInProgress:1;
u8 overworldWeatherDone:1;
u8 startingStatusDone:1;
u8 isAtkCancelerForCalledMove:1; // Certain cases in atk canceler should only be checked once, when the original move is called, however others need to be checked the twice.

View file

@ -20,6 +20,23 @@
#define MOVE_LIMITATION_PLACEHOLDER (1 << 15)
#define MOVE_LIMITATIONS_ALL 0xFFFF
enum MoveBlocked
{
MOVE_BLOCKED_BY_NO_ABILITY,
MOVE_BLOCKED_BY_SOUNDPROOF_OR_BULLETPROOF,
MOVE_BLOCKED_BY_DAZZLING,
MOVE_BLOCKED_BY_PARTNER_DAZZLING,
MOVE_BLOCKED_BY_GOOD_AS_GOLD,
};
enum MoveAbsorbed
{
MOVE_ABSORBED_BY_NO_ABILITY,
MOVE_ABSORBED_BY_DRAIN_HP_ABILITY,
MOVE_ABSORBED_BY_STAT_INCREASE_ABILITY,
MOVE_ABSORBED_BY_BOOST_FLASH_FIRE,
};
enum {
ABILITYEFFECT_ON_SWITCHIN,
ABILITYEFFECT_ENDTURN,
@ -161,6 +178,9 @@ void SetAtkCancellerForCalledMove(void);
u8 AtkCanceller_UnableToUseMove2(void);
bool32 HasNoMonsToSwitch(u32 battler, u8 r1, u8 r2);
bool32 TryChangeBattleWeather(u32 battler, u32 weatherEnumId, bool32 viaAbility);
u32 CanAbilityBlockMove(u32 battlerAtk, u32 battlerDef, u32 move, u32 abilityDef);
u32 CanPartnerAbilityBlockMove(u32 battlerAtk, u32 battlerDef, u32 move, u32 abilityDef);
u32 CanAbilityAbsorbMove(u32 battlerAtk, u32 battlerDef, u32 abilityDef, u32 move, u32 moveType);
u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 moveArg);
bool32 TryPrimalReversion(u32 battler);
bool32 IsNeutralizingGasOnField(void);

View file

@ -873,6 +873,12 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
// target ability checks
if (!DoesBattlerIgnoreAbilityChecks(aiData->abilities[battlerAtk], move))
{
if (CanAbilityBlockMove(battlerAtk, battlerDef, move, aiData->abilities[battlerDef]))
RETURN_SCORE_MINUS(20);
if (CanAbilityAbsorbMove(battlerAtk, battlerDef, aiData->abilities[battlerDef], move, moveType))
RETURN_SCORE_MINUS(20);
switch (aiData->abilities[battlerDef])
{
case ABILITY_MAGIC_GUARD:
@ -903,12 +909,6 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
&& (moveType == TYPE_DARK || moveType == TYPE_GHOST || moveType == TYPE_BUG))
RETURN_SCORE_MINUS(10);
break;
case ABILITY_DAZZLING:
case ABILITY_QUEENLY_MAJESTY:
case ABILITY_ARMOR_TAIL:
if (atkPriority > 0)
RETURN_SCORE_MINUS(10);
break;
case ABILITY_AROMA_VEIL:
if (IsAromaVeilProtectedMove(move))
RETURN_SCORE_MINUS(10);
@ -972,37 +972,14 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
&& IsNonVolatileStatusMoveEffect(moveEffect))
RETURN_SCORE_MINUS(10);
break;
case ABILITY_LIGHTNING_ROD:
if (B_REDIRECT_ABILITY_IMMUNITY < GEN_5)
break;
// Fallthrough
case ABILITY_MOTOR_DRIVE:
case ABILITY_VOLT_ABSORB:
if (moveType == TYPE_ELECTRIC)
RETURN_SCORE_MINUS(20);
break;
case ABILITY_STORM_DRAIN:
if (B_REDIRECT_ABILITY_IMMUNITY < GEN_5)
break;
// Fallthrough
case ABILITY_WATER_ABSORB:
case ABILITY_DRY_SKIN:
if (moveType == TYPE_WATER)
RETURN_SCORE_MINUS(20);
break;
case ABILITY_FLASH_FIRE:
if (moveType == TYPE_FIRE)
RETURN_SCORE_MINUS(20);
break;
case ABILITY_EARTH_EATER:
if (moveType == TYPE_GROUND)
RETURN_SCORE_MINUS(20);
break;
} // def ability checks
// target partner ability checks & not attacking partner
if (isDoubleBattle)
{
if (CanPartnerAbilityBlockMove(battlerAtk, battlerDef, move, aiData->abilities[BATTLE_PARTNER(battlerDef)]))
RETURN_SCORE_MINUS(20);
switch (aiData->abilities[BATTLE_PARTNER(battlerDef)])
{
case ABILITY_LIGHTNING_ROD:
@ -1029,12 +1006,6 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
if (IsAromaVeilProtectedMove(move))
RETURN_SCORE_MINUS(10);
break;
case ABILITY_DAZZLING:
case ABILITY_QUEENLY_MAJESTY:
case ABILITY_ARMOR_TAIL:
if (atkPriority > 0)
RETURN_SCORE_MINUS(10);
break;
}
} // def partner ability checks
} // ignore def ability check

View file

@ -402,12 +402,19 @@ bool32 IsDamageMoveUnusable(u32 move, u32 battlerAtk, u32 battlerDef)
{
struct AiLogicData *aiData = AI_DATA;
u32 battlerDefAbility;
u32 partnerBattlerDefAbility;
u32 moveType = GetMoveType(move);
if (DoesBattlerIgnoreAbilityChecks(aiData->abilities[battlerAtk], move))
{
battlerDefAbility = ABILITY_NONE;
partnerBattlerDefAbility = ABILITY_NONE;
}
else
{
battlerDefAbility = aiData->abilities[battlerDef];
partnerBattlerDefAbility = aiData->abilities[BATTLE_PARTNER(battlerDef)];
}
if (battlerDef == BATTLE_PARTNER(battlerAtk))
battlerDefAbility = aiData->abilities[battlerDef];
@ -415,47 +422,14 @@ bool32 IsDamageMoveUnusable(u32 move, u32 battlerAtk, u32 battlerDef)
if (gBattleStruct->commandingDondozo & (1u << battlerDef))
return TRUE;
switch (battlerDefAbility)
{
case ABILITY_LIGHTNING_ROD:
if (B_REDIRECT_ABILITY_IMMUNITY < GEN_5)
break;
// Fallthrough
case ABILITY_VOLT_ABSORB:
case ABILITY_MOTOR_DRIVE:
if (moveType == TYPE_ELECTRIC)
if (CanAbilityBlockMove(battlerAtk, battlerDef, move, aiData->abilities[battlerDef]))
return TRUE;
break;
case ABILITY_STORM_DRAIN:
if (B_REDIRECT_ABILITY_IMMUNITY < GEN_5)
break;
// Fallthrough
case ABILITY_WATER_ABSORB:
case ABILITY_DRY_SKIN:
if (moveType == TYPE_WATER)
if (CanPartnerAbilityBlockMove(battlerAtk, battlerDef, move, partnerBattlerDefAbility))
return TRUE;
break;
case ABILITY_FLASH_FIRE:
if (moveType == TYPE_FIRE)
if (CanAbilityAbsorbMove(battlerAtk, battlerDef, aiData->abilities[battlerDef], move, moveType))
return TRUE;
break;
case ABILITY_SOUNDPROOF:
if (gMovesInfo[move].soundMove)
return TRUE;
break;
case ABILITY_BULLETPROOF:
if (gMovesInfo[move].ballisticMove)
return TRUE;
break;
case ABILITY_SAP_SIPPER:
if (moveType == TYPE_GRASS)
return TRUE;
break;
case ABILITY_EARTH_EATER:
if (moveType == TYPE_GROUND)
return TRUE;
break;
}
switch (gMovesInfo[move].effect)
{
@ -526,7 +500,7 @@ struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u
bool32 isDamageMoveUnusable = FALSE;
bool32 toggledGimmick = FALSE;
struct AiLogicData *aiData = AI_DATA;
gBattleStruct->aiCalcInProgress = TRUE;
AI_DATA->aiCalcInProgress = TRUE;
if (moveEffect == EFFECT_NATURE_POWER)
move = GetNaturePowerMove(battlerAtk);
@ -736,12 +710,11 @@ struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u
*typeEffectiveness = AI_GetEffectiveness(effectivenessMultiplier);
// Undo temporary settings
gBattleStruct->aiCalcInProgress = FALSE;
gBattleStruct->swapDamageCategory = FALSE;
gBattleStruct->zmove.baseMoves[battlerAtk] = MOVE_NONE;
if (toggledGimmick)
SetActiveGimmick(battlerAtk, GIMMICK_NONE);
AI_DATA->aiCalcInProgress = FALSE;
return simDamage;
}

View file

@ -4202,6 +4202,7 @@ static void HandleTurnActionSelectionState(void)
AI_DATA->mostSuitableMonId[battler] = GetMostSuitableMonToSwitchInto(battler, TRUE);
else
AI_DATA->mostSuitableMonId[battler] = GetMostSuitableMonToSwitchInto(battler, FALSE);
gBattleStruct->aiMoveOrAction[battler] = ComputeBattleAiScores(battler);
}
// fallthrough

View file

@ -4107,12 +4107,125 @@ static void ChooseStatBoostAnimation(u32 battler)
#undef ANIM_STAT_ACC
#undef ANIM_STAT_EVASION
u32 CanAbilityBlockMove(u32 battlerAtk, u32 battlerDef, u32 move, u32 abilityDef)
{
enum MoveBlocked effect = MOVE_BLOCKED_BY_NO_ABILITY;
switch (abilityDef)
{
case ABILITY_SOUNDPROOF:
if (gMovesInfo[move].soundMove && !(GetBattlerMoveTargetType(battlerAtk, move) & MOVE_TARGET_USER))
effect = MOVE_BLOCKED_BY_SOUNDPROOF_OR_BULLETPROOF;
break;
case ABILITY_BULLETPROOF:
if (gMovesInfo[move].ballisticMove)
effect = MOVE_BLOCKED_BY_SOUNDPROOF_OR_BULLETPROOF;
break;
case ABILITY_DAZZLING:
case ABILITY_QUEENLY_MAJESTY:
case ABILITY_ARMOR_TAIL:
if (GetBattlerSide(battlerAtk) != GetBattlerSide(battlerDef))
{
u32 priority = AI_DATA->aiCalcInProgress ? GetMovePriority(battlerAtk, move) : GetChosenMovePriority(battlerAtk);
if (priority > 0)
effect = MOVE_BLOCKED_BY_DAZZLING;
}
break;
case ABILITY_GOOD_AS_GOLD:
if (IS_MOVE_STATUS(move))
{
u32 moveTarget = GetBattlerMoveTargetType(battlerAtk, move);
if (!(moveTarget & MOVE_TARGET_OPPONENTS_FIELD) && !(moveTarget & MOVE_TARGET_ALL_BATTLERS))
effect = MOVE_BLOCKED_BY_GOOD_AS_GOLD;
}
break;
}
if (!effect)
effect = CanPartnerAbilityBlockMove(battlerAtk, battlerDef, move, GetBattlerAbility(BATTLE_PARTNER(battlerDef)));
return effect;
}
u32 CanPartnerAbilityBlockMove(u32 battlerAtk, u32 battlerDef, u32 move, u32 abilityDef)
{
switch (abilityDef)
{
case ABILITY_DAZZLING:
case ABILITY_QUEENLY_MAJESTY:
case ABILITY_ARMOR_TAIL:
if (GetBattlerSide(battlerAtk) != GetBattlerSide(battlerDef))
{
s32 priority = AI_DATA->aiCalcInProgress ? GetMovePriority(battlerAtk, move) : GetChosenMovePriority(battlerAtk);
if (priority > 0)
return MOVE_BLOCKED_BY_PARTNER_DAZZLING;
}
break;
}
return MOVE_BLOCKED_BY_NO_ABILITY;
}
u32 CanAbilityAbsorbMove(u32 battlerAtk, u32 battlerDef, u32 abilityDef, u32 move, u32 moveType)
{
enum MoveAbsorbed effect = MOVE_ABSORBED_BY_NO_ABILITY;
switch (abilityDef)
{
default:
effect = MOVE_ABSORBED_BY_NO_ABILITY;
break;
case ABILITY_VOLT_ABSORB:
if (moveType == TYPE_ELECTRIC && gMovesInfo[move].target != MOVE_TARGET_ALL_BATTLERS)
effect = MOVE_ABSORBED_BY_DRAIN_HP_ABILITY;
break;
case ABILITY_WATER_ABSORB:
case ABILITY_DRY_SKIN:
if (moveType == TYPE_WATER)
effect = MOVE_ABSORBED_BY_DRAIN_HP_ABILITY;
break;
case ABILITY_EARTH_EATER:
if (moveType == TYPE_GROUND)
effect = MOVE_ABSORBED_BY_DRAIN_HP_ABILITY;
break;
case ABILITY_MOTOR_DRIVE:
if (moveType == TYPE_ELECTRIC && gMovesInfo[move].target != MOVE_TARGET_ALL_BATTLERS) // Potential bug in singles (might be solved with simu hp reudction)
effect = MOVE_ABSORBED_BY_STAT_INCREASE_ABILITY;
break;
case ABILITY_LIGHTNING_ROD:
if (B_REDIRECT_ABILITY_IMMUNITY >= GEN_5 && moveType == TYPE_ELECTRIC && gMovesInfo[move].target != MOVE_TARGET_ALL_BATTLERS) // Potential bug in singles (might be solved with simu hp reudction)
effect = MOVE_ABSORBED_BY_STAT_INCREASE_ABILITY;
break;
case ABILITY_STORM_DRAIN:
if (B_REDIRECT_ABILITY_IMMUNITY >= GEN_5 && moveType == TYPE_WATER)
effect = MOVE_ABSORBED_BY_STAT_INCREASE_ABILITY;
break;
case ABILITY_SAP_SIPPER:
if (moveType == TYPE_GRASS)
effect = MOVE_ABSORBED_BY_STAT_INCREASE_ABILITY;
break;
case ABILITY_WELL_BAKED_BODY:
if (moveType == TYPE_FIRE)
effect = MOVE_ABSORBED_BY_STAT_INCREASE_ABILITY;
break;
case ABILITY_WIND_RIDER:
if (gMovesInfo[move].windMove && !(GetBattlerMoveTargetType(battlerAtk, move) & MOVE_TARGET_USER))
effect = MOVE_ABSORBED_BY_STAT_INCREASE_ABILITY;
break;
case ABILITY_FLASH_FIRE:
if (moveType == TYPE_FIRE && (B_FLASH_FIRE_FROZEN >= GEN_5 || !(gBattleMons[battlerDef].status1 & STATUS1_FREEZE)))
effect = MOVE_ABSORBED_BY_BOOST_FLASH_FIRE;
break;
}
return effect;
}
u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 moveArg)
{
u32 effect = 0;
u32 moveType, move;
u32 side;
u32 i, j;
u32 moveType = 0, move = 0;
u32 side = 0;
u32 i = 0, j = 0;
u32 partner = 0;
struct Pokemon *mon;
@ -5206,153 +5319,87 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
}
}
break;
case ABILITYEFFECT_MOVES_BLOCK:
case ABILITYEFFECT_WOULD_BLOCK:
effect = CanAbilityBlockMove(gBattlerAttacker, battler, move, gLastUsedAbility);
if (effect && gLastUsedAbility != 0xFFFF)
RecordAbilityBattle(battler, gLastUsedAbility);
break;
case ABILITYEFFECT_MOVES_BLOCK:
{
u16 moveTarget = GetBattlerMoveTargetType(battler, move);
effect = CanAbilityBlockMove(gBattlerAttacker, battler, move, gLastUsedAbility);
const u8 * battleScriptBlocksMove = NULL;
switch (gLastUsedAbility)
{
case ABILITY_SOUNDPROOF:
if (gMovesInfo[move].soundMove && !(moveTarget & MOVE_TARGET_USER))
effect = 1;
break;
case ABILITY_BULLETPROOF:
if (gMovesInfo[move].ballisticMove)
effect = 1;
break;
case ABILITY_DAZZLING:
case ABILITY_QUEENLY_MAJESTY:
case ABILITY_ARMOR_TAIL:
if (GetChosenMovePriority(gBattlerAttacker) > 0 && GetBattlerSide(gBattlerAttacker) != GetBattlerSide(battler))
effect = 2;
break;
case ABILITY_GOOD_AS_GOLD:
if (IS_MOVE_STATUS(gCurrentMove)
&& !(moveTarget & MOVE_TARGET_OPPONENTS_FIELD)
&& !(moveTarget & MOVE_TARGET_ALL_BATTLERS))
effect = 3;
break;
}
if (!effect)
{
switch (GetBattlerAbility(BATTLE_PARTNER(battler)))
{
case ABILITY_DAZZLING:
case ABILITY_QUEENLY_MAJESTY:
case ABILITY_ARMOR_TAIL:
if (GetChosenMovePriority(gBattlerAttacker) > 0 && GetBattlerSide(gBattlerAttacker) != GetBattlerSide(battler))
effect = 4;
break;
}
}
if (effect == 1)
switch (effect)
{
case MOVE_BLOCKED_BY_SOUNDPROOF_OR_BULLETPROOF:
if (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS)
gHitMarker |= HITMARKER_NO_PPDEDUCT;
battleScriptBlocksMove = BattleScript_SoundproofProtected;
}
else if (effect == 2 || effect == 4)
{
if (effect == 4)
break;
case MOVE_BLOCKED_BY_DAZZLING:
case MOVE_BLOCKED_BY_PARTNER_DAZZLING:
if (effect == MOVE_BLOCKED_BY_PARTNER_DAZZLING)
gBattleScripting.battler = BATTLE_PARTNER(battler);
else
gBattleScripting.battler = battler;
if (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS)
gHitMarker |= HITMARKER_NO_PPDEDUCT;
battleScriptBlocksMove = BattleScript_DazzlingProtected;
}
else if (effect == 3)
{
break;
case MOVE_BLOCKED_BY_GOOD_AS_GOLD:
battleScriptBlocksMove = BattleScript_GoodAsGoldActivates;
}
else if (GetChosenMovePriority(gBattlerAttacker) > 0
break;
default:
if (GetChosenMovePriority(gBattlerAttacker) > 0
&& BlocksPrankster(move, gBattlerAttacker, gBattlerTarget, TRUE)
&& !(IS_MOVE_STATUS(move) && (gLastUsedAbility == ABILITY_MAGIC_BOUNCE || gProtectStructs[gBattlerTarget].bounceMove)))
{
if (!IsDoubleBattle() || !(moveTarget & (MOVE_TARGET_BOTH | MOVE_TARGET_FOES_AND_ALLY)))
if (!IsDoubleBattle()
|| !(GetBattlerMoveTargetType(gBattlerAttacker, move) & (MOVE_TARGET_BOTH | MOVE_TARGET_FOES_AND_ALLY)))
CancelMultiTurnMoves(gBattlerAttacker); // Don't cancel moves that can hit two targets bc one target might not be protected
gBattleScripting.battler = gBattlerAbility = gBattlerTarget;
battleScriptBlocksMove = BattleScript_DarkTypePreventsPrankster;
effect = 1;
}
if (caseID == ABILITYEFFECT_WOULD_BLOCK)
{
if (effect && gLastUsedAbility != 0xFFFF)
RecordAbilityBattle(battler, gLastUsedAbility);
return effect;
}
else if (effect)
{
if (effect)
gBattlescriptCurrInstr = battleScriptBlocksMove;
}
break;
}
case ABILITYEFFECT_ABSORBING:
case ABILITYEFFECT_WOULD_ABSORB:
if (move != MOVE_NONE)
{
u8 statId = 0;
u8 statAmount = 1;
switch (gLastUsedAbility)
{
case ABILITY_VOLT_ABSORB:
if (moveType == TYPE_ELECTRIC && gMovesInfo[move].target != MOVE_TARGET_ALL_BATTLERS)
effect = 1;
break;
case ABILITY_WATER_ABSORB:
case ABILITY_DRY_SKIN:
if (moveType == TYPE_WATER)
effect = 1;
break;
case ABILITY_MOTOR_DRIVE:
if (moveType == TYPE_ELECTRIC && gMovesInfo[move].target != MOVE_TARGET_ALL_BATTLERS)
effect = 2, statId = STAT_SPEED;
break;
case ABILITY_LIGHTNING_ROD:
if (B_REDIRECT_ABILITY_IMMUNITY >= GEN_5 && moveType == TYPE_ELECTRIC && gMovesInfo[move].target != MOVE_TARGET_ALL_BATTLERS)
effect = 2, statId = STAT_SPATK;
break;
case ABILITY_STORM_DRAIN:
if (B_REDIRECT_ABILITY_IMMUNITY >= GEN_5 && moveType == TYPE_WATER)
effect = 2, statId = STAT_SPATK;
break;
case ABILITY_SAP_SIPPER:
if (moveType == TYPE_GRASS)
effect = 2, statId = STAT_ATK;
break;
case ABILITY_FLASH_FIRE:
if (moveType == TYPE_FIRE && (B_FLASH_FIRE_FROZEN >= GEN_5 || !(gBattleMons[battler].status1 & STATUS1_FREEZE)))
effect = 3;
break;
case ABILITY_WELL_BAKED_BODY:
if (moveType == TYPE_FIRE)
effect = 2, statId = STAT_DEF, statAmount = 2;
break;
case ABILITY_WIND_RIDER:
if (gMovesInfo[gCurrentMove].windMove && !(GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove) & MOVE_TARGET_USER))
effect = 2, statId = STAT_ATK;
break;
case ABILITY_EARTH_EATER:
if (moveType == TYPE_GROUND)
effect = 1;
break;
}
if (caseID == ABILITYEFFECT_WOULD_ABSORB)
{
effect = CanAbilityAbsorbMove(gBattlerAttacker, battler, gLastUsedAbility, move, moveType);
gBattleStruct->pledgeMove = FALSE;
if (effect && gLastUsedAbility != 0xFFFF)
RecordAbilityBattle(battler, gLastUsedAbility);
return effect;
}
else if (effect == 1) // Drain Hp ability.
case ABILITYEFFECT_ABSORBING:
{
u32 statId = 0;
u32 statAmount = 1;
effect = CanAbilityAbsorbMove(gBattlerAttacker, battler, gLastUsedAbility, move, moveType);
if (effect)
{
switch(gLastUsedAbility)
{
case ABILITY_MOTOR_DRIVE:
statId = STAT_SPEED;
break;
case ABILITY_LIGHTNING_ROD:
case ABILITY_STORM_DRAIN:
statId = STAT_SPATK;
break;
case ABILITY_SAP_SIPPER:
case ABILITY_WIND_RIDER:
statId = STAT_ATK;
break;
case ABILITY_WELL_BAKED_BODY:
statAmount = 2;
statId = STAT_DEF;
break;
}
}
switch (effect)
{
case MOVE_ABSORBED_BY_DRAIN_HP_ABILITY:
gBattleStruct->pledgeMove = FALSE;
if (BATTLER_MAX_HP(battler) || (B_HEAL_BLOCKING >= GEN_5 && gStatuses3[battler] & STATUS3_HEAL_BLOCK))
{
@ -5373,9 +5420,8 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
gBattleMoveDamage = 1;
gBattleMoveDamage *= -1;
}
}
else if (effect == 2) // Boost Stat ability;
{
break;
case MOVE_ABSORBED_BY_STAT_INCREASE_ABILITY:
gBattleStruct->pledgeMove = FALSE;
if (!CompareStat(battler, statId, MAX_STAT_STAGE, CMP_LESS_THAN))
{
@ -5395,9 +5441,8 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
if (B_ABSORBING_ABILITY_STRING < GEN_5)
PREPARE_STAT_BUFFER(gBattleTextBuff1, statId);
}
}
else if (effect == 3)
{
break;
case MOVE_ABSORBED_BY_BOOST_FLASH_FIRE:
gBattleStruct->pledgeMove = FALSE;
if (!(gBattleResources->flags->flags[battler] & RESOURCE_FLAG_FLASH_FIRE))
{
@ -5416,10 +5461,11 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
else
gBattlescriptCurrInstr = BattleScript_FlashFireBoost_PPLoss;
}
break;
}
if (effect)
gMultiHitCounter = 0; // Prevent multi-hit moves from hitting more than once after move has been absorbed.
}
break;
case ABILITYEFFECT_MOVE_END: // Think contact abilities.
@ -10371,7 +10417,7 @@ static inline void MulByTypeEffectiveness(uq4_12_t *modifier, u32 move, u32 move
mod = UQ_4_12(1.0);
}
if (gBattleStruct->distortedTypeMatchups & (1u << battlerDef) || (gBattleStruct->aiCalcInProgress && ShouldTeraShellDistortTypeMatchups(move, battlerDef)))
if (gBattleStruct->distortedTypeMatchups & (1u << battlerDef) || (AI_DATA->aiCalcInProgress && ShouldTeraShellDistortTypeMatchups(move, battlerDef)))
{
mod = UQ_4_12(0.5);
if (recordAbilities)