#include #include #include #include #include #include #include #include #define BG_COLOR 0x869c #define CIRCLE_COLOR 0xcf1c #define NB_CIRCLE 9 #define NB_BUTTON_ROW 8 #define NB_BUTTON_COL 5 #define NB_BUTTON_TOTAL (NB_BUTTON_ROW * NB_BUTTON_COL) /* define internal circle information structure */ struct circle { int x; int y; int size; } circle[NB_CIRCLE] = { { .x = 42, .y = 163, .size = 35 }, { .x = 72, .y = 31, .size = 55 }, { .x = 223, .y = 239, .size = 60 }, { .x = 265, .y = 109, .size = 51 }, { .x = 358, .y = 0, .size = 34 }, { .x = 396, .y = 185, .size = 50 }, { .x = 506, .y = 83, .size = 65 }, { .x = 587, .y = 230, .size = 53 }, { .x = 606, .y = 38, .size = 27 }, }; //--- // Internal functions //--- /* menu_background() : update and draw circle */ static void menu_background(void) { for (int i = 0; i < NB_CIRCLE; ++i) { circle[i].x += 1; if ((circle[i].x - circle[i].size) >= (int)dwidth()) circle[i].x -= (dwidth() + (circle[i].size << 1)); dcircle_filled( circle[i].x, circle[i].y, circle[i].size, DCIRCLE_CENTER | DCIRCLE_MIDDLE, CIRCLE_COLOR ); } } /* title menu */ /* menu_title_background() : display circle */ static void menu_title_background(void) { menu_background(); } /* menu_icon() : display the game icon */ static void menu_title_icon(void) { extern const image_t gduck_menu_title; did_t did; did = dimage( &gduck_menu_title, dwidth() / 2, (dheight() / 2) - (dheight() / 8), DIMAGE_CENTER | DIMAGE_MIDDLE ); dimage_shader_shadow(did, -4, 4); } /* menu_hub() : display HUD information */ static void menu_title_hud(fps_t *fps, int trigger) { dprint(0, 0, C_WHITE, "FPS: %d", fps->fps); if (trigger != 0) { dtext_opt( dwidth() / 2, (dheight() / 2) + (dheight() / 4), C_WHITE, C_NONE, DTEXT_CENTER | DTEXT_MIDDLE, "Press [SHIFT] to start !", -1 ); } } /* menu_text_blink() : change trigger status each 500ms */ static int menu_title_text_blink(volatile int *trigger) { *trigger ^= 1; return TIMER_CONTINUE; } /* selection menu */ /* menu_select_background() : display circle */ static void menu_select_background(void) { menu_background(); } /* menu_select_ui() : display button selection */ static void menu_select_ui(int max_level, int cursor) { extern const image_t gduck_menu_select_title; extern const image_t gduck_menu_select_button_bg; extern const image_t gduck_menu_select_button_0; extern const image_t gduck_menu_select_button_1; extern const image_t gduck_menu_select_button_2; const image_t *image; int counter; int imgx; int imgy; int imgs; dimage( &gduck_menu_select_title, dwidth() / 2, dheight() / 6, DIMAGE_CENTER | DIMAGE_MIDDLE ); dimage( &gduck_menu_select_button_bg, dwidth() / 2, (dheight() / 2) + (dheight() / 8), DIMAGE_CENTER | DIMAGE_MIDDLE ); imgx = (NB_BUTTON_ROW * gduck_menu_select_button_0.width); imgx += (NB_BUTTON_ROW - 1); imgx = (dwidth() / 2) - (imgx / 2); imgx += (gduck_menu_select_button_0.width / 2); imgy = (NB_BUTTON_COL * gduck_menu_select_button_0.height); imgy += (NB_BUTTON_COL - 1); imgy = ((dheight() / 2) + (dheight() / 8)) - (imgy / 2); imgy += (gduck_menu_select_button_0.height / 2); imgs = imgx; counter = 0; for (int y = 0; y < NB_BUTTON_COL; ++y) { imgx = imgs; for (int x = 0; x < NB_BUTTON_ROW; ++x) { image = &gduck_menu_select_button_0; if (counter == cursor) image = &gduck_menu_select_button_1; if (counter >= max_level) image = &gduck_menu_select_button_2; dimage( image, imgx, imgy, DIMAGE_CENTER | DIMAGE_MIDDLE ); if (image != &gduck_menu_select_button_2) { dprint_opt( imgx - 1, imgy + 1, C_WHITE, C_NONE, DTEXT_CENTER | DTEXT_MIDDLE, "%d", counter + 1 ); } imgx += image->width + 1; counter += 1; } imgy += image->height + 1; } } /* menu_select_hud() : display HUB information */ static void menu_select_hud(fps_t *fps) { dprint(0, 0, C_WHITE, "FPS: %d (%d us)", fps->fps, fps->render); } //--- // Public function //---- /* menu_title() : display logo/animation and wait user interaction */ void menu_title(void) { volatile int trigger; vkey_event_t e; fps_t fps; int exit; int timer; trigger = 1; timer = timer_configure( TIMER_DELAY_MS(500), VHEX_CALL(&menu_title_text_blink, &trigger) ); timer_start(timer); uint32_t cache_us = 0; uint32_t render_us = 0; uint32_t key_us = 0; exit = 1; timer_fps_init(&fps, 30); while (exit) { cache_us = timer_prof_exec({ dclear(BG_COLOR); menu_title_background(); menu_title_icon(); menu_title_hud(&fps, trigger); dprint(0, 20, C_WHITE, "cache = %d us\nrender = %d us\nkey = %d us", cache_us, render_us, key_us ); }); render_us = timer_prof_exec({ dupdate(); }); timer_fps_sync(&fps); key_us = timer_prof_exec({ while (keyvent_poll(&e)) { if (e.type == VKEYEV_NONE) break; if (e.type != VKEYEV_UP) continue; if (e.key == VKEY_SHIFT) exit = 0; } }); } timer_fps_quit(&fps); timer_stop(timer); } /* menu_select() : level selection */ int menu_select(int max_level) { vkey_event_t e; fps_t fps; int cursor; int exit; exit = -1; cursor = 0; timer_fps_init(&fps, 30); while (exit < 0) { dclear(BG_COLOR); menu_select_background(); menu_select_ui(max_level, cursor); menu_select_hud(&fps); dupdate(); timer_fps_sync(&fps); while (keyvent_poll(&e)) { if (e.type != VKEYEV_UP) continue; if (e.key == VKEY_LEFT) cursor -= 1; if (e.key == VKEY_RIGHT) cursor += 1; if (e.key == VKEY_DOWN) cursor += NB_BUTTON_ROW; if (e.key == VKEY_UP) cursor -= NB_BUTTON_ROW; if (cursor < 0) cursor = 0; if (cursor >= max_level) cursor = max_level - 1; if (e.key == VKEY_SHIFT) exit = cursor; } } timer_fps_quit(&fps); return (cursor); }