-- Reactor
-- Version 1.3
-- Studio Kagato
-- Copyright 2006 Andrew Carpenter

-- CONSTANTS

BLACK = Color.new(0,0,0)
RED = Color.new(255,0,0)
GREEN = Color.new(0,255,0)
BLUE = Color.new(0,0,128)
YELLOW = Color.new(255,255,0)
WHITE = Color.new(255,255,255)

-- bubble animation sequence
ANIM = {0,1,1,1,2,2,2,3,3,3}

START_VEL = 0.1 -- pixels per millisecond
VEL_STEP = 0.005 -- speed increase per level
START_COUNT = 5
START_POINTS = 10 -- initial points per hit
MIN_POINTS = 5 -- minimum points per hit
MAX_HEALTH = 10


-- FUNCTIONS

function checkBubbleCollision(x,y,radius)
-- x,y: coordinate of target
-- radius: radius of target

	distance = radius+ball.rad
	-- distance: distance below which collision occurs
	
	actual_dist = math.sqrt( (x - ball.x)^2 + (y - ball.y)^2 )
	
	if actual_dist < distance then
		-- get ball velocity
		bv = math.sqrt(ball.vx^2 + ball.vy^2)
		-- get angle of incidence
		bi = math.atan2(ball.vy, ball.vx)
		-- get angle of normal
		normal = math.atan2(y - ball.y, x - ball.x)
		-- get angle of reflection
		br = bi + math.pi - ((bi-normal)*2)

		-- set new ball velocity
		ball.vx = math.cos(br)*bv
		ball.vy = math.sin(br)*bv

		return true
	end
	return false
end

function checkWallCollision(px,py)
-- px,py: coordinate of paddle

	wdist = walldist-ball.rad
	-- wdist: distance from game centre beyond which wall collision occurs
	
	actual_wdist = math.sqrt( (240 - ball.x)^2 + (136 - ball.y)^2 )
	
	if actual_wdist > wdist then
		-- reset ball's position to be equal to wall distance
		ba = math.atan2(ball.y-136, ball.x-240)
		ball.x = math.cos(ba)*wdist+240
		ball.y = math.sin(ba)*wdist+136
		
		--running = false
		-- get ball velocity
		bv = math.sqrt(ball.vx^2 + ball.vy^2)
		-- get angle of incidence
		bi = math.atan2(ball.vy, ball.vx)
		
		-- get angle of normal to wall
		wall_normal = math.atan2(136 - ball.y, 240 - ball.x)
		-- get angle of reflection from wall
		br = bi + math.pi - ((bi-wall_normal)*2)
		
		-- check if this is a paddle collision
		pdist = paddle.crad+ball.rad
		-- pdist: minimum distance between ball & paddle before collision

		actual_pdist = math.sqrt( (px - ball.x)^2 + (py - ball.y)^2 )

		if actual_pdist < pdist then
			-- PADDLE HIT
			-- get angle of normal to paddle
			pad_normal = math.atan2(py - ball.y, px - ball.x)
			-- get angle of reflection from paddle
			brp = bi + math.pi - ((bi-pad_normal)*2)
			
			-- average of two reflections
			bra = (brp + br) / 2 + math.pi
			
			snd_paddle:play()
		else
			-- WALL HIT
			bra = br
			wall_color = RED
			health = health -1
			snd_wall:play()
		end

		-- set new ball velocity
		ball.vx = math.cos(bra)*bv
		ball.vy = math.sin(bra)*bv
		
		return true
	end
	return false
end

function createBubble()
	local angle = math.random()*math.pi*2
	local distance = math.random(5,80)
	bubble = { c=math.random(7),x=math.cos(angle)*distance+240,y=math.sin(angle)*distance+136,anim=1 }
	return bubble
end

function initGame()
	level = 1
	health = MAX_HEALTH
	score = 0
	ball_vel = START_VEL
	bubbleCount = START_COUNT
	timer = Timer.new()
	initLevel()
end

function initLevel()
	ball.anim = 1
	chain = 0
	points = START_POINTS
	hitCount = 0
	
	if level > 1 then
		ball_vel = ball_vel + VEL_STEP
		bubbleCount = START_COUNT + level
	end
	
	if health < MAX_HEALTH then
		health = health + 1
	end
	
	bubbles = {}
	for i=1,bubbleCount do
		bubbles[i] = createBubble()
	end
	timer:reset()
	lastTime = 0
end

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


-- SPLASH SCREENS

	img_kagato = Image.load("data/kagato.png")
	
	screen:fillRect(0,0,480,272,BLACK)
	screen:flip()
	delay(30)
	
	screen:blit(0,0,img_kagato)
	screen.flip()
	delay(180)
	
	screen:fillRect(0,0,480,272,BLACK)
	screen:flip()
	
	img_kagato = nil
	img_comp = nil

-- INITIALISATION

	img_reactor = Image.load("data/reactor.png")
	img_help = Image.load("data/help.png")
	img_bg = Image.load("data/bg.png")
	img_paddle = Image.load("data/paddle.png")
	img_ball = Image.load("data/ball.png")
	img_gameover = Image.load("data/gameover.png")
	img_levelup = Image.load("data/levelup.png")
	img_paused = Image.load("data/paused.png")
	
	snd_paddle = Sound.load("data/paddle.wav")
	snd_bubble = Sound.load("data/bubble.wav")
	snd_wall = Sound.load("data/wall.wav")
	snd_levelup = Sound.load("data/levelup.wav")
	
	wall_color = BLACK
	
	walldist = 130 -- from centre
	paddle = { dist=135, rad=50, cdist=205,crad=100 }
	ball = { x=240, y=136, vx=0, vy=0, rad=8, anim=1 }
	state = "title"
	-- states: title, help, ready, play, paused, levelup, gameover

-- MAIN LOOP

lastControls = Controls.read()

while true do
	controls = Controls.read()

	jx = controls:analogX()
	jy = controls:analogY()
	
	-- get paddle position
	pangle = math.atan2(jy,jx)
	px = paddle.dist * math.cos(pangle) + 240
	py = paddle.dist * math.sin(pangle) + 136
	pcx = paddle.cdist * math.cos(pangle) + 240
	pcy = paddle.cdist * math.sin(pangle) + 136

	
	if state == "title" then

		screen:blit(0,0,img_reactor)
		if controls~=lastControls and controls:start() then
			initGame()
			state = "ready"
		end
		if controls:cross() then
			initGame()
			state = "help"
		end
		
	elseif state == "help" then

		screen:blit(0,0,img_reactor)
		screen:blit(102,0,img_help)
		if controls:start() then
			initGame()
			state = "ready"
		end
	
	elseif state == "play" or state == "ready" then
	
		time = timer:time() - lastTime
		
		-- move ball
		if ball.anim <= 10 then 
			if state == "play" then
				vel = ball_vel * time
				ball.x = ball.x + (vel*ball.vx)
				ball.y = ball.y + (vel*ball.vy)
			else
				ball.x = (walldist - 10) * math.cos(pangle) + 240
				ball.y = (walldist - 10) * math.sin(pangle) + 136
				-- launch ball?
				if controls:cross() then
					snd_paddle:play()
					ball.vx = -(math.cos(pangle))
					ball.vy = -(math.sin(pangle))
					state = "play"
					timer:start()
				end
			end
		end
		
		-- check for collision with wall/ paddle
		wall_color = BLACK
		if ball.anim==1 then
			if checkWallCollision(pcx,pcy) then
				if chain > 0 then
					points = START_POINTS
				else
					points = points - 1
					if points < MIN_POINTS then points = MIN_POINTS end
				end
				chain = 0
			end
		end
		
		-- advance animation on hit bubbles,
		-- check for collision with bubbles,
		-- copy remaining bubbles to new table
		
		new_bubbles = {}
		for key,bubble in pairs(bubbles) do
			if bubble.anim > 1 then bubble.anim = bubble.anim + 1 end
			
			if bubble.anim==1 then
				if checkBubbleCollision(bubble.x,bubble.y, ball.rad) then
					chain = chain + 1
					bubbleCount = bubbleCount - 1
					score = score + (points*chain)
					bubble.anim = 2
					snd_bubble:play()
				end
			end
			
			if bubble.anim <= 10 then
				table.insert(new_bubbles,bubble)
			end
		end
		bubbles = new_bubbles
		
		
		
		if ball.anim == 1 then
			if bubbleCount <= 0 then ball.anim = 2 end
		else
			if ball.anim <=10 then
				ball.anim = ball.anim + 1
			else
				score = score + (10*health)
				snd_levelup:play()
				state = "levelup"
				timer:stop()
			end
		end
		
		if health <= 0 then
			state = "gameover"
			timer:stop()
		end
		
		if state == "play" and controls~=lastControls and controls:start() then
			state = "paused"
			timer:stop()
		end
		
		lastTime = timer:time()
		
	elseif state == "paused" then
		wall_color = BLUE
		if controls~=lastControls and controls:start() then
			state = "play"
			timer:start()
		end
		
	elseif state == "levelup" then
		wall_color = GREEN
		if controls:start() then
			level = level + 1
			initLevel()
			state = "ready"
		end
		
	elseif state == "gameover" then
		wall_color = RED
		
		if controls~=lastControls and controls:start() then
			initGame()
			state = "title"
		end
	end
	
	if state~="title" and state~="help" then
		-- draw wall
		screen:fillRect(104,0,272,272,wall_color)
		
		if state=="ready" or state=="play" then
			-- draw paddle
			screen:blit(px-paddle.rad,py-paddle.rad,img_paddle)
		end
		
		-- draw background
		screen:blit(0,0,img_bg)
		
		if state=="ready" or state=="play" then
			-- draw ball
			if ball.anim <=10 then screen:blit(ball.x-ball.rad, ball.y-ball.rad, img_ball, 0, ANIM[ball.anim]*ball.rad*2, ball.rad*2, ball.rad*2) end
	
			-- draw bubbles
			for key,bubble in pairs(bubbles) do
				screen:blit(bubble.x-ball.rad, bubble.y-ball.rad, img_ball, bubble.c*ball.rad*2, ANIM[bubble.anim]*ball.rad*2, ball.rad*2, ball.rad*2)
			end
		end
	
		-- draw health meter
		screen:fillRect(430,236-(health*20),10,(health*20),GREEN)
		
		if state=="paused" then
			screen:blit(110,108,img_paused)
		end
		
		if state=="levelup" then
			screen:blit(111,111,img_levelup)
		end
		
		if state=="gameover" then
			screen:blit(110,107,img_gameover)
		end
		
		if chain>1 then
			screen:print(25,40, "Chain:", YELLOW)
			screen:print(30,51, "x"..chain, YELLOW)
			screen:print(25,215, "Score:", YELLOW)
			screen:print(25,226, score, YELLOW)
		else
			screen:print(25,40, "Level:", GREEN)
			screen:print(25,51, level, GREEN)
			screen:print(25,215, "Score:", GREEN)
			screen:print(25,226, score, GREEN)
		end
	end
	
	screen.flip()
	screen.waitVblankStart()
	
	lastControls = controls 

	
end
