/*
* 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,
SDL_Color *text_color, struct _mf_game *game)
{
SDL_Color butn_color;
butn_color.r = MF_COLOR_BUTN_R, butn_color.g = MF_COLOR_BUTN_G;
butn_color.b = MF_COLOR_BUTN_B, butn_color.a = MF_COLOR_BUTN_A;
game->timer = mftk_text_new('\0', '\0', 5, "00:00", text_font,
text_color, 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", text_color,
&butn_color, MF_BTN_P, &_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;
SDL_Color form_color;
SDL_Color text_color;
struct _mf_game game;
struct mftk_window *win = NULL;
int fr;
int won;
Uint32 beg;
int j;
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-Regular.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;
form_color.r = MF_COLOR_FORM_R, form_color.g = MF_COLOR_FORM_G;
form_color.b = MF_COLOR_FORM_B, form_color.a = MF_COLOR_FORM_A;
text_color.r = MF_COLOR_FORE_R, text_color.g = MF_COLOR_FORE_G;
text_color.b = MF_COLOR_FORE_B, text_color.a = MF_COLOR_FORE_A;
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, &form_color,
_mf_game_form(renderer, text_font,
&text_color, &game)));
game.beg = SDL_GetTicks();
fr = 30;
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]);
}
for (i = 0; i < enemies; ++i) {
for (j = i + 1; j < enemies; ++j) {
mf_char_collision(chars[i], chars[j],
SDL_TRUE);
}
}
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;
}