Files
tekton/scenes/lobby.gd
T
2025-12-10 23:11:59 +08:00

403 lines
14 KiB
GDScript

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()