diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 6b7412f1ea..aab46e67be 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -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; } diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 06f24b0e6e..da625034f1 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -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; -} \ No newline at end of file +} diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 51989fee54..54a98d61f2 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -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; diff --git a/src/data/battle_moves.h b/src/data/battle_moves.h index 7395aa23f5..9abe6b164e 100644 --- a/src/data/battle_moves.h +++ b/src/data/battle_moves.h @@ -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] = diff --git a/test/battle/ai_check_viability.c b/test/battle/ai_check_viability.c index b154bbbf29..10adca93f9 100644 --- a/test/battle/ai_check_viability.c +++ b/test/battle/ai_check_viability.c @@ -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) diff --git a/test/battle/move_effect/aura_wheel.c b/test/battle/move_effect/aura_wheel.c new file mode 100644 index 0000000000..35a36699f5 --- /dev/null +++ b/test/battle/move_effect/aura_wheel.c @@ -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!"); + } +} \ No newline at end of file diff --git a/test/battle/move_effect/mortal_spin.c b/test/battle/move_effect/mortal_spin.c deleted file mode 100644 index 5952c58983..0000000000 --- a/test/battle/move_effect/mortal_spin.c +++ /dev/null @@ -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); - } -} - diff --git a/test/battle/move_effect/rapid_spin.c b/test/battle/move_effect/rapid_spin.c index af1c96d7f4..6a088e242a 100644 --- a/test/battle/move_effect/rapid_spin.c +++ b/test/battle/move_effect/rapid_spin.c @@ -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); + } +} + diff --git a/test/battle/move_effect/relic_song.c b/test/battle/move_effect/relic_song.c index 513771d565..4e3a381ea2 100644 --- a/test/battle/move_effect/relic_song.c +++ b/test/battle/move_effect/relic_song.c @@ -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") diff --git a/test/battle/move_effect/throat_chop.c b/test/battle/move_effect/throat_chop.c index 20b701915b..197e18bdc9 100644 --- a/test/battle/move_effect/throat_chop.c +++ b/test/battle/move_effect/throat_chop.c @@ -42,4 +42,4 @@ SINGLE_BATTLE_TEST("Throat Chop won't work through a substitute") } ANIMATION(ANIM_TYPE_MOVE, MOVE_HYPER_VOICE, opponent); } -} \ No newline at end of file +}