diff --git a/assets/fonts/TektonDash2.ttf b/assets/fonts/TektonDash2.ttf new file mode 100644 index 0000000..1c2e2b9 Binary files /dev/null and b/assets/fonts/TektonDash2.ttf differ 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