godot/demos/2d/tetris/grid.gd

195 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())
# Member variables
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
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():
setup(10, 20)
score_label = get_node("../score")
set_process_input(true)