class_name CardDetailViewer extends Control ## CardDetailViewer - Left panel showing enlarged card with details and add-to-deck controls signal add_to_deck_requested(card: CardDatabase.CardData, quantity: int) signal card_info_requested(card: CardDatabase.CardData) const CARD_WIDTH: float = 405.0 const CARD_HEIGHT: float = 567.0 const PANEL_WIDTH: float = 450.0 var current_card: CardDatabase.CardData = null var current_deck_count: int = 0 var quantity_to_add: int = 1 # UI elements var card_image: TextureRect var fallback_rect: ColorRect var fallback_label: Label var name_label: Label var type_label: Label var element_label: Label var cost_label: Label var power_label: Label var job_label: Label var category_label: Label var abilities_label: Label var quantity_label: Label var decrease_btn: Button var increase_btn: Button var add_button: Button var no_card_label: Label func _ready() -> void: custom_minimum_size = Vector2(PANEL_WIDTH, 0) _create_ui() func _create_ui() -> void: # Main panel 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", 12) panel.add_child(main_vbox) # Card image container var image_container = Control.new() image_container.custom_minimum_size = Vector2(CARD_WIDTH, CARD_HEIGHT) main_vbox.add_child(image_container) # Actual card image card_image = TextureRect.new() card_image.set_anchors_preset(Control.PRESET_FULL_RECT) card_image.expand_mode = TextureRect.EXPAND_FIT_WIDTH_PROPORTIONAL card_image.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED image_container.add_child(card_image) # Fallback colored rect (when no image) fallback_rect = ColorRect.new() fallback_rect.set_anchors_preset(Control.PRESET_FULL_RECT) fallback_rect.visible = false image_container.add_child(fallback_rect) fallback_label = Label.new() fallback_label.set_anchors_preset(Control.PRESET_CENTER) fallback_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER fallback_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER fallback_label.add_theme_font_size_override("font_size", 18) fallback_label.visible = false image_container.add_child(fallback_label) # No card selected label no_card_label = Label.new() no_card_label.text = "Select a card to view details" no_card_label.set_anchors_preset(Control.PRESET_CENTER) no_card_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER no_card_label.add_theme_color_override("font_color", Color(0.5, 0.5, 0.5)) image_container.add_child(no_card_label) # Card info section var info_vbox = VBoxContainer.new() info_vbox.add_theme_constant_override("separation", 4) main_vbox.add_child(info_vbox) name_label = _create_info_label("", 20, Color(1.0, 0.95, 0.8)) info_vbox.add_child(name_label) var details_grid = GridContainer.new() details_grid.columns = 2 details_grid.add_theme_constant_override("h_separation", 12) details_grid.add_theme_constant_override("v_separation", 4) info_vbox.add_child(details_grid) type_label = _create_info_label("") cost_label = _create_info_label("") element_label = _create_info_label("") power_label = _create_info_label("") job_label = _create_info_label("") category_label = _create_info_label("") details_grid.add_child(_create_info_label("Type:", 14, Color(0.6, 0.6, 0.6))) details_grid.add_child(type_label) details_grid.add_child(_create_info_label("Cost:", 14, Color(0.6, 0.6, 0.6))) details_grid.add_child(cost_label) details_grid.add_child(_create_info_label("Element:", 14, Color(0.6, 0.6, 0.6))) details_grid.add_child(element_label) details_grid.add_child(_create_info_label("Power:", 14, Color(0.6, 0.6, 0.6))) details_grid.add_child(power_label) details_grid.add_child(_create_info_label("Job:", 14, Color(0.6, 0.6, 0.6))) details_grid.add_child(job_label) details_grid.add_child(_create_info_label("Category:", 14, Color(0.6, 0.6, 0.6))) details_grid.add_child(category_label) # Abilities section var abilities_header = _create_info_label("Abilities", 16, Color(0.8, 0.75, 0.6)) info_vbox.add_child(abilities_header) var abilities_scroll = ScrollContainer.new() abilities_scroll.custom_minimum_size = Vector2(0, 80) abilities_scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED info_vbox.add_child(abilities_scroll) abilities_label = Label.new() abilities_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART abilities_label.add_theme_font_size_override("font_size", 12) abilities_label.add_theme_color_override("font_color", Color(0.8, 0.8, 0.8)) abilities_scroll.add_child(abilities_label) # Quantity selector var qty_hbox = HBoxContainer.new() qty_hbox.add_theme_constant_override("separation", 10) qty_hbox.alignment = BoxContainer.ALIGNMENT_CENTER main_vbox.add_child(qty_hbox) quantity_label = Label.new() quantity_label.text = "In deck: 0/3" quantity_label.add_theme_font_size_override("font_size", 14) qty_hbox.add_child(quantity_label) decrease_btn = _create_quantity_button("-") decrease_btn.pressed.connect(_on_decrease_quantity) qty_hbox.add_child(decrease_btn) var qty_display = Label.new() qty_display.text = "1" qty_display.name = "QtyDisplay" qty_display.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER qty_display.custom_minimum_size = Vector2(30, 0) qty_hbox.add_child(qty_display) increase_btn = _create_quantity_button("+") increase_btn.pressed.connect(_on_increase_quantity) qty_hbox.add_child(increase_btn) # Add to deck button add_button = _create_styled_button("Add to Deck") add_button.pressed.connect(_on_add_to_deck) main_vbox.add_child(add_button) # Initial state _update_ui_state() 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 = 15 style.content_margin_right = 15 style.content_margin_top = 15 style.content_margin_bottom = 15 return style func _create_info_label(text: String, font_size: int = 14, color: Color = Color(0.9, 0.9, 0.9)) -> Label: var label = Label.new() label.text = text label.add_theme_font_size_override("font_size", font_size) label.add_theme_color_override("font_color", color) return label func _create_quantity_button(text: String) -> Button: var button = Button.new() button.text = text button.custom_minimum_size = Vector2(36, 36) 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.4) 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", 18) return button func _create_styled_button(text: String) -> Button: var button = Button.new() button.text = text button.custom_minimum_size = Vector2(0, 44) var style_normal = StyleBoxFlat.new() style_normal.bg_color = Color(0.25, 0.25, 0.3) style_normal.border_color = Color(0.5, 0.5, 0.6) style_normal.set_border_width_all(1) style_normal.set_corner_radius_all(5) button.add_theme_stylebox_override("normal", style_normal) var style_hover = StyleBoxFlat.new() style_hover.bg_color = Color(0.35, 0.35, 0.45) style_hover.border_color = Color(0.7, 0.6, 0.3) style_hover.set_border_width_all(2) style_hover.set_corner_radius_all(5) button.add_theme_stylebox_override("hover", style_hover) var style_disabled = StyleBoxFlat.new() style_disabled.bg_color = Color(0.15, 0.15, 0.18) style_disabled.border_color = Color(0.3, 0.3, 0.35) style_disabled.set_border_width_all(1) style_disabled.set_corner_radius_all(5) button.add_theme_stylebox_override("disabled", style_disabled) button.add_theme_font_size_override("font_size", 16) return button ## Show a card in the detail viewer func show_card(card: CardDatabase.CardData, deck_count: int = 0) -> void: current_card = card current_deck_count = deck_count quantity_to_add = 1 _update_ui_state() ## Update deck count for current card func update_deck_count(count: int) -> void: current_deck_count = count _update_ui_state() ## Clear the detail viewer func clear() -> void: current_card = null current_deck_count = 0 _update_ui_state() func _update_ui_state() -> void: var has_card = current_card != null no_card_label.visible = not has_card card_image.visible = has_card add_button.disabled = not has_card if not has_card: name_label.text = "" type_label.text = "" cost_label.text = "" element_label.text = "" power_label.text = "" job_label.text = "" category_label.text = "" abilities_label.text = "" quantity_label.text = "In deck: 0/3" fallback_rect.visible = false fallback_label.visible = false return # Load card image var texture = CardDatabase.get_card_texture(current_card) if texture: card_image.texture = texture card_image.visible = true fallback_rect.visible = false fallback_label.visible = false else: card_image.visible = false fallback_rect.visible = true fallback_label.visible = true fallback_rect.color = Enums.element_to_color(current_card.get_primary_element()) fallback_label.text = current_card.name # Update info labels name_label.text = current_card.name type_label.text = Enums.card_type_to_string(current_card.type) cost_label.text = str(current_card.cost) var elements_str = "" for i in range(current_card.elements.size()): if i > 0: elements_str += " / " elements_str += Enums.element_to_string(current_card.elements[i]) element_label.text = elements_str power_label.text = str(current_card.power) if current_card.power > 0 else "-" job_label.text = current_card.job if not current_card.job.is_empty() else "-" category_label.text = current_card.category if not current_card.category.is_empty() else "-" # Update abilities var abilities_text = "" for ability in current_card.abilities: if not abilities_text.is_empty(): abilities_text += "\n\n" var ability_type = Enums.ability_type_to_string(ability.type) abilities_text += "[%s]" % ability_type if not ability.trigger.is_empty(): abilities_text += " %s:" % ability.trigger abilities_text += " %s" % ability.effect abilities_label.text = abilities_text if not abilities_text.is_empty() else "No abilities" # Update quantity display var max_addable = Deck.MAX_COPIES - current_deck_count quantity_label.text = "In deck: %d/%d" % [current_deck_count, Deck.MAX_COPIES] var qty_display = get_node_or_null("PanelContainer/VBoxContainer/HBoxContainer/QtyDisplay") if qty_display: qty_display.text = str(quantity_to_add) decrease_btn.disabled = quantity_to_add <= 1 increase_btn.disabled = quantity_to_add >= max_addable or max_addable <= 0 add_button.disabled = max_addable <= 0 func _on_decrease_quantity() -> void: if quantity_to_add > 1: quantity_to_add -= 1 _update_ui_state() func _on_increase_quantity() -> void: var max_addable = Deck.MAX_COPIES - current_deck_count if quantity_to_add < max_addable: quantity_to_add += 1 _update_ui_state() func _on_add_to_deck() -> void: if current_card: add_to_deck_requested.emit(current_card, quantity_to_add)