mirror of
https://github.com/Ninjdai1/pokeemerald.git
synced 2025-01-06 01:23:15 +01:00
1634 lines
46 KiB
C
1634 lines
46 KiB
C
#include "global.h"
|
|
#include "palette.h"
|
|
#include "main.h"
|
|
#include "task.h"
|
|
#include "bg.h"
|
|
#include "malloc.h"
|
|
#include "window.h"
|
|
#include "text.h"
|
|
#include "menu.h"
|
|
#include "international_string_util.h"
|
|
#include "constants/songs.h"
|
|
#include "gpu_regs.h"
|
|
#include "m4a.h"
|
|
#include "constants/rgb.h"
|
|
#include "trainer_pokemon_sprites.h"
|
|
#include "starter_choose.h"
|
|
#include "decompress.h"
|
|
#include "intro_credits_graphics.h"
|
|
#include "sound.h"
|
|
#include "trig.h"
|
|
#include "graphics.h"
|
|
#include "pokedex.h"
|
|
#include "event_data.h"
|
|
#include "random.h"
|
|
|
|
#define COLOR_DARK_GREEN RGB(7, 11, 6)
|
|
#define COLOR_LIGHT_GREEN RGB(13, 20, 12)
|
|
|
|
#define TAG_MON_BG 1001
|
|
|
|
// Positions for the Pokémon images
|
|
enum {
|
|
POS_LEFT,
|
|
POS_CENTER,
|
|
POS_RIGHT,
|
|
};
|
|
|
|
enum {
|
|
MODE_NONE,
|
|
MODE_BIKE_SCENE,
|
|
MODE_SHOW_MONS,
|
|
};
|
|
|
|
#define tState data[0]
|
|
|
|
// Task data for the main Credits tasks
|
|
#define tTaskId_BgScenery data[0] // ID for Task_BicycleBgAnimation (created by CreateBicycleBgAnimationTask)
|
|
#define tTaskId_BikeScene data[1] // ID for Task_BikeScene
|
|
#define tTaskId_SceneryPal data[2] // ID for Task_CycleSceneryPalette
|
|
#define tTaskId_ShowMons data[3] // ID for Task_ShowMons
|
|
#define tEndCredits data[4]
|
|
#define tPlayerSpriteId data[5]
|
|
#define tRivalSpriteId data[6]
|
|
#define tSceneNum data[7]
|
|
// data[8]-[10] are unused
|
|
#define tNextMode data[11]
|
|
#define tTheEndDelay data[12]
|
|
#define tCurrentMode data[13]
|
|
#define tPrintedPage data[14]
|
|
#define tTaskId_UpdatePage data[15]
|
|
|
|
#define NUM_MON_SLIDES 71
|
|
|
|
struct CreditsData
|
|
{
|
|
u16 monToShow[NUM_MON_SLIDES]; // List of Pokemon species ids that will show during the credits
|
|
u16 imgCounter; //how many mon images have been shown
|
|
u16 nextImgPos; //if the next image spawns left/center/right
|
|
u16 currShownMon; //index into monToShow
|
|
u16 numMonToShow; //number of pokemon to show, always NUM_MON_SLIDES after determine function
|
|
u16 caughtMonIds[NATIONAL_DEX_COUNT]; //temporary location to hold a condensed array of all caught pokemon
|
|
u16 numCaughtMon; //count of filled spaces in caughtMonIds
|
|
u16 unused[7];
|
|
};
|
|
|
|
struct CreditsEntry
|
|
{
|
|
u8 unk; // Never read
|
|
bool8 isTitle;
|
|
const u8 *text;
|
|
};
|
|
|
|
static EWRAM_DATA s16 sUnkVar = 0; // Never read, only set to 0
|
|
static EWRAM_DATA u16 sSavedTaskId = 0;
|
|
EWRAM_DATA bool8 gHasHallOfFameRecords = 0;
|
|
static EWRAM_DATA bool8 sUsedSpeedUp = 0; // Never read
|
|
static EWRAM_DATA struct CreditsData *sCreditsData = {0};
|
|
|
|
static const u16 sCredits_Pal[] = INCBIN_U16("graphics/credits/credits.gbapal");
|
|
static const u32 sCreditsCopyrightEnd_Gfx[] = INCBIN_U32("graphics/credits/the_end_copyright.4bpp.lz");
|
|
|
|
static void SpriteCB_CreditsMonBg(struct Sprite *);
|
|
static void Task_WaitPaletteFade(u8);
|
|
static void Task_CreditsMain(u8);
|
|
static void Task_ReadyBikeScene(u8);
|
|
static void Task_SetBikeScene(u8);
|
|
static void Task_LoadShowMons(u8);
|
|
static void Task_ReadyShowMons(u8);
|
|
static void Task_CreditsTheEnd1(u8);
|
|
static void Task_CreditsTheEnd2(u8);
|
|
static void Task_CreditsTheEnd3(u8);
|
|
static void Task_CreditsTheEnd4(u8);
|
|
static void Task_CreditsTheEnd5(u8);
|
|
static void Task_CreditsTheEnd6(u8);
|
|
static void Task_CreditsSoftReset(u8);
|
|
static void ResetGpuAndVram(void);
|
|
static void Task_UpdatePage(u8);
|
|
static u8 CheckChangeScene(u8, u8);
|
|
static void Task_ShowMons(u8);
|
|
static void Task_CycleSceneryPalette(u8);
|
|
static void Task_BikeScene(u8);
|
|
static bool8 LoadBikeScene(u8 data, u8);
|
|
static void ResetCreditsTasks(u8);
|
|
static void LoadTheEndScreen(u16, u16, u16);
|
|
static void DrawTheEnd(u16, u16);
|
|
static void SpriteCB_Player(struct Sprite *);
|
|
static void SpriteCB_Rival(struct Sprite *);
|
|
static u8 CreateCreditsMonSprite(u16, s16, s16, u16);
|
|
static void DeterminePokemonToShow(void);
|
|
|
|
static const u8 sTheEnd_LetterMap_T[] =
|
|
{
|
|
0, 1, 0,
|
|
0xFF, 1, 0xFF,
|
|
0xFF, 1, 0xFF,
|
|
0xFF, 1, 0xFF,
|
|
0xFF, 1, 0xFF,
|
|
};
|
|
|
|
static const u8 sTheEnd_LetterMap_H[] =
|
|
{
|
|
1, 0xFF, 1,
|
|
1, 0xFF, 1,
|
|
1, 2, 1,
|
|
1, 0xFF, 1,
|
|
1, 0xFF, 1,
|
|
};
|
|
|
|
static const u8 sTheEnd_LetterMap_E[] =
|
|
{
|
|
1, 0, 0,
|
|
1, 0xFF, 0xFF,
|
|
1, 2, 2,
|
|
1, 0xFF, 0xFF,
|
|
1, 0x80, 0x80,
|
|
};
|
|
|
|
static const u8 sTheEnd_LetterMap_N[] =
|
|
{
|
|
1, 3, 1,
|
|
1, 4, 1,
|
|
1, 5, 1,
|
|
1, 0xC4, 1,
|
|
1, 0xC3, 1,
|
|
};
|
|
|
|
static const u8 sTheEnd_LetterMap_D[] =
|
|
{
|
|
1, 6, 7,
|
|
1, 8, 9,
|
|
1, 0xFF, 1,
|
|
1, 0x88, 0x89,
|
|
1, 0x86, 0x87,
|
|
};
|
|
|
|
#include "data/credits.h"
|
|
|
|
static const struct BgTemplate sBackgroundTemplates[] =
|
|
{
|
|
{
|
|
.bg = 0,
|
|
.charBaseIndex = 2,
|
|
.mapBaseIndex = 28,
|
|
.screenSize = 0,
|
|
.paletteMode = 0,
|
|
.priority = 0,
|
|
.baseTile = 0
|
|
},
|
|
};
|
|
static const struct WindowTemplate sWindowTemplates[] =
|
|
{
|
|
{
|
|
.bg = 0,
|
|
.tilemapLeft = 0,
|
|
.tilemapTop = 9,
|
|
.width = 30,
|
|
.height = 12,
|
|
.paletteNum = 8,
|
|
.baseBlock = 1
|
|
},
|
|
DUMMY_WIN_TEMPLATE,
|
|
};
|
|
static const u8 sMonSpritePos[][2] =
|
|
{
|
|
{104, 36},
|
|
{120, 36},
|
|
{136, 36},
|
|
};
|
|
|
|
static const union AnimCmd sAnim_Player_Slow[] =
|
|
{
|
|
ANIMCMD_FRAME(0, 8),
|
|
ANIMCMD_FRAME(64, 8),
|
|
ANIMCMD_FRAME(128, 8),
|
|
ANIMCMD_FRAME(192, 8),
|
|
ANIMCMD_JUMP(0),
|
|
};
|
|
|
|
static const union AnimCmd sAnim_Player_Fast[] =
|
|
{
|
|
ANIMCMD_FRAME(0, 4),
|
|
ANIMCMD_FRAME(64, 4),
|
|
ANIMCMD_FRAME(128, 4),
|
|
ANIMCMD_FRAME(192, 4),
|
|
ANIMCMD_JUMP(0),
|
|
};
|
|
|
|
static const union AnimCmd sAnim_Player_LookBack[] =
|
|
{
|
|
ANIMCMD_FRAME(256, 4),
|
|
ANIMCMD_FRAME(320, 4),
|
|
ANIMCMD_FRAME(384, 4),
|
|
ANIMCMD_END,
|
|
};
|
|
|
|
static const union AnimCmd sAnim_Player_LookForward[] =
|
|
{
|
|
ANIMCMD_FRAME(384, 30),
|
|
ANIMCMD_FRAME(320, 30),
|
|
ANIMCMD_FRAME(256, 30),
|
|
ANIMCMD_FRAME(256, 30),
|
|
ANIMCMD_END,
|
|
};
|
|
|
|
static const union AnimCmd *const sAnims_Player[] =
|
|
{
|
|
sAnim_Player_Slow,
|
|
sAnim_Player_Fast,
|
|
sAnim_Player_LookBack,
|
|
sAnim_Player_LookForward,
|
|
};
|
|
|
|
static const union AnimCmd sAnim_Rival_Slow[] =
|
|
{
|
|
ANIMCMD_FRAME(0, 8),
|
|
ANIMCMD_FRAME(64, 8),
|
|
ANIMCMD_FRAME(128, 8),
|
|
ANIMCMD_FRAME(192, 8),
|
|
ANIMCMD_JUMP(0),
|
|
};
|
|
|
|
static const union AnimCmd sAnim_Rival_Fast[] =
|
|
{
|
|
ANIMCMD_FRAME(0, 4),
|
|
ANIMCMD_FRAME(64, 4),
|
|
ANIMCMD_FRAME(128, 4),
|
|
ANIMCMD_FRAME(192, 4),
|
|
ANIMCMD_JUMP(0),
|
|
};
|
|
|
|
static const union AnimCmd sAnim_Rival_Still[] =
|
|
{
|
|
ANIMCMD_FRAME(0, 4),
|
|
ANIMCMD_END,
|
|
};
|
|
|
|
static const union AnimCmd *const sAnims_Rival[] =
|
|
{
|
|
sAnim_Rival_Slow,
|
|
sAnim_Rival_Fast,
|
|
sAnim_Rival_Still,
|
|
};
|
|
|
|
#define MONBG_OFFSET (MON_PIC_SIZE * 3)
|
|
static const struct SpriteSheet sSpriteSheet_MonBg[] = {
|
|
{ gDecompressionBuffer, MONBG_OFFSET, TAG_MON_BG },
|
|
{},
|
|
};
|
|
static const struct SpritePalette sSpritePalette_MonBg[] = {
|
|
{ (const u16 *)&gDecompressionBuffer[MONBG_OFFSET], TAG_MON_BG },
|
|
{},
|
|
};
|
|
|
|
static const struct OamData sOamData_MonBg =
|
|
{
|
|
.y = DISPLAY_HEIGHT,
|
|
.affineMode = ST_OAM_AFFINE_OFF,
|
|
.objMode = ST_OAM_OBJ_NORMAL,
|
|
.mosaic = 0,
|
|
.bpp = ST_OAM_4BPP,
|
|
.shape = SPRITE_SHAPE(64x64),
|
|
.x = 0,
|
|
.matrixNum = 0,
|
|
.size = SPRITE_SIZE(64x64),
|
|
.tileNum = 0,
|
|
.priority = 1,
|
|
.paletteNum = 0,
|
|
.affineParam = 0,
|
|
};
|
|
|
|
static const union AnimCmd sAnim_MonBg_Yellow[] =
|
|
{
|
|
ANIMCMD_FRAME(0, 8),
|
|
ANIMCMD_END,
|
|
};
|
|
|
|
static const union AnimCmd sAnim_MonBg_Red[] =
|
|
{
|
|
ANIMCMD_FRAME(64, 8),
|
|
ANIMCMD_END,
|
|
};
|
|
|
|
static const union AnimCmd sAnim_MonBg_Blue[] =
|
|
{
|
|
ANIMCMD_FRAME(128, 8),
|
|
ANIMCMD_END,
|
|
};
|
|
|
|
static const union AnimCmd *const sAnims_MonBg[] =
|
|
{
|
|
[POS_LEFT] = sAnim_MonBg_Yellow,
|
|
[POS_CENTER] = sAnim_MonBg_Red,
|
|
[POS_RIGHT] = sAnim_MonBg_Blue,
|
|
};
|
|
|
|
static const struct SpriteTemplate sSpriteTemplate_CreditsMonBg =
|
|
{
|
|
.tileTag = TAG_MON_BG,
|
|
.paletteTag = TAG_MON_BG,
|
|
.oam = &sOamData_MonBg,
|
|
.anims = sAnims_MonBg,
|
|
.images = NULL,
|
|
.affineAnims = gDummySpriteAffineAnimTable,
|
|
.callback = SpriteCB_CreditsMonBg,
|
|
};
|
|
|
|
static void VBlankCB_Credits(void)
|
|
{
|
|
LoadOam();
|
|
ProcessSpriteCopyRequests();
|
|
TransferPlttBuffer();
|
|
}
|
|
|
|
static void CB2_Credits(void)
|
|
{
|
|
RunTasks();
|
|
AnimateSprites();
|
|
|
|
if ((JOY_HELD(B_BUTTON))
|
|
&& gHasHallOfFameRecords
|
|
&& gTasks[sSavedTaskId].func == Task_CreditsMain)
|
|
{
|
|
// Speed up credits
|
|
VBlankCB_Credits();
|
|
RunTasks();
|
|
AnimateSprites();
|
|
sUsedSpeedUp = TRUE;
|
|
}
|
|
BuildOamBuffer();
|
|
UpdatePaletteFade();
|
|
}
|
|
|
|
static void InitCreditsBgsAndWindows(void)
|
|
{
|
|
ResetBgsAndClearDma3BusyFlags(0);
|
|
InitBgsFromTemplates(0, sBackgroundTemplates, ARRAY_COUNT(sBackgroundTemplates));
|
|
SetBgTilemapBuffer(0, AllocZeroed(BG_SCREEN_SIZE));
|
|
LoadPalette(sCredits_Pal, 0x80, 64);
|
|
InitWindows(sWindowTemplates);
|
|
DeactivateAllTextPrinters();
|
|
PutWindowTilemap(0);
|
|
CopyWindowToVram(0, COPYWIN_FULL);
|
|
ShowBg(0);
|
|
}
|
|
|
|
static void FreeCreditsBgsAndWindows(void)
|
|
{
|
|
void *ptr;
|
|
FreeAllWindowBuffers();
|
|
ptr = GetBgTilemapBuffer(0);
|
|
if (ptr)
|
|
Free(ptr);
|
|
}
|
|
|
|
static void PrintCreditsText(const u8 *string, u8 y, bool8 isTitle)
|
|
{
|
|
u8 x;
|
|
u8 color[3];
|
|
|
|
color[0] = TEXT_COLOR_TRANSPARENT;
|
|
|
|
if (isTitle == TRUE)
|
|
{
|
|
color[1] = TEXT_COLOR_LIGHT_GRAY;
|
|
color[2] = TEXT_COLOR_RED;
|
|
}
|
|
else
|
|
{
|
|
color[1] = TEXT_COLOR_WHITE;
|
|
color[2] = TEXT_COLOR_DARK_GRAY;
|
|
}
|
|
|
|
x = GetStringCenterAlignXOffsetWithLetterSpacing(FONT_NORMAL, string, DISPLAY_WIDTH, 1);
|
|
AddTextPrinterParameterized4(0, FONT_NORMAL, x, y, 1, 0, color, TEXT_SKIP_DRAW, string);
|
|
}
|
|
|
|
#define tMainTaskId data[1]
|
|
|
|
void CB2_StartCreditsSequence(void)
|
|
{
|
|
u8 taskId;
|
|
s16 bikeTaskId;
|
|
u8 pageTaskId;
|
|
|
|
ResetGpuAndVram();
|
|
SetVBlankCallback(NULL);
|
|
InitHeap(gHeap, HEAP_SIZE);
|
|
ResetPaletteFade();
|
|
ResetTasks();
|
|
InitCreditsBgsAndWindows();
|
|
|
|
taskId = CreateTask(Task_WaitPaletteFade, 0);
|
|
|
|
gTasks[taskId].tEndCredits = FALSE;
|
|
gTasks[taskId].tSceneNum = SCENE_OCEAN_MORNING;
|
|
gTasks[taskId].tNextMode = MODE_NONE;
|
|
gTasks[taskId].tCurrentMode = MODE_BIKE_SCENE;
|
|
|
|
while (TRUE)
|
|
{
|
|
if (LoadBikeScene(SCENE_OCEAN_MORNING, taskId))
|
|
break;
|
|
}
|
|
|
|
bikeTaskId = gTasks[taskId].tTaskId_BikeScene;
|
|
gTasks[bikeTaskId].tState = 40;
|
|
|
|
SetGpuReg(REG_OFFSET_BG0VOFS, 0xFFFC);
|
|
|
|
pageTaskId = CreateTask(Task_UpdatePage, 0);
|
|
|
|
gTasks[pageTaskId].tMainTaskId = taskId;
|
|
gTasks[taskId].tTaskId_UpdatePage = pageTaskId;
|
|
|
|
BeginNormalPaletteFade(PALETTES_ALL, 0, 16, 0, RGB_BLACK);
|
|
EnableInterrupts(INTR_FLAG_VBLANK);
|
|
SetVBlankCallback(VBlankCB_Credits);
|
|
m4aSongNumStart(MUS_CREDITS);
|
|
SetMainCallback2(CB2_Credits);
|
|
sUsedSpeedUp = FALSE;
|
|
sCreditsData = AllocZeroed(sizeof(struct CreditsData));
|
|
|
|
DeterminePokemonToShow();
|
|
|
|
sCreditsData->imgCounter = 0;
|
|
sCreditsData->nextImgPos = POS_LEFT;
|
|
sCreditsData->currShownMon = 0;
|
|
|
|
sSavedTaskId = taskId;
|
|
}
|
|
|
|
static void Task_WaitPaletteFade(u8 taskId)
|
|
{
|
|
if (!gPaletteFade.active)
|
|
gTasks[taskId].func = Task_CreditsMain;
|
|
}
|
|
|
|
static void Task_CreditsMain(u8 taskId)
|
|
{
|
|
u16 mode;
|
|
|
|
if (gTasks[taskId].tEndCredits)
|
|
{
|
|
s16 bikeTaskId = gTasks[taskId].tTaskId_BikeScene;
|
|
gTasks[bikeTaskId].tState = 30;
|
|
|
|
gTasks[taskId].tTheEndDelay = 256;
|
|
gTasks[taskId].func = Task_CreditsTheEnd1;
|
|
return;
|
|
}
|
|
|
|
sUnkVar = 0;
|
|
mode = gTasks[taskId].tNextMode;
|
|
|
|
if (gTasks[taskId].tNextMode == MODE_BIKE_SCENE)
|
|
{
|
|
// Start a bike cutscene
|
|
gTasks[taskId].tCurrentMode = mode;
|
|
gTasks[taskId].tNextMode = MODE_NONE;
|
|
BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 16, RGB_BLACK);
|
|
gTasks[taskId].func = Task_ReadyBikeScene;
|
|
}
|
|
else if (gTasks[taskId].tNextMode == MODE_SHOW_MONS)
|
|
{
|
|
// Start a Pokémon interlude
|
|
gTasks[taskId].tCurrentMode = mode;
|
|
gTasks[taskId].tNextMode = MODE_NONE;
|
|
BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 16, RGB_BLACK);
|
|
gTasks[taskId].func = Task_ReadyShowMons;
|
|
}
|
|
}
|
|
|
|
static void Task_ReadyBikeScene(u8 taskId)
|
|
{
|
|
if (!gPaletteFade.active)
|
|
{
|
|
SetGpuReg(REG_OFFSET_DISPCNT, 0);
|
|
ResetCreditsTasks(taskId);
|
|
gTasks[taskId].func = Task_SetBikeScene;
|
|
}
|
|
}
|
|
|
|
static void Task_SetBikeScene(u8 taskId)
|
|
{
|
|
SetVBlankCallback(NULL);
|
|
|
|
if (LoadBikeScene(gTasks[taskId].tSceneNum, taskId))
|
|
{
|
|
BeginNormalPaletteFade(PALETTES_ALL, 0, 16, 0, RGB_BLACK);
|
|
EnableInterrupts(INTR_FLAG_VBLANK);
|
|
SetVBlankCallback(VBlankCB_Credits);
|
|
gTasks[taskId].func = Task_WaitPaletteFade;
|
|
}
|
|
}
|
|
|
|
static void Task_ReadyShowMons(u8 taskId)
|
|
{
|
|
if (!gPaletteFade.active)
|
|
{
|
|
SetGpuReg(REG_OFFSET_DISPCNT, 0);
|
|
ResetCreditsTasks(taskId);
|
|
gTasks[taskId].func = Task_LoadShowMons;
|
|
}
|
|
}
|
|
|
|
static void Task_LoadShowMons(u8 taskId)
|
|
{
|
|
switch (gMain.state)
|
|
{
|
|
default:
|
|
case 0:
|
|
{
|
|
u16 i;
|
|
u16 *temp;
|
|
|
|
ResetSpriteData();
|
|
ResetAllPicSprites();
|
|
FreeAllSpritePalettes();
|
|
gReservedSpritePaletteCount = 8;
|
|
LZ77UnCompVram(gBirchHelpGfx, (void *)VRAM);
|
|
LZ77UnCompVram(gBirchGrassTilemap, (void *)(BG_SCREEN_ADDR(7)));
|
|
LoadPalette(gBirchBagGrassPal[0] + 1, 1, 31 * 2);
|
|
|
|
for (i = 0; i < MON_PIC_SIZE; i++)
|
|
gDecompressionBuffer[i] = 0x11;
|
|
for (i = 0; i < MON_PIC_SIZE; i++)
|
|
(gDecompressionBuffer + MON_PIC_SIZE)[i] = 0x22;
|
|
for (i = 0; i < MON_PIC_SIZE; i++)
|
|
(gDecompressionBuffer + MON_PIC_SIZE * 2)[i] = 0x33;
|
|
|
|
temp = (u16 *)(&gDecompressionBuffer[MONBG_OFFSET]);
|
|
temp[0] = RGB_BLACK;
|
|
temp[1] = RGB(31, 31, 20); // light yellow
|
|
temp[2] = RGB(31, 20, 20); // light red
|
|
temp[3] = RGB(20, 20, 31); // light blue
|
|
|
|
LoadSpriteSheet(sSpriteSheet_MonBg);
|
|
LoadSpritePalette(sSpritePalette_MonBg);
|
|
|
|
gMain.state++;
|
|
break;
|
|
}
|
|
case 1:
|
|
gTasks[taskId].tTaskId_ShowMons = CreateTask(Task_ShowMons, 0);
|
|
gTasks[gTasks[taskId].tTaskId_ShowMons].tState = 1;
|
|
gTasks[gTasks[taskId].tTaskId_ShowMons].tMainTaskId = taskId;
|
|
gTasks[gTasks[taskId].tTaskId_ShowMons].data[2] = gTasks[taskId].tSceneNum; // data[2] never read
|
|
|
|
BeginNormalPaletteFade(PALETTES_ALL, 0, 16, 0, RGB_BLACK);
|
|
SetGpuReg(REG_OFFSET_BG3HOFS, 0);
|
|
SetGpuReg(REG_OFFSET_BG3VOFS, 32);
|
|
SetGpuReg(REG_OFFSET_BG3CNT, BGCNT_PRIORITY(3)
|
|
| BGCNT_CHARBASE(0)
|
|
| BGCNT_SCREENBASE(7)
|
|
| BGCNT_16COLOR
|
|
| BGCNT_TXT256x256);
|
|
SetGpuReg(REG_OFFSET_DISPCNT, DISPCNT_MODE_0
|
|
| DISPCNT_OBJ_1D_MAP
|
|
| DISPCNT_BG0_ON
|
|
| DISPCNT_BG3_ON
|
|
| DISPCNT_OBJ_ON);
|
|
|
|
gMain.state = 0;
|
|
gIntroCredits_MovingSceneryState = INTROCRED_SCENERY_NORMAL;
|
|
gTasks[taskId].func = Task_WaitPaletteFade;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void Task_CreditsTheEnd1(u8 taskId)
|
|
{
|
|
if (gTasks[taskId].tTheEndDelay)
|
|
{
|
|
gTasks[taskId].tTheEndDelay--;
|
|
return;
|
|
}
|
|
|
|
BeginNormalPaletteFade(PALETTES_ALL, 12, 0, 16, RGB_BLACK);
|
|
gTasks[taskId].func = Task_CreditsTheEnd2;
|
|
}
|
|
|
|
static void Task_CreditsTheEnd2(u8 taskId)
|
|
{
|
|
if (!gPaletteFade.active)
|
|
{
|
|
ResetCreditsTasks(taskId);
|
|
gTasks[taskId].func = Task_CreditsTheEnd3;
|
|
}
|
|
}
|
|
|
|
#define tDelay data[0]
|
|
|
|
static void Task_CreditsTheEnd3(u8 taskId)
|
|
{
|
|
ResetGpuAndVram();
|
|
ResetPaletteFade();
|
|
LoadTheEndScreen(0, 0x3800, 0);
|
|
ResetSpriteData();
|
|
FreeAllSpritePalettes();
|
|
BeginNormalPaletteFade(PALETTES_ALL, 8, 16, 0, RGB_BLACK);
|
|
|
|
SetGpuReg(REG_OFFSET_BG0CNT, BGCNT_PRIORITY(0)
|
|
| BGCNT_CHARBASE(0)
|
|
| BGCNT_SCREENBASE(7)
|
|
| BGCNT_16COLOR
|
|
| BGCNT_TXT256x256);
|
|
EnableInterrupts(INTR_FLAG_VBLANK);
|
|
SetGpuReg(REG_OFFSET_DISPCNT, DISPCNT_MODE_0
|
|
| DISPCNT_OBJ_1D_MAP
|
|
| DISPCNT_BG0_ON);
|
|
|
|
gTasks[taskId].tDelay = 235; //set this to 215 to actually show "THE END" in time to the last song beat
|
|
gTasks[taskId].func = Task_CreditsTheEnd4;
|
|
}
|
|
|
|
static void Task_CreditsTheEnd4(u8 taskId)
|
|
{
|
|
if (gTasks[taskId].tDelay)
|
|
{
|
|
gTasks[taskId].tDelay--;
|
|
return;
|
|
}
|
|
|
|
BeginNormalPaletteFade(PALETTES_ALL, 6, 0, 16, RGB_BLACK);
|
|
gTasks[taskId].func = Task_CreditsTheEnd5;
|
|
}
|
|
|
|
static void Task_CreditsTheEnd5(u8 taskId)
|
|
{
|
|
if (!gPaletteFade.active)
|
|
{
|
|
DrawTheEnd(0x3800, 0);
|
|
|
|
BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 0, RGB_BLACK);
|
|
gTasks[taskId].tDelay = 7200;
|
|
gTasks[taskId].func = Task_CreditsTheEnd6;
|
|
}
|
|
}
|
|
|
|
static void Task_CreditsTheEnd6(u8 taskId)
|
|
{
|
|
if (!gPaletteFade.active)
|
|
{
|
|
if (gTasks[taskId].tDelay == 0 || gMain.newKeys)
|
|
{
|
|
FadeOutBGM(4);
|
|
BeginNormalPaletteFade(PALETTES_ALL, 8, 0, 16, RGB_WHITEALPHA);
|
|
gTasks[taskId].func = Task_CreditsSoftReset;
|
|
return;
|
|
}
|
|
|
|
if (gTasks[taskId].tDelay == 7144)
|
|
FadeOutBGM(8);
|
|
|
|
if (gTasks[taskId].tDelay == 6840)
|
|
m4aSongNumStart(MUS_END);
|
|
|
|
gTasks[taskId].tDelay--;
|
|
}
|
|
}
|
|
|
|
#undef tDelay
|
|
|
|
static void Task_CreditsSoftReset(u8 taskId)
|
|
{
|
|
if (!gPaletteFade.active)
|
|
SoftReset(RESET_ALL);
|
|
}
|
|
|
|
static void ResetGpuAndVram(void)
|
|
{
|
|
SetGpuReg(REG_OFFSET_DISPCNT, 0);
|
|
|
|
SetGpuReg(REG_OFFSET_BG3HOFS, 0);
|
|
SetGpuReg(REG_OFFSET_BG3VOFS, 0);
|
|
SetGpuReg(REG_OFFSET_BG2HOFS, 0);
|
|
SetGpuReg(REG_OFFSET_BG2VOFS, 0);
|
|
SetGpuReg(REG_OFFSET_BG1HOFS, 0);
|
|
SetGpuReg(REG_OFFSET_BG1VOFS, 0);
|
|
SetGpuReg(REG_OFFSET_BG0HOFS, 0);
|
|
SetGpuReg(REG_OFFSET_BG0VOFS, 0);
|
|
|
|
SetGpuReg(REG_OFFSET_BLDCNT, 0);
|
|
SetGpuReg(REG_OFFSET_BLDALPHA, 0);
|
|
SetGpuReg(REG_OFFSET_BLDY, 0);
|
|
|
|
DmaFill16(3, 0, (void *)VRAM, VRAM_SIZE);
|
|
DmaFill32(3, 0, (void *)OAM, OAM_SIZE);
|
|
DmaFill16(3, 0, (void *)(PLTT + 2), PLTT_SIZE - 2);
|
|
}
|
|
|
|
#define tCurrentPage data[2]
|
|
#define tDelay data[3]
|
|
|
|
static void Task_UpdatePage(u8 taskId)
|
|
{
|
|
int i;
|
|
|
|
switch (gTasks[taskId].tState)
|
|
{
|
|
case 0:
|
|
case 6:
|
|
case 7:
|
|
case 8:
|
|
case 9:
|
|
default:
|
|
if (!gPaletteFade.active)
|
|
{
|
|
gTasks[taskId].tState = 1;
|
|
gTasks[taskId].tDelay = 72;
|
|
gTasks[gTasks[taskId].tMainTaskId].tPrintedPage = FALSE;
|
|
sUnkVar = 0;
|
|
}
|
|
return;
|
|
case 1:
|
|
if (gTasks[taskId].tDelay != 0)
|
|
{
|
|
gTasks[taskId].tDelay--;
|
|
return;
|
|
}
|
|
gTasks[taskId].tState++;
|
|
return;
|
|
case 2:
|
|
if (gTasks[gTasks[taskId].tMainTaskId].func == Task_CreditsMain)
|
|
{
|
|
if (gTasks[taskId].tCurrentPage < PAGE_COUNT)
|
|
{
|
|
// Print text for this Credits page
|
|
for (i = 0; i < ENTRIES_PER_PAGE; i++)
|
|
PrintCreditsText(
|
|
sCreditsEntryPointerTable[gTasks[taskId].tCurrentPage][i]->text,
|
|
5 + i * 16,
|
|
sCreditsEntryPointerTable[gTasks[taskId].tCurrentPage][i]->isTitle);
|
|
CopyWindowToVram(0, COPYWIN_GFX);
|
|
|
|
gTasks[taskId].tCurrentPage++;
|
|
gTasks[taskId].tState++;
|
|
|
|
gTasks[gTasks[taskId].tMainTaskId].tPrintedPage = TRUE;
|
|
|
|
if (gTasks[gTasks[taskId].tMainTaskId].tCurrentMode == MODE_BIKE_SCENE)
|
|
BeginNormalPaletteFade(0x300, 0, 16, 0, COLOR_LIGHT_GREEN);
|
|
else // MODE_SHOW_MONS
|
|
BeginNormalPaletteFade(0x300, 0, 16, 0, COLOR_DARK_GREEN);
|
|
return;
|
|
}
|
|
|
|
// Reached final page of Credits, end task
|
|
gTasks[taskId].tState = 10;
|
|
return;
|
|
}
|
|
gTasks[gTasks[taskId].tMainTaskId].tPrintedPage = FALSE;
|
|
return;
|
|
case 3:
|
|
if (!gPaletteFade.active)
|
|
{
|
|
gTasks[taskId].tDelay = 115;
|
|
gTasks[taskId].tState++;
|
|
}
|
|
return;
|
|
case 4:
|
|
if (gTasks[taskId].tDelay != 0)
|
|
{
|
|
gTasks[taskId].tDelay--;
|
|
return;
|
|
}
|
|
|
|
if (CheckChangeScene((u8)gTasks[taskId].tCurrentPage, (u8)gTasks[taskId].tMainTaskId))
|
|
{
|
|
gTasks[taskId].tState++;
|
|
return;
|
|
}
|
|
gTasks[taskId].tState++;
|
|
if (gTasks[gTasks[taskId].tMainTaskId].tCurrentMode == MODE_BIKE_SCENE)
|
|
BeginNormalPaletteFade(0x300, 0, 0, 16, COLOR_LIGHT_GREEN);
|
|
else // MODE_SHOW_MONS
|
|
BeginNormalPaletteFade(0x300, 0, 0, 16, COLOR_DARK_GREEN);
|
|
return;
|
|
case 5:
|
|
if (!gPaletteFade.active)
|
|
{
|
|
// Still more Credits pages to show, return to state 2 to print
|
|
FillWindowPixelBuffer(0, PIXEL_FILL(0));
|
|
CopyWindowToVram(0, COPYWIN_GFX);
|
|
gTasks[taskId].tState = 2;
|
|
}
|
|
return;
|
|
case 10:
|
|
gTasks[gTasks[taskId].tMainTaskId].tEndCredits = TRUE;
|
|
DestroyTask(taskId);
|
|
FreeCreditsBgsAndWindows();
|
|
FREE_AND_SET_NULL(sCreditsData);
|
|
return;
|
|
}
|
|
}
|
|
|
|
#undef tDelay
|
|
|
|
#define PAGE_INTERVAL (PAGE_COUNT / 9) // 9 scenes (5 bike scenes, 4 Pokémon interludes)
|
|
|
|
static u8 CheckChangeScene(u8 page, u8 taskId)
|
|
{
|
|
// Starts with bike + ocean + morning (SCENE_OCEAN_MORNING)
|
|
|
|
if (page == PAGE_INTERVAL * 1)
|
|
{
|
|
// Pokémon interlude
|
|
gTasks[taskId].tNextMode = MODE_SHOW_MONS;
|
|
}
|
|
|
|
if (page == PAGE_INTERVAL * 2)
|
|
{
|
|
// Bike + ocean + sunset
|
|
gTasks[taskId].tSceneNum = SCENE_OCEAN_SUNSET;
|
|
gTasks[taskId].tNextMode = MODE_BIKE_SCENE;
|
|
}
|
|
|
|
if (page == PAGE_INTERVAL * 3)
|
|
{
|
|
// Pokémon interlude
|
|
gTasks[taskId].tNextMode = MODE_SHOW_MONS;
|
|
}
|
|
|
|
if (page == PAGE_INTERVAL * 4)
|
|
{
|
|
// Bike + forest + sunset
|
|
gTasks[taskId].tSceneNum = SCENE_FOREST_RIVAL_ARRIVE;
|
|
gTasks[taskId].tNextMode = MODE_BIKE_SCENE;
|
|
}
|
|
|
|
if (page == PAGE_INTERVAL * 5)
|
|
{
|
|
// Pokémon interlude
|
|
gTasks[taskId].tNextMode = MODE_SHOW_MONS;
|
|
}
|
|
|
|
if (page == PAGE_INTERVAL * 6)
|
|
{
|
|
// Bike + forest + sunset
|
|
gTasks[taskId].tSceneNum = SCENE_FOREST_CATCH_RIVAL;
|
|
gTasks[taskId].tNextMode = MODE_BIKE_SCENE;
|
|
}
|
|
|
|
if (page == PAGE_INTERVAL * 7)
|
|
{
|
|
// Pokémon interlude
|
|
gTasks[taskId].tNextMode = MODE_SHOW_MONS;
|
|
}
|
|
|
|
if (page == PAGE_INTERVAL * 8)
|
|
{
|
|
// Bike + town + night
|
|
gTasks[taskId].tSceneNum = SCENE_CITY_NIGHT;
|
|
gTasks[taskId].tNextMode = MODE_BIKE_SCENE;
|
|
}
|
|
|
|
if (gTasks[taskId].tNextMode != MODE_NONE)
|
|
{
|
|
// Returns true if changed
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
#define tDelay data[3]
|
|
|
|
static void Task_ShowMons(u8 taskId)
|
|
{
|
|
u8 spriteId;
|
|
|
|
switch (gTasks[taskId].tState)
|
|
{
|
|
case 0:
|
|
break;
|
|
case 1:
|
|
if (sCreditsData->nextImgPos == POS_LEFT && gTasks[gTasks[taskId].tMainTaskId].tPrintedPage == FALSE)
|
|
break;
|
|
gTasks[taskId].tState++;
|
|
break;
|
|
case 2:
|
|
if (sCreditsData->imgCounter == NUM_MON_SLIDES || gTasks[gTasks[taskId].tMainTaskId].func != Task_CreditsMain)
|
|
break;
|
|
spriteId = CreateCreditsMonSprite(sCreditsData->monToShow[sCreditsData->currShownMon],
|
|
sMonSpritePos[sCreditsData->nextImgPos][0],
|
|
sMonSpritePos[sCreditsData->nextImgPos][1],
|
|
sCreditsData->nextImgPos);
|
|
if (sCreditsData->currShownMon < sCreditsData->numMonToShow - 1)
|
|
{
|
|
sCreditsData->currShownMon++;
|
|
gSprites[spriteId].data[3] = 50;
|
|
}
|
|
else
|
|
{
|
|
sCreditsData->currShownMon = 0;
|
|
gSprites[spriteId].data[3] = 512;
|
|
}
|
|
sCreditsData->imgCounter++;
|
|
|
|
if (sCreditsData->nextImgPos == POS_RIGHT)
|
|
sCreditsData->nextImgPos = POS_LEFT;
|
|
else
|
|
sCreditsData->nextImgPos++;
|
|
|
|
gTasks[taskId].tDelay = 50;
|
|
gTasks[taskId].tState++;
|
|
break;
|
|
case 3:
|
|
if (gTasks[taskId].tDelay != 0)
|
|
gTasks[taskId].tDelay--;
|
|
else
|
|
gTasks[taskId].tState = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
#undef tMainTaskId
|
|
#undef tDelay
|
|
|
|
#define tPlayer data[2]
|
|
#define tRival data[3]
|
|
#define tDelay data[4]
|
|
#define tSinIdx data[5]
|
|
|
|
static void Task_BikeScene(u8 taskId)
|
|
{
|
|
switch (gTasks[taskId].tState)
|
|
{
|
|
case 0:
|
|
gIntroCredits_MovingSceneryVOffset = Sin((gTasks[taskId].tSinIdx >> 1) & 0x7F, 12);
|
|
gTasks[taskId].tSinIdx++;
|
|
break;
|
|
case 1:
|
|
if (gIntroCredits_MovingSceneryVOffset != 0)
|
|
{
|
|
gIntroCredits_MovingSceneryVOffset = Sin((gTasks[taskId].tSinIdx >> 1) & 0x7F, 12);
|
|
gTasks[taskId].tSinIdx++;
|
|
}
|
|
else
|
|
{
|
|
gSprites[gTasks[taskId].tPlayer].data[0] = 2;
|
|
gTasks[taskId].tSinIdx = 0;
|
|
gTasks[taskId].tState++;
|
|
}
|
|
break;
|
|
case 2:
|
|
if (gTasks[taskId].tSinIdx < 64)
|
|
{
|
|
gTasks[taskId].tSinIdx++;
|
|
gIntroCredits_MovingSceneryVOffset = Sin(gTasks[taskId].tSinIdx & 0x7F, 20);
|
|
}
|
|
else
|
|
{
|
|
gTasks[taskId].tState++;
|
|
}
|
|
break;
|
|
case 3:
|
|
gSprites[gTasks[taskId].tPlayer].data[0] = 3;
|
|
gSprites[gTasks[taskId].tRival].data[0] = 1;
|
|
gTasks[taskId].tDelay = 120;
|
|
gTasks[taskId].tState++;
|
|
break;
|
|
case 4:
|
|
if (gTasks[taskId].tDelay != 0)
|
|
{
|
|
gTasks[taskId].tDelay--;
|
|
}
|
|
else
|
|
{
|
|
gTasks[taskId].tSinIdx = 64;
|
|
gTasks[taskId].tState++;
|
|
}
|
|
break;
|
|
case 5:
|
|
if (gTasks[taskId].tSinIdx > 0)
|
|
{
|
|
gTasks[taskId].tSinIdx--;
|
|
gIntroCredits_MovingSceneryVOffset = Sin(gTasks[taskId].tSinIdx & 0x7F, 20);
|
|
}
|
|
else
|
|
{
|
|
gSprites[gTasks[taskId].tPlayer].data[0] = 1;
|
|
gTasks[taskId].tState++;
|
|
}
|
|
break;
|
|
case 6:
|
|
gTasks[taskId].tState = 50;
|
|
break;
|
|
case 10:
|
|
gSprites[gTasks[taskId].tRival].data[0] = 2;
|
|
gTasks[taskId].tState = 50;
|
|
break;
|
|
case 20:
|
|
gSprites[gTasks[taskId].tPlayer].data[0] = 4;
|
|
gTasks[taskId].tState = 50;
|
|
break;
|
|
case 30:
|
|
gSprites[gTasks[taskId].tPlayer].data[0] = 5;
|
|
gSprites[gTasks[taskId].tRival].data[0] = 3;
|
|
gTasks[taskId].tState = 50;
|
|
break;
|
|
case 50:
|
|
gTasks[taskId].tState = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
#define TIMER_STOP 0x7FFF
|
|
#define tTimer data[1]
|
|
#define tMainTaskId data[2]
|
|
|
|
static void Task_CycleSceneryPalette(u8 taskId)
|
|
{
|
|
s16 bikeTaskId;
|
|
|
|
switch (gTasks[taskId].tState)
|
|
{
|
|
default:
|
|
case SCENE_OCEAN_MORNING:
|
|
if (gTasks[taskId].tTimer != TIMER_STOP)
|
|
{
|
|
if (gTasks[gTasks[gTasks[taskId].tMainTaskId].tTaskId_UpdatePage].tCurrentPage == 2)
|
|
{
|
|
gTasks[gTasks[gTasks[taskId].tMainTaskId].tTaskId_BikeScene].tState = 20;
|
|
gTasks[taskId].tTimer = TIMER_STOP;
|
|
}
|
|
}
|
|
CycleSceneryPalette(0);
|
|
break;
|
|
case SCENE_OCEAN_SUNSET:
|
|
CycleSceneryPalette(0);
|
|
break;
|
|
case SCENE_FOREST_RIVAL_ARRIVE:
|
|
if (gTasks[taskId].tTimer != TIMER_STOP)
|
|
{
|
|
bikeTaskId = gTasks[gTasks[taskId].tMainTaskId].tTaskId_BikeScene;
|
|
|
|
// Floor to multiple of 128
|
|
if ((gTasks[bikeTaskId].tSinIdx & -128) == 640)
|
|
{
|
|
gTasks[bikeTaskId].tState = 1;
|
|
gTasks[taskId].tTimer = TIMER_STOP;
|
|
}
|
|
}
|
|
CycleSceneryPalette(1);
|
|
break;
|
|
case SCENE_FOREST_CATCH_RIVAL:
|
|
if (gTasks[taskId].tTimer != TIMER_STOP)
|
|
{
|
|
|
|
if (gTasks[taskId].tTimer == 584)
|
|
{
|
|
gTasks[gTasks[gTasks[taskId].tMainTaskId].tTaskId_BikeScene].tState = 10;
|
|
gTasks[taskId].tTimer = TIMER_STOP;
|
|
}
|
|
else
|
|
{
|
|
gTasks[taskId].tTimer++;
|
|
}
|
|
}
|
|
CycleSceneryPalette(1);
|
|
break;
|
|
case SCENE_CITY_NIGHT:
|
|
CycleSceneryPalette(2);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void SetBikeScene(u8 scene, u8 taskId)
|
|
{
|
|
switch (scene)
|
|
{
|
|
case SCENE_OCEAN_MORNING:
|
|
gSprites[gTasks[taskId].tPlayerSpriteId].invisible = FALSE;
|
|
gSprites[gTasks[taskId].tRivalSpriteId].invisible = FALSE;
|
|
gSprites[gTasks[taskId].tPlayerSpriteId].x = DISPLAY_WIDTH + 32;
|
|
gSprites[gTasks[taskId].tRivalSpriteId].x = DISPLAY_WIDTH + 32;
|
|
gSprites[gTasks[taskId].tPlayerSpriteId].y = 46;
|
|
gSprites[gTasks[taskId].tRivalSpriteId].y = 46;
|
|
gSprites[gTasks[taskId].tPlayerSpriteId].data[0] = 0;
|
|
gSprites[gTasks[taskId].tRivalSpriteId].data[0] = 0;
|
|
gTasks[taskId].tTaskId_BgScenery = CreateBicycleBgAnimationTask(0, 0x2000, 0x20, 8);
|
|
break;
|
|
case SCENE_OCEAN_SUNSET:
|
|
gSprites[gTasks[taskId].tPlayerSpriteId].invisible = FALSE;
|
|
gSprites[gTasks[taskId].tRivalSpriteId].invisible = FALSE;
|
|
gSprites[gTasks[taskId].tPlayerSpriteId].x = 120;
|
|
gSprites[gTasks[taskId].tRivalSpriteId].x = DISPLAY_WIDTH + 32;
|
|
gSprites[gTasks[taskId].tPlayerSpriteId].y = 46;
|
|
gSprites[gTasks[taskId].tRivalSpriteId].y = 46;
|
|
gSprites[gTasks[taskId].tPlayerSpriteId].data[0] = 0;
|
|
gSprites[gTasks[taskId].tRivalSpriteId].data[0] = 0;
|
|
gTasks[taskId].tTaskId_BgScenery = CreateBicycleBgAnimationTask(0, 0x2000, 0x20, 8);
|
|
break;
|
|
case SCENE_FOREST_RIVAL_ARRIVE:
|
|
gSprites[gTasks[taskId].tPlayerSpriteId].invisible = FALSE;
|
|
gSprites[gTasks[taskId].tRivalSpriteId].invisible = FALSE;
|
|
gSprites[gTasks[taskId].tPlayerSpriteId].x = 120;
|
|
gSprites[gTasks[taskId].tRivalSpriteId].x = DISPLAY_WIDTH + 32;
|
|
gSprites[gTasks[taskId].tPlayerSpriteId].y = 46;
|
|
gSprites[gTasks[taskId].tRivalSpriteId].y = 46;
|
|
gSprites[gTasks[taskId].tPlayerSpriteId].data[0] = 0;
|
|
gSprites[gTasks[taskId].tRivalSpriteId].data[0] = 0;
|
|
gTasks[taskId].tTaskId_BgScenery = CreateBicycleBgAnimationTask(1, 0x2000, 0x200, 8);
|
|
break;
|
|
case SCENE_FOREST_CATCH_RIVAL:
|
|
gSprites[gTasks[taskId].tPlayerSpriteId].invisible = FALSE;
|
|
gSprites[gTasks[taskId].tRivalSpriteId].invisible = FALSE;
|
|
gSprites[gTasks[taskId].tPlayerSpriteId].x = 120;
|
|
gSprites[gTasks[taskId].tRivalSpriteId].x = -32;
|
|
gSprites[gTasks[taskId].tPlayerSpriteId].y = 46;
|
|
gSprites[gTasks[taskId].tRivalSpriteId].y = 46;
|
|
gSprites[gTasks[taskId].tPlayerSpriteId].data[0] = 0;
|
|
gSprites[gTasks[taskId].tRivalSpriteId].data[0] = 0;
|
|
gTasks[taskId].tTaskId_BgScenery = CreateBicycleBgAnimationTask(1, 0x2000, 0x200, 8);
|
|
break;
|
|
case SCENE_CITY_NIGHT:
|
|
gSprites[gTasks[taskId].tPlayerSpriteId].invisible = FALSE;
|
|
gSprites[gTasks[taskId].tRivalSpriteId].invisible = FALSE;
|
|
gSprites[gTasks[taskId].tPlayerSpriteId].x = 88;
|
|
gSprites[gTasks[taskId].tRivalSpriteId].x = 152;
|
|
gSprites[gTasks[taskId].tPlayerSpriteId].y = 46;
|
|
gSprites[gTasks[taskId].tRivalSpriteId].y = 46;
|
|
gSprites[gTasks[taskId].tPlayerSpriteId].data[0] = 0;
|
|
gSprites[gTasks[taskId].tRivalSpriteId].data[0] = 0;
|
|
gTasks[taskId].tTaskId_BgScenery = CreateBicycleBgAnimationTask(2, 0x2000, 0x200, 8);
|
|
break;
|
|
}
|
|
|
|
gTasks[taskId].tTaskId_SceneryPal = CreateTask(Task_CycleSceneryPalette, 0);
|
|
gTasks[gTasks[taskId].tTaskId_SceneryPal].tState = scene;
|
|
gTasks[gTasks[taskId].tTaskId_SceneryPal].tTimer = 0;
|
|
gTasks[gTasks[taskId].tTaskId_SceneryPal].tMainTaskId = taskId;
|
|
|
|
gTasks[taskId].tTaskId_BikeScene = CreateTask(Task_BikeScene, 0);
|
|
gTasks[gTasks[taskId].tTaskId_BikeScene].tState = 0;
|
|
gTasks[gTasks[taskId].tTaskId_BikeScene].data[1] = taskId; // data[1] is never read
|
|
gTasks[gTasks[taskId].tTaskId_BikeScene].tPlayer = gTasks[taskId].tPlayerSpriteId;
|
|
gTasks[gTasks[taskId].tTaskId_BikeScene].tRival = gTasks[taskId].tRivalSpriteId;
|
|
gTasks[gTasks[taskId].tTaskId_BikeScene].tDelay = 0;
|
|
|
|
if (scene == SCENE_FOREST_RIVAL_ARRIVE)
|
|
gTasks[gTasks[taskId].tTaskId_BikeScene].tSinIdx = 69;
|
|
}
|
|
|
|
#undef tTimer
|
|
#undef tDelay
|
|
#undef tSinIdx
|
|
#undef tRival
|
|
#undef tPlayer
|
|
|
|
static bool8 LoadBikeScene(u8 scene, u8 taskId)
|
|
{
|
|
u8 spriteId;
|
|
|
|
switch (gMain.state)
|
|
{
|
|
default:
|
|
case 0:
|
|
SetGpuReg(REG_OFFSET_DISPCNT, 0);
|
|
SetGpuReg(REG_OFFSET_BG3HOFS, 8);
|
|
SetGpuReg(REG_OFFSET_BG3VOFS, 0);
|
|
SetGpuReg(REG_OFFSET_BG2HOFS, 0);
|
|
SetGpuReg(REG_OFFSET_BG2VOFS, 0);
|
|
SetGpuReg(REG_OFFSET_BG1HOFS, 0);
|
|
SetGpuReg(REG_OFFSET_BG1VOFS, 0);
|
|
SetGpuReg(REG_OFFSET_BLDCNT, 0);
|
|
SetGpuReg(REG_OFFSET_BLDALPHA, 0);
|
|
ResetSpriteData();
|
|
FreeAllSpritePalettes();
|
|
gMain.state = 1;
|
|
break;
|
|
case 1:
|
|
gIntroCredits_MovingSceneryVBase = 34;
|
|
gIntroCredits_MovingSceneryVOffset = 0;
|
|
LoadCreditsSceneGraphics(scene);
|
|
gMain.state++;
|
|
break;
|
|
case 2:
|
|
if (gSaveBlock2Ptr->playerGender == MALE)
|
|
{
|
|
LoadCompressedSpriteSheet(gSpriteSheet_CreditsBrendan);
|
|
LoadCompressedSpriteSheet(gSpriteSheet_CreditsRivalMay);
|
|
LoadCompressedSpriteSheet(gSpriteSheet_CreditsBicycle);
|
|
LoadSpritePalettes(gSpritePalettes_Credits);
|
|
|
|
spriteId = CreateIntroBrendanSprite(120, 46);
|
|
gTasks[taskId].tPlayerSpriteId = spriteId;
|
|
gSprites[spriteId].callback = SpriteCB_Player;
|
|
gSprites[spriteId].anims = sAnims_Player;
|
|
|
|
spriteId = CreateIntroMaySprite(DISPLAY_WIDTH + 32, 46);
|
|
gTasks[taskId].tRivalSpriteId = spriteId;
|
|
gSprites[spriteId].callback = SpriteCB_Rival;
|
|
gSprites[spriteId].anims = sAnims_Rival;
|
|
}
|
|
else
|
|
{
|
|
LoadCompressedSpriteSheet(gSpriteSheet_CreditsMay);
|
|
LoadCompressedSpriteSheet(gSpriteSheet_CreditsRivalBrendan);
|
|
LoadCompressedSpriteSheet(gSpriteSheet_CreditsBicycle);
|
|
LoadSpritePalettes(gSpritePalettes_Credits);
|
|
|
|
spriteId = CreateIntroMaySprite(120, 46);
|
|
gTasks[taskId].tPlayerSpriteId = spriteId;
|
|
gSprites[spriteId].callback = SpriteCB_Player;
|
|
gSprites[spriteId].anims = sAnims_Player;
|
|
|
|
spriteId = CreateIntroBrendanSprite(DISPLAY_WIDTH + 32, 46);
|
|
gTasks[taskId].tRivalSpriteId = spriteId;
|
|
gSprites[spriteId].callback = SpriteCB_Rival;
|
|
gSprites[spriteId].anims = sAnims_Rival;
|
|
};
|
|
gMain.state++;
|
|
break;
|
|
case 3:
|
|
SetBikeScene(scene, taskId);
|
|
SetCreditsSceneBgCnt(scene);
|
|
gMain.state = 0;
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static void ResetCreditsTasks(u8 taskId)
|
|
{
|
|
// Destroy Task_BicycleBgAnimation, if running
|
|
if (gTasks[taskId].tTaskId_BgScenery != 0)
|
|
{
|
|
DestroyTask(gTasks[taskId].tTaskId_BgScenery);
|
|
gTasks[taskId].tTaskId_BgScenery = 0;
|
|
}
|
|
|
|
// Destroy Task_BikeScene, if running
|
|
if (gTasks[taskId].tTaskId_BikeScene != 0)
|
|
{
|
|
DestroyTask(gTasks[taskId].tTaskId_BikeScene);
|
|
gTasks[taskId].tTaskId_BikeScene = 0;
|
|
}
|
|
|
|
// Destroy Task_CycleSceneryPalette, if running
|
|
if (gTasks[taskId].tTaskId_SceneryPal != 0)
|
|
{
|
|
DestroyTask(gTasks[taskId].tTaskId_SceneryPal);
|
|
gTasks[taskId].tTaskId_SceneryPal = 0;
|
|
}
|
|
|
|
// Destroy Task_ShowMons, if running
|
|
if (gTasks[taskId].tTaskId_ShowMons != 0)
|
|
{
|
|
DestroyTask(gTasks[taskId].tTaskId_ShowMons);
|
|
gTasks[taskId].tTaskId_ShowMons = 0;
|
|
}
|
|
|
|
gIntroCredits_MovingSceneryState = INTROCRED_SCENERY_DESTROY;
|
|
}
|
|
|
|
static void LoadTheEndScreen(u16 arg0, u16 arg1, u16 palOffset)
|
|
{
|
|
u16 baseTile;
|
|
u16 i;
|
|
|
|
LZ77UnCompVram(sCreditsCopyrightEnd_Gfx, (void *)(VRAM + arg0));
|
|
LoadPalette(gIntroCopyright_Pal, palOffset, sizeof(gIntroCopyright_Pal));
|
|
|
|
baseTile = (palOffset / 16) << 12;
|
|
|
|
for (i = 0; i < 32 * 32; i++)
|
|
((u16 *) (VRAM + arg1))[i] = baseTile + 1;
|
|
}
|
|
|
|
static u16 GetLetterMapTile(u8 baseTiles)
|
|
{
|
|
u16 out = (baseTiles & 0x3F) + 80;
|
|
|
|
if (baseTiles == 0xFF)
|
|
return 1;
|
|
|
|
if (baseTiles & (1 << 7))
|
|
out |= 1 << 11;
|
|
if (baseTiles & (1 << 6))
|
|
out |= 1 << 10;
|
|
|
|
return out;
|
|
}
|
|
|
|
static void DrawLetterMapTiles(const u8 baseTiles[], u8 baseX, u8 baseY, u16 offset, u16 palette)
|
|
{
|
|
u8 y, x;
|
|
const u16 tileOffset = (palette / 16) << 12;
|
|
|
|
for (y = 0; y < 5; y++)
|
|
{
|
|
for (x = 0; x < 3; x++)
|
|
((u16 *) (VRAM + offset + (baseY + y) * 64))[baseX + x] = tileOffset + GetLetterMapTile(baseTiles[y * 3 + x]);
|
|
}
|
|
}
|
|
|
|
static void DrawTheEnd(u16 offset, u16 palette)
|
|
{
|
|
u16 pos;
|
|
u16 baseTile = (palette / 16) << 12;
|
|
|
|
for (pos = 0; pos < 32 * 32; pos++)
|
|
((u16 *) (VRAM + offset))[pos] = baseTile + 1;
|
|
|
|
DrawLetterMapTiles(sTheEnd_LetterMap_T, 3, 7, offset, palette);
|
|
DrawLetterMapTiles(sTheEnd_LetterMap_H, 7, 7, offset, palette);
|
|
DrawLetterMapTiles(sTheEnd_LetterMap_E, 11, 7, offset, palette);
|
|
DrawLetterMapTiles(sTheEnd_LetterMap_E, 16, 7, offset, palette);
|
|
DrawLetterMapTiles(sTheEnd_LetterMap_N, 20, 7, offset, palette);
|
|
DrawLetterMapTiles(sTheEnd_LetterMap_D, 24, 7, offset, palette);
|
|
}
|
|
|
|
#define sState data[0]
|
|
|
|
static void SpriteCB_Player(struct Sprite *sprite)
|
|
{
|
|
if (gIntroCredits_MovingSceneryState != INTROCRED_SCENERY_NORMAL)
|
|
{
|
|
DestroySprite(sprite);
|
|
return;
|
|
}
|
|
|
|
switch (sprite->sState)
|
|
{
|
|
case 0:
|
|
StartSpriteAnimIfDifferent(sprite, 0);
|
|
break;
|
|
case 1:
|
|
StartSpriteAnimIfDifferent(sprite, 1);
|
|
if (sprite->x > -32)
|
|
sprite->x--;
|
|
break;
|
|
case 2:
|
|
StartSpriteAnimIfDifferent(sprite, 2);
|
|
break;
|
|
case 3:
|
|
StartSpriteAnimIfDifferent(sprite, 3);
|
|
break;
|
|
case 4:
|
|
StartSpriteAnimIfDifferent(sprite, 0);
|
|
if (sprite->x > 120)
|
|
sprite->x--;
|
|
break;
|
|
case 5:
|
|
StartSpriteAnimIfDifferent(sprite, 0);
|
|
if (sprite->x > -32)
|
|
sprite->x--;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void SpriteCB_Rival(struct Sprite *sprite)
|
|
{
|
|
if (gIntroCredits_MovingSceneryState != INTROCRED_SCENERY_NORMAL)
|
|
{
|
|
DestroySprite(sprite);
|
|
return;
|
|
}
|
|
|
|
switch (sprite->sState)
|
|
{
|
|
case 0:
|
|
sprite->y2 = 0;
|
|
StartSpriteAnimIfDifferent(sprite, 0);
|
|
break;
|
|
case 1:
|
|
if (sprite->x > 200)
|
|
StartSpriteAnimIfDifferent(sprite, 1);
|
|
else
|
|
StartSpriteAnimIfDifferent(sprite, 2);
|
|
if (sprite->x > -32)
|
|
sprite->x -= 2;
|
|
sprite->y2 = -gIntroCredits_MovingSceneryVOffset;
|
|
break;
|
|
case 2:
|
|
sprite->data[7]++;
|
|
StartSpriteAnimIfDifferent(sprite, 0);
|
|
if ((sprite->data[7] & 3) == 0)
|
|
sprite->x++;
|
|
break;
|
|
case 3:
|
|
StartSpriteAnimIfDifferent(sprite, 0);
|
|
if (sprite->x > -32)
|
|
sprite->x--;
|
|
break;
|
|
}
|
|
}
|
|
|
|
#define sPosition data[1]
|
|
#define sSpriteId data[6]
|
|
|
|
static void SpriteCB_CreditsMon(struct Sprite *sprite)
|
|
{
|
|
if (gIntroCredits_MovingSceneryState != INTROCRED_SCENERY_NORMAL)
|
|
{
|
|
FreeAndDestroyMonPicSprite(sprite->sSpriteId);
|
|
return;
|
|
}
|
|
|
|
sprite->data[7]++;
|
|
switch (sprite->sState)
|
|
{
|
|
case 0:
|
|
default:
|
|
sprite->oam.affineMode = ST_OAM_AFFINE_NORMAL;
|
|
sprite->oam.matrixNum = sprite->sPosition;
|
|
sprite->data[2] = 16;
|
|
SetOamMatrix(sprite->sPosition, 0x10000 / sprite->data[2], 0, 0, 0x10000 / sprite->data[2]);
|
|
sprite->invisible = FALSE;
|
|
sprite->sState = 1;
|
|
break;
|
|
case 1:
|
|
if (sprite->data[2] < 256)
|
|
{
|
|
sprite->data[2] += 8;
|
|
SetOamMatrix(sprite->sPosition, 0x10000 / sprite->data[2], 0, 0, 0x10000 / sprite->data[2]);
|
|
}
|
|
else
|
|
{
|
|
sprite->sState++;
|
|
}
|
|
switch (sprite->sPosition)
|
|
{
|
|
case POS_LEFT + 1:
|
|
if ((sprite->data[7] & 3) == 0)
|
|
sprite->y++;
|
|
sprite->x -= 2;
|
|
break;
|
|
case POS_CENTER + 1:
|
|
break;
|
|
case POS_RIGHT + 1:
|
|
if ((sprite->data[7] & 3) == 0)
|
|
sprite->y++;
|
|
sprite->x += 2;
|
|
break;
|
|
}
|
|
break;
|
|
case 2:
|
|
if (sprite->data[3] != 0)
|
|
{
|
|
sprite->data[3]--;
|
|
}
|
|
else
|
|
{
|
|
SetGpuReg(REG_OFFSET_BLDCNT, BLDCNT_EFFECT_BLEND | BLDCNT_TGT2_BG0 | BLDCNT_TGT2_BG1 | BLDCNT_TGT2_BG2 | BLDCNT_TGT2_BG3);
|
|
SetGpuReg(REG_OFFSET_BLDALPHA, BLDALPHA_BLEND(16, 0));
|
|
sprite->oam.objMode = ST_OAM_OBJ_BLEND;
|
|
sprite->data[3] = 16;
|
|
sprite->sState++;
|
|
}
|
|
break;
|
|
case 3:
|
|
if (sprite->data[3] != 0)
|
|
{
|
|
int data3;
|
|
|
|
sprite->data[3]--;
|
|
|
|
data3 = 16 - sprite->data[3];
|
|
SetGpuReg(REG_OFFSET_BLDALPHA, (data3 << 8) + sprite->data[3]);
|
|
}
|
|
else
|
|
{
|
|
sprite->invisible = TRUE;
|
|
sprite->sState = 9;
|
|
}
|
|
break;
|
|
case 9:
|
|
sprite->sState++;
|
|
break;
|
|
case 10:
|
|
SetGpuReg(REG_OFFSET_BLDCNT, 0);
|
|
SetGpuReg(REG_OFFSET_BLDALPHA, 0);
|
|
FreeAndDestroyMonPicSprite(sprite->data[6]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
#define sMonSpriteId data[0]
|
|
|
|
static u8 CreateCreditsMonSprite(u16 nationalDexNum, s16 x, s16 y, u16 position)
|
|
{
|
|
u8 monSpriteId;
|
|
u8 bgSpriteId;
|
|
|
|
monSpriteId = CreateMonSpriteFromNationalDexNumber(nationalDexNum, x, y, position);
|
|
gSprites[monSpriteId].oam.priority = 1;
|
|
gSprites[monSpriteId].sPosition = position + 1;
|
|
gSprites[monSpriteId].invisible = TRUE;
|
|
gSprites[monSpriteId].callback = SpriteCB_CreditsMon;
|
|
gSprites[monSpriteId].sSpriteId = monSpriteId;
|
|
|
|
bgSpriteId = CreateSprite(&sSpriteTemplate_CreditsMonBg, gSprites[monSpriteId].x, gSprites[monSpriteId].y, 1);
|
|
gSprites[bgSpriteId].sMonSpriteId = monSpriteId;
|
|
|
|
StartSpriteAnimIfDifferent(&gSprites[bgSpriteId], position);
|
|
|
|
return monSpriteId;
|
|
}
|
|
|
|
static void SpriteCB_CreditsMonBg(struct Sprite *sprite)
|
|
{
|
|
if (gSprites[sprite->sMonSpriteId].data[0] == 10
|
|
|| gIntroCredits_MovingSceneryState != INTROCRED_SCENERY_NORMAL)
|
|
{
|
|
DestroySprite(sprite);
|
|
return;
|
|
}
|
|
|
|
// Copy sprite data from the associated Pokémon
|
|
sprite->invisible = gSprites[sprite->sMonSpriteId].invisible;
|
|
sprite->oam.objMode = gSprites[sprite->sMonSpriteId].oam.objMode;
|
|
sprite->oam.affineMode = gSprites[sprite->sMonSpriteId].oam.affineMode;
|
|
sprite->oam.matrixNum = gSprites[sprite->sMonSpriteId].oam.matrixNum;
|
|
sprite->x = gSprites[sprite->sMonSpriteId].x;
|
|
sprite->y = gSprites[sprite->sMonSpriteId].y;
|
|
}
|
|
|
|
static void DeterminePokemonToShow(void)
|
|
{
|
|
u16 starter = SpeciesToNationalPokedexNum(GetStarterPokemon(VarGet(VAR_STARTER_MON)));
|
|
u16 page;
|
|
u16 dexNum;
|
|
u16 j;
|
|
|
|
// Go through the Pokedex, and anything that has gotten caught we put into our massive array.
|
|
// This basically packs all of the caught pokemon into the front of the array
|
|
for (dexNum = 1, j = 0; dexNum < NATIONAL_DEX_COUNT; dexNum++)
|
|
{
|
|
if (GetSetPokedexFlag(dexNum, FLAG_GET_CAUGHT))
|
|
{
|
|
sCreditsData->caughtMonIds[j] = dexNum;
|
|
j++;
|
|
}
|
|
}
|
|
|
|
// Fill the rest of the array with zeroes
|
|
for (dexNum = j; dexNum < NATIONAL_DEX_COUNT; dexNum++)
|
|
sCreditsData->caughtMonIds[dexNum] = NATIONAL_DEX_NONE;
|
|
|
|
// Cap the number of pokemon we care about to NUM_MON_SLIDES, the max we show in the credits scene (-1 for the starter)
|
|
sCreditsData->numCaughtMon = j;
|
|
if (sCreditsData->numCaughtMon < NUM_MON_SLIDES)
|
|
sCreditsData->numMonToShow = j;
|
|
else
|
|
sCreditsData->numMonToShow = NUM_MON_SLIDES;
|
|
|
|
// Loop through our list of caught pokemon and select randomly from it to fill the images to show
|
|
j = 0;
|
|
do
|
|
{
|
|
// Select a random mon, insert into array
|
|
page = Random() % sCreditsData->numCaughtMon;
|
|
sCreditsData->monToShow[j] = sCreditsData->caughtMonIds[page];
|
|
|
|
// Remove the select mon from the array, and condense array entries
|
|
j++;
|
|
sCreditsData->caughtMonIds[page] = 0;
|
|
sCreditsData->numCaughtMon--;
|
|
if (page != sCreditsData->numCaughtMon)
|
|
{
|
|
// Instead of looping through and moving everything down, just take from the end. Order doesn't matter after all.
|
|
sCreditsData->caughtMonIds[page] = sCreditsData->caughtMonIds[sCreditsData->numCaughtMon];
|
|
sCreditsData->caughtMonIds[sCreditsData->numCaughtMon] = 0;
|
|
}
|
|
}
|
|
while (sCreditsData->numCaughtMon != 0 && j < NUM_MON_SLIDES);
|
|
|
|
// If we don't have enough pokemon in the dex to fill everything, copy the selected mon into the end of the array, so it loops
|
|
if (sCreditsData->numMonToShow < NUM_MON_SLIDES)
|
|
{
|
|
for (j = sCreditsData->numMonToShow, page = 0; j < NUM_MON_SLIDES; j++)
|
|
{
|
|
sCreditsData->monToShow[j] = sCreditsData->monToShow[page];
|
|
|
|
page++;
|
|
if (page == sCreditsData->numMonToShow)
|
|
page = 0;
|
|
}
|
|
// Ensure the last pokemon is our starter
|
|
sCreditsData->monToShow[NUM_MON_SLIDES - 1] = starter;
|
|
}
|
|
else
|
|
{
|
|
// Check to see if our starter has already appeared in this list, break if it has
|
|
for (dexNum = 0; sCreditsData->monToShow[dexNum] != starter && dexNum < NUM_MON_SLIDES; dexNum++);
|
|
|
|
// If it has, swap it with the last pokemon, to ensure our starter is the last image
|
|
if (dexNum < sCreditsData->numMonToShow - 1)
|
|
{
|
|
sCreditsData->monToShow[dexNum] = sCreditsData->monToShow[NUM_MON_SLIDES-1];
|
|
sCreditsData->monToShow[NUM_MON_SLIDES - 1] = starter;
|
|
}
|
|
else
|
|
{
|
|
// Ensure the last pokemon is our starter
|
|
sCreditsData->monToShow[NUM_MON_SLIDES - 1] = starter;
|
|
}
|
|
}
|
|
sCreditsData->numMonToShow = NUM_MON_SLIDES;
|
|
}
|