#include "global.h" #include "trainer_card.h" #include "constants/songs.h" #include "contest_util.h" #include "easy_chat.h" #include "event_data.h" #include "ewram.h" #include "field_effect.h" #include "graphics.h" #include "link.h" #include "m4a.h" #include "main.h" #include "menu.h" #include "money.h" #include "overworld.h" #include "palette.h" #include "pokedex.h" #include "scanline_effect.h" #include "sound.h" #include "sprite.h" #include "string_util.h" #include "strings2.h" #include "task.h" #include "util.h" typedef void (*Callback)(void); struct Struct2000000 { /*0x00*/ u8 current_state; /*0x01*/ bool8 isShowingLinkCard; /*0x02*/ u8 starCount; /*0x03*/ bool8 backSideShown; /*0x04*/ u8 var_4; /*0x05*/ bool8 showColon; /*0x06*/ u8 frameCounter; /* Used to flash colon */ /*0x07*/ bool8 showPokedexCount; /*0x08*/ bool8 showHallOfFame; /*0x09*/ bool8 showLinkBattleStatus; /*0x0A*/ bool8 showBattleTowerStatus; /*0x0B*/ bool8 showContestRecord; /*0x0C*/ bool8 showMixingRecord; /*0x0D*/ bool8 showTradingRecord; /*0x0E*/ bool8 ownedBadges[8]; /*0x16*/ u8 filler_16[10]; /*0x20*/ u8 easyChatPhrase[4][0x10]; /*0x60*/ Callback *var_60; /*0x64*/ struct TrainerCard displayedCard; /*0x9C*/ u8 language; // 0x9C }; extern struct LinkPlayerObjectEvent gLinkPlayerObjectEvents[]; EWRAM_DATA struct TrainerCard gTrainerCards[4] = {0}; extern const u16 gUnknown_083B5F0C[]; extern const u16 gBadgesPalette[]; extern const u16 gUnknown_083B5F4C[]; extern const u16 gUnknown_083B5F6C[]; extern const u16 gTrainerCardBadgesMap[][4]; const u8 gBadgesTiles[] = INCBIN_U8("graphics/trainer_card/badges.4bpp"); struct Struct2000000 * const gTrainerCardPtr = (struct Struct2000000 *)gSharedMem; #if DEBUG const struct TrainerCard sTestTrainerCard = { .gender = FEMALE, .stars = 4, .hasPokedex = TRUE, .var_3 = TRUE, .var_4 = TRUE, .firstHallOfFameA = 999, .firstHallOfFameB = 99, .firstHallOfFameC = 99, .pokedexSeen = 411, .trainerId = 12345, .playTimeHours = 99, .playTimeMinutes = 99, .linkBattleWins = 9999, .linkBattleLosses = 9999, .battleTowerWins = 9999, .battleTowerLosses = 9999, .contestsWithFriends = 999, .pokeblocksWithFriends = 0xFFFF, .pokemonTrades = 0xFFFF, .money = 99999, .var_28 = {1, 2, 3, 4}, .playerName = DTR("てすと", "TEST"), }; #endif bool8 TrainerCard_Init(struct Task *); bool8 TrainerCard_WaitForFadeInToFinish(struct Task *); bool8 TrainerCard_WaitForKeys(struct Task *); bool8 TrainerCard_StartFlipAntimation(struct Task *); bool8 TrainerCard_WaitForFlipToFinish(struct Task *); bool8 TrainerCard_FadeOut(struct Task *); bool8 TrainerCard_WaitForFadeOutToFinishAndQuit(struct Task *); bool8 (*const TrainerCard_StateMachine[])(struct Task *) = { TrainerCard_Init, TrainerCard_WaitForFadeInToFinish, TrainerCard_WaitForKeys, TrainerCard_StartFlipAntimation, TrainerCard_WaitForFlipToFinish, TrainerCard_FadeOut, TrainerCard_WaitForFadeOutToFinishAndQuit, }; bool8 TrainerCard_InitFlipAnimation(struct Task *); bool8 TrainerCard_ScaleDownFlipAnimation(struct Task *); bool8 TrainerCard_SwitchToNewSide(struct Task *); bool8 TrainerCard_ScaleUpFlipAnimation(struct Task *); bool8 TrainerCard_FinishFlipAnimation(struct Task *); bool8 (*const TrainerCard_FlipAnimationStateMachine[])(struct Task *) = { TrainerCard_InitFlipAnimation, TrainerCard_ScaleDownFlipAnimation, TrainerCard_SwitchToNewSide, TrainerCard_ScaleUpFlipAnimation, TrainerCard_FinishFlipAnimation, }; // FIXME: Other signature than on save_menu_util.h void FormatPlayTime(u8 *playtime, u16 hours, u16 minutes, s16 colon); u16 GetPokedexSeenCount(void); enum { TD_SHOWING_LINK_CARD, TD_CARD_INDEX, TD_CALLBACK, }; static void sub_8093174(void); static void sub_809323C(void); static void sub_8093254(void); static void TrainerCard_InitScreenForPlayer(Callback callBack); static void TrainerCard_InitScreenForLinkPlayer(u8 arg1, Callback callBack); void TrainerCard_FillTrainerCardStruct(void); static void nullsub_60(u8); static u32 sav12_xor_get_clamped_above(u8 index, u32 maxVal); static u8 TrainerCard_GetStarCount(struct TrainerCard *); static void sub_8093534(void); static void sub_8093550(void); static void sub_8093598(void); static void sub_80935EC(void); static void sub_8093610(void); static void sub_8093688(void); static void TrainerCard_FillFlags(void); static void sub_80937A4(void); static void sub_80937BC(void); static void sub_80937D8(void); static void sub_80937F0(void); static void nullsub_15(void); static void sub_8093800(void); static void TrainerCard_CreateStateMachine(void); static void TrainerCard_RunStateMachine(u8 taskId); static void TrainerCard_CreatePrintPlayTimeTask(void); static void TrainerCard_DestoryPlayTimeTask(void); static void TrainerCard_Front_PrintPlayTime(u8 taskId); static void TrainerCard_CreateFlipAnimationTask(void); static u8 TrainerCard_HasFlipAnimationFinished(void); static void TrainerCard_RunFlipAnimationStateMachine(u8 taskId); static void TrainerCard_FlipAnimationHBlankCallback(void); static void TrainerCard_DrawCard(void); static void TrainerCard_DrawCardFront(void); static void TrainerCard_DrawCardBack(void); static void TrainerCard_ResetOffsetRegisters(void); static void TrainerCard_CopyGraphics(void); static void TrainerCard_LoadPalettes(void); static void TrainerCard_LoadTrainerGraphics(void); static void TrainerCard_LoadCardTileMap(void); static void sub_8093F48(void); static void sub_8093F64(void); static void TrainerCard_LoadTrainerTilemap(void); static void TrainerCard_DrawStars(void); static void TrainerCard_DisplayBadges(void); static void TrainerCard_ClearTrainerGraphics(void); static void TrainerCard_ClearPokedexLabel(void); static void TrainerCard_Front_PrintTexts(void); static void TrainerCard_Back_PrintTexts(void); static void TrainerCard_Front_PrintTrainerID(void); static void TrainerCard_Front_PrintMoney(void); static void TrainerCard_Front_PrintPokedexCount(void); static void TrainerCard_Front_GetPlayTimeString(u8 *arg1, s16 colon); static void TrainerCard_PrintEasyChatPhrase(void); static void TrainerCard_Back_PrintName(void); static void TrainerCard_Back_PrintHallOfFameTime_Label(void); static void TrainerCard_Back_PrintHallOfFameTime(void); static void TrainerCard_Back_PrintLinkBattlesLabel(void); static void TrainerCard_Back_PrintLinkBattles(void); static void TrainerCard_Back_PrintBattleTower_Label(void); static void TrainerCard_Back_PrintBattleTower(void); static void TrainerCard_Back_PrintLinkContests_Label(void); static void TrainerCard_Back_PrintLinkContests(void); static void TrainerCard_Back_PrintLinkPokeblocks_Label(void); static void TrainerCard_Back_PrintLinkPokeblocks(void); static void TrainerCard_Back_PrintPokemonTrades_Label(void); static void TrainerCard_Back_PrintPokemonTrades(void); void unref_sub_8094588(u16 left, u16 top); #if DEBUG static u8 gDebug_03000748; #endif void TrainerCard_ShowPlayerCard(Callback arg1) { #if DEBUG gDebug_03000748 = 0; #endif TrainerCard_InitScreenForPlayer(arg1); SetMainCallback2(sub_8093174); gTrainerCardPtr->language = GAME_LANGUAGE; } void TrainerCard_ShowLinkCard(u8 playerIndex, Callback arg2) { #if DEBUG gDebug_03000748 = 0; #endif TrainerCard_InitScreenForLinkPlayer(playerIndex, arg2); SetMainCallback2(sub_8093174); gTrainerCardPtr->language = gLinkPlayers[gLinkPlayerObjectEvents[playerIndex].linkPlayerId].language; } #if DEBUG void debug_sub_80A0710(Callback callback) { gDebug_03000748 = TRUE; TrainerCard_InitScreenForPlayer(callback); SetMainCallback2(sub_8093174); gTrainerCardPtr->language = GAME_LANGUAGE; } void debug_sub_80A073C(Callback callback) { memcpy(&gTrainerCards[0], &sTestTrainerCard, sizeof(struct TrainerCard)); gDebug_03000748=TRUE; TrainerCard_InitScreenForLinkPlayer(0, callback); SetMainCallback2(sub_8093174); gTrainerCardPtr->language = GAME_LANGUAGE; } void debug_sub_80A0780() { int i; for (i = 0; i < 4; i++) memcpy(&gTrainerCards[i], &sTestTrainerCard, sizeof(struct TrainerCard)); } #endif static void sub_8093174(void) { switch (gMain.state) { case 0: sub_8093534(); sub_8093688(); gMain.state++; break; case 1: sub_8093598(); gMain.state++; break; case 2: sub_80935EC(); gMain.state++; break; case 3: sub_8093610(); sub_80937A4(); gMain.state++; break; case 4: sub_80937BC(); gMain.state++; case 5: if (MultistepInitMenuWindowContinue()) gMain.state++; break; case 6: sub_80937F0(); gMain.state++; break; case 7: sub_80937D8(); gMain.state++; break; case 8: nullsub_15(); sub_8093800(); sub_8093550(); SetMainCallback2(sub_809323C); break; } } static void sub_809323C(void) { RunTasks(); AnimateSprites(); BuildOamBuffer(); UpdatePaletteFade(); } static void sub_8093254(void) { LoadOam(); ProcessSpriteCopyRequests(); TransferPlttBuffer(); gTrainerCardPtr->frameCounter++; if (gTrainerCardPtr->frameCounter >= 60) { gTrainerCardPtr->frameCounter = 0; gTrainerCardPtr->showColon ^= 1; } if (gTrainerCardPtr->var_4) DmaCopy16(3, &gScanlineEffectRegBuffers[0], &gScanlineEffectRegBuffers[1], 0x140); } static void TrainerCard_InitScreenForPlayer(Callback callBack) { u8 taskId = CreateTask(nullsub_60, 0xFF); struct Task *task = &gTasks[taskId]; task->data[TD_SHOWING_LINK_CARD] = FALSE; StoreWordInTwoHalfwords(&task->data[TD_CALLBACK], (u32)callBack); } static void TrainerCard_InitScreenForLinkPlayer(u8 arg1, Callback callBack) { u8 taskId = CreateTask(nullsub_60, 0xFF); struct Task *task = &gTasks[taskId]; task->data[TD_SHOWING_LINK_CARD] = TRUE; task->data[TD_CARD_INDEX] = arg1; StoreWordInTwoHalfwords(&task->data[TD_CALLBACK], (u32)callBack); } void TrainerCard_FillTrainerCardStruct(void) { u8 taskId = FindTaskIdByFunc(nullsub_60); struct Task *task = &gTasks[taskId]; gTrainerCardPtr->isShowingLinkCard = task->data[TD_SHOWING_LINK_CARD]; LoadWordFromTwoHalfwords((u16 *)&task->data[TD_CALLBACK], (u32 *)&gTrainerCardPtr->var_60); if (gTrainerCardPtr->isShowingLinkCard) { gTrainerCardPtr->displayedCard = gTrainerCards[task->data[TD_CARD_INDEX]]; } else { TrainerCard_GenerateCardForPlayer(&gTrainerCardPtr->displayedCard); } } static void nullsub_60(u8 taskid) { } void TrainerCard_GenerateCardForPlayer(struct TrainerCard *trainerCard) { u32 playTime; bool32 enteredHallOfFame; bool8 r4; u8 i; trainerCard->gender = gSaveBlock2.playerGender; trainerCard->playTimeHours = gSaveBlock2.playTimeHours; trainerCard->playTimeMinutes = gSaveBlock2.playTimeMinutes; playTime = GetGameStat(GAME_STAT_FIRST_HOF_PLAY_TIME); enteredHallOfFame = GetGameStat(GAME_STAT_ENTERED_HOF); if (!enteredHallOfFame) { playTime = 0; } trainerCard->firstHallOfFameA = playTime >> 16; trainerCard->firstHallOfFameB = (playTime >> 8) & 0xFF; trainerCard->firstHallOfFameC = playTime & 0xFF; trainerCard->hasPokedex = FlagGet(FLAG_SYS_POKEDEX_GET); trainerCard->var_3 = CompletedHoennPokedex(); trainerCard->pokedexSeen = GetPokedexSeenCount(); trainerCard->trainerId = (gSaveBlock2.playerTrainerId[1] << 8) | gSaveBlock2.playerTrainerId[0]; // Link Cable Battles trainerCard->linkBattleWins = sav12_xor_get_clamped_above(GAME_STAT_LINK_BATTLE_WINS, 9999); trainerCard->linkBattleLosses = sav12_xor_get_clamped_above(GAME_STAT_LINK_BATTLE_LOSSES, 9999); // Contests w/ Friends trainerCard->contestsWithFriends = sav12_xor_get_clamped_above(GAME_STAT_WON_LINK_CONTEST, 999); // Pokéblocks w/ Friends trainerCard->pokeblocksWithFriends = sav12_xor_get_clamped_above(GAME_STAT_POKEBLOCKS_WITH_FRIENDS, 0xFFFF); // Pokémon Trades trainerCard->pokemonTrades = sav12_xor_get_clamped_above(GAME_STAT_POKEMON_TRADES, 0xFFFF); // Battle Tower trainerCard->battleTowerWins = gSaveBlock2.battleTower.totalBattleTowerWins; trainerCard->battleTowerLosses = gSaveBlock2.battleTower.bestBattleTowerWinStreak; if (trainerCard->battleTowerWins > 9999) { trainerCard->battleTowerWins = 9999; } if (trainerCard->battleTowerLosses > 9999) { trainerCard->battleTowerLosses = 9999; } r4 = FALSE; if (CountPlayerMuseumPaintings() > 4) { r4 = TRUE; } trainerCard->var_4 = r4; trainerCard->money = gSaveBlock1.money; for (i = 0; i < 4; i++) { trainerCard->var_28[i] = gSaveBlock1.easyChats.unk2B1C[i]; } for (i = 0; i < 8; i++) { trainerCard->playerName[i] = gSaveBlock2.playerName[i]; } trainerCard->stars = TrainerCard_GetStarCount(trainerCard); } u8 sub_80934C4(u8 id) { return gTrainerCards[id].stars; } static u32 sav12_xor_get_clamped_above(u8 index, u32 maxVal) { u32 value = GetGameStat(index); if (value > maxVal) { value = maxVal; } return value; } static u8 TrainerCard_GetStarCount(struct TrainerCard *trainerCard) { u8 value = 0; if (trainerCard->firstHallOfFameA != 0 || trainerCard->firstHallOfFameB != 0 || trainerCard->firstHallOfFameC != 0) { value++; } if (trainerCard->var_3) { value++; } if (trainerCard->battleTowerLosses > 49) { value++; } if (trainerCard->var_4) { value++; } return value; } static void sub_8093534(void) { SetVBlankCallback(NULL); SetHBlankCallback(NULL); REG_DISPCNT = 0; } static void sub_8093550(void) { u16 backup; SetVBlankCallback(sub_8093254); backup = REG_IME; REG_IME = 0; REG_IE |= INTR_FLAG_VBLANK | INTR_FLAG_HBLANK; REG_IME = backup; REG_DISPSTAT |= DISPSTAT_VBLANK_INTR | DISPSTAT_HBLANK_INTR; REG_DISPCNT = DISPCNT_MODE_0 | DISPCNT_OBJ_1D_MAP | DISPCNT_BG_ALL_ON | DISPCNT_OBJ_ON; } void sub_8093598(void) { DmaFill16Large(3, 0, (void *)VRAM, 0x10000, 0x1000); } void sub_80935EC(void) { DmaFill16Defvars(3, 0, (void *)OAM, 0x400); } void sub_8093610(void) { REG_BG0CNT = 0; REG_BG1CNT = 0; REG_BG2CNT = 0; REG_BG3CNT = 0; REG_BG0HOFS = 0; REG_BG0VOFS = 0; REG_BG1HOFS = 0; REG_BG1VOFS = 0; REG_BG2HOFS = 0; REG_BG2VOFS = 0; REG_BG3HOFS = 0; REG_BG3VOFS = 0; REG_BG0CNT = BGCNT_PRIORITY(0) | BGCNT_CHARBASE(2) | BGCNT_SCREENBASE(30) | BGCNT_16COLOR | BGCNT_TXT256x256; REG_BG1CNT = BGCNT_PRIORITY(1) | BGCNT_CHARBASE(0) | BGCNT_SCREENBASE(8) | BGCNT_16COLOR | BGCNT_TXT256x256; REG_BG2CNT = BGCNT_PRIORITY(2) | BGCNT_CHARBASE(0) | BGCNT_SCREENBASE(9) | BGCNT_16COLOR | BGCNT_TXT256x256; REG_BG3CNT = BGCNT_PRIORITY(3) | BGCNT_CHARBASE(0) | BGCNT_SCREENBASE(10) | BGCNT_16COLOR | BGCNT_TXT256x256; } static void sub_8093688(void) { u8 i; TrainerCard_FillTrainerCardStruct(); gTrainerCardPtr->current_state = 0; gTrainerCardPtr->backSideShown = FALSE; gTrainerCardPtr->var_4 = FALSE; gTrainerCardPtr->starCount = gTrainerCardPtr->displayedCard.stars; gTrainerCardPtr->showColon = 0; gTrainerCardPtr->frameCounter = 0; for (i = 0; i < 4; i++) EasyChat_GetWordText(gTrainerCardPtr->easyChatPhrase[i], gTrainerCardPtr->displayedCard.var_28[i]); TrainerCard_FillFlags(); } static void TrainerCard_FillFlags(void) { gTrainerCardPtr->showPokedexCount = 0; gTrainerCardPtr->showHallOfFame = 0; gTrainerCardPtr->showLinkBattleStatus = 0; gTrainerCardPtr->showBattleTowerStatus = 0; gTrainerCardPtr->showContestRecord = 0; gTrainerCardPtr->showMixingRecord = 0; gTrainerCardPtr->showTradingRecord = 0; memset(gTrainerCardPtr->ownedBadges, 0, sizeof(gTrainerCardPtr->ownedBadges)); if (gTrainerCardPtr->displayedCard.hasPokedex) gTrainerCardPtr->showPokedexCount++; if (gTrainerCardPtr->displayedCard.firstHallOfFameA != 0 || gTrainerCardPtr->displayedCard.firstHallOfFameB != 0 || gTrainerCardPtr->displayedCard.firstHallOfFameC != 0) gTrainerCardPtr->showHallOfFame++; if (gTrainerCardPtr->displayedCard.linkBattleWins != 0 || gTrainerCardPtr->displayedCard.linkBattleLosses != 0) gTrainerCardPtr->showLinkBattleStatus++; if (gTrainerCardPtr->displayedCard.battleTowerWins != 0 || gTrainerCardPtr->displayedCard.battleTowerLosses != 0) gTrainerCardPtr->showBattleTowerStatus++; if (gTrainerCardPtr->displayedCard.contestsWithFriends != 0) gTrainerCardPtr->showContestRecord++; if (gTrainerCardPtr->displayedCard.pokeblocksWithFriends != 0) gTrainerCardPtr->showMixingRecord++; if (gTrainerCardPtr->displayedCard.pokemonTrades != 0) gTrainerCardPtr->showTradingRecord++; if (!gTrainerCardPtr->isShowingLinkCard) { u32 badgeFlag; int i = 0; badgeFlag = FLAG_BADGE01_GET; while (1) { if (FlagGet(badgeFlag)) gTrainerCardPtr->ownedBadges[i]++; badgeFlag++; i++; if (badgeFlag > FLAG_BADGE08_GET) { break; } } } #if DEBUG if (gDebug_03000748 != 0) { gTrainerCardPtr->showHallOfFame = TRUE; gTrainerCardPtr->showLinkBattleStatus = TRUE; gTrainerCardPtr->showBattleTowerStatus = TRUE; gTrainerCardPtr->showContestRecord = TRUE; gTrainerCardPtr->showMixingRecord = TRUE; gTrainerCardPtr->showTradingRecord = TRUE; memset(gTrainerCardPtr->ownedBadges, TRUE, sizeof(gTrainerCardPtr->ownedBadges)); } #endif } void sub_80937A4() { ResetPaletteFade(); ResetSpriteData(); FreeAllSpritePalettes(); ResetTasks(); } void sub_80937BC() { Text_LoadWindowTemplate(&gWindowTemplate_TrainerCard_Back_Values); MultistepInitMenuWindowBegin(&gWindowTemplate_TrainerCard_Back_Values); } static void sub_80937D8() { TrainerCard_ResetOffsetRegisters(); TrainerCard_CopyGraphics(); sub_8093F64(); TrainerCard_DrawCard(); } static void sub_80937F0() { TrainerCard_LoadTrainerGraphics(); } static void nullsub_15(void) { } static void sub_8093800() { TrainerCard_CreateStateMachine(); } static void TrainerCard_CreateStateMachine(void) { u8 taskId; taskId = CreateTask(TrainerCard_RunStateMachine, 0); TrainerCard_RunStateMachine(taskId); } static void TrainerCard_RunStateMachine(u8 taskId) { while (TrainerCard_StateMachine[gTrainerCardPtr->current_state](&gTasks[taskId]) != 0) ; } bool8 TrainerCard_Init(struct Task *task) { gTrainerCardPtr->showColon = gSaveBlock2.playTimeSeconds & 1; gTrainerCardPtr->frameCounter = gSaveBlock2.playTimeVBlanks; TrainerCard_CreatePrintPlayTimeTask(); BeginNormalPaletteFade(0xFFFFFFFF, 0, 16, 0, RGB(0, 0, 0)); gTrainerCardPtr->current_state++; /* Advance state machine */ return FALSE; } bool8 TrainerCard_WaitForFadeInToFinish(struct Task *task) { if (!gPaletteFade.active) gTrainerCardPtr->current_state++; /* Advance state machine */ return FALSE; } bool8 TrainerCard_WaitForKeys(struct Task *task) { if (gMain.newKeys & B_BUTTON) { gTrainerCardPtr->current_state = 5; /* Jump to fadeout state */ return TRUE; } else if (gMain.newKeys & A_BUTTON) { /* It appears that it was previously possible to return the the front side after viewing the back side. This was probably removed due to being unintuitive. */ if (gTrainerCardPtr->backSideShown) { gTrainerCardPtr->current_state = 5; /* Jump to fadeout state */ } else { gTrainerCardPtr->backSideShown ^= 1; /* Switch to back side */ gTrainerCardPtr->current_state = 3; /* Jump to start flip animation state */ } return TRUE; } #if DEBUG else if (gDebug_03000748 && gMain.newKeys & R_BUTTON) { gTrainerCardPtr->starCount++; gTrainerCardPtr->starCount %= 5; TrainerCard_LoadPalettes(); if (gTrainerCardPtr->backSideShown == 0) TrainerCard_DrawStars(); } #endif return FALSE; } bool8 TrainerCard_StartFlipAntimation(struct Task *task) { TrainerCard_CreateFlipAnimationTask(); PlaySE(SE_CARD); gTrainerCardPtr->current_state++; /* Advance state machine */ return FALSE; } bool8 TrainerCard_WaitForFlipToFinish(struct Task *task) { if (TrainerCard_HasFlipAnimationFinished()) gTrainerCardPtr->current_state = 2; /* Return to wait for keys state */ return FALSE; } bool8 TrainerCard_FadeOut(struct Task *task) { TrainerCard_DestoryPlayTimeTask(); BeginNormalPaletteFade(0xFFFFFFFF, 0, 0, 16, RGB(0, 0, 0)); gTrainerCardPtr->current_state++; /* Advance state machine */ return FALSE; } bool8 TrainerCard_WaitForFadeOutToFinishAndQuit(struct Task *task) { if (!gPaletteFade.active) SetMainCallback2((MainCallback)gTrainerCardPtr->var_60); return FALSE; } static void TrainerCard_CreatePrintPlayTimeTask(void) { CreateTask(TrainerCard_Front_PrintPlayTime, 0); BasicInitMenuWindow(&gWindowTemplate_TrainerCard_Back_Values); } static void TrainerCard_DestoryPlayTimeTask(void) { u8 taskId = FindTaskIdByFunc(TrainerCard_Front_PrintPlayTime); if (taskId != 0xFF) DestroyTask(taskId); } static void TrainerCard_Front_PrintPlayTime(u8 taskId) { u8 buffer[32]; struct Task *task = &gTasks[taskId]; if (gTrainerCardPtr->showColon != task->data[TD_CARD_INDEX]) { task->data[TD_CARD_INDEX] = gTrainerCardPtr->showColon; task->data[TD_SHOWING_LINK_CARD] ^= TRUE; } TrainerCard_Front_GetPlayTimeString(buffer, task->data[TD_SHOWING_LINK_CARD]); Menu_PrintText(buffer, 10, 12); } static void TrainerCard_CreateFlipAnimationTask(void) { u8 taskId; taskId = CreateTask(TrainerCard_RunFlipAnimationStateMachine, 0); TrainerCard_RunFlipAnimationStateMachine(taskId); } static u8 TrainerCard_HasFlipAnimationFinished(void) { if (FindTaskIdByFunc(TrainerCard_RunFlipAnimationStateMachine) == 0xFF) return TRUE; else return FALSE; } static void TrainerCard_RunFlipAnimationStateMachine(u8 taskId) { while (TrainerCard_FlipAnimationStateMachine[gTasks[taskId].data[0]](&gTasks[taskId]) != 0) ; } bool8 TrainerCard_InitFlipAnimation(struct Task *task) { u32 i; gTrainerCardPtr->var_4 = FALSE; ScanlineEffect_Clear(); for (i = 0; i < 0xA0; i++) gScanlineEffectRegBuffers[1][i] = -4; SetHBlankCallback(TrainerCard_FlipAnimationHBlankCallback); gTrainerCardPtr->var_4 = TRUE; task->data[0]++; return FALSE; } bool8 TrainerCard_ScaleDownFlipAnimation(struct Task *task) { s16 i; u32 r4, r5, r10, r7, r6, var_24, r9, var; gTrainerCardPtr->var_4 = FALSE; task->data[1] += 3; if (task->data[1] > 79) task->data[1] = 79; r7 = task->data[1]; r9 = 160 - r7; r4 = r9 - r7; r6 = -r7 << 16; r5 = 0xA00000 / r4; r5 += 0xFFFF0000; var_24 = r6 + r5 * r4; r10 = r5 / r4; r5 *= 2; for (i = 0; i < r7; // WHAT?! gScanlineEffectRegBuffers[0][i] = (u32)-i + -4, i++); for (; i < (s16)r9; i++) { var = r6 >> 16; r6 += r5; r5 -= r10; gScanlineEffectRegBuffers[0][i] = var + -4; } for (var = var_24 >> 16; i < 160; // WHAT?! gScanlineEffectRegBuffers[0][i] = var + -4, i++); gTrainerCardPtr->var_4 = TRUE; if (task->data[1] > 74) task->data[0]++; return FALSE; } bool8 TrainerCard_SwitchToNewSide(struct Task *task) { TrainerCard_DestoryPlayTimeTask(); TrainerCard_DrawCard(); if (!gTrainerCardPtr->backSideShown) { /* This code never runs because it is impossible to flip the back side back to the front side */ TrainerCard_CreatePrintPlayTimeTask(); } task->data[0]++; return TRUE; } bool8 TrainerCard_ScaleUpFlipAnimation(struct Task *task) { s16 i; u32 r4, r5, r10, r7, r6, var_24, r9, var; gTrainerCardPtr->var_4 = FALSE; task->data[1] -= 3; if (task->data[1] <= 0) task->data[1] = 0; r7 = task->data[1]; r9 = 160 - r7; r4 = r9 - r7; r6 = -r7 << 16; r5 = 0xA00000 / r4; r5 += 0xFFFF0000; var_24 = r6 + r5 * r4; r10 = r5 / r4; r5 /= 2; for (i = 0; i < r7; // WHAT?! gScanlineEffectRegBuffers[0][i] = (u32)-i + -4, i++); for (; i < (s16)r9; i++) { var = r6 >> 16; r6 += r5; r5 += r10; gScanlineEffectRegBuffers[0][i] = var + -4; } for (var = var_24 >> 16; i < 160; // WHAT?! gScanlineEffectRegBuffers[0][i] = var + -4, i++); gTrainerCardPtr->var_4 = TRUE; if (task->data[1] <= 0) task->data[0]++; return FALSE; } bool8 TrainerCard_FinishFlipAnimation(struct Task *task) { u8 taskId; gTrainerCardPtr->var_4 = FALSE; SetHBlankCallback(NULL); TrainerCard_ResetOffsetRegisters(); taskId = FindTaskIdByFunc(TrainerCard_RunFlipAnimationStateMachine); DestroyTask(taskId); return FALSE; } static void TrainerCard_FlipAnimationHBlankCallback(void) { u16 bgVOffset = gScanlineEffectRegBuffers[1][REG_VCOUNT & 0xFF]; REG_BG0VOFS = bgVOffset; REG_BG1VOFS = bgVOffset; REG_BG2VOFS = bgVOffset; } static void TrainerCard_DrawCard(void) { if (gTrainerCardPtr->backSideShown) TrainerCard_DrawCardBack(); else TrainerCard_DrawCardFront(); } static void TrainerCard_DrawCardFront(void) { Menu_EraseScreen(); TrainerCard_ClearTrainerGraphics(); TrainerCard_LoadCardTileMap(); TrainerCard_LoadTrainerTilemap(); TrainerCard_DrawStars(); TrainerCard_DisplayBadges(); TrainerCard_Front_PrintTexts(); } static void TrainerCard_DrawCardBack(void) { Menu_EraseScreen(); TrainerCard_ClearTrainerGraphics(); sub_8093F48(); TrainerCard_Back_PrintTexts(); } static void TrainerCard_ResetOffsetRegisters(void) { REG_BG0VOFS = -4; REG_BG1HOFS = 0; REG_BG1VOFS = -4; REG_BG2HOFS = 0; REG_BG2VOFS = -4; } static void TrainerCard_CopyGraphics(void) { TrainerCard_LoadPalettes(); LoadPalette(gUnknown_083B5F6C, 0xE0, 32); DmaCopyLarge16(3, gMenuTrainerCard_Gfx, (void *)VRAM, 0x1480, 0x1000); DmaCopy16Defvars(3, gBadgesTiles, (void *)(VRAM + 0x1480), 0x400); } extern const u16 *const gTrainerCardPalettes[]; static void TrainerCard_LoadPalettes(void) { LoadPalette(gTrainerCardPalettes[gTrainerCardPtr->starCount], 0, 48 * 2); LoadPalette(gBadgesPalette, 48, 16 * 2); LoadPalette(gUnknown_083B5F4C, 64, 16 * 2); if (gTrainerCardPtr->displayedCard.gender != MALE) LoadPalette(gUnknown_083B5F0C, 16, 16 * 2); } static void TrainerCard_LoadTrainerGraphics(void) { LoadTrainerGfx_TrainerCard(gTrainerCardPtr->displayedCard.gender, 80, (void *)(VRAM + 0x1880)); } static void TrainerCard_LoadCardTileMap(void) { const void *arr[] = {gMenuTrainerCardFront_Tilemap, gMenuTrainerCardFront2_Tilemap}; CpuFastSet(arr[gTrainerCardPtr->isShowingLinkCard], (void *)(VRAM + 0x4800), 0x140); } // I don't really know where to put the data. It's in such a weird order. const u8 gUnknown_083B5EF4[] = _(" : "); const u16 *const gTrainerCardPalettes[] = { gMenuTrainerCard0Star_Pal, gMenuTrainerCard1Star_Pal, gMenuTrainerCard2Star_Pal, gMenuTrainerCard3Star_Pal, gMenuTrainerCard4Star_Pal, }; const u16 gUnknown_083B5F0C[] = INCBIN_U16("graphics/trainer_card/83B5F0C.gbapal"); const u16 gBadgesPalette[] = INCBIN_U16("graphics/trainer_card/badges.gbapal"); const u16 gUnknown_083B5F4C[] = INCBIN_U16("graphics/trainer_card/83B5F4C.gbapal"); const u16 gUnknown_083B5F6C[] = INCBIN_U16("graphics/trainer_card/83B5F6C.gbapal"); const u16 gTrainerCardBadgesMap[][4] = INCBIN_U16("graphics/trainer_card/83B5F8C_map.bin"); static void sub_8093F48(void) { CpuFastSet(gMenuTrainerCardBack_Tilemap, (void *)(VRAM + 0x4800), 320); } static void sub_8093F64(void) { CpuFastSet(gMenuTrainerCardBackground_Tilemap, (void *)(VRAM + 0x5000), 320); } static void TrainerCard_LoadTrainerTilemap(void) { u16 r5 = 0xC4; u16 *ptr = (u16 *)(VRAM + 0x4000); s16 i; s16 j; for (i = 5; i < 13; i++) { for (j = 19; j < 27; j++, r5++) ptr[i * 32 + j] = r5 | 0x5000; } } static void TrainerCard_DrawStars(void) { u16 *ptr = (u16 *)(VRAM + 0x4000); s16 i = 15; s16 var = 15 + gTrainerCardPtr->starCount; while (i < var) { ptr[6 * 32 + i] = 0x408F; i++; } while (i < 0x13) { ptr[6 * 32 + i] = 0; i++; } } static void TrainerCard_DisplayBadges(void) { if (!gTrainerCardPtr->isShowingLinkCard) { u16 *ptr = (u16 *)(VRAM + 0x4000); s16 i; s16 r2; for (i = 0, r2 = 4; i < 8; i++, r2 += 3) { if (gTrainerCardPtr->ownedBadges[i] != 0) { ptr[15 * 32 + r2 + 0] = gTrainerCardBadgesMap[i][0] | 0x3000; ptr[15 * 32 + r2 + 1] = gTrainerCardBadgesMap[i][1] | 0x3000; ptr[16 * 32 + r2 + 0] = gTrainerCardBadgesMap[i][2] | 0x3000; ptr[16 * 32 + r2 + 1] = gTrainerCardBadgesMap[i][3] | 0x3000; } } } } static void TrainerCard_ClearTrainerGraphics(void) { s16 i; u16 *ptr; for (i = 0, ptr = (u16 *)(VRAM + 0x4000); i < 0x400; i++, ptr++) *ptr = 0; } static void TrainerCard_ClearPokedexLabel(void) { u16 *ptr = (u16 *)(VRAM + 0x4800); u16 i; for (i = 3; i < 17; i++) { ptr[10 * 32 + i] = 1; ptr[11 * 32 + i] = 1; } } static void TrainerCard_Front_PrintTexts(void) { u8 *buffer; BasicInitMenuWindow(&gWindowTemplate_TrainerCard_Back_Values); buffer = gStringVar1; StringCopy(buffer, gTrainerCardPtr->displayedCard.playerName); ConvertInternationalString(buffer, gTrainerCardPtr->language); Menu_PrintText(buffer, 7, 5); TrainerCard_Front_PrintTrainerID(); TrainerCard_Front_PrintMoney(); TrainerCard_Front_PrintPokedexCount(); TrainerCard_PrintEasyChatPhrase(); } static void TrainerCard_Back_PrintTexts(void) { BasicInitMenuWindow(&gWindowTemplate_TrainerCard_Back_Values); TrainerCard_Back_PrintName(); TrainerCard_Back_PrintHallOfFameTime_Label(); TrainerCard_Back_PrintLinkBattlesLabel(); TrainerCard_Back_PrintBattleTower_Label(); TrainerCard_Back_PrintLinkContests_Label(); TrainerCard_Back_PrintLinkPokeblocks_Label(); TrainerCard_Back_PrintPokemonTrades_Label(); BasicInitMenuWindow(&gWindowTemplate_TrainerCard_Back_Labels); TrainerCard_Back_PrintHallOfFameTime(); TrainerCard_Back_PrintLinkBattles(); TrainerCard_Back_PrintBattleTower(); TrainerCard_Back_PrintLinkContests(); TrainerCard_Back_PrintLinkPokeblocks(); TrainerCard_Back_PrintPokemonTrades(); } static void TrainerCard_Front_PrintTrainerID(void) { u8 buffer[8]; ConvertIntToDecimalStringN(buffer, gTrainerCardPtr->displayedCard.trainerId, STR_CONV_MODE_LEADING_ZEROS, 5); Menu_PrintText(buffer, 20, 2); } static void TrainerCard_Front_PrintMoney(void) { sub_80B7AEC(gTrainerCardPtr->displayedCard.money, 16, 8); } static void TrainerCard_Front_PrintPokedexCount(void) { u8 buffer[16]; if ( #if DEBUG gDebug_03000748 == 0 && #endif !gTrainerCardPtr->showPokedexCount) { TrainerCard_ClearPokedexLabel(); } else { ConvertIntToDecimalStringN(buffer, gTrainerCardPtr->displayedCard.pokedexSeen, STR_CONV_MODE_LEFT_ALIGN, 3); MenuPrint_RightAligned(buffer, 16, 10); } } static void TrainerCard_Front_GetPlayTimeString(u8 *arg1, s16 colon) { u8 buffer[16]; u16 playTimeHours; u16 playTimeMinutes; playTimeHours = gSaveBlock2.playTimeHours; playTimeMinutes = gSaveBlock2.playTimeMinutes; if (gTrainerCardPtr->isShowingLinkCard != 0) { playTimeHours = gTrainerCardPtr->displayedCard.playTimeHours; playTimeMinutes = gTrainerCardPtr->displayedCard.playTimeMinutes; } FormatPlayTime(buffer, playTimeHours, playTimeMinutes, colon); AlignStringInMenuWindow(arg1, buffer, 48, 1); } static void TrainerCard_PrintEasyChatPhrase(void) { u8 *str; if (gTrainerCardPtr->isShowingLinkCard != 0) { str = gStringVar1; str = StringCopy(str, gTrainerCardPtr->easyChatPhrase[0]); str[0] = 00; str++; str = StringCopy(str, gTrainerCardPtr->easyChatPhrase[1]); Menu_PrintText(gStringVar1, 2, 14); str = gStringVar1; str = StringCopy(str, gTrainerCardPtr->easyChatPhrase[2]); str[0] = 00; str++; str = StringCopy(str, gTrainerCardPtr->easyChatPhrase[3]); Menu_PrintText(gStringVar1, 2, 16); } } static void TrainerCard_Back_PrintName(void) { u8 *str; str = gStringVar1; StringCopy(str, gTrainerCardPtr->displayedCard.playerName); ConvertInternationalString(str, gTrainerCardPtr->language); #if ENGLISH StringAppend(str, gOtherText_TrainersTrainerCard); #elif GERMAN de_sub_8073174(str, gOtherText_TrainersTrainerCard); #endif MenuPrint_RightAligned(gStringVar1, 28, 2); } static void TrainerCard_Back_PrintHallOfFameTime_Label(void) { if (gTrainerCardPtr->showHallOfFame != 0) Menu_PrintText(gOtherText_FirstHOF, 3, 5); } static void TrainerCard_Back_PrintHallOfFameTime(void) { u8 *str; if (gTrainerCardPtr->showHallOfFame != 0) { str = gStringVar1; str = ConvertIntToDecimalStringN(str, gTrainerCardPtr->displayedCard.firstHallOfFameA, STR_CONV_MODE_RIGHT_ALIGN, 3); str = StringCopy(str, gUnknown_083B5EF4); str = ConvertIntToDecimalStringN(str, gTrainerCardPtr->displayedCard.firstHallOfFameB, STR_CONV_MODE_LEADING_ZEROS, 2); str = StringCopy(str, gUnknown_083B5EF4); str = ConvertIntToDecimalStringN(str, gTrainerCardPtr->displayedCard.firstHallOfFameC, STR_CONV_MODE_LEADING_ZEROS, 2); MenuPrint_RightAligned(gStringVar1, 28, 5); } } static void TrainerCard_Back_PrintLinkBattlesLabel(void) { if (gTrainerCardPtr->showLinkBattleStatus != 0) Menu_PrintText(gOtherText_LinkCableBattles, 3, 7); } static void TrainerCard_Back_PrintLinkBattles(void) { u8 buffer[16]; if (gTrainerCardPtr->showLinkBattleStatus != 0) { ConvertIntToDecimalString(buffer, gTrainerCardPtr->displayedCard.linkBattleWins); MenuPrint_RightAligned(buffer, 22, 7); ConvertIntToDecimalString(buffer, gTrainerCardPtr->displayedCard.linkBattleLosses); MenuPrint_RightAligned(buffer, 28, 7); } } static void TrainerCard_Back_PrintBattleTower_Label(void) { if (gTrainerCardPtr->showBattleTowerStatus != 0) Menu_PrintText(gOtherText_BattleTowerWinRecord, 3, 15); } static void TrainerCard_Back_PrintBattleTower(void) { u8 buffer[16]; if (gTrainerCardPtr->showBattleTowerStatus != 0) { AlignInt2InMenuWindow(buffer, gTrainerCardPtr->displayedCard.battleTowerWins, 24, 1); Menu_PrintTextPixelCoords(buffer, 112, 120, 0); AlignInt2InMenuWindow(buffer, gTrainerCardPtr->displayedCard.battleTowerLosses, 24, 1); Menu_PrintTextPixelCoords(buffer, 149, 120, 0); } } static void TrainerCard_Back_PrintLinkContests_Label(void) { if (gTrainerCardPtr->showContestRecord != 0) Menu_PrintText(gOtherText_ContestRecord, 3, 13); } static void TrainerCard_Back_PrintLinkContests(void) { u8 buffer[8]; if (gTrainerCardPtr->showContestRecord != 0) { ConvertIntToDecimalStringN(buffer, gTrainerCardPtr->displayedCard.contestsWithFriends, STR_CONV_MODE_RIGHT_ALIGN, 3); MenuPrint_RightAligned(buffer, 28, 13); } } static void TrainerCard_Back_PrintLinkPokeblocks_Label(void) { if (gTrainerCardPtr->showMixingRecord != 0) Menu_PrintText(gOtherText_MixingRecord, 3, 11); } static void TrainerCard_Back_PrintLinkPokeblocks(void) { u8 buffer[8]; if (gTrainerCardPtr->showMixingRecord != 0) { ConvertIntToDecimalStringN(buffer, gTrainerCardPtr->displayedCard.pokeblocksWithFriends, STR_CONV_MODE_RIGHT_ALIGN, 5); MenuPrint_RightAligned(buffer, 28, 11); } } static void TrainerCard_Back_PrintPokemonTrades_Label(void) { if (gTrainerCardPtr->showTradingRecord != 0) Menu_PrintText(gOtherText_TradeRecord, 3, 9); } static void TrainerCard_Back_PrintPokemonTrades(void) { u8 buffer[8]; if (gTrainerCardPtr->showTradingRecord != 0) { ConvertIntToDecimalStringN(buffer, gTrainerCardPtr->displayedCard.pokemonTrades, STR_CONV_MODE_RIGHT_ALIGN, 5); MenuPrint_RightAligned(buffer, 28, 9); } } void unref_sub_8094588(u16 left, u16 top) { const u8 *text = gOtherText_Boy; if (gSaveBlock2.playerGender == FEMALE) text = gOtherText_Girl; Menu_PrintText(text, left, top); }