-- ELECTRIS
-- Version 1.0
-- Pineapple Productions
-- Copyright 2006 Andrew Carpenter

-- CONSTANTS
PENDING = 1
INVALID = 2
VALID = 3
RED = Color.new(220,0,0)
WHITE = Color.new(255,255,255)
BLACK = Color.new(0,0,0)

-- {practice,easy,medium,hard}
LEVELUP_SCORE = {0,500,500,250} -- score increment at which level increases
LEVELUP_COUNT = {0,4,3,2} -- number of completed circuits to increase level
PENALTY = {0,90000,45000,30000} -- milliseconds until penalty rows added (if no circuit)



-- text block is 24 characters wide
--			"123456789012345678901234"

VERSION =		"  ELECTRIS VERSION 1.0  ";

MENU_TEXT = {}
MENU_TEXT[1] = {	"NO TIMER, NO LEVELS...  ",
			"TAKE YOUR TIME AND LEARN",
			"THE BASICS OF ELECTRIS. " }

MENU_TEXT[2] = {	"90 SECOND PENALTY TIMER.",
			" LEVEL UP AFTER EVERY   ",
			"500 POINTS, 4 CIRCUITS. "}

MENU_TEXT[3] = {	"45 SECOND PENALTY TIMER.",
			" LEVEL UP AFTER EVERY   ",
			"500 POINTS, 3 CIRCUITS. "}

MENU_TEXT[4] = {	"30 SECOND PENALTY TIMER.",
			" LEVEL UP AFTER EVERY   ",
			"250 POINTS, 2 CIRCUITS. "}

MENU_TEXT[5] = {	" INSTRUCTIONS ON HOW TO ",
			"     PLAY THE GAME.     "}


--		"123456789012345678901234"
HELP = {}
HELP[1] = { 	"ELECTRIS IS PLAYED WITH ",
		"THE PSP HELD VERTICALLY.",
		"USE THE D-PAD TO MOVE,  ",
		"ROTATE AND DROP FALLING ",
		"BLOCKS INTO POSITION.   " }

HELP[2] = { 	"THE GOAL OF ELECTRIS IS ",
		"TO USE THE BLOCKS TO    ",
		"CREATE CLOSED CIRCUITS. "}

HELP[3] = { 	"THE GOAL OF ELECTRIS IS ",
		"TO USE THE BLOCKS TO    ",
		"CREATE CLOSED CIRCUITS. "}

HELP[4] = { 	"WHEN A CIRCUIT HAS BEEN ",
		"CLOSED, THE BLOCKS THAT ",
		"FORM IT ARE REMOVED FROM",
		"THE GAME BOARD.         "}

HELP[5] = { 	"BE CAREFUL WHEN PLACING ",
		"BLOCKS -- YOU WON'T BE  ",
		"ABLE TO CLOSE CIRCUITS  ",
		"WITH OPEN ENDS THAT YOU ",
		"CANNOT REACH!           " }

HELP[6] = { 	"IF YOU CLOSE A CIRCUIT  ",
		"WITH 5 OR MORE BLOCKS,  ",
		"YOU WILL BE GIVEN A     ",
		"'CIRCUIT-BREAKER'.      "}

HELP[7] = { 	"USE CIRCUIT-BREAKERS TO ",
		"REMOVE BLOCKS THAT YOU  ",
		"DON'T WANT. PRESS THE   ",
		"ROTATE BUTTON TO CHANGE ",
		"ITS FUNCTION.           " }

HELP[8] = { 	"BIGGER CIRCUITS PROVIDE ",
		"BIGGER CIRCUIT-BREAKERS;",
		"THE 'LINE BREAKER' WILL ",
		"ERASE A WHOLE COLUMN OR ",
		"ROW OF BLOCKS.          " }

HELP[9] = { 	"THE BAR AT THE TOP IS   ",
		"THE PENALTY TIMER.      ",
		"IF YOU DON'T COMPLETE A ",
		"CIRCUIT BEFORE THE TIMER",
		"RUNS OUT...             " }

HELP[10] = { 	"IF YOU DON'T COMPLETE A ",
		"CIRCUIT BEFORE THE TIMER",
		"RUNS OUT...             ",
		"A PENALTY ROW IS ADDED  ",
		"TO THE BOTTOM.          " }

HELP[11] = { 	"",
		"",
		"       GOOD LUCK!       "}


			

-- FUNCTIONS

function loadData()
	-- load all the items that aren't needed until the game starts
	img_bg = Image.load("graphics/background.png")
	img_block = Image.load("graphics/block.png")
	img_eraser = Image.load("graphics/eraser.png")
	
	snd_drop = Sound.load("sound/click2.wav")
	snd_remove = Sound.load("sound/longzap.wav")
	snd_bonus = Sound.load("sound/doorbell.wav")
	snd_gameover = Sound.load("sound/gameover.wav")
	snd_alarm = Sound.load("sound/alarm.wav")
	
	-- block definition: tile,N,E,S,W
	block = {}
	block[1] = { [1]={1,n=false,e=false,s=false,w=false} }
	
	block[2] = { [1]={2,n=true,e=false,s=false,w=false},
                     [2]={3,n=false,e=true,s=false,w=false},
                     [3]={4,n=false,e=false,s=true,w=false},
                     [4]={5,n=false,e=false,s=false,w=true} }
		     
	block[3] = { [1]={6,n=true,e=true,s=false,w=false},
                     [2]={7,n=false,e=true,s=true,w=false},
                     [3]={8,n=false,e=false,s=true,w=true},
                     [4]={9,n=true,e=false,s=false,w=true} }
		     
	block[4] = { [1]={10,n=true,e=true,s=true,w=false},
                     [2]={11,n=false,e=true,s=true,w=true},
                     [3]={12,n=true,e=false,s=true,w=true},
                     [4]={13,n=true,e=true,s=false,w=true} }
		     
	block[5] = { [1]={14,n=true,e=false,s=true,w=false},
                     [2]={15,n=false,e=true,s=false,w=true} }
		     
	block[6] = { [1]={16,n=true,e=true,s=true,w=true} }

	-- eraser definition: tile, S,W,E,SW,SE (# to erase)
	block[7] = { [1]={2,s=1,w=0,e=0,sw=0,se=0},
					[2]={3,s=0,w=1,e=0,sw=0,se=0},
					[3]={4,s=0,w=0,e=1,sw=0,se=0} } -- single eraser
	block[8] = { [1]={5,s=2,w=0,e=0,sw=0,se=0},
					[2]={6,s=0,w=2,e=0,sw=0,se=0},
					[3]={7,s=0,w=0,e=2,sw=0,se=0},
					[4]={8,s=1,w=0,e=0,sw=0,se=1},
					[5]={9,s=1,w=0,e=0,sw=1,se=0},
					[6]={10,s=0,w=1,e=1,sw=0,se=0} }  -- double eraser
	block[9] = { [1]={11,s=3,w=0,e=0,sw=0,se=0},
					[2]={12,s=0,w=3,e=0,sw=0,se=0},
					[3]={13,s=0,w=0,e=3,sw=0,se=0},
					[4]={14,s=1,w=0,e=0,sw=1,se=1},
					[5]={15,s=1,w=1,e=1,sw=0,se=0} }  -- triple eraser

	block[10] = { [1]={16,s=0,w=10,e=10,sw=0,se=0},
					[2]={17,s=17,w=0,e=0,sw=0,se=0} }  -- line eraser

	-- animation sequence for removing blocks
	sequence = { 3,2,3,2,3,2,3,2,4,5,6,7,8 }
	-- sounds to play at given animation frames
	animsound = { [1]=snd_close, [9]=snd_remove }

	multiplier = 10  -- circuit block count * multiplier = score
	
	emin = { 5, 10, 15, 50 } -- minimum block in circuit to score erasers
	ebonus = 25 -- bonus for not erasing any blocks (double if also dropped to floor)
	
	dropTimer = Timer.new()
	penaltyTimer = Timer.new()

	loaded = true

end

function gameInit(difficulty)
	if not loaded then
		loadData()
	end
	
	levelupscore = LEVELUP_SCORE[difficulty] -- score increment at which level increases
	levelupcount = LEVELUP_COUNT[difficulty] -- number of completed circuits to increase level
	penalty = PENALTY[difficulty] -- milliseconds until penalty rows added (if no circuit)

	--randomize
	math.randomseed( os.time() )
	math.random(); math.random(); math.random()

	-- get current and next blocks
	currentblock = randomBlock()
	pos = { x = 6, y = 0 }
	nextblock = randomBlock()

	-- initialise game grid
	grid = initGrid()
	checkgrid = initGrid()
	
	-- base drop speed in milliseconds; reduced by level - see doDrop()
	dropdelay = 1000
	
	score = 0
	circuitcount = 0
	level = 1
	chain = 1
	bonuscount = 0
	
	nextscore = levelupscore
	nextcount = levelupcount
	
	dropTimer:reset()
	dropTimer:start()
	
	penaltyAlarm = penalty - 4000 -- 5 seconds before penalty
	penaltyTimer:reset()
	if penalty > 0 then penaltyTimer:start() end
end	

function loadHelp(index)
	return Image.load("help/"..index..".png")
end

-- Generate a random block
   -- tile: block tile
   -- rot:  block rotation
function randomBlock()
	
	-- simple tile selection:
	-- local t = math.random(2,6)
	
	-- balanced tile selection:
	local rnd = math.random(1,100)
	
	if     rnd > 80 then t = 2  -- 20%
	elseif rnd > 55 then t = 3  -- 25%
	elseif rnd > 30 then t = 4  -- 25%
	elseif rnd > 10 then t = 5  -- 20%
	else                 t = 6  -- 10%
	end
	
	local r = math.random(1,table.getn(block[t]))
	
	return { tile = t, rot = r };
end

function penaltyBlock()
	return { tile = 1, rot = 1 }
end

function nextBlock()
	currentblock = nextblock
	pos.x = 6; pos.y = 0
	nextblock = randomBlock()
end

function rotateBlock(b)
	local maxrot = table.getn(block[b.tile])
	b.rot = b.rot +1
	if b.rot > maxrot then
		b.rot = 1
	end
end

function getBlock(b)
	return block[ b.tile ][b.rot]
end

function getBlockIndex(b)
	return block[ b.tile ][ b.rot ][1]
end

function drawBlock(x,y,b,i)
	if b.tile >6 then
		screen:blit( 448-(y*26), (x-1)*26 + 6, img_eraser,208-(i*26),(getBlockIndex(b)-1)*26,26,26,true)
	else
		screen:blit( 448-(y*26), (x-1)*26 + 6, img_block,208-(i*26),(getBlockIndex(b)-1)*26,26,26,true)
	end
end

function nextEraser(etype)
	currentblock = { tile = 6 + etype, rot = 1 }
	pos.x = 6; pos.y = 0
end

function isEraser()
	if currentblock.tile > 6 then return true end
	return false
end

function eraseBlocks(x,y,b)
	local ecount = 0
	local block = getBlock(b)
	checkgrid = initGrid()
	checkgrid[x][y] = VALID
	for i=1,block.e do
		if x+i <=10 and grid[x+i][y]~=nil then
			checkgrid[x+i][y] = VALID
			ecount = ecount + 1
		end
	end
	for i=1,block.w do
		if x-i > 0 and grid[x-i][y]~=nil then
			checkgrid[x-i][y] = VALID
			ecount = ecount + 1
		end
	end
	for i=1,block.s do
		if y+i <=17 and grid[x][y+i]~=nil then 
			checkgrid[x][y+i] = VALID
			ecount = ecount + 1
		end
	end
	for i=1,block.se do
		if y+i <=17 and x < 10 and grid[x+1][y+i]~=nil then
			checkgrid[x+1][y+i] = VALID
			ecount = ecount + 1
		end
	end
	for i=1,block.sw do
		if y+i <=17 and x > 1 and grid[x-1][y+i]~=nil then
			checkgrid[x-1][y+i] = VALID
			ecount = ecount + 1
		end
	end
	
	return ecount
end

function playSound(sound,count)
	if(sound ~= nil) then
		sound:play()
		if count ~= nil then
			delay(count)
		end
	end
end

-- Simple delay function, where timer is overkill
function delay(count)
	for i=1,count do
		screen.waitVblankStart()
	end
end

function doDrop()
	dtime = dropTimer:time()
	if dtime > dropdelay / ((level/5)+1) then
		dropTimer:reset()
		dropTimer:start()
		return true
	end
end

function countCircuit()
	circuitcount = circuitcount +1
	
	--increase level if circuit count requires
	if levelupcount > 0 and circuitcount >= nextcount then
		level = level + 1
		nextcount = nextcount + levelupcount
	end
end

function addScore(points)
	score = score + points
	
	--increase level if score requires
	if levelupscore > 0 and score >= nextscore then
		level = level + 1
		nextscore = nextscore + levelupscore
	end
	
	if penalty > 0 and points > 1 then
		-- circuit completed; reset penalty timer
		penaltyAlarm = penalty - 4000
		penaltyTimer:reset()
		penaltyTimer:start()
	end
end


function checkForCircuit(x,y,blockset)

local rootnode = false

if blockset == nil then
	blockset = {}
	rootnode = true
end

	local count = 0;
	local check;
	
	table.insert(blockset,{x,y})

	if grid[x][y] ~= nil and grid[x][y].tile ~= 1 then

		-- mark this position in checkgrid
		checkgrid[x][y] = PENDING

		local b = getBlock(grid[x][y])
		
		-- check all connected blocks
		if b.n then -- CONNECTS NORTH
			if y<=1                              -- open to top border
			or grid[x][y-1]==nil                 -- open to empty space
			or not getBlock(grid[x][y-1]).s      -- doesn't connect to block above
			or checkgrid[x][y-1]==INVALID
			then
				checkgrid[x][y] = INVALID
				return false
			end
			if checkgrid[x][y-1]==nil then        -- check connecting block (if not being checked)
				check = checkForCircuit(x,y-1,blockset)
				if check==false then
					checkgrid[x][y] = INVALID
					if rootnode then invalidate(blockset) end
					return false
				else count = count + check end
			end
		end
		
		if b.e then -- CONNECTS EAST
			if x>=10                             -- open to right border
			or grid[x+1][y]==nil                 -- open to empty space
			or not getBlock(grid[x+1][y]).w      -- doesn't connect to right block
			or checkgrid[x+1][y]==INVALID
			then 
				checkgrid[x][y] = INVALID
				return false
			end
			
			if checkgrid[x+1][y]==nil then        -- check connecting block (if not being checked)
				check = checkForCircuit(x+1,y,blockset)
				if check==false then
					checkgrid[x][y] = INVALID
					if rootnode then invalidate(blockset) end
					return false
				else count = count + check end
			end
		end

		if b.s then -- CONNECTS SOUTH
			if y>=17                             -- open to bottom border
			or grid[x][y+1]==nil                 -- open to empty space
			or not getBlock(grid[x][y+1]).n      -- doesn't connect to block below
			or checkgrid[x][y+1]==INVALID
			then
				checkgrid[x][y] = INVALID
				return false
			end
			
			if checkgrid[x][y+1]==nil then        -- check connecting block (if not being checked)
				check = checkForCircuit(x,y+1,blockset)
				if check==false then
					checkgrid[x][y] = INVALID
					if rootnode then invalidate(blockset) end
					return false
				else count = count + check end
			end
		end

		if b.w then -- CONNECTS WEST
			if x<=1                              -- open to left border
			or grid[x-1][y]==nil                 -- open to empty space
			or not getBlock(grid[x-1][y]).e      -- doesn't connect to left block
			or checkgrid[x-1][y]==INVALID
			then
				checkgrid[x][y] = INVALID
				return false
			end
			
			if checkgrid[x-1][y]==nil then        -- check connecting block (if not being checked)
				check = checkForCircuit(x-1,y,blockset)
				if check==false then
					checkgrid[x][y] = INVALID
					if rootnode then invalidate(blockset) end
					return false
				else count = count + check end
			end
		end
		-- if haven't returned false by now, all "child" blocks are valid
		checkgrid[x][y] = VALID
		return count+1
	else
		-- there is no block here to check
		checkgrid[x][y] = INVALID
		return false
	end
	
end

function invalidate(blockset)
	for key,value in pairs(blockset) do
		checkgrid[value[1]][value[2]] = INVALID
	end
end

-- take a string and display its characters on the screen at x,y
-- (x,y is screen coordinate, not grid reference)
-- row, col refer to position of character in text.png
function drawText(str,x,y,color)
	str = string.upper(str)
	for i=1,string.len(str) do
		local row = 80;
		char = string.byte(str,i)
		if char >= 33 and char <= 90 then
			local col = char - 33
			if char > 64 then
				row = row - 16
				col = char - 65
			end
			
			if color == "yellow" or color == true then row = row - 32
			elseif color == "green" then row = row - 64
			end
			
			screen:blit(x,(i-1)*10+y,img_text,row,col*10,16,10)
		end
	end
end

-- draw active game elements: next block, current block, score etc
-- (grid is done separately for loop efficiency)
function drawGameState()

	--draw nextblock
	drawBlock(5,0,nextblock,1)
	
	--draw currentblock
	drawBlock(pos.x,pos.y,currentblock,2)
	
	--draw score: scorebox is 9 digits wide, starting at 457,9
	-- score is right-aligned
	drawText(score,457,(9 - string.len(score)) * 10 + 9)

	-- draw battery state
	if string.len(score) < 9 then
		local row = 64
		-- can't do this in the test player
		if System.powerIsBatteryCharging() then row = 32 end
		local battery = System.powerGetBatteryLifePercent()
		--local battery = 100
		
		if     battery > 90 then screen:blit(457,9,img_text,row,270,16,10)
		elseif battery > 75 then screen:blit(457,9,img_text,row,280,16,10)
		elseif battery > 50 then screen:blit(457,9,img_text,row,290,16,10)
		elseif battery > 25 then screen:blit(457,9,img_text,row,300,16,10)
		else                     screen:blit(457,9,img_text,row,310,16,10)
		end
	end
	
	--draw info (level, bonus etc): box is 9 digits wide, starting at 457,173
	if chain > 1 then
		drawText("CHAIN*"..chain,457,173,"yellow")
	elseif bonuscount > 0 then
		drawText("BONUS "..(ebonus*bonuscount),457,173,"yellow")
	elseif state == "gameover" then
		drawText("GAME OVER",457,173)
	else	
		-- LEVEL <n>
--		screen:blit(457,173,img_text,32,0,16,50)  -- LEVEL
--		drawDigits(level,457,233)                 -- value

		-- [penalty]  <level n>
		if penalty > 0 then
			local ptime = penaltyTimer:time()
			if ptime < penalty then
				local penaltyBar = math.ceil( (penalty-ptime) / penalty * 54)
				screen:fillRect(458,174,14, penaltyBar,RED)
			end
			
			drawText(level,457,(9 - string.len(level)) * 10 + 173)
		else
			drawText("PRACTICE",457,173,"green")
		end
	end

end

function setState(s)
	-- initial state is "title"
	if s == "help" then
		-- show the help screens
		currenthelp = 1
		playSound(snd_select,30)
		
	elseif s == "menu" then
		-- show the game menu
		img_title = nil
		imgHelp = nil

	elseif s == "play" then
		-- active game
		playSound(snd_select,30)
		if state == "pause" then
			Music.resume()
		else
			Music.playFile( "sound/fr-some.xm", true )
		end
		
	elseif s == "pause" then
		-- paused game
		-- show pause menu
		pausemenu = 1
		Music.pause()
		dropTimer:stop()
		if penalty > 0 then penaltyTimer:stop() end
		playSound(snd_select,30)
		
	elseif s == "circuit" then
		animframe = 1;
		-- circuit has been completed, animate & remove

	elseif s == "fall" then
		-- circuit removed, loose blocks fall
		
	elseif s == "check" then
		-- blocks have fallen, check for new circuits
	
	elseif s == "penalty" then
		-- penalty timer elapsed; add penalty row

	elseif s == "gameover" then
		-- game has ended
		Music.stop()
		playSound(snd_gameover)

	end
	state = s
end

function initGrid()
	local g = {}
	for x=1,10 do
		g[x] = {}
		for y=1,17 do
			g[x][y] = nil
		end
	end
	return g
end



-- INITIALISATION

state = "title"

img_logo = Image.load("graphics/kagato.png")
img_title = Image.load("graphics/title.png")
img_text = Image.load("graphics/text.png")
img_menu = Image.load("graphics/menu.png")

snd_move = Sound.load("sound/click.wav")
snd_select = Sound.load("sound/activate.wav")
snd_close = Sound.load("sound/zap.wav")

currenthelp = 1
menuitem = 3 -- default is MEDIUM

-- INTRO
screen:fillRect(0,0,480,272,BLACK)

screen.flip()
delay(30)

screen:fillRect(0,0,480,272,BLACK)

for i=0,255,16 do
	screen:fillRect(0,0,480,272,Color.new(i,i,i))
	screen.flip()
	delay(2)
end
delay(30)

playSound(snd_close)
screen:fillRect(0,0,480,272,WHITE)
screen:blit( 90, 50, img_logo,0,0,300,150,true)
screen.flip()
delay(5)

screen:fillRect(0,0,480,272,WHITE)
screen:blit( 90, 50, img_logo,0,150,300,150,true)
screen.flip()
delay(5)

screen:fillRect(0,0,480,272,WHITE)
screen:blit( 90, 50, img_logo,0,300,300,150,true)
screen.flip()
delay(120)

screen:fillRect(0,0,480,272,BLACK)
screen.flip()
delay(10)

img_logo = nil

-- MAIN LOOP

lastkeyin = Controls.read()

while true do
	dopaint = true
	local keyin = Controls.read()
	
	-- process game based on current state
	if state == "title" then

		screen:blit(0,0,img_title)
	
		if keyin ~= lastkeyin then
			if keyin:start() or keyin:cross() then
				setState("menu");
				playSound(snd_select,30)
			end
		end

	elseif state == "menu" then
	
		if keyin ~= lastkeyin then
			if keyin:left() then
				menuitem = menuitem + 1
				if menuitem > 5 then menuitem = 1 end
				playSound(snd_move)
				
			elseif keyin:right() then
				menuitem = menuitem - 1
				if menuitem < 1 then menuitem = 5 end
				playSound(snd_move)
			
			elseif keyin:start() or keyin:cross() or keyin:down() then
				if menuitem == 5 then setState("help")
				else
					gameInit(menuitem)
					setState("play")
				end
			end
		end
	
		screen:blit(0,0,img_menu)
		drawText("SELECT DIFFICULTY:",360,46,"green")
		drawText("(PRESS X OR START)",340,46,"green")
		
		drawText("  PRACTICE  ",300,76,(menuitem==1))
		drawText("    EASY    ",280,76,(menuitem==2))
		drawText("   MEDIUM   ",260,76,(menuitem==3))
		drawText("    HARD    ",240,76,(menuitem==4))
		drawText("INSTRUCTIONS",200,76,(menuitem==5))
		
		for i = 1,table.getn(MENU_TEXT[menuitem]) do
			drawText(MENU_TEXT[menuitem][i],132-(i*20),16,"green")
		end
		
		drawText(VERSION,10,16,"green")
		
	elseif state == "help" then
	
		if imgHelp == nil then imgHelp = loadHelp(currenthelp) end

		screen:blit(0,0,img_menu)
		screen:blit(148,0,imgHelp)
		
		for i = 1,table.getn(HELP[currenthelp]) do
			drawText(HELP[currenthelp][i],132-(i*20),16,"green")
		end
		
		if currenthelp > 1 then drawText("< PREV",10,16,"yellow") end
		drawText("X: MENU",10,100)
		if currenthelp < 11 then drawText("NEXT >",10,196,"yellow") end
		
		if keyin ~= lastkeyin then
			if keyin:up() then
				currenthelp = currenthelp - 1;
				if currenthelp < 1 then
					currenthelp = 1
				else
					imgHelp = loadHelp(currenthelp)
					playSound(snd_move)
				end
			end
			if keyin:down() then
				currenthelp = currenthelp + 1;
				if currenthelp > 11 then
					currenthelp = 11
				else
					imgHelp = loadHelp(currenthelp)
					playSound(snd_move)
				end
			end
			if keyin:start() or keyin:cross() then
				playSound(snd_select,30)
				setState("menu")
			end
		end

		
	elseif state == "play" then
		if penalty > 0 then 
			local ptime = penaltyTimer:time()
	
			if ptime > penalty then
				setState("penalty")
			elseif ptime > penaltyAlarm then
				-- 5 second warning
				playSound(snd_alarm)
				penaltyAlarm = penaltyAlarm + 1000
			end
		end

		--drop currentblock 1 square if appropriate 
		if (keyin:left()) or doDrop() then
			if pos.y < 17 and grid[pos.x][pos.y+1] == nil then
				pos.y = pos.y + 1
			else
				if(pos.y == 0) then
					
					setState("gameover")
				else
					playSound(snd_drop)
					
					grid[pos.x][pos.y] = currentblock;
					if isEraser(currentblock) then
						local ecount = eraseBlocks(pos.x,pos.y,currentblock)
						if ecount == 0 then
							-- bonus for not erasing anything
							bonuscount = 1
							addScore(ebonus)
							playSound(snd_bonus,20)
							-- double bonus if you hit the floor
							if pos.y==17 then
								bonuscount = 2
								addScore(ebonus)
								playSound(snd_bonus,20)
							end
						end
						
						setState("circuit")
						nextBlock()
					else
						addScore(1)

						checkgrid = initGrid()
						local check = checkForCircuit(pos.x,pos.y)
						if check ~= false then
							countCircuit()
							addScore(check*multiplier)
							setState("circuit")
							if     check >= emin[4] then nextEraser(4)
							elseif check >= emin[3] then nextEraser(3)
							elseif check >= emin[2] then nextEraser(2)
							elseif check >= emin[1] then nextEraser(1)
							else nextBlock()
							end
						else
							nextBlock()
						end
					
						--delay for a moment in case player is still holding down "drop"
						delay(10)
					end
				end
			end
		end
		
		-- other controls
		if keyin ~= lastkeyin then
			if keyin:left() then
				-- action handled above; sound should only play once
				playSound(snd_move)
				
			elseif keyin:up() then
				playSound(snd_move)
				if pos.y > 0 and pos.x > 1 and grid[pos.x-1][pos.y] == nil then
					pos.x = pos.x -1
				end
				
			elseif keyin:down() then
				playSound(snd_move)
				if pos.y > 0 and pos.x < 10 and grid[pos.x+1][pos.y] == nil then
					pos.x = pos.x +1
				end
			
			elseif keyin:right() then
				playSound(snd_move)
				rotateBlock(currentblock)
			
			elseif keyin:start() then
				setState("pause")
			end
		end
			
		
		--draw background
		screen:blit(0,0,img_bg)
		
		--draw grid
		for x=1,10 do
			for y=1,17 do
				if grid[x][y] ~= nil then
					drawBlock(x,y,grid[x][y],2)
				end
			end
		end

		drawGameState()
		
	elseif state == "pause" then
	
		if keyin ~= lastkeyin then
			if keyin:left() then
				pausemenu = pausemenu + 1
				if pausemenu > 2 then pausemenu = 1 end
				playSound(snd_move)
				
			elseif keyin:right() then
				pausemenu = pausemenu - 1
				if pausemenu < 1 then pausemenu = 2 end
				playSound(snd_move)
			
			elseif keyin:start() or keyin:cross() or keyin:down() then
				if pausemenu == 1 then
					dropTimer:start()
					if penalty > 0 then penaltyTimer:start() end
					setState("play")
				elseif pausemenu == 2 then
					playSound(snd_gameover,90)
					setState("menu")
				end
			end
		end
	
		screen:blit(0,0,img_menu)
		
		-- fill in help window
		for by=13,17 do
			for bx=1,10 do
				screen:blit( 448-(by*26), (bx-1)*26 + 6, img_block,156,0,26,26,true)
			end
		end
		
		drawText("   GAME PAUSED:   ",360,46,"green")
		
		drawText("  CONTINUE  ",300,76,(pausemenu==1))
		drawText("  END GAME  ",280,76,(pausemenu==2))
		
	elseif state == "circuit" then
		
		--draw background
		screen:blit(0,0,img_bg)
		
		--draw grid
		for x=1,10 do
			for y=1,17 do
				if grid[x][y] ~= nil then
					if checkgrid[x][y]==VALID then
						-- animate closed circuit
						if animframe <= table.getn(sequence) then
							drawBlock(x,y,grid[x][y],sequence[animframe])
						else
							-- remove blocks from closed circuit
							grid[x][y] = nil
						end
					else
						drawBlock(x,y,grid[x][y],2)
					end
				end
			end
		end
		
		drawGameState()

		if animframe > table.getn(sequence) then
			bonuscount = 0
			setState("fall")
		else
			playSound( animsound[animframe] )
			delay(2)
			animframe = animframe + 1
		end

	elseif state == "fall" then
	
		--draw background
		screen:blit(0,0,img_bg)
		
		local falling = false
		--draw grid; start from bottom to check for gaps
		for x=1,10 do
			for y=17,1,-1 do
				if grid[x][y] ~= nil then
					if y<17 and grid[x][y+1] == nil then
						falling = true
						grid[x][y+1] = grid[x][y]
						grid[x][y] = nil
						drawBlock(x,y,grid[x][y+1],2)
						
						if y>=16 or grid[x][y+2] ~= nil then
							-- block has hit something
						end
					else
						drawBlock(x,y,grid[x][y],2)
					end
				end
			end
		end
		
		drawGameState()
		
		delay(5)
		
		
		if not falling then
			-- check grid for circuits after fall
			checkgrid = initGrid()
			local circuitFound = false
			local chainscore = 0
			for x=1,10 do
				for y=17,1,-1 do
					if grid[x][y] ~= nil and checkgrid[x][y]==nil then
						local check = checkForCircuit(x,y)
						if check~=false then
							countCircuit()
							chainscore = chainscore + check
							circuitFound = true
						end
					end
				end
			end
			if circuitFound then
				chain = chain + 1
				addScore(chainscore*chain*multiplier)
				nextEraser(4) -- chains automatically grant level 4 eraser 
				setState("circuit")
			else
				if chain > 1 then
					for i=1,chain do
						playSound(snd_bonus,20)
					end
				end
				chain = 1
				state = "play"
			end
		end
		
		elseif state == "penalty" then
		
		--draw background
		screen:blit(0,0,img_bg)
		
		--draw grid
		
		local pasttop = false
		
		for x=1,10 do
			for y=1,17 do
				if y == 1 and grid[x][y] ~= nil then
					pasttop = true
				else
					-- move this block up one row
					grid[x][y-1] = grid[x][y]
					if grid[x][y] ~=nil then
						drawBlock(x,y-1,grid[x][y-1],2)
					end
				end
				if y == 17 then
					grid[x][y] = penaltyBlock()
					drawBlock(x,y,grid[x][y],2)
				end
			end
		end
		
		playSound(snd_drop)
		
		penaltyAlarm = penalty - 4000
		penaltyTimer:reset()
		penaltyTimer:start()

		if grid[pos.x][pos.y] ~=nil then
			if pos.y > 1 then
				pos.y = pos.y - 1
			else
				pasttop = true
			end
		end

		if pasttop then
			setState("gameover")
		else
			state = "play"
		end
		
		drawGameState()
		
		delay(5)

	
	elseif state == "gameover" then
	
		dopaint = false
	
		if keyin ~= lastkeyin then
			if keyin:start() then
				setState("menu");
			end
		end
		
	end
	
	if keyin ~= lastkeyin then
		if keyin:triangle() then
			screenshot = (screenshot or 0) + 1
			screen:save("screenshot"..screenshot..".png")
			playSound(snd_bonus)
		end
	end

	lastkeyin = keyin
	
	-- show updated screen
	if dopaint then
		screen.flip()
	end
	screen.waitVblankStart()
end
