class_name LobbyRoom extends RefCounted var lobby: Control var _bot_names: Dictionary = {} func _init(p_lobby: Control): lobby = p_lobby if lobby.copy_id_btn: lobby.copy_id_btn.pressed.connect(_on_copy_id_pressed) if lobby.duration_option: lobby.duration_option.item_selected.connect(_on_duration_selected) if lobby.random_spawn_check: lobby.random_spawn_check.toggled.connect(_on_random_spawn_toggled) if lobby.enable_timer_check: lobby.enable_timer_check.toggled.connect(_on_enable_timer_toggled) if lobby.area_left_btn: lobby.area_left_btn.pressed.connect(func(): LobbyManager.cycle_area(-1)) if lobby.area_right_btn: lobby.area_right_btn.pressed.connect(func(): LobbyManager.cycle_area(1)) if lobby.leave_btn: lobby.leave_btn.pressed.connect(_on_leave_pressed) if lobby.ready_btn: lobby.ready_btn.toggled.connect(_on_ready_toggled) if lobby.start_game_btn: lobby.start_game_btn.pressed.connect(_on_start_game_pressed) if lobby.scarcity_option: lobby.scarcity_option.item_selected.connect(_on_scarcity_selected) if lobby.game_mode_option: lobby.game_mode_option.item_selected.connect(_on_game_mode_selected) # Connect LobbyManager signals 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) FriendManager.lobby_invite_received.connect(_on_lobby_invite_received) # ============================================================================= # Button Handlers # ============================================================================= func _on_ready_toggled(is_ready: bool) -> void: LobbyManager.set_ready(is_ready) if lobby.ready_btn: lobby.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() lobby._show_panel("main_menu") if lobby.ready_btn: lobby.ready_btn.button_pressed = false lobby.ready_btn.text = "READY" for key in _bot_names.keys(): NameGenerator.release_bot_name(_bot_names[key]) _bot_names.clear() func _on_copy_id_pressed() -> void: DisplayServer.clipboard_set(lobby.current_match_id) if lobby.status_label: lobby.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 = lobby.scarcity_option.get_item_text(index) LobbyManager.set_scarcity_mode(mode) func _on_scarcity_mode_changed(mode: String) -> void: if lobby.scarcity_option: for i in range(lobby.scarcity_option.item_count): if lobby.scarcity_option.get_item_text(i) == mode: lobby.scarcity_option.selected = i break if lobby.scarcity_label: lobby.scarcity_label.text = mode func _on_game_mode_selected(index: int) -> void: if not LobbyManager.is_host: return var mode = lobby.game_mode_option.get_item_text(index) LobbyManager.set_game_mode(mode) func _on_game_mode_changed(mode: String) -> void: if lobby.game_mode_option: for i in range(lobby.game_mode_option.item_count): if lobby.game_mode_option.get_item_text(i) == mode: lobby.game_mode_option.selected = i break if lobby.game_mode_text_label: lobby.game_mode_text_label.text = mode lobby._update_settings_visibility() func _on_sng_update(_val: int = 0) -> void: if not lobby.sng_go_option: return var go_idx = [10, 15, 25].find(LobbyManager.sng_go_duration) if go_idx != -1: lobby.sng_go_option.selected = go_idx var stop_idx = [3, 4, 5].find(LobbyManager.sng_stop_duration) if stop_idx != -1: lobby.sng_stop_option.selected = stop_idx var goals_idx = [5, 8, 12].find(LobbyManager.sng_required_goals) if goals_idx != -1: lobby.sng_goals_option.selected = goals_idx func _on_doors_update(_val: int = 0) -> void: if not lobby.doors_swap_option: return var swap_idx = [10, 15, 30].find(LobbyManager.doors_swap_time) if swap_idx != -1: lobby.doors_swap_option.selected = swap_idx var refresh_idx = [15, 25, 40].find(LobbyManager.doors_refresh_time) if refresh_idx != -1: lobby.doors_refresh_option.selected = refresh_idx var goals_idx = [5, 8, 12].find(LobbyManager.doors_required_goals) if goals_idx != -1: lobby.doors_goals_option.selected = goals_idx # ============================================================================= # LobbyManager Signal Handlers # ============================================================================= func _on_room_joined(room_data: Dictionary) -> void: lobby._show_panel("lobby") lobby.current_match_id = room_data.get("match_id", "") if lobby.match_id_display: lobby.match_id_display.text = "ID: %s" % _truncate_id(lobby.current_match_id) var is_host = LobbyManager.is_host if lobby.host_banner: lobby.host_banner.visible = is_host if lobby.start_game_btn: lobby.start_game_btn.visible = is_host lobby._update_settings_visibility() _on_match_duration_changed(LobbyManager.match_duration) _on_randomize_spawn_changed(LobbyManager.randomize_spawn) _on_enable_cycle_timer_changed(LobbyManager.enable_cycle_timer) _on_scarcity_mode_changed(LobbyManager.scarcity_mode) _on_sng_update() _on_doors_update() if lobby.area_left_btn: lobby.area_left_btn.disabled = not is_host if lobby.area_right_btn: lobby.area_right_btn.disabled = not is_host if lobby.area_name_label: lobby.area_name_label.text = LobbyManager.get_selected_area() if lobby.game_mode_option: lobby.game_mode_option.visible = is_host if lobby.game_mode_text_label: lobby.game_mode_text_label.visible = not is_host _on_game_mode_changed(LobbyManager.game_mode) _update_player_slots() if lobby.connection_status: lobby.connection_status.text = "Connected to room" func _on_room_left() -> void: lobby._show_panel("main_menu") if lobby.connection_status: lobby.connection_status.text = "Left room" for key in _bot_names.keys(): NameGenerator.release_bot_name(_bot_names[key]) _bot_names.clear() func _on_host_disconnected() -> void: if lobby.connection_status: lobby.connection_status.text = "Host disconnected. Returning to menu..." lobby._show_panel("main_menu") func _on_player_joined(player_data: Dictionary) -> void: _update_player_slots() if lobby.status_label: lobby.status_label.text = "%s joined!" % player_data.get("name", "Player") func _on_player_left(_player_id: int) -> void: _update_player_slots() if lobby.status_label: lobby.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 lobby.start_game_btn: lobby.start_game_btn.disabled = false if lobby.status_label: lobby.status_label.text = "All ready! Start the match!" else: if lobby.status_label: lobby.status_label.text = "All ready! Waiting for host..." func _on_game_starting() -> void: if lobby.connection_status: lobby.connection_status.text = "Starting game..." var loading_screen_scene = load("res://scenes/loading_screen/loading_screen.tscn") if loading_screen_scene: var loading_screen = loading_screen_scene.instantiate() lobby.get_tree().root.add_child(loading_screen) loading_screen.load_level("res://scenes/main.tscn") else: lobby.get_tree().change_scene_to_file("res://scenes/main.tscn") func _on_match_duration_changed(duration_seconds: int) -> void: if not LobbyManager.is_host: 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 if lobby.duration_text_label: lobby.duration_text_label.text = duration_text func _on_randomize_spawn_changed(enabled: bool) -> void: if lobby.random_spawn_check: lobby.random_spawn_check.set_pressed_no_signal(enabled) if lobby.random_spawn_label: lobby.random_spawn_label.text = "Random \u2713" if enabled else "Random \u2717" func _on_enable_cycle_timer_changed(enabled: bool) -> void: if lobby.enable_timer_check: lobby.enable_timer_check.set_pressed_no_signal(enabled) if lobby.enable_timer_label: lobby.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: if lobby.area_name_label: lobby.area_name_label.text = area_name func _update_player_slots() -> void: if not lobby.multiplayer.has_multiplayer_peer(): return var players = LobbyManager.get_players() var my_id = lobby.multiplayer.get_unique_id() for i in range(lobby.player_slots.size()): var slot = lobby.player_slots[i] var slot_num = i + 1 if i < players.size(): var player = players[i] slot.visible = true 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 var char_preview = slot.get_node_or_null("CharacterPreview%d" % slot_num) var char_name = player.get("character", "Bob") if char_preview and lobby.character_textures.has(char_name): char_preview.texture = lobby.character_textures[char_name] var is_local_player = player.get("id") == my_id 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 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 var char_nav = slot.get_node_or_null("CharacterNav%d" % slot_num) if char_nav: char_nav.visible = is_local_player 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)) var player_nakama_id: String = player.get("nakama_id", "") var my_nakama_id: String = NakamaManager.session.user_id if NakamaManager.session else "" var add_friend_btn: Button = slot.get_node_or_null("AddFriendBtn%d" % slot_num) if add_friend_btn: if player_nakama_id.is_empty() or player_nakama_id == my_nakama_id: add_friend_btn.visible = false else: add_friend_btn.visible = true if FriendManager.is_friend(player_nakama_id): add_friend_btn.text = "Friend ✓" add_friend_btn.disabled = true else: add_friend_btn.text = "+ Friend" add_friend_btn.disabled = false if not add_friend_btn.pressed.is_connected(func(): _on_add_friend_pressed(player_nakama_id)): add_friend_btn.pressed.connect(func(): _on_add_friend_pressed(player_nakama_id)) else: slot.visible = true if not _bot_names.has(i): _bot_names[i] = NameGenerator.generate_bot_name() var bot_display_name: String = _bot_names[i] var name_label = slot.get_node_or_null("PlayerName%d" % slot_num) if name_label: name_label.text = "🤖 " + bot_display_name 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 lobby.character_textures.has(bot_char): char_preview.texture = lobby.character_textures[bot_char] var char_nav = slot.get_node_or_null("CharacterNav%d" % slot_num) if char_nav: char_nav.visible = false 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 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)) var add_friend_btn: Button = slot.get_node_or_null("AddFriendBtn%d" % slot_num) if add_friend_btn: add_friend_btn.visible = false _update_status() 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 if lobby.status_label: lobby.status_label.text = "Ready: %d/%d" % [ready_count, players.size()] if LobbyManager.is_host: if lobby.start_game_btn: lobby.start_game_btn.disabled = not LobbyManager.is_all_ready() func _truncate_id(id: String) -> String: if id.length() > 16: return id.substr(0, 8) + "..." + id.substr(-4) return id # ============================================================================= # Social / Friend Functions # ============================================================================= func _on_add_friend_pressed(nakama_id: String) -> void: var ok = await FriendManager.add_friend_by_id(nakama_id) if ok: _update_player_slots() func on_invite_friends_pressed() -> void: var match_id = lobby.current_match_id if match_id.is_empty(): return var friends = FriendManager.get_mutual_friends() var scene = load("res://scenes/ui/invite_friends_dialog.tscn") as PackedScene if not scene: return var dialog = scene.instantiate() lobby.add_child(dialog) dialog.open(friends, match_id) dialog.closed.connect(dialog.queue_free) func _on_lobby_invite_received(from_user_id: String, from_name: String, match_id: String) -> void: if lobby.get_tree().current_scene.scene_file_path != "res://scenes/lobby.tscn": return if lobby.lobby_panel and lobby.lobby_panel.visible: return if lobby._invite_popup: lobby._invite_popup.queue_free() lobby._pending_invite_match_id = match_id var scene = load("res://scenes/ui/lobby_invite_popup.tscn") as PackedScene if scene: lobby._invite_popup = scene.instantiate() lobby.add_child(lobby._invite_popup) lobby._invite_popup.setup(from_name) lobby._invite_popup.accepted.connect(_on_invite_accepted) lobby._invite_popup.declined.connect(func(): lobby._invite_popup.queue_free()) lobby._invite_popup.popup_centered() else: var dlg := AcceptDialog.new() dlg.title = "Lobby Invitation" dlg.dialog_text = "%s invited you!\nJoin?" % from_name dlg.ok_button_text = "Join" dlg.add_cancel_button("Decline") lobby.add_child(dlg) dlg.confirmed.connect(_on_invite_accepted) dlg.canceled.connect(dlg.queue_free) dlg.popup_centered() lobby._invite_popup = dlg func _on_invite_accepted() -> void: if not lobby._pending_invite_match_id.is_empty(): LobbyManager.join_room(lobby._pending_invite_match_id) if lobby._invite_popup: lobby._invite_popup.queue_free() lobby._pending_invite_match_id = ""