From 4eaf3c35d63d3475c2277d4ad5ac54f85c3935d1 Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Thu, 22 May 2025 12:24:59 -0400 Subject: [PATCH] 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. --- assets/items.json | 15 --------------- camera.cpp | 4 ++-- camera.hpp | 2 +- constants.hpp | 2 ++ gui/fsm.cpp | 6 +++--- gui/main_ui.cpp | 7 ++++--- gui/main_ui.hpp | 2 +- levelmanager.cpp | 4 ++-- maze.cpp | 16 ++++++---------- worldbuilder.cpp | 39 +++++++++++++++++++++++++++++++-------- worldbuilder.hpp | 3 +++ 11 files changed, 55 insertions(+), 45 deletions(-) diff --git a/assets/items.json b/assets/items.json index e7f2a15..109fab9 100644 --- a/assets/items.json +++ b/assets/items.json @@ -14,21 +14,6 @@ {"_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": { "id": "BARREL_SMALL", "name": "Small Barrel", diff --git a/camera.cpp b/camera.cpp index 2293cd6..46f03bd 100644 --- a/camera.cpp +++ b/camera.cpp @@ -15,9 +15,9 @@ Point CameraLOL::plan_move(int dir, bool strafe) { 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; - 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_y = rayview.$dir_x * sin(angle_dir) + rayview.$dir_y * cos(angle_dir); diff --git a/camera.hpp b/camera.hpp index d403045..12ef8e3 100644 --- a/camera.hpp +++ b/camera.hpp @@ -17,7 +17,7 @@ struct CameraLOL { rayview(rv) {} 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_move(); diff --git a/constants.hpp b/constants.hpp index 8861339..11f8146 100644 --- a/constants.hpp +++ b/constants.hpp @@ -60,6 +60,8 @@ constexpr int COMBAT_UI_Y = RAY_VIEW_HEIGHT; constexpr int COMBAT_UI_WIDTH = RAY_VIEW_WIDTH ; 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 constexpr wchar_t BG_TILE = L'█'; diff --git a/gui/fsm.cpp b/gui/fsm.cpp index 0031ca9..5a4dd67 100644 --- a/gui/fsm.cpp +++ b/gui/fsm.cpp @@ -152,11 +152,11 @@ namespace gui { try_move(1, true); break; case ROTATE_LEFT: - $main_ui.plan_rotate(-1); + $main_ui.plan_rotate(-1, 0.5f); state(State::ROTATING); break; case ROTATE_RIGHT: - $main_ui.plan_rotate(1); + $main_ui.plan_rotate(1, 0.5f); state(State::ROTATING); break; case MAP_OPEN: @@ -356,7 +356,7 @@ namespace gui { if($map_open) { $map_ui.render($window, $main_ui.$compass_dir); } else { - // $mini_map.render($window, $main_ui.$compass_dir); + $mini_map.render($window, $main_ui.$compass_dir); } } } diff --git a/gui/main_ui.cpp b/gui/main_ui.cpp index 70d2ae0..aaa7cb5 100644 --- a/gui/main_ui.cpp +++ b/gui/main_ui.cpp @@ -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 - $compass_dir = ($compass_dir + dir) % COMPASS.size(); - $camera.plan_rotate(dir); + int extra = (amount == 0.5) * dir; + $compass_dir = ($compass_dir + dir + extra) % COMPASS.size(); + $camera.plan_rotate(dir, amount); } Point MainUI::plan_move(int dir, bool strafe) { diff --git a/gui/main_ui.hpp b/gui/main_ui.hpp index 75d4e7d..e696cab 100644 --- a/gui/main_ui.hpp +++ b/gui/main_ui.hpp @@ -29,7 +29,7 @@ namespace gui { void debug(); void render_debug(); - void plan_rotate(int dir); + void plan_rotate(int dir, float amount=0.5f); bool play_rotate(); std::optional play_move(); Point plan_move(int dir, bool strafe); diff --git a/levelmanager.cpp b/levelmanager.cpp index 2fc5baa..ea405f7 100644 --- a/levelmanager.cpp +++ b/levelmanager.cpp @@ -17,8 +17,8 @@ LevelManager::LevelManager() { LevelScaling LevelManager::scale_level() { return { - 21, - 21 + INITIAL_MAP_W + int($current_level * 2), + INITIAL_MAP_H + int($current_level * 2) }; } diff --git a/maze.cpp b/maze.cpp index 62a40cb..c6ab7b9 100644 --- a/maze.cpp +++ b/maze.cpp @@ -95,16 +95,12 @@ namespace maze { } void Builder::randomize_rooms() { - dbc::check($dead_ends.size() >= 2, "must have at least two possible points to place rooms"); - - while($rooms.size() < 2) { - // use those dead ends to randomly place rooms - for(auto at : $dead_ends) { - if(Random::uniform(0,1)) { - size_t offset = Random::uniform(0,1); - Room cur{at.x+offset, at.y+offset, 1, 1}; - $rooms.push_back(cur); - } + // use those dead ends to randomly place rooms + for(auto at : $dead_ends) { + if(Random::uniform(0,1)) { + size_t offset = Random::uniform(0,1); + Room cur{at.x+offset, at.y+offset, 1, 1}; + $rooms.push_back(cur); } } } diff --git a/worldbuilder.cpp b/worldbuilder.cpp index 94da449..acb459c 100644 --- a/worldbuilder.cpp +++ b/worldbuilder.cpp @@ -12,21 +12,41 @@ using namespace components; void WorldBuilder::generate_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.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.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(); - world.set(item, {pos_out.x+1, pos_out.y+1}); + world.set(item, {pos.x, pos.y}); if(entity_data["inventory_count"] > 0) { world.set(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"]); } + $collision.insert(pos, 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) { auto& device_config = config.devices.json(); 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) { diff --git a/worldbuilder.hpp b/worldbuilder.hpp index 66b9b93..bb5663e 100644 --- a/worldbuilder.hpp +++ b/worldbuilder.hpp @@ -3,11 +3,13 @@ #include "map.hpp" #include "dinkyecs.hpp" #include "components.hpp" +#include "spatialmap.hpp" class WorldBuilder { public: Map& $map; components::ComponentMap& $components; + SpatialMap $collision; WorldBuilder(Map &map, components::ComponentMap& components) : $map(map), @@ -20,6 +22,7 @@ class WorldBuilder { 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 generate(DinkyECS::World &world); void randomize_entities(DinkyECS::World &world, components::GameConfig &config);