202 lines
4.8 KiB
GDScript
202 lines
4.8 KiB
GDScript
extends Node2D
|
|
|
|
|
|
signal score_changed(new_score)
|
|
signal new_next_shape(next_shape_scene)
|
|
|
|
var shapes = [
|
|
preload("res://ShapeI.tscn"),
|
|
preload("res://ShapeJ.tscn"),
|
|
preload("res://ShapeL.tscn"),
|
|
preload("res://ShapeZ.tscn"),
|
|
preload("res://ShapeS.tscn"),
|
|
preload("res://ShapeT.tscn"),
|
|
preload("res://ShapeO.tscn")
|
|
]
|
|
|
|
var active_shape: TetrisShape
|
|
var next_shape
|
|
|
|
var score := 0
|
|
|
|
const RASTER_SIZE = 64
|
|
const BOTTOM = RASTER_SIZE * 10
|
|
const SPAWN = -RASTER_SIZE * 7
|
|
const LEFT = -RASTER_SIZE * 5
|
|
const RIGHT = RASTER_SIZE * 5
|
|
|
|
const LINE_CLEAR_SCORES = [
|
|
0,
|
|
10,
|
|
25,
|
|
60,
|
|
100
|
|
]
|
|
|
|
|
|
func _ready():
|
|
# Swipe input
|
|
$SwipeHandler.connect("swipe_up", self, "rotate_shape")
|
|
$SwipeHandler.connect("swipe_left", self, "move_left")
|
|
$SwipeHandler.connect("swipe_right", self, "move_right")
|
|
$SwipeHandler.connect("swipe_down", self, "drop")
|
|
|
|
$GravityTimer.connect("timeout", self, "update_board")
|
|
|
|
update_active_shape()
|
|
|
|
|
|
func update_active_shape():
|
|
if not next_shape:
|
|
active_shape = get_random_shape().instance()
|
|
else:
|
|
active_shape = next_shape.instance()
|
|
|
|
add_child(active_shape)
|
|
active_shape.position.y = SPAWN
|
|
|
|
next_shape = get_random_shape()
|
|
emit_signal("new_next_shape", next_shape)
|
|
|
|
|
|
func get_random_shape():
|
|
return shapes[randi() % shapes.size()]
|
|
|
|
|
|
func update_board():
|
|
if can_active_move_down():
|
|
move_active_down()
|
|
else:
|
|
turn_active_into_static()
|
|
check_for_full_line()
|
|
|
|
update_active_shape()
|
|
|
|
|
|
func move_active_down():
|
|
active_shape.position.y += RASTER_SIZE
|
|
|
|
|
|
func can_active_move_down():
|
|
return _can_active_move_towards(Vector2(0.0, RASTER_SIZE))
|
|
|
|
|
|
func can_active_move_left():
|
|
return _can_active_move_towards(Vector2(-RASTER_SIZE, 0.0))
|
|
|
|
|
|
func can_active_move_right():
|
|
return _can_active_move_towards(Vector2(RASTER_SIZE, 0.0))
|
|
|
|
|
|
func _can_active_move_towards(diff: Vector2):
|
|
for block in active_shape.get_blocks():
|
|
var new_x = block.global_position.x + diff.x
|
|
var new_y = block.global_position.y + diff.y
|
|
|
|
var global_bottom = to_global(Vector2(0.0, BOTTOM)).y
|
|
var global_left = to_global(Vector2(LEFT, 0.0)).x
|
|
var global_right = to_global(Vector2(RIGHT, 0.0)).x
|
|
|
|
if new_x <= global_left \
|
|
or new_x >= global_right \
|
|
or new_y >= global_bottom:
|
|
return false
|
|
|
|
if is_block_at_position(new_x, new_y):
|
|
return false
|
|
|
|
return true
|
|
|
|
|
|
func turn_active_into_static():
|
|
for block in active_shape.get_blocks():
|
|
var global_shape_position = block.global_position
|
|
active_shape.remove_child(block)
|
|
$StaticBlocks.add_child(block)
|
|
block.global_position = global_shape_position
|
|
|
|
active_shape.free()
|
|
|
|
|
|
func check_for_full_line():
|
|
var line_counts = {} # Maps a y position to a count
|
|
|
|
# Count how many blocks we have in each line (with at least 1 block)
|
|
for block in $StaticBlocks.get_children():
|
|
if not line_counts.has(block.position.y):
|
|
line_counts[block.position.y] = 0
|
|
line_counts[block.position.y] += 1
|
|
|
|
# Remember which blocks we need to move down later (we can't apply this immediately because that
|
|
# would mess with comparisons to the line_counts
|
|
var pending_position_diffs := {}
|
|
|
|
# Check how many full lines we have this frame because the score increases non-linearly
|
|
var number_of_full_lines := 0
|
|
|
|
for line_count_y in line_counts.keys():
|
|
if line_counts[line_count_y] == 10:
|
|
number_of_full_lines += 1
|
|
|
|
# Free this line
|
|
for line_block in $StaticBlocks.get_children():
|
|
if line_block.position.y == line_count_y:
|
|
line_block.free()
|
|
elif line_block.position.y < line_count_y:
|
|
# If this block is above the line currently being freed, remember to move down
|
|
# by one raster cell
|
|
if not pending_position_diffs.has(line_block):
|
|
pending_position_diffs[line_block] = 0
|
|
pending_position_diffs[line_block] += RASTER_SIZE
|
|
|
|
# Apply the pending downward movements, but only if the block hasn't been freed (because that
|
|
# line was above, but also full)
|
|
for block in pending_position_diffs.keys():
|
|
if is_instance_valid(block):
|
|
block.position.y += pending_position_diffs[block]
|
|
|
|
score += LINE_CLEAR_SCORES[number_of_full_lines]
|
|
emit_signal("score_changed", score)
|
|
|
|
|
|
func move_left():
|
|
if can_active_move_left():
|
|
active_shape.position.x -= RASTER_SIZE
|
|
|
|
|
|
func move_right():
|
|
if can_active_move_right():
|
|
active_shape.position.x += RASTER_SIZE
|
|
|
|
|
|
func rotate_shape():
|
|
active_shape.rotation_degrees += 90
|
|
|
|
|
|
func drop():
|
|
while can_active_move_down():
|
|
move_active_down()
|
|
|
|
# Restart the timer to give full time for sliding the piece
|
|
$GravityTimer.start()
|
|
|
|
|
|
func is_block_at_position(pos_x, pos_y):
|
|
for block in $StaticBlocks.get_children():
|
|
if block.global_position.x == pos_x and block.global_position.y == pos_y:
|
|
return true
|
|
|
|
return false
|
|
|
|
|
|
func _input(event):
|
|
if event.is_action_pressed("move_left"):
|
|
move_left()
|
|
elif event.is_action_pressed("move_right"):
|
|
move_right()
|
|
elif event.is_action_pressed("rotate"):
|
|
rotate_shape()
|
|
elif event.is_action_pressed("drop"):
|
|
drop()
|