#include // bool #include // EXIT_{FAILURE,SUCCESS} #include // strerror #include // errno #include #include #include "../text/levels.h" #include "../libsok/sok.h" #include "util.h" #include "action.h" #include "warp-buf.h" #include "sprites.h" #include "draw.h" static void draw_moves( const sok_ctx_t * const ctx, const size_t skip_moves ) { char buf[1024] = { 0 }; size_t ofs = 0; for (size_t i = skip_moves; i < ctx->num_moves; i++) { if (ctx->moves[i].dir >= SOK_DIR_LAST) { die("invalid move: %u", ctx->moves[i].dir); } buf[ofs++] = SOK_DIR_TO_CHAR(ctx->moves[i].dir); } SDL_Log("Solution (%zu moves): %s", ctx->num_moves - skip_moves, buf); } static void log_renderer_info( SDL_Renderer * const renderer ) { SDL_RendererInfo info; // get renderer info if (SDL_GetRendererInfo(renderer, &info)) { die("SDL_GetRendererInfo(): %s", SDL_GetError()); } // log renderer info SDL_Log( "renderer:\n" " name = \"%s\"\n" " flags = %u%s%s%s%s\n" " num_texture_formats = %u\n" " max_texture_width = %d\n" " max_texture_height = %d", info.name, info.flags, info.flags & SDL_RENDERER_SOFTWARE ? ", software" : "", info.flags & SDL_RENDERER_ACCELERATED ? ", accelerated" : "", info.flags & SDL_RENDERER_PRESENTVSYNC ? ", presentvsync" : "", info.flags & SDL_RENDERER_TARGETTEXTURE ? ", targettexture" : "", info.num_texture_formats, info.max_texture_width, info.max_texture_height ); } #if 0 static void log_texture_info( SDL_Texture * const tex ) { Uint32 format; int access, w, h; // query texture if (SDL_QueryTexture(tex, &format, &access, &w, &h)) { die("SDL_QueryTexture(): %s", SDL_GetError()); } // log information SDL_Log( "texture:\n" " format = %u\n" " access = %d\n" " width = %d\n" " height = %d", format, access, w, h ); } #endif /* 0 */ static void solve_on_error( const char * const err ) { die("Error solving level: %s", err); } static void set_level( SDL_Window * const win, draw_ctx_t * const draw_ctx, sok_ctx_t * const ctx, const size_t level_num ) { // get level data draw_ctx->level = levels_get_level(level_num); // load level if (!sok_ctx_set_level(ctx, draw_ctx->level->data)) { die("Couldn't load level %zu", level_num); } // build title char buf[1024]; snprintf( buf, sizeof(buf), "Sokoban: %s: %s (#%zu)", draw_ctx->level->pack, draw_ctx->level->name, level_num ); // set window title SDL_SetWindowTitle(win, buf); } int main(int argc, char *argv[]) { size_t level_num = (argc > 1) ? atoi(argv[1]) : 0, zoom = 0; const char *sprites_png_path = "../assets/sprites.png"; const char *font_path = "../assets/roboto.ttf"; // init warp buffer warp_buf_t warp_buf; warp_buf_clear(&warp_buf); // init sok context sok_ctx_t ctx; sok_ctx_init(&ctx, NULL); // init sdl if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO)) { die("SDL_Init(): %s", SDL_GetError()); } // register exit handler if (atexit(SDL_Quit)) { die("atexit(SDL_Init): %s", strerror(errno)); exit(EXIT_FAILURE); } // init SDL_TTF if (TTF_Init()) { die("TTF_Init(): %s", TTF_GetError()); } // register exit handler if (atexit(TTF_Quit)) { die("atexit(TTF_Quit): %s", strerror(errno)); exit(EXIT_FAILURE); } // create window and renderer SDL_Window *win; SDL_Renderer *renderer; if (SDL_CreateWindowAndRenderer(800, 600, SDL_WINDOW_RESIZABLE, &win, &renderer)) { die("SDL_CreateWindowAndRenderer(): %s", SDL_GetError()); } log_renderer_info(renderer); // init draw context draw_ctx_t draw_ctx = { .level_num = &level_num, .ctx = &ctx, .renderer = renderer, .zoom = &zoom, }; // set level set_level(win, &draw_ctx, &ctx, level_num); // init sprites sprites_init(renderer, sprites_png_path, draw_ctx.sprites); // load font draw_ctx.font = TTF_OpenFontIndex(font_path, 16, 0); if (!draw_ctx.font) { die("TTF_OpenFontIndex(): %s", TTF_GetError()); } bool done = false; SDL_Event ev; while (!done) { // read events while (SDL_PollEvent(&ev)) { // get action const action_t action = get_action(&ev); // handle action switch (action.type) { case ACTION_NONE: // do nothing break; case ACTION_QUIT: done = true; break; case ACTION_MOVE: if (!sok_ctx_move(&ctx, (sok_dir_t) action.data)) { warn("move %s failed", SOK_DIR_TO_STR((sok_dir_t) action.data)); } break; case ACTION_UNDO: if (!sok_ctx_undo(&ctx)) { warn("undo failed"); } break; case ACTION_NEXT: if (sok_ctx_is_done(&ctx)) { // advance level level_num++; // set level set_level(win, &draw_ctx, &ctx, level_num); } else { warn("cannot advance to next level"); } break; case ACTION_RESET: // reset level if (!sok_ctx_set_level(&ctx, draw_ctx.level->data)) { die("Couldn't load level %zu", level_num); } break; case ACTION_WARP: if (warp_buf_get(&warp_buf, &level_num)) { // load level set_level(win, &draw_ctx, &ctx, level_num); // clear warp buf warp_buf_clear(&warp_buf); } break; case ACTION_WARP_BUF_PUSH: warp_buf_push_num(&warp_buf, action.data); break; case ACTION_WARP_BUF_POP: warp_buf_pop_num(&warp_buf); break; case ACTION_SOLVE: { // get current number of moves const size_t old_num_moves = ctx.num_moves; if (sok_solve(&ctx, solve_on_error)) { // found solution, print it draw_moves(&ctx, old_num_moves); } else { warn("Couldn't solve level"); } } break; case ACTION_ZOOM_IN: if (zoom < 10) { zoom++; } break; case ACTION_ZOOM_OUT: if (zoom > 0) { zoom--; } break; default: // ignore break; } } // draw draw(&draw_ctx); } // fini renderer, window SDL_DestroyRenderer(renderer); SDL_DestroyWindow(win); // return success return EXIT_SUCCESS; }