Almost working card state machine
This commit is contained in:
@@ -10,6 +10,7 @@ using UI.CardSystem.DragDrop;
|
||||
using UI.DragAndDrop.Core;
|
||||
using Unity.Cinemachine;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace UI.CardSystem
|
||||
@@ -30,8 +31,11 @@ namespace UI.CardSystem
|
||||
|
||||
[Header("Card Display")]
|
||||
[SerializeField] private Transform cardDisplayContainer;
|
||||
[SerializeField] private GameObject flippableCardPrefab; // Placeholder for card backs
|
||||
[FormerlySerializedAs("flippableCardPrefab")]
|
||||
[SerializeField] private GameObject cardPrefab; // New Card prefab using state machine
|
||||
[SerializeField] private float cardSpacing = 150f;
|
||||
[SerializeField] private float cardWidth = 400f;
|
||||
[SerializeField] private float cardHeight = 540f;
|
||||
|
||||
[Header("Settings")]
|
||||
[SerializeField] private float boosterDisappearDuration = 0.5f;
|
||||
@@ -44,12 +48,12 @@ namespace UI.CardSystem
|
||||
private BoosterPackDraggable _currentBoosterInCenter;
|
||||
private List<BoosterPackDraggable> _activeBoostersInSlots = new List<BoosterPackDraggable>();
|
||||
private List<GameObject> _currentRevealedCards = new List<GameObject>();
|
||||
private List<StateMachine.Card> _currentCards = new List<StateMachine.Card>();
|
||||
private CardData[] _currentCardData;
|
||||
private int _revealedCardCount;
|
||||
private int _cardsCompletedInteraction; // Track how many cards finished their new/repeat interaction
|
||||
private StateMachine.Card _activeCard; // Currently selected/revealing card
|
||||
private int _cardsCompletedInteraction; // Track how many cards finished their reveal flow
|
||||
private bool _isProcessingOpening;
|
||||
private const int MAX_VISIBLE_BOOSTERS = 3;
|
||||
private FlippableCard _currentActiveCard; // The card currently awaiting interaction
|
||||
private void Awake()
|
||||
{
|
||||
// Make sure we have a CanvasGroup for transitions
|
||||
@@ -514,8 +518,8 @@ namespace UI.CardSystem
|
||||
// Update visible boosters (remove from end if we drop below thresholds)
|
||||
UpdateVisibleBoosters();
|
||||
|
||||
// Show card backs
|
||||
SpawnCardBacks(_currentCardData.Length);
|
||||
// Show cards using new Card prefab
|
||||
SpawnBoosterCards(_currentCardData);
|
||||
|
||||
// Wait for player to reveal all cards
|
||||
bool isLastBooster = _availableBoosterCount <= 0;
|
||||
@@ -524,10 +528,7 @@ namespace UI.CardSystem
|
||||
// Check if this was the last booster pack
|
||||
if (isLastBooster)
|
||||
{
|
||||
// Wait for all card animations to complete before transitioning
|
||||
// WaitForCardReveals already includes: 0.5s wait + (cardCount * 0.5s stagger) + 0.5s animation + 0.5s final
|
||||
// Total is: 1.5s + (cardCount * 0.5s)
|
||||
// For 5 cards that's 4 seconds total, which should be enough
|
||||
// See earlier comment for timing
|
||||
Logging.Debug("[BoosterOpeningPage] Last booster opened, auto-transitioning to album main page");
|
||||
if (UIPageController.Instance != null)
|
||||
{
|
||||
@@ -539,6 +540,122 @@ namespace UI.CardSystem
|
||||
_isProcessingOpening = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spawn cards for booster opening flow using the new Card prefab and state machine.
|
||||
/// </summary>
|
||||
private void SpawnBoosterCards(CardData[] cards)
|
||||
{
|
||||
if (cardPrefab == null || cardDisplayContainer == null)
|
||||
{
|
||||
Logging.Warning("BoosterOpeningPage: Missing card prefab or container!");
|
||||
return;
|
||||
}
|
||||
|
||||
_currentRevealedCards.Clear();
|
||||
_currentCards.Clear();
|
||||
_cardsCompletedInteraction = 0;
|
||||
_activeCard = null;
|
||||
|
||||
int count = cards.Length;
|
||||
float totalWidth = (count - 1) * cardSpacing;
|
||||
float startX = -totalWidth / 2f;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
GameObject cardObj = Instantiate(cardPrefab, cardDisplayContainer);
|
||||
RectTransform cardRect = cardObj.GetComponent<RectTransform>();
|
||||
if (cardRect != null)
|
||||
{
|
||||
cardRect.anchoredPosition = new Vector2(startX + (i * cardSpacing), 0);
|
||||
cardRect.sizeDelta = new Vector2(cardWidth, cardHeight); // Set card size
|
||||
cardRect.localScale = Vector3.zero; // for pop-in
|
||||
}
|
||||
|
||||
var card = cardObj.GetComponent<StateMachine.Card>();
|
||||
var context = cardObj.GetComponent<StateMachine.CardContext>();
|
||||
if (card != null && context != null)
|
||||
{
|
||||
// Setup card for booster reveal
|
||||
// States will query CardSystemManager for current collection state as needed
|
||||
context.SetupCard(cards[i]);
|
||||
card.SetupForBoosterReveal(cards[i], false); // isNew parameter not used anymore
|
||||
card.SetDraggingEnabled(false);
|
||||
|
||||
// Subscribe to CardDisplay click for selection
|
||||
context.CardDisplay.OnCardClicked += (_) => OnCardClicked(card);
|
||||
|
||||
// Subscribe to reveal flow complete event
|
||||
context.OnRevealFlowComplete += (ctx) => OnCardRevealComplete(card);
|
||||
|
||||
// Track the card
|
||||
_currentCards.Add(card);
|
||||
|
||||
// Tween in
|
||||
Tween.LocalScale(cardObj.transform, Vector3.one, 0.3f, i * 0.1f, Tween.EaseOutBack);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logging.Warning($"[BoosterOpeningPage] Card component or context missing on spawned card {i}!");
|
||||
}
|
||||
|
||||
_currentRevealedCards.Add(cardObj);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle when a card is clicked - start reveal flow if conditions are met
|
||||
/// </summary>
|
||||
private void OnCardClicked(StateMachine.Card card)
|
||||
{
|
||||
// Only allow clicking idle cards when no other card is active
|
||||
if (_activeCard == null && card.IsIdle && card.Context.IsClickable)
|
||||
{
|
||||
Logging.Debug($"[BoosterOpeningPage] Card {card.CardData?.Name} selected for reveal");
|
||||
|
||||
// Set as active and disable all other idle cards
|
||||
_activeCard = card;
|
||||
foreach (var otherCard in _currentCards)
|
||||
{
|
||||
if (otherCard != card && otherCard.IsIdle)
|
||||
{
|
||||
otherCard.Context.IsClickable = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Click will route to IdleState automatically and trigger flip
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle when a card completes its reveal flow
|
||||
/// </summary>
|
||||
private void OnCardRevealComplete(StateMachine.Card card)
|
||||
{
|
||||
_cardsCompletedInteraction++;
|
||||
|
||||
Logging.Debug($"[BoosterOpeningPage] Card {card.CardData?.Name} reveal complete ({_cardsCompletedInteraction}/{_currentCardData.Length})");
|
||||
|
||||
// Add card to inventory NOW (after player saw it)
|
||||
if (card.CardData != null)
|
||||
{
|
||||
Data.CardSystem.CardSystemManager.Instance.AddCardToInventoryDelayed(card.CardData);
|
||||
}
|
||||
|
||||
// Clear active card and re-enable remaining idle cards
|
||||
if (_activeCard == card)
|
||||
{
|
||||
_activeCard = null;
|
||||
|
||||
foreach (var otherCard in _currentCards)
|
||||
{
|
||||
if (otherCard.IsIdle)
|
||||
{
|
||||
otherCard.Context.IsClickable = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Animate the booster pack disappearing
|
||||
/// </summary>
|
||||
@@ -569,249 +686,19 @@ namespace UI.CardSystem
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spawn card back placeholders for revealing
|
||||
/// </summary>
|
||||
private void SpawnCardBacks(int count)
|
||||
{
|
||||
if (flippableCardPrefab == null || cardDisplayContainer == null)
|
||||
{
|
||||
Logging.Warning("BoosterOpeningPage: Missing card prefab or container!");
|
||||
return;
|
||||
}
|
||||
|
||||
_currentRevealedCards.Clear();
|
||||
_revealedCardCount = 0;
|
||||
_cardsCompletedInteraction = 0; // Reset interaction count
|
||||
|
||||
// Calculate positions
|
||||
float totalWidth = (count - 1) * cardSpacing;
|
||||
float startX = -totalWidth / 2f;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
GameObject cardObj = Instantiate(flippableCardPrefab, cardDisplayContainer);
|
||||
RectTransform cardRect = cardObj.GetComponent<RectTransform>();
|
||||
|
||||
if (cardRect != null)
|
||||
{
|
||||
cardRect.anchoredPosition = new Vector2(startX + (i * cardSpacing), 0);
|
||||
}
|
||||
|
||||
// Get FlippableCard component and setup the card data
|
||||
FlippableCard flippableCard = cardObj.GetComponent<FlippableCard>();
|
||||
if (flippableCard != null)
|
||||
{
|
||||
// Setup the card data (stored but not revealed yet)
|
||||
flippableCard.SetupCard(_currentCardData[i]);
|
||||
|
||||
// Subscribe to flip started event (to disable other cards IMMEDIATELY)
|
||||
int cardIndex = i; // Capture for closure
|
||||
flippableCard.OnFlipStarted += OnCardFlipStarted;
|
||||
|
||||
// Subscribe to reveal event to track when flipped
|
||||
flippableCard.OnCardRevealed += (card, data) => OnCardRevealed(cardIndex);
|
||||
|
||||
// Subscribe to inactive click event (for jiggle effect)
|
||||
flippableCard.OnClickedWhileInactive += OnCardClickedWhileInactive;
|
||||
|
||||
// Initially, all cards are clickable (for flipping)
|
||||
flippableCard.SetClickable(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logging.Warning($"[BoosterOpeningPage] FlippableCard component not found on card {i}!");
|
||||
}
|
||||
|
||||
_currentRevealedCards.Add(cardObj);
|
||||
|
||||
// Animate cards flying in
|
||||
cardRect.localScale = Vector3.zero;
|
||||
Tween.LocalScale(cardRect, Vector3.one, 0.3f, i * 0.1f, Tween.EaseOutBack);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle when a card flip starts (disable all other cards IMMEDIATELY)
|
||||
/// </summary>
|
||||
private void OnCardFlipStarted(FlippableCard flippingCard)
|
||||
{
|
||||
Logging.Debug($"[BoosterOpeningPage] Card flip started, disabling all other cards.");
|
||||
|
||||
// Disable ALL cards immediately to prevent multi-flip
|
||||
foreach (GameObject cardObj in _currentRevealedCards)
|
||||
{
|
||||
FlippableCard card = cardObj.GetComponent<FlippableCard>();
|
||||
if (card != null)
|
||||
{
|
||||
card.SetClickable(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle card reveal (when flipped)
|
||||
/// </summary>
|
||||
private void OnCardRevealed(int cardIndex)
|
||||
{
|
||||
Logging.Debug($"[BoosterOpeningPage] Card {cardIndex} revealed!");
|
||||
_revealedCardCount++;
|
||||
|
||||
// Get the flippable card and card data
|
||||
FlippableCard flippableCard = _currentRevealedCards[cardIndex].GetComponent<FlippableCard>();
|
||||
if (flippableCard == null)
|
||||
{
|
||||
Logging.Warning($"[BoosterOpeningPage] FlippableCard not found for card {cardIndex}!");
|
||||
return;
|
||||
}
|
||||
|
||||
CardData cardData = flippableCard.CardData;
|
||||
|
||||
// Check if this is a new card using CardSystemManager
|
||||
bool isNew = Data.CardSystem.CardSystemManager.Instance.IsCardNew(cardData, out CardData existingCard);
|
||||
|
||||
if (isNew)
|
||||
{
|
||||
Logging.Debug($"[BoosterOpeningPage] Card '{cardData.Name}' is NEW!");
|
||||
flippableCard.ShowAsNew();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Check if card is already Legendary - if so, skip progress bar and auto-progress
|
||||
if (existingCard.Rarity == AppleHills.Data.CardSystem.CardRarity.Legendary)
|
||||
{
|
||||
Logging.Debug($"[BoosterOpeningPage] Card '{cardData.Name}' is LEGENDARY - auto-progressing!");
|
||||
// Add to inventory immediately and move to next card
|
||||
Data.CardSystem.CardSystemManager.Instance.AddCardToInventoryDelayed(cardData);
|
||||
_cardsCompletedInteraction++;
|
||||
_revealedCardCount++; // This was already incremented earlier, but we need to track completion
|
||||
EnableUnrevealedCards();
|
||||
return; // Skip showing the card enlarged
|
||||
}
|
||||
|
||||
int ownedCount = existingCard.CopiesOwned;
|
||||
Logging.Debug($"[BoosterOpeningPage] Card '{cardData.Name}' is a REPEAT! Owned: {ownedCount}");
|
||||
|
||||
// Check if this card will trigger an upgrade (ownedCount + 1 >= threshold)
|
||||
bool willUpgrade = (ownedCount + 1) >= flippableCard.CardsToUpgrade && existingCard.Rarity < AppleHills.Data.CardSystem.CardRarity.Legendary;
|
||||
|
||||
if (willUpgrade)
|
||||
{
|
||||
Logging.Debug($"[BoosterOpeningPage] This card will trigger upgrade! ({ownedCount + 1}/{flippableCard.CardsToUpgrade})");
|
||||
// Show as repeat - progress bar will fill and auto-trigger upgrade
|
||||
flippableCard.ShowAsRepeatWithUpgrade(ownedCount, existingCard);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Normal repeat, no upgrade
|
||||
flippableCard.ShowAsRepeat(ownedCount);
|
||||
}
|
||||
}
|
||||
|
||||
// Set this card as the active one (only this card is clickable now)
|
||||
SetActiveCard(flippableCard);
|
||||
|
||||
// Subscribe to tap event to know when interaction is complete
|
||||
flippableCard.OnCardTappedAfterReveal += (card) => OnCardCompletedInteraction(card, cardIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle when a card's interaction is complete (tapped after reveal)
|
||||
/// </summary>
|
||||
private void OnCardCompletedInteraction(FlippableCard card, int cardIndex)
|
||||
{
|
||||
Logging.Debug($"[BoosterOpeningPage] Card {cardIndex} interaction complete!");
|
||||
|
||||
// Add card to inventory NOW (after player saw it)
|
||||
Data.CardSystem.CardSystemManager.Instance.AddCardToInventoryDelayed(card.CardData);
|
||||
|
||||
// Return card to normal size
|
||||
card.ReturnToNormalSize();
|
||||
|
||||
// Increment completed interaction count
|
||||
_cardsCompletedInteraction++;
|
||||
|
||||
// Clear active card
|
||||
_currentActiveCard = null;
|
||||
|
||||
// Re-enable all unrevealed cards (they can be flipped now)
|
||||
EnableUnrevealedCards();
|
||||
|
||||
Logging.Debug($"[BoosterOpeningPage] Cards completed interaction: {_cardsCompletedInteraction}/{_currentCardData.Length}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set which card is currently active (only this card can be clicked)
|
||||
/// </summary>
|
||||
private void SetActiveCard(FlippableCard activeCard)
|
||||
{
|
||||
_currentActiveCard = activeCard;
|
||||
|
||||
// Disable all other cards
|
||||
foreach (GameObject cardObj in _currentRevealedCards)
|
||||
{
|
||||
FlippableCard card = cardObj.GetComponent<FlippableCard>();
|
||||
if (card != null)
|
||||
{
|
||||
// Only the active card is clickable
|
||||
card.SetClickable(card == activeCard);
|
||||
}
|
||||
}
|
||||
|
||||
Logging.Debug($"[BoosterOpeningPage] Set active card. Only one card is now clickable.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Re-enable all unrevealed cards (allow them to be flipped)
|
||||
/// </summary>
|
||||
private void EnableUnrevealedCards()
|
||||
{
|
||||
foreach (GameObject cardObj in _currentRevealedCards)
|
||||
{
|
||||
FlippableCard card = cardObj.GetComponent<FlippableCard>();
|
||||
if (card != null && !card.IsFlipped)
|
||||
{
|
||||
card.SetClickable(true);
|
||||
}
|
||||
}
|
||||
|
||||
Logging.Debug($"[BoosterOpeningPage] Re-enabled unrevealed cards for flipping.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle when a card is clicked while not active (jiggle the active card)
|
||||
/// </summary>
|
||||
private void OnCardClickedWhileInactive(FlippableCard inactiveCard)
|
||||
{
|
||||
Logging.Debug($"[BoosterOpeningPage] Inactive card clicked, jiggling active card.");
|
||||
|
||||
if (_currentActiveCard != null)
|
||||
{
|
||||
_currentActiveCard.Jiggle();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wait until all cards are revealed AND all interactions are complete
|
||||
/// Wait until all cards complete their reveal flow
|
||||
/// </summary>
|
||||
private IEnumerator WaitForCardReveals()
|
||||
{
|
||||
// Wait until all cards are flipped
|
||||
while (_revealedCardCount < _currentCardData.Length)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
|
||||
Logging.Debug($"[BoosterOpeningPage] All cards revealed! Waiting for interactions...");
|
||||
|
||||
// Wait until all cards have completed their new/repeat interaction
|
||||
// Wait until all cards have completed their reveal flow
|
||||
while (_cardsCompletedInteraction < _currentCardData.Length)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
|
||||
Logging.Debug($"[BoosterOpeningPage] All interactions complete! Animating cards to album...");
|
||||
Logging.Debug($"[BoosterOpeningPage] All cards revealed! Animating cards to album...");
|
||||
|
||||
// All cards revealed and interacted with, wait a moment
|
||||
// Small pause
|
||||
yield return new WaitForSeconds(0.5f);
|
||||
|
||||
// Show album icon before cards start tweening to it
|
||||
@@ -829,28 +716,20 @@ namespace UI.CardSystem
|
||||
{
|
||||
if (cardObj != null)
|
||||
{
|
||||
// Stagger each card with 0.5s delay
|
||||
float delay = cardIndex * 0.5f;
|
||||
|
||||
// Animate to album icon position, then destroy
|
||||
// Use world space position tween for root transform
|
||||
Tween.Position(cardObj.transform, targetPosition, 0.5f, delay, Tween.EaseInBack);
|
||||
Tween.LocalScale(cardObj.transform, Vector3.zero, 0.5f, delay, Tween.EaseInBack,
|
||||
completeCallback: () => Destroy(cardObj));
|
||||
|
||||
completeCallback: () => { if (cardObj != null) Destroy(cardObj); });
|
||||
cardIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for all animations to complete
|
||||
// Last card starts at: (cardCount - 1) * 0.5s delay
|
||||
// Last card finishes at: (cardCount - 1) * 0.5s + 0.5s animation duration = cardCount * 0.5s
|
||||
float totalAnimationTime = _currentCardData.Length * 0.5f;
|
||||
|
||||
_currentRevealedCards.Clear();
|
||||
_currentCards.Clear();
|
||||
|
||||
yield return new WaitForSeconds(totalAnimationTime);
|
||||
|
||||
// Album icon stays visible for next booster (will be hidden when next booster is placed)
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user