Remove Duplicate ai code from battle_ai_util.c (#4883)

* Remove Duplicate ai code from battle_ai_util.c

* Add GetBattlerAbility in toxic self check
This commit is contained in:
Alex 2024-07-03 10:48:31 +02:00 committed by GitHub
parent d81662daa2
commit 594633aa15
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 185 additions and 152 deletions

View File

@ -134,10 +134,8 @@ bool32 HasMagicCoatAffectedMove(u32 battler);
bool32 HasSnatchAffectedMove(u32 battler);
// status checks
bool32 AI_CanBeBurned(u32 battler, u32 ability);
bool32 AI_CanGetFrostbite(u32 battler, u32 ability);
bool32 AI_CanBeConfused(u32 battlerAtk, u32 battlerDef, u32 move, u32 ability);
bool32 AI_CanSleep(u32 battler, u32 ability);
bool32 IsBattlerIncapacitated(u32 battler, u32 ability);
bool32 AI_CanPutToSleep(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 partnerMove);
bool32 ShouldPoisonSelf(u32 battler, u32 ability);

View File

@ -235,10 +235,10 @@ bool32 MoveHasAdditionalEffectSelf(u32 move, u32 moveEffect);
bool32 MoveHasAdditionalEffectSelfArg(u32 move, u32 moveEffect, u32 argument);
bool32 MoveHasChargeTurnAdditionalEffect(u32 move);
bool32 CanSleep(u32 battler);
bool32 CanBePoisoned(u32 battlerAttacker, u32 battlerTarget);
bool32 CanBeBurned(u32 battler);
bool32 CanBeParalyzed(u32 battler);
bool32 CanBeSlept(u32 battler, u32 ability);
bool32 CanBePoisoned(u32 battlerAtk, u32 battlerDef, u32 defAbility);
bool32 CanBeBurned(u32 battler, u32 ability);
bool32 CanBeParalyzed(u32 battler, u32 ability);
bool32 CanBeFrozen(u32 battler);
bool32 CanGetFrostbite(u32 battler);
bool32 CanBeConfused(u32 battler);

View File

@ -1866,7 +1866,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
ADJUST_SCORE(-10);
break;
case EFFECT_REST:
if (!AI_CanSleep(battlerAtk, aiData->abilities[battlerAtk]))
if (!CanBeSlept(battlerAtk, aiData->abilities[battlerAtk]))
ADJUST_SCORE(-10);
//fallthrough
case EFFECT_RESTORE_HP:
@ -3456,7 +3456,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
}
break;
case EFFECT_REST:
if (!(AI_CanSleep(battlerAtk, aiData->abilities[battlerAtk])))
if (!(CanBeSlept(battlerAtk, aiData->abilities[battlerAtk])))
{
break;
}
@ -3936,7 +3936,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
ADJUST_SCORE(DECENT_EFFECT);
break;
case HOLD_EFFECT_FLAME_ORB:
if (!ShouldBurnSelf(battlerAtk, aiData->abilities[battlerAtk]) && AI_CanBeBurned(battlerAtk, aiData->abilities[battlerDef]))
if (!ShouldBurnSelf(battlerAtk, aiData->abilities[battlerAtk]) && CanBeBurned(battlerAtk, aiData->abilities[battlerDef]))
ADJUST_SCORE(DECENT_EFFECT);
break;
case HOLD_EFFECT_BLACK_SLUDGE:

View File

@ -442,7 +442,7 @@ static bool32 ShouldSwitchIfGameStatePrompt(u32 battler, bool32 emitResult)
{
//Yawn
if (gStatuses3[battler] & STATUS3_YAWN
&& AI_CanSleep(battler, monAbility)
&& CanBeSlept(battler, monAbility)
&& gBattleMons[battler].hp > gBattleMons[battler].maxHP / 3)
{
switchMon = TRUE;
@ -1291,8 +1291,6 @@ static u32 GetSwitchinHazardsDamage(u32 battler, struct BattlePokemon *battleMon
hazardDamage += spikesDamage;
}
// Toxic Spikes
// TODO: CanBePoisoned compatibility to avoid duplicate code
if ((hazardFlags & SIDE_STATUS_TOXIC_SPIKES) && (defType1 != TYPE_POISON && defType2 != TYPE_POISON
&& defType1 != TYPE_STEEL && defType2 != TYPE_STEEL
&& ability != ABILITY_IMMUNITY && ability != ABILITY_POISON_HEAL && ability != ABILITY_COMATOSE

View File

@ -2624,48 +2624,18 @@ bool32 IsBattlerIncapacitated(u32 battler, u32 ability)
return FALSE;
}
bool32 AI_CanSleep(u32 battler, u32 ability)
{
if (ability == ABILITY_INSOMNIA
|| ability == ABILITY_VITAL_SPIRIT
|| ability == ABILITY_COMATOSE
|| gBattleMons[battler].status1 & STATUS1_ANY
|| gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD
|| (gFieldStatuses & (STATUS_FIELD_MISTY_TERRAIN | STATUS_FIELD_ELECTRIC_TERRAIN))
|| IsAbilityStatusProtected(battler))
return FALSE;
return TRUE;
}
bool32 AI_CanPutToSleep(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 partnerMove)
{
if (!AI_CanSleep(battlerDef, defAbility)
if (!CanBeSlept(battlerDef, defAbility)
|| DoesSubstituteBlockMove(battlerAtk, battlerDef, move)
|| PartnerMoveEffectIsStatusSameTarget(BATTLE_PARTNER(battlerAtk), battlerDef, partnerMove)) // shouldn't try to sleep mon that partner is trying to make sleep
return FALSE;
return TRUE;
}
static bool32 AI_CanBePoisoned(u32 battlerAtk, u32 battlerDef, u32 move)
{
u32 ability = AI_DATA->abilities[battlerDef];
if (!(CanPoisonType(battlerAtk, battlerDef))
|| gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_SAFEGUARD
|| gBattleMons[battlerDef].status1 & STATUS1_ANY
|| ability == ABILITY_IMMUNITY
|| ability == ABILITY_COMATOSE
|| AI_IsAbilityOnSide(battlerDef, ABILITY_PASTEL_VEIL)
|| gBattleMons[battlerDef].status1 & STATUS1_ANY
|| IsAbilityStatusProtected(battlerDef)
|| AI_IsTerrainAffected(battlerDef, STATUS_FIELD_MISTY_TERRAIN))
return FALSE;
return TRUE;
}
bool32 ShouldPoisonSelf(u32 battler, u32 ability)
{
if (AI_CanBePoisoned(battler, battler, 0) && (
if (CanBePoisoned(battler, battler, GetBattlerAbility(battler)) && (
ability == ABILITY_MARVEL_SCALE
|| ability == ABILITY_POISON_HEAL
|| ability == ABILITY_QUICK_FEET
@ -2680,7 +2650,7 @@ bool32 ShouldPoisonSelf(u32 battler, u32 ability)
bool32 AI_CanPoison(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 partnerMove)
{
if (!AI_CanBePoisoned(battlerAtk, battlerDef, move)
if (!CanBePoisoned(battlerAtk, battlerDef, GetBattlerAbility(battlerDef))
|| AI_DATA->effectiveness[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex] == AI_EFFECTIVENESS_x0
|| DoesSubstituteBlockMove(battlerAtk, battlerDef, move)
|| PartnerMoveEffectIsStatusSameTarget(BATTLE_PARTNER(battlerAtk), battlerDef, partnerMove))
@ -2693,20 +2663,9 @@ bool32 AI_CanPoison(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u3
return TRUE;
}
static bool32 AI_CanBeParalyzed(u32 battler, u32 ability)
{
if (ability == ABILITY_LIMBER
|| ability == ABILITY_COMATOSE
|| IS_BATTLER_OF_TYPE(battler, TYPE_ELECTRIC)
|| gBattleMons[battler].status1 & STATUS1_ANY
|| IsAbilityStatusProtected(battler))
return FALSE;
return TRUE;
}
bool32 AI_CanParalyze(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 partnerMove)
{
if (!AI_CanBeParalyzed(battlerDef, defAbility)
if (!CanBeParalyzed(battlerDef, defAbility)
|| AI_DATA->effectiveness[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex] == AI_EFFECTIVENESS_x0
|| gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_SAFEGUARD
|| DoesSubstituteBlockMove(battlerAtk, battlerDef, move)
@ -2740,19 +2699,6 @@ bool32 AI_CanConfuse(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 battler
return TRUE;
}
bool32 AI_CanBeBurned(u32 battler, u32 ability)
{
if (ability == ABILITY_WATER_VEIL
|| ability == ABILITY_WATER_BUBBLE
|| ability == ABILITY_COMATOSE
|| IS_BATTLER_OF_TYPE(battler, TYPE_FIRE)
|| gBattleMons[battler].status1 & STATUS1_ANY
|| IsAbilityStatusProtected(battler)
|| gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD)
return FALSE;
return TRUE;
}
bool32 AI_CanGetFrostbite(u32 battler, u32 ability)
{
if (ability == ABILITY_MAGMA_ARMOR
@ -2767,7 +2713,7 @@ bool32 AI_CanGetFrostbite(u32 battler, u32 ability)
bool32 ShouldBurnSelf(u32 battler, u32 ability)
{
if (AI_CanBeBurned(battler, ability) && (
if (CanBeBurned(battler, ability) && (
ability == ABILITY_QUICK_FEET
|| ability == ABILITY_HEATPROOF
|| ability == ABILITY_MAGIC_GUARD
@ -2781,7 +2727,7 @@ bool32 ShouldBurnSelf(u32 battler, u32 ability)
bool32 AI_CanBurn(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 battlerAtkPartner, u32 move, u32 partnerMove)
{
if (!AI_CanBeBurned(battlerDef, defAbility)
if (!CanBeBurned(battlerDef, defAbility)
|| AI_DATA->effectiveness[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex] == AI_EFFECTIVENESS_x0
|| DoesSubstituteBlockMove(battlerAtk, battlerDef, move)
|| PartnerMoveEffectIsStatusSameTarget(battlerAtkPartner, battlerDef, partnerMove))

View File

@ -791,7 +791,7 @@ void BS_SetMaxMoveEffect(void)
{
static const u8 sSnoozeEffects[] = {TRUE, FALSE};
if (!(gStatuses3[gBattlerTarget] & STATUS3_YAWN)
&& CanSleep(gBattlerTarget)
&& CanBeSlept(gBattlerTarget, GetBattlerAbility(gBattlerTarget))
&& RandomElement(RNG_G_MAX_SNOOZE, sSnoozeEffects)) // 50% chance of success
{
gStatuses3[gBattlerTarget] |= STATUS3_YAWN_TURN(2);
@ -897,7 +897,7 @@ void BS_TrySetStatus1(void)
switch (status1)
{
case STATUS1_POISON:
if (CanBePoisoned(gBattlerAttacker, gBattlerTarget))
if (CanBePoisoned(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerTarget)))
{
gBattleMons[gBattlerTarget].status1 |= STATUS1_POISON;
gBattleCommunication[MULTISTRING_CHOOSER] = 0;
@ -905,7 +905,7 @@ void BS_TrySetStatus1(void)
}
break;
case STATUS1_PARALYSIS:
if (CanBeParalyzed(gBattlerTarget))
if (CanBeParalyzed(gBattlerTarget, GetBattlerAbility(gBattlerTarget)))
{
gBattleMons[gBattlerTarget].status1 |= STATUS1_PARALYSIS;
gBattleCommunication[MULTISTRING_CHOOSER] = 3;
@ -913,7 +913,7 @@ void BS_TrySetStatus1(void)
}
break;
case STATUS1_SLEEP:
if (CanSleep(gBattlerTarget))
if (CanBeSlept(gBattlerTarget, GetBattlerAbility(gBattlerTarget)))
{
if (B_SLEEP_TURNS >= GEN_5)
gBattleMons[gBattlerTarget].status1 |= STATUS1_SLEEP_TURN((Random() % 3) + 2);

View File

@ -2907,7 +2907,7 @@ void SetMoveEffect(bool32 primary, bool32 certain)
if (i != gBattlersCount)
break;
if (!CanSleep(gEffectBattler))
if (!CanBeSlept(gEffectBattler, GetBattlerAbility(gEffectBattler)))
break;
cancelMultiTurnMovesResult = CancelMultiTurnMoves(gEffectBattler);
@ -2946,7 +2946,7 @@ void SetMoveEffect(bool32 primary, bool32 certain)
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STATUS_HAD_NO_EFFECT;
RESET_RETURN
}
if (!CanBePoisoned(gBattleScripting.battler, gEffectBattler))
if (!CanBePoisoned(gBattleScripting.battler, gEffectBattler, GetBattlerAbility(gEffectBattler)))
break;
statusChanged = TRUE;
@ -2990,7 +2990,7 @@ void SetMoveEffect(bool32 primary, bool32 certain)
break;
}
if (!CanBeBurned(gEffectBattler))
if (!CanBeBurned(gEffectBattler, GetBattlerAbility(gEffectBattler)))
break;
statusChanged = TRUE;
@ -3055,7 +3055,7 @@ void SetMoveEffect(bool32 primary, bool32 certain)
}
if (!CanParalyzeType(gBattleScripting.battler, gEffectBattler))
break;
if (!CanBeParalyzed(gEffectBattler))
if (!CanBeParalyzed(gEffectBattler, GetBattlerAbility(gEffectBattler)))
break;
statusChanged = TRUE;
@ -3093,7 +3093,7 @@ void SetMoveEffect(bool32 primary, bool32 certain)
}
if (gBattleMons[gEffectBattler].status1)
break;
if (CanBePoisoned(gBattleScripting.battler, gEffectBattler))
if (CanBePoisoned(gBattleScripting.battler, gEffectBattler, GetBattlerAbility(gEffectBattler)))
{
// It's redundant, because at this point we know the status1 value is 0.
gBattleMons[gEffectBattler].status1 &= ~STATUS1_TOXIC_POISON;
@ -5514,7 +5514,7 @@ static void Cmd_moveend(void)
}
// Not strictly a protect effect, but works the same way
else if (gProtectStructs[gBattlerTarget].beakBlastCharge
&& CanBeBurned(gBattlerAttacker)
&& CanBeBurned(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker))
&& !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT))
{
gProtectStructs[gBattlerAttacker].touchedProtectLike = FALSE;
@ -9940,16 +9940,17 @@ static void Cmd_various(void)
case VARIOUS_PSYCHO_SHIFT:
{
VARIOUS_ARGS(const u8 *failInstr);
u32 targetAbility = GetBattlerAbility(gBattlerTarget);
// Psycho shift works
if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_POISON) && CanBePoisoned(gBattlerAttacker, gBattlerTarget))
if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_POISON) && CanBePoisoned(gBattlerAttacker, gBattlerTarget, targetAbility))
gBattleCommunication[MULTISTRING_CHOOSER] = 0;
else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_TOXIC_POISON) && CanBePoisoned(gBattlerAttacker, gBattlerTarget))
else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_TOXIC_POISON) && CanBePoisoned(gBattlerAttacker, gBattlerTarget, targetAbility))
gBattleCommunication[MULTISTRING_CHOOSER] = 1;
else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_BURN) && CanBeBurned(gBattlerTarget))
else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_BURN) && CanBeBurned(gBattlerTarget, targetAbility))
gBattleCommunication[MULTISTRING_CHOOSER] = 2;
else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_PARALYSIS) && CanBeParalyzed(gBattlerTarget))
else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_PARALYSIS) && CanBeParalyzed(gBattlerTarget, targetAbility))
gBattleCommunication[MULTISTRING_CHOOSER] = 3;
else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP) && CanSleep(gBattlerTarget))
else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP) && CanBeSlept(gBattlerTarget, targetAbility))
gBattleCommunication[MULTISTRING_CHOOSER] = 4;
else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_FROSTBITE) && CanBeFrozen(gBattlerTarget))
gBattleCommunication[MULTISTRING_CHOOSER] = 5;

View File

@ -5455,8 +5455,10 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
}
break;
case ABILITY_EFFECT_SPORE:
{
u32 ability = GetBattlerAbility(gBattlerAttacker);
if (!IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_GRASS)
&& GetBattlerAbility(gBattlerAttacker) != ABILITY_OVERCOAT
&& ability != ABILITY_OVERCOAT
&& GetBattlerHoldEffect(gBattlerAttacker, TRUE) != HOLD_EFFECT_SAFETY_GOGGLES)
{
i = Random() % 3;
@ -5469,7 +5471,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
&& gBattleMons[gBattlerAttacker].hp != 0
&& !gProtectStructs[gBattlerAttacker].confusionSelfDmg
&& TARGET_TURN_DAMAGED
&& CanSleep(gBattlerAttacker)
&& CanBeSlept(gBattlerAttacker, ability)
&& GetBattlerHoldEffect(gBattlerAttacker, TRUE) != HOLD_EFFECT_PROTECTIVE_PADS
&& IsMoveMakingContact(move, gBattlerAttacker)
&& (Random() % 3) == 0)
@ -5482,6 +5484,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
effect++;
}
}
}
break;
POISON_POINT:
case ABILITY_POISON_POINT:
@ -5489,7 +5492,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
&& gBattleMons[gBattlerAttacker].hp != 0
&& !gProtectStructs[gBattlerAttacker].confusionSelfDmg
&& TARGET_TURN_DAMAGED
&& CanBePoisoned(gBattlerTarget, gBattlerAttacker)
&& CanBePoisoned(gBattlerTarget, gBattlerAttacker, GetBattlerAbility(gBattlerAttacker))
&& GetBattlerHoldEffect(gBattlerAttacker, TRUE) != HOLD_EFFECT_PROTECTIVE_PADS
&& IsMoveMakingContact(move, gBattlerAttacker)
&& RandomWeighted(RNG_POISON_POINT, 2, 1))
@ -5508,7 +5511,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
&& gBattleMons[gBattlerAttacker].hp != 0
&& !gProtectStructs[gBattlerAttacker].confusionSelfDmg
&& TARGET_TURN_DAMAGED
&& CanBeParalyzed(gBattlerAttacker)
&& CanBeParalyzed(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker))
&& GetBattlerHoldEffect(gBattlerAttacker, TRUE) != HOLD_EFFECT_PROTECTIVE_PADS
&& IsMoveMakingContact(move, gBattlerAttacker)
&& RandomWeighted(RNG_STATIC, 2, 1))
@ -5528,7 +5531,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
&& GetBattlerHoldEffect(gBattlerAttacker, TRUE) != HOLD_EFFECT_PROTECTIVE_PADS
&& (IsMoveMakingContact(move, gBattlerAttacker))
&& TARGET_TURN_DAMAGED
&& CanBeBurned(gBattlerAttacker)
&& CanBeBurned(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker))
&& RandomWeighted(RNG_FLAME_BODY, 2, 1))
{
gBattleScripting.moveEffect = MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_BURN;
@ -5745,7 +5748,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)
&& gBattleMons[gBattlerTarget].hp != 0
&& !gProtectStructs[gBattlerAttacker].confusionSelfDmg
&& CanBePoisoned(gBattlerAttacker, gBattlerTarget)
&& CanBePoisoned(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerTarget))
&& GetBattlerHoldEffect(gBattlerAttacker, TRUE) != HOLD_EFFECT_PROTECTIVE_PADS
&& IsMoveMakingContact(move, gBattlerAttacker)
&& TARGET_TURN_DAMAGED // Need to actually hit the target
@ -6327,83 +6330,78 @@ bool32 IsBattlerTerrainAffected(u32 battler, u32 terrainFlag)
return IsBattlerGrounded(battler);
}
bool32 CanSleep(u32 battler)
bool32 CanBeSlept(u32 battler, u32 ability)
{
u16 ability = GetBattlerAbility(battler);
if (ability == ABILITY_INSOMNIA
|| ability == ABILITY_VITAL_SPIRIT
|| ability == ABILITY_COMATOSE
|| ability == ABILITY_PURIFYING_SALT
|| gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD
|| gBattleMons[battler].status1 & STATUS1_ANY
|| IsAbilityOnSide(battler, ABILITY_SWEET_VEIL)
|| IsAbilityStatusProtected(battler)
|| IsBattlerTerrainAffected(battler, STATUS_FIELD_ELECTRIC_TERRAIN | STATUS_FIELD_MISTY_TERRAIN))
return FALSE;
return TRUE;
}
bool32 CanBePoisoned(u32 battlerAttacker, u32 battlerTarget)
{
u16 ability = GetBattlerAbility(battlerTarget);
if (!(CanPoisonType(battlerAttacker, battlerTarget))
|| gSideStatuses[GetBattlerSide(battlerTarget)] & SIDE_STATUS_SAFEGUARD
|| gBattleMons[battlerTarget].status1 & STATUS1_ANY
|| ability == ABILITY_IMMUNITY
|| ability == ABILITY_VITAL_SPIRIT
|| ability == ABILITY_COMATOSE
|| ability == ABILITY_PURIFYING_SALT
|| IsAbilityOnSide(battlerTarget, ABILITY_PASTEL_VEIL)
|| IsAbilityStatusProtected(battlerTarget)
|| IsBattlerTerrainAffected(battlerTarget, STATUS_FIELD_MISTY_TERRAIN))
|| gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD
|| gBattleMons[battler].status1 & STATUS1_ANY
|| IsAbilityOnSide(battler, ABILITY_SWEET_VEIL)
|| IsAbilityStatusProtected(battler)
|| IsBattlerTerrainAffected(battler, STATUS_FIELD_ELECTRIC_TERRAIN | STATUS_FIELD_MISTY_TERRAIN))
return FALSE;
return TRUE;
}
bool32 CanBeBurned(u32 battler)
bool32 CanBePoisoned(u32 battlerAtk, u32 battlerDef, u32 defAbility)
{
if (!(CanPoisonType(battlerAtk, battlerDef))
|| gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_SAFEGUARD
|| gBattleMons[battlerDef].status1 & STATUS1_ANY
|| defAbility == ABILITY_IMMUNITY
|| defAbility == ABILITY_COMATOSE
|| defAbility == ABILITY_PURIFYING_SALT
|| IsAbilityOnSide(battlerDef, ABILITY_PASTEL_VEIL)
|| IsAbilityStatusProtected(battlerDef)
|| IsBattlerTerrainAffected(battlerDef, STATUS_FIELD_MISTY_TERRAIN))
return FALSE;
return TRUE;
}
bool32 CanBeBurned(u32 battler, u32 ability)
{
u16 ability = GetBattlerAbility(battler);
if (IS_BATTLER_OF_TYPE(battler, TYPE_FIRE)
|| gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD
|| gBattleMons[battler].status1 & STATUS1_ANY
|| ability == ABILITY_WATER_VEIL
|| ability == ABILITY_WATER_BUBBLE
|| ability == ABILITY_COMATOSE
|| ability == ABILITY_THERMAL_EXCHANGE
|| ability == ABILITY_PURIFYING_SALT
|| IsAbilityStatusProtected(battler)
|| IsBattlerTerrainAffected(battler, STATUS_FIELD_MISTY_TERRAIN))
return FALSE;
return TRUE;
}
bool32 CanBeParalyzed(u32 battler, u32 ability)
{
if ((B_PARALYZE_ELECTRIC >= GEN_6 && IS_BATTLER_OF_TYPE(battler, TYPE_ELECTRIC))
|| gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD
|| gBattleMons[battler].status1 & STATUS1_ANY
|| ability == ABILITY_WATER_VEIL
|| ability == ABILITY_WATER_BUBBLE
|| ability == ABILITY_LIMBER
|| ability == ABILITY_COMATOSE
|| ability == ABILITY_THERMAL_EXCHANGE
|| ability == ABILITY_PURIFYING_SALT
|| gBattleMons[battler].status1 & STATUS1_ANY
|| IsAbilityStatusProtected(battler)
|| IsBattlerTerrainAffected(battler, STATUS_FIELD_MISTY_TERRAIN))
return FALSE;
return TRUE;
}
bool32 CanBeParalyzed(u32 battler)
{
u16 ability = GetBattlerAbility(battler);
if ((B_PARALYZE_ELECTRIC >= GEN_6 && IS_BATTLER_OF_TYPE(battler, TYPE_ELECTRIC))
|| gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD
|| ability == ABILITY_LIMBER
|| ability == ABILITY_COMATOSE
|| ability == ABILITY_PURIFYING_SALT
|| gBattleMons[battler].status1 & STATUS1_ANY
|| IsAbilityStatusProtected(battler)
|| IsBattlerTerrainAffected(battler, STATUS_FIELD_MISTY_TERRAIN))
return FALSE;
return TRUE;
}
bool32 CanBeFrozen(u32 battler)
{
u16 ability = GetBattlerAbility(battler);
if (IS_BATTLER_OF_TYPE(battler, TYPE_ICE)
|| IsBattlerWeatherAffected(battler, B_WEATHER_SUN)
|| gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD
|| ability == ABILITY_MAGMA_ARMOR
|| ability == ABILITY_COMATOSE
|| ability == ABILITY_PURIFYING_SALT
|| gBattleMons[battler].status1 & STATUS1_ANY
|| IsAbilityStatusProtected(battler)
|| IsBattlerTerrainAffected(battler, STATUS_FIELD_MISTY_TERRAIN))
|| IsBattlerWeatherAffected(battler, B_WEATHER_SUN)
|| gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD
|| ability == ABILITY_MAGMA_ARMOR
|| ability == ABILITY_COMATOSE
|| ability == ABILITY_PURIFYING_SALT
|| gBattleMons[battler].status1 & STATUS1_ANY
|| IsAbilityStatusProtected(battler)
|| IsBattlerTerrainAffected(battler, STATUS_FIELD_MISTY_TERRAIN))
return FALSE;
return TRUE;
}
@ -6426,8 +6424,8 @@ bool32 CanGetFrostbite(u32 battler)
bool32 CanBeConfused(u32 battler)
{
if (GetBattlerAbility(battler) == ABILITY_OWN_TEMPO
|| gBattleMons[battler].status2 & STATUS2_CONFUSION
|| IsBattlerTerrainAffected(battler, STATUS_FIELD_MISTY_TERRAIN))
|| gBattleMons[battler].status2 & STATUS2_CONFUSION
|| IsBattlerTerrainAffected(battler, STATUS_FIELD_MISTY_TERRAIN))
return FALSE;
return TRUE;
}
@ -7907,7 +7905,7 @@ u8 ItemBattleEffects(u8 caseID, u32 battler, bool32 moveTurn)
switch (battlerHoldEffect)
{
case HOLD_EFFECT_TOXIC_ORB:
if (CanBePoisoned(battler, battler))
if (CanBePoisoned(battler, battler, GetBattlerAbility(battler)))
{
effect = ITEM_STATUS_CHANGE;
gBattleMons[battler].status1 = STATUS1_TOXIC_POISON;
@ -7916,7 +7914,7 @@ u8 ItemBattleEffects(u8 caseID, u32 battler, bool32 moveTurn)
}
break;
case HOLD_EFFECT_FLAME_ORB:
if (CanBeBurned(battler))
if (CanBeBurned(battler, battlerAbility))
{
effect = ITEM_STATUS_CHANGE;
gBattleMons[battler].status1 = STATUS1_BURN;
@ -8185,7 +8183,7 @@ u8 IsMonDisobedient(void)
obedienceLevel = levelReferenced - obedienceLevel;
calc = (Random() & 255);
if (calc < obedienceLevel && CanSleep(gBattlerAttacker))
if (calc < obedienceLevel && CanBeSlept(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker)))
{
// try putting asleep
int i;

View File

@ -56,6 +56,23 @@ SINGLE_BATTLE_TEST("Own Tempo prevents confusion from moves by the user")
}
}
SINGLE_BATTLE_TEST("Own Tempo is ignored by Mold Breaker")
{
KNOWN_FAILING; // Ideally the func CanBeConfused should be split into AttackerCanBeConfused and TargetCanBeConfused or we do it in the same func but have a check for when battlerAtk == battlerDef
GIVEN {
ASSUME(gMovesInfo[MOVE_CONFUSE_RAY].effect == EFFECT_CONFUSE);
PLAYER(SPECIES_PINSIR) { Ability(ABILITY_MOLD_BREAKER); }
OPPONENT(SPECIES_SLOWPOKE) { Ability(ABILITY_OWN_TEMPO); };
} WHEN {
TURN { MOVE(player, MOVE_CONFUSE_RAY); }
} SCENE {
NONE_OF {
ABILITY_POPUP(opponent, ABILITY_OWN_TEMPO);
MESSAGE("Foe Slowpoke's Own Tempo prevents confusion!");
}
}
}
SINGLE_BATTLE_TEST("Own Tempo cures confusion obtained from an opponent with Mold Breaker")
{
KNOWN_FAILING;

View File

@ -1039,3 +1039,4 @@ AI_SINGLE_BATTLE_TEST("AI avoids contact moves against rocky helmet")
TURN { EXPECT_MOVES(opponent, MOVE_LEAFAGE, MOVE_BRANCH_POKE); }
}
}

View File

@ -48,3 +48,21 @@ SINGLE_BATTLE_TEST("Toxic cannot miss if used by a Poison-type")
}
}
}
AI_SINGLE_BATTLE_TEST("AI avoids toxic when it can not poison target")
{
u32 species, ability;
PARAMETRIZE { species = SPECIES_SNORLAX; ability = ABILITY_IMMUNITY; }
PARAMETRIZE { species = SPECIES_KOMALA; ability = ABILITY_COMATOSE; }
PARAMETRIZE { species = SPECIES_NACLI; ability = ABILITY_PURIFYING_SALT; }
PARAMETRIZE { species = SPECIES_BULBASAUR; ability = ABILITY_OVERGROW; }
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT);
PLAYER(species) { Ability(ability); }
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_CELEBRATE, MOVE_TOXIC); }
} WHEN {
TURN { SCORE_EQ(opponent, MOVE_CELEBRATE, MOVE_TOXIC); } // Both get -10
}
}

View File

@ -35,3 +35,23 @@ SINGLE_BATTLE_TEST("Burn reduces Attack by 50%", s16 damage)
EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage);
}
}
AI_SINGLE_BATTLE_TEST("AI avoids Will-o-Wisp when it can not burn target")
{
u32 species, ability;
PARAMETRIZE { species = SPECIES_BUIZEL; ability = ABILITY_WATER_VEIL; }
PARAMETRIZE { species = SPECIES_DEWPIDER; ability = ABILITY_WATER_BUBBLE; }
PARAMETRIZE { species = SPECIES_KOMALA; ability = ABILITY_COMATOSE; }
PARAMETRIZE { species = SPECIES_ARCTIBAX; ability = ABILITY_THERMAL_EXCHANGE; }
PARAMETRIZE { species = SPECIES_NACLI; ability = ABILITY_PURIFYING_SALT; }
PARAMETRIZE { species = SPECIES_CHARMANDER; ability = ABILITY_BLAZE; }
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT);
PLAYER(species) { Ability(ability); }
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_CELEBRATE, MOVE_WILL_O_WISP); }
} WHEN {
TURN { SCORE_EQ(opponent, MOVE_CELEBRATE, MOVE_WILL_O_WISP); } // Both get -10
}
}

View File

@ -42,3 +42,21 @@ SINGLE_BATTLE_TEST("Paralysis has a 25% chance of skipping the turn")
MESSAGE("Wobbuffet is paralyzed! It can't move!");
}
}
AI_SINGLE_BATTLE_TEST("AI avoids Thunder Wave when it can not paralyse target")
{
u32 species, ability;
PARAMETRIZE { species = SPECIES_HITMONLEE; ability = ABILITY_LIMBER; }
PARAMETRIZE { species = SPECIES_KOMALA; ability = ABILITY_COMATOSE; }
PARAMETRIZE { species = SPECIES_NACLI; ability = ABILITY_PURIFYING_SALT; }
PARAMETRIZE { species = SPECIES_PIKACHU; ability = ABILITY_STATIC; }
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT);
PLAYER(species) { Ability(ability); }
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_CELEBRATE, MOVE_THUNDER_WAVE); }
} WHEN {
TURN { SCORE_EQ(opponent, MOVE_CELEBRATE, MOVE_THUNDER_WAVE); } // Both get -10
}
}

View File

@ -21,3 +21,21 @@ SINGLE_BATTLE_TEST("Sleep prevents the battler from using a move")
MESSAGE("Wobbuffet used Celebrate!");
}
}
AI_SINGLE_BATTLE_TEST("AI avoids hypnosis when it can not put target to sleep")
{
u32 species, ability;
PARAMETRIZE { species = SPECIES_HOOTHOOT; ability = ABILITY_INSOMNIA; }
PARAMETRIZE { species = SPECIES_MANKEY; ability = ABILITY_VITAL_SPIRIT; }
PARAMETRIZE { species = SPECIES_KOMALA; ability = ABILITY_COMATOSE; }
PARAMETRIZE { species = SPECIES_NACLI; ability = ABILITY_PURIFYING_SALT; }
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT);
PLAYER(species) { Ability(ability); }
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_CELEBRATE, MOVE_HYPNOSIS); }
} WHEN {
TURN { SCORE_EQ(opponent, MOVE_CELEBRATE, MOVE_HYPNOSIS); } // Both get -10
}
}