extends Control # UI References - Main Menu @onready var main_menu_panel = $MainMenuPanel @onready var create_room_btn = $MainMenuPanel/VBoxContainer/ButtonSection/CreateRoomBtn @onready var browse_rooms_btn = $MainMenuPanel/VBoxContainer/ButtonSection/BrowseRoomsBtn @onready var main_menu_profile_btn = $MainMenuPanel/VBoxContainer/ButtonSection/ProfileBtn @onready var lobby_settings_btn = $MainMenuPanel/VBoxContainer/ButtonSection/SettingsBtn @onready var quit_btn = $MainMenuPanel/VBoxContainer/ButtonSection/QuitBtn # UI References - Server Selection @onready var server_option = $MainMenuPanel/VBoxContainer/ServerSelectionSection/ServerOption @onready var server_ip_input = $MainMenuPanel/VBoxContainer/ServerSelectionSection/ServerIPInput # Leaderboard Reference @onready var leaderboard_btn = $MainMenuPanel/VBoxContainer/ButtonSection/LeaderboardBtn # 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 @onready var room_list_profile_btn = $RoomListPanel/VBoxContainer/ButtonContainer/ProfileBtn # UI References - Lobby Panel @onready var lobby_panel = $LobbyPanel @onready var room_name_header = $LobbyPanel/RoomNameHeader @onready var host_banner = $LobbyPanel/HostBanner @onready var host_banner_label = $LobbyPanel/HostBanner/HostBannerLabel # UI References - Top Bar @onready var profile_btn = $LobbyPanel/TopBar/ProfileSection/ProfileBtn @onready var logout_btn = $LobbyPanel/TopBar/ProfileSection/LogoutBtn @onready var match_id_display = $LobbyPanel/TopBar/MatchIdContainer/MatchIdDisplay @onready var copy_id_btn = $LobbyPanel/TopBar/MatchIdContainer/CopyIdBtn @onready var duration_option = $LobbyPanel/TopBar/SettingsSection/DurationOption @onready var duration_text_label = $LobbyPanel/TopBar/SettingsSection/DurationTextLabel @onready var lobby_top_settings_btn = $LobbyPanel/TopBar/ProfileSection/LobbySettingsBtn @onready var random_spawn_check = $LobbyPanel/TopBar/SettingsSection/RandomSpawnCheck @onready var random_spawn_label = $LobbyPanel/TopBar/SettingsSection/RandomSpawnLabel @onready var enable_timer_check = $LobbyPanel/TopBar/SettingsSection/EnableTimerCheck @onready var enable_timer_label = $LobbyPanel/TopBar/SettingsSection/EnableTimerLabel @onready var scarcity_option = $LobbyPanel/TopBar/SettingsSection/ScarcityOption @onready var scarcity_label = $LobbyPanel/TopBar/SettingsSection/ScarcityLabel @onready var scarcity_spacer = $LobbyPanel/TopBar/SettingsSection/ScarcitySpacer @onready var spawn_spacer = $LobbyPanel/TopBar/SettingsSection/SpawnSpacer @onready var timer_spacer = $LobbyPanel/TopBar/SettingsSection/TimerSpacer @onready var game_mode_option = $LobbyPanel/TopBar/SettingsSection/GameModeOption @onready var game_mode_text_label = $LobbyPanel/TopBar/SettingsSection/GameModeTextLabel # Custom Settings Containers var sng_settings_container: HBoxContainer var sng_go_option: OptionButton var sng_stop_option: OptionButton var sng_goals_option: OptionButton var doors_settings_container: HBoxContainer var doors_swap_option: OptionButton var doors_refresh_option: OptionButton var doors_goals_option: OptionButton # UI References - Player Slots @onready var players_container = $LobbyPanel/PlayersContainer @onready var players_container2 = $LobbyPanel/PlayersContainer2 @onready var player_slots: Array[Control] = [] # UI References - Area Selector @onready var area_selector = $LobbyPanel/AreaSelector @onready var area_left_btn = $LobbyPanel/AreaSelector/AreaLeftBtn @onready var area_name_label = $LobbyPanel/AreaSelector/AreaName @onready var area_right_btn = $LobbyPanel/AreaSelector/AreaRightBtn # UI References - Bottom Bar @onready var leave_btn = $LobbyPanel/BottomBar/LeaveBtn @onready var ready_btn = $LobbyPanel/BottomBar/ReadyBtn @onready var start_game_btn = $LobbyPanel/BottomBar/StartGameBtn @onready var status_label = $LobbyPanel/StatusLabel # UI References - Status @onready var connection_status = $StatusBar/ConnectionStatus # Character preview textures var character_textures: Dictionary = {} # Profile panel instance var profile_panel_instance: Control var admin_panel_instance: Control # Store current match ID for copy function var current_match_id: String = "" var leaderboard_panel_instance: Control # Server Selection Controls (Now in tscn) # var server_option: OptionButton # var server_ip_input: LineEdit func _ready(): # Check if user is authenticated (Commented out to allow server config on main menu) # if not AuthManager.is_logged_in(): # call_deferred("_go_to_login") # return # Load character textures _load_character_textures() # Server config UI is now in tscn # Get player slot references _setup_player_slots() # Setup Game Mode specific UI dynamically _create_custom_settings_ui() # Set player name from profile if AuthManager.is_guest: LobbyManager.local_player_name = "Guest" else: LobbyManager.local_player_name = UserProfileManager.get_display_name() # Connect button signals - Main Menu create_room_btn.pressed.connect(_on_create_room_pressed) browse_rooms_btn.pressed.connect(_on_browse_rooms_pressed) if main_menu_profile_btn: main_menu_profile_btn.pressed.connect(_on_profile_btn_pressed) if lobby_settings_btn: lobby_settings_btn.pressed.connect(_on_settings_pressed) if leaderboard_btn: leaderboard_btn.pressed.connect(_on_leaderboard_pressed) if quit_btn: quit_btn.pressed.connect(_on_quit_pressed) # Connect Server Selection signals if server_option: server_option.item_selected.connect(_on_server_option_selected) # Initialize state _on_server_option_selected(server_option.selected) if server_ip_input: server_ip_input.text_submitted.connect(_on_server_ip_submitted) server_ip_input.focus_exited.connect(func(): _on_server_ip_submitted(server_ip_input.text)) # Connect button signals - Room List refresh_btn.pressed.connect(_on_refresh_pressed) join_btn.pressed.connect(_on_join_pressed) back_btn.pressed.connect(_on_back_pressed) if room_list_profile_btn: room_list_profile_btn.pressed.connect(_on_profile_btn_pressed) # Connect button signals - Lobby profile_btn.pressed.connect(_on_profile_btn_pressed) if logout_btn: logout_btn.pressed.connect(_on_logout_pressed) if lobby_top_settings_btn: lobby_top_settings_btn.pressed.connect(_on_settings_pressed) copy_id_btn.pressed.connect(_on_copy_id_pressed) duration_option.item_selected.connect(_on_duration_selected) random_spawn_check.toggled.connect(_on_random_spawn_toggled) enable_timer_check.toggled.connect(_on_enable_timer_toggled) area_left_btn.pressed.connect(func(): LobbyManager.cycle_area(-1)) area_right_btn.pressed.connect(func(): LobbyManager.cycle_area(1)) leave_btn.pressed.connect(_on_leave_pressed) ready_btn.toggled.connect(_on_ready_toggled) start_game_btn.pressed.connect(_on_start_game_pressed) if scarcity_option: scarcity_option.item_selected.connect(_on_scarcity_selected) if game_mode_option: game_mode_option.item_selected.connect(_on_game_mode_selected) _setup_game_modes() # 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.host_disconnected.connect(_on_host_disconnected) 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) LobbyManager.match_duration_changed.connect(_on_match_duration_changed) LobbyManager.randomize_spawn_changed.connect(_on_randomize_spawn_changed) LobbyManager.enable_cycle_timer_changed.connect(_on_enable_cycle_timer_changed) LobbyManager.character_changed.connect(_on_character_changed) LobbyManager.area_changed.connect(_on_area_changed) LobbyManager.scarcity_mode_changed.connect(_on_scarcity_mode_changed) LobbyManager.game_mode_changed.connect(_on_game_mode_changed) LobbyManager.player_list_changed.connect(_update_player_slots) LobbyManager.sng_go_duration_changed.connect(_on_sng_update) LobbyManager.sng_stop_duration_changed.connect(_on_sng_update) LobbyManager.sng_required_goals_changed.connect(_on_sng_update) LobbyManager.doors_swap_time_changed.connect(_on_doors_update) LobbyManager.doors_refresh_time_changed.connect(_on_doors_update) LobbyManager.doors_required_goals_changed.connect(_on_doors_update) # Connect NakamaManager signals NakamaManager.connected_to_nakama.connect(_on_connected_to_nakama) NakamaManager.connection_failed.connect(_on_connection_failed) # Connect UserProfileManager signals UserProfileManager.profile_updated.connect(_on_profile_updated) # Show main menu initially _show_panel("main_menu") # Check for disconnection reason from manager if not LobbyManager.disconnect_reason.is_empty(): connection_status.text = LobbyManager.disconnect_reason LobbyManager.disconnect_reason = "" # ============================================================================= # Setup # ============================================================================= func _load_character_textures() -> void: """Load character preview textures.""" var characters = { "Copper": "res://assets/graphics/character_selection/sc_characters/sc_copper.png", "Dabro": "res://assets/graphics/character_selection/sc_characters/sc_dabro.png", "Gatot": "res://assets/graphics/character_selection/sc_characters/sc_gatot.png", "Pip": "res://assets/graphics/character_selection/sc_characters/sc_pip.png", "Random": "res://assets/graphics/character_selection/sc_characters/sc_unknown.png" } for char_name in characters: var tex_path = characters[char_name] if ResourceLoader.exists(tex_path): character_textures[char_name] = load(tex_path) else: print("[Lobby] Character texture not found: ", tex_path) func _on_server_option_selected(index: int) -> void: if index == 0: # Nakama Localhost if server_ip_input: server_ip_input.visible = false NakamaManager.set_server("localhost") LobbyManager.is_lan_mode = false elif index == 1: # Nakama Remote if server_ip_input: server_ip_input.visible = true if server_ip_input: NakamaManager.set_server(server_ip_input.text) LobbyManager.is_lan_mode = false else: # LAN Direct if server_ip_input: server_ip_input.visible = false LobbyManager.is_lan_mode = true func _on_server_ip_submitted(new_text: String) -> void: if server_option and server_option.selected == 1: NakamaManager.set_server(new_text.strip_edges()) func _setup_game_modes() -> void: if not game_mode_option: return game_mode_option.clear() for mode in LobbyManager.available_game_modes: game_mode_option.add_item(mode) # Select current mode for i in range(game_mode_option.item_count): if game_mode_option.get_item_text(i) == LobbyManager.game_mode: game_mode_option.selected = i break func _setup_player_slots() -> void: """Get references to all player slot nodes.""" player_slots.clear() # Slots 1-4 in Container 1 for i in range(1, 5): var slot = players_container.get_node_or_null("PlayerSlot%d" % i) if slot: player_slots.append(slot) _connect_slot_signals(slot, i) # Slots 5-8 in Container 2 if players_container2: for i in range(5, 9): var slot = players_container2.get_node_or_null("PlayerSlot%d" % i) if slot: player_slots.append(slot) _connect_slot_signals(slot, i) func _connect_slot_signals(slot: Control, i: int): # Connect character navigation buttons for all slots var left_btn = slot.get_node_or_null("CharacterNav%d/CharLeftBtn%d" % [i, i]) var right_btn = slot.get_node_or_null("CharacterNav%d/CharRightBtn%d" % [i, i]) if left_btn: left_btn.pressed.connect(func(): LobbyManager.cycle_character(-1)) if right_btn: right_btn.pressed.connect(func(): LobbyManager.cycle_character(1)) # ============================================================================= # 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: # Use profile name for logged-in users, or guest for others if AuthManager.is_guest: if LobbyManager.local_player_name.is_empty() or LobbyManager.local_player_name == "Player": LobbyManager.local_player_name = "Guest" else: LobbyManager.local_player_name = UserProfileManager.get_display_name() if LobbyManager.is_lan_mode: connection_status.text = "Starting LAN room..." var ok = await LobbyManager.create_room_lan("LAN Room " + str(randi_range(100, 999))) if not ok: connection_status.text = "Failed to start LAN room. Check port 7777." else: connection_status.text = "Creating Nakama room..." LobbyManager.create_room("Room %d" % randi_range(1000, 9999)) func _on_browse_rooms_pressed() -> void: _show_panel("room_list") if LobbyManager.is_lan_mode: connection_status.text = "LAN Mode - Enter Host IP to join" match_id_input.placeholder_text = "Enter Host IP (e.g. 192.168.1.10)..." $RoomListPanel/VBoxContainer/MatchIdLabel.text = "DIRECT CONNECT (HOST IP)" _on_refresh_pressed() # Try to discover rooms if implemented else: connection_status.text = "Loading Nakama rooms..." match_id_input.placeholder_text = "Paste match ID here..." $RoomListPanel/VBoxContainer/MatchIdLabel.text = "DIRECT CONNECT (MATCH ID)" 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: var match_id = match_id_input.text.strip_edges() if match_id.is_empty() and not LobbyManager.is_lan_mode: 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", "") # Determine player name if AuthManager.is_guest: if LobbyManager.local_player_name.is_empty(): LobbyManager.local_player_name = "Guest" else: LobbyManager.local_player_name = UserProfileManager.get_display_name() if LobbyManager.is_lan_mode: if match_id.is_empty(): # If nothing entered but something selected in list (discovered), use it var selected_items = room_list.get_selected_items() if selected_items.size() > 0: var idx = selected_items[0] if idx < LobbyManager.available_rooms.size(): match_id = LobbyManager.available_rooms[idx].get("ip", "") if match_id.is_empty(): connection_status.text = "Enter Host IP to join" return connection_status.text = "Connecting to %s..." % match_id var ok = LobbyManager.join_room_lan(match_id) if not ok: connection_status.text = "Failed to connect to %s" % match_id else: if match_id.is_empty(): connection_status.text = "No room selected" return connection_status.text = "Joining Nakama room..." LobbyManager.join_room(match_id) func _on_back_pressed() -> void: _show_panel("main_menu") connection_status.text = "" func _update_settings_visibility() -> void: var is_host = LobbyManager.is_host var is_freemode = LobbyManager.game_mode == "Freemode" # Duration var show_duration = is_freemode duration_option.visible = is_host and show_duration duration_text_label.visible = not is_host and show_duration $LobbyPanel/TopBar/SettingsSection/DurationLabel.visible = show_duration # Random Spawn var show_spawn = is_freemode random_spawn_check.visible = is_host and show_spawn random_spawn_label.visible = not is_host and show_spawn if spawn_spacer: spawn_spacer.visible = show_spawn # Timer var show_timer = is_freemode enable_timer_check.visible = is_host and show_timer enable_timer_label.visible = not is_host and show_timer if timer_spacer: timer_spacer.visible = show_timer # Scarcity var show_scarcity = is_freemode if scarcity_option: scarcity_option.visible = is_host and show_scarcity if scarcity_label: scarcity_label.visible = not is_host and show_scarcity if scarcity_spacer: scarcity_spacer.visible = show_scarcity # Custom mode sets var is_sng = LobbyManager.game_mode == "Stop n Go" if sng_settings_container: sng_settings_container.visible = is_sng sng_go_option.disabled = not is_host sng_stop_option.disabled = not is_host sng_goals_option.disabled = not is_host var is_doors = LobbyManager.game_mode == "Tekton Doors" if doors_settings_container: doors_settings_container.visible = is_doors doors_swap_option.disabled = not is_host doors_refresh_option.disabled = not is_host doors_goals_option.disabled = not is_host func _create_custom_settings_ui() -> void: var settings_section = $LobbyPanel/TopBar/SettingsSection if not settings_section: return # Stop n Go sng_settings_container = HBoxContainer.new() sng_settings_container.visible = false settings_section.add_child(sng_settings_container) _add_label(sng_settings_container, "Go Time:") sng_go_option = OptionButton.new() sng_go_option.add_item("10s"); sng_go_option.add_item("15s"); sng_go_option.add_item("25s") sng_go_option.item_selected.connect(func(idx): if LobbyManager.is_host: LobbyManager.set_sng_go_duration([10, 15, 25][idx])) sng_settings_container.add_child(sng_go_option) _add_label(sng_settings_container, "Stop Time:") sng_stop_option = OptionButton.new() sng_stop_option.add_item("3s"); sng_stop_option.add_item("4s"); sng_stop_option.add_item("5s") sng_stop_option.item_selected.connect(func(idx): if LobbyManager.is_host: LobbyManager.set_sng_stop_duration([3, 4, 5][idx])) sng_settings_container.add_child(sng_stop_option) _add_label(sng_settings_container, "Req Goals:") sng_goals_option = OptionButton.new() sng_goals_option.add_item("5"); sng_goals_option.add_item("8"); sng_goals_option.add_item("12") sng_goals_option.item_selected.connect(func(idx): if LobbyManager.is_host: LobbyManager.set_sng_required_goals([5, 8, 12][idx])) sng_settings_container.add_child(sng_goals_option) # Tekton Doors doors_settings_container = HBoxContainer.new() doors_settings_container.visible = false settings_section.add_child(doors_settings_container) _add_label(doors_settings_container, "Swap Wait:") doors_swap_option = OptionButton.new() doors_swap_option.add_item("10s"); doors_swap_option.add_item("15s"); doors_swap_option.add_item("30s") doors_swap_option.item_selected.connect(func(idx): if LobbyManager.is_host: LobbyManager.set_doors_swap_time([10, 15, 30][idx])) doors_settings_container.add_child(doors_swap_option) _add_label(doors_settings_container, "Tile Refresh:") doors_refresh_option = OptionButton.new() doors_refresh_option.add_item("15s"); doors_refresh_option.add_item("25s"); doors_refresh_option.add_item("40s") doors_refresh_option.item_selected.connect(func(idx): if LobbyManager.is_host: LobbyManager.set_doors_refresh_time([15, 25, 40][idx])) doors_settings_container.add_child(doors_refresh_option) _add_label(doors_settings_container, "Req Goals:") doors_goals_option = OptionButton.new() doors_goals_option.add_item("5"); doors_goals_option.add_item("8"); doors_goals_option.add_item("12") doors_goals_option.item_selected.connect(func(idx): if LobbyManager.is_host: LobbyManager.set_doors_required_goals([5, 8, 12][idx])) doors_settings_container.add_child(doors_goals_option) # Move Game Mode selector to the far right var gm_spacer = settings_section.get_node_or_null("GameModeSpacer") var gm_option = settings_section.get_node_or_null("GameModeOption") var gm_label = settings_section.get_node_or_null("GameModeTextLabel") if gm_spacer: settings_section.move_child(gm_spacer, -1) if gm_option: settings_section.move_child(gm_option, -1) if gm_label: settings_section.move_child(gm_label, -1) func _add_label(parent: Control, text: String): var spacer = Control.new() spacer.custom_minimum_size = Vector2(10, 0) parent.add_child(spacer) var lbl = Label.new() lbl.text = text parent.add_child(lbl) # ============================================================================= # 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) status_label.text = "Match ID copied!" func _on_duration_selected(index: int) -> void: if not LobbyManager.is_host: return var durations = [60, 120, 180, 300, 600] if index >= 0 and index < durations.size(): LobbyManager.set_match_duration(durations[index]) func _on_random_spawn_toggled(toggled_on): LobbyManager.set_randomize_spawn(toggled_on) func _on_enable_timer_toggled(toggled_on): LobbyManager.set_enable_cycle_timer(toggled_on) func _on_scarcity_selected(index: int) -> void: if not LobbyManager.is_host: return var mode = scarcity_option.get_item_text(index) LobbyManager.set_scarcity_mode(mode) func _on_scarcity_mode_changed(mode: String) -> void: if scarcity_option: for i in range(scarcity_option.item_count): if scarcity_option.get_item_text(i) == mode: scarcity_option.selected = i break if scarcity_label: scarcity_label.text = mode func _on_game_mode_selected(index: int) -> void: if not LobbyManager.is_host: return var mode = game_mode_option.get_item_text(index) LobbyManager.set_game_mode(mode) func _on_game_mode_changed(mode: String) -> void: if game_mode_option: for i in range(game_mode_option.item_count): if game_mode_option.get_item_text(i) == mode: game_mode_option.selected = i break if game_mode_text_label: game_mode_text_label.text = mode _update_settings_visibility() func _on_sng_update(_val: int = 0) -> void: if not sng_go_option: return var go_idx = [10, 15, 25].find(LobbyManager.sng_go_duration) if go_idx != -1: sng_go_option.selected = go_idx var stop_idx = [3, 4, 5].find(LobbyManager.sng_stop_duration) if stop_idx != -1: sng_stop_option.selected = stop_idx var goals_idx = [5, 8, 12].find(LobbyManager.sng_required_goals) if goals_idx != -1: sng_goals_option.selected = goals_idx func _on_doors_update(_val: int = 0) -> void: if not doors_swap_option: return var swap_idx = [10, 15, 30].find(LobbyManager.doors_swap_time) if swap_idx != -1: doors_swap_option.selected = swap_idx var refresh_idx = [15, 25, 40].find(LobbyManager.doors_refresh_time) if refresh_idx != -1: doors_refresh_option.selected = refresh_idx var goals_idx = [5, 8, 12].find(LobbyManager.doors_required_goals) if goals_idx != -1: doors_goals_option.selected = goals_idx func _update_random_spawn_label(enabled: bool) -> void: if random_spawn_label: random_spawn_label.text = "Random ✓" if enabled else "Random ✗" func _on_profile_btn_pressed() -> void: 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()) add_child(profile_panel_instance) profile_panel_instance.show_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 _on_quit_pressed() -> void: print("[Lobby] Quitting game...") get_tree().quit() func _on_settings_pressed(): var settings_menu = get_node_or_null("SettingsMenu") if not settings_menu: var scene = load("res://scenes/ui/settings_menu.tscn") if scene: settings_menu = scene.instantiate() settings_menu.name = "SettingsMenu" add_child(settings_menu) # Connect close button var close_btn = settings_menu.get_node_or_null("PanelContainer/VBoxContainer/Header/CloseButton") if close_btn: # settings_menu.gd handles basic visibility, but we can override or add to it pass if settings_menu: settings_menu.open() func _on_leaderboard_pressed() -> void: if not leaderboard_panel_instance: var leaderboard_panel_scene := load("res://scenes/ui/leaderboard_panel.tscn") if leaderboard_panel_scene: leaderboard_panel_instance = leaderboard_panel_scene.instantiate() leaderboard_panel_instance.closed.connect(func(): leaderboard_panel_instance.hide()) add_child(leaderboard_panel_instance) if leaderboard_panel_instance: leaderboard_panel_instance.show_panel() # Center it and apply some offset if needed func _go_to_login() -> void: if get_tree(): get_tree().change_scene_to_file("res://scenes/ui/login_screen.tscn") # ============================================================================= # 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", "") match_id_display.text = "ID: %s" % _truncate_id(current_match_id) # Configure host-specific UI var is_host = LobbyManager.is_host host_banner.visible = is_host start_game_btn.visible = is_host _update_settings_visibility() # Update values from LobbyManager _on_match_duration_changed(LobbyManager.match_duration) _on_randomize_spawn_changed(LobbyManager.randomize_spawn) _on_enable_cycle_timer_changed(LobbyManager.enable_cycle_timer) # Scarcity Update _on_scarcity_mode_changed(LobbyManager.scarcity_mode) # Initial UI sync for custom modes _on_sng_update() _on_doors_update() # Area selector: only host can interact area_left_btn.disabled = not is_host area_right_btn.disabled = not is_host area_name_label.text = LobbyManager.get_selected_area() # Game Mode Update if game_mode_option: game_mode_option.visible = is_host if game_mode_text_label: game_mode_text_label.visible = not is_host _on_game_mode_changed(LobbyManager.game_mode) _update_player_slots() connection_status.text = "Connected to room" func _on_room_left() -> void: _show_panel("main_menu") 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") func _on_player_joined(player_data: Dictionary) -> void: _update_player_slots() status_label.text = "%s joined!" % player_data.get("name", "Player") func _on_player_left(_player_id: int) -> void: _update_player_slots() status_label.text = "A player left" func _on_ready_state_changed(_player_id: int, _is_ready: bool) -> void: _update_player_slots() _update_status() func _on_all_players_ready() -> void: if LobbyManager.is_host: if LobbyManager.is_lan_mode and LobbyManager.players_in_room.size() == 1: # Auto-start for solo LAN testing LobbyManager.start_game() else: start_game_btn.disabled = false status_label.text = "All ready! Start the match!" else: status_label.text = "All ready! Waiting for host..." func _on_game_starting() -> void: connection_status.text = "Starting game..." # 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: _update_duration_text_label(duration_seconds) func _on_randomize_spawn_changed(enabled: bool) -> void: if random_spawn_check: random_spawn_check.set_pressed_no_signal(enabled) if random_spawn_label: random_spawn_label.text = "Random \u2713" if enabled else "Random \u2717" func _on_enable_cycle_timer_changed(enabled: bool) -> void: if enable_timer_check: enable_timer_check.set_pressed_no_signal(enabled) if enable_timer_label: enable_timer_label.text = "Timer \u2713" if enabled else "Timer \u2717" func _on_character_changed(_player_id: int, _character_name: String) -> void: _update_player_slots() func _on_area_changed(area_name: String) -> void: area_name_label.text = area_name 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") func _on_profile_updated() -> void: """Handle profile updates (name/avatar change).""" var new_name = UserProfileManager.get_display_name() # Sync to LobbyManager if we are in a room or just locally LobbyManager.set_player_name(new_name) # ============================================================================= # Player Slot Updates # ============================================================================= func _update_player_slots() -> void: """Update all player slot visuals based on current player list.""" if not multiplayer.has_multiplayer_peer(): return var players = LobbyManager.get_players() var my_id = multiplayer.get_unique_id() for i in range(player_slots.size()): var slot = player_slots[i] var slot_num = i + 1 if i < players.size(): var player = players[i] slot.visible = true # Update player name var name_label = slot.get_node_or_null("PlayerName%d" % slot_num) if name_label: var display_name = player.get("name", "Player %d" % slot_num) if player.get("id") == 1: display_name += " (Host)" name_label.text = display_name # Update character preview var char_preview = slot.get_node_or_null("CharacterPreview%d" % slot_num) var char_name = player.get("character", "Bob") if char_preview and character_textures.has(char_name): char_preview.texture = character_textures[char_name] # Check if this is the local player var is_local_player = player.get("id") == my_id # Update character name in nav (inside CharacterNav) var char_name_in_nav = slot.get_node_or_null("CharacterNav%d/CharacterName%d" % [slot_num, slot_num]) if char_name_in_nav: char_name_in_nav.text = char_name # Update character name label (outside nav, for non-local players) var char_name_label = slot.get_node_or_null("CharacterNameLabel%d" % slot_num) if char_name_label: char_name_label.text = char_name char_name_label.visible = not is_local_player # Show/hide character navigation (only for local player) var char_nav = slot.get_node_or_null("CharacterNav%d" % slot_num) if char_nav: char_nav.visible = is_local_player # Update ready status var ready_label = slot.get_node_or_null("ReadyStatus%d" % slot_num) if ready_label: var is_ready = player.get("is_ready", false) ready_label.text = "READY ✓" if is_ready else "NOT READY" ready_label.add_theme_color_override("font_color", Color(0.4, 0.8, 0.4) if is_ready else Color(0.6, 0.6, 0.6)) else: # Empty slot - show as bot placeholder slot.visible = true # Update name to show as bot var name_label = slot.get_node_or_null("PlayerName%d" % slot_num) if name_label: name_label.text = "🤖 Bot %d" % slot_num # Use a character for bot preview var char_preview = slot.get_node_or_null("CharacterPreview%d" % slot_num) var bot_characters = ["Copper", "Dabro", "Gatot", "Pip"] var bot_char = bot_characters[(i) % bot_characters.size()] if char_preview and character_textures.has(bot_char): char_preview.texture = character_textures[bot_char] # Hide character navigation for bots var char_nav = slot.get_node_or_null("CharacterNav%d" % slot_num) if char_nav: char_nav.visible = false # Update character name label var char_name_label = slot.get_node_or_null("CharacterNameLabel%d" % slot_num) if char_name_label: char_name_label.text = bot_char char_name_label.visible = true # Show ready status as waiting var ready_label = slot.get_node_or_null("ReadyStatus%d" % slot_num) if ready_label: ready_label.text = "WAITING..." ready_label.add_theme_color_override("font_color", Color(0.5, 0.5, 0.7)) 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() # ============================================================================= # Helper Functions # ============================================================================= func _update_duration_text_label(duration_seconds: int) -> void: var duration_text: String match duration_seconds: 60: duration_text = "1 min" 120: duration_text = "2 min" 180: duration_text = "3 min" 300: duration_text = "5 min" 600: duration_text = "10 min" _: duration_text = "%d sec" % duration_seconds duration_text_label.text = duration_text func _truncate_id(id: String) -> String: if id.length() > 16: return id.substr(0, 8) + "..." + id.substr(-4) return id