/* * Copyright (C) 2021 P. J. McDermott * * This file is part of Maze Fight * * Maze Fight is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Maze Fight is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Maze Fight. If not, see . */ #include #include #include #include #include "char.h" #include "defs.h" #include "dirs.h" #include "game.h" #include "maze.h" #include "tk.h" #include "util.h" struct _mf_game { struct mftk_widget *timer; char time_buf[6]; Uint32 beg; int player_cx; int player_cy; int player_travel; int player_dx; int player_dy; int player_x; int player_y; }; static int _mf_game_exit(void *user_data __attribute__((__unused__))) { return 1; } static struct mftk_widget * _mf_game_form(SDL_Renderer *renderer, TTF_Font *text_font, struct _mf_game *game) { game->timer = mftk_text_new(MF_DIGITS ":", 5, "00:00", text_font, 0, MF_WGT_B, SDL_FALSE, NULL, NULL, NULL); return mftk_grid_new(2, 1, MF_ROW_M, MF_COL_M, game->timer, MFTK_GRID_HALIGN_R|MFTK_GRID_VALIGN_T, mftk_button_new(text_font, "Exit", MF_BTN_P, MF_WGT_B, &_mf_game_exit, NULL, renderer), MFTK_GRID_HALIGN_R|MFTK_GRID_VALIGN_B ); } static int _mf_game_fow(SDL_Renderer *renderer, struct _mf_game *game, struct mf_maze *maze, int cw) { int x1; int y1; int travel; int dx; int dy; int x2; int y2; SDL_Rect rect; if (SDL_SetRenderDrawColor(renderer, MF_COLOR_FOGW_R, MF_COLOR_FOGW_G, MF_COLOR_FOGW_B, MF_COLOR_FOGW_A) < 0) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, \ "Couldn't render fog: %s", \ SDL_GetError()); \ return -1; \ } x2 = x1 = game->player_cx; y2 = y1 = game->player_cy; travel = game->player_travel; dx = game->player_dx; dy = game->player_dy; while (!mf_maze_is_wall(maze, dx >= 0 ? x2 : x1, dy >= 0 ? y2 : y1, dx, dy)) { if (dx >= 0) x2 += dx; else x1 += dx; if (dy >= 0) y2 += dy; else y1 += dy; } ++x2, ++y2; x1 *= cw, y1 *= cw, x2 *= cw, y2 *= cw; if (dx >= 0) x1 += travel * dx; else x2 += travel * dx; if (dy >= 0) y1 += travel * dy; else y2 += travel * dy; ++x2, ++y2; #define mf_game_w_ MF_WINDOW_H #define mf_game_h_ MF_WINDOW_H #define mf_game_render_() \ do { \ if (SDL_RenderFillRect(renderer, &rect) < 0) { \ SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, \ "Couldn't render fog: %s", \ SDL_GetError()); \ return -1; \ } \ } while (0) rect.x= 0, rect.y= 0, rect.w= x1, rect.h= mf_game_h_; mf_game_render_(); rect.x= x1, rect.y= 0, rect.w= x2-x1, rect.h= y1; mf_game_render_(); rect.x= x1, rect.y= y2, rect.w= x2-x1, rect.h= mf_game_h_-y2; mf_game_render_(); rect.x= x2, rect.y= 0, rect.w= mf_game_w_-x2, rect.h= mf_game_h_; mf_game_render_(); #undef mf_game_w_ #undef mf_game_h_ #undef mf_game_render_ return 0; } int mf_game(long seed, int size, int fow, int reveal, int enemies, SDL_Renderer *renderer) { struct mf_maze *maze = NULL; struct mf_char *player = NULL; struct mf_char **chars = NULL; int i; SDL_Color maze_color; char *font_path = NULL; TTF_Font *text_font = NULL; struct _mf_game game; struct mftk_window *win = NULL; int fr; int won; Uint32 beg; int secs; Uint32 end; Uint32 delay; SDL_Event event; /* Create maze */ maze = mf_maze_new(seed, size, size, reveal); if (maze == NULL) { goto err; } maze_color.r = MF_COLOR_MAZE_R; maze_color.g = MF_COLOR_MAZE_G; maze_color.b = MF_COLOR_MAZE_B; maze_color.a = MF_COLOR_MAZE_A; chars = calloc(enemies, sizeof(*chars)); if (chars == NULL) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't allocate characters: %s", strerror(errno)); goto err; } player = mf_player_new(maze, MF_WINDOW_H / size); if (player == NULL) { goto err; } for (i = 0; i < enemies; ++i) { chars[i] = mf_enemy_new(maze, MF_WINDOW_H / size, size, enemies, chars); if (chars[i] == NULL) { goto err; } } font_path = mf_strcat(mf_get_fonts_dir(), "/FifteenTwenty-Bold.ttf"); text_font = TTF_OpenFont(font_path, MF_TEXT_FONT_S); if (text_font == NULL) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't open font: %s", TTF_GetError()); goto err; } free(font_path); font_path = NULL; win = mftk_window_new(MF_WINDOW_H, 0, mftk_box_new( MF_WINDOW_W - MF_WINDOW_H, MF_WINDOW_H, MF_WINDOW_W - MF_WINDOW_H, MF_WINDOW_H, MF_FORM_P, MF_FORM_B / 2, _mf_game_form(renderer, text_font, &game))); game.beg = SDL_GetTicks(); fr = 10; won = SDL_FALSE; while(1) { beg = SDL_GetTicks(); while (SDL_PollEvent(&event)) { switch (event.type) { case SDL_QUIT: goto quit; case SDL_KEYDOWN: switch (event.key.keysym.sym) { case SDLK_ESCAPE: goto exit; default: break; } default: break; } mf_player_key_event(player, &event); switch (mftk_window_event(win, &event)) { case 0: break; case 1: goto exit; default: goto err; } } if (won == SDL_FALSE) { mf_char_update(player); for (i = 0; i < enemies; ++i) { mf_char_update(chars[i]); } mf_char_get_vector(player, &game.player_cx, &game.player_cy, &game.player_travel, &game.player_dx, &game.player_dy); game.player_x = game.player_cx * MF_WINDOW_H / size; game.player_y = game.player_cy * MF_WINDOW_H / size; game.player_x += game.player_travel * game.player_dx; game.player_y += game.player_travel * game.player_dy; if (game.player_cx == size - 1 && game.player_cy == size - 1) { won = SDL_TRUE; } } SDL_SetRenderDrawColor(renderer, MF_COLOR_BACK_R, MF_COLOR_BACK_G, MF_COLOR_BACK_B, MF_COLOR_BACK_A); SDL_RenderClear(renderer); mf_char_render(player, renderer); for (i = 0; i < enemies; ++i) { mf_char_render(chars[i], renderer); } if (fow == SDL_TRUE && _mf_game_fow(renderer, &game, maze, MF_WINDOW_H / size) < 0) { goto err; } mf_maze_render(maze, renderer, &maze_color, MF_WINDOW_H / size); if (won == SDL_FALSE) { secs = (beg - game.beg) / 1000; if (sprintf(game.time_buf, "%02d:%02d", secs / 60, secs % 60) < 0) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't allocate string: %s", strerror(errno)); goto err; } mftk_text_set_value(game.timer, game.time_buf); } mftk_window_render(win, renderer); SDL_RenderPresent(renderer); end = SDL_GetTicks(); if ((Uint32) (1000 / fr) > (end - beg)) { delay = 1000 / fr - (end - beg); } else { SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Frame took longer than frame period"); delay = 0; } SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "Frame took %u ms, delaying %u ms", end - beg, delay); SDL_Delay(delay); } exit: mftk_window_destroy(&win); TTF_CloseFont(text_font); text_font = NULL; mf_maze_destroy(&maze); mf_char_destroy(&player); if (chars != NULL) { for (i = 0; i < enemies; ++i) { mf_char_destroy(&chars[i]); } free(chars); } return 0; quit: mftk_window_destroy(&win); TTF_CloseFont(text_font); text_font = NULL; mf_maze_destroy(&maze); mf_char_destroy(&player); if (chars != NULL) { for (i = 0; i < enemies; ++i) { mf_char_destroy(&chars[i]); } free(chars); } return 1; err: if (font_path != NULL) { free(font_path); } if (text_font != NULL) { TTF_CloseFont(text_font); } mftk_window_destroy(&win); mf_maze_destroy(&maze); mf_char_destroy(&player); if (chars != NULL) { for (i = 0; i < enemies; ++i) { mf_char_destroy(&chars[i]); } free(chars); } return -1; }