rem bubble shooter basics in naalaa, by jsm.

import "Speed.lib"
import "SimpleParticle.lib"

constant:
	rem images.
	BUBBLE_IMG 1

	rem grid width and height.
	GW = 14
	GH = 19

visible:
	rem bubbles.
	bubbles[GW][GH][2]
	scrolly = 0
	offsetx = 0

hidden:

set window 32, 32, 480, 640

rem double buffering on.
set redraw off

rem bubble image.
load image BUBBLE_IMG, "assets/bubbles.bmp"
set image grid BUBBLE_IMG, 4, 1
set image colorkey BUBBLE_IMG, 255, 0, 255

rem init level.
for y = 0 to GH - 1
	for x = 0 to GW - 1
		bubbles[x][y][0] = 0
	next
next

randomize time()
for y = 0 to 1
	for x = 0 to GW - 1
		bubbles[x][y][0] = rnd(4) + 1
	next
next
scrolly = 0
offsetx = 0

rem shoot bubble.
flyingBubble = 0
flyingBubbleDX# = 0.0
flyingBubbleDY# = 0.0
flyingBubbleGridX = 0
flyingBubbleGridY = 0

timer = 0

nextBubble = rnd(4) + 1

proc SP_Init 128

do
	rem scroll.
	timer = (timer + 1)%10
	if timer = 0
		scrolly = (scrolly + 1)%32
		rem new line?
		if scrolly = 0
			for y = GH - 1 downto 1
				for x = 0 to GW - 1
					bubbles[x][y][0] = bubbles[x][y - 1][0]
				next
			next
			for x = 0 to GW - 1
				bubbles[x][0][0] = rnd(4) + 1
			next
			offsetx = 1 - offsetx
		endif
	endif

	if flyingBubble = 0
		rem shoot?
		if mousebutton(0, true)
			flyingBubble = nextBubble
			nextBubble = rnd(4) + 1
			flyingBubbleX# = float(width(primary))/2.0 + 16.0
			flyingBubbleY# = float(height(primary)) - 16.0

			dx# = float(mousex()) - flyingBubbleX
			dy# = float(mousey()) - flyingBubbleY
			k# = 16.0/sqr(dx*dx + dy*dy)
			flyingBubbleDX = dx*k
			flyingBubbleDY = dy*k
		endif
	else
		rem move.
		flyingBubbleX = flyingBubbleX + flyingBubbleDX
		flyingBubbleY = flyingBubbleY + flyingBubbleDY
		rem border bounce.
		if flyingBubbleX < 24.0
			flyingBubbleX = 24.0
			flyingBubbleDX = -flyingBubbleDX
		elseif flyingBubbleX > 480.0 - 24.0
			flyingBubbleX = 480.0 - 24.0
			flyingBubbleDX = -flyingBubbleDX
		endif

		rem check for collision.
		gridx = max(int(flyingBubbleX), 13)
		gridy = max(int(flyingBubbleY), 18)
		
		proc ScreenToGrid gridx, gridy
		if 1 = (gridy + offsetx)%2
			if IsBubble(gridx + 1, gridy - 1) or IsBubble(gridx, gridy - 1)
				bubbles[gridx][gridy][0] = flyingBubble
				flyingBubble = 0
 				proc PopFrom gridx, gridy
				
			endif
		else
			if IsBubble(gridx - 1, gridy - 1) or IsBubble(gridx, gridy - 1)
				bubbles[gridx][gridy][0] = flyingBubble
				flyingBubble = 0
				proc PopFrom gridx, gridy
			endif
		endif
		if flyingBubble > 0
			if IsBubble(gridx - 1, gridy) or IsBubble(gridx + 1, gridy)
				bubbles[gridx][gridy][0] = flyingBubble
				flyingBubble = 0
				proc PopFrom gridx, gridy
			endif
		endif
	endif

	proc SP_UpdateParticles

	if shouldDraw
		set color 0, 0, 0
		cls
		proc DrawBubbles

		if flyingBubble > 0
			draw image BUBBLE_IMG, int(flyingBubbleX) - 16, int(flyingBubbleY) - 16, flyingBubble - 1
		endif

		draw image BUBBLE_IMG, width(primary)/2, height(primary) - 32, nextBubble -1
	
		proc SP_DrawParticles

		set color 0, 0, 0
		draw rect 0, 0, 640, 32, true
		set color 255, 255, 255
		draw line 8, 32, 472, 32
		draw line 8, 32, 8, 640	
		draw line 472, 32, 472, 640

		redraw
		wait 16
	endif

	shouldDraw = SPD_HoldFrameDraw(60)

until keydown(27, true)

rem draw bubble field.
procedure DrawBubbles()
	set color 255, 255, 255
	for y = 0 to GH - 1
		addx = ((y + offsetx)%2)*16
		for x = 0 to GW - 1
			if bubbles[x][y][0] > 0
				drawx = x
				drawy = y
				proc GridToScreen drawx, drawy
				draw image BUBBLE_IMG, drawx, drawy, bubbles[x][y][0] - 1
			endif	
		next
	next
endproc

rem return true if there is a bubble at x, y.
function IsBubble(x, y)
	if x < 0 or x >= 14 then return false
	if y < 0 or y >= 19 then return false
	if bubbles[x][y][0] = 0 then return false
	return true
endfunc

rem convert grid to screen coordinates.
procedure GridToScreen(&x, &y)
	x = 8 + x*32 + ((y + offsetx)%2)*16
	y = y*32 + scrolly
endproc

rem convert screen to grid coordinates.
procedure ScreenToGrid(&x, &y)
	gridy = (y - scrolly)/32
	gridx = (x - 8 - ((gridy + offsetx)%2)*16)/32
	x = gridx
	y = gridy
endproc

rem pop from position.
procedure PopFrom(popx, popy)
	for y = 0 to GH - 1
		for x = 0 to GW - 1
			bubbles[x][y][1] = 0
		next
	next
	bubble = bubbles[popx][popy][0]
	count = 0
	proc PopRec bubble, popx, popy, count

	rem more than two?
	if count > 2
		rem pop them.
		for y = 0 to GH - 1
			for x = 0 to GW - 1
				if bubbles[x][y][1] = 2
					bubbles[x][y][0] = 0
				endif
			next
		next

		rem check for fallers.
		for y = 0 to GH - 1
			for x = 0 to GW - 1
				bubbles[x][y][1] = false
			next
		next
		for x = 0 to GW - 1
			count = 0
			proc FallRec x, 0
		next
		rem fall them.
		for y = 0 to GH - 1
			for x = 0 to GW - 1
				if bubbles[x][y][0] > 0 and bubbles[x][y][1] = false
					sx = x
					sy = y
					proc GridToScreen sx, sy
					proc SP_AddParticle BUBBLE_IMG, bubbles[x][y][0] - 1, false, sx + 16, sy + 16, 0.0, -2.0, 0.0, 8.0, 1.0, 0.0, 0.025, 0
					bubbles[x][y][0] = 0
				endif
			next
		next
	endif
endproc

rem recursive pop func.
procedure PopRec(bubble, x, y, &count)
	rem abort.
	if x < 0 or x >= 14 then return
	if y < 0 or y >= 19 then return
	if bubbles[x][y][1] > 0 then return
	rem mark as checked.
	bubbles[x][y][1] = true
	rem abort if color is wrong.
	if bubbles[x][y][0] <> bubble then return
	rem mark as poppable.
	bubbles[x][y][1] = 2
	rem inc count.
	count = count + 1

	rem recursive call.
	proc PopRec bubble, x - 1, y, count
	proc PopRec bubble, x + 1, y, count
	proc PopRec bubble, x, y - 1, count
	proc PopRec bubble, x, y + 1, count
	if 1 = (y + offsetx)%2 
		proc PopRec bubble, x + 1, y - 1, count
		proc PopRec bubble, x + 1, y + 1, count
	else
		proc PopRec bubble, x - 1, y - 1, count
		proc PopRec bubble, x - 1, y + 1, count
	endif
endproc

rem recursive fall func.
procedure FallRec(x, y)
	rem abort.
	if x < 0 or x >= 14 then return
	if y < 0 or y >= 19 then return
	if bubbles[x][y][1] > 0 then return
	if bubbles[x][y][0] = 0 then return
	rem mark as checked.
	bubbles[x][y][1] = true

	rem recursive call.
	proc FallRec x - 1, y
	proc FallRec x + 1, y
	proc FallRec x, y - 1
	proc FallRec x, y + 1
	if 1 = (y + offsetx)%2 
		proc FallRec x + 1, y - 1
		proc FallRec x + 1, y + 1
	else
		proc FallRec x - 1, y - 1
		proc FallRec x - 1, y + 1
	endif
endproc

