new features, play menu, deck builder, deck selection

This commit is contained in:
2026-01-28 20:22:09 -05:00
parent f4c7bab6b0
commit bf9aa3fa23
80 changed files with 4501 additions and 58 deletions

View File

@@ -0,0 +1,355 @@
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)