#include "global.h" #include "battle.h" #include "battle_anim.h" #include "battle_controllers.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 "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/songs.h" #include "constants/trainers.h" #include "constants/rgb.h" static void WallyHandleDrawTrainerPic(u32 battler); static void WallyHandleTrainerSlide(u32 battler); static void WallyHandleSuccessBallThrowAnim(u32 battler); static void WallyHandleBallThrowAnim(u32 battler); static void WallyHandleMoveAnimation(u32 battler); static void WallyHandlePrintString(u32 battler); static void WallyHandlePrintSelectionString(u32 battler); static void WallyHandleChooseAction(u32 battler); static void WallyHandleChooseMove(u32 battler); static void WallyHandleChooseItem(u32 battler); static void WallyHandleHealthBarUpdate(u32 battler); static void WallyHandlePlaySE(u32 battler); static void WallyHandleFaintingCry(u32 battler); static void WallyHandleIntroTrainerBallThrow(u32 battler); static void WallyHandleDrawPartyStatusSummary(u32 battler); static void WallyHandleBattleAnimation(u32 battler); static void WallyHandleEndLinkBattle(u32 battler); static void WallyBufferRunCommand(u32 battler); static void WallyBufferExecCompleted(u32 battler); static void CompleteOnChosenItem(u32 battler); static void Intro_WaitForShinyAnimAndHealthbox(u32 battler); static void (*const sWallyBufferCommands[CONTROLLER_CMDS_COUNT])(u32 battler) = { [CONTROLLER_GETMONDATA] = BtlController_HandleGetMonData, [CONTROLLER_GETRAWMONDATA] = BtlController_HandleGetRawMonData, [CONTROLLER_SETMONDATA] = BtlController_HandleSetMonData, [CONTROLLER_SETRAWMONDATA] = BtlController_Empty, [CONTROLLER_LOADMONSPRITE] = BtlController_Empty, [CONTROLLER_SWITCHINANIM] = BtlController_Empty, [CONTROLLER_RETURNMONTOBALL] = BtlController_HandleReturnMonToBall, [CONTROLLER_DRAWTRAINERPIC] = WallyHandleDrawTrainerPic, [CONTROLLER_TRAINERSLIDE] = WallyHandleTrainerSlide, [CONTROLLER_TRAINERSLIDEBACK] = BtlController_Empty, [CONTROLLER_FAINTANIMATION] = BtlController_Empty, [CONTROLLER_PALETTEFADE] = BtlController_Empty, [CONTROLLER_SUCCESSBALLTHROWANIM] = WallyHandleSuccessBallThrowAnim, [CONTROLLER_BALLTHROWANIM] = WallyHandleBallThrowAnim, [CONTROLLER_PAUSE] = BtlController_Empty, [CONTROLLER_MOVEANIMATION] = WallyHandleMoveAnimation, [CONTROLLER_PRINTSTRING] = WallyHandlePrintString, [CONTROLLER_PRINTSTRINGPLAYERONLY] = WallyHandlePrintSelectionString, [CONTROLLER_CHOOSEACTION] = WallyHandleChooseAction, [CONTROLLER_YESNOBOX] = BtlController_Empty, [CONTROLLER_CHOOSEMOVE] = WallyHandleChooseMove, [CONTROLLER_OPENBAG] = WallyHandleChooseItem, [CONTROLLER_CHOOSEPOKEMON] = BtlController_Empty, [CONTROLLER_23] = BtlController_Empty, [CONTROLLER_HEALTHBARUPDATE] = WallyHandleHealthBarUpdate, [CONTROLLER_EXPUPDATE] = BtlController_Empty, [CONTROLLER_STATUSICONUPDATE] = BtlController_Empty, [CONTROLLER_STATUSANIMATION] = BtlController_Empty, [CONTROLLER_STATUSXOR] = BtlController_Empty, [CONTROLLER_DATATRANSFER] = BtlController_Empty, [CONTROLLER_DMA3TRANSFER] = BtlController_Empty, [CONTROLLER_PLAYBGM] = BtlController_Empty, [CONTROLLER_32] = BtlController_Empty, [CONTROLLER_TWORETURNVALUES] = BtlController_Empty, [CONTROLLER_CHOSENMONRETURNVALUE] = BtlController_Empty, [CONTROLLER_ONERETURNVALUE] = BtlController_Empty, [CONTROLLER_ONERETURNVALUE_DUPLICATE] = BtlController_Empty, [CONTROLLER_HITANIMATION] = BtlController_HandleHitAnimation, [CONTROLLER_CANTSWITCH] = BtlController_Empty, [CONTROLLER_PLAYSE] = WallyHandlePlaySE, [CONTROLLER_PLAYFANFAREORBGM] = BtlController_HandlePlayFanfareOrBGM, [CONTROLLER_FAINTINGCRY] = WallyHandleFaintingCry, [CONTROLLER_INTROSLIDE] = BtlController_HandleIntroSlide, [CONTROLLER_INTROTRAINERBALLTHROW] = WallyHandleIntroTrainerBallThrow, [CONTROLLER_DRAWPARTYSTATUSSUMMARY] = WallyHandleDrawPartyStatusSummary, [CONTROLLER_HIDEPARTYSTATUSSUMMARY] = BtlController_Empty, [CONTROLLER_ENDBOUNCE] = BtlController_Empty, [CONTROLLER_SPRITEINVISIBILITY] = BtlController_Empty, [CONTROLLER_BATTLEANIMATION] = WallyHandleBattleAnimation, [CONTROLLER_LINKSTANDBYMSG] = BtlController_Empty, [CONTROLLER_RESETACTIONMOVESELECTION] = BtlController_Empty, [CONTROLLER_ENDLINKBATTLE] = WallyHandleEndLinkBattle, [CONTROLLER_DEBUGMENU] = BtlController_Empty, [CONTROLLER_TERMINATOR_NOP] = BtlController_TerminatorNop }; void SetControllerToWally(u32 battler) { gBattlerControllerEndFuncs[battler] = WallyBufferExecCompleted; gBattlerControllerFuncs[battler] = WallyBufferRunCommand; gBattleStruct->wallyBattleState = 0; gBattleStruct->wallyMovesState = 0; gBattleStruct->wallyWaitFrames = 0; gBattleStruct->wallyMoveFrames = 0; } static void WallyBufferRunCommand(u32 battler) { if (IsBattleControllerActiveOnLocal(battler)) { if (gBattleResources->bufferA[battler][0] < ARRAY_COUNT(sWallyBufferCommands)) sWallyBufferCommands[gBattleResources->bufferA[battler][0]](battler); else WallyBufferExecCompleted(battler); } } static void WallyHandleActions(u32 battler) { switch (gBattleStruct->wallyBattleState) { case 0: gBattleStruct->wallyWaitFrames = B_WAIT_TIME_LONG; gBattleStruct->wallyBattleState++; case 1: if (--gBattleStruct->wallyWaitFrames == 0) { PlaySE(SE_SELECT); BtlController_EmitTwoReturnValues(battler, B_COMM_TO_ENGINE, B_ACTION_USE_MOVE, 0); WallyBufferExecCompleted(battler); gBattleStruct->wallyBattleState++; gBattleStruct->wallyMovesState = 0; gBattleStruct->wallyWaitFrames = B_WAIT_TIME_LONG; } break; case 2: if (--gBattleStruct->wallyWaitFrames == 0) { PlaySE(SE_SELECT); BtlController_EmitTwoReturnValues(battler, B_COMM_TO_ENGINE, B_ACTION_USE_MOVE, 0); WallyBufferExecCompleted(battler); gBattleStruct->wallyBattleState++; gBattleStruct->wallyMovesState = 0; gBattleStruct->wallyWaitFrames = B_WAIT_TIME_LONG; } break; case 3: if (--gBattleStruct->wallyWaitFrames == 0) { BtlController_EmitTwoReturnValues(battler, B_COMM_TO_ENGINE, B_ACTION_WALLY_THROW, 0); WallyBufferExecCompleted(battler); gBattleStruct->wallyBattleState++; gBattleStruct->wallyMovesState = 0; gBattleStruct->wallyWaitFrames = B_WAIT_TIME_LONG; } break; case 4: if (--gBattleStruct->wallyWaitFrames == 0) { PlaySE(SE_SELECT); ActionSelectionDestroyCursorAt(0); ActionSelectionCreateCursorAt(1, 0); gBattleStruct->wallyWaitFrames = B_WAIT_TIME_LONG; gBattleStruct->wallyBattleState++; } break; case 5: if (--gBattleStruct->wallyWaitFrames == 0) { PlaySE(SE_SELECT); BtlController_EmitTwoReturnValues(battler, B_COMM_TO_ENGINE, B_ACTION_USE_ITEM, 0); WallyBufferExecCompleted(battler); } break; } } static void OpenBagAfterPaletteFade(u32 battler) { if (!gPaletteFade.active) { gBattlerControllerFuncs[battler] = CompleteOnChosenItem; ReshowBattleScreenDummy(); FreeAllWindowBuffers(); DoWallyTutorialBagMenu(); } } static void CompleteOnChosenItem(u32 battler) { if (gMain.callback2 == BattleMainCB2 && !gPaletteFade.active) { BtlController_EmitOneReturnValue(battler, B_COMM_TO_ENGINE, gSpecialVar_ItemId); WallyBufferExecCompleted(battler); } } static void Intro_TryShinyAnimShowHealthbox(u32 battler) { if (!gBattleSpritesDataPtr->healthBoxesData[battler].triedShinyMonAnim && !gBattleSpritesDataPtr->healthBoxesData[battler].ballAnimActive) TryShinyAnimation(battler, GetBattlerMon(battler)); if (!gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].triedShinyMonAnim && !gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].ballAnimActive) TryShinyAnimation(BATTLE_PARTNER(battler), GetBattlerMon(BATTLE_PARTNER(battler))); if (!gBattleSpritesDataPtr->healthBoxesData[battler].ballAnimActive && !gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].ballAnimActive && gSprites[gBattleControllerData[battler]].callback == SpriteCallbackDummy && gSprites[gBattlerSpriteIds[battler]].callback == SpriteCallbackDummy) { if (IsDoubleBattle() && !(gBattleTypeFlags & BATTLE_TYPE_MULTI)) { DestroySprite(&gSprites[gBattleControllerData[BATTLE_PARTNER(battler)]]); UpdateHealthboxAttribute(gHealthboxSpriteIds[BATTLE_PARTNER(battler)], GetBattlerMon(BATTLE_PARTNER(battler)), HEALTHBOX_ALL); StartHealthboxSlideIn(BATTLE_PARTNER(battler)); SetHealthboxSpriteVisible(gHealthboxSpriteIds[BATTLE_PARTNER(battler)]); } DestroySprite(&gSprites[gBattleControllerData[battler]]); UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], GetBattlerMon(battler), HEALTHBOX_ALL); StartHealthboxSlideIn(battler); SetHealthboxSpriteVisible(gHealthboxSpriteIds[battler]); gBattleSpritesDataPtr->animationData->introAnimActive = FALSE; gBattlerControllerFuncs[battler] = Intro_WaitForShinyAnimAndHealthbox; } } static void Intro_WaitForShinyAnimAndHealthbox(u32 battler) { bool32 healthboxAnimDone = FALSE; if (gSprites[gHealthboxSpriteIds[battler]].callback == SpriteCallbackDummy) healthboxAnimDone = TRUE; if (healthboxAnimDone && gBattleSpritesDataPtr->healthBoxesData[battler].finishedShinyMonAnim && gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].finishedShinyMonAnim) { gBattleSpritesDataPtr->healthBoxesData[battler].triedShinyMonAnim = FALSE; gBattleSpritesDataPtr->healthBoxesData[battler].finishedShinyMonAnim = FALSE; gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].triedShinyMonAnim = FALSE; gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].finishedShinyMonAnim = FALSE; FreeSpriteTilesByTag(ANIM_TAG_GOLD_STARS); FreeSpritePaletteByTag(ANIM_TAG_GOLD_STARS); CreateTask(Task_PlayerController_RestoreBgmAfterCry, 10); HandleLowHpMusicChange(GetBattlerMon(battler), battler); WallyBufferExecCompleted(battler); } } static void WallyBufferExecCompleted(u32 battler) { gBattlerControllerFuncs[battler] = WallyBufferRunCommand; if (gBattleTypeFlags & BATTLE_TYPE_LINK) { u8 playerId = GetMultiplayerId(); PrepareBufferDataTransferLink(battler, B_COMM_CONTROLLER_IS_DONE, 4, &playerId); gBattleResources->bufferA[battler][0] = CONTROLLER_TERMINATOR_NOP; } else { MarkBattleControllerIdleOnLocal(battler); } } #define sSpeedX data[0] static void WallyHandleDrawTrainerPic(u32 battler) { BtlController_HandleDrawTrainerPic(battler, TRAINER_BACK_PIC_WALLY, FALSE, 80, 80 + 4 * (8 - gTrainerBacksprites[TRAINER_BACK_PIC_WALLY].coordinates.size), 30); } static void WallyHandleTrainerSlide(u32 battler) { BtlController_HandleTrainerSlide(battler, TRAINER_BACK_PIC_WALLY); } #undef sSpeedX static void WallyHandleSuccessBallThrowAnim(u32 battler) { BtlController_HandleSuccessBallThrowAnim(battler, GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT), B_ANIM_BALL_THROW_WITH_TRAINER, FALSE); } static void WallyHandleBallThrowAnim(u32 battler) { BtlController_HandleBallThrowAnim(battler, GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT), B_ANIM_BALL_THROW_WITH_TRAINER, FALSE); } static void WallyHandleMoveAnimation(u32 battler) { BtlController_HandleMoveAnimation(battler, FALSE); } static void WallyHandlePrintString(u32 battler) { BtlController_HandlePrintString(battler, FALSE, FALSE); } static void WallyHandlePrintSelectionString(u32 battler) { if (IsOnPlayerSide(battler)) WallyHandlePrintString(battler); else WallyBufferExecCompleted(battler); } static void HandleChooseActionAfterDma3(u32 battler) { if (!IsDma3ManagerBusyWithBgCopy()) { gBattle_BG0_X = 0; gBattle_BG0_Y = DISPLAY_HEIGHT; gBattlerControllerFuncs[battler] = WallyHandleActions; } } static void WallyHandleChooseAction(u32 battler) { s32 i; gBattlerControllerFuncs[battler] = HandleChooseActionAfterDma3; BattlePutTextOnWindow(gText_BattleMenu, B_WIN_ACTION_MENU); for (i = 0; i < 4; i++) ActionSelectionDestroyCursorAt(i); ActionSelectionCreateCursorAt(gActionSelectionCursor[battler], 0); BattleStringExpandPlaceholdersToDisplayedString(gText_WhatWillWallyDo); BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_ACTION_PROMPT); } static void WallyHandleChooseMove(u32 battler) { switch (gBattleStruct->wallyMovesState) { case 0: InitMoveSelectionsVarsAndStrings(battler); gBattleStruct->wallyMovesState++; gBattleStruct->wallyMoveFrames = 80; break; case 1: if (!IsDma3ManagerBusyWithBgCopy()) { gBattle_BG0_X = 0; gBattle_BG0_Y = DISPLAY_HEIGHT * 2; gBattleStruct->wallyMovesState++; } break; case 2: if (--gBattleStruct->wallyMoveFrames == 0) { PlaySE(SE_SELECT); BtlController_EmitTwoReturnValues(battler, B_COMM_TO_ENGINE, 10, 0x100); WallyBufferExecCompleted(battler); } break; } } static void WallyHandleChooseItem(u32 battler) { BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 0x10, RGB_BLACK); gBattlerControllerFuncs[battler] = OpenBagAfterPaletteFade; gBattlerInMenuId = battler; } static void WallyHandleHealthBarUpdate(u32 battler) { BtlController_HandleHealthBarUpdate(battler, TRUE); } // For some reason Wally's SE don't take side into account and pan is always the same. Possibly a bug static void WallyHandlePlaySE(u32 battler) { PlaySE(gBattleResources->bufferA[battler][1] | (gBattleResources->bufferA[battler][2] << 8)); WallyBufferExecCompleted(battler); } // All of the other controllers use CRY_MODE_FAINT. // Wally's Pokémon during the tutorial is never intended to faint, so that's probably why it's different here. static void WallyHandleFaintingCry(u32 battler) { u16 species = GetMonData(GetBattlerMon(battler), MON_DATA_SPECIES); PlayCry_Normal(species, 25); WallyBufferExecCompleted(battler); } static void WallyHandleIntroTrainerBallThrow(u32 battler) { const u16 *trainerPal = gTrainerBacksprites[TRAINER_BACK_PIC_WALLY].palette.data; BtlController_HandleIntroTrainerBallThrow(battler, 0xD6F8, trainerPal, 31, Intro_TryShinyAnimShowHealthbox); } static void WallyHandleDrawPartyStatusSummary(u32 battler) { BtlController_HandleDrawPartyStatusSummary(battler, B_SIDE_PLAYER, FALSE); } static void WallyHandleBattleAnimation(u32 battler) { BtlController_HandleBattleAnimation(battler, TRUE, FALSE); } static void WallyHandleEndLinkBattle(u32 battler) { gBattleOutcome = gBattleResources->bufferA[battler][1]; FadeOutMapMusic(5); BeginFastPaletteFade(3); WallyBufferExecCompleted(battler); if (!(gBattleTypeFlags & BATTLE_TYPE_IS_MASTER) && gBattleTypeFlags & BATTLE_TYPE_LINK) gBattlerControllerFuncs[battler] = SetBattleEndCallbacks; }