#include "global.h" #include "malloc.h" #include "battle.h" #include "battle_controllers.h" #include "battle_message.h" #include "bg.h" #include "decompress.h" #include "event_data.h" #include "field_screen_effect.h" #include "gpu_regs.h" #include "graphics.h" #include "international_string_util.h" #include "item.h" #include "item_menu.h" #include "lilycove_lady.h" #include "list_menu.h" #include "main.h" #include "menu.h" #include "menu_helpers.h" #include "overworld.h" #include "palette.h" #include "pokeblock.h" #include "pokemon.h" #include "safari_zone.h" #include "scanline_effect.h" #include "sound.h" #include "string_util.h" #include "strings.h" #include "task.h" #include "text.h" #include "text_window.h" #include "constants/items.h" #include "constants/songs.h" #include "constants/rgb.h" #define MAX_MENU_ITEMS 9 #define MENU_MIDPOINT (MAX_MENU_ITEMS / 2) #define TILE_HIGHLIGHT_NONE 0x0005 // Tile number for the bg of an unselected menu item #define TILE_HIGHLIGHT_BLUE 0x1005 // Tile number for the bg of a selected menu item #define TILE_HIGHLIGHT_RED 0x2005 // Tile number for the bg of a menu item to swap #define TAG_POKEBLOCK_CASE 14800 #define TAG_SCROLL_ARROW 1110 #define POKEBLOCK_MAX_FEEL 99 enum { WIN_TITLE, WIN_LIST, WIN_SPICY, WIN_DRY, WIN_SWEET, WIN_BITTER, WIN_SOUR, WIN_FEEL, WIN_ACTIONS_TALL, WIN_ACTIONS, WIN_TOSS_MSG, }; struct PokeblockMenuStruct { u8 tilemap[BG_SCREEN_SIZE]; void (*callbackOnUse)(void); const u8 *pokeblockActionIds; u8 numActions; u8 caseId; u8 itemsNo; u8 maxShowed; struct ListMenuItem items[POKEBLOCKS_COUNT + 1]; u8 menuItemsStrings[POKEBLOCKS_COUNT + 1][32]; // + 1 because of STOW CASE item u8 pokeblockCaseSpriteId; u8 swapLineSpriteIds[7]; u8 arrowTaskId; bool8 isSwapping; s16 gfxState; u8 unused[8]; }; struct PokeblockSavedData { void (*callback)(void); u16 selectedRow; u16 scrollOffset; }; enum { PKBL_USE_ON_FIELD, PKBL_TOSS, PKBL_CANCEL, PKBL_USE_IN_BATTLE, PKBL_USE_ON_FEEDER, PKBL_GIVE_TO_LADY }; static void CB2_InitPokeblockMenu(void); static bool8 InitPokeblockMenu(void); static bool8 LoadPokeblockMenuGfx(void); static void HandleInitBackgrounds(void); static void HandleInitWindows(void); static void SetMenuItemsCountAndMaxShowed(void); static void LimitMenuScrollAndRow(void); static void SetInitialScroll(void); static void UpdatePokeblockList(void); static void CreateScrollArrows(void); static void MovePokeblockMenuCursor(s32, bool8, struct ListMenu *); static void DrawPokeblockMenuTitleText(void); static void DrawPokeblockMenuHighlight(u16, u16); static void PutPokeblockListMenuString(u8 *, u16); static void Task_HandlePokeblockMenuInput(u8); static void PokeblockAction_UseOnField(u8); static void PokeblockAction_Toss(u8); static void PokeblockAction_Cancel(u8); static void PokeblockAction_UseInBattle(u8); static void PokeblockAction_UseOnPokeblockFeeder(u8); static void PokeblockAction_GiveToContestLady(u8); static void TossedPokeblockMessage(u8); static void CloseTossPokeblockWindow(u8); static void Task_FreeDataAndExitPokeblockCase(u8); static void Task_HandlePokeblockActionsInput(u8); static void ShowPokeblockActionsWindow(u8); static void Task_HandlePokeblocksSwapInput(u8); static void SpriteCB_ShakePokeblockCase(struct Sprite *); static void DrawPokeblockInfo(s32); static void UpdatePokeblockSwapMenu(u8, bool8); static void UsePokeblockOnField(void); static void ReturnToPokeblockCaseOnField(void); static void CreateTossPokeblockYesNoMenu(u8); static void TossPokeblock(u8); EWRAM_DATA static struct PokeblockSavedData sSavedPokeblockData = {0}; EWRAM_DATA static struct PokeblockMenuStruct *sPokeblockMenu = NULL; const s8 gPokeblockFlavorCompatibilityTable[NUM_NATURES * FLAVOR_COUNT] = { // Spicy, Dry, Sweet, Bitter, Sour 0, 0, 0, 0, 0, // Hardy 1, 0, 0, 0, -1, // Lonely 1, 0, -1, 0, 0, // Brave 1, -1, 0, 0, 0, // Adamant 1, 0, 0, -1, 0, // Naughty -1, 0, 0, 0, 1, // Bold 0, 0, 0, 0, 0, // Docile 0, 0, -1, 0, 1, // Relaxed 0, -1, 0, 0, 1, // Impish 0, 0, 0, -1, 1, // Lax -1, 0, 1, 0, 0, // Timid 0, 0, 1, 0, -1, // Hasty 0, 0, 0, 0, 0, // Serious 0, -1, 1, 0, 0, // Jolly 0, 0, 1, -1, 0, // Naive -1, 1, 0, 0, 0, // Modest 0, 1, 0, 0, -1, // Mild 0, 1, -1, 0, 0, // Quiet 0, 0, 0, 0, 0, // Bashful 0, 1, 0, -1, 0, // Rash -1, 0, 0, 1, 0, // Calm 0, 0, 0, 1, -1, // Gentle 0, 0, -1, 1, 0, // Sassy 0, -1, 0, 1, 0, // Careful 0, 0, 0, 0, 0 // Quirky }; static const struct BgTemplate sBgTemplatesForPokeblockMenu[] = { { .bg = 0, .charBaseIndex = 0, .mapBaseIndex = 31, .screenSize = 0, .paletteMode = 0, .priority = 1, .baseTile = 0 }, { .bg = 1, .charBaseIndex = 0, .mapBaseIndex = 30, .screenSize = 0, .paletteMode = 0, .priority = 0, .baseTile = 0 }, { .bg = 2, .charBaseIndex = 3, .mapBaseIndex = 29, .screenSize = 0, .paletteMode = 0, .priority = 2, .baseTile = 0 } }; const u8 *const gPokeblockNames[] = { [PBLOCK_CLR_NONE] = NULL, [PBLOCK_CLR_RED] = gText_RedPokeblock, [PBLOCK_CLR_BLUE] = gText_BluePokeblock, [PBLOCK_CLR_PINK] = gText_PinkPokeblock, [PBLOCK_CLR_GREEN] = gText_GreenPokeblock, [PBLOCK_CLR_YELLOW] = gText_YellowPokeblock, [PBLOCK_CLR_PURPLE] = gText_PurplePokeblock, [PBLOCK_CLR_INDIGO] = gText_IndigoPokeblock, [PBLOCK_CLR_BROWN] = gText_BrownPokeblock, [PBLOCK_CLR_LITE_BLUE] = gText_LiteBluePokeblock, [PBLOCK_CLR_OLIVE] = gText_OlivePokeblock, [PBLOCK_CLR_GRAY] = gText_GrayPokeblock, [PBLOCK_CLR_BLACK] = gText_BlackPokeblock, [PBLOCK_CLR_WHITE] = gText_WhitePokeblock, [PBLOCK_CLR_GOLD] = gText_GoldPokeblock }; static const struct MenuAction sPokeblockMenuActions[] = { [PKBL_USE_ON_FIELD] = {gMenuText_Use, PokeblockAction_UseOnField}, [PKBL_TOSS] = {gMenuText_Toss, PokeblockAction_Toss}, [PKBL_CANCEL] = {gText_Cancel2, PokeblockAction_Cancel}, [PKBL_USE_IN_BATTLE] = {gMenuText_Use, PokeblockAction_UseInBattle}, [PKBL_USE_ON_FEEDER] = {gMenuText_Use, PokeblockAction_UseOnPokeblockFeeder}, [PKBL_GIVE_TO_LADY] = {gMenuText_Give2, PokeblockAction_GiveToContestLady}, }; static const u8 sActionsOnField[] = {PKBL_USE_ON_FIELD, PKBL_TOSS, PKBL_CANCEL}; static const u8 sActionsInBattle[] = {PKBL_USE_IN_BATTLE, PKBL_CANCEL}; static const u8 sActionsOnPokeblockFeeder[] = {PKBL_USE_ON_FEEDER, PKBL_CANCEL}; static const u8 sActionsWhenGivingToLady[] = {PKBL_GIVE_TO_LADY, PKBL_CANCEL}; static const struct YesNoFuncTable sTossYesNoFuncTable = {TossedPokeblockMessage, CloseTossPokeblockWindow}; static const u8 sContestStatsMonData[] = {MON_DATA_COOL, MON_DATA_BEAUTY, MON_DATA_CUTE, MON_DATA_SMART, MON_DATA_TOUGH}; static const struct OamData sOamData_PokeblockCase = { .y = 0, .affineMode = ST_OAM_AFFINE_OFF, .objMode = ST_OAM_OBJ_NORMAL, .mosaic = 0, .bpp = ST_OAM_4BPP, .shape = SPRITE_SHAPE(64x64), .x = 0, .matrixNum = 0, .size = SPRITE_SIZE(64x64), .tileNum = 0, .priority = 2, .paletteNum = 0, .affineParam = 0, }; static const union AnimCmd sSpriteAnim_PokeblockCase[] = { ANIMCMD_FRAME(0, 0), ANIMCMD_END }; static const union AnimCmd *const sSpriteAnimTable_PokeblockCase[] = { sSpriteAnim_PokeblockCase }; static const union AffineAnimCmd sAffineAnim_PokeblockCaseShake[] = { AFFINEANIMCMD_FRAME(0, 0, -2, 2), AFFINEANIMCMD_FRAME(0, 0, 2, 4), AFFINEANIMCMD_FRAME(0, 0, -2, 4), AFFINEANIMCMD_FRAME(0, 0, 2, 2), AFFINEANIMCMD_END }; static const union AffineAnimCmd *const sAffineAnims_PokeblockCaseShake[] = { sAffineAnim_PokeblockCaseShake }; const struct CompressedSpriteSheet gPokeblockCase_SpriteSheet = { gMenuPokeblockDevice_Gfx, 0x800, TAG_POKEBLOCK_CASE }; const struct CompressedSpritePalette gPokeblockCase_SpritePal = { gMenuPokeblockDevice_Pal, TAG_POKEBLOCK_CASE }; static const struct SpriteTemplate sSpriteTemplate_PokeblockCase = { .tileTag = TAG_POKEBLOCK_CASE, .paletteTag = TAG_POKEBLOCK_CASE, .oam = &sOamData_PokeblockCase, .anims = sSpriteAnimTable_PokeblockCase, .images = NULL, .affineAnims = gDummySpriteAffineAnimTable, .callback = SpriteCallbackDummy }; static const u8 sTextColor[3] = {TEXT_COLOR_TRANSPARENT, TEXT_COLOR_DARK_GRAY, TEXT_COLOR_LIGHT_GRAY}; static const struct Pokeblock sFavoritePokeblocksTable[FLAVOR_COUNT] = { [FLAVOR_SPICY] = { PBLOCK_CLR_RED, 20, 0, 0, 0, 0, 20}, [FLAVOR_DRY] = { PBLOCK_CLR_BLUE, 0, 20, 0, 0, 0, 20}, [FLAVOR_SWEET] = { PBLOCK_CLR_PINK, 0, 0, 20, 0, 0, 20}, [FLAVOR_BITTER] = { PBLOCK_CLR_GREEN, 0, 0, 0, 20, 0, 20}, [FLAVOR_SOUR] = { PBLOCK_CLR_YELLOW, 0, 0, 0, 0, 20, 20} }; static const struct WindowTemplate sWindowTemplates[] = { [WIN_TITLE] = { .bg = 0, .tilemapLeft = 2, .tilemapTop = 1, .width = 9, .height = 2, .paletteNum = 15, .baseBlock = 0x1E }, [WIN_LIST] = { .bg = 0, .tilemapLeft = 15, .tilemapTop = 1, .width = 14, .height = 18, .paletteNum = 15, .baseBlock = 0x30 }, [WIN_SPICY] = { .bg = 0, .tilemapLeft = 2, .tilemapTop = 13, .width = 5, .height = 2, .paletteNum = 15, .baseBlock = 0x12C }, [WIN_DRY] = { .bg = 0, .tilemapLeft = 2, .tilemapTop = 15, .width = 5, .height = 2, .paletteNum = 15, .baseBlock = 0x136 }, [WIN_SWEET] = { .bg = 0, .tilemapLeft = 2, .tilemapTop = 17, .width = 5, .height = 2, .paletteNum = 15, .baseBlock = 0x140 }, [WIN_BITTER] = { .bg = 0, .tilemapLeft = 8, .tilemapTop = 13, .width = 5, .height = 2, .paletteNum = 15, .baseBlock = 0x14A }, [WIN_SOUR] = { .bg = 0, .tilemapLeft = 8, .tilemapTop = 15, .width = 5, .height = 2, .paletteNum = 15, .baseBlock = 0x154 }, [WIN_FEEL] = { .bg = 0, .tilemapLeft = 11, .tilemapTop = 17, .width = 2, .height = 2, .paletteNum = 15, .baseBlock = 0x15E }, [WIN_ACTIONS_TALL] = { .bg = 1, .tilemapLeft = 7, .tilemapTop = 5, .width = 6, .height = 6, .paletteNum = 15, .baseBlock = 0x162 }, [WIN_ACTIONS] = { .bg = 1, .tilemapLeft = 7, .tilemapTop = 7, .width = 6, .height = 4, .paletteNum = 15, .baseBlock = 0x186 }, [WIN_TOSS_MSG] = { .bg = 1, .tilemapLeft = 2, .tilemapTop = 15, .width = 27, .height = 4, .paletteNum = 15, .baseBlock = 0x19E }, DUMMY_WIN_TEMPLATE }; static const struct WindowTemplate sTossPkblockWindowTemplate = { .bg = 1, .tilemapLeft = 21, .tilemapTop = 9, .width = 5, .height = 4, .paletteNum = 15, .baseBlock = 0x20A }; static const struct ListMenuTemplate sPokeblockListMenuTemplate = { .items = NULL, .moveCursorFunc = MovePokeblockMenuCursor, .itemPrintFunc = NULL, .totalItems = 0, .maxShowed = 0, .windowId = WIN_LIST, .header_X = 0, .item_X = 1, .cursor_X = 0, .upText_Y = 1, .cursorPal = 2, .fillValue = 0, .cursorShadowPal = 3, .lettersSpacing = 0, .itemVerticalPadding = 0, .scrollMultiple = LIST_MULTIPLE_SCROLL_DPAD, .fontId = FONT_NORMAL, .cursorKind = 1 }; void OpenPokeblockCase(u8 caseId, void (*callback)(void)) { sPokeblockMenu = Alloc(sizeof(*sPokeblockMenu)); sPokeblockMenu->caseId = caseId; sPokeblockMenu->callbackOnUse = NULL; sPokeblockMenu->arrowTaskId = TASK_NONE; sPokeblockMenu->isSwapping = FALSE; sSavedPokeblockData.callback = callback; switch (sPokeblockMenu->caseId) { case PBLOCK_CASE_BATTLE: sPokeblockMenu->pokeblockActionIds = sActionsInBattle; sPokeblockMenu->numActions = ARRAY_COUNT(sActionsInBattle); break; case PBLOCK_CASE_FEEDER: sPokeblockMenu->pokeblockActionIds = sActionsOnPokeblockFeeder; sPokeblockMenu->numActions = ARRAY_COUNT(sActionsOnPokeblockFeeder); break; case PBLOCK_CASE_GIVE: sPokeblockMenu->pokeblockActionIds = sActionsWhenGivingToLady; sPokeblockMenu->numActions = ARRAY_COUNT(sActionsWhenGivingToLady); break; default: // PBLOCK_CASE_FIELD sPokeblockMenu->pokeblockActionIds = sActionsOnField; sPokeblockMenu->numActions = ARRAY_COUNT(sActionsOnField); break; } SetMainCallback2(CB2_InitPokeblockMenu); } void OpenPokeblockCaseInBattle(void) { OpenPokeblockCase(PBLOCK_CASE_BATTLE, CB2_SetUpReshowBattleScreenAfterMenu2); } void OpenPokeblockCaseOnFeeder(void) { OpenPokeblockCase(PBLOCK_CASE_FEEDER, CB2_ReturnToField); } static void CB2_PokeblockMenu(void) { RunTasks(); AnimateSprites(); BuildOamBuffer(); DoScheduledBgTilemapCopiesToVram(); UpdatePaletteFade(); } static void VBlankCB_PokeblockMenu(void) { LoadOam(); ProcessSpriteCopyRequests(); TransferPlttBuffer(); } static void CB2_InitPokeblockMenu(void) { while (1) { if (MenuHelpers_ShouldWaitForLinkRecv() == TRUE) break; if (InitPokeblockMenu() == TRUE) break; if (MenuHelpers_IsLinkActive() == TRUE) break; } } #define tListTaskId data[0] #define tWindowId data[1] #define tToSwapId data[2] static bool8 InitPokeblockMenu(void) { u8 taskId; switch (gMain.state) { case 0: SetVBlankHBlankCallbacksToNull(); ClearScheduledBgCopiesToVram(); gMain.state++; break; case 1: ScanlineEffect_Stop(); gMain.state++; break; case 2: FreeAllSpritePalettes(); gMain.state++; break; case 3: ResetPaletteFade(); gPaletteFade.bufferTransferDisabled = TRUE; gMain.state++; break; case 4: ResetSpriteData(); gMain.state++; break; case 5: if (sPokeblockMenu->caseId != PBLOCK_CASE_BATTLE) ResetTasks(); gMain.state++; break; case 6: HandleInitBackgrounds(); sPokeblockMenu->gfxState = 0; gMain.state++; break; case 7: if (!LoadPokeblockMenuGfx()) return FALSE; gMain.state++; break; case 8: SetMenuItemsCountAndMaxShowed(); LimitMenuScrollAndRow(); SetInitialScroll(); gMain.state++; break; case 9: sPokeblockMenu->pokeblockCaseSpriteId = CreatePokeblockCaseSprite(56, 64, 0); gMain.state++; break; case 10: CreateSwapLineSprites(sPokeblockMenu->swapLineSpriteIds, ARRAY_COUNT(sPokeblockMenu->swapLineSpriteIds)); gMain.state++; break; case 11: DrawPokeblockMenuHighlight(sSavedPokeblockData.selectedRow, TILE_HIGHLIGHT_BLUE); gMain.state++; break; case 12: HandleInitWindows(); gMain.state++; break; case 13: UpdatePokeblockList(); gMain.state++; break; case 14: CreateScrollArrows(); gMain.state++; break; case 15: taskId = CreateTask(Task_HandlePokeblockMenuInput, 0); gTasks[taskId].tListTaskId = ListMenuInit(&gMultiuseListMenuTemplate, sSavedPokeblockData.scrollOffset, sSavedPokeblockData.selectedRow); gMain.state++; break; case 16: DrawPokeblockMenuTitleText(); gMain.state++; break; case 17: BlendPalettes(PALETTES_ALL, 16, 0); gMain.state++; break; case 18: BeginNormalPaletteFade(PALETTES_ALL, 0, 16, 0, RGB_BLACK); gPaletteFade.bufferTransferDisabled = FALSE; gMain.state++; break; default: SetVBlankCallback(VBlankCB_PokeblockMenu); SetMainCallback2(CB2_PokeblockMenu); return TRUE; } return FALSE; } static void HandleInitBackgrounds(void) { ResetVramOamAndBgCntRegs(); ResetBgsAndClearDma3BusyFlags(0); InitBgsFromTemplates(0, sBgTemplatesForPokeblockMenu, ARRAY_COUNT(sBgTemplatesForPokeblockMenu)); SetBgTilemapBuffer(2, sPokeblockMenu->tilemap); ResetAllBgsCoordinates(); ScheduleBgCopyTilemapToVram(2); SetGpuReg(REG_OFFSET_DISPCNT, DISPCNT_OBJ_ON | DISPCNT_OBJ_1D_MAP); ShowBg(0); ShowBg(1); ShowBg(2); SetGpuReg(REG_OFFSET_BLDCNT, 0); } static bool8 LoadPokeblockMenuGfx(void) { switch (sPokeblockMenu->gfxState) { case 0: ResetTempTileDataBuffers(); DecompressAndCopyTileDataToVram(2, gMenuPokeblock_Gfx, 0, 0, 0); sPokeblockMenu->gfxState++; break; case 1: if (FreeTempTileDataBuffersIfPossible() != TRUE) { LZDecompressWram(gMenuPokeblock_Tilemap, sPokeblockMenu->tilemap); sPokeblockMenu->gfxState++; } break; case 2: LoadCompressedPalette(gMenuPokeblock_Pal, 0, 0xC0); sPokeblockMenu->gfxState++; break; case 3: LoadCompressedSpriteSheet(&gPokeblockCase_SpriteSheet); sPokeblockMenu->gfxState++; break; case 4: LoadCompressedSpritePalette(&gPokeblockCase_SpritePal); sPokeblockMenu->gfxState++; break; case 5: LoadListMenuSwapLineGfx(); sPokeblockMenu->gfxState = 0; return TRUE; } return FALSE; } static void HandleInitWindows(void) { u8 i; InitWindows(sWindowTemplates); DeactivateAllTextPrinters(); LoadUserWindowBorderGfx(0, 1, 0xE0); LoadMessageBoxGfx(0, 0xA, 0xD0); LoadPalette(gStandardMenuPalette, 0xF0, 0x20); for (i = 0; i < ARRAY_COUNT(sWindowTemplates) - 1; i++) FillWindowPixelBuffer(i, PIXEL_FILL(0)); ScheduleBgCopyTilemapToVram(0); ScheduleBgCopyTilemapToVram(1); } static void PrintOnPokeblockWindow(u8 windowId, const u8 *string, s32 x) { AddTextPrinterParameterized4(windowId, FONT_NORMAL, x, 1, 0, 0, sTextColor, 0, string); } static void DrawPokeblockMenuTitleText(void) { u8 i; const u8 *itemName = ItemId_GetName(ITEM_POKEBLOCK_CASE); PrintOnPokeblockWindow(WIN_TITLE, itemName, GetStringCenterAlignXOffset(FONT_NORMAL, itemName, 0x48)); PrintOnPokeblockWindow(WIN_SPICY, gText_Spicy, 0); PrintOnPokeblockWindow(WIN_DRY, gText_Dry, 0); PrintOnPokeblockWindow(WIN_SWEET, gText_Sweet, 0); PrintOnPokeblockWindow(WIN_BITTER, gText_Bitter, 0); PrintOnPokeblockWindow(WIN_SOUR, gText_Sour, 0); for (i = 0; i < WIN_ACTIONS_TALL; i++) PutWindowTilemap(i); } static void UpdatePokeblockList(void) { u16 i; for (i = 0; i < sPokeblockMenu->itemsNo - 1; i++) { PutPokeblockListMenuString(sPokeblockMenu->menuItemsStrings[i], i); sPokeblockMenu->items[i].name = sPokeblockMenu->menuItemsStrings[i]; sPokeblockMenu->items[i].id = i; } StringCopy(sPokeblockMenu->menuItemsStrings[i], gText_StowCase); sPokeblockMenu->items[i].name = sPokeblockMenu->menuItemsStrings[i]; sPokeblockMenu->items[i].id = LIST_CANCEL; gMultiuseListMenuTemplate = sPokeblockListMenuTemplate; gMultiuseListMenuTemplate.fontId = FONT_NARROW; gMultiuseListMenuTemplate.totalItems = sPokeblockMenu->itemsNo; gMultiuseListMenuTemplate.items = sPokeblockMenu->items; gMultiuseListMenuTemplate.maxShowed = sPokeblockMenu->maxShowed; } static void PutPokeblockListMenuString(u8 *dst, u16 pkblId) { struct Pokeblock *pkblock = &gSaveBlock1Ptr->pokeblocks[pkblId]; u8 *txtPtr = StringCopy(dst, gPokeblockNames[pkblock->color]); *(txtPtr++) = EXT_CTRL_CODE_BEGIN; *(txtPtr++) = EXT_CTRL_CODE_SKIP; *(txtPtr++) = CHAR_BLOCK_1; ConvertIntToDecimalStringN(gStringVar1, GetHighestPokeblocksFlavorLevel(pkblock), STR_CONV_MODE_LEFT_ALIGN, 3); StringExpandPlaceholders(txtPtr, gText_LvVar1); } static void MovePokeblockMenuCursor(s32 pkblId, bool8 onInit, struct ListMenu *list) { if (onInit != TRUE) { PlaySE(SE_SELECT); gSprites[sPokeblockMenu->pokeblockCaseSpriteId].callback = SpriteCB_ShakePokeblockCase; } if (!sPokeblockMenu->isSwapping) DrawPokeblockInfo(pkblId); } static void DrawPokeblockInfo(s32 pkblId) { u8 i; struct Pokeblock *pokeblock; u16 rectTilemapSrc[2]; FillWindowPixelBuffer(7, PIXEL_FILL(0)); if (pkblId != LIST_CANCEL) { pokeblock = &gSaveBlock1Ptr->pokeblocks[pkblId]; rectTilemapSrc[0] = 0x17; rectTilemapSrc[1] = 0x18; for (i = 0; i < FLAVOR_COUNT; i++) { if (GetPokeblockData(pokeblock, PBLOCK_SPICY + i) > 0) { // Pokéblock has this flavor, draw Pokéblock icon next to it rectTilemapSrc[0] = (i << 12) + 0x17; rectTilemapSrc[1] = (i << 12) + 0x18; } else { // Pokéblock doesn't have this flavor, draw regular tiles rectTilemapSrc[0] = 0xF; rectTilemapSrc[1] = 0xF; } CopyToBgTilemapBufferRect(2, rectTilemapSrc, (i / 3 * 6) + 1, (i % 3 * 2) + 13, 1, 2); } // Print the Pokéblock's feel ConvertIntToDecimalStringN(gStringVar1, GetPokeblocksFeel(pokeblock), STR_CONV_MODE_RIGHT_ALIGN, 2); PrintOnPokeblockWindow(WIN_FEEL, gStringVar1, 4); } else { // Selected cancel, erase info rectTilemapSrc[0] = 0xF; rectTilemapSrc[1] = 0xF; for (i = 0; i < FLAVOR_COUNT; i++) CopyToBgTilemapBufferRect(2, rectTilemapSrc, (i / 3 * 6) + 1, (i % 3 * 2) + 13, 1, 2); CopyWindowToVram(7, COPYWIN_GFX); } ScheduleBgCopyTilemapToVram(0); ScheduleBgCopyTilemapToVram(2); } static void DrawPokeblockMenuHighlight(u16 cursorPos, u16 tileNum) { FillBgTilemapBufferRect_Palette0(2, tileNum, 0xF, (cursorPos * 2) + 1, 0xE, 2); ScheduleBgCopyTilemapToVram(2); } static void CompactPokeblockSlots(void) { u16 i, j; for (i = 0; i < POKEBLOCKS_COUNT - 1; i++) { for (j = i + 1; j < POKEBLOCKS_COUNT; j++) { if (gSaveBlock1Ptr->pokeblocks[i].color == PBLOCK_CLR_NONE) { struct Pokeblock temp = gSaveBlock1Ptr->pokeblocks[i]; gSaveBlock1Ptr->pokeblocks[i] = gSaveBlock1Ptr->pokeblocks[j]; gSaveBlock1Ptr->pokeblocks[j] = temp; } } } } static void SwapPokeblockMenuItems(u32 id1, u32 id2) { s16 i, count; struct Pokeblock *pokeblocks = gSaveBlock1Ptr->pokeblocks; struct Pokeblock *copyPokeblock1; if (id1 == id2) return; copyPokeblock1 = Alloc(sizeof(struct Pokeblock)); *copyPokeblock1 = pokeblocks[id1]; if (id2 > id1) { id2--; for (count = id2, i = id1; i < count; i++) pokeblocks[i] = pokeblocks[i + 1]; } else { for (count = id2, i = id1; i > count; i--) pokeblocks[i] = pokeblocks[i - 1]; } pokeblocks[id2] = *copyPokeblock1; Free(copyPokeblock1); } void ResetPokeblockScrollPositions(void) { sSavedPokeblockData.selectedRow = 0; sSavedPokeblockData.scrollOffset = 0; } static void SetMenuItemsCountAndMaxShowed(void) { u16 i; CompactPokeblockSlots(); for (sPokeblockMenu->itemsNo = 0, i = 0; i < POKEBLOCKS_COUNT; i++) { if (gSaveBlock1Ptr->pokeblocks[i].color != PBLOCK_CLR_NONE) sPokeblockMenu->itemsNo++; } sPokeblockMenu->itemsNo++; // STOW CASE menu item if (sPokeblockMenu->itemsNo > MAX_MENU_ITEMS) sPokeblockMenu->maxShowed = MAX_MENU_ITEMS; else sPokeblockMenu->maxShowed = sPokeblockMenu->itemsNo; } static void LimitMenuScrollAndRow(void) { if (sSavedPokeblockData.scrollOffset != 0) { if (sSavedPokeblockData.scrollOffset + sPokeblockMenu->maxShowed > sPokeblockMenu->itemsNo) sSavedPokeblockData.scrollOffset = sPokeblockMenu->itemsNo - sPokeblockMenu->maxShowed; } if (sSavedPokeblockData.scrollOffset + sSavedPokeblockData.selectedRow >= sPokeblockMenu->itemsNo) { if (sPokeblockMenu->itemsNo == 0) sSavedPokeblockData.selectedRow = 0; else sSavedPokeblockData.selectedRow = sPokeblockMenu->itemsNo - 1; } } static void SetInitialScroll(void) { if (sSavedPokeblockData.selectedRow > MENU_MIDPOINT) { u8 i; for (i = 0; (i < sSavedPokeblockData.selectedRow - MENU_MIDPOINT) && (sSavedPokeblockData.scrollOffset + sPokeblockMenu->maxShowed != sPokeblockMenu->itemsNo); sSavedPokeblockData.selectedRow--, sSavedPokeblockData.scrollOffset++, i++); } } static void CreateScrollArrows(void) { if (sPokeblockMenu->arrowTaskId == TASK_NONE) { sPokeblockMenu->arrowTaskId = AddScrollIndicatorArrowPairParameterized(SCROLL_ARROW_UP, 0xB0, 8, 0x98, sPokeblockMenu->itemsNo - sPokeblockMenu->maxShowed, TAG_SCROLL_ARROW, TAG_SCROLL_ARROW, &sSavedPokeblockData.scrollOffset); } } static void DestroyScrollArrows(void) { if (sPokeblockMenu->arrowTaskId != TASK_NONE) { RemoveScrollIndicatorArrowPair(sPokeblockMenu->arrowTaskId); sPokeblockMenu->arrowTaskId = TASK_NONE; } } u8 CreatePokeblockCaseSprite(s16 x, s16 y, u8 subpriority) { return CreateSprite(&sSpriteTemplate_PokeblockCase, x, y, subpriority); } #define sState data[0] #define sTimer data[1] static void SpriteCB_ShakePokeblockCase(struct Sprite *sprite) { if (sprite->sState > 1) sprite->sState = 0; switch (sprite->sState) { case 0: sprite->oam.affineMode = ST_OAM_AFFINE_NORMAL; sprite->affineAnims = sAffineAnims_PokeblockCaseShake; InitSpriteAffineAnim(sprite); sprite->sState = 1; sprite->sTimer = 0; break; case 1: if (++sprite->sTimer > 11) { sprite->oam.affineMode = ST_OAM_AFFINE_OFF; sprite->sState = 0; sprite->sTimer = 0; FreeOamMatrix(sprite->oam.matrixNum); sprite->callback = SpriteCallbackDummy; } break; } } static void FadePaletteAndSetTaskToClosePokeblockCase(u8 taskId) { BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 16, RGB_BLACK); gTasks[taskId].func = Task_FreeDataAndExitPokeblockCase; } static void Task_FreeDataAndExitPokeblockCase(u8 taskId) { s16 *data = gTasks[taskId].data; if (!gPaletteFade.active) { if (sPokeblockMenu->caseId == PBLOCK_CASE_FEEDER || sPokeblockMenu->caseId == PBLOCK_CASE_GIVE) gFieldCallback = FieldCB_ContinueScriptHandleMusic; DestroyListMenuTask(tListTaskId, &sSavedPokeblockData.scrollOffset, &sSavedPokeblockData.selectedRow); DestroyScrollArrows(); ResetSpriteData(); FreeAllSpritePalettes(); if (sPokeblockMenu->callbackOnUse != NULL) SetMainCallback2(sPokeblockMenu->callbackOnUse); else SetMainCallback2(sSavedPokeblockData.callback); FreeAllWindowBuffers(); Free(sPokeblockMenu); DestroyTask(taskId); } } static void Task_HandlePokeblockMenuInput(u8 taskId) { s16 *data = gTasks[taskId].data; if (!gPaletteFade.active && MenuHelpers_ShouldWaitForLinkRecv() != TRUE) { if (JOY_NEW(SELECT_BUTTON)) { ListMenuGetScrollAndRow(tListTaskId, &sSavedPokeblockData.scrollOffset, &sSavedPokeblockData.selectedRow); if (sSavedPokeblockData.scrollOffset + sSavedPokeblockData.selectedRow != sPokeblockMenu->itemsNo - 1) { // Chose menu item to swap PlaySE(SE_SELECT); DrawPokeblockMenuHighlight(sSavedPokeblockData.selectedRow, TILE_HIGHLIGHT_RED); tToSwapId = sSavedPokeblockData.scrollOffset + sSavedPokeblockData.selectedRow; sPokeblockMenu->isSwapping = TRUE; gTasks[taskId].func = Task_HandlePokeblocksSwapInput; } } else { u16 oldPosition = sSavedPokeblockData.selectedRow; s32 input = ListMenu_ProcessInput(tListTaskId); ListMenuGetScrollAndRow(tListTaskId, &sSavedPokeblockData.scrollOffset, &sSavedPokeblockData.selectedRow); if (oldPosition != sSavedPokeblockData.selectedRow) { // Moved cursor DrawPokeblockMenuHighlight(oldPosition, TILE_HIGHLIGHT_NONE); DrawPokeblockMenuHighlight(sSavedPokeblockData.selectedRow, TILE_HIGHLIGHT_BLUE); } switch (input) { case LIST_NOTHING_CHOSEN: break; case LIST_CANCEL: PlaySE(SE_SELECT); gSpecialVar_Result = 0xFFFF; gSpecialVar_ItemId = 0; FadePaletteAndSetTaskToClosePokeblockCase(taskId); break; default: // Selected Pokéblock PlaySE(SE_SELECT); gSpecialVar_ItemId = input; ShowPokeblockActionsWindow(taskId); break; } } } } static void Task_HandlePokeblocksSwapInput(u8 taskId) { s16 *data = gTasks[taskId].data; if (MenuHelpers_ShouldWaitForLinkRecv() == TRUE) return; if (JOY_NEW(SELECT_BUTTON)) { // Swap items PlaySE(SE_SELECT); ListMenuGetScrollAndRow(tListTaskId, &sSavedPokeblockData.scrollOffset, &sSavedPokeblockData.selectedRow); UpdatePokeblockSwapMenu(taskId, FALSE); } else { u16 i = sSavedPokeblockData.scrollOffset; u16 row = sSavedPokeblockData.selectedRow; s32 input = ListMenu_ProcessInput(tListTaskId); ListMenuGetScrollAndRow(tListTaskId, &sSavedPokeblockData.scrollOffset, &sSavedPokeblockData.selectedRow); if (i != sSavedPokeblockData.scrollOffset || row != sSavedPokeblockData.selectedRow) { for (i = 0; i < MAX_MENU_ITEMS; i++) { row = i + sSavedPokeblockData.scrollOffset; if (row == tToSwapId) DrawPokeblockMenuHighlight(i, TILE_HIGHLIGHT_RED); else DrawPokeblockMenuHighlight(i, TILE_HIGHLIGHT_NONE); } } SetSwapLineSpritesInvisibility(sPokeblockMenu->swapLineSpriteIds, ARRAY_COUNT(sPokeblockMenu->swapLineSpriteIds), FALSE); UpdateSwapLineSpritesPos(sPokeblockMenu->swapLineSpriteIds, ARRAY_COUNT(sPokeblockMenu->swapLineSpriteIds), 128, (sSavedPokeblockData.selectedRow * 16) + 8); switch (input) { case LIST_NOTHING_CHOSEN: break; case LIST_CANCEL: PlaySE(SE_SELECT); if (JOY_NEW(A_BUTTON)) // Pointless check, B Button has been pressed here UpdatePokeblockSwapMenu(taskId, FALSE); else UpdatePokeblockSwapMenu(taskId, TRUE); // Canceled swapping break; default: // Swap items PlaySE(SE_SELECT); UpdatePokeblockSwapMenu(taskId, FALSE); break; } } } static void UpdatePokeblockSwapMenu(u8 taskId, bool8 noSwap) { u8 i; s16 *data = gTasks[taskId].data; u16 swappedFromId = sSavedPokeblockData.scrollOffset + sSavedPokeblockData.selectedRow; sPokeblockMenu->isSwapping = FALSE; DestroyListMenuTask(tListTaskId, &sSavedPokeblockData.scrollOffset, &sSavedPokeblockData.selectedRow); if (!noSwap && tToSwapId != swappedFromId && tToSwapId != swappedFromId - 1) { SwapPokeblockMenuItems(tToSwapId, swappedFromId); UpdatePokeblockList(); } if (tToSwapId < swappedFromId) sSavedPokeblockData.selectedRow--; tListTaskId = ListMenuInit(&gMultiuseListMenuTemplate, sSavedPokeblockData.scrollOffset, sSavedPokeblockData.selectedRow); ScheduleBgCopyTilemapToVram(0); SetSwapLineSpritesInvisibility(sPokeblockMenu->swapLineSpriteIds, ARRAY_COUNT(sPokeblockMenu->swapLineSpriteIds), TRUE); for (i = 0; i < MAX_MENU_ITEMS; i++) DrawPokeblockMenuHighlight(i, TILE_HIGHLIGHT_NONE); DrawPokeblockMenuHighlight(sSavedPokeblockData.selectedRow, TILE_HIGHLIGHT_BLUE); gTasks[taskId].func = Task_HandlePokeblockMenuInput; } static void ShowPokeblockActionsWindow(u8 taskId) { s16 *data = gTasks[taskId].data; if (sPokeblockMenu->numActions == 3) tWindowId = WIN_ACTIONS_TALL; else tWindowId = WIN_ACTIONS; DestroyScrollArrows(); DrawStdFrameWithCustomTileAndPalette(tWindowId, 0, 1, 0xE); PrintMenuActionTextsInUpperLeftCorner(tWindowId, sPokeblockMenu->numActions, sPokeblockMenuActions, sPokeblockMenu->pokeblockActionIds); InitMenuInUpperLeftCornerNormal(tWindowId, sPokeblockMenu->numActions, 0); PutWindowTilemap(tWindowId); ScheduleBgCopyTilemapToVram(1); gTasks[taskId].func = Task_HandlePokeblockActionsInput; } static void Task_HandlePokeblockActionsInput(u8 taskId) { s8 itemId; if (MenuHelpers_ShouldWaitForLinkRecv() == TRUE) return; itemId = Menu_ProcessInputNoWrap(); if (itemId == MENU_NOTHING_CHOSEN) { return; } else if (itemId == MENU_B_PRESSED) { PlaySE(SE_SELECT); PokeblockAction_Cancel(taskId); } else { PlaySE(SE_SELECT); sPokeblockMenuActions[sPokeblockMenu->pokeblockActionIds[itemId]].func.void_u8(taskId); } } static void PokeblockAction_UseOnField(u8 taskId) { sPokeblockMenu->callbackOnUse = UsePokeblockOnField; FadePaletteAndSetTaskToClosePokeblockCase(taskId); } static void UsePokeblockOnField(void) { ChooseMonToGivePokeblock(&gSaveBlock1Ptr->pokeblocks[gSpecialVar_ItemId], ReturnToPokeblockCaseOnField); } static void ReturnToPokeblockCaseOnField(void) { OpenPokeblockCase(PBLOCK_CASE_FIELD, sSavedPokeblockData.callback); } static void PokeblockAction_Toss(u8 taskId) { s16 *data = gTasks[taskId].data; ClearStdWindowAndFrameToTransparent(tWindowId, FALSE); StringCopy(gStringVar1, gPokeblockNames[gSaveBlock1Ptr->pokeblocks[gSpecialVar_ItemId].color]); StringExpandPlaceholders(gStringVar4, gText_ThrowAwayVar1); DisplayMessageAndContinueTask(taskId, WIN_TOSS_MSG, 10, 13, FONT_NORMAL, GetPlayerTextSpeedDelay(), gStringVar4, CreateTossPokeblockYesNoMenu); } static void CreateTossPokeblockYesNoMenu(u8 taskId) { CreateYesNoMenuWithCallbacks(taskId, &sTossPkblockWindowTemplate, 1, 0, 2, 1, 0xE, &sTossYesNoFuncTable); } static void TossedPokeblockMessage(u8 taskId) { StringExpandPlaceholders(gStringVar4, gText_Var1ThrownAway); DisplayMessageAndContinueTask(taskId, WIN_TOSS_MSG, 10, 13, FONT_NORMAL, GetPlayerTextSpeedDelay(), gStringVar4, TossPokeblock); } static void TossPokeblock(u8 taskId) { if (JOY_NEW(A_BUTTON | B_BUTTON)) { s16 *data; u16 *scrollOffset, *selectedRow; TryClearPokeblock(gSpecialVar_ItemId); PlaySE(SE_SELECT); scrollOffset = &sSavedPokeblockData.scrollOffset; selectedRow = &sSavedPokeblockData.selectedRow; data = gTasks[taskId].data; DestroyListMenuTask(tListTaskId, scrollOffset, selectedRow); DrawPokeblockMenuHighlight(*selectedRow, TILE_HIGHLIGHT_NONE); SetMenuItemsCountAndMaxShowed(); LimitMenuScrollAndRow(); UpdatePokeblockList(); tListTaskId = ListMenuInit(&gMultiuseListMenuTemplate, *scrollOffset, *selectedRow); DrawPokeblockMenuHighlight(*selectedRow, TILE_HIGHLIGHT_BLUE); ScheduleBgCopyTilemapToVram(0); ScheduleBgCopyTilemapToVram(1); CloseTossPokeblockWindow(taskId); } } static void CloseTossPokeblockWindow(u8 taskId) { ClearDialogWindowAndFrameToTransparent(WIN_TOSS_MSG, FALSE); ScheduleBgCopyTilemapToVram(1); CreateScrollArrows(); gTasks[taskId].func = Task_HandlePokeblockMenuInput; } static void PokeblockAction_UseInBattle(u8 taskId) { u8 nature = GetNature(&gEnemyParty[0]); s16 gain = PokeblockGetGain(nature, &gSaveBlock1Ptr->pokeblocks[gSpecialVar_ItemId]); StringCopy(gBattleTextBuff1, gPokeblockNames[gSaveBlock1Ptr->pokeblocks[gSpecialVar_ItemId].color]); TryClearPokeblock(gSpecialVar_ItemId); gSpecialVar_ItemId = gSaveBlock1Ptr->pokeblocks[gSpecialVar_ItemId].color << 8; if (gain == 0) gSpecialVar_ItemId += 1; else if (gain > 0) gSpecialVar_ItemId += 2; else gSpecialVar_ItemId += 3; FadePaletteAndSetTaskToClosePokeblockCase(taskId); } static void PokeblockAction_UseOnPokeblockFeeder(u8 taskId) { SafariZoneActivatePokeblockFeeder(gSpecialVar_ItemId); StringCopy(gStringVar1, gPokeblockNames[gSaveBlock1Ptr->pokeblocks[gSpecialVar_ItemId].color]); gSpecialVar_Result = gSpecialVar_ItemId; TryClearPokeblock(gSpecialVar_ItemId); gSpecialVar_ItemId = 0; FadePaletteAndSetTaskToClosePokeblockCase(taskId); } static void PokeblockAction_GiveToContestLady(u8 taskId) { gSpecialVar_0x8004 = GivePokeblockToContestLady(&gSaveBlock1Ptr->pokeblocks[gSpecialVar_ItemId]); gSpecialVar_Result = gSpecialVar_ItemId; TryClearPokeblock(gSpecialVar_ItemId); gSpecialVar_ItemId = 0; FadePaletteAndSetTaskToClosePokeblockCase(taskId); } static void PokeblockAction_Cancel(u8 taskId) { s16 *data = gTasks[taskId].data; ClearStdWindowAndFrameToTransparent(tWindowId, FALSE); ScheduleBgCopyTilemapToVram(1); CreateScrollArrows(); gTasks[taskId].func = Task_HandlePokeblockMenuInput; } static void ClearPokeblock(u8 pkblId) { gSaveBlock1Ptr->pokeblocks[pkblId].color = 0; gSaveBlock1Ptr->pokeblocks[pkblId].spicy = 0; gSaveBlock1Ptr->pokeblocks[pkblId].dry = 0; gSaveBlock1Ptr->pokeblocks[pkblId].sweet = 0; gSaveBlock1Ptr->pokeblocks[pkblId].bitter = 0; gSaveBlock1Ptr->pokeblocks[pkblId].sour = 0; gSaveBlock1Ptr->pokeblocks[pkblId].feel = 0; } void ClearPokeblocks(void) { u8 i; for (i = 0; i < POKEBLOCKS_COUNT; i++) ClearPokeblock(i); } u8 GetHighestPokeblocksFlavorLevel(const struct Pokeblock *pokeblock) { u8 i; u8 maxFlavor = GetPokeblockData(pokeblock, PBLOCK_SPICY); for (i = PBLOCK_SPICY; i < FLAVOR_COUNT; i++) { u8 currFlavor = GetPokeblockData(pokeblock, PBLOCK_SPICY + i); if (maxFlavor < currFlavor) maxFlavor = currFlavor; } return maxFlavor; } u8 GetPokeblocksFeel(const struct Pokeblock *pokeblock) { u8 feel = GetPokeblockData(pokeblock, PBLOCK_FEEL); if (feel > POKEBLOCK_MAX_FEEL) feel = POKEBLOCK_MAX_FEEL; return feel; } s8 GetFirstFreePokeblockSlot(void) { u8 i; for (i = 0; i < POKEBLOCKS_COUNT; i++) { if (gSaveBlock1Ptr->pokeblocks[i].color == PBLOCK_CLR_NONE) return i; } return -1; } bool32 AddPokeblock(const struct Pokeblock *pokeblock) { s8 slot = GetFirstFreePokeblockSlot(); if (slot == -1) { return FALSE; } else { gSaveBlock1Ptr->pokeblocks[slot] = *pokeblock; return TRUE; } } bool32 TryClearPokeblock(u8 pkblId) { if (gSaveBlock1Ptr->pokeblocks[pkblId].color == PBLOCK_CLR_NONE) { return FALSE; } else { ClearPokeblock(pkblId); return TRUE; } } s16 GetPokeblockData(const struct Pokeblock *pokeblock, u8 field) { if (field == PBLOCK_COLOR) return pokeblock->color; if (field == PBLOCK_SPICY) return pokeblock->spicy; if (field == PBLOCK_DRY) return pokeblock->dry; if (field == PBLOCK_SWEET) return pokeblock->sweet; if (field == PBLOCK_BITTER) return pokeblock->bitter; if (field == PBLOCK_SOUR) return pokeblock->sour; if (field == PBLOCK_FEEL) return pokeblock->feel; return 0; } s16 PokeblockGetGain(u8 nature, const struct Pokeblock *pokeblock) { u8 flavor; s16 curGain, totalGain = 0; for (flavor = 0; flavor < FLAVOR_COUNT; flavor++) { curGain = GetPokeblockData(pokeblock, flavor + PBLOCK_SPICY); if (curGain > 0) totalGain += curGain * gPokeblockFlavorCompatibilityTable[FLAVOR_COUNT * nature + flavor]; } return totalGain; } void PokeblockCopyName(const struct Pokeblock *pokeblock, u8 *dest) { u8 color = GetPokeblockData(pokeblock, PBLOCK_COLOR); StringCopy(dest, gPokeblockNames[color]); } bool8 CopyMonFavoritePokeblockName(u8 nature, u8 *dest) { u8 i; for (i = 0; i < FLAVOR_COUNT; i++) { if (PokeblockGetGain(nature, &sFavoritePokeblocksTable[i]) > 0) { StringCopy(dest, gPokeblockNames[i + 1]); return TRUE; } } return FALSE; } u8 GetPokeblocksFlavor(const struct Pokeblock *pokeblock) { s16 bestFlavor = 0; s16 i; for (i = 0; i < FLAVOR_COUNT; i++) { if (GetPokeblockData(pokeblock, bestFlavor + 1) < GetPokeblockData(pokeblock, i + 1)) bestFlavor = i; } return bestFlavor; }