REM ***********************************************
REM Title: Tilemap tutorial
REM Author: Phaelax
REM Downloaded from: http://dbcc.zimnox.com/
REM ***********************************************
 
SetErrorMode(2)
SetWindowTitle( "tut_tilemap" )
SetWindowSize(640,480, 0 )
SetVirtualResolution( 640,480)
SetSyncRate(60, 0 )
UseNewDefaultFonts( 1 )
 
 
Global yellow = 0 : yellow = makeColor(255,255,0)
 
 
Type UDT_Tile
	id  as integer
EndType
 
 
Type UDT_Map
	width      as integer
	height     as integer
	tileSize   as integer
	view       as integer[0,0]
	data       as UDT_Tile[0,0]
	coll	   as integer[0]
	x	       as float
	y          as float
	maxX       as integer
	maxY       as integer
	viewWidth  as integer
	viewHeight as integer
EndType
 
 
Type UDT_Guy
	x     as float
	y     as float
	spr   as integer
	speed as float
EndType
 
 
map as UDT_Map
 
guy as UDT_Guy
 
 
 
// Open file for reading
f = openToRead("map3.txt")
 
// Read first line
r$ = readLine(f)
map.width    = val(getStringToken2(r$, ",", 1))
map.height   = val(getStringToken2(r$, ",", 2))
map.tileSize = val(getStringToken2(r$, ",", 3))
imgFilename$ = getStringToken2(r$, ",", 4)
 
// Get number of tiles
total = countStringTokens2(r$, ",") - 4
map.coll.length = total
// Because the animation frames start at 1 and not 0,
// in this case we'll also start the array at 1. It's 
// only used as a lookup for collision this'll keep simple.
for i = 1 to map.coll.length
	map.coll[i] = val(getStringToken2(r$, ",", i+4))
next i
 
 
// Redfine the size of the 2D array
map.data.length = map.width
 
for x = 0 to map.width-1
	map.data[x].length = map.height
next x
 
// Load map data
for y = 0 to map.height-1
	r$ = readLine(f)
	for x = 0 to map.width-1
		map.data[x, y].id = val(getStringToken2(r$, ",", x+1))
	next
next y
 
// Don't forget to close the file
closeFile(f)
 
 
// Calculate number of tiles that can be shown on screen
map.viewWidth   = ceil((getVirtualWidth()+0.0)  / map.tileSize) + 1
map.viewHeight  = ceil((getVirtualheight()+0.0) / map.tileSize) + 1 
 
map.view.length = map.viewWidth
 
for x = 0 to map.viewWidth-1
	map.view[x].length = map.viewHeight
next x
 
 
 
// Calculate the max boundaries for map scrolling
map.maxX = (map.width  * map.tileSize) - getVirtualWidth()
map.maxY = (map.height * map.tileSize) - getVirtualheight()
 
// The base of all tile sprites used in this map
spr_base = createSprite(loadImage(imgFilename$))
 
// Number of tiles in this tileset
tileCount = (getSpriteWidth(spr_base)  / map.tileSize) * (getSpriteHeight(spr_base) / map.tileSize)
 
setSpriteAnimation(spr_base, map.tileSize, map.tileSize, tileCount)
 
// Position the map tiles
for y = 0 to map.viewHeight-1
	for x = 0 to map.viewWidth-1
		map.view[x, y] = cloneSprite(spr_base)
		setSpriteFrame(map.view[x, y], map.data[x, y].id)
		setSpritePosition(map.view[x, y], x*map.tileSize, y*map.tileSize)
	next
next y
 
setSpriteVisible(spr_base, 0) // don't need to see this
 
 
// Create a periwinkle-colored 32x32 sprite
guy.spr = createSprite(0)
setSpriteSize(guy.spr, 32, 32)
setSpriteColor(guy.spr, 128,128, 255, 255)
 
// How fast the character can move
guy.speed = 5
// Starting position, center of screen
guy.x = getVirtualWidth() / 2
guy.y = getVirtualHeight() / 2
 
 
// How close the player can be to the edge of the screen 
// before it starts scrolling the map.
edgeBuffer = 48
 
do
 
 
    // Use arrow keys to scroll the map
    if getRawKeyState(37) // left
		moveGuy(guy, -guy.speed, 0, map)
	endif
	if getRawKeyState(39) // right
		moveGuy(guy, guy.speed, 0, map)
	endif
    if getRawKeyState(38) // up
		moveGuy(guy, 0, -guy.speed, map)
	endif
	if getRawKeyState(40) // down
		moveGuy(guy, 0, guy.speed, map)
	endif
 
 
 
 
 
	// Boundaries to keep guy on the screen.
	if guy.x < 0 then guy.x = 0
	if guy.x > getVirtualWidth()-getSpriteWidth(guy.spr) then guy.x = getVirtualWidth()-getSpriteWidth(guy.spr)
	if guy.y < 0 then guy.y = 0
	if guy.y > getVirtualHeight()-getSpriteHeight(guy.spr) then guy.y = getVirtualHeight()-getSpriteHeight(guy.spr)
 
 
 
	if guy.x < edgeBuffer
		setMapPosition(map.x-guy.speed, map.y, map)
		if map.x > 0 then guy.x = edgeBuffer
	endif
	if guy.x > getVirtualWidth()-getSpriteWidth(guy.spr)-edgeBuffer
		setMapPosition(map.x+guy.speed, map.y, map)
		if map.x < map.maxX then guy.x = getVirtualWidth()-getSpriteWidth(guy.spr)-edgeBuffer
	endif
	if guy.y < edgeBuffer
		setMapPosition(map.x, map.y-guy.speed, map)
		if map.y > 0 then guy.y = edgeBuffer
	endif
	if guy.y > getVirtualHeight()-getSpriteHeight(guy.spr)-edgeBuffer
		setMapPosition(map.x, map.y+guy.speed, map)
		if map.y < map.maxY then guy.y = getVirtualHeight()-getSpriteHeight(guy.spr)-edgeBuffer
	endif
 
	setSpritePosition(guy.spr, guy.x, guy.y)
 
 
 
	// Displays the world tile coordinates under the mouse
	mx = floor((getRawMouseX() + map.x) / map.tileSize)
	my = floor((getRawMouseY() + map.y) / map.tileSize)
 
	print(str(mx)+":"+str(my))
 
 
    Sync()
loop
 
 
 
function getWorldX(x, m ref as UDT_Map)
	tx = floor((x + m.x) / m.tileSize)
endfunction tx
 
 
function getWorldY(y, m ref as UDT_Map)
	ty = floor((y + m.y) / m.tileSize)
endfunction ty
 
 
 
function moveGuy(g ref as UDT_Guy, vx, vy, m ref as UDT_Map)
	w = getSpriteWidth(g.spr)
	h = getSpriteHeight(g.spr)
 
	a = checkPoint(g.x+vx,   g.y+vy, m)   // top left
	b = checkPoint(g.x+vx+w, g.y+vy, m)   // top right
	c = checkPoint(g.x+vx,   g.y+vy+h, m) // bottom left
	d = checkPoint(g.x+vx+w, g.y+vy+h, m) // bottom right
 
 
	if a+b+c+d = 0
		g.x = g.x + vx
		g.y = g.y + vy
	endif
 
endfunction
 
 
function checkPoint(cx, cy, m ref as UDT_Map)
	// Get this point in world coordinates
	tx = getWorldX(cx, m)
	ty = getWorldY(cy, m)
 
	if tx < 0 or ty < 0 or tx > m.width-1 or ty > m.height-1 then exitfunction 0
 
	// Determine the tile in which this point lies
	frame = m.data[tx,ty].id
 
	// Return 0 or 1, if the tile is passable or not.
	r = m.coll[frame]
 
 
	// Strictly for showing the collision with tiles
	if r = 1
		bx = (tx*m.tileSize) - m.x
		by = (ty*m.tileSize) - m.y
		drawBox(bx, by, bx+m.tileSize, by+m.tileSize, yellow, yellow, yellow, yellow, 0)
	endif
 
endfunction	r
 
 
 
 
 
function setMapPosition(mx, my, m ref as UDT_Map)
	m.x = mx
	m.y = my
 
	// Boundary checks to avoid scrolling beyond the map
	if m.x < 0 then m.x = 0
	if m.y < 0 then m.y = 0
	if m.x > m.maxX then m.x = m.maxX
	if m.y > m.maxY then m.y = m.maxY
 
	for y = 0 to m.viewHeight-1
		for x = 0 to m.viewWidth-1
 
			// Local offsets for viewport tiles
			ox = x*m.tileSize - mod(m.x, m.tileSize)
			oy = y*m.tileSize - mod(m.y, m.tileSize)
 
			// World offsets for map data tiles
			wx = floor(m.x / m.tileSize)
			wy = floor(m.y / m.tileSize)
 
			// Boundary check for edge cases
			if x+wx < m.data.length and y+wy < m.data[x+wx].length
				// Update viewport tiles with correct tile information
				setSpriteFrame(m.view[x,y], m.data[x+wx, y+wy].id)
			endif
			// Position viewport tiles
			setSpritePosition(m.view[x, y], ox, oy)
		next
	next y
endfunction