using System.Collections; using System.Collections.Generic; using System.Linq; using AppleHills.Data.CardSystem; using Data.CardSystem; using Pixelplacement; using UI.Core; using UI.CardSystem.DragDrop; using UI.DragAndDrop.Core; using Unity.Cinemachine; using UnityEngine; using UnityEngine.UI; using UnityEngine.UI; namespace UI.CardSystem { /// /// UI page for opening booster packs and displaying the cards received. /// Manages the entire booster opening flow with drag-and-drop interaction. /// public class BoosterOpeningPage : UIPage { [Header("UI References")] [SerializeField] private CanvasGroup canvasGroup; [SerializeField] private Button closeButton; [Header("Booster Management")] [SerializeField] private GameObject[] boosterPackInstances; // Booster prefabs/instances [SerializeField] private SlotContainer bottomRightSlots; // Holds waiting boosters [SerializeField] private DraggableSlot centerOpeningSlot; // Where booster goes to open [Header("Card Display")] [SerializeField] private Transform cardDisplayContainer; [SerializeField] private GameObject flippableCardPrefab; // Placeholder for card backs [SerializeField] private float cardSpacing = 150f; [Header("Settings")] [SerializeField] private float cardRevealDelay = 0.5f; [SerializeField] private float boosterDisappearDuration = 0.5f; [SerializeField] private CinemachineImpulseSource impulseSource; private int _availableBoosterCount; private BoosterPackDraggable _currentBoosterInCenter; private List _currentRevealedCards = new List(); private CardData[] _currentCardData; private int _revealedCardCount; private bool _isProcessingOpening; private void Awake() { // Make sure we have a CanvasGroup for transitions if (canvasGroup == null) canvasGroup = GetComponent(); if (canvasGroup == null) canvasGroup = gameObject.AddComponent(); // Set up close button if (closeButton != null) { closeButton.onClick.AddListener(OnCloseButtonClicked); } // UI pages should start disabled gameObject.SetActive(false); } private void OnDestroy() { if (closeButton != null) { closeButton.onClick.RemoveListener(OnCloseButtonClicked); } // Unsubscribe from slot events if (centerOpeningSlot != null) { centerOpeningSlot.OnOccupied -= OnBoosterPlacedInCenter; } // Unsubscribe from booster events UnsubscribeFromAllBoosters(); } private void OnCloseButtonClicked() { if (UIPageController.Instance != null) { UIPageController.Instance.PopPage(); } } /// /// Set the number of available booster packs before showing the page /// public void SetAvailableBoosterCount(int count) { _availableBoosterCount = count; } public override void TransitionIn() { base.TransitionIn(); InitializeBoosterDisplay(); } public override void TransitionOut() { CleanupPage(); base.TransitionOut(); } /// /// Initialize the booster pack display based on available count /// private void InitializeBoosterDisplay() { Debug.Log($"[BoosterOpeningPage] InitializeBoosterDisplay called with {_availableBoosterCount} boosters available"); if (boosterPackInstances == null || boosterPackInstances.Length == 0) { Debug.LogWarning("BoosterOpeningPage: No booster pack instances assigned!"); return; } // Calculate how many boosters to show (capped by array size) int visibleCount = Mathf.Min(_availableBoosterCount, boosterPackInstances.Length); Debug.Log($"[BoosterOpeningPage] Will show {visibleCount} boosters out of {boosterPackInstances.Length} instances"); // Show/hide boosters and assign to slots for (int i = 0; i < boosterPackInstances.Length; i++) { if (boosterPackInstances[i] == null) continue; bool shouldShow = i < visibleCount; Debug.Log($"[BoosterOpeningPage] Booster {i} ({boosterPackInstances[i].name}): shouldShow={shouldShow}, position={boosterPackInstances[i].transform.position}"); boosterPackInstances[i].SetActive(shouldShow); if (shouldShow) { // Get the booster draggable component BoosterPackDraggable booster = boosterPackInstances[i].GetComponent(); if (booster != null) { // Reset state booster.ResetTapCount(); booster.SetTapToOpenEnabled(false); // Disable tap-to-open until in center // Subscribe to events booster.OnReadyToOpen += OnBoosterReadyToOpen; // Assign to bottom-right slot if slots available if (bottomRightSlots != null && i < bottomRightSlots.SlotCount) { DraggableSlot slot = bottomRightSlots.GetSlotAtIndex(i); if (slot != null) { Debug.Log($"[BoosterOpeningPage] Assigning booster {i} to slot {slot.name} at {slot.transform.position}"); booster.AssignToSlot(slot, false); } else { Debug.LogWarning($"[BoosterOpeningPage] Slot {i} is null in bottomRightSlots!"); } } else { Debug.LogWarning($"[BoosterOpeningPage] No slot available for booster {i}. bottomRightSlots={bottomRightSlots}, SlotCount={bottomRightSlots?.SlotCount}"); } } else { Debug.LogWarning($"[BoosterOpeningPage] Booster {i} has no BoosterPackDraggable component!"); } } } // Subscribe to center slot events if (centerOpeningSlot != null) { centerOpeningSlot.OnOccupied += OnBoosterPlacedInCenter; Debug.Log($"[BoosterOpeningPage] Subscribed to center slot {centerOpeningSlot.name} at {centerOpeningSlot.transform.position}"); } else { Debug.LogWarning("[BoosterOpeningPage] centerOpeningSlot is null!"); } } /// /// Handle when a booster is placed in the center opening slot /// private void OnBoosterPlacedInCenter(DraggableObject draggable) { BoosterPackDraggable booster = draggable as BoosterPackDraggable; if (booster == null) return; _currentBoosterInCenter = booster; // Lock the slot so it can't be dragged out centerOpeningSlot.SetLocked(true); // Configure booster for opening (disables drag, enables tapping, resets tap count) booster.SetInOpeningSlot(true); // Subscribe to tap events for visual feedback booster.OnTapped += OnBoosterTapped; booster.OnReadyToOpen += OnBoosterReadyToOpen; Debug.Log($"[BoosterOpeningPage] Booster placed in center, ready for {booster.CurrentTapCount} taps"); } private void OnBoosterTapped(BoosterPackDraggable booster, int currentTaps, int maxTaps) { Debug.Log($"[BoosterOpeningPage] Booster tapped: {currentTaps}/{maxTaps}"); // Fire Cinemachine impulse with random velocity (excluding Z) if (impulseSource != null) { // Generate random velocity vector (X and Y only, Z = 0) Vector3 randomVelocity = new Vector3( Random.Range(-1f, 1f), Random.Range(-1f, 1f), 0f ); // Normalize to ensure consistent strength randomVelocity.Normalize(); // Generate the impulse with strength 1 and random velocity impulseSource.GenerateImpulse(randomVelocity); } } /// /// Handle tap-to-place: When player taps a booster in bottom slots, move it to center /// public void OnBoosterTappedInBottomSlot(BoosterPackDraggable booster) { if (_currentBoosterInCenter != null || centerOpeningSlot == null) return; // Center slot already occupied // Move booster to center slot booster.AssignToSlot(centerOpeningSlot, true); } /// /// Handle when booster is ready to open (after max taps) /// private void OnBoosterReadyToOpen(BoosterPackDraggable booster) { if (_isProcessingOpening) return; Debug.Log($"[BoosterOpeningPage] Booster ready to open!"); // Trigger the actual opening sequence booster.TriggerOpen(); StartCoroutine(ProcessBoosterOpening(booster)); } /// /// Process the booster opening sequence /// private IEnumerator ProcessBoosterOpening(BoosterPackDraggable booster) { _isProcessingOpening = true; // Call CardSystemManager to open the pack if (CardSystemManager.Instance != null) { List revealedCardsList = CardSystemManager.Instance.OpenBoosterPack(); _currentCardData = revealedCardsList.ToArray(); // Animate booster disappearing yield return StartCoroutine(AnimateBoosterDisappear(booster)); // Show card backs SpawnCardBacks(_currentCardData.Length); // Wait for player to reveal all cards yield return StartCoroutine(WaitForCardReveals()); // Check if more boosters available _availableBoosterCount--; if (_availableBoosterCount > 0) { // Show next booster yield return StartCoroutine(ShowNextBooster()); } else { // No more boosters, auto-close page yield return new WaitForSeconds(1f); if (UIPageController.Instance != null) { UIPageController.Instance.PopPage(); } } } _isProcessingOpening = false; } /// /// Animate the booster pack disappearing /// private IEnumerator AnimateBoosterDisappear(BoosterPackDraggable booster) { if (booster == null) yield break; // Scale down and fade out Transform boosterTransform = booster.transform; Tween.LocalScale(boosterTransform, Vector3.zero, boosterDisappearDuration, 0f, Tween.EaseInBack); // Also fade the visual if it has a CanvasGroup CanvasGroup boosterCg = booster.GetComponentInChildren(); if (boosterCg != null) { Tween.Value(1f, 0f, (val) => boosterCg.alpha = val, boosterDisappearDuration, 0f); } yield return new WaitForSeconds(boosterDisappearDuration); // Destroy the booster Destroy(booster.gameObject); _currentBoosterInCenter = null; // Unlock center slot centerOpeningSlot.SetLocked(false); } /// /// Spawn card back placeholders for revealing /// private void SpawnCardBacks(int count) { if (flippableCardPrefab == null || cardDisplayContainer == null) { Debug.LogWarning("BoosterOpeningPage: Missing card prefab or container!"); return; } _currentRevealedCards.Clear(); _revealedCardCount = 0; // 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(); if (cardRect != null) { cardRect.anchoredPosition = new Vector2(startX + (i * cardSpacing), 0); } // Add button to handle reveal on click Button cardButton = cardObj.GetComponent