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