#include "AppHdr.h" #include "branch.h" #include "coord.h" #include "coordit.h" #include "dungeon.h" #include "dgn-shoals.h" #include "env.h" #include "maps.h" #include "random.h" #include #include #include static int _shoals_heights[GYM][GXM]; static std::vector _shoals_islands; const int ISLAND_COLLIDE_DIST2 = 5 * 5; const int N_PERTURB_ISLAND_CENTER = 50; const int ISLAND_CENTER_RADIUS_LOW = 3; const int ISLAND_CENTER_RADIUS_HIGH = 10; const int N_PERTURB_OFFSET_LOW = 25; const int N_PERTURB_OFFSET_HIGH = 45; const int PERTURB_OFFSET_RADIUS_LOW = 2; const int PERTURB_OFFSET_RADIUS_HIGH = 7; const int _shoals_margin = 6; enum shoals_height_thresholds { SHT_STONE = 230, SHT_ROCK = 170, SHT_FLOOR = 0, SHT_SHALLOW_WATER = -14 }; static double _to_radians(int degrees) { return degrees * M_PI / 180; } static dungeon_feature_type _shoals_feature_at(const coord_def &c) { const int height = _shoals_heights[c.y][c.x]; return height >= SHT_STONE ? DNGN_STONE_WALL : height >= SHT_ROCK? DNGN_ROCK_WALL : height >= SHT_FLOOR? DNGN_FLOOR : height >= SHT_SHALLOW_WATER? DNGN_SHALLOW_WATER : DNGN_DEEP_WATER; } static void _shoals_init_heights() { for (int y = 0; y < GYM; ++y) for (int x = 0; x < GXM; ++x) _shoals_heights[y][x] = -17; } static double _angle_fuzz() { double fuzz = _to_radians(random2(15)); return coinflip()? fuzz : -fuzz; } static coord_def _random_point_from(const coord_def &c, int radius, int directed_angle = -1) { const double directed_radians( directed_angle == -1? 0.0 : _to_radians(directed_angle)); while (true) { const double angle = directed_angle == -1? _to_radians(random2(360)) : ((coinflip()? directed_radians : -directed_radians) + _angle_fuzz()); coord_def res = c + coord_def(radius * cos(angle), radius * sin(angle)); if (res.x >= _shoals_margin && res.x < GXM - _shoals_margin && res.y >= _shoals_margin && res.y < GYM - _shoals_margin) { return res; } } } static coord_def _random_point(int offset = 0) { return coord_def(random_range(offset, GXM - offset - 1), random_range(offset, GYM - offset - 1)); } static void _shoals_island_center(const coord_def &c, int n_perturb, int radius, int bounce_low, int bounce_high) { for (int i = 0; i < n_perturb; ++i) { coord_def p = _random_point_from(c, random2(1 + radius)); _shoals_heights[p.y][p.x] += random_range(bounce_low, bounce_high); } } static coord_def _shoals_pick_island_spot() { coord_def c; for (int i = 0; i < 15; ++i) { c = _random_point(_shoals_margin * 2); bool collides = false; for (int j = 0, size = _shoals_islands.size(); j < size; ++j) { const coord_def island = _shoals_islands[j]; const coord_def dist = island - c; if (dist.abs() < ISLAND_COLLIDE_DIST2) { collides = true; break; } } if (!collides) break; } _shoals_islands.push_back(c); return c; } static void _shoals_build_island() { coord_def c = _shoals_pick_island_spot(); _shoals_island_center(c, N_PERTURB_ISLAND_CENTER, random_range(ISLAND_CENTER_RADIUS_LOW, ISLAND_CENTER_RADIUS_HIGH), 40, 60); const int additional_heights = random2(4); for (int i = 0; i < additional_heights; ++i) { const int addition_offset = random_range(2, 10); coord_def offsetC = _random_point_from(c, addition_offset); _shoals_island_center(offsetC, random_range(N_PERTURB_OFFSET_LOW, N_PERTURB_OFFSET_HIGH), random_range(PERTURB_OFFSET_RADIUS_LOW, PERTURB_OFFSET_RADIUS_HIGH), 25, 35); } } static void _shoals_init_islands(int depth) { const int nislands = 20 - depth * 2; for (int i = 0; i < nislands; ++i) _shoals_build_island(); } // Cliffs are usually constructed in shallow water adjacent to deep // water (for effect). static void _shoals_build_cliff() { coord_def cliffc = _random_point(_shoals_margin * 2); if (in_bounds(cliffc)) { const int length = random_range(6, 15); double angle = _to_radians(random2(360)); for (int i = 0; i < length; i += 3) { int distance = i - length / 2; coord_def place = cliffc + coord_def(distance * cos(angle), distance * sin(angle)); coord_def fuzz = coord_def(random_range(-2, 2), random_range(-2, 2)); place += fuzz; _shoals_island_center(place, random_range(40, 60), 3, 100, 130); } } } static void _shoals_cliffs() { const int ncliffs = random_range(0, 6, 2); for (int i = 0; i < ncliffs; ++i) _shoals_build_cliff(); } static void _shoals_smooth_at(const coord_def &c, int radius) { int max_delta = radius * radius * 2 + 2; int divisor = 0; int total = 0; for (int y = c.y - radius; y <= c.y + radius; ++y) { for (int x = c.x - radius; x <= c.x + radius; ++x) { const coord_def p(x, y); if (!in_bounds(p)) continue; const coord_def off = c - p; int weight = max_delta - off.abs(); divisor += weight; total += _shoals_heights[p.y][p.x] * weight; } } _shoals_heights[c.y][c.x] = total / divisor; } static void _shoals_smooth() { for (rectangle_iterator ri(0); ri; ++ri) _shoals_smooth_at(*ri, 1); } static void _shoals_apply_level() { for (rectangle_iterator ri(1); ri; ++ri) grd(*ri) = _shoals_feature_at(*ri); } static coord_def _pick_shoals_island() { const int lucky_island = random2(_shoals_islands.size()); const coord_def c = _shoals_islands[lucky_island]; _shoals_islands.erase(_shoals_islands.begin() + lucky_island); return c; } struct point_sort_distance_from { coord_def bad_place; point_sort_distance_from(coord_def c) : bad_place(c) { } bool operator () (coord_def a, coord_def b) const { const int dista = (a - bad_place).abs(), distb = (b - bad_place).abs(); return dista >= distb; } }; static coord_def _pick_shoals_island_distant_from(coord_def bad_place) { ASSERT(!_shoals_islands.empty()); std::sort(_shoals_islands.begin(), _shoals_islands.end(), point_sort_distance_from(bad_place)); const int top_picks = std::min(4, int(_shoals_islands.size())); const int choice = random2(top_picks); coord_def chosen = _shoals_islands[choice]; _shoals_islands.erase(_shoals_islands.begin() + choice); return chosen; } static void _shoals_furniture(int margin) { if (at_branch_bottom()) { const coord_def c = _pick_shoals_island(); // Put all the stairs on one island. grd(c) = DNGN_STONE_STAIRS_UP_I; grd(c + coord_def(1, 0)) = DNGN_STONE_STAIRS_UP_II; grd(c - coord_def(1, 0)) = DNGN_STONE_STAIRS_UP_III; const coord_def p = _pick_shoals_island_distant_from(c); // Place the rune const map_def *vault = random_map_for_tag("shoal_rune"); dgn_place_map(vault, false, true, p); const int nhuts = std::min(8, int(_shoals_islands.size())); for (int i = 2; i < nhuts; ++i) { // Place (non-rune) minivaults on the other islands do vault = random_map_for_tag("shoal"); while (!vault); dgn_place_map(vault, false, true, _pick_shoals_island()); } } else { // Place stairs randomly. No elevators. for (int i = 0; i < 3; ++i) { int x, y; do { x = margin + random2(GXM - 2*margin); y = margin + random2(GYM - 2*margin); } while (grd[x][y] != DNGN_FLOOR); grd[x][y] = static_cast(DNGN_STONE_STAIRS_DOWN_I + i); do { x = margin + random2(GXM - 2*margin); y = margin + random2(GYM - 2*margin); } while (grd[x][y] != DNGN_FLOOR); grd[x][y] = static_cast(DNGN_STONE_STAIRS_UP_I + i); } } } void prepare_shoals(int level_number) { dgn_Build_Method += make_stringf(" shoals+ [%d]", level_number); dgn_Layout_Type = "shoals"; const int shoals_depth = level_id::current().depth - 1; dgn_replace_area(0, 0, GXM-1, GYM-1, DNGN_ROCK_WALL, DNGN_OPEN_SEA); _shoals_init_heights(); _shoals_islands.clear(); _shoals_init_islands(shoals_depth); _shoals_cliffs(); _shoals_smooth(); _shoals_apply_level(); _shoals_furniture(_shoals_margin); }