/*
* File: dgn-height.cc
* Summary: Dungeon heightmap routines.
*/
#include "AppHdr.h"
#include "coord.h"
#include "coordit.h"
#include "dgn-height.h"
#include "dungeon.h"
#include "random.h"
void dgn_initialise_heightmap(int height)
{
env.heightmap.reset(new grid_heightmap);
for (rectangle_iterator ri(0); ri; ++ri)
dgn_height_at(*ri) = height;
}
int resolve_range(int_range range, int nrolls)
{
return random_range(range.first, range.second, nrolls);
}
void dgn_island_centred_at(const coord_def &c,
int n_points,
int radius,
int_range height_delta_range,
int border_margin,
bool make_atoll)
{
for (int i = 0; i < n_points; ++i) {
const int thisrad = make_atoll? radius : random2(1 + radius);
const coord_def p = dgn_random_point_from(c, thisrad, border_margin);
if (!p.origin())
dgn_height_at(p) += resolve_range(height_delta_range);
}
}
void dgn_smooth_height_at(coord_def c, int radius, int max_height)
{
const int height = dgn_height_at(c);
if (max_height != DGN_UNDEFINED_HEIGHT && height > max_height)
return;
const 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 int nheight = dgn_height_at(p);
if (max_height != DGN_UNDEFINED_HEIGHT && nheight > max_height)
continue;
const coord_def off = c - p;
const int weight = max_delta - off.abs();
divisor += weight;
total += nheight * weight;
}
}
dgn_height_at(c) = total / divisor;
}
void dgn_smooth_heights(int radius, int npasses)
{
for (int i = 0; i < npasses; ++i)
{
const int xspan = GXM / 2, yspan = GYM / 2;
for (int y = yspan - 1; y >= 0; --y)
for (int x = xspan - 1; x >= 0; --x)
{
dgn_smooth_height_at(coord_def(x, y), radius);
dgn_smooth_height_at(coord_def(2 * xspan - x - 1, y), radius);
dgn_smooth_height_at(coord_def(x, 2 * yspan - y - 1), radius);
dgn_smooth_height_at(coord_def(2 * xspan - x - 1,
2 * yspan - y - 1),
radius);
}
}
}
//////////////////////////////////////////////////////////////////////
// dgn_island_plan
void dgn_island_plan::build(int nislands)
{
for (int i = 0; i < nislands; ++i)
build_island();
}
coord_def dgn_island_plan::pick_island_spot()
{
coord_def c;
// Try to find a spot that's not too close to other islands; this
// is not a guarantee, though.
for (int i = 0; i < 15; ++i)
{
// Primary island centres should have a little clearance
// around them, so use 2x the actual margin.
c = dgn_random_point_in_margin(level_border_depth * 2);
bool collides = false;
for (int j = 0, size = islands.size(); j < size; ++j)
{
const coord_def island = islands[j];
const coord_def dist = island - c;
if (dist.abs() < island_separation_dist2)
{
collides = true;
break;
}
}
if (!collides)
break;
}
islands.push_back(c);
return c;
}
void dgn_island_plan::build_island()
{
const coord_def c = pick_island_spot();
dgn_island_centred_at(c, resolve_range(n_island_centre_delta_points),
resolve_range(island_centre_radius_range),
island_centre_point_height_increment,
level_border_depth,
x_chance_in_y(atoll_roll, 100));
const int additional_heights = resolve_range(n_aux_centres);
for (int i = 0; i < additional_heights; ++i) {
const int addition_offset = resolve_range(aux_centre_offset_range);
const coord_def offsetC =
dgn_random_point_from(c, addition_offset, level_border_depth);
if (!offsetC.origin())
dgn_island_centred_at(
offsetC, resolve_range(n_island_aux_delta_points),
resolve_range(island_aux_radius_range),
island_aux_point_height_increment,
level_border_depth,
x_chance_in_y(atoll_roll, 100));
}
}
coord_def dgn_island_plan::pick_and_remove_random_island()
{
if (islands.empty())
return coord_def(0, 0);
const int lucky_island = random2(islands.size());
const coord_def c = islands[lucky_island];
islands.erase(islands.begin() + lucky_island);
return c;
}