From ac252bf09dbe37a37b671a144e34748915b3b3f3 Mon Sep 17 00:00:00 2001
From: "Zed A. Shaw" <zed.shaw@gmail.com>
Date: Mon, 19 May 2025 11:19:33 -0400
Subject: [PATCH] Maze works well now and there's something placed in all rooms
 and dead ends. Will need to randomize it more so not every dead end is an
 enemy.

---
 assets/config.json  |   1 -
 assets/devices.json |   2 +-
 assets/items.json   |   2 +-
 map.hpp             |   3 +-
 maze.cpp            |   2 +-
 worldbuilder.cpp    | 107 ++++++++++++++++----------------------------
 worldbuilder.hpp    |   4 +-
 7 files changed, 46 insertions(+), 75 deletions(-)

diff --git a/assets/config.json b/assets/config.json
index bc0ff49..ab36b4d 100644
--- a/assets/config.json
+++ b/assets/config.json
@@ -323,7 +323,6 @@
   },
   "worldgen": {
     "enemy_probability": 50,
-    "empty_room_probability": 1,
     "device_probability": 10
   },
   "graphics": {
diff --git a/assets/devices.json b/assets/devices.json
index d0d2335..9108d00 100644
--- a/assets/devices.json
+++ b/assets/devices.json
@@ -40,7 +40,7 @@
     "description": "Watch where you're going.",
     "inventory_count": 0,
     "components": [
-      {"_type": "Tile", "display": 6855,
+      {"_type": "Tile", "display": 95,
         "foreground": [24, 205, 189],
         "background": [24, 205, 189]
       },
diff --git a/assets/items.json b/assets/items.json
index a8f6df3..e7f2a15 100644
--- a/assets/items.json
+++ b/assets/items.json
@@ -34,7 +34,7 @@
     "name": "Small Barrel",
     "description": "A small rotten barrel that may hold things.",
     "components": [
-      {"_type": "Tile", "display": 43754,
+      {"_type": "Tile", "display": 85,
         "foreground": [150, 100, 189],
         "background": [150, 100, 189]
       },
diff --git a/map.hpp b/map.hpp
index 1dccbe2..04b1bee 100644
--- a/map.hpp
+++ b/map.hpp
@@ -19,8 +19,6 @@ struct Room {
   size_t y = 0;
   size_t width = 0;
   size_t height = 0;
-  Point entry{(size_t)-1, (size_t)-1};
-  Point exit{(size_t)-1, (size_t)-1};
 };
 
 class Map {
@@ -31,6 +29,7 @@ public:
   Matrix $walls;
   Pathing $paths;
   std::vector<Room> $rooms;
+  std::vector<Point> $dead_ends;
 
   Map(size_t width, size_t height);
 
diff --git a/maze.cpp b/maze.cpp
index 630c45c..4b785c7 100644
--- a/maze.cpp
+++ b/maze.cpp
@@ -28,7 +28,6 @@ inline bool complete(Matrix& maze) {
     }
   }
 
-  // dbc::sentinel("LOL it's complete eh?");
   return true;
 }
 
@@ -136,6 +135,7 @@ void maze::hunt_and_kill(Matrix& maze, std::vector<Room>& rooms, std::vector<Poi
     for(auto& room : rooms) {
       Point room_ul{room.x - room.width - 1, room.y - room.height - 1};
       Point room_lr{room.x + room.width - 1, room.y + room.height - 1};
+
       if(at.x >= room_ul.x && at.y >= room_ul.y &&
           at.x <= room_lr.x && at.y <= room_lr.y)
       {
diff --git a/worldbuilder.cpp b/worldbuilder.cpp
index 6a3abbe..367f13d 100644
--- a/worldbuilder.cpp
+++ b/worldbuilder.cpp
@@ -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) {
diff --git a/worldbuilder.hpp b/worldbuilder.hpp
index d8d0356..d92e126 100644
--- a/worldbuilder.hpp
+++ b/worldbuilder.hpp
@@ -14,10 +14,12 @@ class WorldBuilder {
     $components(components)
   { }
 
-  void add_door(Room &room);
   void generate_map();
 
+  DinkyECS::Entity configure_entity_in_map(DinkyECS::World &world, nlohmann::json &entity_data, Point pos);
+
   DinkyECS::Entity configure_entity_in_map(DinkyECS::World &world, nlohmann::json &entity_data, int in_room);
+
   void place_entities(DinkyECS::World &world);
   void generate(DinkyECS::World &world);
   void randomize_entities(DinkyECS::World &world, components::GameConfig &config);