finished random room generation
This commit is contained in:
@ -18,10 +18,13 @@ EXEC = ../bin/cc3k # executable name
|
|||||||
|
|
||||||
########## Targets ##########
|
########## Targets ##########
|
||||||
|
|
||||||
|
test :
|
||||||
|
${CXX} ${CXXFLAGS} position.cc display.cc curses_input.cc curses_output.cc cursor.cc rng.cc map.cc testing.cc -o testing -lncurses
|
||||||
|
|
||||||
.PHONY : clean # not file names
|
.PHONY : clean # not file names
|
||||||
|
|
||||||
${EXEC} : ${OBJECTS} # link step
|
${EXEC} : ${OBJECTS} # link step
|
||||||
${CXX} ${CXXFLAGS} -O0 $^ -o $@ -lncurses # additional object files before $^
|
${CXX} ${CXXFLAGS} $^ -o $@ -lncurses # additional object files before $^
|
||||||
|
|
||||||
${OBJECTS} : ${MAKEFILE_NAME} # OPTIONAL : changes to this file => recompile
|
${OBJECTS} : ${MAKEFILE_NAME} # OPTIONAL : changes to this file => recompile
|
||||||
|
|
||||||
|
347
src/map.cc
347
src/map.cc
@ -3,6 +3,8 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
game_map::game_map(RNG *rng, const feature enabled_features):
|
game_map::game_map(RNG *rng, const feature enabled_features):
|
||||||
enabled_features{enabled_features} {
|
enabled_features{enabled_features} {
|
||||||
map.reserve(MAP_HEIGHT * MAP_WIDTH);
|
map.reserve(MAP_HEIGHT * MAP_WIDTH);
|
||||||
@ -14,174 +16,26 @@ game_map::game_map(RNG *rng, const feature enabled_features):
|
|||||||
std::vector<std::pair<position, int>> room_dims = gen_room_dims(rng);
|
std::vector<std::pair<position, int>> room_dims = gen_room_dims(rng);
|
||||||
|
|
||||||
// width height top left
|
// width height top left
|
||||||
std::vector<std::pair<position, position>> room_data = distr_rooms(rng,
|
std::vector<room> room_data = distr_rooms(rng, room_dims);
|
||||||
room_dims);
|
|
||||||
|
|
||||||
for (size_t r = 0; r < room_data.size(); ++r) {
|
for (size_t r = 0; r < room_data.size(); ++r)
|
||||||
for (int x = 0; x < room_data[r].first.x; ++x)
|
fill_room(room_data[r], r);
|
||||||
for (int y = 0; y < room_data[r].first.y; ++y)
|
|
||||||
map[remap_position({room_data[r].second.x + x,
|
|
||||||
room_data[r].second.y + y})] = r;
|
|
||||||
|
|
||||||
for (int x = 0; x < room_data[r].first.x; ++x) {
|
for (int x = 0; x < MAP_WIDTH; ++x) {
|
||||||
map[remap_position({room_data[r].second.x + x,
|
auto tmp = remap_position({x, 0});
|
||||||
room_data[r].second.y - 1})] = '-';
|
map[tmp] = '-';
|
||||||
map[remap_position({room_data[r].second.x + x,
|
map[remap_position({x, MAP_HEIGHT - 1})] = '-';
|
||||||
room_data[r].second.y +
|
}
|
||||||
room_data[r].first.y + 1})] = '-';
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int y = -1; y <= room_data[r].first.y; ++y) {
|
|
||||||
map[remap_position({room_data[r].second.x - 1,
|
for (int y = 0; y < MAP_HEIGHT; ++y) {
|
||||||
room_data[r].second.y + y})] = '|';
|
map[remap_position({0, y})] = '|';
|
||||||
map[remap_position({room_data[r].second.x +
|
map[remap_position({MAP_WIDTH - 1, y})] = '|';
|
||||||
room_data[r].first.x + 1,
|
|
||||||
room_data[r].second.y + y})] = '|';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
map.shrink_to_fit();
|
map.shrink_to_fit();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::pair<position, position>> game_map::distr_rooms(RNG *rng,
|
|
||||||
std::vector<std::pair<position, int>> &room_dims) {
|
|
||||||
std::vector<std::pair<position, position>> result;
|
|
||||||
result.reserve(room_dims.size());
|
|
||||||
|
|
||||||
int max_layer = room_dims[room_dims.size() - 1].second;
|
|
||||||
// left, right, height
|
|
||||||
std::vector<std::pair<position, int>> layer_data;
|
|
||||||
layer_data.reserve(max_layer + 1);
|
|
||||||
|
|
||||||
// distributing rooms horizontally
|
|
||||||
for (int layer = 0; layer <= max_layer; ++layer) {
|
|
||||||
int l = INF;
|
|
||||||
int r = 0;
|
|
||||||
int layer_height = 0;
|
|
||||||
|
|
||||||
// get the interval for the current layer
|
|
||||||
// and the max height of the layer
|
|
||||||
for (std::size_t i = 0; i < room_dims.size(); ++i)
|
|
||||||
if (room_dims[i].second == layer) {
|
|
||||||
l = std::min(l, (int)i);
|
|
||||||
r = std::max(r, (int)i);
|
|
||||||
layer_height = std::max(layer_height,
|
|
||||||
room_dims[i].first.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
layer_data.push_back({{l, r}, layer_height});
|
|
||||||
|
|
||||||
// distribute the current layer
|
|
||||||
if (l == r) {
|
|
||||||
result.push_back({{
|
|
||||||
0, rng->rand_under(ACTUAL_MAP_WIDTH -
|
|
||||||
room_dims[l].first.x + 1)
|
|
||||||
},
|
|
||||||
room_dims[l].first});
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
int left = 0;
|
|
||||||
int right = ACTUAL_MAP_WIDTH;
|
|
||||||
|
|
||||||
// every time, distribute the last one first
|
|
||||||
for (int i = l; i < r; ++i)
|
|
||||||
left += MIN_ROOM_SPACING + room_dims[i].first.x;
|
|
||||||
|
|
||||||
for (int i = r; i >= l; --i) {
|
|
||||||
int offset = rng->rand_between(left, right - room_dims[i].first.x + 1);
|
|
||||||
result.push_back({{0, offset}, room_dims[i].first});
|
|
||||||
|
|
||||||
right = offset - MIN_ROOM_SPACING;
|
|
||||||
|
|
||||||
if (i != l)
|
|
||||||
left -= MIN_ROOM_SPACING + room_dims[i - 1].first.x;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// distributing rooms vertically
|
|
||||||
int top = 0;
|
|
||||||
int bottom = ACTUAL_MAP_HEIGHT;
|
|
||||||
|
|
||||||
for (int i = 0; i < max_layer; ++i)
|
|
||||||
top += MIN_ROOM_SPACING + layer_data[i].second;
|
|
||||||
|
|
||||||
for (int i = max_layer; i >= 0; --i) {
|
|
||||||
int offset = rng->rand_between(top, bottom - layer_data[i].second + 1);
|
|
||||||
|
|
||||||
for (int j = layer_data[i].first.x; j <= layer_data[i].first.y; ++j)
|
|
||||||
result[j].second.y = offset;
|
|
||||||
|
|
||||||
bottom = offset - MIN_ROOM_SPACING;
|
|
||||||
|
|
||||||
if (i != 0)
|
|
||||||
top -= MIN_ROOM_SPACING + layer_data[i - 1].second;
|
|
||||||
}
|
|
||||||
|
|
||||||
// jitter
|
|
||||||
//jitter(rng, result);
|
|
||||||
|
|
||||||
//add padding
|
|
||||||
for (auto room : result)
|
|
||||||
room.first += {MAP_PADDING, MAP_PADDING};
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void game_map::jitter(RNG *rng,
|
|
||||||
std::vector<std::pair<position, position>> &rooms) {
|
|
||||||
for (auto target = rooms.rbegin(); target != rooms.rend(); ++target) {
|
|
||||||
int t = -INF;
|
|
||||||
int b = INF;
|
|
||||||
int l = -INF;
|
|
||||||
int r = INF;
|
|
||||||
|
|
||||||
for (auto i : rooms)
|
|
||||||
if (i != *target) {
|
|
||||||
if (overlap_x(*target, i)) {
|
|
||||||
if (target->second.y > i.second.y)
|
|
||||||
t = std::max(t,
|
|
||||||
i.second.y + i.first.y
|
|
||||||
+ MIN_ROOM_SPACING);
|
|
||||||
else
|
|
||||||
b = std::min(b,
|
|
||||||
i.second.y
|
|
||||||
- MIN_ROOM_SPACING);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (overlap_y(*target, i)) {
|
|
||||||
if (target->second.x > i.second.x)
|
|
||||||
l = std::max(l,
|
|
||||||
i.second.x + i.first.x
|
|
||||||
+ MIN_ROOM_SPACING);
|
|
||||||
else
|
|
||||||
r = std::min(r,
|
|
||||||
i.second.x
|
|
||||||
- MIN_ROOM_SPACING);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
target->second.x = rng->rand_between(l, r - target->first.x + 1);
|
|
||||||
target->second.y = rng->rand_between(t, b - target->first.y + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool game_map::overlap_x(std::pair<position, position> &room1,
|
|
||||||
std::pair<position, position> &room2) {
|
|
||||||
return (room1.second.x >= room2.second.x &&
|
|
||||||
room1.second.x - 1 <= room2.second.x + room2.first.x + 1) ||
|
|
||||||
(room2.second.x >= room1.second.x &&
|
|
||||||
room2.second.x - 1 <= room1.second.x + room1.first.x + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool game_map::overlap_y(std::pair<position, position> &room1,
|
|
||||||
std::pair<position, position> &room2) {
|
|
||||||
return (room1.second.y >= room2.second.y &&
|
|
||||||
room1.second.y - 1 <= room2.second.y + room2.first.y + 1) ||
|
|
||||||
(room2.second.y >= room1.second.y &&
|
|
||||||
room2.second.y - 1 <= room1.second.y + room1.first.y + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
position game_map::random_size(int min_width, int min_height,
|
position game_map::random_size(int min_width, int min_height,
|
||||||
int max_width, int max_height,
|
int max_width, int max_height,
|
||||||
RNG *rng) {
|
RNG *rng) {
|
||||||
@ -226,15 +80,15 @@ const position_list game_map::get_available_positions() const {
|
|||||||
|
|
||||||
bool game_map::is_available(const position &pos) const {
|
bool game_map::is_available(const position &pos) const {
|
||||||
int idx = remap_position(pos);
|
int idx = remap_position(pos);
|
||||||
return map[idx] <= 9 || map[idx] == '\\' || map[idx] == '>' ||
|
return map[idx] < MAX_ROOM_CNT || map[idx] == '\\' || map[idx] == '>' ||
|
||||||
map[idx] == '<' || map[idx] == '+' || map[idx] == '#';
|
map[idx] == '<' || map[idx] == '+' || map[idx] == '#';
|
||||||
}
|
}
|
||||||
|
|
||||||
void game_map::print(display &out) const {
|
void game_map::print(display *out) const {
|
||||||
for (std::size_t i = 0; i < map.size(); ++i)
|
for (std::size_t i = 0; i < map.size(); ++i)
|
||||||
out.print_char(remap_index(i), map[i] <= 9 ? '.' : map[i],
|
out->print_char(remap_index(i), map[i] <= 9 ? '.' : map[i],
|
||||||
map[i] == '\\' || map[i] == '>' ||
|
map[i] == '\\' || map[i] == '>' ||
|
||||||
map[i] == '<' ? COLOR_PAIR(COLOR_BLUE) : A_NORMAL);
|
map[i] == '<' ? COLOR_PAIR(COLOR_BLUE) : A_NORMAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
position game_map::get_up_stairs() const {
|
position game_map::get_up_stairs() const {
|
||||||
@ -333,3 +187,166 @@ position random_size(int min_width, int min_height,
|
|||||||
return {rng->rand_between(min_width, max_width + 1),
|
return {rng->rand_between(min_width, max_width + 1),
|
||||||
rng->rand_between(min_height, max_height + 1)};
|
rng->rand_between(min_height, max_height + 1)};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void game_map::fill_room(const room &r, const int num) {
|
||||||
|
for (int x = 0; x < r.width; ++x)
|
||||||
|
for (int y = 0; y < r.height; ++y)
|
||||||
|
map[remap_position({r.left + x,
|
||||||
|
r.top + y})] = num;
|
||||||
|
|
||||||
|
for (int x = 0; x < r.width; ++x) {
|
||||||
|
map[remap_position({r.left + x,
|
||||||
|
r.top - 1})] = '-';
|
||||||
|
map[remap_position({r.left + x,
|
||||||
|
r.top +
|
||||||
|
r.height})] = '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int y = -1; y <= r.height; ++y) {
|
||||||
|
map[remap_position({r.left - 1,
|
||||||
|
r.top + y})] = '|';
|
||||||
|
map[remap_position({r.left +
|
||||||
|
r.width,
|
||||||
|
r.top + y})] = '|';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<game_map::room> game_map::distr_rooms(RNG *rng,
|
||||||
|
std::vector<std::pair<position, int>> &room_dims) {
|
||||||
|
std::vector<room> result;
|
||||||
|
result.reserve(room_dims.size());
|
||||||
|
|
||||||
|
int max_layer = room_dims[room_dims.size() - 1].second;
|
||||||
|
// left, right, height
|
||||||
|
std::vector<std::pair<position, int>> layer_data;
|
||||||
|
layer_data.reserve(max_layer + 1);
|
||||||
|
|
||||||
|
// distributing rooms horizontally
|
||||||
|
for (int layer = 0; layer <= max_layer; ++layer) {
|
||||||
|
int l = INF;
|
||||||
|
int r = 0;
|
||||||
|
int layer_height = 0;
|
||||||
|
|
||||||
|
// get the interval for the current layer
|
||||||
|
// and the max height of the layer
|
||||||
|
for (std::size_t i = 0; i < room_dims.size(); ++i)
|
||||||
|
if (room_dims[i].second == layer) {
|
||||||
|
l = std::min(l, (int)i);
|
||||||
|
r = std::max(r, (int)i);
|
||||||
|
layer_height = std::max(layer_height,
|
||||||
|
room_dims[i].first.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
layer_data.push_back({{l, r}, layer_height});
|
||||||
|
|
||||||
|
// distribute the current layer
|
||||||
|
if (l == r) {
|
||||||
|
result.push_back({
|
||||||
|
0, rng->rand_under(ACTUAL_MAP_WIDTH -
|
||||||
|
room_dims[l].first.x + 1),
|
||||||
|
room_dims[l].first.x,
|
||||||
|
room_dims[l].first.y});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int left = 0;
|
||||||
|
int right = ACTUAL_MAP_WIDTH;
|
||||||
|
|
||||||
|
// every time, distribute the last one first
|
||||||
|
for (int i = l; i < r; ++i)
|
||||||
|
left += MIN_ROOM_SPACING + room_dims[i].first.x;
|
||||||
|
|
||||||
|
for (int i = r; i >= l; --i) {
|
||||||
|
int offset = rng->rand_between(left, right - room_dims[i].first.x + 1);
|
||||||
|
result.push_back({0, offset,
|
||||||
|
room_dims[i].first.x,
|
||||||
|
room_dims[i].first.y});
|
||||||
|
|
||||||
|
right = offset - MIN_ROOM_SPACING;
|
||||||
|
|
||||||
|
if (i != l)
|
||||||
|
left -= MIN_ROOM_SPACING + room_dims[i - 1].first.x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// distributing rooms vertically
|
||||||
|
int top = 0;
|
||||||
|
int bottom = ACTUAL_MAP_HEIGHT;
|
||||||
|
|
||||||
|
for (int i = 0; i < max_layer; ++i)
|
||||||
|
top += MIN_ROOM_SPACING + layer_data[i].second;
|
||||||
|
|
||||||
|
for (int i = max_layer; i >= 0; --i) {
|
||||||
|
int offset = rng->rand_between(top,
|
||||||
|
bottom - layer_data[i].second + 1);
|
||||||
|
|
||||||
|
for (int j = layer_data[i].first.x;
|
||||||
|
j <= layer_data[i].first.y; ++j)
|
||||||
|
result[j].top = offset;
|
||||||
|
|
||||||
|
bottom = offset - MIN_ROOM_SPACING;
|
||||||
|
|
||||||
|
if (i != 0)
|
||||||
|
top -= MIN_ROOM_SPACING + layer_data[i - 1].second;
|
||||||
|
}
|
||||||
|
|
||||||
|
// jitter
|
||||||
|
jitter(rng, result);
|
||||||
|
|
||||||
|
//add padding
|
||||||
|
for (std::size_t i = 0; i < result.size(); ++i) {
|
||||||
|
result[i].top += MAP_PADDING;
|
||||||
|
result[i].left += MAP_PADDING;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void game_map::jitter(RNG *rng,
|
||||||
|
std::vector<room> &rooms) {
|
||||||
|
for (int n = rooms.size() - 1; n >= 0; --n) {
|
||||||
|
int t = 0;
|
||||||
|
int b = ACTUAL_MAP_HEIGHT - 1;
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < rooms.size(); ++i) {
|
||||||
|
if (rooms[i] == rooms[n])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (overlap_x(rooms[n], rooms[i])) {
|
||||||
|
if (rooms[n].top > rooms[i].top)
|
||||||
|
t = std::max(t,
|
||||||
|
rooms[i].top +
|
||||||
|
rooms[i].height +
|
||||||
|
MIN_ROOM_SPACING);
|
||||||
|
else
|
||||||
|
b = std::min(b,
|
||||||
|
rooms[i].top -
|
||||||
|
MIN_ROOM_SPACING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t < b - rooms[n].height + 1)
|
||||||
|
rooms[n].top = rng->rand_between(t,
|
||||||
|
b - rooms[n].height + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool game_map::overlap_x(room &room1,
|
||||||
|
room &room2) {
|
||||||
|
return (room1.left >= room2.left &&
|
||||||
|
room1.left - MIN_ROOM_SPACING <=
|
||||||
|
room2.left + room2.width) ||
|
||||||
|
(room2.left >= room1.left &&
|
||||||
|
room2.left - MIN_ROOM_SPACING <=
|
||||||
|
room1.left + room1.width);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool game_map::overlap_y(room &room1,
|
||||||
|
room &room2) {
|
||||||
|
return (room1.top >= room2.top &&
|
||||||
|
room1.top - MIN_ROOM_SPACING <=
|
||||||
|
room2.top + room2.height) ||
|
||||||
|
(room2.top >= room1.top &&
|
||||||
|
room2.top - MIN_ROOM_SPACING <=
|
||||||
|
room1.top + room1.height);
|
||||||
|
}
|
||||||
|
39
src/map.h
39
src/map.h
@ -42,7 +42,7 @@ public:
|
|||||||
|
|
||||||
const std::vector<position_list> get_room_list() const;
|
const std::vector<position_list> get_room_list() const;
|
||||||
// IMPORTANT: always print a map before anything else
|
// IMPORTANT: always print a map before anything else
|
||||||
void print(display &out) const;
|
void print(display *out) const;
|
||||||
|
|
||||||
position get_up_stairs() const;
|
position get_up_stairs() const;
|
||||||
position get_down_stairs() const;
|
position get_down_stairs() const;
|
||||||
@ -50,8 +50,25 @@ public:
|
|||||||
int get_up_stairs_room() const;
|
int get_up_stairs_room() const;
|
||||||
int get_down_stairs_room() const;
|
int get_down_stairs_room() const;
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
struct room {
|
||||||
|
int top;
|
||||||
|
int left;
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
bool operator!=(const room &r) {
|
||||||
|
return !(*this == r);
|
||||||
|
}
|
||||||
|
bool operator==(const room &r) {
|
||||||
|
return top == r.top &&
|
||||||
|
left == r.left &&
|
||||||
|
width == r.width &&
|
||||||
|
height == r.height;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
position remap_index(const int idx) const {
|
position remap_index(const int idx) const {
|
||||||
return {idx % MAP_WIDTH, idx / MAP_HEIGHT};
|
return {idx % MAP_WIDTH, idx / MAP_WIDTH};
|
||||||
}
|
}
|
||||||
|
|
||||||
int remap_position(const position &pos) const {
|
int remap_position(const position &pos) const {
|
||||||
@ -63,15 +80,17 @@ private:
|
|||||||
RNG *rng);
|
RNG *rng);
|
||||||
|
|
||||||
std::vector<std::pair<position, int>> gen_room_dims(RNG *rng);
|
std::vector<std::pair<position, int>> gen_room_dims(RNG *rng);
|
||||||
std::vector<std::pair<position, position>> distr_rooms(RNG *rng,
|
std::vector<room> distr_rooms(RNG *rng,
|
||||||
std::vector<std::pair<position, int>>
|
std::vector<std::pair<position, int>> &room_dims);
|
||||||
&room_dims);
|
|
||||||
|
|
||||||
bool overlap_x(std::pair<position, position> &room1,
|
bool overlap_x(room &room1,
|
||||||
std::pair<position, position> &room2);
|
room &room2);
|
||||||
bool overlap_y(std::pair<position, position> &room1,
|
bool overlap_y(room &room1,
|
||||||
std::pair<position, position> &room2);
|
room &room2);
|
||||||
void jitter(RNG *rng, std::vector<std::pair<position, position>> &rooms);
|
void jitter(RNG *rng, std::vector<room> &rooms);
|
||||||
|
|
||||||
|
void fill_room(const room &r, const int num);
|
||||||
|
void gen_path(std::vector<room> &rooms);
|
||||||
};
|
};
|
||||||
|
|
||||||
const std::string default_map =
|
const std::string default_map =
|
||||||
|
Reference in New Issue
Block a user