From 557ae12ffbd4df17608ac148ed844c28db325730 Mon Sep 17 00:00:00 2001 From: cawtds <38510667+cawtds@users.noreply.github.com> Date: Sat, 3 Aug 2024 13:41:35 +0200 Subject: [PATCH] added battle_controller_player_partner, added functionality for BATTLE_TYPE_INGAME_PARTNER with multi_2_vs_2, fixed issues in battle_controller_opponent --- asm/macros.inc | 1 + asm/macros/battle_frontier/battle_tower.inc | 23 + asm/macros/event.inc | 6 + data/battle_scripts_1.s | 1 + data/event_scripts.s | 1 + data/maps/SevenIsland_House_Room1/scripts.inc | 2 +- data/specials.inc | 4 +- graphics/trainers/back_pics/steven.png | Bin 0 -> 1710 bytes graphics/trainers/palettes/steven.pal | 19 + include/battle_controllers.h | 4 + include/battle_main.h | 3 + include/battle_setup.h | 2 + include/battle_tower.h | 1 + include/constants/battle_partner.h | 2 +- include/constants/battle_setup.h | 2 + include/constants/battle_tower.h | 9 + include/constants/global.h | 6 + include/constants/trainers.h | 1 + include/data.h | 5 + include/graphics.h | 2 + include/party_menu.h | 2 +- include/pokemon.h | 2 +- include/script_pokemon_util.h | 2 +- src/battle_controller_link_opponent.c | 148 ++++-- src/battle_controller_link_partner.c | 65 --- src/battle_controller_opponent.c | 37 +- src/battle_controller_player_partner.c | 452 ++++++++++++++++++ src/battle_controllers.c | 72 ++- src/battle_main.c | 353 ++++++++++++-- src/battle_message.c | 10 +- src/battle_setup.c | 16 +- src/battle_tower.c | 165 ++++++- src/cable_club.c | 4 +- src/data.c | 8 + src/data/battle_partners.h | 66 ++- src/data/graphics/trainers.h | 2 + src/data/trainer_graphics/back_pic_anims.h | 30 +- src/data/trainer_graphics/back_pic_tables.h | 9 +- src/party_menu.c | 4 +- src/pokeball.c | 7 +- src/pokemon.c | 60 +-- src/script_pokemon_util.c | 17 +- 42 files changed, 1367 insertions(+), 258 deletions(-) create mode 100644 asm/macros/battle_frontier/battle_tower.inc create mode 100644 graphics/trainers/back_pics/steven.png create mode 100644 graphics/trainers/palettes/steven.pal create mode 100644 src/battle_controller_player_partner.c diff --git a/asm/macros.inc b/asm/macros.inc index dca13692c..426be085c 100644 --- a/asm/macros.inc +++ b/asm/macros.inc @@ -5,6 +5,7 @@ .include "asm/macros/map.inc" .include "asm/macros/m4a.inc" .include "asm/macros/trainer_tower.inc" + .include "asm/macros/battle_frontier/battle_tower.inc" .macro region_map_location x, y, width, height, name .byte \x diff --git a/asm/macros/battle_frontier/battle_tower.inc b/asm/macros/battle_frontier/battle_tower.inc new file mode 100644 index 000000000..7782cb00b --- /dev/null +++ b/asm/macros/battle_frontier/battle_tower.inc @@ -0,0 +1,23 @@ + @Custom multi-battle commands + + .macro multi_do type:req, partnerId:req + special ReducePlayerPartyToSelectedMons + @setvar VAR_0x8004, FRONTIER_UTIL_FUNC_SET_DATA + @setvar VAR_0x8005, FRONTIER_DATA_SELECTED_MON_ORDER + @special CallFrontierUtilFunc @ saves the mon order, so the non-selected mons get restored afterwards + setvar VAR_0x8004, SPECIAL_BATTLE_MULTI + setvar VAR_0x8005, \type | MULTI_BATTLE_CHOOSE_MONS + setvar VAR_0x8006, \partnerId + special DoSpecialTrainerBattle + waitstate + @setvar VAR_0x8004, FRONTIER_UTIL_FUNC_SAVE_PARTY + @special CallFrontierUtilFunc + special LoadPlayerParty + .endm + + .macro multi_2_vs_2 trainer1Id:req, trainer1LoseText:req, trainer2Id:req, trainer2LoseText:req, partnerId:req + special SavePlayerParty + trainerbattle TRAINER_BATTLE_SET_TRAINER_A, \trainer1Id, 0, NULL, \trainer1LoseText @ set first trainer mons + trainerbattle TRAINER_BATTLE_SET_TRAINER_B, \trainer2Id, 0, NULL, \trainer2LoseText @ set second trainer mons + multi_do MULTI_BATTLE_2_VS_2, \partnerId + .endm diff --git a/asm/macros/event.inc b/asm/macros/event.inc index 8b956795d..4b93931d1 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -729,6 +729,12 @@ .elseif \type == TRAINER_BATTLE_EARLY_RIVAL .4byte \pointer1 @ text .4byte \pointer2 @ text + .elseif \type == TRAINER_BATTLE_SET_TRAINER_A + .4byte \pointer1 @ text + .4byte \pointer2 @ text + .elseif \type == TRAINER_BATTLE_SET_TRAINER_B + .4byte \pointer1 @ text + .4byte \pointer2 @ text .endif .endm diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 95fabc215..111150eb0 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -48,6 +48,7 @@ BattleScript_PayDayMoneyAndPickUpItems:: end2 BattleScript_LocalBattleLost:: + jumpifbattletype BATTLE_TYPE_INGAME_PARTNER, BattleScript_LocalBattleLostPrintWhiteOut jumpifbattletype BATTLE_TYPE_TRAINER_TOWER, BattleScript_BattleTowerLost jumpifbattletype BATTLE_TYPE_EREADER_TRAINER, BattleScript_LocalBattleLostEnd jumpifhalfword CMP_EQUAL, gTrainerBattleOpponent_A, TRAINER_SECRET_BASE, BattleScript_LocalBattleLostEnd diff --git a/data/event_scripts.s b/data/event_scripts.s index d41399a64..448024d67 100644 --- a/data/event_scripts.s +++ b/data/event_scripts.s @@ -2,6 +2,7 @@ #include "config/battle.h" #include "constants/global.h" #include "constants/flags.h" +#include "constants/battle_tower.h" #include "constants/event_objects.h" #include "constants/event_object_movement.h" #include "constants/decorations.h" diff --git a/data/maps/SevenIsland_House_Room1/scripts.inc b/data/maps/SevenIsland_House_Room1/scripts.inc index 056519ed2..0b5548435 100644 --- a/data/maps/SevenIsland_House_Room1/scripts.inc +++ b/data/maps/SevenIsland_House_Room1/scripts.inc @@ -98,7 +98,7 @@ SevenIsland_House_Room1_EventScript_TrainerVisiting:: call EventScript_AskSaveGame goto_if_eq VAR_RESULT, 0, SevenIsland_House_Room1_EventScript_DeclineBattle special SavePlayerParty - special ReducePlayerPartyToThree + special ReducePlayerPartyToSelectedMons msgbox SevenIsland_House_Room1_Text_DontHoldAnythingBack closemessage call_if_eq VAR_FACING, DIR_NORTH, SevenIsland_House_Room1_EventScript_EnterBattleRoomNorth diff --git a/data/specials.inc b/data/specials.inc index 8255c3512..1cf1b2af0 100644 --- a/data/specials.inc +++ b/data/specials.inc @@ -256,7 +256,7 @@ gSpecials:: def_special ChooseBattleTowerPlayerParty def_special ValidateEReaderTrainer def_special NullFieldSpecial - def_special ReducePlayerPartyToThree + def_special ReducePlayerPartyToSelectedMons def_special BedroomPC def_special PlayerPC def_special ShowTownMap @@ -348,7 +348,7 @@ gSpecials:: def_special NullFieldSpecial def_special Dummy_TryEnableBravoTrainerBattleTower def_special DoesPartyHaveEnigmaBerry - def_special NullFieldSpecial + def_special DoSpecialTrainerBattle def_special SetPostgameFlags def_special StartMarowakBattle def_special ForcePlayerOntoBike diff --git a/graphics/trainers/back_pics/steven.png b/graphics/trainers/back_pics/steven.png new file mode 100644 index 0000000000000000000000000000000000000000..961992ff6e68d64c07eaad9975d1b31b0f7f01c8 GIT binary patch literal 1710 zcmV;f22uHmP)&jj00001 zbW%=J06^y0W&i*NEJ;K`RA_g^0=#LWyYcWs&!YS)dU}Yi!?`^S+GnVu> zSgo|bfpX39B25CeM2R*5V=;E69e^PznhHr0FX!f!SiAw2XRnxx+>2yRyJe_Raa~$VOJ$~uB(`g}koQ%f+e5=LxpN_XZF9#y^_U<~)9W7pb{j2MF z4=BCihu*g9+3P|CP?YoD=u1pK%>GS;6Im*KM*>Oos~O{ zKM?1TsVRFqjXyv|W!Lj+e?TK4ae9i2ogC!iy>VHT65x`dCqK2;jSAP$HoqVoUh&plKxJWzl+c0d!5n>?1 z+MnS5K!{CP+5@Uvxe{m%0{8>uHJ7&l>JK*Z)|su~F7 zHGiGc=xD$|*qp`j$shphpt(@gKybFo`_D1y-a3 zASv1k+1VU37eUqlui^}`Ndd2j`Lj4nko^v%EdW2o#T9EAvdcuD5v8u-U(-b09GIj%HiIvABDNVmVG0D-R&)Nbf6H2 zlWk=7&=BA)Jom0M>50QBmkMV92B;S}@*u^nz6#=lJt5w7+8c+%nlUo))|CJ=H(4o> z(-*8T-BG>7XhF;!kW;*jm!HSUQBR7-8eIAKDO@YRFfVA41^@*8uvuFJDestxtAJ1f zh%DPw*6=INunwRjx$H3V(In)ICFj~iiYnfL`13^H zau8+d@!sfN()>N=6T@w^4D^BRDMXl38XaZ0;g zp!+#pDY6bP9Z9qRkLM~N2vMZIYw~Tk%2n@S%MBx<(Uq8v+^`ebY3m$vxgl^Vmpiqr z*G8<&yHQ!4!rLjIssmNN%c%#BAeaBefXH?CM>rts(p}e0`rZcr^|dw;YZEbAn+Uaw zwTY5X1Utw0B#~W^9R%;bjAzVTwWbOU`0BBIYANH)qKSP zF2VSELMlo#e+YX3VGhdafH47)GS!GHB?yo!K=@AqWyWF7v!sb=nb6p50Ti{u!|z`{ zXqzouMq>e@{HHkX9uUH1MD6h4Iyw8W&;TCCe>9)$lWeI0V!FT&Y7-H`iFtmd_4p!V z?UxHIDKuXxFQVD*=(?cV4XhealthBoxesData[battler].finishedShinyMonAnim - || !gBattleSpritesDataPtr->healthBoxesData[battler ^ BIT_FLANK].finishedShinyMonAnim) + if (!gBattleSpritesDataPtr->healthBoxesData[battler].finishedShinyMonAnim) + return; + if (!gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].finishedShinyMonAnim) return; gBattleSpritesDataPtr->healthBoxesData[battler].triedShinyMonAnim = FALSE; gBattleSpritesDataPtr->healthBoxesData[battler].finishedShinyMonAnim = FALSE; - gBattleSpritesDataPtr->healthBoxesData[battler ^ BIT_FLANK].triedShinyMonAnim = FALSE; - gBattleSpritesDataPtr->healthBoxesData[battler ^ BIT_FLANK].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); } - if (gBattleTypeFlags & BATTLE_TYPE_MULTI) - { - if (GetBattlerPosition(battler) == B_POSITION_OPPONENT_LEFT) - m4aMPlayContinue(&gMPlayInfo_BGM); - } else { - m4aMPlayVolumeControl(&gMPlayInfo_BGM, TRACKS_ALL, 256); + if (!gBattleSpritesDataPtr->healthBoxesData[battler].finishedShinyMonAnim) + return; + + gBattleSpritesDataPtr->healthBoxesData[battler].triedShinyMonAnim = FALSE; + gBattleSpritesDataPtr->healthBoxesData[battler].finishedShinyMonAnim = FALSE; + + if (GetBattlerPosition(battler) == B_POSITION_OPPONENT_RIGHT) + { + FreeSpriteTilesByTag(ANIM_TAG_GOLD_STARS); + FreeSpritePaletteByTag(ANIM_TAG_GOLD_STARS); + } } + gBattleSpritesDataPtr->healthBoxesData[battler].introEndDelay = 3; gBattlerControllerFuncs[battler] = Intro_DelayAndEnd; } @@ -168,38 +179,89 @@ static void Intro_WaitForShinyAnimAndHealthbox(u32 battler) static void Intro_TryShinyAnimShowHealthbox(u32 battler) { + bool32 bgmRestored = FALSE; + if (!gBattleSpritesDataPtr->healthBoxesData[battler].ballAnimActive - && !gBattleSpritesDataPtr->healthBoxesData[battler ^ BIT_FLANK].ballAnimActive) + && !gBattleSpritesDataPtr->healthBoxesData[battler].triedShinyMonAnim + && !gBattleSpritesDataPtr->healthBoxesData[battler].finishedShinyMonAnim) { - if (!gBattleSpritesDataPtr->healthBoxesData[battler].triedShinyMonAnim) - TryShinyAnimation(battler, &gEnemyParty[gBattlerPartyIndexes[battler]]); - if (!gBattleSpritesDataPtr->healthBoxesData[battler ^ BIT_FLANK].triedShinyMonAnim) - TryShinyAnimation(battler ^ BIT_FLANK, &gEnemyParty[gBattlerPartyIndexes[battler ^ BIT_FLANK]]); - if (gBattleTypeFlags & BATTLE_TYPE_MULTI && GetBattlerPosition(battler) == 3) + TryShinyAnimation(battler, &gEnemyParty[gBattlerPartyIndexes[battler]]); + } + if (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) + && !gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].ballAnimActive + && !gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].triedShinyMonAnim + && !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 (++gBattleSpritesDataPtr->healthBoxesData[battler].introEndDelay == 1) - return; - gBattleSpritesDataPtr->healthBoxesData[battler].introEndDelay = 0; + if (IsDoubleBattle() && !(gBattleTypeFlags & BATTLE_TYPE_MULTI)) + { + 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]); } - if (IsDoubleBattle() && !(gBattleTypeFlags & BATTLE_TYPE_MULTI)) + 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) { - DestroySprite(&gSprites[gBattleControllerData[battler ^ BIT_FLANK]]); - UpdateHealthboxAttribute(gHealthboxSpriteIds[battler ^ BIT_FLANK], - &gEnemyParty[gBattlerPartyIndexes[battler ^ BIT_FLANK]], - HEALTHBOX_ALL); - StartHealthboxSlideIn(battler ^ BIT_FLANK); - SetHealthboxSpriteVisible(gHealthboxSpriteIds[battler ^ BIT_FLANK]); - SetBattlerShadowSpriteCallback(battler ^ BIT_FLANK, GetMonData(&gEnemyParty[gBattlerPartyIndexes[battler ^ BIT_FLANK]], MON_DATA_SPECIES)); + if (gBattleTypeFlags & BATTLE_TYPE_MULTI && gBattleTypeFlags & BATTLE_TYPE_LINK) + { + if (GetBattlerPosition(battler) == B_POSITION_OPPONENT_LEFT) + m4aMPlayContinue(&gMPlayInfo_BGM); + } + else + { + m4aMPlayVolumeControl(&gMPlayInfo_BGM, TRACKS_ALL, 0x100); + } + } + + gBattleSpritesDataPtr->healthBoxesData[battler].bgmRestored = TRUE; + bgmRestored = TRUE; + } + + if (bgmRestored) + { + if (gSprites[gBattleControllerData[battler]].callback == SpriteCallbackDummy + && gSprites[gBattlerSpriteIds[battler]].callback == SpriteCallbackDummy) + { + if (gBattleTypeFlags & BATTLE_TYPE_MULTI && GetBattlerPosition(battler) == B_POSITION_OPPONENT_RIGHT) + { + if (++gBattleSpritesDataPtr->healthBoxesData[battler].introEndDelay == 1) + return; + gBattleSpritesDataPtr->healthBoxesData[battler].introEndDelay = 0; + } + + if (IsDoubleBattle() && !(gBattleTypeFlags & BATTLE_TYPE_MULTI)) + { + DestroySprite(&gSprites[gBattleControllerData[BATTLE_PARTNER(battler)]]); + SetBattlerShadowSpriteCallback(BATTLE_PARTNER(battler), GetMonData(&gEnemyParty[gBattlerPartyIndexes[BATTLE_PARTNER(battler)]], MON_DATA_SPECIES)); + } + + + DestroySprite(&gSprites[gBattleControllerData[battler]]); + SetBattlerShadowSpriteCallback(battler, GetMonData(&gEnemyParty[gBattlerPartyIndexes[battler]], MON_DATA_SPECIES)); + + gBattleSpritesDataPtr->animationData->introAnimActive = FALSE; + gBattleSpritesDataPtr->healthBoxesData[battler].bgmRestored = FALSE; + gBattleSpritesDataPtr->healthBoxesData[battler].healthboxSlideInStarted = FALSE; + + gBattlerControllerFuncs[battler] = Intro_WaitForShinyAnimAndHealthbox; } - DestroySprite(&gSprites[gBattleControllerData[battler]]); - UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], - &gEnemyParty[gBattlerPartyIndexes[battler]], - HEALTHBOX_ALL); - StartHealthboxSlideIn(battler); - SetHealthboxSpriteVisible(gHealthboxSpriteIds[battler]); - SetBattlerShadowSpriteCallback(battler, GetMonData(&gEnemyParty[gBattlerPartyIndexes[battler]], MON_DATA_SPECIES)); - gBattleSpritesDataPtr->animationData->introAnimActive = FALSE; - gBattlerControllerFuncs[battler] = Intro_WaitForShinyAnimAndHealthbox; } } diff --git a/src/battle_controller_link_partner.c b/src/battle_controller_link_partner.c index b9d5929aa..33e809b6f 100644 --- a/src/battle_controller_link_partner.c +++ b/src/battle_controller_link_partner.c @@ -107,40 +107,6 @@ static void LinkPartnerBufferRunCommand(u32 battler) } } -static void Intro_DelayAndEnd(u32 battler) -{ - if (--gBattleSpritesDataPtr->healthBoxesData[battler].introEndDelay == (u8)-1) - { - gBattleSpritesDataPtr->healthBoxesData[battler].introEndDelay = 0; - LinkPartnerBufferExecCompleted(battler); - } -} - -static void Intro_WaitForHealthbox(u32 battler) -{ - bool32 finished = FALSE; - - if (!IsDoubleBattle() || (IsDoubleBattle() && (gBattleTypeFlags & BATTLE_TYPE_MULTI))) - { - if (gSprites[gHealthboxSpriteIds[battler]].callback == SpriteCallbackDummy) - finished = TRUE; - } - else if (gSprites[gHealthboxSpriteIds[battler]].callback == SpriteCallbackDummy - && gSprites[gHealthboxSpriteIds[BATTLE_PARTNER(battler)]].callback == SpriteCallbackDummy) - { - finished = TRUE; - } - - if (IsCryPlayingOrClearCrySongs()) - finished = FALSE; - - if (finished) - { - gBattleSpritesDataPtr->healthBoxesData[battler].introEndDelay = 3; - gBattlerControllerFuncs[battler] = Intro_DelayAndEnd; - } -} - static void WaitForMonAnimAfterLoad(u32 battler) { if (gSprites[gBattlerSpriteIds[battler]].animEnded && gSprites[gBattlerSpriteIds[battler]].x2 == 0) @@ -262,37 +228,6 @@ static void LinkPartnerHandleHealthBarUpdate(u32 battler) BtlController_HandleHealthBarUpdate(battler, FALSE); } -// Also used by the link partner. -void Controller_PlayerPartnerShowIntroHealthbox(u32 battler) -{ - if (!gBattleSpritesDataPtr->healthBoxesData[battler].ballAnimActive - && !gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].ballAnimActive - && gSprites[gBattleControllerData[battler]].callback == SpriteCallbackDummy - && gSprites[gBattlerSpriteIds[battler]].callback == SpriteCallbackDummy - && ++gBattleSpritesDataPtr->healthBoxesData[battler].introEndDelay != 1) - { - gBattleSpritesDataPtr->healthBoxesData[battler].introEndDelay = 0; - TryShinyAnimation(battler, &gPlayerParty[gBattlerPartyIndexes[battler]]); - - if (IsDoubleBattle() && !(gBattleTypeFlags & BATTLE_TYPE_MULTI)) - { - DestroySprite(&gSprites[gBattleControllerData[BATTLE_PARTNER(battler)]]); - UpdateHealthboxAttribute(gHealthboxSpriteIds[BATTLE_PARTNER(battler)], &gPlayerParty[gBattlerPartyIndexes[BATTLE_PARTNER(battler)]], HEALTHBOX_ALL); - StartHealthboxSlideIn(BATTLE_PARTNER(battler)); - SetHealthboxSpriteVisible(gHealthboxSpriteIds[BATTLE_PARTNER(battler)]); - } - - DestroySprite(&gSprites[gBattleControllerData[battler]]); - UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], &gPlayerParty[gBattlerPartyIndexes[battler]], HEALTHBOX_ALL); - StartHealthboxSlideIn(battler); - SetHealthboxSpriteVisible(gHealthboxSpriteIds[battler]); - - gBattleSpritesDataPtr->animationData->introAnimActive = FALSE; - - gBattlerControllerFuncs[battler] = Intro_WaitForHealthbox; - } -} - static void LinkPartnerHandleIntroTrainerBallThrow(u32 battler) { u32 trainerPicId = LinkPlayerGetTrainerPicId(GetBattlerMultiplayerId(battler)); diff --git a/src/battle_controller_opponent.c b/src/battle_controller_opponent.c index 1826f8417..9fb5730b3 100644 --- a/src/battle_controller_opponent.c +++ b/src/battle_controller_opponent.c @@ -291,21 +291,19 @@ static void Intro_TryShinyAnimShowHealthbox(u32 battler) static void TryShinyAnimAfterMonAnim(u32 battler) { - if (gSprites[gBattlerSpriteIds[battler]].animEnded == TRUE - && gSprites[gBattlerSpriteIds[battler]].x2 == 0) + 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) { - if (!gBattleSpritesDataPtr->healthBoxesData[battler].triedShinyMonAnim) - { - TryShinyAnimation(battler, &gEnemyParty[gBattlerPartyIndexes[battler]]); - } - else if (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); - } + gBattleSpritesDataPtr->healthBoxesData[battler].triedShinyMonAnim = FALSE; + gBattleSpritesDataPtr->healthBoxesData[battler].finishedShinyMonAnim = FALSE; + FreeSpriteTilesByTag(ANIM_TAG_GOLD_STARS); + FreeSpritePaletteByTag(ANIM_TAG_GOLD_STARS); + OpponentBufferExecCompleted(battler); } } @@ -330,7 +328,8 @@ static void SwitchIn_HandleSoundAndEnd(u32 battler) static void SwitchIn_ShowHealthbox(u32 battler) { - if (gBattleSpritesDataPtr->healthBoxesData[battler].finishedShinyMonAnim) + if (gBattleSpritesDataPtr->healthBoxesData[battler].finishedShinyMonAnim + && gSprites[gBattlerSpriteIds[battler]].callback == SpriteCallbackDummy) { gBattleSpritesDataPtr->healthBoxesData[battler].triedShinyMonAnim = FALSE; gBattleSpritesDataPtr->healthBoxesData[battler].finishedShinyMonAnim = FALSE; @@ -347,10 +346,14 @@ static void SwitchIn_ShowHealthbox(u32 battler) static void SwitchIn_TryShinyAnim(u32 battler) { - if (!gBattleSpritesDataPtr->healthBoxesData[battler].triedShinyMonAnim && !gBattleSpritesDataPtr->healthBoxesData[battler].ballAnimActive) + if (!gBattleSpritesDataPtr->healthBoxesData[battler].ballAnimActive + && !gBattleSpritesDataPtr->healthBoxesData[battler].triedShinyMonAnim) + { TryShinyAnimation(battler, &gEnemyParty[gBattlerPartyIndexes[battler]]); + } + if (gSprites[gBattleControllerData[battler]].callback == SpriteCallbackDummy - && !gBattleSpritesDataPtr->healthBoxesData[battler].ballAnimActive) + && !gBattleSpritesDataPtr->healthBoxesData[battler].ballAnimActive) { DestroySprite(&gSprites[gBattleControllerData[battler]]); SetBattlerShadowSpriteCallback(battler, GetMonData(&gEnemyParty[gBattlerPartyIndexes[battler]], MON_DATA_SPECIES)); diff --git a/src/battle_controller_player_partner.c b/src/battle_controller_player_partner.c new file mode 100644 index 000000000..512f2cb4f --- /dev/null +++ b/src/battle_controller_player_partner.c @@ -0,0 +1,452 @@ +#include "global.h" +#include "battle.h" +#include "battle_ai_main.h" +#include "battle_ai_util.h" +#include "battle_anim.h" +#include "battle_controllers.h" +#include "battle_message.h" +#include "battle_interface.h" +#include "battle_setup.h" +#include "battle_tower.h" +#include "battle_z_move.h" +#include "bg.h" +#include "data.h" +#include "item_use.h" +#include "link.h" +#include "main.h" +#include "m4a.h" +#include "palette.h" +#include "party_menu.h" +#include "pokeball.h" +#include "pokemon.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/battle_partner.h" +#include "constants/songs.h" +#include "constants/party_menu.h" +#include "constants/trainers.h" + +static void PlayerPartnerHandleLoadMonSprite(u32 battler); +static void PlayerPartnerHandleSwitchInAnim(u32 battler); +static void PlayerPartnerHandleDrawTrainerPic(u32 battler); +static void PlayerPartnerHandleTrainerSlideBack(u32 battler); +static void PlayerPartnerHandleChooseAction(u32 battler); +static void PlayerPartnerHandleChooseMove(u32 battler); +static void PlayerPartnerHandleChoosePokemon(u32 battler); +static void PlayerPartnerHandleHealthBarUpdate(u32 battler); +static void PlayerPartnerHandleIntroTrainerBallThrow(u32 battler); +static void PlayerPartnerHandleDrawPartyStatusSummary(u32 battler); +static void PlayerPartnerHandleBattleAnimation(u32 battler); +static void PlayerPartnerHandleEndLinkBattle(u32 battler); + +static void PlayerPartnerBufferRunCommand(u32 battler); +static void PlayerPartnerBufferExecCompleted(u32 battler); +static void SwitchIn_WaitAndEnd(u32 battler); + +static void (*const sPlayerPartnerBufferCommands[CONTROLLER_CMDS_COUNT])(u32 battler) = +{ + [CONTROLLER_GETMONDATA] = BtlController_HandleGetMonData, + [CONTROLLER_GETRAWMONDATA] = BtlController_Empty, + [CONTROLLER_SETMONDATA] = BtlController_HandleSetMonData, + [CONTROLLER_SETRAWMONDATA] = BtlController_HandleSetRawMonData, + [CONTROLLER_LOADMONSPRITE] = PlayerPartnerHandleLoadMonSprite, + [CONTROLLER_SWITCHINANIM] = PlayerPartnerHandleSwitchInAnim, + [CONTROLLER_RETURNMONTOBALL] = BtlController_HandleReturnMonToBall, + [CONTROLLER_DRAWTRAINERPIC] = PlayerPartnerHandleDrawTrainerPic, + [CONTROLLER_TRAINERSLIDE] = BtlController_Empty, + [CONTROLLER_TRAINERSLIDEBACK] = PlayerPartnerHandleTrainerSlideBack, + [CONTROLLER_FAINTANIMATION] = BtlController_HandleFaintAnimation, + [CONTROLLER_PALETTEFADE] = BtlController_Empty, + [CONTROLLER_SUCCESSBALLTHROWANIM] = BtlController_Empty, + [CONTROLLER_BALLTHROWANIM] = BtlController_Empty, + [CONTROLLER_PAUSE] = BtlController_Empty, + [CONTROLLER_MOVEANIMATION] = BtlController_HandleMoveAnimation, + [CONTROLLER_PRINTSTRING] = BtlController_HandlePrintString, + [CONTROLLER_PRINTSTRINGPLAYERONLY] = BtlController_Empty, + [CONTROLLER_CHOOSEACTION] = PlayerPartnerHandleChooseAction, + [CONTROLLER_UNKNOWNYESNOBOX] = BtlController_Empty, + [CONTROLLER_CHOOSEMOVE] = PlayerPartnerHandleChooseMove, + [CONTROLLER_OPENBAG] = BtlController_Empty, + [CONTROLLER_CHOOSEPOKEMON] = PlayerPartnerHandleChoosePokemon, + [CONTROLLER_23] = BtlController_Empty, + [CONTROLLER_HEALTHBARUPDATE] = PlayerPartnerHandleHealthBarUpdate, + [CONTROLLER_EXPUPDATE] = PlayerHandleExpUpdate, // Partner's player gets experience the same way as the player. + [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] = PlayerPartnerHandleIntroTrainerBallThrow, + [CONTROLLER_DRAWPARTYSTATUSSUMMARY] = PlayerPartnerHandleDrawPartyStatusSummary, + [CONTROLLER_HIDEPARTYSTATUSSUMMARY] = BtlController_HandleHidePartyStatusSummary, + [CONTROLLER_ENDBOUNCE] = BtlController_Empty, + [CONTROLLER_SPRITEINVISIBILITY] = BtlController_HandleSpriteInvisibility, + [CONTROLLER_BATTLEANIMATION] = PlayerPartnerHandleBattleAnimation, + [CONTROLLER_LINKSTANDBYMSG] = BtlController_Empty, + [CONTROLLER_RESETACTIONMOVESELECTION] = BtlController_Empty, + [CONTROLLER_ENDLINKBATTLE] = PlayerPartnerHandleEndLinkBattle, + [CONTROLLER_DEBUGMENU] = BtlController_Empty, + [CONTROLLER_TERMINATOR_NOP] = BtlController_TerminatorNop +}; + +void SetControllerToPlayerPartner(u32 battler) +{ + gBattlerControllerEndFuncs[battler] = PlayerPartnerBufferExecCompleted; + gBattlerControllerFuncs[battler] = PlayerPartnerBufferRunCommand; +} + +static void PlayerPartnerBufferRunCommand(u32 battler) +{ + if (gBattleControllerExecFlags & gBitTable[battler]) + { + if (gBattleResources->bufferA[battler][0] < ARRAY_COUNT(sPlayerPartnerBufferCommands)) + sPlayerPartnerBufferCommands[gBattleResources->bufferA[battler][0]](battler); + else + PlayerPartnerBufferExecCompleted(battler); + } +} + +static void Intro_DelayAndEnd(u32 battler) +{ + if (--gBattleSpritesDataPtr->healthBoxesData[battler].introEndDelay == (u8)-1) + { + gBattleSpritesDataPtr->healthBoxesData[battler].introEndDelay = 0; + BattleControllerComplete(battler); + } +} + +static void Intro_WaitForHealthbox(u32 battler) +{ + bool32 finished = FALSE; + + if (!IsDoubleBattle() || (IsDoubleBattle() && (gBattleTypeFlags & BATTLE_TYPE_MULTI))) + { + if (gSprites[gHealthboxSpriteIds[battler]].callback == SpriteCallbackDummy) + finished = TRUE; + } + else + { + if (gSprites[gHealthboxSpriteIds[battler]].callback == SpriteCallbackDummy + && gSprites[gHealthboxSpriteIds[BATTLE_PARTNER(battler)]].callback == SpriteCallbackDummy) + { + finished = TRUE; + } + } + + if (IsCryPlayingOrClearCrySongs()) + finished = FALSE; + + if (finished) + { + gBattleSpritesDataPtr->healthBoxesData[battler].introEndDelay = 3; + gBattlerControllerFuncs[battler] = Intro_DelayAndEnd; + } +} + +// Also used by the link partner. +void Controller_PlayerPartnerShowIntroHealthbox(u32 battler) +{ + if (!gBattleSpritesDataPtr->healthBoxesData[battler].ballAnimActive + && !gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].ballAnimActive + && gSprites[gBattleControllerData[battler]].callback == SpriteCallbackDummy + && gSprites[gBattlerSpriteIds[battler]].callback == SpriteCallbackDummy + && ++gBattleSpritesDataPtr->healthBoxesData[battler].introEndDelay != 1) + { + gBattleSpritesDataPtr->healthBoxesData[battler].introEndDelay = 0; + TryShinyAnimation(battler, &gPlayerParty[gBattlerPartyIndexes[battler]]); + + if (IsDoubleBattle() && !(gBattleTypeFlags & BATTLE_TYPE_MULTI)) + { + DestroySprite(&gSprites[gBattleControllerData[BATTLE_PARTNER(battler)]]); + UpdateHealthboxAttribute(gHealthboxSpriteIds[BATTLE_PARTNER(battler)], &gPlayerParty[gBattlerPartyIndexes[BATTLE_PARTNER(battler)]], HEALTHBOX_ALL); + StartHealthboxSlideIn(BATTLE_PARTNER(battler)); + SetHealthboxSpriteVisible(gHealthboxSpriteIds[BATTLE_PARTNER(battler)]); + } + + DestroySprite(&gSprites[gBattleControllerData[battler]]); + UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], &gPlayerParty[gBattlerPartyIndexes[battler]], HEALTHBOX_ALL); + StartHealthboxSlideIn(battler); + SetHealthboxSpriteVisible(gHealthboxSpriteIds[battler]); + + gBattleSpritesDataPtr->animationData->introAnimActive = FALSE; + + gBattlerControllerFuncs[battler] = Intro_WaitForHealthbox; + } +} + +static void WaitForMonAnimAfterLoad(u32 battler) +{ + if (gSprites[gBattlerSpriteIds[battler]].animEnded && gSprites[gBattlerSpriteIds[battler]].x2 == 0) + PlayerPartnerBufferExecCompleted(battler); +} + +static void SwitchIn_ShowSubstitute(u32 battler) +{ + if (gSprites[gHealthboxSpriteIds[battler]].callback == SpriteCallbackDummy) + { + CopyBattleSpriteInvisibility(battler); + if (gBattleSpritesDataPtr->battlerData[battler].behindSubstitute) + InitAndLaunchSpecialAnimation(battler, battler, battler, B_ANIM_MON_TO_SUBSTITUTE); + + gBattlerControllerFuncs[battler] = SwitchIn_WaitAndEnd; + } +} + +static void SwitchIn_WaitAndEnd(u32 battler) +{ + if (!gBattleSpritesDataPtr->healthBoxesData[battler].specialAnimActive + && gSprites[gBattlerSpriteIds[battler]].callback == SpriteCallbackDummy) + { + PlayerPartnerBufferExecCompleted(battler); + } +} + +static void SwitchIn_ShowHealthbox(u32 battler) +{ + if (gBattleSpritesDataPtr->healthBoxesData[battler].finishedShinyMonAnim) + { + gBattleSpritesDataPtr->healthBoxesData[battler].triedShinyMonAnim = FALSE; + gBattleSpritesDataPtr->healthBoxesData[battler].finishedShinyMonAnim = FALSE; + + FreeSpriteTilesByTag(ANIM_TAG_GOLD_STARS); + FreeSpritePaletteByTag(ANIM_TAG_GOLD_STARS); + + CreateTask(Task_PlayerController_RestoreBgmAfterCry, 10); + HandleLowHpMusicChange(&gPlayerParty[gBattlerPartyIndexes[battler]], battler); + StartSpriteAnim(&gSprites[gBattlerSpriteIds[battler]], 0); + UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], &gPlayerParty[gBattlerPartyIndexes[battler]], HEALTHBOX_ALL); + StartHealthboxSlideIn(battler); + SetHealthboxSpriteVisible(gHealthboxSpriteIds[battler]); + + gBattlerControllerFuncs[battler] = SwitchIn_ShowSubstitute; + } +} + +static void SwitchIn_TryShinyAnim(u32 battler) +{ + if (!gBattleSpritesDataPtr->healthBoxesData[battler].triedShinyMonAnim + && !gBattleSpritesDataPtr->healthBoxesData[battler].ballAnimActive) + { + TryShinyAnimation(battler, &gPlayerParty[gBattlerPartyIndexes[battler]]); + } + + if (gSprites[gBattleControllerData[battler]].callback == SpriteCallbackDummy + && !gBattleSpritesDataPtr->healthBoxesData[battler].ballAnimActive) + { + DestroySprite(&gSprites[gBattleControllerData[battler]]); + gBattlerControllerFuncs[battler] = SwitchIn_ShowHealthbox; + } +} + +static void PlayerPartnerBufferExecCompleted(u32 battler) +{ + gBattlerControllerFuncs[battler] = PlayerPartnerBufferRunCommand; + if (gBattleTypeFlags & BATTLE_TYPE_LINK) + { + u8 playerId = GetMultiplayerId(); + + PrepareBufferDataTransferLink(battler, 2, 4, &playerId); + gBattleResources->bufferA[battler][0] = CONTROLLER_TERMINATOR_NOP; + } + else + { + gBattleControllerExecFlags &= ~gBitTable[battler]; + } +} + +static void PlayerPartnerHandleLoadMonSprite(u32 battler) +{ + BtlController_HandleLoadMonSprite(battler, WaitForMonAnimAfterLoad); +} + +static void PlayerPartnerHandleSwitchInAnim(u32 battler) +{ + BtlController_HandleSwitchInAnim(battler, TRUE, SwitchIn_TryShinyAnim); +} + +// some explanation here +// in emerald it's possible to have a tag battle in the battle frontier facilities with AI +// which use the front sprite for both the player and the partner as opposed to any other battles (including the one with Steven) that use the back pic as well as animate it +static void PlayerPartnerHandleDrawTrainerPic(u32 battler) +{ + bool32 isFrontPic; + s16 xPos, yPos; + u32 trainerPicId; + + if (gPartnerTrainerId > TRAINER_PARTNER(PARTNER_NONE)) + { + trainerPicId = gBattlePartners[gPartnerTrainerId - TRAINER_PARTNER(PARTNER_NONE)].trainerPic; + xPos = 90; + yPos = (8 - gTrainerBackPicCoords[trainerPicId].size) * 4 + 80; + } + else if (IsAiVsAiBattle()) + { + trainerPicId = GetTrainerPicFromId(gPartnerTrainerId); + xPos = 60; + yPos = 80; + } + else + { + trainerPicId = GetBattleTowerTrainerFrontSpriteId(gPartnerTrainerId); + xPos = 32; + yPos = 80; + } + + // Use back pic only if the partner Steven or is custom. + if (gPartnerTrainerId > TRAINER_PARTNER(PARTNER_NONE)) + isFrontPic = FALSE; + else + isFrontPic = TRUE; + + BtlController_HandleDrawTrainerPic(battler, trainerPicId, isFrontPic, xPos, yPos, -1); +} + +static void PlayerPartnerHandleTrainerSlideBack(u32 battler) +{ + BtlController_HandleTrainerSlideBack(battler, 35, FALSE); +} + +static void PlayerPartnerHandleChooseAction(u32 battler) +{ + AI_TrySwitchOrUseItem(battler); + PlayerPartnerBufferExecCompleted(battler); +} + +static void PlayerPartnerHandleChooseMove(u32 battler) +{ + u8 chosenMoveId; + struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct *)(&gBattleResources->bufferA[battler][4]); + + chosenMoveId = gBattleStruct->aiMoveOrAction[battler]; + gBattlerTarget = gBattleStruct->aiChosenTarget[battler]; + + if (chosenMoveId == AI_CHOICE_SWITCH) + { + BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, 0xFFFF); + } + else + { + if (gMovesInfo[moveInfo->moves[chosenMoveId]].target & (MOVE_TARGET_USER | MOVE_TARGET_USER_OR_SELECTED)) + gBattlerTarget = battler; + if (gMovesInfo[moveInfo->moves[chosenMoveId]].target & MOVE_TARGET_BOTH) + { + gBattlerTarget = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); + if (gAbsentBattlerFlags & gBitTable[gBattlerTarget]) + gBattlerTarget = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT); + } + // If partner 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)); + } + } + + PlayerPartnerBufferExecCompleted(battler); +} + +static void PlayerPartnerHandleChoosePokemon(u32 battler) +{ + s32 chosenMonId; + // Choosing Revival Blessing target + if ((gBattleResources->bufferA[battler][1] & 0xF) == PARTY_ACTION_CHOOSE_FAINTED_MON) + { + chosenMonId = gSelectedMonPartyId = GetFirstFaintedPartyIndex(battler); + } + // Switching out + else if (gBattleStruct->monToSwitchIntoId[battler] >= PARTY_SIZE || !IsValidForBattle(&gPlayerParty[gBattleStruct->monToSwitchIntoId[battler]])) + { + chosenMonId = GetMostSuitableMonToSwitchInto(battler, TRUE); + + if (chosenMonId == PARTY_SIZE || !IsValidForBattle(&gPlayerParty[chosenMonId])) // just switch to the next mon + { + s32 firstId = (IsAiVsAiBattle()) ? 0 : (PARTY_SIZE / 2); + u32 battler1 = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); + u32 battler2 = IsDoubleBattle() ? GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT) : battler1; + + for (chosenMonId = firstId; chosenMonId < PARTY_SIZE; chosenMonId++) + { + if (GetMonData(&gPlayerParty[chosenMonId], MON_DATA_HP) != 0 + && chosenMonId != gBattlerPartyIndexes[battler1] + && chosenMonId != gBattlerPartyIndexes[battler2]) + { + break; + } + } + } + *(gBattleStruct->monToSwitchIntoId + battler) = chosenMonId; + } + else // Mon to switch out has been already chosen. + { + chosenMonId = gBattleStruct->monToSwitchIntoId[battler]; + gBattleStruct->AI_monToSwitchIntoId[battler] = PARTY_SIZE; + gBattleStruct->monToSwitchIntoId[battler] = chosenMonId; + } + BtlController_EmitChosenMonReturnValue(battler, BUFFER_B, chosenMonId, NULL); + PlayerPartnerBufferExecCompleted(battler); +} + +static void PlayerPartnerHandleHealthBarUpdate(u32 battler) +{ + BtlController_HandleHealthBarUpdate(battler, FALSE); +} + +static void PlayerPartnerHandleIntroTrainerBallThrow(u32 battler) +{ + const u32 *trainerPal; + + if (gPartnerTrainerId > TRAINER_PARTNER(PARTNER_NONE)) + trainerPal = gTrainerBackPicPaletteTable[gBattlePartners[gPartnerTrainerId - TRAINER_PARTNER(PARTNER_NONE)].trainerPic].data; + else if (IsAiVsAiBattle()) + trainerPal = gTrainerBackPicPaletteTable[GetTrainerPicFromId(gPartnerTrainerId)].data; + else + trainerPal = gTrainerBackPicPaletteTable[GetBattleTowerTrainerFrontSpriteId(gPartnerTrainerId)].data; // 2 vs 2 multi battle in Battle Frontier, load front sprite and pal. + + BtlController_HandleIntroTrainerBallThrow(battler, 0xD6F9, trainerPal, 24, Controller_PlayerPartnerShowIntroHealthbox, StartAnimLinearTranslation); +} + +static void PlayerPartnerHandleDrawPartyStatusSummary(u32 battler) +{ + BtlController_HandleDrawPartyStatusSummary(battler, B_SIDE_PLAYER, TRUE); +} + +static void PlayerPartnerHandleBattleAnimation(u32 battler) +{ + BtlController_HandleBattleAnimation(battler, FALSE); +} + +static void PlayerPartnerHandleEndLinkBattle(u32 battler) +{ + gBattleOutcome = gBattleResources->bufferA[battler][1]; + FadeOutMapMusic(5); + BeginFastPaletteFade(3); + PlayerPartnerBufferExecCompleted(battler); + gBattlerControllerFuncs[battler] = SetBattleEndCallbacks; +} diff --git a/src/battle_controllers.c b/src/battle_controllers.c index 3c0312887..c3f71bdec 100644 --- a/src/battle_controllers.c +++ b/src/battle_controllers.c @@ -129,9 +129,56 @@ static void InitSinglePlayerBtlControllers(void) if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER) { - // TODO: - DebugPrintfLevel(MGBA_LOG_ERROR, "BATTLE_TYPE_INGAME_PARTNER not implemented"); - return; + gBattleMainFunc = BeginBattleIntro; + + if (gBattleTypeFlags & BATTLE_TYPE_RECORDED) + { + gBattlerControllerFuncs[0] = SetControllerToRecordedPlayer; + gBattlerPositions[0] = B_POSITION_PLAYER_LEFT; + + gBattlerControllerFuncs[1] = SetControllerToOpponent; + gBattlerPositions[1] = B_POSITION_OPPONENT_LEFT; + + gBattlerControllerFuncs[2] = SetControllerToPlayerPartner; + gBattlerPositions[2] = B_POSITION_PLAYER_RIGHT; + + gBattlerControllerFuncs[3] = SetControllerToOpponent; + gBattlerPositions[3] = B_POSITION_OPPONENT_RIGHT; + } + else + { + gBattlerControllerFuncs[0] = SetControllerToPlayer; + gBattlerPositions[0] = B_POSITION_PLAYER_LEFT; + + gBattlerControllerFuncs[1] = SetControllerToOpponent; + gBattlerPositions[1] = B_POSITION_OPPONENT_LEFT; + + gBattlerControllerFuncs[2] = SetControllerToPlayerPartner; + gBattlerPositions[2] = B_POSITION_PLAYER_RIGHT; + + gBattlerControllerFuncs[3] = SetControllerToOpponent; + gBattlerPositions[3] = B_POSITION_OPPONENT_RIGHT; + } + + gBattlersCount = MAX_BATTLERS_COUNT; + + BufferBattlePartyCurrentOrderBySide(0, 0); + BufferBattlePartyCurrentOrderBySide(1, 0); + BufferBattlePartyCurrentOrderBySide(2, 1); + BufferBattlePartyCurrentOrderBySide(3, 1); + + gBattlerPartyIndexes[0] = 0; + gBattlerPartyIndexes[1] = 0; + if (BATTLE_TWO_VS_ONE_OPPONENT || WILD_DOUBLE_BATTLE) + { + gBattlerPartyIndexes[2] = 3; + gBattlerPartyIndexes[3] = 1; + } + else + { + gBattlerPartyIndexes[2] = 3; + gBattlerPartyIndexes[3] = 3; + } } else if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE)) { @@ -141,9 +188,8 @@ static void InitSinglePlayerBtlControllers(void) gBattlerControllerFuncs[0] = SetControllerToSafari; else if (gBattleTypeFlags & (BATTLE_TYPE_OLD_MAN_TUTORIAL | BATTLE_TYPE_FIRST_BATTLE)) gBattlerControllerFuncs[0] = SetControllerToOakOrOldMan; - // TODO: - // else if (IsAiVsAiBattle()) - // gBattlerControllerFuncs[0] = SetControllerToPlayerPartner; + else if (IsAiVsAiBattle()) + gBattlerControllerFuncs[0] = SetControllerToPlayerPartner; else gBattlerControllerFuncs[0] = SetControllerToPlayer; @@ -195,20 +241,18 @@ static void InitSinglePlayerBtlControllers(void) { gBattleMainFunc = BeginBattleIntro; - // TODO: Partner battle - // if (IsAiVsAiBattle()) - // gBattlerControllerFuncs[0] = SetControllerToPlayerPartner; - // else + if (IsAiVsAiBattle()) + gBattlerControllerFuncs[0] = SetControllerToPlayerPartner; + else gBattlerControllerFuncs[0] = SetControllerToPlayer; gBattlerPositions[0] = B_POSITION_PLAYER_LEFT; gBattlerControllerFuncs[1] = SetControllerToOpponent; gBattlerPositions[1] = B_POSITION_OPPONENT_LEFT; - // TODO: Partner battle - // if (IsAiVsAiBattle()) - // gBattlerControllerFuncs[2] = SetControllerToPlayerPartner; - // else + if (IsAiVsAiBattle()) + gBattlerControllerFuncs[2] = SetControllerToPlayerPartner; + else gBattlerControllerFuncs[2] = SetControllerToPlayer; gBattlerPositions[2] = B_POSITION_PLAYER_RIGHT; diff --git a/src/battle_main.c b/src/battle_main.c index a5956b50d..5209b3349 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -62,7 +62,9 @@ static void HandleEndTurn_MonFled(void); static void HandleEndTurn_FinishBattle(void); static void CB2_InitBattleInternal(void); static void CB2_PreInitMultiBattle(void); +static void CB2_PreInitIngamePlayerPartnerBattle(void); static void CB2_HandleStartMultiBattle(void); +static void CB2_HandleStartMultiPartnerBattle(void); static u8 CreateNPCTrainerParty(struct Pokemon *party, u16 trainerNum, bool8 firstTrainer); static void CB2_HandleStartBattle(void); static void TryCorrectShedinjaLanguage(struct Pokemon *mon); @@ -115,6 +117,7 @@ EWRAM_DATA u8 gBattleTextBuff3[TEXT_BUFF_ARRAY_COUNT + 13] = {0}; // expanded EWRAM_DATA u32 gBattleTypeFlags = 0; EWRAM_DATA u8 gBattleTerrain = 0; EWRAM_DATA struct MultiPartnerMenuPokemon gMultiPartnerParty[MULTI_PARTY_SIZE] = {0}; +EWRAM_DATA static struct MultiPartnerMenuPokemon* sMultiPartnerPartyBuffer = NULL; EWRAM_DATA u8 *gBattleAnimBgTileBuffer = NULL; EWRAM_DATA u8 *gBattleAnimBgTilemapBuffer = NULL; EWRAM_DATA u32 gBattleControllerExecFlags = 0; @@ -448,7 +451,7 @@ void CB2_InitBattle(void) } else { - // SetMainCallback2(CB2_PreInitIngamePlayerPartnerBattle); + SetMainCallback2(CB2_PreInitIngamePlayerPartnerBattle); } gBattleCommunication[MULTIUSE_STATE] = 0; } @@ -554,7 +557,11 @@ static void CB2_InitBattleInternal(void) SetVBlankCallback(VBlankCB_Battle); SetUpBattleVars(); - if (gBattleTypeFlags & BATTLE_TYPE_MULTI) + if (gBattleTypeFlags & BATTLE_TYPE_MULTI && gBattleTypeFlags & BATTLE_TYPE_BATTLE_TOWER) + SetMainCallback2(CB2_HandleStartMultiPartnerBattle); + else if (gBattleTypeFlags & BATTLE_TYPE_MULTI && gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER) + SetMainCallback2(CB2_HandleStartMultiPartnerBattle); + else if (gBattleTypeFlags & BATTLE_TYPE_MULTI) SetMainCallback2(CB2_HandleStartMultiBattle); else SetMainCallback2(CB2_HandleStartBattle); @@ -987,6 +994,35 @@ static void PrepareOwnMultiPartnerBuffer(void) memcpy(gBattleStruct->multiBuffer.multiBattleMons, gMultiPartnerParty, sizeof(gMultiPartnerParty)); } +static void SetMultiPartnerMenuParty(u8 offset) +{ + s32 i, j; + u8 *cur; + + for (i = 0; i < MULTI_PARTY_SIZE; i++) + { + gMultiPartnerParty[i].species = GetMonData(&gPlayerParty[offset + i], MON_DATA_SPECIES); + gMultiPartnerParty[i].heldItem = GetMonData(&gPlayerParty[offset + i], MON_DATA_HELD_ITEM); + GetMonData(&gPlayerParty[offset + i], MON_DATA_NICKNAME, gMultiPartnerParty[i].nickname); + gMultiPartnerParty[i].level = GetMonData(&gPlayerParty[offset + i], MON_DATA_LEVEL); + gMultiPartnerParty[i].hp = GetMonData(&gPlayerParty[offset + i], MON_DATA_HP); + gMultiPartnerParty[i].maxhp = GetMonData(&gPlayerParty[offset + i], MON_DATA_MAX_HP); + gMultiPartnerParty[i].status = GetMonData(&gPlayerParty[offset + i], MON_DATA_STATUS); + gMultiPartnerParty[i].personality = GetMonData(&gPlayerParty[offset + i], MON_DATA_PERSONALITY); + gMultiPartnerParty[i].gender = GetMonGender(&gPlayerParty[offset + i]); + StripExtCtrlCodes(gMultiPartnerParty[i].nickname); + if (GetMonData(&gPlayerParty[offset + i], MON_DATA_LANGUAGE) != LANGUAGE_JAPANESE) + { + for (cur = gMultiPartnerParty[i].nickname, j = 0; cur[j] != EOS; j++) + ; + while (j < 6) + cur[j++] = 0; + cur[j] = EOS; + } + } + memcpy(sMultiPartnerPartyBuffer, gMultiPartnerParty, sizeof(gMultiPartnerParty)); +} + static void CB2_PreInitMultiBattle(void) { s32 i; @@ -1062,41 +1098,41 @@ static void CB2_PreInitMultiBattle(void) } } -// static void CB2_PreInitIngamePlayerPartnerBattle(void) -// { -// u32 *savedBattleTypeFlags; -// void (**savedCallback)(void); +static void CB2_PreInitIngamePlayerPartnerBattle(void) +{ + u32 *savedBattleTypeFlags; + void (**savedCallback)(void); -// savedCallback = &gBattleStruct->savedCallback; -// savedBattleTypeFlags = &gBattleStruct->savedBattleTypeFlags; + savedCallback = &gBattleStruct->savedCallback; + savedBattleTypeFlags = &gBattleStruct->savedBattleTypeFlags; -// RunTasks(); -// AnimateSprites(); -// BuildOamBuffer(); + RunTasks(); + AnimateSprites(); + BuildOamBuffer(); -// switch (gBattleCommunication[MULTIUSE_STATE]) -// { -// case 0: -// sMultiPartnerPartyBuffer = Alloc(sizeof(gMultiPartnerParty)); -// SetMultiPartnerMenuParty(MULTI_PARTY_SIZE); -// gBattleCommunication[MULTIUSE_STATE]++; -// *savedCallback = gMain.savedCallback; -// *savedBattleTypeFlags = gBattleTypeFlags; -// gMain.savedCallback = CB2_PreInitIngamePlayerPartnerBattle; -// ShowPartyMenuToShowcaseMultiBattleParty(); -// break; -// case 1: -// if (!gPaletteFade.active) -// { -// gBattleCommunication[MULTIUSE_STATE] = 2; -// gBattleTypeFlags = *savedBattleTypeFlags; -// gMain.savedCallback = *savedCallback; -// SetMainCallback2(CB2_InitBattleInternal); -// FREE_AND_SET_NULL(sMultiPartnerPartyBuffer); -// } -// break; -// } -// } + switch (gBattleCommunication[MULTIUSE_STATE]) + { + case 0: + sMultiPartnerPartyBuffer = Alloc(sizeof(gMultiPartnerParty)); + SetMultiPartnerMenuParty(MULTI_PARTY_SIZE); + gBattleCommunication[MULTIUSE_STATE]++; + *savedCallback = gMain.savedCallback; + *savedBattleTypeFlags = gBattleTypeFlags; + gMain.savedCallback = CB2_PreInitIngamePlayerPartnerBattle; + ShowPartyMenuToShowcaseMultiBattleParty(); + break; + case 1: + if (!gPaletteFade.active) + { + gBattleCommunication[MULTIUSE_STATE] = 2; + gBattleTypeFlags = *savedBattleTypeFlags; + gMain.savedCallback = *savedCallback; + SetMainCallback2(CB2_InitBattleInternal); + FREE_AND_SET_NULL(sMultiPartnerPartyBuffer); + } + break; + } +} static void CB2_HandleStartMultiBattle(void) { @@ -1361,6 +1397,255 @@ static void CB2_HandleStartMultiBattle(void) } } +static void CB2_HandleStartMultiPartnerBattle(void) +{ + u8 playerMultiplayerId; + u8 partnerMultiplayerId; + + RunTasks(); + AnimateSprites(); + BuildOamBuffer(); + + playerMultiplayerId = GetMultiplayerId(); + gBattleScripting.multiplayerId = playerMultiplayerId; + partnerMultiplayerId = playerMultiplayerId ^ BIT_SIDE; + + switch (gBattleCommunication[MULTIUSE_STATE]) + { + case 0: + if (!IsDma3ManagerBusyWithBgCopy()) + { + ShowBg(0); + ShowBg(1); + ShowBg(2); + ShowBg(3); + BattleInterfaceSetWindowPals(); + gBattleCommunication[MULTIUSE_STATE] = 1; + } + if (gWirelessCommType) + LoadWirelessStatusIndicatorSpriteGfx(); + // fall through + case 1: + // if (gBattleTypeFlags & BATTLE_TYPE_LINK) + // { + // if (gReceivedRemoteLinkPlayers) + // { + // u8 language; + + // gLinkPlayers[0].id = 0; + // gLinkPlayers[1].id = 2; + // gLinkPlayers[2].id = 1; + // gLinkPlayers[3].id = 3; + // GetFrontierTrainerName(gLinkPlayers[2].name, gTrainerBattleOpponent_A); + // GetFrontierTrainerName(gLinkPlayers[3].name, gTrainerBattleOpponent_B); + // GetBattleTowerTrainerLanguage(&language, gTrainerBattleOpponent_A); + // gLinkPlayers[2].language = language; + // GetBattleTowerTrainerLanguage(&language, gTrainerBattleOpponent_B); + // gLinkPlayers[3].language = language; + + // if (IsLinkTaskFinished()) + // { + // // 0x300 + // *(&gBattleStruct->multiBuffer.linkBattlerHeader.versionSignatureLo) = 0; + // *(&gBattleStruct->multiBuffer.linkBattlerHeader.versionSignatureHi) = 3; + // BufferPartyVsScreenHealth_AtStart(); + // SetPlayerBerryDataInBattleStruct(); + // SendBlock(BitmaskAllOtherLinkPlayers(), &gBattleStruct->multiBuffer.linkBattlerHeader, sizeof(gBattleStruct->multiBuffer.linkBattlerHeader)); + // gBattleCommunication[MULTIUSE_STATE] = 2; + // } + + // if (gWirelessCommType) + // CreateWirelessStatusIndicatorSprite(0, 0); + // } + // } + // else + // { + if (!(gBattleTypeFlags & BATTLE_TYPE_RECORDED)) + gBattleTypeFlags |= BATTLE_TYPE_IS_MASTER; + gBattleCommunication[MULTIUSE_STATE] = 13; + SetAllPlayersBerryData(); + // } + break; + case 2: + if ((GetBlockReceivedStatus() & 3) == 3) + { + u8 taskId; + + ResetBlockReceivedFlags(); + LinkBattleComputeBattleTypeFlags(2, playerMultiplayerId); + SetAllPlayersBerryData(); + taskId = CreateTask(InitLinkBattleVsScreen, 0); + gTasks[taskId].data[1] = 0x10E; + gTasks[taskId].data[2] = 0x5A; + gTasks[taskId].data[5] = 0; + gTasks[taskId].data[3] = 0x145; + gTasks[taskId].data[4] = 0x145; + gBattleCommunication[MULTIUSE_STATE]++; + } + break; + case 3: + // Link battle, send/receive party Pokémon in groups + if (IsLinkTaskFinished()) + { + // Send Pokémon 1-2 + SendBlock(BitmaskAllOtherLinkPlayers(), gPlayerParty, sizeof(struct Pokemon) * 2); + gBattleCommunication[MULTIUSE_STATE]++; + } + break; + case 4: + if ((GetBlockReceivedStatus() & 3) == 3) + { + // Recv partner's Pokémon 1-2, and copy partner's and own Pokémon into party positions + ResetBlockReceivedFlags(); + if (gLinkPlayers[playerMultiplayerId].id != 0) + { + memcpy(gPlayerParty, gBlockRecvBuffer[partnerMultiplayerId], sizeof(struct Pokemon) * 2); + memcpy(&gPlayerParty[MULTI_PARTY_SIZE], gBlockRecvBuffer[playerMultiplayerId], sizeof(struct Pokemon) * 2); + } + else + { + memcpy(gPlayerParty, gBlockRecvBuffer[playerMultiplayerId], sizeof(struct Pokemon) * 2); + memcpy(&gPlayerParty[MULTI_PARTY_SIZE], gBlockRecvBuffer[partnerMultiplayerId], sizeof(struct Pokemon) * 2); + } + gBattleCommunication[MULTIUSE_STATE]++; + } + break; + case 5: + if (IsLinkTaskFinished()) + { + // Send Pokémon 3 + SendBlock(BitmaskAllOtherLinkPlayers(), &gPlayerParty[2], sizeof(struct Pokemon)); + gBattleCommunication[MULTIUSE_STATE]++; + } + break; + case 6: + if ((GetBlockReceivedStatus() & 3) == 3) + { + // Recv partner's Pokémon 3, and copy partner's and own Pokémon into party positions + ResetBlockReceivedFlags(); + if (gLinkPlayers[playerMultiplayerId].id != 0) + { + memcpy(&gPlayerParty[2], gBlockRecvBuffer[partnerMultiplayerId], sizeof(struct Pokemon)); + memcpy(&gPlayerParty[2 + MULTI_PARTY_SIZE], gBlockRecvBuffer[playerMultiplayerId], sizeof(struct Pokemon)); + } + else + { + memcpy(&gPlayerParty[2], gBlockRecvBuffer[playerMultiplayerId], sizeof(struct Pokemon)); + memcpy(&gPlayerParty[2 + MULTI_PARTY_SIZE], gBlockRecvBuffer[partnerMultiplayerId], sizeof(struct Pokemon)); + } + gBattleCommunication[MULTIUSE_STATE]++; + } + break; + case 7: + if (IsLinkTaskFinished()) + { + // Send enemy Pokémon 1-2 to partner + SendBlock(BitmaskAllOtherLinkPlayers(), gEnemyParty, sizeof(struct Pokemon) * 2); + gBattleCommunication[MULTIUSE_STATE]++; + } + break; + case 8: + if ((GetBlockReceivedStatus() & 3) == 3) + { + // Recv enemy Pokémon 1-2 (if not master) + ResetBlockReceivedFlags(); + if (GetMultiplayerId() != 0) + memcpy(gEnemyParty, gBlockRecvBuffer[0], sizeof(struct Pokemon) * 2); + gBattleCommunication[MULTIUSE_STATE]++; + } + break; + case 9: + if (IsLinkTaskFinished()) + { + // Send enemy Pokémon 3-4 to partner + SendBlock(BitmaskAllOtherLinkPlayers(), &gEnemyParty[2], sizeof(struct Pokemon) * 2); + gBattleCommunication[MULTIUSE_STATE]++; + } + break; + case 10: + if ((GetBlockReceivedStatus() & 3) == 3) + { + // Recv enemy Pokémon 3-4 (if not master) + ResetBlockReceivedFlags(); + if (GetMultiplayerId() != 0) + memcpy(&gEnemyParty[2], gBlockRecvBuffer[0], sizeof(struct Pokemon) * 2); + gBattleCommunication[MULTIUSE_STATE]++; + } + break; + case 11: + if (IsLinkTaskFinished()) + { + // Send enemy Pokémon 5-6 to partner + SendBlock(BitmaskAllOtherLinkPlayers(), &gEnemyParty[4], sizeof(struct Pokemon) * 2); + gBattleCommunication[MULTIUSE_STATE]++; + } + break; + case 12: + if ((GetBlockReceivedStatus() & 3) == 3) + { + // Recv enemy Pokémon 5-6 (if not master) + ResetBlockReceivedFlags(); + if (GetMultiplayerId() != 0) + memcpy(&gEnemyParty[4], gBlockRecvBuffer[0], sizeof(struct Pokemon) * 2); + + TryCorrectShedinjaLanguage(&gPlayerParty[0]); + TryCorrectShedinjaLanguage(&gPlayerParty[1]); + TryCorrectShedinjaLanguage(&gPlayerParty[2]); + TryCorrectShedinjaLanguage(&gPlayerParty[3]); + TryCorrectShedinjaLanguage(&gPlayerParty[4]); + TryCorrectShedinjaLanguage(&gPlayerParty[5]); + TryCorrectShedinjaLanguage(&gEnemyParty[0]); + TryCorrectShedinjaLanguage(&gEnemyParty[1]); + TryCorrectShedinjaLanguage(&gEnemyParty[2]); + TryCorrectShedinjaLanguage(&gEnemyParty[3]); + TryCorrectShedinjaLanguage(&gEnemyParty[4]); + TryCorrectShedinjaLanguage(&gEnemyParty[5]); + gBattleCommunication[MULTIUSE_STATE]++; + } + break; + case 13: + InitBattleControllers(); + RecordedBattle_SetTrainerInfo(); + gBattleCommunication[SPRITES_INIT_STATE1] = 0; + gBattleCommunication[SPRITES_INIT_STATE2] = 0; + if (gBattleTypeFlags & BATTLE_TYPE_LINK) + gBattleCommunication[MULTIUSE_STATE] = 14; + else + gBattleCommunication[MULTIUSE_STATE] = 16; + break; + case 14: + // Send rng seed for recorded battle + if (IsLinkTaskFinished()) + { + SendBlock(BitmaskAllOtherLinkPlayers(), &gRecordedBattleRngSeed, sizeof(gRecordedBattleRngSeed)); + gBattleCommunication[MULTIUSE_STATE]++; + } + break; + case 15: + // Receive rng seed for recorded battle (only read it if partner is the link master) + if ((GetBlockReceivedStatus() & 3) == 3) + { + ResetBlockReceivedFlags(); + if (!(gBattleTypeFlags & BATTLE_TYPE_IS_MASTER)) + memcpy(&gRecordedBattleRngSeed, gBlockRecvBuffer[partnerMultiplayerId], sizeof(gRecordedBattleRngSeed)); + gBattleCommunication[MULTIUSE_STATE]++; + } + break; + case 16: + // Finish, start battle + if (BattleInitAllSprites(&gBattleCommunication[SPRITES_INIT_STATE1], &gBattleCommunication[SPRITES_INIT_STATE2])) + { + // TrySetLinkBattleTowerEnemyPartyLevel(); + gPreBattleCallback1 = gMain.callback1; + gMain.callback1 = BattleMainCB1; + SetMainCallback2(BattleMainCB2); + if (gBattleTypeFlags & BATTLE_TYPE_LINK) + gBattleTypeFlags |= BATTLE_TYPE_LINK_IN_BATTLE; + } + break; + } +} + void BattleMainCB2(void) { AnimateSprites(); diff --git a/src/battle_message.c b/src/battle_message.c index ceb528dd0..7ca559bd6 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -2825,6 +2825,10 @@ static const u8 *BattleStringGetPlayerName(u8 *text, u8 battler) { toCpy = gLinkPlayers[2].name; } + else if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER) + { + toCpy = gBattlePartners[gPartnerTrainerId - TRAINER_PARTNER(PARTNER_NONE)].trainerName; + } else { toCpy = gSaveBlock2Ptr->playerName; @@ -2922,12 +2926,10 @@ u32 BattleStringExpandPlaceholders(const u8 *src, u8 *dst) case B_TXT_PARTNER_CLASS: // TODO: trainer name // toCpy = gTrainerClasses[GetFrontierOpponentClass(gPartnerTrainerId)].name; - toCpy = gText_TODO; + toCpy = gTrainerClasses[gBattlePartners[gPartnerTrainerId - TRAINER_PARTNER(PARTNER_NONE)].trainerClass].name;; break; case B_TXT_PARTNER_NAME: - // TODO: trainer name - // toCpy = BattleStringGetPlayerName(text, GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT)); - toCpy = gText_TODO; + toCpy = BattleStringGetPlayerName(text, GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT)); break; case B_TXT_COPY_VAR_1: toCpy = gStringVar1; diff --git a/src/battle_setup.c b/src/battle_setup.c index 6bb9bf32e..7bf99acd3 100644 --- a/src/battle_setup.c +++ b/src/battle_setup.c @@ -1,5 +1,6 @@ #include "global.h" #include "task.h" +#include "battle_setup.h" #include "help_system.h" #include "overworld.h" #include "item.h" @@ -62,8 +63,6 @@ static void DoSafariBattle(void); static void DoGhostBattle(void); static void DoStandardWildBattle(bool32 isDouble); static void CB2_EndWildBattle(void); -static u8 GetWildBattleTransition(void); -static u8 GetTrainerBattleTransition(void); static void CB2_EndScriptedWildBattle(void); static void CB2_EndMarowakBattle(void); static bool32 IsPlayerDefeated(u32 battleOutcome); @@ -681,7 +680,7 @@ static u8 GetSumOfEnemyPartyLevel(u16 opponentId, u8 numMons) return sum; } -static u8 GetWildBattleTransition(void) +u8 GetWildBattleTransition(void) { u8 transitionType = GetBattleTransitionTypeByMap(); u8 enemyLevel = GetMonData(&gEnemyParty[0], MON_DATA_LEVEL); @@ -693,7 +692,7 @@ static u8 GetWildBattleTransition(void) return sBattleTransitionTable_Wild[transitionType][1]; } -static u8 GetTrainerBattleTransition(void) +u8 GetTrainerBattleTransition(void) { u8 minPartyCount; u8 transitionType; @@ -880,7 +879,8 @@ static void SetMapVarsToTrainer(void) const u8 *BattleSetup_ConfigureTrainerBattle(const u8 *data) { - InitTrainerBattleVariables(); + if (TrainerBattleLoadArg8(data) != TRAINER_BATTLE_SET_TRAINER_B) + InitTrainerBattleVariables(); sTrainerBattleMode = TrainerBattleLoadArg8(data); switch (sTrainerBattleMode) { @@ -916,6 +916,12 @@ const u8 *BattleSetup_ConfigureTrainerBattle(const u8 *data) case TRAINER_BATTLE_EARLY_RIVAL: TrainerBattleLoadArgs(sEarlyRivalBattleParams, data); return EventScript_DoNoIntroTrainerBattle; + case TRAINER_BATTLE_SET_TRAINER_A: + TrainerBattleLoadArgs(sOrdinaryBattleParams, data); + return sTrainerBattleEndScript; + case TRAINER_BATTLE_SET_TRAINER_B: + TrainerBattleLoadArgs(sTrainerBOrdinaryBattleParams, data); + return sTrainerBattleEndScript; default: if (gApproachingTrainerId == 0) { diff --git a/src/battle_tower.c b/src/battle_tower.c index d09dc3b3f..b52738498 100644 --- a/src/battle_tower.c +++ b/src/battle_tower.c @@ -19,6 +19,7 @@ #include "save.h" #include "item.h" #include "script_pokemon_util.h" +#include "constants/abilities.h" #include "constants/items.h" #include "constants/moves.h" #include "constants/pokemon.h" @@ -984,7 +985,7 @@ void SetBattleTowerParty(void) for (i = 0; i < 3; i++) gSelectedOrderFromParty[i] = gSaveBlock2Ptr->battleTower.selectedPartyMons[i]; - ReducePlayerPartyToThree(); + ReducePlayerPartyToSelectedMons(); } static void SaveCurrentWinStreak(void) @@ -1292,3 +1293,165 @@ void Dummy_TryEnableBravoTrainerBattleTower(void) TakeBravoTrainerBattleTowerOffTheAir(); } } + +#define STEVEN_OTID 61226 + +static void FillPartnerParty(u16 trainerId) +{ + s32 i, j, k; + u32 firstIdPart = 0, secondIdPart = 0, thirdIdPart = 0; + u32 personality; + u32 otID; + u8 trainerName[(PLAYER_NAME_LENGTH * 3) + 1]; + s32 ball = -1; + + if (trainerId > TRAINER_PARTNER(PARTNER_NONE)) + { + for (i = 0; i < 3; i++) + ZeroMonData(&gPlayerParty[i + 3]); + + for (i = 0; i < 3 && i < gBattlePartners[trainerId - TRAINER_PARTNER(PARTNER_NONE)].partySize; i++) + { + const struct TrainerMon *partyData = gBattlePartners[trainerId - TRAINER_PARTNER(PARTNER_NONE)].party; + const u8 *partnerName = gBattlePartners[trainerId - TRAINER_PARTNER(PARTNER_NONE)].trainerName; + + for (k = 0; partnerName[k] != EOS && k < 3; k++) + { + if (k == 0) + { + firstIdPart = partnerName[k]; + secondIdPart = partnerName[k]; + thirdIdPart = partnerName[k]; + } + else if (k == 1) + { + secondIdPart = partnerName[k]; + thirdIdPart = partnerName[k]; + } + else if (k == 2) + { + thirdIdPart = partnerName[k]; + } + } + if (trainerId == TRAINER_PARTNER(PARTNER_STEVEN)) + otID = STEVEN_OTID; + else + otID = ((firstIdPart % 72) * 1000) + ((secondIdPart % 23) * 10) + (thirdIdPart % 37) % 65536; + + personality = Random32(); + if (partyData[i].gender == TRAINER_MON_MALE) + personality = (personality & 0xFFFFFF00) | GeneratePersonalityForGender(MON_MALE, partyData[i].species); + else if (partyData[i].gender == TRAINER_MON_FEMALE) + personality = (personality & 0xFFFFFF00) | GeneratePersonalityForGender(MON_FEMALE, partyData[i].species); + ModifyPersonalityForNature(&personality, partyData[i].nature); + CreateMon(&gPlayerParty[i + 3], partyData[i].species, partyData[i].lvl, 0, TRUE, personality, OT_ID_PRESET, otID); + j = partyData[i].isShiny; + SetMonData(&gPlayerParty[i + 3], MON_DATA_IS_SHINY, &j); + SetMonData(&gPlayerParty[i + 3], MON_DATA_HELD_ITEM, &partyData[i].heldItem); + CustomTrainerPartyAssignMoves(&gPlayerParty[i + 3], &partyData[i]); + + SetMonData(&gPlayerParty[i + 3], MON_DATA_IVS, &(partyData[i].iv)); + if (partyData[i].ev != NULL) + { + SetMonData(&gPlayerParty[i + 3], MON_DATA_HP_EV, &(partyData[i].ev[0])); + SetMonData(&gPlayerParty[i + 3], MON_DATA_ATK_EV, &(partyData[i].ev[1])); + SetMonData(&gPlayerParty[i + 3], MON_DATA_DEF_EV, &(partyData[i].ev[2])); + SetMonData(&gPlayerParty[i + 3], MON_DATA_SPATK_EV, &(partyData[i].ev[3])); + SetMonData(&gPlayerParty[i + 3], MON_DATA_SPDEF_EV, &(partyData[i].ev[4])); + SetMonData(&gPlayerParty[i + 3], MON_DATA_SPEED_EV, &(partyData[i].ev[5])); + } + if (partyData[i].ability != ABILITY_NONE) + { + const struct SpeciesInfo *speciesInfo = &gSpeciesInfo[partyData[i].species]; + u32 maxAbilities = ARRAY_COUNT(speciesInfo->abilities); + for (j = 0; j < maxAbilities; j++) + { + if (speciesInfo->abilities[j] == partyData[i].ability) + break; + } + if (j < maxAbilities) + SetMonData(&gPlayerParty[i + 3], MON_DATA_ABILITY_NUM, &j); + } + SetMonData(&gPlayerParty[i + 3], MON_DATA_FRIENDSHIP, &(partyData[i].friendship)); + if (partyData[i].ball != ITEM_NONE) + { + ball = partyData[i].ball; + SetMonData(&gPlayerParty[i + 3], MON_DATA_POKEBALL, &ball); + } + if (partyData[i].nickname != NULL) + { + SetMonData(&gPlayerParty[i + 3], MON_DATA_NICKNAME, partyData[i].nickname); + } + CalculateMonStats(&gPlayerParty[i + 3]); + + StringCopy(trainerName, gBattlePartners[trainerId - TRAINER_PARTNER(PARTNER_NONE)].trainerName); + SetMonData(&gPlayerParty[i + 3], MON_DATA_OT_NAME, trainerName); + j = gBattlePartners[SanitizeTrainerId(trainerId - TRAINER_PARTNER(PARTNER_NONE))].encounterMusic_gender >> 7; + SetMonData(&gPlayerParty[i + 3], MON_DATA_OT_GENDER, &j); + } + } +} + +static void HandleSpecialTrainerBattleEnd(void) +{ + s32 i; + + switch (gBattleScripting.specialTrainerBattleType) + { + case SPECIAL_BATTLE_MULTI: + for (i = 0; i < 3; i++) + { + if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES)) + gSaveBlock1Ptr->playerParty[i] = gPlayerParty[i]; + } + break; + } + + SetMainCallback2(CB2_ReturnToFieldContinueScriptPlayMapMusic); +} + +static void Task_StartBattleAfterTransition(u8 taskId) +{ + if (IsBattleTransitionDone() == TRUE) + { + gMain.savedCallback = HandleSpecialTrainerBattleEnd; + SetMainCallback2(CB2_InitBattle); + DestroyTask(taskId); + } +} + +void DoSpecialTrainerBattle(void) +{ + gBattleScripting.specialTrainerBattleType = gSpecialVar_0x8004; + switch (gSpecialVar_0x8004) + { + case SPECIAL_BATTLE_MULTI: + if (gSpecialVar_0x8005 & MULTI_BATTLE_2_VS_WILD) // Player + AI against wild mon + { + gBattleTypeFlags = BATTLE_TYPE_DOUBLE | BATTLE_TYPE_MULTI | BATTLE_TYPE_INGAME_PARTNER; + } + else if (gSpecialVar_0x8005 & MULTI_BATTLE_2_VS_1) // Player + AI against one trainer + { + gTrainerBattleOpponent_B = 0xFFFF; + gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_DOUBLE | BATTLE_TYPE_MULTI | BATTLE_TYPE_INGAME_PARTNER; + } + else // MULTI_BATTLE_2_VS_2 + { + gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_DOUBLE | BATTLE_TYPE_TWO_OPPONENTS | BATTLE_TYPE_MULTI | BATTLE_TYPE_INGAME_PARTNER; + } + + gPartnerTrainerId = VarGet(gSpecialVar_0x8006) + TRAINER_PARTNER(PARTNER_NONE); + FillPartnerParty(gPartnerTrainerId); + CreateTask(Task_StartBattleAfterTransition, 1); + PlayMapChosenOrBattleBGM(0); + if (gSpecialVar_0x8005 & MULTI_BATTLE_2_VS_WILD) + BattleTransition_StartOnField(GetWildBattleTransition()); + else + BattleTransition_StartOnField(GetTrainerBattleTransition()); + + if (gSpecialVar_0x8005 & MULTI_BATTLE_CHOOSE_MONS) // Skip mons restoring(done in the script) + gBattleScripting.specialTrainerBattleType = 0xFF; + break; + } + +} diff --git a/src/cable_club.c b/src/cable_club.c index dd486fb73..2f09bc2a8 100644 --- a/src/cable_club.c +++ b/src/cable_club.c @@ -656,7 +656,7 @@ static void Task_StartWiredCableClubBattle(u8 taskId) gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_LINK | BATTLE_TYPE_DOUBLE; break; case USING_MULTI_BATTLE: - ReducePlayerPartyToThree(); + ReducePlayerPartyToSelectedMons(); gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_LINK | BATTLE_TYPE_DOUBLE | BATTLE_TYPE_MULTI; break; } @@ -729,7 +729,7 @@ static void Task_StartWirelessCableClubBattle(u8 taskId) gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_LINK | BATTLE_TYPE_DOUBLE; break; case USING_MULTI_BATTLE: - ReducePlayerPartyToThree(); + ReducePlayerPartyToSelectedMons(); gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_LINK | BATTLE_TYPE_DOUBLE | BATTLE_TYPE_MULTI; break; } diff --git a/src/data.c b/src/data.c index 3e9fa4d2e..3a35c22b4 100644 --- a/src/data.c +++ b/src/data.c @@ -93,6 +93,14 @@ const struct SpriteFrameImage gTrainerBackPicTable_RSMay[] = {gTrainerBackPic_RSMay + 0x1800, 0x0800}, }; +const struct SpriteFrameImage gTrainerBackPicTable_Steven[] = +{ + {gTrainerBackPic_Steven + TRAINER_PIC_SIZE * 0, TRAINER_PIC_SIZE}, + {gTrainerBackPic_Steven + TRAINER_PIC_SIZE * 1, TRAINER_PIC_SIZE}, + {gTrainerBackPic_Steven + TRAINER_PIC_SIZE * 2, TRAINER_PIC_SIZE}, + {gTrainerBackPic_Steven + TRAINER_PIC_SIZE * 3, TRAINER_PIC_SIZE}, +}; + const union AnimCmd sAnim_GeneralFrame0[] = { ANIMCMD_FRAME(0, 0), diff --git a/src/data/battle_partners.h b/src/data/battle_partners.h index fa2c6cc40..5b14133c0 100644 --- a/src/data/battle_partners.h +++ b/src/data/battle_partners.h @@ -8,13 +8,61 @@ const struct Trainer gBattlePartners[] = { .trainerName = _(""), .items = {}, }, - - // [PARTNER_STEVEN] = - // { - // .party = TRAINER_PARTY(sParty_StevenPartner), - // .trainerClass = TRAINER_CLASS_RIVAL, - // .encounterMusic_gender = TRAINER_ENCOUNTER_MUSIC_MALE, - // .trainerPic = TRAINER_BACK_PIC_STEVEN, - // .trainerName = _("STEVEN"), - // }, + [PARTNER_STEVEN] = + { + .trainerName = _("STEVEN"), + .trainerClass = TRAINER_CLASS_RIVAL_LATE, + .trainerPic = TRAINER_BACK_PIC_STEVEN, + .encounterMusic_gender = + TRAINER_ENCOUNTER_MUSIC_MALE, + .partySize = 3, + .party = (const struct TrainerMon[]) + { + { + .species = SPECIES_METANG, + .gender = TRAINER_MON_RANDOM_GENDER, + .ev = TRAINER_PARTY_EVS(0, 252, 252, 0, 6, 0), + .iv = TRAINER_PARTY_IVS(31, 31, 31, 31, 31, 31), + .lvl = 42, + .nature = NATURE_BRAVE, + .dynamaxLevel = MAX_DYNAMAX_LEVEL, + .moves = { + MOVE_LIGHT_SCREEN, + MOVE_PSYCHIC, + MOVE_REFLECT, + MOVE_METAL_CLAW, + }, + }, + { + .species = SPECIES_SKARMORY, + .gender = TRAINER_MON_RANDOM_GENDER, + .ev = TRAINER_PARTY_EVS(252, 0, 0, 0, 6, 252), + .iv = TRAINER_PARTY_IVS(31, 31, 31, 31, 31, 31), + .lvl = 43, + .nature = NATURE_IMPISH, + .dynamaxLevel = MAX_DYNAMAX_LEVEL, + .moves = { + MOVE_TOXIC, + MOVE_AERIAL_ACE, + MOVE_PROTECT, + MOVE_STEEL_WING, + }, + }, + { + .species = SPECIES_AGGRON, + .gender = TRAINER_MON_RANDOM_GENDER, + .ev = TRAINER_PARTY_EVS(0, 252, 0, 0, 252, 6), + .iv = TRAINER_PARTY_IVS(31, 31, 31, 31, 31, 31), + .lvl = 44, + .nature = NATURE_ADAMANT, + .dynamaxLevel = MAX_DYNAMAX_LEVEL, + .moves = { + MOVE_THUNDER, + MOVE_PROTECT, + MOVE_SOLAR_BEAM, + MOVE_DRAGON_CLAW, + }, + }, + }, + }, }; diff --git a/src/data/graphics/trainers.h b/src/data/graphics/trainers.h index 0b4a21e75..2ef633284 100644 --- a/src/data/graphics/trainers.h +++ b/src/data/graphics/trainers.h @@ -446,6 +446,7 @@ const u8 gTrainerBackPic_Red[] = INCBIN_U8("graphics/trainers/back_pics/red_back const u8 gTrainerBackPic_Leaf[] = INCBIN_U8("graphics/trainers/back_pics/leaf_back_pic.4bpp"); const u8 gTrainerBackPic_Pokedude[] = INCBIN_U8("graphics/trainers/back_pics/pokedude_back_pic.4bpp"); const u8 gTrainerBackPic_OldMan[] = INCBIN_U8("graphics/trainers/back_pics/old_man_back_pic.4bpp"); +const u8 gTrainerBackPic_Steven[] = INCBIN_U8("graphics/trainers/back_pics/steven.4bpp"); const u8 gTrainerBackPic_RSBrendan[] = INCBIN_U8("graphics/trainers/back_pics/ruby_sapphire_brendan_back_pic.4bpp"); const u8 gTrainerBackPic_RSMay[] = INCBIN_U8("graphics/trainers/back_pics/ruby_sapphire_may_back_pic.4bpp"); @@ -454,3 +455,4 @@ const u32 gTrainerPalette_RedBackPic[] = INCBIN_U32("graphics/trainers/palettes/ const u32 gTrainerPalette_LeafBackPic[] = INCBIN_U32("graphics/trainers/palettes/leaf_back_pic.gbapal.lz"); const u32 gTrainerPalette_PokedudeBackPic[] = INCBIN_U32("graphics/trainers/palettes/pokedude_back_pic.gbapal.lz"); const u32 gTrainerPalette_OldManBackPic[] = INCBIN_U32("graphics/trainers/palettes/old_man_back_pic.gbapal.lz"); +const u32 gTrainerPalette_Steven[] = INCBIN_U32("graphics/trainers/palettes/steven.gbapal.lz"); diff --git a/src/data/trainer_graphics/back_pic_anims.h b/src/data/trainer_graphics/back_pic_anims.h index 833a0cb28..187761b27 100644 --- a/src/data/trainer_graphics/back_pic_anims.h +++ b/src/data/trainer_graphics/back_pic_anims.h @@ -50,6 +50,26 @@ static const union AnimCmd sAnimCmd_RSMay_1[] = { ANIMCMD_END }; +static const union AnimCmd sAnimCmd_Hoenn[] = +{ + ANIMCMD_FRAME(0, 24), + ANIMCMD_FRAME(1, 9), + ANIMCMD_FRAME(2, 24), + ANIMCMD_FRAME(0, 9), + ANIMCMD_FRAME(3, 50), + ANIMCMD_END, +}; + +static const union AnimCmd sAnimCmd_Point_HGSS[] = +{ + ANIMCMD_FRAME(3, 9), + ANIMCMD_FRAME(0, 9), + ANIMCMD_FRAME(2, 24), + ANIMCMD_FRAME(2, 24), + ANIMCMD_FRAME(3, 50), + ANIMCMD_END, +}; + const union AnimCmd *const sBackAnims_Red[] = { sAnim_GeneralFrame0, sAnimCmd_Red_1 @@ -80,11 +100,19 @@ const union AnimCmd *const sBackAnims_RSMay[] = { sAnimCmd_RSMay_1 }; +static const union AnimCmd *const sBackAnims_Hoenn[] = +{ + sAnim_GeneralFrame3, + sAnimCmd_Hoenn, + sAnimCmd_Point_HGSS, +}; + const union AnimCmd *const *const gTrainerBackAnimsPtrTable[] = { sBackAnims_Red, sBackAnims_Leaf, sBackAnims_RSBrendan, sBackAnims_RSMay, sBackAnims_Pokedude, - sBackAnims_OldMan + sBackAnims_OldMan, + sBackAnims_Hoenn, }; diff --git a/src/data/trainer_graphics/back_pic_tables.h b/src/data/trainer_graphics/back_pic_tables.h index 116dd02de..c7b86812c 100644 --- a/src/data/trainer_graphics/back_pic_tables.h +++ b/src/data/trainer_graphics/back_pic_tables.h @@ -4,7 +4,8 @@ const struct MonCoords gTrainerBackPicCoords[] = { [TRAINER_BACK_PIC_RUBY_SAPPHIRE_BRENDAN] = {.size = 8, .y_offset = 4}, [TRAINER_BACK_PIC_RUBY_SAPPHIRE_MAY] = {.size = 8, .y_offset = 4}, [TRAINER_BACK_PIC_POKEDUDE] = {.size = 8, .y_offset = 4}, - [TRAINER_BACK_PIC_OLD_MAN] = {.size = 8, .y_offset = 4} + [TRAINER_BACK_PIC_OLD_MAN] = {.size = 8, .y_offset = 4}, + [TRAINER_BACK_PIC_STEVEN] = {.size = 8, .y_offset = 4}, }; const struct CompressedSpriteSheet gTrainerBackPicTable[] = { @@ -13,7 +14,8 @@ const struct CompressedSpriteSheet gTrainerBackPicTable[] = { { (const u32 *)gTrainerBackPic_RSBrendan, 0x2000, 2 }, { (const u32 *)gTrainerBackPic_RSMay, 0x2000, 3 }, { (const u32 *)gTrainerBackPic_Pokedude, 0x2000, 4 }, - { (const u32 *)gTrainerBackPic_OldMan, 0x2000, 5 } + { (const u32 *)gTrainerBackPic_OldMan, 0x2000, 5 }, + { (const u32 *)gTrainerBackPic_Steven, 0x2000, 6 }, }; const struct CompressedSpritePalette gTrainerBackPicPaletteTable[] = { @@ -22,5 +24,6 @@ const struct CompressedSpritePalette gTrainerBackPicPaletteTable[] = { { gTrainerPalette_RSBrendan1, 2 }, { gTrainerPalette_RSMay1, 3 }, { gTrainerPalette_PokedudeBackPic, 4 }, - { gTrainerPalette_OldManBackPic, 5 } + { gTrainerPalette_OldManBackPic, 5 }, + { gTrainerPalette_Steven, 6 }, }; diff --git a/src/party_menu.c b/src/party_menu.c index 3cc540d65..6e545e396 100644 --- a/src/party_menu.c +++ b/src/party_menu.c @@ -427,7 +427,7 @@ EWRAM_DATA MainCallback gPostMenuFieldCallback = NULL; static EWRAM_DATA u16 *sSlot1TilemapBuffer = NULL; // for switching party slots static EWRAM_DATA u16 *sSlot2TilemapBuffer = NULL; static EWRAM_DATA struct Pokemon *sSacredAshQuestLogMonBackup = NULL; -EWRAM_DATA u8 gSelectedOrderFromParty[3] = {0}; +EWRAM_DATA u8 gSelectedOrderFromParty[MAX_FRONTIER_PARTY_SIZE] = {0}; static EWRAM_DATA u16 sPartyMenuItemId = ITEM_NONE; ALIGNED(4) EWRAM_DATA u8 gBattlePartyCurrentOrder[PARTY_SIZE / 2] = {0}; // bits 0-3 are the current pos of Slot 1, 4-7 are Slot 2, and so on @@ -1226,7 +1226,7 @@ static void DrawCancelConfirmButtons(void) bool8 IsMultiBattle(void) { - if (gBattleTypeFlags & BATTLE_TYPE_MULTI && gBattleTypeFlags & BATTLE_TYPE_DOUBLE && gBattleTypeFlags & BATTLE_TYPE_TRAINER && gBattleTypeFlags & BATTLE_TYPE_LINK) + if (gBattleTypeFlags & BATTLE_TYPE_MULTI && gBattleTypeFlags & BATTLE_TYPE_DOUBLE && gMain.inBattle) return TRUE; else return FALSE; diff --git a/src/pokeball.c b/src/pokeball.c index d9b1897b3..93ba21376 100644 --- a/src/pokeball.c +++ b/src/pokeball.c @@ -546,7 +546,7 @@ u8 DoPokeballSendOutAnimation(u32 battler, s16 pan, u8 kindOfThrow) static void Task_DoPokeballSendOutAnim(u8 taskId) { - u32 throwCaseId, battlerId, ballId, ballSpriteId; + u32 throwCaseId, ballId, battlerId, ballSpriteId; bool32 notSendOut = FALSE; u32 throwXoffset = (B_ENEMY_THROW_BALLS >= GEN_6) ? 24 : 0; s32 throwYoffset = (B_ENEMY_THROW_BALLS >= GEN_6) ? -16 : 24; @@ -990,7 +990,7 @@ static void SpriteCB_ReleaseMonFromBall(struct Sprite *sprite) if ((battlerId == GetBattlerAtPosition(B_POSITION_PLAYER_LEFT) || battlerId == GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT)) && IsDoubleBattle() && gBattleSpritesDataPtr->animationData->introAnimActive) { - if (gBattleTypeFlags & BATTLE_TYPE_MULTI) + if (gBattleTypeFlags & BATTLE_TYPE_MULTI && gBattleTypeFlags & BATTLE_TYPE_LINK) { if (IsBGMPlaying()) m4aMPlayStop(&gMPlayInfo_BGM); @@ -1033,6 +1033,7 @@ static void SpriteCB_ReleaseMonFromBall(struct Sprite *sprite) gSprites[gBattlerSpriteIds[sprite->sBattler]].callback = SpriteCB_OpponentMonFromBall; else gSprites[gBattlerSpriteIds[sprite->sBattler]].callback = SpriteCB_PlayerMonFromBall; + AnimateSprite(&gSprites[gBattlerSpriteIds[sprite->sBattler]]); gSprites[gBattlerSpriteIds[sprite->sBattler]].data[1] = 0x1000; } @@ -1208,6 +1209,8 @@ static void SpriteCB_MonSendOut_2(struct Sprite *sprite) } } +#undef HIBYTE + static void SpriteCB_ReleaseMon2FromBall(struct Sprite *sprite) { if (sprite->data[0]++ > 24) diff --git a/src/pokemon.c b/src/pokemon.c index 59e11332b..73693e71d 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -993,6 +993,15 @@ static const struct SpriteTemplate sTrainerBackSpriteTemplates[] = .affineAnims = gAffineAnims_BattleSpritePlayerSide, .callback = SpriteCB_AllyMon, }, + [TRAINER_BACK_PIC_STEVEN] = { + .tileTag = TAG_NONE, + .paletteTag = 0, + .oam = &gOamData_BattlerPlayer, + .anims = NULL, + .images = gTrainerBackPicTable_Steven, + .affineAnims = gAffineAnims_BattleSpritePlayerSide, + .callback = SpriteCB_AllyMon, + }, }; // Classes dummied out @@ -5685,10 +5694,17 @@ bool8 IsMonShiny(struct Pokemon *mon) return GetMonData(mon, MON_DATA_IS_SHINY, NULL); } -u8 *GetTrainerPartnerName(void) +const u8 *GetTrainerPartnerName(void) { - u8 id = GetMultiplayerId(); - return gLinkPlayers[GetBattlerMultiplayerId(gLinkPlayers[id].id ^ 2)].name; + if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER) + { + return GetTrainerNameFromId(gPartnerTrainerId); + } + else + { + u8 id = GetMultiplayerId(); + return gLinkPlayers[GetBattlerMultiplayerId(gLinkPlayers[id].id ^ 2)].name; + } } #define READ_PTR_FROM_TASK(taskId, dataId) \ @@ -5714,16 +5730,6 @@ static void Task_AnimateAfterDelay(u8 taskId) } } -// static void Task_PokemonSummaryAnimateAfterDelay(u8 taskId) -// { -// if (--gTasks[taskId].sAnimDelay == 0) -// { -// StartMonSummaryAnimation(READ_PTR_FROM_TASK(taskId, 0), gTasks[taskId].sAnimId); -// SummaryScreen_SetAnimDelayTaskId(TASK_NONE); -// DestroyTask(taskId); -// } -// } - void BattleAnimateFrontSprite(struct Sprite *sprite, u16 species, bool8 noCry, u8 panMode) { if (gHitMarker & HITMARKER_NO_ANIMATIONS && !(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK))) @@ -5779,34 +5785,6 @@ void DoMonFrontSpriteAnimation(struct Sprite *sprite, u16 species, bool8 noCry, } } -// void PokemonSummaryDoMonAnimation(struct Sprite *sprite, u16 species, bool8 oneFrame) -// { -// if (!oneFrame && HasTwoFramesAnimation(species)) -// StartSpriteAnim(sprite, 1); -// if (gSpeciesInfo[species].frontAnimDelay != 0) -// { -// // Animation has delay, start delay task -// u8 taskId = CreateTask(Task_PokemonSummaryAnimateAfterDelay, 0); -// STORE_PTR_IN_TASK(sprite, taskId, 0); -// gTasks[taskId].sAnimId = gSpeciesInfo[species].frontAnimId; -// gTasks[taskId].sAnimDelay = gSpeciesInfo[species].frontAnimDelay; -// SummaryScreen_SetAnimDelayTaskId(taskId); -// SetSpriteCB_MonAnimDummy(sprite); -// } -// else -// { -// // No delay, start animation -// StartMonSummaryAnimation(sprite, gSpeciesInfo[species].frontAnimId); -// } -// } - -// void StopPokemonAnimationDelayTask(void) -// { -// u8 delayTaskId = FindTaskIdByFunc(Task_PokemonSummaryAnimateAfterDelay); -// if (delayTaskId != TASK_NONE) -// DestroyTask(delayTaskId); -// } - void BattleAnimateBackSprite(struct Sprite *sprite, u16 species) { if (gHitMarker & HITMARKER_NO_ANIMATIONS && !(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK))) diff --git a/src/script_pokemon_util.c b/src/script_pokemon_util.c index 9a57a1393..57014832c 100644 --- a/src/script_pokemon_util.c +++ b/src/script_pokemon_util.c @@ -474,7 +474,7 @@ static void CB2_ReturnFromChooseBattleTowerParty(void) gSpecialVar_Result = FALSE; break; default: - ReducePlayerPartyToThree(); + ReducePlayerPartyToSelectedMons(); gSpecialVar_Result = TRUE; break; } @@ -482,24 +482,25 @@ static void CB2_ReturnFromChooseBattleTowerParty(void) SetMainCallback2(CB2_ReturnToFieldContinueScriptPlayMapMusic); } -void ReducePlayerPartyToThree(void) +void ReducePlayerPartyToSelectedMons(void) { - struct Pokemon * party = AllocZeroed(3 * sizeof(struct Pokemon)); + struct Pokemon party[MAX_FRONTIER_PARTY_SIZE]; int i; - // copy the selected pokemon according to the order. - for (i = 0; i < 3; i++) + CpuFill32(0, party, sizeof party); + + // copy the selected Pokémon according to the order. + for (i = 0; i < MAX_FRONTIER_PARTY_SIZE; i++) if (gSelectedOrderFromParty[i]) // as long as the order keeps going (did the player select 1 mon? 2? 3?), do not stop party[i] = gPlayerParty[gSelectedOrderFromParty[i] - 1]; // index is 0 based, not literal CpuFill32(0, gPlayerParty, sizeof gPlayerParty); - // overwrite the first 3 with the order copied to. - for (i = 0; i < 3; i++) + // overwrite the first 4 with the order copied to. + for (i = 0; i < MAX_FRONTIER_PARTY_SIZE; i++) gPlayerParty[i] = party[i]; CalculatePlayerPartyCount(); - Free(party); } void Script_GetChosenMonOffensiveEVs(void)