From e87fa3b6ec2f02d07ce06e66ce7eb98cc0f1dd5c Mon Sep 17 00:00:00 2001 From: Lephenixnoir Date: Wed, 16 Feb 2022 14:54:26 +0100 Subject: [PATCH] GUI transition when entering level --- src/fixed.h | 3 ++- src/menu.c | 38 ++++++++++++++++++++++++++++++-------- src/render.c | 32 +++++++++++++++++++++----------- src/util.c | 8 ++++++++ src/util.h | 11 ++++++++--- 5 files changed, 69 insertions(+), 23 deletions(-) diff --git a/src/fixed.h b/src/fixed.h index 6b52f17..3d9651a 100644 --- a/src/fixed.h +++ b/src/fixed.h @@ -5,10 +5,11 @@ #pragma once #include -#include "util.h" typedef int32_t fixed_t; +#include "util.h" + /* Type of map coordinates */ typedef fixed_t map_coord_t; diff --git a/src/menu.c b/src/menu.c index 66dd07b..41af394 100644 --- a/src/menu.c +++ b/src/menu.c @@ -66,6 +66,9 @@ int menu_level_select(int start) int selection = (start >= 0 && start < OPTION_COUNT) ? start : 0; int target_x=0, x=0; + /* Time spent in the transition animation that moves the GUI items away + before starting the level. */ + fixed_t transition_time = fix(0.0); int volatile frame_tick = 1; int t = timer_configure(TIMER_ANY, 1000000 / FRAME_RATE, @@ -86,6 +89,14 @@ int menu_level_select(int start) x += dx; } + /* 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, + TRANSITION_LEN); + dclear(C_BLACK); for(int i = 0; i < OPTION_COUNT; i++) { @@ -99,15 +110,17 @@ int menu_level_select(int start) options[i]->camera.x = map_center - fix(local_offset) / TILE_WIDTH; menu_render_game(options[i]); - dprint_opt(DWIDTH/2 + local_offset, DHEIGHT - 24, C_WHITE, C_NONE, + dprint_opt(DWIDTH/2 + local_offset, NAME_Y, C_WHITE, C_NONE, DTEXT_CENTER, DTEXT_MIDDLE, "%s", options[i]->level->name); } - dimage(148, 9, &img_menu_title); + dimage(148, LOGO_Y, &img_menu_title); if(selection > 0) - dsubimage(16, 93, &img_menu_arrows, 0, 0, 35, 42, DIMAGE_NOCLIP); + dsubimage(ARROW1_X, 93, &img_menu_arrows, 0, 0, 35, 42, + DIMAGE_NONE); if(selection < OPTION_COUNT - 1) - dsubimage(345, 93, &img_menu_arrows, 35, 0, 35, 42, DIMAGE_NOCLIP); + dsubimage(ARROW2_X, 93, &img_menu_arrows, 35, 0, 35, 42, + DIMAGE_NONE); dupdate(); @@ -115,16 +128,25 @@ int menu_level_select(int start) menu_update_animations(options[i], dt); int key = getkey_opt(GETKEY_MENU, &frame_tick).key; - if(key == KEY_LEFT && selection > 0) { + if(transition_time <= fix(0.0) + && key == KEY_LEFT && selection > 0) { selection--; target_x += DWIDTH; } - if(key == KEY_RIGHT && selection < OPTION_COUNT - 1) { + if(transition_time <= fix(0.0) + && key == KEY_RIGHT && selection < OPTION_COUNT - 1) { selection++; target_x -= DWIDTH; } - if(key == KEY_EXE || key == KEY_SHIFT) - break; + + if(key == KEY_EXE || key == KEY_SHIFT) { + transition_time = dt; + } + else if(transition_time > fix(0.0)) { + transition_time += dt; + if(transition_time >= TRANSITION_LEN) + break; + } } timer_stop(t); diff --git a/src/render.c b/src/render.c index 5b36e4a..3de466f 100644 --- a/src/render.c +++ b/src/render.c @@ -297,7 +297,7 @@ static void render_entities(game_t const *g, camera_t const *camera, free(rendering_order); } -static void render_wave_info(game_t const *g) +static void render_wave_info(game_t const *g, int y0) { level_t const *level = g->level; level_wave_t const *wave = game_current_wave(g); @@ -305,7 +305,7 @@ static void render_wave_info(game_t const *g) for(int i = 0; i < wave->entry_count; i++) wave_enemies += wave->entries[i].amount; - dprint(2, 2, C_WHITE, "Wave %d (%d left)", + dprint(2, y0+2, C_WHITE, "Wave %d (%d left)", g->wave, wave_enemies - g->wave_spawned); if(g->wave + 1 > level->wave_count) @@ -314,7 +314,7 @@ static void render_wave_info(game_t const *g) level_wave_t const *next_wave = &level->waves[g->wave+1 - 1]; int x = DWIDTH - 2; - int y = 10; + int y = y0 + 10; for(int i = next_wave->entry_count - 1; i >= 0; i--) { enemy_t const *enemy = enemy_data(next_wave->entries[i].identity); @@ -419,18 +419,26 @@ void render_game(game_t const *g, bool show_hitboxes) ctx = prof_make(); prof_enter(ctx); + /* GUI positioning variables used during level entry */ + fixed_t MAX_GUI_TIME = fix(0.75); + fixed_t gui_time = MAX_GUI_TIME; + if(g->time_total < MAX_GUI_TIME) + gui_time = g->time_total; + int HUD_Y = cubic(DHEIGHT+30, DHEIGHT, gui_time, MAX_GUI_TIME); + int HEADER_Y = cubic(-15, 0, gui_time, MAX_GUI_TIME); + /* Render wave information */ - render_wave_info(g); + render_wave_info(g, HEADER_Y); /* Render HUD */ extern bopti_image_t img_hud; - dimage(0, DHEIGHT - img_hud.height, &img_hud); + dimage(0, HUD_Y - img_hud.height, &img_hud); extern font_t font_hud; dfont(&font_hud); - dprint_opt(349, DHEIGHT - 5, RGB24(0x15171a), C_NONE, DTEXT_CENTER, + dprint_opt(349, HUD_Y - 5, RGB24(0x15171a), C_NONE, DTEXT_CENTER, DTEXT_TOP, "%d", g->player_data->xp_level); - dprint_opt(349, DHEIGHT - 6, RGB24(0xabb1ba), C_NONE, DTEXT_CENTER, + dprint_opt(349, HUD_Y - 6, RGB24(0xabb1ba), C_NONE, DTEXT_CENTER, DTEXT_TOP, "%d", g->player_data->xp_level); dfont(&font_rogue); @@ -439,21 +447,21 @@ void render_game(game_t const *g, bool show_hitboxes) /* Render life bar */ extern bopti_image_t img_hud_life; int fill_height = (img_hud_life.height * player_f->HP) / player_f->HP_max; - dsubimage(184, DHEIGHT - 5 - fill_height, &img_hud_life, + dsubimage(184, HUD_Y - 5 - fill_height, &img_hud_life, 0, img_hud_life.height - fill_height, img_hud_life.width, fill_height, DIMAGE_NONE); /* Render XP bar. The following values indicate the geometry of the XP bar so that we can show a partially-filled version of it */ if(anim_in(g->hud_xp_anim.frame, &anims_hud_xp_Explode, 0)) { - anim_frame_render(343, DHEIGHT-32, g->hud_xp_anim.frame); + anim_frame_render(343, HUD_Y-32, g->hud_xp_anim.frame); } else { static int const XP_FULL=22; int xp_current = g->player_data->xp_current; int xp_total = max(g->player_data->xp_to_next_level, 1); int fill_height = XP_FULL * xp_current / xp_total; - anim_frame_subrender(343, DHEIGHT-32, g->hud_xp_anim.frame, + anim_frame_subrender(343, HUD_Y-32, g->hud_xp_anim.frame, 0, XP_FULL - fill_height, -1, fill_height); } @@ -468,7 +476,7 @@ void render_game(game_t const *g, bool show_hitboxes) fixed_t cooldown_remaining = player_f->actions_cooldown[i+1]; int x = 31 + 48*i + 64*(i>=3); - int y = DHEIGHT - 33; + int y = HUD_Y - 33; int bg = (cooldown_remaining != 0); dsubimage(x+2, y+2, &img_skillicons, skill_size * bg, 0, skill_size, skill_size, DIMAGE_NONE); @@ -480,6 +488,8 @@ void render_game(game_t const *g, bool show_hitboxes) int height = (cooldown_remaining*skill_box_size) / cooldown_total; int ymin = y + skill_box_size - height; int ymax = y + skill_box_size; + if(ymin >= DHEIGHT) ymin = DHEIGHT; + if(ymax >= DHEIGHT) ymax = DHEIGHT; for(int y1 = ymin; y1 < ymax; y1++) { for(int x1 = x; x1 < x + skill_box_size; x1++) { int i = DWIDTH * y1 + x1; diff --git a/src/util.c b/src/util.c index bcece70..ac00017 100644 --- a/src/util.c +++ b/src/util.c @@ -209,3 +209,11 @@ void font_damage_print(int x, int y, int color, int align_x, int align_y, x += char_w; } } + +int cubic(int start, int end, fixed_t t, fixed_t tmax) +{ + t = fdiv(t, tmax); + fixed_t x = fix(1.0) - fmul(fmul(fix(1.0)-t, fix(1.0)-t), fix(1.0)-t); + x = fix(start) + fmul(fix(end - start), x); + return fround(x); +} diff --git a/src/util.h b/src/util.h index b92f0ca..cbf7904 100644 --- a/src/util.h +++ b/src/util.h @@ -8,6 +8,11 @@ #include #include +/* Integer square root (recursive, logarithmic complexity). */ +int64_t sqrtll(int64_t n); + +#include "fixed.h" + // Heaps /* Queue objects are passed by value in this API. */ @@ -47,9 +52,6 @@ void heap_sort(void *base, size_t n, size_t elsize, // Other utils -/* Integer square root (recursive, logarithmic complexity). */ -int64_t sqrtll(int64_t n); - /* Round F-key. */ void fkey_button(int position, char const *text, int color); @@ -64,3 +66,6 @@ void font_damage_print(int x, int y, int color, int align_x, int align_y, (((hex & 0xf80000) >> 8) | \ ((hex & 0x00fc00) >> 5) | \ ((hex & 0x0000f8) >> 3)) + +/* Cubic transition from [start] to [end] in total time [tmax] */ +int cubic(int start, int end, fixed_t t, fixed_t tmax);