#include // bool #include // sinf() #include "util.h" // warn()/die() #include "color.h" // set_color() #include "draw.h" #include "sprites.h" #include "draw-text.h" #include "bg-style.h" #define M_2_PI (2.0 * 3.1415926) 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; } 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_home( draw_ctx_t * const draw_ctx, const sok_pos_t pos ) { const SDL_Rect rect = get_cell_rect(draw_ctx, pos); SDL_Texture *tex = draw_ctx->sprites[SPRITE_HOME]; if (sok_ctx_is_done(draw_ctx->ctx)) { const Uint32 ticks = SDL_GetTicks(); const double angle = 10 * sin(ticks * M_2_PI / 1000.0); if (SDL_RenderCopyEx(draw_ctx->renderer, tex, NULL, &rect, angle, NULL, SDL_FLIP_NONE)) { die("SDL_RenderCopyEx(): %s", SDL_GetError()); } } else { 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++) { if (x > 0 && y > 0 && x < level_size.x - 1 && y < level_size.y - 1) { 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_home(draw_ctx, pos); 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, }; static const text_style_t TITLE_STYLE = { .align = TEXT_ALIGN_TOP_CENTER, .pad = { 0, 10 }, .colors = { { 0xff, 0xff, 0xff, 0xff }, { 0x00, 0x00, 0x00, 0xff }, }, }; 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 ); // draw text draw_text(draw_ctx->renderer, draw_ctx->font, &TITLE_STYLE, buf); } static const text_style_t MOVES_STYLE = { .align = TEXT_ALIGN_BOTTOM_LEFT, .pad = { 10, 10 }, .colors = { { 0xff, 0xff, 0xff, 0xff }, { 0x00, 0x00, 0x00, 0xff }, }, }; 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!)" : "" ); // draw text draw_text(draw_ctx->renderer, draw_ctx->font, &MOVES_STYLE, buf); } static const text_style_t HELP_STYLE = { .align = TEXT_ALIGN_BOTTOM_RIGHT, .pad = { 10, 10 }, .colors = { { 0xff, 0xff, 0xff, 0xff }, { 0x00, 0x00, 0x00, 0xff }, }, }; static void draw_help_text( draw_ctx_t * const draw_ctx ) { // build text char buf[256]; #define D " " snprintf( buf, sizeof(buf), " %sU: Undo" D "R: Reset" D "S: Solve" D "Q: Quit ", sok_ctx_is_done(draw_ctx->ctx) ? "Space: Next Level" D : "" ); #undef D // draw text draw_text(draw_ctx->renderer, draw_ctx->font, &HELP_STYLE, buf); } static const bg_style_t BG_STYLES[] = {{ // normal style .r = { .base = 0x00 }, .g = { .base = 0x00 }, .b = { .base = 0x00 }, .a = { .base = 0xFF }, }, { // won style .r = { .base = 0x66, .amplitude = 0x33, .phase = 1000, .period = 2000, }, .g = { .base = 0x66, .amplitude = 0x33, .phase = 3000, .period = 5000, }, .b = { .base = 0x66, .amplitude = 0x33, .phase = 5000, .period = 7000, }, .a = { .base = 0xFF, }, }}; static void draw_bg( draw_ctx_t * const draw_ctx ) { // get color const SDL_Color c = bg_style_get_color( BG_STYLES + (sok_ctx_is_done(draw_ctx->ctx) ? 1 : 0), SDL_GetTicks() ); // set color if (SDL_SetRenderDrawColor(draw_ctx->renderer, c.r, c.g, c.b, c.a)) { die("SDL_SetRenderDrawColor(): %s", SDL_GetError()); } // clear background SDL_RenderClear(draw_ctx->renderer); } void draw( draw_ctx_t * const draw_ctx ) { // clear background draw_bg(draw_ctx); // render level 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); }