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:
parent
607c247ee0
commit
1454644376
6 changed files with 230 additions and 219 deletions
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
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)
|
||||
return TRUE;
|
||||
break;
|
||||
case ABILITY_FLASH_FIRE:
|
||||
if (moveType == TYPE_FIRE)
|
||||
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;
|
||||
}
|
||||
if (CanAbilityBlockMove(battlerAtk, battlerDef, move, aiData->abilities[battlerDef]))
|
||||
return TRUE;
|
||||
|
||||
if (CanPartnerAbilityBlockMove(battlerAtk, battlerDef, move, partnerBattlerDefAbility))
|
||||
return TRUE;
|
||||
|
||||
if (CanAbilityAbsorbMove(battlerAtk, battlerDef, aiData->abilities[battlerDef], move, moveType))
|
||||
return TRUE;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
break;
|
||||
default:
|
||||
if (GetChosenMovePriority(gBattlerAttacker) > 0
|
||||
&& BlocksPrankster(move, gBattlerAttacker, gBattlerTarget, TRUE)
|
||||
&& !(IS_MOVE_STATUS(move) && (gLastUsedAbility == ABILITY_MAGIC_BOUNCE || gProtectStructs[gBattlerTarget].bounceMove)))
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
else 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)))
|
||||
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:
|
||||
break;
|
||||
case ABILITYEFFECT_WOULD_ABSORB:
|
||||
if (move != MOVE_NONE)
|
||||
effect = CanAbilityAbsorbMove(gBattlerAttacker, battler, gLastUsedAbility, move, moveType);
|
||||
gBattleStruct->pledgeMove = FALSE;
|
||||
if (effect && gLastUsedAbility != 0xFFFF)
|
||||
RecordAbilityBattle(battler, gLastUsedAbility);
|
||||
return effect;
|
||||
case ABILITYEFFECT_ABSORBING:
|
||||
{
|
||||
u8 statId = 0;
|
||||
u8 statAmount = 1;
|
||||
switch (gLastUsedAbility)
|
||||
u32 statId = 0;
|
||||
u32 statAmount = 1;
|
||||
effect = CanAbilityAbsorbMove(gBattlerAttacker, battler, gLastUsedAbility, move, moveType);
|
||||
if (effect)
|
||||
{
|
||||
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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
if (caseID == ABILITYEFFECT_WOULD_ABSORB)
|
||||
{
|
||||
gBattleStruct->pledgeMove = FALSE;
|
||||
if (effect && gLastUsedAbility != 0xFFFF)
|
||||
RecordAbilityBattle(battler, gLastUsedAbility);
|
||||
|
||||
return effect;
|
||||
}
|
||||
else if (effect == 1) // Drain Hp ability.
|
||||
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)
|
||||
|
|
Loading…
Reference in a new issue