Exploring raycasters and possibly make a little "doom like" game based on it.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
raycaster/worldbuilder.cpp

178 lines
5.1 KiB

#include "worldbuilder.hpp"
#include "rand.hpp"
#include <fmt/core.h>
#include <iostream>
#include "components.hpp"
#include "inventory.hpp"
#include "rituals.hpp"
#include "maze.hpp"
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);
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();
}
DinkyECS::Entity WorldBuilder::configure_entity_in_map(DinkyECS::World &world, json &entity_data, int in_room) {
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) {
world.set<InventoryItem>(item, {entity_data["inventory_count"], entity_data});
}
if(entity_data.contains("components")) {
components::configure_entity($components, world, item, entity_data["components"]);
}
return item;
}
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);
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();
}
}
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);
std::vector<std::string> 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<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];
// pass that to the config as it'll be a generic json
configure_entity_in_map(world, entity_data, room_num);
}
}
void WorldBuilder::place_stairs(DinkyECS::World& world, GameConfig& config) {
auto& device_config = config.devices.json();
auto entity_data = device_config["STAIRS_DOWN"];
int last_room = $map.room_count() - 1;
configure_entity_in_map(world, entity_data, last_room);
}
void WorldBuilder::configure_starting_items(DinkyECS::World &world) {
auto& blanket = world.get_the<ritual::Blanket>();
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<GameConfig>();
// configure a player as a fact of the world
if(world.has_the<Player>()) {
auto& player = world.get_the<Player>();
Point pos_out;
bool placed = $map.place_entity(0, pos_out);
dbc::check(placed, "failed to randomly place item in room");
world.set<Position>(player.entity, {pos_out.x+1, pos_out.y+1});
} else {
auto player_data = config.enemies["PLAYER_TILE"];
auto player_ent = configure_entity_in_map(world, player_data, 0);
// configure player in the world
Player player{player_ent};
world.set_the<Player>(player);
world.set_the<ritual::Belt>({});
world.set_the<ritual::Blanket>({});
configure_starting_items(world);
world.set<Inventory>(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);
}