чување d8640af43c65c9f5d3aa6bcbdcd06b664aed8463
родитељ 95c6889a695fa74daf5a7e006a82a38e8633679c
Аутор: Страхиња Радић <sr@strahinja.org>
Датум: Sat, 9 Aug 2025 01:13:45 +0200
Add game over state; refactor code; improve text output
Diffstat:
| M | TODO | | | 2 | -- |
| M | TODO.done | | | 2 | ++ |
| M | linije.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);