pokeruby/src/starter_choose.c
Eduardo Quezada afcbea1f17
Some checks are pending
CI / build (push) Waiting to run
Use JOY_NEW, JOY_HELD and JOY_REPT across the repo (#885)
2026-02-14 02:33:00 -05:00

616 lines
17 KiB
C

#include "global.h"
#include "starter_choose.h"
#include "data2.h"
#include "decompress.h"
#include "main.h"
#include "menu.h"
#include "palette.h"
#include "pokedex.h"
#include "constants/songs.h"
#include "sound.h"
#include "constants/species.h"
#include "sprite.h"
#include "string_util.h"
#include "strings.h"
#include "task.h"
#include "trig.h"
#include "scanline_effect.h"
extern u16 gSpecialVar_Result;
extern struct SpriteTemplate gCreatingSpriteTemplate;
//--------------------------------------------------
// Graphics Data
//--------------------------------------------------
const u16 gBirchBagGrassPal[2][16] =
{
INCBIN_U16("graphics/misc/birch_bag.gbapal"),
INCBIN_U16("graphics/misc/birch_grass.gbapal"),
};
static const u16 gBirchBallarrow_Pal[] = INCBIN_U16("graphics/misc/birch_ballarrow.gbapal");
static const u16 gBirchCircle_Pal[] = INCBIN_U16("graphics/misc/birch_circle.gbapal");
const u8 gBirchBagTilemap[] = INCBIN_U8("graphics/misc/birch_bag_map.bin.lz");
const u8 gBirchGrassTilemap[] = INCBIN_U8("graphics/misc/birch_grass_map.bin.lz");
const u8 gBirchHelpGfx[] = INCBIN_U8("graphics/misc/birch_help.4bpp.lz");
static const u8 gBirchBallarrow_Gfx[] = INCBIN_U8("graphics/misc/birch_ballarrow.4bpp.lz");
static const u8 gBirchCircle_Gfx[] = INCBIN_U8("graphics/misc/birch_circle.4bpp.lz");
static const u8 gStarterChoose_PokeballCoords[][2] =
{
{60, 64},
{120, 88},
{180, 64},
};
static const u8 gStarterChoose_LabelCoords[][2] =
{
{0, 9},
{16, 10},
{8, 4},
};
static const u16 sStarterMons[] = {SPECIES_TREECKO, SPECIES_TORCHIC, SPECIES_MUDKIP};
static const struct OamData gOamData_83F76CC =
{
.y = 160,
.affineMode = 0,
.objMode = 0,
.mosaic = 0,
.bpp = 0,
.shape = 0,
.x = 0,
.matrixNum = 0,
.size = 2,
.tileNum = 0,
.priority = 1,
.paletteNum = 0,
.affineParam = 0,
};
static const struct OamData gOamData_83F76D4 =
{
.y = 160,
.affineMode = 0,
.objMode = 0,
.mosaic = 0,
.bpp = 0,
.shape = 0,
.x = 0,
.matrixNum = 0,
.size = 2,
.tileNum = 0,
.priority = 1,
.paletteNum = 0,
.affineParam = 0,
};
static const struct OamData gOamData_83F76DC =
{
.y = 160,
.affineMode = 3,
.objMode = 0,
.mosaic = 0,
.bpp = 0,
.shape = 0,
.x = 0,
.matrixNum = 0,
.size = 3,
.tileNum = 0,
.priority = 1,
.paletteNum = 0,
.affineParam = 0,
};
static const u8 gUnknown_083F76E4[][2] =
{
{60, 32},
{120, 56},
{180, 32},
{0, 0},
};
static const union AnimCmd gSpriteAnim_83F76EC[] =
{
ANIMCMD_FRAME(48, 30),
ANIMCMD_END,
};
static const union AnimCmd gSpriteAnim_83F76F4[] =
{
ANIMCMD_FRAME(0, 30),
ANIMCMD_END,
};
static const union AnimCmd gSpriteAnim_83F76FC[] =
{
ANIMCMD_FRAME(16, 4),
ANIMCMD_FRAME(0, 4),
ANIMCMD_FRAME(32, 4),
ANIMCMD_FRAME(0, 4),
ANIMCMD_FRAME(16, 4),
ANIMCMD_FRAME(0, 4),
ANIMCMD_FRAME(32, 4),
ANIMCMD_FRAME(0, 4),
ANIMCMD_FRAME(0, 32),
ANIMCMD_FRAME(16, 8),
ANIMCMD_FRAME(0, 8),
ANIMCMD_FRAME(32, 8),
ANIMCMD_FRAME(0, 8),
ANIMCMD_FRAME(16, 8),
ANIMCMD_FRAME(0, 8),
ANIMCMD_FRAME(32, 8),
ANIMCMD_FRAME(0, 8),
ANIMCMD_JUMP(0),
};
static const union AnimCmd gSpriteAnim_83F7744[] =
{
ANIMCMD_FRAME(0, 8),
ANIMCMD_END,
};
static const union AnimCmd *const gSpriteAnimTable_83F774C[] =
{
gSpriteAnim_83F76EC,
};
static const union AnimCmd *const gSpriteAnimTable_83F7750[] =
{
gSpriteAnim_83F76F4,
gSpriteAnim_83F76FC,
};
static const union AnimCmd *const gSpriteAnimTable_83F7758[] =
{
gSpriteAnim_83F7744,
};
static const union AffineAnimCmd gSpriteAffineAnim_83F775C[] =
{
AFFINEANIMCMD_FRAME(16, 16, 0, 0),
AFFINEANIMCMD_FRAME(16, 16, 0, 15),
AFFINEANIMCMD_END,
};
static const union AffineAnimCmd gSpriteAffineAnim_83F7774[] =
{
AFFINEANIMCMD_FRAME(20, 20, 0, 0),
AFFINEANIMCMD_FRAME(20, 20, 0, 15),
AFFINEANIMCMD_END,
};
static const union AffineAnimCmd *const gSpriteAffineAnimTable_83F778C[] =
{
gSpriteAffineAnim_83F775C,
};
static const union AffineAnimCmd *const gSpriteAffineAnimTable_83F7790[] =
{
gSpriteAffineAnim_83F7774,
};
static const struct CompressedSpriteSheet gUnknown_083F7794[] =
{
{gBirchBallarrow_Gfx, 0x0800, 0x1000},
{NULL},
};
static const struct CompressedSpriteSheet gUnknown_083F77A4[] =
{
{gBirchCircle_Gfx, 0x0800, 0x1001},
{NULL},
};
const struct SpritePalette gUnknown_083F77B4[] =
{
{gBirchBallarrow_Pal, 0x1000},
{gBirchCircle_Pal, 0x1001},
{NULL},
};
static void sub_810A62C(struct Sprite *sprite);
static const struct SpriteTemplate gSpriteTemplate_83F77CC =
{
.tileTag = 4096,
.paletteTag = 4096,
.oam = &gOamData_83F76CC,
.anims = gSpriteAnimTable_83F774C,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = sub_810A62C,
};
static void sub_810A68C(struct Sprite *sprite);
static const struct SpriteTemplate gSpriteTemplate_83F77E4 =
{
.tileTag = 4096,
.paletteTag = 4096,
.oam = &gOamData_83F76D4,
.anims = gSpriteAnimTable_83F7750,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = sub_810A68C,
};
static void StarterPokemonSpriteAnimCallback(struct Sprite *sprite);
static const struct SpriteTemplate gSpriteTemplate_83F77FC =
{
.tileTag = 4097,
.paletteTag = 4097,
.oam = &gOamData_83F76DC,
.anims = gSpriteAnimTable_83F7758,
.images = NULL,
.affineAnims = gSpriteAffineAnimTable_83F7790,
.callback = StarterPokemonSpriteAnimCallback,
};
static void MainCallback2(void);
static void Task_StarterChoose1(u8 taskId);
static void Task_StarterChoose2(u8 taskId);
static void Task_StarterChoose3(u8 taskId);
static void Task_StarterChoose4(u8 taskId);
static void Task_StarterChoose5(u8 taskId);
static void Task_StarterChoose6(u8 taskId);
static void CreateStarterPokemonLabel(u8, u8);
static u8 CreatePokemonFrontSprite(u16, u8, u8);
//Position of the sprite of the selected starter Pokemon
#define STARTER_PKMN_POS_X 120
#define STARTER_PKMN_POS_Y 64
//Retrieves one of the available starter Pokemon
u16 GetStarterPokemon(u16 n)
{
if (n > 3)
n = 0;
return sStarterMons[n];
}
static void VblankCallback(void)
{
LoadOam();
ProcessSpriteCopyRequests();
TransferPlttBuffer();
}
#define tStarterSelection data[0]
#define tPkmnSpriteId data[1]
#define tCircleSpriteId data[2]
void CB2_ChooseStarter(void)
{
u16 savedIme;
u8 taskId;
u8 spriteId;
SetVBlankCallback(NULL);
REG_DISPCNT = 0;
REG_BG3CNT = 0;
REG_BG2CNT = 0;
REG_BG1CNT = 0;
REG_BG0CNT = 0;
REG_BG3HOFS = 0;
REG_BG3VOFS = 0;
REG_BG2HOFS = 0;
REG_BG2VOFS = 0;
REG_BG1HOFS = 0;
REG_BG1VOFS = 0;
REG_BG0HOFS = 0;
REG_BG0VOFS = 0;
DmaFill16(3, 0, VRAM, VRAM_SIZE);
DmaFill32(3, 0, OAM, OAM_SIZE);
DmaFill16(3, 0, PLTT, PLTT_SIZE);
LZ77UnCompVram(&gBirchHelpGfx, (void *)VRAM);
LZ77UnCompVram(&gBirchBagTilemap, (void *)(VRAM + 0x3000));
LZ77UnCompVram(&gBirchGrassTilemap, (void *)(VRAM + 0x3800));
ScanlineEffect_Stop();
ResetTasks();
ResetSpriteData();
ResetPaletteFade();
FreeAllSpritePalettes();
LoadPalette(gBirchBagGrassPal, 0, sizeof(gBirchBagGrassPal));
LoadCompressedObjectPic(&gUnknown_083F7794[0]);
LoadCompressedObjectPic(&gUnknown_083F77A4[0]);
LoadSpritePalettes(gUnknown_083F77B4);
Text_LoadWindowTemplate(&gWindowTemplate_81E6C3C);
InitMenuWindow(&gMenuTextWindowTemplate);
BeginNormalPaletteFade(0xFFFFFFFF, 0, 16, 0, RGB(0, 0, 0));
savedIme = REG_IME;
REG_IME = 0;
REG_IE |= INTR_FLAG_VBLANK;
REG_IME = savedIme;
REG_DISPSTAT |= DISPSTAT_VBLANK_INTR;
SetVBlankCallback(VblankCallback);
SetMainCallback2(MainCallback2);
REG_WININ = 0x3F;
REG_WINOUT = 0x1F;
REG_WIN0H = 0;
REG_WIN0V = 0;
REG_BLDCNT = 0xFE;
REG_BLDALPHA = 0;
REG_BLDY = 0x7;
REG_BG3CNT = BGCNT_PRIORITY(3) | BGCNT_CHARBASE(0) | BGCNT_SCREENBASE(7) | BGCNT_16COLOR | BGCNT_TXT256x256;
REG_BG2CNT = BGCNT_PRIORITY(2) | BGCNT_CHARBASE(0) | BGCNT_SCREENBASE(6) | BGCNT_16COLOR | BGCNT_TXT256x256;
REG_BG0CNT = BGCNT_PRIORITY(0) | BGCNT_CHARBASE(2) | BGCNT_SCREENBASE(31) | BGCNT_16COLOR | BGCNT_TXT256x256;
REG_DISPCNT = DISPCNT_MODE_0 | DISPCNT_OBJ_1D_MAP | DISPCNT_BG0_ON | DISPCNT_BG2_ON | DISPCNT_BG3_ON | DISPCNT_OBJ_ON | DISPCNT_WIN0_ON;
taskId = CreateTask(Task_StarterChoose1, 0);
gTasks[taskId].tStarterSelection = 1;
//Create hand sprite
spriteId = CreateSprite(&gSpriteTemplate_83F77CC, 120, 56, 2);
gSprites[spriteId].data[0] = taskId;
//Create three Pokeball sprites
spriteId = CreateSprite(
&gSpriteTemplate_83F77E4,
gStarterChoose_PokeballCoords[0][0], gStarterChoose_PokeballCoords[0][1], 2);
gSprites[spriteId].data[0] = taskId;
gSprites[spriteId].data[1] = 0;
spriteId = CreateSprite(
&gSpriteTemplate_83F77E4,
gStarterChoose_PokeballCoords[1][0], gStarterChoose_PokeballCoords[1][1], 2);
gSprites[spriteId].data[0] = taskId;
gSprites[spriteId].data[1] = 1;
spriteId = CreateSprite(
&gSpriteTemplate_83F77E4,
gStarterChoose_PokeballCoords[2][0], gStarterChoose_PokeballCoords[2][1], 2);
gSprites[spriteId].data[0] = taskId;
gSprites[spriteId].data[1] = 2;
}
static void MainCallback2(void)
{
RunTasks();
AnimateSprites();
BuildOamBuffer();
UpdatePaletteFade();
}
static void Task_StarterChoose1(u8 taskId)
{
CreateStarterPokemonLabel(0xFF, gTasks[taskId].tStarterSelection);
Menu_DrawStdWindowFrame(2, 14, 27, 19);
Menu_PrintText(gOtherText_BirchInTrouble, 3, 15);
gTasks[taskId].func = Task_StarterChoose2;
}
static void Task_StarterChoose2(u8 taskId)
{
u8 selection = gTasks[taskId].tStarterSelection;
if (JOY_NEW(A_BUTTON))
{
u8 spriteId;
Menu_EraseWindowRect(
gStarterChoose_LabelCoords[selection][0],
gStarterChoose_LabelCoords[selection][1],
gStarterChoose_LabelCoords[selection][0] + 13,
gStarterChoose_LabelCoords[selection][1] + 3);
REG_WIN0H = 0;
REG_WIN0V = 0;
//Create white circle background
spriteId = CreateSprite(
&gSpriteTemplate_83F77FC,
gStarterChoose_PokeballCoords[selection][0],
gStarterChoose_PokeballCoords[selection][1],
1);
gTasks[taskId].tCircleSpriteId = spriteId;
//Create Pokemon sprite
spriteId = CreatePokemonFrontSprite(
GetStarterPokemon(gTasks[taskId].tStarterSelection),
gStarterChoose_PokeballCoords[selection][0],
gStarterChoose_PokeballCoords[selection][1]);
gSprites[spriteId].affineAnims = gSpriteAffineAnimTable_83F778C;
gSprites[spriteId].callback = StarterPokemonSpriteAnimCallback;
gTasks[taskId].tPkmnSpriteId = spriteId;
gTasks[taskId].func = Task_StarterChoose3;
}
else
{
if (JOY_NEW(DPAD_LEFT) && selection > 0)
{
gTasks[taskId].tStarterSelection--;
CreateStarterPokemonLabel(selection, gTasks[taskId].tStarterSelection);
}
else if (JOY_NEW(DPAD_RIGHT) && selection < 2)
{
gTasks[taskId].tStarterSelection++;
CreateStarterPokemonLabel(selection, gTasks[taskId].tStarterSelection);
}
}
}
static void Task_StarterChoose3(u8 taskId)
{
if (gSprites[gTasks[taskId].tCircleSpriteId].affineAnimEnded &&
gSprites[gTasks[taskId].tCircleSpriteId].x == STARTER_PKMN_POS_X &&
gSprites[gTasks[taskId].tCircleSpriteId].y == STARTER_PKMN_POS_Y)
{
gTasks[taskId].func = Task_StarterChoose4;
}
}
static void Task_StarterChoose4(u8 taskId)
{
PlayCry1(GetStarterPokemon(gTasks[taskId].tStarterSelection), 0);
Menu_DrawStdWindowFrame(2, 14, 27, 19);
//"Do you choose this POKEMON?"
Menu_PrintText(gOtherText_DoYouChoosePoke, 3, 15);
DisplayYesNoMenu(21, 7, 1);
gTasks[taskId].func = Task_StarterChoose5;
}
static void Task_StarterChoose5(u8 taskId)
{
u8 spriteId;
switch (Menu_ProcessInputNoWrap_())
{
case 0: // YES
//Return the starter choice and exit.
gSpecialVar_Result = gTasks[taskId].tStarterSelection;
SetMainCallback2(gMain.savedCallback);
break;
case 1: // NO
case -1: // B button
PlaySE(SE_SELECT);
Menu_EraseWindowRect(21, 7, 27, 12);
spriteId = gTasks[taskId].tPkmnSpriteId;
FreeSpritePaletteByTag(GetSpritePaletteTagByPaletteNum(gSprites[spriteId].oam.paletteNum));
FreeOamMatrix(gSprites[spriteId].oam.matrixNum);
DestroySprite(&gSprites[spriteId]);
spriteId = gTasks[taskId].tCircleSpriteId;
FreeOamMatrix(gSprites[spriteId].oam.matrixNum);
DestroySprite(&gSprites[spriteId]);
gTasks[taskId].func = Task_StarterChoose6;
break;
}
}
static void Task_StarterChoose6(u8 taskId)
{
gTasks[taskId].func = Task_StarterChoose1;
}
void AddTextColorCtrlCode(u8 *string, u8 bgColor, u8 textColor, u8 shadowColor)
{
*(string++) = EXT_CTRL_CODE_BEGIN;
*(string++) = 4;
*(string++) = textColor;
*(string++) = bgColor;
*(string++) = shadowColor;
}
#define SET_CHAR(str, index, c) \
{ \
u8 *p = str + index; \
*p = c; \
}
static void CreateStarterPokemonLabel(u8 prevSelection, u8 selection)
{
u8 labelText[72];
const u8 *category;
u8 srcIndex;
u8 dstIndex;
u16 species;
u8 labelLeft;
u8 labelRight;
u8 labelTop;
u8 labelBottom;
if (prevSelection != 0xFF)
{
//Remove the old Pokemon label
Menu_EraseWindowRect(
gStarterChoose_LabelCoords[prevSelection][0],
gStarterChoose_LabelCoords[prevSelection][1],
gStarterChoose_LabelCoords[prevSelection][0] + 13,
gStarterChoose_LabelCoords[prevSelection][1] + 3);
REG_WIN0H = 0;
REG_WIN0V = 0;
}
species = GetStarterPokemon(selection);
category = GetPokemonCategory(SpeciesToNationalPokedexNum(species));
AddTextColorCtrlCode(labelText, 0, 15, 8);
#if ENGLISH
dstIndex = 5;
SET_CHAR(labelText, 5, EXT_CTRL_CODE_BEGIN);
SET_CHAR(labelText, 6, EXT_CTRL_CODE_CLEAR);
SET_CHAR(labelText, 7, dstIndex);
#endif
//Copy category string to label
dstIndex = 8;
srcIndex = 0;
while (category[srcIndex] != EOS && srcIndex <= 10)
{
labelText[dstIndex] = category[srcIndex];
srcIndex++;
dstIndex++;
}
#if ENGLISH
labelText[dstIndex++] = CHAR_SPACE;
//Copy POKEMON string to label
StringCopy(labelText + dstIndex, gOtherText_Poke);
#elif GERMAN
labelText[dstIndex] = EOS;
labelText[5] = EXT_CTRL_CODE_BEGIN;
labelText[6] = EXT_CTRL_CODE_CLEAR;
labelText[7] = (0x70 - 6 * srcIndex) >> 1;
#endif
Menu_PrintText(
labelText,
gStarterChoose_LabelCoords[selection][0],
gStarterChoose_LabelCoords[selection][1]);
AddTextColorCtrlCode(labelText, 0, 15, 8);
//Copy Pokemon name to label
#if ENGLISH
AlignStringInMenuWindow(labelText + 5, gSpeciesNames[species], 0x6B, 1);
#elif GERMAN
AlignStringInMenuWindow(labelText + 5, gSpeciesNames[species], 0x70, 2);
#endif
Menu_PrintText(
labelText,
gStarterChoose_LabelCoords[selection][0],
gStarterChoose_LabelCoords[selection][1] + 2);
labelLeft = gStarterChoose_LabelCoords[selection][0] * 8 + 4;
labelRight = (gStarterChoose_LabelCoords[selection][0] + 13) * 8 + 4;
labelTop = gStarterChoose_LabelCoords[selection][1] * 8;
labelBottom = (gStarterChoose_LabelCoords[selection][1] + 4) * 8;
REG_WIN0H = WIN_RANGE(labelLeft, labelRight);
REG_WIN0V = WIN_RANGE(labelTop, labelBottom);
}
static void nullsub_72(struct Sprite *sprite)
{
}
static u8 CreatePokemonFrontSprite(u16 species, u8 x, u8 y)
{
u8 spriteId;
DecompressPicFromTable_2(
&gMonFrontPicTable[species],
gMonFrontPicCoords[species].coords, gMonFrontPicCoords[species].y_offset,
gMonSpriteGfx_Sprite_ptr[0], gMonSpriteGfx_Sprite_ptr[1],
species);
LoadCompressedObjectPalette(&gMonPaletteTable[species]);
GetMonSpriteTemplate_803C56C(species, 1);
spriteId = CreateSprite(&gCreatingSpriteTemplate, x, y, 0);
gSprites[spriteId].callback = nullsub_72;
gSprites[spriteId].oam.priority = 0;
return spriteId;
}
//Sprite callback
static void sub_810A62C(struct Sprite *sprite)
{
sprite->x = gUnknown_083F76E4[gTasks[sprite->data[0]].tStarterSelection][0];
sprite->y = gUnknown_083F76E4[gTasks[sprite->data[0]].tStarterSelection][1];
sprite->y2 = Sin(sprite->data[1], 8);
sprite->data[1] = (u8)sprite->data[1] + 4;
}
//Sprite callback
static void sub_810A68C(struct Sprite *sprite)
{
if (gTasks[sprite->data[0]].tStarterSelection == sprite->data[1])
StartSpriteAnimIfDifferent(sprite, 1);
else
StartSpriteAnimIfDifferent(sprite, 0);
}
//Sprite callback
static void StarterPokemonSpriteAnimCallback(struct Sprite *sprite)
{
//Move sprite to upper center of screen
if (sprite->x > STARTER_PKMN_POS_X)
sprite->x -= 4;
if (sprite->x < STARTER_PKMN_POS_X)
sprite->x += 4;
if (sprite->y > STARTER_PKMN_POS_Y)
sprite->y -= 2;
if (sprite->y < STARTER_PKMN_POS_Y)
sprite->y += 2;
}