Files
pokeruby/src/sprite.c
T
2016-09-25 07:03:00 -07:00

1809 lines
48 KiB
C

#include "global.h"
#include "main.h"
#include "sprite.h"
#define MAX_SPRITE_COPY_REQUESTS 64
#define OAM_MATRIX_COUNT 32
#define SET_SPRITE_TILE_RANGE(index, start, count) \
{ \
u16 *rangeStarts; \
u16 *rangeCounts; \
rangeStarts = sSpriteTileRanges; \
rangeStarts[index * 2] = start; \
rangeCounts = sSpriteTileRanges + 1; \
rangeCounts[index * 2] = count; \
}
#define ALLOC_SPRITE_TILE(n) \
{ \
gSpriteTileAllocBitmap[(n) / 8] |= (1 << ((n) % 8)); \
}
#define FREE_SPRITE_TILE(n) \
{ \
gSpriteTileAllocBitmap[(n) / 8] &= ~(1 << ((n) % 8)); \
}
#define SPRITE_TILE_IS_ALLOCATED(n) ((gSpriteTileAllocBitmap[(n) / 8] >> ((n) % 8)) & 1)
struct OamMatrix
{
s16 a;
s16 b;
s16 c;
s16 d;
};
struct SpriteCopyRequest
{
u8 *src;
u8 *dest;
u16 size;
};
struct OamDimensions
{
s8 width;
s8 height;
};
void LoadPalette(u16 *, u16, u32);
void sub_814A590(void);
static void UpdateOamCoords(void);
static void BuildSpritePriorities(void);
static void SortSprites(void);
static void CopyMatricesToOamBuffer(void);
static void AddSpritesToOamBuffer(void);
static u8 CreateSpriteAt(u8 index, struct SpriteTemplate *template, s16 x, s16 y, u8 subpriority);
static void ClearSpriteCopyRequests(void);
static void ResetOamMatrices(void);
static void ResetSprite(struct Sprite *sprite);
static s16 AllocSpriteTiles(u16 tileCount);
static u8 SpriteTileAllocBitmapOp(u16 bit, u8 op);
static void RequestSpriteFrameImageCopy(u16 index, u16 tileNum, struct SpriteFrameImage *images);
static void CopyFromSprites(u8 *dest);
static void CopyToSprites(u8 *src);
static void ResetAllSprites(void);
static void BeginAnim(struct Sprite *sprite);
static void ContinueAnim(struct Sprite *sprite);
static void AnimCmd_frame(struct Sprite *sprite);
static void AnimCmd_end(struct Sprite *sprite);
static void AnimCmd_jump(struct Sprite *sprite);
static void AnimCmd_loop(struct Sprite *sprite);
static void BeginAnimLoop(struct Sprite *sprite);
static void ContinueAnimLoop(struct Sprite *sprite);
static void JumpToTopOfAnimLoop(struct Sprite *sprite);
static void BeginAffineAnim(struct Sprite *sprite);
static void ContinueAffineAnim(struct Sprite *sprite);
static void AffineAnimDelay(u8 matrixNum, struct Sprite *sprite);
static void AffineAnimCmd_loop(u8 matrixNum, struct Sprite *sprite);
static void BeginAffineAnimLoop(u8 matrixNum, struct Sprite *sprite);
static void ContinueAffineAnimLoop(u8 matrixNum, struct Sprite *sprite);
static void JumpToTopOfAffineAnimLoop(u8 matrixNum, struct Sprite *sprite);
static void AffineAnimCmd_jump(u8 matrixNum, struct Sprite *sprite);
static void AffineAnimCmd_end(u8 matrixNum, struct Sprite *sprite);
static void AffineAnimCmd_frame(u8 matrixNum, struct Sprite *sprite);
static void CopyOamMatrix(u8 destMatrixIndex, struct OamMatrix *srcMatrix);
static u8 GetSpriteMatrixNum(struct Sprite *sprite);
static void SetSpriteOamFlipBits(struct Sprite *sprite, u8 hFlip, u8 vFlip);
static void AffineAnimStateRestartAnim(u8 matrixNum);
static void AffineAnimStateStartAnim(u8 matrixNum, u8 animNum);
static void AffineAnimStateReset(u8 matrixNum);
static void ApplyAffineAnimFrameAbsolute(u8 matrixNum, struct AffineAnimFrameCmd *frameCmd);
static void DecrementAnimDelayCounter(struct Sprite *sprite);
static bool8 DecrementAffineAnimDelayCounter(struct Sprite *sprite, u8 matrixNum);
static void ApplyAffineAnimFrameRelativeAndUpdateMatrix(u8 matrixNum, struct AffineAnimFrameCmd *frameCmd);
static s16 ConvertScaleParam(s16 scale);
static void GetAffineAnimFrame(u8 matrixNum, struct Sprite *sprite, struct AffineAnimFrameCmd *frameCmd);
static void ApplyAffineAnimFrame(u8 matrixNum, struct AffineAnimFrameCmd *frameCmd);
static void ResetAffineAnimData(void);
static u8 IndexOfSpriteTileTag(u16 tag);
static void AllocSpriteTileRange(u16 tag, u16 start, u16 count);
static void DoLoadSpritePalette(u16 *src, u16 paletteOffset);
typedef void (*AnimFunc)(struct Sprite *);
typedef void (*AnimCmdFunc)(struct Sprite *);
typedef void (*AffineAnimCmdFunc)(u8 matrixNum, struct Sprite *);
#define DUMMY_OAM_DATA \
{ \
160, /* Y (off-screen) */ \
0, \
0, \
0, \
0, \
0, \
304, /* X */ \
0, \
0, \
0, \
3, /* lowest priority */ \
0, \
0 \
}
#define ANIM_END 0xFFFF
#define AFFINE_ANIM_END 0x7FFF
// forward declarations
const union AnimCmd * const gDummySpriteAnimTable[];
const union AffineAnimCmd * const gDummySpriteAffineAnimTable[];
const struct SpriteTemplate gDummySpriteTemplate;
// Unreferenced error message.
// It means "The DMA transfer request table has exceeded its limit."
static const u8 sDmaOverErrorMsg[] =
_"DMA OVER\n"
"DMAてんそう\n"
"リクエストテーブルが\n"
"オーバーしました";
// Unreferenced data.
static const u8 sUnknownData[24] =
{
0x01, 0x04, 0x10, 0x40,
0x02, 0x04, 0x08, 0x20,
0x02, 0x04, 0x08, 0x20,
0x01, 0x04, 0x10, 0x40,
0x02, 0x04, 0x08, 0x20,
0x02, 0x04, 0x08, 0x20,
};
static const u8 sCenterToCornerVecTable[3][4][2] =
{
{ // square
{ -4, -4 },
{ -8, -8 },
{ -16, -16 },
{ -32, -32 },
},
{ // horizontal rectangle
{ -8, -4 },
{ -16, -4 },
{ -16, -8 },
{ -32, -16 },
},
{ // vertical rectangle
{ -4, -8 },
{ -4, -16 },
{ -8, -16 },
{ -16, -32 },
},
};
static const struct Sprite sDummySprite =
{
DUMMY_OAM_DATA,
(union AnimCmd **)gDummySpriteAnimTable,
NULL,
(union AffineAnimCmd **)gDummySpriteAffineAnimTable,
(struct SpriteTemplate *)&gDummySpriteTemplate,
NULL,
SpriteCallbackDummy,
{ 304, 160 },
{ 0, 0 },
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0xFF
};
const struct OamData gDummyOamData = DUMMY_OAM_DATA;
static const union AnimCmd sDummyAnim = { ANIM_END };
const union AnimCmd * const gDummySpriteAnimTable[] = { &sDummyAnim };
static const union AffineAnimCmd sDummyAffineAnim = { AFFINE_ANIM_END };
const union AffineAnimCmd * const gDummySpriteAffineAnimTable[] = { &sDummyAffineAnim };
const struct SpriteTemplate gDummySpriteTemplate =
{
0,
0xFFFF,
(struct OamData *)&gDummyOamData,
(union AnimCmd **)gDummySpriteAnimTable,
NULL,
(union AffineAnimCmd **)gDummySpriteAffineAnimTable,
SpriteCallbackDummy
};
// TODO: Find out what these are used for.
static const u16 sOamBitmasks[9] =
{
0xFF00, 0x00FF, 0x001F,
0xFE00, 0x01FF, 0x03E0,
0xFC00, 0x03FF, 0xFC00,
};
static const AnimFunc sAnimFuncs[] =
{
ContinueAnim,
BeginAnim,
};
static const AnimFunc sAffineAnimFuncs[] =
{
ContinueAffineAnim,
BeginAffineAnim,
};
static const AnimCmdFunc sAnimCmdFuncs[] =
{
AnimCmd_loop,
AnimCmd_jump,
AnimCmd_end,
AnimCmd_frame,
};
static const AffineAnimCmdFunc sAffineAnimCmdFuncs[] =
{
AffineAnimCmd_loop,
AffineAnimCmd_jump,
AffineAnimCmd_end,
AffineAnimCmd_frame,
};
static const struct OamDimensions sOamDimensions[3][4] =
{
{ // square
{ 8, 8 },
{ 16, 16 },
{ 32, 32 },
{ 64, 64 },
},
{ // horizontal rectangle
{ 16, 8 },
{ 32, 8 },
{ 32, 16 },
{ 64, 32 },
},
{ // vertical rectangle
{ 8, 16 },
{ 8, 32 },
{ 16, 32 },
{ 32, 64 },
},
};
static u16 sSpriteTileRangeTags[MAX_SPRITES];
static u16 sSpriteTileRanges[MAX_SPRITES * 2];
static struct AffineAnimState sAffineAnimStates[OAM_MATRIX_COUNT];
static u16 sSpritePaletteTags[16];
u8 gSpriteOrder[MAX_SPRITES];
bool8 gShouldProcessSpriteCopyRequests;
u8 gSpriteCopyRequestCount;
struct SpriteCopyRequest gSpriteCopyRequests[MAX_SPRITE_COPY_REQUESTS];
u8 gOamLimit;
u16 gReservedSpriteTileCount;
u8 gSpriteTileAllocBitmap[128];
s16 gSpriteCoordOffsetX;
s16 gSpriteCoordOffsetY;
u32 gOamMatrixAllocBitmap;
struct OamMatrix gOamMatrices[OAM_MATRIX_COUNT];
u8 gReservedSpritePaletteCount;
EWRAM_DATA struct Sprite gSprites[MAX_SPRITES + 1] = {0};
EWRAM_DATA u16 gSpritePriorities[MAX_SPRITES] = {0};
EWRAM_DATA u8 gAffineAnimsDisabled = {0};
void ResetSpriteData(void)
{
ResetOamRange(0, 128);
ResetAllSprites();
ClearSpriteCopyRequests();
ResetAffineAnimData();
FreeSpriteTileRanges();
gOamLimit = 64;
gReservedSpriteTileCount = 0;
AllocSpriteTiles(0);
gSpriteCoordOffsetX = 0;
gSpriteCoordOffsetY = 0;
}
void AnimateSprites(void)
{
u8 i;
for (i = 0; i < MAX_SPRITES; i++)
{
struct Sprite *sprite = &gSprites[i];
if (sprite->inUse)
{
sprite->callback(sprite);
if (sprite->inUse)
AnimateSprite(sprite);
}
}
}
void BuildOamBuffer(void)
{
u8 temp;
UpdateOamCoords();
BuildSpritePriorities();
SortSprites();
temp = gMain.oamLoadDisabled;
gMain.oamLoadDisabled = TRUE;
AddSpritesToOamBuffer();
CopyMatricesToOamBuffer();
gMain.oamLoadDisabled = temp;
gShouldProcessSpriteCopyRequests = TRUE;
}
static void UpdateOamCoords(void)
{
u8 i;
for (i = 0; i < MAX_SPRITES; i++)
{
struct Sprite *sprite = &gSprites[i];
if (sprite->inUse && !sprite->invisible)
{
if (sprite->coordOffsetEnabled)
{
sprite->oam.x = sprite->pos1.x + sprite->pos2.x + sprite->centerToCornerVecX + gSpriteCoordOffsetX;
sprite->oam.y = sprite->pos1.y + sprite->pos2.y + sprite->centerToCornerVecY + gSpriteCoordOffsetY;
}
else
{
sprite->oam.x = sprite->pos1.x + sprite->pos2.x + sprite->centerToCornerVecX;
sprite->oam.y = sprite->pos1.y + sprite->pos2.y + sprite->centerToCornerVecY;
}
}
}
}
static void BuildSpritePriorities(void)
{
u16 i;
for (i = 0; i < MAX_SPRITES; i++)
{
struct Sprite *sprite = &gSprites[i];
u16 priority = sprite->subpriority | (sprite->oam.priority << 8);
gSpritePriorities[i] = priority;
}
}
static void SortSprites(void)
{
u8 i;
for (i = 1; i < MAX_SPRITES; i++)
{
u8 j = i;
struct Sprite *sprite1 = &gSprites[gSpriteOrder[i - 1]];
struct Sprite *sprite2 = &gSprites[gSpriteOrder[i]];
u16 sprite1Priority = gSpritePriorities[gSpriteOrder[i - 1]];
u16 sprite2Priority = gSpritePriorities[gSpriteOrder[i]];
s16 sprite1Y = sprite1->oam.y;
s16 sprite2Y = sprite2->oam.y;
if (sprite1Y >= DISPLAY_HEIGHT)
sprite1Y = sprite1Y - 256;
if (sprite2Y >= DISPLAY_HEIGHT)
sprite2Y = sprite2Y - 256;
if (sprite1->oam.affineMode == ST_OAM_AFFINE_DOUBLE
&& sprite1->oam.size == 3)
{
u32 shape = sprite1->oam.shape;
if (shape == ST_OAM_SQUARE || shape == 2)
{
if (sprite1Y > 128)
sprite1Y = sprite1Y - 256;
}
}
if (sprite2->oam.affineMode == ST_OAM_AFFINE_DOUBLE
&& sprite2->oam.size == 3)
{
u32 shape = sprite2->oam.shape;
if (shape == ST_OAM_SQUARE || shape == ST_OAM_V_RECTANGLE)
{
if (sprite2Y > 128)
sprite2Y = sprite2Y - 256;
}
}
while (j > 0
&& ((sprite1Priority > sprite2Priority)
|| (sprite1Priority == sprite2Priority && sprite1Y < sprite2Y)))
{
u8 temp = gSpriteOrder[j];
gSpriteOrder[j] = gSpriteOrder[j - 1];
gSpriteOrder[j - 1] = temp;
// UB: If j equals 1, then j-- makes j equal 0.
// Then, gSpriteOrder[-1] gets accessed below.
// Although this doesn't result in a bug in the ROM,
// the behavior is undefined.
j--;
sprite1 = &gSprites[gSpriteOrder[j - 1]];
sprite2 = &gSprites[gSpriteOrder[j]];
sprite1Priority = gSpritePriorities[gSpriteOrder[j - 1]];
sprite2Priority = gSpritePriorities[gSpriteOrder[j]];
sprite1Y = sprite1->oam.y;
sprite2Y = sprite2->oam.y;
if (sprite1Y >= DISPLAY_HEIGHT)
sprite1Y = sprite1Y - 256;
if (sprite2Y >= DISPLAY_HEIGHT)
sprite2Y = sprite2Y - 256;
if (sprite1->oam.affineMode == ST_OAM_AFFINE_DOUBLE
&& sprite1->oam.size == 3)
{
u32 shape = sprite1->oam.shape;
if (shape == ST_OAM_SQUARE || shape == ST_OAM_V_RECTANGLE)
{
if (sprite1Y > 128)
sprite1Y = sprite1Y - 256;
}
}
if (sprite2->oam.affineMode == ST_OAM_AFFINE_DOUBLE
&& sprite2->oam.size == 3)
{
u32 shape = sprite2->oam.shape;
if (shape == ST_OAM_SQUARE || shape == ST_OAM_V_RECTANGLE)
{
if (sprite2Y > 128)
sprite2Y = sprite2Y - 256;
}
}
}
}
}
static void CopyMatricesToOamBuffer(void)
{
u8 i;
for (i = 0; i < OAM_MATRIX_COUNT; i++)
{
u32 base = 4 * i;
gMain.oamBuffer[base + 0].affineParam = gOamMatrices[i].a;
gMain.oamBuffer[base + 1].affineParam = gOamMatrices[i].b;
gMain.oamBuffer[base + 2].affineParam = gOamMatrices[i].c;
gMain.oamBuffer[base + 3].affineParam = gOamMatrices[i].d;
}
}
static void AddSpritesToOamBuffer(void)
{
u8 i = 0;
u8 oamIndex = 0;
while (i < MAX_SPRITES)
{
struct Sprite *sprite = &gSprites[gSpriteOrder[i]];
if (sprite->inUse && !sprite->invisible && AddSpriteToOamBuffer(sprite, &oamIndex))
break;
i++;
}
gMain.objCount = oamIndex;
while (oamIndex < gOamLimit)
{
gMain.oamBuffer[oamIndex] = gDummyOamData;
oamIndex++;
}
}
u8 CreateSprite(struct SpriteTemplate *template, u16 x, u16 y, u8 subpriority)
{
u8 i;
for (i = 0; i < MAX_SPRITES; i++)
if (!gSprites[i].inUse)
return CreateSpriteAt(i, template, x, y, subpriority);
return MAX_SPRITES;
}
u8 CreateSpriteAtEnd(struct SpriteTemplate *template, u16 x, u16 y, u8 subpriority)
{
s16 i;
for (i = MAX_SPRITES - 1; i > -1; i--)
if (!gSprites[i].inUse)
return CreateSpriteAt(i, template, x, y, subpriority);
return MAX_SPRITES;
}
u8 CreateInvisibleSprite(void (*callback)(struct Sprite *))
{
u8 index = CreateSprite((struct SpriteTemplate *)&gDummySpriteTemplate, 0, 0, 31);
if (index == MAX_SPRITES)
{
return MAX_SPRITES;
}
else
{
gSprites[index].invisible = TRUE;
gSprites[index].callback = callback;
return index;
}
}
static u8 CreateSpriteAt(u8 index, struct SpriteTemplate *template, s16 x, s16 y, u8 subpriority)
{
struct Sprite *sprite = &gSprites[index];
ResetSprite(sprite);
sprite->inUse = TRUE;
sprite->animBeginning = TRUE;
sprite->affineAnimBeginning = TRUE;
sprite->usingSheet = TRUE;
sprite->subpriority = subpriority;
sprite->oam = *template->oam;
sprite->anims = template->anims;
sprite->affineAnims = template->affineAnims;
sprite->template = template;
sprite->callback = template->callback;
sprite->pos1.x = x;
sprite->pos1.y = y;
CalcCenterToCornerVec(sprite, sprite->oam.shape, sprite->oam.size, sprite->oam.affineMode);
if (template->tileTag == 0xFFFF)
{
s16 tileNum;
sprite->images = template->images;
tileNum = AllocSpriteTiles((u8)(sprite->images->size / TILE_SIZE_4BPP));
if (tileNum == -1)
{
ResetSprite(sprite);
return MAX_SPRITES;
}
sprite->oam.tileNum = tileNum;
sprite->usingSheet = FALSE;
sprite->sheetTileStart = 0;
}
else
{
sprite->sheetTileStart = GetSpriteTileStartByTag(template->tileTag);
SetSpriteSheetFrameTileNum(sprite);
}
if (sprite->oam.affineMode & ST_OAM_AFFINE_ON_MASK)
InitSpriteAffineAnim(sprite);
if (template->paletteTag != 0xFFFF)
sprite->oam.paletteNum = IndexOfSpritePaletteTag(template->paletteTag);
return index;
}
u8 CreateSpriteAndAnimate(struct SpriteTemplate *template, s16 x, s16 y, u8 subpriority)
{
u8 i;
for (i = 0; i < MAX_SPRITES; i++)
{
struct Sprite *sprite = &gSprites[i];
if (!gSprites[i].inUse)
{
u8 index = CreateSpriteAt(i, template, x, y, subpriority);
if (index == MAX_SPRITES)
return MAX_SPRITES;
gSprites[i].callback(sprite);
if (gSprites[i].inUse)
AnimateSprite(sprite);
return index;
}
}
return MAX_SPRITES;
}
void DestroySprite(struct Sprite *sprite)
{
if (sprite->inUse)
{
if (!sprite->usingSheet)
{
u16 i;
u16 tileEnd = (sprite->images->size / TILE_SIZE_4BPP) + sprite->oam.tileNum;
for (i = sprite->oam.tileNum; i < tileEnd; i++)
FREE_SPRITE_TILE(i);
}
ResetSprite(sprite);
}
}
void ResetOamRange(u8 a, u8 b)
{
u8 i;
for (i = a; i < b; i++)
{
struct OamData *oamBuffer = gMain.oamBuffer;
oamBuffer[i] = *(struct OamData *)&gDummyOamData;
}
}
void LoadOam(void)
{
if (!gMain.oamLoadDisabled)
CpuCopy32(gMain.oamBuffer, (void *)OAM, sizeof(gMain.oamBuffer));
}
static void ClearSpriteCopyRequests(void)
{
u8 i;
gShouldProcessSpriteCopyRequests = FALSE;
gSpriteCopyRequestCount = 0;
for (i = 0; i < MAX_SPRITE_COPY_REQUESTS; i++)
{
gSpriteCopyRequests[i].src = 0;
gSpriteCopyRequests[i].dest = 0;
gSpriteCopyRequests[i].size = 0;
}
}
static void ResetOamMatrices(void)
{
u8 i;
for (i = 0; i < OAM_MATRIX_COUNT; i++)
{
// set to identity matrix
gOamMatrices[i].a = 0x0100;
gOamMatrices[i].b = 0x0000;
gOamMatrices[i].c = 0x0000;
gOamMatrices[i].d = 0x0100;
}
}
void SetOamMatrix(u8 matrixNum, u16 a, u16 b, u16 c, u16 d)
{
gOamMatrices[matrixNum].a = a;
gOamMatrices[matrixNum].b = b;
gOamMatrices[matrixNum].c = c;
gOamMatrices[matrixNum].d = d;
}
static void ResetSprite(struct Sprite *sprite)
{
*sprite = sDummySprite;
}
void CalcCenterToCornerVec(struct Sprite *sprite, u8 shape, u8 size, u8 affineMode)
{
u8 x = sCenterToCornerVecTable[shape][size][0];
u8 y = sCenterToCornerVecTable[shape][size][1];
if (affineMode & ST_OAM_AFFINE_DOUBLE_MASK)
{
x *= 2;
y *= 2;
}
sprite->centerToCornerVecX = x;
sprite->centerToCornerVecY = y;
}
static s16 AllocSpriteTiles(u16 tileCount)
{
u16 i;
s16 start;
u16 numTilesFound;
if (tileCount == 0)
{
// Free all unreserved tiles if the tile count is 0.
for (i = gReservedSpriteTileCount; i < TOTAL_OBJ_TILE_COUNT; i++)
FREE_SPRITE_TILE(i);
return 0;
}
i = gReservedSpriteTileCount;
for (;;)
{
while (SPRITE_TILE_IS_ALLOCATED(i))
{
i++;
if (i == TOTAL_OBJ_TILE_COUNT)
return -1;
}
start = i;
numTilesFound = 1;
while (numTilesFound != tileCount)
{
i++;
if (i == TOTAL_OBJ_TILE_COUNT)
return -1;
if (!SPRITE_TILE_IS_ALLOCATED(i))
numTilesFound++;
else
break;
}
if (numTilesFound == tileCount)
break;
}
for (i = start; i < tileCount + start; i++)
ALLOC_SPRITE_TILE(i);
return start;
}
static u8 SpriteTileAllocBitmapOp(u16 bit, u8 op)
{
u8 index = bit / 8;
u8 shift = bit % 8;
u8 val = bit % 8;
u8 retVal = 0;
if (op == 0)
{
val = ~(1 << val);
gSpriteTileAllocBitmap[index] &= val;
}
else if (op == 1)
{
val = (1 << val);
gSpriteTileAllocBitmap[index] |= val;
}
else
{
retVal = 1 << shift;
retVal &= gSpriteTileAllocBitmap[index];
}
return retVal;
}
void SpriteCallbackDummy(struct Sprite *sprite)
{
}
void ProcessSpriteCopyRequests(void)
{
if (gShouldProcessSpriteCopyRequests)
{
u8 i = 0;
while (gSpriteCopyRequestCount > 0)
{
CpuCopy16(gSpriteCopyRequests[i].src, gSpriteCopyRequests[i].dest, gSpriteCopyRequests[i].size);
gSpriteCopyRequestCount--;
i++;
}
gShouldProcessSpriteCopyRequests = FALSE;
}
}
static void RequestSpriteFrameImageCopy(u16 index, u16 tileNum, struct SpriteFrameImage *images)
{
if (gSpriteCopyRequestCount < MAX_SPRITE_COPY_REQUESTS)
{
gSpriteCopyRequests[gSpriteCopyRequestCount].src = images[index].data;
gSpriteCopyRequests[gSpriteCopyRequestCount].dest = (u8 *)OBJ_VRAM0 + TILE_SIZE_4BPP * tileNum;
gSpriteCopyRequests[gSpriteCopyRequestCount].size = images[index].size;
gSpriteCopyRequestCount++;
}
}
void RequestSpriteCopy(u8 *src, u8 *dest, u16 size)
{
if (gSpriteCopyRequestCount < MAX_SPRITE_COPY_REQUESTS)
{
gSpriteCopyRequests[gSpriteCopyRequestCount].src = src;
gSpriteCopyRequests[gSpriteCopyRequestCount].dest = dest;
gSpriteCopyRequests[gSpriteCopyRequestCount].size = size;
gSpriteCopyRequestCount++;
}
}
static void CopyFromSprites(u8 *dest)
{
u32 i;
u8 *src = (u8 *)gSprites;
for (i = 0; i < sizeof(struct Sprite) * MAX_SPRITES; i++)
{
*dest = *src;
dest++;
src++;
}
}
static void CopyToSprites(u8 *src)
{
u32 i;
u8 *dest = (u8 *)gSprites;
for (i = 0; i < sizeof(struct Sprite) * MAX_SPRITES; i++)
{
*dest = *src;
src++;
dest++;
}
}
static void ResetAllSprites(void)
{
u8 i;
for (i = 0; i < MAX_SPRITES; i++)
{
ResetSprite(&gSprites[i]);
gSpriteOrder[i] = i;
}
ResetSprite(&gSprites[i]);
sub_814A590();
}
void FreeSpriteTiles(struct Sprite *sprite)
{
if (sprite->template->tileTag != 0xFFFF)
FreeSpriteTilesByTag(sprite->template->tileTag);
}
void FreeSpritePalette(struct Sprite *sprite)
{
FreeSpritePaletteByTag(sprite->template->paletteTag);
}
void FreeSpriteOamMatrix(struct Sprite *sprite)
{
if (sprite->oam.affineMode & ST_OAM_AFFINE_ON_MASK)
{
FreeOamMatrix(sprite->oam.matrixNum);
sprite->oam.affineMode = ST_OAM_AFFINE_OFF;
}
}
void DestroySpriteAndFreeResources(struct Sprite *sprite)
{
FreeSpriteTiles(sprite);
FreeSpritePalette(sprite);
FreeSpriteOamMatrix(sprite);
DestroySprite(sprite);
}
void sub_800142C(u32 a1, u32 a2, u16 *a3, u16 a4, u32 a5)
{
u16 *d = a3;
struct OamData *oam = &gMain.oamBuffer[gMain.objCount];
while (!(gMain.objCount & 0x80) && (s16)(d[0] + 1) != 0)
{
u16 *x = (u16 *)oam;
x[0] = (d[0] & sOamBitmasks[0]) | ((d[0] + a2) & sOamBitmasks[1]) | ((a4 & sOamBitmasks[2]) << 8);
x[1] = (d[1] & sOamBitmasks[3]) | ((d[1] + a1) & sOamBitmasks[4]) | ((a4 & sOamBitmasks[5]) << 4);
x[2] = (d[2] & sOamBitmasks[6]) | ((d[2] + a5) & sOamBitmasks[7]) | (a4 & sOamBitmasks[8]);
oam++;
gMain.objCount++;
d += 3;
}
}
void AnimateSprite(struct Sprite *sprite)
{
sAnimFuncs[sprite->animBeginning](sprite);
if (!gAffineAnimsDisabled)
sAffineAnimFuncs[sprite->affineAnimBeginning](sprite);
}
static void BeginAnim(struct Sprite *sprite)
{
s16 imageValue;
u8 duration;
u8 hFlip;
u8 vFlip;
sprite->animCmdIndex = 0;
sprite->animEnded = FALSE;
sprite->animLoopCounter = 0;
imageValue = sprite->anims[sprite->animNum][sprite->animCmdIndex].frame.imageValue;
if (imageValue != -1)
{
sprite->animBeginning = FALSE;
duration = sprite->anims[sprite->animNum][sprite->animCmdIndex].frame.duration;
hFlip = sprite->anims[sprite->animNum][sprite->animCmdIndex].frame.hFlip;
vFlip = sprite->anims[sprite->animNum][sprite->animCmdIndex].frame.vFlip;
if (duration)
duration--;
sprite->animDelayCounter = duration;
if (!(sprite->oam.affineMode & ST_OAM_AFFINE_ON_MASK))
SetSpriteOamFlipBits(sprite, hFlip, vFlip);
if (sprite->usingSheet)
sprite->oam.tileNum = sprite->sheetTileStart + imageValue;
else
RequestSpriteFrameImageCopy(imageValue, sprite->oam.tileNum, sprite->images);
}
}
static void ContinueAnim(struct Sprite *sprite)
{
if (sprite->animDelayCounter)
{
u8 hFlip;
u8 vFlip;
DecrementAnimDelayCounter(sprite);
hFlip = sprite->anims[sprite->animNum][sprite->animCmdIndex].frame.hFlip;
vFlip = sprite->anims[sprite->animNum][sprite->animCmdIndex].frame.vFlip;
if (!(sprite->oam.affineMode & ST_OAM_AFFINE_ON_MASK))
SetSpriteOamFlipBits(sprite, hFlip, vFlip);
}
else if (!sprite->animPaused)
{
s16 type;
s16 funcIndex;
sprite->animCmdIndex++;
type = sprite->anims[sprite->animNum][sprite->animCmdIndex].type;
funcIndex = 3;
if (type < 0)
funcIndex = type + 3;
sAnimCmdFuncs[funcIndex](sprite);
}
}
static void AnimCmd_frame(struct Sprite *sprite)
{
s16 imageValue;
u8 duration;
u8 hFlip;
u8 vFlip;
imageValue = sprite->anims[sprite->animNum][sprite->animCmdIndex].frame.imageValue;
duration = sprite->anims[sprite->animNum][sprite->animCmdIndex].frame.duration;
hFlip = sprite->anims[sprite->animNum][sprite->animCmdIndex].frame.hFlip;
vFlip = sprite->anims[sprite->animNum][sprite->animCmdIndex].frame.vFlip;
if (duration)
duration--;
sprite->animDelayCounter = duration;
if (!(sprite->oam.affineMode & ST_OAM_AFFINE_ON_MASK))
SetSpriteOamFlipBits(sprite, hFlip, vFlip);
if (sprite->usingSheet)
sprite->oam.tileNum = sprite->sheetTileStart + imageValue;
else
RequestSpriteFrameImageCopy(imageValue, sprite->oam.tileNum, sprite->images);
}
static void AnimCmd_end(struct Sprite *sprite)
{
sprite->animCmdIndex--;
sprite->animEnded = TRUE;
}
static void AnimCmd_jump(struct Sprite *sprite)
{
s16 imageValue;
u8 duration;
u8 hFlip;
u8 vFlip;
sprite->animCmdIndex = sprite->anims[sprite->animNum][sprite->animCmdIndex].jump.target;
imageValue = sprite->anims[sprite->animNum][sprite->animCmdIndex].frame.imageValue;
duration = sprite->anims[sprite->animNum][sprite->animCmdIndex].frame.duration;
hFlip = sprite->anims[sprite->animNum][sprite->animCmdIndex].frame.hFlip;
vFlip = sprite->anims[sprite->animNum][sprite->animCmdIndex].frame.vFlip;
if (duration)
duration--;
sprite->animDelayCounter = duration;
if (!(sprite->oam.affineMode & ST_OAM_AFFINE_ON_MASK))
SetSpriteOamFlipBits(sprite, hFlip, vFlip);
if (sprite->usingSheet)
sprite->oam.tileNum = sprite->sheetTileStart + imageValue;
else
RequestSpriteFrameImageCopy(imageValue, sprite->oam.tileNum, sprite->images);
}
static void AnimCmd_loop(struct Sprite *sprite)
{
if (sprite->animLoopCounter)
ContinueAnimLoop(sprite);
else
BeginAnimLoop(sprite);
}
static void BeginAnimLoop(struct Sprite *sprite)
{
sprite->animLoopCounter = sprite->anims[sprite->animNum][sprite->animCmdIndex].loop.count;
JumpToTopOfAnimLoop(sprite);
ContinueAnim(sprite);
}
static void ContinueAnimLoop(struct Sprite *sprite)
{
sprite->animLoopCounter--;
JumpToTopOfAnimLoop(sprite);
ContinueAnim(sprite);
}
static void JumpToTopOfAnimLoop(struct Sprite *sprite)
{
if (sprite->animLoopCounter)
{
sprite->animCmdIndex--;
while (sprite->anims[sprite->animNum][sprite->animCmdIndex - 1].type != -3)
{
if (sprite->animCmdIndex == 0)
break;
sprite->animCmdIndex--;
}
sprite->animCmdIndex--;
}
}
static void BeginAffineAnim(struct Sprite *sprite)
{
if ((sprite->oam.affineMode & ST_OAM_AFFINE_ON_MASK) && sprite->affineAnims[0][0].type != 32767)
{
struct AffineAnimFrameCmd frameCmd;
u8 matrixNum = GetSpriteMatrixNum(sprite);
AffineAnimStateRestartAnim(matrixNum);
GetAffineAnimFrame(matrixNum, sprite, &frameCmd);
sprite->affineAnimBeginning = FALSE;
sprite->affineAnimEnded = FALSE;
ApplyAffineAnimFrame(matrixNum, &frameCmd);
sAffineAnimStates[matrixNum].delayCounter = frameCmd.duration;
}
}
static void ContinueAffineAnim(struct Sprite *sprite)
{
if (sprite->oam.affineMode & ST_OAM_AFFINE_ON_MASK)
{
u8 matrixNum = GetSpriteMatrixNum(sprite);
if (sAffineAnimStates[matrixNum].delayCounter)
{
AffineAnimDelay(matrixNum, sprite);
}
else if (!sprite->affineAnimPaused)
{
s16 type;
s16 funcIndex;
sAffineAnimStates[matrixNum].animCmdIndex++;
type = sprite->affineAnims[sAffineAnimStates[matrixNum].animNum][sAffineAnimStates[matrixNum].animCmdIndex].type;
funcIndex = 3;
if (type >= 32765)
funcIndex = type - 32765;
sAffineAnimCmdFuncs[funcIndex](matrixNum, sprite);
}
}
}
static void AffineAnimDelay(u8 matrixNum, struct Sprite *sprite)
{
if (!DecrementAffineAnimDelayCounter(sprite, matrixNum))
{
struct AffineAnimFrameCmd frameCmd;
GetAffineAnimFrame(matrixNum, sprite, &frameCmd);
ApplyAffineAnimFrameRelativeAndUpdateMatrix(matrixNum, &frameCmd);
}
}
static void AffineAnimCmd_loop(u8 matrixNum, struct Sprite *sprite)
{
if (sAffineAnimStates[matrixNum].loopCounter)
ContinueAffineAnimLoop(matrixNum, sprite);
else
BeginAffineAnimLoop(matrixNum, sprite);
}
static void BeginAffineAnimLoop(u8 matrixNum, struct Sprite *sprite)
{
sAffineAnimStates[matrixNum].loopCounter = sprite->affineAnims[sAffineAnimStates[matrixNum].animNum][sAffineAnimStates[matrixNum].animCmdIndex].loop.count;
JumpToTopOfAffineAnimLoop(matrixNum, sprite);
ContinueAffineAnim(sprite);
}
static void ContinueAffineAnimLoop(u8 matrixNum, struct Sprite *sprite)
{
sAffineAnimStates[matrixNum].loopCounter--;
JumpToTopOfAffineAnimLoop(matrixNum, sprite);
ContinueAffineAnim(sprite);
}
static void JumpToTopOfAffineAnimLoop(u8 matrixNum, struct Sprite *sprite)
{
if (sAffineAnimStates[matrixNum].loopCounter)
{
sAffineAnimStates[matrixNum].animCmdIndex--;
while (sprite->affineAnims[sAffineAnimStates[matrixNum].animNum][sAffineAnimStates[matrixNum].animCmdIndex - 1].type != 32765)
{
if (sAffineAnimStates[matrixNum].animCmdIndex == 0)
break;
sAffineAnimStates[matrixNum].animCmdIndex--;
}
sAffineAnimStates[matrixNum].animCmdIndex--;
}
}
static void AffineAnimCmd_jump(u8 matrixNum, struct Sprite *sprite)
{
struct AffineAnimFrameCmd frameCmd;
sAffineAnimStates[matrixNum].animCmdIndex = sprite->affineAnims[sAffineAnimStates[matrixNum].animNum][sAffineAnimStates[matrixNum].animCmdIndex].jump.target;
GetAffineAnimFrame(matrixNum, sprite, &frameCmd);
ApplyAffineAnimFrame(matrixNum, &frameCmd);
sAffineAnimStates[matrixNum].delayCounter = frameCmd.duration;
}
static void AffineAnimCmd_end(u8 matrixNum, struct Sprite *sprite)
{
struct AffineAnimFrameCmd dummyFrameCmd = {0};
sprite->affineAnimEnded = TRUE;
sAffineAnimStates[matrixNum].animCmdIndex--;
ApplyAffineAnimFrameRelativeAndUpdateMatrix(matrixNum, &dummyFrameCmd);
}
static void AffineAnimCmd_frame(u8 matrixNum, struct Sprite *sprite)
{
struct AffineAnimFrameCmd frameCmd;
GetAffineAnimFrame(matrixNum, sprite, &frameCmd);
ApplyAffineAnimFrame(matrixNum, &frameCmd);
sAffineAnimStates[matrixNum].delayCounter = frameCmd.duration;
}
static void CopyOamMatrix(u8 destMatrixIndex, struct OamMatrix *srcMatrix)
{
gOamMatrices[destMatrixIndex].a = srcMatrix->a;
gOamMatrices[destMatrixIndex].b = srcMatrix->b;
gOamMatrices[destMatrixIndex].c = srcMatrix->c;
gOamMatrices[destMatrixIndex].d = srcMatrix->d;
}
static u8 GetSpriteMatrixNum(struct Sprite *sprite)
{
u8 matrixNum = 0;
if (sprite->oam.affineMode & ST_OAM_AFFINE_ON_MASK)
matrixNum = sprite->oam.matrixNum;
return matrixNum;
}
static void SetSpriteOamFlipBits(struct Sprite *sprite, u8 hFlip, u8 vFlip)
{
sprite->oam.matrixNum &= 0x7;
sprite->oam.matrixNum |= (((hFlip ^ sprite->hFlip) & 1) << 3);
sprite->oam.matrixNum |= (((vFlip ^ sprite->vFlip) & 1) << 4);
}
static void AffineAnimStateRestartAnim(u8 matrixNum)
{
sAffineAnimStates[matrixNum].animCmdIndex = 0;
sAffineAnimStates[matrixNum].delayCounter = 0;
sAffineAnimStates[matrixNum].loopCounter = 0;
}
static void AffineAnimStateStartAnim(u8 matrixNum, u8 animNum)
{
sAffineAnimStates[matrixNum].animNum = animNum;
sAffineAnimStates[matrixNum].animCmdIndex = 0;
sAffineAnimStates[matrixNum].delayCounter = 0;
sAffineAnimStates[matrixNum].loopCounter = 0;
sAffineAnimStates[matrixNum].xScale = 0x0100;
sAffineAnimStates[matrixNum].yScale = 0x0100;
sAffineAnimStates[matrixNum].rotation = 0;
}
static void AffineAnimStateReset(u8 matrixNum)
{
sAffineAnimStates[matrixNum].animNum = 0;
sAffineAnimStates[matrixNum].animCmdIndex = 0;
sAffineAnimStates[matrixNum].delayCounter = 0;
sAffineAnimStates[matrixNum].loopCounter = 0;
sAffineAnimStates[matrixNum].xScale = 0x0100;
sAffineAnimStates[matrixNum].yScale = 0x0100;
sAffineAnimStates[matrixNum].rotation = 0;
}
static void ApplyAffineAnimFrameAbsolute(u8 matrixNum, struct AffineAnimFrameCmd *frameCmd)
{
sAffineAnimStates[matrixNum].xScale = frameCmd->xScale;
sAffineAnimStates[matrixNum].yScale = frameCmd->yScale;
sAffineAnimStates[matrixNum].rotation = frameCmd->rotation << 8;
}
static void DecrementAnimDelayCounter(struct Sprite *sprite)
{
if (!sprite->animPaused)
sprite->animDelayCounter--;
}
static bool8 DecrementAffineAnimDelayCounter(struct Sprite *sprite, u8 matrixNum)
{
if (!sprite->affineAnimPaused)
--sAffineAnimStates[matrixNum].delayCounter;
return sprite->affineAnimPaused;
}
static void ApplyAffineAnimFrameRelativeAndUpdateMatrix(u8 matrixNum, struct AffineAnimFrameCmd *frameCmd)
{
struct ObjAffineSrcData srcData;
struct OamMatrix matrix;
sAffineAnimStates[matrixNum].xScale += frameCmd->xScale;
sAffineAnimStates[matrixNum].yScale += frameCmd->yScale;
sAffineAnimStates[matrixNum].rotation = (sAffineAnimStates[matrixNum].rotation + (frameCmd->rotation << 8)) & ~0xFF;
srcData.xScale = ConvertScaleParam(sAffineAnimStates[matrixNum].xScale);
srcData.yScale = ConvertScaleParam(sAffineAnimStates[matrixNum].yScale);
srcData.rotation = sAffineAnimStates[matrixNum].rotation;
ObjAffineSet(&srcData, &matrix, 1, 2);
CopyOamMatrix(matrixNum, &matrix);
}
static s16 ConvertScaleParam(s16 scale)
{
s32 val = 0x10000;
return val / scale;
}
static void GetAffineAnimFrame(u8 matrixNum, struct Sprite *sprite, struct AffineAnimFrameCmd *frameCmd)
{
frameCmd->xScale = sprite->affineAnims[sAffineAnimStates[matrixNum].animNum][sAffineAnimStates[matrixNum].animCmdIndex].frame.xScale;
frameCmd->yScale = sprite->affineAnims[sAffineAnimStates[matrixNum].animNum][sAffineAnimStates[matrixNum].animCmdIndex].frame.yScale;
frameCmd->rotation = sprite->affineAnims[sAffineAnimStates[matrixNum].animNum][sAffineAnimStates[matrixNum].animCmdIndex].frame.rotation;
frameCmd->duration = sprite->affineAnims[sAffineAnimStates[matrixNum].animNum][sAffineAnimStates[matrixNum].animCmdIndex].frame.duration;
}
static void ApplyAffineAnimFrame(u8 matrixNum, struct AffineAnimFrameCmd *frameCmd)
{
struct AffineAnimFrameCmd dummyFrameCmd = {0};
if (frameCmd->duration)
{
frameCmd->duration--;
ApplyAffineAnimFrameRelativeAndUpdateMatrix(matrixNum, frameCmd);
}
else
{
ApplyAffineAnimFrameAbsolute(matrixNum, frameCmd);
ApplyAffineAnimFrameRelativeAndUpdateMatrix(matrixNum, &dummyFrameCmd);
}
}
void StartSpriteAnim(struct Sprite *sprite, u8 animNum)
{
sprite->animNum = animNum;
sprite->animBeginning = TRUE;
sprite->animEnded = FALSE;
}
void StartSpriteAnimIfDifferent(struct Sprite *sprite, u8 animNum)
{
if (sprite->animNum != animNum)
StartSpriteAnim(sprite, animNum);
}
void SeekSpriteAnim(struct Sprite *sprite, u8 animCmdIndex)
{
u8 temp = sprite->animPaused;
sprite->animCmdIndex = animCmdIndex - 1;
sprite->animDelayCounter = 0;
sprite->animBeginning = FALSE;
sprite->animEnded = FALSE;
sprite->animPaused = FALSE;
ContinueAnim(sprite);
if (sprite->animDelayCounter)
sprite->animDelayCounter++;
sprite->animPaused = temp;
}
void StartSpriteAffineAnim(struct Sprite *sprite, u8 animNum)
{
u8 matrixNum = GetSpriteMatrixNum(sprite);
AffineAnimStateStartAnim(matrixNum, animNum);
sprite->affineAnimBeginning = TRUE;
sprite->affineAnimEnded = FALSE;
}
void StartSpriteAffineAnimIfDifferent(struct Sprite *sprite, u8 animNum)
{
u8 matrixNum = GetSpriteMatrixNum(sprite);
if (sAffineAnimStates[matrixNum].animNum != animNum)
StartSpriteAffineAnim(sprite, animNum);
}
void ChangeSpriteAffineAnim(struct Sprite *sprite, u8 animNum)
{
u8 matrixNum = GetSpriteMatrixNum(sprite);
sAffineAnimStates[matrixNum].animNum = animNum;
sprite->affineAnimBeginning = TRUE;
sprite->affineAnimEnded = FALSE;
}
void ChangeSpriteAffineAnimIfDifferent(struct Sprite *sprite, u8 animNum)
{
u8 matrixNum = GetSpriteMatrixNum(sprite);
if (sAffineAnimStates[matrixNum].animNum != animNum)
ChangeSpriteAffineAnim(sprite, animNum);
}
void SetSpriteSheetFrameTileNum(struct Sprite *sprite)
{
if (sprite->usingSheet)
{
s16 tileOffset = sprite->anims[sprite->animNum][sprite->animCmdIndex].frame.imageValue;
if (tileOffset < 0)
tileOffset = 0;
sprite->oam.tileNum = sprite->sheetTileStart + tileOffset;
}
}
static void ResetAffineAnimData(void)
{
u8 i;
gAffineAnimsDisabled = 0;
gOamMatrixAllocBitmap = 0;
ResetOamMatrices();
for (i = 0; i < OAM_MATRIX_COUNT; i++)
AffineAnimStateReset(i);
}
u8 AllocOamMatrix(void)
{
u8 i = 0;
u32 bit = 1;
u32 bitmap = gOamMatrixAllocBitmap;
while (i < OAM_MATRIX_COUNT)
{
if (!(bitmap & bit))
{
gOamMatrixAllocBitmap |= bit;
return i;
}
i++;
bit <<= 1;
}
return 0xFF;
}
void FreeOamMatrix(u8 matrixNum)
{
u8 i = 0;
u32 bit = 1;
while (i < matrixNum)
{
i++;
bit <<= 1;
}
gOamMatrixAllocBitmap &= ~bit;
SetOamMatrix(matrixNum, 0x100, 0, 0, 0x100);
}
void InitSpriteAffineAnim(struct Sprite *sprite)
{
u8 matrixNum = AllocOamMatrix();
if (matrixNum != 0xFF)
{
CalcCenterToCornerVec(sprite, sprite->oam.shape, sprite->oam.size, sprite->oam.affineMode);
sprite->oam.matrixNum = matrixNum;
sprite->affineAnimBeginning = TRUE;
AffineAnimStateReset(matrixNum);
}
}
void SetOamMatrixRotationScaling(u8 matrixNum, s16 xScale, s16 yScale, u16 rotation)
{
struct ObjAffineSrcData srcData;
struct OamMatrix matrix;
srcData.xScale = ConvertScaleParam(xScale);
srcData.yScale = ConvertScaleParam(yScale);
srcData.rotation = rotation;
ObjAffineSet(&srcData, &matrix, 1, 2);
CopyOamMatrix(matrixNum, &matrix);
}
u16 LoadSpriteSheet(struct SpriteSheet *sheet)
{
s16 tileStart = AllocSpriteTiles(sheet->size / TILE_SIZE_4BPP);
if (tileStart < 0)
{
return 0;
}
else
{
AllocSpriteTileRange(sheet->tag, (u16)tileStart, sheet->size / TILE_SIZE_4BPP);
CpuCopy16(sheet->data, (u8 *)OBJ_VRAM0 + TILE_SIZE_4BPP * tileStart, sheet->size);
return (u16)tileStart;
}
}
void LoadSpriteSheets(struct SpriteSheet *sheets)
{
u8 i;
for (i = 0; sheets[i].data != NULL; i++)
LoadSpriteSheet(&sheets[i]);
}
u16 AllocTilesForSpriteSheet(struct SpriteSheet *sheet)
{
s16 tileStart = AllocSpriteTiles(sheet->size / TILE_SIZE_4BPP);
if (tileStart < 0)
{
return 0;
}
else
{
AllocSpriteTileRange(sheet->tag, (u16)tileStart, sheet->size / TILE_SIZE_4BPP);
return (u16)tileStart;
}
}
void AllocTilesForSpriteSheets(struct SpriteSheet *sheets)
{
u8 i;
for (i = 0; sheets[i].data != NULL; i++)
AllocTilesForSpriteSheet(&sheets[i]);
}
void LoadTilesForSpriteSheet(struct SpriteSheet *sheet)
{
u8 *data = sheet->data;
u16 tileStart = GetSpriteTileStartByTag(sheet->tag);
CpuCopy16(data, (u8 *)OBJ_VRAM0 + TILE_SIZE_4BPP * tileStart, sheet->size);
}
void LoadTilesForSpriteSheets(struct SpriteSheet *sheets)
{
u8 i;
for (i = 0; sheets[i].data != NULL; i++)
LoadTilesForSpriteSheet(&sheets[i]);
}
void FreeSpriteTilesByTag(u16 tag)
{
u8 index = IndexOfSpriteTileTag(tag);
if (index != 0xFF)
{
u16 i;
u16 *rangeStarts;
u16 *rangeCounts;
u16 start;
u16 count;
rangeStarts = sSpriteTileRanges;
start = rangeStarts[index * 2];
rangeCounts = sSpriteTileRanges + 1;
count = rangeCounts[index * 2];
for (i = start; i < start + count; i++)
FREE_SPRITE_TILE(i);
sSpriteTileRangeTags[index] = 0xFFFF;
}
}
void FreeSpriteTileRanges(void)
{
u8 i;
for (i = 0; i < MAX_SPRITES; i++)
{
sSpriteTileRangeTags[i] = 0xFFFF;
SET_SPRITE_TILE_RANGE(i, 0, 0);
}
}
u16 GetSpriteTileStartByTag(u16 tag)
{
u8 index = IndexOfSpriteTileTag(tag);
if (index == 0xFF)
return 0xFFFF;
return sSpriteTileRanges[index * 2];
}
static u8 IndexOfSpriteTileTag(u16 tag)
{
u8 i;
for (i = 0; i < MAX_SPRITES; i++)
if (sSpriteTileRangeTags[i] == tag)
return i;
return 0xFF;
}
u16 GetSpriteTileTagByTileStart(u16 start)
{
u8 i;
for (i = 0; i < MAX_SPRITES; i++)
{
if (sSpriteTileRangeTags[i] != 0xFFFF && sSpriteTileRanges[i * 2] == start)
return sSpriteTileRangeTags[i];
}
return 0xFFFF;
}
static void AllocSpriteTileRange(u16 tag, u16 start, u16 count)
{
u8 freeIndex = IndexOfSpriteTileTag(0xFFFF);
sSpriteTileRangeTags[freeIndex] = tag;
SET_SPRITE_TILE_RANGE(freeIndex, start, count);
}
void RequestSpriteSheetCopy(struct SpriteSheet *sheet)
{
u8 *data = sheet->data;
u16 tileStart = GetSpriteTileStartByTag(sheet->tag);
RequestSpriteCopy(data, (u8 *)OBJ_VRAM0 + tileStart * TILE_SIZE_4BPP, sheet->size);
}
u16 LoadSpriteSheetDeferred(struct SpriteSheet *sheet)
{
s16 tileStart = AllocSpriteTiles(sheet->size / TILE_SIZE_4BPP);
if (tileStart < 0)
{
return 0;
}
else
{
AllocSpriteTileRange(sheet->tag, (u16)tileStart, sheet->size / TILE_SIZE_4BPP);
RequestSpriteSheetCopy(sheet);
return (u16)tileStart;
}
}
void FreeAllSpritePalettes(void)
{
u8 i;
gReservedSpritePaletteCount = 0;
for (i = 0; i < 16; i++)
sSpritePaletteTags[i] = 0xFFFF;
}
u8 LoadSpritePalette(struct SpritePalette *palette)
{
u8 index = IndexOfSpritePaletteTag(palette->tag);
if (index != 0xFF)
return index;
index = IndexOfSpritePaletteTag(0xFFFF);
if (index == 0xFF)
{
return 0xFF;
}
else
{
sSpritePaletteTags[index] = palette->tag;
DoLoadSpritePalette(palette->data, index * 16);
return index;
}
}
void LoadSpritePalettes(struct SpritePalette *palettes)
{
u8 i;
for (i = 0; palettes[i].data != NULL; i++)
if (LoadSpritePalette(&palettes[i]) == 0xFF)
break;
}
static void DoLoadSpritePalette(u16 *src, u16 paletteOffset)
{
LoadPalette(src, paletteOffset + 0x100, 32);
}
u8 AllocSpritePalette(u16 tag)
{
u8 index = IndexOfSpritePaletteTag(0xFFFF);
if (index == 0xFF)
{
return 0xFF;
}
else
{
sSpritePaletteTags[index] = tag;
return index;
}
}
u8 IndexOfSpritePaletteTag(u16 tag)
{
u8 i;
for (i = gReservedSpritePaletteCount; i < 16; i++)
if (sSpritePaletteTags[i] == tag)
return i;
return 0xFF;
}
u16 GetSpritePaletteTagByPaletteNum(u8 paletteNum)
{
return sSpritePaletteTags[paletteNum];
}
void FreeSpritePaletteByTag(u16 tag)
{
u8 index = IndexOfSpritePaletteTag(tag);
if (index != 0xFF)
sSpritePaletteTags[index] = 0xFFFF;
}
void SetSubspriteTables(struct Sprite *sprite, struct SubspriteTable *subspriteTables)
{
sprite->subspriteTables = subspriteTables;
sprite->subspriteTableNum = 0;
sprite->subspriteMode = SUBSPRITES_ON;
}
bool8 AddSpriteToOamBuffer(struct Sprite *sprite, u8 *oamIndex)
{
if (*oamIndex >= gOamLimit)
return 1;
if (!sprite->subspriteTables || sprite->subspriteMode == SUBSPRITES_OFF)
{
gMain.oamBuffer[*oamIndex] = sprite->oam;
(*oamIndex)++;
return 0;
}
else
{
return AddSubspritesToOamBuffer(sprite, &gMain.oamBuffer[*oamIndex], oamIndex);
}
}
bool8 AddSubspritesToOamBuffer(struct Sprite *sprite, struct OamData *destOam, u8 *oamIndex)
{
struct SubspriteTable *subspriteTable;
struct OamData *oam;
if (*oamIndex >= gOamLimit)
return 1;
subspriteTable = &sprite->subspriteTables[sprite->subspriteTableNum];
oam = &sprite->oam;
if (!subspriteTable || !subspriteTable->subsprites)
{
*destOam = *oam;
(*oamIndex)++;
return 0;
}
else
{
u16 tileNum;
u16 baseX;
u16 baseY;
u8 subspriteCount;
u8 hFlip;
u8 vFlip;
u8 i;
tileNum = oam->tileNum;
subspriteCount = subspriteTable->subspriteCount;
hFlip = ((s32)oam->matrixNum >> 3) & 1;
vFlip = ((s32)oam->matrixNum >> 4) & 1;
baseX = oam->x - sprite->centerToCornerVecX;
baseY = oam->y - sprite->centerToCornerVecY;
for (i = 0; i < subspriteCount; i++, (*oamIndex)++)
{
u16 x;
u16 y;
if (*oamIndex >= gOamLimit)
return 1;
x = subspriteTable->subsprites[i].x;
y = subspriteTable->subsprites[i].y;
if (hFlip)
{
s8 width = sOamDimensions[subspriteTable->subsprites[i].shape][subspriteTable->subsprites[i].size].width;
s16 right = x;
right += width;
x = right;
x = ~x + 1;
}
if (vFlip)
{
s8 height = sOamDimensions[subspriteTable->subsprites[i].shape][subspriteTable->subsprites[i].size].height;
s16 bottom = y;
bottom += height;
y = bottom;
y = ~y + 1;
}
destOam[i] = *oam;
destOam[i].shape = subspriteTable->subsprites[i].shape;
destOam[i].size = subspriteTable->subsprites[i].size;
destOam[i].x = (s16)baseX + (s16)x;
destOam[i].y = baseY + y;
destOam[i].tileNum = tileNum + subspriteTable->subsprites[i].tileOffset;
if (sprite->subspriteMode != SUBSPRITES_IGNORE_PRIORITY)
destOam[i].priority = subspriteTable->subsprites[i].priority;
}
}
return 0;
}