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.

master
Zed A. Shaw 10 hours ago
parent 6cbfcf993e
commit 7a0b2f988d
  1. 4
      Makefile
  2. 16
      gui/status_ui.cpp
  3. 4
      levelmanager.cpp
  4. 2
      matrix.cpp
  5. 145
      maze.cpp
  6. 7
      maze.hpp
  7. 2
      meson.build
  8. 23
      tests/mazes.cpp
  9. 60
      worldbuilder.cpp

@ -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) {

@ -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));
}

@ -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,

@ -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;
tile_name = tile_name == "FLOOR_TILE" ? "WALL_PLAIN" : tile_name; (void)size;
for(matrix::circle it{$map.$walls, pos_out, size}; it.next();) { //tile_name = tile_name == "FLOOR_TILE" ? "WALL_PLAIN" : tile_name;
for(int x = it.left; x < it.right; x++) {
if($map.iswall(x, it.y)) { //for(matrix::circle it{$map.$walls, pos_out, size}; it.next();) {
// a wall tile // for(int x = it.left; x < it.right; x++) {
$map.$tiles.set_tile(x, it.y, tile_name); // if($map.iswall(x, it.y)) {
} else { // // a wall tile
// a floor tile // $map.$tiles.set_tile(x, it.y, tile_name);
$map.$tiles.set_tile(x, it.y, "FLOOR_TILE"); // } else {
} // // a 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);
PointList holes; matrix::dump("AFTER MAZE:", $map.$walls);
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…
Cancel
Save