diff --git a/asm/macros/battle_ai_script.inc b/asm/macros/battle_ai_script.inc index fa70c4d25..173595c82 100644 --- a/asm/macros/battle_ai_script.inc +++ b/asm/macros/battle_ai_script.inc @@ -640,6 +640,30 @@ .4byte \ptr .endm + .macro if_battler_absent battler:req, ptr:req + .byte 0x70 + .byte \battler + .4byte \ptr + .endm + + .macro if_grounded battler:req, ptr:req + .byte 0x71 + .byte \battler + .4byte \ptr + .endm + + .macro get_best_dmg_hp_percent + .byte 0x72 + .endm + + .macro get_curr_dmg_hp_percent + .byte 0x73 + .endm + + .macro get_move_split_from_result + .byte 0x74 + .endm + @ useful script macros .macro if_has_physical_move battler:req, ptr:req if_has_move_with_split \battler, SPLIT_PHYSICAL, \ptr diff --git a/data/battle_ai_scripts.s b/data/battle_ai_scripts.s index 31e91f135..31c614ac0 100644 --- a/data/battle_ai_scripts.s +++ b/data/battle_ai_scripts.s @@ -163,6 +163,7 @@ AI_CheckBadMove_CheckEffect: @ 82DC045 if_effect EFFECT_SANDSTORM, AI_CBM_Sandstorm if_effect EFFECT_SWAGGER, AI_CBM_Confuse if_effect EFFECT_ATTRACT, AI_CBM_Attract + if_effect EFFECT_CAPTIVATE, AI_CBM_Captivate if_effect EFFECT_RETURN, AI_CBM_HighRiskForDamage if_effect EFFECT_PRESENT, AI_CBM_HighRiskForDamage if_effect EFFECT_FRUSTRATION, AI_CBM_HighRiskForDamage @@ -254,6 +255,12 @@ AI_CheckBadMove_CheckEffect: @ 82DC045 if_effect EFFECT_PROTECT, AI_CBM_Protect if_effect EFFECT_TAUNT, AI_CBM_Taunt if_effect EFFECT_HEAL_BELL, AI_CBM_HealBell + if_effect EFFECT_FOLLOW_ME, AI_CBM_FollowMe + end + +AI_CBM_FollowMe: + if_not_double_battle Score_Minus10 + if_battler_absent AI_USER_PARTNER, Score_Minus10 end AI_CBM_HealBell: @@ -724,26 +731,33 @@ AI_CBM_Sandstorm: @ 82DC5ED get_weather if_equal AI_WEATHER_SANDSTORM, Score_Minus8 end - -AI_CBM_Attract: @ 82DC5F5 - if_status2 AI_TARGET, STATUS2_INFATUATION, Score_Minus10 + +AI_IsOppositeGender: get_ability AI_TARGET if_equal ABILITY_OBLIVIOUS, Score_Minus10 get_gender AI_USER - if_equal 0, AI_CBM_Attract_CheckIfTargetIsFemale - if_equal 254, AI_CBM_Attract_CheckIfTargetIsMale + if_equal 0, AI_IsOppositeGenderFemale + if_equal 254, AI_IsOppositeGenderMale goto Score_Minus10 - -AI_CBM_Attract_CheckIfTargetIsFemale: @ 82DC61A +AI_IsOppositeGenderFemale: @ 82DC61A get_gender AI_TARGET if_equal 254, AI_CBM_Attract_End goto Score_Minus10 - -AI_CBM_Attract_CheckIfTargetIsMale: @ 82DC627 +AI_IsOppositeGenderMale: @ 82DC627 get_gender AI_TARGET if_equal 0, AI_CBM_Attract_End goto Score_Minus10 + end + +AI_CBM_Captivate: + call AI_IsOppositeGender + goto AI_CBM_SpAtkDown +AI_CBM_Attract: @ 82DC5F5 + if_status2 AI_TARGET, STATUS2_INFATUATION, Score_Minus10 + call AI_IsOppositeGender + end + AI_CBM_Attract_End: @ 82DC634 end @@ -942,6 +956,19 @@ AI_CV_DmgMove: get_how_powerful_move_is if_equal MOVE_POWER_WEAK, Score_Minus1 end + +@ If move deals shit damage, and there are other mons to switch in, use support moves instead +AI_WeakDmg: + get_considered_move_power + if_equal 0, AI_Ret + if_has_no_move_with_split AI_USER, SPLIT_STATUS, AI_Ret + get_curr_dmg_hp_percent + if_more_than 30, AI_Ret + if_more_than 20, Score_Minus1 + get_how_powerful_move_is + if_equal MOVE_POWER_BEST, Score_Minus2 + score -3 + end AI_CheckViability: if_target_is_ally AI_Ret @@ -949,6 +976,7 @@ AI_CheckViability: call_if_move_flag FLAG_HIGH_CRIT, AI_CV_HighCrit call AI_CheckIfAlreadyDead call AI_CV_DmgMove + call AI_WeakDmg if_effect EFFECT_HIT, AI_CV_Hit if_effect EFFECT_SLEEP, AI_CV_Sleep if_effect EFFECT_ABSORB, AI_CV_Absorb @@ -1081,8 +1109,31 @@ AI_CheckViability: if_effect EFFECT_SPIKES, AI_CV_Hazards if_effect EFFECT_STICKY_WEB, AI_CV_Hazards if_effect EFFECT_TOXIC_SPIKES, AI_CV_Hazards + if_effect EFFECT_PERISH_SONG, AI_CV_PerishSong end +AI_CV_PerishSong: + get_ability AI_USER + if_equal ABILITY_ARENA_TRAP, AI_CV_PerishSong_ArenaTrap + if_equal ABILITY_MAGNET_PULL, AI_CV_PerishSong_MagnetPull + if_equal ABILITY_SHADOW_TAG, AI_CV_PerishSong_ShadowTag +AI_CV_PerishSongCheckTrap: + if_status2 AI_TARGET, STATUS2_WRAPPED | STATUS2_ESCAPE_PREVENTION, Score_Plus3 + @ If has a move that can trap, use it first, then use Perish Song + if_double_battle AI_Ret + if_has_move_with_effect AI_USER, EFFECT_TRAP, Score_Minus5 + if_has_move_with_effect AI_USER, EFFECT_MEAN_LOOK, Score_Minus5 + end +AI_CV_PerishSong_ArenaTrap: + if_grounded AI_TARGET, Score_Plus2 + goto AI_CV_PerishSongCheckTrap +AI_CV_PerishSong_MagnetPull: + if_type AI_TARGET, TYPE_STEEL, Score_Plus2 + goto AI_CV_PerishSongCheckTrap +AI_CV_PerishSong_ShadowTag: + if_no_ability AI_TARGET, ABILITY_SHADOW_TAG, Score_Plus2 + goto AI_CV_PerishSongCheckTrap + AI_CV_Hazards: if_ability AI_TARGET, ABILITY_MAGIC_BOUNCE, AI_CV_StealthRockEnd is_first_turn_for AI_USER @@ -1315,8 +1366,8 @@ AI_CV_DefenseUp4: @ 82DCC28 get_move_power_from_result if_equal 0, AI_CV_DefenseUp5 get_last_used_bank_move AI_TARGET - get_move_type_from_result - if_not_in_bytes AI_CV_DefenseUp_PhysicalTypes, AI_CV_DefenseUp_ScoreDown2 + get_move_split_from_result + if_not_equal SPLIT_PHYSICAL, AI_CV_DefenseUp_ScoreDown2 if_random_less_than 60, AI_CV_DefenseUp_End AI_CV_DefenseUp5: @ 82DCC4A @@ -1328,18 +1379,6 @@ AI_CV_DefenseUp_ScoreDown2: @ 82DCC50 AI_CV_DefenseUp_End: @ 82DCC52 end -AI_CV_DefenseUp_PhysicalTypes: @ 82DCC53 - .byte TYPE_NORMAL - .byte TYPE_FIGHTING - .byte TYPE_POISON - .byte TYPE_GROUND - .byte TYPE_FLYING - .byte TYPE_ROCK - .byte TYPE_BUG - .byte TYPE_GHOST - .byte TYPE_STEEL - .byte -1 - AI_CV_SpeedUp: @ 82DCC5D if_target_faster AI_CV_SpeedUp2 score -3 @@ -1395,8 +1434,8 @@ AI_CV_SpDefUp4: @ 82DCCDF get_move_power_from_result if_equal 0, AI_CV_SpDefUp5 get_last_used_bank_move AI_TARGET - get_move_type_from_result - if_in_bytes AI_CV_SpDefUp_PhysicalTypes, AI_CV_SpDefUp_ScoreDown2 + get_move_split_from_result + if_not_equal SPLIT_SPECIAL, AI_CV_SpDefUp_ScoreDown2 if_random_less_than 60, AI_CV_SpDefUp_End AI_CV_SpDefUp5: @ 82DCD01 @@ -1408,18 +1447,6 @@ AI_CV_SpDefUp_ScoreDown2: @ 82DCD07 AI_CV_SpDefUp_End: @ 82DCD09 end -AI_CV_SpDefUp_PhysicalTypes: @ 82DCD0A - .byte TYPE_NORMAL - .byte TYPE_FIGHTING - .byte TYPE_POISON - .byte TYPE_GROUND - .byte TYPE_FLYING - .byte TYPE_ROCK - .byte TYPE_BUG - .byte TYPE_GHOST - .byte TYPE_STEEL - .byte -1 - AI_CV_AccuracyUp: if_stat_level_less_than AI_USER, STAT_ACC, 9, AI_CV_AccuracyUp2 if_random_less_than 50, AI_CV_AccuracyUp2 @@ -1895,11 +1922,15 @@ AI_CV_SuperFang: AI_CV_SuperFang_End: end - + AI_CV_Trap: + if_status2 AI_TARGET, STATUS2_WRAPPED | STATUS2_ESCAPE_PREVENTION, AI_CV_TrapEnd + if_status3 AI_TARGET, STATUS3_PERISH_SONG, AI_CV_Trap5 + if_doesnt_have_move_with_effect AI_USER, EFFECT_PERISH_SONG, AI_CV_Trap1 + score +3 +AI_CV_Trap1: if_status AI_TARGET, STATUS1_TOXIC_POISON, AI_CV_Trap2 if_status2 AI_TARGET, STATUS2_CURSED | STATUS2_INFATUATION, AI_CV_Trap2 - if_status3 AI_TARGET, STATUS3_PERISH_SONG, AI_CV_Trap5 goto AI_CV_TrapItem AI_CV_Trap5: score +2 @@ -2069,20 +2100,27 @@ AI_CV_VitalThrow_End: end AI_CV_Substitute: + if_not_status2 AI_TARGET, STATUS2_WRAPPED | STATUS2_ESCAPE_PREVENTION, AI_CV_Substitute1 + if_status3 AI_TARGET, STATUS3_PERISH_SONG, AI_CV_SubstitutePlus3Continue + if_status AI_TARGET, STATUS1_BURN | STATUS1_PSN_ANY, AI_CV_SubstitutePlus1Continue + goto AI_CV_Substitute1 +AI_CV_SubstitutePlus1Continue: + score +1 + goto AI_CV_Substitute1 +AI_CV_SubstitutePlus3Continue: + score +3 +AI_CV_Substitute1: if_hp_more_than AI_USER, 90, AI_CV_Substitute4 if_hp_more_than AI_USER, 70, AI_CV_Substitute3 if_hp_more_than AI_USER, 50, AI_CV_Substitute2 if_random_less_than 100, AI_CV_Substitute2 score -1 - AI_CV_Substitute2: if_random_less_than 100, AI_CV_Substitute3 score -1 - AI_CV_Substitute3: if_random_less_than 100, AI_CV_Substitute4 score -1 - AI_CV_Substitute4: if_target_faster AI_CV_Substitute_End get_last_used_bank_move AI_TARGET @@ -2095,22 +2133,17 @@ AI_CV_Substitute4: if_equal EFFECT_CONFUSE, AI_CV_Substitute6 if_equal EFFECT_LEECH_SEED, AI_CV_Substitute7 goto AI_CV_Substitute_End - AI_CV_Substitute5: if_not_status AI_TARGET, STATUS1_ANY, AI_CV_Substitute8 goto AI_CV_Substitute_End - AI_CV_Substitute6: if_not_status2 AI_TARGET, STATUS2_CONFUSION, AI_CV_Substitute8 goto AI_CV_Substitute_End - AI_CV_Substitute7: if_status3 AI_TARGET, STATUS3_LEECHSEED, AI_CV_Substitute_End - AI_CV_Substitute8: if_random_less_than 100, AI_CV_Substitute_End score +1 - AI_CV_Substitute_End: end @@ -2525,20 +2558,16 @@ AI_CV_BatonPass: if_stat_level_more_than AI_USER, STAT_SPDEF, 8, AI_CV_BatonPass2 if_stat_level_more_than AI_USER, STAT_EVASION, 8, AI_CV_BatonPass2 goto AI_CV_BatonPass5 - AI_CV_BatonPass2: if_target_faster AI_CV_BatonPass3 - if_hp_more_than AI_USER, 60, AI_CV_BatonPass_End + if_hp_more_than AI_USER, 60, AI_CV_BatonPass_Last goto AI_CV_BatonPass4 - AI_CV_BatonPass3: - if_hp_more_than AI_USER, 70, AI_CV_BatonPass_End - + if_hp_more_than AI_USER, 70, AI_CV_BatonPass_Last AI_CV_BatonPass4: - if_random_less_than 80, AI_CV_BatonPass_End + if_random_less_than 80, AI_CV_BatonPass_Last score +2 - goto AI_CV_BatonPass_End - + goto AI_CV_BatonPass_Last AI_CV_BatonPass5: if_stat_level_more_than AI_USER, STAT_ATK, 7, AI_CV_BatonPass7 if_stat_level_more_than AI_USER, STAT_DEF, 7, AI_CV_BatonPass7 @@ -2546,18 +2575,38 @@ AI_CV_BatonPass5: if_stat_level_more_than AI_USER, STAT_SPDEF, 7, AI_CV_BatonPass7 if_stat_level_more_than AI_USER, STAT_EVASION, 7, AI_CV_BatonPass7 goto AI_CV_BatonPass_ScoreDown2 - AI_CV_BatonPass7: if_target_faster AI_CV_BatonPass8 + if_ai_can_go_down AI_CV_BatonPass4 if_hp_more_than AI_USER, 60, AI_CV_BatonPass_ScoreDown2 - goto AI_CV_BatonPass_End - + goto AI_CV_BatonPass_Last AI_CV_BatonPass8: - if_hp_less_than AI_USER, 70, AI_CV_BatonPass_End - + if_ai_can_go_down AI_CV_BatonPass_ScoreDown2 + if_hp_less_than AI_USER, 70, AI_CV_BatonPass_Last + goto AI_CV_BatonPass_ScoreDown2 +AI_CV_BatonPass9: + if_stat_level_more_than AI_USER, STAT_ATK, 6, AI_CV_BatonPass10 + if_stat_level_more_than AI_USER, STAT_DEF, 6, AI_CV_BatonPass10 + if_stat_level_more_than AI_USER, STAT_SPATK, 6, AI_CV_BatonPass10 + if_stat_level_more_than AI_USER, STAT_SPDEF, 6, AI_CV_BatonPass10 + if_stat_level_more_than AI_USER, STAT_EVASION, 6, AI_CV_BatonPass10 + goto AI_CV_BatonPass_ScoreDown2 +AI_CV_BatonPass10: + if_target_faster AI_CV_BatonPass11 + if_ai_can_go_down AI_CV_BatonPass4 + if_hp_more_than AI_USER, 60, AI_CV_BatonPass_ScoreDown2 + goto AI_CV_BatonPass_Last +AI_CV_BatonPass11: + if_ai_can_go_down AI_CV_BatonPass_ScoreDown2 + if_hp_less_than AI_USER, 70, AI_CV_BatonPass_Last + goto AI_CV_BatonPass_ScoreDown2 AI_CV_BatonPass_ScoreDown2: score -2 - + end +AI_CV_BatonPass_Last: + get_best_dmg_hp_percent + if_less_than 10, Score_Plus2 + if_less_than 20, Score_Plus1 AI_CV_BatonPass_End: end @@ -3386,36 +3435,7 @@ AI_Risky_EffectsToEncourage: .byte EFFECT_REVENGE .byte EFFECT_TEETER_DANCE .byte -1 - -AI_PreferBatonPass: - if_target_is_ally AI_Ret - count_usable_party_mons AI_USER - if_equal 0, AI_PreferBatonPassEnd - get_how_powerful_move_is - if_not_equal MOVE_POWER_DISCOURAGED, AI_PreferBatonPassEnd - if_has_move_with_effect AI_USER, EFFECT_BATON_PASS, AI_PreferBatonPass_GoForBatonPass - if_random_less_than 80, AI_Risky_End - -AI_PreferBatonPass_GoForBatonPass: - get_considered_move_effect - if_in_hwords sEffectsStatRaise, AI_PreferBatonPass2 - if_effect EFFECT_PROTECT, AI_PreferBatonPass_End - if_move MOVE_BATON_PASS, AI_PreferBatonPass_EncourageIfHighStats - if_random_less_than 20, AI_Risky_End - score +3 - -AI_PreferBatonPass2: - get_turn_count - if_equal 0, Score_Plus5 - if_hp_less_than AI_USER, 60, Score_Minus10 - goto Score_Plus1 - -AI_PreferBatonPass_End: - get_last_used_bank_move AI_USER - if_in_hwords sMovesTable_ProtectMoves, Score_Minus2 - score +2 - end - + .align 1 sMovesTable_ProtectMoves: .2byte MOVE_PROTECT @@ -3423,10 +3443,15 @@ sMovesTable_ProtectMoves: .2byte -1 sEffectsStatRaise: + .2byte EFFECT_ATTACK_UP .2byte EFFECT_ATTACK_UP_2 + .2byte EFFECT_DEFENSE_UP .2byte EFFECT_DEFENSE_UP_2 + .2byte EFFECT_SPEED_UP .2byte EFFECT_SPEED_UP_2 + .2byte EFFECT_SPECIAL_ATTACK_UP .2byte EFFECT_SPECIAL_ATTACK_UP_2 + .2byte EFFECT_SPECIAL_DEFENSE_UP .2byte EFFECT_SPECIAL_DEFENSE_UP_2 .2byte EFFECT_CALM_MIND .2byte EFFECT_DRAGON_DANCE @@ -3440,6 +3465,28 @@ sEffectsStatRaise: .2byte EFFECT_QUIVER_DANCE .2byte -1 +AI_PreferBatonPass: + if_target_is_ally AI_Ret + count_usable_party_mons AI_USER + if_equal 0, AI_PreferBatonPassEnd + get_how_powerful_move_is + if_not_equal MOVE_POWER_DISCOURAGED, AI_PreferBatonPassEnd + if_doesnt_have_move_with_effect AI_USER, EFFECT_BATON_PASS, AI_PreferBatonPassEnd + get_considered_move_effect + if_in_hwords sEffectsStatRaise, AI_PreferBatonPass2 + if_effect EFFECT_PROTECT, AI_PreferBatonPass3 + if_move MOVE_BATON_PASS, AI_PreferBatonPass_EncourageIfHighStats + end +AI_PreferBatonPass2: + get_turn_count + if_equal 0, Score_Plus5 + if_hp_less_than AI_USER, 60, Score_Minus10 + goto Score_Plus1 +AI_PreferBatonPass3: + get_last_used_bank_move AI_USER + if_in_hwords sMovesTable_ProtectMoves, Score_Minus2 + score +2 + end AI_PreferBatonPass_EncourageIfHighStats: get_turn_count if_equal 0, Score_Minus2 @@ -3450,7 +3497,6 @@ AI_PreferBatonPass_EncourageIfHighStats: if_stat_level_more_than AI_USER, STAT_SPATK, 7, Score_Plus2 if_stat_level_more_than AI_USER, STAT_SPATK, 6, Score_Plus1 end - AI_PreferBatonPassEnd: end @@ -3459,6 +3505,7 @@ AI_ConsiderAllyChosenMove: if_equal 0, AI_ConsiderAllyChosenMoveRet get_move_effect_from_result if_equal EFFECT_HELPING_HAND, AI_PartnerChoseHelpingHand + if_equal EFFECT_PERISH_SONG, AI_PartnerChosePerishSong AI_ConsiderAllyChosenMoveRet: end @@ -3468,17 +3515,30 @@ AI_PartnerChoseHelpingHand: if_equal 0, Score_Minus5 end +AI_PartnerChosePerishSong: + if_status2 AI_TARGET, STATUS2_ESCAPE_PREVENTION | STATUS2_WRAPPED, AI_Ret + get_considered_move_effect + if_equal EFFECT_MEAN_LOOK, Score_Plus1 + if_equal EFFECT_TRAP, Score_Plus1 + end + AI_ConsiderAllyKnownMoves: @ If ally already chose a move, there is nothing to do here. get_ally_chosen_move if_not_equal 0, AI_Ret if_move MOVE_HELPING_HAND, AI_HelpingHandInDoubles + if_move MOVE_PERISH_SONG, AI_PerishSongInDoubles end AI_HelpingHandInDoubles: if_has_no_attacking_moves AI_USER_PARTNER, Score_Minus5 end +AI_PerishSongInDoubles: + if_has_move_with_effect AI_USER_PARTNER, EFFECT_MEAN_LOOK, Score_Plus1 + if_has_move_with_effect AI_USER_PARTNER, EFFECT_TRAP, Score_Plus1 + end + AI_DoubleBattle: call AI_ConsiderAllyChosenMove call AI_ConsiderAllyKnownMoves diff --git a/src/battle_ai_script_commands.c b/src/battle_ai_script_commands.c index 2b4789cfa..68e517f9b 100644 --- a/src/battle_ai_script_commands.c +++ b/src/battle_ai_script_commands.c @@ -170,6 +170,11 @@ static void BattleAICmd_if_ai_can_go_down(void); static void BattleAICmd_if_has_move_with_type(void); static void BattleAICmd_if_no_move_used(void); static void BattleAICmd_if_has_move_with_flag(void); +static void BattleAICmd_if_battler_absent(void); +static void BattleAICmd_is_grounded(void); +static void BattleAICmd_get_best_dmg_hp_percent(void); +static void BattleAICmd_get_curr_dmg_hp_percent(void); +static void BattleAICmd_get_move_split_from_result(void); // ewram EWRAM_DATA const u8 *gAIScriptPtr = NULL; @@ -292,6 +297,11 @@ static const BattleAICmdFunc sBattleAICmdTable[] = BattleAICmd_if_has_move_with_type, // 0x6D BattleAICmd_if_no_move_used, // 0x6E BattleAICmd_if_has_move_with_flag, // 0x6F + BattleAICmd_if_battler_absent, // 0x70 + BattleAICmd_is_grounded, // 0x71 + BattleAICmd_get_best_dmg_hp_percent, // 0x72 + BattleAICmd_get_curr_dmg_hp_percent, // 0x73 + BattleAICmd_get_move_split_from_result, // 0x74 }; static const u16 sDiscouragedPowerfulMoveEffects[] = @@ -404,7 +414,7 @@ void BattleAI_SetupAIData(u8 defaultScoreMoves) move = gBattleMons[sBattler_AI].moves[i]; if (gBattleMoves[move].power != 0 && !(moveLimitations & gBitTable[i])) { - dmg = AI_CalcDamage(move, sBattler_AI, gBattlerTarget) * (100 - (Random() % 16)) / 100; + dmg = AI_CalcDamage(move, sBattler_AI, gBattlerTarget) * (100 - (Random() % 10)) / 100; if (dmg == 0) dmg = 1; } @@ -2665,3 +2675,53 @@ static void BattleAICmd_if_no_move_used(void) gAIScriptPtr += 6; } } + +static void BattleAICmd_if_battler_absent(void) +{ + u32 battler = BattleAI_GetWantedBattler(gAIScriptPtr[1]); + + if (!IsBattlerAlive(battler)) + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 2); + else + gAIScriptPtr += 6; +} + +static void BattleAICmd_is_grounded(void) +{ + u32 battler = BattleAI_GetWantedBattler(gAIScriptPtr[1]); + + if (IsBattlerGrounded(battler)) + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 2); + else + gAIScriptPtr += 6; +} + +static void BattleAICmd_get_best_dmg_hp_percent(void) +{ + int i, bestDmg; + + bestDmg = 0; + for (i = 0; i < MAX_MON_MOVES; i++) + { + if (gBattleResources->ai->simulatedDmg[sBattler_AI][gBattlerTarget][i] > bestDmg) + bestDmg = gBattleResources->ai->simulatedDmg[sBattler_AI][gBattlerTarget][i]; + } + + gBattleResources->ai->funcResult = (bestDmg * 100) / gBattleMons[gBattlerTarget].maxHP; + gAIScriptPtr++; +} + +static void BattleAICmd_get_curr_dmg_hp_percent(void) +{ + int bestDmg = gBattleResources->ai->simulatedDmg[sBattler_AI][gBattlerTarget][AI_THINKING_STRUCT->movesetIndex]; + + gBattleResources->ai->funcResult = (bestDmg * 100) / gBattleMons[gBattlerTarget].maxHP; + gAIScriptPtr++; +} + +static void BattleAICmd_get_move_split_from_result(void) +{ + AI_THINKING_STRUCT->funcResult = gBattleMoves[AI_THINKING_STRUCT->funcResult].type; + + gAIScriptPtr += 1; +} diff --git a/src/battle_ai_switch_items.c b/src/battle_ai_switch_items.c index 02f524a24..ce85a37f7 100644 --- a/src/battle_ai_switch_items.c +++ b/src/battle_ai_switch_items.c @@ -579,15 +579,15 @@ void AI_TrySwitchOrUseItem(void) u8 GetMostSuitableMonToSwitchInto(void) { - u8 opposingBattler; - u32 bestDmg; - u8 bestMonId; - u8 battlerIn1, battlerIn2; - s32 firstId; - s32 lastId; // + 1 + u8 opposingBattler = 0; + u32 bestDmg = 0; + u8 bestMonId = 0; + u8 battlerIn1 = 0, battlerIn2 = 0; + s32 firstId = 0; + s32 lastId = 0; // + 1 struct Pokemon *party; - s32 i, j; - u8 invalidMons; + s32 i, j, aliveCount = 0; + u8 invalidMons = 0, bits = 0; u16 move; if (*(gBattleStruct->monToSwitchIntoId + gActiveBattler) != PARTY_SIZE) @@ -603,8 +603,7 @@ u8 GetMostSuitableMonToSwitchInto(void) else battlerIn2 = GetBattlerAtPosition(GetBattlerPosition(gActiveBattler) ^ BIT_FLANK); - // UB: It considers the opponent only player's side even though it can battle alongside player. - opposingBattler = Random() & BIT_FLANK; + opposingBattler = BATTLE_OPPOSITE(battlerIn1); if (gAbsentBattlerFlags & gBitTable[opposingBattler]) opposingBattler ^= BIT_FLANK; } @@ -622,24 +621,56 @@ u8 GetMostSuitableMonToSwitchInto(void) else party = gEnemyParty; - invalidMons = 0; + // Get invalid slots ids. + for (i = firstId; i < lastId; i++) + { + if (GetMonData(&party[i], MON_DATA_SPECIES) == SPECIES_NONE + || GetMonData(&party[i], MON_DATA_HP) == 0 + || gBattlerPartyIndexes[battlerIn1] == i + || gBattlerPartyIndexes[battlerIn2] == i + || i == *(gBattleStruct->monToSwitchIntoId + battlerIn1) + || i == *(gBattleStruct->monToSwitchIntoId + battlerIn2)) + invalidMons |= gBitTable[i]; + else + aliveCount++; + } - while (invalidMons != 0x3F) // All mons are invalid. + // If there are two(or more) mons to choose from, always choose one that has baton pass + // as most often it can't do much on its own. + for (i = firstId; i < lastId; i++) + { + if (invalidMons & gBitTable[i]) + continue; + + for (j = 0; j < MAX_MON_MOVES; j++) + { + if (GetMonData(&party[i], MON_DATA_MOVE1 + j, NULL) == MOVE_BATON_PASS) + { + bits |= gBitTable[i]; + break; + } + } + } + if ((aliveCount == 2 || (aliveCount > 2 && Random() % 3 == 0)) && bits) + { + do + { + bestMonId = (Random() % (lastId - firstId)) + firstId; + } while (!(bits & gBitTable[bestMonId])); + return bestMonId; + } + + bits = 0; + while (bits != 0x3F) // All mons are invalid. { bestDmg = 0; - bestMonId = 6; + bestMonId = PARTY_SIZE; // Find the mon whose type is the most suitable offensively. for (i = firstId; i < lastId; i++) { - u16 species = GetMonData(&party[i], MON_DATA_SPECIES); - if (species != SPECIES_NONE - && GetMonData(&party[i], MON_DATA_HP) != 0 - && !(gBitTable[i] & invalidMons) - && gBattlerPartyIndexes[battlerIn1] != i - && gBattlerPartyIndexes[battlerIn2] != i - && i != *(gBattleStruct->monToSwitchIntoId + battlerIn1) - && i != *(gBattleStruct->monToSwitchIntoId + battlerIn2)) + if (!(gBitTable[i] & invalidMons) && !(gBitTable[i] & bits)) { + u16 species = GetMonData(&party[i], MON_DATA_SPECIES); u32 typeDmg = UQ_4_12(1.0); u8 atkType1 = gBaseStats[species].type1; @@ -662,10 +693,6 @@ u8 GetMostSuitableMonToSwitchInto(void) bestMonId = i; } } - else - { - invalidMons |= gBitTable[i]; - } } // Ok, we know the mon has the right typing but does it have at least one super effective move? @@ -681,11 +708,11 @@ u8 GetMostSuitableMonToSwitchInto(void) if (i != MAX_MON_MOVES) return bestMonId; // Has both the typing and at least one super effective move. - invalidMons |= gBitTable[bestMonId]; // Sorry buddy, we want something better. + bits |= gBitTable[bestMonId]; // Sorry buddy, we want something better. } else { - invalidMons = 0x3F; // No viable mon to switch. + bits = 0x3F; // No viable mon to switch. } } @@ -697,31 +724,20 @@ u8 GetMostSuitableMonToSwitchInto(void) // If we couldn't find the best mon in terms of typing, find the one that deals most damage. for (i = firstId; i < lastId; i++) { - if ((u16)(GetMonData(&party[i], MON_DATA_SPECIES)) == SPECIES_NONE) - continue; - if (GetMonData(&party[i], MON_DATA_HP) == 0) - continue; - if (gBattlerPartyIndexes[battlerIn1] == i) - continue; - if (gBattlerPartyIndexes[battlerIn2] == i) - continue; - if (i == *(gBattleStruct->monToSwitchIntoId + battlerIn1)) - continue; - if (i == *(gBattleStruct->monToSwitchIntoId + battlerIn2)) + if (gBitTable[i] & invalidMons) continue; for (j = 0; j < MAX_MON_MOVES; j++) { - s32 dmg = 0; move = GetMonData(&party[i], MON_DATA_MOVE1 + j); - if (move != MOVE_NONE && gBattleMoves[move].power != 1) + if (move != MOVE_NONE && gBattleMoves[move].power != 0) { - dmg = AI_CalcPartyMonDamage(move, gActiveBattler, opposingBattler, &party[i]); - } - if (bestDmg < dmg) - { - bestDmg = dmg; - bestMonId = i; + s32 dmg = AI_CalcPartyMonDamage(move, gActiveBattler, opposingBattler, &party[i]); + if (bestDmg < dmg) + { + bestDmg = dmg; + bestMonId = i; + } } } }