2025-11-06 11:11:15 +01:00
using System.Collections ;
2025-11-06 10:10:54 +01:00
using System.Collections.Generic ;
2025-11-06 11:11:15 +01:00
using System.Linq ;
2025-11-06 10:10:54 +01:00
using AppleHills.Data.CardSystem ;
using Data.CardSystem ;
using Pixelplacement ;
using UI.Core ;
2025-11-06 11:11:15 +01:00
using UI.CardSystem.DragDrop ;
using UI.DragAndDrop.Core ;
2025-11-06 10:10:54 +01:00
using UnityEngine ;
using UnityEngine.UI ;
namespace UI.CardSystem
{
/// <summary>
/// UI page for opening booster packs and displaying the cards received.
2025-11-06 11:11:15 +01:00
/// Manages the entire booster opening flow with drag-and-drop interaction.
2025-11-06 10:10:54 +01:00
/// </summary>
public class BoosterOpeningPage : UIPage
{
[Header("UI References")]
[SerializeField] private CanvasGroup canvasGroup ;
[SerializeField] private Button closeButton ;
2025-11-06 11:11:15 +01:00
[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
2025-11-06 10:10:54 +01:00
[Header("Card Display")]
[SerializeField] private Transform cardDisplayContainer ;
2025-11-06 11:11:15 +01:00
[SerializeField] private GameObject flippableCardPrefab ; // Placeholder for card backs
[SerializeField] private float cardSpacing = 150f ;
2025-11-06 10:10:54 +01:00
[Header("Settings")]
[SerializeField] private float cardRevealDelay = 0.5f ;
2025-11-06 11:11:15 +01:00
[SerializeField] private float boosterDisappearDuration = 0.5f ;
private int _availableBoosterCount ;
private BoosterPackDraggable _currentBoosterInCenter ;
private List < GameObject > _currentRevealedCards = new List < GameObject > ( ) ;
private CardData [ ] _currentCardData ;
private int _revealedCardCount ;
private bool _isProcessingOpening ;
2025-11-06 10:10:54 +01:00
private void Awake ( )
{
// Make sure we have a CanvasGroup for transitions
if ( canvasGroup = = null )
canvasGroup = GetComponent < CanvasGroup > ( ) ;
if ( canvasGroup = = null )
canvasGroup = gameObject . AddComponent < CanvasGroup > ( ) ;
// 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 ) ;
}
2025-11-06 11:11:15 +01:00
// Unsubscribe from slot events
if ( centerOpeningSlot ! = null )
{
centerOpeningSlot . OnOccupied - = OnBoosterPlacedInCenter ;
}
// Unsubscribe from booster events
UnsubscribeFromAllBoosters ( ) ;
2025-11-06 10:10:54 +01:00
}
private void OnCloseButtonClicked ( )
{
if ( UIPageController . Instance ! = null )
{
UIPageController . Instance . PopPage ( ) ;
}
}
2025-11-06 11:11:15 +01:00
/// <summary>
/// Set the number of available booster packs before showing the page
/// </summary>
public void SetAvailableBoosterCount ( int count )
{
_availableBoosterCount = count ;
}
public override void TransitionIn ( )
{
base . TransitionIn ( ) ;
InitializeBoosterDisplay ( ) ;
}
public override void TransitionOut ( )
{
CleanupPage ( ) ;
base . TransitionOut ( ) ;
}
/// <summary>
/// Initialize the booster pack display based on available count
/// </summary>
private void InitializeBoosterDisplay ( )
{
2025-11-06 13:18:39 +01:00
Debug . Log ( $"[BoosterOpeningPage] InitializeBoosterDisplay called with {_availableBoosterCount} boosters available" ) ;
2025-11-06 11:11:15 +01:00
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 ) ;
2025-11-06 13:18:39 +01:00
Debug . Log ( $"[BoosterOpeningPage] Will show {visibleCount} boosters out of {boosterPackInstances.Length} instances" ) ;
2025-11-06 11:11:15 +01:00
// Show/hide boosters and assign to slots
for ( int i = 0 ; i < boosterPackInstances . Length ; i + + )
{
if ( boosterPackInstances [ i ] = = null ) continue ;
bool shouldShow = i < visibleCount ;
2025-11-06 13:18:39 +01:00
Debug . Log ( $"[BoosterOpeningPage] Booster {i} ({boosterPackInstances[i].name}): shouldShow={shouldShow}, position={boosterPackInstances[i].transform.position}" ) ;
2025-11-06 11:11:15 +01:00
boosterPackInstances [ i ] . SetActive ( shouldShow ) ;
if ( shouldShow )
{
// Get the booster draggable component
BoosterPackDraggable booster = boosterPackInstances [ i ] . GetComponent < BoosterPackDraggable > ( ) ;
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 )
{
2025-11-06 13:18:39 +01:00
Debug . Log ( $"[BoosterOpeningPage] Assigning booster {i} to slot {slot.name} at {slot.transform.position}" ) ;
2025-11-06 11:11:15 +01:00
booster . AssignToSlot ( slot , false ) ;
}
2025-11-06 13:18:39 +01:00
else
{
Debug . LogWarning ( $"[BoosterOpeningPage] Slot {i} is null in bottomRightSlots!" ) ;
}
2025-11-06 11:11:15 +01:00
}
2025-11-06 13:18:39 +01:00
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!" ) ;
2025-11-06 11:11:15 +01:00
}
}
}
// Subscribe to center slot events
if ( centerOpeningSlot ! = null )
{
centerOpeningSlot . OnOccupied + = OnBoosterPlacedInCenter ;
2025-11-06 13:18:39 +01:00
Debug . Log ( $"[BoosterOpeningPage] Subscribed to center slot {centerOpeningSlot.name} at {centerOpeningSlot.transform.position}" ) ;
}
else
{
Debug . LogWarning ( "[BoosterOpeningPage] centerOpeningSlot is null!" ) ;
2025-11-06 11:11:15 +01:00
}
}
/// <summary>
/// Handle when a booster is placed in the center opening slot
/// </summary>
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 ) ;
// Enable tap-to-open and reset tap count
booster . ResetTapCount ( ) ;
booster . SetTapToOpenEnabled ( true ) ;
}
/// <summary>
/// Handle tap-to-place: When player taps a booster in bottom slots, move it to center
/// </summary>
public void OnBoosterTappedInBottomSlot ( BoosterPackDraggable booster )
{
if ( _currentBoosterInCenter ! = null | | centerOpeningSlot = = null )
return ; // Center slot already occupied
// Move booster to center slot
booster . AssignToSlot ( centerOpeningSlot , true ) ;
}
/// <summary>
/// Handle when booster is ready to open (after max taps)
/// </summary>
private void OnBoosterReadyToOpen ( BoosterPackDraggable booster )
{
if ( _isProcessingOpening ) return ;
StartCoroutine ( ProcessBoosterOpening ( booster ) ) ;
}
/// <summary>
/// Process the booster opening sequence
/// </summary>
private IEnumerator ProcessBoosterOpening ( BoosterPackDraggable booster )
{
_isProcessingOpening = true ;
// Call CardSystemManager to open the pack
if ( CardSystemManager . Instance ! = null )
{
List < CardData > 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 ;
}
/// <summary>
/// Animate the booster pack disappearing
/// </summary>
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 < CanvasGroup > ( ) ;
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 ) ;
}
/// <summary>
/// Spawn card back placeholders for revealing
/// </summary>
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 < RectTransform > ( ) ;
if ( cardRect ! = null )
{
cardRect . anchoredPosition = new Vector2 ( startX + ( i * cardSpacing ) , 0 ) ;
}
// Add button to handle reveal on click
Button cardButton = cardObj . GetComponent < Button > ( ) ;
if ( cardButton = = null )
{
cardButton = cardObj . AddComponent < Button > ( ) ;
}
int cardIndex = i ; // Capture for closure
cardButton . onClick . AddListener ( ( ) = > OnCardClicked ( cardIndex , cardObj ) ) ;
_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 card click to reveal
/// </summary>
private void OnCardClicked ( int cardIndex , GameObject cardObj )
{
if ( cardIndex > = _currentCardData . Length ) return ;
// Flip/reveal animation (placeholder - just show card data for now)
CardDisplay cardDisplay = cardObj . GetComponent < CardDisplay > ( ) ;
if ( cardDisplay ! = null )
{
cardDisplay . SetupCard ( _currentCardData [ cardIndex ] ) ;
}
// Disable button so it can't be clicked again
Button cardButton = cardObj . GetComponent < Button > ( ) ;
if ( cardButton ! = null )
{
cardButton . interactable = false ;
}
// Scale punch animation
Tween . LocalScale ( cardObj . transform , Vector3 . one * 1.2f , 0.15f , 0f , Tween . EaseOutBack ,
completeCallback : ( ) = > {
Tween . LocalScale ( cardObj . transform , Vector3 . one , 0.15f , 0f , Tween . EaseInBack ) ;
} ) ;
_revealedCardCount + + ;
}
/// <summary>
/// Wait until all cards are revealed
/// </summary>
private IEnumerator WaitForCardReveals ( )
{
while ( _revealedCardCount < _currentCardData . Length )
{
yield return null ;
}
// All cards revealed, wait a moment
yield return new WaitForSeconds ( 1f ) ;
// Clear cards
foreach ( GameObject card in _currentRevealedCards )
{
if ( card ! = null )
{
// Animate out
Tween . LocalScale ( card . transform , Vector3 . zero , 0.3f , 0f , Tween . EaseInBack ,
completeCallback : ( ) = > Destroy ( card ) ) ;
}
}
_currentRevealedCards . Clear ( ) ;
yield return new WaitForSeconds ( 0.5f ) ;
}
/// <summary>
/// Show the next booster pack
/// </summary>
private IEnumerator ShowNextBooster ( )
{
// Find the next inactive booster and activate it
for ( int i = 0 ; i < boosterPackInstances . Length ; i + + )
{
if ( boosterPackInstances [ i ] ! = null & & ! boosterPackInstances [ i ] . activeSelf )
{
boosterPackInstances [ i ] . SetActive ( true ) ;
BoosterPackDraggable booster = boosterPackInstances [ i ] . GetComponent < BoosterPackDraggable > ( ) ;
if ( booster ! = null )
{
booster . ResetTapCount ( ) ;
booster . SetTapToOpenEnabled ( false ) ;
booster . OnReadyToOpen + = OnBoosterReadyToOpen ;
// Assign to first available slot
DraggableSlot slot = bottomRightSlots ? . GetAvailableSlots ( ) . FirstOrDefault ( ) ;
if ( slot ! = null )
{
booster . AssignToSlot ( slot , true ) ;
}
}
break ;
}
}
yield return null ;
}
/// <summary>
/// Clean up the page when hidden
/// </summary>
private void CleanupPage ( )
{
UnsubscribeFromAllBoosters ( ) ;
// Clear any remaining cards
foreach ( GameObject card in _currentRevealedCards )
{
if ( card ! = null )
Destroy ( card ) ;
}
_currentRevealedCards . Clear ( ) ;
_currentBoosterInCenter = null ;
_isProcessingOpening = false ;
}
/// <summary>
/// Unsubscribe from all booster events
/// </summary>
private void UnsubscribeFromAllBoosters ( )
{
if ( boosterPackInstances = = null ) return ;
foreach ( GameObject boosterObj in boosterPackInstances )
{
if ( boosterObj = = null ) continue ;
BoosterPackDraggable booster = boosterObj . GetComponent < BoosterPackDraggable > ( ) ;
if ( booster ! = null )
{
booster . OnReadyToOpen - = OnBoosterReadyToOpen ;
}
}
}
2025-11-06 10:10:54 +01:00
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 , obeyTimescale : false ) ;
}
else
{
// Fallback if no CanvasGroup
onComplete ? . Invoke ( ) ;
}
}
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 , obeyTimescale : false ) ;
}
else
{
// Fallback if no CanvasGroup
onComplete ? . Invoke ( ) ;
}
}
}
}