ef8a402f71
Fixes a problem where the restart button would keep focus after being pressed, making the tetris' pieces impossible to rotate without activating the button again.
218 lines
4.2 KiB
GDScript
218 lines
4.2 KiB
GDScript
|
|
|
|
extends Control
|
|
|
|
# Simple Tetris-like demo, (c) 2012 Juan Linietsky
|
|
# Implemented by using a regular Control and drawing on it during the _draw() callback.
|
|
# The drawing surface is updated only when changes happen (by calling update())
|
|
|
|
|
|
var score = 0
|
|
var score_label=null
|
|
|
|
const MAX_SHAPES = 7
|
|
|
|
var block = preload("block.png")
|
|
|
|
var block_colors=[
|
|
Color(1,0.5,0.5),
|
|
Color(0.5,1,0.5),
|
|
Color(0.5,0.5,1),
|
|
Color(0.8,0.4,0.8),
|
|
Color(0.8,0.8,0.4),
|
|
Color(0.4,0.8,0.8),
|
|
Color(0.7,0.7,0.7)]
|
|
|
|
var block_shapes=[
|
|
[ Vector2(0,-1),Vector2(0,0),Vector2(0,1),Vector2(0,2) ], # I
|
|
[ Vector2(0,0),Vector2(1,0),Vector2(1,1),Vector2(0,1) ], # O
|
|
[ Vector2(-1,1),Vector2(0,1),Vector2(0,0),Vector2(1,0) ], # S
|
|
[ Vector2(1,1),Vector2(0,1),Vector2(0,0),Vector2(-1,0) ], # Z
|
|
[ Vector2(-1,1),Vector2(-1,0),Vector2(0,0),Vector2(1,0) ], # L
|
|
[ Vector2(1,1),Vector2(1,0),Vector2(0,0),Vector2(-1,0) ], # J
|
|
[ Vector2(0,1),Vector2(1,0),Vector2(0,0),Vector2(-1,0) ]] # T
|
|
|
|
|
|
var block_rotations=[
|
|
Matrix32( Vector2(1,0),Vector2(0,1), Vector2() ),
|
|
Matrix32( Vector2(0,1),Vector2(-1,0), Vector2() ),
|
|
Matrix32( Vector2(-1,0),Vector2(0,-1), Vector2() ),
|
|
Matrix32( Vector2(0,-1),Vector2(1,0), Vector2() )
|
|
]
|
|
|
|
|
|
var width=0
|
|
var height=0
|
|
|
|
var cells={}
|
|
|
|
var piece_active=false
|
|
var piece_shape=0
|
|
var piece_pos=Vector2()
|
|
var piece_rot=0
|
|
|
|
|
|
func piece_cell_xform(p,er=0):
|
|
var r = (4+er+piece_rot)%4
|
|
return piece_pos+block_rotations[r].xform(p)
|
|
|
|
func _draw():
|
|
|
|
var sb = get_stylebox("bg","Tree") # use line edit bg
|
|
draw_style_box(sb,Rect2(Vector2(),get_size()).grow(3))
|
|
|
|
var bs = block.get_size()
|
|
for y in range(height):
|
|
for x in range(width):
|
|
if (Vector2(x,y) in cells):
|
|
draw_texture_rect(block,Rect2(Vector2(x,y)*bs,bs),false,block_colors[cells[Vector2(x,y)]])
|
|
|
|
if (piece_active):
|
|
|
|
for c in block_shapes[piece_shape]:
|
|
draw_texture_rect(block,Rect2(piece_cell_xform(c)*bs,bs),false,block_colors[piece_shape])
|
|
|
|
|
|
func piece_check_fit(ofs,er=0):
|
|
|
|
for c in block_shapes[piece_shape]:
|
|
var pos = piece_cell_xform(c,er)+ofs
|
|
if (pos.x < 0):
|
|
return false
|
|
if (pos.y < 0):
|
|
return false
|
|
if (pos.x >= width):
|
|
return false
|
|
if (pos.y >= height):
|
|
return false
|
|
if (pos in cells):
|
|
return false
|
|
|
|
return true
|
|
|
|
func new_piece():
|
|
|
|
piece_shape = randi() % MAX_SHAPES
|
|
piece_pos = Vector2(width/2,0)
|
|
piece_active=true
|
|
piece_rot=0
|
|
if (piece_shape==0):
|
|
piece_pos.y+=1
|
|
|
|
if (not piece_check_fit(Vector2())):
|
|
#game over
|
|
#print("GAME OVER!")
|
|
game_over()
|
|
|
|
update()
|
|
|
|
|
|
func test_collapse_rows():
|
|
var accum_down=0
|
|
for i in range(height):
|
|
var y = height - i - 1
|
|
var collapse = true
|
|
for x in range(width):
|
|
if (Vector2(x,y) in cells):
|
|
if (accum_down):
|
|
cells[ Vector2(x,y+accum_down) ] = cells[Vector2(x,y)]
|
|
else:
|
|
collapse=false
|
|
if (accum_down):
|
|
cells.erase( Vector2(x,y+accum_down) )
|
|
|
|
if (collapse):
|
|
accum_down+=1
|
|
|
|
|
|
score+=accum_down*100
|
|
score_label.set_text(str(score))
|
|
|
|
|
|
func game_over():
|
|
|
|
piece_active=false
|
|
get_node("gameover").set_text("Game Over")
|
|
update()
|
|
|
|
|
|
func restart_pressed():
|
|
|
|
score=0
|
|
score_label.set_text("0")
|
|
cells.clear()
|
|
get_node("gameover").set_text("")
|
|
piece_active=true
|
|
get_node("../restart").release_focus()
|
|
update()
|
|
|
|
|
|
|
|
func piece_move_down():
|
|
|
|
if (!piece_active):
|
|
return
|
|
if (piece_check_fit(Vector2(0,1))):
|
|
piece_pos.y+=1
|
|
update()
|
|
else:
|
|
|
|
for c in block_shapes[piece_shape]:
|
|
var pos = piece_cell_xform(c)
|
|
cells[pos]=piece_shape
|
|
test_collapse_rows()
|
|
new_piece()
|
|
|
|
|
|
func piece_rotate():
|
|
|
|
var adv = 1
|
|
if (not piece_check_fit(Vector2(),1)):
|
|
return
|
|
piece_rot = (piece_rot + adv) % 4
|
|
update()
|
|
|
|
|
|
|
|
func _input(ie):
|
|
|
|
|
|
if (not piece_active):
|
|
return
|
|
if (!ie.is_pressed()):
|
|
return
|
|
|
|
if (ie.is_action("move_left")):
|
|
if (piece_check_fit(Vector2(-1,0))):
|
|
piece_pos.x-=1
|
|
update()
|
|
elif (ie.is_action("move_right")):
|
|
if (piece_check_fit(Vector2(1,0))):
|
|
piece_pos.x+=1
|
|
update()
|
|
elif (ie.is_action("move_down")):
|
|
piece_move_down()
|
|
elif (ie.is_action("rotate")):
|
|
piece_rotate()
|
|
|
|
|
|
func setup(w,h):
|
|
width=w
|
|
height=h
|
|
set_size( Vector2(w,h)*block.get_size() )
|
|
new_piece()
|
|
get_node("timer").start()
|
|
|
|
|
|
func _ready():
|
|
# Initalization here
|
|
|
|
setup(10,20)
|
|
score_label = get_node("../score")
|
|
|
|
set_process_input(true)
|
|
|
|
|
|
|
|
|