1 /**
2  * @file
3  * @brief Implementations of some of the dungeon layouts.
4 **/
5 
6 #include "AppHdr.h"
7 
8 #include "dgn-layouts.h"
9 
10 #include "coord.h"
11 #include "coordit.h"
12 #include "dungeon.h"
13 #include "stringutil.h"
14 #include "tag-version.h"
15 #include "terrain.h"
16 
17 static bool _find_forbidden_in_area(dgn_region& area, unsigned int mask);
18 static int _count_antifeature_in_box(int x0, int y0, int x1, int y1,
19                                      dungeon_feature_type feat);
20 static int _trail_random_dir(int pos, int bound, int margin);
21 static void _make_trail(int xs, int xr, int ys, int yr, int corrlength,
22                         int intersect_chance, int no_corr,
23                         coord_def& begin, coord_def& end);
24 static void _builder_extras(int level_number);
25 static bool _octa_room(dgn_region& region, int oblique_max,
26                        dungeon_feature_type type_floor);
27 static dungeon_feature_type _random_wall();
28 static void _chequerboard(dgn_region& region, dungeon_feature_type target,
29                           dungeon_feature_type floor1,
30                           dungeon_feature_type floor2);
31 static int _box_room_door_spot(int x, int y);
32 static int  _box_room_doors(int bx1, int bx2, int by1, int by2, int new_doors);
33 static void _box_room(int bx1, int bx2, int by1, int by2,
34                       dungeon_feature_type wall_type);
35 static bool _is_wall(int x, int y);
36 static void _big_room(int level_number);
37 static void _diamond_rooms(int level_number);
38 static int _good_door_spot(int x, int y);
39 static bool _make_room(int sx,int sy,int ex,int ey,int max_doors, int doorlevel);
40 static void _make_random_rooms(int num, int max_doors, int door_level,
41                                int max_x, int max_y, int max_room_size);
42 static void _place_pool(dungeon_feature_type pool_type, uint8_t pool_x1,
43                         uint8_t pool_y1, uint8_t pool_x2, uint8_t pool_y2);
44 static void _many_pools(dungeon_feature_type pool_type);
45 static bool _may_overwrite_pos(coord_def c);
46 static void _build_river(dungeon_feature_type river_type);
47 static void _build_lake(dungeon_feature_type lake_type);
48 
dgn_build_basic_level()49 void dgn_build_basic_level()
50 {
51     int level_number = env.absdepth0;
52 
53     int corrlength = 2 + random2(14);
54     int no_corr = (one_chance_in(100) ? 500 + random2(500)
55                                       : 30 + random2(200));
56     int intersect_chance = (one_chance_in(20) ? 400 : random2(20));
57 
58     coord_def begin;
59     coord_def end;
60 
61     vector<coord_def> upstairs;
62 
63     _make_trail(35, 30, 35, 20, corrlength, intersect_chance, no_corr,
64                  begin, end);
65 
66     if (!begin.origin() && !end.origin())
67     {
68         env.grid(begin) = DNGN_STONE_STAIRS_DOWN_I;
69         env.grid(end)   = DNGN_STONE_STAIRS_UP_I;
70         upstairs.push_back(begin);
71     }
72 
73     begin.reset(); end.reset();
74 
75     _make_trail(10, 15, 10, 15, corrlength, intersect_chance, no_corr,
76                  begin, end);
77 
78     if (!begin.origin() && !end.origin())
79     {
80         env.grid(begin) = DNGN_STONE_STAIRS_DOWN_II;
81         env.grid(end)   = DNGN_STONE_STAIRS_UP_II;
82         upstairs.push_back(begin);
83     }
84 
85     begin.reset(); end.reset();
86 
87     _make_trail(50, 20, 10, 15, corrlength, intersect_chance, no_corr,
88                  begin, end);
89 
90     if (!begin.origin() && !end.origin())
91     {
92         env.grid(begin) = DNGN_STONE_STAIRS_DOWN_III;
93         env.grid(end)   = DNGN_STONE_STAIRS_UP_III;
94         upstairs.push_back(begin);
95     }
96 
97     for (auto pathstart = upstairs.begin();
98          pathstart != upstairs.end(); pathstart++)
99     {
100         auto pathend = pathstart;
101         pathend++;
102         for (; pathend != upstairs.end(); pathend++)
103             join_the_dots(*pathstart, *pathend, MMT_VAULT);
104     }
105 
106     if (level_number > 1 && one_chance_in(16))
107         _big_room(level_number);
108 
109     if (random2(level_number) > 6 && one_chance_in(3))
110         _diamond_rooms(level_number);
111 
112     // Make some rooms:
113     int doorlevel = random2(11);
114     int roomsize  = 4 + random2(5);
115     roomsize += random2(6); // force a sequence point between random calls
116 
117     int no_rooms = random_choose_weighted(636, (5 + random2avg(29, 2)),
118                                           49, 100,
119                                           15, 1);
120 
121     _make_random_rooms(no_rooms, 2 + random2(8), doorlevel, 50, 40, roomsize);
122 
123     _make_random_rooms(1 + random2(3), 1, doorlevel, 55, 45, 6);
124 
125     _builder_extras(level_number);
126 }
127 
dgn_build_bigger_room_level()128 void dgn_build_bigger_room_level()
129 {
130     for (rectangle_iterator ri(10); ri; ++ri)
131         if (env.grid(*ri) == DNGN_ROCK_WALL
132             && !map_masked(*ri, MMT_VAULT))
133         {
134             env.grid(*ri) = DNGN_FLOOR;
135         }
136 
137     dungeon_feature_type pool_type = DNGN_DEEP_WATER;
138 
139     if (one_chance_in(15))
140         pool_type = DNGN_TREE;
141 
142     _many_pools(pool_type);
143 
144     if (one_chance_in(3))
145     {
146         if (coinflip())
147             _build_river(DNGN_DEEP_WATER);
148         else
149             _build_lake(DNGN_DEEP_WATER);
150     }
151 
152     dgn_place_stone_stairs(true);
153 }
154 
155 // A more chaotic version of city level.
dgn_build_chaotic_city_level(dungeon_feature_type force_wall)156 void dgn_build_chaotic_city_level(dungeon_feature_type force_wall)
157 {
158     // TODO: Attach this information to the vault name string
159     //       instead of the build method string.
160     env.level_build_method += make_stringf(" [%s]",
161         force_wall == NUM_FEATURES ? "any" : dungeon_feature_name(force_wall));
162 
163     dungeon_feature_type drawing = DNGN_ROCK_WALL;
164 
165     int number_boxes = random_choose_weighted(32, 4000,
166                                               24, 3000,
167                                               16, 5000,
168                                               8, 2000,
169                                               1, 1000);
170 
171     if (force_wall != NUM_FEATURES)
172         drawing = force_wall;
173     else
174     {
175         drawing = random_choose_weighted(10, DNGN_ROCK_WALL,
176                                          5, DNGN_STONE_WALL,
177                                          3, DNGN_METAL_WALL);
178     }
179 
180     dgn_replace_area(10, 10, (GXM - 10), (GYM - 10), DNGN_ROCK_WALL,
181                      DNGN_FLOOR, MMT_VAULT);
182 
183     // replace_area can also be used to fill in:
184     uint8_t b1x, b1y, b2x, b2y;
185     for (int i = 0; i < number_boxes; i++)
186     {
187         int room_width = 3 + random2(7);
188         room_width += random2(5); // force a sequence point between random2s
189         int room_height = 3 + random2(7);
190         room_height += random2(5); // ditto
191 
192         b1x = 11 + random2(GXM - 21 - room_width);
193         b1y = 11 + random2(GYM - 21 - room_height);
194 
195         b2x = b1x + room_width;
196         b2y = b1y + room_height;
197 
198         dgn_region box = dgn_region::absolute(b1x, b1y, b2x, b2y);
199         if (_find_forbidden_in_area(box, MMT_VAULT))
200             continue;
201 
202         if (_count_antifeature_in_box(b1x-1, b1y-1, b2x+2, b2y+2, DNGN_FLOOR))
203             continue;
204 
205         if (force_wall == NUM_FEATURES && one_chance_in(3))
206         {
207             drawing = random_choose_weighted(261, DNGN_ROCK_WALL,
208                                              116, DNGN_STONE_WALL,
209                                              40, DNGN_METAL_WALL);
210         }
211 
212         if (one_chance_in(3))
213             _box_room(b1x, b2x, b1y, b2y, drawing);
214         else
215         {
216             dgn_replace_area(b1x, b1y, b2x, b2y, DNGN_FLOOR, drawing,
217                              MMT_VAULT);
218         }
219     }
220 
221     dgn_region room = dgn_region::absolute(25, 25, 55, 45);
222 
223     // A market square.
224     if (!_find_forbidden_in_area(room, MMT_VAULT) && one_chance_in(4))
225     {
226         int oblique_max = 0;
227         if (!one_chance_in(4))
228             oblique_max = 5 + random2(20);      // used elsewhere {dlb}
229 
230         dungeon_feature_type feature = DNGN_FLOOR;
231         if (one_chance_in(10))
232             feature = random_choose(DNGN_DEEP_WATER, DNGN_LAVA);
233 
234         _octa_room(room, oblique_max, feature);
235     }
236 }
237 
238 /* Helper functions */
239 
_find_forbidden_in_area(dgn_region & area,unsigned int mask)240 static bool _find_forbidden_in_area(dgn_region& area, unsigned int mask)
241 {
242     for (rectangle_iterator ri(area.pos, area.end()); ri; ++ri)
243         if (map_masked(*ri, mask))
244             return true;
245 
246     return false;
247 }
248 
_count_antifeature_in_box(int x0,int y0,int x1,int y1,dungeon_feature_type feat)249 static int _count_antifeature_in_box(int x0, int y0, int x1, int y1,
250                                      dungeon_feature_type feat)
251 {
252     return (x1-x0)*(y1-y0) - count_feature_in_box(x0, y0, x1, y1, feat);
253 }
254 
_trail_random_dir(int pos,int bound,int margin)255 static int _trail_random_dir(int pos, int bound, int margin)
256 {
257     int dir = 0;
258 
259     if (pos < margin)
260         dir = 1;
261     else if (pos > bound - margin)
262         dir = -1;
263 
264     if (dir == 0 || x_chance_in_y(2, 5))
265         dir = random_choose(-1, 1);
266 
267     return dir;
268 }
269 
_viable_trail_start_location(const coord_def & c)270 static bool _viable_trail_start_location(const coord_def &c)
271 {
272     if (env.grid(c) != DNGN_ROCK_WALL && env.grid(c) != DNGN_FLOOR
273         || map_masked(c, MMT_VAULT))
274     {
275         return false;
276     }
277     for (orth_adjacent_iterator ai(c); ai; ++ai)
278     {
279         if (in_bounds(*ai)
280             && env.grid(*ai) == DNGN_ROCK_WALL
281             && !map_masked(*ai, MMT_VAULT))
282         {
283             return true;
284         }
285     }
286     return false;
287 }
288 
_make_trail(int xs,int xr,int ys,int yr,int corrlength,int intersect_chance,int no_corr,coord_def & begin,coord_def & end)289 static void _make_trail(int xs, int xr, int ys, int yr, int corrlength,
290                         int intersect_chance, int no_corr,
291                         coord_def& begin, coord_def& end)
292 {
293     int finish = 0;
294     int length = 0;
295     int tries = 200;
296 
297     coord_def pos;
298     coord_def dir(0, 0);
299 
300     do
301     {
302         pos.x = xs + random2(xr);
303         pos.y = ys + random2(yr);
304     }
305     while (!_viable_trail_start_location(pos) && tries-- > 0);
306 
307     if (tries < 0)
308         return;
309 
310     tries = 200;
311     // assign begin position
312     begin = pos;
313 
314     // wander
315     while (finish < no_corr)
316     {
317         if (!(tries--)) // give up after 200 tries
318             return;
319 
320         dir.reset();
321 
322         // Put something in to make it go to parts of map it isn't in now.
323         if (coinflip())
324             dir.x = _trail_random_dir(pos.x, GXM, 15);
325         else
326             dir.y = _trail_random_dir(pos.y, GYM, 15);
327 
328         if (dir.origin() || map_masked(pos + dir, MMT_VAULT))
329             continue;
330 
331         // Corridor length... change only when going vertical?
332         if (dir.x == 0 || length == 0)
333             length = random2(corrlength) + 2;
334 
335         for (int bi = 0; bi < length; bi++)
336         {
337             if (pos.x < X_BOUND_1 + 4)
338                 dir.set(1, 0);
339 
340             if (pos.x > (X_BOUND_2 - 4))
341                 dir.set(-1, 0);
342 
343             if (pos.y < Y_BOUND_1 + 4)
344                 dir.set(0, 1);
345 
346             if (pos.y > (Y_BOUND_2 - 4))
347                 dir.set(0, -1);
348 
349             if (map_masked(pos + dir, MMT_VAULT))
350                 break;
351 
352             // See if we stop due to intersection with another corridor/room.
353             if (env.grid(pos + dir * 2) == DNGN_FLOOR
354                 && !one_chance_in(intersect_chance))
355             {
356                 break;
357             }
358 
359             pos += dir;
360 
361             if (env.grid(pos) == DNGN_ROCK_WALL)
362                 env.grid(pos) = DNGN_FLOOR;
363         }
364 
365         if (finish == no_corr - 1 && env.grid(pos) != DNGN_FLOOR)
366             finish -= 2;
367 
368         finish++;
369     }
370 
371     // assign end position
372     end = pos;
373 }
374 
_builder_extras(int level_number)375 static void _builder_extras(int level_number)
376 {
377     if (level_number > 6 && one_chance_in(10))
378     {
379         dungeon_feature_type pool_type = (level_number < 11
380                                           || coinflip()) ? DNGN_DEEP_WATER
381                                                          : DNGN_LAVA;
382         if (one_chance_in(15))
383             pool_type = DNGN_TREE;
384 
385         _many_pools(pool_type);
386         return;
387     }
388 
389     //mv: It's better to be here so other dungeon features are not overridden
390     //    by water.
391     dungeon_feature_type river_type
392         = (one_chance_in(5 + level_number) ? DNGN_SHALLOW_WATER
393                                              : DNGN_DEEP_WATER);
394 
395     if (level_number > 11
396         && (one_chance_in(5) || (level_number > 15 && !one_chance_in(5))))
397     {
398         river_type = DNGN_LAVA;
399     }
400 
401     if (player_in_branch(BRANCH_GEHENNA))
402     {
403         river_type = DNGN_LAVA;
404 
405         if (coinflip())
406             _build_river(river_type);
407         else
408             _build_lake(river_type);
409     }
410     else if (player_in_branch(BRANCH_COCYTUS))
411     {
412         river_type = DNGN_DEEP_WATER;
413 
414         if (coinflip())
415             _build_river(river_type);
416         else
417             _build_lake(river_type);
418     }
419 
420     if (level_number > 8 && one_chance_in(16))
421         _build_river(river_type);
422     else if (level_number > 8 && one_chance_in(12))
423     {
424         _build_lake((river_type != DNGN_SHALLOW_WATER) ? river_type
425                                                        : DNGN_DEEP_WATER);
426     }
427 }
428 
_octa_room(dgn_region & region,int oblique_max,dungeon_feature_type type_floor)429 static bool _octa_room(dgn_region& region, int oblique_max,
430                        dungeon_feature_type type_floor)
431 {
432     // TODO: Attach this information to the vault name string
433     //       instead of the build method string.
434     env.level_build_method += make_stringf(" octa_room [oblique %d, %s]", oblique_max,
435                                            dungeon_feature_name(type_floor));
436 
437     int x,y;
438 
439     coord_def& tl = region.pos;
440     coord_def br = region.end();
441 
442     // Hack - avoid lava in the crypt {gdl}
443     if ((player_in_branch(BRANCH_CRYPT) || player_in_branch(BRANCH_TOMB)
444          || player_in_branch(BRANCH_COCYTUS))
445          && type_floor == DNGN_LAVA)
446     {
447         type_floor = DNGN_SHALLOW_WATER;
448     }
449 
450     int oblique = oblique_max;
451 
452     for (x = tl.x; x < br.x; x++)
453     {
454         if (x > tl.x - oblique_max)
455             oblique += 2;
456 
457         if (oblique > 0)
458             oblique--;
459     }
460 
461     oblique = oblique_max;
462 
463     for (x = tl.x; x < br.x; x++)
464     {
465         for (y = tl.y + oblique; y < br.y - oblique; y++)
466         {
467             if (map_masked(coord_def(x, y), MMT_VAULT))
468                 continue;
469 
470             if (_is_wall(x, y))
471                 env.grid[x][y] = type_floor;
472 
473             if (env.grid[x][y] == DNGN_FLOOR && type_floor == DNGN_SHALLOW_WATER)
474                 env.grid[x][y] = DNGN_SHALLOW_WATER;
475 
476             if (env.grid[x][y] == DNGN_CLOSED_DOOR && !feat_is_solid(type_floor))
477                 env.grid[x][y] = DNGN_FLOOR;       // ick
478         }
479 
480         if (x > br.x - oblique_max)
481             oblique += 2;
482 
483         if (oblique > 0)
484             oblique--;
485     }
486 
487     return true;
488 }
489 
_random_wall()490 static dungeon_feature_type _random_wall()
491 {
492     const dungeon_feature_type min_rand = DNGN_METAL_WALL;
493     const dungeon_feature_type max_rand = DNGN_STONE_WALL;
494     dungeon_feature_type wall;
495     do
496     {
497         wall = static_cast<dungeon_feature_type>(
498                    random_range(min_rand, max_rand));
499     }
500     while (wall == DNGN_SLIMY_WALL);
501 
502     return wall;
503 }
504 
505 // Helper function for chequerboard rooms.
506 // Note that box boundaries are INclusive.
_chequerboard(dgn_region & region,dungeon_feature_type target,dungeon_feature_type floor1,dungeon_feature_type floor2)507 static void _chequerboard(dgn_region& region, dungeon_feature_type target,
508                            dungeon_feature_type floor1,
509                            dungeon_feature_type floor2)
510 {
511     for (rectangle_iterator ri(region.pos, region.end()); ri; ++ri)
512         if (env.grid(*ri) == target && !map_masked(*ri, MMT_VAULT))
513             env.grid(*ri) = ((ri->x + ri->y) % 2) ? floor2 : floor1;
514 }
515 
_box_room_door_spot(int x,int y)516 static int _box_room_door_spot(int x, int y)
517 {
518     // If there is a door near us embedded in rock, we have to be a door too.
519     if (env.grid[x-1][y] == DNGN_CLOSED_DOOR
520             && _is_wall(x-1,y-1) && _is_wall(x-1,y+1)
521         || env.grid[x+1][y] == DNGN_CLOSED_DOOR
522             && _is_wall(x+1,y-1) && _is_wall(x+1,y+1)
523         || env.grid[x][y-1] == DNGN_CLOSED_DOOR
524             && _is_wall(x-1,y-1) && _is_wall(x+1,y-1)
525         || env.grid[x][y+1] == DNGN_CLOSED_DOOR
526             && _is_wall(x-1,y+1) && _is_wall(x+1,y+1))
527     {
528         env.grid[x][y] = DNGN_CLOSED_DOOR;
529         return 2;
530     }
531 
532     // To be a good spot for a door, we need non-wall on two sides and
533     // wall on two sides.
534     bool nor = _is_wall(x, y-1);
535     bool sou = _is_wall(x, y+1);
536     bool eas = _is_wall(x-1, y);
537     bool wes = _is_wall(x+1, y);
538 
539     if (nor == sou && eas == wes && nor != eas)
540         return 1;
541 
542     return 0;
543 }
544 
_box_room_doors(int bx1,int bx2,int by1,int by2,int new_doors)545 static int _box_room_doors(int bx1, int bx2, int by1, int by2, int new_doors)
546 {
547     int good_doors[200];        // 1 == good spot, 2 == door placed!
548     int spot;
549     int i, j;
550     int doors_placed = new_doors;
551 
552     // sanity
553     if (2 * (bx2-bx1 + by2-by1) > 200)
554         return 0;
555 
556     // Go through, building list of good door spots, and replacing wall
557     // with door if we're about to block off another door.
558     int spot_count = 0;
559 
560     // top & bottom
561     for (i = bx1 + 1; i < bx2; i++)
562     {
563         good_doors[spot_count ++] = _box_room_door_spot(i, by1);
564         good_doors[spot_count ++] = _box_room_door_spot(i, by2);
565     }
566     // left & right
567     for (i = by1+1; i < by2; i++)
568     {
569         good_doors[spot_count ++] = _box_room_door_spot(bx1, i);
570         good_doors[spot_count ++] = _box_room_door_spot(bx2, i);
571     }
572 
573     if (new_doors == 0)
574     {
575         // Count # of doors we HAD to place.
576         for (i = 0; i < spot_count; i++)
577             if (good_doors[i] == 2)
578                 doors_placed++;
579 
580         return doors_placed;
581     }
582 
583     // Avoid an infinite loop if there are not enough good spots. --KON
584     j = 0;
585     for (i = 0; i < spot_count; i++)
586         if (good_doors[i] == 1)
587             j++;
588 
589     if (new_doors > j)
590         new_doors = j;
591 
592     while (new_doors > 0 && spot_count > 0)
593     {
594         spot = random2(spot_count);
595         if (good_doors[spot] != 1)
596             continue;
597 
598         j = 0;
599         for (i = bx1 + 1; i < bx2; i++)
600         {
601             if (spot == j++)
602             {
603                 env.grid[i][by1] = DNGN_CLOSED_DOOR;
604                 break;
605             }
606             if (spot == j++)
607             {
608                 env.grid[i][by2] = DNGN_CLOSED_DOOR;
609                 break;
610             }
611         }
612 
613         for (i = by1 + 1; i < by2; i++)
614         {
615             if (spot == j++)
616             {
617                 env.grid[bx1][i] = DNGN_CLOSED_DOOR;
618                 break;
619             }
620             if (spot == j++)
621             {
622                 env.grid[bx2][i] = DNGN_CLOSED_DOOR;
623                 break;
624             }
625         }
626 
627         // Try not to put a door in the same place twice.
628         good_doors[spot] = 2;
629         new_doors --;
630     }
631 
632     return doors_placed;
633 }
634 
_box_room(int bx1,int bx2,int by1,int by2,dungeon_feature_type wall_type)635 static void _box_room(int bx1, int bx2, int by1, int by2,
636                       dungeon_feature_type wall_type)
637 {
638     // Hack -- avoid lava in the crypt. {gdl}
639     if ((player_in_branch(BRANCH_CRYPT) || player_in_branch(BRANCH_TOMB))
640          && wall_type == DNGN_LAVA)
641     {
642         wall_type = DNGN_SHALLOW_WATER;
643     }
644 
645     int new_doors, doors_placed;
646 
647     // Do top & bottom walls.
648     dgn_replace_area(bx1, by1, bx2, by1, DNGN_FLOOR, wall_type, MMT_VAULT);
649     dgn_replace_area(bx1, by2, bx2, by2, DNGN_FLOOR, wall_type, MMT_VAULT);
650 
651     // Do left & right walls.
652     dgn_replace_area(bx1, by1+1, bx1, by2-1, DNGN_FLOOR, wall_type, MMT_VAULT);
653     dgn_replace_area(bx2, by1+1, bx2, by2-1, DNGN_FLOOR, wall_type, MMT_VAULT);
654 
655     // Sometimes we have to place doors, or else we shut in other
656     // buildings' doors.
657     doors_placed = _box_room_doors(bx1, bx2, by1, by2, 0);
658 
659     new_doors = random_choose_weighted(54, 2,
660                                        23, 1,
661                                        23, 3);
662 
663     // Small rooms don't have as many doors.
664     if ((bx2-bx1)*(by2-by1) < 36 && new_doors > 1)
665         new_doors--;
666 
667     new_doors -= doors_placed;
668     if (new_doors > 0)
669         _box_room_doors(bx1, bx2, by1, by2, new_doors);
670 }
671 
_is_wall(int x,int y)672 static bool _is_wall(int x, int y)
673 {
674     return feat_is_wall(env.grid[x][y]);
675 }
676 
_big_room(int level_number)677 static void _big_room(int level_number)
678 {
679     dungeon_feature_type type_floor = DNGN_FLOOR;
680     dungeon_feature_type type_2 = DNGN_FLOOR;
681 
682     dgn_region region;
683 
684     int overlap_tries = 200;
685 
686     if (one_chance_in(4))
687     {
688         int oblique = 5 + random2(20);
689 
690         do
691         {
692             const int left = 8 + random2(30);
693             const int top = 8 + random2(22);
694             const int width = 21 + random2(10);
695             const int height = 21 + random2(8);
696             region = dgn_region(left, top, width, height);
697         }
698         while (_find_forbidden_in_area(region, MMT_VAULT)
699                && overlap_tries-- > 0);
700 
701         if (overlap_tries < 0)
702             return;
703 
704         // Usually floor, except at higher levels.
705         if (!one_chance_in(5) || level_number < 8 + random2(8))
706         {
707             _octa_room(region, oblique, DNGN_FLOOR);
708             return;
709         }
710 
711         // Default is lava.
712         type_floor = DNGN_LAVA;
713 
714         if (level_number > 7)
715         {
716             type_floor = (x_chance_in_y(14, level_number) ? DNGN_DEEP_WATER
717                                                           : DNGN_LAVA);
718         }
719 
720         _octa_room(region, oblique, type_floor);
721     }
722 
723     overlap_tries = 200;
724 
725     // What now?
726     do
727     {
728         // TODO: code duplication with above
729         const int left = 8 + random2(30);
730         const int top = 8 + random2(22);
731         const int width = 21 + random2(10);
732         const int height = 21 + random2(8);
733 
734         region = dgn_region(left, top, width, height);
735     }
736     while (_find_forbidden_in_area(region, MMT_VAULT) && overlap_tries-- > 0);
737 
738     if (overlap_tries < 0)
739         return;
740 
741     if (level_number > 7 && one_chance_in(4))
742     {
743         type_floor = (x_chance_in_y(14, level_number) ? DNGN_DEEP_WATER
744                                                       : DNGN_LAVA);
745     }
746 
747     // Make the big room.
748     dgn_replace_area(region.pos, region.end(), DNGN_ROCK_WALL, type_floor,
749                      MMT_VAULT);
750     dgn_replace_area(region.pos, region.end(), DNGN_CLOSED_DOOR, type_floor,
751                      MMT_VAULT);
752 
753     if (type_floor == DNGN_FLOOR)
754         type_2 = _random_wall();
755 
756     // No lava in the Crypt or Tomb, thanks!
757     if (player_in_branch(BRANCH_CRYPT) || player_in_branch(BRANCH_TOMB))
758     {
759         if (type_floor == DNGN_LAVA)
760             type_floor = DNGN_SHALLOW_WATER;
761 
762         if (type_2 == DNGN_LAVA)
763             type_2 = DNGN_SHALLOW_WATER;
764     }
765 
766     // Sometimes make it a chequerboard.
767     if (one_chance_in(4))
768         _chequerboard(region, type_floor, type_floor, type_2);
769     // Sometimes make an inside room w/ stone wall.
770     else if (one_chance_in(6))
771     {
772         int i = region.pos.x;
773         int j = region.pos.y;
774         int k = region.end().x;
775         int l = region.end().y;
776 
777         do
778         {
779             i += 2 + random2(3);
780             j += 2 + random2(3);
781             k -= 2 + random2(3);
782             l -= 2 + random2(3);
783             // check for too small
784             if (i >= k - 3)
785                 break;
786             if (j >= l - 3)
787                 break;
788 
789             _box_room(i, k, j, l, DNGN_STONE_WALL);
790         }
791         while (level_number < 1500);       // ie forever
792     }
793 }
794 
_diamond_rooms(int level_number)795 static void _diamond_rooms(int level_number)
796 {
797     int numb_diam = 1 + random2(10);
798     dungeon_feature_type type_floor = DNGN_DEEP_WATER;
799     int runthru = 0;
800     int i, oblique_max;
801 
802     // I guess no diamond rooms in either of these places. {dlb}
803     if (player_in_branch(BRANCH_DIS) || player_in_branch(BRANCH_TARTARUS))
804         return;
805 
806     if (level_number > 5 + random2(5) && coinflip())
807         type_floor = DNGN_SHALLOW_WATER;
808 
809     if (level_number > 10 + random2(5) && coinflip())
810         type_floor = DNGN_DEEP_WATER;
811 
812     if (level_number > 17 && coinflip())
813         type_floor = DNGN_LAVA;
814 
815     if (level_number > 10 && one_chance_in(15))
816         type_floor = random_choose(DNGN_STONE_WALL, DNGN_ROCK_WALL);
817 
818     if (level_number > 12 && one_chance_in(20))
819         type_floor = DNGN_METAL_WALL;
820 
821     if (player_in_branch(BRANCH_GEHENNA))
822         type_floor = DNGN_LAVA;
823     else if (player_in_branch(BRANCH_COCYTUS))
824         type_floor = DNGN_DEEP_WATER;
825 
826     for (i = 0; i < numb_diam; i++)
827     {
828         int overlap_tries = 200;
829         dgn_region room;
830         do
831         {
832             const int left = 8 + random2(43);
833             const int top = 8 + random2(35);
834             const int width = 6 + random2(15);
835             const int height = 6 + random2(10);
836             room = dgn_region(left, top, width, height);
837         }
838         while (_find_forbidden_in_area(room, MMT_VAULT)
839                && overlap_tries-- > 0);
840 
841         if (overlap_tries < 0)
842             return;
843 
844         oblique_max = room.size.x / 2;
845 
846         if (!_octa_room(room, oblique_max, type_floor))
847         {
848             runthru++;
849             if (runthru > 9)
850                 runthru = 0;
851             else
852             {
853                 i--;
854                 continue;
855             }
856         }
857     }
858 }
859 
_good_door_spot(int x,int y)860 static int _good_door_spot(int x, int y)
861 {
862     return (!feat_is_solid(env.grid[x][y]) || feat_is_closed_door(env.grid[x][y]))
863             && !feat_is_critical(env.grid[x][y]);
864 }
865 
866 // Returns TRUE if a room was made successfully.
_make_room(int sx,int sy,int ex,int ey,int max_doors,int doorlevel)867 static bool _make_room(int sx,int sy,int ex,int ey,int max_doors, int doorlevel)
868 {
869     int find_door = 0;
870     int diag_door = 0;
871     int rx, ry;
872 
873     // Check top & bottom for possible doors.
874     for (rx = sx; rx <= ex; rx++)
875     {
876         find_door += _good_door_spot(rx,sy);
877         find_door += _good_door_spot(rx,ey);
878     }
879 
880     // Check left and right for possible doors.
881     for (ry = sy + 1; ry < ey; ry++)
882     {
883         find_door += _good_door_spot(sx,ry);
884         find_door += _good_door_spot(ex,ry);
885     }
886 
887     diag_door += _good_door_spot(sx,sy);
888     diag_door += _good_door_spot(ex,sy);
889     diag_door += _good_door_spot(sx,ey);
890     diag_door += _good_door_spot(ex,ey);
891 
892     if ((diag_door + find_door) > 1 && max_doors == 1)
893         return false;
894 
895     if (find_door == 0 || find_door > max_doors)
896         return false;
897 
898     // Convert the area to floor.
899     for (rx = sx; rx <= ex; rx++)
900         for (ry = sy; ry <= ey; ry++)
901         {
902             if (!feat_has_dry_floor(env.grid[rx][ry]))
903                 env.grid[rx][ry] = DNGN_FLOOR;
904         }
905 
906     // Put some doors on the sides (but not in corners),
907     // where it makes sense to do so.
908     for (ry = sy + 1; ry < ey; ry++)
909     {
910         // left side
911         if (env.grid[sx-1][ry] == DNGN_FLOOR
912             && feat_is_solid(env.grid[sx-1][ry-1])
913             && feat_is_solid(env.grid[sx-1][ry+1]))
914         {
915             if (x_chance_in_y(doorlevel, 10))
916                 env.grid[sx-1][ry] = DNGN_CLOSED_DOOR;
917         }
918 
919         // right side
920         if (env.grid[ex+1][ry] == DNGN_FLOOR
921             && feat_is_solid(env.grid[ex+1][ry-1])
922             && feat_is_solid(env.grid[ex+1][ry+1]))
923         {
924             if (x_chance_in_y(doorlevel, 10))
925                 env.grid[ex+1][ry] = DNGN_CLOSED_DOOR;
926         }
927     }
928 
929     // Put some doors on the top & bottom.
930     for (rx = sx + 1; rx < ex; rx++)
931     {
932         // top
933         if (env.grid[rx][sy-1] == DNGN_FLOOR
934             && feat_is_solid(env.grid[rx-1][sy-1])
935             && feat_is_solid(env.grid[rx+1][sy-1]))
936         {
937             if (x_chance_in_y(doorlevel, 10))
938                 env.grid[rx][sy-1] = DNGN_CLOSED_DOOR;
939         }
940 
941         // bottom
942         if (env.grid[rx][ey+1] == DNGN_FLOOR
943             && feat_is_solid(env.grid[rx-1][ey+1])
944             && feat_is_solid(env.grid[rx+1][ey+1]))
945         {
946             if (x_chance_in_y(doorlevel, 10))
947                 env.grid[rx][ey+1] = DNGN_CLOSED_DOOR;
948         }
949     }
950 
951     return true;
952 }
953 
_make_random_rooms(int num,int max_doors,int door_level,int max_x,int max_y,int max_room_size)954 static void _make_random_rooms(int num, int max_doors, int door_level,
955                                int max_x, int max_y, int max_room_size)
956 {
957     int i, sx, sy, ex, ey, time_run = 0;
958     dgn_region room;
959 
960     for (i = 0; i < num; i++)
961     {
962         int overlap_tries = 200;
963         do
964         {
965             sx = 8 + random2(max_x);
966             sy = 8 + random2(max_y);
967             ex = sx + 2 + random2(max_room_size);
968             ey = sy + 2 + random2(max_room_size);
969             room = dgn_region::absolute(sx, sy, ex, ey);
970         }
971         while (_find_forbidden_in_area(room, MMT_VAULT)
972                && overlap_tries-- > 0);
973 
974         if (overlap_tries < 0)
975             return;
976 
977         if (!_make_room(sx, sy, ex, ey, max_doors, door_level))
978         {
979             time_run++;
980             i--;
981         }
982 
983         if (time_run > 30)
984         {
985             time_run = 0;
986             i++;
987         }
988     }
989 }
990 
_place_pool(dungeon_feature_type pool_type,uint8_t pool_x1,uint8_t pool_y1,uint8_t pool_x2,uint8_t pool_y2)991 static void _place_pool(dungeon_feature_type pool_type, uint8_t pool_x1,
992                         uint8_t pool_y1, uint8_t pool_x2,
993                         uint8_t pool_y2)
994 {
995     int i, j;
996     uint8_t left_edge, right_edge;
997 
998     // Don't place LAVA pools in crypt... use shallow water instead.
999     if (pool_type == DNGN_LAVA
1000         && (player_in_branch(BRANCH_CRYPT) || player_in_branch(BRANCH_TOMB)))
1001     {
1002         pool_type = DNGN_SHALLOW_WATER;
1003     }
1004 
1005     if (pool_x1 >= pool_x2 - 4 || pool_y1 >= pool_y2 - 4)
1006         return;
1007 
1008     left_edge  = pool_x1 + 2 + random2(pool_x2 - pool_x1);
1009     right_edge = pool_x2 - 2 - random2(pool_x2 - pool_x1);
1010 
1011     for (j = pool_y1 + 1; j < pool_y2 - 1; j++)
1012     {
1013         for (i = pool_x1 + 1; i < pool_x2 - 1; i++)
1014         {
1015             if (i >= left_edge && i <= right_edge && env.grid[i][j] == DNGN_FLOOR)
1016                 env.grid[i][j] = pool_type;
1017         }
1018 
1019         if (j - pool_y1 < (pool_y2 - pool_y1) / 2 || one_chance_in(4))
1020         {
1021             if (left_edge > pool_x1 + 1)
1022                 left_edge -= random2(3);
1023 
1024             if (right_edge < pool_x2 - 1)
1025                 right_edge += random2(3);
1026         }
1027 
1028         if (left_edge < pool_x2 - 1
1029             && (j - pool_y1 >= (pool_y2 - pool_y1) / 2
1030                 || left_edge <= pool_x1 + 2 || one_chance_in(4)))
1031         {
1032             left_edge += random2(3);
1033         }
1034 
1035         if (right_edge > pool_x1 + 1
1036             && (j - pool_y1 >= (pool_y2 - pool_y1) / 2
1037                 || right_edge >= pool_x2 - 2 || one_chance_in(4)))
1038         {
1039             right_edge -= random2(3);
1040         }
1041     }
1042 }
1043 
_many_pools(dungeon_feature_type pool_type)1044 static void _many_pools(dungeon_feature_type pool_type)
1045 {
1046     if (player_in_branch(BRANCH_COCYTUS))
1047         pool_type = DNGN_DEEP_WATER;
1048     else if (player_in_branch(BRANCH_GEHENNA))
1049         pool_type = DNGN_LAVA;
1050     else if (player_in_branch(BRANCH_CRYPT))
1051         return;
1052 
1053     const int num_pools = 20 + random2avg(9, 2);
1054     int pools = 0;
1055 
1056         // TODO: Attach this information to the vault name string
1057         //       instead of the build method string.
1058     env.level_build_method += make_stringf(" many_pools [%s %d]",
1059         dungeon_feature_name(pool_type), num_pools);
1060 
1061     for (int timeout = 0; pools < num_pools && timeout < 30000; ++timeout)
1062     {
1063         const int i = random_range(X_BOUND_1 + 1, X_BOUND_2 - 21);
1064         const int j = random_range(Y_BOUND_1 + 1, Y_BOUND_2 - 21);
1065         const int k = i + 2 + roll_dice(2, 9);
1066         const int l = j + 2 + roll_dice(2, 9);
1067         dgn_region room = dgn_region::absolute(i, j, k, l);
1068 
1069         if (_count_antifeature_in_box(i, j, k, l, DNGN_FLOOR) == 0
1070             && !_find_forbidden_in_area(room, MMT_VAULT))
1071         {
1072             _place_pool(pool_type, i, j, k, l);
1073             pools++;
1074         }
1075     }
1076 }
1077 
1078 // Used for placement of rivers/lakes.
_may_overwrite_pos(coord_def c)1079 static bool _may_overwrite_pos(coord_def c)
1080 {
1081     if (map_masked(c, MMT_VAULT))
1082         return false;
1083 
1084     const dungeon_feature_type grid = env.grid(c);
1085 
1086     // Don't overwrite any stairs or branch entrances.
1087     if (feat_is_stair(grid)
1088         || grid == DNGN_ENTER_SHOP
1089 #if TAG_MAJOR_VERSION == 34
1090         || grid == DNGN_TELEPORTER
1091 #endif
1092         || grid == DNGN_TRANSPORTER)
1093     {
1094         return false;
1095     }
1096 
1097     // Don't overwrite feature if there's a monster or item there.
1098     // Otherwise, items/monsters might end up stuck in deep water.
1099     return !monster_at(c) && env.igrid(c) == NON_ITEM;
1100 }
1101 
_build_river(dungeon_feature_type river_type)1102 static void _build_river(dungeon_feature_type river_type) //mv
1103 {
1104     if (player_in_branch(BRANCH_CRYPT) || player_in_branch(BRANCH_TOMB))
1105         return;
1106 
1107     // TODO: Attach this information to the vault name string
1108     //       instead of the build method string.
1109     env.level_build_method += make_stringf(" river [%s]",
1110                                            dungeon_feature_name(river_type));
1111 
1112     // Made rivers less wide... min width five rivers were too annoying. -- bwr
1113     int width = 3 + random2(4);
1114     int y = 10 - width + random2avg(GYM-10, 3);
1115 
1116     for (int i = 5; i < (GXM - 5); i++)
1117     {
1118         if (one_chance_in(3))   y++;
1119         if (one_chance_in(3))   y--;
1120         if (coinflip())         width++;
1121         if (coinflip())         width--;
1122 
1123         if (width < 2) width = 2;
1124         if (width > 6) width = 6;
1125 
1126         for (int j = y; j < y+width ; j++)
1127             if (j >= 5 && j <= GYM - 5)
1128             {
1129                 // Note that vaults might have been created in this area!
1130                 // So we'll avoid the silliness of orcs/Royal Jelly on
1131                 // lava and deep water grids. -- bwr
1132                 if (!one_chance_in(200) && _may_overwrite_pos(coord_def(i, j)))
1133                 {
1134                     if (width == 2 && river_type == DNGN_DEEP_WATER
1135                         && coinflip())
1136                     {
1137                         env.grid[i][j] = DNGN_SHALLOW_WATER;
1138                     }
1139                     else
1140                         env.grid[i][j] = river_type;
1141 
1142                     // Override existing markers.
1143                     env.markers.remove_markers_at(coord_def(i, j), MAT_ANY);
1144                 }
1145             }
1146     }
1147 }
1148 
_build_lake(dungeon_feature_type lake_type)1149 static void _build_lake(dungeon_feature_type lake_type) //mv
1150 {
1151     int i, j;
1152     int x1, y1, x2, y2;
1153 
1154     if (player_in_branch(BRANCH_CRYPT) || player_in_branch(BRANCH_TOMB))
1155         return;
1156 
1157         // TODO: Attach this information to the vault name string
1158         //       instead of the build method string.
1159     env.level_build_method += make_stringf(" lake [%s]",
1160                                            dungeon_feature_name(lake_type));
1161 
1162     x1 = 5 + random2(GXM - 30);
1163     y1 = 5 + random2(GYM - 30);
1164     x2 = x1 + 4 + random2(16);
1165     y2 = y1 + 8 + random2(12);
1166 
1167     for (j = y1; j < y2; j++)
1168     {
1169         if (coinflip())  x1 += random2(3);
1170         if (coinflip())  x1 -= random2(3);
1171         if (coinflip())  x2 += random2(3);
1172         if (coinflip())  x2 -= random2(3);
1173 
1174         if (j - y1 < (y2 - y1) / 2)
1175         {
1176             x2 += random2(3);
1177             x1 -= random2(3);
1178         }
1179         else
1180         {
1181             x2 -= random2(3);
1182             x1 += random2(3);
1183         }
1184 
1185         for (i = x1; i < x2 ; i++)
1186             if (j >= 5 && j <= GYM - 5 && i >= 5 && i <= GXM - 5)
1187             {
1188                 // Note that vaults might have been created in this area!
1189                 // So we'll avoid the silliness of monsters and items
1190                 // on lava and deep water grids. -- bwr
1191                 if (!one_chance_in(200) && _may_overwrite_pos(coord_def(i, j)))
1192                 {
1193                     env.grid[i][j] = lake_type;
1194 
1195                     // Override markers. (No underwater portals, please.)
1196                     env.markers.remove_markers_at(coord_def(i, j), MAT_ANY);
1197                 }
1198             }
1199     }
1200 }
1201