pokeemmo/src/battle_controller_player.c
DavidJCobb b4ee45f3e8 Renamed BATTLELINKMSGTYPE_ to BATTLELINKCOMMTYPE_
I want to avoid the word "message" since PRET has already chosen "battle message" as the term for predefined strings usable in the battle scripts and UI.

I was considering the term "battle link packet" instead, but the library for wireless communications already uses the word "packet" even though GBATEK does not. I'm going with "battle link communication type" instead. For this particular context, I don't think we need a compound noun ("X type"); the singular noun "type" is fine enough.
2025-04-26 11:37:20 -04:00

3148 lines
119 KiB
C

#include "global.h"
#include "battle.h"
#include "battle_anim.h"
#include "battle_arena.h"
#include "battle_controllers.h"
#include "battle_dome.h"
#include "battle_interface.h"
#include "battle_message.h"
#include "battle_setup.h"
#include "battle_tv.h"
#include "bg.h"
#include "data.h"
#include "item.h"
#include "item_menu.h"
#include "link.h"
#include "main.h"
#include "m4a.h"
#include "palette.h"
#include "party_menu.h"
#include "pokeball.h"
#include "pokemon.h"
#include "random.h"
#include "recorded_battle.h"
#include "reshow_battle_screen.h"
#include "sound.h"
#include "string_util.h"
#include "task.h"
#include "text.h"
#include "util.h"
#include "window.h"
#include "constants/battle_anim.h"
#include "constants/items.h"
#include "constants/moves.h"
#include "constants/party_menu.h"
#include "constants/songs.h"
#include "constants/trainers.h"
#include "constants/rgb.h"
static void PlayerHandleGetMonData(void);
static void PlayerHandleSetMonData(void);
static void PlayerHandleSetRawMonData(void);
static void PlayerHandleLoadMonSprite(void);
static void PlayerHandleSwitchInAnim(void);
static void PlayerHandleReturnMonToBall(void);
static void PlayerHandleDrawTrainerPic(void);
static void PlayerHandleTrainerSlide(void);
static void PlayerHandleTrainerSlideBack(void);
static void PlayerHandleFaintAnimation(void);
static void PlayerHandlePaletteFade(void);
static void PlayerHandleSuccessBallThrowAnim(void);
static void PlayerHandleBallThrowAnim(void);
static void PlayerHandlePause(void);
static void PlayerHandleMoveAnimation(void);
static void PlayerHandlePrintString(void);
static void PlayerHandlePrintSelectionString(void);
static void PlayerHandleChooseAction(void);
static void PlayerHandleYesNoBox(void);
static void PlayerHandleChooseMove(void);
static void PlayerHandleChooseItem(void);
static void PlayerHandleChoosePokemon(void);
static void PlayerHandleCmd23(void);
static void PlayerHandleHealthBarUpdate(void);
static void PlayerHandleExpUpdate(void);
static void PlayerHandleStatusIconUpdate(void);
static void PlayerHandleStatusAnimation(void);
static void PlayerHandleStatusXor(void);
static void PlayerHandleDataTransfer(void);
static void PlayerHandleDMA3Transfer(void);
static void PlayerHandlePlayBGM(void);
static void PlayerHandleCmd32(void);
static void PlayerHandleTwoReturnValues(void);
static void PlayerHandleChosenMonReturnValue(void);
static void PlayerHandleOneReturnValue(void);
static void PlayerHandleOneReturnValue_Duplicate(void);
static void PlayerHandleClearUnkVar(void);
static void PlayerHandleSetUnkVar(void);
static void PlayerHandleClearUnkFlag(void);
static void PlayerHandleToggleUnkFlag(void);
static void PlayerHandleHitAnimation(void);
static void PlayerHandleCantSwitch(void);
static void PlayerHandlePlaySE(void);
static void PlayerHandlePlayFanfareOrBGM(void);
static void PlayerHandleFaintingCry(void);
static void PlayerHandleIntroSlide(void);
static void PlayerHandleIntroTrainerBallThrow(void);
static void PlayerHandleDrawPartyStatusSummary(void);
static void PlayerHandleHidePartyStatusSummary(void);
static void PlayerHandleEndBounceEffect(void);
static void PlayerHandleSpriteInvisibility(void);
static void PlayerHandleBattleAnimation(void);
static void PlayerHandleLinkStandbyMsg(void);
static void PlayerHandleResetActionMoveSelection(void);
static void PlayerHandleEndLinkBattle(void);
static void PlayerCmdEnd(void);
static void PlayerBufferRunCommand(void);
static void HandleInputChooseTarget(void);
static void HandleInputChooseMove(void);
static void MoveSelectionCreateCursorAt(u8, u8);
static void MoveSelectionDestroyCursorAt(u8);
static void MoveSelectionDisplayPpNumber(void);
static void MoveSelectionDisplayPpString(void);
static void MoveSelectionDisplayMoveType(void);
static void MoveSelectionDisplayMoveNames(void);
static void HandleMoveSwitching(void);
static void SwitchIn_HandleSoundAndEnd(void);
static void WaitForMonSelection(void);
static void CompleteWhenChoseItem(void);
static void Task_LaunchLvlUpAnim(u8);
static void Task_PrepareToGiveExpWithExpBar(u8);
static void DestroyExpTaskAndCompleteOnInactiveTextPrinter(u8);
static void Task_GiveExpWithExpBar(u8);
static void Task_UpdateLvlInHealthbox(u8);
static void PrintLinkStandbyMsg(void);
static u32 CopyPlayerMonData(u8, u8 *);
static void SetPlayerMonData(u8);
static void StartSendOutAnim(u8, bool8);
static void DoSwitchOutAnimation(void);
static void PlayerDoMoveAnimation(void);
static void Task_StartSendOutAnim(u8);
static void EndDrawPartyStatusSummary(void);
static void (*const sPlayerBufferCommands[CONTROLLER_CMDS_COUNT])(void) =
{
[CONTROLLER_GETMONDATA] = PlayerHandleGetMonData,
[CONTROLLER_GETRAWMONDATA] = PlayerHandleGetRawMonData,
[CONTROLLER_SETMONDATA] = PlayerHandleSetMonData,
[CONTROLLER_SETRAWMONDATA] = PlayerHandleSetRawMonData,
[CONTROLLER_LOADMONSPRITE] = PlayerHandleLoadMonSprite,
[CONTROLLER_SWITCHINANIM] = PlayerHandleSwitchInAnim,
[CONTROLLER_RETURNMONTOBALL] = PlayerHandleReturnMonToBall,
[CONTROLLER_DRAWTRAINERPIC] = PlayerHandleDrawTrainerPic,
[CONTROLLER_TRAINERSLIDE] = PlayerHandleTrainerSlide,
[CONTROLLER_TRAINERSLIDEBACK] = PlayerHandleTrainerSlideBack,
[CONTROLLER_FAINTANIMATION] = PlayerHandleFaintAnimation,
[CONTROLLER_PALETTEFADE] = PlayerHandlePaletteFade,
[CONTROLLER_SUCCESSBALLTHROWANIM] = PlayerHandleSuccessBallThrowAnim,
[CONTROLLER_BALLTHROWANIM] = PlayerHandleBallThrowAnim,
[CONTROLLER_PAUSE] = PlayerHandlePause,
[CONTROLLER_MOVEANIMATION] = PlayerHandleMoveAnimation,
[CONTROLLER_PRINTSTRING] = PlayerHandlePrintString,
[CONTROLLER_PRINTSTRINGPLAYERONLY] = PlayerHandlePrintSelectionString,
[CONTROLLER_CHOOSEACTION] = PlayerHandleChooseAction,
[CONTROLLER_YESNOBOX] = PlayerHandleYesNoBox,
[CONTROLLER_CHOOSEMOVE] = PlayerHandleChooseMove,
[CONTROLLER_OPENBAG] = PlayerHandleChooseItem,
[CONTROLLER_CHOOSEPOKEMON] = PlayerHandleChoosePokemon,
[CONTROLLER_23] = PlayerHandleCmd23,
[CONTROLLER_HEALTHBARUPDATE] = PlayerHandleHealthBarUpdate,
[CONTROLLER_EXPUPDATE] = PlayerHandleExpUpdate,
[CONTROLLER_STATUSICONUPDATE] = PlayerHandleStatusIconUpdate,
[CONTROLLER_STATUSANIMATION] = PlayerHandleStatusAnimation,
[CONTROLLER_STATUSXOR] = PlayerHandleStatusXor,
[CONTROLLER_DATATRANSFER] = PlayerHandleDataTransfer,
[CONTROLLER_DMA3TRANSFER] = PlayerHandleDMA3Transfer,
[CONTROLLER_PLAYBGM] = PlayerHandlePlayBGM,
[CONTROLLER_32] = PlayerHandleCmd32,
[CONTROLLER_TWORETURNVALUES] = PlayerHandleTwoReturnValues,
[CONTROLLER_CHOSENMONRETURNVALUE] = PlayerHandleChosenMonReturnValue,
[CONTROLLER_ONERETURNVALUE] = PlayerHandleOneReturnValue,
[CONTROLLER_ONERETURNVALUE_DUPLICATE] = PlayerHandleOneReturnValue_Duplicate,
[CONTROLLER_CLEARUNKVAR] = PlayerHandleClearUnkVar,
[CONTROLLER_SETUNKVAR] = PlayerHandleSetUnkVar,
[CONTROLLER_CLEARUNKFLAG] = PlayerHandleClearUnkFlag,
[CONTROLLER_TOGGLEUNKFLAG] = PlayerHandleToggleUnkFlag,
[CONTROLLER_HITANIMATION] = PlayerHandleHitAnimation,
[CONTROLLER_CANTSWITCH] = PlayerHandleCantSwitch,
[CONTROLLER_PLAYSE] = PlayerHandlePlaySE,
[CONTROLLER_PLAYFANFAREORBGM] = PlayerHandlePlayFanfareOrBGM,
[CONTROLLER_FAINTINGCRY] = PlayerHandleFaintingCry,
[CONTROLLER_INTROSLIDE] = PlayerHandleIntroSlide,
[CONTROLLER_INTROTRAINERBALLTHROW] = PlayerHandleIntroTrainerBallThrow,
[CONTROLLER_DRAWPARTYSTATUSSUMMARY] = PlayerHandleDrawPartyStatusSummary,
[CONTROLLER_HIDEPARTYSTATUSSUMMARY] = PlayerHandleHidePartyStatusSummary,
[CONTROLLER_ENDBOUNCE] = PlayerHandleEndBounceEffect,
[CONTROLLER_SPRITEINVISIBILITY] = PlayerHandleSpriteInvisibility,
[CONTROLLER_BATTLEANIMATION] = PlayerHandleBattleAnimation,
[CONTROLLER_LINKSTANDBYMSG] = PlayerHandleLinkStandbyMsg,
[CONTROLLER_RESETACTIONMOVESELECTION] = PlayerHandleResetActionMoveSelection,
[CONTROLLER_ENDLINKBATTLE] = PlayerHandleEndLinkBattle,
[CONTROLLER_TERMINATOR_NOP] = PlayerCmdEnd
};
static const u8 sTargetIdentities[MAX_BATTLERS_COUNT] = {B_POSITION_PLAYER_LEFT, B_POSITION_PLAYER_RIGHT, B_POSITION_OPPONENT_RIGHT, B_POSITION_OPPONENT_LEFT};
// unknown unused data
static const u8 sUnused[] = {0x48, 0x48, 0x20, 0x5a, 0x50, 0x50, 0x50, 0x58};
void BattleControllerDummy(void)
{
}
void SetControllerToPlayer(void)
{
gBattlerControllerFuncs[gActiveBattler] = PlayerBufferRunCommand;
gDoingBattleAnim = FALSE;
gPlayerDpadHoldFrames = 0;
}
static void PlayerBufferExecCompleted(void)
{
gBattlerControllerFuncs[gActiveBattler] = PlayerBufferRunCommand;
if (gBattleTypeFlags & BATTLE_TYPE_LINK)
{
u8 playerId = GetMultiplayerId();
PrepareBufferDataTransferLink(BATTLELINKCOMMTYPE_CONTROLLER_BECOMING_IDLE, 4, &playerId);
gBattleBufferA[gActiveBattler][0] = CONTROLLER_TERMINATOR_NOP;
}
else
{
gBattleControllerExecFlags &= ~gBitTable[gActiveBattler];
}
}
static void PlayerBufferRunCommand(void)
{
if (gBattleControllerExecFlags & gBitTable[gActiveBattler])
{
if (gBattleBufferA[gActiveBattler][0] < ARRAY_COUNT(sPlayerBufferCommands))
sPlayerBufferCommands[gBattleBufferA[gActiveBattler][0]]();
else
PlayerBufferExecCompleted();
}
}
static void CompleteOnBankSpritePosX_0(void)
{
if (gSprites[gBattlerSpriteIds[gActiveBattler]].x2 == 0)
PlayerBufferExecCompleted();
}
static void HandleInputChooseAction(void)
{
u16 itemId = gBattleBufferA[gActiveBattler][2] | (gBattleBufferA[gActiveBattler][3] << 8);
DoBounceEffect(gActiveBattler, BOUNCE_HEALTHBOX, 7, 1);
DoBounceEffect(gActiveBattler, BOUNCE_MON, 7, 1);
if (JOY_REPEAT(DPAD_ANY) && gSaveBlock2Ptr->optionsButtonMode == OPTIONS_BUTTON_MODE_L_EQUALS_A)
gPlayerDpadHoldFrames++;
else
gPlayerDpadHoldFrames = 0;
if (JOY_NEW(A_BUTTON))
{
PlaySE(SE_SELECT);
switch (gActionSelectionCursor[gActiveBattler])
{
case 0: // Top left
BtlController_EmitTwoReturnValues(BATTLELINKCOMMTYPE_CONTROLLER_TO_ENGINE, B_ACTION_USE_MOVE, 0);
break;
case 1: // Top right
BtlController_EmitTwoReturnValues(BATTLELINKCOMMTYPE_CONTROLLER_TO_ENGINE, B_ACTION_USE_ITEM, 0);
break;
case 2: // Bottom left
BtlController_EmitTwoReturnValues(BATTLELINKCOMMTYPE_CONTROLLER_TO_ENGINE, B_ACTION_SWITCH, 0);
break;
case 3: // Bottom right
BtlController_EmitTwoReturnValues(BATTLELINKCOMMTYPE_CONTROLLER_TO_ENGINE, B_ACTION_RUN, 0);
break;
}
PlayerBufferExecCompleted();
}
else if (JOY_NEW(DPAD_LEFT))
{
if (gActionSelectionCursor[gActiveBattler] & 1) // if is B_ACTION_USE_ITEM or B_ACTION_RUN
{
PlaySE(SE_SELECT);
ActionSelectionDestroyCursorAt(gActionSelectionCursor[gActiveBattler]);
gActionSelectionCursor[gActiveBattler] ^= 1;
ActionSelectionCreateCursorAt(gActionSelectionCursor[gActiveBattler], 0);
}
}
else if (JOY_NEW(DPAD_RIGHT))
{
if (!(gActionSelectionCursor[gActiveBattler] & 1)) // if is B_ACTION_USE_MOVE or B_ACTION_SWITCH
{
PlaySE(SE_SELECT);
ActionSelectionDestroyCursorAt(gActionSelectionCursor[gActiveBattler]);
gActionSelectionCursor[gActiveBattler] ^= 1;
ActionSelectionCreateCursorAt(gActionSelectionCursor[gActiveBattler], 0);
}
}
else if (JOY_NEW(DPAD_UP))
{
if (gActionSelectionCursor[gActiveBattler] & 2) // if is B_ACTION_SWITCH or B_ACTION_RUN
{
PlaySE(SE_SELECT);
ActionSelectionDestroyCursorAt(gActionSelectionCursor[gActiveBattler]);
gActionSelectionCursor[gActiveBattler] ^= 2;
ActionSelectionCreateCursorAt(gActionSelectionCursor[gActiveBattler], 0);
}
}
else if (JOY_NEW(DPAD_DOWN))
{
if (!(gActionSelectionCursor[gActiveBattler] & 2)) // if is B_ACTION_USE_MOVE or B_ACTION_USE_ITEM
{
PlaySE(SE_SELECT);
ActionSelectionDestroyCursorAt(gActionSelectionCursor[gActiveBattler]);
gActionSelectionCursor[gActiveBattler] ^= 2;
ActionSelectionCreateCursorAt(gActionSelectionCursor[gActiveBattler], 0);
}
}
else if (JOY_NEW(B_BUTTON) || gPlayerDpadHoldFrames > 59)
{
if ((gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
&& GetBattlerPosition(gActiveBattler) == B_POSITION_PLAYER_RIGHT
&& !(gAbsentBattlerFlags & gBitTable[GetBattlerAtPosition(B_POSITION_PLAYER_LEFT)])
&& !(gBattleTypeFlags & BATTLE_TYPE_MULTI))
{
if (gBattleBufferA[gActiveBattler][1] == B_ACTION_USE_ITEM)
{
// Add item to bag if it is a ball
if (itemId <= LAST_BALL)
AddBagItem(itemId, 1);
else
return;
}
PlaySE(SE_SELECT);
BtlController_EmitTwoReturnValues(BATTLELINKCOMMTYPE_CONTROLLER_TO_ENGINE, B_ACTION_CANCEL_PARTNER, 0);
PlayerBufferExecCompleted();
}
}
else if (JOY_NEW(START_BUTTON))
{
SwapHpBarsWithHpText();
}
}
static void UNUSED UnusedEndBounceEffect(void)
{
EndBounceEffect(gActiveBattler, BOUNCE_HEALTHBOX);
EndBounceEffect(gActiveBattler, BOUNCE_MON);
gBattlerControllerFuncs[gActiveBattler] = HandleInputChooseTarget;
}
static void HandleInputChooseTarget(void)
{
s32 i;
u8 identities[MAX_BATTLERS_COUNT];
memcpy(identities, sTargetIdentities, ARRAY_COUNT(sTargetIdentities));
DoBounceEffect(gMultiUsePlayerCursor, BOUNCE_HEALTHBOX, 15, 1);
// what a weird loop
i = 0;
if (gBattlersCount != 0)
{
do
{
if (i != gMultiUsePlayerCursor)
EndBounceEffect(i, BOUNCE_HEALTHBOX);
i++;
} while (i < gBattlersCount);
}
if (JOY_HELD(DPAD_ANY) && gSaveBlock2Ptr->optionsButtonMode == OPTIONS_BUTTON_MODE_L_EQUALS_A)
gPlayerDpadHoldFrames++;
else
gPlayerDpadHoldFrames = 0;
if (JOY_NEW(A_BUTTON))
{
PlaySE(SE_SELECT);
gSprites[gBattlerSpriteIds[gMultiUsePlayerCursor]].callback = SpriteCB_HideAsMoveTarget;
BtlController_EmitTwoReturnValues(BATTLELINKCOMMTYPE_CONTROLLER_TO_ENGINE, 10, gMoveSelectionCursor[gActiveBattler] | (gMultiUsePlayerCursor << 8));
EndBounceEffect(gMultiUsePlayerCursor, BOUNCE_HEALTHBOX);
PlayerBufferExecCompleted();
}
else if (JOY_NEW(B_BUTTON) || gPlayerDpadHoldFrames > 59)
{
PlaySE(SE_SELECT);
gSprites[gBattlerSpriteIds[gMultiUsePlayerCursor]].callback = SpriteCB_HideAsMoveTarget;
gBattlerControllerFuncs[gActiveBattler] = HandleInputChooseMove;
DoBounceEffect(gActiveBattler, BOUNCE_HEALTHBOX, 7, 1);
DoBounceEffect(gActiveBattler, BOUNCE_MON, 7, 1);
EndBounceEffect(gMultiUsePlayerCursor, BOUNCE_HEALTHBOX);
}
else if (JOY_NEW(DPAD_LEFT | DPAD_UP))
{
PlaySE(SE_SELECT);
gSprites[gBattlerSpriteIds[gMultiUsePlayerCursor]].callback = SpriteCB_HideAsMoveTarget;
do
{
u8 currSelIdentity = GetBattlerPosition(gMultiUsePlayerCursor);
for (i = 0; i < MAX_BATTLERS_COUNT; i++)
{
if (currSelIdentity == identities[i])
break;
}
do
{
if (--i < 0)
#ifdef UBFIX
i = MAX_BATTLERS_COUNT - 1;
#else
i = MAX_BATTLERS_COUNT; // UB: array out of range
#endif
gMultiUsePlayerCursor = GetBattlerAtPosition(identities[i]);
} while (gMultiUsePlayerCursor == gBattlersCount);
i = 0;
switch (GetBattlerPosition(gMultiUsePlayerCursor))
{
case B_POSITION_PLAYER_LEFT:
case B_POSITION_PLAYER_RIGHT:
if (gActiveBattler != gMultiUsePlayerCursor)
i++;
else if (gBattleMoves[GetMonData(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_MOVE1 + gMoveSelectionCursor[gActiveBattler])].target & MOVE_TARGET_USER_OR_SELECTED)
i++;
break;
case B_POSITION_OPPONENT_LEFT:
case B_POSITION_OPPONENT_RIGHT:
i++;
break;
}
if (gAbsentBattlerFlags & gBitTable[gMultiUsePlayerCursor])
i = 0;
} while (i == 0);
gSprites[gBattlerSpriteIds[gMultiUsePlayerCursor]].callback = SpriteCB_ShowAsMoveTarget;
}
else if (JOY_NEW(DPAD_RIGHT | DPAD_DOWN))
{
PlaySE(SE_SELECT);
gSprites[gBattlerSpriteIds[gMultiUsePlayerCursor]].callback = SpriteCB_HideAsMoveTarget;
do
{
u8 currSelIdentity = GetBattlerPosition(gMultiUsePlayerCursor);
for (i = 0; i < MAX_BATTLERS_COUNT; i++)
{
if (currSelIdentity == identities[i])
break;
}
do
{
if (++i > 3)
i = 0;
gMultiUsePlayerCursor = GetBattlerAtPosition(identities[i]);
} while (gMultiUsePlayerCursor == gBattlersCount);
i = 0;
switch (GetBattlerPosition(gMultiUsePlayerCursor))
{
case B_POSITION_PLAYER_LEFT:
case B_POSITION_PLAYER_RIGHT:
if (gActiveBattler != gMultiUsePlayerCursor)
i++;
else if (gBattleMoves[GetMonData(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_MOVE1 + gMoveSelectionCursor[gActiveBattler])].target & MOVE_TARGET_USER_OR_SELECTED)
i++;
break;
case B_POSITION_OPPONENT_LEFT:
case B_POSITION_OPPONENT_RIGHT:
i++;
break;
}
if (gAbsentBattlerFlags & gBitTable[gMultiUsePlayerCursor])
i = 0;
} while (i == 0);
gSprites[gBattlerSpriteIds[gMultiUsePlayerCursor]].callback = SpriteCB_ShowAsMoveTarget;
}
}
static void HandleInputChooseMove(void)
{
bool32 canSelectTarget = FALSE;
struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct *)(&gBattleBufferA[gActiveBattler][4]);
if (JOY_HELD(DPAD_ANY) && gSaveBlock2Ptr->optionsButtonMode == OPTIONS_BUTTON_MODE_L_EQUALS_A)
gPlayerDpadHoldFrames++;
else
gPlayerDpadHoldFrames = 0;
if (JOY_NEW(A_BUTTON))
{
u8 moveTarget;
PlaySE(SE_SELECT);
if (moveInfo->moves[gMoveSelectionCursor[gActiveBattler]] == MOVE_CURSE)
{
if (moveInfo->monTypes[0] != TYPE_GHOST && moveInfo->monTypes[1] != TYPE_GHOST)
moveTarget = MOVE_TARGET_USER;
else
moveTarget = MOVE_TARGET_SELECTED;
}
else
{
moveTarget = gBattleMoves[moveInfo->moves[gMoveSelectionCursor[gActiveBattler]]].target;
}
if (moveTarget & MOVE_TARGET_USER)
gMultiUsePlayerCursor = gActiveBattler;
else
gMultiUsePlayerCursor = GetBattlerAtPosition(BATTLE_OPPOSITE(GET_BATTLER_SIDE(gActiveBattler)));
if (!gBattleBufferA[gActiveBattler][1]) // not a double battle
{
if (moveTarget & MOVE_TARGET_USER_OR_SELECTED && !gBattleBufferA[gActiveBattler][2])
canSelectTarget++;
}
else // double battle
{
if (!(moveTarget & (MOVE_TARGET_RANDOM | MOVE_TARGET_BOTH | MOVE_TARGET_DEPENDS | MOVE_TARGET_FOES_AND_ALLY | MOVE_TARGET_OPPONENTS_FIELD | MOVE_TARGET_USER)))
canSelectTarget++; // either selected or user
if (moveInfo->currentPp[gMoveSelectionCursor[gActiveBattler]] == 0)
{
canSelectTarget = FALSE;
}
else if (!(moveTarget & (MOVE_TARGET_USER | MOVE_TARGET_USER_OR_SELECTED)) && CountAliveMonsInBattle(BATTLE_ALIVE_EXCEPT_ACTIVE) <= 1)
{
gMultiUsePlayerCursor = GetDefaultMoveTarget(gActiveBattler);
canSelectTarget = FALSE;
}
}
if (!canSelectTarget)
{
BtlController_EmitTwoReturnValues(BATTLELINKCOMMTYPE_CONTROLLER_TO_ENGINE, 10, gMoveSelectionCursor[gActiveBattler] | (gMultiUsePlayerCursor << 8));
PlayerBufferExecCompleted();
}
else
{
gBattlerControllerFuncs[gActiveBattler] = HandleInputChooseTarget;
if (moveTarget & (MOVE_TARGET_USER | MOVE_TARGET_USER_OR_SELECTED))
gMultiUsePlayerCursor = gActiveBattler;
else if (gAbsentBattlerFlags & gBitTable[GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT)])
gMultiUsePlayerCursor = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT);
else
gMultiUsePlayerCursor = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT);
gSprites[gBattlerSpriteIds[gMultiUsePlayerCursor]].callback = SpriteCB_ShowAsMoveTarget;
}
}
else if (JOY_NEW(B_BUTTON) || gPlayerDpadHoldFrames > 59)
{
PlaySE(SE_SELECT);
BtlController_EmitTwoReturnValues(BATTLELINKCOMMTYPE_CONTROLLER_TO_ENGINE, 10, 0xFFFF);
PlayerBufferExecCompleted();
}
else if (JOY_NEW(DPAD_LEFT))
{
if (gMoveSelectionCursor[gActiveBattler] & 1)
{
MoveSelectionDestroyCursorAt(gMoveSelectionCursor[gActiveBattler]);
gMoveSelectionCursor[gActiveBattler] ^= 1;
PlaySE(SE_SELECT);
MoveSelectionCreateCursorAt(gMoveSelectionCursor[gActiveBattler], 0);
MoveSelectionDisplayPpNumber();
MoveSelectionDisplayMoveType();
}
}
else if (JOY_NEW(DPAD_RIGHT))
{
if (!(gMoveSelectionCursor[gActiveBattler] & 1)
&& (gMoveSelectionCursor[gActiveBattler] ^ 1) < gNumberOfMovesToChoose)
{
MoveSelectionDestroyCursorAt(gMoveSelectionCursor[gActiveBattler]);
gMoveSelectionCursor[gActiveBattler] ^= 1;
PlaySE(SE_SELECT);
MoveSelectionCreateCursorAt(gMoveSelectionCursor[gActiveBattler], 0);
MoveSelectionDisplayPpNumber();
MoveSelectionDisplayMoveType();
}
}
else if (JOY_NEW(DPAD_UP))
{
if (gMoveSelectionCursor[gActiveBattler] & 2)
{
MoveSelectionDestroyCursorAt(gMoveSelectionCursor[gActiveBattler]);
gMoveSelectionCursor[gActiveBattler] ^= 2;
PlaySE(SE_SELECT);
MoveSelectionCreateCursorAt(gMoveSelectionCursor[gActiveBattler], 0);
MoveSelectionDisplayPpNumber();
MoveSelectionDisplayMoveType();
}
}
else if (JOY_NEW(DPAD_DOWN))
{
if (!(gMoveSelectionCursor[gActiveBattler] & 2)
&& (gMoveSelectionCursor[gActiveBattler] ^ 2) < gNumberOfMovesToChoose)
{
MoveSelectionDestroyCursorAt(gMoveSelectionCursor[gActiveBattler]);
gMoveSelectionCursor[gActiveBattler] ^= 2;
PlaySE(SE_SELECT);
MoveSelectionCreateCursorAt(gMoveSelectionCursor[gActiveBattler], 0);
MoveSelectionDisplayPpNumber();
MoveSelectionDisplayMoveType();
}
}
else if (JOY_NEW(SELECT_BUTTON))
{
if (gNumberOfMovesToChoose > 1 && !(gBattleTypeFlags & BATTLE_TYPE_LINK))
{
MoveSelectionCreateCursorAt(gMoveSelectionCursor[gActiveBattler], 29);
if (gMoveSelectionCursor[gActiveBattler] != 0)
gMultiUsePlayerCursor = 0;
else
gMultiUsePlayerCursor = gMoveSelectionCursor[gActiveBattler] + 1;
MoveSelectionCreateCursorAt(gMultiUsePlayerCursor, 27);
BattlePutTextOnWindow(gText_BattleSwitchWhich, B_WIN_SWITCH_PROMPT);
gBattlerControllerFuncs[gActiveBattler] = HandleMoveSwitching;
}
}
}
static u32 UNUSED HandleMoveInputUnused(void)
{
u32 var = 0;
if (JOY_NEW(A_BUTTON))
{
PlaySE(SE_SELECT);
var = 1;
}
if (JOY_NEW(B_BUTTON))
{
PlaySE(SE_SELECT);
gBattle_BG0_X = 0;
gBattle_BG0_Y = DISPLAY_HEIGHT * 2;
var = 0xFF;
}
if (JOY_NEW(DPAD_LEFT) && gMoveSelectionCursor[gActiveBattler] & 1)
{
MoveSelectionDestroyCursorAt(gMoveSelectionCursor[gActiveBattler]);
gMoveSelectionCursor[gActiveBattler] ^= 1;
PlaySE(SE_SELECT);
MoveSelectionCreateCursorAt(gMoveSelectionCursor[gActiveBattler], 0);
}
if (JOY_NEW(DPAD_RIGHT) && !(gMoveSelectionCursor[gActiveBattler] & 1)
&& (gMoveSelectionCursor[gActiveBattler] ^ 1) < gNumberOfMovesToChoose)
{
MoveSelectionDestroyCursorAt(gMoveSelectionCursor[gActiveBattler]);
gMoveSelectionCursor[gActiveBattler] ^= 1;
PlaySE(SE_SELECT);
MoveSelectionCreateCursorAt(gMoveSelectionCursor[gActiveBattler], 0);
}
if (JOY_NEW(DPAD_UP) && gMoveSelectionCursor[gActiveBattler] & 2)
{
MoveSelectionDestroyCursorAt(gMoveSelectionCursor[gActiveBattler]);
gMoveSelectionCursor[gActiveBattler] ^= 2;
PlaySE(SE_SELECT);
MoveSelectionCreateCursorAt(gMoveSelectionCursor[gActiveBattler], 0);
}
if (JOY_NEW(DPAD_DOWN) && !(gMoveSelectionCursor[gActiveBattler] & 2)
&& (gMoveSelectionCursor[gActiveBattler] ^ 2) < gNumberOfMovesToChoose)
{
MoveSelectionDestroyCursorAt(gMoveSelectionCursor[gActiveBattler]);
gMoveSelectionCursor[gActiveBattler] ^= 2;
PlaySE(SE_SELECT);
MoveSelectionCreateCursorAt(gMoveSelectionCursor[gActiveBattler], 0);
}
return var;
}
static void HandleMoveSwitching(void)
{
u8 perMovePPBonuses[MAX_MON_MOVES];
struct ChooseMoveStruct moveStruct;
u8 totalPPBonuses;
if (JOY_NEW(A_BUTTON | SELECT_BUTTON))
{
PlaySE(SE_SELECT);
if (gMoveSelectionCursor[gActiveBattler] != gMultiUsePlayerCursor)
{
struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct *)(&gBattleBufferA[gActiveBattler][4]);
s32 i;
// swap moves and pp
i = moveInfo->moves[gMoveSelectionCursor[gActiveBattler]];
moveInfo->moves[gMoveSelectionCursor[gActiveBattler]] = moveInfo->moves[gMultiUsePlayerCursor];
moveInfo->moves[gMultiUsePlayerCursor] = i;
i = moveInfo->currentPp[gMoveSelectionCursor[gActiveBattler]];
moveInfo->currentPp[gMoveSelectionCursor[gActiveBattler]] = moveInfo->currentPp[gMultiUsePlayerCursor];
moveInfo->currentPp[gMultiUsePlayerCursor] = i;
i = moveInfo->maxPp[gMoveSelectionCursor[gActiveBattler]];
moveInfo->maxPp[gMoveSelectionCursor[gActiveBattler]] = moveInfo->maxPp[gMultiUsePlayerCursor];
moveInfo->maxPp[gMultiUsePlayerCursor] = i;
if (gDisableStructs[gActiveBattler].mimickedMoves & gBitTable[gMoveSelectionCursor[gActiveBattler]])
{
gDisableStructs[gActiveBattler].mimickedMoves &= (~gBitTable[gMoveSelectionCursor[gActiveBattler]]);
gDisableStructs[gActiveBattler].mimickedMoves |= gBitTable[gMultiUsePlayerCursor];
}
MoveSelectionDisplayMoveNames();
for (i = 0; i < MAX_MON_MOVES; i++)
perMovePPBonuses[i] = (gBattleMons[gActiveBattler].ppBonuses & (3 << (i * 2))) >> (i * 2);
totalPPBonuses = perMovePPBonuses[gMoveSelectionCursor[gActiveBattler]];
perMovePPBonuses[gMoveSelectionCursor[gActiveBattler]] = perMovePPBonuses[gMultiUsePlayerCursor];
perMovePPBonuses[gMultiUsePlayerCursor] = totalPPBonuses;
totalPPBonuses = 0;
for (i = 0; i < MAX_MON_MOVES; i++)
totalPPBonuses |= perMovePPBonuses[i] << (i * 2);
gBattleMons[gActiveBattler].ppBonuses = totalPPBonuses;
for (i = 0; i < MAX_MON_MOVES; i++)
{
gBattleMons[gActiveBattler].moves[i] = moveInfo->moves[i];
gBattleMons[gActiveBattler].pp[i] = moveInfo->currentPp[i];
}
if (!(gBattleMons[gActiveBattler].status2 & STATUS2_TRANSFORMED))
{
for (i = 0; i < MAX_MON_MOVES; i++)
{
moveStruct.moves[i] = GetMonData(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_MOVE1 + i);
moveStruct.currentPp[i] = GetMonData(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_PP1 + i);
}
totalPPBonuses = GetMonData(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_PP_BONUSES);
for (i = 0; i < MAX_MON_MOVES; i++)
perMovePPBonuses[i] = (totalPPBonuses & (3 << (i * 2))) >> (i * 2);
i = moveStruct.moves[gMoveSelectionCursor[gActiveBattler]];
moveStruct.moves[gMoveSelectionCursor[gActiveBattler]] = moveStruct.moves[gMultiUsePlayerCursor];
moveStruct.moves[gMultiUsePlayerCursor] = i;
i = moveStruct.currentPp[gMoveSelectionCursor[gActiveBattler]];
moveStruct.currentPp[gMoveSelectionCursor[gActiveBattler]] = moveStruct.currentPp[gMultiUsePlayerCursor];
moveStruct.currentPp[gMultiUsePlayerCursor] = i;
totalPPBonuses = perMovePPBonuses[gMoveSelectionCursor[gActiveBattler]];
perMovePPBonuses[gMoveSelectionCursor[gActiveBattler]] = perMovePPBonuses[gMultiUsePlayerCursor];
perMovePPBonuses[gMultiUsePlayerCursor] = totalPPBonuses;
totalPPBonuses = 0;
for (i = 0; i < MAX_MON_MOVES; i++)
totalPPBonuses |= perMovePPBonuses[i] << (i * 2);
for (i = 0; i < MAX_MON_MOVES; i++)
{
SetMonData(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_MOVE1 + i, &moveStruct.moves[i]);
SetMonData(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_PP1 + i, &moveStruct.currentPp[i]);
}
SetMonData(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_PP_BONUSES, &totalPPBonuses);
}
}
gBattlerControllerFuncs[gActiveBattler] = HandleInputChooseMove;
gMoveSelectionCursor[gActiveBattler] = gMultiUsePlayerCursor;
MoveSelectionCreateCursorAt(gMoveSelectionCursor[gActiveBattler], 0);
MoveSelectionDisplayPpString();
MoveSelectionDisplayPpNumber();
MoveSelectionDisplayMoveType();
}
else if (JOY_NEW(B_BUTTON | SELECT_BUTTON))
{
PlaySE(SE_SELECT);
MoveSelectionDestroyCursorAt(gMultiUsePlayerCursor);
MoveSelectionCreateCursorAt(gMoveSelectionCursor[gActiveBattler], 0);
gBattlerControllerFuncs[gActiveBattler] = HandleInputChooseMove;
MoveSelectionDisplayPpString();
MoveSelectionDisplayPpNumber();
MoveSelectionDisplayMoveType();
}
else if (JOY_NEW(DPAD_LEFT))
{
if (gMultiUsePlayerCursor & 1)
{
if (gMultiUsePlayerCursor == gMoveSelectionCursor[gActiveBattler])
MoveSelectionCreateCursorAt(gMoveSelectionCursor[gActiveBattler], 29);
else
MoveSelectionDestroyCursorAt(gMultiUsePlayerCursor);
gMultiUsePlayerCursor ^= 1;
PlaySE(SE_SELECT);
if (gMultiUsePlayerCursor == gMoveSelectionCursor[gActiveBattler])
MoveSelectionCreateCursorAt(gMultiUsePlayerCursor, 0);
else
MoveSelectionCreateCursorAt(gMultiUsePlayerCursor, 27);
}
}
else if (JOY_NEW(DPAD_RIGHT))
{
if (!(gMultiUsePlayerCursor & 1) && (gMultiUsePlayerCursor ^ 1) < gNumberOfMovesToChoose)
{
if (gMultiUsePlayerCursor == gMoveSelectionCursor[gActiveBattler])
MoveSelectionCreateCursorAt(gMoveSelectionCursor[gActiveBattler], 29);
else
MoveSelectionDestroyCursorAt(gMultiUsePlayerCursor);
gMultiUsePlayerCursor ^= 1;
PlaySE(SE_SELECT);
if (gMultiUsePlayerCursor == gMoveSelectionCursor[gActiveBattler])
MoveSelectionCreateCursorAt(gMultiUsePlayerCursor, 0);
else
MoveSelectionCreateCursorAt(gMultiUsePlayerCursor, 27);
}
}
else if (JOY_NEW(DPAD_UP))
{
if (gMultiUsePlayerCursor & 2)
{
if (gMultiUsePlayerCursor == gMoveSelectionCursor[gActiveBattler])
MoveSelectionCreateCursorAt(gMoveSelectionCursor[gActiveBattler], 29);
else
MoveSelectionDestroyCursorAt(gMultiUsePlayerCursor);
gMultiUsePlayerCursor ^= 2;
PlaySE(SE_SELECT);
if (gMultiUsePlayerCursor == gMoveSelectionCursor[gActiveBattler])
MoveSelectionCreateCursorAt(gMultiUsePlayerCursor, 0);
else
MoveSelectionCreateCursorAt(gMultiUsePlayerCursor, 27);
}
}
else if (JOY_NEW(DPAD_DOWN))
{
if (!(gMultiUsePlayerCursor & 2) && (gMultiUsePlayerCursor ^ 2) < gNumberOfMovesToChoose)
{
if (gMultiUsePlayerCursor == gMoveSelectionCursor[gActiveBattler])
MoveSelectionCreateCursorAt(gMoveSelectionCursor[gActiveBattler], 29);
else
MoveSelectionDestroyCursorAt(gMultiUsePlayerCursor);
gMultiUsePlayerCursor ^= 2;
PlaySE(SE_SELECT);
if (gMultiUsePlayerCursor == gMoveSelectionCursor[gActiveBattler])
MoveSelectionCreateCursorAt(gMultiUsePlayerCursor, 0);
else
MoveSelectionCreateCursorAt(gMultiUsePlayerCursor, 27);
}
}
}
static void SetLinkBattleEndCallbacks(void)
{
if (gWirelessCommType == 0)
{
if (gReceivedRemoteLinkPlayers == 0)
{
m4aSongNumStop(SE_LOW_HEALTH);
gMain.inBattle = FALSE;
gMain.callback1 = gPreBattleCallback1;
SetMainCallback2(CB2_InitEndLinkBattle);
if (gBattleOutcome == B_OUTCOME_WON)
TryPutLinkBattleTvShowOnAir();
FreeAllWindowBuffers();
}
}
else
{
if (IsLinkTaskFinished())
{
m4aSongNumStop(SE_LOW_HEALTH);
gMain.inBattle = FALSE;
gMain.callback1 = gPreBattleCallback1;
SetMainCallback2(CB2_InitEndLinkBattle);
if (gBattleOutcome == B_OUTCOME_WON)
TryPutLinkBattleTvShowOnAir();
FreeAllWindowBuffers();
}
}
}
// Despite handling link battles separately, this is only ever used by link battles
void SetBattleEndCallbacks(void)
{
if (!gPaletteFade.active)
{
if (gBattleTypeFlags & BATTLE_TYPE_LINK)
{
if (IsLinkTaskFinished())
{
if (gWirelessCommType == 0)
SetCloseLinkCallback();
else
SetLinkStandbyCallback();
gBattlerControllerFuncs[gActiveBattler] = SetLinkBattleEndCallbacks;
}
}
else
{
m4aSongNumStop(SE_LOW_HEALTH);
gMain.inBattle = FALSE;
gMain.callback1 = gPreBattleCallback1;
SetMainCallback2(gMain.savedCallback);
}
}
}
static void CompleteOnBattlerSpriteCallbackDummy(void)
{
if (gSprites[gBattlerSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy)
PlayerBufferExecCompleted();
}
static void CompleteOnBankSpriteCallbackDummy2(void)
{
if (gSprites[gBattlerSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy)
PlayerBufferExecCompleted();
}
static void FreeTrainerSpriteAfterSlide(void)
{
if (gSprites[gBattlerSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy)
{
BattleGfxSfxDummy3(gSaveBlock2Ptr->playerGender);
FreeSpriteOamMatrix(&gSprites[gBattlerSpriteIds[gActiveBattler]]);
DestroySprite(&gSprites[gBattlerSpriteIds[gActiveBattler]]);
PlayerBufferExecCompleted();
}
}
static void Intro_DelayAndEnd(void)
{
if (--gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].introEndDelay == (u8)-1)
{
gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].introEndDelay = 0;
PlayerBufferExecCompleted();
}
}
static void Intro_WaitForShinyAnimAndHealthbox(void)
{
bool8 healthboxAnimDone = FALSE;
// Check if healthbox has finished sliding in
if (!IsDoubleBattle() || (IsDoubleBattle() && (gBattleTypeFlags & BATTLE_TYPE_MULTI)))
{
if (gSprites[gHealthboxSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy)
healthboxAnimDone = TRUE;
}
else
{
if (gSprites[gHealthboxSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy
&& gSprites[gHealthboxSpriteIds[BATTLE_PARTNER(gActiveBattler)]].callback == SpriteCallbackDummy)
healthboxAnimDone = TRUE;
}
// If healthbox and shiny anim are done
if (healthboxAnimDone && gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].finishedShinyMonAnim
&& gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(gActiveBattler)].finishedShinyMonAnim)
{
// Reset shiny anim (even if it didn't occur)
gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].triedShinyMonAnim = FALSE;
gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].finishedShinyMonAnim = FALSE;
gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(gActiveBattler)].triedShinyMonAnim = FALSE;
gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(gActiveBattler)].finishedShinyMonAnim = FALSE;
FreeSpriteTilesByTag(ANIM_TAG_GOLD_STARS);
FreeSpritePaletteByTag(ANIM_TAG_GOLD_STARS);
HandleLowHpMusicChange(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], gActiveBattler);
if (IsDoubleBattle())
HandleLowHpMusicChange(&gPlayerParty[gBattlerPartyIndexes[BATTLE_PARTNER(gActiveBattler)]], BATTLE_PARTNER(gActiveBattler));
gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].introEndDelay = 3;
gBattlerControllerFuncs[gActiveBattler] = Intro_DelayAndEnd;
}
}
static void Intro_TryShinyAnimShowHealthbox(void)
{
bool32 bgmRestored = FALSE;
bool32 battlerAnimsDone = FALSE;
// Start shiny animation if applicable for 1st Pokémon
if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].triedShinyMonAnim
&& !gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].ballAnimActive)
TryShinyAnimation(gActiveBattler, &gPlayerParty[gBattlerPartyIndexes[gActiveBattler]]);
// Start shiny animation if applicable for 2nd Pokémon
if (!gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(gActiveBattler)].triedShinyMonAnim
&& !gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(gActiveBattler)].ballAnimActive)
TryShinyAnimation(BATTLE_PARTNER(gActiveBattler), &gPlayerParty[gBattlerPartyIndexes[BATTLE_PARTNER(gActiveBattler)]]);
// Show healthbox after ball anim
if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].ballAnimActive
&& !gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(gActiveBattler)].ballAnimActive)
{
if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].healthboxSlideInStarted)
{
if (IsDoubleBattle() && !(gBattleTypeFlags & BATTLE_TYPE_MULTI))
{
UpdateHealthboxAttribute(gHealthboxSpriteIds[BATTLE_PARTNER(gActiveBattler)], &gPlayerParty[gBattlerPartyIndexes[BATTLE_PARTNER(gActiveBattler)]], HEALTHBOX_ALL);
StartHealthboxSlideIn(BATTLE_PARTNER(gActiveBattler));
SetHealthboxSpriteVisible(gHealthboxSpriteIds[BATTLE_PARTNER(gActiveBattler)]);
}
UpdateHealthboxAttribute(gHealthboxSpriteIds[gActiveBattler], &gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], HEALTHBOX_ALL);
StartHealthboxSlideIn(gActiveBattler);
SetHealthboxSpriteVisible(gHealthboxSpriteIds[gActiveBattler]);
}
gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].healthboxSlideInStarted = TRUE;
}
// Restore bgm after cry has played and healthbox anim is started
if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].waitForCry
&& gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].healthboxSlideInStarted
&& !gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(gActiveBattler)].waitForCry
&& !IsCryPlayingOrClearCrySongs())
{
if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].bgmRestored)
{
if (gBattleTypeFlags & BATTLE_TYPE_MULTI && gBattleTypeFlags & BATTLE_TYPE_LINK)
m4aMPlayContinue(&gMPlayInfo_BGM);
else
m4aMPlayVolumeControl(&gMPlayInfo_BGM, TRACKS_ALL, 0x100);
}
gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].bgmRestored = TRUE;
bgmRestored = TRUE;
}
// Wait for battler anims
if (!IsDoubleBattle() || (IsDoubleBattle() && (gBattleTypeFlags & BATTLE_TYPE_MULTI)))
{
if (gSprites[gBattleControllerData[gActiveBattler]].callback == SpriteCallbackDummy
&& gSprites[gBattlerSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy)
{
battlerAnimsDone = TRUE;
}
}
else
{
if (gSprites[gBattleControllerData[gActiveBattler]].callback == SpriteCallbackDummy
&& gSprites[gBattlerSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy
&& gSprites[gBattleControllerData[BATTLE_PARTNER(gActiveBattler)]].callback == SpriteCallbackDummy
&& gSprites[gBattlerSpriteIds[BATTLE_PARTNER(gActiveBattler)]].callback == SpriteCallbackDummy)
{
battlerAnimsDone = TRUE;
}
}
// Clean up
if (bgmRestored && battlerAnimsDone)
{
if (IsDoubleBattle() && !(gBattleTypeFlags & BATTLE_TYPE_MULTI))
DestroySprite(&gSprites[gBattleControllerData[BATTLE_PARTNER(gActiveBattler)]]);
DestroySprite(&gSprites[gBattleControllerData[gActiveBattler]]);
gBattleSpritesDataPtr->animationData->introAnimActive = FALSE;
gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].bgmRestored = FALSE;
gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].healthboxSlideInStarted = FALSE;
gBattlerControllerFuncs[gActiveBattler] = Intro_WaitForShinyAnimAndHealthbox;
}
}
static void SwitchIn_CleanShinyAnimShowSubstitute(void)
{
if (gSprites[gHealthboxSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy
&& gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].finishedShinyMonAnim
&& gSprites[gBattlerSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy)
{
CopyBattleSpriteInvisibility(gActiveBattler);
// Reset shiny anim (even if it didn't occur)
gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].triedShinyMonAnim = FALSE;
gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].finishedShinyMonAnim = FALSE;
FreeSpriteTilesByTag(ANIM_TAG_GOLD_STARS);
FreeSpritePaletteByTag(ANIM_TAG_GOLD_STARS);
// Check if Substitute should be shown
if (gBattleSpritesDataPtr->battlerData[gActiveBattler].behindSubstitute)
InitAndLaunchSpecialAnimation(gActiveBattler, gActiveBattler, gActiveBattler, B_ANIM_MON_TO_SUBSTITUTE);
gBattlerControllerFuncs[gActiveBattler] = SwitchIn_HandleSoundAndEnd;
}
}
static void SwitchIn_HandleSoundAndEnd(void)
{
if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].specialAnimActive
&& !IsCryPlayingOrClearCrySongs())
{
m4aMPlayVolumeControl(&gMPlayInfo_BGM, TRACKS_ALL, 0x100);
HandleLowHpMusicChange(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], gActiveBattler);
PlayerBufferExecCompleted();
}
}
static void SwitchIn_TryShinyAnimShowHealthbox(void)
{
// Start shiny animation if applicable
if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].triedShinyMonAnim
&& !gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].ballAnimActive)
TryShinyAnimation(gActiveBattler, &gPlayerParty[gBattlerPartyIndexes[gActiveBattler]]);
// Wait for ball anim, then show healthbox
if (gSprites[gBattleControllerData[gActiveBattler]].callback == SpriteCallbackDummy
&& !gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].ballAnimActive)
{
DestroySprite(&gSprites[gBattleControllerData[gActiveBattler]]);
UpdateHealthboxAttribute(gHealthboxSpriteIds[gActiveBattler], &gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], HEALTHBOX_ALL);
StartHealthboxSlideIn(gActiveBattler);
SetHealthboxSpriteVisible(gHealthboxSpriteIds[gActiveBattler]);
gBattlerControllerFuncs[gActiveBattler] = SwitchIn_CleanShinyAnimShowSubstitute;
}
}
void Task_PlayerController_RestoreBgmAfterCry(u8 taskId)
{
if (!IsCryPlayingOrClearCrySongs())
{
m4aMPlayVolumeControl(&gMPlayInfo_BGM, TRACKS_ALL, 0x100);
DestroyTask(taskId);
}
}
static void CompleteOnHealthbarDone(void)
{
s16 hpValue = MoveBattleBar(gActiveBattler, gHealthboxSpriteIds[gActiveBattler], HEALTH_BAR, 0);
SetHealthboxSpriteVisible(gHealthboxSpriteIds[gActiveBattler]);
if (hpValue != -1)
{
UpdateHpTextInHealthbox(gHealthboxSpriteIds[gActiveBattler], hpValue, HP_CURRENT);
}
else
{
HandleLowHpMusicChange(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], gActiveBattler);
PlayerBufferExecCompleted();
}
}
static void CompleteOnInactiveTextPrinter(void)
{
if (!IsTextPrinterActive(B_WIN_MSG))
PlayerBufferExecCompleted();
}
#define tExpTask_monId data[0]
#define tExpTask_gainedExp data[1]
#define tExpTask_battler data[2]
#define tExpTask_frames data[10]
static void Task_GiveExpToMon(u8 taskId)
{
u32 monId = (u8)(gTasks[taskId].tExpTask_monId);
u8 battlerId = gTasks[taskId].tExpTask_battler;
s16 gainedExp = gTasks[taskId].tExpTask_gainedExp;
if (IsDoubleBattle() == TRUE || monId != gBattlerPartyIndexes[battlerId]) // Give exp without moving the expbar.
{
struct Pokemon *mon = &gPlayerParty[monId];
u16 species = GetMonData(mon, MON_DATA_SPECIES);
u8 level = GetMonData(mon, MON_DATA_LEVEL);
u32 currExp = GetMonData(mon, MON_DATA_EXP);
u32 nextLvlExp = gExperienceTables[gSpeciesInfo[species].growthRate][level + 1];
if (currExp + gainedExp >= nextLvlExp)
{
u8 savedActiveBattler;
SetMonData(mon, MON_DATA_EXP, &nextLvlExp);
CalculateMonStats(mon);
gainedExp -= nextLvlExp - currExp;
savedActiveBattler = gActiveBattler;
gActiveBattler = battlerId;
BtlController_EmitTwoReturnValues(BATTLELINKCOMMTYPE_CONTROLLER_TO_ENGINE, RET_VALUE_LEVELED_UP, gainedExp);
gActiveBattler = savedActiveBattler;
if (IsDoubleBattle() == TRUE
&& ((u16)(monId) == gBattlerPartyIndexes[battlerId] || (u16)(monId) == gBattlerPartyIndexes[BATTLE_PARTNER(battlerId)]))
gTasks[taskId].func = Task_LaunchLvlUpAnim;
else
gTasks[taskId].func = DestroyExpTaskAndCompleteOnInactiveTextPrinter;
}
else
{
currExp += gainedExp;
SetMonData(mon, MON_DATA_EXP, &currExp);
gBattlerControllerFuncs[battlerId] = CompleteOnInactiveTextPrinter;
DestroyTask(taskId);
}
}
else
{
gTasks[taskId].func = Task_PrepareToGiveExpWithExpBar;
}
}
static void Task_PrepareToGiveExpWithExpBar(u8 taskId)
{
u8 monIndex = gTasks[taskId].tExpTask_monId;
s32 gainedExp = gTasks[taskId].tExpTask_gainedExp;
u8 battlerId = gTasks[taskId].tExpTask_battler;
struct Pokemon *mon = &gPlayerParty[monIndex];
u8 level = GetMonData(mon, MON_DATA_LEVEL);
u16 species = GetMonData(mon, MON_DATA_SPECIES);
u32 exp = GetMonData(mon, MON_DATA_EXP);
u32 currLvlExp = gExperienceTables[gSpeciesInfo[species].growthRate][level];
u32 expToNextLvl;
exp -= currLvlExp;
expToNextLvl = gExperienceTables[gSpeciesInfo[species].growthRate][level + 1] - currLvlExp;
SetBattleBarStruct(battlerId, gHealthboxSpriteIds[battlerId], expToNextLvl, exp, -gainedExp);
PlaySE(SE_EXP);
gTasks[taskId].func = Task_GiveExpWithExpBar;
}
static void Task_GiveExpWithExpBar(u8 taskId)
{
if (gTasks[taskId].tExpTask_frames < 13)
{
gTasks[taskId].tExpTask_frames++;
}
else
{
u8 monId = gTasks[taskId].tExpTask_monId;
s16 gainedExp = gTasks[taskId].tExpTask_gainedExp;
u8 battlerId = gTasks[taskId].tExpTask_battler;
s16 newExpPoints;
newExpPoints = MoveBattleBar(battlerId, gHealthboxSpriteIds[battlerId], EXP_BAR, 0);
SetHealthboxSpriteVisible(gHealthboxSpriteIds[battlerId]);
if (newExpPoints == -1) // The bar has been filled with given exp points.
{
u8 level;
s32 currExp;
u16 species;
s32 expOnNextLvl;
m4aSongNumStop(SE_EXP);
level = GetMonData(&gPlayerParty[monId], MON_DATA_LEVEL);
currExp = GetMonData(&gPlayerParty[monId], MON_DATA_EXP);
species = GetMonData(&gPlayerParty[monId], MON_DATA_SPECIES);
expOnNextLvl = gExperienceTables[gSpeciesInfo[species].growthRate][level + 1];
if (currExp + gainedExp >= expOnNextLvl)
{
u8 savedActiveBattler;
SetMonData(&gPlayerParty[monId], MON_DATA_EXP, &expOnNextLvl);
CalculateMonStats(&gPlayerParty[monId]);
gainedExp -= expOnNextLvl - currExp;
savedActiveBattler = gActiveBattler;
gActiveBattler = battlerId;
BtlController_EmitTwoReturnValues(BATTLELINKCOMMTYPE_CONTROLLER_TO_ENGINE, RET_VALUE_LEVELED_UP, gainedExp);
gActiveBattler = savedActiveBattler;
gTasks[taskId].func = Task_LaunchLvlUpAnim;
}
else
{
currExp += gainedExp;
SetMonData(&gPlayerParty[monId], MON_DATA_EXP, &currExp);
gBattlerControllerFuncs[battlerId] = CompleteOnInactiveTextPrinter;
DestroyTask(taskId);
}
}
}
}
static void Task_LaunchLvlUpAnim(u8 taskId)
{
u8 battlerId = gTasks[taskId].tExpTask_battler;
u8 monIndex = gTasks[taskId].tExpTask_monId;
if (IsDoubleBattle() == TRUE && monIndex == gBattlerPartyIndexes[BATTLE_PARTNER(battlerId)])
battlerId ^= BIT_FLANK;
InitAndLaunchSpecialAnimation(battlerId, battlerId, battlerId, B_ANIM_LVL_UP);
gTasks[taskId].func = Task_UpdateLvlInHealthbox;
}
static void Task_UpdateLvlInHealthbox(u8 taskId)
{
u8 battlerId = gTasks[taskId].tExpTask_battler;
if (!gBattleSpritesDataPtr->healthBoxesData[battlerId].specialAnimActive)
{
u8 monIndex = gTasks[taskId].tExpTask_monId;
GetMonData(&gPlayerParty[monIndex], MON_DATA_LEVEL); // Unused return value.
if (IsDoubleBattle() == TRUE && monIndex == gBattlerPartyIndexes[BATTLE_PARTNER(battlerId)])
UpdateHealthboxAttribute(gHealthboxSpriteIds[BATTLE_PARTNER(battlerId)], &gPlayerParty[monIndex], HEALTHBOX_ALL);
else
UpdateHealthboxAttribute(gHealthboxSpriteIds[battlerId], &gPlayerParty[monIndex], HEALTHBOX_ALL);
gTasks[taskId].func = DestroyExpTaskAndCompleteOnInactiveTextPrinter;
}
}
static void DestroyExpTaskAndCompleteOnInactiveTextPrinter(u8 taskId)
{
u8 monIndex;
u8 battlerId;
monIndex = gTasks[taskId].tExpTask_monId;
GetMonData(&gPlayerParty[monIndex], MON_DATA_LEVEL); // Unused return value.
battlerId = gTasks[taskId].tExpTask_battler;
gBattlerControllerFuncs[battlerId] = CompleteOnInactiveTextPrinter;
DestroyTask(taskId);
}
static void FreeMonSpriteAfterFaintAnim(void)
{
if (gSprites[gBattlerSpriteIds[gActiveBattler]].y + gSprites[gBattlerSpriteIds[gActiveBattler]].y2 > DISPLAY_HEIGHT)
{
u16 species = GetMonData(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_SPECIES);
BattleGfxSfxDummy2(species);
FreeOamMatrix(gSprites[gBattlerSpriteIds[gActiveBattler]].oam.matrixNum);
DestroySprite(&gSprites[gBattlerSpriteIds[gActiveBattler]]);
SetHealthboxSpriteInvisible(gHealthboxSpriteIds[gActiveBattler]);
PlayerBufferExecCompleted();
}
}
static void FreeMonSpriteAfterSwitchOutAnim(void)
{
if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].specialAnimActive)
{
FreeSpriteOamMatrix(&gSprites[gBattlerSpriteIds[gActiveBattler]]);
DestroySprite(&gSprites[gBattlerSpriteIds[gActiveBattler]]);
SetHealthboxSpriteInvisible(gHealthboxSpriteIds[gActiveBattler]);
PlayerBufferExecCompleted();
}
}
static void CompleteOnInactiveTextPrinter2(void)
{
if (!IsTextPrinterActive(B_WIN_MSG))
PlayerBufferExecCompleted();
}
static void OpenPartyMenuToChooseMon(void)
{
if (!gPaletteFade.active)
{
u8 caseId;
gBattlerControllerFuncs[gActiveBattler] = WaitForMonSelection;
caseId = gTasks[gBattleControllerData[gActiveBattler]].data[0];
DestroyTask(gBattleControllerData[gActiveBattler]);
FreeAllWindowBuffers();
OpenPartyMenuInBattle(caseId);
}
}
static void WaitForMonSelection(void)
{
if (gMain.callback2 == BattleMainCB2 && !gPaletteFade.active)
{
if (gPartyMenuUseExitCallback == TRUE)
BtlController_EmitChosenMonReturnValue(BATTLELINKCOMMTYPE_CONTROLLER_TO_ENGINE, gSelectedMonPartyId, gBattlePartyCurrentOrder);
else
BtlController_EmitChosenMonReturnValue(BATTLELINKCOMMTYPE_CONTROLLER_TO_ENGINE, PARTY_SIZE, NULL);
if ((gBattleBufferA[gActiveBattler][1] & 0xF) == 1)
PrintLinkStandbyMsg();
PlayerBufferExecCompleted();
}
}
static void OpenBagAndChooseItem(void)
{
if (!gPaletteFade.active)
{
gBattlerControllerFuncs[gActiveBattler] = CompleteWhenChoseItem;
ReshowBattleScreenDummy();
FreeAllWindowBuffers();
CB2_BagMenuFromBattle();
}
}
static void CompleteWhenChoseItem(void)
{
if (gMain.callback2 == BattleMainCB2 && !gPaletteFade.active)
{
BtlController_EmitOneReturnValue(BATTLELINKCOMMTYPE_CONTROLLER_TO_ENGINE, gSpecialVar_ItemId);
PlayerBufferExecCompleted();
}
}
static void CompleteOnSpecialAnimDone(void)
{
if (!gDoingBattleAnim || !gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].specialAnimActive)
PlayerBufferExecCompleted();
}
static void DoHitAnimBlinkSpriteEffect(void)
{
u8 spriteId = gBattlerSpriteIds[gActiveBattler];
if (gSprites[spriteId].data[1] == 32)
{
gSprites[spriteId].data[1] = 0;
gSprites[spriteId].invisible = FALSE;
gDoingBattleAnim = FALSE;
PlayerBufferExecCompleted();
}
else
{
if ((gSprites[spriteId].data[1] % 4) == 0)
gSprites[spriteId].invisible ^= 1;
gSprites[spriteId].data[1]++;
}
}
static void PlayerHandleYesNoInput(void)
{
if (JOY_NEW(DPAD_UP) && gMultiUsePlayerCursor != 0)
{
PlaySE(SE_SELECT);
BattleDestroyYesNoCursorAt(gMultiUsePlayerCursor);
gMultiUsePlayerCursor = 0;
BattleCreateYesNoCursorAt(0);
}
if (JOY_NEW(DPAD_DOWN) && gMultiUsePlayerCursor == 0)
{
PlaySE(SE_SELECT);
BattleDestroyYesNoCursorAt(gMultiUsePlayerCursor);
gMultiUsePlayerCursor = 1;
BattleCreateYesNoCursorAt(1);
}
if (JOY_NEW(A_BUTTON))
{
HandleBattleWindow(YESNOBOX_X_Y, WINDOW_CLEAR);
PlaySE(SE_SELECT);
if (gMultiUsePlayerCursor != 0)
BtlController_EmitTwoReturnValues(BATTLELINKCOMMTYPE_CONTROLLER_TO_ENGINE, 0xE, 0);
else
BtlController_EmitTwoReturnValues(BATTLELINKCOMMTYPE_CONTROLLER_TO_ENGINE, 0xD, 0);
PlayerBufferExecCompleted();
}
if (JOY_NEW(B_BUTTON))
{
HandleBattleWindow(YESNOBOX_X_Y, WINDOW_CLEAR);
PlaySE(SE_SELECT);
PlayerBufferExecCompleted();
}
}
static void MoveSelectionDisplayMoveNames(void)
{
s32 i;
struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct *)(&gBattleBufferA[gActiveBattler][4]);
gNumberOfMovesToChoose = 0;
for (i = 0; i < MAX_MON_MOVES; i++)
{
MoveSelectionDestroyCursorAt(i);
StringCopy(gDisplayedStringBattle, gMoveNames[moveInfo->moves[i]]);
// Prints on windows B_WIN_MOVE_NAME_1, B_WIN_MOVE_NAME_2, B_WIN_MOVE_NAME_3, B_WIN_MOVE_NAME_4
BattlePutTextOnWindow(gDisplayedStringBattle, i + B_WIN_MOVE_NAME_1);
if (moveInfo->moves[i] != MOVE_NONE)
gNumberOfMovesToChoose++;
}
}
static void MoveSelectionDisplayPpString(void)
{
StringCopy(gDisplayedStringBattle, gText_MoveInterfacePP);
BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_PP);
}
static void MoveSelectionDisplayPpNumber(void)
{
u8 *txtPtr;
struct ChooseMoveStruct *moveInfo;
if (gBattleBufferA[gActiveBattler][2] == TRUE) // check if we didn't want to display pp number
return;
SetPpNumbersPaletteInMoveSelection();
moveInfo = (struct ChooseMoveStruct *)(&gBattleBufferA[gActiveBattler][4]);
txtPtr = ConvertIntToDecimalStringN(gDisplayedStringBattle, moveInfo->currentPp[gMoveSelectionCursor[gActiveBattler]], STR_CONV_MODE_RIGHT_ALIGN, 2);
*(txtPtr)++ = CHAR_SLASH;
ConvertIntToDecimalStringN(txtPtr, moveInfo->maxPp[gMoveSelectionCursor[gActiveBattler]], STR_CONV_MODE_RIGHT_ALIGN, 2);
BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_PP_REMAINING);
}
static void MoveSelectionDisplayMoveType(void)
{
u8 *txtPtr;
struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct *)(&gBattleBufferA[gActiveBattler][4]);
txtPtr = StringCopy(gDisplayedStringBattle, gText_MoveInterfaceType);
*(txtPtr)++ = EXT_CTRL_CODE_BEGIN;
*(txtPtr)++ = EXT_CTRL_CODE_FONT;
*(txtPtr)++ = FONT_NORMAL;
StringCopy(txtPtr, gTypeNames[gBattleMoves[moveInfo->moves[gMoveSelectionCursor[gActiveBattler]]].type]);
BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_MOVE_TYPE);
}
static void MoveSelectionCreateCursorAt(u8 cursorPosition, u8 baseTileNum)
{
u16 src[2];
src[0] = baseTileNum + 1;
src[1] = baseTileNum + 2;
CopyToBgTilemapBufferRect_ChangePalette(0, src, 9 * (cursorPosition & 1) + 1, 55 + (cursorPosition & 2), 1, 2, 0x11);
CopyBgTilemapBufferToVram(0);
}
static void MoveSelectionDestroyCursorAt(u8 cursorPosition)
{
u16 src[2];
src[0] = 0x1016;
src[1] = 0x1016;
CopyToBgTilemapBufferRect_ChangePalette(0, src, 9 * (cursorPosition & 1) + 1, 55 + (cursorPosition & 2), 1, 2, 0x11);
CopyBgTilemapBufferToVram(0);
}
void ActionSelectionCreateCursorAt(u8 cursorPosition, u8 baseTileNum)
{
u16 src[2];
src[0] = 1;
src[1] = 2;
CopyToBgTilemapBufferRect_ChangePalette(0, src, 7 * (cursorPosition & 1) + 16, 35 + (cursorPosition & 2), 1, 2, 0x11);
CopyBgTilemapBufferToVram(0);
}
void ActionSelectionDestroyCursorAt(u8 cursorPosition)
{
u16 src[2];
src[0] = 0x1016;
src[1] = 0x1016;
CopyToBgTilemapBufferRect_ChangePalette(0, src, 7 * (cursorPosition & 1) + 16, 35 + (cursorPosition & 2), 1, 2, 0x11);
CopyBgTilemapBufferToVram(0);
}
void CB2_SetUpReshowBattleScreenAfterMenu(void)
{
SetMainCallback2(ReshowBattleScreenAfterMenu);
}
void CB2_SetUpReshowBattleScreenAfterMenu2(void)
{
SetMainCallback2(ReshowBattleScreenAfterMenu);
}
static void CompleteOnFinishedStatusAnimation(void)
{
if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].statusAnimActive)
PlayerBufferExecCompleted();
}
static void CompleteOnFinishedBattleAnimation(void)
{
if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animFromTableActive)
PlayerBufferExecCompleted();
}
static void PrintLinkStandbyMsg(void)
{
if (gBattleTypeFlags & BATTLE_TYPE_LINK)
{
gBattle_BG0_X = 0;
gBattle_BG0_Y = 0;
BattlePutTextOnWindow(gText_LinkStandby, B_WIN_MSG);
}
}
static void PlayerHandleGetMonData(void)
{
u8 monData[sizeof(struct Pokemon) * 2 + 56]; // this allows to get full data of two Pokémon, trying to get more will result in overwriting data
u32 size = 0;
u8 monToCheck;
s32 i;
if (gBattleBufferA[gActiveBattler][2] == 0)
{
size += CopyPlayerMonData(gBattlerPartyIndexes[gActiveBattler], monData);
}
else
{
monToCheck = gBattleBufferA[gActiveBattler][2];
for (i = 0; i < PARTY_SIZE; i++)
{
if (monToCheck & 1)
size += CopyPlayerMonData(i, monData + size);
monToCheck >>= 1;
}
}
BtlController_EmitDataTransfer(BATTLELINKCOMMTYPE_CONTROLLER_TO_ENGINE, size, monData);
PlayerBufferExecCompleted();
}
static u32 CopyPlayerMonData(u8 monId, u8 *dst)
{
struct BattlePokemon battleMon;
struct MovePpInfo moveData;
u8 nickname[POKEMON_NAME_BUFFER_SIZE];
u8 *src;
s16 data16;
u32 data32;
s32 size = 0;
switch (gBattleBufferA[gActiveBattler][1])
{
case REQUEST_ALL_BATTLE:
battleMon.species = GetMonData(&gPlayerParty[monId], MON_DATA_SPECIES);
battleMon.item = GetMonData(&gPlayerParty[monId], MON_DATA_HELD_ITEM);
for (size = 0; size < MAX_MON_MOVES; size++)
{
battleMon.moves[size] = GetMonData(&gPlayerParty[monId], MON_DATA_MOVE1 + size);
battleMon.pp[size] = GetMonData(&gPlayerParty[monId], MON_DATA_PP1 + size);
}
battleMon.ppBonuses = GetMonData(&gPlayerParty[monId], MON_DATA_PP_BONUSES);
battleMon.friendship = GetMonData(&gPlayerParty[monId], MON_DATA_FRIENDSHIP);
battleMon.experience = GetMonData(&gPlayerParty[monId], MON_DATA_EXP);
battleMon.hpIV = GetMonData(&gPlayerParty[monId], MON_DATA_HP_IV);
battleMon.attackIV = GetMonData(&gPlayerParty[monId], MON_DATA_ATK_IV);
battleMon.defenseIV = GetMonData(&gPlayerParty[monId], MON_DATA_DEF_IV);
battleMon.speedIV = GetMonData(&gPlayerParty[monId], MON_DATA_SPEED_IV);
battleMon.spAttackIV = GetMonData(&gPlayerParty[monId], MON_DATA_SPATK_IV);
battleMon.spDefenseIV = GetMonData(&gPlayerParty[monId], MON_DATA_SPDEF_IV);
battleMon.personality = GetMonData(&gPlayerParty[monId], MON_DATA_PERSONALITY);
battleMon.status1 = GetMonData(&gPlayerParty[monId], MON_DATA_STATUS);
battleMon.level = GetMonData(&gPlayerParty[monId], MON_DATA_LEVEL);
battleMon.hp = GetMonData(&gPlayerParty[monId], MON_DATA_HP);
battleMon.maxHP = GetMonData(&gPlayerParty[monId], MON_DATA_MAX_HP);
battleMon.attack = GetMonData(&gPlayerParty[monId], MON_DATA_ATK);
battleMon.defense = GetMonData(&gPlayerParty[monId], MON_DATA_DEF);
battleMon.speed = GetMonData(&gPlayerParty[monId], MON_DATA_SPEED);
battleMon.spAttack = GetMonData(&gPlayerParty[monId], MON_DATA_SPATK);
battleMon.spDefense = GetMonData(&gPlayerParty[monId], MON_DATA_SPDEF);
battleMon.isEgg = GetMonData(&gPlayerParty[monId], MON_DATA_IS_EGG);
battleMon.abilityNum = GetMonData(&gPlayerParty[monId], MON_DATA_ABILITY_NUM);
battleMon.otId = GetMonData(&gPlayerParty[monId], MON_DATA_OT_ID);
GetMonData(&gPlayerParty[monId], MON_DATA_NICKNAME, nickname);
StringCopy_Nickname(battleMon.nickname, nickname);
GetMonData(&gPlayerParty[monId], MON_DATA_OT_NAME, battleMon.otName);
src = (u8 *)&battleMon;
for (size = 0; size < sizeof(battleMon); size++)
dst[size] = src[size];
break;
case REQUEST_SPECIES_BATTLE:
data16 = GetMonData(&gPlayerParty[monId], MON_DATA_SPECIES);
dst[0] = data16;
dst[1] = data16 >> 8;
size = 2;
break;
case REQUEST_HELDITEM_BATTLE:
data16 = GetMonData(&gPlayerParty[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(&gPlayerParty[monId], MON_DATA_MOVE1 + size);
moveData.pp[size] = GetMonData(&gPlayerParty[monId], MON_DATA_PP1 + size);
}
moveData.ppBonuses = GetMonData(&gPlayerParty[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(&gPlayerParty[monId], MON_DATA_MOVE1 + gBattleBufferA[gActiveBattler][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(&gPlayerParty[monId], MON_DATA_PP1 + size);
dst[size] = GetMonData(&gPlayerParty[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(&gPlayerParty[monId], MON_DATA_PP1 + gBattleBufferA[gActiveBattler][1] - REQUEST_PPMOVE1_BATTLE);
size = 1;
break;
case REQUEST_OTID_BATTLE:
data32 = GetMonData(&gPlayerParty[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(&gPlayerParty[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(&gPlayerParty[monId], MON_DATA_HP_EV);
size = 1;
break;
case REQUEST_ATK_EV_BATTLE:
dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_ATK_EV);
size = 1;
break;
case REQUEST_DEF_EV_BATTLE:
dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_DEF_EV);
size = 1;
break;
case REQUEST_SPEED_EV_BATTLE:
dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_SPEED_EV);
size = 1;
break;
case REQUEST_SPATK_EV_BATTLE:
dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_SPATK_EV);
size = 1;
break;
case REQUEST_SPDEF_EV_BATTLE:
dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_SPDEF_EV);
size = 1;
break;
case REQUEST_FRIENDSHIP_BATTLE:
dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_FRIENDSHIP);
size = 1;
break;
case REQUEST_POKERUS_BATTLE:
dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_POKERUS);
size = 1;
break;
case REQUEST_MET_LOCATION_BATTLE:
dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_MET_LOCATION);
size = 1;
break;
case REQUEST_MET_LEVEL_BATTLE:
dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_MET_LEVEL);
size = 1;
break;
case REQUEST_MET_GAME_BATTLE:
dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_MET_GAME);
size = 1;
break;
case REQUEST_POKEBALL_BATTLE:
dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_POKEBALL);
size = 1;
break;
case REQUEST_ALL_IVS_BATTLE:
dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_HP_IV);
dst[1] = GetMonData(&gPlayerParty[monId], MON_DATA_ATK_IV);
dst[2] = GetMonData(&gPlayerParty[monId], MON_DATA_DEF_IV);
dst[3] = GetMonData(&gPlayerParty[monId], MON_DATA_SPEED_IV);
dst[4] = GetMonData(&gPlayerParty[monId], MON_DATA_SPATK_IV);
dst[5] = GetMonData(&gPlayerParty[monId], MON_DATA_SPDEF_IV);
size = 6;
break;
case REQUEST_HP_IV_BATTLE:
dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_HP_IV);
size = 1;
break;
case REQUEST_ATK_IV_BATTLE:
dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_ATK_IV);
size = 1;
break;
case REQUEST_DEF_IV_BATTLE:
dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_DEF_IV);
size = 1;
break;
case REQUEST_SPEED_IV_BATTLE:
dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_SPEED_IV);
size = 1;
break;
case REQUEST_SPATK_IV_BATTLE:
dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_SPATK_IV);
size = 1;
break;
case REQUEST_SPDEF_IV_BATTLE:
dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_SPDEF_IV);
size = 1;
break;
case REQUEST_PERSONALITY_BATTLE:
data32 = GetMonData(&gPlayerParty[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(&gPlayerParty[monId], MON_DATA_CHECKSUM);
dst[0] = data16;
dst[1] = data16 >> 8;
size = 2;
break;
case REQUEST_STATUS_BATTLE:
data32 = GetMonData(&gPlayerParty[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(&gPlayerParty[monId], MON_DATA_LEVEL);
size = 1;
break;
case REQUEST_HP_BATTLE:
data16 = GetMonData(&gPlayerParty[monId], MON_DATA_HP);
dst[0] = data16;
dst[1] = data16 >> 8;
size = 2;
break;
case REQUEST_MAX_HP_BATTLE:
data16 = GetMonData(&gPlayerParty[monId], MON_DATA_MAX_HP);
dst[0] = data16;
dst[1] = data16 >> 8;
size = 2;
break;
case REQUEST_ATK_BATTLE:
data16 = GetMonData(&gPlayerParty[monId], MON_DATA_ATK);
dst[0] = data16;
dst[1] = data16 >> 8;
size = 2;
break;
case REQUEST_DEF_BATTLE:
data16 = GetMonData(&gPlayerParty[monId], MON_DATA_DEF);
dst[0] = data16;
dst[1] = data16 >> 8;
size = 2;
break;
case REQUEST_SPEED_BATTLE:
data16 = GetMonData(&gPlayerParty[monId], MON_DATA_SPEED);
dst[0] = data16;
dst[1] = data16 >> 8;
size = 2;
break;
case REQUEST_SPATK_BATTLE:
data16 = GetMonData(&gPlayerParty[monId], MON_DATA_SPATK);
dst[0] = data16;
dst[1] = data16 >> 8;
size = 2;
break;
case REQUEST_SPDEF_BATTLE:
data16 = GetMonData(&gPlayerParty[monId], MON_DATA_SPDEF);
dst[0] = data16;
dst[1] = data16 >> 8;
size = 2;
break;
case REQUEST_COOL_BATTLE:
dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_COOL);
size = 1;
break;
case REQUEST_BEAUTY_BATTLE:
dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_BEAUTY);
size = 1;
break;
case REQUEST_CUTE_BATTLE:
dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_CUTE);
size = 1;
break;
case REQUEST_SMART_BATTLE:
dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_SMART);
size = 1;
break;
case REQUEST_TOUGH_BATTLE:
dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_TOUGH);
size = 1;
break;
case REQUEST_SHEEN_BATTLE:
dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_SHEEN);
size = 1;
break;
case REQUEST_COOL_RIBBON_BATTLE:
dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_COOL_RIBBON);
size = 1;
break;
case REQUEST_BEAUTY_RIBBON_BATTLE:
dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_BEAUTY_RIBBON);
size = 1;
break;
case REQUEST_CUTE_RIBBON_BATTLE:
dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_CUTE_RIBBON);
size = 1;
break;
case REQUEST_SMART_RIBBON_BATTLE:
dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_SMART_RIBBON);
size = 1;
break;
case REQUEST_TOUGH_RIBBON_BATTLE:
dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_TOUGH_RIBBON);
size = 1;
break;
}
return size;
}
void PlayerHandleGetRawMonData(void)
{
struct BattlePokemon battleMon;
u8 *src = (u8 *)&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]] + gBattleBufferA[gActiveBattler][1];
u8 *dst = (u8 *)&battleMon + gBattleBufferA[gActiveBattler][1];
u8 i;
for (i = 0; i < gBattleBufferA[gActiveBattler][2]; i++)
dst[i] = src[i];
BtlController_EmitDataTransfer(BATTLELINKCOMMTYPE_CONTROLLER_TO_ENGINE, gBattleBufferA[gActiveBattler][2], dst);
PlayerBufferExecCompleted();
}
static void PlayerHandleSetMonData(void)
{
u8 monToCheck;
u8 i;
if (gBattleBufferA[gActiveBattler][2] == 0)
{
SetPlayerMonData(gBattlerPartyIndexes[gActiveBattler]);
}
else
{
monToCheck = gBattleBufferA[gActiveBattler][2];
for (i = 0; i < PARTY_SIZE; i++)
{
if (monToCheck & 1)
SetPlayerMonData(i);
monToCheck >>= 1;
}
}
PlayerBufferExecCompleted();
}
static void SetPlayerMonData(u8 monId)
{
struct BattlePokemon *battlePokemon = (struct BattlePokemon *)&gBattleBufferA[gActiveBattler][3];
struct MovePpInfo *moveData = (struct MovePpInfo *)&gBattleBufferA[gActiveBattler][3];
s32 i;
switch (gBattleBufferA[gActiveBattler][1])
{
case REQUEST_ALL_BATTLE:
{
u8 iv;
SetMonData(&gPlayerParty[monId], MON_DATA_SPECIES, &battlePokemon->species);
SetMonData(&gPlayerParty[monId], MON_DATA_HELD_ITEM, &battlePokemon->item);
for (i = 0; i < MAX_MON_MOVES; i++)
{
SetMonData(&gPlayerParty[monId], MON_DATA_MOVE1 + i, &battlePokemon->moves[i]);
SetMonData(&gPlayerParty[monId], MON_DATA_PP1 + i, &battlePokemon->pp[i]);
}
SetMonData(&gPlayerParty[monId], MON_DATA_PP_BONUSES, &battlePokemon->ppBonuses);
SetMonData(&gPlayerParty[monId], MON_DATA_FRIENDSHIP, &battlePokemon->friendship);
SetMonData(&gPlayerParty[monId], MON_DATA_EXP, &battlePokemon->experience);
iv = battlePokemon->hpIV;
SetMonData(&gPlayerParty[monId], MON_DATA_HP_IV, &iv);
iv = battlePokemon->attackIV;
SetMonData(&gPlayerParty[monId], MON_DATA_ATK_IV, &iv);
iv = battlePokemon->defenseIV;
SetMonData(&gPlayerParty[monId], MON_DATA_DEF_IV, &iv);
iv = battlePokemon->speedIV;
SetMonData(&gPlayerParty[monId], MON_DATA_SPEED_IV, &iv);
iv = battlePokemon->spAttackIV;
SetMonData(&gPlayerParty[monId], MON_DATA_SPATK_IV, &iv);
iv = battlePokemon->spDefenseIV;
SetMonData(&gPlayerParty[monId], MON_DATA_SPDEF_IV, &iv);
SetMonData(&gPlayerParty[monId], MON_DATA_PERSONALITY, &battlePokemon->personality);
SetMonData(&gPlayerParty[monId], MON_DATA_STATUS, &battlePokemon->status1);
SetMonData(&gPlayerParty[monId], MON_DATA_LEVEL, &battlePokemon->level);
SetMonData(&gPlayerParty[monId], MON_DATA_HP, &battlePokemon->hp);
SetMonData(&gPlayerParty[monId], MON_DATA_MAX_HP, &battlePokemon->maxHP);
SetMonData(&gPlayerParty[monId], MON_DATA_ATK, &battlePokemon->attack);
SetMonData(&gPlayerParty[monId], MON_DATA_DEF, &battlePokemon->defense);
SetMonData(&gPlayerParty[monId], MON_DATA_SPEED, &battlePokemon->speed);
SetMonData(&gPlayerParty[monId], MON_DATA_SPATK, &battlePokemon->spAttack);
SetMonData(&gPlayerParty[monId], MON_DATA_SPDEF, &battlePokemon->spDefense);
}
break;
case REQUEST_SPECIES_BATTLE:
SetMonData(&gPlayerParty[monId], MON_DATA_SPECIES, &gBattleBufferA[gActiveBattler][3]);
break;
case REQUEST_HELDITEM_BATTLE:
SetMonData(&gPlayerParty[monId], MON_DATA_HELD_ITEM, &gBattleBufferA[gActiveBattler][3]);
break;
case REQUEST_MOVES_PP_BATTLE:
for (i = 0; i < MAX_MON_MOVES; i++)
{
SetMonData(&gPlayerParty[monId], MON_DATA_MOVE1 + i, &moveData->moves[i]);
SetMonData(&gPlayerParty[monId], MON_DATA_PP1 + i, &moveData->pp[i]);
}
SetMonData(&gPlayerParty[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(&gPlayerParty[monId], MON_DATA_MOVE1 + gBattleBufferA[gActiveBattler][1] - REQUEST_MOVE1_BATTLE, &gBattleBufferA[gActiveBattler][3]);
break;
case REQUEST_PP_DATA_BATTLE:
SetMonData(&gPlayerParty[monId], MON_DATA_PP1, &gBattleBufferA[gActiveBattler][3]);
SetMonData(&gPlayerParty[monId], MON_DATA_PP2, &gBattleBufferA[gActiveBattler][4]);
SetMonData(&gPlayerParty[monId], MON_DATA_PP3, &gBattleBufferA[gActiveBattler][5]);
SetMonData(&gPlayerParty[monId], MON_DATA_PP4, &gBattleBufferA[gActiveBattler][6]);
SetMonData(&gPlayerParty[monId], MON_DATA_PP_BONUSES, &gBattleBufferA[gActiveBattler][7]);
break;
case REQUEST_PPMOVE1_BATTLE:
case REQUEST_PPMOVE2_BATTLE:
case REQUEST_PPMOVE3_BATTLE:
case REQUEST_PPMOVE4_BATTLE:
SetMonData(&gPlayerParty[monId], MON_DATA_PP1 + gBattleBufferA[gActiveBattler][1] - REQUEST_PPMOVE1_BATTLE, &gBattleBufferA[gActiveBattler][3]);
break;
case REQUEST_OTID_BATTLE:
SetMonData(&gPlayerParty[monId], MON_DATA_OT_ID, &gBattleBufferA[gActiveBattler][3]);
break;
case REQUEST_EXP_BATTLE:
SetMonData(&gPlayerParty[monId], MON_DATA_EXP, &gBattleBufferA[gActiveBattler][3]);
break;
case REQUEST_HP_EV_BATTLE:
SetMonData(&gPlayerParty[monId], MON_DATA_HP_EV, &gBattleBufferA[gActiveBattler][3]);
break;
case REQUEST_ATK_EV_BATTLE:
SetMonData(&gPlayerParty[monId], MON_DATA_ATK_EV, &gBattleBufferA[gActiveBattler][3]);
break;
case REQUEST_DEF_EV_BATTLE:
SetMonData(&gPlayerParty[monId], MON_DATA_DEF_EV, &gBattleBufferA[gActiveBattler][3]);
break;
case REQUEST_SPEED_EV_BATTLE:
SetMonData(&gPlayerParty[monId], MON_DATA_SPEED_EV, &gBattleBufferA[gActiveBattler][3]);
break;
case REQUEST_SPATK_EV_BATTLE:
SetMonData(&gPlayerParty[monId], MON_DATA_SPATK_EV, &gBattleBufferA[gActiveBattler][3]);
break;
case REQUEST_SPDEF_EV_BATTLE:
SetMonData(&gPlayerParty[monId], MON_DATA_SPDEF_EV, &gBattleBufferA[gActiveBattler][3]);
break;
case REQUEST_FRIENDSHIP_BATTLE:
SetMonData(&gPlayerParty[monId], MON_DATA_FRIENDSHIP, &gBattleBufferA[gActiveBattler][3]);
break;
case REQUEST_POKERUS_BATTLE:
SetMonData(&gPlayerParty[monId], MON_DATA_POKERUS, &gBattleBufferA[gActiveBattler][3]);
break;
case REQUEST_MET_LOCATION_BATTLE:
SetMonData(&gPlayerParty[monId], MON_DATA_MET_LOCATION, &gBattleBufferA[gActiveBattler][3]);
break;
case REQUEST_MET_LEVEL_BATTLE:
SetMonData(&gPlayerParty[monId], MON_DATA_MET_LEVEL, &gBattleBufferA[gActiveBattler][3]);
break;
case REQUEST_MET_GAME_BATTLE:
SetMonData(&gPlayerParty[monId], MON_DATA_MET_GAME, &gBattleBufferA[gActiveBattler][3]);
break;
case REQUEST_POKEBALL_BATTLE:
SetMonData(&gPlayerParty[monId], MON_DATA_POKEBALL, &gBattleBufferA[gActiveBattler][3]);
break;
case REQUEST_ALL_IVS_BATTLE:
SetMonData(&gPlayerParty[monId], MON_DATA_HP_IV, &gBattleBufferA[gActiveBattler][3]);
SetMonData(&gPlayerParty[monId], MON_DATA_ATK_IV, &gBattleBufferA[gActiveBattler][4]);
SetMonData(&gPlayerParty[monId], MON_DATA_DEF_IV, &gBattleBufferA[gActiveBattler][5]);
SetMonData(&gPlayerParty[monId], MON_DATA_SPEED_IV, &gBattleBufferA[gActiveBattler][6]);
SetMonData(&gPlayerParty[monId], MON_DATA_SPATK_IV, &gBattleBufferA[gActiveBattler][7]);
SetMonData(&gPlayerParty[monId], MON_DATA_SPDEF_IV, &gBattleBufferA[gActiveBattler][8]);
break;
case REQUEST_HP_IV_BATTLE:
SetMonData(&gPlayerParty[monId], MON_DATA_HP_IV, &gBattleBufferA[gActiveBattler][3]);
break;
case REQUEST_ATK_IV_BATTLE:
SetMonData(&gPlayerParty[monId], MON_DATA_ATK_IV, &gBattleBufferA[gActiveBattler][3]);
break;
case REQUEST_DEF_IV_BATTLE:
SetMonData(&gPlayerParty[monId], MON_DATA_DEF_IV, &gBattleBufferA[gActiveBattler][3]);
break;
case REQUEST_SPEED_IV_BATTLE:
SetMonData(&gPlayerParty[monId], MON_DATA_SPEED_IV, &gBattleBufferA[gActiveBattler][3]);
break;
case REQUEST_SPATK_IV_BATTLE:
SetMonData(&gPlayerParty[monId], MON_DATA_SPATK_IV, &gBattleBufferA[gActiveBattler][3]);
break;
case REQUEST_SPDEF_IV_BATTLE:
SetMonData(&gPlayerParty[monId], MON_DATA_SPDEF_IV, &gBattleBufferA[gActiveBattler][3]);
break;
case REQUEST_PERSONALITY_BATTLE:
SetMonData(&gPlayerParty[monId], MON_DATA_PERSONALITY, &gBattleBufferA[gActiveBattler][3]);
break;
case REQUEST_CHECKSUM_BATTLE:
SetMonData(&gPlayerParty[monId], MON_DATA_CHECKSUM, &gBattleBufferA[gActiveBattler][3]);
break;
case REQUEST_STATUS_BATTLE:
SetMonData(&gPlayerParty[monId], MON_DATA_STATUS, &gBattleBufferA[gActiveBattler][3]);
break;
case REQUEST_LEVEL_BATTLE:
SetMonData(&gPlayerParty[monId], MON_DATA_LEVEL, &gBattleBufferA[gActiveBattler][3]);
break;
case REQUEST_HP_BATTLE:
SetMonData(&gPlayerParty[monId], MON_DATA_HP, &gBattleBufferA[gActiveBattler][3]);
break;
case REQUEST_MAX_HP_BATTLE:
SetMonData(&gPlayerParty[monId], MON_DATA_MAX_HP, &gBattleBufferA[gActiveBattler][3]);
break;
case REQUEST_ATK_BATTLE:
SetMonData(&gPlayerParty[monId], MON_DATA_ATK, &gBattleBufferA[gActiveBattler][3]);
break;
case REQUEST_DEF_BATTLE:
SetMonData(&gPlayerParty[monId], MON_DATA_DEF, &gBattleBufferA[gActiveBattler][3]);
break;
case REQUEST_SPEED_BATTLE:
SetMonData(&gPlayerParty[monId], MON_DATA_SPEED, &gBattleBufferA[gActiveBattler][3]);
break;
case REQUEST_SPATK_BATTLE:
SetMonData(&gPlayerParty[monId], MON_DATA_SPATK, &gBattleBufferA[gActiveBattler][3]);
break;
case REQUEST_SPDEF_BATTLE:
SetMonData(&gPlayerParty[monId], MON_DATA_SPDEF, &gBattleBufferA[gActiveBattler][3]);
break;
case REQUEST_COOL_BATTLE:
SetMonData(&gPlayerParty[monId], MON_DATA_COOL, &gBattleBufferA[gActiveBattler][3]);
break;
case REQUEST_BEAUTY_BATTLE:
SetMonData(&gPlayerParty[monId], MON_DATA_BEAUTY, &gBattleBufferA[gActiveBattler][3]);
break;
case REQUEST_CUTE_BATTLE:
SetMonData(&gPlayerParty[monId], MON_DATA_CUTE, &gBattleBufferA[gActiveBattler][3]);
break;
case REQUEST_SMART_BATTLE:
SetMonData(&gPlayerParty[monId], MON_DATA_SMART, &gBattleBufferA[gActiveBattler][3]);
break;
case REQUEST_TOUGH_BATTLE:
SetMonData(&gPlayerParty[monId], MON_DATA_TOUGH, &gBattleBufferA[gActiveBattler][3]);
break;
case REQUEST_SHEEN_BATTLE:
SetMonData(&gPlayerParty[monId], MON_DATA_SHEEN, &gBattleBufferA[gActiveBattler][3]);
break;
case REQUEST_COOL_RIBBON_BATTLE:
SetMonData(&gPlayerParty[monId], MON_DATA_COOL_RIBBON, &gBattleBufferA[gActiveBattler][3]);
break;
case REQUEST_BEAUTY_RIBBON_BATTLE:
SetMonData(&gPlayerParty[monId], MON_DATA_BEAUTY_RIBBON, &gBattleBufferA[gActiveBattler][3]);
break;
case REQUEST_CUTE_RIBBON_BATTLE:
SetMonData(&gPlayerParty[monId], MON_DATA_CUTE_RIBBON, &gBattleBufferA[gActiveBattler][3]);
break;
case REQUEST_SMART_RIBBON_BATTLE:
SetMonData(&gPlayerParty[monId], MON_DATA_SMART_RIBBON, &gBattleBufferA[gActiveBattler][3]);
break;
case REQUEST_TOUGH_RIBBON_BATTLE:
SetMonData(&gPlayerParty[monId], MON_DATA_TOUGH_RIBBON, &gBattleBufferA[gActiveBattler][3]);
break;
}
HandleLowHpMusicChange(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], gActiveBattler);
}
static void PlayerHandleSetRawMonData(void)
{
u8 *dst = (u8 *)&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]] + gBattleBufferA[gActiveBattler][1];
u8 i;
for (i = 0; i < gBattleBufferA[gActiveBattler][2]; i++)
dst[i] = gBattleBufferA[gActiveBattler][3 + i];
PlayerBufferExecCompleted();
}
static void PlayerHandleLoadMonSprite(void)
{
BattleLoadPlayerMonSpriteGfx(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], gActiveBattler);
gSprites[gBattlerSpriteIds[gActiveBattler]].oam.paletteNum = gActiveBattler;
gBattlerControllerFuncs[gActiveBattler] = CompleteOnBankSpritePosX_0;
}
static void PlayerHandleSwitchInAnim(void)
{
ClearTemporarySpeciesSpriteData(gActiveBattler, gBattleBufferA[gActiveBattler][2]);
gBattlerPartyIndexes[gActiveBattler] = gBattleBufferA[gActiveBattler][1];
BattleLoadPlayerMonSpriteGfx(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], gActiveBattler);
gActionSelectionCursor[gActiveBattler] = 0;
gMoveSelectionCursor[gActiveBattler] = 0;
StartSendOutAnim(gActiveBattler, gBattleBufferA[gActiveBattler][2]);
gBattlerControllerFuncs[gActiveBattler] = SwitchIn_TryShinyAnimShowHealthbox;
}
static void StartSendOutAnim(u8 battlerId, bool8 dontClearSubstituteBit)
{
u16 species;
ClearTemporarySpeciesSpriteData(battlerId, dontClearSubstituteBit);
gBattlerPartyIndexes[battlerId] = gBattleBufferA[battlerId][1];
species = GetMonData(&gPlayerParty[gBattlerPartyIndexes[battlerId]], MON_DATA_SPECIES);
gBattleControllerData[battlerId] = CreateInvisibleSpriteWithCallback(SpriteCB_WaitForBattlerBallReleaseAnim);
SetMultiuseSpriteTemplateToPokemon(species, GetBattlerPosition(battlerId));
gBattlerSpriteIds[battlerId] = CreateSprite(
&gMultiuseSpriteTemplate,
GetBattlerSpriteCoord(battlerId, BATTLER_COORD_X_2),
GetBattlerSpriteDefault_Y(battlerId),
GetBattlerSpriteSubpriority(battlerId));
gSprites[gBattleControllerData[battlerId]].data[1] = gBattlerSpriteIds[battlerId];
gSprites[gBattleControllerData[battlerId]].data[2] = battlerId;
gSprites[gBattlerSpriteIds[battlerId]].data[0] = battlerId;
gSprites[gBattlerSpriteIds[battlerId]].data[2] = species;
gSprites[gBattlerSpriteIds[battlerId]].oam.paletteNum = battlerId;
StartSpriteAnim(&gSprites[gBattlerSpriteIds[battlerId]], gBattleMonForms[battlerId]);
gSprites[gBattlerSpriteIds[battlerId]].invisible = TRUE;
gSprites[gBattlerSpriteIds[battlerId]].callback = SpriteCallbackDummy;
gSprites[gBattleControllerData[battlerId]].data[0] = DoPokeballSendOutAnimation(0, POKEBALL_PLAYER_SENDOUT);
}
static void PlayerHandleReturnMonToBall(void)
{
if (!gBattleBufferA[gActiveBattler][1])
{
gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState = 0;
gBattlerControllerFuncs[gActiveBattler] = DoSwitchOutAnimation;
}
else
{
// Skip animation, just remove battler
FreeSpriteOamMatrix(&gSprites[gBattlerSpriteIds[gActiveBattler]]);
DestroySprite(&gSprites[gBattlerSpriteIds[gActiveBattler]]);
SetHealthboxSpriteInvisible(gHealthboxSpriteIds[gActiveBattler]);
PlayerBufferExecCompleted();
}
}
static void DoSwitchOutAnimation(void)
{
switch (gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState)
{
case 0:
if (gBattleSpritesDataPtr->battlerData[gActiveBattler].behindSubstitute)
InitAndLaunchSpecialAnimation(gActiveBattler, gActiveBattler, gActiveBattler, B_ANIM_SUBSTITUTE_TO_MON);
gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState = 1;
break;
case 1:
if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].specialAnimActive)
{
gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState = 0;
InitAndLaunchSpecialAnimation(gActiveBattler, gActiveBattler, gActiveBattler, B_ANIM_SWITCH_OUT_PLAYER_MON);
gBattlerControllerFuncs[gActiveBattler] = FreeMonSpriteAfterSwitchOutAnim;
}
break;
}
}
#define sSpeedX data[0]
// 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.
static void PlayerHandleDrawTrainerPic(void)
{
s16 xPos, yPos;
u32 trainerPicId;
if (gBattleTypeFlags & BATTLE_TYPE_LINK)
{
if ((gLinkPlayers[GetMultiplayerId()].version & 0xFF) == VERSION_FIRE_RED
|| (gLinkPlayers[GetMultiplayerId()].version & 0xFF) == VERSION_LEAF_GREEN)
{
trainerPicId = gLinkPlayers[GetMultiplayerId()].gender + TRAINER_BACK_PIC_RED;
}
else if ((gLinkPlayers[GetMultiplayerId()].version & 0xFF) == VERSION_RUBY
|| (gLinkPlayers[GetMultiplayerId()].version & 0xFF) == VERSION_SAPPHIRE)
{
trainerPicId = gLinkPlayers[GetMultiplayerId()].gender + TRAINER_BACK_PIC_RUBY_SAPPHIRE_BRENDAN;
}
else
{
trainerPicId = gLinkPlayers[GetMultiplayerId()].gender + TRAINER_BACK_PIC_BRENDAN;
}
}
else
{
trainerPicId = gSaveBlock2Ptr->playerGender;
}
if (gBattleTypeFlags & BATTLE_TYPE_MULTI)
{
if ((GetBattlerPosition(gActiveBattler) & BIT_FLANK) != B_FLANK_LEFT) // Second mon, on the right.
xPos = 90;
else // First mon, on the left.
xPos = 32;
if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER && gPartnerTrainerId != TRAINER_STEVEN_PARTNER)
{
xPos = 90;
yPos = (8 - gTrainerFrontPicCoords[trainerPicId].size) * 4 + 80;
}
else
{
yPos = (8 - gTrainerBackPicCoords[trainerPicId].size) * 4 + 80;
}
}
else
{
xPos = 80;
yPos = (8 - gTrainerBackPicCoords[trainerPicId].size) * 4 + 80;
}
// Use front pic table for any tag battles unless your partner is Steven.
if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER && gPartnerTrainerId != TRAINER_STEVEN_PARTNER)
{
trainerPicId = PlayerGenderToFrontTrainerPicId(gSaveBlock2Ptr->playerGender);
DecompressTrainerFrontPic(trainerPicId, gActiveBattler);
SetMultiuseSpriteTemplateToTrainerFront(trainerPicId, GetBattlerPosition(gActiveBattler));
gBattlerSpriteIds[gActiveBattler] = CreateSprite(&gMultiuseSpriteTemplate, xPos, yPos, GetBattlerSpriteSubpriority(gActiveBattler));
gSprites[gBattlerSpriteIds[gActiveBattler]].oam.paletteNum = IndexOfSpritePaletteTag(gTrainerFrontPicPaletteTable[trainerPicId].tag);
gSprites[gBattlerSpriteIds[gActiveBattler]].x2 = DISPLAY_WIDTH;
gSprites[gBattlerSpriteIds[gActiveBattler]].y2 = 48;
gSprites[gBattlerSpriteIds[gActiveBattler]].sSpeedX = -2;
gSprites[gBattlerSpriteIds[gActiveBattler]].callback = SpriteCB_TrainerSlideIn;
gSprites[gBattlerSpriteIds[gActiveBattler]].oam.affineMode = ST_OAM_AFFINE_OFF;
gSprites[gBattlerSpriteIds[gActiveBattler]].hFlip = 1;
}
// Use the back pic in any other scenario.
else
{
DecompressTrainerBackPic(trainerPicId, gActiveBattler);
SetMultiuseSpriteTemplateToTrainerBack(trainerPicId, GetBattlerPosition(gActiveBattler));
gBattlerSpriteIds[gActiveBattler] = CreateSprite(&gMultiuseSpriteTemplate, xPos, yPos, GetBattlerSpriteSubpriority(gActiveBattler));
gSprites[gBattlerSpriteIds[gActiveBattler]].oam.paletteNum = gActiveBattler;
gSprites[gBattlerSpriteIds[gActiveBattler]].x2 = DISPLAY_WIDTH;
gSprites[gBattlerSpriteIds[gActiveBattler]].sSpeedX = -2;
gSprites[gBattlerSpriteIds[gActiveBattler]].callback = SpriteCB_TrainerSlideIn;
}
gBattlerControllerFuncs[gActiveBattler] = CompleteOnBattlerSpriteCallbackDummy;
}
static void PlayerHandleTrainerSlide(void)
{
u32 trainerPicId;
if (gBattleTypeFlags & BATTLE_TYPE_LINK)
{
if ((gLinkPlayers[GetMultiplayerId()].version & 0xFF) == VERSION_FIRE_RED
|| (gLinkPlayers[GetMultiplayerId()].version & 0xFF) == VERSION_LEAF_GREEN)
{
trainerPicId = gLinkPlayers[GetMultiplayerId()].gender + TRAINER_BACK_PIC_RED;
}
else if ((gLinkPlayers[GetMultiplayerId()].version & 0xFF) == VERSION_RUBY
|| (gLinkPlayers[GetMultiplayerId()].version & 0xFF) == VERSION_SAPPHIRE)
{
trainerPicId = gLinkPlayers[GetMultiplayerId()].gender + TRAINER_BACK_PIC_RUBY_SAPPHIRE_BRENDAN;
}
else
{
trainerPicId = gLinkPlayers[GetMultiplayerId()].gender + TRAINER_BACK_PIC_BRENDAN;
}
}
else
{
trainerPicId = gSaveBlock2Ptr->playerGender + TRAINER_BACK_PIC_BRENDAN;
}
DecompressTrainerBackPic(trainerPicId, gActiveBattler);
SetMultiuseSpriteTemplateToTrainerBack(trainerPicId, GetBattlerPosition(gActiveBattler));
gBattlerSpriteIds[gActiveBattler] = CreateSprite(&gMultiuseSpriteTemplate, 80, (8 - gTrainerBackPicCoords[trainerPicId].size) * 4 + 80, 30);
gSprites[gBattlerSpriteIds[gActiveBattler]].oam.paletteNum = gActiveBattler;
gSprites[gBattlerSpriteIds[gActiveBattler]].x2 = -96;
gSprites[gBattlerSpriteIds[gActiveBattler]].sSpeedX = 2;
gSprites[gBattlerSpriteIds[gActiveBattler]].callback = SpriteCB_TrainerSlideIn;
gBattlerControllerFuncs[gActiveBattler] = CompleteOnBankSpriteCallbackDummy2;
}
#undef sSpeedX
static void PlayerHandleTrainerSlideBack(void)
{
SetSpritePrimaryCoordsFromSecondaryCoords(&gSprites[gBattlerSpriteIds[gActiveBattler]]);
gSprites[gBattlerSpriteIds[gActiveBattler]].data[0] = 50;
gSprites[gBattlerSpriteIds[gActiveBattler]].data[2] = -40;
gSprites[gBattlerSpriteIds[gActiveBattler]].data[4] = gSprites[gBattlerSpriteIds[gActiveBattler]].y;
gSprites[gBattlerSpriteIds[gActiveBattler]].callback = StartAnimLinearTranslation;
StoreSpriteCallbackInData6(&gSprites[gBattlerSpriteIds[gActiveBattler]], SpriteCallbackDummy);
StartSpriteAnim(&gSprites[gBattlerSpriteIds[gActiveBattler]], 1);
gBattlerControllerFuncs[gActiveBattler] = FreeTrainerSpriteAfterSlide;
}
#define sSpeedX data[1]
#define sSpeedY data[2]
static void PlayerHandleFaintAnimation(void)
{
if (gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState == 0)
{
if (gBattleSpritesDataPtr->battlerData[gActiveBattler].behindSubstitute)
InitAndLaunchSpecialAnimation(gActiveBattler, gActiveBattler, gActiveBattler, B_ANIM_SUBSTITUTE_TO_MON);
gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState++;
}
else
{
if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].specialAnimActive)
{
gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState = 0;
HandleLowHpMusicChange(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], gActiveBattler);
PlaySE12WithPanning(SE_FAINT, SOUND_PAN_ATTACKER);
gSprites[gBattlerSpriteIds[gActiveBattler]].sSpeedX = 0;
gSprites[gBattlerSpriteIds[gActiveBattler]].sSpeedY = 5;
gSprites[gBattlerSpriteIds[gActiveBattler]].callback = SpriteCB_FaintSlideAnim;
gBattlerControllerFuncs[gActiveBattler] = FreeMonSpriteAfterFaintAnim;
}
}
}
#undef sSpeedX
#undef sSpeedY
static void PlayerHandlePaletteFade(void)
{
BeginNormalPaletteFade(PALETTES_ALL, 2, 0, 16, RGB_BLACK);
PlayerBufferExecCompleted();
}
static void PlayerHandleSuccessBallThrowAnim(void)
{
gBattleSpritesDataPtr->animationData->ballThrowCaseId = BALL_3_SHAKES_SUCCESS;
gDoingBattleAnim = TRUE;
InitAndLaunchSpecialAnimation(gActiveBattler, gActiveBattler, GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT), B_ANIM_BALL_THROW);
gBattlerControllerFuncs[gActiveBattler] = CompleteOnSpecialAnimDone;
}
static void PlayerHandleBallThrowAnim(void)
{
u8 ballThrowCaseId = gBattleBufferA[gActiveBattler][1];
gBattleSpritesDataPtr->animationData->ballThrowCaseId = ballThrowCaseId;
gDoingBattleAnim = TRUE;
InitAndLaunchSpecialAnimation(gActiveBattler, gActiveBattler, GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT), B_ANIM_BALL_THROW);
gBattlerControllerFuncs[gActiveBattler] = CompleteOnSpecialAnimDone;
}
static void PlayerHandlePause(void)
{
u8 timer = gBattleBufferA[gActiveBattler][1];
while (timer != 0)
timer--;
PlayerBufferExecCompleted();
}
static void PlayerHandleMoveAnimation(void)
{
if (!IsBattleSEPlaying(gActiveBattler))
{
u16 move = gBattleBufferA[gActiveBattler][1] | (gBattleBufferA[gActiveBattler][2] << 8);
gAnimMoveTurn = gBattleBufferA[gActiveBattler][3];
gAnimMovePower = gBattleBufferA[gActiveBattler][4] | (gBattleBufferA[gActiveBattler][5] << 8);
gAnimMoveDmg = gBattleBufferA[gActiveBattler][6] | (gBattleBufferA[gActiveBattler][7] << 8) | (gBattleBufferA[gActiveBattler][8] << 16) | (gBattleBufferA[gActiveBattler][9] << 24);
gAnimFriendship = gBattleBufferA[gActiveBattler][10];
gWeatherMoveAnim = gBattleBufferA[gActiveBattler][12] | (gBattleBufferA[gActiveBattler][13] << 8);
gAnimDisableStructPtr = (struct DisableStruct *)&gBattleBufferA[gActiveBattler][16];
gTransformedPersonalities[gActiveBattler] = gAnimDisableStructPtr->transformedMonPersonality;
if (IsMoveWithoutAnimation(move, gAnimMoveTurn)) // Always returns FALSE.
{
PlayerBufferExecCompleted();
}
else
{
gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState = 0;
gBattlerControllerFuncs[gActiveBattler] = PlayerDoMoveAnimation;
BattleTv_SetDataBasedOnMove(move, gWeatherMoveAnim, gAnimDisableStructPtr);
}
}
}
static void PlayerDoMoveAnimation(void)
{
u16 move = gBattleBufferA[gActiveBattler][1] | (gBattleBufferA[gActiveBattler][2] << 8);
u8 multihit = gBattleBufferA[gActiveBattler][11];
switch (gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState)
{
case 0:
if (gBattleSpritesDataPtr->battlerData[gActiveBattler].behindSubstitute
&& !gBattleSpritesDataPtr->battlerData[gActiveBattler].flag_x8)
{
gBattleSpritesDataPtr->battlerData[gActiveBattler].flag_x8 = 1;
InitAndLaunchSpecialAnimation(gActiveBattler, gActiveBattler, gActiveBattler, B_ANIM_SUBSTITUTE_TO_MON);
}
gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState = 1;
break;
case 1:
if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].specialAnimActive)
{
SetBattlerSpriteAffineMode(ST_OAM_AFFINE_OFF);
DoMoveAnim(move);
gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState = 2;
}
break;
case 2:
gAnimScriptCallback();
if (!gAnimScriptActive)
{
SetBattlerSpriteAffineMode(ST_OAM_AFFINE_NORMAL);
if (gBattleSpritesDataPtr->battlerData[gActiveBattler].behindSubstitute && multihit < 2)
{
InitAndLaunchSpecialAnimation(gActiveBattler, gActiveBattler, gActiveBattler, B_ANIM_MON_TO_SUBSTITUTE);
gBattleSpritesDataPtr->battlerData[gActiveBattler].flag_x8 = 0;
}
gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState = 3;
}
break;
case 3:
if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].specialAnimActive)
{
CopyAllBattleSpritesInvisibilities();
TrySetBehindSubstituteSpriteBit(gActiveBattler, gBattleBufferA[gActiveBattler][1] | (gBattleBufferA[gActiveBattler][2] << 8));
gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState = 0;
PlayerBufferExecCompleted();
}
break;
}
}
static void PlayerHandlePrintString(void)
{
u16 *stringId;
gBattle_BG0_X = 0;
gBattle_BG0_Y = 0;
stringId = (u16 *)(&gBattleBufferA[gActiveBattler][2]);
BufferStringBattle(*stringId);
BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_MSG);
gBattlerControllerFuncs[gActiveBattler] = CompleteOnInactiveTextPrinter2;
BattleTv_SetDataBasedOnString(*stringId);
BattleArena_DeductSkillPoints(gActiveBattler, *stringId);
}
static void PlayerHandlePrintSelectionString(void)
{
if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER)
PlayerHandlePrintString();
else
PlayerBufferExecCompleted();
}
static void HandleChooseActionAfterDma3(void)
{
if (!IsDma3ManagerBusyWithBgCopy())
{
gBattle_BG0_X = 0;
gBattle_BG0_Y = DISPLAY_HEIGHT;
gBattlerControllerFuncs[gActiveBattler] = HandleInputChooseAction;
}
}
static void PlayerHandleChooseAction(void)
{
s32 i;
gBattlerControllerFuncs[gActiveBattler] = HandleChooseActionAfterDma3;
BattleTv_ClearExplosionFaintCause();
BattlePutTextOnWindow(gText_BattleMenu, B_WIN_ACTION_MENU);
for (i = 0; i < 4; i++)
ActionSelectionDestroyCursorAt(i);
ActionSelectionCreateCursorAt(gActionSelectionCursor[gActiveBattler], 0);
BattleStringExpandPlaceholdersToDisplayedString(gText_WhatWillPkmnDo);
BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_ACTION_PROMPT);
}
static void PlayerHandleYesNoBox(void)
{
if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER)
{
HandleBattleWindow(YESNOBOX_X_Y, 0);
BattlePutTextOnWindow(gText_BattleYesNoChoice, B_WIN_YESNO);
gMultiUsePlayerCursor = 1;
BattleCreateYesNoCursorAt(1);
gBattlerControllerFuncs[gActiveBattler] = PlayerHandleYesNoInput;
}
else
{
PlayerBufferExecCompleted();
}
}
static void HandleChooseMoveAfterDma3(void)
{
if (!IsDma3ManagerBusyWithBgCopy())
{
gBattle_BG0_X = 0;
gBattle_BG0_Y = DISPLAY_HEIGHT * 2;
gBattlerControllerFuncs[gActiveBattler] = HandleInputChooseMove;
}
}
// arenaMindPoints is used here as a placeholder for a timer.
static void PlayerChooseMoveInBattlePalace(void)
{
if (--*(gBattleStruct->arenaMindPoints + gActiveBattler) == 0)
{
gBattlePalaceMoveSelectionRngValue = gRngValue;
BtlController_EmitTwoReturnValues(BATTLELINKCOMMTYPE_CONTROLLER_TO_ENGINE, 10, ChooseMoveAndTargetInBattlePalace());
PlayerBufferExecCompleted();
}
}
static void PlayerHandleChooseMove(void)
{
if (gBattleTypeFlags & BATTLE_TYPE_PALACE)
{
*(gBattleStruct->arenaMindPoints + gActiveBattler) = 8;
gBattlerControllerFuncs[gActiveBattler] = PlayerChooseMoveInBattlePalace;
}
else
{
InitMoveSelectionsVarsAndStrings();
gBattlerControllerFuncs[gActiveBattler] = HandleChooseMoveAfterDma3;
}
}
void InitMoveSelectionsVarsAndStrings(void)
{
MoveSelectionDisplayMoveNames();
gMultiUsePlayerCursor = 0xFF;
MoveSelectionCreateCursorAt(gMoveSelectionCursor[gActiveBattler], 0);
MoveSelectionDisplayPpString();
MoveSelectionDisplayPpNumber();
MoveSelectionDisplayMoveType();
}
static void PlayerHandleChooseItem(void)
{
s32 i;
BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 0x10, RGB_BLACK);
gBattlerControllerFuncs[gActiveBattler] = OpenBagAndChooseItem;
gBattlerInMenuId = gActiveBattler;
for (i = 0; i < (int)ARRAY_COUNT(gBattlePartyCurrentOrder); i++)
gBattlePartyCurrentOrder[i] = gBattleBufferA[gActiveBattler][1 + i];
}
static void PlayerHandleChoosePokemon(void)
{
s32 i;
for (i = 0; i < (int)ARRAY_COUNT(gBattlePartyCurrentOrder); i++)
gBattlePartyCurrentOrder[i] = gBattleBufferA[gActiveBattler][4 + i];
if (gBattleTypeFlags & BATTLE_TYPE_ARENA && (gBattleBufferA[gActiveBattler][1] & 0xF) != PARTY_ACTION_CANT_SWITCH)
{
BtlController_EmitChosenMonReturnValue(BATTLELINKCOMMTYPE_CONTROLLER_TO_ENGINE, gBattlerPartyIndexes[gActiveBattler] + 1, gBattlePartyCurrentOrder);
PlayerBufferExecCompleted();
}
else
{
gBattleControllerData[gActiveBattler] = CreateTask(TaskDummy, 0xFF);
gTasks[gBattleControllerData[gActiveBattler]].data[0] = gBattleBufferA[gActiveBattler][1] & 0xF;
*(&gBattleStruct->battlerPreventingSwitchout) = gBattleBufferA[gActiveBattler][1] >> 4;
*(&gBattleStruct->prevSelectedPartySlot) = gBattleBufferA[gActiveBattler][2];
*(&gBattleStruct->abilityPreventingSwitchout) = gBattleBufferA[gActiveBattler][3];
BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 0x10, RGB_BLACK);
gBattlerControllerFuncs[gActiveBattler] = OpenPartyMenuToChooseMon;
gBattlerInMenuId = gActiveBattler;
}
}
static void PlayerHandleCmd23(void)
{
BattleStopLowHpSound();
BeginNormalPaletteFade(PALETTES_ALL, 2, 0, 16, RGB_BLACK);
PlayerBufferExecCompleted();
}
static void PlayerHandleHealthBarUpdate(void)
{
s16 hpVal;
LoadBattleBarGfx(0);
hpVal = gBattleBufferA[gActiveBattler][2] | (gBattleBufferA[gActiveBattler][3] << 8);
// gPlayerPartyLostHP used by Battle Dome, but never read
if (hpVal > 0)
gPlayerPartyLostHP += hpVal;
if (hpVal != INSTANT_HP_BAR_DROP)
{
u32 maxHP = GetMonData(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_MAX_HP);
u32 curHP = GetMonData(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_HP);
SetBattleBarStruct(gActiveBattler, gHealthboxSpriteIds[gActiveBattler], maxHP, curHP, hpVal);
}
else
{
u32 maxHP = GetMonData(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_MAX_HP);
SetBattleBarStruct(gActiveBattler, gHealthboxSpriteIds[gActiveBattler], maxHP, 0, hpVal);
UpdateHpTextInHealthbox(gHealthboxSpriteIds[gActiveBattler], 0, HP_CURRENT);
}
gBattlerControllerFuncs[gActiveBattler] = CompleteOnHealthbarDone;
}
static void PlayerHandleExpUpdate(void)
{
u8 monId = gBattleBufferA[gActiveBattler][1];
if (GetMonData(&gPlayerParty[monId], MON_DATA_LEVEL) >= MAX_LEVEL)
{
PlayerBufferExecCompleted();
}
else
{
s16 expPointsToGive;
u8 taskId;
LoadBattleBarGfx(1);
GetMonData(&gPlayerParty[monId], MON_DATA_SPECIES); // Unused return value.
expPointsToGive = T1_READ_16(&gBattleBufferA[gActiveBattler][2]);
taskId = CreateTask(Task_GiveExpToMon, 10);
gTasks[taskId].tExpTask_monId = monId;
gTasks[taskId].tExpTask_gainedExp = expPointsToGive;
gTasks[taskId].tExpTask_battler = gActiveBattler;
gBattlerControllerFuncs[gActiveBattler] = BattleControllerDummy;
}
}
#undef tExpTask_monId
#undef tExpTask_gainedExp
#undef tExpTask_battler
#undef tExpTask_frames
static void PlayerHandleStatusIconUpdate(void)
{
if (!IsBattleSEPlaying(gActiveBattler))
{
u8 battlerId;
UpdateHealthboxAttribute(gHealthboxSpriteIds[gActiveBattler], &gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], HEALTHBOX_STATUS_ICON);
battlerId = gActiveBattler;
gBattleSpritesDataPtr->healthBoxesData[battlerId].statusAnimActive = 0;
gBattlerControllerFuncs[gActiveBattler] = CompleteOnFinishedStatusAnimation;
}
}
static void PlayerHandleStatusAnimation(void)
{
if (!IsBattleSEPlaying(gActiveBattler))
{
InitAndLaunchChosenStatusAnimation(gBattleBufferA[gActiveBattler][1],
gBattleBufferA[gActiveBattler][2] | (gBattleBufferA[gActiveBattler][3] << 8) | (gBattleBufferA[gActiveBattler][4] << 16) | (gBattleBufferA[gActiveBattler][5] << 24));
gBattlerControllerFuncs[gActiveBattler] = CompleteOnFinishedStatusAnimation;
}
}
static void PlayerHandleStatusXor(void)
{
u8 val = GetMonData(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_STATUS) ^ gBattleBufferA[gActiveBattler][1];
SetMonData(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_STATUS, &val);
PlayerBufferExecCompleted();
}
static void PlayerHandleDataTransfer(void)
{
PlayerBufferExecCompleted();
}
static void PlayerHandleDMA3Transfer(void)
{
u32 dstArg = gBattleBufferA[gActiveBattler][1]
| (gBattleBufferA[gActiveBattler][2] << 8)
| (gBattleBufferA[gActiveBattler][3] << 16)
| (gBattleBufferA[gActiveBattler][4] << 24);
u16 sizeArg = gBattleBufferA[gActiveBattler][5] | (gBattleBufferA[gActiveBattler][6] << 8);
const u8 *src = &gBattleBufferA[gActiveBattler][7];
u8 *dst = (u8 *)(dstArg);
u32 size = sizeArg;
while (1)
{
if (size <= 0x1000)
{
DmaCopy16(3, src, dst, size);
break;
}
DmaCopy16(3, src, dst, 0x1000);
src += 0x1000;
dst += 0x1000;
size -= 0x1000;
}
PlayerBufferExecCompleted();
}
static void PlayerHandlePlayBGM(void)
{
PlayBGM(gBattleBufferA[gActiveBattler][1] | (gBattleBufferA[gActiveBattler][2] << 8));
PlayerBufferExecCompleted();
}
static void PlayerHandleCmd32(void)
{
PlayerBufferExecCompleted();
}
static void PlayerHandleTwoReturnValues(void)
{
BtlController_EmitTwoReturnValues(BATTLELINKCOMMTYPE_CONTROLLER_TO_ENGINE, 0, 0);
PlayerBufferExecCompleted();
}
static void PlayerHandleChosenMonReturnValue(void)
{
BtlController_EmitChosenMonReturnValue(BATTLELINKCOMMTYPE_CONTROLLER_TO_ENGINE, 0, NULL);
PlayerBufferExecCompleted();
}
static void PlayerHandleOneReturnValue(void)
{
BtlController_EmitOneReturnValue(BATTLELINKCOMMTYPE_CONTROLLER_TO_ENGINE, 0);
PlayerBufferExecCompleted();
}
static void PlayerHandleOneReturnValue_Duplicate(void)
{
BtlController_EmitOneReturnValue_Duplicate(BATTLELINKCOMMTYPE_CONTROLLER_TO_ENGINE, 0);
PlayerBufferExecCompleted();
}
static void PlayerHandleClearUnkVar(void)
{
gUnusedControllerStruct.unk = 0;
PlayerBufferExecCompleted();
}
static void PlayerHandleSetUnkVar(void)
{
gUnusedControllerStruct.unk = gBattleBufferA[gActiveBattler][1];
PlayerBufferExecCompleted();
}
static void PlayerHandleClearUnkFlag(void)
{
gUnusedControllerStruct.flag = 0;
PlayerBufferExecCompleted();
}
static void PlayerHandleToggleUnkFlag(void)
{
gUnusedControllerStruct.flag ^= 1;
PlayerBufferExecCompleted();
}
static void PlayerHandleHitAnimation(void)
{
if (gSprites[gBattlerSpriteIds[gActiveBattler]].invisible == TRUE)
{
PlayerBufferExecCompleted();
}
else
{
gDoingBattleAnim = TRUE;
gSprites[gBattlerSpriteIds[gActiveBattler]].data[1] = 0;
DoHitAnimHealthboxEffect(gActiveBattler);
gBattlerControllerFuncs[gActiveBattler] = DoHitAnimBlinkSpriteEffect;
}
}
static void PlayerHandleCantSwitch(void)
{
PlayerBufferExecCompleted();
}
static void PlayerHandlePlaySE(void)
{
s8 pan;
if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER)
pan = SOUND_PAN_ATTACKER;
else
pan = SOUND_PAN_TARGET;
PlaySE12WithPanning(gBattleBufferA[gActiveBattler][1] | (gBattleBufferA[gActiveBattler][2] << 8), pan);
PlayerBufferExecCompleted();
}
static void PlayerHandlePlayFanfareOrBGM(void)
{
if (gBattleBufferA[gActiveBattler][3])
{
BattleStopLowHpSound();
PlayBGM(gBattleBufferA[gActiveBattler][1] | (gBattleBufferA[gActiveBattler][2] << 8));
}
else
{
PlayFanfare(gBattleBufferA[gActiveBattler][1] | (gBattleBufferA[gActiveBattler][2] << 8));
}
PlayerBufferExecCompleted();
}
static void PlayerHandleFaintingCry(void)
{
u16 species = GetMonData(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_SPECIES);
PlayCry_ByMode(species, -25, CRY_MODE_FAINT);
PlayerBufferExecCompleted();
}
static void PlayerHandleIntroSlide(void)
{
HandleIntroSlide(gBattleBufferA[gActiveBattler][1]);
gIntroSlideFlags |= 1;
PlayerBufferExecCompleted();
}
// Task data for Task_StartSendOutAnim
#define tBattlerId data[0]
#define tStartTimer data[1]
#define sBattlerId data[5]
static void PlayerHandleIntroTrainerBallThrow(void)
{
u8 paletteNum;
u8 taskId;
SetSpritePrimaryCoordsFromSecondaryCoords(&gSprites[gBattlerSpriteIds[gActiveBattler]]);
gSprites[gBattlerSpriteIds[gActiveBattler]].data[0] = 50;
gSprites[gBattlerSpriteIds[gActiveBattler]].data[2] = -40;
gSprites[gBattlerSpriteIds[gActiveBattler]].data[4] = gSprites[gBattlerSpriteIds[gActiveBattler]].y;
gSprites[gBattlerSpriteIds[gActiveBattler]].callback = StartAnimLinearTranslation;
gSprites[gBattlerSpriteIds[gActiveBattler]].sBattlerId = gActiveBattler;
StoreSpriteCallbackInData6(&gSprites[gBattlerSpriteIds[gActiveBattler]], SpriteCB_FreePlayerSpriteLoadMonSprite);
StartSpriteAnim(&gSprites[gBattlerSpriteIds[gActiveBattler]], 1);
paletteNum = AllocSpritePalette(0xD6F8);
LoadCompressedPalette(gTrainerBackPicPaletteTable[gSaveBlock2Ptr->playerGender].data, OBJ_PLTT_ID(paletteNum), PLTT_SIZE_4BPP);
gSprites[gBattlerSpriteIds[gActiveBattler]].oam.paletteNum = paletteNum;
taskId = CreateTask(Task_StartSendOutAnim, 5);
gTasks[taskId].tBattlerId = gActiveBattler;
if (gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].partyStatusSummaryShown)
gTasks[gBattlerStatusSummaryTaskId[gActiveBattler]].func = Task_HidePartyStatusSummary;
gBattleSpritesDataPtr->animationData->introAnimActive = TRUE;
gBattlerControllerFuncs[gActiveBattler] = BattleControllerDummy;
}
void SpriteCB_FreePlayerSpriteLoadMonSprite(struct Sprite *sprite)
{
u8 battlerId = sprite->sBattlerId;
// Free player trainer sprite
FreeSpriteOamMatrix(sprite);
FreeSpritePaletteByTag(GetSpritePaletteTagByPaletteNum(sprite->oam.paletteNum));
DestroySprite(sprite);
// Load mon sprite
BattleLoadPlayerMonSpriteGfx(&gPlayerParty[gBattlerPartyIndexes[battlerId]], battlerId);
StartSpriteAnim(&gSprites[gBattlerSpriteIds[battlerId]], 0);
}
#undef sBattlerId
// Send out at start of battle
static void Task_StartSendOutAnim(u8 taskId)
{
if (gTasks[taskId].tStartTimer < 31)
{
gTasks[taskId].tStartTimer++;
}
else
{
u8 savedActiveBattler = gActiveBattler;
gActiveBattler = gTasks[taskId].tBattlerId;
if (!IsDoubleBattle() || (gBattleTypeFlags & BATTLE_TYPE_MULTI))
{
gBattleBufferA[gActiveBattler][1] = gBattlerPartyIndexes[gActiveBattler];
StartSendOutAnim(gActiveBattler, FALSE);
}
else
{
gBattleBufferA[gActiveBattler][1] = gBattlerPartyIndexes[gActiveBattler];
StartSendOutAnim(gActiveBattler, FALSE);
gActiveBattler ^= BIT_FLANK;
gBattleBufferA[gActiveBattler][1] = gBattlerPartyIndexes[gActiveBattler];
BattleLoadPlayerMonSpriteGfx(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], gActiveBattler);
StartSendOutAnim(gActiveBattler, FALSE);
gActiveBattler ^= BIT_FLANK;
}
gBattlerControllerFuncs[gActiveBattler] = Intro_TryShinyAnimShowHealthbox;
gActiveBattler = savedActiveBattler;
DestroyTask(taskId);
}
}
#undef tBattlerId
#undef tStartTimer
static void PlayerHandleDrawPartyStatusSummary(void)
{
if (gBattleBufferA[gActiveBattler][1] != 0 && GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER)
{
PlayerBufferExecCompleted();
}
else
{
gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].partyStatusSummaryShown = 1;
gBattlerStatusSummaryTaskId[gActiveBattler] = CreatePartyStatusSummarySprites(gActiveBattler, (struct HpAndStatus *)&gBattleBufferA[gActiveBattler][4], gBattleBufferA[gActiveBattler][1], gBattleBufferA[gActiveBattler][2]);
gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].partyStatusDelayTimer = 0;
// If intro, skip the delay after drawing
if (gBattleBufferA[gActiveBattler][2] != 0)
gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].partyStatusDelayTimer = 93;
gBattlerControllerFuncs[gActiveBattler] = EndDrawPartyStatusSummary;
}
}
static void EndDrawPartyStatusSummary(void)
{
if (gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].partyStatusDelayTimer++ > 92)
{
gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].partyStatusDelayTimer = 0;
PlayerBufferExecCompleted();
}
}
static void PlayerHandleHidePartyStatusSummary(void)
{
if (gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].partyStatusSummaryShown)
gTasks[gBattlerStatusSummaryTaskId[gActiveBattler]].func = Task_HidePartyStatusSummary;
PlayerBufferExecCompleted();
}
static void PlayerHandleEndBounceEffect(void)
{
EndBounceEffect(gActiveBattler, BOUNCE_HEALTHBOX);
EndBounceEffect(gActiveBattler, BOUNCE_MON);
PlayerBufferExecCompleted();
}
static void PlayerHandleSpriteInvisibility(void)
{
if (IsBattlerSpritePresent(gActiveBattler))
{
gSprites[gBattlerSpriteIds[gActiveBattler]].invisible = gBattleBufferA[gActiveBattler][1];
CopyBattleSpriteInvisibility(gActiveBattler);
}
PlayerBufferExecCompleted();
}
static void PlayerHandleBattleAnimation(void)
{
if (!IsBattleSEPlaying(gActiveBattler))
{
u8 animationId = gBattleBufferA[gActiveBattler][1];
u16 argument = gBattleBufferA[gActiveBattler][2] | (gBattleBufferA[gActiveBattler][3] << 8);
if (TryHandleLaunchBattleTableAnimation(gActiveBattler, gActiveBattler, gActiveBattler, animationId, argument))
PlayerBufferExecCompleted();
else
gBattlerControllerFuncs[gActiveBattler] = CompleteOnFinishedBattleAnimation;
BattleTv_SetDataBasedOnAnimation(animationId);
}
}
static void PlayerHandleLinkStandbyMsg(void)
{
RecordedBattle_RecordAllBattlerData(&gBattleBufferA[gActiveBattler][2]);
switch (gBattleBufferA[gActiveBattler][1])
{
case LINK_STANDBY_MSG_STOP_BOUNCE:
PrintLinkStandbyMsg();
// fall through
case LINK_STANDBY_STOP_BOUNCE_ONLY:
EndBounceEffect(gActiveBattler, BOUNCE_HEALTHBOX);
EndBounceEffect(gActiveBattler, BOUNCE_MON);
break;
case LINK_STANDBY_MSG_ONLY:
PrintLinkStandbyMsg();
break;
}
PlayerBufferExecCompleted();
}
static void PlayerHandleResetActionMoveSelection(void)
{
switch (gBattleBufferA[gActiveBattler][1])
{
case RESET_ACTION_MOVE_SELECTION:
gActionSelectionCursor[gActiveBattler] = 0;
gMoveSelectionCursor[gActiveBattler] = 0;
break;
case RESET_ACTION_SELECTION:
gActionSelectionCursor[gActiveBattler] = 0;
break;
case RESET_MOVE_SELECTION:
gMoveSelectionCursor[gActiveBattler] = 0;
break;
}
PlayerBufferExecCompleted();
}
static void PlayerHandleEndLinkBattle(void)
{
RecordedBattle_RecordAllBattlerData(&gBattleBufferA[gActiveBattler][4]);
gBattleOutcome = gBattleBufferA[gActiveBattler][1];
gSaveBlock2Ptr->frontier.disableRecordBattle = gBattleBufferA[gActiveBattler][2];
FadeOutMapMusic(5);
BeginFastPaletteFade(3);
PlayerBufferExecCompleted();
gBattlerControllerFuncs[gActiveBattler] = SetBattleEndCallbacks;
}
static void PlayerCmdEnd(void)
{
}