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 000000000..961992ff6 Binary files /dev/null and b/graphics/trainers/back_pics/steven.png differ diff --git a/graphics/trainers/palettes/steven.pal b/graphics/trainers/palettes/steven.pal new file mode 100644 index 000000000..bb2fedd5f --- /dev/null +++ b/graphics/trainers/palettes/steven.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +115 197 164 +255 222 205 +222 180 148 +189 148 115 +123 90 82 +189 115 205 +164 82 172 +82 82 90 +49 49 57 +213 222 238 +172 189 213 +123 148 164 +255 98 90 +197 65 65 +255 255 255 +0 0 0 diff --git a/include/battle_controllers.h b/include/battle_controllers.h index a01fed44a..9b2db242a 100644 --- a/include/battle_controllers.h +++ b/include/battle_controllers.h @@ -330,6 +330,10 @@ void SetControllerToRecordedPlayer(u32 battler); void SetControllerToOpponent(u32 battler); void OpponentBufferExecCompleted(u32 battler); +// player partner controller +void Controller_PlayerPartnerShowIntroHealthbox(u32 battler); // Also used by the link partner. +void SetControllerToPlayerPartner(u32 battler); + // safari controller void SetControllerToSafari(u32 battler); diff --git a/include/battle_main.h b/include/battle_main.h index 9a84f1ffc..9e9f06e05 100644 --- a/include/battle_main.h +++ b/include/battle_main.h @@ -118,6 +118,9 @@ void SetTypeBeforeUsingMove(u32 move, u32 battlerAtk); bool32 IsWildMonSmart(void); u8 CreateNPCTrainerPartyFromTrainer(struct Pokemon *party, const struct Trainer *trainer, bool32 firstTrainer, u32 battleTypeFlags); void ModifyPersonalityForNature(u32 *personality, u32 newNature); +u32 GeneratePersonalityForGender(u32 gender, u32 species); +void CustomTrainerPartyAssignMoves(struct Pokemon *mon, const struct TrainerMon *partyEntry); + extern const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES]; diff --git a/include/battle_setup.h b/include/battle_setup.h index 7e1c140b5..356bc5e05 100644 --- a/include/battle_setup.h +++ b/include/battle_setup.h @@ -42,5 +42,7 @@ const u8 *GetTrainerALoseText(void); const u8 *GetTrainerBLoseText(void); const u8 *GetTrainerWonSpeech(void); void BattleSetup_StartTrainerBattle_Debug(void); +u8 GetWildBattleTransition(void); +u8 GetTrainerBattleTransition(void); #endif // GUARD_BATTLE_SETUP_H diff --git a/include/battle_tower.h b/include/battle_tower.h index f10f21fa7..306f3b0b0 100644 --- a/include/battle_tower.h +++ b/include/battle_tower.h @@ -35,5 +35,6 @@ void CopyEReaderTrainerName5(u8 *dest); void GetBattleTowerTrainerName(u8 *text); u8 GetEreaderTrainerClassId(void); u8 GetBattleTowerTrainerClassNameId(void); +void DoSpecialTrainerBattle(void); #endif //GUARD_BATTLE_TOWER_H diff --git a/include/constants/battle_partner.h b/include/constants/battle_partner.h index e4f496df6..5622349a8 100644 --- a/include/constants/battle_partner.h +++ b/include/constants/battle_partner.h @@ -3,6 +3,6 @@ #define GUARD_CONSTANTS_BATTLE_PARTNERS_H #define PARTNER_NONE 0 -// #define PARTNER_STEVEN 1 +#define PARTNER_STEVEN 1 #endif // GUARD_CONSTANTS_BATTLE_PARTNERS_H diff --git a/include/constants/battle_setup.h b/include/constants/battle_setup.h index 1e804fd18..3067a521f 100644 --- a/include/constants/battle_setup.h +++ b/include/constants/battle_setup.h @@ -11,5 +11,7 @@ #define TRAINER_BATTLE_REMATCH_DOUBLE 7 #define TRAINER_BATTLE_CONTINUE_SCRIPT_DOUBLE_NO_MUSIC 8 #define TRAINER_BATTLE_EARLY_RIVAL 9 +#define TRAINER_BATTLE_SET_TRAINER_A 10 +#define TRAINER_BATTLE_SET_TRAINER_B 11 #endif // GUARD_CONSTANTS_BATTLE_SETUP_H diff --git a/include/constants/battle_tower.h b/include/constants/battle_tower.h index 6e37d15ec..241a569d5 100644 --- a/include/constants/battle_tower.h +++ b/include/constants/battle_tower.h @@ -79,4 +79,13 @@ #define BTSPECIAL_RESULT_QUICKSAVE 4 #define BTSPECIAL_RESULT_INACTIVE 5 +// Ids for special multi battle types +#define MULTI_BATTLE_2_VS_2 0 +#define MULTI_BATTLE_2_VS_WILD 1 +#define MULTI_BATTLE_2_VS_1 2 +#define MULTI_BATTLE_CHOOSE_MONS 0x80 + + +#define SPECIAL_BATTLE_MULTI 10 + #endif //GUARD_CONSTANTS_BATTLE_TOWER_H diff --git a/include/constants/global.h b/include/constants/global.h index 816a56da3..b5ca0179b 100644 --- a/include/constants/global.h +++ b/include/constants/global.h @@ -42,6 +42,12 @@ // party sizes #define PARTY_SIZE 6 #define MULTI_PARTY_SIZE (PARTY_SIZE / 2) +#define FRONTIER_PARTY_SIZE 3 +#define FRONTIER_DOUBLES_PARTY_SIZE 4 +#define FRONTIER_MULTI_PARTY_SIZE 2 +#define MAX_FRONTIER_PARTY_SIZE (max(FRONTIER_PARTY_SIZE, \ + max(FRONTIER_DOUBLES_PARTY_SIZE,\ + FRONTIER_MULTI_PARTY_SIZE))) // capacities of various saveblock objects #define DAYCARE_MON_COUNT 2 diff --git a/include/constants/trainers.h b/include/constants/trainers.h index 1f8850d7f..0a38c9f25 100644 --- a/include/constants/trainers.h +++ b/include/constants/trainers.h @@ -174,6 +174,7 @@ #define TRAINER_BACK_PIC_RUBY_SAPPHIRE_MAY 3 #define TRAINER_BACK_PIC_POKEDUDE 4 #define TRAINER_BACK_PIC_OLD_MAN 5 +#define TRAINER_BACK_PIC_STEVEN 6 // Special Trainer Ids. #define TRAINER_UNION_ROOM 0xC00 diff --git a/include/data.h b/include/data.h index 301ae281d..bd4e90c72 100644 --- a/include/data.h +++ b/include/data.h @@ -25,6 +25,10 @@ struct MonCoords #define MAX_TRAINER_ITEMS 4 +#define TRAINER_PIC_WIDTH 64 +#define TRAINER_PIC_HEIGHT 64 +#define TRAINER_PIC_SIZE (TRAINER_PIC_WIDTH * TRAINER_PIC_HEIGHT / 2) + enum { BATTLER_AFFINE_NORMAL, BATTLER_AFFINE_EMERGE, @@ -140,6 +144,7 @@ extern const struct SpriteFrameImage gTrainerBackPicTable_Pokedude[]; extern const struct SpriteFrameImage gTrainerBackPicTable_OldMan[]; extern const struct SpriteFrameImage gTrainerBackPicTable_RSBrendan[]; extern const struct SpriteFrameImage gTrainerBackPicTable_RSMay[]; +extern const struct SpriteFrameImage gTrainerBackPicTable_Steven[]; extern const union AnimCmd sAnim_GeneralFrame0[]; diff --git a/include/graphics.h b/include/graphics.h index f148284d8..e2a9643e4 100644 --- a/include/graphics.h +++ b/include/graphics.h @@ -2685,6 +2685,7 @@ extern const u32 gTrainerPalette_LeafBackPic[]; extern const u32 gTrainerPalette_RedBackPic[]; extern const u32 gTrainerPalette_PokedudeBackPic[]; extern const u32 gTrainerPalette_OldManBackPic[]; +extern const u32 gTrainerPalette_Steven[]; extern const u32 gTrainerPalette_RSPokemonBreederM[]; extern const u32 gTrainerPalette_RSPokemonBreederF[]; extern const u32 gTrainerPalette_RSPokemonRangerM[]; @@ -4935,6 +4936,7 @@ extern const u8 gTrainerBackPic_Pokedude[]; extern const u8 gTrainerBackPic_OldMan[]; extern const u8 gTrainerBackPic_RSBrendan[]; extern const u8 gTrainerBackPic_RSMay[]; +extern const u8 gTrainerBackPic_Steven[]; // mail extern const u16 gFile_graphics_mail_orange_palette_pal[]; diff --git a/include/party_menu.h b/include/party_menu.h index 894305391..de34393eb 100644 --- a/include/party_menu.h +++ b/include/party_menu.h @@ -23,7 +23,7 @@ extern struct PartyMenu gPartyMenu; extern bool8 gPartyMenuUseExitCallback; extern u8 gSelectedMonPartyId; extern MainCallback gPostMenuFieldCallback; -extern u8 gSelectedOrderFromParty[3]; +extern u8 gSelectedOrderFromParty[MAX_FRONTIER_PARTY_SIZE]; extern u8 gBattlePartyCurrentOrder[PARTY_SIZE / 2]; extern void (*gItemUseCB)(u8, TaskFunc); diff --git a/include/pokemon.h b/include/pokemon.h index 6ca390a48..b5b083398 100644 --- a/include/pokemon.h +++ b/include/pokemon.h @@ -832,7 +832,7 @@ void BoxMonRestorePP(struct BoxPokemon *boxMon); void SetMonPreventsSwitchingString(void); void SetWildMonHeldItem(void); bool8 IsMonShiny(struct Pokemon *mon); -u8 *GetTrainerPartnerName(void); +const u8 *GetTrainerPartnerName(void); void BattleAnimateFrontSprite(struct Sprite *sprite, u16 species, bool8 noCry, u8 panMode); void DoMonFrontSpriteAnimation(struct Sprite *sprite, u16 species, bool8 noCry, u8 panModeAnimFlag); void BattleAnimateBackSprite(struct Sprite *sprite, u16 species); diff --git a/include/script_pokemon_util.h b/include/script_pokemon_util.h index c42f39f7f..ae3b2c550 100644 --- a/include/script_pokemon_util.h +++ b/include/script_pokemon_util.h @@ -5,7 +5,7 @@ u32 ScriptGiveMon(u16 species, u8 level, u16 item); bool8 ScriptGiveEgg(u16 species); void ScriptSetMonMoveSlot(u8 partyIdx, u16 move, u8 slot); void HealPlayerParty(void); -void ReducePlayerPartyToThree(void); +void ReducePlayerPartyToSelectedMons(void); void CreateScriptedWildMon(u16 species, u8 level, u16 item); void CreateScriptedDoubleWildMon(u16 species1, u8 level1, u16 item1, u16 species2, u8 level2, u16 item2); diff --git a/src/battle_controller_link_opponent.c b/src/battle_controller_link_opponent.c index 59dbe5678..d04976fd7 100644 --- a/src/battle_controller_link_opponent.c +++ b/src/battle_controller_link_opponent.c @@ -122,45 +122,56 @@ static void Intro_DelayAndEnd(u32 battler) static void Intro_WaitForShinyAnimAndHealthbox(u32 battler) { bool32 healthboxAnimDone = FALSE; + bool32 twoMons = FALSE; if (!IsDoubleBattle() || (IsDoubleBattle() && (gBattleTypeFlags & BATTLE_TYPE_MULTI))) { if (gSprites[gHealthboxSpriteIds[battler]].callback == SpriteCallbackDummy) healthboxAnimDone = TRUE; } - else if (gSprites[gHealthboxSpriteIds[battler]].callback == SpriteCallbackDummy - && gSprites[gHealthboxSpriteIds[battler ^ BIT_FLANK]].callback == gSprites[gHealthboxSpriteIds[battler]].callback) + else { - healthboxAnimDone = TRUE; + if (gSprites[gHealthboxSpriteIds[battler]].callback == SpriteCallbackDummy + && gSprites[gHealthboxSpriteIds[BATTLE_PARTNER(battler)]].callback == SpriteCallbackDummy) + { + healthboxAnimDone = TRUE; + } + twoMons = TRUE; } - if (IsCryPlayingOrClearCrySongs()) - healthboxAnimDone = FALSE; - if (healthboxAnimDone) { - if (GetBattlerPosition(battler) == B_POSITION_OPPONENT_LEFT) + if (twoMons || !IsBattlerSpriteVisible(BATTLE_PARTNER(battler))) { - if (!gBattleSpritesDataPtr->healthBoxesData[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)