pokeplatinum/src/sprite.c
2025-08-23 14:31:51 -07:00

930 lines
28 KiB
C

#include "sprite.h"
#include <nitro.h>
#include <string.h>
#include "heap.h"
#include "system.h"
typedef void (*SpriteDrawFunc)(const SpriteList *, Sprite *);
typedef void (*SpriteAnimUpdateFunc)(Sprite *);
static void SpriteList_Reset(SpriteList *list);
static enum CellAnimType SpriteResourcesHeader_GetCellType(const SpriteResourcesHeader *resourceData);
static void Sprite_SetCellBank(const NNSG2dCellDataBank *cellBank, Sprite *sprite);
static void Sprite_SetCellAnimBank(const NNSG2dCellAnimBankData *cellAnimBank, Sprite *sprite);
static void Sprite_SetMultiCellBank(const NNSG2dMultiCellDataBank *multiCellBank, Sprite *sprite);
static void Sprite_SetMultiCellAnimBank(const NNSG2dMultiCellAnimBankData *multiCellAnimBank, Sprite *sprite);
static void Sprite_CreateCellAnim(Sprite *sprite, enum HeapID heapID);
static void Sprite_CreateVRamCellAnim(const SpriteResourcesHeader *resourceData, Sprite *sprite, enum HeapID heapID);
static void Sprite_CreateMultiCellAnim(Sprite *sprite, enum HeapID heapID);
static BOOL SpriteList_InitSprite(const SpriteList *list,
const SpriteResourcesHeader *resourceData,
Sprite *sprite,
enum HeapID heapID);
static u32 GetPaletteIndexForProxy(const NNSG2dImagePaletteProxy *paletteProxy, u32 vramType);
static void SpriteList_DrawSprite(const SpriteList *list, Sprite *sprite);
static void SpriteList_DrawSprite_Stub(const SpriteList *list, Sprite *sprite);
static void Sprite_UpdateAnimInternal(Sprite *list);
static void Sprite_UpdateAnimInternal_Stub(Sprite *list);
static void SpriteList_Insert(SpriteList *list, Sprite *sprite);
static void SpriteList_Remove(Sprite *sprite);
static void SpriteList_InitSprites(SpriteList *list);
static Sprite *SpriteList_AllocSprite(SpriteList *list);
static BOOL SpriteList_FreeSprite(SpriteList *list, Sprite *sprite);
SpriteList *SpriteList_New(const SpriteListParams *params)
{
GF_ASSERT(params);
GF_ASSERT(params->renderer);
SpriteList *list = Heap_Alloc(params->heapID, sizeof(SpriteList));
GF_ASSERT(list);
SpriteList_Reset(list);
list->sprites = Heap_Alloc(params->heapID, sizeof(Sprite) * params->maxElements);
GF_ASSERT(list->sprites);
list->capacity = params->maxElements;
list->freeSprites = Heap_Alloc(params->heapID, sizeof(Sprite *) * params->maxElements);
GF_ASSERT(list->freeSprites);
SpriteList_InitSprites(list);
Sprite_Reset(&list->sentinelData);
list->sentinelData.prev = &list->sentinelData;
list->sentinelData.next = &list->sentinelData;
list->renderer = params->renderer;
list->rawAnimData = ReadFileToHeap(params->heapID, "data/clact_default.NANR");
NNS_G2dGetUnpackedAnimBank(list->rawAnimData, &list->defaultAnimBank);
list->active = TRUE;
return list;
}
BOOL SpriteList_Delete(SpriteList *list)
{
if (list == NULL) {
return FALSE;
}
if (list->sprites == NULL) {
return TRUE;
}
SpriteList_DeleteAll(list);
Heap_Free(list->rawAnimData);
Heap_Free(list->freeSprites);
Heap_Free(list->sprites);
SpriteList_Reset(list);
Heap_Free(list);
list = NULL;
return TRUE;
}
BOOL SpriteList_SetActive(SpriteList *list, u8 active)
{
if (list == NULL) {
return FALSE;
}
if (list->sprites == NULL) {
return FALSE;
}
list->active = active;
return TRUE;
}
BOOL SpriteList_DeleteAll(SpriteList *list)
{
if (list == NULL) {
return FALSE;
}
if (list->sprites == NULL) {
return TRUE;
}
Sprite *sprite = list->sentinelData.next;
while (sprite != &list->sentinelData) {
Sprite *next = sprite->next;
Sprite_Delete(sprite);
sprite = next;
}
return TRUE;
}
void SpriteList_Update(const SpriteList *list)
{
static const SpriteDrawFunc sDrawFuncs[] = {
SpriteList_DrawSprite_Stub,
SpriteList_DrawSprite
};
static const SpriteAnimUpdateFunc sAnimUpdateFuncs[] = {
Sprite_UpdateAnimInternal_Stub,
Sprite_UpdateAnimInternal
};
GF_ASSERT(list);
if (list->active == FALSE) {
return;
}
Sprite *sprite = list->sentinelData.next;
while (sprite != &list->sentinelData) {
sDrawFuncs[sprite->draw](list, sprite);
sAnimUpdateFuncs[sprite->animate](sprite);
sprite = sprite->next;
}
}
static void SpriteList_Reset(SpriteList *list)
{
list->sprites = NULL;
list->capacity = 0;
list->freeSprites = NULL;
list->freeSpriteHead = 0;
list->renderer = NULL;
Sprite_Reset(&list->sentinelData);
list->active = FALSE;
}
void Sprite_Reset(Sprite *sprite)
{
sprite->list = NULL;
memset(sprite, 0, sizeof(Sprite));
NNS_G2dInitImageProxy(&sprite->imageProxy);
NNS_G2dInitImagePaletteProxy(&sprite->paletteProxy);
sprite->explicitOamMode = GX_OAM_MODE_NORMAL;
}
Sprite *SpriteList_AddAffine(const AffineSpriteListTemplate *template)
{
Sprite *sprite = SpriteList_AllocSprite(template->list);
if (sprite == NULL) {
return NULL;
}
sprite->list = template->list;
sprite->activeAnimID = 0;
sprite->position = template->position;
sprite->affineScale = template->affineScale;
sprite->affineZRotation = template->affineZRotation;
sprite->vramType = template->vramType;
sprite->priority = template->priority;
sprite->affineOverwriteMode = NNS_G2D_RND_AFFINE_OVERWRITE_NONE;
sprite->flip = NNS_G2D_RENDERERFLIP_NONE;
sprite->explicitMosaic = FALSE;
sprite->explicitOamMode = GX_OAM_MODE_NORMAL;
sprite->overwriteFlags = NNS_G2D_RND_OVERWRITE_PLTTNO_OFFS | NNS_G2D_RND_OVERWRITE_PRIORITY;
NNS_G2dSetRndCoreAffineOverwriteMode(
&template->list->renderer->rendererCore,
sprite->affineOverwriteMode);
NNS_G2dSetRndCoreFlipMode(
&template->list->renderer->rendererCore,
sprite->flip & NNS_G2D_RENDERERFLIP_H,
sprite->flip & NNS_G2D_RENDERERFLIP_V);
sprite->draw = TRUE;
sprite->animate = FALSE;
sprite->animSpeed = (FX32_ONE * 2);
if (SpriteList_InitSprite(template->list, template->resourceData, sprite, template->heapID) == FALSE) {
Sprite_Delete(sprite);
return NULL;
}
sprite->explicitPaletteOffset = GetPaletteIndexForProxy(&sprite->paletteProxy, sprite->vramType);
sprite->explicitPalette = sprite->explicitPaletteOffset;
SpriteList_Insert(template->list, sprite);
return sprite;
}
Sprite *SpriteList_Add(const SpriteListTemplate *template)
{
AffineSpriteListTemplate affineTemplate;
affineTemplate.list = template->list;
affineTemplate.resourceData = template->resourceData;
affineTemplate.position = template->position;
affineTemplate.affineScale.x = FX32_ONE;
affineTemplate.affineScale.y = FX32_ONE;
affineTemplate.affineScale.z = FX32_ONE;
affineTemplate.affineZRotation = 0;
affineTemplate.priority = template->priority;
affineTemplate.vramType = template->vramType;
affineTemplate.heapID = template->heapID;
return SpriteList_AddAffine(&affineTemplate);
}
void Sprite_Delete(Sprite *sprite)
{
if (sprite->type == CELL_ANIM_TYPE_NONE) {
return;
}
if (sprite->prev != NULL) {
SpriteList_Remove(sprite);
}
if (sprite->type == CELL_ANIM_TYPE_VRAM_CELL) {
VRamCellAnimationData *vramCellAnim = (VRamCellAnimationData *)&sprite->animData;
if (NNS_G2dGetImageLocation(&sprite->imageProxy, sprite->vramType) != NNS_G2D_VRAM_ADDR_NONE) {
NNS_G2dFreeCellTransferStateHandle(vramCellAnim->transferHandle);
}
}
if (sprite->type == CELL_ANIM_TYPE_MULTI_CELL) {
MultiCellAnimationData *multiCellAnim = (MultiCellAnimationData *)&sprite->animData;
if (multiCellAnim->nodes != NULL) {
Heap_Free(multiCellAnim->nodes);
}
if (multiCellAnim->cellAnims != NULL) {
Heap_Free(multiCellAnim->cellAnims);
}
}
sprite->type = CELL_ANIM_TYPE_NONE;
SpriteList_FreeSprite(sprite->list, sprite);
}
void Sprite_SetPosition(Sprite *sprite, const VecFx32 *position)
{
sprite->position = *position;
}
void Sprite_SetAffineTranslation(Sprite *sprite, const VecFx32 *translation)
{
sprite->affineTranslation = *translation;
}
void Sprite_SetAffineScale(Sprite *sprite, const VecFx32 *scale)
{
sprite->affineScale = *scale;
}
void Sprite_SetAffineScaleEx(Sprite *sprite, const VecFx32 *scale, enum AffineOverwriteMode mode)
{
Sprite_SetAffineScale(sprite, scale);
Sprite_SetAffineOverwriteMode(sprite, mode);
}
void Sprite_SetAffineZRotation(Sprite *sprite, u16 angle)
{
sprite->affineZRotation = angle;
}
void Sprite_SetAffineZRotationEx(Sprite *sprite, u16 angle, enum AffineOverwriteMode mode)
{
Sprite_SetAffineZRotation(sprite, angle);
Sprite_SetAffineOverwriteMode(sprite, mode);
}
void Sprite_SetDrawFlag(Sprite *sprite, BOOL draw)
{
GF_ASSERT(sprite);
GF_ASSERT(draw < 2); // This value is used as an index into an array of size 2 inside SpriteList_Update
sprite->draw = draw;
}
void Sprite_SetAnimateFlag(Sprite *sprite, BOOL animate)
{
GF_ASSERT(sprite);
GF_ASSERT(animate < 2); // This value is used as an index into an array of size 2 inside SpriteList_Update
sprite->animate = animate;
}
void Sprite_SetAnimSpeed(Sprite *sprite, fx32 speed)
{
GF_ASSERT(sprite);
sprite->animSpeed = speed;
}
void Sprite_SetAffineOverwriteMode(Sprite *sprite, enum AffineOverwriteMode mode)
{
GF_ASSERT(sprite);
sprite->affineOverwriteMode = mode;
}
void Sprite_SetFlipMode(Sprite *sprite, u32 mode)
{
GF_ASSERT(sprite);
sprite->flip = mode;
sprite->affineOverwriteMode = NNS_G2D_RND_AFFINE_OVERWRITE_NONE;
}
const VecFx32 *Sprite_GetPosition(const Sprite *sprite)
{
return &sprite->position;
}
const VecFx32 *Sprite_GetAffineScale(const Sprite *sprite)
{
return &sprite->affineScale;
}
u16 Sprite_GetAffineZRotation(const Sprite *sprite)
{
return sprite->affineZRotation;
}
BOOL Sprite_GetDrawFlag(const Sprite *sprite)
{
return sprite->draw;
}
BOOL Sprite_GetAnimateFlag(const Sprite *sprite)
{
return sprite->animate;
}
u32 Sprite_GetAnimCount(const Sprite *sprite)
{
GF_ASSERT(sprite);
if (sprite->type == CELL_ANIM_TYPE_CELL || sprite->type == CELL_ANIM_TYPE_VRAM_CELL) {
CellAnimationData *cellAnim = (CellAnimationData *)&sprite->animData;
return cellAnim->animBank->numSequences;
} else {
MultiCellAnimationData *multiCellAnim = (MultiCellAnimationData *)&sprite->animData;
return multiCellAnim->animBank->numSequences;
}
}
void Sprite_SetAnim(Sprite *sprite, u32 animID)
{
GF_ASSERT(Sprite_GetAnimCount(sprite) > animID);
sprite->activeAnimID = animID;
if (sprite->type == CELL_ANIM_TYPE_CELL || sprite->type == CELL_ANIM_TYPE_VRAM_CELL) {
CellAnimationData *cellAnim = (CellAnimationData *)&sprite->animData;
const NNSG2dAnimSequence *animSequence = NNS_G2dGetAnimSequenceByIdx(cellAnim->animBank, animID);
NNS_G2dSetCellAnimationSequence(&cellAnim->anim, animSequence);
NNS_G2dStartAnimCtrl(&cellAnim->anim.animCtrl);
} else {
MultiCellAnimationData *multiCellAnim = (MultiCellAnimationData *)&sprite->animData;
const NNSG2dAnimSequence *animSequence = NNS_G2dGetAnimSequenceByIdx(multiCellAnim->animBank, animID);
NNS_G2dSetAnimSequenceToMCAnimation(&multiCellAnim->anim, animSequence);
NNS_G2dStartAnimCtrl(&multiCellAnim->anim.animCtrl);
}
}
void Sprite_SetAnimNoRestart(Sprite *sprite, u32 animID)
{
if (sprite->activeAnimID != animID) {
Sprite_SetAnim(sprite, animID);
}
}
void Sprite_RestartAnim(Sprite *sprite)
{
if (sprite->type == CELL_ANIM_TYPE_CELL || sprite->type == CELL_ANIM_TYPE_VRAM_CELL) {
CellAnimationData *cellAnim = (CellAnimationData *)&sprite->animData;
NNS_G2dResetAnimCtrlState(&cellAnim->anim.animCtrl);
NNS_G2dStartAnimCtrl(&cellAnim->anim.animCtrl);
Sprite_SetAnimFrame(sprite, 0);
} else {
MultiCellAnimationData *multiCellAnim = (MultiCellAnimationData *)&sprite->animData;
NNS_G2dResetAnimCtrlState(&multiCellAnim->anim.animCtrl);
NNS_G2dStartAnimCtrl(&multiCellAnim->anim.animCtrl);
Sprite_SetAnimFrame(sprite, 0);
}
}
u32 Sprite_GetActiveAnim(const Sprite *sprite)
{
return sprite->activeAnimID;
}
void Sprite_UpdateAnim(Sprite *sprite, fx32 frames)
{
if (sprite->type == CELL_ANIM_TYPE_CELL || sprite->type == CELL_ANIM_TYPE_VRAM_CELL) {
CellAnimationData *cellAnim = (CellAnimationData *)&sprite->animData;
NNS_G2dTickCellAnimation(&cellAnim->anim, frames);
} else {
MultiCellAnimationData *multiCellAnim = (MultiCellAnimationData *)&sprite->animData;
NNS_G2dTickMCAnimation(&multiCellAnim->anim, frames);
}
}
void Sprite_SetAnimFrame(Sprite *sprite, u16 frame)
{
if (sprite->type == CELL_ANIM_TYPE_CELL || sprite->type == CELL_ANIM_TYPE_VRAM_CELL) {
CellAnimationData *cellAnim = (CellAnimationData *)&sprite->animData;
NNS_G2dSetCellAnimationCurrentFrame(&cellAnim->anim, frame);
} else {
MultiCellAnimationData *multiCellAnim = (MultiCellAnimationData *)&sprite->animData;
NNS_G2dSetMCAnimationCurrentFrame(&multiCellAnim->anim, frame);
}
}
u16 Sprite_GetAnimFrame(const Sprite *sprite)
{
NNSG2dAnimController *controller;
if (sprite->type == CELL_ANIM_TYPE_CELL || sprite->type == CELL_ANIM_TYPE_VRAM_CELL) {
CellAnimationData *cellAnim = (CellAnimationData *)&sprite->animData;
controller = NNS_G2dGetCellAnimationAnimCtrl(&cellAnim->anim);
} else {
MultiCellAnimationData *multiCellAnim = (MultiCellAnimationData *)&sprite->animData;
controller = NNS_G2dGetMCAnimAnimCtrl(&multiCellAnim->anim);
}
return NNS_G2dGetAnimCtrlCurrentFrame(controller);
}
void Sprite_SetExplicitPriority(Sprite *sprite, u8 priority)
{
sprite->explicitPriority = priority;
}
u8 Sprite_GetExplicitPriority(const Sprite *sprite)
{
return sprite->explicitPriority;
}
void Sprite_SetExplicitPalette(Sprite *sprite, u32 palette)
{
GF_ASSERT(sprite);
sprite->explicitPalette = palette;
sprite->overwriteFlags |= NNS_G2D_RND_OVERWRITE_PLTTNO;
sprite->overwriteFlags &= ~NNS_G2D_RND_OVERWRITE_PLTTNO_OFFS;
}
void Sprite_SetExplicitPaletteWithOffset(Sprite *sprite, u32 palette)
{
Sprite_SetExplicitPalette(sprite, palette);
sprite->explicitPalette += GetPaletteIndexForProxy(&sprite->paletteProxy, sprite->vramType);
}
u32 Sprite_GetExplicitPalette(const Sprite *sprite)
{
return sprite->explicitPalette;
}
void Sprite_SetExplicitPaletteOffset(Sprite *sprite, u32 paletteOffset)
{
GF_ASSERT(sprite);
sprite->explicitPaletteOffset = paletteOffset;
sprite->overwriteFlags |= NNS_G2D_RND_OVERWRITE_PLTTNO_OFFS;
sprite->overwriteFlags &= ~NNS_G2D_RND_OVERWRITE_PLTTNO;
}
void Sprite_SetExplicitPaletteOffsetAutoAdjust(Sprite *sprite, u32 paletteOffset)
{
Sprite_SetExplicitPaletteOffset(sprite, paletteOffset);
sprite->explicitPaletteOffset += GetPaletteIndexForProxy(&sprite->paletteProxy, sprite->vramType);
}
u32 Sprite_GetExplicitPaletteOffset(const Sprite *sprite)
{
GF_ASSERT(sprite);
return sprite->explicitPaletteOffset;
}
void Sprite_SetPriority(Sprite *sprite, u32 priority)
{
SpriteList *list = sprite->list; // Required to match
sprite->priority = priority;
SpriteList_Remove(sprite);
SpriteList_Insert(list, sprite);
}
u32 Sprite_GetPriority(const Sprite *sprite)
{
return sprite->priority;
}
void Sprite_SetImageProxy(Sprite *sprite, const NNSG2dImageProxy *imageProxy)
{
sprite->imageProxy = *imageProxy;
}
NNSG2dImageProxy *Sprite_GetImageProxy(Sprite *sprite)
{
return &sprite->imageProxy;
}
NNSG2dImagePaletteProxy *Sprite_GetPaletteProxy(Sprite *paletteProxy)
{
return &paletteProxy->paletteProxy;
}
void Sprite_SetMosaicFlag(Sprite *sprite, BOOL mosaic)
{
sprite->explicitMosaic = mosaic;
if (mosaic == TRUE) {
sprite->overwriteFlags |= NNS_G2D_RND_OVERWRITE_MOSAIC;
} else {
sprite->overwriteFlags ^= NNS_G2D_RND_OVERWRITE_MOSAIC;
}
}
NNS_G2D_VRAM_TYPE Sprite_GetVRamType(const Sprite *sprite)
{
return sprite->vramType;
}
BOOL Sprite_IsAnimated(Sprite *sprite)
{
GF_ASSERT(sprite);
if (sprite->type == CELL_ANIM_TYPE_CELL || sprite->type == CELL_ANIM_TYPE_VRAM_CELL) {
CellAnimationData *cellAnim = (CellAnimationData *)&sprite->animData;
return NNS_G2dIsAnimCtrlActive(&cellAnim->anim.animCtrl);
} else {
MultiCellAnimationData *multiCellAnim = (MultiCellAnimationData *)&sprite->animData;
return NNS_G2dIsAnimCtrlActive(&multiCellAnim->anim.animCtrl);
}
}
void Sprite_SetExplicitOAMMode(Sprite *sprite, GXOamMode mode)
{
GF_ASSERT(sprite);
sprite->explicitOamMode = mode;
if (mode == GX_OAM_MODE_NORMAL) {
sprite->overwriteFlags ^= NNS_G2D_RND_OVERWRITE_OBJMODE;
} else {
sprite->overwriteFlags |= NNS_G2D_RND_OVERWRITE_OBJMODE;
}
}
void Utility_Clear2DMainOAM(enum HeapID heapID)
{
GXOamAttr *oam = Heap_Alloc(heapID, sizeof(GXOamAttr) * MAX_SPRITES);
MI_CpuFill16(oam, 0x2C0, sizeof(GXOamAttr) * MAX_SPRITES);
DC_FlushRange(oam, sizeof(GXOamAttr) * MAX_SPRITES);
GX_LoadOAM(oam, 0, sizeof(GXOamAttr) * MAX_SPRITES);
Heap_Free(oam);
}
void Utility_Clear2DSubOAM(enum HeapID heapID)
{
GXOamAttr *oam = Heap_Alloc(heapID, sizeof(GXOamAttr) * MAX_SPRITES);
MI_CpuFill16(oam, 0x2C0, sizeof(GXOamAttr) * MAX_SPRITES);
// According to the NitroSDK docs there should be a call to DC_FlushRange here.
GXS_LoadOAM(oam, 0, sizeof(GXOamAttr) * MAX_SPRITES);
Heap_Free(oam);
}
u32 Sprite_GetUserAttrForAnimFrame(const Sprite *sprite, u32 animID, u32 frame)
{
const NNSG2dAnimBankData *animBank;
if (sprite->type == CELL_ANIM_TYPE_CELL || sprite->type == CELL_ANIM_TYPE_VRAM_CELL) {
CellAnimationData *cellAnim = (CellAnimationData *)&sprite->animData;
animBank = cellAnim->animBank;
} else {
MultiCellAnimationData *multiCellAnim = (MultiCellAnimationData *)&sprite->animData;
animBank = multiCellAnim->animBank;
}
const NNSG2dUserExAnimAttrBank *animAttrBank = NNS_G2dGetUserExAnimAttrBank(animBank);
// Guard clauses don't match here so the nested if's are required
if (animAttrBank) {
const NNSG2dUserExAnimSequenceAttr *animAttr = NNS_G2dGetUserExAnimSequenceAttr(animAttrBank, animID);
if (animAttr) {
const NNSG2dUserExAnimFrameAttr *frameAttr = NNS_G2dGetUserExAnimFrameAttr(animAttr, frame);
if (frameAttr) {
return NNS_G2dGetUserExAnimFrmAttrValue(frameAttr);
}
}
}
return 0;
}
u32 Sprite_GetUserAttrForCurrentAnimFrame(const Sprite *sprite)
{
u32 animID = Sprite_GetActiveAnim(sprite);
u32 frame = Sprite_GetAnimFrame(sprite);
return Sprite_GetUserAttrForAnimFrame(sprite, animID, frame);
}
static BOOL SpriteList_InitSprite(const SpriteList *list,
const SpriteResourcesHeader *resourceData,
Sprite *sprite,
enum HeapID heapID)
{
sprite->type = SpriteResourcesHeader_GetCellType(resourceData);
sprite->imageProxy = *resourceData->imageProxy;
sprite->paletteProxy = *resourceData->paletteProxy;
Sprite_SetCellBank(resourceData->cellBank, sprite);
if (resourceData->cellAnimBank) {
Sprite_SetCellAnimBank(resourceData->cellAnimBank, sprite);
} else {
Sprite_SetCellAnimBank(list->defaultAnimBank, sprite);
}
if (sprite->type == CELL_ANIM_TYPE_MULTI_CELL) {
Sprite_SetMultiCellBank(resourceData->multiCellBank, sprite);
Sprite_SetMultiCellAnimBank(resourceData->multiCellAnimBank, sprite);
Sprite_CreateMultiCellAnim(sprite, heapID);
} else if (sprite->type == CELL_ANIM_TYPE_VRAM_CELL) {
Sprite_CreateVRamCellAnim(resourceData, sprite, heapID);
} else {
Sprite_CreateCellAnim(sprite, heapID);
}
sprite->explicitPriority = resourceData->priority;
return TRUE;
}
static enum CellAnimType SpriteResourcesHeader_GetCellType(const SpriteResourcesHeader *resourceData)
{
if (resourceData->multiCellBank != NULL) {
return CELL_ANIM_TYPE_MULTI_CELL;
}
if (resourceData->isVRamTransfer == TRUE) {
return CELL_ANIM_TYPE_VRAM_CELL;
}
return CELL_ANIM_TYPE_CELL;
}
static void Sprite_SetCellBank(const NNSG2dCellDataBank *cellBank, Sprite *sprite)
{
CellAnimationData *cellAnim = (CellAnimationData *)&sprite->animData;
cellAnim->cellBank = cellBank;
}
static void Sprite_SetCellAnimBank(const NNSG2dCellAnimBankData *cellAnimBank, Sprite *sprite)
{
CellAnimationData *cellAnim = (CellAnimationData *)&sprite->animData;
cellAnim->animBank = cellAnimBank;
}
static void Sprite_SetMultiCellBank(const NNSG2dMultiCellDataBank *multiCellBank, Sprite *sprite)
{
MultiCellAnimationData *multiCell = (MultiCellAnimationData *)&sprite->animData;
multiCell->cellBank = multiCellBank;
}
static void Sprite_SetMultiCellAnimBank(const NNSG2dMultiCellAnimBankData *multiCellAnimBank, Sprite *sprite)
{
MultiCellAnimationData *multiCellAnim = (MultiCellAnimationData *)&sprite->animData;
multiCellAnim->animBank = multiCellAnimBank;
}
static void Sprite_CreateCellAnim(Sprite *sprite, enum HeapID heapID)
{
CellAnimationData *cellAnim = (CellAnimationData *)&sprite->animData;
NNS_G2dInitCellAnimation(
&cellAnim->anim,
NNS_G2dGetAnimSequenceByIdx(cellAnim->animBank, 0),
cellAnim->cellBank);
}
static void Sprite_CreateVRamCellAnim(const SpriteResourcesHeader *resourceData, Sprite *sprite, enum HeapID heapID)
{
VRamCellAnimationData *vramCellAnim = (VRamCellAnimationData *)&sprite->animData;
vramCellAnim->transferHandle = NNS_G2dGetNewCellTransferStateHandle();
const NNSG2dCharacterData *charData = resourceData->charData;
NNS_G2dInitCellAnimationVramTransfered(
&vramCellAnim->anim,
NNS_G2dGetAnimSequenceByIdx(vramCellAnim->animBank, 0),
vramCellAnim->cellBank,
vramCellAnim->transferHandle,
NNS_G2D_VRAM_ADDR_NONE,
NNS_G2dGetImageLocation(&sprite->imageProxy, NNS_G2D_VRAM_TYPE_2DMAIN),
NNS_G2dGetImageLocation(&sprite->imageProxy, NNS_G2D_VRAM_TYPE_2DSUB),
charData->pRawData,
NULL,
charData->szByte);
}
static void Sprite_CreateMultiCellAnim(Sprite *sprite, enum HeapID heapID)
{
MultiCellAnimationData *multiCellAnim = (MultiCellAnimationData *)&sprite->animData;
const NNSG2dMultiCellAnimSequence *animSequence = NNS_G2dGetAnimSequenceByIdx(multiCellAnim->animBank, 0);
u16 maxNodes = NNS_G2dGetMCBankNumNodesRequired(multiCellAnim->cellBank);
multiCellAnim->nodes = Heap_Alloc(heapID, sizeof(NNSG2dNode) * maxNodes);
multiCellAnim->cellAnims = Heap_Alloc(heapID, sizeof(NNSG2dCellAnimation) * maxNodes);
NNS_G2dInitMCAnimation(
&multiCellAnim->anim,
multiCellAnim->nodes,
multiCellAnim->cellAnims,
maxNodes,
multiCellAnim->individualAnimBank,
multiCellAnim->individualCellBank,
multiCellAnim->cellBank);
NNS_G2dSetAnimSequenceToMCAnimation(
&multiCellAnim->anim,
animSequence);
}
static u32 GetPaletteIndexForProxy(const NNSG2dImagePaletteProxy *paletteProxy, u32 vramType)
{
u32 paletteSize;
if (paletteProxy->bExtendedPlt) {
paletteSize = 32 * 16;
} else {
if (paletteProxy->fmt == GX_TEXFMT_PLTT256) {
paletteSize = 0;
} else {
paletteSize = 32;
}
}
return paletteSize != 0
? NNS_G2dGetImagePaletteLocation(paletteProxy, vramType) / paletteSize
: 0;
}
static void SpriteList_DrawSprite(const SpriteList *list, Sprite *sprite)
{
VecFx32 pos = sprite->position;
NNS_G2dSetRendererImageProxy(list->renderer, &sprite->imageProxy, &sprite->paletteProxy);
NNS_G2dBeginRendering(list->renderer);
NNS_G2dPushMtx();
NNS_G2dSetRndCoreAffineOverwriteMode(&list->renderer->rendererCore, sprite->affineOverwriteMode);
if (sprite->affineOverwriteMode == NNS_G2D_RND_AFFINE_OVERWRITE_NONE) {
NNS_G2dSetRndCoreFlipMode(
&list->renderer->rendererCore,
sprite->flip & NNS_G2D_RENDERERFLIP_H,
sprite->flip & NNS_G2D_RENDERERFLIP_V);
} else {
NNS_G2dSetRndCoreFlipMode(&list->renderer->rendererCore, FALSE, FALSE);
}
NNS_G2dTranslate(pos.x, pos.y, pos.z);
if (sprite->affineOverwriteMode != NNS_G2D_RND_AFFINE_OVERWRITE_NONE) {
NNS_G2dTranslate(sprite->affineTranslation.x, sprite->affineTranslation.y, sprite->affineTranslation.z);
NNS_G2dScale(sprite->affineScale.x, sprite->affineScale.y, sprite->affineScale.z);
NNS_G2dRotZ(FX_SinIdx(sprite->affineZRotation), FX_CosIdx(sprite->affineZRotation));
// affineTranslation only serves as a pivot point for rotation and scaling
// so we undo this translation after applying these transformations.
NNS_G2dTranslate(-sprite->affineTranslation.x, -sprite->affineTranslation.y, -sprite->affineTranslation.z);
}
// Set the overwrite parameters
// We always want the actual overwrite flags to be equal to overwriteParam
// so we set the flags that are not in overwriteParam to 0 with the second call
NNS_G2dSetRendererOverwriteEnable(list->renderer, sprite->overwriteFlags);
NNS_G2dSetRendererOverwriteDisable(list->renderer, ~sprite->overwriteFlags);
NNS_G2dSetRendererOverwritePlttNo(list->renderer, sprite->explicitPalette);
NNS_G2dSetRendererOverwritePlttNoOffset(list->renderer, sprite->explicitPaletteOffset);
NNS_G2dSetRendererOverwriteMosaicFlag(list->renderer, sprite->explicitMosaic);
NNS_G2dSetRendererOverwriteOBJMode(list->renderer, sprite->explicitOamMode);
NNS_G2dSetRendererOverwritePriority(list->renderer, sprite->explicitPriority);
if (sprite->type == CELL_ANIM_TYPE_CELL || sprite->type == CELL_ANIM_TYPE_VRAM_CELL) {
CellAnimationData *cellAnim = (CellAnimationData *)&sprite->animData;
NNS_G2dDrawCellAnimation(&cellAnim->anim);
} else {
MultiCellAnimationData *multiCellAnim = (MultiCellAnimationData *)&sprite->animData;
NNS_G2dDrawMultiCellAnimation(&multiCellAnim->anim);
}
NNS_G2dPopMtx();
NNS_G2dEndRendering();
}
static void SpriteList_DrawSprite_Stub(const SpriteList *list, Sprite *sprite)
{
return;
}
static void Sprite_UpdateAnimInternal(Sprite *sprite)
{
Sprite_UpdateAnim(sprite, sprite->animSpeed);
}
static void Sprite_UpdateAnimInternal_Stub(Sprite *sprite)
{
return;
}
static void SpriteList_Insert(SpriteList *list, Sprite *sprite)
{
// If the list is empty, insert the sprite as the only sprite
if (list->sentinelData.next == &list->sentinelData) {
list->sentinelData.next = sprite;
list->sentinelData.prev = sprite;
sprite->prev = &list->sentinelData;
sprite->next = &list->sentinelData;
return;
}
// Check if the sprite should be inserted at the end of the list (lowest priority)
if (list->sentinelData.prev->priority <= sprite->priority) {
sprite->prev = list->sentinelData.prev;
list->sentinelData.prev->next = sprite;
sprite->next = &list->sentinelData;
list->sentinelData.prev = sprite;
return;
}
// Insert the sprite in the correct position according to its priority
Sprite *cur = list->sentinelData.next;
while (cur != &list->sentinelData) {
if (cur->priority > sprite->priority) {
cur->prev->next = sprite;
sprite->prev = cur->prev;
cur->prev = sprite;
sprite->next = cur;
break;
}
cur = cur->next;
}
}
static void SpriteList_Remove(Sprite *sprite)
{
sprite->prev->next = sprite->next;
sprite->next->prev = sprite->prev;
}
static void SpriteList_InitSprites(SpriteList *list)
{
for (int i = 0; i < list->capacity; i++) {
Sprite_Reset(&list->sprites[i]);
list->freeSprites[i] = list->sprites + i;
}
list->freeSpriteHead = 0;
}
static Sprite *SpriteList_AllocSprite(SpriteList *list)
{
if (list->freeSpriteHead >= list->capacity) {
return NULL;
}
Sprite *sprite = list->freeSprites[list->freeSpriteHead];
list->freeSpriteHead++;
return sprite;
}
static BOOL SpriteList_FreeSprite(SpriteList *list, Sprite *sprite)
{
if (list->freeSpriteHead <= 0) {
return FALSE;
}
Sprite_Reset(sprite);
list->freeSpriteHead--;
list->freeSprites[list->freeSpriteHead] = sprite;
return TRUE;
}