; ATALAN Tetris
; Tetris implemented in ATALAN Programming language
; (c) 2010 Rudla Kudla

use atari

;Screen and DISPLAY LIST

;This if GR.0 screen 40 characters width and 24 characters height
;We force it to address $8000 to make it aligned correctly.

out s@$8000:array(0..39,0..23) of byte

;Display list is defined as constant array.
;We do not specify size of the array, so it is cimputed automatically.
;At the top are three empty lines 8 pixels height.
;First line specifies the pointer to start of the data (see the reference to s).
 
const dl:array = 3 times $70, $42, s, 23 times $02, $41, dl

; Position of well on the screen (Left Margin, Right Margin, ...)
const 
	LM = 15
	RM = 26
	TM = 1
	BM = 22

	NEXT_LEFT = 29
	NEXT_TOP  = 5 
	
const ;Graphics characters 
	WALL_CHR = 69
	RWALL_CHR = 70

x:16..25	;x position of the falling tile
y:1..21		;y position of the falling tile
ti:0..15  ; size of tile definition (we use 4x4 tiles, so it is 16 bytes)
c:byte
nx:x			; new x position of tile
ny:y      ; new y position of tile
steps:0..20
action'delay:0..10
collision:bool
j:0..3
k:0..3
down'lock:bool
tile, next'tile:0..18
ntile:tile
score:0..30000

;Array of tile shapes.
; Base tile character is 64
; If the left line should be empty, add 1
; To make bottom line empty add 2

const tiles:array(ti, tile) = (

	0,65,0,0
	0,65,67,0
	0,66,0,0
	0,0,0,0
;--- 1	
	0,0,0,0
	0,65,64,0
	0,66,67,0
	0,0,0,0
;--- 2
	0,65,0,0
	0,65,0,0
	0,65,0,0
	0,66,0,0
;--- 3	
	0,65,67,0
	0,65,0,0
	0,66,0,0
	0,0,0,0
;--- 4
	66,68,0,0
	0 ,65,0,0
	0 ,66,0,0
	0 ,0 ,0,0
;--- 5
	0,65,0,0
	0,66,68,0
	0,0,66,0
	0,0,0,0	
;--- 6	
	0,0,65,0
	0,65,67,0
	0,66,0,0
	0,0,0,0
;--- 7
	0,0,0,0
	66,68,67,0
	0,66,0,0
	0,0,0,0
;--- 8	
	0,65,0,0
	66,68,0,0
	0,66,0,0
	0,0,0,0
;---9
	0,65,0,0
	66,67,67,0
	0,0,0,0
	0,0,0,0
;--- 10  (-> 2)
	0,0,0,0
	66,67,67,67
	0,0,0,0
	0,0,0,0
;--- 11  (->3)
	0,0,0,0
	66,67,68,0
	0,0,66,0
	0,0,0,0
;--- 12
	0,65,0,0
	0,65,0,0
	66,67,0,0
	0,0,0,0
;--- 13
	65,0,0,0
	66,67,67,0
	0,0,0,0
	0,0,0,0
;--- 14
	0, 0, 65,0
	66,67,67,0
	0, 0, 0, 0
	0, 0, 0, 0
;--- 15
	0 ,65,0 ,0
	0 ,65,0 ,0
	0 ,66,67,0
	0 ,0 ,0 ,0
;--- 16
	0,0,0,0
	65,67,67,0
	66,0,0,0
	0,0,0,0
;--- 17
	0, 65,67,0
	66,67,0, 0
	0, 0, 0, 0
	0, 0, 0, 0
;--- 18
	66,68,0, 0
	0, 66,67, 0
	0, 0, 0, 0
	0, 0, 0, 0

	)

;Rotation array defines number of rotated tile for every tile
 
const rotation:array(tile) of tile = 7, 1, 10, 11, 14, 17, 18, 8, 9, 0, 2, 12, 13, 3, 15, 16, 4, 5, 6

draw'tile:proc(tile:tile x:x y:y) =
	ti = 0
	for j
		for k
			c = tiles(ti, tile)
			if c <> 0 then s(x+k,y+j) = c
			ti = ti + 1

erase'tile:proc(tile:tile x:x y:y) =
	ti = 0
	for j
		for k
			c = tiles(ti, tile)
			if c <> 0 then s(x+k,y+j) = 0
			ti = ti + 1

test'collision:proc tile:tile x:x y:y >coll:bool =	
	coll = false
	ti = 0
	for j 
		for k
			c = tiles(ti, tile)
			ti = ti + 1
			if c <> 0 if s(x+k,y+j) <> 0 then return true


;Initialize graphics.
;We set display list, colors and font.

sdlstl = dl
COLOR0(2) = 0
COLOR0(5) = 0
set'font file "tetris.fnt"

start'game@

; Draw the well

s(0,0) = "ATALAN TETRIS"
s(NEXT_LEFT,3) = "NEXT"
s(NEXT_LEFT,1)   = "SCORE: 0     "

for y1:y
	for x s(x,y1) = 0

for y1:TM..BM
  s(LM,y1) = WALL_CHR
  s(RM,y1) = RWALL_CHR

for x:LM..RM
	s(x,BM) = WALL_CHR
			
action'delay = 0
down'lock = false

;Generate a new tile
;New tile is always generated on same position.

x,y = 19,1
tile = RANDOM bitand 7
next'tile = RANDOM bitand 7
score = 0

draw'tile next'tile NEXT_LEFT NEXT_TOP
		
loop@

steps = 20
	
if STICK(0) = down and not down'lock then steps = 1
	
while steps > 0

	draw'tile tile x y
		
	nx = x
	ntile = tile

	if STICK(0) <> down then down'lock = false
	
	;Move or rotate the tile (there is minimum delay between two actions)
	if action'delay > 0
		action'delay = action'delay - 1
	else
		action'delay = 6
		if STICK(0) = right
			inc nx
		else if STICK(0) = left
			dec nx		
		else if STRIG(0) = pressed
			ntile = rotation(tile)
		else
			action'delay = 0
		
	;Wait tick (synchronize with the screen)
	timer = 0
	while timer < 1
		
	erase'tile tile x y
	
	;move the tile to new position, if there is no collision

	;collision = test'collision ntile nx y	
	if not test'collision ntile nx y then
		x = nx
		tile = ntile
	
	dec steps

	;Tile falls faster, if user keeps joystick down
	if STICK(0) = down and not down'lock then
			if steps > 5 then steps = 0
			 

;Tile moves down.
			
;collision = test'collision	tile x y+1

if not test'collision(tile x y+1) then
	inc y
else
	draw'tile tile x y 
	
	;=== remove full lines
 	
	y,ny = 21
	
	while y >= 1
		collision = false
		for x where s(x,y) = 0 collision = true
						
		if collision then
			for x s(x,ny) = s(x, y)
			dec ny
			dec y 
		else
			score = score + 1
			; Line y will be skipped (it is not copied)
			dec y
			; The line above skipped line must be modified to cleanly 'cut' the tiles
			for x
				c = s(x,y)
				if c = 64 then c = 67
				if c = 65 then c = 66
				if c = 68 then c = 67
				s(x,y) = c 

	s(29,1) = "SCORE: [score]"
	
	;If player holds down, we do not want the new tile to start falling fast
	if STICK(0) = down then down'lock = true

	;Next tile becomes falling tile and we generate and draw new next tile 
	tile = next'tile
	erase'tile next'tile NEXT_LEFT NEXT_TOP
	next'tile = random bitand 7
	draw'tile next'tile NEXT_LEFT NEXT_TOP
	x,y = 19,1

	;If the new tile collides immediatelly, the game is over
		
	if test'collision tile x y then goto game'over
		
goto loop

;The game is over.
;Display message and wait for the player to press the button to play again.
	
game'over@

s(19,10) = "GAME"
s(19,11) = "OVER"

until STRIG(0) = pressed

erase'tile next'tile NEXT_LEFT NEXT_TOP

goto start'game
