Card UI
This commit is contained in:
parent
3d5aa8ade7
commit
c3b166112b
6 changed files with 153 additions and 94 deletions
|
|
@ -4,16 +4,27 @@ const CARD_WIDTH = 150
|
||||||
const CARD_HEIGHT = 250
|
const CARD_HEIGHT = 250
|
||||||
const CARD_MARGIN = 10
|
const CARD_MARGIN = 10
|
||||||
|
|
||||||
var cardDisplays = []
|
var cardDisplays = [] # Array of {panel: Node, card: Card}
|
||||||
var selectedCard = null
|
var selectedCard = null
|
||||||
var container: HBoxContainer
|
var container: HBoxContainer
|
||||||
|
var cardPreviewScene = preload("res://card_preview_panel.tscn")
|
||||||
|
var currently_hovered_card = null
|
||||||
|
# Preview card panel instance for hovering
|
||||||
|
var hoveredPreview: CardPreviewPanel = null
|
||||||
|
var isPreviewVisible = false
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
# Create the container first
|
# Create the container for the hand
|
||||||
container = HBoxContainer.new()
|
container = HBoxContainer.new()
|
||||||
container.name = "Hand"
|
container.name = "Hand"
|
||||||
container.position = Vector2(10, 500)
|
container.position = Vector2(10, 500)
|
||||||
|
container.custom_minimum_size = Vector2(0, CARD_HEIGHT)
|
||||||
add_child(container)
|
add_child(container)
|
||||||
|
|
||||||
|
# Create a hover preview panel that will follow the mouse
|
||||||
|
hoveredPreview = cardPreviewScene.instantiate()
|
||||||
|
hoveredPreview.visible = false
|
||||||
|
add_child(hoveredPreview)
|
||||||
|
|
||||||
func update_hand(hand: Array):
|
func update_hand(hand: Array):
|
||||||
clear_cards()
|
clear_cards()
|
||||||
|
|
@ -21,45 +32,83 @@ func update_hand(hand: Array):
|
||||||
add_card_display(card)
|
add_card_display(card)
|
||||||
|
|
||||||
func add_card_display(card):
|
func add_card_display(card):
|
||||||
var cardPanel = PanelContainer.new()
|
# Create a container for the card
|
||||||
cardPanel.custom_minimum_size = Vector2(CARD_WIDTH, CARD_HEIGHT)
|
var cardContainer = PanelContainer.new()
|
||||||
|
cardContainer.custom_minimum_size = Vector2(CARD_WIDTH, CARD_HEIGHT)
|
||||||
|
|
||||||
|
# Create a stylebox for selection highlighting
|
||||||
|
# var style = StyleBoxFlat.new()
|
||||||
|
# style.bg_color = Utils.DARK_CELL
|
||||||
|
# style.corner_radius_top_left = 5
|
||||||
|
# style.corner_radius_top_right = 5
|
||||||
|
# style.corner_radius_bottom_left = 5
|
||||||
|
# style.corner_radius_bottom_right = 5
|
||||||
|
# cardContainer.add_theme_stylebox_override("panel", style)
|
||||||
|
|
||||||
|
# Create a simplified card view
|
||||||
var vbox = VBoxContainer.new()
|
var vbox = VBoxContainer.new()
|
||||||
vbox.set_meta("card_id", card.id)
|
vbox.set_meta("card_id", card.id)
|
||||||
cardPanel.add_child(vbox)
|
cardContainer.add_child(vbox)
|
||||||
|
|
||||||
|
# Card name
|
||||||
var nameLabel = Label.new()
|
var nameLabel = Label.new()
|
||||||
nameLabel.text = card.cardName
|
nameLabel.text = card.cardName
|
||||||
nameLabel.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
nameLabel.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
|
nameLabel.add_theme_font_size_override("font_size", 16)
|
||||||
vbox.add_child(nameLabel)
|
vbox.add_child(nameLabel)
|
||||||
|
|
||||||
|
# Card rank with color
|
||||||
var rankLabel = Label.new()
|
var rankLabel = Label.new()
|
||||||
rankLabel.text = "Rank " + str(card.rank)
|
rankLabel.text = "Rank " + str(card.rank)
|
||||||
rankLabel.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
rankLabel.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
|
|
||||||
|
# Apply color based on rank
|
||||||
|
match card.rank:
|
||||||
|
Card.Rank.RANK_0:
|
||||||
|
rankLabel.add_theme_color_override("font_color", Color(0.9, 0.1, 0.1, 1.0)) # Red
|
||||||
|
Card.Rank.RANK_1:
|
||||||
|
rankLabel.add_theme_color_override("font_color", Color(0.9, 0.6, 0.1, 1.0)) # Orange
|
||||||
|
Card.Rank.RANK_2:
|
||||||
|
rankLabel.add_theme_color_override("font_color", Color(0.1, 0.7, 0.1, 1.0)) # Green
|
||||||
|
Card.Rank.RANK_3:
|
||||||
|
rankLabel.add_theme_color_override("font_color", Color(0.1, 0.7, 0.9, 1.0)) # Blue
|
||||||
|
|
||||||
vbox.add_child(rankLabel)
|
vbox.add_child(rankLabel)
|
||||||
|
|
||||||
|
# Card description (truncated)
|
||||||
var descLabel = Label.new()
|
var descLabel = Label.new()
|
||||||
descLabel.text = card.description
|
descLabel.text = card.description
|
||||||
|
if descLabel.text.length() > 50:
|
||||||
|
descLabel.text = descLabel.text.substr(0, 50) + "..."
|
||||||
descLabel.autowrap_mode = TextServer.AUTOWRAP_WORD
|
descLabel.autowrap_mode = TextServer.AUTOWRAP_WORD
|
||||||
descLabel.custom_minimum_size = Vector2(CARD_WIDTH - 10, 0)
|
descLabel.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
|
descLabel.custom_minimum_size = Vector2(CARD_WIDTH - 20, 0)
|
||||||
vbox.add_child(descLabel)
|
vbox.add_child(descLabel)
|
||||||
|
|
||||||
var unitWhitelistLabel = Label.new()
|
# Unit whitelist
|
||||||
unitWhitelistLabel.text = ", ".join(card.unitWhitelist)
|
var unitLabel = Label.new()
|
||||||
unitWhitelistLabel.autowrap_mode = TextServer.AUTOWRAP_WORD
|
if card.unitWhitelist.is_empty():
|
||||||
unitWhitelistLabel.custom_minimum_size = Vector2(CARD_WIDTH - 10, 0)
|
unitLabel.text = "Any piece"
|
||||||
vbox.add_child(unitWhitelistLabel)
|
else:
|
||||||
|
unitLabel.text = ", ".join(card.unitWhitelist)
|
||||||
var idLabel = Label.new()
|
unitLabel.autowrap_mode = TextServer.AUTOWRAP_WORD
|
||||||
idLabel.text = card.id.substr(card.id.length() - 8, -1)
|
unitLabel.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
idLabel.autowrap_mode = true
|
unitLabel.custom_minimum_size = Vector2(CARD_WIDTH - 20, 0)
|
||||||
idLabel.custom_minimum_size = Vector2(CARD_WIDTH - 10, 0)
|
vbox.add_child(unitLabel)
|
||||||
vbox.add_child(idLabel)
|
|
||||||
|
|
||||||
cardPanel.gui_input.connect(func(event): _on_card_clicked(event, card))
|
|
||||||
|
|
||||||
cardDisplays.append(cardPanel)
|
# Connect signals
|
||||||
container.add_child(cardPanel)
|
cardContainer.gui_input.connect(func(event): _on_card_clicked(event, card))
|
||||||
|
|
||||||
|
# Connect hover signals for preview
|
||||||
|
cardContainer.mouse_entered.connect(func(): _on_card_mouse_entered(card))
|
||||||
|
cardContainer.mouse_exited.connect(func(): _on_card_mouse_exited())
|
||||||
|
|
||||||
|
# Add to display list
|
||||||
|
cardDisplays.append({
|
||||||
|
"panel": cardContainer,
|
||||||
|
"card": card
|
||||||
|
})
|
||||||
|
container.add_child(cardContainer)
|
||||||
|
|
||||||
func _on_card_clicked(event: InputEvent, card: Card):
|
func _on_card_clicked(event: InputEvent, card: Card):
|
||||||
if event is InputEventMouseButton and event.pressed:
|
if event is InputEventMouseButton and event.pressed:
|
||||||
|
|
@ -70,29 +119,71 @@ func _on_card_clicked(event: InputEvent, card: Card):
|
||||||
selectedCard = null
|
selectedCard = null
|
||||||
highlight_selectedCard(selectedCard)
|
highlight_selectedCard(selectedCard)
|
||||||
|
|
||||||
|
func _on_card_mouse_entered(card: Card):
|
||||||
|
# Show card preview when hovering
|
||||||
|
currently_hovered_card = card
|
||||||
|
hoveredPreview.preview_card(card)
|
||||||
|
hoveredPreview.visible = true
|
||||||
|
|
||||||
|
# Position it above the hand area
|
||||||
|
var mouse_pos = get_viewport().get_mouse_position()
|
||||||
|
hoveredPreview.position = Vector2(mouse_pos.x - hoveredPreview.size.x/2, mouse_pos.y - hoveredPreview.size.y - 20)
|
||||||
|
|
||||||
|
# Make sure it's visible within screen bounds
|
||||||
|
hoveredPreview.position.x = clamp(hoveredPreview.position.x, 10, get_viewport_rect().size.x - hoveredPreview.size.x - 10)
|
||||||
|
hoveredPreview.position.y = clamp(hoveredPreview.position.y, 10, get_viewport_rect().size.y - hoveredPreview.size.y - 10)
|
||||||
|
|
||||||
|
isPreviewVisible = true
|
||||||
|
|
||||||
|
func _on_card_mouse_exited():
|
||||||
|
# Only hide the preview after a delay and if we're not hovering over a new card
|
||||||
|
var exited_card = currently_hovered_card
|
||||||
|
currently_hovered_card = null
|
||||||
|
|
||||||
|
# Wait a bit before hiding
|
||||||
|
await get_tree().create_timer(0.2).timeout
|
||||||
|
|
||||||
|
# Only hide if we're not hovering over a new card
|
||||||
|
if exited_card == currently_hovered_card or currently_hovered_card == null:
|
||||||
|
hoveredPreview.visible = false
|
||||||
|
isPreviewVisible = false
|
||||||
|
|
||||||
|
func _process(delta):
|
||||||
|
# If preview is visible, update its position to follow the mouse
|
||||||
|
if isPreviewVisible:
|
||||||
|
var mouse_pos = get_viewport().get_mouse_position()
|
||||||
|
hoveredPreview.position = Vector2(mouse_pos.x - hoveredPreview.size.x/2, mouse_pos.y - hoveredPreview.size.y - 20)
|
||||||
|
hoveredPreview.position.x = clamp(hoveredPreview.position.x, 10, get_viewport_rect().size.x - hoveredPreview.size.x - 10)
|
||||||
|
hoveredPreview.position.y = clamp(hoveredPreview.position.y, 10, get_viewport_rect().size.y - hoveredPreview.size.y - 10)
|
||||||
|
|
||||||
func highlight_selectedCard(selected: Card) -> void:
|
func highlight_selectedCard(selected: Card) -> void:
|
||||||
for display in cardDisplays:
|
for card_data in cardDisplays:
|
||||||
|
var panel = card_data.panel
|
||||||
var style = StyleBoxFlat.new()
|
var style = StyleBoxFlat.new()
|
||||||
style.bg_color = Utils.DARK_CELL # Default color
|
style.bg_color = Utils.DARK_CELL # Default color
|
||||||
|
|
||||||
var vbox = display.get_child(0)
|
# Style corners
|
||||||
if selected && vbox.get_meta("card_id") == selected.id:
|
style.corner_radius_top_left = 5
|
||||||
|
style.corner_radius_top_right = 5
|
||||||
|
style.corner_radius_bottom_left = 5
|
||||||
|
style.corner_radius_bottom_right = 5
|
||||||
|
|
||||||
|
if selected && card_data.card.id == selected.id:
|
||||||
style.bg_color = Color(0.4, 0.6, 0.4, 1) # Selected color
|
style.bg_color = Color(0.4, 0.6, 0.4, 1) # Selected color
|
||||||
|
|
||||||
display.add_theme_stylebox_override("panel", style)
|
panel.add_theme_stylebox_override("panel", style)
|
||||||
|
|
||||||
func get_card_by_uuid(uuid: String) -> Card:
|
func get_card_by_uuid(uuid: String) -> Card:
|
||||||
for display in cardDisplays:
|
for card_data in cardDisplays:
|
||||||
var vbox = display.get_child(0)
|
if card_data.card.id == uuid:
|
||||||
if vbox.get_meta("card_id") == uuid:
|
return card_data.card
|
||||||
return selectedCard
|
|
||||||
return null
|
return null
|
||||||
|
|
||||||
func getSelectedCard() -> Card:
|
func getSelectedCard() -> Card:
|
||||||
return selectedCard
|
return selectedCard
|
||||||
|
|
||||||
func clear_cards():
|
func clear_cards():
|
||||||
for display in cardDisplays:
|
for card_data in cardDisplays:
|
||||||
display.queue_free()
|
card_data.panel.queue_free()
|
||||||
cardDisplays.clear()
|
cardDisplays.clear()
|
||||||
selectedCard = null
|
selectedCard = null
|
||||||
|
|
@ -6,88 +6,55 @@ const PREVIEW_HEIGHT = 200
|
||||||
const SCREEN_MARGIN = 20
|
const SCREEN_MARGIN = 20
|
||||||
|
|
||||||
var currentCard: Card = null
|
var currentCard: Card = null
|
||||||
var previewPanel: PanelContainer
|
var previewPanel: CardPreviewPanel
|
||||||
|
|
||||||
|
var movesRemainingLabel: Label
|
||||||
func _init() -> void:
|
func _init() -> void:
|
||||||
|
|
||||||
previewPanel = PanelContainer.new()
|
|
||||||
previewPanel.custom_minimum_size = Vector2(PREVIEW_WIDTH, PREVIEW_HEIGHT)
|
previewPanel = preload("res://card_preview_panel.tscn").instantiate()
|
||||||
|
|
||||||
|
|
||||||
position = Vector2(0, 0)
|
position = Vector2(0, 0)
|
||||||
|
|
||||||
var vbox = VBoxContainer.new()
|
|
||||||
previewPanel.add_child(vbox)
|
|
||||||
|
|
||||||
var style = StyleBoxFlat.new()
|
|
||||||
style.bg_color = Color(0.2, 0.2, 0.2, 0.9)
|
|
||||||
style.corner_radius_top_left = 10
|
|
||||||
style.corner_radius_top_right = 10
|
|
||||||
style.corner_radius_bottom_left = 10
|
|
||||||
style.corner_radius_bottom_right = 10
|
|
||||||
previewPanel.add_theme_stylebox_override("panel", style)
|
|
||||||
|
|
||||||
previewPanel.hide()
|
previewPanel.hide()
|
||||||
|
|
||||||
|
# Add the preview panel as a child
|
||||||
add_child(previewPanel)
|
add_child(previewPanel)
|
||||||
|
|
||||||
|
# Add a custom label for moves remaining that we'll show/hide as needed
|
||||||
|
movesRemainingLabel = Label.new()
|
||||||
|
movesRemainingLabel.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
|
movesRemainingLabel.add_theme_font_size_override("font_size", 14)
|
||||||
|
movesRemainingLabel.visible = false
|
||||||
|
|
||||||
|
# Add the label to the preview panel's content
|
||||||
|
var content = previewPanel.get_node("CardContent")
|
||||||
|
if content:
|
||||||
|
content.add_child(movesRemainingLabel)
|
||||||
|
|
||||||
func show_card_preview(card: Card) -> void:
|
func show_card_preview(card: Card) -> void:
|
||||||
if card == null:
|
if card == null:
|
||||||
previewPanel.hide()
|
hide_preview()
|
||||||
currentCard = null
|
|
||||||
return
|
return
|
||||||
|
|
||||||
currentCard = card
|
currentCard = card
|
||||||
var vbox = previewPanel.get_child(0)
|
|
||||||
|
|
||||||
for child in vbox.get_children():
|
# Use the preview_card method from CardPreviewPanel
|
||||||
child.queue_free()
|
previewPanel.preview_card(card)
|
||||||
|
|
||||||
var nameLabel = Label.new()
|
# Since we're showing a new card, hide the moves remaining label
|
||||||
nameLabel.text = card.cardName
|
movesRemainingLabel.visible = false
|
||||||
nameLabel.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
|
||||||
nameLabel.add_theme_font_size_override("font_size", 18)
|
|
||||||
vbox.add_child(nameLabel)
|
|
||||||
|
|
||||||
var rankLabel = Label.new()
|
|
||||||
rankLabel.text = "Rank " + str(card.rank)
|
|
||||||
rankLabel.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
|
||||||
vbox.add_child(rankLabel)
|
|
||||||
|
|
||||||
var idLabel = Label.new()
|
|
||||||
idLabel.text = "ID: " + card.id.substr(card.id.length() - 8, -1)
|
|
||||||
idLabel.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
|
||||||
vbox.add_child(idLabel)
|
|
||||||
|
|
||||||
var durationLabel = Label.new()
|
|
||||||
durationLabel.text = str(card.remaining_turns) + " turns remaining"
|
|
||||||
durationLabel.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
|
||||||
vbox.add_child(durationLabel)
|
|
||||||
|
|
||||||
var descLabel = Label.new()
|
|
||||||
descLabel.text = card.description
|
|
||||||
descLabel.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
|
||||||
descLabel.autowrap_mode = TextServer.AUTOWRAP_WORD
|
|
||||||
descLabel.custom_minimum_size = Vector2(PREVIEW_WIDTH - 20, 0)
|
|
||||||
vbox.add_child(descLabel)
|
|
||||||
|
|
||||||
previewPanel.show()
|
|
||||||
|
|
||||||
func hide_preview() -> void:
|
func hide_preview() -> void:
|
||||||
previewPanel.hide()
|
previewPanel.hide_preview()
|
||||||
currentCard = null
|
currentCard = null
|
||||||
|
movesRemainingLabel.visible = false
|
||||||
|
|
||||||
func update_moves_remaining(moves: int) -> void:
|
func update_moves_remaining(moves: int) -> void:
|
||||||
if currentCard == null:
|
if currentCard == null:
|
||||||
return
|
return
|
||||||
|
|
||||||
var vbox = previewPanel.get_child(0)
|
|
||||||
var movesLabel = Label.new()
|
|
||||||
movesLabel.text = "Moves remaining this turn: " + str(moves)
|
|
||||||
movesLabel.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
|
||||||
|
|
||||||
# Remove old moves label if it exists
|
# Update and show the moves remaining label
|
||||||
for child in vbox.get_children():
|
movesRemainingLabel.text = "Moves remaining this turn: " + str(moves)
|
||||||
if child.text.begins_with("Moves remaining this turn:"):
|
movesRemainingLabel.visible = true
|
||||||
child.queue_free()
|
|
||||||
|
|
||||||
vbox.add_child(movesLabel)
|
|
||||||
|
|
@ -80,4 +80,4 @@ func preview_card(card: Card):
|
||||||
visible = true
|
visible = true
|
||||||
|
|
||||||
func hide_preview():
|
func hide_preview():
|
||||||
visible = false
|
visible = false
|
||||||
|
|
|
||||||
|
|
@ -257,7 +257,7 @@ func setupUI() -> void:
|
||||||
var control_container = VBoxContainer.new()
|
var control_container = VBoxContainer.new()
|
||||||
control_container.name = "ZoomControls"
|
control_container.name = "ZoomControls"
|
||||||
|
|
||||||
control_container.position = Vector2(windowXSize - 350, windowYSize - 120)
|
control_container.position = Vector2(windowXSize - 100, windowYSize - 80)
|
||||||
|
|
||||||
var h_container = HBoxContainer.new()
|
var h_container = HBoxContainer.new()
|
||||||
h_container.add_child(zoom_out_button)
|
h_container.add_child(zoom_out_button)
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ signal back_pressed
|
||||||
@onready var card_preview = $CardPreviewPanel
|
@onready var card_preview = $CardPreviewPanel
|
||||||
|
|
||||||
# Default deck size (can be overridden via options)
|
# Default deck size (can be overridden via options)
|
||||||
var maxDeckSize = 35
|
var maxDeckSize = 40
|
||||||
var deckManager = null
|
var deckManager = null
|
||||||
var bankCards = [] # Cards available but not in deck
|
var bankCards = [] # Cards available but not in deck
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ horizontal_alignment = 1
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="RankLabel" type="Label" parent="CardContent"]
|
[node name="RankLabel" type="Label" parent="CardContent"]
|
||||||
|
custom_minimum_size = Vector2(0, 30)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
theme_override_font_sizes/font_size = 18
|
theme_override_font_sizes/font_size = 18
|
||||||
text = "Rank 3 (Reused)"
|
text = "Rank 3 (Reused)"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue