feat: Implement comprehensive lobby system with main menu, room management, and loading screen.
This commit is contained in:
@@ -31,13 +31,8 @@ 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
|
||||
[node name="loading_screen" type="CanvasLayer"]
|
||||
layer = 128
|
||||
script = ExtResource("1_u2jrd")
|
||||
|
||||
[node name="Bg" type="TextureRect" parent="."]
|
||||
|
||||
+2
-1
@@ -708,8 +708,9 @@ func _on_room_left() -> void:
|
||||
connection_status.text = "Left room"
|
||||
|
||||
func _on_host_disconnected() -> void:
|
||||
# Keep the connection status updated in the UI
|
||||
connection_status.text = "Host disconnected. Returning to menu..."
|
||||
_show_panel("main_menu")
|
||||
connection_status.text = "Host disconnected. Match terminated."
|
||||
|
||||
func _on_player_joined(player_data: Dictionary) -> void:
|
||||
_update_player_slots()
|
||||
|
||||
+49
-21
@@ -460,6 +460,8 @@ func _setup_global_match_timer_ui():
|
||||
add_child(panel)
|
||||
|
||||
func _process(delta):
|
||||
if not is_inside_tree(): return
|
||||
if not check_multiplayer(): return
|
||||
if multiplayer.is_server() and GameStateManager.is_game_started():
|
||||
if TurnManager.turn_based_mode:
|
||||
rpc("sync_turn_index", TurnManager.current_turn_index)
|
||||
@@ -528,7 +530,9 @@ func _setup_host_game():
|
||||
# Moved _assign_random_spawn_positions() to after bot loop
|
||||
|
||||
# Wait for players to be fully ready (player.gd has 0.1s await in _ready before managers init)
|
||||
await get_tree().create_timer(0.3).timeout
|
||||
# Faster for LAN mode
|
||||
var setup_delay = 0.1 if LobbyManager.is_lan_mode else 0.3
|
||||
await get_tree().create_timer(setup_delay).timeout
|
||||
|
||||
# Set host goals - get goals directly from GoalManager
|
||||
var host_goals = GoalManager.get_goals_for_player(0)
|
||||
@@ -651,18 +655,20 @@ func _setup_client_game():
|
||||
powerup_ui.setup(player_character)
|
||||
print("Client: PowerUpInventoryUI setup forced for ", my_id)
|
||||
|
||||
# Wait shorter time for host to be ready, then request full sync to correct positions/state
|
||||
await get_tree().create_timer(1.0).timeout
|
||||
# Wait for host to be ready, then request full sync
|
||||
# Snappier for LAN mode as peer is already established
|
||||
var client_setup_delay = 0.2 if LobbyManager.is_lan_mode else 1.0
|
||||
await get_tree().create_timer(client_setup_delay).timeout
|
||||
|
||||
if multiplayer.has_multiplayer_peer() and multiplayer.multiplayer_peer.get_connection_status() == MultiplayerPeer.CONNECTION_CONNECTED:
|
||||
if check_multiplayer():
|
||||
# Ensure we see the server (Peer 1)
|
||||
if 1 in multiplayer.get_peers():
|
||||
rpc_id(1, "request_full_player_sync", my_id)
|
||||
rpc_id(1, "request_full_grid_sync")
|
||||
else:
|
||||
print("Client: Connected but Peer 1 not found yet. Retrying in 1s...")
|
||||
await get_tree().create_timer(1.0).timeout
|
||||
if 1 in multiplayer.get_peers():
|
||||
await get_tree().create_timer(0.5).timeout
|
||||
if check_multiplayer() and 1 in multiplayer.get_peers():
|
||||
rpc_id(1, "request_full_player_sync", my_id)
|
||||
rpc_id(1, "request_full_grid_sync")
|
||||
|
||||
@@ -692,17 +698,19 @@ func _auto_start_from_lobby():
|
||||
func _start_game():
|
||||
if multiplayer.is_server():
|
||||
# Wait for Nakama websocket to actually be open, up to 5 seconds
|
||||
var nakama = get_node_or_null("/root/NakamaManager")
|
||||
if nakama and nakama.has_method("is_connected_to_nakama"):
|
||||
var wait_time = 0.0
|
||||
while not nakama.is_connected_to_nakama() and wait_time < 5.0:
|
||||
await get_tree().create_timer(0.2).timeout
|
||||
wait_time += 0.2
|
||||
# SKIP THIS FOR LAN MODE
|
||||
if not LobbyManager.is_lan_mode:
|
||||
var nakama = get_node_or_null("/root/NakamaManager")
|
||||
if nakama and nakama.has_method("is_connected_to_nakama"):
|
||||
var wait_time = 0.0
|
||||
while not nakama.is_connected_to_nakama() and wait_time < 5.0:
|
||||
await get_tree().create_timer(0.2).timeout
|
||||
wait_time += 0.2
|
||||
|
||||
# Allow socket/peer to stabilize before blasting RPCs
|
||||
# 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
|
||||
# Stabilization delay to allow clients to finish loading and spawning
|
||||
# We wait 1.5s to ensure the 1.2s loading screen buffer has finished
|
||||
# before the countdown starts.
|
||||
await get_tree().create_timer(1.5).timeout
|
||||
|
||||
# NOW assign spawn positions for EVERYONE (Host, Client, Bots)
|
||||
# This safely sends RPCs over the completed socket connection
|
||||
@@ -1230,6 +1238,7 @@ func add_player_character(peer_id: int, is_bot: bool = false):
|
||||
ui_manager.update_playerboard_ui()
|
||||
|
||||
func _on_peer_connected(new_peer_id: int):
|
||||
if not is_inside_tree(): return
|
||||
if multiplayer.is_server():
|
||||
await get_tree().create_timer(0.1).timeout
|
||||
add_player_character(new_peer_id)
|
||||
@@ -1252,6 +1261,7 @@ func add_newly_connected_player_character(new_peer_id: int):
|
||||
add_player_character(new_peer_id)
|
||||
|
||||
func _on_peer_disconnected(peer_id: int):
|
||||
if not is_inside_tree(): return
|
||||
if multiplayer.is_server():
|
||||
print("[Main] Peer %d disconnected. Checking for bot replacement..." % peer_id)
|
||||
|
||||
@@ -1305,14 +1315,24 @@ func create_bot_with_state(bot_id: int, pos: Vector2i, p_score: int, p_goals: Ar
|
||||
bot_character.update_player_position(pos)
|
||||
|
||||
func _on_host_disconnected():
|
||||
"""Called when the host leaves. Returns clients to the main menu."""
|
||||
if not is_inside_tree(): return
|
||||
"""Called when the host leaves. Returns clients to the lobby."""
|
||||
print("[Main] Host disconnected. Match terminated. Cleaning up and returning to lobby...")
|
||||
LobbyManager.leave_room()
|
||||
get_tree().change_scene_to_file("res://scenes/lobby.tscn")
|
||||
|
||||
# Use loading screen to return to lobby
|
||||
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:
|
||||
get_tree().change_scene_to_file("res://scenes/lobby.tscn")
|
||||
|
||||
func _on_rematch_starting():
|
||||
if not is_inside_tree(): return
|
||||
"""Called when a rematch is triggered. Reloads the game scene."""
|
||||
print("[Main] Rematch starting! Resetting state and reloading scene...")
|
||||
print("[Main] Rematch starting. Resetting local state...")
|
||||
|
||||
# Reset singletons/managers that persist across scene reloads
|
||||
GameStateManager.reset()
|
||||
@@ -2439,8 +2459,7 @@ func _on_joystick_toggled(enabled: bool):
|
||||
touch_controls._save_settings()
|
||||
|
||||
func can_rpc() -> bool:
|
||||
if not multiplayer.has_multiplayer_peer(): return false
|
||||
if multiplayer.multiplayer_peer.get_connection_status() != MultiplayerPeer.CONNECTION_CONNECTED: return false
|
||||
if not check_multiplayer(): return false
|
||||
|
||||
if LobbyManager.is_lan_mode:
|
||||
return true
|
||||
@@ -2451,6 +2470,15 @@ func can_rpc() -> bool:
|
||||
|
||||
return true
|
||||
|
||||
func check_multiplayer() -> bool:
|
||||
"""Safety check for multiplayer peer access."""
|
||||
if not is_inside_tree(): return false
|
||||
# Accessing multiplayer here is safe because we checked is_inside_tree
|
||||
var peer = multiplayer.multiplayer_peer
|
||||
if not peer: return false
|
||||
if peer.get_connection_status() == MultiplayerPeer.CONNECTION_DISCONNECTED: return false
|
||||
return true
|
||||
|
||||
@rpc("authority", "call_local", "reliable")
|
||||
func display_message(message: String, type: int = 0):
|
||||
"""Broadcasts a message to the local player's UI. This is called via main.rpc from various managers."""
|
||||
|
||||
@@ -290,19 +290,21 @@ func leave_room() -> void:
|
||||
reset()
|
||||
_stop_lan_broadcast()
|
||||
|
||||
# Emit before nulling peer so UI can still access peer info if needed
|
||||
emit_signal("room_left")
|
||||
|
||||
if is_lan_mode:
|
||||
# LAN mode: just close the ENet peer directly
|
||||
if multiplayer.has_multiplayer_peer():
|
||||
multiplayer.set_multiplayer_peer(null)
|
||||
is_lan_mode = false
|
||||
# LAN mode: Host should keep peer alive long enough to reach lobby
|
||||
if not is_host or get_tree().current_scene.name == "Lobby":
|
||||
if multiplayer.has_multiplayer_peer():
|
||||
multiplayer.set_multiplayer_peer(null)
|
||||
is_lan_mode = false
|
||||
else:
|
||||
# Nakama mode: full Nakama cleanup
|
||||
NakamaManager.cleanup()
|
||||
|
||||
# Important: Clean up game state as well to prevent ghost players
|
||||
GameStateManager.reset()
|
||||
|
||||
emit_signal("room_left")
|
||||
|
||||
func refresh_room_list() -> void:
|
||||
"""Request updated room list from Nakama or scan for LAN rooms."""
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
extends Control
|
||||
extends CanvasLayer
|
||||
|
||||
@export var tips: Array[String] = [
|
||||
"Use your cards wisely!",
|
||||
@@ -79,6 +79,14 @@ func change_scene(resource: PackedScene):
|
||||
|
||||
get_tree().change_scene_to_packed(resource)
|
||||
|
||||
# Update label to show we are initializing the game world
|
||||
if scene_name_label:
|
||||
scene_name_label.text = "Preparing Game..."
|
||||
|
||||
# Wait for assets (Tektons, Spawn Tiles) to initialize in the background
|
||||
# This keeps the loading screen visible while main.gd runs its _ready() setup
|
||||
await get_tree().create_timer(1.2).timeout
|
||||
|
||||
# Clean up self (Loading Screen)
|
||||
queue_free()
|
||||
|
||||
@@ -86,7 +94,7 @@ func change_scene(resource: PackedScene):
|
||||
func load_level(_path: String):
|
||||
print("Starting load for: ", _path)
|
||||
path = _path
|
||||
show()
|
||||
visible = true
|
||||
content_control.show()
|
||||
|
||||
if scene_name_label:
|
||||
|
||||
Reference in New Issue
Block a user