80x80 Sprites - pret/pokeemerald GitHub Wiki
Disclaimer: This code does not give you functionality for 80x80 sprites in battle. This is only a means of creating 80x80 sprites on the screen; nothing more. Credits:
- froggestspirit for the
CreateBigSprite
code, which I have modified - hedara for the
ConvertToTiles4BppBig
code
sprite.c
u8 CreateBigSpriteAt(u8 index, const struct SpriteTemplate *template, s16 x, s16 y, u8 subpriority)
{
u8 ic;
struct Sprite *spriteA = &gSprites[index];
struct Sprite *spriteB = &gSprites[index + 1];
struct Sprite *spriteC = &gSprites[index + 2];
struct Sprite *spriteD = &gSprites[index + 3];
struct Sprite *spriteE = &gSprites[index + 4];
struct Sprite *spriteF = &gSprites[index + 5];
ResetSprite(spriteA);
ResetSprite(spriteB);
ResetSprite(spriteC);
ResetSprite(spriteD);
ResetSprite(spriteE);
ResetSprite(spriteF);
spriteA->inUse = TRUE;
spriteA->animBeginning = TRUE;
spriteA->affineAnimBeginning = TRUE;
spriteA->usingSheet = TRUE;
spriteA->subpriority = subpriority;
spriteA->oam = *template->oam;
spriteA->anims = template->anims;
spriteA->affineAnims = template->affineAnims;
spriteA->template = template;
spriteA->callback = template->callback;
spriteB->subpriority = subpriority;
spriteB->oam = *template->oam;
spriteB->anims = template->anims;
spriteB->affineAnims = template->affineAnims;
spriteB->template = template;
spriteB->callback = SpriteCallbackDummy;
spriteC->subpriority = subpriority;
spriteC->oam = *template->oam;
spriteC->anims = template->anims;
spriteC->affineAnims = template->affineAnims;
spriteC->template = template;
spriteC->callback = SpriteCallbackDummy;
spriteD->subpriority = subpriority;
spriteD->oam = *template->oam;
spriteD->anims = template->anims;
spriteD->affineAnims = template->affineAnims;
spriteD->template = template;
spriteD->callback = SpriteCallbackDummy;
spriteE->subpriority = subpriority;
spriteE->oam = *template->oam;
spriteE->anims = template->anims;
spriteE->affineAnims = template->affineAnims;
spriteE->template = template;
spriteE->callback = SpriteCallbackDummy;
spriteF->subpriority = subpriority;
spriteF->oam = *template->oam;
spriteF->anims = template->anims;
spriteF->affineAnims = template->affineAnims;
spriteF->template = template;
spriteF->callback = SpriteCallbackDummy;
spriteA->x = x;
spriteA->y = y;
gSprites[0].x = x;
gSprites[0].y = y;
gSprites[1].x = x;
gSprites[1].y = y + 64;
gSprites[2].x = x + 32;
gSprites[2].y = y + 64;
gSprites[3].x = x + 64;
gSprites[3].y = y;
gSprites[4].x = x + 64;
gSprites[4].y = y + 32;
gSprites[5].x = x + 64;
gSprites[5].y = y + 64;
spriteA->oam.shape = 0; //square
spriteA->oam.size = SPRITE_SIZE(64x64); //64x64
spriteB->oam.shape = 1; //horizontal
spriteB->oam.size = SPRITE_SIZE(32x16); //32x16
spriteC->oam.shape = 1; //horizontal
spriteC->oam.size = SPRITE_SIZE(32x16); //32x16
spriteD->oam.shape = 2; //vertical
spriteD->oam.size = SPRITE_SIZE(16x32); //16x32
spriteE->oam.shape = 2; //vertical
spriteE->oam.size = SPRITE_SIZE(16x32); //16x32
spriteF->oam.shape = 0; //square
spriteF->oam.size = SPRITE_SIZE(16x16); //16x16
if (template->tileTag == 0xFFFF)
{
s16 tileNum;
spriteA->images = template->images;
tileNum = AllocSpriteTiles((u8)(CARD_PIC_SIZE / TILE_SIZE_8BPP)); //allocate for a 80x80 sprite
if (tileNum == -1)
{
ResetSprite(spriteA);
ResetSprite(spriteB);
ResetSprite(spriteC);
ResetSprite(spriteD);
ResetSprite(spriteE);
ResetSprite(spriteF);
return MAX_SPRITES;
}
spriteA->oam.tileNum = tileNum;
spriteA->usingSheet = FALSE;
spriteA->sheetTileStart = 0;
}
else
{
spriteA->sheetTileStart = GetSpriteTileStartByTag(template->tileTag);
SetSpriteSheetFrameTileNum(spriteA);
}
spriteB->usingSheet = spriteA->usingSheet;
spriteB->sheetTileStart = spriteA->sheetTileStart + 0x80;
spriteB->oam.tileNum = spriteA->oam.tileNum + 0x80;
spriteB->images = spriteA->images + 0x600;
spriteC->usingSheet = spriteA->usingSheet;
spriteC->sheetTileStart = spriteA->sheetTileStart + 0x90;
spriteC->oam.tileNum = spriteA->oam.tileNum + 0x90;
spriteC->images = spriteA->images + 0xA00;
spriteD->usingSheet = spriteA->usingSheet;
spriteD->sheetTileStart = spriteA->sheetTileStart + 0xA0;
spriteD->oam.tileNum = spriteA->oam.tileNum + 0xA0;
spriteD->images = spriteA->images + 0xA80;
spriteE->usingSheet = spriteA->usingSheet;
spriteE->sheetTileStart = spriteA->sheetTileStart + 0xB0;
spriteE->oam.tileNum = spriteA->oam.tileNum + 0xB0;
spriteE->images = spriteA->images + 0xB00;
spriteF->usingSheet = spriteA->usingSheet;
spriteF->sheetTileStart = spriteA->sheetTileStart + 0xC0;
spriteF->oam.tileNum = spriteA->oam.tileNum + 0xC0;
spriteF->images = spriteA->images + 0xB80;
if (spriteA->oam.affineMode & ST_OAM_AFFINE_ON_MASK)
InitSpriteAffineAnim(spriteA);
if (template->paletteTag != 0xFFFF){
spriteA->oam.paletteNum = IndexOfSpritePaletteTag(template->paletteTag);
}
spriteB->inUse = TRUE;
spriteC->inUse = TRUE;
spriteD->inUse = TRUE;
spriteE->inUse = TRUE;
spriteF->inUse = TRUE;
return index;
}
u8 CreateBigSprite(const struct SpriteTemplate *template, s16 x, s16 y, u8 subpriority)
{
u8 i;
u8 ret = 0;
for (i = 0; i < MAX_SPRITES; i++){
if (!gSprites[i].inUse){
if(++ret == 5){
return CreateBigSpriteAt(i - 4, template, x, y, subpriority);
}
}else{
ret = 0;
}
}
return MAX_SPRITES;
}
tools/gbagfx/gfx.c
static void ConvertToTiles4BppBig(unsigned char *src, unsigned char *dest, int images)
{
int subTileX = 0;
int subTileY = 0;
int metatileX = 0;
int metatileY = 0;
int pitch = 10 * 4;
for (int im = 0; im < images; im++) {
metatileX = 0;
metatileY = im * 10;
for (int i = 0; i < 80; i++) { //64x80 part
for (int j = 0; j < 8; j++) {
int srcY = metatileY * 8 + j;
for (int k = 0; k < 4; k++) {
int srcX = metatileX * 4 + k;
unsigned char srcPixelPair = src[srcY * pitch + srcX];
unsigned char leftPixel = srcPixelPair >> 4;
unsigned char rightPixel = srcPixelPair & 0xF;
*dest++ = (rightPixel << 4) | leftPixel;
}
}
AdvanceMetatilePosition(&subTileX, &subTileY, &metatileX, &metatileY, 8, 1, 1);
}
metatileX = 0;
metatileY = im * 10;
for (int i = 0; i < 20; i++) { //16x80 part
for (int j = 0; j < 8; j++) {
int srcY = metatileY * 8 + j;
for (int k = 0; k < 4; k++) {
int srcX = (metatileX + 8) * 4 + k;
unsigned char srcPixelPair = src[srcY * pitch + srcX];
if(metatileX >= 2) srcPixelPair = 0;
unsigned char leftPixel = srcPixelPair >> 4;
unsigned char rightPixel = srcPixelPair & 0xF;
*dest++ = (rightPixel << 4) | leftPixel;
}
}
AdvanceMetatilePosition(&subTileX, &subTileY, &metatileX, &metatileY, 2, 1, 1);
}
}
}
tools/gbagfx/main.c
#define TILE_SIZE 64
#define TILES_PER_ROW 10
int tile_index(int x, int y) {
return y * TILES_PER_ROW + x;
}
void copy_tile(unsigned char *dst, unsigned char *src, int index) {
memcpy(dst, src + index * TILE_SIZE, TILE_SIZE);
}
void HandleBigSpriteCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
{
int size;
unsigned char *fileContents = ReadWholeFile(inputPath, &size);
unsigned char *outputContents = malloc(80 * 80); // 6400 bytes
int index = 0;
// 64x64 sprite (tiles 0-7 in rows 0-7)
for (int y = 0; y < 8; y++) {
for (int x = 0; x < 8; x++) {
copy_tile(outputContents + index, fileContents, tile_index(x, y));
index += TILE_SIZE;
}
}
// 32x16 A (row 8, columns 0–3)
for (int y = 8; y < 10; y++) {
for (int x = 0; x < 4; x++) {
copy_tile(outputContents + index, fileContents, tile_index(x, y));
index += TILE_SIZE;
}
}
// 32x16 B (row 8, columns 4–7)
for (int y = 8; y < 10; y++) {
for (int x = 4; x < 8; x++) {
copy_tile(outputContents + index, fileContents, tile_index(x, y));
index += TILE_SIZE;
}
}
// 16x32 A (tile column 8, rows 0–3)
for (int y = 0; y < 4; y++) {
for (int x = 8; x < 10; x++) {
copy_tile(outputContents + index, fileContents, tile_index(x, y));
index += TILE_SIZE;
}
}
// 16x32 B (tile column 8, rows 4–7)
for (int y = 4; y < 8; y++) {
for (int x = 8; x < 10; x++) {
copy_tile(outputContents + index, fileContents, tile_index(x, y));
index += TILE_SIZE;
}
}
// 16x16 (bottom-right)
for (int y = 8; y < 10; y++) {
for (int x = 8; x < 10; x++) {
copy_tile(outputContents + index, fileContents, tile_index(x, y));
index += TILE_SIZE;
}
}
WriteWholeFile(outputPath, outputContents, size);
free(fileContents);
free(outputContents);
}
How to Use:
In struct CommandHandler handlers[]
in int main
in tools/gbagfx/main.c, add { "8bpp", "8bpp", HandleBigSpriteCommand },
so that you can do gbagfx pic.8bpp pic.8bpp
in command line to convert from a normal pic to the format used for a sprite stitch in the code.
In WriteTileImage
in tools/gbagfx/gfx.c, replace case 4 with:
case 4:
if (image->width == 80 && ((image->height % 80) == 0))
{
ConvertToTiles4BppBig(image->pixels, buffer, image->height / 80);
}
else
ConvertToTiles4Bpp(image->pixels, buffer, maxNumTiles, metatilesWide, metatileWidth, metatileHeight, invertColors);
break;
This will allow you to perform gbagfx pic.png pic.4bpp
(the 4bpp sprite reformatting function).
Now, all you have to do is use it like CreateSprite on a SpriteTemplate (SPRITE_SHAPE(64x64)
and SPRITE_SIZE(64x64)
, still) and, if you've performed the gbagfx conversion, it should turn out properly.