vxGDuck/src/menu.c

287 lines
5.9 KiB
C

#include <vhex/display.h>
#include <vhex/display/draw/circle.h>
#include <vhex/display/draw/pixel.h>
#include <vhex/display/image.h>
#include <vhex/timer.h>
#include <vhex/timer/fps.h>
#include <vhex/timer/profiling.h>
#include <vhex/keyboard.h>
#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);
}