new features, play menu, deck builder, deck selection
This commit is contained in:
470
scripts/ui/DeckBuilder.gd
Normal file
470
scripts/ui/DeckBuilder.gd
Normal file
@@ -0,0 +1,470 @@
|
||||
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)
|
||||
Reference in New Issue
Block a user