#include // bool #include // sinf() #include "util.h" // warn()/die() #include "color.h" // set_color() #include "draw.h" #include "sprites.h" #define DRAW_SPRITES static size_t get_cell_size( const draw_ctx_t * const draw_ctx ) { return 32 + 8 * *draw_ctx->zoom; } static SDL_Rect get_cell_rect( const draw_ctx_t * const draw_ctx, const sok_pos_t pos ) { const size_t cell_size = get_cell_size(draw_ctx); return (SDL_Rect) { draw_ctx->render_ofs_x + pos.x * cell_size, draw_ctx->render_ofs_y + pos.y * cell_size, cell_size, cell_size }; } static void on_size( const sok_ctx_t * const ctx, const sok_pos_t level_size, void * const data ) { draw_ctx_t * const draw_ctx = data; const size_t cell_size = get_cell_size(draw_ctx); // get renderer size int renderer_x, renderer_y; if (SDL_GetRendererOutputSize(draw_ctx->renderer, &renderer_x, &renderer_y)) { die("SDL_GetRendererOutputSize(): %s", SDL_GetError()); } // calculate renderer offset draw_ctx->render_ofs_x = (renderer_x - level_size.x * cell_size) / 2; draw_ctx->render_ofs_y = (renderer_y - level_size.y * cell_size) / 2; } #ifdef DRAW_SPRITES static void draw_cell( draw_ctx_t * const draw_ctx, const sok_pos_t pos, const sprite_t sprite ) { const SDL_Rect rect = get_cell_rect(draw_ctx, pos); SDL_Texture *tex = draw_ctx->sprites[sprite]; if (SDL_RenderCopy(draw_ctx->renderer, tex, NULL, &rect)) { die("SDL_RenderCopy(): %s", SDL_GetError()); } } static void draw_border( draw_ctx_t * const draw_ctx, const sok_pos_t level_size ) { const size_t cell_size = get_cell_size(draw_ctx); // calc rect const SDL_Rect rect = { draw_ctx->render_ofs_x - cell_size / 8, draw_ctx->render_ofs_y - cell_size / 8, level_size.x * cell_size + cell_size / 4, level_size.y * cell_size + cell_size / 4, }; // set color if (SDL_SetRenderDrawColor(draw_ctx->renderer, 0, 0, 0, 0xFF)) { die("SDL_SetRenderDrawColor(): %s", SDL_GetError()); } // fill border rect if (SDL_RenderFillRect(draw_ctx->renderer, &rect)) { die("SDL_RenderFillRect(): %s", SDL_GetError()); } } static bool draw_sprites_on_size( const sok_ctx_t * const ctx, const sok_pos_t level_size, void * const data ) { on_size(ctx, level_size, data); draw_ctx_t * const draw_ctx = data; draw_border(draw_ctx, level_size); // draw floor for (size_t y = 0; y < level_size.y; y++) { for (size_t x = 0; x < level_size.x; x++) { const sok_pos_t pos = { x, y }; draw_cell(draw_ctx, pos, SPRITE_FLOOR); } } return true; } static bool draw_sprites_on_wall( const sok_ctx_t * const ctx, const sok_pos_t pos, void * const data ) { draw_ctx_t * const draw_ctx = data; draw_cell(draw_ctx, pos, SPRITE_WALL); return true; } static bool draw_sprites_on_goal( const sok_ctx_t * const ctx, const sok_pos_t pos, const bool has_player, const bool has_box, void * const data ) { draw_ctx_t * const draw_ctx = data; draw_cell(draw_ctx, pos, SPRITE_GOAL); return true; } static bool draw_sprites_on_home( const sok_ctx_t * const ctx, const sok_pos_t pos, const bool has_goal, void * const data ) { draw_ctx_t * const draw_ctx = data; draw_cell(draw_ctx, pos, SPRITE_HOME); return true; } static bool draw_sprites_on_box( const sok_ctx_t * const ctx, const sok_pos_t pos, const bool has_goal, void * const data ) { draw_ctx_t * const draw_ctx = data; draw_cell(draw_ctx, pos, SPRITE_BOX); return true; } static const sok_ctx_walk_cbs_t DRAW_CBS = { .on_size = draw_sprites_on_size, .on_wall = draw_sprites_on_wall, .on_goal = draw_sprites_on_goal, .on_home = draw_sprites_on_home, .on_box = draw_sprites_on_box, }; #else /* !DRAW_SPRITES */ static void fill_cell( draw_ctx_t * const draw_ctx, const sok_pos_t pos ) { const SDL_Rect rect = get_cell_rect(draw_ctx, pos); if (SDL_RenderFillRect(draw_ctx->renderer, &rect)) { die("SDL_RenderFillRect(): %s", SDL_GetError()); } } static bool draw_colors_on_size( const sok_ctx_t * const ctx, const sok_pos_t level_size, void * const data ) { on_size(ctx, level_size, data); return true; } static bool draw_colors_on_walls_start( const sok_ctx_t * const ctx, void * const data ) { draw_ctx_t * const draw_ctx = data; set_color(draw_ctx->renderer, COLOR_WALL); return true; } static bool draw_colors_on_wall( const sok_ctx_t * const ctx, const sok_pos_t pos, void * const data ) { draw_ctx_t * const draw_ctx = data; fill_cell(draw_ctx, pos); return true; } static bool draw_colors_on_goals_start( const sok_ctx_t * const ctx, void * const data ) { draw_ctx_t * const draw_ctx = data; set_color(draw_ctx->renderer, COLOR_GOAL); return true; } static bool draw_colors_on_goal( const sok_ctx_t * const ctx, const sok_pos_t pos, const bool has_player, const bool has_box, void * const data ) { draw_ctx_t * const draw_ctx = data; fill_cell(draw_ctx, pos); return true; } static bool draw_colors_on_home( const sok_ctx_t * const ctx, const sok_pos_t pos, const bool has_goal, void * const data ) { draw_ctx_t * const draw_ctx = data; set_color(draw_ctx->renderer, has_goal ? COLOR_HOME_GOAL : COLOR_HOME); fill_cell(draw_ctx, pos); return true; } static bool draw_colors_on_box( const sok_ctx_t * const ctx, const sok_pos_t pos, const bool has_goal, void * const data ) { draw_ctx_t * const draw_ctx = data; set_color(draw_ctx->renderer, has_goal ? COLOR_BOX_GOAL : COLOR_BOX); fill_cell(draw_ctx, pos); return true; } static const sok_ctx_walk_cbs_t DRAW_CBS = { .on_size = draw_colors_on_size, .on_walls_start = draw_colors_on_walls_start, .on_wall = draw_colors_on_wall, .on_goals_start = draw_colors_on_goals_start, .on_goal = draw_colors_on_goal, .on_home = draw_colors_on_home, .on_box = draw_colors_on_box, }; #endif /* DRAW_SPRITES */ static const SDL_Color TEXT_COLORS[] = { { 0xff, 0xff, 0xff, 0xff }, // fg { 0x00, 0x00, 0x00, 0xff }, // bg }; static void draw_text( draw_ctx_t * const draw_ctx, const char * const buf, const SDL_Rect * const rect ) { // create surface SDL_Surface *surface = TTF_RenderText_Shaded( draw_ctx->font, buf, TEXT_COLORS[0], TEXT_COLORS[1] ); // check for error if (!surface) { die("TTF_RenderText_Blended(): %s", TTF_GetError()); } // create texture SDL_Texture *texture = SDL_CreateTextureFromSurface(draw_ctx->renderer, surface); if (!texture) { die("SDL_CreateTextureFromSurface(): %s", SDL_GetError()); } // free surface SDL_FreeSurface(surface); // render text if (SDL_RenderCopy(draw_ctx->renderer, texture, NULL, rect)) { die("SDL_RenderCopy(): %s", SDL_GetError()); } // free texture SDL_DestroyTexture(texture); } static void draw_title_text( draw_ctx_t * const draw_ctx ) { // build text char buf[256]; snprintf( buf, sizeof(buf), " %s: %s (#%zu) ", draw_ctx->level->pack, draw_ctx->level->name, *draw_ctx->level_num ); // get renderer width int renderer_w; if (SDL_GetRendererOutputSize(draw_ctx->renderer, &renderer_w, NULL)) { die("SDL_GetRendererOutputSize(): %s", SDL_GetError()); } // get text size int text_w, text_h; if (TTF_SizeText(draw_ctx->font, buf, &text_w, &text_h)) { die("TTF_SizeText(): %s", TTF_GetError()); } // build rect const SDL_Rect rect = { (renderer_w - text_w) / 2, 10, text_w, text_h }; // draw text draw_text(draw_ctx, buf, &rect); } static void draw_moves_text( draw_ctx_t * const draw_ctx ) { // build text char buf[256]; snprintf( buf, sizeof(buf), " Moves: %zu%s ", draw_ctx->ctx->num_moves, sok_ctx_is_done(draw_ctx->ctx) ? " (Won!)" : "" ); // get renderer height int renderer_h; if (SDL_GetRendererOutputSize(draw_ctx->renderer, NULL, &renderer_h)) { die("SDL_GetRendererOutputSize(): %s", SDL_GetError()); } // get text size int text_w, text_h; if (TTF_SizeText(draw_ctx->font, buf, &text_w, &text_h)) { die("TTF_SizeText(): %s", TTF_GetError()); } // build rect const SDL_Rect rect = { 10, renderer_h - text_h - 10, text_w, text_h }; // draw text draw_text(draw_ctx, buf, &rect); } #define DELIM " " static void draw_help_text( draw_ctx_t * const draw_ctx ) { // build text char buf[256]; snprintf( buf, sizeof(buf), " %sU: Undo" DELIM "R: Reset" DELIM "S: Solve" DELIM "Q: Quit ", sok_ctx_is_done(draw_ctx->ctx) ? "Space: Next Level" DELIM : "" ); // get renderer height int renderer_w, renderer_h; if (SDL_GetRendererOutputSize(draw_ctx->renderer, &renderer_w, &renderer_h)) { die("SDL_GetRendererOutputSize(): %s", SDL_GetError()); } // get text size int text_w, text_h; if (TTF_SizeText(draw_ctx->font, buf, &text_w, &text_h)) { die("TTF_SizeText(): %s", TTF_GetError()); } // build rect const SDL_Rect rect = { renderer_w - text_w - 10, renderer_h - text_h - 10, text_w, text_h, }; // draw text draw_text(draw_ctx, buf, &rect); } static void set_bg_won_color( draw_ctx_t * const draw_ctx ) { const Uint32 ticks = SDL_GetTicks(); // gen color const SDL_Color c = { .r = 0x66 + 0x33 * sinf((1000 + ticks) * 2.0 * 3.14159 / 2000.0), .g = 0x66 + 0x33 * sinf((3000 + ticks) * 2.0 * 3.14159 / 5000.0), .b = 0x66 + 0x33 * sinf((5000 + ticks) * 2.0 * 3.14159 / 7000.0), .a = 0xFF, }; // set color if (SDL_SetRenderDrawColor(draw_ctx->renderer, c.r, c.g, c.b, c.a)) { die("SDL_SetRenderDrawColor(): %s", SDL_GetError()); } } void draw( draw_ctx_t * const draw_ctx ) { const bool is_done = sok_ctx_is_done(draw_ctx->ctx); // set bg color if (sok_ctx_is_done(draw_ctx->ctx)) { // set bg won color set_bg_won_color(draw_ctx); } else { // set normal bg color set_color(draw_ctx->renderer, is_done ? COLOR_BG_WON : COLOR_BG); } // clear background SDL_RenderClear(draw_ctx->renderer); // render sok_ctx_walk(draw_ctx->ctx, &DRAW_CBS, draw_ctx); // render text draw_title_text(draw_ctx); draw_moves_text(draw_ctx); draw_help_text(draw_ctx); // flip SDL_RenderPresent(draw_ctx->renderer); }