diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 8160357d7647..647af99e329c 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -4834,7 +4834,7 @@ BattleScript_AlreadyAtFullHp:: waitmessage B_WAIT_TIME_LONG goto BattleScript_MoveEnd -BattleScript_EffectFakeOut:: +BattleScript_EffectFirstTurnOnly:: attackcanceler jumpifnotfirstturn BattleScript_FailedFromAtkString goto BattleScript_EffectHit diff --git a/include/battle_ai_util.h b/include/battle_ai_util.h index d3a99e962531..8cfa5b2cb643 100644 --- a/include/battle_ai_util.h +++ b/include/battle_ai_util.h @@ -29,7 +29,7 @@ bool32 IsBattlerTrapped(u32 battler, bool32 switching); s32 AI_WhoStrikesFirst(u32 battlerAI, u32 battler2, u32 moveConsidered); bool32 CanTargetFaintAi(u32 battlerDef, u32 battlerAtk); u32 NoOfHitsForTargetToFaintAI(u32 battlerDef, u32 battlerAtk); -u32 GetBestDmgMoveFromTarget(u32 battlerAtk, u32 battlerDef); +u32 GetBestDmgMoveFromBattler(u32 battlerAtk, u32 battlerDef); bool32 CanTargetMoveFaintAi(u32 move, u32 battlerDef, u32 battlerAtk, u32 nHits); bool32 CanTargetFaintAiWithMod(u32 battlerDef, u32 battlerAtk, s32 hpMod, s32 dmgMod); s32 AI_DecideKnownAbilityForTurn(u32 battlerId); diff --git a/include/battle_scripts.h b/include/battle_scripts.h index 5e373aa7841a..667dec5166c0 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -648,7 +648,7 @@ extern const u8 BattleScript_EffectBeatUp[]; extern const u8 BattleScript_EffectSemiInvulnerable[]; extern const u8 BattleScript_EffectDefenseCurl[]; extern const u8 BattleScript_EffectSoftboiled[]; -extern const u8 BattleScript_EffectFakeOut[]; +extern const u8 BattleScript_EffectFirstTurnOnly[]; extern const u8 BattleScript_EffectUproar[]; extern const u8 BattleScript_EffectStockpile[]; extern const u8 BattleScript_EffectSpitUp[]; diff --git a/include/constants/battle_move_effects.h b/include/constants/battle_move_effects.h index 7f6b607c639c..3eac95a86012 100644 --- a/include/constants/battle_move_effects.h +++ b/include/constants/battle_move_effects.h @@ -128,7 +128,7 @@ enum { EFFECT_SEMI_INVULNERABLE, EFFECT_DEFENSE_CURL, EFFECT_SOFTBOILED, // differences vs Recover - can be used outside of battle to restore HP - EFFECT_FAKE_OUT, + EFFECT_FIRST_TURN_ONLY, EFFECT_UPROAR, EFFECT_STOCKPILE, EFFECT_SPIT_UP, diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 320aaa628a11..3f0918c8b44a 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -880,6 +880,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) break; case ABILITY_DAZZLING: case ABILITY_QUEENLY_MAJESTY: + case ABILITY_ARMOR_TAIL: if (atkPriority > 0) RETURN_SCORE_MINUS(10); break; @@ -979,6 +980,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) break; case ABILITY_DAZZLING: case ABILITY_QUEENLY_MAJESTY: + case ABILITY_ARMOR_TAIL: if (atkPriority > 0) RETURN_SCORE_MINUS(10); break; @@ -1706,7 +1708,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) case EFFECT_TELEPORT: ADJUST_SCORE(-10); break; - case EFFECT_FAKE_OUT: + case EFFECT_FIRST_TURN_ONLY: if (!gDisableStructs[battlerAtk].isFirstTurn) ADJUST_SCORE(-10); break; @@ -3818,14 +3820,11 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move) ADJUST_SCORE(DECENT_EFFECT); IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_DEF, &score); break; - case EFFECT_FAKE_OUT: - if (move == MOVE_FAKE_OUT) // filter out first impression - { - if (ShouldFakeOut(battlerAtk, battlerDef, move)) - ADJUST_SCORE(BEST_EFFECT); - else - ADJUST_SCORE(-10); - } + case EFFECT_FIRST_TURN_ONLY: + if (ShouldFakeOut(battlerAtk, battlerDef, move)) + ADJUST_SCORE(GOOD_EFFECT); + else if (gMovesInfo[move].priority >= 1 && gDisableStructs[battlerAtk].isFirstTurn && GetBestDmgMoveFromBattler(battlerAtk, battlerDef) == move) + ADJUST_SCORE(BEST_EFFECT); break; case EFFECT_STOCKPILE: if (aiData->abilities[battlerAtk] == ABILITY_CONTRARY) @@ -4623,7 +4622,7 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move) ADJUST_SCORE(GOOD_EFFECT); break; case MOVE_EFFECT_THROAT_CHOP: - if (gMovesInfo[GetBestDmgMoveFromTarget(battlerDef, battlerAtk)].soundMove) + if (gMovesInfo[GetBestDmgMoveFromBattler(battlerDef, battlerAtk)].soundMove) { if (AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER) ADJUST_SCORE(GOOD_EFFECT); diff --git a/src/battle_ai_switch_items.c b/src/battle_ai_switch_items.c index d759f237ae95..1704edc89f9c 100644 --- a/src/battle_ai_switch_items.c +++ b/src/battle_ai_switch_items.c @@ -105,7 +105,7 @@ static bool32 HasBadOdds(u32 battler, bool32 emitResult) || aiMoveEffect == EFFECT_SPIKES || aiMoveEffect == EFFECT_TOXIC_SPIKES || aiMoveEffect == EFFECT_STEALTH_ROCK || aiMoveEffect == EFFECT_STICKY_WEB || aiMoveEffect == EFFECT_LEECH_SEED || aiMoveEffect == EFFECT_EXPLOSION || aiMoveEffect == EFFECT_SLEEP || aiMoveEffect == EFFECT_YAWN || aiMoveEffect == EFFECT_TOXIC || aiMoveEffect == EFFECT_WILL_O_WISP || aiMoveEffect == EFFECT_PARALYZE - || aiMoveEffect == EFFECT_TRICK || aiMoveEffect == EFFECT_TRICK_ROOM || aiMoveEffect== EFFECT_WONDER_ROOM || aiMoveEffect == EFFECT_PSYCHO_SHIFT || aiMoveEffect == EFFECT_FAKE_OUT + || aiMoveEffect == EFFECT_TRICK || aiMoveEffect == EFFECT_TRICK_ROOM || aiMoveEffect== EFFECT_WONDER_ROOM || aiMoveEffect == EFFECT_PSYCHO_SHIFT || aiMoveEffect == EFFECT_FIRST_TURN_ONLY ) { hasStatusMove = TRUE; diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 80ae833017ef..0258fa536048 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -867,20 +867,20 @@ u32 NoOfHitsForTargetToFaintAI(u32 battlerDef, u32 battlerAtk) return leastNumberOfHits; } -u32 GetBestDmgMoveFromTarget(u32 battlerDef, u32 battlerAtk) +u32 GetBestDmgMoveFromBattler(u32 battlerAtk, u32 battlerDef) { u32 i; u32 move = 0; u32 bestDmg = 0; - u32 unusable = AI_DATA->moveLimitations[battlerDef]; - u16 *moves = GetMovesArray(battlerDef); + u32 unusable = AI_DATA->moveLimitations[battlerAtk]; + u16 *moves = GetMovesArray(battlerAtk); for (i = 0; i < MAX_MON_MOVES; i++) { if (moves[i] != MOVE_NONE && moves[i] != MOVE_UNAVAILABLE && !(unusable & gBitTable[i]) - && bestDmg < AI_DATA->simulatedDmg[battlerDef][battlerAtk][i]) + && bestDmg < AI_DATA->simulatedDmg[battlerAtk][battlerDef][i]) { - bestDmg = AI_DATA->simulatedDmg[battlerDef][battlerAtk][i]; + bestDmg = AI_DATA->simulatedDmg[battlerAtk][battlerDef][i]; move = moves[i]; } } @@ -2724,7 +2724,7 @@ bool32 ShouldTrap(u32 battlerAtk, u32 battlerDef, u32 move) bool32 ShouldFakeOut(u32 battlerAtk, u32 battlerDef, u32 move) { - if (!gDisableStructs[battlerAtk].isFirstTurn + if ((!gDisableStructs[battlerAtk].isFirstTurn && MoveHasMoveEffectWithChance(move, MOVE_EFFECT_FLINCH, 100)) || AI_DATA->abilities[battlerAtk] == ABILITY_GORILLA_TACTICS || AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_CHOICE_BAND || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_COVERT_CLOAK @@ -3418,7 +3418,7 @@ void IncreaseParalyzeScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score) if ((defSpeed >= atkSpeed && defSpeed / 2 < atkSpeed) // You'll go first after paralyzing foe || HasMoveEffectANDArg(battlerAtk, EFFECT_DOUBLE_POWER_ON_ARG_STATUS, STATUS1_PARALYSIS) - || (HasMoveWithMoveEffectExcept(battlerAtk, MOVE_EFFECT_FLINCH, EFFECT_FAKE_OUT)) // filter out Fake Out + || (HasMoveWithMoveEffectExcept(battlerAtk, MOVE_EFFECT_FLINCH, EFFECT_FIRST_TURN_ONLY)) // filter out Fake Out || gBattleMons[battlerDef].status2 & STATUS2_INFATUATION || gBattleMons[battlerDef].status2 & STATUS2_CONFUSION) ADJUST_SCORE_PTR(GOOD_EFFECT); @@ -3459,7 +3459,7 @@ void IncreaseConfusionScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score { if (gBattleMons[battlerDef].status1 & STATUS1_PARALYSIS || gBattleMons[battlerDef].status2 & STATUS2_INFATUATION - || (AI_DATA->abilities[battlerAtk] == ABILITY_SERENE_GRACE && HasMoveWithMoveEffectExcept(battlerAtk, MOVE_EFFECT_FLINCH, EFFECT_FAKE_OUT))) + || (AI_DATA->abilities[battlerAtk] == ABILITY_SERENE_GRACE && HasMoveWithMoveEffectExcept(battlerAtk, MOVE_EFFECT_FLINCH, EFFECT_FIRST_TURN_ONLY))) ADJUST_SCORE_PTR(GOOD_EFFECT); else ADJUST_SCORE_PTR(DECENT_EFFECT); diff --git a/src/battle_arena.c b/src/battle_arena.c index 591893bb75bf..26df519be0c4 100644 --- a/src/battle_arena.c +++ b/src/battle_arena.c @@ -363,7 +363,7 @@ void BattleArena_AddMindPoints(u8 battler) // All moves with power == 0 give 0 points, with the following exceptions: // - Protect, Detect, and Endure subtract 1 point - if (gMovesInfo[gCurrentMove].effect == EFFECT_FAKE_OUT + if (gMovesInfo[gCurrentMove].effect == EFFECT_FIRST_TURN_ONLY || gMovesInfo[gCurrentMove].effect == EFFECT_PROTECT || gMovesInfo[gCurrentMove].effect == EFFECT_ENDURE) { diff --git a/src/data/battle_move_effects.h b/src/data/battle_move_effects.h index b739c007a722..2fcb3d64b67d 100644 --- a/src/data/battle_move_effects.h +++ b/src/data/battle_move_effects.h @@ -817,9 +817,9 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = .encourageEncore = TRUE, }, - [EFFECT_FAKE_OUT] = + [EFFECT_FIRST_TURN_ONLY] = { - .battleScript = BattleScript_EffectFakeOut, + .battleScript = BattleScript_EffectFirstTurnOnly, .battleTvScore = 4, .encourageEncore = TRUE, }, diff --git a/src/data/moves_info.h b/src/data/moves_info.h index 3aec5e12e75d..4f9f671a2ec9 100644 --- a/src/data/moves_info.h +++ b/src/data/moves_info.h @@ -6188,7 +6188,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = "that causes flinching."), .priority = B_UPDATED_MOVE_DATA >= GEN_5 ? 3 : 1, .makesContact = B_UPDATED_MOVE_DATA >= GEN_4, - .effect = EFFECT_FAKE_OUT, + .effect = EFFECT_FIRST_TURN_ONLY, .power = 40, .type = TYPE_NORMAL, .accuracy = 100, @@ -14987,7 +14987,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .description = COMPOUND_STRING( "Hits hard and first.\n" "Only works first turn."), - .effect = EFFECT_FAKE_OUT, + .effect = EFFECT_FIRST_TURN_ONLY, .power = 90, .type = TYPE_BUG, .accuracy = 100, diff --git a/test/battle/ai.c b/test/battle/ai.c index 81d5e7959a3f..a12bd90649fc 100644 --- a/test/battle/ai.c +++ b/test/battle/ai.c @@ -569,7 +569,7 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: AI considers hazard damage whe OPPONENT(SPECIES_TYPHLOSION) { Speed(200); Moves(MOVE_FLAMETHROWER); SpAttack(317); SpDefense(207); MaxHP(297); } // Outspeends and 2HKOs Meganium } WHEN { TURN { MOVE(player, MOVE_STEALTH_ROCK) ;} - TURN { MOVE(player, MOVE_SURF) ; EXPECT_SEND_OUT(opponent, aiIsSmart ? 2 : 1); } // AI sends out Typhlosion to get the KO with the flag rather than Charizard + TURN { MOVE(player, MOVE_SURF); EXPECT_SEND_OUT(opponent, aiIsSmart ? 2 : 1); } // AI sends out Typhlosion to get the KO with the flag rather than Charizard } } @@ -593,7 +593,7 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: Mid-battle switches prioritize OPPONENT(SPECIES_LOMBRE) { Level(30); Moves(move2); Speed(4); } OPPONENT(SPECIES_HARIYAMA) { Level(30); Moves(MOVE_VITAL_THROW); Speed(4); } } WHEN { - TURN { MOVE(player, MOVE_GROWL) ; EXPECT_SWITCH(opponent, expectedIndex); } + TURN { MOVE(player, MOVE_GROWL); EXPECT_SWITCH(opponent, expectedIndex); } } } @@ -606,7 +606,7 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: Mid-battle switches prioritize OPPONENT(SPECIES_ARON) { Level(30); Moves(MOVE_HEADBUTT); Speed(4); } // Mid battle, AI sends out Aron OPPONENT(SPECIES_ELECTRODE) { Level(30); Moves(MOVE_CHARGE_BEAM); Speed(6); } } WHEN { - TURN { MOVE(player, MOVE_WING_ATTACK) ; EXPECT_SWITCH(opponent, 1); } + TURN { MOVE(player, MOVE_WING_ATTACK); EXPECT_SWITCH(opponent, 1); } } } @@ -619,7 +619,7 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: Post-KO switches prioritize of OPPONENT(SPECIES_ARON) { Level(30); Moves(MOVE_HEADBUTT); Speed(4); } // Mid battle, AI sends out Aron OPPONENT(SPECIES_ELECTRODE) { Level(30); Moves(MOVE_CHARGE_BEAM); Speed(6); } } WHEN { - TURN { MOVE(player, MOVE_WING_ATTACK) ; EXPECT_SEND_OUT(opponent, 2); } + TURN { MOVE(player, MOVE_WING_ATTACK); EXPECT_SEND_OUT(opponent, 2); } } } @@ -631,8 +631,8 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI switches out after sufficient OPPONENT(SPECIES_GRIMER) { Level(30); Moves(MOVE_TACKLE); Speed(4); } OPPONENT(SPECIES_PONYTA) { Level(30); Moves(MOVE_HEADBUTT); Speed(4); } } WHEN { - TURN { MOVE(player, MOVE_CHARM) ;} - TURN { MOVE(player, MOVE_TACKLE) ; EXPECT_SWITCH(opponent, 1); } + TURN { MOVE(player, MOVE_CHARM); } + TURN { MOVE(player, MOVE_TACKLE); EXPECT_SWITCH(opponent, 1); } } } @@ -640,8 +640,8 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will not switch out if Pokemo { u32 move1; - PARAMETRIZE{move1 = MOVE_TACKLE; } - PARAMETRIZE{move1 = MOVE_RAPID_SPIN; } + PARAMETRIZE{ move1 = MOVE_TACKLE; } + PARAMETRIZE{ move1 = MOVE_RAPID_SPIN; } GIVEN { ASSUME(gMovesInfo[MOVE_TACKLE].category == DAMAGE_CATEGORY_PHYSICAL); @@ -653,9 +653,9 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will not switch out if Pokemo OPPONENT(SPECIES_GRIMER) { Level(30); Moves(MOVE_TACKLE); Item(ITEM_FOCUS_SASH); Speed(4); } OPPONENT(SPECIES_PONYTA) { Level(30); Moves(MOVE_HEADBUTT, move1); Speed(4); } } WHEN { - TURN { MOVE(player, MOVE_STEALTH_ROCK) ;} - TURN { MOVE(player, MOVE_EARTHQUAKE) ;} - TURN { MOVE(player, MOVE_CHARM) ;} + TURN { MOVE(player, MOVE_STEALTH_ROCK); } + TURN { MOVE(player, MOVE_EARTHQUAKE); } + TURN { MOVE(player, MOVE_CHARM); } TURN { // If the AI has a mon that can remove hazards, don't prevent them switching out MOVE(player, MOVE_CHARM); if (move1 == MOVE_RAPID_SPIN) @@ -665,3 +665,40 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will not switch out if Pokemo } } } + +AI_SINGLE_BATTLE_TEST("First Impression is preferred on the first turn of the species if it's the best dmg move") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_FIRST_IMPRESSION].effect == EFFECT_FIRST_TURN_ONLY); + ASSUME(gMovesInfo[MOVE_FIRST_IMPRESSION].power == 90); + ASSUME(gMovesInfo[MOVE_LUNGE].power == 80); + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); + PLAYER(SPECIES_KANGASKHAN); + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_FIRST_IMPRESSION, MOVE_LUNGE); } + } WHEN { + TURN { EXPECT_MOVE(opponent, MOVE_FIRST_IMPRESSION); } + TURN { EXPECT_MOVE(opponent, MOVE_LUNGE); } + } +} + +AI_SINGLE_BATTLE_TEST("First Impression is not chosen if it's blocked by certain abilities") +{ + u16 species; + u16 ability; + + PARAMETRIZE { species = SPECIES_BRUXISH; ability = ABILITY_DAZZLING; } + PARAMETRIZE { species = SPECIES_FARIGIRAF; ability = ABILITY_ARMOR_TAIL; } + PARAMETRIZE { species = SPECIES_TSAREENA; ability = ABILITY_QUEENLY_MAJESTY; } + + KNOWN_FAILING; // Fails because the Omniscient flag is currently broken. It should pass after it is fixed + GIVEN { + ASSUME(gMovesInfo[MOVE_FIRST_IMPRESSION].effect == EFFECT_FIRST_TURN_ONLY); + ASSUME(gMovesInfo[MOVE_FIRST_IMPRESSION].power == 90); + ASSUME(gMovesInfo[MOVE_LUNGE].power == 80); + 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_FIRST_IMPRESSION, MOVE_LUNGE); } + } WHEN { + TURN { EXPECT_MOVE(opponent, MOVE_LUNGE); } + } +} diff --git a/test/dynamax.c b/test/dynamax.c index d9110fea1eb9..2db55d87b693 100644 --- a/test/dynamax.c +++ b/test/dynamax.c @@ -58,7 +58,7 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Dynamax expires after three turns", u16 hp) SINGLE_BATTLE_TEST("(DYNAMAX) Dynamaxed Pokemon cannot be flinched") { GIVEN { - ASSUME(gMovesInfo[MOVE_FAKE_OUT].effect == EFFECT_FAKE_OUT); + ASSUME(gMovesInfo[MOVE_FAKE_OUT].effect == EFFECT_FIRST_TURN_ONLY); PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); } WHEN {