[gd_scene load_steps=2 format=3 uid="uid://d3b12iqla7uh6"] [sub_resource type="GDScript" id="GDScript_rmgh7"] script/source = "extends Control signal request_play_level @onready var carousel := $VBoxContainer/Selection/Carousel @onready var label_time := $VBoxContainer/Presentation/MarginContainer/VBoxContainer/BestTime const levels = [ \"res://levels/base/level.tscn\", \"res://levels/forest/level.tscn\", \"res://levels/night/level.tscn\", ] var loaded_level: Level var loaded_level_scene: PackedScene var loaded_level_path: String func _enter_tree() -> void: if len(loaded_level_path): prepare_level(loaded_level_path) func _ready() -> void: var placeholders := carousel.get_children() for placeholder in placeholders: placeholder.queue_free() for i in len(levels): var btn := Button.new() btn.text = \"Level \" + str(i + 1) btn.connect(\"pressed\", func(): prepare_level(levels[i])) carousel.add_child(btn) func prepare_level(level_scene_path: String): if is_instance_valid(loaded_level): loaded_level.queue_free() ResourceLoader.load_threaded_request(level_scene_path) loaded_level_scene = ResourceLoader.load_threaded_get(level_scene_path) loaded_level = loaded_level_scene.instantiate() loaded_level_path = level_scene_path $VBoxContainer/Presentation/Thumbnail.texture = loaded_level.thumbnail $VBoxContainer/Presentation/MarginContainer/VBoxContainer/LevelName.text = \"The \" + loaded_level.id.capitalize() $VBoxContainer/Presentation.show() $VBoxContainer/MarginContainer/PlayButton.show() display_file_data(SaveFiles.read(SaveFiles.selected_file)) func display_file_data(data: Variant) -> void: label_time.text = \"Best time: \" var property_name := loaded_level.id + \"_best_time\" if data.has(property_name) and data[property_name] is float: var seconds: float = data[property_name] var minutes: int = floor(seconds / 60) label_time.text += (\"%0*d\" % [2, minutes]) + \":\" + (\"%0*.3f\" % [6, seconds - (minutes * 60)]) else: label_time.text += \"00:00.000\" func _on_play_button_pressed() -> void: if is_instance_valid(loaded_level): request_play_level.emit(loaded_level, loaded_level_scene) func seconds_to_readable(seconds: float) -> String: var minutes: int = floor(seconds / 60) return (\"%0*d\" % [2, minutes]) + \":\" + (\"%0*.3f\" % [6, seconds - (minutes * 60)]) ## This prevents a memory leak that is possible when this menu is destroyed ## and the loaded_level becomes unavailable to the rest of the game ## For example in the main menu, go to this menu, select a level, ## press \"return\" and go to this menu again, boom, memory leak prevented by this func _notification(what: int) -> void: match what: NOTIFICATION_PREDELETE: if is_instance_valid(loaded_level) && !loaded_level.is_inside_tree(): loaded_level.queue_free() " [node name="LevelSelectionMenu" type="Control"] layout_mode = 3 anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 script = SubResource("GDScript_rmgh7") [node name="VBoxContainer" type="VBoxContainer" parent="."] layout_mode = 1 anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 [node name="Selection" type="ScrollContainer" parent="VBoxContainer"] custom_minimum_size = Vector2(0, 50) layout_mode = 2 [node name="Carousel" type="HBoxContainer" parent="VBoxContainer/Selection"] layout_mode = 2 size_flags_horizontal = 3 theme_override_constants/separation = 10 alignment = 1 [node name="Placeholder1" type="Button" parent="VBoxContainer/Selection/Carousel"] layout_mode = 2 theme_override_constants/icon_max_width = 150 text = "Placeholder" [node name="Placeholder2" type="Button" parent="VBoxContainer/Selection/Carousel"] layout_mode = 2 text = "Placeholder" [node name="Placeholder3" type="Button" parent="VBoxContainer/Selection/Carousel"] layout_mode = 2 text = "Placeholder" [node name="Presentation" type="HBoxContainer" parent="VBoxContainer"] visible = false layout_mode = 2 [node name="Thumbnail" type="TextureRect" parent="VBoxContainer/Presentation"] custom_minimum_size = Vector2(150, 150) layout_mode = 2 expand_mode = 1 stretch_mode = 4 [node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/Presentation"] layout_mode = 2 theme_override_constants/margin_left = 5 [node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/Presentation/MarginContainer"] layout_mode = 2 alignment = 1 [node name="LevelName" type="Label" parent="VBoxContainer/Presentation/MarginContainer/VBoxContainer"] layout_mode = 2 text = "Placeholder" [node name="BestTime" type="Label" parent="VBoxContainer/Presentation/MarginContainer/VBoxContainer"] layout_mode = 2 text = "Best time: 00:00.000" [node name="MarginContainer" type="MarginContainer" parent="VBoxContainer"] layout_mode = 2 theme_override_constants/margin_top = 15 [node name="PlayButton" type="Button" parent="VBoxContainer/MarginContainer"] visible = false layout_mode = 2 text = "Play!" [connection signal="pressed" from="VBoxContainer/MarginContainer/PlayButton" to="." method="_on_play_button_pressed"]