Mostly cleaned up world get to handle more rooms and paths, but rando_rect needs to be actually random.
This commit is contained in:
parent
c19c53b15b
commit
e9277bf052
11 changed files with 257 additions and 39 deletions
2
.gdbinit
2
.gdbinit
|
@ -7,4 +7,4 @@ set pagination off
|
|||
break abort
|
||||
#break _invalid_parameter_noinfo
|
||||
#break _invalid_parameter
|
||||
# catch throw
|
||||
catch throw
|
||||
|
|
2
dbc.cpp
2
dbc.cpp
|
@ -2,7 +2,7 @@
|
|||
#include <iostream>
|
||||
|
||||
void dbc::log(const string &message) {
|
||||
std::cerr << message << std::endl;
|
||||
std::cerr << "!!!!!!!!!!" << message << std::endl;
|
||||
}
|
||||
|
||||
void dbc::sentinel(const string &message) {
|
||||
|
|
43
map.cpp
43
map.cpp
|
@ -44,18 +44,20 @@ void Map::clear_target(const Point &at) {
|
|||
$paths.clear_target(at);
|
||||
}
|
||||
|
||||
Point Map::place_entity(size_t room_index) {
|
||||
bool Map::place_entity(size_t room_index, Point &out) {
|
||||
dbc::check(room_index < $rooms.size(), "room_index is out of bounds, not enough rooms");
|
||||
|
||||
Room &start = $rooms[room_index];
|
||||
|
||||
size_t size = std::max(start.width, start.height);
|
||||
for(matrix::box it{$walls, start.x, start.y, size}; it.next();) {
|
||||
if(!iswall(it.x, it.y)) return {it.x, it.y};
|
||||
for(matrix::rando_rect it{$walls, start.x, start.y, start.width, start.height}; it.next();) {
|
||||
if(!iswall(it.x, it.y)) {
|
||||
out.x = it.x;
|
||||
out.y = it.y;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
dbc::sentinel("DIDN'T FIND AN OPEN SPACE!");
|
||||
return {start.x, start.y};
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Map::iswall(size_t x, size_t y) {
|
||||
|
@ -205,3 +207,32 @@ void Map::expand() {
|
|||
$paths = Pathing($width, $height);
|
||||
$tiles = TileMap($width, $height);
|
||||
}
|
||||
|
||||
void Map::add_room(Room &room) {
|
||||
// println(">>ADDING ROOM x/y={},{}; w/h={},{}; map={},{}",
|
||||
// room.x, room.y, room.width, room.height, $width, $height);
|
||||
|
||||
room.x++;
|
||||
room.y++;
|
||||
room.width--;
|
||||
room.height--;
|
||||
|
||||
if(room.x + room.width >= $width) {
|
||||
// fix the width
|
||||
room.x--;
|
||||
}
|
||||
|
||||
if(room.y + room.height >= $height) {
|
||||
// fix the height
|
||||
room.y--;
|
||||
}
|
||||
|
||||
$rooms.push_back(room);
|
||||
}
|
||||
|
||||
void Map::invert_space() {
|
||||
for(matrix::each_cell it{$walls}; it.next();) {
|
||||
int is_wall = !$walls[it.y][it.x];
|
||||
$walls[it.y][it.x] = is_wall;
|
||||
}
|
||||
}
|
||||
|
|
4
map.hpp
4
map.hpp
|
@ -53,7 +53,7 @@ public:
|
|||
Room &room(size_t at) { return $rooms[at]; }
|
||||
size_t room_count() { return $rooms.size(); }
|
||||
|
||||
Point place_entity(size_t room_index);
|
||||
bool place_entity(size_t room_index, Point &out);
|
||||
bool inmap(size_t x, size_t y);
|
||||
bool iswall(size_t x, size_t y);
|
||||
bool can_move(Point move_to);
|
||||
|
@ -73,4 +73,6 @@ public:
|
|||
bool INVARIANT();
|
||||
|
||||
void load_tiles();
|
||||
void add_room(Room &room);
|
||||
void invert_space();
|
||||
};
|
||||
|
|
60
matrix.hpp
60
matrix.hpp
|
@ -3,8 +3,12 @@
|
|||
#include <queue>
|
||||
#include <string>
|
||||
#include <array>
|
||||
#include <numeric>
|
||||
#include <algorithm>
|
||||
#include <fmt/core.h>
|
||||
#include "point.hpp"
|
||||
#include "rand.hpp"
|
||||
#include "dbc.hpp"
|
||||
|
||||
namespace matrix {
|
||||
using std::vector, std::queue, std::array;
|
||||
|
@ -324,4 +328,60 @@ namespace matrix {
|
|||
};
|
||||
|
||||
using circle = circle_t<Matrix>;
|
||||
|
||||
template<typename MAT>
|
||||
struct rectangle_t {
|
||||
int x;
|
||||
int y;
|
||||
int left;
|
||||
int right;
|
||||
int width;
|
||||
int height;
|
||||
int bottom;
|
||||
|
||||
rectangle_t(MAT &mat, size_t start_x, size_t start_y, size_t width, size_t height) :
|
||||
left(start_x),
|
||||
width(width),
|
||||
height(height)
|
||||
{
|
||||
size_t h = matrix::height(mat);
|
||||
size_t w = matrix::width(mat);
|
||||
y = start_y - 1;
|
||||
x = left - 1; // must be -1 for next()
|
||||
right = min(start_x + width, w);
|
||||
|
||||
y = start_y;
|
||||
bottom = min(start_y + height, h);
|
||||
}
|
||||
|
||||
bool next() {
|
||||
x = next_x(x, right);
|
||||
y = next_y(x, y);
|
||||
x = max(x, left);
|
||||
return at_end(y, bottom);
|
||||
}
|
||||
};
|
||||
|
||||
using rectangle = rectangle_t<Matrix>;
|
||||
|
||||
template<typename MAT>
|
||||
struct rando_rect_t {
|
||||
int x;
|
||||
int y;
|
||||
rectangle_t<MAT> it;
|
||||
|
||||
rando_rect_t(MAT &mat, size_t start_x, size_t start_y, size_t width, size_t height) :
|
||||
it{mat, start_x, start_y, width, height}
|
||||
{
|
||||
}
|
||||
|
||||
bool next() {
|
||||
bool done = it.next();
|
||||
x = it.x;
|
||||
y = it.y;
|
||||
return done;
|
||||
}
|
||||
};
|
||||
|
||||
using rando_rect = rando_rect_t<Matrix>;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
TODAY'S GOAL:
|
||||
|
||||
* UNKNOWN COLLISION TYPE 6
|
||||
* Either reduce the amount of size_t or use amit's suggestion of 0u since small sized unsigned can be casted to size_t.
|
||||
|
||||
|
||||
* https://github.com/Ericsson/codechecker?tab=readme-ov-file
|
||||
* Goblins will be in the world and not move or are already dead.
|
||||
* https://pkl-lang.org/
|
||||
|
|
|
@ -13,9 +13,11 @@ TEST_CASE("lighting a map works", "[lighting]") {
|
|||
Map map(20,23);
|
||||
WorldBuilder builder(map);
|
||||
builder.generate_map();
|
||||
Point light1, light2;
|
||||
|
||||
REQUIRE(map.place_entity(0, light1));
|
||||
REQUIRE(map.place_entity(1, light1));
|
||||
|
||||
Point light1 = map.place_entity(0);
|
||||
Point light2 = map.place_entity(1);
|
||||
LightSource source1{6, 1.0};
|
||||
LightSource source2{4,3};
|
||||
|
||||
|
|
|
@ -30,6 +30,31 @@ TEST_CASE("camera control", "[map]") {
|
|||
REQUIRE(translation.y == 2);
|
||||
}
|
||||
|
||||
TEST_CASE("map placement test", "[map:placement]") {
|
||||
for(int i = 0; i < 50; i++) {
|
||||
size_t width = Random::uniform<size_t>(9, 21);
|
||||
size_t height = Random::uniform<size_t>(13, 25);
|
||||
Map map(width, height);
|
||||
WorldBuilder builder(map);
|
||||
builder.generate_rooms();
|
||||
map.invert_space();
|
||||
|
||||
for(size_t rnum = 0; rnum < map.room_count(); rnum++) {
|
||||
Room &room = map.room(rnum);
|
||||
Point pos;
|
||||
|
||||
REQUIRE(map.place_entity(rnum, pos));
|
||||
// matrix::dump("ROOM PLACEMENT TEST", map.walls(), pos.x, pos.y);
|
||||
|
||||
REQUIRE(!map.iswall(pos.x, pos.y));
|
||||
REQUIRE(pos.x >= room.x);
|
||||
REQUIRE(pos.y >= room.y);
|
||||
REQUIRE(pos.x <= room.x + room.width);
|
||||
REQUIRE(pos.y <= room.y + room.height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("dijkstra algo test", "[map]") {
|
||||
json data = load_test_data("./tests/dijkstra.json");
|
||||
|
||||
|
|
|
@ -194,7 +194,8 @@ TEST_CASE("prototype flood algorithm", "[matrix:flood]") {
|
|||
|
||||
if(map.room_count() < 2) continue;
|
||||
|
||||
Point start = map.place_entity(map.room_count() / 2);
|
||||
Point start;
|
||||
REQUIRE(map.place_entity(map.room_count() / 2, start));
|
||||
map.set_target(start);
|
||||
map.make_paths();
|
||||
Matrix result = map.paths();
|
||||
|
@ -286,7 +287,8 @@ TEST_CASE("viewport iterator", "[matrix:viewport]") {
|
|||
|
||||
size_t view_width = width/2;
|
||||
size_t view_height = height/2;
|
||||
Point player = map.place_entity(1);
|
||||
Point player;
|
||||
REQUIRE(map.place_entity(1, player));
|
||||
Point start = map.center_camera(player, view_width, view_height);
|
||||
|
||||
size_t end_x = std::min(view_width, map.width() - start.x);
|
||||
|
@ -300,3 +302,71 @@ TEST_CASE("viewport iterator", "[matrix:viewport]") {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("random rectangle", "[matrix:rando_rect]") {
|
||||
for(int i = 0; i < 10; i++) {
|
||||
size_t width = Random::uniform<size_t>(9, 21);
|
||||
size_t height = Random::uniform<size_t>(13, 25);
|
||||
Map map(width, height);
|
||||
WorldBuilder builder(map);
|
||||
builder.generate_rooms();
|
||||
map.invert_space();
|
||||
auto wall_copy = map.walls();
|
||||
|
||||
for(size_t rnum = 0; rnum < map.room_count(); rnum++) {
|
||||
Room &room = map.room(rnum);
|
||||
Point pos;
|
||||
|
||||
for(matrix::rando_rect it{map.walls(), room.x, room.y, room.width, room.height}; it.next();)
|
||||
{
|
||||
if(map.iswall(it.x, it.y)) {
|
||||
matrix::dump("BAD RECTANGLE SPOT", map.walls(), it.x, it.y);
|
||||
}
|
||||
|
||||
REQUIRE(!map.iswall(it.x, it.y));
|
||||
REQUIRE(size_t(it.x) >= room.x);
|
||||
REQUIRE(size_t(it.y) >= room.y);
|
||||
REQUIRE(size_t(it.x) <= room.x + room.width);
|
||||
REQUIRE(size_t(it.y) <= room.y + room.height);
|
||||
|
||||
wall_copy[it.y][it.x] = wall_copy[it.y][it.x] + 5;
|
||||
}
|
||||
}
|
||||
|
||||
matrix::dump("WALLS FILLED", wall_copy);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("standard rectangle", "[matrix:rectangle]") {
|
||||
for(int i = 0; i < 20; i++) {
|
||||
size_t width = Random::uniform<size_t>(9, 21);
|
||||
size_t height = Random::uniform<size_t>(13, 25);
|
||||
Map map(width, height);
|
||||
WorldBuilder builder(map);
|
||||
builder.generate_rooms();
|
||||
map.invert_space();
|
||||
auto wall_copy = map.walls();
|
||||
|
||||
for(size_t rnum = 0; rnum < map.room_count(); rnum++) {
|
||||
Room &room = map.room(rnum);
|
||||
Point pos;
|
||||
|
||||
for(matrix::rectangle it{map.walls(), room.x, room.y, room.width, room.height}; it.next();)
|
||||
{
|
||||
if(map.iswall(it.x, it.y)) {
|
||||
matrix::dump("BAD RECTANGLE SPOT", map.walls(), it.x, it.y);
|
||||
}
|
||||
|
||||
REQUIRE(!map.iswall(it.x, it.y));
|
||||
REQUIRE(size_t(it.x) >= room.x);
|
||||
REQUIRE(size_t(it.y) >= room.y);
|
||||
REQUIRE(size_t(it.x) <= room.x + room.width);
|
||||
REQUIRE(size_t(it.y) <= room.y + room.height);
|
||||
|
||||
wall_copy[it.y][it.x] = wall_copy[it.y][it.x] + 5;
|
||||
}
|
||||
}
|
||||
|
||||
matrix::dump("WALLS FILLED", wall_copy);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,9 +53,10 @@ void WorldBuilder::add_door(Room &room) {
|
|||
}
|
||||
|
||||
void WorldBuilder::partition_map(Room &cur, int depth) {
|
||||
if(cur.width >= 5 && cur.width <= 10 &&
|
||||
cur.height >= 5 && cur.height <= 10) {
|
||||
$map.$rooms.push_back(cur);
|
||||
if(cur.width >= 3 && cur.width <= 6 &&
|
||||
cur.height >= 3 && cur.height <= 6)
|
||||
{
|
||||
$map.add_room(cur);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -97,9 +98,11 @@ void WorldBuilder::update_door(Point &at, int wall_or_space) {
|
|||
|
||||
|
||||
void WorldBuilder::stylize_room(int room, string tile_name, float size) {
|
||||
Point center = $map.place_entity(room);
|
||||
Point pos_out;
|
||||
bool placed = $map.place_entity(room, pos_out);
|
||||
dbc::check(placed, "failed to place style in room");
|
||||
|
||||
for(matrix::circle it{$map.$walls, center, size}; it.next();) {
|
||||
for(matrix::circle it{$map.$walls, pos_out, size}; it.next();) {
|
||||
for(int x = it.left; x < it.right; x++) {
|
||||
if(!$map.iswall(x, it.y)) {
|
||||
$map.$tiles.set_tile(x, it.y, tile_name);
|
||||
|
@ -108,22 +111,24 @@ void WorldBuilder::stylize_room(int room, string tile_name, float size) {
|
|||
}
|
||||
}
|
||||
|
||||
void WorldBuilder::generate_map() {
|
||||
PointList holes;
|
||||
void WorldBuilder::generate_rooms() {
|
||||
Room root{
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = $map.$width,
|
||||
.height = $map.$height
|
||||
};
|
||||
|
||||
// BUG: depth should be configurable
|
||||
partition_map(root, 10);
|
||||
|
||||
place_rooms();
|
||||
|
||||
dbc::check($map.room_count() > 0, "map generated zero rooms, map too small?");
|
||||
}
|
||||
|
||||
void WorldBuilder::generate_map() {
|
||||
generate_rooms();
|
||||
|
||||
PointList holes;
|
||||
for(size_t i = 0; i < $map.$rooms.size() - 1; i++) {
|
||||
tunnel_doors(holes, $map.$rooms[i], $map.$rooms[i+1]);
|
||||
}
|
||||
|
@ -133,14 +138,21 @@ void WorldBuilder::generate_map() {
|
|||
|
||||
// place all the holes
|
||||
for(auto hole : holes) {
|
||||
|
||||
if(!matrix::inbounds($map.$walls, hole.x, hole.y)) {
|
||||
matrix::dump("MAP BEFORE CRASH", $map.$walls, hole.x, hole.y);
|
||||
|
||||
auto err = fmt::format("invalid hold target {},{} map is only {},{}",
|
||||
hole.x, hole.y, matrix::width($map.$walls),
|
||||
matrix::height($map.$walls));
|
||||
|
||||
dbc::sentinel(err);
|
||||
}
|
||||
|
||||
$map.$walls[hole.y][hole.x] = INV_SPACE;
|
||||
}
|
||||
|
||||
for(matrix::each_cell it{$map.$walls}; it.next();) {
|
||||
int is_wall = !$map.$walls[it.y][it.x];
|
||||
$map.$walls[it.y][it.x] = is_wall;
|
||||
}
|
||||
|
||||
$map.invert_space();
|
||||
$map.expand();
|
||||
$map.load_tiles();
|
||||
|
||||
|
@ -159,9 +171,11 @@ void WorldBuilder::generate_map() {
|
|||
DinkyECS::Entity place_item(DinkyECS::World &world, Map &game_map, std::string name, int in_room) {
|
||||
auto &config = world.get_the<GameConfig>();
|
||||
auto item = world.entity();
|
||||
auto pos = game_map.place_entity(in_room);
|
||||
Point pos_out;
|
||||
bool placed = game_map.place_entity(in_room, pos_out);
|
||||
dbc::check(placed, "failed to randomly place item in room");
|
||||
json item_data = config.items[name];
|
||||
world.set<Position>(item, {pos.x+1, pos.y+1});
|
||||
world.set<Position>(item, {pos_out.x+1, pos_out.y+1});
|
||||
|
||||
if(item_data["inventory_count"] > 0) {
|
||||
world.set<InventoryItem>(item, {item_data["inventory_count"], item_data});
|
||||
|
@ -178,7 +192,9 @@ DinkyECS::Entity place_combatant(DinkyECS::World &world, Map &game_map, std::str
|
|||
auto &config = world.get_the<GameConfig>();
|
||||
auto enemy = world.entity();
|
||||
auto enemy_data = config.enemies[name];
|
||||
auto pos = game_map.place_entity(in_room);
|
||||
Point pos;
|
||||
bool placed = game_map.place_entity(in_room, pos);
|
||||
dbc::check(placed, "failed to place combatant in room");
|
||||
world.set<Position>(enemy, {pos});
|
||||
|
||||
if(enemy_data.contains("components")) {
|
||||
|
@ -250,11 +266,8 @@ void WorldBuilder::make_room(size_t origin_x, size_t origin_y, size_t w, size_t
|
|||
|
||||
void WorldBuilder::place_rooms() {
|
||||
for(auto &cur : $map.$rooms) {
|
||||
cur.x += WORLDBUILD_SHRINK;
|
||||
cur.y += WORLDBUILD_SHRINK;
|
||||
cur.width -= WORLDBUILD_SHRINK * 2;
|
||||
cur.height -= WORLDBUILD_SHRINK * 2;
|
||||
|
||||
// println("ROOM x/y={},{}; w/h={},{}; map={},{}",
|
||||
// cur.x, cur.y, cur.width, cur.height, $map.$width, $map.$height);
|
||||
add_door(cur);
|
||||
make_room(cur.x, cur.y, cur.width, cur.height);
|
||||
}
|
||||
|
@ -281,24 +294,34 @@ inline bool random_path(Map &map, PointList &holes, Point src, Point target) {
|
|||
return target_found;
|
||||
}
|
||||
|
||||
inline void straight_path(PointList &holes, Point src, Point target) {
|
||||
inline void straight_path(Map &map, PointList &holes, Point src, Point target) {
|
||||
for(matrix::line dig{src, target}; dig.next();) {
|
||||
holes.push_back({size_t(dig.x), size_t(dig.y)});
|
||||
holes.push_back({size_t(dig.x+1), size_t(dig.y)});
|
||||
Point expand{(size_t)dig.x+1, (size_t)dig.y};
|
||||
|
||||
if(map.inmap(expand.x, expand.y)) {
|
||||
// BUG? should really just move doors away from the edge
|
||||
holes.push_back(expand);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WorldBuilder::tunnel_doors(PointList &holes, Room &src, Room &target) {
|
||||
int path_type = Random::uniform<int>(0, 3);
|
||||
|
||||
switch(path_type) {
|
||||
case 0:
|
||||
// for now do 25% as simple straight paths
|
||||
straight_path(holes, src.exit, target.entry);
|
||||
straight_path($map, holes, src.exit, target.entry);
|
||||
break;
|
||||
case 1:
|
||||
// for now do 25% as simple straight paths
|
||||
straight_path($map, holes, src.exit, target.entry);
|
||||
break;
|
||||
default:
|
||||
// then do the rest as random with fallback
|
||||
if(!random_path($map, holes, src.exit, target.entry)) {
|
||||
straight_path(holes, src.exit, target.entry);
|
||||
straight_path($map, holes, src.exit, target.entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ class WorldBuilder {
|
|||
void tunnel_doors(PointList &holes, Room &src, Room &target);
|
||||
void update_door(Point &at, int wall_or_space);
|
||||
void stylize_room(int room, string tile_name, float size);
|
||||
void generate_rooms();
|
||||
void generate_map();
|
||||
void place_entities(DinkyECS::World &world);
|
||||
void generate(DinkyECS::World &world);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue