extends WorldEnvironment const Tetromino = preload("res://Tetrominos/Tetromino.gd") const TetroI = preload("res://Tetrominos/TetroI.tscn") const TetroJ = preload("res://Tetrominos/TetroJ.tscn") const TetroL = preload("res://Tetrominos/TetroL.tscn") const TetroO = preload("res://Tetrominos/TetroO.tscn") const TetroS = preload("res://Tetrominos/TetroS.tscn") const TetroT = preload("res://Tetrominos/TetroT.tscn") const TetroZ = preload("res://Tetrominos/TetroZ.tscn") const password = "TETRIS 3000" const NEXT_POSITION = Vector3(13, 16, 0) const START_POSITION = Vector3(5, 20, 0) const HOLD_POSITION = Vector3(-5, 16, 0) const movements = { "move_right": Vector3(1, 0, 0), "move_left": Vector3(-1, 0, 0), "soft_drop": Vector3(0, -1, 0) } var random_bag = [] var next_piece var current_piece var held_piece var current_piece_held = false var autoshift_action = "" var playing = false signal piece_dropped(score) signal piece_locked(lines, t_spin) func _ready(): load_user_data() func load_user_data(): var save_game = File.new() if not save_game.file_exists("user://data.save"): $Stats.high_score = 0 else: save_game.open_encrypted_with_pass("user://data.save", File.READ, password) $Stats.high_score = int(save_game.get_line()) $Stats/VBC/HighScore.text = str($Stats.high_score) save_game.close() func _on_Start_start(level): $GridMap.clear() if held_piece: remove_child(held_piece) held_piece = null next_piece = random_piece() new_piece() $MidiPlayer.position = 0 $Start.visible = false $Stats.new_game(level) resume() func new_piece(): if current_piece: remove_child(current_piece) current_piece = next_piece current_piece.translation = START_POSITION current_piece.emit_trail(true) autoshift_action = "" next_piece = random_piece() next_piece.translation = NEXT_POSITION if move(movements["soft_drop"]): $DropTimer.start() $LockDelay.start() current_piece_held = false else: game_over() func random_piece(): if not random_bag: random_bag = [ TetroI, TetroJ, TetroL, TetroO, TetroS, TetroT, TetroZ ] var choice = randi() % random_bag.size() var piece = random_bag[choice].instance() random_bag.remove(choice) add_child(piece) return piece func _on_Stats_level_up(): $DropTimer.wait_time = pow(0.8 - (($Stats.level - 1) * 0.007), $Stats.level - 1) if $Stats.level > 15: $LockDelay.wait_time = 0.5 * pow(0.9, $Stats.level-15) func _process(delta): if Input.is_action_just_pressed("pause"): if playing: pause() $controls_ui.visible = true elif $controls_ui.enable_resume: resume() if playing: for action in movements: if action == autoshift_action: if not Input.is_action_pressed(action): $AutoShiftDelay.stop() $AutoShiftTimer.stop() autoshift_action = "" else: if Input.is_action_pressed(action): autoshift_action = action process_autoshift_action() $AutoShiftTimer.stop() $AutoShiftDelay.start() if Input.is_action_just_pressed("hard_drop"): hard_drop() if Input.is_action_just_pressed("rotate_clockwise"): if rotate(Tetromino.CLOCKWISE): $MidiPlayer.move() if Input.is_action_just_pressed("rotate_counterclockwise"): if rotate(Tetromino.COUNTERCLOCKWISE): $MidiPlayer.move() if Input.is_action_just_pressed("hold"): hold() func _on_AutoShiftDelay_timeout(): if playing and autoshift_action: process_autoshift_action() $AutoShiftTimer.start() func _on_AutoShiftTimer_timeout(): if playing and autoshift_action: process_autoshift_action() func process_autoshift_action(): if move(movements[autoshift_action]): $MidiPlayer.move() if autoshift_action == "soft_drop": emit_signal("piece_dropped", 1) func hard_drop(): $MidiPlayer.move() var score = 0 while move(movements["soft_drop"]): score += 2 emit_signal("piece_dropped", score) lock() func move(movement): if current_piece.move(movement): $LockDelay.start() return true else: return false func rotate(direction): if current_piece.rotate(direction): $LockDelay.start() return true else: return false func _on_DropTimer_timeout(): move(movements["soft_drop"]) func _on_LockDelay_timeout(): if not move(movements["soft_drop"]): lock() func lock(): $GridMap.lock(current_piece) emit_signal("piece_locked", $GridMap.clear_lines(), current_piece.t_spin) new_piece() func hold(): if not current_piece_held: current_piece_held = true var swap = current_piece current_piece = held_piece held_piece = swap held_piece.emit_trail(false) held_piece.translation = HOLD_POSITION if current_piece: current_piece.translation = START_POSITION current_piece.emit_trail(true) else: new_piece() func resume(): playing = true $DropTimer.start() $LockDelay.start() $Stats.time = OS.get_system_time_secs() - $Stats.time $Stats/Clock.start() $MidiPlayer.resume() $controls_ui.visible = false $Stats.visible = true $GridMap.visible = true $Backs.visible = true current_piece.visible = true if held_piece: held_piece.visible = true next_piece.visible = true func pause(hide=true): playing = false $Stats.time = OS.get_system_time_secs() - $Stats.time if hide: $Stats.visible = false $GridMap.visible = false $Backs.visible = false current_piece.visible = false if held_piece: held_piece.visible = false next_piece.visible = false $MidiPlayer.stop() $DropTimer.stop() $LockDelay.stop() $Stats/Clock.stop() func game_over(): $FlashText.print("GAME\nOVER") pause(false) $ReplayButton.visible = true func _on_ReplayButton_pressed(): pause() $ReplayButton.visible = false $Start.visible = true func _notification(what): match what: MainLoop.NOTIFICATION_WM_FOCUS_OUT: if playing: pause() MainLoop.NOTIFICATION_WM_QUIT_REQUEST: save_user_data() func save_user_data(): var save_game = File.new() save_game.open_encrypted_with_pass("user://data.save", File.WRITE, password) save_game.store_line(str($Stats.high_score)) save_game.close()