/* * This file is part of Deliantra, the Roguelike Realtime MMORPG. * * Copyright (©) 2005,2006,2007,2008,2009 Marc Alexander Lehmann / Robin Redeker / the Deliantra team * Copyright (©) 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 "random_map.h" #include "rproto.h" struct point { short x; short y; point () { } point (int x, int y) : x(x), y(y) { } }; /* global variables that everyone needs: don't want to pass them in as parameters every time. */ static point *seed_list; static int seed_size; static int xsize, ysize; static char **maze; /* 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 make_wall_free_list () { // xsize * ysize is plenty too much, but it's temporary seed_list = salloc (xsize * ysize); seed_size = 0; /* top and bottom wall */ for (int x = 2; x < xsize - 2; x++) { seed_list [seed_size++] = point (x, 0); seed_list [seed_size++] = point (x, ysize - 1); } /* left and right wall */ for (int y = 2; y < ysize - 2; y++) { seed_list [seed_size++] = point (0, y); seed_list [seed_size++] = point (xsize - 1, y); } } /* randomly returns one of the elements from the wall point list */ static point pop_point () { int index = rmg_rndm (seed_size); point p = seed_list [index]; /* write the last array point here */ seed_list [index] = seed_list [--seed_size]; return p; } /* 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; } /* recursive routine which will fill every available space in the maze with walls*/ static void fill_maze_full (point p) { point pc; /* write a wall here */ maze[p.x][p.y] = '#'; /* decide if we're going to pick from the wall_free_list */ if (rmg_rndm (2) && seed_size) fill_maze_full (pop_point ()); /* change the while to an if for a sparse maze. */ while (find_free_point (pc, p) != -1) fill_maze_full (pc); } /* recursive routine which will fill much of the maze, but will leave some free spots (possibly large) toward the center.*/ static void fill_maze_sparse (point p) { point pc; /* write a wall here */ maze[p.x][p.y] = '#'; /* decide if we're going to pick from the wall_free_list */ if (rmg_rndm (2) && seed_size) fill_maze_full (pop_point ()); /* change the if to a while for a complete maze. */ if (find_free_point (pc, p) != -1) fill_maze_full (pc); } /* 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 = 1, full, 0, sparse.*/ void maze_gen (Layout maze, int full) { xsize = maze->w; ysize = maze->h; ::maze = maze; maze->clear (); maze->border (); if (xsize < 4 || ysize < 4) return; make_wall_free_list (); /* recursively generate the walls of the maze */ /* first pop a random starting point */ while (seed_size) if (full) fill_maze_full (pop_point ()); else fill_maze_sparse (pop_point ()); /* clean up our intermediate data structures. */ sfree (seed_list, xsize * ysize); } #if 0 static struct demo { demo () { Layout layout (30, 30); rmg_rndm.seed (time (0)); for(int i=1;i<10;++i) { maze_gen (layout, 1); layout.print (); } exit (1); } } demo; #endif