#include "global.h" #include "gpu_regs.h" #include "main.h" #include "trainer_card.h" #include "battle_anim.h" #include "event_data.h" #include "recorded_battle.h" #include "malloc.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/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_DUMMY, WINDOW_COUNT }; // Windows displayed in the facilities map view. enum { MAP_WINDOW_UNUSED, // Overlaps the "Battle Frontier" title area of the map MAP_WINDOW_NAME, MAP_WINDOW_DESCRIPTION, MAP_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 containing the symbols CURSOR_AREA_SYMBOL_TOWER, CURSOR_AREA_SYMBOL_DOME, CURSOR_AREA_SYMBOL_PALACE, CURSOR_AREA_SYMBOL_ARENA, CURSOR_AREA_SYMBOL_FACTORY, CURSOR_AREA_SYMBOL_PIKE, CURSOR_AREA_SYMBOL_PYRAMID, CURSOR_AREA_COUNT }; // Start of symbol cursor areas #define CURSOR_AREA_SYMBOL CURSOR_AREA_SYMBOL_TOWER enum { MAP_INDICATOR_RECTANGLE, MAP_INDICATOR_SQUARE, }; enum { TAG_CURSOR, TAG_MAP_INDICATOR, TAG_MEDAL_SILVER, TAG_MEDAL_GOLD, TAG_HEAD_MALE, TAG_HEAD_FEMALE, }; // Error return codes. Never read enum { SUCCESS, ERR_ALREADY_DONE, ERR_ALLOC_FAILED, }; struct FrontierPassData { void (*callback)(void); u16 state; u16 battlePoints; s16 cursorX; s16 cursorY; u8 cursorArea; u8 previousCursorArea; bool8 hasBattleRecord:1; u8 areaToShow:3; u8 trainerStars:4; u8 facilitySymbols[NUM_FRONTIER_FACILITIES]; // 0: no symbol, 1: silver, 2: gold }; struct FrontierPassGfx { struct Sprite *cursorSprite; struct Sprite *symbolSprites[NUM_FRONTIER_FACILITIES]; // These 3 tilemaps are used to overwrite the respective area when highlighted u8 *mapAndCardZoomTilemap; u8 *mapAndCardTilemap; u8 *battleRecordTilemap; bool8 zooming; s16 scaleX; s16 scaleY; u8 tilemapBuff1[BG_SCREEN_SIZE * 2]; u8 tilemapBuff2[BG_SCREEN_SIZE * 2]; u8 tilemapBuff3[BG_SCREEN_SIZE / 2]; }; struct FrontierPassSaved { void (*callback)(void); s16 cursorX; s16 cursorY; }; struct FrontierMapData { void (*callback)(void); struct Sprite *cursorSprite; struct Sprite *playerHeadSprite; struct Sprite *mapIndicatorSprite; u8 cursorPos; u8 unused; u8 tilemapBuff0[BG_SCREEN_SIZE * 2]; u8 tilemapBuff1[BG_SCREEN_SIZE * 2]; u8 tilemapBuff2[BG_SCREEN_SIZE * 2]; }; static EWRAM_DATA struct FrontierPassData *sPassData = NULL; static EWRAM_DATA struct FrontierPassGfx *sPassGfx = NULL; static EWRAM_DATA struct FrontierMapData *sMapData = NULL; static EWRAM_DATA struct FrontierPassSaved sSavedPassData = {0}; static u32 AllocateFrontierPassData(void (*callback)(void)); static void ShowFrontierMap(void (*callback)(void)); static void CB2_InitFrontierPass(void); static void DrawFrontierPassBg(void); static void FreeCursorAndSymbolSprites(void); static void LoadCursorAndSymbolSprites(void); static u32 FreeFrontierPassData(void); static bool32 InitFrontierPass(void); static bool32 HideFrontierPass(void); static void Task_HandleFrontierPassInput(u8); static void Task_PassAreaZoom(u8); static void UpdateAreaHighlight(u8, u8); static void PrintAreaDescription(u8); static void ShowHideZoomingArea(bool8, bool8); static void SpriteCB_PlayerHead(struct Sprite *); static const u16 sMaleHead_Pal[] = INCBIN_U16("graphics/frontier_pass/map_heads.gbapal"); static const u16 sFemaleHead_Pal[] = INCBIN_U16("graphics/frontier_pass/map_heads_female.gbapal"); static const u32 sMapScreen_Gfx[] = INCBIN_U32("graphics/frontier_pass/map_screen.4bpp.lz"); static const u32 sCursor_Gfx[] = INCBIN_U32("graphics/frontier_pass/cursor.4bpp.lz"); static const u32 sHeads_Gfx[] = INCBIN_U32("graphics/frontier_pass/map_heads.4bpp.lz"); static const u32 sMapCursor_Gfx[] = INCBIN_U32("graphics/frontier_pass/map_cursor.4bpp.lz"); static const u32 sMapScreen_Tilemap[] = INCBIN_U32("graphics/frontier_pass/map_screen.bin.lz"); static const u32 sMapAndCard_ZoomedOut_Tilemap[] = INCBIN_U32("graphics/frontier_pass/small_map_and_card.bin.lz"); static const u32 sUnusedData[] = INCBIN_U32("graphics/frontier_pass/unused.bin"); static const u32 sBattleRecord_Tilemap[] = INCBIN_U32("graphics/frontier_pass/record_frame.bin.lz"); static const u32 sMapAndCard_Zooming_Tilemap[] = INCBIN_U32("graphics/frontier_pass/small_map_and_card_affine.bin.lz"); static const s16 sBgAffineCoords[][2] = { [CURSOR_AREA_MAP - 1] = {216, 32}, [CURSOR_AREA_CARD - 1] = {216, 128} }; static const struct BgTemplate sPassBgTemplates[] = { { .bg = 0, .charBaseIndex = 2, .mapBaseIndex = 31, .screenSize = 0, .paletteMode = 0, .priority = 1, .baseTile = 0 }, { .bg = 1, .charBaseIndex = 0, .mapBaseIndex = 30, .screenSize = 0, .paletteMode = 0, .priority = 2, .baseTile = 0 }, { .bg = 2, .charBaseIndex = 1, .mapBaseIndex = 29, .screenSize = 1, .paletteMode = 1, .priority = 0, .baseTile = 0 }, }; static const struct BgTemplate sMapBgTemplates[] = { { .bg = 0, .charBaseIndex = 2, .mapBaseIndex = 31, .screenSize = 0, .paletteMode = 0, .priority = 0, .baseTile = 0 }, { .bg = 1, .charBaseIndex = 0, .mapBaseIndex = 30, .screenSize = 0, .paletteMode = 0, .priority = 1, .baseTile = 0 }, { .bg = 2, .charBaseIndex = 0, .mapBaseIndex = 29, .screenSize = 0, .paletteMode = 0, .priority = 2, .baseTile = 0 }, }; static const struct WindowTemplate sPassWindowTemplates[WINDOW_COUNT] = { [WINDOW_EARNED_SYMBOLS] = { .bg = 0, .tilemapLeft = 2, .tilemapTop = 3, .width = 12, .height = 3, .paletteNum = 15, .baseBlock = 0x1, }, [WINDOW_BATTLE_RECORD] = { .bg = 0, .tilemapLeft = 2, .tilemapTop = 10, .width = 12, .height = 3, .paletteNum = 15, .baseBlock = 0x26, }, [WINDOW_BATTLE_POINTS] = { .bg = 0, .tilemapLeft = 2, .tilemapTop = 13, .width = 12, .height = 4, .paletteNum = 15, .baseBlock = 0x4B, }, [WINDOW_DESCRIPTION] = { .bg = 0, .tilemapLeft = 0, .tilemapTop = 18, .width = 30, .height = 3, .paletteNum = 15, .baseBlock = 0x7C, }, DUMMY_WIN_TEMPLATE }; static const struct WindowTemplate sMapWindowTemplates[] = { [MAP_WINDOW_UNUSED] = { .bg = 0, .tilemapLeft = 0, .tilemapTop = 1, .width = 15, .height = 5, .paletteNum = 15, .baseBlock = 0x1, }, [MAP_WINDOW_NAME] = { .bg = 0, .tilemapLeft = 20, .tilemapTop = 1, .width = 10, .height = 14, .paletteNum = 15, .baseBlock = 0x4D, }, [MAP_WINDOW_DESCRIPTION] = { .bg = 0, .tilemapLeft = 2, .tilemapTop = 16, .width = 26, .height = 4, .paletteNum = 15, .baseBlock = 0xDA, }, DUMMY_WIN_TEMPLATE }; static const u8 sTextColors[][3] = { {TEXT_COLOR_TRANSPARENT, TEXT_COLOR_DARK_GRAY, TEXT_COLOR_LIGHT_GRAY}, {TEXT_COLOR_TRANSPARENT, TEXT_COLOR_WHITE, TEXT_COLOR_LIGHT_BLUE}, {TEXT_COLOR_TRANSPARENT, TEXT_COLOR_RED, TEXT_COLOR_LIGHT_RED}, }; struct { s16 yStart; s16 yEnd; s16 xStart; s16 xEnd; } static const sPassAreasLayout[CURSOR_AREA_COUNT - 1] = { [CURSOR_AREA_MAP - 1] = { 28, 76, 132, 220}, [CURSOR_AREA_CARD - 1] = { 84, 132, 132, 220}, [CURSOR_AREA_RECORD - 1] = { 80, 102, 20, 108}, [CURSOR_AREA_CANCEL - 1] = { 0, 16, 152, 240}, [CURSOR_AREA_POINTS - 1] = {108, 134, 20, 108}, [CURSOR_AREA_EARNED_SYMBOLS - 1] = { 24, 48, 20, 108}, [CURSOR_AREA_SYMBOL_TOWER - 1] = { 50, 66, 20, 36}, [CURSOR_AREA_SYMBOL_DOME - 1] = { 66, 82, 32, 48}, [CURSOR_AREA_SYMBOL_PALACE - 1] = { 50, 66, 44, 60}, [CURSOR_AREA_SYMBOL_ARENA - 1] = { 66, 82, 56, 72}, [CURSOR_AREA_SYMBOL_FACTORY - 1] = { 50, 66, 68, 84}, [CURSOR_AREA_SYMBOL_PIKE - 1] = { 66, 82, 80, 96}, [CURSOR_AREA_SYMBOL_PYRAMID - 1] = { 50, 66, 92, 108}, }; static const struct CompressedSpriteSheet sCursorSpriteSheets[] = { {sCursor_Gfx, 0x100, TAG_CURSOR}, {sMapCursor_Gfx, 0x400, TAG_MAP_INDICATOR}, {gFrontierPassMedals_Gfx, 0x380, TAG_MEDAL_SILVER}, }; static const struct CompressedSpriteSheet sHeadsSpriteSheet[] = { {sHeads_Gfx, 0x100, TAG_HEAD_MALE}, {} }; static const struct SpritePalette sSpritePalettes[] = { {gFrontierPassCursor_Pal, TAG_CURSOR}, {gFrontierPassMapCursor_Pal, TAG_MAP_INDICATOR}, {gFrontierPassMedalsSilver_Pal, TAG_MEDAL_SILVER}, {gFrontierPassMedalsGold_Pal, TAG_MEDAL_GOLD}, {sMaleHead_Pal, TAG_HEAD_MALE}, {sFemaleHead_Pal, TAG_HEAD_FEMALE}, {} }; static const union AnimCmd sAnim_Frame1_Unused[] = { ANIMCMD_FRAME(0, 0), ANIMCMD_END }; static const union AnimCmd sAnim_Frame1[] = { ANIMCMD_FRAME(0, 0), ANIMCMD_END }; static const union AnimCmd sAnim_Frame2[] = { ANIMCMD_FRAME(4, 0), ANIMCMD_END }; static const union AnimCmd sAnim_Frame3[] = { ANIMCMD_FRAME(8, 0), ANIMCMD_END }; static const union AnimCmd sAnim_Frame4[] = { ANIMCMD_FRAME(12, 0), ANIMCMD_END }; static const union AnimCmd sAnim_Frame5[] = { ANIMCMD_FRAME(16, 0), ANIMCMD_END }; static const union AnimCmd sAnim_Frame6[] = { ANIMCMD_FRAME(20, 0), ANIMCMD_END }; static const union AnimCmd sAnim_Frame7[] = { ANIMCMD_FRAME(24, 0), ANIMCMD_END }; static const union AnimCmd sAnim_MapIndicatorCursor_Rectangle[] = { ANIMCMD_FRAME(0, 45), ANIMCMD_FRAME(8, 45), ANIMCMD_JUMP(0) }; static const union AnimCmd sAnim_MapIndicatorCursor_Square[] = { ANIMCMD_FRAME(16, 45), ANIMCMD_FRAME(24, 45), ANIMCMD_JUMP(0) }; // Used both by the cursor and the map head icons static const union AnimCmd *const sAnims_TwoFrame[] = { sAnim_Frame1, sAnim_Frame2 }; static const union AnimCmd *const sAnims_Medal[] = { [CURSOR_AREA_SYMBOL_TOWER - CURSOR_AREA_SYMBOL] = sAnim_Frame1, [CURSOR_AREA_SYMBOL_DOME - CURSOR_AREA_SYMBOL] = sAnim_Frame2, [CURSOR_AREA_SYMBOL_PALACE - CURSOR_AREA_SYMBOL] = sAnim_Frame3, [CURSOR_AREA_SYMBOL_ARENA - CURSOR_AREA_SYMBOL] = sAnim_Frame4, [CURSOR_AREA_SYMBOL_FACTORY - CURSOR_AREA_SYMBOL] = sAnim_Frame5, [CURSOR_AREA_SYMBOL_PIKE - CURSOR_AREA_SYMBOL] = sAnim_Frame6, [CURSOR_AREA_SYMBOL_PYRAMID - CURSOR_AREA_SYMBOL] = sAnim_Frame7 }; static const union AnimCmd *const sAnims_MapIndicatorCursor[] = { [MAP_INDICATOR_RECTANGLE] = sAnim_MapIndicatorCursor_Rectangle, [MAP_INDICATOR_SQUARE] = sAnim_MapIndicatorCursor_Square }; static const union AffineAnimCmd sAffineAnim_Unused[] = { AFFINEANIMCMD_FRAME(256, 256, 0, 0), AFFINEANIMCMD_END }; static const union AffineAnimCmd *const sAffineAnims_Unused[] = { sAffineAnim_Unused }; static const struct SpriteTemplate sSpriteTemplates_Cursors[] = { // Triangular cursor { .tileTag = TAG_CURSOR, .paletteTag = TAG_CURSOR, .oam = &gOamData_AffineOff_ObjNormal_16x16, .anims = sAnims_TwoFrame, .images = NULL, .affineAnims = gDummySpriteAffineAnimTable, .callback = SpriteCallbackDummy, }, // Map indicator cursor { .tileTag = TAG_MAP_INDICATOR, .paletteTag = TAG_MAP_INDICATOR, .oam = &gOamData_AffineOff_ObjNormal_32x16, .anims = sAnims_MapIndicatorCursor, .images = NULL, .affineAnims = gDummySpriteAffineAnimTable, .callback = SpriteCallbackDummy, }, }; static const struct SpriteTemplate sSpriteTemplate_Medal = { .tileTag = TAG_MEDAL_SILVER, .paletteTag = TAG_MEDAL_SILVER, .oam = &gOamData_AffineOff_ObjNormal_16x16, .anims = sAnims_Medal, .images = NULL, .affineAnims = gDummySpriteAffineAnimTable, .callback = SpriteCallbackDummy, }; static const struct SpriteTemplate sSpriteTemplate_PlayerHead = { .tileTag = TAG_HEAD_MALE, .paletteTag = TAG_HEAD_MALE, .oam = &gOamData_AffineOff_ObjNormal_16x16, .anims = sAnims_TwoFrame, .images = NULL, .affineAnims = gDummySpriteAffineAnimTable, .callback = SpriteCB_PlayerHead, }; static const u8 *const sPassAreaDescriptions[CURSOR_AREA_COUNT + 1] = { [CURSOR_AREA_NOTHING] = gText_ThereIsNoBattleRecord, // NOTHING is re-used for CURSOR_AREA_RECORD when no Record is present [CURSOR_AREA_MAP] = gText_CheckFrontierMap, [CURSOR_AREA_CARD] = gText_CheckTrainerCard, [CURSOR_AREA_RECORD] = gText_ViewRecordedBattle, [CURSOR_AREA_CANCEL] = gText_PutAwayFrontierPass, [CURSOR_AREA_POINTS] = gText_CurrentBattlePoints, [CURSOR_AREA_EARNED_SYMBOLS] = gText_CollectedSymbols, [CURSOR_AREA_SYMBOL_TOWER] = gText_BattleTowerAbilitySymbol, [CURSOR_AREA_SYMBOL_DOME] = gText_BattleDomeTacticsSymbol, [CURSOR_AREA_SYMBOL_PALACE] = gText_BattlePalaceSpiritsSymbol, [CURSOR_AREA_SYMBOL_ARENA] = gText_BattleArenaGutsSymbol, [CURSOR_AREA_SYMBOL_FACTORY] = gText_BattleFactoryKnowledgeSymbol, [CURSOR_AREA_SYMBOL_PIKE] = gText_BattlePikeLuckSymbol, [CURSOR_AREA_SYMBOL_PYRAMID] = gText_BattlePyramidBraveSymbol, [CURSOR_AREA_COUNT] = gText_EmptyString7, }; struct { const u8 *name; const u8 *description; s16 x; s16 y; u8 animNum; } static const sMapLandmarks[NUM_FRONTIER_FACILITIES] = { [FRONTIER_FACILITY_TOWER] = {gText_BattleTower3, gText_BattleTowerDesc, 89, 40, MAP_INDICATOR_SQUARE}, [FRONTIER_FACILITY_DOME] = {gText_BattleDome2, gText_BattleDomeDesc, 33, 42, MAP_INDICATOR_SQUARE}, [FRONTIER_FACILITY_PALACE] = {gText_BattlePalace2, gText_BattlePalaceDesc, 120, 86, MAP_INDICATOR_RECTANGLE}, [FRONTIER_FACILITY_ARENA] = {gText_BattleArena2, gText_BattleArenaDesc, 114, 59, MAP_INDICATOR_RECTANGLE}, [FRONTIER_FACILITY_FACTORY] = {gText_BattleFactory2, gText_BattleFactoryDesc, 25, 67, MAP_INDICATOR_RECTANGLE}, [FRONTIER_FACILITY_PIKE] = {gText_BattlePike2, gText_BattlePikeDesc, 57, 57, MAP_INDICATOR_SQUARE}, [FRONTIER_FACILITY_PYRAMID] = {gText_BattlePyramid2, gText_BattlePyramidDesc, 134, 41, MAP_INDICATOR_SQUARE}, }; static void ResetGpuRegsAndBgs(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, BG_COORD_SET); ChangeBgY(0, 0, BG_COORD_SET); ChangeBgX(1, 0, BG_COORD_SET); ChangeBgY(1, 0, BG_COORD_SET); ChangeBgX(2, 0, BG_COORD_SET); ChangeBgY(2, 0, BG_COORD_SET); ChangeBgX(3, 0, BG_COORD_SET); ChangeBgY(3, 0, BG_COORD_SET); 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); } static void LeaveFrontierPass(void) { SetMainCallback2(sPassData->callback); FreeFrontierPassData(); } static u32 AllocateFrontierPassData(void (*callback)(void)) { u8 i; if (sPassData != NULL) return ERR_ALREADY_DONE; sPassData = AllocZeroed(sizeof(*sPassData)); if (sPassData == NULL) return ERR_ALLOC_FAILED; sPassData->callback = callback; i = GetCurrentRegionMapSectionId(); if (i != MAPSEC_BATTLE_FRONTIER && i != MAPSEC_ARTISAN_CAVE) { // Player is not in the frontier, set // cursor position to the Trainer Card sPassData->cursorX = 176; sPassData->cursorY = 104; } else { // Player is in the frontier, set // cursor position to the frontier map sPassData->cursorX = 176; sPassData->cursorY = 48; } sPassData->battlePoints = gSaveBlock2Ptr->frontier.battlePoints; sPassData->hasBattleRecord = CanCopyRecordedBattleSaveData(); sPassData->areaToShow = CURSOR_AREA_NOTHING; sPassData->trainerStars = CountPlayerTrainerStars(); for (i = 0; i < NUM_FRONTIER_FACILITIES; i++) { if (FlagGet(FLAG_SYS_TOWER_SILVER + i * 2)) sPassData->facilitySymbols[i]++; if (FlagGet(FLAG_SYS_TOWER_GOLD + i * 2)) sPassData->facilitySymbols[i]++; } return SUCCESS; } static u32 FreeFrontierPassData(void) { if (sPassData == NULL) return ERR_ALREADY_DONE; memset(sPassData, 0, sizeof(*sPassData)); // Why clear data, if it's going to be freed anyway? FREE_AND_SET_NULL(sPassData); return SUCCESS; } static u32 AllocateFrontierPassGfx(void) { if (sPassGfx != NULL) return ERR_ALREADY_DONE; sPassGfx = AllocZeroed(sizeof(*sPassGfx)); if (sPassGfx == NULL) return ERR_ALLOC_FAILED; return SUCCESS; } static u32 FreeFrontierPassGfx(void) { FreeAllWindowBuffers(); if (sPassGfx == NULL) return ERR_ALREADY_DONE; TRY_FREE_AND_SET_NULL(sPassGfx->battleRecordTilemap); TRY_FREE_AND_SET_NULL(sPassGfx->mapAndCardTilemap); TRY_FREE_AND_SET_NULL(sPassGfx->mapAndCardZoomTilemap); memset(sPassGfx, 0, sizeof(*sPassGfx)); // Why clear data, if it's going to be freed anyway? FREE_AND_SET_NULL(sPassGfx); return SUCCESS; } static void VBlankCB_FrontierPass(void) { if (sPassGfx->zooming) { SetBgAffine(2, sBgAffineCoords[sPassData->areaToShow - 1][0] << 8, sBgAffineCoords[sPassData->areaToShow - 1][1] << 8, sBgAffineCoords[sPassData->areaToShow - 1][0], sBgAffineCoords[sPassData->areaToShow - 1][1], sPassGfx->scaleX, sPassGfx->scaleY, 0); } LoadOam(); ProcessSpriteCopyRequests(); TransferPlttBuffer(); } static void CB2_FrontierPass(void) { RunTasks(); AnimateSprites(); BuildOamBuffer(); } static void CB2_InitFrontierPass(void) { if (InitFrontierPass()) { CreateTask(Task_HandleFrontierPassInput, 0); SetMainCallback2(CB2_FrontierPass); } } static void CB2_HideFrontierPass(void) { if (HideFrontierPass()) LeaveFrontierPass(); } static bool32 InitFrontierPass(void) { u32 sizeOut = 0; switch (sPassData->state) { case 0: SetVBlankCallback(NULL); ScanlineEffect_Stop(); SetVBlankHBlankCallbacksToNull(); DisableInterrupts(INTR_FLAG_HBLANK); break; case 1: ResetGpuRegsAndBgs(); break; case 2: ResetTasks(); ResetSpriteData(); FreeAllSpritePalettes(); ResetPaletteFade(); ResetTempTileDataBuffers(); break; case 3: AllocateFrontierPassGfx(); break; case 4: ResetBgsAndClearDma3BusyFlags(0); InitBgsFromTemplates(1, sPassBgTemplates, ARRAY_COUNT(sPassBgTemplates)); SetBgTilemapBuffer(1, sPassGfx->tilemapBuff1); SetBgTilemapBuffer(2, sPassGfx->tilemapBuff2); SetBgTilemapBuffer(3, sPassGfx->tilemapBuff3); SetBgAttribute(2, BG_ATTR_WRAPAROUND, 1); break; case 5: InitWindows(sPassWindowTemplates); DeactivateAllTextPrinters(); break; case 6: sPassGfx->mapAndCardZoomTilemap = malloc_and_decompress(sMapAndCard_Zooming_Tilemap, &sizeOut); sPassGfx->mapAndCardTilemap = malloc_and_decompress(sMapAndCard_ZoomedOut_Tilemap, &sizeOut); sPassGfx->battleRecordTilemap = malloc_and_decompress(sBattleRecord_Tilemap, &sizeOut); DecompressAndCopyTileDataToVram(1, gFrontierPassBg_Gfx, 0, 0, 0); DecompressAndCopyTileDataToVram(2, gFrontierPassMapAndCard_Gfx, 0, 0, 0); break; case 7: if (FreeTempTileDataBuffersIfPossible()) 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(gFrontierPassBg_Pal[0], 0, 0x1A0); LoadPalette(gFrontierPassBg_Pal[1 + sPassData->trainerStars], 0x10, 0x20); LoadPalette(GetTextWindowPalette(0), 0xF0, 0x20); DrawFrontierPassBg(); UpdateAreaHighlight(sPassData->cursorArea, sPassData->previousCursorArea); if (sPassData->areaToShow == CURSOR_AREA_MAP || sPassData->areaToShow == CURSOR_AREA_CARD) { sPassData->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(PALETTES_ALL, 16, RGB_BLACK); BeginNormalPaletteFade(PALETTES_ALL, 0, 16, 0, RGB_BLACK); break; case 10: AnimateSprites(); BuildOamBuffer(); if (UpdatePaletteFade()) return FALSE; sPassData->state = 0; return TRUE; } sPassData->state++; return FALSE; } static bool32 HideFrontierPass(void) { switch (sPassData->state) { case 0: if (sPassData->areaToShow != CURSOR_AREA_MAP && sPassData->areaToShow != CURSOR_AREA_CARD) { BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 16, RGB_BLACK); } else { sPassData->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: ResetGpuRegsAndBgs(); ResetTasks(); ResetSpriteData(); FreeAllSpritePalettes(); break; case 5: UnsetBgTilemapBuffer(0); UnsetBgTilemapBuffer(1); UnsetBgTilemapBuffer(2); FreeFrontierPassGfx(); sPassData->state = 0; return TRUE; } sPassData->state++; return FALSE; } static u8 GetCursorAreaFromCoords(s16 x, s16 y) { u8 i; // Minus/Plus 1, because the table doesn't take CURSOR_AREA_NOTHING into account. for (i = 0; i < CURSOR_AREA_COUNT - 1; i++) { if (sPassAreasLayout[i].yStart <= y && sPassAreasLayout[i].yEnd >= y && sPassAreasLayout[i].xStart <= x && sPassAreasLayout[i].xEnd >= x) { if (i >= CURSOR_AREA_SYMBOL - 1 && sPassData->facilitySymbols[i - CURSOR_AREA_SYMBOL + 1] == 0) break; return i + 1; } } return CURSOR_AREA_NOTHING; } // For Task_PassAreaZoom #define tZoomOut data[0] void CB2_ReshowFrontierPass(void) { u8 taskId; if (!InitFrontierPass()) return; switch (sPassData->areaToShow) { case CURSOR_AREA_MAP: case CURSOR_AREA_CARD: taskId = CreateTask(Task_PassAreaZoom, 0); gTasks[taskId].tZoomOut = TRUE; break; case CURSOR_AREA_RECORD: default: sPassData->areaToShow = CURSOR_AREA_NOTHING; taskId = CreateTask(Task_HandleFrontierPassInput, 0); break; } SetMainCallback2(CB2_FrontierPass); } static void CB2_ReturnFromRecord(void) { AllocateFrontierPassData(sSavedPassData.callback); sPassData->cursorX = sSavedPassData.cursorX; sPassData->cursorY = sSavedPassData.cursorY; memset(&sSavedPassData, 0, sizeof(sSavedPassData)); switch (InBattlePyramid()) { case 1: PlayBGM(MUS_B_PYRAMID); break; case 2: PlayBGM(MUS_B_PYRAMID_TOP); break; default: Overworld_PlaySpecialMapMusic(); break; } SetMainCallback2(CB2_ReshowFrontierPass); } static void CB2_ShowFrontierPassFeature(void) { if (!HideFrontierPass()) return; switch (sPassData->areaToShow) { case CURSOR_AREA_MAP: ShowFrontierMap(CB2_ReshowFrontierPass); break; case CURSOR_AREA_RECORD: sSavedPassData.callback = sPassData->callback; sSavedPassData.cursorX = sPassData->cursorX; sSavedPassData.cursorY = sPassData->cursorY; FreeFrontierPassData(); PlayRecordedBattle(CB2_ReturnFromRecord); break; case CURSOR_AREA_CARD: ShowPlayerTrainerCard(CB2_ReshowFrontierPass); break; } } static bool32 TryCallPassAreaFunction(u8 taskId, u8 cursorArea) { switch (cursorArea) { case CURSOR_AREA_RECORD: if (!sPassData->hasBattleRecord) return FALSE; sPassData->areaToShow = CURSOR_AREA_RECORD; DestroyTask(taskId); SetMainCallback2(CB2_ShowFrontierPassFeature); break; case CURSOR_AREA_MAP: case CURSOR_AREA_CARD: sPassData->areaToShow = cursorArea; gTasks[taskId].func = Task_PassAreaZoom; gTasks[taskId].tZoomOut = FALSE; break; default: return FALSE; } sPassData->cursorX = sPassGfx->cursorSprite->x; sPassData->cursorY = sPassGfx->cursorSprite->y; return TRUE; } static void Task_HandleFrontierPassInput(u8 taskId) { u8 var = FALSE; // Reused, first informs whether the cursor moves, then used as the new cursor area. if (JOY_HELD(DPAD_UP) && sPassGfx->cursorSprite->y >= 9) { sPassGfx->cursorSprite->y -= 2; if (sPassGfx->cursorSprite->y <= 7) sPassGfx->cursorSprite->y = 2; var = TRUE; } if (JOY_HELD(DPAD_DOWN) && sPassGfx->cursorSprite->y <= 135) { sPassGfx->cursorSprite->y += 2; if (sPassGfx->cursorSprite->y >= 137) sPassGfx->cursorSprite->y = 136; var = TRUE; } if (JOY_HELD(DPAD_LEFT) && sPassGfx->cursorSprite->x >= 6) { sPassGfx->cursorSprite->x -= 2; if (sPassGfx->cursorSprite->x <= 4) sPassGfx->cursorSprite->x = 5; var = TRUE; } if (JOY_HELD(DPAD_RIGHT) && sPassGfx->cursorSprite->x <= 231) { sPassGfx->cursorSprite->x += 2; if (sPassGfx->cursorSprite->x >= 233) sPassGfx->cursorSprite->x = 232; var = TRUE; } if (!var) // Cursor did not change. { if (sPassData->cursorArea != CURSOR_AREA_NOTHING && JOY_NEW(A_BUTTON)) { if (sPassData->cursorArea <= CURSOR_AREA_RECORD) // Map, Card, Record { PlaySE(SE_SELECT); if (TryCallPassAreaFunction(taskId, sPassData->cursorArea)) return; } else if (sPassData->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. #ifdef BUGFIX return; #endif } } if (JOY_NEW(B_BUTTON)) { PlaySE(SE_PC_OFF); SetMainCallback2(CB2_HideFrontierPass); DestroyTask(taskId); } } else { var = GetCursorAreaFromCoords(sPassGfx->cursorSprite->x - 5, sPassGfx->cursorSprite->y + 5); if (sPassData->cursorArea != var) { PrintAreaDescription(var); sPassData->previousCursorArea = sPassData->cursorArea; sPassData->cursorArea = var; UpdateAreaHighlight(sPassData->cursorArea, sPassData->previousCursorArea); } } } #define tScaleX data[1] #define tScaleY data[2] #define tScaleSpeedX data[3] #define tScaleSpeedY data[4] // Zoom in/out for the Frontier map or the trainer card static void Task_PassAreaZoom(u8 taskId) { s16 *data = gTasks[taskId].data; switch (sPassData->state) { case 0: // Initialize the zoom, start fading in/out if (!tZoomOut) { // Zooming in to map/card screen ShowHideZoomingArea(TRUE, FALSE); tScaleX = Q_8_8(1); tScaleY = Q_8_8(1); tScaleSpeedX = 0x15; tScaleSpeedY = 0x15; BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 16, RGB_WHITE); } else { // Zooming out of map/card screen back to frontier pass tScaleX = Q_8_8(1.984375); // 1 and 63/64 tScaleY = Q_8_8(1.984375); tScaleSpeedX = -0x15; tScaleSpeedY = -0x15; SetGpuReg(REG_OFFSET_DISPCNT, DISPCNT_OBJ_ON | DISPCNT_OBJ_1D_MAP); ShowBg(0); ShowBg(1); ShowBg(2); LoadCursorAndSymbolSprites(); SetVBlankCallback(VBlankCB_FrontierPass); BlendPalettes(PALETTES_ALL, 16, RGB_WHITE); BeginNormalPaletteFade(PALETTES_ALL, 0, 16, 0, RGB_WHITE); } sPassGfx->zooming = TRUE; sPassGfx->scaleX = MathUtil_Inv16(tScaleX); sPassGfx->scaleY = MathUtil_Inv16(tScaleY); break; case 1: // Update the fade and zoom UpdatePaletteFade(); tScaleX += tScaleSpeedX; tScaleY += tScaleSpeedY; sPassGfx->scaleX = MathUtil_Inv16(tScaleX); sPassGfx->scaleY = MathUtil_Inv16(tScaleY); // Check if zoom hasn't reached target if (!tZoomOut) { if (tScaleX <= Q_8_8(1.984375)) return; } else { if (tScaleX != Q_8_8(1)) return; } break; case 2: if (sPassGfx->zooming) sPassGfx->zooming = FALSE; if (UpdatePaletteFade()) return; if (!tZoomOut) { // Zoomed in and faded out, switch to map or trainer card DestroyTask(taskId); SetMainCallback2(CB2_ShowFrontierPassFeature); } else { // Zoomed out and faded in, return to frontier pass ShowHideZoomingArea(FALSE, FALSE); sPassData->areaToShow = CURSOR_AREA_NOTHING; gTasks[taskId].func = Task_HandleFrontierPassInput; } SetBgAttribute(2, BG_ATTR_WRAPAROUND, 0); sPassData->state = 0; return; } sPassData->state++; } static void ShowAndPrintWindows(void) { s32 x; u8 i; for (i = 0; i < WINDOW_COUNT; i++) { PutWindowTilemap(i); FillWindowPixelBuffer(i, PIXEL_FILL(0)); } x = GetStringCenterAlignXOffset(FONT_NORMAL, gText_SymbolsEarned, 96); AddTextPrinterParameterized3(WINDOW_EARNED_SYMBOLS, FONT_NORMAL, x, 5, sTextColors[0], 0, gText_SymbolsEarned); x = GetStringCenterAlignXOffset(FONT_NORMAL, gText_BattleRecord, 96); AddTextPrinterParameterized3(WINDOW_BATTLE_RECORD, FONT_NORMAL, x, 5, sTextColors[0], 0, gText_BattleRecord); AddTextPrinterParameterized3(WINDOW_BATTLE_POINTS, FONT_SMALL_NARROW, 5, 4, sTextColors[0], 0, gText_BattlePoints); ConvertIntToDecimalStringN(gStringVar4, sPassData->battlePoints, STR_CONV_MODE_LEFT_ALIGN, 5); x = GetStringRightAlignXOffset(FONT_SMALL_NARROW, gStringVar4, 91); AddTextPrinterParameterized3(WINDOW_BATTLE_POINTS, FONT_SMALL_NARROW, x, 16, sTextColors[0], 0, gStringVar4); sPassData->cursorArea = GetCursorAreaFromCoords(sPassData->cursorX - 5, sPassData->cursorY + 5); sPassData->previousCursorArea = CURSOR_AREA_NOTHING; PrintAreaDescription(sPassData->cursorArea); for (i = 0; i < WINDOW_COUNT; i++) CopyWindowToVram(i, COPYWIN_FULL); CopyBgTilemapBufferToVram(0); } static void PrintAreaDescription(u8 cursorArea) { FillWindowPixelBuffer(WINDOW_DESCRIPTION, PIXEL_FILL(0)); if (cursorArea == CURSOR_AREA_RECORD && !sPassData->hasBattleRecord) AddTextPrinterParameterized3(WINDOW_DESCRIPTION, FONT_NORMAL, 2, 0, sTextColors[1], 0, sPassAreaDescriptions[CURSOR_AREA_NOTHING]); else if (cursorArea != CURSOR_AREA_NOTHING) AddTextPrinterParameterized3(WINDOW_DESCRIPTION, FONT_NORMAL, 2, 0, sTextColors[1], 0, sPassAreaDescriptions[cursorArea]); CopyWindowToVram(WINDOW_DESCRIPTION, COPYWIN_FULL); CopyBgTilemapBufferToVram(0); } static void ShowHideZoomingArea(bool8 show, bool8 zoomedIn) { switch (sPassData->areaToShow) { case CURSOR_AREA_MAP: if (show) CopyToBgTilemapBufferRect_ChangePalette(2, sPassGfx->mapAndCardZoomTilemap, 16, 3, 12, 7, 16); else FillBgTilemapBufferRect(2, 0, 16, 3, 12, 7, 16); break; case CURSOR_AREA_CARD: if (show) CopyToBgTilemapBufferRect_ChangePalette(2, sPassGfx->mapAndCardZoomTilemap + 84, 16, 10, 12, 7, 16); else FillBgTilemapBufferRect(2, 0, 16, 10, 12, 7, 16); break; default: return; } CopyBgTilemapBufferToVram(2); if (zoomedIn) { SetBgAffine(2, sBgAffineCoords[sPassData->areaToShow - 1][0] << 8, sBgAffineCoords[sPassData->areaToShow - 1][1] << 8, sBgAffineCoords[sPassData->areaToShow - 1][0], sBgAffineCoords[sPassData->areaToShow - 1][1], MathUtil_Inv16(Q_8_8(1.984375)), // 1 and 63/64 MathUtil_Inv16(Q_8_8(1.984375)), 0); } else { SetBgAffine(2, sBgAffineCoords[sPassData->areaToShow - 1][0] << 8, sBgAffineCoords[sPassData->areaToShow - 1][1] << 8, sBgAffineCoords[sPassData->areaToShow - 1][0], sBgAffineCoords[sPassData->areaToShow - 1][1], MathUtil_Inv16(Q_8_8(1)), MathUtil_Inv16(Q_8_8(1)), 0); } } static void UpdateAreaHighlight(u8 cursorArea, u8 previousCursorArea) { #define NON_HIGHLIGHT_AREA(area)((area) == CURSOR_AREA_NOTHING || (area) > CURSOR_AREA_CANCEL) // If moving off highlightable area, unhighlight it switch (previousCursorArea) { case CURSOR_AREA_MAP: CopyToBgTilemapBufferRect_ChangePalette(1, sPassGfx->mapAndCardTilemap, 16, 3, 12, 7, 17); break; case CURSOR_AREA_CARD: CopyToBgTilemapBufferRect_ChangePalette(1, sPassGfx->mapAndCardTilemap + 336, 16, 10, 12, 7, 17); break; case CURSOR_AREA_RECORD: if (sPassData->hasBattleRecord) CopyToBgTilemapBufferRect_ChangePalette(1, sPassGfx->battleRecordTilemap, 2, 10, 12, 3, 17); else if (NON_HIGHLIGHT_AREA(cursorArea)) return; break; case CURSOR_AREA_CANCEL: CopyToBgTilemapBufferRect_ChangePalette(1, gFrontierPassCancelButton_Tilemap, 21, 0, 9, 2, 17); break; default: if (NON_HIGHLIGHT_AREA(cursorArea)) return; break; } // If moving on to highlightable area, highlight it switch (cursorArea) { case CURSOR_AREA_MAP: CopyToBgTilemapBufferRect_ChangePalette(1, sPassGfx->mapAndCardTilemap + 168, 16, 3, 12, 7, 17); break; case CURSOR_AREA_CARD: CopyToBgTilemapBufferRect_ChangePalette(1, sPassGfx->mapAndCardTilemap + 504, 16, 10, 12, 7, 17); break; case CURSOR_AREA_RECORD: if (sPassData->hasBattleRecord) CopyToBgTilemapBufferRect_ChangePalette(1, sPassGfx->battleRecordTilemap + 72, 2, 10, 12, 3, 17); else return; break; case CURSOR_AREA_CANCEL: CopyToBgTilemapBufferRect_ChangePalette(1, gFrontierPassCancelButtonHighlighted_Tilemap, 21, 0, 9, 2, 17); break; default: if (NON_HIGHLIGHT_AREA(previousCursorArea)) return; } CopyBgTilemapBufferToVram(1); } static void DrawFrontierPassBg(void) { CopyToBgTilemapBuffer(1, gFrontierPassBg_Tilemap, 0, 0); UpdateAreaHighlight(sPassData->cursorArea, sPassData->previousCursorArea); ShowHideZoomingArea(TRUE, sPassData->areaToShow); // If returning to frontier pass from map/card (areaToShow will be != 0) ShowAndPrintWindows(); CopyBgTilemapBufferToVram(1); } static void LoadCursorAndSymbolSprites(void) { u8 spriteId; u8 i = 0; FreeAllSpritePalettes(); ResetAffineAnimData(); LoadSpritePalettes(sSpritePalettes); LoadCompressedSpriteSheet(&sCursorSpriteSheets[0]); LoadCompressedSpriteSheet(&sCursorSpriteSheets[2]); spriteId = CreateSprite(&sSpriteTemplates_Cursors[0], sPassData->cursorX, sPassData->cursorY, 0); sPassGfx->cursorSprite = &gSprites[spriteId]; sPassGfx->cursorSprite->oam.priority = 0; for (i = 0; i < NUM_FRONTIER_FACILITIES; i++) { if (sPassData->facilitySymbols[i] != 0) { struct SpriteTemplate sprite = sSpriteTemplate_Medal; sprite.paletteTag += sPassData->facilitySymbols[i] - 1; // Adds 1 if gold for TAG_MEDAL_GOLD spriteId = CreateSprite(&sprite, sPassAreasLayout[i + CURSOR_AREA_SYMBOL - 1].xStart + 8, sPassAreasLayout[i + CURSOR_AREA_SYMBOL - 1].yStart + 6, i + 1); sPassGfx->symbolSprites[i] = &gSprites[spriteId]; sPassGfx->symbolSprites[i]->oam.priority = 2; StartSpriteAnim(sPassGfx->symbolSprites[i], i); } } } static void FreeCursorAndSymbolSprites(void) { u8 i = 0; DestroySprite(sPassGfx->cursorSprite); sPassGfx->cursorSprite = NULL; for (i = 0; i < NUM_FRONTIER_FACILITIES; i++) { if (sPassGfx->symbolSprites[i] != NULL) { DestroySprite(sPassGfx->symbolSprites[i]); sPassGfx->symbolSprites[i] = NULL; } } FreeAllSpritePalettes(); FreeSpriteTilesByTag(TAG_MEDAL_SILVER); FreeSpriteTilesByTag(TAG_CURSOR); } static void SpriteCB_PlayerHead(struct Sprite *sprite) { } // Frontier Map code. // Forward declarations. static void Task_HandleFrontierMap(u8 taskId); static void PrintOnFrontierMap(void); static void InitFrontierMapSprites(void); static void HandleFrontierMapCursorMove(u8 direction); static void ShowFrontierMap(void (*callback)(void)) { if (sMapData != NULL) SetMainCallback2(callback); // This line doesn't make sense at all, since it gets overwritten later anyway. sMapData = AllocZeroed(sizeof(*sMapData)); sMapData->callback = callback; ResetTasks(); CreateTask(Task_HandleFrontierMap, 0); SetMainCallback2(CB2_FrontierPass); } static void FreeFrontierMap(void) { ResetTasks(); SetMainCallback2(sMapData->callback); memset(sMapData, 0, sizeof(*sMapData)); // Pointless memory clear. FREE_AND_SET_NULL(sMapData); } static bool32 InitFrontierMap(void) { switch (sPassData->state) { case 0: SetVBlankCallback(NULL); ScanlineEffect_Stop(); SetVBlankHBlankCallbacksToNull(); break; case 1: ResetGpuRegsAndBgs(); break; case 2: ResetSpriteData(); FreeAllSpritePalettes(); ResetPaletteFade(); ResetTempTileDataBuffers(); break; case 3: ResetBgsAndClearDma3BusyFlags(0); InitBgsFromTemplates(0, sMapBgTemplates, ARRAY_COUNT(sMapBgTemplates)); SetBgTilemapBuffer(0, sMapData->tilemapBuff0); SetBgTilemapBuffer(1, sMapData->tilemapBuff1); SetBgTilemapBuffer(2, sMapData->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(sMapWindowTemplates); DeactivateAllTextPrinters(); PrintOnFrontierMap(); DecompressAndCopyTileDataToVram(1, sMapScreen_Gfx, 0, 0, 0); break; case 5: if (FreeTempTileDataBuffersIfPossible()) return FALSE; LoadPalette(gFrontierPassBg_Pal[0], 0, 0x1A0); LoadPalette(GetTextWindowPalette(0), 0xF0, 0x20); CopyToBgTilemapBuffer(2, sMapScreen_Tilemap, 0, 0); CopyBgTilemapBufferToVram(2); break; case 6: SetGpuReg(REG_OFFSET_DISPCNT, DISPCNT_OBJ_ON | DISPCNT_OBJ_1D_MAP); ShowBg(0); ShowBg(1); ShowBg(2); InitFrontierMapSprites(); SetVBlankCallback(VBlankCB_FrontierPass); BlendPalettes(PALETTES_ALL, 16, RGB_WHITE); BeginNormalPaletteFade(PALETTES_ALL, 0, 16, 0, RGB_WHITE); break; case 7: if (UpdatePaletteFade()) return FALSE; sPassData->state = 0; return TRUE; } sPassData->state++; return FALSE; } static bool32 ExitFrontierMap(void) { switch (sPassData->state) { case 0: BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 16, 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 (sMapData->cursorSprite != NULL) { DestroySprite(sMapData->cursorSprite); FreeSpriteTilesByTag(TAG_CURSOR); } if (sMapData->mapIndicatorSprite != NULL) { DestroySprite(sMapData->mapIndicatorSprite); FreeSpriteTilesByTag(TAG_MAP_INDICATOR); } if (sMapData->playerHeadSprite != NULL) { DestroySprite(sMapData->playerHeadSprite); FreeSpriteTilesByTag(TAG_HEAD_MALE); } FreeAllWindowBuffers(); break; case 4: ResetGpuRegsAndBgs(); ResetSpriteData(); FreeAllSpritePalettes(); break; case 5: UnsetBgTilemapBuffer(0); UnsetBgTilemapBuffer(1); UnsetBgTilemapBuffer(2); sPassData->state = 0; return TRUE; } sPassData->state++; return FALSE; } #define tState data[0] #define tMoveSteps data[1] static void Task_HandleFrontierMap(u8 taskId) { s16 *data = gTasks[taskId].data; switch (tState) { case 0: if (InitFrontierMap()) break; return; case 1: if (JOY_NEW(B_BUTTON)) { PlaySE(SE_PC_OFF); tState = 4; } else if (JOY_NEW(DPAD_DOWN)) { if (sMapData->cursorPos >= NUM_FRONTIER_FACILITIES - 1) HandleFrontierMapCursorMove(0); else tState = 2; } else if (JOY_NEW(DPAD_UP)) { if (sMapData->cursorPos == 0) HandleFrontierMapCursorMove(1); else tState = 3; } return; case 2: if (tMoveSteps > 3) { HandleFrontierMapCursorMove(0); tMoveSteps = 0; tState = 1; } else { sMapData->cursorSprite->y += 4; tMoveSteps++; } return; case 3: if (tMoveSteps > 3) { HandleFrontierMapCursorMove(1); tMoveSteps = 0; tState = 1; } else { sMapData->cursorSprite->y -= 4; tMoveSteps++; } return; case 4: if (ExitFrontierMap()) break; return; case 5: DestroyTask(taskId); FreeFrontierMap(); return; } tState++; } static u8 MapNumToFrontierFacilityId(u16 mapNum) // id + 1, zero means not a frontier map number { // In Battle Tower 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_PARTNER_ROOM) && mapNum <= MAP_NUM(BATTLE_FRONTIER_BATTLE_TOWER_MULTI_BATTLE_ROOM))) return FRONTIER_FACILITY_TOWER + 1; // In Battle Dome 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; // In Battle Palace 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; // In Battle Arena 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; // In Battle Factory 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; // In Battle Pike 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_ROOM_NORMAL) || mapNum == MAP_NUM(BATTLE_FRONTIER_BATTLE_PIKE_ROOM_FINAL) || mapNum == MAP_NUM(BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS)) return FRONTIER_FACILITY_PIKE + 1; // In Battle Pyramid else if (mapNum == MAP_NUM(BATTLE_FRONTIER_BATTLE_PYRAMID_LOBBY) || mapNum == MAP_NUM(BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR) || mapNum == MAP_NUM(BATTLE_FRONTIER_BATTLE_PYRAMID_TOP)) return FRONTIER_FACILITY_PYRAMID + 1; else return 0; } static void InitFrontierMapSprites(void) { struct SpriteTemplate sprite; u8 spriteId; u8 id; s16 x = 0, y; FreeAllSpritePalettes(); LoadSpritePalettes(sSpritePalettes); LoadCompressedSpriteSheet(&sCursorSpriteSheets[0]); spriteId = CreateSprite(&sSpriteTemplates_Cursors[0], 155, (sMapData->cursorPos * 16) + 8, 2); sMapData->cursorSprite = &gSprites[spriteId]; sMapData->cursorSprite->oam.priority = 0; sMapData->cursorSprite->hFlip = TRUE; StartSpriteAnim(sMapData->cursorSprite, 1); LoadCompressedSpriteSheet(&sCursorSpriteSheets[1]); spriteId = CreateSprite(&sSpriteTemplates_Cursors[1], sMapLandmarks[sMapData->cursorPos].x, sMapLandmarks[sMapData->cursorPos].y, 1); sMapData->mapIndicatorSprite = &gSprites[spriteId]; sMapData->mapIndicatorSprite->oam.priority = 0; StartSpriteAnim(sMapData->mapIndicatorSprite, sMapLandmarks[sMapData->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 = MapNumToFrontierFacilityId(mapNum); if (id != 0) { x = sMapLandmarks[id - 1].x; y = sMapLandmarks[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(sHeadsSpriteSheet); sprite = sSpriteTemplate_PlayerHead; sprite.paletteTag = gSaveBlock2Ptr->playerGender + TAG_HEAD_MALE; // TAG_HEAD_FEMALE if gender is FEMALE if (id != 0) { spriteId = CreateSprite(&sprite, x, y, 0); } else { x *= 8; y *= 8; spriteId = CreateSprite(&sprite, x + 20, y + 36, 0); } sMapData->playerHeadSprite = &gSprites[spriteId]; sMapData->playerHeadSprite->oam.priority = 0; if (gSaveBlock2Ptr->playerGender != MALE) StartSpriteAnim(sMapData->playerHeadSprite, 1); } } static void PrintOnFrontierMap(void) { u8 i; for (i = 0; i < MAP_WINDOW_COUNT; i++) { PutWindowTilemap(i); FillWindowPixelBuffer(i, PIXEL_FILL(0)); } for (i = 0; i < NUM_FRONTIER_FACILITIES; i++) { if (i == sMapData->cursorPos) AddTextPrinterParameterized3(MAP_WINDOW_NAME, FONT_NARROW, 4, (i * 16) + 1, sTextColors[2], 0, sMapLandmarks[i].name); else AddTextPrinterParameterized3(MAP_WINDOW_NAME, FONT_NARROW, 4, (i * 16) + 1, sTextColors[1], 0, sMapLandmarks[i].name); } AddTextPrinterParameterized3(MAP_WINDOW_DESCRIPTION, FONT_NORMAL, 4, 0, sTextColors[0], 0, sMapLandmarks[sMapData->cursorPos].description); for (i = 0; i < MAP_WINDOW_COUNT; i++) CopyWindowToVram(i, COPYWIN_FULL); CopyBgTilemapBufferToVram(0); } static void HandleFrontierMapCursorMove(u8 direction) { u8 oldCursorPos, i; if (direction) { oldCursorPos = sMapData->cursorPos; sMapData->cursorPos = (oldCursorPos + 6) % NUM_FRONTIER_FACILITIES; } else { oldCursorPos = sMapData->cursorPos; sMapData->cursorPos = (oldCursorPos + 1) % NUM_FRONTIER_FACILITIES; } AddTextPrinterParameterized3(MAP_WINDOW_NAME, FONT_NARROW, 4, (oldCursorPos * 16) + 1, sTextColors[1], 0, sMapLandmarks[oldCursorPos].name); AddTextPrinterParameterized3(MAP_WINDOW_NAME, FONT_NARROW, 4, (sMapData->cursorPos * 16) + 1, sTextColors[2], 0, sMapLandmarks[sMapData->cursorPos].name); sMapData->cursorSprite->y = (sMapData->cursorPos * 16) + 8; StartSpriteAnim(sMapData->mapIndicatorSprite, sMapLandmarks[sMapData->cursorPos].animNum); sMapData->mapIndicatorSprite->x = sMapLandmarks[sMapData->cursorPos].x; sMapData->mapIndicatorSprite->y = sMapLandmarks[sMapData->cursorPos].y; FillWindowPixelBuffer(MAP_WINDOW_DESCRIPTION, PIXEL_FILL(0)); AddTextPrinterParameterized3(MAP_WINDOW_DESCRIPTION, FONT_NORMAL, 4, 0, sTextColors[0], 0, sMapLandmarks[sMapData->cursorPos].description); for (i = 0; i < MAP_WINDOW_COUNT; i++) CopyWindowToVram(i, COPYWIN_FULL); CopyBgTilemapBufferToVram(0); PlaySE(SE_DEX_SCROLL); }