2025-04-24 14:54:54 +02:00
|
|
|
[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
|
|
|
|
|
2025-04-25 16:03:36 +02:00
|
|
|
@onready var carousel := $VBoxContainer/Selection/Carousel
|
|
|
|
@onready var label_time := $VBoxContainer/Presentation/MarginContainer/VBoxContainer/BestTime
|
2025-04-24 14:54:54 +02:00
|
|
|
const levels = [
|
2025-04-25 16:03:36 +02:00
|
|
|
\"res://levels/base/level.tscn\",
|
|
|
|
\"res://levels/forest/level.tscn\",
|
|
|
|
\"res://levels/night/level.tscn\",
|
2025-04-24 14:54:54 +02:00
|
|
|
]
|
2025-04-25 16:03:36 +02:00
|
|
|
var loaded_level: Level
|
2025-04-27 21:30:03 +02:00
|
|
|
var loaded_level_scene: PackedScene
|
|
|
|
var loaded_level_path: String
|
|
|
|
|
|
|
|
func _enter_tree() -> void:
|
|
|
|
if len(loaded_level_path):
|
|
|
|
prepare_level(loaded_level_path)
|
2025-04-24 14:54:54 +02:00
|
|
|
|
|
|
|
func _ready() -> void:
|
2025-04-25 16:03:36 +02:00
|
|
|
var placeholders := carousel.get_children()
|
|
|
|
for placeholder in placeholders:
|
|
|
|
placeholder.queue_free()
|
|
|
|
|
2025-04-24 14:54:54 +02:00
|
|
|
for i in len(levels):
|
|
|
|
var btn := Button.new()
|
|
|
|
btn.text = \"Level \" + str(i + 1)
|
2025-04-25 16:03:36 +02:00
|
|
|
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)
|
2025-04-27 21:30:03 +02:00
|
|
|
loaded_level_scene = ResourceLoader.load_threaded_get(level_scene_path)
|
|
|
|
loaded_level = loaded_level_scene.instantiate()
|
|
|
|
loaded_level_path = level_scene_path
|
2025-04-25 16:03:36 +02:00
|
|
|
$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):
|
2025-04-27 21:30:03 +02:00
|
|
|
request_play_level.emit(loaded_level, loaded_level_scene)
|
2025-04-25 16:03:36 +02:00
|
|
|
|
|
|
|
func seconds_to_readable(seconds: float) -> String:
|
|
|
|
var minutes: int = floor(seconds / 60)
|
|
|
|
return (\"%0*d\" % [2, minutes]) + \":\" + (\"%0*.3f\" % [6, seconds - (minutes * 60)])
|
2025-05-03 14:33:49 +02:00
|
|
|
|
|
|
|
## 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()
|
2025-04-24 14:54:54 +02:00
|
|
|
"
|
|
|
|
|
|
|
|
[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
|
|
|
|
|
2025-04-25 16:03:36 +02:00
|
|
|
[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"]
|
2025-04-24 14:54:54 +02:00
|
|
|
layout_mode = 2
|
|
|
|
alignment = 1
|
2025-04-25 16:03:36 +02:00
|
|
|
|
|
|
|
[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"]
|