662 lines
22 KiB
GDScript
662 lines
22 KiB
GDScript
class_name OnlineLobby
|
|
extends CanvasLayer
|
|
|
|
## OnlineLobby - Matchmaking UI for ranked queue and private rooms
|
|
|
|
signal game_starting(game_data: Dictionary)
|
|
signal back_pressed
|
|
signal profile_requested
|
|
signal leaderboard_requested
|
|
|
|
# Window dimensions
|
|
const WINDOW_SIZE := Vector2i(600, 700)
|
|
|
|
# UI Components
|
|
var main_container: VBoxContainer
|
|
var back_button: Button
|
|
var header_container: HBoxContainer
|
|
var username_label: Label
|
|
var elo_label: Label
|
|
var deck_section: VBoxContainer
|
|
var deck_dropdown: OptionButton
|
|
var ranked_section: PanelContainer
|
|
var ranked_content: VBoxContainer
|
|
var find_match_button: Button
|
|
var cancel_search_button: Button
|
|
var queue_status_label: Label
|
|
var private_section: PanelContainer
|
|
var private_content: VBoxContainer
|
|
var create_room_button: Button
|
|
var join_container: HBoxContainer
|
|
var room_code_input: LineEdit
|
|
var join_room_button: Button
|
|
var room_section: PanelContainer
|
|
var room_content: VBoxContainer
|
|
var room_code_label: Label
|
|
var copy_code_button: Button
|
|
var host_label: Label
|
|
var guest_label: Label
|
|
var ready_button: Button
|
|
var leave_room_button: Button
|
|
var error_label: Label
|
|
var nav_buttons_container: HBoxContainer
|
|
var profile_button: Button
|
|
var leaderboard_button: Button
|
|
|
|
# State
|
|
var is_in_queue: bool = false
|
|
var is_in_room: bool = false
|
|
var is_ready: bool = false
|
|
var queue_start_time: float = 0.0
|
|
var current_room_code: String = ""
|
|
var selected_deck_id: String = ""
|
|
|
|
# Styling
|
|
var custom_font: Font = preload("res://JimNightshade-Regular.ttf")
|
|
const BG_COLOR := Color(0.12, 0.11, 0.15, 1.0)
|
|
const PANEL_COLOR := Color(0.18, 0.16, 0.22, 1.0)
|
|
const ACCENT_COLOR := Color(0.4, 0.35, 0.55, 1.0)
|
|
const TEXT_COLOR := Color(0.9, 0.88, 0.82, 1.0)
|
|
const MUTED_COLOR := Color(0.6, 0.58, 0.52, 1.0)
|
|
|
|
|
|
func _ready() -> void:
|
|
_create_ui()
|
|
_connect_network_signals()
|
|
_update_user_info()
|
|
_fetch_decks()
|
|
|
|
|
|
func _process(_delta: float) -> void:
|
|
# Update queue timer if searching
|
|
if is_in_queue:
|
|
var elapsed = Time.get_ticks_msec() / 1000.0 - queue_start_time
|
|
var minutes = int(elapsed) / 60
|
|
var seconds = int(elapsed) % 60
|
|
queue_status_label.text = "Searching... %d:%02d" % [minutes, seconds]
|
|
|
|
|
|
func _create_ui() -> void:
|
|
# Background
|
|
var bg = ColorRect.new()
|
|
bg.color = BG_COLOR
|
|
bg.set_anchors_preset(Control.PRESET_FULL_RECT)
|
|
add_child(bg)
|
|
|
|
# Main container
|
|
main_container = VBoxContainer.new()
|
|
main_container.set_anchors_preset(Control.PRESET_FULL_RECT)
|
|
main_container.add_theme_constant_override("separation", 16)
|
|
add_child(main_container)
|
|
|
|
var margin = MarginContainer.new()
|
|
margin.add_theme_constant_override("margin_left", 24)
|
|
margin.add_theme_constant_override("margin_right", 24)
|
|
margin.add_theme_constant_override("margin_top", 16)
|
|
margin.add_theme_constant_override("margin_bottom", 16)
|
|
margin.set_anchors_preset(Control.PRESET_FULL_RECT)
|
|
main_container.add_child(margin)
|
|
|
|
var content = VBoxContainer.new()
|
|
content.add_theme_constant_override("separation", 16)
|
|
margin.add_child(content)
|
|
|
|
# Back button
|
|
back_button = _create_button("< Back", false)
|
|
back_button.custom_minimum_size = Vector2(80, 32)
|
|
back_button.pressed.connect(_on_back_pressed)
|
|
content.add_child(back_button)
|
|
|
|
# Header with username and ELO
|
|
header_container = HBoxContainer.new()
|
|
header_container.add_theme_constant_override("separation", 16)
|
|
content.add_child(header_container)
|
|
|
|
username_label = Label.new()
|
|
username_label.add_theme_font_override("font", custom_font)
|
|
username_label.add_theme_font_size_override("font_size", 24)
|
|
username_label.add_theme_color_override("font_color", TEXT_COLOR)
|
|
username_label.text = "Welcome!"
|
|
username_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
header_container.add_child(username_label)
|
|
|
|
elo_label = Label.new()
|
|
elo_label.add_theme_font_override("font", custom_font)
|
|
elo_label.add_theme_font_size_override("font_size", 20)
|
|
elo_label.add_theme_color_override("font_color", ACCENT_COLOR)
|
|
elo_label.text = "ELO: 1000"
|
|
header_container.add_child(elo_label)
|
|
|
|
# Deck selection section
|
|
deck_section = VBoxContainer.new()
|
|
deck_section.add_theme_constant_override("separation", 8)
|
|
content.add_child(deck_section)
|
|
|
|
var deck_label = Label.new()
|
|
deck_label.add_theme_font_override("font", custom_font)
|
|
deck_label.add_theme_font_size_override("font_size", 16)
|
|
deck_label.add_theme_color_override("font_color", MUTED_COLOR)
|
|
deck_label.text = "SELECT DECK"
|
|
deck_section.add_child(deck_label)
|
|
|
|
deck_dropdown = OptionButton.new()
|
|
deck_dropdown.add_theme_font_override("font", custom_font)
|
|
deck_dropdown.add_theme_font_size_override("font_size", 16)
|
|
deck_dropdown.custom_minimum_size = Vector2(0, 40)
|
|
deck_dropdown.item_selected.connect(_on_deck_selected)
|
|
deck_section.add_child(deck_dropdown)
|
|
|
|
# Ranked match section
|
|
ranked_section = _create_panel_section("RANKED MATCH")
|
|
content.add_child(ranked_section)
|
|
|
|
ranked_content = ranked_section.get_child(0) as VBoxContainer
|
|
|
|
var ranked_desc = Label.new()
|
|
ranked_desc.add_theme_font_override("font", custom_font)
|
|
ranked_desc.add_theme_font_size_override("font_size", 14)
|
|
ranked_desc.add_theme_color_override("font_color", MUTED_COLOR)
|
|
ranked_desc.text = "Find opponents near your skill level"
|
|
ranked_content.add_child(ranked_desc)
|
|
|
|
find_match_button = _create_button("Find Match", true)
|
|
find_match_button.pressed.connect(_on_find_match_pressed)
|
|
ranked_content.add_child(find_match_button)
|
|
|
|
cancel_search_button = _create_button("Cancel Search", false)
|
|
cancel_search_button.pressed.connect(_on_cancel_search_pressed)
|
|
cancel_search_button.visible = false
|
|
ranked_content.add_child(cancel_search_button)
|
|
|
|
queue_status_label = Label.new()
|
|
queue_status_label.add_theme_font_override("font", custom_font)
|
|
queue_status_label.add_theme_font_size_override("font_size", 14)
|
|
queue_status_label.add_theme_color_override("font_color", ACCENT_COLOR)
|
|
queue_status_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
|
queue_status_label.visible = false
|
|
ranked_content.add_child(queue_status_label)
|
|
|
|
# Private match section
|
|
private_section = _create_panel_section("PRIVATE MATCH")
|
|
content.add_child(private_section)
|
|
|
|
private_content = private_section.get_child(0) as VBoxContainer
|
|
|
|
create_room_button = _create_button("Create Room", true)
|
|
create_room_button.pressed.connect(_on_create_room_pressed)
|
|
private_content.add_child(create_room_button)
|
|
|
|
var separator_label = Label.new()
|
|
separator_label.add_theme_font_override("font", custom_font)
|
|
separator_label.add_theme_font_size_override("font_size", 12)
|
|
separator_label.add_theme_color_override("font_color", MUTED_COLOR)
|
|
separator_label.text = "─────────── OR ───────────"
|
|
separator_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
|
private_content.add_child(separator_label)
|
|
|
|
join_container = HBoxContainer.new()
|
|
join_container.add_theme_constant_override("separation", 8)
|
|
private_content.add_child(join_container)
|
|
|
|
var code_label = Label.new()
|
|
code_label.add_theme_font_override("font", custom_font)
|
|
code_label.add_theme_font_size_override("font_size", 14)
|
|
code_label.add_theme_color_override("font_color", TEXT_COLOR)
|
|
code_label.text = "Code:"
|
|
join_container.add_child(code_label)
|
|
|
|
room_code_input = LineEdit.new()
|
|
room_code_input.add_theme_font_override("font", custom_font)
|
|
room_code_input.add_theme_font_size_override("font_size", 16)
|
|
room_code_input.placeholder_text = "ABC123"
|
|
room_code_input.max_length = 6
|
|
room_code_input.custom_minimum_size = Vector2(100, 36)
|
|
room_code_input.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
room_code_input.text_changed.connect(_on_room_code_changed)
|
|
join_container.add_child(room_code_input)
|
|
|
|
join_room_button = _create_button("Join", false)
|
|
join_room_button.custom_minimum_size = Vector2(80, 36)
|
|
join_room_button.pressed.connect(_on_join_room_pressed)
|
|
join_container.add_child(join_room_button)
|
|
|
|
# Room section (shown when in a room)
|
|
room_section = _create_panel_section("ROOM")
|
|
room_section.visible = false
|
|
content.add_child(room_section)
|
|
|
|
room_content = room_section.get_child(0) as VBoxContainer
|
|
|
|
var room_header = HBoxContainer.new()
|
|
room_header.add_theme_constant_override("separation", 8)
|
|
room_content.add_child(room_header)
|
|
|
|
room_code_label = Label.new()
|
|
room_code_label.add_theme_font_override("font", custom_font)
|
|
room_code_label.add_theme_font_size_override("font_size", 20)
|
|
room_code_label.add_theme_color_override("font_color", ACCENT_COLOR)
|
|
room_code_label.text = "Room: ------"
|
|
room_code_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
room_header.add_child(room_code_label)
|
|
|
|
copy_code_button = _create_button("Copy", false)
|
|
copy_code_button.custom_minimum_size = Vector2(60, 28)
|
|
copy_code_button.pressed.connect(_on_copy_code_pressed)
|
|
room_header.add_child(copy_code_button)
|
|
|
|
var players_container = VBoxContainer.new()
|
|
players_container.add_theme_constant_override("separation", 4)
|
|
room_content.add_child(players_container)
|
|
|
|
host_label = Label.new()
|
|
host_label.add_theme_font_override("font", custom_font)
|
|
host_label.add_theme_font_size_override("font_size", 16)
|
|
host_label.add_theme_color_override("font_color", TEXT_COLOR)
|
|
host_label.text = "Host: ---"
|
|
players_container.add_child(host_label)
|
|
|
|
guest_label = Label.new()
|
|
guest_label.add_theme_font_override("font", custom_font)
|
|
guest_label.add_theme_font_size_override("font_size", 16)
|
|
guest_label.add_theme_color_override("font_color", TEXT_COLOR)
|
|
guest_label.text = "Guest: Waiting..."
|
|
players_container.add_child(guest_label)
|
|
|
|
var room_buttons = HBoxContainer.new()
|
|
room_buttons.add_theme_constant_override("separation", 8)
|
|
room_content.add_child(room_buttons)
|
|
|
|
ready_button = _create_button("Ready", true)
|
|
ready_button.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
ready_button.pressed.connect(_on_ready_pressed)
|
|
room_buttons.add_child(ready_button)
|
|
|
|
leave_room_button = _create_button("Leave Room", false)
|
|
leave_room_button.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
leave_room_button.pressed.connect(_on_leave_room_pressed)
|
|
room_buttons.add_child(leave_room_button)
|
|
|
|
# Error label
|
|
error_label = Label.new()
|
|
error_label.add_theme_font_override("font", custom_font)
|
|
error_label.add_theme_font_size_override("font_size", 14)
|
|
error_label.add_theme_color_override("font_color", Color(0.9, 0.3, 0.3))
|
|
error_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
|
error_label.autowrap_mode = TextServer.AUTOWRAP_WORD
|
|
error_label.visible = false
|
|
content.add_child(error_label)
|
|
|
|
# Navigation buttons (Profile and Leaderboard)
|
|
nav_buttons_container = HBoxContainer.new()
|
|
nav_buttons_container.add_theme_constant_override("separation", 16)
|
|
nav_buttons_container.alignment = BoxContainer.ALIGNMENT_CENTER
|
|
content.add_child(nav_buttons_container)
|
|
|
|
profile_button = _create_button("Profile", false)
|
|
profile_button.custom_minimum_size = Vector2(120, 36)
|
|
profile_button.pressed.connect(_on_profile_pressed)
|
|
nav_buttons_container.add_child(profile_button)
|
|
|
|
leaderboard_button = _create_button("Leaderboard", false)
|
|
leaderboard_button.custom_minimum_size = Vector2(120, 36)
|
|
leaderboard_button.pressed.connect(_on_leaderboard_pressed)
|
|
nav_buttons_container.add_child(leaderboard_button)
|
|
|
|
|
|
func _create_panel_section(title: String) -> PanelContainer:
|
|
var panel = PanelContainer.new()
|
|
var style = StyleBoxFlat.new()
|
|
style.bg_color = PANEL_COLOR
|
|
style.set_corner_radius_all(8)
|
|
style.set_content_margin_all(16)
|
|
panel.add_theme_stylebox_override("panel", style)
|
|
|
|
var vbox = VBoxContainer.new()
|
|
vbox.add_theme_constant_override("separation", 12)
|
|
panel.add_child(vbox)
|
|
|
|
var title_label = Label.new()
|
|
title_label.add_theme_font_override("font", custom_font)
|
|
title_label.add_theme_font_size_override("font_size", 18)
|
|
title_label.add_theme_color_override("font_color", TEXT_COLOR)
|
|
title_label.text = title
|
|
title_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
|
vbox.add_child(title_label)
|
|
|
|
return panel
|
|
|
|
|
|
func _create_button(text: String, primary: bool) -> Button:
|
|
var button = Button.new()
|
|
button.text = text
|
|
button.add_theme_font_override("font", custom_font)
|
|
button.add_theme_font_size_override("font_size", 16)
|
|
button.custom_minimum_size = Vector2(0, 40)
|
|
|
|
var normal = StyleBoxFlat.new()
|
|
var hover = StyleBoxFlat.new()
|
|
var pressed = StyleBoxFlat.new()
|
|
var disabled = StyleBoxFlat.new()
|
|
|
|
if primary:
|
|
normal.bg_color = ACCENT_COLOR
|
|
hover.bg_color = ACCENT_COLOR.lightened(0.15)
|
|
pressed.bg_color = ACCENT_COLOR.darkened(0.15)
|
|
button.add_theme_color_override("font_color", Color.WHITE)
|
|
button.add_theme_color_override("font_hover_color", Color.WHITE)
|
|
else:
|
|
normal.bg_color = Color(0.25, 0.23, 0.3)
|
|
hover.bg_color = Color(0.3, 0.28, 0.35)
|
|
pressed.bg_color = Color(0.2, 0.18, 0.25)
|
|
button.add_theme_color_override("font_color", TEXT_COLOR)
|
|
button.add_theme_color_override("font_hover_color", TEXT_COLOR)
|
|
|
|
disabled.bg_color = Color(0.2, 0.18, 0.22)
|
|
button.add_theme_color_override("font_disabled_color", MUTED_COLOR)
|
|
|
|
for style in [normal, hover, pressed, disabled]:
|
|
style.set_corner_radius_all(6)
|
|
style.set_content_margin_all(8)
|
|
|
|
button.add_theme_stylebox_override("normal", normal)
|
|
button.add_theme_stylebox_override("hover", hover)
|
|
button.add_theme_stylebox_override("pressed", pressed)
|
|
button.add_theme_stylebox_override("disabled", disabled)
|
|
|
|
return button
|
|
|
|
|
|
func _connect_network_signals() -> void:
|
|
NetworkManager.queue_joined.connect(_on_queue_joined)
|
|
NetworkManager.queue_left.connect(_on_queue_left)
|
|
NetworkManager.room_created.connect(_on_room_created)
|
|
NetworkManager.room_joined.connect(_on_room_joined)
|
|
NetworkManager.room_updated.connect(_on_room_updated)
|
|
NetworkManager.matchmaking_update.connect(_on_matchmaking_update)
|
|
NetworkManager.match_found.connect(_on_match_found)
|
|
NetworkManager.network_error.connect(_on_network_error)
|
|
|
|
|
|
func _disconnect_network_signals() -> void:
|
|
if NetworkManager.queue_joined.is_connected(_on_queue_joined):
|
|
NetworkManager.queue_joined.disconnect(_on_queue_joined)
|
|
if NetworkManager.queue_left.is_connected(_on_queue_left):
|
|
NetworkManager.queue_left.disconnect(_on_queue_left)
|
|
if NetworkManager.room_created.is_connected(_on_room_created):
|
|
NetworkManager.room_created.disconnect(_on_room_created)
|
|
if NetworkManager.room_joined.is_connected(_on_room_joined):
|
|
NetworkManager.room_joined.disconnect(_on_room_joined)
|
|
if NetworkManager.room_updated.is_connected(_on_room_updated):
|
|
NetworkManager.room_updated.disconnect(_on_room_updated)
|
|
if NetworkManager.matchmaking_update.is_connected(_on_matchmaking_update):
|
|
NetworkManager.matchmaking_update.disconnect(_on_matchmaking_update)
|
|
if NetworkManager.match_found.is_connected(_on_match_found):
|
|
NetworkManager.match_found.disconnect(_on_match_found)
|
|
if NetworkManager.network_error.is_connected(_on_network_error):
|
|
NetworkManager.network_error.disconnect(_on_network_error)
|
|
|
|
|
|
func _update_user_info() -> void:
|
|
var user = NetworkManager.current_user
|
|
if user.has("username"):
|
|
username_label.text = "Welcome, %s!" % user.username
|
|
if user.has("stats") and user.stats.has("elo_rating"):
|
|
elo_label.text = "ELO: %d" % user.stats.elo_rating
|
|
else:
|
|
elo_label.text = "ELO: 1000"
|
|
|
|
|
|
func _fetch_decks() -> void:
|
|
deck_dropdown.clear()
|
|
deck_dropdown.add_item("-- Select a Deck --", 0)
|
|
|
|
# Add decks from user profile
|
|
var user = NetworkManager.current_user
|
|
if user.has("decks") and user.decks is Array:
|
|
for i in range(user.decks.size()):
|
|
var deck = user.decks[i]
|
|
deck_dropdown.add_item(deck.name, i + 1)
|
|
deck_dropdown.set_item_metadata(i + 1, deck.id)
|
|
|
|
# Also add local starter decks as fallback
|
|
if deck_dropdown.item_count <= 1:
|
|
var starter_decks = _get_local_decks()
|
|
for i in range(starter_decks.size()):
|
|
deck_dropdown.add_item(starter_decks[i].name, i + 1)
|
|
deck_dropdown.set_item_metadata(i + 1, "local_%d" % i)
|
|
|
|
|
|
func _get_local_decks() -> Array:
|
|
# Load starter decks from local file
|
|
var decks = []
|
|
var file_path = "res://data/starter_decks.json"
|
|
if FileAccess.file_exists(file_path):
|
|
var file = FileAccess.open(file_path, FileAccess.READ)
|
|
if file:
|
|
var json = JSON.new()
|
|
var result = json.parse(file.get_as_text())
|
|
if result == OK and json.data is Dictionary:
|
|
if json.data.has("decks"):
|
|
decks = json.data.decks
|
|
file.close()
|
|
return decks
|
|
|
|
|
|
func _show_error(message: String) -> void:
|
|
error_label.text = message
|
|
error_label.visible = true
|
|
# Auto-hide after 5 seconds
|
|
await get_tree().create_timer(5.0).timeout
|
|
if is_instance_valid(error_label):
|
|
error_label.visible = false
|
|
|
|
|
|
func _update_ui_state() -> void:
|
|
# Update button states based on current state
|
|
var has_deck = selected_deck_id != ""
|
|
|
|
# Queue UI
|
|
find_match_button.visible = not is_in_queue and not is_in_room
|
|
find_match_button.disabled = not has_deck
|
|
cancel_search_button.visible = is_in_queue
|
|
queue_status_label.visible = is_in_queue
|
|
|
|
# Private match UI
|
|
private_section.visible = not is_in_queue and not is_in_room
|
|
create_room_button.disabled = not has_deck
|
|
join_room_button.disabled = not has_deck or room_code_input.text.length() != 6
|
|
|
|
# Room UI
|
|
room_section.visible = is_in_room
|
|
|
|
# Disable ranked section when in room
|
|
ranked_section.visible = not is_in_room
|
|
|
|
|
|
# ========== BUTTON HANDLERS ==========
|
|
|
|
func _on_profile_pressed() -> void:
|
|
profile_requested.emit()
|
|
|
|
|
|
func _on_leaderboard_pressed() -> void:
|
|
leaderboard_requested.emit()
|
|
|
|
|
|
func _on_back_pressed() -> void:
|
|
# Leave queue or room before going back
|
|
if is_in_queue:
|
|
NetworkManager.leave_queue()
|
|
if is_in_room:
|
|
NetworkManager.leave_room()
|
|
_disconnect_network_signals()
|
|
back_pressed.emit()
|
|
|
|
|
|
func _on_deck_selected(index: int) -> void:
|
|
if index == 0:
|
|
selected_deck_id = ""
|
|
else:
|
|
selected_deck_id = str(deck_dropdown.get_item_metadata(index))
|
|
_update_ui_state()
|
|
|
|
|
|
func _on_room_code_changed(_new_text: String) -> void:
|
|
room_code_input.text = room_code_input.text.to_upper()
|
|
_update_ui_state()
|
|
|
|
|
|
func _on_find_match_pressed() -> void:
|
|
if selected_deck_id == "":
|
|
_show_error("Please select a deck first")
|
|
return
|
|
|
|
# Connect to WebSocket if not connected
|
|
if NetworkManager.connection_state < NetworkManager.ConnectionState.CONNECTED:
|
|
NetworkManager.connect_websocket()
|
|
await NetworkManager.connection_state_changed
|
|
# Wait a bit for auth
|
|
await get_tree().create_timer(0.5).timeout
|
|
|
|
NetworkManager.join_queue(selected_deck_id)
|
|
|
|
|
|
func _on_cancel_search_pressed() -> void:
|
|
NetworkManager.leave_queue()
|
|
|
|
|
|
func _on_create_room_pressed() -> void:
|
|
if selected_deck_id == "":
|
|
_show_error("Please select a deck first")
|
|
return
|
|
|
|
# Connect to WebSocket if not connected
|
|
if NetworkManager.connection_state < NetworkManager.ConnectionState.CONNECTED:
|
|
NetworkManager.connect_websocket()
|
|
await NetworkManager.connection_state_changed
|
|
await get_tree().create_timer(0.5).timeout
|
|
|
|
NetworkManager.create_room(selected_deck_id)
|
|
|
|
|
|
func _on_join_room_pressed() -> void:
|
|
var code = room_code_input.text.strip_edges().to_upper()
|
|
if code.length() != 6:
|
|
_show_error("Room code must be 6 characters")
|
|
return
|
|
|
|
if selected_deck_id == "":
|
|
_show_error("Please select a deck first")
|
|
return
|
|
|
|
# Connect to WebSocket if not connected
|
|
if NetworkManager.connection_state < NetworkManager.ConnectionState.CONNECTED:
|
|
NetworkManager.connect_websocket()
|
|
await NetworkManager.connection_state_changed
|
|
await get_tree().create_timer(0.5).timeout
|
|
|
|
NetworkManager.join_room(code, selected_deck_id)
|
|
|
|
|
|
func _on_copy_code_pressed() -> void:
|
|
DisplayServer.clipboard_set(current_room_code)
|
|
copy_code_button.text = "Copied!"
|
|
await get_tree().create_timer(1.5).timeout
|
|
if is_instance_valid(copy_code_button):
|
|
copy_code_button.text = "Copy"
|
|
|
|
|
|
func _on_ready_pressed() -> void:
|
|
is_ready = not is_ready
|
|
ready_button.text = "Not Ready" if is_ready else "Ready"
|
|
NetworkManager.set_room_ready(is_ready)
|
|
|
|
|
|
func _on_leave_room_pressed() -> void:
|
|
NetworkManager.leave_room()
|
|
|
|
|
|
# ========== NETWORK SIGNAL HANDLERS ==========
|
|
|
|
func _on_queue_joined() -> void:
|
|
is_in_queue = true
|
|
queue_start_time = Time.get_ticks_msec() / 1000.0
|
|
queue_status_label.text = "Searching... 0:00"
|
|
_update_ui_state()
|
|
|
|
|
|
func _on_queue_left() -> void:
|
|
is_in_queue = false
|
|
_update_ui_state()
|
|
|
|
|
|
func _on_room_created(room_data: Dictionary) -> void:
|
|
is_in_room = true
|
|
current_room_code = room_data.get("code", "")
|
|
_update_room_display(room_data)
|
|
_update_ui_state()
|
|
|
|
|
|
func _on_room_joined(room_data: Dictionary) -> void:
|
|
is_in_room = true
|
|
current_room_code = room_data.get("code", "")
|
|
_update_room_display(room_data)
|
|
_update_ui_state()
|
|
|
|
|
|
func _on_room_updated(room_data: Dictionary) -> void:
|
|
_update_room_display(room_data)
|
|
|
|
|
|
func _update_room_display(room_data: Dictionary) -> void:
|
|
room_code_label.text = "Room: %s" % room_data.get("code", "------")
|
|
|
|
var host = room_data.get("host", {})
|
|
var host_ready = " ✓" if host.get("ready", false) else ""
|
|
host_label.text = "Host: %s%s" % [host.get("username", "---"), host_ready]
|
|
|
|
var guest = room_data.get("guest", null)
|
|
if guest:
|
|
var guest_ready = " ✓" if guest.get("ready", false) else ""
|
|
guest_label.text = "Guest: %s%s" % [guest.get("username", "---"), guest_ready]
|
|
ready_button.disabled = false
|
|
else:
|
|
guest_label.text = "Guest: Waiting for opponent..."
|
|
ready_button.disabled = true
|
|
|
|
|
|
func _on_matchmaking_update(data: Dictionary) -> void:
|
|
var update_type = data.get("type", "")
|
|
match update_type:
|
|
"queue_left":
|
|
is_in_queue = false
|
|
_update_ui_state()
|
|
"room_left":
|
|
is_in_room = false
|
|
is_ready = false
|
|
ready_button.text = "Ready"
|
|
current_room_code = ""
|
|
room_code_input.text = ""
|
|
_update_ui_state()
|
|
var reason = data.get("reason", "")
|
|
if reason != "":
|
|
_show_error(reason)
|
|
|
|
|
|
func _on_match_found(game_data: Dictionary) -> void:
|
|
print("Match found! Game ID: ", game_data.get("game_id", ""))
|
|
is_in_queue = false
|
|
is_in_room = false
|
|
_disconnect_network_signals()
|
|
game_starting.emit(game_data)
|
|
|
|
|
|
func _on_network_error(error: String) -> void:
|
|
_show_error(error)
|
|
|
|
|
|
# ========== CLEANUP ==========
|
|
|
|
func _exit_tree() -> void:
|
|
_disconnect_network_signals()
|