ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/random_maps/layout.C
Revision: 1.27
Committed: Sun Aug 22 20:23:06 2010 UTC (13 years, 9 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.26: +1 -1 lines
Log Message:
rproto.h => include and random_map.h => include/rmg.h

File Contents

# Content
1 /*
2 * This file is part of Deliantra, the Roguelike Realtime MMORPG.
3 *
4 * Copyright (©) 2010 Marc Alexander Lehmann / Robin Redeker / the Deliantra team
5 * Copyright (©) Crossfire Development Team (restored, original file without copyright notice)
6 *
7 * Deliantra is free software: you can redistribute it and/or modify it under
8 * the terms of the Affero GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the Affero GNU General Public License
18 * and the GNU General Public License along with this program. If not, see
19 * <http://www.gnu.org/licenses/>.
20 *
21 * The authors can be reached via e-mail to <support@deliantra.net>
22 */
23
24 #include <global.h>
25 #include <rmg.h>
26 #include <rproto.h>
27
28 void noinline
29 layout::alloc (int w, int h)
30 {
31 assert (sizeof (cell) == 1);
32
33 this->w = w;
34 this->h = h;
35
36 // we store the layout in a single contiguous memory layout
37 // first part consists of pointers to each column, followed
38 // by the actual columns (not rows!)
39 size = (sizeof (cell *) + sizeof (cell) * h) * w;
40 data = (cell **)salloc<char> (size);
41
42 cell *p = (cell *)(data + w);
43
44 for (int x = 0; x < w; ++x)
45 data [x] = p + x * h;
46 }
47
48 layout::layout (int w, int h)
49 {
50 alloc (w, h);
51 }
52
53 layout::layout (layout &copy)
54 {
55 alloc (copy.w, copy.h);
56
57 memcpy (data [0], copy.data [0], sizeof (cell) * h * w);
58 }
59
60 layout::layout (layout &orig, int x1, int y1, int x2, int y2)
61 {
62 w = x2 - x1;
63 h = y2 - y1;
64
65 // we only allocate space for the pointers
66 size = sizeof (cell *) * w;
67 data = (cell **)salloc<char> (size);
68
69 // and now we point back into the original layout
70 for (int x = 0; x < w; ++x)
71 data [x] = orig.data [x + x1] + y1;
72 }
73
74 layout::~layout ()
75 {
76 sfree ((char *)data, size);
77 }
78
79 void noinline
80 layout::fill (char fill)
81 {
82 //memset (data [0], fill, w * h); // only when contiguous :/
83 fill_rect (0, 0, w, h, fill);
84 }
85
86 void noinline
87 layout::replace (char from, char to)
88 {
89 for (int x = 0; x < w; ++x)
90 for (int y = 0; y < h; ++y)
91 if (data [x][y] == from)
92 data [x][y] = to;
93 }
94
95 void noinline
96 layout::rect (int x1, int y1, int x2, int y2, char fill)
97 {
98 --x2;
99
100 memset (data [x1] + y1, fill, y2 - y1);
101 memset (data [x2] + y1, fill, y2 - y1);
102
103 while (++x1 < x2)
104 data [x1][y1] = data [x1][y2 - 1] = fill;
105 }
106
107 void noinline
108 layout::fill_rect (int x1, int y1, int x2, int y2, char fill)
109 {
110 for (; x1 < x2; ++x1)
111 memset (data [x1] + y1, fill, y2 - y1);
112 }
113
114 void
115 layout::border (char fill)
116 {
117 rect (0, 0, w, h, fill);
118 }
119
120 void noinline
121 layout::fill_rand (int percent)
122 {
123 percent = lerp (percent, 0, 100, 0, 256);
124
125 for (int x = 0; x < w; ++x)
126 for (int y = 0; y < h; ++y)
127 data [x][y] = rmg_rndm (256) > percent ? 0 : '#';
128 }
129
130 /////////////////////////////////////////////////////////////////////////////
131
132 // erode by cellular automata
133 void noinline
134 layout::erode_1_2 (int c1, int c2, int repeat)
135 {
136 layout neu (w, h);
137
138 while (repeat--)
139 {
140 for (int x = 0; x < w; ++x)
141 {
142 coroapi::cede_to_tick ();
143
144 for (int y = 0; y < h; ++y)
145 {
146 int n1 = 0, n2 = 0;
147
148 // a 5x5 area, dx, dy, distance (1 == <= 1, 0 <= 2)
149 static I8 dds[][3] = {
150 { -2, -1, 0 }, { -2, 0, 0 }, { -2, 1, 0 },
151 { -1, -2, 0 }, { -1, -1, 1 }, { -1, 0, 1 }, { -1, 1, 1 }, { -1, 2, 0 },
152 { 0, -2, 0 }, { 0, -1, 1 }, { 0, 0, 1 }, { 0, 1, 1 }, { 0, 2, 0 },
153 { 1, -2, 0 }, { 1, -1, 1 }, { 1, 0, 1 }, { 1, 1, 1 }, { 1, 2, 0 },
154 { 2, -1, 0 }, { 2, 0, 0 }, { 2, 1, 0 },
155 };
156
157 for (int i = array_length (dds); i--; )
158 {
159 int nx = x + dds [i][0];
160 int ny = y + dds [i][1];
161
162 if (!IN_RANGE_EXC (nx, 0, w) || !IN_RANGE_EXC (ny, 0, h) || !data [nx][ny])
163 {
164 n1 += dds [i][2];
165 n2++;
166 }
167 }
168
169 neu [x][y] = n1 >= c1 || n2 <= c2 ? '#' : 0;
170 }
171 }
172
173 swap (neu);
174 }
175 }
176
177 /////////////////////////////////////////////////////////////////////////////
178
179 void
180 layout::print () const
181 {
182 for (int y = 0; y < h; y++)
183 {
184 for (int x = 0; x < w; x++)
185 {
186 U8 c = (U8)data [x][y];
187
188 if (!c)
189 c = ' ';
190 else if (c < 10)
191 c += '0';
192 else if (c < 32)
193 c += 'a' - 10;
194
195 putc ((char)c, stdout);
196 }
197
198 putc ('\n', stdout);
199 }
200
201 putc ('\n', stdout);
202 }
203
204 /////////////////////////////////////////////////////////////////////////////
205 // isolation remover - ensures single connected area
206
207 typedef fixed_stack<point> pointlist;
208
209 static void noinline
210 push_flood_fill (layout &dist, pointlist &seeds, int x, int y)
211 {
212 if (dist [x][y])
213 return;
214
215 while (y > 0 && !dist [x][y - 1])
216 --y;
217
218 int y0 = y;
219
220 while (y < dist.h && !dist [x][y])
221 {
222 seeds.push (point (x, y));
223
224 dist [x][y] = 1;
225 ++y;
226 }
227
228 while (--y >= y0)
229 {
230 if (x > 0 && !dist [x - 1][y]) push_flood_fill (dist, seeds, x - 1, y);
231 if (x < dist.w - 1 && !dist [x + 1][y]) push_flood_fill (dist, seeds, x + 1, y);
232 }
233 }
234
235 static void inline
236 make_tunnel (layout &dist, pointlist &seeds, int x, int y, U8 d, int perturb)
237 {
238 for (;;)
239 {
240 point neigh[4];
241 int ncnt = 0;
242
243 d += perturb > 1;
244
245 if (x > 0 && U8 (dist [x - 1][y]) < d && dist [x - 1][y] > 1) neigh [ncnt++] = point (x - 1, y);
246 if (x < dist.w - 1 && U8 (dist [x + 1][y]) < d && dist [x + 1][y] > 1) neigh [ncnt++] = point (x + 1, y);
247 if (y > 0 && U8 (dist [x][y - 1]) < d && dist [x][y - 1] > 1) neigh [ncnt++] = point (x, y - 1);
248 if (y < dist.h - 1 && U8 (dist [x][y + 1]) < d && dist [x][y + 1] > 1) neigh [ncnt++] = point (x, y + 1);
249
250 if (!ncnt)
251 return;
252
253 point p = neigh [perturb ? rmg_rndm (ncnt) : 0];
254
255 seeds.push (p);
256
257 x = p.x;
258 y = p.y;
259
260 d = dist [x][y];
261 dist [x][y] = 1;
262 }
263 }
264
265 static void inline
266 maybe_push (layout &dist, pointlist &seeds, int x, int y, U8 d)
267 {
268 char &D = dist [x][y];
269
270 if (U8 (D) > d) // if wall and higher distance, lower distance
271 D = d;
272 else if (D) // otherwise, if it's no room, this space is uninteresting
273 return;
274
275 seeds.push (point (x, y));
276 }
277
278 // isolation remover, works on a "distance" map
279 // the map must be initialised with 0 == rooms, 255 = walls
280 static void noinline
281 isolation_remover (layout &dist, unsigned int perturb = 2)
282 {
283 // dist contains
284 // 0 == invisited rooms
285 // 1 == visited rooms
286 // 2+ shortest distance to random near room
287
288 clamp_it (perturb, 0, 2);
289
290 // phase 1, find seed
291 int cnt = 0;
292 int x, y;
293
294 for (int i = 0; i < dist.w; ++i)
295 for (int j = 0; j < dist.h; ++j)
296 if (!dist [i][j] && !rmg_rndm (++cnt))
297 x = i, y = j;
298
299 if (!cnt)
300 {
301 // map is completely massive, this is not good,
302 // so make it empty instead.
303 dist.fill (1);
304 return;
305 }
306
307 fixed_stack<point> seeds (dist.w * dist.h * 5);
308
309 // found first free space - picking the first one gives
310 // us a slight bias for tunnels, but usually you won't
311 // notice that in-game
312 seeds.push (point (x, y));
313
314 // phase 2, while we have seeds, if
315 // seed is empty, floodfill, else grow
316
317 int rem_index = 0; // used to remove "somewhat ordered"
318
319 while (seeds.size)
320 {
321 coroapi::cede_to_tick ();
322
323 int i = perturb
324 ? rmg_rndm (max (0, seeds.size - 8), seeds.size - 1)
325 : rem_index ++ % seeds.size;
326
327 point p = seeds.remove (i);
328
329 x = p.x;
330 y = p.y;
331
332 if (!dist [x][y])
333 {
334 // found new isolated area, make tunnel
335 push_flood_fill (dist, seeds, x, y);
336 make_tunnel (dist, seeds, x, y, 254, perturb);
337 }
338 else
339 {
340 // nothing here, continue to expand
341 U8 d = U8 (dist [x][y]) + 1;
342
343 if (x < dist.w - 1) maybe_push (dist, seeds, x + 1, y, d);
344 if (x > 0) maybe_push (dist, seeds, x - 1, y, d);
345 if (y < dist.h - 1) maybe_push (dist, seeds, x, y + 1, d);
346 if (y > 0) maybe_push (dist, seeds, x, y - 1, d);
347 }
348 }
349 }
350
351 void
352 layout::isolation_remover (int perturb)
353 {
354 layout dist (w - 2, h - 2); // map without border
355
356 for (int x = 1; x < w - 1; ++x)
357 for (int y = 1; y < h - 1; ++y)
358 dist [x - 1][y - 1] = data [x][y] == '#' ? U8 (255) : 0;
359
360 ::isolation_remover (dist, perturb);
361
362 // now copy the tunnels over
363 for (int x = 1; x < w - 1; ++x)
364 for (int y = 1; y < h - 1; ++y)
365 if (data [x][y] == '#' && dist [x - 1][y - 1] == 1)
366 data [x][y] = 0;
367 }
368
369 /////////////////////////////////////////////////////////////////////////////
370
371 //+GPL
372
373 /* puts doors at appropriate locations in a maze. */
374 void
375 layout::doorify ()
376 {
377 int ndoors = w * h / 60; /* reasonable number of doors. */
378
379 coroapi::cede_to_tick ();
380
381 fixed_stack<point> doorloc (w * h);
382
383 /* make a list of possible door locations */
384 for (int i = 1; i < w - 1; i++)
385 for (int j = 1; j < h - 1; j++)
386 {
387 int sindex = surround_flag (*this, i, j);
388
389 if (sindex == 3 || sindex == 12) /* these are possible door sindex */
390 doorloc.push (point (i, j));
391 }
392
393 while (ndoors && doorloc.size)
394 {
395 point p = doorloc.remove (rmg_rndm (doorloc.size));
396
397 int sindex = surround_flag (*this, p.x, p.y);
398
399 if (sindex == 3 || sindex == 12) /* these are possible door sindex */
400 {
401 data [p.x][p.y] = 'D';
402 --ndoors;
403 }
404 }
405 }
406
407 /* takes a map and makes it symmetric: adjusts Xsize and
408 * Ysize to produce a symmetric map.
409 */
410 void
411 layout::symmetrize (int symmetry)
412 {
413 if (symmetry == SYMMETRY_NONE)
414 return;
415
416 layout sym_layout (
417 symmetry == SYMMETRY_X || symmetry == SYMMETRY_XY ? w * 2 - 3 : w,
418 symmetry == SYMMETRY_Y || symmetry == SYMMETRY_XY ? h * 2 - 3 : h
419 );
420
421 if (symmetry == SYMMETRY_X)
422 for (int i = 0; i < sym_layout.w / 2 + 1; i++)
423 for (int j = 0; j < sym_layout.h; j++)
424 {
425 sym_layout[i ][j] =
426 sym_layout[sym_layout.w - i - 1][j] = data [i][j];
427 }
428
429 if (symmetry == SYMMETRY_Y)
430 for (int i = 0; i < sym_layout.w; i++)
431 for (int j = 0; j < sym_layout.h / 2 + 1; j++)
432 {
433 sym_layout[i][j ] =
434 sym_layout[i][sym_layout.h - j - 1] = data [i][j];
435 }
436
437 if (symmetry == SYMMETRY_XY)
438 for (int i = 0; i < sym_layout.w / 2 + 1; i++)
439 for (int j = 0; j < sym_layout.h / 2 + 1; j++)
440 {
441 sym_layout[i ][j ] =
442 sym_layout[i ][sym_layout.h - j - 1] =
443 sym_layout[sym_layout.w - i - 1][j ] =
444 sym_layout[sym_layout.w - i - 1][sym_layout.h - j - 1] = data [i][j];
445 }
446
447 /* need to run the isolation remover for some layouts */
448 #if 0
449 switch (RP->map_layout_style)
450 {
451 case LAYOUT_ONION:
452 case LAYOUT_SNAKE:
453 case LAYOUT_SQUARE_SPIRAL:
454 // safe
455 break;
456
457 default:
458 sym_layout.isolation_remover ();
459 break;
460 }
461 #endif
462 sym_layout.isolation_remover ();
463
464 swap (sym_layout);
465 }
466
467 //-GPL
468
469 void
470 layout::rotate (int rotation)
471 {
472 coroapi::cede_to_tick ();
473
474 switch (rotation & 3)
475 {
476 case 2: /* a reflection */
477 {
478 layout new_layout (w, h);
479
480 for (int i = 0; i < w; i++) /* copy a reflection back */
481 for (int j = 0; j < h; j++)
482 new_layout [i][j] = data [w - i - 1][h - j - 1];
483
484 swap (new_layout);
485 }
486 break;
487
488 case 1:
489 case 3:
490 {
491 layout new_layout (h, w);
492
493 if (rotation == 1) /* swap x and y */
494 for (int i = 0; i < w; i++)
495 for (int j = 0; j < h; j++)
496 new_layout [j][i] = data [i][j];
497
498 if (rotation == 3) /* swap x and y */
499 for (int i = 0; i < w; i++)
500 for (int j = 0; j < h; j++)
501 new_layout [j][i] = data [w - i - 1][h - j - 1];
502
503 swap (new_layout);
504 }
505 break;
506 }
507 }
508
509 /////////////////////////////////////////////////////////////////////////////
510
511 //+GPL
512
513 /*
514 * Expands a maze by 2x in each dimension.
515 * H. S. Teoh
516 */
517
518 /* Copy the old tile X into the new one at location (i*2, j*2) and
519 * fill up the rest of the 2x2 result with \0:
520 * X ---> X \0
521 * \0 \0
522 */
523 static void inline
524 expand_misc (layout &newlayout, int i, int j, layout &maze)
525 {
526 newlayout[i * 2 + rmg_rndm (1)][j * 2 + rmg_rndm (1)] = maze[i][j];
527 /* (Note: no need to reset rest of 2x2 area to \0 because calloc does that
528 * for us.) */
529 }
530
531 /* Returns a bitmap that represents which squares on the right and bottom
532 * edges of a square (i,j) match the given character:
533 * 1 match on (i+1, j)
534 * 2 match on (i, j+1)
535 * 4 match on (i+1, j+1)
536 * and the possible combinations thereof.
537 */
538 static int noinline
539 calc_pattern (char ch, layout &maze, int i, int j)
540 {
541 int pattern = 0;
542
543 if (i + 1 < maze.w && maze[i + 1][j] == ch)
544 pattern |= 1;
545
546 if (j + 1 < maze.h)
547 {
548 if (maze[i][j + 1] == ch)
549 pattern |= 2;
550
551 if (i + 1 < maze.w && maze[i + 1][j + 1] == ch)
552 pattern |= 4;
553 }
554
555 return pattern;
556 }
557
558 /* Expand a wall. This function will try to sensibly connect the resulting
559 * wall to adjacent wall squares, so that the result won't have disconnected
560 * walls.
561 */
562 static void inline
563 expand_wall (layout &newlayout, int i, int j, layout &maze)
564 {
565 int wall_pattern = calc_pattern ('#', maze, i, j);
566 int door_pattern = calc_pattern ('D', maze, i, j);
567 int both_pattern = wall_pattern | door_pattern;
568
569 newlayout[i * 2][j * 2] = '#';
570
571 if (i + 1 < maze.w)
572 {
573 if (both_pattern & 1)
574 { /* join walls/doors to the right */
575 /* newlayout[i*2+1][j*2] = '#'; */
576 newlayout[i * 2 + 1][j * 2] = maze[i + 1][j];
577 }
578 }
579
580 if (j + 1 < maze.h)
581 {
582 if (both_pattern & 2)
583 { /* join walls/doors to the bottom */
584 /* newlayout[i*2][j*2+1] = '#'; */
585 newlayout[i * 2][j * 2 + 1] = maze[i][j + 1];
586 }
587
588 if (wall_pattern == 7)
589 { /* if orig maze is a 2x2 wall block,
590 * we fill the result with walls. */
591 newlayout[i * 2 + 1][j * 2 + 1] = '#';
592 }
593 }
594 }
595
596 /* This function will try to sensibly connect doors so that they meet up with
597 * adjacent walls. Note that it will also presumptuously delete (ignore) doors
598 * that it doesn't know how to correctly expand.
599 */
600 static void inline
601 expand_door (layout &newlayout, int i, int j, layout &maze)
602 {
603 int wall_pattern = calc_pattern ('#', maze, i, j);
604 int door_pattern = calc_pattern ('D', maze, i, j);
605 int join_pattern;
606
607 /* Doors "like" to connect to walls more than other doors. If there is
608 * a wall and another door, this door will connect to the wall and
609 * disconnect from the other door. */
610 if (wall_pattern & 3)
611 join_pattern = wall_pattern;
612 else
613 join_pattern = door_pattern;
614
615 newlayout[i * 2][j * 2] = 'D';
616
617 if (i + 1 < maze.w)
618 if (join_pattern & 1)
619 /* there is a door/wall to the right */
620 newlayout[i * 2 + 1][j * 2] = 'D';
621
622 if (j + 1 < maze.h)
623 if (join_pattern & 2)
624 /* there is a door/wall below */
625 newlayout[i * 2][j * 2 + 1] = 'D';
626 }
627
628 void
629 layout::expand2x ()
630 {
631 layout new_layout (w * 2 - 1, h * 2 - 1);
632
633 new_layout.clear ();
634
635 coroapi::cede_to_tick ();
636
637 for (int i = 0; i < w; i++)
638 for (int j = 0; j < h; j++)
639 switch (data [i][j])
640 {
641 case '#': expand_wall (new_layout, i, j, *this); break;
642 case 'D': expand_door (new_layout, i, j, *this); break;
643 default: expand_misc (new_layout, i, j, *this); break;
644 }
645
646 swap (new_layout);
647 }
648
649 /////////////////////////////////////////////////////////////////////////////
650
651 /* checks the maze to see if I can stick a horizontal(dir = 0) wall
652 (or vertical, dir == 1)
653 here which ends up on other walls sensibly. */
654 static int
655 can_make_wall (const layout &maze, int dx, int dy, int dir)
656 {
657 int i1;
658 int length = 0;
659
660 /* dont make walls if we're on the edge. */
661 if (dx == 0 || dx == (maze.w - 1) || dy == 0 || dy == (maze.h - 1))
662 return -1;
663
664 /* don't make walls if we're ON a wall. */
665 if (maze [dx][dy] != 0)
666 return -1;
667
668 if (dir == 0) /* horizontal */
669 {
670 int y = dy;
671
672 for (i1 = dx - 1; i1 > 0; i1--)
673 {
674 int sindex = surround_flag2 (maze, i1, y);
675
676 if (sindex == 1) break;
677 if (sindex != 0) return -1; /* can't make horiz. wall here */
678 if (maze[i1][y] != 0) return -1; /* can't make horiz. wall here */
679
680 length++;
681 }
682
683 for (i1 = dx + 1; i1 < maze.w - 1; i1++)
684 {
685 int sindex = surround_flag2 (maze, i1, y);
686
687 if (sindex == 2) break;
688 if (sindex != 0) return -1; /* can't make horiz. wall here */
689 if (maze[i1][y] != 0) return -1; /* can't make horiz. wall here */
690
691 length++;
692 }
693 return length;
694 }
695 else
696 { /* vertical */
697 int x = dx;
698
699 for (i1 = dy - 1; i1 > 0; i1--)
700 {
701 int sindex = surround_flag2 (maze, x, i1);
702
703 if (sindex == 4) break;
704 if (sindex != 0) return -1; /* can't make vert. wall here */
705 if (maze[x][i1] != 0) return -1; /* can't make horiz. wall here */
706
707 length++;
708 }
709
710 for (i1 = dy + 1; i1 < maze.h - 1; i1++)
711 {
712 int sindex = surround_flag2 (maze, x, i1);
713
714 if (sindex == 8) break;
715 if (sindex != 0) return -1; /* can't make verti. wall here */
716 if (maze[x][i1] != 0) return -1; /* can't make horiz. wall here */
717
718 length++;
719 }
720
721 return length;
722 }
723
724 return -1;
725 }
726
727 int
728 make_wall (layout &maze, int x, int y, int dir)
729 {
730 maze[x][y] = 'D'; /* mark a door */
731
732 switch (dir)
733 {
734 case 0: /* horizontal */
735 {
736 for (int i1 = x - 1; maze[i1][y] == 0; --i1) maze[i1][y] = '#';
737 for (int i1 = x + 1; maze[i1][y] == 0; ++i1) maze[i1][y] = '#';
738 break;
739 }
740 case 1: /* vertical */
741 {
742 for (int i1 = y - 1; maze[x][i1] == 0; --i1) maze[x][i1] = '#';
743 for (int i1 = y + 1; maze[x][i1] == 0; ++i1) maze[x][i1] = '#';
744 break;
745 }
746 }
747
748 return 0;
749 }
750
751 void
752 layout::roomify ()
753 {
754 int tries = w * h / 30;
755
756 coroapi::cede_to_tick ();
757
758 for (int ti = 0; ti < tries; ti++)
759 {
760 /* starting location for looking at creating a door */
761 int dx = rmg_rndm (w);
762 int dy = rmg_rndm (h);
763
764 /* results of checking on creating walls. */
765 int cx = can_make_wall (*this, dx, dy, 0); /* horizontal */
766 int cy = can_make_wall (*this, dx, dy, 1); /* vertical */
767
768 if (cx == -1)
769 {
770 if (cy != -1)
771 make_wall (*this, dx, dy, 1);
772
773 continue;
774 }
775
776 if (cy == -1)
777 {
778 make_wall (*this, dx, dy, 0);
779 continue;
780 }
781
782 if (cx < cy)
783 make_wall (*this, dx, dy, 0);
784 else
785 make_wall (*this, dx, dy, 1);
786 }
787 }
788
789 //-GPL
790
791 /////////////////////////////////////////////////////////////////////////////
792
793 // inspired mostly by http://www.jimrandomh.org/misc/caves.txt
794 void
795 layout::gen_cave (int subtype)
796 {
797 switch (subtype)
798 {
799 // a rough cave
800 case 0:
801 fill_rand (rmg_rndm (85, 97));
802 break;
803
804 // corridors
805 case 1:
806 fill_rand (rmg_rndm (5, 40));
807 erode_1_2 (5, 2, 10);
808 erode_1_2 (5, -1, 10);
809 erode_1_2 (5, 2, 1);
810 break;
811
812 // somewhat open, some room-like structures
813 case 2:
814 fill_rand (45);
815 erode_1_2 (5, 2, 4);
816 erode_1_2 (5, -1, 3);
817 break;
818
819 // wide open, roundish
820 case 3:
821 fill_rand (45);
822 erode_1_2 (5, 0, 5);
823 erode_1_2 (5, 1, 1);
824 break;
825 }
826
827 border ();
828 isolation_remover (1);
829 }
830
831 void
832 layout::gen_castle ()
833 {
834 fill ('#');
835
836 for (int n = w * h / 30 + 1; n--; )
837 {
838 int rw = rmg_rndm (6, 10);
839 int rh = rmg_rndm (6, 10);
840
841 if (rw > w || rh > h)
842 continue;
843
844 int rx = rmg_rndm (0, w - rw);
845 int ry = rmg_rndm (0, h - rh);
846
847 rect (rx, ry, rx + rw, ry + rh, '#');
848 fill_rect (rx + 1, ry + 1, rx + rw - 1, ry + rh - 1, 0);
849 }
850
851 border ();
852 isolation_remover (0);
853 }
854
855 static void
856 gen_mixed_ (layout &maze, random_map_params *RP)
857 {
858 if (maze.w > maze.h && maze.w > 16)
859 {
860 int m = rmg_rndm (8, maze.w - 8);
861
862 layout m1 (maze, 0, 0, m , maze.h); gen_mixed_ (m1, RP);
863 layout m2 (maze, m, 0, maze.w, maze.h); gen_mixed_ (m2, RP);
864 }
865 else if (maze.h > 16)
866 {
867 int m = rmg_rndm (8, maze.h - 8);
868
869 layout m1 (maze, 0, 0, maze.w, m ); gen_mixed_ (m1, RP);
870 layout m2 (maze, 0, m, maze.w, maze.h); gen_mixed_ (m2, RP);
871 }
872 else
873 {
874 RP->map_layout_style = rmg_rndm (NROFLAYOUTS - 2) + 1;
875
876 if (RP->map_layout_style == LAYOUT_MULTIPLE)
877 ++RP->map_layout_style;
878
879 maze.generate (RP);
880 }
881
882 coroapi::cede_to_tick ();
883 }
884
885 // recursive subdivision with random sublayouts
886 static void
887 gen_mixed (layout &maze, random_map_params *RP)
888 {
889 random_map_params &rp = *new random_map_params (RP);
890 gen_mixed_ (maze, &rp);
891 delete &rp;
892
893 maze.border ();
894
895 // exits currently do not work so well, as they
896 // are currently often found together, so nuke entrances
897 maze.replace ('<', ' ');
898
899 maze.isolation_remover (0);
900 }
901
902 //+GPL
903
904 /* function selects the maze function and gives it whatever
905 arguments it needs. */
906 void
907 layout::generate (random_map_params *RP)
908 {
909 switch (RP->map_layout_style)
910 {
911 case LAYOUT_ONION:
912 map_gen_onion (*this, RP->layoutoptions1, RP->layoutoptions2);
913
914 if (!(rmg_rndm (3)) && !(RP->layoutoptions1 & (RMOPT_WALLS_ONLY | RMOPT_WALL_OFF)))
915 roomify ();
916
917 break;
918
919 case LAYOUT_MAZE:
920 maze_gen (*this, RP->get_iv ("maze_type", rmg_rndm (4)));
921
922 if (rmg_rndm (2))
923 doorify ();
924
925 break;
926
927 case LAYOUT_SPIRAL:
928 map_gen_spiral (*this, RP->layoutoptions1);
929
930 if (rmg_rndm (2))
931 doorify ();
932
933 break;
934
935 case LAYOUT_ROGUELIKE:
936 /* Don't put symmetry in rogue maps. There isn't much reason to
937 * do so in the first place (doesn't make it any more interesting),
938 * but more importantly, the symmetry code presumes we are symmetrizing
939 * spirals, or maps with lots of passages - making a symmetric rogue
940 * map fails because its likely that the passages the symmetry process
941 * creates may not connect the rooms.
942 */
943 RP->symmetry_used = SYMMETRY_NONE;
944 roguelike_layout_gen (*this, RP->layoutoptions1);
945 /* no doorifying... done already */
946 break;
947
948 case LAYOUT_SNAKE:
949 make_snake_layout (*this, RP->layoutoptions1);
950
951 if (rmg_rndm (2))
952 roomify ();
953
954 break;
955
956 case LAYOUT_SQUARE_SPIRAL:
957 make_square_spiral_layout (*this, RP->layoutoptions1);
958
959 if (rmg_rndm (2))
960 roomify ();
961
962 break;
963
964 case LAYOUT_CAVE:
965 gen_cave (RP->get_iv ("cave_type", rmg_rndm (4)));
966
967 if (rmg_rndm (2))
968 doorify ();
969
970 break;
971
972 case LAYOUT_CASTLE:
973 gen_castle ();
974
975 if (rmg_rndm (2))
976 doorify ();
977
978 break;
979
980 case LAYOUT_MULTIPLE:
981 gen_mixed (*this, RP);
982 break;
983
984 default:
985 abort ();
986 }
987 }
988
989 //-GPL
990
991 #if 0
992 static void
993 gen_village (layout &maze)
994 {
995 maze.clear ();
996 maze.border ();
997
998 for (int n = maze.w * maze.h / 200 + 1; n--; )
999 {
1000 int rw = rmg_rndm (6, 10);
1001 int rh = rmg_rndm (6, 10);
1002
1003 int rx = rmg_rndm (2, maze.w - rw - 2);
1004 int ry = rmg_rndm (2, maze.h - rh - 2);
1005
1006 maze.rect (rx, ry, rx + rw, ry + rh, '#');
1007 }
1008
1009 maze.border ();
1010 maze.isolation_remover (2);
1011 }
1012
1013 static struct demo
1014 {
1015 demo ()
1016 {
1017 rmg_rndm.seed (time (0));
1018 extern void hack();hack ();
1019
1020 for(int i=1;i<100;i++)
1021 {
1022 layout maze (40, 30);
1023 maze.fill_rand (99);
1024 maze.border ();
1025 maze.isolation_remover (2);
1026 maze.print ();
1027 }
1028
1029 exit (1);
1030 }
1031 } demo;
1032 #endif