Files
AppleHillsProduction/Assets/Scripts/UI/CardSystem/BoosterOpeningPage.cs
2025-10-20 16:33:58 +02:00

601 lines
22 KiB
C#

using System.Collections;
using System.Collections.Generic;
using AppleHills.Data.CardSystem;
using Core;
using Data.CardSystem;
using Pixelplacement;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
namespace AppleHills.UI.CardSystem
{
/// <summary>
/// UI page for opening booster packs and displaying the cards obtained.
/// </summary>
public class BoosterOpeningPage : UIPage
{
[Header("UI Elements")]
[SerializeField] private GameObject boosterPackObject;
[SerializeField] private RectTransform cardRevealContainer; // This should have a HorizontalLayoutGroup component with pre-populated card backs
[SerializeField] private GameObject cardPrefab;
[SerializeField] private Button openBoosterButton;
[SerializeField] private Button continueButton;
[SerializeField] private CanvasGroup canvasGroup;
[Header("Navigation")]
[SerializeField] private Button backButton;
[Header("Animation Settings")]
[SerializeField] private float cardRevealDelay = 0.3f;
[SerializeField] private float cardMoveToBackpackDelay = 0.8f;
[SerializeField] private float flipAnimationDuration = 0.5f;
// State tracking
private enum OpeningState
{
BoosterReady,
CardBacksVisible,
CardsRevealing,
CardsRevealed,
MovingToBackpack,
Completed
}
private OpeningState _currentState = OpeningState.BoosterReady;
private List<CardUIElement> _revealedCards = new List<CardUIElement>();
private List<Button> _cardBackButtons = new List<Button>();
private List<CardData> _boosterCards = new List<CardData>();
private int _revealedCardCount = 0;
private CardSystemManager _cardManager;
private CardAlbumUI _cardAlbumUI;
private Coroutine _moveToBackpackCoroutine;
private void Awake()
{
_cardManager = CardSystemManager.Instance;
_cardAlbumUI = FindObjectOfType<CardAlbumUI>();
// Set up button listeners
if (openBoosterButton != null)
{
openBoosterButton.onClick.AddListener(OnOpenBoosterClicked);
}
if (continueButton != null)
{
continueButton.onClick.AddListener(OnContinueClicked);
continueButton.gameObject.SetActive(false);
}
// Set up back button
if (backButton != null)
{
backButton.onClick.AddListener(OnBackButtonClicked);
}
// Make sure we have a CanvasGroup for transitions
if (canvasGroup == null)
canvasGroup = GetComponent<CanvasGroup>();
if (canvasGroup == null)
canvasGroup = gameObject.AddComponent<CanvasGroup>();
// Cache card back buttons from container
CacheCardBackButtons();
// Initially hide all card backs
HideAllCardBacks();
}
/// <summary>
/// Cache all card back buttons from the container
/// </summary>
private void CacheCardBackButtons()
{
_cardBackButtons.Clear();
if (cardRevealContainer != null)
{
// Get all buttons in the container (these are our card backs)
Button[] buttonsInContainer = cardRevealContainer.GetComponentsInChildren<Button>(true);
_cardBackButtons.AddRange(buttonsInContainer);
Debug.Log($"[BoosterOpeningPage] Found {_cardBackButtons.Count} card back buttons in container");
}
else
{
Debug.LogError("[BoosterOpeningPage] Card reveal container is null, can't find card backs!");
}
}
/// <summary>
/// Hides all card backs in the container
/// </summary>
private void HideAllCardBacks()
{
foreach (var cardBack in _cardBackButtons)
{
if (cardBack != null && cardBack.gameObject != null)
{
cardBack.gameObject.SetActive(false);
}
}
}
private void OnDestroy()
{
// Clean up button listeners
if (openBoosterButton != null)
{
openBoosterButton.onClick.RemoveListener(OnOpenBoosterClicked);
}
if (continueButton != null)
{
continueButton.onClick.RemoveListener(OnContinueClicked);
}
if (backButton != null)
{
backButton.onClick.RemoveListener(OnBackButtonClicked);
}
// Stop any running coroutines
if (_moveToBackpackCoroutine != null)
StopCoroutine(_moveToBackpackCoroutine);
}
/// <summary>
/// Handles click on the back button
/// </summary>
private void OnBackButtonClicked()
{
// Don't allow going back during animations or card reveals
if (_currentState == OpeningState.CardsRevealing ||
_currentState == OpeningState.MovingToBackpack)
{
return;
}
// Use the UIPageController to go back to the previous page
UIPageController pageController = UIPageController.Instance;
if (pageController != null)
{
pageController.PopPage();
}
}
/// <summary>
/// Resets the page to its initial state when it becomes active
/// </summary>
public override void TransitionIn()
{
base.TransitionIn();
ResetState();
}
/// <summary>
/// Resets the state of the booster opening process
/// </summary>
private void ResetState()
{
// Clear any previously revealed cards
foreach (var card in _revealedCards)
{
if (card != null && card.gameObject != null)
Destroy(card.gameObject);
}
_revealedCards.Clear();
// Re-cache card backs in case they changed
CacheCardBackButtons();
// Reset all card backs - both visibility and scale
foreach (var cardBack in _cardBackButtons)
{
if (cardBack != null && cardBack.gameObject != null)
{
cardBack.gameObject.SetActive(false);
cardBack.transform.localScale = Vector3.one; // Reset scale
cardBack.transform.localRotation = Quaternion.identity; // Reset rotation
}
}
// Reset state
_currentState = OpeningState.BoosterReady;
_revealedCardCount = 0;
_boosterCards.Clear();
// Show booster pack, show open button, hide continue button
if (boosterPackObject != null)
{
boosterPackObject.SetActive(true);
boosterPackObject.transform.localScale = Vector3.one; // Reset scale
boosterPackObject.transform.localRotation = Quaternion.identity; // Reset rotation
}
if (openBoosterButton != null)
{
openBoosterButton.gameObject.SetActive(true);
}
if (continueButton != null)
{
continueButton.gameObject.SetActive(false);
continueButton.transform.localScale = Vector3.one; // Reset scale
}
// Make back button visible
if (backButton != null)
{
backButton.gameObject.SetActive(true);
}
Debug.Log("[BoosterOpeningPage] State reset complete, all scales and rotations reset to defaults");
}
/// <summary>
/// Handles click on the booster pack to open it
/// </summary>
private void OnOpenBoosterClicked()
{
if (_currentState != OpeningState.BoosterReady) return;
_currentState = OpeningState.CardBacksVisible;
// Open the booster pack and get the cards
_boosterCards = _cardManager.OpenBoosterPack();
if (_boosterCards.Count > 0)
{
// Hide the booster pack and open button
if (boosterPackObject != null)
{
// Animate the booster pack opening
Tween.LocalScale(boosterPackObject.transform, Vector3.zero, 0.3f, 0f, Tween.EaseInBack, Tween.LoopType.None, null, () => {
boosterPackObject.SetActive(false);
});
}
if (openBoosterButton != null)
{
openBoosterButton.gameObject.SetActive(false);
}
// Show card backs first
StartCoroutine(ShowCardBacks());
}
else
{
Logging.Warning("[BoosterOpeningPage] No cards were obtained from the booster pack.");
UIPageController.Instance.PopPage();
}
}
/// <summary>
/// Shows card backs in the reveal positions
/// </summary>
private IEnumerator ShowCardBacks()
{
// Wait a short delay before showing card backs
yield return new WaitForSeconds(0.5f);
// Check if we have proper container setup
if (cardRevealContainer == null)
{
Debug.LogError("[BoosterOpeningPage] Card reveal container is null!");
yield break;
}
// Check if we found any card backs
if (_cardBackButtons.Count == 0)
{
Debug.LogError("[BoosterOpeningPage] No card back buttons found in container!");
yield break;
}
// Determine how many cards to show based on the booster cards and available card backs
int cardsToShow = Mathf.Min(_boosterCards.Count, _cardBackButtons.Count);
// Activate and animate the card backs
for (int i = 0; i < cardsToShow; i++)
{
Button cardBack = _cardBackButtons[i];
if (cardBack == null) continue;
GameObject cardBackObj = cardBack.gameObject;
// Ensure the card back is active
cardBackObj.SetActive(true);
// Store the index for later reference when clicked
int cardIndex = i;
// Configure the button
cardBack.onClick.RemoveAllListeners(); // Clear any previous listeners
cardBack.onClick.AddListener(() => OnCardBackClicked(cardIndex));
// Set initial scale to zero for animation
cardBackObj.transform.localScale = Vector3.zero;
Debug.Log($"[BoosterOpeningPage] Card back {i} activated");
// Play reveal animation using Pixelplacement.Tween
Tween.LocalScale(cardBackObj.transform, Vector3.one, 0.5f, 0f, Tween.EaseOutBack);
// Wait for animation delay
yield return new WaitForSeconds(cardRevealDelay);
}
// Update state
_currentState = OpeningState.CardBacksVisible;
Debug.Log($"[BoosterOpeningPage] All {cardsToShow} card backs should now be visible");
}
/// <summary>
/// Handles click on a card back to reveal the card
/// </summary>
private void OnCardBackClicked(int cardIndex)
{
Logging.Debug($"[BoosterOpeningPage] Card back clicked at index {cardIndex}");
// Only respond to clicks when in the appropriate state
if (_currentState != OpeningState.CardBacksVisible)
{
Logging.Warning($"[BoosterOpeningPage] Card clicked in wrong state: {_currentState}");
return;
}
// Ensure the index is valid
if (cardIndex < 0 || cardIndex >= _boosterCards.Count || cardIndex >= _cardBackButtons.Count)
{
Debug.LogError($"[BoosterOpeningPage] Invalid card index: {cardIndex}");
return;
}
// Get the card data and card back
CardData cardData = _boosterCards[cardIndex];
Button cardBack = _cardBackButtons[cardIndex];
// Start the reveal animation for this specific card
StartCoroutine(RevealCard(cardIndex, cardData, cardBack.gameObject));
}
/// <summary>
/// Reveals an individual card with animation
/// </summary>
private IEnumerator RevealCard(int cardIndex, CardData cardData, GameObject cardBack)
{
if (cardBack == null)
yield break;
// Start flip animation
Transform cardBackTransform = cardBack.transform;
// Step 1: Flip the card 90 degrees (showing the edge)
Tween.LocalRotation(cardBackTransform, new Vector3(0, 90, 0), flipAnimationDuration * 0.5f, 0);
// Wait for half the flip duration
yield return new WaitForSeconds(flipAnimationDuration * 0.5f);
// Step 2: Hide the card back and show the actual card
cardBack.SetActive(false);
// Instantiate the card prefab at the same position
if (cardPrefab != null)
{
// Instantiate the card in the same parent as the card back and at the same position
GameObject cardObj = Instantiate(cardPrefab, cardBack.transform.parent);
cardObj.transform.SetSiblingIndex(cardBackTransform.GetSiblingIndex()); // Keep the same order in hierarchy
cardObj.transform.position = cardBackTransform.position; // Same world position
// Set initial rotation to continue the flip animation
cardObj.transform.localRotation = Quaternion.Euler(0, 90, 0);
// Configure the card UI with the card data
CardUIElement cardUI = cardObj.GetComponent<CardUIElement>();
if (cardUI != null)
{
cardUI.SetupCard(cardData);
_revealedCards.Add(cardUI);
// Play special effects based on card rarity
PlayRevealEffect(cardObj, cardData.Rarity);
}
// Step 3: Finish the flip animation (from 90 degrees to 0)
Tween.LocalRotation(cardObj.transform, Vector3.zero, flipAnimationDuration * 0.5f, 0);
// Increment counter of revealed cards
_revealedCardCount++;
// Update state if all cards are revealed
if (_revealedCardCount >= _boosterCards.Count)
{
_currentState = OpeningState.CardsRevealed;
// Show continue button after a short delay
StartCoroutine(ShowContinueButton());
}
}
}
/// <summary>
/// Plays reveal effect for a card based on its rarity
/// </summary>
private void PlayRevealEffect(GameObject cardObject, CardRarity rarity)
{
// Add visual effect based on rarity
if (rarity >= CardRarity.Rare)
{
// For rare cards and above, add a particle effect
var particleSystem = cardObject.GetComponentInChildren<ParticleSystem>();
if (particleSystem != null)
{
particleSystem.Play();
}
// Scale up and down for emphasis
Transform cardTransform = cardObject.transform;
Vector3 originalScale = cardTransform.localScale;
// Sequence: Scale up slightly, then back to normal
Tween.LocalScale(cardTransform, originalScale * 1.2f, 0.2f, 0.1f, Tween.EaseOutBack);
Tween.LocalScale(cardTransform, originalScale, 0.15f, 0.3f, Tween.EaseIn);
// Play sound effect based on rarity (if available)
// This would require audio source components to be set up
}
}
/// <summary>
/// Shows the continue button after all cards are revealed
/// </summary>
private IEnumerator ShowContinueButton()
{
// Wait for a moment to let the user see all cards
yield return new WaitForSeconds(1.0f);
if (continueButton != null)
{
// Show the continue button with a nice animation
continueButton.gameObject.SetActive(true);
continueButton.transform.localScale = Vector3.zero;
Tween.LocalScale(continueButton.transform, Vector3.one, 0.3f, 0f, Tween.EaseOutBack);
}
}
/// <summary>
/// Handles click on the continue button
/// </summary>
private void OnContinueClicked()
{
if (_currentState != OpeningState.CardsRevealed) return;
_currentState = OpeningState.MovingToBackpack;
// Hide continue button
if (continueButton != null)
{
continueButton.gameObject.SetActive(false);
}
// Hide back button during transition
if (backButton != null)
{
backButton.gameObject.SetActive(false);
}
// Start animation to move cards to backpack
_moveToBackpackCoroutine = StartCoroutine(MoveCardsToBackpack());
}
/// <summary>
/// Animates cards moving to the backpack icon
/// </summary>
private IEnumerator MoveCardsToBackpack()
{
// Find the backpack icon position if available
Transform backpackTransform = null;
if (_cardAlbumUI != null && _cardAlbumUI.BackpackIcon != null)
{
backpackTransform = _cardAlbumUI.BackpackIcon.transform;
}
if (backpackTransform == null)
{
// If no backpack is found, just return to the menu
UIPageController.Instance.PopPage();
yield break;
}
// Move each card to the backpack with slight delay between cards
for (int i = 0; i < _revealedCards.Count; i++)
{
CardUIElement card = _revealedCards[i];
if (card != null)
{
// Get the world position of the backpack
Vector3 backpackWorldPos = backpackTransform.position;
// Convert to local space of the card's parent for Tween
Vector3 targetPos = card.transform.parent.InverseTransformPoint(backpackWorldPos);
// Start the move animation
Tween.LocalPosition(card.transform, targetPos, 0.5f, cardMoveToBackpackDelay * i);
Tween.LocalScale(card.transform, Vector3.zero, 0.5f, cardMoveToBackpackDelay * i);
}
// Wait before starting the next card
yield return new WaitForSeconds(cardMoveToBackpackDelay);
}
// Wait a bit after the last card
yield return new WaitForSeconds(0.5f);
// Update state
_currentState = OpeningState.Completed;
// Return to the menu
UIPageController.Instance.PopPage();
}
/// <summary>
/// Override for transition in animation using Pixelplacement.Tween
/// </summary>
protected override void DoTransitionIn(System.Action onComplete)
{
// Simple fade in animation
if (canvasGroup != null)
{
canvasGroup.alpha = 0f;
Tween.Value(0f, 1f, (value) => canvasGroup.alpha = value, transitionDuration, 0f, Tween.EaseInOut, Tween.LoopType.None, null, onComplete);
}
else
{
// Fallback if no CanvasGroup
onComplete?.Invoke();
}
}
/// <summary>
/// Override for transition out animation using Pixelplacement.Tween
/// </summary>
protected override void DoTransitionOut(System.Action onComplete)
{
// Simple fade out animation
if (canvasGroup != null)
{
Tween.Value(canvasGroup.alpha, 0f, (value) => canvasGroup.alpha = value, transitionDuration, 0f, Tween.EaseInOut, Tween.LoopType.None, null, onComplete);
}
else
{
// Fallback if no CanvasGroup
onComplete?.Invoke();
}
}
/// <summary>
/// OnEnable override to ensure proper initialization
/// </summary>
private void OnEnable()
{
if (_cardManager == null)
{
_cardManager = CardSystemManager.Instance;
}
if (_cardAlbumUI == null)
{
_cardAlbumUI = FindObjectOfType<CardAlbumUI>();
}
// Re-cache card backs in case they changed while disabled
CacheCardBackButtons();
}
}
}