mirror of
https://github.com/pret/pokefirered.git
synced 2026-05-13 07:48:23 -05:00
2796 lines
83 KiB
C
2796 lines
83 KiB
C
#include "global.h"
|
|
#include "gflib.h"
|
|
#include "m4a.h"
|
|
#include "task.h"
|
|
#include "scanline_effect.h"
|
|
#include "libgcnmultiboot.h"
|
|
#include "link.h"
|
|
#include "menu.h"
|
|
#include "random.h"
|
|
#include "save.h"
|
|
#include "new_game.h"
|
|
#include "title_screen.h"
|
|
#include "decompress.h"
|
|
#include "util.h"
|
|
#include "trig.h"
|
|
#include "load_save.h"
|
|
#include "constants/songs.h"
|
|
#include "constants/sound.h"
|
|
|
|
/*
|
|
The intro is grouped into the following scenes
|
|
- Copyright screen
|
|
- GF Logo
|
|
Scene 1. Brief close up shot of grass
|
|
Scene 2. A panning wide shot followed by a close-up of Gengar/Nidorino
|
|
Scene 3. A fight between Gengar/Nidorino
|
|
|
|
After this it progresses to the title screen
|
|
*/
|
|
|
|
enum {
|
|
GFXTAG_STAR,
|
|
GFXTAG_SPARKLES_SMALL,
|
|
GFXTAG_SPARKLES_BIG,
|
|
GFXTAG_GF_LOGO,
|
|
GFXTAG_PRESENTS,
|
|
GFXTAG_SCENE3_NIDORINO,
|
|
GFXTAG_SCENE2_GENGAR,
|
|
GFXTAG_SCENE2_NIDORINO,
|
|
GFXTAG_SCENE3_GRASS,
|
|
GFXTAG_SCENE3_GENGAR,
|
|
GFXTAG_SCENE3_SWIPE,
|
|
GFXTAG_SCENE3_RECOIL_DUST,
|
|
};
|
|
|
|
enum {
|
|
PALTAG_STAR,
|
|
PALTAG_SPARKLES,
|
|
PALTAG_UNUSED_2,
|
|
PALTAG_GF,
|
|
PALTAG_UNUSED_4,
|
|
PALTAG_UNUSED_5,
|
|
PALTAG_GENGAR,
|
|
PALTAG_NIDORINO,
|
|
PALTAG_SCENE3_GRASS,
|
|
PALTAG_UNUSED_9,
|
|
PALTAG_SCENE3_SWIPE,
|
|
PALTAG_SCENE3_RECOIL_DUST,
|
|
};
|
|
|
|
// Background IDs for Game Freak logo scene
|
|
enum {
|
|
BG_GF_TEXT_LOGO = 2,
|
|
BG_GF_BACKGROUND
|
|
};
|
|
|
|
|
|
// Background IDs for Scene 1
|
|
enum {
|
|
BG_SCENE1_GRASS,
|
|
BG_SCENE1_BACKGROUND,
|
|
BG_SCENE1_UNUSED1,
|
|
BG_SCENE1_UNUSED2
|
|
};
|
|
|
|
#define PALSLOT_SCENE1_GRASS 1
|
|
#define PALSLOT_SCENE1_BG 2
|
|
|
|
// Background IDs for Scene 2
|
|
enum {
|
|
BG_SCENE2_PLANTS,
|
|
BG_SCENE2_NIDORINO,
|
|
BG_SCENE2_GENGAR,
|
|
BG_SCENE2_BACKGROUND // Bg for wide shot on upper half, close up on lower half
|
|
};
|
|
|
|
// Background IDs for Scene 3
|
|
enum {
|
|
BG_SCENE3_GENGAR,
|
|
BG_SCENE3_BACKGROUND,
|
|
BG_SCENE3_UNUSED1,
|
|
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
|
|
|
|
struct IntroSequenceData;
|
|
|
|
typedef void (*IntroCallback)(struct IntroSequenceData *);
|
|
|
|
struct IntroSequenceData
|
|
{
|
|
IntroCallback callback;
|
|
u8 state;
|
|
u8 taskId;
|
|
bool8 gengarAttackLanded;
|
|
u16 data[5]; // [0] and [1] are set but never read, the rest are unused
|
|
u16 timer;
|
|
struct Sprite *gameFreakLogoArtSprite;
|
|
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];
|
|
u8 unused1[0x2080];
|
|
}; // size: 0x28BC
|
|
|
|
static EWRAM_DATA struct GcmbStruct sGcmb = {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);
|
|
static void Intro_ResetGpuRegs(void);
|
|
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 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 SpriteCB_GengarSwipe(struct Sprite *sprite);
|
|
static void SpriteCB_RecoilDust(struct Sprite *sprite);
|
|
static void SpriteCB_NidorinoCry(struct Sprite *sprite);
|
|
static void SpriteCB_NidorinoRecoil(struct Sprite *sprite);
|
|
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");
|
|
|
|
// Game Freak
|
|
static const u16 sGameFreakBg_Pal[] = INCBIN_U16("graphics/intro/game_freak/bg.gbapal");
|
|
static const u8 sGameFreakBg_Gfx[] = INCBIN_U8( "graphics/intro/game_freak/bg.4bpp.lz");
|
|
static const u8 sGameFreakBg_Map[] = INCBIN_U8( "graphics/intro/game_freak/bg.bin.lz");
|
|
static const u16 sGameFreakLogo_Pal[] = INCBIN_U16("graphics/intro/game_freak/logo.gbapal");
|
|
static const u8 sGameFreakText_Gfx[] = INCBIN_U8( "graphics/intro/game_freak/game_freak.4bpp.lz");
|
|
static const u32 sGameFreakLogo_Gfx[] = INCBIN_U32("graphics/intro/game_freak/logo.4bpp.lz");
|
|
static const u16 sStar_Pal[] = INCBIN_U16("graphics/intro/game_freak/star.gbapal");
|
|
static const u32 sStar_Gfx[] = INCBIN_U32("graphics/intro/game_freak/star.4bpp.lz");
|
|
static const u16 sSparkles_Pal[] = INCBIN_U16("graphics/intro/game_freak/sparkles.gbapal");
|
|
static const u32 sSparklesSmall_Gfx[] = INCBIN_U32("graphics/intro/game_freak/sparkles_small.4bpp.lz");
|
|
static const u32 sSparklesBig_Gfx[] = INCBIN_U32("graphics/intro/game_freak/sparkles_big.4bpp.lz");
|
|
static const u32 sPresents_Gfx[] = INCBIN_U32("graphics/intro/game_freak/presents.4bpp.lz");
|
|
|
|
// Scene 1 (Grass close up)
|
|
static const u16 sScene1_Grass_Pal[] = INCBIN_U16("graphics/intro/scene_1/grass.gbapal");
|
|
static const u8 sScene1_Grass_Gfx[] = INCBIN_U8( "graphics/intro/scene_1/grass.4bpp.lz");
|
|
static const u8 sScene1_Grass_Map[] = INCBIN_U8( "graphics/intro/scene_1/grass.bin.lz");
|
|
static const u16 sScene1_Bg_Pal[] = INCBIN_U16("graphics/intro/scene_1/bg.gbapal");
|
|
static const u8 sScene1_Bg_Gfx[] = INCBIN_U8( "graphics/intro/scene_1/bg.4bpp.lz");
|
|
static const u8 sScene1_Bg_Map[] = INCBIN_U8( "graphics/intro/scene_1/bg.bin.lz");
|
|
|
|
// Scenes 2 and 3 (Gengar and Nidorino)
|
|
static const u16 sScene2_Bg_Pal[] = INCBIN_U16("graphics/intro/scene_2/bg.gbapal");
|
|
static const u8 sScene2_Bg_Gfx[] = INCBIN_U8( "graphics/intro/scene_2/bg.4bpp.lz");
|
|
static const u8 sScene2_Bg_Map[] = INCBIN_U8( "graphics/intro/scene_2/bg.bin.lz");
|
|
static const u16 sScene2_Plants_Pal[] = INCBIN_U16("graphics/intro/scene_2/plants.gbapal"); // Unused
|
|
static const u8 sScene2_Plants_Gfx[] = INCBIN_U8( "graphics/intro/scene_2/plants.4bpp.lz");
|
|
static const u8 sScene2_Plants_Map[] = INCBIN_U8( "graphics/intro/scene_2/plants.bin.lz");
|
|
static const u16 sGengar_Pal[] = INCBIN_U16("graphics/intro/gengar.gbapal"); // Used by multiple scenes
|
|
static const u8 sScene2_GengarClose_Gfx[] = INCBIN_U8( "graphics/intro/scene_2/gengar_close.4bpp.lz");
|
|
static const u8 sScene2_GengarClose_Map[] = INCBIN_U8( "graphics/intro/scene_2/gengar_close.bin.lz");
|
|
static const u16 sScene2_NidorinoClose_Pal[] = INCBIN_U16("graphics/intro/scene_2/nidorino_close.gbapal");
|
|
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");
|
|
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");
|
|
static const u32 sScene2_Gengar_Gfx[] = INCBIN_U32("graphics/intro/scene_2/gengar.4bpp.lz");
|
|
static const u16 sNidorino_Pal[] = INCBIN_U16("graphics/intro/nidorino.gbapal"); // Used by multiple scenes
|
|
static const u32 sScene2_Nidorino_Gfx[] = INCBIN_U32("graphics/intro/scene_2/nidorino.4bpp.lz");
|
|
static const u16 sScene3_Grass_Pal[] = INCBIN_U16("graphics/intro/scene_3/grass.gbapal");
|
|
static const u32 sScene3_Grass_Gfx[] = INCBIN_U32("graphics/intro/scene_3/grass.4bpp.lz");
|
|
static const u32 sScene3_GengarStatic_Gfx[] = INCBIN_U32("graphics/intro/scene_3/gengar_static.4bpp.lz");
|
|
static const u32 sScene3_Nidorino_Gfx[] = INCBIN_U32("graphics/intro/scene_3/nidorino.4bpp.lz");
|
|
static const u16 sScene3_Swipe_Pal[] = INCBIN_U16("graphics/intro/scene_3/swipe.gbapal");
|
|
static const u16 sScene3_RecoilDust_Pal[] = INCBIN_U16("graphics/intro/scene_3/recoil_dust.gbapal");
|
|
static const u32 sScene3_Swipe_Gfx[] = INCBIN_U32("graphics/intro/scene_3/swipe.4bpp.lz");
|
|
static const u32 sScene3_RecoilDust_Gfx[] = INCBIN_U32("graphics/intro/scene_3/recoil_dust.4bpp.lz");
|
|
|
|
static const struct BgTemplate sBgTemplates_GameFreakScene[] = {
|
|
{
|
|
.bg = BG_GF_BACKGROUND,
|
|
.charBaseIndex = 3,
|
|
.mapBaseIndex = 31,
|
|
.screenSize = 0,
|
|
.paletteMode = 0,
|
|
.priority = 3,
|
|
.baseTile = 0x000
|
|
}, {
|
|
.bg = BG_GF_TEXT_LOGO,
|
|
.charBaseIndex = 3,
|
|
.mapBaseIndex = 30,
|
|
.screenSize = 0,
|
|
.paletteMode = 0,
|
|
.priority = 2,
|
|
.baseTile = 0x010
|
|
}
|
|
};
|
|
|
|
static const struct BgTemplate sBgTemplates_Scene1[] = {
|
|
{
|
|
.bg = BG_SCENE1_GRASS,
|
|
.charBaseIndex = 0,
|
|
.mapBaseIndex = 28,
|
|
.screenSize = 2,
|
|
.paletteMode = 0,
|
|
.priority = 0,
|
|
.baseTile = 0x000
|
|
}, {
|
|
.bg = BG_SCENE1_BACKGROUND,
|
|
.charBaseIndex = 1,
|
|
.mapBaseIndex = 30,
|
|
.screenSize = 2,
|
|
.paletteMode = 0,
|
|
.priority = 0,
|
|
.baseTile = 0x000
|
|
}
|
|
};
|
|
|
|
static const struct BgTemplate sBgTemplates_Scene2[] = {
|
|
{
|
|
.bg = BG_SCENE2_BACKGROUND,
|
|
.charBaseIndex = 1,
|
|
.mapBaseIndex = 30,
|
|
.screenSize = 2,
|
|
.paletteMode = 0,
|
|
.priority = 3,
|
|
.baseTile = 0x000
|
|
}, {
|
|
.bg = BG_SCENE2_PLANTS,
|
|
.charBaseIndex = 0,
|
|
.mapBaseIndex = 29,
|
|
.screenSize = 0,
|
|
.paletteMode = 0,
|
|
.priority = 0,
|
|
.baseTile = 0x000
|
|
}, {
|
|
.bg = BG_SCENE2_GENGAR,
|
|
.charBaseIndex = 3,
|
|
.mapBaseIndex = 27,
|
|
.screenSize = 0,
|
|
.paletteMode = 0,
|
|
.priority = 2,
|
|
.baseTile = 0x000
|
|
}, {
|
|
.bg = BG_SCENE2_NIDORINO,
|
|
.charBaseIndex = 2,
|
|
.mapBaseIndex = 28,
|
|
.screenSize = 0,
|
|
.paletteMode = 0,
|
|
.priority = 1,
|
|
.baseTile = 0x000
|
|
}
|
|
};
|
|
|
|
static const struct BgTemplate sBgTemplates_Scene3[] = {
|
|
{
|
|
.bg = BG_SCENE3_BACKGROUND,
|
|
.charBaseIndex = 0,
|
|
.mapBaseIndex = 29,
|
|
.screenSize = 0,
|
|
.paletteMode = 0,
|
|
.priority = 1,
|
|
.baseTile = 0x000
|
|
}, {
|
|
.bg = BG_SCENE3_GENGAR,
|
|
.charBaseIndex = 1,
|
|
.mapBaseIndex = 30,
|
|
.screenSize = 2,
|
|
.paletteMode = 0,
|
|
.priority = 0,
|
|
.baseTile = 0x000
|
|
}
|
|
};
|
|
|
|
static const struct WindowTemplate sWindowTemplates[WIN_COUNT + 1] = {
|
|
[WIN_GF_TEXT_LOGO] = {
|
|
.bg = BG_GF_TEXT_LOGO,
|
|
.tilemapLeft = 6,
|
|
.tilemapTop = 4,
|
|
.width = 18,
|
|
.height = 9,
|
|
.paletteNum = 13,
|
|
.baseBlock = 0x000
|
|
},
|
|
[WIN_COUNT] = DUMMY_WIN_TEMPLATE
|
|
};
|
|
|
|
static const u8 sGengarZoomMatrixAnchors[NUM_GENGAR_BACK_SPRITES][2] = {
|
|
{63, 63},
|
|
{ 0, 63},
|
|
{63, 0},
|
|
{ 0, 0}
|
|
};
|
|
|
|
static const struct CompressedSpriteSheet sSpriteSheets_GameFreakScene[] = {
|
|
{sStar_Gfx, 0x80, GFXTAG_STAR},
|
|
{sSparklesSmall_Gfx, 0x80, GFXTAG_SPARKLES_SMALL},
|
|
{sSparklesBig_Gfx, 0x800, GFXTAG_SPARKLES_BIG},
|
|
{sGameFreakLogo_Gfx, 0x400, GFXTAG_GF_LOGO},
|
|
{sPresents_Gfx, 0x100, GFXTAG_PRESENTS}
|
|
};
|
|
|
|
static const struct SpritePalette sSpritePalettes_GameFreakScene[] = {
|
|
{sStar_Pal, PALTAG_STAR},
|
|
{sSparkles_Pal, PALTAG_SPARKLES},
|
|
{sGameFreakLogo_Pal, PALTAG_GF},
|
|
{0}
|
|
};
|
|
|
|
static const struct Coords16 sTextSparkleCoords[] = {
|
|
{ 72, 80},
|
|
{136, 74},
|
|
{168, 80},
|
|
{120, 80},
|
|
{104, 86},
|
|
{ 88, 74},
|
|
{184, 74},
|
|
{ 56, 86},
|
|
{152, 86}
|
|
};
|
|
|
|
static const struct OamData sOam_Star = {
|
|
.affineMode = ST_OAM_AFFINE_OFF,
|
|
.objMode = ST_OAM_OBJ_NORMAL,
|
|
.mosaic = FALSE,
|
|
.bpp = ST_OAM_4BPP,
|
|
.shape = SPRITE_SHAPE(16x16),
|
|
.matrixNum = 0,
|
|
.size = SPRITE_SIZE(16x16),
|
|
.tileNum = 0x000,
|
|
.priority = 2,
|
|
.paletteNum = 0
|
|
};
|
|
|
|
static const struct OamData sOam_SparklesSmall = {
|
|
.affineMode = ST_OAM_AFFINE_OFF,
|
|
.objMode = ST_OAM_OBJ_NORMAL,
|
|
.mosaic = FALSE,
|
|
.bpp = ST_OAM_4BPP,
|
|
.shape = SPRITE_SHAPE(8x8),
|
|
.matrixNum = 0,
|
|
.size = SPRITE_SIZE(8x8),
|
|
.tileNum = 0x000,
|
|
.priority = 2,
|
|
.paletteNum = 0
|
|
};
|
|
|
|
static const union AnimCmd sAnim_SparklesSmall_Loop[] = {
|
|
ANIMCMD_FRAME(0, 4),
|
|
ANIMCMD_FRAME(1, 4),
|
|
ANIMCMD_FRAME(2, 4),
|
|
ANIMCMD_FRAME(3, 4),
|
|
ANIMCMD_JUMP(0)
|
|
};
|
|
|
|
static const union AnimCmd sAnim_SparklesSmall_Once[] = {
|
|
ANIMCMD_FRAME(0, 4),
|
|
ANIMCMD_FRAME(1, 4),
|
|
ANIMCMD_FRAME(2, 4),
|
|
ANIMCMD_FRAME(3, 4),
|
|
ANIMCMD_END
|
|
};
|
|
|
|
static const union AnimCmd *const sAnims_SparklesSmall[] = {
|
|
[ANIM_SPARKLE_LOOP] = sAnim_SparklesSmall_Loop,
|
|
[ANIM_SPARKLE_ONCE] = sAnim_SparklesSmall_Once
|
|
};
|
|
|
|
static const struct SpriteTemplate sSpriteTemplate_Star = {
|
|
.tileTag = GFXTAG_STAR,
|
|
.paletteTag = PALTAG_STAR,
|
|
.oam = &sOam_Star,
|
|
.anims = gDummySpriteAnimTable,
|
|
.images = NULL,
|
|
.affineAnims = gDummySpriteAffineAnimTable,
|
|
.callback = SpriteCB_Star
|
|
};
|
|
|
|
static const struct SpriteTemplate sSpriteTemplate_SparklesSmall = {
|
|
.tileTag = GFXTAG_SPARKLES_SMALL,
|
|
.paletteTag = PALTAG_SPARKLES,
|
|
.oam = &sOam_SparklesSmall,
|
|
.anims = sAnims_SparklesSmall,
|
|
.images = NULL,
|
|
.affineAnims = gDummySpriteAffineAnimTable,
|
|
.callback = SpriteCB_SparklesSmall_Star
|
|
};
|
|
|
|
static const struct OamData sOam_SparklesBig = {
|
|
.affineMode = ST_OAM_AFFINE_OFF,
|
|
.objMode = ST_OAM_OBJ_NORMAL,
|
|
.mosaic = FALSE,
|
|
.bpp = ST_OAM_4BPP,
|
|
.shape = SPRITE_SHAPE(32x32),
|
|
.matrixNum = 0,
|
|
.size = SPRITE_SIZE(32x32),
|
|
.tileNum = 0x000,
|
|
.priority = 2,
|
|
.paletteNum = 0
|
|
};
|
|
|
|
static const union AnimCmd sAnim_SparklesBig[] = {
|
|
ANIMCMD_FRAME(0, 8),
|
|
ANIMCMD_FRAME(16, 8),
|
|
ANIMCMD_FRAME(32, 8),
|
|
ANIMCMD_FRAME(48, 8),
|
|
ANIMCMD_END
|
|
};
|
|
|
|
static const union AnimCmd *const sAnims_SparklesBig[] = {
|
|
sAnim_SparklesBig
|
|
};
|
|
|
|
static const struct SpriteTemplate sSpriteTemplate_SparklesBig = {
|
|
.tileTag = GFXTAG_SPARKLES_BIG,
|
|
.paletteTag = PALTAG_SPARKLES,
|
|
.oam = &sOam_SparklesBig,
|
|
.anims = sAnims_SparklesBig,
|
|
.images = NULL,
|
|
.affineAnims = gDummySpriteAffineAnimTable,
|
|
.callback = SpriteCB_SparklesBig
|
|
};
|
|
|
|
static const struct OamData sOam_GameFreakLogo = {
|
|
.affineMode = ST_OAM_AFFINE_OFF,
|
|
.objMode = ST_OAM_OBJ_BLEND,
|
|
.mosaic = FALSE,
|
|
.bpp = ST_OAM_4BPP,
|
|
.shape = SPRITE_SHAPE(32x64),
|
|
.matrixNum = 0,
|
|
.size = SPRITE_SIZE(32x64),
|
|
.tileNum = 0x000,
|
|
.priority = 3,
|
|
.paletteNum = 0
|
|
};
|
|
|
|
static const struct SpriteTemplate sSpriteTemplate_GameFreakLogoArt = {
|
|
.tileTag = GFXTAG_GF_LOGO,
|
|
.paletteTag = PALTAG_GF,
|
|
.oam = &sOam_GameFreakLogo,
|
|
.anims = gDummySpriteAnimTable,
|
|
.images = NULL,
|
|
.affineAnims = gDummySpriteAffineAnimTable,
|
|
.callback = SpriteCallbackDummy
|
|
};
|
|
|
|
static const struct OamData sOam_PresentsText = {
|
|
.affineMode = ST_OAM_AFFINE_OFF,
|
|
.objMode = ST_OAM_OBJ_BLEND,
|
|
.mosaic = FALSE,
|
|
.bpp = ST_OAM_4BPP,
|
|
.shape = SPRITE_SHAPE(32x8),
|
|
.matrixNum = 0,
|
|
.size = SPRITE_SIZE(32x8),
|
|
.tileNum = 0x000,
|
|
.priority = 3,
|
|
.paletteNum = 0
|
|
};
|
|
|
|
static const struct SpriteTemplate sSpriteTemplate_Presents = {
|
|
.tileTag = GFXTAG_PRESENTS,
|
|
.paletteTag = PALTAG_GF,
|
|
.oam = &sOam_PresentsText,
|
|
.anims = gDummySpriteAnimTable,
|
|
.images = NULL,
|
|
.affineAnims = gDummySpriteAffineAnimTable,
|
|
.callback = SpriteCallbackDummy
|
|
};
|
|
|
|
static const struct OamData sOam_Scene3_Nidorino = {
|
|
.affineMode = ST_OAM_AFFINE_DOUBLE,
|
|
.objMode = ST_OAM_OBJ_NORMAL,
|
|
.mosaic = FALSE,
|
|
.bpp = ST_OAM_4BPP,
|
|
.shape = SPRITE_SHAPE(64x64),
|
|
.matrixNum = 0,
|
|
.size = SPRITE_SIZE(64x64),
|
|
.tileNum = 0x000,
|
|
.priority = 1,
|
|
.paletteNum = 0
|
|
};
|
|
|
|
static const union AnimCmd sAnim_Scene3_Nidorino_Normal[] = {
|
|
ANIMCMD_FRAME(0, 1),
|
|
ANIMCMD_END
|
|
};
|
|
|
|
static const union AnimCmd sAnim_Scene3_Nidorino_Cry[] = {
|
|
ANIMCMD_FRAME(64, 1),
|
|
ANIMCMD_END
|
|
};
|
|
|
|
static const union AnimCmd sAnim_Scene3_Nidorino_Crouch[] = {
|
|
ANIMCMD_FRAME(128, 1),
|
|
ANIMCMD_END
|
|
};
|
|
|
|
static const union AnimCmd sAnim_Scene3_Nidorino_Hop[] = {
|
|
ANIMCMD_FRAME(192, 1),
|
|
ANIMCMD_END
|
|
};
|
|
|
|
static const union AnimCmd sAnim_Scene3_Nidorino_Attack[] = {
|
|
ANIMCMD_FRAME(256, 1),
|
|
ANIMCMD_END
|
|
};
|
|
|
|
static const union AnimCmd *const sAnims_Scene3_Nidorino[] = {
|
|
[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_Normal[] = {
|
|
AFFINEANIMCMD_FRAME(256, 256, 0, 0),
|
|
AFFINEANIMCMD_END
|
|
};
|
|
|
|
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[] = {
|
|
[AFFINEANIM_NORMAL] = sAffineAnim_Scene3_Mons_Normal,
|
|
[AFFINEANIM_ZOOM] = sAffineAnim_Scene3_Mons_Zoom
|
|
};
|
|
|
|
static const struct SpriteTemplate sSpriteTemplate_Scene3_Nidorino = {
|
|
.tileTag = GFXTAG_SCENE3_NIDORINO,
|
|
.paletteTag = PALTAG_NIDORINO,
|
|
.oam = &sOam_Scene3_Nidorino,
|
|
.anims = sAnims_Scene3_Nidorino,
|
|
.images = NULL,
|
|
.affineAnims = sAffineAnims_Scene3_Mons,
|
|
.callback = SpriteCallbackDummy
|
|
};
|
|
|
|
static const struct OamData sOam_Scene2_Mons = {
|
|
.affineMode = ST_OAM_AFFINE_OFF,
|
|
.objMode = ST_OAM_OBJ_NORMAL,
|
|
.mosaic = FALSE,
|
|
.bpp = ST_OAM_4BPP,
|
|
.shape = SPRITE_SHAPE(64x64),
|
|
.matrixNum = 0,
|
|
.size = SPRITE_SIZE(64x64),
|
|
.tileNum = 0x000,
|
|
.priority = 1,
|
|
.paletteNum = 0
|
|
};
|
|
|
|
static const struct SpriteTemplate sSpriteTemplate_Scene2_Nidorino = {
|
|
.tileTag = GFXTAG_SCENE2_NIDORINO,
|
|
.paletteTag = PALTAG_NIDORINO,
|
|
.oam = &sOam_Scene2_Mons,
|
|
.anims = gDummySpriteAnimTable,
|
|
.images = NULL,
|
|
.affineAnims = gDummySpriteAffineAnimTable,
|
|
.callback = SpriteCallbackDummy
|
|
};
|
|
|
|
static const struct SpriteTemplate sSpriteTemplate_Scene2_Gengar = {
|
|
.tileTag = GFXTAG_SCENE2_GENGAR,
|
|
.paletteTag = PALTAG_GENGAR,
|
|
.oam = &sOam_Scene2_Mons,
|
|
.anims = gDummySpriteAnimTable,
|
|
.images = NULL,
|
|
.affineAnims = gDummySpriteAffineAnimTable,
|
|
.callback = SpriteCallbackDummy
|
|
};
|
|
|
|
static const struct OamData sOam_Grass = {
|
|
.affineMode = ST_OAM_AFFINE_OFF,
|
|
.objMode = ST_OAM_OBJ_NORMAL,
|
|
.mosaic = FALSE,
|
|
.bpp = ST_OAM_4BPP,
|
|
.shape = SPRITE_SHAPE(64x32),
|
|
.matrixNum = 0,
|
|
.size = SPRITE_SIZE(64x32),
|
|
.tileNum = 0x000,
|
|
.priority = 0,
|
|
.paletteNum = 0
|
|
};
|
|
|
|
static const union AnimCmd sAnim_Grass_Static[] = {
|
|
ANIMCMD_FRAME(0, 0),
|
|
ANIMCMD_END
|
|
};
|
|
|
|
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_Static,
|
|
sAnim_Grass_Rustle // Unused
|
|
};
|
|
|
|
static const struct SpriteTemplate sSpriteTemplate_Grass = {
|
|
.tileTag = GFXTAG_SCENE3_GRASS,
|
|
.paletteTag = PALTAG_SCENE3_GRASS,
|
|
.oam = &sOam_Grass,
|
|
.anims = sAnims_Grass,
|
|
.images = NULL,
|
|
.affineAnims = gDummySpriteAffineAnimTable,
|
|
.callback = SpriteCallbackDummy
|
|
};
|
|
|
|
static const struct OamData sOam_Scene3_Gengar = {
|
|
.affineMode = ST_OAM_AFFINE_DOUBLE,
|
|
.objMode = ST_OAM_OBJ_NORMAL,
|
|
.mosaic = FALSE,
|
|
.bpp = ST_OAM_4BPP,
|
|
.shape = SPRITE_SHAPE(64x64),
|
|
.matrixNum = 0,
|
|
.size = SPRITE_SIZE(64x64),
|
|
.tileNum = 0x000,
|
|
.priority = 1,
|
|
.paletteNum = 0
|
|
};
|
|
|
|
static const union AnimCmd sAnim_Scene3_Gengar_TopLeft[] = {
|
|
ANIMCMD_FRAME(0, 0),
|
|
ANIMCMD_END
|
|
};
|
|
|
|
static const union AnimCmd sAnim_Scene3_Gengar_TopRight[] = {
|
|
ANIMCMD_FRAME(64, 0),
|
|
ANIMCMD_END
|
|
};
|
|
|
|
static const union AnimCmd sAnim_Scene3_Gengar_BottomLeft[] = {
|
|
ANIMCMD_FRAME(96, 0),
|
|
ANIMCMD_END
|
|
};
|
|
|
|
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_TopLeft,
|
|
sAnim_Scene3_Gengar_TopRight,
|
|
sAnim_Scene3_Gengar_BottomLeft,
|
|
sAnim_Scene3_Gengar_BottomRight
|
|
};
|
|
|
|
static const struct SpriteTemplate sSpriteTemplate_Scene3_Gengar = {
|
|
.tileTag = GFXTAG_SCENE3_GENGAR,
|
|
.paletteTag = PALTAG_GENGAR,
|
|
.oam = &sOam_Scene3_Gengar,
|
|
.anims = sAnims_Scene3_Gengar,
|
|
.images = NULL,
|
|
.affineAnims = sAffineAnims_Scene3_Mons,
|
|
.callback = SpriteCallbackDummy
|
|
};
|
|
|
|
static const struct OamData sOam_Swipe = {
|
|
.affineMode = ST_OAM_AFFINE_OFF,
|
|
.objMode = ST_OAM_OBJ_NORMAL,
|
|
.mosaic = FALSE,
|
|
.bpp = ST_OAM_4BPP,
|
|
.shape = SPRITE_SHAPE(32x64),
|
|
.matrixNum = 0,
|
|
.size = SPRITE_SIZE(32x64),
|
|
.tileNum = 0x000,
|
|
.priority = 1,
|
|
.paletteNum = 0
|
|
};
|
|
|
|
static const union AnimCmd sAnim_Swipe_Top[] = {
|
|
ANIMCMD_FRAME(0, 8),
|
|
ANIMCMD_FRAME(32, 4),
|
|
ANIMCMD_END
|
|
};
|
|
|
|
static const union AnimCmd sAnim_Swipe_Bottom[] = {
|
|
ANIMCMD_FRAME(64, 8),
|
|
ANIMCMD_FRAME(72, 4),
|
|
ANIMCMD_END
|
|
};
|
|
|
|
static const union AnimCmd *const sAnims_Swipe[] = {
|
|
[ANIM_SWIPE_TOP] = sAnim_Swipe_Top,
|
|
[ANIM_SWIPE_BOTTOM] = sAnim_Swipe_Bottom
|
|
};
|
|
|
|
static const struct SpriteTemplate sSpriteTemplate_GengarSwipe = {
|
|
.tileTag = GFXTAG_SCENE3_SWIPE,
|
|
.paletteTag = PALTAG_SCENE3_SWIPE,
|
|
.oam = &sOam_Swipe,
|
|
.anims = sAnims_Swipe,
|
|
.images = NULL,
|
|
.affineAnims = gDummySpriteAffineAnimTable,
|
|
.callback = SpriteCB_GengarSwipe
|
|
};
|
|
|
|
static const struct OamData sOam_RecoilDust = {
|
|
.affineMode = ST_OAM_AFFINE_OFF,
|
|
.objMode = ST_OAM_OBJ_NORMAL,
|
|
.mosaic = FALSE,
|
|
.bpp = ST_OAM_4BPP,
|
|
.shape = SPRITE_SHAPE(16x16),
|
|
.matrixNum = 0,
|
|
.size = SPRITE_SIZE(16x16),
|
|
.tileNum = 0x000,
|
|
.priority = 1,
|
|
.paletteNum = 0
|
|
};
|
|
|
|
static const union AnimCmd sAnim_RecoilDust[] = {
|
|
ANIMCMD_FRAME(0, 10),
|
|
ANIMCMD_FRAME(4, 10),
|
|
ANIMCMD_FRAME(8, 10),
|
|
ANIMCMD_FRAME(12, 8),
|
|
ANIMCMD_END
|
|
};
|
|
|
|
static const union AnimCmd *const sAnims_RecoilDust[] = {
|
|
sAnim_RecoilDust
|
|
};
|
|
|
|
static const struct SpriteTemplate sSpriteTemplate_NidorinoRecoilDust = {
|
|
.tileTag = GFXTAG_SCENE3_RECOIL_DUST,
|
|
.paletteTag = PALTAG_SCENE3_RECOIL_DUST,
|
|
.oam = &sOam_RecoilDust,
|
|
.anims = sAnims_RecoilDust,
|
|
.images = NULL,
|
|
.affineAnims = gDummySpriteAffineAnimTable,
|
|
.callback = SpriteCB_RecoilDust
|
|
};
|
|
|
|
static const struct CompressedSpriteSheet sFightSceneSpriteSheets[] = {
|
|
{sScene2_Gengar_Gfx, 0x800, GFXTAG_SCENE2_GENGAR},
|
|
{sScene2_Nidorino_Gfx, 0x800, GFXTAG_SCENE2_NIDORINO},
|
|
{sScene3_Nidorino_Gfx, 0x2800, GFXTAG_SCENE3_NIDORINO},
|
|
{sScene3_Grass_Gfx, 0x800, GFXTAG_SCENE3_GRASS},
|
|
{sScene3_GengarStatic_Gfx, 0x1800, GFXTAG_SCENE3_GENGAR},
|
|
{sScene3_Swipe_Gfx, 0xA00, GFXTAG_SCENE3_SWIPE},
|
|
{sScene3_RecoilDust_Gfx, 0x200, GFXTAG_SCENE3_RECOIL_DUST}
|
|
};
|
|
|
|
// POTENTIAL UB
|
|
// 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},
|
|
{sScene3_RecoilDust_Pal, PALTAG_SCENE3_RECOIL_DUST},
|
|
#ifdef BUGFIX
|
|
{0}
|
|
#endif
|
|
};
|
|
|
|
static void VBlankCB_Copyright(void)
|
|
{
|
|
LoadOam();
|
|
ProcessSpriteCopyRequests();
|
|
TransferPlttBuffer();
|
|
}
|
|
|
|
static void CB2_WaitFadeBeforeSetUpIntro(void)
|
|
{
|
|
if (!UpdatePaletteFade())
|
|
SetMainCallback2(CB2_SetUpIntro);
|
|
}
|
|
|
|
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, sizeof(sCopyright_Pal));
|
|
}
|
|
|
|
static void SerialCB_CopyrightScreen(void)
|
|
{
|
|
GameCubeMultiBoot_HandleSerialInterrupt(&sGcmb);
|
|
}
|
|
|
|
static bool8 SetUpCopyrightScreen(void)
|
|
{
|
|
switch (gMain.state)
|
|
{
|
|
case 0:
|
|
SetVBlankCallback(NULL);
|
|
SetGpuReg(REG_OFFSET_BLDCNT, 0);
|
|
SetGpuReg(REG_OFFSET_BLDALPHA, 0);
|
|
SetGpuReg(REG_OFFSET_BLDY, 0);
|
|
((vu16*)PLTT)[0] = RGB_WHITE;
|
|
SetGpuReg(REG_OFFSET_DISPCNT, 0);
|
|
SetGpuReg(REG_OFFSET_BG0HOFS, 0);
|
|
SetGpuReg(REG_OFFSET_BG0VOFS, 0);
|
|
DmaFill16(3, 0, VRAM, VRAM_SIZE);
|
|
DmaFill32(3, 0, OAM, OAM_SIZE);
|
|
DmaFill16(3, 0, PLTT + sizeof(vu16), PLTT_SIZE - sizeof(vu16));
|
|
ResetPaletteFade();
|
|
LoadCopyrightGraphics(0 * BG_CHAR_SIZE, 7 * BG_SCREEN_SIZE, BG_PLTT_ID(0));
|
|
ScanlineEffect_Stop();
|
|
ResetTasks();
|
|
ResetSpriteData();
|
|
FreeAllSpritePalettes();
|
|
BeginNormalPaletteFade(PALETTES_ALL, 0, 16, 0, RGB_WHITEALPHA);
|
|
SetGpuReg(REG_OFFSET_BG0CNT, BGCNT_PRIORITY(0) | BGCNT_CHARBASE(0) | BGCNT_16COLOR | BGCNT_SCREENBASE(7));
|
|
EnableInterrupts(INTR_FLAG_VBLANK);
|
|
SetVBlankCallback(VBlankCB_Copyright);
|
|
SetGpuReg(REG_OFFSET_DISPCNT, DISPCNT_OBJ_1D_MAP | DISPCNT_BG0_ON);
|
|
SetSerialCallback(SerialCB_CopyrightScreen);
|
|
GameCubeMultiBoot_Init(&sGcmb);
|
|
// fallthrough
|
|
default:
|
|
UpdatePaletteFade();
|
|
gMain.state++;
|
|
GameCubeMultiBoot_Main(&sGcmb);
|
|
break;
|
|
case 140:
|
|
GameCubeMultiBoot_Main(&sGcmb);
|
|
if (sGcmb.gcmb_field_2 != 1)
|
|
{
|
|
BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 16, RGB_BLACK);
|
|
gMain.state++;
|
|
}
|
|
break;
|
|
case 141:
|
|
if (!UpdatePaletteFade())
|
|
{
|
|
gMain.state++;
|
|
if (sGcmb.gcmb_field_2 != 0)
|
|
{
|
|
if (sGcmb.gcmb_field_2 == 2)
|
|
{
|
|
if (*(u32 *)(EWRAM_START + 0xAC) == COLOSSEUM_GAME_CODE)
|
|
{
|
|
CpuCopy16(gMultiBootProgram_PokemonColosseum_Start, (void *)EWRAM_START, 0x28000);
|
|
*(u32 *)(EWRAM_START + 0xAC) = COLOSSEUM_GAME_CODE;
|
|
}
|
|
GameCubeMultiBoot_ExecuteProgram(&sGcmb);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
GameCubeMultiBoot_Quit();
|
|
SetSerialCallback(SerialCB);
|
|
}
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case 142:
|
|
ResetSerial();
|
|
SetMainCallback2(CB2_WaitFadeBeforeSetUpIntro);
|
|
break;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
void CB2_InitCopyrightScreenAfterBootup(void)
|
|
{
|
|
if (!SetUpCopyrightScreen())
|
|
{
|
|
SeedRngAndSetTrainerId();
|
|
SetSaveBlocksPointers(GetSaveBlocksPointersBaseOffset());
|
|
ResetMenuAndMonGlobals();
|
|
Save_ResetSaveCounters();
|
|
LoadGameSave(SAVE_NORMAL);
|
|
if (gSaveFileStatus == SAVE_STATUS_EMPTY || gSaveFileStatus == SAVE_STATUS_INVALID)
|
|
Sav2_ClearSetDefault();
|
|
SetPokemonCryStereo(gSaveBlock2Ptr->optionsSound);
|
|
InitHeap(gHeap, HEAP_SIZE);
|
|
}
|
|
}
|
|
|
|
void CB2_InitCopyrightScreenAfterTitleScreen(void)
|
|
{
|
|
SetUpCopyrightScreen();
|
|
}
|
|
|
|
static void CB2_SetUpIntro(void)
|
|
{
|
|
switch (gMain.state)
|
|
{
|
|
default:
|
|
gMain.state = 0;
|
|
// fallthrough
|
|
case 0:
|
|
SetVBlankCallback(NULL);
|
|
SetGpuReg(REG_OFFSET_DISPCNT, 0);
|
|
InitHeap(gHeap, HEAP_SIZE);
|
|
ResetTasks();
|
|
ResetSpriteData();
|
|
ResetPaletteFade();
|
|
ResetTempTileDataBuffers();
|
|
Intro_ResetGpuRegs();
|
|
DmaFill16(3, 0, VRAM, VRAM_SIZE);
|
|
DmaFill32(3, 0, OAM, OAM_SIZE);
|
|
DmaFill16(3, 0, PLTT, PLTT_SIZE);
|
|
FillPalette(RGB_BLACK, 0, PLTT_SIZE);
|
|
ResetBgsAndClearDma3BusyFlags(FALSE);
|
|
InitBgsFromTemplates(0, sBgTemplates_GameFreakScene, ARRAY_COUNT(sBgTemplates_GameFreakScene));
|
|
break;
|
|
case 1:
|
|
LoadPalette(sGameFreakBg_Pal, BG_PLTT_ID(0), sizeof(sGameFreakBg_Pal));
|
|
DecompressAndCopyTileDataToVram(BG_GF_BACKGROUND, sGameFreakBg_Gfx, 0, 0, 0);
|
|
DecompressAndCopyTileDataToVram(BG_GF_BACKGROUND, sGameFreakBg_Map, 0, 0, 1);
|
|
LoadPalette(sGameFreakLogo_Pal, BG_PLTT_ID(13), sizeof(sGameFreakLogo_Pal));
|
|
break;
|
|
case 2:
|
|
if (!FreeTempTileDataBuffersIfPossible())
|
|
{
|
|
StartIntroSequence();
|
|
BlendPalettes(PALETTES_ALL, 16, RGB_BLACK);
|
|
SetMainCallback2(CB2_Intro);
|
|
SetVBlankCallback(VBlankCB_Intro);
|
|
}
|
|
return;
|
|
}
|
|
gMain.state++;
|
|
}
|
|
|
|
static void CB2_Intro(void)
|
|
{
|
|
RunTasks();
|
|
AnimateSprites();
|
|
BuildOamBuffer();
|
|
UpdatePaletteFade();
|
|
}
|
|
|
|
static void VBlankCB_Intro(void)
|
|
{
|
|
LoadOam();
|
|
ProcessSpriteCopyRequests();
|
|
TransferPlttBuffer();
|
|
}
|
|
|
|
static void Intro_ResetGpuRegs(void)
|
|
{
|
|
SetGpuReg(REG_OFFSET_DISPCNT, 0);
|
|
SetGpuReg(REG_OFFSET_BLDCNT, 0);
|
|
SetGpuReg(REG_OFFSET_BLDALPHA, 0);
|
|
SetGpuReg(REG_OFFSET_BLDY, 0);
|
|
SetGpuReg(REG_OFFSET_BG0HOFS, 0);
|
|
SetGpuReg(REG_OFFSET_BG0VOFS, 0);
|
|
SetGpuReg(REG_OFFSET_BG1HOFS, 0);
|
|
SetGpuReg(REG_OFFSET_BG1VOFS, 0);
|
|
SetGpuReg(REG_OFFSET_BG2HOFS, 0);
|
|
SetGpuReg(REG_OFFSET_BG2VOFS, 0);
|
|
SetGpuReg(REG_OFFSET_BG3HOFS, 0);
|
|
SetGpuReg(REG_OFFSET_BG3VOFS, 0);
|
|
SetGpuRegBits(REG_OFFSET_DISPCNT, DISPCNT_OBJ_1D_MAP | DISPCNT_OBJ_ON);
|
|
}
|
|
|
|
static void StartIntroSequence(void)
|
|
{
|
|
struct IntroSequenceData * ptr = Alloc(sizeof(*ptr));
|
|
SetIntroCB(ptr, IntroCB_Init);
|
|
ptr->taskId = CreateTask(Task_CallIntroCallback, 3);
|
|
SetWordTaskArg(ptr->taskId, 0, (uintptr_t)ptr);
|
|
}
|
|
|
|
static void SetIntroCB(struct IntroSequenceData * ptr, IntroCallback cb)
|
|
{
|
|
ptr->callback = cb;
|
|
ptr->state = 0;
|
|
}
|
|
|
|
static void Task_CallIntroCallback(u8 taskId)
|
|
{
|
|
struct IntroSequenceData * ptr = (void *)GetWordTaskArg(taskId, 0);
|
|
|
|
// 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);
|
|
}
|
|
|
|
static void IntroCB_Init(struct IntroSequenceData * this)
|
|
{
|
|
switch (this->state)
|
|
{
|
|
case 0:
|
|
InitWindows(sWindowTemplates);
|
|
LZ77UnCompWram(sGameFreakText_Gfx, this->gameFreakTextGfx);
|
|
LZ77UnCompWram(sGameFreakLogo_Gfx, this->gameFreakLogoGfx);
|
|
FillBgTilemapBufferRect(BG_GF_TEXT_LOGO, 0x000, 0, 0, 32, 32, 17);
|
|
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_GF_OpenWindow);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void IntroCB_GF_OpenWindow(struct IntroSequenceData * this)
|
|
{
|
|
switch (this->state)
|
|
{
|
|
case 0:
|
|
SetGpuRegBits(REG_OFFSET_DISPCNT, DISPCNT_WIN1_ON);
|
|
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(BG_GF_BACKGROUND);
|
|
BlendPalettes(PALETTES_ALL, 0, RGB_BLACK);
|
|
this->state++;
|
|
break;
|
|
case 2:
|
|
// 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_GF_Star(struct IntroSequenceData * this)
|
|
{
|
|
switch (this->state)
|
|
{
|
|
case 0:
|
|
PlaySE(MUS_GAME_FREAK);
|
|
GFScene_LoadGfxCreateStar();
|
|
this->timer = 0;
|
|
this->state++;
|
|
break;
|
|
case 1:
|
|
if (++this->timer == 30)
|
|
{
|
|
GFScene_StartNameSparklesSmall();
|
|
this->timer = 0;
|
|
this->state++;
|
|
}
|
|
break;
|
|
case 2:
|
|
this->timer++;
|
|
if (this->timer == 90)
|
|
SetIntroCB(this, IntroCB_GF_RevealName);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void IntroCB_GF_RevealName(struct IntroSequenceData * this)
|
|
{
|
|
switch (this->state)
|
|
{
|
|
case 0:
|
|
GFScene_StartNameSparklesBig();
|
|
this->timer = 0;
|
|
this->state++;
|
|
break;
|
|
case 1:
|
|
if (++this->timer >= 40)
|
|
this->state++;
|
|
break;
|
|
case 2:
|
|
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(BG_GF_TEXT_LOGO);
|
|
this->state++;
|
|
break;
|
|
case 4:
|
|
if (!IsBlendTaskActive())
|
|
{
|
|
SetGpuReg(REG_OFFSET_BLDCNT, 0);
|
|
this->timer = 0;
|
|
this->state++;
|
|
}
|
|
break;
|
|
case 5:
|
|
if (++this->timer > 50)
|
|
SetIntroCB(this, IntroCB_GF_RevealLogo);
|
|
break;
|
|
}
|
|
}
|
|
|
|
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_ALL);
|
|
StartBlendTask(0, 16, 16, 0, 16, 0);
|
|
this->data[0] = 0; // Never read
|
|
this->data[1] = 16; // Never read
|
|
this->timer = 0;
|
|
this->state++;
|
|
break;
|
|
case 1:
|
|
this->gameFreakLogoArtSprite = GFScene_CreateLogoSprite();
|
|
this->state++;
|
|
break;
|
|
case 2:
|
|
if (!IsBlendTaskActive())
|
|
{
|
|
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;
|
|
case 3:
|
|
if (!IsDma3ManagerBusyWithBgCopy())
|
|
{
|
|
DestroySprite(this->gameFreakLogoArtSprite);
|
|
GFScene_CreatePresentsSprite();
|
|
this->timer = 0;
|
|
this->state++;
|
|
}
|
|
break;
|
|
case 4:
|
|
if (++this->timer > 90)
|
|
{
|
|
SetGpuRegBits(REG_OFFSET_BLDCNT, BLDCNT_TGT1_BG2);
|
|
StartBlendTask(16, 0, 0, 16, 20, 0);
|
|
this->state++;
|
|
}
|
|
break;
|
|
case 5:
|
|
if (!IsBlendTaskActive())
|
|
{
|
|
HideBg(BG_GF_TEXT_LOGO);
|
|
this->state++;
|
|
}
|
|
break;
|
|
case 6:
|
|
ResetSpriteData();
|
|
FreeAllSpritePalettes();
|
|
this->timer = 0;
|
|
this->state++;
|
|
break;
|
|
case 7:
|
|
if (++this->timer > 20)
|
|
{
|
|
SetGpuReg(REG_OFFSET_BLDCNT, 0);
|
|
SetIntroCB(this, IntroCB_Scene1);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void IntroCB_Scene1(struct IntroSequenceData * this)
|
|
{
|
|
switch (this->state)
|
|
{
|
|
case 0:
|
|
SetVBlankCallback(NULL);
|
|
LoadPalette(sScene1_Grass_Pal, BG_PLTT_ID(PALSLOT_SCENE1_GRASS), sizeof(sScene1_Grass_Pal));
|
|
LoadPalette(sScene1_Bg_Pal, BG_PLTT_ID(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);
|
|
LoadFightSceneSpriteGraphics();
|
|
SetVBlankCallback(VBlankCB_Intro);
|
|
this->state++;
|
|
break;
|
|
case 1:
|
|
if (!FreeTempTileDataBuffersIfPossible())
|
|
{
|
|
DecompressAndCopyTileDataToVram(BG_SCENE1_GRASS, sScene1_Grass_Gfx, 0, 0, 0);
|
|
DecompressAndCopyTileDataToVram(BG_SCENE1_GRASS, sScene1_Grass_Map, 0, 0, 1);
|
|
ResetBgPositions();
|
|
ShowBg(BG_SCENE1_BACKGROUND);
|
|
this->state++;
|
|
}
|
|
break;
|
|
case 2:
|
|
if (!FreeTempTileDataBuffersIfPossible())
|
|
{
|
|
ShowBg(BG_SCENE1_GRASS);
|
|
CreateTask(Scene1_Task_AnimateGrass, 0);
|
|
BeginNormalPaletteFade((1 << PALSLOT_SCENE1_GRASS) | (1 << PALSLOT_SCENE1_BG), -2, 16, 0, RGB_WHITE);
|
|
this->state++;
|
|
}
|
|
break;
|
|
case 3:
|
|
if (!gPaletteFade.active)
|
|
{
|
|
m4aSongNumStart(MUS_INTRO_FIGHT);
|
|
this->timer = 0;
|
|
this->state++;
|
|
}
|
|
break;
|
|
case 4:
|
|
if (++this->timer == 20)
|
|
{
|
|
// Start animation for transitioning to the next scene
|
|
CreateTask(Scene1_Task_BgZoom, 0);
|
|
Scene1_StartGrassScrolling();
|
|
}
|
|
if (this->timer >= 30)
|
|
{
|
|
// 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(Scene1_Task_AnimateGrass));
|
|
DestroyTask(FindTaskIdByFunc(Scene1_Task_BgZoom));
|
|
SetIntroCB(this, IntroCB_Scene2);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
#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;
|
|
|
|
// 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)
|
|
{
|
|
tTimer = 0;
|
|
if (++tFrame >= 3)
|
|
tFrame = 0;
|
|
ChangeBgY(BG_SCENE1_GRASS, tFrame << 15, BG_COORD_SET);
|
|
}
|
|
|
|
// 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)
|
|
{
|
|
tScroll += 0x120;
|
|
ChangeBgY(BG_SCENE1_GRASS, tScroll, BG_COORD_SUB);
|
|
}
|
|
}
|
|
|
|
static void Scene1_StartGrassScrolling(void)
|
|
{
|
|
u8 taskId = FindTaskIdByFunc(Scene1_Task_AnimateGrass);
|
|
gTasks[taskId].tExiting = TRUE;
|
|
}
|
|
|
|
#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 (++tTimer > 3)
|
|
{
|
|
tTimer = 0;
|
|
if (tFrame < 2)
|
|
tFrame++;
|
|
ChangeBgY(BG_SCENE1_BACKGROUND, tFrame << 15, BG_COORD_SET);
|
|
}
|
|
}
|
|
|
|
#undef tTimer
|
|
#undef tFrame
|
|
|
|
static void IntroCB_Scene2(struct IntroSequenceData * this)
|
|
{
|
|
switch (this->state)
|
|
{
|
|
case 0:
|
|
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);
|
|
this->state++;
|
|
break;
|
|
case 1:
|
|
if (!FreeTempTileDataBuffersIfPossible())
|
|
{
|
|
SetVBlankCallback(NULL);
|
|
LoadPalette(sScene2_Bg_Pal, BG_PLTT_ID(1), sizeof(sScene2_Bg_Pal));
|
|
LoadPalette(sGengar_Pal, BG_PLTT_ID(5), sizeof(sGengar_Pal));
|
|
LoadPalette(sScene2_NidorinoClose_Pal, BG_PLTT_ID(6), 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);
|
|
DecompressAndCopyTileDataToVram(BG_SCENE2_NIDORINO, sScene2_NidorinoClose_Map, 0, 0, 1);
|
|
DecompressAndCopyTileDataToVram(BG_SCENE2_GENGAR, sScene2_GengarClose_Gfx, 0, 0, 0);
|
|
DecompressAndCopyTileDataToVram(BG_SCENE2_GENGAR, sScene2_GengarClose_Map, 0, 0, 1);
|
|
ResetBgPositions();
|
|
ShowBg(BG_SCENE2_PLANTS);
|
|
HideBg(BG_SCENE2_NIDORINO); // Hide bgs for the close up shot
|
|
HideBg(BG_SCENE2_GENGAR);
|
|
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++;
|
|
}
|
|
break;
|
|
case 2:
|
|
if (!FreeTempTileDataBuffersIfPossible())
|
|
{
|
|
BeginNormalPaletteFade(PALETTES_ALL & ~1, -2, 16, 0, RGB_WHITE);
|
|
this->state++;
|
|
}
|
|
break;
|
|
case 3:
|
|
if (!gPaletteFade.active)
|
|
{
|
|
this->timer = 0;
|
|
this->state++;
|
|
}
|
|
break;
|
|
case 4:
|
|
if (++this->timer >= 60)
|
|
{
|
|
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);
|
|
ShowBg(BG_SCENE2_GENGAR);
|
|
this->state++;
|
|
}
|
|
break;
|
|
case 5:
|
|
if (!IsDma3ManagerBusyWithBgCopy())
|
|
{
|
|
this->timer = 0;
|
|
this->state++;
|
|
}
|
|
break;
|
|
case 6:
|
|
if (++this->timer >= 60)
|
|
{
|
|
DestroyTask(FindTaskIdByFunc(Scene2_Task_PanMons));
|
|
SetIntroCB(this, IntroCB_Scene3_Entrance);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// 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, BG_COORD_SUB);
|
|
ChangeBgX(BG_SCENE2_PLANTS, 0x110, BG_COORD_ADD);
|
|
}
|
|
|
|
// Pan Gengar up and Nidorino down in the close up shot
|
|
static void Scene2_Task_PanMons(u8 taskId)
|
|
{
|
|
ChangeBgY(BG_SCENE2_GENGAR, 0x020, BG_COORD_ADD);
|
|
ChangeBgY(BG_SCENE2_NIDORINO, 0x024, BG_COORD_SUB);
|
|
}
|
|
|
|
// Create the Gengar/Nidorino sprites for the wide shot in scene 2
|
|
static void Scene2_CreateMonSprites(struct IntroSequenceData * this)
|
|
{
|
|
u8 spriteId;
|
|
|
|
this->scene2GengarSprite = NULL;
|
|
this->scene2NidorinoSprite = NULL;
|
|
|
|
spriteId = CreateSprite(&sSpriteTemplate_Scene2_Nidorino, 168, 80, 11);
|
|
if (spriteId != MAX_SPRITES)
|
|
this->scene2NidorinoSprite = &gSprites[spriteId];
|
|
|
|
spriteId = CreateSprite(&sSpriteTemplate_Scene2_Gengar, 72, 80, 12);
|
|
if (spriteId != MAX_SPRITES)
|
|
this->scene2GengarSprite = &gSprites[spriteId];
|
|
}
|
|
|
|
static void Scene2_DestroyMonSprites(struct IntroSequenceData * this)
|
|
{
|
|
if (this->scene2GengarSprite != NULL)
|
|
DestroySprite(this->scene2GengarSprite);
|
|
if (this->scene2NidorinoSprite != NULL)
|
|
DestroySprite(this->scene2NidorinoSprite);
|
|
}
|
|
|
|
// 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, BG_PLTT_ID(1), sizeof(sScene3_Bg_Pal));
|
|
LoadPalette(sGengar_Pal, BG_PLTT_ID(5), 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);
|
|
HideBg(BG_SCENE3_GENGAR);
|
|
HideBg(BG_SCENE3_UNUSED1);
|
|
HideBg(BG_SCENE3_UNUSED2);
|
|
ResetBgPositions();
|
|
this->state++;
|
|
SetGpuRegBits(REG_OFFSET_DISPCNT, DISPCNT_WIN0_ON);
|
|
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);
|
|
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(PALETTES_ALL & ~1, 0, RGB_WHITE);
|
|
ShowBg(BG_SCENE3_GENGAR);
|
|
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->timer == 16)
|
|
Scene3_CreateGrassSprite(this);
|
|
if (!Scene3_IsNidorinoEntering(this) && !FuncIsActiveTask(Scene3_Task_GengarEnter))
|
|
SetIntroCB(this, IntroCB_Scene3_Fight);
|
|
break;
|
|
}
|
|
}
|
|
|
|
#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].tSlow)
|
|
ChangeBgX(BG_SCENE3_BACKGROUND, 0x400, BG_COORD_SUB);
|
|
else
|
|
ChangeBgX(BG_SCENE3_BACKGROUND, 0x020, BG_COORD_SUB);
|
|
}
|
|
|
|
static void Scene3_StartBgScroll(void)
|
|
{
|
|
CreateTask(Scene3_Task_BgScroll, 0);
|
|
}
|
|
|
|
static void Scene3_SlowBgScroll(void)
|
|
{
|
|
u8 taskId = FindTaskIdByFunc(Scene3_Task_BgScroll);
|
|
gTasks[taskId].tSlow = TRUE;
|
|
}
|
|
|
|
#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 (!tPaused)
|
|
{
|
|
if (++tTimer >= 30)
|
|
{
|
|
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 Scene3_PauseGengarBounce(void)
|
|
{
|
|
u8 taskId = FindTaskIdByFunc(Scene3_Task_GengarBounce);
|
|
gTasks[taskId].tPaused = TRUE;
|
|
}
|
|
|
|
static void Scene3_ResumeGengarBounce(void)
|
|
{
|
|
u8 taskId = FindTaskIdByFunc(Scene3_Task_GengarBounce);
|
|
gTasks[taskId].tPaused = FALSE;
|
|
}
|
|
|
|
static bool8 Scene3_IsGengarMidBounce(void)
|
|
{
|
|
u8 taskId = FindTaskIdByFunc(Scene3_Task_GengarBounce);
|
|
return gTasks[taskId].tState;
|
|
}
|
|
|
|
#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->scene3GrassSprite = &gSprites[spriteId];
|
|
this->scene3GrassSprite->callback = SpriteCB_Grass;
|
|
}
|
|
else
|
|
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 (sState)
|
|
{
|
|
case 0:
|
|
sBaseX = sprite->x << 5;
|
|
sVeloc = 160;
|
|
sState++;
|
|
// fallthrough
|
|
case 1:
|
|
sBaseX -= sVeloc;
|
|
sprite->x = sBaseX >> 5;
|
|
if (sprite->x <= 52)
|
|
{
|
|
Scene3_SlowBgScroll();
|
|
sState++;
|
|
}
|
|
break;
|
|
case 2:
|
|
sBaseX -= 32;
|
|
sprite->x = sBaseX >> 5;
|
|
if (sprite->x <= -32)
|
|
{
|
|
sprite->invisible = TRUE;
|
|
sprite->sState++;
|
|
DestroySprite(sprite);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
#undef sState
|
|
#undef sBaseX
|
|
#undef sVeloc
|
|
|
|
static void IntroCB_Scene3_Fight(struct IntroSequenceData * this)
|
|
{
|
|
switch (this->state)
|
|
{
|
|
case 0:
|
|
this->timer = 0;
|
|
this->state++;
|
|
break;
|
|
case 1:
|
|
if (++this->timer > 30)
|
|
{
|
|
Scene3_StartNidorinoCry(this);
|
|
this->state++;
|
|
}
|
|
break;
|
|
case 2:
|
|
if (!Scene3_NidorinoAnimIsRunning(this))
|
|
{
|
|
this->timer = 0;
|
|
this->state++;
|
|
}
|
|
break;
|
|
case 3:
|
|
if (++this->timer > 30)
|
|
{
|
|
Scene3_PauseGengarBounce();
|
|
Scene3_StartGengarAttack(this);
|
|
this->timer = 0;
|
|
this->state++;
|
|
}
|
|
break;
|
|
case 4:
|
|
if (this->gengarAttackLanded)
|
|
{
|
|
Scene3_StartNidorinoRecoil(this);
|
|
this->state++;
|
|
}
|
|
break;
|
|
case 5:
|
|
if (!Scene3_NidorinoAnimIsRunning(this))
|
|
{
|
|
Scene3_ResumeGengarBounce();
|
|
this->timer = 0;
|
|
this->state++;
|
|
}
|
|
break;
|
|
case 6:
|
|
if (++this->timer > 16)
|
|
{
|
|
// Nidorino's 1st hop backwards in preparation to attack
|
|
Scene3_StartNidorinoHop(this->scene3NidorinoSprite, 8, 12, 5);
|
|
this->state++;
|
|
}
|
|
break;
|
|
case 7:
|
|
if (!Scene3_NidorinoAnimIsRunning(this))
|
|
{
|
|
// Nidorino's 2nd hop backwards in preparation to attack
|
|
Scene3_StartNidorinoHop(this->scene3NidorinoSprite, 8, 12, 5);
|
|
this->state++;
|
|
}
|
|
break;
|
|
case 8:
|
|
if (!Scene3_NidorinoAnimIsRunning(this))
|
|
{
|
|
this->timer = 0;
|
|
this->state++;
|
|
}
|
|
break;
|
|
case 9:
|
|
if (++this->timer > 20)
|
|
{
|
|
Scene3_StartNidorinoAttack(this);
|
|
this->timer = 0;
|
|
this->state++;
|
|
}
|
|
break;
|
|
case 10:
|
|
if (!Scene3_IsGengarMidBounce())
|
|
{
|
|
Scene3_PauseGengarBounce();
|
|
Scene3_CreateGengarSprite(this);
|
|
this->state++;
|
|
}
|
|
break;
|
|
case 11:
|
|
HideBg(BG_SCENE3_GENGAR);
|
|
this->timer = 0;
|
|
this->state++;
|
|
break;
|
|
case 12:
|
|
if (++this->timer == 48)
|
|
BeginNormalPaletteFade((1 << 1) | (1 << 2), 2, 0, 16, RGB_WHITE);
|
|
if (this->timer > 120)
|
|
{
|
|
Scene3_NidorinoZoom(this);
|
|
Scene3_GengarZoom(this);
|
|
this->state++;
|
|
this->timer = 0;
|
|
}
|
|
break;
|
|
case 13:
|
|
if (++this->timer > 8)
|
|
{
|
|
CpuFill16(RGB_WHITE, &gPlttBufferUnfaded[BG_PLTT_ID(1)], 2 * PLTT_SIZE_4BPP);
|
|
BeginNormalPaletteFade(PALETTES_ALL & ~1, -2, 0, 16, RGB_BLACK);
|
|
this->state++;
|
|
}
|
|
break;
|
|
case 14:
|
|
if (!gPaletteFade.active)
|
|
{
|
|
this->timer = 0;
|
|
this->state++;
|
|
}
|
|
break;
|
|
case 15:
|
|
if (++this->timer > 60)
|
|
SetIntroCB(this, IntroCB_ExitToTitleScreen);
|
|
break;
|
|
default:
|
|
if (JOY_NEW(R_BUTTON))
|
|
{
|
|
BlendPalettes(PALETTES_OBJECTS | (1 << 2) | (1 << 5) | (1 << 6), 0, RGB_WHITE);
|
|
this->scene3NidorinoSprite->x2 = 0;
|
|
this->scene3NidorinoSprite->x = 180;
|
|
this->state = 1;
|
|
this->timer = 30;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void Scene3_CalcCenterToCornerVec(struct Sprite *sprite)
|
|
{
|
|
CalcCenterToCornerVec(sprite, sprite->oam.shape, sprite->oam.size, sprite->oam.affineMode);
|
|
}
|
|
|
|
static void Scene3_CreateGengarSprite(struct IntroSequenceData * this)
|
|
{
|
|
int i;
|
|
|
|
// Not using a subsprite table for this
|
|
for (i = 0; i < NUM_GENGAR_BACK_SPRITES; i++)
|
|
{
|
|
int x = (i & 1) * 48 + 49;
|
|
int y = (i / 2) * 64 + 72;
|
|
u8 spriteId = CreateSprite(&sSpriteTemplate_Scene3_Gengar, x, y, 8);
|
|
if (spriteId != MAX_SPRITES)
|
|
{
|
|
StartSpriteAnim(&gSprites[spriteId], i);
|
|
this->scene3GengarSprites[i] = &gSprites[spriteId];
|
|
if (i & 1)
|
|
this->scene3GengarSprites[i]->oam.shape = ST_OAM_V_RECTANGLE;
|
|
Scene3_CalcCenterToCornerVec(this->scene3GengarSprites[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void Scene3_NidorinoZoom(struct IntroSequenceData * this)
|
|
{
|
|
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_Idle(struct Sprite *sprite)
|
|
{
|
|
}
|
|
|
|
static void Scene3_GengarZoom(struct IntroSequenceData * this)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < NUM_GENGAR_BACK_SPRITES; i++)
|
|
{
|
|
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_ExitToTitleScreen(struct IntroSequenceData * this)
|
|
{
|
|
switch (this->state)
|
|
{
|
|
case 0:
|
|
FillPalette(RGB_BLACK, 0, PLTT_SIZE);
|
|
this->state++;
|
|
break;
|
|
case 1:
|
|
if (!FreeTempTileDataBuffersIfPossible())
|
|
{
|
|
DestroyTask(this->taskId);
|
|
Free(this);
|
|
DisableInterrupts(INTR_FLAG_HBLANK);
|
|
SetHBlankCallback(NULL);
|
|
SetMainCallback2(CB2_InitTitleScreen);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// 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 sStarSparklesRngSeed = 0;
|
|
|
|
for (i = 0; i < ARRAY_COUNT(sSpriteSheets_GameFreakScene); i++)
|
|
LoadCompressedSpriteSheet(&sSpriteSheets_GameFreakScene[i]);
|
|
LoadSpritePalettes(sSpritePalettes_GameFreakScene);
|
|
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].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);
|
|
}
|
|
}
|
|
|
|
// 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 = (random & sStarSparklesXmodMask) + 2;
|
|
s16 yMod = sYmod;
|
|
if (++sYmod > 3)
|
|
sYmod = -3;
|
|
x += xMod;
|
|
y += yMod;
|
|
if (x > 0 && x < DISPLAY_WIDTH)
|
|
{
|
|
spriteId = CreateSprite(&sSpriteTemplate_SparklesSmall, x, y, 1);
|
|
if (spriteId != MAX_SPRITES)
|
|
{
|
|
gSprites[spriteId].sSmSparkleStar_BaseX = x << sStarSparklesXprecision;
|
|
gSprites[spriteId].sSmSparkleStar_BaseY = y << sStarSparklesYprecision;
|
|
gSprites[spriteId].sSmSparkleStar_SpeedX = sStarSparklesXspeed * xMod;
|
|
gSprites[spriteId].sSmSparkleStar_SpeedY = sStarSparklesYspeed * yMod;
|
|
}
|
|
}
|
|
}
|
|
|
|
#define tSparkleIdx data[0]
|
|
#define tNumLoops data[1]
|
|
#define tTimer data[2]
|
|
|
|
static void GFScene_StartNameSparklesSmall(void)
|
|
{
|
|
CreateTask(GFScene_Task_NameSparklesSmall, 1);
|
|
}
|
|
|
|
// 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 i;
|
|
u8 spriteId;
|
|
|
|
tTimer++;
|
|
data[3]++; // Unused
|
|
if (tTimer > 6)
|
|
{
|
|
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 (++tNumLoops > 1)
|
|
DestroyTask(taskId);
|
|
else
|
|
tSparkleIdx = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
#undef tSparkleIdx
|
|
#undef tNumLoops
|
|
#undef tTimer
|
|
|
|
#define tTimer data[0]
|
|
#define tSparkleIdx data[1]
|
|
#define tNumSparkles data[2]
|
|
|
|
static void GFScene_StartNameSparklesBig(void)
|
|
{
|
|
CreateTask(GFScene_Task_NameSparklesBig, 2);
|
|
}
|
|
|
|
static void GFScene_Task_NameSparklesBig(u8 taskId)
|
|
{
|
|
s16 * data = gTasks[taskId].data;
|
|
u8 i;
|
|
|
|
if (tTimer == 0)
|
|
{
|
|
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 (++tTimer > 9)
|
|
tTimer = 0;
|
|
}
|
|
|
|
#undef tTimer
|
|
#undef tSparkleIdx
|
|
#undef tNumSparkles
|
|
|
|
static struct Sprite *GFScene_CreateLogoSprite(void)
|
|
{
|
|
u8 spriteId = CreateSprite(&sSpriteTemplate_GameFreakLogoArt, 120, 70, 4);
|
|
return &gSprites[spriteId];
|
|
}
|
|
|
|
static void GFScene_CreatePresentsSprite(void)
|
|
{
|
|
int i;
|
|
for (i = 0; i < 2; i++)
|
|
gSprites[CreateSprite(&sSpriteTemplate_Presents, 104 + 32 * i, 108, 5)].oam.tileNum += i * 4;
|
|
}
|
|
|
|
#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(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 Scene3_ApplyGengarAnim(int frame, int xSub, int ySub, int xBase)
|
|
{
|
|
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 Scene3_Task_GengarAttack(u8 taskId)
|
|
{
|
|
s16 * data = gTasks[taskId].data;
|
|
s32 xSub, ySub;
|
|
s32 sinIdx;
|
|
switch (tState)
|
|
{
|
|
case 0:
|
|
tFrame = 2; // Gengar raises arm up
|
|
tTimer = 0;
|
|
tMultY = 6;
|
|
tMultX = 32;
|
|
tState++;
|
|
break;
|
|
case 1:
|
|
// Gengar moves in a backward arc
|
|
tSinIdx -= 2;
|
|
if (++tTimer > 15)
|
|
{
|
|
tTimer = 0;
|
|
tState++;
|
|
}
|
|
break;
|
|
case 2:
|
|
// Gengar pauses at end of backward arc
|
|
if (++tTimer == 14)
|
|
((struct IntroSequenceData *)GetWordTaskArg(taskId, IDX_INTRO_DATA))->gengarAttackLanded = TRUE;
|
|
if (tTimer > 15)
|
|
{
|
|
tTimer = 0;
|
|
tState++;
|
|
}
|
|
break;
|
|
case 3:
|
|
// Gengar moves in a forward arc
|
|
tSinIdx += 8;
|
|
if (++tTimer == 4)
|
|
{
|
|
Scene3_CreateGengarSwipeSprites();
|
|
tMultY = 32;
|
|
tMultX = 48;
|
|
tFrame = 3; // Gengar swipes arm down
|
|
}
|
|
if (tTimer > 7)
|
|
{
|
|
tTimer = 0;
|
|
tState++;
|
|
}
|
|
break;
|
|
case 4:
|
|
// Gengar moves in a backward arc to its original position
|
|
tSinIdx -= 8;
|
|
if (++tTimer > 3)
|
|
{
|
|
tFrame = 0; // Gengar returns to normal posture
|
|
tSinIdx = 64;
|
|
tTimer = 0;
|
|
tState++;
|
|
}
|
|
break;
|
|
case 5:
|
|
DestroyTask(taskId);
|
|
return;
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
|
|
#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 = 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 = 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 ^= 1;
|
|
if (sprite->animEnded)
|
|
DestroySprite(sprite);
|
|
}
|
|
|
|
#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 sGengarScroll = 0;
|
|
|
|
switch (tState)
|
|
{
|
|
case 0:
|
|
tSpeed = 0x400;
|
|
tState++;
|
|
// fallthrough
|
|
case 1:
|
|
// 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 (sGengarScroll >= 0xEF00)
|
|
{
|
|
ChangeBgX(BG_SCENE3_GENGAR, 0xEF00, BG_COORD_SET);
|
|
DestroyTask(taskId);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
#undef tState
|
|
#undef tSpeed
|
|
#undef tMoves
|
|
|
|
static void SpriteCB_Star(struct Sprite *sprite)
|
|
{
|
|
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((u16*)&sprite->sStar_SparkleRngSeed, &random);
|
|
random = ISO_RANDOMIZE1(random);
|
|
StoreWordInTwoHalfwords((u16*)&sprite->sStar_SparkleRngSeed, random);
|
|
random >>= 16;
|
|
GFScene_CreateStarSparkle(sprite->x, sprite->y + sprite->y2, random);
|
|
}
|
|
if (sprite->x < -8)
|
|
DestroySprite(sprite);
|
|
}
|
|
|
|
// Callback for the sparkles that trail behind the star
|
|
static void SpriteCB_SparklesSmall_Star(struct Sprite *sprite)
|
|
{
|
|
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->sSmSparkleStar_Timer > sStarSparklesDestroySpriteTime)
|
|
DestroySprite(sprite);
|
|
}
|
|
if (sprite->y + sprite->y2 < 0 || sprite->y + sprite->y2 > DISPLAY_HEIGHT)
|
|
DestroySprite(sprite);
|
|
}
|
|
|
|
// Callback for the small sparkles during the "Game Freak" text reveal
|
|
static void SpriteCB_SparklesSmall_Name(struct Sprite *sprite)
|
|
{
|
|
if (sprite->sSmSparkleName_AnimTimer)
|
|
{
|
|
sprite->sSmSparkleName_AnimTimer--;
|
|
sprite->sSmSparkleName_BaseY++;
|
|
sprite->y = sprite->sSmSparkleName_BaseY >> 4;
|
|
if (sprite->y > 86)
|
|
{
|
|
sprite->y = 74;
|
|
sprite->sSmSparkleName_BaseY = 74 << 4;
|
|
}
|
|
if (sprite->animEnded)
|
|
{
|
|
if (sprite->sSmSparkleName_State == 0)
|
|
{
|
|
sprite->x += 26;
|
|
if (sprite->x > 188)
|
|
{
|
|
sprite->x = (188 * 2) - sprite->x;
|
|
sprite->sSmSparkleName_State = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sprite->x -= 26;
|
|
if (sprite->x < 52)
|
|
{
|
|
sprite->x = (52 * 2) - sprite->x;
|
|
sprite->sSmSparkleName_State = 0;
|
|
}
|
|
}
|
|
StartSpriteAnim(sprite, ANIM_SPARKLE_ONCE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (sprite->sSmSparkleName_NumLoops)
|
|
DestroySprite(sprite);
|
|
if (sprite->animEnded)
|
|
StartSpriteAnim(sprite, ANIM_SPARKLE_LOOP);
|
|
sprite->sSmSparkleName_BaseY += 4;
|
|
sprite->y = sprite->sSmSparkleName_BaseY >> 4;
|
|
if (++sprite->sSmSparkleName_DestroyTimer > 50)
|
|
DestroySprite(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 Scene3_CreateNidorinoSprite(struct IntroSequenceData * this)
|
|
{
|
|
u8 spriteId = CreateSprite(&sSpriteTemplate_Scene3_Nidorino, 0, 0, 9);
|
|
this->scene3NidorinoSprite = &gSprites[spriteId];
|
|
}
|
|
|
|
#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->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 = Scene3_SpriteCB_NidorinoEnter;
|
|
}
|
|
|
|
static void Scene3_SpriteCB_NidorinoEnter(struct Sprite *sprite)
|
|
{
|
|
if (++sprite->sTimer >= 40)
|
|
{
|
|
// Start decelerating after 40 frames
|
|
if (sprite->sSpeed > 1)
|
|
sprite->sSpeed--;
|
|
}
|
|
sprite->sX += sprite->sSpeed;
|
|
sprite->x = sprite->sX >> 4;
|
|
if (sprite->x >= sprite->sTargetX)
|
|
{
|
|
// Reached final position
|
|
sprite->x = sprite->sTargetX;
|
|
sprite->callback = SpriteCallbackDummy;
|
|
}
|
|
}
|
|
|
|
static bool32 Scene3_IsNidorinoEntering(struct IntroSequenceData * ptr)
|
|
{
|
|
return ptr->scene3NidorinoSprite->callback == Scene3_SpriteCB_NidorinoEnter ? TRUE : FALSE;
|
|
}
|
|
|
|
#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->scene3NidorinoSprite, ANIM_NIDORINO_CROUCH);
|
|
ptr->scene3NidorinoSprite->sState = 0;
|
|
ptr->scene3NidorinoSprite->sStateTimer = 0;
|
|
ptr->scene3NidorinoSprite->y2 = 3;
|
|
ptr->scene3NidorinoSprite->callback = SpriteCB_NidorinoCry;
|
|
}
|
|
|
|
static void SpriteCB_NidorinoCry(struct Sprite *sprite)
|
|
{
|
|
switch (sprite->sState)
|
|
{
|
|
case 0:
|
|
if (++sprite->sStateTimer > 8)
|
|
{
|
|
StartSpriteAnim(sprite, ANIM_NIDORINO_CRY);
|
|
sprite->y2 = 0;
|
|
sprite->sState++;
|
|
}
|
|
break;
|
|
case 1:
|
|
PlayCry_ByMode(SPECIES_NIDORINO, 0x3F, CRY_MODE_DOUBLES);
|
|
sprite->sStateTimer = 0;
|
|
sprite->sState++;
|
|
break;
|
|
case 2:
|
|
if (++sprite->sBounceTimer > 1)
|
|
{
|
|
// Nidorino bounces slightly while crying
|
|
sprite->sBounceTimer = 0;
|
|
sprite->y2 = sprite->y2 == 0 ? 1 : 0;
|
|
}
|
|
if (++sprite->sStateTimer > 48)
|
|
{
|
|
StartSpriteAnim(sprite, ANIM_NIDORINO_NORMAL);
|
|
sprite->y2 = 0;
|
|
sprite->callback = SpriteCallbackDummy;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
#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)
|
|
{
|
|
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->sState)
|
|
{
|
|
case 0:
|
|
if (++sprite->sStateTimer > 4)
|
|
{
|
|
StartSpriteAnim(sprite, ANIM_NIDORINO_HOP);
|
|
sprite->sState++;
|
|
}
|
|
break;
|
|
case 1:
|
|
// 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->sSlowdownTimer = 0;
|
|
sprite->sSpeedX--;
|
|
}
|
|
if (++sprite->sLandTimer > 15)
|
|
{
|
|
// Nidorino hits the ground
|
|
StartSpriteAnim(sprite, ANIM_NIDORINO_CROUCH);
|
|
sprite->sStateTimer = 0;
|
|
sprite->sRandSeed = 0x4757;
|
|
sprite->sSpeedX = 28;
|
|
sprite->sState++;
|
|
}
|
|
break;
|
|
case 2:
|
|
// Nidorino sliding on the ground
|
|
sprite->sOffsetX += sprite->sSpeedX;
|
|
sprite->x2 = sprite->sOffsetX >> 4;
|
|
if (++sprite->sStateTimer > 6)
|
|
{
|
|
// 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->sStateTimer > 12)
|
|
{
|
|
StartSpriteAnim(sprite, ANIM_NIDORINO_NORMAL);
|
|
sprite->sStateTimer = 0;
|
|
sprite->sState++;
|
|
}
|
|
break;
|
|
case 3:
|
|
// Nidorino hops back to its original position
|
|
if (++sprite->sStateTimer > 16)
|
|
Scene3_StartNidorinoHop(sprite, sNidorinoRecoilReturnTime, -sprite->x2, 4);
|
|
break;
|
|
}
|
|
}
|
|
|
|
#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->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].sSpeedX = (seed % 13) + 8;
|
|
gSprites[spriteId].sSpeedY = seed % 3;
|
|
gSprites[spriteId].sInvisibleTimer = i;
|
|
seed *= RAND_MULT;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void SpriteCB_RecoilDust(struct Sprite *sprite)
|
|
{
|
|
s16 * data = sprite->data;
|
|
|
|
switch (sprite->sState)
|
|
{
|
|
case 0:
|
|
sX = sprite->x << 4;
|
|
sY = sprite->y << 4;
|
|
sprite->sState++;
|
|
// fallthrough
|
|
case 1:
|
|
sX -= sSpeedX;
|
|
sY += sSpeedY;
|
|
sprite->x = sX >> 4;
|
|
sprite->y = sY >> 4;
|
|
if (sprite->animEnded)
|
|
DestroySprite(sprite);
|
|
break;
|
|
}
|
|
|
|
// Recoil dust flashes in and out
|
|
if (++sInvisibleTimer > 1)
|
|
{
|
|
sInvisibleTimer = 0;
|
|
sprite->invisible ^= 1;
|
|
}
|
|
}
|
|
|
|
#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->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_NidorinoHop(struct Sprite *sprite)
|
|
{
|
|
switch (sprite->sState)
|
|
{
|
|
case 0:
|
|
if (++sprite->sTimer > 4)
|
|
{
|
|
StartSpriteAnim(sprite, ANIM_NIDORINO_HOP);
|
|
sprite->sTimer = 0;
|
|
sprite->sState++;
|
|
}
|
|
break;
|
|
case 1:
|
|
if (--sprite->sAirTime)
|
|
{
|
|
// 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
|
|
{
|
|
// Nidorino lands
|
|
sprite->x2 = (u16)sprite->sOffsetX >> 4;
|
|
sprite->y2 = 0;
|
|
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->sTimer = 0;
|
|
sprite->sState++;
|
|
}
|
|
}
|
|
break;
|
|
case 2:
|
|
if (++sprite->sTimer > 4)
|
|
{
|
|
StartSpriteAnim(sprite, ANIM_NIDORINO_NORMAL);
|
|
sprite->callback = SpriteCallbackDummy;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
#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->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_NidorinoAttack(struct Sprite *sprite)
|
|
{
|
|
switch (sprite->sState)
|
|
{
|
|
case 0:
|
|
if (++sprite->sTimer & 1)
|
|
{
|
|
// Nidorino shakes horizontally before attacking
|
|
if (++sprite->sShakeTimer & 1)
|
|
sprite->x2++;
|
|
else
|
|
sprite->x2--;
|
|
}
|
|
if (sprite->sTimer > 17)
|
|
{
|
|
sprite->sTimer = 0;
|
|
sprite->sState++;
|
|
}
|
|
break;
|
|
case 1:
|
|
if (++sprite->sTimer >= sNidorinoAnimDelayTime)
|
|
{
|
|
StartSpriteAnim(sprite, ANIM_NIDORINO_ATTACK);
|
|
sprite->sTimer = 0;
|
|
sprite->sShakeTimer = 0;
|
|
sprite->sState++;
|
|
}
|
|
break;
|
|
case 2:
|
|
// 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;
|
|
}
|
|
}
|
|
|
|
#undef sState
|
|
#undef sTimer
|
|
#undef sShakeTimer
|
|
#undef sSpeed
|
|
|
|
static void LoadFightSceneSpriteGraphics(void)
|
|
{
|
|
int i;
|
|
for (i = 0; i < ARRAY_COUNT(sFightSceneSpriteSheets); i++)
|
|
LoadCompressedSpriteSheet(&sFightSceneSpriteSheets[i]);
|
|
LoadSpritePalettes(sFightSceneSpritePalettes);
|
|
}
|