#include #include #include #include #include "pokedex_common.h" #define TB_PKNAME 0 #define TB_PKSIZE 1 #define TB_DESC 2 #define TB_PKNAME_W 30 #define TB_SW_W 12 extern pchar pdex_str_size[]; extern pchar pdex_str_weight[]; extern pchar pdex_str_size_unit[]; extern pchar pdex_str_weight_unit[]; extern pchar pdex_str_comma[]; struct TextboxTemplate dexdetail_boxes[] = { {.bg_id = 0, .x = 0, .y = 0, .width = TB_PKNAME_W, .height = 4, .pal_id = 15, .charbase = 1}, {.bg_id = 0, .x = 17, .y = 5, .width = TB_SW_W, .height = 4, .pal_id = 15, .charbase = 121}, {.bg_id = 1, .x = 1, .y = 0, .width = 28, .height = 20, .pal_id = 15, .charbase = 161}, {.bg_id = 0xFF}, }; u8 dexdetail_type_to_oam_type[18] = {0, 9, 8, 12, 6, 5, 11, 10, 14, 17, 1, 2, 3, 4, 13, 7, 16, 15}; #define O_TYPE(t) (dexdetail_type_to_oam_type[t]) struct OamData dexdetail_type_oam = { .affine_mode = 0, .obj_mode = 0, .mosaic = false, .shape = 1, .size = 2, }; void dexdetail_update_type_oam(u8 type, struct Object *obj) { memcpy((void *)((0x06010000) + (obj->final_oam.tile_num * 0x20)), pokemon_type_chart_gfx + (128 * O_TYPE(type)) + (512 * (O_TYPE(type) >> 2)), 128); memcpy((void *)((0x06010000) + 128 + (obj->final_oam.tile_num * 0x20)), pokemon_type_chart_gfx + 512 + (128 * O_TYPE(type)) + (512 * (O_TYPE(type) >> 2)), 128); } s8 dexdetail_load_type_oam(u8 type, u16 tag) { struct SpriteTiles typeTiles = {(&pokemon_type_chart_gfx[0]) + 256 * O_TYPE(type), 256, tag}; gpu_tile_obj_alloc_tag_and_upload(&typeTiles); const struct Template typeTemplate = { .tiles_tag = tag, .pal_tag = DEX_DETAIL_TYPEPAL, .oam = &dexdetail_type_oam, .animation = &anim_image_empty, .graphics = &typeTiles, .rotscale = &rotscale_empty, .callback = oac_nullsub, }; s8 id = (s8)template_instanciate_forward_search(&typeTemplate, 100, 100, 0); dexdetail_update_type_oam(type, &objects[id]); return id; } extern pchar pdex_str_form_alola[]; void dexdetail_load_pokemon(u16 dexindex, enum DexFormType formType) { u16 species = pdex_lazy_lookup_entry(dexindex)->species; rboxid_clear_pixels(TB_PKNAME, 0); rboxid_clear_pixels(TB_PKSIZE, 0); rboxid_clear_pixels(TB_DESC, 0); pstrcpy(string_buffer, &pokemon_names[species][0]); switch (formType) { case ALOLA: pstrcat(string_buffer, pdex_str_form_alola); break; default: break; } u16 twidth = font_get_width_of_string(FONT_DEX_STD, string_buffer, 0x0000); rboxid_print(TB_PKNAME, FONT_DEX_STD, TB_STD_CENTER(twidth, 8 * TB_PKNAME_W), 0, &pdex_text_color, 0, string_buffer); pchar *strType = &pokedex_data[dexindex].category_name[0]; u16 typeTwidth = font_get_width_of_string(FONT_DEX_STD, strType, 0x0000); rboxid_print(TB_PKNAME, FONT_DEX_STD, TB_STD_CENTER(typeTwidth, 88) + 110, 14, &pdex_text_color, 0, strType); rboxid_print(TB_PKSIZE, FONT_DEX_STD, 4, 1, &pdex_text_color, 0, pdex_str_size); rboxid_print(TB_PKSIZE, FONT_DEX_STD, 4, 14, &pdex_text_color, 0, pdex_str_weight); u16 weightNumber = pokedex_data[dexindex].weight / 10; u16 weightDecimal = pokedex_data[dexindex].weight % 10; pchar buffer[20]; fmt_int_10(string_buffer, weightNumber, 0, 3); fmt_int_10(&buffer[0], weightDecimal, 0, 1); pstrcat(string_buffer, pdex_str_comma); pstrcat(string_buffer, buffer); pstrcat(string_buffer, pdex_str_weight_unit); u16 weightTwidth = font_get_width_of_string(FONT_DEX_STD, string_buffer, 0x0000); rboxid_print(TB_PKSIZE, FONT_DEX_STD, TB_STD_RIGHT(weightTwidth, TB_SW_W * 8) - 6, 14, &pdex_text_color, 0, string_buffer); u16 sizeNumber = pokedex_data[dexindex].height / 10; u16 sizeDecimal = pokedex_data[dexindex].height % 10; fmt_int_10(string_buffer, sizeNumber, 0, 3); fmt_int_10(&buffer[0], sizeDecimal, 0, 1); pstrcat(string_buffer, pdex_str_comma); pstrcat(string_buffer, buffer); pstrcat(string_buffer, pdex_str_size_unit); rboxid_print(TB_PKSIZE, FONT_DEX_STD, TB_STD_RIGHT(weightTwidth, TB_SW_W * 8) - 6, 1, &pdex_text_color, 0, string_buffer); rboxid_print(TB_DESC, FONT_DEX_STD, 3, 2, &pdex_text_color, 0, pokedex_data[dexindex].description1); /* setup scrolling parameters */ u8 descLines = pstr_lines(&pokedex_data[dexindex].description1[0]); if (descLines > 4) pokedex_context->detail_scroll_max = descLines - 4; else pokedex_context->detail_scroll_max = 0; /* load the species sprite */ if (pokedex_context->detail_pokemon_oam != -1) { lz77UnCompVram( pokemon_graphics_front[species].data, ((void *)(objects[pokedex_context->detail_pokemon_oam].final_oam.tile_num * 32) + ADDR_VRAM + 0x10000)); gpu_pal_apply_compressed(pokemon_palette_normal[species].data, 16 * (objects[pokedex_context->detail_pokemon_oam].final_oam.palette_num + 16), 32); } else { struct SpriteTiles pkmnTiles = {pokemon_graphics_front[species].data, 2048, DEX_PKMN_TAG_ONE}; struct SpritePalette pkmnPal = {pokemon_palette_normal[species].data, DEX_PKMN_TAG_ONE}; const struct Template pkmnTemplate = { .tiles_tag = DEX_PKMN_TAG_ONE, .pal_tag = DEX_PKMN_TAG_ONE, .oam = &pdex_oam_pkmn, .animation = &anim_image_empty, .graphics = &pkmnTiles, .rotscale = &rotscale_empty, .callback = oac_nullsub, }; gpu_tile_obj_decompress_alloc_tag_and_upload(&pkmnTiles); gpu_pal_decompress_alloc_tag_and_upload(&pkmnPal); pokedex_context->detail_pokemon_oam = (s8)template_instanciate_forward_search(&pkmnTemplate, 10, 10, 0); objects[pokedex_context->detail_pokemon_oam].pos1.x = 55; objects[pokedex_context->detail_pokemon_oam].pos1.y = 55; } // draw the first type icon if (pokedex_context->detail_type_oam[0] != -1) { dexdetail_update_type_oam(pokemon_base_stats[species].type[0], &objects[pokedex_context->detail_type_oam[0]]); } else { pokedex_context->detail_type_oam[0] = dexdetail_load_type_oam(pokemon_base_stats[species].type[0], DEX_DETAIL_TYPE1); objects[pokedex_context->detail_type_oam[0]].pos1.x = 156; objects[pokedex_context->detail_type_oam[0]].pos1.y = 80; } // draw the second type icon eventually if (pokemon_base_stats[species].type[0] != pokemon_base_stats[species].type[1]) { if (pokedex_context->detail_type_oam[1] != -1) { dexdetail_update_type_oam(pokemon_base_stats[species].type[1], &objects[pokedex_context->detail_type_oam[1]]); } else { pokedex_context->detail_type_oam[1] = dexdetail_load_type_oam(pokemon_base_stats[species].type[1], DEX_DETAIL_TYPE2); objects[pokedex_context->detail_type_oam[1]].pos1.x = 210; objects[pokedex_context->detail_type_oam[1]].pos1.y = 80; } OBJID_SHOW(pokedex_context->detail_type_oam[1]); } else { OBJID_HIDE(pokedex_context->detail_type_oam[1]); } rboxid_update_tilemap_and_tileset(TB_DESC); rboxid_update_tilemap_and_tileset(TB_PKNAME); rboxid_update_tilemap_and_tileset(TB_PKSIZE); } void dexdetail_update_form(u16 form) { REG_DISPCNT &= ~((1 << 8) | (1 << 9)); if (form == 0) { dexdetail_load_pokemon(pokedex_context->cursor_position_top + pokedex_context->cursor_position_internal, NORMAL); } else { form--; if (pokedex_context->detail_forms[form].index > PDEX_LAST_SHOWN) { dexdetail_load_pokemon(pokedex_context->detail_forms[form].index, pokedex_context->detail_forms[form].type); } else { // update the oam only u16 species = pokedex_context->detail_forms[form].species; lz77UnCompVram( pokemon_graphics_front[species].data, ((void *)(objects[pokedex_context->detail_pokemon_oam].final_oam.tile_num * 32) + ADDR_VRAM + 0x10000)); gpu_pal_apply_compressed(pokemon_palette_normal[species].data, 16 * (objects[pokedex_context->detail_pokemon_oam].final_oam.palette_num + 16), 32); } } REG_DISPCNT |= ((1 << 8) | (1 << 9)); } struct DexFormEntry *dexdetail_get_forms(u16 index) { u16 current = 0; while (pdex_forms[current].index != 0xFFFF) { if (pdex_forms[current].index == index) return &pdex_forms[current].forms[0]; current++; } return NULL; } void dexdetail_oac_arrow(struct Object *obj) { if (pokedex_context->detail_forms != NULL) { if (obj->priv[0]) { // this is the left arrow if (pokedex_context->detail_form_position != 0) { obj->final_oam.palette_num = obj->priv[1]; } else { obj->final_oam.palette_num = obj->priv[2]; } } else { // this is the right arrow if (pokedex_context->detail_forms[pokedex_context->detail_form_position].type != FORM_END) { obj->final_oam.palette_num = obj->priv[1]; } else { obj->final_oam.palette_num = obj->priv[2]; } } } } struct SpriteTiles pdex_arrow_rot_tiles = {pdexArrowRotatedTiles, 256, DEX_ARROW_TAG}; struct SpritePalette pdex_arrow_rot_pal = {pdexArrowRotatedPal, DEX_ARROW_TAG}; struct SpritePalette pdex_arrow_rot_pal_gray = {pdexArrowRotatedPal, DEX_ARROW_TAG_EPAL}; const struct OamData pdex_arrow_rot_oam = { .affine_mode = 0, .obj_mode = 0, .mosaic = false, .shape = 0, .size = 1, }; const struct Template pdex_arrow_rot_template = { .tiles_tag = DEX_ARROW_TAG, .pal_tag = DEX_ARROW_TAG, .oam = &pdex_arrow_rot_oam, .animation = &anim_image_empty, .graphics = &pdex_arrow_rot_tiles, .rotscale = &rotscale_empty, .callback = dexdetail_oac_arrow, }; void dexdetail_load_form_arrows(void) { gpu_tile_obj_decompress_alloc_tag_and_upload(&pdex_arrow_rot_tiles); u8 normalPal = gpu_pal_obj_alloc_tag_and_apply(&pdex_arrow_rot_pal); u8 grayPal = gpu_pal_obj_alloc_tag_and_apply(&pdex_arrow_rot_pal_gray); tint_palette_gray(&palette_bg_unfaded[16 * (16 + grayPal)], 16); u8 leftArrow = template_instanciate_forward_search(&pdex_arrow_rot_template, 10, 55, 0); u8 rightArrow = template_instanciate_forward_search(&pdex_arrow_rot_template, 100, 55, 0); objects[leftArrow].priv[0] = true; objects[leftArrow].final_oam.h_flip = true; objects[leftArrow].priv[1] = normalPal; objects[leftArrow].priv[2] = grayPal; objects[rightArrow].priv[0] = false; objects[rightArrow].priv[1] = normalPal; objects[rightArrow].priv[2] = grayPal; pokedex_context->detail_arrows_oam[0] = leftArrow; pokedex_context->detail_arrows_oam[1] = rightArrow; } void dexdetail_scroll_update(void) { /* base of -24576 + (scroll_amount * pixel_length * 14 pixels) */ bgid_mod_y_offset(1, -24576 + (pokedex_context->detail_scroll_amount * 256 * 14), 0); } void dexdetail_loop(u8 tid) { u16 currentIndex = pokedex_context->cursor_position_top + pokedex_context->cursor_position_internal; switch (pokedex_context->state) { case 0: bgid_send_tilemap(2); dexdetail_load_form_arrows(); dexdetail_load_pokemon(currentIndex, NORMAL); pokedex_context->detail_form_position = 0; struct DexFormEntry *forms = dexdetail_get_forms(currentIndex); if (forms != NULL) { OBJID_SHOW(pokedex_context->detail_arrows_oam[0]); OBJID_SHOW(pokedex_context->detail_arrows_oam[1]); } else { OBJID_HIDE(pokedex_context->detail_arrows_oam[0]); OBJID_HIDE(pokedex_context->detail_arrows_oam[1]); } pokedex_context->detail_forms = forms; palette_bg_faded_fill_black(); pokedex_context->state++; break; case 1: gpu_sync_bg_show(0); gpu_sync_bg_show(1); gpu_sync_bg_hide(3); gpu_sync_bg_show(2); fade_screen(0xFFFFFFFF, PDEX_FADEIN_SPD, 16, 0, 0x0000); pokedex_context->state++; break; case 2: if (!pal_fade_control.active) { pokecry_play(pdex_lazy_lookup_entry(currentIndex)->species, 0); pokedex_context->state++; } break; case 3: switch (super.buttons_new) { case KEY_B: pokedex_context->state = 12; u16 dexindex = pokedex_context->cursor_position_top + pokedex_context->cursor_position_internal; if (dexindex <= PDEX_LAST_SHOWN - 7) { pokedex_context->cursor_position_top = dexindex; pokedex_context->cursor_position_internal = 0; } else { pokedex_context->cursor_position_top = PDEX_LAST_SHOWN - 7; pokedex_context->cursor_position_internal = (dexindex - pokedex_context->cursor_position_top); } m4aSongNumStart(601); fade_screen(0xFFFFFFFF, PDEX_FADEIN_SPD, 0, 16, 0x0000); break; case KEY_LEFT: if (pokedex_context->detail_form_position != 0) { pokedex_context->detail_form_position--; dexdetail_update_form(pokedex_context->detail_form_position); } break; case KEY_RIGHT: if (pokedex_context->detail_forms[pokedex_context->detail_form_position].type != FORM_END) { pokedex_context->detail_form_position++; dexdetail_update_form(pokedex_context->detail_form_position); } break; case KEY_START: pokecry_play(pdex_lazy_lookup_entry(currentIndex)->species, 0); break; case KEY_UP: if (pokedex_context->detail_scroll_amount > 0) { pokedex_context->detail_scroll_amount--; dexdetail_scroll_update(); } break; case KEY_DOWN: if (pokedex_context->detail_scroll_amount < pokedex_context->detail_scroll_max) { pokedex_context->detail_scroll_amount++; dexdetail_scroll_update(); } break; } break; case 12: if (!pal_fade_control.active) { task_del(tid); pdex_vram_free_bgmaps(); set_callback2(pdex_load); } break; default: break; } } struct SpritePalette dexdetail_type_pal = {&pokemon_type_chart_pal[0], DEX_DETAIL_TYPEPAL}; void dexdetail_load_gfx(void) { rbox_init_from_templates(&dexdetail_boxes[0]); lz77UnCompVram(pdexDetailBgTiles, (void *)0x0600C000); LZ77UnCompWram(pdexDetailBgMap, bgid_get_tilemap(2)); gpu_pal_apply_compressed(pdexDetailBgPal, 0, 32); gpu_pal_apply(pdex_text_pal, 15 * 16, 32); gpu_pal_obj_alloc_tag_and_apply(&dexdetail_type_pal); lcd_io_set(REG_ID_WIN0H, (8 << 8) | (232)); lcd_io_set(REG_ID_WIN0V, ((100 << 8) | (160))); lcd_io_set(REG_ID_WININ, WIN_BG0 | WIN_BG1 | WIN_BG2 | WIN_BG3 | WIN_OBJ); lcd_io_set(REG_ID_WINOUT, WIN_BG0 | WIN_BG2 | WIN_BG3 | WIN_OBJ); bgid_mod_y_offset(1, -24576, 1); bgid_mark_for_sync(0); } void dexdetail_load(void) { pdex_vram_setup(); dexdetail_load_gfx(); pokedex_context->state = 0; pokedex_context->detail_pokemon_oam = -1; pokedex_context->detail_type_oam[0] = -1; pokedex_context->detail_type_oam[1] = -1; task_add(dexdetail_loop, 0); set_callback2(pdex_cb_handler); }