linije

Клон игре Color lines
git clone https://git.sr.ht/~strahinja/linije
Дневник | Датотеке | Референце | ПРОЧИТАЈМЕ | ЛИЦЕНЦА

чување 6b07be5002aeccc62dd1a7fe9dc810fb389402a0
родитељ be83635a98b692d08b7062d0e6e7d9a1dd56b987
Аутор: Страхиња Радић <sr@strahinja.org>
Датум:   Wed, 20 May 2026 15:38:12 +0000

Continue hall of fame WIP 2

Diffstat:
Mhiscore.c | 18+++++++++++++-----
Mhiscore.h | 9+++++----
Mlinije.c | 586+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
измењених датотека: 3, додавања: 456(+), брисања: 157(-)

diff --git a/hiscore.c b/hiscore.c @@ -11,7 +11,7 @@ int hs_add(struct HiScoreEntry** table, const char* name, const int name_len, - const int score) + const unsigned int score) { struct HiScoreEntry* p = NULL; struct HiScoreEntry* q = NULL; @@ -40,7 +40,7 @@ hs_add(struct HiScoreEntry** table, const char* name, const int name_len, } int -hs_assign_ps(const char* name, const int name_len, const int score, +hs_assign_ps(const char* name, const int name_len, const unsigned int score, struct HiScoreEntry* dest) { char* new_name = NULL; @@ -59,7 +59,11 @@ hs_assign_ps(const char* name, const int name_len, const int score, return 1; } } - memccpy(dest->name, name, 0, name_len); + if (!memccpy(dest->name, name, 0, name_len)) + dest->name[name_len] = 0; + /*SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, + "hs_assign_ps: dest->name = {%s} [%d]", + dest->name, dest->name_len);*/ hs_assign_ps_skip_name: dest->name_len = name_len; @@ -86,7 +90,11 @@ hs_assign_ss(const struct HiScoreEntry* source, struct HiScoreEntry* dest) return 1; } } - memccpy(dest->name, source->name, 0, source->name_len); + if (!memccpy(dest->name, source->name, 0, source->name_len)) + dest->name[source->name_len] = 0; + /*SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, + "hs_assign_ss: dest->name = {%s} [%d]", + dest->name, dest->name_len);*/ hs_assign_ss_skip_name: dest->name_len = source->name_len; @@ -110,7 +118,7 @@ hs_init(struct HiScoreEntry** table) if (!p->name) goto hs_init_malloc_error; memccpy(p->name, HI_SCORE_DEFAULT_NAME, 0, len); - p->name_len = len; + p->name_len = len - 1; p->score = 0; } diff --git a/hiscore.h b/hiscore.h @@ -3,18 +3,19 @@ * See the file LICENSE for exact copyright and license details. */ #define HI_SCORE_DEFAULT_NAME "Unknown player" -#define HI_SCORE_MAX 10 +#define HI_SCORE_MAX 10 +#define HI_SCORE_NAME_MAX 40 struct HiScoreEntry { char* name; int name_len; - int score; + unsigned int score; }; int hs_add(struct HiScoreEntry** table, const char* name, const int name_len, - const int score); + const unsigned int score); int hs_assign(const struct HiScoreEntry* source, struct HiScoreEntry* dest); int hs_assign_ss(const struct HiScoreEntry* source, struct HiScoreEntry* dest); -int hs_assign_ps(const char* name, const int name_len, const int score, +int hs_assign_ps(const char* name, const int name_len, const unsigned int score, struct HiScoreEntry* dest); int hs_init(struct HiScoreEntry** table); diff --git a/linije.c b/linije.c @@ -25,22 +25,26 @@ #define FPS 60 #define ANIM_STEPS (ANIM_SPEED * FPS / 1000) -#define BOLD_START '\1' -#define BOLD_END '\2' -#define BUFSIZE 4096 -#define CELL_SIZE 20 -#define DEFAULT_WIDTH 1280 -#define DEFAULT_HEIGHT 720 -#define HELP_DIALOG_WIDTH 900 -#define HELP_DIALOG_HEIGHT 500 -#define HOF_DIALOG_WIDTH 900 -#define HOF_DIALOG_HEIGHT 470 -#define HOF_DIALOG_MAX_LINE_LEN 60 -#define HOF_DIALOG_TEXT_ORIGIN_X 70 +#define BOLD_START '\1' +#define BOLD_END '\2' +#define CENTER_LINE '\3' +#define BUFSIZE 4096 +#define CELL_SIZE 20 +#define DEFAULT_WIDTH 1280 +#define DEFAULT_HEIGHT 720 +#define HELP_DIALOG_WIDTH 900 +#define HELP_DIALOG_HEIGHT 500 +#define HOF_DIALOG_WIDTH 900 +#define HOF_DIALOG_HEIGHT 470 +#define HOF_DIALOG_MAX_LINE_LEN 60 +// #define HOF_DIALOG_TEXT_ORIGIN_X 70 +#define HOF_DIALOG_TEXT_ORIGIN_X 0 #define GAME_OVER_DIALOG_WIDTH 270 #define GAME_OVER_DIALOG_HEIGHT 110 #define GRID_WIDTH 15 #define GRID_HEIGHT 15 +#define INPUT_WIDTH 600 +#define INPUT_HEIGHT 120 #define LOCATION_STACK_MAX (GRID_WIDTH * GRID_HEIGHT) #define MIN_POP_LEN 5 /* clang-format off */ @@ -49,6 +53,8 @@ /* clang-format on */ #define PRINT_BUFSIZE 4096 #define PROGNAME "linije" +#define QUIT_MSGBOX_WIDTH 450 +#define QUIT_MSGBOX_HEIGHT 150 #define TER12_WIDTH 6 #define TER12_HEIGHT 12 #define TEXT_OPACITY_NORMAL 0.6f @@ -105,15 +111,14 @@ const char* state_descriptions[] = { }; const char* game_over_text[] = { - "==== \1GAME OVER\2 ====", - "Press \1F2\2 to restart", + "\3==== \1GAME OVER\2 ====", + "\3Press \1F2\2 to restart", NULL }; const char* help_text[] = { - "\1linije - Color lines clone\2", - "\1==========================\2", - "Version " VERSION, + "\3==== \1linije - Color lines clone\2 ====", + "\3\\\\ Version " VERSION " //", "", "\1F1\2 - Show this help screen", "\1F2\2 - Restart the game", @@ -130,6 +135,42 @@ const char* help_text[] = { NULL }; +const char* name_text[] = { + "\3==== \1ENTER YOUR NAME\2 ====", + "", + NULL +}; + +const char* quit_msgbox_text[] = { + "\3==== \1CONFIRM\2 ====", + "", + "\3Do you really want to quit [\1y\2/\1n\2]?", + NULL +}; + +const char* restart_msgbox_text[] = { + "\3==== \1CONFIRM\2 ====", + "", + "\3Do you really want to start a new game [\1y\2/\1n\2]?", + NULL +}; + +const char ascii_shift[255] = { + ['`'] = '~', ['1'] = '!', ['2'] = '@', ['3'] = '#', ['4'] = '$', + ['5'] = '%', ['6'] = '^', ['7'] = '&', ['8'] = '*', ['9'] = '(', + ['0'] = ')', ['-'] = '_', ['='] = '+', + + ['q'] = 'Q', ['w'] = 'W', ['e'] = 'E', ['r'] = 'R', ['t'] = 'T', + ['y'] = 'Y', ['u'] = 'U', ['i'] = 'I', ['o'] = 'O', ['p'] = 'P', + ['['] = '{', [']'] = '}', + + ['a'] = 'A', ['s'] = 'S', ['d'] = 'D', ['f'] = 'F', ['g'] = 'G', + ['h'] = 'H', ['j'] = 'J', ['k'] = 'K', ['l'] = 'L', [';'] = ':', + ['\''] = '"',['\\'] = '|', + + ['z'] = 'Z', ['x'] = 'X', ['c'] = 'C', ['v'] = 'V', ['b'] = 'B', + ['n'] = 'N', ['m'] = 'M', [','] = '<', ['.'] = '>', ['/'] = '?' +}; /* clang-format on */ struct AnimationState { @@ -173,6 +214,9 @@ struct State { int display_height; int full_screen; struct HiScoreEntry* hall_of_fame; + void (*input_callback)(struct State*); + int input_col; + int input_shown; int location_pointer; SDL_FPoint* location_stack; int mode; @@ -181,8 +225,10 @@ struct State { int nfree; struct Cell* path_start; struct Cell* path_end; - char* player_name; - int player_name_len; + char player_name[HI_SCORE_NAME_MAX]; + int player_name_length; + int query_shown; + void (*query_callback)(struct State*); int redraw; SDL_Renderer* renderer; int running; @@ -214,6 +260,7 @@ void erase_cell(struct State* state, struct Cell* c); void fill_triplet(struct State* state); int find_shortest_path(struct Cell* c, struct Cell* source, const int dist); void game_over(struct State* state); +void game_over_dialog(struct State* state); void get_sprite_xy(const char cell, float* x, float* y); void grid_coord_to_screen_coord(const struct State* state, const int x, const int y, const float startx, const float starty, float* to_x, @@ -223,21 +270,27 @@ void grid_coord_to_screen_coordF(const struct State* state, const float x, float* to_y); void handle_click(struct State* state); void handle_event(struct State* state, SDL_Event* event); +void handle_input(struct State* state, SDL_Event* event); +void handle_key_down(struct State* state, SDL_Event* event); void init_cell(struct Cell* c, const int full, const SDL_FPoint* coords); void init_state(struct State* state); +void input_name(struct State* state); +void input_name_callback(struct State* state); int load_textures(struct State* state); void location_delete(struct State* state, const int index); int location_index(const struct State* state, const SDL_FPoint* location); void location_push(struct State* state, const SDL_FPoint* location); void mark_five(struct State* state, SDL_FPoint* start, SDL_FPoint* end); void mark_path(struct Cell* c); -int num_size(const int num); +int num_size(const unsigned int num); void pop_strings(struct State* state); -void prepare_hof_dialog(struct State* state, const int show); void prepare_dialog(struct State* state, const int w, const int h, const char** text, const int dx, const int show); +void prepare_hof_dialog(struct State* state, const int show); void print_matrix(const struct Cell* M); void print_path(struct Cell* c); +void quit_callback(struct State* state); +void really_quit(struct State* state); void render_background(struct State* state, SDL_FRect* bg_rect); void render_balls(struct State* state, const float startx, const float starty); void render_dialog(struct State* state); @@ -256,6 +309,7 @@ void screen_coord_to_grid_coordF(struct State* state, const float x, const float y, const float startx, const float starty, float* to_x, float* to_y); void step_animation(struct State* state); +int strlen_visible(const char* s); SDL_Texture* try_load(SDL_Renderer* renderer, const char* filename); void @@ -645,12 +699,17 @@ game_over(struct State* state) { assert(state != NULL); state->mode = STATE_GAME_OVER; + if (state->score > state->hall_of_fame[HI_SCORE_MAX - 1].score) + input_name(state); + else + game_over_dialog(state); +} + +void +game_over_dialog(struct State* state) +{ prepare_dialog(state, GAME_OVER_DIALOG_WIDTH, GAME_OVER_DIALOG_HEIGHT, game_over_text, 0, 1); - SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, - "game_over: Prepared game over dialog"); - hs_add(&state->hall_of_fame, state->player_name, state->player_name_len, - state->score); } void @@ -853,81 +912,208 @@ handle_event(struct State* state, SDL_Event* event) break; case SDL_EVENT_KEY_DOWN: SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, - "Got Keydown, key = %X, mod = %X", event->key.key, - event->key.mod); + "handle_event (before): player = {%s} [%d]", + state->player_name, state->player_name_length); + handle_key_down(state, event); + SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, + "handle_event (after): player = {%s} [%d]", + state->player_name, state->player_name_length); + break; + case SDL_EVENT_MOUSE_BUTTON_UP: + handle_click(state); + break; + case SDL_EVENT_MOUSE_MOTION: + state->mouse.x = event->motion.x; + state->mouse.y = event->motion.y; + break; + } +} - switch (event->key.key) - { - case SDLK_F1: - if (event->key.mod & SDL_KMOD_CTRL) - { - state->mode = STATE_GAME_OVER; - prepare_dialog(state, GAME_OVER_DIALOG_WIDTH, - GAME_OVER_DIALOG_HEIGHT, game_over_text, - 0, 1); - break; - } +void +handle_input(struct State* state, SDL_Event* event) +{ + int i; - if (state->dialog_shown) - break; - prepare_dialog(state, HELP_DIALOG_WIDTH, - HELP_DIALOG_HEIGHT, help_text, 0, 1); - SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, - "Dialog shown: %s", - state->dialog_shown ? "YES" : "NO"); + SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, + "handle_input (before): state->player_name = {%s} [%d], %d", + state->player_name, state->player_name_length, + state->input_col); + switch (event->key.key) + { + case SDLK_ESCAPE: + state->input_shown = 0; + game_over_dialog(state); + break; + case SDLK_RETURN: + state->input_shown = 0; + if (state->input_callback) + state->input_callback(state); + game_over_dialog(state); + break; + case SDLK_BACKSPACE: + if (state->input_col == 0) break; - case SDLK_F2: - if (state->dialog_shown - && state->dialog_text == help_text) - break; - reset(state); - fill_triplet(state); + for (i = state->input_col - 1; i < state->player_name_length; + i++) + state->player_name[i] = state->player_name[i + 1]; + state->player_name_length--; + state->player_name[state->player_name_length] = 0; + state->input_col--; + state->redraw = 1; + break; + case SDLK_DELETE: + if (state->input_col == state->player_name_length) break; - case SDLK_F3: - if (state->dialog_shown) - break; - prepare_hof_dialog(state, 1); + for (i = state->input_col; i < state->player_name_length; i++) + state->player_name[i] = state->player_name[i + 1]; + state->player_name_length--; + state->player_name[state->player_name_length] = 0; + state->redraw = 1; + break; + case SDLK_LEFT: + if (state->input_col > 0) + { + state->input_col--; + state->redraw = 1; + } + SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, + "SDLK_LEFT: player_name = {%s} [%d]", + state->player_name, state->player_name_length); + break; + case SDLK_RIGHT: + if (state->input_col < state->player_name_length) + { + state->input_col++; + state->redraw = 1; + } + SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, + "SDLK_RIGHT: player_name = {%s} [%d]", + state->player_name, state->player_name_length); + break; + default: + if (state->player_name_length < HI_SCORE_NAME_MAX + && event->key.key >= SDLK_SPACE + && event->key.key <= SDLK_TILDE) + { + for (i = state->player_name_length; + i > state->input_col; i--) + state->player_name[i] + = state->player_name[i - 1]; + state->player_name[state->input_col++] + = event->key.mod & SDL_KMOD_SHIFT + ? ascii_shift[event->key.key] + : event->key.key; + state->player_name_length++; + state->player_name[state->player_name_length] = 0; SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, - "Dialog shown: %s", - state->dialog_shown ? "YES" : "NO"); - break; - case SDLK_RETURN: - if (!(event->key.mod & SDL_KMOD_ALT)) - break; - state->full_screen = !state->full_screen; + "handle_input: event->key.key = %d", + event->key.key); SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, - "Set fullscreen state: %s", - state->full_screen ? "ON" : "OFF"); - SDL_SetWindowFullscreen(state->window, - state->full_screen); - SDL_SetWindowBordered(state->window, - !state->full_screen); - break; - case SDLK_ESCAPE: - if (state->dialog_shown - && state->mode == STATE_GAME_OVER) - break; - state->dialog_shown = 0; - state->dialog_text = NULL; - assign_point_xyII(-1, -1, &state->dialog_dims); + "handle_input (after): state->player_name = " + "{%s} [%d], %d", + state->player_name, state->player_name_length, + state->input_col); state->redraw = 1; + } + } +} + +void +handle_key_down(struct State* state, SDL_Event* event) +{ + if (state->input_shown) + { + SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, + "handle_key_down (before): player = {%s} [%d]", + state->player_name, state->player_name_length); + handle_input(state, event); + SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, + "handle_key_down (after): player = {%s} [%d]", + state->player_name, state->player_name_length); + return; + } + + SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, + "Got Keydown, key = %X, mod = %X", event->key.key, + event->key.mod); + + switch (event->key.key) + { + case SDLK_F1: + if (event->key.mod & SDL_KMOD_CTRL) + { + game_over(state); break; - case SDLK_Q: - if (event->key.mod & SDL_KMOD_CTRL) - state->running = 0; + } + + if (state->dialog_shown) break; - default: - SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, - "Handle keyboard event: switch fallthrough"); + prepare_dialog(state, HELP_DIALOG_WIDTH, HELP_DIALOG_HEIGHT, + help_text, 0, 1); + SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "Dialog shown: %s", + state->dialog_shown ? "YES" : "NO"); + break; + case SDLK_F2: + if (state->dialog_shown && state->dialog_text == help_text) + break; + reset(state); + fill_triplet(state); + break; + case SDLK_F3: + if (state->dialog_shown) + break; + prepare_hof_dialog(state, 1); + SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "Dialog shown: %s", + state->dialog_shown ? "YES" : "NO"); + break; + case SDLK_RETURN: + if (!(event->key.mod & SDL_KMOD_ALT)) + break; + state->full_screen = !state->full_screen; + SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, + "Set fullscreen state: %s", + state->full_screen ? "ON" : "OFF"); + SDL_SetWindowFullscreen(state->window, state->full_screen); + SDL_SetWindowBordered(state->window, !state->full_screen); + break; + case SDLK_ESCAPE: + if (state->dialog_shown && state->mode == STATE_GAME_OVER) + break; + state->dialog_shown = 0; + state->dialog_text = NULL; + state->query_shown = 0; + // assign_point_xyII(-1, -1, &state->dialog_dims); + state->redraw = 1; + break; + case SDLK_Y: + if (state->query_shown) + { + state->dialog_shown = 0; + state->dialog_text = NULL; + state->query_shown = 0; + state->redraw = 1; + if (state->query_callback) + state->query_callback(state); } break; - case SDL_EVENT_MOUSE_BUTTON_UP: - handle_click(state); + case SDLK_N: + if (state->query_shown) + { + state->dialog_shown = 0; + state->dialog_text = NULL; + state->query_callback = NULL; + state->query_shown = 0; + state->redraw = 1; + } break; - case SDL_EVENT_MOUSE_MOTION: - state->mouse.x = event->motion.x; - state->mouse.y = event->motion.y; + case SDLK_Q: + if (event->key.mod & SDL_KMOD_CTRL) + really_quit(state); + // state->running = 0; break; + default: + SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, + "Handle keyboard event: switch fallthrough"); } } @@ -987,22 +1173,27 @@ init_state(struct State* state) state->full_screen = 1; state->hall_of_fame = NULL; // state->hof_text = NULL; + state->input_callback = NULL; + state->input_col = 0; + state->input_shown = 0; state->location_pointer = 0; state->location_stack = NULL; state->mode = STATE_IDLE; state->moveno = 1; state->nfree = GRID_WIDTH * GRID_HEIGHT; assign_point_xyIF(0, 0, &state->mouse); - state->path_start = NULL; - state->path_end = NULL; - state->player_name = NULL; - state->player_name_len = -1; - state->redraw = 1; - state->renderer = NULL; - state->running = 1; - state->screen_width = DEFAULT_WIDTH; - state->screen_height = DEFAULT_HEIGHT; - state->score = 0; + state->path_start = NULL; + state->path_end = NULL; + state->player_name[0] = 0; + state->player_name_length = 0; + state->query_shown = 0; + state->query_callback = NULL; + state->redraw = 1; + state->renderer = NULL; + state->running = 1; + state->screen_width = DEFAULT_WIDTH; + state->screen_height = DEFAULT_HEIGHT; + state->score = 0; assign_point_xyIF(-1, -1, &state->selection_start); assign_point_xyIF(-1, -1, &state->selection_end); state->sprites = NULL; @@ -1011,6 +1202,35 @@ init_state(struct State* state) state->window = NULL; } +void +input_name(struct State* state) +{ + state->input_shown = 1; + state->query_shown = 0; + state->input_col = 0; + state->player_name_length = 0; + state->player_name[0] = 0; + state->input_callback = &input_name_callback; + prepare_dialog(state, INPUT_WIDTH, INPUT_HEIGHT, name_text, 0, 1); +} + +void +input_name_callback(struct State* state) +{ + int i; + assert(state != NULL); + state->input_callback = NULL; + SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, + "input_name_callback: player_name = %s", state->player_name); + for (i = 0; i < state->player_name_length; i++) + SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "%d", + state->player_name[i]); + hs_add(&state->hall_of_fame, state->player_name, + state->player_name_length, state->score); + prepare_dialog(state, GAME_OVER_DIALOG_WIDTH, GAME_OVER_DIALOG_HEIGHT, + game_over_text, 0, 1); +} + int load_textures(struct State* state) { @@ -1124,7 +1344,7 @@ mark_path(struct Cell* c) } int -num_size(const int num) +num_size(const unsigned int num) { if (num > 999999999) return 10; @@ -1168,12 +1388,28 @@ pop_strings(struct State* state) } void +prepare_dialog(struct State* state, const int w, const int h, const char** text, + const int dx, const int show) +{ + assert((state != NULL) && (text != NULL)); + + assign_point_xyII(w, h, &state->dialog_dims); + state->dialog_text_origin.x = dx; + state->dialog_text = text; + state->dialog_shown = show; + if (show) + state->redraw = 1; +} + +#define HOF_SCORES_START_IDX 2 + +void prepare_hof_dialog(struct State* state, const int show) { - struct HiScoreEntry* hof = NULL; - static char** hof_text = NULL; - static int hof_size = HI_SCORE_MAX + 3 + 2 + 1; - char* dots = NULL; + struct HiScoreEntry* e = NULL; + static char** hof_text = NULL; + static int hof_size = HI_SCORE_MAX + 2 + 2 + 1; + char* dots = NULL; int i; assert(state != NULL); @@ -1182,32 +1418,39 @@ prepare_hof_dialog(struct State* state, const int show) if (!(dots = malloc(HOF_DIALOG_MAX_LINE_LEN + 1))) goto prepare_hof_dialog_malloc_error; - if (hof_text) + /*if (hof_text) for (i = 3; i < hof_size - 3; i++) free(hof_text[i]); - free(hof_text); + free(hof_text);*/ - if (!(hof_text = calloc(hof_size, sizeof(struct HiScoreEntry*)))) - goto prepare_hof_dialog_malloc_error; + if (!hof_text) + { + if (!(hof_text = calloc(hof_size, sizeof(char*)))) + goto prepare_hof_dialog_malloc_error; - hof_text[0] = "\1HALL OF FAME\2"; - hof_text[1] = "\1============\2"; - hof_text[2] = ""; + hof_text[0] = "\3==== \1HALL OF FAME\2 ===="; + hof_text[1] = ""; + hof_text[hof_size - 3] = ""; + hof_text[hof_size - 2] = "\3Press \1Esc\2 to close"; + hof_text[hof_size - 1] = NULL; + } for (i = 0; i < HI_SCORE_MAX; i++) { - if (!(hof_text[3 + i] = malloc(HOF_DIALOG_MAX_LINE_LEN + 1))) - goto prepare_hof_dialog_malloc_error; - hof = &state->hall_of_fame[i]; + if (!(hof_text[HOF_SCORES_START_IDX + i])) + { + if (!(hof_text[HOF_SCORES_START_IDX + i] + = malloc(HOF_DIALOG_MAX_LINE_LEN + 1))) + goto prepare_hof_dialog_malloc_error; + } + e = &state->hall_of_fame[i]; repeat_char(&dots, '.', - HOF_DIALOG_MAX_LINE_LEN - 4 - hof->name_len - - num_size(hof->score)); - snprintf(hof_text[3 + i], HOF_DIALOG_MAX_LINE_LEN, - "%2d. %s%s%d", i + 1, hof->name, dots, hof->score); + HOF_DIALOG_MAX_LINE_LEN - 4 - e->name_len + - num_size(e->score) - 1); + snprintf(hof_text[HOF_SCORES_START_IDX + i], + HOF_DIALOG_MAX_LINE_LEN, "%2d. %s%s%u", i + 1, e->name, + dots, e->score); } - hof_text[hof_size - 3] = ""; - hof_text[hof_size - 2] = "Press \1Esc\2 to close"; - hof_text[hof_size - 1] = NULL; prepare_dialog(state, HOF_DIALOG_WIDTH, HOF_DIALOG_HEIGHT, (const char**)hof_text, HOF_DIALOG_TEXT_ORIGIN_X, show); @@ -1216,22 +1459,27 @@ prepare_hof_dialog(struct State* state, const int show) return; prepare_hof_dialog_malloc_error: - perror("malloc"); + SDL_LogError(SDL_LOG_CATEGORY_ERROR, "malloc failed"); exit(1); } void -prepare_dialog(struct State* state, const int w, const int h, const char** text, - const int dx, const int show) +quit_callback(struct State* state) { - assert((state != NULL) && (text != NULL)); + assert(state != NULL); + UNUSED(state); + state->query_callback = NULL; + state->running = 0; +} - assign_point_xyII(w, h, &state->dialog_dims); - state->dialog_text_origin.x = dx; - state->dialog_text = text; - state->dialog_shown = show; - if (show) - state->redraw = 1; +void +really_quit(struct State* state) +{ + assert(state != NULL); + state->query_shown = 1; + state->query_callback = quit_callback; + prepare_dialog(state, QUIT_MSGBOX_WIDTH, QUIT_MSGBOX_HEIGHT, + quit_msgbox_text, 0, 1); } void @@ -1407,10 +1655,10 @@ void render_dialog(struct State* state) { const char** ptext = NULL; - SDL_FRect dialog_rect; + SDL_FRect dialog_rect, input_rect, cursor_rect; float tsf; int w, h; - int sx, sy, dx; + int sx, sy, dx, _sx; assert(state != NULL); assert(state->dialog_text != NULL); @@ -1450,14 +1698,46 @@ render_dialog(struct State* state) while (*ptext) { - render_text(state, sx + dx + tsf * (2 * TER12_WIDTH), + _sx = sx + dx + tsf * (2 * TER12_WIDTH); + // SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "1. _sx=%d", _sx); + if (strchr(*ptext, CENTER_LINE)) + _sx += w / 2 - dx + - tsf * TER12_WIDTH + * (2 + strlen_visible(*ptext) / 2); + // SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "2. _sx=%d", _sx); + + render_text(state, _sx, sy + (ptext - state->dialog_text + 1) * tsf * (TER12_HEIGHT + 2), - TER12_WIDTH, TER12_HEIGHT, state->ter12_texture, + TER12_WIDTH, TER12_HEIGHT, state->ter12_texture, "%s", *ptext); ptext++; } + + if (state->input_shown) + { + input_rect.w = tsf * TER12_WIDTH * (HI_SCORE_NAME_MAX + 2); + input_rect.h = tsf * TER12_HEIGHT; + input_rect.x = sx + w / 2 + - tsf * TER12_WIDTH * HI_SCORE_NAME_MAX / 2; + input_rect.y = sy + h - tsf * 2 * TER12_HEIGHT; + + cursor_rect.w = TER12_WIDTH; + cursor_rect.h = 3; + + SDL_SetRenderDrawBlendMode(state->renderer, SDL_BLENDMODE_BLEND); + SDL_SetRenderDrawColor(state->renderer, 55, 55, 55, .75 * 255); + SDL_RenderFillRect(state->renderer, &input_rect); + _sx = input_rect.x + tsf * TER12_WIDTH; + + cursor_rect.x = _sx + tsf * TER12_WIDTH * state->input_col; + cursor_rect.y + = input_rect.y + tsf * TER12_HEIGHT - cursor_rect.h; + SDL_SetRenderDrawColor(state->renderer, 255, 255, 255, + SDL_ALPHA_OPAQUE); + SDL_RenderFillRect(state->renderer, &cursor_rect); + } } void @@ -1572,6 +1852,11 @@ render_text(struct State* state, const int x, const int y, const int font_width, xstart = 1 + index % chars_per_row * (font_width + 1); ystart = 1 + index / chars_per_row * (font_height + 1); } + else if (*pbuf == CENTER_LINE) + { + pbuf++; + continue; + } else if (*pbuf == BOLD_START) { opacity = TEXT_OPACITY_BOLD; @@ -1822,6 +2107,25 @@ step_animation(struct State* state) state->animation.active ? "ACTIVE" : "INACTIVE"); } +int +strlen_visible(const char* s) +{ + int result = 0; + const char* ps = NULL; + + assert(s != NULL); + ps = s; + while (*ps) + { + if (*ps != CENTER_LINE && *ps != BOLD_START && *ps != BOLD_END) + result++; + ps++; + } + SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "strlen_visible(%s)=%d\n", s, + result); + return result; +} + SDL_Texture* try_load(SDL_Renderer* renderer, const char* filename) { @@ -1860,7 +2164,7 @@ main(int argc, char** argv) struct Cell* c = NULL; struct State state; struct HiScoreEntry* hof = NULL; - struct HiScoreEntry* p = NULL; + // struct HiScoreEntry* p = NULL; struct timespec now; SDL_FPoint coords; float px, py; @@ -1875,20 +2179,6 @@ main(int argc, char** argv) hs_init(&hof); state.hall_of_fame = hof; - /* - for (p = hof; p < hof + HI_SCORE_MAX; p++) - printf("%s (%d) -> %d\n", p->name, p->name_len, p->score); - - hs_add(&hof, "Player 2 with a very long name", - sizeof("Player 2 with a very long name"), 2); - hs_add(&hof, "Player 1", sizeof("Player 1"), 50); - hs_add(&hof, "Strajder", sizeof("Strajder"), 250); - hs_add(&hof, "Player 3", sizeof("Player 3"), 44); - - for (p = hof; p < hof + HI_SCORE_MAX; p++) - printf("%s (%d) -> %d\n", p->name, p->name_len, p->score); - - return 0;*/ if (!SDL_Init(SDL_INIT_VIDEO)) {