-- title:  tetris
-- author: AlKau http://alkau.itch.io
-- desc:   classic Tetris game
-- script: lua
-- input:  gamepad

local CELL_H=8
local CELL_W=8

local isFirstTic=true
local isSfxOn=true
local state=0

local MENU_SCR="Menu"
local GAME_SCR="Game"
local PAUSE_SCR="Pause"
local OVER_SCR="GameOver"

local MENU_SFX="MenuSfx"
local SEL_MENU_SFX="SelectionMenuSfx"
local GAME_OVER_SFX="GameOverSfx"
local FELL_SFX="FellSfx"
local LINE_SFX="LineSfx"

local EMPTY_CELL_SPR=1
local DIE1_CELL_SPR=17
local DIE2_CELL_SPR=16

local DARKBLUE_CELL_SPR=3
local GREEN_CELL_SPR=4
local YELLOW_CELL_SPR=5
local RED_CELL_SPR=6
local BLUE_CELL_SPR=7
local BROWN_CELL_SPR=8
local LIGHTBLUE_CELL_SPR=9

local bestScore=0
local playerScore=0
local screenMng
local gameScreen
local soundMng

local saveSfxOnIdx=0
local saveScoreIdx=1

-- Helpers --
function PrintToCell(text,cx,cy,color)
 print(text,CELL_W*cx,CELL_H*cy+2,color)
end

function DrawToCell(sprId,cx,cy)
 spr(sprId,CELL_W*cx,CELL_H*cy)
end

-- temporary placeholder
function sfx()
end

local function UpdateBestScore(score)
 if bestScore<score then
	 bestScore=score
	end
 pmem(saveScoreIdx,bestScore)
end

local function LoadBestScore()
 bestScore = pmem(saveScoreIdx)
end

-- Game Grid
local function GameGrid(width,height)
 local s={
	 grid=nil,
		width=width or 10,
		height=height or 16,
		animDelay=100,
		animTime=0,
		isAnim=false
	}
	
	function s.Create()
	 s.grid={}
	 for y=1,s.height do
		 s.grid[y]={}
		 for x=1,s.width do
			 s.grid[y][x]=EMPTY_CELL_SPR
			end
		end
	end
	
	function s.Draw(cx,cy)
	 for y=1,s.height do
 	 for x=1,s.width do
			 DrawToCell(s.grid[y][x],cx+x-1,cy+y-1)
			end
		end
	end
	
	function s.Add(x,y,sprId)
	 s.grid[y][x]=sprId
	end
	
	function s.UpdateLine()
	 local res=0
	 for y=1,s.height do
		 local isLine = true
 	 for x=1,s.width do
    isLine = isLine and not (s.grid[y][x] == EMPTY_CELL_SPR)
				if not isLine then break end
			end
			
			if isLine then
			 res=res+1
			 s.isAnim = true
				s.animTime=time()
			 for x=1,s.width do
     s.grid[y][x] = DIE1_CELL_SPR
				end
			end
			
		end
		return res
	end
	
	local function replace(oldSprId, newSprId)
	 local res = false
		for y=1,s.height do
 	 for x=1,s.width do
			 if s.grid[y][x] == oldSprId then
				 s.grid[y][x]=newSprId
					res = true
				end
			end
		end
		
		return res
	end
	
	local function removeLine(gridLine)
		table.remove(s.grid,gridLine)
		local newLine={}
		for x=1,s.width do
			newLine[x]=EMPTY_CELL_SPR
		end
		table.insert(s.grid,1,newLine)
	end
	
	local function remove(oldSprId)
 	local process=true
  while process do
		 process=false
			for y=1,s.height do
				 if s.grid[y][1] == oldSprId then
					 removeLine(y)
						process=true
						break
					end
				end
		end
	end
	
	function s.AnimationUpdate()
	 if not s.isAnim then return end
		local now = time()
		if now > s.animTime + s.animDelay then
		 s.animTime=now
		 remove(DIE2_CELL_SPR)
			s.isAnim = replace(DIE1_CELL_SPR, DIE2_CELL_SPR)
		end
	end
	
	return s
end

-- Tetrimino class
local function Tetrimino(sprId)
 local s={
	 sprId=sprId,
		width=0,
		height=0,
		pos={x=0,y=0},
		shape=0,
	 shapes={},
		wallkickGap=0,
		isFell=false
	}
	
	function s.Move(x,y)
	 s.pos.x=x
		s.pos.y=y
	end
	
	local function isCollision(grid,shape,pos)
	 local sh=s.GetShape(shape)
		for y=1,s.height do
	  for x=1,s.width do
			 if sh~=nil and sh[y][x]>0 then
				 local posX=x+pos.x
					local posY=y+pos.y
					
					if posX<=0 or posX>grid.width then
					 return true
					elseif posY>grid.height then
					 return true
					elseif not (grid.grid[posY][posX]==EMPTY_CELL_SPR) then
					 return true
					end
				end
			end
		end
		
		return false
	end
	
	function s.Create()end
	
	function s.Draw(cx,cy)
	 local shape=s.GetShape(s.shape)
		for y=1,s.height do
	  for x=1,s.width do
			 if shape~=nil and shape[y][x]>0 then
				 local posX=cx+x-1+s.pos.x
					local posY=cy+y-1+s.pos.y
				 DrawToCell(s.sprId,posX,posY)
				end
			end
		end
	end
	
	function s.GetShape(shape)
	 if shape>0 then
		 return s.shapes[shape]
		end
		return nil
	end
	
	function s.GoDown(grid)
  local p={x=s.pos.x,y=s.pos.y}
		p.y=p.y+1
	 
		if not isCollision(grid,s.shape,p) then
		 s.pos=p
		else
		 s.isFell=true
		end
	end
	
	function s.GoLeft(grid)
  local p={x=s.pos.x,y=s.pos.y}
		p.x=p.x-1
	 
		if not isCollision(grid,s.shape,p) then
		 s.pos=p
		end
	end
	
	function s.GoRight(grid)
  local p={x=s.pos.x,y=s.pos.y}
		p.x=p.x+1
	 
		if not isCollision(grid,s.shape,p) then
		 s.pos=p
		end
	end
	
	local function doWallkick(grid,shape)
	 if shape==2 or shape==4 then return end
	
  for gap=1,s.wallkickGap do
			-- try right
	  local pRight={x=s.pos.x+gap,y=s.pos.y}
	  if not isCollision(grid,shape,pRight) then
		  s.pos = pRight
			 s.shape = shape
			 return
		 end
			
	 	-- try left
   local pLeft={x=s.pos.x-gap,y=s.pos.y}
	  if not isCollision(grid,shape,pLeft) then
			 s.pos = pLeft
			 s.shape = shape
			 return
		 end
		end

	end
	
	function s.DoRotation(clockwise,grid)
 	local shape=s.shape;
	 if clockwise then
		 shape=shape+1
		else
		 shape=shape-1
		end
		
		if shape>4 then
		 shape=1
		elseif shape<1 then
		 shape=4
		end

		if not isCollision(grid,shape,s.pos) then
		 s.shape=shape
		else
		 doWallkick(grid,shape)
		end
	end
	
	function s.CopyToGrid(grid)
	 local sh=s.GetShape(s.shape)
			for y=1,s.height do
				for x=1,s.width do
			 	if sh~=nil and sh[y][x]>0 then
						local posX=x+s.pos.x
						local posY=y+s.pos.y
			
     	grid.grid[posY][posX]=s.sprId
				end
			end
		end
	end
	
	function s.IsGameOver(grid)
	 return isCollision(grid,s.shape,s.pos)
	end
	
	return s
end

local function ZTetrimino(sprId)
 local s=Tetrimino(sprId)
	
	function s.Create()
		s.width=3
	 s.height=3
	 s.shape=1
		s.wallkickGap=1
	 s.shapes[1]={
			{1,1,0},
			{0,1,1},
			{0,0,0}
		}
		
	 s.shapes[2]={
		 {0,0,1},
			{0,1,1},
			{0,1,0}
		}

	 s.shapes[3]={
		 {0,0,0},
			{1,1,0},
			{0,1,1}
		}
		
	 s.shapes[4]={
		 {0,1,0},
			{1,1,0},
			{1,0,0}
		}
	end
	
	return s
end

local function STetrimino(sprId)
 local s=Tetrimino(sprId)
	
	function s.Create()
		s.width=3
	 s.height=3
	 s.shape=1
		s.wallkickGap=1
	 s.shapes[1]={
			{0,1,1},
			{1,1,0},
			{0,0,0}
		}
		
	 s.shapes[2]={
		 {0,1,0},
			{0,1,1},
			{0,0,1}
		}

	 s.shapes[3]={
		 {0,0,0},
			{0,1,1},
			{1,1,0}
		}
		
	 s.shapes[4]={
		 {1,0,0},
			{1,1,0},
			{0,1,0}
		}
	end
	
	return s
end

local function LTetrimino(sprId)
 local s=Tetrimino(sprId)
	
	function s.Create()
		s.width=3
	 s.height=3
	 s.shape=1
		s.wallkickGap=1
	 s.shapes[1]={
			{0,0,1},
			{1,1,1},
			{0,0,0}
		}
		
	 s.shapes[2]={
		 {0,1,0},
			{0,1,0},
			{0,1,1}
		}

	 s.shapes[3]={
		 {0,0,0},
			{1,1,1},
			{1,0,0}
		}
		
	 s.shapes[4]={
		 {1,1,0},
			{0,1,0},
			{0,1,0}
		}
	end
	
	return s
end

local function JTetrimino(sprId)
 local s=Tetrimino(sprId)
	
	function s.Create()
		s.width=3
	 s.height=3
	 s.shape=1
		s.wallkickGap=1
	 s.shapes[1]={
			{1,0,0},
			{1,1,1},
			{0,0,0}
		}
		
	 s.shapes[2]={
		 {0,1,1},
			{0,1,0},
			{0,1,0}
		}

	 s.shapes[3]={
		 {0,0,0},
			{1,1,1},
			{0,0,1}
		}
		
	 s.shapes[4]={
		 {0,1,0},
			{0,1,0},
			{1,1,0}
		}
	end
	
	return s
end

local function TTetrimino(sprId)
 local s=Tetrimino(sprId)
	
	function s.Create()
		s.width=3
	 s.height=3
	 s.shape=1
		s.wallkickGap=1
	 s.shapes[1]={
			{0,1,0},
			{1,1,1},
			{0,0,0}
		}
		
	 s.shapes[2]={
		 {0,1,0},
			{0,1,1},
			{0,1,0}
		}

	 s.shapes[3]={
		 {0,0,0},
			{1,1,1},
			{0,1,0}
		}
		
	 s.shapes[4]={
		 {0,1,0},
			{1,1,0},
			{0,1,0}
		}
	end
	
	return s
end

local function ITetrimino(sprId)
 local s=Tetrimino(sprId)
	
	function s.Create()
		s.width=4
	 s.height=4
	 s.shape=1
		s.wallkickGap=2
	 s.shapes[1]={
			{0,0,0,0},
			{1,1,1,1},
			{0,0,0,0},
			{0,0,0,0}
		}
		
	 s.shapes[2]={
			{0,0,1,0},
			{0,0,1,0},
			{0,0,1,0},
			{0,0,1,0}
		}

	 s.shapes[3]={
			{0,0,0,0},
			{0,0,0,0},
			{1,1,1,1},
			{0,0,0,0}
		}
		
	 s.shapes[4]={
			{0,1,0,0},
			{0,1,0,0},
			{0,1,0,0},
			{0,1,0,0}
		}
	end
	
	return s
end

local function OTetrimino(sprId)
 local s=Tetrimino(sprId)
	
	function s.Create()
		s.width=4
	 s.height=3
	 s.shape=1
		s.wallkickGap=0
	 s.shapes[1]={
			{0,1,1,0},
			{0,1,1,0},
			{0,0,0,0}
		}
		
	 s.shapes[2]={
			{0,1,1,0},
			{0,1,1,0},
			{0,0,0,0}
		}

	 s.shapes[3]={
			{0,1,1,0},
			{0,1,1,0},
			{0,0,0,0}
		}
		
	 s.shapes[4]={
			{0,1,1,0},
			{0,1,1,0},
			{0,0,0,0}
		}
	end
	
	return s
end

local function Spawner()
 local s={
 	items={}
	}
	
	function s.AddTetrimino(tetrimino,sprId)
	 table.insert(s.items,{tetrimino=tetrimino,sprId=sprId})
	end
	
	function s.Spawn()
	 local item = s.items[math.random(#s.items)]
		return item.tetrimino(item.sprId)
	end
	
	return s
end
-- UI --

-- Menu class --
local function Menu(gap,color)
 local s={
	 items={},
		gap = gap or 1,
		color = color or 15,
		idx=1
	}
	
	local function change(move)
	 local idx=s.idx
	
	 s.idx = s.idx + move
		if s.idx > #s.items then s.idx = #s.items end
		if s.idx < 1 then s.idx = 1 end
		
		if idx~=s.idx then
		 soundMng.PlaySfx(MENU_SFX)
		end
	end
	
	local	function action()
	 soundMng.PlaySfx(SEL_MENU_SFX)
	 s.items[s.idx].Action()
	end
	
	function s.Add(item)
	 table.insert(s.items,item)
	end
	
	function s.Draw(x,y)
	 for i,item in pairs(s.items) do
		 local offset = (i-1)*s.gap
		 item.isSel = i == s.idx
		 item.Draw(x,y+offset,s.color)
		end
	end
	
	function s.Update()
		if btnp(0) then
		 change(-1)
		elseif btnp(1) then
		 change(1)
		elseif btnp(4) or btnp(5) then
		 action()
		end
	end
	
	function s.Reset()
	 s.idx=1
	end

	return s
end

-- MenuItem class
local function MenuItem(name,action,selClr,sel)
	local s={
		sel=sel or ">",
		selClr = selClr or 15,
		name=name or "item",
		isSel=false,
		action=action
	}
	
	function s.Draw (x,y,color)
	 local clr = color
	 if s.isSel then
		 clr = s.selClr
		 PrintToCell(s.sel,x,y,clr)
		else
		
		end
	 PrintToCell(s.name,x+2,y,clr)
 end
	 
	function s.Action()
	 if s.action~=nil then s.action(s) end
	end
	
	return s
end

-- ToggleMenuItem class : MenuItem
local function ToggleMenuItem(name,isOn,action,sel,on,off)
 local s = MenuItem(name,action,sel)
	s.rawName = name
	s.isOn = isOn or false
	s.On = on or "On"
	s.Off = off or "Off"
	s.DrawBase = s.Draw
	s.ActionBase = s.Action
	
	function s.Action()
	 s.isOn = not s.isOn
		s.ActionBase()
	end
	
	function s.Draw(x,y,color)
		local toggle = s.Off
		if s.isOn then toggle = s.On	end
	 s.name = s.rawName.." "..toggle
		s.DrawBase(x,y,color)
 end
	
	return s
end

-- Screen --
local function ScreenManager()
 local s={
	 items={},
		current=nil
	}
	
	local function leave()
	 if s.current~=nil then s.current.OnLeave() end
	end
	
	local function enter()
	 if s.current~=nil then s.current.OnEnter() end
	end
	
	function s.Add(item)
		table.insert(s.items,item)
	end
	
	function s.GoTo(name)
	 for i,screen in pairs(s.items) do
		 if screen.name == name then
			 leave()
			 s.current = screen
				enter()
			end
		end
	end
	
	function s.Update()
	 s.current.Update()
	end
	
	function s.Draw()
	 s.current.Draw()
	end
	
	return s
end

local function Screen(name)
 local s={
	 name = name or "scr#1"
	}
	
	function s.Update()	end
	function s.Draw()	end
	function s.OnEnter() end
	function s.OnLeave() end
	
	return s
end

-- Menu Screen
local function MenuScreen()
 local s = Screen(MENU_SCR)
	s.menu = Menu(2)
	
	local function onNewGame()
 	gameScreen.Reset()
	 screenMng.GoTo(GAME_SCR)
	end
	
	local function onSoundChanged(itemMenu)
	 soundMng.isOn=itemMenu.isOn
		soundMng.SaveSettings()
	end
	
	s.menu.Add(MenuItem("New Game",onNewGame,14))
	s.menu.Add(ToggleMenuItem("Sound",soundMng.isOn,onSoundChanged,14))
		
	function s.OnEnter()
		s.menu.Reset()
	end
	
	function s.Update()
	 s.menu.Update()
	end
	
	function s.Draw()
		map(30,0,30,17)
	 s.menu.Draw(10,10,15)
	end
	
	return s
end

-- Pause Screen
local function PauseScreen()
 local s=Screen(PAUSE_SCR)
	
	s.menu = Menu(2)
	
	local function onContinue()
	 screenMng.GoTo(GAME_SCR)
	end
	
	local function onMenu()
	 screenMng.GoTo(MENU_SCR)
	end
	
	s.menu.Add(MenuItem("Continue",onContinue,14))
	s.menu.Add(MenuItem("Main Menu",onMenu,14))
 
	function s.OnEnter()
	 s.menu.Reset()
	end
	 
	function s.Update()
	 s.menu.Update()
	end
	
	function s.Draw()
		map(60,0,30,17)
	 s.menu.Draw(10,10,15)
	end
		
	return s
end

-- Game Screen
local function GameScreen()
 local s=Screen(GAME_SCR)
	s.grid=GameGrid()
	s.spawner=Spawner()
	s.current=nil
	s.next=nil
	s.level=1
	s.fallSpeed=550
	s.levelSpeed=45*1000
	s.fallTime=0
	s.levelTime=0
	s.pauseTime=0
	s.isGameOver=false
	
	local function onPause()
	 screenMng.GoTo(PAUSE_SCR)
		s.pauseTime=time()
	end
	
	local function onGameOver()
		UpdateBestScore(playerScore)
	 screenMng.GoTo(OVER_SCR)
	end
	
	local function spawn()
	 if s.current==nil and not s.grid.isAnim then
	  s.current =	s.next
		 s.current.Move(3,0)
			s.fallTime=time()
			
	 	s.next =	s.spawner.Spawn()
 		s.next.Create()
		end
	end
	
	local function levelUp()
	 if s.level < 10 then
		 s.level = s.level + 1
			s.fallSpeed = s.fallSpeed-50
		end
	end
	
	local function fall()
 	local now=time()
	 if s.current~=nil and s.fallTime+s.fallSpeed < now then
		 s.fallTime = now
			s.current.GoDown(s.grid)
		end
		
		if s.levelTime+s.levelSpeed < now then
		 s.levelTime = now
			levelUp()
		end
	end
	
	local function completion()
	 if s.current==nil then return end

  if s.current.IsGameOver(s.grid) then
		 s.isGameOver=true
		 return
		end
		
	 if s.current.isFell then
		 s.current.CopyToGrid(s.grid)
	  s.current = nil
			local res = s.grid.UpdateLine()
			
			local score=0
			
			if     res==1 then score=100
			elseif res==2 then score=300
			elseif res==3 then score=500
			elseif res==4 then score=800
			end
			
			if score==0 then
 			soundMng.PlaySfx(FELL_SFX)
			else
 			soundMng.PlaySfx(LINE_SFX)
			end
	
	  score=score+4*s.level
	
			playerScore = playerScore + s.level*score
		end
	end
	
	function s.Create()
 	s.spawner.AddTetrimino(ZTetrimino,RED_CELL_SPR)
 	s.spawner.AddTetrimino(STetrimino,GREEN_CELL_SPR)
 	s.spawner.AddTetrimino(LTetrimino,YELLOW_CELL_SPR)
 	s.spawner.AddTetrimino(JTetrimino,DARKBLUE_CELL_SPR)
 	s.spawner.AddTetrimino(TTetrimino,BLUE_CELL_SPR)
 	s.spawner.AddTetrimino(OTetrimino,BROWN_CELL_SPR)
 	s.spawner.AddTetrimino(ITetrimino,LIGHTBLUE_CELL_SPR)
	end
	
	function s.Reset()
	 s.grid.Create()

		s.next =	s.spawner.Spawn()
		s.next.Create()
		s.current=nil
		
		s.levelTime=time()
		s.fallTime=time()
		playerScore=0
		s.level=1
		s.fallSpeed=550
		s.isGameOver=false
	end
	
	function s.OnEnter()
	 spawn()
		s.fallTime=time()
		
		if s.pauseTime>0 then
		 local passTime=s.levelTime-s.pauseTime
			s.levelTime=time()+passTime
		end
		
		s.pauseTime=0
	end
	
	function s.Update()
		completion()
		spawn()
		
		if s.isGameOver then
		 onGameOver()
		 return
		end
	
		if btnp(5) then onPause() end
  
		if s.current ~= nil then
 		if btnp(4,10,10) or btnp(0,10,10) then s.current.DoRotation(true,s.grid) end
 		if btnp(1,5,5) then s.current.GoDown(s.grid) end
 		if btnp(2,5,5) then s.current.GoLeft(s.grid) end
 		if btnp(3,5,5) then s.current.GoRight(s.grid) end
		end
		
		s.grid.AnimationUpdate()
		fall()
	end
	
	function s.Draw()
	
	--Background
 	map(0,0,30,17)
	
	--Score
  local score = string.format("%07d", playerScore)
 	PrintToCell("Score: ",1,1,6)
 	PrintToCell(score,1,2,15)

	--BestScore
	 local best = string.format("%07d", bestScore)
 	PrintToCell("Best: ",1,4,6)
 	PrintToCell(best,1,5,15)
		
	--Level
	 local level = string.format("%02d", s.level)
 	PrintToCell("Level: ",1,13,6)
 	PrintToCell(level,2,14,15)
	
	 s.grid.Draw(9,0)
		s.next.Draw(23,2)
		if s.current~=nil then
		 s.current.Draw(9,0)
		end
	end
		
	return s
end

-- Game Over Screen
local function GameOverScreen()
 local s=Screen(OVER_SCR)
	local start
	local delay=5000
	
	local function onMenu()
	 screenMng.GoTo(MENU_SCR)
	end
	
	function s.OnEnter()
		start=time()
		soundMng.PlaySfx(GAME_OVER_SFX)
	end
	
	function s.OnLeave()
		soundMng.BreakSfx(GAME_OVER_SFX)
	end
	 
	function s.Update()
		local now=time()
		local isTime=(now>start+delay)
		local isBtn=btnp(4) or btnp(5)
		if isTime or isBtn  then
			onMenu()
		end
	end
	
	function s.Draw()
		map(90,0,30,17)
		
			--Score
  local score = string.format("%07d", playerScore)
 	PrintToCell("Score: ",4,11,6)
 	PrintToCell(score,4,12,15)

	--BestScore
	 local best = string.format("%07d", bestScore)
 	PrintToCell("Best: ",22,4,6)
 	PrintToCell(best,22,5,15)
	end
		
	return s
end

-- SoundManager
local function SoundManager()
 local s={
		sounds={},
		isOn=true,
		volume=15
	}
	
	function s.LoadSettings()
		local isOn=pmem(saveSfxOnIdx)
		
	 if isOn<2 then
	  s.isOn=true
	 else
	  s.isOn=false
	 end
	end
	
	function s.SaveSettings()
	 local isOn=0
	
	 if s.isOn then
	  isOn=1 --On
	 else
	  isOn=2 --Off
	 end
	
  pmem(saveSfxOnIdx,isOn)
	end
	
	local function findByName(name)
	 for i,sound in pairs(s.sounds) do
		 if sound.name == name then
			 return sound
			end
		end
		
		return nil
	end
	
	local function foreach(condition,action)
	 for i,sound in pairs(s.sounds) do
		 if condition(sound) then
			 action(sound)
			end
		end
	end
	
	function s.Add(sound)
	 table.insert(s.sounds,sound)
 end
	
	function s.PlaySfx(name)
  local sound = findByName(name)
		if sound==nil then return end
		
		foreach(function(item)
           return item.channel==sound.channel
			       end,
										function(item)
										 item.Break()
										end)
										
		sound.StartPlay()
	end
	
	function s.BreakSfx(name)
  local sound = findByName(name)
		if sound==nil then return end
		
		sound.Break()
	end
	
	function s.PlayProcess()
	 local vol = 0
		
		if s.isOn then
		 vol = s.volume
		end
		
	 foreach(function(sound)
           return sound.isFinished==false
	         end,
										function(sound)
											sound.PlayProcess(vol)
										end)
	end
	
	return s
end

-- SoundEffect
local function SoundEffect(name)
 local s={
	 name=name or "sound#1",
	 currentTic=0,
		isFinished=true,
		channel=0
	}
	
	function s.Test() end
	
	function s.PlayNote(volume) end
	
	function s.StartPlay()
	 s.currentTic=0
		s.isFinished=false
		sfx(-1,0,s.channel)
	end
	
	function s.PlayProcess(volume)
	 if s.isFinished then return end
		
	 if s.currentTic>0 then
		 s.PlayNote(volume)
		end

		s.currentTic=s.currentTic+1
	end
	
	function s.Break()
	 s.currentTic=0
		s.isFinished=true
		sfx(-1,0,s.channel)
	end
	
	return s
end

-- Fell sound
function FellSfx()
 local s=SoundEffect(FELL_SFX)
	s.channel=2
	
	function s.PlayNote(volume)
	 if s.currentTic==1 then
 	 sfx(0,12,s.channel,volume,3)
		end
	end
	
	return s
end

function LineSfx()
 local s=SoundEffect(LINE_SFX)
	s.channel=2
	
	function s.PlayNote(volume)
	 if s.currentTic==1 then
 	 sfx(2,23,s.channel,volume,2)
		end
	end
	
	return s
end

function GameOverSfx()
 local s=SoundEffect(GAME_OVER_SFX)
	s.channel=3
	
	function s.PlayNote(volume)
	 if s.currentTic==1 then
 	 sfx(3,55,s.channel,volume,-2)
		end
	end
	
	return s
end

function MenuSfx()
 local s=SoundEffect(MENU_SFX)
	s.channel=0
	
	function s.PlayNote(volume)
	 if s.currentTic==1 then
 	 sfx(1,4,s.channel,volume,3)
		end
	end
	
	return s
end

function SelMenuSfx()
 local s=SoundEffect(SEL_MENU_SFX)
	s.channel=3
	
	function s.PlayNote(volume)
	 if s.currentTic==1 then
 	 sfx(1,4,s.channel,volume,3)
		end
	end
	
	return s
end

local function Init()
 if not isFirstTic then return end
	isFirstTic=false
	
	LoadBestScore()
	
	soundMng = SoundManager()
	soundMng.LoadSettings()
	soundMng.Add(FellSfx())
	soundMng.Add(LineSfx())
	soundMng.Add(GameOverSfx())
	soundMng.Add(MenuSfx())
	soundMng.Add(SelMenuSfx())
	
	screenMng = ScreenManager()
	screenMng.Add(MenuScreen())
	screenMng.Add(PauseScreen())
	gameScreen = GameScreen()
	gameScreen.Create()
	screenMng.Add(gameScreen)
	screenMng.Add(GameOverScreen())
	screenMng.GoTo(MENU_SCR)
	
end

local function Update()
 screenMng.Update()
	soundMng.PlayProcess()
end

local function Draw()
 screenMng.Draw()
end

function TIC()
	Init()
	Update()
	Draw()
end

-- <TILES>
-- 001:0000000101111113011111130111111301111113011111130111111313333333
-- 002:fffffffafaaaaaa0faaaaaa0faaaaaa0faaaaaa0faaaaaa0faaaaaa0a0000000
-- 003:8888888282222220822222208222222082222220822222208222222020000000
-- 004:bbbbbbb5b5555550b5555550b5555550b5555550b5555550b555555050000000
-- 005:eeeeeee9e9999990e9999990e9999990e9999990e9999990e999999090000000
-- 006:ccccccc6c6666660c6666660c6666660c6666660c6666660c666666060000000
-- 007:ddddddd8d8888880d8888880d8888880d8888880d8888880d888888080000000
-- 008:fffffffefeeeeee0feeeeee0feeeeee0feeeeee0feeeeee0feeeeee0e0000000
-- 009:fffffffdfdddddd0fdddddd0fdddddd0fdddddd0fdddddd0fdddddd0d0000000
-- 016:0000000101111113011111130111111301111113011111130111111313333333
-- 017:fffffffcfcccccc0fcccccc0fcccccc0fcccccc0fcccccc0fcccccc0c0000000
-- 018:0000000a0aaaaaaf0aaaaaaf0aaaaaaf0aaaaaaf0aaaaaaf0aaaaaafafffffff
-- 019:0000000202222228022222280222222802222228022222280222222828888888
-- 020:000000050555555b0555555b0555555b0555555b0555555b0555555b5bbbbbbb
-- 021:000000090999999e0999999e0999999e0999999e0999999e0999999e9eeeeeee
-- 022:000000060666666c0666666c0666666c0666666c0666666c0666666c6ccccccc
-- 023:000000080888888d0888888d0888888d0888888d0888888d0888888d8ddddddd
-- 024:0000000404444449044444490444444904444449044444490444444949999999
-- 025:0000000d0ddddddf0ddddddf0ddddddf0ddddddf0ddddddf0ddddddfdfffffff
-- 033:9999999494444440944444409444444094444440944444409444444040000000
-- </TILES>

-- <MAP>
-- 000:000000000000000020101010101010101010102000202020202020202000202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-- 001:000000000000000020101010101010101010102000201010101010102000201010101010101010101010101010101010101010101010101010101020201010101010101010101010101010101010101010101010101010101020201010101010101010101010101010101010101010101010101010101020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-- 002:000000000000000020101010101010101010102000201010101010102000201010103030301040404010505050106060601070707010505050101020201010101030303010105050106010601070707010404040106010101020201010101010101010101010101010101010101010101010101010101020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-- 003:000000000000000020101010101010101010102000201010101010102000201010101030101040101010105010106010601010701010501010101020201010101030103010501050106010601070101010401010106010101020201010303030101040401050501050101060606010000000000000001020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-- 004:000000000000000020101010101010101010102000201010101010102000201010101030101040404010105010106060101010701010505050101020201010101030303010501050106010601070707010404040106010101020201010301010104010401050105010501060101010000000000000001020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-- 005:000000000000000020101010101010101010102000202020202020202000201010101030101040101010105010106010601010701010101050101020201010101030101010505050106010601010107010401010101010101020201010301010104010401050105010501060606010000000000000001020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-- 006:000000000000000020101010101010101010102000000000000000000000201010101030101040404010105010106010601070707010505050101020201010101030101010501050106060601070707010404040106010101020201010301030104040401050105010501060101010000000000000001020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-- 007:000000000000000020101010101010101010102000000000000000000000201010101010101010101010101010101010101010101010101010101020201010101010101010101010101010101010101010101010101010101020201010303030104010401050105010501060606010101010101010101020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-- 008:000000000000000020101010101010101010102000000000000000000000201010101010101010101010101010101010101010101010101010101020201010101010101010101010101010101010101010101010101010101020201010101010101010101010101010101010101010101010101010101020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-- 009:000000000000000020101010101010101010102000000000000000000000201010101010100000000000000000000000000000000010101010101020201010101010100000000000000000000000000000000010101010101020201010101010101010101010606060103010301040404010707070101020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-- 010:000000000000000020101010101010101010102000000000000000000000201010101010100000000000000000000000000000000010101010101020201010101010100000000000000000000000000000000010101010101020201010000000000000001010601060103010301040101010701070101020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-- 011:000000000000000020101010101010101010102000000000000000000000201010101010100000000000000000000000000000000010101010101020201010101010100000000000000000000000000000000010101010101020201010000000000000001010601060103010301040404010707010101020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-- 012:000000000000000020101010101010101010102000000000000000000000201010101010100000000000000000000000000000000010101010101020201010101010100000000000000000000000000000000010101010101020201010000000000000001010601060103010301040101010701070101020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-- 013:000000000000000020101010101010101010102000000000000000000000201010101010100000000000000000000000000000000010101010101020201010101010100000000000000000000000000000000010101010101020201010000000000000001010606060101030101040404010701070101020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-- 014:000000000000000020101010101010101010102000000000000000000000201010101010101010101010101010101010101010101010101010101020201010101010101010101010101010101010101010101010101010101020201010101010101010101010101010101010101010101010101010101020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-- 015:000000000000000020101010101010101010102000000000000000000000201010101010101010101010101010101010101010101010101010101020201010101010101010101010101010101010101010101010101010101020201010101010101010101010101010101010101010101010101010101020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-- 016:000000000000000020202020202020202020202000000000000000000000202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-- </MAP>

-- <PALETTE>
-- 000:140c1c44243430346d4e4a4e854c30346524d04648757161597dced27d2c8595a16daa2cd2aa996dc2cadad45edeeed6
-- </PALETTE>

