linije

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

чување be83635a98b692d08b7062d0e6e7d9a1dd56b987
родитељ 66d911adc541ddb9914eb1160c93dd1537f6a692
Аутор: Страхиња Радић <sr@strahinja.org>
Датум:   Fri,  8 May 2026 13:53:56 +0000

Continue hall of fame WIP

Diffstat:
Mlinije.c | 225+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
измењених датотека: 1, додавања: 174(+), брисања: 51(-)

diff --git a/linije.c b/linije.c @@ -25,20 +25,24 @@ #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 470 -#define GAME_OVER_DIALOG_WIDTH 270 -#define GAME_OVER_DIALOG_HEIGHT 110 -#define GRID_WIDTH 15 -#define GRID_HEIGHT 15 -#define LOCATION_STACK_MAX (GRID_WIDTH * GRID_HEIGHT) -#define MIN_POP_LEN 5 +#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 GAME_OVER_DIALOG_WIDTH 270 +#define GAME_OVER_DIALOG_HEIGHT 110 +#define GRID_WIDTH 15 +#define GRID_HEIGHT 15 +#define LOCATION_STACK_MAX (GRID_WIDTH * GRID_HEIGHT) +#define MIN_POP_LEN 5 /* clang-format off */ #define POP_STRING_CELL_VALUE 100 /* Multiplied by # of balls in a string and * added to score when popping a string */ @@ -113,6 +117,7 @@ const char* help_text[] = { "", "\1F1\2 - Show this help screen", "\1F2\2 - Restart the game", + "\1F3\2 - Show the hall of fame", "\1Esc\2 - Hide this help screen", "\1Alt+Enter\2 - Toggle fullscreen", "\1Ctrl+Q\2 - Exit", @@ -124,6 +129,7 @@ const char* help_text[] = { "See the file LICENSE for exact copyright and license details.", NULL }; + /* clang-format on */ struct AnimationState { @@ -160,11 +166,13 @@ struct State { struct Cell* grid; float cell_scale_factor; SDL_Point dialog_dims; + SDL_Point dialog_text_origin; int dialog_shown; const char** dialog_text; int display_width; /* Physical, detected */ int display_height; int full_screen; + struct HiScoreEntry* hall_of_fame; int location_pointer; SDL_FPoint* location_stack; int mode; @@ -173,6 +181,8 @@ struct State { int nfree; struct Cell* path_start; struct Cell* path_end; + char* player_name; + int player_name_len; int redraw; SDL_Renderer* renderer; int running; @@ -203,6 +213,7 @@ void draw(struct State* state); 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 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, @@ -220,9 +231,11 @@ 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); 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 show); + const char** text, const int dx, const int show); void print_matrix(const struct Cell* M); void print_path(struct Cell* c); void render_background(struct State* state, SDL_FRect* bg_rect); @@ -234,6 +247,7 @@ void render_status(struct State* state); void render_text(struct State* state, const int x, const int y, const int font_width, const int font_height, SDL_Texture* font_tex, const char* text, ...); +void repeat_char(char** s, const char ch, const int num_times); void reset(struct State* state); void reset_path(struct Cell* grid); void screen_coord_to_grid_coord(struct State* state, const int x, const int y, @@ -575,7 +589,7 @@ fill_triplet_generate: { state->mode = STATE_GAME_OVER; prepare_dialog(state, GAME_OVER_DIALOG_WIDTH, - GAME_OVER_DIALOG_HEIGHT, game_over_text, 1); + GAME_OVER_DIALOG_HEIGHT, game_over_text, 0, 1); return; } pass++; @@ -590,7 +604,7 @@ fill_triplet_generate: fill_triplet_fail: state->mode = STATE_GAME_OVER; prepare_dialog(state, GAME_OVER_DIALOG_WIDTH, GAME_OVER_DIALOG_HEIGHT, - game_over_text, 1); + game_over_text, 0, 1); SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "fill_triplet: Prepared game over dialog"); } @@ -627,6 +641,19 @@ find_shortest_path(struct Cell* c, struct Cell* source, const int dist) } void +game_over(struct State* state) +{ + assert(state != NULL); + state->mode = STATE_GAME_OVER; + 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 get_sprite_xy(const char cell, float* x, float* y) { assert((x != NULL) && (y != NULL)); @@ -832,10 +859,19 @@ handle_event(struct State* state, SDL_Event* event) 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; + } + if (state->dialog_shown) break; prepare_dialog(state, HELP_DIALOG_WIDTH, - HELP_DIALOG_HEIGHT, help_text, 1); + HELP_DIALOG_HEIGHT, help_text, 0, 1); SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "Dialog shown: %s", state->dialog_shown ? "YES" : "NO"); @@ -850,9 +886,10 @@ handle_event(struct State* state, SDL_Event* event) case SDLK_F3: if (state->dialog_shown) break; - state->mode = STATE_GAME_OVER; - prepare_dialog(state, GAME_OVER_DIALOG_WIDTH, - GAME_OVER_DIALOG_HEIGHT, game_over_text, 1); + 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)) @@ -868,7 +905,7 @@ handle_event(struct State* state, SDL_Event* event) break; case SDLK_ESCAPE: if (state->dialog_shown - && state->dialog_text != help_text) + && state->mode == STATE_GAME_OVER) break; state->dialog_shown = 0; state->dialog_text = NULL; @@ -942,25 +979,30 @@ init_state(struct State* state) state->grid = NULL; state->cell_scale_factor = 2.0f; assign_point_xyII(-1, -1, &state->dialog_dims); - state->dialog_shown = 0; - state->dialog_text = NULL; - state->display_width = DEFAULT_WIDTH; - state->display_height = DEFAULT_HEIGHT; - state->full_screen = 1; + assign_point_xyII(0, 0, &state->dialog_text_origin); + state->dialog_shown = 0; + state->dialog_text = NULL; + state->display_width = DEFAULT_WIDTH; + state->display_height = DEFAULT_HEIGHT; + state->full_screen = 1; + state->hall_of_fame = NULL; + // state->hof_text = NULL; 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->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 = 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; assign_point_xyIF(-1, -1, &state->selection_start); assign_point_xyIF(-1, -1, &state->selection_end); state->sprites = NULL; @@ -1081,6 +1123,30 @@ mark_path(struct Cell* c) mark_path(c->path_prev); } +int +num_size(const int num) +{ + if (num > 999999999) + return 10; + if (num > 99999999) + return 9; + if (num > 9999999) + return 8; + if (num > 999999) + return 7; + if (num > 99999) + return 6; + if (num > 9999) + return 5; + if (num > 999) + return 4; + if (num > 99) + return 3; + if (num > 9) + return 2; + return 1; +} + void pop_strings(struct State* state) { @@ -1102,14 +1168,68 @@ pop_strings(struct State* state) } 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; + int i; + + assert(state != NULL); + errno = 0; + + if (!(dots = malloc(HOF_DIALOG_MAX_LINE_LEN + 1))) + goto prepare_hof_dialog_malloc_error; + + if (hof_text) + for (i = 3; i < hof_size - 3; i++) + free(hof_text[i]); + free(hof_text); + + if (!(hof_text = calloc(hof_size, sizeof(struct HiScoreEntry*)))) + goto prepare_hof_dialog_malloc_error; + + hof_text[0] = "\1HALL OF FAME\2"; + hof_text[1] = "\1============\2"; + hof_text[2] = ""; + + 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]; + 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_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); + + free(dots); + return; + +prepare_hof_dialog_malloc_error: + perror("malloc"); + exit(1); +} + +void prepare_dialog(struct State* state, const int w, const int h, const char** text, - const int show) + const int dx, const int show) { assert((state != NULL) && (text != NULL)); assign_point_xyII(w, h, &state->dialog_dims); - state->dialog_text = text; - state->dialog_shown = show; + state->dialog_text_origin.x = dx; + state->dialog_text = text; + state->dialog_shown = show; if (show) state->redraw = 1; } @@ -1290,7 +1410,7 @@ render_dialog(struct State* state) SDL_FRect dialog_rect; float tsf; int w, h; - int sx, sy; + int sx, sy, dx; assert(state != NULL); assert(state->dialog_text != NULL); @@ -1301,6 +1421,7 @@ render_dialog(struct State* state) h = state->dialog_dims.y; sx = state->screen_width / 2 - w / 2; sy = state->screen_height / 2 - h / 2; + dx = state->dialog_text_origin.x; tsf = state->text_scale_factor; dialog_rect.x = sx; @@ -1329,7 +1450,7 @@ render_dialog(struct State* state) while (*ptext) { - render_text(state, sx + tsf * (2 * TER12_WIDTH), + render_text(state, sx + dx + tsf * (2 * TER12_WIDTH), sy + (ptext - state->dialog_text + 1) * tsf * (TER12_HEIGHT + 2), @@ -1482,6 +1603,16 @@ render_text(struct State* state, const int x, const int y, const int font_width, } void +repeat_char(char** s, const char ch, const int num_times) +{ + int i; + assert(s != NULL); + for (i = 0; i < num_times; i++) + (*s)[i] = ch; + (*s)[i] = 0; +} + +void reset(struct State* state) { struct Cell* c = NULL; @@ -1673,16 +1804,7 @@ step_animation(struct State* state) if (check_five(state, to)) pop_strings(state); else if (state->nfree < 3) - { - state->mode = STATE_GAME_OVER; - prepare_dialog(state, GAME_OVER_DIALOG_WIDTH, - GAME_OVER_DIALOG_HEIGHT, game_over_text, - 1); - SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, - "check_five: Prepared game over " - "dialog"); - return; - } + game_over(state); else fill_triplet(state); state->moveno++; @@ -1738,7 +1860,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; @@ -1752,6 +1874,7 @@ main(int argc, char** argv) init_state(&state); 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);