RiffleShuffleAnimation.cs 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. //-----------------------------------------------------------------------------
  2. // RiffleShuffleAnimation.cs
  3. //
  4. // Implements a classic riffle shuffle animation where the deck is split
  5. // in half and cards cascade together in an interleaving pattern
  6. //-----------------------------------------------------------------------------
  7. using System;
  8. using System.Collections.Generic;
  9. using Microsoft.Xna.Framework;
  10. using Microsoft.Xna.Framework.Graphics;
  11. namespace CardsFramework
  12. {
  13. /// <summary>
  14. /// Riffle shuffle: splits deck into two halves, then interleaves them
  15. /// with a cascading visual effect. This is the classic casino shuffle.
  16. /// </summary>
  17. public class RiffleShuffleAnimation : ShuffleAnimation
  18. {
  19. /// <summary>
  20. /// How far apart the two halves split (in pixels)
  21. /// </summary>
  22. public float SplitDistance { get; set; } = 120f;
  23. /// <summary>
  24. /// Maximum rotation angle for cards during cascade (in radians)
  25. /// </summary>
  26. public float MaxRotation { get; set; } = 0.15f;
  27. /// <summary>
  28. /// Height of the cascade arc
  29. /// </summary>
  30. public float CascadeHeight { get; set; } = 60f;
  31. /// <summary>
  32. /// Number of times to repeat the shuffle
  33. /// </summary>
  34. public int ShuffleCycles { get; set; } = 2;
  35. /// <summary>
  36. /// Creates a new riffle shuffle animation
  37. /// </summary>
  38. public RiffleShuffleAnimation(CardsGame cardGame, Vector2 position, TimeSpan duration, Vector2 cardSize)
  39. : base(cardGame, position, duration, cardSize)
  40. {
  41. }
  42. /// <summary>
  43. /// Creates the animated cards with riffle shuffle animations
  44. /// </summary>
  45. public override List<AnimatedCardsGameComponent> CreateAnimatedCards(
  46. List<TraditionalCard> deck,
  47. SpriteBatch spriteBatch,
  48. Matrix globalTransformation)
  49. {
  50. var animatedCards = new List<AnimatedCardsGameComponent>();
  51. // Only show a subset of cards for a clear visual effect (every 3rd card)
  52. int cardsToShow = Math.Min(18, deck.Count / 3); // Show ~18 cards max
  53. int step = deck.Count / cardsToShow;
  54. // Split into two halves
  55. int halfPoint = cardsToShow / 2;
  56. // Time calculations for animation phases
  57. TimeSpan splitDuration = TimeSpan.FromMilliseconds(Duration.TotalMilliseconds * 0.2);
  58. TimeSpan cascadeDuration = TimeSpan.FromMilliseconds(Duration.TotalMilliseconds * 0.6);
  59. TimeSpan gatherDuration = TimeSpan.FromMilliseconds(Duration.TotalMilliseconds * 0.2);
  60. for (int i = 0; i < cardsToShow; i++)
  61. {
  62. int deckIndex = i * step;
  63. if (deckIndex >= deck.Count) break;
  64. // Determine which half this card belongs to
  65. bool isLeftHalf = i < halfPoint;
  66. int cardIndexInHalf = isLeftHalf ? i : (i - halfPoint);
  67. int totalInHalf = halfPoint;
  68. // Create card component with slight vertical offset for depth
  69. float depthOffset = i * 0.5f;
  70. var cardComponent = CreateCardComponent(deck[deckIndex], spriteBatch, globalTransformation,
  71. Position + new Vector2(0, depthOffset), true);
  72. animatedCards.Add(cardComponent);
  73. // Phase 1: Split the deck into two piles
  74. Vector2 splitPosition = Position + new Vector2(
  75. isLeftHalf ? -SplitDistance : SplitDistance,
  76. depthOffset);
  77. AddTransition(
  78. cardComponent,
  79. splitPosition,
  80. TimeSpan.Zero,
  81. splitDuration);
  82. // Phase 2: Cascade/riffle together with visible arc
  83. // Stagger the cascade timing so cards drop one by one
  84. double cascadeProgress = (double)cardIndexInHalf / totalInHalf;
  85. TimeSpan cascadeDelay = splitDuration +
  86. TimeSpan.FromMilliseconds(cascadeDuration.TotalMilliseconds * cascadeProgress * 0.7);
  87. // Create a high arc path for visibility
  88. Vector2 midPoint = Position + new Vector2(
  89. isLeftHalf ? -SplitDistance / 2 : SplitDistance / 2,
  90. -CascadeHeight);
  91. Vector2 finalPosition = Position + new Vector2(
  92. (isLeftHalf ? -10 : 10) + Random.Next(-8, 8), // Slight spread
  93. depthOffset + Random.Next(-3, 3));
  94. // Arc up
  95. AddTransition(
  96. cardComponent,
  97. midPoint,
  98. cascadeDelay,
  99. TimeSpan.FromMilliseconds(cascadeDuration.TotalMilliseconds * 0.2));
  100. // Arc down to center
  101. AddTransition(
  102. cardComponent,
  103. finalPosition,
  104. cascadeDelay + TimeSpan.FromMilliseconds(cascadeDuration.TotalMilliseconds * 0.2),
  105. TimeSpan.FromMilliseconds(cascadeDuration.TotalMilliseconds * 0.3));
  106. // Phase 3: Gather into neat pile
  107. TimeSpan gatherDelay = splitDuration + cascadeDuration;
  108. AddTransition(
  109. cardComponent,
  110. Position,
  111. gatherDelay,
  112. gatherDuration);
  113. }
  114. return animatedCards;
  115. }
  116. }
  117. }