#include "global.h" #include "battle.h" #include "battle_ai_main.h" #include "battle_ai_util.h" #include "constants/battle_ai.h" #include "battle_anim.h" #include "battle_arena.h" #include "battle_controllers.h" #include "battle_message.h" #include "battle_interface.h" #include "battle_setup.h" #include "battle_tower.h" #include "battle_tv.h" #include "battle_z_move.h" #include "bg.h" #include "data.h" #include "frontier_util.h" #include "item.h" #include "link.h" #include "main.h" #include "m4a.h" #include "palette.h" #include "party_menu.h" #include "pokeball.h" #include "pokemon.h" #include "random.h" #include "reshow_battle_screen.h" #include "sound.h" #include "string_util.h" #include "task.h" #include "text.h" #include "util.h" #include "window.h" #include "constants/battle_anim.h" #include "constants/items.h" #include "constants/moves.h" #include "constants/party_menu.h" #include "constants/songs.h" #include "constants/trainers.h" #include "trainer_hill.h" #include "test_runner.h" static void OpponentHandleLoadMonSprite(u32 battler); static void OpponentHandleSwitchInAnim(u32 battler); static void OpponentHandleDrawTrainerPic(u32 battler); static void OpponentHandleTrainerSlide(u32 battler); static void OpponentHandleTrainerSlideBack(u32 battler); static void OpponentHandleMoveAnimation(u32 battler); static void OpponentHandlePrintString(u32 battler); static void OpponentHandleChooseAction(u32 battler); static void OpponentHandleChooseMove(u32 battler); static void OpponentHandleChooseItem(u32 battler); static void OpponentHandleChoosePokemon(u32 battler); static void OpponentHandleHealthBarUpdate(u32 battler); static void OpponentHandleIntroTrainerBallThrow(u32 battler); static void OpponentHandleDrawPartyStatusSummary(u32 battler); static void OpponentHandleBattleAnimation(u32 battler); static void OpponentHandleEndLinkBattle(u32 battler); static u8 CountAIAliveNonEggMonsExcept(u8 slotToIgnore); static void OpponentBufferRunCommand(u32 battler); static void OpponentBufferExecCompleted(u32 battler); static void SwitchIn_HandleSoundAndEnd(u32 battler); static void (*const sOpponentBufferCommands[CONTROLLER_CMDS_COUNT])(u32 battler) = { [CONTROLLER_GETMONDATA] = BtlController_HandleGetMonData, [CONTROLLER_GETRAWMONDATA] = BtlController_HandleGetRawMonData, [CONTROLLER_SETMONDATA] = BtlController_HandleSetMonData, [CONTROLLER_SETRAWMONDATA] = BtlController_HandleSetRawMonData, [CONTROLLER_LOADMONSPRITE] = OpponentHandleLoadMonSprite, [CONTROLLER_SWITCHINANIM] = OpponentHandleSwitchInAnim, [CONTROLLER_RETURNMONTOBALL] = BtlController_HandleReturnMonToBall, [CONTROLLER_DRAWTRAINERPIC] = OpponentHandleDrawTrainerPic, [CONTROLLER_TRAINERSLIDE] = OpponentHandleTrainerSlide, [CONTROLLER_TRAINERSLIDEBACK] = OpponentHandleTrainerSlideBack, [CONTROLLER_FAINTANIMATION] = BtlController_HandleFaintAnimation, [CONTROLLER_PALETTEFADE] = BtlController_Empty, [CONTROLLER_SUCCESSBALLTHROWANIM] = BtlController_Empty, [CONTROLLER_BALLTHROWANIM] = BtlController_Empty, [CONTROLLER_PAUSE] = BtlController_Empty, [CONTROLLER_MOVEANIMATION] = OpponentHandleMoveAnimation, [CONTROLLER_PRINTSTRING] = OpponentHandlePrintString, [CONTROLLER_PRINTSTRINGPLAYERONLY] = BtlController_Empty, [CONTROLLER_CHOOSEACTION] = OpponentHandleChooseAction, [CONTROLLER_YESNOBOX] = BtlController_Empty, [CONTROLLER_CHOOSEMOVE] = OpponentHandleChooseMove, [CONTROLLER_OPENBAG] = OpponentHandleChooseItem, [CONTROLLER_CHOOSEPOKEMON] = OpponentHandleChoosePokemon, [CONTROLLER_23] = BtlController_Empty, [CONTROLLER_HEALTHBARUPDATE] = OpponentHandleHealthBarUpdate, [CONTROLLER_EXPUPDATE] = BtlController_Empty, [CONTROLLER_STATUSICONUPDATE] = BtlController_HandleStatusIconUpdate, [CONTROLLER_STATUSANIMATION] = BtlController_HandleStatusAnimation, [CONTROLLER_STATUSXOR] = BtlController_Empty, [CONTROLLER_DATATRANSFER] = BtlController_Empty, [CONTROLLER_DMA3TRANSFER] = BtlController_Empty, [CONTROLLER_PLAYBGM] = BtlController_Empty, [CONTROLLER_32] = BtlController_Empty, [CONTROLLER_TWORETURNVALUES] = BtlController_Empty, [CONTROLLER_CHOSENMONRETURNVALUE] = BtlController_Empty, [CONTROLLER_ONERETURNVALUE] = BtlController_Empty, [CONTROLLER_ONERETURNVALUE_DUPLICATE] = BtlController_Empty, [CONTROLLER_CLEARUNKVAR] = BtlController_HandleClearUnkVar, [CONTROLLER_SETUNKVAR] = BtlController_HandleSetUnkVar, [CONTROLLER_CLEARUNKFLAG] = BtlController_HandleClearUnkFlag, [CONTROLLER_TOGGLEUNKFLAG] = BtlController_HandleToggleUnkFlag, [CONTROLLER_HITANIMATION] = BtlController_HandleHitAnimation, [CONTROLLER_CANTSWITCH] = BtlController_Empty, [CONTROLLER_PLAYSE] = BtlController_HandlePlaySE, [CONTROLLER_PLAYFANFAREORBGM] = BtlController_HandlePlayFanfareOrBGM, [CONTROLLER_FAINTINGCRY] = BtlController_HandleFaintingCry, [CONTROLLER_INTROSLIDE] = BtlController_HandleIntroSlide, [CONTROLLER_INTROTRAINERBALLTHROW] = OpponentHandleIntroTrainerBallThrow, [CONTROLLER_DRAWPARTYSTATUSSUMMARY] = OpponentHandleDrawPartyStatusSummary, [CONTROLLER_HIDEPARTYSTATUSSUMMARY] = BtlController_HandleHidePartyStatusSummary, [CONTROLLER_ENDBOUNCE] = BtlController_Empty, [CONTROLLER_SPRITEINVISIBILITY] = BtlController_HandleSpriteInvisibility, [CONTROLLER_BATTLEANIMATION] = OpponentHandleBattleAnimation, [CONTROLLER_LINKSTANDBYMSG] = BtlController_Empty, [CONTROLLER_RESETACTIONMOVESELECTION] = BtlController_Empty, [CONTROLLER_ENDLINKBATTLE] = OpponentHandleEndLinkBattle, [CONTROLLER_DEBUGMENU] = BtlController_Empty, [CONTROLLER_TERMINATOR_NOP] = BtlController_TerminatorNop }; void SetControllerToOpponent(u32 battler) { gBattlerControllerEndFuncs[battler] = OpponentBufferExecCompleted; gBattlerControllerFuncs[battler] = OpponentBufferRunCommand; } static void OpponentBufferRunCommand(u32 battler) { if (gBattleControllerExecFlags & (1u << battler)) { if (gBattleResources->bufferA[battler][0] < ARRAY_COUNT(sOpponentBufferCommands)) sOpponentBufferCommands[gBattleResources->bufferA[battler][0]](battler); else OpponentBufferExecCompleted(battler); } } static void Intro_DelayAndEnd(u32 battler) { if (--gBattleSpritesDataPtr->healthBoxesData[battler].introEndDelay == (u8)-1) { gBattleSpritesDataPtr->healthBoxesData[battler].introEndDelay = 0; OpponentBufferExecCompleted(battler); } } static void Intro_WaitForShinyAnimAndHealthbox(u32 battler) { bool8 healthboxAnimDone = FALSE; bool8 twoMons; twoMons = TwoOpponentIntroMons(battler); if (!twoMons || ((twoMons && (gBattleTypeFlags & BATTLE_TYPE_MULTI) && !BATTLE_TWO_VS_ONE_OPPONENT) || (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS))) { if (gSprites[gHealthboxSpriteIds[battler]].callback == SpriteCallbackDummy) healthboxAnimDone = TRUE; twoMons = FALSE; } else { if (gSprites[gHealthboxSpriteIds[battler]].callback == SpriteCallbackDummy && gSprites[gHealthboxSpriteIds[BATTLE_PARTNER(battler)]].callback == SpriteCallbackDummy) healthboxAnimDone = TRUE; twoMons = TRUE; } if (healthboxAnimDone) { if (twoMons == TRUE) { if (gBattleSpritesDataPtr->healthBoxesData[battler].finishedShinyMonAnim && gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].finishedShinyMonAnim) { gBattleSpritesDataPtr->healthBoxesData[battler].triedShinyMonAnim = FALSE; gBattleSpritesDataPtr->healthBoxesData[battler].finishedShinyMonAnim = FALSE; gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].triedShinyMonAnim = FALSE; gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].finishedShinyMonAnim = FALSE; FreeSpriteTilesByTag(ANIM_TAG_GOLD_STARS); FreeSpritePaletteByTag(ANIM_TAG_GOLD_STARS); } else { return; } } else if (gBattleSpritesDataPtr->healthBoxesData[battler].finishedShinyMonAnim) { if (GetBattlerPosition(battler) == 3) { if (!gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].triedShinyMonAnim && !gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].finishedShinyMonAnim) { FreeSpriteTilesByTag(ANIM_TAG_GOLD_STARS); FreeSpritePaletteByTag(ANIM_TAG_GOLD_STARS); } else { return; } } gBattleSpritesDataPtr->healthBoxesData[battler].triedShinyMonAnim = FALSE; gBattleSpritesDataPtr->healthBoxesData[battler].finishedShinyMonAnim = FALSE; } else { return; } gBattleSpritesDataPtr->healthBoxesData[battler].introEndDelay = 3; gBattlerControllerFuncs[battler] = Intro_DelayAndEnd; } } static void TrySetBattlerShadowSpriteCallback(u32 battler) { if (gSprites[gBattleSpritesDataPtr->healthBoxesData[battler].shadowSpriteIdPrimary].callback == SpriteCallbackDummy) { if (B_ENEMY_MON_SHADOW_STYLE <= GEN_3 || P_GBA_STYLE_SPECIES_GFX == TRUE || gSprites[gBattleSpritesDataPtr->healthBoxesData[battler].shadowSpriteIdSecondary].callback == SpriteCallbackDummy) { SetBattlerShadowSpriteCallback(battler, GetMonData(&gEnemyParty[gBattlerPartyIndexes[battler]], MON_DATA_SPECIES)); } } } static void Intro_TryShinyAnimShowHealthbox(u32 battler) { bool32 bgmRestored = FALSE; bool32 battlerAnimsDone = FALSE; bool32 twoMons; if (!gBattleSpritesDataPtr->healthBoxesData[battler].triedShinyMonAnim && !gBattleSpritesDataPtr->healthBoxesData[battler].ballAnimActive && !gBattleSpritesDataPtr->healthBoxesData[battler].finishedShinyMonAnim) TryShinyAnimation(battler, &gEnemyParty[gBattlerPartyIndexes[battler]]); twoMons = TwoOpponentIntroMons(battler); if (!(gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) && (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) || BATTLE_TWO_VS_ONE_OPPONENT) && twoMons && !gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].triedShinyMonAnim && !gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].ballAnimActive && !gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].finishedShinyMonAnim) TryShinyAnimation(BATTLE_PARTNER(battler), &gEnemyParty[gBattlerPartyIndexes[BATTLE_PARTNER(battler)]]); if (!gBattleSpritesDataPtr->healthBoxesData[battler].ballAnimActive && !gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].ballAnimActive) { if (!gBattleSpritesDataPtr->healthBoxesData[battler].healthboxSlideInStarted) { if (twoMons && (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) || BATTLE_TWO_VS_ONE_OPPONENT)) { UpdateHealthboxAttribute(gHealthboxSpriteIds[BATTLE_PARTNER(battler)], &gEnemyParty[gBattlerPartyIndexes[BATTLE_PARTNER(battler)]], HEALTHBOX_ALL); StartHealthboxSlideIn(BATTLE_PARTNER(battler)); SetHealthboxSpriteVisible(gHealthboxSpriteIds[BATTLE_PARTNER(battler)]); } UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], &gEnemyParty[gBattlerPartyIndexes[battler]], HEALTHBOX_ALL); StartHealthboxSlideIn(battler); SetHealthboxSpriteVisible(gHealthboxSpriteIds[battler]); } gBattleSpritesDataPtr->healthBoxesData[battler].healthboxSlideInStarted = TRUE; } if (!gBattleSpritesDataPtr->healthBoxesData[battler].waitForCry && gBattleSpritesDataPtr->healthBoxesData[battler].healthboxSlideInStarted && !gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].waitForCry && !IsCryPlayingOrClearCrySongs()) { if (!gBattleSpritesDataPtr->healthBoxesData[battler].bgmRestored) { if (gBattleTypeFlags & BATTLE_TYPE_MULTI && gBattleTypeFlags & BATTLE_TYPE_LINK) { if (GetBattlerPosition(battler) == 1) m4aMPlayContinue(&gMPlayInfo_BGM); } else { m4aMPlayVolumeControl(&gMPlayInfo_BGM, TRACKS_ALL, 0x100); } } gBattleSpritesDataPtr->healthBoxesData[battler].bgmRestored = TRUE; bgmRestored = TRUE; } if (!twoMons || (twoMons && gBattleTypeFlags & BATTLE_TYPE_MULTI && !BATTLE_TWO_VS_ONE_OPPONENT)) { if (gSprites[gBattleControllerData[battler]].callback == SpriteCallbackDummy) { TrySetBattlerShadowSpriteCallback(battler); if (gSprites[gBattlerSpriteIds[battler]].callback == SpriteCallbackDummy) { battlerAnimsDone = TRUE; } } } else { if (gSprites[gBattleControllerData[battler]].callback == SpriteCallbackDummy && gSprites[gBattleControllerData[BATTLE_PARTNER(battler)]].callback == SpriteCallbackDummy) { TrySetBattlerShadowSpriteCallback(battler); TrySetBattlerShadowSpriteCallback(BATTLE_PARTNER(battler)); if (gSprites[gBattlerSpriteIds[battler]].callback == SpriteCallbackDummy && gSprites[gBattlerSpriteIds[BATTLE_PARTNER(battler)]].callback == SpriteCallbackDummy) { battlerAnimsDone = TRUE; } } } if (bgmRestored && battlerAnimsDone) { if (twoMons && (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) || BATTLE_TWO_VS_ONE_OPPONENT)) DestroySprite(&gSprites[gBattleControllerData[BATTLE_PARTNER(battler)]]); DestroySprite(&gSprites[gBattleControllerData[battler]]); gBattleSpritesDataPtr->animationData->introAnimActive = FALSE; gBattleSpritesDataPtr->healthBoxesData[battler].bgmRestored = FALSE; gBattleSpritesDataPtr->healthBoxesData[battler].healthboxSlideInStarted = FALSE; gBattlerControllerFuncs[battler] = Intro_WaitForShinyAnimAndHealthbox; } } static void TryShinyAnimAfterMonAnim(u32 battler) { if (gSprites[gBattlerSpriteIds[battler]].x2 == 0 && !gBattleSpritesDataPtr->healthBoxesData[battler].triedShinyMonAnim && !gBattleSpritesDataPtr->healthBoxesData[battler].finishedShinyMonAnim) TryShinyAnimation(battler, &gEnemyParty[gBattlerPartyIndexes[battler]]); if (gSprites[gBattlerSpriteIds[battler]].callback == SpriteCallbackDummy && gBattleSpritesDataPtr->healthBoxesData[battler].finishedShinyMonAnim) { gBattleSpritesDataPtr->healthBoxesData[battler].triedShinyMonAnim = FALSE; gBattleSpritesDataPtr->healthBoxesData[battler].finishedShinyMonAnim = FALSE; FreeSpriteTilesByTag(ANIM_TAG_GOLD_STARS); FreeSpritePaletteByTag(ANIM_TAG_GOLD_STARS); OpponentBufferExecCompleted(battler); } } static void SwitchIn_ShowSubstitute(u32 battler) { if (gSprites[gHealthboxSpriteIds[battler]].callback == SpriteCallbackDummy) { if (gBattleSpritesDataPtr->battlerData[battler].behindSubstitute) InitAndLaunchSpecialAnimation(battler, battler, battler, B_ANIM_MON_TO_SUBSTITUTE); gBattlerControllerFuncs[battler] = SwitchIn_HandleSoundAndEnd; } } static void SwitchIn_HandleSoundAndEnd(u32 battler) { if (!gBattleSpritesDataPtr->healthBoxesData[battler].specialAnimActive && !IsCryPlayingOrClearCrySongs()) { if (gSprites[gBattlerSpriteIds[battler]].callback == SpriteCallbackDummy || gSprites[gBattlerSpriteIds[battler]].callback == SpriteCallbackDummy_2) { m4aMPlayVolumeControl(&gMPlayInfo_BGM, TRACKS_ALL, 0x100); OpponentBufferExecCompleted(battler); } } } static void SwitchIn_ShowHealthbox(u32 battler) { if (gBattleSpritesDataPtr->healthBoxesData[battler].finishedShinyMonAnim && gSprites[gBattlerSpriteIds[battler]].callback == SpriteCallbackDummy) { gBattleSpritesDataPtr->healthBoxesData[battler].triedShinyMonAnim = FALSE; gBattleSpritesDataPtr->healthBoxesData[battler].finishedShinyMonAnim = FALSE; FreeSpriteTilesByTag(ANIM_TAG_GOLD_STARS); FreeSpritePaletteByTag(ANIM_TAG_GOLD_STARS); StartSpriteAnim(&gSprites[gBattlerSpriteIds[battler]], 0); UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], &gEnemyParty[gBattlerPartyIndexes[battler]], HEALTHBOX_ALL); StartHealthboxSlideIn(battler); SetHealthboxSpriteVisible(gHealthboxSpriteIds[battler]); CopyBattleSpriteInvisibility(battler); gBattlerControllerFuncs[battler] = SwitchIn_ShowSubstitute; } } static void SwitchIn_TryShinyAnim(u32 battler) { if (!gBattleSpritesDataPtr->healthBoxesData[battler].triedShinyMonAnim && !gBattleSpritesDataPtr->healthBoxesData[battler].ballAnimActive) TryShinyAnimation(battler, &gEnemyParty[gBattlerPartyIndexes[battler]]); if (gSprites[gBattleControllerData[battler]].callback == SpriteCallbackDummy && !gBattleSpritesDataPtr->healthBoxesData[battler].ballAnimActive) { DestroySprite(&gSprites[gBattleControllerData[battler]]); SetBattlerShadowSpriteCallback(battler, GetMonData(&gEnemyParty[gBattlerPartyIndexes[battler]], MON_DATA_SPECIES)); gBattlerControllerFuncs[battler] = SwitchIn_ShowHealthbox; } } static void OpponentBufferExecCompleted(u32 battler) { gBattlerControllerFuncs[battler] = OpponentBufferRunCommand; if (gBattleTypeFlags & BATTLE_TYPE_LINK) { u8 playerId = GetMultiplayerId(); PrepareBufferDataTransferLink(battler, 2, 4, &playerId); gBattleResources->bufferA[battler][0] = CONTROLLER_TERMINATOR_NOP; } else { gBattleControllerExecFlags &= ~(1u << battler); } } static void OpponentHandleLoadMonSprite(u32 battler) { BtlController_HandleLoadMonSprite(battler, TryShinyAnimAfterMonAnim); } static void OpponentHandleSwitchInAnim(u32 battler) { gBattleStruct->monToSwitchIntoId[battler] = PARTY_SIZE; BtlController_HandleSwitchInAnim(battler, FALSE, SwitchIn_TryShinyAnim); } static u32 OpponentGetTrainerPicId(u32 battlerId) { u32 trainerPicId; if (gBattleTypeFlags & BATTLE_TYPE_SECRET_BASE) { trainerPicId = GetSecretBaseTrainerPicIndex(); } else if (gTrainerBattleOpponent_A == TRAINER_FRONTIER_BRAIN) { trainerPicId = GetFrontierBrainTrainerPicIndex(); } else if (gBattleTypeFlags & BATTLE_TYPE_TRAINER_HILL) { if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) { if (battlerId == 1) trainerPicId = GetTrainerHillTrainerFrontSpriteId(gTrainerBattleOpponent_A); else trainerPicId = GetTrainerHillTrainerFrontSpriteId(gTrainerBattleOpponent_B); } else { trainerPicId = GetTrainerHillTrainerFrontSpriteId(gTrainerBattleOpponent_A); } } else if (gBattleTypeFlags & BATTLE_TYPE_FRONTIER) { if (gBattleTypeFlags & (BATTLE_TYPE_TWO_OPPONENTS | BATTLE_TYPE_TOWER_LINK_MULTI)) { if (battlerId == 1) trainerPicId = GetFrontierTrainerFrontSpriteId(gTrainerBattleOpponent_A); else trainerPicId = GetFrontierTrainerFrontSpriteId(gTrainerBattleOpponent_B); } else { trainerPicId = GetFrontierTrainerFrontSpriteId(gTrainerBattleOpponent_A); } } else if (gBattleTypeFlags & BATTLE_TYPE_EREADER_TRAINER) { trainerPicId = GetEreaderTrainerFrontSpriteId(); } else if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) { if (battlerId != 1) trainerPicId = GetTrainerPicFromId(gTrainerBattleOpponent_B); else trainerPicId = GetTrainerPicFromId(gTrainerBattleOpponent_A); } else { trainerPicId = GetTrainerPicFromId(gTrainerBattleOpponent_A); } return trainerPicId; } static void OpponentHandleDrawTrainerPic(u32 battler) { s16 xPos; u32 trainerPicId = OpponentGetTrainerPicId(battler); if (gBattleTypeFlags & (BATTLE_TYPE_MULTI | BATTLE_TYPE_TWO_OPPONENTS) && !BATTLE_TWO_VS_ONE_OPPONENT) { if ((GetBattlerPosition(battler) & BIT_FLANK) != 0) // second mon xPos = 152; else // first mon xPos = 200; } else { xPos = 176; } BtlController_HandleDrawTrainerPic(battler, trainerPicId, TRUE, xPos, 40, -1); } static void OpponentHandleTrainerSlide(u32 battler) { u32 trainerPicId = OpponentGetTrainerPicId(battler); BtlController_HandleTrainerSlide(battler, trainerPicId); } static void OpponentHandleTrainerSlideBack(u32 battler) { BtlController_HandleTrainerSlideBack(battler, 35, FALSE); } static void OpponentHandleMoveAnimation(u32 battler) { BtlController_HandleMoveAnimation(battler, FALSE); } static void OpponentHandlePrintString(u32 battler) { BtlController_HandlePrintString(battler, FALSE, TRUE); } static void OpponentHandleChooseAction(u32 battler) { AI_TrySwitchOrUseItem(battler); OpponentBufferExecCompleted(battler); } static void OpponentHandleChooseMove(u32 battler) { u8 chosenMoveId; struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct *)(&gBattleResources->bufferA[battler][4]); if (gBattleTypeFlags & (BATTLE_TYPE_TRAINER | BATTLE_TYPE_FIRST_BATTLE | BATTLE_TYPE_SAFARI | BATTLE_TYPE_ROAMER) || IsWildMonSmart()) { if (gBattleTypeFlags & BATTLE_TYPE_PALACE) { BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, ChooseMoveAndTargetInBattlePalace(battler)); } else { chosenMoveId = gBattleStruct->aiMoveOrAction[battler]; gBattlerTarget = gBattleStruct->aiChosenTarget[battler]; switch (chosenMoveId) { case AI_CHOICE_WATCH: BtlController_EmitTwoReturnValues(battler, BUFFER_B, B_ACTION_SAFARI_WATCH_CAREFULLY, 0); break; case AI_CHOICE_FLEE: BtlController_EmitTwoReturnValues(battler, BUFFER_B, B_ACTION_RUN, 0); break; case 6: BtlController_EmitTwoReturnValues(battler, BUFFER_B, 15, gBattlerTarget); break; default: { u16 chosenMove = moveInfo->moves[chosenMoveId]; if (GetBattlerMoveTargetType(battler, chosenMove) & (MOVE_TARGET_USER_OR_SELECTED | MOVE_TARGET_USER)) gBattlerTarget = battler; if (GetBattlerMoveTargetType(battler, chosenMove) & MOVE_TARGET_BOTH) { gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); if (gAbsentBattlerFlags & (1u << gBattlerTarget)) gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT); } // If opponent can and should use a gimmick (considering trainer data), do it if (gBattleStruct->gimmick.usableGimmick[battler] != GIMMICK_NONE && !(gBattleStruct->gimmick.usableGimmick[battler] == GIMMICK_Z_MOVE && !ShouldUseZMove(battler, gBattlerTarget, moveInfo->moves[chosenMoveId]))) { BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (RET_GIMMICK) | (gBattlerTarget << 8)); } else { BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (gBattlerTarget << 8)); } } break; } } OpponentBufferExecCompleted(battler); } else // Wild pokemon - use random move { u16 move; u8 target; do { chosenMoveId = Random() & 3; move = moveInfo->moves[chosenMoveId]; } while (move == MOVE_NONE); if (GetBattlerMoveTargetType(battler, move) & (MOVE_TARGET_USER_OR_SELECTED | MOVE_TARGET_USER)) BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (battler << 8)); else if (IsDoubleBattle()) { do { target = GetBattlerAtPosition(Random() & 2); } while (!CanTargetBattler(battler, target, move)); // Don't bother to loop through table if the move can't attack ally if (B_WILD_NATURAL_ENEMIES == TRUE && !(gMovesInfo[move].target & MOVE_TARGET_BOTH)) { u16 i, speciesAttacker, speciesTarget, isPartnerEnemy = FALSE; static const u16 naturalEnemies[][2] = { // Attacker Target {SPECIES_ZANGOOSE, SPECIES_SEVIPER}, {SPECIES_SEVIPER, SPECIES_ZANGOOSE}, {SPECIES_HEATMOR, SPECIES_DURANT}, {SPECIES_DURANT, SPECIES_HEATMOR}, {SPECIES_SABLEYE, SPECIES_CARBINK}, {SPECIES_MAREANIE, SPECIES_CORSOLA}, }; speciesAttacker = gBattleMons[battler].species; speciesTarget = gBattleMons[GetBattlerAtPosition(BATTLE_PARTNER(battler))].species; for (i = 0; i < ARRAY_COUNT(naturalEnemies); i++) { if (speciesAttacker == naturalEnemies[i][0] && speciesTarget == naturalEnemies[i][1]) { isPartnerEnemy = TRUE; break; } } if (isPartnerEnemy && CanTargetBattler(battler, target, move)) BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (GetBattlerAtPosition(BATTLE_PARTNER(battler)) << 8)); else BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (target << 8)); } else { BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (target << 8)); } } else BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (GetBattlerAtPosition(B_POSITION_PLAYER_LEFT) << 8)); OpponentBufferExecCompleted(battler); } } static void OpponentHandleChooseItem(u32 battler) { BtlController_EmitOneReturnValue(battler, BUFFER_B, gBattleStruct->chosenItem[battler]); OpponentBufferExecCompleted(battler); } static inline bool32 IsAcePokemon(u32 chosenMonId, u32 pokemonInBattle, u32 battler) { return AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_ACE_POKEMON && (chosenMonId == CalculateEnemyPartyCountInSide(battler) - 1) && CountAIAliveNonEggMonsExcept(PARTY_SIZE) != pokemonInBattle; } static inline bool32 IsDoubleAcePokemon(u32 chosenMonId, u32 pokemonInBattle, u32 battler) { return AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_DOUBLE_ACE_POKEMON && (chosenMonId == CalculateEnemyPartyCountInSide(battler) - 1) && (chosenMonId == CalculateEnemyPartyCountInSide(battler) - 2) && CountAIAliveNonEggMonsExcept(PARTY_SIZE) != pokemonInBattle && CountAIAliveNonEggMonsExcept(PARTY_SIZE-1) != pokemonInBattle; } static void OpponentHandleChoosePokemon(u32 battler) { s32 chosenMonId; s32 pokemonInBattle = 1; // Choosing Revival Blessing target if ((gBattleResources->bufferA[battler][1] & 0xF) == PARTY_ACTION_CHOOSE_FAINTED_MON) { chosenMonId = gSelectedMonPartyId = GetFirstFaintedPartyIndex(battler); } // Switching out else if (gBattleStruct->AI_monToSwitchIntoId[battler] == PARTY_SIZE) { chosenMonId = GetMostSuitableMonToSwitchInto(battler, TRUE); if (chosenMonId == PARTY_SIZE) { s32 battler1, battler2, firstId, lastId; if (!IsDoubleBattle()) { battler2 = battler1 = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); } else { battler1 = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); battler2 = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT); pokemonInBattle = 2; } GetAIPartyIndexes(battler, &firstId, &lastId); for (chosenMonId = (lastId-1); chosenMonId >= firstId; chosenMonId--) { if (!IsValidForBattle(&gEnemyParty[chosenMonId]) || chosenMonId == gBattlerPartyIndexes[battler1] || chosenMonId == gBattlerPartyIndexes[battler2]) continue; if (!IsAcePokemon(chosenMonId, pokemonInBattle, battler) && !IsDoubleAcePokemon(chosenMonId, pokemonInBattle, battler)) break; } } gBattleStruct->monToSwitchIntoId[battler] = chosenMonId; } else { chosenMonId = gBattleStruct->AI_monToSwitchIntoId[battler]; gBattleStruct->AI_monToSwitchIntoId[battler] = PARTY_SIZE; gBattleStruct->monToSwitchIntoId[battler] = chosenMonId; } #if TESTING TestRunner_Battle_CheckSwitch(battler, chosenMonId); #endif // TESTING BtlController_EmitChosenMonReturnValue(battler, BUFFER_B, chosenMonId, NULL); OpponentBufferExecCompleted(battler); } static u8 CountAIAliveNonEggMonsExcept(u8 slotToIgnore) { u16 i, count; for (i = 0, count = 0; i < PARTY_SIZE; i++) { if (i != slotToIgnore && IsValidForBattle(&gEnemyParty[i])) { count++; } } return count; } static void OpponentHandleHealthBarUpdate(u32 battler) { BtlController_HandleHealthBarUpdate(battler, FALSE); } static void OpponentHandleIntroTrainerBallThrow(u32 battler) { BtlController_HandleIntroTrainerBallThrow(battler, 0, NULL, 0, Intro_TryShinyAnimShowHealthbox); } static void OpponentHandleDrawPartyStatusSummary(u32 battler) { BtlController_HandleDrawPartyStatusSummary(battler, B_SIDE_OPPONENT, TRUE); } static void OpponentHandleBattleAnimation(u32 battler) { BtlController_HandleBattleAnimation(battler, FALSE, FALSE); } static void OpponentHandleEndLinkBattle(u32 battler) { if (gBattleTypeFlags & BATTLE_TYPE_LINK && !(gBattleTypeFlags & BATTLE_TYPE_IS_MASTER)) { gMain.inBattle = FALSE; gMain.callback1 = gPreBattleCallback1; SetMainCallback2(gMain.savedCallback); } OpponentBufferExecCompleted(battler); }