rem ==================================================================
rem Snake Game.
rem
rem By Marcus.
rem ==================================================================

import "Keycodes.lib"
import "Speed.lib"

rem Set max length of snake to the biggest theoretical one.
SNAKE_MAX_LENGTH = 30*40

rem This 2d array contains the positions of the different parts of the
rem snake.
snake[SNAKE_MAX_LENGTH][2]

rem Direction consants, for readability only.
UP = 1
DOWN = 2
LEFT = 3
RIGHT = 4

rem Level grid, where we can put the circular thingies that the snake
rem is meant to eat.
level[40][30]

rem Image constants, for readability only.
SNAKE_IMAGE = 0
GREEN_BALL_IMAGE = 1
RED_BALL_IMAGE = 2
LOGO_IMAGE = 3

rem Set up window and turn of automatic redraw.
set window 16, 16, 640, 480
set redraw off

rem Load a font that has preveously been created and modified in
rem GIMP.
rem create font 0,"comic sans ms", 24, true, false, false, true
rem save font 0, "data/comic24.txt", "data/comic24.bmp"
load font 0, "data/comic24.txt", "data/comic24.bmp"

rem Load some ball sprites.
load image SNAKE_IMAGE, "data/blue_ball.bmp"
load image GREEN_BALL_IMAGE, "data/green_ball.bmp"
load image RED_BALL_IMAGE, "data/red_ball.bmp"
load image LOGO_IMAGE, "data/snake_logo.bmp"

rem Load top score if possible. You should always use the path
rem returned by datafolder$() when writing something to file. 
open file 0, datafolder$("NaaLaa") + "snake_top_score.txt"
if file(0)
	topscore = read(0)
	free file 0
else
	topscore = 0
endif

rem Main loop.
do
	rem Init snake and speed.
	snakeLength = 1
	snakeHead = 0
	snakeDirection = 0
	speed = 10
	speedCounter = speed
	rem Start position of snake's head.
	snake[snakeHead][0] = 20
	snake[snakeHead][1] = 15
	rem Clear level.
	for y = 0 to 29; for x = 0 to 39
		level[x][y] = 0
	next; next

	rem Init score and set gameOver flag to false.
	score = 0
	gameOver = false

	rem Time when next circular thingie is to pop up.
	nextPlupTime = time()

	rem Game loop.
	do
		rem If time is greater than nextPlupTime and snake is moving, we
		rem should add another circular thingie for the snake to eat.
		if time() >= nextPlupTime and snakeDirection <> 0
			rem Make every third circular thingie red.
			if rnd(3) = 0
				level[rnd(40)][rnd(30)] = 2
			else
				level[rnd(40)][rnd(30)] = 1
			endif
			nextPlupTime = time() + 1500 + rnd(1500)
		endif

		rem Control snake.
		oldDirection = snakeDirection
		if keydown(VK_UP, true) and snakeDirection <> DOWN then snakeDirection = UP
		if keydown(VK_DOWN, true) and snakeDirection <> UP then snakeDirection = DOWN
		if keydown(VK_LEFT, true) and snakeDirection <> RIGHT then snakeDirection = LEFT
		if keydown(VK_RIGHT, true) and snakeDirection <> LEFT then snakeDirection = RIGHT
	
		rem Only move when speedCounter is 0 or player has changed the
		rem direction.
		speedCounter = speedCounter - 1
		if speedCounter = 0 or snakeDirection <> oldDirection
			speedCounter = speed

			rem Get a new position in the snake array.
			prev = snakeHead
			if snakeDirection > 0 then snakeHead = (snakeHead + 1)%SNAKE_MAX_LENGTH

			rem Move.
			if snakeDirection = UP
				snake[snakeHead][0] = snake[prev][0]
				snake[snakeHead][1] = snake[prev][1] - 1
			elseif snakeDirection = DOWN
				snake[snakeHead][0] = snake[prev][0]
				snake[snakeHead][1] = snake[prev][1] + 1
			elseif snakeDirection = LEFT
				snake[snakeHead][0] = snake[prev][0] - 1
				snake[snakeHead][1] = snake[prev][1]
			elseif snakeDirection = RIGHT
				snake[snakeHead][0] = snake[prev][0] + 1
				snake[snakeHead][1] = snake[prev][1]
			endif

			rem If snake has left the screen, make it appear on the opposite
			rem side.
			if snake[snakeHead][0] < 0 then snake[snakeHead][0] = 39
			if snake[snakeHead][0] = 40 then snake[snakeHead][0] = 0
			if snake[snakeHead][1] < 0 then snake[snakeHead][1] = 29
			if snake[snakeHead][1] = 30 then snake[snakeHead][1] = 0

			rem Check for circular thingies at snake's head position.
			x = snake[snakeHead][0]
			y = snake[snakeHead][1]
			if level[x][y] > 0
				rem increase length of snake.
				snakeLength = snakeLength + 1
				rem Red circular thingie?
				if level[x][y] = 2
					speed = max(speed - 1, 3)
					score = score + 50
				else
					score = score + 10
				endif

				rem Also set topscore if score is greater.
				if score > topscore then topscore = score

				rem Remove circular thingie.
				level[x][y] = 0
			endif

		endif

		rem Clear screen.
		set color 64, 64, 96
		cls

		rem Draw circular thingies.
		set color 255, 255, 255
		for y = 0 to 29
			for x = 0 to 39
				if level[x][y] = 1
					draw image GREEN_BALL_IMAGE, x*16, y*16
				elseif level[x][y] = 2
					draw image RED_BALL_IMAGE, x*16, y*16
				endif
			next
		next

		rem Draw snake.
		x = snake[snakeHead][0]
		y = snake[snakeHead][1]
		for i = snakeHead downto snakeHead - snakeLength + 1
			j = i
			if j < 0 then j = j + SNAKE_MAX_LENGTH
			draw image SNAKE_IMAGE, snake[j][0]*16, snake[j][1]*16
			rem Check for self collision.
			if i <> snakeHead
				if snake[j][0] = x and snake[j][1] = y then gameOver = true
			endif
		next

		rem Print score.
		set decimal 4, 0
		set caret 10, 10
		set color 255, 255, 255
		write "SCORE  "
		set color 255, 255, 0
		write score
		set color 255, 255, 255
		write "    TOP SCORE  "
		set color 0, 255, 0
		write topscore
	
		rem Print instructions if snake has not started moving.
		if snakeDirection = 0
			set color 255, 255, 255
			draw image LOGO_IMAGE, 320 - width(LOGO_IMAGE)/2, 56
			set caret 320, 119
			set color 255, 255, 0
			center "Control the snake with the arrow keys!"
			center
			set color 255, 255, 255
			center "Eat as many circular thingies that you can!"
			center "Red circular thingies makes the snake move faster,"
			center "but they also give you more points!"
			center
			center "If your snake leaves the screen on one side,"
			center "it'll appear on the other side, so don't worry!"
			center "But if the snake runs into itself you lose!"
			center
			center "Press any arrow key to start playing ..."
		endif
	
		rem Copy graphics to window and wait for a while.
		redraw
		proc SPD_HoldFrame 60
	until gameOver

	rem Save top score.
	create file 0, datafolder$("NaaLaa") + "snake_top_score.txt"
	if file(0)
		wln file 0, topscore
		free file 0
	endif
loop
