436 lines
16 KiB
GDScript
436 lines
16 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 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
|
|
|
|
# UI References - Player Slots
|
|
@onready var players_container = $LobbyPanel/PlayersContainer
|
|
@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 = ""
|
|
|
|
func _ready():
|
|
# Check if user is authenticated
|
|
if not AuthManager.is_logged_in():
|
|
call_deferred("_go_to_login")
|
|
return
|
|
|
|
# Load character textures
|
|
_load_character_textures()
|
|
|
|
# Get player slot references
|
|
_setup_player_slots()
|
|
|
|
# Set player name from profile
|
|
if player_name_input:
|
|
player_name_input.text = 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)
|
|
|
|
# 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)
|
|
|
|
# Connect button signals - Lobby
|
|
profile_btn.pressed.connect(_on_profile_btn_pressed)
|
|
logout_btn.pressed.connect(_on_logout_pressed)
|
|
copy_id_btn.pressed.connect(_on_copy_id_pressed)
|
|
duration_option.item_selected.connect(_on_duration_selected)
|
|
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)
|
|
|
|
# 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)
|
|
LobbyManager.match_duration_changed.connect(_on_match_duration_changed)
|
|
LobbyManager.character_changed.connect(_on_character_changed)
|
|
LobbyManager.area_changed.connect(_on_area_changed)
|
|
LobbyManager.player_list_changed.connect(_update_player_slots)
|
|
|
|
# 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")
|
|
|
|
# =============================================================================
|
|
# Setup
|
|
# =============================================================================
|
|
|
|
func _load_character_textures() -> void:
|
|
"""Load character expression textures for preview."""
|
|
var characters = ["Bob", "Gatot", "Masbro", "Oldpop"]
|
|
for char_name in characters:
|
|
var tex_path = "res://assets/characters/%s_%s-expression.png" % [char_name, char_name.to_lower()]
|
|
if ResourceLoader.exists(tex_path):
|
|
character_textures[char_name] = load(tex_path)
|
|
else:
|
|
print("[Lobby] Character texture not found: ", tex_path)
|
|
|
|
func _setup_player_slots() -> void:
|
|
"""Get references to all player slot nodes."""
|
|
player_slots.clear()
|
|
for i in range(1, 5):
|
|
var slot = players_container.get_node_or_null("PlayerSlot%d" % i)
|
|
if slot:
|
|
player_slots.append(slot)
|
|
# 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:
|
|
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..."
|
|
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:
|
|
var match_id = match_id_input.text.strip_edges()
|
|
|
|
if match_id.is_empty():
|
|
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
|
|
|
|
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)
|
|
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_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 _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", "")
|
|
room_name_header.text = "ROOM: %s" % room_data.get("room_name", "Unknown")
|
|
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
|
|
|
|
# Duration: host sees dropdown, clients see text
|
|
duration_option.visible = is_host
|
|
duration_text_label.visible = not is_host
|
|
if not is_host:
|
|
_update_duration_text_label(LobbyManager.get_match_duration())
|
|
|
|
# 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()
|
|
|
|
_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_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:
|
|
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..."
|
|
await get_tree().create_timer(0.5).timeout
|
|
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_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")
|
|
|
|
# =============================================================================
|
|
# Player Slot Updates
|
|
# =============================================================================
|
|
|
|
func _update_player_slots() -> void:
|
|
"""Update all player slot visuals based on current player list."""
|
|
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:
|
|
slot.visible = false
|
|
|
|
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
|