#include "global.h" #include "gpu_regs.h" #include "main.h" #include "trainer_card.h" #include "event_data.h" #include "recorded_battle.h" #include "alloc.h" #include "sprite.h" #include "scanline_effect.h" #include "text_window.h" #include "task.h" #include "graphics.h" #include "strings.h" #include "frontier_pass.h" #include "international_string_util.h" #include "palette.h" #include "window.h" #include "decompress.h" #include "menu_helpers.h" #include "menu.h" #include "bg.h" #include "sound.h" #include "string_util.h" #include "battle_pyramid.h" #include "overworld.h" #include "math_util.h" #include "constants/battle_frontier.h" #include "constants/maps.h" #include "constants/rgb.h" #include "constants/region_map_sections.h" #include "constants/songs.h" // All windows displayed in the frontier pass. enum { WINDOW_EARNED_SYMBOLS, WINDOW_BATTLE_RECORD, WINDOW_BATTLE_POINTS, WINDOW_DESCRIPTION, WINDOW_4, WINDOW_COUNT }; enum { CURSOR_AREA_NOTHING, CURSOR_AREA_MAP, CURSOR_AREA_CARD, CURSOR_AREA_RECORD, CURSOR_AREA_CANCEL, CURSOR_AREA_POINTS, CURSOR_AREA_EARNED_SYMBOLS, // The window. CURSOR_AREA_SYMBOL, // All 7 symbols. CURSOR_AREA_COUNT = CURSOR_AREA_SYMBOL + NUM_FRONTIER_FACILITIES, }; struct FrontierPassData { void (*callback)(void); u16 state; u16 battlePoints; s16 cursorX; s16 cursorY; u8 cursorArea; u8 previousCursorArea; u8 hasBattleRecord:1; u8 unkE:3; u8 trainerStars:4; u8 facilitySymbols[NUM_FRONTIER_FACILITIES]; }; struct FrontierPassGfx { struct Sprite *cursorSprite; struct Sprite *symbolSprites[NUM_FRONTIER_FACILITIES]; u8 *unk20; u8 *unk24; u8 *unk28; bool8 setAffine; s16 unk2E; s16 unk30; u8 tilemapBuff1[0x1000]; u8 tilemapBuff2[0x1000]; u8 tilemapBuff3[0x400]; }; struct FrontierPassSaved { void (*callback)(void); s16 cursorX; s16 cursorY; }; extern struct FrontierPassData *gUnknown_02039CEC; extern struct FrontierPassGfx *gUnknown_02039CF0; extern struct FrontierPassSaved gUnknown_02039CF8; // This file's functions. u32 AllocateFrontierPassData(void (*callback)(void)); void ShowFrontierMap(void (*callback)(void)); void CB2_InitFrontierPass(void); void sub_80C629C(void); void FreeCursorAndSymbolSprites(void); void LoadCursorAndSymbolSprites(void); u32 FreeFrontierPassData(void); bool32 InitFrontierPass(void); bool32 HideFrontierPass(void); void Task_HandleFrontierPassInput(u8 taskId); void Task_DoFadeEffect(u8 taskId); void sub_80C6104(u8 cursorArea, u8 previousCursorArea); void PrintAreaDescription(u8 cursorArea); void sub_80C5F58(bool8 arg0, bool8 arg1); // Const rom data. extern const s16 gUnknown_085713E0[][2]; extern const struct BgTemplate gUnknown_085713E8[3]; extern const struct WindowTemplate gUnknown_08571400[]; extern const u32 gUnknown_085712F8[]; extern const u32 gUnknown_085712C0[]; extern const u32 gUnknown_08571060[]; extern const u8 gUnknown_08571448[][3]; extern const u8 *const gUnknown_08571614[]; extern const struct SpritePalette gUnknown_085714E4[]; extern const struct CompressedSpriteSheet gUnknown_085714BC[]; extern const struct SpriteTemplate gUnknown_085715B4[2]; extern const struct SpriteTemplate gUnknown_085715E4; // code void sub_80C50D0(void) { SetGpuReg(REG_OFFSET_DISPCNT, 0); SetGpuReg(REG_OFFSET_BG3CNT, 0); SetGpuReg(REG_OFFSET_BG2CNT, 0); SetGpuReg(REG_OFFSET_BG1CNT, 0); SetGpuReg(REG_OFFSET_BG0CNT, 0); ChangeBgX(0, 0, 0); ChangeBgY(0, 0, 0); ChangeBgX(1, 0, 0); ChangeBgY(1, 0, 0); ChangeBgX(2, 0, 0); ChangeBgY(2, 0, 0); ChangeBgX(3, 0, 0); ChangeBgY(3, 0, 0); SetGpuReg(REG_OFFSET_BLDCNT, 0); SetGpuReg(REG_OFFSET_BLDY, 0); SetGpuReg(REG_OFFSET_BLDALPHA, 0); SetGpuReg(REG_OFFSET_WIN0H, 0); SetGpuReg(REG_OFFSET_WIN0V, 0); SetGpuReg(REG_OFFSET_WIN1H, 0); SetGpuReg(REG_OFFSET_WIN1V, 0); SetGpuReg(REG_OFFSET_WININ, 0); SetGpuReg(REG_OFFSET_WINOUT, 0); CpuFill16(0, (void *)VRAM, VRAM_SIZE); CpuFill32(0, (void *)OAM, OAM_SIZE); } void ShowFrontierPass(void (*callback)(void)) { AllocateFrontierPassData(callback); SetMainCallback2(CB2_InitFrontierPass); } void LeaveFrontierPass(void) { SetMainCallback2(gUnknown_02039CEC->callback); FreeFrontierPassData(); } u32 AllocateFrontierPassData(void (*callback)(void)) { u8 i; if (gUnknown_02039CEC != NULL) return 1; gUnknown_02039CEC = AllocZeroed(sizeof(*gUnknown_02039CEC)); if (gUnknown_02039CEC == NULL) return 2; gUnknown_02039CEC->callback = callback; i = GetCurrentRegionMapSectionId(); if (i != MAPSEC_BATTLE_FRONTIER && i != MAPSEC_ARTISAN_CAVE) { gUnknown_02039CEC->cursorX = 176; gUnknown_02039CEC->cursorY = 104; } else { gUnknown_02039CEC->cursorX = 176; gUnknown_02039CEC->cursorY = 48; } gUnknown_02039CEC->battlePoints = gSaveBlock2Ptr->frontier.battlePoints; gUnknown_02039CEC->hasBattleRecord = CanCopyRecordedBattleSaveData(); gUnknown_02039CEC->unkE = 0; gUnknown_02039CEC->trainerStars = CountPlayerTrainerStars(); for (i = 0; i < 7; i++) { if (FlagGet(FLAG_SYS_TOWER_SILVER + i * 2)) gUnknown_02039CEC->facilitySymbols[i]++; if (FlagGet(FLAG_SYS_TOWER_GOLD + i * 2)) gUnknown_02039CEC->facilitySymbols[i]++; } return 0; } u32 FreeFrontierPassData(void) { if (gUnknown_02039CEC == NULL) return 1; memset(gUnknown_02039CEC, 0, sizeof(*gUnknown_02039CEC)); // Why clear data, if it's going to be freed anyway? FREE_AND_SET_NULL(gUnknown_02039CEC); return 0; } u32 AllocateFrontierPassGfx(void) { if (gUnknown_02039CF0 != NULL) return 1; gUnknown_02039CF0 = AllocZeroed(sizeof(*gUnknown_02039CF0)); if (gUnknown_02039CF0 == NULL) return 2; return 0; } u32 FreeFrontierPassGfx(void) { FreeAllWindowBuffers(); if (gUnknown_02039CF0 == NULL) return 1; if (gUnknown_02039CF0->unk28 != NULL) FREE_AND_SET_NULL(gUnknown_02039CF0->unk28); if (gUnknown_02039CF0->unk24 != NULL) FREE_AND_SET_NULL(gUnknown_02039CF0->unk24); if (gUnknown_02039CF0->unk20 != NULL) FREE_AND_SET_NULL(gUnknown_02039CF0->unk20); memset(gUnknown_02039CF0, 0, sizeof(*gUnknown_02039CF0)); // Why clear data, if it's going to be freed anyway? FREE_AND_SET_NULL(gUnknown_02039CF0); return 0; } void VblankCb_FrontierPass(void) { if (gUnknown_02039CF0->setAffine) { SetBgAffine(2, gUnknown_085713E0[gUnknown_02039CEC->unkE - 1][0] << 8, gUnknown_085713E0[gUnknown_02039CEC->unkE - 1][1] << 8, gUnknown_085713E0[gUnknown_02039CEC->unkE - 1][0], gUnknown_085713E0[gUnknown_02039CEC->unkE - 1][1], gUnknown_02039CF0->unk2E, gUnknown_02039CF0->unk30, 0); } LoadOam(); ProcessSpriteCopyRequests(); TransferPlttBuffer(); } void CB2_FrontierPass(void) { RunTasks(); AnimateSprites(); BuildOamBuffer(); } void CB2_InitFrontierPass(void) { if (InitFrontierPass()) { CreateTask(Task_HandleFrontierPassInput, 0); SetMainCallback2(CB2_FrontierPass); } } void CB2_HideFrontierPass(void) { if (HideFrontierPass()) { LeaveFrontierPass(); } } bool32 InitFrontierPass(void) { u32 sizeOut = 0; switch (gUnknown_02039CEC->state) { case 0: SetVBlankCallback(NULL); ScanlineEffect_Stop(); SetVBlankHBlankCallbacksToNull(); DisableInterrupts(INTR_FLAG_HBLANK); break; case 1: sub_80C50D0(); break; case 2: ResetTasks(); ResetSpriteData(); FreeAllSpritePalettes(); ResetPaletteFade(); reset_temp_tile_data_buffers(); break; case 3: AllocateFrontierPassGfx(); break; case 4: ResetBgsAndClearDma3BusyFlags(0); InitBgsFromTemplates(1, gUnknown_085713E8, ARRAY_COUNT(gUnknown_085713E8)); SetBgTilemapBuffer(1, gUnknown_02039CF0->tilemapBuff1); SetBgTilemapBuffer(2, gUnknown_02039CF0->tilemapBuff2); SetBgTilemapBuffer(3, gUnknown_02039CF0->tilemapBuff3); SetBgAttribute(2, BG_ATTR_WRAPAROUND, 1); break; case 5: InitWindows(gUnknown_08571400); DeactivateAllTextPrinters(); break; case 6: gUnknown_02039CF0->unk20 = malloc_and_decompress(gUnknown_085712F8, &sizeOut); gUnknown_02039CF0->unk24 = malloc_and_decompress(gUnknown_08571060, &sizeOut); gUnknown_02039CF0->unk28 = malloc_and_decompress(gUnknown_085712C0, &sizeOut); decompress_and_copy_tile_data_to_vram(1, gUnknown_08DE08C8, 0, 0, 0); decompress_and_copy_tile_data_to_vram(2, gUnknown_08DE2084, 0, 0, 0); break; case 7: if (free_temp_tile_data_buffers_if_possible()) return FALSE; FillBgTilemapBufferRect_Palette0(0, 0, 0, 0, 30, 20); FillBgTilemapBufferRect_Palette0(1, 0, 0, 0, 30, 20); FillBgTilemapBufferRect_Palette0(2, 0, 0, 0, 30, 20); CopyBgTilemapBufferToVram(0); CopyBgTilemapBufferToVram(1); CopyBgTilemapBufferToVram(2); break; case 8: LoadPalette(gUnknown_08DE07C8[0], 0, 0x1A0); LoadPalette(gUnknown_08DE07C8[1 + gUnknown_02039CEC->trainerStars], 0x10, 0x20); LoadPalette(stdpal_get(0), 0xF0, 0x20); sub_80C629C(); sub_80C6104(gUnknown_02039CEC->cursorArea, gUnknown_02039CEC->previousCursorArea); if (gUnknown_02039CEC->unkE == 1 || gUnknown_02039CEC->unkE == 2) { gUnknown_02039CEC->state = 0; return TRUE; } break; case 9: SetGpuReg(REG_OFFSET_DISPCNT, DISPCNT_OBJ_ON | DISPCNT_OBJ_1D_MAP); ShowBg(0); ShowBg(1); ShowBg(2); LoadCursorAndSymbolSprites(); SetVBlankCallback(VblankCb_FrontierPass); BlendPalettes(0xFFFFFFFF, 0x10, 0); BeginNormalPaletteFade(0xFFFFFFFF, 0, 0x10, 0, RGB_BLACK); break; case 10: AnimateSprites(); BuildOamBuffer(); if (UpdatePaletteFade()) return FALSE; gUnknown_02039CEC->state = 0; return TRUE; } gUnknown_02039CEC->state++; return FALSE; } bool32 HideFrontierPass(void) { switch (gUnknown_02039CEC->state) { case 0: if (gUnknown_02039CEC->unkE != 1 && gUnknown_02039CEC->unkE != 2) { BeginNormalPaletteFade(0xFFFFFFFF, 0, 0, 0x10, RGB_BLACK); } else { gUnknown_02039CEC->state = 2; return FALSE; } break; case 1: if (UpdatePaletteFade()) return FALSE; break; case 2: SetGpuReg(REG_OFFSET_DISPCNT, 0); HideBg(0); HideBg(1); HideBg(2); SetVBlankCallback(NULL); ScanlineEffect_Stop(); SetVBlankHBlankCallbacksToNull(); break; case 3: FreeCursorAndSymbolSprites(); break; case 4: sub_80C50D0(); ResetTasks(); ResetSpriteData(); FreeAllSpritePalettes(); break; case 5: UnsetBgTilemapBuffer(0); UnsetBgTilemapBuffer(1); UnsetBgTilemapBuffer(2); FreeFrontierPassGfx(); gUnknown_02039CEC->state = 0; return TRUE; } gUnknown_02039CEC->state++; return FALSE; } struct { s16 yStart; s16 yEnd; s16 xStart; s16 xEnd; } extern const gUnknown_08571454[]; u8 GetCursorAreaFromCoords(s16 x, s16 y) { u8 i; // Minus/Plus 1, because the table doesn't take into account the nothing field. for (i = 0; i < CURSOR_AREA_COUNT - 1; i++) { if (gUnknown_08571454[i].yStart <= y && gUnknown_08571454[i].yEnd >= y && gUnknown_08571454[i].xStart <= x && gUnknown_08571454[i].xEnd >= x) { if (i >= CURSOR_AREA_SYMBOL - 1 && gUnknown_02039CEC->facilitySymbols[i - CURSOR_AREA_SYMBOL + 1] == 0) break; return i + 1; } } return 0; } void CB2_ReshowFrontierPass(void) { u8 taskId; if (!InitFrontierPass()) return; switch (gUnknown_02039CEC->unkE) { case 1: case 2: taskId = CreateTask(Task_DoFadeEffect, 0); gTasks[taskId].data[0] = TRUE; break; case 3: default: gUnknown_02039CEC->unkE = 0; taskId = CreateTask(Task_HandleFrontierPassInput, 0); break; } SetMainCallback2(CB2_FrontierPass); } void CB2_ReturnFromRecord(void) { AllocateFrontierPassData(gUnknown_02039CF8.callback); gUnknown_02039CEC->cursorX = gUnknown_02039CF8.cursorX; gUnknown_02039CEC->cursorY = gUnknown_02039CF8.cursorY; memset(&gUnknown_02039CF8, 0, sizeof(gUnknown_02039CF8)); switch (InBattlePyramid()) { case 1: PlayBGM(MUS_PYRAMID); break; case 2: PlayBGM(MUS_PYRAMID_TOP); break; default: Overworld_PlaySpecialMapMusic(); break; } SetMainCallback2(CB2_ReshowFrontierPass); } void CB2_ShowFrontierPassFeature(void) { if (!HideFrontierPass()) return; switch (gUnknown_02039CEC->unkE) { case 1: ShowFrontierMap(CB2_ReshowFrontierPass); break; case 3: gUnknown_02039CF8.callback = gUnknown_02039CEC->callback; gUnknown_02039CF8.cursorX = gUnknown_02039CEC->cursorX; gUnknown_02039CF8.cursorY = gUnknown_02039CEC->cursorY; FreeFrontierPassData(); PlayRecordedBattle(CB2_ReturnFromRecord); break; case 2: ShowPlayerTrainerCard(CB2_ReshowFrontierPass); break; } } bool32 TryCallPassAreaFunction(u8 taskId, u8 cursorArea) { switch (cursorArea) { case CURSOR_AREA_RECORD: if (!gUnknown_02039CEC->hasBattleRecord) return FALSE; gUnknown_02039CEC->unkE = 3; DestroyTask(taskId); SetMainCallback2(CB2_ShowFrontierPassFeature); break; case CURSOR_AREA_MAP: case CURSOR_AREA_CARD: gUnknown_02039CEC->unkE = cursorArea; gTasks[taskId].func = Task_DoFadeEffect; gTasks[taskId].data[0] = FALSE; break; default: return FALSE; } gUnknown_02039CEC->cursorX = gUnknown_02039CF0->cursorSprite->pos1.x; gUnknown_02039CEC->cursorY = gUnknown_02039CF0->cursorSprite->pos1.y; return TRUE; } void Task_HandleFrontierPassInput(u8 taskId) { u8 var = FALSE; // Reused, first informs whether the cursor moves, then used as the new cursor area. if (gMain.heldKeys & DPAD_UP && gUnknown_02039CF0->cursorSprite->pos1.y >= 9) { gUnknown_02039CF0->cursorSprite->pos1.y -= 2; if (gUnknown_02039CF0->cursorSprite->pos1.y <= 7) gUnknown_02039CF0->cursorSprite->pos1.y = 2; var = TRUE; } if (gMain.heldKeys & DPAD_DOWN && gUnknown_02039CF0->cursorSprite->pos1.y <= 135) { gUnknown_02039CF0->cursorSprite->pos1.y += 2; if (gUnknown_02039CF0->cursorSprite->pos1.y >= 137) gUnknown_02039CF0->cursorSprite->pos1.y = 136; var = TRUE; } if (gMain.heldKeys & DPAD_LEFT && gUnknown_02039CF0->cursorSprite->pos1.x >= 6) { gUnknown_02039CF0->cursorSprite->pos1.x -= 2; if (gUnknown_02039CF0->cursorSprite->pos1.x <= 4) gUnknown_02039CF0->cursorSprite->pos1.x = 5; var = TRUE; } if (gMain.heldKeys & DPAD_RIGHT && gUnknown_02039CF0->cursorSprite->pos1.x <= 231) { gUnknown_02039CF0->cursorSprite->pos1.x += 2; if (gUnknown_02039CF0->cursorSprite->pos1.x >= 233) gUnknown_02039CF0->cursorSprite->pos1.x = 232; var = TRUE; } if (!var) // Cursor did not change. { if (gUnknown_02039CEC->cursorArea != CURSOR_AREA_NOTHING && gMain.newKeys & A_BUTTON) { if (gUnknown_02039CEC->cursorArea <= CURSOR_AREA_RECORD) // Map, Card, Record { PlaySE(SE_SELECT); if (TryCallPassAreaFunction(taskId, gUnknown_02039CEC->cursorArea)) return; } else if (gUnknown_02039CEC->cursorArea == CURSOR_AREA_CANCEL) { PlaySE(SE_PC_OFF); SetMainCallback2(CB2_HideFrontierPass); DestroyTask(taskId); // BUG. The function should return here. Otherwise, it can play the same sound twice and destroy the same task twice. } } if (gMain.newKeys & B_BUTTON) { PlaySE(SE_PC_OFF); SetMainCallback2(CB2_HideFrontierPass); DestroyTask(taskId); } } else { var = GetCursorAreaFromCoords(gUnknown_02039CF0->cursorSprite->pos1.x - 5, gUnknown_02039CF0->cursorSprite->pos1.y + 5); if (gUnknown_02039CEC->cursorArea != var) { PrintAreaDescription(var); gUnknown_02039CEC->previousCursorArea = gUnknown_02039CEC->cursorArea; gUnknown_02039CEC->cursorArea = var; sub_80C6104(gUnknown_02039CEC->cursorArea, gUnknown_02039CEC->previousCursorArea); } } } void Task_DoFadeEffect(u8 taskId) { s16 *data = gTasks[taskId].data; switch (gUnknown_02039CEC->state) { case 0: if (!data[0]) { sub_80C5F58(TRUE, FALSE); data[1] = 0x100; data[2] = 0x100; data[3] = 0x15; data[4] = 0x15; BeginNormalPaletteFade(0xFFFFFFFF, 0, 0, 0x10, RGB_WHITE); } else { data[1] = 0x1FC; data[2] = 0x1FC; data[3] = -0x15; data[4] = -0x15; SetGpuReg(REG_OFFSET_DISPCNT, DISPCNT_OBJ_ON | DISPCNT_OBJ_1D_MAP); ShowBg(0); ShowBg(1); ShowBg(2); LoadCursorAndSymbolSprites(); SetVBlankCallback(VblankCb_FrontierPass); BlendPalettes(0xFFFFFFFF, 0x10, RGB_WHITE); BeginNormalPaletteFade(0xFFFFFFFF, 0, 0x10, 0, RGB_WHITE); } gUnknown_02039CF0->setAffine = TRUE; gUnknown_02039CF0->unk2E = sub_8151624(data[1]); gUnknown_02039CF0->unk30 = sub_8151624(data[2]); break; case 1: UpdatePaletteFade(); data[1] += data[3]; data[2] += data[4]; gUnknown_02039CF0->unk2E = sub_8151624(data[1]); gUnknown_02039CF0->unk30 = sub_8151624(data[2]); if (!data[0]) { if (data[1] <= 0x1FC) return; } else { if (data[1] != 0x100) return; } break; case 2: if (gUnknown_02039CF0->setAffine) // Nonsensical check. gUnknown_02039CF0->setAffine = FALSE; if (UpdatePaletteFade()) return; if (!data[0]) { DestroyTask(taskId); SetMainCallback2(CB2_ShowFrontierPassFeature); } else { sub_80C5F58(FALSE, FALSE); gUnknown_02039CEC->unkE = 0; gTasks[taskId].func = Task_HandleFrontierPassInput; } SetBgAttribute(2, BG_ATTR_WRAPAROUND, 0); gUnknown_02039CEC->state = 0; return; } gUnknown_02039CEC->state++; } void ShowAndPrintWindows(void) { s32 x; u8 i; for (i = 0; i < WINDOW_COUNT; i++) { PutWindowTilemap(i); FillWindowPixelBuffer(i, 0); } x = GetStringCenterAlignXOffset(1, gText_SymbolsEarned, 0x60); AddTextPrinterParameterized3(WINDOW_EARNED_SYMBOLS, 1, x, 5, gUnknown_08571448[0], 0, gText_SymbolsEarned); x = GetStringCenterAlignXOffset(1, gText_BattleRecord, 0x60); AddTextPrinterParameterized3(WINDOW_BATTLE_RECORD, 1, x, 5, gUnknown_08571448[0], 0, gText_BattleRecord); AddTextPrinterParameterized3(WINDOW_BATTLE_POINTS, 8, 5, 4, gUnknown_08571448[0], 0, gText_BattlePoints); ConvertIntToDecimalStringN(gStringVar4, gUnknown_02039CEC->battlePoints, STR_CONV_MODE_LEFT_ALIGN, 5); x = GetStringRightAlignXOffset(8, gStringVar4, 0x5B); AddTextPrinterParameterized3(WINDOW_BATTLE_POINTS, 8, x, 16, gUnknown_08571448[0], 0, gStringVar4); gUnknown_02039CEC->cursorArea = GetCursorAreaFromCoords(gUnknown_02039CEC->cursorX - 5, gUnknown_02039CEC->cursorY + 5); gUnknown_02039CEC->previousCursorArea = CURSOR_AREA_NOTHING; PrintAreaDescription(gUnknown_02039CEC->cursorArea); for (i = 0; i < WINDOW_COUNT; i++) CopyWindowToVram(i, 3); CopyBgTilemapBufferToVram(0); } void PrintAreaDescription(u8 cursorArea) { FillWindowPixelBuffer(WINDOW_DESCRIPTION, 0); if (cursorArea == CURSOR_AREA_RECORD && !gUnknown_02039CEC->hasBattleRecord) AddTextPrinterParameterized3(WINDOW_DESCRIPTION, 1, 2, 0, gUnknown_08571448[1], 0, gUnknown_08571614[0]); else if (cursorArea != CURSOR_AREA_NOTHING) AddTextPrinterParameterized3(WINDOW_DESCRIPTION, 1, 2, 0, gUnknown_08571448[1], 0, gUnknown_08571614[cursorArea]); CopyWindowToVram(WINDOW_DESCRIPTION, 3); CopyBgTilemapBufferToVram(0); } void sub_80C5F58(bool8 arg0, bool8 arg1) { switch (gUnknown_02039CEC->unkE) { case 1: if (arg0) CopyToBgTilemapBufferRect_ChangePalette(2, gUnknown_02039CF0->unk20, 16, 3, 12, 7, 16); else FillBgTilemapBufferRect(2, 0, 16, 3, 12, 7, 16); break; case 2: if (arg0) CopyToBgTilemapBufferRect_ChangePalette(2, gUnknown_02039CF0->unk20 + 84, 16, 10, 12, 7, 16); else FillBgTilemapBufferRect(2, 0, 16, 10, 12, 7, 16); break; default: return; } CopyBgTilemapBufferToVram(2); if (arg1) { SetBgAffine(2, gUnknown_085713E0[gUnknown_02039CEC->unkE - 1][0] << 8, gUnknown_085713E0[gUnknown_02039CEC->unkE - 1][1] << 8, gUnknown_085713E0[gUnknown_02039CEC->unkE - 1][0], gUnknown_085713E0[gUnknown_02039CEC->unkE - 1][1], sub_8151624(0x1FC), sub_8151624(0x1FC), 0); } else { SetBgAffine(2, gUnknown_085713E0[gUnknown_02039CEC->unkE - 1][0] << 8, gUnknown_085713E0[gUnknown_02039CEC->unkE - 1][1] << 8, gUnknown_085713E0[gUnknown_02039CEC->unkE - 1][0], gUnknown_085713E0[gUnknown_02039CEC->unkE - 1][1], sub_8151624(0x100), sub_8151624(0x100), 0); } } void sub_80C6104(u8 cursorArea, u8 previousCursorArea) { bool32 var; switch (previousCursorArea) { case CURSOR_AREA_MAP: CopyToBgTilemapBufferRect_ChangePalette(1, gUnknown_02039CF0->unk24, 16, 3, 12, 7, 17); var = TRUE; break; case CURSOR_AREA_CARD: CopyToBgTilemapBufferRect_ChangePalette(1, gUnknown_02039CF0->unk24 + 336, 16, 10, 12, 7, 17); var = TRUE; break; case CURSOR_AREA_RECORD: if (!gUnknown_02039CEC->hasBattleRecord) { var = FALSE; } else { CopyToBgTilemapBufferRect_ChangePalette(1, gUnknown_02039CF0->unk28, 2, 10, 12, 3, 17); var = TRUE; } break; case CURSOR_AREA_CANCEL: CopyToBgTilemapBufferRect_ChangePalette(1, gUnknown_08DE3350, 21, 0, 9, 2, 17); var = TRUE; break; default: var = FALSE; break; } if (!var) { if (cursorArea == CURSOR_AREA_NOTHING || cursorArea > CURSOR_AREA_CANCEL) return; } switch (cursorArea) { case CURSOR_AREA_MAP: CopyToBgTilemapBufferRect_ChangePalette(1, gUnknown_02039CF0->unk24 + 168, 16, 3, 12, 7, 17); var = TRUE; break; case CURSOR_AREA_CARD: CopyToBgTilemapBufferRect_ChangePalette(1, gUnknown_02039CF0->unk24 + 504, 16, 10, 12, 7, 17); var = TRUE; break; case CURSOR_AREA_RECORD: if (!gUnknown_02039CEC->hasBattleRecord) return; CopyToBgTilemapBufferRect_ChangePalette(1, gUnknown_02039CF0->unk28 + 72, 2, 10, 12, 3, 17); var = TRUE; break; case CURSOR_AREA_CANCEL: CopyToBgTilemapBufferRect_ChangePalette(1, gUnknown_08DE3374, 21, 0, 9, 2, 17); var = TRUE; break; default: var = FALSE; break; } if (!var) { asm("":::"r4"); if (previousCursorArea == CURSOR_AREA_NOTHING || previousCursorArea > CURSOR_AREA_CANCEL) return; } CopyBgTilemapBufferToVram(1); } void sub_80C629C(void) { CopyToBgTilemapBuffer(1, gUnknown_08DE3060, 0, 0); sub_80C6104(gUnknown_02039CEC->cursorArea, gUnknown_02039CEC->previousCursorArea); sub_80C5F58(TRUE, gUnknown_02039CEC->unkE); ShowAndPrintWindows(); CopyBgTilemapBufferToVram(1); } void LoadCursorAndSymbolSprites(void) { u8 spriteId; u8 i = 0; FreeAllSpritePalettes(); ResetAffineAnimData(); LoadSpritePalettes(gUnknown_085714E4); LoadCompressedSpriteSheet(&gUnknown_085714BC[0]); LoadCompressedSpriteSheet(&gUnknown_085714BC[2]); spriteId = CreateSprite(&gUnknown_085715B4[0], gUnknown_02039CEC->cursorX, gUnknown_02039CEC->cursorY, 0); gUnknown_02039CF0->cursorSprite = &gSprites[spriteId]; gUnknown_02039CF0->cursorSprite->oam.priority = 0; for (i = 0; i < NUM_FRONTIER_FACILITIES; i++) { if (gUnknown_02039CEC->facilitySymbols[i] != 0) { struct SpriteTemplate sprite = gUnknown_085715E4; sprite.paletteTag += gUnknown_02039CEC->facilitySymbols[i] - 1; spriteId = CreateSprite(&sprite, gUnknown_08571454[i + CURSOR_AREA_SYMBOL - 1].xStart + 8, gUnknown_08571454[i + CURSOR_AREA_SYMBOL - 1].yStart + 6, i + 1); gUnknown_02039CF0->symbolSprites[i] = &gSprites[spriteId]; gUnknown_02039CF0->symbolSprites[i]->oam.priority = 2; StartSpriteAnim(gUnknown_02039CF0->symbolSprites[i], i); } } } void FreeCursorAndSymbolSprites(void) { u8 i = 0; DestroySprite(gUnknown_02039CF0->cursorSprite); gUnknown_02039CF0->cursorSprite = NULL; for (i = 0; i < NUM_FRONTIER_FACILITIES; i++) { if (gUnknown_02039CF0->symbolSprites[i] != NULL) { DestroySprite(gUnknown_02039CF0->symbolSprites[i]); gUnknown_02039CF0->symbolSprites[i] = NULL; } } FreeAllSpritePalettes(); FreeSpriteTilesByTag(2); FreeSpriteTilesByTag(0); } void nullsub_39(void) { } // Frontier Map code. struct FrontierMapData { void (*callback)(void); struct Sprite *cursorSprite; struct Sprite *playerHeadSprite; struct Sprite *mapIndicatorSprite; u8 cursorPos; u8 unk11; u8 tilemapBuff0[0x1000]; u8 tilemapBuff1[0x1000]; u8 tilemapBuff2[0x1000]; }; extern struct FrontierMapData *gUnknown_02039CF4; // Forward declarations. void sub_80C67BC(u8 taskId); void sub_80C6B94(void); void sub_80C6974(void); void sub_80C6C70(u8 direction); extern const struct BgTemplate gUnknown_085713F4[3]; extern const struct WindowTemplate gUnknown_08571428[]; extern const u32 gUnknown_0856FBBC[]; extern const u32 gUnknown_08570E00[]; void ShowFrontierMap(void (*callback)(void)) { if (gUnknown_02039CF4 != NULL) SetMainCallback2(callback); // This line doesn't make sense at all, since it gets overwritten later anyway. gUnknown_02039CF4 = AllocZeroed(sizeof(*gUnknown_02039CF4)); gUnknown_02039CF4->callback = callback; ResetTasks(); CreateTask(sub_80C67BC, 0); SetMainCallback2(CB2_FrontierPass); } void sub_80C6498(void) { ResetTasks(); SetMainCallback2(gUnknown_02039CF4->callback); memset(gUnknown_02039CF4, 0, sizeof(*gUnknown_02039CF4)); // Pointless memory clear. FREE_AND_SET_NULL(gUnknown_02039CF4); } bool32 sub_80C64CC(void) { switch (gUnknown_02039CEC->state) { case 0: SetVBlankCallback(NULL); ScanlineEffect_Stop(); SetVBlankHBlankCallbacksToNull(); break; case 1: sub_80C50D0(); break; case 2: ResetSpriteData(); FreeAllSpritePalettes(); ResetPaletteFade(); reset_temp_tile_data_buffers(); break; case 3: ResetBgsAndClearDma3BusyFlags(0); InitBgsFromTemplates(0, gUnknown_085713F4, ARRAY_COUNT(gUnknown_085713F4)); SetBgTilemapBuffer(0, gUnknown_02039CF4->tilemapBuff0); SetBgTilemapBuffer(1, gUnknown_02039CF4->tilemapBuff1); SetBgTilemapBuffer(2, gUnknown_02039CF4->tilemapBuff2); FillBgTilemapBufferRect_Palette0(0, 0, 0, 0, 30, 20); FillBgTilemapBufferRect_Palette0(1, 0, 0, 0, 30, 20); FillBgTilemapBufferRect_Palette0(2, 0, 0, 0, 30, 20); CopyBgTilemapBufferToVram(0); CopyBgTilemapBufferToVram(1); CopyBgTilemapBufferToVram(2); break; case 4: InitWindows(gUnknown_08571428); DeactivateAllTextPrinters(); sub_80C6B94(); decompress_and_copy_tile_data_to_vram(1, gUnknown_0856FBBC, 0, 0, 0); break; case 5: if (free_temp_tile_data_buffers_if_possible()) return FALSE; LoadPalette(gUnknown_08DE07C8[0], 0, 0x1A0); LoadPalette(stdpal_get(0), 0xF0, 0x20); CopyToBgTilemapBuffer(2, gUnknown_08570E00, 0, 0); CopyBgTilemapBufferToVram(2); break; case 6: SetGpuReg(REG_OFFSET_DISPCNT, DISPCNT_OBJ_ON | DISPCNT_OBJ_1D_MAP); ShowBg(0); ShowBg(1); ShowBg(2); sub_80C6974(); SetVBlankCallback(VblankCb_FrontierPass); BlendPalettes(0xFFFFFFFF, 0x10, RGB_WHITE); BeginNormalPaletteFade(0xFFFFFFFF, 0, 0x10, 0, RGB_WHITE); break; case 7: if (UpdatePaletteFade()) return FALSE; gUnknown_02039CEC->state = 0; return TRUE; } gUnknown_02039CEC->state++; return FALSE; } bool32 sub_80C66AC(void) { switch (gUnknown_02039CEC->state) { case 0: BeginNormalPaletteFade(0xFFFFFFFF, 0, 0, 0x10, RGB_WHITE); break; case 1: if (UpdatePaletteFade()) return FALSE; SetGpuReg(REG_OFFSET_DISPCNT, 0); HideBg(0); HideBg(1); HideBg(2); break; case 2: SetVBlankCallback(NULL); ScanlineEffect_Stop(); SetVBlankHBlankCallbacksToNull(); break; case 3: if (gUnknown_02039CF4->cursorSprite != NULL) { DestroySprite(gUnknown_02039CF4->cursorSprite); FreeSpriteTilesByTag(0); } if (gUnknown_02039CF4->mapIndicatorSprite != NULL) { DestroySprite(gUnknown_02039CF4->mapIndicatorSprite); FreeSpriteTilesByTag(1); } if (gUnknown_02039CF4->playerHeadSprite != NULL) { DestroySprite(gUnknown_02039CF4->playerHeadSprite); FreeSpriteTilesByTag(4); } FreeAllWindowBuffers(); break; case 4: sub_80C50D0(); ResetSpriteData(); FreeAllSpritePalettes(); break; case 5: UnsetBgTilemapBuffer(0); UnsetBgTilemapBuffer(1); UnsetBgTilemapBuffer(2); gUnknown_02039CEC->state = 0; return TRUE; } gUnknown_02039CEC->state++; return FALSE; } void sub_80C67BC(u8 taskId) { s16 *data = gTasks[taskId].data; switch (data[0]) { case 0: if (sub_80C64CC()) break; return; case 1: if (gMain.newKeys & B_BUTTON) { PlaySE(SE_PC_OFF); data[0] = 4; } else if (gMain.newKeys & DPAD_DOWN) { if (gUnknown_02039CF4->cursorPos >= NUM_FRONTIER_FACILITIES - 1) sub_80C6C70(0); else data[0] = 2; } else if (gMain.newKeys & DPAD_UP) { if (gUnknown_02039CF4->cursorPos == 0) sub_80C6C70(1); else data[0] = 3; } return; case 2: if (data[1] > 3) { sub_80C6C70(0); data[1] = 0; data[0] = 1; } else { gUnknown_02039CF4->cursorSprite->pos1.y += 4; data[1]++; } return; case 3: if (data[1] > 3) { sub_80C6C70(1); data[1] = 0; data[0] = 1; } else { gUnknown_02039CF4->cursorSprite->pos1.y -= 4; data[1]++; } return; case 4: if (sub_80C66AC()) break; return; case 5: DestroyTask(taskId); sub_80C6498(); return; } data[0]++; } u8 sub_80C68E8(u16 mapNum) // id + 1, zero means not a frontier map number { if ((mapNum >= MAP_NUM(BATTLE_FRONTIER_BATTLE_TOWER_LOBBY) && mapNum <= MAP_NUM(BATTLE_FRONTIER_BATTLE_TOWER_BATTLE_ROOM)) || (mapNum >= MAP_NUM(BATTLE_FRONTIER_BATTLE_TOWER_MULTI_BATTLE_ROOM) && mapNum <= MAP_NUM(BATTLE_FRONTIER_BATTLE_TOWER_BATTLE_ROOM2))) return FRONTIER_FACILITY_TOWER + 1; else if (mapNum == MAP_NUM(BATTLE_FRONTIER_BATTLE_DOME_LOBBY) || mapNum == MAP_NUM(BATTLE_FRONTIER_BATTLE_DOME_CORRIDOR) || mapNum == MAP_NUM(BATTLE_FRONTIER_BATTLE_DOME_PRE_BATTLE_ROOM) || mapNum == MAP_NUM(BATTLE_FRONTIER_BATTLE_DOME_BATTLE_ROOM)) return FRONTIER_FACILITY_DOME + 1; else if (mapNum == MAP_NUM(BATTLE_FRONTIER_BATTLE_PALACE_LOBBY) || mapNum == MAP_NUM(BATTLE_FRONTIER_BATTLE_PALACE_CORRIDOR) || mapNum == MAP_NUM(BATTLE_FRONTIER_BATTLE_PALACE_BATTLE_ROOM)) return FRONTIER_FACILITY_PALACE + 1; else if (mapNum == MAP_NUM(BATTLE_FRONTIER_BATTLE_ARENA_LOBBY) || mapNum == MAP_NUM(BATTLE_FRONTIER_BATTLE_ARENA_CORRIDOR) || mapNum == MAP_NUM(BATTLE_FRONTIER_BATTLE_ARENA_BATTLE_ROOM)) return FRONTIER_FACILITY_ARENA + 1; else if (mapNum == MAP_NUM(BATTLE_FRONTIER_BATTLE_FACTORY_LOBBY) || mapNum == MAP_NUM(BATTLE_FRONTIER_BATTLE_FACTORY_PRE_BATTLE_ROOM) || mapNum == MAP_NUM(BATTLE_FRONTIER_BATTLE_FACTORY_BATTLE_ROOM)) return FRONTIER_FACILITY_FACTORY + 1; else if (mapNum == MAP_NUM(BATTLE_FRONTIER_BATTLE_PIKE_LOBBY) || mapNum == MAP_NUM(BATTLE_FRONTIER_BATTLE_PIKE_CORRIDOR) || mapNum == MAP_NUM(BATTLE_FRONTIER_BATTLE_PIKE_THREE_PATH_ROOM) || mapNum == MAP_NUM(BATTLE_FRONTIER_BATTLE_PIKE_RANDOM_ROOM1) || mapNum == MAP_NUM(BATTLE_FRONTIER_BATTLE_PIKE_RANDOM_ROOM2) || mapNum == MAP_NUM(BATTLE_FRONTIER_BATTLE_PIKE_RANDOM_ROOM3)) return FRONTIER_FACILITY_PIKE + 1; else if (mapNum == MAP_NUM(BATTLE_FRONTIER_BATTLE_PYRAMID_LOBBY) || mapNum == MAP_NUM(BATTLE_FRONTIER_BATTLE_PYRAMID_EMPTY_SQUARE) || mapNum == MAP_NUM(BATTLE_FRONTIER_BATTLE_PYRAMID_TOP)) return FRONTIER_FACILITY_PYRAMID + 1; else return 0; } struct { const u8 *name; const u8 *description; s16 x; s16 y; u8 animNum; } extern const gUnknown_08571650[]; extern const struct CompressedSpriteSheet gUnknown_085714D4[]; extern const struct SpriteTemplate gUnknown_085715FC; void sub_80C6974(void) { struct SpriteTemplate sprite; u8 spriteId; u8 id; s16 x = 0, y; FreeAllSpritePalettes(); LoadSpritePalettes(gUnknown_085714E4); LoadCompressedSpriteSheet(&gUnknown_085714BC[0]); spriteId = CreateSprite(&gUnknown_085715B4[0], 155, (gUnknown_02039CF4->cursorPos * 16) + 8, 2); gUnknown_02039CF4->cursorSprite = &gSprites[spriteId]; gUnknown_02039CF4->cursorSprite->oam.priority = 0; gUnknown_02039CF4->cursorSprite->hFlip = TRUE; StartSpriteAnim(gUnknown_02039CF4->cursorSprite, 1); LoadCompressedSpriteSheet(&gUnknown_085714BC[1]); spriteId = CreateSprite(&gUnknown_085715B4[1], gUnknown_08571650[gUnknown_02039CF4->cursorPos].x, gUnknown_08571650[gUnknown_02039CF4->cursorPos].y, 1); gUnknown_02039CF4->mapIndicatorSprite = &gSprites[spriteId]; gUnknown_02039CF4->mapIndicatorSprite->oam.priority = 0; StartSpriteAnim(gUnknown_02039CF4->mapIndicatorSprite, gUnknown_08571650[gUnknown_02039CF4->cursorPos].animNum); // Create player indicator head sprite only if it's in vicinity of battle frontier. id = GetCurrentRegionMapSectionId(); if (id == MAPSEC_BATTLE_FRONTIER || id == MAPSEC_ARTISAN_CAVE) { s8 mapNum = gSaveBlock1Ptr->location.mapNum; if (mapNum == MAP_NUM(BATTLE_FRONTIER_OUTSIDE_WEST) || (mapNum == MAP_NUM(BATTLE_FRONTIER_OUTSIDE_EAST) && (x = 55))) { x += gSaveBlock1Ptr->pos.x; y = gSaveBlock1Ptr->pos.y; x /= 8; y /= 8; id = 0; } else { id = sub_80C68E8(mapNum); if (id != 0) { x = gUnknown_08571650[id - 1].x; y = gUnknown_08571650[id - 1].y; } else { // Handle Artisan Cave. if (gSaveBlock1Ptr->escapeWarp.mapNum == MAP_NUM(BATTLE_FRONTIER_OUTSIDE_EAST)) x = gSaveBlock1Ptr->escapeWarp.x + 55; else x = gSaveBlock1Ptr->escapeWarp.x; y = gSaveBlock1Ptr->escapeWarp.y; x /= 8; y /= 8; } } LoadCompressedSpriteSheet(gUnknown_085714D4); sprite = gUnknown_085715FC; sprite.paletteTag = gSaveBlock2Ptr->playerGender + 4; if (id != 0) { spriteId = CreateSprite(&sprite, x, y, 0); } else { x *= 8; y *= 8; spriteId = CreateSprite(&sprite, x + 20, y + 36, 0); } gUnknown_02039CF4->playerHeadSprite = &gSprites[spriteId]; gUnknown_02039CF4->playerHeadSprite->oam.priority = 0; if (gSaveBlock2Ptr->playerGender != MALE) StartSpriteAnim(gUnknown_02039CF4->playerHeadSprite, 1); } } enum { MAP_WINDOW_0, MAP_WINDOW_NAME, MAP_WINDOW_DESCRIPTION, MAP_WINDOW_COUNT }; void sub_80C6B94(void) { u8 i; for (i = 0; i < MAP_WINDOW_COUNT; i++) { PutWindowTilemap(i); FillWindowPixelBuffer(i, 0); } for (i = 0; i < NUM_FRONTIER_FACILITIES; i++) { if (i == gUnknown_02039CF4->cursorPos) AddTextPrinterParameterized3(MAP_WINDOW_NAME, 7, 4, (i * 16) + 1, gUnknown_08571448[2], 0, gUnknown_08571650[i].name); else AddTextPrinterParameterized3(MAP_WINDOW_NAME, 7, 4, (i * 16) + 1, gUnknown_08571448[1], 0, gUnknown_08571650[i].name); } AddTextPrinterParameterized3(MAP_WINDOW_DESCRIPTION, 1, 4, 0, gUnknown_08571448[0], 0, gUnknown_08571650[gUnknown_02039CF4->cursorPos].description); for (i = 0; i < MAP_WINDOW_COUNT; i++) CopyWindowToVram(i, 3); CopyBgTilemapBufferToVram(0); } void sub_80C6C70(u8 direction) { u8 oldCursorPos, i; if (direction) { oldCursorPos = gUnknown_02039CF4->cursorPos; gUnknown_02039CF4->cursorPos = (oldCursorPos + 6) % NUM_FRONTIER_FACILITIES; } else { oldCursorPos = gUnknown_02039CF4->cursorPos; gUnknown_02039CF4->cursorPos = (oldCursorPos + 1) % NUM_FRONTIER_FACILITIES; } AddTextPrinterParameterized3(MAP_WINDOW_NAME, 7, 4, (oldCursorPos * 16) + 1, gUnknown_08571448[1], 0, gUnknown_08571650[oldCursorPos].name); AddTextPrinterParameterized3(MAP_WINDOW_NAME, 7, 4, (gUnknown_02039CF4->cursorPos * 16) + 1, gUnknown_08571448[2], 0, gUnknown_08571650[gUnknown_02039CF4->cursorPos].name); gUnknown_02039CF4->cursorSprite->pos1.y = (gUnknown_02039CF4->cursorPos * 16) + 8; StartSpriteAnim(gUnknown_02039CF4->mapIndicatorSprite, gUnknown_08571650[gUnknown_02039CF4->cursorPos].animNum); gUnknown_02039CF4->mapIndicatorSprite->pos1.x = gUnknown_08571650[gUnknown_02039CF4->cursorPos].x; gUnknown_02039CF4->mapIndicatorSprite->pos1.y = gUnknown_08571650[gUnknown_02039CF4->cursorPos].y; FillWindowPixelBuffer(MAP_WINDOW_DESCRIPTION, 0); AddTextPrinterParameterized3(MAP_WINDOW_DESCRIPTION, 1, 4, 0, gUnknown_08571448[0], 0, gUnknown_08571650[gUnknown_02039CF4->cursorPos].description); for (i = 0; i < 3; i++) CopyWindowToVram(i, 3); CopyBgTilemapBufferToVram(0); PlaySE(SE_Z_SCROLL); }