From 606429a6ed4fd68778023b3f0ca1f2daec2f7e0f Mon Sep 17 00:00:00 2001 From: Peisong Xiao Date: Fri, 12 Jul 2024 20:11:56 -0400 Subject: [PATCH] finished random room generation --- src/Makefile | 5 +- src/map.cc | 347 +++++++++++++++++++++++++++------------------------ src/map.h | 39 ++++-- 3 files changed, 215 insertions(+), 176 deletions(-) diff --git a/src/Makefile b/src/Makefile index 68df479..379130a 100644 --- a/src/Makefile +++ b/src/Makefile @@ -18,10 +18,13 @@ EXEC = ../bin/cc3k # executable name ########## 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 ${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 diff --git a/src/map.cc b/src/map.cc index 6d531f8..49ec0fe 100644 --- a/src/map.cc +++ b/src/map.cc @@ -3,6 +3,8 @@ #include #include +#include + game_map::game_map(RNG *rng, const feature enabled_features): enabled_features{enabled_features} { map.reserve(MAP_HEIGHT * MAP_WIDTH); @@ -14,174 +16,26 @@ game_map::game_map(RNG *rng, const feature enabled_features): std::vector> room_dims = gen_room_dims(rng); // width height top left - std::vector> room_data = distr_rooms(rng, - room_dims); + std::vector room_data = distr_rooms(rng, room_dims); - for (size_t r = 0; r < room_data.size(); ++r) { - for (int x = 0; x < room_data[r].first.x; ++x) - 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 (size_t r = 0; r < room_data.size(); ++r) + fill_room(room_data[r], r); - for (int x = 0; x < room_data[r].first.x; ++x) { - map[remap_position({room_data[r].second.x + x, - room_data[r].second.y - 1})] = '-'; - map[remap_position({room_data[r].second.x + x, - room_data[r].second.y + - room_data[r].first.y + 1})] = '-'; - } + for (int x = 0; x < MAP_WIDTH; ++x) { + auto tmp = remap_position({x, 0}); + map[tmp] = '-'; + map[remap_position({x, MAP_HEIGHT - 1})] = '-'; + } - for (int y = -1; y <= room_data[r].first.y; ++y) { - map[remap_position({room_data[r].second.x - 1, - room_data[r].second.y + y})] = '|'; - map[remap_position({room_data[r].second.x + - room_data[r].first.x + 1, - room_data[r].second.y + y})] = '|'; - } + + for (int y = 0; y < MAP_HEIGHT; ++y) { + map[remap_position({0, y})] = '|'; + map[remap_position({MAP_WIDTH - 1, y})] = '|'; } map.shrink_to_fit(); } -std::vector> game_map::distr_rooms(RNG *rng, -std::vector> &room_dims) { - std::vector> result; - result.reserve(room_dims.size()); - - int max_layer = room_dims[room_dims.size() - 1].second; - // left, right, height - std::vector> 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> &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 &room1, - std::pair &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 &room1, - std::pair &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, int max_width, int max_height, RNG *rng) { @@ -226,15 +80,15 @@ const position_list game_map::get_available_positions() const { bool game_map::is_available(const position &pos) const { 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] == '#'; } -void game_map::print(display &out) const { +void game_map::print(display *out) const { for (std::size_t i = 0; i < map.size(); ++i) - out.print_char(remap_index(i), map[i] <= 9 ? '.' : map[i], - map[i] == '\\' || map[i] == '>' || - map[i] == '<' ? COLOR_PAIR(COLOR_BLUE) : A_NORMAL); + out->print_char(remap_index(i), map[i] <= 9 ? '.' : map[i], + map[i] == '\\' || map[i] == '>' || + map[i] == '<' ? COLOR_PAIR(COLOR_BLUE) : A_NORMAL); } 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), 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::distr_rooms(RNG *rng, + std::vector> &room_dims) { + std::vector result; + result.reserve(room_dims.size()); + + int max_layer = room_dims[room_dims.size() - 1].second; + // left, right, height + std::vector> 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 &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); +} diff --git a/src/map.h b/src/map.h index a349833..c4e73f6 100644 --- a/src/map.h +++ b/src/map.h @@ -42,7 +42,7 @@ public: const std::vector get_room_list() const; // 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_down_stairs() const; @@ -50,8 +50,25 @@ public: int get_up_stairs_room() const; int get_down_stairs_room() const; 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 { - return {idx % MAP_WIDTH, idx / MAP_HEIGHT}; + return {idx % MAP_WIDTH, idx / MAP_WIDTH}; } int remap_position(const position &pos) const { @@ -63,15 +80,17 @@ private: RNG *rng); std::vector> gen_room_dims(RNG *rng); - std::vector> distr_rooms(RNG *rng, - std::vector> - &room_dims); + std::vector distr_rooms(RNG *rng, + std::vector> &room_dims); - bool overlap_x(std::pair &room1, - std::pair &room2); - bool overlap_y(std::pair &room1, - std::pair &room2); - void jitter(RNG *rng, std::vector> &rooms); + bool overlap_x(room &room1, + room &room2); + bool overlap_y(room &room1, + room &room2); + void jitter(RNG *rng, std::vector &rooms); + + void fill_room(const room &r, const int num); + void gen_path(std::vector &rooms); }; const std::string default_map =