This repository has been archived on 2022-05-21. You can view files and clone it, but cannot push or open issues or pull requests.
LEFTONE/core/loui/menu.c

367 lines
10 KiB
C
Raw Permalink Normal View History

#include "./menu.h"
#include <assert.h>
#include <stddef.h>
#include "util/glyphas/block.h"
#include "util/glyphas/cache.h"
#include "util/math/constant.h"
#include "util/math/vector.h"
#include "core/locommon/easing.h"
#include "core/locommon/input.h"
#include "core/locommon/screen.h"
#include "core/locommon/ticker.h"
#include "core/loplayer/player.h"
#include "core/loplayer/stance.h"
#include "core/loresource/set.h"
#include "core/loresource/text.h"
#include "core/loshader/set.h"
#define FADE_SPEED_ 2
#define STANCE_TEXT_LINEHEIGHT_ 1.5f
#define STANCE_TEXT_MAX_WIDTH_ 3.5f /* inch */
#define STANCE_NAME_RESERVE_ 32
#define STANCE_DESC_RESERVE_ 256
#define STANCE_NOTE_RESERVE_ 256
#define STANCE_NAME_COLOR_ vec4(1, 1, 1, 1)
#define STANCE_DESC_COLOR_ vec4(1, 1, 1, 1)
#define STANCE_NOTE_COLOR_ vec4(.8f, .8f, 0, 1)
#define STANCE_ICON_HIGHLIGHT_SPEED_ 5
#define STANCE_ICON_HIGHLIGHT_SIZE_ .2f
static void loui_menu_calc_geometry_(
loui_menu_t* menu, const locommon_screen_t* screen) {
assert(menu != NULL);
assert(locommon_screen_valid(screen));
typeof(menu->geo)* g = &menu->geo;
# define calc_font_geo_(name, sz) do { \
locommon_screen_calc_pixels_from_inch( \
screen, &g->name##_fontpx, sz); \
locommon_screen_calc_winpos_from_pixels( \
screen, &g->name##_fontsz, &g->big_fontpx); \
} while (0)
calc_font_geo_(big, &vec2(.20f, .20f));
calc_font_geo_(normal, &vec2(.16f, .16f));
calc_font_geo_(small, &vec2(.12f, .12f));
# undef calc_font_geo_
locommon_screen_calc_winpos_from_inch(
screen, &g->stance_icon_interval, &vec2(2, 2));
locommon_screen_calc_winpos_from_inch(
screen, &g->stance_iconsz, &vec2(.3f, .3f));
}
static void loui_menu_initialize_stance_(
loui_menu_t* menu,
loui_menu_stance_t* stance,
const locommon_screen_t* screen,
const char* name,
const char* desc,
const char* note) {
assert(menu != NULL);
assert(stance != NULL);
assert(name != NULL);
assert(desc != NULL);
assert(note != NULL);
assert(locommon_screen_valid(screen));
*stance = (typeof(*stance)) {0};
const float theta =
(stance - menu->stances)*1.f/LOPLAYER_STANCE_COUNT*2*MATH_PI +
MATH_PI/2;
stance->pos.x = cos(theta) * menu->geo.stance_icon_interval.x;
stance->pos.y = sin(theta) * menu->geo.stance_icon_interval.y;
const float width = screen->dpi.x*STANCE_TEXT_MAX_WIDTH_;
const vec2_t invreso = vec2(2/screen->resolution.x, 2/screen->resolution.y);
stance->name = glyphas_block_new(
GLYPHAS_ALIGNER_DIRECTION_HORIZONTAL,
-menu->geo.big_fontpx.y*STANCE_TEXT_LINEHEIGHT_,
INT32_MAX,
STANCE_NAME_RESERVE_);
glyphas_block_add_characters(
stance->name,
menu->font.sans.big,
&STANCE_NAME_COLOR_,
name,
strlen(name));
glyphas_block_scale(stance->name, &invreso);
stance->desc = glyphas_block_new(
GLYPHAS_ALIGNER_DIRECTION_HORIZONTAL,
-menu->geo.normal_fontpx.y*STANCE_TEXT_LINEHEIGHT_,
width,
STANCE_DESC_RESERVE_);
glyphas_block_add_characters(
stance->desc,
menu->font.sans.normal,
&STANCE_DESC_COLOR_,
desc,
strlen(desc));
glyphas_block_scale(stance->desc, &invreso);
stance->note = glyphas_block_new(
GLYPHAS_ALIGNER_DIRECTION_HORIZONTAL,
-menu->geo.small_fontpx.y,
width,
STANCE_NOTE_RESERVE_);
glyphas_block_add_characters(
stance->note,
menu->font.serif.small,
&STANCE_NOTE_COLOR_,
note,
strlen(note));
glyphas_block_scale(stance->note, &invreso);
glyphas_block_set_origin(stance->name, &vec2(.5f, 0));
glyphas_block_set_origin(stance->desc, &vec2(.5f, 0));
glyphas_block_set_origin(stance->note, &vec2(.5f, 0));
vec2_t dummy;
vec2_t namesz;
glyphas_block_calculate_geometry(stance->name, &dummy, &namesz);
vec2_t descsz;
glyphas_block_calculate_geometry(stance->desc, &dummy, &descsz);
vec2_t notesz;
glyphas_block_calculate_geometry(stance->note, &dummy, &notesz);
const float note_padding = menu->geo.small_fontsz.y;
float y = (namesz.y + descsz.y + note_padding + notesz.y)/2;
glyphas_block_translate(stance->name, &vec2(0, y));
y -= namesz.y;
glyphas_block_translate(stance->desc, &vec2(0, y));
y -= descsz.y + note_padding;
glyphas_block_translate(stance->note, &vec2(0, y));
}
static void loui_menu_deinitialize_stance_(loui_menu_stance_t* stance) {
assert(stance != NULL);
glyphas_block_delete(stance->name);
glyphas_block_delete(stance->desc);
glyphas_block_delete(stance->note);
}
static glyphas_block_t* loui_menu_create_exit_text_(
loui_menu_t* menu, const locommon_screen_t* screen, const char* text) {
assert(menu != NULL);
assert(screen != NULL);
assert(text != NULL);
glyphas_block_t* b = glyphas_block_new(
GLYPHAS_ALIGNER_DIRECTION_HORIZONTAL,
-menu->geo.normal_fontpx.y,
INT32_MAX,
32);
glyphas_block_add_characters(
b, menu->font.sans.normal, &vec4(1, 1, 1, 1), text, strlen(text));
glyphas_block_scale(
b, &vec2(2.f/screen->resolution.x, 2.f/screen->resolution.y));
glyphas_block_set_origin(b, &vec2(.5f, -1));
glyphas_block_translate(b, &vec2(0, -1));
return b;
}
static loui_menu_stance_t* loui_menu_find_stance_icon_at_(
loui_menu_t* menu, const vec2_t* pos) {
assert(menu != NULL);
assert(vec2_valid(pos));
for (size_t i = 0; i < LOPLAYER_STANCE_COUNT; ++i) {
vec2_t p;
vec2_sub(&p, pos, &menu->stances[i].pos);
p.x /= menu->geo.stance_iconsz.x;
p.y /= menu->geo.stance_iconsz.y;
if (fabsf(p.x) + fabsf(p.y) < 1) {
return &menu->stances[i];
}
}
return NULL;
}
static void loui_menu_update_stances_(
loui_menu_t* menu, const locommon_input_t* input) {
assert(menu != NULL);
const float dt = menu->ticker->delta_f;
menu->hovered_stance =
input == NULL? NULL:
loui_menu_find_stance_icon_at_(menu, &input->cursor);
for (size_t i = 0; i < LOPLAYER_STANCE_COUNT; ++i) {
loui_menu_stance_t* s = &menu->stances[i];
locommon_easing_linear_float(
&s->highlight,
s == menu->hovered_stance,
dt*STANCE_ICON_HIGHLIGHT_SPEED_);
}
}
static void loui_menu_draw_stance_icons_(const loui_menu_t* menu) {
assert(menu != NULL);
for (size_t i = 0; i < LOPLAYER_STANCE_COUNT; ++i) {
const loui_menu_stance_t* s = &menu->stances[i];
const loshader_menu_stance_id_t id =
loplayer_stance_set_has(&menu->player->stances, i)?
loplayer_stance_get_menu_shader_id(i):
LOSHADER_MENU_STANCE_ID_EMPTY;
vec2_t sz = menu->geo.stance_iconsz;
vec2_muleq(&sz, 1+powf(s->highlight, 2.f)*STANCE_ICON_HIGHLIGHT_SIZE_);
loshader_menu_stance_drawer_add_instance(
&menu->shaders->drawer.menu_stance,
&(loshader_menu_stance_drawer_instance_t) {
.id = id,
.pos = s->pos,
.size = sz,
.alpha = menu->alpha,
});
}
}
static void loui_menu_draw_stance_text_(const loui_menu_t* menu) {
assert(menu != NULL);
if (menu->hovered_stance == NULL) return;
const size_t index = menu->hovered_stance - menu->stances;
const loui_menu_stance_t* s = menu->hovered_stance;
if (!loplayer_stance_set_has(&menu->player->stances, index)) {
s = &menu->stances[LOPLAYER_STANCE_COUNT];
}
loshader_menu_text_drawer_add_block(
&menu->shaders->drawer.menu_text, s->name);
loshader_menu_text_drawer_add_block(
&menu->shaders->drawer.menu_text, s->desc);
loshader_menu_text_drawer_add_block(
&menu->shaders->drawer.menu_text, s->note);
}
void loui_menu_initialize(
loui_menu_t* menu,
loresource_set_t* res,
loshader_set_t* shaders,
const locommon_screen_t* screen,
const locommon_ticker_t* ticker,
loplayer_t* player) {
assert(menu != NULL);
assert(res != NULL);
assert(shaders != NULL);
assert(ticker != NULL);
assert(player != NULL);
assert(locommon_screen_valid(screen));
*menu = (typeof(*menu)) {
.shaders = shaders,
.ticker = ticker,
.player = player,
};
loui_menu_calc_geometry_(menu, screen);
menu->font = (typeof(menu->font)) {
.sans = {
.big = glyphas_cache_new(
shaders->tex.menu_text,
&res->font.sans,
menu->geo.big_fontpx.x,
menu->geo.big_fontpx.y),
.normal = glyphas_cache_new(
shaders->tex.menu_text,
&res->font.sans,
menu->geo.normal_fontpx.x,
menu->geo.normal_fontpx.y),
},
.serif = {
.small = glyphas_cache_new(
shaders->tex.menu_text,
&res->font.serif,
menu->geo.small_fontpx.x,
menu->geo.small_fontpx.y),
},
};
# define text_(v) loresource_text_get(res->lang, v)
# define each_(NAME, name) \
loui_menu_initialize_stance_( \
menu, \
&menu->stances[LOPLAYER_STANCE_##NAME], \
screen, \
text_("stance_"#name"_name"), \
text_("stance_"#name"_desc"), \
text_("stance_"#name"_note"))
LOPLAYER_STANCE_EACH(each_);
# undef each_
loui_menu_initialize_stance_(
menu, &menu->stances[LOPLAYER_STANCE_COUNT],
screen,
text_("stance_unknown_name"),
text_("stance_unknown_desc"),
text_("stance_unknown_note"));
menu->exit_text = loui_menu_create_exit_text_(
menu, screen, text_("menu_exit"));
# undef text_
}
void loui_menu_deinitialize(loui_menu_t* menu) {
assert(menu != NULL);
for (size_t i = 0; i < LOPLAYER_STANCE_COUNT+1; ++i) {
loui_menu_deinitialize_stance_(&menu->stances[i]);
}
glyphas_block_delete(menu->exit_text);
glyphas_cache_delete(menu->font.sans.big);
glyphas_cache_delete(menu->font.sans.normal);
glyphas_cache_delete(menu->font.serif.small);
}
void loui_menu_update(loui_menu_t* menu, const locommon_input_t* input) {
assert(menu != NULL);
loui_menu_update_stances_(menu, input);
menu->request_exit =
input != NULL &&
input->buttons & LOCOMMON_INPUT_BUTTON_OK &&
input->cursor.y < -1+menu->geo.normal_fontsz.y;
}
void loui_menu_draw(const loui_menu_t* menu) {
assert(menu != NULL);
if (menu->alpha <= 0) return;
menu->shaders->drawer.menu_text.alpha = menu->alpha;
menu->shaders->drawer.menu_background.alpha = menu->alpha;
loui_menu_draw_stance_icons_(menu);
loui_menu_draw_stance_text_(menu);
loshader_menu_text_drawer_add_block(
&menu->shaders->drawer.menu_text, menu->exit_text);
}