/* * Copyright (C) 2021 P. J. McDermott * * This file is part of Dodge Balls * * Dodge Balls is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Dodge Balls is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Dodge Balls. If not, see . */ #include #include #include #include "collision.h" #include "defs.h" #include "dirs.h" #include "game.h" #include "help.h" #include "main-menu.h" #include "output.h" #include "util.h" static int _db_main_menu_quit; struct _db_main_menu_button { SDL_Texture *texture_text; SDL_Texture *texture_over; SDL_Rect rect; struct _db_main_menu_button *u; struct _db_main_menu_button *d; struct _db_main_menu_button *l; struct _db_main_menu_button *r; struct _db_main_menu_button *p; struct _db_main_menu_button *n; int (*action)(void *, void *); void *user_data_1; void *user_data_2; }; static SDL_Texture * _db_main_menu_text(TTF_Font *font, const char *text, SDL_Color *color, int width, SDL_Renderer *renderer, SDL_Rect *rect) { SDL_Surface *surface; SDL_Texture *texture; surface = TTF_RenderText_Blended_Wrapped(font, text, *color, width); if (surface == NULL) { db_err("Failed to create surface (%s)", TTF_GetError()); return NULL; } texture = SDL_CreateTextureFromSurface(renderer, surface); if (texture == NULL) { db_err("Failed to create texture (%s)", SDL_GetError()); return NULL; } rect->w = surface->w; rect->h = surface->h; SDL_FreeSurface(surface); return texture; } static int _db_main_menu_action_help(void *user_data_1, void *user_data_2 __attribute__((__unused__))) { SDL_Window *window; window = user_data_1; return db_help(window); } static int _db_main_menu_action_quit(void *user_data_1 __attribute__((__unused__)), void *user_data_2 __attribute__((__unused__))) { _db_main_menu_quit = 1; return 0; } static int _db_main_menu_action_game(void *user_data_1, void *user_data_2) { SDL_Renderer *renderer; struct db_game *game; renderer = user_data_1; game = user_data_2; db_dbg("Loading game \"%s\"", db_game_get_name(game)); return db_game_play(renderer, game); } int db_main_menu(SDL_Window *window) { const char *games_dir; char *font_path; SDL_Renderer *renderer; SDL_Color text_color; SDL_Color over_color; SDL_Rect dest_rect; TTF_Font *font; SDL_Texture *texture_title; struct db_game **games; int n; struct _db_main_menu_button **buttons; char *name_desc; int i; struct _db_main_menu_button *active; SDL_Event event; games_dir = db_get_games_dir(); font_path = db_strcat(db_get_fonts_dir(), "/UbuntuTitling-Bold.ttf"); renderer = SDL_GetRenderer(window); if (renderer == NULL) { db_err("Failed to get renderer (%s)", SDL_GetError()); return -1; } text_color.r = DB_COLOR_FORE_R; text_color.g = DB_COLOR_FORE_G; text_color.b = DB_COLOR_FORE_B; text_color.a = DB_COLOR_FORE_A; over_color.r = DB_COLOR_ACTV_R; over_color.g = DB_COLOR_ACTV_G; over_color.b = DB_COLOR_ACTV_B; over_color.a = DB_COLOR_ACTV_A; /* Render title text */ font = TTF_OpenFont(font_path, DB_FONT_TITLE_SIZE); if (font == NULL) { db_err("Failed to open font (%s)", TTF_GetError()); free(font_path); return -1; } texture_title = _db_main_menu_text(font, "Dodge Balls", &text_color, 0, renderer, &dest_rect); if (texture_title == NULL) { goto err; } dest_rect.x = DB_WINDOW_P; dest_rect.y = DB_WINDOW_P; TTF_CloseFont(font); font = TTF_OpenFont(font_path, DB_FONT_TEXT_SIZE); if (font == NULL) { db_err("Failed to open font (%s)", TTF_GetError()); free(font_path); return -1; } /* Find games */ games = NULL; n = db_games_find(games_dir, &games); buttons = calloc(n + 2, sizeof(*buttons)); if (buttons == NULL) { db_err("Failed to allocate memory"); goto err; } /* Render help button */ buttons[0] = malloc(sizeof(**buttons)); if (buttons[0] == NULL) { db_err("Failed to allocate memory"); goto err; } buttons[0]->texture_text = _db_main_menu_text(font, "How to Play", &text_color, 0, renderer, &buttons[0]->rect); if (buttons[0]->texture_text == NULL) { goto err; } buttons[0]->texture_over = _db_main_menu_text(font, "How to Play", &over_color, 0, renderer, &buttons[0]->rect); if (buttons[0]->texture_over == NULL) { goto err; } buttons[0]->rect.x = DB_WINDOW_W - DB_WINDOW_P - buttons[0]->rect.w; buttons[0]->rect.y = DB_WINDOW_P; buttons[0]->action = &_db_main_menu_action_help; buttons[0]->user_data_1 = window; buttons[0]->user_data_2 = NULL; /* Render quit button */ buttons[1] = malloc(sizeof(**buttons)); if (buttons[1] == NULL) { db_err("Failed to allocate memory"); goto err; } buttons[1]->texture_text = _db_main_menu_text(font, "Quit", &text_color, 0, renderer, &buttons[1]->rect); if (buttons[1]->texture_text == NULL) { goto err; } buttons[1]->texture_over = _db_main_menu_text(font, "Quit", &over_color, 0, renderer, &buttons[1]->rect); if (buttons[1]->texture_over == NULL) { goto err; } buttons[1]->rect.x = DB_WINDOW_W - DB_WINDOW_P - buttons[1]->rect.w; buttons[1]->rect.y = DB_WINDOW_P + DB_FONT_TEXT_SIZE + DB_MARGIN; buttons[1]->action = &_db_main_menu_action_quit; buttons[1]->user_data_1 = NULL; buttons[1]->user_data_2 = NULL; /* Render game buttons */ buttons[1]->n = buttons[0]; /* Quit button link (may change below) */ for (i = 0; i < n; ++i) { buttons[i + 2] = malloc(sizeof(**buttons)); if (buttons[i + 2] == NULL) { db_err("Failed to allocate memory"); goto err; } name_desc = malloc((strlen(db_game_get_name(games[i])) + strlen(db_game_get_desc(games[i])) + 2) * sizeof(*name_desc)); if (name_desc == NULL) { db_err("Failed to allocate memory"); goto err; } sprintf(name_desc, "%s\n%s", db_game_get_name(games[i]), db_game_get_desc(games[i])); buttons[i + 2]->rect.x = DB_WINDOW_P; buttons[i + 2]->rect.y = DB_WINDOW_P + DB_FONT_TITLE_SIZE + DB_MARGIN + (DB_FONT_TEXT_SIZE * 2 + DB_MARGIN) * i; buttons[i + 2]->texture_text = _db_main_menu_text(font, name_desc, &text_color, DB_WINDOW_W - DB_WINDOW_P * 2, renderer, &buttons[i + 2]->rect); if (buttons[i + 2]->texture_text == NULL) { free(name_desc); goto err; } buttons[i + 2]->texture_over = _db_main_menu_text(font, name_desc, &over_color, DB_WINDOW_W - DB_WINDOW_P * 2, renderer, &buttons[i + 2]->rect); free(name_desc); if (buttons[i + 2]->texture_over == NULL) { goto err; } if (i > 0) { buttons[i + 2]->u = buttons[i + 1]; buttons[i + 1]->d = buttons[i + 2]; } buttons[i + 2]->l = NULL; buttons[i + 2]->r = buttons[0]; buttons[i + 2]->p = buttons[i + 1]; buttons[i + 1]->n = buttons[i + 2]; buttons[i + 2]->action = &_db_main_menu_action_game; buttons[i + 2]->user_data_1 = renderer; buttons[i + 2]->user_data_2 = games[i]; } if (n > 0) { buttons[2]->u = NULL; buttons[n + 1]->d = NULL; buttons[i + 1]->n = buttons[0]; } /* Help button links */ buttons[0]->u = NULL; buttons[0]->d = buttons[1]; if (n > 0) { buttons[0]->l = buttons[2]; } else { buttons[0]->l = NULL; } buttons[0]->r = NULL; buttons[0]->p = buttons[1 + n]; buttons[0]->n = buttons[1]; /* Quit button links */ buttons[1]->u = buttons[0]; buttons[1]->d = NULL; if (n > 0) { buttons[1]->l = buttons[2]; } else { buttons[1]->l = NULL; } buttons[1]->r = NULL; buttons[1]->p = buttons[0]; if (n > 0) { active = buttons[2]; } else { active = buttons[1]; } _db_main_menu_quit = 0; while (SDL_WaitEvent(&event)) { switch (event.type) { case SDL_QUIT: _db_main_menu_quit = 1; break; case SDL_KEYDOWN: switch (event.key.keysym.sym) { case SDLK_UP: if (active->u != NULL) { active = active->u; } break; case SDLK_DOWN: if (active->d != NULL) { active = active->d; } break; case SDLK_LEFT: if (active->l != NULL) { active = active->l; } break; case SDLK_RIGHT: if (active->r != NULL) { active = active->r; } break; case SDLK_TAB: if (event.key.keysym.mod & KMOD_SHIFT) { if (active->p != NULL) { active = active->p; } } else { if (active->n != NULL) { active = active->n; } } break; case SDLK_RETURN: case SDLK_KP_ENTER: if (active->action(active-> user_data_1, active-> user_data_2) != 0) { _db_main_menu_quit = 2; } break; default: break; } break; case SDL_MOUSEMOTION: if (event.motion.state != 0) { break; } for (i = 0; i < n + 2; ++i) { if (db_pt_in_rect(event.motion.x, event.motion.y, &buttons[i]->rect)) { active = buttons[i]; break; } } break; case SDL_MOUSEBUTTONUP: if (event.button.button != SDL_BUTTON_LEFT) { break; } if (db_pt_in_rect(event.button.x, event.button.y, &active->rect)) { if (active->action(active->user_data_1, active-> user_data_2) != 0) { _db_main_menu_quit = 2; } } break; default: break; } if (_db_main_menu_quit > 0) { break; } SDL_SetRenderDrawColor(renderer, DB_COLOR_BACK_R, DB_COLOR_BACK_G, DB_COLOR_BACK_B, DB_COLOR_BACK_A); SDL_RenderClear(renderer); SDL_RenderCopy(renderer, texture_title, NULL, &dest_rect); for (i = 0; i < n + 2; ++i) { if (buttons[i] == active) { SDL_RenderCopy(renderer, buttons[i]->texture_over, NULL, &buttons[i]->rect); } else { SDL_RenderCopy(renderer, buttons[i]->texture_text, NULL, &buttons[i]->rect); } } SDL_RenderPresent(renderer); } err: free(font_path); TTF_CloseFont(font); SDL_DestroyTexture(texture_title); return _db_main_menu_quit - 1; }