class_name RegisterScreen extends CanvasLayer ## RegisterScreen - Account creation form for online play signal registration_successful(message: String) signal login_requested signal back_pressed const WINDOW_SIZE := Vector2(400, 600) # Validation constants const USERNAME_MIN_LENGTH = 3 const USERNAME_MAX_LENGTH = 32 const PASSWORD_MIN_LENGTH = 8 # UI Components var background: PanelContainer var main_vbox: VBoxContainer var title_label: Label var email_input: LineEdit var username_input: LineEdit var password_input: LineEdit var confirm_password_input: LineEdit var register_button: Button var login_link: Button var back_button: Button var error_label: Label var success_label: Label # State var _is_loading: bool = false func _ready() -> void: layer = 100 _create_ui() func _create_ui() -> void: # Background panel background = PanelContainer.new() add_child(background) background.position = Vector2.ZERO background.size = WINDOW_SIZE background.add_theme_stylebox_override("panel", _create_panel_style()) # Main layout with margin var margin = MarginContainer.new() background.add_child(margin) margin.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) margin.add_theme_constant_override("margin_left", 40) margin.add_theme_constant_override("margin_right", 40) margin.add_theme_constant_override("margin_top", 25) margin.add_theme_constant_override("margin_bottom", 25) main_vbox = VBoxContainer.new() margin.add_child(main_vbox) main_vbox.add_theme_constant_override("separation", 10) # Title _create_title() # Registration form _create_form() # Messages _create_message_labels() # Spacer var spacer = Control.new() spacer.size_flags_vertical = Control.SIZE_EXPAND_FILL main_vbox.add_child(spacer) # Links _create_links() # Back button _create_back_button() func _create_title() -> void: title_label = Label.new() title_label.text = "CREATE ACCOUNT" title_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER title_label.add_theme_font_size_override("font_size", 28) title_label.add_theme_color_override("font_color", Color(1.0, 0.95, 0.8)) main_vbox.add_child(title_label) # Separator var separator = HSeparator.new() separator.add_theme_stylebox_override("separator", _create_separator_style()) main_vbox.add_child(separator) # Small spacer var spacer = Control.new() spacer.custom_minimum_size.y = 10 main_vbox.add_child(spacer) func _create_form() -> void: # Email field var email_label = Label.new() email_label.text = "Email" email_label.add_theme_font_size_override("font_size", 15) email_label.add_theme_color_override("font_color", Color(0.9, 0.85, 0.7)) main_vbox.add_child(email_label) email_input = LineEdit.new() email_input.placeholder_text = "Enter your email" email_input.custom_minimum_size = Vector2(0, 38) _style_input(email_input) main_vbox.add_child(email_input) # Username field var username_label = Label.new() username_label.text = "Username" username_label.add_theme_font_size_override("font_size", 15) username_label.add_theme_color_override("font_color", Color(0.9, 0.85, 0.7)) main_vbox.add_child(username_label) username_input = LineEdit.new() username_input.placeholder_text = "3-32 characters" username_input.custom_minimum_size = Vector2(0, 38) username_input.max_length = USERNAME_MAX_LENGTH _style_input(username_input) main_vbox.add_child(username_input) # Password field var password_label = Label.new() password_label.text = "Password" password_label.add_theme_font_size_override("font_size", 15) password_label.add_theme_color_override("font_color", Color(0.9, 0.85, 0.7)) main_vbox.add_child(password_label) password_input = LineEdit.new() password_input.placeholder_text = "At least 8 characters" password_input.secret = true password_input.custom_minimum_size = Vector2(0, 38) _style_input(password_input) main_vbox.add_child(password_input) # Confirm password field var confirm_label = Label.new() confirm_label.text = "Confirm Password" confirm_label.add_theme_font_size_override("font_size", 15) confirm_label.add_theme_color_override("font_color", Color(0.9, 0.85, 0.7)) main_vbox.add_child(confirm_label) confirm_password_input = LineEdit.new() confirm_password_input.placeholder_text = "Re-enter your password" confirm_password_input.secret = true confirm_password_input.custom_minimum_size = Vector2(0, 38) _style_input(confirm_password_input) confirm_password_input.text_submitted.connect(_on_input_submitted) main_vbox.add_child(confirm_password_input) # Register button var button_spacer = Control.new() button_spacer.custom_minimum_size.y = 10 main_vbox.add_child(button_spacer) register_button = Button.new() register_button.text = "Create Account" register_button.custom_minimum_size = Vector2(0, 45) _style_button(register_button, true) register_button.pressed.connect(_on_register_pressed) main_vbox.add_child(register_button) func _create_message_labels() -> void: # Error label error_label = Label.new() error_label.text = "" error_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER error_label.add_theme_font_size_override("font_size", 13) error_label.add_theme_color_override("font_color", Color(1.0, 0.4, 0.4)) error_label.autowrap_mode = TextServer.AUTOWRAP_WORD error_label.visible = false main_vbox.add_child(error_label) # Success label success_label = Label.new() success_label.text = "" success_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER success_label.add_theme_font_size_override("font_size", 13) success_label.add_theme_color_override("font_color", Color(0.4, 1.0, 0.5)) success_label.autowrap_mode = TextServer.AUTOWRAP_WORD success_label.visible = false main_vbox.add_child(success_label) func _create_links() -> void: # Login link login_link = Button.new() login_link.text = "Already have an account? Login" login_link.flat = true login_link.add_theme_font_size_override("font_size", 14) login_link.add_theme_color_override("font_color", Color(0.6, 0.7, 1.0)) login_link.add_theme_color_override("font_hover_color", Color(0.8, 0.85, 1.0)) login_link.pressed.connect(_on_login_pressed) main_vbox.add_child(login_link) func _create_back_button() -> void: var button_container = HBoxContainer.new() button_container.alignment = BoxContainer.ALIGNMENT_CENTER main_vbox.add_child(button_container) back_button = Button.new() back_button.text = "Back" back_button.custom_minimum_size = Vector2(100, 38) _style_button(back_button, false) back_button.pressed.connect(_on_back_pressed) button_container.add_child(back_button) # ======= STYLING ======= func _create_panel_style() -> StyleBoxFlat: var style = StyleBoxFlat.new() style.bg_color = Color(0.08, 0.08, 0.12, 1.0) style.set_border_width_all(0) style.set_corner_radius_all(0) return style func _create_separator_style() -> StyleBoxFlat: var style = StyleBoxFlat.new() style.bg_color = Color(0.5, 0.4, 0.2, 0.5) style.content_margin_top = 1 return style func _style_input(input: LineEdit) -> void: var style = StyleBoxFlat.new() style.bg_color = Color(0.12, 0.12, 0.16) style.border_color = Color(0.4, 0.35, 0.25) style.set_border_width_all(1) style.set_corner_radius_all(4) style.content_margin_left = 12 style.content_margin_right = 12 style.content_margin_top = 6 style.content_margin_bottom = 6 input.add_theme_stylebox_override("normal", style) var focus_style = style.duplicate() focus_style.border_color = Color(0.7, 0.6, 0.3) input.add_theme_stylebox_override("focus", focus_style) input.add_theme_color_override("font_color", Color(0.95, 0.9, 0.8)) input.add_theme_color_override("font_placeholder_color", Color(0.5, 0.5, 0.55)) input.add_theme_font_size_override("font_size", 15) func _style_button(button: Button, is_primary: bool) -> void: var style = StyleBoxFlat.new() if is_primary: style.bg_color = Color(0.3, 0.25, 0.15) style.border_color = Color(0.6, 0.5, 0.3) else: style.bg_color = Color(0.15, 0.15, 0.2) style.border_color = Color(0.4, 0.35, 0.25) style.set_border_width_all(2) style.set_corner_radius_all(6) style.content_margin_left = 20 style.content_margin_right = 20 style.content_margin_top = 8 style.content_margin_bottom = 8 button.add_theme_stylebox_override("normal", style) var hover_style = style.duplicate() if is_primary: hover_style.bg_color = Color(0.4, 0.35, 0.2) hover_style.border_color = Color(0.8, 0.7, 0.4) else: hover_style.bg_color = Color(0.2, 0.2, 0.25) hover_style.border_color = Color(0.5, 0.45, 0.35) button.add_theme_stylebox_override("hover", hover_style) var pressed_style = style.duplicate() pressed_style.bg_color = Color(0.1, 0.1, 0.12) button.add_theme_stylebox_override("pressed", pressed_style) var disabled_style = style.duplicate() disabled_style.bg_color = Color(0.1, 0.1, 0.12) disabled_style.border_color = Color(0.25, 0.25, 0.3) button.add_theme_stylebox_override("disabled", disabled_style) button.add_theme_color_override("font_color", Color(0.9, 0.85, 0.7)) button.add_theme_color_override("font_hover_color", Color(1.0, 0.95, 0.8)) button.add_theme_color_override("font_pressed_color", Color(0.7, 0.65, 0.55)) button.add_theme_color_override("font_disabled_color", Color(0.45, 0.42, 0.38)) button.add_theme_font_size_override("font_size", 16) # ======= EVENT HANDLERS ======= func _on_input_submitted(_text: String) -> void: _on_register_pressed() func _on_register_pressed() -> void: if _is_loading: return var email = email_input.text.strip_edges() var username = username_input.text.strip_edges() var password = password_input.text var confirm_password = confirm_password_input.text # Validate inputs var validation_error = _validate_inputs(email, username, password, confirm_password) if validation_error != "": _show_error(validation_error) return # Start registration _set_loading(true) _hide_messages() var result = await NetworkManager.register(email, password, username) _set_loading(false) if result.success: _show_success(result.message) registration_successful.emit(result.message) else: _show_error(result.message) func _on_login_pressed() -> void: login_requested.emit() func _on_back_pressed() -> void: back_pressed.emit() # ======= VALIDATION ======= func _validate_inputs(email: String, username: String, password: String, confirm_password: String) -> String: # Email validation if email.is_empty(): return "Please enter your email" if not _is_valid_email(email): return "Please enter a valid email address" # Username validation if username.is_empty(): return "Please enter a username" if username.length() < USERNAME_MIN_LENGTH: return "Username must be at least %d characters" % USERNAME_MIN_LENGTH if username.length() > USERNAME_MAX_LENGTH: return "Username must be at most %d characters" % USERNAME_MAX_LENGTH if not _is_valid_username(username): return "Username can only contain letters, numbers, underscores, and hyphens" # Password validation if password.is_empty(): return "Please enter a password" if password.length() < PASSWORD_MIN_LENGTH: return "Password must be at least %d characters" % PASSWORD_MIN_LENGTH # Confirm password if confirm_password != password: return "Passwords do not match" return "" func _is_valid_email(email: String) -> bool: var regex = RegEx.new() regex.compile("^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$") return regex.search(email) != null func _is_valid_username(username: String) -> bool: var regex = RegEx.new() regex.compile("^[a-zA-Z0-9_-]+$") return regex.search(username) != null # ======= HELPERS ======= func _show_error(message: String) -> void: error_label.text = message error_label.visible = true success_label.visible = false func _show_success(message: String) -> void: success_label.text = message success_label.visible = true error_label.visible = false func _hide_messages() -> void: error_label.visible = false success_label.visible = false func _set_loading(loading: bool) -> void: _is_loading = loading register_button.disabled = loading register_button.text = "Creating Account..." if loading else "Create Account" email_input.editable = not loading username_input.editable = not loading password_input.editable = not loading confirm_password_input.editable = not loading func clear_form() -> void: email_input.text = "" username_input.text = "" password_input.text = "" confirm_password_input.text = "" _hide_messages() _set_loading(false) func focus_email() -> void: email_input.grab_focus()