linije

Unnamed repository; edit this file 'description' to name the repository.
Дневник | Датотеке | Референце | ПРОЧИТАЈМЕ | ЛИЦЕНЦА

чување d8640af43c65c9f5d3aa6bcbdcd06b664aed8463
родитељ 95c6889a695fa74daf5a7e006a82a38e8633679c
Аутор: Страхиња Радић <sr@strahinja.org>
Датум:   Sat,  9 Aug 2025 01:13:45 +0200

Add game over state; refactor code; improve text output

Diffstat:
MTODO | 2--
MTODO.done | 2++
Mlinije.c | 1393+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
измењених датотека: 3, додавања: 802(+), брисања: 595(-)

diff --git a/TODO b/TODO @@ -1,8 +1,6 @@ TODO ==== -[ ] Game over - < > High score? < > Menu? diff --git a/TODO.done b/TODO.done @@ -1,5 +1,7 @@ Done or canceled todos ====================== +[x] Game over + [x] Handle cases with multiple strings popping when moving one ball (cross sections) diff --git a/linije.c b/linije.c @@ -23,16 +23,20 @@ #define FPS 60 #define ANIM_STEPS (ANIM_SPEED * FPS / 1000) -#define BUFSIZE 4096 -#define CELL_SIZE 20 -#define DEFAULT_WIDTH 1280 -#define DEFAULT_HEIGHT 720 -#define HELP_DIALOG_WIDTH 900 -#define HELP_DIALOG_HEIGHT 450 -#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 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 /* clang-format off */ #define POP_STRING_CELL_VALUE 100 /* Multiplied by # of balls in a string and * added to score when popping a string */ @@ -63,7 +67,8 @@ enum { enum { STATE_IDLE, STATE_SELECTION, - STATE_MOVING + STATE_MOVING, + STATE_GAME_OVER }; const char* cell_names[] = { @@ -86,20 +91,28 @@ const int check_five_deltas[][4] = { const char* state_descriptions[] = { "Idle", "Selecting", - "Moving" + "Moving", + "Game over" +}; + +const char* game_over_text[] = { + "==== \1GAME OVER\2 ====", + "Press \1F2\2 to restart", + NULL }; const char* help_text[] = { - "linije - Color lines clone", - "==========================", + "\1linije - Color lines clone\2", + "\1==========================\2", "Version " VERSION, "", - "F1 - Show this help screen", - "Esc - Hide this help screen", - "Alt+Enter - Toggle fullscreen", - "C-Q - Exit", + "\1F1\2 - Show this help screen", + "\1F2\2 - Restart the game", + "\1Esc\2 - Hide this help screen", + "\1Alt+Enter\2 - Toggle fullscreen", + "\1Ctrl+Q\2 - Exit", "", - "---", + "\1---\2", "", "This program is licensed under the terms of GNU GPL v3 or (at your", "option) any later version. Copyright (C) 2025 Strahinya Radich.", @@ -148,9 +161,11 @@ struct State { int display_height; int full_screen; int location_pointer; - SDL_Point* location_stack; + SDL_FPoint* location_stack; int mode; SDL_FPoint mouse; + int moveno; + int nfree; struct Cell* path_start; struct Cell* path_end; int redraw; @@ -167,6 +182,8 @@ struct State { }; /* clang-format on */ +void add_cell(struct State* state, struct Cell* c, const int color, + const int index); void assign_pointFF(const SDL_FPoint* from, SDL_FPoint* to); void assign_pointIF(const SDL_Point* from, SDL_FPoint* to); void assign_point_xyIF(const int x, const int y, SDL_FPoint* to); @@ -174,18 +191,13 @@ void assign_point_xyII(const int x, const int y, SDL_Point* to); void calculate_delta(struct State* state); void calculate_grid_origin(const struct State* state, float* sx, float* sy); void calculate_display_size(struct State* state); -void check_five(struct State* state, struct Cell* c); +int check_five(struct State* state, struct Cell* c); void cleanup(struct State* state); void do_exit(const int code, struct State* state); void draw(struct State* state); -void draw_background(struct State* state, SDL_FRect* bg_rect); -void draw_dialog(struct State* state); -void draw_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 erase_cell(struct Cell* c); +void erase_cell(struct State* state, struct Cell* c); void fill_triplet(struct State* state); -void find_shortest_path(struct Cell* c, struct Cell* source, const int dist); +int find_shortest_path(struct Cell* c, struct Cell* source, const int dist); 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, @@ -198,17 +210,27 @@ void handle_event(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); int load_textures(struct State* state); -int location_pop(struct State* state, SDL_Point* location); -void location_push(struct State* state, const SDL_Point* location); +void location_delete(struct State* state, const int index); +int location_index(const struct State* state, const SDL_FPoint* location); +int location_pop(struct State* state, 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); -void next_step(struct State* state); int passable(const struct State* state, const int x, const int y); void pop_strings(struct State* state); void prepare_dialog(struct State* state, const int w, const int h, const char** text, 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); +void render_balls(struct State* state, const float startx, const float starty); +void render_dialog(struct State* state); +void render_selection(struct State* state, const float startx, + const float starty); +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 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, @@ -217,7 +239,22 @@ 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); -SDL_Surface* try_load(const char* filename); +SDL_Texture* try_load(SDL_Renderer* renderer, const char* filename); + +void +add_cell(struct State* state, struct Cell* c, const int color, const int index) +{ + assert((state != NULL) && (c != NULL)); + c->contents = color; + c->empty = 0; + state->nfree--; + location_delete(state, index); + SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, + "add_cell: Cell at (%0.0f, %0.0f) is %s, " + "new location_pointer is %d", + c->coords.x, c->coords.y, cell_names[c->contents], + state->location_pointer); +} void assign_pointFF(const SDL_FPoint* from, SDL_FPoint* to) @@ -307,7 +344,8 @@ calculate_display_size(struct State* state) state->display_height); } -void +/* Returns: 0 if no strings, 1 if present */ +int check_five(struct State* state, struct Cell* c) { SDL_FPoint start, end; @@ -360,20 +398,9 @@ check_five_loop: } if (len > MIN_POP_LEN) { - /*x = start.x; - y = start.y; - do - { - erase_cell(&state->grid[INDEX(x, y)]); - x += check_five_deltas[i][2]; - y += check_five_deltas[i][3]; - } while (!((x == end.x + check_five_deltas[i][2]) - && (y == end.y + check_five_deltas[i][3])));*/ - mark_five(state, &start, &end); state->score += len * POP_STRING_CELL_VALUE; found_string = 1; - //goto check_five_finish; } else { @@ -384,12 +411,13 @@ check_five_loop: if (i < sizeof(check_five_deltas) / sizeof(check_five_deltas[0])) goto check_five_loop; - if (found_string) - pop_strings(state); - else - fill_triplet(state); + SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, + "check_five: nfree = %d < 3? %s", state->nfree, + state->nfree < 3 ? "TRUE" : "FALSE"); state->mode = STATE_IDLE; + + return found_string; } void @@ -398,7 +426,6 @@ cleanup(struct State* state) assert(state != NULL); free(state->location_stack); - free(state->grid); if (state->background) SDL_DestroyTexture(state->background); @@ -424,30 +451,28 @@ do_exit(const int code, struct State* state) void draw(struct State* state) { - struct Cell* c = NULL; - SDL_FRect bg_rect, cell_rect; + SDL_FRect bg_rect; SDL_FRect dest_rect; - int x, y; - float crx, cry; - float drx, dry; float startx, starty; + float csf; assert(state != NULL); assert(state->cell_scale_factor != 0.0f); + csf = state->cell_scale_factor; calculate_grid_origin(state, &startx, &starty); bg_rect.x = 0; bg_rect.y = 0; - bg_rect.w = state->cell_scale_factor * (CELL_SIZE * GRID_WIDTH + 1); - bg_rect.h = state->cell_scale_factor * (CELL_SIZE * GRID_HEIGHT + 1); + bg_rect.w = csf * (CELL_SIZE * GRID_WIDTH + 1); + bg_rect.h = csf * (CELL_SIZE * GRID_HEIGHT + 1); SDL_SetRenderDrawColor(state->renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); SDL_RenderClear(state->renderer); /* Render background */ if (!state->background) - draw_background(state, &bg_rect); + render_background(state, &bg_rect); /* Draw buffered copy of background */ dest_rect.x = startx; @@ -458,65 +483,20 @@ draw(struct State* state) SDL_RenderTexture(state->renderer, state->background, &bg_rect, &dest_rect); - /* Draw balls */ - cell_rect.w = CELL_SIZE; - cell_rect.h = CELL_SIZE; - dest_rect.w = state->cell_scale_factor * CELL_SIZE; - dest_rect.h = state->cell_scale_factor * CELL_SIZE; - for (y = 0; y < GRID_HEIGHT; y++) - for (x = 0; x < GRID_WIDTH; x++) - { - c = &state->grid[INDEX(x, y)]; - if (c->contents == CELL_EMPTY) - continue; - - get_sprite_xy(c->contents, &crx, &cry); - cell_rect.x = crx; - cell_rect.y = cry; - if (c->path) - { - grid_coord_to_screen_coord(state, - state->animation.position.x, - state->animation.position.y, startx + 1, - starty + 1, &drx, &dry); - } - else - { - grid_coord_to_screen_coord(state, x, y, - startx + 1, starty + 1, &drx, &dry); - } - dest_rect.x = drx; - dest_rect.y = dry; - SDL_RenderTexture(state->renderer, state->sprites, - &cell_rect, &dest_rect); - } + /* Render balls */ + render_balls(state, startx, starty); - /* Draw selection */ + /* Render selection */ if (state->selection_start.x != -1) - { - get_sprite_xy(CELL_SELECTED, &crx, &cry); - cell_rect.x = crx; - cell_rect.y = cry; - grid_coord_to_screen_coord(state, state->selection_start.x, - state->selection_start.y, startx + 1, starty + 1, &drx, - &dry); - dest_rect.x = drx; - dest_rect.y = dry; - SDL_RenderTexture(state->renderer, state->sprites, &cell_rect, - &dest_rect); - } + render_selection(state, startx, starty); /* Render status */ - draw_text(state, 0, - state->screen_height - state->text_scale_factor * TER12_HEIGHT, - TER12_WIDTH, TER12_HEIGHT, state->ter12_texture, - "Score: %06u F1 = Help Status: %s", state->score, - state_descriptions[state->mode]); + render_status(state); if (state->dialog_shown) - draw_dialog(state); + render_dialog(state); - /*draw_text(600, screen_height - text_scale_factor * TER12_HEIGHT, + /*render_text(600, screen_height - text_scale_factor * TER12_HEIGHT, TER12_WIDTH, TER12_HEIGHT, ter12_texture, "(%05.2f, %05.2f)", pointer.x, pointer.y);*/ @@ -524,362 +504,198 @@ draw(struct State* state) } void -draw_background(struct State* state, SDL_FRect* bg_rect) +erase_cell(struct State* state, struct Cell* c) { - const SDL_DisplayMode* mode; - SDL_FRect cell_rect, dest_rect; - SDL_DisplayID display; - float crx, cry; - float drx, dry; - int x, y; + assert((state != NULL) && (c != NULL)); + c->contents = CELL_EMPTY; + c->empty = 1; + location_push(state, &c->coords); +} - assert((state != NULL) && (bg_rect != NULL)); - assert(state->cell_scale_factor != 0.0f); +void +fill_triplet(struct State* state) +{ + struct Cell* c = NULL; + SDL_FPoint* loc = NULL; + int idx; + int pass; + char color; - if ((display = SDL_GetDisplayForWindow(state->window)) == 0) + pass = 0; +fill_triplet_generate: + if (state->location_pointer == 0) + goto fill_triplet_fail; + idx = random() % state->location_pointer; + SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, + "fill_triplet: idx=%d/%d (%0.0f, %0.0f)", idx, + state->location_pointer, state->location_stack[idx].x, + state->location_stack[idx].y); + loc = &state->location_stack[idx]; + c = &state->grid[INDEXFI(loc->x, loc->y)]; + + /* Should never happen */ + if (c->contents != CELL_EMPTY) { - SDL_LogError(SDL_LOG_CATEGORY_ERROR, - "SDL_GetDisplayForWindow failed: %s", SDL_GetError()); + SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, + "fill_triplet: Should not happen! Contents of cell " + "at (%0.0f, %0.0f) is %s", + c->coords.x, c->coords.y, cell_names[c->contents]); do_exit(1, state); + goto fill_triplet_generate; } - if ((mode = SDL_GetCurrentDisplayMode(display)) == NULL) + switch (random() % 5) { - SDL_LogError(SDL_LOG_CATEGORY_ERROR, - "SDL_GetCurrentDisplayMode failed: %s", SDL_GetError()); - do_exit(1, state); + case 0: + color = CELL_RED; + break; + case 1: + color = CELL_BLUE; + break; + case 2: + color = CELL_GREEN; + break; + case 3: + color = CELL_PURPLE; + break; + default: + color = CELL_ORANGE; } - - state->background = SDL_CreateTexture(state->renderer, mode->format, - SDL_TEXTUREACCESS_TARGET, bg_rect->w, bg_rect->h); - if (!state->background) + add_cell(state, c, color, idx); + if (check_five(state, c)) { - SDL_LogError(SDL_LOG_CATEGORY_ERROR, - "SDL_CreateTexture failed: %s", SDL_GetError()); - do_exit(1, state); + pop_strings(state); + goto fill_triplet_generate; } - if (!SDL_SetRenderTarget(state->renderer, state->background)) + if (state->nfree < 3) { - SDL_LogError(SDL_LOG_CATEGORY_ERROR, - "SDL_SetRenderTarget failed: %s", SDL_GetError()); - do_exit(1, state); + state->mode = STATE_GAME_OVER; + prepare_dialog(state, GAME_OVER_DIALOG_WIDTH, + GAME_OVER_DIALOG_HEIGHT, game_over_text, 1); + return; } + pass++; + SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, + "fill_triplet: %s at (%0.0f, %0.0f)", cell_names[(int)color], + c->coords.x, c->coords.y); + if (pass < 3) + goto fill_triplet_generate; - dest_rect.w = state->cell_scale_factor * CELL_SIZE; - dest_rect.h = state->cell_scale_factor * CELL_SIZE; - get_sprite_xy(CELL_EMPTY, &crx, &cry); - cell_rect.x = crx; - cell_rect.y = cry; - cell_rect.w = CELL_SIZE; - cell_rect.h = CELL_SIZE; - for (y = 0; y < GRID_HEIGHT; y++) - for (x = 0; x < GRID_WIDTH; x++) - { - grid_coord_to_screen_coord(state, x, y, 0.0f, 0.0f, - &drx, &dry); - dest_rect.x = state->cell_scale_factor + drx; - dest_rect.y = state->cell_scale_factor + dry; - SDL_RenderTexture(state->renderer, state->sprites, - &cell_rect, &dest_rect); - } + return; - /* Draw upper border */ - for (x = -1; x < GRID_WIDTH; x++) +fill_triplet_fail: + 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, + "fill_triplet: Prepared game over dialog"); +} + +int +find_shortest_path(struct Cell* c, struct Cell* source, const int dist) +{ + int result = 0; + + if (!c || c->path_dist <= dist) + return 0; + + if (!c->empty && !c->starting && !c->ending) + return 0; + + if (c->starting) + c->path_dist = dist; + + if (c->path_dist > dist) { - grid_coord_to_screen_coord(state, x, -1, 0.0f, 0.0f, &drx, &dry); - dest_rect.x = state->cell_scale_factor + drx; - dest_rect.y = state->cell_scale_factor + dry; - SDL_RenderTexture(state->renderer, state->sprites, &cell_rect, - &dest_rect); + c->path_dist = dist; + c->path_prev = source; } - /* Draw left border */ - for (y = 0; y < GRID_WIDTH; y++) + if (c->ending) + return 1; + + result |= find_shortest_path(c->left, c, dist + 1); + result |= find_shortest_path(c->up, c, dist + 1); + result |= find_shortest_path(c->right, c, dist + 1); + result |= find_shortest_path(c->down, c, dist + 1); + + return result; +} + +void +get_sprite_xy(const char cell, float* x, float* y) +{ + assert((x != NULL) && (y != NULL)); + switch (cell) { - grid_coord_to_screen_coord(state, -1, y, 0.0f, 0.0f, &drx, &dry); - dest_rect.x = state->cell_scale_factor + drx; - dest_rect.y = state->cell_scale_factor + dry; - SDL_RenderTexture(state->renderer, state->sprites, &cell_rect, - &dest_rect); + case CELL_RED: + *x = 1 * CELL_SIZE; + *y = 0; + break; + case CELL_BLUE: + *x = 2 * CELL_SIZE; + *y = 0; + break; + case CELL_GREEN: + *x = 3 * CELL_SIZE; + *y = 0; + break; + case CELL_PURPLE: + *x = 4 * CELL_SIZE; + *y = 0; + break; + case CELL_ORANGE: + *x = 5 * CELL_SIZE; + *y = 0; + break; + case CELL_SELECTED: + *x = 0; + *y = 1 * CELL_SIZE; + break; + default: + *x = 0; + *y = 0; } + /*SDL_LogDebug("get_sprite_xy: %d @ (%0.0f, %0.0f)", + cell, *x, *y);*/ +} - SDL_SetRenderDrawBlendMode(state->renderer, SDL_BLENDMODE_NONE); - SDL_SetRenderDrawColor(state->renderer, 255, 255, 255, SDL_ALPHA_OPAQUE); +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, float* to_y) +{ + assert((state != NULL) && (to_x != NULL) && (to_y != NULL)); + *to_x = startx + x * state->cell_scale_factor * CELL_SIZE; + *to_y = starty + y * state->cell_scale_factor * CELL_SIZE; +} - SDL_SetRenderTarget(state->renderer, NULL); +void +grid_coord_to_screen_coordF(const struct State* state, const float x, + const float y, const float startx, const float starty, float* to_x, + float* to_y) +{ + assert((state != NULL) && (to_x != NULL) && (to_y != NULL)); + *to_x = startx + x * state->cell_scale_factor * CELL_SIZE; + *to_y = starty + y * state->cell_scale_factor * CELL_SIZE; } -/* state is not const because of renderer, etc */ +static char* cell_image[] = {"[ ]", "[R]", "[B]", "[G]", "[P]", "[O]", "[/]", + "[S]", "[E]", "[.]"}; + +#define CELL_START 7 +#define CELL_END 8 +#define CELL_PATH 9 + void -draw_dialog(struct State* state) +handle_click(struct State* state) { - const char** ptext = NULL; - SDL_FRect dialog_rect; - float tsf; - int w, h; - int sx, sy; + float startx, starty; assert(state != NULL); - assert(state->dialog_text != NULL); - ptext = state->dialog_text; - w = state->dialog_dims.x; - h = state->dialog_dims.y; - sx = state->screen_width / 2 - w / 2; - sy = state->screen_height / 2 - h / 2; - tsf = state->text_scale_factor; - - dialog_rect.x = sx; - dialog_rect.y = sy; - dialog_rect.w = w; - dialog_rect.h = h; - - SDL_SetRenderDrawBlendMode(state->renderer, SDL_BLENDMODE_BLEND); - SDL_SetRenderDrawColor(state->renderer, 0, 0, 0, .75 * 255); - SDL_RenderFillRect(state->renderer, &dialog_rect); - - SDL_SetRenderDrawBlendMode(state->renderer, SDL_BLENDMODE_NONE); - SDL_SetRenderDrawColor(state->renderer, 255, 255, 255, SDL_ALPHA_OPAQUE); - SDL_RenderRect(state->renderer, &dialog_rect); - SDL_SetRenderDrawColor(state->renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); - dialog_rect.x--; - dialog_rect.y--; - dialog_rect.w += 2; - dialog_rect.h += 2; - SDL_RenderRect(state->renderer, &dialog_rect); - dialog_rect.x--; - dialog_rect.y--; - dialog_rect.w += 2; - dialog_rect.h += 2; - SDL_RenderRect(state->renderer, &dialog_rect); - - while (*ptext) - { - draw_text(state, sx + tsf * (2 * TER12_WIDTH), - sy - + (ptext - state->dialog_text + 1) * tsf - * (TER12_HEIGHT + 2), - TER12_WIDTH, TER12_HEIGHT, state->ter12_texture, - *ptext); - ptext++; - } -} - -void -draw_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, ...) -{ - char buf[PRINT_BUFSIZE]; - va_list args; - const char* pbuf = NULL; - SDL_FRect source_rect; - SDL_FRect dest_rect; - float tsf; - int xstart = 0; - int ystart = 0; - int index = 0; - int chars_per_row = 0; - char first_char = ' '; - char last_char = '~'; - float tex_w, tex_h; - - assert((font_tex != NULL) && (text != NULL)); - assert(state->text_scale_factor != 0.0f); - assert(font_width != -1); - - tsf = state->text_scale_factor; - - va_start(args, text); - vsnprintf(buf, sizeof(buf), text, args); - va_end(args); - - source_rect.w = font_width; - source_rect.h = font_height; - - dest_rect.x = x; - dest_rect.y = y; - dest_rect.w = tsf * font_width; - dest_rect.h = tsf * font_height; - - if (!SDL_GetTextureSize(font_tex, &tex_w, &tex_h)) - { - SDL_LogError(SDL_LOG_CATEGORY_ERROR, - "SDL_GetTextureSize failed: %s", SDL_GetError()); - do_exit(1, state); - } - - chars_per_row = tex_w / (font_width + 1); - - pbuf = buf; - while (*pbuf) - { - if (*pbuf >= first_char && *pbuf <= last_char) - { - index = (int)(*pbuf - first_char); - - xstart = 1 + index % chars_per_row * (font_width + 1); - ystart = 1 + index / chars_per_row * (font_height + 1); - } - else - break; - - source_rect.x = xstart; - source_rect.y = ystart; - - SDL_RenderTexture(state->renderer, font_tex, &source_rect, - &dest_rect); - - dest_rect.x += tsf * font_width; - - pbuf++; - } -} - -void -erase_cell(struct Cell* c) -{ - assert(c != NULL); - c->contents = CELL_EMPTY; - c->empty = 1; -} - -void -fill_triplet(struct State* state) -{ - int x, y; - int pass; - char color; - - pass = 0; -fill_triplet_generate: - x = random() % GRID_WIDTH; - y = random() % GRID_HEIGHT; - if (state->grid[INDEX(x, y)].contents != CELL_EMPTY) - goto fill_triplet_generate; - - switch (random() % 5) - { - case 0: - color = CELL_RED; - break; - case 1: - color = CELL_BLUE; - break; - case 2: - color = CELL_GREEN; - break; - case 3: - color = CELL_PURPLE; - break; - default: - color = CELL_ORANGE; - } - state->grid[INDEX(x, y)].contents = color; - state->grid[INDEX(x, y)].empty = 0; - pass++; - SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, - "fill_triplet: %s at (%d, %d)", cell_names[(int)color], x, y); - if (pass < 3) - goto fill_triplet_generate; -} - -void -find_shortest_path(struct Cell* c, struct Cell* source, const int dist) -{ - if (!c || c->path_dist <= dist) - return; - - if (!c->empty && !c->starting && !c->ending) - return; - - if (c->starting) - c->path_dist = dist; - - if (c->path_dist > dist) - { - c->path_dist = dist; - c->path_prev = source; - } - - if (c->ending) + if (state->dialog_shown) return; - find_shortest_path(c->left, c, dist + 1); - find_shortest_path(c->up, c, dist + 1); - find_shortest_path(c->right, c, dist + 1); - find_shortest_path(c->down, c, dist + 1); -} - -void -get_sprite_xy(const char cell, float* x, float* y) -{ - assert((x != NULL) && (y != NULL)); - switch (cell) - { - case CELL_RED: - *x = 1 * CELL_SIZE; - *y = 0; - break; - case CELL_BLUE: - *x = 2 * CELL_SIZE; - *y = 0; - break; - case CELL_GREEN: - *x = 3 * CELL_SIZE; - *y = 0; - break; - case CELL_PURPLE: - *x = 4 * CELL_SIZE; - *y = 0; - break; - case CELL_ORANGE: - *x = 5 * CELL_SIZE; - *y = 0; - break; - case CELL_SELECTED: - *x = 0; - *y = 1 * CELL_SIZE; - break; - default: - *x = 0; - *y = 0; - } - /*SDL_LogDebug("get_sprite_xy: %d @ (%0.0f, %0.0f)", - cell, *x, *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, float* to_y) -{ - assert((state != NULL) && (to_x != NULL) && (to_y != NULL)); - *to_x = startx + x * state->cell_scale_factor * CELL_SIZE; - *to_y = starty + y * state->cell_scale_factor * CELL_SIZE; -} - -void -grid_coord_to_screen_coordF(const struct State* state, const float x, - const float y, const float startx, const float starty, float* to_x, - float* to_y) -{ - assert((state != NULL) && (to_x != NULL) && (to_y != NULL)); - *to_x = startx + x * state->cell_scale_factor * CELL_SIZE; - *to_y = starty + y * state->cell_scale_factor * CELL_SIZE; -} - -static char* cell_image[] = {"[ ]", "[R]", "[B]", "[G]", "[P]", "[O]", "[/]", - "[S]", "[E]", "[.]"}; - -#define CELL_START 7 -#define CELL_END 8 -#define CELL_PATH 9 - -void -handle_click(struct State* state) -{ - float startx, starty; - - assert(state != NULL); if (state->mode == STATE_IDLE) { calculate_grid_origin(state, &startx, &starty); @@ -926,6 +742,7 @@ handle_click(struct State* state) state->path_end = &state->grid[INDEXFI(state->selection_end.x, state->selection_end.y)]; + assert((state->path_start != NULL) && (state->path_end != NULL)); if (!state->path_end->empty) goto handle_click_reset; @@ -948,9 +765,8 @@ handle_click(struct State* state) printf("M =\n"); print_matrix(state->grid); - /* Should never happen */ if (!state->path_start->path_next) - goto handle_click_reset; + return; state->animation.end.x = state->path_start->path_next->coords.x; state->animation.end.y = state->path_start->path_next->coords.y; @@ -987,7 +803,7 @@ handle_event(struct State* state, SDL_Event* event) { /* assert(3) within the SDL_PollEvent(3) loop hangs the entire system * when given an argument which evaluates to zero, so we are taking the - * risk here (and only here) */ + * risk of not using assert(3) here (and only here) */ /*assert((state != NULL) && (event != NULL));*/ switch (event->type) @@ -1007,12 +823,28 @@ handle_event(struct State* state, SDL_Event* event) switch (event->key.key) { case SDLK_F1: + if (state->dialog_shown) + break; prepare_dialog(state, HELP_DIALOG_WIDTH, HELP_DIALOG_HEIGHT, help_text, 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; + state->mode = STATE_GAME_OVER; + prepare_dialog(state, GAME_OVER_DIALOG_WIDTH, + GAME_OVER_DIALOG_HEIGHT, game_over_text, 1); + break; case SDLK_RETURN: if (!(event->key.mod & SDL_KMOD_ALT)) break; @@ -1022,8 +854,13 @@ handle_event(struct State* state, SDL_Event* event) 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->dialog_text != help_text) + break; state->dialog_shown = 0; state->dialog_text = NULL; assign_point_xyII(-1, -1, &state->dialog_dims); @@ -1106,6 +943,8 @@ init_state(struct State* state) 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; @@ -1126,45 +965,53 @@ init_state(struct State* state) int load_textures(struct State* state) { - SDL_Surface* surface = NULL; SDL_ScaleMode scale_mode = SDL_SCALEMODE_PIXELART; assert(state != NULL); - surface = try_load(SPRITES_PNG); - if (!surface) - return -1; - state->sprites = SDL_CreateTextureFromSurface(state->renderer, surface); + state->sprites = try_load(state->renderer, SPRITES_PNG); if (!state->sprites) - { - SDL_LogError(SDL_LOG_CATEGORY_ERROR, - "SDL_CreateTextureFromSurface failed: %s", - SDL_GetError()); - do_exit(1, state); - } + return -1; SDL_SetTextureScaleMode(state->sprites, scale_mode); - SDL_DestroySurface(surface); - surface = try_load(TER12_PNG); - if (!surface) - return -1; - state->ter12_texture - = SDL_CreateTextureFromSurface(state->renderer, surface); + state->ter12_texture = try_load(state->renderer, TER12_PNG); if (!state->ter12_texture) - { - SDL_LogError(SDL_LOG_CATEGORY_ERROR, - "SDL_CreateTextureFromSurface failed: %s", - SDL_GetError()); - do_exit(1, state); - } + return -1; SDL_SetTextureScaleMode(state->ter12_texture, scale_mode); - SDL_DestroySurface(surface); return 0; } +void +location_delete(struct State* state, const int index) +{ + assert(state != NULL); + if (state->location_pointer == 0 || index < 0 + || index >= state->location_pointer) + return; + assign_pointFF(&state->location_stack[state->location_pointer - 1], + &state->location_stack[index]); + state->location_pointer--; +} + int -location_pop(struct State* state, SDL_Point* location) +location_index(const struct State* state, const SDL_FPoint* location) +{ + SDL_FPoint* p = NULL; + int i; + + assert((state != NULL) && (location != NULL)); + for (i = 0; i < state->location_pointer; i++) + { + p = &state->location_stack[i]; + if (p->x == location->x && p->y == location->y) + return i; + } + return -1; +} + +int +location_pop(struct State* state, SDL_FPoint* location) { assert((state != NULL) && (location != NULL)); if (state->location_pointer == 0) @@ -1175,172 +1022,503 @@ location_pop(struct State* state, SDL_Point* location) return 0; } -void -location_push(struct State* state, const SDL_Point* location) -{ - assert(location != NULL); - state->location_stack[state->location_pointer].x = location->x; - state->location_stack[state->location_pointer].y = location->y; - state->location_pointer++; +void +location_push(struct State* state, const SDL_FPoint* location) +{ + assert((state != NULL) && (location != NULL)); + state->location_stack[state->location_pointer].x = location->x; + state->location_stack[state->location_pointer].y = location->y; + state->location_pointer++; +} + +void +mark_five(struct State* state, SDL_FPoint* start, SDL_FPoint* end) +{ + struct Cell* c = NULL; + float dx, dy, x, y; + + assert((state != NULL) && (start != NULL) && (end != NULL)); + assert(!((start->x == end->x) && (start->y == end->y))); + + dx = end->x - start->x; + dy = end->y - start->y; + if (dx != 0.0f) + dx = dx / fabsf(dx); + if (dy != 0.0f) + dy = dy / fabsf(dy); + SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, + "mark_five: String[%d], from (%0.0f, %0.0f) to (%0.0f, " + "%0.0f)", + (int)floor( + MAX(fabsf(end->x - start->x), fabsf(end->y - start->y))) + + 1, + start->x, start->y, end->x, end->y); + x = start->x; + y = start->y; + do + { + c = &state->grid[INDEXFI(x, y)]; + c->popping = 1; + x += dx; + y += dy; + /* clang-format off */ + } + while (!((x == end->x + dx) && (y == end->y + dy))); + /* clang-format on */ +} + +void +mark_path(struct Cell* c) +{ + if (!c) + return; + + c->path = 1; + if (c->path_prev) + { + c->path_prev->path_next = c; + SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, + "mark_path: Next of (%0.0f, %0.0f) is (%0.0f, %0.0f)", + c->path_prev->coords.x, c->path_prev->coords.y, + c->path_prev->path_next->coords.x, + c->path_prev->path_next->coords.y); + } + mark_path(c->path_prev); +} + +int +passable(const struct State* state, const int x, const int y) +{ + assert(state != NULL); + return x >= 0 && x < GRID_WIDTH && y >= 0 && y < GRID_HEIGHT + && state->grid[INDEX(x, y)].contents == CELL_EMPTY; +} + +void +pop_strings(struct State* state) +{ + struct Cell* c = NULL; + int x, y; + + assert(state != NULL); + for (y = 0; y < GRID_HEIGHT; y++) + for (x = 0; x < GRID_WIDTH; x++) + { + c = &state->grid[INDEX(x, y)]; + if (c->popping) + { + erase_cell(state, c); + state->nfree++; + c->popping = 0; + } + } +} + +void +prepare_dialog(struct State* state, const int w, const int h, const char** text, + const int show) +{ + assert((state != NULL) && (text != NULL)); + + assign_point_xyII(w, h, &state->dialog_dims); + state->dialog_text = text; + state->dialog_shown = show; + if (show) + state->redraw = 1; +} + +void +print_matrix(const struct Cell* M) +{ + const struct Cell* c = NULL; + int x, y; + + for (y = 0; y < GRID_HEIGHT; y++) + { + for (x = 0; x < GRID_WIDTH; x++) + { + c = &M[INDEX(x, y)]; + if (c->starting) + printf("%s", cell_image[CELL_START]); + else if (c->ending) + printf("%s", cell_image[CELL_END]); + else if (c->path) + printf("%s", cell_image[CELL_PATH]); + else if (!c->empty) + printf("%s", cell_image[c->contents]); + else + printf("%s", cell_image[CELL_EMPTY]); + } + printf("\n"); + } + // printf("\n"); +} + +void +print_path(struct Cell* c) +{ + printf("(%0.0f, %0.0f)", c->coords.x, c->coords.y); + if (c->path_next) + { + printf(" -> "); + print_path(c->path_next); + } + else + printf("\n"); +} + +void +render_background(struct State* state, SDL_FRect* bg_rect) +{ + const SDL_DisplayMode* mode; + SDL_FRect cell_rect, dest_rect; + SDL_DisplayID display; + float crx, cry; + float drx, dry; + int x, y; + + assert((state != NULL) && (bg_rect != NULL)); + assert(state->cell_scale_factor != 0.0f); + + if ((display = SDL_GetDisplayForWindow(state->window)) == 0) + { + SDL_LogError(SDL_LOG_CATEGORY_ERROR, + "SDL_GetDisplayForWindow failed: %s", SDL_GetError()); + do_exit(1, state); + } + + if ((mode = SDL_GetCurrentDisplayMode(display)) == NULL) + { + SDL_LogError(SDL_LOG_CATEGORY_ERROR, + "SDL_GetCurrentDisplayMode failed: %s", SDL_GetError()); + do_exit(1, state); + } + + state->background = SDL_CreateTexture(state->renderer, mode->format, + SDL_TEXTUREACCESS_TARGET, bg_rect->w, bg_rect->h); + if (!state->background) + { + SDL_LogError(SDL_LOG_CATEGORY_ERROR, + "SDL_CreateTexture failed: %s", SDL_GetError()); + do_exit(1, state); + } + if (!SDL_SetRenderTarget(state->renderer, state->background)) + { + SDL_LogError(SDL_LOG_CATEGORY_ERROR, + "SDL_SetRenderTarget failed: %s", SDL_GetError()); + do_exit(1, state); + } + + dest_rect.w = state->cell_scale_factor * CELL_SIZE; + dest_rect.h = state->cell_scale_factor * CELL_SIZE; + get_sprite_xy(CELL_EMPTY, &crx, &cry); + cell_rect.x = crx; + cell_rect.y = cry; + cell_rect.w = CELL_SIZE; + cell_rect.h = CELL_SIZE; + for (y = 0; y < GRID_HEIGHT; y++) + for (x = 0; x < GRID_WIDTH; x++) + { + grid_coord_to_screen_coord(state, x, y, 0.0f, 0.0f, + &drx, &dry); + dest_rect.x = state->cell_scale_factor + drx; + dest_rect.y = state->cell_scale_factor + dry; + SDL_RenderTexture(state->renderer, state->sprites, + &cell_rect, &dest_rect); + } + + /* Draw upper border */ + for (x = -1; x < GRID_WIDTH; x++) + { + grid_coord_to_screen_coord(state, x, -1, 0.0f, 0.0f, &drx, &dry); + dest_rect.x = state->cell_scale_factor + drx; + dest_rect.y = state->cell_scale_factor + dry; + SDL_RenderTexture(state->renderer, state->sprites, &cell_rect, + &dest_rect); + } + + /* Draw left border */ + for (y = 0; y < GRID_WIDTH; y++) + { + grid_coord_to_screen_coord(state, -1, y, 0.0f, 0.0f, &drx, &dry); + dest_rect.x = state->cell_scale_factor + drx; + dest_rect.y = state->cell_scale_factor + dry; + SDL_RenderTexture(state->renderer, state->sprites, &cell_rect, + &dest_rect); + } + + SDL_SetRenderDrawBlendMode(state->renderer, SDL_BLENDMODE_NONE); + SDL_SetRenderDrawColor(state->renderer, 255, 255, 255, SDL_ALPHA_OPAQUE); + + SDL_SetRenderTarget(state->renderer, NULL); +} + +void +render_balls(struct State* state, const float startx, const float starty) +{ + struct Cell* c = NULL; + SDL_FRect cell_rect, dest_rect; + float csf; + float crx, cry; + float drx, dry; + int x, y; + + assert(state != NULL); + assert(state->cell_scale_factor != 0.0f); + + csf = state->cell_scale_factor; + cell_rect.w = CELL_SIZE; + cell_rect.h = CELL_SIZE; + dest_rect.w = csf * CELL_SIZE; + dest_rect.h = csf * CELL_SIZE; + for (y = 0; y < GRID_HEIGHT; y++) + for (x = 0; x < GRID_WIDTH; x++) + { + c = &state->grid[INDEX(x, y)]; + if (c->contents == CELL_EMPTY) + continue; + + get_sprite_xy(c->contents, &crx, &cry); + cell_rect.x = crx; + cell_rect.y = cry; + if (c->path) + grid_coord_to_screen_coord(state, + state->animation.position.x, + state->animation.position.y, startx + 1, + starty + 1, &drx, &dry); + else + grid_coord_to_screen_coord(state, x, y, + startx + 1, starty + 1, &drx, &dry); + dest_rect.x = drx; + dest_rect.y = dry; + SDL_RenderTexture(state->renderer, state->sprites, + &cell_rect, &dest_rect); + } } +/* state is not const because of renderer, etc */ void -mark_five(struct State* state, SDL_FPoint* start, SDL_FPoint* end) +render_dialog(struct State* state) { - struct Cell* c = NULL; - float dx, dy, x, y; + const char** ptext = NULL; + SDL_FRect dialog_rect; + float tsf; + int w, h; + int sx, sy; - assert((state != NULL) && (start != NULL) && (end != NULL)); - assert(!((start->x == end->x) && (start->y == end->y))); + assert(state != NULL); + assert(state->dialog_text != NULL); - dx = end->x - start->x; - dy = end->y - start->y; - if (dx != 0.0f) - dx = dx / fabsf(dx); - if (dy != 0.0f) - dy = dy / fabsf(dy); - SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, - "check_five: String[%d], from (%0.0f, %0.0f) to (%0.0f, " - "%0.0f)", - (int)floor(MAX(fabsf(end->x - start->x), - fabsf(end->y - start->y)))+1, - start->x, start->y, end->x, end->y); - x = start->x; - y = start->y; - do - { - c = &state->grid[INDEXFI(x, y)]; - c->popping = 1; - x += dx; - y += dy; - } - while(!((x == end->x + dx) && (y == end->y + dy))); -} + ptext = state->dialog_text; + w = state->dialog_dims.x; + h = state->dialog_dims.y; + sx = state->screen_width / 2 - w / 2; + sy = state->screen_height / 2 - h / 2; + tsf = state->text_scale_factor; -void -mark_path(struct Cell* c) -{ - if (!c) - return; + dialog_rect.x = sx; + dialog_rect.y = sy; + dialog_rect.w = w; + dialog_rect.h = h; - c->path = 1; - if (c->path_prev) + SDL_SetRenderDrawBlendMode(state->renderer, SDL_BLENDMODE_BLEND); + SDL_SetRenderDrawColor(state->renderer, 0, 0, 0, .75 * 255); + SDL_RenderFillRect(state->renderer, &dialog_rect); + + SDL_SetRenderDrawBlendMode(state->renderer, SDL_BLENDMODE_NONE); + SDL_SetRenderDrawColor(state->renderer, 255, 255, 255, SDL_ALPHA_OPAQUE); + SDL_RenderRect(state->renderer, &dialog_rect); + SDL_SetRenderDrawColor(state->renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); + dialog_rect.x--; + dialog_rect.y--; + dialog_rect.w += 2; + dialog_rect.h += 2; + SDL_RenderRect(state->renderer, &dialog_rect); + dialog_rect.x--; + dialog_rect.y--; + dialog_rect.w += 2; + dialog_rect.h += 2; + SDL_RenderRect(state->renderer, &dialog_rect); + + while (*ptext) { - c->path_prev->path_next = c; - SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, - "mark_path: Next of (%0.0f, %0.0f) is (%0.0f, %0.0f)", - c->path_prev->coords.x, c->path_prev->coords.y, - c->path_prev->path_next->coords.x, - c->path_prev->path_next->coords.y); + render_text(state, sx + tsf * (2 * TER12_WIDTH), + sy + + (ptext - state->dialog_text + 1) * tsf + * (TER12_HEIGHT + 2), + TER12_WIDTH, TER12_HEIGHT, state->ter12_texture, + *ptext); + ptext++; } - mark_path(c->path_prev); } void -next_step(struct State* state) +render_selection(struct State* state, const float startx, const float starty) { - assert(state != NULL); - state->redraw = 1; -} + SDL_FRect cell_rect, dest_rect; + float crx, cry; + float drx, dry; + float csf; -int -passable(const struct State* state, const int x, const int y) -{ assert(state != NULL); - return x >= 0 && x < GRID_WIDTH && y >= 0 && y < GRID_HEIGHT - && state->grid[INDEX(x, y)].contents == CELL_EMPTY; + + csf = state->cell_scale_factor; + get_sprite_xy(CELL_SELECTED, &crx, &cry); + cell_rect.x = crx; + cell_rect.y = cry; + cell_rect.w = CELL_SIZE; + cell_rect.h = CELL_SIZE; + grid_coord_to_screen_coord(state, state->selection_start.x, + state->selection_start.y, startx + 1, starty + 1, &drx, &dry); + dest_rect.x = drx; + dest_rect.y = dry; + dest_rect.w = csf * CELL_SIZE; + dest_rect.h = csf * CELL_SIZE; + SDL_RenderTexture(state->renderer, state->sprites, &cell_rect, + &dest_rect); } void -pop_strings(struct State* state) +render_status(struct State* state) { - struct Cell* c = NULL; - int x, y; + float h, tsf; + int maxw = 30; assert(state != NULL); - for (y = 0; y < GRID_HEIGHT; y++) - for (x = 0; x < GRID_WIDTH; x++) - { - c = &state->grid[INDEX(x, y)]; - if (c->popping) - { - erase_cell(c); - c->popping = 0; - } - } -} -void -prepare_dialog(struct State* state, const int w, const int h, const char** text, - const int show) -{ - assert((state != NULL) && (text != NULL)); + h = state->screen_height; + tsf = state->text_scale_factor; - assign_point_xyII(w, h, &state->dialog_dims); - state->dialog_text = text; - state->dialog_shown = show; - if (show) - state->redraw = 1; + render_text(state, 0, h - tsf * TER12_HEIGHT, TER12_WIDTH, TER12_HEIGHT, + state->ter12_texture, "Score: \1%u\2", state->score); + render_text(state, maxw * TER12_WIDTH, h - tsf * TER12_HEIGHT, + TER12_WIDTH, TER12_HEIGHT, state->ter12_texture, + "\1F1\2 = Help"); + render_text(state, 2 * maxw * TER12_WIDTH, h - tsf * TER12_HEIGHT, + TER12_WIDTH, TER12_HEIGHT, state->ter12_texture, "Move: \1%u\2", + state->moveno); + render_text(state, 3 * maxw * TER12_WIDTH, h - tsf * TER12_HEIGHT, + TER12_WIDTH, TER12_HEIGHT, state->ter12_texture, "Free: \1%u\2", + state->nfree); + render_text(state, 4 * maxw * TER12_WIDTH, h - tsf * TER12_HEIGHT, + TER12_WIDTH, TER12_HEIGHT, state->ter12_texture, + "Status: \1%s\2", state_descriptions[state->mode]); } void -print_matrix(const struct Cell* M) +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, ...) { - const struct Cell* c = NULL; - int x, y; + char buf[PRINT_BUFSIZE]; + va_list args; + const char* pbuf = NULL; + SDL_FRect source_rect; + SDL_FRect dest_rect; + float tsf; + int xstart = 0; + int ystart = 0; + int index = 0; + int chars_per_row = 0; + char first_char = ' '; + char last_char = '~'; + float tex_w, tex_h; + float opacity = 0.4f; - for (y = 0; y < GRID_HEIGHT; y++) + assert((font_tex != NULL) && (text != NULL)); + assert(state->text_scale_factor != 0.0f); + assert(font_width != -1); + + tsf = state->text_scale_factor; + + va_start(args, text); + vsnprintf(buf, sizeof(buf), text, args); + va_end(args); + + source_rect.w = font_width; + source_rect.h = font_height; + + dest_rect.x = x; + dest_rect.y = y; + dest_rect.w = tsf * font_width; + dest_rect.h = tsf * font_height; + + if (!SDL_GetTextureSize(font_tex, &tex_w, &tex_h)) { - for (x = 0; x < GRID_WIDTH; x++) - { - c = &M[INDEX(x, y)]; - if (c->starting) - printf("%s", cell_image[CELL_START]); - else if (c->ending) - printf("%s", cell_image[CELL_END]); - else if (c->path) - printf("%s", cell_image[CELL_PATH]); - else if (!c->empty) - printf("%s", cell_image[c->contents]); - else - printf("%s", cell_image[CELL_EMPTY]); - } - printf("\n"); + SDL_LogError(SDL_LOG_CATEGORY_ERROR, + "SDL_GetTextureSize failed: %s", SDL_GetError()); + do_exit(1, state); } - // printf("\n"); -} -void -print_path(struct Cell* c) -{ - printf("(%0.0f, %0.0f)", c->coords.x, c->coords.y); - if (c->path_next) + SDL_SetTextureBlendMode(font_tex, SDL_BLENDMODE_BLEND); + + chars_per_row = tex_w / (font_width + 1); + + pbuf = buf; + while (*pbuf) { - printf(" -> "); - print_path(c->path_next); + if (*pbuf >= first_char && *pbuf <= last_char) + { + index = (int)(*pbuf - first_char); + + xstart = 1 + index % chars_per_row * (font_width + 1); + ystart = 1 + index / chars_per_row * (font_height + 1); + } + else if (*pbuf == BOLD_START) + { + opacity = 0.9f; + pbuf++; + continue; + } + else if (*pbuf == BOLD_END) + { + opacity = 0.4f; + pbuf++; + continue; + } + else + break; + + source_rect.x = xstart; + source_rect.y = ystart; + + if (!SDL_SetTextureAlphaModFloat(font_tex, opacity)) + SDL_LogError(SDL_LOG_CATEGORY_ERROR, + "SDL_SetTextureAlphaModFloat is not supported"); + SDL_RenderTexture(state->renderer, font_tex, &source_rect, + &dest_rect); + + dest_rect.x += tsf * font_width; + + pbuf++; } - else - printf("\n"); } void reset(struct State* state) { struct Cell* c = NULL; + int x, y; - for (int y = 0; y < GRID_HEIGHT; y++) - for (int x = 0; x < GRID_WIDTH; x++) + state->location_pointer = 0; + for (y = 0; y < GRID_HEIGHT; y++) + for (x = 0; x < GRID_WIDTH; x++) { c = &state->grid[INDEX(x, y)]; c->contents = CELL_EMPTY; c->popping = 0; + location_push(state, &c->coords); } + + state->dialog_text = NULL; + state->dialog_shown = 0; state->mode = STATE_IDLE; + state->moveno = 1; + state->nfree = GRID_WIDTH * GRID_HEIGHT; + state->score = 0; state->selection_start.x = -1; state->selection_start.y = -1; state->selection_end.x = -1; state->selection_end.y = -1; - state->location_pointer = 0; state->animation.active = 0; state->redraw = 1; } @@ -1438,6 +1616,7 @@ step_animation(struct State* state) struct Cell* from = NULL; struct Cell* to = NULL; int isx, isy, iex, iey; + int idx; if (state->animation.step == state->animation.final_step) { @@ -1480,11 +1659,19 @@ step_animation(struct State* state) "End of path at (%0.0f, %0.0f)", state->animation.end.x, state->animation.end.y); state->animation.active = 0; + state->redraw = 1; } SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "grid[%d][%d] = %d <-> grid[%d][%d] = %d", iey, iex, to->contents, isy, isx, from->contents); + idx = location_index(state, &to->coords); + SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, + "location_index: Index of (%0.0f, %0.0f) is %d", + to->coords.x, to->coords.y, idx); + assert(idx != -1); + location_delete(state, idx); + location_push(state, &from->coords); to->contents = from->contents; from->contents = CELL_EMPTY; to->empty = 0; @@ -1496,7 +1683,22 @@ step_animation(struct State* state) state->path_start = NULL; state->path_end = NULL; - check_five(state, to); + 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; + } + else + fill_triplet(state); + state->moveno++; } } @@ -1512,27 +1714,27 @@ step_animation(struct State* state) state->animation.active ? "ACTIVE" : "INACTIVE"); } -SDL_Surface* -try_load(const char* filename) +SDL_Texture* +try_load(SDL_Renderer* renderer, const char* filename) { - SDL_Surface* surface = NULL; + SDL_Texture* tex = NULL; char tex_pathname[BUFSIZE]; - assert(filename != NULL); + assert((renderer != NULL) && (filename != NULL)); snprintf(tex_pathname, BUFSIZE, "%s/%s", DATADIR, filename); - surface = IMG_Load(tex_pathname); - if (!surface) + tex = IMG_LoadTexture(renderer, tex_pathname); + if (!tex) { SDL_LogError(SDL_LOG_CATEGORY_ERROR, - "try_load: IMG_Load failed: %s", SDL_GetError()); + "try_load: IMG_LoadTexture failed: %s", SDL_GetError()); snprintf(tex_pathname, BUFSIZE, "./%s", filename); SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "try_load: trying %s", tex_pathname); - surface = IMG_Load(tex_pathname); - if (!surface) + tex = IMG_LoadTexture(renderer, tex_pathname); + if (!tex) { SDL_LogError(SDL_LOG_CATEGORY_ERROR, - "try_load: IMG_Load failed: %s", + "try_load: IMG_LoadTexture failed: %s", SDL_GetError()); exit(1); } @@ -1540,7 +1742,7 @@ try_load(const char* filename) "try_load: success (%s)", tex_pathname); } - return surface; + return tex; } int @@ -1588,7 +1790,13 @@ main(int argc, char** argv) state.grid = malloc(sizeof(struct Cell) * GRID_WIDTH * GRID_HEIGHT + 1); if (!state.grid) { - SDL_LogError(SDL_LOG_CATEGORY_ERROR, "calloc failed"); + SDL_LogError(SDL_LOG_CATEGORY_ERROR, "malloc failed"); + do_exit(1, &state); + } + state.location_stack = malloc(sizeof(SDL_FPoint) * LOCATION_STACK_MAX); + if (!state.location_stack) + { + SDL_LogError(SDL_LOG_CATEGORY_ERROR, "malloc failed"); do_exit(1, &state); } for (y = 0; y < GRID_HEIGHT; y++) @@ -1598,6 +1806,7 @@ main(int argc, char** argv) coords.x = x; coords.y = y; init_cell(c, 1, &coords); + location_push(&state, &c->coords); if (x > 0) c->left = &state.grid[INDEX(x - 1, y)]; if (y > 0) @@ -1629,8 +1838,6 @@ main(int argc, char** argv) if (load_textures(&state) < 0) do_exit(1, &state); - state.location_stack - = (SDL_Point*)calloc(LOCATION_STACK_MAX, sizeof(SDL_Point)); state.animation.active = 0; SDL_GetMouseState(&px, &py); @@ -1653,7 +1860,7 @@ main(int argc, char** argv) current_time = SDL_GetTicks(); if (state.animation.active) { - if (current_time > last_time + 1000 / 60) + if (current_time > last_time + 1000 / FPS) { draw(&state); step_animation(&state);