Crossfire Server, Trunk  1.75.0
room_gen_crawl.cpp
Go to the documentation of this file.
1 
26 #include <queue>
27 
28 #include <stdlib.h>
29 #include <global.h>
30 #include <random_map.h>
31 
32 
33 #define MAP_CRAWL_NONE 0
34 #define MAP_CRAWL_WEST 1
35 #define MAP_CRAWL_EAST 2
36 #define MAP_CRAWL_NORTH 4
37 #define MAP_CRAWL_SOUTH 8
38 // Mask of all the directions
39 #define MAP_CRAWL_ALL 15
40 
41 // Define to dump the layout of the internal paths determination, for debug purposes
42 //#define DEBUG_CRAWL
43 
44 
45 #ifdef DEBUG_CRAWL
46 
60 static void print_debug_paths(uint8_t *paths, int x_eff, int y_eff) {
61  for (int y = 0; y < y_eff; ++y) {
62  for (int x = 0; x < x_eff; ++x) {
63  switch (paths[x * y_eff + y]) {
64  case MAP_CRAWL_WEST:
65  printf("╸");
66  break;
68  printf("━");
69  break;
71  printf("┛");
72  break;
74  printf("┓");
75  break;
77  printf("┻");
78  break;
80  printf("┳");
81  break;
83  printf("┫");
84  break;
86  printf("╋");
87  break;
88  case MAP_CRAWL_EAST:
89  printf("╺");
90  break;
92  printf("┗");
93  break;
95  printf("┏");
96  break;
98  printf("┣");
99  break;
100  case MAP_CRAWL_NORTH:
101  printf("╹");
102  break;
104  printf("┃");
105  break;
106  case MAP_CRAWL_SOUTH:
107  printf("╻");
108  break;
109  case MAP_CRAWL_NONE:
110  printf(".");
111  break;
112  default:
113  printf("?");
114  }
115  }
116  printf("\n");
117  }
118 }
119 #endif
120 
145 char **map_gen_crawl(int xsize, int ysize, int iter, int hallway) {
146  // Create a two-dimensional array of zero-filled characters.
147  char **crawl = (char **)malloc(xsize * sizeof(char *));
148  for (int i = 0; i < xsize; ++i) {
149  crawl[i] = (char *)calloc(ysize, sizeof(char));
150  }
151 
152  // If no hallway size is provided, select at random in range [1,3].
153  if (hallway == 0) {
154  hallway = (RANDOM() % 3) + 1;
155  }
156 
157  // Determine the functional x and y sizes for maze design.
158  // This is affected by the hallway size.
159  // We remove one from the size for the unaccounted part of the outer wall.
160  // Also handle the upper-left of walls in each block.
161  int x_eff = (xsize - 1) / (hallway + 1),
162  y_eff = (ysize - 1) / (hallway + 1);
163 
164  // If x_eff or y_eff are zero, bail entirely.
165  if (!x_eff || !y_eff) {
166  // Crawl is a blank dungeon.
167  // I don't think this code path will happen without a defined hallway width that is far too large.
168  return crawl;
169  }
170 
171  // Allocate an array of int8s to hold the flags of where the map goes.
172  uint8_t *paths = (uint8_t *)calloc(xsize * ysize, sizeof(uint8_t));
173 
174  // Determine where we start spidering from.
175  // Make sure to avoid starting at the edge if we have enough room
176  int startx, starty;
177  if (x_eff <= 2) {
178  startx = RANDOM() % x_eff;
179  }
180  else {
181  startx = (RANDOM() % (x_eff - 2)) + 1;
182  }
183  if (y_eff <= 2) {
184  starty = RANDOM() % y_eff;
185  }
186  else {
187  starty = (RANDOM() % (y_eff - 2)) + 1;
188  }
189 
190  // Count the sections we create.
191  int sections = 0;
192 
193  // Add the start position to the queue
194  std::queue<int> queue;
195  queue.push(startx * y_eff + starty);
196 
197  // Set a flag for marking the first entry.
198  // We need it to guarantee generating something.
199  int8_t first = 1;
200 
201  while (!queue.empty()) {
202  // Dequeue the point info from the queue
203  // Why is this two separate calls? Really STL?
204  int loc = queue.front();
205  queue.pop();
206  // Massage the point info out of the int
207  int at_x = loc / y_eff;
208  int at_y = loc % y_eff;
209 
210  ++sections;
211 
212  // Sanity check for the tile. If it is already filled, skip.
213  if (paths[at_x * y_eff + at_y] != MAP_CRAWL_NONE) {
214  continue;
215  }
216 
217  // Paths we can take. Start by assuming all directions are clear.
218  uint8_t avail_paths = MAP_CRAWL_ALL;
219 
220  // Paths we must take to complete open paths. Start by assuming none.
221  uint8_t req_paths = MAP_CRAWL_NONE;
222 
223  // Determine what directions are available.
224  // We cannot leave the bounds of the map,
225  // Nor can we trample existing path definitions.
226  if (at_x == 0) {
227  avail_paths &= ~MAP_CRAWL_WEST;
228  }
229  else if (paths[(at_x - 1) * y_eff + at_y] != MAP_CRAWL_NONE) {
230  // If non-blank tile has no path to here, exclude it.
231  if ((paths[(at_x - 1) * y_eff + at_y] & MAP_CRAWL_EAST) == 0) {
232  avail_paths &= ~MAP_CRAWL_WEST;
233  }
234  // Otherwise, require it.
235  else {
236  req_paths |= MAP_CRAWL_WEST;
237  }
238  }
239  if (at_x == x_eff - 1) {
240  avail_paths &= ~MAP_CRAWL_EAST;
241  }
242  else if (paths[(at_x + 1) * y_eff + at_y] != MAP_CRAWL_NONE) {
243  // If non-blank tile has no path to here, exclude it.
244  if ((paths[(at_x + 1) * y_eff + at_y] & MAP_CRAWL_WEST) == 0) {
245  avail_paths &= ~MAP_CRAWL_EAST;
246  }
247  // Otherwise, require it.
248  else {
249  req_paths |= MAP_CRAWL_EAST;
250  }
251  }
252  if (at_y == 0) {
253  avail_paths &= ~MAP_CRAWL_NORTH;
254  }
255  else if (paths[at_x * y_eff + at_y - 1] != MAP_CRAWL_NONE) {
256  // If non-blank tile has no path to here, exclude it.
257  if ((paths[at_x * y_eff + at_y - 1] & MAP_CRAWL_SOUTH) == 0) {
258  avail_paths &= ~MAP_CRAWL_NORTH;
259  }
260  // Otherwise, require it.
261  else {
262  req_paths |= MAP_CRAWL_NORTH;
263  }
264  }
265  if (at_y == y_eff - 1) {
266  avail_paths &= ~MAP_CRAWL_SOUTH;
267  }
268  else if (paths[at_x * y_eff + at_y + 1] != MAP_CRAWL_NONE) {
269  // If non-blank tile has no path to here, exclude it.
270  if ((paths[at_x * y_eff + at_y + 1] & MAP_CRAWL_NORTH) == 0) {
271  avail_paths &= ~MAP_CRAWL_SOUTH;
272  }
273  // Otherwise, require it.
274  else {
275  req_paths |= MAP_CRAWL_SOUTH;
276  }
277  }
278 
279  uint8_t path;
280  // Make sure the first try of the attmept always generates at least one path.
281  // Otherwise it is possible (albeit rare, 1/16 in each generation) to have an all-wall result.
282  do {
283  path = (RANDOM() & avail_paths) | req_paths;
284  } while (first && !path);
285 
286  // Mask out the unvailable paths and required paths, and randomly generate remaining connections.
287  paths[at_x * y_eff + at_y] = path;
288  first = 0;
289 
290  // Now we enqueue any new paths into the queue
291  // req_paths happens to tell us where some adjacent stuff is,
292  // and checking them is cheap and easy.
293  // If not from a required connection, we add to the queue.
294  // We can assert there is a filled space when it was required, so we
295  // don't need to process that tile again.
296  if (paths[at_x * y_eff + at_y] & MAP_CRAWL_WEST & ~req_paths) {
297  queue.push((at_x - 1) * y_eff + at_y);
298  }
299  if (paths[at_x * y_eff + at_y] & MAP_CRAWL_EAST & ~req_paths) {
300  queue.push((at_x + 1) * y_eff + at_y);
301  }
302  if (paths[at_x * y_eff + at_y] & MAP_CRAWL_NORTH & ~req_paths) {
303  queue.push(at_x * y_eff + at_y - 1);
304  }
305  if (paths[at_x * y_eff + at_y] & MAP_CRAWL_SOUTH & ~req_paths) {
306  queue.push(at_x * y_eff + at_y + 1);
307  }
308 
309  }
310 
311  // Sanity check on our sections.
312  if (iter == 0 && sections < x_eff * y_eff / 5) {
313  // Clean up this run and try again.
314  for (int i = 0; i < xsize; ++i) {
315  free(crawl[i]);
316  }
317  free(crawl);
318  free(paths);
319  // Single try at recursion.
320  return map_gen_crawl(xsize, ysize, 1, hallway);
321  }
322 
323 #ifdef DEBUG_CRAWL
324  print_debug_paths(paths, x_eff, y_eff);
325 #endif
326 
327  // Translate our generation onto the actual random map.
328 
329  // First, figure out the offset to center our generatable space
330  // in the center of the map size
331  int offset_x = (xsize - (hallway + 1) * x_eff - 1) / 2,
332  offset_y = (ysize - (hallway + 1) * y_eff - 1) / 2;
333 
334  // Fill in unused areas with wall
335  // Left side
336  for (int i = 0; i < offset_x; ++i) {
337  for (int j = 0; j < ysize; ++j) {
338  crawl[i][j] = '#';
339  }
340  }
341  // Top side
342  for (int j = 0; j < offset_y; ++j) {
343  for (int i = 0; i < xsize; ++i) {
344  crawl[i][j] = '#';
345  }
346  }
347  // Right side
348  for (int i = offset_x + (hallway + 1) * x_eff + 1; i < xsize; ++i) {
349  for (int j = 0; j < ysize; ++j) {
350  crawl[i][j] = '#';
351  }
352  }
353  // Bottom side
354  for (int j = offset_y + (hallway + 1) * y_eff + 1; j < ysize; ++j) {
355  for (int i = 0; i < xsize; ++i) {
356  crawl[i][j] = '#';
357  }
358  }
359 
360  // Handle the used area.
361  for (int x = 0; x < x_eff; ++x) {
362  for (int y = 0; y < y_eff; ++y) {
363  // We affect hallway + 2 tiles in each direction.
364  // But we shift hallway + 1 tiles for the next tile,
365  // which will cause some repeated wall constructions,
366  // but will prevent a mess of checks in this section.
367 
368  uint8_t cur_path = paths[x * y_eff + y];
369 
370  // If no paths, fill with walls
371  if ((cur_path & MAP_CRAWL_ALL) == 0) {
372  for (int tmpx = 0; tmpx < hallway + 2; ++tmpx) {
373  for (int tmpy = 0; tmpy < hallway + 2; ++tmpy) {
374  crawl[x * (hallway + 1) + offset_x + tmpx][y * (hallway + 1) + offset_y + tmpy] = '#';
375  }
376  }
377  }
378  // Otherwise, draw the necessary walls
379  else {
380  // If north is blocked, write walls
381  if ((cur_path & MAP_CRAWL_NORTH) == 0) {
382  for (int tmp = 0; tmp < hallway + 2; ++tmp) {
383  crawl[x * (hallway + 1) + offset_x + tmp][y * (hallway + 1) + offset_y] = '#';
384  }
385  }
386  // Do the same for each other direction.
387  if ((cur_path & MAP_CRAWL_SOUTH) == 0) {
388  for (int tmp = 0; tmp < hallway + 2; ++tmp) {
389  crawl[x * (hallway + 1) + offset_x + tmp][(y + 1) * (hallway + 1) + offset_y] = '#';
390  }
391  }
392  if ((cur_path & MAP_CRAWL_WEST) == 0) {
393  for (int tmp = 0; tmp < hallway + 2; ++tmp) {
394  crawl[x * (hallway + 1) + offset_x][y * (hallway + 1) + offset_y + tmp] = '#';
395  }
396  }
397  if ((cur_path & MAP_CRAWL_EAST) == 0) {
398  for (int tmp = 0; tmp < hallway + 2; ++tmp) {
399  crawl[(x + 1) * (hallway + 1) + offset_x][y * (hallway + 1) + offset_y + tmp] = '#';
400  }
401  }
402  }
403  }
404  }
405 
406  // Cleanup
407  free(paths);
408 
409  return crawl;
410 }
global.h
random_map.h
MAP_CRAWL_NORTH
#define MAP_CRAWL_NORTH
Definition: room_gen_crawl.cpp:36
MAP_CRAWL_EAST
#define MAP_CRAWL_EAST
Definition: room_gen_crawl.cpp:35
MAP_CRAWL_ALL
#define MAP_CRAWL_ALL
Definition: room_gen_crawl.cpp:39
MAP_CRAWL_SOUTH
#define MAP_CRAWL_SOUTH
Definition: room_gen_crawl.cpp:37
RANDOM
#define RANDOM()
Definition: define.h:628
map_gen_crawl
char ** map_gen_crawl(int xsize, int ysize, int iter, int hallway)
Generator of the crawl maze.
Definition: room_gen_crawl.cpp:145
MAP_CRAWL_NONE
#define MAP_CRAWL_NONE
Definition: room_gen_crawl.cpp:33
paths
*envar *is the environment if one that can also be used as an override If both the flag and the envar are the envar takes precedence name flag envar notes confdir conf absolute datadir data CROSSFIRE_LIBDIR absolute localdir CROSSFIRE_LOCALDIR absolute mapdir maps CROSSFIRE_MAPDIR relative to datadir or localdir playerdir playerdir CROSSFIRE_PLAYERDIR relative to localdir uniquedir uniquedir CROSSFIRE_UNIQUEDIR relative to localdir tmpdir tmpdir CROSSFIRE_TMPDIR absolute regions regions unused Paths marked absolute if you contain relative paths
Definition: server-directories.txt:25
MAP_CRAWL_WEST
#define MAP_CRAWL_WEST
Definition: room_gen_crawl.cpp:34