--boxEngine
--reddit.com/r/boxengine 	<- for discussion!
--Doc.txt					<- boxEngine documentation!

--todo support: load .obj files [perhaps some weird trick with a lone vertex to specify center, else calculated center of bounding box]
--optional todo: edit mode and move mode with simple physics, loading multiple maps!!, simple enemies/entities, continue gameplay

--So, need to add an assortment of features QUICK, plan for 4-5 days.  priority list:
-- weapons, entities!  entities unloaded with blocks

--History!  this project was started: Thursday, November 03, 2011

dofile "data/scripts/lib/bxeGeneral.lua"
dofile "data/scripts/lib/bxeMath.lua"
dofile "data/scripts/lib/classes.lua"

metaRots = {}

dofile "data/scripts/scData/defineRots.lua"


-- #################### Tiles / Materials ###############

gTileNames = {}
gMaterials = {}

--first param of inVals is 'type'
--type: 1 is defined texture, rgba color, blendmode, useLights
--type: 2 is defined shader, texture

function DefineMaterial( inVals )
	insert( gMaterials, inVals )
	
end

function UseMaterial( inIndex, inPass )
	local retV = false
	if ( inPass == -1 ) then retV = true end
	
	local mat = gMaterials[ inIndex + 1 ]
	if ( mat[ 1 ] == 1 ) then
		local bMode = mat[ 4 ]
		
		local modes = { { 0, 1, 1 }, { 1, 1, 1 }, { 1, 1, 0 }, { 1, 0, 0 } }
		local wPass = { 1, 1, 2, 2 }
		if ( ( wPass[ bMode ] == inPass or retV ) ) then
			retV = true
			Tex( mat[ 2 ] )
			Shad( -1 )
			local c = mat[ 3 ]
			if ( #c > 0 ) then
				Color( c[ 1 ], c[ 2 ], c[ 3 ], c[ 4 ] )
			end
			local mode = modes[ bMode ]
			Blend( mode[ 1 ] )
			SetDepth( mode[ 2 ], mode[ 3 ] )
			SetLighting( mat[ 5 ] )
		end
		
	elseif ( mat[ 1 ] == 2 ) then
		if ( inPass == 1 or inPass == -1 ) then
			Blend( 0 )
			Shad( mat[ 2 ] )
			Tex( mat[ 3 ] )
			retV = true
			
			
		end
	end
	return retV
end

dofile "data/scripts/scData/scGeom.lua"

gNumMaterials = #gMaterials

-- #################### Blocks ############################

gPlBoundsShift = 0

gNDSA = 3
gBlockDirSize = { gNDSA, gNDSA, gNDSA }
--gBlockDirSize = { 1, 1, 1 }
gBlocks = {}
gCurBlock = nil
gCurBlockCords = { 0, 0, 0 }

gRootBlockCord = { 0, 0, 0 }

--used for blocking out main area!!
gStartBlockCord = { 0, 0, 0 }

gDirSize = 32 * 1
gMouseSens = 9.0

gLightColorAtten = false

gGenData = {}

gDrawOutlines = true

-- ################### Edit / Cam ###########################

gCamDist = 10
gCursorAxis = { 1, 2 }
gCursorMod = { 1, 1 }
gCurTileI = 0
gRotI = 0
gCamPos = { 0, 0, 0 }
gCurMatI = 0

gCursor = { int( .5 * gDirSize ), int( .5 * gDirSize ), int( .5 * gDirSize ) }
--gCursor = { 0, 0, 0 }
gMouseClick = { false, false, false }

gRots = { 0, 0 }

gPrevMouse = { 0, 0 }

gKeyCtrl = false
gKeyShift = false

gEnableSaving = true--false

gSeed = 2--RandInt( 1, 500 )
--print ('seed', gSeed)

gPresets = { }

gLocalBorder = true

gCamMode = 0
gPlCord = { 0, 0, 0 }
gPlAng = { 0, 0 }
gPlPos = { .5 * gDirSize, .5 * gDirSize, .5 * gDirSize }

gPlGravity = 1.0
gPlJumpSpd = 1.0
gMaxZFallSpd = 3.0
plSpeedUp = .5
gPlSpd = { 0, 0, 0 }

--horizontal, negZ, posZ
gPlRadius = { .4, .4, .4 }

gPlIsOnGround = false

gPlMovePressed = { false, false, false, false, false, false }


gPlNextFire = 0
gProjectiles = {}

gEntities = {}


gRemObjs = {}

function RemoveObject( inIndex )
	insert( gRemObjs, 1, inIndex )
end


function ProcessRemove( inTable )
	
	for i, v in pairs( gRemObjs ) do
		table.remove( inTable, v )
	end
	
	gRemObjs = {}
end


for i = 1,5 do
	gPresets[ i ] = { 0, 0, 0 }
end

function FileExists( inFName )
	local file = io.open( inFName )
	if file then
		io.close(file)
		return true
	else
		return false
	end

end

Block = class()

function Block : Init( inSelf, inX, inY, inZ )
	if( inSelf == nil ) then
		inSelf = self
	end
	--inSelf : Reset( inSelf, inT, inPos )
	self.x = inX
	self.y = inY
	self.z = inZ
	
	self.meshI = {}
	self.dirty = false
	self.zeros = true
	
	self.mapData = {}
	
	for x = 1,gDirSize do
		for y = 1,gDirSize do
			for z = 1,gDirSize do
				local val = 0
				insert( self.mapData, { val, 0, 1 } )
			end
		end
	end
	
	
end

function Block : ClearMapData()
	if ( self.zeros == false ) then
		local n = #self.mapData
		for i = 1, n do
			local data = self.mapData[ i ]
			data[ 1 ] = 0
			data[ 2 ] = 0
			data[ 3 ] = 0
		end
		self.zeros = true
	end
end


function Block : ClearMeshs()
	self.meshI = {}
	for i = 1, gNumMaterials do
		insert( self.meshI, NewGeom() )
		--print( 'new', self.meshI[ i ] )
		--self.meshI[ i ] = NewGeom()
	end

end


function Block : GetDataPtr( inX, inY, inZ )
	local i = inX * pow( gDirSize, 2 ) + inY * gDirSize + inZ
	return self.mapData[ i + 1 ]
end

function Block : PlaceAllGeom()
	--temporarily make new mapI, until we have a 'clear' function
	self : ClearMeshs()
	--print( 'inplaceallgeom' )
	for x = 0,gDirSize - 1 do
		for y = 0,gDirSize - 1 do
			for z = 0,gDirSize - 1 do
				local data = self : GetDataPtr( x, y, z )
				--if ( x == 0 and y == 0 and z == 0 ) then print( data[ 1 ] ) end
				if data[ 1 ] > 0 then
					self : PlaceTile( x, y, z, data[ 1 ], data[ 2 ], data[ 3 ] )
				end
			end
		end
	end
	

end

function Block : PlaceTile( x, y, z, tileI, rotI, matI )
	GPush()
	GMove( x * 1, y * 1, z * 1 )
	GScale( .5 )
	
	local rotVals = GetRotVals( rotI )
	local nr = # rotVals
	for i = 1, nr do
		local v = rotVals[ i ]
		GRotate( v[ 1 ], v[ 2 ], v[ 3 ], v[ 4 ] )
	end
	
	Place( 'tiles', gTileNames[ tileI ], self.meshI[ matI ] )
	--print( 'placing', self.meshI[ matI ] )
	
	GPop()
end

function Block : SetTile( x, y, z, tileI, rotI, matI )
	local data = self : GetDataPtr( x, y, z )
	
	data[ 1 ] = tileI
	data[ 2 ] = rotI
	data[ 3 ] = matI
	
	self.zeros = false
end

function Block : SetPlaceTile( x, y, z, tileI, rotI, matI )
	
	self : SetTile( x, y, z, tileI, rotI, matI )
	self : PlaceTile( x, y, z, tileI, rotI, matI )

end

function Block : Draw( inMatI )
	
	Geom( self.meshI[ inMatI ] )
	
end

function Block : SaveToFile()
	
	
	if ( gEnableSaving == false or self.dirty == false ) then
		return
	end
	
	
	
	local fname = string.format( 'data/maps/default/map%i_%i_%i.bxm', self.x, self.y, self.z )
	
	io.output(io.open( fname, 'wb' ))
	io.write( "1 1\n" )	--format and version
	io.write( string.format( "%i\n", gDirSize ) )	--format and version
	local index = 1
	for x = 0,gDirSize - 1 do
		for y = 0,gDirSize - 1 do
			for z = 0,gDirSize - 1 do
				data = self.mapData[ index ]
				if ( data[ 1 ] > 0 ) then
					-- x y z tile rot mat
					io.write( string.format( "%i %i %i %i\n", index, data[ 1 ], data[ 2 ], data[ 3 ] ) )
					--print ( 'st: ', x, y, z, data[ 1 ], data[ 2 ], data [ 3 ] )
				end
				index = index + 1
			end
		end
	end
	io.close()
	self.dirty = false

end

function InitGenData()
	--inits/resets gGenData
	local i = 1
	for x = 0,gDirSize - 1 do
		for y = 0,gDirSize - 1 do
			for z = 0,gDirSize - 1 do
				gGenData[ i ] = 0
				i = i + 1
			end
		end
	end
	



end

function Block : GenerateBlockContent()
	
	InitGenData()
	
	
	local objs = { 1, 11, 12, 15, 16 }
	local mats = { 1, 2, 3, 4 }
	local mx = self.x * gDirSize
	local my = self.y * gDirSize
	local mz = self.z * gDirSize
	--print ( mx, my ,mz )
	local i = 1
	for x = 0,gDirSize - 1 do
		for y = 0,gDirSize - 1 do
			for z = 0,gDirSize - 1 do
				--local data = self : GetDataPtr( x, y, z )
				local obj = 0
				local matI = 0--RandInt( 1, #mats )--randint( 0, 10 )
				local gx = mx + x
				local gy = my + y
				local gz = mz + z
				--if ( x == 0 or x == gDirSize - 1 or z % 3 == 0 ) then obj = objs[ RandInt( 1, #objs ) ] end
				scl1 = .016 * 3
				o1 = 0
				scl2 = .015 * .7
				o2 = 100
				scl3 = .1
				o3 = 300
				scl4 = .05 * .5
				o4 = 1300
				scl5 = .05 * .5
				o5 = -1300
				scl6a = .05
				scl6b = .005
				o6 = 70
				v1 = Noise( scl1 * gx + o1, scl1 * gy + o1, scl1 * gz + o1 )
				v2 = Noise( scl2 * gx + o2, scl2 * gy + o2, scl2 * gz + o2 )
				v3 = Noise( scl3 * gx + o3, scl3 * gy + o3, scl3 * gz + o3 )
				v4 = Noise( scl4 * gx + o4, scl4 * gy + o4, scl4 * gz + o4 )
				v5 = Noise( scl5 * gx + o5, scl5 * gy + o5, scl5 * gz + o5 )
				v6 = Noise( scl6a * gx + o6, scl6a * gy + o6, scl6b * gz + o6 )
				
				
				if ( v1 + v2 + max( 0, v4 * v3 ) > .13 + v5 * .5 and ( v1 < v5 + .05 )  ) then
					gGenData[ i ] = 1
					if ( v2 > v4 and v1 > 0 ) then
						gGenData[ i ] = 3
					end
					
				end
				
				if ( ( v2 < -.05 and ( v4 < v2 and v1 - .15 < v5 - .5 * v4 ) and v1 + .05 > v3 ) or v6 > .2  ) then
					gGenData[ i ] = 2 + int( .2 * x + .1 * y + .2 * z ) % 2
					
				end
				
				if ( v1 < v4 + .05) then
					gGenData[ i ] = 0
				end
				--if ( v > .35 + metv and v < .35 + metv + .05 ) then
				--	obj = 1
				--	matI = mats[ 1 + int( v * 20 + 80 ) % 4 ]
				--end
				--if ( ( gx + gy ) % 10 < gz % 5 ) then obj = 1 end
				--if ( gz % 30 == 0 ) then obj = 2; matI = 10 end
				
				---if ( obj > 0 ) then
				---	self : SetPlaceTile( x, y, z, obj, 0, matI )
				--end
				i = i + 1
			end
		end
	end
	
	--for localborder
	local xoff = self.x - gStartBlockCord[ 1 ]
	local yoff = self.y - gStartBlockCord[ 2 ]
	local zoff = self.z - gStartBlockCord[ 3 ]
	local inBorder = true
	if ( xoff < 0 or xoff > gBlockDirSize[ 1 ] - 1 or
		 yoff < 0 or yoff > gBlockDirSize[ 2 ] - 1 or
		 zoff < 0 or zoff > gBlockDirSize[ 3 ] - 1 ) then
		 inBorder = false
	end
	
	
	i = 1
	for x = 0,gDirSize - 1 do
		for y = 0,gDirSize - 1 do
			for z = 0,gDirSize - 1 do
				
				if ( gLocalBorder and inBorder ) then
					if ( 
						( z == 0 and zoff == 0 ) or 
						( z == gDirSize - 1 and zoff == gBlockDirSize[ 3 ] - 1 ) or 
						( y == 0 and yoff == 0 and xoff ~= 1 ) or 
						( y == gDirSize - 1 and yoff == gBlockDirSize[ 2 ] - 1 ) or  
						( x == 0 and xoff == 0 ) or 
						( x == gDirSize - 1 and xoff == gBlockDirSize[ 1 ] - 1 ) 
						
					) then
						gGenData[ i ] = 1
						if ( int( .3 * z ) % 2 == 1 ) then
						
							gGenData[ i ] = int( .1 * ( z + 4 * sin( .4 * x ) ) ) % 6 + 1 + 4
						end
						
						if ( z == 0 and zoff == 0 ) then
							gGenData[ i ] = int( .2 * ( x * y * .1 ) ) % 3 + 1
						end
					end
				end
				
				if ( gGenData[ i ] > 0 ) then
					local d = gGenData[ i ]
					local o = 1
					local r = 0
					if ( z % 4 < 2 ) then
						o = 15
						r = 0 + ( int( .2 * x + .3 * y  ) % 4 ) * 5
					
					end
					if ( d == 3 and rand( 0, 1 ) > .7 ) then
						o = 11
						r = randint( 0, 23 )
						d = randint( 4, 6 )
						
					end
					self : SetPlaceTile( x, y, z, o, r, d )
				end
				i = i + 1
			end
		end
	end
	
	self.dirty = true
	self.zeros = false
	
	

end

function Block : LoadFromFile()
	local fname = string.format( 'data/maps/default/map%i_%i_%i.bxm', self.x, self.y, self.z )
	
	self : ClearMapData()
	self : ClearMeshs()
	
	local exists = FileExists( fname )
	
	if ( exists ) then
		local inp = assert( io.open( fname, "rb" ) )
		
		
		local fformat = inp:read("*n")
		local fversion = inp:read("*n")
		local tDS = inp:read("*n")
		assert( gDirSize == tDS )
		
		local numLoaded = 0
		local good = true
		while good == true do
			local index = inp:read("*n")
			if ( index == nil ) then
				good = false
			else
				numLoaded = numLoaded + 1
				local t = inp:read("*n")
				local r = inp:read("*n")
				local m = inp:read("*n")
				self.mapData[ index ][ 1 ] = t
				self.mapData[ index ][ 2 ] = r
				self.mapData[ index ][ 3 ] = m
				local rem = index - 1
				local x = int( rem / pow( gDirSize, 2 ) )
				rem = rem - x * pow( gDirSize, 2 )
				local y = int( rem / gDirSize )
				local z = rem - y * gDirSize
				--print ( "tile: ", x, y, z, t, r, m, index, numLoaded )
				
				--self : SetTile( x, y, z, t, r, m )
				self : PlaceTile( x, y, z, t, r, m )
				
			end
		end
		
		if ( numLoaded > 0 ) then
			self.zeros = false
		end
		
		
		inp:close()
		
		--self : PlaceAllGeom()
	else
		self : GenerateBlockContent()
	end
end


function SetCurBlock( inUpdateFromPlayer )
	if ( inUpdateFromPlayer == true ) then
		for i = 1, 3 do
			gCurBlockCords[ i ] = gPlCord[ i ] - gRootBlockCord[ i ]
		end
	end
	
	i = 1 + gCurBlockCords[ 1 ] * ( gBlockDirSize[ 3 ] * gBlockDirSize[ 2 ] ) + gCurBlockCords[ 2 ] * ( gBlockDirSize[ 3 ] ) + gCurBlockCords[ 3 ]
	gCurBlock = gBlocks[ i ]
end

function GetBlockGlobal( inX, inY, inZ )
	local x = inX - gRootBlockCord[ 1 ]
	local y = inY - gRootBlockCord[ 2 ]
	local z = inZ - gRootBlockCord[ 3 ]
	
	
	i = 1 + x * ( gBlockDirSize[ 3 ] * gBlockDirSize[ 2 ] ) + y * ( gBlockDirSize[ 3 ] ) + z
	return gBlocks[ i ]
end

function InitBlocks()
	local nBlocks = gBlockDirSize[ 1 ] * gBlockDirSize[ 2 ] * gBlockDirSize[ 3 ]
	local i = 1
	for x = 1, gBlockDirSize[ 1 ] do
		for y = 1, gBlockDirSize[ 2 ] do
			for z = 1, gBlockDirSize[ 3 ] do
				gBlocks[ i ] = Block( nil, gRootBlockCord[ 1 ] + x - 1, gRootBlockCord[ 2 ] + y - 1, gRootBlockCord[ 3 ] + z - 1 )
				gBlocks[ i ] : LoadFromFile()
				i = i + 1
			end
		end
	end
	
	SetCurBlock()
	
end



function ClearMapData()
	n = #mapData
	for i = 1, n do
		data = mapData[ i ]
		data[ 1 ] = 0
		data[ 2 ] = 0
		data[ 3 ] = 0
	end
end
	
function Init()
	
	AddGeom( "data/geom/SimpleCube/common.bxo" )
	
	SetNoiseSeed( gSeed )
	SCContentInit()
	
	InitBlocks()
	local c = .0
	SetAmbientColor( c, c, c )
	
	--PlaceAllGeom()
	
	CalcCursorAxis()
	
	
end


function SaveVisibleBlocks()
	
	for i = 1, gBlockDirSize[ 1 ] * gBlockDirSize[ 2 ] * gBlockDirSize[ 3 ] do
		if ( gBlocks[ i ].dirty ) then
			gBlocks[ i ] : SaveToFile()
			gBlocks[ i ].dirty = false
		end
	end
end

function ShiftBlocks( inAxi, inDir )	--in this version, we shift all elements of the block array - not most efficient, but easiest.
	gRootBlockCord[ inAxi ] = gRootBlockCord[ inAxi ] + inDir
	
	--if going negative, we are pushing everything up 1 axi [deleting end row] and loading start row
	-- loop from high to low
	--if going positive, pushing everything down 1 axi [deleting start row] loading end row
	-- loop from low to high
	local iOff = 1
	if ( inAxi == 1 ) then
		iOff = gBlockDirSize[ 2 ] * gBlockDirSize[ 3 ]
	elseif ( inAxi == 2 ) then
		iOff = gBlockDirSize[ 3 ]
	end
	iOff = iOff * inDir
	
	if ( inDir == 1 ) then	--yes, we copy/pasted, fix this if these require massive changes
		
		
		local i = 1
		local loadRow = gBlockDirSize[ inAxi ]
		local saveRow = 1
		local cords = { 1, 1, 1 }
		
		for x = 1, gBlockDirSize[ 1 ] do
			for y = 1, gBlockDirSize[ 2 ] do
				for z = 1, gBlockDirSize[ 3 ] do
					cords[ 1 ] = x
					cords[ 2 ] = y
					cords[ 3 ] = z
					if ( cords[ inAxi ] == saveRow ) then --save row!
						gBlocks[ i ] : SaveToFile()
					end
					--print ( 'i x y z', i, x, y, z )
					if cords[ inAxi ] == loadRow then	--load new in place (delete other?)
						gBlocks[ i ] = Block( nil, gRootBlockCord[ 1 ] + x - 1, gRootBlockCord[ 2 ] + y - 1, gRootBlockCord[ 3 ] + z - 1 )
						gBlocks[ i ] : LoadFromFile()
						
					else--move from other
						gBlocks[ i ] = gBlocks[ i + iOff ]
					end
					i = i + 1
					
				end
			end
		end
	else
		local i = gBlockDirSize[ 1 ] * gBlockDirSize[ 2 ] * gBlockDirSize[ 3 ]
		local loadRow = 1
		local saveRow = gBlockDirSize[ inAxi ]
		
		local cords = { 1, 1, 1 }
		
		for x = gBlockDirSize[ 1 ], 1, -1 do
			for y = gBlockDirSize[ 2 ], 1, -1 do
				for z = gBlockDirSize[ 3 ], 1, -1 do
					cords[ 1 ] = x
					cords[ 2 ] = y
					cords[ 3 ] = z
					if ( cords[ inAxi ] == saveRow ) then --save row!
						gBlocks[ i ] : SaveToFile()
					end
					--print ( 'i x y z', i, x, y, z )
					if cords[ inAxi ] == loadRow then	--load new in place (delete other?)
						gBlocks[ i ] = Block( nil, gRootBlockCord[ 1 ] + x - 1, gRootBlockCord[ 2 ] + y - 1, gRootBlockCord[ 3 ] + z - 1 )
						gBlocks[ i ] : LoadFromFile()
						
					else--move from other
						gBlocks[ i ] = gBlocks[ i + iOff ]
					end
					i = i - 1
					
				end
			end
		end
		
	
	end
	
end



function GetRotVals( inI )
	
	return metaRots[ inI + 1 ]
	
end

function CalcCursorAxis()
		--calculate cam vector!
		
		
		
		cosR2 = cos( gRots[ 2 ] )
		vec = { -sin( gRots[ 1 ] ) * cosR2, -cos( gRots[ 1 ] ) * cosR2, sin( gRots[ 2 ] ) }
		--print ( vec[ 1 ], vec[ 2 ], vec[ 3 ] )
		largest = 1
		avec = { abs( vec[ 1 ] ), abs( vec[ 2 ] ), abs( vec[ 3 ] ) }
		if ( avec[ 2 ] > avec[ 1 ] ) then largest = 2 end
		if ( avec[ 3 ] > avec[ largest ] ) then largest = 3 end
		vals = { { 2, 3 }, { 1, 3 }, { 1, 2 } }
		
		
		
		
		gCursorAxis[ 1 ] = vals[ largest ][ 1 ]
		gCursorAxis[ 2 ] = vals[ largest ][ 2 ]
		gCursorMod = { 1, 1 }
		
		if ( largest == 2 and vec[ largest ] > 0 ) then
			gCursorMod[ 1 ] = -1
		end
		if ( largest == 1 and vec[ largest ] < 0 ) then
			gCursorMod[ 1 ] = -1
		end
		if ( largest == 3 ) then	--need to figure top-down axis
			--medium = 1
			--if ( avec[ 2 ] > avec[ 1 ] ) then medium = 2 end
			
			axis = { { 1, 2 }, { 2, 1 }, { 1, 2 }, { 2, 1 } }
			mods = { { 1, 1 }, { -1, 1 }, { -1, -1 }, { 1, -1 } }
			wDir = int( gRots[ 1 ] / PIhalf + .5 ) % 4 + 1
			--print(wDir);
			gCursorAxis[ 1 ] = axis[ wDir ][ 1 ]
			gCursorAxis[ 2 ] = axis[ wDir ][ 2 ]
			gCursorMod[ 1 ] = mods[ wDir ][ 1 ]
			gCursorMod[ 2 ] = mods[ wDir ][ 2 ]
			
			if ( vec[ 3 ] < 0 ) then
				--gCursorMod[ 1 ] = -1 * gCursorMod[ 1 ]
				gCursorMod[ 2 ] = -1 * gCursorMod[ 2 ]
			end
			
		end

end


function DrawBlockOutline()
	local zr = -.5
	local m = gDirSize
	Push()
	Move( zr, zr, zr )
	DrawLine( 0, 0, 0, 0, 0, m )
	DrawLine( m, 0, 0, m, 0, m )
	DrawLine( m, m, 0, m, m, m )
	DrawLine( 0, m, 0, 0, m, m )
	DrawLine( 0, 0, 0, 0, m, 0 )
	DrawLine( m, 0, 0, m, m, 0 )
	DrawLine( 0, 0, m, 0, m, m )
	DrawLine( m, 0, m, m, m, m )
	DrawLine( 0, 0, 0, m, 0, 0 )
	DrawLine( 0, m, 0, m, m, 0 )
	DrawLine( 0, m, m, m, m, m )
	DrawLine( 0, 0, m, m, 0, m )
	
	Pop()
end



function KeyHappen( inVal, inDown )	--also registers mouse clicks
	nTiles = #gTileNames
	
	
	if ( inVal == 32 ) then gKeyCtrl = inDown end
	if ( inVal == 31 ) then gKeyShift = inDown end
	
	if ( inDown == true and false ) then
		print ( inVal, inDown, chVal )
	end
	
	
	if ( inVal >= 6 and inVal <= 8 ) then
		gMouseClick[ inVal - 5 ] = inDown
	end
	
	if ( inVal == 20 and inDown ) then gDrawOutlines = not gDrawOutlines end
	
	
	if ( inVal == 17 and inDown ) then
		gCamMode = 1 - gCamMode
		
		if ( gCamMode == 1 ) then
			for i = 1, 3 do
				gPlCord[ i ] = gCurBlockCords[ i ] + gRootBlockCord[ i ]
			end
		end 
		if ( gCamMode == 0 ) then
			for i = 1, 3 do
				gCurBlockCords[ i ] = gPlCord[ i ] - gRootBlockCord[ i ]
			end
		end 
		
		
		
	end
	
	if ( gCamMode == 0 ) then
		if ( inVal == 11 and inDown == true ) then
			if ( gKeyCtrl ) then
				--gCurTileI = ( gCurTileI + 1 ) % nTiles
				gMouseSens = gMouseSens * 1.3
			elseif ( gKeyShift ) then
				gRotI = ( gRotI + 1 ) % #metaRots
			else
				gCamDist = gCamDist * .9
			end
		end
		
		if ( inVal == 12 and inDown == true ) then
			if ( gKeyCtrl ) then
				--gCurTileI = ( gCurTileI - 1 + nTiles ) % nTiles
				gMouseSens = gMouseSens / 1.3
			elseif ( gKeyShift ) then
				gRotI = ( gRotI - 1 + #metaRots ) % #metaRots
			else
				gCamDist = gCamDist * 1.1
			end
		end
		
		
		if ( inDown == true ) then
			
			if ( inVal == 27 ) then	--erase
				x = int( gCursor[ 1 ] )
				y = int( gCursor[ 2 ] )
				z = int( gCursor[ 3 ] )
				data = gCurBlock : GetDataPtr( x, y, z )
				if ( data[ 1 ] ~= 0 ) then
					gCurBlock : SetTile( x, y, z, 0, 0, 0 )
					gCurBlock : PlaceAllGeom()
					gCurBlock.dirty = true
				
				end
			end
			if ( inVal == 28 ) then	--load
				gCurBlock : LoadFromFile()
			end
			if ( inVal == 30 ) then	--save
				--gCurBlock : SaveToFile()
				SaveVisibleBlocks()
			end
			
			if ( inVal == 22 ) then
				gCurMatI = ( gCurMatI - 1 + gNumMaterials ) % gNumMaterials
			end
			if ( inVal == 23 ) then
				gCurMatI = ( gCurMatI + 1 ) % gNumMaterials
			end
			
			if ( inVal == 18 ) then	--18,0,2,1
				gCurTileI = ( gCurTileI - 1 + nTiles ) % nTiles
			end
			if ( inVal == 0 ) then
				gCurTileI = ( gCurTileI + 1 ) % nTiles
			end
			if ( inVal == 1 ) then
				gRotI = ( gRotI - 1 + #metaRots ) % #metaRots
			end
			if ( inVal == 2 ) then
				gRotI = ( gRotI + 1 ) % #metaRots
			end
			if ( inVal == 21 ) then --get current tile
				x = int( gCursor[ 1 ] )
				y = int( gCursor[ 2 ] )
				z = int( gCursor[ 3 ] )
				data = gCurBlock : GetDataPtr( x, y, z )
				if ( data[ 1 ] ~= 0 ) then
					gCurTileI = data[ 1 ] - 1
					gRotI = data[ 2 ]
					gCurMatI = data[ 3 ] - 1
				end
				
			end
			
			if ( ( inVal >= 13 and inVal <= 16 ) or inVal == 25 ) then
				wPre = inVal - 12
				if ( inVal == 25 ) then
					wPre = 5
				end
				pre = gPresets[ wPre ]
				if ( gKeyCtrl ) then
					pre[ 1 ] = gCurTileI
					pre[ 2 ] = gRotI
					pre[ 3 ] = gCurMatI
					
				else
					gCurTileI = pre[ 1 ]
					gRotI = pre[ 2 ]
					gCurMatI = pre[ 3 ]
					
				end
			
			end
			
		end
	else	--in play mode
		--gPlMovePressed  up down left right jump c
		if ( inVal >= 0 and inVal <= 5 ) then
			gPlMovePressed[ inVal + 1 ] = inDown
		end
		
		
	
	end
end
	


function GetViewVector( inAngs, inZCoef )
	if ( inZCoef == nil ) then inZCoef = 1.0 end
	
	local v = { 0, 0, 0 }
	local c2 = cos( inAngs[ 2 ] ) * inZCoef + ( 1.0 - inZCoef )
	v[ 1 ] = cos( inAngs[ 1 ] ) * c2
	v[ 2 ] = sin( inAngs[ 1 ] ) * c2
	v[ 3 ] = sin( inAngs[ 2 ] ) * inZCoef
	
	return v
end	

	
function Draw( t, dt )
	local mouseX, mouseY = GetMouseTotal()
	local dMX = mouseX - gPrevMouse[ 1 ]
	local dMY = -( mouseY - gPrevMouse[ 2 ] )
	gPrevMouse[ 1 ] = mouseX
	gPrevMouse[ 2 ] = mouseY
	
	dt = min( .1, dt )
	
	if ( int( .2 * t ) ~= int( .2 * ( t - dt ) ) ) then
		print( '#proj:', # gProjectiles )
	end
	
	
	if ( gCamMode == 0 ) then
	
		if ( gMouseClick[ 3 ] == true ) then
			
			gRots[ 2 ] = gRots[ 2 ] - dMY
			gRots[ 1 ] = gRots[ 1 ] + dMX
			gRots[ 2 ] = max( -PIhalf, min( PIhalf, gRots[ 2 ] ) )
			
			CalcCursorAxis()
			
			
			
			
		else
			local axi0 = gCursorAxis[ 1 ]
			local axi1 = gCursorAxis[ 2 ]
			
			local xMod = 1
			local yMod = 1
			if ( gKeyCtrl ) then xMod = 0 end
			if ( gKeyShift ) then yMod = 0 end
			
			--gCursor[ axi0 ] = max( 0, min( gDirSize - 1, gCursor[ axi0 ] + dMX * 9 * gCursorMod[ 1 ] * xMod ) )
			--gCursor[ axi1 ] = max( 0, min( gDirSize - 1, gCursor[ axi1 ] + dMY * 9 * gCursorMod[ 2 ] * yMod ) )
			local axs = { axi0, axi1 }
			local dms = { dMX, dMY }
			local wMod = { xMod, yMod }
			for i = 1, 2 do	--derp, better method of loop?
				ax = axs[ i ]
				wdm = dms[ i ]
				gCursor[ ax ] = gCursor[ ax ] + wdm * gMouseSens * gCursorMod[ i ] * wMod[ i ]
				if ( gCursor[ ax ] < 0 ) then
					if ( gCurBlockCords[ ax ] > 0 ) then
						gCurBlockCords[ ax ] = gCurBlockCords[ ax ] - 1
						gCursor[ ax ] = gDirSize - .01
						gCamPos[ ax ] = gCamPos[ ax ] + gDirSize
						SetCurBlock()
					else
						ShiftBlocks( ax, -1 )
						--gCursor[ ax ] = 0
						gCursor[ ax ] = gDirSize - .01
						SetCurBlock()
					end
				end
				
				if ( gCursor[ ax ] >= gDirSize ) then
					if ( gCurBlockCords[ ax ] + 1 < gBlockDirSize[ ax ] ) then
						gCurBlockCords[ ax ] = gCurBlockCords[ ax ] + 1
						gCursor[ ax ] = 0
						gCamPos[ ax ] = gCamPos[ ax ] - gDirSize
						SetCurBlock()
					else
						--gCursor[ ax ] = gDirSize - 1
						ShiftBlocks( ax, 1 )
						gCursor[ ax ] = 0
						SetCurBlock()
					end
				end
			end
			
			
		end
		if ( gMouseClick[ 1 ] == true ) then
			local x = int( gCursor[ 1 ] )
			local y = int( gCursor[ 2 ] )
			local z = int( gCursor[ 3 ] )
			local data = gCurBlock : GetDataPtr( x, y, z )
			if ( data[ 1 ] ~= gCurTileI + 1 or data[ 2 ] ~= gRotI or data[ 3 ] ~= gCurMatI + 1 ) then
				if data[ 1 ] == 0 then
					--PlaceAllGeom()
					gCurBlock : SetPlaceTile( x, y, z, gCurTileI + 1, gRotI, gCurMatI + 1 )
				else
					gCurBlock : SetTile( x, y, z, gCurTileI + 1, gRotI, gCurMatI + 1 )
					gCurBlock : PlaceAllGeom()
				end
				
				gCurBlock.dirty = true
			end
		end
		
		
		for i = 1, 3 do
			local v = 1.0 - 4 * dt
			gCamPos[ i ] = gCamPos[ i ] * v + ( 1.0 - v ) * gCursor[ i ]
		end
		
		local off = gCamPos
		LookAt( off[ 1 ], off[ 2 ] -gCamDist, off[ 3 ], off[ 1 ], off[ 2 ], off[ 3 ] )	--3d view
		Move( off[ 1 ], off[ 2 ], off[ 3 ] )
		Rotate( 1, 0, 0, gRots[ 2 ] )
		Rotate( 0, 0, 1, gRots[ 1 ] )
		Move( -off[ 1 ], -off[ 2 ], -off[ 3 ] )
		local lightX = .5 * gDirSize * sin( .8 * t )	-- 3 * mouseX
		local lightY = .5 * gDirSize * cos( .8 * t )	--0-- -3 * mouseY
		local lightZ = .5 * gDirSize * sin( t + 1 )
		--print ( gRootBlockCord[ 1 ], gCurBlockCords[ 1 ], gPlCord[ 1 ] )
		
		Push()
		Move( -gCurBlockCords[ 1 ] * gDirSize, -gCurBlockCords[ 2 ] * gDirSize, -gCurBlockCords[ 3 ] * gDirSize )
	
	else --gCamMode == 1
		
		
		gPlAng[ 2 ] = gPlAng[ 2 ] + dMY
		gPlAng[ 1 ] = gPlAng[ 1 ] - dMX
		gPlAng[ 2 ] = max( -PIhalf, min( PIhalf, gPlAng[ 2 ] ) )
		
		
		
		local v  = GetViewVector( gPlAng, 1 )
		local vB = GetViewVector( gPlAng, 0 )
		
		
		local mvVec = { 0, 0, 0 }
		local mvAxiX = { vB[ 1 ], -vB[ 1 ], -vB[ 2 ], vB[ 2 ] }
		local mvAxiY = { vB[ 2 ], -vB[ 2 ], vB[ 1 ], -vB[ 1 ] }
		
		for i = 1, 4 do
			if ( gPlMovePressed[ i ] ) then
				--mvVec[ 1 ] = mvVec[ 1 ] + mvAxiX[ i ]
				--mvVec[ 2 ] = mvVec[ 2 ] + mvAxiY[ i ]
				gPlSpd[ 1 ] = gPlSpd[ 1 ] + mvAxiX[ i ] * dt
				gPlSpd[ 2 ] = gPlSpd[ 2 ] + mvAxiY[ i ] * dt
			end
		end
		
		mvVec[ 1 ] = gPlSpd[ 1 ]
		mvVec[ 2 ] = gPlSpd[ 2 ]
		if ( gPlIsOnGround ) then
			gPlSpd[ 1 ] = gPlSpd[ 1 ] * ( 1 - 4 * dt )
			gPlSpd[ 2 ] = gPlSpd[ 2 ] * ( 1 - 4 * dt )
		end
		-- if ( gPlMovePressed[ 5 ] ) then
			-- mvVec[ 3 ] = 1
		-- end
		-- if ( gPlMovePressed[ 6 ] ) then
			-- mvVec[ 3 ] = mvVec[ 3 ] - 1
		-- end
		
		if ( gPlMovePressed[ 5 ] == false ) then
			gPlSpd[ 3 ] = gPlSpd[ 3 ] - dt * gPlGravity
		else
			if ( gPlIsOnGround ) then
				gPlSpd[ 3 ] = gPlJumpSpd
			end
			gPlSpd[ 3 ] = gPlSpd[ 3 ] + dt * plSpeedUp
		end
		
		mvVec[ 3 ] = mvVec[ 3 ] + gPlSpd[ 3 ]
		mvVec[ 3 ] = max( -gMaxZFallSpd, mvVec[ 3 ] )
		local plBlockOffset = { 0, 0, 0 }
		local plCamOffset = { 0, 0, 0 }
		
		local oPos = { gPlPos[ 1 ], gPlPos[ 2 ], gPlPos[ 3 ] }
		local nPos = { gPlPos[ 1 ], gPlPos[ 2 ], gPlPos[ 3 ] }
		local radPos = { gPlPos[ 1 ], gPlPos[ 2 ], gPlPos[ 3 ] }
		
		--local radis = { 1, 1, 1, 1, 2, 3 }
		
		gPlIsOnGround = false;
		
		for i = 1, 3 do
			local dir = 0
			local rSign = -1
			if ( mvVec[ i ] > 0 ) then
				dir = 1
				rSign = 1
			end
			
			local radI = 1
			if ( i == 3 ) then
				if ( dir == 0 ) then
					radI = 2
				else
					radI = 3
				end
			end
			
			local maxDist = gPlRadius[ radI ]
			local mvDist = mvVec[ i ] * dt * 16 * .52
			
			nPos[ i ] = nPos[ i ] + mvDist
			
			local radPos = { gPlPos[ 1 ], gPlPos[ 2 ], gPlPos[ 3 ] }
			radPos[ i ] = max( 0, min( gDirSize - 1, oPos[ i ] + maxDist * rSign ) )
			
			local data = gCurBlock : GetDataPtr( int( nPos[ 1 ] ), int( nPos[ 2 ] ), int( nPos[ 3 ] ) )
			local dataR = gCurBlock : GetDataPtr( int( radPos[ 1 ] ), int( radPos[ 2 ] ), int( radPos[ 3 ] ) )
			
			if ( dataR ~= nil and dataR[ 1 ] > 0 ) then
				nPos[ i ] = oPos[ i ]
				gPlSpd[ i ] = 0
				-- if ( dir == 1 ) then
					-- nPos[ i ] = int( nPos[ i ] ) - maxDist
				-- else
					-- nPos[ i ] = int( nPos[ i ] ) + 1 + maxDist
				-- end
				if ( i == 3 and dir == 0 ) then
					gPlSpd[ 3 ] = 0
					gPlIsOnGround = true
				end
			end
			--gPlPos[ i ] = gPlPos[ i ] + mvVec[ i ] * dt * 16 * .2
			gPlPos[ i ] = nPos[ i ]
			if ( gPlPos[ i ] >= gDirSize ) then
				gPlCord[ i ] = gPlCord[ i ] + 1
				gPlPos[ i ] = gPlPos[ i ] - ( gDirSize )
				
				plBlockOffset[ i ] = gPlCord[ i ] - gRootBlockCord[ i ]
				if ( gBlockDirSize[ i ] - ( plBlockOffset[ i ] + 1 ) < gPlBoundsShift ) then
					ShiftBlocks( i, 1 )
				end
				
				SetCurBlock( true )
				
			end
			
			if ( gPlPos[ i ] < 0 ) then
				gPlCord[ i ] = gPlCord[ i ] - 1
				gPlPos[ i ] = gPlPos[ i ] + ( gDirSize )
				
				plBlockOffset[ i ] = gPlCord[ i ] - gRootBlockCord[ i ]
				if ( plBlockOffset[ i ] + 1 == gPlBoundsShift ) then
					ShiftBlocks( i, -1 )
				end
				
				SetCurBlock( true )
				
			end
			
			plBlockOffset[ i ] = gPlCord[ i ] - gRootBlockCord[ i ]
			plCamOffset[ i ] = plBlockOffset[ i ] * ( gDirSize ) - .5
		end
		--print (gRootBlockCord[ 1 ], gCurBlockCords[ 1 ], gPlCord[ 1 ], plBlockOffset[ 1 ], gPlPos[ 1 ] )
		--	 -1 bla -3 -2 pos
		--	 0  1  -1  -1  pos
		
		if ( t >= gPlNextFire and gMouseClick[ 1 ] ) then
			gPlNextFire = t + .01
			local radius = 3
			insert( gProjectiles, { 
				{ gPlCord[ 1 ], gPlCord[ 2 ], gPlCord[ 3 ] }, 
				{ gPlPos[ 1 ] + v[ 1 ] * radius - .5, gPlPos[ 2 ] + v[ 2 ] * radius - .5, gPlPos[ 3 ] + v[ 3 ] * radius - .5 }, 
				{ gPlAng[ 1 ], gPlAng[ 2 ] }, 
				{ v[ 1 ], v[ 2 ], v[ 3 ] },
				t
				} )
		end
		
		
		
		
		
		LookAt( plCamOffset[ 1 ] + gPlPos[ 1 ], 		 plCamOffset[ 2 ] + gPlPos[ 2 ], 		  plCamOffset[ 3 ] + gPlPos[ 3 ], 
				plCamOffset[ 1 ] + gPlPos[ 1 ] + v[ 1 ], plCamOffset[ 2 ] + gPlPos[ 2 ] + v[ 2 ], plCamOffset[ 3 ] + gPlPos[ 3 ] + v[ 3 ]  )	--3d view
		Push()
	
	
	end
	--Tex( -1 )
	
	--SetLighting( 0 )
	
	if ( gDrawOutlines ) then
		Color( .1, .1, .1, .1 )
		SetDepth( 0, 0 )
		for x = 1, gBlockDirSize[ 1 ] do
			for y = 1, gBlockDirSize[ 2 ] do
				for z = 1, gBlockDirSize[ 3 ] do
					Push()
					Move( gDirSize * ( x - 1 ), gDirSize * ( y - 1 ), gDirSize * ( z - 1 ) )
					DrawBlockOutline()
					
					Pop()
				end
			end
		end
		SetDepth( 1, 1 )
		
		Color( 1 )
	end
	
	local sz = gDirSize * 2.82
	
	SetLight( 0, lightX, lightY, lightZ )
	SetLight( 0, sz, sz , 0 * -sz )
	SetLight( 1, sz * 0, -sz * 0, sz )
	
	if ( gLightColorAtten ) then
		SetLightAtten( 0, 0, 0, .0001 * .7 )
		SetLightAtten( 1, 0, 0, .0001 * .4 )
		SetLightColor( 1, 1, 1, .6 )
		SetLightColor( 0, .3, .3, 1 )
	else
		SetLightAtten( 0, 0, 0, .0001 * .6 )
		SetLightAtten( 1, 0, 0, .0001 * .6 )
		SetLightColor( 1, 1, 1, 1 )
		SetLightColor( 1, 1, 1, 1 )
	end
	
	for p = 1, 2 do
		for i = 1, gNumMaterials do
			doDraw = UseMaterial( i - 1, p )
			if ( doDraw ) then
				local j = 1
				for x = 1, gBlockDirSize[ 1 ] do
					for y = 1, gBlockDirSize[ 2 ] do
						for z = 1, gBlockDirSize[ 3 ] do
							Push()
							Move( gDirSize * ( x - 1 ), gDirSize * ( y - 1 ), gDirSize * ( z - 1 ) )
							--SetLight( 0, -sz, -sz, -sz * 2 )
							--SetLight( 1, sz, -sz, sz * 2 )
							gBlocks[ j ] : Draw( i )
							j = j + 1
							Pop()
						end
					end
				end
				
				Color( 1 )
				Tex( -1 )
				Shad( -1 )
				Blend( 0 )
				SetDepth( 1, 1 )
			end
		end
	end
	
	Pop()
	
	
	--!!!!!!!!!!!!!!!!!!!!projectiles
	SetLighting( 1 )
	UseMaterial( 37, -1 )
	--UseMaterial( 0, -1 )
	local np = # gProjectiles
	for i = 1, np do
		local proj = gProjectiles[ i ]
		Push()
		local cord = proj[ 1 ]
		local cOff = { cord[ 1 ] - gRootBlockCord[ 1 ], cord[ 2 ] - gRootBlockCord[ 2 ], cord[ 3 ] - gRootBlockCord[ 3 ] }
		local pos = proj[ 2 ]
		local ang = proj[ 3 ]
		local vec = proj[ 4 ]
		local age = t - proj[ 5 ]
		local spd = 30
		oInt = { int( pos[ 1 ] ), int( pos[ 2 ] ), int( pos[ 3 ] ) }
		pos[ 1 ] = pos[ 1 ] + vec[ 1 ] * dt * spd
		pos[ 2 ] = pos[ 2 ] + vec[ 2 ] * dt * spd 
		pos[ 3 ] = pos[ 3 ] + vec[ 3 ] * dt * spd
		Move( cOff[ 1 ] * gDirSize + pos[ 1 ], cOff[ 2 ] * gDirSize + pos[ 2 ], cOff[ 3 ] * gDirSize + pos[ 3 ] )
		Rotate( 0, 0, 1, ang[ 1 ] - .5 * t)
		Rotate( 1, 0, 0, ang[ 2 ] + .8 * t )
		--Rotate( 0, 0, 1, ang[ 1 ] )
		Scale( .3 )
		--Geom( 'cube' )
		Geom( 'tiles', 'quads1' )
		for ax = 1, 3 do
			if ( pos[ ax ] < 0 ) then
				pos[ ax ] = pos[ ax ] + gDirSize
				cord[ ax ] = cord[ ax ] - 1
			elseif ( pos[ ax ] >= gDirSize ) then
				pos[ ax ] = pos[ ax ] - gDirSize
				cord[ ax ] = cord[ ax ] + 1
			end
		end
		
		--check the stuff!! walls!
		block = GetBlockGlobal( cord[ 1 ], cord[ 2 ], cord[ 3 ] )
		if ( block ~= nil ) then
			data = block : GetDataPtr( int( pos[ 1 ] ), int( pos[ 2 ] ), int( pos[ 3 ] ) )
			if ( data[ 1 ] > 0 ) then
				for ax = 1, 3 do
					if ( oInt[ ax ] - int( pos[ ax ] ) ~= 0 ) then
						vec[ ax ] = vec[ ax ] * -1
						pos[ ax ] = pos[ ax ] + .1 * vec[ ax ]
						
					end
				end
				
			end
		end
		
		if ( age > 7.0 ) then RemoveObject( i ) end
		
		
		Pop()
	
	end
	
	
	ProcessRemove( gProjectiles )
	
	
	--!!!! Draw cursor
	
	
	Push()
	
	Move( int( gCursor[ 1 ] ), int( gCursor[ 2 ] ), int( gCursor[ 3 ] ) )
	Scale( .5 )
	
	local rotVals = GetRotVals( gRotI )
	local nr = # rotVals
	for i = 1, nr do
		v = rotVals[ i ]
		Rotate( v[ 1 ], v[ 2 ], v[ 3 ], v[ 4 ] )
	end
	UseMaterial( gCurMatI, -1 )
	Geom( 'tiles', gTileNames[ gCurTileI + 1 ] )
	Pop()
	
	SetLighting( 0 )
	Color( 1 )
	Tex( -1 )
	Shad( -1 )
	Blend( 0 )
	SetDepth( 1, 1 )
	
	
end
