/* A C implementation of some dungeon generator, or at least an attempt. Based mainly on http://journal.stuffwithstuff.com/2014/12/21/rooms-and-mazes (C)2019 Marisa Kirisame, UnSX Team. Released under the MIT license. */ #include #include #include #include #include // map dimensions // should be odd, otherwise you'll get some "padding" on the bottom and right #define MAP_WIDTH 32 #define MAP_HEIGHT 32 // dimensions of the display window #define WIN_WIDTH (MAP_WIDTH*16) #define WIN_HEIGHT (MAP_HEIGHT*16) // size of each cell in pixels when rendered #define CW (WIN_WIDTH/MAP_WIDTH) #define CH (WIN_HEIGHT/MAP_HEIGHT) // delay after each "draw map" operation // helps visualize the generation better #define GENDELAY 10 // room generator parameters // sizes must be odd // more tries means more dense room placement // I wonder what the probability of there being only one room could be #define ROOM_MIN 3 #define ROOM_MAX 7 #define ROOM_TRIES 100 // tile types #define TILE_WALL 0 #define TILE_AIR 1 #define TILE_DOOR 2 // probability of mazes being more squiggly #define WINDING_PCT 0.35 // probability of additional doors into a region appearing #define EXTDOOR_PCT 0.15 // probability of doors connecting back to the same region #define SELFDOOR_PCT 0.25 // the map itself uint8_t map[MAP_WIDTH][MAP_HEIGHT] = {{TILE_WALL}}; // what region each cell is part of // 0 = no region int region[MAP_WIDTH][MAP_HEIGHT] = {{0}}; // secondary region, for grouping rooms int sregion[MAP_WIDTH][MAP_HEIGHT] = {{0}}; SDL_Surface *ws; SDL_Window *w; int active = 1; // a 2d vector typedef struct { int x, y; } vec2_t; // a room typedef struct { int x1, y1, x2, y2; } room_t; // a connection between two regions typedef struct { int x, y, r[2]; } conn_t; // do these rooms touch each other? int room_intersect( room_t* a, room_t* b ) { return ((a->x1 <= b->x2) && (a->x2 >= b->x1) && (a->y1 <= b->y2) && (a->y2 >= b->y1)); } vec2_t dirs[8] = { { 0,-1}, // north { 0, 1}, // south {-1, 0}, // west { 1, 0}, // east {-1,-1}, // northwest { 1,-1}, // northeast {-1, 1}, // southwest { 1, 1}, // southeast }; int current_region = 0; int current_sregion = 0; // RNG stuff double FRandom( double a, double b ) { return a + (rand()/(RAND_MAX/(b-a))); } int Random( int a, int b ) { return FRandom(a,b); } // draw the map surface onto the window surface // black for walls, white for air void drawmap( void ) { uint32_t *px = ws->pixels; uint32_t pal[3] = { SDL_MapRGB(ws->format,16,16,16), SDL_MapRGB(ws->format,192,192,192), SDL_MapRGB(ws->format,16,255,16), }; for ( int y=0; yy1; jy2; j++ ) for ( int i=r->x1; ix2; i++ ) carve(i,j,TILE_AIR); // add other stuff here if you want } // place rooms void add_rooms() { int rw, rh, rx, ry; room_t *rooms = malloc(0); int nrooms = 0; for ( int i=0; i= MAP_WIDTH) || (y < 0) || (y >= MAP_HEIGHT) ) return 0; x = sx+dirs[dir].x*2; y = sy+dirs[dir].y*2; return (map[x][y] == TILE_WALL); } // make a maze void grow_maze( int sx, int sy ) { int lastdir = -1; current_region++; carve(sx,sy,TILE_AIR); vec2_t *cells = malloc(sizeof(vec2_t)); int ncells = 1; cells[0].x = sx; cells[0].y = sy; while ( ncells > 0 ) { vec2_t *ccell = &cells[ncells-1]; int carvedirs[4] = {0}; int cancarve = 0; for ( int i=0; i<4; i++ ) { if ( can_carve(ccell->x,ccell->y,i) ) { carvedirs[i] = 1; cancarve = 1; } } if ( cancarve ) { int dir; if ( (lastdir != -1) && carvedirs[lastdir] && (FRandom(0,1) > WINDING_PCT) ) dir = lastdir; else { // cheap-arse random picking int wdir[4]; int ndirs = 0; int j = 0; for ( int i=0; i<4; i++ ) { if ( !carvedirs[i] ) continue; wdir[j++] = i; ndirs++; } // shuffle for ( int i=0; ix+dirs[dir].x; int y = ccell->y+dirs[dir].y; carve(x,y,TILE_AIR); x += dirs[dir].x; y += dirs[dir].y; carve(x,y,TILE_AIR); ncells++; cells = realloc(cells,sizeof(vec2_t)*ncells); cells[ncells-1].x = x; cells[ncells-1].y = y; lastdir = dir; } else { ncells--; cells = realloc(cells,sizeof(vec2_t)*ncells); lastdir = -1; } } free(cells); } // this generates doors // it's kind of convoluted // but not as much as the original implementation lol void connect_regions() { int creg[MAP_WIDTH][MAP_HEIGHT][5] = {{{0}}}; for ( int j=1; j SELFDOOR_PCT) ) continue; nconn++; conn = realloc(conn,nconn*sizeof(conn_t)); memcpy(conn+nconn-1,&cconn,sizeof(conn_t)); } // iterate through all regions and place doors // make sure there is at least ONE door between two regions // but allow a random chance for more for ( int i=1; i EXTDOOR_PCT ) continue; // open an extra door carve(conn[connl[j]].x,conn[connl[j]].y,TILE_DOOR); } free(connl); } // clean up any overlapping doors // must be done on a second pass for ( int i=0; i 0 ); } // get rid of groups of rooms that are "isolated" from the larger part of the // dungeon int spread_sregion( int x, int y ) { // non-recursive flood fill using the same queue system as the maze // generator int ssz = 1; int ncells = 1; vec2_t *cells = malloc(sizeof(vec2_t)); sregion[x][y] = current_sregion; cells[0].x = x; cells[0].y = y; while ( ncells > 0 ) { x = cells[ncells-1].x; y = cells[ncells-1].y; int dir = -1; for ( int i=0; i<4; i++ ) { if ( (map[x+dirs[i].x][y+dirs[i].y] == TILE_WALL) || sregion[x+dirs[i].x][y+dirs[i].y] ) continue; dir = i; break; } if ( dir != -1 ) { ncells++; cells = realloc(cells,sizeof(vec2_t)*ncells); sregion[x+dirs[dir].x][y+dirs[dir].y] = current_sregion; cells[ncells-1].x = x+dirs[dir].x; cells[ncells-1].y = y+dirs[dir].y; ssz++; } else { ncells--; cells = realloc(cells,sizeof(vec2_t)*ncells); } } free(cells); return ssz; } void clean_isolated() { int *ssizes = malloc(0); for ( int j=1; j ssizes[largest] ) largest = i; for ( int j=1; j 1 ) sscanf(argv[1],"%u",&seed); else seed = time(0); srand(seed); printf("seed: %u\n",seed); SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_EVENTS); w = SDL_CreateWindow("Dungeon",SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,WIN_WIDTH,WIN_HEIGHT, SDL_WINDOW_SHOWN); ws = SDL_GetWindowSurface(w); dungeon_make(); while ( active ) drawmap(); return 0; }