`Real-time Matrix/Canyon generator (intemediate tutorial)
`======================
`©CPU
`======================
`*note* many times I will refer to a matrix with x,y coordites, the
`help files refer to them as x,z coordintes.  If you were looking at
`a matrix from the top this is what you would see:
`  help files              Me
`  ____z____           ____y____
` |    |    |         |    |    |
` |    |    |         |    |    |
` -----------x        -----------x
` |    |    |         |    |    |
` |____|____|         |____|____|
 
 
rem this will go to a subroutene that will set up our display mode
rem fonts, system optitions and other things that belong at the
rem very begining of our code
gosub startup
 
rem general varibles
#constant camera_matrix_collision = 1
#constant camera_radius = 15
#constant fogYN = 1
#constant fogcolor = RGB(188,165,129)
#constant fogdis = 2000
 
rem some matrix specific numbers
#constant matrix_tilex = 50
#constant matrix_tiley = 50
#constant matrix_number = 1
#constant matrix_image = 1
#constant matrix_wire_num = 2
#constant matrix_offsetx = 0
#constant matrix_offsety = 0
#constant matrix_offsetz = 0
#constant matrix_width = 1000
#constant matrix_height = 1000
 
rem initialize some vaibles and constants used in the terrain
#constant turnchance = 3
#constant turn_base = 8
#constant turn_dev = 5
#constant hillchance = 7
#constant hill_base = 10
#constant hill_dev = 5
#constant hill_sharpness = 5
#constant narrow_chance = 10
#constant narrow_base = 3
#constant narrow_dev = 4
#constant spikechance = 100
#constant spikeheight = 25
#constant height_min = 50
#constant height_max = 300
#constant width_min = 5
#constant width_max = 25
#constant roughness = 0
 
`build an initial canyon so we don't start out with a flat matrix...
gosub initialCanyonBuild
 
repeat
 
	rem control mouse, if control key is pressed move 7 units at a time
	rem instead of moving 3 units normaly
	if controlkey() = 1
		MouseControl(7)
	else
		MouseControl(3)
	endif
 
	rem if shiftkey is pressed then shift the matrix down and update the canyon
	if shiftkey() = 1
		shift matrix down matrix_number
		gosub canyon
		equate_matrix(matrix_number, matrix_wire_num, matrix_tilex, matrix_tiley)
		update matrix matrix_number
		update matrix matrix_wire_num
	endif
 
	rem position the wireframe overlay if the returnkey is pressed
	if returnkey() = 1
		position matrix matrix_wire_num, matrix_offsetx, matrix_offsety + 0.5, matrix_offsetz
	else
		rem unfortunatly there is no "hide matrix" command
		position matrix matrix_wire_num,-100000,-100000,-100000
	endif
 
	rem display some text and update screen
	gosub ScreenPrompts
	sync
rem exit on escapekey
until escapekey() = 1
end
 
startup:
	rem housecleaning
	set display mode 800,600,32
	sync on
	sync rate 0
	hide mouse
 
	rem makes it so that the camera doesn't intstantly zoom to newly created objects
	autocam off
 
	color backdrop rgb(0,0,0)
	set text size 10
 
	rem turn on fog
	if fogYN = 1 and fog available() = 1
		fog on
		fog color fogcolor
		fog distance fogdis
	endif
 
	rem set the random seed so that the canyon is different every time
	randomize timer()
return
 
`the first time we go through our code we need to initaly setup the canyon
 
initialCanyonBuild:
rem these are varibles used to turn, vary the height and narrow or
rem widen the matrix canyon
global turn as integer = 0
global narrow as integer = 0
global hill as integer = 0
 
rem these are some varibles that hold what height, width, and
rem position(offset) the matrix is updating at
global tilex as integer = 0
global tiley as integer = 0
global offset as integer = 20
global canyon_width as integer = 10
global canyon_height_factor as integer = 100
 
rem create the ground matrix
`load image "ground.jpg", matrix_image,0
GroundTexture(matrix_image)
make matrix matrix_number, matrix_width, matrix_height ,matrix_tilex, matrix_tiley
prepare matrix texture matrix_number,matrix_image, 1, 1
set matrix texture matrix_number, 1, 1
position matrix matrix_number, matrix_offsetx, matrix_offsety, matrix_offset
 
rem make the overlay wireframe matrix
make matrix matrix_wire_num, matrix_width, matrix_height, matrix_tilex, matrix_tiley
position matrix matrix_wire_num, matrix_offsetx, matrix_offsety + 0.5, matrix_offsetz
set matrix wireframe on matrix_wire_num
set matrix priority matrix_wire_num, 1
 
 
rem this generates a canyon so you don't start with a flat matrix
for tiley = 0 to matrix_tiley - 1
   gosub canyon
next tiley
 
rem update the matrixs so that you can see the changes
update matrix matrix_number
equate_matrix(matrix_number, matrix_wire_num, matrix_tilex, matrix_tiley)
update matrix matrix_wire_num
 
rem tiley is the varible that tells the computer which matrix square
rem to update in the y direction
tiley = matrix_tiley - 1
 
rem set the initial position of the camera
position camera 0,150,-500
return
 
canyon:
	rem handel the canyon turning
	gosub turn_canyon
 
	rem hande the changing the height of the canyon
	gosub hill_canyon
 
	rem hande the matrix narrowing and widening
	gosub narrow_canyon
 
	rem randomize the matrix
	for tilex = 0 to matrix_tilex - 1
		set matrix height matrix_number, tilex, tiley, rnd(roughness)
	next tilex
 
	rem handel the canyion turning
	rem form the canyon
	tmp_angle_inc = 180/(canyon_width)
	for tilex = offset to offset + canyon_width
		rem apply basic curve to the height
		height = -1*(sin((tilex - offset)*tmp_angle_inc) * canyon_height_factor)
 
		rem apply spike chance
		if rnd(spikechance) = 1
			height = height + (spikeheight/2.0) + rnd(spikeheight/2.0)
		endif
 
		rem apply roughness
		height = height + rnd(roughness)
		if tilex <= matrix_tilex and tiley <= matrix_tilex and tilex >= 0 and tiley >= 0
			set matrix height matrix_number, tilex, tiley, height
		endif
	next tilex
 
	rem make the far end a "wall"
	for tilex = 0 to matrix_tilex
	   set matrix height matrix_number,tilex,matrix_tiley,0
	next x
return
 
turn_canyon:
   rem make bends in the canyon
   if turn <> 0
      if turn > 0
         rem turn right
         if turn mod 2 = 0
            inc offset
         endif
         dec turn
      else
         rem turn left
         if turn mod 2 = 0
            dec offset
         endif
         inc turn
      endif
   else
   	rem if turnchance = 1 then turn canyon   
      if rnd(turnchance) = 1
      	rem if it equals 1 it's a right turn if it equals 0 its a left one
         if rnd(1) = 1
 
            turn = turn_base + rnd(turn_dev)
            if offset + turn + width_max > matrix_tilex - 1
            	rem if the turn will "exit" the matrix then turn the oposite way
               turn = turn * -1
            endif
         else
            turn = -1 * turn_base - rnd(turn_dev)
            if offset + turn < 1
            	rem if the turn will "exit" the matrix then turn the oposite way
               turn = turn * -1
            endif
         endif
      endif
   endif
return
 
hill_canyon:
   rem make rises and falls in the height of the canyon
   if hill <> 0
      if hill > 0
         rem go down
         inc canyon_height_factor, hill_sharpness
         dec hill
      else
         rem go up
         dec canyon_height_factor, hill_sharpness
         inc hill
      endif
   else
   	rem if hillchance = 1 then raise or lower the canyon
      if rnd(hillchance) = 1
      	rem if it equals 1 it goes down, 0 it goes up
         if rnd(1) = 1
 
            hill = hill_base + rnd(hill_dev)
            if (hill * hill_sharpness)+ canyon_height_factor > height_max
            	rem if the hill will go to low or to high then flip
            	rem the direction of the slope
               hill = hill * -1
            endif
         else
            hill = -1 * hill_base - rnd(hill_dev)
            if (hill * hill_sharpness)+ canyon_height_factor < height_min
            	rem if the hill will go to low or to high then flip
            	rem the direction of the slope
               hill = hill * -1
            endif
         endif
      endif
   endif
return
 
narrow_canyon:
rem narrow or widen the canyion
	if narrow <> 0
		if narrow > 0
			rem widen 
			inc canyon_width
			dec narrow
		else
			rem narrow
			dec canyon_width
			inc narrow
		endif
	else
		if rnd(narrow_chance) = 1
			rem if it equles 1 it gets narrower, 0 it gets wider
			if rnd(1) = 1
				narrow = -1 * narrow_base - rnd(narrow_dev)
				if narrow + canyon_width < width_min
					rem if its going to get to small then make it get bigger
					narrow = narrow * -1
				endif
			else
				narrow = narrow_base + rnd(narrow_dev)
				if narrow + canyon_width > width_max
					rem if it's going to get to wide then make it get smaller
					narrow = narrow * -1
				endif
			endif
		endif
	endif
return
 
ScreenPrompts:
   text 0,0,"Screen FPS = " + str$(screen fps())
   text 0,15,"Press Enter to overlay wireframe"
   text 0,30,"Press Shift to scroll matrix"
   text 0,45,"Use Left Mouse button to move forward"
   text 0,60,"Use Right Mouse button to move backwards"
   text 0,75,"Look using mouse"
return
 
function equate_matrix(matrix_a, matrix_b, xsize, ysize)
	rem this function makes matrix b equal to in form to matrix a
	for n = 0 to xsize
		for m = 0 to ysize
			set matrix height matrix_b, n, m, get matrix height(matrix_a, n, m)
		next m
	next n
endfunction
 
function MouseControl(Speed as float)
	xrotate camera camera angle x()+(mousemovey()/2.0)
	yrotate camera camera angle y()+(mousemovex()/2.0)
	if mouseclick()=1 then move camera Speed
	if mouseclick()=2 then move camera (0-Speed)
 
	rem this handels camera matrix collision
	if camera_matrix_collision = 1
		if camera position y() < get ground height(matrix_number, camera position x(), camera position z())+1
			position camera camera position x(), get ground height(matrix_number, camera position x(), camera position z())+camera_radius, camera position z()
		endif
	endif
endfunction
 
function GroundTexture(imgNum as integer)
	local xSize as integer
	local ySize as integer
 
	local tmpXa as integer
	local tmpYa as integer
	local tmpXb as integer
	local tmpYb as integer
 
	local extream as integer
 
	xSize = 255
	ySize = 255
 
	cls rgb(120,90,70)
	for n = 1 to (xSize+ySize)*50
		extream = -60 + rnd(100)
		ink rgb(110+rnd(20)+extream,80+rnd(20)+extream,60+rnd(20)+extream),0
		circle rnd(xSize), rnd(ySize),3
	next n
 
	for n = 1 to (xSize+ySize)*3
		extream = -60 + rnd(100)
		ink rgb(110+rnd(20)+extream,80+rnd(20)+extream,60+rnd(20)+extream),0
		tmpXa = rnd(xSize)
		tmpYa = rnd(ySize)
		tmpXb = rnd(xSize)
		tmpYb = rnd(ySize)
 
		line tmpXa, tmpYa, tmpXb, tmpYb
 
		extream = -60 + rnd(100)
		ink rgb(110+rnd(20)+extream,80+rnd(20)+extream,60+rnd(20)+extream),0
		tmpXa = rnd(10)
		for x = 1 to 5
			circle rnd(xSize), rnd(ySize),5+tmpXa+x
		next x
	next n
 
	blur bitmap 0,5
 
	get image imgNum,xSize/4,ySize/4,(xSize*3)/4,(ySize*3)/4,0
endfunction