чување be83635a98b692d08b7062d0e6e7d9a1dd56b987
родитељ 66d911adc541ddb9914eb1160c93dd1537f6a692
Аутор: Страхиња Радић <sr@strahinja.org>
Датум: Fri, 8 May 2026 13:53:56 +0000
Continue hall of fame WIP
Diffstat:
| M | linije.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);