#include "global.h" #include "battle.h" #include "battle_ai_script_commands.h" #include "battle_anim.h" #include "battle_controllers.h" #include "battle_message.h" #include "cable_club.h" #include "link.h" #include "link_rfu.h" #include "party_menu.h" #include "recorded_battle.h" #include "task.h" #include "util.h" #include "constants/abilities.h" static EWRAM_DATA u8 sLinkSendTaskId = 0; static EWRAM_DATA u8 sLinkReceiveTaskId = 0; static EWRAM_DATA u8 sUnused = 0; // Debug? Never read EWRAM_DATA struct UnusedControllerStruct gUnusedControllerStruct = {}; // Debug? Unused code that writes to it, never read static EWRAM_DATA u8 sBattleBuffersTransferData[0x100] = {}; 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); 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(); ClearBattleMonForms(); BattleAI_HandleItemUseBeforeAISetup(0xF); 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); } // Below are never read gUnusedFirstBattleVar1 = 0; gUnusedFirstBattleVar2 = 0; } 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[B_BATTLER_0] = SetControllerToRecordedPlayer; gBattlerPositions[B_BATTLER_0] = B_POSITION_PLAYER_LEFT; gBattlerControllerFuncs[B_BATTLER_1] = SetControllerToOpponent; gBattlerPositions[B_BATTLER_1] = B_POSITION_OPPONENT_LEFT; gBattlerControllerFuncs[B_BATTLER_2] = SetControllerToPlayerPartner; gBattlerPositions[B_BATTLER_2] = B_POSITION_PLAYER_RIGHT; gBattlerControllerFuncs[B_BATTLER_3] = SetControllerToOpponent; gBattlerPositions[B_BATTLER_3] = B_POSITION_OPPONENT_RIGHT; } else { gBattlerControllerFuncs[B_BATTLER_0] = SetControllerToPlayer; gBattlerPositions[B_BATTLER_0] = B_POSITION_PLAYER_LEFT; gBattlerControllerFuncs[B_BATTLER_1] = SetControllerToOpponent; gBattlerPositions[B_BATTLER_1] = B_POSITION_OPPONENT_LEFT; gBattlerControllerFuncs[B_BATTLER_2] = SetControllerToPlayerPartner; gBattlerPositions[B_BATTLER_2] = B_POSITION_PLAYER_RIGHT; gBattlerControllerFuncs[B_BATTLER_3] = SetControllerToOpponent; gBattlerPositions[B_BATTLER_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_DOUBLE)) { gBattleMainFunc = BeginBattleIntro; if (gBattleTypeFlags & BATTLE_TYPE_SAFARI) gBattlerControllerFuncs[B_BATTLER_0] = SetControllerToSafari; else if (gBattleTypeFlags & BATTLE_TYPE_WALLY_TUTORIAL) gBattlerControllerFuncs[B_BATTLER_0] = SetControllerToWally; else gBattlerControllerFuncs[B_BATTLER_0] = SetControllerToPlayer; gBattlerPositions[B_BATTLER_0] = B_POSITION_PLAYER_LEFT; gBattlerControllerFuncs[B_BATTLER_1] = SetControllerToOpponent; gBattlerPositions[B_BATTLER_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[B_BATTLER_0] = SetControllerToRecordedPlayer; gBattlerPositions[B_BATTLER_0] = B_POSITION_PLAYER_LEFT; gBattlerControllerFuncs[B_BATTLER_1] = SetControllerToRecordedOpponent; gBattlerPositions[B_BATTLER_1] = B_POSITION_OPPONENT_LEFT; gBattlersCount = 2; } else // see how the banks are switched { gBattlerControllerFuncs[B_BATTLER_1] = SetControllerToRecordedPlayer; gBattlerPositions[B_BATTLER_1] = B_POSITION_PLAYER_LEFT; gBattlerControllerFuncs[B_BATTLER_0] = SetControllerToRecordedOpponent; gBattlerPositions[B_BATTLER_0] = B_POSITION_OPPONENT_LEFT; gBattlersCount = 2; } } else { gBattlerControllerFuncs[B_BATTLER_0] = SetControllerToRecordedPlayer; gBattlerPositions[B_BATTLER_0] = B_POSITION_PLAYER_LEFT; gBattlerControllerFuncs[B_BATTLER_1] = SetControllerToOpponent; gBattlerPositions[B_BATTLER_1] = B_POSITION_OPPONENT_LEFT; } } } else { gBattleMainFunc = BeginBattleIntro; gBattlerControllerFuncs[B_BATTLER_0] = SetControllerToPlayer; gBattlerPositions[B_BATTLER_0] = B_POSITION_PLAYER_LEFT; gBattlerControllerFuncs[B_BATTLER_1] = SetControllerToOpponent; gBattlerPositions[B_BATTLER_1] = B_POSITION_OPPONENT_LEFT; gBattlerControllerFuncs[B_BATTLER_2] = SetControllerToPlayer; gBattlerPositions[B_BATTLER_2] = B_POSITION_PLAYER_RIGHT; gBattlerControllerFuncs[B_BATTLER_3] = SetControllerToOpponent; gBattlerPositions[B_BATTLER_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[B_BATTLER_0] = SetControllerToRecordedPlayer; gBattlerPositions[B_BATTLER_0] = B_POSITION_PLAYER_LEFT; gBattlerControllerFuncs[B_BATTLER_1] = SetControllerToOpponent; gBattlerPositions[B_BATTLER_1] = B_POSITION_OPPONENT_LEFT; gBattlerControllerFuncs[B_BATTLER_2] = SetControllerToRecordedPlayer; gBattlerPositions[B_BATTLER_2] = B_POSITION_PLAYER_RIGHT; gBattlerControllerFuncs[B_BATTLER_3] = SetControllerToOpponent; gBattlerPositions[B_BATTLER_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[B_BATTLER_0] = SetControllerToRecordedPlayer; gBattlerPositions[B_BATTLER_0] = B_POSITION_PLAYER_LEFT; gBattlerControllerFuncs[B_BATTLER_2] = SetControllerToRecordedPlayer; gBattlerPositions[B_BATTLER_2] = B_POSITION_PLAYER_RIGHT; if (gBattleTypeFlags & BATTLE_TYPE_RECORDED_LINK) { gBattlerControllerFuncs[B_BATTLER_1] = SetControllerToRecordedOpponent; gBattlerPositions[B_BATTLER_1] = B_POSITION_OPPONENT_LEFT; gBattlerControllerFuncs[B_BATTLER_3] = SetControllerToRecordedOpponent; gBattlerPositions[B_BATTLER_3] = B_POSITION_OPPONENT_RIGHT; } else { gBattlerControllerFuncs[B_BATTLER_1] = SetControllerToOpponent; gBattlerPositions[B_BATTLER_1] = B_POSITION_OPPONENT_LEFT; gBattlerControllerFuncs[B_BATTLER_3] = SetControllerToOpponent; gBattlerPositions[B_BATTLER_3] = B_POSITION_OPPONENT_RIGHT; } } else { gBattlerControllerFuncs[B_BATTLER_1] = SetControllerToRecordedPlayer; gBattlerPositions[B_BATTLER_1] = B_POSITION_PLAYER_LEFT; gBattlerControllerFuncs[B_BATTLER_3] = SetControllerToRecordedPlayer; gBattlerPositions[B_BATTLER_3] = B_POSITION_PLAYER_RIGHT; if (gBattleTypeFlags & BATTLE_TYPE_RECORDED_LINK) { gBattlerControllerFuncs[B_BATTLER_0] = SetControllerToRecordedOpponent; gBattlerPositions[B_BATTLER_0] = B_POSITION_OPPONENT_LEFT; gBattlerControllerFuncs[B_BATTLER_2] = SetControllerToRecordedOpponent; gBattlerPositions[B_BATTLER_2] = B_POSITION_OPPONENT_RIGHT; } else { gBattlerControllerFuncs[B_BATTLER_0] = SetControllerToOpponent; gBattlerPositions[B_BATTLER_0] = B_POSITION_OPPONENT_LEFT; gBattlerControllerFuncs[B_BATTLER_2] = SetControllerToOpponent; gBattlerPositions[B_BATTLER_2] = B_POSITION_OPPONENT_RIGHT; } } } } } static void InitLinkBtlControllers(void) { s32 i; u8 multiplayerId; if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE)) { if (gBattleTypeFlags & BATTLE_TYPE_IS_MASTER) { gBattleMainFunc = BeginBattleIntro; gBattlerControllerFuncs[B_BATTLER_0] = SetControllerToPlayer; gBattlerPositions[B_BATTLER_0] = B_POSITION_PLAYER_LEFT; gBattlerControllerFuncs[B_BATTLER_1] = SetControllerToLinkOpponent; gBattlerPositions[B_BATTLER_1] = B_POSITION_OPPONENT_LEFT; gBattlersCount = 2; } else { gBattlerControllerFuncs[B_BATTLER_1] = SetControllerToPlayer; gBattlerPositions[B_BATTLER_1] = B_POSITION_PLAYER_LEFT; gBattlerControllerFuncs[B_BATTLER_0] = SetControllerToLinkOpponent; gBattlerPositions[B_BATTLER_0] = B_POSITION_OPPONENT_LEFT; gBattlersCount = 2; } } else if (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && gBattleTypeFlags & BATTLE_TYPE_DOUBLE) { if (gBattleTypeFlags & BATTLE_TYPE_IS_MASTER) { gBattleMainFunc = BeginBattleIntro; gBattlerControllerFuncs[B_BATTLER_0] = SetControllerToPlayer; gBattlerPositions[B_BATTLER_0] = B_POSITION_PLAYER_LEFT; gBattlerControllerFuncs[B_BATTLER_1] = SetControllerToLinkOpponent; gBattlerPositions[B_BATTLER_1] = B_POSITION_OPPONENT_LEFT; gBattlerControllerFuncs[B_BATTLER_2] = SetControllerToPlayer; gBattlerPositions[B_BATTLER_2] = B_POSITION_PLAYER_RIGHT; gBattlerControllerFuncs[B_BATTLER_3] = SetControllerToLinkOpponent; gBattlerPositions[B_BATTLER_3] = B_POSITION_OPPONENT_RIGHT; gBattlersCount = MAX_BATTLERS_COUNT; } else { gBattlerControllerFuncs[B_BATTLER_1] = SetControllerToPlayer; gBattlerPositions[B_BATTLER_1] = B_POSITION_PLAYER_LEFT; gBattlerControllerFuncs[B_BATTLER_0] = SetControllerToLinkOpponent; gBattlerPositions[B_BATTLER_0] = B_POSITION_OPPONENT_LEFT; gBattlerControllerFuncs[B_BATTLER_3] = SetControllerToPlayer; gBattlerPositions[B_BATTLER_3] = B_POSITION_PLAYER_RIGHT; gBattlerControllerFuncs[B_BATTLER_2] = SetControllerToLinkOpponent; gBattlerPositions[B_BATTLER_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[B_BATTLER_0] = SetControllerToPlayer; gBattlerPositions[B_BATTLER_0] = B_POSITION_PLAYER_LEFT; gBattlerControllerFuncs[B_BATTLER_1] = SetControllerToOpponent; gBattlerPositions[B_BATTLER_1] = B_POSITION_OPPONENT_LEFT; gBattlerControllerFuncs[B_BATTLER_2] = SetControllerToLinkPartner; gBattlerPositions[B_BATTLER_2] = B_POSITION_PLAYER_RIGHT; gBattlerControllerFuncs[B_BATTLER_3] = SetControllerToOpponent; gBattlerPositions[B_BATTLER_3] = B_POSITION_OPPONENT_RIGHT; gBattlersCount = MAX_BATTLERS_COUNT; } else { gBattlerControllerFuncs[B_BATTLER_0] = SetControllerToLinkPartner; gBattlerPositions[B_BATTLER_0] = B_POSITION_PLAYER_LEFT; gBattlerControllerFuncs[B_BATTLER_1] = SetControllerToLinkOpponent; gBattlerPositions[B_BATTLER_1] = B_POSITION_OPPONENT_LEFT; gBattlerControllerFuncs[B_BATTLER_2] = SetControllerToPlayer; gBattlerPositions[B_BATTLER_2] = B_POSITION_PLAYER_RIGHT; gBattlerControllerFuncs[B_BATTLER_3] = SetControllerToLinkOpponent; gBattlerPositions[B_BATTLER_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; } } 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 (GET_BATTLER_SIDE2(i) == B_SIDE_PLAYER) { if (GetMonData(&gPlayerParty[j], MON_DATA_HP) != 0 && GetMonData(&gPlayerParty[j], MON_DATA_SPECIES_OR_EGG) != SPECIES_NONE && GetMonData(&gPlayerParty[j], MON_DATA_SPECIES_OR_EGG) != SPECIES_EGG && !GetMonData(&gPlayerParty[j], MON_DATA_IS_EGG)) { gBattlerPartyIndexes[i] = j; break; } } else { if (GetMonData(&gEnemyParty[j], MON_DATA_HP) != 0 && GetMonData(&gEnemyParty[j], MON_DATA_SPECIES_OR_EGG) != SPECIES_NONE && GetMonData(&gEnemyParty[j], MON_DATA_SPECIES_OR_EGG) != SPECIES_EGG && !GetMonData(&gEnemyParty[j], MON_DATA_IS_EGG)) { gBattlerPartyIndexes[i] = j; break; } } } else { if (GET_BATTLER_SIDE2(i) == B_SIDE_PLAYER) { if (GetMonData(&gPlayerParty[j], MON_DATA_HP) != 0 && GetMonData(&gPlayerParty[j], MON_DATA_SPECIES) != SPECIES_NONE // Probably a typo by Game Freak. The rest use SPECIES2. && GetMonData(&gPlayerParty[j], MON_DATA_SPECIES_OR_EGG) != SPECIES_EGG && !GetMonData(&gPlayerParty[j], MON_DATA_IS_EGG) && gBattlerPartyIndexes[i - 2] != j) { gBattlerPartyIndexes[i] = j; break; } } else { if (GetMonData(&gEnemyParty[j], MON_DATA_HP) != 0 && GetMonData(&gEnemyParty[j], MON_DATA_SPECIES_OR_EGG) != SPECIES_NONE && GetMonData(&gEnemyParty[j], MON_DATA_SPECIES_OR_EGG) != SPECIES_EGG && !GetMonData(&gEnemyParty[j], MON_DATA_IS_EGG) && gBattlerPartyIndexes[i - 2] != j) { gBattlerPartyIndexes[i] = j; break; } } } } } if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) gBattlerPartyIndexes[1] = 0, gBattlerPartyIndexes[3] = 3; } } static void PrepareBufferDataTransfer(u8 bufferId, u8 *data, u16 size) { s32 i; if (gBattleTypeFlags & BATTLE_TYPE_LINK) { PrepareBufferDataTransferLink(bufferId, size, data); } else { switch (bufferId) { case B_COMM_TO_CONTROLLER: for (i = 0; i < size; data++, i++) gBattleBufferA[gActiveBattler][i] = *data; break; case B_COMM_TO_ENGINE: for (i = 0; i < size; data++, i++) gBattleBufferB[gActiveBattler][i] = *data; break; } } } #define tInitialDelayTimer data[10] #define tState data[11] #define tCurrentBlock_WrapFrom data[12] #define tBlockSendDelayTimer data[13] #define tCurrentBlock_End data[14] #define tCurrentBlock_Start data[15] // // Inbound communications are stored in one buffer; outbound communications // in another. Both buffers work pretty similarly: the next message will be // written into the buffer after the previous message (with 4-byte alignment), // unless we're too close to the end of the buffer, in which case we jump back // to the start. // // Regarding the task variable names above: these variables are altered in- // place, so the precise operational definition of "current" depends on when // the variables are being accessed. When data is present in the send and // receive buffers, "current" refers to the most recently received message; // but when a message is actually being placed in the buffers, "current" // refers to the previous message, until such time that we finish updating it // to refer to the new message. static void CreateTasksForSendRecvLinkBuffers(void) { sLinkSendTaskId = CreateTask(Task_HandleSendLinkBuffersData, 0); gTasks[sLinkSendTaskId].tState = 0; gTasks[sLinkSendTaskId].tCurrentBlock_WrapFrom = 0; gTasks[sLinkSendTaskId].tBlockSendDelayTimer = 0; gTasks[sLinkSendTaskId].tCurrentBlock_End = 0; gTasks[sLinkSendTaskId].tCurrentBlock_Start = 0; sLinkReceiveTaskId = CreateTask(Task_HandleCopyReceivedLinkBuffersData, 0); gTasks[sLinkReceiveTaskId].tCurrentBlock_WrapFrom = 0; gTasks[sLinkReceiveTaskId].tBlockSendDelayTimer = 0; // not used by "receive" task gTasks[sLinkReceiveTaskId].tCurrentBlock_End = 0; gTasks[sLinkReceiveTaskId].tCurrentBlock_Start = 0; sUnused = 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, }; // We want to send a message. Place it into the "send" buffer. // First argument is a BATTLELINKCOMMTYPE_... void PrepareBufferDataTransferLink(u8 bufferId, u16 size, u8 *data) { s32 alignedSize; s32 i; alignedSize = size - size % 4 + 4; if (gTasks[sLinkSendTaskId].tCurrentBlock_End + alignedSize + LINK_BUFF_DATA + 1 > BATTLE_BUFFER_LINK_SIZE) { gTasks[sLinkSendTaskId].tCurrentBlock_WrapFrom = gTasks[sLinkSendTaskId].tCurrentBlock_End; gTasks[sLinkSendTaskId].tCurrentBlock_End = 0; } #define BYTE_TO_SEND(offset) \ gLinkBattleSendBuffer[gTasks[sLinkSendTaskId].tCurrentBlock_End + offset] BYTE_TO_SEND(LINK_BUFF_BUFFER_ID) = bufferId; BYTE_TO_SEND(LINK_BUFF_ACTIVE_BATTLER) = gActiveBattler; BYTE_TO_SEND(LINK_BUFF_ATTACKER) = gBattlerAttacker; BYTE_TO_SEND(LINK_BUFF_TARGET) = gBattlerTarget; BYTE_TO_SEND(LINK_BUFF_SIZE_LO) = alignedSize; BYTE_TO_SEND(LINK_BUFF_SIZE_HI) = (alignedSize & 0x0000FF00) >> 8; BYTE_TO_SEND(LINK_BUFF_ABSENT_BATTLER_FLAGS) = gAbsentBattlerFlags; BYTE_TO_SEND(LINK_BUFF_EFFECT_BATTLER) = gEffectBattler; for (i = 0; i < size; i++) BYTE_TO_SEND(LINK_BUFF_DATA + i) = data[i]; #undef BYTE_TO_SEND gTasks[sLinkSendTaskId].tCurrentBlock_End = gTasks[sLinkSendTaskId].tCurrentBlock_End + alignedSize + LINK_BUFF_DATA; } enum { SENDTASK_STATE_INITIALIZE = 0, SENDTASK_STATE_INITIAL_DELAY = 1, SENDTASK_STATE_COUNT_PLAYERS = 2, SENDTASK_STATE_BEGIN_SEND_BLOCK = 3, SENDTASK_STATE_FINISH_SEND_BLOCK = 4, SENDTASK_STATE_UNUSED_STATE = 5, }; static void Task_HandleSendLinkBuffersData(u8 taskId) { u16 numPlayers; u16 blockSize; #define BYTE_TO_SEND(offset) \ gLinkBattleSendBuffer[gTasks[taskId].tCurrentBlock_Start + offset] switch (gTasks[taskId].tState) { case SENDTASK_STATE_INITIALIZE: gTasks[taskId].tInitialDelayTimer = 100; gTasks[taskId].tState++; break; case SENDTASK_STATE_INITIAL_DELAY: gTasks[taskId].tInitialDelayTimer--; if (gTasks[taskId].tInitialDelayTimer == 0) gTasks[taskId].tState++; break; case SENDTASK_STATE_COUNT_PLAYERS: if (gWirelessCommType) { gTasks[taskId].tState++; } 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].tState++; } else { gTasks[taskId].tState++; } } } break; case SENDTASK_STATE_BEGIN_SEND_BLOCK: if (gTasks[taskId].tCurrentBlock_Start != gTasks[taskId].tCurrentBlock_End) { if (gTasks[taskId].tBlockSendDelayTimer == 0) { if (gTasks[taskId].tCurrentBlock_Start > gTasks[taskId].tCurrentBlock_End && gTasks[taskId].tCurrentBlock_Start == gTasks[taskId].tCurrentBlock_WrapFrom) { gTasks[taskId].tCurrentBlock_WrapFrom = 0; gTasks[taskId].tCurrentBlock_Start = 0; } blockSize = (BYTE_TO_SEND(LINK_BUFF_SIZE_LO) | (BYTE_TO_SEND(LINK_BUFF_SIZE_HI) << 8)) + LINK_BUFF_DATA; SendBlock(BitmaskAllOtherLinkPlayers(), &BYTE_TO_SEND(0), blockSize); gTasks[taskId].tState++; } else { gTasks[taskId].tBlockSendDelayTimer--; break; } } break; case SENDTASK_STATE_FINISH_SEND_BLOCK: if (IsLinkTaskFinished()) { blockSize = BYTE_TO_SEND(LINK_BUFF_SIZE_LO) | (BYTE_TO_SEND(LINK_BUFF_SIZE_HI) << 8); gTasks[taskId].tBlockSendDelayTimer = 1; gTasks[taskId].tCurrentBlock_Start = gTasks[taskId].tCurrentBlock_Start + blockSize + LINK_BUFF_DATA; gTasks[taskId].tState = SENDTASK_STATE_BEGIN_SEND_BLOCK; } break; case SENDTASK_STATE_UNUSED_STATE: if (--gTasks[taskId].tBlockSendDelayTimer == 0) { gTasks[taskId].tBlockSendDelayTimer = 1; gTasks[taskId].tState = SENDTASK_STATE_BEGIN_SEND_BLOCK; } break; } #undef BYTE_TO_SEND } // We have received a message. Place it into the "receive" buffer. // // Counterintuitively, we also "receive" the outbound messages that // we send to other players. The GBA basically stores communicated // data for all four players, so inbound and outbound data can be // handled uniformly unless a game specifically decides to do // otherwise. Pokemon, evidently, did not specifically decide to do // otherwise. 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() & gBitTable[i]) { ResetBlockReceivedFlag(i); recvBuffer = (u8 *)gBlockRecvBuffer[i]; { u8 *dest, *src; u16 dataSize = gBlockRecvBuffer[i][2]; if (gTasks[sLinkReceiveTaskId].tCurrentBlock_End + 9 + dataSize > 0x1000) { gTasks[sLinkReceiveTaskId].tCurrentBlock_WrapFrom = gTasks[sLinkReceiveTaskId].tCurrentBlock_End; gTasks[sLinkReceiveTaskId].tCurrentBlock_End = 0; } dest = &gLinkBattleRecvBuffer[gTasks[sLinkReceiveTaskId].tCurrentBlock_End]; src = recvBuffer; for (j = 0; j < dataSize + 8; j++) dest[j] = src[j]; gTasks[sLinkReceiveTaskId].tCurrentBlock_End = gTasks[sLinkReceiveTaskId].tCurrentBlock_End + dataSize + 8; } } } } } static void Task_HandleCopyReceivedLinkBuffersData(u8 taskId) { u16 blockSize; u8 battler; u8 playerId; #define BYTE_TO_RECEIVE(offset) \ gLinkBattleRecvBuffer[gTasks[taskId].tCurrentBlock_Start + offset] if (gTasks[taskId].tCurrentBlock_Start != gTasks[taskId].tCurrentBlock_End) { if (gTasks[taskId].tCurrentBlock_Start > gTasks[taskId].tCurrentBlock_End && gTasks[taskId].tCurrentBlock_Start == gTasks[taskId].tCurrentBlock_WrapFrom) { gTasks[taskId].tCurrentBlock_WrapFrom = 0; gTasks[taskId].tCurrentBlock_Start = 0; } battler = BYTE_TO_RECEIVE(LINK_BUFF_ACTIVE_BATTLER); blockSize = BYTE_TO_RECEIVE(LINK_BUFF_SIZE_LO) | (BYTE_TO_RECEIVE(LINK_BUFF_SIZE_HI) << 8); switch (BYTE_TO_RECEIVE(0)) { case B_COMM_TO_CONTROLLER: if (IS_BATTLE_CONTROLLER_ACTIVE_ON_LOCAL(battler)) return; memcpy(gBattleBufferA[battler], &BYTE_TO_RECEIVE(LINK_BUFF_DATA), blockSize); MarkBattlerReceivedLinkData(battler); if (!(gBattleTypeFlags & BATTLE_TYPE_IS_MASTER)) { gBattlerAttacker = BYTE_TO_RECEIVE(LINK_BUFF_ATTACKER); gBattlerTarget = BYTE_TO_RECEIVE(LINK_BUFF_TARGET); gAbsentBattlerFlags = BYTE_TO_RECEIVE(LINK_BUFF_ABSENT_BATTLER_FLAGS); gEffectBattler = BYTE_TO_RECEIVE(LINK_BUFF_EFFECT_BATTLER); } break; case B_COMM_TO_ENGINE: memcpy(gBattleBufferB[battler], &gLinkBattleRecvBuffer[gTasks[taskId].tCurrentBlock_Start + LINK_BUFF_DATA], blockSize); break; case B_COMM_CONTROLLER_IS_DONE: playerId = BYTE_TO_RECEIVE(LINK_BUFF_DATA); MARK_BATTLE_CONTROLLER_IDLE_FOR_PLAYER(battler, playerId); break; } gTasks[taskId].tCurrentBlock_Start = gTasks[taskId].tCurrentBlock_Start + blockSize + LINK_BUFF_DATA; } #undef BYTE_TO_RECEIVE } #undef tInitialDelayTimer #undef tState #undef tCurrentBlock_WrapFrom #undef tBlockSendDelayTimer #undef tCurrentBlock_End #undef tCurrentBlock_Start void BtlController_EmitGetMonData(u8 bufferId, u8 requestId, u8 monToCheck) { sBattleBuffersTransferData[0] = CONTROLLER_GETMONDATA; sBattleBuffersTransferData[1] = requestId; sBattleBuffersTransferData[2] = monToCheck; sBattleBuffersTransferData[3] = 0; PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); } static void UNUSED BtlController_EmitGetRawMonData(u8 bufferId, u8 monId, u8 bytes) { sBattleBuffersTransferData[0] = CONTROLLER_GETRAWMONDATA; sBattleBuffersTransferData[1] = monId; sBattleBuffersTransferData[2] = bytes; sBattleBuffersTransferData[3] = 0; PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); } void BtlController_EmitSetMonData(u8 bufferId, u8 requestId, u8 monToCheck, u8 bytes, void *data) { s32 i; sBattleBuffersTransferData[0] = CONTROLLER_SETMONDATA; sBattleBuffersTransferData[1] = requestId; sBattleBuffersTransferData[2] = monToCheck; for (i = 0; i < bytes; i++) sBattleBuffersTransferData[3 + i] = *(u8 *)(data++); PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 3 + bytes); } static void UNUSED BtlController_EmitSetRawMonData(u8 bufferId, u8 monId, u8 bytes, void *data) { s32 i; sBattleBuffersTransferData[0] = CONTROLLER_SETRAWMONDATA; sBattleBuffersTransferData[1] = monId; sBattleBuffersTransferData[2] = bytes; for (i = 0; i < bytes; i++) sBattleBuffersTransferData[3 + i] = *(u8 *)(data++); PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, bytes + 3); } void BtlController_EmitLoadMonSprite(u8 bufferId) { sBattleBuffersTransferData[0] = CONTROLLER_LOADMONSPRITE; sBattleBuffersTransferData[1] = CONTROLLER_LOADMONSPRITE; sBattleBuffersTransferData[2] = CONTROLLER_LOADMONSPRITE; sBattleBuffersTransferData[3] = CONTROLLER_LOADMONSPRITE; PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); } void BtlController_EmitSwitchInAnim(u8 bufferId, u8 partyId, bool8 dontClearSubstituteBit) { sBattleBuffersTransferData[0] = CONTROLLER_SWITCHINANIM; sBattleBuffersTransferData[1] = partyId; sBattleBuffersTransferData[2] = dontClearSubstituteBit; sBattleBuffersTransferData[3] = 5; PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); } void BtlController_EmitReturnMonToBall(u8 bufferId, bool8 skipAnim) { sBattleBuffersTransferData[0] = CONTROLLER_RETURNMONTOBALL; sBattleBuffersTransferData[1] = skipAnim; PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 2); } void BtlController_EmitDrawTrainerPic(u8 bufferId) { sBattleBuffersTransferData[0] = CONTROLLER_DRAWTRAINERPIC; sBattleBuffersTransferData[1] = CONTROLLER_DRAWTRAINERPIC; sBattleBuffersTransferData[2] = CONTROLLER_DRAWTRAINERPIC; sBattleBuffersTransferData[3] = CONTROLLER_DRAWTRAINERPIC; PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); } void BtlController_EmitTrainerSlide(u8 bufferId) { sBattleBuffersTransferData[0] = CONTROLLER_TRAINERSLIDE; sBattleBuffersTransferData[1] = CONTROLLER_TRAINERSLIDE; sBattleBuffersTransferData[2] = CONTROLLER_TRAINERSLIDE; sBattleBuffersTransferData[3] = CONTROLLER_TRAINERSLIDE; PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); } void BtlController_EmitTrainerSlideBack(u8 bufferId) { sBattleBuffersTransferData[0] = CONTROLLER_TRAINERSLIDEBACK; sBattleBuffersTransferData[1] = CONTROLLER_TRAINERSLIDEBACK; sBattleBuffersTransferData[2] = CONTROLLER_TRAINERSLIDEBACK; sBattleBuffersTransferData[3] = CONTROLLER_TRAINERSLIDEBACK; PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); } void BtlController_EmitFaintAnimation(u8 bufferId) { sBattleBuffersTransferData[0] = CONTROLLER_FAINTANIMATION; sBattleBuffersTransferData[1] = CONTROLLER_FAINTANIMATION; sBattleBuffersTransferData[2] = CONTROLLER_FAINTANIMATION; sBattleBuffersTransferData[3] = CONTROLLER_FAINTANIMATION; PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); } static void UNUSED BtlController_EmitPaletteFade(u8 bufferId) { sBattleBuffersTransferData[0] = CONTROLLER_PALETTEFADE; sBattleBuffersTransferData[1] = CONTROLLER_PALETTEFADE; sBattleBuffersTransferData[2] = CONTROLLER_PALETTEFADE; sBattleBuffersTransferData[3] = CONTROLLER_PALETTEFADE; PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); } static void UNUSED BtlController_EmitSuccessBallThrowAnim(u8 bufferId) { sBattleBuffersTransferData[0] = CONTROLLER_SUCCESSBALLTHROWANIM; sBattleBuffersTransferData[1] = CONTROLLER_SUCCESSBALLTHROWANIM; sBattleBuffersTransferData[2] = CONTROLLER_SUCCESSBALLTHROWANIM; sBattleBuffersTransferData[3] = CONTROLLER_SUCCESSBALLTHROWANIM; PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); } void BtlController_EmitBallThrowAnim(u8 bufferId, u8 caseId) { sBattleBuffersTransferData[0] = CONTROLLER_BALLTHROWANIM; sBattleBuffersTransferData[1] = caseId; PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 2); } static void UNUSED BtlController_EmitPause(u8 bufferId, u8 toWait, void *data) { s32 i; sBattleBuffersTransferData[0] = CONTROLLER_PAUSE; sBattleBuffersTransferData[1] = toWait; for (i = 0; i < toWait * 3; i++) sBattleBuffersTransferData[2 + i] = *(u8 *)(data++); PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, toWait * 3 + 2); } void BtlController_EmitMoveAnimation(u8 bufferId, u16 move, u8 turnOfMove, u16 movePower, s32 dmg, u8 friendship, struct DisableStruct *disableStructPtr, u8 multihit) { sBattleBuffersTransferData[0] = CONTROLLER_MOVEANIMATION; sBattleBuffersTransferData[1] = move; sBattleBuffersTransferData[2] = (move & 0xFF00) >> 8; sBattleBuffersTransferData[3] = turnOfMove; sBattleBuffersTransferData[4] = movePower; sBattleBuffersTransferData[5] = (movePower & 0xFF00) >> 8; sBattleBuffersTransferData[6] = dmg; sBattleBuffersTransferData[7] = (dmg & 0x0000FF00) >> 8; sBattleBuffersTransferData[8] = (dmg & 0x00FF0000) >> 16; sBattleBuffersTransferData[9] = (dmg & 0xFF000000) >> 24; sBattleBuffersTransferData[10] = friendship; sBattleBuffersTransferData[11] = multihit; if (WEATHER_HAS_EFFECT2) { sBattleBuffersTransferData[12] = gBattleWeather; sBattleBuffersTransferData[13] = (gBattleWeather & 0xFF00) >> 8; } else { sBattleBuffersTransferData[12] = 0; sBattleBuffersTransferData[13] = 0; } sBattleBuffersTransferData[14] = 0; sBattleBuffersTransferData[15] = 0; memcpy(&sBattleBuffersTransferData[16], disableStructPtr, sizeof(struct DisableStruct)); PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 16 + sizeof(struct DisableStruct)); } void BtlController_EmitPrintString(u8 bufferId, u16 stringID) { s32 i; struct BattleMsgData *stringInfo; sBattleBuffersTransferData[0] = CONTROLLER_PRINTSTRING; sBattleBuffersTransferData[1] = gBattleOutcome; sBattleBuffersTransferData[2] = stringID; sBattleBuffersTransferData[3] = (stringID & 0xFF00) >> 8; stringInfo = (struct BattleMsgData *)(&sBattleBuffersTransferData[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 = gBattleMoves[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(bufferId, sBattleBuffersTransferData, sizeof(struct BattleMsgData) + 4); } void BtlController_EmitPrintSelectionString(u8 bufferId, u16 stringID) { s32 i; struct BattleMsgData *stringInfo; sBattleBuffersTransferData[0] = CONTROLLER_PRINTSTRINGPLAYERONLY; sBattleBuffersTransferData[1] = CONTROLLER_PRINTSTRINGPLAYERONLY; sBattleBuffersTransferData[2] = stringID; sBattleBuffersTransferData[3] = (stringID & 0xFF00) >> 8; stringInfo = (struct BattleMsgData *)(&sBattleBuffersTransferData[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(bufferId, sBattleBuffersTransferData, sizeof(struct BattleMsgData) + 4); } // itemId only relevant for B_ACTION_USE_ITEM void BtlController_EmitChooseAction(u8 bufferId, u8 action, u16 itemId) { sBattleBuffersTransferData[0] = CONTROLLER_CHOOSEACTION; sBattleBuffersTransferData[1] = action; sBattleBuffersTransferData[2] = itemId; sBattleBuffersTransferData[3] = (itemId & 0xFF00) >> 8; PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); } // Only used by the forfeit prompt in the Battle Frontier // For other Yes/No boxes in battle, see Cmd_yesnobox void BtlController_EmitYesNoBox(u8 bufferId) { sBattleBuffersTransferData[0] = CONTROLLER_YESNOBOX; sBattleBuffersTransferData[1] = CONTROLLER_YESNOBOX; sBattleBuffersTransferData[2] = CONTROLLER_YESNOBOX; sBattleBuffersTransferData[3] = CONTROLLER_YESNOBOX; PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); } void BtlController_EmitChooseMove(u8 bufferId, bool8 isDoubleBattle, bool8 NoPpNumber, struct ChooseMoveStruct *movePpData) { s32 i; sBattleBuffersTransferData[0] = CONTROLLER_CHOOSEMOVE; sBattleBuffersTransferData[1] = isDoubleBattle; sBattleBuffersTransferData[2] = NoPpNumber; sBattleBuffersTransferData[3] = 0; for (i = 0; i < sizeof(*movePpData); i++) sBattleBuffersTransferData[4 + i] = *((u8 *)(movePpData) + i); PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, sizeof(*movePpData) + 4); } void BtlController_EmitChooseItem(u8 bufferId, u8 *battlePartyOrder) { s32 i; sBattleBuffersTransferData[0] = CONTROLLER_OPENBAG; for (i = 0; i < PARTY_SIZE / 2; i++) sBattleBuffersTransferData[1 + i] = battlePartyOrder[i]; PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); } void BtlController_EmitChoosePokemon(u8 bufferId, u8 caseId, u8 slotId, u8 abilityId, u8 *data) { s32 i; sBattleBuffersTransferData[0] = CONTROLLER_CHOOSEPOKEMON; sBattleBuffersTransferData[1] = caseId; sBattleBuffersTransferData[2] = slotId; sBattleBuffersTransferData[3] = abilityId; for (i = 0; i < 3; i++) sBattleBuffersTransferData[4 + i] = data[i]; PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 8); // Only 7 bytes were written. } static void UNUSED BtlController_EmitCmd23(u8 bufferId) { sBattleBuffersTransferData[0] = CONTROLLER_23; sBattleBuffersTransferData[1] = CONTROLLER_23; sBattleBuffersTransferData[2] = CONTROLLER_23; sBattleBuffersTransferData[3] = CONTROLLER_23; PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); } // why is the argument u16 if it's being cast to s16 anyway? void BtlController_EmitHealthBarUpdate(u8 bufferId, u16 hpValue) { sBattleBuffersTransferData[0] = CONTROLLER_HEALTHBARUPDATE; sBattleBuffersTransferData[1] = 0; sBattleBuffersTransferData[2] = (s16)hpValue; sBattleBuffersTransferData[3] = ((s16)hpValue & 0xFF00) >> 8; PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); } // why is the argument u16 if it's being cast to s16 anyway? void BtlController_EmitExpUpdate(u8 bufferId, u8 partyId, u16 expPoints) { sBattleBuffersTransferData[0] = CONTROLLER_EXPUPDATE; sBattleBuffersTransferData[1] = partyId; sBattleBuffersTransferData[2] = (s16)expPoints; sBattleBuffersTransferData[3] = ((s16)expPoints & 0xFF00) >> 8; PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); } void BtlController_EmitStatusIconUpdate(u8 bufferId, u32 status1, u32 status2) { sBattleBuffersTransferData[0] = CONTROLLER_STATUSICONUPDATE; sBattleBuffersTransferData[1] = status1; sBattleBuffersTransferData[2] = (status1 & 0x0000FF00) >> 8; sBattleBuffersTransferData[3] = (status1 & 0x00FF0000) >> 16; sBattleBuffersTransferData[4] = (status1 & 0xFF000000) >> 24; sBattleBuffersTransferData[5] = status2; sBattleBuffersTransferData[6] = (status2 & 0x0000FF00) >> 8; sBattleBuffersTransferData[7] = (status2 & 0x00FF0000) >> 16; sBattleBuffersTransferData[8] = (status2 & 0xFF000000) >> 24; PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 9); } void BtlController_EmitStatusAnimation(u8 bufferId, bool8 status2, u32 status) { sBattleBuffersTransferData[0] = CONTROLLER_STATUSANIMATION; sBattleBuffersTransferData[1] = status2; sBattleBuffersTransferData[2] = status; sBattleBuffersTransferData[3] = (status & 0x0000FF00) >> 8; sBattleBuffersTransferData[4] = (status & 0x00FF0000) >> 16; sBattleBuffersTransferData[5] = (status & 0xFF000000) >> 24; PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 6); } static void UNUSED BtlController_EmitStatusXor(u8 bufferId, u8 b) { sBattleBuffersTransferData[0] = CONTROLLER_STATUSXOR; sBattleBuffersTransferData[1] = b; PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 2); } void BtlController_EmitDataTransfer(u8 bufferId, u16 size, void *data) { s32 i; sBattleBuffersTransferData[0] = CONTROLLER_DATATRANSFER; sBattleBuffersTransferData[1] = CONTROLLER_DATATRANSFER; sBattleBuffersTransferData[2] = size; sBattleBuffersTransferData[3] = (size & 0xFF00) >> 8; for (i = 0; i < size; i++) sBattleBuffersTransferData[4 + i] = *(u8 *)(data++); PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, size + 4); } static void UNUSED BtlController_EmitDMA3Transfer(u8 bufferId, void *dst, u16 size, void *data) { s32 i; sBattleBuffersTransferData[0] = CONTROLLER_DMA3TRANSFER; sBattleBuffersTransferData[1] = (u32)(dst); sBattleBuffersTransferData[2] = ((u32)(dst) & 0x0000FF00) >> 8; sBattleBuffersTransferData[3] = ((u32)(dst) & 0x00FF0000) >> 16; sBattleBuffersTransferData[4] = ((u32)(dst) & 0xFF000000) >> 24; sBattleBuffersTransferData[5] = size; sBattleBuffersTransferData[6] = (size & 0xFF00) >> 8; for (i = 0; i < size; i++) sBattleBuffersTransferData[7 + i] = *(u8 *)(data++); PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, size + 7); } static void UNUSED BtlController_EmitPlayBGM(u8 bufferId, u16 songId, void *data) { s32 i; sBattleBuffersTransferData[0] = CONTROLLER_PLAYBGM; sBattleBuffersTransferData[1] = songId; sBattleBuffersTransferData[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++) sBattleBuffersTransferData[3 + i] = *(u8 *)(data++); PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, songId + 3); } static void UNUSED BtlController_EmitCmd32(u8 bufferId, u16 size, void *data) { s32 i; sBattleBuffersTransferData[0] = CONTROLLER_32; sBattleBuffersTransferData[1] = size; sBattleBuffersTransferData[2] = (size & 0xFF00) >> 8; for (i = 0; i < size; i++) sBattleBuffersTransferData[3 + i] = *(u8 *)(data++); PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, size + 3); } void BtlController_EmitTwoReturnValues(u8 bufferId, u8 ret8, u16 ret16) { sBattleBuffersTransferData[0] = CONTROLLER_TWORETURNVALUES; sBattleBuffersTransferData[1] = ret8; sBattleBuffersTransferData[2] = ret16; sBattleBuffersTransferData[3] = (ret16 & 0xFF00) >> 8; PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); } void BtlController_EmitChosenMonReturnValue(u8 bufferId, u8 partyId, u8 *battlePartyOrder) { s32 i; sBattleBuffersTransferData[0] = CONTROLLER_CHOSENMONRETURNVALUE; sBattleBuffersTransferData[1] = partyId; for (i = 0; i < (int)ARRAY_COUNT(gBattlePartyCurrentOrder); i++) sBattleBuffersTransferData[2 + i] = battlePartyOrder[i]; PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 5); } void BtlController_EmitOneReturnValue(u8 bufferId, u16 ret) { sBattleBuffersTransferData[0] = CONTROLLER_ONERETURNVALUE; sBattleBuffersTransferData[1] = ret; sBattleBuffersTransferData[2] = (ret & 0xFF00) >> 8; sBattleBuffersTransferData[3] = 0; PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); } void BtlController_EmitOneReturnValue_Duplicate(u8 bufferId, u16 ret) { sBattleBuffersTransferData[0] = CONTROLLER_ONERETURNVALUE_DUPLICATE; sBattleBuffersTransferData[1] = ret; sBattleBuffersTransferData[2] = (ret & 0xFF00) >> 8; sBattleBuffersTransferData[3] = 0; PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); } static void UNUSED BtlController_EmitClearUnkVar(u8 bufferId) { sBattleBuffersTransferData[0] = CONTROLLER_CLEARUNKVAR; sBattleBuffersTransferData[1] = CONTROLLER_CLEARUNKVAR; sBattleBuffersTransferData[2] = CONTROLLER_CLEARUNKVAR; sBattleBuffersTransferData[3] = CONTROLLER_CLEARUNKVAR; PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); } static void UNUSED BtlController_EmitSetUnkVar(u8 bufferId, u8 b) { sBattleBuffersTransferData[0] = CONTROLLER_SETUNKVAR; sBattleBuffersTransferData[1] = b; PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 2); } static void UNUSED BtlController_EmitClearUnkFlag(u8 bufferId) { sBattleBuffersTransferData[0] = CONTROLLER_CLEARUNKFLAG; sBattleBuffersTransferData[1] = CONTROLLER_CLEARUNKFLAG; sBattleBuffersTransferData[2] = CONTROLLER_CLEARUNKFLAG; sBattleBuffersTransferData[3] = CONTROLLER_CLEARUNKFLAG; PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); } static void UNUSED BtlController_EmitToggleUnkFlag(u8 bufferId) { sBattleBuffersTransferData[0] = CONTROLLER_TOGGLEUNKFLAG; sBattleBuffersTransferData[1] = CONTROLLER_TOGGLEUNKFLAG; sBattleBuffersTransferData[2] = CONTROLLER_TOGGLEUNKFLAG; sBattleBuffersTransferData[3] = CONTROLLER_TOGGLEUNKFLAG; PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); } void BtlController_EmitHitAnimation(u8 bufferId) { sBattleBuffersTransferData[0] = CONTROLLER_HITANIMATION; sBattleBuffersTransferData[1] = CONTROLLER_HITANIMATION; sBattleBuffersTransferData[2] = CONTROLLER_HITANIMATION; sBattleBuffersTransferData[3] = CONTROLLER_HITANIMATION; PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); } void BtlController_EmitCantSwitch(u8 bufferId) { sBattleBuffersTransferData[0] = CONTROLLER_CANTSWITCH; sBattleBuffersTransferData[1] = CONTROLLER_CANTSWITCH; sBattleBuffersTransferData[2] = CONTROLLER_CANTSWITCH; sBattleBuffersTransferData[3] = CONTROLLER_CANTSWITCH; PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); } void BtlController_EmitPlaySE(u8 bufferId, u16 songId) { sBattleBuffersTransferData[0] = CONTROLLER_PLAYSE; sBattleBuffersTransferData[1] = songId; sBattleBuffersTransferData[2] = (songId & 0xFF00) >> 8; sBattleBuffersTransferData[3] = 0; PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); } void BtlController_EmitPlayFanfareOrBGM(u8 bufferId, u16 songId, bool8 playBGM) { sBattleBuffersTransferData[0] = CONTROLLER_PLAYFANFAREORBGM; sBattleBuffersTransferData[1] = songId; sBattleBuffersTransferData[2] = (songId & 0xFF00) >> 8; sBattleBuffersTransferData[3] = playBGM; PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); } void BtlController_EmitFaintingCry(u8 bufferId) { sBattleBuffersTransferData[0] = CONTROLLER_FAINTINGCRY; sBattleBuffersTransferData[1] = CONTROLLER_FAINTINGCRY; sBattleBuffersTransferData[2] = CONTROLLER_FAINTINGCRY; sBattleBuffersTransferData[3] = CONTROLLER_FAINTINGCRY; PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); } void BtlController_EmitIntroSlide(u8 bufferId, u8 environmentId) { sBattleBuffersTransferData[0] = CONTROLLER_INTROSLIDE; sBattleBuffersTransferData[1] = environmentId; PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 2); } void BtlController_EmitIntroTrainerBallThrow(u8 bufferId) { sBattleBuffersTransferData[0] = CONTROLLER_INTROTRAINERBALLTHROW; sBattleBuffersTransferData[1] = CONTROLLER_INTROTRAINERBALLTHROW; sBattleBuffersTransferData[2] = CONTROLLER_INTROTRAINERBALLTHROW; sBattleBuffersTransferData[3] = CONTROLLER_INTROTRAINERBALLTHROW; PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); } void BtlController_EmitDrawPartyStatusSummary(u8 bufferId, struct HpAndStatus* hpAndStatus, u8 flags) { s32 i; sBattleBuffersTransferData[0] = CONTROLLER_DRAWPARTYSTATUSSUMMARY; sBattleBuffersTransferData[1] = flags & ~PARTY_SUMM_SKIP_DRAW_DELAY; // If true, skip player side sBattleBuffersTransferData[2] = (flags & PARTY_SUMM_SKIP_DRAW_DELAY) >> 7; // If true, skip delay after drawing. True during intro sBattleBuffersTransferData[3] = CONTROLLER_DRAWPARTYSTATUSSUMMARY; for (i = 0; i < (s32)(sizeof(struct HpAndStatus) * PARTY_SIZE); i++) sBattleBuffersTransferData[4 + i] = *(i + (u8 *)(hpAndStatus)); PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, sizeof(struct HpAndStatus) * PARTY_SIZE + 4); } void BtlController_EmitHidePartyStatusSummary(u8 bufferId) { sBattleBuffersTransferData[0] = CONTROLLER_HIDEPARTYSTATUSSUMMARY; sBattleBuffersTransferData[1] = CONTROLLER_HIDEPARTYSTATUSSUMMARY; sBattleBuffersTransferData[2] = CONTROLLER_HIDEPARTYSTATUSSUMMARY; sBattleBuffersTransferData[3] = CONTROLLER_HIDEPARTYSTATUSSUMMARY; PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); } void BtlController_EmitEndBounceEffect(u8 bufferId) { sBattleBuffersTransferData[0] = CONTROLLER_ENDBOUNCE; sBattleBuffersTransferData[1] = CONTROLLER_ENDBOUNCE; sBattleBuffersTransferData[2] = CONTROLLER_ENDBOUNCE; sBattleBuffersTransferData[3] = CONTROLLER_ENDBOUNCE; PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); } void BtlController_EmitSpriteInvisibility(u8 bufferId, bool8 isInvisible) { sBattleBuffersTransferData[0] = CONTROLLER_SPRITEINVISIBILITY; sBattleBuffersTransferData[1] = isInvisible; sBattleBuffersTransferData[2] = CONTROLLER_SPRITEINVISIBILITY; sBattleBuffersTransferData[3] = CONTROLLER_SPRITEINVISIBILITY; PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); } void BtlController_EmitBattleAnimation(u8 bufferId, u8 animationId, u16 argument) { sBattleBuffersTransferData[0] = CONTROLLER_BATTLEANIMATION; sBattleBuffersTransferData[1] = animationId; sBattleBuffersTransferData[2] = argument; sBattleBuffersTransferData[3] = (argument & 0xFF00) >> 8; PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); } // mode is a LINK_STANDBY_* constant void BtlController_EmitLinkStandbyMsg(u8 bufferId, u8 mode, bool32 record) { bool8 record_ = record; sBattleBuffersTransferData[0] = CONTROLLER_LINKSTANDBYMSG; sBattleBuffersTransferData[1] = mode; if (record_) sBattleBuffersTransferData[3] = sBattleBuffersTransferData[2] = RecordedBattle_BufferNewBattlerData(&sBattleBuffersTransferData[4]); else sBattleBuffersTransferData[3] = sBattleBuffersTransferData[2] = 0; PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, sBattleBuffersTransferData[2] + 4); } void BtlController_EmitResetActionMoveSelection(u8 bufferId, u8 caseId) { sBattleBuffersTransferData[0] = CONTROLLER_RESETACTIONMOVESELECTION; sBattleBuffersTransferData[1] = caseId; PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 2); } void BtlController_EmitEndLinkBattle(u8 bufferId, u8 battleOutcome) { sBattleBuffersTransferData[0] = CONTROLLER_ENDLINKBATTLE; sBattleBuffersTransferData[1] = battleOutcome; sBattleBuffersTransferData[2] = gSaveBlock2Ptr->frontier.disableRecordBattle; sBattleBuffersTransferData[3] = gSaveBlock2Ptr->frontier.disableRecordBattle; sBattleBuffersTransferData[5] = sBattleBuffersTransferData[4] = RecordedBattle_BufferNewBattlerData(&sBattleBuffersTransferData[6]); PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, sBattleBuffersTransferData[4] + 6); }