Files
FFCardGame/scripts/ui/DeckListPanel.gd

353 lines
11 KiB
GDScript

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()