tumbledemerald-legacy/src/rayquaza_scene.c

3190 lines
86 KiB
C

#include "global.h"
#include "rayquaza_scene.h"
#include "sprite.h"
#include "task.h"
#include "graphics.h"
#include "bg.h"
#include "main.h"
#include "malloc.h"
#include "palette.h"
#include "scanline_effect.h"
#include "menu.h"
#include "menu_helpers.h"
#include "gpu_regs.h"
#include "decompress.h"
#include "sound.h"
#include "constants/songs.h"
#include "constants/rgb.h"
#include "random.h"
/*
This file handles the cutscene showing Rayquaza arriving to settle the Groudon/Kyogre fight
It consists of 5 separate scenes:
- Groudon and Kyogre facing one another in a thunderstorm (RAY_ANIM_DUO_FIGHT)
- Over-the-shoulder of Rayquaza flying (RAY_ANIM_TAKES_FLIGHT)
- Rayquaza emerging from a spotlight down through the clouds (RAY_ANIM_DESCENDS)
- A close-up of Rayquaza flying down (RAY_ANIM_CHARGES)
- Rayquaza floating above Groudon/Kyogre as they back away offscreen (RAY_ANIM_CHASES_AWAY)
A shortened version of the first scene is used when the player first arrives
in Sootopolis during the Groudon/Kyogre conflict, before awakening Rayquaza (RAY_ANIM_DUO_FIGHT_PRE)
This is indicated with the first two arguments to DoRayquazaScene
*/
enum
{
RAY_ANIM_DUO_FIGHT_PRE,
RAY_ANIM_DUO_FIGHT,
RAY_ANIM_TAKES_FLIGHT,
RAY_ANIM_DESCENDS,
RAY_ANIM_CHARGES,
RAY_ANIM_CHASES_AWAY,
RAY_ANIM_END
};
#define TAG_DUOFIGHT_GROUDON 30505
#define TAG_DUOFIGHT_GROUDON_SHOULDER 30506
#define TAG_DUOFIGHT_GROUDON_CLAW 30507
#define TAG_DUOFIGHT_KYOGRE 30508
#define TAG_DUOFIGHT_KYOGRE_PECTORAL_FIN 30509
#define TAG_DUOFIGHT_KYOGRE_DORSAL_FIN 30510
#define TAG_FLIGHT_SMOKE 30555
#define TAG_DESCENDS_RAYQUAZA 30556
#define TAG_DESCENDS_RAYQUAZA_TAIL 30557
#define TAG_CHASE_GROUDON 30565
#define TAG_CHASE_GROUDON_TAIL 30566
#define TAG_CHASE_KYOGRE 30568
#define TAG_CHASE_RAYQUAZA 30569
#define TAG_CHASE_RAYQUAZA_TAIL 30570
#define TAG_CHASE_SPLASH 30571
#define MAX_SMOKE 10
struct RayquazaScene
{
MainCallback exitCallback;
u8 tilemapBuffers[4][BG_SCREEN_SIZE];
u16 unk; // never read
u8 animId;
bool8 endEarly;
s16 revealedLightLine;
s16 revealedLightTimer;
u8 unused[12];
};
static EWRAM_DATA struct RayquazaScene *sRayScene = NULL;
static void CB2_InitRayquazaScene(void);
static void CB2_RayquazaScene(void);
static void Task_EndAfterFadeScreen(u8);
// RAY_ANIM_DUO_FIGHT_PRE / RAY_ANIM_DUO_FIGHT
static void Task_DuoFightAnim(u8);
static void Task_HandleDuoFight(u8);
static void Task_DuoFightEnd(u8);
static void DuoFightEnd(u8, s8);
static void Task_DuoFight_AnimateClouds(u8);
static void DuoFight_PanOffScene(u8);
static void DuoFight_AnimateRain(void);
static void DuoFight_Lightning1(void);
static void DuoFight_Lightning2(void);
static void DuoFight_LightningLong(void);
static u8 DuoFightPre_CreateGroudonSprites(void);
static u8 DuoFightPre_CreateKyogreSprites(void);
static u8 DuoFight_CreateGroudonSprites(void);
static u8 DuoFight_CreateKyogreSprites(void);
static void SpriteCB_DuoFightPre_Groudon(struct Sprite *);
static void SpriteCB_DuoFightPre_Kyogre(struct Sprite *);
static void SpriteCB_DuoFight_Groudon(struct Sprite *);
static void SpriteCB_DuoFight_Kyogre(struct Sprite *);
static void DuoFight_SlideGroudonDown(struct Sprite *);
static void DuoFight_SlideKyogreDown(struct Sprite *);
// RAY_ANIM_TAKES_FLIGHT
static void Task_RayTakesFlightAnim(u8);
static void Task_HandleRayTakesFlight(u8);
static void Task_RayTakesFlightEnd(u8);
static void Task_TakesFlight_CreateSmoke(u8);
static void SpriteCB_TakesFlight_Smoke(struct Sprite *);
// RAY_ANIM_DESCENDS
static void Task_RayDescendsAnim(u8);
static void Task_HandleRayDescends(u8);
static void Task_RayDescendsEnd(u8);
static u8 CreateDescendsRayquazaSprite(void);
static void SpriteCB_Descends_Rayquaza(struct Sprite *);
// RAY_ANIM_CHARGES
static void Task_RayChargesAnim(u8);
static void Task_HandleRayCharges(u8);
static void Task_RayChargesEnd(u8);
static void Task_RayCharges_ShakeRayquaza(u8);
static void Task_RayCharges_FlyOffscreen(u8);
static void RayCharges_AnimateBg(void);
// RAY_ANIM_CHASES_AWAY
static void Task_RayChasesAwayAnim(u8);
static void Task_HandleRayChasesAway(u8);
static void Task_RayChasesAwayEnd(u8);
static void Task_ChasesAway_AnimateBg(u8);
static void ChasesAway_KyogreStartLeave(u8);
static void ChasesAway_GroudonStartLeave(u8);
static void ChasesAway_CreateTrioSprites(u8);
static void Task_ChasesAway_AnimateRing(u8);
static void SpriteCB_ChasesAway_GroudonLeave(struct Sprite *);
static void SpriteCB_ChasesAway_KyogreLeave(struct Sprite *);
static void SpriteCB_ChasesAway_RayquazaFloat(struct Sprite *);
static void SpriteCB_ChasesAway_Rayquaza(struct Sprite *);
static void SpriteCB_ChasesAway_DuoRingPush(struct Sprite *);
static void ChasesAway_SetRayquazaAnim(struct Sprite *, u8, s16, s16);
static const TaskFunc sTasksForAnimations[] =
{
[RAY_ANIM_DUO_FIGHT_PRE] = Task_DuoFightAnim,
[RAY_ANIM_DUO_FIGHT] = Task_DuoFightAnim,
[RAY_ANIM_TAKES_FLIGHT] = Task_RayTakesFlightAnim,
[RAY_ANIM_DESCENDS] = Task_RayDescendsAnim,
[RAY_ANIM_CHARGES] = Task_RayChargesAnim,
[RAY_ANIM_CHASES_AWAY] = Task_RayChasesAwayAnim,
[RAY_ANIM_END] = Task_EndAfterFadeScreen,
};
static const struct OamData sOam_64x64 =
{
.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 = 2,
.paletteNum = 0,
.affineParam = 0
};
static const struct OamData sOam_32x32 =
{
.y = 0,
.affineMode = ST_OAM_AFFINE_OFF,
.objMode = ST_OAM_OBJ_NORMAL,
.mosaic = 0,
.bpp = ST_OAM_4BPP,
.shape = SPRITE_SHAPE(32x32),
.x = 0,
.matrixNum = 0,
.size = SPRITE_SIZE(32x32),
.tileNum = 0,
.priority = 2,
.paletteNum = 0,
.affineParam = 0
};
static const struct OamData sOam_64x32 =
{
.y = 0,
.affineMode = ST_OAM_AFFINE_OFF,
.objMode = ST_OAM_OBJ_NORMAL,
.mosaic = 0,
.bpp = ST_OAM_4BPP,
.shape = SPRITE_SHAPE(64x32),
.x = 0,
.matrixNum = 0,
.size = SPRITE_SIZE(64x32),
.tileNum = 0,
.priority = 2,
.paletteNum = 0,
.affineParam = 0
};
static const struct OamData sOam_32x16 =
{
.y = 0,
.affineMode = ST_OAM_AFFINE_OFF,
.objMode = ST_OAM_OBJ_NORMAL,
.mosaic = 0,
.bpp = ST_OAM_4BPP,
.shape = SPRITE_SHAPE(32x16),
.x = 0,
.matrixNum = 0,
.size = SPRITE_SIZE(32x16),
.tileNum = 0,
.priority = 2,
.paletteNum = 0,
.affineParam = 0
};
static const struct OamData sOam_16x8 =
{
.y = 0,
.affineMode = ST_OAM_AFFINE_OFF,
.objMode = ST_OAM_OBJ_NORMAL,
.mosaic = 0,
.bpp = ST_OAM_4BPP,
.shape = SPRITE_SHAPE(16x8),
.x = 0,
.matrixNum = 0,
.size = SPRITE_SIZE(16x8),
.tileNum = 0,
.priority = 2,
.paletteNum = 0,
.affineParam = 0
};
static const struct OamData sOam_16x32 =
{
.y = 0,
.affineMode = ST_OAM_AFFINE_OFF,
.objMode = ST_OAM_OBJ_NORMAL,
.mosaic = 0,
.bpp = ST_OAM_4BPP,
.shape = SPRITE_SHAPE(16x32),
.x = 0,
.matrixNum = 0,
.size = SPRITE_SIZE(16x32),
.tileNum = 0,
.priority = 2,
.paletteNum = 0,
.affineParam = 0
};
static const struct OamData sOam_16x16 =
{
.y = 0,
.affineMode = ST_OAM_AFFINE_OFF,
.objMode = ST_OAM_OBJ_NORMAL,
.mosaic = 0,
.bpp = ST_OAM_4BPP,
.shape = SPRITE_SHAPE(16x16),
.x = 0,
.matrixNum = 0,
.size = SPRITE_SIZE(16x16),
.tileNum = 0,
.priority = 2,
.paletteNum = 0,
.affineParam = 0
};
static const struct OamData sOam_32x8 =
{
.y = 0,
.affineMode = ST_OAM_AFFINE_OFF,
.objMode = ST_OAM_OBJ_NORMAL,
.mosaic = 0,
.bpp = ST_OAM_4BPP,
.shape = SPRITE_SHAPE(32x8),
.x = 0,
.matrixNum = 0,
.size = SPRITE_SIZE(32x8),
.tileNum = 0,
.priority = 2,
.paletteNum = 0,
.affineParam = 0
};
static const union AnimCmd sAnim_DuoFightPre_Groudon_Head[] =
{
ANIMCMD_FRAME(0, 30),
ANIMCMD_FRAME(64, 30),
ANIMCMD_FRAME(128, 30),
ANIMCMD_FRAME(64, 30),
ANIMCMD_JUMP(0),
};
static const union AnimCmd sAnim_DuoFightPre_Groudon_Body[] =
{
ANIMCMD_FRAME(192, 30),
ANIMCMD_FRAME(256, 30),
ANIMCMD_FRAME(320, 30),
ANIMCMD_FRAME(256, 30),
ANIMCMD_JUMP(0),
};
static const union AnimCmd *const sAnims_DuoFightPre_Groudon[] =
{
sAnim_DuoFightPre_Groudon_Head,
sAnim_DuoFightPre_Groudon_Body
};
static const struct SpriteTemplate sSpriteTemplate_DuoFightPre_Groudon =
{
.tileTag = TAG_DUOFIGHT_GROUDON,
.paletteTag = TAG_DUOFIGHT_GROUDON,
.oam = &sOam_64x64,
.anims = sAnims_DuoFightPre_Groudon,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCallbackDummy,
};
static const union AnimCmd sAnim_DuoFightPre_GroudonShoulderKyogreDorsalFin[] =
{
ANIMCMD_FRAME(0, 1),
ANIMCMD_END
};
static const union AnimCmd *const sAnims_DuoFightPre_GroudonShoulderKyogreDorsalFin[] =
{
sAnim_DuoFightPre_GroudonShoulderKyogreDorsalFin
};
static const struct SpriteTemplate sSpriteTemplate_DuoFightPre_GroudonShoulder =
{
.tileTag = TAG_DUOFIGHT_GROUDON_SHOULDER,
.paletteTag = TAG_DUOFIGHT_GROUDON,
.oam = &sOam_32x32,
.anims = sAnims_DuoFightPre_GroudonShoulderKyogreDorsalFin,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCallbackDummy,
};
static const union AnimCmd sAnim_DuoFightPre_GroudonClaw[] =
{
ANIMCMD_FRAME(0, 1),
ANIMCMD_END
};
static const union AnimCmd *const sAnims_DuoFightPre_GroudonClaw[] =
{
sAnim_DuoFightPre_GroudonClaw
};
static const struct SpriteTemplate sSpriteTemplate_DuoFightPre_GroudonClaw =
{
.tileTag = TAG_DUOFIGHT_GROUDON_CLAW,
.paletteTag = TAG_DUOFIGHT_GROUDON,
.oam = &sOam_64x32,
.anims = sAnims_DuoFightPre_GroudonClaw,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCallbackDummy,
};
static const union AnimCmd sAnim_DuoFightPre_Kyogre_TopLeft[] =
{
ANIMCMD_FRAME(0, 1),
ANIMCMD_END
};
static const union AnimCmd sAnim_DuoFightPre_Kyogre_TopRight[] =
{
ANIMCMD_FRAME(8, 1),
ANIMCMD_END
};
static const union AnimCmd sAnim_DuoFightPre_Kyogre_FaceLeft[] =
{
ANIMCMD_FRAME(16, 1),
ANIMCMD_END
};
static const union AnimCmd sAnim_DuoFightPre_Kyogre_FaceRight[] =
{
ANIMCMD_FRAME(24, 1),
ANIMCMD_END
};
static const union AnimCmd sAnim_DuoFightPre_Kyogre_ChinLeft[] =
{
ANIMCMD_FRAME(32, 1),
ANIMCMD_END
};
static const union AnimCmd sAnim_DuoFightPre_Kyogre_ChinRight[] =
{
ANIMCMD_FRAME(40, 1),
ANIMCMD_END
};
static const union AnimCmd sAnim_DuoFightPre_Kyogre_LeftPectoralFin[] =
{
ANIMCMD_FRAME(48, 36),
ANIMCMD_FRAME(64, 36),
ANIMCMD_FRAME(80, 36),
ANIMCMD_FRAME(64, 36),
ANIMCMD_JUMP(0),
};
static const union AnimCmd sAnim_DuoFightPre_Kyogre_LeftShoulder[] =
{
ANIMCMD_FRAME(56, 36),
ANIMCMD_FRAME(72, 36),
ANIMCMD_FRAME(88, 36),
ANIMCMD_FRAME(72, 36),
ANIMCMD_JUMP(0),
};
static const union AnimCmd sAnim_DuoFightPre_Kyogre_RightShoulder[] =
{
ANIMCMD_FRAME(96, 36),
ANIMCMD_FRAME(104, 36),
ANIMCMD_FRAME(112, 36),
ANIMCMD_FRAME(104, 36),
ANIMCMD_JUMP(0),
};
// Edge of the right pectoral fin is its own sprite (see KyogrePectoralFin)
static const union AnimCmd *const sAnims_DuoFightPre_Kyogre[] =
{
sAnim_DuoFightPre_Kyogre_TopLeft,
sAnim_DuoFightPre_Kyogre_TopRight,
sAnim_DuoFightPre_Kyogre_FaceLeft,
sAnim_DuoFightPre_Kyogre_FaceRight,
sAnim_DuoFightPre_Kyogre_ChinLeft,
sAnim_DuoFightPre_Kyogre_ChinRight,
sAnim_DuoFightPre_Kyogre_LeftPectoralFin,
sAnim_DuoFightPre_Kyogre_LeftShoulder,
sAnim_DuoFightPre_Kyogre_RightShoulder
};
static const struct SpriteTemplate sSpriteTemplate_DuoFightPre_Kyogre =
{
.tileTag = TAG_DUOFIGHT_KYOGRE,
.paletteTag = TAG_DUOFIGHT_KYOGRE,
.oam = &sOam_32x16,
.anims = sAnims_DuoFightPre_Kyogre,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCallbackDummy,
};
static const union AnimCmd sAnim_DuoFightPre_KyogrePectoralFin[] =
{
ANIMCMD_FRAME(0, 36),
ANIMCMD_FRAME(2, 36),
ANIMCMD_FRAME(4, 36),
ANIMCMD_FRAME(2, 36),
ANIMCMD_JUMP(0),
};
static const union AnimCmd *const sAnims_DuoFightPre_KyogrePectoralFin[] =
{
sAnim_DuoFightPre_KyogrePectoralFin
};
static const struct SpriteTemplate sSpriteTemplate_DuoFightPre_KyogrePectoralFin =
{
.tileTag = TAG_DUOFIGHT_KYOGRE_PECTORAL_FIN,
.paletteTag = TAG_DUOFIGHT_KYOGRE,
.oam = &sOam_16x8,
.anims = sAnims_DuoFightPre_KyogrePectoralFin,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCallbackDummy,
};
static const struct SpriteTemplate sSpriteTemplate_DuoFightPre_KyogreDorsalFin =
{
.tileTag = TAG_DUOFIGHT_KYOGRE_DORSAL_FIN,
.paletteTag = TAG_DUOFIGHT_KYOGRE,
.oam = &sOam_32x32,
.anims = sAnims_DuoFightPre_GroudonShoulderKyogreDorsalFin,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCallbackDummy,
};
static const struct ScanlineEffectParams sScanlineParams_DuoFight_Clouds =
{
.dmaDest = &REG_BG1HOFS,
.dmaControl = SCANLINE_EFFECT_DMACNT_16BIT,
.initState = 1
};
static const struct BgTemplate sBgTemplates_DuoFight[] =
{
{
.bg = 0,
.charBaseIndex = 0,
.mapBaseIndex = 31,
.screenSize = 0,
.paletteMode = 0,
.priority = 0,
.baseTile = 0
},
{
.bg = 1,
.charBaseIndex = 0,
.mapBaseIndex = 30,
.screenSize = 0,
.paletteMode = 0,
.priority = 2,
.baseTile = 0
},
{
.bg = 2,
.charBaseIndex = 0,
.mapBaseIndex = 29,
.screenSize = 0,
.paletteMode = 0,
.priority = 1,
.baseTile = 0
},
};
static const union AnimCmd sAnim_DuoFight_Groudon_Head[] =
{
ANIMCMD_FRAME(0, 20),
ANIMCMD_FRAME(64, 20),
ANIMCMD_FRAME(128, 20),
ANIMCMD_FRAME(64, 20),
ANIMCMD_JUMP(0),
};
static const union AnimCmd sAnim_DuoFight_Groudon_Body[] =
{
ANIMCMD_FRAME(192, 20),
ANIMCMD_FRAME(256, 20),
ANIMCMD_FRAME(320, 20),
ANIMCMD_FRAME(256, 20),
ANIMCMD_JUMP(0),
};
static const union AnimCmd *const sAnims_DuoFight_Groudon[] =
{
sAnim_DuoFight_Groudon_Head,
sAnim_DuoFight_Groudon_Body
};
static const struct CompressedSpriteSheet sSpriteSheet_DuoFight_Groudon =
{
gRaySceneDuoFight_Groudon_Gfx, 0x3000, TAG_DUOFIGHT_GROUDON
};
static const struct CompressedSpritePalette sSpritePal_DuoFight_Groudon =
{
gRaySceneDuoFight_Groudon_Pal, TAG_DUOFIGHT_GROUDON
};
static const struct SpriteTemplate sSpriteTemplate_DuoFight_Groudon =
{
.tileTag = TAG_DUOFIGHT_GROUDON,
.paletteTag = TAG_DUOFIGHT_GROUDON,
.oam = &sOam_64x64,
.anims = sAnims_DuoFight_Groudon,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCallbackDummy,
};
static const union AnimCmd sAnim_DuoFight_GroudonShoulderKyogreDorsalFin[] =
{
ANIMCMD_FRAME(0, 1),
ANIMCMD_END
};
static const union AnimCmd *const sAnims_DuoFight_GroudonShoulderKyogreDorsalFin[] =
{
sAnim_DuoFight_GroudonShoulderKyogreDorsalFin
};
static const struct CompressedSpriteSheet sSpriteSheet_DuoFight_GroudonShoulder =
{
gRaySceneDuoFight_GroudonShoulder_Gfx, 0x200, TAG_DUOFIGHT_GROUDON_SHOULDER
};
static const struct SpriteTemplate sSpriteTemplate_DuoFight_GroudonShoulder =
{
.tileTag = TAG_DUOFIGHT_GROUDON_SHOULDER,
.paletteTag = TAG_DUOFIGHT_GROUDON,
.oam = &sOam_32x32,
.anims = sAnims_DuoFight_GroudonShoulderKyogreDorsalFin,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCallbackDummy,
};
static const union AnimCmd sAnim_DuoFight_GroudonClaw[] =
{
ANIMCMD_FRAME(0, 1),
ANIMCMD_END
};
static const union AnimCmd *const sAnims_DuoFight_GroudonClaw[] =
{
sAnim_DuoFight_GroudonClaw
};
static const struct CompressedSpriteSheet sSpriteSheet_DuoFight_GroudonClaw =
{
gRaySceneDuoFight_GroudonClaw_Gfx, 0x400, TAG_DUOFIGHT_GROUDON_CLAW
};
static const struct SpriteTemplate sSpriteTemplate_DuoFight_GroudonClaw =
{
.tileTag = TAG_DUOFIGHT_GROUDON_CLAW,
.paletteTag = TAG_DUOFIGHT_GROUDON,
.oam = &sOam_64x32,
.anims = sAnims_DuoFight_GroudonClaw,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCallbackDummy,
};
static const union AnimCmd sAnim_DuoFight_Kyogre_TopLeft[] =
{
ANIMCMD_FRAME(0, 1),
ANIMCMD_END
};
static const union AnimCmd sAnim_DuoFight_Kyogre_TopRight[] =
{
ANIMCMD_FRAME(8, 1),
ANIMCMD_END
};
static const union AnimCmd sAnim_DuoFight_Kyogre_FaceLeft[] =
{
ANIMCMD_FRAME(16, 1),
ANIMCMD_END
};
static const union AnimCmd sAnim_DuoFight_Kyogre_FaceRight[] =
{
ANIMCMD_FRAME(24, 1),
ANIMCMD_END
};
static const union AnimCmd sAnim_DuoFight_Kyogre_ChinLeft[] =
{
ANIMCMD_FRAME(32, 1),
ANIMCMD_END
};
static const union AnimCmd sAnim_DuoFight_Kyogre_ChinRight[] =
{
ANIMCMD_FRAME(40, 1),
ANIMCMD_END
};
static const union AnimCmd sAnim_DuoFight_Kyogre_LeftPectoralFin[] =
{
ANIMCMD_FRAME(48, 24),
ANIMCMD_FRAME(64, 24),
ANIMCMD_FRAME(80, 24),
ANIMCMD_FRAME(64, 24),
ANIMCMD_JUMP(0),
};
static const union AnimCmd sAnim_DuoFight_Kyogre_LeftShoulder[] =
{
ANIMCMD_FRAME(56, 24),
ANIMCMD_FRAME(72, 24),
ANIMCMD_FRAME(88, 24),
ANIMCMD_FRAME(72, 24),
ANIMCMD_JUMP(0),
};
static const union AnimCmd sAnim_DuoFight_Kyogre_RightShoulder[] =
{
ANIMCMD_FRAME(96, 24),
ANIMCMD_FRAME(104, 24),
ANIMCMD_FRAME(112, 24),
ANIMCMD_FRAME(104, 24),
ANIMCMD_JUMP(0),
};
static const union AnimCmd *const sAnims_DuoFight_Kyogre[] =
{
sAnim_DuoFight_Kyogre_TopLeft,
sAnim_DuoFight_Kyogre_TopRight,
sAnim_DuoFight_Kyogre_FaceLeft,
sAnim_DuoFight_Kyogre_FaceRight,
sAnim_DuoFight_Kyogre_ChinLeft,
sAnim_DuoFight_Kyogre_ChinRight,
sAnim_DuoFight_Kyogre_LeftPectoralFin,
sAnim_DuoFight_Kyogre_LeftShoulder,
sAnim_DuoFight_Kyogre_RightShoulder
};
static const struct CompressedSpriteSheet sSpriteSheet_DuoFight_Kyogre =
{
gRaySceneDuoFight_Kyogre_Gfx, 0xF00, TAG_DUOFIGHT_KYOGRE
};
static const struct CompressedSpritePalette sSpritePal_DuoFight_Kyogre =
{
gRaySceneDuoFight_Kyogre_Pal, TAG_DUOFIGHT_KYOGRE
};
static const struct SpriteTemplate sSpriteTemplate_DuoFight_Kyogre =
{
.tileTag = TAG_DUOFIGHT_KYOGRE,
.paletteTag = TAG_DUOFIGHT_KYOGRE,
.oam = &sOam_32x16,
.anims = sAnims_DuoFight_Kyogre,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCallbackDummy,
};
static const union AnimCmd sAnim_DuoFight_KyogrePectoralFin[] =
{
ANIMCMD_FRAME(0, 24),
ANIMCMD_FRAME(2, 24),
ANIMCMD_FRAME(4, 24),
ANIMCMD_FRAME(2, 24),
ANIMCMD_JUMP(0),
};
static const union AnimCmd *const sAnims_DuoFight_KyogrePectoralFin[] =
{
sAnim_DuoFight_KyogrePectoralFin
};
static const struct CompressedSpriteSheet sSpriteSheet_DuoFight_KyogrePectoralFin =
{
gRaySceneDuoFight_KyogrePectoralFin_Gfx, 0xC0, TAG_DUOFIGHT_KYOGRE_PECTORAL_FIN
};
static const struct SpriteTemplate sSpriteTemplate_DuoFight_KyogrePectoralFin =
{
.tileTag = TAG_DUOFIGHT_KYOGRE_PECTORAL_FIN,
.paletteTag = TAG_DUOFIGHT_KYOGRE,
.oam = &sOam_16x8,
.anims = sAnims_DuoFight_KyogrePectoralFin,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCallbackDummy,
};
static const struct CompressedSpriteSheet sSpriteSheet_DuoFight_KyogreDorsalFin =
{
gRaySceneDuoFight_KyogreDorsalFin_Gfx, 0x200, TAG_DUOFIGHT_KYOGRE_DORSAL_FIN
};
static const struct SpriteTemplate sSpriteTemplate_DuoFight_KyogreDorsalFin =
{
.tileTag = TAG_DUOFIGHT_KYOGRE_DORSAL_FIN,
.paletteTag = TAG_DUOFIGHT_KYOGRE,
.oam = &sOam_32x32,
.anims = sAnims_DuoFight_GroudonShoulderKyogreDorsalFin,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCallbackDummy,
};
static const struct BgTemplate sBgTemplates_TakesFlight[] =
{
{
.bg = 0,
.charBaseIndex = 2,
.mapBaseIndex = 31,
.screenSize = 0,
.paletteMode = 0,
.priority = 0,
.baseTile = 0
},
{
.bg = 1,
.charBaseIndex = 0,
.mapBaseIndex = 30,
.screenSize = 0,
.paletteMode = 0,
.priority = 2,
.baseTile = 0
},
{
.bg = 2,
.charBaseIndex = 1,
.mapBaseIndex = 29,
.screenSize = 1,
.paletteMode = 0,
.priority = 1,
.baseTile = 0
}
};
static const union AnimCmd sAnim_TakesFlight_Smoke[] =
{
ANIMCMD_FRAME(0, 1),
ANIMCMD_END
};
static const union AnimCmd *const sAnims_TakesFlight_Smoke[] =
{
sAnim_TakesFlight_Smoke
};
static const union AffineAnimCmd sAffineAnim_TakesFlight_Smoke[] =
{
AFFINEANIMCMD_FRAME(-64, -64, 0, 1),
AFFINEANIMCMD_FRAME(32, 32, 0, 14),
AFFINEANIMCMD_FRAME(256, 256, 0, 0),
AFFINEANIMCMD_JUMP(0)
};
static const union AffineAnimCmd *const sAffineAnims_TakesFlight_Smoke[] =
{
sAffineAnim_TakesFlight_Smoke
};
static const struct CompressedSpriteSheet sSpriteSheet_TakesFlight_Smoke =
{
gRaySceneTakesFlight_Smoke_Gfx, 0x100, TAG_FLIGHT_SMOKE
};
static const struct CompressedSpritePalette sSpritePal_TakesFlight_Smoke =
{
gRaySceneTakesFlight_Smoke_Pal, TAG_FLIGHT_SMOKE
};
static const struct SpriteTemplate sSpriteTemplate_TakesFlight_Smoke =
{
.tileTag = TAG_FLIGHT_SMOKE,
.paletteTag = TAG_FLIGHT_SMOKE,
.oam = &sOam_32x16,
.anims = sAnims_TakesFlight_Smoke,
.images = NULL,
.affineAnims = sAffineAnims_TakesFlight_Smoke,
.callback = SpriteCB_TakesFlight_Smoke,
};
static const s8 sTakesFlight_SmokeCoords[MAX_SMOKE][2] =
{
{-1, 5},
{-3, -4},
{ 5, -3},
{-7, 2},
{-9, -1},
{ 1, -5},
{ 3, 4},
{-5, 3},
{ 7, -2},
{ 9, 1}
};
static const struct BgTemplate sBgTemplates_Descends[] =
{
{
.bg = 0,
.charBaseIndex = 0,
.mapBaseIndex = 31,
.screenSize = 0,
.paletteMode = 0,
.priority = 0,
.baseTile = 0
},
{
.bg = 1,
.charBaseIndex = 1,
.mapBaseIndex = 30,
.screenSize = 0,
.paletteMode = 0,
.priority = 1,
.baseTile = 0
},
{
.bg = 2,
.charBaseIndex = 2,
.mapBaseIndex = 29,
.screenSize = 0,
.paletteMode = 0,
.priority = 2,
.baseTile = 0
},
{
.bg = 3,
.charBaseIndex = 1,
.mapBaseIndex = 28,
.screenSize = 0,
.paletteMode = 0,
.priority = 3,
.baseTile = 0
}
};
static const union AnimCmd sAnim_Descends_Rayquaza[] =
{
ANIMCMD_FRAME(0, 32),
ANIMCMD_FRAME(64, 32),
ANIMCMD_JUMP(0),
};
static const union AnimCmd *const sAnims_Descends_Rayquaza[] =
{
sAnim_Descends_Rayquaza
};
static const union AnimCmd sAnim_Descends_RayquazaTail[] =
{
ANIMCMD_FRAME(0, 32),
ANIMCMD_FRAME(8, 32),
ANIMCMD_JUMP(0),
};
static const union AnimCmd *const sAnims_Descends_RayquazaTail[] =
{
sAnim_Descends_RayquazaTail
};
static const struct CompressedSpriteSheet sSpriteSheet_Descends_Rayquaza =
{
gRaySceneDescends_Rayquaza_Gfx, 0x1000, TAG_DESCENDS_RAYQUAZA
};
static const struct CompressedSpriteSheet sSpriteSheet_Descends_RayquazaTail =
{
gRaySceneDescends_RayquazaTail_Gfx, 0x200, TAG_DESCENDS_RAYQUAZA_TAIL
};
static const struct CompressedSpritePalette sSpritePal_Descends_Rayquaza =
{
gRaySceneTakesFlight_Rayquaza_Pal, TAG_DESCENDS_RAYQUAZA // "Takes flight" palette re-used here
};
static const struct SpriteTemplate sSpriteTemplate_Descends_Rayquaza =
{
.tileTag = TAG_DESCENDS_RAYQUAZA,
.paletteTag = TAG_DESCENDS_RAYQUAZA,
.oam = &sOam_64x64,
.anims = sAnims_Descends_Rayquaza,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCallbackDummy,
};
static const struct SpriteTemplate sSpriteTemplate_Descends_RayquazaTail =
{
.tileTag = TAG_DESCENDS_RAYQUAZA_TAIL,
.paletteTag = TAG_DESCENDS_RAYQUAZA,
.oam = &sOam_16x32,
.anims = sAnims_Descends_RayquazaTail,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCallbackDummy,
};
static const struct BgTemplate sBgTemplates_Charges[] =
{
{
.bg = 0,
.charBaseIndex = 2,
.mapBaseIndex = 31,
.screenSize = 0,
.paletteMode = 0,
.priority = 0,
.baseTile = 0
},
{
.bg = 1,
.charBaseIndex = 1,
.mapBaseIndex = 30,
.screenSize = 0,
.paletteMode = 0,
.priority = 1,
.baseTile = 0
},
{
.bg = 2,
.charBaseIndex = 2,
.mapBaseIndex = 29,
.screenSize = 0,
.paletteMode = 0,
.priority = 2,
.baseTile = 0
},
{
.bg = 3,
.charBaseIndex = 3,
.mapBaseIndex = 28,
.screenSize = 0,
.paletteMode = 0,
.priority = 3,
.baseTile = 0
}
};
static const union AnimCmd sAnim_ChasesAway_Groudon_Still[] =
{
ANIMCMD_FRAME(0, 1),
ANIMCMD_END
};
static const union AnimCmd sAnim_ChasesAway_Groudon_Moving[] =
{
ANIMCMD_FRAME(0, 48),
ANIMCMD_FRAME(64, 32),
ANIMCMD_FRAME(0, 48),
ANIMCMD_FRAME(128, 32),
ANIMCMD_JUMP(0)
};
static const union AnimCmd *const sAnims_ChasesAway_Groudon[] =
{
sAnim_ChasesAway_Groudon_Still,
sAnim_ChasesAway_Groudon_Moving
};
static const union AnimCmd sAnim_ChasesAway_GroudonTail[] =
{
ANIMCMD_FRAME(0, 1),
ANIMCMD_END
};
static const union AnimCmd *const sAnims_ChasesAway_GroudonTail[] =
{
sAnim_ChasesAway_GroudonTail,
};
static const union AnimCmd sAnim_ChasesAway_Kyogre_Front[] =
{
ANIMCMD_FRAME(0, 1),
ANIMCMD_END
};
static const union AnimCmd sAnim_ChasesAway_Kyogre_Back[] =
{
ANIMCMD_FRAME(16, 1),
ANIMCMD_END
};
static const union AnimCmd sAnim_ChasesAway_Kyogre_Tail[] =
{
ANIMCMD_FRAME(32, 1),
ANIMCMD_END
};
static const union AnimCmd *const sAnims_ChasesAway_Kyogre[] =
{
sAnim_ChasesAway_Kyogre_Front,
sAnim_ChasesAway_Kyogre_Back,
sAnim_ChasesAway_Kyogre_Tail
};
static const union AnimCmd sAnim_ChasesAway_Rayquaza_FlyingDown[] =
{
ANIMCMD_FRAME(0, 1),
ANIMCMD_END
};
static const union AnimCmd sAnim_ChasesAway_Rayquaza_Arriving[] =
{
ANIMCMD_FRAME(64, 1),
ANIMCMD_END
};
static const union AnimCmd sAnim_ChasesAway_Rayquaza_Floating[] =
{
ANIMCMD_FRAME(128, 1),
ANIMCMD_END
};
static const union AnimCmd sAnim_ChasesAway_Rayquaza_Shouting[] =
{
ANIMCMD_FRAME(192, 1),
ANIMCMD_END
};
static const union AnimCmd *const sAnims_ChasesAway_Rayquaza[] =
{
sAnim_ChasesAway_Rayquaza_FlyingDown,
sAnim_ChasesAway_Rayquaza_Arriving,
sAnim_ChasesAway_Rayquaza_Floating,
sAnim_ChasesAway_Rayquaza_Shouting
};
static const union AnimCmd sAnim_ChasesAway_RayquazaTail_FlyingDown[] =
{
ANIMCMD_FRAME(0, 1),
ANIMCMD_END
};
static const union AnimCmd sAnim_ChasesAway_RayquazaTail_Arriving[] =
{
ANIMCMD_FRAME(16, 1),
ANIMCMD_END
};
static const union AnimCmd sAnim_ChasesAway_RayquazaTail_Floating[] =
{
ANIMCMD_FRAME(32, 1),
ANIMCMD_END
};
static const union AnimCmd sAnim_ChasesAway_RayquazaTail_Shouting[] =
{
ANIMCMD_FRAME(48, 1),
ANIMCMD_END
};
static const union AnimCmd *const sAnims_ChasesAway_RayquazaTail[] =
{
sAnim_ChasesAway_RayquazaTail_FlyingDown,
sAnim_ChasesAway_RayquazaTail_Arriving,
sAnim_ChasesAway_RayquazaTail_Floating,
sAnim_ChasesAway_RayquazaTail_Shouting
};
static const union AnimCmd sAnim_ChasesAway_KyogreSplash[] =
{
ANIMCMD_FRAME(0, 8),
ANIMCMD_FRAME(4, 8),
ANIMCMD_FRAME(8, 8),
ANIMCMD_FRAME(12, 8),
ANIMCMD_FRAME(16, 8),
ANIMCMD_FRAME(20, 8),
ANIMCMD_JUMP(0)
};
static const union AnimCmd *const sAnims_ChasesAway_KyogreSplash[] =
{
sAnim_ChasesAway_KyogreSplash
};
static const struct CompressedSpriteSheet sSpriteSheet_ChasesAway_Groudon =
{
gRaySceneChasesAway_Groudon_Gfx, 0x1800, TAG_CHASE_GROUDON
};
static const struct CompressedSpriteSheet sSpriteSheet_ChasesAway_GroudonTail =
{
gRaySceneChasesAway_GroudonTail_Gfx, 0x80, TAG_CHASE_GROUDON_TAIL
};
static const struct CompressedSpriteSheet sSpriteSheet_ChasesAway_Kyogre =
{
gRaySceneChasesAway_Kyogre_Gfx, 0x600, TAG_CHASE_KYOGRE
};
static const struct CompressedSpriteSheet sSpriteSheet_ChasesAway_Rayquaza =
{
gRaySceneChasesAway_Rayquaza_Gfx, 0x2000, TAG_CHASE_RAYQUAZA
};
static const struct CompressedSpriteSheet sSpriteSheet_ChasesAway_RayquazaTail =
{
gRaySceneChasesAway_RayquazaTail_Gfx, 0x800, TAG_CHASE_RAYQUAZA_TAIL
};
static const struct CompressedSpriteSheet sSpriteSheet_ChasesAway_KyogreSplash =
{
gRaySceneChasesAway_KyogreSplash_Gfx, 0x300, TAG_CHASE_SPLASH
};
static const struct CompressedSpritePalette sSpritePal_ChasesAway_Groudon =
{
gRaySceneChasesAway_Groudon_Pal, TAG_CHASE_GROUDON
};
static const struct CompressedSpritePalette sSpritePal_ChasesAway_Kyogre =
{
gRaySceneChasesAway_Kyogre_Pal, TAG_CHASE_KYOGRE
};
static const struct CompressedSpritePalette sSpritePal_ChasesAway_Rayquaza =
{
gRaySceneChasesAway_Rayquaza_Pal, TAG_CHASE_RAYQUAZA
};
static const struct CompressedSpritePalette sSpritePal_ChasesAway_KyogreSplash =
{
gRaySceneChasesAway_KyogreSplash_Pal, TAG_CHASE_SPLASH
};
static const struct SpriteTemplate sSpriteTemplate_ChasesAway_Groudon =
{
.tileTag = TAG_CHASE_GROUDON,
.paletteTag = TAG_CHASE_GROUDON,
.oam = &sOam_64x64,
.anims = sAnims_ChasesAway_Groudon,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCallbackDummy,
};
static const struct SpriteTemplate sSpriteTemplate_ChasesAway_GroudonTail =
{
.tileTag = TAG_CHASE_GROUDON_TAIL,
.paletteTag = TAG_CHASE_GROUDON,
.oam = &sOam_16x16,
.anims = sAnims_ChasesAway_GroudonTail,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCallbackDummy,
};
static const struct SpriteTemplate sSpriteTemplate_ChasesAway_Kyogre =
{
.tileTag = TAG_CHASE_KYOGRE,
.paletteTag = TAG_CHASE_KYOGRE,
.oam = &sOam_32x32,
.anims = sAnims_ChasesAway_Kyogre,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCallbackDummy,
};
static const struct SpriteTemplate sSpriteTemplate_ChasesAway_Rayquaza =
{
.tileTag = TAG_CHASE_RAYQUAZA,
.paletteTag = TAG_CHASE_RAYQUAZA,
.oam = &sOam_64x64,
.anims = sAnims_ChasesAway_Rayquaza,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCB_ChasesAway_Rayquaza,
};
static const struct SpriteTemplate sSpriteTemplate_ChasesAway_RayquazaTail =
{
.tileTag = TAG_CHASE_RAYQUAZA_TAIL,
.paletteTag = TAG_CHASE_RAYQUAZA,
.oam = &sOam_32x32,
.anims = sAnims_ChasesAway_RayquazaTail,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCallbackDummy,
};
static const struct SpriteTemplate sSpriteTemplate_ChasesAway_KyogreSplash =
{
.tileTag = TAG_CHASE_SPLASH,
.paletteTag = TAG_CHASE_SPLASH,
.oam = &sOam_32x8,
.anims = sAnims_ChasesAway_KyogreSplash,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCallbackDummy,
};
static const struct BgTemplate sBgTemplates_ChasesAway[] =
{
{
.bg = 0,
.charBaseIndex = 1,
.mapBaseIndex = 31,
.screenSize = 0,
.paletteMode = 0,
.priority = 1,
.baseTile = 0
},
{
.bg = 1,
.charBaseIndex = 1,
.mapBaseIndex = 30,
.screenSize = 0,
.paletteMode = 0,
.priority = 2,
.baseTile = 0
},
{
.bg = 2,
.charBaseIndex = 0,
.mapBaseIndex = 29,
.screenSize = 0,
.paletteMode = 0,
.priority = 0,
.baseTile = 0
}
};
void DoRayquazaScene(u8 animId, bool8 endEarly, void (*exitCallback)(void))
{
sRayScene = AllocZeroed(sizeof(*sRayScene));
sRayScene->animId = animId;
sRayScene->exitCallback = exitCallback;
sRayScene->endEarly = endEarly;
SetMainCallback2(CB2_InitRayquazaScene);
}
static void CB2_InitRayquazaScene(void)
{
SetVBlankHBlankCallbacksToNull();
ClearScheduledBgCopiesToVram();
ScanlineEffect_Stop();
FreeAllSpritePalettes();
ResetPaletteFade();
ResetSpriteData();
ResetTasks();
FillPalette(RGB_BLACK, 0xF0, 32);
CreateTask(sTasksForAnimations[sRayScene->animId], 0);
SetMainCallback2(CB2_RayquazaScene);
}
static void CB2_RayquazaScene(void)
{
RunTasks();
AnimateSprites();
BuildOamBuffer();
DoScheduledBgTilemapCopiesToVram();
UpdatePaletteFade();
}
static void VBlankCB_RayquazaScene(void)
{
LoadOam();
ProcessSpriteCopyRequests();
TransferPlttBuffer();
}
static void Task_EndAfterFadeScreen(u8 taskId)
{
if (!gPaletteFade.active)
{
ResetSpriteData();
FreeAllSpritePalettes();
SetMainCallback2(sRayScene->exitCallback);
Free(sRayScene);
DestroyTask(taskId);
}
}
static void Task_SetNextAnim(u8 taskId)
{
if (!gPaletteFade.active)
{
if (sRayScene->endEarly == TRUE)
{
gTasks[taskId].func = Task_EndAfterFadeScreen;
}
else
{
sRayScene->animId++;
sRayScene->unk = 0;
gTasks[taskId].func = sTasksForAnimations[sRayScene->animId];
}
}
}
// The cutscene window is cropped to a narrower view, with black borders on each vertical edge
// This function is used in scenes where sprites in these borders need to be hidden
static void SetWindowsHideVertBorders(void)
{
SetGpuReg(REG_OFFSET_WININ, WININ_WIN0_ALL);
SetGpuReg(REG_OFFSET_WINOUT, 0);
SetGpuReg(REG_OFFSET_WIN0H, WIN_RANGE(0, DISPLAY_WIDTH));
SetGpuReg(REG_OFFSET_WIN0V, WIN_RANGE(24, DISPLAY_HEIGHT - 24));
gPlttBufferUnfaded[0] = 0;
gPlttBufferFaded[0] = 0;
}
static void ResetWindowDimensions(void)
{
SetGpuReg(REG_OFFSET_WININ, WININ_WIN0_ALL);
SetGpuReg(REG_OFFSET_WINOUT, WINOUT_WIN01_ALL);
}
// RAY_ANIM_DUO_FIGHT / RAY_ANIM_DUO_FIGHT_PRE
#define tTimer data[0]
#define tHelperTaskId data[1]
#define tGroudonSpriteId data[2]
#define tKyogreSpriteId data[3]
#define sGroudonBodySpriteId data[0]
#define sGroudonShoulderSpriteId data[1]
#define sGroudonClawSpriteId data[2]
static void Task_HandleDuoFightPre(u8 taskId)
{
s16 *data = gTasks[taskId].data;
DuoFight_AnimateRain();
if (!gPaletteFade.active)
{
s16 frame = tTimer;
if (frame == 64)
{
DuoFight_Lightning1();
}
else if (frame == 144)
{
DuoFight_Lightning2();
}
else
{
switch (frame)
{
case 328:
DuoFightEnd(taskId, 0);
return;
case 148:
DuoFight_LightningLong();
break;
}
}
tTimer++;
}
}
static u8 DuoFightPre_CreateGroudonSprites(void)
{
u8 spriteId;
s16 *data;
spriteId = CreateSprite(&sSpriteTemplate_DuoFightPre_Groudon, 88, 72, 3);
gSprites[spriteId].callback = SpriteCB_DuoFightPre_Groudon;
data = gSprites[spriteId].data;
sGroudonBodySpriteId = CreateSprite(&sSpriteTemplate_DuoFightPre_Groudon, 56, 104, 3);
sGroudonShoulderSpriteId = CreateSprite(&sSpriteTemplate_DuoFightPre_GroudonShoulder, 75, 101, 0);
sGroudonClawSpriteId = CreateSprite(&sSpriteTemplate_DuoFightPre_GroudonClaw, 109, 114, 1);
StartSpriteAnim(&gSprites[sGroudonBodySpriteId], 1);
return spriteId;
}
static void SpriteCB_DuoFightPre_Groudon(struct Sprite *sprite)
{
s16 *data = sprite->data;
data[5]++;
data[5] &= 0x1F;
if (data[5] == 0 && sprite->x != 72)
{
sprite->x--;
gSprites[sprite->sGroudonBodySpriteId].x--;
gSprites[sGroudonShoulderSpriteId].x--;
gSprites[sGroudonClawSpriteId].x--;
}
switch (sprite->animCmdIndex)
{
case 0:
gSprites[sGroudonShoulderSpriteId].x2 = 0;
gSprites[sGroudonShoulderSpriteId].y2 = 0;
gSprites[sGroudonClawSpriteId].x2 = 0;
gSprites[sGroudonClawSpriteId].y2 = 0;
break;
case 1:
case 3:
gSprites[sGroudonShoulderSpriteId].x2 = -1;
gSprites[sGroudonShoulderSpriteId].y2 = 0;
gSprites[sGroudonClawSpriteId].x2 = -1;
gSprites[sGroudonClawSpriteId].y2 = 0;
break;
case 2:
gSprites[sGroudonShoulderSpriteId].x2 = -1;
gSprites[sGroudonShoulderSpriteId].y2 = 1;
gSprites[sGroudonClawSpriteId].x2 = -2;
gSprites[sGroudonClawSpriteId].y2 = 1;
break;
}
}
static u8 DuoFightPre_CreateKyogreSprites(void)
{
u8 spriteId;
s16 *data;
spriteId = CreateSprite(&sSpriteTemplate_DuoFightPre_Kyogre, 136, 96, 1);
gSprites[spriteId].callback = SpriteCB_DuoFightPre_Kyogre;
data = gSprites[spriteId].data;
data[0] = CreateSprite(&sSpriteTemplate_DuoFightPre_Kyogre, 168, 96, 1) << 8;
data[0] |= CreateSprite(&sSpriteTemplate_DuoFightPre_Kyogre, 136, 112, 1);
data[1] = CreateSprite(&sSpriteTemplate_DuoFightPre_Kyogre, 168, 112, 1) << 8;
data[1] |= CreateSprite(&sSpriteTemplate_DuoFightPre_Kyogre, 136, 128, 1);
data[2] = CreateSprite(&sSpriteTemplate_DuoFightPre_Kyogre, 168, 128, 1) << 8;
data[2] |= CreateSprite(&sSpriteTemplate_DuoFightPre_Kyogre, 104, 128, 2);
data[3] = CreateSprite(&sSpriteTemplate_DuoFightPre_Kyogre, 136, 128, 2) << 8;
data[3] |= CreateSprite(&sSpriteTemplate_DuoFightPre_Kyogre, 184, 128, 0);
data[4] = CreateSprite(&sSpriteTemplate_DuoFightPre_KyogrePectoralFin, 208, 132, 0) << 8;
data[4] |= CreateSprite(&sSpriteTemplate_DuoFightPre_KyogreDorsalFin, 200, 120, 1);
StartSpriteAnim(&gSprites[data[0] >> 8], 1);
StartSpriteAnim(&gSprites[data[0] & 0xFF], 2);
StartSpriteAnim(&gSprites[data[1] >> 8], 3);
StartSpriteAnim(&gSprites[data[1] & 0xFF], 4);
StartSpriteAnim(&gSprites[data[2] >> 8], 5);
StartSpriteAnim(&gSprites[data[2] & 0xFF], 6);
StartSpriteAnim(&gSprites[data[3] >> 8], 7);
StartSpriteAnim(&gSprites[data[3] & 0xFF], 8);
return spriteId;
}
static void SpriteCB_DuoFightPre_Kyogre(struct Sprite *sprite)
{
s16 *data = sprite->data;
data[5]++;
data[5] &= 0x1F;
if (data[5] == 0 && sprite->x != 152)
{
sprite->x++;
gSprites[sprite->data[0] >> 8].x++;
gSprites[sprite->data[0] & 0xFF].x++;
gSprites[data[1] >> 8].x++;
gSprites[data[1] & 0xFF].x++;
gSprites[data[2] >> 8].x++;
gSprites[data[2] & 0xFF].x++;
gSprites[data[3] >> 8].x++;
gSprites[data[3] & 0xFF].x++;
gSprites[data[4] >> 8].x++;
gSprites[data[4] & 0xFF].x++;
}
switch (gSprites[data[2] & 0xFF].animCmdIndex)
{
case 0:
sprite->y2 = 0;
gSprites[data[0] >> 8].y2 = 0;
gSprites[data[0] & 0xFF].y2 = 0;
gSprites[data[1] >> 8].y2 = 0;
gSprites[data[1] & 0xFF].y2 = 0;
gSprites[data[2] >> 8].y2 = 0;
gSprites[data[2] & 0xFF].y2 = 0;
gSprites[data[3] >> 8].y2 = 0;
gSprites[data[3] & 0xFF].y2 = 0;
gSprites[data[4] >> 8].y2 = 0;
gSprites[data[4] & 0xFF].y2 = 0;
break;
case 1:
case 3:
sprite->y2 = 1;
gSprites[data[0] >> 8].y2 = 1;
gSprites[data[0] & 0xFF].y2 = 1;
gSprites[data[1] >> 8].y2 = 1;
gSprites[data[1] & 0xFF].y2 = 1;
gSprites[data[2] >> 8].y2 = 1;
gSprites[data[2] & 0xFF].y2 = 1;
gSprites[data[3] >> 8].y2 = 1;
gSprites[data[3] & 0xFF].y2 = 1;
gSprites[data[4] >> 8].y2 = 1;
gSprites[data[4] & 0xFF].y2 = 1;
break;
case 2:
sprite->y2 = 2;
gSprites[data[0] >> 8].y2 = 2;
gSprites[data[0] & 0xFF].y2 = 2;
gSprites[data[1] >> 8].y2 = 2;
gSprites[data[1] & 0xFF].y2 = 2;
gSprites[data[2] >> 8].y2 = 2;
gSprites[data[4] & 0xFF].y2 = 2;
break;
}
}
static void VBlankCB_DuoFight(void)
{
VBlankCB_RayquazaScene();
ScanlineEffect_InitHBlankDmaTransfer();
}
static void InitDuoFightSceneBgs(void)
{
ResetVramOamAndBgCntRegs();
ResetBgsAndClearDma3BusyFlags(0);
InitBgsFromTemplates(0, sBgTemplates_DuoFight, ARRAY_COUNT(sBgTemplates_DuoFight));
SetBgTilemapBuffer(0, sRayScene->tilemapBuffers[0]);
SetBgTilemapBuffer(1, sRayScene->tilemapBuffers[1]);
SetBgTilemapBuffer(2, sRayScene->tilemapBuffers[2]);
ResetAllBgsCoordinates();
ScheduleBgCopyTilemapToVram(0);
ScheduleBgCopyTilemapToVram(1);
ScheduleBgCopyTilemapToVram(2);
SetGpuReg(REG_OFFSET_DISPCNT, DISPCNT_OBJ_ON | DISPCNT_OBJ_1D_MAP);
ShowBg(0);
ShowBg(1);
ShowBg(2);
SetGpuReg(REG_OFFSET_BLDCNT, 0);
}
static void LoadDuoFightSceneGfx(void)
{
ResetTempTileDataBuffers();
DecompressAndCopyTileDataToVram(0, gRaySceneDuoFight_Clouds_Gfx, 0, 0, 0);
while (FreeTempTileDataBuffersIfPossible())
;
LZDecompressWram(gRaySceneDuoFight_Clouds2_Tilemap, sRayScene->tilemapBuffers[0]);
LZDecompressWram(gRaySceneDuoFight_Clouds1_Tilemap, sRayScene->tilemapBuffers[1]);
LZDecompressWram(gRaySceneDuoFight_Clouds3_Tilemap, sRayScene->tilemapBuffers[2]);
LoadCompressedPalette(gRaySceneDuoFight_Clouds_Pal, 0, 0x40);
LoadCompressedSpriteSheet(&sSpriteSheet_DuoFight_Groudon);
LoadCompressedSpriteSheet(&sSpriteSheet_DuoFight_GroudonShoulder);
LoadCompressedSpriteSheet(&sSpriteSheet_DuoFight_GroudonClaw);
LoadCompressedSpriteSheet(&sSpriteSheet_DuoFight_Kyogre);
LoadCompressedSpriteSheet(&sSpriteSheet_DuoFight_KyogrePectoralFin);
LoadCompressedSpriteSheet(&sSpriteSheet_DuoFight_KyogreDorsalFin);
LoadCompressedSpritePalette(&sSpritePal_DuoFight_Groudon);
LoadCompressedSpritePalette(&sSpritePal_DuoFight_Kyogre);
}
static void Task_DuoFightAnim(u8 taskId)
{
s16 *data = gTasks[taskId].data;
ScanlineEffect_Clear();
InitDuoFightSceneBgs();
LoadDuoFightSceneGfx();
CpuFastFill16(0, gScanlineEffectRegBuffers, sizeof(gScanlineEffectRegBuffers));
ScanlineEffect_SetParams(sScanlineParams_DuoFight_Clouds);
tTimer = 0;
tHelperTaskId = CreateTask(Task_DuoFight_AnimateClouds, 0);
if (sRayScene->animId == RAY_ANIM_DUO_FIGHT_PRE)
{
tGroudonSpriteId = DuoFightPre_CreateGroudonSprites();
tKyogreSpriteId = DuoFightPre_CreateKyogreSprites();
gTasks[taskId].func = Task_HandleDuoFightPre;
}
else
{
tGroudonSpriteId = DuoFight_CreateGroudonSprites();
tKyogreSpriteId = DuoFight_CreateKyogreSprites();
gTasks[taskId].func = Task_HandleDuoFight;
StopMapMusic();
}
BlendPalettes(PALETTES_ALL, 0x10, 0);
BeginNormalPaletteFade(PALETTES_ALL, 0, 0x10, 0, RGB_BLACK);
SetVBlankCallback(VBlankCB_DuoFight);
PlaySE(SE_DOWNPOUR);
}
static void Task_DuoFight_AnimateClouds(u8 taskId)
{
s16 i;
u16 *data = gTasks[taskId].data;
for (i = 24; i < 92; i++)
{
if (i <= 47)
{
gScanlineEffectRegBuffers[0][i] = data[0] >> 8;
gScanlineEffectRegBuffers[1][i] = data[0] >> 8;
}
else if (i <= 63)
{
gScanlineEffectRegBuffers[0][i] = data[1] >> 8;
gScanlineEffectRegBuffers[1][i] = data[1] >> 8;
}
else if (i <= 75)
{
gScanlineEffectRegBuffers[0][i] = data[2] >> 8;
gScanlineEffectRegBuffers[1][i] = data[2] >> 8;
}
else if (i <= 83)
{
gScanlineEffectRegBuffers[0][i] = data[3] >> 8;
gScanlineEffectRegBuffers[1][i] = data[3] >> 8;
}
else if (i <= 87)
{
gScanlineEffectRegBuffers[0][i] = data[4] >> 8;
gScanlineEffectRegBuffers[1][i] = data[4] >> 8;
}
else
{
gScanlineEffectRegBuffers[0][i] = data[5] >> 8;
gScanlineEffectRegBuffers[1][i] = data[5] >> 8;
}
}
if (sRayScene->animId == RAY_ANIM_DUO_FIGHT_PRE)
{
data[0] += 448;
data[1] += 384;
data[2] += 320;
data[3] += 256;
data[4] += 192;
data[5] += 128;
}
else
{
data[0] += 768;
data[1] += 640;
data[2] += 512;
data[3] += 384;
data[4] += 256;
data[5] += 128;
}
}
static void Task_HandleDuoFight(u8 taskId)
{
s16 *data = gTasks[taskId].data;
DuoFight_AnimateRain();
if (!gPaletteFade.active)
{
s16 frame = tTimer;
if (frame == 32 || frame == 112)
{
DuoFight_Lightning1();
}
else if (frame == 216)
{
DuoFight_Lightning2();
}
else if (frame == 220)
{
DuoFight_LightningLong();
}
else
{
switch (frame)
{
case 412:
DuoFightEnd(taskId, 2);
return;
case 380:
SetGpuReg(REG_OFFSET_BLDCNT, BLDCNT_TGT1_BG2 | BLDCNT_TGT2_BG1 | BLDCNT_EFFECT_BLEND);
gTasks[tHelperTaskId].func = DuoFight_PanOffScene;
gTasks[tHelperTaskId].data[0] = 0;
gTasks[tHelperTaskId].data[2] = data[2];
gTasks[tHelperTaskId].data[3] = data[3];
ScanlineEffect_Stop();
break;
}
}
tTimer++;
}
}
// In the below functions, BlendPalettesGradually flashes the bg white and the duo black
// and gradually fades them back to original color to simulate lightning
static void DuoFight_Lightning1(void)
{
PlaySE(SE_THUNDER);
BlendPalettesGradually(PALETTES_BG & ~(0x8000), 0, 16, 0, RGB_WHITEALPHA, 0, 0);
BlendPalettesGradually(PALETTES_OBJECTS, 0, 16, 0, RGB_BLACK, 0, 1);
}
static void DuoFight_Lightning2(void)
{
PlaySE(SE_THUNDER);
BlendPalettesGradually(PALETTES_BG & ~(0x8000), 0, 16, 16, RGB_WHITEALPHA, 0, 0);
BlendPalettesGradually(PALETTES_OBJECTS, 0, 16, 16, RGB_BLACK, 0, 1);
}
static void DuoFight_LightningLong(void)
{
BlendPalettesGradually(PALETTES_BG & ~(0x8000), 4, 16, 0, RGB_WHITEALPHA, 0, 0);
BlendPalettesGradually(PALETTES_OBJECTS, 4, 16, 0, RGB_BLACK, 0, 1);
}
static void DuoFight_AnimateRain(void)
{
ChangeBgX(2, 0x400, BG_COORD_ADD);
ChangeBgY(2, 0x800, BG_COORD_SUB);
}
// Only used by the full version, which pans up at the end (so scene objects move down)
// DuoFightPre just fades to black with no pan
static void DuoFight_PanOffScene(u8 taskId)
{
u16 bgY;
s16 *data = gTasks[taskId].data;
DuoFight_SlideGroudonDown(&gSprites[tGroudonSpriteId]);
DuoFight_SlideKyogreDown(&gSprites[tKyogreSpriteId]);
bgY = GetBgY(1);
if (GetBgY(1) == 0 || bgY > 0x8000)
ChangeBgY(1, 0x400, BG_COORD_SUB);
if (tTimer != 16)
{
tTimer++;
SetGpuReg(REG_OFFSET_BLDALPHA, BLDALPHA_BLEND(16 - tTimer, tTimer));
}
}
static void DuoFightEnd(u8 taskId, s8 palDelay)
{
PlaySE(SE_DOWNPOUR_STOP);
BeginNormalPaletteFade(PALETTES_ALL, palDelay, 0, 0x10, RGB_BLACK);
gTasks[taskId].func = Task_DuoFightEnd;
}
static void Task_DuoFightEnd(u8 taskId)
{
s16 *data = gTasks[taskId].data;
DuoFight_AnimateRain();
if (!gPaletteFade.active)
{
DestroyTask(tHelperTaskId);
ChangeBgY(1, 0, BG_COORD_SET);
SetVBlankCallback(NULL);
ScanlineEffect_Stop();
ResetSpriteData();
FreeAllSpritePalettes();
tTimer = 0;
gTasks[taskId].func = Task_SetNextAnim;
}
}
static u8 DuoFight_CreateGroudonSprites(void)
{
u8 spriteId;
s16 *data;
spriteId = CreateSprite(&sSpriteTemplate_DuoFight_Groudon, 98, 72, 3);
gSprites[spriteId].callback = SpriteCB_DuoFight_Groudon;
data = gSprites[spriteId].data;
sGroudonBodySpriteId = CreateSprite(&sSpriteTemplate_DuoFight_Groudon, 66, 104, 3);
sGroudonShoulderSpriteId = CreateSprite(&sSpriteTemplate_DuoFight_GroudonShoulder, 85, 101, 0);
sGroudonClawSpriteId = CreateSprite(&sSpriteTemplate_DuoFight_GroudonClaw, 119, 114, 1);
StartSpriteAnim(&gSprites[sGroudonBodySpriteId], 1);
return spriteId;
}
static void SpriteCB_DuoFight_Groudon(struct Sprite *sprite)
{
s16 *data = sprite->data;
data[5]++;
data[5] &= 0xF;
if (!(data[5] & 7) && sprite->x != 72)
{
sprite->x--;
gSprites[sprite->sGroudonBodySpriteId].x--;
gSprites[sGroudonShoulderSpriteId].x--;
gSprites[sGroudonClawSpriteId].x--;
}
switch (sprite->animCmdIndex)
{
case 0:
gSprites[sGroudonShoulderSpriteId].x2 = 0;
gSprites[sGroudonShoulderSpriteId].y2 = 0;
gSprites[sGroudonClawSpriteId].x2 = 0;
gSprites[sGroudonClawSpriteId].y2 = 0;
break;
case 1:
case 3:
gSprites[sGroudonShoulderSpriteId].x2 = -1;
gSprites[sGroudonShoulderSpriteId].y2 = 0;
gSprites[sGroudonClawSpriteId].x2 = -1;
gSprites[sGroudonClawSpriteId].y2 = 0;
break;
case 2:
gSprites[sGroudonShoulderSpriteId].x2 = -1;
gSprites[sGroudonShoulderSpriteId].y2 = 1;
gSprites[sGroudonClawSpriteId].x2 = -2;
gSprites[sGroudonClawSpriteId].y2 = 1;
break;
}
}
static void DuoFight_SlideGroudonDown(struct Sprite *sprite)
{
s16 *data = sprite->data;
if (sprite->y <= DISPLAY_HEIGHT)
{
sprite->y += 8;
gSprites[sprite->sGroudonBodySpriteId].y += 8;
gSprites[sGroudonShoulderSpriteId].y += 8;
gSprites[sGroudonClawSpriteId].y += 8;
}
}
static u8 DuoFight_CreateKyogreSprites(void)
{
u8 spriteId;
s16 *data;
spriteId = CreateSprite(&sSpriteTemplate_DuoFight_Kyogre, 126, 96, 1);
gSprites[spriteId].callback = SpriteCB_DuoFight_Kyogre;
data = gSprites[spriteId].data;
data[0] = CreateSprite(&sSpriteTemplate_DuoFight_Kyogre, 158, 96, 1) << 8;
data[0] |= CreateSprite(&sSpriteTemplate_DuoFight_Kyogre, 126, 112, 1);
data[1] = CreateSprite(&sSpriteTemplate_DuoFight_Kyogre, 158, 112, 1) << 8;
data[1] |= CreateSprite(&sSpriteTemplate_DuoFight_Kyogre, 126, 128, 1);
data[2] = CreateSprite(&sSpriteTemplate_DuoFight_Kyogre, 158, 128, 1) << 8;
data[2] |= CreateSprite(&sSpriteTemplate_DuoFight_Kyogre, 94, 128, 2);
data[3] = CreateSprite(&sSpriteTemplate_DuoFight_Kyogre, 126, 128, 2) << 8;
data[3] |= CreateSprite(&sSpriteTemplate_DuoFight_Kyogre, 174, 128, 0);
data[4] = CreateSprite(&sSpriteTemplate_DuoFight_KyogrePectoralFin, 198, 132, 0) << 8;
data[4] |= CreateSprite(&sSpriteTemplate_DuoFight_KyogreDorsalFin, 190, 120, 1);
StartSpriteAnim(&gSprites[data[0] >> 8], 1);
StartSpriteAnim(&gSprites[data[0] & 0xFF], 2);
StartSpriteAnim(&gSprites[data[1] >> 8], 3);
StartSpriteAnim(&gSprites[data[1] & 0xFF], 4);
StartSpriteAnim(&gSprites[data[2] >> 8], 5);
StartSpriteAnim(&gSprites[data[2] & 0xFF], 6);
StartSpriteAnim(&gSprites[data[3] >> 8], 7);
StartSpriteAnim(&gSprites[data[3] & 0xFF], 8);
return spriteId;
}
static void SpriteCB_DuoFight_Kyogre(struct Sprite *sprite)
{
s16 *data = sprite->data;
data[5]++;
data[5] &= 0xF;
if (!(data[5] & 7) && sprite->x != 152)
{
sprite->x++;
gSprites[sprite->data[0] >> 8].x++;
gSprites[sprite->data[0] & 0xFF].x++;
gSprites[data[1] >> 8].x++;
gSprites[data[1] & 0xFF].x++;
gSprites[data[2] >> 8].x++;
gSprites[data[2] & 0xFF].x++;
gSprites[data[3] >> 8].x++;
gSprites[data[3] & 0xFF].x++;
gSprites[data[4] >> 8].x++;
gSprites[data[4] & 0xFF].x++;
}
switch (gSprites[data[2] & 0xFF].animCmdIndex)
{
case 0:
sprite->y2 = 0;
gSprites[data[0] >> 8].y2 = 0;
gSprites[data[0] & 0xFF].y2 = 0;
gSprites[data[1] >> 8].y2 = 0;
gSprites[data[1] & 0xFF].y2 = 0;
gSprites[data[2] >> 8].y2 = 0;
gSprites[data[2] & 0xFF].y2 = 0;
gSprites[data[3] >> 8].y2 = 0;
gSprites[data[3] & 0xFF].y2 = 0;
gSprites[data[4] >> 8].y2 = 0;
gSprites[data[4] & 0xFF].y2 = 0;
break;
case 1:
case 3:
sprite->y2 = 1;
gSprites[data[0] >> 8].y2 = 1;
gSprites[data[0] & 0xFF].y2 = 1;
gSprites[data[1] >> 8].y2 = 1;
gSprites[data[1] & 0xFF].y2 = 1;
gSprites[data[2] >> 8].y2 = 1;
gSprites[data[2] & 0xFF].y2 = 1;
gSprites[data[3] >> 8].y2 = 1;
gSprites[data[3] & 0xFF].y2 = 1;
gSprites[data[4] >> 8].y2 = 1;
gSprites[data[4] & 0xFF].y2 = 1;
break;
case 2:
sprite->y2 = 2;
gSprites[data[0] >> 8].y2 = 2;
gSprites[data[0] & 0xFF].y2 = 2;
gSprites[data[1] >> 8].y2 = 2;
gSprites[data[1] & 0xFF].y2 = 2;
gSprites[data[2] >> 8].y2 = 2;
gSprites[data[4] & 0xFF].y2 = 2;
break;
}
}
static void DuoFight_SlideKyogreDown(struct Sprite *sprite)
{
s16 *data = sprite->data;
if (sprite->y <= DISPLAY_HEIGHT)
{
sprite->y += 8;
gSprites[sprite->data[0] >> 8].y += 8;
gSprites[sprite->data[0] & 0xFF].y += 8;
gSprites[data[1] >> 8].y += 8;
gSprites[data[1] & 0xFF].y += 8;
gSprites[data[2] >> 8].y += 8;
gSprites[data[2] & 0xFF].y += 8;
gSprites[data[3] >> 8].y += 8;
gSprites[data[3] & 0xFF].y += 8;
gSprites[data[4] >> 8].y += 8;
gSprites[data[4] & 0xFF].y += 8;
}
}
#undef tTimer
#undef tHelperTaskId
#undef tGroudonSpriteId
#undef tKyogreSpriteId
#undef sGroudonBodySpriteId
#undef sGroudonShoulderSpriteId
#undef sGroudonClawSpriteId
// RAY_ANIM_TAKES_FLIGHT
#define tState data[0]
#define tTimer data[1]
#define tScale data[2]
#define tScaleSpeed data[3]
#define tYCoord data[4]
#define tYSpeed data[5]
#define tYOffset data[6]
#define tYOffsetDir data[7]
static void InitTakesFlightSceneBgs(void)
{
ResetVramOamAndBgCntRegs();
ResetBgsAndClearDma3BusyFlags(0);
InitBgsFromTemplates(1, sBgTemplates_TakesFlight, ARRAY_COUNT(sBgTemplates_TakesFlight));
SetBgTilemapBuffer(0, sRayScene->tilemapBuffers[0]);
SetBgTilemapBuffer(1, sRayScene->tilemapBuffers[1]);
SetBgTilemapBuffer(2, sRayScene->tilemapBuffers[2]);
ResetAllBgsCoordinates();
ScheduleBgCopyTilemapToVram(0);
ScheduleBgCopyTilemapToVram(1);
ScheduleBgCopyTilemapToVram(2);
SetGpuReg(REG_OFFSET_DISPCNT, DISPCNT_OBJ_ON | DISPCNT_OBJ_1D_MAP);
ShowBg(0);
ShowBg(1);
ShowBg(2);
SetGpuReg(REG_OFFSET_BLDCNT, 0);
}
static void LoadTakesFlightSceneGfx(void)
{
ResetTempTileDataBuffers();
DecompressAndCopyTileDataToVram(0, gRaySceneDuoFight_Clouds_Gfx, 0, 0, 0); // Re-uses clouds from previous scene
DecompressAndCopyTileDataToVram(1, gRaySceneTakesFlight_Bg_Gfx, 0, 0, 0);
DecompressAndCopyTileDataToVram(2, gRaySceneTakesFlight_Rayquaza_Gfx, 0, 0, 0);
while (FreeTempTileDataBuffersIfPossible())
;
LZDecompressWram(gRaySceneDuoFight_Clouds2_Tilemap, sRayScene->tilemapBuffers[0]);
LZDecompressWram(gRaySceneTakesFlight_Bg_Tilemap, sRayScene->tilemapBuffers[1]);
LZDecompressWram(gRaySceneTakesFlight_Rayquaza_Tilemap, sRayScene->tilemapBuffers[2]);
LoadCompressedPalette(gRaySceneTakesFlight_Rayquaza_Pal, 0, 64);
LoadCompressedSpriteSheet(&sSpriteSheet_TakesFlight_Smoke);
LoadCompressedSpritePalette(&sSpritePal_TakesFlight_Smoke);
}
static void Task_RayTakesFlightAnim(u8 taskId)
{
s16 *data = gTasks[taskId].data;
PlayNewMapMusic(MUS_RAYQUAZA_APPEARS);
InitTakesFlightSceneBgs();
LoadTakesFlightSceneGfx();
SetGpuReg(REG_OFFSET_BLDCNT, BLDCNT_TGT1_OBJ | BLDCNT_TGT2_BG1 | BLDCNT_EFFECT_BLEND);
SetGpuReg(REG_OFFSET_BLDALPHA, BLDALPHA_BLEND(8, 8));
BlendPalettes(PALETTES_ALL, 16, 0);
SetVBlankCallback(VBlankCB_RayquazaScene);
CreateTask(Task_TakesFlight_CreateSmoke, 0);
tState = 0;
tTimer = 0;
gTasks[taskId].func = Task_HandleRayTakesFlight;
}
// Animate Rayquaza (flying up and down, and changing size as it gets further from the screen)
// In this scene Rayquaza is a bg tilemap on bg 2, not a sprite
static void Task_HandleRayTakesFlight(u8 taskId)
{
s16 *data = gTasks[taskId].data;
switch (tState)
{
case 0:
// Delay, then fade in
if (tTimer == 8)
{
BeginNormalPaletteFade(PALETTES_ALL, 0, 0x10, 0, RGB_BLACK);
tScale = 0;
tScaleSpeed = 30;
tYCoord = 0;
tYSpeed = 7;
tTimer = 0;
tState++;
}
else
{
tTimer++;
}
break;
case 1:
// Fly in
tScale += tScaleSpeed;
tYCoord += tYSpeed;
if (tScaleSpeed > 3)
tScaleSpeed -= 3;
if (tYSpeed != 0)
tYSpeed--;
if (tScale > 255)
{
tScale = 256;
tScaleSpeed = 0;
tYOffset = 12;
tYOffsetDir = -1;
tTimer = 0;
tState++;
}
SetBgAffine(2, 0x7800, 0x1800, 120, tYCoord + 32, tScale, tScale, 0);
break;
case 2:
// Float up and down
tTimer++;
SetBgAffine(2, 0x7800, 0x1800, 120, tYCoord + 32 + (tYOffset >> 2), tScale, tScale, 0);
tYOffset += tYOffsetDir;
if (tYOffset == 12 || tYOffset == -12)
{
tYOffsetDir *= -1;
if (tTimer > 295)
{
tState++;
BeginNormalPaletteFade(PALETTES_ALL, 6, 0, 0x10, RGB_BLACK);
}
}
break;
case 3:
// Fly away, fade out
tScale += 16;
SetBgAffine(2, 0x7800, 0x1800, 120, tYCoord + 32, tScale, tScale, 0);
Task_RayTakesFlightEnd(taskId);
break;
}
}
#undef tState
#undef tTimer
#undef tScale
#undef tScaleSpeed
#undef tYCoord
#undef tYSpeed
#undef tYOffset
#undef tYOffsetDir
static void Task_RayTakesFlightEnd(u8 taskId)
{
if (!gPaletteFade.active)
{
SetVBlankCallback(NULL);
ResetSpriteData();
FreeAllSpritePalettes();
gTasks[taskId].func = Task_SetNextAnim;
}
}
#define tSmokeId data[0]
#define tTimer data[1]
#define sSmokeId data[0]
#define sTimer data[1]
static void Task_TakesFlight_CreateSmoke(u8 taskId)
{
s16 *data = gTasks[taskId].data;
if ((tTimer & 3) == 0)
{
u8 spriteId = CreateSprite(&sSpriteTemplate_TakesFlight_Smoke,
(sTakesFlight_SmokeCoords[tSmokeId][0] * 4) + 120,
(sTakesFlight_SmokeCoords[tSmokeId][1] * 4) + 80,
0);
gSprites[spriteId].sSmokeId = (s8)(tSmokeId);
gSprites[spriteId].oam.objMode = ST_OAM_OBJ_BLEND;
gSprites[spriteId].oam.affineMode = ST_OAM_AFFINE_DOUBLE;
gSprites[spriteId].oam.priority = 2;
InitSpriteAffineAnim(&gSprites[spriteId]);
if (tSmokeId == MAX_SMOKE - 1)
{
DestroyTask(taskId);
return;
}
else
{
tSmokeId++;
}
}
tTimer++;
}
#undef tSmokeId
#undef tTimer
static void SpriteCB_TakesFlight_Smoke(struct Sprite *sprite)
{
if (sprite->sTimer == 0)
{
sprite->x2 = 0;
sprite->y2 = 0;
}
else
{
sprite->x2 += sTakesFlight_SmokeCoords[sprite->sSmokeId][0];
sprite->y2 += sTakesFlight_SmokeCoords[sprite->sSmokeId][1];
}
sprite->sTimer++;
sprite->sTimer &= 0xF;
}
#undef sSmokeId
#undef sTimer
// RAY_ANIM_DESCENDS
static void InitDescendsSceneBgs(void)
{
ResetVramOamAndBgCntRegs();
ResetBgsAndClearDma3BusyFlags(0);
InitBgsFromTemplates(0, sBgTemplates_Descends, ARRAY_COUNT(sBgTemplates_Descends));
SetBgTilemapBuffer(0, sRayScene->tilemapBuffers[0]);
SetBgTilemapBuffer(1, sRayScene->tilemapBuffers[1]);
SetBgTilemapBuffer(2, sRayScene->tilemapBuffers[2]);
SetBgTilemapBuffer(3, sRayScene->tilemapBuffers[3]);
ResetAllBgsCoordinates();
ScheduleBgCopyTilemapToVram(0);
ScheduleBgCopyTilemapToVram(1);
ScheduleBgCopyTilemapToVram(2);
ScheduleBgCopyTilemapToVram(3);
SetGpuReg(REG_OFFSET_DISPCNT, DISPCNT_OBJ_ON | DISPCNT_OBJ_1D_MAP);
ShowBg(0);
ShowBg(1);
ShowBg(2);
ShowBg(3);
SetGpuReg(REG_OFFSET_BLDCNT, 0);
}
static void LoadDescendsSceneGfx(void)
{
ResetTempTileDataBuffers();
DecompressAndCopyTileDataToVram(0, gRaySceneDescends_Light_Gfx, 0, 0, 0);
DecompressAndCopyTileDataToVram(1, gRaySceneDescends_Bg_Gfx, 0, 0, 0);
while (FreeTempTileDataBuffersIfPossible())
;
LZDecompressWram(gRaySceneDescends_Light_Tilemap, sRayScene->tilemapBuffers[0]);
LZDecompressWram(gRaySceneDescends_Bg_Tilemap, sRayScene->tilemapBuffers[3]);
CpuFastFill16(0, sRayScene->tilemapBuffers[2], BG_SCREEN_SIZE);
CpuFastCopy(sRayScene->tilemapBuffers[3], sRayScene->tilemapBuffers[1], BG_SCREEN_SIZE);
CpuFastFill16(0, &sRayScene->tilemapBuffers[1][0x100], 0x340);
LoadCompressedPalette(gRaySceneDescends_Bg_Pal, 0, 0x40);
gPlttBufferUnfaded[0] = RGB_WHITE;
gPlttBufferFaded[0] = RGB_WHITE;
LoadCompressedSpriteSheet(&sSpriteSheet_Descends_Rayquaza);
LoadCompressedSpriteSheet(&sSpriteSheet_Descends_RayquazaTail);
LoadCompressedSpritePalette(&sSpritePal_Descends_Rayquaza);
}
// Draw ray of light emerging from the clouds
static void HBlankCB_RayDescends(void)
{
u16 vcount = GetGpuReg(REG_OFFSET_VCOUNT);
if (vcount >= 24 && vcount <= 135 && vcount - 24 <= sRayScene->revealedLightLine)
REG_BLDALPHA = 0xD08; // This line is above where light has been revealed, draw it
else
REG_BLDALPHA = 0x1000; // Below where light has been revealed, hide it
if (vcount == 0)
{
if (sRayScene->revealedLightLine <= 0x1FFF)
{
// Increase the number of pixel rows of the light that have been revealed
// Gradually slows as it reaches the bottom
if (sRayScene->revealedLightLine <= 39)
sRayScene->revealedLightLine += 4;
else if (sRayScene->revealedLightLine <= 79)
sRayScene->revealedLightLine += 2;
else
sRayScene->revealedLightLine += 1;
}
// Pointless
sRayScene->revealedLightTimer++;
}
}
#define tState data[0]
#define tTimer data[1]
static void Task_RayDescendsAnim(u8 taskId)
{
s16 *data = gTasks[taskId].data;
InitDescendsSceneBgs();
LoadDescendsSceneGfx();
SetGpuRegBits(REG_OFFSET_BLDCNT, BLDCNT_TGT1_BG0 | BLDCNT_TGT2_BG1 | BLDCNT_TGT2_BG2 | BLDCNT_TGT2_BG3 | BLDCNT_TGT2_OBJ | BLDCNT_EFFECT_BLEND);
SetGpuReg(REG_OFFSET_BLDALPHA, BLDALPHA_BLEND(0, 16));
BlendPalettes(PALETTES_ALL, 0x10, 0);
SetVBlankCallback(VBlankCB_RayquazaScene);
sRayScene->revealedLightLine = 0;
sRayScene->revealedLightTimer = 0;
tState = 0;
tTimer = 0;
data[2] = 0; // Below data assignments do nothing
data[3] = 0;
data[4] = 0x1000;
gTasks[taskId].func = Task_HandleRayDescends;
}
static void Task_HandleRayDescends(u8 taskId)
{
s16 *data = gTasks[taskId].data;
switch (tState)
{
case 0:
// Delay, then fade in
if (tTimer == 8)
{
BeginNormalPaletteFade(PALETTES_ALL, 0, 0x10, 0, RGB_BLACK);
tTimer = 0;
tState++;
}
else
{
tTimer++;
}
break;
case 1:
if (!gPaletteFade.active)
{
// Delay, then start ray of light
if (tTimer == 10)
{
tTimer = 0;
tState++;
SetHBlankCallback(HBlankCB_RayDescends);
EnableInterrupts(INTR_FLAG_HBLANK | INTR_FLAG_VBLANK);
}
else
{
tTimer++;
}
}
break;
case 2:
// Delay, then start Rayquaza emerging from clouds
if (tTimer == 80)
{
tTimer = 0;
tState++;
CreateDescendsRayquazaSprite();
}
else
{
tTimer++;
}
break;
case 3:
// Wait while Rayquaza descends
if (++tTimer == 368)
{
tTimer = 0;
tState++;
}
break;
case 4:
// Fade out
BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 0x10, RGB_BLACK);
gTasks[taskId].func = Task_RayDescendsEnd;
break;
}
}
static void Task_RayDescendsEnd(u8 taskId)
{
if (!gPaletteFade.active)
{
SetVBlankCallback(NULL);
SetHBlankCallback(NULL);
ResetSpriteData();
FreeAllSpritePalettes();
gTasks[taskId].func = Task_SetNextAnim;
}
}
#define sTailSpriteId data[0]
#define sTimer data[2]
#define sXMovePeriod data[3]
#define sYMovePeriod data[4]
static u8 CreateDescendsRayquazaSprite(void)
{
u8 spriteId = CreateSprite(&sSpriteTemplate_Descends_Rayquaza, 160, 0, 0);
s16 *data = gSprites[spriteId].data;
sTailSpriteId = CreateSprite(&sSpriteTemplate_Descends_RayquazaTail, 184, -48, 0);
gSprites[spriteId].callback = SpriteCB_Descends_Rayquaza;
gSprites[spriteId].oam.priority = 3;
gSprites[sTailSpriteId].oam.priority = 3;
return spriteId;
}
static void SpriteCB_Descends_Rayquaza(struct Sprite *sprite)
{
s16 *data = sprite->data;
s16 frame = sTimer;
// Updates to Rayquaza's coords occur more frequently
// as time goes on (it accelerates as it emerges)
if (frame == 0)
{
sXMovePeriod = 12;
sYMovePeriod = 8;
}
else if (frame == 256)
{
sXMovePeriod = 9;
sYMovePeriod = 7;
}
else if (frame == 268)
{
sXMovePeriod = 8;
sYMovePeriod = 6;
}
else if (frame == 280)
{
sXMovePeriod = 7;
sYMovePeriod = 5;
}
else if (frame == 292)
{
sXMovePeriod = 6;
sYMovePeriod = 4;
}
else if (frame == 304)
{
sXMovePeriod = 5;
sYMovePeriod = 3;
}
else if (frame == 320)
{
sXMovePeriod = 4;
sYMovePeriod = 2;
}
if (sTimer % sXMovePeriod == 0)
{
sprite->x2--;
gSprites[sTailSpriteId].x2--;
}
if (sTimer % sYMovePeriod == 0)
{
sprite->y2++;
gSprites[sTailSpriteId].y2++;
}
sTimer++;
}
#undef sTailSpriteId
#undef sTimer
#undef sXMovePeriod
#undef sYMovePeriod
// RAY_ANIM_CHARGES
static void InitChargesSceneBgs(void)
{
ResetVramOamAndBgCntRegs();
ResetBgsAndClearDma3BusyFlags(0);
InitBgsFromTemplates(0, sBgTemplates_Charges, ARRAY_COUNT(sBgTemplates_Charges));
SetBgTilemapBuffer(0, sRayScene->tilemapBuffers[0]);
SetBgTilemapBuffer(1, sRayScene->tilemapBuffers[1]);
SetBgTilemapBuffer(2, sRayScene->tilemapBuffers[2]);
SetBgTilemapBuffer(3, sRayScene->tilemapBuffers[3]);
ResetAllBgsCoordinates();
ScheduleBgCopyTilemapToVram(0);
ScheduleBgCopyTilemapToVram(1);
ScheduleBgCopyTilemapToVram(2);
ScheduleBgCopyTilemapToVram(3);
SetGpuReg(REG_OFFSET_DISPCNT, DISPCNT_OBJ_ON | DISPCNT_OBJ_1D_MAP | DISPCNT_WIN0_ON);
ShowBg(0);
ShowBg(1);
ShowBg(2);
ShowBg(3);
SetGpuReg(REG_OFFSET_BLDCNT, 0);
}
static void LoadChargesSceneGfx(void)
{
ResetTempTileDataBuffers();
DecompressAndCopyTileDataToVram(1, gRaySceneCharges_Rayquaza_Gfx, 0, 0, 0);
DecompressAndCopyTileDataToVram(2, gRaySceneCharges_Streaks_Gfx, 0, 0, 0);
DecompressAndCopyTileDataToVram(3, gRaySceneCharges_Bg_Gfx, 0, 0, 0);
while (FreeTempTileDataBuffersIfPossible())
;
LZDecompressWram(gRaySceneCharges_Orbs_Tilemap, sRayScene->tilemapBuffers[0]);
LZDecompressWram(gRaySceneCharges_Rayquaza_Tilemap, sRayScene->tilemapBuffers[1]);
LZDecompressWram(gRaySceneCharges_Streaks_Tilemap, sRayScene->tilemapBuffers[2]);
LZDecompressWram(gRaySceneCharges_Bg_Tilemap, sRayScene->tilemapBuffers[3]);
LoadCompressedPalette(gRaySceneCharges_Bg_Pal, 0, 0x80);
}
#define tState data[0]
#define tTimer data[1]
#define tRayquazaTaskId data[2]
#define tSoundTimer data[3]
static void Task_RayChargesAnim(u8 taskId)
{
s16 *data = gTasks[taskId].data;
InitChargesSceneBgs();
LoadChargesSceneGfx();
SetWindowsHideVertBorders();
BlendPalettes(PALETTES_ALL, 0x10, 0);
SetVBlankCallback(VBlankCB_RayquazaScene);
tState = 0;
tTimer = 0;
tRayquazaTaskId = CreateTask(Task_RayCharges_ShakeRayquaza, 0);
gTasks[taskId].func = Task_HandleRayCharges;
}
static void Task_HandleRayCharges(u8 taskId)
{
s16 *data = gTasks[taskId].data;
RayCharges_AnimateBg();
if ((tSoundTimer & 7) == 0 && tState <= 1 && tTimer <= 89)
PlaySE(SE_INTRO_BLAST);
tSoundTimer++;
switch (tState)
{
case 0:
// Delay, then fade in
if (tTimer == 8)
{
BeginNormalPaletteFade(PALETTES_ALL, 0, 0x10, 0, RGB_BLACK);
tTimer = 0;
tState++;
}
else
{
tTimer++;
}
break;
case 1:
// Delay while Rayquaza shakes, then start Rayquaza moving offscreen
if (tTimer == 127)
{
tTimer = 0;
tState++;
gTasks[tRayquazaTaskId].func = Task_RayCharges_FlyOffscreen;
}
else
{
tTimer++;
}
break;
case 2:
// Delay for Rayquaza's flying animation
if (tTimer == 12)
{
tTimer = 0;
tState++;
}
else
{
tTimer++;
}
break;
case 3:
// Fade out
BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 0x10, RGB_BLACK);
gTasks[taskId].func = Task_RayChargesEnd;
break;
}
}
#undef tState
#undef tTimer
#undef tSoundTimer
#define tState data[0]
#define tOffset data[1]
#define tShakeDir data[2]
#define tTimer data[15]
static void Task_RayCharges_ShakeRayquaza(u8 taskId)
{
s16 *data = gTasks[taskId].data;
if ((tTimer & 3) == 0)
{
ChangeBgX(1, (Random() % 8 - 4) << 8, BG_COORD_SET);
ChangeBgY(1, (Random() % 8 - 4) << 8, BG_COORD_SET);
}
tTimer++;
}
// Rayquaza backs up then launches forward
static void Task_RayCharges_FlyOffscreen(u8 taskId)
{
s16 *data = gTasks[taskId].data;
if (tState == 0)
{
ChangeBgX(1, 0, BG_COORD_SET);
ChangeBgY(1, 0, BG_COORD_SET);
tState++;
tOffset = 10;
tShakeDir = -1;
}
else if (tState == 1)
{
ChangeBgX(1, tOffset << 8, BG_COORD_SUB);
ChangeBgY(1, tOffset << 8, BG_COORD_ADD);
tOffset += tShakeDir;
if (tOffset == -10)
tShakeDir *= -1;
}
}
#undef tState
#undef tOffset
#undef tShakeDir
#undef tTimer
static void RayCharges_AnimateBg(void)
{
// Update yellow orbs
ChangeBgX(2, 0x400, BG_COORD_SUB);
ChangeBgY(2, 0x400, BG_COORD_ADD);
// Update blue streaks
ChangeBgX(0, 0x800, BG_COORD_SUB);
ChangeBgY(0, 0x800, BG_COORD_ADD);
}
static void Task_RayChargesEnd(u8 taskId)
{
s16 *data = gTasks[taskId].data;
RayCharges_AnimateBg();
if (!gPaletteFade.active)
{
SetVBlankCallback(NULL);
ResetWindowDimensions();
DestroyTask(tRayquazaTaskId);
gTasks[taskId].func = Task_SetNextAnim;
}
}
#undef tRayquazaTaskId
// RAY_ANIM_CHASES_AWAY
static void InitChasesAwaySceneBgs(void)
{
ResetVramOamAndBgCntRegs();
ResetBgsAndClearDma3BusyFlags(0);
InitBgsFromTemplates(1, sBgTemplates_ChasesAway, ARRAY_COUNT(sBgTemplates_ChasesAway));
SetBgTilemapBuffer(0, sRayScene->tilemapBuffers[0]);
SetBgTilemapBuffer(1, sRayScene->tilemapBuffers[1]);
SetBgTilemapBuffer(2, sRayScene->tilemapBuffers[2]);
ResetAllBgsCoordinates();
ScheduleBgCopyTilemapToVram(0);
ScheduleBgCopyTilemapToVram(1);
ScheduleBgCopyTilemapToVram(2);
SetGpuReg(REG_OFFSET_DISPCNT, DISPCNT_OBJ_ON | DISPCNT_OBJ_1D_MAP | DISPCNT_WIN0_ON);
ShowBg(0);
ShowBg(1);
ShowBg(2);
SetGpuReg(REG_OFFSET_BLDCNT, 0);
}
static void LoadChasesAwaySceneGfx(void)
{
ResetTempTileDataBuffers();
DecompressAndCopyTileDataToVram(2, gRaySceneChasesAway_Ring_Gfx, 0, 0, 0);
DecompressAndCopyTileDataToVram(0, gRaySceneChasesAway_Light_Gfx, 0, 0, 0);
while (FreeTempTileDataBuffersIfPossible())
;
LZDecompressWram(gRaySceneChasesAway_Bg_Tilemap, sRayScene->tilemapBuffers[1]);
LZDecompressWram(gRaySceneChasesAway_Light_Tilemap, sRayScene->tilemapBuffers[0]);
LZDecompressWram(gRaySceneChasesAway_Ring_Tilemap, sRayScene->tilemapBuffers[2]);
LoadCompressedPalette(gRaySceneChasesAway_Bg_Pal, 0, 0x60);
LoadCompressedSpriteSheet(&sSpriteSheet_ChasesAway_Groudon);
LoadCompressedSpriteSheet(&sSpriteSheet_ChasesAway_GroudonTail);
LoadCompressedSpriteSheet(&sSpriteSheet_ChasesAway_Kyogre);
LoadCompressedSpriteSheet(&sSpriteSheet_ChasesAway_Rayquaza);
LoadCompressedSpriteSheet(&sSpriteSheet_ChasesAway_RayquazaTail);
LoadCompressedSpriteSheet(&sSpriteSheet_ChasesAway_KyogreSplash);
LoadCompressedSpritePalette(&sSpritePal_ChasesAway_Groudon);
LoadCompressedSpritePalette(&sSpritePal_ChasesAway_Kyogre);
LoadCompressedSpritePalette(&sSpritePal_ChasesAway_Rayquaza);
LoadCompressedSpritePalette(&sSpritePal_ChasesAway_KyogreSplash);
}
#define tState data[0]
#define tTimer data[1]
#define tBgTaskId data[2]
static void Task_RayChasesAwayAnim(u8 taskId)
{
s16 *data = gTasks[taskId].data;
InitChasesAwaySceneBgs();
LoadChasesAwaySceneGfx();
SetWindowsHideVertBorders();
ClearGpuRegBits(REG_OFFSET_DISPCNT, DISPCNT_BG2_ON);
SetGpuReg(REG_OFFSET_BLDCNT, BLDCNT_TGT1_BG0 | BLDCNT_TGT2_BG1 | BLDCNT_EFFECT_BLEND);
SetGpuReg(REG_OFFSET_BLDALPHA, BLDALPHA_BLEND(9, 14));
BlendPalettes(PALETTES_ALL, 0x10, 0);
SetVBlankCallback(VBlankCB_RayquazaScene);
tState = 0;
tTimer = 0;
gTasks[taskId].func = Task_HandleRayChasesAway;
tBgTaskId = CreateTask(Task_ChasesAway_AnimateBg, 0);
gTasks[tBgTaskId].data[0] = 0;
gTasks[tBgTaskId].data[1] = 0;
gTasks[tBgTaskId].data[2] = 0;
gTasks[tBgTaskId].data[3] = 1;
gTasks[tBgTaskId].data[4] = 1;
}
static void Task_HandleRayChasesAway(u8 taskId)
{
s16 *data = gTasks[taskId].data;
switch (tState)
{
case 0:
// Delay, then fade in
if (tTimer == 8)
{
ChasesAway_CreateTrioSprites(taskId);
BeginNormalPaletteFade(PALETTES_ALL, 0, 0x10, 0, RGB_BLACK);
tTimer = 0;
tState++;
}
else
{
tTimer++;
}
break;
case 1:
// Wait for Rayquaza to enter and finish shout anim
if (gSprites[data[5]].callback == SpriteCB_ChasesAway_RayquazaFloat)
{
// Delay, then start Groudon/Kyogre leaving
if (tTimer == 64)
{
ChasesAway_KyogreStartLeave(taskId);
ChasesAway_GroudonStartLeave(taskId);
tTimer = 0;
tState++;
}
else
{
tTimer++;
}
}
break;
case 2:
// Wait for Groudon/Kyogre to leave
if (tTimer == 448)
{
tTimer = 0;
tState++;
}
else
{
// Flash bg white and trio black a few times
tTimer++;
if (tTimer % 144 == 0)
{
BlendPalettesGradually(PALETTES_BG & ~1, 0, 16, 0, RGB_WHITEALPHA, 0, 0);
BlendPalettesGradually(PALETTES_OBJECTS, 0, 16, 0, RGB_BLACK, 0, 1);
}
}
break;
case 3:
// Fade out
BeginNormalPaletteFade(PALETTES_ALL, 4, 0, 0x10, RGB_BLACK);
gTasks[taskId].func = Task_RayChasesAwayEnd;
break;
}
}
#undef tState
#undef tTimer
#define tTimer data[0]
#define tBlendHi data[1]
#define tBlendLo data[2]
#define tBlendHiDir data[3]
#define tBlendLoDir data[4]
// Flickers the ray of light behind Rayquaza
static void Task_ChasesAway_AnimateBg(u8 taskId)
{
s16 *data = gTasks[taskId].data;
if ((tTimer & 0xF) == 0)
{
SetGpuReg(REG_OFFSET_BLDALPHA, ((tBlendHi + 14) << 8 & 0x1F00) | ((tBlendLo + 9) & 0xF));
tBlendHi -= tBlendHiDir;
tBlendLo += tBlendLoDir;
if (tBlendHi == -3 || tBlendHi == 0)
tBlendHiDir *= -1;
if (tBlendLo == 3 || tBlendLo == 0)
tBlendLoDir *= -1;
}
tTimer++;
}
#undef tTimer
#undef tBlendHi
#undef tBlendLo
#undef tBlendHiDir
#undef tBlendLoDir
#define tTimer data[1]
static void Task_RayChasesAwayEnd(u8 taskId)
{
s16 *data = gTasks[taskId].data;
if (!gPaletteFade.active)
{
StopMapMusic();
if (tTimer == 0)
{
SetVBlankCallback(NULL);
ResetWindowDimensions();
ResetSpriteData();
FreeAllSpritePalettes();
DestroyTask(tBgTaskId);
}
if (tTimer == 32)
{
tTimer = 0;
gTasks[taskId].func = Task_SetNextAnim;
}
else
{
tTimer++;
}
}
}
#undef tTimer
#undef tBgTaskId
#define tGroudonSpriteId taskData[3]
#define tKyogreSpriteId taskData[4]
#define tRayquazaSpriteId taskData[5]
static void ChasesAway_CreateTrioSprites(u8 taskId)
{
s16 *taskData, *spriteData;
taskData = gTasks[taskId].data;
tGroudonSpriteId = CreateSprite(&sSpriteTemplate_ChasesAway_Groudon, 64, 120, 0);
spriteData = gSprites[tGroudonSpriteId].data;
spriteData[0] = CreateSprite(&sSpriteTemplate_ChasesAway_GroudonTail, 16, 130, 0);
gSprites[tGroudonSpriteId].oam.priority = 1;
gSprites[spriteData[0]].oam.priority = 1;
tKyogreSpriteId = CreateSprite(&sSpriteTemplate_ChasesAway_Kyogre, 160, 128, 1);
spriteData = gSprites[tKyogreSpriteId].data;
spriteData[0] = CreateSprite(&sSpriteTemplate_ChasesAway_Kyogre, 192, 128, 1);
spriteData[1] = CreateSprite(&sSpriteTemplate_ChasesAway_Kyogre, 224, 128, 1);
gSprites[tKyogreSpriteId].oam.priority = 1;
gSprites[spriteData[0]].oam.priority = 1;
gSprites[spriteData[1]].oam.priority = 1;
StartSpriteAnim(&gSprites[spriteData[0]], 1);
StartSpriteAnim(&gSprites[spriteData[1]], 2);
tRayquazaSpriteId = CreateSprite(&sSpriteTemplate_ChasesAway_Rayquaza, 120, -65, 0);
spriteData = gSprites[tRayquazaSpriteId].data;
spriteData[0] = CreateSprite(&sSpriteTemplate_ChasesAway_RayquazaTail, 120, -113, 0);
gSprites[tRayquazaSpriteId].oam.priority = 1;
gSprites[spriteData[0]].oam.priority = 1;
}
#define sBodyPartSpriteId1 data[0]
#define sBodyPartSpriteId2 data[1]
#define sTimer data[4]
#define sDecel data[5]
#define sSpeed data[6]
#define sIsKyogre data[7]
static void ChasesAway_PushDuoBack(u8 taskId)
{
s16 *taskData = gTasks[taskId].data;
gSprites[tGroudonSpriteId].callback = SpriteCB_ChasesAway_DuoRingPush;
gSprites[tGroudonSpriteId].sTimer = 0;
gSprites[tGroudonSpriteId].sDecel = 0;
gSprites[tGroudonSpriteId].sSpeed = 4;
gSprites[tGroudonSpriteId].sIsKyogre = FALSE;
gSprites[tKyogreSpriteId].callback = SpriteCB_ChasesAway_DuoRingPush;
gSprites[tKyogreSpriteId].sTimer = 0;
gSprites[tKyogreSpriteId].sDecel = 0;
gSprites[tKyogreSpriteId].sSpeed = 4;
gSprites[tKyogreSpriteId].sIsKyogre = TRUE;
}
// Pushes Groudon/Kyogre back slightly, for when Rayquaza's hyper voice ring comes out
static void SpriteCB_ChasesAway_DuoRingPush(struct Sprite *sprite)
{
if ((sprite->sTimer & 7) == 0)
{
if (!sprite->sIsKyogre)
{
sprite->x -= sprite->sSpeed;
gSprites[sprite->sBodyPartSpriteId1].x -= sprite->sSpeed;
}
else
{
sprite->x += sprite->sSpeed;
gSprites[sprite->sBodyPartSpriteId1].x += sprite->sSpeed;
gSprites[sprite->sBodyPartSpriteId2].x += sprite->sSpeed;
}
sprite->sDecel++;
sprite->sSpeed -= sprite->sDecel;
if (sprite->sDecel == 3)
{
sprite->sTimer = 0;
sprite->sDecel = 0;
sprite->sSpeed = 0;
sprite->callback = SpriteCallbackDummy;
return;
}
}
sprite->sTimer++;
}
#undef sBodyPartSpriteId1
#undef sBodyPartSpriteId2
#undef sTimer
#undef sDecel
#undef sSpeed
#undef sIsKyogre
static void ChasesAway_GroudonStartLeave(u8 taskId)
{
s16 *taskData = gTasks[taskId].data;
gSprites[tGroudonSpriteId].callback = SpriteCB_ChasesAway_GroudonLeave;
StartSpriteAnim(&gSprites[tGroudonSpriteId], 1);
}
static void SpriteCB_ChasesAway_GroudonLeave(struct Sprite *sprite)
{
switch (sprite->animCmdIndex)
{
case 0:
case 2:
if (sprite->animDelayCounter % 12 == 0)
{
sprite->x -= 2;
gSprites[sprite->data[0]].x -=2;
}
gSprites[sprite->data[0]].y2 = 0;
break;
case 1:
case 3:
gSprites[sprite->data[0]].y2 = -2;
if ((sprite->animDelayCounter & 15) == 0)
{
sprite->y++;
gSprites[sprite->data[0]].y++;
}
break;
}
}
static void ChasesAway_KyogreStartLeave(u8 taskId)
{
s16 *taskData, *spriteData;
taskData = gTasks[taskId].data;
spriteData = gSprites[tKyogreSpriteId].data;
gSprites[tKyogreSpriteId].callback = SpriteCB_ChasesAway_KyogreLeave;
gSprites[spriteData[0]].callback = SpriteCB_ChasesAway_KyogreLeave;
gSprites[spriteData[1]].callback = SpriteCB_ChasesAway_KyogreLeave;
}
static void SpriteCB_ChasesAway_KyogreLeave(struct Sprite *sprite)
{
if ((sprite->data[4] & 3) == 0)
{
if (sprite->x2 == 1)
sprite->x2 = -1;
else
sprite->x2 = 1;
}
if (sprite->data[5] == 128)
{
sprite->data[7] = CreateSprite(&sSpriteTemplate_ChasesAway_KyogreSplash, 152, 132, 0);
gSprites[sprite->data[7]].oam.priority = 1;
sprite->data[7] = CreateSprite(&sSpriteTemplate_ChasesAway_KyogreSplash, 224, 132, 0);
gSprites[sprite->data[7]].oam.priority = 1;
gSprites[sprite->data[7]].hFlip = 1;
sprite->data[5]++;
}
if (sprite->data[5] > 127)
{
if (sprite->y2 != 32)
{
sprite->data[6]++;
sprite->y2 = sprite->data[6] >> 4;
}
}
else
{
sprite->data[5]++;
}
if (sprite->data[4] % 64 == 0)
PlaySE(SE_M_WHIRLPOOL);
sprite->data[4]++;
}
#define sTailSpriteId data[0]
#define sYOffset data[4]
#define sYOffsetDir data[5]
#define sFloatTimer data[6]
#define sTimer data[7]
#define sTailFloatDelay data[4]
#define sTailFloatPeak data[5]
static void SpriteCB_ChasesAway_Rayquaza(struct Sprite *sprite)
{
s16 frame = sprite->sTimer;
if (frame <= 64)
{
sprite->y2 += 2;
gSprites[sprite->sTailSpriteId].y2 += 2;
if (sprite->sTimer == 64)
{
ChasesAway_SetRayquazaAnim(sprite, 1, 0, -48);
sprite->sYOffset = 5;
sprite->sYOffsetDir = -1;
gSprites[sprite->sTailSpriteId].sTailFloatDelay = 3;
gSprites[sprite->sTailSpriteId].sTailFloatPeak = 5;
}
}
else if (frame <= 111)
{
SpriteCB_ChasesAway_RayquazaFloat(sprite);
if (sprite->sYOffset == 0)
PlaySE(SE_MUGSHOT);
if (sprite->sYOffset == -3)
ChasesAway_SetRayquazaAnim(sprite, 2, 48, 16);
}
else if (frame == 112)
{
gSprites[sprite->sTailSpriteId].sTailFloatDelay = 7;
gSprites[sprite->sTailSpriteId].sTailFloatPeak = 3;
SpriteCB_ChasesAway_RayquazaFloat(sprite);
}
else if (frame <= 327)
{
SpriteCB_ChasesAway_RayquazaFloat(sprite);
}
else if (frame == 328)
{
SpriteCB_ChasesAway_RayquazaFloat(sprite);
ChasesAway_SetRayquazaAnim(sprite, 3, 48, 16);
sprite->x2 = 1;
gSprites[sprite->sTailSpriteId].x2 = 1;
PlayCry_Normal(SPECIES_RAYQUAZA, 0);
CreateTask(Task_ChasesAway_AnimateRing, 0);
}
else
{
switch (frame)
{
case 376:
sprite->x2 = 0;
gSprites[sprite->sTailSpriteId].x2 = 0;
SpriteCB_ChasesAway_RayquazaFloat(sprite);
ChasesAway_SetRayquazaAnim(sprite, 2, 48, 16);
sprite->callback = SpriteCB_ChasesAway_RayquazaFloat;
return;
case 352:
ChasesAway_PushDuoBack(FindTaskIdByFunc(Task_HandleRayChasesAway));
break;
}
}
if (sprite->sTimer > 328 && (sprite->sTimer & 1) == 0)
{
sprite->x2 *= -1;
gSprites[sprite->sTailSpriteId].x2 = sprite->x2;
}
sprite->sTimer++;
}
static void SpriteCB_ChasesAway_RayquazaFloat(struct Sprite *body)
{
struct Sprite *tail = &gSprites[body->sTailSpriteId];
if (!(body->sFloatTimer & tail->sTailFloatDelay))
{
body->y2 += body->sYOffset;
gSprites[body->sTailSpriteId].y2 += body->sYOffset; // why access gSprites again? tail->y2 would be sufficient
body->sYOffset += body->sYOffsetDir;
if (body->sYOffset >= tail->sTailFloatPeak || body->sYOffset <= -tail->sTailFloatPeak)
{
if (body->sYOffset > tail->sTailFloatPeak)
body->sYOffset = tail->sTailFloatPeak;
else if (body->sYOffset < -tail->sTailFloatPeak)
body->sYOffset = -tail->sTailFloatPeak;
body->sYOffsetDir *= -1;
}
}
body->sFloatTimer++;
}
static void ChasesAway_SetRayquazaAnim(struct Sprite *body, u8 animNum, s16 x, s16 y)
{
struct Sprite *tail = &gSprites[body->sTailSpriteId];
tail->x = body->x + x;
tail->y = body->y + y;
tail->x2 = body->x2;
tail->y2 = body->y2;
StartSpriteAnim(body, animNum);
StartSpriteAnim(tail, animNum);
}
#undef sTailSpriteId
#undef sYOffset
#undef sYOffsetDir
#undef sFloatTimer
#undef sTimer
#undef sTailFloatDelay
#undef sTailFloatPeak
#define tState data[0]
#define tScale data[1]
#define tNumRings data[2]
#define tScaleTimer data[3]
#define tScaleSpeed data[4]
#define tSoundTimer data[5]
static void Task_ChasesAway_AnimateRing(u8 taskId)
{
s16 *data = gTasks[taskId].data;
switch (tState)
{
case 0:
SetBgAffine(2, 0x4000, 0x4000, 120, 64, 256, 256, 0);
SetGpuRegBits(REG_OFFSET_DISPCNT, DISPCNT_BG2_ON);
tScaleSpeed = 16;
tState++;
break;
case 1:
if (tSoundTimer == 8)
PlaySE(SE_SLIDING_DOOR);
if (tNumRings == 2)
{
tState++;
}
else
{
tScale += tScaleSpeed;
tSoundTimer++;
if (tScaleTimer % 3 == 0 && tScaleSpeed != 4)
tScaleSpeed -= 2;
tScaleTimer++;
SetBgAffine(2, 0x4000, 0x4000, 120, 64, 256 - tScale, 256 - tScale, 0);
if (tScale > 255)
{
tScale = 0;
tScaleTimer = 0;
tSoundTimer = 0;
tScaleSpeed = 16;
tNumRings++;
}
}
break;
case 2:
ClearGpuRegBits(REG_OFFSET_DISPCNT, DISPCNT_BG2_ON);
DestroyTask(taskId);
break;
}
}