diff --git a/include/battle_ai_main.h b/include/battle_ai_main.h index c98cedbd9..66c614f78 100644 --- a/include/battle_ai_main.h +++ b/include/battle_ai_main.h @@ -3,10 +3,9 @@ // return values for BattleAI_ChooseMoveOrAction // 0 - 3 are move idx -#define AI_CHOICE_FLEE 4 -#define AI_CHOICE_WATCH 5 -#define AI_CHOICE_SWITCH 7 -#define AI_CHOICE_USE_ITEM 8 +#define AI_CHOICE_FLEE 4 +#define AI_CHOICE_WATCH 5 +#define AI_CHOICE_SWITCH 7 #define RETURN_SCORE_PLUS(val) \ { \ diff --git a/include/battle_ai_switch_items.h b/include/battle_ai_switch_items.h index 6a8042cc9..70dc41b34 100644 --- a/include/battle_ai_switch_items.h +++ b/include/battle_ai_switch_items.h @@ -32,7 +32,7 @@ enum { }; void GetAIPartyIndexes(u32 battlerId, s32 *firstId, s32 *lastId); -u8 AI_TrySwitchOrUseItem(u8 currAction); +void AI_TrySwitchOrUseItem(void); u8 GetMostSuitableMonToSwitchInto(void); bool32 ShouldSwitch(void); diff --git a/include/battle_main.h b/include/battle_main.h index 184786ea5..bf95ca156 100644 --- a/include/battle_main.h +++ b/include/battle_main.h @@ -21,8 +21,6 @@ struct UnknownPokemonStruct4 /*0x1D*/ u8 language; }; -struct ChooseMoveStruct; - #define TYPE_NAME_LENGTH 6 #define ABILITY_NAME_LENGTH 12 @@ -72,7 +70,6 @@ void RunBattleScriptCommands(void); bool8 TryRunFromBattle(u8 battlerId); void SpecialStatusesClear(void); void SetTypeBeforeUsingMove(u16 move, u8 battlerAtk); -void FillChooseMoveStruct(struct ChooseMoveStruct *moveInfo); extern struct UnknownPokemonStruct4 gMultiPartnerParty[MULTI_PARTY_SIZE]; diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 86e5f4049..25250c480 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -209,10 +209,6 @@ u8 BattleAI_ChooseMoveOrAction(void) ret = ChooseMoveOrAction_Singles(); else ret = ChooseMoveOrAction_Doubles(); - - AI_THINKING_STRUCT->movesetIndex = ret; - AI_THINKING_STRUCT->moveConsidered = gBattleMons[sBattler_AI].moves[AI_THINKING_STRUCT->movesetIndex]; - ret = AI_TrySwitchOrUseItem(ret); // Clear protect structures, some flags may be set during AI calcs // e.g. pranksterElevated from GetMovePriority @@ -282,6 +278,48 @@ static u8 ChooseMoveOrAction_Singles(void) return AI_CHOICE_WATCH; gActiveBattler = sBattler_AI; + + // If can switch. + if (CountUsablePartyMons(sBattler_AI) > 0 + && !IsAbilityPreventingEscape(sBattler_AI) + && !(gBattleMons[gActiveBattler].status2 & (STATUS2_WRAPPED | STATUS2_ESCAPE_PREVENTION)) + && !(gStatuses3[gActiveBattler] & STATUS3_ROOTED) + && !(gBattleTypeFlags & (BATTLE_TYPE_ARENA | BATTLE_TYPE_PALACE)) + && AI_THINKING_STRUCT->aiFlags & (AI_FLAG_CHECK_VIABILITY | AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_PREFER_BATON_PASS)) + { + // Consider switching if all moves are worthless to use. + if (GetTotalBaseStat(gBattleMons[sBattler_AI].species) >= 310 // Mon is not weak. + && gBattleMons[sBattler_AI].hp >= gBattleMons[sBattler_AI].maxHP / 2) + { + s32 cap = AI_THINKING_STRUCT->aiFlags & (AI_FLAG_CHECK_VIABILITY) ? 95 : 93; + for (i = 0; i < MAX_MON_MOVES; i++) + { + if (AI_THINKING_STRUCT->score[i] > cap) + break; + } + + if (i == MAX_MON_MOVES && GetMostSuitableMonToSwitchInto() != PARTY_SIZE) + { + AI_THINKING_STRUCT->switchMon = TRUE; + return AI_CHOICE_SWITCH; + } + } + + // Consider switching if your mon with truant is bodied by Protect spam. + // Or is using a double turn semi invulnerable move(such as Fly) and is faster. + if (GetBattlerAbility(sBattler_AI) == ABILITY_TRUANT + && IsTruantMonVulnerable(sBattler_AI, gBattlerTarget) + && gDisableStructs[sBattler_AI].truantCounter + && gBattleMons[sBattler_AI].hp >= gBattleMons[sBattler_AI].maxHP / 2) + { + if (GetMostSuitableMonToSwitchInto() != PARTY_SIZE) + { + AI_THINKING_STRUCT->switchMon = TRUE; + return AI_CHOICE_SWITCH; + } + } + } + numOfBestMoves = 1; currentMoveArray[0] = AI_THINKING_STRUCT->score[0]; consideredMoveArray[0] = 0; diff --git a/src/battle_ai_switch_items.c b/src/battle_ai_switch_items.c index f40677bd9..a3a1af8da 100644 --- a/src/battle_ai_switch_items.c +++ b/src/battle_ai_switch_items.c @@ -18,77 +18,8 @@ static bool8 HasSuperEffectiveMoveAgainstOpponents(bool8 noRng); static bool8 FindMonWithFlagsAndSuperEffective(u16 flags, u8 moduloPercent); static bool8 ShouldUseItem(void); -static bool32 AI_ShouldHeal(u8 healAmount); // Functions -u8 AI_TrySwitchOrUseItem(u8 currAction) -{ - struct Pokemon *party; - u8 battlerIn1, battlerIn2; - s32 firstId; - s32 lastId; // + 1 - u8 battlerIdentity = GetBattlerPosition(gActiveBattler); - - if (!(gBattleTypeFlags & BATTLE_TYPE_TRAINER)) - return currAction; - - if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER) - party = gPlayerParty; // Player's partner - else - party = gEnemyParty; // Enemy trainer - - // Switching logic - if (ShouldSwitch()) - { - if (*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) == PARTY_SIZE) - { - s32 monToSwitchId = GetMostSuitableMonToSwitchInto(); - if (monToSwitchId == PARTY_SIZE) - { - if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE)) - { - battlerIn1 = GetBattlerAtPosition(battlerIdentity); - battlerIn2 = battlerIn1; - } - else - { - battlerIn1 = GetBattlerAtPosition(battlerIdentity); - battlerIn2 = GetBattlerAtPosition(battlerIdentity ^ BIT_FLANK); - } - - GetAIPartyIndexes(gActiveBattler, &firstId, &lastId); - - for (monToSwitchId = firstId; monToSwitchId < lastId; monToSwitchId++) - { - if (GetMonData(&party[monToSwitchId], MON_DATA_HP) == 0) - continue; - if (monToSwitchId == gBattlerPartyIndexes[battlerIn1]) - continue; - if (monToSwitchId == gBattlerPartyIndexes[battlerIn2]) - continue; - if (monToSwitchId == *(gBattleStruct->monToSwitchIntoId + battlerIn1)) - continue; - if (monToSwitchId == *(gBattleStruct->monToSwitchIntoId + battlerIn2)) - continue; - - break; - } - } - - *(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) = monToSwitchId; - } - - *(gBattleStruct->monToSwitchIntoId + gActiveBattler) = *(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler); - return AI_CHOICE_SWITCH; - } - - // Item Logic - if (ShouldUseItem()) - return AI_CHOICE_USE_ITEM; - - return currAction; -} - void GetAIPartyIndexes(u32 battlerId, s32 *firstId, s32 *lastId) { if (BATTLE_TWO_VS_ONE_OPPONENT && (battlerId & BIT_SIDE) == B_SIDE_OPPONENT) @@ -110,52 +41,11 @@ void GetAIPartyIndexes(u32 battlerId, s32 *firstId, s32 *lastId) static bool8 ShouldSwitchIfAllBadMoves(void) { - u32 i; - - if (AI_THINKING_STRUCT->aiFlags & (AI_FLAG_CHECK_VIABILITY | AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_PREFER_BATON_PASS)) + if (gBattleResources->ai->switchMon) { - if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) - { - // TODO double battle bad move switching logic - } - else - { - // Single battle. gActiveBattler is the enemy's battler id - if (GetTotalBaseStat(gBattleMons[gActiveBattler].species) >= 310 // Mon is not weak. - && gBattleMons[gActiveBattler].hp >= gBattleMons[gActiveBattler].maxHP / 2) - { - s32 cap = AI_THINKING_STRUCT->aiFlags & (AI_FLAG_CHECK_VIABILITY) ? 95 : 93; - for (i = 0; i < MAX_MON_MOVES; i++) - { - if (AI_THINKING_STRUCT->score[i] > cap) - break; - } - - if (i == MAX_MON_MOVES && GetMostSuitableMonToSwitchInto() != PARTY_SIZE) - { - AI_THINKING_STRUCT->switchMon = TRUE; - } - } - - // Consider switching if your mon with truant is bodied by Protect spam. - // Or is using a double turn semi invulnerable move(such as Fly) and is faster. - if (GetBattlerAbility(gActiveBattler) == ABILITY_TRUANT - && IsTruantMonVulnerable(gActiveBattler, gBattlerTarget) - && gDisableStructs[gActiveBattler].truantCounter - && gBattleMons[gActiveBattler].hp >= gBattleMons[gActiveBattler].maxHP / 2) - { - if (GetMostSuitableMonToSwitchInto() != PARTY_SIZE) - { - AI_THINKING_STRUCT->switchMon = TRUE; - } - } - } - } - - if (AI_THINKING_STRUCT->switchMon) - { - AI_THINKING_STRUCT->switchMon = FALSE; + gBattleResources->ai->switchMon = 0; *(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) = PARTY_SIZE; + BtlController_EmitTwoReturnValues(1, B_ACTION_SWITCH, 0); return TRUE; } else @@ -170,6 +60,7 @@ static bool8 ShouldSwitchIfPerishSong(void) && gDisableStructs[gActiveBattler].perishSongTimer == 0) { *(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) = PARTY_SIZE; + BtlController_EmitTwoReturnValues(1, B_ACTION_SWITCH, 0); return TRUE; } else @@ -236,6 +127,7 @@ static bool8 ShouldSwitchIfWonderGuard(void) { // We found a mon. *(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) = i; + BtlController_EmitTwoReturnValues(1, B_ACTION_SWITCH, 0); return TRUE; } } @@ -326,6 +218,7 @@ static bool8 FindMonThatAbsorbsOpponentsMove(void) { // we found a mon. *(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) = i; + BtlController_EmitTwoReturnValues(1, B_ACTION_SWITCH, 0); return TRUE; } } @@ -345,11 +238,13 @@ static bool8 ShouldSwitchIfNaturalCure(void) if ((gLastLandedMoves[gActiveBattler] == 0 || gLastLandedMoves[gActiveBattler] == 0xFFFF) && Random() & 1) { *(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) = PARTY_SIZE; + BtlController_EmitTwoReturnValues(1, B_ACTION_SWITCH, 0); return TRUE; } else if (gBattleMoves[gLastLandedMoves[gActiveBattler]].power == 0 && Random() & 1) { *(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) = PARTY_SIZE; + BtlController_EmitTwoReturnValues(1, B_ACTION_SWITCH, 0); return TRUE; } @@ -361,6 +256,7 @@ static bool8 ShouldSwitchIfNaturalCure(void) if (Random() & 1) { *(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) = PARTY_SIZE; + BtlController_EmitTwoReturnValues(1, B_ACTION_SWITCH, 0); return TRUE; } @@ -541,10 +437,6 @@ bool32 ShouldSwitch(void) return FALSE; availableToSwitch = 0; - AI_THINKING_STRUCT->switchMon = FALSE; - - if (CountUsablePartyMons(gActiveBattler) == 0) // No pokemon to switch to - return FALSE; if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) { @@ -610,6 +502,74 @@ bool32 ShouldSwitch(void) return FALSE; } +void AI_TrySwitchOrUseItem(void) +{ + struct Pokemon *party; + u8 battlerIn1, battlerIn2; + s32 firstId; + s32 lastId; // + 1 + u8 battlerIdentity = GetBattlerPosition(gActiveBattler); + + if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER) + party = gPlayerParty; + else + party = gEnemyParty; + + if (gBattleTypeFlags & BATTLE_TYPE_TRAINER) + { + if (ShouldSwitch()) + { + if (*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) == PARTY_SIZE) + { + s32 monToSwitchId = GetMostSuitableMonToSwitchInto(); + if (monToSwitchId == PARTY_SIZE) + { + if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE)) + { + battlerIn1 = GetBattlerAtPosition(battlerIdentity); + battlerIn2 = battlerIn1; + } + else + { + battlerIn1 = GetBattlerAtPosition(battlerIdentity); + battlerIn2 = GetBattlerAtPosition(battlerIdentity ^ BIT_FLANK); + } + + GetAIPartyIndexes(gActiveBattler, &firstId, &lastId); + + for (monToSwitchId = firstId; monToSwitchId < lastId; monToSwitchId++) + { + if (GetMonData(&party[monToSwitchId], MON_DATA_HP) == 0) + continue; + if (monToSwitchId == gBattlerPartyIndexes[battlerIn1]) + continue; + if (monToSwitchId == gBattlerPartyIndexes[battlerIn2]) + continue; + if (monToSwitchId == *(gBattleStruct->monToSwitchIntoId + battlerIn1)) + continue; + if (monToSwitchId == *(gBattleStruct->monToSwitchIntoId + battlerIn2)) + continue; + + break; + } + } + + *(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) = monToSwitchId; + } + + *(gBattleStruct->monToSwitchIntoId + gActiveBattler) = *(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler); + return; + } + else if (ShouldUseItem()) + { + return; + } + } + + // AI has decided it shouldn't switch or use an item, so it will now choose a move to use + BtlController_EmitTwoReturnValues(1, B_ACTION_USE_MOVE, (gActiveBattler ^ BIT_SIDE) << 8); +} + // 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. static u32 GetBestMonBatonPass(struct Pokemon *party, int firstId, int lastId, u8 invalidMons, int aliveCount) @@ -882,10 +842,21 @@ static bool8 ShouldUseItem(void) switch (*(gBattleStruct->AI_itemType + gActiveBattler / 2)) { case AI_ITEM_FULL_RESTORE: - shouldUse = AI_ShouldHeal(0); + if (gBattleMons[gActiveBattler].hp >= gBattleMons[gActiveBattler].maxHP / 4) + break; + if (gBattleMons[gActiveBattler].hp == 0) + break; + shouldUse = TRUE; break; case AI_ITEM_HEAL_HP: - shouldUse = AI_ShouldHeal(itemEffects[GetItemEffectParamOffset(item, 4, 4)]); + paramOffset = GetItemEffectParamOffset(item, 4, 4); + if (paramOffset == 0) + break; + if (gBattleMons[gActiveBattler].hp == 0) + break; + if (gBattleMons[gActiveBattler].hp < gBattleMons[gActiveBattler].maxHP / 4 + || gBattleMons[gActiveBattler].maxHP - gBattleMons[gActiveBattler].hp > itemEffects[paramOffset]) + shouldUse = TRUE; break; case AI_ITEM_CURE_CONDITION: *(gBattleStruct->AI_itemFlags + gActiveBattler / 2) = 0; @@ -967,6 +938,7 @@ static bool8 ShouldUseItem(void) if (shouldUse) { + BtlController_EmitTwoReturnValues(1, B_ACTION_USE_ITEM, 0); *(gBattleStruct->chosenItem + (gActiveBattler / 2) * 2) = item; gBattleResources->battleHistory->trainerItems[i] = 0; return shouldUse; @@ -975,41 +947,3 @@ static bool8 ShouldUseItem(void) return FALSE; } - -static bool32 AI_ShouldHeal(u8 healAmount) -{ - bool32 shouldHeal = FALSE; - u32 i; - - if (gBattleMons[gActiveBattler].hp < gBattleMons[gActiveBattler].maxHP / 4 - || gBattleMons[gActiveBattler].hp == 0 - || (healAmount != 0 && gBattleMons[gActiveBattler].maxHP - gBattleMons[gActiveBattler].hp > healAmount)) - { - // We have low enough HP to consider healing - shouldHeal = TRUE; - - // Check special cases to NOT heal - for (i = 0; i < gBattlersCount; i++) - { - if (GetBattlerSide(i) == B_SIDE_PLAYER) - { - if (CanTargetFaintAiWithMod(i, gActiveBattler, healAmount, 0)) - { - // Target is expected to faint us even after we heal. So why bother. - shouldHeal = FALSE; - break; - } - - // AI_THINKING_STRUCT->movesetIndex is the array index of the AI's chosen move - if (CanIndexMoveFaintTarget(gActiveBattler, i, AI_THINKING_STRUCT->movesetIndex, 0) && AI_WhoStrikesFirst(gActiveBattler, i) == AI_IS_FASTER) - { - // We can faint the target and move first -> don't heal - shouldHeal = FALSE; - break; - } - } - } - } - - return shouldHeal; -} diff --git a/src/battle_controller_opponent.c b/src/battle_controller_opponent.c index 5975ab2a1..f4982b804 100644 --- a/src/battle_controller_opponent.c +++ b/src/battle_controller_opponent.c @@ -1545,70 +1545,7 @@ static void OpponentHandlePrintSelectionString(void) static void OpponentHandleChooseAction(void) { - if (gBattleTypeFlags & BATTLE_TYPE_PALACE) - { - BtlController_EmitTwoReturnValues(1, B_ACTION_USE_MOVE, ChooseMoveAndTargetInBattlePalace()); - } - else - { - u8 chosenMoveId; - struct ChooseMoveStruct moveInfo; - - FillChooseMoveStruct(&moveInfo); - if (gBattleTypeFlags & (BATTLE_TYPE_TRAINER | BATTLE_TYPE_FIRST_BATTLE | BATTLE_TYPE_SAFARI | BATTLE_TYPE_ROAMER)) - { - BattleAI_SetupAIData(0xF); - chosenMoveId = BattleAI_ChooseMoveOrAction(); - switch (chosenMoveId) - { - case AI_CHOICE_USE_ITEM: - BtlController_EmitTwoReturnValues(1, B_ACTION_USE_ITEM, 0); - break; - case AI_CHOICE_SWITCH: - BtlController_EmitTwoReturnValues(1, B_ACTION_SWITCH, 0); - break; - case AI_CHOICE_WATCH: - BtlController_EmitTwoReturnValues(1, B_ACTION_SAFARI_WATCH_CAREFULLY, 0); - break; - case AI_CHOICE_FLEE: - BtlController_EmitTwoReturnValues(1, B_ACTION_RUN, 0); - break; - default: - if (gBattleMoves[moveInfo.moves[chosenMoveId]].target & (MOVE_TARGET_USER_OR_SELECTED | MOVE_TARGET_USER)) - gBattlerTarget = gActiveBattler; - if (gBattleMoves[moveInfo.moves[chosenMoveId]].target & MOVE_TARGET_BOTH) - { - gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); - if (gAbsentBattlerFlags & gBitTable[gBattlerTarget]) - gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT); - } - - if (CanMegaEvolve(gActiveBattler)) { // If opponent can mega evolve, do it. - BtlController_EmitTwoReturnValues(1, B_ACTION_USE_MOVE, (chosenMoveId) | (RET_MEGA_EVOLUTION) | (gBattlerTarget << 8)); - } else { - BtlController_EmitTwoReturnValues(1, B_ACTION_USE_MOVE, (chosenMoveId) | (gBattlerTarget << 8)); - } - break; - } - } - else // Wild pokemon - use random move - { - u16 move; - do - { - chosenMoveId = Random() & 3; - move = moveInfo.moves[chosenMoveId]; - } while (move == MOVE_NONE); - - if (gBattleMoves[move].target & (MOVE_TARGET_USER_OR_SELECTED | MOVE_TARGET_USER)) - BtlController_EmitTwoReturnValues(1, B_ACTION_USE_MOVE, (chosenMoveId) | (gActiveBattler << 8)); - else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) - BtlController_EmitTwoReturnValues(1, B_ACTION_USE_MOVE, (chosenMoveId) | (GetBattlerAtPosition(Random() & 2) << 8)); - else - BtlController_EmitTwoReturnValues(1, B_ACTION_USE_MOVE, (chosenMoveId) | (GetBattlerAtPosition(B_POSITION_PLAYER_LEFT) << 8)); - } - } - + AI_TrySwitchOrUseItem(); // TODO consider move choice first OpponentBufferExecCompleted(); } @@ -1619,9 +1556,74 @@ static void OpponentHandleYesNoBox(void) static void OpponentHandleChooseMove(void) { - u8 *bufferB = gBattleResources->bufferB[gActiveBattler]; - BtlController_EmitTwoReturnValues(1, 10, bufferB[2] | (bufferB[3] << 8)); - OpponentBufferExecCompleted(); + if (gBattleTypeFlags & BATTLE_TYPE_PALACE) + { + BtlController_EmitTwoReturnValues(1, 10, ChooseMoveAndTargetInBattlePalace()); + OpponentBufferExecCompleted(); + } + else + { + u8 chosenMoveId; + struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct*)(&gBattleResources->bufferA[gActiveBattler][4]); + + if (gBattleTypeFlags & (BATTLE_TYPE_TRAINER | BATTLE_TYPE_FIRST_BATTLE | BATTLE_TYPE_SAFARI | BATTLE_TYPE_ROAMER)) + { + BattleAI_SetupAIData(0xF); + chosenMoveId = BattleAI_ChooseMoveOrAction(); + + switch (chosenMoveId) + { + case AI_CHOICE_WATCH: + BtlController_EmitTwoReturnValues(1, B_ACTION_SAFARI_WATCH_CAREFULLY, 0); + break; + case AI_CHOICE_FLEE: + BtlController_EmitTwoReturnValues(1, B_ACTION_RUN, 0); + break; + case AI_CHOICE_SWITCH: + BtlController_EmitTwoReturnValues(1, 10, 0xFFFF); + break; + case 6: + BtlController_EmitTwoReturnValues(1, 15, gBattlerTarget); + break; + default: + if (gBattleMoves[moveInfo->moves[chosenMoveId]].target & (MOVE_TARGET_USER_OR_SELECTED | MOVE_TARGET_USER)) + gBattlerTarget = gActiveBattler; + if (gBattleMoves[moveInfo->moves[chosenMoveId]].target & MOVE_TARGET_BOTH) + { + gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); + if (gAbsentBattlerFlags & gBitTable[gBattlerTarget]) + gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT); + } + + // If opponent can mega evolve, do it. + if (CanMegaEvolve(gActiveBattler)) + BtlController_EmitTwoReturnValues(1, 10, (chosenMoveId) | (RET_MEGA_EVOLUTION) | (gBattlerTarget << 8)); + else + BtlController_EmitTwoReturnValues(1, 10, (chosenMoveId) | (gBattlerTarget << 8)); + break; + } + + OpponentBufferExecCompleted(); + } + else // Wild pokemon - use random move + { + u16 move; + do + { + chosenMoveId = Random() & 3; + move = moveInfo->moves[chosenMoveId]; + } while (move == MOVE_NONE); + + if (gBattleMoves[move].target & (MOVE_TARGET_USER_OR_SELECTED | MOVE_TARGET_USER)) + BtlController_EmitTwoReturnValues(1, 10, (chosenMoveId) | (gActiveBattler << 8)); + else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) + BtlController_EmitTwoReturnValues(1, 10, (chosenMoveId) | (GetBattlerAtPosition(Random() & 2) << 8)); + else + BtlController_EmitTwoReturnValues(1, 10, (chosenMoveId) | (GetBattlerAtPosition(B_POSITION_PLAYER_LEFT) << 8)); + + OpponentBufferExecCompleted(); + } + } } static void OpponentHandleChooseItem(void) diff --git a/src/battle_controller_player_partner.c b/src/battle_controller_player_partner.c index b4052402e..2b4187a31 100644 --- a/src/battle_controller_player_partner.c +++ b/src/battle_controller_player_partner.c @@ -1509,37 +1509,7 @@ static void PlayerPartnerHandlePrintSelectionString(void) static void PlayerPartnerHandleChooseAction(void) { - u8 chosenMoveId; - struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct*)(&gBattleResources->bufferA[gActiveBattler][4]); - - BattleAI_SetupAIData(0xF); - chosenMoveId = BattleAI_ChooseMoveOrAction(); - - switch (chosenMoveId) - { - case AI_CHOICE_USE_ITEM: - BtlController_EmitTwoReturnValues(1, B_ACTION_USE_ITEM, 0); - break; - case AI_CHOICE_SWITCH: - BtlController_EmitTwoReturnValues(1, B_ACTION_SWITCH, 0); - break; - default: - if (gBattleMoves[moveInfo->moves[chosenMoveId]].target & (MOVE_TARGET_USER | MOVE_TARGET_USER_OR_SELECTED)) - gBattlerTarget = gActiveBattler; - if (gBattleMoves[moveInfo->moves[chosenMoveId]].target & MOVE_TARGET_BOTH) - { - gBattlerTarget = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); - if (gAbsentBattlerFlags & gBitTable[gBattlerTarget]) - gBattlerTarget = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT); - } - - if (CanMegaEvolve(gActiveBattler)) // If partner can mega evolve, do it. - BtlController_EmitTwoReturnValues(1, B_ACTION_USE_MOVE, (chosenMoveId) | (RET_MEGA_EVOLUTION) | (gBattlerTarget << 8)); - else - BtlController_EmitTwoReturnValues(1, B_ACTION_USE_MOVE, (chosenMoveId) | (gBattlerTarget << 8)); - break; - } - + AI_TrySwitchOrUseItem(); PlayerPartnerBufferExecCompleted(); } @@ -1550,6 +1520,26 @@ static void PlayerPartnerHandleYesNoBox(void) static void PlayerPartnerHandleChooseMove(void) { + u8 chosenMoveId; + struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct*)(&gBattleResources->bufferA[gActiveBattler][4]); + + BattleAI_SetupAIData(0xF); + chosenMoveId = BattleAI_ChooseMoveOrAction(); + + if (gBattleMoves[moveInfo->moves[chosenMoveId]].target & (MOVE_TARGET_USER | MOVE_TARGET_USER_OR_SELECTED)) + gBattlerTarget = gActiveBattler; + if (gBattleMoves[moveInfo->moves[chosenMoveId]].target & MOVE_TARGET_BOTH) + { + gBattlerTarget = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); + if (gAbsentBattlerFlags & gBitTable[gBattlerTarget]) + gBattlerTarget = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT); + } + + if (CanMegaEvolve(gActiveBattler)) // If partner can mega evolve, do it. + BtlController_EmitTwoReturnValues(1, 10, (chosenMoveId) | (RET_MEGA_EVOLUTION) | (gBattlerTarget << 8)); + else + BtlController_EmitTwoReturnValues(1, 10, (chosenMoveId) | (gBattlerTarget << 8)); + PlayerPartnerBufferExecCompleted(); } diff --git a/src/battle_main.c b/src/battle_main.c index 84f66d6de..766736048 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -3877,7 +3877,22 @@ static void HandleTurnActionSelectionState(void) { struct ChooseMoveStruct moveInfo; - FillChooseMoveStruct(&moveInfo); + moveInfo.mega = gBattleStruct->mega; + moveInfo.species = gBattleMons[gActiveBattler].species; + moveInfo.monType1 = gBattleMons[gActiveBattler].type1; + moveInfo.monType2 = gBattleMons[gActiveBattler].type2; + moveInfo.monType3 = gBattleMons[gActiveBattler].type3; + + for (i = 0; i < MAX_MON_MOVES; i++) + { + moveInfo.moves[i] = gBattleMons[gActiveBattler].moves[i]; + moveInfo.currentPp[i] = gBattleMons[gActiveBattler].pp[i]; + moveInfo.maxPp[i] = CalculatePPWithBonus( + gBattleMons[gActiveBattler].moves[i], + gBattleMons[gActiveBattler].ppBonuses, + i); + } + BtlController_EmitChooseMove(0, (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) != 0, FALSE, &moveInfo); MarkBattlerForControllerExec(gActiveBattler); } @@ -5302,24 +5317,3 @@ void SetTotemBoost(void) } } } - -void FillChooseMoveStruct(struct ChooseMoveStruct * moveInfo) -{ - int i; - - moveInfo->mega = gBattleStruct->mega; - moveInfo->species = gBattleMons[gActiveBattler].species; - moveInfo->monType1 = gBattleMons[gActiveBattler].type1; - moveInfo->monType2 = gBattleMons[gActiveBattler].type2; - moveInfo->monType3 = gBattleMons[gActiveBattler].type3; - - for (i = 0; i < MAX_MON_MOVES; i++) - { - moveInfo->moves[i] = gBattleMons[gActiveBattler].moves[i]; - moveInfo->currentPp[i] = gBattleMons[gActiveBattler].pp[i]; - moveInfo->maxPp[i] = CalculatePPWithBonus( - gBattleMons[gActiveBattler].moves[i], - gBattleMons[gActiveBattler].ppBonuses, - i); - } -}