extends Control # UI References - Main Menu @onready var main_menu_panel = $MainMenuPanel @onready var player_name_input = $MainMenuPanel/VBoxContainer/InputSection/PlayerNameInput @onready var create_room_btn = $MainMenuPanel/VBoxContainer/ButtonSection/CreateRoomBtn @onready var browse_rooms_btn = $MainMenuPanel/VBoxContainer/ButtonSection/BrowseRoomsBtn # UI References - Room List @onready var room_list_panel = $RoomListPanel @onready var room_list = $RoomListPanel/VBoxContainer/RoomList @onready var match_id_input = $RoomListPanel/VBoxContainer/MatchIdInput @onready var refresh_btn = $RoomListPanel/VBoxContainer/ButtonContainer/RefreshBtn @onready var join_btn = $RoomListPanel/VBoxContainer/ButtonContainer/JoinBtn @onready var back_btn = $RoomListPanel/VBoxContainer/ButtonContainer/BackBtn # UI References - Lobby @onready var lobby_panel = $LobbyPanel @onready var room_name_header = $LobbyPanel/VBoxContainer/RoomNameHeader @onready var match_id_display = $LobbyPanel/VBoxContainer/MatchIdContainer/MatchIdDisplay @onready var copy_id_btn = $LobbyPanel/VBoxContainer/MatchIdContainer/CopyIdBtn @onready var player_list = $LobbyPanel/VBoxContainer/PlayerList @onready var status_label = $LobbyPanel/VBoxContainer/StatusLabel @onready var ready_btn = $LobbyPanel/VBoxContainer/ButtonContainer/ReadyBtn @onready var start_game_btn = $LobbyPanel/VBoxContainer/ButtonContainer/StartGameBtn @onready var leave_btn = $LobbyPanel/VBoxContainer/ButtonContainer/LeaveBtn # UI References - Status @onready var connection_status = $StatusBar/ConnectionStatus # UI References - User Profile Bar (will be added to scene) var user_profile_bar: Control var profile_panel_instance: Control var admin_panel_instance: Control # Store current match ID for copy function var current_match_id: String = "" func _ready(): # Check if user is authenticated if not AuthManager.is_logged_in(): # Redirect to login screen - must use deferred call during _ready() call_deferred("_go_to_login") return # Initialize user profile bar _setup_user_profile_bar() # Set player name from profile if player_name_input: player_name_input.text = UserProfileManager.get_display_name() # Connect button signals create_room_btn.pressed.connect(_on_create_room_pressed) browse_rooms_btn.pressed.connect(_on_browse_rooms_pressed) refresh_btn.pressed.connect(_on_refresh_pressed) join_btn.pressed.connect(_on_join_pressed) back_btn.pressed.connect(_on_back_pressed) ready_btn.toggled.connect(_on_ready_toggled) start_game_btn.pressed.connect(_on_start_game_pressed) leave_btn.pressed.connect(_on_leave_pressed) copy_id_btn.pressed.connect(_on_copy_id_pressed) # Connect LobbyManager signals LobbyManager.room_list_updated.connect(_on_room_list_updated) LobbyManager.room_joined.connect(_on_room_joined) LobbyManager.room_left.connect(_on_room_left) LobbyManager.player_joined.connect(_on_player_joined) LobbyManager.player_left.connect(_on_player_left) LobbyManager.ready_state_changed.connect(_on_ready_state_changed) LobbyManager.all_players_ready.connect(_on_all_players_ready) LobbyManager.game_starting.connect(_on_game_starting) # Connect NakamaManager signals NakamaManager.connected_to_nakama.connect(_on_connected_to_nakama) NakamaManager.connection_failed.connect(_on_connection_failed) # Show main menu initially _show_panel("main_menu") _update_profile_bar() # ============================================================================= # User Profile Bar # ============================================================================= func _setup_user_profile_bar() -> void: # Create profile bar dynamically (or get reference if in scene) user_profile_bar = _create_profile_bar() add_child(user_profile_bar) func _create_profile_bar() -> Control: var bar := HBoxContainer.new() bar.name = "UserProfileBar" bar.set_anchors_preset(Control.PRESET_TOP_WIDE) bar.offset_top = 5 bar.offset_bottom = 45 bar.offset_left = 10 bar.offset_right = -10 # Avatar var avatar := TextureRect.new() avatar.name = "Avatar" avatar.custom_minimum_size = Vector2(35, 35) avatar.expand_mode = TextureRect.EXPAND_FIT_WIDTH avatar.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED bar.add_child(avatar) # Display name var name_label := Label.new() name_label.name = "DisplayName" name_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL name_label.add_theme_color_override("font_color", Color(0, 0.831, 1)) bar.add_child(name_label) # Account type badge var badge := Label.new() badge.name = "AccountBadge" badge.add_theme_font_size_override("font_size", 10) badge.add_theme_color_override("font_color", Color(0.5, 0.5, 0.6)) bar.add_child(badge) # Profile button var profile_btn := Button.new() profile_btn.name = "ProfileBtn" profile_btn.text = "Profile" profile_btn.pressed.connect(_on_profile_btn_pressed) bar.add_child(profile_btn) # Admin button (only visible for admins/hosts) var admin_btn := Button.new() admin_btn.name = "AdminBtn" admin_btn.text = "Admin" admin_btn.visible = false # Hidden by default, shown if user is admin admin_btn.pressed.connect(_on_admin_btn_pressed) bar.add_child(admin_btn) # Logout button var logout_btn := Button.new() logout_btn.name = "LogoutBtn" logout_btn.text = "Logout" logout_btn.pressed.connect(_on_logout_pressed) bar.add_child(logout_btn) return bar func _update_profile_bar() -> void: if not user_profile_bar: return var name_label := user_profile_bar.get_node_or_null("DisplayName") as Label if name_label: name_label.text = UserProfileManager.get_display_name() var badge := user_profile_bar.get_node_or_null("AccountBadge") as Label if badge: badge.text = "[Guest]" if AuthManager.is_guest else "[Registered]" var avatar := user_profile_bar.get_node_or_null("Avatar") as TextureRect if avatar: var avatar_url := UserProfileManager.get_avatar_url() if ResourceLoader.exists(avatar_url): avatar.texture = load(avatar_url) # Show admin button if user is admin or host var admin_btn := user_profile_bar.get_node_or_null("AdminBtn") as Button if admin_btn: # Check if user is admin (you can define admin check logic here) var is_admin = _check_if_admin() admin_btn.visible = is_admin func _on_profile_btn_pressed() -> void: # Show profile panel if not profile_panel_instance: var profile_panel_scene := load("res://scenes/ui/profile_panel.tscn") profile_panel_instance = profile_panel_scene.instantiate() profile_panel_instance.closed.connect(func(): profile_panel_instance.hide()) profile_panel_instance.profile_updated.connect(_update_profile_bar) add_child(profile_panel_instance) profile_panel_instance.show_panel() # Center the panel profile_panel_instance.position = (get_viewport_rect().size - profile_panel_instance.size) / 2 func _on_logout_pressed() -> void: AuthManager.logout() _go_to_login() func _go_to_login() -> void: if get_tree(): get_tree().change_scene_to_file("res://scenes/ui/login_screen.tscn") func _on_admin_btn_pressed() -> void: # Show admin panel if not admin_panel_instance: var admin_panel_scene := load("res://scenes/ui/admin_panel.tscn") admin_panel_instance = admin_panel_scene.instantiate() # Connect close signal if available if admin_panel_instance.has_signal("closed"): admin_panel_instance.closed.connect(func(): admin_panel_instance.hide()) add_child(admin_panel_instance) admin_panel_instance.show() # Center the panel admin_panel_instance.position = (get_viewport_rect().size - admin_panel_instance.size) / 2 func _check_if_admin() -> bool: # Check if user is admin - can be host or have specific admin role # You can extend this to check Nakama user metadata or roles if LobbyManager.is_host: return true # Check if user has admin role in their profile (optional) var user_id = AuthManager.get_user_id() if AuthManager.has_method("get_user_id") else "" # Add your admin user IDs here or check from Nakama metadata var admin_user_ids = ["admin_user_id_1", "admin_user_id_2"] # Configure as needed return user_id in admin_user_ids # ============================================================================= # Panel Management # ============================================================================= func _show_panel(panel_name: String) -> void: main_menu_panel.visible = panel_name == "main_menu" room_list_panel.visible = panel_name == "room_list" lobby_panel.visible = panel_name == "lobby" # ============================================================================= # Main Menu Button Handlers # ============================================================================= func _on_create_room_pressed() -> void: # Set player name LobbyManager.local_player_name = player_name_input.text.strip_edges() if LobbyManager.local_player_name.is_empty(): LobbyManager.local_player_name = "Host" connection_status.text = "Creating room..." # Room name auto-generated since Nakama doesn't support custom names LobbyManager.create_room("Room %d" % randi_range(1000, 9999)) func _on_browse_rooms_pressed() -> void: _show_panel("room_list") connection_status.text = "Loading rooms..." LobbyManager.refresh_room_list() # ============================================================================= # Room List Button Handlers # ============================================================================= func _on_refresh_pressed() -> void: connection_status.text = "Refreshing..." room_list.clear() LobbyManager.refresh_room_list() func _on_join_pressed() -> void: # First check if there's a match ID entered var match_id = match_id_input.text.strip_edges() if match_id.is_empty(): # Try to use selected room from list var selected_items = room_list.get_selected_items() if selected_items.size() == 0: connection_status.text = "Please select a room or enter Match ID" return var selected_idx = selected_items[0] if selected_idx < LobbyManager.available_rooms.size(): match_id = LobbyManager.available_rooms[selected_idx].get("match_id", "") if match_id.is_empty(): connection_status.text = "No room selected" return # Set player name LobbyManager.local_player_name = player_name_input.text.strip_edges() if LobbyManager.local_player_name.is_empty(): LobbyManager.local_player_name = "Player" connection_status.text = "Joining room..." LobbyManager.join_room(match_id) func _on_back_pressed() -> void: _show_panel("main_menu") connection_status.text = "" # ============================================================================= # Lobby Button Handlers # ============================================================================= func _on_ready_toggled(is_ready: bool) -> void: LobbyManager.set_ready(is_ready) ready_btn.text = "READY ✓" if is_ready else "READY" func _on_start_game_pressed() -> void: LobbyManager.start_game() func _on_leave_pressed() -> void: LobbyManager.leave_room() _show_panel("main_menu") ready_btn.button_pressed = false ready_btn.text = "READY" func _on_copy_id_pressed() -> void: DisplayServer.clipboard_set(current_match_id) connection_status.text = "Match ID copied to clipboard!" # ============================================================================= # LobbyManager Signal Handlers # ============================================================================= func _on_room_list_updated(rooms: Array) -> void: room_list.clear() for room in rooms: var room_name = room.get("room_name", "Unknown") var host_name = room.get("host_name", "Unknown") var player_count = room.get("player_count", 1) var max_players = room.get("max_players", 4) room_list.add_item("%s - %s (%d/%d)" % [room_name, host_name, player_count, max_players]) if rooms.size() == 0: connection_status.text = "No rooms available" else: connection_status.text = "Found %d room(s)" % rooms.size() func _on_room_joined(room_data: Dictionary) -> void: _show_panel("lobby") current_match_id = room_data.get("match_id", "") room_name_header.text = "ROOM: %s" % room_data.get("room_name", "Unknown") match_id_display.text = "Match ID: %s " % current_match_id # Update start button visibility (host only) start_game_btn.visible = LobbyManager.is_host _update_player_list() connection_status.text = "Connected to room" func _on_room_left() -> void: _show_panel("main_menu") connection_status.text = "Left room" func _on_player_joined(player_data: Dictionary) -> void: _update_player_list() status_label.text = "%s joined!" % player_data.get("name", "Player") func _on_player_left(_player_id: int) -> void: _update_player_list() status_label.text = "A player left" func _on_ready_state_changed(_player_id: int, _is_ready: bool) -> void: _update_player_list() _update_status() func _on_all_players_ready() -> void: if LobbyManager.is_host: start_game_btn.disabled = false status_label.text = "All players ready! Host can start." else: status_label.text = "All players ready! Waiting for host..." func _on_game_starting() -> void: connection_status.text = "Starting game..." # Small delay for visual feedback await get_tree().create_timer(0.5).timeout get_tree().change_scene_to_file("res://scenes/main.tscn") # ============================================================================= # NakamaManager Signal Handlers # ============================================================================= func _on_connected_to_nakama() -> void: connection_status.text = "Connected to server" func _on_connection_failed(error_message: String) -> void: connection_status.text = "Connection failed: %s" % error_message _show_panel("main_menu") # ============================================================================= # Helper Functions # ============================================================================= func _update_player_list() -> void: player_list.clear() var players = LobbyManager.get_players() for player in players: var player_name = player.get("name", "Unknown") var is_ready = player.get("is_ready", false) var ready_icon = " ✓" if is_ready else " ✗" var host_tag = " (Host)" if player.get("id") == 1 else "" player_list.add_item("%s%s%s" % [player_name, host_tag, ready_icon]) func _update_status() -> void: var players = LobbyManager.get_players() var ready_count = 0 for player in players: if player.get("is_ready", false): ready_count += 1 status_label.text = "Ready: %d/%d" % [ready_count, players.size()] if LobbyManager.is_host: start_game_btn.disabled = not LobbyManager.is_all_ready()