summaryrefslogtreecommitdiff
path: root/src/sprite.c
diff options
context:
space:
mode:
authorcamthesaxman <cameronghall@cox.net>2018-02-18 19:59:51 -0600
committercamthesaxman <cameronghall@cox.net>2018-02-18 19:59:51 -0600
commitdaa05c1b7c5e079ad8554e88a290e07b333bd2db (patch)
treef635e307268f6e6027423369d221e2adf32b438d /src/sprite.c
parent2e21a867487e2187a954a351f775bd3f3a9535b1 (diff)
remove subdirectories
Diffstat (limited to 'src/sprite.c')
-rw-r--r--src/sprite.c1803
1 files changed, 1803 insertions, 0 deletions
diff --git a/src/sprite.c b/src/sprite.c
new file mode 100644
index 000000000..a9d84e01a
--- /dev/null
+++ b/src/sprite.c
@@ -0,0 +1,1803 @@
+#include "global.h"
+#include "debug.h"
+#include "sprite.h"
+#include "main.h"
+#include "menu_cursor.h"
+#include "palette.h"
+#include "sprite.h"
+
+#define MAX_SPRITE_COPY_REQUESTS 64
+
+#define OAM_MATRIX_COUNT 32
+
+#define SET_SPRITE_TILE_RANGE(index, start, count) \
+{ \
+ sSpriteTileRanges[index * 2] = start; \
+ (sSpriteTileRanges + 1)[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 SpriteCopyRequest
+{
+ const u8 *src;
+ u8 *dest;
+ u16 size;
+};
+
+struct OamDimensions
+{
+ s8 width;
+ s8 height;
+};
+
+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, const 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 void RequestSpriteFrameImageCopy(u16 index, u16 tileNum, const struct SpriteFrameImage *images);
+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(const 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 =
+{
+ .oam = DUMMY_OAM_DATA,
+ .anims = gDummySpriteAnimTable,
+ .images = NULL,
+ .affineAnims = gDummySpriteAffineAnimTable,
+ .template = &gDummySpriteTemplate,
+ .subspriteTables = NULL,
+ .callback = SpriteCallbackDummy,
+ .pos1 = { 304, 160 },
+ .pos2 = { 0, 0 },
+ .centerToCornerVecX = 0,
+ .centerToCornerVecY = 0,
+ .animNum = 0,
+ .animCmdIndex = 0,
+ .animDelayCounter = 0,
+ .animPaused = 0,
+ .affineAnimPaused = 0,
+ .animLoopCounter = 0,
+ .data = {0},
+ .inUse = 0,
+ .coordOffsetEnabled = 0,
+ .invisible = 0,
+ .flags_3 = 0,
+ .flags_4 = 0,
+ .flags_5 = 0,
+ .flags_6 = 0,
+ .flags_7 = 0,
+ .hFlip = 0,
+ .vFlip = 0,
+ .animBeginning = 0,
+ .affineAnimBeginning = 0,
+ .animEnded = 0,
+ .affineAnimEnded = 0,
+ .usingSheet = 0,
+ .flags_f = 0,
+ .sheetTileStart = 0,
+ .subspriteTableNum = 0,
+ .subspriteMode = 0,
+ .subpriority = 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 =
+{
+ .tileTag = 0,
+ .paletteTag = 0xFFFF,
+ .oam = &gDummyOamData,
+ .anims = gDummySpriteAnimTable,
+ .images = NULL,
+ .affineAnims = gDummySpriteAffineAnimTable,
+ .callback = 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(const struct SpriteTemplate *template, s16 x, s16 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(const struct SpriteTemplate *template, s16 x, s16 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(&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, const 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(const 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;
+}
+
+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, const struct SpriteFrameImage *images)
+{
+ if (gSpriteCopyRequestCount < MAX_SPRITE_COPY_REQUESTS)
+ {
+ gSpriteCopyRequests[gSpriteCopyRequestCount].src = images[index].data;
+ gSpriteCopyRequests[gSpriteCopyRequestCount].dest = OBJ_VRAM0 + TILE_SIZE_4BPP * tileNum;
+ gSpriteCopyRequests[gSpriteCopyRequestCount].size = images[index].size;
+ gSpriteCopyRequestCount++;
+ }
+#if DEBUG
+ else
+ {
+ Crash(sDmaOverErrorMsg);
+ }
+#endif
+}
+
+void RequestSpriteCopy(const void *src, u8 *dest, u16 size)
+{
+ if (gSpriteCopyRequestCount < MAX_SPRITE_COPY_REQUESTS)
+ {
+ gSpriteCopyRequests[gSpriteCopyRequestCount].src = src;
+ gSpriteCopyRequests[gSpriteCopyRequestCount].dest = dest;
+ gSpriteCopyRequests[gSpriteCopyRequestCount].size = size;
+ gSpriteCopyRequestCount++;
+ }
+#if DEBUG
+ else
+ {
+ Crash(sDmaOverErrorMsg);
+ }
+#endif
+}
+
+// these two functions are unused.
+void CopyFromSprites(u8 *dest)
+{
+ u32 i;
+ u8 *src = (u8 *)gSprites;
+ for (i = 0; i < sizeof(struct Sprite) * MAX_SPRITES; i++)
+ {
+ *dest = *src;
+ dest++;
+ src++;
+ }
+}
+
+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);
+}
+
+// Loads the mon name, level, and hp sprites into OAM.
+void DrawPartyMenuMonText(u32 a1, u32 a2, const u16 *a3, u16 a4, u32 baseTileNumber)
+{
+ const 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] + baseTileNumber) & 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(const 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, OBJ_VRAM0 + TILE_SIZE_4BPP * tileStart, sheet->size);
+ return (u16)tileStart;
+ }
+}
+
+void LoadSpriteSheets(const 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(const struct SpriteSheet *sheet)
+{
+ const u8 *data = sheet->data;
+ u16 tileStart = GetSpriteTileStartByTag(sheet->tag);
+ CpuCopy16(data, 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(const struct SpriteSheet *sheet)
+{
+ const u8 *data = sheet->data;
+ u16 tileStart = GetSpriteTileStartByTag(sheet->tag);
+ RequestSpriteCopy(data, OBJ_VRAM0 + tileStart * TILE_SIZE_4BPP, sheet->size);
+}
+
+u16 LoadSpriteSheetDeferred(const 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(const 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(const struct SpritePalette *palettes)
+{
+ u8 i;
+ for (i = 0; palettes[i].data != NULL; i++)
+ if (LoadSpritePalette(&palettes[i]) == 0xFF)
+ break;
+}
+
+static void DoLoadSpritePalette(const 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, const 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)
+{
+ const 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;
+}