extends Control ## Login screen controller - handles authentication UI # Login panel elements @onready var guest_button := %GuestButton as Button @onready var email_input := %EmailInput as LineEdit @onready var password_input := %PasswordInput as LineEdit @onready var remember_me := %RememberMe as CheckBox @onready var login_button := %LoginButton as Button @onready var register_link := %RegisterLink as LinkButton @onready var google_button := %GoogleButton as Button @onready var apple_button := %AppleButton as Button @onready var facebook_button := %FacebookButton as Button @onready var status_label := %StatusLabel as Label @onready var loading_spinner := %LoadingSpinner as TextureProgressBar # Registration panel elements @onready var registration_panel := %RegistrationPanel as PanelContainer @onready var reg_email_input := %RegEmailInput as LineEdit @onready var reg_username_input := %RegUsernameInput as LineEdit @onready var reg_password_input := %RegPasswordInput as LineEdit @onready var reg_confirm_password_input := %RegConfirmPasswordInput as LineEdit @onready var password_strength := %PasswordStrength as ProgressBar @onready var password_hint := %PasswordHint as Label @onready var register_button := %RegisterButton as Button @onready var back_to_login_link := %BackToLoginLink as LinkButton @onready var reg_status_label := %RegStatusLabel as Label # Main panel reference @onready var main_panel := $CenterContainer/MainPanel as PanelContainer var is_loading: bool = false func _ready() -> void: _connect_signals() _setup_ui() # Check if already authenticated if AuthManager.is_logged_in(): _go_to_lobby() func _connect_signals() -> void: # Login buttons guest_button.pressed.connect(_on_guest_pressed) login_button.pressed.connect(_on_login_pressed) register_link.pressed.connect(_show_registration) # Social buttons google_button.pressed.connect(_on_google_pressed) apple_button.pressed.connect(_on_apple_pressed) facebook_button.pressed.connect(_on_facebook_pressed) # Registration buttons register_button.pressed.connect(_on_register_pressed) back_to_login_link.pressed.connect(_show_login) # Password strength checker reg_password_input.text_changed.connect(_check_password_strength) # Auth manager signals AuthManager.auth_started.connect(_on_auth_started) AuthManager.auth_completed.connect(_on_auth_completed) AuthManager.auth_failed.connect(_on_auth_failed) AuthManager.session_restored.connect(_on_session_restored) # Enter key to submit password_input.text_submitted.connect(func(_t): _on_login_pressed()) reg_confirm_password_input.text_submitted.connect(func(_t): _on_register_pressed()) func _setup_ui() -> void: status_label.text = "" reg_status_label.text = "" loading_spinner.visible = false registration_panel.visible = false main_panel.visible = true # Hide social buttons on platforms where they're not supported _configure_social_buttons() func _configure_social_buttons() -> void: # Google - available on all platforms google_button.visible = true # Apple - iOS and macOS only (or hide if not configured) var os := OS.get_name() apple_button.visible = os in ["iOS", "macOS"] # Facebook - available on all platforms facebook_button.visible = true # ============================================================================= # Panel Switching # ============================================================================= func _show_registration() -> void: main_panel.visible = false registration_panel.visible = true reg_status_label.text = "" reg_email_input.grab_focus() func _show_login() -> void: registration_panel.visible = false main_panel.visible = true status_label.text = "" email_input.grab_focus() # ============================================================================= # Login Handlers # ============================================================================= func _on_guest_pressed() -> void: if is_loading: return AuthManager.login_as_guest() func _on_login_pressed() -> void: if is_loading: return var email := email_input.text.strip_edges() var password := password_input.text if email.is_empty(): _show_error("Please enter your email") return if password.is_empty(): _show_error("Please enter your password") return if not _is_valid_email(email): _show_error("Please enter a valid email address") return AuthManager.login_with_email(email, password, remember_me.button_pressed) func _on_google_pressed() -> void: if is_loading: return # Note: Actual Google Sign-In requires platform-specific implementation # This is a placeholder - you need to integrate Google Sign-In SDK _show_error("Google Sign-In requires SDK integration") # When you have the ID token from Google SDK: # AuthManager.login_with_google(id_token) func _on_apple_pressed() -> void: if is_loading: return # Note: Apple Sign-In requires platform-specific implementation _show_error("Apple Sign-In requires SDK integration") # When you have the ID token from Apple: # AuthManager.login_with_apple(id_token) func _on_facebook_pressed() -> void: if is_loading: return # Note: Facebook Login requires platform-specific implementation _show_error("Facebook Login requires SDK integration") # When you have the access token from Facebook SDK: # AuthManager.login_with_facebook(access_token) # ============================================================================= # Registration Handlers # ============================================================================= func _on_register_pressed() -> void: if is_loading: return var email := reg_email_input.text.strip_edges() var username := reg_username_input.text.strip_edges() var password := reg_password_input.text var confirm_password := reg_confirm_password_input.text # Validation if email.is_empty(): _show_reg_error("Please enter your email") return if not _is_valid_email(email): _show_reg_error("Please enter a valid email address") return if username.is_empty(): _show_reg_error("Please enter a username") return if username.length() < 3: _show_reg_error("Username must be at least 3 characters") return if password.is_empty(): _show_reg_error("Please enter a password") return if password.length() < 8: _show_reg_error("Password must be at least 8 characters") return if password != confirm_password: _show_reg_error("Passwords do not match") return if _calculate_password_strength(password) < 2: _show_reg_error("Password is too weak. Add numbers or symbols.") return AuthManager.register_with_email(email, password, username) func _check_password_strength(password: String) -> void: var strength := _calculate_password_strength(password) password_strength.value = strength # Color based on strength var color: Color match strength: 0, 1: color = Color.RED password_hint.text = "Weak - add more characters" 2: color = Color.ORANGE password_hint.text = "Fair - add numbers or symbols" 3: color = Color.YELLOW_GREEN password_hint.text = "Good" 4: color = Color.GREEN password_hint.text = "Strong!" # Apply color to progress bar var style := StyleBoxFlat.new() style.bg_color = color password_strength.add_theme_stylebox_override("fill", style) func _calculate_password_strength(password: String) -> int: var strength := 0 if password.length() >= 8: strength += 1 if password.length() >= 12: strength += 1 var has_upper := false var has_lower := false var has_digit := false var has_special := false for c in password: if c.to_upper() != c.to_lower(): if c == c.to_upper(): has_upper = true else: has_lower = true elif c.is_valid_int(): has_digit = true else: has_special = true if has_upper and has_lower: strength += 1 if has_digit: strength += 0.5 if has_special: strength += 0.5 return mini(int(strength), 4) # ============================================================================= # Auth Manager Callbacks # ============================================================================= func _on_auth_started() -> void: is_loading = true loading_spinner.visible = true _set_inputs_enabled(false) status_label.text = "" reg_status_label.text = "" func _on_auth_completed(success: bool, _user_data: Dictionary) -> void: # Check if node is still in tree (may have been freed during scene transition) if not is_inside_tree(): return is_loading = false loading_spinner.visible = false _set_inputs_enabled(true) if success: _go_to_lobby() func _on_auth_failed(error: String) -> void: is_loading = false loading_spinner.visible = false _set_inputs_enabled(true) if registration_panel.visible: _show_reg_error(error) else: _show_error(error) func _on_session_restored() -> void: if not is_inside_tree(): return _go_to_lobby() # ============================================================================= # Helper Functions # ============================================================================= func _show_error(message: String) -> void: status_label.text = message status_label.add_theme_color_override("font_color", Color(1, 0.4, 0.4)) func _show_reg_error(message: String) -> void: reg_status_label.text = message reg_status_label.add_theme_color_override("font_color", Color(1, 0.4, 0.4)) func _set_inputs_enabled(enabled: bool) -> void: guest_button.disabled = not enabled login_button.disabled = not enabled register_button.disabled = not enabled google_button.disabled = not enabled apple_button.disabled = not enabled facebook_button.disabled = not enabled email_input.editable = enabled password_input.editable = enabled reg_email_input.editable = enabled reg_username_input.editable = enabled reg_password_input.editable = enabled reg_confirm_password_input.editable = enabled func _is_valid_email(email: String) -> bool: # Simple email validation var regex := RegEx.new() regex.compile("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$") return regex.search(email) != null func _go_to_lobby() -> void: # Navigate to lobby scene - use deferred call to avoid issues during signal callbacks if get_tree(): get_tree().change_scene_to_file("res://scenes/lobby.tscn") else: # Fallback: try deferred call call_deferred("_deferred_go_to_lobby") func _deferred_go_to_lobby() -> void: if get_tree(): get_tree().change_scene_to_file("res://scenes/lobby.tscn")