Review changes
Numerous AI updates and test fixes; added a test for Aura Wheel
This commit is contained in:
parent
59d76c9321
commit
25f0179628
@ -758,6 +758,235 @@ static inline void BattleAI_DoAIProcessing(struct AI_ThinkingStruct *aiThink, u3
|
||||
aiThink->movesetIndex = 0;
|
||||
}
|
||||
|
||||
static s32 AI_CheckMoveEffects(u32 battlerAtk, u32 battlerDef, u32 move, s32 score, struct AiLogicData *aiData, u32 predictedMove, bool32 isDoubleBattle)
|
||||
{
|
||||
u8 i;
|
||||
// check move additional effects that are likely to happen
|
||||
for (i = 0; i < gBattleMoves[move].numAdditionalEffects; i++)
|
||||
{
|
||||
// Only consider effects with a guaranteed chance to happen
|
||||
if (!MoveEffectIsGuaranteed(battlerAtk, aiData->abilities[battlerAtk], &gBattleMoves[move].additionalEffects[i]))
|
||||
continue;
|
||||
|
||||
// Consider move effects that target self
|
||||
if (gBattleMoves[move].additionalEffects[i].self)
|
||||
{
|
||||
switch (gBattleMoves[move].additionalEffects[i].moveEffect)
|
||||
{
|
||||
case MOVE_EFFECT_SPD_PLUS_2:
|
||||
case MOVE_EFFECT_SPD_PLUS_1:
|
||||
if (aiData->abilities[battlerAtk] != ABILITY_CONTRARY && !AI_STRIKES_FIRST(battlerAtk, battlerDef, move))
|
||||
ADJUST_SCORE(3);
|
||||
break;
|
||||
case MOVE_EFFECT_ATK_PLUS_1:
|
||||
case MOVE_EFFECT_DEF_PLUS_1:
|
||||
case MOVE_EFFECT_SP_ATK_PLUS_1:
|
||||
case MOVE_EFFECT_SP_DEF_PLUS_1:
|
||||
case MOVE_EFFECT_ACC_PLUS_1:
|
||||
case MOVE_EFFECT_EVS_PLUS_1:
|
||||
IncreaseStatUpScore(
|
||||
battlerAtk,
|
||||
battlerDef,
|
||||
STAT_ATK + gBattleMoves[move].additionalEffects[i].moveEffect - MOVE_EFFECT_ATK_PLUS_1,
|
||||
&score
|
||||
);
|
||||
break;
|
||||
case MOVE_EFFECT_ATK_PLUS_2:
|
||||
case MOVE_EFFECT_DEF_PLUS_2:
|
||||
case MOVE_EFFECT_SP_ATK_PLUS_2:
|
||||
case MOVE_EFFECT_SP_DEF_PLUS_2:
|
||||
case MOVE_EFFECT_ACC_PLUS_2:
|
||||
case MOVE_EFFECT_EVS_PLUS_2:
|
||||
IncreaseStatUpScore(
|
||||
battlerAtk,
|
||||
battlerDef,
|
||||
STAT_ATK + gBattleMoves[move].additionalEffects[i].moveEffect - MOVE_EFFECT_ATK_PLUS_2,
|
||||
&score
|
||||
);
|
||||
break;
|
||||
// Effects that lower stat(s) - only need to consider Contrary
|
||||
case MOVE_EFFECT_ATK_MINUS_1:
|
||||
case MOVE_EFFECT_DEF_MINUS_1:
|
||||
case MOVE_EFFECT_SPD_MINUS_1:
|
||||
case MOVE_EFFECT_SP_ATK_MINUS_1:
|
||||
case MOVE_EFFECT_SP_DEF_MINUS_1:
|
||||
case MOVE_EFFECT_V_CREATE:
|
||||
case MOVE_EFFECT_DEF_SPDEF_DOWN:
|
||||
case MOVE_EFFECT_ATK_DEF_DOWN:
|
||||
case MOVE_EFFECT_SP_ATK_TWO_DOWN:
|
||||
if (aiData->abilities[battlerAtk] == ABILITY_CONTRARY)
|
||||
ADJUST_SCORE(3);
|
||||
break;
|
||||
case MOVE_EFFECT_RAPIDSPIN:
|
||||
if ((gSideStatuses[GetBattlerSide(battlerAtk)] & SIDE_STATUS_HAZARDS_ANY && CountUsablePartyMons(battlerAtk) != 0)
|
||||
|| (gStatuses3[battlerAtk] & STATUS3_LEECHSEED || gBattleMons[battlerAtk].status2 & STATUS2_WRAPPED))
|
||||
{
|
||||
ADJUST_SCORE(3);
|
||||
break;
|
||||
}
|
||||
//Spin checks
|
||||
if (!(gSideStatuses[GetBattlerSide(battlerAtk)] & SIDE_STATUS_HAZARDS_ANY))
|
||||
ADJUST_SCORE(-6);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else // consider move effects that hinder the target
|
||||
{
|
||||
switch (gBattleMoves[move].additionalEffects[i].moveEffect)
|
||||
{
|
||||
case MOVE_EFFECT_FLINCH:
|
||||
score += ShouldTryToFlinch(battlerAtk, battlerDef, aiData->abilities[battlerAtk], aiData->abilities[battlerDef], move);
|
||||
break;
|
||||
case MOVE_EFFECT_SPD_MINUS_1:
|
||||
case MOVE_EFFECT_SPD_MINUS_2:
|
||||
if (!ShouldLowerSpeed(battlerAtk, battlerDef, aiData->abilities[battlerDef]))
|
||||
break;
|
||||
case MOVE_EFFECT_ATK_MINUS_1:
|
||||
case MOVE_EFFECT_DEF_MINUS_1:
|
||||
case MOVE_EFFECT_SP_ATK_MINUS_1:
|
||||
case MOVE_EFFECT_SP_DEF_MINUS_1:
|
||||
case MOVE_EFFECT_ACC_MINUS_1:
|
||||
case MOVE_EFFECT_EVS_MINUS_1:
|
||||
if (aiData->abilities[battlerDef] != ABILITY_CONTRARY)
|
||||
ADJUST_SCORE(2);
|
||||
break;
|
||||
case MOVE_EFFECT_ATK_MINUS_2:
|
||||
case MOVE_EFFECT_DEF_MINUS_2:
|
||||
case MOVE_EFFECT_SP_ATK_MINUS_2:
|
||||
case MOVE_EFFECT_SP_DEF_MINUS_2:
|
||||
case MOVE_EFFECT_ACC_MINUS_2:
|
||||
case MOVE_EFFECT_EVS_MINUS_2:
|
||||
if (aiData->abilities[battlerDef] != ABILITY_CONTRARY)
|
||||
ADJUST_SCORE(3);
|
||||
break;
|
||||
case MOVE_EFFECT_POISON:
|
||||
IncreasePoisonScore(battlerAtk, battlerDef, move, &score);
|
||||
break;
|
||||
case MOVE_EFFECT_CLEAR_SMOG:
|
||||
score += AI_TryToClearStats(battlerAtk, battlerDef, FALSE);
|
||||
break;
|
||||
case MOVE_EFFECT_SPECTRAL_THIEF:
|
||||
score += AI_ShouldCopyStatChanges(battlerAtk, battlerDef);
|
||||
break;
|
||||
case MOVE_EFFECT_BUG_BITE: // And pluck
|
||||
if (gBattleMons[battlerDef].status2 & STATUS2_SUBSTITUTE || aiData->abilities[battlerDef] == ABILITY_STICKY_HOLD)
|
||||
break;
|
||||
else if (ItemId_GetPocket(aiData->items[battlerDef]) == POCKET_BERRIES)
|
||||
ADJUST_SCORE(3);
|
||||
break;
|
||||
case MOVE_EFFECT_INCINERATE:
|
||||
if (gBattleMons[battlerDef].status2 & STATUS2_SUBSTITUTE || aiData->abilities[battlerDef] == ABILITY_STICKY_HOLD)
|
||||
break;
|
||||
else if (ItemId_GetPocket(aiData->items[battlerDef]) == POCKET_BERRIES || aiData->holdEffects[battlerDef] == HOLD_EFFECT_GEMS)
|
||||
ADJUST_SCORE(3);
|
||||
break;
|
||||
case MOVE_EFFECT_SMACK_DOWN:
|
||||
if (!IsBattlerGrounded(battlerDef) && HasDamagingMoveOfType(battlerAtk, TYPE_GROUND))
|
||||
ADJUST_SCORE(1);
|
||||
break;
|
||||
case MOVE_EFFECT_KNOCK_OFF:
|
||||
if (CanKnockOffItem(battlerDef, aiData->items[battlerDef]))
|
||||
{
|
||||
switch (aiData->holdEffects[battlerDef])
|
||||
{
|
||||
case HOLD_EFFECT_IRON_BALL:
|
||||
if (HasMoveEffect(battlerDef, EFFECT_FLING))
|
||||
ADJUST_SCORE(4);
|
||||
break;
|
||||
case HOLD_EFFECT_LAGGING_TAIL:
|
||||
case HOLD_EFFECT_STICKY_BARB:
|
||||
break;
|
||||
default:
|
||||
ADJUST_SCORE(3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MOVE_EFFECT_STEAL_ITEM:
|
||||
{
|
||||
bool32 canSteal = FALSE;
|
||||
|
||||
if (B_TRAINERS_KNOCK_OFF_ITEMS == TRUE)
|
||||
canSteal = TRUE;
|
||||
if (gBattleTypeFlags & BATTLE_TYPE_FRONTIER || GetBattlerSide(battlerAtk) == B_SIDE_PLAYER)
|
||||
canSteal = TRUE;
|
||||
|
||||
if (canSteal && aiData->items[battlerAtk] == ITEM_NONE
|
||||
&& aiData->items[battlerDef] != ITEM_NONE
|
||||
&& CanBattlerGetOrLoseItem(battlerDef, aiData->items[battlerDef])
|
||||
&& CanBattlerGetOrLoseItem(battlerAtk, aiData->items[battlerDef])
|
||||
&& !HasMoveEffect(battlerAtk, EFFECT_ACROBATICS)
|
||||
&& aiData->abilities[battlerDef] != ABILITY_STICKY_HOLD)
|
||||
{
|
||||
switch (aiData->holdEffects[battlerDef])
|
||||
{
|
||||
case HOLD_EFFECT_NONE:
|
||||
break;
|
||||
case HOLD_EFFECT_CHOICE_BAND:
|
||||
case HOLD_EFFECT_CHOICE_SCARF:
|
||||
case HOLD_EFFECT_CHOICE_SPECS:
|
||||
ADJUST_SCORE(2);
|
||||
break;
|
||||
case HOLD_EFFECT_TOXIC_ORB:
|
||||
if (ShouldPoisonSelf(battlerAtk, aiData->abilities[battlerAtk]))
|
||||
ADJUST_SCORE(2);
|
||||
break;
|
||||
case HOLD_EFFECT_FLAME_ORB:
|
||||
if (ShouldBurnSelf(battlerAtk, aiData->abilities[battlerAtk]))
|
||||
ADJUST_SCORE(2);
|
||||
break;
|
||||
case HOLD_EFFECT_BLACK_SLUDGE:
|
||||
if (IS_BATTLER_OF_TYPE(battlerAtk, TYPE_POISON))
|
||||
ADJUST_SCORE(2);
|
||||
break;
|
||||
case HOLD_EFFECT_IRON_BALL:
|
||||
if (HasMoveEffect(battlerAtk, EFFECT_FLING))
|
||||
ADJUST_SCORE(2);
|
||||
break;
|
||||
case HOLD_EFFECT_LAGGING_TAIL:
|
||||
case HOLD_EFFECT_STICKY_BARB:
|
||||
break;
|
||||
default:
|
||||
ADJUST_SCORE(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case MOVE_EFFECT_STEALTH_ROCK:
|
||||
case MOVE_EFFECT_SPIKES:
|
||||
score += AI_ShouldSetUpHazards(battlerAtk, battlerDef, aiData);
|
||||
break;
|
||||
case MOVE_EFFECT_FEINT:
|
||||
if (gBattleMoves[predictedMove].effect == EFFECT_PROTECT)
|
||||
ADJUST_SCORE(3);
|
||||
break;
|
||||
case MOVE_EFFECT_THROAT_CHOP:
|
||||
if (HasSoundMove(battlerDef) && gBattleMoves[predictedMove].soundMove && AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER)
|
||||
ADJUST_SCORE(3);
|
||||
break;
|
||||
case MOVE_EFFECT_FLAME_BURST:
|
||||
if (isDoubleBattle)
|
||||
{
|
||||
if (IsBattlerAlive(BATTLE_PARTNER(battlerDef))
|
||||
&& aiData->hpPercents[BATTLE_PARTNER(battlerDef)] < 12
|
||||
&& aiData->abilities[BATTLE_PARTNER(battlerDef)] != ABILITY_MAGIC_GUARD
|
||||
&& !IS_BATTLER_OF_TYPE(BATTLE_PARTNER(battlerDef), TYPE_FIRE))
|
||||
ADJUST_SCORE(1);
|
||||
}
|
||||
break;
|
||||
case MOVE_EFFECT_WRAP:
|
||||
if (!HasMoveWithMoveEffect(battlerDef, MOVE_EFFECT_RAPIDSPIN) && !IsBattlerTrapped(battlerDef, TRUE) && ShouldTrap(battlerAtk, battlerDef, move))
|
||||
ADJUST_SCORE(5);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
// AI Score Functions
|
||||
// AI_FLAG_CHECK_BAD_MOVE - decreases move scores
|
||||
static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
@ -1052,23 +1281,15 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
}
|
||||
}
|
||||
|
||||
// check things that aren't move effects (recoil)
|
||||
if (gBattleMoves[move].recoil > 0)
|
||||
{
|
||||
if (AI_IsDamagedByRecoil(battlerAtk))
|
||||
{
|
||||
u32 recoilDmg = max(1, aiData->simulatedDmg[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex] * max(1, gBattleMoves[move].recoil) / 100);
|
||||
if (!ShouldUseRecoilMove(battlerAtk, battlerDef, recoilDmg, AI_THINKING_STRUCT->movesetIndex))
|
||||
ADJUST_SCORE(-10);
|
||||
}
|
||||
}
|
||||
|
||||
// check move effects
|
||||
switch (moveEffect)
|
||||
{
|
||||
case EFFECT_HIT: // only applies to Vital Throw
|
||||
if (gBattleMoves[move].priority < 0 && AI_STRIKES_FIRST(battlerAtk, battlerDef, move) && aiData->hpPercents[battlerAtk] < 40)
|
||||
ADJUST_SCORE(-1); // don't want to move last
|
||||
ADJUST_SCORE(-2); // don't want to move last
|
||||
|
||||
// TEMPORARY - should applied to all moves regardless of effect
|
||||
score = AI_CheckMoveEffects(battlerAtk, battlerDef, move, score, aiData, predictedMove, isDoubleBattle);
|
||||
break;
|
||||
default:
|
||||
break; // check move damage
|
||||
@ -1420,13 +1641,9 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
}
|
||||
}
|
||||
break;
|
||||
//case EFFECT_BIDE:
|
||||
//case EFFECT_COUNTER:
|
||||
case EFFECT_PRESENT:
|
||||
case EFFECT_ARG_FIXED_DAMAGE:
|
||||
//case EFFECT_MIRROR_COAT:
|
||||
case EFFECT_FOCUS_PUNCH:
|
||||
//case EFFECT_ENDEAVOR:
|
||||
// AI_CBM_HighRiskForDamage
|
||||
if (aiData->abilities[battlerDef] == ABILITY_WONDER_GUARD && effectiveness < AI_EFFECTIVENESS_x2)
|
||||
ADJUST_SCORE(-10);
|
||||
@ -3576,7 +3793,7 @@ static s32 AI_CheckViability(u32 battlerAtk, u32 battlerDef, u32 move, s32 score
|
||||
ADJUST_SCORE(-3);
|
||||
break;
|
||||
}
|
||||
score += AI_TryToClearStats(battlerAtk, battlerDef, FALSE);
|
||||
score += AI_TryToClearStats(battlerAtk, battlerDef, isDoubleBattle);
|
||||
break;
|
||||
case EFFECT_MULTI_HIT:
|
||||
case EFFECT_TRIPLE_KICK:
|
||||
@ -4139,13 +4356,7 @@ static s32 AI_CheckViability(u32 battlerAtk, u32 battlerDef, u32 move, s32 score
|
||||
|
||||
// check defog lowering evasion
|
||||
if (ShouldLowerEvasion(battlerAtk, battlerDef, aiData->abilities[battlerDef]))
|
||||
{
|
||||
if (gBattleMons[battlerDef].statStages[STAT_EVASION] > 7
|
||||
|| HasMoveWithLowAccuracy(battlerAtk, battlerDef, 90, TRUE, aiData->abilities[battlerAtk], aiData->abilities[battlerDef], aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef]))
|
||||
ADJUST_SCORE(2); // encourage lowering evasion if they are evasive or we have a move with low accuracy
|
||||
else
|
||||
ADJUST_SCORE(1);
|
||||
}
|
||||
ADJUST_SCORE(2);
|
||||
}
|
||||
break;
|
||||
case EFFECT_TORMENT:
|
||||
@ -4735,231 +4946,6 @@ static s32 AI_CheckViability(u32 battlerAtk, u32 battlerDef, u32 move, s32 score
|
||||
break;
|
||||
} // move effect checks
|
||||
|
||||
// check move additional effects that are likely to happen
|
||||
for (i = 0; i < gBattleMoves[move].numAdditionalEffects; i++)
|
||||
{
|
||||
// Only consider effects with a guaranteed chance to happen
|
||||
if (!MoveEffectIsGuaranteed(battlerAtk, aiData->abilities[battlerAtk], &gBattleMoves[move].additionalEffects[i]))
|
||||
continue;
|
||||
|
||||
// Consider move effects that target self
|
||||
if (gBattleMoves[move].additionalEffects[i].self)
|
||||
{
|
||||
switch (gBattleMoves[move].additionalEffects[i].moveEffect)
|
||||
{
|
||||
case MOVE_EFFECT_SPD_PLUS_2:
|
||||
case MOVE_EFFECT_SPD_PLUS_1:
|
||||
if (aiData->abilities[battlerAtk] != ABILITY_CONTRARY && !AI_STRIKES_FIRST(battlerAtk, battlerDef, move))
|
||||
ADJUST_SCORE(3);
|
||||
break;
|
||||
case MOVE_EFFECT_ATK_PLUS_1:
|
||||
case MOVE_EFFECT_DEF_PLUS_1:
|
||||
case MOVE_EFFECT_SP_ATK_PLUS_1:
|
||||
case MOVE_EFFECT_SP_DEF_PLUS_1:
|
||||
case MOVE_EFFECT_ACC_PLUS_1:
|
||||
case MOVE_EFFECT_EVS_PLUS_1:
|
||||
IncreaseStatUpScore(
|
||||
battlerAtk,
|
||||
battlerDef,
|
||||
STAT_ATK + gBattleMoves[move].additionalEffects[i].moveEffect - MOVE_EFFECT_ATK_PLUS_1,
|
||||
&score
|
||||
);
|
||||
break;
|
||||
case MOVE_EFFECT_ATK_PLUS_2:
|
||||
case MOVE_EFFECT_DEF_PLUS_2:
|
||||
case MOVE_EFFECT_SP_ATK_PLUS_2:
|
||||
case MOVE_EFFECT_SP_DEF_PLUS_2:
|
||||
case MOVE_EFFECT_ACC_PLUS_2:
|
||||
case MOVE_EFFECT_EVS_PLUS_2:
|
||||
IncreaseStatUpScore(
|
||||
battlerAtk,
|
||||
battlerDef,
|
||||
STAT_ATK + gBattleMoves[move].additionalEffects[i].moveEffect - MOVE_EFFECT_ATK_PLUS_2,
|
||||
&score
|
||||
);
|
||||
break;
|
||||
// Effects that lower stat(s) - only need to consider Contrary
|
||||
case MOVE_EFFECT_ATK_MINUS_1:
|
||||
case MOVE_EFFECT_DEF_MINUS_1:
|
||||
case MOVE_EFFECT_SPD_MINUS_1:
|
||||
case MOVE_EFFECT_SP_ATK_MINUS_1:
|
||||
case MOVE_EFFECT_SP_DEF_MINUS_1:
|
||||
case MOVE_EFFECT_V_CREATE:
|
||||
case MOVE_EFFECT_DEF_SPDEF_DOWN:
|
||||
case MOVE_EFFECT_ATK_DEF_DOWN:
|
||||
case MOVE_EFFECT_SP_ATK_TWO_DOWN:
|
||||
if (aiData->abilities[battlerAtk] == ABILITY_CONTRARY)
|
||||
ADJUST_SCORE(3);
|
||||
break;
|
||||
case MOVE_EFFECT_RAPIDSPIN:
|
||||
if ((gSideStatuses[GetBattlerSide(battlerAtk)] & SIDE_STATUS_HAZARDS_ANY && CountUsablePartyMons(battlerAtk) != 0)
|
||||
|| (gStatuses3[battlerAtk] & STATUS3_LEECHSEED || gBattleMons[battlerAtk].status2 & STATUS2_WRAPPED))
|
||||
{
|
||||
ADJUST_SCORE(3);
|
||||
break;
|
||||
}
|
||||
//Spin checks
|
||||
if (!(gSideStatuses[GetBattlerSide(battlerAtk)] & SIDE_STATUS_HAZARDS_ANY))
|
||||
ADJUST_SCORE(-6);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else // consider move effects that hinder the target
|
||||
{
|
||||
switch (gBattleMoves[move].additionalEffects[i].moveEffect)
|
||||
{
|
||||
case MOVE_EFFECT_FLINCH:
|
||||
score += ShouldTryToFlinch(battlerAtk, battlerDef, aiData->abilities[battlerAtk], aiData->abilities[battlerDef], move);
|
||||
break;
|
||||
case MOVE_EFFECT_SPD_MINUS_1:
|
||||
case MOVE_EFFECT_SPD_MINUS_2:
|
||||
if (!ShouldLowerSpeed(battlerAtk, battlerDef, aiData->abilities[battlerDef]))
|
||||
break;
|
||||
case MOVE_EFFECT_ATK_MINUS_1:
|
||||
case MOVE_EFFECT_DEF_MINUS_1:
|
||||
case MOVE_EFFECT_SP_ATK_MINUS_1:
|
||||
case MOVE_EFFECT_SP_DEF_MINUS_1:
|
||||
case MOVE_EFFECT_ACC_MINUS_1:
|
||||
case MOVE_EFFECT_EVS_MINUS_1:
|
||||
if (aiData->abilities[battlerDef] != ABILITY_CONTRARY)
|
||||
ADJUST_SCORE(2);
|
||||
break;
|
||||
case MOVE_EFFECT_ATK_MINUS_2:
|
||||
case MOVE_EFFECT_DEF_MINUS_2:
|
||||
case MOVE_EFFECT_SP_ATK_MINUS_2:
|
||||
case MOVE_EFFECT_SP_DEF_MINUS_2:
|
||||
case MOVE_EFFECT_ACC_MINUS_2:
|
||||
case MOVE_EFFECT_EVS_MINUS_2:
|
||||
if (aiData->abilities[battlerDef] != ABILITY_CONTRARY)
|
||||
ADJUST_SCORE(3);
|
||||
break;
|
||||
case MOVE_EFFECT_POISON:
|
||||
IncreasePoisonScore(battlerAtk, battlerDef, move, &score);
|
||||
break;
|
||||
case MOVE_EFFECT_CLEAR_SMOG:
|
||||
score += AI_TryToClearStats(battlerAtk, battlerDef, FALSE);
|
||||
break;
|
||||
case MOVE_EFFECT_SPECTRAL_THIEF:
|
||||
score += AI_ShouldCopyStatChanges(battlerAtk, battlerDef);
|
||||
break;
|
||||
case MOVE_EFFECT_BUG_BITE: // And pluck
|
||||
if (gBattleMons[battlerDef].status2 & STATUS2_SUBSTITUTE || aiData->abilities[battlerDef] == ABILITY_STICKY_HOLD)
|
||||
break;
|
||||
else if (ItemId_GetPocket(aiData->items[battlerDef]) == POCKET_BERRIES)
|
||||
ADJUST_SCORE(3);
|
||||
break;
|
||||
case MOVE_EFFECT_INCINERATE:
|
||||
if (gBattleMons[battlerDef].status2 & STATUS2_SUBSTITUTE || aiData->abilities[battlerDef] == ABILITY_STICKY_HOLD)
|
||||
break;
|
||||
else if (ItemId_GetPocket(aiData->items[battlerDef]) == POCKET_BERRIES || aiData->holdEffects[battlerDef] == HOLD_EFFECT_GEMS)
|
||||
ADJUST_SCORE(3);
|
||||
break;
|
||||
case MOVE_EFFECT_SMACK_DOWN:
|
||||
if (!IsBattlerGrounded(battlerDef))
|
||||
ADJUST_SCORE(3);
|
||||
break;
|
||||
case MOVE_EFFECT_KNOCK_OFF:
|
||||
if (CanKnockOffItem(battlerDef, aiData->items[battlerDef]))
|
||||
{
|
||||
switch (aiData->holdEffects[battlerDef])
|
||||
{
|
||||
case HOLD_EFFECT_IRON_BALL:
|
||||
if (HasMoveEffect(battlerDef, EFFECT_FLING))
|
||||
ADJUST_SCORE(4);
|
||||
break;
|
||||
case HOLD_EFFECT_LAGGING_TAIL:
|
||||
case HOLD_EFFECT_STICKY_BARB:
|
||||
break;
|
||||
default:
|
||||
ADJUST_SCORE(3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MOVE_EFFECT_STEAL_ITEM:
|
||||
{
|
||||
bool32 canSteal = FALSE;
|
||||
|
||||
if (B_TRAINERS_KNOCK_OFF_ITEMS == TRUE)
|
||||
canSteal = TRUE;
|
||||
if (gBattleTypeFlags & BATTLE_TYPE_FRONTIER || GetBattlerSide(battlerAtk) == B_SIDE_PLAYER)
|
||||
canSteal = TRUE;
|
||||
|
||||
if (canSteal && aiData->items[battlerAtk] == ITEM_NONE
|
||||
&& aiData->items[battlerDef] != ITEM_NONE
|
||||
&& CanBattlerGetOrLoseItem(battlerDef, aiData->items[battlerDef])
|
||||
&& CanBattlerGetOrLoseItem(battlerAtk, aiData->items[battlerDef])
|
||||
&& !HasMoveEffect(battlerAtk, EFFECT_ACROBATICS)
|
||||
&& aiData->abilities[battlerDef] != ABILITY_STICKY_HOLD)
|
||||
{
|
||||
switch (aiData->holdEffects[battlerDef])
|
||||
{
|
||||
case HOLD_EFFECT_NONE:
|
||||
break;
|
||||
case HOLD_EFFECT_CHOICE_BAND:
|
||||
case HOLD_EFFECT_CHOICE_SCARF:
|
||||
case HOLD_EFFECT_CHOICE_SPECS:
|
||||
ADJUST_SCORE(2);
|
||||
break;
|
||||
case HOLD_EFFECT_TOXIC_ORB:
|
||||
if (ShouldPoisonSelf(battlerAtk, aiData->abilities[battlerAtk]))
|
||||
ADJUST_SCORE(2);
|
||||
break;
|
||||
case HOLD_EFFECT_FLAME_ORB:
|
||||
if (ShouldBurnSelf(battlerAtk, aiData->abilities[battlerAtk]))
|
||||
ADJUST_SCORE(2);
|
||||
break;
|
||||
case HOLD_EFFECT_BLACK_SLUDGE:
|
||||
if (IS_BATTLER_OF_TYPE(battlerAtk, TYPE_POISON))
|
||||
ADJUST_SCORE(2);
|
||||
break;
|
||||
case HOLD_EFFECT_IRON_BALL:
|
||||
if (HasMoveEffect(battlerAtk, EFFECT_FLING))
|
||||
ADJUST_SCORE(2);
|
||||
break;
|
||||
case HOLD_EFFECT_LAGGING_TAIL:
|
||||
case HOLD_EFFECT_STICKY_BARB:
|
||||
break;
|
||||
default:
|
||||
ADJUST_SCORE(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case MOVE_EFFECT_STEALTH_ROCK:
|
||||
case MOVE_EFFECT_SPIKES:
|
||||
score += AI_ShouldSetUpHazards(battlerAtk, battlerDef, aiData);
|
||||
break;
|
||||
case MOVE_EFFECT_FEINT:
|
||||
if (gBattleMoves[predictedMove].effect == EFFECT_PROTECT)
|
||||
ADJUST_SCORE(3);
|
||||
break;
|
||||
case MOVE_EFFECT_THROAT_CHOP:
|
||||
if (predictedMove != MOVE_NONE && gBattleMoves[predictedMove].soundMove && AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER)
|
||||
ADJUST_SCORE(3); // Ai goes first and predicts the target will use a sound move
|
||||
else if (HasSoundMove(battlerDef))
|
||||
ADJUST_SCORE(3);
|
||||
break;
|
||||
case MOVE_EFFECT_FLAME_BURST:
|
||||
if (isDoubleBattle)
|
||||
{
|
||||
if (IsBattlerAlive(BATTLE_PARTNER(battlerDef))
|
||||
&& aiData->hpPercents[BATTLE_PARTNER(battlerDef)] < 12
|
||||
&& aiData->abilities[BATTLE_PARTNER(battlerDef)] != ABILITY_MAGIC_GUARD
|
||||
&& !IS_BATTLER_OF_TYPE(BATTLE_PARTNER(battlerDef), TYPE_FIRE))
|
||||
ADJUST_SCORE(1);
|
||||
}
|
||||
break;
|
||||
case MOVE_EFFECT_WRAP:
|
||||
if (!HasMoveWithMoveEffect(battlerDef, MOVE_EFFECT_RAPIDSPIN) && !IsBattlerTrapped(battlerDef, TRUE) && ShouldTrap(battlerAtk, battlerDef, move))
|
||||
ADJUST_SCORE(5);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
@ -5067,24 +5053,25 @@ static s32 AI_SetupFirstTurn(u32 battlerAtk, u32 battlerDef, u32 move, s32 score
|
||||
case EFFECT_VICTORY_DANCE:
|
||||
ADJUST_SCORE(2);
|
||||
break;
|
||||
case EFFECT_HIT:
|
||||
// TEMPORARY - should applied to all moves regardless of EFFECT
|
||||
// Consider move effects
|
||||
for (i = 0; i < gBattleMoves[move].numAdditionalEffects; i++)
|
||||
{
|
||||
switch (gBattleMoves[move].additionalEffects[i].moveEffect)
|
||||
{
|
||||
case MOVE_EFFECT_STEALTH_ROCK:
|
||||
case MOVE_EFFECT_SPIKES:
|
||||
ADJUST_SCORE(2);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Consider move effects
|
||||
for (i = 0; i < gBattleMoves[move].numAdditionalEffects; i++)
|
||||
{
|
||||
switch (gBattleMoves[move].additionalEffects[i].moveEffect)
|
||||
{
|
||||
case MOVE_EFFECT_STEALTH_ROCK:
|
||||
case MOVE_EFFECT_SPIKES:
|
||||
ADJUST_SCORE(2);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
@ -5121,24 +5108,25 @@ static s32 AI_Risky(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
if (Random() & 1)
|
||||
ADJUST_SCORE(2);
|
||||
break;
|
||||
case EFFECT_HIT:
|
||||
// TEMPORARY - should applied to all moves regardless of EFFECT
|
||||
// Consider move effects
|
||||
for (i = 0; i < gBattleMoves[move].numAdditionalEffects; i++)
|
||||
{
|
||||
switch (gBattleMoves[move].additionalEffects[i].moveEffect)
|
||||
{
|
||||
case MOVE_EFFECT_ALL_STATS_UP:
|
||||
if (Random() & 1)
|
||||
ADJUST_SCORE(2);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Consider move effects
|
||||
for (i = 0; i < gBattleMoves[move].numAdditionalEffects; i++)
|
||||
{
|
||||
switch (gBattleMoves[move].additionalEffects[i].moveEffect)
|
||||
{
|
||||
case MOVE_EFFECT_ALL_STATS_UP:
|
||||
if (Random() & 1)
|
||||
ADJUST_SCORE(2);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
|
||||
@ -3585,4 +3585,4 @@ bool32 ShouldUseZMove(u32 battlerAtk, u32 battlerDef, u32 chosenMove)
|
||||
bool32 AI_IsBattlerAsleepOrComatose(u32 battlerId)
|
||||
{
|
||||
return (gBattleMons[battlerId].status1 & STATUS1_SLEEP) || AI_DATA->abilities[battlerId] == ABILITY_COMATOSE;
|
||||
}
|
||||
}
|
||||
|
||||
@ -3224,7 +3224,7 @@ void SetMoveEffect(bool32 primary, bool32 certain)
|
||||
static const u8 sTriAttackEffects[] =
|
||||
{
|
||||
MOVE_EFFECT_BURN,
|
||||
B_USE_FROSTBITE == TRUE ? MOVE_EFFECT_FROSTBITE : MOVE_EFFECT_FREEZE,
|
||||
MOVE_EFFECT_FREEZE_OR_FROSTBITE,
|
||||
MOVE_EFFECT_PARALYSIS
|
||||
};
|
||||
gBattleScripting.moveEffect = RandomElement(RNG_TRI_ATTACK, sTriAttackEffects);
|
||||
@ -3768,7 +3768,7 @@ void SetMoveEffect(bool32 primary, bool32 certain)
|
||||
break;
|
||||
case BATTLE_TERRAIN_SNOW:
|
||||
case BATTLE_TERRAIN_ICE:
|
||||
gBattleScripting.moveEffect = (B_USE_FROSTBITE == TRUE ? MOVE_EFFECT_FROSTBITE : MOVE_EFFECT_FREEZE);
|
||||
gBattleScripting.moveEffect = MOVE_EFFECT_FREEZE_OR_FROSTBITE;
|
||||
break;
|
||||
case BATTLE_TERRAIN_VOLCANO:
|
||||
gBattleScripting.moveEffect = MOVE_EFFECT_BURN;
|
||||
|
||||
@ -2420,6 +2420,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_DYNAMAX] =
|
||||
.effect = EFFECT_TWO_TURNS_ATTACK,
|
||||
.power = 140,
|
||||
.type = TYPE_FLYING,
|
||||
.criticalHitStage = 1,
|
||||
.accuracy = 90,
|
||||
.pp = 5,
|
||||
.target = MOVE_TARGET_SELECTED,
|
||||
@ -14097,9 +14098,6 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_DYNAMAX] =
|
||||
.makesContact = TRUE,
|
||||
.ignoresProtect = TRUE,
|
||||
.slicingMove = TRUE,
|
||||
.additionalEffects = ADDITIONAL_EFFECTS({
|
||||
.moveEffect = MOVE_EFFECT_FEINT,
|
||||
}),
|
||||
},
|
||||
|
||||
[MOVE_TACHYON_CUTTER] =
|
||||
|
||||
@ -72,23 +72,22 @@ AI_SINGLE_BATTLE_TEST("AI sees increased base power of Wake Up Slap")
|
||||
}
|
||||
}
|
||||
|
||||
// Underlying AI calcs are broken and need fixing
|
||||
// AI_SINGLE_BATTLE_TEST("AI sees increased base power of Grav Apple")
|
||||
// {
|
||||
// u32 movePlayer;
|
||||
// u16 expectedMove;
|
||||
|
||||
// PARAMETRIZE { movePlayer = MOVE_CELEBRATE; expectedMove = MOVE_TROP_KICK; }
|
||||
// PARAMETRIZE { movePlayer = MOVE_CELEBRATE; expectedMove = MOVE_POWER_WHIP; }
|
||||
// PARAMETRIZE { movePlayer = MOVE_GRAVITY; expectedMove = MOVE_GRAV_APPLE; }
|
||||
|
||||
// GIVEN {
|
||||
// ASSUME(gBattleMoves[MOVE_GRAV_APPLE].effect == EFFECT_GRAV_APPLE);
|
||||
// ASSUME(MoveHasMoveEffect(MOVE_TROP_KICK, MOVE_EFFECT_ATK_MINUS_1) == TRUE);
|
||||
// ASSUME(MoveHasMoveEffect(MOVE_GRAV_APPLE, MOVE_EFFECT_DEF_MINUS_1) == TRUE);
|
||||
// AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
|
||||
// PLAYER(SPECIES_WOBBUFFET) { HP(81); Speed(20); }
|
||||
// OPPONENT(SPECIES_WOBBUFFET) { Speed(10); Moves(MOVE_TROP_KICK, MOVE_GRAV_APPLE); }
|
||||
// OPPONENT(SPECIES_WOBBUFFET) { Speed(10); Moves(MOVE_POWER_WHIP, MOVE_GRAV_APPLE); }
|
||||
// } WHEN {
|
||||
// TURN { MOVE(player, movePlayer); EXPECT_MOVE(opponent, MOVE_TROP_KICK); }
|
||||
// TURN { MOVE(player, movePlayer); EXPECT_MOVE(opponent, MOVE_POWER_WHIP); }
|
||||
// TURN { MOVE(player, MOVE_CELEBRATE); EXPECT_MOVE(opponent, expectedMove); }
|
||||
// } SCENE {
|
||||
// if (expectedMove == MOVE_GRAV_APPLE)
|
||||
|
||||
51
test/battle/move_effect/aura_wheel.c
Normal file
51
test/battle/move_effect/aura_wheel.c
Normal file
@ -0,0 +1,51 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(MoveHasMoveEffectSelf(MOVE_AURA_WHEEL, MOVE_EFFECT_SPD_PLUS_1) == TRUE);
|
||||
ASSUME(gBattleMoves[MOVE_AURA_WHEEL].effect == EFFECT_AURA_WHEEL);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Aura Wheel raises Speed; fails if the user is not Morpeko")
|
||||
{
|
||||
u16 species;
|
||||
PARAMETRIZE{ species = SPECIES_WOBBUFFET; }
|
||||
PARAMETRIZE{ species = SPECIES_MORPEKO; }
|
||||
GIVEN {
|
||||
PLAYER(species);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_AURA_WHEEL); }
|
||||
} SCENE {
|
||||
if (species != SPECIES_MORPEKO)
|
||||
{
|
||||
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_AURA_WHEEL, player);
|
||||
MESSAGE("But Wobbuffet can't use the move!");
|
||||
}
|
||||
else {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_AURA_WHEEL, player);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
|
||||
MESSAGE("Morpeko's Speed rose!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Aura Wheel changes type depending on Morpeko's form")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_MORPEKO) { Ability(ABILITY_HUNGER_SWITCH); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_AURA_WHEEL); }
|
||||
TURN { MOVE(player, MOVE_AURA_WHEEL); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_AURA_WHEEL, player);
|
||||
HP_BAR(opponent);
|
||||
NOT MESSAGE("It's super effective!");
|
||||
// Turn 2 (Hangry)
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_AURA_WHEEL, player);
|
||||
HP_BAR(opponent);
|
||||
MESSAGE("It's super effective!");
|
||||
}
|
||||
}
|
||||
@ -1,27 +0,0 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(MoveHasMoveEffectSelf(MOVE_MORTAL_SPIN, MOVE_EFFECT_RAPIDSPIN) == TRUE);
|
||||
ASSUME(MoveHasMoveEffect(MOVE_MORTAL_SPIN, MOVE_EFFECT_POISON) == TRUE);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Mortal Spin blows away Wrap, hazards and poisons foe")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_WRAP); }
|
||||
TURN { MOVE(opponent, MOVE_STEALTH_ROCK); MOVE(player, MOVE_MORTAL_SPIN); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_STEALTH_ROCK, opponent);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_MORTAL_SPIN, player);
|
||||
MESSAGE("Wobbuffet got free of Foe Wobbuffet's Wrap!");
|
||||
MESSAGE("Wobbuffet blew away Stealth Rock!");
|
||||
MESSAGE("Foe Wobbuffet was poisoned!");
|
||||
STATUS_ICON(opponent, poison: TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,6 +7,8 @@ ASSUMPTIONS
|
||||
#if B_SPEED_BUFFING_RAPID_SPIN >= GEN_8
|
||||
ASSUME(MoveHasMoveEffectSelf(MOVE_RAPID_SPIN, MOVE_EFFECT_SPD_PLUS_1) == TRUE);
|
||||
#endif
|
||||
ASSUME(MoveHasMoveEffectSelf(MOVE_MORTAL_SPIN, MOVE_EFFECT_RAPIDSPIN) == TRUE);
|
||||
ASSUME(MoveHasMoveEffect(MOVE_MORTAL_SPIN, MOVE_EFFECT_POISON) == TRUE);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Rapin Spin blows away Wrap, hazards and raises Speed (Gen 8+)")
|
||||
@ -29,3 +31,21 @@ SINGLE_BATTLE_TEST("Rapin Spin blows away Wrap, hazards and raises Speed (Gen 8+
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Mortal Spin blows away Wrap, hazards and poisons foe")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_WRAP); }
|
||||
TURN { MOVE(opponent, MOVE_STEALTH_ROCK); MOVE(player, MOVE_MORTAL_SPIN); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_STEALTH_ROCK, opponent);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_MORTAL_SPIN, player);
|
||||
MESSAGE("Wobbuffet got free of Foe Wobbuffet's Wrap!");
|
||||
MESSAGE("Wobbuffet blew away Stealth Rock!");
|
||||
MESSAGE("Foe Wobbuffet was poisoned!");
|
||||
STATUS_ICON(opponent, poison: TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -5,7 +5,6 @@ ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gBattleMoves[MOVE_RELIC_SONG].effect == EFFECT_RELIC_SONG);
|
||||
ASSUME(MoveHasMoveEffect(MOVE_RELIC_SONG, MOVE_EFFECT_SLEEP) == TRUE);
|
||||
ASSUME(P_GEN_5_POKEMON == TRUE);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Relic Song has a 10% chance to put the target to sleep")
|
||||
|
||||
@ -42,4 +42,4 @@ SINGLE_BATTLE_TEST("Throat Chop won't work through a substitute")
|
||||
}
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_HYPER_VOICE, opponent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user