Code archives/Graphics/TDungeon

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

Download source code

TDungeon by impixi
(Posted 20 years ago)
Generate perfect mazes and imperfect dungeons
See below for code...

Comments

impixi
(Posted 20 years ago)
Core class. Necessary for all the examples.

TDungeon.bmx:


Rem

TDungeon
V0.1

December 2006


PURPOSE

	A simple class for generating 'perfect' mazes and 'imperfect' dungeons


TEST SYSTEMS

	Athlon 64 3200+, 2GB DDR400, ATI Radeon X800 GT 256MB GDDR3
	Intel Centrino 1600, 512MB DDR2, ATI Radeon Mobility X700 128MB
	Windows XP Pro SP2
	BlitzMax V1.24


MINIMUM REQUIREMENTS

	BlitzMax 1.24
	Windows XP (Linux and MacOS untested)


VERSION CHANGES

	0.1
	  * Initial release
	
	
CONSTANTS

	AREACLEAR_FULL
	AREACLEAR_PERIMETER
	
	AREATYPE_VOID
	AREATYPE_PILLARS
	AREATYPE_WALLED
	
	ENTRY_INCLUDECORNERS
	ENTRY_EXCLUDECORNERS
	
	
METHODS AND FUNCTIONS:
		
	generate
		checkNeighbour
		
	createArea
		clearCell
	makeAreas
	
	getWallGrid
	getAreaGrid

NOTES:
	
	Initially, all generated dungeons are 'perfect' mazes (that is, every cell in the maze is reachable by 
	every other cell).
	
	To create a more interesting dungeon, 'areas' are added to the maze. An area is a space that may be
	blank (void), or an empty 'room' with a number of 'entries'. Clever use of areas, alone or in combinations,
	can offer interesting results. See the relevent method and the associated examples for more 
	information.  
		
End Rem


SuperStrict

Const AREACLEAR_FULL:Int = 0
Const AREACLEAR_PERIMETER:Int = 1
Const AREATYPE_VOID:Int = 0
Const AREATYPE_PILLARS:Int = 1
Const AREATYPE_WALLED: Int = 2
Const ENTRY_INCLUDECORNERS:Int = 0
Const ENTRY_EXCLUDECORNERS:Int = 1


Type TDungeon

	Field SizeX:Int
	Field SizeZ:Int
	Field Dungeon:TDungeonCell[,]


	Method generate(seed:Int = 0, szx:Int = 16, szz:Int = 16)
	Rem
  	Creates a 'perfect' maze.
  	seed: random seed. Use Millisecs() to generate a different result every time.
  	szx: number of grid columns. Minimum 16.
	szz: number of grid rows. Minimum 16.
  	End Rem
	
		If szx < 1 Then SizeX = 16 Else SizeX = szx
		If szz < 1 Then SizeZ = 16 Else SizeZ = szz
	
		Dungeon = New TDungeonCell[SizeX, SizeZ]
	
		For Local x:Int = 0 To SizeX - 1
			For Local z:Int = 0 To SizeZ - 1
				Dungeon[x, z] = New TDungeonCell
				Dungeon[x, z].X = x
				Dungeon[x, z].Z = z
			Next
		Next
		
		SeedRnd seed
		
		Local totalcells:Int = SizeX * SizeZ
		Local cellstack:TDungeonCell[]
		cellstack = cellstack[..cellstack.length + 1]
		cellstack[cellstack.length - 1] = Dungeon[Rand(0, SizeX - 1), Rand(0, SizeZ - 1)]
		Local currentcell:TDungeonCell = cellstack[cellstack.length - 1]
		Local visitedcells:Int = 1

		Local x:Int = currentcell.X
		Local z:Int = currentcell.Z
	
		While visitedcells < totalcells And (Not KeyHit(KEY_ESCAPE))
		
			Local i:Int = -1
			Local neighbors:TDungeonCell[4]
			Local neighborsdir:Int[4]
	
			If x > 0 Then checkNeighbour(Dungeon[x - 1, z], 2, i, neighbors, neighborsdir)
			If x < (SizeX - 1) Then checkNeighbour(Dungeon[x + 1, z], 3, i, neighbors, neighborsdir)
			If z > 0 Then checkNeighbour(Dungeon[x, z - 1], 0, i, neighbors, neighborsdir)
			If z < (SizeZ - 1) Then checkNeighbour(Dungeon[x, z + 1], 1, i, neighbors, neighborsdir)
	
			If (i > -1)
				Local n:Int = Rand(0,i)
		
				Select neighborsdir[n]
					Case 0
						currentcell.WallN = False
						neighbors[n].WallS = False
					Case 1
						currentcell.WallS = False
						neighbors[n].WallN = False
					Case 2
						currentcell.WallW = False
						neighbors[n].WallE = False
					Case 3
						currentcell.WallE = False
						neighbors[n].WallW = False
				End Select
		
				cellstack = cellstack[..cellstack.length + 1]
				cellstack[cellstack.length - 1] = neighbors[n]
				currentcell = cellstack[cellstack.length - 1]
				x = currentcell.X
				z = currentcell.Z
		
				visitedcells :+ 1
			Else
				If cellstack.length > 0  
					currentcell = cellstack[cellstack.length - 1]
					x = currentcell.X
					z = currentcell.Z
					cellstack = cellstack[..cellstack.length - 1]
				EndIf
			EndIf
	
		Wend
	
	EndMethod


	Function checkNeighbour(cell:TDungeonCell Var, ndir:Int, i:Int Var, neighbors:TDungeonCell[] Var, neighborsdir:Int[] Var)
	Rem
	Utility Function used by the generate method.
	EndRem
	
		If ((cell.WallN = True) And (cell.WallS = True) And (cell.WallE = True) And (cell.WallW = True))
			i :+ 1
			neighbors[i] = cell
			neighborsdir[i] = ndir
		EndIf

	EndFunction
	

	Method createArea(id:Byte = 0, posx:Int, posz:Int, sx:Int, sz:Int, cleartyp:Int = AREACLEAR_FULL, typ:Int = AREATYPE_VOID, typdata1:Int = 0, typdata2:Int = ENTRY_INCLUDECORNERS)
	Rem
  	Create an 'area' within the dungeon.
	id: Area id number to assign to the area' cells. Minimum 0. Maximum 255.
  	posx: Top left column of the area. > 0 and less than the dungeon's width.
	posz: Top left row of the area. > 0 and less than the dungeon's height.
	sx: width of the area. Minimum 3. Some area types require higher minimums for full effect.
	sz: height of the area. Minumum 3. Some area types require higher minimums for full effect.
	cleartyp: Clearing type. 	AREACLEAR_FULL - clear the entire area of walls. 
								AREACLEAR_PERIMETER - clear only the perimeter, preserving the inner contents.
	typ: Type of area to create. AREATYPE_VOID - completely blank.
								AREATYPE_PILLARS - pillars effect.
								AREATYPE_WALLED - a walled room.
	typdata1: if type is AREATYPE_WALLED then typdata1 = number of doors into room. Minimum 0.
	typdata2: if type is AREATYPE_WALLED then typdata2 = corner door placement type. ENTRY_INCLUDECORNERS or ENTRY_EXCLUDECORNERS
  	End Rem

		If (posx < 0) Or (posx > (SizeX - 1)) Then Return
		If (posz < 0) Or (posz > (SizeZ - 1)) Then Return
		If (sx < 3) Or (sx >= SizeX) Then sx = 3
		If (sz < 3) Or (sz >= SizeZ) Then sz = 3

		'Tag relevent cells with the area id. Any existing id will be overwritten. ie a cell can only be 
		'classified as one room (though the visual effects will accrue)
		For Local x:Int = posx + 1 To (posx + sx) - 2
			For Local z:Int = posz + 1 To (posz + sz) - 2
				If (x >= 0) And (x < SizeX) And (z >= 0) And (z < SizeZ) Then Dungeon[x, z].AreaID = id
			Next
		Next

		If cleartyp = AREACLEAR_FULL 

			For Local x:Int = posx To (posx + sx) - 1
				For Local z:Int = posz To (posz + sz) - 1
					clearCell(x, z)
				Next
			Next
		
		Else If cleartyp = AREACLEAR_PERIMETER 
			
			For Local x:Int = posx To (posx + sx) - 1
				For Local z:Int = posz To (posz + 1)
					clearCell(x, z)
				Next
			Next
			For Local x:Int = posx To (posx + 1)
				For Local z:Int = posz To (posz + sz) - 1
					clearCell(x, z)
				Next
			Next
			For Local x:Int = (posx + sx - 2) To (posx + sx) - 1
				For Local z:Int = posz To (posz + sz) - 1
					clearCell(x, z)
				Next
			Next
			For Local x:Int = posx To (posx + sx) - 1
				For Local z:Int = (posz + sz - 2) To (posz + sz) - 1
					clearCell(x, z)
				Next
			Next
			
		EndIf
				
		Select typ
		
			Case AREATYPE_VOID 
		
			Case AREATYPE_PILLARS 
		
				If sx >= 5 And sz >= 5 'Only possible if the width and height of the area is >= 5
					For Local x:Int = (posx + 1) To (posx - 1 + sx) - 1 Step 2
						For Local z:Int = (posz + 1) To (posz - 1 + sz) - 1 Step 2
							If (x =< (SizeX - 1)) And (z =< (SizeZ - 1))
								Local cell:TDungeonCell = Dungeon[x, z]
								If (Not (x = 0)) Then cell.WallW = True
								If (Not (z = 0)) Then cell.WallN = True
								If (Not (x = (SizeX - 1))) Then cell.WallE = True
								If (Not (z = (SizeZ - 1))) Then cell.WallS = True
							EndIf
						Next
					Next
				EndIf 
			
			Case AREATYPE_WALLED 'Walled room with entries.
				'typdata1: number of doors
				'typdata2 = 1: exclude corners

				Local pdcells:TDungeonCell[]	'Potential room entries

				'West and East
				For Local z:Int = (posz + 1) To (posz + sz - 2)
					If (z =< (SizeZ - 1))
						Local x:Int = posx + 1
						If x < SizeX
							Local cell:TDungeonCell = Dungeon[x, z]
							cell.WallW = True
						
							pdcells = pdcells[..pdcells.length + 1]
							pdcells[pdcells.length - 1] = cell
						
							x = posx + sx - 2
							If (x =< (SizeX - 1))
								cell = Dungeon[x, z]
								cell.WallE = True
								pdcells = pdcells[..pdcells.length + 1]
								pdcells[pdcells.length - 1] = cell
							EndIf
						EndIf
					EndIf
				Next
				
				'North and South
				For Local x:Int = (posx + 1) To (posx + sx - 2)
					If (x =< (SizeX - 1))
						Local z:Int = posz + 1
						If z < SizeZ
							Local cell:TDungeonCell = Dungeon[x, z]
							cell.WallN = True
				
							pdcells = pdcells[..pdcells.length + 1]
							pdcells[pdcells.length - 1] = cell

							z = posz + sz - 2
							If (z =< (SizeZ - 1))
								cell = Dungeon[x, z]
								pdcells = pdcells[..pdcells.length + 1]
								pdcells[pdcells.length - 1] = cell
								cell.WallS = True
							EndIf
						EndIf
					EndIf
				Next
				
				'If necessary, remove corners from potential entry list
				If typdata2 = ENTRY_EXCLUDECORNERS
				
					Local eol:Int = False
					Local i:Int = 0
					
					While (Not eol)
					
						Local remove:Int = False
						
						If pdcells 
						
						If (pdcells[i].X = (posx + 1) And pdcells[i].Z = (posz + 1))
							remove = True
 						Else
							If (pdcells[i].X = (posx + sx - 2) And pdcells[i].Z = (posz + sz - 2))
								remove = True
							Else
								If (pdcells[i].X = (posx + 1) And pdcells[i].Z = (posz + sz - 2))
									remove = True
								Else
									If (pdcells[i].X = (posx + sx - 2) And pdcells[i].Z = (posz + 1))
										remove = True
									EndIf
								EndIf
							EndIf
						EndIf
						
						EndIf
												
						If remove
							pdcells[i] = pdcells[pdcells.length - 1]
							pdcells = pdcells[..pdcells.length - 1]
						Else
							i :+ 1
						EndIf
							
							
												
						If i > pdcells.length - 1 Then eol = True

					Wend
					
				EndIf
				
				'shuffle potential room entries
				If (pdcells.length > 0)
				
					For Local n:Int = 1 To 100
						Local i1:Int = Rand(0, pdcells.length - 1)
						Local i2:Int = Rand(0, pdcells.length - 1)
						Local cell1:TDungeonCell = pdcells[i1]
						Local cell2:TDungeonCell = pdcells[i2]
						pdcells[i1] = cell2
						pdcells[i2] = cell1
					Next
				
					'create entries
					For Local n:Int = 0 To (typdata1 - 1)
						If n < pdcells.length
							clearCell(pdcells[n].X, pdcells[n].Z)
						EndIf
					Next
					
				EndIf
				
		EndSelect	
	
	EndMethod


	Method clearCell(x:Int, z:Int)
	Rem
	Clear a specific cell of all walls, but presevere the dungeon's outermost edges if necessary.
	x: cell column.
	z: cell row.
	End Rem
	
		If (x =< (SizeX - 1)) And (z =< (SizeZ - 1))
			Local cell:TDungeonCell = Dungeon[x, z]
			If (Not (x = 0)) Then cell.WallW = False
			If (Not (z = 0)) Then cell.WallN = False
			If (Not (x = (SizeX - 1))) Then cell.WallE = False
			If (Not (z = (SizeZ - 1))) Then cell.WallS = False
		EndIf

	EndMethod


	Method makeAreas(qty:Int = 1, maxsizex:Int = 4, maxsizez:Int = 4)
	Rem
	Create a number of areas in the dungeon at random locations and of random sizes.
	qty: Quantity of areas to create. Minimum 1.
	maxsizex: Maximum possible area width. Minimum 4.
	maxsizez: Maximum possible area height. Minimum 4.
	EndRem
	
		If qty <= 0 Then qty = 1
		If maxsizex <= 3 Then maxsizex = 0.4 * SizeX
		If maxsizez <= 3 Then maxsizez = 0.4 * SizeZ
	
		For Local n:Int = 1 To qty
			createArea(n, Rand(0, SizeX - maxsizex), Rand(0, SizeZ - maxsizez), Rand(3, maxsizex), Rand(3, maxsizez), Rand(0, 1), Rand(0, 2), Rand(1, 4), Rand(0, 1))
		Next
		
	EndMethod

	
	Method getWallGrid:Byte[,]()
	Rem
	Create a grid representation of the dungeon's walls. 
	Most applications utilise such a data structure for dungeon processing. 
	Returns: 2d array of byte values. True = wall. False = no wall.
	NOTE: The returned grid dimensions will be double the TDungeon sizes.
	NOTE2:'Double' walls are ignored.
			ie: North walls correspond to the South walls of the upper cell,
				South walls correspond to the North walls of the lower cell,
				East walls correspond to the West walls of the right cell,
				West walls correspond to the East walls of the left cell.
	EndRem
		
		Local arr:Byte[SizeX * 2 + 1, SizeZ * 2 + 1]
	
		For Local x:Int = 0 To SizeX - 1 
			Local ax:Int = x * 2 + 1
			For Local z:Int = 0 To SizeZ - 1 
				Local az:Int = z * 2 + 1
				If Dungeon[x, z].WallN = True 
					arr[ax, az - 1] = True
					arr[ax - 1, az - 1] = True
					arr[ax + 1, az - 1] = True
				EndIf
				If Dungeon[x, z].WallS = True 
					arr[ax, az + 1] = True
					arr[ax - 1, az + 1] = True
					arr[ax + 1, az + 1] = True
				EndIf
				If Dungeon[x, z].WallW = True 
					arr[ax - 1, az] = True
					arr[ax - 1, az + 1] = True
					arr[ax - 1, az - 1] = True
				EndIf
				If Dungeon[x, z].WallE = True 
					arr[ax + 1, az] = True
					arr[ax + 1, az - 1] = True
					arr[ax + 1, az + 1] = True
				EndIf
			Next
		Next	
	
		Return arr
	
	EndMethod


	Method getAreaGrid:Byte[,]()
	Rem
	Create a grid representation of the dungeon's area ids. 
	Returns: 2d array of byte values. Values = ids.
	NOTE: The returned grid dimensions will be double the TDungeon sizes.
	EndRem
		Local arr:Byte[SizeX * 2 + 1, SizeZ * 2 + 1]
	
		For Local x:Int = 0 To SizeX - 1 
			Local ax:Int = x * 2 + 1
			For Local z:Int = 0 To SizeZ - 1 
				Local az:Int = z * 2 + 1
				If Dungeon[x, z].AreaID <> 0
					arr[ax, az] = Dungeon[x, z].AreaID
					arr[ax, az + 1] = Dungeon[x, z].AreaID
					arr[ax , az - 1] = Dungeon[x, z].AreaID
					arr[ax + 1, az] = Dungeon[x, z].AreaID
					arr[ax + 1, az + 1] = Dungeon[x, z].AreaID
					arr[ax + 1, az - 1] = Dungeon[x, z].AreaID
					arr[ax - 1, az] = Dungeon[x, z].AreaID
					arr[ax - 1, az + 1] = Dungeon[x, z].AreaID
					arr[ax - 1, az - 1] = Dungeon[x, z].AreaID
				EndIf
			Next
		Next	
	
		Return arr
	
	EndMethod

EndType


Type TDungeonCell
	Rem
	Utility class used by TDungeon.
	EndRem 
	
	Field WallN:Int = True		
	Field WallS:Int = True 
	Field WallE:Int	= True
	Field WallW:Int	= True
	
	Field X:Int
	Field Z:Int
	
	Field AreaID:Byte = 0

EndType




Maze Example 1

test1.bmx:


Rem

	TDungeon V0.1 Test #1 - Maze Example 1.
	
		
	MINIMUM REQUIREMENTS

		BlitzMax 1.24
		Windows XP (Linux and MacOS untested, but should work)
	
	
	DEPENDENCIES
	
		TDungeon.bmx
	
			
	NOTES
				
		This example makes use of TDungeon's built-in TDungeonCell grid.
		
		(*) - Indicates an essential step in creating and generating the maze.
		
		A 'perfect' maze is one in which every cell can be reached by every other cell.
		ie, there are NO closed sections inaccesable from other sections.
		 
		See TDungeon.bmx for function and method-specific documentation.
			
End Rem

' ----- INITIALISATION -----

SuperStrict

'(*) Import the TDungeon class file.
Import "TDungeon.bmx"

'Set up the graphics window.
AppTitle = "Maze Example 1"
Graphics 800, 600
SetBlend ALPHABLEND
SetAlpha 1.0
SetColor 0, 0, 0
SetClsColor 255, 255, 255

'Define some global variables to store the maze's dimensions.
Global Columns:Int = 32
Global Rows:Int = 32

'(*) Define and create an instance of the TDungeon class.
Global Maze:TDungeon = New TDungeon

'(*) Generate a 'perfect' maze measuring Columns x Rows dimensions.
Maze.generate MilliSecs(), Columns, Rows

' ----- MAIN LOOP -----

While Not KeyHit(KEY_ESCAPE)

	Cls
	
	'Generate a new maze if the left mouse button is clicked.
	If MouseHit(1)
		Maze.generate MilliSecs(), Columns, Rows
	EndIf
	
	'draw the maze onto the backbuffer.
	drawMaze 16, 5, 5
	
	DrawText "Left click to generate a new maze.", 5, (16 * (Rows + 1)) + 5
	
	'Flip the backbuffer for screen display.
	Flip 1

	Delay 10
		
Wend

End

'----- FUNCTIONS -----

Function drawMaze(cell_size:Int = 8, col_offset:Int = 0, row_offset:Int = 0)
				
	'Draw a simple representation of the maze. 
	'In this example, 'double' walls are ignored.
	'ie: 	North walls correspond to the South walls of the upper cell,
	'		South walls correspond to the North walls of the lower cell,
	'		East walls correspond to the West walls of the right cell,
	'		West walls correspond to the East walls of the left cell.	
	
	'Iterate through every maze cell.
	'sz and sx correspond to screen pixel coordinates.		
	For Local c:Int = 0 To Columns - 1
		Local sx:Int = (c * cell_size) + col_offset
		
		For Local r:Int = 0 To Rows - 1
			Local sz:Int = (r * cell_size) + row_offset
			
			'Draw an appropriate line for every wall: 
			'horizontal for north and south walls; vertical for east and west walls.
			If Maze.Dungeon[c, r].WallN = True Then DrawLine sx, sz, sx + cell_size, sz
			If Maze.Dungeon[c, r].WallS = True Then DrawLine sx, sz + cell_size, sx + cell_size, sz + cell_size
			If Maze.Dungeon[c, r].WallW = True Then DrawLine sx, sz, sx, sz + cell_size
			If Maze.Dungeon[c, r].WallE = True Then DrawLine sx + cell_size, sz, sx + cell_size, sz + cell_size
		Next
	Next
		
EndFunction




Maze Example 2

test2.bmx:


Rem

	TDungeon V0.1 Test #2 - Maze Example 2.
	
		
	MINIMUM REQUIREMENTS

		BlitzMax 1.24
		Windows XP (Linux and MacOS untested, but should work)
	
	
	DEPENDENCIES
	
		TDungeon.bmx
	
			
	NOTES
				
		This example makes use of TDungeon's getWallGrid method.
		
		(*) - Indicates an essential step in creating and generating the maze.
		
		A 'perfect' maze is one in which every cell can be reached by every other cell.
		ie, there are NO closed sections inaccesable from other sections.
		 
		See TDungeon.bmx for function and method-specific documentation.
			
End Rem

' ----- INITIALISATION -----

SuperStrict

'(*) Import the TDungeon class file.
Import "TDungeon.bmx"

'Set up the graphics window.
AppTitle = "Maze Example 2"
Graphics 800, 600
SetBlend ALPHABLEND
SetAlpha 1.0
SetColor 0, 0, 0
SetClsColor 255, 255, 255

'(*) Define a 2d BYTE array to store our walls. 
'Note that no dimension sizes are specified. 
Global WallGrid:Byte[,]

'Create some global variables to store the grid's dimensions.
'Again note that the sizes are not explicitely specified.
'They will be calculated later.
Global Columns:Int
Global Rows:Int

generateWallGrid

' ----- MAIN LOOP -----

While Not KeyHit(KEY_ESCAPE)

	Cls
	
	'Generate a new maze if the left mouse button is clicked.
	If MouseHit(1)
		generateWallGrid
	EndIf
	
	'draw the maze onto the backbuffer.
	drawMaze 16, 5, 5
	
	SetColor 0, 0, 0
	DrawText "Left click to generate a new maze.", 5, (16 * (Rows + 1)) + 5
	
	'Flip the backbuffer for screen display.
	Flip 1

	Delay 10
		
Wend

End

'----- FUNCTIONS -----

Function generateWallGrid()

	'Define some local variables to store the maze's INITIAL dimensions.
	Local initcols:Int = 16
	Local initrows:Int = 16

	'(*) Define and create an local instance of the TDungeon class.
	Local maze:TDungeon = New TDungeon

	'(*) Generate a 'perfect' maze measuring Columns x Rows dimensions.
	maze.generate MilliSecs(), initcols, initrows

	'(*) Get a True/False grid representation of the walls and assign it to our global MazeGrid variable
	WallGrid = maze.getWallGrid()

	'Set our global Columns and Rows based on the returned grids' dimensions: 
	'usually 2 x our initial dimensions, but it's best to calculate it this way just in case.
	Local d:Int[] = WallGrid.Dimensions()
	Columns = d[0]
	Rows = d[1]

	'Now that we have a grid representation of our maze we no longer need the TDungeon instance,
	'so it can be deleted from memory. BlitzMax will automatically do this eventually
	'but we'll force it to happen now:
	maze = Null
	GCCollect

EndFunction


Function drawMaze(cell_size:Int = 8, col_offset:Int = 0, row_offset:Int = 0)
				
	'Draw a simple representation of the maze. 
	
	'Iterate through every maze cell.
	'sz and sx correspond to screen pixel coordinates.		
	For Local c:Int = 0 To Columns - 1
		Local sx:Int = (c * cell_size) + col_offset
		
		For Local r:Int = 0 To Rows - 1
			Local sz:Int = (r * cell_size) + row_offset
			
			'Draw an appropriately colored rectangle for every cell: 
			'grey for walls; black for no wall.
			If WallGrid[c, r] = True Then SetColor 100, 100, 100 Else SetColor 0, 0, 0
			DrawRect sx, sz, cell_size, cell_size
			
		Next
	Next
		
EndFunction




Dungeon Example 1

test3.bmx:


Rem

	TDungeon V0.1 Test #3 - Dungeon Example 1.
	
		
	MINIMUM REQUIREMENTS

		BlitzMax 1.24
		Windows XP (Linux and MacOS untested, but should work)
	
	
	DEPENDENCIES
	
		TDungeon.bmx
	
			
	NOTES
				
		This example makes use of TDungeon's built-in TDungeonCell grid.
		
		In the context of the TDungeon class, dungeons are simply modifications to a 'perfect' maze.
		'Areas' are created within the maze that similate open regions and rooms. Some interesting 
		results can be achieved by overlapping areas.
		
		See test1.bmx for the basics of maze generation.
				 
		See TDungeon.bmx for function and method-specific documentation.
			
End Rem

' ----- INITIALISATION -----

SuperStrict

Import "TDungeon.bmx"

AppTitle = "Dungeon Example 1"
Graphics 800, 600
SetBlend ALPHABLEND
SetAlpha 1.0
SetColor 0, 0, 0
SetClsColor 255, 255, 255

Global AreaTog:Int = 0

Global Columns:Int = 32
Global Rows:Int = 32

Global MyDungeon:TDungeon = New TDungeon

generateMyDungeon

' ----- MAIN LOOP -----

While Not KeyHit(KEY_ESCAPE)

	Cls
	
	If MouseHit(1)
		generateMyDungeon
	EndIf
	
	If MouseHit(2)
		'Create a new walled area (ie room) of a random size in a random location.
		'See the createArea method in the TDungeon class for more information.
		MyDungeon.createArea(Rand(1, 255), Rand(1, Columns - 1), Rand(1, Rows - 1), Rand(4, 10), Rand(4, 10), Rand(0, 1), AREATYPE_WALLED, Rand(1, 4), ENTRY_EXCLUDECORNERS)
	EndIf
	
	If KeyHit(KEY_TAB) Then AreaTog :~ 1
	
	drawDungeon 16, 5, 5
	
	DrawText "Left click to generate a new dungeon.", 5, (16 * (Rows + 1)) + 5
	DrawText "Right click to generate a new room.", 5, (16 * (Rows + 1)) + 20 + 5
	DrawText "TAB toggles AreaID color.", 5, (16 * (Rows + 1)) + 40 + 5
	
	Flip 1

	Delay 10
		
Wend

End

'----- FUNCTIONS -----

Function generateMyDungeon()

	MyDungeon.generate MilliSecs(), Columns, Rows
	
	'Create a number of 'areas' in random locations and of random types and sizes.
	'See the makeAreas and createArea methods in the TDungeon class for more information.
	'Every cell has an AreaID (0 to 255), set whenever an area is created. This value can
	'be used, for example, to determine 'enemy placement, environmental effects, 
	'door, floor and wall types, etc.
	MyDungeon.makeAreas(12, 15, 15)

EndFunction

Function drawDungeon(cell_size:Int = 8, col_offset:Int = 0, row_offset:Int = 0)
			
	'In this example, AreaID's <> 0 are denoted by green rectangles.
	If AreaTog
		SetColor 0, 196, 0  
		For Local c:Int = 0 To Columns - 1
			Local sx:Int = (c * cell_size) + col_offset
			For Local r:Int = 0 To Rows - 1
				Local sz:Int = (r * cell_size) + row_offset
 				If MyDungeon.Dungeon[c, r].AreaID <> 0
					DrawRect sx, sz, cell_size, cell_size
				EndIf
			Next
		Next
	EndIf		
			
	SetColor 0, 0, 0		
				
	For Local c:Int = 0 To Columns - 1
		Local sx:Int = (c * cell_size) + col_offset
		
		For Local r:Int = 0 To Rows - 1
			Local sz:Int = (r * cell_size) + row_offset

			If MyDungeon.Dungeon[c, r].WallN = True Then DrawLine sx, sz, sx + cell_size, sz
			If MyDungeon.Dungeon[c, r].WallS = True Then DrawLine sx, sz + cell_size, sx + cell_size, sz + cell_size
			If MyDungeon.Dungeon[c, r].WallW = True Then DrawLine sx, sz, sx, sz + cell_size
			If MyDungeon.Dungeon[c, r].WallE = True Then DrawLine sx + cell_size, sz, sx + cell_size, sz + cell_size
		Next
	Next
		
EndFunction



impixi
(Posted 20 years ago)
Edit: Removed dead links.

Booticus
(Posted 20 years ago)
DUDE! this is totally taking me back! Dungeon Master, Ultima, right on! I appreciate the code and work you've done!

impixi
(Posted 20 years ago)
I can always count on your support, Mario. Thanks. :)

puki
(Posted 20 years ago)
BlitzMax?

Sniffilisation.

impixi
(Posted 20 years ago)
EDIT: Removed dead links.

SculptureOfSoul
(Posted 20 years ago)
Awesome work. I might end up borrowing some ideas from this code to generate random dungeons for my MUD. Many thanks!

impixi
(Posted 20 years ago)
EDIT: Removed dead links.

Code Archives Forum