#include "global.h" #include "malloc.h" #include "battle.h" #include "battle_message.h" #include "bg.h" #include "data.h" #include "decompress.h" #include "evolution_scene.h" #include "evolution_graphics.h" #include "gpu_regs.h" #include "link.h" #include "link_rfu.h" #include "m4a.h" #include "main.h" #include "menu.h" #include "overworld.h" #include "palette.h" #include "pokedex.h" #include "pokemon.h" #include "pokemon_summary_screen.h" #include "scanline_effect.h" #include "sound.h" #include "sprite.h" #include "string_util.h" #include "strings.h" #include "task.h" #include "text.h" #include "text_window.h" #include "trig.h" #include "trade.h" #include "util.h" #include "constants/battle_string_ids.h" #include "constants/songs.h" #include "constants/rgb.h" #include "constants/items.h" extern struct Evolution gEvolutionTable[][EVOS_PER_MON]; struct EvoInfo { u8 preEvoSpriteId; u8 postEvoSpriteId; u8 evoTaskId; u8 delayTimer; u16 savedPalette[48]; }; static EWRAM_DATA struct EvoInfo *sEvoStructPtr = NULL; static EWRAM_DATA u16 *sBgAnimPal = NULL; void (*gCB2_AfterEvolution)(void); #define sEvoCursorPos gBattleCommunication[1] // when learning a new move #define sEvoGraphicsTaskId gBattleCommunication[2] static void Task_EvolutionScene(u8 taskId); static void Task_TradeEvolutionScene(u8 taskId); static void CB2_EvolutionSceneUpdate(void); static void CB2_TradeEvolutionSceneUpdate(void); static void EvoDummyFunc(void); static void VBlankCB_EvolutionScene(void); static void VBlankCB_TradeEvolutionScene(void); static void EvoScene_DoMonAnimAndCry(u8 monSpriteId, u16 speciesId); static bool32 EvoScene_IsMonAnimFinished(u8 monSpriteId); static void StartBgAnimation(bool8 isLink); static void StopBgAnimation(void); static void Task_AnimateBg(u8 taskId); static void RestoreBgAfterAnim(void); static const u16 sUnusedPal1[] = INCBIN_U16("graphics/evolution_scene/unused_1.gbapal"); static const u32 sBgAnim_Gfx[] = INCBIN_U32("graphics/evolution_scene/bg.4bpp.lz"); static const u32 sBgAnim_Inner_Tilemap[] = INCBIN_U32("graphics/evolution_scene/bg_inner.bin.lz"); static const u32 sBgAnim_Outer_Tilemap[] = INCBIN_U32("graphics/evolution_scene/bg_outer.bin.lz"); static const u16 sBgAnim_Intro_Pal[] = INCBIN_U16("graphics/evolution_scene/bg_anim_intro.gbapal"); static const u16 sUnusedPal2[] = INCBIN_U16("graphics/evolution_scene/unused_2.gbapal"); static const u16 sUnusedPal3[] = INCBIN_U16("graphics/evolution_scene/unused_3.gbapal"); static const u16 sUnusedPal4[] = INCBIN_U16("graphics/evolution_scene/unused_4.gbapal"); static const u16 sBgAnim_Pal[] = INCBIN_U16("graphics/evolution_scene/bg_anim.gbapal"); static const u8 sText_ShedinjaJapaneseName[] = _("ヌケニン"); // The below table is used by Task_UpdateBgPalette to control the speed at which the bg color updates. // The first two values are indexes into sBgAnim_PalIndexes (indirectly, via sBgAnimPal), and are // the start and end of the range of colors in sBgAnim_PalIndexes it will move through incrementally // before starting over. It will repeat this cycle x number of times, where x = the 3rd value, // delaying each increment by y, where y = the 4th value. // Once it has cycled x number of times, it will move to the next array in this table. static const u8 sBgAnim_PaletteControl[][4] = { { 0, 12, 1, 6 }, { 13, 36, 5, 2 }, { 13, 24, 1, 2 }, { 37, 49, 1, 6 }, }; // Indexes into sBgAnim_Pal, 0 is black, transitioning to a bright light blue (172, 213, 255) at 13 static const u8 sBgAnim_PalIndexes[][16] = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 0, 0 }, { 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 0 }, { 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0 }, { 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0 }, { 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 11, 0, 0 }, { 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0 }, { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0, 0 }, { 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 12, 0, 0 }, { 0, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 12, 11, 0, 0 }, { 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 12, 11, 10, 0, 0 }, { 0, 5, 6, 7, 8, 9, 10, 11, 12, 13, 12, 11, 10, 9, 0, 0 }, { 0, 6, 7, 8, 9, 10, 11, 12, 13, 12, 11, 10, 9, 8, 0, 0 }, { 0, 7, 8, 9, 10, 11, 12, 13, 12, 11, 10, 9, 8, 7, 0, 0 }, { 0, 8, 9, 10, 11, 12, 13, 12, 11, 10, 9, 8, 7, 6, 0, 0 }, { 0, 9, 10, 11, 12, 13, 12, 11, 10, 9, 8, 7, 6, 5, 0, 0 }, { 0, 10, 11, 12, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 0, 0 }, { 0, 11, 12, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 0, 0 }, { 0, 12, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 0, 0 }, { 0, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0 }, { 0, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, 0, 0 }, { 0, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, 3, 0, 0 }, { 0, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, 3, 4, 0, 0 }, { 0, 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, 3, 4, 5, 0, 0 }, { 0, 8, 7, 6, 5, 4, 3, 2, 1, 2, 3, 4, 5, 6, 0, 0 }, { 0, 7, 6, 5, 4, 3, 2, 1, 2, 3, 4, 5, 6, 7, 0, 0 }, { 0, 6, 5, 4, 3, 2, 1, 2, 3, 4, 5, 6, 7, 8, 0, 0 }, { 0, 5, 4, 3, 2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0 }, { 0, 4, 3, 2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0 }, { 0, 3, 2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 0 }, { 0, 2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0 }, { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0, 0 }, { 0, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0 }, { 0, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0 }, { 0, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0 }, { 0, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0 }, { 0, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0 }, { 0, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }; static void CB2_BeginEvolutionScene(void) { UpdatePaletteFade(); RunTasks(); } #define tState data[0] #define tPreEvoSpecies data[1] #define tPostEvoSpecies data[2] #define tCanStop data[3] #define tBits data[3] #define tLearnsFirstMove data[4] #define tLearnMoveState data[6] #define tLearnMoveYesState data[7] #define tLearnMoveNoState data[8] #define tEvoWasStopped data[9] #define tPartyId data[10] #define TASK_BIT_CAN_STOP (1 << 0) #define TASK_BIT_LEARN_MOVE (1 << 7) static void Task_BeginEvolutionScene(u8 taskId) { struct Pokemon* mon = NULL; switch (gTasks[taskId].tState) { case 0: BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 0x10, RGB_BLACK); gTasks[taskId].tState++; break; case 1: if (!gPaletteFade.active) { u16 postEvoSpecies; bool8 canStopEvo; u8 partyId; mon = &gPlayerParty[gTasks[taskId].tPartyId]; postEvoSpecies = gTasks[taskId].tPostEvoSpecies; canStopEvo = gTasks[taskId].tCanStop; partyId = gTasks[taskId].tPartyId; DestroyTask(taskId); EvolutionScene(mon, postEvoSpecies, canStopEvo, partyId); } break; } } void BeginEvolutionScene(struct Pokemon* mon, u16 postEvoSpecies, bool8 canStopEvo, u8 partyId) { u8 taskId = CreateTask(Task_BeginEvolutionScene, 0); gTasks[taskId].tState = 0; gTasks[taskId].tPostEvoSpecies = postEvoSpecies; gTasks[taskId].tCanStop = canStopEvo; gTasks[taskId].tPartyId = partyId; SetMainCallback2(CB2_BeginEvolutionScene); } void EvolutionScene(struct Pokemon* mon, u16 postEvoSpecies, bool8 canStopEvo, u8 partyId) { u8 name[20]; u16 currSpecies; u32 trainerId, personality; const struct CompressedSpritePalette* pokePal; u8 ID; SetHBlankCallback(NULL); SetVBlankCallback(NULL); CpuFill32(0, (void*)(VRAM), VRAM_SIZE); SetGpuReg(REG_OFFSET_MOSAIC, 0); SetGpuReg(REG_OFFSET_WIN0H, 0); SetGpuReg(REG_OFFSET_WIN0V, 0); SetGpuReg(REG_OFFSET_WIN1H, 0); SetGpuReg(REG_OFFSET_WIN1V, 0); SetGpuReg(REG_OFFSET_WININ, 0); SetGpuReg(REG_OFFSET_WINOUT, 0); ResetPaletteFade(); gBattle_BG0_X = 0; gBattle_BG0_Y = 0; gBattle_BG1_X = 0; gBattle_BG1_Y = 0; gBattle_BG2_X = 0; gBattle_BG2_Y = 0; gBattle_BG3_X = 256; gBattle_BG3_Y = 0; gBattleTerrain = BATTLE_TERRAIN_PLAIN; InitBattleBgsVideo(); LoadBattleTextboxAndBackground(); ResetSpriteData(); ScanlineEffect_Stop(); ResetTasks(); FreeAllSpritePalettes(); gReservedSpritePaletteCount = 4; sEvoStructPtr = AllocZeroed(sizeof(struct EvoInfo)); AllocateMonSpritesGfx(); GetMonData(mon, MON_DATA_NICKNAME, name); StringCopy_Nickname(gStringVar1, name); StringCopy(gStringVar2, gSpeciesNames[postEvoSpecies]); // preEvo sprite currSpecies = GetMonData(mon, MON_DATA_SPECIES); trainerId = GetMonData(mon, MON_DATA_OT_ID); personality = GetMonData(mon, MON_DATA_PERSONALITY); DecompressPicFromTable_2(&gMonFrontPicTable[currSpecies], gMonSpritesGfxPtr->sprites.ptr[B_POSITION_OPPONENT_LEFT], currSpecies); pokePal = GetMonSpritePalStructFromOtIdPersonality(currSpecies, trainerId, personality); LoadCompressedPalette(pokePal->data, 0x110, 0x20); SetMultiuseSpriteTemplateToPokemon(currSpecies, B_POSITION_OPPONENT_LEFT); gMultiuseSpriteTemplate.affineAnims = gDummySpriteAffineAnimTable; sEvoStructPtr->preEvoSpriteId = ID = CreateSprite(&gMultiuseSpriteTemplate, 120, 64, 30); gSprites[ID].callback = SpriteCallbackDummy_2; gSprites[ID].oam.paletteNum = 1; gSprites[ID].invisible = TRUE; // postEvo sprite DecompressPicFromTable_2(&gMonFrontPicTable[postEvoSpecies], gMonSpritesGfxPtr->sprites.ptr[B_POSITION_OPPONENT_RIGHT], postEvoSpecies); pokePal = GetMonSpritePalStructFromOtIdPersonality(postEvoSpecies, trainerId, personality); LoadCompressedPalette(pokePal->data, 0x120, 0x20); SetMultiuseSpriteTemplateToPokemon(postEvoSpecies, B_POSITION_OPPONENT_RIGHT); gMultiuseSpriteTemplate.affineAnims = gDummySpriteAffineAnimTable; sEvoStructPtr->postEvoSpriteId = ID = CreateSprite(&gMultiuseSpriteTemplate, 120, 64, 30); gSprites[ID].callback = SpriteCallbackDummy_2; gSprites[ID].oam.paletteNum = 2; gSprites[ID].invisible = TRUE; LoadEvoSparkleSpriteAndPal(); sEvoStructPtr->evoTaskId = ID = CreateTask(Task_EvolutionScene, 0); gTasks[ID].tState = 0; gTasks[ID].tPreEvoSpecies = currSpecies; gTasks[ID].tPostEvoSpecies = postEvoSpecies; gTasks[ID].tCanStop = canStopEvo; gTasks[ID].tLearnsFirstMove = TRUE; gTasks[ID].tEvoWasStopped = FALSE; gTasks[ID].tPartyId = partyId; memcpy(&sEvoStructPtr->savedPalette, &gPlttBufferUnfaded[0x20], sizeof(sEvoStructPtr->savedPalette)); SetGpuReg(REG_OFFSET_DISPCNT, DISPCNT_OBJ_ON | DISPCNT_BG_ALL_ON | DISPCNT_OBJ_1D_MAP); SetHBlankCallback(EvoDummyFunc); SetVBlankCallback(VBlankCB_EvolutionScene); m4aMPlayAllStop(); SetMainCallback2(CB2_EvolutionSceneUpdate); } static void CB2_EvolutionSceneLoadGraphics(void) { u8 ID; const struct CompressedSpritePalette* pokePal; u16 postEvoSpecies; u32 trainerId, personality; struct Pokemon* mon = &gPlayerParty[gTasks[sEvoStructPtr->evoTaskId].tPartyId]; postEvoSpecies = gTasks[sEvoStructPtr->evoTaskId].tPostEvoSpecies; trainerId = GetMonData(mon, MON_DATA_OT_ID); personality = GetMonData(mon, MON_DATA_PERSONALITY); SetHBlankCallback(NULL); SetVBlankCallback(NULL); CpuFill32(0, (void*)(VRAM), VRAM_SIZE); SetGpuReg(REG_OFFSET_MOSAIC, 0); SetGpuReg(REG_OFFSET_WIN0H, 0); SetGpuReg(REG_OFFSET_WIN0V, 0); SetGpuReg(REG_OFFSET_WIN1H, 0); SetGpuReg(REG_OFFSET_WIN1V, 0); SetGpuReg(REG_OFFSET_WININ, 0); SetGpuReg(REG_OFFSET_WINOUT, 0); ResetPaletteFade(); gBattle_BG0_X = 0; gBattle_BG0_Y = 0; gBattle_BG1_X = 0; gBattle_BG1_Y = 0; gBattle_BG2_X = 0; gBattle_BG2_Y = 0; gBattle_BG3_X = 256; gBattle_BG3_Y = 0; gBattleTerrain = BATTLE_TERRAIN_PLAIN; InitBattleBgsVideo(); LoadBattleTextboxAndBackground(); ResetSpriteData(); FreeAllSpritePalettes(); gReservedSpritePaletteCount = 4; DecompressPicFromTable_2(&gMonFrontPicTable[postEvoSpecies], gMonSpritesGfxPtr->sprites.ptr[B_POSITION_OPPONENT_RIGHT], postEvoSpecies); pokePal = GetMonSpritePalStructFromOtIdPersonality(postEvoSpecies, trainerId, personality); LoadCompressedPalette(pokePal->data, 0x120, 0x20); SetMultiuseSpriteTemplateToPokemon(postEvoSpecies, B_POSITION_OPPONENT_RIGHT); gMultiuseSpriteTemplate.affineAnims = gDummySpriteAffineAnimTable; sEvoStructPtr->postEvoSpriteId = ID = CreateSprite(&gMultiuseSpriteTemplate, 120, 64, 30); gSprites[ID].callback = SpriteCallbackDummy_2; gSprites[ID].oam.paletteNum = 2; SetGpuReg(REG_OFFSET_DISPCNT, DISPCNT_OBJ_ON | DISPCNT_BG_ALL_ON | DISPCNT_OBJ_1D_MAP); SetHBlankCallback(EvoDummyFunc); SetVBlankCallback(VBlankCB_EvolutionScene); SetMainCallback2(CB2_EvolutionSceneUpdate); BeginNormalPaletteFade(PALETTES_ALL, 0, 0x10, 0, RGB_BLACK); ShowBg(0); ShowBg(1); ShowBg(2); ShowBg(3); } static void CB2_TradeEvolutionSceneLoadGraphics(void) { struct Pokemon* mon = &gPlayerParty[gTasks[sEvoStructPtr->evoTaskId].tPartyId]; u16 postEvoSpecies = gTasks[sEvoStructPtr->evoTaskId].tPostEvoSpecies; switch (gMain.state) { case 0: SetGpuReg(REG_OFFSET_DISPCNT, 0); SetHBlankCallback(NULL); SetVBlankCallback(NULL); ResetSpriteData(); FreeAllSpritePalettes(); gReservedSpritePaletteCount = 4; gBattle_BG0_X = 0; gBattle_BG0_Y = 0; gBattle_BG1_X = 0; gBattle_BG1_Y = 0; gBattle_BG2_X = 0; gBattle_BG2_Y = 0; gBattle_BG3_X = 256; gBattle_BG3_Y = 0; gMain.state++; break; case 1: ResetPaletteFade(); SetHBlankCallback(EvoDummyFunc); SetVBlankCallback(VBlankCB_TradeEvolutionScene); gMain.state++; break; case 2: InitTradeBg(); gMain.state++; break; case 3: FillBgTilemapBufferRect(1, 0, 0, 0, 0x20, 0x20, 0x11); CopyBgTilemapBufferToVram(1); gMain.state++; break; case 4: { const struct CompressedSpritePalette* pokePal; u32 trainerId = GetMonData(mon, MON_DATA_OT_ID); u32 personality = GetMonData(mon, MON_DATA_PERSONALITY); DecompressPicFromTable_2(&gMonFrontPicTable[postEvoSpecies], gMonSpritesGfxPtr->sprites.ptr[B_POSITION_OPPONENT_RIGHT], postEvoSpecies); pokePal = GetMonSpritePalStructFromOtIdPersonality(postEvoSpecies, trainerId, personality); LoadCompressedPalette(pokePal->data, 0x120, 0x20); gMain.state++; } break; case 5: { u8 ID; SetMultiuseSpriteTemplateToPokemon(postEvoSpecies, B_POSITION_OPPONENT_LEFT); gMultiuseSpriteTemplate.affineAnims = gDummySpriteAffineAnimTable; sEvoStructPtr->postEvoSpriteId = ID = CreateSprite(&gMultiuseSpriteTemplate, 120, 64, 30); gSprites[ID].callback = SpriteCallbackDummy_2; gSprites[ID].oam.paletteNum = 2; gMain.state++; LinkTradeDrawWindow(); } break; case 6: if (gWirelessCommType) { LoadWirelessStatusIndicatorSpriteGfx(); CreateWirelessStatusIndicatorSprite(0, 0); } BlendPalettes(PALETTES_ALL, 0x10, 0); gMain.state++; break; case 7: BeginNormalPaletteFade(PALETTES_ALL, 0, 0x10, 0, RGB_BLACK); InitTradeSequenceBgGpuRegs(); ShowBg(0); ShowBg(1); SetMainCallback2(CB2_TradeEvolutionSceneUpdate); SetGpuReg(REG_OFFSET_DISPCNT, DISPCNT_OBJ_ON | DISPCNT_BG0_ON | DISPCNT_BG1_ON | DISPCNT_OBJ_1D_MAP); break; } } void TradeEvolutionScene(struct Pokemon* mon, u16 postEvoSpecies, u8 preEvoSpriteId, u8 partyId) { u8 name[20]; u16 currSpecies; u32 trainerId, personality; const struct CompressedSpritePalette* pokePal; u8 ID; GetMonData(mon, MON_DATA_NICKNAME, name); StringCopy_Nickname(gStringVar1, name); StringCopy(gStringVar2, gSpeciesNames[postEvoSpecies]); gAffineAnimsDisabled = TRUE; // preEvo sprite currSpecies = GetMonData(mon, MON_DATA_SPECIES); personality = GetMonData(mon, MON_DATA_PERSONALITY); trainerId = GetMonData(mon, MON_DATA_OT_ID); sEvoStructPtr = AllocZeroed(sizeof(struct EvoInfo)); sEvoStructPtr->preEvoSpriteId = preEvoSpriteId; DecompressPicFromTable_2(&gMonFrontPicTable[postEvoSpecies], gMonSpritesGfxPtr->sprites.ptr[B_POSITION_OPPONENT_LEFT], postEvoSpecies); pokePal = GetMonSpritePalStructFromOtIdPersonality(postEvoSpecies, trainerId, personality); LoadCompressedPalette(pokePal->data, 0x120, 0x20); SetMultiuseSpriteTemplateToPokemon(postEvoSpecies, B_POSITION_OPPONENT_LEFT); gMultiuseSpriteTemplate.affineAnims = gDummySpriteAffineAnimTable; sEvoStructPtr->postEvoSpriteId = ID = CreateSprite(&gMultiuseSpriteTemplate, 120, 64, 30); gSprites[ID].callback = SpriteCallbackDummy_2; gSprites[ID].oam.paletteNum = 2; gSprites[ID].invisible = TRUE; LoadEvoSparkleSpriteAndPal(); sEvoStructPtr->evoTaskId = ID = CreateTask(Task_TradeEvolutionScene, 0); gTasks[ID].tState = 0; gTasks[ID].tPreEvoSpecies = currSpecies; gTasks[ID].tPostEvoSpecies = postEvoSpecies; gTasks[ID].tLearnsFirstMove = TRUE; gTasks[ID].tEvoWasStopped = FALSE; gTasks[ID].tPartyId = partyId; gBattle_BG0_X = 0; gBattle_BG0_Y = 0; gBattle_BG1_X = 0; gBattle_BG1_Y = 0; gBattle_BG2_X = 0; gBattle_BG2_Y = 0; gBattle_BG3_X = 256; gBattle_BG3_Y = 0; gTextFlags.useAlternateDownArrow = 1; SetVBlankCallback(VBlankCB_TradeEvolutionScene); SetMainCallback2(CB2_TradeEvolutionSceneUpdate); } static void CB2_EvolutionSceneUpdate(void) { AnimateSprites(); BuildOamBuffer(); RunTextPrinters(); UpdatePaletteFade(); RunTasks(); } static void CB2_TradeEvolutionSceneUpdate(void) { AnimateSprites(); BuildOamBuffer(); RunTextPrinters(); UpdatePaletteFade(); RunTasks(); } static void CreateShedinja(u16 preEvoSpecies, struct Pokemon* mon) { u32 data = 0; if (gEvolutionTable[preEvoSpecies][0].method == EVO_LEVEL_NINJASK && gPlayerPartyCount < PARTY_SIZE) { s32 i; struct Pokemon* shedinja = &gPlayerParty[gPlayerPartyCount]; CopyMon(&gPlayerParty[gPlayerPartyCount], mon, sizeof(struct Pokemon)); SetMonData(&gPlayerParty[gPlayerPartyCount], MON_DATA_SPECIES, &gEvolutionTable[preEvoSpecies][1].targetSpecies); SetMonData(&gPlayerParty[gPlayerPartyCount], MON_DATA_NICKNAME, gSpeciesNames[gEvolutionTable[preEvoSpecies][1].targetSpecies]); SetMonData(&gPlayerParty[gPlayerPartyCount], MON_DATA_HELD_ITEM, &data); SetMonData(&gPlayerParty[gPlayerPartyCount], MON_DATA_MARKINGS, &data); SetMonData(&gPlayerParty[gPlayerPartyCount], MON_DATA_ENCRYPT_SEPARATOR, &data); for (i = MON_DATA_COOL_RIBBON; i < MON_DATA_COOL_RIBBON + CONTEST_CATEGORIES_COUNT; i++) SetMonData(&gPlayerParty[gPlayerPartyCount], i, &data); for (i = MON_DATA_CHAMPION_RIBBON; i <= MON_DATA_UNUSED_RIBBONS; i++) SetMonData(&gPlayerParty[gPlayerPartyCount], i, &data); SetMonData(&gPlayerParty[gPlayerPartyCount], MON_DATA_STATUS, &data); data = MAIL_NONE; SetMonData(&gPlayerParty[gPlayerPartyCount], MON_DATA_MAIL, &data); CalculateMonStats(&gPlayerParty[gPlayerPartyCount]); CalculatePlayerPartyCount(); GetSetPokedexFlag(SpeciesToNationalPokedexNum(gEvolutionTable[preEvoSpecies][1].targetSpecies), FLAG_SET_SEEN); GetSetPokedexFlag(SpeciesToNationalPokedexNum(gEvolutionTable[preEvoSpecies][1].targetSpecies), FLAG_SET_CAUGHT); if (GetMonData(shedinja, MON_DATA_SPECIES) == SPECIES_SHEDINJA && GetMonData(shedinja, MON_DATA_LANGUAGE) == LANGUAGE_JAPANESE && GetMonData(mon, MON_DATA_SPECIES) == SPECIES_NINJASK) SetMonData(shedinja, MON_DATA_NICKNAME, sText_ShedinjaJapaneseName); } } // States for the main switch in Task_EvolutionScene enum { EVOSTATE_FADE_IN, EVOSTATE_INTRO_MSG, EVOSTATE_INTRO_MON_ANIM, EVOSTATE_INTRO_SOUND, EVOSTATE_START_MUSIC, EVOSTATE_START_BG_AND_SPARKLE_SPIRAL, EVOSTATE_SPARKLE_ARC, EVOSTATE_CYCLE_MON_SPRITE, EVOSTATE_WAIT_CYCLE_MON_SPRITE, EVOSTATE_SPARKLE_CIRCLE, EVOSTATE_SPARKLE_SPRAY, EVOSTATE_EVO_SOUND, EVOSTATE_RESTORE_SCREEN, EVOSTATE_EVO_MON_ANIM, EVOSTATE_SET_MON_EVOLVED, EVOSTATE_TRY_LEARN_MOVE, EVOSTATE_END, EVOSTATE_CANCEL, EVOSTATE_CANCEL_MON_ANIM, EVOSTATE_CANCEL_MSG, EVOSTATE_LEARNED_MOVE, EVOSTATE_TRY_LEARN_ANOTHER_MOVE, EVOSTATE_REPLACE_MOVE, }; // States for the switch in EVOSTATE_REPLACE_MOVE enum { MVSTATE_INTRO_MSG_1, MVSTATE_INTRO_MSG_2, MVSTATE_INTRO_MSG_3, MVSTATE_PRINT_YES_NO, MVSTATE_HANDLE_YES_NO, MVSTATE_SHOW_MOVE_SELECT, MVSTATE_HANDLE_MOVE_SELECT, MVSTATE_FORGET_MSG_1, MVSTATE_FORGET_MSG_2, MVSTATE_LEARNED_MOVE, MVSTATE_ASK_CANCEL, MVSTATE_CANCEL, MVSTATE_RETRY_AFTER_HM, }; // Task data from CycleEvolutionMonSprite #define tEvoStopped data[8] static void Task_EvolutionScene(u8 taskId) { u32 var; struct Pokemon* mon = &gPlayerParty[gTasks[taskId].tPartyId]; // check if B Button was held, so the evolution gets stopped if (gMain.heldKeys == B_BUTTON && gTasks[taskId].tState == EVOSTATE_WAIT_CYCLE_MON_SPRITE && gTasks[sEvoGraphicsTaskId].isActive && gTasks[taskId].tBits & TASK_BIT_CAN_STOP) { gTasks[taskId].tState = EVOSTATE_CANCEL; gTasks[sEvoGraphicsTaskId].tEvoStopped = TRUE; StopBgAnimation(); return; } switch (gTasks[taskId].tState) { case EVOSTATE_FADE_IN: BeginNormalPaletteFade(PALETTES_ALL, 0, 0x10, 0, RGB_BLACK); gSprites[sEvoStructPtr->preEvoSpriteId].invisible = FALSE; gTasks[taskId].tState++; ShowBg(0); ShowBg(1); ShowBg(2); ShowBg(3); break; case EVOSTATE_INTRO_MSG: if (!gPaletteFade.active) { StringExpandPlaceholders(gStringVar4, gText_PkmnIsEvolving); BattlePutTextOnWindow(gStringVar4, B_WIN_MSG); gTasks[taskId].tState++; } break; case EVOSTATE_INTRO_MON_ANIM: if (!IsTextPrinterActive(0)) { EvoScene_DoMonAnimAndCry(sEvoStructPtr->preEvoSpriteId, gTasks[taskId].tPreEvoSpecies); gTasks[taskId].tState++; } break; case EVOSTATE_INTRO_SOUND: if (EvoScene_IsMonAnimFinished(sEvoStructPtr->preEvoSpriteId)) { PlaySE(MUS_EVOLUTION_INTRO); gTasks[taskId].tState++; } break; case EVOSTATE_START_MUSIC: if (!IsSEPlaying()) { // Start music, fade background to black PlayNewMapMusic(MUS_EVOLUTION); gTasks[taskId].tState++; BeginNormalPaletteFade(0x1C, 4, 0, 0x10, RGB_BLACK); } break; case EVOSTATE_START_BG_AND_SPARKLE_SPIRAL: if (!gPaletteFade.active) { StartBgAnimation(FALSE); sEvoGraphicsTaskId = EvolutionSparkles_SpiralUpward(17); gTasks[taskId].tState++; } break; case EVOSTATE_SPARKLE_ARC: if (!gTasks[sEvoGraphicsTaskId].isActive) { gTasks[taskId].tState++; sEvoStructPtr->delayTimer = 1; sEvoGraphicsTaskId = EvolutionSparkles_ArcDown(); } break; case EVOSTATE_CYCLE_MON_SPRITE: // launch task that flashes pre evo with post evo sprites if (!gTasks[sEvoGraphicsTaskId].isActive) { sEvoGraphicsTaskId = CycleEvolutionMonSprite(sEvoStructPtr->preEvoSpriteId, sEvoStructPtr->postEvoSpriteId); gTasks[taskId].tState++; } break; case EVOSTATE_WAIT_CYCLE_MON_SPRITE: if (--sEvoStructPtr->delayTimer == 0) { sEvoStructPtr->delayTimer = 3; if (!gTasks[sEvoGraphicsTaskId].isActive) gTasks[taskId].tState++; } break; case EVOSTATE_SPARKLE_CIRCLE: sEvoGraphicsTaskId = EvolutionSparkles_CircleInward(); gTasks[taskId].tState++; break; case EVOSTATE_SPARKLE_SPRAY: if (!gTasks[sEvoGraphicsTaskId].isActive) { sEvoGraphicsTaskId = EvolutionSparkles_SprayAndFlash(gTasks[taskId].tPostEvoSpecies); gTasks[taskId].tState++; } break; case EVOSTATE_EVO_SOUND: if (!gTasks[sEvoGraphicsTaskId].isActive) { PlaySE(SE_EXP); gTasks[taskId].tState++; } break; case EVOSTATE_RESTORE_SCREEN: // stop music, return screen to pre-fade state if (IsSEPlaying()) { m4aMPlayAllStop(); memcpy(&gPlttBufferUnfaded[0x20], sEvoStructPtr->savedPalette, sizeof(sEvoStructPtr->savedPalette)); RestoreBgAfterAnim(); BeginNormalPaletteFade(0x1C, 0, 0x10, 0, RGB_BLACK); gTasks[taskId].tState++; } break; case EVOSTATE_EVO_MON_ANIM: if (!gPaletteFade.active) { EvoScene_DoMonAnimAndCry(sEvoStructPtr->postEvoSpriteId, gTasks[taskId].tPostEvoSpecies); gTasks[taskId].tState++; } break; case EVOSTATE_SET_MON_EVOLVED: if (IsCryFinished()) { StringExpandPlaceholders(gStringVar4, gText_CongratsPkmnEvolved); BattlePutTextOnWindow(gStringVar4, B_WIN_MSG); PlayBGM(MUS_EVOLVED); gTasks[taskId].tState++; SetMonData(mon, MON_DATA_SPECIES, (void*)(&gTasks[taskId].tPostEvoSpecies)); CalculateMonStats(mon); EvolutionRenameMon(mon, gTasks[taskId].tPreEvoSpecies, gTasks[taskId].tPostEvoSpecies); GetSetPokedexFlag(SpeciesToNationalPokedexNum(gTasks[taskId].tPostEvoSpecies), FLAG_SET_SEEN); GetSetPokedexFlag(SpeciesToNationalPokedexNum(gTasks[taskId].tPostEvoSpecies), FLAG_SET_CAUGHT); IncrementGameStat(GAME_STAT_EVOLVED_POKEMON); } break; case EVOSTATE_TRY_LEARN_MOVE: if (!IsTextPrinterActive(0)) { var = MonTryLearningNewMove(mon, gTasks[taskId].tLearnsFirstMove); if (var != MOVE_NONE && !gTasks[taskId].tEvoWasStopped) { u8 text[20]; if (!(gTasks[taskId].tBits & TASK_BIT_LEARN_MOVE)) { StopMapMusic(); Overworld_PlaySpecialMapMusic(); } gTasks[taskId].tBits |= TASK_BIT_LEARN_MOVE; gTasks[taskId].tLearnsFirstMove = FALSE; gTasks[taskId].tLearnMoveState = MVSTATE_INTRO_MSG_1; GetMonData(mon, MON_DATA_NICKNAME, text); StringCopy_Nickname(gBattleTextBuff1, text); if (var == MON_HAS_MAX_MOVES) gTasks[taskId].tState = EVOSTATE_REPLACE_MOVE; else if (var == MON_ALREADY_KNOWS_MOVE) break; else gTasks[taskId].tState = EVOSTATE_LEARNED_MOVE; } else // no move to learn, or evolution was canceled { BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 0x10, RGB_BLACK); gTasks[taskId].tState++; } } break; case EVOSTATE_END: if (!gPaletteFade.active) { if (!(gTasks[taskId].tBits & TASK_BIT_LEARN_MOVE)) { StopMapMusic(); Overworld_PlaySpecialMapMusic(); } if (!gTasks[taskId].tEvoWasStopped) CreateShedinja(gTasks[taskId].tPreEvoSpecies, mon); DestroyTask(taskId); FreeMonSpritesGfx(); Free(sEvoStructPtr); sEvoStructPtr = NULL; FreeAllWindowBuffers(); SetMainCallback2(gCB2_AfterEvolution); } break; case EVOSTATE_CANCEL: if (!gTasks[sEvoGraphicsTaskId].isActive) { m4aMPlayAllStop(); BeginNormalPaletteFade(0x6001C, 0, 0x10, 0, RGB_WHITE); gTasks[taskId].tState++; } break; case EVOSTATE_CANCEL_MON_ANIM: if (!gPaletteFade.active) { EvoScene_DoMonAnimAndCry(sEvoStructPtr->preEvoSpriteId, gTasks[taskId].tPreEvoSpecies); gTasks[taskId].tState++; } break; case EVOSTATE_CANCEL_MSG: if (EvoScene_IsMonAnimFinished(sEvoStructPtr->preEvoSpriteId)) { if (gTasks[taskId].tEvoWasStopped) StringExpandPlaceholders(gStringVar4, gText_EllipsisQuestionMark); else // Fire Red leftover probably StringExpandPlaceholders(gStringVar4, gText_PkmnStoppedEvolving); BattlePutTextOnWindow(gStringVar4, B_WIN_MSG); gTasks[taskId].tEvoWasStopped = TRUE; gTasks[taskId].tState = EVOSTATE_TRY_LEARN_MOVE; } break; case EVOSTATE_LEARNED_MOVE: if (!IsTextPrinterActive(0) && !IsSEPlaying()) { BufferMoveToLearnIntoBattleTextBuff2(); PlayFanfare(MUS_LEVEL_UP); BattleStringExpandPlaceholdersToDisplayedString(gBattleStringsTable[STRINGID_PKMNLEARNEDMOVE - BATTLESTRINGS_ID_ADDER]); BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_MSG); gTasks[taskId].tLearnsFirstMove = 0x40; // re-used as a counter gTasks[taskId].tState++; } break; case EVOSTATE_TRY_LEARN_ANOTHER_MOVE: if (!IsTextPrinterActive(0) && !IsSEPlaying() && --gTasks[taskId].tLearnsFirstMove == 0) gTasks[taskId].tState = EVOSTATE_TRY_LEARN_MOVE; break; case EVOSTATE_REPLACE_MOVE: switch (gTasks[taskId].tLearnMoveState) { case MVSTATE_INTRO_MSG_1: if (!IsTextPrinterActive(0) && !IsSEPlaying()) { // "{mon} is trying to learn {move}" BufferMoveToLearnIntoBattleTextBuff2(); BattleStringExpandPlaceholdersToDisplayedString(gBattleStringsTable[STRINGID_TRYTOLEARNMOVE1 - BATTLESTRINGS_ID_ADDER]); BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_MSG); gTasks[taskId].tLearnMoveState++; } break; case MVSTATE_INTRO_MSG_2: if (!IsTextPrinterActive(0) && !IsSEPlaying()) { // "But, {mon} can't learn more than four moves" BattleStringExpandPlaceholdersToDisplayedString(gBattleStringsTable[STRINGID_TRYTOLEARNMOVE2 - BATTLESTRINGS_ID_ADDER]); BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_MSG); gTasks[taskId].tLearnMoveState++; } break; case MVSTATE_INTRO_MSG_3: if (!IsTextPrinterActive(0) && !IsSEPlaying()) { // "Delete a move to make room for {move}?" BattleStringExpandPlaceholdersToDisplayedString(gBattleStringsTable[STRINGID_TRYTOLEARNMOVE3 - BATTLESTRINGS_ID_ADDER]); BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_MSG); gTasks[taskId].tLearnMoveYesState = MVSTATE_SHOW_MOVE_SELECT; gTasks[taskId].tLearnMoveNoState = MVSTATE_ASK_CANCEL; gTasks[taskId].tLearnMoveState++; } case MVSTATE_PRINT_YES_NO: if (!IsTextPrinterActive(0) && !IsSEPlaying()) { HandleBattleWindow(0x18, 8, 0x1D, 0xD, 0); BattlePutTextOnWindow(gText_BattleYesNoChoice, B_WIN_YESNO); gTasks[taskId].tLearnMoveState++; sEvoCursorPos = 0; BattleCreateYesNoCursorAt(0); } break; case MVSTATE_HANDLE_YES_NO: // This Yes/No is used for both the initial "delete move?" prompt // and for the "stop learning move?" prompt // What Yes/No do next is determined by tLearnMoveYesState / tLearnMoveNoState if (JOY_NEW(DPAD_UP) && sEvoCursorPos != 0) { // Moved onto YES PlaySE(SE_SELECT); BattleDestroyYesNoCursorAt(sEvoCursorPos); sEvoCursorPos = 0; BattleCreateYesNoCursorAt(0); } if (JOY_NEW(DPAD_DOWN) && sEvoCursorPos == 0) { // Moved onto NO PlaySE(SE_SELECT); BattleDestroyYesNoCursorAt(sEvoCursorPos); sEvoCursorPos = 1; BattleCreateYesNoCursorAt(1); } if (JOY_NEW(A_BUTTON)) { HandleBattleWindow(0x18, 8, 0x1D, 0xD, WINDOW_CLEAR); PlaySE(SE_SELECT); if (sEvoCursorPos != 0) { // NO gTasks[taskId].tLearnMoveState = gTasks[taskId].tLearnMoveNoState; } else { // YES gTasks[taskId].tLearnMoveState = gTasks[taskId].tLearnMoveYesState; if (gTasks[taskId].tLearnMoveState == MVSTATE_SHOW_MOVE_SELECT) BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 0x10, RGB_BLACK); } } if (JOY_NEW(B_BUTTON)) { // Equivalent to selecting NO HandleBattleWindow(0x18, 8, 0x1D, 0xD, WINDOW_CLEAR); PlaySE(SE_SELECT); gTasks[taskId].tLearnMoveState = gTasks[taskId].tLearnMoveNoState; } break; case MVSTATE_SHOW_MOVE_SELECT: if (!gPaletteFade.active) { FreeAllWindowBuffers(); ShowSelectMovePokemonSummaryScreen(gPlayerParty, gTasks[taskId].tPartyId, gPlayerPartyCount - 1, CB2_EvolutionSceneLoadGraphics, gMoveToLearn); gTasks[taskId].tLearnMoveState++; } break; case MVSTATE_HANDLE_MOVE_SELECT: if (!gPaletteFade.active && gMain.callback2 == CB2_EvolutionSceneUpdate) { var = GetMoveSlotToReplace(); if (var == MAX_MON_MOVES) { // Didn't select move slot gTasks[taskId].tLearnMoveState = MVSTATE_ASK_CANCEL; } else { // Selected move to forget u16 move = GetMonData(mon, var + MON_DATA_MOVE1); if (IsHMMove2(move)) { // Can't forget HMs BattleStringExpandPlaceholdersToDisplayedString(gBattleStringsTable[STRINGID_HMMOVESCANTBEFORGOTTEN - BATTLESTRINGS_ID_ADDER]); BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_MSG); gTasks[taskId].tLearnMoveState = MVSTATE_RETRY_AFTER_HM; } else { // Forget move PREPARE_MOVE_BUFFER(gBattleTextBuff2, move) RemoveMonPPBonus(mon, var); SetMonMoveSlot(mon, gMoveToLearn, var); gTasks[taskId].tLearnMoveState++; } } } break; case MVSTATE_FORGET_MSG_1: BattleStringExpandPlaceholdersToDisplayedString(gBattleStringsTable[STRINGID_123POOF - BATTLESTRINGS_ID_ADDER]); BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_MSG); gTasks[taskId].tLearnMoveState++; break; case MVSTATE_FORGET_MSG_2: if (!IsTextPrinterActive(0) && !IsSEPlaying()) { BattleStringExpandPlaceholdersToDisplayedString(gBattleStringsTable[STRINGID_PKMNFORGOTMOVE - BATTLESTRINGS_ID_ADDER]); BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_MSG); gTasks[taskId].tLearnMoveState++; } break; case MVSTATE_LEARNED_MOVE: if (!IsTextPrinterActive(0) && !IsSEPlaying()) { BattleStringExpandPlaceholdersToDisplayedString(gBattleStringsTable[STRINGID_ANDELLIPSIS - BATTLESTRINGS_ID_ADDER]); BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_MSG); gTasks[taskId].tState = EVOSTATE_LEARNED_MOVE; } break; case MVSTATE_ASK_CANCEL: BattleStringExpandPlaceholdersToDisplayedString(gBattleStringsTable[STRINGID_STOPLEARNINGMOVE - BATTLESTRINGS_ID_ADDER]); BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_MSG); gTasks[taskId].tLearnMoveYesState = MVSTATE_CANCEL; gTasks[taskId].tLearnMoveNoState = MVSTATE_INTRO_MSG_1; gTasks[taskId].tLearnMoveState = MVSTATE_PRINT_YES_NO; break; case MVSTATE_CANCEL: BattleStringExpandPlaceholdersToDisplayedString(gBattleStringsTable[STRINGID_DIDNOTLEARNMOVE - BATTLESTRINGS_ID_ADDER]); BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_MSG); gTasks[taskId].tState = EVOSTATE_TRY_LEARN_MOVE; break; case MVSTATE_RETRY_AFTER_HM: if (!IsTextPrinterActive(0) && !IsSEPlaying()) gTasks[taskId].tLearnMoveState = MVSTATE_SHOW_MOVE_SELECT; break; } break; } } // States for the main switch in Task_TradeEvolutionScene enum { T_EVOSTATE_INTRO_MSG, T_EVOSTATE_INTRO_CRY, T_EVOSTATE_INTRO_SOUND, T_EVOSTATE_START_MUSIC, T_EVOSTATE_START_BG_AND_SPARKLE_SPIRAL, T_EVOSTATE_SPARKLE_ARC, T_EVOSTATE_CYCLE_MON_SPRITE, T_EVOSTATE_WAIT_CYCLE_MON_SPRITE, T_EVOSTATE_SPARKLE_CIRCLE, T_EVOSTATE_SPARKLE_SPRAY, T_EVOSTATE_EVO_SOUND, T_EVOSTATE_EVO_MON_ANIM, T_EVOSTATE_SET_MON_EVOLVED, T_EVOSTATE_TRY_LEARN_MOVE, T_EVOSTATE_END, T_EVOSTATE_CANCEL, T_EVOSTATE_CANCEL_MON_ANIM, T_EVOSTATE_CANCEL_MSG, T_EVOSTATE_LEARNED_MOVE, T_EVOSTATE_TRY_LEARN_ANOTHER_MOVE, T_EVOSTATE_REPLACE_MOVE, }; // States for the switch in T_EVOSTATE_REPLACE_MOVE enum { T_MVSTATE_INTRO_MSG_1, T_MVSTATE_INTRO_MSG_2, T_MVSTATE_INTRO_MSG_3, T_MVSTATE_PRINT_YES_NO, T_MVSTATE_HANDLE_YES_NO, T_MVSTATE_SHOW_MOVE_SELECT, T_MVSTATE_HANDLE_MOVE_SELECT, T_MVSTATE_FORGET_MSG, T_MVSTATE_LEARNED_MOVE, T_MVSTATE_ASK_CANCEL, T_MVSTATE_CANCEL, T_MVSTATE_RETRY_AFTER_HM, }; // Compare to Task_EvolutionScene, very similar static void Task_TradeEvolutionScene(u8 taskId) { u32 var = 0; struct Pokemon* mon = &gPlayerParty[gTasks[taskId].tPartyId]; switch (gTasks[taskId].tState) { case T_EVOSTATE_INTRO_MSG: StringExpandPlaceholders(gStringVar4, gText_PkmnIsEvolving); DrawTextOnTradeWindow(0, gStringVar4, 1); gTasks[taskId].tState++; break; case T_EVOSTATE_INTRO_CRY: if (!IsTextPrinterActive(0)) { PlayCry_Normal(gTasks[taskId].tPreEvoSpecies, 0); gTasks[taskId].tState++; } break; case T_EVOSTATE_INTRO_SOUND: if (IsCryFinished()) { m4aSongNumStop(MUS_EVOLUTION); PlaySE(MUS_EVOLUTION_INTRO); gTasks[taskId].tState++; } break; case T_EVOSTATE_START_MUSIC: if (!IsSEPlaying()) { PlayBGM(MUS_EVOLUTION); gTasks[taskId].tState++; BeginNormalPaletteFade(0x1C, 4, 0, 0x10, RGB_BLACK); } break; case T_EVOSTATE_START_BG_AND_SPARKLE_SPIRAL: if (!gPaletteFade.active) { StartBgAnimation(TRUE); var = gSprites[sEvoStructPtr->preEvoSpriteId].oam.paletteNum + 16; sEvoGraphicsTaskId = EvolutionSparkles_SpiralUpward(var); gTasks[taskId].tState++; SetGpuReg(REG_OFFSET_BG3CNT, BGCNT_PRIORITY(3) | BGCNT_SCREENBASE(6)); } break; case T_EVOSTATE_SPARKLE_ARC: if (!gTasks[sEvoGraphicsTaskId].isActive) { gTasks[taskId].tState++; sEvoStructPtr->delayTimer = 1; sEvoGraphicsTaskId = EvolutionSparkles_ArcDown(); } break; case T_EVOSTATE_CYCLE_MON_SPRITE: if (!gTasks[sEvoGraphicsTaskId].isActive) { sEvoGraphicsTaskId = CycleEvolutionMonSprite(sEvoStructPtr->preEvoSpriteId, sEvoStructPtr->postEvoSpriteId); gTasks[taskId].tState++; } break; case T_EVOSTATE_WAIT_CYCLE_MON_SPRITE: if (--sEvoStructPtr->delayTimer == 0) { sEvoStructPtr->delayTimer = 3; if (!gTasks[sEvoGraphicsTaskId].isActive) gTasks[taskId].tState++; } break; case T_EVOSTATE_SPARKLE_CIRCLE: sEvoGraphicsTaskId = EvolutionSparkles_CircleInward(); gTasks[taskId].tState++; break; case T_EVOSTATE_SPARKLE_SPRAY: if (!gTasks[sEvoGraphicsTaskId].isActive) { sEvoGraphicsTaskId = EvolutionSparkles_SprayAndFlash_Trade(gTasks[taskId].tPostEvoSpecies); gTasks[taskId].tState++; } break; case T_EVOSTATE_EVO_SOUND: if (!gTasks[sEvoGraphicsTaskId].isActive) { PlaySE(SE_EXP); gTasks[taskId].tState++; } break; case T_EVOSTATE_EVO_MON_ANIM: if (IsSEPlaying()) { // Restore bg, do mon anim/cry Free(sBgAnimPal); EvoScene_DoMonAnimAndCry(sEvoStructPtr->postEvoSpriteId, gTasks[taskId].tPostEvoSpecies); memcpy(&gPlttBufferUnfaded[0x20], sEvoStructPtr->savedPalette, sizeof(sEvoStructPtr->savedPalette)); gTasks[taskId].tState++; } break; case T_EVOSTATE_SET_MON_EVOLVED: if (IsCryFinished()) { StringExpandPlaceholders(gStringVar4, gText_CongratsPkmnEvolved); DrawTextOnTradeWindow(0, gStringVar4, 1); PlayFanfare(MUS_EVOLVED); gTasks[taskId].tState++; SetMonData(mon, MON_DATA_SPECIES, (&gTasks[taskId].tPostEvoSpecies)); CalculateMonStats(mon); EvolutionRenameMon(mon, gTasks[taskId].tPreEvoSpecies, gTasks[taskId].tPostEvoSpecies); GetSetPokedexFlag(SpeciesToNationalPokedexNum(gTasks[taskId].tPostEvoSpecies), FLAG_SET_SEEN); GetSetPokedexFlag(SpeciesToNationalPokedexNum(gTasks[taskId].tPostEvoSpecies), FLAG_SET_CAUGHT); IncrementGameStat(GAME_STAT_EVOLVED_POKEMON); } break; case T_EVOSTATE_TRY_LEARN_MOVE: if (!IsTextPrinterActive(0) && IsFanfareTaskInactive() == TRUE) { var = MonTryLearningNewMove(mon, gTasks[taskId].tLearnsFirstMove); if (var != MOVE_NONE && !gTasks[taskId].tEvoWasStopped) { u8 text[20]; gTasks[taskId].tBits |= TASK_BIT_LEARN_MOVE; gTasks[taskId].tLearnsFirstMove = FALSE; gTasks[taskId].tLearnMoveState = 0; GetMonData(mon, MON_DATA_NICKNAME, text); StringCopy_Nickname(gBattleTextBuff1, text); if (var == MON_HAS_MAX_MOVES) gTasks[taskId].tState = T_EVOSTATE_REPLACE_MOVE; else if (var == MON_ALREADY_KNOWS_MOVE) break; else gTasks[taskId].tState = T_EVOSTATE_LEARNED_MOVE; } else { PlayBGM(MUS_EVOLUTION); DrawTextOnTradeWindow(0, gText_CommunicationStandby5, 1); gTasks[taskId].tState++; } } break; case T_EVOSTATE_END: if (!IsTextPrinterActive(0)) { DestroyTask(taskId); Free(sEvoStructPtr); sEvoStructPtr = NULL; gTextFlags.useAlternateDownArrow = 0; SetMainCallback2(gCB2_AfterEvolution); } break; case T_EVOSTATE_CANCEL: if (!gTasks[sEvoGraphicsTaskId].isActive) { m4aMPlayAllStop(); BeginNormalPaletteFade((1 << (gSprites[sEvoStructPtr->preEvoSpriteId].oam.paletteNum + 16)) | (0x4001C), 0, 0x10, 0, RGB_WHITE); gTasks[taskId].tState++; } break; case T_EVOSTATE_CANCEL_MON_ANIM: if (!gPaletteFade.active) { EvoScene_DoMonAnimAndCry(sEvoStructPtr->preEvoSpriteId, gTasks[taskId].tPreEvoSpecies); gTasks[taskId].tState++; } break; case T_EVOSTATE_CANCEL_MSG: if (EvoScene_IsMonAnimFinished(sEvoStructPtr->preEvoSpriteId)) { StringExpandPlaceholders(gStringVar4, gText_EllipsisQuestionMark); DrawTextOnTradeWindow(0, gStringVar4, 1); gTasks[taskId].tEvoWasStopped = TRUE; gTasks[taskId].tState = T_EVOSTATE_TRY_LEARN_MOVE; } break; case T_EVOSTATE_LEARNED_MOVE: if (!IsTextPrinterActive(0) && !IsSEPlaying()) { BufferMoveToLearnIntoBattleTextBuff2(); PlayFanfare(MUS_LEVEL_UP); BattleStringExpandPlaceholdersToDisplayedString(gBattleStringsTable[STRINGID_PKMNLEARNEDMOVE - BATTLESTRINGS_ID_ADDER]); DrawTextOnTradeWindow(0, gDisplayedStringBattle, 1); gTasks[taskId].tLearnsFirstMove = 0x40; // re-used as a counter gTasks[taskId].tState++; } break; case T_EVOSTATE_TRY_LEARN_ANOTHER_MOVE: if (!IsTextPrinterActive(0) && IsFanfareTaskInactive() == TRUE && --gTasks[taskId].tLearnsFirstMove == 0) gTasks[taskId].tState = T_EVOSTATE_TRY_LEARN_MOVE; break; case T_EVOSTATE_REPLACE_MOVE: switch (gTasks[taskId].tLearnMoveState) { case T_MVSTATE_INTRO_MSG_1: if (!IsTextPrinterActive(0) && !IsSEPlaying()) { // "{mon} is trying to learn {move}" BufferMoveToLearnIntoBattleTextBuff2(); BattleStringExpandPlaceholdersToDisplayedString(gBattleStringsTable[STRINGID_TRYTOLEARNMOVE1 - BATTLESTRINGS_ID_ADDER]); DrawTextOnTradeWindow(0, gDisplayedStringBattle, 1); gTasks[taskId].tLearnMoveState++; } break; case T_MVSTATE_INTRO_MSG_2: if (!IsTextPrinterActive(0) && !IsSEPlaying()) { // "But, {mon} can't learn more than four moves" BattleStringExpandPlaceholdersToDisplayedString(gBattleStringsTable[STRINGID_TRYTOLEARNMOVE2 - BATTLESTRINGS_ID_ADDER]); DrawTextOnTradeWindow(0, gDisplayedStringBattle, 1); gTasks[taskId].tLearnMoveState++; } break; case T_MVSTATE_INTRO_MSG_3: if (!IsTextPrinterActive(0) && !IsSEPlaying()) { // "Delete a move to make room for {move}?" BattleStringExpandPlaceholdersToDisplayedString(gBattleStringsTable[STRINGID_TRYTOLEARNMOVE3 - BATTLESTRINGS_ID_ADDER]); DrawTextOnTradeWindow(0, gDisplayedStringBattle, 1); gTasks[taskId].tLearnMoveYesState = T_MVSTATE_SHOW_MOVE_SELECT; gTasks[taskId].tLearnMoveNoState = T_MVSTATE_ASK_CANCEL; gTasks[taskId].tLearnMoveState++; } case T_MVSTATE_PRINT_YES_NO: if (!IsTextPrinterActive(0) && !IsSEPlaying()) { LoadUserWindowBorderGfx(0, 0xA8, 0xE0); CreateYesNoMenu(&gTradeEvolutionSceneYesNoWindowTemplate, 0xA8, 0xE, 0); sEvoCursorPos = 0; gTasks[taskId].tLearnMoveState++; sEvoCursorPos = 0; } break; case T_MVSTATE_HANDLE_YES_NO: switch (Menu_ProcessInputNoWrapClearOnChoose()) { case 0: // YES sEvoCursorPos = 0; BattleStringExpandPlaceholdersToDisplayedString(gBattleStringsTable[STRINGID_EMPTYSTRING3 - BATTLESTRINGS_ID_ADDER]); DrawTextOnTradeWindow(0, gDisplayedStringBattle, 1); gTasks[taskId].tLearnMoveState = gTasks[taskId].tLearnMoveYesState; if (gTasks[taskId].tLearnMoveState == T_MVSTATE_SHOW_MOVE_SELECT) BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 0x10, RGB_BLACK); break; case 1: // NO case MENU_B_PRESSED: sEvoCursorPos = 1; BattleStringExpandPlaceholdersToDisplayedString(gBattleStringsTable[STRINGID_EMPTYSTRING3 - BATTLESTRINGS_ID_ADDER]); DrawTextOnTradeWindow(0, gDisplayedStringBattle, 1); gTasks[taskId].tLearnMoveState = gTasks[taskId].tLearnMoveNoState; break; } break; case T_MVSTATE_SHOW_MOVE_SELECT: if (!gPaletteFade.active) { if (gWirelessCommType) DestroyWirelessStatusIndicatorSprite(); Free(GetBgTilemapBuffer(3)); Free(GetBgTilemapBuffer(1)); Free(GetBgTilemapBuffer(0)); FreeAllWindowBuffers(); ShowSelectMovePokemonSummaryScreen(gPlayerParty, gTasks[taskId].tPartyId, gPlayerPartyCount - 1, CB2_TradeEvolutionSceneLoadGraphics, gMoveToLearn); gTasks[taskId].tLearnMoveState++; } break; case T_MVSTATE_HANDLE_MOVE_SELECT: if (!gPaletteFade.active && gMain.callback2 == CB2_TradeEvolutionSceneUpdate) { var = GetMoveSlotToReplace(); if (var == MAX_MON_MOVES) { // Didn't select move slot gTasks[taskId].tLearnMoveState = T_MVSTATE_ASK_CANCEL; } else { // Selected move to forget u16 move = GetMonData(mon, var + MON_DATA_MOVE1); if (IsHMMove2(move)) { // Can't forget HMs BattleStringExpandPlaceholdersToDisplayedString(gBattleStringsTable[STRINGID_HMMOVESCANTBEFORGOTTEN - BATTLESTRINGS_ID_ADDER]); DrawTextOnTradeWindow(0, gDisplayedStringBattle, 1); gTasks[taskId].tLearnMoveState = T_MVSTATE_RETRY_AFTER_HM; } else { // Forget move PREPARE_MOVE_BUFFER(gBattleTextBuff2, move) RemoveMonPPBonus(mon, var); SetMonMoveSlot(mon, gMoveToLearn, var); BattleStringExpandPlaceholdersToDisplayedString(gBattleStringsTable[STRINGID_123POOF - BATTLESTRINGS_ID_ADDER]); DrawTextOnTradeWindow(0, gDisplayedStringBattle, 1); gTasks[taskId].tLearnMoveState++; } } } break; case T_MVSTATE_FORGET_MSG: if (!IsTextPrinterActive(0) && !IsSEPlaying()) { BattleStringExpandPlaceholdersToDisplayedString(gBattleStringsTable[STRINGID_PKMNFORGOTMOVE - BATTLESTRINGS_ID_ADDER]); DrawTextOnTradeWindow(0, gDisplayedStringBattle, 1); gTasks[taskId].tLearnMoveState++; } break; case T_MVSTATE_LEARNED_MOVE: if (!IsTextPrinterActive(0) && !IsSEPlaying()) { BattleStringExpandPlaceholdersToDisplayedString(gBattleStringsTable[STRINGID_ANDELLIPSIS - BATTLESTRINGS_ID_ADDER]); DrawTextOnTradeWindow(0, gDisplayedStringBattle, 1); gTasks[taskId].tState = T_EVOSTATE_LEARNED_MOVE; } break; case T_MVSTATE_ASK_CANCEL: BattleStringExpandPlaceholdersToDisplayedString(gBattleStringsTable[STRINGID_STOPLEARNINGMOVE - BATTLESTRINGS_ID_ADDER]); DrawTextOnTradeWindow(0, gDisplayedStringBattle, 1); gTasks[taskId].tLearnMoveYesState = T_MVSTATE_CANCEL; gTasks[taskId].tLearnMoveNoState = T_MVSTATE_INTRO_MSG_1; gTasks[taskId].tLearnMoveState = T_MVSTATE_PRINT_YES_NO; break; case T_MVSTATE_CANCEL: BattleStringExpandPlaceholdersToDisplayedString(gBattleStringsTable[STRINGID_DIDNOTLEARNMOVE - BATTLESTRINGS_ID_ADDER]); DrawTextOnTradeWindow(0, gDisplayedStringBattle, 1); gTasks[taskId].tState = T_EVOSTATE_TRY_LEARN_MOVE; break; case T_MVSTATE_RETRY_AFTER_HM: if (!IsTextPrinterActive(0) && !IsSEPlaying()) gTasks[taskId].tLearnMoveState = T_MVSTATE_SHOW_MOVE_SELECT; break; } break; } } #undef tState #undef tPreEvoSpecies #undef tPostEvoSpecies #undef tCanStop #undef tBits #undef tLearnsFirstMove #undef tLearnMoveState #undef tLearnMoveYesState #undef tLearnMoveNoState #undef tEvoWasStopped #undef tPartyId static void EvoDummyFunc(void) { } static void VBlankCB_EvolutionScene(void) { SetGpuReg(REG_OFFSET_BG0HOFS, gBattle_BG0_X); SetGpuReg(REG_OFFSET_BG0VOFS, gBattle_BG0_Y); SetGpuReg(REG_OFFSET_BG1HOFS, gBattle_BG1_X); SetGpuReg(REG_OFFSET_BG1VOFS, gBattle_BG1_Y); SetGpuReg(REG_OFFSET_BG2HOFS, gBattle_BG2_X); SetGpuReg(REG_OFFSET_BG2VOFS, gBattle_BG2_Y); SetGpuReg(REG_OFFSET_BG3HOFS, gBattle_BG3_X); SetGpuReg(REG_OFFSET_BG3VOFS, gBattle_BG3_Y); LoadOam(); ProcessSpriteCopyRequests(); TransferPlttBuffer(); ScanlineEffect_InitHBlankDmaTransfer(); } static void VBlankCB_TradeEvolutionScene(void) { SetGpuReg(REG_OFFSET_BG0HOFS, gBattle_BG0_X); SetGpuReg(REG_OFFSET_BG0VOFS, gBattle_BG0_Y); SetGpuReg(REG_OFFSET_BG1HOFS, gBattle_BG1_X); SetGpuReg(REG_OFFSET_BG1VOFS, gBattle_BG1_Y); SetGpuReg(REG_OFFSET_BG2HOFS, gBattle_BG2_X); SetGpuReg(REG_OFFSET_BG2VOFS, gBattle_BG2_Y); SetGpuReg(REG_OFFSET_BG3HOFS, gBattle_BG3_X); SetGpuReg(REG_OFFSET_BG3VOFS, gBattle_BG3_Y); LoadOam(); ProcessSpriteCopyRequests(); TransferPlttBuffer(); ScanlineEffect_InitHBlankDmaTransfer(); } #define tCycleTimer data[0] #define tPalStage data[1] #define tControlStage data[2] #define tNumCycles data[3] #define tStartTimer data[5] #define tPaused data[6] // See comments above sBgAnim_PaletteControl #define START_PAL sBgAnim_PaletteControl[tControlStage][0] #define END_PAL sBgAnim_PaletteControl[tControlStage][1] #define CYCLES sBgAnim_PaletteControl[tControlStage][2] #define DELAY sBgAnim_PaletteControl[tControlStage][3] // Cycles the background through a set range of palettes in a series // of stages, each stage having a different palette range and timing static void Task_UpdateBgPalette(u8 taskId) { s16 *data = gTasks[taskId].data; if (tPaused) return; if (tStartTimer++ < 20) return; if (tCycleTimer++ > DELAY) { if (END_PAL == tPalStage) { // Reached final palette in current stage, completed a 'cycle' // If this is the final cycle for this stage, move to the next stage tNumCycles++; if (tNumCycles == CYCLES) { tNumCycles = 0; tControlStage++; } tPalStage = START_PAL; } else { // Haven't reached final palette in current stage, load the current palette LoadPalette(&sBgAnimPal[tPalStage * 16], 0xA0, 0x20); tCycleTimer = 0; tPalStage++; } } if (tControlStage == (int)ARRAY_COUNT(sBgAnim_PaletteControl[0])) DestroyTask(taskId); } #undef tCycleTimer #undef tPalStage #undef tControlStage #undef tNumCycles #undef tStartTimer #undef START_PAL #undef END_PAL #undef CYCLES #undef DELAY #define tIsLink data[2] static void CreateBgAnimTask(bool8 isLink) { u8 taskId = CreateTask(Task_AnimateBg, 7); if (!isLink) gTasks[taskId].tIsLink = FALSE; else gTasks[taskId].tIsLink = TRUE; } static void Task_AnimateBg(u8 taskId) { u16 *outer_X, *outer_Y; u16 *inner_X = &gBattle_BG1_X; u16 *inner_Y = &gBattle_BG1_Y; if (!gTasks[taskId].tIsLink) { outer_X = &gBattle_BG2_X; outer_Y = &gBattle_BG2_Y; } else { outer_X = &gBattle_BG3_X; outer_Y = &gBattle_BG3_Y; } gTasks[taskId].data[0] = (gTasks[taskId].data[0] + 5) & 0xFF; gTasks[taskId].data[1] = (gTasks[taskId].data[0] + 0x80) & 0xFF; *inner_X = Cos(gTasks[taskId].data[0], 4) + 8; *inner_Y = Sin(gTasks[taskId].data[0], 4) + 16; *outer_X = Cos(gTasks[taskId].data[1], 4) + 8; *outer_Y = Sin(gTasks[taskId].data[1], 4) + 16; if (!FuncIsActiveTask(Task_UpdateBgPalette)) { DestroyTask(taskId); *inner_X = 0; *inner_Y = 0; *outer_X = 256; *outer_Y = 0; } } #undef tIsLink static void InitMovingBgPalette(u16 *palette) { s32 i, j; for (i = 0; i < (int)ARRAY_COUNT(sBgAnim_PalIndexes); i++) { for (j = 0; j < 16; j++) { palette[i * 16 + j] = sBgAnim_Pal[sBgAnim_PalIndexes[i][j]]; } } } static void StartBgAnimation(bool8 isLink) { u8 innerBgId, outerBgId; sBgAnimPal = AllocZeroed(0x640); InitMovingBgPalette(sBgAnimPal); if (!isLink) innerBgId = 1, outerBgId = 2; else innerBgId = 1, outerBgId = 3; LoadPalette(sBgAnim_Intro_Pal, 0xA0, 0x20); DecompressAndLoadBgGfxUsingHeap(1, sBgAnim_Gfx, FALSE, 0, 0); CopyToBgTilemapBuffer(innerBgId, sBgAnim_Inner_Tilemap, 0, 0); CopyToBgTilemapBuffer(outerBgId, sBgAnim_Outer_Tilemap, 0, 0); CopyBgTilemapBufferToVram(innerBgId); CopyBgTilemapBufferToVram(outerBgId); if (!isLink) { SetGpuReg(REG_OFFSET_BLDCNT, BLDCNT_TGT1_BG1 | BLDCNT_EFFECT_BLEND | BLDCNT_TGT2_BG2); SetGpuReg(REG_OFFSET_BLDALPHA, BLDALPHA_BLEND(8, 8)); SetGpuReg(REG_OFFSET_DISPCNT, DISPCNT_OBJ_ON | DISPCNT_BG2_ON | DISPCNT_BG1_ON | DISPCNT_BG0_ON | DISPCNT_OBJ_1D_MAP); SetBgAttribute(innerBgId, BG_ATTR_PRIORITY, 2); SetBgAttribute(outerBgId, BG_ATTR_PRIORITY, 2); ShowBg(1); ShowBg(2); } else { SetGpuReg(REG_OFFSET_BLDCNT, BLDCNT_TGT1_BG1 | BLDCNT_EFFECT_BLEND | BLDCNT_TGT2_BG3); SetGpuReg(REG_OFFSET_BLDALPHA, BLDALPHA_BLEND(8, 8)); SetGpuReg(REG_OFFSET_DISPCNT, DISPCNT_OBJ_ON | DISPCNT_BG3_ON | DISPCNT_BG1_ON | DISPCNT_BG0_ON | DISPCNT_OBJ_1D_MAP); } CreateTask(Task_UpdateBgPalette, 5); CreateBgAnimTask(isLink); } // Unused static void PauseBgPaletteAnim(void) { u8 taskId = FindTaskIdByFunc(Task_UpdateBgPalette); if (taskId != TASK_NONE) gTasks[taskId].tPaused = TRUE; FillPalette(0, 0xA0, 0x20); } #undef tPaused static void StopBgAnimation(void) { u8 taskId; if ((taskId = FindTaskIdByFunc(Task_UpdateBgPalette)) != TASK_NONE) DestroyTask(taskId); if ((taskId = FindTaskIdByFunc(Task_AnimateBg)) != TASK_NONE) DestroyTask(taskId); FillPalette(0, 0xA0, 0x20); RestoreBgAfterAnim(); } static void RestoreBgAfterAnim(void) { SetGpuReg(REG_OFFSET_BLDCNT, 0); gBattle_BG1_X = 0; gBattle_BG1_Y = 0; gBattle_BG2_X = 0; SetBgAttribute(1, BG_ATTR_PRIORITY, GetBattleBgTemplateData(1, 5)); SetBgAttribute(2, BG_ATTR_PRIORITY, GetBattleBgTemplateData(2, 5)); SetGpuReg(REG_OFFSET_DISPCNT, DISPCNT_OBJ_ON | DISPCNT_BG3_ON | DISPCNT_BG0_ON | DISPCNT_OBJ_1D_MAP); Free(sBgAnimPal); } static void EvoScene_DoMonAnimAndCry(u8 monSpriteId, u16 speciesId) { DoMonFrontSpriteAnimation(&gSprites[monSpriteId], speciesId, FALSE, 0); } static bool32 EvoScene_IsMonAnimFinished(u8 monSpriteId) { if (gSprites[monSpriteId].callback == SpriteCallbackDummy) return TRUE; return FALSE; }