#include "worldbuilder.hpp" #include "rand.hpp" #include #include #include "components.hpp" #include "inventory.hpp" #include "rituals.hpp" #include "maze.hpp" using namespace fmt; using namespace components; void WorldBuilder::generate_map() { maze::Builder maze($map); maze.hunt_and_kill(); maze.randomize_rooms(); if($map.width() > 20) { maze.inner_box(4, 2); } maze.hunt_and_kill(); $map.expand(); $map.load_tiles(); } bool WorldBuilder::find_open_spot(Point& pos_out) { // NOTE: still spawning near a player but not sure if this is the place // to solve that. Idea: Get the player, don't place anything too close. for(matrix::rando_rect it{$map.walls(), pos_out.x, pos_out.y, 3}; it.next();) { Point test{size_t(it.x), size_t(it.y)}; if($map.can_move(test) && !$collision.occupied(test)) { pos_out = test; return true; } } return false; } DinkyECS::Entity WorldBuilder::configure_entity_in_map(DinkyECS::World &world, json &entity_data, Point pos) { bool found = find_open_spot(pos); dbc::check(found, "Failed to find a place for this thing."); auto item = world.entity(); world.set(item, {pos.x, pos.y}); if(entity_data["inventory_count"] > 0) { world.set(item, {entity_data["inventory_count"], entity_data}); } if(entity_data.contains("components")) { components::configure_entity($components, world, item, entity_data["components"]); } $collision.insert(pos, item); return item; } DinkyECS::Entity WorldBuilder::configure_entity_in_room(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"); auto entity = configure_entity_in_map(world, entity_data, pos_out); return entity; } inline json &select_entity_type(GameConfig &config, json &gen_config) { int enemy_test = Random::uniform(0,100); int device_test = Random::uniform(0, 100); if(enemy_test < gen_config["enemy_probability"]) { return config.enemies.json(); } else if(device_test < gen_config["device_probability"]) { return config.devices.json(); } else { return config.items.json(); } } inline json& random_entity_data(GameConfig& config, json& gen_config) { json& entity_db = select_entity_type(config, gen_config); std::vector keys; for(auto& el : entity_db.items()) { auto& data = el.value(); if(data["placement"] == nullptr) { keys.push_back(el.key()); } } int rand_entity = Random::uniform(0, keys.size() - 1); std::string key = keys[rand_entity]; // BUG: this may crash if PLAYER_TILE isn't first return entity_db[key]; } void WorldBuilder::randomize_entities(DinkyECS::World &world, GameConfig &config) { auto& gen_config = config.game["worldgen"]; for(int 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_room(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) { auto& device_config = config.devices.json(); auto entity_data = device_config["STAIRS_DOWN"]; auto at_end = $map.$dead_ends.back(); configure_entity_in_map(world, entity_data, at_end); } void WorldBuilder::configure_starting_items(DinkyECS::World &world) { auto& blanket = world.get_the(); Config config("assets/rituals.json"); for(auto& el : config["starting_junk"]) { ritual::JunkItem name = el; blanket.add(name); }; } void WorldBuilder::place_entities(DinkyECS::World &world) { auto &config = world.get_the(); // configure a player as a fact of the world if(world.has_the()) { auto& player = world.get_the(); Point pos_out; 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}); } else { auto player_data = config.enemies["PLAYER_TILE"]; auto player_ent = configure_entity_in_room(world, player_data, 0); // configure player in the world Player player{player_ent}; world.set_the(player); world.set_the({}); world.set_the({}); configure_starting_items(world); world.set(player.entity, {5}); world.make_constant(player.entity); } randomize_entities(world, config); place_stairs(world, config); } void WorldBuilder::generate(DinkyECS::World &world) { generate_map(); place_entities(world); }