#include "global.h" #include "decompress.h" #include "dynamic_placeholder_text_util.h" #include "graphics.h" #include "international_string_util.h" #include "pokenav.h" #include "sound.h" #include "sprite.h" #include "string_util.h" #include "strings.h" #include "text.h" #include "trainer_pokemon_sprites.h" #include "window.h" #include "constants/songs.h" enum { RIBBONS_SUMMARY_FUNC_NONE, RIBBONS_SUMMARY_FUNC_SWITCH_MONS, RIBBONS_SUMMARY_FUNC_SELECT_RIBBON, RIBBONS_SUMMARY_FUNC_EXPANDED_CURSOR_MOVE, RIBBONS_SUMMARY_FUNC_EXPANDED_CANCEL, RIBBONS_SUMMARY_FUNC_EXIT, }; #define GFXTAG_RIBBON_ICONS_BIG 9 #define PALTAG_RIBBON_ICONS_1 15 #define PALTAG_RIBBON_ICONS_2 16 #define PALTAG_RIBBON_ICONS_3 17 #define PALTAG_RIBBON_ICONS_4 18 #define PALTAG_RIBBON_ICONS_5 19 #define RIBBONS_PER_ROW 9 #define GIFT_RIBBON_ROW (1 + (FIRST_GIFT_RIBBON / RIBBONS_PER_ROW)) // Gift ribbons start on a new row after the normal ribbons. #define GIFT_RIBBON_START_POS (RIBBONS_PER_ROW * GIFT_RIBBON_ROW) #define MON_SPRITE_X_ON 40 #define MON_SPRITE_X_OFF -32 #define MON_SPRITE_Y 104 struct Pokenav_RibbonsSummaryList { u8 unused1[8]; struct PokenavMonList *monList; u16 selectedPos; u16 normalRibbonLastRowStart; u16 numNormalRibbons; u16 numGiftRibbons; u32 ribbonIds[FIRST_GIFT_RIBBON]; u32 giftRibbonIds[NUM_GIFT_RIBBONS]; u32 unused2; u32 (*callback)(struct Pokenav_RibbonsSummaryList *); }; struct Pokenav_RibbonsSummaryMenu { u32 (*callback)(void); u32 loopedTaskId; u16 nameWindowId; u16 ribbonCountWindowId; u16 listIdxWindowId; u16 unusedWindowId; u16 monSpriteId; struct Sprite *bigRibbonSprite; u32 unused; u8 tilemapBuffers[2][BG_SCREEN_SIZE]; }; // Used for the initial drawing of the ribbons static u32 sRibbonDraw_Total; static u32 sRibbonDraw_Current; static void PrintCurrentMonRibbonCount(struct Pokenav_RibbonsSummaryMenu *); static void PrintRibbbonsSummaryMonInfo(struct Pokenav_RibbonsSummaryMenu *); static void PrintRibbonsMonListIndex(struct Pokenav_RibbonsSummaryMenu *); static void ZoomOutSelectedRibbon(struct Pokenav_RibbonsSummaryMenu *); static void UpdateAndZoomInSelectedRibbon(struct Pokenav_RibbonsSummaryMenu *); static void PrintRibbonNameAndDescription(struct Pokenav_RibbonsSummaryMenu *); static void ResetSpritesAndDrawMonFrontPic(struct Pokenav_RibbonsSummaryMenu *); static void AddRibbonListIndexWindow(struct Pokenav_RibbonsSummaryMenu *); static void DestroyRibbonsMonFrontPic(struct Pokenav_RibbonsSummaryMenu *); static void SlideMonSpriteOff(struct Pokenav_RibbonsSummaryMenu *); static void SlideMonSpriteOn(struct Pokenav_RibbonsSummaryMenu *); static void AddRibbonCountWindow(struct Pokenav_RibbonsSummaryMenu *); static void CreateBigRibbonSprite(struct Pokenav_RibbonsSummaryMenu *); static void AddRibbonSummaryMonNameWindow(struct Pokenav_RibbonsSummaryMenu *); static void DrawAllRibbonsSmall(struct Pokenav_RibbonsSummaryMenu *); static bool32 IsRibbonAnimating(struct Pokenav_RibbonsSummaryMenu *); static bool32 IsMonSpriteAnimating(struct Pokenav_RibbonsSummaryMenu *); static void GetMonRibbons(struct Pokenav_RibbonsSummaryList *); static u32 HandleExpandedRibbonInput(struct Pokenav_RibbonsSummaryList *); static u32 RibbonsSummaryHandleInput(struct Pokenav_RibbonsSummaryList *); static u32 ReturnToRibbonsListFromSummary(struct Pokenav_RibbonsSummaryList *); static bool32 TrySelectRibbonUp(struct Pokenav_RibbonsSummaryList *); static bool32 TrySelectRibbonRight(struct Pokenav_RibbonsSummaryList *); static bool32 TrySelectRibbonLeft(struct Pokenav_RibbonsSummaryList *); static bool32 TrySelectRibbonDown(struct Pokenav_RibbonsSummaryList *); static bool32 GetCurrentLoopedTaskActive(void); static u32 GetRibbonsSummaryCurrentIndex(void); static u32 GetRibbonsSummaryMonListCount(void); static u16 DrawRibbonsMonFrontPic(s32, s32); static void StartMonSpriteSlide(struct Sprite *, s32, s32, s32); static void SpriteCB_MonSpriteSlide(struct Sprite *); static void ClearRibbonsSummaryBg(void); static void BufferSmallRibbonGfxData(u16 *, u32); static void DrawRibbonSmall(u32, u32); static void SpriteCB_WaitForRibbonAnimation(struct Sprite *); static u32 LoopedTask_OpenRibbonsSummaryMenu(s32); static u32 LoopedTask_SwitchRibbonsSummaryMon(s32); static u32 LoopedTask_ExpandSelectedRibbon(s32); static u32 LoopedTask_MoveRibbonsCursorExpanded(s32); static u32 LoopedTask_ShrinkExpandedRibbon(s32); static u32 LoopedTask_ExitRibbonsSummaryMenu(s32); struct { u8 numBits; // The number of bits needed to represent numRibbons u8 numRibbons; // Never read. The contest ribbons have 4 (1 for each rank), the rest are just 1 ribbon u8 ribbonId; bool8 isGiftRibbon; } static const sRibbonData[] = { {1, 1, CHAMPION_RIBBON, FALSE}, {3, 4, COOL_RIBBON_NORMAL, FALSE}, {3, 4, BEAUTY_RIBBON_NORMAL, FALSE}, {3, 4, CUTE_RIBBON_NORMAL, FALSE}, {3, 4, SMART_RIBBON_NORMAL, FALSE}, {3, 4, TOUGH_RIBBON_NORMAL, FALSE}, {1, 1, WINNING_RIBBON, FALSE}, {1, 1, VICTORY_RIBBON, FALSE}, {1, 1, ARTIST_RIBBON, FALSE}, {1, 1, EFFORT_RIBBON, FALSE}, {1, 1, MARINE_RIBBON, TRUE}, {1, 1, LAND_RIBBON, TRUE}, {1, 1, SKY_RIBBON, TRUE}, {1, 1, COUNTRY_RIBBON, TRUE}, {1, 1, NATIONAL_RIBBON, TRUE}, {1, 1, EARTH_RIBBON, TRUE}, {1, 1, WORLD_RIBBON, TRUE} }; #include "data/text/ribbon_descriptions.h" #include "data/text/gift_ribbon_descriptions.h" static const u16 sRibbonIcons1_Pal[] = INCBIN_U16("graphics/pokenav/ribbons/icons1.gbapal"); static const u16 sRibbonIcons2_Pal[] = INCBIN_U16("graphics/pokenav/ribbons/icons2.gbapal"); static const u16 sRibbonIcons3_Pal[] = INCBIN_U16("graphics/pokenav/ribbons/icons3.gbapal"); static const u16 sRibbonIcons4_Pal[] = INCBIN_U16("graphics/pokenav/ribbons/icons4.gbapal"); static const u16 sRibbonIcons5_Pal[] = INCBIN_U16("graphics/pokenav/ribbons/icons5.gbapal"); static const u16 sMonInfo_Pal[] = INCBIN_U16("graphics/pokenav/ribbons/mon_info.gbapal"); // palette for Pokémon's name/gender/level text static const u32 sRibbonIconsSmall_Gfx[] = INCBIN_U32("graphics/pokenav/ribbons/icons.4bpp.lz"); static const u32 sRibbonIconsBig_Gfx[] = INCBIN_U32("graphics/pokenav/ribbons/icons_big.4bpp.lz"); static const struct BgTemplate sBgTemplates[] = { { .bg = 1, .charBaseIndex = 3, .mapBaseIndex = 0x07, .screenSize = 0, .paletteMode = 0, .priority = 1, .baseTile = 0 }, { .bg = 2, .charBaseIndex = 1, .mapBaseIndex = 0x06, .screenSize = 0, .paletteMode = 0, .priority = 2, .baseTile = 0 } }; static const LoopedTask sRibbonsSummaryMenuLoopTaskFuncs[] = { [RIBBONS_SUMMARY_FUNC_NONE] = NULL, [RIBBONS_SUMMARY_FUNC_SWITCH_MONS] = LoopedTask_SwitchRibbonsSummaryMon, [RIBBONS_SUMMARY_FUNC_SELECT_RIBBON] = LoopedTask_ExpandSelectedRibbon, [RIBBONS_SUMMARY_FUNC_EXPANDED_CURSOR_MOVE] = LoopedTask_MoveRibbonsCursorExpanded, [RIBBONS_SUMMARY_FUNC_EXPANDED_CANCEL] = LoopedTask_ShrinkExpandedRibbon, [RIBBONS_SUMMARY_FUNC_EXIT] = LoopedTask_ExitRibbonsSummaryMenu }; bool32 PokenavCallback_Init_RibbonsSummaryMenu(void) { struct Pokenav_RibbonsSummaryList *list = AllocSubstruct(POKENAV_SUBSTRUCT_RIBBONS_SUMMARY_LIST, sizeof(struct Pokenav_RibbonsSummaryList)); if (list == NULL) return FALSE; list->monList = GetSubstructPtr(POKENAV_SUBSTRUCT_MON_LIST); if (list->monList == NULL) return FALSE; GetMonRibbons(list); list->callback = RibbonsSummaryHandleInput; gKeyRepeatContinueDelay = 3; gKeyRepeatStartDelay = 10; return TRUE; } u32 GetRibbonsSummaryMenuCallback(void) { struct Pokenav_RibbonsSummaryList *list = GetSubstructPtr(POKENAV_SUBSTRUCT_RIBBONS_SUMMARY_LIST); return list->callback(list); } void FreeRibbonsSummaryScreen1(void) { FreePokenavSubstruct(POKENAV_SUBSTRUCT_RIBBONS_SUMMARY_LIST); } // Handles input when a specific ribbon is not currently selected static u32 RibbonsSummaryHandleInput(struct Pokenav_RibbonsSummaryList *list) { // Handle Up/Down movement to select a new Pokémon to show ribbons for if (JOY_REPEAT(DPAD_UP) && list->monList->currIndex != 0) { list->monList->currIndex--; list->selectedPos = 0; GetMonRibbons(list); return RIBBONS_SUMMARY_FUNC_SWITCH_MONS; } if (JOY_REPEAT(DPAD_DOWN) && list->monList->currIndex < list->monList->listCount - 1) { list->monList->currIndex++; list->selectedPos = 0; GetMonRibbons(list); return RIBBONS_SUMMARY_FUNC_SWITCH_MONS; } if (JOY_NEW(A_BUTTON)) { // Enter ribbon selection list->callback = HandleExpandedRibbonInput; return RIBBONS_SUMMARY_FUNC_SELECT_RIBBON; } if (JOY_NEW(B_BUTTON)) { // Exit ribbon summary menu list->callback = ReturnToRibbonsListFromSummary; return RIBBONS_SUMMARY_FUNC_EXIT; } return RIBBONS_SUMMARY_FUNC_NONE; } // Handles input when a ribbon is selected static u32 HandleExpandedRibbonInput(struct Pokenav_RibbonsSummaryList *list) { // Handle movement while a ribbon is selected if (JOY_REPEAT(DPAD_UP) && TrySelectRibbonUp(list)) return RIBBONS_SUMMARY_FUNC_EXPANDED_CURSOR_MOVE; if (JOY_REPEAT(DPAD_DOWN) && TrySelectRibbonDown(list)) return RIBBONS_SUMMARY_FUNC_EXPANDED_CURSOR_MOVE; if (JOY_REPEAT(DPAD_LEFT) && TrySelectRibbonLeft(list)) return RIBBONS_SUMMARY_FUNC_EXPANDED_CURSOR_MOVE; if (JOY_REPEAT(DPAD_RIGHT) && TrySelectRibbonRight(list)) return RIBBONS_SUMMARY_FUNC_EXPANDED_CURSOR_MOVE; if (JOY_NEW(B_BUTTON)) { // Exit ribbon selection list->callback = RibbonsSummaryHandleInput; return RIBBONS_SUMMARY_FUNC_EXPANDED_CANCEL; } return RIBBONS_SUMMARY_FUNC_NONE; } static u32 ReturnToRibbonsListFromSummary(struct Pokenav_RibbonsSummaryList *list) { return POKENAV_RIBBONS_RETURN_TO_MON_LIST; } static bool32 TrySelectRibbonUp(struct Pokenav_RibbonsSummaryList *list) { if (list->selectedPos < FIRST_GIFT_RIBBON) { // In normal ribbons, try to move up a row if (list->selectedPos < RIBBONS_PER_ROW) return FALSE; list->selectedPos -= RIBBONS_PER_ROW; return TRUE; } if (list->numNormalRibbons != 0) { // In gift ribbons, try to move up into normal ribbons // If there's > 1 row of gift ribbons (not normally possible) // it's impossible to move up between them u32 ribbonPos = list->selectedPos - GIFT_RIBBON_START_POS; list->selectedPos = ribbonPos + list->normalRibbonLastRowStart; if (list->selectedPos >= list->numNormalRibbons) list->selectedPos = list->numNormalRibbons - 1; return TRUE; } return FALSE; } static bool32 TrySelectRibbonDown(struct Pokenav_RibbonsSummaryList *list) { if (list->selectedPos >= FIRST_GIFT_RIBBON) return FALSE; if (list->selectedPos < list->normalRibbonLastRowStart) { // Not in last row of normal ribbons, advance to next row list->selectedPos += RIBBONS_PER_ROW; if (list->selectedPos >= list->numNormalRibbons) list->selectedPos = list->numNormalRibbons - 1; return TRUE; } if (list->numGiftRibbons != 0) { // In/beyond last of row of normal ribbons and gift ribbons present, move down to gift ribbon row int ribbonPos = list->selectedPos - list->normalRibbonLastRowStart; if (ribbonPos >= list->numGiftRibbons) ribbonPos = list->numGiftRibbons - 1; list->selectedPos = ribbonPos + GIFT_RIBBON_START_POS; return TRUE; } return FALSE; } static bool32 TrySelectRibbonLeft(struct Pokenav_RibbonsSummaryList *list) { u16 column = list->selectedPos % RIBBONS_PER_ROW; if (column != 0) { list->selectedPos--; return TRUE; } return FALSE; } static bool32 TrySelectRibbonRight(struct Pokenav_RibbonsSummaryList *list) { int column = list->selectedPos % RIBBONS_PER_ROW; if (column >= RIBBONS_PER_ROW - 1) return FALSE; if (list->selectedPos < GIFT_RIBBON_START_POS) { // Move right in normal ribbon row if (list->selectedPos < list->numNormalRibbons - 1) { list->selectedPos++; return TRUE; } } else { // Move right in gift ribbon row if (column < list->numGiftRibbons - 1) { list->selectedPos++; return TRUE; } } return FALSE; } static u32 GetRibbonsSummaryCurrentIndex(void) { struct Pokenav_RibbonsSummaryList *list = GetSubstructPtr(POKENAV_SUBSTRUCT_RIBBONS_SUMMARY_LIST); return list->monList->currIndex; } static u32 GetRibbonsSummaryMonListCount(void) { struct Pokenav_RibbonsSummaryList *list = GetSubstructPtr(POKENAV_SUBSTRUCT_RIBBONS_SUMMARY_LIST); return list->monList->listCount; } static void GetMonNicknameLevelGender(u8 *nick, u8 *level, u8 *gender) { struct Pokenav_RibbonsSummaryList *list = GetSubstructPtr(POKENAV_SUBSTRUCT_RIBBONS_SUMMARY_LIST); struct PokenavMonList *mons = list->monList; struct PokenavMonListItem *monInfo = &mons->monData[mons->currIndex]; if (monInfo->boxId == TOTAL_BOXES_COUNT) { // Get info for party mon struct Pokemon *mon = &gPlayerParty[monInfo->monId]; GetMonData(mon, MON_DATA_NICKNAME, nick); *level = GetLevelFromMonExp(mon); *gender = GetMonGender(mon); } else { // Get info for PC box mon struct BoxPokemon *boxMon = GetBoxedMonPtr(monInfo->boxId, monInfo->monId); *gender = GetBoxMonGender(boxMon); *level = GetLevelFromBoxMonExp(boxMon); GetBoxMonData(boxMon, MON_DATA_NICKNAME, nick); } StringGet_Nickname(nick); } static void GetMonSpeciesPersonalityOtId(u16 *species, u32 *personality, u32 *otId) { struct Pokenav_RibbonsSummaryList *list = GetSubstructPtr(POKENAV_SUBSTRUCT_RIBBONS_SUMMARY_LIST); struct PokenavMonList *mons = list->monList; struct PokenavMonListItem *monInfo = &mons->monData[mons->currIndex]; if (monInfo->boxId == TOTAL_BOXES_COUNT) { // Get info for party mon struct Pokemon *mon = &gPlayerParty[monInfo->monId]; *species = GetMonData(mon, MON_DATA_SPECIES); *personality = GetMonData(mon, MON_DATA_PERSONALITY); *otId = GetMonData(mon, MON_DATA_OT_ID); } else { // Get info for PC box mon struct BoxPokemon *boxMon = GetBoxedMonPtr(monInfo->boxId, monInfo->monId); *species = GetBoxMonData(boxMon, MON_DATA_SPECIES); *personality = GetBoxMonData(boxMon, MON_DATA_PERSONALITY); *otId = GetBoxMonData(boxMon, MON_DATA_OT_ID); } } static u32 GetCurrMonRibbonCount(void) { struct Pokenav_RibbonsSummaryList *list = GetSubstructPtr(POKENAV_SUBSTRUCT_RIBBONS_SUMMARY_LIST); struct PokenavMonList *mons = list->monList; struct PokenavMonListItem *monInfo = &mons->monData[mons->currIndex]; if (monInfo->boxId == TOTAL_BOXES_COUNT) return GetMonData(&gPlayerParty[monInfo->monId], MON_DATA_RIBBON_COUNT); else return GetBoxMonDataAt(monInfo->boxId, monInfo->monId, MON_DATA_RIBBON_COUNT); } static void GetMonRibbons(struct Pokenav_RibbonsSummaryList *list) { u32 ribbonFlags; s32 i, j; struct PokenavMonList *mons = list->monList; struct PokenavMonListItem *monInfo = &mons->monData[mons->currIndex]; if (monInfo->boxId == TOTAL_BOXES_COUNT) ribbonFlags = GetMonData(&gPlayerParty[monInfo->monId], MON_DATA_RIBBONS); else ribbonFlags = GetBoxMonDataAt(monInfo->boxId, monInfo->monId, MON_DATA_RIBBONS); list->numNormalRibbons = 0; list->numGiftRibbons = 0; for (i = 0; i < ARRAY_COUNT(sRibbonData); i++) { // For all non-contest ribbons, numRibbons will be 1 if they have it, 0 if they don't // For contest ribbons, numRibbons will be 0-4 s32 numRibbons = ((1 << sRibbonData[i].numBits) - 1) & ribbonFlags; if (!sRibbonData[i].isGiftRibbon) { for (j = 0; j < numRibbons; j++) list->ribbonIds[list->numNormalRibbons++] = sRibbonData[i].ribbonId + j; } else { for (j = 0; j < numRibbons; j++) list->giftRibbonIds[list->numGiftRibbons++] = sRibbonData[i].ribbonId + j; } ribbonFlags >>= sRibbonData[i].numBits; } if (list->numNormalRibbons != 0) { list->normalRibbonLastRowStart = ((list->numNormalRibbons - 1) / RIBBONS_PER_ROW) * RIBBONS_PER_ROW; list->selectedPos = 0; } else { // There are no normal ribbons, move cursor to first gift ribbon list->normalRibbonLastRowStart = 0; list->selectedPos = GIFT_RIBBON_START_POS; } } static u32 *GetNormalRibbonIds(u32 *size) { struct Pokenav_RibbonsSummaryList *list = GetSubstructPtr(POKENAV_SUBSTRUCT_RIBBONS_SUMMARY_LIST); *size = list->numNormalRibbons; return list->ribbonIds; } static u32 *GetGiftRibbonIds(u32 *size) { struct Pokenav_RibbonsSummaryList *list = GetSubstructPtr(POKENAV_SUBSTRUCT_RIBBONS_SUMMARY_LIST); *size = list->numGiftRibbons; return list->giftRibbonIds; } static u16 GetSelectedPosition(void) { struct Pokenav_RibbonsSummaryList *list = GetSubstructPtr(POKENAV_SUBSTRUCT_RIBBONS_SUMMARY_LIST); return list->selectedPos; } static u32 GetRibbonId(void) { struct Pokenav_RibbonsSummaryList *list = GetSubstructPtr(POKENAV_SUBSTRUCT_RIBBONS_SUMMARY_LIST); int ribbonPos = list->selectedPos; if (ribbonPos < FIRST_GIFT_RIBBON) return list->ribbonIds[ribbonPos]; else return list->giftRibbonIds[ribbonPos - GIFT_RIBBON_START_POS]; } bool32 OpenRibbonsSummaryMenu(void) { struct Pokenav_RibbonsSummaryMenu *menu = AllocSubstruct(POKENAV_SUBSTRUCT_RIBBONS_SUMMARY_MENU, sizeof(struct Pokenav_RibbonsSummaryMenu)); if (menu == NULL) return FALSE; menu->loopedTaskId = CreateLoopedTask(LoopedTask_OpenRibbonsSummaryMenu, 1); menu->callback = GetCurrentLoopedTaskActive; return TRUE; } void CreateRibbonsSummaryLoopedTask(s32 id) { struct Pokenav_RibbonsSummaryMenu *menu = GetSubstructPtr(POKENAV_SUBSTRUCT_RIBBONS_SUMMARY_MENU); menu->loopedTaskId = CreateLoopedTask(sRibbonsSummaryMenuLoopTaskFuncs[id], 1); menu->callback = GetCurrentLoopedTaskActive; } u32 IsRibbonsSummaryLoopedTaskActive(void) { struct Pokenav_RibbonsSummaryMenu *menu = GetSubstructPtr(POKENAV_SUBSTRUCT_RIBBONS_SUMMARY_MENU); return menu->callback(); } void FreeRibbonsSummaryScreen2(void) { struct Pokenav_RibbonsSummaryMenu *menu = GetSubstructPtr(POKENAV_SUBSTRUCT_RIBBONS_SUMMARY_MENU); RemoveWindow(menu->ribbonCountWindowId); RemoveWindow(menu->nameWindowId); RemoveWindow(menu->listIdxWindowId); #ifndef BUGFIX RemoveWindow(menu->unusedWindowId); // Removing window, but window id is never set #endif DestroyRibbonsMonFrontPic(menu); FreeSpriteTilesByTag(GFXTAG_RIBBON_ICONS_BIG); FreeSpritePaletteByTag(PALTAG_RIBBON_ICONS_1); FreeSpritePaletteByTag(PALTAG_RIBBON_ICONS_2); FreeSpritePaletteByTag(PALTAG_RIBBON_ICONS_3); FreeSpritePaletteByTag(PALTAG_RIBBON_ICONS_4); FreeSpritePaletteByTag(PALTAG_RIBBON_ICONS_5); FreeSpriteOamMatrix(menu->bigRibbonSprite); DestroySprite(menu->bigRibbonSprite); FreePokenavSubstruct(POKENAV_SUBSTRUCT_RIBBONS_SUMMARY_MENU); } static bool32 GetCurrentLoopedTaskActive(void) { struct Pokenav_RibbonsSummaryMenu *menu = GetSubstructPtr(POKENAV_SUBSTRUCT_RIBBONS_SUMMARY_MENU); return IsLoopedTaskActive(menu->loopedTaskId); } static u32 LoopedTask_OpenRibbonsSummaryMenu(s32 state) { struct Pokenav_RibbonsSummaryMenu *menu = GetSubstructPtr(POKENAV_SUBSTRUCT_RIBBONS_SUMMARY_MENU); switch (state) { case 0: InitBgTemplates(sBgTemplates, ARRAY_COUNT(sBgTemplates)); DecompressAndCopyTileDataToVram(2, gPokenavRibbonsSummaryBg_Gfx, 0, 0, 0); SetBgTilemapBuffer(2, menu->tilemapBuffers[0]); CopyToBgTilemapBuffer(2, gPokenavRibbonsSummaryBg_Tilemap, 0, 0); CopyPaletteIntoBufferUnfaded(gPokenavRibbonsSummaryBg_Pal, 0x10, 0x20); CopyBgTilemapBufferToVram(2); return LT_INC_AND_PAUSE; case 1: if (!FreeTempTileDataBuffersIfPossible()) { BgDmaFill(1, 0, 0, 1); DecompressAndCopyTileDataToVram(1, sRibbonIconsSmall_Gfx, 0, 1, 0); SetBgTilemapBuffer(1, menu->tilemapBuffers[1]); FillBgTilemapBufferRect_Palette0(1, 0, 0, 0, 32, 20); CopyPaletteIntoBufferUnfaded(sRibbonIcons1_Pal, 0x20, 0xA0); CopyPaletteIntoBufferUnfaded(sMonInfo_Pal, 0xA0, 0x20); CopyBgTilemapBufferToVram(1); return LT_INC_AND_PAUSE; } return LT_PAUSE; case 2: if (!FreeTempTileDataBuffersIfPossible()) { AddRibbonCountWindow(menu); return LT_INC_AND_PAUSE; } return LT_PAUSE; case 3: if (!FreeTempTileDataBuffersIfPossible()) { AddRibbonSummaryMonNameWindow(menu); return LT_INC_AND_PAUSE; } return LT_PAUSE; case 4: if (!FreeTempTileDataBuffersIfPossible()) { AddRibbonListIndexWindow(menu); return LT_INC_AND_PAUSE; } return LT_PAUSE; case 5: if (!IsDma3ManagerBusyWithBgCopy()) { CopyBgTilemapBufferToVram(2); return LT_INC_AND_PAUSE; } return LT_PAUSE; case 6: if (!IsDma3ManagerBusyWithBgCopy()) { ResetSpritesAndDrawMonFrontPic(menu); return LT_INC_AND_CONTINUE; } return LT_PAUSE; case 7: DrawAllRibbonsSmall(menu); PrintHelpBarText(HELPBAR_RIBBONS_LIST); return LT_INC_AND_PAUSE; case 8: if (!IsDma3ManagerBusyWithBgCopy()) { CreateBigRibbonSprite(menu); ChangeBgX(1, 0, BG_COORD_SET); ChangeBgY(1, 0, BG_COORD_SET); ChangeBgX(2, 0, BG_COORD_SET); ChangeBgY(2, 0, BG_COORD_SET); ShowBg(1); ShowBg(2); HideBg(3); PokenavFadeScreen(POKENAV_FADE_FROM_BLACK); return LT_INC_AND_PAUSE; } return LT_PAUSE; case 9: if (IsPaletteFadeActive()) return LT_PAUSE; } return LT_FINISH; } static u32 LoopedTask_ExitRibbonsSummaryMenu(s32 state) { switch (state) { case 0: PlaySE(SE_SELECT); PokenavFadeScreen(POKENAV_FADE_TO_BLACK); return LT_INC_AND_PAUSE; case 1: if (IsPaletteFadeActive()) return LT_PAUSE; return LT_FINISH; } return LT_FINISH; } static u32 LoopedTask_SwitchRibbonsSummaryMon(s32 state) { struct Pokenav_RibbonsSummaryMenu *menu = GetSubstructPtr(POKENAV_SUBSTRUCT_RIBBONS_SUMMARY_MENU); switch (state) { case 0: PlaySE(SE_SELECT); SlideMonSpriteOff(menu); return LT_INC_AND_PAUSE; case 1: if (!IsMonSpriteAnimating(menu)) { PrintRibbbonsSummaryMonInfo(menu); return LT_INC_AND_CONTINUE; } return LT_PAUSE; case 2: DrawAllRibbonsSmall(menu); return LT_INC_AND_CONTINUE; case 3: PrintRibbonsMonListIndex(menu); return LT_INC_AND_CONTINUE; case 4: PrintCurrentMonRibbonCount(menu); return LT_INC_AND_CONTINUE; case 5: if (!IsDma3ManagerBusyWithBgCopy()) { SlideMonSpriteOn(menu); return LT_INC_AND_PAUSE; } return LT_PAUSE; case 6: if (IsMonSpriteAnimating(menu)) return LT_PAUSE; } return LT_FINISH; } static u32 LoopedTask_ExpandSelectedRibbon(s32 state) { struct Pokenav_RibbonsSummaryMenu *menu = GetSubstructPtr(POKENAV_SUBSTRUCT_RIBBONS_SUMMARY_MENU); switch (state) { case 0: PlaySE(SE_SELECT); UpdateAndZoomInSelectedRibbon(menu); return LT_INC_AND_PAUSE; case 1: if (!IsRibbonAnimating(menu)) { PrintRibbonNameAndDescription(menu); PrintHelpBarText(HELPBAR_RIBBONS_CHECK); return LT_INC_AND_PAUSE; } return LT_PAUSE; case 2: if (IsDma3ManagerBusyWithBgCopy()) return LT_PAUSE; } return LT_FINISH; } static u32 LoopedTask_MoveRibbonsCursorExpanded(s32 state) { struct Pokenav_RibbonsSummaryMenu *menu = GetSubstructPtr(POKENAV_SUBSTRUCT_RIBBONS_SUMMARY_MENU); switch (state) { case 0: PlaySE(SE_SELECT); ZoomOutSelectedRibbon(menu); return LT_INC_AND_PAUSE; case 1: if (!IsRibbonAnimating(menu)) { UpdateAndZoomInSelectedRibbon(menu); return LT_INC_AND_PAUSE; } return LT_PAUSE; case 2: if (!IsRibbonAnimating(menu)) { PrintRibbonNameAndDescription(menu); return LT_INC_AND_PAUSE; } return LT_PAUSE; case 3: if (IsDma3ManagerBusyWithBgCopy()) return LT_PAUSE; } return LT_FINISH; } static u32 LoopedTask_ShrinkExpandedRibbon(s32 state) { struct Pokenav_RibbonsSummaryMenu *menu = GetSubstructPtr(POKENAV_SUBSTRUCT_RIBBONS_SUMMARY_MENU); switch (state) { case 0: PlaySE(SE_SELECT); ZoomOutSelectedRibbon(menu); return LT_INC_AND_PAUSE; case 1: if (!IsRibbonAnimating(menu)) { PrintCurrentMonRibbonCount(menu); PrintHelpBarText(HELPBAR_RIBBONS_LIST); return LT_INC_AND_PAUSE; } return LT_PAUSE; case 2: if (IsDma3ManagerBusyWithBgCopy()) return LT_PAUSE; } return LT_FINISH; } static const struct WindowTemplate sRibbonCountWindowTemplate = { .bg = 2, .tilemapLeft = 12, .tilemapTop = 13, .width = 16, .height = 4, .paletteNum = 1, .baseBlock = 0x14, }; static void AddRibbonCountWindow(struct Pokenav_RibbonsSummaryMenu *menu) { menu->ribbonCountWindowId = AddWindow(&sRibbonCountWindowTemplate); PutWindowTilemap(menu->ribbonCountWindowId); PrintCurrentMonRibbonCount(menu); } static void PrintCurrentMonRibbonCount(struct Pokenav_RibbonsSummaryMenu *menu) { u8 color[] = {TEXT_COLOR_RED, TEXT_COLOR_DARK_GRAY, TEXT_COLOR_LIGHT_GRAY}; ConvertIntToDecimalStringN(gStringVar1, GetCurrMonRibbonCount(), STR_CONV_MODE_LEFT_ALIGN, 2); DynamicPlaceholderTextUtil_Reset(); DynamicPlaceholderTextUtil_SetPlaceholderPtr(0, gStringVar1); DynamicPlaceholderTextUtil_ExpandPlaceholders(gStringVar4, gText_RibbonsF700); FillWindowPixelBuffer(menu->ribbonCountWindowId, PIXEL_FILL(4)); AddTextPrinterParameterized3(menu->ribbonCountWindowId, FONT_NORMAL, 0, 1, color, TEXT_SKIP_DRAW, gStringVar4); CopyWindowToVram(menu->ribbonCountWindowId, COPYWIN_GFX); } static void PrintRibbonNameAndDescription(struct Pokenav_RibbonsSummaryMenu *menu) { s32 i; u32 ribbonId = GetRibbonId(); u8 color[] = {TEXT_COLOR_RED, TEXT_COLOR_DARK_GRAY, TEXT_COLOR_LIGHT_GRAY}; FillWindowPixelBuffer(menu->ribbonCountWindowId, PIXEL_FILL(4)); if (ribbonId < FIRST_GIFT_RIBBON) { // Print normal ribbon name/description for (i = 0; i < 2; i++) AddTextPrinterParameterized3(menu->ribbonCountWindowId, FONT_NORMAL, 0, (i * 16) + 1, color, TEXT_SKIP_DRAW, gRibbonDescriptionPointers[ribbonId][i]); } else { // ribbonId here is one of the 'gift' ribbon slots, used to read // its actual value from giftRibbons to determine which specific // gift ribbon it is ribbonId = gSaveBlock1Ptr->giftRibbons[ribbonId - FIRST_GIFT_RIBBON]; // If 0, this gift ribbon slot is unoccupied if (ribbonId == 0) return; // Print gift ribbon name/description ribbonId--; for (i = 0; i < 2; i++) AddTextPrinterParameterized3(menu->ribbonCountWindowId, FONT_NORMAL, 0, (i * 16) + 1, color, TEXT_SKIP_DRAW, gGiftRibbonDescriptionPointers[ribbonId][i]); } CopyWindowToVram(menu->ribbonCountWindowId, COPYWIN_GFX); } static const struct WindowTemplate sRibbonSummaryMonNameWindowTemplate = { .bg = 2, .tilemapLeft = 14, .tilemapTop = 1, .width = 13, .height = 2, .paletteNum = 10, .baseBlock = 0x54, }; static void AddRibbonSummaryMonNameWindow(struct Pokenav_RibbonsSummaryMenu *menu) { menu->nameWindowId = AddWindow(&sRibbonSummaryMonNameWindowTemplate); PutWindowTilemap(menu->nameWindowId); PrintRibbbonsSummaryMonInfo(menu); } static const u8 sMaleIconString[] = _("{COLOR_HIGHLIGHT_SHADOW}{LIGHT_RED}{WHITE}{GREEN}♂{COLOR_HIGHLIGHT_SHADOW}{DARK_GRAY}{WHITE}{LIGHT_GRAY}"); static const u8 sFemaleIconString[] = _("{COLOR_HIGHLIGHT_SHADOW}{LIGHT_GREEN}{WHITE}{BLUE}♀{COLOR_HIGHLIGHT_SHADOW}{DARK_GRAY}{WHITE}{LIGHT_GRAY}"); static const u8 sGenderlessIconString[] = _("{UNK_SPACER}"); static void PrintRibbbonsSummaryMonInfo(struct Pokenav_RibbonsSummaryMenu *menu) { const u8 *genderTxt; u8 *txtPtr; u8 level, gender; u16 windowId = menu->nameWindowId; FillWindowPixelBuffer(windowId, PIXEL_FILL(1)); GetMonNicknameLevelGender(gStringVar3, &level, &gender); AddTextPrinterParameterized(windowId, FONT_NORMAL, gStringVar3, 0, 1, TEXT_SKIP_DRAW, NULL); switch (gender) { case MON_MALE: genderTxt = sMaleIconString; break; case MON_FEMALE: genderTxt = sFemaleIconString; break; default: genderTxt = sGenderlessIconString; break; } txtPtr = StringCopy(gStringVar1, genderTxt); *(txtPtr++) = CHAR_SLASH; *(txtPtr++) = CHAR_EXTRA_SYMBOL; *(txtPtr++) = CHAR_LV_2; ConvertIntToDecimalStringN(txtPtr, level, STR_CONV_MODE_LEFT_ALIGN, 3); AddTextPrinterParameterized(windowId, FONT_NORMAL, gStringVar1, 60, 1, TEXT_SKIP_DRAW, NULL); CopyWindowToVram(windowId, COPYWIN_GFX); } static const struct WindowTemplate sRibbonMonListIndexWindowTemplate[] = { { .bg = 2, .tilemapLeft = 1, .tilemapTop = 5, .width = 7, .height = 2, .paletteNum = 1, .baseBlock = 0x6E, }, {}, }; static void AddRibbonListIndexWindow(struct Pokenav_RibbonsSummaryMenu *menu) { menu->listIdxWindowId = AddWindow(sRibbonMonListIndexWindowTemplate); FillWindowPixelBuffer(menu->listIdxWindowId, PIXEL_FILL(1)); PutWindowTilemap(menu->listIdxWindowId); PrintRibbonsMonListIndex(menu); } static void PrintRibbonsMonListIndex(struct Pokenav_RibbonsSummaryMenu *menu) { s32 x; u8 *txtPtr; u32 id = GetRibbonsSummaryCurrentIndex() + 1; u32 count = GetRibbonsSummaryMonListCount(); txtPtr = ConvertIntToDecimalStringN(gStringVar1, id, STR_CONV_MODE_RIGHT_ALIGN, 3); *(txtPtr++) = CHAR_SLASH; ConvertIntToDecimalStringN(txtPtr, count, STR_CONV_MODE_RIGHT_ALIGN, 3); x = GetStringCenterAlignXOffset(FONT_NORMAL, gStringVar1, 56); AddTextPrinterParameterized(menu->listIdxWindowId, FONT_NORMAL, gStringVar1, x, 1, TEXT_SKIP_DRAW, NULL); CopyWindowToVram(menu->listIdxWindowId, COPYWIN_GFX); } static void ResetSpritesAndDrawMonFrontPic(struct Pokenav_RibbonsSummaryMenu *menu) { u16 species; u32 personality, otId; GetMonSpeciesPersonalityOtId(&species, &personality, &otId); ResetAllPicSprites(); menu->monSpriteId = DrawRibbonsMonFrontPic(MON_SPRITE_X_ON, MON_SPRITE_Y); PokenavFillPalette(15, 0); } static void DestroyRibbonsMonFrontPic(struct Pokenav_RibbonsSummaryMenu *menu) { FreeAndDestroyMonPicSprite(menu->monSpriteId); } // x and y arguments are ignored // y is always given as MON_SPRITE_Y // x is given as either MON_SPRITE_X_ON or MON_SPRITE_X_OFF (but ignored and MON_SPRITE_X_ON is used) static u16 DrawRibbonsMonFrontPic(s32 x, s32 y) { u16 species, spriteId; u32 personality, otId; GetMonSpeciesPersonalityOtId(&species, &personality, &otId); spriteId = CreateMonPicSprite_HandleDeoxys(species, otId, personality, TRUE, MON_SPRITE_X_ON, MON_SPRITE_Y, 15, TAG_NONE); gSprites[spriteId].oam.priority = 0; return spriteId; } static void SlideMonSpriteOff(struct Pokenav_RibbonsSummaryMenu *menu) { StartMonSpriteSlide(&gSprites[menu->monSpriteId], MON_SPRITE_X_ON, MON_SPRITE_X_OFF, 6); } static void SlideMonSpriteOn(struct Pokenav_RibbonsSummaryMenu *menu) { // Switch to new mon sprite FreeAndDestroyMonPicSprite(menu->monSpriteId); menu->monSpriteId = DrawRibbonsMonFrontPic(MON_SPRITE_X_OFF, MON_SPRITE_Y); // Slide on StartMonSpriteSlide(&gSprites[menu->monSpriteId], MON_SPRITE_X_OFF, MON_SPRITE_X_ON, 6); } // Is Pokémon summary sprite still sliding off/on static bool32 IsMonSpriteAnimating(struct Pokenav_RibbonsSummaryMenu *menu) { return (gSprites[menu->monSpriteId].callback != SpriteCallbackDummy); } #define sCurrX data[0] #define sMoveIncr data[1] #define sTime data[2] #define sDestX data[3] static void StartMonSpriteSlide(struct Sprite *sprite, s32 startX, s32 destX, s32 time) { u32 delta = destX - startX; sprite->x = startX; sprite->sCurrX = startX << 4; sprite->sMoveIncr = (delta << 4) / time; sprite->sTime = time; sprite->sDestX = destX; sprite->callback = SpriteCB_MonSpriteSlide; } static void SpriteCB_MonSpriteSlide(struct Sprite *sprite) { if (sprite->sTime != 0) { sprite->sTime--; sprite->sCurrX += sprite->sMoveIncr; sprite->x = sprite->sCurrX >> 4; if (sprite->x <= MON_SPRITE_X_OFF) sprite->invisible = TRUE; else sprite->invisible = FALSE; } else { sprite->x = sprite->sDestX; sprite->callback = SpriteCallbackDummy; } } #undef sCurrX #undef sMoveIncr #undef sTime #undef sDestX static void DrawAllRibbonsSmall(struct Pokenav_RibbonsSummaryMenu *menu) { u32 *ribbonIds; ClearRibbonsSummaryBg(); ribbonIds = GetNormalRibbonIds(&sRibbonDraw_Total); for (sRibbonDraw_Current = 0; sRibbonDraw_Current < sRibbonDraw_Total; sRibbonDraw_Current++) DrawRibbonSmall(sRibbonDraw_Current, *(ribbonIds++)); ribbonIds = GetGiftRibbonIds(&sRibbonDraw_Total); for (sRibbonDraw_Current = 0; sRibbonDraw_Current < sRibbonDraw_Total; sRibbonDraw_Current++) DrawRibbonSmall(sRibbonDraw_Current + GIFT_RIBBON_START_POS, *(ribbonIds++)); CopyBgTilemapBufferToVram(1); } // Redundant, the same FillBg is called in LoopedTask_OpenRibbonsSummaryMenu static void ClearRibbonsSummaryBg(void) { FillBgTilemapBufferRect_Palette0(1, 0, 0, 0, 32, 20); } static void DrawRibbonSmall(u32 i, u32 ribbonId) { u16 bgData[4]; u32 destX = (i % RIBBONS_PER_ROW) * 2 + 11; u32 destY = (i / RIBBONS_PER_ROW) * 2 + 4; BufferSmallRibbonGfxData(bgData, ribbonId); CopyToBgTilemapBufferRect(1, bgData, destX, destY, 2, 2); } // Below correspond to a ribbon icon in ribbons/icons.png and ribbons/icons_big.png; 0 at top, 11 at bottom enum { RIBBONGFX_CHAMPION, RIBBONGFX_CONTEST_NORMAL, RIBBONGFX_CONTEST_SUPER, RIBBONGFX_CONTEST_HYPER, RIBBONGFX_CONTEST_MASTER, RIBBONGFX_WINNING, RIBBONGFX_VICTORY, RIBBONGFX_ARTIST, RIBBONGFX_EFFORT, RIBBONGFX_GIFT_1, RIBBONGFX_GIFT_2, RIBBONGFX_GIFT_3, }; #define TO_PAL_OFFSET(palNum)((palNum) - PALTAG_RIBBON_ICONS_1) struct { u16 tileNumOffset; u16 palNumOffset; } static const sRibbonGfxData[] = { [CHAMPION_RIBBON] = { RIBBONGFX_CHAMPION, TO_PAL_OFFSET(PALTAG_RIBBON_ICONS_1)}, [COOL_RIBBON_NORMAL] = { RIBBONGFX_CONTEST_NORMAL, TO_PAL_OFFSET(PALTAG_RIBBON_ICONS_1)}, [COOL_RIBBON_SUPER] = { RIBBONGFX_CONTEST_SUPER, TO_PAL_OFFSET(PALTAG_RIBBON_ICONS_1)}, [COOL_RIBBON_HYPER] = { RIBBONGFX_CONTEST_HYPER, TO_PAL_OFFSET(PALTAG_RIBBON_ICONS_1)}, [COOL_RIBBON_MASTER] = { RIBBONGFX_CONTEST_MASTER, TO_PAL_OFFSET(PALTAG_RIBBON_ICONS_1)}, [BEAUTY_RIBBON_NORMAL] = { RIBBONGFX_CONTEST_NORMAL, TO_PAL_OFFSET(PALTAG_RIBBON_ICONS_2)}, [BEAUTY_RIBBON_SUPER] = { RIBBONGFX_CONTEST_SUPER, TO_PAL_OFFSET(PALTAG_RIBBON_ICONS_2)}, [BEAUTY_RIBBON_HYPER] = { RIBBONGFX_CONTEST_HYPER, TO_PAL_OFFSET(PALTAG_RIBBON_ICONS_2)}, [BEAUTY_RIBBON_MASTER] = { RIBBONGFX_CONTEST_MASTER, TO_PAL_OFFSET(PALTAG_RIBBON_ICONS_2)}, [CUTE_RIBBON_NORMAL] = { RIBBONGFX_CONTEST_NORMAL, TO_PAL_OFFSET(PALTAG_RIBBON_ICONS_3)}, [CUTE_RIBBON_SUPER] = { RIBBONGFX_CONTEST_SUPER, TO_PAL_OFFSET(PALTAG_RIBBON_ICONS_3)}, [CUTE_RIBBON_HYPER] = { RIBBONGFX_CONTEST_HYPER, TO_PAL_OFFSET(PALTAG_RIBBON_ICONS_3)}, [CUTE_RIBBON_MASTER] = { RIBBONGFX_CONTEST_MASTER, TO_PAL_OFFSET(PALTAG_RIBBON_ICONS_3)}, [SMART_RIBBON_NORMAL] = { RIBBONGFX_CONTEST_NORMAL, TO_PAL_OFFSET(PALTAG_RIBBON_ICONS_4)}, [SMART_RIBBON_SUPER] = { RIBBONGFX_CONTEST_SUPER, TO_PAL_OFFSET(PALTAG_RIBBON_ICONS_4)}, [SMART_RIBBON_HYPER] = { RIBBONGFX_CONTEST_HYPER, TO_PAL_OFFSET(PALTAG_RIBBON_ICONS_4)}, [SMART_RIBBON_MASTER] = { RIBBONGFX_CONTEST_MASTER, TO_PAL_OFFSET(PALTAG_RIBBON_ICONS_4)}, [TOUGH_RIBBON_NORMAL] = { RIBBONGFX_CONTEST_NORMAL, TO_PAL_OFFSET(PALTAG_RIBBON_ICONS_5)}, [TOUGH_RIBBON_SUPER] = { RIBBONGFX_CONTEST_SUPER, TO_PAL_OFFSET(PALTAG_RIBBON_ICONS_5)}, [TOUGH_RIBBON_HYPER] = { RIBBONGFX_CONTEST_HYPER, TO_PAL_OFFSET(PALTAG_RIBBON_ICONS_5)}, [TOUGH_RIBBON_MASTER] = { RIBBONGFX_CONTEST_MASTER, TO_PAL_OFFSET(PALTAG_RIBBON_ICONS_5)}, [WINNING_RIBBON] = { RIBBONGFX_WINNING, TO_PAL_OFFSET(PALTAG_RIBBON_ICONS_1)}, [VICTORY_RIBBON] = { RIBBONGFX_VICTORY, TO_PAL_OFFSET(PALTAG_RIBBON_ICONS_1)}, [ARTIST_RIBBON] = { RIBBONGFX_ARTIST, TO_PAL_OFFSET(PALTAG_RIBBON_ICONS_2)}, [EFFORT_RIBBON] = { RIBBONGFX_EFFORT, TO_PAL_OFFSET(PALTAG_RIBBON_ICONS_3)}, [MARINE_RIBBON] = { RIBBONGFX_GIFT_1, TO_PAL_OFFSET(PALTAG_RIBBON_ICONS_2)}, [LAND_RIBBON] = { RIBBONGFX_GIFT_1, TO_PAL_OFFSET(PALTAG_RIBBON_ICONS_4)}, [SKY_RIBBON] = { RIBBONGFX_GIFT_1, TO_PAL_OFFSET(PALTAG_RIBBON_ICONS_5)}, [COUNTRY_RIBBON] = { RIBBONGFX_GIFT_2, TO_PAL_OFFSET(PALTAG_RIBBON_ICONS_4)}, [NATIONAL_RIBBON] = { RIBBONGFX_GIFT_2, TO_PAL_OFFSET(PALTAG_RIBBON_ICONS_5)}, [EARTH_RIBBON] = { RIBBONGFX_GIFT_3, TO_PAL_OFFSET(PALTAG_RIBBON_ICONS_1)}, [WORLD_RIBBON] = { RIBBONGFX_GIFT_3, TO_PAL_OFFSET(PALTAG_RIBBON_ICONS_2)}, }; #undef TO_PAL_OFFSET static void BufferSmallRibbonGfxData(u16 *dst, u32 ribbonId) { u16 palNum = sRibbonGfxData[ribbonId].palNumOffset + 2; u16 tileNum = (sRibbonGfxData[ribbonId].tileNumOffset * 2) + 1; dst[0] = tileNum | (palNum << 12); dst[1] = tileNum | (palNum << 12) | 0x400; dst[2] = (tileNum + 1) | (palNum << 12); dst[3] = (tileNum + 1) | (palNum << 12) | 0x400; } static const struct CompressedSpriteSheet sSpriteSheet_RibbonIconsBig = { sRibbonIconsBig_Gfx, 0x1800, GFXTAG_RIBBON_ICONS_BIG }; static const struct SpritePalette sSpritePalettes_RibbonIcons[] = { {sRibbonIcons1_Pal, PALTAG_RIBBON_ICONS_1}, {sRibbonIcons2_Pal, PALTAG_RIBBON_ICONS_2}, {sRibbonIcons3_Pal, PALTAG_RIBBON_ICONS_3}, {sRibbonIcons4_Pal, PALTAG_RIBBON_ICONS_4}, {sRibbonIcons5_Pal, PALTAG_RIBBON_ICONS_5}, {}, }; static const struct OamData sOamData_RibbonIconBig = { .y = 0, .affineMode = ST_OAM_AFFINE_NORMAL, .objMode = ST_OAM_OBJ_NORMAL, .mosaic = 0, .bpp = ST_OAM_4BPP, .shape = SPRITE_SHAPE(32x32), .x = 0, .matrixNum = 0, .size = SPRITE_SIZE(32x32), .tileNum = 0, .priority = 1, .paletteNum = 0, .affineParam = 0 }; static const union AffineAnimCmd sAffineAnim_RibbonIconBig_Normal[] = { AFFINEANIMCMD_FRAME(128, 128, 0, 0), AFFINEANIMCMD_END }; static const union AffineAnimCmd sAffineAnim_RibbonIconBig_ZoomIn[] = { AFFINEANIMCMD_FRAME(128, 128, 0, 0), AFFINEANIMCMD_FRAME(32, 32, 0, 4), AFFINEANIMCMD_END }; static const union AffineAnimCmd sAffineAnim_RibbonIconBig_ZoomOut[] = { AFFINEANIMCMD_FRAME(256, 256, 0, 0), AFFINEANIMCMD_FRAME(-32, -32, 0, 4), AFFINEANIMCMD_END }; enum { RIBBONANIM_NORMAL, RIBBONANIM_ZOOM_IN, RIBBONANIM_ZOOM_OUT, }; static const union AffineAnimCmd *const sAffineAnims_RibbonIconBig[] = { [RIBBONANIM_NORMAL] = sAffineAnim_RibbonIconBig_Normal, [RIBBONANIM_ZOOM_IN] = sAffineAnim_RibbonIconBig_ZoomIn, [RIBBONANIM_ZOOM_OUT] = sAffineAnim_RibbonIconBig_ZoomOut }; static const struct SpriteTemplate sSpriteTemplate_RibbonIconBig = { .tileTag = GFXTAG_RIBBON_ICONS_BIG, .paletteTag = PALTAG_RIBBON_ICONS_1, .oam = &sOamData_RibbonIconBig, .anims = gDummySpriteAnimTable, .images = NULL, .affineAnims = sAffineAnims_RibbonIconBig, .callback = SpriteCallbackDummy, }; // Create dummy sprite to be used for the zoomed in version of the selected ribbon static void CreateBigRibbonSprite(struct Pokenav_RibbonsSummaryMenu *menu) { u8 spriteId; LoadCompressedSpriteSheet(&sSpriteSheet_RibbonIconsBig); Pokenav_AllocAndLoadPalettes(sSpritePalettes_RibbonIcons); spriteId = CreateSprite(&sSpriteTemplate_RibbonIconBig, 0, 0, 0); menu->bigRibbonSprite = &gSprites[spriteId]; menu->bigRibbonSprite->invisible = TRUE; } #define sInvisibleWhenDone data[0] static void UpdateAndZoomInSelectedRibbon(struct Pokenav_RibbonsSummaryMenu *menu) { u32 ribbonId; s32 position = GetSelectedPosition(); s32 x = (position % RIBBONS_PER_ROW) * 16 + 96; s32 y = (position / RIBBONS_PER_ROW) * 16 + 40; menu->bigRibbonSprite->x = x; menu->bigRibbonSprite->y = y; // Set new selected ribbon's gfx data ribbonId = GetRibbonId(); menu->bigRibbonSprite->oam.tileNum = (sRibbonGfxData[ribbonId].tileNumOffset * 16) + GetSpriteTileStartByTag(GFXTAG_RIBBON_ICONS_BIG); menu->bigRibbonSprite->oam.paletteNum = IndexOfSpritePaletteTag(sRibbonGfxData[ribbonId].palNumOffset + PALTAG_RIBBON_ICONS_1); // Start zoom in animation StartSpriteAffineAnim(menu->bigRibbonSprite, RIBBONANIM_ZOOM_IN); menu->bigRibbonSprite->invisible = FALSE; menu->bigRibbonSprite->sInvisibleWhenDone = FALSE; menu->bigRibbonSprite->callback = SpriteCB_WaitForRibbonAnimation; } // Start animation to zoom out of selected ribbon static void ZoomOutSelectedRibbon(struct Pokenav_RibbonsSummaryMenu *menu) { menu->bigRibbonSprite->sInvisibleWhenDone = TRUE; StartSpriteAffineAnim(menu->bigRibbonSprite, RIBBONANIM_ZOOM_OUT); menu->bigRibbonSprite->callback = SpriteCB_WaitForRibbonAnimation; } static bool32 IsRibbonAnimating(struct Pokenav_RibbonsSummaryMenu *menu) { return (menu->bigRibbonSprite->callback != SpriteCallbackDummy); } static void SpriteCB_WaitForRibbonAnimation(struct Sprite *sprite) { if (sprite->affineAnimEnded) { sprite->invisible = sprite->sInvisibleWhenDone; sprite->callback = SpriteCallbackDummy; } }