extends Node2D 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 const RASTER_SIZE = 64 const BOTTOM = RASTER_SIZE * 10 const SPAWN = -RASTER_SIZE * 7 const LEFT = -RASTER_SIZE * 5 const RIGHT = RASTER_SIZE * 5 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") active_shape = get_random_shape() func get_random_shape(): var new_shape = shapes[randi() % shapes.size()].instance() add_child(new_shape) new_shape.position.y = SPAWN return new_shape func update_board(): if can_active_move_down(): move_active_down() else: turn_active_into_static() check_for_full_line() active_shape = get_random_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 = {} for line_count_y in line_counts.keys(): if line_counts[line_count_y] == 10: # 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] 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()