ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/random_maps/layout.C
Revision: 1.34
Committed: Sat Nov 17 23:40:02 2018 UTC (5 years, 6 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.33: +1 -0 lines
Log Message:
copyright update 2018

File Contents

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