Field of view using recursive shadowcasting

Advanced game related topics
eesau
Enthusiast
Enthusiast
Posts: 589
Joined: Fri Apr 27, 2007 12:38 pm
Location: Finland

Field of view using recursive shadowcasting

Post by eesau »

Recursive shadowcasting is an algorithm used for creating a very fast field of view or a "fog of war" in 2d tile-based games such as roguelikes for example. The algorithm is described in detail here.

Please note that this is only a quickly hacked-up example, and there might be a few bugs... Here is the PB implementation:

Code: Select all

Enumeration

	#MaxDistance = 35

	#MinX = 1 : #MaxX = 500
	#MinY = 1 : #MaxY = 500

EndEnumeration

Structure LOCATION

	Terrain    . L
	Visibility . L
	Distance   . L
	Lighting   . L
	
EndStructure

Global Dim Map . LOCATION ( #MaxX , #MaxY )

Global PX = 5 + Random ( #MaxX - 10 )
Global PY = 5 + Random ( #MaxY - 10 )

CreateImage ( 100 , 71 * 8 , 71 * 8 )

If StartDrawing ( ImageOutput ( 100 ) )
	
	Box ( 0 , 0 , 800 , 800 , #Black )
	
	StopDrawing ( )

EndIf

Macro PointInRange ( X , Y )

	( X => #MinX And X <= #MaxX And Y => #MinY And Y <= #MaxY )
	
EndMacro

Procedure MapMake ( )

	For CounterY = #MinY To #MaxY : For CounterX = #MinX To #MaxX
		
		Map ( CounterX , CounterY ) \ Terrain = #Null
			
	Next : Next

	For Counter = #MinX To #MaxX : Map ( Counter , #MinY ) \ Terrain = 1 : Next
	For Counter = #MinX To #MaxX : Map ( Counter , #MaxY ) \ Terrain = 1 : Next
	For Counter = #MinY To #MaxY : Map ( #MinX , Counter ) \ Terrain = 1 : Next
	For Counter = #MinY To #MaxY : Map ( #MaxX , Counter ) \ Terrain = 1 : Next

	For Counter = 1 To 100
	
		X = Random ( #MaxX - 20 ) + 10
		Y = Random ( #MaxY - 20 ) + 10
	
		If Random ( 1 )
	
			Map ( X , Y ) \ Terrain = 1
			
		Else
		
			Map ( X , Y ) \ Terrain = 1
			Map ( X , Y + 1 ) \ Terrain = 1
			Map ( X + 1 , Y ) \ Terrain = 1
			Map ( X + 1 , Y + 1 ) \ Terrain = 1
		
		EndIf
	
	Next
	
	For Counter = 1 To 1000
	
		X = Random ( #MaxX - 30 ) + 5
		Y = Random ( #MaxY - 30 ) + 5
	
		If Random ( 1 )
	
			For Z = 1 To Random ( 15 ) : Map ( X , Y + Z ) \ Terrain = 1 : Next
		
		Else

			For Z = 1 To Random ( 15 ) : Map ( X + Z , Y ) \ Terrain = 1 : Next		
		
		EndIf
	
	Next
	
	For Counter = 1 To 200
	
		Size = Random ( 9 )
		
		X = 10 + Random ( #MaxX - 20 )
		Y = 10 + Random ( #MaxY - 20 )
		
		Map ( X , Y ) \ Terrain = 1
		
		For Z = 1 To Size
					
			Map ( X + Z , Y ) \ Terrain = 1
			Map ( X , Y + Z ) \ Terrain = 1
			Map ( X + Size , Y + Z ) \ Terrain = 1
			Map ( X + Z , Y + Size ) \ Terrain = 1
						
		Next
			
	Next
	
EndProcedure

Procedure MapForget ( )

	For X = 1 To #MaxX
	
		For Y = 1 To #MaxY

			Map ( X , Y ) \ Visibility = 0
			Map ( X , Y ) \ Lighting   = 0
			
		Next
		
	Next

EndProcedure

Procedure TileBlocked ( X , Y )

	If PointInRange ( X , Y ) And Map ( X , Y ) \ Terrain > #Null
		
		ProcedureReturn #True
		
	EndIf
	
EndProcedure

Procedure SetTileVisible ( X , Y , Distance )

	If PointInRange ( X , Y )

		Map ( X , Y ) \ Visibility = #True
		Map ( X , Y ) \ Distance = Distance

	EndIf

EndProcedure

Procedure SetTileLighting ( X , Y , Distance )

	If PointInRange ( X , Y )

		Map ( X , Y ) \ Lighting = ( #MaxDistance - Distance ) + Random ( 1 ) - Random ( 1 )

	EndIf

EndProcedure

Enumeration

	#FovRectangular
	#FovCircular

EndEnumeration

Procedure . f Slope ( X1 . f , Y1 . f , X2 . f , Y2 . f )

	ProcedureReturn ( X1 - X2 ) / ( Y1 - Y2 )
	
EndProcedure

Procedure . f InverseSlope ( X1 . f , Y1 . f , X2 . f , Y2 . f )

	ProcedureReturn 1 / Slope ( X1 , Y1 , X2 , Y2 )

EndProcedure

Procedure Distance ( X1 , Y1 , X2 , Y2 )
	
	ProcedureReturn Pow ( ( X2 - X1 ) * ( X2 - X1 ) + ( Y2 - Y1 ) * ( Y2 - Y1 ) , 0.5 )

EndProcedure

Procedure ScanOctant1 ( Type , X , Y , Distance , MaxDistance , AlphaSlope . f , OmegaSlope . f , *Function )

	If Distance > MaxDistance
	
		ProcedureReturn #Null

	EndIf

	AlphaX = Int ( X + 0.5 - AlphaSlope * Distance )
	OmegaX = Int ( X + 0.5 - OmegaSlope * Distance )
		
  	CheckY = Y - Distance
	
	If Type = #FovRectangular And AlphaX <> X - Distance
			
		CallFunctionFast ( *Function , AlphaX , CheckY , Distance )

	EndIf
	
	If Type = #FovCircular
	
		AlphaX - 1
	
	EndIf
	
	Blocked = TileBlocked ( AlphaX , CheckY )

	For CheckX = AlphaX + 1 To OmegaX

		If Type = #FovCircular And Distance ( X , Y , CheckX , CheckY ) > MaxDistance
	
			Continue
	
		EndIf
		
		If CheckX <> X

			CallFunctionFast ( *Function , CheckX , CheckY , Distance )

		EndIf
			
		If TileBlocked ( CheckX , CheckY )

			If Not Blocked

				ScanOctant1 ( Type , X , Y , Distance + 1 , MaxDistance , AlphaSlope , Slope ( X + 0.5 , Y + 0.5 , CheckX - 0.00001 , CheckY + 0.99999 ) , *Function )
			
			EndIf
			
			Blocked = #True
			
		Else 
		
			If Blocked

				AlphaSlope = Slope ( X + 0.5 , Y + 0.5 , CheckX , CheckY )
				
			EndIf
			
			Blocked = #False
			
		EndIf
					
	Next

	If Not Blocked
	
		ScanOctant1 ( Type , X , Y , Distance + 1 , MaxDistance , AlphaSlope , OmegaSlope , *Function )
		
	EndIf

EndProcedure

Procedure ScanOctant2 ( Type , X , Y , Distance , MaxDistance , AlphaSlope . f , OmegaSlope . f , *Function )

	If Distance > MaxDistance 

		ProcedureReturn #Null

	EndIf

  	AlphaX = Int ( X + 0.5 - AlphaSlope * Distance )
  	OmegaX = Int ( X + 0.5 - OmegaSlope * Distance )
  	
	CheckY = Y - Distance

	If Type = #FovRectangular And AlphaX <> X - ( -Distance )

		CallFunctionFast ( *Function , AlphaX , CheckY , Distance )

	EndIf

	If Type = #FovCircular
	
		AlphaX + 1
	
	EndIf

	Blocked = TileBlocked ( AlphaX , CheckY )

	For CheckX = AlphaX - 1 To OmegaX Step -1
	
		If Type = #FovCircular And Distance ( X , Y , CheckX , CheckY ) > MaxDistance
	
			Continue
			
		EndIf
			
		If CheckX <> X

			CallFunctionFast ( *Function , CheckX , CheckY , Distance )

		EndIf

		If TileBlocked ( CheckX , CheckY )
		
			If Not Blocked

				ScanOctant2 ( Type , X , Y , Distance + 1 , MaxDistance , AlphaSlope , Slope ( X + 0.5 , Y + 0.5 , CheckX + 1.0 , CheckY + 0.99999 ) , *Function )

			EndIf
			
			Blocked = #True
			
		Else 
		
			If Blocked
				
				AlphaSlope = Slope ( X + 0.5 , Y + 0.5 , CheckX + 0.99999 , CheckY )
				
			EndIf
			
			Blocked = #False
			
		EndIf
					
	Next

	If Not Blocked
	
		ScanOctant2 ( Type , X , Y , Distance + 1 , MaxDistance , AlphaSlope , OmegaSlope , *Function )
	
	EndIf

EndProcedure

Procedure ScanOctant3 ( Type , X , Y , Distance , MaxDistance , AlphaSlope . f , OmegaSlope . f , *Function )

	If Distance > MaxDistance
	
		ProcedureReturn #Null

	EndIf

  	AlphaY = Int ( Y + 0.5 + AlphaSlope * Distance )
  	OmegaY = Int ( Y + 0.5 + OmegaSlope * Distance )
  	
	CheckX = X + Distance

	If Type = #FovRectangular And AlphaY <> Y + ( -Distance )

		CallFunctionFast ( *Function , CheckX , AlphaY , Distance )

	EndIf

	If Type = #FovCircular
	
		AlphaY - 1
	
	EndIf

	Blocked = TileBlocked ( CheckX , AlphaY )

	For CheckY = AlphaY + 1 To OmegaY

		If Type = #FovCircular And Distance ( X , Y , CheckX , CheckY ) > MaxDistance
	
			Continue
	
		EndIf

		If CheckY <> Y

			CallFunctionFast ( *Function , CheckX , CheckY , Distance )

		EndIf

		If TileBlocked ( CheckX , CheckY )
		
			If Not Blocked

				ScanOctant3 ( Type , X , Y , Distance + 1 , MaxDistance , AlphaSlope , InverseSlope ( X + 0.5 , Y + 0.5 , CheckX , CheckY - 0.00001 ) , *Function )

			EndIf
			
			Blocked = #True
			
		Else 
		
			If Blocked
				
				AlphaSlope = InverseSlope ( X + 0.5 , Y + 0.5 , CheckX + 0.99999 , CheckY )
				
			EndIf
			
			Blocked = #False
			
		EndIf
					
	Next

	If Not Blocked
	
		ScanOctant3 ( Type , X , Y , Distance + 1 , MaxDistance , AlphaSlope , OmegaSlope , *Function )
	
	EndIf

EndProcedure

Procedure ScanOctant4 ( Type , X , Y , Distance , MaxDistance , AlphaSlope . f , OmegaSlope . f , *Function )

	If Distance > MaxDistance
	
		ProcedureReturn #Null

	EndIf

  	AlphaY = Int ( Y + 0.5 + AlphaSlope * Distance )
  	OmegaY = Int ( Y + 0.5 + OmegaSlope * Distance )
  	
	CheckX = X + Distance

	If Type = #FovRectangular And AlphaY <> Y + Distance

		CallFunctionFast ( *Function , CheckX , AlphaY , Distance )

	EndIf

	If Type = #FovCircular
	
		AlphaY + 1
	
	EndIf

	Blocked = TileBlocked ( CheckX , AlphaY )
	
	For CheckY = AlphaY - 1 To OmegaY Step -1

		If Type = #FovCircular And Distance ( X , Y , CheckX , CheckY ) > MaxDistance
	
			Continue

		EndIf

		If CheckY <> Y
			
			CallFunctionFast ( *Function , CheckX , CheckY , Distance )

		EndIf

		If TileBlocked ( CheckX , CheckY )
		
			If Not Blocked

				ScanOctant4 ( Type , X , Y , Distance + 1 , MaxDistance , AlphaSlope , InverseSlope ( X + 0.5 , Y + 0.5 , CheckX , CheckY + 1.0 ) , *Function )

			EndIf
			
			Blocked = #True
			
		Else 
		
			If Blocked
				
				AlphaSlope = InverseSlope ( X + 0.5 , Y + 0.5 , CheckX + 0.99999 , CheckY + 0.99999 )
				
			EndIf
			
			Blocked = #False
			
		EndIf
					
	Next

	If Not Blocked
	
		ScanOctant4 ( Type , X , Y , Distance + 1 , MaxDistance , AlphaSlope , OmegaSlope , *Function )
		
	EndIf

EndProcedure

Procedure ScanOctant5 ( Type , X , Y , Distance , MaxDistance , AlphaSlope . f , OmegaSlope . f , *Function )

	If Distance > MaxDistance
	
		ProcedureReturn #Null

	EndIf

  	AlphaX = Int ( X + 0.5 + AlphaSlope * Distance )
  	OmegaX = Int ( X + 0.5 + OmegaSlope * Distance )
  	
	CheckY = Y + Distance

	If Type = #FovRectangular And AlphaX <> X + Distance

		CallFunctionFast ( *Function , AlphaX , CheckY , Distance )
	
	EndIf

	If Type = #FovCircular
	
		AlphaX + 1
	
	EndIf

	Blocked = TileBlocked ( AlphaX , CheckY )

	For CheckX = AlphaX - 1 To OmegaX Step -1

		If Type = #FovCircular And Distance ( X , Y , CheckX , CheckY ) > MaxDistance
	
			Continue
			
		EndIf

		If CheckX <> X

			CallFunctionFast ( *Function , CheckX , CheckY , Distance )

		EndIf

		If TileBlocked ( CheckX , CheckY )
		
			If Not Blocked

				ScanOctant5 ( Type , X , Y , Distance + 1 , MaxDistance , AlphaSlope , Slope ( X + 0.5 , Y + 0.5 , CheckX + 1.0 , CheckY ) , *Function )

			EndIf
			
			Blocked = #True
			
		Else 
		
			If Blocked
				
				AlphaSlope = Slope ( X + 0.5 , Y + 0.5 , CheckX + 0.99999 , CheckY + 0.99999 )
				
			EndIf
			
			Blocked = #False
			
		EndIf
					
	Next

	If Not Blocked
	
		ScanOctant5 ( Type , X , Y , Distance + 1 , MaxDistance , AlphaSlope , OmegaSlope , *Function )
		
	EndIf

EndProcedure

Procedure ScanOctant6 ( Type , X , Y , Distance , MaxDistance , AlphaSlope . f , OmegaSlope . f , *Function )

	If Distance > MaxDistance
	
		ProcedureReturn #Null

	EndIf

	AlphaX = Int ( X + 0.5 + AlphaSlope * Distance )
  	OmegaX = Int ( X + 0.5 + OmegaSlope * Distance )
  	  	
	CheckY = Y + Distance

	If Type = #FovRectangular And AlphaX <> X + ( -Distance )

		CallFunctionFast ( *Function , AlphaX , CheckY , Distance )

	EndIf

	If Type = #FovCircular
	
		AlphaX - 1
	
	EndIf

	Blocked = TileBlocked ( AlphaX , CheckY )

	For CheckX = AlphaX + 1 To OmegaX
	
		If Type = #FovCircular And Distance ( X , Y , CheckX , CheckY ) > MaxDistance
	
			Continue
	
		EndIf
			
		If CheckX <> X

			CallFunctionFast ( *Function , CheckX , CheckY , Distance )

		EndIf

		If TileBlocked ( CheckX , CheckY )
		
			If Not Blocked

				ScanOctant6 ( Type , X , Y , Distance + 1 , MaxDistance , AlphaSlope , Slope ( X + 0.5 , Y + 0.5 , CheckX - 0.00001 , CheckY ) , *Function )

			EndIf
			
			Blocked = #True
			
		Else 
		
			If Blocked

				AlphaSlope = Slope ( X + 0.5 , Y + 0.5 , CheckX , CheckY + 0.99999 )

			EndIf
			
			Blocked = #False
			
		EndIf
					
	Next

	If Not Blocked
	
		ScanOctant6 ( Type , X , Y , Distance + 1 , MaxDistance , AlphaSlope , OmegaSlope , *Function )
		
	EndIf

EndProcedure

Procedure ScanOctant7 ( Type , X , Y , Distance , MaxDistance , AlphaSlope . f , OmegaSlope . f , *Function )

	If Distance > MaxDistance
	
		ProcedureReturn #Null

	EndIf

  	AlphaY = Int ( Y + 0.5 - AlphaSlope * Distance )
  	OmegaY = Int ( Y + 0.5 - OmegaSlope * Distance )
  	
	CheckX = X - Distance

	If Type = #FovRectangular And AlphaY <> Y - ( -Distance )

		CallFunctionFast ( *Function , CheckX , AlphaY , Distance )

	EndIf

	If Type = #FovCircular
	
		AlphaY + 1
	
	EndIf

	Blocked = TileBlocked ( CheckX , AlphaY )

	For CheckY = AlphaY - 1 To OmegaY Step -1

		If Type = #FovCircular And Distance ( X , Y , CheckX , CheckY ) > MaxDistance
	
			Continue
				
		EndIf

		If CheckY <> Y

			CallFunctionFast ( *Function , CheckX , CheckY , Distance )

		EndIf

		If TileBlocked ( CheckX , CheckY )
		
			If Not Blocked

				ScanOctant7 ( Type , X , Y , Distance + 1 , MaxDistance , AlphaSlope , InverseSlope ( X + 0.5 , Y + 0.5 , CheckX + 0.99999 , CheckY + 1.0 ) , *Function )

			EndIf
			
			Blocked = #True
			
		Else 
		
			If Blocked
				
				AlphaSlope = InverseSlope ( X + 0.5 , Y + 0.5 , CheckX , CheckY + 0.99999 )
								
			EndIf
			
			Blocked = #False
			
		EndIf
					
	Next

	If Not Blocked
	
		ScanOctant7 ( Type , X , Y , Distance + 1 , MaxDistance , AlphaSlope , OmegaSlope , *Function )
		
	EndIf

EndProcedure

Procedure ScanOctant8 ( Type , X , Y , Distance , MaxDistance , AlphaSlope . f , OmegaSlope . f , *Function )

	If Distance > MaxDistance
	
		ProcedureReturn #Null

	EndIf

  	AlphaY = Int ( Y + 0.5 - AlphaSlope * Distance )
  	OmegaY = Int ( Y + 0.5 - OmegaSlope * Distance )
  	
	CheckX = X - Distance

	If Type = #FovRectangular And AlphaY <> Y - Distance

		CallFunctionFast ( *Function , CheckX , AlphaY , Distance )

	EndIf

	If Type = #FovCircular
	
		AlphaY - 1
	
	EndIf

	Blocked = TileBlocked ( CheckX , AlphaY )

	For CheckY = AlphaY + 1 To OmegaY

		If Type = #FovCircular And Distance ( X , Y , CheckX , CheckY ) > MaxDistance
	
			Continue
	
		EndIf

		If CheckY <> Y

			CallFunctionFast ( *Function , CheckX , CheckY , Distance )

		EndIf

		If TileBlocked ( CheckX , CheckY )
		
			If Not Blocked

				ScanOctant8 ( Type , X , Y , Distance + 1 , MaxDistance , AlphaSlope , InverseSlope ( X + 0.5 , Y + 0.5 , CheckX + 0.99999 , CheckY - 0.00001 ) , *Function )

			EndIf
			
			Blocked = #True
			
		Else 
		
			If Blocked
				
				AlphaSlope = InverseSlope ( X + 0.5 , Y + 0.5 , CheckX , CheckY )
				
			EndIf
			
			Blocked = #False
			
		EndIf
					
	Next

	If Not Blocked
	
		ScanOctant8 ( Type , X , Y , Distance + 1 , MaxDistance , AlphaSlope , OmegaSlope , *Function )
		
	EndIf

EndProcedure

Procedure ScanNW ( Type , X , Y , MaxDistance , *Function )

	For Counter = 1 To MaxDistance

		If Type = #FovCircular And Distance ( X , Y , X - Counter , Y - Counter ) > MaxDistance
		
			ProcedureReturn #Null
			
		EndIf
		
		CallFunctionFast ( *Function , X - Counter , Y - Counter , Counter )
		
		If TileBlocked ( X - Counter , Y - Counter )
		
			ProcedureReturn #Null
		
		EndIf
	
	Next

EndProcedure

Procedure ScanN ( Type , X , Y , MaxDistance , *Function )

	For Counter = 1 To MaxDistance
		
		CallFunctionFast ( *Function , X , Y - Counter , Counter )
		
		If TileBlocked ( X , Y - Counter )
		
			ProcedureReturn #Null
		
		EndIf
	
	Next

EndProcedure

Procedure ScanNE ( Type , X , Y , MaxDistance , *Function )

	For Counter = 1 To MaxDistance
	
		If Type = #FovCircular And Distance ( X , Y , X + Counter , Y - Counter ) > MaxDistance
		
			ProcedureReturn #Null
			
		EndIf
		
		CallFunctionFast ( *Function , X + Counter , Y - Counter , Counter )
		
		If TileBlocked ( X + Counter , Y - Counter )
		
			ProcedureReturn #Null
		
		EndIf
	
	Next

EndProcedure

Procedure ScanE ( Type , X , Y , MaxDistance , *Function )

	For Counter = 1 To MaxDistance
		
		CallFunctionFast ( *Function , X + Counter , Y , Counter )
		
		If TileBlocked ( X + Counter , Y )
		
			ProcedureReturn #Null
		
		EndIf
	
	Next

EndProcedure

Procedure ScanSE ( Type , X , Y , MaxDistance , *Function )

	For Counter = 1 To MaxDistance

		If Type = #FovCircular And Distance ( X , Y , X + Counter , Y + Counter ) > MaxDistance
		
			ProcedureReturn #Null
		
		EndIf
		
		CallFunctionFast ( *Function , X + Counter , Y + Counter , Counter )
		
		If TileBlocked ( X + Counter , Y + Counter )
		
			ProcedureReturn #Null
			
		EndIf
	
	Next

EndProcedure

Procedure ScanS ( Type , X , Y , MaxDistance , *Function )

	For Counter = 1 To MaxDistance
				
		CallFunctionFast ( *Function , X , Y + Counter , Counter )
		
		If TileBlocked ( X , Y + Counter )
		
			ProcedureReturn #Null
		
		EndIf
	
	Next

EndProcedure

Procedure ScanSW ( Type , X , Y , MaxDistance , *Function )

	For Counter = 1 To MaxDistance

		If Type = #FovCircular And Distance ( X , Y , X - Counter , Y + Counter ) > MaxDistance
		
			ProcedureReturn #Null
	
		EndIf
	
		CallFunctionFast ( *Function , X - Counter , Y + Counter , Counter )
		
		If TileBlocked ( X - Counter , Y + Counter )
		
			ProcedureReturn #Null
		
		EndIf
	
	Next

EndProcedure

Procedure ScanW ( Type , X , Y , MaxDistance , *Function )

	For Counter = 1 To MaxDistance

		CallFunctionFast ( *Function , X - Counter , Y , Counter )

		If TileBlocked ( X - Counter , Y )
		
			ProcedureReturn #Null
		
		EndIf
	
	Next

EndProcedure

Procedure CastLight ( X , Y , Radius )

	*Function = @SetTileLighting ( )

 	ScanNW ( #FovCircular , X , Y , Radius , *Function )
 	ScanN  ( #FovCircular , X , Y , Radius , *Function )
 	ScanNE ( #FovCircular , X , Y , Radius , *Function )
	ScanE  ( #FovCircular , X , Y , Radius , *Function )
	ScanSE ( #FovCircular , X , Y , Radius , *Function )
	ScanS  ( #FovCircular , X , Y , Radius , *Function )
	ScanSW ( #FovCircular , X , Y , Radius , *Function )
	ScanW  ( #FovCircular , X , Y , Radius , *Function )

	ScanOctant1 ( #FovCircular , X , Y , 1 , Radius ,  1.0 , 0 , *Function )
	ScanOctant2 ( #FovCircular , X , Y , 1 , Radius , -1.0 , 0 , *Function )
	ScanOctant3 ( #FovCircular , X , Y , 1 , Radius , -1.0 , 0 , *Function )
	ScanOctant4 ( #FovCircular , X , Y , 1 , Radius ,  1.0 , 0 , *Function )
	ScanOctant5 ( #FovCircular , X , Y , 1 , Radius ,  1.0 , 0 , *Function )
	ScanOctant6 ( #FovCircular , X , Y , 1 , Radius , -1.0 , 0 , *Function )
	ScanOctant7 ( #FovCircular , X , Y , 1 , Radius , -1.0 , 0 , *Function )
	ScanOctant8 ( #FovCircular , X , Y , 1 , Radius ,  1.0 , 0 , *Function )
	
EndProcedure

Procedure CastVision ( X , Y , Distance , Type )

	*Function = @SetTileVisible ( )

	ScanNW ( Type , X , Y , Distance , *Function )
 	ScanN  ( Type , X , Y , Distance , *Function )
 	ScanNE ( Type , X , Y , Distance , *Function )
	ScanE  ( Type , X , Y , Distance , *Function )
	ScanSE ( Type , X , Y , Distance , *Function )
	ScanS  ( Type , X , Y , Distance , *Function )
	ScanSW ( Type , X , Y , Distance , *Function )
	ScanW  ( Type , X , Y , Distance , *Function )

	ScanOctant1 ( Type , X , Y , 1 , Distance ,  1.0 , 0 , *Function )
	ScanOctant2 ( Type , X , Y , 1 , Distance , -1.0 , 0 , *Function )
	ScanOctant3 ( Type , X , Y , 1 , Distance , -1.0 , 0 , *Function )
	ScanOctant4 ( Type , X , Y , 1 , Distance ,  1.0 , 0 , *Function )
	ScanOctant5 ( Type , X , Y , 1 , Distance ,  1.0 , 0 , *Function )
	ScanOctant6 ( Type , X , Y , 1 , Distance , -1.0 , 0 , *Function )
	ScanOctant7 ( Type , X , Y , 1 , Distance , -1.0 , 0 , *Function )
	ScanOctant8 ( Type , X , Y , 1 , Distance ,  1.0 , 0 , *Function )
	
	CastLight ( X , Y , #MaxDistance / 2 )
	
EndProcedure

Procedure Draw ( )

	If StartDrawing ( ImageOutput ( 100 ) )
	
		Box ( 0 , 0 , ImageWidth ( 100 ) , ImageHeight ( 100 ) , #Black )

		Width  = 8
		Height = 8

		For CounterY = PY - 35 To PY + 35
			
			For CounterX = PX - 35 To PX + 35
		
				If PointInRange ( CounterX , CounterY )
						
					If Map ( CounterX , CounterY ) \ Visibility

						TargetX = ( CounterX - PX + 35 ) * Width
						TargetY = ( CounterY - PY + 35 ) * Height
				
						Distance = Map ( CounterX , CounterY ) \ Distance
												
						If Map ( CounterX , CounterY ) \ Terrain = 0 : Color = RGB ( 120 - Distance , 120 - Distance , 120 - Distance ) : EndIf							
						If 	Map ( CounterX , CounterY ) \ Terrain = 1 : Color = RGB ( 180 - Distance , 180 - Distance, 180 - Distance ) : EndIf
					
						Lighting = Map ( CounterX , CounterY ) \ Lighting
					
						If Lighting
					
							Color = RGB ( Red ( Color ) + Lighting , Green ( Color ) + Lighting , Blue ( Color ) + Lighting )
							
						EndIf
																		
						Box ( TargetX , TargetY , Width , Height , Color )
						
					EndIf
												
				EndIf	
	
			Next
		Next
		
		Box ( 35 * Width , 35 * Height , Width , Height , #White )
		
		StopDrawing ( )
	
	EndIf

EndProcedure

OpenWindow ( 0 , 0 , 0 , ImageWidth ( 100 ) , ImageHeight ( 100 ) , "" , #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_ScreenCentered )

If CreateGadgetList ( WindowID ( 0 ) ) : ImageGadget ( 1 , 0 , 0 , 200 , 200 , ImageID ( 100 ) ) : EndIf

MapMake ( ) : CastVision ( PX , PY , #MaxDistance , #FovCircular ) : Changed = #True

Repeat

	Event = WaitWindowEvent ( )

	Select Event
	
		Case #WM_KEYDOWN
		
			Select EventwParam ( )
			
				Case #VK_LEFT
				
					If Not TileBlocked ( PX - 1 , PY ) And PointInRange ( PX - 1 , PY )
						
						PX - 1 : MapForget ( ) : CastVision ( PX , PY , #MaxDistance , 1 ) : Changed = #True
									
					EndIf
			
				Case #VK_RIGHT
				
					If Not TileBlocked ( PX + 1 , PY ) And PointInRange ( PX + 1 , PY )
						
						PX + 1 : MapForget ( ) : CastVision ( PX , PY , #MaxDistance , 1 ) : Changed = #True
									
					EndIf					
				
				Case #VK_UP

					If Not TileBlocked ( PX , PY - 1 ) And PointInRange ( PX , PY - 1 )
						
						PY - 1 : MapForget ( ) : CastVision ( PX , PY , #MaxDistance , 1 ) : Changed = #True
									
					EndIf					
				
				Case #VK_DOWN
				
					If Not TileBlocked ( PX , PY + 1 ) And PointInRange ( PX , PY + 1 )
						
						PY + 1 : MapForget ( ) : CastVision ( PX , PY , #MaxDistance , 1 ) : Changed = #True
									
					EndIf				
			
			EndSelect

	EndSelect
		
	If Changed
		
		Draw ( ) : SetGadgetState ( 1 , ImageID ( 100 ) ) : Changed = #False
		
	EndIf
	
	If Event = #PB_Event_CloseWindow
	
		Break

	EndIf

ForEver
Last edited by eesau on Tue Mar 18, 2008 9:45 am, edited 1 time in total.
User avatar
Rook Zimbabwe
Addict
Addict
Posts: 4322
Joined: Tue Jan 02, 2007 8:16 pm
Location: Cypress TX
Contact:

Post by Rook Zimbabwe »

Nice work eesau!

My only thought is not to have to keep pressing the arrow key to move...

Maybe you could move the "player" first then cast shadows?

Great nonetheless!!! :D
Binarily speaking... it takes 10 to Tango!!!

Image
http://www.bluemesapc.com/
eesau
Enthusiast
Enthusiast
Posts: 589
Joined: Fri Apr 27, 2007 12:38 pm
Location: Finland

Post by eesau »

Rook Zimbabwe wrote:Nice work eesau!

My only thought is not to have to keep pressing the arrow key to move...

Maybe you could move the "player" first then cast shadows?

Great nonetheless!!! :D
Thanks!

To get continual movement, you can change the #WM_KEYUP to #WM_KEYDOWN in the event loop.
Dare
Addict
Addict
Posts: 1965
Joined: Mon May 29, 2006 1:01 am
Location: Outback

Post by Dare »

Thank you!
Dare2 cut down to size
SofT MANiAC
Enthusiast
Enthusiast
Posts: 142
Joined: Mon Sep 17, 2007 10:28 am
Location: P.O.P
Contact:

Post by SofT MANiAC »

great
POiNT.OF.PRESENCE group
MrMat
Enthusiast
Enthusiast
Posts: 762
Joined: Sun Sep 05, 2004 6:27 am
Location: England

Post by MrMat »

Really nice :)
Mat
citystate
Enthusiast
Enthusiast
Posts: 638
Joined: Sun Feb 12, 2006 10:06 pm

Post by citystate »

by adding

Code: Select all

EyeUnCast ( PX , PY )
EyeCast ( PX , PY )   
Changed = #True
just before the Repeat loop, you get an initial snapshot rather than a black screen
there is no sig, only zuul (and the following disclaimer)

WARNING: may be talking out of his hat
eesau
Enthusiast
Enthusiast
Posts: 589
Joined: Fri Apr 27, 2007 12:38 pm
Location: Finland

Post by eesau »

Edit. Edited the code in first post to remove a few bugs, the cast shadows should be correct now. Also made it more versatile so it is easy to add lights for example, and added a circular fov as well.
codeman
User
User
Posts: 27
Joined: Thu Feb 19, 2009 7:12 am

Post by codeman »

How does your algorythmen work?

Could you please explain or at least write some comments to?
I'm sitting here for half the day and trying to understand your way of doing that but I got no idea of how it works
gnasen
Enthusiast
Enthusiast
Posts: 282
Joined: Wed Sep 24, 2008 12:21 am

Post by gnasen »

pb 5.11
Post Reply