/*
* 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 "maze.h"
struct mf_maze {
int w;
int h;
int show_all;
unsigned int *walls;
unsigned int *show;
unsigned int *visited;
};
/*
* Black magic follows
*/
void
mf_maze_rm_wall(struct mf_maze *m, int x, int y, int dx, int dy)
{
int i;
if (x + dx < 0 || x + dx >= m->w || y + dy < 0 || y + dy >= m->h) {
return;
}
if (dx < 0) --x;
if (dy < 0) --y;
i = y * m->w + x;
i *= 2;
if (dx == 0) ++i; /* Moving vertically */
m->walls[i / (sizeof(*m->walls)*8)] |= 1 << (i % (sizeof(*m->walls)*8));
}
int
mf_maze_is_wall(struct mf_maze *m, int x, int y, int dx, int dy)
{
int i;
if (x + dx < 0 || x + dx >= m->w || y + dy < 0 || y + dy >= m->h) {
return 1;
}
if (dx < 0) --x;
if (dy < 0) --y;
i = y * m->w + x;
i *= 2;
if (dx == 0) ++i; /* Moving vertically */
return !(m->walls[i / (sizeof(*m->walls)*8)]
& 1 << (i % (sizeof(*m->walls)*8)));
}
void
mf_maze_reveal_wall(struct mf_maze *m, int x, int y, int dx, int dy)
{
int i;
if (x + dx < 0 || x + dx >= m->w || y + dy < 0 || y + dy >= m->h) {
return;
}
if (dx < 0) --x;
if (dy < 0) --y;
i = y * m->w + x;
i *= 2;
if (dx == 0) ++i; /* Moving vertically */
m->show[i / (sizeof(*m->show)*8)] |= 1 << (i % (sizeof(*m->show)*8));
}
static int
_mf_maze_revealed_wall(struct mf_maze *m, int x, int y, int dx, int dy)
{
int i;
if (m->show_all) {
return 1;
}
if (x + dx < 0 || x + dx >= m->w || y + dy < 0 || y + dy >= m->h) {
return 1;
}
if (dx < 0) --x;
if (dy < 0) --y;
i = y * m->w + x;
i *= 2;
if (dx == 0) ++i; /* Moving vertically */
return m->show[i / (sizeof(*m->show)*8)]
& 1 << (i % (sizeof(*m->show)*8));
}
static void
_mf_maze_visit(struct mf_maze *m, int x, int y)
{
int i;
++x, ++y; /* Coordinates start from -1 (edges) */
i = y * (m->w+2) + x;
m->visited[i / (sizeof(*m->visited)*8)] |=
1 << (i % (sizeof(*m->visited)*8));
}
static int
_mf_maze_is_visited(struct mf_maze *m, int x, int y)
{
int i;
++x, ++y; /* Coordinates start from -1 (edges) */
i = y * (m->w+2) + x;
return m->visited[i / (sizeof(*m->visited)*8)]
& 1 << (i % (sizeof(*m->visited)*8));
}
/*
* End of black magic
*/
static void
_mf_maze_carve(struct mf_maze *m, int x, int y)
{
int dirs[4][2] = {{0, -1}, {0, 1}, {1, 0}, {-1, 0}};
int i;
int j;
int t_x;
int t_y;
/* Shuffle directions */
for (i = 0; i < 4; ++i) {
j = rand() / (RAND_MAX / (i+1));
t_x = dirs[i][0];
t_y = dirs[i][1];
dirs[i][0] = dirs[j][0];
dirs[i][1] = dirs[j][1];
dirs[j][0] = t_x;
dirs[j][1] = t_y;
}
/* You are here */
_mf_maze_visit(m, x, y);
/* Carve passages */
for (i = 0; i < 4; ++i) {
if (!_mf_maze_is_visited(m, x + dirs[i][0], y + dirs[i][1])) {
mf_maze_rm_wall(m, x, y, dirs[i][0], dirs[i][1]);
_mf_maze_carve(m, x + dirs[i][0], y + dirs[i][1]);
}
}
}
struct mf_maze *
mf_maze_new(int s, int w, int h, int show_all)
{
struct mf_maze *m;
int x;
int y;
/* Allocate everything */
m = calloc(1, sizeof(*m));
if (m == NULL) {
return NULL;
}
m->walls = calloc(
(w * h * 2 + sizeof(*m->walls)*8 - 1) /
(sizeof(*m->walls)*8),
sizeof(*m->walls));
if (m->walls == NULL) {
free(m);
return NULL;
}
m->show = calloc(
(w * h * 2 + sizeof(*m->show)*8 - 1) /
(sizeof(*m->show)*8),
sizeof(*m->show));
if (m->show == NULL) {
free(m->walls);
free(m);
return NULL;
}
m->visited = calloc(
((w+2) * (h+2) + sizeof(*m->visited)*8 - 1) /
(sizeof(*m->visited)*8),
sizeof(*m->visited));
if (m->visited == NULL) {
free(m->show);
free(m->walls);
free(m);
return NULL;
}
/* Initialize parameters */
m->w = w;
m->h = h;
m->show_all = show_all;
/* Mark edges as visited */
for (x = -1; x <= w; ++x) {
_mf_maze_visit(m, x, -1); /* Top */
_mf_maze_visit(m, x, h); /* Bottom */
}
for (y = 0; y < h; ++y) {
_mf_maze_visit(m, -1, y); /* Left */
_mf_maze_visit(m, w, y); /* Right */
}
/* Carve passages */
srand(s);
_mf_maze_carve(m, 0, 0);
free(m->visited);
return m;
}
int
mf_maze_render(struct mf_maze *m, SDL_Renderer *renderer, SDL_Color *color,
int cw)
{
int e;
int x;
int y;
e = 0;
if (SDL_SetRenderDrawColor(renderer,
color->r, color->g, color->b, color->a) < 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Couldn't set drawing color: %s",
SDL_GetError());
e = -1;
}
for (y = 0; y < m->h; ++y) {
for (x = 0; x < m->w; ++x) {
if (y < m->h - 1 && mf_maze_is_wall(m, x, y, 0, 1) &&
_mf_maze_revealed_wall(m, x, y, 0, 1)) {
/* Draw h line */
if (SDL_RenderDrawLine(renderer,
(x ) * cw, (y+1) * cw,
(x+1) * cw, (y+1) * cw)
< 0) {
SDL_LogError(
SDL_LOG_CATEGORY_APPLICATION,
"Couldn't draw line: %s",
SDL_GetError());
e = -1;
}
}
if (x < m->w - 1 && mf_maze_is_wall(m, x, y, 1, 0) &&
_mf_maze_revealed_wall(m, x, y, 1, 0)) {
/* Draw v line */
if (SDL_RenderDrawLine(renderer,
(x+1) * cw, (y ) * cw,
(x+1) * cw, (y+1) * cw)
< 0) {
SDL_LogError(
SDL_LOG_CATEGORY_APPLICATION,
"Couldn't draw line: %s",
SDL_GetError());
e = -1;
}
}
}
}
return e;
}
void
mf_maze_destroy(struct mf_maze **m_p)
{
struct mf_maze *m;
if (m_p == NULL || *m_p == NULL) {
return;
}
m = *m_p;
if (m->walls != NULL) {
free(m->walls);
}
if (m->show != NULL) {
free(m->show);
}
free(m);
m = NULL;
}