#include "global.h" #include "bg.h" #include "data.h" #include "decompress.h" #include "dma3.h" #include "international_string_util.h" #include "main.h" #include "match_call.h" #include "menu.h" #include "overworld.h" #include "palette.h" #include "pokenav.h" #include "region_map.h" #include "sound.h" #include "sprite.h" #include "string_util.h" #include "strings.h" #include "task.h" #include "text.h" #include "text_window.h" #include "trig.h" #include "window.h" #include "constants/game_stat.h" #include "constants/region_map_sections.h" #include "constants/songs.h" #define GFXTAG_CURSOR 7 #define GFXTAG_TRAINER_PIC 8 #define PALTAG_CURSOR 12 #define PALTAG_TRAINER_PIC 13 struct Pokenav_MatchCallGfx { bool32 (*isTaskActiveCB)(void); u32 loopTaskId; u8 filler8[6]; bool8 skipHangUpSE; bool8 newRematchRequest; u16 locWindowId; u16 infoBoxWindowId; u16 msgBoxWindowId; s16 pageDelta; u8 unused18; u8 unused19; u16 trainerPicPalOffset; struct Sprite *optionsCursorSprite; struct Sprite *trainerPicSprite; u8 bgTilemapBuffer1[BG_SCREEN_SIZE]; u8 unusedTilemapBuffer[BG_SCREEN_SIZE]; u8 bgTilemapBuffer2[BG_SCREEN_SIZE]; u8 *trainerPicGfxPtr; u8 trainerPicGfx[0x800]; u8 trainerPicPal[0x20]; }; static bool32 GetCurrentLoopedTaskActive(void); static u32 LoopedTask_OpenMatchCall(s32); static void CreateMatchCallList(void); static void DestroyMatchCallList(void); static void FreeMatchCallSprites(void); static void LoadCallWindowAndFade(struct Pokenav_MatchCallGfx *); static void DrawMatchCallLeftColumnWindows(struct Pokenav_MatchCallGfx *); static void UpdateMatchCallInfoBox(struct Pokenav_MatchCallGfx *); static void PrintMatchCallLocation(struct Pokenav_MatchCallGfx *, int); static void AllocMatchCallSprites(void); static void SetPokeballIconsFlashing(bool32); static void PrintMatchCallSelectionOptions(struct Pokenav_MatchCallGfx *); static bool32 ShowOptionsCursor(struct Pokenav_MatchCallGfx *); static void UpdateCursorGfxPos(struct Pokenav_MatchCallGfx *, int); static bool32 IsDma3ManagerBusyWithBgCopy1(struct Pokenav_MatchCallGfx *); static void UpdateWindowsReturnToTrainerList(struct Pokenav_MatchCallGfx *); static void DrawMsgBoxForMatchCallMsg(struct Pokenav_MatchCallGfx *); static bool32 IsDma3ManagerBusyWithBgCopy2(struct Pokenav_MatchCallGfx *); static void PrintCallingDots(struct Pokenav_MatchCallGfx *); static bool32 WaitForCallingDotsText(struct Pokenav_MatchCallGfx *); static void PrintMatchCallMessage(struct Pokenav_MatchCallGfx *); static bool32 WaitForMatchCallMessageText(struct Pokenav_MatchCallGfx *); static void DrawMsgBoxForCloseByMsg(struct Pokenav_MatchCallGfx *); static void PrintTrainerIsCloseBy(struct Pokenav_MatchCallGfx *); static bool32 WaitForTrainerIsCloseByText(struct Pokenav_MatchCallGfx *); static void EraseCallMessageBox(struct Pokenav_MatchCallGfx *); static bool32 WaitForCallMessageBoxErase(struct Pokenav_MatchCallGfx *); static void UpdateWindowsToShowCheckPage(struct Pokenav_MatchCallGfx *); static void LoadCheckPageTrainerPic(struct Pokenav_MatchCallGfx *); static bool32 WaitForTrainerPic(struct Pokenav_MatchCallGfx *); static void TrainerPicSlideOffscreen(struct Pokenav_MatchCallGfx *); static void Task_FlashPokeballIcons(u8); static void TryDrawRematchPokeballIcon(u16, u32, u32); static void PrintNumberRegisteredLabel(u16); static void PrintNumberRegistered(u16); static void PrintNumberOfBattlesLabel(u16); static void PrintNumberOfBattles(u16); static void PrintMatchCallInfoLabel(u16, const u8 *, int); static void PrintMatchCallInfoNumber(u16, const u8 *, int); static void CreateOptionsCursorSprite(struct Pokenav_MatchCallGfx *, int); static void CloseMatchCallSelectOptionsWindow(struct Pokenav_MatchCallGfx *); static struct Sprite *CreateTrainerPicSprite(void); static void SpriteCB_TrainerPicSlideOnscreen(struct Sprite *); static void SpriteCB_TrainerPicSlideOffscreen(struct Sprite *); static void SpriteCB_OptionsCursor(struct Sprite *); static u32 MatchCallListCursorDown(s32); static u32 MatchCallListCursorUp(s32); static u32 MatchCallListPageDown(s32); static u32 MatchCallListPageUp(s32); static u32 SelectMatchCallEntry(s32); static u32 MoveMatchCallOptionsCursor(s32); static u32 CancelMatchCallSelection(s32); static u32 DoMatchCallMessage(s32); static u32 DoTrainerCloseByMessage(s32); static u32 CloseMatchCallMessage(s32); static u32 ShowCheckPage(s32); static u32 ShowCheckPageUp(s32); static u32 ShowCheckPageDown(s32); static u32 ExitCheckPage(s32); static u32 ExitMatchCall(s32); static const u16 sMatchCallUI_Pal[] = INCBIN_U16("graphics/pokenav/match_call/ui.gbapal"); static const u32 sMatchCallUI_Gfx[] = INCBIN_U32("graphics/pokenav/match_call/ui.4bpp.lz"); static const u32 sMatchCallUI_Tilemap[] = INCBIN_U32("graphics/pokenav/match_call/ui.bin.lz"); static const u16 sOptionsCursor_Pal[] = INCBIN_U16("graphics/pokenav/match_call/options_cursor.gbapal"); static const u32 sOptionsCursor_Gfx[] = INCBIN_U32("graphics/pokenav/match_call/options_cursor.4bpp.lz"); static const u16 sCallWindow_Pal[] = INCBIN_U16("graphics/pokenav/match_call/call_window.gbapal"); static const u16 sListWindow_Pal[] = INCBIN_U16("graphics/pokenav/match_call/list_window.gbapal"); static const u16 sPokeball_Pal[] = INCBIN_U16("graphics/pokenav/match_call/pokeball.gbapal"); static const u32 sPokeball_Gfx[] = INCBIN_U32("graphics/pokenav/match_call/pokeball.4bpp.lz"); static const struct BgTemplate sMatchCallBgTemplates[3] = { { .bg = 1, .charBaseIndex = 3, .mapBaseIndex = 0x1F, .screenSize = 0, .paletteMode = 0, .priority = 1, .baseTile = 0 }, { .bg = 2, .charBaseIndex = 2, .mapBaseIndex = 0x06, .screenSize = 0, .paletteMode = 0, .priority = 2, .baseTile = 0x80 }, { .bg = 3, .charBaseIndex = 1, .mapBaseIndex = 0x07, .screenSize = 0, .paletteMode = 0, .priority = 3, .baseTile = 0 } }; static const LoopedTask sMatchCallLoopTaskFuncs[] = { [POKENAV_MC_FUNC_NONE] = NULL, [POKENAV_MC_FUNC_DOWN] = MatchCallListCursorDown, [POKENAV_MC_FUNC_UP] = MatchCallListCursorUp, [POKENAV_MC_FUNC_PG_DOWN] = MatchCallListPageDown, [POKENAV_MC_FUNC_PG_UP] = MatchCallListPageUp, [POKENAV_MC_FUNC_SELECT] = SelectMatchCallEntry, [POKENAV_MC_FUNC_MOVE_OPTIONS_CURSOR] = MoveMatchCallOptionsCursor, [POKENAV_MC_FUNC_CANCEL] = CancelMatchCallSelection, [POKENAV_MC_FUNC_CALL_MSG] = DoMatchCallMessage, [POKENAV_MC_FUNC_NEARBY_MSG] = DoTrainerCloseByMessage, [POKENAV_MC_FUNC_EXIT_CALL] = CloseMatchCallMessage, [POKENAV_MC_FUNC_SHOW_CHECK_PAGE] = ShowCheckPage, [POKENAV_MC_FUNC_CHECK_PAGE_UP] = ShowCheckPageUp, [POKENAV_MC_FUNC_CHECK_PAGE_DOWN] = ShowCheckPageDown, [POKENAV_MC_FUNC_EXIT_CHECK_PAGE] = ExitCheckPage, [POKENAV_MC_FUNC_EXIT] = ExitMatchCall }; static const struct WindowTemplate sMatchCallLocationWindowTemplate = { .bg = 2, .tilemapLeft = 0, .tilemapTop = 5, .width = 11, .height = 2, .paletteNum = 2, .baseBlock = 16 }; static const struct WindowTemplate sMatchCallInfoBoxWindowTemplate = { .bg = 2, .tilemapLeft = 0, .tilemapTop = 9, .width = 11, .height = 8, .paletteNum = 2, .baseBlock = 38 }; static const u8 *const sMatchCallOptionTexts[MATCH_CALL_OPTION_COUNT] = { [MATCH_CALL_OPTION_CALL] = gText_Call, [MATCH_CALL_OPTION_CHECK] = gText_Check, [MATCH_CALL_OPTION_CANCEL] = gText_Cancel6 }; // The series of 5 dots that appear when someone is called with Match Call static const u8 sText_CallingDots[] = _("·{PAUSE 4}·{PAUSE 4}·{PAUSE 4}·{PAUSE 4}·\p"); static const struct WindowTemplate sCallMsgBoxWindowTemplate = { .bg = 1, .tilemapLeft = 1, .tilemapTop = 12, .width = 28, .height = 4, .paletteNum = 1, .baseBlock = 10 }; static const struct CompressedSpriteSheet sOptionsCursorSpriteSheets[1] = { {sOptionsCursor_Gfx, 0x40, GFXTAG_CURSOR} }; static const struct SpritePalette sOptionsCursorSpritePalettes[2] = { {sOptionsCursor_Pal, PALTAG_CURSOR} }; static const struct OamData sOptionsCursorOamData = { .y = 0, .affineMode = ST_OAM_AFFINE_OFF, .objMode = ST_OAM_OBJ_NORMAL, .bpp = ST_OAM_4BPP, .shape = SPRITE_SHAPE(8x16), .x = 0, .size = SPRITE_SIZE(8x16), .tileNum = 0, .priority = 1, .paletteNum = 0, }; static const struct SpriteTemplate sOptionsCursorSpriteTemplate = { .tileTag = GFXTAG_CURSOR, .paletteTag = PALTAG_CURSOR, .oam = &sOptionsCursorOamData, .anims = gDummySpriteAnimTable, .images = NULL, .affineAnims = gDummySpriteAffineAnimTable, .callback = SpriteCB_OptionsCursor, }; static const struct OamData sTrainerPicOamData = { .y = 0, .affineMode = ST_OAM_AFFINE_OFF, .objMode = ST_OAM_OBJ_NORMAL, .bpp = ST_OAM_4BPP, .shape = SPRITE_SHAPE(64x64), .x = 0, .size = SPRITE_SIZE(64x64), .tileNum = 0, .priority = 1, .paletteNum = 0, }; static const struct SpriteTemplate sTrainerPicSpriteTemplate = { .tileTag = GFXTAG_TRAINER_PIC, .paletteTag = PALTAG_TRAINER_PIC, .oam = &sTrainerPicOamData, .anims = gDummySpriteAnimTable, .images = NULL, .affineAnims = gDummySpriteAffineAnimTable, .callback = SpriteCallbackDummy, }; bool32 OpenMatchCall(void) { struct Pokenav_MatchCallGfx *gfx = AllocSubstruct(POKENAV_SUBSTRUCT_MATCH_CALL_OPEN, sizeof(struct Pokenav_MatchCallGfx)); if (!gfx) return FALSE; gfx->unused19 = 0; gfx->loopTaskId = CreateLoopedTask(LoopedTask_OpenMatchCall, 1); gfx->isTaskActiveCB = GetCurrentLoopedTaskActive; return TRUE; } void CreateMatchCallLoopedTask(s32 index) { struct Pokenav_MatchCallGfx *gfx = GetSubstructPtr(POKENAV_SUBSTRUCT_MATCH_CALL_OPEN); gfx->loopTaskId = CreateLoopedTask(sMatchCallLoopTaskFuncs[index], 1); gfx->isTaskActiveCB = GetCurrentLoopedTaskActive; } bool32 IsMatchCallLoopedTaskActive(void) { struct Pokenav_MatchCallGfx *gfx = GetSubstructPtr(POKENAV_SUBSTRUCT_MATCH_CALL_OPEN); return gfx->isTaskActiveCB(); } void FreeMatchCallSubstruct2(void) { struct Pokenav_MatchCallGfx *gfx = GetSubstructPtr(POKENAV_SUBSTRUCT_MATCH_CALL_OPEN); FreeMatchCallSprites(); DestroyMatchCallList(); RemoveWindow(gfx->infoBoxWindowId); RemoveWindow(gfx->locWindowId); RemoveWindow(gfx->msgBoxWindowId); FreePokenavSubstruct(POKENAV_SUBSTRUCT_MATCH_CALL_OPEN); } static bool32 GetCurrentLoopedTaskActive(void) { struct Pokenav_MatchCallGfx *gfx = GetSubstructPtr(POKENAV_SUBSTRUCT_MATCH_CALL_OPEN); return IsLoopedTaskActive(gfx->loopTaskId); } static u32 LoopedTask_OpenMatchCall(s32 state) { struct Pokenav_MatchCallGfx *gfx = GetSubstructPtr(POKENAV_SUBSTRUCT_MATCH_CALL_OPEN); switch (state) { case 0: InitBgTemplates(sMatchCallBgTemplates, ARRAY_COUNT(sMatchCallBgTemplates)); ChangeBgX(2, 0, BG_COORD_SET); ChangeBgY(2, 0, BG_COORD_SET); DecompressAndCopyTileDataToVram(2, sMatchCallUI_Gfx, 0, 0, 0); SetBgTilemapBuffer(2, gfx->bgTilemapBuffer2); CopyToBgTilemapBuffer(2, sMatchCallUI_Tilemap, 0, 0); CopyBgTilemapBufferToVram(2); CopyPaletteIntoBufferUnfaded(sMatchCallUI_Pal, 0x20, 0x20); CopyBgTilemapBufferToVram(2); return LT_INC_AND_PAUSE; case 1: if (FreeTempTileDataBuffersIfPossible()) return LT_PAUSE; BgDmaFill(1, 0, 0, 1); SetBgTilemapBuffer(1, gfx->bgTilemapBuffer1); FillBgTilemapBufferRect_Palette0(1, 0x1000, 0, 0, 32, 20); CopyPaletteIntoBufferUnfaded(sCallWindow_Pal, 0x10, 0x20); CopyBgTilemapBufferToVram(1); return LT_INC_AND_PAUSE; case 2: if (FreeTempTileDataBuffersIfPossible()) return LT_PAUSE; LoadCallWindowAndFade(gfx); DecompressAndCopyTileDataToVram(3, sPokeball_Gfx, 0, 0, 0); CopyPaletteIntoBufferUnfaded(sListWindow_Pal, 0x30, 0x20); CopyPaletteIntoBufferUnfaded(sPokeball_Pal, 0x50, 0x20); return LT_INC_AND_PAUSE; case 3: if (FreeTempTileDataBuffersIfPossible() || !IsMatchCallListInitFinished()) return LT_PAUSE; CreateMatchCallList(); return LT_INC_AND_PAUSE; case 4: if (IsCreatePokenavListTaskActive()) return LT_PAUSE; DrawMatchCallLeftColumnWindows(gfx); return LT_INC_AND_PAUSE; case 5: UpdateMatchCallInfoBox(gfx); PrintMatchCallLocation(gfx, 0); return LT_INC_AND_PAUSE; case 6: ChangeBgX(1, 0, BG_COORD_SET); ChangeBgY(1, 0, BG_COORD_SET); ShowBg(2); ShowBg(3); ShowBg(1); AllocMatchCallSprites(); LoadLeftHeaderGfxForIndex(3); ShowLeftHeaderGfx(POKENAV_GFX_MATCH_CALL_MENU, 1, 0); PokenavFadeScreen(POKENAV_FADE_FROM_BLACK); return LT_INC_AND_PAUSE; case 7: if (IsPaletteFadeActive() || AreLeftHeaderSpritesMoving()) return LT_PAUSE; SetPokeballIconsFlashing(TRUE); return LT_FINISH; default: return LT_FINISH; } } static u32 MatchCallListCursorDown(s32 state) { struct Pokenav_MatchCallGfx *gfx = GetSubstructPtr(POKENAV_SUBSTRUCT_MATCH_CALL_OPEN); switch (state) { case 0: switch (PokenavList_MoveCursorDown()) { case 0: break; case 1: PlaySE(SE_SELECT); return LT_SET_STATE(2); case 2: PlaySE(SE_SELECT); // fall through default: return LT_INC_AND_PAUSE; } break; case 1: if (PokenavList_IsMoveWindowTaskActive()) return LT_PAUSE; PrintMatchCallLocation(gfx, 0); return LT_INC_AND_PAUSE; case 2: PrintMatchCallLocation(gfx, 0); return LT_INC_AND_PAUSE; case 3: if (IsDma3ManagerBusyWithBgCopy()) return LT_PAUSE; break; } return LT_FINISH; } static u32 MatchCallListCursorUp(s32 state) { struct Pokenav_MatchCallGfx *gfx = GetSubstructPtr(POKENAV_SUBSTRUCT_MATCH_CALL_OPEN); switch (state) { case 0: switch (PokenavList_MoveCursorUp()) { case 0: break; case 1: PlaySE(SE_SELECT); return LT_SET_STATE(2); case 2: PlaySE(SE_SELECT); // fall through default: return LT_INC_AND_PAUSE; } break; case 1: if (PokenavList_IsMoveWindowTaskActive()) return LT_PAUSE; PrintMatchCallLocation(gfx, 0); return LT_INC_AND_PAUSE; case 2: PrintMatchCallLocation(gfx, 0); return LT_INC_AND_PAUSE; case 3: if (IsDma3ManagerBusyWithBgCopy()) return LT_PAUSE; break; } return LT_FINISH; } static u32 MatchCallListPageDown(s32 state) { struct Pokenav_MatchCallGfx *gfx = GetSubstructPtr(POKENAV_SUBSTRUCT_MATCH_CALL_OPEN); switch (state) { case 0: switch (PokenavList_PageDown()) { case 0: break; case 1: PlaySE(SE_SELECT); return LT_SET_STATE(2); case 2: PlaySE(SE_SELECT); // fall through default: return LT_INC_AND_PAUSE; } break; case 1: if (PokenavList_IsMoveWindowTaskActive()) return LT_PAUSE; PrintMatchCallLocation(gfx, 0); return LT_INC_AND_PAUSE; case 2: PrintMatchCallLocation(gfx, 0); return LT_INC_AND_PAUSE; case 3: if (IsDma3ManagerBusyWithBgCopy()) return LT_PAUSE; break; } return LT_FINISH; } static u32 MatchCallListPageUp(s32 state) { struct Pokenav_MatchCallGfx *gfx = GetSubstructPtr(POKENAV_SUBSTRUCT_MATCH_CALL_OPEN); switch (state) { case 0: switch (PokenavList_PageUp()) { case 0: break; case 1: PlaySE(SE_SELECT); return LT_SET_STATE(2); case 2: PlaySE(SE_SELECT); // fall through default: return LT_INC_AND_PAUSE; } break; case 1: if (PokenavList_IsMoveWindowTaskActive()) return LT_PAUSE; PrintMatchCallLocation(gfx, 0); return LT_INC_AND_PAUSE; case 2: PrintMatchCallLocation(gfx, 0); return LT_INC_AND_PAUSE; case 3: if (IsDma3ManagerBusyWithBgCopy()) return LT_PAUSE; break; } return LT_FINISH; } static u32 SelectMatchCallEntry(s32 state) { struct Pokenav_MatchCallGfx *gfx = GetSubstructPtr(POKENAV_SUBSTRUCT_MATCH_CALL_OPEN); switch (state) { case 0: PlaySE(SE_SELECT); PrintMatchCallSelectionOptions(gfx); PrintHelpBarText(HELPBAR_MC_CALL_MENU); return LT_INC_AND_PAUSE; case 1: if (ShowOptionsCursor(gfx)) return LT_PAUSE; break; } return LT_FINISH; } static u32 MoveMatchCallOptionsCursor(s32 state) { struct Pokenav_MatchCallGfx *gfx; u16 cursorPos; PlaySE(SE_SELECT); gfx = GetSubstructPtr(POKENAV_SUBSTRUCT_MATCH_CALL_OPEN); cursorPos = GetMatchCallOptionCursorPos(); UpdateCursorGfxPos(gfx, cursorPos); return LT_FINISH; } static u32 CancelMatchCallSelection(s32 state) { struct Pokenav_MatchCallGfx *gfx = GetSubstructPtr(POKENAV_SUBSTRUCT_MATCH_CALL_OPEN); switch (state) { case 0: PlaySE(SE_SELECT); UpdateWindowsReturnToTrainerList(gfx); PrintHelpBarText(HELPBAR_MC_TRAINER_LIST); return LT_INC_AND_PAUSE; case 1: if (IsDma3ManagerBusyWithBgCopy1(gfx)) return LT_PAUSE; break; } return LT_FINISH; } static u32 DoMatchCallMessage(s32 state) { struct Pokenav_MatchCallGfx *gfx = GetSubstructPtr(POKENAV_SUBSTRUCT_MATCH_CALL_OPEN); switch (state) { case 0: PokenavList_ToggleVerticalArrows(TRUE); DrawMsgBoxForMatchCallMsg(gfx); return LT_INC_AND_PAUSE; case 1: if (IsDma3ManagerBusyWithBgCopy2(gfx)) return LT_PAUSE; PrintCallingDots(gfx); PlaySE(SE_POKENAV_CALL); gfx->skipHangUpSE = FALSE; return LT_INC_AND_PAUSE; case 2: if (WaitForCallingDotsText(gfx)) return LT_PAUSE; PrintMatchCallMessage(gfx); return LT_INC_AND_PAUSE; case 3: if (WaitForMatchCallMessageText(gfx)) return LT_PAUSE; break; } return LT_FINISH; } static u32 DoTrainerCloseByMessage(s32 state) { struct Pokenav_MatchCallGfx *gfx = GetSubstructPtr(POKENAV_SUBSTRUCT_MATCH_CALL_OPEN); switch (state) { case 0: PlaySE(SE_SELECT); DrawMsgBoxForCloseByMsg(gfx); PokenavList_ToggleVerticalArrows(TRUE); gfx->skipHangUpSE = TRUE; return LT_INC_AND_PAUSE; case 1: if (IsDma3ManagerBusyWithBgCopy2(gfx)) return LT_PAUSE; PrintTrainerIsCloseBy(gfx); return LT_INC_AND_PAUSE; case 2: if (WaitForTrainerIsCloseByText(gfx)) return LT_PAUSE; break; } return LT_FINISH; } static u32 CloseMatchCallMessage(s32 state) { struct Pokenav_MatchCallGfx *gfx = GetSubstructPtr(POKENAV_SUBSTRUCT_MATCH_CALL_OPEN); u32 result = LT_INC_AND_PAUSE; switch (state) { case 0: if (!gfx->skipHangUpSE) PlaySE(SE_POKENAV_HANG_UP); PlaySE(SE_SELECT); break; case 1: EraseCallMessageBox(gfx); break; case 2: if (WaitForCallMessageBoxErase(gfx)) result = LT_PAUSE; break; case 3: UpdateWindowsReturnToTrainerList(gfx); break; case 4: if (IsDma3ManagerBusyWithBgCopy1(gfx)) result = LT_PAUSE; PrintHelpBarText(HELPBAR_MC_TRAINER_LIST); break; case 5: if (WaitForHelpBar()) { result = LT_PAUSE; } else { if (gfx->newRematchRequest) { // This call was a new rematch request, // add the Pokéball icon to their entry PokenavList_DrawCurrentItemIcon(); result = LT_INC_AND_CONTINUE; } else { PokenavList_ToggleVerticalArrows(FALSE); result = LT_FINISH; } } break; case 6: if (IsDma3ManagerBusyWithBgCopy()) { result = LT_PAUSE; } else { PokenavList_ToggleVerticalArrows(FALSE); result = LT_FINISH; } break; } return result; } static u32 ShowCheckPage(s32 state) { struct Pokenav_MatchCallGfx *gfx = GetSubstructPtr(POKENAV_SUBSTRUCT_MATCH_CALL_OPEN); switch (state) { case 0: PlaySE(SE_SELECT); PokenavList_EraseListForCheckPage(); UpdateWindowsToShowCheckPage(gfx); return LT_INC_AND_PAUSE; case 1: if (PokenavList_IsTaskActive() || IsDma3ManagerBusyWithBgCopy1(gfx)) return LT_PAUSE; PrintHelpBarText(HELPBAR_MC_CHECK_PAGE); return LT_INC_AND_PAUSE; case 2: PrintCheckPageInfo(0); LoadCheckPageTrainerPic(gfx); return LT_INC_AND_PAUSE; case 3: if (PokenavList_IsTaskActive() || WaitForTrainerPic(gfx) || WaitForHelpBar()) return LT_PAUSE; break; } return LT_FINISH; } static u32 ShowCheckPageDown(s32 state) { int topId; int delta; struct Pokenav_MatchCallGfx *gfx = GetSubstructPtr(POKENAV_SUBSTRUCT_MATCH_CALL_OPEN); switch (state) { case 0: topId = PokenavList_GetTopIndex(); delta = GetIndexDeltaOfNextCheckPageDown(topId); if (delta) { PlaySE(SE_SELECT); gfx->pageDelta = delta; TrainerPicSlideOffscreen(gfx); return LT_INC_AND_PAUSE; } break; case 1: if (WaitForTrainerPic(gfx)) return LT_PAUSE; PrintMatchCallLocation(gfx, gfx->pageDelta); return LT_INC_AND_PAUSE; case 2: PrintCheckPageInfo(gfx->pageDelta); return LT_INC_AND_PAUSE; case 3: LoadCheckPageTrainerPic(gfx); return LT_INC_AND_PAUSE; case 4: if (PokenavList_IsTaskActive() || WaitForTrainerPic(gfx)) return LT_PAUSE; break; } return LT_FINISH; } static u32 ExitCheckPage(s32 state) { struct Pokenav_MatchCallGfx *gfx = GetSubstructPtr(POKENAV_SUBSTRUCT_MATCH_CALL_OPEN); switch (state) { case 0: PlaySE(SE_SELECT); TrainerPicSlideOffscreen(gfx); PokenavList_ReshowListFromCheckPage(); return LT_INC_AND_PAUSE; case 1: if (PokenavList_IsTaskActive() || WaitForTrainerPic(gfx)) return LT_PAUSE; PrintHelpBarText(HELPBAR_MC_TRAINER_LIST); UpdateMatchCallInfoBox(gfx); return LT_INC_AND_PAUSE; case 2: if (IsDma3ManagerBusyWithBgCopy()) return LT_PAUSE; break; } return LT_FINISH; } static u32 ShowCheckPageUp(s32 state) { int topId; int delta; struct Pokenav_MatchCallGfx *gfx = GetSubstructPtr(POKENAV_SUBSTRUCT_MATCH_CALL_OPEN); switch (state) { case 0: topId = PokenavList_GetTopIndex(); delta = GetIndexDeltaOfNextCheckPageUp(topId); if (delta) { PlaySE(SE_SELECT); gfx->pageDelta = delta; TrainerPicSlideOffscreen(gfx); return LT_INC_AND_PAUSE; } break; case 1: if (WaitForTrainerPic(gfx)) return LT_PAUSE; PrintMatchCallLocation(gfx, gfx->pageDelta); return LT_INC_AND_PAUSE; case 2: PrintCheckPageInfo(gfx->pageDelta); return LT_INC_AND_PAUSE; case 3: LoadCheckPageTrainerPic(gfx); return LT_INC_AND_PAUSE; case 4: if (PokenavList_IsTaskActive() || WaitForTrainerPic(gfx)) return LT_PAUSE; break; } return LT_FINISH; } static u32 ExitMatchCall(s32 state) { switch (state) { case 0: PlaySE(SE_SELECT); SetPokeballIconsFlashing(FALSE); PokenavFadeScreen(POKENAV_FADE_TO_BLACK); SlideMenuHeaderDown(); return LT_INC_AND_PAUSE; case 1: if (IsPaletteFadeActive() || MainMenuLoopedTaskIsBusy()) return LT_PAUSE; SetLeftHeaderSpritesInvisibility(); break; } return LT_FINISH; } static void CreateMatchCallList(void) { struct PokenavListTemplate template; template.list = (struct PokenavListItem *)GetMatchCallList(); template.count = GetNumberRegistered(); template.itemSize = sizeof(struct PokenavListItem); template.startIndex = 0; template.item_X = 13; template.windowWidth = 16; template.listTop = 1; template.maxShowed = 8; template.fillValue = 3; template.fontId = FONT_NARROW; template.bufferItemFunc = (PokenavListBufferItemFunc)BufferMatchCallNameAndDesc; template.iconDrawFunc = TryDrawRematchPokeballIcon; CreatePokenavList(&sMatchCallBgTemplates[2], &template, 2); CreateTask(Task_FlashPokeballIcons, 7); } static void DestroyMatchCallList(void) { DestroyPokenavList(); DestroyTask(FindTaskIdByFunc(Task_FlashPokeballIcons)); } #define tSinIdx data[0] #define tSinVal data[1] #define tActive data[15] static void SetPokeballIconsFlashing(bool32 active) { u8 taskId = FindTaskIdByFunc(Task_FlashPokeballIcons); if (taskId != TASK_NONE) gTasks[taskId].tActive = active; } static void Task_FlashPokeballIcons(u8 taskId) { s16 *data = gTasks[taskId].data; if (tActive) { tSinIdx += 4; tSinIdx &= 0x7F; tSinVal = gSineTable[tSinIdx] >> 4; PokenavCopyPalette(sPokeball_Pal, &sPokeball_Pal[0x10], 0x10, 0x10, tSinVal, &gPlttBufferUnfaded[0x50]); if (!gPaletteFade.active) CpuCopy32(&gPlttBufferUnfaded[0x50], &gPlttBufferFaded[0x50], 0x20); } } #undef tSinIdx #undef tSinVal #undef tActive enum { POKEBALL_ICON_TOP = 0x5000, POKEBALL_ICON_BOTTOM, POKEBALL_ICON_EMPTY, }; static void TryDrawRematchPokeballIcon(u16 windowId, u32 rematchId, u32 tileOffset) { u8 bg = GetWindowAttribute(windowId, WINDOW_BG); u16 *tilemap = GetBgTilemapBuffer(bg); tilemap += tileOffset * 64 + 0x1D; if (ShouldDrawRematchPokeballIcon(rematchId)) { tilemap[0] = POKEBALL_ICON_TOP; tilemap[0x20] = POKEBALL_ICON_BOTTOM; } else { tilemap[0] = POKEBALL_ICON_EMPTY; tilemap[0x20] = POKEBALL_ICON_EMPTY; } } void ClearRematchPokeballIcon(u16 windowId, u32 tileOffset) { u8 bg = GetWindowAttribute(windowId, WINDOW_BG); u16 *tilemap = GetBgTilemapBuffer(bg); tilemap += tileOffset * 64 + 0x1D; tilemap[0] = POKEBALL_ICON_EMPTY; tilemap[0x20] = POKEBALL_ICON_EMPTY; } static void DrawMatchCallLeftColumnWindows(struct Pokenav_MatchCallGfx *gfx) { gfx->locWindowId = AddWindow(&sMatchCallLocationWindowTemplate); gfx->infoBoxWindowId = AddWindow(&sMatchCallInfoBoxWindowTemplate); FillWindowPixelBuffer(gfx->locWindowId, PIXEL_FILL(1)); PutWindowTilemap(gfx->locWindowId); FillWindowPixelBuffer(gfx->infoBoxWindowId, PIXEL_FILL(1)); PutWindowTilemap(gfx->infoBoxWindowId); CopyWindowToVram(gfx->locWindowId, COPYWIN_MAP); } static void UpdateMatchCallInfoBox(struct Pokenav_MatchCallGfx *gfx) { FillWindowPixelBuffer(gfx->infoBoxWindowId, PIXEL_FILL(1)); PrintNumberRegisteredLabel(gfx->infoBoxWindowId); PrintNumberRegistered(gfx->infoBoxWindowId); PrintNumberOfBattlesLabel(gfx->infoBoxWindowId); PrintNumberOfBattles(gfx->infoBoxWindowId); CopyWindowToVram(gfx->infoBoxWindowId, COPYWIN_GFX); } static void PrintNumberRegisteredLabel(u16 windowId) { PrintMatchCallInfoLabel(windowId, gText_NumberRegistered, 0); } static void PrintNumberRegistered(u16 windowId) { u8 str[3]; ConvertIntToDecimalStringN(str, GetNumberRegistered(), STR_CONV_MODE_LEFT_ALIGN, 3); PrintMatchCallInfoNumber(windowId, str, 1); } static void PrintNumberOfBattlesLabel(u16 windowId) { PrintMatchCallInfoLabel(windowId, gText_NumberOfBattles, 2); } static void PrintNumberOfBattles(u16 windowId) { u8 str[5]; int numTrainerBattles = GetGameStat(GAME_STAT_TRAINER_BATTLES); if (numTrainerBattles > 99999) numTrainerBattles = 99999; ConvertIntToDecimalStringN(str, numTrainerBattles, STR_CONV_MODE_LEFT_ALIGN, 5); PrintMatchCallInfoNumber(windowId, str, 3); } static void PrintMatchCallInfoLabel(u16 windowId, const u8 *str, int top) { int y = top * 16 + 1; AddTextPrinterParameterized(windowId, FONT_NARROW, str, 2, y, TEXT_SKIP_DRAW, NULL); } static void PrintMatchCallInfoNumber(u16 windowId, const u8 *str, int top) { int x = GetStringRightAlignXOffset(FONT_NARROW, str, 86); int y = top * 16 + 1; AddTextPrinterParameterized(windowId, FONT_NARROW, str, x, y, TEXT_SKIP_DRAW, NULL); } static void PrintMatchCallLocation(struct Pokenav_MatchCallGfx *gfx, int delta) { u8 mapName[32]; int x; int index = PokenavList_GetSelectedIndex() + delta; int mapSec = GetMatchCallMapSec(index); if (mapSec != MAPSEC_NONE) GetMapName(mapName, mapSec, 0); else StringCopy(mapName, gText_Unknown); x = GetStringCenterAlignXOffset(FONT_NARROW, mapName, 88); FillWindowPixelBuffer(gfx->locWindowId, PIXEL_FILL(1)); AddTextPrinterParameterized(gfx->locWindowId, FONT_NARROW, mapName, x, 1, 0, NULL); } static void PrintMatchCallSelectionOptions(struct Pokenav_MatchCallGfx *gfx) { u32 i; FillWindowPixelBuffer(gfx->infoBoxWindowId, PIXEL_FILL(1)); for (i = 0; i < MATCH_CALL_OPTION_COUNT; i++) { int optionText = GetMatchCallOptionId(i); if (optionText == MATCH_CALL_OPTION_COUNT) break; AddTextPrinterParameterized(gfx->infoBoxWindowId, FONT_NARROW, sMatchCallOptionTexts[optionText], 16, i * 16 + 1, TEXT_SKIP_DRAW, NULL); } CopyWindowToVram(gfx->infoBoxWindowId, COPYWIN_GFX); } static bool32 ShowOptionsCursor(struct Pokenav_MatchCallGfx *gfx) { if (!IsDma3ManagerBusyWithBgCopy()) { CreateOptionsCursorSprite(gfx, GetMatchCallOptionCursorPos()); return FALSE; } return TRUE; } static void UpdateWindowsReturnToTrainerList(struct Pokenav_MatchCallGfx *gfx) { CloseMatchCallSelectOptionsWindow(gfx); UpdateMatchCallInfoBox(gfx); } static bool32 IsDma3ManagerBusyWithBgCopy1(struct Pokenav_MatchCallGfx *gfx) { return IsDma3ManagerBusyWithBgCopy(); } static void UpdateWindowsToShowCheckPage(struct Pokenav_MatchCallGfx *gfx) { CloseMatchCallSelectOptionsWindow(gfx); FillWindowPixelBuffer(gfx->infoBoxWindowId, PIXEL_FILL(1)); CopyWindowToVram(gfx->infoBoxWindowId, COPYWIN_GFX); } static void LoadCallWindowAndFade(struct Pokenav_MatchCallGfx *gfx) { gfx->msgBoxWindowId = AddWindow(&sCallMsgBoxWindowTemplate); LoadMatchCallWindowGfx(gfx->msgBoxWindowId, 1, 4); FadeToBlackExceptPrimary(); } static void DrawMsgBoxForMatchCallMsg(struct Pokenav_MatchCallGfx *gfx) { struct Sprite *sprite; LoadMatchCallWindowGfx(gfx->msgBoxWindowId, 1, 4); DrawMatchCallTextBoxBorder(gfx->msgBoxWindowId, 1, 4); FillWindowPixelBuffer(gfx->msgBoxWindowId, PIXEL_FILL(1)); PutWindowTilemap(gfx->msgBoxWindowId); CopyWindowToVram(gfx->msgBoxWindowId, COPYWIN_FULL); sprite = GetSpinningPokenavSprite(); sprite->x = 24; sprite->y = 112; sprite->y2 = 0; } static void DrawMsgBoxForCloseByMsg(struct Pokenav_MatchCallGfx *gfx) { LoadUserWindowBorderGfx(gfx->msgBoxWindowId, 1, 0x40); DrawTextBorderOuter(gfx->msgBoxWindowId, 1, 4); FillWindowPixelBuffer(gfx->msgBoxWindowId, PIXEL_FILL(1)); PutWindowTilemap(gfx->msgBoxWindowId); CopyWindowToVram(gfx->msgBoxWindowId, COPYWIN_FULL); } static bool32 IsDma3ManagerBusyWithBgCopy2(struct Pokenav_MatchCallGfx *gfx) { return IsDma3ManagerBusyWithBgCopy(); } static void PrintCallingDots(struct Pokenav_MatchCallGfx *gfx) { AddTextPrinterParameterized(gfx->msgBoxWindowId, FONT_NORMAL, sText_CallingDots, 32, 1, 1, NULL); } static bool32 WaitForCallingDotsText(struct Pokenav_MatchCallGfx *gfx) { RunTextPrinters(); return IsTextPrinterActive(gfx->msgBoxWindowId); } static void PrintTrainerIsCloseBy(struct Pokenav_MatchCallGfx *gfx) { AddTextPrinterParameterized(gfx->msgBoxWindowId, FONT_NORMAL, gText_TrainerCloseBy, 0, 1, 1, NULL); } static bool32 WaitForTrainerIsCloseByText(struct Pokenav_MatchCallGfx *gfx) { RunTextPrinters(); return IsTextPrinterActive(gfx->msgBoxWindowId); } static void PrintMatchCallMessage(struct Pokenav_MatchCallGfx *gfx) { int index = PokenavList_GetSelectedIndex(); const u8 *str = GetMatchCallMessageText(index, &gfx->newRematchRequest); u8 speed = GetPlayerTextSpeedDelay(); AddTextPrinterParameterized(gfx->msgBoxWindowId, FONT_NORMAL, str, 32, 1, speed, NULL); } static bool32 WaitForMatchCallMessageText(struct Pokenav_MatchCallGfx *gfx) { if (JOY_HELD(A_BUTTON)) gTextFlags.canABSpeedUpPrint = TRUE; else gTextFlags.canABSpeedUpPrint = FALSE; RunTextPrinters(); return IsTextPrinterActive(gfx->msgBoxWindowId); } static void EraseCallMessageBox(struct Pokenav_MatchCallGfx *gfx) { HideSpinningPokenavSprite(); FillBgTilemapBufferRect_Palette0(1, 0, 0, 0, 32, 20); CopyBgTilemapBufferToVram(1); } static bool32 WaitForCallMessageBoxErase(struct Pokenav_MatchCallGfx *gfx) { return IsDma3ManagerBusyWithBgCopy(); } static void AllocMatchCallSprites(void) { int i; u8 paletteNum; struct SpriteSheet spriteSheet; struct Pokenav_MatchCallGfx *gfx = GetSubstructPtr(POKENAV_SUBSTRUCT_MATCH_CALL_OPEN); // Load options cursor gfx for (i = 0; i < ARRAY_COUNT(sOptionsCursorSpriteSheets); i++) LoadCompressedSpriteSheet(&sOptionsCursorSpriteSheets[i]); Pokenav_AllocAndLoadPalettes(sOptionsCursorSpritePalettes); gfx->optionsCursorSprite = NULL; // Load trainer pic gfx spriteSheet.data = gfx->trainerPicGfx; spriteSheet.size = sizeof(gfx->trainerPicGfx); spriteSheet.tag = GFXTAG_TRAINER_PIC; gfx->trainerPicGfxPtr = (u8 *)OBJ_VRAM0 + LoadSpriteSheet(&spriteSheet) * 0x20; paletteNum = AllocSpritePalette(PALTAG_TRAINER_PIC); gfx->trainerPicPalOffset = 0x100 + paletteNum * 0x10; gfx->trainerPicSprite = CreateTrainerPicSprite(); gfx->trainerPicSprite->invisible = TRUE; } static void FreeMatchCallSprites(void) { struct Pokenav_MatchCallGfx *gfx = GetSubstructPtr(POKENAV_SUBSTRUCT_MATCH_CALL_OPEN); if (gfx->optionsCursorSprite) DestroySprite(gfx->optionsCursorSprite); if (gfx->trainerPicSprite) DestroySprite(gfx->trainerPicSprite); FreeSpriteTilesByTag(GFXTAG_TRAINER_PIC); FreeSpriteTilesByTag(GFXTAG_CURSOR); FreeSpritePaletteByTag(PALTAG_CURSOR); FreeSpritePaletteByTag(PALTAG_TRAINER_PIC); } static void CreateOptionsCursorSprite(struct Pokenav_MatchCallGfx *gfx, int top) { if (!gfx->optionsCursorSprite) { u8 spriteId = CreateSprite(&sOptionsCursorSpriteTemplate, 4, 80, 5); gfx->optionsCursorSprite = &gSprites[spriteId]; UpdateCursorGfxPos(gfx, top); } } static void CloseMatchCallSelectOptionsWindow(struct Pokenav_MatchCallGfx *gfx) { DestroySprite(gfx->optionsCursorSprite); gfx->optionsCursorSprite = NULL; } static void UpdateCursorGfxPos(struct Pokenav_MatchCallGfx *gfx, int top) { gfx->optionsCursorSprite->y2 = top * 16; } static void SpriteCB_OptionsCursor(struct Sprite *sprite) { if (++sprite->data[0] > 3) { sprite->data[0] = 0; sprite->x2 = (sprite->x2 + 1) & 7; } } static struct Sprite *CreateTrainerPicSprite(void) { u8 spriteId = CreateSprite(&sTrainerPicSpriteTemplate, 44, 104, 6); return &gSprites[spriteId]; } static void LoadCheckPageTrainerPic(struct Pokenav_MatchCallGfx *gfx) { u16 cursor; int trainerPic = GetMatchCallTrainerPic(PokenavList_GetSelectedIndex()); if (trainerPic >= 0) { DecompressPicFromTable(&gTrainerFrontPicTable[trainerPic], gfx->trainerPicGfx, SPECIES_NONE); LZ77UnCompWram(gTrainerFrontPicPaletteTable[trainerPic].data, gfx->trainerPicPal); cursor = RequestDma3Copy(gfx->trainerPicGfx, gfx->trainerPicGfxPtr, sizeof(gfx->trainerPicGfx), 1); LoadPalette(gfx->trainerPicPal, gfx->trainerPicPalOffset, sizeof(gfx->trainerPicPal)); gfx->trainerPicSprite->data[0] = 0; gfx->trainerPicSprite->data[7] = cursor; gfx->trainerPicSprite->callback = SpriteCB_TrainerPicSlideOnscreen; } } static void TrainerPicSlideOffscreen(struct Pokenav_MatchCallGfx *gfx) { gfx->trainerPicSprite->callback = SpriteCB_TrainerPicSlideOffscreen; } static bool32 WaitForTrainerPic(struct Pokenav_MatchCallGfx *gfx) { return gfx->trainerPicSprite->callback != SpriteCallbackDummy; } static void SpriteCB_TrainerPicSlideOnscreen(struct Sprite *sprite) { switch (sprite->data[0]) { case 0: if (CheckForSpaceForDma3Request(sprite->data[7]) != -1) { sprite->x2 = -80; sprite->invisible = FALSE; sprite->data[0]++; } break; case 1: sprite->x2 += 8; if (sprite->x2 >= 0) { sprite->x2 = 0; sprite->callback = SpriteCallbackDummy; } break; } } static void SpriteCB_TrainerPicSlideOffscreen(struct Sprite *sprite) { sprite->x2 -= 8; if (sprite->x2 <= -80) { sprite->invisible = TRUE; sprite->callback = SpriteCallbackDummy; } }