summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMenase Teshale <osome3717@gmail.com>2026-09-07 03:14:22 +0300
committerMenase Teshale <osome3717@gmail.com>2026-09-07 03:17:14 +0300
commita03a982c026a185d617d25e6a37ce939a481e946 (patch)
treed787eb5cb59f57d81d161470885fde42f1636a6c
parent7b407b8083268e982cbc80b6c1f969dcdc6dec8d (diff)
Started work on menus
-rw-r--r--CMakeLists.txt1
-rw-r--r--src/app.h5
-rw-r--r--src/font.c32
-rw-r--r--src/game.c54
-rw-r--r--src/main.c15
-rw-r--r--src/menu.c108
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
diff --git a/src/app.h b/src/app.h
index c75ece6..31666b2 100644
--- a/src/app.h
+++ b/src/app.h
@@ -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_
diff --git a/src/font.c b/src/font.c
index b04c0df..93a2cfe 100644
--- a/src/font.c
+++ b/src/font.c
@@ -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
});
diff --git a/src/game.c b/src/game.c
index c4dd0e0..bac0e6c 100644
--- a/src/game.c
+++ b/src/game.c
@@ -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
);
}
diff --git a/src/main.c b/src/main.c
index 5102a7f..f1518fd 100644
--- a/src/main.c
+++ b/src/main.c
@@ -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);
+}