using Dominion.NET_WPF.Enums; using Dominion.NET_WPF.ViewModel; using DominionBase.Cards; using DominionBase.Enums; using DominionBase.Utilities; using System; using System.Collections.Generic; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Media.Effects; namespace Dominion.NET_WPF.Controls { /// /// Interaction logic for CardStackControl.xaml /// public partial class CardStackControl : UserControl, IDisposable { public static readonly RoutedEvent CardStackControlClickEvent = EventManager.RegisterRoutedEvent( "CardStackControlClick", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(CardStackControl)); public event RoutedEventHandler CardStackControlClick { add { AddHandler(CardStackControlClickEvent, value); } remove { RemoveHandler(CardStackControlClickEvent, value); } } public static readonly DependencyProperty AnchorPointProperty = DependencyProperty.Register( "AnchorPoint", typeof(Point), typeof(CardStackControl), new FrameworkPropertyMetadata(new Point(0, 0), FrameworkPropertyMetadataOptions.AffectsMeasure)); public Point AnchorPoint { get { return (Point)GetValue(AnchorPointProperty); } set { SetValue(AnchorPointProperty, value); } } public void InitAnchor() { LayoutUpdated -= CardStackControl_LayoutUpdated; LayoutUpdated += CardStackControl_LayoutUpdated; } private void CardStackControl_LayoutUpdated(object sender, EventArgs e) { var size = RenderSize; var ofs = new Point(size.Width / 2, size.Height / 2); if (Window.GetWindow(this) is WMain window) AnchorPoint = TransformToVisual(window).Transform(ofs); } private IEnumerable _cardCollection = new List(); private DominionBase.TokenCollection _tokens = new DominionBase.TokenCollection(); private CardLabelCollection _cardLabels = new CardLabelCollection(); //private DominionBase.Cards.CardCollection _CardCollection = new DominionBase.Cards.CardCollection(); private int _glowSize; private DominionBase.IPlayer _player; private DominionBase.Players.PhaseEnum _playerPhase = DominionBase.Players.PhaseEnum.Action; private DominionBase.Players.PlayerMode _playerMode = DominionBase.Players.PlayerMode.Waiting; private CardSize _cardSize = CardSize.Medium; private HighlightMode _highlight; public bool IsCardsVisible { get; set; } public Card ClickedCard { get; private set; } public DominionBase.IPlayer Player { private get { return _player; } set { _player = value; UpdateCardDisplay(); } } public DominionBase.Players.PhaseEnum Phase { private get { return _playerPhase; } set { _playerPhase = value; UpdateGlowEffectAll(); } } public DominionBase.Players.PlayerMode PlayerMode { private get { return _playerMode; } set { _playerMode = value; UpdateGlowEffectAll(); } } public bool IsClickable { get { return bImages.IsEnabled; } set { if (value) { bImages.IsEnabled = bName.IsEnabled = true; bImages.Visibility = bName.Visibility = Visibility.Visible; Panel.SetZIndex(bImages, 1); Panel.SetZIndex(bName, 1); } else { bImages.IsEnabled = bName.IsEnabled = false; bImages.Visibility = bName.Visibility = Visibility.Collapsed; Panel.SetZIndex(bImages, -1); Panel.SetZIndex(bName, -1); } } } public CardSize CardSize { get { return _cardSize; } set { _cardSize = value; switch (_cardSize) { case CardSize.Text: case CardSize.SmallText: gImages.Visibility = Visibility.Collapsed; _glowSize = 0; break; case CardSize.Small: gImages.Visibility = Visibility.Visible; _glowSize = 10; break; case CardSize.Medium: gImages.Visibility = Visibility.Visible; _glowSize = 20; break; } dpMain.Margin = new Thickness(_glowSize / 4d, 0, _glowSize / 4d, 0); gImages.Margin = new Thickness(_glowSize / 2d); UpdateCardDisplay(); } } public HighlightMode Highlight { get { return _highlight; } set { _highlight = value; DropShadowEffect dse = null; switch (_highlight) { case HighlightMode.None: bMain.BorderBrush = Brushes.Transparent; break; case HighlightMode.Primary: dse = Caching.DropShadowRepository.GetDSE(_glowSize * 4, Colors.Crimson, 1.0, false); bMain.BorderBrush = Brushes.Crimson; break; case HighlightMode.Secondary: dse = Caching.DropShadowRepository.GetDSE(_glowSize * 4, Colors.Crimson, 1.0, false); bMain.BorderBrush = Brushes.Goldenrod; break; } if (IsCardsVisible && PileVisibility == DominionBase.Piles.Visibility.All) { var image = gImages.Children.OfType().LastOrDefault(); if (image != null) image.Effect = dse; lName.Effect = dse; } } } public void CountVPs(IEnumerable cards) { dpVPCount.Visibility = Visibility.Visible; if (_cardCollection.Any()) { var count = 0; if (_cardCollection.ElementAt(0) is DominionBase.IPoints cp) count = cp.ComputeVictoryPoints(Player, cards.OfType()); lVPCount.Content = count; lVP.Content = StringUtility.Plural("VP", count, false); } else lVPCount.Content = string.Empty; } public void HideVPs() { lVPCount.Content = string.Empty; dpVPCount.Visibility = Visibility.Collapsed; } public CardStackControl() { DataContext = new CardStackControlViewModel(); InitializeComponent(); IsClickable = false; if (WMain.Settings != null) { WMain.Settings.SettingsChanged += Settings_SettingsChanged; Settings_SettingsChanged(WMain.Settings, null); } } public void Dispose() { if (WMain.Settings != null) { WMain.Settings.SettingsChanged -= Settings_SettingsChanged; } } protected virtual void Settings_SettingsChanged(object sender, SettingsChangedEventArgs e) { if (sender is Settings settings) { if (settings.ToolTipShowDuration == ToolTipShowDuration.Off) ToolTipService.SetIsEnabled(this, false); else { ToolTipService.SetIsEnabled(this, true); ToolTipService.SetShowDuration(this, (int)settings.ToolTipShowDuration); } } } public IEnumerable CardCollection { get { return _cardCollection; } set { _cardCollection = value; UpdateCardDisplay(); } } public DominionBase.TokenCollection Tokens { get { return _tokens; } set { _tokens = value; UpdateCardDisplay(); } } public CardLabelCollection CardLabels { get { return _cardLabels; } set { _cardLabels = value; UpdateCardDisplay(); } } public string Title { get { return tbName.Text; } private set { tbName.Text = value; tbName.ToolTip = value; lName.Visibility = string.IsNullOrEmpty(value) ? Visibility.Hidden : Visibility.Visible; } } public DominionBase.Piles.Visibility PileVisibility { get; set; } public bool ExactCount { get; set; } public bool CascadePile { get; set; } = true; private void UpdateCardDisplay() { spExtraStuff.Visibility = Visibility.Visible; spExtraStuff.Children.Clear(); Tokens.ForEach(t => spExtraStuff.Children.Add(new ucTokenIcon { Token = t, Size = CardSize.Text })); if (spExtraStuff.Children.Count == 0) spExtraStuff.Visibility = Visibility.Collapsed; var stackOffset = new Size(); switch (CardSize) { case CardSize.Text: break; case CardSize.SmallText: dpName.Height = 16; lCount.Margin = new Thickness(0); lName.Padding = new Thickness(0); lName.Margin = new Thickness(0, 0, 2, 0); Padding = new Thickness(0); break; case CardSize.Small: stackOffset = new Size(12, 6); break; case CardSize.Medium: stackOffset = new Size(24, 10); break; } lCount.Visibility = Visibility.Collapsed; if (_cardCollection.Count() > 4 || (gImages.Visibility == Visibility.Collapsed && _cardCollection.Count() > 1)) { lCount.Visibility = Visibility.Visible; if (IsCardsVisible && PileVisibility == DominionBase.Piles.Visibility.All) lCount.Content = $"{_cardCollection.Count()}x"; stackOffset.Width = 0.75 * stackOffset.Width; } if (_cardCollection.Count() > 6 && stackOffset.Height > 0) { var verticalScale = 56f / (_cardCollection.Count() - 1); stackOffset = new Size((int)(0.625 * stackOffset.Width * verticalScale / stackOffset.Height), (int)verticalScale); } if (PileVisibility != DominionBase.Piles.Visibility.All) stackOffset.Width = 0; var startIndex = (CascadePile || !_cardCollection.Any()) ? 0 : (_cardCollection.Count() - 1); for (var index = startIndex; index < _cardCollection.Count(); index++) { var card = _cardCollection.ElementAt(index); if (card == null) continue; if ((IsCardsVisible && PileVisibility == DominionBase.Piles.Visibility.All || PileVisibility == DominionBase.Piles.Visibility.Top) && !(card is DominionBase.Cards.Universal.Blank)) { var category = card.Category; if (card is Card c) category = c.PhysicalCategory; lName.Background = Caching.BrushRepository.GetBackgroundBrush(category); lName.Foreground = Caching.BrushRepository.GetForegroundBrush(category); // Flip the foreground/background for Events if (category.HasFlag(Categories.Event)) { var t = lName.Foreground; lName.Foreground = lName.Background; lName.Background = t; } if (category.HasFlag(Categories.Reaction)) tbName.Effect = Caching.DropShadowRepository.GetDSE(8, Colors.White, 1d); } if (gImages.Visibility == Visibility.Visible) { var newImage = new Image(); var repo = Caching.ImageRepository.Acquire(); switch (CardSize) { case CardSize.Small: if (IsCardsVisible && (PileVisibility == DominionBase.Piles.Visibility.All || PileVisibility == DominionBase.Piles.Visibility.Top) && card.Type != DominionBase.Cards.Universal.TypeClass.Dummy && card.Type != DominionBase.Cards.Universal.TypeClass.DummyRed && card.Type != DominionBase.Cards.Universal.TypeClass.Blank) newImage.Source = repo.GetBitmapImage(card.ImageName, "small"); else { switch (card.CardBack) { case CardBack.Standard: newImage.Source = repo.GetBitmapImage("back", "small"); break; case CardBack.Red: newImage.Source = repo.GetBitmapImage("back_red", "small"); break; case CardBack.Blank: newImage.Source = repo.GetBitmapImage("blank", "small"); break; } } break; case CardSize.Medium: if (IsCardsVisible && (PileVisibility == DominionBase.Piles.Visibility.All || PileVisibility == DominionBase.Piles.Visibility.Top) && card.Type != DominionBase.Cards.Universal.TypeClass.Dummy && card.Type != DominionBase.Cards.Universal.TypeClass.DummyRed && card.Type != DominionBase.Cards.Universal.TypeClass.Blank) newImage.Source = repo.GetBitmapImage(card.ImageName, "medium"); else { switch (card.CardBack) { case CardBack.Standard: newImage.Source = repo.GetBitmapImage("back", "medium"); break; case CardBack.Red: newImage.Source = repo.GetBitmapImage("back_red", "medium"); break; case CardBack.Blank: newImage.Source = repo.GetBitmapImage("blank", "medium"); break; } } break; } Caching.ImageRepository.Release(); if (newImage.Source != null) { newImage.Width = newImage.Source.Width; newImage.Height = newImage.Source.Height; } newImage.HorizontalAlignment = HorizontalAlignment.Left; newImage.VerticalAlignment = VerticalAlignment.Top; newImage.Margin = new Thickness((index - startIndex) * stackOffset.Width, (index - startIndex) * stackOffset.Height, 0, 0); newImage.Tag = card; gImages.Children.Insert(gImages.Children.Count - 1, newImage); } } Title = string.Empty; if (_cardCollection.Any() && IsCardsVisible) { if (PileVisibility == DominionBase.Piles.Visibility.All || PileVisibility == DominionBase.Piles.Visibility.Top) { var card = _cardCollection.Last(); if (card is DominionBase.Cards.Universal.Blank) Title = "Empty"; else if (card != null) { if (card.Facing == Facing.FaceDown) { lStatus.Content = "(Face Down)"; lStatus.Visibility = Visibility.Visible; } else lStatus.Visibility = Visibility.Collapsed; lLabels.Content = string.Join(", ", CardLabels.Select(cl => cl.Name)); lLabels.Visibility = CardLabels.Any() ? Visibility.Visible : Visibility.Collapsed; Title = card.Name; if (_cardCollection.First() is DominionBase.Cards.Universal.Dummy || _cardCollection.First() is DominionBase.Cards.Universal.DummyRed) ttcCard.Card = card; else ttcCard.CardList = _cardCollection.Reverse(); } } } UpdateGlowEffectAll(); if (!IsCardsVisible || PileVisibility == DominionBase.Piles.Visibility.None) { var count = _cardCollection.Count(); if (count == 1 && _cardCollection.ElementAt(0) is DominionBase.Cards.Universal.Blank) Title = "Empty"; else if (ExactCount || count <= 1) Title = StringUtility.Plural("Card", count); } } private void UpdateGlowEffectAll() { if (IsCardsVisible && PileVisibility == DominionBase.Piles.Visibility.All) { var anyGlowAdded = false; foreach (var image in gImages.Children.OfType()) { var addGlow = image.Tag is Card card && PlayerMode == DominionBase.Players.PlayerMode.Normal && ( (Phase == DominionBase.Players.PhaseEnum.Action && ((card.Category.HasFlag(Categories.Action) && Player.Actions > 0) || card.Category.HasFlag(Categories.Treasure)) ) || ((Phase == DominionBase.Players.PhaseEnum.ActionTreasure || Phase == DominionBase.Players.PhaseEnum.BuyTreasure) && card.Category.HasFlag(Categories.Treasure)) || (Phase == DominionBase.Players.PhaseEnum.Night && card.Category.HasFlag(Categories.Night)) ); UpdateGlowEffect(image, addGlow, _glowSize, Colors.Black, Colors.MediumBlue); anyGlowAdded |= addGlow; } UpdateGlowEffect(lName, anyGlowAdded, _glowSize, Colors.Black, Colors.MediumBlue); } } private void UpdateGlowEffect(UIElement element, bool addGlow, int glowSize, Color color1, Color color2) { if (addGlow) { var dse = Caching.DropShadowRepository.GetDSE(glowSize, color1, 0.85, false); var ca = new ColorAnimation { From = color1, To = color2, Duration = TimeSpan.FromSeconds(1), AutoReverse = true, RepeatBehavior = RepeatBehavior.Forever, AccelerationRatio = 0.4, }; var da = new DoubleAnimation { From = glowSize * 2 / 3, To = glowSize * 1.5, Duration = TimeSpan.FromSeconds(1), AutoReverse = true, RepeatBehavior = RepeatBehavior.Forever, AccelerationRatio = 0.4, }; dse.BeginAnimation(DropShadowEffect.ColorProperty, ca); dse.BeginAnimation(DropShadowEffect.BlurRadiusProperty, da); element.Effect = dse; IsClickable = true; bImages.Cursor = bName.Cursor = Cursors.Hand; } else { element.Effect = null; IsClickable = false; bImages.Cursor = bName.Cursor = null; } } internal Control FindGameObject(DominionBase.IGameObject gameObject) { return CardCollection.Any(card => card.UniqueId == gameObject.UniqueId) ? this : null; } private void B_Click(object sender, RoutedEventArgs e) { var button = sender as Button; if (button != null) button.IsEnabled = false; ClickedCard = (gImages.Children[gImages.Children.Count - 2] as Image)?.Tag as Card; RaiseEvent(new RoutedEventArgs(CardStackControlClickEvent)); ClickedCard = null; if (button != null) button.IsEnabled = true; } private void UserControl_MouseDown(object sender, MouseButtonEventArgs e) { if (WMain.Settings == null || ((ToolTip as ToolTip)?.Content as ToolTipIDisplayable)?.Card == null || (((ToolTip)ToolTip).Content as ToolTipIDisplayable)?.Card.Type == DominionBase.Cards.Universal.TypeClass.Dummy || (((ToolTip)ToolTip).Content as ToolTipIDisplayable)?.Card.Type == DominionBase.Cards.Universal.TypeClass.DummyRed || (((ToolTip)ToolTip).Content as ToolTipIDisplayable)?.Card.Type == DominionBase.Cards.Universal.TypeClass.Blank) return; if (WMain.Settings.ShowToolTipOnRightClick && e.ChangedButton == MouseButton.Right && e.ButtonState == MouseButtonState.Pressed) { CaptureMouse(); ((ToolTip)ToolTip).IsOpen = true; e.Handled = true; } } private void UserControl_MouseUp(object sender, MouseButtonEventArgs e) { if (WMain.Settings == null || ToolTip == null) return; if (WMain.Settings.ShowToolTipOnRightClick && e.ChangedButton == MouseButton.Right && e.ButtonState == MouseButtonState.Released) { ReleaseMouseCapture(); if (ToolTip is ToolTip toolTip) toolTip.IsOpen = false; e.Handled = true; } } private void UserControl_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e) { if (ToolTip is ToolTip toolTip && toolTip.IsOpen) toolTip.IsOpen = false; } private void UserControl_MouseEnter(object sender, MouseEventArgs e) { CardHover.RaiseCardHoverEvent(this, new CardHoverEventArgs(ttcCard.Card?.DisplayCard)); } private void UserControl_StylusEnter(object sender, StylusEventArgs e) { CardHover.RaiseCardHoverEvent(this, new CardHoverEventArgs(ttcCard.Card?.DisplayCard)); } private void UserControl_TouchEnter(object sender, TouchEventArgs e) { CardHover.RaiseCardHoverEvent(this, new CardHoverEventArgs(ttcCard.Card?.DisplayCard)); } } }