/* * 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 (); }