//-----------------------------------------------------------------------------
// ShuffleAnimationComponent.cs
//
// Game component that manages a shuffle animation, creates and coordinates
// all animated card components, and handles cleanup
//-----------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace CardsFramework
{
///
/// Component that orchestrates a shuffle animation by creating animated
/// cards and managing their lifecycle
///
public class ShuffleAnimationComponent : DrawableGameComponent
{
private readonly ShuffleAnimation shuffleAnimation;
private readonly List deck;
private readonly SpriteBatch spriteBatch;
private readonly Matrix globalTransformation;
private List animatedCards;
private TimeSpan elapsedTime;
private bool animationStarted = false;
private bool animationCompleted = false;
///
/// Gets whether the shuffle animation has completed
///
public bool IsComplete => animationCompleted;
///
/// Creates a new shuffle animation component
///
/// The game instance
/// The shuffle animation to perform
/// The deck of cards to shuffle
/// Shared sprite batch for rendering
/// Transformation matrix for scaling
public ShuffleAnimationComponent(
Game game,
ShuffleAnimation shuffleAnimation,
List deck,
SpriteBatch spriteBatch,
Matrix globalTransformation)
: base(game)
{
this.shuffleAnimation = shuffleAnimation;
this.deck = deck;
this.spriteBatch = spriteBatch;
this.globalTransformation = globalTransformation;
// Component should not be visible initially
Visible = false;
}
///
/// Initialize and start the animation
///
public override void Initialize()
{
base.Initialize();
// Create all animated cards
animatedCards = shuffleAnimation.CreateAnimatedCards(deck, spriteBatch, globalTransformation);
// Initialize cards but don't add to Game.Components
// (we'll update and draw them manually for better batching)
foreach (var card in animatedCards)
{
card.Initialize();
}
// Make component visible and trigger start callback
Visible = true;
animationStarted = true;
shuffleAnimation.OnAnimationStart?.Invoke();
}
///
/// Update animation progress and check for completion
///
public override void Update(GameTime gameTime)
{
base.Update(gameTime);
if (!animationStarted)
return;
elapsedTime += gameTime.ElapsedGameTime;
// Update all animated cards
if (animatedCards != null)
{
foreach (var card in animatedCards)
{
if (card.Enabled)
card.Update(gameTime);
}
}
// Check if animation duration has elapsed
if (elapsedTime >= shuffleAnimation.Duration && !animationCompleted)
{
CompleteAnimation();
}
}
///
/// Draw all cards in a single batched draw call
///
public override void Draw(GameTime gameTime)
{
base.Draw(gameTime);
if (!animationStarted || animatedCards == null)
return;
// Begin batch once for all cards
spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, null, null, globalTransformation);
// Draw all visible cards
foreach (var card in animatedCards)
{
if (card.Visible)
card.Draw(gameTime);
}
spriteBatch.End();
}
///
/// Called when animation finishes
///
private void CompleteAnimation()
{
animationCompleted = true;
// CRITICAL: Hide cards immediately before doing anything else
// This prevents the "ghost deck" from appearing at shuffle position
if (animatedCards != null)
{
foreach (var card in animatedCards)
{
card.Visible = false;
}
}
// Invoke completion callback
shuffleAnimation.OnAnimationComplete?.Invoke();
// Clean up all card components
CleanupCards();
// Remove this component from the game
Game.Components.Remove(this);
}
///
/// Removes all animated card components from the game
///
private void CleanupCards()
{
if (animatedCards != null)
{
// Dispose all cards
foreach (var card in animatedCards)
{
card.Visible = false;
card.Dispose();
}
animatedCards.Clear();
}
}
///
/// Dispose and cleanup
///
protected override void Dispose(bool disposing)
{
if (disposing)
{
CleanupCards();
}
base.Dispose(disposing);
}
}
}