diff --git a/include/constants/battle_move_effects.h b/include/constants/battle_move_effects.h index d2ad1afdbc..b19bc0f7b1 100644 --- a/include/constants/battle_move_effects.h +++ b/include/constants/battle_move_effects.h @@ -191,7 +191,6 @@ enum { EFFECT_ROUND, EFFECT_BRINE, EFFECT_RETALIATE, - EFFECT_BULLDOZE, EFFECT_FOUL_PLAY, EFFECT_PSYSHOCK, EFFECT_ROOST, diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 6f79042a0b..6d9a7cacde 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -423,7 +423,7 @@ static void SetBattlerAiMovesData(struct AiLogicData *aiData, u32 battlerAtk, u3 // Simulate dmg for both ai controlled mons and for player controlled mons. for (battlerDef = 0; battlerDef < battlersCount; battlerDef++) { - if (battlerAtk == battlerDef) + if (battlerAtk == battlerDef || !IsBattlerAlive(battlerDef)) continue; SaveBattlerData(battlerDef); @@ -3028,21 +3028,6 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) else if (IsAbilityOfRating(aiData->abilities[battlerAtk], 0) || IsAbilityOfRating(aiData->abilities[battlerDef], 10)) ADJUST_SCORE(DECENT_EFFECT); // we want to transfer our bad ability or take their awesome ability break; - case EFFECT_EARTHQUAKE: - case EFFECT_MAGNITUDE: - if (!IsBattlerGrounded(battlerAtkPartner) - || (IsBattlerGrounded(battlerAtkPartner) - && AI_WhoStrikesFirst(battlerAtk, battlerAtkPartner, move) == AI_IS_SLOWER - && IsUngroundingEffect(gMovesInfo[aiData->partnerMove].effect))) - ADJUST_SCORE(DECENT_EFFECT); - else if (IS_BATTLER_OF_TYPE(battlerAtkPartner, TYPE_FIRE) - || IS_BATTLER_OF_TYPE(battlerAtkPartner, TYPE_ELECTRIC) - || IS_BATTLER_OF_TYPE(battlerAtkPartner, TYPE_POISON) - || IS_BATTLER_OF_TYPE(battlerAtkPartner, TYPE_ROCK)) - ADJUST_SCORE(-10); // partner will be hit by earthquake and is weak to it - else if (IsBattlerAlive(battlerAtkPartner)) - ADJUST_SCORE(-3); - break; } // lightning rod, flash fire against enemy handled in AI_CheckBadMove @@ -3082,6 +3067,18 @@ static s32 CompareMoveAccuracies(u32 battlerAtk, u32 battlerDef, u32 moveSlot1, return 0; } +static inline bool32 ShouldUseSpreadDamageMove(u32 battlerAtk, u32 move, u32 moveIndex, u32 hitsToFaintOpposingBattler) +{ + u32 partnerBattler = BATTLE_PARTNER(battlerAtk); + u32 noOfHitsToFaintPartner = GetNoOfHitsToKOBattler(battlerAtk, partnerBattler, moveIndex); + return (IsDoubleBattle() + && noOfHitsToFaintPartner != 0 // Immunity check + && IsBattlerAlive(partnerBattler) + && gMovesInfo[move].target == MOVE_TARGET_FOES_AND_ALLY + && !(noOfHitsToFaintPartner < 4 && hitsToFaintOpposingBattler == 1) + && noOfHitsToFaintPartner < 7); +} + static s32 AI_CompareDamagingMoves(u32 battlerAtk, u32 battlerDef, u32 currId) { u32 i; @@ -3099,7 +3096,13 @@ 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 && noOfHits[i] != 0) + if (ShouldUseSpreadDamageMove(battlerAtk,moves[i], i, noOfHits[i])) + { + noOfHits[i] = -1; + viableMoveScores[i] = 0; + isTwoTurnNotSemiInvulnerableMove[i] = FALSE; + } + else if (noOfHits[i] < leastHits && noOfHits[i] != 0) { leastHits = noOfHits[i]; } diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 02e6c6151b..b99339d16e 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -371,14 +371,17 @@ bool32 IsDamageMoveUnusable(u32 move, u32 battlerAtk, u32 battlerDef) s32 moveType; struct AiLogicData *aiData = AI_DATA; u32 battlerDefAbility; + GET_MOVE_TYPE(move, moveType); if (DoesBattlerIgnoreAbilityChecks(aiData->abilities[battlerAtk], move)) battlerDefAbility = ABILITY_NONE; else battlerDefAbility = aiData->abilities[battlerDef]; - SetTypeBeforeUsingMove(move, battlerAtk); - GET_MOVE_TYPE(move, moveType); + // Battler doesn't see partners Ability for some reason. + // This is a small hack to avoid the issue but should be investigated + if (battlerDef == BATTLE_PARTNER(battlerAtk)) + battlerDefAbility = GetBattlerAbility(battlerDef); switch (battlerDefAbility) { @@ -472,14 +475,13 @@ s32 AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u8 *typeEffectivenes gBattleStruct->dynamicMoveType = 0; - SetTypeBeforeUsingMove(move, battlerAtk); GET_MOVE_TYPE(move, moveType); + effectivenessMultiplier = CalcTypeEffectivenessMultiplier(move, moveType, battlerAtk, battlerDef, aiData->abilities[battlerDef], FALSE); if (gMovesInfo[move].power) isDamageMoveUnusable = IsDamageMoveUnusable(move, battlerAtk, battlerDef); - effectivenessMultiplier = CalcTypeEffectivenessMultiplier(move, moveType, battlerAtk, battlerDef, aiData->abilities[battlerDef], FALSE); if (gMovesInfo[move].power && !isDamageMoveUnusable) { s32 critChanceIndex, normalDmg, fixedBasePower, n; diff --git a/src/battle_util.c b/src/battle_util.c index 64cba43782..8cda4c4ecc 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -8846,7 +8846,6 @@ static inline u32 CalcMoveBasePowerAfterModifiers(u32 move, u32 battlerAtk, u32 if (gBattleStruct->lastMoveFailed & gBitTable[battlerAtk]) modifier = uq4_12_multiply(modifier, UQ_4_12(2.0)); break; - case EFFECT_BULLDOZE: case EFFECT_MAGNITUDE: case EFFECT_EARTHQUAKE: if (gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN && !(gStatuses3[battlerDef] & STATUS3_SEMI_INVULNERABLE)) diff --git a/src/data/battle_move_effects.h b/src/data/battle_move_effects.h index d0dc94afff..9c53f50396 100644 --- a/src/data/battle_move_effects.h +++ b/src/data/battle_move_effects.h @@ -1228,12 +1228,6 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = .battleTvScore = 0, // TODO: Assign points }, - [EFFECT_BULLDOZE] = - { - .battleScript = BattleScript_EffectHit, - .battleTvScore = 0, // TODO: Assign points - }, - [EFFECT_FOUL_PLAY] = { .battleScript = BattleScript_EffectHit, diff --git a/src/data/moves_info.h b/src/data/moves_info.h index 87edd20d9e..fa11b510e1 100644 --- a/src/data/moves_info.h +++ b/src/data/moves_info.h @@ -12504,7 +12504,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .description = COMPOUND_STRING( "Stomps down on the ground.\n" "Lowers Speed."), - .effect = EFFECT_BULLDOZE, + .effect = EFFECT_EARTHQUAKE, .power = 60, .type = TYPE_GROUND, .accuracy = 100, diff --git a/test/battle/ai.c b/test/battle/ai.c index 11702a0c5a..28e541befd 100644 --- a/test/battle/ai.c +++ b/test/battle/ai.c @@ -882,3 +882,76 @@ AI_SINGLE_BATTLE_TEST("AI will choose Superpower over Outrage with Contrary") } } } + +AI_DOUBLE_BATTLE_TEST("AI will not choose Earthquake if it damages the partner") +{ + u32 species; + + PARAMETRIZE { species = SPECIES_CHARIZARD; } + PARAMETRIZE { species = SPECIES_CHARMANDER; } + PARAMETRIZE { species = SPECIES_CHIKORITA; } + + GIVEN { + ASSUME(gMovesInfo[MOVE_EARTHQUAKE].target == MOVE_TARGET_FOES_AND_ALLY); + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_PHANPY) { Moves(MOVE_EARTHQUAKE, MOVE_TACKLE); } + OPPONENT(species) { Moves(MOVE_CELEBRATE); } + } WHEN { + if (species == SPECIES_CHARIZARD) + TURN { EXPECT_MOVE(opponentLeft, MOVE_EARTHQUAKE); } + else + TURN { EXPECT_MOVE(opponentLeft, MOVE_TACKLE, target: playerLeft); } + } +} + +AI_DOUBLE_BATTLE_TEST("AI will choose Earthquake if partner is not alive") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_EARTHQUAKE].target == MOVE_TARGET_FOES_AND_ALLY); + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_EARTHQUAKE, MOVE_TACKLE); } + OPPONENT(SPECIES_PIKACHU) { HP(1); Moves(MOVE_CELEBRATE); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_TACKLE, target: opponentRight); } + TURN { EXPECT_MOVE(opponentLeft, MOVE_EARTHQUAKE); } + } +} + +AI_DOUBLE_BATTLE_TEST("AI will choose Earthquake if it kill an opposing mon and does 1/3 of damage to AI") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_EARTHQUAKE].target == MOVE_TARGET_FOES_AND_ALLY); + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET) { HP(1); } + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_EARTHQUAKE, MOVE_TACKLE); } + OPPONENT(SPECIES_PARAS) { Moves(MOVE_CELEBRATE); } + } WHEN { + TURN { EXPECT_MOVE(opponentLeft, MOVE_EARTHQUAKE); } + } +} + +AI_DOUBLE_BATTLE_TEST("AI will the see a corresponding absorbing ability on partner to one of its moves") +{ + u32 ability; + PARAMETRIZE { ability = ABILITY_LIGHTNING_ROD; } + PARAMETRIZE { ability = ABILITY_STATIC; } + + GIVEN { + ASSUME(gMovesInfo[MOVE_DISCHARGE].target == MOVE_TARGET_FOES_AND_ALLY); + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_DISCHARGE, MOVE_TACKLE); } + OPPONENT(SPECIES_PIKACHU) { HP(1); Ability(ability); Moves(MOVE_CELEBRATE); } + } WHEN { + if (ability == ABILITY_LIGHTNING_ROD) + TURN { EXPECT_MOVE(opponentLeft, MOVE_DISCHARGE); } + else + TURN { EXPECT_MOVE(opponentLeft, MOVE_TACKLE); } + } +} diff --git a/test/battle/terrain/grassy.c b/test/battle/terrain/grassy.c index 1cb846dd41..af0776d3df 100644 --- a/test/battle/terrain/grassy.c +++ b/test/battle/terrain/grassy.c @@ -68,7 +68,7 @@ SINGLE_BATTLE_TEST("Grassy Terrain decreases power of Earthquake and Bulldoze by PARAMETRIZE { terrain = TRUE; move = MOVE_BULLDOZE; } // 3 GIVEN { ASSUME(gMovesInfo[MOVE_EARTHQUAKE].effect == EFFECT_EARTHQUAKE); - ASSUME(gMovesInfo[MOVE_BULLDOZE].effect == EFFECT_BULLDOZE); + ASSUME(gMovesInfo[MOVE_BULLDOZE].effect == EFFECT_EARTHQUAKE); PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); } WHEN {