757 lines
23 KiB
C#
757 lines
23 KiB
C#
using Godot;
|
|
using RobotAndDonkey.Game.Cards;
|
|
using RobotAndDonkey.Game.Modifiers;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Linq;
|
|
using RobotAndDonkey.Game;
|
|
|
|
public partial class CardRow : Control
|
|
{
|
|
public override void _Ready()
|
|
{
|
|
CacheBackgroundPanels();
|
|
UpdateBackgroundVisibility();
|
|
}
|
|
|
|
public override void _Process(double delta)
|
|
{
|
|
if (s_CardPressActive && !Input.IsMouseButtonPressed(MouseButton.Left))
|
|
s_CardPressActive = false;
|
|
|
|
if (OrderedCards.Count == 0)
|
|
{
|
|
m_LayoutDirty = false;
|
|
return;
|
|
}
|
|
|
|
var needsLayout = m_LayoutDirty || GlobalPosition != m_LastPosition || Size != m_LastSize;
|
|
if (!needsLayout)
|
|
return;
|
|
|
|
m_LayoutDirty = false;
|
|
LayoutCards();
|
|
if (GetParent() is not Container)
|
|
SetAnchorsPreset(LayoutPreset.Center);
|
|
}
|
|
|
|
public override void _GuiInput(InputEvent @event)
|
|
{
|
|
if (@event is InputEventMouseButton mouseButton && mouseButton.ButtonIndex == MouseButton.Left && mouseButton.Pressed)
|
|
ClearSelection();
|
|
}
|
|
|
|
public void Configure(IReadOnlyList<Card> cards, Tween tween, bool celebrateModifiers, bool drag)
|
|
{
|
|
Debug.WriteLine($"Configure {cards.Count} cards, celebrate={celebrateModifiers}, drag={drag}");
|
|
if (CardScene == null)
|
|
{
|
|
GD.PushWarning($"{nameof(CardRow)} has no CardScene assigned. No cards will be shown.");
|
|
return;
|
|
}
|
|
|
|
if (cards.SequenceEqual(OrderedCards))
|
|
return;
|
|
|
|
var newSet = new HashSet<Guid>(cards.Select(c => c.CardId));
|
|
var toRemove = OrderedCards.Where(c => !newSet.Contains(c.CardId)).ToList();
|
|
|
|
foreach (var card in toRemove)
|
|
{
|
|
if (!CardToControl.Remove(card.CardId, out var control))
|
|
continue;
|
|
|
|
control.QueueFree();
|
|
}
|
|
|
|
OrderedCards.Clear();
|
|
OrderedCards.AddRange(cards);
|
|
|
|
var newControls = new List<CardControl>();
|
|
for (var index = 0; index < cards.Count; index++)
|
|
{
|
|
var card = cards[index];
|
|
if (!CardToControl.TryGetValue(card.CardId, out var control))
|
|
{
|
|
Debug.WriteLine($"Add new card {card.Id}");
|
|
control = CreateCardControl(card);
|
|
CardToControl.Add(card.CardId, control);
|
|
AddChild(control);
|
|
newControls.Add(control);
|
|
}
|
|
else
|
|
{
|
|
control.Configure(card, this);
|
|
}
|
|
}
|
|
|
|
LayoutCards();
|
|
foreach (var control in newControls)
|
|
control.SnapToDesired();
|
|
|
|
if (celebrateModifiers)
|
|
{
|
|
foreach (var card in cards)
|
|
{
|
|
foreach (var modifier in card.Modifiers)
|
|
{
|
|
ModifyCard(card, modifier.Id, tween);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void GetLayoutMetrics(Card card, out float visualWidth, out float leftOffset, out float topOffset, out float bottomOffset)
|
|
{
|
|
var size = CardSize;
|
|
var desiredScale = Vector2.One;
|
|
var desiredPivot = size / 2f;
|
|
|
|
if (CardToControl.TryGetValue(card.CardId, out var control))
|
|
{
|
|
size = control.Size;
|
|
desiredScale = control.DesiredScale;
|
|
desiredPivot = control.DesiredPivotOffset;
|
|
}
|
|
|
|
visualWidth = size.X * desiredScale.X;
|
|
leftOffset = (1f - desiredScale.X) * desiredPivot.X;
|
|
topOffset = (1f - desiredScale.Y) * desiredPivot.Y;
|
|
bottomOffset = topOffset + size.Y * desiredScale.Y;
|
|
}
|
|
|
|
private void CalculateCardPositions(IReadOnlyList<Card> cards)
|
|
{
|
|
m_CardPositions.Clear();
|
|
var count = cards.Count;
|
|
if (count == 0)
|
|
return;
|
|
|
|
var cardHeight = CardSize.Y;
|
|
|
|
var cardIds = new Guid[count];
|
|
var visualWidths = new float[count];
|
|
var leftOffsets = new float[count];
|
|
var topOffsets = new float[count];
|
|
var bottomOffsets = new float[count];
|
|
var totalVisualWidth = 0f;
|
|
|
|
for (var index = 0; index < count; index++)
|
|
{
|
|
var card = cards[index];
|
|
cardIds[index] = card.CardId;
|
|
|
|
GetLayoutMetrics(card, out var visualWidth, out var leftOffset, out var topOffset, out var bottomOffset);
|
|
|
|
visualWidths[index] = visualWidth;
|
|
leftOffsets[index] = leftOffset;
|
|
topOffsets[index] = topOffset;
|
|
bottomOffsets[index] = bottomOffset;
|
|
totalVisualWidth += visualWidth;
|
|
}
|
|
|
|
var availableWidth = Size.X > 0 ? Size.X : CardSize.X;
|
|
var availableHeight = Size.Y > 0 ? Size.Y : CardSize.Y;
|
|
|
|
if (availableWidth <= 0)
|
|
availableWidth = totalVisualWidth + (count - 1) * CardSpacing;
|
|
|
|
if (availableHeight <= 0)
|
|
availableHeight = cardHeight;
|
|
|
|
if (WrapCards)
|
|
{
|
|
var rowSpacing = CardSpacing;
|
|
var rowTopOffsets = new List<float>();
|
|
var rowBottomOffsets = new List<float>();
|
|
var rowIndices = new int[count];
|
|
|
|
var rowWidth = 0f;
|
|
var rowIndex = 0;
|
|
var rowHasCards = false;
|
|
var rowTopOffset = 0f;
|
|
var rowBottomOffset = 0f;
|
|
|
|
for (var index = 0; index < count; index++)
|
|
{
|
|
var cardWidthForLayout = visualWidths[index];
|
|
var spacing = rowWidth > 0f ? CardSpacing : 0f;
|
|
|
|
if (rowWidth > 0f && rowWidth + spacing + cardWidthForLayout > availableWidth)
|
|
{
|
|
rowTopOffsets.Add(rowTopOffset);
|
|
rowBottomOffsets.Add(rowBottomOffset);
|
|
rowIndex += 1;
|
|
rowWidth = 0f;
|
|
rowHasCards = false;
|
|
spacing = 0f;
|
|
}
|
|
|
|
if (!rowHasCards)
|
|
{
|
|
rowTopOffset = topOffsets[index];
|
|
rowBottomOffset = bottomOffsets[index];
|
|
rowHasCards = true;
|
|
}
|
|
else
|
|
{
|
|
rowTopOffset = Mathf.Min(rowTopOffset, topOffsets[index]);
|
|
rowBottomOffset = Mathf.Max(rowBottomOffset, bottomOffsets[index]);
|
|
}
|
|
|
|
rowIndices[index] = rowIndex;
|
|
rowWidth += spacing + cardWidthForLayout;
|
|
}
|
|
|
|
if (rowHasCards)
|
|
{
|
|
rowTopOffsets.Add(rowTopOffset);
|
|
rowBottomOffsets.Add(rowBottomOffset);
|
|
}
|
|
|
|
var rowTops = new float[rowTopOffsets.Count];
|
|
if (rowTops.Length > 0)
|
|
{
|
|
rowTops[0] = 0f;
|
|
for (var index = 1; index < rowTopOffsets.Count; index++)
|
|
{
|
|
var previousBottom = rowTops[index - 1] + rowBottomOffsets[index - 1];
|
|
rowTops[index] = previousBottom + rowSpacing - rowTopOffsets[index];
|
|
}
|
|
}
|
|
|
|
var currentRow = 0;
|
|
var currentVisualLeft = 0f;
|
|
|
|
for (var index = 0; index < count; index++)
|
|
{
|
|
var cardRowIndex = rowIndices[index];
|
|
if (cardRowIndex != currentRow)
|
|
{
|
|
currentRow = cardRowIndex;
|
|
currentVisualLeft = 0f;
|
|
}
|
|
|
|
if (currentVisualLeft > 0f)
|
|
currentVisualLeft += CardSpacing;
|
|
|
|
var positionX = currentVisualLeft - leftOffsets[index];
|
|
var positionY = rowTops[cardRowIndex];
|
|
m_CardPositions[cardIds[index]] = new Vector2(positionX, positionY);
|
|
|
|
currentVisualLeft += visualWidths[index];
|
|
}
|
|
|
|
if (rowTopOffsets.Count > 0)
|
|
{
|
|
var totalHeight = rowTops[^1] + rowBottomOffsets[^1];
|
|
CustomMinimumSize = new(0, totalHeight);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var spacing = CardSpacing;
|
|
var widthWithBaseSpacing = totalVisualWidth + (count - 1) * CardSpacing;
|
|
|
|
if (count > 1)
|
|
{
|
|
if (widthWithBaseSpacing > availableWidth)
|
|
{
|
|
spacing = (availableWidth - totalVisualWidth) / (count - 1);
|
|
}
|
|
}
|
|
|
|
var startVisualLeft = (availableWidth - widthWithBaseSpacing) * 0.5f;
|
|
var y = (availableHeight - cardHeight) * 0.5f;
|
|
|
|
var currentVisualLeft = startVisualLeft;
|
|
|
|
for (var index = 0; index < count; index++)
|
|
{
|
|
var positionX = currentVisualLeft - leftOffsets[index];
|
|
m_CardPositions[cardIds[index]] = new Vector2(positionX, y);
|
|
|
|
currentVisualLeft += visualWidths[index] + spacing;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void RunCard(Card card, Tween tween)
|
|
{
|
|
if (!CardToControl.Remove(card.CardId, out var control))
|
|
return;
|
|
|
|
Main.Instance.Music.Play(MusicManager.ESound.Card);
|
|
control.QueueFree();
|
|
OrderedCards.Remove(card);
|
|
LayoutCards();
|
|
}
|
|
|
|
public void ModifyCard(Card card, EModifierId modifier, Tween tween)
|
|
{
|
|
if (!CardToControl.TryGetValue(card.CardId, out var control))
|
|
{
|
|
Debug.WriteLine($"Failed to modify {card.Id}");
|
|
return;
|
|
}
|
|
|
|
var hasModifier = card.Modifiers.Any(m => m.Id == modifier && m.DebuffSources.Count == 0);
|
|
Debug.WriteLine($"Modify {card.Id}");
|
|
control.Configure(card, this);
|
|
if (hasModifier)
|
|
Main.Instance.Music.Play(modifier);
|
|
}
|
|
|
|
private CardControl CreateCardControl(Card card)
|
|
{
|
|
var node = CardScene.Instantiate<CardControl>();
|
|
node.Name = $"Card_{card.Id}";
|
|
|
|
node.SetAnchorsPreset(LayoutPreset.TopLeft);
|
|
node.Size = CardSize;
|
|
node.PivotOffset = CardSize / 2f;
|
|
node.CanDrag = CardsCanDrag;
|
|
node.CanSelect = CardsCanSelect;
|
|
node.Configure(card, this);
|
|
|
|
node.Connect(CardControl.SignalName.CardDroppedOn, new(this, nameof(OnCardDroppedOn)));
|
|
node.Connect(CardControl.SignalName.CardSelectionChanged, new(this, nameof(OnCardSelectionChanged)));
|
|
node.Connect(CardControl.SignalName.CardStateChanged, new(this, nameof(OnCardStateChanged)));
|
|
|
|
return node;
|
|
}
|
|
|
|
private void OnCardStateChanged(CardControl card)
|
|
{
|
|
m_LayoutDirty = true;
|
|
}
|
|
|
|
private void OnCardSelectionChanged(CardControl card, bool selected)
|
|
{
|
|
if (!m_SuppressSelectionValidation && CanChangeSelection != null && !CanChangeSelection(card, selected))
|
|
{
|
|
m_SuppressSelectionValidation = true;
|
|
card.Selected = !selected;
|
|
m_SuppressSelectionValidation = false;
|
|
return;
|
|
}
|
|
|
|
if (m_SuppressSelectionSignals)
|
|
return;
|
|
|
|
if (selected)
|
|
SelectionContext = this;
|
|
|
|
if (selected && !SupportsMultiSelect)
|
|
{
|
|
m_SuppressSelectionValidation = true;
|
|
foreach (var other in CardToControl.Values)
|
|
{
|
|
if (other != card && other.Selected)
|
|
other.Selected = false;
|
|
}
|
|
m_SuppressSelectionValidation = false;
|
|
}
|
|
|
|
EmitSignal(SignalName.SelectionChanged, CardToControl.Values.Where(c => c.Selected).ToArray());
|
|
}
|
|
|
|
private void OnCardDroppedOn(CardControl source, CardControl target, bool before)
|
|
{
|
|
if (source == target)
|
|
return;
|
|
|
|
var sourceRow = source.CardRow;
|
|
var targetRow = target.CardRow;
|
|
|
|
var fromCardId = sourceRow.CardToControl.FirstOrDefault(kv => kv.Value == source).Key;
|
|
var toCardId = targetRow.CardToControl.FirstOrDefault(kv => kv.Value == target).Key;
|
|
|
|
if (fromCardId == Guid.Empty || toCardId == Guid.Empty)
|
|
return;
|
|
|
|
var fromCard = sourceRow.OrderedCards.Single(c => c.CardId == fromCardId);
|
|
var toCard = OrderedCards.Single(c => c.CardId == toCardId);
|
|
var draggedCards = GetDraggedCards(sourceRow, fromCard);
|
|
|
|
if (draggedCards.Contains(toCard))
|
|
{
|
|
if (SelectedCards.Contains(toCard))
|
|
{
|
|
draggedCards = [fromCard];
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
var targetCards = targetRow.OrderedCards;
|
|
var toIndex = targetCards.IndexOf(toCard);
|
|
if (toIndex == -1)
|
|
return;
|
|
|
|
var insertIndex = before ? toIndex : toIndex + 1;
|
|
|
|
MoveCards(sourceRow, targetRow, draggedCards, insertIndex);
|
|
}
|
|
|
|
public override bool _CanDropData(Vector2 atPosition, Variant data)
|
|
{
|
|
if (Disabled || !CardsCanDrag)
|
|
return false;
|
|
|
|
if (data.Obj is not CardControl sourceControl)
|
|
return false;
|
|
|
|
var sourceRow = sourceControl.CardRow;
|
|
var fromCardId = sourceRow.CardToControl.FirstOrDefault(kv => kv.Value == sourceControl).Key;
|
|
if (fromCardId == Guid.Empty)
|
|
return false;
|
|
|
|
var fromCard = sourceRow.OrderedCards.Single(c => c.CardId == fromCardId);
|
|
var draggedCards = GetDraggedCards(sourceRow, fromCard);
|
|
if (draggedCards.Count == 0)
|
|
return false;
|
|
|
|
if (sourceRow != this && OccupiedSpace + draggedCards.Select(c => c.OccupiedSpace).Sum() > MaxCards)
|
|
return false;
|
|
|
|
sourceControl.UpdateDragPreviewForDropTarget(null);
|
|
return true;
|
|
}
|
|
|
|
public override void _DropData(Vector2 atPosition, Variant data)
|
|
{
|
|
if (Disabled || !CardsCanDrag)
|
|
return;
|
|
|
|
if (data.Obj is not CardControl sourceControl)
|
|
return;
|
|
|
|
var sourceRow = sourceControl.CardRow;
|
|
var fromCardId = sourceRow?.CardToControl.FirstOrDefault(kv => kv.Value == sourceControl).Key;
|
|
if (fromCardId == Guid.Empty)
|
|
return;
|
|
|
|
var fromCard = sourceRow.OrderedCards.Single(c => c.CardId == fromCardId);
|
|
var draggedCards = GetDraggedCards(sourceRow, fromCard);
|
|
if (draggedCards.Count == 0)
|
|
return;
|
|
|
|
if (OccupiedSpace + draggedCards.Select(c => c.OccupiedSpace).Sum() > MaxCards)
|
|
return;
|
|
|
|
var insertIndex = GetDropIndex(atPosition);
|
|
MoveCards(sourceRow, this, draggedCards, insertIndex);
|
|
}
|
|
|
|
private List<Card> GetDraggedCards(CardRow sourceRow, Card fromCard)
|
|
{
|
|
if (fromCard == null)
|
|
return [];
|
|
|
|
if (!sourceRow.SupportsMultiDrag)
|
|
return [fromCard];
|
|
|
|
var selected = sourceRow.SelectedCardIndices.Select(i => sourceRow.OrderedCards[i]).ToList();
|
|
if (selected.Count > 1 && selected.Contains(fromCard))
|
|
{
|
|
var originalOrder = sourceRow.OrderedCards;
|
|
return selected.OrderBy(c => originalOrder.IndexOf(c)).ToList();
|
|
}
|
|
|
|
return [fromCard];
|
|
}
|
|
|
|
private void MoveCards(CardRow sourceRow, CardRow targetRow, List<Card> draggedCards, int rawInsertIndex)
|
|
{
|
|
if (draggedCards == null || draggedCards.Count == 0)
|
|
return;
|
|
|
|
var draggedSet = new HashSet<Card>(draggedCards);
|
|
|
|
if (sourceRow == targetRow)
|
|
{
|
|
var original = sourceRow.OrderedCards.ToList();
|
|
if (original.Count == 0)
|
|
return;
|
|
|
|
var insertIndex = Mathf.Clamp(rawInsertIndex, 0, original.Count);
|
|
|
|
var removedBefore = 0;
|
|
foreach (var card in draggedCards)
|
|
{
|
|
var idx = original.IndexOf(card);
|
|
if (idx >= 0 && idx < insertIndex)
|
|
removedBefore++;
|
|
}
|
|
|
|
var adjustedIndex = insertIndex - removedBefore;
|
|
|
|
var newOrder = original.Where(c => !draggedSet.Contains(c)).ToList();
|
|
adjustedIndex = Mathf.Clamp(adjustedIndex, 0, newOrder.Count);
|
|
newOrder.InsertRange(adjustedIndex, draggedCards);
|
|
|
|
sourceRow.Configure(newOrder, null, false, true);
|
|
sourceRow.EmitSignal(SignalName.OrderChanged);
|
|
}
|
|
else
|
|
{
|
|
var sourceList = sourceRow.OrderedCards.ToList();
|
|
var targetList = targetRow.OrderedCards.ToList();
|
|
|
|
foreach (var card in draggedCards)
|
|
{
|
|
sourceList.Remove(card);
|
|
targetList.Remove(card);
|
|
}
|
|
|
|
var insertIndex = Mathf.Clamp(rawInsertIndex, 0, targetList.Count);
|
|
targetList.InsertRange(insertIndex, draggedCards);
|
|
|
|
sourceRow.Configure(sourceList, null, false, true);
|
|
targetRow.Configure(targetList, null, false, true);
|
|
targetRow.EmitSignal(SignalName.OrderChanged);
|
|
}
|
|
}
|
|
|
|
private int GetDropIndex(Vector2 localPosition)
|
|
{
|
|
var count = OrderedCards.Count;
|
|
if (count == 0)
|
|
return 0;
|
|
|
|
var orderedControls = OrderedCards.Select(card => CardToControl.GetValueOrDefault(card.CardId)).Where(control => control != null).ToList();
|
|
|
|
if (orderedControls.Count == 0)
|
|
return 0;
|
|
|
|
var mouseX = localPosition.X;
|
|
|
|
if (mouseX <= orderedControls[0].Position.X)
|
|
return 0;
|
|
|
|
var last = orderedControls[^1];
|
|
if (mouseX >= last.Position.X + CardSize.X)
|
|
return orderedControls.Count;
|
|
|
|
for (var i = 0; i < orderedControls.Count; i++)
|
|
{
|
|
var control = orderedControls[i];
|
|
if (mouseX < control.Position.X)
|
|
return i;
|
|
}
|
|
|
|
return orderedControls.Count;
|
|
}
|
|
|
|
private void LayoutCards()
|
|
{
|
|
Debug.WriteLine("Layout");
|
|
var count = OrderedCards.Count;
|
|
if (count == 0)
|
|
return;
|
|
|
|
m_LastPosition = GlobalPosition;
|
|
m_LastSize = Size;
|
|
|
|
CalculateCardPositions(OrderedCards);
|
|
|
|
foreach (var card in OrderedCards)
|
|
{
|
|
if (!CardToControl.TryGetValue(card.CardId, out var control))
|
|
continue;
|
|
|
|
if (m_CardPositions.TryGetValue(card.CardId, out var targetPos))
|
|
control.SetDesiredPosition(targetPos);
|
|
}
|
|
}
|
|
|
|
private void CacheBackgroundPanels()
|
|
{
|
|
m_Backgrounds.Clear();
|
|
|
|
foreach (var child in GetChildren())
|
|
{
|
|
if (child is Control control && control.Name.ToString().StartsWith("Background"))
|
|
{
|
|
control.ZIndex = 0;
|
|
control.MouseFilter = MouseFilterEnum.Ignore;
|
|
m_Backgrounds.Add(control);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void UpdateBackgroundVisibility()
|
|
{
|
|
if (m_Backgrounds.Count == 0)
|
|
return;
|
|
|
|
var clamped = Mathf.Clamp(BackgroundVariant, 0, m_Backgrounds.Count - 1);
|
|
for (var i = 0; i < m_Backgrounds.Count; i++)
|
|
m_Backgrounds[i].Visible = i == clamped;
|
|
}
|
|
|
|
public void ApplySelection(IReadOnlyCollection<Guid> selectedCardIds, bool suppressSignals)
|
|
{
|
|
m_SuppressSelectionValidation = true;
|
|
m_SuppressSelectionSignals = suppressSignals;
|
|
foreach (var kvp in CardToControl)
|
|
{
|
|
kvp.Value.Selected = selectedCardIds.Contains(kvp.Key);
|
|
}
|
|
m_SuppressSelectionSignals = false;
|
|
m_SuppressSelectionValidation = false;
|
|
}
|
|
|
|
public void ClearSelection()
|
|
{
|
|
ApplySelection([], true);
|
|
EmitSignal(SignalName.SelectionChanged, []);
|
|
}
|
|
|
|
public static List<Card> GetSortedCards(IEnumerable<Card> cards)
|
|
{
|
|
return cards.Order(Comparer<Card>.Create((a, b) =>
|
|
{
|
|
var cmp = a.Rarity.CompareTo(b.Rarity);
|
|
if (cmp == 0)
|
|
cmp = a.Id.CompareTo(b.Id);
|
|
if (cmp == 0)
|
|
cmp = a.Modifiers.Count.CompareTo(b.Modifiers.Count);
|
|
if (cmp == 0)
|
|
cmp = a.CardId.CompareTo(b.CardId);
|
|
return cmp;
|
|
})).ToList();
|
|
}
|
|
|
|
[Export]
|
|
public PackedScene CardScene { get; set; }
|
|
|
|
[Export]
|
|
public Vector2 CardSize { get; set; } = new(96, 96);
|
|
|
|
[Export]
|
|
public float CardSpacing { get; set; } = 8f;
|
|
|
|
[Export]
|
|
public float AnimationDuration { get; set; } = 0.15f;
|
|
|
|
[Export]
|
|
public bool CardsCanDrag { get; set; } = true;
|
|
|
|
[Export]
|
|
public bool CardsCanSelect { get; set; } = false;
|
|
|
|
[Export]
|
|
public bool SupportsMultiSelect { get; set; } = false;
|
|
|
|
[Export]
|
|
public bool SupportsMultiDrag { get; set; } = false;
|
|
|
|
[Export]
|
|
public int BackgroundVariant
|
|
{
|
|
get => m_BackgroundVariant;
|
|
set
|
|
{
|
|
if (m_BackgroundVariant == value)
|
|
return;
|
|
|
|
m_BackgroundVariant = value;
|
|
UpdateBackgroundVisibility();
|
|
}
|
|
}
|
|
|
|
public Func<CardControl, bool, bool> CanChangeSelection { get; set; }
|
|
|
|
public static bool IsCardPressActive => s_CardPressActive;
|
|
|
|
public static void NotifyCardPress(bool pressed)
|
|
{
|
|
s_CardPressActive = pressed;
|
|
}
|
|
|
|
|
|
public static CardRow SelectionContext
|
|
{
|
|
get => s_SelectionContext;
|
|
set
|
|
{
|
|
if (s_SelectionContext == value)
|
|
return;
|
|
|
|
if (s_SelectionContext != null)
|
|
{
|
|
foreach (var card in s_SelectionContext.CardToControl)
|
|
card.Value.Selected = false;
|
|
}
|
|
|
|
s_SelectionContext = value;
|
|
}
|
|
}
|
|
|
|
[Export]
|
|
public bool Disabled
|
|
{
|
|
get => m_Disabled;
|
|
set
|
|
{
|
|
if (m_Disabled == value)
|
|
return;
|
|
|
|
m_Disabled = value;
|
|
foreach (var card in CardToControl)
|
|
card.Value.Disabled = m_Disabled;
|
|
}
|
|
}
|
|
|
|
[Export]
|
|
public int MaxCards { get; set; } = int.MaxValue;
|
|
|
|
[Export]
|
|
public bool WrapCards { get; set; } = false;
|
|
|
|
public int OccupiedSpace => OrderedCards.Select(c => c.OccupiedSpace).Sum();
|
|
|
|
public IEnumerable<int> SelectedCardIndices =>
|
|
OrderedCards.Select((c, i) => (Card: CardToControl[c.CardId], Index: i)).Where(pair => pair.Card.Selected).Select(pair => pair.Index);
|
|
|
|
public IReadOnlyList<Guid> SelectedCardIds =>
|
|
OrderedCards.Where(card => CardToControl.TryGetValue(card.CardId, out var control) && control.Selected)
|
|
.Select(card => card.CardId)
|
|
.ToList();
|
|
|
|
public IReadOnlyList<Card> SelectedCards =>
|
|
OrderedCards.Where(card => CardToControl.TryGetValue(card.CardId, out var control) && control.Selected)
|
|
.ToList();
|
|
|
|
public List<Card> OrderedCards { get; } = [];
|
|
|
|
public Dictionary<Guid, CardControl> CardToControl => m_CardToControl;
|
|
|
|
[Signal]
|
|
public delegate void OrderChangedEventHandler();
|
|
|
|
[Signal]
|
|
public delegate void SelectionChangedEventHandler(CardControl[] selection);
|
|
|
|
private static CardRow s_SelectionContext;
|
|
|
|
private static bool s_CardPressActive;
|
|
|
|
private readonly List<Control> m_Backgrounds = [];
|
|
private readonly Dictionary<Guid, CardControl> m_CardToControl = new();
|
|
private readonly Dictionary<Guid, Vector2> m_CardPositions = [];
|
|
|
|
private int m_BackgroundVariant;
|
|
private bool m_Disabled;
|
|
private bool m_SuppressSelectionValidation;
|
|
private bool m_SuppressSelectionSignals;
|
|
private bool m_LayoutDirty;
|
|
private Vector2 m_LastPosition;
|
|
private Vector2 m_LastSize;
|
|
}
|