/*
* This file is part of Deliantra, the Roguelike Realtime MMORPG.
*
* Copyright (©) 2017,2018 Marc Alexander Lehmann / the Deliantra team
* Copyright (©) 2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2016 Marc Alexander Lehmann / Robin Redeker / the Deliantra team
* Copyright (©) 1994-2004 Crossfire Development Team (restored, original file without copyright notice)
*
* Deliantra is free software: you can redistribute it and/or modify it under
* the terms of the Affero GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program 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 Affero GNU General Public License
* and the GNU General Public License along with this program. If not, see
* .
*
* The authors can be reached via e-mail to
*/
/* peterm@langmuir.eecs.berkeley.edu: this function generates a random
blocked maze with the property that there is only one path from one spot
to any other, and there is always a path from one spot to any other.
input: xsize, ysize;
output: a char** array with # and . for closed and open respectively.
a char value of 0 represents a blank space: a '#' is
a wall.
*/
/* we need to maintain a list of wall points to generate
reasonable mazes: a straightforward recursive random walk maze
generator would generate a map with a trivial circle-the-outer-wall solution */
#include
#include
#include
#include "rproto.h"
/* global variables that everyone needs: don't want to pass them in
as parameters every time. */
static fixed_stack seeds;
static int xsize, ysize;
static char **maze;
static void
push (point p)
{
seeds.push (p);
maze [p.x][p.y] = '#';
}
/* randomly returns one of the elements from the wall point list */
static point
pop_rand ()
{
return seeds.remove (rmg_rndm (seeds.size));
}
/* the free wall points are those outer points which aren't corners or
near corners, and don't have a maze wall growing out of them already. */
static void
push_walls ()
{
/* top and bottom wall */
for (int x = 2; x < xsize - 2; x++)
{
push (point (x, 0));
push (point (x, ysize - 1));
}
/* left and right wall */
for (int y = 2; y < ysize - 2; y++)
{
push (point ( 0, y));
push (point (xsize - 1, y));
}
}
/* find free point: randomly look for a square adjacent to this one where
we can place a new block without closing a path. We may only look
up, down, right, or left. */
static int
find_free_point (point &p, point pc)
{
/* we will randomly pick from this list, 1=up,2=down,3=right,4=left */
int dirlist[4];
int count = 0; /* # elements in dirlist */
int xc = pc.x;
int yc = pc.y;
/* look up */
if (yc < ysize - 2 && xc > 2 && xc < xsize - 2) /* it is valid to look up */
{
int cleartest = maze[xc][yc + 1] + maze[xc - 1][yc + 1] + maze[xc + 1][yc + 1]
+ maze[xc][yc + 2] + maze[xc - 1][yc + 2] + maze[xc + 1][yc + 2];
if (cleartest == 0)
dirlist[count++] = 1;
}
/* look down */
if (yc > 2 && xc > 2 && xc < xsize - 2) /* it is valid to look down */
{
int cleartest = maze[xc][yc - 1] + maze[xc - 1][yc - 1] + maze[xc + 1][yc - 1]
+ maze[xc][yc - 2] + maze[xc - 1][yc - 2] + maze[xc + 1][yc - 2];
if (cleartest == 0)
dirlist[count++] = 2;
}
/* look right */
if (xc < xsize - 2 && yc > 2 && yc < ysize - 2) /* it is valid to look left */
{
int cleartest = maze[xc + 1][yc] + maze[xc + 1][yc - 1] + maze[xc + 1][yc + 1]
+ maze[xc + 2][yc] + maze[xc + 2][yc - 1] + maze[xc + 2][yc + 1];
if (cleartest == 0)
dirlist[count++] = 3;
}
/* look left */
if (xc > 2 && yc > 2 && yc < ysize - 2) /* it is valid to look down */
{
int cleartest = maze[xc - 1][yc] + maze[xc - 1][yc - 1] + maze[xc - 1][yc + 1]
+ maze[xc - 2][yc] + maze[xc - 2][yc - 1] + maze[xc - 2][yc + 1];
if (cleartest == 0)
dirlist[count++] = 4;
}
if (count == 0)
return -1; /* failed to find any clear points */
/* choose a random direction */
switch (dirlist [rmg_rndm (count)])
{
case 1: /* up */
p.x = xc;
p.y = yc + 1;
break;
case 2: /* down */
p.x = xc;
p.y = yc - 1;
break;
case 3: /* right */
p.x = xc + 1;
p.y = yc;
break;
case 4: /* left */
p.x = xc - 1;
p.y = yc;
break;
}
return 1;
}
/* the outsize interface routine: accepts sizes, returns a char
** maze. option is a flag for either a sparse or a full maze. Sparse
mazes have sizable rooms. option = 3=full, 2=braided, 1=sparse, 0=rooms.*/
void
maze_gen (layout &maze, int subtype)
{
xsize = maze.w;
ysize = maze.h;
::maze = maze;
maze.clear ();
maze.border ();
if (xsize < 4 || ysize < 4)
return;
seeds.reset (xsize * ysize);
if (subtype > 0)
push_walls ();
if (subtype == 0 || subtype == 2)
for (int i = (xsize + ysize) / 2; i; --i)
push (point (rmg_rndm (1, xsize - 2), rmg_rndm (1, ysize - 2)));
bool full = subtype == 3;
/* recursively generate the walls of the maze */
/* first pop a random starting point */
while (seeds.size)
{
point p = pop_rand ();
for (;;)
{
point pc;
maze [p.x][p.y] = '#';
if (find_free_point (pc, p) < 0)
break;
if (full)
push (p);
if (!rmg_rndm (8))
{
if (!full)
push (pc);
break;
}
p = pc;
}
}
seeds.free ();
}