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