#include "global.h" #include "battle.h" #include "battle_transition.h" #include "battle_transition_frontier.h" #include "bg.h" #include "decompress.h" #include "event_object_movement.h" #include "field_camera.h" #include "field_effect.h" #include "field_weather.h" #include "gpu_regs.h" #include "main.h" #include "malloc.h" #include "overworld.h" #include "palette.h" #include "random.h" #include "scanline_effect.h" #include "sound.h" #include "sprite.h" #include "task.h" #include "trig.h" #include "util.h" #include "constants/field_effects.h" #include "constants/songs.h" #include "constants/trainers.h" #include "constants/rgb.h" #define PALTAG_UNUSED_MUGSHOT 0x100A #define B_TRANS_DMA_FLAGS (1 | ((DMA_SRC_INC | DMA_DEST_FIXED | DMA_REPEAT | DMA_16BIT | DMA_START_HBLANK | DMA_ENABLE) << 16)) // Used by each transition task to determine which of its functions to call #define tState data[0] // Below are data defines for InitBlackWipe and UpdateBlackWipe, for the TransitionData data array. // These will be re-used by any transitions that use these functions. #define tWipeStartX data[0] #define tWipeStartY data[1] #define tWipeCurrX data[2] #define tWipeCurrY data[3] #define tWipeEndX data[4] #define tWipeEndY data[5] #define tWipeXMove data[6] #define tWipeYMove data[7] #define tWipeXDist data[8] #define tWipeYDist data[9] #define tWipeTemp data[10] #define SET_TILE(ptr, posY, posX, tile) \ { \ u32 index = (posY) * 32 + posX; \ ptr[index] = tile | (0xF0 << 8); \ } struct TransitionData { vu8 VBlank_DMA; u16 WININ; u16 WINOUT; u16 WIN0H; u16 WIN0V; u16 unused1; u16 unused2; u16 BLDCNT; u16 BLDALPHA; u16 BLDY; s16 cameraX; s16 cameraY; s16 BG0HOFS_Lower; s16 BG0HOFS_Upper; s16 BG0VOFS; // used but not set s16 unused3; s16 counter; s16 unused4; s16 data[11]; }; struct RectangularSpiralLine { u8 state; s16 position; u8 moveIdx; s16 reboundPosition; bool8 outward; }; typedef bool8 (*TransitionStateFunc)(struct Task *task); typedef bool8 (*TransitionSpriteCallback)(struct Sprite *sprite); static bool8 Transition_StartIntro(struct Task *); static bool8 Transition_WaitForIntro(struct Task *); static bool8 Transition_StartMain(struct Task *); static bool8 Transition_WaitForMain(struct Task *); static void LaunchBattleTransitionTask(u8); static void Task_BattleTransition(u8); static void Task_Intro(u8); static void Task_Blur(u8); static void Task_Swirl(u8); static void Task_Shuffle(u8); static void Task_BigPokeball(u8); static void Task_PokeballsTrail(u8); static void Task_ClockwiseWipe(u8); static void Task_Ripple(u8); static void Task_Wave(u8); static void Task_Slice(u8); static void Task_WhiteBarsFade(u8); static void Task_GridSquares(u8); static void Task_AngledWipes(u8); static void Task_Sidney(u8); static void Task_Phoebe(u8); static void Task_Glacia(u8); static void Task_Drake(u8); static void Task_Champion(u8); static void Task_Aqua(u8); static void Task_Magma(u8); static void Task_Regice(u8); static void Task_Registeel(u8); static void Task_Regirock(u8); static void Task_Kyogre(u8); static void Task_Groudon(u8); static void Task_Rayquaza(u8); static void Task_ShredSplit(u8); static void Task_Blackhole(u8); static void Task_BlackholePulsate(u8); static void Task_RectangularSpiral(u8); static void Task_FrontierLogoWiggle(u8); static void Task_FrontierLogoWave(u8); static void Task_FrontierSquares(u8); static void Task_FrontierSquaresScroll(u8); static void Task_FrontierSquaresSpiral(u8); static void VBlankCB_BattleTransition(void); static void VBlankCB_Swirl(void); static void HBlankCB_Swirl(void); static void VBlankCB_Shuffle(void); static void HBlankCB_Shuffle(void); static void VBlankCB_PatternWeave(void); static void VBlankCB_CircularMask(void); static void VBlankCB_ClockwiseWipe(void); static void VBlankCB_Ripple(void); static void HBlankCB_Ripple(void); static void VBlankCB_FrontierLogoWave(void); static void HBlankCB_FrontierLogoWave(void); static void VBlankCB_Wave(void); static void VBlankCB_Slice(void); static void HBlankCB_Slice(void); static void VBlankCB_WhiteBarsFade(void); static void VBlankCB_WhiteBarsFade_Blend(void); static void HBlankCB_WhiteBarsFade(void); static void VBlankCB_AngledWipes(void); static void VBlankCB_Rayquaza(void); static bool8 Blur_Init(struct Task *); static bool8 Blur_Main(struct Task *); static bool8 Blur_End(struct Task *); static bool8 Swirl_Init(struct Task *); static bool8 Swirl_End(struct Task *); static bool8 Shuffle_Init(struct Task *); static bool8 Shuffle_End(struct Task *); static bool8 Aqua_Init(struct Task *); static bool8 Aqua_SetGfx(struct Task *); static bool8 Magma_Init(struct Task *); static bool8 Magma_SetGfx(struct Task *); static bool8 FramesCountdown(struct Task *); static bool8 Regi_Init(struct Task *); static bool8 Regice_SetGfx(struct Task *); static bool8 Registeel_SetGfx(struct Task *); static bool8 Regirock_SetGfx(struct Task *); static bool8 WeatherTrio_BgFadeBlack(struct Task *); static bool8 WeatherTrio_WaitFade(struct Task *); static bool8 Kyogre_Init(struct Task *); static bool8 Kyogre_PaletteFlash(struct Task *); static bool8 Kyogre_PaletteBrighten(struct Task *); static bool8 Groudon_Init(struct Task *); static bool8 Groudon_PaletteFlash(struct Task *); static bool8 Groudon_PaletteBrighten(struct Task *); static bool8 WeatherDuo_FadeOut(struct Task *); static bool8 WeatherDuo_End(struct Task *); static bool8 BigPokeball_Init(struct Task *); static bool8 BigPokeball_SetGfx(struct Task *); static bool8 PatternWeave_Blend1(struct Task *); static bool8 PatternWeave_Blend2(struct Task *); static bool8 PatternWeave_FinishAppear(struct Task *); static bool8 PatternWeave_CircularMask(struct Task *); static bool8 PokeballsTrail_Init(struct Task *); static bool8 PokeballsTrail_Main(struct Task *); static bool8 PokeballsTrail_End(struct Task *); static bool8 ClockwiseWipe_Init(struct Task *); static bool8 ClockwiseWipe_TopRight(struct Task *); static bool8 ClockwiseWipe_Right(struct Task *); static bool8 ClockwiseWipe_Bottom(struct Task *); static bool8 ClockwiseWipe_Left(struct Task *); static bool8 ClockwiseWipe_TopLeft(struct Task *); static bool8 ClockwiseWipe_End(struct Task *); static bool8 Ripple_Init(struct Task *); static bool8 Ripple_Main(struct Task *); static bool8 Wave_Init(struct Task *); static bool8 Wave_Main(struct Task *); static bool8 Wave_End(struct Task *); static bool8 Slice_Init(struct Task *); static bool8 Slice_Main(struct Task *); static bool8 Slice_End(struct Task *); static bool8 WhiteBarsFade_Init(struct Task *); static bool8 WhiteBarsFade_StartBars(struct Task *); static bool8 WhiteBarsFade_WaitBars(struct Task *); static bool8 WhiteBarsFade_BlendToBlack(struct Task *); static bool8 WhiteBarsFade_End(struct Task *); static bool8 GridSquares_Init(struct Task *); static bool8 GridSquares_Main(struct Task *); static bool8 GridSquares_End(struct Task *); static bool8 AngledWipes_Init(struct Task *); static bool8 AngledWipes_SetWipeData(struct Task *); static bool8 AngledWipes_DoWipe(struct Task *); static bool8 AngledWipes_TryEnd(struct Task *); static bool8 AngledWipes_StartNext(struct Task *); static bool8 ShredSplit_Init(struct Task *); static bool8 ShredSplit_Main(struct Task *); static bool8 ShredSplit_BrokenCheck(struct Task *); static bool8 ShredSplit_End(struct Task *); static bool8 Blackhole_Init(struct Task *); static bool8 Blackhole_Vibrate(struct Task *); static bool8 Blackhole_GrowEnd(struct Task *); static bool8 BlackholePulsate_Main(struct Task *); static bool8 RectangularSpiral_Init(struct Task *); static bool8 RectangularSpiral_Main(struct Task *); static bool8 RectangularSpiral_End(struct Task *); static bool8 FrontierLogoWiggle_Init(struct Task *); static bool8 FrontierLogoWiggle_SetGfx(struct Task *); static bool8 FrontierLogoWave_Init(struct Task *); static bool8 FrontierLogoWave_SetGfx(struct Task *); static bool8 FrontierLogoWave_InitScanline(struct Task *); static bool8 FrontierLogoWave_Main(struct Task *); static bool8 Rayquaza_Init(struct Task *); static bool8 Rayquaza_SetGfx(struct Task *); static bool8 Rayquaza_PaletteFlash(struct Task *); static bool8 Rayquaza_FadeToBlack(struct Task *); static bool8 Rayquaza_WaitFade(struct Task *); static bool8 Rayquaza_SetBlack(struct Task *); static bool8 Rayquaza_TriRing(struct Task *); static bool8 FrontierSquares_Init(struct Task *); static bool8 FrontierSquares_Draw(struct Task *); static bool8 FrontierSquares_Shrink(struct Task *); static bool8 FrontierSquares_End(struct Task *); static bool8 FrontierSquaresSpiral_Init(struct Task *); static bool8 FrontierSquaresSpiral_Outward(struct Task *); static bool8 FrontierSquaresSpiral_SetBlack(struct Task *); static bool8 FrontierSquaresSpiral_Inward(struct Task *); static bool8 FrontierSquaresScroll_Init(struct Task *); static bool8 FrontierSquaresScroll_Draw(struct Task *); static bool8 FrontierSquaresScroll_SetBlack(struct Task *); static bool8 FrontierSquaresScroll_Erase(struct Task *); static bool8 FrontierSquaresScroll_End(struct Task *); static bool8 Mugshot_Init(struct Task *); static bool8 Mugshot_SetGfx(struct Task *); static bool8 Mugshot_ShowBanner(struct Task *); static bool8 Mugshot_StartOpponentSlide(struct Task *); static bool8 Mugshot_WaitStartPlayerSlide(struct Task *); static bool8 Mugshot_WaitPlayerSlide(struct Task *); static bool8 Mugshot_GradualWhiteFade(struct Task *); static bool8 Mugshot_InitFadeWhiteToBlack(struct Task *); static bool8 Mugshot_FadeToBlack(struct Task *); static bool8 Mugshot_End(struct Task *); static void DoMugshotTransition(u8); static void Mugshots_CreateTrainerPics(struct Task *); static void VBlankCB_Mugshots(void); static void VBlankCB_MugshotsFadeOut(void); static void HBlankCB_Mugshots(void); static void InitTransitionData(void); static void FadeScreenBlack(void); static void CreateIntroTask(s16, s16, s16, s16, s16); static void SetCircularMask(u16 *, s16, s16, s16); static void SetSinWave(s16 *, s16, s16, s16, s16, s16); static void GetBg0TilemapDst(u16 **); static void InitBlackWipe(s16 *, s16, s16, s16, s16, s16, s16); static bool8 UpdateBlackWipe(s16 *, bool8, bool8); static void SetTrainerPicSlideDirection(s16, s16); static void IncrementTrainerPicState(s16); static s16 IsTrainerPicSlideDone(s16); static bool8 TransitionIntro_FadeToGray(struct Task *); static bool8 TransitionIntro_FadeFromGray(struct Task *); static bool8 IsIntroTaskDone(void); static bool16 UpdateRectangularSpiralLine(const s16 * const *, struct RectangularSpiralLine *); static void SpriteCB_FldEffPokeballTrail(struct Sprite *); static void SpriteCB_MugshotTrainerPic(struct Sprite *); static void SpriteCB_WhiteBarFade(struct Sprite *); static bool8 MugshotTrainerPic_Pause(struct Sprite *); static bool8 MugshotTrainerPic_Init(struct Sprite *); static bool8 MugshotTrainerPic_Slide(struct Sprite *); static bool8 MugshotTrainerPic_SlideSlow(struct Sprite *); static bool8 MugshotTrainerPic_SlideOffscreen(struct Sprite *); static s16 sDebug_RectangularSpiralData; static u8 sTestingTransitionId; static u8 sTestingTransitionState; static struct RectangularSpiralLine sRectangularSpiralLines[4]; EWRAM_DATA static struct TransitionData *sTransitionData = NULL; static const u32 sBigPokeball_Tileset[] = INCBIN_U32("graphics/battle_transitions/big_pokeball.4bpp"); static const u32 sPokeballTrail_Tileset[] = INCBIN_U32("graphics/battle_transitions/pokeball_trail.4bpp"); static const u8 sPokeball_Gfx[] = INCBIN_U8("graphics/battle_transitions/pokeball.4bpp"); static const u32 sEliteFour_Tileset[] = INCBIN_U32("graphics/battle_transitions/elite_four_bg.4bpp"); static const u8 sUnusedBrendan_Gfx[] = INCBIN_U8("graphics/battle_transitions/unused_brendan.4bpp"); static const u8 sUnusedLass_Gfx[] = INCBIN_U8("graphics/battle_transitions/unused_lass.4bpp"); static const u32 sShrinkingBoxTileset[] = INCBIN_U32("graphics/battle_transitions/shrinking_box.4bpp"); static const u16 sEvilTeam_Palette[] = INCBIN_U16("graphics/battle_transitions/evil_team.gbapal"); static const u32 sTeamAqua_Tileset[] = INCBIN_U32("graphics/battle_transitions/team_aqua.4bpp.lz"); static const u32 sTeamAqua_Tilemap[] = INCBIN_U32("graphics/battle_transitions/team_aqua.bin.lz"); static const u32 sTeamMagma_Tileset[] = INCBIN_U32("graphics/battle_transitions/team_magma.4bpp.lz"); static const u32 sTeamMagma_Tilemap[] = INCBIN_U32("graphics/battle_transitions/team_magma.bin.lz"); static const u32 sRegis_Tileset[] = INCBIN_U32("graphics/battle_transitions/regis.4bpp"); static const u16 sRegice_Palette[] = INCBIN_U16("graphics/battle_transitions/regice.gbapal"); static const u16 sRegisteel_Palette[] = INCBIN_U16("graphics/battle_transitions/registeel.gbapal"); static const u16 sRegirock_Palette[] = INCBIN_U16("graphics/battle_transitions/regirock.gbapal"); static const u32 sRegice_Tilemap[] = INCBIN_U32("graphics/battle_transitions/regice.bin"); static const u32 sRegisteel_Tilemap[] = INCBIN_U32("graphics/battle_transitions/registeel.bin"); static const u32 sRegirock_Tilemap[] = INCBIN_U32("graphics/battle_transitions/regirock.bin"); static const u16 sUnused_Palette[] = INCBIN_U16("graphics/battle_transitions/unused.gbapal"); static const u32 sKyogre_Tileset[] = INCBIN_U32("graphics/battle_transitions/kyogre.4bpp.lz"); static const u32 sKyogre_Tilemap[] = INCBIN_U32("graphics/battle_transitions/kyogre.bin.lz"); static const u32 sGroudon_Tileset[] = INCBIN_U32("graphics/battle_transitions/groudon.4bpp.lz"); static const u32 sGroudon_Tilemap[] = INCBIN_U32("graphics/battle_transitions/groudon.bin.lz"); static const u16 sKyogre1_Palette[] = INCBIN_U16("graphics/battle_transitions/kyogre_pt1.gbapal"); static const u16 sKyogre2_Palette[] = INCBIN_U16("graphics/battle_transitions/kyogre_pt2.gbapal"); static const u16 sGroudon1_Palette[] = INCBIN_U16("graphics/battle_transitions/groudon_pt1.gbapal"); static const u16 sGroudon2_Palette[] = INCBIN_U16("graphics/battle_transitions/groudon_pt2.gbapal"); static const u16 sRayquaza_Palette[] = INCBIN_U16("graphics/battle_transitions/rayquaza.gbapal"); static const u32 sRayquaza_Tileset[] = INCBIN_U32("graphics/battle_transitions/rayquaza.4bpp"); static const u32 sRayquaza_Tilemap[] = INCBIN_U32("graphics/battle_transitions/rayquaza.bin"); static const u16 sFrontierLogo_Palette[] = INCBIN_U16("graphics/battle_transitions/frontier_logo.gbapal"); static const u32 sFrontierLogo_Tileset[] = INCBIN_U32("graphics/battle_transitions/frontier_logo.4bpp.lz"); static const u32 sFrontierLogo_Tilemap[] = INCBIN_U32("graphics/battle_transitions/frontier_logo.bin.lz"); static const u16 sFrontierSquares_Palette[] = INCBIN_U16("graphics/battle_transitions/frontier_squares_blanktiles.gbapal"); static const u32 sFrontierSquares_FilledBg_Tileset[] = INCBIN_U32("graphics/battle_transitions/frontier_square_1.4bpp.lz"); static const u32 sFrontierSquares_EmptyBg_Tileset[] = INCBIN_U32("graphics/battle_transitions/frontier_square_2.4bpp.lz"); static const u32 sFrontierSquares_Shrink1_Tileset[] = INCBIN_U32("graphics/battle_transitions/frontier_square_3.4bpp.lz"); static const u32 sFrontierSquares_Shrink2_Tileset[] = INCBIN_U32("graphics/battle_transitions/frontier_square_4.4bpp.lz"); static const u32 sFrontierSquares_Tilemap[] = INCBIN_U32("graphics/battle_transitions/frontier_squares.bin"); // All battle transitions use the same intro static const TaskFunc sTasks_Intro[B_TRANSITION_COUNT] = { [0 ... B_TRANSITION_COUNT - 1] = &Task_Intro }; // After the intro each transition has a unique main task. // This task will call the functions that do the transition effects. static const TaskFunc sTasks_Main[B_TRANSITION_COUNT] = { [B_TRANSITION_BLUR] = Task_Blur, [B_TRANSITION_SWIRL] = Task_Swirl, [B_TRANSITION_SHUFFLE] = Task_Shuffle, [B_TRANSITION_BIG_POKEBALL] = Task_BigPokeball, [B_TRANSITION_POKEBALLS_TRAIL] = Task_PokeballsTrail, [B_TRANSITION_CLOCKWISE_WIPE] = Task_ClockwiseWipe, [B_TRANSITION_RIPPLE] = Task_Ripple, [B_TRANSITION_WAVE] = Task_Wave, [B_TRANSITION_SLICE] = Task_Slice, [B_TRANSITION_WHITE_BARS_FADE] = Task_WhiteBarsFade, [B_TRANSITION_GRID_SQUARES] = Task_GridSquares, [B_TRANSITION_ANGLED_WIPES] = Task_AngledWipes, [B_TRANSITION_SIDNEY] = Task_Sidney, [B_TRANSITION_PHOEBE] = Task_Phoebe, [B_TRANSITION_GLACIA] = Task_Glacia, [B_TRANSITION_DRAKE] = Task_Drake, [B_TRANSITION_CHAMPION] = Task_Champion, [B_TRANSITION_AQUA] = Task_Aqua, [B_TRANSITION_MAGMA] = Task_Magma, [B_TRANSITION_REGICE] = Task_Regice, [B_TRANSITION_REGISTEEL] = Task_Registeel, [B_TRANSITION_REGIROCK] = Task_Regirock, [B_TRANSITION_KYOGRE] = Task_Kyogre, [B_TRANSITION_GROUDON] = Task_Groudon, [B_TRANSITION_RAYQUAZA] = Task_Rayquaza, [B_TRANSITION_SHRED_SPLIT] = Task_ShredSplit, [B_TRANSITION_BLACKHOLE] = Task_Blackhole, [B_TRANSITION_BLACKHOLE_PULSATE] = Task_BlackholePulsate, [B_TRANSITION_RECTANGULAR_SPIRAL] = Task_RectangularSpiral, [B_TRANSITION_FRONTIER_LOGO_WIGGLE] = Task_FrontierLogoWiggle, [B_TRANSITION_FRONTIER_LOGO_WAVE] = Task_FrontierLogoWave, [B_TRANSITION_FRONTIER_SQUARES] = Task_FrontierSquares, [B_TRANSITION_FRONTIER_SQUARES_SCROLL] = Task_FrontierSquaresScroll, [B_TRANSITION_FRONTIER_SQUARES_SPIRAL] = Task_FrontierSquaresSpiral, [B_TRANSITION_FRONTIER_CIRCLES_MEET] = Task_FrontierCirclesMeet, [B_TRANSITION_FRONTIER_CIRCLES_CROSS] = Task_FrontierCirclesCross, [B_TRANSITION_FRONTIER_CIRCLES_ASYMMETRIC_SPIRAL] = Task_FrontierCirclesAsymmetricSpiral, [B_TRANSITION_FRONTIER_CIRCLES_SYMMETRIC_SPIRAL] = Task_FrontierCirclesSymmetricSpiral, [B_TRANSITION_FRONTIER_CIRCLES_MEET_IN_SEQ] = Task_FrontierCirclesMeetInSeq, [B_TRANSITION_FRONTIER_CIRCLES_CROSS_IN_SEQ] = Task_FrontierCirclesCrossInSeq, [B_TRANSITION_FRONTIER_CIRCLES_ASYMMETRIC_SPIRAL_IN_SEQ] = Task_FrontierCirclesAsymmetricSpiralInSeq, [B_TRANSITION_FRONTIER_CIRCLES_SYMMETRIC_SPIRAL_IN_SEQ] = Task_FrontierCirclesSymmetricSpiralInSeq, }; static const TransitionStateFunc sTaskHandlers[] = { &Transition_StartIntro, &Transition_WaitForIntro, &Transition_StartMain, &Transition_WaitForMain }; static const TransitionStateFunc sBlur_Funcs[] = { Blur_Init, Blur_Main, Blur_End }; static const TransitionStateFunc sSwirl_Funcs[] = { Swirl_Init, Swirl_End, }; static const TransitionStateFunc sShuffle_Funcs[] = { Shuffle_Init, Shuffle_End, }; static const TransitionStateFunc sAqua_Funcs[] = { Aqua_Init, Aqua_SetGfx, PatternWeave_Blend1, PatternWeave_Blend2, PatternWeave_FinishAppear, FramesCountdown, PatternWeave_CircularMask }; static const TransitionStateFunc sMagma_Funcs[] = { Magma_Init, Magma_SetGfx, PatternWeave_Blend1, PatternWeave_Blend2, PatternWeave_FinishAppear, FramesCountdown, PatternWeave_CircularMask }; static const TransitionStateFunc sBigPokeball_Funcs[] = { BigPokeball_Init, BigPokeball_SetGfx, PatternWeave_Blend1, PatternWeave_Blend2, PatternWeave_FinishAppear, PatternWeave_CircularMask }; static const TransitionStateFunc sRegice_Funcs[] = { Regi_Init, Regice_SetGfx, PatternWeave_Blend1, PatternWeave_Blend2, PatternWeave_FinishAppear, PatternWeave_CircularMask }; static const TransitionStateFunc sRegisteel_Funcs[] = { Regi_Init, Registeel_SetGfx, PatternWeave_Blend1, PatternWeave_Blend2, PatternWeave_FinishAppear, PatternWeave_CircularMask }; static const TransitionStateFunc sRegirock_Funcs[] = { Regi_Init, Regirock_SetGfx, PatternWeave_Blend1, PatternWeave_Blend2, PatternWeave_FinishAppear, PatternWeave_CircularMask }; static const TransitionStateFunc sKyogre_Funcs[] = { WeatherTrio_BgFadeBlack, WeatherTrio_WaitFade, Kyogre_Init, Kyogre_PaletteFlash, Kyogre_PaletteBrighten, FramesCountdown, WeatherDuo_FadeOut, WeatherDuo_End }; static const TransitionStateFunc sPokeballsTrail_Funcs[] = { PokeballsTrail_Init, PokeballsTrail_Main, PokeballsTrail_End }; #define NUM_POKEBALL_TRAILS 5 static const s16 sPokeballsTrail_StartXCoords[2] = { -16, DISPLAY_WIDTH + 16 }; static const s16 sPokeballsTrail_Delays[NUM_POKEBALL_TRAILS] = {0, 32, 64, 18, 48}; static const s16 sPokeballsTrail_Speeds[2] = {8, -8}; static const TransitionStateFunc sClockwiseWipe_Funcs[] = { ClockwiseWipe_Init, ClockwiseWipe_TopRight, ClockwiseWipe_Right, ClockwiseWipe_Bottom, ClockwiseWipe_Left, ClockwiseWipe_TopLeft, ClockwiseWipe_End }; static const TransitionStateFunc sRipple_Funcs[] = { Ripple_Init, Ripple_Main }; static const TransitionStateFunc sWave_Funcs[] = { Wave_Init, Wave_Main, Wave_End }; static const TransitionStateFunc sMugshot_Funcs[] = { Mugshot_Init, Mugshot_SetGfx, Mugshot_ShowBanner, Mugshot_StartOpponentSlide, Mugshot_WaitStartPlayerSlide, Mugshot_WaitPlayerSlide, Mugshot_GradualWhiteFade, Mugshot_InitFadeWhiteToBlack, Mugshot_FadeToBlack, Mugshot_End }; static const u8 sMugshotsTrainerPicIDsTable[MUGSHOTS_COUNT] = { [MUGSHOT_SIDNEY] = TRAINER_PIC_ELITE_FOUR_SIDNEY, [MUGSHOT_PHOEBE] = TRAINER_PIC_ELITE_FOUR_PHOEBE, [MUGSHOT_GLACIA] = TRAINER_PIC_ELITE_FOUR_GLACIA, [MUGSHOT_DRAKE] = TRAINER_PIC_ELITE_FOUR_DRAKE, [MUGSHOT_CHAMPION] = TRAINER_PIC_CHAMPION_WALLACE, }; static const s16 sMugshotsOpponentRotationScales[MUGSHOTS_COUNT][2] = { [MUGSHOT_SIDNEY] = {0x200, 0x200}, [MUGSHOT_PHOEBE] = {0x200, 0x200}, [MUGSHOT_GLACIA] = {0x1B0, 0x1B0}, [MUGSHOT_DRAKE] = {0x1A0, 0x1A0}, [MUGSHOT_CHAMPION] = {0x188, 0x188}, }; static const s16 sMugshotsOpponentCoords[MUGSHOTS_COUNT][2] = { [MUGSHOT_SIDNEY] = { 0, 0}, [MUGSHOT_PHOEBE] = { 0, 0}, [MUGSHOT_GLACIA] = {-4, 4}, [MUGSHOT_DRAKE] = { 0, 5}, [MUGSHOT_CHAMPION] = {-8, 7}, }; static const TransitionSpriteCallback sMugshotTrainerPicFuncs[] = { MugshotTrainerPic_Pause, MugshotTrainerPic_Init, MugshotTrainerPic_Slide, MugshotTrainerPic_SlideSlow, MugshotTrainerPic_Pause, MugshotTrainerPic_SlideOffscreen, MugshotTrainerPic_Pause }; // One element per slide direction. // Sign of acceleration is opposite speed, so slide decelerates. static const s16 sTrainerPicSlideSpeeds[2] = {12, -12}; static const s16 sTrainerPicSlideAccels[2] = {-1, 1}; static const TransitionStateFunc sSlice_Funcs[] = { Slice_Init, Slice_Main, Slice_End }; static const TransitionStateFunc sShredSplit_Funcs[] = { ShredSplit_Init, ShredSplit_Main, ShredSplit_BrokenCheck, ShredSplit_End }; static const u8 sShredSplit_SectionYCoords[] = {39, DISPLAY_HEIGHT - 41}; static const s16 sShredSplit_SectionMoveDirs[] = {1, -1}; static const TransitionStateFunc sBlackhole_Funcs[] = { Blackhole_Init, Blackhole_Vibrate, Blackhole_GrowEnd }; static const TransitionStateFunc sBlackholePulsate_Funcs[] = { Blackhole_Init, BlackholePulsate_Main }; // Blackhole rapidly alternates adding these values to the radius, // resulting in a vibrating shrink/grow effect. static const s16 sBlackhole_Vibrations[] = {-6, 4}; static const TransitionStateFunc sRectangularSpiral_Funcs[] = { RectangularSpiral_Init, RectangularSpiral_Main, RectangularSpiral_End }; #define SPIRAL_END (-1) #define SPIRAL_REBOUND (-2) // Note that the directions are inverted for the lines originating at the bottom. // i.e. MOVE_RIGHT is a right move for the top lines and a left move for the inverted bottom lines. enum { MOVE_RIGHT = 1, MOVE_LEFT, MOVE_UP, MOVE_DOWN, }; // Offsets of the movement data for spiraling in either direction. #define SPIRAL_INWARD_START 0 #define SPIRAL_INWARD_END 3 #define SPIRAL_OUTWARD_START 4 #define SPIRAL_OUTWARD_END 7 static const s16 sRectangularSpiral_Major_InwardRight[] = {MOVE_RIGHT, 27, 275, SPIRAL_END}; static const s16 sRectangularSpiral_Major_InwardLeft[] = {MOVE_LEFT, 486, SPIRAL_END}; static const s16 sRectangularSpiral_Major_InwardUp[] = {MOVE_UP, 262, SPIRAL_END}; static const s16 sRectangularSpiral_Major_InwardDown[] = {MOVE_DOWN, 507, SPIRAL_REBOUND}; static const s16 sRectangularSpiral_Minor_InwardRight[] = {MOVE_RIGHT, 213, SPIRAL_END}; static const s16 sRectangularSpiral_Minor_InwardLeft[] = {MOVE_LEFT, 548, SPIRAL_REBOUND}; static const s16 sRectangularSpiral_Minor_InwardUp[] = {MOVE_UP, 196, SPIRAL_END}; static const s16 sRectangularSpiral_Minor_InwardDown[] = {MOVE_DOWN, 573, 309, SPIRAL_END}; static const s16 sRectangularSpiral_Minor_OutwardRight[] = {MOVE_RIGHT, 474, SPIRAL_END}; static const s16 sRectangularSpiral_Minor_OutwardLeft[] = {MOVE_LEFT, 295, 32, SPIRAL_END}; static const s16 sRectangularSpiral_Minor_OutwardUp[] = {MOVE_UP, 58, SPIRAL_END}; static const s16 sRectangularSpiral_Minor_OutwardDown[] = {MOVE_DOWN, 455, SPIRAL_END}; static const s16 sRectangularSpiral_Major_OutwardRight[] = {MOVE_RIGHT, 540, SPIRAL_END}; static const s16 sRectangularSpiral_Major_OutwardLeft[] = {MOVE_LEFT, 229, SPIRAL_END}; static const s16 sRectangularSpiral_Major_OutwardUp[] = {MOVE_UP, 244, 28, SPIRAL_END}; static const s16 sRectangularSpiral_Major_OutwardDown[] = {MOVE_DOWN, 517, SPIRAL_END}; // Move data for spiral lines starting in the top left / bottom right static const s16 *const sRectangularSpiral_MoveDataTable_MajorDiagonal[] = { [SPIRAL_INWARD_START] = sRectangularSpiral_Major_InwardRight, sRectangularSpiral_Major_InwardDown, sRectangularSpiral_Major_InwardLeft, sRectangularSpiral_Major_InwardUp, [SPIRAL_OUTWARD_START] = sRectangularSpiral_Major_OutwardUp, sRectangularSpiral_Major_OutwardLeft, sRectangularSpiral_Major_OutwardDown, sRectangularSpiral_Major_OutwardRight }; // Move data for spiral lines starting in the top right / bottom left static const s16 *const sRectangularSpiral_MoveDataTable_MinorDiagonal[] = { [SPIRAL_INWARD_START] = sRectangularSpiral_Minor_InwardDown, sRectangularSpiral_Minor_InwardLeft, sRectangularSpiral_Minor_InwardUp, sRectangularSpiral_Minor_InwardRight, [SPIRAL_OUTWARD_START] = sRectangularSpiral_Minor_OutwardLeft, sRectangularSpiral_Minor_OutwardDown, sRectangularSpiral_Minor_OutwardRight, sRectangularSpiral_Minor_OutwardUp }; static const s16 *const *const sRectangularSpiral_MoveDataTables[] = { sRectangularSpiral_MoveDataTable_MajorDiagonal, sRectangularSpiral_MoveDataTable_MinorDiagonal }; static const TransitionStateFunc sGroudon_Funcs[] = { WeatherTrio_BgFadeBlack, WeatherTrio_WaitFade, Groudon_Init, Groudon_PaletteFlash, Groudon_PaletteBrighten, FramesCountdown, WeatherDuo_FadeOut, WeatherDuo_End }; static const TransitionStateFunc sRayquaza_Funcs[] = { WeatherTrio_BgFadeBlack, WeatherTrio_WaitFade, Rayquaza_Init, Rayquaza_SetGfx, Rayquaza_PaletteFlash, Rayquaza_FadeToBlack, Rayquaza_WaitFade, Rayquaza_SetBlack, Rayquaza_TriRing, Blackhole_Vibrate, Blackhole_GrowEnd }; static const TransitionStateFunc sWhiteBarsFade_Funcs[] = { WhiteBarsFade_Init, WhiteBarsFade_StartBars, WhiteBarsFade_WaitBars, WhiteBarsFade_BlendToBlack, WhiteBarsFade_End }; #define NUM_WHITE_BARS 8 static const s16 sWhiteBarsFade_StartDelays[NUM_WHITE_BARS] = {0, 20, 15, 40, 10, 25, 35, 5}; static const TransitionStateFunc sGridSquares_Funcs[] = { GridSquares_Init, GridSquares_Main, GridSquares_End }; static const TransitionStateFunc sAngledWipes_Funcs[] = { AngledWipes_Init, AngledWipes_SetWipeData, AngledWipes_DoWipe, AngledWipes_TryEnd, AngledWipes_StartNext }; #define NUM_ANGLED_WIPES 7 static const s16 sAngledWipes_MoveData[NUM_ANGLED_WIPES][5] = { // startX startY endX endY yDirection {56, 0, 0, DISPLAY_HEIGHT, 0}, {104, DISPLAY_HEIGHT, DISPLAY_WIDTH, 88, 1}, {DISPLAY_WIDTH, 72, 56, 0, 1}, {0, 32, 144, DISPLAY_HEIGHT, 0}, {144, DISPLAY_HEIGHT, 184, 0, 1}, {56, 0, 168, DISPLAY_HEIGHT, 0}, {168, DISPLAY_HEIGHT, 48, 0, 1}, }; static const s16 sAngledWipes_EndDelays[NUM_ANGLED_WIPES] = {8, 4, 2, 1, 1, 1, 0}; static const TransitionStateFunc sTransitionIntroFuncs[] = { TransitionIntro_FadeToGray, TransitionIntro_FadeFromGray }; static const struct SpriteFrameImage sSpriteImage_Pokeball[] = { sPokeball_Gfx, sizeof(sPokeball_Gfx) }; static const union AnimCmd sSpriteAnim_Pokeball[] = { ANIMCMD_FRAME(0, 1), ANIMCMD_END }; static const union AnimCmd *const sSpriteAnimTable_Pokeball[] = { sSpriteAnim_Pokeball }; static const union AffineAnimCmd sSpriteAffineAnim_Pokeball1[] = { AFFINEANIMCMD_FRAME(0, 0, -4, 1), AFFINEANIMCMD_JUMP(0) }; static const union AffineAnimCmd sSpriteAffineAnim_Pokeball2[] = { AFFINEANIMCMD_FRAME(0, 0, 4, 1), AFFINEANIMCMD_JUMP(0) }; static const union AffineAnimCmd *const sSpriteAffineAnimTable_Pokeball[] = { sSpriteAffineAnim_Pokeball1, sSpriteAffineAnim_Pokeball2 }; static const struct SpriteTemplate sSpriteTemplate_Pokeball = { .tileTag = TAG_NONE, .paletteTag = FLDEFF_PAL_TAG_POKEBALL_TRAIL, .oam = &gObjectEventBaseOam_32x32, .anims = sSpriteAnimTable_Pokeball, .images = sSpriteImage_Pokeball, .affineAnims = sSpriteAffineAnimTable_Pokeball, .callback = SpriteCB_FldEffPokeballTrail }; static const struct OamData sOam_UnusedBrendanLass = { .y = 0, .affineMode = ST_OAM_AFFINE_OFF, .objMode = ST_OAM_OBJ_NORMAL, .mosaic = 0, .bpp = ST_OAM_4BPP, .shape = SPRITE_SHAPE(64x64), .x = 0, .matrixNum = 0, .size = SPRITE_SIZE(64x64), .tileNum = 0, .priority = 0, .paletteNum = 0, .affineParam = 0, }; static const struct SpriteFrameImage sImageTable_UnusedBrendan[] = { sUnusedBrendan_Gfx, sizeof(sUnusedBrendan_Gfx) }; static const struct SpriteFrameImage sImageTable_UnusedLass[] = { sUnusedLass_Gfx, sizeof(sUnusedLass_Gfx) }; static const union AnimCmd sSpriteAnim_UnusedBrendanLass[] = { ANIMCMD_FRAME(0, 1), ANIMCMD_END }; static const union AnimCmd *const sSpriteAnimTable_UnusedBrendanLass[] = { sSpriteAnim_UnusedBrendanLass }; static const struct SpriteTemplate sSpriteTemplate_UnusedBrendan = { .tileTag = TAG_NONE, .paletteTag = PALTAG_UNUSED_MUGSHOT, .oam = &sOam_UnusedBrendanLass, .anims = sSpriteAnimTable_UnusedBrendanLass, .images = sImageTable_UnusedBrendan, .affineAnims = gDummySpriteAffineAnimTable, .callback = SpriteCB_MugshotTrainerPic }; static const struct SpriteTemplate sSpriteTemplate_UnusedLass = { .tileTag = TAG_NONE, .paletteTag = PALTAG_UNUSED_MUGSHOT, .oam = &sOam_UnusedBrendanLass, .anims = sSpriteAnimTable_UnusedBrendanLass, .images = sImageTable_UnusedLass, .affineAnims = gDummySpriteAffineAnimTable, .callback = SpriteCB_MugshotTrainerPic }; static const u16 sFieldEffectPal_Pokeball[] = INCBIN_U16("graphics/field_effects/palettes/pokeball.gbapal"); const struct SpritePalette gSpritePalette_Pokeball = {sFieldEffectPal_Pokeball, FLDEFF_PAL_TAG_POKEBALL_TRAIL}; static const u16 sMugshotPal_Sidney[] = INCBIN_U16("graphics/battle_transitions/sidney_bg.gbapal"); static const u16 sMugshotPal_Phoebe[] = INCBIN_U16("graphics/battle_transitions/phoebe_bg.gbapal"); static const u16 sMugshotPal_Glacia[] = INCBIN_U16("graphics/battle_transitions/glacia_bg.gbapal"); static const u16 sMugshotPal_Drake[] = INCBIN_U16("graphics/battle_transitions/drake_bg.gbapal"); static const u16 sMugshotPal_Champion[] = INCBIN_U16("graphics/battle_transitions/wallace_bg.gbapal"); static const u16 sMugshotPal_Brendan[] = INCBIN_U16("graphics/battle_transitions/brendan_bg.gbapal"); static const u16 sMugshotPal_May[] = INCBIN_U16("graphics/battle_transitions/may_bg.gbapal"); static const u16 *const sOpponentMugshotsPals[MUGSHOTS_COUNT] = { [MUGSHOT_SIDNEY] = sMugshotPal_Sidney, [MUGSHOT_PHOEBE] = sMugshotPal_Phoebe, [MUGSHOT_GLACIA] = sMugshotPal_Glacia, [MUGSHOT_DRAKE] = sMugshotPal_Drake, [MUGSHOT_CHAMPION] = sMugshotPal_Champion }; static const u16 *const sPlayerMugshotsPals[GENDER_COUNT] = { [MALE] = sMugshotPal_Brendan, [FEMALE] = sMugshotPal_May }; static const u16 sUnusedTrainerPalette[] = INCBIN_U16("graphics/battle_transitions/unused_trainer.gbapal"); static const struct SpritePalette sSpritePalette_UnusedTrainer = {sUnusedTrainerPalette, PALTAG_UNUSED_MUGSHOT}; static const u16 sBigPokeball_Tilemap[] = INCBIN_U16("graphics/battle_transitions/big_pokeball_map.bin"); static const u16 sMugshotsTilemap[] = INCBIN_U16("graphics/battle_transitions/elite_four_bg_map.bin"); static const TransitionStateFunc sFrontierLogoWiggle_Funcs[] = { FrontierLogoWiggle_Init, FrontierLogoWiggle_SetGfx, PatternWeave_Blend1, PatternWeave_Blend2, PatternWeave_FinishAppear, PatternWeave_CircularMask }; static const TransitionStateFunc sFrontierLogoWave_Funcs[] = { FrontierLogoWave_Init, FrontierLogoWave_SetGfx, FrontierLogoWave_InitScanline, FrontierLogoWave_Main }; static const TransitionStateFunc sFrontierSquares_Funcs[] = { FrontierSquares_Init, FrontierSquares_Draw, FrontierSquares_Shrink, FrontierSquares_End }; static const TransitionStateFunc sFrontierSquaresSpiral_Funcs[] = { FrontierSquaresSpiral_Init, FrontierSquaresSpiral_Outward, FrontierSquaresSpiral_SetBlack, FrontierSquaresSpiral_Inward, FrontierSquares_End }; static const TransitionStateFunc sFrontierSquaresScroll_Funcs[] = { FrontierSquaresScroll_Init, FrontierSquaresScroll_Draw, FrontierSquaresScroll_SetBlack, FrontierSquaresScroll_Erase, FrontierSquaresScroll_End }; #define SQUARE_SIZE 4 #define MARGIN_SIZE 1 // Squares do not fit evenly across the width, so there is a margin on either side. #define NUM_SQUARES_PER_ROW ((DISPLAY_WIDTH - (MARGIN_SIZE * 8 * 2)) / (SQUARE_SIZE * 8)) #define NUM_SQUARES_PER_COL (DISPLAY_HEIGHT / (SQUARE_SIZE * 8)) #define NUM_SQUARES (NUM_SQUARES_PER_ROW * NUM_SQUARES_PER_COL) // The order in which the squares should appear/disappear to create // the spiral effect. Spiraling inward starts with the first element, // and spiraling outward starts with the last. The positions are the // squares numbered left-to-right top-to-bottom. static const u8 sFrontierSquaresSpiral_Positions[NUM_SQUARES] = { 28, 29, 30, 31, 32, 33, 34, 27, 20, 13, 6, 5, 4, 3, 2, 1, 0, 7, 14, 21, 22, 23, 24, 25, 26, 19, 12, 11, 10, 9, 8, 15, 16, 17, 18 }; // In the scrolling version the squares appear/disappear in a "random" order // dictated by the list below. static const u8 sFrontierSquaresScroll_Positions[] = { 0, 16, 41, 22, 44, 2, 43, 21, 46, 27, 9, 48, 38, 5, 57, 59, 12, 63, 35, 28, 10, 53, 7, 49, 39, 23, 55, 1, 62, 17, 61, 30, 6, 34, 15, 51, 32, 58, 13, 45, 37, 52, 11, 24, 60, 19, 56, 33, 29, 50, 40, 54, 14, 3, 47, 20, 18, 25, 4, 36, 26, 42, 31, 8 }; //--------------------------- // Main transition functions //--------------------------- static void CB2_TestBattleTransition(void) { switch (sTestingTransitionState) { case 0: LaunchBattleTransitionTask(sTestingTransitionId); sTestingTransitionState++; break; case 1: if (IsBattleTransitionDone()) { sTestingTransitionState = 0; SetMainCallback2(CB2_ReturnToField); } break; } RunTasks(); AnimateSprites(); BuildOamBuffer(); UpdatePaletteFade(); } // Unused static void TestBattleTransition(u8 transitionId) { sTestingTransitionId = transitionId; SetMainCallback2(CB2_TestBattleTransition); } void BattleTransition_StartOnField(u8 transitionId) { gMain.callback2 = CB2_OverworldBasic; LaunchBattleTransitionTask(transitionId); } void BattleTransition_Start(u8 transitionId) { LaunchBattleTransitionTask(transitionId); } // main task that launches sub-tasks for phase1 and phase2 #define tTransitionId data[1] #define tTransitionDone data[15] bool8 IsBattleTransitionDone(void) { u8 taskId = FindTaskIdByFunc(Task_BattleTransition); if (gTasks[taskId].tTransitionDone) { DestroyTask(taskId); FREE_AND_SET_NULL(sTransitionData); return TRUE; } else { return FALSE; } } static void LaunchBattleTransitionTask(u8 transitionId) { u8 taskId = CreateTask(Task_BattleTransition, 2); gTasks[taskId].tTransitionId = transitionId; sTransitionData = AllocZeroed(sizeof(*sTransitionData)); } static void Task_BattleTransition(u8 taskId) { while (sTaskHandlers[gTasks[taskId].tState](&gTasks[taskId])); } static bool8 Transition_StartIntro(struct Task *task) { SetWeatherScreenFadeOut(); CpuCopy32(gPlttBufferFaded, gPlttBufferUnfaded, sizeof(gPlttBufferUnfaded)); if (sTasks_Intro[task->tTransitionId] != NULL) { CreateTask(sTasks_Intro[task->tTransitionId], 4); task->tState++; return FALSE; } else { task->tState = 2; return TRUE; } } static bool8 Transition_WaitForIntro(struct Task *task) { if (FindTaskIdByFunc(sTasks_Intro[task->tTransitionId]) == TASK_NONE) { task->tState++; return TRUE; } else { return FALSE; } } static bool8 Transition_StartMain(struct Task *task) { CreateTask(sTasks_Main[task->tTransitionId], 0); task->tState++; return FALSE; } static bool8 Transition_WaitForMain(struct Task *task) { task->tTransitionDone = FALSE; if (FindTaskIdByFunc(sTasks_Main[task->tTransitionId]) == TASK_NONE) task->tTransitionDone = TRUE; return FALSE; } #undef tTransitionId #undef tTransitionDone static void Task_Intro(u8 taskId) { if (gTasks[taskId].tState == 0) { gTasks[taskId].tState++; CreateIntroTask(0, 0, 3, 2, 2); } else if (IsIntroTaskDone()) { DestroyTask(taskId); } } //-------------------- // B_TRANSITION_BLUR //-------------------- #define tDelay data[1] #define tCounter data[2] static void Task_Blur(u8 taskId) { while (sBlur_Funcs[gTasks[taskId].tState](&gTasks[taskId])); } static bool8 Blur_Init(struct Task *task) { SetGpuReg(REG_OFFSET_MOSAIC, 0); SetGpuRegBits(REG_OFFSET_BG1CNT, BGCNT_MOSAIC); SetGpuRegBits(REG_OFFSET_BG2CNT, BGCNT_MOSAIC); SetGpuRegBits(REG_OFFSET_BG3CNT, BGCNT_MOSAIC); task->tState++; return TRUE; } static bool8 Blur_Main(struct Task *task) { if (task->tDelay != 0) { task->tDelay--; } else { task->tDelay = 4; if (++task->tCounter == 10) BeginNormalPaletteFade(PALETTES_ALL, -1, 0, 16, RGB_BLACK); SetGpuReg(REG_OFFSET_MOSAIC, (task->tCounter & 15) * 17); if (task->tCounter > 14) task->tState++; } return FALSE; } static bool8 Blur_End(struct Task *task) { if (!gPaletteFade.active) { u8 taskId = FindTaskIdByFunc(Task_Blur); DestroyTask(taskId); } return FALSE; } #undef tDelay #undef tCounter //-------------------- // B_TRANSITION_SWIRL //-------------------- #define tSinIndex data[1] #define tAmplitude data[2] static void Task_Swirl(u8 taskId) { while (sSwirl_Funcs[gTasks[taskId].tState](&gTasks[taskId])); } static bool8 Swirl_Init(struct Task *task) { InitTransitionData(); ScanlineEffect_Clear(); BeginNormalPaletteFade(PALETTES_ALL, 4, 0, 16, RGB_BLACK); SetSinWave(gScanlineEffectRegBuffers[1], sTransitionData->cameraX, 0, 2, 0, DISPLAY_HEIGHT); SetVBlankCallback(VBlankCB_Swirl); SetHBlankCallback(HBlankCB_Swirl); EnableInterrupts(INTR_FLAG_VBLANK | INTR_FLAG_HBLANK); task->tState++; return FALSE; } static bool8 Swirl_End(struct Task *task) { sTransitionData->VBlank_DMA = FALSE; task->tSinIndex += 4; task->tAmplitude += 8; SetSinWave(gScanlineEffectRegBuffers[0], sTransitionData->cameraX, task->tSinIndex, 2, task->tAmplitude, DISPLAY_HEIGHT); if (!gPaletteFade.active) { u8 taskId = FindTaskIdByFunc(Task_Swirl); DestroyTask(taskId); } sTransitionData->VBlank_DMA++; return FALSE; } static void VBlankCB_Swirl(void) { VBlankCB_BattleTransition(); if (sTransitionData->VBlank_DMA) DmaCopy16(3, gScanlineEffectRegBuffers[0], gScanlineEffectRegBuffers[1], DISPLAY_HEIGHT * 2); } static void HBlankCB_Swirl(void) { u16 var = gScanlineEffectRegBuffers[1][REG_VCOUNT]; REG_BG1HOFS = var; REG_BG2HOFS = var; REG_BG3HOFS = var; } #undef tSinIndex #undef tAmplitude //---------------------- // B_TRANSITION_SHUFFLE //---------------------- #define tSinVal data[1] #define tAmplitude data[2] static void Task_Shuffle(u8 taskId) { while (sShuffle_Funcs[gTasks[taskId].tState](&gTasks[taskId])); } static bool8 Shuffle_Init(struct Task *task) { InitTransitionData(); ScanlineEffect_Clear(); BeginNormalPaletteFade(PALETTES_ALL, 4, 0, 16, RGB_BLACK); memset(gScanlineEffectRegBuffers[1], sTransitionData->cameraY, DISPLAY_HEIGHT * 2); SetVBlankCallback(VBlankCB_Shuffle); SetHBlankCallback(HBlankCB_Shuffle); EnableInterrupts(INTR_FLAG_VBLANK | INTR_FLAG_HBLANK); task->tState++; return FALSE; } static bool8 Shuffle_End(struct Task *task) { u8 i; u16 amplitude, sinVal; sTransitionData->VBlank_DMA = FALSE; sinVal = task->tSinVal; amplitude = task->tAmplitude >> 8; task->tSinVal += 4224; task->tAmplitude += 384; for (i = 0; i < DISPLAY_HEIGHT; i++, sinVal += 4224) { u16 sinIndex = sinVal / 256; gScanlineEffectRegBuffers[0][i] = sTransitionData->cameraY + Sin(sinIndex, amplitude); } if (!gPaletteFade.active) DestroyTask(FindTaskIdByFunc(Task_Shuffle)); sTransitionData->VBlank_DMA++; return FALSE; } static void VBlankCB_Shuffle(void) { VBlankCB_BattleTransition(); if (sTransitionData->VBlank_DMA) DmaCopy16(3, gScanlineEffectRegBuffers[0], gScanlineEffectRegBuffers[1], DISPLAY_HEIGHT * 2); } static void HBlankCB_Shuffle(void) { u16 var = gScanlineEffectRegBuffers[1][REG_VCOUNT]; REG_BG1VOFS = var; REG_BG2VOFS = var; REG_BG3VOFS = var; } #undef tSinVal #undef tAmplitude //------------------------------------------------------------------------ // B_TRANSITION_BIG_POKEBALL, B_TRANSITION_AQUA, B_TRANSITION_MAGMA, // B_TRANSITION_REGICE, B_TRANSITION_REGISTEEL, B_TRANSITION_REGIROCK // and B_TRANSITION_KYOGRE. // // With the exception of B_TRANSITION_KYOGRE, all of the above transitions // use the same weave effect (see the PatternWeave functions). // Unclear why Kyogre's was grouped here and not with Groudon/Rayquaza's. //------------------------------------------------------------------------ #define tBlendTarget1 data[1] #define tBlendTarget2 data[2] #define tBlendDelay data[3] // Data 1-3 change purpose for PatternWeave_CircularMask #define tRadius data[1] #define tRadiusDelta data[2] #define tVBlankSet data[3] #define tSinIndex data[4] #define tAmplitude data[5] #define tEndDelay data[8] static void Task_BigPokeball(u8 taskId) { while (sBigPokeball_Funcs[gTasks[taskId].tState](&gTasks[taskId])); } static void Task_Aqua(u8 taskId) { while (sAqua_Funcs[gTasks[taskId].tState](&gTasks[taskId])); } static void Task_Magma(u8 taskId) { while (sMagma_Funcs[gTasks[taskId].tState](&gTasks[taskId])); } static void Task_Regice(u8 taskId) { while (sRegice_Funcs[gTasks[taskId].tState](&gTasks[taskId])); } static void Task_Registeel(u8 taskId) { while (sRegisteel_Funcs[gTasks[taskId].tState](&gTasks[taskId])); } static void Task_Regirock(u8 taskId) { while (sRegirock_Funcs[gTasks[taskId].tState](&gTasks[taskId])); } static void Task_Kyogre(u8 taskId) { while (sKyogre_Funcs[gTasks[taskId].tState](&gTasks[taskId])); } static void InitPatternWeaveTransition(struct Task *task) { s32 i; InitTransitionData(); ScanlineEffect_Clear(); task->tBlendTarget1 = 16; task->tBlendTarget2 = 0; task->tSinIndex = 0; task->tAmplitude = 0x4000; sTransitionData->WININ = WININ_WIN0_ALL; sTransitionData->WINOUT = 0; sTransitionData->WIN0H = DISPLAY_WIDTH; sTransitionData->WIN0V = DISPLAY_HEIGHT; sTransitionData->BLDCNT = BLDCNT_TGT1_BG0 | BLDCNT_EFFECT_BLEND | BLDCNT_TGT2_ALL; sTransitionData->BLDALPHA = BLDALPHA_BLEND(task->tBlendTarget2, task->tBlendTarget1); for (i = 0; i < DISPLAY_HEIGHT; i++) gScanlineEffectRegBuffers[1][i] = DISPLAY_WIDTH; SetVBlankCallback(VBlankCB_PatternWeave); } static bool8 Aqua_Init(struct Task *task) { u16 *tilemap, *tileset; task->tEndDelay = 60; InitPatternWeaveTransition(task); GetBg0TilesDst(&tilemap, &tileset); CpuFill16(0, tilemap, BG_SCREEN_SIZE); LZ77UnCompVram(sTeamAqua_Tileset, tileset); LoadPalette(sEvilTeam_Palette, 0xF0, sizeof(sEvilTeam_Palette)); task->tState++; return FALSE; } static bool8 Magma_Init(struct Task *task) { u16 *tilemap, *tileset; task->tEndDelay = 60; InitPatternWeaveTransition(task); GetBg0TilesDst(&tilemap, &tileset); CpuFill16(0, tilemap, BG_SCREEN_SIZE); LZ77UnCompVram(sTeamMagma_Tileset, tileset); LoadPalette(sEvilTeam_Palette, 0xF0, sizeof(sEvilTeam_Palette)); task->tState++; return FALSE; } static bool8 Regi_Init(struct Task *task) { u16 *tilemap, *tileset; task->tEndDelay = 60; InitPatternWeaveTransition(task); GetBg0TilesDst(&tilemap, &tileset); CpuFill16(0, tilemap, BG_SCREEN_SIZE); CpuCopy16(sRegis_Tileset, tileset, 0x2000); task->tState++; return FALSE; } static bool8 BigPokeball_Init(struct Task *task) { u16 *tilemap, *tileset; InitPatternWeaveTransition(task); GetBg0TilesDst(&tilemap, &tileset); CpuFill16(0, tilemap, BG_SCREEN_SIZE); CpuCopy16(sBigPokeball_Tileset, tileset, sizeof(sBigPokeball_Tileset)); LoadPalette(sFieldEffectPal_Pokeball, 0xF0, sizeof(sFieldEffectPal_Pokeball)); task->tState++; return FALSE; } static bool8 BigPokeball_SetGfx(struct Task *task) { s16 i, j; u16 *tilemap, *tileset; const u16 *bigPokeballMap; GetBg0TilesDst(&tilemap, &tileset); bigPokeballMap = sBigPokeball_Tilemap; for (i = 0; i < 20; i++) { for (j = 0; j < 30; j++, bigPokeballMap++) SET_TILE(tilemap, i, j, *bigPokeballMap); } SetSinWave(gScanlineEffectRegBuffers[0], 0, task->tSinIndex, 132, task->tAmplitude, DISPLAY_HEIGHT); task->tState++; return TRUE; } static bool8 Aqua_SetGfx(struct Task *task) { u16 *tilemap, *tileset; GetBg0TilesDst(&tilemap, &tileset); LZ77UnCompVram(sTeamAqua_Tilemap, tilemap); SetSinWave(gScanlineEffectRegBuffers[0], 0, task->tSinIndex, 132, task->tAmplitude, DISPLAY_HEIGHT); task->tState++; return FALSE; } static bool8 Magma_SetGfx(struct Task *task) { u16 *tilemap, *tileset; GetBg0TilesDst(&tilemap, &tileset); LZ77UnCompVram(sTeamMagma_Tilemap, tilemap); SetSinWave(gScanlineEffectRegBuffers[0], 0, task->tSinIndex, 132, task->tAmplitude, DISPLAY_HEIGHT); task->tState++; return FALSE; } static bool8 Regice_SetGfx(struct Task *task) { u16 *tilemap, *tileset; GetBg0TilesDst(&tilemap, &tileset); LoadPalette(sRegice_Palette, 0xF0, sizeof(sRegice_Palette)); CpuCopy16(sRegice_Tilemap, tilemap, 0x500); SetSinWave(gScanlineEffectRegBuffers[0], 0, task->tSinIndex, 132, task->tAmplitude, DISPLAY_HEIGHT); task->tState++; return FALSE; } static bool8 Registeel_SetGfx(struct Task *task) { u16 *tilemap, *tileset; GetBg0TilesDst(&tilemap, &tileset); LoadPalette(sRegisteel_Palette, 0xF0, sizeof(sRegisteel_Palette)); CpuCopy16(sRegisteel_Tilemap, tilemap, 0x500); SetSinWave(gScanlineEffectRegBuffers[0], 0, task->tSinIndex, 132, task->tAmplitude, DISPLAY_HEIGHT); task->tState++; return FALSE; } static bool8 Regirock_SetGfx(struct Task *task) { u16 *tilemap, *tileset; GetBg0TilesDst(&tilemap, &tileset); LoadPalette(sRegirock_Palette, 0xF0, sizeof(sRegirock_Palette)); CpuCopy16(sRegirock_Tilemap, tilemap, 0x500); SetSinWave(gScanlineEffectRegBuffers[0], 0, task->tSinIndex, 132, task->tAmplitude, DISPLAY_HEIGHT); task->tState++; return FALSE; } #define tTimer data[1] static bool8 Kyogre_Init(struct Task *task) { u16 *tilemap, *tileset; GetBg0TilesDst(&tilemap, &tileset); CpuFill16(0, tilemap, BG_SCREEN_SIZE); LZ77UnCompVram(sKyogre_Tileset, tileset); LZ77UnCompVram(sKyogre_Tilemap, tilemap); task->tState++; return FALSE; } static bool8 Kyogre_PaletteFlash(struct Task *task) { if (task->tTimer % 3 == 0) { u16 offset = task->tTimer % 30; offset /= 3; LoadPalette(&sKyogre1_Palette[offset * 16], 0xF0, 0x20); } if (++task->tTimer > 58) { task->tState++; task->tTimer = 0; } return FALSE; } static bool8 Kyogre_PaletteBrighten(struct Task *task) { if (task->tTimer % 5 == 0) { s16 offset = task->tTimer / 5; LoadPalette(&sKyogre2_Palette[offset * 16], 0xF0, 0x20); } if (++task->tTimer > 68) { task->tState++; task->tTimer = 0; task->tEndDelay = 30; } return FALSE; } static bool8 WeatherDuo_FadeOut(struct Task *task) { BeginNormalPaletteFade(PALETTES_OBJECTS | (1 << 15), 1, 0, 16, RGB_BLACK); task->tState++; return FALSE; } static bool8 WeatherDuo_End(struct Task *task) { if (!gPaletteFade.active) { DmaStop(0); FadeScreenBlack(); DestroyTask(FindTaskIdByFunc(task->func)); } return FALSE; } #undef tTimer // The PatternWeave_ functions are used by several different transitions. // They create an effect where a pattern/image (such as the Magma emblem) is // formed by a shimmering weave effect. static bool8 PatternWeave_Blend1(struct Task *task) { sTransitionData->VBlank_DMA = FALSE; if (task->tBlendDelay == 0 || --task->tBlendDelay == 0) { task->tBlendTarget2++; task->tBlendDelay = 2; } sTransitionData->BLDALPHA = BLDALPHA_BLEND(task->tBlendTarget2, task->tBlendTarget1); if (task->tBlendTarget2 > 15) task->tState++; task->tSinIndex += 8; task->tAmplitude -= 256; SetSinWave(gScanlineEffectRegBuffers[0], 0, task->tSinIndex, 132, task->tAmplitude >> 8, DISPLAY_HEIGHT); sTransitionData->VBlank_DMA++; return FALSE; } static bool8 PatternWeave_Blend2(struct Task *task) { sTransitionData->VBlank_DMA = FALSE; if (task->tBlendDelay == 0 || --task->tBlendDelay == 0) { task->tBlendTarget1--; task->tBlendDelay = 2; } sTransitionData->BLDALPHA = BLDALPHA_BLEND(task->tBlendTarget2, task->tBlendTarget1); if (task->tBlendTarget1 == 0) task->tState++; task->tSinIndex += 8; task->tAmplitude -= 256; SetSinWave(gScanlineEffectRegBuffers[0], 0, task->tSinIndex, 132, task->tAmplitude >> 8, DISPLAY_HEIGHT); sTransitionData->VBlank_DMA++; return FALSE; } static bool8 PatternWeave_FinishAppear(struct Task *task) { sTransitionData->VBlank_DMA = FALSE; task->tSinIndex += 8; task->tAmplitude -= 256; SetSinWave(gScanlineEffectRegBuffers[0], 0, task->tSinIndex, 132, task->tAmplitude >> 8, DISPLAY_HEIGHT); if (task->tAmplitude <= 0) { task->tState++; task->tRadius = DISPLAY_HEIGHT; task->tRadiusDelta = 1 << 8; task->tVBlankSet = FALSE; } sTransitionData->VBlank_DMA++; return FALSE; } static bool8 FramesCountdown(struct Task *task) { if (--task->tEndDelay == 0) task->tState++; return FALSE; } static bool8 WeatherTrio_BgFadeBlack(struct Task *task) { BeginNormalPaletteFade(PALETTES_BG, 1, 0, 16, RGB_BLACK); task->tState++; return FALSE; } static bool8 WeatherTrio_WaitFade(struct Task *task) { if (!gPaletteFade.active) task->tState++; return FALSE; } // Do a shrinking circular mask to go to a black screen after the pattern appears. static bool8 PatternWeave_CircularMask(struct Task *task) { sTransitionData->VBlank_DMA = FALSE; if (task->tRadiusDelta < (4 << 8)) task->tRadiusDelta += 128; // 256 is 1 unit of speed. Speed up every other frame (128 / 256) if (task->tRadius != 0) { task->tRadius -= task->tRadiusDelta >> 8; if (task->tRadius < 0) task->tRadius = 0; } SetCircularMask(gScanlineEffectRegBuffers[0], DISPLAY_WIDTH / 2, DISPLAY_HEIGHT / 2, task->tRadius); if (task->tRadius == 0) { SetVBlankCallback(NULL); DmaStop(0); FadeScreenBlack(); DestroyTask(FindTaskIdByFunc(task->func)); } else { if (!task->tVBlankSet) { task->tVBlankSet++; SetVBlankCallback(VBlankCB_CircularMask); } sTransitionData->VBlank_DMA++; } return FALSE; } static void VBlankCB_SetWinAndBlend(void) { DmaStop(0); VBlankCB_BattleTransition(); if (sTransitionData->VBlank_DMA) DmaCopy16(3, gScanlineEffectRegBuffers[0], gScanlineEffectRegBuffers[1], DISPLAY_HEIGHT * 2); REG_WININ = sTransitionData->WININ; REG_WINOUT = sTransitionData->WINOUT; REG_WIN0V = sTransitionData->WIN0V; REG_BLDCNT = sTransitionData->BLDCNT; REG_BLDALPHA = sTransitionData->BLDALPHA; } static void VBlankCB_PatternWeave(void) { VBlankCB_SetWinAndBlend(); DmaSet(0, gScanlineEffectRegBuffers[1], ®_BG0HOFS, B_TRANS_DMA_FLAGS); } static void VBlankCB_CircularMask(void) { VBlankCB_SetWinAndBlend(); DmaSet(0, gScanlineEffectRegBuffers[1], ®_WIN0H, B_TRANS_DMA_FLAGS); } #undef tAmplitude #undef tSinIndex #undef tBlendTarget1 #undef tBlendTarget2 #undef tRadius #undef tRadiusDelta #undef tVBlankSet //------------------------------ // B_TRANSITION_POKEBALLS_TRAIL //------------------------------ #define sSide data[0] #define sDelay data[1] #define sPrevX data[2] static void Task_PokeballsTrail(u8 taskId) { while (sPokeballsTrail_Funcs[gTasks[taskId].tState](&gTasks[taskId])); } static bool8 PokeballsTrail_Init(struct Task *task) { u16 *tilemap, *tileset; GetBg0TilesDst(&tilemap, &tileset); CpuSet(sPokeballTrail_Tileset, tileset, 0x20); CpuFill32(0, tilemap, BG_SCREEN_SIZE); LoadPalette(sFieldEffectPal_Pokeball, 0xF0, sizeof(sFieldEffectPal_Pokeball)); task->tState++; return FALSE; } static bool8 PokeballsTrail_Main(struct Task *task) { s16 i; s16 side; s16 startX[ARRAY_COUNT(sPokeballsTrail_StartXCoords)]; s16 delays[ARRAY_COUNT(sPokeballsTrail_Delays)]; memcpy(startX, sPokeballsTrail_StartXCoords, sizeof(sPokeballsTrail_StartXCoords)); memcpy(delays, sPokeballsTrail_Delays, sizeof(sPokeballsTrail_Delays)); // Randomly pick which side the first ball should start on. // The side is then flipped for each subsequent ball. side = Random() & 1; for (i = 0; i < NUM_POKEBALL_TRAILS; i++, side ^= 1) { gFieldEffectArguments[0] = startX[side]; // x gFieldEffectArguments[1] = (i * 32) + 16; // y gFieldEffectArguments[2] = side; gFieldEffectArguments[3] = delays[i]; FieldEffectStart(FLDEFF_POKEBALL_TRAIL); } task->tState++; return FALSE; } static bool8 PokeballsTrail_End(struct Task *task) { if (!FieldEffectActiveListContains(FLDEFF_POKEBALL_TRAIL)) { FadeScreenBlack(); DestroyTask(FindTaskIdByFunc(Task_PokeballsTrail)); } return FALSE; } bool8 FldEff_PokeballTrail(void) { u8 spriteId = CreateSpriteAtEnd(&sSpriteTemplate_Pokeball, gFieldEffectArguments[0], gFieldEffectArguments[1], 0); gSprites[spriteId].oam.priority = 0; gSprites[spriteId].oam.affineMode = ST_OAM_AFFINE_NORMAL; gSprites[spriteId].sSide = gFieldEffectArguments[2]; gSprites[spriteId].sDelay = gFieldEffectArguments[3]; gSprites[spriteId].sPrevX = -1; InitSpriteAffineAnim(&gSprites[spriteId]); StartSpriteAffineAnim(&gSprites[spriteId], gFieldEffectArguments[2]); return FALSE; } static void SpriteCB_FldEffPokeballTrail(struct Sprite *sprite) { s16 speeds[ARRAY_COUNT(sPokeballsTrail_Speeds)]; memcpy(speeds, sPokeballsTrail_Speeds, sizeof(sPokeballsTrail_Speeds)); if (sprite->sDelay != 0) { sprite->sDelay--; } else { if (sprite->x >= 0 && sprite->x <= DISPLAY_WIDTH) { // Set Pokéball position s16 posX = sprite->x >> 3; s16 posY = sprite->y >> 3; // If Pokéball moved forward clear trail behind it if (posX != sprite->sPrevX) { u32 var; u16 *ptr; sprite->sPrevX = posX; var = ((REG_BG0CNT >> 8) & 0x1F) << 11; ptr = (u16 *)(BG_VRAM + var); SET_TILE(ptr, posY - 2, posX, 1); SET_TILE(ptr, posY - 1, posX, 1); SET_TILE(ptr, posY - 0, posX, 1); SET_TILE(ptr, posY + 1, posX, 1); } } sprite->x += speeds[sprite->sSide]; if (sprite->x < -15 || sprite->x > DISPLAY_WIDTH + 15) FieldEffectStop(sprite, FLDEFF_POKEBALL_TRAIL); } } #undef sSide #undef sDelay #undef sPrevX //----------------------------- // B_TRANSITION_CLOCKWISE_WIPE //----------------------------- static void Task_ClockwiseWipe(u8 taskId) { while (sClockwiseWipe_Funcs[gTasks[taskId].tState](&gTasks[taskId])); } static bool8 ClockwiseWipe_Init(struct Task *task) { u16 i; InitTransitionData(); ScanlineEffect_Clear(); sTransitionData->WININ = 0; sTransitionData->WINOUT = WINOUT_WIN01_ALL; sTransitionData->WIN0H = WIN_RANGE(DISPLAY_WIDTH, DISPLAY_WIDTH + 1); sTransitionData->WIN0V = DISPLAY_HEIGHT; for (i = 0; i < DISPLAY_HEIGHT; i++) gScanlineEffectRegBuffers[1][i] = ((DISPLAY_WIDTH + 3) << 8) | (DISPLAY_WIDTH + 4); SetVBlankCallback(VBlankCB_ClockwiseWipe); sTransitionData->tWipeEndX = DISPLAY_WIDTH / 2; task->tState++; return TRUE; } static bool8 ClockwiseWipe_TopRight(struct Task *task) { sTransitionData->VBlank_DMA = FALSE; InitBlackWipe(sTransitionData->data, DISPLAY_WIDTH / 2, DISPLAY_HEIGHT / 2, sTransitionData->tWipeEndX, -1, 1, 1); do { gScanlineEffectRegBuffers[0][sTransitionData->tWipeCurrY] = (sTransitionData->tWipeCurrX + 1) | ((DISPLAY_WIDTH / 2) << 8); } while (!UpdateBlackWipe(sTransitionData->data, TRUE, TRUE)); sTransitionData->tWipeEndX += 16; if (sTransitionData->tWipeEndX >= DISPLAY_WIDTH) { sTransitionData->tWipeEndY = 0; task->tState++; } sTransitionData->VBlank_DMA++; return FALSE; } static bool8 ClockwiseWipe_Right(struct Task *task) { s16 start, end; vu8 finished = FALSE; sTransitionData->VBlank_DMA = FALSE; InitBlackWipe(sTransitionData->data, DISPLAY_WIDTH / 2, DISPLAY_HEIGHT / 2, DISPLAY_WIDTH, sTransitionData->tWipeEndY, 1, 1); while (1) { start = DISPLAY_WIDTH / 2, end = sTransitionData->tWipeCurrX + 1; if (sTransitionData->tWipeEndY >= DISPLAY_HEIGHT / 2) start = sTransitionData->tWipeCurrX, end = DISPLAY_WIDTH; gScanlineEffectRegBuffers[0][sTransitionData->tWipeCurrY] = end | (start << 8); if (finished) break; finished = UpdateBlackWipe(sTransitionData->data, TRUE, TRUE); } sTransitionData->tWipeEndY += 8; if (sTransitionData->tWipeEndY >= DISPLAY_HEIGHT) { sTransitionData->tWipeEndX = DISPLAY_WIDTH; task->tState++; } else { while (sTransitionData->tWipeCurrY < sTransitionData->tWipeEndY) gScanlineEffectRegBuffers[0][++sTransitionData->tWipeCurrY] = end | (start << 8); } sTransitionData->VBlank_DMA++; return FALSE; } static bool8 ClockwiseWipe_Bottom(struct Task *task) { sTransitionData->VBlank_DMA = FALSE; InitBlackWipe(sTransitionData->data, DISPLAY_WIDTH / 2, DISPLAY_HEIGHT / 2, sTransitionData->tWipeEndX, DISPLAY_HEIGHT, 1, 1); do { gScanlineEffectRegBuffers[0][sTransitionData->tWipeCurrY] = (sTransitionData->tWipeCurrX << 8) | DISPLAY_WIDTH; } while (!UpdateBlackWipe(sTransitionData->data, TRUE, TRUE)); sTransitionData->tWipeEndX -= 16; if (sTransitionData->tWipeEndX <= 0) { sTransitionData->tWipeEndY = DISPLAY_HEIGHT; task->tState++; } sTransitionData->VBlank_DMA++; return FALSE; } static bool8 ClockwiseWipe_Left(struct Task *task) { s16 end, start, temp; vu8 finished = FALSE; sTransitionData->VBlank_DMA = FALSE; InitBlackWipe(sTransitionData->data, DISPLAY_WIDTH / 2, DISPLAY_HEIGHT / 2, 0, sTransitionData->tWipeEndY, 1, 1); while (1) { end = (gScanlineEffectRegBuffers[0][sTransitionData->tWipeCurrY]) & 0xFF; start = sTransitionData->tWipeCurrX; if (sTransitionData->tWipeEndY <= DISPLAY_HEIGHT / 2) start = DISPLAY_WIDTH / 2, end = sTransitionData->tWipeCurrX; temp = end | (start << 8); gScanlineEffectRegBuffers[0][sTransitionData->tWipeCurrY] = temp; if (finished) break; finished = UpdateBlackWipe(sTransitionData->data, TRUE, TRUE); } sTransitionData->tWipeEndY -= 8; if (sTransitionData->tWipeEndY <= 0) { sTransitionData->tWipeEndX = 0; task->tState++; } else { while (sTransitionData->tWipeCurrY > sTransitionData->tWipeEndY) gScanlineEffectRegBuffers[0][--sTransitionData->tWipeCurrY] = end | (start << 8); } sTransitionData->VBlank_DMA++; return FALSE; } static bool8 ClockwiseWipe_TopLeft(struct Task *task) { sTransitionData->VBlank_DMA = FALSE; InitBlackWipe(sTransitionData->data, DISPLAY_WIDTH / 2, DISPLAY_HEIGHT / 2, sTransitionData->tWipeEndX, 0, 1, 1); do { s16 start, end; start = DISPLAY_WIDTH / 2, end = sTransitionData->tWipeCurrX; if (sTransitionData->tWipeCurrX >= DISPLAY_WIDTH / 2) start = 0, end = DISPLAY_WIDTH; gScanlineEffectRegBuffers[0][sTransitionData->tWipeCurrY] = end | (start << 8); } while (!UpdateBlackWipe(sTransitionData->data, TRUE, TRUE)); sTransitionData->tWipeEndX += 16; if (sTransitionData->tWipeCurrX > DISPLAY_WIDTH / 2) task->tState++; sTransitionData->VBlank_DMA++; return FALSE; } static bool8 ClockwiseWipe_End(struct Task *task) { DmaStop(0); FadeScreenBlack(); DestroyTask(FindTaskIdByFunc(Task_ClockwiseWipe)); return FALSE; } static void VBlankCB_ClockwiseWipe(void) { DmaStop(0); VBlankCB_BattleTransition(); if (sTransitionData->VBlank_DMA != 0) DmaCopy16(3, gScanlineEffectRegBuffers[0], gScanlineEffectRegBuffers[1], DISPLAY_HEIGHT * 2); REG_WININ = sTransitionData->WININ; REG_WINOUT = sTransitionData->WINOUT; REG_WIN0V = sTransitionData->WIN0V; REG_WIN0H = gScanlineEffectRegBuffers[1][0]; DmaSet(0, gScanlineEffectRegBuffers[1], ®_WIN0H, B_TRANS_DMA_FLAGS); } //--------------------- // B_TRANSITION_RIPPLE //--------------------- #define tSinVal data[1] #define tAmplitudeVal data[2] #define tTimer data[3] #define tFadeStarted data[4] static void Task_Ripple(u8 taskId) { while (sRipple_Funcs[gTasks[taskId].tState](&gTasks[taskId])); } static bool8 Ripple_Init(struct Task *task) { u8 i; InitTransitionData(); ScanlineEffect_Clear(); for (i = 0; i < DISPLAY_HEIGHT; i++) gScanlineEffectRegBuffers[1][i] = sTransitionData->cameraY; SetVBlankCallback(VBlankCB_Ripple); SetHBlankCallback(HBlankCB_Ripple); EnableInterrupts(INTR_FLAG_HBLANK); task->tState++; return TRUE; } static bool8 Ripple_Main(struct Task *task) { u8 i; s16 amplitude; u16 sinVal, speed; sTransitionData->VBlank_DMA = FALSE; amplitude = task->tAmplitudeVal >> 8; sinVal = task->tSinVal; speed = 0x180; task->tSinVal += 0x400; if (task->tAmplitudeVal <= 0x1FFF) task->tAmplitudeVal += 0x180; for (i = 0; i < DISPLAY_HEIGHT; i++, sinVal += speed) { s16 sinIndex = sinVal >> 8; gScanlineEffectRegBuffers[0][i] = sTransitionData->cameraY + Sin(sinIndex & 0xffff, amplitude); } if (++task->tTimer == 81) { task->tFadeStarted++; BeginNormalPaletteFade(PALETTES_ALL, -2, 0, 16, RGB_BLACK); } if (task->tFadeStarted && !gPaletteFade.active) DestroyTask(FindTaskIdByFunc(Task_Ripple)); sTransitionData->VBlank_DMA++; return FALSE; } static void VBlankCB_Ripple(void) { VBlankCB_BattleTransition(); if (sTransitionData->VBlank_DMA) DmaCopy16(3, gScanlineEffectRegBuffers[0], gScanlineEffectRegBuffers[1], DISPLAY_HEIGHT * 2); } static void HBlankCB_Ripple(void) { u16 var = gScanlineEffectRegBuffers[1][REG_VCOUNT]; REG_BG1VOFS = var; REG_BG2VOFS = var; REG_BG3VOFS = var; } #undef tSinVal #undef tAmplitudeVal #undef tTimer #undef tFadeStarted //------------------- // B_TRANSITION_WAVE //------------------- #define tX data[1] #define tSinIndex data[2] static void Task_Wave(u8 taskId) { while (sWave_Funcs[gTasks[taskId].tState](&gTasks[taskId])); } static bool8 Wave_Init(struct Task *task) { u8 i; InitTransitionData(); ScanlineEffect_Clear(); sTransitionData->WININ = WININ_WIN0_ALL; sTransitionData->WINOUT = 0; sTransitionData->WIN0H = DISPLAY_WIDTH; sTransitionData->WIN0V = DISPLAY_HEIGHT; for (i = 0; i < DISPLAY_HEIGHT; i++) gScanlineEffectRegBuffers[1][i] = DISPLAY_WIDTH + 2; SetVBlankCallback(VBlankCB_Wave); task->tState++; return TRUE; } static bool8 Wave_Main(struct Task *task) { u8 i, sinIndex; u16* toStore; bool8 finished; sTransitionData->VBlank_DMA = FALSE; toStore = gScanlineEffectRegBuffers[0]; sinIndex = task->tSinIndex; task->tSinIndex += 16; task->tX += 8; for (i = 0, finished = TRUE; i < DISPLAY_HEIGHT; i++, sinIndex += 4, toStore++) { s16 x = task->tX + Sin(sinIndex, 40); if (x < 0) x = 0; if (x > DISPLAY_WIDTH) x = DISPLAY_WIDTH; *toStore = (x << 8) | (DISPLAY_WIDTH + 1); if (x < DISPLAY_WIDTH) finished = FALSE; } if (finished) task->tState++; sTransitionData->VBlank_DMA++; return FALSE; } static bool8 Wave_End(struct Task *task) { DmaStop(0); FadeScreenBlack(); DestroyTask(FindTaskIdByFunc(Task_Wave)); return FALSE; } static void VBlankCB_Wave(void) { DmaStop(0); VBlankCB_BattleTransition(); if (sTransitionData->VBlank_DMA != 0) DmaCopy16(3, gScanlineEffectRegBuffers[0], gScanlineEffectRegBuffers[1], DISPLAY_HEIGHT * 2); REG_WININ = sTransitionData->WININ; REG_WINOUT = sTransitionData->WINOUT; REG_WIN0V = sTransitionData->WIN0V; DmaSet(0, gScanlineEffectRegBuffers[1], ®_WIN0H, B_TRANS_DMA_FLAGS); } #undef tX #undef tSinIndex //---------------------------------------------------------------- // B_TRANSITION_SIDNEY, B_TRANSITION_PHOEBE, B_TRANSITION_GLACIA, // B_TRANSITION_DRAKE, and B_TRANSITION_CHAMPION // // These are all the "mugshot" transitions, where a banner shows // the trainer pic of the player and their opponent. //---------------------------------------------------------------- #define tSinIndex data[1] #define tTopBannerX data[2] #define tBottomBannerX data[3] #define tTimer data[3] // Re-used #define tFadeSpread data[4] #define tOpponentSpriteId data[13] #define tPlayerSpriteId data[14] #define tMugshotId data[15] // Sprite data for trainer sprites in mugshots #define sState data[0] #define sSlideSpeed data[1] #define sSlideAccel data[2] #define sDone data[6] #define sSlideDir data[7] static void Task_Sidney(u8 taskId) { gTasks[taskId].tMugshotId = MUGSHOT_SIDNEY; DoMugshotTransition(taskId); } static void Task_Phoebe(u8 taskId) { gTasks[taskId].tMugshotId = MUGSHOT_PHOEBE; DoMugshotTransition(taskId); } static void Task_Glacia(u8 taskId) { gTasks[taskId].tMugshotId = MUGSHOT_GLACIA; DoMugshotTransition(taskId); } static void Task_Drake(u8 taskId) { gTasks[taskId].tMugshotId = MUGSHOT_DRAKE; DoMugshotTransition(taskId); } static void Task_Champion(u8 taskId) { gTasks[taskId].tMugshotId = MUGSHOT_CHAMPION; DoMugshotTransition(taskId); } static void DoMugshotTransition(u8 taskId) { while (sMugshot_Funcs[gTasks[taskId].tState](&gTasks[taskId])); } static bool8 Mugshot_Init(struct Task *task) { u8 i; InitTransitionData(); ScanlineEffect_Clear(); Mugshots_CreateTrainerPics(task); task->tSinIndex = 0; task->tTopBannerX = 1; task->tBottomBannerX = DISPLAY_WIDTH - 1; sTransitionData->WININ = WININ_WIN0_ALL; sTransitionData->WINOUT = WINOUT_WIN01_BG1 | WINOUT_WIN01_BG2 | WINOUT_WIN01_BG3 | WINOUT_WIN01_OBJ | WINOUT_WIN01_CLR; sTransitionData->WIN0V = DISPLAY_HEIGHT; for (i = 0; i < DISPLAY_HEIGHT; i++) gScanlineEffectRegBuffers[1][i] = (DISPLAY_WIDTH << 8) | (DISPLAY_WIDTH + 1); SetVBlankCallback(VBlankCB_Mugshots); task->tState++; return FALSE; } static bool8 Mugshot_SetGfx(struct Task *task) { s16 i, j; u16 *tilemap, *tileset; const u16 *mugshotsMap; mugshotsMap = sMugshotsTilemap; GetBg0TilesDst(&tilemap, &tileset); CpuSet(sEliteFour_Tileset, tileset, 0xF0); LoadPalette(sOpponentMugshotsPals[task->tMugshotId], 0xF0, 0x20); LoadPalette(sPlayerMugshotsPals[gSaveBlock2Ptr->playerGender], 0xFA, 0xC); for (i = 0; i < 20; i++) { for (j = 0; j < 32; j++, mugshotsMap++) SET_TILE(tilemap, i, j, *mugshotsMap); } EnableInterrupts(INTR_FLAG_HBLANK); SetHBlankCallback(HBlankCB_Mugshots); task->tState++; return FALSE; } static bool8 Mugshot_ShowBanner(struct Task *task) { u8 i, sinIndex; u16* toStore; s16 x; s32 mergedValue; sTransitionData->VBlank_DMA = FALSE; toStore = gScanlineEffectRegBuffers[0]; sinIndex = task->tSinIndex; task->tSinIndex += 16; // Update top banner for (i = 0; i < DISPLAY_HEIGHT / 2; i++, toStore++, sinIndex += 16) { x = task->tTopBannerX + Sin(sinIndex, 16); if (x < 0) x = 1; if (x > DISPLAY_WIDTH) x = DISPLAY_WIDTH; *toStore = x; } // Update bottom banner for (; i < DISPLAY_HEIGHT; i++, toStore++, sinIndex += 16) { x = task->tBottomBannerX - Sin(sinIndex, 16); if (x < 0) x = 0; if (x > DISPLAY_WIDTH - 1) x = DISPLAY_WIDTH - 1; *toStore = (x << 8) | DISPLAY_WIDTH; } // Slide banners across screen task->tTopBannerX += 8; task->tBottomBannerX -= 8; if (task->tTopBannerX > DISPLAY_WIDTH) task->tTopBannerX = DISPLAY_WIDTH; if (task->tBottomBannerX < 0) task->tBottomBannerX = 0; mergedValue = *(s32*)(&task->tTopBannerX); if (mergedValue == DISPLAY_WIDTH) task->tState++; sTransitionData->BG0HOFS_Lower -= 8; sTransitionData->BG0HOFS_Upper += 8; sTransitionData->VBlank_DMA++; return FALSE; } static bool8 Mugshot_StartOpponentSlide(struct Task *task) { u8 i; u16* toStore; sTransitionData->VBlank_DMA = FALSE; for (i = 0, toStore = gScanlineEffectRegBuffers[0]; i < DISPLAY_HEIGHT; i++, toStore++) *toStore = DISPLAY_WIDTH; task->tState++; // Clear old data task->tSinIndex = 0; task->tTopBannerX = 0; task->tBottomBannerX = 0; sTransitionData->BG0HOFS_Lower -= 8; sTransitionData->BG0HOFS_Upper += 8; SetTrainerPicSlideDirection(task->tOpponentSpriteId, 0); SetTrainerPicSlideDirection(task->tPlayerSpriteId, 1); // Start opponent slide IncrementTrainerPicState(task->tOpponentSpriteId); PlaySE(SE_MUGSHOT); sTransitionData->VBlank_DMA++; return FALSE; } static bool8 Mugshot_WaitStartPlayerSlide(struct Task *task) { sTransitionData->BG0HOFS_Lower -= 8; sTransitionData->BG0HOFS_Upper += 8; // Start player's slide in once the opponent is finished if (IsTrainerPicSlideDone(task->tOpponentSpriteId)) { task->tState++; IncrementTrainerPicState(task->tPlayerSpriteId); } return FALSE; } static bool8 Mugshot_WaitPlayerSlide(struct Task *task) { sTransitionData->BG0HOFS_Lower -= 8; sTransitionData->BG0HOFS_Upper += 8; if (IsTrainerPicSlideDone(task->tPlayerSpriteId)) { sTransitionData->VBlank_DMA = FALSE; SetVBlankCallback(NULL); DmaStop(0); memset(gScanlineEffectRegBuffers[0], 0, DISPLAY_HEIGHT * 2); memset(gScanlineEffectRegBuffers[1], 0, DISPLAY_HEIGHT * 2); SetGpuReg(REG_OFFSET_WIN0H, DISPLAY_WIDTH); SetGpuReg(REG_OFFSET_BLDY, 0); task->tState++; task->tTimer = 0; task->tFadeSpread = 0; sTransitionData->BLDCNT = BLDCNT_TGT1_ALL | BLDCNT_EFFECT_LIGHTEN; SetVBlankCallback(VBlankCB_MugshotsFadeOut); } return FALSE; } static bool8 Mugshot_GradualWhiteFade(struct Task *task) { bool32 active; sTransitionData->VBlank_DMA = FALSE; active = TRUE; sTransitionData->BG0HOFS_Lower -= 8; sTransitionData->BG0HOFS_Upper += 8; if (task->tFadeSpread < DISPLAY_HEIGHT / 2) task->tFadeSpread += 2; if (task->tFadeSpread > DISPLAY_HEIGHT / 2) task->tFadeSpread = DISPLAY_HEIGHT / 2; if (++task->tTimer & 1) { s16 i; for (i = 0, active = FALSE; i <= task->tFadeSpread; i++) { // Fade starts in middle of screen and // spreads outwards in both directions. s16 index1 = DISPLAY_HEIGHT / 2 - i; s16 index2 = DISPLAY_HEIGHT / 2 + i; if (gScanlineEffectRegBuffers[0][index1] <= 15) { active = TRUE; gScanlineEffectRegBuffers[0][index1]++; } if (gScanlineEffectRegBuffers[0][index2] <= 15) { active = TRUE; gScanlineEffectRegBuffers[0][index2]++; } } } if (task->tFadeSpread == DISPLAY_HEIGHT / 2 && !active) task->tState++; sTransitionData->VBlank_DMA++; return FALSE; } // Set palette to white to replace the scanline white fade // before the screen fades to black. static bool8 Mugshot_InitFadeWhiteToBlack(struct Task *task) { sTransitionData->VBlank_DMA = FALSE; BlendPalettes(PALETTES_ALL, 16, RGB_WHITE); sTransitionData->BLDCNT = 0xFF; task->tTimer = 0; task->tState++; return TRUE; } static bool8 Mugshot_FadeToBlack(struct Task *task) { sTransitionData->VBlank_DMA = FALSE; task->tTimer++; memset(gScanlineEffectRegBuffers[0], task->tTimer, DISPLAY_HEIGHT * 2); if (task->tTimer > 15) task->tState++; sTransitionData->VBlank_DMA++; return FALSE; } static bool8 Mugshot_End(struct Task *task) { DmaStop(0); FadeScreenBlack(); DestroyTask(FindTaskIdByFunc(task->func)); return FALSE; } static void VBlankCB_Mugshots(void) { DmaStop(0); VBlankCB_BattleTransition(); if (sTransitionData->VBlank_DMA != 0) DmaCopy16(3, gScanlineEffectRegBuffers[0], gScanlineEffectRegBuffers[1], DISPLAY_HEIGHT * 2); REG_BG0VOFS = sTransitionData->BG0VOFS; REG_WININ = sTransitionData->WININ; REG_WINOUT = sTransitionData->WINOUT; REG_WIN0V = sTransitionData->WIN0V; DmaSet(0, gScanlineEffectRegBuffers[1], ®_WIN0H, B_TRANS_DMA_FLAGS); } static void VBlankCB_MugshotsFadeOut(void) { DmaStop(0); VBlankCB_BattleTransition(); if (sTransitionData->VBlank_DMA != 0) DmaCopy16(3, gScanlineEffectRegBuffers[0], gScanlineEffectRegBuffers[1], DISPLAY_HEIGHT * 2); REG_BLDCNT = sTransitionData->BLDCNT; DmaSet(0, gScanlineEffectRegBuffers[1], ®_BLDY, B_TRANS_DMA_FLAGS); } static void HBlankCB_Mugshots(void) { if (REG_VCOUNT < DISPLAY_HEIGHT / 2) REG_BG0HOFS = sTransitionData->BG0HOFS_Lower; else REG_BG0HOFS = sTransitionData->BG0HOFS_Upper; } static void Mugshots_CreateTrainerPics(struct Task *task) { struct Sprite *opponentSprite, *playerSprite; s16 mugshotId = task->tMugshotId; task->tOpponentSpriteId = CreateTrainerSprite(sMugshotsTrainerPicIDsTable[mugshotId], sMugshotsOpponentCoords[mugshotId][0] - 32, sMugshotsOpponentCoords[mugshotId][1] + 42, 0, gDecompressionBuffer); task->tPlayerSpriteId = CreateTrainerSprite(PlayerGenderToFrontTrainerPicId(gSaveBlock2Ptr->playerGender), DISPLAY_WIDTH + 32, 106, 0, gDecompressionBuffer); opponentSprite = &gSprites[task->tOpponentSpriteId]; playerSprite = &gSprites[task->tPlayerSpriteId]; opponentSprite->callback = SpriteCB_MugshotTrainerPic; playerSprite->callback = SpriteCB_MugshotTrainerPic; opponentSprite->oam.affineMode = ST_OAM_AFFINE_DOUBLE; playerSprite->oam.affineMode = ST_OAM_AFFINE_DOUBLE; opponentSprite->oam.matrixNum = AllocOamMatrix(); playerSprite->oam.matrixNum = AllocOamMatrix(); opponentSprite->oam.shape = SPRITE_SHAPE(64x32); playerSprite->oam.shape = SPRITE_SHAPE(64x32); opponentSprite->oam.size = SPRITE_SIZE(64x32); playerSprite->oam.size = SPRITE_SIZE(64x32); CalcCenterToCornerVec(opponentSprite, SPRITE_SHAPE(64x32), SPRITE_SIZE(64x32), ST_OAM_AFFINE_DOUBLE); CalcCenterToCornerVec(playerSprite, SPRITE_SHAPE(64x32), SPRITE_SIZE(64x32), ST_OAM_AFFINE_DOUBLE); SetOamMatrixRotationScaling(opponentSprite->oam.matrixNum, sMugshotsOpponentRotationScales[mugshotId][0], sMugshotsOpponentRotationScales[mugshotId][1], 0); SetOamMatrixRotationScaling(playerSprite->oam.matrixNum, -512, 512, 0); } static void SpriteCB_MugshotTrainerPic(struct Sprite *sprite) { while (sMugshotTrainerPicFuncs[sprite->sState](sprite)); } // Wait until IncrementTrainerPicState is called static bool8 MugshotTrainerPic_Pause(struct Sprite *sprite) { return FALSE; } static bool8 MugshotTrainerPic_Init(struct Sprite *sprite) { s16 speeds[ARRAY_COUNT(sTrainerPicSlideSpeeds)]; s16 accels[ARRAY_COUNT(sTrainerPicSlideAccels)]; memcpy(speeds, sTrainerPicSlideSpeeds, sizeof(sTrainerPicSlideSpeeds)); memcpy(accels, sTrainerPicSlideAccels, sizeof(sTrainerPicSlideAccels)); sprite->sState++; sprite->sSlideSpeed = speeds[sprite->sSlideDir]; sprite->sSlideAccel = accels[sprite->sSlideDir]; return TRUE; } static bool8 MugshotTrainerPic_Slide(struct Sprite *sprite) { sprite->x += sprite->sSlideSpeed; // Advance state when pic passes ~40% of screen if (sprite->sSlideDir && sprite->x < DISPLAY_WIDTH - 107) sprite->sState++; else if (!sprite->sSlideDir && sprite->x > 103) sprite->sState++; return FALSE; } static bool8 MugshotTrainerPic_SlideSlow(struct Sprite *sprite) { // Add acceleration value to speed, then add speed. // For both sides acceleration is opposite speed, so slide slows down. sprite->sSlideSpeed += sprite->sSlideAccel; sprite->x += sprite->sSlideSpeed; // Advance state when slide comes to a stop if (sprite->sSlideSpeed == 0) { sprite->sState++; sprite->sSlideAccel = -sprite->sSlideAccel; sprite->sDone = TRUE; } return FALSE; } // Slides trainer pic offscreen. This is never reached, because it's preceded // by a second MugshotTrainerPic_Pause, and IncrementTrainerPicState is // only called once per trainer pic. static bool8 MugshotTrainerPic_SlideOffscreen(struct Sprite *sprite) { sprite->sSlideSpeed += sprite->sSlideAccel; sprite->x += sprite->sSlideSpeed; if (sprite->x < -31 || sprite->x > DISPLAY_WIDTH + 31) sprite->sState++; return FALSE; } static void SetTrainerPicSlideDirection(s16 spriteId, s16 dirId) { gSprites[spriteId].sSlideDir = dirId; } static void IncrementTrainerPicState(s16 spriteId) { gSprites[spriteId].sState++; } static s16 IsTrainerPicSlideDone(s16 spriteId) { return gSprites[spriteId].sDone; } #undef sState #undef sSlideSpeed #undef sSlideAccel #undef sDone #undef sSlideDir #undef tSinIndex #undef tTopBannerX #undef tBottomBannerX #undef tTimer #undef tFadeSpread #undef tOpponentSpriteId #undef tPlayerSpriteId #undef tMugshotId //-------------------- // B_TRANSITION_SLICE //-------------------- #define tEffectX data[1] #define tSpeed data[2] #define tAccel data[3] static void Task_Slice(u8 taskId) { while (sSlice_Funcs[gTasks[taskId].tState](&gTasks[taskId])); } static bool8 Slice_Init(struct Task *task) { u16 i; InitTransitionData(); ScanlineEffect_Clear(); task->tSpeed = 1 << 8; task->tAccel = 1; sTransitionData->WININ = WININ_WIN0_ALL; sTransitionData->WINOUT = 0; sTransitionData->WIN0V = DISPLAY_HEIGHT; sTransitionData->VBlank_DMA = FALSE; for (i = 0; i < DISPLAY_HEIGHT; i++) { gScanlineEffectRegBuffers[1][i] = sTransitionData->cameraX; gScanlineEffectRegBuffers[1][DISPLAY_HEIGHT + i] = DISPLAY_WIDTH; } EnableInterrupts(INTR_FLAG_HBLANK); SetGpuRegBits(REG_OFFSET_DISPSTAT, DISPSTAT_HBLANK_INTR); SetVBlankCallback(VBlankCB_Slice); SetHBlankCallback(HBlankCB_Slice); task->tState++; return TRUE; } static bool8 Slice_Main(struct Task *task) { u16 i; sTransitionData->VBlank_DMA = FALSE; task->tEffectX += (task->tSpeed >> 8); if (task->tEffectX > DISPLAY_WIDTH) task->tEffectX = DISPLAY_WIDTH; if (task->tSpeed <= 0xFFF) task->tSpeed += task->tAccel; if (task->tAccel < 128) task->tAccel <<= 1; // multiplying by two for (i = 0; i < DISPLAY_HEIGHT; i++) { u16 *storeLoc1 = &gScanlineEffectRegBuffers[0][i]; u16 *storeLoc2 = &gScanlineEffectRegBuffers[0][i + DISPLAY_HEIGHT]; // Alternate rows if (i % 2) { *storeLoc1 = sTransitionData->cameraX + task->tEffectX; *storeLoc2 = DISPLAY_WIDTH - task->tEffectX; } else { *storeLoc1 = sTransitionData->cameraX - task->tEffectX; *storeLoc2 = (task->tEffectX << 8) | (DISPLAY_WIDTH + 1); } } if (task->tEffectX >= DISPLAY_WIDTH) task->tState++; sTransitionData->VBlank_DMA++; return FALSE; } static bool8 Slice_End(struct Task *task) { DmaStop(0); FadeScreenBlack(); DestroyTask(FindTaskIdByFunc(Task_Slice)); return FALSE; } static void VBlankCB_Slice(void) { DmaStop(0); VBlankCB_BattleTransition(); REG_WININ = sTransitionData->WININ; REG_WINOUT = sTransitionData->WINOUT; REG_WIN0V = sTransitionData->WIN0V; if (sTransitionData->VBlank_DMA) DmaCopy16(3, gScanlineEffectRegBuffers[0], gScanlineEffectRegBuffers[1], DISPLAY_HEIGHT * 4); DmaSet(0, &gScanlineEffectRegBuffers[1][DISPLAY_HEIGHT], ®_WIN0H, B_TRANS_DMA_FLAGS); } static void HBlankCB_Slice(void) { if (REG_VCOUNT < DISPLAY_HEIGHT) { u16 var = gScanlineEffectRegBuffers[1][REG_VCOUNT]; REG_BG1HOFS = var; REG_BG2HOFS = var; REG_BG3HOFS = var; } } #undef tEffectX #undef tSpeed #undef tAccel //-------------------------- // B_TRANSITION_SHRED_SPLIT //-------------------------- // Data starts at 4. Possible it shared data // with Slice above, which ends at 3. #define tDelayTimer data[4] #define tExtent data[5] #define tDelay data[6] static void Task_ShredSplit(u8 taskId) { while (sShredSplit_Funcs[gTasks[taskId].tState](&gTasks[taskId])); } static bool8 ShredSplit_Init(struct Task *task) { u16 i; InitTransitionData(); ScanlineEffect_Clear(); sTransitionData->WININ = WININ_WIN0_ALL; sTransitionData->WINOUT = 0; sTransitionData->WIN0V = DISPLAY_HEIGHT; for (i = 0; i < DISPLAY_HEIGHT; i++) { gScanlineEffectRegBuffers[1][i] = sTransitionData->cameraX; gScanlineEffectRegBuffers[1][DISPLAY_HEIGHT + i] = DISPLAY_WIDTH; gScanlineEffectRegBuffers[0][i] = sTransitionData->cameraX; gScanlineEffectRegBuffers[0][DISPLAY_HEIGHT + i] = DISPLAY_WIDTH; gScanlineEffectRegBuffers[0][DISPLAY_HEIGHT * 2 + i] = 0; gScanlineEffectRegBuffers[0][DISPLAY_HEIGHT * 3 + i] = 256; gScanlineEffectRegBuffers[0][DISPLAY_HEIGHT * 4 + i] = 1; } task->tDelayTimer = 0; task->tExtent = 0; task->tDelay = 7; EnableInterrupts(INTR_FLAG_HBLANK); SetVBlankCallback(VBlankCB_Slice); SetHBlankCallback(HBlankCB_Slice); task->tState++; return TRUE; } static bool8 ShredSplit_Main(struct Task *task) { u16 i, j, k; u8 baseY[ARRAY_COUNT(sShredSplit_SectionYCoords)]; s16 moveDirs[ARRAY_COUNT(sShredSplit_SectionMoveDirs)]; u8 linesFinished; u16 *ptr4, *ptr3, *ptr1, *ptr2; s16 y; memcpy(baseY, sShredSplit_SectionYCoords, sizeof(baseY)); memcpy(moveDirs, sShredSplit_SectionMoveDirs, sizeof(moveDirs)); sTransitionData->VBlank_DMA = FALSE; linesFinished = 0; for (i = 0; i <= task->tExtent; i++) { // Slide half of the pixel rows (alternating) right for (j = 0; j < 2; j++) { for (k = 0; k < 2; k++) { y = baseY[j] + (moveDirs[k] * -i * 2); if (y >= 0 && (y != DISPLAY_HEIGHT / 2 - 1 || j != 1)) { ptr4 = &gScanlineEffectRegBuffers[0][y + DISPLAY_HEIGHT * 2]; ptr3 = &gScanlineEffectRegBuffers[0][y + DISPLAY_HEIGHT * 3]; ptr1 = &gScanlineEffectRegBuffers[0][y + DISPLAY_HEIGHT * 4]; if (*ptr4 >= DISPLAY_WIDTH) { *ptr4 = DISPLAY_WIDTH; linesFinished++; } else { *ptr4 += (*ptr3 >> 8); if (*ptr1 <= 0x7F) *ptr1 *= 2; if (*ptr3 <= 0xFFF) *ptr3 += *ptr1; } ptr2 = &gScanlineEffectRegBuffers[0][y]; ptr3 = &gScanlineEffectRegBuffers[0][y + DISPLAY_HEIGHT]; *ptr2 = sTransitionData->cameraX + *ptr4; *ptr3 = DISPLAY_WIDTH - *ptr4; if (i == 0) break; } } } // Slide the other half of the rows left for (j = 0; j < 2; j++) { for (k = 0; k < 2; k++) { y = baseY[j] + 1 + (moveDirs[k] * -i * 2); if (y <= DISPLAY_HEIGHT && (y != DISPLAY_HEIGHT / 2 || j != 1)) { ptr4 = &gScanlineEffectRegBuffers[0][y + DISPLAY_HEIGHT * 2]; ptr3 = &gScanlineEffectRegBuffers[0][y + DISPLAY_HEIGHT * 3]; ptr1 = &gScanlineEffectRegBuffers[0][y + DISPLAY_HEIGHT * 4]; if (*ptr4 >= DISPLAY_WIDTH) { *ptr4 = DISPLAY_WIDTH; linesFinished++; } else { *ptr4 += (*ptr3 >> 8); if (*ptr1 <= 0x7F) *ptr1 *= 2; if (*ptr3 <= 0xFFF) *ptr3 += *ptr1; } ptr2 = &gScanlineEffectRegBuffers[0][y]; ptr3 = &gScanlineEffectRegBuffers[0][y + DISPLAY_HEIGHT]; *ptr2 = sTransitionData->cameraX - *ptr4; *ptr3 = (*ptr4 << 8) | (DISPLAY_WIDTH + 1); if (i == 0) break; } } } } // Count down to next move if (--task->tDelayTimer < 0) task->tDelayTimer = 0; // Try increase effect's extent if (task->tDelayTimer <= 0 && task->tExtent + 1 <= DISPLAY_HEIGHT / 8) { task->tDelayTimer = task->tDelay; task->tExtent++; } // All lines have reached screen width, move on. if (linesFinished >= DISPLAY_HEIGHT) task->tState++; sTransitionData->VBlank_DMA++; return FALSE; } // This function never increments the state counter, because the loop condition // is always false, resulting in the game being stuck in an infinite loop. // It's possible this transition is only partially // done and the second part was left out. // In any case removing or bypassing this state allows the transition to finish. static bool8 ShredSplit_BrokenCheck(struct Task *task) { u16 i; bool32 done = TRUE; u16 checkVar2 = 0xFF10; for (i = 0; i < DISPLAY_HEIGHT; i++) { if (gScanlineEffectRegBuffers[1][i] != DISPLAY_WIDTH && gScanlineEffectRegBuffers[1][i] != checkVar2) done = FALSE; } if (done == TRUE) task->tState++; return FALSE; } static bool8 ShredSplit_End(struct Task *task) { DmaStop(0); FadeScreenBlack(); DestroyTask(FindTaskIdByFunc(Task_ShredSplit)); return FALSE; } #undef tDelayTimer #undef tExtent #undef tDelay //----------------------------------------------------------- // B_TRANSITION_BLACKHOLE and B_TRANSITION_BLACKHOLE_PULSATE //----------------------------------------------------------- #define tRadius data[1] #define tGrowSpeed data[2] #define tSinIndex data[5] #define tVibrateId data[6] #define tAmplitude data[6] // Used differently by the two transitions #define tFlag data[7] // Used generally to indicate an action has taken place. static void Task_Blackhole(u8 taskId) { while (sBlackhole_Funcs[gTasks[taskId].tState](&gTasks[taskId])); } static void Task_BlackholePulsate(u8 taskId) { while (sBlackholePulsate_Funcs[gTasks[taskId].tState](&gTasks[taskId])); } // Init is shared by both transitions static bool8 Blackhole_Init(struct Task *task) { s32 i; InitTransitionData(); ScanlineEffect_Clear(); sTransitionData->WININ = 0; sTransitionData->WINOUT = WINOUT_WIN01_ALL; sTransitionData->WIN0H = DISPLAY_WIDTH; sTransitionData->WIN0V = DISPLAY_HEIGHT; for (i = 0; i < DISPLAY_HEIGHT; i++) gScanlineEffectRegBuffers[1][i] = 0; SetVBlankCallback(VBlankCB_CircularMask); task->tState++; task->tRadius = 1; task->tGrowSpeed = 1 << 8; task->tFlag = FALSE; return FALSE; } static bool8 Blackhole_GrowEnd(struct Task *task) { if (task->tFlag == TRUE) { DmaStop(0); SetVBlankCallback(NULL); DestroyTask(FindTaskIdByFunc(task->func)); } else { sTransitionData->VBlank_DMA = FALSE; if (task->tGrowSpeed < 1024) task->tGrowSpeed += 128; if (task->tRadius < DISPLAY_HEIGHT) task->tRadius += task->tGrowSpeed >> 8; if (task->tRadius > DISPLAY_HEIGHT) task->tRadius = DISPLAY_HEIGHT; SetCircularMask(gScanlineEffectRegBuffers[0], DISPLAY_WIDTH / 2, DISPLAY_HEIGHT / 2, task->tRadius); if (task->tRadius == DISPLAY_HEIGHT) { task->tFlag = TRUE; FadeScreenBlack(); } else { sTransitionData->VBlank_DMA++; } } return FALSE; } static bool8 Blackhole_Vibrate(struct Task *task) { sTransitionData->VBlank_DMA = FALSE; if (task->tFlag == FALSE) { task->tFlag++; task->tRadius = 48; task->tVibrateId = 0; } task->tRadius += sBlackhole_Vibrations[task->tVibrateId]; task->tVibrateId = (task->tVibrateId + 1) % (int)ARRAY_COUNT(sBlackhole_Vibrations); SetCircularMask(gScanlineEffectRegBuffers[0], DISPLAY_WIDTH / 2, DISPLAY_HEIGHT / 2, task->tRadius); if (task->tRadius < 9) { task->tState++; task->tFlag = FALSE; } sTransitionData->VBlank_DMA++; return FALSE; } static bool8 BlackholePulsate_Main(struct Task *task) { u16 index; // should be s16 I think s16 amplitude; sTransitionData->VBlank_DMA = FALSE; if (task->tFlag == FALSE) { task->tFlag++; task->tSinIndex = 2; task->tAmplitude = 2; } if (task->tRadius > DISPLAY_HEIGHT) task->tRadius = DISPLAY_HEIGHT; SetCircularMask(gScanlineEffectRegBuffers[0], DISPLAY_WIDTH / 2, DISPLAY_HEIGHT / 2, task->tRadius); if (task->tRadius == DISPLAY_HEIGHT) { DmaStop(0); FadeScreenBlack(); DestroyTask(FindTaskIdByFunc(task->func)); } index = task->tSinIndex; if ((task->tSinIndex & 0xFF) <= 128) { amplitude = task->tAmplitude; task->tSinIndex += 8; } else { amplitude = task->tAmplitude - 1; task->tSinIndex += 16; } task->tRadius += Sin(index & 0xFF, amplitude); if (task->tRadius <= 0) task->tRadius = 1; if (task->tSinIndex >= 0xFF) { task->tSinIndex >>= 8; task->tAmplitude++; } sTransitionData->VBlank_DMA++; return FALSE; } #undef tRadius #undef tGrowSpeed #undef tSinIndex #undef tVibrateId #undef tAmplitude #undef tFlag //--------------------------------- // B_TRANSITION_RECTANGULAR_SPIRAL //--------------------------------- static void Task_RectangularSpiral(u8 taskId) { while (sRectangularSpiral_Funcs[gTasks[taskId].tState](&gTasks[taskId])); } static bool8 RectangularSpiral_Init(struct Task *task) { u16 *tilemap, *tileset; GetBg0TilesDst(&tilemap, &tileset); CpuCopy16(sShrinkingBoxTileset, tileset, 0x20); CpuCopy16(&sShrinkingBoxTileset[0x70], &tileset[0x20], 0x20); CpuFill16(0xF0 << 8, tilemap, BG_SCREEN_SIZE); LoadPalette(sFieldEffectPal_Pokeball, 0xF0, sizeof(sFieldEffectPal_Pokeball)); task->data[3] = 1; task->tState++; // Line starting in top left sRectangularSpiralLines[0].state = SPIRAL_INWARD_START; sRectangularSpiralLines[0].position = -1; sRectangularSpiralLines[0].moveIdx = 1; sRectangularSpiralLines[0].reboundPosition = 308; sRectangularSpiralLines[0].outward = FALSE; // Line starting in bottom right sRectangularSpiralLines[1].state = SPIRAL_INWARD_START; sRectangularSpiralLines[1].position = -1; sRectangularSpiralLines[1].moveIdx = 1; sRectangularSpiralLines[1].reboundPosition = 308; sRectangularSpiralLines[1].outward = FALSE; // Line starting in top right sRectangularSpiralLines[2].state = SPIRAL_INWARD_START; sRectangularSpiralLines[2].position = -3; sRectangularSpiralLines[2].moveIdx = 1; sRectangularSpiralLines[2].reboundPosition = 307; sRectangularSpiralLines[2].outward = FALSE; // Line starting in bottom left sRectangularSpiralLines[3].state = SPIRAL_INWARD_START; sRectangularSpiralLines[3].position = -3; sRectangularSpiralLines[3].moveIdx = 1; sRectangularSpiralLines[3].reboundPosition = 307; sRectangularSpiralLines[3].outward = FALSE; return FALSE; } static bool8 RectangularSpiral_Main(struct Task *task) { u16 *tilemap, *tileset; u8 i; u16 j; bool32 done = TRUE; GetBg0TilesDst(&tilemap, &tileset); // Draw 2 tiles at a time for each spiral line for (i = 0; i < 2; i++) { for (j = 0; j < ARRAY_COUNT(sRectangularSpiralLines); j++) { s16 position = 0; s16 x = 0, y = 0; if (UpdateRectangularSpiralLine(sRectangularSpiral_MoveDataTables[j / 2], &sRectangularSpiralLines[j])) { // The line moved to a new position, draw the tile. done = FALSE; position = sRectangularSpiralLines[j].position; // Invert position for the two lines that start at the bottom. if ((j % 2) == 1) position = 637 - position; x = position % 32; y = position / 32; SET_TILE(tilemap, y, x, 2); } } } if (done == TRUE) task->tState++; return FALSE; } static bool8 RectangularSpiral_End(struct Task *task) { DmaStop(0); FadeScreenBlack(); DestroyTask(FindTaskIdByFunc(task->func)); return FALSE; } // Returns TRUE if a tile should be drawn, FALSE otherwise static bool16 UpdateRectangularSpiralLine(const s16 * const *moveDataTable, struct RectangularSpiralLine *line) { const s16 *moveData = moveDataTable[line->state]; // Has spiral finished? // Note that most move data arrays endsin SPIRAL_END but it is // only ever reached on the final array of spiraling outward. if (moveData[line->moveIdx] == SPIRAL_END) return FALSE; // Presumably saving data for debug. sDebug_RectangularSpiralData = moveData[0]; sDebug_RectangularSpiralData = moveData[1]; sDebug_RectangularSpiralData = moveData[2]; sDebug_RectangularSpiralData = moveData[3]; // Note that for the two lines originating at the bottom the // position is inverted, so the directions are flipped. // i.e. position += 1 is right for the top lines and left // for their inverted partners on the bottom. switch (moveData[0]) { case MOVE_RIGHT: line->position += 1; break; case MOVE_LEFT: line->position -= 1; break; case MOVE_UP: line->position -= 32; break; case MOVE_DOWN: line->position += 32; break; } // Below check is never true. // SPIRAL_END was already checked, and position is never >= 640 if (line->position >= 640 || moveData[line->moveIdx] == SPIRAL_END) return FALSE; if (!line->outward && moveData[line->moveIdx] == SPIRAL_REBOUND) { // Line has reached the final point of spiraling inward. // Time to flip and start spiraling outward. line->outward = TRUE; line->moveIdx = 1; line->position = line->reboundPosition; line->state = SPIRAL_OUTWARD_START; } // Reached move target, advance to next movement. if (line->position == moveData[line->moveIdx]) { line->state++; if (line->outward == TRUE) { if (line->state > SPIRAL_OUTWARD_END) { // Still spiraling outward, loop back to the first state // but use the second set of move targets. // For example, the 28 in sRectangularSpiral_Major_OutwardUp line->moveIdx++; line->state = SPIRAL_OUTWARD_START; } } else { if (line->state > SPIRAL_INWARD_END) { // Still spiraling inward, loop back to the first state // but use the second set of move targets. // For example, the 275 in sRectangularSpiral_Major_InwardRight line->moveIdx++; line->state = SPIRAL_INWARD_START; } } } return TRUE; } //---------------------- // B_TRANSITION_GROUDON //---------------------- #define tTimer data[1] static void Task_Groudon(u8 taskId) { while (sGroudon_Funcs[gTasks[taskId].tState](&gTasks[taskId])); } static bool8 Groudon_Init(struct Task *task) { u16 *tilemap, *tileset; GetBg0TilesDst(&tilemap, &tileset); CpuFill16(0, tilemap, BG_SCREEN_SIZE); LZ77UnCompVram(sGroudon_Tileset, tileset); LZ77UnCompVram(sGroudon_Tilemap, tilemap); task->tState++; task->tTimer = 0; return FALSE; } static bool8 Groudon_PaletteFlash(struct Task *task) { if (task->tTimer % 3 == 0) { u16 offset = (task->tTimer % 30) / 3; LoadPalette(&sGroudon1_Palette[offset * 16], 0xF0, 0x20); } if (++task->tTimer > 58) { task->tState++; task->tTimer = 0; } return FALSE; } static bool8 Groudon_PaletteBrighten(struct Task *task) { if (task->tTimer % 5 == 0) { s16 offset = task->tTimer / 5; LoadPalette(&sGroudon2_Palette[offset * 16], 0xF0, 0x20); } if (++task->tTimer > 68) { task->tState++; task->tTimer = 0; task->tEndDelay = 30; } return FALSE; } #undef tTimer #undef tEndDelay //----------------------- // B_TRANSITION_RAYQUAZA //----------------------- #define tTimer data[1] #define tGrowSpeed data[2] // Shared from B_TRANSITION_BLACKHOLE #define tFlag data[7] // Shared from B_TRANSITION_BLACKHOLE static void Task_Rayquaza(u8 taskId) { while (sRayquaza_Funcs[gTasks[taskId].tState](&gTasks[taskId])); } static bool8 Rayquaza_Init(struct Task *task) { u16 *tilemap, *tileset; u16 i; InitTransitionData(); ScanlineEffect_Clear(); SetGpuReg(REG_OFFSET_BG0CNT, BGCNT_CHARBASE(2) | BGCNT_SCREENBASE(26) | BGCNT_TXT256x512); GetBg0TilesDst(&tilemap, &tileset); CpuFill16(0, tilemap, BG_SCREEN_SIZE); CpuCopy16(sRayquaza_Tileset, tileset, 0x2000); sTransitionData->counter = 0; task->tState++; LoadPalette(&sRayquaza_Palette[0x50], 0xF0, 0x20); for (i = 0; i < DISPLAY_HEIGHT; i++) { gScanlineEffectRegBuffers[0][i] = 0; gScanlineEffectRegBuffers[1][i] = 0x100; } SetVBlankCallback(VBlankCB_Rayquaza); return FALSE; } static bool8 Rayquaza_SetGfx(struct Task *task) { u16 *tilemap, *tileset; GetBg0TilesDst(&tilemap, &tileset); CpuCopy16(sRayquaza_Tilemap, tilemap, sizeof(sRayquaza_Tilemap)); task->tState++; return FALSE; } static bool8 Rayquaza_PaletteFlash(struct Task *task) { if ((task->tTimer % 4) == 0) { u16 value = task->tTimer / 4; const u16 *palPtr = &sRayquaza_Palette[(value + 5) * 16]; LoadPalette(palPtr, 0xF0, 0x20); } if (++task->tTimer > 40) { task->tState++; task->tTimer = 0; } return FALSE; } static bool8 Rayquaza_FadeToBlack(struct Task *task) { if (++task->tTimer > 20) { task->tState++; task->tTimer = 0; BeginNormalPaletteFade(PALETTES_OBJECTS | (1 << 15), 2, 0, 16, RGB_BLACK); } return FALSE; } static bool8 Rayquaza_WaitFade(struct Task *task) { if (!gPaletteFade.active) { sTransitionData->counter = 1; task->tState++; } return FALSE; } static bool8 Rayquaza_SetBlack(struct Task *task) { BlendPalettes(PALETTES_BG & ~(1 << 15), 8, RGB_BLACK); BlendPalettes(PALETTES_OBJECTS | (1 << 15), 0, RGB_BLACK); task->tState++; return FALSE; } static bool8 Rayquaza_TriRing(struct Task *task) { if ((task->tTimer % 3) == 0) { u16 value = task->tTimer / 3; const u16 *palPtr = &sRayquaza_Palette[(value + 0) * 16]; LoadPalette(palPtr, 0xF0, 0x20); } if (++task->tTimer >= 40) { u16 i; sTransitionData->WININ = 0; sTransitionData->WINOUT = WINOUT_WIN01_ALL; sTransitionData->WIN0H = DISPLAY_WIDTH; sTransitionData->WIN0V = DISPLAY_HEIGHT; for (i = 0; i < DISPLAY_HEIGHT; i++) gScanlineEffectRegBuffers[1][i] = 0; SetVBlankCallback(VBlankCB_CircularMask); task->tState++; task->tGrowSpeed = 1 << 8; task->tFlag = FALSE; ClearGpuRegBits(REG_OFFSET_DISPCNT, DISPCNT_BG0_ON); } return FALSE; } static void VBlankCB_Rayquaza(void) { void *dmaSrc; DmaStop(0); VBlankCB_BattleTransition(); if (sTransitionData->counter == 0) dmaSrc = gScanlineEffectRegBuffers[0]; else if (sTransitionData->counter == 1) dmaSrc = gScanlineEffectRegBuffers[1]; else dmaSrc = gScanlineEffectRegBuffers[0]; DmaSet(0, dmaSrc, ®_BG0VOFS, B_TRANS_DMA_FLAGS); } #undef tTimer #undef tGrowSpeed #undef tFlag //------------------------------ // B_TRANSITION_WHITE_BARS_FADE //------------------------------ #define sFade data[0] #define sFinished data[1] #define sDestroyAttempts data[2] #define sDelay data[5] #define sIsMainSprite data[6] #define FADE_TARGET (16 << 8) static void Task_WhiteBarsFade(u8 taskId) { while (sWhiteBarsFade_Funcs[gTasks[taskId].tState](&gTasks[taskId])); } static bool8 WhiteBarsFade_Init(struct Task *task) { u16 i; InitTransitionData(); ScanlineEffect_Clear(); sTransitionData->BLDCNT = BLDCNT_TGT1_ALL | BLDCNT_EFFECT_LIGHTEN; sTransitionData->BLDY = 0; sTransitionData->WININ = WININ_WIN0_BG1 | WININ_WIN0_BG2 | WININ_WIN0_BG3 | WININ_WIN0_OBJ; sTransitionData->WINOUT = WINOUT_WIN01_ALL; sTransitionData->WIN0V = DISPLAY_HEIGHT; for (i = 0; i < DISPLAY_HEIGHT; i++) { gScanlineEffectRegBuffers[1][i] = 0; gScanlineEffectRegBuffers[1][i + DISPLAY_HEIGHT] = DISPLAY_WIDTH; } EnableInterrupts(INTR_FLAG_HBLANK); SetHBlankCallback(HBlankCB_WhiteBarsFade); SetVBlankCallback(VBlankCB_WhiteBarsFade); task->tState++; return FALSE; } static bool8 WhiteBarsFade_StartBars(struct Task *task) { s16 i, posY; s16 delays[ARRAY_COUNT(sWhiteBarsFade_StartDelays)]; struct Sprite *sprite; memcpy(delays, sWhiteBarsFade_StartDelays, sizeof(sWhiteBarsFade_StartDelays)); for (i = 0, posY = 0; i < NUM_WHITE_BARS; i++, posY += DISPLAY_HEIGHT / NUM_WHITE_BARS) { sprite = &gSprites[CreateInvisibleSprite(SpriteCB_WhiteBarFade)]; sprite->x = DISPLAY_WIDTH; sprite->y = posY; sprite->sDelay = delays[i]; } // Set on one sprite only. This one will enable the DMA // copy in VBlank and wait for the others to destroy. sprite->sIsMainSprite++; task->tState++; return FALSE; } static bool8 WhiteBarsFade_WaitBars(struct Task *task) { sTransitionData->VBlank_DMA = 0; if (sTransitionData->counter >= NUM_WHITE_BARS) { BlendPalettes(PALETTES_ALL, 16, RGB_WHITE); task->tState++; } return FALSE; } static bool8 WhiteBarsFade_BlendToBlack(struct Task *task) { sTransitionData->VBlank_DMA = 0; DmaStop(0); SetVBlankCallback(0); SetHBlankCallback(0); sTransitionData->WIN0H = DISPLAY_WIDTH; sTransitionData->BLDY = 0; sTransitionData->BLDCNT = 0xFF; sTransitionData->WININ = WININ_WIN0_ALL; SetVBlankCallback(VBlankCB_WhiteBarsFade_Blend); task->tState++; return FALSE; } static bool8 WhiteBarsFade_End(struct Task *task) { if (++sTransitionData->BLDY > 16) { FadeScreenBlack(); DestroyTask(FindTaskIdByFunc(Task_WhiteBarsFade)); } return FALSE; } static void VBlankCB_WhiteBarsFade(void) { DmaStop(0); VBlankCB_BattleTransition(); REG_BLDCNT = sTransitionData->BLDCNT; REG_WININ = sTransitionData->WININ; REG_WINOUT = sTransitionData->WINOUT; REG_WIN0V = sTransitionData->WIN0V; if (sTransitionData->VBlank_DMA) DmaCopy16(3, gScanlineEffectRegBuffers[0], gScanlineEffectRegBuffers[1], DISPLAY_HEIGHT * 4); DmaSet(0, &gScanlineEffectRegBuffers[1][DISPLAY_HEIGHT], ®_WIN0H, B_TRANS_DMA_FLAGS); } static void VBlankCB_WhiteBarsFade_Blend(void) { VBlankCB_BattleTransition(); REG_BLDY = sTransitionData->BLDY; REG_BLDCNT = sTransitionData->BLDCNT; REG_WININ = sTransitionData->WININ; REG_WINOUT = sTransitionData->WINOUT; REG_WIN0H = sTransitionData->WIN0H; REG_WIN0V = sTransitionData->WIN0V; } static void HBlankCB_WhiteBarsFade(void) { REG_BLDY = gScanlineEffectRegBuffers[1][REG_VCOUNT]; } static void SpriteCB_WhiteBarFade(struct Sprite *sprite) { if (sprite->sDelay) { sprite->sDelay--; if (sprite->sIsMainSprite) sTransitionData->VBlank_DMA = 1; } else { u16 i; u16* ptr1 = &gScanlineEffectRegBuffers[0][sprite->y]; u16* ptr2 = &gScanlineEffectRegBuffers[0][sprite->y + DISPLAY_HEIGHT]; for (i = 0; i < DISPLAY_HEIGHT / NUM_WHITE_BARS; i++) { ptr1[i] = sprite->sFade >> 8; ptr2[i] = (u8)sprite->x; } if (sprite->x == 0 && sprite->sFade == FADE_TARGET) sprite->sFinished = TRUE; sprite->x -= 16; sprite->sFade += FADE_TARGET / 32; if (sprite->x < 0) sprite->x = 0; if (sprite->sFade > FADE_TARGET) sprite->sFade = FADE_TARGET; if (sprite->sIsMainSprite) sTransitionData->VBlank_DMA = 1; if (sprite->sFinished) { // If not the main sprite, destroy self. Otherwise, wait until the // others have destroyed themselves, or until enough time has elapsed. if (!sprite->sIsMainSprite || (sTransitionData->counter >= NUM_WHITE_BARS - 1 && sprite->sDestroyAttempts++ > 7)) { sTransitionData->counter++; DestroySprite(sprite); } } } } #undef sFade #undef sFinished #undef sDestroyAttempts #undef sDelay #undef sIsMainSprite //--------------------------- // B_TRANSITION_GRID_SQUARES //--------------------------- #define tDelay data[1] #define tShrinkStage data[2] static void Task_GridSquares(u8 taskId) { while (sGridSquares_Funcs[gTasks[taskId].tState](&gTasks[taskId])); } static bool8 GridSquares_Init(struct Task *task) { u16 *tilemap, *tileset; GetBg0TilesDst(&tilemap, &tileset); CpuSet(sShrinkingBoxTileset, tileset, 16); CpuFill16(0xF0 << 8, tilemap, BG_SCREEN_SIZE); LoadPalette(sFieldEffectPal_Pokeball, 0xF0, sizeof(sFieldEffectPal_Pokeball)); task->tState++; return FALSE; } static bool8 GridSquares_Main(struct Task *task) { u16* tileset; if (task->tDelay == 0) { GetBg0TilemapDst(&tileset); task->tDelay = 3; task->tShrinkStage++; CpuSet(&sShrinkingBoxTileset[task->tShrinkStage * 8], tileset, 16); if (task->tShrinkStage > 13) { task->tState++; task->tDelay = 16; } } task->tDelay--; return FALSE; } static bool8 GridSquares_End(struct Task *task) { if (--task->tDelay == 0) { FadeScreenBlack(); DestroyTask(FindTaskIdByFunc(Task_GridSquares)); } return FALSE; } #undef tDelay #undef tShrinkStage //--------------------------- // B_TRANSITION_ANGLED_WIPES //--------------------------- #define tWipeId data[1] #define tDir data[2] #define tDelay data[3] static void Task_AngledWipes(u8 taskId) { while (sAngledWipes_Funcs[gTasks[taskId].tState](&gTasks[taskId])); } static bool8 AngledWipes_Init(struct Task *task) { u16 i; InitTransitionData(); ScanlineEffect_Clear(); sTransitionData->WININ = WININ_WIN0_ALL; sTransitionData->WINOUT = 0; sTransitionData->WIN0V = DISPLAY_HEIGHT; for (i = 0; i < DISPLAY_HEIGHT; i++) gScanlineEffectRegBuffers[0][i] = DISPLAY_WIDTH; CpuSet(gScanlineEffectRegBuffers[0], gScanlineEffectRegBuffers[1], DISPLAY_HEIGHT); SetVBlankCallback(VBlankCB_AngledWipes); task->tState++; return TRUE; } static bool8 AngledWipes_SetWipeData(struct Task *task) { InitBlackWipe(sTransitionData->data, sAngledWipes_MoveData[task->tWipeId][0], sAngledWipes_MoveData[task->tWipeId][1], sAngledWipes_MoveData[task->tWipeId][2], sAngledWipes_MoveData[task->tWipeId][3], 1, 1); task->tDir = sAngledWipes_MoveData[task->tWipeId][4]; task->tState++; return TRUE; } static bool8 AngledWipes_DoWipe(struct Task *task) { s16 i; bool8 finished; sTransitionData->VBlank_DMA = 0; for (i = 0, finished = FALSE; i < 16; i++) { s16 r3 = gScanlineEffectRegBuffers[0][sTransitionData->tWipeCurrY] >> 8; s16 r4 = gScanlineEffectRegBuffers[0][sTransitionData->tWipeCurrY] & 0xFF; if (task->tDir == 0) { // Moving down if (r3 < sTransitionData->tWipeCurrX) r3 = sTransitionData->tWipeCurrX; if (r3 > r4) r3 = r4; } else { // Moving up if (r4 > sTransitionData->tWipeCurrX) r4 = sTransitionData->tWipeCurrX; if (r4 <= r3) r4 = r3; } gScanlineEffectRegBuffers[0][sTransitionData->tWipeCurrY] = (r4) | (r3 << 8); if (finished) { task->tState++; break; } finished = UpdateBlackWipe(sTransitionData->data, TRUE, TRUE); } sTransitionData->VBlank_DMA++; return FALSE; } static bool8 AngledWipes_TryEnd(struct Task *task) { if (++task->tWipeId < NUM_ANGLED_WIPES) { // Continue with next wipe task->tState++; task->tDelay = sAngledWipes_EndDelays[task->tWipeId - 1]; return TRUE; } else { // End transition DmaStop(0); FadeScreenBlack(); DestroyTask(FindTaskIdByFunc(Task_AngledWipes)); return FALSE; } } static bool8 AngledWipes_StartNext(struct Task *task) { if (--task->tDelay == 0) { // Return to AngledWipes_SetWipeData task->tState = 1; return TRUE; } return FALSE; } static void VBlankCB_AngledWipes(void) { DmaStop(0); VBlankCB_BattleTransition(); if (sTransitionData->VBlank_DMA) DmaCopy16(3, gScanlineEffectRegBuffers[0], gScanlineEffectRegBuffers[1], DISPLAY_HEIGHT * 2); REG_WININ = sTransitionData->WININ; REG_WINOUT = sTransitionData->WINOUT; REG_WIN0V = sTransitionData->WIN0V; REG_WIN0H = gScanlineEffectRegBuffers[1][0]; DmaSet(0, gScanlineEffectRegBuffers[1], ®_WIN0H, B_TRANS_DMA_FLAGS); } #undef tWipeId #undef tDir #undef tDelay //----------------------------------- // Transition intro //----------------------------------- #define tFadeToGrayDelay data[1] #define tFadeFromGrayDelay data[2] #define tNumFades data[3] #define tFadeToGrayIncrement data[4] #define tFadeFromGrayIncrement data[5] #define tDelayTimer data[6] #define tBlend data[7] static void CreateIntroTask(s16 fadeToGrayDelay, s16 fadeFromGrayDelay, s16 numFades, s16 fadeToGrayIncrement, s16 fadeFromGrayIncrement) { u8 taskId = CreateTask(Task_BattleTransition_Intro, 3); gTasks[taskId].tFadeToGrayDelay = fadeToGrayDelay; gTasks[taskId].tFadeFromGrayDelay = fadeFromGrayDelay; gTasks[taskId].tNumFades = numFades; gTasks[taskId].tFadeToGrayIncrement = fadeToGrayIncrement; gTasks[taskId].tFadeFromGrayIncrement = fadeFromGrayIncrement; gTasks[taskId].tDelayTimer = fadeToGrayDelay; } static bool8 IsIntroTaskDone(void) { if (FindTaskIdByFunc(Task_BattleTransition_Intro) == TASK_NONE) return TRUE; else return FALSE; } void Task_BattleTransition_Intro(u8 taskId) { while (sTransitionIntroFuncs[gTasks[taskId].tState](&gTasks[taskId])); } static bool8 TransitionIntro_FadeToGray(struct Task *task) { if (task->tDelayTimer == 0 || --task->tDelayTimer == 0) { task->tDelayTimer = task->tFadeToGrayDelay; task->tBlend += task->tFadeToGrayIncrement; if (task->tBlend > 16) task->tBlend = 16; BlendPalettes(PALETTES_ALL, task->tBlend, RGB(11, 11, 11)); } if (task->tBlend >= 16) { // Fade to gray complete, start fade back task->tState++; task->tDelayTimer = task->tFadeFromGrayDelay; } return FALSE; } static bool8 TransitionIntro_FadeFromGray(struct Task *task) { if (task->tDelayTimer == 0 || --task->tDelayTimer == 0) { task->tDelayTimer = task->tFadeFromGrayDelay; task->tBlend -= task->tFadeFromGrayIncrement; if (task->tBlend < 0) task->tBlend = 0; BlendPalettes(PALETTES_ALL, task->tBlend, RGB(11, 11, 11)); } if (task->tBlend == 0) { if (--task->tNumFades == 0) { // All fades done, end intro DestroyTask(FindTaskIdByFunc(Task_BattleTransition_Intro)); } else { // Fade from gray complete, start new fade task->tDelayTimer = task->tFadeToGrayDelay; task->tState = 0; } } return FALSE; } #undef tFadeToGrayDelay #undef tFadeFromGrayDelay #undef tNumFades #undef tFadeToGrayIncrement #undef tFadeFromGrayIncrement #undef tDelayTimer #undef tBlend //----------------------------------- // General transition functions //----------------------------------- static void InitTransitionData(void) { memset(sTransitionData, 0, sizeof(*sTransitionData)); GetCameraOffsetWithPan(&sTransitionData->cameraX, &sTransitionData->cameraY); } static void VBlankCB_BattleTransition(void) { LoadOam(); ProcessSpriteCopyRequests(); TransferPlttBuffer(); } static void GetBg0TilemapDst(u16 **tileset) { u16 charBase = REG_BG0CNT >> 2; charBase <<= 14; *tileset = (u16*)(BG_VRAM + charBase); } void GetBg0TilesDst(u16 **tilemap, u16 **tileset) { u16 screenBase = REG_BG0CNT >> 8; u16 charBase = REG_BG0CNT >> 2; screenBase <<= 11; charBase <<= 14; *tilemap = (u16*)(BG_VRAM + screenBase); *tileset = (u16*)(BG_VRAM + charBase); } static void FadeScreenBlack(void) { BlendPalettes(PALETTES_ALL, 16, RGB_BLACK); } static void SetSinWave(s16 *array, s16 sinAdd, s16 index, s16 indexIncrementer, s16 amplitude, s16 arrSize) { u8 i; for (i = 0; arrSize > 0; arrSize--, i++, index += indexIncrementer) array[i] = sinAdd + Sin(index & 0xFF, amplitude); } static void SetCircularMask(u16 *buffer, s16 centerX, s16 centerY, s16 radius) { s16 i; memset(buffer, 10, DISPLAY_HEIGHT * sizeof(u16)); for (i = 0; i < 64; i++) { s16 sinResult, cosResult; s16 drawXLeft, drawYBottNext, drawYTopNext, drawX, drawYTop, drawYBott; sinResult = Sin(i, radius); cosResult = Cos(i, radius); drawXLeft = centerX - sinResult; drawX = centerX + sinResult; drawYTop = centerY - cosResult; drawYBott = centerY + cosResult; if (drawXLeft < 0) drawXLeft = 0; if (drawX > DISPLAY_WIDTH) drawX = DISPLAY_WIDTH; if (drawYTop < 0) drawYTop = 0; if (drawYBott > DISPLAY_HEIGHT - 1) drawYBott = DISPLAY_HEIGHT - 1; drawX |= (drawXLeft << 8); buffer[drawYTop] = drawX; buffer[drawYBott] = drawX; cosResult = Cos(i + 1, radius); drawYTopNext = centerY - cosResult; drawYBottNext = centerY + cosResult; if (drawYTopNext < 0) drawYTopNext = 0; if (drawYBottNext > DISPLAY_HEIGHT - 1) drawYBottNext = DISPLAY_HEIGHT - 1; while (drawYTop > drawYTopNext) buffer[--drawYTop] = drawX; while (drawYTop < drawYTopNext) buffer[++drawYTop] = drawX; while (drawYBott > drawYBottNext) buffer[--drawYBott] = drawX; while (drawYBott < drawYBottNext) buffer[++drawYBott] = drawX; } } static void InitBlackWipe(s16 *data, s16 startX, s16 startY, s16 endX, s16 endY, s16 xMove, s16 yMove) { tWipeStartX = startX; tWipeStartY = startY; tWipeCurrX = startX; tWipeCurrY = startY; tWipeEndX = endX; tWipeEndY = endY; tWipeXMove = xMove; tWipeYMove = yMove; tWipeXDist = endX - startX; if (tWipeXDist < 0) { // If end was less than start, reverse direction tWipeXDist = -tWipeXDist; tWipeXMove = -xMove; } tWipeYDist = endY - startY; if (tWipeYDist < 0) { // If end was less than start, reverse direction tWipeYDist = -tWipeYDist; tWipeYMove = -yMove; } tWipeTemp = 0; } static bool8 UpdateBlackWipe(s16 *data, bool8 xExact, bool8 yExact) { u8 numFinished; if (tWipeXDist > tWipeYDist) { // X has further to move, move it first tWipeCurrX += tWipeXMove; // If it has been far enough since Y's // last move then move it too tWipeTemp += tWipeYDist; if (tWipeTemp > tWipeXDist) { tWipeCurrY += tWipeYMove; tWipeTemp -= tWipeXDist; } } else { // Y has further to move, move it first tWipeCurrY += tWipeYMove; // If it has been far enough since X's // last move then move it too tWipeTemp += tWipeXDist; if (tWipeTemp > tWipeYDist) { tWipeCurrX += tWipeXMove; tWipeTemp -= tWipeYDist; } } numFinished = 0; // Has X coord reached end? if ((tWipeXMove > 0 && tWipeCurrX >= tWipeEndX) || (tWipeXMove < 0 && tWipeCurrX <= tWipeEndX)) { numFinished++; if (xExact) tWipeCurrX = tWipeEndX; } // Has Y coord reached end? if ((tWipeYMove > 0 && tWipeCurrY >= tWipeEndY) || (tWipeYMove < 0 && tWipeCurrY <= tWipeEndY)) { numFinished++; if (yExact) tWipeCurrY = tWipeEndY; } // Return TRUE if both coords have reached end if (numFinished == 2) return TRUE; else return FALSE; } //----------------------------------- // B_TRANSITION_FRONTIER_LOGO_WIGGLE //----------------------------------- #define tSinIndex data[4] #define tAmplitude data[5] static bool8 FrontierLogoWiggle_Init(struct Task *task) { u16 *tilemap, *tileset; InitPatternWeaveTransition(task); GetBg0TilesDst(&tilemap, &tileset); CpuFill16(0, tilemap, BG_SCREEN_SIZE); LZ77UnCompVram(sFrontierLogo_Tileset, tileset); LoadPalette(sFrontierLogo_Palette, 0xF0, sizeof(sFrontierLogo_Palette)); task->tState++; return FALSE; } static bool8 FrontierLogoWiggle_SetGfx(struct Task *task) { u16 *tilemap, *tileset; GetBg0TilesDst(&tilemap, &tileset); LZ77UnCompVram(sFrontierLogo_Tilemap, tilemap); SetSinWave(gScanlineEffectRegBuffers[0], 0, task->tSinIndex, 132, task->tAmplitude, DISPLAY_HEIGHT); task->tState++; return TRUE; } static void Task_FrontierLogoWiggle(u8 taskId) { while (sFrontierLogoWiggle_Funcs[gTasks[taskId].tState](&gTasks[taskId])); } #undef tSinIndex #undef tAmplitude //--------------------------------- // B_TRANSITION_FRONTIER_LOGO_WAVE //--------------------------------- #define tSinVal data[1] #define tAmplitudeVal data[2] #define tTimer data[3] #define tStartedFade data[4] #define tBlendTarget2 data[5] #define tBlendTarget1 data[6] #define tSinDecrement data[7] static void Task_FrontierLogoWave(u8 taskId) { while (sFrontierLogoWave_Funcs[gTasks[taskId].tState](&gTasks[taskId])); } static bool8 FrontierLogoWave_Init(struct Task *task) { u16 *tilemap, *tileset; InitTransitionData(); ScanlineEffect_Clear(); ClearGpuRegBits(REG_OFFSET_DISPCNT, DISPCNT_WIN0_ON | DISPCNT_WIN1_ON); task->tAmplitudeVal = 32 << 8; task->tSinVal = 0x7FFF; task->tBlendTarget2 = 0; task->tBlendTarget1 = 16; task->tSinDecrement = 2560; sTransitionData->BLDCNT = BLDCNT_TGT1_BG0 | BLDCNT_EFFECT_BLEND | BLDCNT_TGT2_ALL; sTransitionData->BLDALPHA = BLDALPHA_BLEND(task->tBlendTarget2, task->tBlendTarget1); REG_BLDCNT = sTransitionData->BLDCNT; REG_BLDALPHA = sTransitionData->BLDALPHA; GetBg0TilesDst(&tilemap, &tileset); CpuFill16(0, tilemap, BG_SCREEN_SIZE); LZ77UnCompVram(sFrontierLogo_Tileset, tileset); LoadPalette(sFrontierLogo_Palette, 0xF0, sizeof(sFrontierLogo_Palette)); sTransitionData->cameraY = 0; task->tState++; return FALSE; } static bool8 FrontierLogoWave_SetGfx(struct Task *task) { u16 *tilemap, *tileset; GetBg0TilesDst(&tilemap, &tileset); LZ77UnCompVram(sFrontierLogo_Tilemap, tilemap); task->tState++; return TRUE; } static bool8 FrontierLogoWave_InitScanline(struct Task *task) { u8 i; for (i = 0; i < DISPLAY_HEIGHT; i++) gScanlineEffectRegBuffers[1][i] = sTransitionData->cameraY; SetVBlankCallback(VBlankCB_FrontierLogoWave); SetHBlankCallback(HBlankCB_FrontierLogoWave); EnableInterrupts(INTR_FLAG_HBLANK); task->tState++; return TRUE; } static bool8 FrontierLogoWave_Main(struct Task *task) { u8 i; u16 sinVal, amplitude, sinSpread; sTransitionData->VBlank_DMA = FALSE; amplitude = task->tAmplitudeVal >> 8; sinVal = task->tSinVal; sinSpread = 384; task->tSinVal -= task->tSinDecrement; if (task->tTimer >= 70) { // Decrease amount of logo movement and distortion // until it rests normally in the middle of the screen. if (task->tAmplitudeVal - 384 >= 0) task->tAmplitudeVal -= 384; else task->tAmplitudeVal = 0; } if (task->tTimer >= 0 && task->tTimer % 3 == 0) { // Blend logo into view if (task->tBlendTarget2 < 16) task->tBlendTarget2++; else if (task->tBlendTarget1 > 0) task->tBlendTarget1--; sTransitionData->BLDALPHA = BLDALPHA_BLEND(task->tBlendTarget2, task->tBlendTarget1); } // Move logo up and down and distort it for (i = 0; i < DISPLAY_HEIGHT; i++, sinVal += sinSpread) { s16 index = sinVal / 256; gScanlineEffectRegBuffers[0][i] = sTransitionData->cameraY + Sin(index & 0xff, amplitude); } if (++task->tTimer == 101) { task->tStartedFade++; BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 16, RGB_BLACK); } if (task->tStartedFade && !gPaletteFade.active) DestroyTask(FindTaskIdByFunc(Task_FrontierLogoWave)); task->tSinDecrement -= 17; sTransitionData->VBlank_DMA++; return FALSE; } static void VBlankCB_FrontierLogoWave(void) { VBlankCB_BattleTransition(); REG_BLDCNT = sTransitionData->BLDCNT; REG_BLDALPHA = sTransitionData->BLDALPHA; if (sTransitionData->VBlank_DMA) DmaCopy16(3, gScanlineEffectRegBuffers[0], gScanlineEffectRegBuffers[1], DISPLAY_HEIGHT * 2); } static void HBlankCB_FrontierLogoWave(void) { u16 var = gScanlineEffectRegBuffers[1][REG_VCOUNT]; REG_BG0VOFS = var; } #undef tSinVal #undef tAmplitudeVal #undef tTimer #undef tStartedFade #undef tBlendTarget2 #undef tBlendTarget1 #undef tSinDecrement //---------------------------------------------------------------------- // B_TRANSITION_FRONTIER_SQUARES, B_TRANSITION_FRONTIER_SQUARES_SCROLL, // and B_TRANSITION_FRONTIER_SQUARES_SPIRAL //---------------------------------------------------------------------- #define tPosX data[2] #define tPosY data[3] #define tRowPos data[4] #define tShrinkState data[5] #define tShrinkDelayTimer data[6] #define tShrinkDelay data[7] static void Task_FrontierSquares(u8 taskId) { while (sFrontierSquares_Funcs[gTasks[taskId].tState](&gTasks[taskId])); } static void Task_FrontierSquaresSpiral(u8 taskId) { while (sFrontierSquaresSpiral_Funcs[gTasks[taskId].tState](&gTasks[taskId])); } static void Task_FrontierSquaresScroll(u8 taskId) { while (sFrontierSquaresScroll_Funcs[gTasks[taskId].tState](&gTasks[taskId])); } static bool8 FrontierSquares_Init(struct Task *task) { u16 *tilemap, *tileset; GetBg0TilesDst(&tilemap, &tileset); LZ77UnCompVram(sFrontierSquares_FilledBg_Tileset, tileset); FillBgTilemapBufferRect_Palette0(0, 0, 0, 0, 32, 32); FillBgTilemapBufferRect(0, 1, 0, 0, MARGIN_SIZE, 32, 15); FillBgTilemapBufferRect(0, 1, 30 - MARGIN_SIZE, 0, MARGIN_SIZE, 32, 15); CopyBgTilemapBufferToVram(0); LoadPalette(sFrontierSquares_Palette, 0xF0, sizeof(sFrontierSquares_Palette)); task->tPosX = MARGIN_SIZE; task->tPosY = 0; task->tRowPos = 0; task->tShrinkDelay = 10; task->tState++; return FALSE; } static bool8 FrontierSquares_Draw(struct Task *task) { CopyRectToBgTilemapBufferRect(0, sFrontierSquares_Tilemap, 0, 0, SQUARE_SIZE, SQUARE_SIZE, task->tPosX, task->tPosY, SQUARE_SIZE, SQUARE_SIZE, 15, 0, 0); CopyBgTilemapBufferToVram(0); task->tPosX += SQUARE_SIZE; if (++task->tRowPos == NUM_SQUARES_PER_ROW) { task->tPosX = MARGIN_SIZE; task->tPosY += SQUARE_SIZE; task->tRowPos = 0; if (task->tPosY >= NUM_SQUARES_PER_COL * SQUARE_SIZE) task->tState++; } return FALSE; } static bool8 FrontierSquares_Shrink(struct Task *task) { u8 i; u16 *tilemap, *tileset; GetBg0TilesDst(&tilemap, &tileset); if (task->tShrinkDelayTimer++ >= task->tShrinkDelay) { switch (task->tShrinkState) { case 0: for (i = 250; i < 255; i++) { gPlttBufferUnfaded[i] = RGB_BLACK; gPlttBufferFaded[i] = RGB_BLACK; } break; case 1: BlendPalettes(PALETTES_ALL & ~(1 << 15), 16, RGB_BLACK); LZ77UnCompVram(sFrontierSquares_EmptyBg_Tileset, tileset); break; case 2: LZ77UnCompVram(sFrontierSquares_Shrink1_Tileset, tileset); break; case 3: LZ77UnCompVram(sFrontierSquares_Shrink2_Tileset, tileset); break; default: FillBgTilemapBufferRect_Palette0(0, 1, 0, 0, 32, 32); CopyBgTilemapBufferToVram(0); task->tState++; return FALSE; } task->tShrinkDelayTimer = 0; task->tShrinkState++; } return FALSE; } #undef tPosX #undef tPosY #undef tRowPos #undef tShrinkState #undef tShrinkDelayTimer #undef tShrinkDelay #define tSquareNum data[2] #define tFadeFlag data[3] static bool8 FrontierSquaresSpiral_Init(struct Task *task) { u16 *tilemap, *tileset; GetBg0TilesDst(&tilemap, &tileset); LZ77UnCompVram(sFrontierSquares_FilledBg_Tileset, tileset); FillBgTilemapBufferRect_Palette0(0, 0, 0, 0, 32, 32); FillBgTilemapBufferRect(0, 1, 0, 0, MARGIN_SIZE, 32, 15); FillBgTilemapBufferRect(0, 1, 30 - MARGIN_SIZE, 0, MARGIN_SIZE, 32, 15); CopyBgTilemapBufferToVram(0); LoadPalette(sFrontierSquares_Palette, 0xE0, sizeof(sFrontierSquares_Palette)); LoadPalette(sFrontierSquares_Palette, 0xF0, sizeof(sFrontierSquares_Palette)); BlendPalette(0xE0, 16, 8, RGB_BLACK); task->tSquareNum = NUM_SQUARES - 1; task->tFadeFlag = 0; task->tState++; return FALSE; } static bool8 FrontierSquaresSpiral_Outward(struct Task *task) { u8 pos = sFrontierSquaresSpiral_Positions[task->tSquareNum]; u8 x = pos % NUM_SQUARES_PER_ROW; u8 y = pos / NUM_SQUARES_PER_ROW; CopyRectToBgTilemapBufferRect(0, sFrontierSquares_Tilemap, 0, 0, SQUARE_SIZE, SQUARE_SIZE, SQUARE_SIZE * x + MARGIN_SIZE, SQUARE_SIZE * y, SQUARE_SIZE, SQUARE_SIZE, 15, 0, 0); CopyBgTilemapBufferToVram(0); if (--task->tSquareNum < 0) task->tState++; return FALSE; } // Now that the overworld is completely covered by the squares, // set it to black so it's not revealed when the squares are removed. static bool8 FrontierSquaresSpiral_SetBlack(struct Task *task) { BlendPalette(0xE0, 16, 3, RGB_BLACK); BlendPalettes(PALETTES_ALL & ~(1 << 15 | 1 << 14), 16, RGB_BLACK); task->tSquareNum = 0; task->tFadeFlag = 0; task->tState++; return FALSE; } // Spiral inward erasing the squares static bool8 FrontierSquaresSpiral_Inward(struct Task *task) { // Each square is faded first, then the one that was faded last move is erased. if (task->tFadeFlag ^= 1) { // Shade square CopyRectToBgTilemapBufferRect(0, sFrontierSquares_Tilemap, 0, 0, SQUARE_SIZE, SQUARE_SIZE, SQUARE_SIZE * (sFrontierSquaresSpiral_Positions[task->tSquareNum] % NUM_SQUARES_PER_ROW) + MARGIN_SIZE, SQUARE_SIZE * (sFrontierSquaresSpiral_Positions[task->tSquareNum] / NUM_SQUARES_PER_ROW), SQUARE_SIZE, SQUARE_SIZE, 14, 0, 0); } else { if (task->tSquareNum > 0) { // Erase square FillBgTilemapBufferRect(0, 1, SQUARE_SIZE * (sFrontierSquaresSpiral_Positions[task->tSquareNum - 1] % NUM_SQUARES_PER_ROW) + MARGIN_SIZE, SQUARE_SIZE * (sFrontierSquaresSpiral_Positions[task->tSquareNum - 1] / NUM_SQUARES_PER_ROW), SQUARE_SIZE, SQUARE_SIZE, 15); } task->tSquareNum++; } if (task->tSquareNum >= NUM_SQUARES) task->tState++; CopyBgTilemapBufferToVram(0); return FALSE; } static bool8 FrontierSquares_End(struct Task *task) { FillBgTilemapBufferRect_Palette0(0, 1, 0, 0, 32, 32); CopyBgTilemapBufferToVram(0); BlendPalettes(PALETTES_ALL, 16, RGB_BLACK); DestroyTask(FindTaskIdByFunc(task->func)); return FALSE; } #undef tSquareNum #undef tFadeFlag #define tScrollXDir data[0] #define tScrollYDir data[1] #define tScrollUpdateFlag data[2] #define tSquareNum data[2] static void Task_ScrollBg(u8 taskId) { if (!(gTasks[taskId].tScrollUpdateFlag ^= 1)) { SetGpuReg(REG_OFFSET_BG0VOFS, gBattle_BG0_X); SetGpuReg(REG_OFFSET_BG0HOFS, gBattle_BG0_Y); gBattle_BG0_X += gTasks[taskId].tScrollXDir; gBattle_BG0_Y += gTasks[taskId].tScrollYDir; } } static bool8 FrontierSquaresScroll_Init(struct Task *task) { u8 taskId = 0; u16 *tilemap, *tileset; GetBg0TilesDst(&tilemap, &tileset); LZ77UnCompVram(sFrontierSquares_FilledBg_Tileset, tileset); FillBgTilemapBufferRect_Palette0(0, 0, 0, 0, 32, 32); CopyBgTilemapBufferToVram(0); LoadPalette(sFrontierSquares_Palette, 0xF0, sizeof(sFrontierSquares_Palette)); gBattle_BG0_X = 0; gBattle_BG0_Y = 0; SetGpuReg(REG_OFFSET_BG0VOFS, gBattle_BG0_X); SetGpuReg(REG_OFFSET_BG0HOFS, gBattle_BG0_Y); task->tSquareNum = 0; // Start scrolling bg in a random direction. taskId = CreateTask(Task_ScrollBg, 1); switch (Random() % 4) { case 0: // Down/right gTasks[taskId].tScrollXDir = 1; gTasks[taskId].tScrollYDir = 1; break; case 1: // Up/left gTasks[taskId].tScrollXDir = -1; gTasks[taskId].tScrollYDir = -1; break; case 2: // Up/right gTasks[taskId].tScrollXDir = 1; gTasks[taskId].tScrollYDir = -1; break; default: // Down/left gTasks[taskId].tScrollXDir = -1; gTasks[taskId].tScrollYDir = 1; break; } task->tState++; return FALSE; } static bool8 FrontierSquaresScroll_Draw(struct Task *task) { u8 pos = sFrontierSquaresScroll_Positions[task->tSquareNum]; u8 x = pos / (NUM_SQUARES_PER_ROW + 1); // +1 because during scroll an additional column covers the margin. u8 y = pos % (NUM_SQUARES_PER_ROW + 1); CopyRectToBgTilemapBufferRect(0, &sFrontierSquares_Tilemap, 0, 0, SQUARE_SIZE, SQUARE_SIZE, SQUARE_SIZE * x + MARGIN_SIZE, SQUARE_SIZE * y, SQUARE_SIZE, SQUARE_SIZE, 15, 0, 0); CopyBgTilemapBufferToVram(0); if (++task->tSquareNum >= (int)ARRAY_COUNT(sFrontierSquaresScroll_Positions)) task->tState++; return 0; } // Now that the overworld is completely covered by the squares, // set it to black so it's not revealed when the squares are removed. static bool8 FrontierSquaresScroll_SetBlack(struct Task *task) { BlendPalettes(PALETTES_ALL & ~(1 << 15), 16, RGB_BLACK); task->tSquareNum = 0; task->tState++; return FALSE; } static bool8 FrontierSquaresScroll_Erase(struct Task *task) { u8 pos = sFrontierSquaresScroll_Positions[task->tSquareNum]; u8 x = pos / (NUM_SQUARES_PER_ROW + 1); u8 y = pos % (NUM_SQUARES_PER_ROW + 1); FillBgTilemapBufferRect(0, 1, SQUARE_SIZE * x + MARGIN_SIZE, SQUARE_SIZE * y, SQUARE_SIZE, SQUARE_SIZE, 15); CopyBgTilemapBufferToVram(0); if (++task->tSquareNum >= (int)ARRAY_COUNT(sFrontierSquaresScroll_Positions)) { DestroyTask(FindTaskIdByFunc(Task_ScrollBg)); task->tState++; } return FALSE; } static bool8 FrontierSquaresScroll_End(struct Task *task) { gBattle_BG0_X = 0; gBattle_BG0_Y = 0; SetGpuReg(REG_OFFSET_BG0VOFS, 0); SetGpuReg(REG_OFFSET_BG0HOFS, gBattle_BG0_Y); FillBgTilemapBufferRect_Palette0(0, 1, 0, 0, 32, 32); CopyBgTilemapBufferToVram(0); BlendPalettes(PALETTES_ALL, 16, RGB_BLACK); DestroyTask(FindTaskIdByFunc(task->func)); #ifndef UBFIX task->tState++; // UB: changing value of a destroyed task #endif return FALSE; } #undef tScrollXDir #undef tScrollYDir #undef tScrollUpdateFlag #undef tSquareNum