Fixed up the map generator so that it's placing entities in non-overlapping tiles and adapting the style for the size. It can also deal with maps that have no rooms better and places the stairs better.

master
Zed A. Shaw 10 months ago
parent 5f1a453fb4
commit 4eaf3c35d6
  1. 15
      assets/items.json
  2. 4
      camera.cpp
  3. 2
      camera.hpp
  4. 2
      constants.hpp
  5. 6
      gui/fsm.cpp
  6. 7
      gui/main_ui.cpp
  7. 2
      gui/main_ui.hpp
  8. 4
      levelmanager.cpp
  9. 16
      maze.cpp
  10. 39
      worldbuilder.cpp
  11. 3
      worldbuilder.hpp

@ -14,21 +14,6 @@
{"_type": "Sound", "attack": "pickup", "death": "blank"} {"_type": "Sound", "attack": "pickup", "death": "blank"}
] ]
}, },
"SWORD_RUSTY": {
"id": "SWORD_RUSTY",
"name": "Rusty Junk Sword",
"description": "A sword left to rot in a deep hole where it acquired a patina of dirt and tetanus. You aren't sure if it's more deadly for you to hold it or for the people you stab with it.",
"inventory_count": 1,
"components": [
{"_type": "Weapon", "damage": 15},
{"_type": "Tile", "display": 7735,
"foreground": [24, 120, 189],
"background": [24, 120, 189]
},
{"_type": "Sprite", "name": "cinqueda", "width": 256, "height": 256, "scale": 1.0},
{"_type": "Sound", "attack": "pickup", "death": "blank"}
]
},
"BARREL_SMALL": { "BARREL_SMALL": {
"id": "BARREL_SMALL", "id": "BARREL_SMALL",
"name": "Small Barrel", "name": "Small Barrel",

@ -15,9 +15,9 @@ Point CameraLOL::plan_move(int dir, bool strafe) {
return {size_t(target_x), size_t(target_y)}; return {size_t(target_x), size_t(target_y)};
} }
void CameraLOL::plan_rotate(int dir) { void CameraLOL::plan_rotate(int dir, float amount) {
t = 0.0; t = 0.0;
double angle_dir = std::numbers::pi * 0.25 * dir; double angle_dir = std::numbers::pi * amount * float(dir);
target_dir_x = rayview.$dir_x * cos(angle_dir) - rayview.$dir_y * sin(angle_dir); target_dir_x = rayview.$dir_x * cos(angle_dir) - rayview.$dir_y * sin(angle_dir);
target_dir_y = rayview.$dir_x * sin(angle_dir) + rayview.$dir_y * cos(angle_dir); target_dir_y = rayview.$dir_x * sin(angle_dir) + rayview.$dir_y * cos(angle_dir);

@ -17,7 +17,7 @@ struct CameraLOL {
rayview(rv) {} rayview(rv) {}
Point plan_move(int dir, bool strafe); Point plan_move(int dir, bool strafe);
void plan_rotate(int dir); void plan_rotate(int dir, float amount=0.5f);
bool play_rotate(); bool play_rotate();
bool play_move(); bool play_move();

@ -60,6 +60,8 @@ constexpr int COMBAT_UI_Y = RAY_VIEW_HEIGHT;
constexpr int COMBAT_UI_WIDTH = RAY_VIEW_WIDTH ; constexpr int COMBAT_UI_WIDTH = RAY_VIEW_WIDTH ;
constexpr int COMBAT_UI_HEIGHT = SCREEN_HEIGHT - RAY_VIEW_HEIGHT; constexpr int COMBAT_UI_HEIGHT = SCREEN_HEIGHT - RAY_VIEW_HEIGHT;
constexpr int INITIAL_MAP_W = 17;
constexpr int INITIAL_MAP_H = 15;
// for the panels/renderer // for the panels/renderer
constexpr wchar_t BG_TILE = L''; constexpr wchar_t BG_TILE = L'';

@ -152,11 +152,11 @@ namespace gui {
try_move(1, true); try_move(1, true);
break; break;
case ROTATE_LEFT: case ROTATE_LEFT:
$main_ui.plan_rotate(-1); $main_ui.plan_rotate(-1, 0.5f);
state(State::ROTATING); state(State::ROTATING);
break; break;
case ROTATE_RIGHT: case ROTATE_RIGHT:
$main_ui.plan_rotate(1); $main_ui.plan_rotate(1, 0.5f);
state(State::ROTATING); state(State::ROTATING);
break; break;
case MAP_OPEN: case MAP_OPEN:
@ -356,7 +356,7 @@ namespace gui {
if($map_open) { if($map_open) {
$map_ui.render($window, $main_ui.$compass_dir); $map_ui.render($window, $main_ui.$compass_dir);
} else { } else {
// $mini_map.render($window, $main_ui.$compass_dir); $mini_map.render($window, $main_ui.$compass_dir);
} }
} }
} }

@ -77,10 +77,11 @@ namespace gui {
} }
} }
void MainUI::plan_rotate(int dir) { void MainUI::plan_rotate(int dir, float amount) {
// -1 is left, 1 is right // -1 is left, 1 is right
$compass_dir = ($compass_dir + dir) % COMPASS.size(); int extra = (amount == 0.5) * dir;
$camera.plan_rotate(dir); $compass_dir = ($compass_dir + dir + extra) % COMPASS.size();
$camera.plan_rotate(dir, amount);
} }
Point MainUI::plan_move(int dir, bool strafe) { Point MainUI::plan_move(int dir, bool strafe) {

@ -29,7 +29,7 @@ namespace gui {
void debug(); void debug();
void render_debug(); void render_debug();
void plan_rotate(int dir); void plan_rotate(int dir, float amount=0.5f);
bool play_rotate(); bool play_rotate();
std::optional<Point> play_move(); std::optional<Point> play_move();
Point plan_move(int dir, bool strafe); Point plan_move(int dir, bool strafe);

@ -17,8 +17,8 @@ LevelManager::LevelManager() {
LevelScaling LevelManager::scale_level() { LevelScaling LevelManager::scale_level() {
return { return {
21, INITIAL_MAP_W + int($current_level * 2),
21 INITIAL_MAP_H + int($current_level * 2)
}; };
} }

@ -95,16 +95,12 @@ namespace maze {
} }
void Builder::randomize_rooms() { void Builder::randomize_rooms() {
dbc::check($dead_ends.size() >= 2, "must have at least two possible points to place rooms"); // use those dead ends to randomly place rooms
for(auto at : $dead_ends) {
while($rooms.size() < 2) { if(Random::uniform(0,1)) {
// use those dead ends to randomly place rooms size_t offset = Random::uniform(0,1);
for(auto at : $dead_ends) { Room cur{at.x+offset, at.y+offset, 1, 1};
if(Random::uniform(0,1)) { $rooms.push_back(cur);
size_t offset = Random::uniform(0,1);
Room cur{at.x+offset, at.y+offset, 1, 1};
$rooms.push_back(cur);
}
} }
} }
} }

@ -12,21 +12,41 @@ using namespace components;
void WorldBuilder::generate_map() { void WorldBuilder::generate_map() {
maze::Builder maze($map); 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(); maze.hunt_and_kill();
maze.randomize_rooms();
dbc::check($map.$dead_ends.size() > 0, "world builder/maze builder made a map with no dead ends."); if($map.width() > 20) {
maze.inner_box(4, 2);
}
maze.hunt_and_kill();
$map.expand(); $map.expand();
$map.load_tiles(); $map.load_tiles();
} }
DinkyECS::Entity WorldBuilder::configure_entity_in_map(DinkyECS::World &world, json &entity_data, Point pos_out) { 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(); auto item = world.entity();
world.set<Position>(item, {pos_out.x+1, pos_out.y+1}); world.set<Position>(item, {pos.x, pos.y});
if(entity_data["inventory_count"] > 0) { if(entity_data["inventory_count"] > 0) {
world.set<InventoryItem>(item, {entity_data["inventory_count"], entity_data}); world.set<InventoryItem>(item, {entity_data["inventory_count"], entity_data});
@ -36,6 +56,8 @@ DinkyECS::Entity WorldBuilder::configure_entity_in_map(DinkyECS::World &world, j
components::configure_entity($components, world, item, entity_data["components"]); components::configure_entity($components, world, item, entity_data["components"]);
} }
$collision.insert(pos, item);
return item; return item;
} }
@ -97,8 +119,9 @@ void WorldBuilder::randomize_entities(DinkyECS::World &world, GameConfig &config
void WorldBuilder::place_stairs(DinkyECS::World& world, GameConfig& config) { void WorldBuilder::place_stairs(DinkyECS::World& world, GameConfig& config) {
auto& device_config = config.devices.json(); auto& device_config = config.devices.json();
auto entity_data = device_config["STAIRS_DOWN"]; auto entity_data = device_config["STAIRS_DOWN"];
int last_room = $map.room_count() - 1;
configure_entity_in_room(world, entity_data, last_room); auto at_end = $map.$dead_ends.back();
configure_entity_in_map(world, entity_data, at_end);
} }
void WorldBuilder::configure_starting_items(DinkyECS::World &world) { void WorldBuilder::configure_starting_items(DinkyECS::World &world) {

@ -3,11 +3,13 @@
#include "map.hpp" #include "map.hpp"
#include "dinkyecs.hpp" #include "dinkyecs.hpp"
#include "components.hpp" #include "components.hpp"
#include "spatialmap.hpp"
class WorldBuilder { class WorldBuilder {
public: public:
Map& $map; Map& $map;
components::ComponentMap& $components; components::ComponentMap& $components;
SpatialMap $collision;
WorldBuilder(Map &map, components::ComponentMap& components) : WorldBuilder(Map &map, components::ComponentMap& components) :
$map(map), $map(map),
@ -20,6 +22,7 @@ class WorldBuilder {
DinkyECS::Entity configure_entity_in_room(DinkyECS::World &world, nlohmann::json &entity_data, int in_room); DinkyECS::Entity configure_entity_in_room(DinkyECS::World &world, nlohmann::json &entity_data, int in_room);
bool find_open_spot(Point& pos_out);
void place_entities(DinkyECS::World &world); void place_entities(DinkyECS::World &world);
void generate(DinkyECS::World &world); void generate(DinkyECS::World &world);
void randomize_entities(DinkyECS::World &world, components::GameConfig &config); void randomize_entities(DinkyECS::World &world, components::GameConfig &config);