Hunt-and-kill algorithm rocks. It handles everything I need for map gen, including spawn points, room placement, and the maze like map.
This commit is contained in:
parent
0f8e61797f
commit
a0b785cb2a
8 changed files with 62 additions and 336 deletions
179
worldbuilder.cpp
179
worldbuilder.cpp
|
@ -18,11 +18,6 @@ inline int make_split(Room &cur, bool horiz) {
|
|||
return Random::uniform<int>(min, max);
|
||||
}
|
||||
|
||||
void WorldBuilder::set_door(Room &room, int value) {
|
||||
$map.$walls[room.entry.y][room.entry.x] = value;
|
||||
$map.$walls[room.exit.y][room.exit.x] = value;
|
||||
}
|
||||
|
||||
void rand_side(Room &room, Point &door) {
|
||||
dbc::check(int(room.width) > 0 && int(room.height) > 0, "Weird room with 0 for height or width.");
|
||||
int rand_x = Random::uniform<int>(0, room.width - 1);
|
||||
|
@ -55,101 +50,22 @@ void WorldBuilder::add_door(Room &room) {
|
|||
rand_side(room, room.exit);
|
||||
}
|
||||
|
||||
void WorldBuilder::partition_map(Room &cur, int depth) {
|
||||
if(cur.width >= 3 && cur.width <= 6 &&
|
||||
cur.height >= 3 && cur.height <= 6)
|
||||
{
|
||||
$map.add_room(cur);
|
||||
return;
|
||||
}
|
||||
|
||||
bool horiz = cur.width > cur.height ? false : true;
|
||||
int split = make_split(cur, horiz);
|
||||
if(split <= 0) return; // end recursion
|
||||
|
||||
Room left = cur;
|
||||
Room right = cur;
|
||||
|
||||
if(horiz) {
|
||||
if(split >= int(cur.height)) return; // end recursion
|
||||
|
||||
left.height = size_t(split - 1);
|
||||
right.y = cur.y + split;
|
||||
right.height = size_t(cur.height - split);
|
||||
} else {
|
||||
if(split >= int(cur.width)) return; // end recursion
|
||||
|
||||
left.width = size_t(split-1);
|
||||
right.x = cur.x + split,
|
||||
right.width = size_t(cur.width - split);
|
||||
}
|
||||
|
||||
// BUG: min room size should be configurable
|
||||
if(depth > 0 && left.width > 2 && left.height > 2) {
|
||||
partition_map(left, depth-1);
|
||||
}
|
||||
|
||||
// BUG: min room size should be configurable
|
||||
if(depth > 0 && right.width > 2 && right.height > 2) {
|
||||
partition_map(right, depth-1);
|
||||
}
|
||||
}
|
||||
|
||||
void WorldBuilder::update_door(Point &at, int wall_or_space) {
|
||||
$map.$walls[at.y][at.x] = wall_or_space;
|
||||
}
|
||||
|
||||
|
||||
void WorldBuilder::stylize_room(int room, string tile_name, float size) {
|
||||
Point pos_out;
|
||||
bool placed = $map.place_entity(room, pos_out);
|
||||
dbc::check(placed, "failed to place style in room");
|
||||
(void)tile_name;
|
||||
(void)size;
|
||||
|
||||
//tile_name = tile_name == "FLOOR_TILE" ? "WALL_PLAIN" : tile_name;
|
||||
|
||||
//for(matrix::circle it{$map.$walls, pos_out, size}; it.next();) {
|
||||
// for(int x = it.left; x < it.right; x++) {
|
||||
// if($map.iswall(x, it.y)) {
|
||||
// // a wall tile
|
||||
// $map.$tiles.set_tile(x, it.y, tile_name);
|
||||
// } else {
|
||||
// // a floor tile
|
||||
// $map.$tiles.set_tile(x, it.y, "FLOOR_TILE");
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
||||
void WorldBuilder::generate_rooms() {
|
||||
Room root{
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = $map.$width,
|
||||
.height = $map.$height
|
||||
};
|
||||
// BUG: depth should be configurable
|
||||
partition_map(root, 10);
|
||||
place_rooms();
|
||||
|
||||
dbc::check($map.room_count() > 0, "map generated zero rooms, map too small?");
|
||||
}
|
||||
|
||||
void WorldBuilder::generate_map() {
|
||||
maze::hunt_and_kill($map.$walls, $map.$rooms);
|
||||
std::vector<Point> dead_ends;
|
||||
maze::hunt_and_kill($map.$walls, $map.$rooms, dead_ends);
|
||||
|
||||
for(auto at : dead_ends) {
|
||||
if(Random::uniform(0,1)) {
|
||||
Room cur{at.x, at.y, 2, 2};
|
||||
add_door(cur);
|
||||
$map.add_room(cur);
|
||||
}
|
||||
}
|
||||
|
||||
maze::hunt_and_kill($map.$walls, $map.$rooms, dead_ends);
|
||||
|
||||
$map.expand();
|
||||
$map.load_tiles();
|
||||
|
||||
// get only the tiles with no collision to fill rooms
|
||||
auto room_types = $map.$tiles.tile_names(false);
|
||||
|
||||
for(size_t i = 0; i < $map.$rooms.size() - 1; i++) {
|
||||
size_t room_type = Random::uniform<size_t>(0, room_types.size() - 1);
|
||||
int room_size = Random::uniform<int>(100, 800);
|
||||
string tile_name = room_types[room_type];
|
||||
stylize_room(i, tile_name, room_size * 0.01f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -260,72 +176,3 @@ void WorldBuilder::generate(DinkyECS::World &world) {
|
|||
generate_map();
|
||||
place_entities(world);
|
||||
}
|
||||
|
||||
void WorldBuilder::make_room(size_t origin_x, size_t origin_y, size_t w, size_t h) {
|
||||
$map.INVARIANT();
|
||||
dbc::pre("y out of bounds", origin_y + h < $map.$height);
|
||||
dbc::pre("x out of bounds", origin_x + w < $map.$width);
|
||||
|
||||
for(size_t y = origin_y; y < origin_y + h; ++y) {
|
||||
for(size_t x = origin_x; x < origin_x + w; ++x) {
|
||||
$map.$walls[y][x] = INV_SPACE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void WorldBuilder::place_rooms() {
|
||||
for(auto &cur : $map.$rooms) {
|
||||
add_door(cur);
|
||||
make_room(cur.x, cur.y, cur.width, cur.height);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool random_path(Map &map, PointList &holes, Point src, Point target) {
|
||||
bool keep_going = false;
|
||||
bool target_found = false;
|
||||
int count = 0;
|
||||
map.set_target(target);
|
||||
map.make_paths();
|
||||
Matrix &paths = map.paths();
|
||||
|
||||
Point out{src.x, src.y};
|
||||
do {
|
||||
keep_going = map.neighbors(out, true);
|
||||
holes.push_back(out);
|
||||
target_found = paths[out.y][out.x] == 0;
|
||||
} while(!target_found && keep_going && ++count < WORLDBUILD_MAX_PATH);
|
||||
|
||||
map.INVARIANT();
|
||||
map.clear_target(target);
|
||||
|
||||
return target_found;
|
||||
}
|
||||
|
||||
inline void straight_path(Map &map, PointList &holes, Point src, Point target) {
|
||||
for(matrix::line dig{src, target}; dig.next();) {
|
||||
holes.emplace_back(size_t(dig.x), size_t(dig.y));
|
||||
Point expand{(size_t)dig.x+1, (size_t)dig.y};
|
||||
|
||||
if(map.inmap(expand.x, expand.y)) {
|
||||
// BUG? should really just move doors away from the edge
|
||||
holes.push_back(expand);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WorldBuilder::tunnel_doors(PointList &holes, Room &src, Room &target) {
|
||||
int path_type = Random::uniform<int>(0, 10);
|
||||
|
||||
switch(path_type) {
|
||||
case 0:
|
||||
// then do the rest as random with fallback
|
||||
if(!random_path($map, holes, src.exit, target.entry)) {
|
||||
straight_path($map, holes, src.exit, target.entry);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// for now do 25% as simple straight paths
|
||||
straight_path($map, holes, src.exit, target.entry);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue