diff options
| author | Menase Teshale <osome3717@gmail.com> | 2026-09-07 03:14:22 +0300 |
|---|---|---|
| committer | Menase Teshale <osome3717@gmail.com> | 2026-09-07 03:17:14 +0300 |
| commit | a03a982c026a185d617d25e6a37ce939a481e946 (patch) | |
| tree | d787eb5cb59f57d81d161470885fde42f1636a6c | |
| parent | 7b407b8083268e982cbc80b6c1f969dcdc6dec8d (diff) | |
Started work on menus
| -rw-r--r-- | CMakeLists.txt | 1 | ||||
| -rw-r--r-- | src/app.h | 5 | ||||
| -rw-r--r-- | src/font.c | 32 | ||||
| -rw-r--r-- | src/game.c | 54 | ||||
| -rw-r--r-- | src/main.c | 15 | ||||
| -rw-r--r-- | src/menu.c | 108 |
6 files changed, 188 insertions, 27 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 56a8b13..685ec7d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,6 +14,7 @@ add_executable(game "src/main.c" "src/game.c" "src/font.c" + "src/menu.c" ) target_link_libraries(game PRIVATE @@ -18,4 +18,9 @@ void update_game(float dt); void draw_game(SDL_Renderer* renderer); void game_event(SDL_Event* e); +void init_menu(SDL_Renderer* renderer); +void free_menu(); +void draw_menu(SDL_Renderer* renderer); +SDL_AppResult menu_event(SDL_Event* e); + #endif // APP_H_ @@ -326,6 +326,30 @@ bool load_sprite_font(SDL_Renderer* renderer, const char* filename, sprite_font* return true; } +void measure_text +( + sprite_font* font, + const unsigned char* text, + float scale, + float* width, float* height +) { + *width = 0; + *height = 0; + + while (*text) { + const font_char* fc = &font->characters[*text-font->smallest_character]; + if (!fc->active) { + text++; + continue; + } + + *width += fc->x_advance * scale; + *height = SDL_max(*height, (fc->y_offset+fc->height) * scale); + + text++; + } +} + void draw_text_with_spritefont_extended ( SDL_Renderer* renderer, @@ -334,6 +358,12 @@ void draw_text_with_spritefont_extended float x_position, float y_position, float scale ) { + float text_column_length, text_column_height; + measure_text(font, text, scale, &text_column_length, &text_column_height); + + x_position -= text_column_length / 2; + y_position -= text_column_height / 2; + while (*text) { const font_char* fc = &font->characters[*text-font->smallest_character]; if (!fc->active) { @@ -351,7 +381,7 @@ void draw_text_with_spritefont_extended }, &(SDL_FRect){ .x = x_position + fc->x_offset*scale, - .y = (y_position + fc->y_offset) * scale, + .y = y_position + fc->y_offset * scale, .w = fc->width * scale, .h = fc->height * scale }); @@ -36,6 +36,7 @@ #define BACKGROUND_SCROLL_SPEED 40 #define MAX_CLEARED_TILES_STORED 50 +#define LOW_TIME_REMAINING 20 //In seconds typedef enum { linear_move_state, @@ -110,13 +111,12 @@ typedef struct { SDL_Texture* background_texture; float background_offset; - sprite_font f; + sprite_font text_font; float background_scaled_width; float background_scaled_height; //Sounds - MIX_Track* background_music_track; MIX_Track* effect_track; MIX_Audio* pop_audio; @@ -129,6 +129,20 @@ s_game game = {0}; extern SDL_Window* window; +static inline float get_window_relative_xpos(float vp) { + int width; + SDL_GetWindowSizeInPixels(window, &width, NULL); + + return width * vp; +} + +static inline float get_window_relative_ypos(float vp) { + int height; + SDL_GetWindowSizeInPixels(window, NULL, &height); + + return height * vp; +} + static inline float random_float_0to1() { return (float)rand() / RAND_MAX; } @@ -232,6 +246,7 @@ void clear_board() { } void init_game(SDL_Renderer* renderer, MIX_Mixer* mixer) { + clear_board(); SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); if (game.spritesheet == NULL) { @@ -252,14 +267,6 @@ void init_game(SDL_Renderer* renderer, MIX_Mixer* mixer) { game.background_scaled_height = scale * texture_height; } - if (game.background_music_track == NULL) { - MIX_Audio* music_audio = MIX_LoadAudio(mixer, "assets/Music/background.mp3", false); - game.background_music_track = MIX_CreateTrack(mixer); - - MIX_SetTrackAudio(game.background_music_track, music_audio); - MIX_DestroyAudio(music_audio); - } - if (game.effect_track == NULL) { game.effect_track = MIX_CreateTrack(mixer); } @@ -268,17 +275,12 @@ void init_game(SDL_Renderer* renderer, MIX_Mixer* mixer) { game.pop_audio = MIX_LoadAudio(mixer, "assets/pop-sound.mp3", false); } - if (game.f.spritesheet == NULL) { + if (game.text_font.spritesheet == NULL) { load_sprite_font(renderer, "assets/Fonts/Peaberry-Font-v2.0/Peaberry Bitmap Fonts/1. WhitePeaberry/2. White Peaberry/WhitePeaberry.txt", - &game.f); + &game.text_font); } - SDL_PropertiesID props = SDL_CreateProperties(); - SDL_SetNumberProperty(props, MIX_PROP_PLAY_LOOPS_NUMBER, -1); - MIX_PlayTrack(game.background_music_track, props); - SDL_DestroyProperties(props); - game.board_width = BOARD_WIDTH; game.board_height = BOARD_HEIGHT; @@ -298,10 +300,9 @@ void init_timed_game(SDL_Renderer* renderer, MIX_Mixer* mixer, float time_remain } void free_game() { - free_sprite_font(&game.f); + free_sprite_font(&game.text_font); SDL_DestroyTexture(game.spritesheet); SDL_DestroyTexture(game.background_texture); - MIX_DestroyTrack(game.background_music_track); MIX_DestroyTrack(game.effect_track); MIX_DestroyAudio(game.pop_audio); } @@ -751,12 +752,19 @@ void draw_timed_mode(SDL_Renderer* renderer) { timed_mode_data* timed_mode_data = &game.timed_mode_data; char timed_mode_display[50]; - snprintf(timed_mode_display, 50, "%.1fs, Timed Mode", timed_mode_data->time_remaining); + snprintf(timed_mode_display, 50, "%.1fs", timed_mode_data->time_remaining); + + //Slight wobble effect when running low on time + float scale = 2; + if (timed_mode_data->time_remaining <= LOW_TIME_REMAINING) { + scale += 0.25 * SDL_sin(-timed_mode_data->time_remaining * 4); + } draw_text_with_spritefont_extended(renderer, - &game.f, + &game.text_font, (unsigned char*)timed_mode_display, - 0, 0, - 2.0 + get_window_relative_xpos(0.5), + get_window_relative_ypos(0.1), + scale ); } @@ -44,16 +44,21 @@ SDL_AppResult SDL_AppEvent(void* userdata, SDL_Event* e) { return SDL_APP_SUCCESS; } + SDL_AppResult result = SDL_APP_CONTINUE; switch (app_state) { case game_state: game_event(e); break; + case menu_state: + result = menu_event(e); + break; + default: break; } - return SDL_APP_CONTINUE; + return result; } SDL_AppResult SDL_AppIterate(void* userdata) { @@ -71,6 +76,9 @@ SDL_AppResult SDL_AppIterate(void* userdata) { draw_game(renderer); break; + case menu_state: + draw_menu(renderer); + default: break; @@ -89,9 +97,10 @@ void SDL_AppQuit(void* userdata, SDL_AppResult result) { } void init_app(SDL_Renderer* renderer, MIX_Mixer* mixer) { - init_timed_game(renderer, mixer, 200); + init_menu(renderer); + init_timed_game(renderer, mixer, 20); - app_state = game_state; + app_state = menu_state; } void free_app() { diff --git a/src/menu.c b/src/menu.c new file mode 100644 index 0000000..2bbc4df --- /dev/null +++ b/src/menu.c @@ -0,0 +1,108 @@ +#include "app.h" +#include "font.h" + +#define MENU_ITEM_TOP_YPOS_RELATIVE 0.3 +#define MENU_ITEM_YOFFSET_RELATIVE 0.03 +#define UNSELECTED_ITEM_SCALE 1 +#define SELECTED_ITEM_SCALE 1.2 + +typedef enum { + timed_mode_option = 0, + exit_option +} menu_option; + +menu_option selected_option; +sprite_font text_font; + +extern SDL_Window* window; +extern application_state app_state; + +static inline float get_window_relative_xpos(float vp) { + int width; + SDL_GetWindowSizeInPixels(window, &width, NULL); + + return width * vp; +} + +static inline float get_window_relative_ypos(float vp) { + int height; + SDL_GetWindowSizeInPixels(window, NULL, &height); + + return height * vp; +} + +void init_menu(SDL_Renderer* renderer) { + if (text_font.spritesheet == NULL) { + load_sprite_font(renderer, + "assets/Fonts/Peaberry-Font-v2.0/Peaberry Bitmap Fonts/1. WhitePeaberry/2. White Peaberry/WhitePeaberry.txt", + &text_font); + } + + selected_option = timed_mode_option; +} + +void draw_menu(SDL_Renderer* renderer) { + SDL_RenderClear(renderer); + + const char* labels[] = { + "Timed Mode", + "Exit", + }; + + float yp = MENU_ITEM_TOP_YPOS_RELATIVE; + for (menu_option i = timed_mode_option; i <= exit_option; i++) { + float scale = i == selected_option ? SELECTED_ITEM_SCALE : UNSELECTED_ITEM_SCALE; + + draw_text_with_spritefont_extended(renderer, + &text_font, + (unsigned char*)labels[i], + get_window_relative_xpos(0.5), + get_window_relative_ypos(yp), + scale + ); + yp += MENU_ITEM_YOFFSET_RELATIVE; + } + + SDL_RenderPresent(renderer); +} + +static inline void select_next_option() { + selected_option = SDL_min(selected_option+1, exit_option); +} + +static inline void select_previous_option() { + if (selected_option > timed_mode_option) { + selected_option--; + } +} + +SDL_AppResult menu_execute_action() { + if (selected_option == exit_option) { + return SDL_APP_SUCCESS; + } if (selected_option == timed_mode_option) { + init_timed_game(NULL, NULL, 40); + app_state = game_state; + } + + return SDL_APP_CONTINUE; +} + +SDL_AppResult menu_event(SDL_Event* e) { + if (e->type == SDL_EVENT_KEY_DOWN) { + SDL_Scancode pressed_button = e->key.scancode; + + if (pressed_button == SDL_SCANCODE_RETURN) { + return menu_execute_action(); + } else if (pressed_button == SDL_SCANCODE_DOWN) { + select_next_option(); + } else if (pressed_button == SDL_SCANCODE_UP) { + select_previous_option(); + } + } + + return SDL_APP_CONTINUE; +} + +void free_menu() { + free_sprite_font(&text_font); +} |
