diff --git a/asm/macros/event.inc b/asm/macros/event.inc index 78d3037aeb..d2d8f8ca6f 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -2018,3 +2018,10 @@ setvar VAR_0x8002, \tryMultiple special TrySpecialOverworldEvo .endm + + .macro ai_vs_ai_battle trainer1:req, trainer2:req + setflag B_FLAG_AI_VS_AI_BATTLE + setvar VAR_0x8004, \trainer1 + callnative CreateTrainerPartyForPlayer + trainerbattle_no_intro \trainer2, NULL + .endm diff --git a/include/battle_ai_util.h b/include/battle_ai_util.h index fd55a99f73..5f077fe0cc 100644 --- a/include/battle_ai_util.h +++ b/include/battle_ai_util.h @@ -9,6 +9,7 @@ bool32 AI_RandLessThan(u8 val); void RecordLastUsedMoveByTarget(void); +bool32 IsAiVsAiBattle(void); bool32 BattlerHasAi(u32 battlerId); bool32 IsAiBattlerAware(u32 battlerId); void ClearBattlerMoveHistory(u8 battlerId); diff --git a/include/config/battle.h b/include/config/battle.h index ae64647b60..403da76e43 100644 --- a/include/config/battle.h +++ b/include/config/battle.h @@ -156,6 +156,7 @@ #define B_SMART_WILD_AI_FLAG 0 // If not 0, you can set this flag in a script to enable smart wild pokemon #define B_FLAG_NO_BAG_USE 0 // If this flag is set, the ability to use the bag in battle is disabled. #define B_FLAG_NO_CATCHING 0 // If this flag is set, the ability to catch wild Pokémon is disabled. +#define B_FLAG_AI_VS_AI_BATTLE 0 // If this flag is set, the player's mons will be controlled by the ai next battles. // Var Settings // To use the following features in scripting, replace the 0s with the var ID you're assigning it to. diff --git a/src/battle_ai_switch_items.c b/src/battle_ai_switch_items.c index faaeff12b1..8808784d2c 100644 --- a/src/battle_ai_switch_items.c +++ b/src/battle_ai_switch_items.c @@ -1030,6 +1030,9 @@ static bool8 ShouldUseItem(void) u8 validMons = 0; bool8 shouldUse = FALSE; + if (IsAiVsAiBattle()) + return FALSE; + // If teaming up with player and Pokemon is on the right, or Pokemon is currently held by Sky Drop if ((gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER && GetBattlerPosition(gActiveBattler) == B_POSITION_PLAYER_RIGHT) || gStatuses3[gActiveBattler] & STATUS3_SKY_DROPPED) diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index c6788cf8d7..71fb0f1088 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -8,6 +8,7 @@ #include "battle_ai_switch_items.h" #include "battle_factory.h" #include "battle_setup.h" +#include "event_data.h" #include "data.h" #include "item.h" #include "pokemon.h" @@ -440,17 +441,27 @@ void RecordLastUsedMoveByTarget(void) RecordKnownMove(gBattlerTarget, gLastMoves[gBattlerTarget]); } +bool32 IsAiVsAiBattle(void) +{ + return (B_FLAG_AI_VS_AI_BATTLE && FlagGet(B_FLAG_AI_VS_AI_BATTLE)); +} + bool32 BattlerHasAi(u32 battlerId) { switch (GetBattlerPosition(battlerId)) { case B_POSITION_PLAYER_LEFT: + if (IsAiVsAiBattle()) + return TRUE; default: return FALSE; case B_POSITION_OPPONENT_LEFT: return TRUE; case B_POSITION_PLAYER_RIGHT: - return ((gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER) != 0); + if ((gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER) || IsAiVsAiBattle()) + return TRUE; + else + return FALSE; case B_POSITION_OPPONENT_RIGHT: return TRUE; } diff --git a/src/battle_controller_player_partner.c b/src/battle_controller_player_partner.c index 0b2cb55d6a..5a00d8cbde 100644 --- a/src/battle_controller_player_partner.c +++ b/src/battle_controller_player_partner.c @@ -307,6 +307,12 @@ static void PlayerPartnerHandleDrawTrainerPic(u32 battler) xPos = 90; yPos = (8 - gTrainerBackPicCoords[trainerPicId].size) * 4 + 80; } + else if (IsAiVsAiBattle()) + { + trainerPicId = gTrainers[gPartnerTrainerId].trainerPic; + xPos = 60; + yPos = (8 - gTrainerFrontPicCoords[trainerPicId].size) * 4 + 80; + } else { trainerPicId = GetFrontierTrainerFrontSpriteId(gPartnerTrainerId); @@ -434,6 +440,8 @@ static void PlayerPartnerHandleIntroTrainerBallThrow(u32 battler) trainerPal = gTrainerBackPicPaletteTable[TRAINER_STEVEN_PARTNER].data; else if (gPartnerTrainerId >= TRAINER_CUSTOM_PARTNER) // Custom multi battle. trainerPal = gTrainerBackPicPaletteTable[gPartnerSpriteId].data; + else if (IsAiVsAiBattle()) + trainerPal = gTrainerFrontPicPaletteTable[gTrainers[gPartnerTrainerId].trainerPic].data; else trainerPal = gTrainerFrontPicPaletteTable[GetFrontierTrainerFrontSpriteId(gPartnerTrainerId)].data; // 2 vs 2 multi battle in Battle Frontier, load front sprite and pal. diff --git a/src/battle_controllers.c b/src/battle_controllers.c index 1518e46345..bb2fc2905b 100644 --- a/src/battle_controllers.c +++ b/src/battle_controllers.c @@ -1,8 +1,9 @@ #include "global.h" #include "battle.h" #include "battle_ai_main.h" -#include "battle_arena.h" +#include "battle_ai_util.h" #include "battle_anim.h" +#include "battle_arena.h" #include "battle_controllers.h" #include "battle_gfx_sfx_util.h" #include "battle_interface.h" @@ -185,6 +186,8 @@ static void InitSinglePlayerBtlControllers(void) gBattlerControllerFuncs[0] = SetControllerToSafari; else if (gBattleTypeFlags & BATTLE_TYPE_WALLY_TUTORIAL) gBattlerControllerFuncs[0] = SetControllerToWally; + else if (IsAiVsAiBattle()) + gBattlerControllerFuncs[0] = SetControllerToPlayerPartner; else gBattlerControllerFuncs[0] = SetControllerToPlayer; @@ -236,13 +239,19 @@ static void InitSinglePlayerBtlControllers(void) { gBattleMainFunc = BeginBattleIntro; - gBattlerControllerFuncs[0] = SetControllerToPlayer; + if (IsAiVsAiBattle()) + gBattlerControllerFuncs[0] = SetControllerToPlayerPartner; + else + gBattlerControllerFuncs[0] = SetControllerToPlayer; gBattlerPositions[0] = B_POSITION_PLAYER_LEFT; gBattlerControllerFuncs[1] = SetControllerToOpponent; gBattlerPositions[1] = B_POSITION_OPPONENT_LEFT; - gBattlerControllerFuncs[2] = SetControllerToPlayer; + if (IsAiVsAiBattle()) + gBattlerControllerFuncs[2] = SetControllerToPlayerPartner; + else + gBattlerControllerFuncs[2] = SetControllerToPlayer; gBattlerPositions[2] = B_POSITION_PLAYER_RIGHT; gBattlerControllerFuncs[3] = SetControllerToOpponent; diff --git a/src/battle_main.c b/src/battle_main.c index 84dcf8c758..d26fc325f1 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -2071,6 +2071,13 @@ static u8 CreateNPCTrainerParty(struct Pokemon *party, u16 trainerNum, bool8 fir return retVal; } +void CreateTrainerPartyForPlayer(void) +{ + ZeroPlayerPartyMons(); + gPartnerTrainerId = gSpecialVar_0x8004; + CreateNPCTrainerPartyFromTrainer(gPlayerParty, &gTrainers[gSpecialVar_0x8004], TRUE, BATTLE_TYPE_TRAINER); +} + void VBlankCB_Battle(void) { // Change gRngSeed every vblank unless the battle could be recorded. diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index aa7043c573..50c64d5201 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -4068,7 +4068,9 @@ static void Cmd_getexp(void) switch (gBattleScripting.getexpState) { case 0: // check if should receive exp at all - if (GetBattlerSide(gBattlerFainted) != B_SIDE_OPPONENT || (gBattleTypeFlags & + if (GetBattlerSide(gBattlerFainted) != B_SIDE_OPPONENT + || IsAiVsAiBattle() + || (gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK | BATTLE_TYPE_TRAINER_HILL diff --git a/src/battle_util.c b/src/battle_util.c index 926599c955..8eae10cb13 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -7961,7 +7961,7 @@ u8 IsMonDisobedient(void) if (gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK)) return 0; - if (GetBattlerSide(gBattlerAttacker) == B_SIDE_OPPONENT) + if (BattlerHasAi(gBattlerAttacker)) return 0; if (IsBattlerModernFatefulEncounter(gBattlerAttacker)) // only false if illegal Mew or Deoxys