Code archives/Miscellaneous/Simple Dungeon Generator

This code has been declared by its author to be Public Domain code.

Download source code

Simple Dungeon Generator by Ryan Burnside2008
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

Comments

Tobo2010
What's with all these .bmx(s) showing as .bb(s)?


_PJ_2010
I think sometimes people click 'add to the code archives' but don't change the actual pull-down.


slenkar2010
and you cant change it back after you have posted it :)


Code Archives Forum