Commit 55498a7e authored by Stuart John Watson's avatar Stuart John Watson

Restructured dungeons

Split it in the more abstract dungeon which both the DungeonGenerator and loader inherit from
Also modified game to used magic pointers in the vectores so dungeons can be either
I can now see why people invented other launguages.
parent 0d38472c
#include <iostream>
#include <random>
#include <tuple>
#include <libconfig.h++>
#include "../configReader.h"
#include "types.h"
#include "space.h"
#include "types.h"
#include <string>
#include <iostream>
//Initilize statics
libconfig::Config cfg;
int _ = loadConfig(std::string("config.cfg"),&cfg);
const std::string VERSION = fromConfig(&cfg,"version",std::string(""));
const float Dungeon::ROOM_CHANCE = fromConfig(&cfg,"generator.pRoom",0.5f);
const int Dungeon::CORRI_MIN_LENGTH = fromConfig(&cfg,"generator.corridor.minLength",10);
const int Dungeon::CORRI_MAX_LENGTH = fromConfig(&cfg,"generator.corridor.maxLength",20);
const int Dungeon::CORRI_MIN_WIDTH = fromConfig(&cfg,"generator.corridor.minWidth",3);
const int Dungeon::COORI_MAX_WIDTH = fromConfig(&cfg,"generator.corridor.maxWidth",5);
const int Dungeon::ROOM_MIN_SIZE = fromConfig(&cfg,"generator.room.minSize",7);
const int Dungeon::ROOM_MAX_SIZE = fromConfig(&cfg,"generator.room.maxSize",19);
const int Dungeon::PLACE_TRIES = fromConfig(&cfg,"generator.nPlacetries",1000);
const int Dungeon::CONNECTION_TRIES = fromConfig(&cfg,"generator.connector.nTries",200);
const int Dungeon::CONNECTION_LENGTH= fromConfig(&cfg,"generator.connector.maxLength",10);
Dungeon::Dungeon(int _width,int _height,int seed) : Space(_width,_height){
generator = std::default_random_engine(seed);
}
std::tuple<int,int> Dungeon::upStairPosition(){
std::tuple<int,int> DungeonGenerator::upStairPosition(){
int x,y;
for (x=0;x<width;x++){
for (y=0;y<height;y++){
......@@ -39,7 +15,7 @@ std::tuple<int,int> Dungeon::upStairPosition(){
return std::make_tuple(-1,-1);
}
std::tuple<int,int> Dungeon::downStairPosition(){
std::tuple<int,int> DungeonGenerator::downStairPosition(){
int x,y;
for(x = 0; x < width; x++){
for(y = 0; y < height; y++){
......@@ -50,220 +26,3 @@ std::tuple<int,int> Dungeon::downStairPosition(){
}
return std::make_tuple(-1,-1);
}
void Dungeon::populate(){
Space::populate();
//Chance we put a room instead of a corridor down
std::uniform_real_distribution<float> roomChance(0.0,1.0);
//Distribution for the length of our corridors
std::uniform_int_distribution<int> corriLength(Dungeon::CORRI_MIN_LENGTH,Dungeon::CORRI_MAX_LENGTH);
//Distribution for the width of our corridors
std::uniform_int_distribution<int> corriWidth(Dungeon::CORRI_MIN_WIDTH,Dungeon::COORI_MAX_WIDTH);
//Distribution for the size of our rooms (used for width and height sepertally)
std::uniform_int_distribution<int> roomSize(Dungeon::ROOM_MIN_SIZE,Dungeon::ROOM_MAX_SIZE);
/*/Chance we later knock down a joining wall for a door to increase conectivity
std::uniform_int_distribution<int> mergeDoor(1,20);*/
//Place a room in the center of the dungeon
Rectangle room = Rectangle(roomSize(generator),roomSize(generator));
room.populate();
insertSpace(room,getWidth()/2,getHeight()/2,MIDDLE_CENTER);
//Now keep trying to
for (int tries=0;tries<Dungeon::PLACE_TRIES;tries++){
//Locate a wall we can remove
std::tuple<int,int,LOCATION> removing = findWallToRemove();
int atX = std::get<0>(removing);
int atY = std::get<1>(removing);
LOCATION dir = std::get<2>(removing);
int roomWidth;
int roomHeight;
//Make a room or corridor to go there
if (roomChance(generator) < Dungeon::ROOM_CHANCE){
roomWidth = roomSize(generator);
roomHeight = roomSize(generator);
} else if (dir == TOP_CENTER || dir == BOTTOM_CENTER){
roomWidth = corriWidth(generator);
roomHeight = corriLength(generator);
} else if (dir == MIDDLE_LEFT || dir == MIDDLE_RIGHT){
roomWidth = corriLength(generator);
roomHeight = corriWidth(generator);
} else {
break;
}
//Put that corridor there if we can
Rectangle nRoom = Rectangle(roomWidth,roomHeight);
nRoom.populate();
if (not insertSpace(nRoom,atX,atY,dir)){
setTile(atX,atY,DOOR);
}
}
//Next up we are going to try and tunnel from 1 room/coridor to another to make more links
for (int tries=0;tries<Dungeon::CONNECTION_TRIES;tries++){
std::tuple<int,int,LOCATION> removing = findWallToRemove();
int atX = std::get<0>(removing);
int atY = std::get<1>(removing);
LOCATION dir = std::get<2>(removing);
//std::cerr << atX << atY << dir << std::endl;
if (dir == MIDDLE_CENTER){
break;
}
int chgX = 0;
int chgY = 0;
switch (dir){
case TOP_CENTER:
chgY = 1;
break;
case BOTTOM_CENTER:
chgY = -1;
break;
case MIDDLE_LEFT:
chgX = 1;
break;
case MIDDLE_RIGHT:
chgX = -1;
break;
}
int dist = 0;
int x=atX+chgX;
int y=atY+chgY;
int making = true;
while (dist<Dungeon::CONNECTION_LENGTH){
if (x == 0 || x == width-1 || y == 0 || y == height-1){
making = false;
break;
}
if (getTile(x,y) == WALL){
making = (getTile(x+chgX,y+chgY) == FLOOR);
break;
}
if (getTile(x,y) == FLOOR){
making = false;
break;
}
dist++;
x+=chgX;
y+=chgY;
}
if (!making || dist == Dungeon::CONNECTION_LENGTH){
continue;
}
Rectangle conn(abs(atX-x)+2*abs(chgY)+1,abs(atY-y)+2*abs(chgX)+1);
conn.populate();
if (insertSpace(conn,atX,atY,dir)){
//This shouldn't fail but maybe we didn't check something *shrug*
continue;
}
// Now add some fecking mobs to the game.
//Put the doors in
setTile(atX,atY,DOOR);
setTile(x,y,DOOR);
}
//Select stairs up and stairs down
int upX,upY,downX,downY;
int c = 0 ;
for(int x=1;x<width-1;x++){
for(int y=1;y<height-1;y++){
if (getTile(x-1,y-1) != FLOOR || getTile(x,y-1) != FLOOR || getTile(x+1,y-1) != FLOOR ||
getTile(x-1,y ) != FLOOR || getTile(x,y ) != FLOOR || getTile(x+1,y ) != FLOOR ||
getTile(x-1,y+1) != FLOOR || getTile(x,y+1) != FLOOR || getTile(x+1,y+1) != FLOOR
){
continue;
}
c++;
switch(c){
case 1:
upX=x;
upY=y;
break;
case 2:
downX=x;
downY=y;
break;
default:
std::uniform_int_distribution<int> dist(1,c);
switch(dist(generator)){
case 1:
upX=x;
upY=y;
break;
case 2:
upX=x;
upY=y;
break;
}
break;
}
}
}
setTile(upX,upY,STAIRS_UP);
setTile(downX,downY,STAIRS_DOWN);
// Now to place the Hero class on the STAIRS_UP block as if he just came down.
}
std::tuple<int,int,LOCATION> Dungeon::findWallToRemove(){
int c = 0;
int rX;
int rY;
LOCATION rD = MIDDLE_CENTER;
for(int x=1;x<width-1;x++){
for(int y=1;y<height-1;y++){
if (getTile(x,y) != WALL) {
continue;
}
TILE_TYPES up = getTile(x,y-1);
TILE_TYPES down = getTile(x,y+1);
TILE_TYPES left = getTile(x-1,y);
TILE_TYPES right = getTile(x+1,y);
LOCATION dir = MIDDLE_CENTER;
if (up == UNSET && down == FLOOR){
dir = BOTTOM_CENTER;
} else if (down == UNSET && up == FLOOR) {
dir = TOP_CENTER;
} else if (left == UNSET && right == FLOOR) {
dir = MIDDLE_RIGHT;
} else if (right == UNSET && left == FLOOR) {
dir = MIDDLE_LEFT;
} else {
continue;
}
std::uniform_int_distribution<int> dist(0,c++);
if (!dist(generator)){
rX = x;
rY = y;
rD = dir;
}
}
}
return std::make_tuple(rX,rY,rD);
}
#include <iostream>
#include <random>
#include <tuple>
#include <libconfig.h++>
#include "../configReader.h"
#include "types.h"
#include "space.h"
//Initilize statics
libconfig::Config cfg;
int _ = loadConfig(std::string("config.cfg"),&cfg);
const std::string VERSION = fromConfig(&cfg,"version",std::string(""));
const float DungeonGenerator::ROOM_CHANCE = fromConfig(&cfg,"generator.pRoom",0.5f);
const int DungeonGenerator::CORRI_MIN_LENGTH = fromConfig(&cfg,"generator.corridor.minLength",10);
const int DungeonGenerator::CORRI_MAX_LENGTH = fromConfig(&cfg,"generator.corridor.maxLength",20);
const int DungeonGenerator::CORRI_MIN_WIDTH = fromConfig(&cfg,"generator.corridor.minWidth",3);
const int DungeonGenerator::COORI_MAX_WIDTH = fromConfig(&cfg,"generator.corridor.maxWidth",5);
const int DungeonGenerator::ROOM_MIN_SIZE = fromConfig(&cfg,"generator.room.minSize",7);
const int DungeonGenerator::ROOM_MAX_SIZE = fromConfig(&cfg,"generator.room.maxSize",19);
const int DungeonGenerator::PLACE_TRIES = fromConfig(&cfg,"generator.nPlacetries",1000);
const int DungeonGenerator::CONNECTION_TRIES = fromConfig(&cfg,"generator.connector.nTries",200);
const int DungeonGenerator::CONNECTION_LENGTH= fromConfig(&cfg,"generator.connector.maxLength",10);
DungeonGenerator::DungeonGenerator(int _width,int _height,int seed) : Dungeon(_width,_height){
generator = std::default_random_engine(seed);
}
void DungeonGenerator::populate(){
Space::populate();
//Chance we put a room instead of a corridor down
std::uniform_real_distribution<float> roomChance(0.0,1.0);
//Distribution for the length of our corridors
std::uniform_int_distribution<int> corriLength(DungeonGenerator::CORRI_MIN_LENGTH,DungeonGenerator::CORRI_MAX_LENGTH);
//Distribution for the width of our corridors
std::uniform_int_distribution<int> corriWidth(DungeonGenerator::CORRI_MIN_WIDTH,DungeonGenerator::COORI_MAX_WIDTH);
//Distribution for the size of our rooms (used for width and height sepertally)
std::uniform_int_distribution<int> roomSize(DungeonGenerator::ROOM_MIN_SIZE,DungeonGenerator::ROOM_MAX_SIZE);
/*/Chance we later knock down a joining wall for a door to increase conectivity
std::uniform_int_distribution<int> mergeDoor(1,20);*/
//Place a room in the center of the DungeonGenerator
Rectangle room = Rectangle(roomSize(generator),roomSize(generator));
room.populate();
insertSpace(room,getWidth()/2,getHeight()/2,MIDDLE_CENTER);
//Now keep trying to
for (int tries=0;tries<DungeonGenerator::PLACE_TRIES;tries++){
//Locate a wall we can remove
std::tuple<int,int,LOCATION> removing = findWallToRemove();
int atX = std::get<0>(removing);
int atY = std::get<1>(removing);
LOCATION dir = std::get<2>(removing);
int roomWidth;
int roomHeight;
//Make a room or corridor to go there
if (roomChance(generator) < DungeonGenerator::ROOM_CHANCE){
roomWidth = roomSize(generator);
roomHeight = roomSize(generator);
} else if (dir == TOP_CENTER || dir == BOTTOM_CENTER){
roomWidth = corriWidth(generator);
roomHeight = corriLength(generator);
} else if (dir == MIDDLE_LEFT || dir == MIDDLE_RIGHT){
roomWidth = corriLength(generator);
roomHeight = corriWidth(generator);
} else {
break;
}
//Put that corridor there if we can
Rectangle nRoom = Rectangle(roomWidth,roomHeight);
nRoom.populate();
if (not insertSpace(nRoom,atX,atY,dir)){
setTile(atX,atY,DOOR);
}
}
//Next up we are going to try and tunnel from 1 room/coridor to another to make more links
for (int tries=0;tries<DungeonGenerator::CONNECTION_TRIES;tries++){
std::tuple<int,int,LOCATION> removing = findWallToRemove();
int atX = std::get<0>(removing);
int atY = std::get<1>(removing);
LOCATION dir = std::get<2>(removing);
//std::cerr << atX << atY << dir << std::endl;
if (dir == MIDDLE_CENTER){
break;
}
int chgX = 0;
int chgY = 0;
switch (dir){
case TOP_CENTER:
chgY = 1;
break;
case BOTTOM_CENTER:
chgY = -1;
break;
case MIDDLE_LEFT:
chgX = 1;
break;
case MIDDLE_RIGHT:
chgX = -1;
break;
}
int dist = 0;
int x=atX+chgX;
int y=atY+chgY;
int making = true;
while (dist<DungeonGenerator::CONNECTION_LENGTH){
if (x == 0 || x == width-1 || y == 0 || y == height-1){
making = false;
break;
}
if (getTile(x,y) == WALL){
making = (getTile(x+chgX,y+chgY) == FLOOR);
break;
}
if (getTile(x,y) == FLOOR){
making = false;
break;
}
dist++;
x+=chgX;
y+=chgY;
}
if (!making || dist == DungeonGenerator::CONNECTION_LENGTH){
continue;
}
Rectangle conn(abs(atX-x)+2*abs(chgY)+1,abs(atY-y)+2*abs(chgX)+1);
conn.populate();
if (insertSpace(conn,atX,atY,dir)){
//This shouldn't fail but maybe we didn't check something *shrug*
continue;
}
// Now add some fecking mobs to the game.
//Put the doors in
setTile(atX,atY,DOOR);
setTile(x,y,DOOR);
}
//Select stairs up and stairs down
int upX,upY,downX,downY;
int c = 0 ;
for(int x=1;x<width-1;x++){
for(int y=1;y<height-1;y++){
if (getTile(x-1,y-1) != FLOOR || getTile(x,y-1) != FLOOR || getTile(x+1,y-1) != FLOOR ||
getTile(x-1,y ) != FLOOR || getTile(x,y ) != FLOOR || getTile(x+1,y ) != FLOOR ||
getTile(x-1,y+1) != FLOOR || getTile(x,y+1) != FLOOR || getTile(x+1,y+1) != FLOOR
){
continue;
}
c++;
switch(c){
case 1:
upX=x;
upY=y;
break;
case 2:
downX=x;
downY=y;
break;
default:
std::uniform_int_distribution<int> dist(1,c);
switch(dist(generator)){
case 1:
upX=x;
upY=y;
break;
case 2:
upX=x;
upY=y;
break;
}
break;
}
}
}
setTile(upX,upY,STAIRS_UP);
setTile(downX,downY,STAIRS_DOWN);
// Now to place the Hero class on the STAIRS_UP block as if he just came down.
}
std::tuple<int,int,LOCATION> DungeonGenerator::findWallToRemove(){
int c = 0;
int rX;
int rY;
LOCATION rD = MIDDLE_CENTER;
for(int x=1;x<width-1;x++){
for(int y=1;y<height-1;y++){
if (getTile(x,y) != WALL) {
continue;
}
TILE_TYPES up = getTile(x,y-1);
TILE_TYPES down = getTile(x,y+1);
TILE_TYPES left = getTile(x-1,y);
TILE_TYPES right = getTile(x+1,y);
LOCATION dir = MIDDLE_CENTER;
if (up == UNSET && down == FLOOR){
dir = BOTTOM_CENTER;
} else if (down == UNSET && up == FLOOR) {
dir = TOP_CENTER;
} else if (left == UNSET && right == FLOOR) {
dir = MIDDLE_RIGHT;
} else if (right == UNSET && left == FLOOR) {
dir = MIDDLE_LEFT;
} else {
continue;
}
std::uniform_int_distribution<int> dist(0,c++);
if (!dist(generator)){
rX = x;
rY = y;
rD = dir;
}
}
}
return std::make_tuple(rX,rY,rD);
}
......@@ -5,7 +5,7 @@
#include <streambuf>
#include <iostream>
Loader::Loader(int w,int h,std::string _filename): Dungeon(w,h,0){
Loader::Loader(int w,int h,std::string _filename): Dungeon(w,h){
filename = _filename.c_str();
}
......
......@@ -33,15 +33,20 @@ protected:
class Dungeon : public Space {
public:
Dungeon(int,int,int);
using Space::Space;
std::tuple<int,int> upStairPosition();
std::tuple<int,int> downStairPosition();
}
class DungeonGenerator : public Dungeon {
public:
DungeonGenerator(int,int,int);
void populate();
protected:
std::default_random_engine generator;
std::tuple<int,int,LOCATION> findWallToRemove();
public:
std::tuple<int,int> upStairPosition();
std::tuple<int,int> downStairPosition();
const static float ROOM_CHANCE;
const static int CORRI_MIN_LENGTH;
const static int CORRI_MAX_LENGTH;
......
  • I am going to start the SDL rendering today. This is a big step towards creating a few new test cases which would be useful for the CI. I would suggest splitting these new abstracted classes into new folders so it matches with the current layout in src/entities

  • Yep I will do, I also wonder if we need to swap most of the properties out to a getter/setter model

  • actually that would make sense. But the getter setter model wouldn't work with const variables. Would be good to actually get these values set in the constructor for the generator

Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment