Grid based lightning

BlitzMax Forums/BlitzMax Programming/Grid based lightning

Hezkore(Posted 2011) [#1]
Anyone has any example of a grid based lightning system?

Basically what I have atm. is a world built out of 1's and 0's in an Array, 1 is a wall while 0 is nothing.
And I need to figure out what the player can see, like in older games such as NetHack you can't see behind walls, and the area that you can see lights up.

Ontop of that, I also need some sort of lightning system that's grid based.
Kinda like what Terraria has, if anyone's seen that.


But yeah, you guys think that would be hard to do?
I'm horrible at math, so any help or tips would be much appreciated.

Last edited 2011


Oddball(Posted 2011) [#2]
Rogue Basin has some articles on this.
http://roguebasin.roguelikedevelopment.org/index.php/FOV

And you could also try the LibTCod module which has a function for field of view if you don't want to roll your own. Brucey has a wrapper for it somewhere.


Hezkore(Posted 2011) [#3]
Yeah, I did have a look at LibTCod, but it feels very dirty having that entire thing running in the background.
I prefer having everything made myself specifically for my needs.

Thanks for the link, I'll have a look at it. :)


ima747(Posted 2011) [#4]
A simple hack, that a surprisingly large number of games use is just to have a visibility radius. Don't bother calculating if you can see through a wall or not. Saves a ton of time and the end result is not much different unless you're planning very tight maps with hidden things physically close to expected paths... not exactly what you're looking for but it's easy with less overhead so worth a mention.


Paul "Taiphoz"(Posted 2011) [#5]
How about.

if your game is made up of blocks, say 16*16, they could be any size the block size does not matter, conver your world/map into a 1pixel by 1pixel image, where walls are represented by a red pixel, the player by a blue pixel and empty space by black pixels. kinda like a mini map.

Now draw a line from the blue pixel to the edge of the map, or until it hits a red pixel, do this for the 360 degree's around the player, once your have done this light pass, read the pixel's any you read as white, are places your player can see, get the appropriate block from your world that corresponds to that white pixel and make it visible.

It's the first thing I would try.


Paul "Taiphoz"(Posted 2011) [#6]
OH, I would also set the light value of a pixel, to be half of the light value of all 8 neighbouring pixels, that should glow your light a bit and let it bleed out to darkness.

hope this helps.


SoggyP(Posted 2011) [#7]
Hello.

I've got something for you, let me dig it out...

Goodbye.


SoggyP(Posted 2011) [#8]
Hello.

Here you go, something I knocked up a while ago inspired by my love of Larn - another Meh it went nowhere project :)

It uses someone else's maze code (taken from the forums) and apologies for not crediting, but I can't remember whose. I was only going to use it for testing.

As you'll appreciate, the code is pretty much brute force, not overly efficient and can be made so much better. Commenting would help, but again, Meh, never intended for public release. Be nice.

As with anything I post here, it's free to use BUT I'd like crediting (oh, the irony) and would also appreciate any improvements be posted back onto the site. Be nice.

Everything is self contained, there is extraneous code and well, enjoy.

Strict 
Graphics 1024,768

Local x:Float
Local y:Float
Local mx,my:Float
Global cx,cy:Float

' Map Size Must Not be Odd
Const MaxX = 1024
Const MaxY = 1024
Global lines:Int = 4

Global lampsize:Int = 8

Local size:Int = 16 ' Image Size

Global Beast_Im:TImage = CreateImage(size,size)
Cls
SetColor 0,255,0
DrawOval 0,0,size,size
SetColor 0,192,0
DrawOval 0,0,size,size
GrabImage Beast_Im,0,0

Global Player_Im:TImage= CreateImage(size,size)
Cls
SetColor 255,0,0
DrawOval 0,0,size,size
SetColor 64,0,0
DrawOval 1,1,size-2,size-2
GrabImage(Player_Im,0,0)

Global wall_im:TImage = CreateImage(size,size)
Cls
SetColor 128,64,0
DrawRect 0,0,size,size
For Local i:Int = 0 To 100
	Local x:Int = Rand(size)
	Local y:Int = Rand(size)
	SetColor 240+Rand(32)-16,192+Rand(128)-64,0
	Plot x,y
Next
GrabImage(wall_im, 0,0)

Global floor_im:TImage = CreateImage(size,size)
Cls
SetColor 192,255,192
DrawRect 0,0,size,size
For Local i:Int = 0 To 100
	Local x:Int = Rand(size)
	Local y:Int = Rand(size)
	SetColor 240+Rand(32)-16,192+Rand(128)-64,0
	Plot x,y
Next
GrabImage floor_im,0,0

Global Object_Im:TImage = CreateImage(size,size)
Cls
SetColor 255,255,255
DrawOval 0,0,size,size
SetColor 255,255,0
DrawOval 1,1,size-2,size-2
GrabImage(Object_im,0,0)

Local ccx,ccy:Float
Local mmx,mmy:Float

Local icx,icy,imx,imy:Int

Type PlayerType
	Field x:Int
	Field y:Int
	Field freq:Int
	Field Current:Int
End Type

Global p:playertype = New PlayerType

p.freq = 2
p.Current = p.freq

Type MapType
	Field x:Int
	Field y:Int
	Field Blocked:Int
	Field visible:Int
	Field visited:Int
	Field Done:Int
	Field Dist:Float
	Field Shadow:Float
End Type

Type ObjectType
	Field x:Int
	Field y:Int
	Field Id:Int
End Type

Global ObjectList:TList = New TList

Type BeastType
	Field x:Int
	Field y:Int
	Field xd:Int
	Field yd:Int
	Field freq:Int
	Field Current:Int
End Type

Global BeastList:TList = New TList

Local Maze[,] = MakeMaze(MaxX-1,MaxY-1,0,Rand(10))

Global Map:Maptype[MaxX,MaxY]

Local n:Int

For Local y:Int = 0 To Maxy-1
	For Local x:Int = 0 To MaxX-1
		Map[x,y] = New MapType
		Map[x,y].Visible = False
	Next
Next

For Local y:Int = 0 To MaxY-1
	For Local x:Int = 0 To MaxX-1
		Map[x,y].Blocked = Maze[x,y]
	Next
Next

MakeMap lines

Repeat
	cx = Rand(Maxx-1)
	cy = Rand(MaxY-1)
Until Map[cx,cy].Blocked = False


Local ms1,ms2:Int
Local ems1,ems2:Int

Global los_list:TList = New TList
Global vis_list:TList = New TList

Local ocx,ocy:Int

MakeObjects
MakeBeasts

While Not KeyDown(Key_Escape)
	Cls

	Local MainTimer:Int = MilliSecs()

	p.Current:-1
	If p.Current = 0 Then
		p.Current = p.freq		
		If KeyDown(key_left) Then cx=cx-1
		If KeyDown(key_right) Then cx=cx+1
		If KeyDown(key_up) Then cy=cy-1
		If KeyDown(key_down) Then cy=cy+1
			
		If cx<0 Or cx>MaxX-1 Or cy<0 Or cy>MaxY-1 Or Map[cx,cy].Blocked = True Then
			cx = ocx
			cy = ocy
		End If
	
		icx = Int(cx)
		icy = Int(cy)

	End If
		
	ClearShadows

	If icx<>ocx Or icy<>ocy Then
		ms1 = MilliSecs()
		For Local y:Int = icy-(lampsize+1) To icy+(lampsize+1)
			For Local x:Int = icx-(lampsize+1) To icx+(lampsize+1)
				If x>=0 And x<=MaxX-1 And y>=0 And y<=MaxY-1 Then
					Map[x,y].Visited = 0
					Local dist:Float = Sqr( ((cx-x)*(cx-x)) + ((cy-y)*(cy-y)))
					If dist<lampsize Then
						Map[x,y].Visible = False
						Map[x,y].Dist = dist
						Map[x,y].Shadow = dist
					Else
						Map[x,y].Visited = 1
						Map[x,y].visible = False
						Map[x,y].Dist = lampsize + 1
					End If
				End If
			Next
		Next
	
		vis_list.clear
		
		Local map_item:MapType = New MapType
	
		map_item.x = icx
		map_item.y = icy
		map_item.visible = True
		map_item.visited = 1
		map_item.done = 0
		
		los_list.clear()
		los_list.addfirst map_item
		
		Local counter = 1
		While counter<>0 And Not(KeyDown(key_escape))
			counter = 0
			
			For Local li:MapType = EachIn los_list
				Local lx,ly:Int
				lx = li.x
				ly = li.y
				
				map[lx,ly].visible = True
				If li.done = 0 Then
					li.done = 1
				
					If lx>0 Then
						If Map[lx-1,ly].visited = 0 Then
							map[lx-1,ly].visited = 1
							map[lx-1,ly].visible = True
							If map[lx-1,ly].blocked = False Then
								map_item = New maptype
								map_item.x = lx-1
								map_item.y = ly
								los_list.addlast map_item
							Else
								DrawBlockLineHeader icx,icy,lx-1,ly,1
							End If
						End If
					End If
		
					If lx<MaxX-1 Then
						If Map[lx+1,ly].visited = 0 Then
							map[lx+1,ly].visited = 1
							map[lx+1,ly].visible = True
							If map[lx+1,ly].blocked = False Then
								map_item = New maptype
								map_item.x = lx+1
								map_item.y = ly
								los_list.addlast map_item
							Else
								DrawBlockLineHeader icx,icy,lx+1,ly,1
							End If
						End If
					End If
		
					If ly>0 Then
						If Map[lx,ly-1].visited = 0 Then
							map[lx,ly-1].visited = 1
							map[lx,ly-1].visible = True
							If map[lx,ly-1].blocked = False Then
								map_item = New maptype
								map_item.x = lx
								map_item.y = ly-1
								los_list.addlast map_item
							Else
								DrawBlocklineHeader icx,icy,lx,ly-1,1
							End If
						End If
					End If
		
					If ly<MaxY-1 Then
						If Map[lx,ly+1].visited = 0 Then
							map[lx,ly+1].visited = 1
							map[lx,ly+1].visible = True
							If map[lx,ly+1].blocked = False Then
								map_item = New maptype
								map_item.x = lx
								map_item.y = ly+1
								los_list.addlast map_item
							Else
								DrawBlocklineHeader icx,icy,lx,ly+1,1
							End If
						End If
					End If
							
					counter:+1
				End If
			Next
		Wend
	
	
		For Local vi:MapType = EachIn Vis_List
			Local ix:Int = vi.x
			Local iy:Int = vi.y
		
			If Map[ix,iy].Blocked = True Then
				If ix>0 Then
					If map[ix-1,iy].Visible = True And Map[ix-1,iy].Blocked = False Then Map[ix,iy].Visible = True
				End If
				
				If ix<MaxX- 1 Then
					If map[ix+1,iy].Visible = True And Map[ix+1,iy].Blocked = False Then Map[ix,iy].Visible = True
				End If
				
				If iy>0 Then
					If map[ix,iy-1].Visible = True And Map[ix,iy-1].Blocked = False Then Map[ix,iy].Visible = True
				End If
				
				If iy<MaxY-1 Then
					If map[ix,iy+1].Visible = True And Map[ix,iy+1].Blocked = False Then Map[ix,iy].Visible = True
				End If
			End If
	
		Next
			
		For Local iy:Int = cy-1 To cy+1
			For Local ix:Int = cx-1 To cx+1
				If ix>=0 And ix<MaxX And iy>=0 And iy<MaxY Then
					Map[ix,iy].Visible = True
				End If
			Next
		Next
				
		ems1 = MilliSecs()	
	
		
	End If

	For Local obj:ObjectType = EachIn ObjectList
		If map[obj.x,obj.y].visible = True And map[obj.x,obj.y].dist<5 Then
			If cx<>obj.x Or cy<>obj.y Then
				DrawBlockLineHeader Int(cx),Int(cy),Int(obj.x),Int(obj.y),2
			End If
		End If
	Next
		
	For Local b:BeastType = EachIn BeastList

		b.Current:-1
		If b.Current<=0 Then	
			b.Current = b.freq
			
			Local obx:Int = b.x
			Local oby:Int = b.y
			
			b.x:+b.xd
			b.y:+b.yd
			
			If map[b.x,b.y].blocked = True Then
				b.x = obx
				b.y = oby
				b.xd = Rand(3)-2
				b.yd = Rand(3)-2
				If b.xd = 0 And b.yd = 0 Then b.yd = 1
			End If
			
		End If

		If map[b.x,b.y].visible = True Then 'And map[b.x,b.y].dist<5 Then
			If cx<>b.x Or cy<>b.y Then
				DrawBlockLineHeader Int(cx),Int(cy),Int(b.x),Int(b.y),2
			End If
		End If

	Next
	
	Local EndMainTimer:Int = MilliSecs()
	
	If endMainTimer-MainTimer<50 Then	
		While EndMainTimer-MainTimer<50
			EndMainTimer = MilliSecs()			
		Wend
	Else
		DebugLog "Took longer than 200: " + (EndMainTimer-MainTimer)
	End If
	
	Cls
	
	SetOrigin 512-icx*size, 384-icy*size
	
	SetBlend lightblend
	ms2 = MilliSecs()
	DrawMapArray size
	ems2 = MilliSecs()
	

	SetBlend alphablend
	For Local obj:ObjectType = EachIn ObjectList
		If map[obj.x,obj.y].visible = True Then
			Local col:Float = 255-(map[obj.x,obj.y].dist * (256/lampsize))
			SetColor col,col,col
			DrawImage Object_im,obj.x * size, obj.y * size
		End If
	Next			

	For Local b:BeastType = EachIn BeastList
		If map[b.x,b.y].visible = True Then
			Local col:Float = 255-(map[b.x,b.y].dist * (256/lampsize))
			SetColor col,col,col
			DrawImage Beast_im,b.x * size, b.y * size
		End If
	Next			


	SetColor 255,255,255
	SetBlend alphablend
	DrawImage Player_Im, icx*size, icy*size			

	Flip
	
	ocx = cx
	ocy = cy

	SetOrigin 0,0
	
Wend
End

Function ClearShadows()
For Local y:Int  = cy-(lampsize+1) To cy+(lampsize+1)
	For Local x:Int = cx-(lampsize+1) To cx+(lampsize+1)
		If x>=0 And x<=MaxX-1 And y>=0 And y<=MaxY-1 Then
			If Map[x,y].visible = True Then
				Map[x,y].Shadow = map[x,y].dist
			End If			
		End If
	Next
Next
SetColor 255,255,255
End Function

Function DrawMapArray(size:Int=16)
For Local y:Int  = cy-(lampsize+1) To cy+(lampsize+1)
	For Local x:Int = cx-(lampsize+1) To cx+(lampsize+1)
		If x>=0 And x<=MaxX-1 And y>=0 And y<=MaxY-1 Then
			Local col:Float = 255-(map[x,y].Shadow * (256/lampsize))
			SetColor col,col,col
			If Map[x,y].visible = True Then
				If Map[x,y].Blocked = True Then
					DrawImage wall_im,x * size, y * size
				Else
					DrawImage Floor_im, x * size, y*size
				EndIf
			End If			
		End If
	Next
Next
SetColor 255,255,255
End Function

Function DrawBlockLineHeader(x#,y#,mx#,my#,blocktype:Int)
	' blocktype
	'	1 = Wall
	'	2 = Object
	
	DrawBlockLine2 x * 16 + 8, y * 16 + 8, mx * 16 + 8,my * 16 + 8,blocktype ' Centre
	DrawBlockLine2 x * 16 + 8, y * 16 + 8, mx * 16 + 4,my * 16 + 4,blocktype ' Top Left
	DrawBlockLine2 x * 16 + 8, y * 16 + 8, mx * 16 + 12, my * 16 + 4,blocktype 'Top right
	DrawBlockLine2 x * 16 + 8, y * 16 + 8, mx * 16 + 4,my * 16 + 12,blocktype ' Bottom Left
	DrawBlockLine2 x * 16 + 8, y * 16 + 8, mx * 16 + 12, my * 16 + 12,blocktype 'Bottom right	
End Function

Function DrawBlockLine2(x#,y#,mx#,my#, blocktype:Int)

'If blocktype = 2 Then DebugLog "Entering Blocktype = " + blocktype

Local vecx:Float = mx-x
Local vecy:Float = my-y
Local xs:Float
Local ys:Float
Local draw_it:Int = 0
Local drawn:Int = 0
Local current_x:Float
Local current_y:Float

'If blocktype = 2 Then DebugLog " Vec X: " + vecx + " Vec Y: " + vecy
If vecx<>0 And vecy<>0 Then
       'It's not an even boundary/straight line
       If Abs(vecx)=Abs(vecy) Then
              ' It's an even diagonal line
              xs = vecx/Abs(vecx)
              ys = vecy/Abs(vecy)
              draw_it = 1
       Else
              If Abs(vecx)>Abs(vecy) Then
                     ' XVector is greater
                     xs = (vecx/Abs(vecx))/2
                     ys = (vecy/Abs(vecx))/2
                     draw_it = 1
              Else
                     ' YVector is greater
                     ys = (vecy/Abs(vecy))/2
                     xs = (vecx/Abs(vecy))/2
                     draw_it = 1
              End If
       End If
Else
       'It's an even boundary
       If vecx = 0 And vecy = 0 Then
              xs = 0
              ys = 0
'			If blocktype = 2 Then DebugLog "Both vecx and vecy = 0" ; DebugStop
       ElseIf vecx = 0 Then
              ys = vecy/Abs(vecy)
              xs = 0
              draw_it = 1
'			If blocktype = 2 Then DebugLog "vecx = 0"; DebugStop
       ElseIf vecy = 0 Then
              xs = vecx/Abs(vecx)
              ys = 0
              draw_it = 1
'			If blocktype = 2 Then DebugLog "vecy = 0" ; DebugStop
       End If
End If

Local icx,icy:Int
Local oicx,oicy:Int
Local px,py:Int

px = Int(x/16)
py = Int(y/16)

Local count:Int = 0
Local shadaddValue:Int = 5

'If blocktype=2 Then DebugLog blocktype

If draw_it = 1 Then
	current_x = mx
	current_y = my
	
      While Not drawn
		current_x:+xs
     	      current_y:+ys
		
		icx = Int(current_x/16)
		icy = Int(current_y/16)
		
		If icx<0 Or icx>MaxX-1 Or icy<0 Or icy>MaxY-1 Then
			drawn = True
'			DebugLog "here"
		Else
			If map[icx,icy].dist>lampsize Then 
				drawn = True
'				DebugLog "here2"
			Else
				Select blocktype
					Case 1
						If Map[icx,icy].Visited = False Then
							Map[icx,icy].Visible = False
							Map[icx,icy].Visited = True
							If map[icx,icy].Blocked = True Then
								Local mi:MapType = New MapType
								mi.x = icx
								mi.y = icy
								vis_List.addlast mi
							End If				
						End If
					Case 2
'						DebugLog "here3"
						If oicx<>icx Or oicy<>icy Then
							If map[icx,icy].Blocked = False Then
								Map[icx,icy].Shadow = Map[icx,icy].Dist + shadaddvalue
								shadaddvalue:-1
								If shadaddvalue = 0 Then drawn = True
'							DebugLog "ICX: " + icx + " ICY: " + icy + " Shadow: " + Map[icx,icy].Shadow
								oicx = icx 
								oicy = icy
							Else
								drawn = True
							End If
						End If
				End Select					
			End If
		End If
      Wend
End If
'DebugLog "-------"

End Function

Function MakeBeasts()
For Local i:Int = 0 To MaxY
	Local b:BeastType = New BeastType
	Local x:Int
	Local y:Int
	Local xd:Int
	Local yd:Int
	
	Local done:Int = 0
	While done = 0
		x = Rand(MaxX-1)
		y = Rand(MaxY-1)
		If map[x,y].blocked=False Then done = 1
	Wend
	b.x = x 
	b.y = y
	
	done = 0
	While done = 0
		xd = Rand(3)-2
		yd = Rand(3)-2
		If xd<>0 Or yd<>0 Then done = 1
	Wend
	
	b.xd = xd
	b.yd = yd
	
	b.freq = Rand(10)
	b.Current = b.freq
	
'	DebugLog "XD: " + xd + " YD: " + yd
	BeastList.AddLast b
Next
End Function

Function MakeObjects()
For Local i:Int = 0 To MaxY '* 10
	Local obj:ObjectType =  New ObjectType
	Repeat
		obj.x = Rand(MaxX-1)
		obj.y = Rand(MaxY-1)
	Until Map[obj.x,obj.y].Blocked = False
	
	obj.Id = 1
	
	ObjectList.AddLast obj
Next
End Function

Function MakeMap(lines)
For Local i:Int = 1 To (MaxX)
	Local xs1 = Rand(MaxX-1)
	Local ys1 = Rand(MaxY-1)
	Map[xs1,ys1].Blocked = True
Next
End Function

#MapData
DefData 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
DefData 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
DefData 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
DefData 1,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
DefData 1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
DefData 1,0,1,0,1,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,1
DefData 1,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,1
DefData 1,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,1
DefData 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
DefData 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
DefData 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
DefData 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
DefData 1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1
DefData 1,0,0,0,1,0,1,0,0,0,0,0,1,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1
DefData 1,0,0,0,1,0,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
DefData 1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
DefData 1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,1
DefData 1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1
DefData 1,0,0,0,1,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,1
DefData 1,0,0,0,1,0,0,0,0,0,0,0,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
DefData 1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
DefData 1,0,1,1,1,1,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
DefData 1,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1
DefData 1,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1
DefData 1,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1
DefData 1,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1
DefData 1,0,0,0,0,0,0,0,1,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,1,0,0,0,0,0,0,1
DefData 1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1
DefData 1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1
DefData 1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1
DefData 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1
DefData 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1



'MAKEMAZE()
'Returns a 2D array of integers. 1=wall. 0=open space.
'OPEN tells how wide the space between walls are
'If OPEN is large the mazes tend to look the same
'when EXACT is 1 a maze wall can only make a turn on an odd number cell
'EXACT=1
'#
'###
'  #
'  #
'EXACT=0
'# 
'## 
' ##
'  #
'ex. when EXACT is 5, a wall can only be put on every fifth cell
'#####
'    #
'    #
'    #
'    ######

Function MakeMaze:Int[,](width,height,exact=0,open=1)
	Local fx,fy,allx,ally
	Local go,farx,fary,alldir
	Local dir,ok,f,f2
	Local x,y,x2,y2
	Local did,didmax
	Local mx2,my2
	Local mx=width,my=height
	Global movex[4],movey[4]
	Local cango[4]
	Local candir,candir2

	If exact = 1 Then
		If (my & 1)=0 Or (mx & 1)=0 Then RuntimeError "maze size must be odd for exact mazes"
	EndIf
	If open<1 Then RuntimeError "open must be greater than 0"
	
	movex[0]=+1
	movey[1]=+1
	movex[2]=-1
	movey[3]=-1
	
	Local maze[,]
	maze=New Int[mx+1,my+1]
	For fx=1 To mx
		maze[fx,1]=1
		maze[fx,my]=1
	Next
	For fy=1 To my
		maze[1,fy]=1
		maze[mx,fy]=1
	Next
	farx=mx
	fary=my
	ally=1
	allx=1
	exact:+1
	didmax=mx*my
	mx2=mx+exact+1
	my2=my+exact+1
	Repeat
		Repeat
			allx=Rand(1,mx2)
			ally=Rand(1,my2)
			allx=allx/exact*exact-1
			ally=ally/exact*exact-1
			If allx<1 Then allx=1
			If ally<1 Then ally=1
			If allx>mx Then allx=mx
			If ally>my Then ally=my
			If maze[allx,ally]=1 Then Exit
		Forever
		did:+1
		If did>didmax Then Exit
		x=allx
		y=ally
		dir=Rand(0,3)
		Repeat
			If Rand(didmax)=1 Then Exit
			ok=0
			For candir=0 To 3
				For f=1 To exact+open
					x2=x+movex[candir]*f
					y2=y+movey[candir]*f
					If x2<=0 Or y2<=0 Or x2>mx Or y2>my Then f=9999;Exit
					If maze[x2,y2]=1 Then f=9999;Exit
					For f2=1 To open
						candir2=(candir+1) & 3
						If maze[x2+movex[candir2]*f2,y2+movey[candir2]*f2]=1 Then f=9999;Exit
						candir2=(candir-1) & 3
						If maze[x2+movex[candir2]*f2,y2+movey[candir2]*f2]=1 Then f=9999;Exit
					Next
				Next
				cango[candir]=(f<9999)
				If cango[candir] Then ok=1
			Next
			If ok=0 Then Exit
			Repeat
				dir=(dir+Rand(-1,+1)) & 3
				If cango[dir]=1 Then Exit
			Forever
			For f=1 To exact
				x:+movex[dir]
				y:+movey[dir]
				maze[x,y]=1
			Next
		Forever
	Forever
	Return maze
EndFunction




Hezkore(Posted 2011) [#9]
Whoa, many thanks SoggyP!!

If I do end up using your code you'll surely get all the credits, even if it's just a small fraction of it I'll actually use. :)

Last edited 2011


SoggyP(Posted 2011) [#10]
Hello.

No worries, it's a good feeling being able to help after such a long time.

Goodbye.


Hezkore(Posted 2011) [#11]
Right so... Even after all this help, example code and advice, I STILL can't get it to work correctly!
I have something that "almost" works but is far from perfect.

The problem are all the artifacts you get when viewing stuff from the side.
I've whipped together this example code to illustrate the problem.

Now when you try this code... Remember to firstly draw some floor (left click) and then draw some walls (left click on floor).
You can remove floor and walls with the right mouse buttons.
Move the player around with arrow keys.

Const Grid:Int = 32 'You may change this for bigger levels
Global Map:Int[25, 19, 2] 'X,Y,Layer (Layer 0 is floor while 1 is wall)
Global MouseAction:Int 'Mouse action for removing/adding walls/floors
Global PlayerX:Int = 12
Global PlayerY:Int = 9

Graphics(800, 600, 0, 0, 2)
SetBlend(ALPHABLEND) 'Alpha blend is needed to cover stuff with "shadows"

'Fill the map with floor tiles
For Local Y:Int = 0 To 19 - 1
For Local X:Int = 0 To 25 - 1
	Map[X, Y, 0] = 1
Next
Next

While Not KeyDown(KEY_ESCAPE)
	DoMouseAction() 'Check what the mouse does (debug)
	RenderMap() 'Render the entire map
	RenderFov() 'Throw black boxes over it all for "shadows"
	
	'Player stuff
	DrawRect(PlayerX * Grid, PlayerY * Grid, Grid, Grid)
	If KeyHit(KEY_UP) And Map[PlayerX, PlayerY - 1, 0] And Not Map[PlayerX, PlayerY - 1, 1] Then PlayerY:-1
	If KeyHit(KEY_DOWN) And Map[PlayerX, PlayerY + 1, 0] And Not Map[PlayerX, PlayerY + 1, 1] Then PlayerY:+1
	If KeyHit(KEY_LEFT) And Map[PlayerX - 1, PlayerY, 0] And Not Map[PlayerX - 1, PlayerY, 1] Then PlayerX:-1
	If KeyHit(KEY_RIGHT) And Map[PlayerX + 1, PlayerY, 0] And Not Map[PlayerX + 1, PlayerY, 1] Then PlayerX:+1
	
	'Normal render stuff... :3
	Flip()
	Cls()
Wend

Function RenderMap()
	'Run through the entire array
	For Local Y:Int = 0 To 19 - 1
	For Local X:Int = 0 To 25 - 1
		'Floor
		SetColor(50, 10, 50)
		If Map[X, Y, 0]
			DrawRect(X * Grid, Y * Grid, Grid, Grid)
		EndIf
		
		'Wall
		SetColor(100, 255, 255)
		If Map[X, Y, 1]
			DrawRect(X * Grid, Y * Grid, Grid, Grid)
		EndIf
	Next
	Next
	SetColor(255, 255, 255) 'RESET! \o/
End Function

Function RenderFov()
	'Set the color and alpha for the shadows
	SetColor(0, 0, 0)
	SetAlpha(0.5)
	
	'Loop throught the ENTIRE map array! :o
	'(This should of course be optimized to work with a range instead of the entire array)
	For Local Y:Int = 0 To 19 - 1
	For Local X:Int = 0 To 25 - 1
		If Map[X, Y, 1] Or Map[X, Y, 0] Then 'If there's floor or a wall...
			If Not CheckFov(X, Y) Then DrawRect(X * Grid, Y * Grid, Grid, Grid) 'Check if it's visible, and if it's not... draw a black box!
		EndIf
	Next
	Next
	
	'Reset drawing stuffzorz
	SetAlpha(1)
	SetColor(255, 255, 255)
End Function

Function CheckFov(X:Int, Y:Int)
	'This is stolen from http://roguebasin.roguelikedevelopment.org/index.php/Eligloscode
	'Basically what we do is a "linepick" between the player and the tile
	'and if there's a block in the way we return False, otherwise we return True
	
	Local vx:Float
	Local vy:Float
	Local ox:Float
	Local oy:Float
	Local l:Float
	vx = PlayerX - x
	vy = PlayerY - y
	ox = X + 0.5
	oy = Y + 0.5
	l = Sqr((PlayerX - X) ^ 2 + (PlayerY - Y) ^ 2)
	vx:/l
	vy:/l
	
	For Local i:Int = 0 To l
		If Map(Floor(ox), Floor(oy), 1)
			Return(False)
		EndIf
		ox:+vx
		oy:+vy
	Next
	Return(True)
End Function

Function DoMouseAction() 'This function is unimportant as it's only for debugging...
	If MouseHit(1) Then
		MouseAction = 0
		If Not Map[MouseX() / Grid, MouseY() / Grid, 0] > 0 Then MouseAction = 1;Print "Making floor"
		If Map[MouseX() / Grid, MouseY() / Grid, 0] > 0 Then MouseAction = 2;Print "Making wall"
	EndIf
	If MouseHit(2) Then
		MouseAction = 0
		If Map[MouseX() / Grid, MouseY() / Grid, 0] > 0 Then MouseAction = 3;Print "Removing floor"
		If Map[MouseX() / Grid, MouseY() / Grid, 1] > 0 Then MouseAction = 4;Print "Removing wall"
	EndIf
	If MouseAction < 3 And Not MouseDown(1) Then MouseAction = 0
	If MouseAction >= 3 And Not MouseDown(2) Then MouseAction = 0
	Select MouseAction
		Case 1 Map[MouseX() / Grid, MouseY() / Grid, 0] = 1
		Case 2 Map[MouseX() / Grid, MouseY() / Grid, 1] = 1
		Case 3 Map[MouseX() / Grid, MouseY() / Grid, 0] = Null
		Case 4 Map[MouseX() / Grid, MouseY() / Grid, 1] = Null
	End Select
End Function


Last edited 2011


Hezkore(Posted 2011) [#12]
Here's a picture of the problem for those of you who can't understand my ramblings.
The green areas has some nasty spread shadows...



Last edited 2011

Last edited 2011


Paul "Taiphoz"(Posted 2011) [#13]
..

Last edited 2011


Paul "Taiphoz"(Posted 2011) [#14]
Your code works perfectly on my machine, it may be a bug or driver issue, not sure actually its prety straight forward stuff.

But yeah its working fine here.


Hezkore(Posted 2011) [#15]
Give the code a go again Taiphoz, I solved one issue and updated it... Another issue now. x)


Paul "Taiphoz"(Posted 2011) [#16]



Paul "Taiphoz"(Posted 2011) [#17]
Nope. it's all looking good m8. nothing wrong with the code I am running. and tested after your last update so.. what's the issue ?


Hezkore(Posted 2011) [#18]
If you take one step to the left it should show some nasty jittery shadows.
It's standing near edges that displays it.
Have a look at the picture I posted above (Updated that too heh...)
It's also very visible if you place a wall directly AT the player! :o

Last edited 2011


Paul "Taiphoz"(Posted 2011) [#19]
Take each Pixel that's light, and look around it, if its next to a shadow pixel, then change the light value of that shadow pixel to be HALF of the light pixel its touching.

So, it will render out like this.

[Light][half light][quarter light][black]

Make the values additive, so 1 black pixel between 2 light pixels will become a light pixel, and you will no longer get that jaggy shadow. plus you will get nice blending of your shadows.


Hezkore(Posted 2011) [#20]
I don't mind the shadows being 100% "black" and not fading out.
Adding shading to it wouldn't really "fix" it, it'd just cover it up a bit and I believe the issue would still exist if I did faded shadows, we'd just end up with scattered faded shadows. x)
The issue has to do with the "line picking" not being very precise and my lack of math skills. heh

Last edited 2011


Hezkore(Posted 2011) [#21]
Well... All problems have been solved and I've fully integrated this into my game, much more optimized though of course.
If anyone's interested, here's the updated example with working line-of-sight.

Thanks everyone for the help, and feel free to improve on this if you want.
Post result here though! :)

Const Grid:Int = 32 'You may change this for bigger levels
Global Map:Int[25, 19, 2] 'X,Y,Layer (Layer 0 is floor while 1 is wall)
Global MouseAction:Int 'Mouse action for removing/adding walls/floors
Global PlayerX:Int = 12
Global PlayerY:Int = 9

Graphics(800, 600, 0, 0, 2)
SetBlend(ALPHABLEND) 'Alpha blend is needed to cover stuff with "shadows"

'Fill the map with floor tiles
For Local Y:Int = 0 To 19 - 1
For Local X:Int = 0 To 25 - 1
	Map[X, Y, 0] = 1
Next
Next

While Not KeyDown(KEY_ESCAPE)
	DoMouseAction() 'Check what the mouse does (debug)
	RenderMap() 'Render the entire map
	RenderFov() 'Throw black boxes over it all for "shadows"
	
	'Player stuff
	DrawRect(PlayerX * Grid, PlayerY * Grid, Grid, Grid)
	If KeyHit(KEY_UP) And Map[PlayerX, PlayerY - 1, 0] And Not Map[PlayerX, PlayerY - 1, 1] Then PlayerY:-1
	If KeyHit(KEY_DOWN) And Map[PlayerX, PlayerY + 1, 0] And Not Map[PlayerX, PlayerY + 1, 1] Then PlayerY:+1
	If KeyHit(KEY_LEFT) And Map[PlayerX - 1, PlayerY, 0] And Not Map[PlayerX - 1, PlayerY, 1] Then PlayerX:-1
	If KeyHit(KEY_RIGHT) And Map[PlayerX + 1, PlayerY, 0] And Not Map[PlayerX + 1, PlayerY, 1] Then PlayerX:+1
	
	'Normal render stuff... :3
	Flip()
	Cls()
Wend

Function RenderMap()
	'Run through the entire array
	For Local Y:Int = 0 To 19 - 1
	For Local X:Int = 0 To 25 - 1
		'Floor
		SetColor(50, 10, 50)
		If Map[X, Y, 0]
			DrawRect(X * Grid, Y * Grid, Grid, Grid)
		EndIf
		
		'Wall
		SetColor(100, 255, 255)
		If Map[X, Y, 1]
			DrawRect(X * Grid, Y * Grid, Grid, Grid)
		EndIf
	Next
	Next
	SetColor(255, 255, 255) 'RESET! \o/
End Function

Function RenderFov()
	'Set the color and alpha for the shadows
	SetColor(0, 0, 0)
	SetAlpha(0.5)
	
	Local X:Int
	Local Y:Int
	
	'Loop throught the ENTIRE map array! :o
	'(This should of course be optimized to work with a range instead of the entire array)
	For Y= 0 To 19 - 1
	For X= 0 To 25 - 1
		If Map[X, Y, 1] Or Map[X, Y, 0] Then 'If there's floor or a wall...
			'Check if it's visible, and if it's not... draw a black box!
			If Not CheckFov(X, Y) Then DrawRect(X * Grid, Y * Grid, Grid, Grid)
		EndIf
	Next
	Next
	
	rem Smooth attempt
	SetAlpha(0.5)
	For Y = 0 To 19 - 1
	For X= 0 To 25 - 1
		If Map[X, Y, 1] Or Map[X, Y, 0] Then 'If there's floor or a wall...
			If Not CheckFov(X+1, Y+1) Then DrawRect(X * Grid, Y * Grid, Grid, Grid)
			If Not CheckFov(X-1, Y+1) Then DrawRect(X * Grid, Y * Grid, Grid, Grid)
			If Not CheckFov(X-1, Y-1) Then DrawRect(X * Grid, Y * Grid, Grid, Grid)
			If Not CheckFov(X + 1, Y - 1) Then DrawRect(X * Grid, Y * Grid, Grid, Grid)
		EndIf
	Next
	Next
	endrem
	
	'Reset drawing stuffzorz
	SetAlpha(1)
	SetColor(255, 255, 255)
End Function

Function CheckFov(X:Int, Y:Int)
	Local vx:Float
	Local vy:Float
	Local ox:Float
	Local oy:Float
	Local l:Float
	vx = PlayerX - x
	vy = PlayerY - y
	ox = X + 0.5
	oy = Y + 0.5
	
	l = Max(Abs(PlayerX - X),Abs(PlayerY - Y))
	
	For Local i:Int = 0 To l - 1
		If ..
			Map(Floor(ox+vx*(i/l)-0.0001), Floor(oy+vy*(i/l)-0.0001), 1) Or ..
			Map(Floor(ox+vx*(i/l)+0.0001), Floor(oy+vy*(i/l)-0.0001), 1) Or ..
			Map(Floor(ox+vx*(i/l)+0.0001), Floor(oy+vy*(i/l)+0.0001), 1) Or ..
			Map(Floor(ox+vx*(i/l)-0.0001), Floor(oy+vy*(i/l)+0.0001), 1) ..
		Then Return(False)
	Next
	
	Return(True)
End Function

Function DoMouseAction() 'This function is unimportant as it's only for debugging...
	If MouseHit(1) Then
		MouseAction = 0
		If Not Map[MouseX() / Grid, MouseY() / Grid, 0] > 0 Then MouseAction = 1;Print "Making floor"
		If Map[MouseX() / Grid, MouseY() / Grid, 0] > 0 Then MouseAction = 2;Print "Making wall"
	EndIf
	If MouseHit(2) Then
		MouseAction = 0
		If Map[MouseX() / Grid, MouseY() / Grid, 0] > 0 Then MouseAction = 3;Print "Removing floor"
		If Map[MouseX() / Grid, MouseY() / Grid, 1] > 0 Then MouseAction = 4;Print "Removing wall"
	EndIf
	If MouseAction < 3 And Not MouseDown(1) Then MouseAction = 0
	If MouseAction >= 3 And Not MouseDown(2) Then MouseAction = 0
	Select MouseAction
		Case 1 Map[MouseX() / Grid, MouseY() / Grid, 0] = 1
		Case 2 Map[MouseX() / Grid, MouseY() / Grid, 1] = 1
		Case 3 Map[MouseX() / Grid, MouseY() / Grid, 0] = Null
		Case 4 Map[MouseX() / Grid, MouseY() / Grid, 1] = Null
	End Select
End Function


Last edited 2011