|
|
|
@ -10,70 +10,28 @@ |
|
|
|
|
using namespace fmt; |
|
|
|
|
using namespace components; |
|
|
|
|
|
|
|
|
|
inline int make_split(Room &cur, bool horiz) { |
|
|
|
|
size_t dimension = horiz ? cur.height : cur.width; |
|
|
|
|
int min = dimension / WORLDBUILD_DIVISION; |
|
|
|
|
int max = dimension - min; |
|
|
|
|
|
|
|
|
|
return Random::uniform<int>(min, max); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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); |
|
|
|
|
int rand_y = Random::uniform<int>(0, room.height - 1); |
|
|
|
|
|
|
|
|
|
switch(Random::uniform<int>(0,3)) { |
|
|
|
|
case 0: // north
|
|
|
|
|
door.x = room.x + rand_x; |
|
|
|
|
door.y = room.y-1; |
|
|
|
|
break; |
|
|
|
|
case 1: // south
|
|
|
|
|
door.x = room.x + rand_x; |
|
|
|
|
door.y = room.y + room.height; |
|
|
|
|
break; |
|
|
|
|
case 2: // east
|
|
|
|
|
door.x = room.x + room.width; |
|
|
|
|
door.y = room.y + rand_y; |
|
|
|
|
break; |
|
|
|
|
case 3: // west
|
|
|
|
|
door.x = room.x - 1; |
|
|
|
|
door.y = room.y + rand_y; |
|
|
|
|
break; |
|
|
|
|
default: |
|
|
|
|
dbc::sentinel("impossible side"); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void WorldBuilder::add_door(Room &room) { |
|
|
|
|
rand_side(room, room.entry); |
|
|
|
|
rand_side(room, room.exit); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void WorldBuilder::generate_map() { |
|
|
|
|
std::vector<Point> dead_ends; |
|
|
|
|
maze::hunt_and_kill($map.$walls, $map.$rooms, dead_ends); |
|
|
|
|
// run it once to find dead ends
|
|
|
|
|
maze::hunt_and_kill($map.$walls, $map.$rooms, $map.$dead_ends); |
|
|
|
|
|
|
|
|
|
for(auto at : dead_ends) { |
|
|
|
|
// use those dead ends to randomly place rooms
|
|
|
|
|
for(auto at : $map.$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); |
|
|
|
|
// run it again to create the final map with rooms
|
|
|
|
|
// NOTE: hund_and_kill is responsible for clearing the map correctly
|
|
|
|
|
maze::hunt_and_kill($map.$walls, $map.$rooms, $map.$dead_ends); |
|
|
|
|
|
|
|
|
|
$map.expand(); |
|
|
|
|
$map.load_tiles(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DinkyECS::Entity WorldBuilder::configure_entity_in_map(DinkyECS::World &world, json &entity_data, int in_room) { |
|
|
|
|
DinkyECS::Entity WorldBuilder::configure_entity_in_map(DinkyECS::World &world, json &entity_data, Point pos_out) { |
|
|
|
|
auto item = world.entity(); |
|
|
|
|
Point pos_out; |
|
|
|
|
bool placed = $map.place_entity(in_room, pos_out); |
|
|
|
|
dbc::check(placed, "failed to randomly place item in room"); |
|
|
|
|
world.set<Position>(item, {pos_out.x+1, pos_out.y+1}); |
|
|
|
|
|
|
|
|
|
if(entity_data["inventory_count"] > 0) { |
|
|
|
@ -86,6 +44,14 @@ DinkyECS::Entity WorldBuilder::configure_entity_in_map(DinkyECS::World &world, j |
|
|
|
|
return item; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
DinkyECS::Entity WorldBuilder::configure_entity_in_map(DinkyECS::World &world, json &entity_data, int in_room) { |
|
|
|
|
Point pos_out; |
|
|
|
|
bool placed = $map.place_entity(in_room, pos_out); |
|
|
|
|
dbc::check(placed, "failed to randomly place item in room"); |
|
|
|
|
return configure_entity_in_map(world, entity_data, pos_out); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
inline json &select_entity_type(GameConfig &config, json &gen_config) { |
|
|
|
|
int enemy_test = Random::uniform<int>(0,100); |
|
|
|
|
int device_test = Random::uniform<int>(0, 100); |
|
|
|
@ -99,32 +65,37 @@ inline json &select_entity_type(GameConfig &config, json &gen_config) { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void WorldBuilder::randomize_entities(DinkyECS::World &world, GameConfig &config) { |
|
|
|
|
auto &gen_config = config.game["worldgen"]; |
|
|
|
|
|
|
|
|
|
for(size_t room_num = $map.room_count() - 1; room_num > 0; room_num--) { |
|
|
|
|
int empty_room = Random::uniform<int>(0, 100); |
|
|
|
|
if(empty_room < gen_config["empty_room_probability"]) continue; |
|
|
|
|
|
|
|
|
|
json& entity_db = select_entity_type(config, gen_config); |
|
|
|
|
inline json& random_entity_data(GameConfig& config, json& gen_config) { |
|
|
|
|
json& entity_db = select_entity_type(config, gen_config); |
|
|
|
|
|
|
|
|
|
std::vector<std::string> keys; |
|
|
|
|
for(auto& el : entity_db.items()) { |
|
|
|
|
auto& data = el.value(); |
|
|
|
|
std::vector<std::string> keys; |
|
|
|
|
for(auto& el : entity_db.items()) { |
|
|
|
|
auto& data = el.value(); |
|
|
|
|
|
|
|
|
|
if(data["placement"] == nullptr) { |
|
|
|
|
keys.push_back(el.key()); |
|
|
|
|
} |
|
|
|
|
if(data["placement"] == nullptr) { |
|
|
|
|
keys.push_back(el.key()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int rand_entity = Random::uniform<int>(0, keys.size() - 1); |
|
|
|
|
std::string key = keys[rand_entity]; |
|
|
|
|
// BUG: this may crash if PLAYER_TILE isn't first
|
|
|
|
|
return entity_db[key]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int rand_entity = Random::uniform<int>(0, keys.size() - 1); |
|
|
|
|
std::string key = keys[rand_entity]; |
|
|
|
|
// BUG: this may crash if PLAYER_TILE isn't first
|
|
|
|
|
auto entity_data = entity_db[key]; |
|
|
|
|
void WorldBuilder::randomize_entities(DinkyECS::World &world, GameConfig &config) { |
|
|
|
|
auto& gen_config = config.game["worldgen"]; |
|
|
|
|
|
|
|
|
|
for(size_t room_num = $map.room_count() - 1; room_num > 0; room_num--) { |
|
|
|
|
// pass that to the config as it'll be a generic json
|
|
|
|
|
auto& entity_data = random_entity_data(config, gen_config); |
|
|
|
|
configure_entity_in_map(world, entity_data, room_num); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for(auto& at : $map.$dead_ends) { |
|
|
|
|
auto& entity_data = random_entity_data(config, gen_config); |
|
|
|
|
configure_entity_in_map(world, entity_data, at); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void WorldBuilder::place_stairs(DinkyECS::World& world, GameConfig& config) { |
|
|
|
|