diff --git a/asm/code_2.s b/asm/code_2.s index 246dd2cf..9f609b98 100644 --- a/asm/code_2.s +++ b/asm/code_2.s @@ -33426,8 +33426,8 @@ _080ABEB6: adds r1, #0x20 str r1, [r7, #0x10] movs r4, #0 - ldr r1, _080AC028 @ =0x00000462 - mov sb, r1 + ldr r1, _080AC028 @ =0x00000462 @ ANIM_ASCII + mov sb, r1 @ sb = r1 = ANIM_ASCII mov r1, sb strh r1, [r0, #0xc] movs r1, #0xe @@ -33464,7 +33464,7 @@ _080ABEB6: ldr r1, [r7, #0x10] adds r1, #0x40 str r1, [r7, #0x10] - mov r1, sb + mov r1, sb @ r1 = sb = ANIM_ASCII strh r1, [r0, #0xc] movs r1, #6 strb r1, [r0, #0x1a] @@ -33496,7 +33496,7 @@ _080ABEB6: ldr r1, [r7, #0x10] adds r1, #0x40 str r1, [r7, #0x10] - mov r1, sb + mov r1, sb @ r1 = sb = ANIM_ASCII strh r1, [r0, #0xc] movs r1, #8 strb r1, [r0, #0x1a] @@ -33528,7 +33528,7 @@ _080ABEB6: ldr r1, [r7, #0x10] adds r1, #0x40 str r1, [r7, #0x10] - mov r1, sb + mov r1, sb @ r1 = sb = ANIM_ASCII strh r1, [r0, #0xc] movs r1, #9 strb r1, [r0, #0x1a] @@ -33560,7 +33560,7 @@ _080ABEB6: ldr r1, [r7, #0x10] adds r1, #0x40 str r1, [r7, #0x10] - mov r1, sb + mov r1, sb @ r1 = sb = ANIM_ASCII strh r1, [r0, #0xc] movs r1, #0xd strb r1, [r0, #0x1a] diff --git a/include/animation_commands.h b/include/animation_commands.h index eb700650..86b1e052 100644 --- a/include/animation_commands.h +++ b/include/animation_commands.h @@ -9,7 +9,7 @@ typedef AnimCmdResult (*AnimationCommandFunc)(void *cursor, Sprite *sprite); typedef struct { - /* 0x00 */ s32 cmdId; // -2 + /* 0x00 */ s32 cmdId; // -1 // Note(Jace): This needs to be signed, since a // negative value infers that it's using 8bit-colors diff --git a/include/constants/animations.h b/include/constants/animations.h index b65cac92..1b538142 100644 --- a/include/constants/animations.h +++ b/include/constants/animations.h @@ -233,6 +233,12 @@ #define ANIM_ASCII 1122 +#define ANIM_ASCII_FIRST_CHAR '!' + +#define ANIM_ASCII_YEN '\\' +#define ANIM_ASCII_CHAR(c) ((c)-ANIM_ASCII_FIRST_CHAR + 1) +#define ANIM_NUM_ASCII_CHARS 94 + #define ANIM_BONUS_CAPSULE 1154 #define ANIM_BONUS_CAPSULE_POINTS 1155 #define ANIM_BONUS_CAPSULE_SWITCH 1156 @@ -252,10 +258,10 @@ #define ANIM_CONDOR_PROJ 1170 #define ANIM_APE 1171 -#define ANIM_FROG 1173 -#define ANIM_MINIMOLE 1174 -#define ANIM_BUZZER 1175 - +#define ANIM_FROG 1173 +#define ANIM_MINIMOLE 1174 +#define ANIM_BUZZER 1175 +#define ANIM_BUZZER_PROJ 1176 #define ANIM_JOUSUN 1177 /* Green Plane */ #define ANIM_TAKKON 1178 /* Red Octopus */ #define ANIM_UUTSUBO 1179 /* Yellow Eel */ diff --git a/include/sprite.h b/include/sprite.h index 2b7747a4..1284eae5 100644 --- a/include/sprite.h +++ b/include/sprite.h @@ -414,7 +414,7 @@ extern u8 gNextFreeAffineIndex; // related to Sprite.frameFlags _sprite->frameFlags = (SPRITE_FLAG(PRIORITY, _priority) | (_flags)); #define SPRITE_INIT(_sprite, _numTiles, _anim, _variant, _order, _priority) \ - _sprite->tiles = VramMalloc(_numTiles); \ + _sprite->tiles = VramMalloc(_numTiles); \ SPRITE_INIT_WITHOUT_VRAM(_sprite, _anim, _variant, _order, _priority, 0); #define SF_SHIFT(name) (SPRITE_FLAG_SHIFT_##name) diff --git a/ldscript.txt b/ldscript.txt index 50b67bcb..0e1562c0 100644 --- a/ldscript.txt +++ b/ldscript.txt @@ -169,6 +169,9 @@ SECTIONS { src/game/math.o(.text); asm/special_stage.o(.text); src/game/partner_ai.o(.text); +#if DEBUG + src/debug/animation_ed.o +#endif src/lib/m4a/m4a0.o(.text); src/lib/m4a/m4a.o(.text); src/core.o(.text); diff --git a/src/debug/animation_ed.c b/src/debug/animation_ed.c new file mode 100644 index 00000000..cd34c9e7 --- /dev/null +++ b/src/debug/animation_ed.c @@ -0,0 +1,252 @@ +#include "global.h" +#include "core.h" +#include "sprite.h" +#include "malloc_vram.h" +#include "data/sprite_tables.h" +#include "constants/animations.h" +#include "constants/anim_commands.h" +#include "constants/tilemaps.h" +#include "animation_commands.h" + +typedef struct { + Sprite sprPreview; + Sprite sprIndicator; + Background bg; + s32 frameCount; +} AnimationEd; + +void Task_AnimEd(); +void HandleInput(AnimationEd *ed); +void DrawCommands(AnimationEd *ed); +void SkipCommand(const ACmd **pCursor); +s32 GetCurrentFrameIndex(Sprite *s); +void DrawEndOrLoopCommand(AnimationEd *ed); +void TaskDestructor_AnimEd(struct Task *t); +s32 CountFrames(AnimationEd *ed); + +#define CMD_COUNT 13 +#define CMD_MARGIN 8 +#define CMD_WINDOW_Y (DISPLAY_HEIGHT - (CMD_COUNT * CMD_MARGIN)) +#define CMD_TILES_Y (CMD_WINDOW_Y + CMD_MARGIN) + +s8 cmdSizes[CMD_COUNT] = { + AnimCommandSizeInWords(ACmd_GetTiles), AnimCommandSizeInWords(ACmd_GetPalette), + AnimCommandSizeInWords(ACmd_JumpBack), AnimCommandSizeInWords(ACmd_4), + AnimCommandSizeInWords(ACmd_PlaySoundEffect), AnimCommandSizeInWords(ACmd_Hitbox), + AnimCommandSizeInWords(ACmd_TranslateSprite), AnimCommandSizeInWords(ACmd_8), + AnimCommandSizeInWords(ACmd_SetIdAndVariant), AnimCommandSizeInWords(ACmd_10), + AnimCommandSizeInWords(ACmd_SetSpritePriority), AnimCommandSizeInWords(ACmd_SetOamOrder), + AnimCommandSizeInWords(ACmd_ShowFrame), +}; + +/* NOTE(Jace): This is still missing a lot of features, but right now it's more important to focus on removing ASM */ + +void CreateAnimationEd(void) +{ + AnimationEd *ed = TASK_DATA(TaskCreate(Task_AnimEd, sizeof(AnimationEd), 0x2000, 0, TaskDestructor_AnimEd)); + Sprite *s = &ed->sprPreview; + Sprite *sprIndicator = &ed->sprIndicator; + Background *bgA = &ed->bg; + + gDispCnt |= DISPCNT_BG0_ON; +#if 0 + gBgCntRegs[0] = BGCNT_256COLOR | BGCNT_AFF256x256 | BGCNT_SCREENBASE(14) | BGCNT_CHARBASE(1) | BGCNT_PRIORITY(3); + gBgScrollRegs[0][0] = 0; + gBgScrollRegs[0][1] = 0; + bgA->graphics.dest = (void *)BG_SCREEN_ADDR(8); + bgA->graphics.anim = 0; + bgA->layoutVram = (void *)BG_SCREEN_ADDR(14); + bgA->unk18 = 0; + bgA->unk1A = 0; + bgA->tilemapId = 434; + bgA->unk1E = 0; + bgA->unk20 = 0; + bgA->unk22 = 0; + bgA->unk24 = 0; + bgA->targetTilesX = 32; + bgA->targetTilesY = 32; + bgA->paletteOffset = 0; + bgA->flags = 0; + DrawBackground(bgA); +#endif + + { + AnimId anim = ANIM_ASCII; + u8 pattern = 0; + SPRITE_INIT_WITHOUT_VRAM(s, anim, pattern, 0, 2, 0); + s->tiles = OBJ_VRAM0; + s->x = DISPLAY_CENTER_X + 32; + s->y = 32; + UpdateSpriteAnimation(s); + DisplaySprite(s); + + ed->frameCount = CountFrames(ed); + } + + { + const s32 indicatorSize = 4; + AnimId anim = ANIM_BUZZER_PROJ; + u8 pattern = 0; + sprIndicator->palId = 2; + SPRITE_INIT(sprIndicator, indicatorSize, anim, pattern, 3, 3); + UpdateSpriteAnimation(sprIndicator); + } +} + +void Task_AnimEd() +{ + AnimationEd *ed = TASK_DATA(gCurTask); + Sprite *s = &ed->sprPreview; + Sprite *sprIndicator = &ed->sprIndicator; + + HandleInput(ed); + + if (gSpriteTables.animations[s->anim] == NULL) { + // Render "EMPTY" string + } else { + UpdateSpriteAnimation(s); + DisplaySprite(s); + + DrawCommands(ed); + } +} + +void HandleInput(AnimationEd *ed) +{ + Sprite *s = &ed->sprPreview; + + if (gInput & L_BUTTON) { + AnimId newAnim = (s->anim - 1); + + if (newAnim == (AnimId)-1) { + newAnim = ANIM_COUNT - 1; + } + + s->anim = newAnim; + } else if (gInput & R_BUTTON) { + AnimId newAnim = (s->anim + 1) % ANIM_COUNT; + + s->anim = newAnim; + } + + if (gInput & (L_BUTTON | R_BUTTON)) { + ed->frameCount = CountFrames(ed); + } +} + +#define IS_LAST_CMD(_cmd) (((_cmd)->id == ANIM_CMD__END) || ((_cmd)->id == ANIM_CMD__JUMP_BACK)) +#define IS_DISPLAY_CMD(_cmd) ((_cmd)->id >= 0) + +// TODO: Only call this when sprPreview anim changed. +s32 CountFrames(AnimationEd *ed) +{ + Sprite *sprPreview = &ed->sprPreview; + const ACmd **patterns = gSpriteTables.animations[sprPreview->anim]; + s32 frameCount = 0; + + if (patterns != NULL) { + const ACmd *cursor = patterns[sprPreview->variant]; + + while (!IS_LAST_CMD(cursor)) { + if (IS_DISPLAY_CMD(cursor)) { + frameCount++; + } + + SkipCommand(&cursor); + } + } + + return frameCount; +} + +void SkipCommand(const ACmd **pCursor) +{ + s32 *cmd = (s32 *)(*pCursor); + s32 dummy; + + if ((*pCursor)->id < 0) { + cmd += cmdSizes[~((*pCursor)->id)]; + } else { + cmd += cmdSizes[~ANIM_CMD__SHOW_FRAME]; + } + + *pCursor = (const ACmd *)cmd; +} + +void DrawCommands(AnimationEd *ed) +{ + Sprite *sprPreview = &ed->sprPreview; + Sprite *sprIndicator = &ed->sprIndicator; + const ACmd **patterns = gSpriteTables.animations[sprPreview->anim]; + + if (patterns != NULL) { + const ACmd *cursorFirst = patterns[sprPreview->variant]; + const ACmd *cursor = cursorFirst; + + s32 frame = 0; + s32 currentFrame = GetCurrentFrameIndex(sprPreview); + s32 frameCount = ed->frameCount; + s32 margin = 10; + float invFrame = 1. / (float)frameCount; + + while (!IS_LAST_CMD(cursor)) { + float normalizedX = ((float)frame) * invFrame; + sprIndicator->x = margin + (s32)((float)(DISPLAY_WIDTH - 2 * margin) * normalizedX); + + while (!IS_DISPLAY_CMD(cursor)) { + sprIndicator->y = CMD_TILES_Y + (~cursor->id) * (2 * CMD_MARGIN); + DisplaySprite(sprIndicator); + SkipCommand(&cursor); + } + + if (frame == currentFrame) { + sprIndicator->y = CMD_TILES_Y - CMD_MARGIN; + DisplaySprite(sprIndicator); + } + + frame++; + // Skip DISPLAY command + SkipCommand(&cursor); + } + + DrawEndOrLoopCommand(ed); + } +} + +s32 GetCurrentFrameIndex(Sprite *s) +{ + s32 frameIndex = 0; + const ACmd **patterns = gSpriteTables.animations[s->anim]; + + if (patterns != NULL) { + const ACmd *cursorFirst = patterns[s->variant]; + const ACmd *cursor = cursorFirst; + s32 i; + + for (i = 0; i < s->animCursor;) { + if (cursor->id < 0) { + SkipCommand(&cursor); + } else { + frameIndex++; + SkipCommand(&cursor); + } + + if (cursor->id < 0) { + i += cmdSizes[~cursor->id]; + } else { + i += cmdSizes[~ANIM_CMD__SHOW_FRAME]; + } + } + + if (frameIndex > 0) // this *should* always be true. + { + frameIndex--; + } + } + + return frameIndex; +} + +void DrawEndOrLoopCommand(AnimationEd *ed) { } + +void TaskDestructor_AnimEd(struct Task *t) { } \ No newline at end of file diff --git a/src/game/game.c b/src/game/game.c index 2ad78a89..44028a8e 100644 --- a/src/game/game.c +++ b/src/game/game.c @@ -73,6 +73,7 @@ void sub_80001EC(void) } } +extern void CreateAnimationEd(void); void Task_8000284(void) { DmaFill32(3, 0, BG_CHAR_ADDR_FROM_BGCNT(2), 2 * TILE_SIZE_4BPP); @@ -86,7 +87,11 @@ void Task_8000284(void) gStageData.timer = 0; sub_80003B8(); #if DEBUG - CreateCharacterSelect(0); + if (REG_KEYINPUT & SELECT_BUTTON) { + CreateAnimationEd(); + } else { + CreateCharacterSelect(0); + } #else CreateGameIntroState(1); #endif diff --git a/src/sprite.c b/src/sprite.c index 3856350a..1014da81 100644 --- a/src/sprite.c +++ b/src/sprite.c @@ -685,9 +685,15 @@ void DisplaySprite(Sprite *s) // Default behavior from SA1 / SA2 dimensions = (const SpriteOffset2 *)&((const SpriteOffset *)gRefSpriteTables->dimensions[s->anim])[s->frameNum]; } else { +#ifndef NON_MATCHING // TODO: WTF!?!? dimensions = &((const SpriteOffset2 *)gRefSpriteTables->dimensions[s->anim])[s->frameNum]; +#else + // NOTE(Jace): Using the regular / SA1|SA2 code is the only way to make this not crash. Idk what is going on... + dimensions = (const SpriteOffset2 *)&((const SpriteOffset *)gRefSpriteTables->dimensions[s->anim])[s->frameNum]; +#endif } + s->numSubFrames = (u8)dimensions->base.numSubframes; x = s->x; y = s->y;