Move most damage AI_BadMove checks to AI_CalcDamage (#4238)
* Move a couple damage AI_BadMove checks to AI_CalcDamage * re-add effectivness score decrease * reduce score for bad move in ai_checkviability * review changes
This commit is contained in:
parent
5acc770f00
commit
8d58af4d33
3 changed files with 131 additions and 103 deletions
|
@ -798,47 +798,30 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
|||
SetTypeBeforeUsingMove(move, battlerAtk);
|
||||
GET_MOVE_TYPE(move, moveType);
|
||||
|
||||
if (gMovesInfo[move].powderMove && !IsAffectedByPowder(battlerDef, aiData->abilities[battlerDef], aiData->holdEffects[battlerDef]))
|
||||
RETURN_SCORE_MINUS(10);
|
||||
|
||||
if (IsSemiInvulnerable(battlerDef, move) && moveEffect != EFFECT_SEMI_INVULNERABLE && AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER)
|
||||
RETURN_SCORE_MINUS(10);
|
||||
|
||||
if (IsTwoTurnNotSemiInvulnerableMove(battlerAtk, move) && CanTargetFaintAi(battlerDef, battlerAtk))
|
||||
RETURN_SCORE_MINUS(10);
|
||||
|
||||
// check if negates type
|
||||
switch (effectiveness)
|
||||
{
|
||||
case AI_EFFECTIVENESS_x0:
|
||||
RETURN_SCORE_MINUS(20);
|
||||
break;
|
||||
case AI_EFFECTIVENESS_x0_125:
|
||||
case AI_EFFECTIVENESS_x0_25:
|
||||
RETURN_SCORE_MINUS(10);
|
||||
break;
|
||||
}
|
||||
|
||||
// check non-user target
|
||||
if (!(moveTarget & MOVE_TARGET_USER))
|
||||
{
|
||||
// handle negative checks on non-user target
|
||||
// check powder moves
|
||||
if (gMovesInfo[move].powderMove && !IsAffectedByPowder(battlerDef, aiData->abilities[battlerDef], aiData->holdEffects[battlerDef]))
|
||||
{
|
||||
RETURN_SCORE_MINUS(20);
|
||||
}
|
||||
|
||||
// check ground immunities
|
||||
if (moveType == TYPE_GROUND
|
||||
&& !IsBattlerGrounded(battlerDef)
|
||||
&& ((aiData->abilities[battlerDef] == ABILITY_LEVITATE
|
||||
&& DoesBattlerIgnoreAbilityChecks(aiData->abilities[battlerAtk], move))
|
||||
|| aiData->holdEffects[battlerDef] == HOLD_EFFECT_AIR_BALLOON
|
||||
|| (gStatuses3[battlerDef] & (STATUS3_MAGNET_RISE | STATUS3_TELEKINESIS)))
|
||||
&& move != MOVE_THOUSAND_ARROWS)
|
||||
{
|
||||
RETURN_SCORE_MINUS(20);
|
||||
}
|
||||
|
||||
// check off screen
|
||||
if (IsSemiInvulnerable(battlerDef, move) && moveEffect != EFFECT_SEMI_INVULNERABLE && AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER)
|
||||
RETURN_SCORE_MINUS(20); // if target off screen and we go first, don't use move
|
||||
|
||||
if (IsTwoTurnNotSemiInvulnerableMove(battlerAtk, move) && CanTargetFaintAi(battlerDef, battlerAtk))
|
||||
RETURN_SCORE_MINUS(10);
|
||||
|
||||
// check if negates type
|
||||
switch (effectiveness)
|
||||
{
|
||||
case AI_EFFECTIVENESS_x0:
|
||||
RETURN_SCORE_MINUS(20);
|
||||
break;
|
||||
case AI_EFFECTIVENESS_x0_125:
|
||||
case AI_EFFECTIVENESS_x0_25:
|
||||
RETURN_SCORE_MINUS(10);
|
||||
break;
|
||||
}
|
||||
|
||||
// target ability checks
|
||||
if (!DoesBattlerIgnoreAbilityChecks(aiData->abilities[battlerAtk], move))
|
||||
{
|
||||
|
@ -859,30 +842,10 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
|||
break;
|
||||
}
|
||||
break;
|
||||
case ABILITY_VOLT_ABSORB:
|
||||
case ABILITY_MOTOR_DRIVE:
|
||||
case ABILITY_LIGHTNING_ROD:
|
||||
if (moveType == TYPE_ELECTRIC)
|
||||
RETURN_SCORE_MINUS(20);
|
||||
break;
|
||||
case ABILITY_WATER_ABSORB:
|
||||
case ABILITY_DRY_SKIN:
|
||||
case ABILITY_STORM_DRAIN:
|
||||
if (moveType == TYPE_WATER)
|
||||
RETURN_SCORE_MINUS(20);
|
||||
break;
|
||||
case ABILITY_FLASH_FIRE:
|
||||
if (moveType == TYPE_FIRE)
|
||||
RETURN_SCORE_MINUS(20);
|
||||
break;
|
||||
case ABILITY_WONDER_GUARD:
|
||||
if (effectiveness < AI_EFFECTIVENESS_x2)
|
||||
return 0;
|
||||
break;
|
||||
case ABILITY_SAP_SIPPER:
|
||||
if (moveType == TYPE_GRASS)
|
||||
RETURN_SCORE_MINUS(20);
|
||||
break;
|
||||
case ABILITY_JUSTIFIED:
|
||||
if (moveType == TYPE_DARK && !IS_MOVE_STATUS(move))
|
||||
RETURN_SCORE_MINUS(10);
|
||||
|
@ -892,14 +855,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_SOUNDPROOF:
|
||||
if (gMovesInfo[move].soundMove)
|
||||
RETURN_SCORE_MINUS(10);
|
||||
break;
|
||||
case ABILITY_BULLETPROOF:
|
||||
if (gMovesInfo[move].ballisticMove)
|
||||
RETURN_SCORE_MINUS(10);
|
||||
break;
|
||||
case ABILITY_DAZZLING:
|
||||
case ABILITY_QUEENLY_MAJESTY:
|
||||
case ABILITY_ARMOR_TAIL:
|
||||
|
@ -1105,12 +1060,6 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
|||
ADJUST_SCORE(-1);
|
||||
}
|
||||
break;
|
||||
case EFFECT_DREAM_EATER:
|
||||
if (!AI_IsBattlerAsleepOrComatose(battlerDef))
|
||||
ADJUST_SCORE(-8);
|
||||
else if (effectiveness == AI_EFFECTIVENESS_x0)
|
||||
ADJUST_SCORE(-10);
|
||||
break;
|
||||
// stat raising effects
|
||||
case EFFECT_ATTACK_UP:
|
||||
case EFFECT_ATTACK_UP_2:
|
||||
|
@ -1906,10 +1855,6 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
|||
else if (aiData->hpPercents[battlerAtk] >= 90)
|
||||
ADJUST_SCORE(-8); //No point in healing, but should at least do it if nothing better
|
||||
break;
|
||||
case EFFECT_SUPER_FANG:
|
||||
if (aiData->hpPercents[battlerDef] < 50)
|
||||
ADJUST_SCORE(-4);
|
||||
break;
|
||||
case EFFECT_RECOIL_IF_MISS:
|
||||
if (aiData->abilities[battlerAtk] != ABILITY_MAGIC_GUARD && AI_DATA->moveAccuracy[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex] < 75)
|
||||
ADJUST_SCORE(-6);
|
||||
|
@ -1935,11 +1880,6 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
|||
break;
|
||||
case EFFECT_METRONOME:
|
||||
break;
|
||||
case EFFECT_ENDEAVOR:
|
||||
case EFFECT_PAIN_SPLIT:
|
||||
if (gBattleMons[battlerAtk].hp > (gBattleMons[battlerAtk].hp + gBattleMons[battlerDef].hp) / 2)
|
||||
ADJUST_SCORE(-10);
|
||||
break;
|
||||
|
||||
case EFFECT_CONVERSION_2:
|
||||
//TODO
|
||||
|
@ -1965,9 +1905,6 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
|||
if (gBattleMons[battlerDef].status2 & STATUS2_DESTINY_BOND)
|
||||
ADJUST_SCORE(-10);
|
||||
break;
|
||||
case EFFECT_FALSE_SWIPE:
|
||||
// TODO
|
||||
break;
|
||||
case EFFECT_HEAL_BELL:
|
||||
if (!AnyPartyMemberStatused(battlerAtk, gMovesInfo[move].soundMove) || PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, aiData->partnerMove))
|
||||
ADJUST_SCORE(-10);
|
||||
|
@ -2050,10 +1987,6 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
|||
|| DoesPartnerHaveSameMoveEffect(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove))
|
||||
ADJUST_SCORE(-9);
|
||||
break;
|
||||
case EFFECT_FAIL_IF_NOT_ARG_TYPE:
|
||||
if (!IS_BATTLER_OF_TYPE(battlerAtk, gMovesInfo[move].argument))
|
||||
ADJUST_SCORE(-10);
|
||||
break;
|
||||
case EFFECT_DEFOG:
|
||||
if (gSideStatuses[GetBattlerSide(battlerDef)]
|
||||
& (SIDE_STATUS_REFLECT | SIDE_STATUS_LIGHTSCREEN | SIDE_STATUS_AURORA_VEIL | SIDE_STATUS_SAFEGUARD | SIDE_STATUS_MIST)
|
||||
|
@ -2155,10 +2088,6 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
|||
if (!HasMagicCoatAffectedMove(battlerDef))
|
||||
ADJUST_SCORE(-10);
|
||||
break;
|
||||
case EFFECT_BELCH:
|
||||
if (ItemId_GetPocket(GetUsedHeldItem(battlerAtk)) != POCKET_BERRIES)
|
||||
ADJUST_SCORE(-10); // attacker has not consumed a berry
|
||||
break;
|
||||
case EFFECT_YAWN:
|
||||
if (gStatuses3[battlerDef] & STATUS3_YAWN)
|
||||
ADJUST_SCORE(-10);
|
||||
|
@ -2578,10 +2507,6 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
|||
if (!CanCamouflage(battlerAtk))
|
||||
ADJUST_SCORE(-10);
|
||||
break;
|
||||
case EFFECT_LAST_RESORT:
|
||||
if (!CanUseLastResort(battlerAtk))
|
||||
ADJUST_SCORE(-10);
|
||||
break;
|
||||
case EFFECT_SYNCHRONOISE:
|
||||
//Check holding ring target or is of same type
|
||||
if (aiData->holdEffects[battlerDef] == HOLD_EFFECT_RING_TARGET
|
||||
|
@ -2655,10 +2580,6 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
|||
&& !BattlerStatCanRise(battlerAtk, aiData->abilities[battlerAtk], STAT_SPDEF))
|
||||
ADJUST_SCORE(-10);
|
||||
break;
|
||||
case EFFECT_LOW_KICK:
|
||||
if (IsDynamaxed(battlerDef))
|
||||
ADJUST_SCORE(-10);
|
||||
break;
|
||||
case EFFECT_UPPER_HAND:
|
||||
if (predictedMove == MOVE_NONE || IS_MOVE_STATUS(predictedMove) || AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_SLOWER || GetMovePriority(battlerDef, move) < 1 || GetMovePriority(battlerDef, move) > 3) // Opponent going first or not using priority move
|
||||
ADJUST_SCORE(-10);
|
||||
|
@ -3140,7 +3061,7 @@ static s32 AI_CompareDamagingMoves(u32 battlerAtk, u32 battlerDef, u32 currId)
|
|||
if (moves[i] != MOVE_NONE && gMovesInfo[moves[i]].power)
|
||||
{
|
||||
noOfHits[i] = GetNoOfHitsToKOBattler(battlerAtk, battlerDef, i);
|
||||
if (noOfHits[i] < leastHits)
|
||||
if (noOfHits[i] < leastHits && noOfHits[i] != 0)
|
||||
{
|
||||
leastHits = noOfHits[i];
|
||||
}
|
||||
|
@ -4694,7 +4615,12 @@ static s32 AI_CheckViability(u32 battlerAtk, u32 battlerDef, u32 move, s32 score
|
|||
return score;
|
||||
|
||||
if (gMovesInfo[move].power)
|
||||
score += AI_CompareDamagingMoves(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex);
|
||||
{
|
||||
if (GetNoOfHitsToKOBattler(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex) == 0)
|
||||
ADJUST_SCORE(-20);
|
||||
else
|
||||
score += AI_CompareDamagingMoves(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex);
|
||||
}
|
||||
|
||||
// Calculates score based on effects of a move
|
||||
score += AI_CalcMoveScore(battlerAtk, battlerDef, move);
|
||||
|
|
|
@ -366,10 +366,84 @@ static inline s32 LowestRollDmg(s32 dmg)
|
|||
return dmg;
|
||||
}
|
||||
|
||||
bool32 IsDamageMoveUsable(u32 move, u32 battlerAtk, u32 battlerDef)
|
||||
{
|
||||
s32 moveType;
|
||||
struct AiLogicData *aiData = AI_DATA;
|
||||
u32 battlerDefAbility;
|
||||
|
||||
if (DoesBattlerIgnoreAbilityChecks(aiData->abilities[battlerAtk], move))
|
||||
battlerDefAbility = ABILITY_NONE;
|
||||
else
|
||||
battlerDefAbility = aiData->abilities[battlerDef];
|
||||
|
||||
SetTypeBeforeUsingMove(move, battlerAtk);
|
||||
GET_MOVE_TYPE(move, moveType);
|
||||
|
||||
switch (battlerDefAbility)
|
||||
{
|
||||
case ABILITY_VOLT_ABSORB:
|
||||
case ABILITY_MOTOR_DRIVE:
|
||||
case ABILITY_LIGHTNING_ROD:
|
||||
if (moveType == TYPE_ELECTRIC)
|
||||
return TRUE;
|
||||
break;
|
||||
case ABILITY_WATER_ABSORB:
|
||||
case ABILITY_DRY_SKIN:
|
||||
case ABILITY_STORM_DRAIN:
|
||||
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;
|
||||
}
|
||||
|
||||
switch (gMovesInfo[move].effect)
|
||||
{
|
||||
case EFFECT_DREAM_EATER:
|
||||
if (!AI_IsBattlerAsleepOrComatose(battlerDef))
|
||||
return TRUE;
|
||||
break;
|
||||
case EFFECT_BELCH:
|
||||
if (ItemId_GetPocket(GetUsedHeldItem(battlerAtk)) != POCKET_BERRIES)
|
||||
return TRUE;
|
||||
break;
|
||||
case EFFECT_LAST_RESORT:
|
||||
if (!CanUseLastResort(battlerAtk))
|
||||
return TRUE;
|
||||
break;
|
||||
case EFFECT_LOW_KICK:
|
||||
if (IsDynamaxed(battlerDef))
|
||||
return TRUE;
|
||||
break;
|
||||
case EFFECT_FAIL_IF_NOT_ARG_TYPE:
|
||||
if (!IS_BATTLER_OF_TYPE(battlerAtk, gMovesInfo[move].argument))
|
||||
return TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
s32 AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u8 *typeEffectiveness, bool32 considerZPower, u32 weather)
|
||||
{
|
||||
s32 dmg, moveType;
|
||||
uq4_12_t effectivenessMultiplier;
|
||||
bool32 isDamageMoveUnusable = FALSE;
|
||||
struct AiLogicData *aiData = AI_DATA;
|
||||
|
||||
SetBattlerData(battlerAtk);
|
||||
|
@ -393,8 +467,11 @@ s32 AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u8 *typeEffectivenes
|
|||
SetTypeBeforeUsingMove(move, battlerAtk);
|
||||
GET_MOVE_TYPE(move, moveType);
|
||||
|
||||
effectivenessMultiplier = CalcTypeEffectivenessMultiplier(move, moveType, battlerAtk, battlerDef, aiData->abilities[battlerDef], FALSE);
|
||||
if (gMovesInfo[move].power)
|
||||
isDamageMoveUnusable = IsDamageMoveUsable(move, battlerAtk, battlerDef);
|
||||
|
||||
effectivenessMultiplier = CalcTypeEffectivenessMultiplier(move, moveType, battlerAtk, battlerDef, aiData->abilities[battlerDef], FALSE);
|
||||
if (gMovesInfo[move].power && !isDamageMoveUnusable)
|
||||
{
|
||||
s32 critChanceIndex, normalDmg, fixedBasePower, n;
|
||||
|
||||
|
|
|
@ -752,3 +752,28 @@ AI_DOUBLE_BATTLE_TEST("AI will not try to switch for the same pokemon for 2 spot
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
AI_SINGLE_BATTLE_TEST("AI will not choose Burn Up if the user lost the Fire typing")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_BURN_UP].effect == EFFECT_FAIL_IF_NOT_ARG_TYPE);
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_CYNDAQUIL) { Moves(MOVE_BURN_UP, MOVE_EXTRASENSORY, MOVE_FLAMETHROWER); }
|
||||
} WHEN {
|
||||
TURN { EXPECT_MOVE(opponent, MOVE_BURN_UP); }
|
||||
TURN { EXPECT_MOVE(opponent, MOVE_FLAMETHROWER); }
|
||||
}
|
||||
}
|
||||
|
||||
AI_SINGLE_BATTLE_TEST("AI will choose Surf over Thunderbolt and Ice Beam if the opposing mon has Volt Absorb")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_THUNDERBOLT].type == TYPE_ELECTRIC);
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
|
||||
PLAYER(SPECIES_LANTURN) { Ability(ABILITY_VOLT_ABSORB); };
|
||||
OPPONENT(SPECIES_LANTURN) { Moves(MOVE_THUNDERBOLT, MOVE_ICE_BEAM, MOVE_SURF); }
|
||||
} WHEN {
|
||||
TURN { EXPECT_MOVE(opponent, MOVE_SURF); }
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue