diff --git a/gui/fsm.cpp b/gui/fsm.cpp index f507a0b..0031ca9 100644 --- a/gui/fsm.cpp +++ b/gui/fsm.cpp @@ -320,6 +320,8 @@ namespace gui { sound::mute(false); $debug_ui.debug(); shaders::reload(); + dbc::log("save map!"); + $map_ui.save_map("map.txt", $main_ui.$compass_dir); break; case KEY::O: autowalking = true; diff --git a/gui/map_view.cpp b/gui/map_view.cpp index 6377cff..06af275 100644 --- a/gui/map_view.cpp +++ b/gui/map_view.cpp @@ -10,6 +10,7 @@ #include #include #include +#include namespace gui { using namespace components; @@ -58,6 +59,19 @@ namespace gui { // $gui.debug_layout(window); } + void MapViewUI::save_map(const std::string& outfile, int compass_dir) { + std::wstring map_out = System::draw_map( + $level, $level.map->width(), $level.map->height(), compass_dir); + dbc::check(map_out.size() > 0, "WHAT? printed map has nothing in it."); + + std::wofstream out(outfile, std::ios::binary); + std::locale loc(std::locale::classic(), new std::codecvt_utf8); + out.imbue(loc); + + out << map_out; + dbc::check(out.good(), "failed to write map file"); + } + void MapViewUI::update() { if($gui.has($log_to)) { auto& text = $gui.get($log_to); diff --git a/gui/map_view.hpp b/gui/map_view.hpp index 05978f8..9a34819 100644 --- a/gui/map_view.hpp +++ b/gui/map_view.hpp @@ -20,5 +20,6 @@ namespace gui { void update_level(GameLevel &level); void log(std::wstring msg); void update(); + void save_map(const std::string& outfile, int compass_dir); }; } diff --git a/map.cpp b/map.cpp index a2a4b28..7d06c1e 100644 --- a/map.cpp +++ b/map.cpp @@ -45,12 +45,11 @@ void Map::clear_target(const Point &at) { } bool Map::place_entity(size_t room_index, Point &out) { + dbc::check($dead_ends.size() != 0, "no dead ends?!"); if($rooms.size() == 0) { - dbc::log("fucking dead end?"); out = $dead_ends.at(room_index % $dead_ends.size()); return true; } else { - dbc::log("fucking fuckng fuck fuck"); dbc::check(room_index < $rooms.size(), "room_index is out of bounds, not enough rooms"); Room &start = $rooms.at(room_index); diff --git a/maze.cpp b/maze.cpp index bfef304..62a40cb 100644 --- a/maze.cpp +++ b/maze.cpp @@ -7,216 +7,221 @@ using std::string; using matrix::Matrix; -inline size_t rand(size_t i, size_t j) { - if(i < j) { - return Random::uniform(i, j); - } else if(j < i) { - return Random::uniform(j, i); - } else { - return i; +namespace maze { + inline size_t rand(size_t i, size_t j) { + if(i < j) { + return Random::uniform(i, j); + } else if(j < i) { + return Random::uniform(j, i); + } else { + return i; + } } -} -inline bool complete(Matrix& maze) { - size_t width = matrix::width(maze); - size_t height = matrix::height(maze); + inline bool complete(Matrix& maze) { + size_t width = matrix::width(maze); + size_t height = matrix::height(maze); - for(size_t row = 1; row < height; row += 2) { - for(size_t col = 1; col < width; col += 2) { - if(maze[row][col] != 0) return false; + for(size_t row = 1; row < height; row += 2) { + for(size_t col = 1; col < width; col += 2) { + if(maze[row][col] != 0) return false; + } } - } - return true; -} + return true; + } -std::vector neighborsAB(Matrix& maze, Point on) { - std::vector result; + std::vector neighborsAB(Matrix& maze, Point on) { + std::vector result; - std::array points{{ - {on.x, on.y - 2}, - {on.x, on.y + 2}, - {on.x - 2, on.y}, - {on.x + 2, on.y} - }}; + std::array points{{ + {on.x, on.y - 2}, + {on.x, on.y + 2}, + {on.x - 2, on.y}, + {on.x + 2, on.y} + }}; - for(auto point : points) { - if(matrix::inbounds(maze, point.x, point.y)) { - result.push_back(point); + for(auto point : points) { + if(matrix::inbounds(maze, point.x, point.y)) { + result.push_back(point); + } } - } - return result; -} + return result; + } -std::vector neighbors(Matrix& maze, Point on) { - std::vector result; + std::vector neighbors(Matrix& maze, Point on) { + std::vector result; - std::array points{{ - {on.x, on.y - 2}, - {on.x, on.y + 2}, - {on.x - 2, on.y}, - {on.x + 2, on.y} - }}; + std::array points{{ + {on.x, on.y - 2}, + {on.x, on.y + 2}, + {on.x - 2, on.y}, + {on.x + 2, on.y} + }}; - for(auto point : points) { - if(matrix::inbounds(maze, point.x, point.y)) { - if(maze[point.y][point.x] == WALL_VALUE) { - result.push_back(point); + for(auto point : points) { + if(matrix::inbounds(maze, point.x, point.y)) { + if(maze[point.y][point.x] == WALL_VALUE) { + result.push_back(point); + } } } - } - return result; -} + return result; + } -inline std::pair find_coord(Matrix& maze) { - size_t width = matrix::width(maze); - size_t height = matrix::height(maze); + inline std::pair find_coord(Matrix& maze) { + size_t width = matrix::width(maze); + size_t height = matrix::height(maze); - for(size_t y = 1; y < height; y += 2) { - for(size_t x = 1; x < width; x += 2) { - if(maze[y][x] == WALL_VALUE) { - auto found = neighborsAB(maze, {x, y}); + for(size_t y = 1; y < height; y += 2) { + for(size_t x = 1; x < width; x += 2) { + if(maze[y][x] == WALL_VALUE) { + auto found = neighborsAB(maze, {x, y}); - for(auto point : found) { - if(maze[point.y][point.x] == 0) { - return {{x, y}, point}; + for(auto point : found) { + if(maze[point.y][point.x] == 0) { + return {{x, y}, point}; + } } } } } - } - matrix::dump("BAD MAZE", maze); - dbc::sentinel("failed to find coord?"); -} + matrix::dump("BAD MAZE", maze); + dbc::sentinel("failed to find coord?"); + } -void maze::randomize_rooms(std::vector& rooms_out, std::vector& maybe_here) { - dbc::check(maybe_here.size() >= 2, "must have at least two possible points to place rooms"); + void Builder::randomize_rooms() { + dbc::check($dead_ends.size() >= 2, "must have at least two possible points to place rooms"); - while(rooms_out.size() < 2) { - // use those dead ends to randomly place rooms - for(auto at : maybe_here) { - if(Random::uniform(0,1)) { - size_t offset = Random::uniform(0,1); - Room cur{at.x+offset, at.y+offset, 1, 1}; - rooms_out.push_back(cur); + while($rooms.size() < 2) { + // use those dead ends to randomly place rooms + for(auto at : $dead_ends) { + if(Random::uniform(0,1)) { + size_t offset = Random::uniform(0,1); + Room cur{at.x+offset, at.y+offset, 1, 1}; + $rooms.push_back(cur); + } } } } -} -void maze::init(Matrix& maze) { - matrix::assign(maze, WALL_VALUE); -} - -void maze::divide(Matrix& maze, Point start, Point end) { - for(matrix::line it{start, end}; it.next();) { - maze[it.y][it.x] = 0; - maze[it.y+1][it.x] = 0; + void Builder::init() { + matrix::assign($walls, WALL_VALUE); } -} -void maze::hunt_and_kill(Matrix& maze, std::vector& rooms, std::vector& dead_ends) { - - for(auto& room : rooms) { - for(matrix::box it{maze, room.x, room.y, room.width}; it.next();) { - maze[it.y][it.x] = 0; + void Builder::divide(Point start, Point end) { + for(matrix::line it{start, end}; it.next();) { + $walls[it.y][it.x] = 0; + $walls[it.y+1][it.x] = 0; } } - Point on{1,1}; - - while(!complete(maze)) { - auto n = neighbors(maze, on); - if(n.size() == 0) { - dead_ends.push_back(on); - auto t = find_coord(maze); - on = t.first; - maze[on.y][on.x] = 0; - size_t row = (on.y + t.second.y) / 2; - size_t col = (on.x + t.second.x) / 2; - maze[row][col] = 0; - } else { - auto nb = n[rand(size_t(0), n.size() - 1)]; - maze[nb.y][nb.x] = 0; + void Builder::hunt_and_kill() { + for(auto& room : $rooms) { + for(matrix::box it{$walls, room.x, room.y, room.width}; it.next();) { + $walls[it.y][it.x] = 0; + } + } - size_t row = (nb.y + on.y) / 2; - size_t col = (nb.x + on.x) / 2; - maze[row][col] = 0; - on = nb; + Point on{1,1}; + + while(!complete($walls)) { + auto n = neighbors($walls, on); + if(n.size() == 0) { + $dead_ends.push_back(on); + auto t = find_coord($walls); + on = t.first; + $walls[on.y][on.x] = 0; + size_t row = (on.y + t.second.y) / 2; + size_t col = (on.x + t.second.x) / 2; + $walls[row][col] = 0; + } else { + auto nb = n[rand(size_t(0), n.size() - 1)]; + $walls[nb.y][nb.x] = 0; + + size_t row = (nb.y + on.y) / 2; + size_t col = (nb.x + on.x) / 2; + $walls[row][col] = 0; + on = nb; + } } - } - for(auto at : dead_ends) { - for(auto& room : rooms) { - Point room_ul{room.x - room.width - 1, room.y - room.height - 1}; - Point room_lr{room.x + room.width - 1, room.y + room.height - 1}; - - if(at.x >= room_ul.x && at.y >= room_ul.y && - at.x <= room_lr.x && at.y <= room_lr.y) - { - for(matrix::compass it{maze, at.x, at.y}; it.next();) { - if(maze[it.y][it.x] == 1) { - maze[it.y][it.x] = 0; - break; + for(auto at : $dead_ends) { + for(auto& room : $rooms) { + Point room_ul{room.x - room.width - 1, room.y - room.height - 1}; + Point room_lr{room.x + room.width - 1, room.y + room.height - 1}; + + if(at.x >= room_ul.x && at.y >= room_ul.y && + at.x <= room_lr.x && at.y <= room_lr.y) + { + for(matrix::compass it{$walls, at.x, at.y}; it.next();) { + if($walls[it.y][it.x] == 1) { + $walls[it.y][it.x] = 0; + break; + } } } } } } -} -void maze::inner_donut(Matrix& maze, float outer_rad, float inner_rad) { - size_t x = matrix::width(maze) / 2; - size_t y = matrix::height(maze) / 2; + void Builder::inner_donut(float outer_rad, float inner_rad) { + size_t x = matrix::width($walls) / 2; + size_t y = matrix::height($walls) / 2; - for(matrix::circle it{maze, {x, y}, outer_rad}; - it.next();) - { - for(int x = it.left; x < it.right; x++) { - maze[it.y][x] = 0; + for(matrix::circle it{$walls, {x, y}, outer_rad}; + it.next();) + { + for(int x = it.left; x < it.right; x++) { + $walls[it.y][x] = 0; + } } - } - for(matrix::circle it{maze, {x, y}, inner_rad}; - it.next();) - { - for(int x = it.left; x < it.right; x++) { - maze[it.y][x] = 1; + for(matrix::circle it{$walls, {x, y}, inner_rad}; + it.next();) + { + for(int x = it.left; x < it.right; x++) { + $walls[it.y][x] = 1; + } } } -} -void maze::inner_box(Matrix& maze, size_t outer_size, size_t inner_size) { + void Builder::inner_box(size_t outer_size, size_t inner_size) { + size_t x = matrix::width($walls) / 2; + size_t y = matrix::height($walls) / 2; - size_t x = matrix::width(maze) / 2; - size_t y = matrix::height(maze) / 2; - - for(matrix::box it{maze, x, y, outer_size}; - it.next();) - { - maze[it.y][it.x] = 0; - } + for(matrix::box it{$walls, x, y, outer_size}; + it.next();) + { + $walls[it.y][it.x] = 0; + } - for(matrix::box it{maze, x, y, inner_size}; - it.next();) - { - maze[it.y][it.x] = 1; + for(matrix::box it{$walls, x, y, inner_size}; + it.next();) + { + $walls[it.y][it.x] = 1; + } } -} -void maze::remove_dead_ends(Matrix& maze, std::vector& dead_ends) { - for(auto at : dead_ends) { - for(matrix::compass it{maze, at.x, at.y}; it.next();) { - if(maze[it.y][it.x] == 0) { - int diff_x = at.x - it.x; - int diff_y = at.y - it.y; - maze[at.y + diff_y][at.x + diff_x] = 0; + void Builder::remove_dead_ends() { + dbc::check($dead_ends.size() > 0, "you have to run an algo first, no dead_ends to remove"); + for(auto at : $dead_ends) { + for(matrix::compass it{$walls, at.x, at.y}; it.next();) { + if($walls[it.y][it.x] == 0) { + int diff_x = at.x - it.x; + int diff_y = at.y - it.y; + $walls[at.y + diff_y][at.x + diff_x] = 0; + } } } } + + void Builder::dump(const std::string& msg) { + matrix::dump(msg, $walls); + } } diff --git a/maze.hpp b/maze.hpp index bd09193..48e98df 100644 --- a/maze.hpp +++ b/maze.hpp @@ -4,16 +4,26 @@ namespace maze { - void init(Matrix& maze); - void hunt_and_kill(Matrix& maze, std::vector& rooms, std::vector& dead_ends); + struct Builder { + Matrix& $walls; + std::vector& $rooms; + std::vector& $dead_ends; - void randomize_rooms(std::vector& rooms_out, std::vector& maybe_here); + Builder(Map& map) : + $walls(map.$walls), $rooms(map.$rooms), $dead_ends(map.$dead_ends) + { + init(); + } - void inner_donut(Matrix& maze, float outer_rad, float inner_rad); - void inner_box(Matrix& map, size_t outer_size, size_t inner_size); + void hunt_and_kill(); - void divide(Matrix& maze, Point start, Point end); - - void remove_dead_ends(Matrix& maze, std::vector& dead_ends); + void init(); + void randomize_rooms(); + void inner_donut(float outer_rad, float inner_rad); + void inner_box(size_t outer_size, size_t inner_size); + void divide(Point start, Point end); + void remove_dead_ends(); + void dump(const std::string& msg); + }; } diff --git a/tests/mazes.cpp b/tests/mazes.cpp index dd7f208..a2c846e 100644 --- a/tests/mazes.cpp +++ b/tests/mazes.cpp @@ -9,112 +9,98 @@ using std::string; using matrix::Matrix; - TEST_CASE("hunt-and-kill", "[mazes]") { - auto map = matrix::make(21, 21); - std::vector rooms; - std::vector dead_ends; + Map map(21, 21); + maze::Builder maze(map); + + maze.hunt_and_kill(); + maze.dump("BASIC MAZE"); - maze::init(map); - maze::hunt_and_kill(map, rooms, dead_ends); - matrix::dump("BASIC MAZE", map); + maze.randomize_rooms(); + maze.hunt_and_kill(); - maze::randomize_rooms(rooms, dead_ends); - maze::hunt_and_kill(map, rooms, dead_ends); + REQUIRE(map.$dead_ends.size() > 0); + REQUIRE(map.$rooms.size() > 0); - for(auto& room : rooms) { - for(matrix::box it{map, room.x, room.y, room.width}; + for(auto& room : maze.$rooms) { + for(matrix::box it{maze.$walls, room.x, room.y, room.width}; it.next();) { - map[it.y][it.x] = WALL_PATH_LIMIT; + maze.$walls[it.y][it.x] = WALL_PATH_LIMIT; } } - matrix::dump("MAZE WITH ROOMS", map); + maze.dump("MAZE WITH ROOMS"); } TEST_CASE("hunt-and-kill box", "[mazes]") { - auto map = matrix::make(21, 21); - std::vector rooms; - std::vector dead_ends; + Map map(21, 21); + maze::Builder maze(map); - maze::init(map); - maze::inner_box(map, 5, 3); - maze::hunt_and_kill(map, rooms, dead_ends); + maze.inner_box(5, 3); + maze.hunt_and_kill(); - for(auto at : dead_ends) { - map[at.y][at.x]=32; + for(auto at : maze.$dead_ends) { + maze.$walls[at.y][at.x]=32; } - matrix::dump("INNER BOX", map); + maze.dump("INNER BOX"); - REQUIRE(rooms.size() == 0); + REQUIRE(maze.$rooms.size() == 0); } TEST_CASE("hunt-and-kill ring", "[mazes]") { - auto map = matrix::make(21, 21); - std::vector rooms; - std::vector dead_ends; + Map map(21, 21); + maze::Builder maze(map); - maze::init(map); - maze::inner_donut(map, 5.5, 3.5); - maze::hunt_and_kill(map, rooms, dead_ends); + maze.inner_donut(5.5, 3.5); + maze.hunt_and_kill(); - for(auto at : dead_ends) { - map[at.y][at.x]=32; + for(auto at : maze.$dead_ends) { + maze.$walls[at.y][at.x]=32; } - matrix::dump("INNER RING", map); + maze.dump("INNER RING"); - REQUIRE(rooms.size() == 0); + REQUIRE(maze.$rooms.size() == 0); } TEST_CASE("hunt-and-kill fissure", "[mazes]") { - auto map = matrix::make(21, 21); - std::vector rooms; - std::vector dead_ends; + Map map(21, 21); + maze::Builder maze(map); - maze::init(map); - maze::divide(map, {3,3}, {19,18}); - maze::hunt_and_kill(map, rooms, dead_ends); + maze.divide({3,3}, {19,18}); + maze.hunt_and_kill(); - for(auto at : dead_ends) { - map[at.y][at.x]=32; + for(auto at : maze.$dead_ends) { + maze.$walls[at.y][at.x]=32; } - matrix::dump("FISSURE MAZE", map); + maze.dump("FISSURE MAZE"); - REQUIRE(rooms.size() == 0); + REQUIRE(maze.$rooms.size() == 0); } TEST_CASE("hunt-and-kill no-dead-ends", "[mazes]") { - auto map = matrix::make(21, 21); - std::vector rooms; - std::vector dead_ends; - - maze::init(map); + Map map(21, 21); + maze::Builder maze(map); - maze::hunt_and_kill(map, rooms, dead_ends); + maze.hunt_and_kill(); - maze::remove_dead_ends(map, dead_ends); + maze.remove_dead_ends(); - matrix::dump("NO DEAD ENDS", map); + maze.dump("NO DEAD ENDS"); } TEST_CASE("hunt-and-kill too much", "[mazes]") { - auto map = matrix::make(21, 21); - std::vector rooms; - std::vector dead_ends; - - maze::init(map); - maze::inner_donut(map, 4, 2); - maze::divide(map, {3,3}, {19,18}); - auto copy = map; - - maze::hunt_and_kill(copy, rooms, dead_ends); + Map map(21, 21); + maze::Builder maze(map); - map = copy; - maze::randomize_rooms(rooms, dead_ends); - maze::hunt_and_kill(map, rooms, dead_ends); + maze.hunt_and_kill(); + maze.randomize_rooms(); + maze.init(); + maze.inner_donut(4, 2); + maze.divide({3,3}, {19,18}); + maze.hunt_and_kill(); - matrix::dump("COMBINED", map); + maze.dump("COMBINED"); } diff --git a/worldbuilder.cpp b/worldbuilder.cpp index b6a298b..94da449 100644 --- a/worldbuilder.cpp +++ b/worldbuilder.cpp @@ -11,17 +11,20 @@ using namespace fmt; using namespace components; void WorldBuilder::generate_map() { - maze::init($map.$walls); - maze::hunt_and_kill($map.$walls, $map.$rooms, $map.$dead_ends); - maze::randomize_rooms($map.$rooms, $map.$dead_ends); - maze::hunt_and_kill($map.$walls, $map.$rooms, $map.$dead_ends); + maze::Builder maze($map); + size_t x_diff = $map.width() / 4; + size_t y_diff = $map.height() / 4; + + maze.divide({x_diff, y_diff}, {$map.width() - x_diff, $map.height() - y_diff}); + maze.hunt_and_kill(); + + dbc::check($map.$dead_ends.size() > 0, "world builder/maze builder made a map with no dead ends."); $map.expand(); $map.load_tiles(); } DinkyECS::Entity WorldBuilder::configure_entity_in_map(DinkyECS::World &world, json &entity_data, Point pos_out) { - dbc::log(">>>>>>>>>>> ENTER"); auto item = world.entity(); world.set(item, {pos_out.x+1, pos_out.y+1}); @@ -33,17 +36,14 @@ DinkyECS::Entity WorldBuilder::configure_entity_in_map(DinkyECS::World &world, j components::configure_entity($components, world, item, entity_data["components"]); } - dbc::log("<<<<<<<<<<<<< EXIT"); return item; } DinkyECS::Entity WorldBuilder::configure_entity_in_room(DinkyECS::World &world, json &entity_data, int in_room) { Point pos_out; - dbc::log("is it configure_entity_in_map's fault?"); bool placed = $map.place_entity(in_room, pos_out); dbc::check(placed, "failed to randomly place item in room"); auto entity = configure_entity_in_map(world, entity_data, pos_out); - dbc::log("<<<<<<<<<<<<<<<<<<<<<<<<<<< leaving configure_entity_in_room"); return entity; } @@ -118,7 +118,6 @@ void WorldBuilder::place_entities(DinkyECS::World &world) { if(world.has_the()) { auto& player = world.get_the(); Point pos_out; - dbc::log("or is it in place_entities placing the player?"); bool placed = $map.place_entity(0, pos_out); dbc::check(placed, "failed to randomly place item in room"); world.set(player.entity, {pos_out.x+1, pos_out.y+1});