Code archives/Graphics/Water 2D

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

Download source code

Water 2D by Curtastic2006



This is a fun program where you play with water.
You click to make walls. Make it rain and watch the water hit the walls and collect in puddles.


BLITZMAX version with alpha effects:



Blitz3D version:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;WATER 2D. Written by Curtastic.
;CONTROLS:
;Leftclick to make walls.
;Rightclick to erase walls.
;Spacebar or middleclick to make water.
;Enter to make it rain.
;Best run with debug off.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


;if pressure<>0 then water levels are accurate. it runs slower
;the higher the number, the faster and less accurate pressure is
;if pressure is positive then a gap must be at least 2 pixels wide for pressure to work
Const PRESSURE=1

;makes water that falls be sprayish, instead of smooth-slimy. runs a little slower
Const FRAY=1

;shows error messages and stuff
Const DEBUG=0




Const scx=320,scy=240
Graphics scx,scy,0,1


;those rude people who break your stuff.
;actually its to debug, its the amount of particles that are moving.
Global movers


;map=0 =empty
;map=1 =wall
;map=2 =particle
Dim map(scx,scy)

Dim dotmap.dot(scx,scy)

Global image=CreateImage(scx,scy)

;get the transparent color
Global backcolor

SetBuffer ImageBuffer(image)
backcolor=ReadPixel(1,1)

;make border walls
Rect 0,0,scx,scy,0


;make map know border walls
For fx=0 To scx-1
	map(fx,0)=1
	map(fx,scy-1)=1
Next

For fy=0 To scy-1
	map(0,fy)=1
	map(scx-1,fy)=1
Next


Type dot
	Field x,y,moved,presx,blue
End Type


Dim movex(4),movey(4)
movex(0)=1
movex(2)=-1
movey(1)=1
movey(3)=-1


timer=CreateTimer(250)

Local d2.dot


Repeat
	SetBuffer ImageBuffer(image)
	
	controls()
	dotsupdate()

	SetBuffer FrontBuffer()

	drawfront()
	
	;Flip False ;Uncomment if using BlitzPlus
	
	
	WaitTimer timer
	
	If KeyHit(1) Then End
Forever



Function DrawFront()
	DrawBlock image,0,0
	Color 255,0,0
	If DEBUG Then Text 1,1,movers
	Rect MouseX(),MouseY(),3,3

End Function



Function DotsUpdate()
	Local d2.dot

	If DEBUG=0 Then LockBuffer
	If DEBUG Then movers=0
	For d.dot=Each dot
		If d\moved=1 Then
			If DEBUG Then movers=movers+1

				
			If d\y>=scy-3 Then
				;fell to the bottom of the screen
				map(d\x,d\y)=0
				dotmap(d\x,d\y)=Null
				For fy=-1 To 0
					For fx=-1 To +1
						d2=dotmap(d\x+fx,d\y+fy)
						If d2<>Null Then
							d2\moved=1
						EndIf
					Next
				Next
				WritePixelFast d\x,d\y,0
				Delete d
			Else
				If map(d\x,d\y+1)=0 Then
					;nothing under it
		
					;move down
					
					If FRAY Then
						;push other fallers aside to widen the downward stream
						For dir=-1 To +1 Step 2
							d2=dotmap(d\x+dir,d\y+1)
							If d2<>Null Then d2\presx=d2\presx+dir
						Next
	
						If d\presx=0 Then
							dotmove(d,0,1)
						ElseIf map(d\x+Sgn(d\presx),d\y+1)=0 Then
							;move to the side also
							dotmove(d,Sgn(d\presx),1)
							d\presx=0
						Else					
							dotmove(d,0,1)
							d\presx=0
						EndIf
					Else
						dotmove(d,0,1)
					EndIf
				Else
					;something under it
	
					nodir=0
					For go=1 To scx+1
						For dir=-1 To +1 Step 2
							If nodir<>dir Then
								If map(d\x+go*dir,d\y)=1 Then
									If nodir<>0 Then
										;both dirs were nonos. nowhere to teleport to
										d\moved=0
										
										If PRESSURE Then
											If map(d\x,d\y+1)=2 Then
												If Rand(1,PRESSURE)=1 Then
													dotcheckpressure(d)
												EndIf
											EndIf
										EndIf
										
										Goto dotdone
									EndIf
									nodir=dir
								EndIf
								If map(d\x+go*dir,d\y+1)=0 Then
									dotmove(d,go*dir,1)
									Goto dotdone
								ElseIf map(d\x+go*dir,d\y)=0 Then
									d2=dotmap(d\x+go*dir,d\y+1)
									If d2<>Null Then
										If d2\moved=1 Then
											dotmove(d,go*dir,0)
											Goto dotdone
										EndIf
									EndIf
								EndIf
							EndIf
						Next
					Next
					If DEBUG Then error "unreachable code reached"
				EndIf
			EndIf
			
		EndIf
		.dotdone
	Next

	If DEBUG=0 Then UnlockBuffer

End Function




Function DotMove(d.dot,x,y)
	Local d2.dot

	dotmap(d\x,d\y)=Null
	map(d\x,d\y)=0

	If x=0 And y=1 Then
		;common speedup
		For fx=-1 To +1
			d2=dotmap(d\x+fx,d\y-1)
			If d2<>Null Then d2\moved=1
		Next
	Else
		For fy=-1 To +1
			For fx=-1 To +1
				d2=dotmap(d\x+fx,d\y+fy)
				If d2<>Null Then d2\moved=1
			Next
		Next
	EndIf

	WritePixelFast d\x,d\y,backcolor
	
	d\x=d\x+x
	d\y=d\y+y
	dotmap(d\x,d\y)=d
	WritePixelFast d\x,d\y,d\blue
	
	map(d\x,d\y)=2

	d2=dotmap(d\x-1,d\y-1)
	If d2<>Null Then d2\moved=1
	d2=dotmap(d\x,d\y-1)
	If d2<>Null Then d2\moved=1
	d2=dotmap(d\x+1,d\y-1)
	If d2<>Null Then d2\moved=1
	d2=dotmap(d\x-1,d\y)
	If d2<>Null Then d2\moved=1
	d2=dotmap(d\x+1,d\y)
	If d2<>Null Then d2\moved=1
End Function


;this function is only called if PRESSURE<>0
;travels the border of the pool to find a spot that can move up, and is farther down than d.dot
Function DotCheckPressure(d.dot)
	they=d\y+1

	startx=d\x
	starty=d\y

	ang=0
	;0=right, 1=down, 2=left, 3=up
	;"And %11" wraps number to be in range of 0 to 3
	
	x=startx
	y=starty
	
	;scale the outer wall by always trying to move left. so the outer wall will always be on your left
	;for traveling the edge of the the pool, anything that is not a particle is considered a wall
	Repeat

		;try to turn left
		ang2=(ang-1) And %11
		newx=x+movex(ang2)
		newy=y+movey(ang2)
		If map(newx,newy)=2 Then
			x=newx
			y=newy
			ang=ang2
		Else
			;try to go straight
			newx=x+movex(ang)
			newy=y+movey(ang)
			If map(newx,newy)=2 Then
				x=newx
				y=newy
			Else
				;try to turn right
				ang2=(ang+1) And %11
				newx=x+movex(ang2)
				newy=y+movey(ang2)
				If map(newx,newy)=2 Then
					x=newx
					y=newy
					ang=ang2
				Else
					;turn around
					ang=(ang+2) And %11
				EndIf
			EndIf
		EndIf
		
		
		;find a spot where the water is lower and can move up
		If y>they Then
			If ang<>1 Then ;speedup, cant happen when going down
				
				;found what we are looking for
				If map(x,y-1)=0 Then
					dotmove(d, x-d\x, y-1-d\y)
					Return 1
				EndIf
				;check sides
				If PRESSURE<0 Then
					For fx=-1 To +1 Step 2
						If map(x+fx,y)=0 Then
							dotmove(d, x+fx-d\x, y-d\y)
							Return 1
						EndIf
					Next
				EndIf
			EndIf
		EndIf
		If DEBUG Then If KeyDown(2) Then WritePixel x,y,$FF0000+Rnd(255)
		
		;just went in a big circle around the pool
		If x=startx And y=starty Then Return 0
	Forever
End Function



Function Controls()
	Local d2.dot
	
	;Enter to make rain.
	If KeyDown(28) Then
		For f=1 To 10
			d.dot=New dot
			d\x=Rnd(scx-3)+1
			d\y=1
			If map(d\x,d\y)>0 Then
				Delete d
			Else
				d\blue=Rnd(100,255)
				d\moved=1
				dotmap(d\x,d\y)=d
				map(d\x,d\y)=2
				WritePixel d\x,d\y,d\blue
				dotmove(d,0,0)
			EndIf
		Next
	EndIf
	
	;Make water from the mouse.
	If KeyDown(57) Or MouseDown(3) Then
		d.dot=New dot
		d\x=MouseX()
		d\y=MouseY()
		If d\x<1 Then d\x=1
		If d\y<1 Then d\y=1
		If d\x>scx-2 Then d\x=scx-2
		If d\y>scy-2 Then d\y=scy-2
		If map(d\x,d\y)>0 Then
			Delete d
		Else
			d\blue=Rnd(100,255)
			d\moved=1
			dotmap(d\x,d\y)=d
			map(d\x,d\y)=2
			WritePixel d\x,d\y,d\blue
			dotmove(d,0,0)
		EndIf
	EndIf
	
	;Draw terrain.
	If MouseDown(1) Then
		mx=MouseX()
		my=MouseY()
		For fy=0 To 5
			For fx=0 To 5
				If inscreen(mx+fx,my+fy,1) Then
					If map(mx+fx,my+fy)=0 Then
						map(mx+fx,my+fy)=1
						WritePixel mx+fx,my+fy,Rnd(55+fy*20,155+fy*20) Shl 8
					EndIf
				EndIf
			Next
		Next
	EndIf
	
	;Erase terrain.
	If MouseDown(2) Then
		mx=MouseX()
		my=MouseY()
		For fy=0 To 5
			For fx=0 To 5
				If inscreen(mx+fx,my+fy,1) Then
					If map(mx+fx,my+fy)=1 Then
						WritePixel mx+fx,my+fy,backcolor
						map(mx+fx,my+fy)=0
						For fy2=-1 To +1
							For fx2=-1 To +1
								d2=dotmap(mx+fx+fx2,my+fy+fy2)
								If d2<>Null Then
									d2\moved=1
								EndIf
							Next
						Next
					EndIf
				EndIf
			Next
		Next
	EndIf
End Function



;used for controls only
Function InScreen(x,y,closer=0)
	Return x>=closer And y>=closer And x<scx-closer And y<scy-closer
End Function




;this function is changed to be WritePixelFast when DEBUG is on
Function WritePixelFast2(x,y,c)
	If inscreen(x,y,1)=0 Then
		If DEBUG=0 Then UnlockBuffer
		error x+" "+y
	EndIf
	WritePixel x,y,c
End Function


Function Error(s$)
	If DEBUG=0 Then UnlockBuffer
	RuntimeError "Error: "+s
End Function

Comments

Réno2006
Nice !


kragoth742006
Cool. But you need to add a "flip" at the end of the "DrawFront" function to make it work with BlitzPlus. ;-)


Curtastic2006
Thanks! Updated.


Pineapple2007
Curtastic, you have no idea how much this code has helped me - thanks so much


Yo! Wazzup?2007
I just played with this for about 1/2 an hour... I wish I had screenshots to show you what I did...


syntax2007
This thing is pretty frecken awsome! Great great job!


Xzider2007
Thats awesome, Im still having fun.


Kyler2007
Very cool!


Cold Storage2010
Nice quick code... ;O)

Reminds me more of sand than water though?
Either way, great job!


Code Archives Forum