tetris-mobile/GameBoard.gd

223 lines
5.4 KiB
GDScript3
Raw Normal View History

2021-11-17 16:30:13 +01:00
extends Node2D
2021-11-21 13:47:38 +01:00
signal score_changed(new_score)
signal new_next_shape(next_shape_scene)
2021-11-21 15:06:47 +01:00
signal game_over
2021-11-21 13:47:38 +01:00
2021-11-17 23:31:27 +01:00
var shapes = [
preload("res://Shapes/ShapeI.tscn"),
preload("res://Shapes/ShapeJ.tscn"),
preload("res://Shapes/ShapeL.tscn"),
preload("res://Shapes/ShapeZ.tscn"),
preload("res://Shapes/ShapeS.tscn"),
preload("res://Shapes/ShapeT.tscn"),
preload("res://Shapes/ShapeO.tscn")
2021-11-17 23:31:27 +01:00
]
var active_shape: TetrisShape
2021-11-21 13:47:38 +01:00
var next_shape
var score := 0
2021-11-17 16:30:13 +01:00
const RASTER_SIZE = 64
2021-11-17 17:02:34 +01:00
const BOTTOM = RASTER_SIZE * 10
2021-11-21 12:52:19 +01:00
const SPAWN = -RASTER_SIZE * 7
const LEFT = -RASTER_SIZE * 5
const RIGHT = RASTER_SIZE * 5
2021-11-17 16:30:13 +01:00
2021-11-21 13:47:38 +01:00
const LINE_CLEAR_SCORES = [
0,
2021-11-21 15:10:17 +01:00
10, # 1 line -> basic 10 points
30, # 2 lines -> 10 points bonus
50, # 3 lines -> 20 points bonus
90 # 4 lines -> 50 points bonus
2021-11-21 13:47:38 +01:00
]
2021-11-17 16:30:13 +01:00
func _ready():
2021-11-21 15:06:47 +01:00
randomize()
2021-11-21 13:24:03 +01:00
# 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")
2021-11-17 16:30:13 +01:00
$GravityTimer.connect("timeout", self, "update_board")
2021-11-17 23:31:27 +01:00
2021-11-21 13:47:38 +01:00
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)
2021-11-17 16:30:13 +01:00
2021-11-17 17:02:34 +01:00
func get_random_shape():
2021-11-21 13:47:38 +01:00
return shapes[randi() % shapes.size()]
2021-11-17 17:02:34 +01:00
2021-11-17 16:30:13 +01:00
func update_board():
2021-11-21 15:13:37 +01:00
# The player lost if the active shape is within another shape, since that means that they're
# piling up on top
if is_shape_stuck():
2021-11-21 15:06:47 +01:00
game_over()
2021-11-17 18:24:58 +01:00
if can_active_move_down():
move_active_down()
else:
2021-11-17 17:02:34 +01:00
turn_active_into_static()
check_for_full_line()
2021-11-21 13:47:38 +01:00
update_active_shape()
2021-11-17 18:24:58 +01:00
2021-11-21 15:06:47 +01:00
func game_over():
$GravityTimer.stop()
emit_signal("game_over")
2021-11-17 18:24:58 +01:00
func move_active_down():
2021-11-17 16:30:13 +01:00
active_shape.position.y += RASTER_SIZE
2021-11-17 18:24:58 +01:00
func can_active_move_down():
2021-11-21 12:52:19 +01:00
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))
2021-11-21 15:13:37 +01:00
func is_shape_stuck():
2021-11-21 15:06:47 +01:00
return not _can_active_move_towards(Vector2.ZERO)
2021-11-21 12:52:19 +01:00
func _can_active_move_towards(diff: Vector2):
2021-11-17 18:24:58 +01:00
for block in active_shape.get_blocks():
2021-11-21 12:52:19 +01:00
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
2021-11-17 18:24:58 +01:00
2021-11-21 12:52:19 +01:00
if is_block_at_position(new_x, new_y):
2021-11-17 18:24:58 +01:00
return false
return true
2021-11-17 17:02:34 +01:00
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
2021-11-21 12:52:19 +01:00
# Count how many blocks we have in each line (with at least 1 block)
2021-11-17 17:02:34 +01:00
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
2021-11-21 12:52:19 +01:00
# 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
2021-11-21 13:47:38 +01:00
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
2021-11-21 12:52:19 +01:00
2021-11-17 17:02:34 +01:00
for line_count_y in line_counts.keys():
if line_counts[line_count_y] == 10:
2021-11-21 13:47:38 +01:00
number_of_full_lines += 1
2021-11-17 17:02:34 +01:00
# Free this line
for line_block in $StaticBlocks.get_children():
if line_block.position.y == line_count_y:
line_block.free()
2021-11-17 17:52:33 +01:00
elif line_block.position.y < line_count_y:
2021-11-21 12:52:19 +01:00
# 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]
2021-11-21 13:47:38 +01:00
score += LINE_CLEAR_SCORES[number_of_full_lines]
emit_signal("score_changed", score)
2021-11-17 17:02:34 +01:00
2021-11-17 16:30:13 +01:00
func move_left():
2021-11-21 12:52:19 +01:00
if can_active_move_left():
active_shape.position.x -= RASTER_SIZE
2021-11-17 16:30:13 +01:00
func move_right():
2021-11-21 12:52:19 +01:00
if can_active_move_right():
active_shape.position.x += RASTER_SIZE
2021-11-17 16:30:13 +01:00
2021-11-17 17:52:33 +01:00
func rotate_shape():
active_shape.rotation_degrees += 90
2021-11-21 15:13:37 +01:00
# Revert the rotation if that would cause it to be stuck inside another
if is_shape_stuck():
active_shape.rotation_degrees -= 90
2021-11-17 17:52:33 +01:00
2021-11-17 17:02:34 +01:00
func drop():
2021-11-17 18:24:58 +01:00
while can_active_move_down():
move_active_down()
2021-11-17 17:02:34 +01:00
# Restart the timer to give full time for sliding the piece
$GravityTimer.start()
2021-11-17 18:24:58 +01:00
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
2021-11-17 16:30:13 +01:00
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"):
2021-11-17 17:52:33 +01:00
rotate_shape()
2021-11-17 16:30:13 +01:00
elif event.is_action_pressed("drop"):
2021-11-17 17:02:34 +01:00
drop()