diff --git a/include/gba/io_reg.h b/include/gba/io_reg.h index 548e27163..53b60d5b6 100644 --- a/include/gba/io_reg.h +++ b/include/gba/io_reg.h @@ -552,6 +552,7 @@ #define WININ_WIN0_BG_ALL (WININ_WIN0_BG0 | WININ_WIN0_BG1 | WININ_WIN0_BG2 | WININ_WIN0_BG3) #define WININ_WIN0_OBJ (1 << 4) #define WININ_WIN0_CLR (1 << 5) +#define WININ_WIN0_ALL (WININ_WIN0_BG_ALL | WININ_WIN0_OBJ | WININ_WIN0_CLR) #define WININ_WIN1_BG0 (1 << 8) #define WININ_WIN1_BG1 (1 << 9) #define WININ_WIN1_BG2 (1 << 10) @@ -559,6 +560,7 @@ #define WININ_WIN1_BG_ALL (WININ_WIN1_BG0 | WININ_WIN1_BG1 | WININ_WIN1_BG2 | WININ_WIN1_BG3) #define WININ_WIN1_OBJ (1 << 12) #define WININ_WIN1_CLR (1 << 13) +#define WININ_WIN1_ALL (WININ_WIN1_BG_ALL | WININ_WIN1_OBJ | WININ_WIN1_CLR) #define WINOUT_WIN01_BG0 (1 << 0) #define WINOUT_WIN01_BG1 (1 << 1) @@ -567,6 +569,7 @@ #define WINOUT_WIN01_BG_ALL (WINOUT_WIN01_BG0 | WINOUT_WIN01_BG1 | WINOUT_WIN01_BG2 | WINOUT_WIN01_BG3) #define WINOUT_WIN01_OBJ (1 << 4) #define WINOUT_WIN01_CLR (1 << 5) +#define WINOUT_WIN01_ALL (WINOUT_WIN01_BG_ALL | WINOUT_WIN01_OBJ | WINOUT_WIN01_CLR) #define WINOUT_WINOBJ_BG0 (1 << 8) #define WINOUT_WINOBJ_BG1 (1 << 9) #define WINOUT_WINOBJ_BG2 (1 << 10) @@ -574,6 +577,7 @@ #define WINOUT_WINOBJ_BG_ALL (WINOUT_WINOBJ_BG0 | WINOUT_WINOBJ_BG1 | WINOUT_WINOBJ_BG2 | WINOUT_WINOBJ_BG3) #define WINOUT_WINOBJ_OBJ (1 << 12) #define WINOUT_WINOBJ_CLR (1 << 13) +#define WINOUT_WINOBJ_ALL (WINOUT_WINOBJ_BG_ALL | WINOUT_WINOBJ_OBJ | WINOUT_WINOBJ_CLR) #define WIN_RANGE(a, b) (((a) << 8) | (b)) #define WIN_RANGE2(a, b) ((b) | ((a) << 8)) @@ -584,8 +588,10 @@ #define BLDCNT_TGT1_BG1 (1 << 1) #define BLDCNT_TGT1_BG2 (1 << 2) #define BLDCNT_TGT1_BG3 (1 << 3) +#define BLDCNT_TGT1_BG_ALL (BLDCNT_TGT1_BG0 | BLDCNT_TGT1_BG1 | BLDCNT_TGT1_BG2 | BLDCNT_TGT1_BG3) #define BLDCNT_TGT1_OBJ (1 << 4) #define BLDCNT_TGT1_BD (1 << 5) +#define BLDCNT_TGT1_ALL (BLDCNT_TGT1_BG_ALL | BLDCNT_TGT1_OBJ | BLDCNT_TGT1_BD) // Bits 6-7 select the special effect #define BLDCNT_EFFECT_NONE (0 << 6) // no special effect #define BLDCNT_EFFECT_BLEND (1 << 6) // 1st+2nd targets mixed (controlled by BLDALPHA) @@ -596,9 +602,10 @@ #define BLDCNT_TGT2_BG1 (1 << 9) #define BLDCNT_TGT2_BG2 (1 << 10) #define BLDCNT_TGT2_BG3 (1 << 11) +#define BLDCNT_TGT2_BG_ALL (BLDCNT_TGT2_BG0 | BLDCNT_TGT2_BG1 | BLDCNT_TGT2_BG2 | BLDCNT_TGT2_BG3) #define BLDCNT_TGT2_OBJ (1 << 12) #define BLDCNT_TGT2_BD (1 << 13) -#define BLDCNT_TGT2_ALL (BLDCNT_TGT2_BG0 | BLDCNT_TGT2_BG1 | BLDCNT_TGT2_BG2 | BLDCNT_TGT2_BG3 | BLDCNT_TGT2_OBJ | BLDCNT_TGT2_BD) +#define BLDCNT_TGT2_ALL (BLDCNT_TGT2_BG_ALL | BLDCNT_TGT2_OBJ | BLDCNT_TGT2_BD) // BLDALPHA #define BLDALPHA_BLEND(target1, target2) (((target2) << 8) | (target1)) diff --git a/include/sprite.h b/include/sprite.h index eed582775..6a1b27211 100644 --- a/include/sprite.h +++ b/include/sprite.h @@ -7,6 +7,9 @@ #define SPRITE_NONE 0xFF #define TAG_NONE 0xFFFF +// Given to SetSpriteMatrixAnchor to skip anchoring one of the coords. +#define NO_ANCHOR 0x800 + struct SpriteSheet { const void *data; // Raw uncompressed pixel data @@ -228,7 +231,7 @@ struct Sprite u16 animEnded:1; //0x10 u16 affineAnimEnded:1; //0x20 u16 usingSheet:1; //0x40 - u16 flags_f:1; //0x80 + u16 anchored:1; //0x80 /*0x40*/ u16 sheetTileStart; @@ -316,6 +319,6 @@ void ClearSpriteCopyRequests(void); void ResetAffineAnimData(void); void FreeSpriteTilesIfNotUsingSheet(struct Sprite *sprite); s16 AllocSpriteTiles(u16 tileCount); -void obj_pos2_update_enable(struct Sprite* sprite, s16 xmod, s16 ymod); +void SetSpriteMatrixAnchor(struct Sprite* sprite, s16 xmod, s16 ymod); #endif //GUARD_SPRITE_H diff --git a/src/bg.c b/src/bg.c index 287569fa6..251f42616 100644 --- a/src/bg.c +++ b/src/bg.c @@ -600,58 +600,58 @@ u32 ChangeBgX(u8 bg, u32 value, u8 op) switch (op) { - case 0: - default: - sGpuBgConfigs2[bg].bg_x = value; - break; - case 1: - sGpuBgConfigs2[bg].bg_x += value; - break; - case 2: - sGpuBgConfigs2[bg].bg_x -= value; - break; + case BG_COORD_SET: + default: + sGpuBgConfigs2[bg].bg_x = value; + break; + case BG_COORD_ADD: + sGpuBgConfigs2[bg].bg_x += value; + break; + case BG_COORD_SUB: + sGpuBgConfigs2[bg].bg_x -= value; + break; } mode = GetBgMode(); switch (bg) { - case 0: - temp1 = sGpuBgConfigs2[0].bg_x >> 0x8; - SetGpuReg(REG_OFFSET_BG0HOFS, temp1); - break; - case 1: - temp1 = sGpuBgConfigs2[1].bg_x >> 0x8; - SetGpuReg(REG_OFFSET_BG1HOFS, temp1); - break; - case 2: - if (mode == 0) - { - temp1 = sGpuBgConfigs2[2].bg_x >> 0x8; - SetGpuReg(REG_OFFSET_BG2HOFS, temp1); - } - else - { - temp1 = sGpuBgConfigs2[2].bg_x >> 0x10; - temp2 = sGpuBgConfigs2[2].bg_x & 0xFFFF; - SetGpuReg(REG_OFFSET_BG2X_H, temp1); - SetGpuReg(REG_OFFSET_BG2X_L, temp2); - } - break; - case 3: - if (mode == 0) - { - temp1 = sGpuBgConfigs2[3].bg_x >> 0x8; - SetGpuReg(REG_OFFSET_BG3HOFS, temp1); - } - else if (mode == 2) - { - temp1 = sGpuBgConfigs2[3].bg_x >> 0x10; - temp2 = sGpuBgConfigs2[3].bg_x & 0xFFFF; - SetGpuReg(REG_OFFSET_BG3X_H, temp1); - SetGpuReg(REG_OFFSET_BG3X_L, temp2); - } - break; + case 0: + temp1 = sGpuBgConfigs2[0].bg_x >> 0x8; + SetGpuReg(REG_OFFSET_BG0HOFS, temp1); + break; + case 1: + temp1 = sGpuBgConfigs2[1].bg_x >> 0x8; + SetGpuReg(REG_OFFSET_BG1HOFS, temp1); + break; + case 2: + if (mode == 0) + { + temp1 = sGpuBgConfigs2[2].bg_x >> 0x8; + SetGpuReg(REG_OFFSET_BG2HOFS, temp1); + } + else + { + temp1 = sGpuBgConfigs2[2].bg_x >> 0x10; + temp2 = sGpuBgConfigs2[2].bg_x & 0xFFFF; + SetGpuReg(REG_OFFSET_BG2X_H, temp1); + SetGpuReg(REG_OFFSET_BG2X_L, temp2); + } + break; + case 3: + if (mode == 0) + { + temp1 = sGpuBgConfigs2[3].bg_x >> 0x8; + SetGpuReg(REG_OFFSET_BG3HOFS, temp1); + } + else if (mode == 2) + { + temp1 = sGpuBgConfigs2[3].bg_x >> 0x10; + temp2 = sGpuBgConfigs2[3].bg_x & 0xFFFF; + SetGpuReg(REG_OFFSET_BG3X_H, temp1); + SetGpuReg(REG_OFFSET_BG3X_L, temp2); + } + break; } return sGpuBgConfigs2[bg].bg_x; @@ -679,58 +679,58 @@ u32 ChangeBgY(u8 bg, u32 value, u8 op) switch (op) { - case 0: - default: - sGpuBgConfigs2[bg].bg_y = value; - break; - case 1: - sGpuBgConfigs2[bg].bg_y += value; - break; - case 2: - sGpuBgConfigs2[bg].bg_y -= value; - break; + case BG_COORD_SET: + default: + sGpuBgConfigs2[bg].bg_y = value; + break; + case BG_COORD_ADD: + sGpuBgConfigs2[bg].bg_y += value; + break; + case BG_COORD_SUB: + sGpuBgConfigs2[bg].bg_y -= value; + break; } mode = GetBgMode(); switch (bg) { - case 0: - temp1 = sGpuBgConfigs2[0].bg_y >> 0x8; - SetGpuReg(REG_OFFSET_BG0VOFS, temp1); - break; - case 1: - temp1 = sGpuBgConfigs2[1].bg_y >> 0x8; - SetGpuReg(REG_OFFSET_BG1VOFS, temp1); - break; - case 2: - if (mode == 0) - { - temp1 = sGpuBgConfigs2[2].bg_y >> 0x8; - SetGpuReg(REG_OFFSET_BG2VOFS, temp1); - } - else - { - temp1 = sGpuBgConfigs2[2].bg_y >> 0x10; - temp2 = sGpuBgConfigs2[2].bg_y & 0xFFFF; - SetGpuReg(REG_OFFSET_BG2Y_H, temp1); - SetGpuReg(REG_OFFSET_BG2Y_L, temp2); - } - break; - case 3: - if (mode == 0) - { - temp1 = sGpuBgConfigs2[3].bg_y >> 0x8; - SetGpuReg(REG_OFFSET_BG3VOFS, temp1); - } - else if (mode == 2) - { - temp1 = sGpuBgConfigs2[3].bg_y >> 0x10; - temp2 = sGpuBgConfigs2[3].bg_y & 0xFFFF; - SetGpuReg(REG_OFFSET_BG3Y_H, temp1); - SetGpuReg(REG_OFFSET_BG3Y_L, temp2); - } - break; + case 0: + temp1 = sGpuBgConfigs2[0].bg_y >> 0x8; + SetGpuReg(REG_OFFSET_BG0VOFS, temp1); + break; + case 1: + temp1 = sGpuBgConfigs2[1].bg_y >> 0x8; + SetGpuReg(REG_OFFSET_BG1VOFS, temp1); + break; + case 2: + if (mode == 0) + { + temp1 = sGpuBgConfigs2[2].bg_y >> 0x8; + SetGpuReg(REG_OFFSET_BG2VOFS, temp1); + } + else + { + temp1 = sGpuBgConfigs2[2].bg_y >> 0x10; + temp2 = sGpuBgConfigs2[2].bg_y & 0xFFFF; + SetGpuReg(REG_OFFSET_BG2Y_H, temp1); + SetGpuReg(REG_OFFSET_BG2Y_L, temp2); + } + break; + case 3: + if (mode == 0) + { + temp1 = sGpuBgConfigs2[3].bg_y >> 0x8; + SetGpuReg(REG_OFFSET_BG3VOFS, temp1); + } + else if (mode == 2) + { + temp1 = sGpuBgConfigs2[3].bg_y >> 0x10; + temp2 = sGpuBgConfigs2[3].bg_y & 0xFFFF; + SetGpuReg(REG_OFFSET_BG3Y_H, temp1); + SetGpuReg(REG_OFFSET_BG3Y_L, temp2); + } + break; } return sGpuBgConfigs2[bg].bg_y; diff --git a/src/intro.c b/src/intro.c index d3f5fe1e4..f3621b143 100644 --- a/src/intro.c +++ b/src/intro.c @@ -73,6 +73,9 @@ enum { BG_SCENE1_UNUSED2 }; +#define PALSLOT_SCENE1_GRASS 1 +#define PALSLOT_SCENE1_BG 2 + // Background IDs for Scene 2 enum { BG_SCENE2_PLANTS, @@ -89,6 +92,35 @@ enum { BG_SCENE3_UNUSED2 }; +enum { + ANIM_NIDORINO_NORMAL, + ANIM_NIDORINO_CRY, + ANIM_NIDORINO_CROUCH, + ANIM_NIDORINO_HOP, + ANIM_NIDORINO_ATTACK, +}; + +enum { + ANIM_SPARKLE_LOOP, + ANIM_SPARKLE_ONCE, +}; + +enum { + ANIM_SWIPE_TOP, + ANIM_SWIPE_BOTTOM, +}; + +enum { + AFFINEANIM_NORMAL, + AFFINEANIM_ZOOM, +}; + +// Window ids for sWindowTemplates (only one) +enum { + WIN_GF_TEXT_LOGO, + WIN_COUNT +}; + #define NUM_GENGAR_BACK_SPRITES 4 #define COLOSSEUM_GAME_CODE 0x65366347 // "Gc6e" in ASCII @@ -103,13 +135,14 @@ struct IntroSequenceData u8 state; u8 taskId; bool8 gengarAttackLanded; - u16 data[6]; + u16 data[5]; // [0] and [1] are set but never read, the rest are unused + u16 timer; struct Sprite *gameFreakLogoArtSprite; - struct Sprite *nidorinoAnimSprite; - struct Sprite *gengarStaticSprite; - struct Sprite *nidorinoStaticSprite; - struct Sprite *grassSprite; - struct Sprite *gengarBackSprites[NUM_GENGAR_BACK_SPRITES]; + struct Sprite *scene3NidorinoSprite; + struct Sprite *scene2GengarSprite; + struct Sprite *scene2NidorinoSprite; + struct Sprite *scene3GrassSprite; + struct Sprite *scene3GengarSprites[NUM_GENGAR_BACK_SPRITES]; u8 unused0[4]; u8 gameFreakLogoGfx[0x400]; u8 gameFreakTextGfx[0x400]; @@ -117,26 +150,27 @@ struct IntroSequenceData }; // size: 0x28BC static EWRAM_DATA struct GcmbStruct sGcmb = {0}; -static EWRAM_DATA u16 gUnknown_203AB00 = 0; -static EWRAM_DATA u16 gUnknown_203AB02 = 0; -static EWRAM_DATA u16 gUnknown_203AB04 = 0; -static EWRAM_DATA u16 gUnknown_203AB06 = 0; -static EWRAM_DATA u16 gUnknown_203AB08 = 0; -static EWRAM_DATA u16 gUnknown_203AB0A = 0; -static EWRAM_DATA u16 gUnknown_203AB0C = 0; -static EWRAM_DATA u16 sLargeStarXSpeed = 0; -static EWRAM_DATA u16 sLargeStarYSpeed = 0; -static EWRAM_DATA u16 sTrailingSparklesXmodMask = 0; -static EWRAM_DATA u16 sUnusedVarRelatedToGameFreakStars = 0; -static EWRAM_DATA u16 sTrailingSparklesSpawnRate = 0; -static EWRAM_DATA u16 sTrailingSparklesFlickerStartTime = 0; -static EWRAM_DATA u16 sTrailingSparklesDestroySpriteTime = 0; -static EWRAM_DATA u16 sTrailingSparklesGravityShift = 0; -static EWRAM_DATA u16 sTrailingSparklesXspeed = 0; -static EWRAM_DATA u16 sTrailingSparklesYspeed = 0; -static EWRAM_DATA u16 sTrailingSparklesXprecision = 0; -static EWRAM_DATA u16 sTrailingSparklesYprecision = 0; +static EWRAM_DATA u16 sUnusedScene3Var0 = 0; // Set but never read +static EWRAM_DATA u16 sUnusedScene3Var1 = 0; // Set but never read +static EWRAM_DATA u16 sNidorinoJumpMult = 0; +static EWRAM_DATA u16 sNidorinoAnimDelayTime = 0; +static EWRAM_DATA u16 sNidorinoJumpDiv = 0; +static EWRAM_DATA u16 sNidorinoRecoilReturnTime = 0; +static EWRAM_DATA u16 sNidorinoUnusedVar = 0; // Set but never read +static EWRAM_DATA u16 sStarSpeedX = 0; +static EWRAM_DATA u16 sStarSpeedY = 0; +static EWRAM_DATA u16 sStarSparklesXmodMask = 0; +static EWRAM_DATA u16 sStarSparklesUnusedVar = 0; // Set but never read +static EWRAM_DATA u16 sStarSparklesSpawnRate = 0; +static EWRAM_DATA u16 sStarSparklesFlickerStartTime = 0; +static EWRAM_DATA u16 sStarSparklesDestroySpriteTime = 0; +static EWRAM_DATA u16 sStarSparklesGravityShift = 0; +static EWRAM_DATA u16 sStarSparklesXspeed = 0; +static EWRAM_DATA u16 sStarSparklesYspeed = 0; +static EWRAM_DATA u16 sStarSparklesXprecision = 0; +static EWRAM_DATA u16 sStarSparklesYprecision = 0; +// General static void CB2_SetUpIntro(void); static void CB2_Intro(void); static void VBlankCB_Intro(void); @@ -145,67 +179,76 @@ static void StartIntroSequence(void); static void Task_CallIntroCallback(u8 taskId); static void SetIntroCB(struct IntroSequenceData * ptr, IntroCallback cb); static void IntroCB_Init(struct IntroSequenceData * ptr); -static void IntroCB_OpenWin1ToTheaterDimensions(struct IntroSequenceData * ptr); -static void IntroCB_GameFreakStar(struct IntroSequenceData * ptr); -static void IntroCB_GameFreakScene_RevealGameFreakText(struct IntroSequenceData * ptr); -static void IntroCB_GameFreakScene_CreateGameFreakLogo(struct IntroSequenceData * ptr); -static void IntroCB_FightScene(struct IntroSequenceData * ptr); -static void Task_FightScene1_GrassyFieldAnim(u8 taskId); -static void FightScene1_SignalEndGrassyFieldAnim(void); -static void Task_FightScene1_ZoomEffect(u8 taskId); -static void IntroCB_FightScene2(struct IntroSequenceData * ptr); -static void Task_FightScene2_CameraHorizPanEffect(u8 taskId); -static void Task_FightScene2_CameraVertPanEffect(u8 taskId); -static void CreateMonStaticSprites(struct IntroSequenceData * ptr); -static void DestroyStaticMonSprites(struct IntroSequenceData * ptr); -static void IntroCB_FightScene3(struct IntroSequenceData * ptr); -static void FightScene3_StartBg1Scroll(void); -static void Task_FightScene3_ForestBgScroll(u8 taskId); -static void CreateGrassSprite(struct IntroSequenceData * ptr); +static void LoadFightSceneSpriteGraphics(void); +static void IntroCB_ExitToTitleScreen(struct IntroSequenceData * ptr); + +// GF scene +static void IntroCB_GF_OpenWindow(struct IntroSequenceData * ptr); +static void IntroCB_GF_Star(struct IntroSequenceData * ptr); +static void IntroCB_GF_RevealName(struct IntroSequenceData * ptr); +static void IntroCB_GF_RevealLogo(struct IntroSequenceData * ptr); +static void GFScene_LoadGfxCreateStar(void); +static void GFScene_StartNameSparklesSmall(void); +static void GFScene_StartNameSparklesBig(void); +static void GFScene_Task_NameSparklesSmall(u8 taskId); +static void GFScene_Task_NameSparklesBig(u8 taskId); +static struct Sprite *GFScene_CreateLogoSprite(void); +static void GFScene_CreatePresentsSprite(void); +static void SpriteCB_Star(struct Sprite *sprite); +static void SpriteCB_SparklesSmall_Star(struct Sprite *sprite); +static void SpriteCB_SparklesSmall_Name(struct Sprite *sprite); +static void SpriteCB_SparklesBig(struct Sprite *sprite); + +// Scene 1 +static void IntroCB_Scene1(struct IntroSequenceData * ptr); +static void Scene1_Task_AnimateGrass(u8 taskId); +static void Scene1_StartGrassScrolling(void); +static void Scene1_Task_BgZoom(u8 taskId); + +// Scene 2 +static void IntroCB_Scene2(struct IntroSequenceData * ptr); +static void Scene2_Task_PanForest(u8 taskId); +static void Scene2_Task_PanMons(u8 taskId); +static void Scene2_CreateMonSprites(struct IntroSequenceData * ptr); +static void Scene2_DestroyMonSprites(struct IntroSequenceData * ptr); + +// Scene 3 +static void IntroCB_Scene3_Entrance(struct IntroSequenceData * ptr); +static void IntroCB_Scene3_Fight(struct IntroSequenceData * ptr); +static void Scene3_StartBgScroll(void); +static void Scene3_Task_GengarBounce(u8 taskId); +static void Scene3_CreateGrassSprite(struct IntroSequenceData * ptr); +static void Scene3_CreateGengarSprite(struct IntroSequenceData * ptr); +static void Scene3_StartNidorinoCry(struct IntroSequenceData * ptr); +static void Scene3_StartNidorinoHop(struct Sprite *sprite, u16 time, s16 targetX, u8 heightShift); +static void Scene3_StartGengarAttack(struct IntroSequenceData * ptr); +static void Scene3_Task_GengarAttack(u8 taskId); +static void Scene3_NidorinoZoom(struct IntroSequenceData * ptr); +static void Scene3_GengarZoom(struct IntroSequenceData * ptr); +static void Scene3_CreateGengarSwipeSprites(void); +static void Scene3_Task_GengarEnter(u8 taskId); +static void Scene3_CreateNidorinoSprite(struct IntroSequenceData * ptr); +static void Scene3_StartNidorinoEntrance(struct Sprite *sprite, s16 xStart, s16 xEnd, u16 speed); +static void Scene3_SpriteCB_NidorinoEnter(struct Sprite *sprite); +static bool32 Scene3_IsNidorinoEntering(struct IntroSequenceData * ptr); +static void Scene3_StartNidorinoRecoil(struct IntroSequenceData * ptr); +static bool8 Scene3_NidorinoAnimIsRunning(struct IntroSequenceData * ptr); +static void CreateNidorinoRecoilDustSprites(s16 x, s16 y, s16 seed); +static void Scene3_StartNidorinoAttack(struct IntroSequenceData * ptr); static void SpriteCB_Grass(struct Sprite *sprite); -static void IntroCB_FightScene4(struct IntroSequenceData * ptr); -static void CreateGengarBackSprite(struct IntroSequenceData * ptr); -static void FightScene4_StartNidorinoAffineAnim(struct IntroSequenceData * ptr); -static void FightScene4_StartGengarAffineAnim(struct IntroSequenceData * ptr); -static void IntroCB_CleanUp(struct IntroSequenceData * ptr); -static void GameFreakScene_LoadGfxCreateStar(void); -static void GameFreakScene_StartTrailingSparkleSpawner(void); -static void Task_GameFreakScene_TrailingSparkleSpawner(u8 taskId); -static void GameFreakScene_StartRevealGameFreakTextSparklesSpawner(void); -static void Task_RevealGameFreakTextSparklesSpawner(u8 taskId); -static struct Sprite *CreateGameFreakLogoArtSprite(void); -static void FightScene4_StartGengarAttack(struct IntroSequenceData * ptr); -static void Task_FightScene4_GengarAttack(u8 taskId); -static void FightScene4_CreateGengarSwipeSprites(void); static void SpriteCB_GengarSwipe(struct Sprite *sprite); -static void Task_FightScene3_Bg0Scroll(u8 taskId); -static void SpriteCB_LargeStar(struct Sprite *sprite); -static void SpriteCB_SparklesSmall(struct Sprite *sprite); -static void SpriteCB_SparklesSmall2(struct Sprite *sprite); -static void SpriteCB_RevealGameFreakTextSparkles(struct Sprite *sprite); -static void CreateNidorinoAnimSprite(struct IntroSequenceData * ptr); -static void StartNidorinoAnimSpriteSlideIn(struct Sprite *sprite, s16 x0, s16 x1, u16 speed); -static void SpriteCB_NidorinoAnimSpriteSlideIn(struct Sprite *sprite); -static bool32 IsNidorinoAnimSpriteSlideInRunning(struct IntroSequenceData * ptr); -static void FightScene4_NidorinoRearsUp(struct IntroSequenceData * ptr); -static void SpriteCB_NidorinoRearsUp(struct Sprite *sprite); -static void FightScene4_StartNidorinoRecoilAnim(struct IntroSequenceData * ptr); +static void SpriteCB_RecoilDust(struct Sprite *sprite); +static void SpriteCB_NidorinoCry(struct Sprite *sprite); static void SpriteCB_NidorinoRecoil(struct Sprite *sprite); -static bool8 FightScene4_NidorinoAnimIsRunning(struct IntroSequenceData * ptr); -static void CreateNidorinoRecoilDustSprites(s16 a1, s16 a2, s16 a3); -static void SpriteCB_NidorinoRecoilDust(struct Sprite *sprite); -static void StartSpriteHopToPosAnim(struct Sprite *sprite, u16 a1, s16 a2, u8 a3); -static void SpriteCB_HopToPos(struct Sprite *sprite); -static void StartNidorinoAnim_LaunchSelfAtGengarAnim(struct IntroSequenceData * ptr); -static void SpriteCB_NidorinoAnim_LaunchSelfAtGengar(struct Sprite *sprite); -static void LoadFightSceneSpriteTilesAndPals(void); -static void CreateGameFreakPresentsText(void); +static void SpriteCB_NidorinoHop(struct Sprite *sprite); +static void SpriteCB_NidorinoAttack(struct Sprite *sprite); extern const u32 gMultiBootProgram_PokemonColosseum_Start[]; +extern const u32 gMultiBootProgram_PokemonColosseum_End[]; static const u16 sCopyright_Pal[] = INCBIN_U16("graphics/intro/copyright.gbapal"); -static const u8 sCopyright_Gfx[] = INCBIN_U8("graphics/intro/copyright.4bpp.lz"); -static const u8 sCopyright_Map[] = INCBIN_U8("graphics/intro/copyright.bin.lz"); +static const u8 sCopyright_Gfx[] = INCBIN_U8( "graphics/intro/copyright.4bpp.lz"); +static const u8 sCopyright_Map[] = INCBIN_U8( "graphics/intro/copyright.bin.lz"); // Game Freak static const u16 sGameFreakBg_Pal[] = INCBIN_U16("graphics/intro/game_freak/bg.gbapal"); @@ -243,7 +286,7 @@ static const u16 sScene2_NidorinoClose_Pal[] = INCBIN_U16("graphics/intro/scene_ static const u8 sScene2_NidorinoClose_Gfx[] = INCBIN_U8( "graphics/intro/scene_2/nidorino_close.4bpp.lz"); static const u8 sScene2_NidorinoClose_Map[] = INCBIN_U8( "graphics/intro/scene_2/nidorino_close.bin.lz"); static const u16 sScene3_Bg_Pal[] = INCBIN_U16("graphics/intro/scene_3/bg.gbapal"); -static const u8 sScene3_Bg_Gfx[] = INCBIN_U8( "graphics/intro/scene_3/bg.4bpp.lz"); // give -width 16 +static const u8 sScene3_Bg_Gfx[] = INCBIN_U8( "graphics/intro/scene_3/bg.4bpp.lz"); static const u8 sScene3_Bg_Map[] = INCBIN_U8( "graphics/intro/scene_3/bg.bin.lz"); static const u8 sScene3_GengarAnim_Gfx[] = INCBIN_U8( "graphics/intro/scene_3/gengar_anim.4bpp.lz"); static const u8 sScene3_GengarAnim_Map[] = INCBIN_U8( "graphics/intro/scene_3/gengar_anim.bin.lz"); @@ -355,8 +398,8 @@ static const struct BgTemplate sBgTemplates_Scene3[] = { } }; -static const struct WindowTemplate sWindowTemplate_GameFreakTextLogo[] = { - { +static const struct WindowTemplate sWindowTemplates[WIN_COUNT + 1] = { + [WIN_GF_TEXT_LOGO] = { .bg = BG_GF_TEXT_LOGO, .tilemapLeft = 6, .tilemapTop = 4, @@ -364,14 +407,15 @@ static const struct WindowTemplate sWindowTemplate_GameFreakTextLogo[] = { .height = 9, .paletteNum = 0xD, .baseBlock = 0x000 - }, DUMMY_WIN_TEMPLATE + }, + [WIN_COUNT] = DUMMY_WIN_TEMPLATE }; -static const u8 sGengarBackSpritePos2UpdateMods[NUM_GENGAR_BACK_SPRITES][2] = { - {0x3f, 0x3f}, - {0x00, 0x3f}, - {0x3f, 0x00}, - {0x00, 0x00} +static const u8 sGengarZoomMatrixAnchors[NUM_GENGAR_BACK_SPRITES][2] = { + {63, 63}, + { 0, 63}, + {63, 0}, + { 0, 0} }; static const struct CompressedSpriteSheet sSpriteSheets_GameFreakScene[] = { @@ -389,7 +433,7 @@ static const struct SpritePalette sSpritePalettes_GameFreakScene[] = { {0} }; -static const struct Coords16 sTrailingSparkleCoords[] = { +static const struct Coords16 sTextSparkleCoords[] = { { 72, 80}, {136, 74}, {168, 80}, @@ -406,9 +450,9 @@ static const struct OamData sOam_Star = { .objMode = ST_OAM_OBJ_NORMAL, .mosaic = FALSE, .bpp = ST_OAM_4BPP, - .shape = ST_OAM_SQUARE, + .shape = SPRITE_SHAPE(16x16), .matrixNum = 0, - .size = ST_OAM_SIZE_1, + .size = SPRITE_SIZE(16x16), .tileNum = 0x000, .priority = 2, .paletteNum = 0 @@ -419,15 +463,15 @@ static const struct OamData sOam_SparklesSmall = { .objMode = ST_OAM_OBJ_NORMAL, .mosaic = FALSE, .bpp = ST_OAM_4BPP, - .shape = ST_OAM_SQUARE, + .shape = SPRITE_SHAPE(8x8), .matrixNum = 0, - .size = ST_OAM_SIZE_0, + .size = SPRITE_SIZE(8x8), .tileNum = 0x000, .priority = 2, .paletteNum = 0 }; -static const union AnimCmd sAnim_SparklesSmall_0[] = { +static const union AnimCmd sAnim_SparklesSmall_Loop[] = { ANIMCMD_FRAME(0, 4), ANIMCMD_FRAME(1, 4), ANIMCMD_FRAME(2, 4), @@ -435,7 +479,7 @@ static const union AnimCmd sAnim_SparklesSmall_0[] = { ANIMCMD_JUMP(0) }; -static const union AnimCmd sAnim_SparklesSmall_1[] = { +static const union AnimCmd sAnim_SparklesSmall_Once[] = { ANIMCMD_FRAME(0, 4), ANIMCMD_FRAME(1, 4), ANIMCMD_FRAME(2, 4), @@ -444,8 +488,8 @@ static const union AnimCmd sAnim_SparklesSmall_1[] = { }; static const union AnimCmd *const sAnims_SparklesSmall[] = { - sAnim_SparklesSmall_0, - sAnim_SparklesSmall_1 + [ANIM_SPARKLE_LOOP] = sAnim_SparklesSmall_Loop, + [ANIM_SPARKLE_ONCE] = sAnim_SparklesSmall_Once }; static const struct SpriteTemplate sSpriteTemplate_Star = { @@ -455,7 +499,7 @@ static const struct SpriteTemplate sSpriteTemplate_Star = { .anims = gDummySpriteAnimTable, .images = NULL, .affineAnims = gDummySpriteAffineAnimTable, - .callback = SpriteCB_LargeStar + .callback = SpriteCB_Star }; static const struct SpriteTemplate sSpriteTemplate_SparklesSmall = { @@ -465,7 +509,7 @@ static const struct SpriteTemplate sSpriteTemplate_SparklesSmall = { .anims = sAnims_SparklesSmall, .images = NULL, .affineAnims = gDummySpriteAffineAnimTable, - .callback = SpriteCB_SparklesSmall + .callback = SpriteCB_SparklesSmall_Star }; static const struct OamData sOam_SparklesBig = { @@ -473,9 +517,9 @@ static const struct OamData sOam_SparklesBig = { .objMode = ST_OAM_OBJ_NORMAL, .mosaic = FALSE, .bpp = ST_OAM_4BPP, - .shape = ST_OAM_SQUARE, + .shape = SPRITE_SHAPE(32x32), .matrixNum = 0, - .size = ST_OAM_SIZE_2, + .size = SPRITE_SIZE(32x32), .tileNum = 0x000, .priority = 2, .paletteNum = 0 @@ -500,7 +544,7 @@ static const struct SpriteTemplate sSpriteTemplate_SparklesBig = { .anims = sAnims_SparklesBig, .images = NULL, .affineAnims = gDummySpriteAffineAnimTable, - .callback = SpriteCB_RevealGameFreakTextSparkles + .callback = SpriteCB_SparklesBig }; static const struct OamData sOam_GameFreakLogo = { @@ -508,9 +552,9 @@ static const struct OamData sOam_GameFreakLogo = { .objMode = ST_OAM_OBJ_BLEND, .mosaic = FALSE, .bpp = ST_OAM_4BPP, - .shape = ST_OAM_V_RECTANGLE, + .shape = SPRITE_SHAPE(32x64), .matrixNum = 0, - .size = ST_OAM_SIZE_3, + .size = SPRITE_SIZE(32x64), .tileNum = 0x000, .priority = 3, .paletteNum = 0 @@ -531,15 +575,15 @@ static const struct OamData sOam_PresentsText = { .objMode = ST_OAM_OBJ_BLEND, .mosaic = FALSE, .bpp = ST_OAM_4BPP, - .shape = ST_OAM_H_RECTANGLE, + .shape = SPRITE_SHAPE(32x8), .matrixNum = 0, - .size = ST_OAM_SIZE_1, + .size = SPRITE_SIZE(32x8), .tileNum = 0x000, .priority = 3, .paletteNum = 0 }; -static const struct SpriteTemplate sSpriteTemplate_PresentsText = { +static const struct SpriteTemplate sSpriteTemplate_Presents = { .tileTag = GFXTAG_PRESENTS, .paletteTag = PALTAG_GF, .oam = &sOam_PresentsText, @@ -554,61 +598,61 @@ static const struct OamData sOam_Scene3_Nidorino = { .objMode = ST_OAM_OBJ_NORMAL, .mosaic = FALSE, .bpp = ST_OAM_4BPP, - .shape = ST_OAM_SQUARE, + .shape = SPRITE_SHAPE(64x64), .matrixNum = 0, - .size = ST_OAM_SIZE_3, + .size = SPRITE_SIZE(64x64), .tileNum = 0x000, .priority = 1, .paletteNum = 0 }; -static const union AnimCmd sAnim_Scene3_Nidorino_0[] = { +static const union AnimCmd sAnim_Scene3_Nidorino_Normal[] = { ANIMCMD_FRAME(0, 1), ANIMCMD_END }; -static const union AnimCmd sAnim_Scene3_Nidorino_1[] = { +static const union AnimCmd sAnim_Scene3_Nidorino_Cry[] = { ANIMCMD_FRAME(64, 1), ANIMCMD_END }; -static const union AnimCmd sAnim_Scene3_Nidorino_2[] = { +static const union AnimCmd sAnim_Scene3_Nidorino_Crouch[] = { ANIMCMD_FRAME(128, 1), ANIMCMD_END }; -static const union AnimCmd sAnim_Scene3_Nidorino_3[] = { +static const union AnimCmd sAnim_Scene3_Nidorino_Hop[] = { ANIMCMD_FRAME(192, 1), ANIMCMD_END }; -static const union AnimCmd sAnim_Scene3_Nidorino_4[] = { +static const union AnimCmd sAnim_Scene3_Nidorino_Attack[] = { ANIMCMD_FRAME(256, 1), ANIMCMD_END }; static const union AnimCmd *const sAnims_Scene3_Nidorino[] = { - sAnim_Scene3_Nidorino_0, - sAnim_Scene3_Nidorino_1, - sAnim_Scene3_Nidorino_2, - sAnim_Scene3_Nidorino_3, - sAnim_Scene3_Nidorino_4 + [ANIM_NIDORINO_NORMAL] = sAnim_Scene3_Nidorino_Normal, + [ANIM_NIDORINO_CRY] = sAnim_Scene3_Nidorino_Cry, + [ANIM_NIDORINO_CROUCH] = sAnim_Scene3_Nidorino_Crouch, + [ANIM_NIDORINO_HOP] = sAnim_Scene3_Nidorino_Hop, + [ANIM_NIDORINO_ATTACK] = sAnim_Scene3_Nidorino_Attack }; -static const union AffineAnimCmd sAffineAnim_Scene3_Mons_0[] = { +static const union AffineAnimCmd sAffineAnim_Scene3_Mons_Normal[] = { AFFINEANIMCMD_FRAME(256, 256, 0, 0), AFFINEANIMCMD_END }; -static const union AffineAnimCmd sAffineAnim_Scene3_Mons_1[] = { +static const union AffineAnimCmd sAffineAnim_Scene3_Mons_Zoom[] = { AFFINEANIMCMD_FRAME(256, 256, 0, 0), AFFINEANIMCMD_FRAME(32, 32, 0, 8), AFFINEANIMCMD_END }; static const union AffineAnimCmd *const sAffineAnims_Scene3_Mons[] = { - sAffineAnim_Scene3_Mons_0, - sAffineAnim_Scene3_Mons_1 + [AFFINEANIM_NORMAL] = sAffineAnim_Scene3_Mons_Normal, + [AFFINEANIM_ZOOM] = sAffineAnim_Scene3_Mons_Zoom }; static const struct SpriteTemplate sSpriteTemplate_Scene3_Nidorino = { @@ -626,9 +670,9 @@ static const struct OamData sOam_Scene2_Mons = { .objMode = ST_OAM_OBJ_NORMAL, .mosaic = FALSE, .bpp = ST_OAM_4BPP, - .shape = ST_OAM_SQUARE, + .shape = SPRITE_SHAPE(64x64), .matrixNum = 0, - .size = ST_OAM_SIZE_3, + .size = SPRITE_SIZE(64x64), .tileNum = 0x000, .priority = 1, .paletteNum = 0 @@ -659,28 +703,28 @@ static const struct OamData sOam_Grass = { .objMode = ST_OAM_OBJ_NORMAL, .mosaic = FALSE, .bpp = ST_OAM_4BPP, - .shape = ST_OAM_H_RECTANGLE, + .shape = SPRITE_SHAPE(64x32), .matrixNum = 0, - .size = ST_OAM_SIZE_3, + .size = SPRITE_SIZE(64x32), .tileNum = 0x000, .priority = 0, .paletteNum = 0 }; -static const union AnimCmd sAnim_Grass_0[] = { +static const union AnimCmd sAnim_Grass_Static[] = { ANIMCMD_FRAME(0, 0), ANIMCMD_END }; -static const union AnimCmd sAnim_Grass_1[] = { +static const union AnimCmd sAnim_Grass_Rustle[] = { ANIMCMD_FRAME(32, 4), ANIMCMD_FRAME(0, 4), ANIMCMD_END }; static const union AnimCmd *const sAnims_Grass[] = { - sAnim_Grass_0, - sAnim_Grass_1 + sAnim_Grass_Static, + sAnim_Grass_Rustle // Unused }; static const struct SpriteTemplate sSpriteTemplate_Grass = { @@ -698,39 +742,39 @@ static const struct OamData sOam_Scene3_Gengar = { .objMode = ST_OAM_OBJ_NORMAL, .mosaic = FALSE, .bpp = ST_OAM_4BPP, - .shape = ST_OAM_SQUARE, + .shape = SPRITE_SHAPE(64x64), .matrixNum = 0, - .size = ST_OAM_SIZE_3, + .size = SPRITE_SIZE(64x64), .tileNum = 0x000, .priority = 1, .paletteNum = 0 }; -static const union AnimCmd sAnim_Scene3_Gengar_0[] = { +static const union AnimCmd sAnim_Scene3_Gengar_TopLeft[] = { ANIMCMD_FRAME(0, 0), ANIMCMD_END }; -static const union AnimCmd sAnim_Scene3_Gengar_1[] = { +static const union AnimCmd sAnim_Scene3_Gengar_TopRight[] = { ANIMCMD_FRAME(64, 0), ANIMCMD_END }; -static const union AnimCmd sAnim_Scene3_Gengar_2[] = { +static const union AnimCmd sAnim_Scene3_Gengar_BottomLeft[] = { ANIMCMD_FRAME(96, 0), ANIMCMD_END }; -static const union AnimCmd sAnim_Scene3_Gengar_3[] = { +static const union AnimCmd sAnim_Scene3_Gengar_BottomRight[] = { ANIMCMD_FRAME(160, 0), ANIMCMD_END }; static const union AnimCmd *const sAnims_Scene3_Gengar[NUM_GENGAR_BACK_SPRITES] = { - sAnim_Scene3_Gengar_0, - sAnim_Scene3_Gengar_1, - sAnim_Scene3_Gengar_2, - sAnim_Scene3_Gengar_3 + sAnim_Scene3_Gengar_TopLeft, + sAnim_Scene3_Gengar_TopRight, + sAnim_Scene3_Gengar_BottomLeft, + sAnim_Scene3_Gengar_BottomRight }; static const struct SpriteTemplate sSpriteTemplate_Scene3_Gengar = { @@ -748,29 +792,29 @@ static const struct OamData sOam_Swipe = { .objMode = ST_OAM_OBJ_NORMAL, .mosaic = FALSE, .bpp = ST_OAM_4BPP, - .shape = ST_OAM_V_RECTANGLE, + .shape = SPRITE_SHAPE(32x64), .matrixNum = 0, - .size = ST_OAM_SIZE_3, + .size = SPRITE_SIZE(32x64), .tileNum = 0x000, .priority = 1, .paletteNum = 0 }; -static const union AnimCmd sAnim_Swipe_0[] = { +static const union AnimCmd sAnim_Swipe_Top[] = { ANIMCMD_FRAME(0, 8), ANIMCMD_FRAME(32, 4), ANIMCMD_END }; -static const union AnimCmd sAnim_Swipe_1[] = { +static const union AnimCmd sAnim_Swipe_Bottom[] = { ANIMCMD_FRAME(64, 8), ANIMCMD_FRAME(72, 4), ANIMCMD_END }; static const union AnimCmd *const sAnims_Swipe[] = { - sAnim_Swipe_0, - sAnim_Swipe_1 + [ANIM_SWIPE_TOP] = sAnim_Swipe_Top, + [ANIM_SWIPE_BOTTOM] = sAnim_Swipe_Bottom }; static const struct SpriteTemplate sSpriteTemplate_GengarSwipe = { @@ -788,9 +832,9 @@ static const struct OamData sOam_RecoilDust = { .objMode = ST_OAM_OBJ_NORMAL, .mosaic = FALSE, .bpp = ST_OAM_4BPP, - .shape = ST_OAM_SQUARE, + .shape = SPRITE_SHAPE(16x16), .matrixNum = 0, - .size = ST_OAM_SIZE_1, + .size = SPRITE_SIZE(16x16), .tileNum = 0x000, .priority = 1, .paletteNum = 0 @@ -815,7 +859,7 @@ static const struct SpriteTemplate sSpriteTemplate_NidorinoRecoilDust = { .anims = sAnims_RecoilDust, .images = NULL, .affineAnims = gDummySpriteAffineAnimTable, - .callback = SpriteCB_NidorinoRecoilDust + .callback = SpriteCB_RecoilDust }; static const struct CompressedSpriteSheet sFightSceneSpriteSheets[] = { @@ -829,15 +873,15 @@ static const struct CompressedSpriteSheet sFightSceneSpriteSheets[] = { }; // POTENTIAL UB -// This array is passed to LoadSpritePalettes in LoadFightSceneSpriteTilesAndPals. +// This array is passed to LoadSpritePalettes in LoadFightSceneSpriteGraphics. // LoadSpritePalettes uses a {0} entry to signal end of array. // Because such an entry is absent in this case, the function // continues reading into the next .rodata section. static const struct SpritePalette sFightSceneSpritePalettes[] = { - {sGengar_Pal, PALTAG_GENGAR}, - {sNidorino_Pal, PALTAG_NIDORINO}, - {sScene3_Grass_Pal, PALTAG_SCENE3_GRASS}, - {sScene3_Swipe_Pal, PALTAG_SCENE3_SWIPE}, + {sGengar_Pal, PALTAG_GENGAR}, + {sNidorino_Pal, PALTAG_NIDORINO}, + {sScene3_Grass_Pal, PALTAG_SCENE3_GRASS}, + {sScene3_Swipe_Pal, PALTAG_SCENE3_SWIPE}, {sScene3_RecoilDust_Pal, PALTAG_SCENE3_RECOIL_DUST}, #ifdef BUGFIX {0} @@ -861,7 +905,7 @@ static void LoadCopyrightGraphics(u16 charBase, u16 screenBase, u16 palOffset) { LZ77UnCompVram(sCopyright_Gfx, (void *)BG_VRAM + charBase); LZ77UnCompVram(sCopyright_Map, (void *)BG_VRAM + screenBase); - LoadPalette(sCopyright_Pal, palOffset, 0x20); + LoadPalette(sCopyright_Pal, palOffset, sizeof(sCopyright_Pal)); } static void SerialCB_CopyrightScreen(void) @@ -983,13 +1027,13 @@ static void CB2_SetUpIntro(void) DmaFill16(3, 0, PLTT, PLTT_SIZE); FillPalette(RGB_BLACK, 0, 0x400); ResetBgsAndClearDma3BusyFlags(FALSE); - InitBgsFromTemplates(0, sBgTemplates_GameFreakScene, NELEMS(sBgTemplates_GameFreakScene)); + InitBgsFromTemplates(0, sBgTemplates_GameFreakScene, ARRAY_COUNT(sBgTemplates_GameFreakScene)); break; case 1: - LoadPalette(sGameFreakBg_Pal, 0x00, 0x20); + LoadPalette(sGameFreakBg_Pal, 0x00, sizeof(sGameFreakBg_Pal)); DecompressAndCopyTileDataToVram(BG_GF_BACKGROUND, sGameFreakBg_Gfx, 0, 0, 0); DecompressAndCopyTileDataToVram(BG_GF_BACKGROUND, sGameFreakBg_Map, 0, 0, 1); - LoadPalette(sGameFreakLogo_Pal, 0xD0, 0x20); + LoadPalette(sGameFreakLogo_Pal, 0xD0, sizeof(sGameFreakLogo_Pal)); break; case 2: if (!FreeTempTileDataBuffersIfPossible()) @@ -1038,7 +1082,7 @@ static void Intro_ResetGpuRegs(void) static void StartIntroSequence(void) { - struct IntroSequenceData * ptr = Alloc(sizeof(struct IntroSequenceData)); + struct IntroSequenceData * ptr = Alloc(sizeof(*ptr)); SetIntroCB(ptr, IntroCB_Init); ptr->taskId = CreateTask(Task_CallIntroCallback, 3); SetWordTaskArg(ptr->taskId, 0, (uintptr_t)ptr); @@ -1053,8 +1097,11 @@ static void SetIntroCB(struct IntroSequenceData * ptr, IntroCallback cb) static void Task_CallIntroCallback(u8 taskId) { struct IntroSequenceData * ptr = (void *)GetWordTaskArg(taskId, 0); - if (JOY_NEW(A_BUTTON | START_BUTTON | SELECT_BUTTON) && ptr->callback != IntroCB_CleanUp) - SetIntroCB(ptr, IntroCB_CleanUp); + + // End intro early if player presses A/Start/Select + if (JOY_NEW(A_BUTTON | START_BUTTON | SELECT_BUTTON) && ptr->callback != IntroCB_ExitToTitleScreen) + SetIntroCB(ptr, IntroCB_ExitToTitleScreen); + ptr->callback(ptr); } @@ -1063,138 +1110,138 @@ static void IntroCB_Init(struct IntroSequenceData * this) switch (this->state) { case 0: - InitWindows(sWindowTemplate_GameFreakTextLogo); + InitWindows(sWindowTemplates); LZ77UnCompWram(sGameFreakText_Gfx, this->gameFreakTextGfx); LZ77UnCompWram(sGameFreakLogo_Gfx, this->gameFreakLogoGfx); FillBgTilemapBufferRect(BG_GF_TEXT_LOGO, 0x000, 0, 0, 32, 32, 0x11); - FillWindowPixelBuffer(0, PIXEL_FILL(0)); - BlitBitmapToWindow(0, this->gameFreakTextGfx, 0, 40, 144, 16); - PutWindowTilemap(0); - CopyWindowToVram(0, COPYWIN_FULL); + FillWindowPixelBuffer(WIN_GF_TEXT_LOGO, PIXEL_FILL(0)); + BlitBitmapToWindow(WIN_GF_TEXT_LOGO, this->gameFreakTextGfx, 0, 40, 144, 16); + PutWindowTilemap(WIN_GF_TEXT_LOGO); + CopyWindowToVram(WIN_GF_TEXT_LOGO, COPYWIN_FULL); this->state++; break; case 1: if (!IsDma3ManagerBusyWithBgCopy()) - SetIntroCB(this, IntroCB_OpenWin1ToTheaterDimensions); + SetIntroCB(this, IntroCB_GF_OpenWindow); break; } } -static void IntroCB_OpenWin1ToTheaterDimensions(struct IntroSequenceData * this) +static void IntroCB_GF_OpenWindow(struct IntroSequenceData * this) { switch (this->state) { case 0: SetGpuRegBits(REG_OFFSET_DISPCNT, DISPCNT_WIN1_ON); - SetGpuReg(REG_OFFSET_WININ, 0x3F00); - SetGpuReg(REG_OFFSET_WINOUT, 0x0000); - SetGpuReg(REG_OFFSET_WIN1H, 0x00F0); - SetGpuReg(REG_OFFSET_WIN1V, 0x0000); - this->data[5] = 0; + SetGpuReg(REG_OFFSET_WININ, WININ_WIN1_ALL); + SetGpuReg(REG_OFFSET_WINOUT, 0); + SetGpuReg(REG_OFFSET_WIN1H, DISPLAY_WIDTH); + SetGpuReg(REG_OFFSET_WIN1V, 0); + this->timer = 0; this->state++; break; case 1: - ShowBg(3); - BlendPalettes(PALETTES_ALL, 0x00, RGB_BLACK); + ShowBg(BG_GF_BACKGROUND); + BlendPalettes(PALETTES_ALL, 0, RGB_BLACK); this->state++; break; case 2: - this->data[5] += 8; - if (this->data[5] >= 0x30) - this->data[5] = 0x30; - SetGpuReg(REG_OFFSET_WIN1V, ((0x50 - this->data[5]) << 8) | (0x50 + this->data[5])); - if (this->data[5] == 0x30) - SetIntroCB(this, IntroCB_GameFreakStar); + // Extend window height in both directions from midpoint until it reaches a narrow "theatric" view + this->timer += 8; + if (this->timer >= 48) + this->timer = 48; + SetGpuReg(REG_OFFSET_WIN1V, WIN_RANGE(DISPLAY_HEIGHT / 2 - this->timer, DISPLAY_HEIGHT / 2 + this->timer)); + if (this->timer == 48) + SetIntroCB(this, IntroCB_GF_Star); break; } } -static void IntroCB_GameFreakStar(struct IntroSequenceData * this) +static void IntroCB_GF_Star(struct IntroSequenceData * this) { switch (this->state) { case 0: PlaySE(MUS_GAME_FREAK); - GameFreakScene_LoadGfxCreateStar(); - this->data[5] = 0; + GFScene_LoadGfxCreateStar(); + this->timer = 0; this->state++; break; case 1: - this->data[5]++; - if (this->data[5] == 30) + if (++this->timer == 30) { - GameFreakScene_StartTrailingSparkleSpawner(); - this->data[5] = 0; + GFScene_StartNameSparklesSmall(); + this->timer = 0; this->state++; } break; case 2: - this->data[5]++; - if (this->data[5] == 90) - SetIntroCB(this, IntroCB_GameFreakScene_RevealGameFreakText); + this->timer++; + if (this->timer == 90) + SetIntroCB(this, IntroCB_GF_RevealName); break; } } -static void IntroCB_GameFreakScene_RevealGameFreakText(struct IntroSequenceData * this) +static void IntroCB_GF_RevealName(struct IntroSequenceData * this) { switch (this->state) { case 0: - GameFreakScene_StartRevealGameFreakTextSparklesSpawner(); - this->data[5] = 0; + GFScene_StartNameSparklesBig(); + this->timer = 0; this->state++; break; case 1: - if (++this->data[5] >= 40) + if (++this->timer >= 40) this->state++; break; case 2: - SetGpuReg(REG_OFFSET_BLDCNT, BLDCNT_TGT1_BG2 | BLDCNT_EFFECT_BLEND | BLDCNT_TGT2_BG0 | BLDCNT_TGT2_BG1 | BLDCNT_TGT2_BG2 | BLDCNT_TGT2_BG3 | BLDCNT_TGT2_OBJ | BLDCNT_TGT2_BD); + SetGpuReg(REG_OFFSET_BLDCNT, BLDCNT_TGT1_BG2 | BLDCNT_EFFECT_BLEND | BLDCNT_TGT2_ALL); StartBlendTask(0, 16, 16, 0, 48, 0); this->state++; break; case 3: - ShowBg(2); + ShowBg(BG_GF_TEXT_LOGO); this->state++; break; case 4: if (!IsBlendTaskActive()) { SetGpuReg(REG_OFFSET_BLDCNT, 0); - this->data[5] = 0; + this->timer = 0; this->state++; } break; case 5: - if (++this->data[5] > 50) - SetIntroCB(this, IntroCB_GameFreakScene_CreateGameFreakLogo); + if (++this->timer > 50) + SetIntroCB(this, IntroCB_GF_RevealLogo); break; } } -static void IntroCB_GameFreakScene_CreateGameFreakLogo(struct IntroSequenceData * this) +static void IntroCB_GF_RevealLogo(struct IntroSequenceData * this) { switch (this->state) { case 0: - SetGpuReg(REG_OFFSET_BLDCNT, BLDCNT_TGT1_OBJ | BLDCNT_EFFECT_BLEND | BLDCNT_TGT2_BG0 | BLDCNT_TGT2_BG1 | BLDCNT_TGT2_BG2 | BLDCNT_TGT2_BG3 | BLDCNT_TGT2_OBJ | BLDCNT_TGT2_BD); + SetGpuReg(REG_OFFSET_BLDCNT, BLDCNT_TGT1_OBJ | BLDCNT_EFFECT_BLEND | BLDCNT_TGT2_ALL); StartBlendTask(0, 16, 16, 0, 16, 0); - this->data[0] = 0; - this->data[1] = 16; - this->data[5] = 0; + this->data[0] = 0; // Never read + this->data[1] = 16; // Never read + this->timer = 0; this->state++; break; case 1: - this->gameFreakLogoArtSprite = CreateGameFreakLogoArtSprite(); + this->gameFreakLogoArtSprite = GFScene_CreateLogoSprite(); this->state++; break; case 2: if (!IsBlendTaskActive()) { - BlitBitmapToWindow(0, this->gameFreakLogoGfx, 0x38, 0x06, 0x20, 0x40); - BlitBitmapToWindow(0, this->gameFreakTextGfx, 0x00, 0x28, 0x90, 0x10); - CopyWindowToVram(0, COPYWIN_GFX); + BlitBitmapToWindow(WIN_GF_TEXT_LOGO, this->gameFreakLogoGfx, 0x38, 0x06, 0x20, 0x40); + BlitBitmapToWindow(WIN_GF_TEXT_LOGO, this->gameFreakTextGfx, 0x00, 0x28, 0x90, 0x10); + CopyWindowToVram(WIN_GF_TEXT_LOGO, COPYWIN_GFX); this->state++; } break; @@ -1203,14 +1250,14 @@ static void IntroCB_GameFreakScene_CreateGameFreakLogo(struct IntroSequenceData { DestroySprite(this->gameFreakLogoArtSprite); #if REVISION >= 1 - CreateGameFreakPresentsText(); + GFScene_CreatePresentsSprite(); #endif - this->data[5] = 0; + this->timer = 0; this->state++; } break; case 4: - if (++this->data[5] > 90) + if (++this->timer > 90) { SetGpuRegBits(REG_OFFSET_BLDCNT, BLDCNT_TGT1_BG2); StartBlendTask(16, 0, 0, 16, 20, 0); @@ -1227,36 +1274,36 @@ static void IntroCB_GameFreakScene_CreateGameFreakLogo(struct IntroSequenceData case 6: ResetSpriteData(); FreeAllSpritePalettes(); - this->data[5] = 0; + this->timer = 0; this->state++; break; case 7: - if (++this->data[5] > 20) + if (++this->timer > 20) { SetGpuReg(REG_OFFSET_BLDCNT, 0); - SetIntroCB(this, IntroCB_FightScene); + SetIntroCB(this, IntroCB_Scene1); } break; } } -static void IntroCB_FightScene(struct IntroSequenceData * this) +static void IntroCB_Scene1(struct IntroSequenceData * this) { switch (this->state) { case 0: SetVBlankCallback(NULL); - LoadPalette(sScene1_Grass_Pal, 0x10, 0x20); - LoadPalette(sScene1_Bg_Pal, 0x20, 0x20); - BlendPalettes(0x00000006, 0x10, RGB_WHITE); - InitBgsFromTemplates(0, sBgTemplates_Scene1, NELEMS(sBgTemplates_Scene1)); + LoadPalette(sScene1_Grass_Pal, 16 * PALSLOT_SCENE1_GRASS, sizeof(sScene1_Grass_Pal)); + LoadPalette(sScene1_Bg_Pal, 16 * PALSLOT_SCENE1_BG, sizeof(sScene1_Bg_Pal)); + BlendPalettes((1 << PALSLOT_SCENE1_GRASS) | (1 << PALSLOT_SCENE1_BG), 16, RGB_WHITE); + InitBgsFromTemplates(0, sBgTemplates_Scene1, ARRAY_COUNT(sBgTemplates_Scene1)); DecompressAndCopyTileDataToVram(BG_SCENE1_BACKGROUND, sScene1_Bg_Gfx, 0, 0, 0); DecompressAndCopyTileDataToVram(BG_SCENE1_BACKGROUND, sScene1_Bg_Map, 0, 0, 1); ShowBg(BG_SCENE1_BACKGROUND); HideBg(BG_SCENE1_GRASS); HideBg(BG_SCENE1_UNUSED1); HideBg(BG_SCENE1_UNUSED2); - LoadFightSceneSpriteTilesAndPals(); + LoadFightSceneSpriteGraphics(); SetVBlankCallback(VBlankCB_Intro); this->state++; break; @@ -1274,8 +1321,8 @@ static void IntroCB_FightScene(struct IntroSequenceData * this) if (!FreeTempTileDataBuffersIfPossible()) { ShowBg(BG_SCENE1_GRASS); - CreateTask(Task_FightScene1_GrassyFieldAnim, 0); - BeginNormalPaletteFade(0x00000006, -2, 16, 0, RGB_WHITE); + CreateTask(Scene1_Task_AnimateGrass, 0); + BeginNormalPaletteFade((1 << PALSLOT_SCENE1_GRASS) | (1 << PALSLOT_SCENE1_BG), -2, 16, 0, RGB_WHITE); this->state++; } break; @@ -1283,80 +1330,106 @@ static void IntroCB_FightScene(struct IntroSequenceData * this) if (!gPaletteFade.active) { m4aSongNumStart(MUS_INTRO_FIGHT); - this->data[5] = 0; + this->timer = 0; this->state++; } break; case 4: - this->data[5]++; - if (this->data[5] == 20) + if (++this->timer == 20) { - CreateTask(Task_FightScene1_ZoomEffect, 0); - FightScene1_SignalEndGrassyFieldAnim(); + // Start animation for transitioning to the next scene + CreateTask(Scene1_Task_BgZoom, 0); + Scene1_StartGrassScrolling(); } - if (this->data[5] >= 30) + if (this->timer >= 30) { - BlendPalettes(0xFFFFFFFE, 16, RGB_WHITE); - DestroyTask(FindTaskIdByFunc(Task_FightScene1_GrassyFieldAnim)); - DestroyTask(FindTaskIdByFunc(Task_FightScene1_ZoomEffect)); - SetIntroCB(this, IntroCB_FightScene2); + // End scene + BlendPalettes(PALETTES_ALL & ~1, 16, RGB_WHITE); + DestroyTask(FindTaskIdByFunc(Scene1_Task_AnimateGrass)); + DestroyTask(FindTaskIdByFunc(Scene1_Task_BgZoom)); + SetIntroCB(this, IntroCB_Scene2); } break; case 5: + // Never reached if (!gPaletteFade.active) { - DestroyTask(FindTaskIdByFunc(Task_FightScene1_GrassyFieldAnim)); - DestroyTask(FindTaskIdByFunc(Task_FightScene1_ZoomEffect)); - SetIntroCB(this, IntroCB_FightScene2); + DestroyTask(FindTaskIdByFunc(Scene1_Task_AnimateGrass)); + DestroyTask(FindTaskIdByFunc(Scene1_Task_BgZoom)); + SetIntroCB(this, IntroCB_Scene2); } break; } } -static void Task_FightScene1_GrassyFieldAnim(u8 taskId) +#define tTimer data[0] +#define tFrame data[1] +#define tExiting data[2] +#define tScroll data[3] + +static void Scene1_Task_AnimateGrass(u8 taskId) { s16 * data = gTasks[taskId].data; - if (++data[0] > 5) + // Each of the 3 frames of the bg grass animation is separated vertically on the tilemap. + // The conditional below changes the frame by setting the y coordinate of the bg. + if (++tTimer > 5) { - data[0] = 0; - if (++data[1] > 2) - data[1] = 0; - ChangeBgY(BG_SCENE1_GRASS, data[1] << 15, 0); + tTimer = 0; + if (++tFrame >= 3) + tFrame = 0; + ChangeBgY(BG_SCENE1_GRASS, tFrame << 15, BG_COORD_SET); } - if (data[2]) + + // When it's time to progress to the next scene, the grass is meant to scroll downward offscreen. + // This scrolling is overwritten by the coord change above, and so the grass "stutters" back upward. + // They don't mask the bg, so if it were to continue scrolling offscreen it would reveal the frame above on the tilemap. + if (tExiting) { - data[3] += 0x120; - ChangeBgY(BG_SCENE1_GRASS, data[3], 2); + tScroll += 0x120; + ChangeBgY(BG_SCENE1_GRASS, tScroll, BG_COORD_SUB); } } -static void FightScene1_SignalEndGrassyFieldAnim(void) +static void Scene1_StartGrassScrolling(void) { - u8 taskId = FindTaskIdByFunc(Task_FightScene1_GrassyFieldAnim); - gTasks[taskId].data[2] = TRUE; + u8 taskId = FindTaskIdByFunc(Scene1_Task_AnimateGrass); + gTasks[taskId].tExiting = TRUE; } -static void Task_FightScene1_ZoomEffect(u8 taskId) +#undef tTimer +#undef tFrame +#undef tExiting +#undef tScroll + +#define tTimer data[0] +#define tFrame data[1] + +// Have the silhouetted forest background "zoom in" during the transition to the next scene. +// Same as the grass animation above, this achieved by separating frames vertically on the bg tilemap. +static void Scene1_Task_BgZoom(u8 taskId) { s16 * data = gTasks[taskId].data; - if (++data[0] > 3) + if (++tTimer > 3) { - data[0] = 0; - if (data[1] < 2) - data[1]++; - ChangeBgY(BG_SCENE1_BACKGROUND, data[1] << 15, 0); + tTimer = 0; + if (tFrame < 2) + tFrame++; + ChangeBgY(BG_SCENE1_BACKGROUND, tFrame << 15, BG_COORD_SET); } } -static void IntroCB_FightScene2(struct IntroSequenceData * this) +#undef tTimer +#undef tFrame + +static void IntroCB_Scene2(struct IntroSequenceData * this) { switch (this->state) { case 0: - BlendPalettes(0xFFFFFFFE, 16, RGB_WHITE); - InitBgsFromTemplates(0, sBgTemplates_Scene2, NELEMS(sBgTemplates_Scene2)); + BlendPalettes(PALETTES_ALL & ~1, 16, RGB_WHITE); + InitBgsFromTemplates(0, sBgTemplates_Scene2, ARRAY_COUNT(sBgTemplates_Scene2)); DecompressAndCopyTileDataToVram(BG_SCENE2_BACKGROUND, sScene2_Bg_Gfx, 0, 0, 0); DecompressAndCopyTileDataToVram(BG_SCENE2_BACKGROUND, sScene2_Bg_Map, 0, 0, 1); ShowBg(BG_SCENE2_BACKGROUND); @@ -1366,10 +1439,10 @@ static void IntroCB_FightScene2(struct IntroSequenceData * this) if (!FreeTempTileDataBuffersIfPossible()) { SetVBlankCallback(NULL); - LoadPalette(sScene2_Bg_Pal, 0x10, 0x60); - LoadPalette(sGengar_Pal, 0x50, 0x20); - LoadPalette(sScene2_NidorinoClose_Pal, 0x60, 0x20); - BlendPalettes(0xFFFFFFFE, 16, RGB_WHITE); + LoadPalette(sScene2_Bg_Pal, 0x10, sizeof(sScene2_Bg_Pal)); + LoadPalette(sGengar_Pal, 0x50, sizeof(sGengar_Pal)); + LoadPalette(sScene2_NidorinoClose_Pal, 0x60, sizeof(sScene2_NidorinoClose_Pal)); + BlendPalettes(PALETTES_ALL & ~1, 16, RGB_WHITE); DecompressAndCopyTileDataToVram(BG_SCENE2_PLANTS, sScene2_Plants_Gfx, 0, 0, 0); DecompressAndCopyTileDataToVram(BG_SCENE2_PLANTS, sScene2_Plants_Map, 0, 0, 1); DecompressAndCopyTileDataToVram(BG_SCENE2_NIDORINO, sScene2_NidorinoClose_Gfx, 0, 0, 0); @@ -1378,13 +1451,13 @@ static void IntroCB_FightScene2(struct IntroSequenceData * this) DecompressAndCopyTileDataToVram(BG_SCENE2_GENGAR, sScene2_GengarClose_Map, 0, 0, 1); ResetBgPositions(); ShowBg(BG_SCENE2_PLANTS); - HideBg(BG_SCENE2_NIDORINO); // Hide bgs for scene 3 + HideBg(BG_SCENE2_NIDORINO); // Hide bgs for the close up shot HideBg(BG_SCENE2_GENGAR); - ChangeBgY(BG_SCENE2_GENGAR, 0x0001CE00, 0); - ChangeBgY(BG_SCENE2_NIDORINO, 0x00002800, 0); - CreateTask(Task_FightScene2_CameraHorizPanEffect, 0); - CreateMonStaticSprites(this); - BlendPalettes(0xFFFFFFFE, 16, RGB_WHITE); + ChangeBgY(BG_SCENE2_GENGAR, 0x0001CE00, BG_COORD_SET); + ChangeBgY(BG_SCENE2_NIDORINO, 0x00002800, BG_COORD_SET); + CreateTask(Scene2_Task_PanForest, 0); + Scene2_CreateMonSprites(this); + BlendPalettes(PALETTES_ALL & ~1, 16, RGB_WHITE); SetVBlankCallback(VBlankCB_Intro); this->state++; } @@ -1392,25 +1465,25 @@ static void IntroCB_FightScene2(struct IntroSequenceData * this) case 2: if (!FreeTempTileDataBuffersIfPossible()) { - BeginNormalPaletteFade(0xFFFFFFFE, -2, 16, 0, RGB_WHITE); + BeginNormalPaletteFade(PALETTES_ALL & ~1, -2, 16, 0, RGB_WHITE); this->state++; } break; case 3: if (!gPaletteFade.active) { - this->data[5] = 0; + this->timer = 0; this->state++; } break; case 4: - if (++this->data[5] >= 60) + if (++this->timer >= 60) { - this->data[5] = 0; - DestroyTask(FindTaskIdByFunc(Task_FightScene2_CameraHorizPanEffect)); - DestroyStaticMonSprites(this); - CreateTask(Task_FightScene2_CameraVertPanEffect, 0); - ChangeBgY(BG_SCENE2_BACKGROUND, 0x00010000, 0); // Move background from upper half (wide shot) to lower half (close up) + this->timer = 0; + DestroyTask(FindTaskIdByFunc(Scene2_Task_PanForest)); + Scene2_DestroyMonSprites(this); + CreateTask(Scene2_Task_PanMons, 0); + ChangeBgY(BG_SCENE2_BACKGROUND, 0x00010000, BG_COORD_SET); // Move background from upper half (wide shot) to lower half (close up) HideBg(BG_SCENE2_PLANTS); ShowBg(BG_SCENE2_BACKGROUND); ShowBg(BG_SCENE2_NIDORINO); @@ -1421,65 +1494,69 @@ static void IntroCB_FightScene2(struct IntroSequenceData * this) case 5: if (!IsDma3ManagerBusyWithBgCopy()) { - this->data[5] = 0; + this->timer = 0; this->state++; } break; case 6: - if (++this->data[5] >= 60) + if (++this->timer >= 60) { - DestroyTask(FindTaskIdByFunc(Task_FightScene2_CameraVertPanEffect)); - SetIntroCB(this, IntroCB_FightScene3); + DestroyTask(FindTaskIdByFunc(Scene2_Task_PanMons)); + SetIntroCB(this, IntroCB_Scene3_Entrance); } break; } } -static void Task_FightScene2_CameraHorizPanEffect(u8 taskId) +// Pan the background trees right and the foreground plants left in the wide shot +static void Scene2_Task_PanForest(u8 taskId) { - ChangeBgX(BG_SCENE2_BACKGROUND, 0x0E0, 2); - ChangeBgX(BG_SCENE2_PLANTS, 0x110, 1); + ChangeBgX(BG_SCENE2_BACKGROUND, 0x0E0, BG_COORD_SUB); + ChangeBgX(BG_SCENE2_PLANTS, 0x110, BG_COORD_ADD); } -static void Task_FightScene2_CameraVertPanEffect(u8 taskId) +// Pan Gengar up and Nidorino down in the close up shot +static void Scene2_Task_PanMons(u8 taskId) { - ChangeBgY(BG_SCENE2_GENGAR, 0x020, 1); - ChangeBgY(BG_SCENE2_NIDORINO, 0x024, 2); + ChangeBgY(BG_SCENE2_GENGAR, 0x020, BG_COORD_ADD); + ChangeBgY(BG_SCENE2_NIDORINO, 0x024, BG_COORD_SUB); } -static void CreateMonStaticSprites(struct IntroSequenceData * this) +// Create the Gengar/Nidorino sprites for the wide shot in scene 2 +static void Scene2_CreateMonSprites(struct IntroSequenceData * this) { u8 spriteId; - this->gengarStaticSprite = NULL; - this->nidorinoStaticSprite = NULL; + this->scene2GengarSprite = NULL; + this->scene2NidorinoSprite = NULL; spriteId = CreateSprite(&sSpriteTemplate_Scene2_Nidorino, 168, 80, 11); if (spriteId != MAX_SPRITES) - this->nidorinoStaticSprite = &gSprites[spriteId]; + this->scene2NidorinoSprite = &gSprites[spriteId]; spriteId = CreateSprite(&sSpriteTemplate_Scene2_Gengar, 72, 80, 12); if (spriteId != MAX_SPRITES) - this->gengarStaticSprite = &gSprites[spriteId]; + this->scene2GengarSprite = &gSprites[spriteId]; } -static void DestroyStaticMonSprites(struct IntroSequenceData * this) +static void Scene2_DestroyMonSprites(struct IntroSequenceData * this) { - if (this->gengarStaticSprite != NULL) - DestroySprite(this->gengarStaticSprite); - if (this->nidorinoStaticSprite != NULL) - DestroySprite(this->nidorinoStaticSprite); + if (this->scene2GengarSprite != NULL) + DestroySprite(this->scene2GengarSprite); + if (this->scene2NidorinoSprite != NULL) + DestroySprite(this->scene2NidorinoSprite); } -static void IntroCB_FightScene3(struct IntroSequenceData * this) +// Set up the scene 3 graphics, then start the scrolling to get Gengar and Nidorino in their fight positions +static void IntroCB_Scene3_Entrance(struct IntroSequenceData * this) { switch (this->state) { case 0: - LoadPalette(sScene3_Bg_Pal, 0x10, 0x40); - LoadPalette(sGengar_Pal, 0x50, 0x20); - BlendPalettes(0xFFFFFFFE, 16, RGB_WHITE); - InitBgsFromTemplates(0, sBgTemplates_Scene3, NELEMS(sBgTemplates_Scene3)); + LoadPalette(sScene3_Bg_Pal, 0x10, sizeof(sScene3_Bg_Pal)); + LoadPalette(sGengar_Pal, 0x50, sizeof(sGengar_Pal)); + BlendPalettes(PALETTES_ALL & ~1, 16, RGB_WHITE); + InitBgsFromTemplates(0, sBgTemplates_Scene3, ARRAY_COUNT(sBgTemplates_Scene3)); DecompressAndCopyTileDataToVram(BG_SCENE3_BACKGROUND, sScene3_Bg_Gfx, 0, 0, 0); DecompressAndCopyTileDataToVram(BG_SCENE3_BACKGROUND, sScene3_Bg_Map, 0, 0, 1); ShowBg(BG_SCENE3_BACKGROUND); @@ -1489,280 +1566,306 @@ static void IntroCB_FightScene3(struct IntroSequenceData * this) ResetBgPositions(); this->state++; SetGpuRegBits(REG_OFFSET_DISPCNT, DISPCNT_WIN0_ON); - SetGpuRegBits(REG_OFFSET_WININ, 0x12); - ClearGpuRegBits(REG_OFFSET_WININ, 0x01); - SetGpuRegBits(REG_OFFSET_WINOUT, 0x00); - SetGpuReg(REG_OFFSET_WIN0V, 0x2080); - SetGpuReg(REG_OFFSET_WIN0H, 0x0078); + SetGpuRegBits(REG_OFFSET_WININ, WININ_WIN0_BG1 | WININ_WIN0_OBJ); + ClearGpuRegBits(REG_OFFSET_WININ, WININ_WIN0_BG0); + SetGpuRegBits(REG_OFFSET_WINOUT, 0); + SetGpuReg(REG_OFFSET_WIN0V, WIN_RANGE(32, DISPLAY_HEIGHT - 32)); + SetGpuReg(REG_OFFSET_WIN0H, WIN_RANGE(0, DISPLAY_WIDTH / 2)); break; case 1: if (!FreeTempTileDataBuffersIfPossible()) { DecompressAndCopyTileDataToVram(BG_SCENE3_GENGAR, sScene3_GengarAnim_Gfx, 0, 0, 0); DecompressAndCopyTileDataToVram(BG_SCENE3_GENGAR, sScene3_GengarAnim_Map, 0, 0, 1); - gUnknown_203AB00 = 4; - gUnknown_203AB02 = 52; - ChangeBgX(BG_SCENE3_GENGAR, 0x00001800, 0); - ChangeBgY(BG_SCENE3_GENGAR, 0x0001F000, 0); + sUnusedScene3Var0 = 4; + sUnusedScene3Var1 = 52; + ChangeBgX(BG_SCENE3_GENGAR, 0x00001800, BG_COORD_SET); + ChangeBgY(BG_SCENE3_GENGAR, 0x0001F000, BG_COORD_SET); this->state++; } break; case 2: if (!FreeTempTileDataBuffersIfPossible()) { - BlendPalettes(0xFFFFFFFE, 0, RGB_WHITE); + BlendPalettes(PALETTES_ALL & ~1, 0, RGB_WHITE); ShowBg(BG_SCENE3_GENGAR); - CreateTask(Task_FightScene3_ForestBgScroll, 0); - CreateNidorinoAnimSprite(this); - StartNidorinoAnimSpriteSlideIn(this->nidorinoAnimSprite, 0, 0xB4, 0x34); - CreateTask(Task_FightScene3_Bg0Scroll, 0); - FightScene3_StartBg1Scroll(); - this->data[5] = 0; + CreateTask(Scene3_Task_GengarBounce, 0); + Scene3_CreateNidorinoSprite(this); + Scene3_StartNidorinoEntrance(this->scene3NidorinoSprite, 0, 180, 52); + CreateTask(Scene3_Task_GengarEnter, 0); + Scene3_StartBgScroll(); + this->timer = 0; this->state++; } break; case 3: - if (++this->data[5] == 16) - CreateGrassSprite(this); - if (!IsNidorinoAnimSpriteSlideInRunning(this) && !FuncIsActiveTask(Task_FightScene3_Bg0Scroll)) - SetIntroCB(this, IntroCB_FightScene4); + if (++this->timer == 16) + Scene3_CreateGrassSprite(this); + if (!Scene3_IsNidorinoEntering(this) && !FuncIsActiveTask(Scene3_Task_GengarEnter)) + SetIntroCB(this, IntroCB_Scene3_Fight); break; } } -static void Task_FightScene3_Bg1Scroll(u8 taskId) +#define tSlow data[0] + +// Pan the background trees right during the fight scene. +// It pans quickly while Gengar/Nidorino are sliding onscreen, and it pans slowly thereafter. +static void Scene3_Task_BgScroll(u8 taskId) { - if (gTasks[taskId].data[0] == 0) - ChangeBgX(BG_SCENE3_BACKGROUND, 0x400, 2); + if (!gTasks[taskId].tSlow) + ChangeBgX(BG_SCENE3_BACKGROUND, 0x400, BG_COORD_SUB); else - ChangeBgX(BG_SCENE3_BACKGROUND, 0x020, 2); + ChangeBgX(BG_SCENE3_BACKGROUND, 0x020, BG_COORD_SUB); } -static void FightScene3_StartBg1Scroll(void) +static void Scene3_StartBgScroll(void) { - CreateTask(Task_FightScene3_Bg1Scroll, 0); + CreateTask(Scene3_Task_BgScroll, 0); } -static void FightScene3_SlowBg1Scroll(void) +static void Scene3_SlowBgScroll(void) { - u8 taskId = FindTaskIdByFunc(Task_FightScene3_Bg1Scroll); - gTasks[taskId].data[0] = 1; + u8 taskId = FindTaskIdByFunc(Scene3_Task_BgScroll); + gTasks[taskId].tSlow = TRUE; } -static void Task_FightScene3_ForestBgScroll(u8 taskId) +#undef tSlow + +#define tPaused data[0] +#define tTimer data[1] +#define tState data[2] + +// Gengar has an "idle" animation where it bounces a little +static void Scene3_Task_GengarBounce(u8 taskId) { s16 * data = gTasks[taskId].data; - if (data[0] == 0) + if (!tPaused) { - if (++data[1] >= 30) + if (++tTimer >= 30) { - data[1] = 0; - data[2] ^= 1; - ChangeBgY(BG_SCENE3_GENGAR, (data[2] << 15) + 0x1F000, 0); + tTimer = 0; + tState ^= 1; // Alternate between the 0th (normal) and 1st (slightly crouched) bg frames + ChangeBgY(BG_SCENE3_GENGAR, (tState << 15) + 0x1F000, BG_COORD_SET); } } } -static void FightScene3_PauseForestBgScroll(void) +static void Scene3_PauseGengarBounce(void) { - u8 taskId = FindTaskIdByFunc(Task_FightScene3_ForestBgScroll); - gTasks[taskId].data[0] = 1; + u8 taskId = FindTaskIdByFunc(Scene3_Task_GengarBounce); + gTasks[taskId].tPaused = TRUE; } -static void FightScene3_ResumeForestBgScroll(void) +static void Scene3_ResumeGengarBounce(void) { - u8 taskId = FindTaskIdByFunc(Task_FightScene3_ForestBgScroll); - gTasks[taskId].data[0] = 0; + u8 taskId = FindTaskIdByFunc(Scene3_Task_GengarBounce); + gTasks[taskId].tPaused = FALSE; } -static bool8 FightScene3_GetForestBgScrollState(void) +static bool8 Scene3_IsGengarMidBounce(void) { - u8 taskId = FindTaskIdByFunc(Task_FightScene3_ForestBgScroll); - return gTasks[taskId].data[2]; + u8 taskId = FindTaskIdByFunc(Scene3_Task_GengarBounce); + return gTasks[taskId].tState; } -static void CreateGrassSprite(struct IntroSequenceData * this) +#undef tPaused +#undef tTimer +#undef tState + +// The small clump of grass that passes by in the foreground during the fight +static void Scene3_CreateGrassSprite(struct IntroSequenceData * this) { u8 spriteId = CreateSprite(&sSpriteTemplate_Grass, 296, 112, 7); if (spriteId != MAX_SPRITES) { - this->grassSprite = &gSprites[spriteId]; - this->grassSprite->callback = SpriteCB_Grass; + this->scene3GrassSprite = &gSprites[spriteId]; + this->scene3GrassSprite->callback = SpriteCB_Grass; } else - this->grassSprite = NULL; + this->scene3GrassSprite = NULL; } +#define sState data[0] +#define sBaseX data[1] +#define sVeloc data[2] + static void SpriteCB_Grass(struct Sprite *sprite) { s16 * data = sprite->data; - switch (data[0]) + switch (sState) { case 0: - data[1] = sprite->x << 5; - data[2] = 160; - data[0]++; + sBaseX = sprite->x << 5; + sVeloc = 160; + sState++; // fallthrough case 1: - data[1] -= data[2]; - sprite->x = data[1] >> 5; + sBaseX -= sVeloc; + sprite->x = sBaseX >> 5; if (sprite->x <= 52) { - FightScene3_SlowBg1Scroll(); - data[0]++; + Scene3_SlowBgScroll(); + sState++; } break; case 2: - data[1] -= 32; - sprite->x = data[1] >> 5; + sBaseX -= 32; + sprite->x = sBaseX >> 5; if (sprite->x <= -32) { sprite->invisible = TRUE; - sprite->data[0]++; + sprite->sState++; DestroySprite(sprite); } break; } } -static void IntroCB_FightScene4(struct IntroSequenceData * this) +#undef sState +#undef sBaseX +#undef sVeloc + +static void IntroCB_Scene3_Fight(struct IntroSequenceData * this) { switch (this->state) { case 0: - this->data[5] = 0; + this->timer = 0; this->state++; break; case 1: - if (++this->data[5] > 30) + if (++this->timer > 30) { - FightScene4_NidorinoRearsUp(this); + Scene3_StartNidorinoCry(this); this->state++; } break; case 2: - if (!FightScene4_NidorinoAnimIsRunning(this)) + if (!Scene3_NidorinoAnimIsRunning(this)) { - this->data[5] = 0; + this->timer = 0; this->state++; } break; case 3: - if (++this->data[5] > 30) + if (++this->timer > 30) { - FightScene3_PauseForestBgScroll(); - FightScene4_StartGengarAttack(this); - this->data[5] = 0; + Scene3_PauseGengarBounce(); + Scene3_StartGengarAttack(this); + this->timer = 0; this->state++; } break; case 4: if (this->gengarAttackLanded) { - FightScene4_StartNidorinoRecoilAnim(this); + Scene3_StartNidorinoRecoil(this); this->state++; } break; case 5: - if (!FightScene4_NidorinoAnimIsRunning(this)) + if (!Scene3_NidorinoAnimIsRunning(this)) { - FightScene3_ResumeForestBgScroll(); - this->data[5] = 0; + Scene3_ResumeGengarBounce(); + this->timer = 0; this->state++; } break; case 6: - if (++this->data[5] > 16) + if (++this->timer > 16) { - StartSpriteHopToPosAnim(this->nidorinoAnimSprite, 8, 12, 5); + // Nidorino's 1st hop backwards in preparation to attack + Scene3_StartNidorinoHop(this->scene3NidorinoSprite, 8, 12, 5); this->state++; } break; case 7: - if (!FightScene4_NidorinoAnimIsRunning(this)) + if (!Scene3_NidorinoAnimIsRunning(this)) { - StartSpriteHopToPosAnim(this->nidorinoAnimSprite, 8, 12, 5); + // Nidorino's 2nd hop backwards in preparation to attack + Scene3_StartNidorinoHop(this->scene3NidorinoSprite, 8, 12, 5); this->state++; } break; case 8: - if (!FightScene4_NidorinoAnimIsRunning(this)) + if (!Scene3_NidorinoAnimIsRunning(this)) { - this->data[5] = 0; + this->timer = 0; this->state++; } break; case 9: - if (++this->data[5] > 20) + if (++this->timer > 20) { - StartNidorinoAnim_LaunchSelfAtGengarAnim(this); - this->data[5] = 0; + Scene3_StartNidorinoAttack(this); + this->timer = 0; this->state++; } break; case 10: - if (!FightScene3_GetForestBgScrollState()) + if (!Scene3_IsGengarMidBounce()) { - FightScene3_PauseForestBgScroll(); - CreateGengarBackSprite(this); + Scene3_PauseGengarBounce(); + Scene3_CreateGengarSprite(this); this->state++; } break; case 11: HideBg(BG_SCENE3_GENGAR); - this->data[5] = 0; + this->timer = 0; this->state++; break; case 12: - if (++this->data[5] == 48) - BeginNormalPaletteFade(0x00000006, 2, 0, 16, RGB_WHITE); - if (this->data[5] > 120) + if (++this->timer == 48) + BeginNormalPaletteFade((1 << 1) | (1 << 2), 2, 0, 16, RGB_WHITE); + if (this->timer > 120) { - FightScene4_StartNidorinoAffineAnim(this); - FightScene4_StartGengarAffineAnim(this); + Scene3_NidorinoZoom(this); + Scene3_GengarZoom(this); this->state++; - this->data[5] = 0; + this->timer = 0; } break; case 13: - if (++this->data[5] > 8) + if (++this->timer > 8) { CpuFill16(RGB_WHITE, gPlttBufferUnfaded + 16, 64); - BeginNormalPaletteFade(0xFFFFFFFE, -2, 0, 16, RGB_BLACK); + BeginNormalPaletteFade(PALETTES_ALL & ~1, -2, 0, 16, RGB_BLACK); this->state++; } break; case 14: if (!gPaletteFade.active) { - this->data[5] = 0; + this->timer = 0; this->state++; } break; case 15: - if (++this->data[5] > 60) - SetIntroCB(this, IntroCB_CleanUp); + if (++this->timer > 60) + SetIntroCB(this, IntroCB_ExitToTitleScreen); break; default: if (JOY_NEW(R_BUTTON)) { - BlendPalettes(0xFFFF0064, 0, RGB_WHITE); - this->nidorinoAnimSprite->x2 = 0; - this->nidorinoAnimSprite->x = 0xB4; + BlendPalettes(PALETTES_OBJECTS | (1 << 2) | (1 << 5) | (1 << 6), 0, RGB_WHITE); + this->scene3NidorinoSprite->x2 = 0; + this->scene3NidorinoSprite->x = 180; this->state = 1; - this->data[5] = 30; + this->timer = 30; } break; } } -static void FightScene_CalcCenterToCornerVec(struct Sprite *sprite) +static void Scene3_CalcCenterToCornerVec(struct Sprite *sprite) { CalcCenterToCornerVec(sprite, sprite->oam.shape, sprite->oam.size, sprite->oam.affineMode); } -static void CreateGengarBackSprite(struct IntroSequenceData * this) +static void Scene3_CreateGengarSprite(struct IntroSequenceData * this) { int i; @@ -1775,40 +1878,40 @@ static void CreateGengarBackSprite(struct IntroSequenceData * this) if (spriteId != MAX_SPRITES) { StartSpriteAnim(&gSprites[spriteId], i); - this->gengarBackSprites[i] = &gSprites[spriteId]; + this->scene3GengarSprites[i] = &gSprites[spriteId]; if (i & 1) - this->gengarBackSprites[i]->oam.shape = ST_OAM_V_RECTANGLE; - FightScene_CalcCenterToCornerVec(this->gengarBackSprites[i]); + this->scene3GengarSprites[i]->oam.shape = ST_OAM_V_RECTANGLE; + Scene3_CalcCenterToCornerVec(this->scene3GengarSprites[i]); } } } -static void FightScene4_StartNidorinoAffineAnim(struct IntroSequenceData * this) +static void Scene3_NidorinoZoom(struct IntroSequenceData * this) { - this->nidorinoAnimSprite->x += this->nidorinoAnimSprite->x2; - this->nidorinoAnimSprite->y += this->nidorinoAnimSprite->y2; - obj_pos2_update_enable(this->nidorinoAnimSprite, 0, 0x2A); - this->nidorinoAnimSprite->callback = SpriteCallbackDummy; - StartSpriteAffineAnim(this->nidorinoAnimSprite, 1); + this->scene3NidorinoSprite->x += this->scene3NidorinoSprite->x2; + this->scene3NidorinoSprite->y += this->scene3NidorinoSprite->y2; + SetSpriteMatrixAnchor(this->scene3NidorinoSprite, 0, 42); + this->scene3NidorinoSprite->callback = SpriteCallbackDummy; + StartSpriteAffineAnim(this->scene3NidorinoSprite, AFFINEANIM_ZOOM); } -static void SpriteCB_DummyButNotDummy(struct Sprite *sprite) +static void SpriteCB_Idle(struct Sprite *sprite) { } -static void FightScene4_StartGengarAffineAnim(struct IntroSequenceData * this) +static void Scene3_GengarZoom(struct IntroSequenceData * this) { int i; for (i = 0; i < NUM_GENGAR_BACK_SPRITES; i++) { - StartSpriteAffineAnim(this->gengarBackSprites[i], 1); - this->gengarBackSprites[i]->callback = SpriteCB_DummyButNotDummy; - obj_pos2_update_enable(this->gengarBackSprites[i], sGengarBackSpritePos2UpdateMods[i][0], sGengarBackSpritePos2UpdateMods[i][1]); + StartSpriteAffineAnim(this->scene3GengarSprites[i], AFFINEANIM_ZOOM); + this->scene3GengarSprites[i]->callback = SpriteCB_Idle; + SetSpriteMatrixAnchor(this->scene3GengarSprites[i], sGengarZoomMatrixAnchors[i][0], sGengarZoomMatrixAnchors[i][1]); } } -static void IntroCB_CleanUp(struct IntroSequenceData * this) +static void IntroCB_ExitToTitleScreen(struct IntroSequenceData * this) { switch (this->state) { @@ -1829,45 +1932,63 @@ static void IntroCB_CleanUp(struct IntroSequenceData * this) } } -static void GameFreakScene_LoadGfxCreateStar(void) +// Sprite data for SpriteCB_Star +#define sStar_BaseX data[0] +#define sStar_BaseY data[1] +#define sStar_SpeedX data[2] +#define sStar_SpeedY data[3] +#define sStar_SinIdx data[4] +#define sStar_SparkleTimer data[5] +#define sStar_SparkleRngSeed data[6] + +static void GFScene_LoadGfxCreateStar(void) { int i; u8 spriteId; - static EWRAM_DATA u32 sTrailingSparklesRngSeed = 0; + static EWRAM_DATA u32 sStarSparklesRngSeed = 0; - for (i = 0; i < NELEMS(sSpriteSheets_GameFreakScene); i++) + for (i = 0; i < ARRAY_COUNT(sSpriteSheets_GameFreakScene); i++) LoadCompressedSpriteSheet(&sSpriteSheets_GameFreakScene[i]); LoadSpritePalettes(sSpritePalettes_GameFreakScene); - sLargeStarXSpeed = 0x60; - sLargeStarYSpeed = 0x10; - sTrailingSparklesXmodMask = 0x07; - sUnusedVarRelatedToGameFreakStars = 5; - sTrailingSparklesSpawnRate = 8; - sTrailingSparklesFlickerStartTime = 90; - sTrailingSparklesDestroySpriteTime = 120; - sTrailingSparklesXspeed = 1; - sTrailingSparklesYspeed = 1; - sTrailingSparklesXprecision = 5; - sTrailingSparklesYprecision = 5; - if (sTrailingSparklesRngSeed == 0) - sTrailingSparklesRngSeed = 354128453; - spriteId = CreateSprite(&sSpriteTemplate_Star, 0xF8, 0x37, 0); + sStarSpeedX = 96; + sStarSpeedY = 16; + sStarSparklesXmodMask = 0x07; + sStarSparklesUnusedVar = 5; + sStarSparklesSpawnRate = 8; + sStarSparklesFlickerStartTime = 90; + sStarSparklesDestroySpriteTime = 120; + sStarSparklesXspeed = 1; + sStarSparklesYspeed = 1; + sStarSparklesXprecision = 5; + sStarSparklesYprecision = 5; + if (sStarSparklesRngSeed == 0) + sStarSparklesRngSeed = 354128453; + spriteId = CreateSprite(&sSpriteTemplate_Star, 248, 55, 0); if (spriteId != MAX_SPRITES) { - gSprites[spriteId].data[0] = 0xF80; - gSprites[spriteId].data[1] = 0x370; - gSprites[spriteId].data[2] = sLargeStarXSpeed; - gSprites[spriteId].data[3] = sLargeStarYSpeed; - StoreWordInTwoHalfwords((u16 *)&gSprites[spriteId].data[6], sTrailingSparklesRngSeed); + gSprites[spriteId].sStar_BaseX = 248 << 4; + gSprites[spriteId].sStar_BaseY = 55 << 4; + gSprites[spriteId].sStar_SpeedX = sStarSpeedX; + gSprites[spriteId].sStar_SpeedY = sStarSpeedY; + StoreWordInTwoHalfwords((u16 *)&gSprites[spriteId].sStar_SparkleRngSeed, sStarSparklesRngSeed); } } -static void GameFreakScene_TrailingSparklesGen(s16 x, s16 y, u16 a2) +// Sprite data for SpriteCB_SparklesSmall_Star +#define sSmSparkleStar_BaseX data[0] +#define sSmSparkleStar_BaseY data[1] +#define sSmSparkleStar_SpeedX data[2] +#define sSmSparkleStar_SpeedY data[3] +#define sSmSparkleStar_FallSpeed data[4] +#define sSmSparkleStar_FallDist data[5] +#define sSmSparkleStar_Timer data[7] + +static void GFScene_CreateStarSparkle(s16 x, s16 y, u16 random) { static EWRAM_DATA s16 sYmod = 0; u8 spriteId; - s16 xMod = (a2 & sTrailingSparklesXmodMask) + 2; + s16 xMod = (random & sStarSparklesXmodMask) + 2; s16 yMod = sYmod; if (++sYmod > 3) sYmod = -3; @@ -1878,285 +1999,342 @@ static void GameFreakScene_TrailingSparklesGen(s16 x, s16 y, u16 a2) spriteId = CreateSprite(&sSpriteTemplate_SparklesSmall, x, y, 1); if (spriteId != MAX_SPRITES) { - gSprites[spriteId].data[0] = x << sTrailingSparklesXprecision; - gSprites[spriteId].data[1] = y << sTrailingSparklesYprecision; - gSprites[spriteId].data[2] = sTrailingSparklesXspeed * xMod; - gSprites[spriteId].data[3] = sTrailingSparklesYspeed * yMod; + gSprites[spriteId].sSmSparkleStar_BaseX = x << sStarSparklesXprecision; + gSprites[spriteId].sSmSparkleStar_BaseY = y << sStarSparklesYprecision; + gSprites[spriteId].sSmSparkleStar_SpeedX = sStarSparklesXspeed * xMod; + gSprites[spriteId].sSmSparkleStar_SpeedY = sStarSparklesYspeed * yMod; } } } -static void GameFreakScene_StartTrailingSparkleSpawner(void) +#define tSparkleIdx data[0] +#define tNumLoops data[1] +#define tTimer data[2] + +static void GFScene_StartNameSparklesSmall(void) { - CreateTask(Task_GameFreakScene_TrailingSparkleSpawner, 1); + CreateTask(GFScene_Task_NameSparklesSmall, 1); } -static void Task_GameFreakScene_TrailingSparkleSpawner(u8 taskId) +// Sprite data for SpriteCB_SparklesSmall_Name +#define sSmSparkleName_State data[0] +#define sSmSparkleName_BaseY data[1] +#define sSmSparkleName_AnimTimer data[2] +#define sSmSparkleName_NumLoops data[3] +#define sSmSparkleName_DestroyTimer data[4] + +static void GFScene_Task_NameSparklesSmall(u8 taskId) { s16 * data = gTasks[taskId].data; - u8 r6; + u8 i; u8 spriteId; - data[2]++, data[3]++; - if (data[2] > 6) + tTimer++; + data[3]++; // Unused + if (tTimer > 6) { - data[2] = 0; - r6 = data[0]; - spriteId = CreateSprite(&sSpriteTemplate_SparklesSmall, sTrailingSparkleCoords[r6].x, sTrailingSparkleCoords[r6].y, 2); - StartSpriteAnim(&gSprites[spriteId], 1); - gSprites[spriteId].callback = SpriteCB_SparklesSmall2; - gSprites[spriteId].data[1] = sTrailingSparkleCoords[r6].y << 4; - gSprites[spriteId].data[2] = 120; - gSprites[spriteId].data[3] = data[1]; - if (gSprites[spriteId].data[3] < 0) - gSprites[spriteId].data[3] = 1; - if (++data[0] >= NELEMS(sTrailingSparkleCoords)) + tTimer = 0; + i = tSparkleIdx; + spriteId = CreateSprite(&sSpriteTemplate_SparklesSmall, sTextSparkleCoords[i].x, sTextSparkleCoords[i].y, 2); + StartSpriteAnim(&gSprites[spriteId], ANIM_SPARKLE_ONCE); + gSprites[spriteId].callback = SpriteCB_SparklesSmall_Name; + gSprites[spriteId].sSmSparkleName_BaseY = sTextSparkleCoords[i].y << 4; + gSprites[spriteId].sSmSparkleName_AnimTimer = 120; + gSprites[spriteId].sSmSparkleName_NumLoops = tNumLoops; + if (gSprites[spriteId].sSmSparkleName_NumLoops < 0) + gSprites[spriteId].sSmSparkleName_NumLoops = 1; + if (++tSparkleIdx >= ARRAY_COUNT(sTextSparkleCoords)) { - if (++data[1] > 1) + if (++tNumLoops > 1) DestroyTask(taskId); else - data[0] = 0; + tSparkleIdx = 0; } } } -static void GameFreakScene_StartRevealGameFreakTextSparklesSpawner(void) +#undef tSparkleIdx +#undef tNumLoops +#undef tTimer + +#define tTimer data[0] +#define tSparkleIdx data[1] +#define tNumSparkles data[2] + +static void GFScene_StartNameSparklesBig(void) { - CreateTask(Task_RevealGameFreakTextSparklesSpawner, 2); + CreateTask(GFScene_Task_NameSparklesBig, 2); } -static void Task_RevealGameFreakTextSparklesSpawner(u8 taskId) +static void GFScene_Task_NameSparklesBig(u8 taskId) { s16 * data = gTasks[taskId].data; - u8 r2; + u8 i; - if (data[0] == 0) + if (tTimer == 0) { - r2 = data[1]; - data[1] += 4; - if (data[1] >= NELEMS(sTrailingSparkleCoords)) - data[1] -= NELEMS(sTrailingSparkleCoords); - CreateSprite(&sSpriteTemplate_SparklesBig, sTrailingSparkleCoords[r2].x, sTrailingSparkleCoords[r2].y, 3); - if (++data[2] > 8) + i = tSparkleIdx; + tSparkleIdx += 4; + if (tSparkleIdx >= ARRAY_COUNT(sTextSparkleCoords)) + tSparkleIdx -= ARRAY_COUNT(sTextSparkleCoords); + CreateSprite(&sSpriteTemplate_SparklesBig, sTextSparkleCoords[i].x, sTextSparkleCoords[i].y, 3); + if (++tNumSparkles >= (int)ARRAY_COUNT(sTextSparkleCoords)) DestroyTask(taskId); } - if (++data[0] > 9) - data[0] = 0; + if (++tTimer > 9) + tTimer = 0; } -static struct Sprite *CreateGameFreakLogoArtSprite(void) +#undef tTimer +#undef tSparkleIdx +#undef tNumSparkles + +static struct Sprite *GFScene_CreateLogoSprite(void) { u8 spriteId = CreateSprite(&sSpriteTemplate_GameFreakLogoArt, 120, 70, 4); return &gSprites[spriteId]; } #if REVISION >= 1 -static void CreateGameFreakPresentsText(void) +static void GFScene_CreatePresentsSprite(void) { int i; for (i = 0; i < 2; i++) - gSprites[CreateSprite(&sSpriteTemplate_PresentsText, 0x68 + 32 * i, 0x6c, 5)].oam.tileNum += i * 4; + gSprites[CreateSprite(&sSpriteTemplate_Presents, 104 + 32 * i, 108, 5)].oam.tileNum += i * 4; } #endif -static void FightScene4_StartGengarAttack(struct IntroSequenceData * this) +#define tState data[0] +#define tTimer data[1] +#define tSinIdx data[3] +#define tBaseX data[4] +#define IDX_INTRO_DATA 5 // A pointer to the IntroSequenceData will be stored at data[5] and data[6] +#define tFrame data[7] +#define tMultY data[8] +#define tMultX data[9] + +static void Scene3_StartGengarAttack(struct IntroSequenceData * this) { u8 taskId; this->gengarAttackLanded = FALSE; - taskId = CreateTask(Task_FightScene4_GengarAttack, 4); - SetWordTaskArg(taskId, 5, (uintptr_t)this); - gTasks[taskId].data[3] = 64; - gTasks[taskId].data[4] = GetBgX(BG_SCENE3_GENGAR); + taskId = CreateTask(Scene3_Task_GengarAttack, 4); + SetWordTaskArg(taskId, IDX_INTRO_DATA, (uintptr_t)this); + gTasks[taskId].tSinIdx = 64; + gTasks[taskId].tBaseX = GetBgX(BG_SCENE3_GENGAR); } -static void FightScene4_ApplyGengarAnim(int a, int b, int c, int d) +static void Scene3_ApplyGengarAnim(int frame, int xSub, int ySub, int xBase) { - ChangeBgY(BG_SCENE3_GENGAR, (a << 15) + 0x1F000, 0); - ChangeBgX(BG_SCENE3_GENGAR, d, 0); - ChangeBgX(BG_SCENE3_GENGAR, b << 8, 2); - ChangeBgY(BG_SCENE3_GENGAR, c << 8, 2); + ChangeBgY(BG_SCENE3_GENGAR, (frame << 15) + 0x1F000, BG_COORD_SET); + ChangeBgX(BG_SCENE3_GENGAR, xBase, BG_COORD_SET); + ChangeBgX(BG_SCENE3_GENGAR, xSub << 8, BG_COORD_SUB); + ChangeBgY(BG_SCENE3_GENGAR, ySub << 8, BG_COORD_SUB); } -static void Task_FightScene4_GengarAttack(u8 taskId) +static void Scene3_Task_GengarAttack(u8 taskId) { s16 * data = gTasks[taskId].data; - int b, c; - int angle; - switch (data[0]) + s32 xSub, ySub; + s32 sinIdx; + switch (tState) { case 0: - data[7] = 2; - data[1] = 0; - data[8] = 6; - data[9] = 32; - data[0]++; + tFrame = 2; // Gengar raises arm up + tTimer = 0; + tMultY = 6; + tMultX = 32; + tState++; break; case 1: - data[3] -= 2; - if (++data[1] > 15) + // Gengar moves in a backward arc + tSinIdx -= 2; + if (++tTimer > 15) { - data[1] = 0; - data[0]++; + tTimer = 0; + tState++; } break; case 2: - if (++data[1] == 14) - ((struct IntroSequenceData *)GetWordTaskArg(taskId, 5))->gengarAttackLanded = TRUE; - if (data[1] > 15) + // Gengar pauses at end of backward arc + if (++tTimer == 14) + ((struct IntroSequenceData *)GetWordTaskArg(taskId, IDX_INTRO_DATA))->gengarAttackLanded = TRUE; + if (tTimer > 15) { - data[1] = 0; - data[0]++; + tTimer = 0; + tState++; } break; case 3: - data[3] += 8; - if (++data[1] == 4) + // Gengar moves in a forward arc + tSinIdx += 8; + if (++tTimer == 4) { - FightScene4_CreateGengarSwipeSprites(); - data[8] = 32; - data[9] = 48; - data[7] = 3; + Scene3_CreateGengarSwipeSprites(); + tMultY = 32; + tMultX = 48; + tFrame = 3; // Gengar swipes arm down } - if (data[1] > 7) + if (tTimer > 7) { - data[1] = 0; - data[0]++; + tTimer = 0; + tState++; } break; case 4: - data[3] -= 8; - if (++data[1] > 3) + // Gengar moves in a backward arc to its original position + tSinIdx -= 8; + if (++tTimer > 3) { - data[7] = 0; - data[3] = 64; - data[1] = 0; - data[0]++; + tFrame = 0; // Gengar returns to normal posture + tSinIdx = 64; + tTimer = 0; + tState++; } break; case 5: DestroyTask(taskId); return; } - angle = data[3]; - b = -((gSineTable[angle + 0x40] * data[9]) >> 8); - c = data[8] - ((gSineTable[angle] * data[8]) >> 8); - FightScene4_ApplyGengarAnim(data[7], b, c, data[4]); + + // Animate current movement arc / frame change + sinIdx = tSinIdx; + xSub = -((gSineTable[sinIdx + 64] * tMultX) >> 8); + ySub = tMultY - ((gSineTable[sinIdx] * tMultY) >> 8); + Scene3_ApplyGengarAnim(tFrame, xSub, ySub, tBaseX); } -static void FightScene4_CreateGengarSwipeSprites(void) +#undef tState +#undef tTimer +#undef tSinIdx +#undef tBaseX +#undef IDX_INTRO_DATA +#undef tFrame +#undef tMultY +#undef tMultX + +static void Scene3_CreateGengarSwipeSprites(void) { - u8 spriteId; - - spriteId = CreateSprite(&sSpriteTemplate_GengarSwipe, 132, 78, 6); + u8 spriteId = CreateSprite(&sSpriteTemplate_GengarSwipe, 132, 78, 6); // Implicitly ANIM_SWIPE_TOP spriteId = CreateSprite(&sSpriteTemplate_GengarSwipe, 132, 118, 6); if (spriteId != MAX_SPRITES) { - gSprites[spriteId].oam.shape = ST_OAM_H_RECTANGLE; - gSprites[spriteId].oam.size = ST_OAM_SIZE_2; - FightScene_CalcCenterToCornerVec(&gSprites[spriteId]); - StartSpriteAnim(&gSprites[spriteId], 1); + gSprites[spriteId].oam.shape = SPRITE_SHAPE(32x16); + gSprites[spriteId].oam.size = SPRITE_SIZE(32x16); + Scene3_CalcCenterToCornerVec(&gSprites[spriteId]); + StartSpriteAnim(&gSprites[spriteId], ANIM_SWIPE_BOTTOM); } } static void SpriteCB_GengarSwipe(struct Sprite *sprite) { - sprite->invisible ^= TRUE; + sprite->invisible ^= 1; if (sprite->animEnded) DestroySprite(sprite); } -static void Task_FightScene3_Bg0Scroll(u8 taskId) +#define tState data[0] +#define tSpeed data[1] +#define tMoves data[2] + +// Scroll Gengar into position for the fight +static void Scene3_Task_GengarEnter(u8 taskId) { s16 * data = gTasks[taskId].data; - static EWRAM_DATA u32 gUnknown_203AB30 = 0; + static EWRAM_DATA u32 sGengarScroll = 0; - switch (data[0]) + switch (tState) { case 0: - data[1] = 0x400; - data[0]++; + tSpeed = 0x400; + tState++; // fallthrough case 1: - if (++data[2] > 39 && data[1] > 16) - data[1] -= 16; - gUnknown_203AB30 = ChangeBgX(BG_SCENE3_GENGAR, data[1], 1); - if (gUnknown_203AB30 >= 0x8000) + // Don't decelerate for the first 40 movements + if (++tMoves >= 40 && tSpeed > 16) + tSpeed -= 16; + sGengarScroll = ChangeBgX(BG_SCENE3_GENGAR, tSpeed, BG_COORD_ADD); + + if (sGengarScroll >= 0x8000) ClearGpuRegBits(REG_OFFSET_DISPCNT, DISPCNT_WIN0_ON); - if (gUnknown_203AB30 >= 0xEF00) + + if (sGengarScroll >= 0xEF00) { - ChangeBgX(BG_SCENE3_GENGAR, 0xEF00, 0); + ChangeBgX(BG_SCENE3_GENGAR, 0xEF00, BG_COORD_SET); DestroyTask(taskId); } break; } } -static void SpriteCB_LargeStar(struct Sprite *sprite) +#undef tState +#undef tSpeed +#undef tMoves + +static void SpriteCB_Star(struct Sprite *sprite) { - unsigned v; - sprite->data[0] -= sprite->data[2]; - sprite->data[1] += sprite->data[3]; - sprite->data[4] += 48; - sprite->x = sprite->data[0] >> 4; - sprite->y = sprite->data[1] >> 4; - sprite->y2 = gSineTable[(sprite->data[4] >> 4) + 0x40] >> 5; - sprite->data[5]++; - if (sprite->data[5] % sTrailingSparklesSpawnRate) + u32 random; + sprite->sStar_BaseX -= sprite->sStar_SpeedX; + sprite->sStar_BaseY += sprite->sStar_SpeedY; + sprite->sStar_SinIdx += 48; + sprite->x = sprite->sStar_BaseX >> 4; + sprite->y = sprite->sStar_BaseY >> 4; + sprite->y2 = gSineTable[(sprite->sStar_SinIdx >> 4) + 64] >> 5; + sprite->sStar_SparkleTimer++; + if (sprite->sStar_SparkleTimer % sStarSparklesSpawnRate) { - LoadWordFromTwoHalfwords(&sprite->data[6], &v); - v = ISO_RANDOMIZE1(v); - StoreWordInTwoHalfwords(&sprite->data[6], v); - v >>= 16; - GameFreakScene_TrailingSparklesGen(sprite->x, sprite->y + sprite->y2, v); + LoadWordFromTwoHalfwords(&sprite->sStar_SparkleRngSeed, &random); + random = ISO_RANDOMIZE1(random); + StoreWordInTwoHalfwords(&sprite->sStar_SparkleRngSeed, random); + random >>= 16; + GFScene_CreateStarSparkle(sprite->x, sprite->y + sprite->y2, random); } if (sprite->x < -8) DestroySprite(sprite); } -static void SpriteCB_SparklesSmall(struct Sprite *sprite) +// Callback for the sparkles that trail behind the star +static void SpriteCB_SparklesSmall_Star(struct Sprite *sprite) { - u32 v; - - sprite->data[0] += sprite->data[2]; - sprite->data[1] += sprite->data[3]; - sprite->data[4]++; - sprite->data[5] += sprite->data[4]; - sprite->data[7]++; - sprite->x = (u16)sprite->data[0] >> sTrailingSparklesXprecision; - sprite->y = sprite->data[1] >> sTrailingSparklesYprecision; - if (sTrailingSparklesGravityShift && sprite->data[3] < 0) - sprite->y2 = sprite->data[5] >> sTrailingSparklesGravityShift; - if (sprite->data[7] > sTrailingSparklesFlickerStartTime) + sprite->sSmSparkleStar_BaseX += sprite->sSmSparkleStar_SpeedX; + sprite->sSmSparkleStar_BaseY += sprite->sSmSparkleStar_SpeedY; + sprite->sSmSparkleStar_FallDist += ++sprite->sSmSparkleStar_FallSpeed; + sprite->sSmSparkleStar_Timer++; + sprite->x = (u16)sprite->sSmSparkleStar_BaseX >> sStarSparklesXprecision; + sprite->y = sprite->sSmSparkleStar_BaseY >> sStarSparklesYprecision; + if (sStarSparklesGravityShift && sprite->sSmSparkleStar_SpeedY < 0) + sprite->y2 = sprite->sSmSparkleStar_FallDist >> sStarSparklesGravityShift; + if (sprite->sSmSparkleStar_Timer > sStarSparklesFlickerStartTime) { sprite->invisible = !sprite->invisible; - if (sprite->data[7] > sTrailingSparklesDestroySpriteTime) + if (sprite->sSmSparkleStar_Timer > sStarSparklesDestroySpriteTime) DestroySprite(sprite); } - if (sprite->y + sprite->y2 < 0 || sprite->y + sprite->y2 > 160) + if (sprite->y + sprite->y2 < 0 || sprite->y + sprite->y2 > DISPLAY_HEIGHT) DestroySprite(sprite); } -static void SpriteCB_SparklesSmall2(struct Sprite *sprite) +// Callback for the small sparkles during the "Game Freak" text reveal +static void SpriteCB_SparklesSmall_Name(struct Sprite *sprite) { - if (sprite->data[2]) + if (sprite->sSmSparkleName_AnimTimer) { - sprite->data[2]--; - sprite->data[1]++; - sprite->y = sprite->data[1] >> 4; - if (sprite->y > 0x56) + sprite->sSmSparkleName_AnimTimer--; + sprite->sSmSparkleName_BaseY++; + sprite->y = sprite->sSmSparkleName_BaseY >> 4; + if (sprite->y > 86) { - sprite->y = 0x4A; - sprite->data[1] = 0x4A0; + sprite->y = 74; + sprite->sSmSparkleName_BaseY = 74 << 4; } if (sprite->animEnded) { - if (sprite->data[0] == 0) + if (sprite->sSmSparkleName_State == 0) { sprite->x += 26; if (sprite->x > 188) { - sprite->x = 376 - sprite->x; - sprite->data[0] = 1; + sprite->x = (188 * 2) - sprite->x; + sprite->sSmSparkleName_State = 1; } } else @@ -2164,106 +2342,126 @@ static void SpriteCB_SparklesSmall2(struct Sprite *sprite) sprite->x -= 26; if (sprite->x < 52) { - sprite->x = 104 - sprite->x; - sprite->data[0] = 0; + sprite->x = (52 * 2) - sprite->x; + sprite->sSmSparkleName_State = 0; } } - StartSpriteAnim(sprite, 1); + StartSpriteAnim(sprite, ANIM_SPARKLE_ONCE); } } else { - if (sprite->data[3]) + if (sprite->sSmSparkleName_NumLoops) DestroySprite(sprite); if (sprite->animEnded) - StartSpriteAnim(sprite, 0); - sprite->data[1] += 4; - sprite->y = sprite->data[1] >> 4; - if (++sprite->data[4] > 50) + StartSpriteAnim(sprite, ANIM_SPARKLE_LOOP); + sprite->sSmSparkleName_BaseY += 4; + sprite->y = sprite->sSmSparkleName_BaseY >> 4; + if (++sprite->sSmSparkleName_DestroyTimer > 50) DestroySprite(sprite); } } -static void SpriteCB_RevealGameFreakTextSparkles(struct Sprite *sprite) +// Callback for the big sparkles during the "Game Freak" text reveal +static void SpriteCB_SparklesBig(struct Sprite *sprite) { if (sprite->animEnded) DestroySprite(sprite); } -static void CreateNidorinoAnimSprite(struct IntroSequenceData * this) +static void Scene3_CreateNidorinoSprite(struct IntroSequenceData * this) { u8 spriteId = CreateSprite(&sSpriteTemplate_Scene3_Nidorino, 0, 0, 9); - this->nidorinoAnimSprite = &gSprites[spriteId]; + this->scene3NidorinoSprite = &gSprites[spriteId]; } -static void StartNidorinoAnimSpriteSlideIn(struct Sprite *sprite, s16 x0, s16 x1, u16 speed) +#define sX data[0] +#define sSpeed data[1] +#define sTime data[2] // Not read +#define sTargetX data[3] +#define sTimer data[4] + +static void Scene3_StartNidorinoEntrance(struct Sprite *sprite, s16 xStart, s16 x1, u16 time) { - sprite->data[0] = x0 << 4; - sprite->data[1] = ((x1 - x0) << 4) / speed; - sprite->data[2] = speed; - sprite->data[3] = x1; - sprite->data[4] = 0; - sprite->x = x0; + sprite->sX = xStart << 4; + sprite->sSpeed = ((x1 - xStart) << 4) / time; + sprite->sTime = time; + sprite->sTargetX = x1; + sprite->sTimer = 0; + sprite->x = xStart; sprite->y = 100; - sprite->callback = SpriteCB_NidorinoAnimSpriteSlideIn; + sprite->callback = Scene3_SpriteCB_NidorinoEnter; } -static void SpriteCB_NidorinoAnimSpriteSlideIn(struct Sprite *sprite) +static void Scene3_SpriteCB_NidorinoEnter(struct Sprite *sprite) { - if (++sprite->data[4] >= 40) + if (++sprite->sTimer >= 40) { - if (sprite->data[1] > 1) - sprite->data[1]--; + // Start decelerating after 40 frames + if (sprite->sSpeed > 1) + sprite->sSpeed--; } - sprite->data[0] += sprite->data[1]; - sprite->x = sprite->data[0] >> 4; - if (sprite->x >= sprite->data[3]) + sprite->sX += sprite->sSpeed; + sprite->x = sprite->sX >> 4; + if (sprite->x >= sprite->sTargetX) { - sprite->x = sprite->data[3]; + // Reached final position + sprite->x = sprite->sTargetX; sprite->callback = SpriteCallbackDummy; } } -static bool32 IsNidorinoAnimSpriteSlideInRunning(struct IntroSequenceData * ptr) +static bool32 Scene3_IsNidorinoEntering(struct IntroSequenceData * ptr) { - return ptr->nidorinoAnimSprite->callback == SpriteCB_NidorinoAnimSpriteSlideIn ? TRUE : FALSE; + return ptr->scene3NidorinoSprite->callback == Scene3_SpriteCB_NidorinoEnter ? TRUE : FALSE; } -static void FightScene4_NidorinoRearsUp(struct IntroSequenceData * ptr) +#undef sX +#undef sSpeed +#undef sTime +#undef sTargetX +#undef sTimer + +#define sState data[0] +#define sStateTimer data[1] +#define sBounceTimer data[2] + +static void Scene3_StartNidorinoCry(struct IntroSequenceData * ptr) { - StartSpriteAnim(ptr->nidorinoAnimSprite, 2); - ptr->nidorinoAnimSprite->data[0] = 0; - ptr->nidorinoAnimSprite->data[1] = 0; - ptr->nidorinoAnimSprite->y2 = 3; - ptr->nidorinoAnimSprite->callback = SpriteCB_NidorinoRearsUp; + StartSpriteAnim(ptr->scene3NidorinoSprite, ANIM_NIDORINO_CROUCH); + ptr->scene3NidorinoSprite->sState = 0; + ptr->scene3NidorinoSprite->sStateTimer = 0; + ptr->scene3NidorinoSprite->y2 = 3; + ptr->scene3NidorinoSprite->callback = SpriteCB_NidorinoCry; } -static void SpriteCB_NidorinoRearsUp(struct Sprite *sprite) +static void SpriteCB_NidorinoCry(struct Sprite *sprite) { - switch (sprite->data[0]) + switch (sprite->sState) { case 0: - if (++sprite->data[1] > 8) + if (++sprite->sStateTimer > 8) { - StartSpriteAnim(sprite, 1); + StartSpriteAnim(sprite, ANIM_NIDORINO_CRY); sprite->y2 = 0; - sprite->data[0]++; + sprite->sState++; } break; case 1: PlayCry_ByMode(SPECIES_NIDORINO, 0x3F, CRY_MODE_DOUBLES); - sprite->data[1] = 0; - sprite->data[0]++; + sprite->sStateTimer = 0; + sprite->sState++; break; case 2: - if (++sprite->data[2] > 1) + if (++sprite->sBounceTimer > 1) { - sprite->data[2] = 0; + // Nidorino bounces slightly while crying + sprite->sBounceTimer = 0; sprite->y2 = sprite->y2 == 0 ? 1 : 0; } - if (++sprite->data[1] > 48) + if (++sprite->sStateTimer > 48) { - StartSpriteAnim(sprite, 0); + StartSpriteAnim(sprite, ANIM_NIDORINO_NORMAL); sprite->y2 = 0; sprite->callback = SpriteCallbackDummy; } @@ -2271,256 +2469,328 @@ static void SpriteCB_NidorinoRearsUp(struct Sprite *sprite) } } -static void FightScene4_StartNidorinoRecoilAnim(struct IntroSequenceData * ptr) +#undef sState +#undef sStateTimer +#undef sBounceTimer + +#define sState data[0] +#define sStateTimer data[1] +#define sOffsetX data[2] +#define sSinIdx data[3] +#define sLandTimer data[4] +#define sSlowdownTimer data[5] +#define sRandSeed data[6] +#define sSpeedX data[7] + +static void Scene3_StartNidorinoRecoil(struct IntroSequenceData * ptr) { - gUnknown_203AB0A = 16; - gUnknown_203AB04 = 3; - gUnknown_203AB08 = 5; - gUnknown_203AB06 = 0; - StartSpriteAnim(ptr->nidorinoAnimSprite, 2); - ptr->nidorinoAnimSprite->data[0] = 0; - ptr->nidorinoAnimSprite->data[1] = 0; - ptr->nidorinoAnimSprite->data[2] = 0; - ptr->nidorinoAnimSprite->data[3] = 0; - ptr->nidorinoAnimSprite->data[4] = 0; - ptr->nidorinoAnimSprite->data[7] = 40; - ptr->nidorinoAnimSprite->callback = SpriteCB_NidorinoRecoil; + sNidorinoRecoilReturnTime = 16; + sNidorinoJumpMult = 3; + sNidorinoJumpDiv = 5; + sNidorinoAnimDelayTime = 0; + StartSpriteAnim(ptr->scene3NidorinoSprite, ANIM_NIDORINO_CROUCH); + ptr->scene3NidorinoSprite->sState = 0; + ptr->scene3NidorinoSprite->sStateTimer = 0; + ptr->scene3NidorinoSprite->sOffsetX = 0; + ptr->scene3NidorinoSprite->sSinIdx = 0; + ptr->scene3NidorinoSprite->sLandTimer = 0; + ptr->scene3NidorinoSprite->sSpeedX = 40; + ptr->scene3NidorinoSprite->callback = SpriteCB_NidorinoRecoil; } static void SpriteCB_NidorinoRecoil(struct Sprite *sprite) { - switch (sprite->data[0]) + switch (sprite->sState) { case 0: - sprite->data[1]++; - if (sprite->data[1] > 4) + if (++sprite->sStateTimer > 4) { - StartSpriteAnim(sprite, 3); - sprite->data[0]++; + StartSpriteAnim(sprite, ANIM_NIDORINO_HOP); + sprite->sState++; } break; case 1: - sprite->data[2] += sprite->data[7]; - sprite->data[3] += 8; - sprite->x2 = sprite->data[2] >> 4; - sprite->y2 = -((gSineTable[sprite->data[3]] * gUnknown_203AB04) >> gUnknown_203AB08); - sprite->data[5]++; - if (sprite->data[5] > gUnknown_203AB06) + // Nidorino jumping backwards in the air + sprite->sOffsetX += sprite->sSpeedX; + sprite->sSinIdx += 8; + sprite->x2 = sprite->sOffsetX >> 4; + sprite->y2 = -((gSineTable[sprite->sSinIdx] * sNidorinoJumpMult) >> sNidorinoJumpDiv); + sprite->sSlowdownTimer++; + if (sprite->sSlowdownTimer > sNidorinoAnimDelayTime) { - sprite->data[5] = 0; - sprite->data[7]--; + sprite->sSlowdownTimer = 0; + sprite->sSpeedX--; } - sprite->data[4]++; - if (sprite->data[4] > 15) + if (++sprite->sLandTimer > 15) { - StartSpriteAnim(sprite, 2); - sprite->data[1] = 0; - sprite->data[6] = 0x4757; - sprite->data[7] = 28; - sprite->data[0]++; + // Nidorino hits the ground + StartSpriteAnim(sprite, ANIM_NIDORINO_CROUCH); + sprite->sStateTimer = 0; + sprite->sRandSeed = 0x4757; + sprite->sSpeedX = 28; + sprite->sState++; } break; case 2: - sprite->data[2] += sprite->data[7]; - sprite->x2 = sprite->data[2] >> 4; - sprite->data[1]++; - if (sprite->data[1] > 6) + // Nidorino sliding on the ground + sprite->sOffsetX += sprite->sSpeedX; + sprite->x2 = sprite->sOffsetX >> 4; + if (++sprite->sStateTimer > 6) { - CreateNidorinoRecoilDustSprites(sprite->x + sprite->x2, sprite->y + sprite->y2, sprite->data[6]); - sprite->data[6] *= RAND_MULT; + // The position of each subsequent dust sprite is "random", but with a fixed + // initial seed so that they'll be in the same positions between intro runs + CreateNidorinoRecoilDustSprites(sprite->x + sprite->x2, sprite->y + sprite->y2, sprite->sRandSeed); + sprite->sRandSeed *= RAND_MULT; } - if (sprite->data[1] > 12) + if (sprite->sStateTimer > 12) { - StartSpriteAnim(sprite, 0); - sprite->data[1] = 0; - sprite->data[0]++; + StartSpriteAnim(sprite, ANIM_NIDORINO_NORMAL); + sprite->sStateTimer = 0; + sprite->sState++; } break; case 3: - sprite->data[1]++; - if (sprite->data[1] > 16) - StartSpriteHopToPosAnim(sprite, gUnknown_203AB0A, -sprite->x2, 4); + // Nidorino hops back to its original position + if (++sprite->sStateTimer > 16) + Scene3_StartNidorinoHop(sprite, sNidorinoRecoilReturnTime, -sprite->x2, 4); break; } } -static bool8 FightScene4_NidorinoAnimIsRunning(struct IntroSequenceData * ptr) +#undef sState +#undef sStateTimer +#undef sOffsetX +#undef sSinIdx +#undef sLandTimer +#undef sSlowdownTimer +#undef sRandSeed +#undef sSpeedX + +static bool8 Scene3_NidorinoAnimIsRunning(struct IntroSequenceData * ptr) { - return ptr->nidorinoAnimSprite->callback == SpriteCallbackDummy ? FALSE : TRUE; + return ptr->scene3NidorinoSprite->callback == SpriteCallbackDummy ? FALSE : TRUE; } +#define sState data[0] +#define sX data[1] +#define sY data[2] +#define sSpeedX data[3] +#define sSpeedY data[4] +#define sInvisibleTimer data[7] + static void CreateNidorinoRecoilDustSprites(s16 x, s16 y, s16 seed) { int i; u8 spriteId; + // Recoil dust sprites are created in pairs at the same initial position but with different speeds. + // Only one of each pair will be visible at a time. for (i = 0; i < 2; i++) { spriteId = CreateSprite(&sSpriteTemplate_NidorinoRecoilDust, x - 22, y + 24, 10); if (spriteId != MAX_SPRITES) { - gSprites[spriteId].data[3] = (seed % 13) + 8; - gSprites[spriteId].data[4] = seed % 3; - gSprites[spriteId].data[7] = i; + gSprites[spriteId].sSpeedX = (seed % 13) + 8; + gSprites[spriteId].sSpeedY = seed % 3; + gSprites[spriteId].sInvisibleTimer = i; seed *= RAND_MULT; } } } -static void SpriteCB_NidorinoRecoilDust(struct Sprite *sprite) +static void SpriteCB_RecoilDust(struct Sprite *sprite) { s16 * data = sprite->data; - switch (sprite->data[0]) + switch (sprite->sState) { case 0: - data[1] = sprite->x << 4; - data[2] = sprite->y << 4; - sprite->data[0]++; + sX = sprite->x << 4; + sY = sprite->y << 4; + sprite->sState++; // fallthrough case 1: - data[1] -= data[3]; - data[2] += data[4]; - sprite->x = data[1] >> 4; - sprite->y = data[2] >> 4; + sX -= sSpeedX; + sY += sSpeedY; + sprite->x = sX >> 4; + sprite->y = sY >> 4; if (sprite->animEnded) DestroySprite(sprite); break; } - if (++data[7] > 1) + + // Recoil dust flashes in and out + if (++sInvisibleTimer > 1) { - data[7] = 0; - sprite->invisible ^= TRUE; + sInvisibleTimer = 0; + sprite->invisible ^= 1; } } -static void StartSpriteHopToPosAnim(struct Sprite *sprite, u16 a1, s16 a2, u8 a3) +#undef sState +#undef sX +#undef sY +#undef sSpeedX +#undef sSpeedY +#undef sInvisibleTimer + +#define sState data[0] +#define sAirTime data[1] +#define sOffsetX data[2] +#define sSpeedX data[3] +#define sSinIdx data[4] +#define sSpeedY data[5] +#define sTimer data[6] +#define sHeightShift data[7] + +static void Scene3_StartNidorinoHop(struct Sprite *sprite, u16 time, s16 targetX, u8 heightShift) { - sprite->data[0] = 0; - sprite->data[1] = a1; - sprite->data[2] = sprite->x2 << 4; - sprite->data[3] = (a2 << 4) / a1; - sprite->data[4] = 0; - sprite->data[5] = 0x800 / a1; - sprite->data[6] = 0; - sprite->data[7] = a3; - StartSpriteAnim(sprite, 2); - sprite->callback = SpriteCB_HopToPos; + sprite->sState = 0; + sprite->sAirTime = time; + sprite->sOffsetX = sprite->x2 << 4; + sprite->sSpeedX = (targetX << 4) / time; + sprite->sSinIdx = 0; + sprite->sSpeedY = 0x800 / time; + sprite->sTimer = 0; + sprite->sHeightShift = heightShift; + StartSpriteAnim(sprite, ANIM_NIDORINO_CROUCH); + sprite->callback = SpriteCB_NidorinoHop; } -static void SpriteCB_HopToPos(struct Sprite *sprite) +static void SpriteCB_NidorinoHop(struct Sprite *sprite) { - switch (sprite->data[0]) + switch (sprite->sState) { case 0: - if (++sprite->data[6] > 4) + if (++sprite->sTimer > 4) { - StartSpriteAnim(sprite, 3); - sprite->data[6] = 0; - sprite->data[0]++; + StartSpriteAnim(sprite, ANIM_NIDORINO_HOP); + sprite->sTimer = 0; + sprite->sState++; } break; case 1: - sprite->data[1]--; - if (sprite->data[1]) + if (--sprite->sAirTime) { - sprite->data[2] += sprite->data[3]; - sprite->data[4] += sprite->data[5]; - sprite->x2 = sprite->data[2] >> 4; - sprite->y2 = -(gSineTable[sprite->data[4] >> 4] >> sprite->data[7]); + // Nidorino moving through the air + sprite->sOffsetX += sprite->sSpeedX; + sprite->sSinIdx += sprite->sSpeedY; + sprite->x2 = sprite->sOffsetX >> 4; + sprite->y2 = -(gSineTable[sprite->sSinIdx >> 4] >> sprite->sHeightShift); } else { - sprite->x2 = (u16)sprite->data[2] >> 4; + // Nidorino lands + sprite->x2 = (u16)sprite->sOffsetX >> 4; sprite->y2 = 0; - StartSpriteAnim(sprite, 2); - if (sprite->data[7] == 5) + StartSpriteAnim(sprite, ANIM_NIDORINO_CROUCH); + if (sprite->sHeightShift == 5) + { + // This is used by the short hops before Nidorino's attack. + // The last state is skipped so that Nidorino will stay in the crouched animation. sprite->callback = SpriteCallbackDummy; + } else { - sprite->data[6] = 0; - sprite->data[0]++; + sprite->sTimer = 0; + sprite->sState++; } } break; case 2: - if (++sprite->data[6] > 4) + if (++sprite->sTimer > 4) { - StartSpriteAnim(sprite, 0); + StartSpriteAnim(sprite, ANIM_NIDORINO_NORMAL); sprite->callback = SpriteCallbackDummy; } break; } } -static void StartNidorinoAnim_LaunchSelfAtGengarAnim(struct IntroSequenceData * ptr) +#undef sState +#undef sAirTime +#undef sOffsetX +#undef sSpeedX +#undef sSinIdx +#undef sSpeedY +#undef sTimer +#undef sHeightShift + +#define sState data[0] +#define sTimer data[1] +#define sShakeTimer data[2] +#define sSpeed data[7] + +static void Scene3_StartNidorinoAttack(struct IntroSequenceData * ptr) { - ptr->nidorinoAnimSprite->data[0] = 0; - ptr->nidorinoAnimSprite->data[1] = 0; - ptr->nidorinoAnimSprite->data[2] = 0; - ptr->nidorinoAnimSprite->data[3] = 0; - ptr->nidorinoAnimSprite->data[4] = 0; - ptr->nidorinoAnimSprite->data[5] = 0; - ptr->nidorinoAnimSprite->x += ptr->nidorinoAnimSprite->x2; - ptr->nidorinoAnimSprite->x2 = 0; - gUnknown_203AB0C = 0x24; - gUnknown_203AB06 = 0x28; - gUnknown_203AB04 = 0x03; - gUnknown_203AB08 = 0x04; - ptr->nidorinoAnimSprite->data[7] = 36; - StartSpriteAnim(ptr->nidorinoAnimSprite, 2); - ptr->nidorinoAnimSprite->callback = SpriteCB_NidorinoAnim_LaunchSelfAtGengar; + ptr->scene3NidorinoSprite->sState = 0; + ptr->scene3NidorinoSprite->sTimer = 0; + ptr->scene3NidorinoSprite->sShakeTimer = 0; + ptr->scene3NidorinoSprite->data[3] = 0; // Unused + ptr->scene3NidorinoSprite->data[4] = 0; // Unused + ptr->scene3NidorinoSprite->data[5] = 0; // Unused + ptr->scene3NidorinoSprite->x += ptr->scene3NidorinoSprite->x2; + ptr->scene3NidorinoSprite->x2 = 0; + sNidorinoUnusedVar = 36; + sNidorinoAnimDelayTime = 40; + sNidorinoJumpMult = 3; + sNidorinoJumpDiv = 4; + ptr->scene3NidorinoSprite->sSpeed = 36; + StartSpriteAnim(ptr->scene3NidorinoSprite, ANIM_NIDORINO_CROUCH); + ptr->scene3NidorinoSprite->callback = SpriteCB_NidorinoAttack; } -static void SpriteCB_NidorinoAnim_LaunchSelfAtGengar(struct Sprite *sprite) +static void SpriteCB_NidorinoAttack(struct Sprite *sprite) { - switch (sprite->data[0]) + switch (sprite->sState) { case 0: - if (++sprite->data[1] & 1) + if (++sprite->sTimer & 1) { - if (++sprite->data[2] & 1) + // Nidorino shakes horizontally before attacking + if (++sprite->sShakeTimer & 1) sprite->x2++; else sprite->x2--; } - if (sprite->data[1] > 17) + if (sprite->sTimer > 17) { - sprite->data[1] = 0; - sprite->data[0]++; + sprite->sTimer = 0; + sprite->sState++; } break; case 1: - if (++sprite->data[1] >= gUnknown_203AB06) + if (++sprite->sTimer >= sNidorinoAnimDelayTime) { - StartSpriteAnim(sprite, 4); - sprite->data[1] = 0; - sprite->data[2] = 0; - sprite->data[0]++; + StartSpriteAnim(sprite, ANIM_NIDORINO_ATTACK); + sprite->sTimer = 0; + sprite->sShakeTimer = 0; + sprite->sState++; } break; case 2: - sprite->data[1] += sprite->data[7]; - sprite->x2 = -(sprite->data[1] >> 4); - sprite->y2 = -((gSineTable[sprite->data[1] >> 4] * gUnknown_203AB04) >> gUnknown_203AB08); - sprite->data[2]++; - if (sprite->data[7] > 12) - sprite->data[7]--; - if ((sprite->data[1] >> 4) > 0x3F) + // Nidorino jumps at Gengar + sprite->sTimer += sprite->sSpeed; + sprite->x2 = -(sprite->sTimer >> 4); + sprite->y2 = -((gSineTable[sprite->sTimer >> 4] * sNidorinoJumpMult) >> sNidorinoJumpDiv); + sprite->sShakeTimer++; // Does nothing + if (sprite->sSpeed > 12) + sprite->sSpeed--; // Decelerate as jump progresses + if ((sprite->sTimer >> 4) > 63) sprite->callback = SpriteCallbackDummy; break; } } -static void LoadFightSceneSpriteTilesAndPals(void) +#undef sState +#undef sTimer +#undef sShakeTimer +#undef sSpeed + +static void LoadFightSceneSpriteGraphics(void) { int i; - - for (i = 0; i < NELEMS(sFightSceneSpriteSheets); i++) - { + for (i = 0; i < ARRAY_COUNT(sFightSceneSpriteSheets); i++) LoadCompressedSpriteSheet(&sFightSceneSpriteSheets[i]); - } - // sFightSceneSpritePalettes is not properly terminated, so this - // call exhibits undefined behavior. LoadSpritePalettes(sFightSceneSpritePalettes); } diff --git a/src/minigame_countdown.c b/src/minigame_countdown.c index 278a0c0de..b7495c328 100644 --- a/src/minigame_countdown.c +++ b/src/minigame_countdown.c @@ -80,8 +80,7 @@ static bool32 RunMinigameCountdownDigitsAnim(u8 spriteId) switch (sprite->data[0]) { case 0: - // some sort of affine transform; x transform disabled - obj_pos2_update_enable(sprite, 0x800, 0x1A); + SetSpriteMatrixAnchor(sprite, NO_ANCHOR, 26); sprite->data[0]++; // fallthrough case 1: diff --git a/src/sprite.c b/src/sprite.c index 482003a5f..5b1ea5e4b 100644 --- a/src/sprite.c +++ b/src/sprite.c @@ -5,6 +5,9 @@ #define OAM_MATRIX_COUNT 32 +#define sAnchorX data[6] +#define sAnchorY data[7] + #define SET_SPRITE_TILE_RANGE(index, start, count) \ { \ sSpriteTileRanges[index * 2] = start; \ @@ -83,7 +86,7 @@ static void ApplyAffineAnimFrame(u8 matrixNum, struct AffineAnimFrameCmd *frameC static u8 IndexOfSpriteTileTag(u16 tag); static void AllocSpriteTileRange(u16 tag, u16 start, u16 count); static void DoLoadSpritePalette(const u16 *src, u16 paletteOffset); -static void obj_update_pos2(struct Sprite* sprite, s32 a1, s32 a2); +static void UpdateSpriteMatrixAnchorPos(struct Sprite* sprite, s32 a1, s32 a2); typedef void (*AnimFunc)(struct Sprite *); typedef void (*AnimCmdFunc)(struct Sprite *); @@ -161,41 +164,11 @@ static const struct Sprite sDummySprite = { .oam = DUMMY_OAM_DATA, .anims = gDummySpriteAnimTable, - .images = NULL, .affineAnims = gDummySpriteAffineAnimTable, .template = &gDummySpriteTemplate, - .subspriteTables = NULL, .callback = SpriteCallbackDummy, - .x = 304, .y = 160, - .x2 = 0, .y2 = 0, - .centerToCornerVecX = 0, - .centerToCornerVecY = 0, - .animNum = 0, - .animCmdIndex = 0, - .animDelayCounter = 0, - .animPaused = 0, - .affineAnimPaused = 0, - .animLoopCounter = 0, - .data = {0, 0, 0, 0, 0, 0, 0}, - .inUse = 0, - .coordOffsetEnabled = 0, - .invisible = 0, - .flags_3 = 0, - .flags_4 = 0, - .flags_5 = 0, - .flags_6 = 0, - .flags_7 = 0, - .hFlip = 0, - .vFlip = 0, - .animBeginning = 0, - .affineAnimBeginning = 0, - .animEnded = 0, - .affineAnimEnded = 0, - .usingSheet = 0, - .flags_f = 0, - .sheetTileStart = 0, - .subspriteTableNum = 0, - .subspriteMode = 0, + .x = DISPLAY_WIDTH + 64, + .y = DISPLAY_HEIGHT, .subpriority = 0xFF }; @@ -212,7 +185,7 @@ const union AffineAnimCmd * const gDummySpriteAffineAnimTable[] = { &sDummyAffin const struct SpriteTemplate gDummySpriteTemplate = { .tileTag = 0, - .paletteTag = 0xFFFF, + .paletteTag = TAG_NONE, .oam = &gDummyOamData, .anims = gDummySpriteAnimTable, .images = NULL, @@ -1099,8 +1072,8 @@ void BeginAffineAnim(struct Sprite *sprite) sprite->affineAnimEnded = FALSE; ApplyAffineAnimFrame(matrixNum, &frameCmd); sAffineAnimStates[matrixNum].delayCounter = frameCmd.duration; - if (sprite->flags_f) - obj_update_pos2(sprite, sprite->data[6], sprite->data[7]); + if (sprite->anchored) + UpdateSpriteMatrixAnchorPos(sprite, sprite->sAnchorX, sprite->sAnchorY); } } @@ -1125,8 +1098,8 @@ void ContinueAffineAnim(struct Sprite *sprite) funcIndex = type - 32765; sAffineAnimCmdFuncs[funcIndex](matrixNum, sprite); } - if (sprite->flags_f) - obj_update_pos2(sprite, sprite->data[6], sprite->data[7]); + if (sprite->anchored) + UpdateSpriteMatrixAnchorPos(sprite, sprite->sAnchorX, sprite->sAnchorY); } } @@ -1220,14 +1193,14 @@ u8 GetSpriteMatrixNum(struct Sprite *sprite) return matrixNum; } -void obj_pos2_update_enable(struct Sprite* sprite, s16 xmod, s16 ymod) +void SetSpriteMatrixAnchor(struct Sprite* sprite, s16 x, s16 y) { - sprite->data[6] = xmod; - sprite->data[7] = ymod; - sprite->flags_f = 1; + sprite->sAnchorX = x; + sprite->sAnchorY = y; + sprite->anchored = TRUE; } -static s32 affine_get_new_pos2(s32 baseDim, s32 xformed, s32 modifier) +static s32 GetAnchorCoord(s32 baseDim, s32 xformed, s32 modifier) { s32 subResult, shiftResult; @@ -1239,24 +1212,24 @@ static s32 affine_get_new_pos2(s32 baseDim, s32 xformed, s32 modifier) return modifier - ((u32)(modifier * xformed) / (u32)(baseDim) + shiftResult); } -static void obj_update_pos2(struct Sprite *sprite, s32 xmod, s32 ymod) +static void UpdateSpriteMatrixAnchorPos(struct Sprite *sprite, s32 x, s32 y) { s32 dim, baseDim, xFormed; u32 matrixNum = sprite->oam.matrixNum; - if (xmod != 0x800) + if (x != NO_ANCHOR) { dim = sOamDimensionsCopy[sprite->oam.shape][sprite->oam.size][0]; baseDim = dim << 8; xFormed = (dim << 16) / gOamMatrices[matrixNum].a; - sprite->x2 = affine_get_new_pos2(baseDim, xFormed, xmod); + sprite->x2 = GetAnchorCoord(baseDim, xFormed, x); } - if (ymod != 0x800) + if (y != NO_ANCHOR) { dim = sOamDimensionsCopy[sprite->oam.shape][sprite->oam.size][1]; baseDim = dim << 8; xFormed = (dim << 16) / gOamMatrices[matrixNum].d; - sprite->y2 = affine_get_new_pos2(baseDim, xFormed, ymod); + sprite->y2 = GetAnchorCoord(baseDim, xFormed, y); } }