-- Lua Map Maker  v0.6
-- by  Merick
--3/19/07
-- see readme for credits

emptyram = System.getFreeMemory()
System.usbDiskModeActivate()

lum = Image.load("lum.png")
fader = Image.load("fader.png")
hyperion = Font.load("hyperion.ttf")
hyperion2 = Font.load("hyperion.ttf")
hyperion:setPixelSizes(0, 18)
hyperion2:setPixelSizes(43,50)

red=Color.new(255,0,0)
blue=Color.new(0,0,255)
white=Color.new(255,255,255)

dofile("danzeff.lua")
keys = Danzeff:New()
keys:Init(20, 20)
menuitem = 1

map = {}				-- stores map data
map.tileimg = {}		-- tile image data
map.tilename = {}     	-- tile filenames
map.xsize = 1			-- map width in tiles
map.ysize = 1			-- map height in tiles
map.tilesize = 8		-- size of tiles ( not actual size, but size used for map grid)

loop = true
pname=""
ct = 1 		-- current tile
objn = 1 	-- number assignment for objects 

layer = 1
-- 1 = map layer
-- 2 = object layer
-- 3 = map overlay

-- map x,y position
xpos = 1
ypos = 1

-- offsets to keep the map x,y position on screen
Xoffset = 0
Yoffset = 0

-- screen position
scposX=(((xpos*map.tilesize)-map.tilesize)+Xoffset)
scposY=(((ypos*map.tilesize)-map.tilesize)+Yoffset)

-- begin function defs
dofile("tablesave.lua") -- needed to save/load map data

function displayram() -- dipsplay free memory
	local ram = math.floor((System.getFreeMemory()/ 1024)/1024) --tracks memory used
	screen:print(350, 10, "Free RAM = " .. ram.."mb", white)
end

function getpname() -- use osk to get name for new project
	local loop = true
	while loop do
		screen:clear()
		screen:fontPrint(hyperion, 180, 20, "left trigger = numbers", white)
		screen:fontPrint(hyperion, 180, 50, "right trigger = shift", white)
		screen:fontPrint(hyperion, 180, 80, "D-pad up = backspace", white)
		screen:fontPrint(hyperion, 180, 110, "start = accept name", white)
		pad=Controls.read()
		newchar=keys:Input(pad)
		if newchar > 10 and newchar ~= oldchar then pname = pname .. string.char(newchar) end
		if newchar == 8 and newchar ~= oldchar then pname = string.sub(pname, 1, string.len(pname) - 1) end
		if newchar ==4 and pad ~= oldpad then loop=false end
		if pname ~= nil then screen:fontPrint(hyperion, 20, 200, pname, white) end
		oldchar = newchar
		oldpad = pad
		keys:Display()
		screen:waitVblankStart()
		screen:flip()
	end
end

function mapinit() -- initialize empty map array
	for i = 1, map.xsize do
		map[i] = {}
		for j = 1, map.ysize do
			map[i][j] = {tile = nil, object = nil, otile = nil}
			-- tile = base layer, file name of the tile at this location 
			-- object:  stores a number value for objects
			-- otile = same as tile, but for overlay layer
		end
	end
end

function getVP()
-- set up map viewport
	vpx = math.floor(500 / map.tilesize) + 1 -- width of map viewport
	vpy = math.floor(300 / map.tilesize) + 1 -- height of map viewport

-- start and end positions for map scanner
	Xstart = xpos - math.floor(vpx/2)
	if Xstart < 1 then Xstart = 1 end
	Xend = Xstart + vpx
	if Xend > map.xsize then
		Xend = map.xsize
		Xstart = Xend - vpx
	end
	Ystart = ypos - math.floor(vpy/2)
	if Ystart < 1 then Ystart = 1 end
	Yend = Ystart + vpy
	if Yend > map.ysize then
		Yend = map.ysize
		Ystart = Yend - vpy
	end
end

function drawmap() -- draw map
getVP()
--scan and draw map area
Image.startBlit()
	for i  = Xstart, Xend do
		for j = Ystart, Yend do
			if map[i][j].tile ~= nil
				then screen:fastBlit(((i*map.tilesize)+Xoffset)-map.tilesize, ((j*map.tilesize)+Yoffset)-map.tilesize, map.tileimg[map[i][j].tile])
			end
		end
	end
Image.endBlit()
end

function drawobject() -- draw map objects
	for i  = Xstart, Xend do
		for j = Ystart, Yend do
			if map[i][j].object  ~= nil
			then screen:fillRect(((i*map.tilesize)+Xoffset)-map.tilesize, ((j*map.tilesize)+Yoffset)-map.tilesize, map.tilesize, map.tilesize, Color.new(100,100,100))
				screen:print((((i*map.tilesize)+Xoffset)-map.tilesize)+3, ((j*map.tilesize)+Yoffset)-(map.tilesize/2), map[i][j].object, white)
			end
		end
	end
end

function drawoverlay() -- draw map overlay
--Image.startBlit()
	for i  = Xstart, Xend do
		for j = Ystart, Yend do
			if map[i][j].otile ~= nil
				then screen:blit(((i*map.tilesize)+Xoffset)-map.tilesize, ((j*map.tilesize)+Yoffset)-map.tilesize, map.tileimg[map[i][j].otile], true)
			end 
		end
	end
--Image.endBlit()
end

function loadtiles() -- load all images from tiles folder
	screen:clear()
	local count = 0
	files = System.listDirectory("./tiles")
	table.remove(files,1) 
	table.remove(files,1)
		for i = 1, table.getn(files) do
			screen:fontPrint(hyperion, 100, 100, "loading tiles", red)
			displayram()
			screen:flip()
			screen:clear()
			if files[i].directory == false then
				if string.sub(files[i].name:lower(), -4) == ".png" or
				   string.sub(files[i].name:lower(), -4) == ".jpg" then
					count = count +1
					map.tilename[count] = files[i].name
					map.tileimg[files[i].name] = Image.load("./tiles/"..files[i].name)
				end
			end
		end
	return
end	

function editmap() -- map edit routine
	loop = true
	while loop do
		pad = Controls.read()
		if pad:left() and pad ~= oldpad and xpos > 1  then xpos = xpos - 1 end
		if pad:right() and xpos < map.xsize and pad ~= oldpad then xpos = xpos + 1 end
		if pad:up() and ypos >1 and pad ~= oldpad then ypos =  ypos - 1 end
		if pad:down() and ypos < map.ysize and pad ~= oldpad then ypos = ypos + 1 end

		scposX=((xpos*map.tilesize)+Xoffset)
		scposY=((ypos*map.tilesize)+Yoffset)

		if scposX > (240+(map.tilesize/2)) 
			and ((map.xsize*map.tilesize)-(xpos*map.tilesize)) > (240-map.tilesize) 
			then Xoffset = Xoffset - map.tilesize end
			
		if scposY > (136+(map.tilesize/2))
			and ((map.ysize*map.tilesize)-(ypos*map.tilesize)) > (136-map.tilesize)
			then Yoffset = Yoffset - map.tilesize end

		if scposX < 240-(map.tilesize/2)
			and (xpos*map.tilesize) > (240-(map.tilesize/2)) 
			then Xoffset = Xoffset + map.tilesize end
			
		if scposY < 136-(map.tilesize/2)
			and (ypos*map.tilesize) > (136-(map.tilesize/2))
			then Yoffset = Yoffset + map.tilesize end

		-- select tile
		if pad:triangle() and pad ~= oldpad then
			if layer == 1 or layer == 3 then ct = ct +1 end
			if layer == 2 then objn = objn +1 end
		end
		if pad:circle() and pad ~= oldpad then
			if layer == 1 or layer == 3 then ct = ct -1 end
			if layer == 2 and objn > 1 then objn = objn -1 end
		end
		if ct > table.getn(map.tilename) then ct = 1 end
		if ct < 1 then ct =table.getn(map.tilename) end

		-- place tile or object code
		if pad:square() and pad ~= oldpad then
			if layer == 1 then map[xpos][ypos].tile = map.tilename[ct] end
			if layer == 2 then map[xpos][ypos].object = objn end
			if layer == 3 then map[xpos][ypos].otile = map.tilename[ct] end
		end

		-- erase tile or object code 
		if pad:cross() and pad ~= oldpad then
			if layer == 1 then map[xpos][ypos].tile = nil end
			if layer == 2 then map[xpos][ypos].object = nil end
			if layer == 3 then map[xpos][ypos].otile = nil end
		end

		if pad:r() and pad ~= oldpad then layer = layer +1 end
		if layer >3 then layer = 1 end

		drawmap()
		if layer > 1 then screen:blit(0,0,fader) end
		if layer > 1 then drawobject() end
		if layer == 3 then screen:blit(0,0,fader) end
		if layer == 3 then drawoverlay() end

		screen:fillRect(scposX-1-map.tilesize, scposY-1-map.tilesize, map.tilesize+2, map.tilesize+2, blue)
		if layer == 1 or layer == 3 then screen:blit((scposX-map.tilesize), (scposY-map.tilesize), map.tileimg[map.tilename[ct]])end
		if layer == 2 then screen:fontPrint(hyperion, (scposX+3)-map.tilesize, scposY-(map.tilesize/2), objn, white) end

		displayram()
		screen:print(10, 10, "xpos = " .. xpos .. " ypos = "..ypos, white)
		screen:waitVblankStart()
		screen:flip()
		screen:clear()
		if pad:start() and pad ~= oldpad then layer = 0  loop = false end
		oldpad = pad
		collectgarbage()
	end
end

function loadproject()  -- edited from Tactical Penguins file browser
	local s = 1
	local k = 0
	local fl=System.listDirectory("./projects")
	table.remove(fl,1)
	table.remove(fl,1)
	local nf=table.getn(fl)
	loop = true
	oldpad = Controls.read()
	while loop do
		screen:clear()
		pad = Controls.read()

		--go from bottom to top part--
		if s > nf then s = 1 end
		if s < 1 then s = nf end

		--scrollingpart--
		if s < 27 then k = 0 end

		for i = 1,10 do
			if s > i*27-1 and s < i*27 + 27 then k = -270*i end
		end

		--print contents of directory
		for i=1,nf do
			screen:print(10,k+i*10,fl [i].name,white)
		end

		--print current selected file in red --
		screen:print(10,k+10*s,fl [s].name, red)

		--change selector part--
		if pad:down() and oldpad:down() ~= pad:down() then s = s + 1 end
		if pad:up() and oldpad:up() ~= pad:up() then s = s - 1 end

		--select project
		if pad:start() and pad ~= oldpad and fl[s].directory == true then
			pname = (fl[s].name)
			loop = false
			pjdir = "./projects/"..pname.."/"
			map = table.load(pjdir..pname..".map")
		end
		screen.waitVblankStart()
		screen.flip()
		oldpad = pad
	end
end

-- end of functions

-- start menu screen
while loop do
	screen:clear()
	screen:blit(0, 0, lum)

	if menuitem==1 then screen:fontPrint(hyperion, 10, 150,"Start New Project", red)
		else screen:fontPrint(hyperion, 10, 150,"Start New Project", white)
	end

	if menuitem==2 then screen:fontPrint(hyperion, 10, 180,"Load Project", red)
		else screen:fontPrint(hyperion, 10, 180,"Load Project", white)
	end

	if menuitem==3 then screen:fontPrint(hyperion, 10, 210,"Exit LuMM", red)
		else screen:fontPrint(hyperion, 10, 210,"Exit LuMM", white)
	end

	pad = Controls.read()
	if pad:up() and pad ~= oldpad then menuitem = menuitem - 1 end
	if pad:down() and pad ~= oldpad then menuitem = menuitem + 1 end
	if menuitem < 1 then menuitem = 3 end
	if menuitem > 3 then menuitem = 1 end
	if pad:start() then loop=false oldpad = pad end
	screen:waitVblankStart()
	screen:flip( )
	oldpad = pad
end

if menuitem == 3 then System.Quit() end

-- create new project
if menuitem==1 then getpname()
	pjdir = "./projects/"..pname.."/"
	loop = true
	while loop do
		screen:clear()
		screen:fontPrint(hyperion, 15, 15, "Use D-pad to change number", white)
		screen:fontPrint(hyperion, 15, 35, "Cross to use selected numer", white)
		screen:fontPrint(hyperion, 15, 65, "Select tile X,Y size in pixels:", white)
		screen:fontPrint(hyperion, 15, 95, "      (minimum is 8)", blue)
		pad=Controls.read()
		if pad:up() then map.tilesize = map.tilesize +1 end
		if pad:down() and map.tilesize > 8 then map.tilesize = map.tilesize -1 end
		if pad:cross() and pad ~= oldpad then loop = false end
		screen:fontPrint(hyperion, 15, 125, map.tilesize, white)
		screen:flip()
		oldpad = pad
	end
	loop = true
	while loop do
		screen:clear()
		screen:fontPrint(hyperion, 15, 15, "Use D-pad to change number", white)
		screen:fontPrint(hyperion, 15, 35, "Cross to use selected numer", white)
		screen:fontPrint(hyperion, 15, 65, "Select X size of map in tiles:", white)
		pad=Controls.read()
		if pad:up() then map.xsize = map.xsize +1 end
		if pad:down() and map.xsize > 1 then map.xsize = map.xsize -1 end
		screen:fontPrint(hyperion, 15, 95, map.xsize, white)
		screen:fontPrint(hyperion, 15, 155, "Map will be "..(math.floor(((map.xsize*map.tilesize)/480)*100)/100).." screens wide", blue)
		if pad:cross() and pad ~= oldpad then loop = false oldpad=pad  end
		screen:flip()
		oldpad = pad
		end
	loop = true
	while loop do
		screen:clear()
		screen:fontPrint(hyperion, 15, 15, "Use D-pad to change number", white)
		screen:fontPrint(hyperion, 15, 35, "Cross to use selected numer", white)
		screen:fontPrint(hyperion, 15, 65, "Select Y size of map in tiles:", white)
		pad=Controls.read()
		if pad:up()  then map.ysize = map.ysize +1 end
		if pad:down() and map.ysize > 1 then map.ysize = map.ysize -1 end
		if pad:cross() and pad ~= oldpad then loop = false end
		screen:fontPrint(hyperion, 15, 95, map.ysize, white)
		screen:fontPrint(hyperion, 15, 155, "Map will be "..(math.floor(((map.ysize*map.tilesize)/272)*100)/100).." screens high", blue)
		screen:flip()
		oldpad = pad
	end
	mapinit()
end

if menuitem == 2 then loadproject() end


loadtiles()
editmap()

-- main loop

while true do
	screen:clear()
	screen:blit(0, 0, lum)
	if menuitem==1 then screen:fontPrint(hyperion, 10, 150,"Save Project", red)
		else screen:fontPrint(hyperion, 10, 150,"Save Project", white)
	end
	if menuitem==2 then screen:fontPrint(hyperion, 10, 180,"Continue editing", red)
		else screen:fontPrint(hyperion, 10, 180,"Continue editing", white)
	end
	if menuitem==3 then screen:fontPrint(hyperion, 10, 210,"Exit LuMM", red)
		else screen:fontPrint(hyperion, 10, 210,"Exit LuMM", white)
	end
	pad = Controls.read()
	if pad:up() and pad ~= oldpad then menuitem = menuitem - 1 end
	if pad:down() and pad ~= oldpad then menuitem = menuitem + 1 end
	if menuitem < 1 then menuitem = 3 end
	if menuitem > 3 then menuitem = 1 end
	if pad:start() and pad ~= oldpad then
		if menuitem == 1 then
			System.usbDiskModeDeactivate()
			System.createDirectory(pjdir)
			table.save(map, pjdir..pname..".map")
		end
		if menuitem == 2 then 
			oldpad = pad
			editmap() end
		if menuitem == 3 then System.quit() end
	end
	oldpad = pad
	screen:waitVblankStart()
	screen:flip( )
end
