Code archives/Miscellaneous/Simple Dungeon Generator
This code has been declared by its author to be Public Domain code.
Download source code
| This code should produce 2d dungeons reasonably fast. I would like credit if you use this code. Please feel free to explore different parameters. |
'......................................................................................................... ' Ryan Burnside Dungeon Engine Release 1.0 ' ***Please credit "Ryan Burnside" if possible.*** ' This dungeon generator is meant to do just that, nothing more and nothing less ' a dungeon holds an array and rooms that overlay spaces in the array ' the dungeon array is simply called "array" ' this array holds values for walls floor and will eventually hold special data for tiles ' it is up to the programmer to defing meaningful conventions for special values ' rooms can be seeded with special values using the "add_value" command ' it is important to note that the add_value command will overwrite values if the random room spot is taken ' because of this you will want to make your stairs last and items and monster values first ' once an item is obtained you will want to set the square back to 0 in the dungeon array '......................................................................................................... Strict SeedRnd(MilliSecs()) ' A wrapper object for an array and holder object for room instances Type dungeon Field array:Byte[,] , rooms:TList = New TList, rooms_maxed:Byte = False, room_count:Int = 0 Method add_room(min_width:Int, min_height:Int, max_width:Int, max_height:Int) Local array_width:Int = array.dimensions()[0] Local array_height:Int = array.dimensions()[1] ' first ensure that the room is not larger than the array If max_width > array_width max_width = array_width End If If max_height > array_height max_height = array_height End If ' ensure that the min values are larger than 0 If min_width < 2 min_width = 2 If min_height < 2 min_height = 2 ' now set the size of this room Local width:Int = Rand(min_width, max_width) Local height:Int = Rand(min_height, max_height) Local search_width:Int = array_width - width Local search_height:Int = array_height - height Local search_x:Int = Rand(0, search_width) Local search_y:Int = Rand(0, search_height) Local max_checks:Int = search_width * search_height Local checked:Int = 0 Local finished:Int = 0 While Not finished If room_count > 0 Local r:room = New room r.x = search_x r.y = search_y r.x2 = search_x + width - 1 r.y2 = search_y + height - 1 Local collisions:Int = 0 For Local b:room = EachIn(rooms) If rooms_collide(b, r) = True collisions:+1 End If Next If Not collisions carve_room(r) ListAddFirst(rooms, r) room_count:+1 finished = True Else End If EndIf If room_count = 0 Local r:room = New room r.x = search_x r.y = search_y r.x2 = search_x + width - 1 r.y2 = search_y + height - 1 carve_room(r) ListAddFirst(rooms, r) room_count:+1 finished = True Exit EndIf ' add to the index search_x:+1 If search_x > search_width search_x = 0 search_y:+1 End If If search_y > search_height search_y = 0 End If checked:+1 If checked = max_checks finished = True End If Wend End Method Method carve_room(r:room) ' take a room and carve out the space needed set squares to 0's For Local x = r.x To r.x2 For Local y = r.y To r.y2 array[x, y] = 0 Next Next End Method Method ready_array(length:Int, height:Int) ' sets all indexes to 1 and readys the array for writing Local a:Byte[length, height] array = a For Local x:Int = 0 To length - 1 For Local y:Int = 0 To height - 1 array[x, y] = 1 Next Next End Method End Type ' A rectangular field that serves as a storage house for seeded values Type room Field x:Int, y:Int, x2:Int, y2:Int End Type ' return if rooms collide (used in create_dungeon) Function rooms_collide:Int(room1:room, room2:room) If room1.y2 + 3 < room2.y Return 0 If room1.y - 3 > room2.y2 Return 0 If room1.x2 + 3 < room2.x Return 0 If room1.x - 3 > room2.x2 Return 0 Return 1 End Function ' connect 2 rooms (used in create_dungeon) Function connect_rooms(room1:room, room2:room, d:dungeon) ' first pick between 2 connection styles ' we always draw from left to right so we must choose what order to process the rooms Local x1:Int = (room1.x + room1.x2) / 2.0 Local y1:Int = (room1.y + room1.y2) / 2.0 Local x2:Int = (room2.x + room2.x2) / 2.0 Local y2:Int = (room2.y + room2.y2) / 2.0 ' make sure the values for each of the x's and y's are EVEN so no corridors touch If Float(x1 Mod 2.0) x1:+1 End If If Float(x2 Mod 2) x2:+1 End If If Float(y1 Mod 2) y1:+1 End If If Float(y2 Mod 2) y2:+1 End If draw_hori(y1, x1, x2, d) draw_vert(x2, y1, y2, d) End Function ' draw a verticle line on the array (used in create_dungeon) Function draw_vert(x1:Int, y1:Int, y2:Int, d:dungeon) ' see if step multiplier is negative Local dist:Int = Abs(y1 - y2) Local mult:Int = 1 If y1 > y2 mult = -1 End If ' draw For Local i = 0 To dist d.array[x1, y1 + (i * mult)] = 0 Next EndFunction ' draw a horizontal line on the array (used in create_dungeon) Function draw_hori(y1:Int, x1:Int, x2:Int, d:dungeon) ' see if step multiplier is negative Local dist:Int = Abs(x1 - x2) Local mult:Int = 1 If x1 > x2 mult = -1 End If ' draw For Local i = 0 To dist d.array[x1 + (i * mult), y1] = 0 Next EndFunction ' *IMPORTANT* lets a programmer seed the dungeon with item, monster and exit values as needed Function add_value(d:dungeon, value:Int, attempts:Int) For Local i:Int = 0 To attempts - 1 Local r:room = room(d.rooms.ValueAtIndex(Rand(0, CountList(d.rooms) - 1))) d.array[Rand(r.x, r.x2), Rand(r.y, r.y2)] = value Next End Function ' *IMPORTANT* returns a freshly made dungeon, the workhorse of the program! Function create_dungeon:dungeon(width:Int, height:Int, room_count:Int, room_min_height:Int, room_max_height:Int, room_min_width:Int, room_max_width:Int) Local d:dungeon = New dungeon 'ready the array d.ready_array(width, height) 'add rooms For Local i = 0 To room_count - 1 d.add_room(room_min_width, room_min_height, room_max_width, room_max_height) Next 'connect rooms in a loop Local length:Int = CountList(d.rooms) For Local j:Int = 0 To length If j + 1 < length connect_rooms(room(d.rooms.ValueAtIndex(j)) , room(d.rooms.ValueAtIndex(j + 1)) , d) EndIf Next connect_rooms(room(d.rooms.ValueAtIndex(0)) , room(d.rooms.ValueAtIndex(length - 1)) , d) Return d End Function 'ENGINE ENDS! 'test here------DELETE THE FOLLOWING TO JUST HAVE THE BASE ENGINE!--------------- Local t:Float = MilliSecs() ' make a new dungeon with our create_dungeon command Local d:dungeon = create_dungeon(40, 40, 6, 3, 12, 3, 12) ' seed the rooms with about 5 "2", these could be treasure chests if you wanted add_value(d, 2, 5) ' seed the rooms with 1 stairway, we will call this value "3" add_value(d, 3, 1) Notify("Generated in :" + String((MilliSecs() - t) *.001) + " seconds") ' test draw dungeon please AppTitle = "Ryan Burnside Dungeon Engine" Graphics(640, 480) While Not KeyHit(KEY_ESCAPE) ' draw the dungeon values as tiles For Local i = 0 To d.array.dimensions()[0] - 1 For Local j = 0 To d.array.dimensions()[1] - 1 If d.array[i, j] Select d.array[i, j] Case 1 SetColor 128, 128, 128 DrawRect(i * 8, j * 8, 7, 7) Case 2 SetColor 255, 128, 0 DrawRect(i * 8, j * 8, 7, 7) Case 3 SetColor 0, 128, 255 DrawRect(i * 8, j * 8, 7, 7) EndSelect End If If Not d.array[i, j] SetColor 200, 200, 200 DrawRect(i * 8, j * 8, 7, 7) End If Next Next If KeyHit(KEY_SPACE) ' new dungeon! Cls d = create_dungeon(40, 40, 6, 3, 12, 3, 12) add_value(d, 2, 5) add_value(d, 3, 1) End If Flip Wend |