05 Oct, 2009, Asylumius wrote in the 1st comment:
Votes: 0
I want to implement random dungeons (think Diablo) into my ROM derivative MUD. What I'm looking for are some examples of algorithms that might already exist for creating maps that are random but still make a little bit of sense at the same time.

Anyone done this before and/or know of some resources that might be useful?
05 Oct, 2009, David Haley wrote in the 2nd comment:
Votes: 0
Procedural generation is the term I'd search for. I don't really have specific pointers to algorithms, though, but I am sure that there are very many out there with games like Nethack, Dungeon Hack, Diablo, etc.
05 Oct, 2009, Omega wrote in the 3rd comment:
Votes: 0
Since you wanted it….

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// simple maze-generation!
// Its really simple, if a room is flagged as a 1, it is enterable, if it is beside another room flagged with a 1
// that too becomes linked for movement. If the room is a 2, it checks for up/down, in-which case, if it finds a 1 or a 2
// above or below it in Z value's, then movement up or down is available, (or both)
// Maze stems off nicely, if it reaches a point where it maxes out failed attempts, it moves to a new random location
// and starts generating another map from there. In hopes that they infact-meet up somewhere.
// if it fails 10 times in a row, it aborts the generation where it is, logs it, and lets the maze work.
// if the maze at this point in time is completely broken, the player who auto-genned it will know, as it wont' be
// very usable.
// if the maze gen fails, broken becomes true, and it notifies the player that with it being broken, they *CANNOT*
// complete the maze, and will have to re-enter, and hope that the next one does not fail.
//
// The generation here is quick, and painful. We want to ensure that the map is carefully designed as-to not be too
// generic. Which is why the start-z/y/x exist, making each map alittle different from the last, based entirely off of
// how it selects the start-point, and the random direction it chooses to go.
//
// Memory intensity: This system is very memory intensive, massive maps, being created on the fly, constantly. This will
// need to have some-level of management, such as you cannot create a maze if you've recently been in one (like 30 minutes)
// stopping players from entering/exiting until they get the 'type' of map they want. Only caveat for this is if the maze
// is marked as broken. In that case, don't flag them.
//
// On topic of memory intensity. To avoid massive cpu spiking, it would be recommended that the cooldown between mazes
// per player be a min of half an hour.
//
// *Note: If flagging a player, if they quit the maze before completion, or if they complete it, the wait time should
// always be the same. Infact, quitters should most likely be lagged longer, just because they were part of it.
//
// If an entire group quits, at that point in time, the maze is deleted from memory. Upon completion of the maze
// all players will be sent back to the summoning stone for the maze, including the person who entered the maze itself.
// summon stone should be approx 15/20 x/y coords away from a given maze.
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Lexi::List<maze_data *>maze_list; // keep track of our globally used mazes!

#define ROOM_VNUM_MAZE 4 // Like wilderness. Makes life easier.

class maze_data {
public:
maze_data() { sz = 0; sy = 0; sx = 0; actual_size = 0; mx = 0; broken = false;
level = 0; treasure = 0; repop = 0; rare_drop = 0; elite = false;
monsters_left = 0; bosses_left = 0; started = false;
puzzle_start = 0; puzzles_left = 0;
}
~maze_data() { free_string(name);}
private:
vnum_t repop;
vnum_t rare_drop;
short maze[30][250][250];
short puzzle[30][250][250]; // puzzles to go into the next room!
short summon_x; // the mazes X summon-stone location
short summon_y; // the mazes Y summon-stone location
short sz; // Start Z loc
short sx; // Start X loc
short sy; // Start Y loc
short actual_size; // actual size
short mx; // max size (should be on-par with actual-size)
short level;
short treasure;
short monsters_left; // how many monsters are left (after each kill)
short bosses_left; // how many bosses are left!

short mob_start; // original mob counts
short boss_Start; // original boss counts

short puzzle_start; // how many puzzles will be in the maze
short puzzles_left; // how many puzzles are left!

short players_in_maze; // how many players are in the maze!

const char *maze_name; // name of the maze/dungeon your in!
bool elite;
bool broken;
bool maze_started;
public:
short get_start_x() { return sx; }
short get_start_y() { return sy; }
short get_start_z() { return sz; }

short get_summon_x() { return summon_x; }
short get_summon_y() { return summon_y; }

short get_players() { return players_in_maze; }
short get_puzzles() { return puzzles_left; }
short get_maze(short z, short x, short y) { return maze[z][x][y]; } // (0-1-2 (hopefully it doesn't exceed the max))
short get_size() { return actual_size; }
short get_max() { return mx; }
short get_level() {return level; }
short get_monsters() { return monsters_left; }
short get_bosses() { return bosses_left; }
const char *get_name() { return maze_name; }
bool get_broken() { return broken; }
bool started() { return maze_started; }
bool get_elite() { return elite; }
bool get_broken() { return broken; }
void set_start() { maze_started = true; }
void kill_mob(CHAR_DATA *grp) { // done on raw-kill
if(monsters_left > 0) {
monsters_left–;
if(monsters_left == 0) {
// insert leaderboard here (for ALL group members)
if(elite) {
// secondary leaderboard for elite monsters
}
}
}
return;
}
void kill_boss(CHAR_DATA *grp) { // done on raw-kill
if(bosses_left > 0) {
bosses_left–;
if(bosses_left == 0) {
// clearing dungeons = hardcore.
// insert leaderboard here (for ALL group members)
if(elite) {
// secondary leaderboard for elite boss's
}
}
}
return;
}

/////////////////////////////////////////////////////////////////////////////////////////
// Generates a puzzle so that players cannot pass until they have figure'd it out.
// it can be anything from a saying, phrase, special command issue'd.
// the puzzles are selected from lua scripts.
//
// lua-progs that are puzzles, their description is used not to describe its functionality
// but should infact, be a puzzle, riddle, something for the players to figure out.
//
// puzzle's are marked as staff 3 in the lua.
//
// Puzzles are what make the the maze's worth-while. Solving puzzles helps progress through
// the map, aswell as get bonus's, leaderboard titles. And all sorts of other fun-ness.
// Note, after-awhile, the puzzles will be, well, easy, as they will start repeating, which
// means we will have to write hundreds of puzzles, if not thousands to make this worth-while.
// but hey, thats no worries, as it is *WELL* worth it.
//
void generate_puzzle(short z, short y, short z) {
// puzzle will be the vnum of the puzzle chosen.

}

/////////////////////////////////////////////////////////////////////////////////////////
// Spawn a mobile at position z/x/y, roughly mob_level, from repop_vnum, if marked elite
// we increase stats.
void spawn_mobile(short z, short x, short y, short mob_level, vnum_t repop_vnum, bool elite) {
// 55 % chance of spawning a mobile.
// with the size of mazes, (being up to 530 rooms) it seems to reason that
// nomatter what, mobs are going to spawn. And be quite nasty!
// This should allow us to cap out on our mobs/bosses easily enough, most likely
// with room-to-spare, but thats why we let the mobs wander!
if(number_percent() > 55)
return;

if(mob_start == monsters_left)
return;

WILD_MOB *wmob = find_wild_pattern(repop_vnum);
if(!wmob) return;

int spawn = number_range(0,10);
MOB_INDEX_DATA *mob = get_mob_index(wmob->vnum_list[spawn]);
if(!mob) return;

CHAR_DATA *instanced = create_mobile(mob);
// move to the coordinates
instanced->cord[CORD_Z] = z;
instanced->cord[CORD_X] = x;
instanced->cord[CORD_Y] = y;

////////////////////////////////////////////////////////////////////////
// Ensure that the instanced mobs will group up against the enemies.
instanced->group = (repop_vnum+1); // should ensure their groupie-ness!


char_to_room(get_room_index(ROOM_VNUM_MAZE), instanced); // move into the maze

// set the level
instanced->level = number_range(mob_level-5, mob_level+5);
if(instanced->level < 1) instanced->level = 1;

// We want *ALL* of the mobs to be extremely hostile, so we go aggressive for good measure.
if(!IS_SET(instanced->act, ACT_AGGRESSIVE)) SET_BIT(instanced->act, ACT_AGGRESSIVE);

// Let the mobiles wander, this should help spread out the beasties nicely
// especially if they all spawn at the very start of the maze, and not at the end of its
// generation. Eventually, the mobs should walk all over the place :)
if(IS_SET(instanced->act, ACT_SENTINEL)) REMOVE_BIT(instanced->act, ACT_SENTINEL);

// set our boss's.
if(instanced->level == mob_level+5 && boss_left != boss_start) {
if(!IS_SET(instanced->act, ACT_BOSS)) SET_BIT(instanced->act, ACT_BOSS);
bosses_left++;
}

// increment our monsters.
monsters_left++;

// this fixes the stats with the new level and if a boss, with the new boss flag.
correct_mobile(instanced);

// equip the monsters with random gear so that it is worth while to kill these
// creatures. *Note: If the mobs have a rare drops on them, this gets even
// more interesting, as on kill, they risk creating a nice random object.
maze_output(instanced);
return;
}

/////////////////////////////////////////////////////////////////////////////////////////
// This will populate the maze with mobs from the repop_vnum, which loads the spawns from
// wild_mob list, normally reserved for patterns, is now being used here to help populate
// the random maze.
// mob_level assigns the approximate level of the mobs
// repop_vnum pulls the respawned mobs from the pattern maps.
// treasure-level is the approximate worth of the treasure (1-10, 10 being best)
// rare_drop_vnum is the vnum of the object that will be the random-drop on the boss
// mob in the dungeon.
// elite means the players selected elite mobs when they summoned in their friends.
// this means mobs are even stronger, and smarter.
// aswell as improving the treasure (even if it was a 10, they get better)
/////////////////////////////////////////////////////////////////////////////////////////
// Maze generation as easy as maze->generate_maze(20,100,88, 1800, 325, 88, 10, 7, 25, 3, 5, 88, true, "Jaspers Dungeon of Territorial Doom");
// we randomly generate the maze!
// this is fun, smart, and overall, exciting.
void generate_maze(short z, short x, short y, short sum_x, short sum_y, short mob_count, short boss_count, puzzle_count, short mob_level, vnum_t repop_vnum, short treasure_level, vnum_t rare_drop_vnum, bool elite, const char *name) {
int xy, yy, zy;
int max_size = 0;

spawn_mobile(current_z, current_x, current_y, mob_level, repop_vnum, elite);

////////////////////////////////////////////////////////////////////////
// x/y/z variables just allocate our max-size(in total) they don't manage
// how far east/west/up/down/north/south things will go.
// those are maxed anyways. However, we always ensure we are more then
// taken-care of with our overall layout.
// ensure our maximums are not exceeded
if(x > 250) x = 250;
if(y > 250) y = 250;
if(z > 30) z = 30;
maze_name = str_dup(name);

mob_start = mob_count;
boss_start = boss_count;
puzzle_start = puzzle_count;

////////////////////////////////////////////////////////////////////////
// zero the map first. (safe practice before having fun)
for(xy = 0; xy < x; xy++ ) {
for(yy = 0; yy < y; yy++ ) {
for(zy = 0; zy < z; zy++ ) {
maze[x][y][z] = 0;
}
}
}
int counter = 0;
int start_room_x, start_room_y, start_room_z;


summon_x = sum_x;
summon_y = sum_y;

max_size == (x+y)+z; // z feels left out ;) (making a joke with code)
mx = max_size; // set our 'supposed' to be size.
start_room_x = number_range(0,x);
start_room_y = number_range(0,y);
start_room_z = number_range(0,z);

////////////////////////////////////////////////////////////////////////
// make that room in the maze exist!
maze[start_room_z][start_room_x][start_room_y] = 1;

// our start-room in the maze! (MUAHAHAHAHAHA)
sz = start_room_z;
sx = start_room_x;
sy = start_room_y;

// so we know where we are in the map!
int current_x = start_room_x;
int current_y = start_room_y;
int current_z = start_room_z;

int attempt = 0;
int attempt_maxed 0;

// Lets make the maze!
for(;;) {

int direction = number_range(0,6);
bool failed = false;
short jump_back = false;

// map is done! Reached our max-size! Time to populate! (if we managed to breach it
// with the jumpback code, then we say whatever, and carry on peacefully)
if(count >= max_size) break;

if(attempt == 6) {
// failed 6 times, that means all exits around the person, are filled.
// and it wouldn't progress anyfurther. (or it just had bad luck
// randomizing the same number, but, to prevent a potential lockup
// we move the current_x/y/z locations to a random spot, and hope
// it manages to link up with the rest. Else wise, the dungeon
// could be pooched!
attempt = 0; // reset attempt count!
attempt_maxed++; // maxed our attempt 6 times
// now we count that.
// if we have a massive glitch
// we'd like to know!

if(attempt_max == 10) // we maxed our attempts 10 times
{ // now we do some fancy logging when this happens.
broken = true; // yup, its broken.
log_string("[MAZE GEN] %s reached max generation attempts, with a count of %d of %d", __PRETTY_FUNCTION__, count, max_size);
break; // we don't want to lockup
} // so we break the generation
// and hope that all is well.
current_x = number_range(0,x);
current_y = number_range(0,y);
current_z = number_range(0,z);
}

// here is where we get…. creepy!
jump_back = number_range(0,11);

///////////////////////////////////////////////////////////////////////////////////////////////////////
// Jumping back, this got the name because it creates stretch's off of the main-path and then jumps
// back to the path, and carries on with the normal map generation. Sick little plan.
// This will be interesting.
// *Note: east/west/north/south directions care if a room is already-set, and won't move into it.
// however, up/down don't care, and will move into it, and change the exits to 2, so it knows it can
// go up or down.
// the bonus stretches can head off up to 12 rooms (including the original off-shoot)
// the extra stretches are a percent chance to happen, they do not always occur when jump-back takes
// place, this should help create little balls of extra-joy.
///////////////////////////////////////////////////////////////////////////////////////////////////////
if(jump_back == 4) { // why 4? Why not!
short where = number_range(0,3);
short max_p = number_range(3,11);
// change in x/y/z location.
switch(where) {
// NORTH/SOUTH MANIPULATION
default:
case 0:
if(number_range(0,1) == 1) {
current_x++;
if(current_x != 250) {
if(maze[current_z][current_x][current_y] == 0) {
maze[current_z][current_x][current_y] = 1;
count++;
if(number_percent() > number_range(78,100))
for(int p = 0; p < max_p; p++) {
if(current_x+p < 250) {
if(maze[current_z][current_x+p][current_y] == 0) {
maze[current_z][current_x+p][current_y] = 1;
count++;
spawn_mobile(current_z, current_x+p, current_y, mob_level, repop_vnum, elite);
}
}
}

}
current_x–; // go back to our original x location and continue from there!
} else {
current_x++;
if(current_x != 250) {
if(maze[current_z][current_x][current_y] == 0) {
maze[current_z][current_x][current_y] = 1;
count++;
if(number_percent() > number_range(78,100))
for(int p = 0; p < max_p; p++) {
if(current_x-p > -1) {
if(maze[current_z][current_x-p][current_y] == 0) {
maze[current_z][current_x-p][current_y] = 1;
count++;
spawn_mobile(current_z, current_x-p, current_y, mob_level, repop_vnum, elite);
}
}
}

}
current_x–; // go back to our original x location and continue from there!
}
// EAST/WEST CHANGING
case 1:
if(number_range(0,1) == 1) {
current_y++;
if(current_y != 250) {
if(maze[current_z][current_x][current_y] == 0) {
maze[current_z][current_x][current_y] = 1;
count++;
if(number_percent() > number_range(78,100))
for(int p = 0; p < 5; max_p++) {
if(current_y+p < 250) {
if(maze[current_z][current_x][current_y+p] == 0) {
maze[current_z][current_x][current_y+p] = 1;
count++;
spawn_mobile(current_z, current_x, current_y+p, mob_level, repop_vnum, elite);
}
}
}

}
current_y–; // go back to our original x location and continue from there!
} else {
current_y++;
if(current_y != 250) {
if(maze[current_z][current_x][current_y] == 0) {
maze[current_z][current_x][current_y] = 1;
count++;
if(number_percent() > number_range(78,100))
for(int p = 0; p < max_p; p++) {
if(current_y-p > -1) {
if(maze[current_z][current_x][current_y-p] == 0) {
maze[current_z][current_x][current_y-p] = 1;
count++;
spawn_mobile(current_z, current_x, current_y-p, mob_level, repop_vnum, elite);
}
}
}

}
current_y–; // go back to our original x location and continue from there!
}
///////////////////////////////////////////////////////////////////////////////////////////
// UP/DOWN MANIPULATION.. THIS *IS* VITAL AND SCARY AT THE SAME TIME!
case 2:
if(number_range(0,1) == 1) {
current_z++;
if(current_z != 30) {
maze[current_z][current_x][current_y] = 2;
count++;
if(number_percent() > number_range(78,100))
for(int p = 0; p < max_p; p++) {
if(current_x+p < 30) {
maze[current_z+p][current_x][current_y] = 2;
count++;
spawn_mobile(current_z+p, current_x, current_y, mob_level, repop_vnum, elite);
}
}
current_z–; // go back to our original x location and continue from there!
} else {
current_z++;
if(current_z != 30) {
maze[current_z][current_x][current_y] = 2;
count++;
if(number_percent() > number_range(78,100))
for(int p = 0; p < max_p; p++) {
if(current_z-1p > -1) {
maze[current_z-p][current_x][current_y] = 2;
count++;
spawn_mobile(current_z+p, current_x, current_y, mob_level, repop_vnum, elite);
}

}
current_z–; // go back to our original x location and continue from there!
}
break;
}
}

///////////////////////////////////////////////////////////////////////////////////////////////////////
// Continuing the insanity here:
// Our now official goal, if we did, or did not stem off, is to now change our direction, and parse
// through and hopefully generate a really cool new design/layout for the overall map
// because of the treeing system above, we hope to see many sub-paths carry off from the original
// path.. Perfect to hide evil little minions in them.
///////////////////////////////////////////////////////////////////////////////////////////////////////
switch(direction) {
default:
case DIR_NORTH:
// cannot go any-further north!
if(current_x+1 == 250){
failed = true;
break;
}
if(maze[current_x][current_x+1][current_y] == 0) {
current_x++;
maze[current_z][current_x][current_y] = 1;
spawn_mobile(current_z, current_x, current_y, mob_level, repop_vnum, elite);
// here we go with a chance to make an extra direction (helps space-out the map)
if(current_x+1 == 250) break; // not failed because we did increment.
if(number_range(0,5) == number_range(0,5)) {
if(maze[current_z][current_x+1][current_y] == 0) {
current_x++;
count++; // so we keep our counts properly.
maze[current_z][current_x][current_y] = 1;
}
}
} else { failed = true; }
break;
case DIR_SOUTH:
// cannot go any-further north!
if(current_x-1 == -1){
failed = true;
break;
}
if(maze[current_x][current_x-1][current_y] == 0) {
current_x–;
maze[current_z][current_x][current_y] = 1;
spawn_mobile(current_z, current_x, current_y, mob_level, repop_vnum, elite);
// here we go with a chance to make an extra direction (helps space-out the map)
if(current_x-1 == -1) break; // not failed because we did increment.
if(number_range(0,5) == number_range(0,5)) {
if(maze[current_z][current_x-1][current_y] == 0) {
current_x–;
count++; // so we keep our counts properly.
maze[current_z][current_x][current_y] = 1;
}
}
} else { failed = true; }
break;
case DIR_EAST:
// cannot go any-further north!
if(current_y+1 == 250){
failed = true;
break;
}
if(maze[current_z][current_x][current_y+1] == 0) {
current_y++;
maze[current_z][current_x][current_y] = 1;
spawn_mobile(current_z, current_x, current_y, mob_level, repop_vnum, elite);
// here we go with a chance to make an extra direction (helps space-out the map)
if(current_y+1 == 250) break; // not failed because we did increment.
if(number_range(0,5) == number_range(0,5)) {
if(maze[current_z][current_x][current_y+1] == 0) {
current_y++;
count++; // so we keep our counts properly.
maze[current_z][current_x][current_y] = 1;
}
}
} else { failed = true; }
break;
case DIR_WEST:
// cannot go any-further north!
if(current_y-1 == -1){
failed = true;
break;
}
if(maze[current_z][current_x][current_y-1] == 0) {
current_y++;
maze[current_z][current_x][current_y] = 1;
spawn_mobile(current_z, current_x, current_y, mob_level, repop_vnum, elite);
// here we go with a chance to make an extra direction (helps space-out the map)
if(current_y-1 == 250) break; // not failed because we did increment.
if(number_range(0,5) == number_range(0,5)) {
if(maze[current_z][current_x][current_y-1] == 0) {
current_y–;
count++; // so we keep our counts properly.
maze[current_z][current_x][current_y] = 1;
}
}
} else { failed = true; }
break;
case DIR_UP:
// cannot go any-further north!
if(current_z+1 == 30){
failed = true;
break;
}

// up/down don't care if the value is 0, it creates a up/down link anyways!
// helps create a random-link.
current_z++;
spawn_mobile(current_z, current_x, current_y, mob_level, repop_vnum, elite);
maze[current_z][current_x][current_y] = 2; // 2 means it links up!
// here we go with a chance to make an extra direction (helps space-out the map)
if(current_z+1 == 30) break; // not failed because we did increment.
if(number_range(0,5) == number_range(0,5)) {
current_z++;
count++; // so we keep our counts properly.
maze[current_z][current_x][current_y] = 2;
}
break;
case DIR_DOWN:
// cannot go any-further north!
if(current_z-1 == -1){
failed = true;
break;
}

// up/down don't care if the value is 0, it creates a up/down link anyways!
// helps create a random-link.
current_z–;
spawn_mobile(current_z, current_x, current_y, mob_level, repop_vnum, elite);
maze[current_z][current_x][current_y] = 2; // 2 means it links up or down (or both)
// here we go with a chance to make an extra direction (helps space-out the map)
if(current_z+1 == 30) break; // not failed because we did increment.
if(number_range(0,5) == number_range(0,5)) {
current_z–;
count++; // so we keep our counts properly.
maze[current_z][current_x][current_y] = 2;
}
break;
}

if(!failed)
count++;
else
attempt++; // saves us from infinate loop!

}

actual_size = count;
maze_list.push_back(this);
}
};

////////////////////////////////////////////////////////////////////////////////////////
// display some minor information about the maze in question.
COMMAND(do_maze) {
if(ch->in_maze == NULL) {
ch->Sendf("But your not in a maze!\n\r");
return;
}

maze_data *maze = ch->in_maze;

if(NullString(argument)) {
ch->Sendf("Syntax: maze <start|summon|quit|info>\n\r");
return;
}

if(!str_cmp(argument, "summon")) {
if(maze->started()) {
ch->Sendf("You cannot summon once the maze has started!\n\r");
return;
}

// pull the groupies to the maze from the summoning stone.
FOREACH(Lexi::List<CHAR_DATA *ch>, player_list, player_iter) {
CHAR_DATA *groupie = (*player_iter);
if(is_same_group(ch, groupie) && groupie->x == maze->summon_x && groupie->y == maze->summon_y) {
if(groupie->recent_maze != 0) continue;
char_from_room(groupie);
char_to_room(get_room_index(4), ch);
groupie->cord[CORD_X] = ch->cord[CORD_X];
groupie->cord[CORD_Y] = ch->cord[CORD_Y];
groupie->cord[CORD_Z] = ch->cord[CORD_Z];
groupie->in_maze = ch->in_maze;
do_look(groupie, "auto");
in_maze->get_players()++; // increment the players (crazy technique here)
}
}

}

if(!str_cmp(argument, "start")) {
if(maze->started()) {
ch->Sendf("The maze has already been started! Hurry up!\n\r");
return;
}
maze->set_start();
do_function(ch, &do_gtell, "Maze started up! Prepare yourselves %s!", ch->rebel ? "Comrades" : "my good chums");
return;
}

// just know alittle about your dungeon of ultimate doom!
if(!str_cmp(argument, "info")) {
ch->Sendf("\n\r+———————[ %s%s ]———————+\n\r", maze->get_broken ? "[Broken] " : "", maze->get_name());
ch->Sendf(" [ %s ] [Level: %d]", maze->get_elite() ? "Elite" : "Normal", maze->get_level());
ch->Sendf(" [Bosses Left: %d]", maze->get_bosses());
ch->Sendf(" [Monsters Left: %d]", maze->get_monsters());
ch->Sendf(" [Puzzles Left: %d]", maze->get_puzzles());
return;
}

if(!str_cmp(argument, "quit")) {
do_function(ch, &do_gtell, "Sorry %s, but I have to bail! %s.", ch->rebel ? "my friends" : "guys", ch->rebel ? "Cheers" : "Good-bye");
// is it really that simple? Yes, because we aren't modifying the x/y locations, it checks to see if your in_maze, if so.
// it reads and does everything differently! This will restore you back to the worldmap. where you entered (summoning stone)
char_from_room(ch);
char_to_room(get_room_index(3), ch); // wilderness!

// send them back to the summon stone!
ch->x = in_maze->get_summon_x();
ch->y = in_maze->get_summon_y();
ch->in_maze = NULL;
ch->recent_maze = 35; // 35 ticks until you can enter a new one
// you cannot be summoned in unless you have 0
return;
}

ch->Sendf("What option is that!\n\r");
return;
}

////////////////////////////////////////////////////////////////////////////////////////
// Just display what mazes are open. Since they are linked from the world-map
// and are instanced *PER* group, we should see plenty of these open.
COMMAND(cmd_mazelist) {
BUFFER *output;
int count = 0;
output = new_buf();
BufPrintf(output, "Mazes currently in-use!\n\r");
FOREACH(Lexi::List<maze_data *>, maze_list, maze_iter) {
maze_data *maze;
BufPrintf(output, "| %3d: | Start: z%5d x%5d y%5d | Max size: %5d | Actual Size: %5d | Broken: %5s|\n\r", count, maze->get_start_z(), maze->get_start_x(), maze->get_start_y(), maze->get_max(), maze->get_broken() ? "Yes" : "No");
count++;
}

BufPrintf(output, "There are currently %d mazes in-use!\n\r", count);
page_to_char(buf_string(output), ch);
free_buf(output);
return;
}


This is a snippet I wrote, It won't plug straight in, since it does have some code that is direct from my mud. However, with some modification, it should work. There may also be syntax errors in this, because I wrote it at work and emailed it to myself at home, and later installed in my mud. Since I am at work right now, I cannot give you the one that does not have any syntax issue's.


Anyways, you'll have to fill in the blanks for anything you cannot figure out/understand. I'll try and lend a hand to anyone who wishes to install this in their mud, when I have the time.


Oh yeah, one last thing, this was designed to create instanced maps off of my worldmap. Thus why the maze command for summon exists, only 1 person could enter at a time, and then they would have to use maze summon, to summon their friends from the summoning stone.
05 Oct, 2009, Scandum wrote in the 4th comment:
Votes: 0
One option that works well is adding a routine that randomly rotates the room exits of an existing area.

Edit: Don't know of a freely available implementation of this. First thing you'd do would be picking a random rotation and giving all rooms that rotation, like rotating all exits clockwise. Next you go from the first to the last room looking for valid exit swaps that can be carried out, this is most likely to succeed on two exit rooms.

This method is kind of how Diablo does it, it randomizes existing content rather than creating from scratch.
05 Oct, 2009, David Haley wrote in the 5th comment:
Votes: 0
Diablo doesn't just rotate room exits. It has templates for rooms and other "dungeon components" and it generates a random new layout. It doesn't necessarily use the same pieces in every instance of a randomized dungeon.
05 Oct, 2009, Tyche wrote in the 6th comment:
Votes: 0
I'm using a variation of one of these..
http://www.google.com/search?q=dungeon+g...
I don't remember which one has the original source code… but James Britt was the author and it's in C++.
Mine is a Ruby version of it… minus the D&D stuff.

The code first constructs a maze, then lays out randomly sized and shaped rooms on top of the grid, then destructs stray and dead end passages, and finally inserts doors.

The final product isn't necessarily suitable for a Diku style room-exit graph.
0.0/6