diff --git a/assets-cg/font_rogue.png b/assets-cg/font_rogue.png index cc77b1d..129d6d1 100644 Binary files a/assets-cg/font_rogue.png and b/assets-cg/font_rogue.png differ diff --git a/assets-cg/hud_small.png b/assets-cg/hud_small.png index 3be3dd0..3c3638e 100644 Binary files a/assets-cg/hud_small.png and b/assets-cg/hud_small.png differ diff --git a/src/menu.c b/src/menu.c index 25f0048..23134e3 100644 --- a/src/menu.c +++ b/src/menu.c @@ -8,6 +8,7 @@ #include #include #include +#include typedef struct { /* Same mechanism as in game_t */ @@ -50,24 +51,50 @@ static void menu_render_game(menu_game_t *mg) DIMAGE_NONE); } +static void darken_vram(void) +{ + int top_margin = 0; // 24; + int bottom_margin = 0; // 32; + + uint32_t *v = (void *)(gint_vram + top_margin * DWIDTH); + int N = DWIDTH * (DHEIGHT - top_margin - bottom_margin) / 2; + + for(int i = 0; i < N; i++) +// v[i] = (v[i] & 0xf7def7de) >> 1; + v[i] = (v[i] & 0xe79ce79c) >> 2; +} + static void menu_update_animations(menu_game_t *mg, fixed_t dt) { for(int i = 0; i < mg->map->width * mg->map->height; i++) mg->map_anim[i] += fround(dt * 1000); } +static void dsprint(int x, int y, int fg1, int fg2, char const *fmt, ...) +{ + va_list args; + char str[256]; + va_start(args, fmt); + vsnprintf(str, 256, fmt, args); + va_end(args); + + dtext(x, y+1, fg2, str); + dtext(x, y, fg1, str); +} + int menu_level_select(int start) { - extern bopti_image_t img_menu_title, img_menu_arrows; + extern bopti_image_t img_menu_title; menu_game_t *options[LEVEL_COUNT]; - #define OPTION_COUNT LEVEL_COUNT + /* Don't show the sandbox in the menu; ALPHA+EXE to enter it instead */ + #define OPTION_COUNT (LEVEL_COUNT-1) for(int i = 0; i < LEVEL_COUNT; i++) options[i] = menu_load_game(level_all[i]); int selection = (start >= 0 && start < OPTION_COUNT) ? start : 0; - int target_x=0, x=0; + int target_y=0, y=0; /* Time spent in the transition animation that moves the GUI items away before starting the level. */ fixed_t transition_time = fix(0.0); @@ -82,21 +109,20 @@ int menu_level_select(int start) sleep(); fixed_t dt = fix(1) / FRAME_RATE; - if(x != target_x) { - int dx = (target_x - x) / 6; - if(x < target_x && dx < 6) - dx = min(target_x-x, 6); - if(x > target_x && dx > -6) - dx = max(target_x-x, -6); - x += dx; + if(y != target_y) { + int dy = (target_y - y) / 4; + if(y < target_y && dy < 6) + dy = min(target_y-y, 6); + if(y > target_y && dy > -6) + dy = max(target_y-y, -6); + y += dy; } /* GUI positioning variables, accounting for the final transition */ fixed_t TRANSITION_LEN = fix(0.75); - int LOGO_Y = cubic(9, 9-85, transition_time, TRANSITION_LEN); - int ARROW1_X = cubic(16, 16-50, transition_time, TRANSITION_LEN); - int ARROW2_X = cubic(345, 345+50, transition_time, TRANSITION_LEN); - int NAME_Y = cubic(DHEIGHT-24, DHEIGHT+10, transition_time, + int LOGO_Y = cubic(5, 5-80, transition_time, TRANSITION_LEN); + int LEVELS_X = cubic(40, 40-200, transition_time, TRANSITION_LEN); + int HISCORE_X = cubic(DWIDTH-80, DWIDTH, transition_time, TRANSITION_LEN); extern font_t font_rogue; @@ -105,27 +131,37 @@ int menu_level_select(int start) dclear(C_BLACK); for(int i = 0; i < OPTION_COUNT; i++) { - if(-x <= (i-1)*DWIDTH) + if(-y <= (i-1)*DHEIGHT) continue; - if(-x >= (i+1)*DWIDTH) + if(-y >= (i+1)*DHEIGHT) continue; - int local_offset = x + i*DWIDTH; - fixed_t map_center = fix(options[i]->map->width) / 2; - options[i]->camera.x = map_center - fix(local_offset) / TILE_WIDTH; + int local_offset = y + i*DHEIGHT; + fixed_t map_center = fix(options[i]->map->height) / 2; + options[i]->camera.y = + map_center - fix(local_offset + 2) / TILE_HEIGHT; menu_render_game(options[i]); - - dprint_opt(DWIDTH/2 + local_offset, NAME_Y, C_WHITE, C_NONE, - DTEXT_CENTER, DTEXT_MIDDLE, "%s", options[i]->level->name); } + if(transition_time == 0) + darken_vram(); dimage(148, LOGO_Y, &img_menu_title); - if(selection > 0) - dsubimage(ARROW1_X, 93, &img_menu_arrows, 0, 0, 35, 42, - DIMAGE_NONE); - if(selection < OPTION_COUNT - 1) - dsubimage(ARROW2_X, 93, &img_menu_arrows, 35, 0, 35, 42, - DIMAGE_NONE); + render_small_text(LEVELS_X, 80, C_WHITE, "PLAY", -1); + render_small_text(HISCORE_X, 80, C_WHITE, "HIGH SCORE", -1); + for(int i = 0; i < OPTION_COUNT; i++) { + dsprint(LEVELS_X, 95+16*i, + (i == selection) ? C_WHITE : C_RGB(20, 20, 20), C_BLACK, + "%s%s", + (i == selection) ? "> " : "", options[i]->level->name); + // TODO: Wire high score to main menu + dsprint(HISCORE_X, 95+16*i, + (i == selection) ? C_WHITE : C_RGB(20, 20, 20), C_BLACK, + "%03d", 0); + } + render_small_text(LEVELS_X, 180, C_WHITE, "OTHER", -1); + dsprint(LEVELS_X, 195, + (selection == OPTION_COUNT) ? C_WHITE : C_RGB(20, 20, 20), C_BLACK, + (selection == OPTION_COUNT) ? "> Credits" : "Credits"); dfont(old_font); dupdate(); @@ -144,17 +180,19 @@ int menu_level_select(int start) usb_fxlink_videocapture(false); } - if(transition_time <= fix(0.0) - && key == KEY_LEFT && selection > 0) { + if(key == KEY_UP && selection > 0) { selection--; - target_x += DWIDTH; + target_y += DHEIGHT; } - if(transition_time <= fix(0.0) - && key == KEY_RIGHT && selection < OPTION_COUNT - 1) { + if(key == KEY_DOWN && selection < OPTION_COUNT) { selection++; - target_x -= DWIDTH; + target_y -= DHEIGHT; } + if(key == KEY_EXE && keydown(KEY_ALPHA)) { + selection = LEVEL_COUNT - 1; + break; + } if((key == KEY_EXE || key == KEY_SHIFT) && transition_time == 0) { transition_time = dt; } diff --git a/src/render.c b/src/render.c index 750cb15..501f6bf 100644 --- a/src/render.c +++ b/src/render.c @@ -448,31 +448,27 @@ static void anim_frame_render_outline(int x, int y, anim_frame_t const *frame, frame->x, frame->y, frame->w, frame->h, color); } -static int small_text(int x, int y, int color, char const *text, int size) +int render_small_text(int x, int y, int color, char const *text, int size) { extern bopti_image_t img_hud_small; + (void)color; + if(size < 0) size = strlen(text); - if(!strncmp(text, "SHIFT", size)) { - dsubimage(x-1, y+3, &img_hud_small, 0, 0, 27, 8, DIMAGE_NONE); - return 27-1; - } - if(!strncmp(text, "HP", size)) { - dsubimage(x-1, y+3, &img_hud_small, 0, 8, 12, 8, DIMAGE_NONE); - return 12-1; - } - if(!strncmp(text, "ATK", size)) { - dsubimage(x-1, y+3, &img_hud_small, 0, 16, 17, 8, DIMAGE_NONE); - return 17-1; - } - if(!strncmp(text, "MAG", size)) { - dsubimage(x-1, y+3, &img_hud_small, 0, 24, 19, 8, DIMAGE_NONE); - return 19-1; - } - if(!strncmp(text, "DEF", size)) { - dsubimage(x-1, y+3, &img_hud_small, 0, 32, 18, 8, DIMAGE_NONE); - return 18-1; + static char const *texts[] = { + "SHIFT", "HP", "ATK", "MAG", "DEF", "PLAY", "HIGH SCORE", "OTHER" + }; + static int const widths[] = { + 27, 12, 17, 19, 18, 22, 51, 28 + }; + + for(size_t i = 0; i < sizeof texts / sizeof texts[0]; i++) { + if(!strncmp(text, texts[i], size)) { + dsubimage(x-1, y+3, &img_hud_small, 0, 8*i, widths[i], 8, + DIMAGE_NONE); + return widths[i] - 1; + } } return 0; } @@ -508,12 +504,12 @@ static void dtext_multi(int x0, int y, int color, char const *str) while(*str) { if(!strncmp(str, "HP", 2)) { - x += small_text(x, y, color, str, 2) + 1; + x += render_small_text(x, y, color, str, 2) + 1; str += 2; } else if(!strncmp(str, "ATK", 3) || !strncmp(str, "MAG", 3) || !strncmp(str, "DEF", 3)) { - x += small_text(x, y, color, str, 3) + 1; + x += render_small_text(x, y, color, str, 3) + 1; str += 3; } else if(*str == '\n') { @@ -704,7 +700,7 @@ void render_game(game_t const *g, bool show_hitboxes) if(desc) dtext_multi(x1+7, 123, C_WHITE, desc); - int dx = small_text(x1+7, 156, -1, "SHIFT", -1); + int dx = render_small_text(x1+7, 156, -1, "SHIFT", -1); char const *use_str = "Use"; if(item_is_equip(selected_item)) { use_str = "Equip"; @@ -728,16 +724,16 @@ void render_game(game_t const *g, bool show_hitboxes) switched_equipment[selected_slot] = g->menu_cursor; growth_equip = player_compute_growth(g->player, switched_equipment); - small_text(x2+14, 100, C_WHITE, "HP", -1); + render_small_text(x2+14, 100, C_WHITE, "HP", -1); print_stat_opt(x2+44, 100, growth_equip.HP, growth_base.HP, "%d/%d", player_f->HP, player_f->HP_max); - small_text(x2+14, 114, C_WHITE, "ATK", -1); + render_small_text(x2+14, 114, C_WHITE, "ATK", -1); print_stat(x2+44, 114, player_f->ATK, growth_equip.ATK, growth_base.ATK); - small_text(x2+14, 128, C_WHITE, "MAG", -1); + render_small_text(x2+14, 128, C_WHITE, "MAG", -1); print_stat(x2+44, 128, player_f->MAG, growth_equip.MAG, growth_base.MAG); - small_text(x2+14, 142, C_WHITE, "DEF", -1); + render_small_text(x2+14, 142, C_WHITE, "DEF", -1); print_stat(x2+44, 142, player_f->DEF, growth_equip.DEF, growth_base.DEF); dfont(old_font); diff --git a/src/render.h b/src/render.h index ee54967..64eec65 100644 --- a/src/render.h +++ b/src/render.h @@ -71,3 +71,6 @@ void render_game(struct game const *g, bool show_hitboxes); /* Render pathfinding results on the grid. */ void render_pfg_all2one(pfg_all2one_t const *paths, camera_t const *c, uint8_t *occupation); + +/* Render one of some predefined short strings using an image. */ +int render_small_text(int x, int y, int color, char const *text, int size);