#include "global.h" #include "battle.h" #include "battle_ai_main.h" #include "battle_ai_util.h" #include "battle_anim.h" #include "battle_arena.h" #include "battle_controllers.h" #include "battle_gfx_sfx_util.h" #include "battle_interface.h" #include "battle_message.h" #include "battle_setup.h" #include "battle_tv.h" #include "cable_club.h" #include "event_object_movement.h" #include "link.h" #include "link_rfu.h" #include "palette.h" #include "party_menu.h" #include "recorded_battle.h" #include "string_util.h" #include "sound.h" #include "task.h" #include "test_runner.h" #include "util.h" #include "text.h" #include "constants/abilities.h" #include "constants/songs.h" static EWRAM_DATA u8 sLinkSendTaskId = 0; static EWRAM_DATA u8 sLinkReceiveTaskId = 0; EWRAM_DATA struct UnusedControllerStruct gUnusedControllerStruct = {}; // Debug? Unused code that writes to it, never read COMMON_DATA void (*gBattlerControllerFuncs[MAX_BATTLERS_COUNT])(u32 battler) = {0}; COMMON_DATA u8 gBattleControllerData[MAX_BATTLERS_COUNT] = {0}; // Used by the battle controllers to store misc sprite/task IDs for each battler COMMON_DATA void (*gBattlerControllerEndFuncs[MAX_BATTLERS_COUNT])(u32 battler) = {0}; // Controller's buffer complete function for each battler static void CreateTasksForSendRecvLinkBuffers(void); static void InitLinkBtlControllers(void); static void InitSinglePlayerBtlControllers(void); static void SetBattlePartyIds(void); static void Task_HandleSendLinkBuffersData(u8 taskId); static void Task_HandleCopyReceivedLinkBuffersData(u8 taskId); static void Task_StartSendOutAnim(u8 taskId); static void SpriteCB_FreePlayerSpriteLoadMonSprite(struct Sprite *sprite); static void SpriteCB_FreeOpponentSprite(struct Sprite *sprite); void HandleLinkBattleSetup(void) { if (gBattleTypeFlags & BATTLE_TYPE_LINK) { if (gWirelessCommType) SetWirelessCommType1(); if (!gReceivedRemoteLinkPlayers) OpenLink(); CreateTask(Task_WaitForLinkPlayerConnection, 0); CreateTasksForSendRecvLinkBuffers(); } } void SetUpBattleVarsAndBirchZigzagoon(void) { s32 i; gBattleMainFunc = BeginBattleIntroDummy; for (i = 0; i < MAX_BATTLERS_COUNT; i++) { gBattlerControllerFuncs[i] = BattleControllerDummy; gBattlerPositions[i] = 0xFF; gActionSelectionCursor[i] = 0; gMoveSelectionCursor[i] = 0; } HandleLinkBattleSetup(); gBattleControllerExecFlags = 0; ClearBattleAnimationVars(); BattleAI_SetupItems(); BattleAI_SetupFlags(); if (gBattleTypeFlags & BATTLE_TYPE_FIRST_BATTLE) { ZeroEnemyPartyMons(); CreateMon(&gEnemyParty[0], SPECIES_ZIGZAGOON, 2, USE_RANDOM_IVS, 0, 0, OT_ID_PLAYER_ID, 0); i = 0; SetMonData(&gEnemyParty[0], MON_DATA_HELD_ITEM, &i); } } void InitBattleControllers(void) { s32 i; if (!(gBattleTypeFlags & BATTLE_TYPE_RECORDED)) RecordedBattle_Init(B_RECORD_MODE_RECORDING); else RecordedBattle_Init(B_RECORD_MODE_PLAYBACK); if (!(gBattleTypeFlags & BATTLE_TYPE_RECORDED)) RecordedBattle_SaveParties(); if (gBattleTypeFlags & BATTLE_TYPE_LINK) InitLinkBtlControllers(); else InitSinglePlayerBtlControllers(); SetBattlePartyIds(); if (!(gBattleTypeFlags & BATTLE_TYPE_MULTI)) { for (i = 0; i < gBattlersCount; i++) BufferBattlePartyCurrentOrderBySide(i, 0); } for (i = 0; i < sizeof(gBattleStruct->tvMovePoints); i++) *((u8 *)(&gBattleStruct->tvMovePoints) + i) = 0; for (i = 0; i < sizeof(gBattleStruct->tv); i++) *((u8 *)(&gBattleStruct->tv) + i) = 0; } static void InitSinglePlayerBtlControllers(void) { s32 i; if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER) { 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 (!IsDoubleBattle()) { gBattleMainFunc = BeginBattleIntro; if (gBattleTypeFlags & BATTLE_TYPE_SAFARI) gBattlerControllerFuncs[0] = SetControllerToSafari; else if (gBattleTypeFlags & BATTLE_TYPE_WALLY_TUTORIAL) gBattlerControllerFuncs[0] = SetControllerToWally; 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; gBattlersCount = 2; if (gBattleTypeFlags & BATTLE_TYPE_RECORDED) { if (gBattleTypeFlags & BATTLE_TYPE_RECORDED_LINK) { if (gBattleTypeFlags & BATTLE_TYPE_RECORDED_IS_MASTER) { gBattleMainFunc = BeginBattleIntro; gBattlerControllerFuncs[0] = SetControllerToRecordedPlayer; gBattlerPositions[0] = B_POSITION_PLAYER_LEFT; gBattlerControllerFuncs[1] = SetControllerToRecordedOpponent; gBattlerPositions[1] = B_POSITION_OPPONENT_LEFT; gBattlersCount = 2; } else // see how the banks are switched { gBattlerControllerFuncs[1] = SetControllerToRecordedPlayer; gBattlerPositions[1] = B_POSITION_PLAYER_LEFT; gBattlerControllerFuncs[0] = SetControllerToRecordedOpponent; gBattlerPositions[0] = B_POSITION_OPPONENT_LEFT; gBattlersCount = 2; } } else { gBattlerControllerFuncs[0] = SetControllerToRecordedPlayer; gBattlerPositions[0] = B_POSITION_PLAYER_LEFT; gBattlerControllerFuncs[1] = SetControllerToOpponent; gBattlerPositions[1] = B_POSITION_OPPONENT_LEFT; } } } else { gBattleMainFunc = BeginBattleIntro; if (IsAiVsAiBattle()) gBattlerControllerFuncs[0] = SetControllerToPlayerPartner; else gBattlerControllerFuncs[0] = SetControllerToPlayer; gBattlerPositions[0] = B_POSITION_PLAYER_LEFT; gBattlerControllerFuncs[1] = SetControllerToOpponent; gBattlerPositions[1] = B_POSITION_OPPONENT_LEFT; if (IsAiVsAiBattle()) gBattlerControllerFuncs[2] = SetControllerToPlayerPartner; else gBattlerControllerFuncs[2] = SetControllerToPlayer; gBattlerPositions[2] = B_POSITION_PLAYER_RIGHT; gBattlerControllerFuncs[3] = SetControllerToOpponent; gBattlerPositions[3] = B_POSITION_OPPONENT_RIGHT; gBattlersCount = MAX_BATTLERS_COUNT; if (gBattleTypeFlags & BATTLE_TYPE_RECORDED) { if (gBattleTypeFlags & BATTLE_TYPE_MULTI && gBattleTypeFlags & BATTLE_TYPE_BATTLE_TOWER) { gBattleMainFunc = BeginBattleIntro; gBattlerControllerFuncs[0] = SetControllerToRecordedPlayer; gBattlerPositions[0] = B_POSITION_PLAYER_LEFT; gBattlerControllerFuncs[1] = SetControllerToOpponent; gBattlerPositions[1] = B_POSITION_OPPONENT_LEFT; gBattlerControllerFuncs[2] = SetControllerToRecordedPlayer; 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; gBattlerPartyIndexes[2] = 3; gBattlerPartyIndexes[3] = 3; } else if (gBattleTypeFlags & BATTLE_TYPE_MULTI) { u8 multiplayerId; for (multiplayerId = gRecordedBattleMultiplayerId, i = 0; i < MAX_BATTLERS_COUNT; i++) { switch (gLinkPlayers[i].id) { case 0: case 3: BufferBattlePartyCurrentOrderBySide(gLinkPlayers[i].id, 0); break; case 1: case 2: BufferBattlePartyCurrentOrderBySide(gLinkPlayers[i].id, 1); break; } if (i == multiplayerId) { gBattlerControllerFuncs[gLinkPlayers[i].id] = SetControllerToRecordedPlayer; switch (gLinkPlayers[i].id) { case 0: case 3: gBattlerPositions[gLinkPlayers[i].id] = B_POSITION_PLAYER_LEFT; gBattlerPartyIndexes[gLinkPlayers[i].id] = 0; break; case 1: case 2: gBattlerPositions[gLinkPlayers[i].id] = B_POSITION_PLAYER_RIGHT; gBattlerPartyIndexes[gLinkPlayers[i].id] = 3; break; } } else if ((!(gLinkPlayers[i].id & 1) && !(gLinkPlayers[multiplayerId].id & 1)) || ((gLinkPlayers[i].id & 1) && (gLinkPlayers[multiplayerId].id & 1))) { gBattlerControllerFuncs[gLinkPlayers[i].id] = SetControllerToRecordedPlayer; switch (gLinkPlayers[i].id) { case 0: case 3: gBattlerPositions[gLinkPlayers[i].id] = B_POSITION_PLAYER_LEFT; gBattlerPartyIndexes[gLinkPlayers[i].id] = 0; break; case 1: case 2: gBattlerPositions[gLinkPlayers[i].id] = B_POSITION_PLAYER_RIGHT; gBattlerPartyIndexes[gLinkPlayers[i].id] = 3; break; } } else { gBattlerControllerFuncs[gLinkPlayers[i].id] = SetControllerToRecordedOpponent; switch (gLinkPlayers[i].id) { case 0: case 3: gBattlerPositions[gLinkPlayers[i].id] = B_POSITION_OPPONENT_LEFT; gBattlerPartyIndexes[gLinkPlayers[i].id] = 0; break; case 1: case 2: gBattlerPositions[gLinkPlayers[i].id] = B_POSITION_OPPONENT_RIGHT; gBattlerPartyIndexes[gLinkPlayers[i].id] = 3; break; } } } } else if (gBattleTypeFlags & BATTLE_TYPE_IS_MASTER) { gBattlerControllerFuncs[0] = SetControllerToRecordedPlayer; gBattlerPositions[0] = B_POSITION_PLAYER_LEFT; gBattlerControllerFuncs[2] = SetControllerToRecordedPlayer; gBattlerPositions[2] = B_POSITION_PLAYER_RIGHT; if (gBattleTypeFlags & BATTLE_TYPE_RECORDED_LINK) { gBattlerControllerFuncs[1] = SetControllerToRecordedOpponent; gBattlerPositions[1] = B_POSITION_OPPONENT_LEFT; gBattlerControllerFuncs[3] = SetControllerToRecordedOpponent; gBattlerPositions[3] = B_POSITION_OPPONENT_RIGHT; } else { gBattlerControllerFuncs[1] = SetControllerToOpponent; gBattlerPositions[1] = B_POSITION_OPPONENT_LEFT; gBattlerControllerFuncs[3] = SetControllerToOpponent; gBattlerPositions[3] = B_POSITION_OPPONENT_RIGHT; } } else { gBattlerControllerFuncs[1] = SetControllerToRecordedPlayer; gBattlerPositions[1] = B_POSITION_PLAYER_LEFT; gBattlerControllerFuncs[3] = SetControllerToRecordedPlayer; gBattlerPositions[3] = B_POSITION_PLAYER_RIGHT; if (gBattleTypeFlags & BATTLE_TYPE_RECORDED_LINK) { gBattlerControllerFuncs[0] = SetControllerToRecordedOpponent; gBattlerPositions[0] = B_POSITION_OPPONENT_LEFT; gBattlerControllerFuncs[2] = SetControllerToRecordedOpponent; gBattlerPositions[2] = B_POSITION_OPPONENT_RIGHT; } else { gBattlerControllerFuncs[0] = SetControllerToOpponent; gBattlerPositions[0] = B_POSITION_OPPONENT_LEFT; gBattlerControllerFuncs[2] = SetControllerToOpponent; gBattlerPositions[2] = B_POSITION_OPPONENT_RIGHT; } } } } } static void InitLinkBtlControllers(void) { s32 i; u8 multiplayerId; if (!IsDoubleBattle()) { if (gBattleTypeFlags & BATTLE_TYPE_IS_MASTER) { gBattleMainFunc = BeginBattleIntro; gBattlerControllerFuncs[0] = SetControllerToPlayer; gBattlerPositions[0] = B_POSITION_PLAYER_LEFT; gBattlerControllerFuncs[1] = SetControllerToLinkOpponent; gBattlerPositions[1] = B_POSITION_OPPONENT_LEFT; gBattlersCount = 2; } else { gBattlerControllerFuncs[1] = SetControllerToPlayer; gBattlerPositions[1] = B_POSITION_PLAYER_LEFT; gBattlerControllerFuncs[0] = SetControllerToLinkOpponent; gBattlerPositions[0] = B_POSITION_OPPONENT_LEFT; gBattlersCount = 2; } } else if (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && IsDoubleBattle()) { if (gBattleTypeFlags & BATTLE_TYPE_IS_MASTER) { gBattleMainFunc = BeginBattleIntro; gBattlerControllerFuncs[0] = SetControllerToPlayer; gBattlerPositions[0] = B_POSITION_PLAYER_LEFT; gBattlerControllerFuncs[1] = SetControllerToLinkOpponent; gBattlerPositions[1] = B_POSITION_OPPONENT_LEFT; gBattlerControllerFuncs[2] = SetControllerToPlayer; gBattlerPositions[2] = B_POSITION_PLAYER_RIGHT; gBattlerControllerFuncs[3] = SetControllerToLinkOpponent; gBattlerPositions[3] = B_POSITION_OPPONENT_RIGHT; gBattlersCount = MAX_BATTLERS_COUNT; } else { gBattlerControllerFuncs[1] = SetControllerToPlayer; gBattlerPositions[1] = B_POSITION_PLAYER_LEFT; gBattlerControllerFuncs[0] = SetControllerToLinkOpponent; gBattlerPositions[0] = B_POSITION_OPPONENT_LEFT; gBattlerControllerFuncs[3] = SetControllerToPlayer; gBattlerPositions[3] = B_POSITION_PLAYER_RIGHT; gBattlerControllerFuncs[2] = SetControllerToLinkOpponent; gBattlerPositions[2] = B_POSITION_OPPONENT_RIGHT; gBattlersCount = MAX_BATTLERS_COUNT; } } else if (gBattleTypeFlags & BATTLE_TYPE_BATTLE_TOWER) { if (gBattleTypeFlags & BATTLE_TYPE_IS_MASTER) { gBattleMainFunc = BeginBattleIntro; gBattlerControllerFuncs[0] = SetControllerToPlayer; gBattlerPositions[0] = B_POSITION_PLAYER_LEFT; gBattlerControllerFuncs[1] = SetControllerToOpponent; gBattlerPositions[1] = B_POSITION_OPPONENT_LEFT; gBattlerControllerFuncs[2] = SetControllerToLinkPartner; gBattlerPositions[2] = B_POSITION_PLAYER_RIGHT; gBattlerControllerFuncs[3] = SetControllerToOpponent; gBattlerPositions[3] = B_POSITION_OPPONENT_RIGHT; gBattlersCount = MAX_BATTLERS_COUNT; } else { gBattlerControllerFuncs[0] = SetControllerToLinkPartner; gBattlerPositions[0] = B_POSITION_PLAYER_LEFT; gBattlerControllerFuncs[1] = SetControllerToLinkOpponent; gBattlerPositions[1] = B_POSITION_OPPONENT_LEFT; gBattlerControllerFuncs[2] = SetControllerToPlayer; gBattlerPositions[2] = B_POSITION_PLAYER_RIGHT; gBattlerControllerFuncs[3] = SetControllerToLinkOpponent; 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; gBattlerPartyIndexes[2] = 3; gBattlerPartyIndexes[3] = 3; } else { multiplayerId = GetMultiplayerId(); if (gBattleTypeFlags & BATTLE_TYPE_IS_MASTER) gBattleMainFunc = BeginBattleIntro; for (i = 0; i < MAX_BATTLERS_COUNT; i++) { switch (gLinkPlayers[i].id) { case 0: case 3: BufferBattlePartyCurrentOrderBySide(gLinkPlayers[i].id, 0); break; case 1: case 2: BufferBattlePartyCurrentOrderBySide(gLinkPlayers[i].id, 1); break; } if (i == multiplayerId) { gBattlerControllerFuncs[gLinkPlayers[i].id] = SetControllerToPlayer; switch (gLinkPlayers[i].id) { case 0: case 3: gBattlerPositions[gLinkPlayers[i].id] = B_POSITION_PLAYER_LEFT; gBattlerPartyIndexes[gLinkPlayers[i].id] = 0; break; case 1: case 2: gBattlerPositions[gLinkPlayers[i].id] = B_POSITION_PLAYER_RIGHT; gBattlerPartyIndexes[gLinkPlayers[i].id] = 3; break; } } else { if ((!(gLinkPlayers[i].id & 1) && !(gLinkPlayers[multiplayerId].id & 1)) || ((gLinkPlayers[i].id & 1) && (gLinkPlayers[multiplayerId].id & 1))) { gBattlerControllerFuncs[gLinkPlayers[i].id] = SetControllerToLinkPartner; switch (gLinkPlayers[i].id) { case 0: case 3: gBattlerPositions[gLinkPlayers[i].id] = B_POSITION_PLAYER_LEFT; gBattlerPartyIndexes[gLinkPlayers[i].id] = 0; break; case 1: case 2: gBattlerPositions[gLinkPlayers[i].id] = B_POSITION_PLAYER_RIGHT; gBattlerPartyIndexes[gLinkPlayers[i].id] = 3; break; } } else { gBattlerControllerFuncs[gLinkPlayers[i].id] = SetControllerToLinkOpponent; switch (gLinkPlayers[i].id) { case 0: case 3: gBattlerPositions[gLinkPlayers[i].id] = B_POSITION_OPPONENT_LEFT; gBattlerPartyIndexes[gLinkPlayers[i].id] = 0; break; case 1: case 2: gBattlerPositions[gLinkPlayers[i].id] = B_POSITION_OPPONENT_RIGHT; gBattlerPartyIndexes[gLinkPlayers[i].id] = 3; break; } } } } gBattlersCount = MAX_BATTLERS_COUNT; } } bool32 IsValidForBattle(struct Pokemon *mon) { u32 species = GetMonData(mon, MON_DATA_SPECIES_OR_EGG); return (species != SPECIES_NONE && species != SPECIES_EGG && GetMonData(mon, MON_DATA_HP) != 0 && GetMonData(mon, MON_DATA_IS_EGG) == FALSE); } static void SetBattlePartyIds(void) { s32 i, j; if (!(gBattleTypeFlags & BATTLE_TYPE_MULTI)) { for (i = 0; i < gBattlersCount; i++) { for (j = 0; j < PARTY_SIZE; j++) { if (i < 2) { if (GetBattlerSide(i) == B_SIDE_PLAYER) { if (IsValidForBattle(&gPlayerParty[j])) { gBattlerPartyIndexes[i] = j; break; } } else { if (IsValidForBattle(&gEnemyParty[j])) { gBattlerPartyIndexes[i] = j; break; } } } else { if (GetBattlerSide(i) == B_SIDE_PLAYER) { if (IsValidForBattle(&gPlayerParty[j]) && gBattlerPartyIndexes[i - 2] != j) { gBattlerPartyIndexes[i] = j; break; } } else { if (IsValidForBattle(&gEnemyParty[j]) && gBattlerPartyIndexes[i - 2] != j) { gBattlerPartyIndexes[i] = j; break; } } // No valid mons were found. Add the empty slot. if (gBattlerPartyIndexes[i - 2] == 0) gBattlerPartyIndexes[i] = 1; else gBattlerPartyIndexes[i] = 0; } } } if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) gBattlerPartyIndexes[1] = 0, gBattlerPartyIndexes[3] = 3; } } static void PrepareBufferDataTransfer(u32 battler, u32 bufferId, u8 *data, u16 size) { s32 i; if (gBattleTypeFlags & BATTLE_TYPE_LINK) { PrepareBufferDataTransferLink(battler, bufferId, size, data); } else { switch (bufferId) { case BUFFER_A: for (i = 0; i < size; data++, i++) gBattleResources->bufferA[battler][i] = *data; break; case BUFFER_B: for (i = 0; i < size; data++, i++) gBattleResources->bufferB[battler][i] = *data; break; } } } static void CreateTasksForSendRecvLinkBuffers(void) { sLinkSendTaskId = CreateTask(Task_HandleSendLinkBuffersData, 0); gTasks[sLinkSendTaskId].data[11] = 0; gTasks[sLinkSendTaskId].data[12] = 0; gTasks[sLinkSendTaskId].data[13] = 0; gTasks[sLinkSendTaskId].data[14] = 0; gTasks[sLinkSendTaskId].data[15] = 0; sLinkReceiveTaskId = CreateTask(Task_HandleCopyReceivedLinkBuffersData, 0); gTasks[sLinkReceiveTaskId].data[12] = 0; gTasks[sLinkReceiveTaskId].data[13] = 0; gTasks[sLinkReceiveTaskId].data[14] = 0; gTasks[sLinkReceiveTaskId].data[15] = 0; } enum { LINK_BUFF_BUFFER_ID, LINK_BUFF_ACTIVE_BATTLER, LINK_BUFF_ATTACKER, LINK_BUFF_TARGET, LINK_BUFF_SIZE_LO, LINK_BUFF_SIZE_HI, LINK_BUFF_ABSENT_BATTLER_FLAGS, LINK_BUFF_EFFECT_BATTLER, LINK_BUFF_DATA, }; void PrepareBufferDataTransferLink(u32 battler, u32 bufferId, u16 size, u8 *data) { s32 alignedSize; s32 i; alignedSize = size - size % 4 + 4; if (gTasks[sLinkSendTaskId].data[14] + alignedSize + LINK_BUFF_DATA + 1 > BATTLE_BUFFER_LINK_SIZE) { gTasks[sLinkSendTaskId].data[12] = gTasks[sLinkSendTaskId].data[14]; gTasks[sLinkSendTaskId].data[14] = 0; } gLinkBattleSendBuffer[gTasks[sLinkSendTaskId].data[14] + LINK_BUFF_BUFFER_ID] = bufferId; gLinkBattleSendBuffer[gTasks[sLinkSendTaskId].data[14] + LINK_BUFF_ACTIVE_BATTLER] = battler; gLinkBattleSendBuffer[gTasks[sLinkSendTaskId].data[14] + LINK_BUFF_ATTACKER] = gBattlerAttacker; gLinkBattleSendBuffer[gTasks[sLinkSendTaskId].data[14] + LINK_BUFF_TARGET] = gBattlerTarget; gLinkBattleSendBuffer[gTasks[sLinkSendTaskId].data[14] + LINK_BUFF_SIZE_LO] = alignedSize; gLinkBattleSendBuffer[gTasks[sLinkSendTaskId].data[14] + LINK_BUFF_SIZE_HI] = (alignedSize & 0x0000FF00) >> 8; gLinkBattleSendBuffer[gTasks[sLinkSendTaskId].data[14] + LINK_BUFF_ABSENT_BATTLER_FLAGS] = gAbsentBattlerFlags; gLinkBattleSendBuffer[gTasks[sLinkSendTaskId].data[14] + LINK_BUFF_EFFECT_BATTLER] = gEffectBattler; for (i = 0; i < size; i++) gLinkBattleSendBuffer[gTasks[sLinkSendTaskId].data[14] + LINK_BUFF_DATA + i] = data[i]; gTasks[sLinkSendTaskId].data[14] = gTasks[sLinkSendTaskId].data[14] + alignedSize + LINK_BUFF_DATA; } static void Task_HandleSendLinkBuffersData(u8 taskId) { u16 numPlayers; u16 blockSize; switch (gTasks[taskId].data[11]) { case 0: gTasks[taskId].data[10] = 100; gTasks[taskId].data[11]++; break; case 1: gTasks[taskId].data[10]--; if (gTasks[taskId].data[10] == 0) gTasks[taskId].data[11]++; break; case 2: if (gWirelessCommType) { gTasks[taskId].data[11]++; } else { if (gBattleTypeFlags & BATTLE_TYPE_BATTLE_TOWER) numPlayers = 2; else numPlayers = (gBattleTypeFlags & BATTLE_TYPE_MULTI) ? 4 : 2; if (GetLinkPlayerCount_2() >= numPlayers) { if (IsLinkMaster()) { CheckShouldAdvanceLinkState(); gTasks[taskId].data[11]++; } else { gTasks[taskId].data[11]++; } } } break; case 3: if (gTasks[taskId].data[15] != gTasks[taskId].data[14]) { if (gTasks[taskId].data[13] == 0) { if (gTasks[taskId].data[15] > gTasks[taskId].data[14] && gTasks[taskId].data[15] == gTasks[taskId].data[12]) { gTasks[taskId].data[12] = 0; gTasks[taskId].data[15] = 0; } blockSize = (gLinkBattleSendBuffer[gTasks[taskId].data[15] + LINK_BUFF_SIZE_LO] | (gLinkBattleSendBuffer[gTasks[taskId].data[15] + LINK_BUFF_SIZE_HI] << 8)) + LINK_BUFF_DATA; SendBlock(BitmaskAllOtherLinkPlayers(), &gLinkBattleSendBuffer[gTasks[taskId].data[15]], blockSize); gTasks[taskId].data[11]++; } else { gTasks[taskId].data[13]--; break; } } break; case 4: if (IsLinkTaskFinished()) { blockSize = gLinkBattleSendBuffer[gTasks[taskId].data[15] + LINK_BUFF_SIZE_LO] | (gLinkBattleSendBuffer[gTasks[taskId].data[15] + LINK_BUFF_SIZE_HI] << 8); gTasks[taskId].data[13] = 1; gTasks[taskId].data[15] = gTasks[taskId].data[15] + blockSize + LINK_BUFF_DATA; gTasks[taskId].data[11] = 3; } break; case 5: if (--gTasks[taskId].data[13] == 0) { gTasks[taskId].data[13] = 1; gTasks[taskId].data[11] = 3; } break; } } void TryReceiveLinkBattleData(void) { u8 i; s32 j; u8 *recvBuffer; if (gReceivedRemoteLinkPlayers && (gBattleTypeFlags & BATTLE_TYPE_LINK_IN_BATTLE)) { DestroyTask_RfuIdle(); for (i = 0; i < GetLinkPlayerCount(); i++) { if (GetBlockReceivedStatus() & (1 << (i))) { ResetBlockReceivedFlag(i); recvBuffer = (u8 *)gBlockRecvBuffer[i]; { u8 *dest, *src; u16 dataSize = gBlockRecvBuffer[i][2]; if (gTasks[sLinkReceiveTaskId].data[14] + 9 + dataSize > 0x1000) { gTasks[sLinkReceiveTaskId].data[12] = gTasks[sLinkReceiveTaskId].data[14]; gTasks[sLinkReceiveTaskId].data[14] = 0; } dest = &gLinkBattleRecvBuffer[gTasks[sLinkReceiveTaskId].data[14]]; src = recvBuffer; for (j = 0; j < dataSize + 8; j++) dest[j] = src[j]; gTasks[sLinkReceiveTaskId].data[14] = gTasks[sLinkReceiveTaskId].data[14] + dataSize + 8; } } } } } static void Task_HandleCopyReceivedLinkBuffersData(u8 taskId) { u16 blockSize; u8 battler; u8 var; if (gTasks[taskId].data[15] != gTasks[taskId].data[14]) { if (gTasks[taskId].data[15] > gTasks[taskId].data[14] && gTasks[taskId].data[15] == gTasks[taskId].data[12]) { gTasks[taskId].data[12] = 0; gTasks[taskId].data[15] = 0; } battler = gLinkBattleRecvBuffer[gTasks[taskId].data[15] + LINK_BUFF_ACTIVE_BATTLER]; blockSize = gLinkBattleRecvBuffer[gTasks[taskId].data[15] + LINK_BUFF_SIZE_LO] | (gLinkBattleRecvBuffer[gTasks[taskId].data[15] + LINK_BUFF_SIZE_HI] << 8); switch (gLinkBattleRecvBuffer[gTasks[taskId].data[15] + 0]) { case 0: if (gBattleControllerExecFlags & (1u << battler)) return; memcpy(gBattleResources->bufferA[battler], &gLinkBattleRecvBuffer[gTasks[taskId].data[15] + LINK_BUFF_DATA], blockSize); MarkBattlerReceivedLinkData(battler); if (!(gBattleTypeFlags & BATTLE_TYPE_IS_MASTER)) { gBattlerAttacker = gLinkBattleRecvBuffer[gTasks[taskId].data[15] + LINK_BUFF_ATTACKER]; gBattlerTarget = gLinkBattleRecvBuffer[gTasks[taskId].data[15] + LINK_BUFF_TARGET]; gAbsentBattlerFlags = gLinkBattleRecvBuffer[gTasks[taskId].data[15] + LINK_BUFF_ABSENT_BATTLER_FLAGS]; gEffectBattler = gLinkBattleRecvBuffer[gTasks[taskId].data[15] + LINK_BUFF_EFFECT_BATTLER]; } break; case 1: memcpy(gBattleResources->bufferB[battler], &gLinkBattleRecvBuffer[gTasks[taskId].data[15] + LINK_BUFF_DATA], blockSize); break; case 2: var = gLinkBattleRecvBuffer[gTasks[taskId].data[15] + LINK_BUFF_DATA]; gBattleControllerExecFlags &= ~(1u << (battler + var * 4)); break; } gTasks[taskId].data[15] = gTasks[taskId].data[15] + blockSize + LINK_BUFF_DATA; } } void BtlController_EmitGetMonData(u32 battler, u32 bufferId, u8 requestId, u8 monToCheck) { gBattleResources->transferBuffer[0] = CONTROLLER_GETMONDATA; gBattleResources->transferBuffer[1] = requestId; gBattleResources->transferBuffer[2] = monToCheck; gBattleResources->transferBuffer[3] = 0; PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } static void UNUSED BtlController_EmitGetRawMonData(u32 battler, u32 bufferId, u8 monId, u8 bytes) { gBattleResources->transferBuffer[0] = CONTROLLER_GETRAWMONDATA; gBattleResources->transferBuffer[1] = monId; gBattleResources->transferBuffer[2] = bytes; gBattleResources->transferBuffer[3] = 0; PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } void BtlController_EmitSetMonData(u32 battler, u32 bufferId, u8 requestId, u8 monToCheck, u8 bytes, void *data) { s32 i; gBattleResources->transferBuffer[0] = CONTROLLER_SETMONDATA; gBattleResources->transferBuffer[1] = requestId; gBattleResources->transferBuffer[2] = monToCheck; for (i = 0; i < bytes; i++) gBattleResources->transferBuffer[3 + i] = *(u8 *)(data++); PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 3 + bytes); } static void UNUSED BtlController_EmitSetRawMonData(u32 battler, u32 bufferId, u8 monId, u8 bytes, void *data) { s32 i; gBattleResources->transferBuffer[0] = CONTROLLER_SETRAWMONDATA; gBattleResources->transferBuffer[1] = monId; gBattleResources->transferBuffer[2] = bytes; for (i = 0; i < bytes; i++) gBattleResources->transferBuffer[3 + i] = *(u8 *)(data++); PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, bytes + 3); } void BtlController_EmitLoadMonSprite(u32 battler, u32 bufferId) { gBattleResources->transferBuffer[0] = CONTROLLER_LOADMONSPRITE; gBattleResources->transferBuffer[1] = CONTROLLER_LOADMONSPRITE; gBattleResources->transferBuffer[2] = CONTROLLER_LOADMONSPRITE; gBattleResources->transferBuffer[3] = CONTROLLER_LOADMONSPRITE; PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } void BtlController_EmitSwitchInAnim(u32 battler, u32 bufferId, u8 partyId, bool8 dontClearSubstituteBit) { gBattleResources->transferBuffer[0] = CONTROLLER_SWITCHINANIM; gBattleResources->transferBuffer[1] = partyId; gBattleResources->transferBuffer[2] = dontClearSubstituteBit; gBattleResources->transferBuffer[3] = 5; PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } void BtlController_EmitReturnMonToBall(u32 battler, u32 bufferId, bool8 skipAnim) { gBattleResources->transferBuffer[0] = CONTROLLER_RETURNMONTOBALL; gBattleResources->transferBuffer[1] = skipAnim; PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 2); } void BtlController_EmitDrawTrainerPic(u32 battler, u32 bufferId) { gBattleResources->transferBuffer[0] = CONTROLLER_DRAWTRAINERPIC; gBattleResources->transferBuffer[1] = CONTROLLER_DRAWTRAINERPIC; gBattleResources->transferBuffer[2] = CONTROLLER_DRAWTRAINERPIC; gBattleResources->transferBuffer[3] = CONTROLLER_DRAWTRAINERPIC; PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } void BtlController_EmitTrainerSlide(u32 battler, u32 bufferId) { gBattleResources->transferBuffer[0] = CONTROLLER_TRAINERSLIDE; gBattleResources->transferBuffer[1] = CONTROLLER_TRAINERSLIDE; gBattleResources->transferBuffer[2] = CONTROLLER_TRAINERSLIDE; gBattleResources->transferBuffer[3] = CONTROLLER_TRAINERSLIDE; PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } void BtlController_EmitTrainerSlideBack(u32 battler, u32 bufferId) { gBattleResources->transferBuffer[0] = CONTROLLER_TRAINERSLIDEBACK; gBattleResources->transferBuffer[1] = CONTROLLER_TRAINERSLIDEBACK; gBattleResources->transferBuffer[2] = CONTROLLER_TRAINERSLIDEBACK; gBattleResources->transferBuffer[3] = CONTROLLER_TRAINERSLIDEBACK; PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } void BtlController_EmitFaintAnimation(u32 battler, u32 bufferId) { gBattleResources->transferBuffer[0] = CONTROLLER_FAINTANIMATION; gBattleResources->transferBuffer[1] = CONTROLLER_FAINTANIMATION; gBattleResources->transferBuffer[2] = CONTROLLER_FAINTANIMATION; gBattleResources->transferBuffer[3] = CONTROLLER_FAINTANIMATION; PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } static void UNUSED BtlController_EmitPaletteFade(u32 battler, u32 bufferId) { gBattleResources->transferBuffer[0] = CONTROLLER_PALETTEFADE; gBattleResources->transferBuffer[1] = CONTROLLER_PALETTEFADE; gBattleResources->transferBuffer[2] = CONTROLLER_PALETTEFADE; gBattleResources->transferBuffer[3] = CONTROLLER_PALETTEFADE; PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } static void UNUSED BtlController_EmitSuccessBallThrowAnim(u32 battler, u32 bufferId) { gBattleResources->transferBuffer[0] = CONTROLLER_SUCCESSBALLTHROWANIM; gBattleResources->transferBuffer[1] = CONTROLLER_SUCCESSBALLTHROWANIM; gBattleResources->transferBuffer[2] = CONTROLLER_SUCCESSBALLTHROWANIM; gBattleResources->transferBuffer[3] = CONTROLLER_SUCCESSBALLTHROWANIM; PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } void BtlController_EmitBallThrowAnim(u32 battler, u32 bufferId, u8 caseId) { gBattleResources->transferBuffer[0] = CONTROLLER_BALLTHROWANIM; gBattleResources->transferBuffer[1] = caseId; PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 2); } static void UNUSED BtlController_EmitPause(u32 battler, u32 bufferId, u8 toWait, void *data) { s32 i; gBattleResources->transferBuffer[0] = CONTROLLER_PAUSE; gBattleResources->transferBuffer[1] = toWait; for (i = 0; i < toWait * 3; i++) gBattleResources->transferBuffer[2 + i] = *(u8 *)(data++); PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, toWait * 3 + 2); } void BtlController_EmitMoveAnimation(u32 battler, u32 bufferId, u16 move, u8 turnOfMove, u16 movePower, s32 dmg, u8 friendship, struct DisableStruct *disableStructPtr, u8 multihit) { gBattleResources->transferBuffer[0] = CONTROLLER_MOVEANIMATION; gBattleResources->transferBuffer[1] = move; gBattleResources->transferBuffer[2] = (move & 0xFF00) >> 8; gBattleResources->transferBuffer[3] = turnOfMove; gBattleResources->transferBuffer[4] = movePower; gBattleResources->transferBuffer[5] = (movePower & 0xFF00) >> 8; gBattleResources->transferBuffer[6] = dmg; gBattleResources->transferBuffer[7] = (dmg & 0x0000FF00) >> 8; gBattleResources->transferBuffer[8] = (dmg & 0x00FF0000) >> 16; gBattleResources->transferBuffer[9] = (dmg & 0xFF000000) >> 24; gBattleResources->transferBuffer[10] = friendship; gBattleResources->transferBuffer[11] = multihit; if (WEATHER_HAS_EFFECT) { gBattleResources->transferBuffer[12] = gBattleWeather; gBattleResources->transferBuffer[13] = (gBattleWeather & 0xFF00) >> 8; } else { gBattleResources->transferBuffer[12] = 0; gBattleResources->transferBuffer[13] = 0; } gBattleResources->transferBuffer[14] = 0; gBattleResources->transferBuffer[15] = 0; memcpy(&gBattleResources->transferBuffer[16], disableStructPtr, sizeof(struct DisableStruct)); PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 16 + sizeof(struct DisableStruct)); } void BtlController_EmitPrintString(u32 battler, u32 bufferId, u16 stringID) { s32 i; struct BattleMsgData *stringInfo; gBattleResources->transferBuffer[0] = CONTROLLER_PRINTSTRING; gBattleResources->transferBuffer[1] = gBattleOutcome; gBattleResources->transferBuffer[2] = stringID; gBattleResources->transferBuffer[3] = (stringID & 0xFF00) >> 8; stringInfo = (struct BattleMsgData *)(&gBattleResources->transferBuffer[4]); stringInfo->currentMove = gCurrentMove; stringInfo->originallyUsedMove = gChosenMove; stringInfo->lastItem = gLastUsedItem; stringInfo->lastAbility = gLastUsedAbility; stringInfo->scrActive = gBattleScripting.battler; stringInfo->bakScriptPartyIdx = gBattleStruct->scriptPartyIdx; stringInfo->hpScale = gBattleStruct->hpScale; stringInfo->itemEffectBattler = gPotentialItemEffectBattler; stringInfo->moveType = gMovesInfo[gCurrentMove].type; for (i = 0; i < MAX_BATTLERS_COUNT; i++) stringInfo->abilities[i] = gBattleMons[i].ability; for (i = 0; i < TEXT_BUFF_ARRAY_COUNT; i++) { stringInfo->textBuffs[0][i] = gBattleTextBuff1[i]; stringInfo->textBuffs[1][i] = gBattleTextBuff2[i]; stringInfo->textBuffs[2][i] = gBattleTextBuff3[i]; } PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, sizeof(struct BattleMsgData) + 4); } void BtlController_EmitPrintSelectionString(u32 battler, u32 bufferId, u16 stringID) { s32 i; struct BattleMsgData *stringInfo; gBattleResources->transferBuffer[0] = CONTROLLER_PRINTSTRINGPLAYERONLY; gBattleResources->transferBuffer[1] = CONTROLLER_PRINTSTRINGPLAYERONLY; gBattleResources->transferBuffer[2] = stringID; gBattleResources->transferBuffer[3] = (stringID & 0xFF00) >> 8; stringInfo = (struct BattleMsgData *)(&gBattleResources->transferBuffer[4]); stringInfo->currentMove = gCurrentMove; stringInfo->originallyUsedMove = gChosenMove; stringInfo->lastItem = gLastUsedItem; stringInfo->lastAbility = gLastUsedAbility; stringInfo->scrActive = gBattleScripting.battler; stringInfo->bakScriptPartyIdx = gBattleStruct->scriptPartyIdx; for (i = 0; i < MAX_BATTLERS_COUNT; i++) stringInfo->abilities[i] = gBattleMons[i].ability; for (i = 0; i < TEXT_BUFF_ARRAY_COUNT; i++) { stringInfo->textBuffs[0][i] = gBattleTextBuff1[i]; stringInfo->textBuffs[1][i] = gBattleTextBuff2[i]; stringInfo->textBuffs[2][i] = gBattleTextBuff3[i]; } PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, sizeof(struct BattleMsgData) + 4); } // itemId only relevant for B_ACTION_USE_ITEM void BtlController_EmitChooseAction(u32 battler, u32 bufferId, u8 action, u16 itemId) { gBattleResources->transferBuffer[0] = CONTROLLER_CHOOSEACTION; gBattleResources->transferBuffer[1] = action; gBattleResources->transferBuffer[2] = itemId; gBattleResources->transferBuffer[3] = (itemId & 0xFF00) >> 8; PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } // Only used by the forfeit prompt in the Battle Frontier // For other Yes/No boxes in battle, see Cmd_yesnobox void BtlController_EmitYesNoBox(u32 battler, u32 bufferId) { gBattleResources->transferBuffer[0] = CONTROLLER_YESNOBOX; gBattleResources->transferBuffer[1] = CONTROLLER_YESNOBOX; gBattleResources->transferBuffer[2] = CONTROLLER_YESNOBOX; gBattleResources->transferBuffer[3] = CONTROLLER_YESNOBOX; PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } void BtlController_EmitChooseMove(u32 battler, u32 bufferId, bool8 isDoubleBattle, bool8 NoPpNumber, struct ChooseMoveStruct *movePpData) { s32 i; gBattleResources->transferBuffer[0] = CONTROLLER_CHOOSEMOVE; gBattleResources->transferBuffer[1] = isDoubleBattle; gBattleResources->transferBuffer[2] = NoPpNumber; gBattleResources->transferBuffer[3] = 0; for (i = 0; i < sizeof(*movePpData); i++) gBattleResources->transferBuffer[4 + i] = *((u8 *)(movePpData) + i); PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, sizeof(*movePpData) + 4); } void BtlController_EmitChooseItem(u32 battler, u32 bufferId, u8 *battlePartyOrder) { s32 i; gBattleResources->transferBuffer[0] = CONTROLLER_OPENBAG; for (i = 0; i < PARTY_SIZE / 2; i++) gBattleResources->transferBuffer[1 + i] = battlePartyOrder[i]; PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } void BtlController_EmitChoosePokemon(u32 battler, u32 bufferId, u8 caseId, u8 slotId, u16 abilityId, u8 *data) { s32 i; gBattleResources->transferBuffer[0] = CONTROLLER_CHOOSEPOKEMON; gBattleResources->transferBuffer[1] = caseId; gBattleResources->transferBuffer[2] = slotId; gBattleResources->transferBuffer[3] = abilityId & 0xFF; gBattleResources->transferBuffer[7] = (abilityId >> 8) & 0xFF; for (i = 0; i < 3; i++) gBattleResources->transferBuffer[4 + i] = data[i]; PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 8); // Only 7 bytes were written. } static void UNUSED BtlController_EmitCmd23(u32 battler, u32 bufferId) { gBattleResources->transferBuffer[0] = CONTROLLER_23; gBattleResources->transferBuffer[1] = CONTROLLER_23; gBattleResources->transferBuffer[2] = CONTROLLER_23; gBattleResources->transferBuffer[3] = CONTROLLER_23; PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } // why is the argument u16 if it's being cast to s16 anyway? void BtlController_EmitHealthBarUpdate(u32 battler, u32 bufferId, u16 hpValue) { gBattleResources->transferBuffer[0] = CONTROLLER_HEALTHBARUPDATE; gBattleResources->transferBuffer[1] = 0; gBattleResources->transferBuffer[2] = (s16)hpValue; gBattleResources->transferBuffer[3] = ((s16)hpValue & 0xFF00) >> 8; PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } void BtlController_EmitExpUpdate(u32 battler, u32 bufferId, u8 partyId, s32 expPoints) { gBattleResources->transferBuffer[0] = CONTROLLER_EXPUPDATE; gBattleResources->transferBuffer[1] = partyId; gBattleResources->transferBuffer[2] = expPoints; gBattleResources->transferBuffer[3] = (expPoints & 0x0000FF00) >> 8; gBattleResources->transferBuffer[4] = (expPoints & 0x00FF0000) >> 16; gBattleResources->transferBuffer[5] = (expPoints & 0xFF000000) >> 24; PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 6); } void BtlController_EmitStatusIconUpdate(u32 battler, u32 bufferId, u32 status1, u32 status2) { gBattleResources->transferBuffer[0] = CONTROLLER_STATUSICONUPDATE; gBattleResources->transferBuffer[1] = status1; gBattleResources->transferBuffer[2] = (status1 & 0x0000FF00) >> 8; gBattleResources->transferBuffer[3] = (status1 & 0x00FF0000) >> 16; gBattleResources->transferBuffer[4] = (status1 & 0xFF000000) >> 24; gBattleResources->transferBuffer[5] = status2; gBattleResources->transferBuffer[6] = (status2 & 0x0000FF00) >> 8; gBattleResources->transferBuffer[7] = (status2 & 0x00FF0000) >> 16; gBattleResources->transferBuffer[8] = (status2 & 0xFF000000) >> 24; PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 9); } void BtlController_EmitStatusAnimation(u32 battler, u32 bufferId, bool8 status2, u32 status) { gBattleResources->transferBuffer[0] = CONTROLLER_STATUSANIMATION; gBattleResources->transferBuffer[1] = status2; gBattleResources->transferBuffer[2] = status; gBattleResources->transferBuffer[3] = (status & 0x0000FF00) >> 8; gBattleResources->transferBuffer[4] = (status & 0x00FF0000) >> 16; gBattleResources->transferBuffer[5] = (status & 0xFF000000) >> 24; PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 6); } static void UNUSED BtlController_EmitStatusXor(u32 battler, u32 bufferId, u8 b) { gBattleResources->transferBuffer[0] = CONTROLLER_STATUSXOR; gBattleResources->transferBuffer[1] = b; PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 2); } void BtlController_EmitDataTransfer(u32 battler, u32 bufferId, u16 size, void *data) { s32 i; gBattleResources->transferBuffer[0] = CONTROLLER_DATATRANSFER; gBattleResources->transferBuffer[1] = CONTROLLER_DATATRANSFER; gBattleResources->transferBuffer[2] = size; gBattleResources->transferBuffer[3] = (size & 0xFF00) >> 8; for (i = 0; i < size; i++) gBattleResources->transferBuffer[4 + i] = *(u8 *)(data++); PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, size + 4); } static void UNUSED BtlController_EmitDMA3Transfer(u32 battler, u32 bufferId, void *dst, u16 size, void *data) { s32 i; gBattleResources->transferBuffer[0] = CONTROLLER_DMA3TRANSFER; gBattleResources->transferBuffer[1] = (u32)(dst); gBattleResources->transferBuffer[2] = ((u32)(dst) & 0x0000FF00) >> 8; gBattleResources->transferBuffer[3] = ((u32)(dst) & 0x00FF0000) >> 16; gBattleResources->transferBuffer[4] = ((u32)(dst) & 0xFF000000) >> 24; gBattleResources->transferBuffer[5] = size; gBattleResources->transferBuffer[6] = (size & 0xFF00) >> 8; for (i = 0; i < size; i++) gBattleResources->transferBuffer[7 + i] = *(u8 *)(data++); PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, size + 7); } static void UNUSED BtlController_EmitPlayBGM(u32 battler, u32 bufferId, u16 songId, void *data) { s32 i; gBattleResources->transferBuffer[0] = CONTROLLER_PLAYBGM; gBattleResources->transferBuffer[1] = songId; gBattleResources->transferBuffer[2] = (songId & 0xFF00) >> 8; // Nonsense loop using songId as a size // Would go out of bounds for any song id after SE_RG_BAG_POCKET (253) for (i = 0; i < songId; i++) gBattleResources->transferBuffer[3 + i] = *(u8 *)(data++); PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, songId + 3); } static void UNUSED BtlController_EmitCmd32(u32 battler, u32 bufferId, u16 size, void *data) { s32 i; gBattleResources->transferBuffer[0] = CONTROLLER_32; gBattleResources->transferBuffer[1] = size; gBattleResources->transferBuffer[2] = (size & 0xFF00) >> 8; for (i = 0; i < size; i++) gBattleResources->transferBuffer[3 + i] = *(u8 *)(data++); PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, size + 3); } void BtlController_EmitTwoReturnValues(u32 battler, u32 bufferId, u8 ret8, u32 ret32) { gBattleResources->transferBuffer[0] = CONTROLLER_TWORETURNVALUES; gBattleResources->transferBuffer[1] = ret8; gBattleResources->transferBuffer[2] = ret32; gBattleResources->transferBuffer[3] = (ret32 & 0x0000FF00) >> 8; gBattleResources->transferBuffer[4] = (ret32 & 0x00FF0000) >> 16; gBattleResources->transferBuffer[5] = (ret32 & 0xFF000000) >> 24; PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 6); } void BtlController_EmitChosenMonReturnValue(u32 battler, u32 bufferId, u8 partyId, u8 *battlePartyOrder) { s32 i; gBattleResources->transferBuffer[0] = CONTROLLER_CHOSENMONRETURNVALUE; gBattleResources->transferBuffer[1] = partyId; for (i = 0; i < (int)ARRAY_COUNT(gBattlePartyCurrentOrder); i++) gBattleResources->transferBuffer[2 + i] = battlePartyOrder[i]; PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 5); } void BtlController_EmitOneReturnValue(u32 battler, u32 bufferId, u16 ret) { gBattleResources->transferBuffer[0] = CONTROLLER_ONERETURNVALUE; gBattleResources->transferBuffer[1] = ret; gBattleResources->transferBuffer[2] = (ret & 0xFF00) >> 8; gBattleResources->transferBuffer[3] = 0; PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } void BtlController_EmitOneReturnValue_Duplicate(u32 battler, u32 bufferId, u16 ret) { gBattleResources->transferBuffer[0] = CONTROLLER_ONERETURNVALUE_DUPLICATE; gBattleResources->transferBuffer[1] = ret; gBattleResources->transferBuffer[2] = (ret & 0xFF00) >> 8; gBattleResources->transferBuffer[3] = 0; PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } static void UNUSED BtlController_EmitClearUnkVar(u32 battler, u32 bufferId) { gBattleResources->transferBuffer[0] = CONTROLLER_CLEARUNKVAR; gBattleResources->transferBuffer[1] = CONTROLLER_CLEARUNKVAR; gBattleResources->transferBuffer[2] = CONTROLLER_CLEARUNKVAR; gBattleResources->transferBuffer[3] = CONTROLLER_CLEARUNKVAR; PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } static void UNUSED BtlController_EmitSetUnkVar(u32 battler, u32 bufferId, u8 b) { gBattleResources->transferBuffer[0] = CONTROLLER_SETUNKVAR; gBattleResources->transferBuffer[1] = b; PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 2); } static void UNUSED BtlController_EmitClearUnkFlag(u32 battler, u32 bufferId) { gBattleResources->transferBuffer[0] = CONTROLLER_CLEARUNKFLAG; gBattleResources->transferBuffer[1] = CONTROLLER_CLEARUNKFLAG; gBattleResources->transferBuffer[2] = CONTROLLER_CLEARUNKFLAG; gBattleResources->transferBuffer[3] = CONTROLLER_CLEARUNKFLAG; PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } static void UNUSED BtlController_EmitToggleUnkFlag(u32 battler, u32 bufferId) { gBattleResources->transferBuffer[0] = CONTROLLER_TOGGLEUNKFLAG; gBattleResources->transferBuffer[1] = CONTROLLER_TOGGLEUNKFLAG; gBattleResources->transferBuffer[2] = CONTROLLER_TOGGLEUNKFLAG; gBattleResources->transferBuffer[3] = CONTROLLER_TOGGLEUNKFLAG; PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } void BtlController_EmitHitAnimation(u32 battler, u32 bufferId) { gBattleResources->transferBuffer[0] = CONTROLLER_HITANIMATION; gBattleResources->transferBuffer[1] = CONTROLLER_HITANIMATION; gBattleResources->transferBuffer[2] = CONTROLLER_HITANIMATION; gBattleResources->transferBuffer[3] = CONTROLLER_HITANIMATION; PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } void BtlController_EmitCantSwitch(u32 battler, u32 bufferId) { gBattleResources->transferBuffer[0] = CONTROLLER_CANTSWITCH; gBattleResources->transferBuffer[1] = CONTROLLER_CANTSWITCH; gBattleResources->transferBuffer[2] = CONTROLLER_CANTSWITCH; gBattleResources->transferBuffer[3] = CONTROLLER_CANTSWITCH; PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } void BtlController_EmitPlaySE(u32 battler, u32 bufferId, u16 songId) { gBattleResources->transferBuffer[0] = CONTROLLER_PLAYSE; gBattleResources->transferBuffer[1] = songId; gBattleResources->transferBuffer[2] = (songId & 0xFF00) >> 8; gBattleResources->transferBuffer[3] = 0; PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } void BtlController_EmitPlayFanfareOrBGM(u32 battler, u32 bufferId, u16 songId, bool8 playBGM) { gBattleResources->transferBuffer[0] = CONTROLLER_PLAYFANFAREORBGM; gBattleResources->transferBuffer[1] = songId; gBattleResources->transferBuffer[2] = (songId & 0xFF00) >> 8; gBattleResources->transferBuffer[3] = playBGM; PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } void BtlController_EmitFaintingCry(u32 battler, u32 bufferId) { gBattleResources->transferBuffer[0] = CONTROLLER_FAINTINGCRY; gBattleResources->transferBuffer[1] = CONTROLLER_FAINTINGCRY; gBattleResources->transferBuffer[2] = CONTROLLER_FAINTINGCRY; gBattleResources->transferBuffer[3] = CONTROLLER_FAINTINGCRY; PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } void BtlController_EmitIntroSlide(u32 battler, u32 bufferId, u8 terrainId) { gBattleResources->transferBuffer[0] = CONTROLLER_INTROSLIDE; gBattleResources->transferBuffer[1] = terrainId; PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 2); } void BtlController_EmitIntroTrainerBallThrow(u32 battler, u32 bufferId) { gBattleResources->transferBuffer[0] = CONTROLLER_INTROTRAINERBALLTHROW; gBattleResources->transferBuffer[1] = CONTROLLER_INTROTRAINERBALLTHROW; gBattleResources->transferBuffer[2] = CONTROLLER_INTROTRAINERBALLTHROW; gBattleResources->transferBuffer[3] = CONTROLLER_INTROTRAINERBALLTHROW; PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } void BtlController_EmitDrawPartyStatusSummary(u32 battler, u32 bufferId, struct HpAndStatus* hpAndStatus, u8 flags) { s32 i; gBattleResources->transferBuffer[0] = CONTROLLER_DRAWPARTYSTATUSSUMMARY; gBattleResources->transferBuffer[1] = flags & ~PARTY_SUMM_SKIP_DRAW_DELAY; // If true, skip player side gBattleResources->transferBuffer[2] = (flags & PARTY_SUMM_SKIP_DRAW_DELAY) >> 7; // If true, skip delay after drawing. True during intro gBattleResources->transferBuffer[3] = CONTROLLER_DRAWPARTYSTATUSSUMMARY; for (i = 0; i < (s32)(sizeof(struct HpAndStatus) * PARTY_SIZE); i++) gBattleResources->transferBuffer[4 + i] = *(i + (u8 *)(hpAndStatus)); PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, sizeof(struct HpAndStatus) * PARTY_SIZE + 4); } void BtlController_EmitHidePartyStatusSummary(u32 battler, u32 bufferId) { gBattleResources->transferBuffer[0] = CONTROLLER_HIDEPARTYSTATUSSUMMARY; gBattleResources->transferBuffer[1] = CONTROLLER_HIDEPARTYSTATUSSUMMARY; gBattleResources->transferBuffer[2] = CONTROLLER_HIDEPARTYSTATUSSUMMARY; gBattleResources->transferBuffer[3] = CONTROLLER_HIDEPARTYSTATUSSUMMARY; PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } void BtlController_EmitEndBounceEffect(u32 battler, u32 bufferId) { gBattleResources->transferBuffer[0] = CONTROLLER_ENDBOUNCE; gBattleResources->transferBuffer[1] = CONTROLLER_ENDBOUNCE; gBattleResources->transferBuffer[2] = CONTROLLER_ENDBOUNCE; gBattleResources->transferBuffer[3] = CONTROLLER_ENDBOUNCE; PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } void BtlController_EmitSpriteInvisibility(u32 battler, u32 bufferId, bool8 isInvisible) { gBattleResources->transferBuffer[0] = CONTROLLER_SPRITEINVISIBILITY; gBattleResources->transferBuffer[1] = isInvisible; gBattleResources->transferBuffer[2] = CONTROLLER_SPRITEINVISIBILITY; gBattleResources->transferBuffer[3] = CONTROLLER_SPRITEINVISIBILITY; PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } void BtlController_EmitBattleAnimation(u32 battler, u32 bufferId, u8 animationId, struct DisableStruct* disableStructPtr, u16 argument) { gBattleResources->transferBuffer[0] = CONTROLLER_BATTLEANIMATION; gBattleResources->transferBuffer[1] = animationId; gBattleResources->transferBuffer[2] = argument; gBattleResources->transferBuffer[3] = (argument & 0xFF00) >> 8; memcpy(&gBattleResources->transferBuffer[4], disableStructPtr, sizeof(struct DisableStruct)); PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4 + sizeof(struct DisableStruct)); } // mode is a LINK_STANDBY_* constant void BtlController_EmitLinkStandbyMsg(u32 battler, u32 bufferId, u8 mode, bool32 record) { gBattleResources->transferBuffer[0] = CONTROLLER_LINKSTANDBYMSG; gBattleResources->transferBuffer[1] = mode; if (record) gBattleResources->transferBuffer[3] = gBattleResources->transferBuffer[2] = RecordedBattle_BufferNewBattlerData(&gBattleResources->transferBuffer[4]); else gBattleResources->transferBuffer[3] = gBattleResources->transferBuffer[2] = 0; PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, gBattleResources->transferBuffer[2] + 4); } void BtlController_EmitResetActionMoveSelection(u32 battler, u32 bufferId, u8 caseId) { gBattleResources->transferBuffer[0] = CONTROLLER_RESETACTIONMOVESELECTION; gBattleResources->transferBuffer[1] = caseId; PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 2); } void BtlController_EmitEndLinkBattle(u32 battler, u32 bufferId, u8 battleOutcome) { gBattleResources->transferBuffer[0] = CONTROLLER_ENDLINKBATTLE; gBattleResources->transferBuffer[1] = battleOutcome; gBattleResources->transferBuffer[2] = gSaveBlock2Ptr->frontier.disableRecordBattle; gBattleResources->transferBuffer[3] = gSaveBlock2Ptr->frontier.disableRecordBattle; gBattleResources->transferBuffer[5] = gBattleResources->transferBuffer[4] = RecordedBattle_BufferNewBattlerData(&gBattleResources->transferBuffer[6]); PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, gBattleResources->transferBuffer[4] + 6); } void BtlController_EmitDebugMenu(u32 battler, u32 bufferId) { gBattleResources->transferBuffer[0] = CONTROLLER_DEBUGMENU; PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 1); } // Standardized Controller functions // Can be used for all the controllers. void BattleControllerComplete(u32 battler) { gBattlerControllerEndFuncs[battler](battler); } static u32 GetBattlerMonData(u32 battler, struct Pokemon *party, u32 monId, u8 *dst) { struct BattlePokemon battleMon; struct MovePpInfo moveData; u8 nickname[POKEMON_NAME_LENGTH * 2]; u8 *src; s16 data16; u32 data32; s32 size = 0; switch (gBattleResources->bufferA[battler][1]) { case REQUEST_ALL_BATTLE: battleMon.species = GetMonData(&party[monId], MON_DATA_SPECIES); battleMon.item = GetMonData(&party[monId], MON_DATA_HELD_ITEM); for (size = 0; size < MAX_MON_MOVES; size++) { battleMon.moves[size] = GetMonData(&party[monId], MON_DATA_MOVE1 + size); battleMon.pp[size] = GetMonData(&party[monId], MON_DATA_PP1 + size); } battleMon.ppBonuses = GetMonData(&party[monId], MON_DATA_PP_BONUSES); battleMon.friendship = GetMonData(&party[monId], MON_DATA_FRIENDSHIP); battleMon.experience = GetMonData(&party[monId], MON_DATA_EXP); battleMon.hpIV = GetMonData(&party[monId], MON_DATA_HP_IV); battleMon.attackIV = GetMonData(&party[monId], MON_DATA_ATK_IV); battleMon.defenseIV = GetMonData(&party[monId], MON_DATA_DEF_IV); battleMon.speedIV = GetMonData(&party[monId], MON_DATA_SPEED_IV); battleMon.spAttackIV = GetMonData(&party[monId], MON_DATA_SPATK_IV); battleMon.spDefenseIV = GetMonData(&party[monId], MON_DATA_SPDEF_IV); battleMon.personality = GetMonData(&party[monId], MON_DATA_PERSONALITY); battleMon.status1 = GetMonData(&party[monId], MON_DATA_STATUS); battleMon.level = GetMonData(&party[monId], MON_DATA_LEVEL); battleMon.hp = GetMonData(&party[monId], MON_DATA_HP); battleMon.maxHP = GetMonData(&party[monId], MON_DATA_MAX_HP); battleMon.attack = GetMonData(&party[monId], MON_DATA_ATK); battleMon.defense = GetMonData(&party[monId], MON_DATA_DEF); battleMon.speed = GetMonData(&party[monId], MON_DATA_SPEED); battleMon.spAttack = GetMonData(&party[monId], MON_DATA_SPATK); battleMon.spDefense = GetMonData(&party[monId], MON_DATA_SPDEF); battleMon.abilityNum = GetMonData(&party[monId], MON_DATA_ABILITY_NUM); battleMon.otId = GetMonData(&party[monId], MON_DATA_OT_ID); battleMon.metLevel = GetMonData(&party[monId], MON_DATA_MET_LEVEL); battleMon.isShiny = GetMonData(&party[monId], MON_DATA_IS_SHINY); GetMonData(&party[monId], MON_DATA_NICKNAME, nickname); StringCopy_Nickname(battleMon.nickname, nickname); GetMonData(&party[monId], MON_DATA_OT_NAME, battleMon.otName); src = (u8 *)&battleMon; for (size = 0; size < sizeof(battleMon); size++) dst[size] = src[size]; #if TESTING if (gTestRunnerEnabled) { u32 side = GetBattlerSide(battler); u32 partyIndex = gBattlerPartyIndexes[battler]; if (TestRunner_Battle_GetForcedAbility(side, partyIndex)) gBattleMons[battler].ability = gBattleStruct->overwrittenAbilities[battler] = TestRunner_Battle_GetForcedAbility(side, partyIndex); } #endif break; case REQUEST_SPECIES_BATTLE: data16 = GetMonData(&party[monId], MON_DATA_SPECIES); dst[0] = data16; dst[1] = data16 >> 8; size = 2; break; case REQUEST_HELDITEM_BATTLE: data16 = GetMonData(&party[monId], MON_DATA_HELD_ITEM); dst[0] = data16; dst[1] = data16 >> 8; size = 2; break; case REQUEST_MOVES_PP_BATTLE: for (size = 0; size < MAX_MON_MOVES; size++) { moveData.moves[size] = GetMonData(&party[monId], MON_DATA_MOVE1 + size); moveData.pp[size] = GetMonData(&party[monId], MON_DATA_PP1 + size); } moveData.ppBonuses = GetMonData(&party[monId], MON_DATA_PP_BONUSES); src = (u8 *)(&moveData); for (size = 0; size < sizeof(moveData); size++) dst[size] = src[size]; break; case REQUEST_MOVE1_BATTLE: case REQUEST_MOVE2_BATTLE: case REQUEST_MOVE3_BATTLE: case REQUEST_MOVE4_BATTLE: data16 = GetMonData(&party[monId], MON_DATA_MOVE1 + gBattleResources->bufferA[battler][1] - REQUEST_MOVE1_BATTLE); dst[0] = data16; dst[1] = data16 >> 8; size = 2; break; case REQUEST_PP_DATA_BATTLE: for (size = 0; size < MAX_MON_MOVES; size++) dst[size] = GetMonData(&party[monId], MON_DATA_PP1 + size); dst[size] = GetMonData(&party[monId], MON_DATA_PP_BONUSES); size++; break; case REQUEST_PPMOVE1_BATTLE: case REQUEST_PPMOVE2_BATTLE: case REQUEST_PPMOVE3_BATTLE: case REQUEST_PPMOVE4_BATTLE: dst[0] = GetMonData(&party[monId], MON_DATA_PP1 + gBattleResources->bufferA[battler][1] - REQUEST_PPMOVE1_BATTLE); size = 1; break; case REQUEST_OTID_BATTLE: data32 = GetMonData(&party[monId], MON_DATA_OT_ID); dst[0] = (data32 & 0x000000FF); dst[1] = (data32 & 0x0000FF00) >> 8; dst[2] = (data32 & 0x00FF0000) >> 16; size = 3; break; case REQUEST_EXP_BATTLE: data32 = GetMonData(&party[monId], MON_DATA_EXP); dst[0] = (data32 & 0x000000FF); dst[1] = (data32 & 0x0000FF00) >> 8; dst[2] = (data32 & 0x00FF0000) >> 16; size = 3; break; case REQUEST_HP_EV_BATTLE: dst[0] = GetMonData(&party[monId], MON_DATA_HP_EV); size = 1; break; case REQUEST_ATK_EV_BATTLE: dst[0] = GetMonData(&party[monId], MON_DATA_ATK_EV); size = 1; break; case REQUEST_DEF_EV_BATTLE: dst[0] = GetMonData(&party[monId], MON_DATA_DEF_EV); size = 1; break; case REQUEST_SPEED_EV_BATTLE: dst[0] = GetMonData(&party[monId], MON_DATA_SPEED_EV); size = 1; break; case REQUEST_SPATK_EV_BATTLE: dst[0] = GetMonData(&party[monId], MON_DATA_SPATK_EV); size = 1; break; case REQUEST_SPDEF_EV_BATTLE: dst[0] = GetMonData(&party[monId], MON_DATA_SPDEF_EV); size = 1; break; case REQUEST_FRIENDSHIP_BATTLE: dst[0] = GetMonData(&party[monId], MON_DATA_FRIENDSHIP); size = 1; break; case REQUEST_POKERUS_BATTLE: dst[0] = GetMonData(&party[monId], MON_DATA_POKERUS); size = 1; break; case REQUEST_MET_LOCATION_BATTLE: dst[0] = GetMonData(&party[monId], MON_DATA_MET_LOCATION); size = 1; break; case REQUEST_MET_LEVEL_BATTLE: dst[0] = GetMonData(&party[monId], MON_DATA_MET_LEVEL); size = 1; break; case REQUEST_MET_GAME_BATTLE: dst[0] = GetMonData(&party[monId], MON_DATA_MET_GAME); size = 1; break; case REQUEST_POKEBALL_BATTLE: dst[0] = GetMonData(&party[monId], MON_DATA_POKEBALL); size = 1; break; case REQUEST_ALL_IVS_BATTLE: dst[0] = GetMonData(&party[monId], MON_DATA_HP_IV); dst[1] = GetMonData(&party[monId], MON_DATA_ATK_IV); dst[2] = GetMonData(&party[monId], MON_DATA_DEF_IV); dst[3] = GetMonData(&party[monId], MON_DATA_SPEED_IV); dst[4] = GetMonData(&party[monId], MON_DATA_SPATK_IV); dst[5] = GetMonData(&party[monId], MON_DATA_SPDEF_IV); size = 6; break; case REQUEST_HP_IV_BATTLE: dst[0] = GetMonData(&party[monId], MON_DATA_HP_IV); size = 1; break; case REQUEST_ATK_IV_BATTLE: dst[0] = GetMonData(&party[monId], MON_DATA_ATK_IV); size = 1; break; case REQUEST_DEF_IV_BATTLE: dst[0] = GetMonData(&party[monId], MON_DATA_DEF_IV); size = 1; break; case REQUEST_SPEED_IV_BATTLE: dst[0] = GetMonData(&party[monId], MON_DATA_SPEED_IV); size = 1; break; case REQUEST_SPATK_IV_BATTLE: dst[0] = GetMonData(&party[monId], MON_DATA_SPATK_IV); size = 1; break; case REQUEST_SPDEF_IV_BATTLE: dst[0] = GetMonData(&party[monId], MON_DATA_SPDEF_IV); size = 1; break; case REQUEST_PERSONALITY_BATTLE: data32 = GetMonData(&party[monId], MON_DATA_PERSONALITY); dst[0] = (data32 & 0x000000FF); dst[1] = (data32 & 0x0000FF00) >> 8; dst[2] = (data32 & 0x00FF0000) >> 16; dst[3] = (data32 & 0xFF000000) >> 24; size = 4; break; case REQUEST_CHECKSUM_BATTLE: data16 = GetMonData(&party[monId], MON_DATA_CHECKSUM); dst[0] = data16; dst[1] = data16 >> 8; size = 2; break; case REQUEST_STATUS_BATTLE: data32 = GetMonData(&party[monId], MON_DATA_STATUS); dst[0] = (data32 & 0x000000FF); dst[1] = (data32 & 0x0000FF00) >> 8; dst[2] = (data32 & 0x00FF0000) >> 16; dst[3] = (data32 & 0xFF000000) >> 24; size = 4; break; case REQUEST_LEVEL_BATTLE: dst[0] = GetMonData(&party[monId], MON_DATA_LEVEL); size = 1; break; case REQUEST_HP_BATTLE: data16 = GetMonData(&party[monId], MON_DATA_HP); dst[0] = data16; dst[1] = data16 >> 8; size = 2; break; case REQUEST_MAX_HP_BATTLE: data16 = GetMonData(&party[monId], MON_DATA_MAX_HP); dst[0] = data16; dst[1] = data16 >> 8; size = 2; break; case REQUEST_ATK_BATTLE: data16 = GetMonData(&party[monId], MON_DATA_ATK); dst[0] = data16; dst[1] = data16 >> 8; size = 2; break; case REQUEST_DEF_BATTLE: data16 = GetMonData(&party[monId], MON_DATA_DEF); dst[0] = data16; dst[1] = data16 >> 8; size = 2; break; case REQUEST_SPEED_BATTLE: data16 = GetMonData(&party[monId], MON_DATA_SPEED); dst[0] = data16; dst[1] = data16 >> 8; size = 2; break; case REQUEST_SPATK_BATTLE: data16 = GetMonData(&party[monId], MON_DATA_SPATK); dst[0] = data16; dst[1] = data16 >> 8; size = 2; break; case REQUEST_SPDEF_BATTLE: data16 = GetMonData(&party[monId], MON_DATA_SPDEF); dst[0] = data16; dst[1] = data16 >> 8; size = 2; break; case REQUEST_COOL_BATTLE: dst[0] = GetMonData(&party[monId], MON_DATA_COOL); size = 1; break; case REQUEST_BEAUTY_BATTLE: dst[0] = GetMonData(&party[monId], MON_DATA_BEAUTY); size = 1; break; case REQUEST_CUTE_BATTLE: dst[0] = GetMonData(&party[monId], MON_DATA_CUTE); size = 1; break; case REQUEST_SMART_BATTLE: dst[0] = GetMonData(&party[monId], MON_DATA_SMART); size = 1; break; case REQUEST_TOUGH_BATTLE: dst[0] = GetMonData(&party[monId], MON_DATA_TOUGH); size = 1; break; case REQUEST_SHEEN_BATTLE: dst[0] = GetMonData(&party[monId], MON_DATA_SHEEN); size = 1; break; case REQUEST_COOL_RIBBON_BATTLE: dst[0] = GetMonData(&party[monId], MON_DATA_COOL_RIBBON); size = 1; break; case REQUEST_BEAUTY_RIBBON_BATTLE: dst[0] = GetMonData(&party[monId], MON_DATA_BEAUTY_RIBBON); size = 1; break; case REQUEST_CUTE_RIBBON_BATTLE: dst[0] = GetMonData(&party[monId], MON_DATA_CUTE_RIBBON); size = 1; break; case REQUEST_SMART_RIBBON_BATTLE: dst[0] = GetMonData(&party[monId], MON_DATA_SMART_RIBBON); size = 1; break; case REQUEST_TOUGH_RIBBON_BATTLE: dst[0] = GetMonData(&party[monId], MON_DATA_TOUGH_RIBBON); size = 1; break; } return size; } static void SetBattlerMonData(u32 battler, struct Pokemon *party, u32 monId) { struct BattlePokemon *battlePokemon = (struct BattlePokemon *)&gBattleResources->bufferA[battler][3]; struct MovePpInfo *moveData = (struct MovePpInfo *)&gBattleResources->bufferA[battler][3]; s32 i; switch (gBattleResources->bufferA[battler][1]) { case REQUEST_ALL_BATTLE: { u8 iv; SetMonData(&party[monId], MON_DATA_SPECIES, &battlePokemon->species); SetMonData(&party[monId], MON_DATA_HELD_ITEM, &battlePokemon->item); for (i = 0; i < MAX_MON_MOVES; i++) { SetMonData(&party[monId], MON_DATA_MOVE1 + i, &battlePokemon->moves[i]); SetMonData(&party[monId], MON_DATA_PP1 + i, &battlePokemon->pp[i]); } SetMonData(&party[monId], MON_DATA_PP_BONUSES, &battlePokemon->ppBonuses); SetMonData(&party[monId], MON_DATA_FRIENDSHIP, &battlePokemon->friendship); SetMonData(&party[monId], MON_DATA_EXP, &battlePokemon->experience); iv = battlePokemon->hpIV; SetMonData(&party[monId], MON_DATA_HP_IV, &iv); iv = battlePokemon->attackIV; SetMonData(&party[monId], MON_DATA_ATK_IV, &iv); iv = battlePokemon->defenseIV; SetMonData(&party[monId], MON_DATA_DEF_IV, &iv); iv = battlePokemon->speedIV; SetMonData(&party[monId], MON_DATA_SPEED_IV, &iv); iv = battlePokemon->spAttackIV; SetMonData(&party[monId], MON_DATA_SPATK_IV, &iv); iv = battlePokemon->spDefenseIV; SetMonData(&party[monId], MON_DATA_SPDEF_IV, &iv); SetMonData(&party[monId], MON_DATA_PERSONALITY, &battlePokemon->personality); SetMonData(&party[monId], MON_DATA_STATUS, &battlePokemon->status1); SetMonData(&party[monId], MON_DATA_LEVEL, &battlePokemon->level); SetMonData(&party[monId], MON_DATA_HP, &battlePokemon->hp); SetMonData(&party[monId], MON_DATA_MAX_HP, &battlePokemon->maxHP); SetMonData(&party[monId], MON_DATA_ATK, &battlePokemon->attack); SetMonData(&party[monId], MON_DATA_DEF, &battlePokemon->defense); SetMonData(&party[monId], MON_DATA_SPEED, &battlePokemon->speed); SetMonData(&party[monId], MON_DATA_SPATK, &battlePokemon->spAttack); SetMonData(&party[monId], MON_DATA_SPDEF, &battlePokemon->spDefense); } break; case REQUEST_SPECIES_BATTLE: SetMonData(&party[monId], MON_DATA_SPECIES, &gBattleResources->bufferA[battler][3]); break; case REQUEST_HELDITEM_BATTLE: SetMonData(&party[monId], MON_DATA_HELD_ITEM, &gBattleResources->bufferA[battler][3]); break; case REQUEST_MOVES_PP_BATTLE: for (i = 0; i < MAX_MON_MOVES; i++) { SetMonData(&party[monId], MON_DATA_MOVE1 + i, &moveData->moves[i]); SetMonData(&party[monId], MON_DATA_PP1 + i, &moveData->pp[i]); } SetMonData(&party[monId], MON_DATA_PP_BONUSES, &moveData->ppBonuses); break; case REQUEST_MOVE1_BATTLE: case REQUEST_MOVE2_BATTLE: case REQUEST_MOVE3_BATTLE: case REQUEST_MOVE4_BATTLE: SetMonData(&party[monId], MON_DATA_MOVE1 + gBattleResources->bufferA[battler][1] - REQUEST_MOVE1_BATTLE, &gBattleResources->bufferA[battler][3]); break; case REQUEST_PP_DATA_BATTLE: SetMonData(&party[monId], MON_DATA_PP1, &gBattleResources->bufferA[battler][3]); SetMonData(&party[monId], MON_DATA_PP2, &gBattleResources->bufferA[battler][4]); SetMonData(&party[monId], MON_DATA_PP3, &gBattleResources->bufferA[battler][5]); SetMonData(&party[monId], MON_DATA_PP4, &gBattleResources->bufferA[battler][6]); SetMonData(&party[monId], MON_DATA_PP_BONUSES, &gBattleResources->bufferA[battler][7]); break; case REQUEST_PPMOVE1_BATTLE: case REQUEST_PPMOVE2_BATTLE: case REQUEST_PPMOVE3_BATTLE: case REQUEST_PPMOVE4_BATTLE: SetMonData(&party[monId], MON_DATA_PP1 + gBattleResources->bufferA[battler][1] - REQUEST_PPMOVE1_BATTLE, &gBattleResources->bufferA[battler][3]); break; case REQUEST_OTID_BATTLE: SetMonData(&party[monId], MON_DATA_OT_ID, &gBattleResources->bufferA[battler][3]); break; case REQUEST_EXP_BATTLE: SetMonData(&party[monId], MON_DATA_EXP, &gBattleResources->bufferA[battler][3]); break; case REQUEST_HP_EV_BATTLE: SetMonData(&party[monId], MON_DATA_HP_EV, &gBattleResources->bufferA[battler][3]); break; case REQUEST_ATK_EV_BATTLE: SetMonData(&party[monId], MON_DATA_ATK_EV, &gBattleResources->bufferA[battler][3]); break; case REQUEST_DEF_EV_BATTLE: SetMonData(&party[monId], MON_DATA_DEF_EV, &gBattleResources->bufferA[battler][3]); break; case REQUEST_SPEED_EV_BATTLE: SetMonData(&party[monId], MON_DATA_SPEED_EV, &gBattleResources->bufferA[battler][3]); break; case REQUEST_SPATK_EV_BATTLE: SetMonData(&party[monId], MON_DATA_SPATK_EV, &gBattleResources->bufferA[battler][3]); break; case REQUEST_SPDEF_EV_BATTLE: SetMonData(&party[monId], MON_DATA_SPDEF_EV, &gBattleResources->bufferA[battler][3]); break; case REQUEST_FRIENDSHIP_BATTLE: SetMonData(&party[monId], MON_DATA_FRIENDSHIP, &gBattleResources->bufferA[battler][3]); break; case REQUEST_POKERUS_BATTLE: SetMonData(&party[monId], MON_DATA_POKERUS, &gBattleResources->bufferA[battler][3]); break; case REQUEST_MET_LOCATION_BATTLE: SetMonData(&party[monId], MON_DATA_MET_LOCATION, &gBattleResources->bufferA[battler][3]); break; case REQUEST_MET_LEVEL_BATTLE: SetMonData(&party[monId], MON_DATA_MET_LEVEL, &gBattleResources->bufferA[battler][3]); break; case REQUEST_MET_GAME_BATTLE: SetMonData(&party[monId], MON_DATA_MET_GAME, &gBattleResources->bufferA[battler][3]); break; case REQUEST_POKEBALL_BATTLE: SetMonData(&party[monId], MON_DATA_POKEBALL, &gBattleResources->bufferA[battler][3]); break; case REQUEST_ALL_IVS_BATTLE: SetMonData(&party[monId], MON_DATA_HP_IV, &gBattleResources->bufferA[battler][3]); SetMonData(&party[monId], MON_DATA_ATK_IV, &gBattleResources->bufferA[battler][4]); SetMonData(&party[monId], MON_DATA_DEF_IV, &gBattleResources->bufferA[battler][5]); SetMonData(&party[monId], MON_DATA_SPEED_IV, &gBattleResources->bufferA[battler][6]); SetMonData(&party[monId], MON_DATA_SPATK_IV, &gBattleResources->bufferA[battler][7]); SetMonData(&party[monId], MON_DATA_SPDEF_IV, &gBattleResources->bufferA[battler][8]); break; case REQUEST_HP_IV_BATTLE: SetMonData(&party[monId], MON_DATA_HP_IV, &gBattleResources->bufferA[battler][3]); break; case REQUEST_ATK_IV_BATTLE: SetMonData(&party[monId], MON_DATA_ATK_IV, &gBattleResources->bufferA[battler][3]); break; case REQUEST_DEF_IV_BATTLE: SetMonData(&party[monId], MON_DATA_DEF_IV, &gBattleResources->bufferA[battler][3]); break; case REQUEST_SPEED_IV_BATTLE: SetMonData(&party[monId], MON_DATA_SPEED_IV, &gBattleResources->bufferA[battler][3]); break; case REQUEST_SPATK_IV_BATTLE: SetMonData(&party[monId], MON_DATA_SPATK_IV, &gBattleResources->bufferA[battler][3]); break; case REQUEST_SPDEF_IV_BATTLE: SetMonData(&party[monId], MON_DATA_SPDEF_IV, &gBattleResources->bufferA[battler][3]); break; case REQUEST_PERSONALITY_BATTLE: SetMonData(&party[monId], MON_DATA_PERSONALITY, &gBattleResources->bufferA[battler][3]); break; case REQUEST_CHECKSUM_BATTLE: SetMonData(&party[monId], MON_DATA_CHECKSUM, &gBattleResources->bufferA[battler][3]); break; case REQUEST_STATUS_BATTLE: SetMonData(&party[monId], MON_DATA_STATUS, &gBattleResources->bufferA[battler][3]); break; case REQUEST_LEVEL_BATTLE: SetMonData(&party[monId], MON_DATA_LEVEL, &gBattleResources->bufferA[battler][3]); break; case REQUEST_HP_BATTLE: SetMonData(&party[monId], MON_DATA_HP, &gBattleResources->bufferA[battler][3]); break; case REQUEST_MAX_HP_BATTLE: SetMonData(&party[monId], MON_DATA_MAX_HP, &gBattleResources->bufferA[battler][3]); break; case REQUEST_ATK_BATTLE: SetMonData(&party[monId], MON_DATA_ATK, &gBattleResources->bufferA[battler][3]); break; case REQUEST_DEF_BATTLE: SetMonData(&party[monId], MON_DATA_DEF, &gBattleResources->bufferA[battler][3]); break; case REQUEST_SPEED_BATTLE: SetMonData(&party[monId], MON_DATA_SPEED, &gBattleResources->bufferA[battler][3]); break; case REQUEST_SPATK_BATTLE: SetMonData(&party[monId], MON_DATA_SPATK, &gBattleResources->bufferA[battler][3]); break; case REQUEST_SPDEF_BATTLE: SetMonData(&party[monId], MON_DATA_SPDEF, &gBattleResources->bufferA[battler][3]); break; case REQUEST_COOL_BATTLE: SetMonData(&party[monId], MON_DATA_COOL, &gBattleResources->bufferA[battler][3]); break; case REQUEST_BEAUTY_BATTLE: SetMonData(&party[monId], MON_DATA_BEAUTY, &gBattleResources->bufferA[battler][3]); break; case REQUEST_CUTE_BATTLE: SetMonData(&party[monId], MON_DATA_CUTE, &gBattleResources->bufferA[battler][3]); break; case REQUEST_SMART_BATTLE: SetMonData(&party[monId], MON_DATA_SMART, &gBattleResources->bufferA[battler][3]); break; case REQUEST_TOUGH_BATTLE: SetMonData(&party[monId], MON_DATA_TOUGH, &gBattleResources->bufferA[battler][3]); break; case REQUEST_SHEEN_BATTLE: SetMonData(&party[monId], MON_DATA_SHEEN, &gBattleResources->bufferA[battler][3]); break; case REQUEST_COOL_RIBBON_BATTLE: SetMonData(&party[monId], MON_DATA_COOL_RIBBON, &gBattleResources->bufferA[battler][3]); break; case REQUEST_BEAUTY_RIBBON_BATTLE: SetMonData(&party[monId], MON_DATA_BEAUTY_RIBBON, &gBattleResources->bufferA[battler][3]); break; case REQUEST_CUTE_RIBBON_BATTLE: SetMonData(&party[monId], MON_DATA_CUTE_RIBBON, &gBattleResources->bufferA[battler][3]); break; case REQUEST_SMART_RIBBON_BATTLE: SetMonData(&party[monId], MON_DATA_SMART_RIBBON, &gBattleResources->bufferA[battler][3]); break; case REQUEST_TOUGH_RIBBON_BATTLE: SetMonData(&party[monId], MON_DATA_TOUGH_RIBBON, &gBattleResources->bufferA[battler][3]); break; } if (GetBattlerSide(battler) == B_SIDE_PLAYER) HandleLowHpMusicChange(&party[gBattlerPartyIndexes[battler]], battler); } // In normal singles, if follower pokemon is out, have it slide in instead of being thrown static bool8 ShouldDoSlideInAnim(void) { struct ObjectEvent *followerObj = GetFollowerObject(); if (!followerObj || followerObj->invisible) return FALSE; if (gBattleTypeFlags & ( BATTLE_TYPE_LINK | BATTLE_TYPE_DOUBLE | BATTLE_TYPE_FRONTIER | BATTLE_TYPE_FIRST_BATTLE | BATTLE_TYPE_SAFARI | BATTLE_TYPE_WALLY_TUTORIAL | BATTLE_TYPE_EREADER_TRAINER | BATTLE_TYPE_TWO_OPPONENTS | BATTLE_TYPE_INGAME_PARTNER | BATTLE_TYPE_RECORDED | BATTLE_TYPE_TRAINER_HILL) ) return FALSE; return TRUE; } void StartSendOutAnim(u32 battler, bool32 dontClearSubstituteBit, bool32 doSlideIn) { u16 species; u32 side = GetBattlerSide(battler); struct Pokemon *party = GetBattlerParty(battler); ClearTemporarySpeciesSpriteData(battler, dontClearSubstituteBit); gBattlerPartyIndexes[battler] = gBattleResources->bufferA[battler][1]; species = GetIllusionMonSpecies(battler); if (species == SPECIES_NONE) species = GetMonData(&party[gBattlerPartyIndexes[battler]], MON_DATA_SPECIES); gBattleControllerData[battler] = CreateInvisibleSpriteWithCallback(SpriteCB_WaitForBattlerBallReleaseAnim); // Load sprite for opponent only, player sprite is expected to be already loaded. if (side == B_SIDE_OPPONENT) BattleLoadMonSpriteGfx(&party[gBattlerPartyIndexes[battler]], battler); SetMultiuseSpriteTemplateToPokemon(species, GetBattlerPosition(battler)); gBattlerSpriteIds[battler] = CreateSprite(&gMultiuseSpriteTemplate, GetBattlerSpriteCoord(battler, BATTLER_COORD_X_2), GetBattlerSpriteDefault_Y(battler), GetBattlerSpriteSubpriority(battler)); gSprites[gBattlerSpriteIds[battler]].data[0] = battler; gSprites[gBattlerSpriteIds[battler]].data[2] = species; gSprites[gBattlerSpriteIds[battler]].oam.paletteNum = battler; StartSpriteAnim(&gSprites[gBattlerSpriteIds[battler]], 0); gSprites[gBattlerSpriteIds[battler]].invisible = TRUE; gSprites[gBattlerSpriteIds[battler]].callback = SpriteCallbackDummy; gSprites[gBattleControllerData[battler]].data[1] = gBattlerSpriteIds[battler]; gSprites[gBattleControllerData[battler]].data[2] = battler; gSprites[gBattleControllerData[battler]].data[0] = DoPokeballSendOutAnimation(battler, 0, (side == B_SIDE_OPPONENT) ? POKEBALL_OPPONENT_SENDOUT : (doSlideIn ? POKEBALL_PLAYER_SLIDEIN : POKEBALL_PLAYER_SENDOUT)); } static void FreeMonSprite(u32 battler) { FreeSpriteOamMatrix(&gSprites[gBattlerSpriteIds[battler]]); DestroySprite(&gSprites[gBattlerSpriteIds[battler]]); if (GetBattlerSide(battler) == B_SIDE_OPPONENT) HideBattlerShadowSprite(battler); SetHealthboxSpriteInvisible(gHealthboxSpriteIds[battler]); } static void Controller_ReturnMonToBall2(u32 battler) { if (!gBattleSpritesDataPtr->healthBoxesData[battler].specialAnimActive) { FreeMonSprite(battler); BattleControllerComplete(battler); } } static void Controller_ReturnMonToBall(u32 battler) { switch (gBattleSpritesDataPtr->healthBoxesData[battler].animationState) { case 0: if (gBattleSpritesDataPtr->battlerData[battler].behindSubstitute) InitAndLaunchSpecialAnimation(battler, battler, battler, B_ANIM_SUBSTITUTE_TO_MON); gBattleSpritesDataPtr->healthBoxesData[battler].animationState = 1; break; case 1: if (!gBattleSpritesDataPtr->healthBoxesData[battler].specialAnimActive) { gBattleSpritesDataPtr->healthBoxesData[battler].animationState = 0; InitAndLaunchSpecialAnimation(battler, battler, battler, (GetBattlerSide(battler) == B_SIDE_OPPONENT) ? B_ANIM_SWITCH_OUT_OPPONENT_MON : B_ANIM_SWITCH_OUT_PLAYER_MON); gBattlerControllerFuncs[battler] = Controller_ReturnMonToBall2; } break; } } static void Controller_FaintPlayerMon(u32 battler) { u32 spriteId = gBattlerSpriteIds[battler]; if (gSprites[spriteId].y + gSprites[spriteId].y2 > DISPLAY_HEIGHT) { BattleGfxSfxDummy2(GetMonData(&gPlayerParty[gBattlerPartyIndexes[battler]], MON_DATA_SPECIES)); FreeOamMatrix(gSprites[spriteId].oam.matrixNum); DestroySprite(&gSprites[spriteId]); SetHealthboxSpriteInvisible(gHealthboxSpriteIds[battler]); BattleControllerComplete(battler); } } static void Controller_FaintOpponentMon(u32 battler) { if (!gSprites[gBattlerSpriteIds[battler]].inUse) { SetHealthboxSpriteInvisible(gHealthboxSpriteIds[battler]); BattleControllerComplete(battler); } } static void Controller_DoMoveAnimation(u32 battler) { u16 move = gBattleResources->bufferA[battler][1] | (gBattleResources->bufferA[battler][2] << 8); switch (gBattleSpritesDataPtr->healthBoxesData[battler].animationState) { case 0: if (gBattleSpritesDataPtr->battlerData[battler].behindSubstitute && !gBattleSpritesDataPtr->battlerData[battler].flag_x8) { gBattleSpritesDataPtr->battlerData[battler].flag_x8 = 1; InitAndLaunchSpecialAnimation(battler, battler, battler, B_ANIM_SUBSTITUTE_TO_MON); } gBattleSpritesDataPtr->healthBoxesData[battler].animationState = 1; break; case 1: if (!gBattleSpritesDataPtr->healthBoxesData[battler].specialAnimActive) { SetBattlerSpriteAffineMode(ST_OAM_AFFINE_OFF); DoMoveAnim(move); gBattleSpritesDataPtr->healthBoxesData[battler].animationState = 2; } break; case 2: gAnimScriptCallback(); if (!gAnimScriptActive) { u8 multihit = gBattleResources->bufferA[battler][11]; SetBattlerSpriteAffineMode(ST_OAM_AFFINE_NORMAL); if (gBattleSpritesDataPtr->battlerData[battler].behindSubstitute && multihit < 2) { InitAndLaunchSpecialAnimation(battler, battler, battler, B_ANIM_MON_TO_SUBSTITUTE); gBattleSpritesDataPtr->battlerData[battler].flag_x8 = 0; } gBattleSpritesDataPtr->healthBoxesData[battler].animationState = 3; } break; case 3: if (!gBattleSpritesDataPtr->healthBoxesData[battler].specialAnimActive) { CopyAllBattleSpritesInvisibilities(); TrySetBehindSubstituteSpriteBit(battler, gBattleResources->bufferA[battler][1] | (gBattleResources->bufferA[battler][2] << 8)); gBattleSpritesDataPtr->healthBoxesData[battler].animationState = 0; BattleControllerComplete(battler); } break; } } static void Controller_HandleTrainerSlideBack(u32 battler) { if (gSprites[gBattlerSpriteIds[battler]].callback == SpriteCallbackDummy) { if (GetBattlerSide(battler) == B_SIDE_OPPONENT) FreeTrainerFrontPicPalette(gSprites[gBattlerSpriteIds[battler]].oam.affineParam); FreeSpriteOamMatrix(&gSprites[gBattlerSpriteIds[battler]]); DestroySprite(&gSprites[gBattlerSpriteIds[battler]]); BattleControllerComplete(battler); } } void Controller_WaitForHealthBar(u32 battler) { s16 hpValue = MoveBattleBar(battler, gHealthboxSpriteIds[battler], HEALTH_BAR, 0); SetHealthboxSpriteVisible(gHealthboxSpriteIds[battler]); if (hpValue != -1) { UpdateHpTextInHealthbox(gHealthboxSpriteIds[battler], HP_CURRENT, hpValue, gBattleMons[battler].maxHP); } else { if (GetBattlerSide(battler) == B_SIDE_PLAYER) HandleLowHpMusicChange(&gPlayerParty[gBattlerPartyIndexes[battler]], battler); BattleControllerComplete(battler); } } static void Controller_WaitForBallThrow(u32 battler) { if (!gDoingBattleAnim || !gBattleSpritesDataPtr->healthBoxesData[battler].specialAnimActive) BattleControllerComplete(battler); } static void Controller_WaitForBattleAnimation(u32 battler) { if (!gBattleSpritesDataPtr->healthBoxesData[battler].animFromTableActive) BattleControllerComplete(battler); } static void Controller_WaitForStatusAnimation(u32 battler) { if (!gBattleSpritesDataPtr->healthBoxesData[battler].statusAnimActive) BattleControllerComplete(battler); } static void Controller_WaitForTrainerPic(u32 battler) { if (gSprites[gBattlerSpriteIds[battler]].callback == SpriteCallbackDummy) BattleControllerComplete(battler); } void Controller_WaitForString(u32 battler) { if (!IsTextPrinterActive(B_WIN_MSG)) BattleControllerComplete(battler); } static void Controller_WaitForPartyStatusSummary(u32 battler) { if (gBattleSpritesDataPtr->healthBoxesData[battler].partyStatusDelayTimer++ > 92) { gBattleSpritesDataPtr->healthBoxesData[battler].partyStatusDelayTimer = 0; BattleControllerComplete(battler); } } static void Controller_HitAnimation(u32 battler) { u32 spriteId = gBattlerSpriteIds[battler]; if (gSprites[spriteId].data[1] == 32) { gSprites[spriteId].data[1] = 0; gSprites[spriteId].invisible = FALSE; gDoingBattleAnim = FALSE; BattleControllerComplete(battler); } else { if ((gSprites[spriteId].data[1] % 4) == 0) gSprites[spriteId].invisible ^= 1; gSprites[spriteId].data[1]++; } } // Used for all the commands which do nothing. void BtlController_Empty(u32 battler) { BattleControllerComplete(battler); } // Dummy function at the end of the table. void BtlController_TerminatorNop(u32 battler) { } void BattleControllerDummy(u32 battler) { } // Handlers of the controller commands void BtlController_HandleGetMonData(u32 battler) { u8 monData[sizeof(struct Pokemon) * 2 + 56]; // this allows to get full data of two pokemon, trying to get more will result in overwriting data struct Pokemon *party = GetBattlerParty(battler); u32 size = 0; u8 monToCheck; s32 i; if (gBattleResources->bufferA[battler][2] == 0) { size += GetBattlerMonData(battler, party, gBattlerPartyIndexes[battler], monData); } else { monToCheck = gBattleResources->bufferA[battler][2]; for (i = 0; i < PARTY_SIZE; i++) { if (monToCheck & 1) size += GetBattlerMonData(battler, party, i, monData + size); monToCheck >>= 1; } } BtlController_EmitDataTransfer(battler, BUFFER_B, size, monData); BattleControllerComplete(battler); } void BtlController_HandleGetRawMonData(u32 battler) { struct BattlePokemon battleMon; struct Pokemon *party = GetBattlerParty(battler); u8 *src = (u8 *)&party[gBattlerPartyIndexes[battler]] + gBattleResources->bufferA[battler][1]; u8 *dst = (u8 *)&battleMon + gBattleResources->bufferA[battler][1]; u8 i; for (i = 0; i < gBattleResources->bufferA[battler][2]; i++) dst[i] = src[i]; BtlController_EmitDataTransfer(battler, BUFFER_B, gBattleResources->bufferA[battler][2], dst); BattleControllerComplete(battler); } void BtlController_HandleSetMonData(u32 battler) { struct Pokemon *party = GetBattlerParty(battler); u32 i, monToCheck; if (gBattleResources->bufferA[battler][2] == 0) { SetBattlerMonData(battler, party, gBattlerPartyIndexes[battler]); } else { monToCheck = gBattleResources->bufferA[battler][2]; for (i = 0; i < PARTY_SIZE; i++) { if (monToCheck & 1) SetBattlerMonData(battler, party, i); monToCheck >>= 1; } } BattleControllerComplete(battler); } void BtlController_HandleSetRawMonData(u32 battler) { u32 i; struct Pokemon *party = GetBattlerParty(battler); u8 *dst = (u8 *)&party[gBattlerPartyIndexes[battler]] + gBattleResources->bufferA[battler][1]; for (i = 0; i < gBattleResources->bufferA[battler][2]; i++) dst[i] = gBattleResources->bufferA[battler][3 + i]; BattleControllerComplete(battler); } void BtlController_HandleLoadMonSprite(u32 battler, void (*controllerCallback)(u32 battler)) { struct Pokemon *party = GetBattlerParty(battler); u16 species = GetMonData(&party[gBattlerPartyIndexes[battler]], MON_DATA_SPECIES); BattleLoadMonSpriteGfx(&party[gBattlerPartyIndexes[battler]], battler); SetMultiuseSpriteTemplateToPokemon(species, GetBattlerPosition(battler)); gBattlerSpriteIds[battler] = CreateSprite(&gMultiuseSpriteTemplate, GetBattlerSpriteCoord(battler, BATTLER_COORD_X_2), GetBattlerSpriteDefault_Y(battler), GetBattlerSpriteSubpriority(battler)); gSprites[gBattlerSpriteIds[battler]].x2 = -DISPLAY_WIDTH; gSprites[gBattlerSpriteIds[battler]].data[0] = battler; gSprites[gBattlerSpriteIds[battler]].data[2] = species; gSprites[gBattlerSpriteIds[battler]].oam.paletteNum = battler; StartSpriteAnim(&gSprites[gBattlerSpriteIds[battler]], 0); SetBattlerShadowSpriteCallback(battler, species); gBattlerControllerFuncs[battler] = controllerCallback; } void BtlController_HandleSwitchInAnim(u32 battler, bool32 isPlayerSide, void (*controllerCallback)(u32 battler)) { if (isPlayerSide) ClearTemporarySpeciesSpriteData(battler, gBattleResources->bufferA[battler][2]); gBattlerPartyIndexes[battler] = gBattleResources->bufferA[battler][1]; if (isPlayerSide) BattleLoadMonSpriteGfx(&gPlayerParty[gBattlerPartyIndexes[battler]], battler); StartSendOutAnim(battler, gBattleResources->bufferA[battler][2], FALSE); gBattlerControllerFuncs[battler] = controllerCallback; } void BtlController_HandleReturnMonToBall(u32 battler) { if (gBattleResources->bufferA[battler][1] == 0) { gBattleSpritesDataPtr->healthBoxesData[battler].animationState = 0; gBattlerControllerFuncs[battler] = Controller_ReturnMonToBall; } else { FreeMonSprite(battler); BattleControllerComplete(battler); } } // 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 an animated back pic. #define sSpeedX data[0] void BtlController_HandleDrawTrainerPic(u32 battler, u32 trainerPicId, bool32 isFrontPic, s16 xPos, s16 yPos, s32 subpriority) { if (GetBattlerSide(battler) == B_SIDE_OPPONENT) // Always the front sprite for the opponent. { DecompressTrainerFrontPic(trainerPicId, battler); SetMultiuseSpriteTemplateToTrainerBack(trainerPicId, GetBattlerPosition(battler)); if (subpriority == -1) subpriority = GetBattlerSpriteSubpriority(battler); gBattlerSpriteIds[battler] = CreateSprite(&gMultiuseSpriteTemplate, xPos, yPos, subpriority); gSprites[gBattlerSpriteIds[battler]].oam.paletteNum = IndexOfSpritePaletteTag(gTrainerSprites[trainerPicId].palette.tag); gSprites[gBattlerSpriteIds[battler]].x2 = -DISPLAY_WIDTH; gSprites[gBattlerSpriteIds[battler]].sSpeedX = 2; gSprites[gBattlerSpriteIds[battler]].oam.affineParam = trainerPicId; } else // Player's side { if (isFrontPic) { DecompressTrainerFrontPic(trainerPicId, battler); SetMultiuseSpriteTemplateToTrainerFront(trainerPicId, GetBattlerPosition(battler)); if (subpriority == -1) subpriority = GetBattlerSpriteSubpriority(battler); gBattlerSpriteIds[battler] = CreateSprite(&gMultiuseSpriteTemplate, xPos, yPos, subpriority); gSprites[gBattlerSpriteIds[battler]].oam.paletteNum = IndexOfSpritePaletteTag(gTrainerSprites[trainerPicId].palette.tag); gSprites[gBattlerSpriteIds[battler]].oam.affineMode = ST_OAM_AFFINE_OFF; gSprites[gBattlerSpriteIds[battler]].hFlip = 1; gSprites[gBattlerSpriteIds[battler]].y2 = 48; } else { DecompressTrainerBackPic(trainerPicId, battler); SetMultiuseSpriteTemplateToTrainerBack(trainerPicId, GetBattlerPosition(battler)); if (subpriority == -1) subpriority = GetBattlerSpriteSubpriority(battler); gBattlerSpriteIds[battler] = CreateSprite(&gMultiuseSpriteTemplate, xPos, yPos, subpriority); gSprites[gBattlerSpriteIds[battler]].oam.paletteNum = battler; } gSprites[gBattlerSpriteIds[battler]].x2 = DISPLAY_WIDTH; gSprites[gBattlerSpriteIds[battler]].sSpeedX = -2; } gSprites[gBattlerSpriteIds[battler]].callback = SpriteCB_TrainerSlideIn; gBattlerControllerFuncs[battler] = Controller_WaitForTrainerPic; } void BtlController_HandleTrainerSlide(u32 battler, u32 trainerPicId) { if (GetBattlerSide(battler) == B_SIDE_PLAYER) { DecompressTrainerBackPic(trainerPicId, battler); SetMultiuseSpriteTemplateToTrainerBack(trainerPicId, GetBattlerPosition(battler)); gBattlerSpriteIds[battler] = CreateSprite(&gMultiuseSpriteTemplate, 80, (8 - gTrainerBacksprites[trainerPicId].coordinates.size) * 4 + 80, 30); gSprites[gBattlerSpriteIds[battler]].oam.paletteNum = battler; gSprites[gBattlerSpriteIds[battler]].x2 = -96; gSprites[gBattlerSpriteIds[battler]].sSpeedX = 2; } else { DecompressTrainerFrontPic(trainerPicId, battler); SetMultiuseSpriteTemplateToTrainerBack(trainerPicId, GetBattlerPosition(battler)); gBattlerSpriteIds[battler] = CreateSprite(&gMultiuseSpriteTemplate, 176, 40, 30); gSprites[gBattlerSpriteIds[battler]].oam.affineParam = trainerPicId; gSprites[gBattlerSpriteIds[battler]].oam.paletteNum = IndexOfSpritePaletteTag(gTrainerSprites[trainerPicId].palette.tag); gSprites[gBattlerSpriteIds[battler]].x2 = 96; gSprites[gBattlerSpriteIds[battler]].x += 32; gSprites[gBattlerSpriteIds[battler]].sSpeedX = -2; } gSprites[gBattlerSpriteIds[battler]].callback = SpriteCB_TrainerSlideIn; gBattlerControllerFuncs[battler] = Controller_WaitForTrainerPic; } #undef sSpeedX void BtlController_HandleTrainerSlideBack(u32 battler, s16 data0, bool32 startAnim) { u32 side = GetBattlerSide(battler); SetSpritePrimaryCoordsFromSecondaryCoords(&gSprites[gBattlerSpriteIds[battler]]); gSprites[gBattlerSpriteIds[battler]].data[0] = data0; gSprites[gBattlerSpriteIds[battler]].data[2] = (side == B_SIDE_PLAYER) ? -40 : 280; gSprites[gBattlerSpriteIds[battler]].data[4] = gSprites[gBattlerSpriteIds[battler]].y; gSprites[gBattlerSpriteIds[battler]].callback = StartAnimLinearTranslation; StoreSpriteCallbackInData6(&gSprites[gBattlerSpriteIds[battler]], SpriteCallbackDummy); if (startAnim) StartSpriteAnim(&gSprites[gBattlerSpriteIds[battler]], 1); gBattlerControllerFuncs[battler] = Controller_HandleTrainerSlideBack; } #define sSpeedX data[1] #define sSpeedY data[2] void BtlController_HandleFaintAnimation(u32 battler) { if (gBattleSpritesDataPtr->healthBoxesData[battler].animationState == 0) { if (gBattleSpritesDataPtr->battlerData[battler].behindSubstitute) InitAndLaunchSpecialAnimation(battler, battler, battler, B_ANIM_SUBSTITUTE_TO_MON); gBattleSpritesDataPtr->healthBoxesData[battler].animationState++; } else { if (!gBattleSpritesDataPtr->healthBoxesData[battler].specialAnimActive) { gBattleSpritesDataPtr->healthBoxesData[battler].animationState = 0; if (GetBattlerSide(battler) == B_SIDE_PLAYER) { HandleLowHpMusicChange(&gPlayerParty[gBattlerPartyIndexes[battler]], battler); gSprites[gBattlerSpriteIds[battler]].sSpeedX = 0; gSprites[gBattlerSpriteIds[battler]].sSpeedY = 5; PlaySE12WithPanning(SE_FAINT, SOUND_PAN_ATTACKER); gSprites[gBattlerSpriteIds[battler]].callback = SpriteCB_FaintSlideAnim; gBattlerControllerFuncs[battler] = Controller_FaintPlayerMon; } else { PlaySE12WithPanning(SE_FAINT, SOUND_PAN_TARGET); gSprites[gBattlerSpriteIds[battler]].callback = SpriteCB_FaintOpponentMon; gBattlerControllerFuncs[battler] = Controller_FaintOpponentMon; } // The player's sprite callback just slides the mon, the opponent's removes the sprite. // The player's sprite is removed in Controller_FaintPlayerMon. Controller_FaintOpponentMon only removes the healthbox once the sprite is removed by SpriteCB_FaintOpponentMon. } } } #undef sSpeedX #undef sSpeedY static void HandleBallThrow(u32 battler, u32 target, u32 animId, bool32 allowCriticalCapture) { gDoingBattleAnim = TRUE; if (allowCriticalCapture && IsCriticalCapture()) animId = B_ANIM_CRITICAL_CAPTURE_THROW; InitAndLaunchSpecialAnimation(battler, battler, target, animId); gBattlerControllerFuncs[battler] = Controller_WaitForBallThrow; } void BtlController_HandleSuccessBallThrowAnim(u32 battler, u32 target, u32 animId, bool32 allowCriticalCapture) { gBattleSpritesDataPtr->animationData->ballThrowCaseId = BALL_3_SHAKES_SUCCESS; HandleBallThrow(battler, target, animId, allowCriticalCapture); } void BtlController_HandleBallThrowAnim(u32 battler, u32 target, u32 animId, bool32 allowCriticalCapture) { gBattleSpritesDataPtr->animationData->ballThrowCaseId = gBattleResources->bufferA[battler][1]; HandleBallThrow(battler, target, animId, allowCriticalCapture); } void BtlController_HandleMoveAnimation(u32 battler, bool32 updateTvData) { if (!IsBattleSEPlaying(battler)) { u16 move = gBattleResources->bufferA[battler][1] | (gBattleResources->bufferA[battler][2] << 8); gAnimMoveTurn = gBattleResources->bufferA[battler][3]; gAnimMovePower = gBattleResources->bufferA[battler][4] | (gBattleResources->bufferA[battler][5] << 8); gAnimMoveDmg = gBattleResources->bufferA[battler][6] | (gBattleResources->bufferA[battler][7] << 8) | (gBattleResources->bufferA[battler][8] << 16) | (gBattleResources->bufferA[battler][9] << 24); gAnimFriendship = gBattleResources->bufferA[battler][10]; gWeatherMoveAnim = gBattleResources->bufferA[battler][12] | (gBattleResources->bufferA[battler][13] << 8); gAnimDisableStructPtr = (struct DisableStruct *)&gBattleResources->bufferA[battler][16]; gTransformedPersonalities[battler] = gAnimDisableStructPtr->transformedMonPersonality; gTransformedShininess[battler] = gAnimDisableStructPtr->transformedMonShininess; gBattleSpritesDataPtr->healthBoxesData[battler].animationState = 0; gBattlerControllerFuncs[battler] = Controller_DoMoveAnimation; if (updateTvData) BattleTv_SetDataBasedOnMove(move, gWeatherMoveAnim, gAnimDisableStructPtr); } } void BtlController_HandlePrintString(u32 battler, bool32 updateTvData, bool32 arenaPtsDeduct) { u16 *stringId; gBattle_BG0_X = 0; gBattle_BG0_Y = 0; stringId = (u16 *)(&gBattleResources->bufferA[battler][2]); BufferStringBattle(*stringId, battler); if (gTestRunnerEnabled) { TestRunner_Battle_RecordMessage(gDisplayedStringBattle); if (gTestRunnerHeadless) { BattleControllerComplete(battler); return; } } BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_MSG); gBattlerControllerFuncs[battler] = Controller_WaitForString; if (updateTvData) BattleTv_SetDataBasedOnString(*stringId); if (arenaPtsDeduct) BattleArena_DeductSkillPoints(battler, *stringId); } void BtlController_HandleHealthBarUpdate(u32 battler, bool32 updateHpText) { s32 maxHP, curHP; s16 hpVal; struct Pokemon *party = GetBattlerParty(battler); LoadBattleBarGfx(0); hpVal = gBattleResources->bufferA[battler][2] | (gBattleResources->bufferA[battler][3] << 8); maxHP = GetMonData(&party[gBattlerPartyIndexes[battler]], MON_DATA_MAX_HP); curHP = GetMonData(&party[gBattlerPartyIndexes[battler]], MON_DATA_HP); if (hpVal != INSTANT_HP_BAR_DROP) { SetBattleBarStruct(battler, gHealthboxSpriteIds[battler], maxHP, curHP, hpVal); TestRunner_Battle_RecordHP(battler, curHP, min(maxHP, max(0, curHP - hpVal))); } else { SetBattleBarStruct(battler, gHealthboxSpriteIds[battler], maxHP, 0, hpVal); if (updateHpText) UpdateHpTextInHealthbox(gHealthboxSpriteIds[battler], HP_CURRENT, 0, maxHP); TestRunner_Battle_RecordHP(battler, curHP, 0); } gBattlerControllerFuncs[battler] = Controller_WaitForHealthBar; } void DoStatusIconUpdate(u32 battler) { struct Pokemon *party = GetBattlerParty(battler); UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], &party[gBattlerPartyIndexes[battler]], HEALTHBOX_STATUS_ICON); gBattleSpritesDataPtr->healthBoxesData[battler].statusAnimActive = 0; gBattlerControllerFuncs[battler] = Controller_WaitForStatusAnimation; } void BtlController_HandleStatusIconUpdate(u32 battler) { if (!IsBattleSEPlaying(battler)) { DoStatusIconUpdate(battler); } } void BtlController_HandleStatusAnimation(u32 battler) { if (!IsBattleSEPlaying(battler)) { InitAndLaunchChosenStatusAnimation(battler, gBattleResources->bufferA[battler][1], gBattleResources->bufferA[battler][2] | (gBattleResources->bufferA[battler][3] << 8) | (gBattleResources->bufferA[battler][4] << 16) | (gBattleResources->bufferA[battler][5] << 24)); gBattlerControllerFuncs[battler] = Controller_WaitForStatusAnimation; } } void BtlController_HandleClearUnkVar(u32 battler) { gUnusedControllerStruct.unk = 0; BattleControllerComplete(battler); } void BtlController_HandleSetUnkVar(u32 battler) { gUnusedControllerStruct.unk = gBattleResources->bufferA[battler][1]; BattleControllerComplete(battler); } void BtlController_HandleClearUnkFlag(u32 battler) { gUnusedControllerStruct.flag = 0; BattleControllerComplete(battler); } void BtlController_HandleToggleUnkFlag(u32 battler) { gUnusedControllerStruct.flag ^= 1; BattleControllerComplete(battler); } void BtlController_HandleHitAnimation(u32 battler) { if (gSprites[gBattlerSpriteIds[battler]].invisible == TRUE) { BattleControllerComplete(battler); } else { gDoingBattleAnim = TRUE; gSprites[gBattlerSpriteIds[battler]].data[1] = 0; DoHitAnimHealthboxEffect(battler); gBattlerControllerFuncs[battler] = Controller_HitAnimation; } } void BtlController_HandlePlaySE(u32 battler) { s8 pan = (GetBattlerSide(battler) == B_SIDE_PLAYER) ? SOUND_PAN_ATTACKER : SOUND_PAN_TARGET; PlaySE12WithPanning(gBattleResources->bufferA[battler][1] | (gBattleResources->bufferA[battler][2] << 8), pan); BattleControllerComplete(battler); } void BtlController_HandlePlayFanfareOrBGM(u32 battler) { if (gBattleResources->bufferA[battler][3]) { BattleStopLowHpSound(); PlayBGM(gBattleResources->bufferA[battler][1] | (gBattleResources->bufferA[battler][2] << 8)); } else { PlayFanfare(gBattleResources->bufferA[battler][1] | (gBattleResources->bufferA[battler][2] << 8)); } BattleControllerComplete(battler); } void BtlController_HandleFaintingCry(u32 battler) { struct Pokemon *party; s8 pan; if (GetBattlerSide(battler) == B_SIDE_PLAYER) { party = gPlayerParty; pan = -25; } else { party = gEnemyParty; pan = 25; } PlayCry_ByMode(GetMonData(&party[gBattlerPartyIndexes[battler]], MON_DATA_SPECIES), pan, CRY_MODE_FAINT); BattleControllerComplete(battler); } void BtlController_HandleIntroSlide(u32 battler) { HandleIntroSlide(gBattleResources->bufferA[battler][1]); gIntroSlideFlags |= 1; BattleControllerComplete(battler); } void BtlController_HandleSpriteInvisibility(u32 battler) { if (IsBattlerSpritePresent(battler)) { gSprites[gBattlerSpriteIds[battler]].invisible = gBattleResources->bufferA[battler][1]; CopyBattleSpriteInvisibility(battler); } BattleControllerComplete(battler); } bool32 TwoPlayerIntroMons(u32 battler) // Double battle with both player pokemon active. { return (IsDoubleBattle() && IsValidForBattle(&gPlayerParty[gBattlerPartyIndexes[battler ^ BIT_FLANK]])); } bool32 TwoOpponentIntroMons(u32 battler) // Double battle with both opponent pokemon active. { return (IsDoubleBattle() && IsValidForBattle(&gEnemyParty[gBattlerPartyIndexes[battler]]) && IsValidForBattle(&gEnemyParty[gBattlerPartyIndexes[BATTLE_PARTNER(battler)]])); } // Task data for Task_StartSendOutAnim #define tBattlerId data[0] #define tStartTimer data[1] #define tFramesToWait data[2] #define tControllerFunc_1 3 // Stored as two halfwords #define tControllerFunc_2 4 // Sprite data for SpriteCB_FreePlayerSpriteLoadMonSprite #define sBattlerId data[5] void BtlController_HandleIntroTrainerBallThrow(u32 battler, u16 tagTrainerPal, const u32 *trainerPal, s16 framesToWait, void (*controllerCallback)(u32 battler)) { u8 paletteNum, taskId; u32 side = GetBattlerSide(battler); SetSpritePrimaryCoordsFromSecondaryCoords(&gSprites[gBattlerSpriteIds[battler]]); if (side == B_SIDE_PLAYER) { gSprites[gBattlerSpriteIds[battler]].data[0] = 50; gSprites[gBattlerSpriteIds[battler]].data[2] = -40; } else { gSprites[gBattlerSpriteIds[battler]].data[0] = 35; gSprites[gBattlerSpriteIds[battler]].data[2] = 280; } gSprites[gBattlerSpriteIds[battler]].data[4] = gSprites[gBattlerSpriteIds[battler]].y; gSprites[gBattlerSpriteIds[battler]].callback = StartAnimLinearTranslation; gSprites[gBattlerSpriteIds[battler]].sBattlerId = battler; if (side == B_SIDE_PLAYER) { StoreSpriteCallbackInData6(&gSprites[gBattlerSpriteIds[battler]], SpriteCB_FreePlayerSpriteLoadMonSprite); StartSpriteAnim(&gSprites[gBattlerSpriteIds[battler]], ShouldDoSlideInAnim() ? 2 : 1); paletteNum = AllocSpritePalette(tagTrainerPal); LoadCompressedPalette(trainerPal, OBJ_PLTT_ID(paletteNum), PLTT_SIZE_4BPP); gSprites[gBattlerSpriteIds[battler]].oam.paletteNum = paletteNum; } else { StoreSpriteCallbackInData6(&gSprites[gBattlerSpriteIds[battler]], SpriteCB_FreeOpponentSprite); } taskId = CreateTask(Task_StartSendOutAnim, 5); gTasks[taskId].tBattlerId = battler; gTasks[taskId].tFramesToWait = framesToWait; SetWordTaskArg(taskId, tControllerFunc_1, (uint32_t)(controllerCallback)); if (gBattleSpritesDataPtr->healthBoxesData[battler].partyStatusSummaryShown) gTasks[gBattlerStatusSummaryTaskId[battler]].func = Task_HidePartyStatusSummary; gBattleSpritesDataPtr->animationData->introAnimActive = TRUE; gBattlerControllerFuncs[battler] = BattleControllerDummy; } static bool32 TwoMonsAtSendOut(u32 battler) { if (GetBattlerSide(battler) == B_SIDE_PLAYER) { if (TwoPlayerIntroMons(battler) && !(gBattleTypeFlags & BATTLE_TYPE_MULTI)) return TRUE; else return FALSE; } else { if ((!TwoOpponentIntroMons(battler) || (gBattleTypeFlags & BATTLE_TYPE_MULTI)) && !BATTLE_TWO_VS_ONE_OPPONENT) return FALSE; else if ((gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) || (BATTLE_TWO_VS_ONE_OPPONENT && !TwoOpponentIntroMons(battler))) return FALSE; else return TRUE; } return FALSE; } // Send out at start of battle static void Task_StartSendOutAnim(u8 taskId) { if (gTasks[taskId].tFramesToWait != 0 && gTasks[taskId].tStartTimer < gTasks[taskId].tFramesToWait) { gTasks[taskId].tStartTimer++; } else { u32 battlerPartner; u32 battler = gTasks[taskId].tBattlerId; if (TwoMonsAtSendOut(battler)) { gBattleResources->bufferA[battler][1] = gBattlerPartyIndexes[battler]; StartSendOutAnim(battler, FALSE, ShouldDoSlideInAnim()); battlerPartner = battler ^ BIT_FLANK; gBattleResources->bufferA[battlerPartner][1] = gBattlerPartyIndexes[battlerPartner]; BattleLoadMonSpriteGfx(&gPlayerParty[gBattlerPartyIndexes[battlerPartner]], battlerPartner); StartSendOutAnim(battlerPartner, FALSE, ShouldDoSlideInAnim()); } else { gBattleResources->bufferA[battler][1] = gBattlerPartyIndexes[battler]; StartSendOutAnim(battler, FALSE, ShouldDoSlideInAnim()); } gBattlerControllerFuncs[battler] = (void*)(GetWordTaskArg(taskId, tControllerFunc_1)); DestroyTask(taskId); } } #undef tBattlerId #undef tStartTimer #undef tFramesToWait #undef tControllerFunc_1 #undef tControllerFunc_2 static void SpriteCB_FreePlayerSpriteLoadMonSprite(struct Sprite *sprite) { u8 battler = sprite->sBattlerId; // Free player trainer sprite FreeSpriteOamMatrix(sprite); FreeSpritePaletteByTag(GetSpritePaletteTagByPaletteNum(sprite->oam.paletteNum)); DestroySprite(sprite); // Load mon sprite BattleLoadMonSpriteGfx(&gPlayerParty[gBattlerPartyIndexes[battler]], battler); StartSpriteAnim(&gSprites[gBattlerSpriteIds[battler]], 0); } static void SpriteCB_FreeOpponentSprite(struct Sprite *sprite) { FreeTrainerFrontPicPalette(sprite->oam.affineParam); FreeSpriteOamMatrix(sprite); DestroySprite(sprite); } #undef sBattlerId void BtlController_HandleDrawPartyStatusSummary(u32 battler, u32 side, bool32 considerDelay) { if (gBattleResources->bufferA[battler][1] != 0 && GetBattlerSide(battler) == B_SIDE_PLAYER) { BattleControllerComplete(battler); } else { gBattleSpritesDataPtr->healthBoxesData[battler].partyStatusSummaryShown = 1; if (side == B_SIDE_OPPONENT && gBattleResources->bufferA[battler][2] != 0) { if (gBattleSpritesDataPtr->healthBoxesData[battler].opponentDrawPartyStatusSummaryDelay < 2) { gBattleSpritesDataPtr->healthBoxesData[battler].opponentDrawPartyStatusSummaryDelay++; return; } else { gBattleSpritesDataPtr->healthBoxesData[battler].opponentDrawPartyStatusSummaryDelay = 0; } } gBattlerStatusSummaryTaskId[battler] = CreatePartyStatusSummarySprites(battler, (struct HpAndStatus *)&gBattleResources->bufferA[battler][4], gBattleResources->bufferA[battler][1], gBattleResources->bufferA[battler][2]); gBattleSpritesDataPtr->healthBoxesData[battler].partyStatusDelayTimer = 0; // If intro, skip the delay after drawing if (considerDelay && gBattleResources->bufferA[battler][2] != 0) gBattleSpritesDataPtr->healthBoxesData[battler].partyStatusDelayTimer = 93; gBattlerControllerFuncs[battler] = Controller_WaitForPartyStatusSummary; } } void BtlController_HandleHidePartyStatusSummary(u32 battler) { if (gBattleSpritesDataPtr->healthBoxesData[battler].partyStatusSummaryShown) gTasks[gBattlerStatusSummaryTaskId[battler]].func = Task_HidePartyStatusSummary; BattleControllerComplete(battler); } void BtlController_HandleBattleAnimation(u32 battler, bool32 ignoreSE, bool32 updateTvData) { if (ignoreSE || !IsBattleSEPlaying(battler)) { u8 animationId = gBattleResources->bufferA[battler][1]; u16 argument = gBattleResources->bufferA[battler][2] | (gBattleResources->bufferA[battler][3] << 8); gAnimDisableStructPtr = (struct DisableStruct *)&gBattleResources->bufferA[battler][4]; if (TryHandleLaunchBattleTableAnimation(battler, battler, battler, animationId, argument)) BattleControllerComplete(battler); else gBattlerControllerFuncs[battler] = Controller_WaitForBattleAnimation; if (updateTvData) BattleTv_SetDataBasedOnAnimation(animationId); } }