From b877f94e3421fecb0aeef7aa4e381267dd324504 Mon Sep 17 00:00:00 2001 From: Yogi Wiguna Date: Tue, 17 Mar 2026 11:11:00 +0800 Subject: [PATCH] feat: Add a loading screen, a lobby scene, and a new main game script, along with a custom font. --- assets/fonts/TektonDash2.ttf | Bin 0 -> 32708 bytes assets/fonts/TektonDash2.ttf.import | 36 ++++++ scenes/loading_screen/loading_screen.tscn | 149 ++++++++++++++++++++++ scenes/lobby.gd | 12 +- scenes/lobby.tscn | 4 +- scenes/main.gd | 52 ++++++-- scripts/ui/loading_screen.gd | 108 ++++++++++++++++ scripts/ui/loading_screen.gd.uid | 1 + 8 files changed, 345 insertions(+), 17 deletions(-) create mode 100644 assets/fonts/TektonDash2.ttf create mode 100644 assets/fonts/TektonDash2.ttf.import create mode 100644 scenes/loading_screen/loading_screen.tscn create mode 100644 scripts/ui/loading_screen.gd create mode 100644 scripts/ui/loading_screen.gd.uid diff --git a/assets/fonts/TektonDash2.ttf b/assets/fonts/TektonDash2.ttf new file mode 100644 index 0000000000000000000000000000000000000000..1c2e2b9d72973fbf18a5be3fe041589021038354 GIT binary patch literal 32708 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 literal 0 HcmV?d00001 diff --git a/assets/fonts/TektonDash2.ttf.import b/assets/fonts/TektonDash2.ttf.import new file mode 100644 index 0000000..8b2c3ed --- /dev/null +++ b/assets/fonts/TektonDash2.ttf.import @@ -0,0 +1,36 @@ +[remap] + +importer="font_data_dynamic" +type="FontFile" +uid="uid://dsqg0x5lnosou" +path="res://.godot/imported/TektonDash2.ttf-1516aff8b98a916de0041ae3337aaaad.fontdata" + +[deps] + +source_file="res://assets/fonts/TektonDash2.ttf" +dest_files=["res://.godot/imported/TektonDash2.ttf-1516aff8b98a916de0041ae3337aaaad.fontdata"] + +[params] + +Rendering=null +antialiasing=1 +generate_mipmaps=false +disable_embedded_bitmaps=true +multichannel_signed_distance_field=false +msdf_pixel_range=8 +msdf_size=48 +allow_system_fallback=true +force_autohinter=false +modulate_color_glyphs=false +hinting=1 +subpixel_positioning=4 +keep_rounding_remainders=true +oversampling=0.0 +Fallbacks=null +fallbacks=[] +Compress=null +compress=true +preload=[] +language_support={} +script_support={} +opentype_features={} diff --git a/scenes/loading_screen/loading_screen.tscn b/scenes/loading_screen/loading_screen.tscn new file mode 100644 index 0000000..66e6b81 --- /dev/null +++ b/scenes/loading_screen/loading_screen.tscn @@ -0,0 +1,149 @@ +[gd_scene load_steps=10 format=3 uid="uid://dtjppx0u68jw5"] + +[ext_resource type="Script" path="res://scripts/ui/loading_screen.gd" id="1_u2jrd"] +[ext_resource type="Texture2D" uid="uid://beimain5tk480" path="res://assets/graphics/loading_screen/bg_loading.png" id="2_gardl"] +[ext_resource type="Shader" uid="uid://df6pi8e32lmco" path="res://assets/graphics/loading_screen/loading_screen.gdshader" id="3_vqw5v"] +[ext_resource type="Texture2D" uid="uid://boenrxbgftva1" path="res://assets/graphics/loading_screen/pattern.png" id="4_u2jrd"] +[ext_resource type="FontFile" path="res://assets/fonts/TektonDash2.ttf" id="5_ho08k"] + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_pc0ba"] +shader = ExtResource("3_vqw5v") + +[sub_resource type="FontVariation" id="FontVariation_8cml4"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_a8kcw"] +bg_color = Color(0.529067, 0.529067, 0.529067, 1) +border_width_left = 5 +border_width_top = 5 +border_width_right = 5 +border_width_bottom = 5 +border_color = Color(0, 0, 0, 1) +corner_radius_top_left = 10 +corner_radius_top_right = 10 +corner_radius_bottom_right = 10 +corner_radius_bottom_left = 10 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_8cml4"] +bg_color = Color(1, 1, 1, 1) +border_width_left = 5 +border_width_top = 5 +border_width_right = 5 +border_width_bottom = 5 +border_color = Color(0, 0, 0, 1) + +[node name="loading_screen" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_u2jrd") + +[node name="Bg" type="TextureRect" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +texture = ExtResource("2_gardl") + +[node name="BgSeamless" type="TextureRect" parent="."] +self_modulate = Color(1, 1, 1, 0.3529412) +texture_repeat = 2 +material = SubResource("ShaderMaterial_pc0ba") +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_top = -421.0 +offset_bottom = 421.0 +grow_horizontal = 2 +grow_vertical = 2 +texture = ExtResource("4_u2jrd") +stretch_mode = 1 + +[node name="Control" type="Control" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="scene_name" type="VBoxContainer" parent="Control"] +visible = false +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -100.0 +offset_top = -100.0 +offset_right = 100.0 +offset_bottom = -20.0 +grow_horizontal = 2 +grow_vertical = 2 +alignment = 1 + +[node name="label" type="Label" parent="Control/scene_name"] +layout_mode = 2 +text = "Loading..." +horizontal_alignment = 1 + +[node name="name_of_load_scene" type="Label" parent="Control/scene_name"] +layout_mode = 2 +text = "Map" +horizontal_alignment = 1 + +[node name="tips" type="VBoxContainer" parent="Control"] +layout_mode = 1 +anchors_preset = 7 +anchor_left = 0.5 +anchor_top = 1.0 +anchor_right = 0.5 +anchor_bottom = 1.0 +offset_left = -200.0 +offset_top = -150.0 +offset_right = 200.0 +offset_bottom = -100.0 +grow_horizontal = 2 +grow_vertical = 0 +alignment = 1 + +[node name="label" type="Label" parent="Control/tips"] +layout_mode = 2 +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/outline_size = 10 +theme_override_fonts/font = ExtResource("5_ho08k") +theme_override_font_sizes/font_size = 24 +text = "Tips" +horizontal_alignment = 1 + +[node name="tip_value" type="Label" parent="Control/tips"] +layout_mode = 2 +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/outline_size = 10 +theme_override_fonts/font = ExtResource("5_ho08k") +theme_override_font_sizes/font_size = 30 +text = "Tip Value" +horizontal_alignment = 1 + +[node name="progress_bar" type="ProgressBar" parent="."] +layout_mode = 1 +anchors_preset = 12 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = 50.0 +offset_top = -50.0 +offset_right = -50.0 +offset_bottom = -20.0 +grow_horizontal = 2 +grow_vertical = 0 +theme_override_colors/font_color = Color(0, 0, 0, 1) +theme_override_fonts/font = SubResource("FontVariation_8cml4") +theme_override_styles/background = SubResource("StyleBoxFlat_a8kcw") +theme_override_styles/fill = SubResource("StyleBoxFlat_8cml4") diff --git a/scenes/lobby.gd b/scenes/lobby.gd index aed0209..f6c75f2 100644 --- a/scenes/lobby.gd +++ b/scenes/lobby.gd @@ -736,8 +736,16 @@ func _on_all_players_ready() -> void: func _on_game_starting() -> void: connection_status.text = "Starting game..." - await get_tree().create_timer(0.5).timeout - get_tree().change_scene_to_file("res://scenes/main.tscn") + + # Instantiate and use the loading screen + var loading_screen_scene = load("res://scenes/loading_screen/loading_screen.tscn") + if loading_screen_scene: + var loading_screen = loading_screen_scene.instantiate() + get_tree().root.add_child(loading_screen) + loading_screen.load_level("res://scenes/main.tscn") + else: + # Fallback if loading screen fails to load + get_tree().change_scene_to_file("res://scenes/main.tscn") func _on_match_duration_changed(duration_seconds: int) -> void: if not LobbyManager.is_host: diff --git a/scenes/lobby.tscn b/scenes/lobby.tscn index a1badd8..a99b287 100644 --- a/scenes/lobby.tscn +++ b/scenes/lobby.tscn @@ -1136,9 +1136,9 @@ anchors_preset = 12 anchor_top = 1.0 anchor_right = 1.0 anchor_bottom = 1.0 -offset_left = 466.0 +offset_left = 462.0 offset_top = -65.0 -offset_right = -459.0 +offset_right = -463.0 offset_bottom = -16.0 grow_horizontal = 2 grow_vertical = 0 diff --git a/scenes/main.gd b/scenes/main.gd index db780cc..d041747 100644 --- a/scenes/main.gd +++ b/scenes/main.gd @@ -364,11 +364,19 @@ func _start_pre_game_countdown(): @rpc("call_local", "reliable") func sync_countdown(text: String): - var label = get_node_or_null("CountdownLabel") + # Use a CanvasLayer to ensure the countdown is on top of everything + var countdown_layer = get_node_or_null("CountdownLayerUI") + if not countdown_layer: + countdown_layer = CanvasLayer.new() + countdown_layer.name = "CountdownLayerUI" + countdown_layer.layer = 100 # Very high priority + add_child(countdown_layer) + + var label = countdown_layer.get_node_or_null("CountdownLabel") if not label and text != "": label = Label.new() label.name = "CountdownLabel" - add_child(label) + countdown_layer.add_child(label) # Center and Style label.anchors_preset = Control.PRESET_CENTER @@ -378,15 +386,20 @@ func sync_countdown(text: String): label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER - label.add_theme_font_size_override("font_size", 120) + label.add_theme_font_size_override("font_size", 140) label.add_theme_color_override("font_outline_color", Color.BLACK) - label.add_theme_constant_override("outline_size", 12) + label.add_theme_constant_override("outline_size", 20) label.add_theme_color_override("font_color", Color.YELLOW) + + # Use Nougat font if available + var nougat = load("res://assets/fonts/Nougat-ExtraBlack.ttf") + if nougat: + label.add_theme_font_override("font", nougat) if label: label.text = text if text == "": - label.queue_free() + countdown_layer.queue_free() elif text == "GO!": label.add_theme_color_override("font_color", Color.GREEN) @@ -687,8 +700,8 @@ func _start_game(): wait_time += 0.2 # Allow socket/peer to stabilize before blasting RPCs - # Snappier delay for LAN mode - var delay = 0.5 if LobbyManager.is_lan_mode else 2.0 + # Snappier delay since we already waited for scene load + var delay = 0.2 if LobbyManager.is_lan_mode else 0.5 await get_tree().create_timer(delay).timeout # NOW assign spawn positions for EVERYONE (Host, Client, Bots) @@ -2391,8 +2404,16 @@ func _on_quit_match_pressed(): get_tree().paused = false # Ensure unpaused when returning to menu # Properly disconnect from Nakama and clear lobby state to prevent desync LobbyManager.leave_room() - # Return to lobby or main menu - get_tree().change_scene_to_file("res://scenes/lobby.tscn") + + # Return to lobby via loading screen + var loading_screen_scene = load("res://scenes/loading_screen/loading_screen.tscn") + if loading_screen_scene: + var loading_screen = loading_screen_scene.instantiate() + get_tree().root.add_child(loading_screen) + loading_screen.load_level("res://scenes/lobby.tscn") + else: + # Fallback + get_tree().change_scene_to_file("res://scenes/lobby.tscn") func _on_settings_back_pressed(): var pause_menu = get_node_or_null("PauseMenu") @@ -2418,11 +2439,16 @@ func _on_joystick_toggled(enabled: bool): touch_controls._save_settings() func can_rpc() -> bool: - if not multiplayer.has_multiplayer_peer() or multiplayer.multiplayer_peer.get_connection_status() != MultiplayerPeer.CONNECTION_CONNECTED: - return false + if not multiplayer.has_multiplayer_peer(): return false + if multiplayer.multiplayer_peer.get_connection_status() != MultiplayerPeer.CONNECTION_CONNECTED: return false + + if LobbyManager.is_lan_mode: + return true + var nakama = get_node_or_null("/root/NakamaManager") - if nakama and nakama.has_method("is_connected_to_nakama") and not nakama.is_connected_to_nakama(): - return false + if nakama and nakama.has_method("is_connected_to_nakama"): + return nakama.is_connected_to_nakama() + return true @rpc("authority", "call_local", "reliable") diff --git a/scripts/ui/loading_screen.gd b/scripts/ui/loading_screen.gd new file mode 100644 index 0000000..439cb7a --- /dev/null +++ b/scripts/ui/loading_screen.gd @@ -0,0 +1,108 @@ +extends Control + +@export var tips: Array[String] = [ + "Use your cards wisely!", + "Plan your moves ahead.", + "Watch out for obstacles!", + "Combine cards for powerful effects." +] + +var is_loading: bool = false +var path: String = "" +var progress: Array[float] = [] + +@onready var progress_bar = $progress_bar +@onready var tip_value = $Control/tips/tip_value +@onready var scene_name_label = $Control/scene_name/name_of_load_scene +@onready var content_control = $Control + +func _ready(): + set_process(false) + +var load_start_time = 0.0 +var stuck_time = 0.0 +var last_progress = 0.0 + +func _process(delta): + if is_loading: + var status = ResourceLoader.load_threaded_get_status(path, progress) + match status: + ResourceLoader.THREAD_LOAD_IN_PROGRESS: + if progress.size() > 0: + var actual_progress = progress[0] * 100 + + # If progress is actually advancing, use it and reset stuck timer + if actual_progress > last_progress: + progress_bar.value = actual_progress + last_progress = actual_progress + stuck_time = 0.0 + else: + # Progress is stuck, increment stuck timer + stuck_time += delta + + # If stuck for more than 0.5 seconds, start simulating progress + if stuck_time > 0.5: + # Calculate how much more progress needed + var remaining_percent = 99.0 - progress_bar.value + + # Adjust increment speed based on how much progress we've already made + # Slower at the beginning, faster near the end + var increment_speed = 5.0 + (progress_bar.value / 20.0) + + # Apply increment + progress_bar.value = min(progress_bar.value + increment_speed * delta, 99.0) + + ResourceLoader.THREAD_LOAD_LOADED: + progress_bar.value = 100 + call_deferred("_handle_load_complete") + + ResourceLoader.THREAD_LOAD_FAILED: + print("Loading failed: ", path) + is_loading = false + set_process(false) + +func _handle_load_complete(): + var resource = ResourceLoader.load_threaded_get(path) + if resource: + change_scene(resource) + else: + print("Failed to get loaded resource") + + set_process(false) + is_loading = false + +func change_scene(resource: PackedScene): + print("Loading complete. Switching scene...") + # The Lobby (parent) should be removed by the caller or we handle it here if we are the root. + # But in this architecture, the Lobby is a child of root or MainMenu? + # Actually, usually we use get_tree().change_scene_to_packed(resource) + + get_tree().change_scene_to_packed(resource) + + # Clean up self (Loading Screen) + queue_free() + + +func load_level(_path: String): + print("Starting load for: ", _path) + path = _path + show() + content_control.show() + + if scene_name_label: + scene_name_label.text = "Loading Game..." # _path.get_file().get_basename() + + if tips.size() > 0: + $Control/tips.show() + tip_value.text = tips.pick_random() + else: + $Control/tips.hide() + + # 1. Request Load + var error = ResourceLoader.load_threaded_request(path) + if error == OK: + is_loading = true + set_process(true) + else: + print("Failed to start loading: ", error) + # Fallback? diff --git a/scripts/ui/loading_screen.gd.uid b/scripts/ui/loading_screen.gd.uid new file mode 100644 index 0000000..bc1cd01 --- /dev/null +++ b/scripts/ui/loading_screen.gd.uid @@ -0,0 +1 @@ +uid://dffsxj8h5sud6