class_name DeckListPanel extends Control ## DeckListPanel - Right panel showing deck contents with thumbnails and stats signal card_clicked(card_id: String) signal card_removed(card_id: String) signal deck_cleared const PANEL_WIDTH: float = 400.0 const CARD_WIDTH: float = 90.0 const CARD_HEIGHT: float = 126.0 const CARD_GAP: float = 4.0 const COLUMNS: int = 4 var current_deck: Deck = null var card_cells: Dictionary = {} # card_id -> Control # UI elements var stats_panel: Control var element_labels: Dictionary = {} var type_labels: Dictionary = {} var total_label: Label var deck_scroll: ScrollContainer var deck_grid: Control var validation_label: Label var clear_button: Button func _ready() -> void: custom_minimum_size = Vector2(PANEL_WIDTH, 0) _create_ui() func _create_ui() -> void: var panel = PanelContainer.new() panel.set_anchors_preset(Control.PRESET_FULL_RECT) panel.add_theme_stylebox_override("panel", _create_panel_style()) add_child(panel) var main_vbox = VBoxContainer.new() main_vbox.add_theme_constant_override("separation", 10) panel.add_child(main_vbox) # Header var header = Label.new() header.text = "Deck" header.add_theme_font_size_override("font_size", 18) header.add_theme_color_override("font_color", Color(1.0, 0.95, 0.8)) main_vbox.add_child(header) # Stats panel _create_stats_panel(main_vbox) # Deck grid deck_scroll = ScrollContainer.new() deck_scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED deck_scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL main_vbox.add_child(deck_scroll) deck_grid = Control.new() deck_grid.custom_minimum_size = Vector2(COLUMNS * (CARD_WIDTH + CARD_GAP) - CARD_GAP, 0) deck_scroll.add_child(deck_grid) # Validation label validation_label = Label.new() validation_label.text = "" validation_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART validation_label.add_theme_font_size_override("font_size", 12) validation_label.add_theme_color_override("font_color", Color(1.0, 0.4, 0.4)) main_vbox.add_child(validation_label) # Clear deck button clear_button = _create_styled_button("Clear Deck") clear_button.pressed.connect(_on_clear_pressed) main_vbox.add_child(clear_button) func _create_stats_panel(parent: Control) -> void: stats_panel = PanelContainer.new() var stats_style = StyleBoxFlat.new() stats_style.bg_color = Color(0.12, 0.12, 0.16, 0.8) stats_style.set_corner_radius_all(4) stats_style.content_margin_left = 8 stats_style.content_margin_right = 8 stats_style.content_margin_top = 6 stats_style.content_margin_bottom = 6 stats_panel.add_theme_stylebox_override("panel", stats_style) parent.add_child(stats_panel) var stats_vbox = VBoxContainer.new() stats_vbox.add_theme_constant_override("separation", 4) stats_panel.add_child(stats_vbox) # Total cards total_label = Label.new() total_label.text = "Cards: 0/50" total_label.add_theme_font_size_override("font_size", 14) total_label.add_theme_color_override("font_color", Color(0.9, 0.85, 0.7)) stats_vbox.add_child(total_label) # Element breakdown var elem_hbox = HBoxContainer.new() elem_hbox.add_theme_constant_override("separation", 8) stats_vbox.add_child(elem_hbox) for element in Enums.Element.values(): var elem_container = HBoxContainer.new() elem_container.add_theme_constant_override("separation", 2) var elem_icon = ColorRect.new() elem_icon.custom_minimum_size = Vector2(12, 12) elem_icon.color = Enums.element_to_color(element) elem_container.add_child(elem_icon) var elem_label = Label.new() elem_label.text = "0" elem_label.add_theme_font_size_override("font_size", 10) elem_container.add_child(elem_label) element_labels[element] = elem_label elem_hbox.add_child(elem_container) # Type breakdown var type_hbox = HBoxContainer.new() type_hbox.add_theme_constant_override("separation", 12) stats_vbox.add_child(type_hbox) for card_type in [Enums.CardType.FORWARD, Enums.CardType.BACKUP, Enums.CardType.SUMMON]: var type_label = Label.new() type_label.text = "%s: 0" % Enums.card_type_to_string(card_type).substr(0, 3) type_label.add_theme_font_size_override("font_size", 10) type_label.add_theme_color_override("font_color", Color(0.7, 0.7, 0.7)) type_hbox.add_child(type_label) type_labels[card_type] = type_label func _create_panel_style() -> StyleBoxFlat: var style = StyleBoxFlat.new() style.bg_color = Color(0.08, 0.08, 0.12, 0.95) style.border_color = Color(0.5, 0.4, 0.2) style.set_border_width_all(2) style.set_corner_radius_all(6) style.content_margin_left = 12 style.content_margin_right = 12 style.content_margin_top = 12 style.content_margin_bottom = 12 return style func _create_styled_button(text: String) -> Button: var button = Button.new() button.text = text button.custom_minimum_size = Vector2(0, 36) var style_normal = StyleBoxFlat.new() style_normal.bg_color = Color(0.4, 0.2, 0.2) style_normal.border_color = Color(0.6, 0.3, 0.3) 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.5, 0.25, 0.25) style_hover.border_color = Color(0.8, 0.4, 0.4) 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", 14) return button ## Set the deck to display func set_deck(deck: Deck) -> void: if current_deck: current_deck.deck_changed.disconnect(_on_deck_changed) current_deck = deck if current_deck: current_deck.deck_changed.connect(_on_deck_changed) _refresh_display() func _on_deck_changed() -> void: _refresh_display() func _refresh_display() -> void: # Clear existing cells for cell in card_cells.values(): cell.queue_free() card_cells.clear() if not current_deck: total_label.text = "Cards: 0/50" validation_label.text = "" _update_stats({}) return # Get stats var stats = current_deck.get_stats() _update_stats(stats) # Update total total_label.text = "Cards: %d/50" % stats.total # Validate var errors = current_deck.validate() validation_label.text = "\n".join(errors) # Create card cells var card_ids = current_deck.get_card_ids() card_ids.sort() # Sort alphabetically var row = 0 var col = 0 for card_id in card_ids: var count = current_deck.get_card_count(card_id) var cell = _create_deck_cell(card_id, count) cell.position = Vector2(col * (CARD_WIDTH + CARD_GAP), row * (CARD_HEIGHT + CARD_GAP)) deck_grid.add_child(cell) card_cells[card_id] = cell col += 1 if col >= COLUMNS: col = 0 row += 1 # Update grid size var total_rows = ceili(float(card_ids.size()) / COLUMNS) deck_grid.custom_minimum_size.y = total_rows * (CARD_HEIGHT + CARD_GAP) func _update_stats(stats: Dictionary) -> void: # Update element counts for element in element_labels: var elem_name = Enums.element_to_string(element) var count = stats.get("elements", {}).get(elem_name, 0) element_labels[element].text = str(count) # Update type counts for card_type in type_labels: var type_name = Enums.card_type_to_string(card_type) var count = stats.get("types", {}).get(type_name, 0) type_labels[card_type].text = "%s: %d" % [type_name.substr(0, 3), count] func _create_deck_cell(card_id: String, count: int) -> Control: var cell = Panel.new() cell.custom_minimum_size = Vector2(CARD_WIDTH, CARD_HEIGHT) cell.size = Vector2(CARD_WIDTH, CARD_HEIGHT) cell.mouse_filter = Control.MOUSE_FILTER_STOP cell.set_meta("card_id", card_id) var style = StyleBoxFlat.new() style.bg_color = Color(0.15, 0.15, 0.2, 0.8) style.border_color = Color(0.3, 0.3, 0.35) style.set_border_width_all(1) style.set_corner_radius_all(2) cell.add_theme_stylebox_override("panel", style) # Card image var card_data = CardDatabase.get_card(card_id) if card_data: var texture = CardDatabase.get_card_texture(card_data) if texture: var tex_rect = TextureRect.new() tex_rect.set_anchors_preset(Control.PRESET_FULL_RECT) tex_rect.expand_mode = TextureRect.EXPAND_IGNORE_SIZE tex_rect.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED tex_rect.texture = texture cell.add_child(tex_rect) else: var fallback = ColorRect.new() fallback.set_anchors_preset(Control.PRESET_FULL_RECT) fallback.color = Enums.element_to_color(card_data.get_primary_element()).darkened(0.3) cell.add_child(fallback) var name_label = Label.new() name_label.text = card_data.name name_label.set_anchors_preset(Control.PRESET_CENTER) name_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER name_label.autowrap_mode = TextServer.AUTOWRAP_WORD name_label.add_theme_font_size_override("font_size", 8) cell.add_child(name_label) # Quantity badge if count > 1: var badge = Panel.new() badge.size = Vector2(22, 22) badge.position = Vector2(CARD_WIDTH - 26, 4) var badge_style = StyleBoxFlat.new() badge_style.bg_color = Color(0.8, 0.6, 0.2, 0.95) badge_style.set_corner_radius_all(11) badge.add_theme_stylebox_override("panel", badge_style) cell.add_child(badge) var badge_label = Label.new() badge_label.text = "x%d" % count badge_label.set_anchors_preset(Control.PRESET_CENTER) badge_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER badge_label.add_theme_font_size_override("font_size", 10) badge.add_child(badge_label) # Remove button (X in top-left) var remove_btn = Button.new() remove_btn.text = "X" remove_btn.size = Vector2(20, 20) remove_btn.position = Vector2(4, 4) remove_btn.flat = true remove_btn.add_theme_font_size_override("font_size", 10) remove_btn.add_theme_color_override("font_color", Color(1.0, 0.5, 0.5)) remove_btn.add_theme_color_override("font_hover_color", Color(1.0, 0.3, 0.3)) remove_btn.pressed.connect(_on_remove_card.bind(card_id)) remove_btn.visible = false remove_btn.name = "RemoveBtn" cell.add_child(remove_btn) # Hover effects cell.mouse_entered.connect(func(): remove_btn.visible = true style.border_color = Color(0.6, 0.5, 0.3) cell.add_theme_stylebox_override("panel", style) ) cell.mouse_exited.connect(func(): remove_btn.visible = false style.border_color = Color(0.3, 0.3, 0.35) cell.add_theme_stylebox_override("panel", style) ) # Click to select cell.gui_input.connect(func(event): if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT: card_clicked.emit(card_id) ) return cell func _on_remove_card(card_id: String) -> void: card_removed.emit(card_id) func _on_clear_pressed() -> void: deck_cleared.emit() ## Refresh display for a specific card (when count changes) func refresh_card(card_id: String) -> void: _refresh_display()