Add a terrible maze generation algorithm to test if a maze style map looks/feels better. The walls are disabled so you can walk around.
This commit is contained in:
parent
6cbfcf993e
commit
7a0b2f988d
9 changed files with 207 additions and 52 deletions
4
Makefile
4
Makefile
|
@ -26,7 +26,7 @@ tracy_build:
|
||||||
meson compile -j 10 -C builddir
|
meson compile -j 10 -C builddir
|
||||||
|
|
||||||
test: build
|
test: build
|
||||||
./builddir/runtests
|
./builddir/runtests "[maze-gen]"
|
||||||
|
|
||||||
run: build test
|
run: build test
|
||||||
ifeq '$(OS)' 'Windows_NT'
|
ifeq '$(OS)' 'Windows_NT'
|
||||||
|
@ -49,7 +49,7 @@ clean:
|
||||||
meson compile --clean -C builddir
|
meson compile --clean -C builddir
|
||||||
|
|
||||||
debug_test: build
|
debug_test: build
|
||||||
gdb --nx -x .gdbinit --ex run --args builddir/runtests -e
|
gdb --nx -x .gdbinit --ex run --args builddir/runtests -e "[maze-gen]"
|
||||||
|
|
||||||
win_installer:
|
win_installer:
|
||||||
powershell 'start "C:\Program Files (x86)\solicus\InstallForge\bin\ifbuilderenvx86.exe" scripts\win_installer.ifp'
|
powershell 'start "C:\Program Files (x86)\solicus\InstallForge\bin\ifbuilderenvx86.exe" scripts\win_installer.ifp'
|
||||||
|
|
|
@ -15,11 +15,11 @@ namespace gui {
|
||||||
$gui.position(STATUS_UI_X, STATUS_UI_Y, STATUS_UI_WIDTH, STATUS_UI_HEIGHT);
|
$gui.position(STATUS_UI_X, STATUS_UI_Y, STATUS_UI_WIDTH, STATUS_UI_HEIGHT);
|
||||||
$gui.layout(
|
$gui.layout(
|
||||||
"[ ritual_ui ]"
|
"[ ritual_ui ]"
|
||||||
"[inv_slot1 | inv_slot2 | inv_slot3]"
|
"[inv_1|inv_2|inv_3]"
|
||||||
"[inv_slot4 | inv_slot5 | inv_slot6]"
|
"[inv_4|*%(200,300)character_view|_|inv_5]"
|
||||||
"[*%(200,300)character_view|_|stat1]"
|
"[inv_6|_|_ |inv_7]"
|
||||||
"[_|_|stat2]"
|
"[inv_8|_|_ |inv_9]"
|
||||||
"[_|_|stat3]");
|
"[inv_10|inv_11|inv_12]");
|
||||||
|
|
||||||
size_t inv_id = 0;
|
size_t inv_id = 0;
|
||||||
for(auto [name, entity] : $gui.$name_ents) {
|
for(auto [name, entity] : $gui.$name_ents) {
|
||||||
|
@ -37,14 +37,9 @@ namespace gui {
|
||||||
auto char_view = $gui.entity(name);
|
auto char_view = $gui.entity(name);
|
||||||
$gui.set<Rectangle>(char_view, {});
|
$gui.set<Rectangle>(char_view, {});
|
||||||
$gui.set<Sprite>(char_view, {"peasant_girl"});
|
$gui.set<Sprite>(char_view, {"peasant_girl"});
|
||||||
} else if(name.starts_with("stat")) {
|
|
||||||
auto stat = $gui.entity(name);
|
|
||||||
$gui.set<Rectangle>(stat, {});
|
|
||||||
$gui.set<Label>(stat, {guecs::to_wstring(name)});
|
|
||||||
} else {
|
} else {
|
||||||
auto button = $gui.entity(name);
|
auto button = $gui.entity(name);
|
||||||
$gui.set<Rectangle>(button, {});
|
$gui.set<Rectangle>(button, {});
|
||||||
$gui.set<Textual>(button, {L""});
|
|
||||||
$gui.set<ActionData>(button, {make_any<string>(name)});
|
$gui.set<ActionData>(button, {make_any<string>(name)});
|
||||||
|
|
||||||
if(name == "ritual_ui") {
|
if(name == "ritual_ui") {
|
||||||
|
@ -53,6 +48,7 @@ namespace gui {
|
||||||
});
|
});
|
||||||
$gui.set<Sound>(button, {"pickup"});
|
$gui.set<Sound>(button, {"pickup"});
|
||||||
} else {
|
} else {
|
||||||
|
$gui.set<Textual>(button, {guecs::to_wstring(name)});
|
||||||
$gui.set<Clickable>(button, {
|
$gui.set<Clickable>(button, {
|
||||||
[this](auto ent, auto data){ select_slot(ent, data); }
|
[this](auto ent, auto data){ select_slot(ent, data); }
|
||||||
});
|
});
|
||||||
|
|
|
@ -17,8 +17,8 @@ LevelManager::LevelManager() {
|
||||||
|
|
||||||
LevelScaling LevelManager::scale_level() {
|
LevelScaling LevelManager::scale_level() {
|
||||||
return {
|
return {
|
||||||
20 + (5 * int($current_level)),
|
21,
|
||||||
15 + (5 * int($current_level))
|
21
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,8 @@ namespace matrix {
|
||||||
print("{:x}<", cell);
|
print("{:x}<", cell);
|
||||||
} else if(cell == WALL_PATH_LIMIT) {
|
} else if(cell == WALL_PATH_LIMIT) {
|
||||||
print("# ");
|
print("# ");
|
||||||
|
} else if(cell == 0) {
|
||||||
|
print(". ");
|
||||||
} else if(cell > 15 && cell < 32) {
|
} else if(cell > 15 && cell < 32) {
|
||||||
print("{:x}+", cell - 16);
|
print("{:x}+", cell - 16);
|
||||||
} else if(cell > 31) {
|
} else if(cell > 31) {
|
||||||
|
|
145
maze.cpp
Normal file
145
maze.cpp
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
#include <fmt/core.h>
|
||||||
|
#include <string>
|
||||||
|
#include "rand.hpp"
|
||||||
|
#include "constants.hpp"
|
||||||
|
#include "maze.hpp"
|
||||||
|
|
||||||
|
using std::string;
|
||||||
|
using matrix::Matrix;
|
||||||
|
|
||||||
|
inline size_t rand(size_t i, size_t j) {
|
||||||
|
if(i < j) {
|
||||||
|
return Random::uniform(i, j);
|
||||||
|
} else if(j < i) {
|
||||||
|
return Random::uniform(j, i);
|
||||||
|
} else {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline bool split_dir(size_t iDim, size_t jDim) {
|
||||||
|
if(iDim < jDim) {
|
||||||
|
return false;
|
||||||
|
} else if(jDim < iDim) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return Random::uniform(0, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool good_hole(Matrix &map, size_t split, size_t hole, bool horiz) {
|
||||||
|
if(hole % 2 == 0) return false;
|
||||||
|
|
||||||
|
size_t j = horiz ? split : hole;
|
||||||
|
size_t i = horiz ? hole : split;
|
||||||
|
if(map[j][i] == WALL_PATH_LIMIT) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void divide(Matrix& map, std::vector<Room> &rooms,
|
||||||
|
Point iCoords, Point jCoords, bool horizontal) {
|
||||||
|
int iDim = iCoords.y - iCoords.x;
|
||||||
|
int jDim = jCoords.y - jCoords.x;
|
||||||
|
bool punch_room = false;
|
||||||
|
|
||||||
|
if(iDim <= 0 || jDim <= 0) {
|
||||||
|
return;
|
||||||
|
} else if(iDim <= 2 && jDim <= 2) {
|
||||||
|
fmt::println("MADE ROOM! {},{}; {},{}",
|
||||||
|
iCoords.x, iCoords.y, jCoords.x, jCoords.y);
|
||||||
|
punch_room = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(horizontal) {
|
||||||
|
size_t split = 0;
|
||||||
|
do {
|
||||||
|
split = rand(iCoords.x, iCoords.x + iDim + 1);
|
||||||
|
} while(split % 2);
|
||||||
|
|
||||||
|
size_t hole = 0;
|
||||||
|
do {
|
||||||
|
hole = rand(jCoords.x, jCoords.x + jDim +1);
|
||||||
|
} while(good_hole(map, split, hole, horizontal));
|
||||||
|
|
||||||
|
for(size_t j = jCoords.x; j <= jCoords.y; j++) {
|
||||||
|
if(j != hole) {
|
||||||
|
map[split][j] = WALL_PATH_LIMIT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
divide(map, rooms,
|
||||||
|
{iCoords.x, size_t(split - 1)},
|
||||||
|
jCoords,
|
||||||
|
split_dir(split - iCoords.x - 1, jDim));
|
||||||
|
|
||||||
|
divide(map, rooms,
|
||||||
|
{size_t(split + 1), iCoords.y},
|
||||||
|
jCoords,
|
||||||
|
split_dir(iCoords.x - split - 1, jDim));
|
||||||
|
} else {
|
||||||
|
size_t split = 0;
|
||||||
|
do {
|
||||||
|
split = rand(jCoords.x, jCoords.x + jDim + 1);
|
||||||
|
} while(split % 2);
|
||||||
|
|
||||||
|
size_t hole = 0;
|
||||||
|
do {
|
||||||
|
hole = rand(iCoords.x, iCoords.x + iDim + 1);
|
||||||
|
} while(good_hole(map, split, hole, horizontal));
|
||||||
|
|
||||||
|
for(size_t i = iCoords.x; i <= iCoords.y; i++) {
|
||||||
|
if(i != hole) {
|
||||||
|
map[i][split] = WALL_PATH_LIMIT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
divide(map, rooms,
|
||||||
|
iCoords,
|
||||||
|
{jCoords.x, size_t(split - 1)},
|
||||||
|
split_dir(iDim, split - jCoords.x - 1));
|
||||||
|
|
||||||
|
divide(map, rooms,
|
||||||
|
iCoords,
|
||||||
|
{size_t(split + 1), jCoords.y},
|
||||||
|
Random::uniform(0, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(punch_room) {
|
||||||
|
for(size_t j = jCoords.x; j <= jCoords.y; j++) {
|
||||||
|
for(size_t i = iCoords.x; i <= iCoords.y; i++) {
|
||||||
|
map[j][i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Room room{iCoords.x, jCoords.x, iCoords.y - iCoords.x + 1, jCoords.y - jCoords.x + 1};
|
||||||
|
|
||||||
|
for(auto r : rooms) {
|
||||||
|
if(r.x == room.x && r.y == room.y) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rooms.push_back(room);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void maze::recursive_div(Matrix& map, std::vector<Room>& rooms) {
|
||||||
|
size_t width = matrix::width(map);
|
||||||
|
size_t height = matrix::height(map);
|
||||||
|
|
||||||
|
for(size_t i = 0; i < height; i++) {
|
||||||
|
for(size_t j = 0; j < width; j++) {
|
||||||
|
int val = (i == 0 ||
|
||||||
|
j == 0 ||
|
||||||
|
i == height - 1 ||
|
||||||
|
j == width - 1);
|
||||||
|
|
||||||
|
map[i][j] = val == 1 ? WALL_PATH_LIMIT : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
divide(map, rooms, {1, height - 2}, {1, width - 2}, split_dir(1, 1));
|
||||||
|
}
|
7
maze.hpp
Normal file
7
maze.hpp
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
#pragma once
|
||||||
|
#include "matrix.hpp"
|
||||||
|
#include "map.hpp"
|
||||||
|
|
||||||
|
namespace maze {
|
||||||
|
void recursive_div(matrix::Matrix& map, std::vector<Room>& rooms);
|
||||||
|
}
|
|
@ -124,6 +124,7 @@ sources = [
|
||||||
'textures.cpp',
|
'textures.cpp',
|
||||||
'tilemap.cpp',
|
'tilemap.cpp',
|
||||||
'worldbuilder.cpp',
|
'worldbuilder.cpp',
|
||||||
|
'maze.cpp'
|
||||||
]
|
]
|
||||||
|
|
||||||
executable('runtests', sources + [
|
executable('runtests', sources + [
|
||||||
|
@ -150,6 +151,7 @@ executable('runtests', sources + [
|
||||||
'tests/stats.cpp',
|
'tests/stats.cpp',
|
||||||
'tests/textures.cpp',
|
'tests/textures.cpp',
|
||||||
'tests/tilemap.cpp',
|
'tests/tilemap.cpp',
|
||||||
|
'tests/mazes.cpp',
|
||||||
],
|
],
|
||||||
cpp_args: cpp_args,
|
cpp_args: cpp_args,
|
||||||
link_args: link_args,
|
link_args: link_args,
|
||||||
|
|
23
tests/mazes.cpp
Normal file
23
tests/mazes.cpp
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
#include <fmt/core.h>
|
||||||
|
#include <string>
|
||||||
|
#include "matrix.hpp"
|
||||||
|
#include "rand.hpp"
|
||||||
|
#include "constants.hpp"
|
||||||
|
#include "maze.hpp"
|
||||||
|
|
||||||
|
using std::string;
|
||||||
|
using matrix::Matrix;
|
||||||
|
|
||||||
|
TEST_CASE("simple maze first attempt", "[maze-gen]") {
|
||||||
|
auto map = matrix::make(21, 21);
|
||||||
|
std::vector<Room> rooms;
|
||||||
|
|
||||||
|
maze::recursive_div(map, rooms);
|
||||||
|
matrix::dump("MAZE?", map);
|
||||||
|
|
||||||
|
for(auto& room : rooms) {
|
||||||
|
fmt::println("room: {},{}; {},{}",
|
||||||
|
room.x, room.y, room.width, room.height);
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,7 @@
|
||||||
#include "components.hpp"
|
#include "components.hpp"
|
||||||
#include "inventory.hpp"
|
#include "inventory.hpp"
|
||||||
#include "rituals.hpp"
|
#include "rituals.hpp"
|
||||||
|
#include "maze.hpp"
|
||||||
|
|
||||||
using namespace fmt;
|
using namespace fmt;
|
||||||
using namespace components;
|
using namespace components;
|
||||||
|
@ -103,20 +104,22 @@ void WorldBuilder::stylize_room(int room, string tile_name, float size) {
|
||||||
Point pos_out;
|
Point pos_out;
|
||||||
bool placed = $map.place_entity(room, pos_out);
|
bool placed = $map.place_entity(room, pos_out);
|
||||||
dbc::check(placed, "failed to place style in room");
|
dbc::check(placed, "failed to place style in room");
|
||||||
|
(void)tile_name;
|
||||||
|
(void)size;
|
||||||
|
|
||||||
tile_name = tile_name == "FLOOR_TILE" ? "WALL_PLAIN" : tile_name;
|
//tile_name = tile_name == "FLOOR_TILE" ? "WALL_PLAIN" : tile_name;
|
||||||
|
|
||||||
for(matrix::circle it{$map.$walls, pos_out, size}; it.next();) {
|
//for(matrix::circle it{$map.$walls, pos_out, size}; it.next();) {
|
||||||
for(int x = it.left; x < it.right; x++) {
|
// for(int x = it.left; x < it.right; x++) {
|
||||||
if($map.iswall(x, it.y)) {
|
// if($map.iswall(x, it.y)) {
|
||||||
// a wall tile
|
// // a wall tile
|
||||||
$map.$tiles.set_tile(x, it.y, tile_name);
|
// $map.$tiles.set_tile(x, it.y, tile_name);
|
||||||
} else {
|
// } else {
|
||||||
// a floor tile
|
// // a floor tile
|
||||||
$map.$tiles.set_tile(x, it.y, "FLOOR_TILE");
|
// $map.$tiles.set_tile(x, it.y, "FLOOR_TILE");
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorldBuilder::generate_rooms() {
|
void WorldBuilder::generate_rooms() {
|
||||||
|
@ -134,33 +137,10 @@ void WorldBuilder::generate_rooms() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorldBuilder::generate_map() {
|
void WorldBuilder::generate_map() {
|
||||||
generate_rooms();
|
matrix::dump("BEFORE MAZE:", $map.$walls);
|
||||||
|
maze::recursive_div($map.$walls, $map.$rooms);
|
||||||
|
matrix::dump("AFTER MAZE:", $map.$walls);
|
||||||
|
|
||||||
PointList holes;
|
|
||||||
for(size_t i = 0; i < $map.$rooms.size() - 1; i++) {
|
|
||||||
tunnel_doors(holes, $map.$rooms[i], $map.$rooms[i+1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// one last connection from first room to last
|
|
||||||
tunnel_doors(holes, $map.$rooms.back(), $map.$rooms.front());
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
$map.invert_space();
|
|
||||||
$map.expand();
|
$map.expand();
|
||||||
$map.load_tiles();
|
$map.load_tiles();
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue