class_name DeckBuilder extends CanvasLayer ## DeckBuilder - Main deck builder screen with three-panel layout signal back_pressed signal deck_selected(deck: Deck) const WINDOW_SIZE = Vector2i(1600, 900) var current_deck: Deck = null var current_deck_filename: String = "" # UI Components var detail_viewer: CardDetailViewer var filter_bar: CardFilterBar var card_grid: CardGrid var deck_panel: DeckListPanel # Header elements var back_button: Button var deck_name_field: LineEdit var card_count_label: Label var save_button: Button var load_button: Button var new_button: Button var play_button: Button # Dialogs var save_dialog: Control var load_dialog: Control func _ready() -> void: layer = 10 _create_ui() _connect_signals() _new_deck() _load_all_cards() func _create_ui() -> void: # Root control var root = Control.new() root.set_anchors_preset(Control.PRESET_FULL_RECT) root.mouse_filter = Control.MOUSE_FILTER_STOP add_child(root) # Background var bg = ColorRect.new() bg.set_anchors_preset(Control.PRESET_FULL_RECT) bg.color = Color(0.05, 0.05, 0.08, 1.0) root.add_child(bg) # Main layout var main_vbox = VBoxContainer.new() main_vbox.set_anchors_preset(Control.PRESET_FULL_RECT) main_vbox.add_theme_constant_override("separation", 0) root.add_child(main_vbox) # Header bar _create_header(main_vbox) # Content area (3 panels) var content_hbox = HBoxContainer.new() content_hbox.size_flags_vertical = Control.SIZE_EXPAND_FILL content_hbox.add_theme_constant_override("separation", 0) main_vbox.add_child(content_hbox) # Left panel - Card Detail Viewer detail_viewer = CardDetailViewer.new() content_hbox.add_child(detail_viewer) # Center panel container var center_panel = VBoxContainer.new() center_panel.size_flags_horizontal = Control.SIZE_EXPAND_FILL center_panel.add_theme_constant_override("separation", 0) content_hbox.add_child(center_panel) # Filter bar filter_bar = CardFilterBar.new() center_panel.add_child(filter_bar) # Results count var results_bar = HBoxContainer.new() results_bar.custom_minimum_size = Vector2(0, 30) var results_style = StyleBoxFlat.new() results_style.bg_color = Color(0.08, 0.08, 0.1) results_style.content_margin_left = 12 results_style.content_margin_top = 4 var results_panel = PanelContainer.new() results_panel.add_theme_stylebox_override("panel", results_style) results_panel.size_flags_horizontal = Control.SIZE_EXPAND_FILL center_panel.add_child(results_panel) card_count_label = Label.new() card_count_label.text = "Loading cards..." card_count_label.add_theme_font_size_override("font_size", 12) card_count_label.add_theme_color_override("font_color", Color(0.6, 0.6, 0.6)) results_panel.add_child(card_count_label) # Card grid card_grid = CardGrid.new() card_grid.size_flags_vertical = Control.SIZE_EXPAND_FILL center_panel.add_child(card_grid) # Right panel - Deck List deck_panel = DeckListPanel.new() content_hbox.add_child(deck_panel) # Create dialogs (hidden) _create_save_dialog(root) _create_load_dialog(root) func _create_header(parent: Control) -> void: var header = PanelContainer.new() header.custom_minimum_size = Vector2(0, 50) var header_style = StyleBoxFlat.new() header_style.bg_color = Color(0.1, 0.1, 0.14) header_style.border_color = Color(0.3, 0.25, 0.15) header_style.border_width_bottom = 2 header_style.content_margin_left = 15 header_style.content_margin_right = 15 header.add_theme_stylebox_override("panel", header_style) parent.add_child(header) var header_hbox = HBoxContainer.new() header_hbox.add_theme_constant_override("separation", 15) header_hbox.alignment = BoxContainer.ALIGNMENT_BEGIN header.add_child(header_hbox) # Back button back_button = _create_header_button("< Back") header_hbox.add_child(back_button) # Deck name var name_label = Label.new() name_label.text = "Deck:" name_label.add_theme_font_size_override("font_size", 14) header_hbox.add_child(name_label) deck_name_field = LineEdit.new() deck_name_field.text = "New Deck" deck_name_field.custom_minimum_size = Vector2(200, 0) deck_name_field.text_changed.connect(_on_deck_name_changed) header_hbox.add_child(deck_name_field) # Spacer var spacer = Control.new() spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL header_hbox.add_child(spacer) # Action buttons new_button = _create_header_button("New") save_button = _create_header_button("Save") load_button = _create_header_button("Load") play_button = _create_header_button("Play with Deck") play_button.add_theme_color_override("font_color", Color(0.4, 0.8, 0.4)) header_hbox.add_child(new_button) header_hbox.add_child(save_button) header_hbox.add_child(load_button) header_hbox.add_child(play_button) func _create_header_button(text: String) -> Button: var button = Button.new() button.text = text button.custom_minimum_size = Vector2(80, 32) var style_normal = StyleBoxFlat.new() style_normal.bg_color = Color(0.2, 0.2, 0.25) style_normal.border_color = Color(0.4, 0.4, 0.5) style_normal.set_border_width_all(1) style_normal.set_corner_radius_all(4) button.add_theme_stylebox_override("normal", style_normal) var style_hover = StyleBoxFlat.new() style_hover.bg_color = Color(0.3, 0.3, 0.38) style_hover.border_color = Color(0.6, 0.5, 0.3) style_hover.set_border_width_all(1) style_hover.set_corner_radius_all(4) button.add_theme_stylebox_override("hover", style_hover) button.add_theme_font_size_override("font_size", 13) return button func _create_save_dialog(parent: Control) -> void: save_dialog = _create_dialog_base("Save Deck") parent.add_child(save_dialog) var content = save_dialog.get_node("Panel/VBox") var name_hbox = HBoxContainer.new() name_hbox.add_theme_constant_override("separation", 8) content.add_child(name_hbox) var label = Label.new() label.text = "Filename:" name_hbox.add_child(label) var save_name_field = LineEdit.new() save_name_field.name = "SaveNameField" save_name_field.custom_minimum_size = Vector2(200, 0) name_hbox.add_child(save_name_field) var btn_hbox = HBoxContainer.new() btn_hbox.add_theme_constant_override("separation", 10) btn_hbox.alignment = BoxContainer.ALIGNMENT_CENTER content.add_child(btn_hbox) var save_btn = _create_header_button("Save") save_btn.pressed.connect(_on_save_confirmed) btn_hbox.add_child(save_btn) var cancel_btn = _create_header_button("Cancel") cancel_btn.pressed.connect(func(): save_dialog.visible = false) btn_hbox.add_child(cancel_btn) func _create_load_dialog(parent: Control) -> void: load_dialog = _create_dialog_base("Load Deck") parent.add_child(load_dialog) var content = load_dialog.get_node("Panel/VBox") var deck_list = ItemList.new() deck_list.name = "DeckList" deck_list.custom_minimum_size = Vector2(300, 200) deck_list.item_activated.connect(_on_deck_item_activated) content.add_child(deck_list) var btn_hbox = HBoxContainer.new() btn_hbox.add_theme_constant_override("separation", 10) btn_hbox.alignment = BoxContainer.ALIGNMENT_CENTER content.add_child(btn_hbox) var load_btn = _create_header_button("Load") load_btn.pressed.connect(_on_load_confirmed) btn_hbox.add_child(load_btn) var delete_btn = _create_header_button("Delete") delete_btn.add_theme_color_override("font_color", Color(1.0, 0.5, 0.5)) delete_btn.pressed.connect(_on_delete_deck) btn_hbox.add_child(delete_btn) var cancel_btn = _create_header_button("Cancel") cancel_btn.pressed.connect(func(): load_dialog.visible = false) btn_hbox.add_child(cancel_btn) func _create_dialog_base(title: String) -> Control: var overlay = Control.new() overlay.set_anchors_preset(Control.PRESET_FULL_RECT) overlay.visible = false var bg = ColorRect.new() bg.set_anchors_preset(Control.PRESET_FULL_RECT) bg.color = Color(0, 0, 0, 0.6) bg.gui_input.connect(func(event): if event is InputEventMouseButton and event.pressed: overlay.visible = false ) overlay.add_child(bg) var panel = PanelContainer.new() panel.name = "Panel" panel.set_anchors_preset(Control.PRESET_CENTER) panel.custom_minimum_size = Vector2(350, 250) var style = StyleBoxFlat.new() style.bg_color = Color(0.1, 0.1, 0.14, 0.98) style.border_color = Color(0.5, 0.4, 0.2) style.set_border_width_all(2) style.set_corner_radius_all(8) style.content_margin_left = 20 style.content_margin_right = 20 style.content_margin_top = 15 style.content_margin_bottom = 15 panel.add_theme_stylebox_override("panel", style) overlay.add_child(panel) var vbox = VBoxContainer.new() vbox.name = "VBox" vbox.add_theme_constant_override("separation", 15) panel.add_child(vbox) var title_label = Label.new() title_label.text = title title_label.add_theme_font_size_override("font_size", 18) title_label.add_theme_color_override("font_color", Color(1.0, 0.95, 0.8)) title_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER vbox.add_child(title_label) return overlay func _connect_signals() -> void: back_button.pressed.connect(_on_back_pressed) new_button.pressed.connect(_new_deck) save_button.pressed.connect(_show_save_dialog) load_button.pressed.connect(_show_load_dialog) play_button.pressed.connect(_on_play_pressed) filter_bar.filters_changed.connect(_on_filters_changed) card_grid.card_selected.connect(_on_card_selected) card_grid.card_double_clicked.connect(_on_card_double_clicked) detail_viewer.add_to_deck_requested.connect(_on_add_to_deck) deck_panel.card_clicked.connect(_on_deck_card_clicked) deck_panel.card_removed.connect(_on_deck_card_removed) deck_panel.deck_cleared.connect(_on_deck_cleared) func _load_all_cards() -> void: var all_cards = CardDatabase.get_all_cards() card_grid.set_cards(all_cards) card_count_label.text = "Showing %d of %d cards" % [all_cards.size(), all_cards.size()] func _new_deck() -> void: current_deck = Deck.new() current_deck.name = DeckManager.generate_unique_name() current_deck_filename = "" deck_name_field.text = current_deck.name deck_panel.set_deck(current_deck) detail_viewer.clear() func _on_back_pressed() -> void: back_pressed.emit() func _on_play_pressed() -> void: if current_deck and current_deck.is_valid(): deck_selected.emit(current_deck) else: # Show validation errors var errors = current_deck.validate() if current_deck else ["No deck loaded"] push_warning("Cannot play - deck invalid: " + ", ".join(errors)) func _on_deck_name_changed(new_name: String) -> void: if current_deck: current_deck.name = new_name func _on_filters_changed(filters: Dictionary) -> void: var results = CardDatabase.filter_cards(filters) card_grid.set_cards(results) card_count_label.text = "Showing %d of %d cards" % [results.size(), CardDatabase.get_card_count()] func _on_card_selected(card: CardDatabase.CardData) -> void: var deck_count = current_deck.get_card_count(card.id) if current_deck else 0 detail_viewer.show_card(card, deck_count) func _on_card_double_clicked(card: CardDatabase.CardData) -> void: if current_deck: var error = current_deck.add_card(card.id) if error.is_empty(): detail_viewer.update_deck_count(current_deck.get_card_count(card.id)) func _on_add_to_deck(card: CardDatabase.CardData, quantity: int) -> void: if not current_deck: return for i in range(quantity): var error = current_deck.add_card(card.id) if not error.is_empty(): break detail_viewer.update_deck_count(current_deck.get_card_count(card.id)) func _on_deck_card_clicked(card_id: String) -> void: var card_data = CardDatabase.get_card(card_id) if card_data: var deck_count = current_deck.get_card_count(card_id) if current_deck else 0 detail_viewer.show_card(card_data, deck_count) func _on_deck_card_removed(card_id: String) -> void: if current_deck: current_deck.remove_card(card_id) # Update detail viewer if showing this card var card_data = CardDatabase.get_card(card_id) if card_data: detail_viewer.update_deck_count(current_deck.get_card_count(card_id)) func _on_deck_cleared() -> void: if current_deck: current_deck.clear() detail_viewer.clear() func _show_save_dialog() -> void: var save_name_field = save_dialog.get_node("Panel/VBox/HBoxContainer/SaveNameField") as LineEdit save_name_field.text = current_deck.name if current_deck else "New Deck" save_dialog.visible = true func _on_save_confirmed() -> void: var save_name_field = save_dialog.get_node("Panel/VBox/HBoxContainer/SaveNameField") as LineEdit var filename = save_name_field.text.strip_edges() if filename.is_empty(): return if current_deck: current_deck.name = filename deck_name_field.text = filename if DeckManager.save_deck(current_deck, filename): current_deck_filename = filename print("Deck saved: ", filename) else: push_error("Failed to save deck") save_dialog.visible = false func _show_load_dialog() -> void: var deck_list = load_dialog.get_node("Panel/VBox/DeckList") as ItemList deck_list.clear() for deck_name in DeckManager.list_decks(): deck_list.add_item(deck_name) load_dialog.visible = true func _on_deck_item_activated(index: int) -> void: _on_load_confirmed() func _on_load_confirmed() -> void: var deck_list = load_dialog.get_node("Panel/VBox/DeckList") as ItemList var selected = deck_list.get_selected_items() if selected.is_empty(): return var filename = deck_list.get_item_text(selected[0]) var loaded_deck = DeckManager.load_deck(filename) if loaded_deck: current_deck = loaded_deck current_deck_filename = filename deck_name_field.text = current_deck.name deck_panel.set_deck(current_deck) detail_viewer.clear() print("Deck loaded: ", filename) else: push_error("Failed to load deck") load_dialog.visible = false func _on_delete_deck() -> void: var deck_list = load_dialog.get_node("Panel/VBox/DeckList") as ItemList var selected = deck_list.get_selected_items() if selected.is_empty(): return var filename = deck_list.get_item_text(selected[0]) if DeckManager.delete_deck(filename): deck_list.remove_item(selected[0]) print("Deck deleted: ", filename)