Working card dragging into album slots, persistent slots

This commit is contained in:
Michal Pikulski
2025-11-07 11:24:19 +01:00
parent 0d8702a5f6
commit 77f7d1ee97
21 changed files with 5093 additions and 3257 deletions

View File

@@ -0,0 +1,172 @@
using System;
using AppleHills.Data.CardSystem;
using Pixelplacement;
using UnityEngine;
using UnityEngine.EventSystems;
namespace UI.CardSystem
{
/// <summary>
/// Album card component that wraps CardDisplay.
/// Handles tap-to-enlarge and tap-to-shrink interactions for cards placed in album slots.
///
/// TODO: Consider refactoring to state machine pattern (PendingReveal, PlacedInSlot, Enlarged)
/// This would eliminate the need for separate AlbumPlacementCard wrapper and simplify the hierarchy.
/// See design discussion with state transitions for cleaner architecture.
/// </summary>
public class AlbumCard : MonoBehaviour, IPointerClickHandler
{
[Header("References")]
[SerializeField] private CardDisplay cardDisplay;
[Header("Enlarge Settings")]
[SerializeField] private float enlargedScale = 2.5f;
[SerializeField] private float scaleDuration = 0.3f;
// Events for AlbumViewPage to manage backdrop and reparenting
public event Action<AlbumCard> OnEnlargeRequested;
public event Action<AlbumCard> OnShrinkRequested;
private AlbumCardSlot _parentSlot;
private CardData _cardData;
private bool _isEnlarged;
private Vector3 _originalScale;
private Transform _originalParent;
private Vector3 _originalLocalPosition;
private Quaternion _originalLocalRotation;
private void Awake()
{
// Auto-find CardDisplay if not assigned
if (cardDisplay == null)
{
cardDisplay = GetComponentInChildren<CardDisplay>();
}
// Store original scale
_originalScale = transform.localScale;
}
/// <summary>
/// Setup card with data
/// </summary>
public void SetupCard(CardData data)
{
_cardData = data;
if (cardDisplay != null)
{
cardDisplay.SetupCard(data);
}
}
/// <summary>
/// Set the parent slot this card belongs to
/// </summary>
public void SetParentSlot(AlbumCardSlot slot)
{
_parentSlot = slot;
}
/// <summary>
/// Get the card data
/// </summary>
public CardData GetCardData()
{
return _cardData;
}
/// <summary>
/// Handle tap on card - request enlarge/shrink from parent page
/// Only process clicks when card is placed in a slot (not during reveal flow)
/// </summary>
public void OnPointerClick(PointerEventData eventData)
{
// During reveal flow (before placed in slot), ignore clicks
// Let the parent FlippableCard handle them
if (_parentSlot == null)
return;
if (_isEnlarged)
{
OnShrinkRequested?.Invoke(this);
}
else
{
OnEnlargeRequested?.Invoke(this);
}
}
/// <summary>
/// Enlarge card (called by AlbumViewPage after reparenting)
/// </summary>
public void EnlargeCard()
{
if (_isEnlarged) return;
_isEnlarged = true;
// Store original transform info for restoration
_originalParent = transform.parent;
_originalLocalPosition = transform.localPosition;
_originalLocalRotation = transform.localRotation;
// Scale up with snappy tween
Tween.LocalScale(transform, _originalScale * enlargedScale, scaleDuration, 0f, Tween.EaseOutBack);
}
/// <summary>
/// Shrink card back to original size (called by AlbumViewPage before reparenting back)
/// </summary>
/// <param name="onComplete">Optional callback to invoke when shrink animation completes</param>
public void ShrinkCard(System.Action onComplete = null)
{
if (!_isEnlarged) return;
_isEnlarged = false;
// Scale back down with snappy tween, invoke callback when done
Tween.LocalScale(transform, _originalScale, scaleDuration, 0f, Tween.EaseInBack,
completeCallback: () => onComplete?.Invoke());
}
/// <summary>
/// Get original parent for restoration
/// </summary>
public Transform GetOriginalParent()
{
return _originalParent;
}
/// <summary>
/// Get original local position for restoration
/// </summary>
public Vector3 GetOriginalLocalPosition()
{
return _originalLocalPosition;
}
/// <summary>
/// Get original local rotation for restoration
/// </summary>
public Quaternion GetOriginalLocalRotation()
{
return _originalLocalRotation;
}
/// <summary>
/// Check if card is currently enlarged
/// </summary>
public bool IsEnlarged => _isEnlarged;
/// <summary>
/// Force reset enlarged state (for cleanup scenarios like page closing)
/// </summary>
public void ForceResetEnlargedState()
{
_isEnlarged = false;
transform.localScale = _originalScale;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 258a530448814715b5ec19737df2a658
timeCreated: 1762505823

View File

@@ -1,57 +0,0 @@
using AppleHills.Data.CardSystem;
using UI.DragAndDrop.Core;
using UnityEngine;
namespace UI.CardSystem
{
/// <summary>
/// Specialized slot for album pages that only accepts a specific card.
/// Validates cards based on their CardDefinition.
/// </summary>
public class AlbumCardSlot : DraggableSlot
{
[Header("Album Slot Configuration")]
[SerializeField] private CardDefinition targetCardDefinition; // Which card this slot accepts
private bool _isOccupiedPermanently = false;
/// <summary>
/// Set the target card this slot should accept
/// </summary>
public void SetTargetCard(CardDefinition definition)
{
targetCardDefinition = definition;
}
/// <summary>
/// Check if this slot can accept a specific card
/// </summary>
public bool CanAcceptCard(CardData cardData)
{
if (cardData == null || targetCardDefinition == null) return false;
if (_isOccupiedPermanently) return false;
// Card must match this slot's target definition
return cardData.DefinitionId == targetCardDefinition.Id;
}
/// <summary>
/// Called when a card is successfully placed in this slot
/// </summary>
public void OnCardPlaced()
{
_isOccupiedPermanently = true;
}
/// <summary>
/// Check if this slot has been permanently filled
/// </summary>
public bool IsOccupiedPermanently => _isOccupiedPermanently;
/// <summary>
/// Get the target card definition for this slot
/// </summary>
public CardDefinition TargetCardDefinition => targetCardDefinition;
}
}

View File

@@ -27,14 +27,18 @@ namespace UI.CardSystem
[Header("Album Card Reveal")]
[SerializeField] private SlotContainer bottomRightSlots;
[SerializeField] private GameObject albumCardPrefab;
[SerializeField] private GameObject albumCardPlacementPrefab; // The wrapper prefab with flip/drag (AlbumPlacementCard)
[Header("Card Enlarge System")]
[SerializeField] private GameObject cardEnlargedBackdrop; // Backdrop to block interactions
[SerializeField] private Transform cardEnlargedContainer; // Container for enlarged cards (sits above backdrop)
[Header("Booster Pack UI")]
[SerializeField] private GameObject[] boosterPackButtons;
[SerializeField] private BoosterOpeningPage boosterOpeningPage;
private Input.InputMode _previousInputMode;
private List<AlbumCardDraggable> _activeCards = new List<AlbumCardDraggable>();
private List<AlbumCardPlacementDraggable> _activeCards = new List<AlbumCardPlacementDraggable>();
private const int MAX_VISIBLE_CARDS = 3;
private void Awake()
@@ -45,6 +49,12 @@ namespace UI.CardSystem
if (canvasGroup == null)
canvasGroup = gameObject.AddComponent<CanvasGroup>();
// Hide backdrop initially
if (cardEnlargedBackdrop != null)
{
cardEnlargedBackdrop.SetActive(false);
}
// Set up exit button
if (exitButton != null)
{
@@ -242,6 +252,9 @@ namespace UI.CardSystem
protected override void DoTransitionOut(System.Action onComplete)
{
// Clean up any enlarged card state before closing
CleanupEnlargedCardState();
// Simple fade out animation
if (canvasGroup != null)
{
@@ -254,6 +267,43 @@ namespace UI.CardSystem
}
}
/// <summary>
/// Clean up enlarged card state when closing the album
/// </summary>
private void CleanupEnlargedCardState()
{
// Hide backdrop if visible
if (cardEnlargedBackdrop != null && cardEnlargedBackdrop.activeSelf)
{
cardEnlargedBackdrop.SetActive(false);
}
// If there's an enlarged card in the container, return it to its slot
if (cardEnlargedContainer != null && cardEnlargedContainer.childCount > 0)
{
// Get all enlarged cards (should only be one, but just in case)
for (int i = cardEnlargedContainer.childCount - 1; i >= 0; i--)
{
Transform cardTransform = cardEnlargedContainer.GetChild(i);
AlbumCard albumCard = cardTransform.GetComponent<AlbumCard>();
if (albumCard != null && albumCard.IsEnlarged)
{
// Force reset state and return to slot
Transform originalParent = albumCard.GetOriginalParent();
if (originalParent != null)
{
cardTransform.SetParent(originalParent, true);
cardTransform.localPosition = albumCard.GetOriginalLocalPosition();
cardTransform.localRotation = albumCard.GetOriginalLocalRotation();
}
albumCard.ForceResetEnlargedState();
}
}
}
}
#region Album Card Reveal System
/// <summary>
@@ -262,7 +312,7 @@ namespace UI.CardSystem
/// </summary>
private void SpawnPendingCards()
{
if (CardSystemManager.Instance == null || bottomRightSlots == null || albumCardPrefab == null)
if (CardSystemManager.Instance == null || bottomRightSlots == null || albumCardPlacementPrefab == null)
return;
var pending = CardSystemManager.Instance.GetPendingRevealCards();
@@ -306,26 +356,26 @@ namespace UI.CardSystem
// Instantiate card directly as child of the slot container (not the slot itself, not canvas root)
// This keeps it in the correct UI hierarchy
GameObject cardObj = Instantiate(albumCardPrefab, bottomRightSlots.transform);
AlbumCardDraggable card = cardObj.GetComponent<AlbumCardDraggable>();
GameObject cardObj = Instantiate(albumCardPlacementPrefab, bottomRightSlots.transform);
AlbumCardPlacementDraggable cardPlacement = cardObj.GetComponent<AlbumCardPlacementDraggable>();
if (card != null)
if (cardPlacement != null)
{
// Setup card data
card.SetupCard(cardData);
cardPlacement.SetupCard(cardData);
// Subscribe to events
card.OnCardRevealed += OnCardRevealed;
card.OnCardPlacedInAlbum += OnCardPlacedInAlbum;
cardPlacement.OnCardRevealed += OnCardRevealed;
cardPlacement.OnCardPlacedInAlbum += OnCardPlacedInAlbum;
// NOW assign to slot - this will:
// 1. Reparent to slot
// 2. Apply slot's occupantSizeMode scaling
// 3. Animate to slot position
card.AssignToSlot(slot, true);
cardPlacement.AssignToSlot(slot, true);
// Track it
_activeCards.Add(card);
_activeCards.Add(cardPlacement);
Debug.Log($"[AlbumViewPage] Spawned card '{cardData.Name}' (CopiesOwned: {cardData.CopiesOwned}) in slot {slotIndex}");
}
@@ -371,7 +421,7 @@ namespace UI.CardSystem
/// <summary>
/// Handle when a card is revealed (flipped)
/// </summary>
private void OnCardRevealed(AlbumCardDraggable card, CardData cardData)
private void OnCardRevealed(AlbumCardPlacementDraggable cardPlacement, CardData cardData)
{
Debug.Log($"[AlbumViewPage] Card revealed: {cardData.Name} (Zone: {cardData.Zone}, CopiesOwned: {cardData.CopiesOwned})");
@@ -383,7 +433,7 @@ namespace UI.CardSystem
}
// Remove this card from active cards list
_activeCards.Remove(card);
_activeCards.Remove(cardPlacement);
// Check if we're currently viewing the correct zone for this card
CardZone currentZone = GetCurrentZone();
@@ -409,13 +459,13 @@ namespace UI.CardSystem
/// Card data already moved to inventory in OnCardRevealed
/// This just handles cleanup
/// </summary>
private void OnCardPlacedInAlbum(AlbumCardDraggable card, CardData cardData)
private void OnCardPlacedInAlbum(AlbumCardPlacementDraggable cardPlacement, CardData cardData)
{
Debug.Log($"[AlbumViewPage] Card placed in album slot: {cardData.Name}");
// Unsubscribe from events (card is now static in album)
card.OnCardRevealed -= OnCardRevealed;
card.OnCardPlacedInAlbum -= OnCardPlacedInAlbum;
cardPlacement.OnCardRevealed -= OnCardRevealed;
cardPlacement.OnCardPlacedInAlbum -= OnCardPlacedInAlbum;
// Note: Card already removed from _activeCards in OnCardRevealed
// Note: Shuffle and spawn already done in OnCardRevealed
@@ -560,5 +610,91 @@ namespace UI.CardSystem
}
#endregion
#region Card Enlarge System
/// <summary>
/// Subscribe to album card events when a card is spawned in a slot
/// Call this when AlbumCardSlot spawns a card
/// </summary>
public void RegisterAlbumCard(AlbumCard albumCard)
{
if (albumCard == null) return;
albumCard.OnEnlargeRequested += OnCardEnlargeRequested;
albumCard.OnShrinkRequested += OnCardShrinkRequested;
}
/// <summary>
/// Unsubscribe from album card events
/// </summary>
public void UnregisterAlbumCard(AlbumCard albumCard)
{
if (albumCard == null) return;
albumCard.OnEnlargeRequested -= OnCardEnlargeRequested;
albumCard.OnShrinkRequested -= OnCardShrinkRequested;
}
/// <summary>
/// Handle card enlarge request - show backdrop and reparent card
/// </summary>
private void OnCardEnlargeRequested(AlbumCard card)
{
if (card == null) return;
Debug.Log($"[AlbumViewPage] OnCardEnlargeRequested called for card: {card.name}, current parent: {card.transform.parent.name}");
// IMPORTANT: Call EnlargeCard FIRST to store original parent (the slot)
// BEFORE reparenting to the enlarged container
card.EnlargeCard();
// Show backdrop
if (cardEnlargedBackdrop != null)
{
cardEnlargedBackdrop.SetActive(true);
Debug.Log($"[AlbumViewPage] Backdrop shown");
}
// NOW reparent card to enlarged container (above backdrop)
if (cardEnlargedContainer != null)
{
card.transform.SetParent(cardEnlargedContainer, true);
card.transform.SetAsLastSibling(); // Ensure on top
Debug.Log($"[AlbumViewPage] Card reparented to enlarged container");
}
Debug.Log($"[AlbumViewPage] Card enlarged: {card.GetCardData()?.Name}");
}
/// <summary>
/// Handle card shrink request - hide backdrop and reparent card back to slot
/// </summary>
private void OnCardShrinkRequested(AlbumCard card)
{
if (card == null) return;
// Trigger shrink animation
card.ShrinkCard();
// Hide backdrop
if (cardEnlargedBackdrop != null)
{
cardEnlargedBackdrop.SetActive(false);
}
// Reparent back to original parent (the slot)
Transform originalParent = card.GetOriginalParent();
if (originalParent != null)
{
card.transform.SetParent(originalParent, true);
card.transform.localPosition = card.GetOriginalLocalPosition();
card.transform.localRotation = card.GetOriginalLocalRotation();
}
Debug.Log($"[AlbumViewPage] Card shrunk: {card.GetCardData()?.Name}");
}
#endregion
}
}

View File

@@ -2,6 +2,7 @@
using System.Collections;
using AppleHills.Data.CardSystem;
using Data.CardSystem;
using Pixelplacement;
using UI.DragAndDrop.Core;
using UnityEngine;
@@ -12,7 +13,7 @@ namespace UI.CardSystem
/// Handles both tap and drag-hold interactions for revealing cards.
/// Auto-snaps to matching album slot on release/tap.
/// </summary>
public class AlbumCardDraggable : DraggableObject
public class AlbumCardPlacementDraggable : DraggableObject
{
[Header("Album Card Settings")]
[SerializeField] private FlippableCard flippableCard;
@@ -26,8 +27,8 @@ namespace UI.CardSystem
private bool _isHolding = false; // Track if pointer is currently down
// Events
public event Action<AlbumCardDraggable, CardData> OnCardRevealed;
public event Action<AlbumCardDraggable, CardData> OnCardPlacedInAlbum;
public event Action<AlbumCardPlacementDraggable, CardData> OnCardRevealed;
public event Action<AlbumCardPlacementDraggable, CardData> OnCardPlacedInAlbum;
public CardData CardData => _cardData;
public bool IsRevealed => _isRevealed;
@@ -81,7 +82,7 @@ namespace UI.CardSystem
{
if (_cardData == null)
{
Debug.LogWarning("[AlbumCardDraggable] Cannot snap to slot - no card data assigned.");
Debug.LogWarning("[AlbumCardPlacementDraggable] Cannot snap to slot - no card data assigned.");
return;
}
@@ -100,22 +101,77 @@ namespace UI.CardSystem
if (matchingSlot != null)
{
// Assign to slot with animation
AssignToSlot(matchingSlot, true);
// Mark slot as permanently occupied
matchingSlot.OnCardPlaced();
// Disable dragging - card is now static in album
SetDraggingEnabled(false);
// Notify that card was placed
// Note: Card already moved from pending to inventory in OnCardRevealed
OnCardPlacedInAlbum?.Invoke(this, _cardData);
// NEW FLOW: Extract AlbumCard FIRST, then tween it
if (flippableCard != null)
{
AlbumCard extractedCard = flippableCard.ExtractAlbumCard(matchingSlot.transform);
if (extractedCard != null)
{
// Notify slot that card was placed
matchingSlot.OnCardPlaced(extractedCard);
// NOW tween the extracted AlbumCard into position
TweenExtractedCardToSlot(extractedCard, () =>
{
// After animation completes
Debug.Log($"[AlbumCardPlacementDraggable] Card placement animation complete for {_cardData.Name}");
// Notify that card was placed
OnCardPlacedInAlbum?.Invoke(this, _cardData);
// Destroy this wrapper (the AlbumPlacementCard)
Destroy(gameObject);
});
}
else
{
Debug.LogWarning("[AlbumCardPlacementDraggable] Failed to extract AlbumCard from wrapper!");
}
}
}
else
{
Debug.LogWarning($"[AlbumCardDraggable] Could not find matching slot for card '{_cardData.Name}' (Zone: {_cardData.Zone}, Index: {_cardData.CollectionIndex})");
Debug.LogWarning($"[AlbumCardPlacementDraggable] Could not find matching slot for card '{_cardData.Name}' (Zone: {_cardData.Zone}, Index: {_cardData.CollectionIndex})");
}
}
/// <summary>
/// Tween the extracted AlbumCard into its slot position
/// Tweens from current size to slot size - AspectRatioFitter handles width
/// </summary>
private void TweenExtractedCardToSlot(AlbumCard card, System.Action onComplete)
{
Transform cardTransform = card.transform;
RectTransform cardRect = cardTransform as RectTransform;
if (cardRect != null)
{
// Get target height from slot
RectTransform slotRect = cardTransform.parent as RectTransform;
float targetHeight = slotRect != null ? slotRect.rect.height : cardRect.sizeDelta.y;
// Tween from current size to target size (AspectRatioFitter will adjust width)
Vector2 targetSize = new Vector2(cardRect.sizeDelta.x, targetHeight);
Tween.Size(cardRect, targetSize, snapDuration, 0f, Tween.EaseOutBack);
// Tween position and rotation to slot center
Tween.LocalPosition(cardRect, Vector3.zero, snapDuration, 0f, Tween.EaseOutBack);
Tween.LocalRotation(cardTransform, Quaternion.identity, snapDuration, 0f, Tween.EaseOutBack,
completeCallback: () =>
{
Debug.Log($"[AlbumCardPlacementDraggable] Tween complete for extracted card {card.name}, final height: {cardRect.sizeDelta.y}");
onComplete?.Invoke();
});
}
else
{
// No RectTransform, just reset and call callback
cardTransform.localPosition = Vector3.zero;
cardTransform.localRotation = Quaternion.identity;
onComplete?.Invoke();
}
}

View File

@@ -0,0 +1,163 @@
using AppleHills.Data.CardSystem;
using Data.CardSystem;
using UI.DragAndDrop.Core;
using UnityEngine;
namespace UI.CardSystem
{
/// <summary>
/// Specialized slot for album pages that only accepts a specific card.
/// Validates cards based on their CardDefinition.
/// Self-populates with owned cards when enabled.
/// </summary>
public class AlbumCardSlot : DraggableSlot
{
[Header("Album Slot Configuration")]
[SerializeField] private CardDefinition targetCardDefinition; // Which card this slot accepts
[SerializeField] private GameObject albumCardPrefab; // Prefab to spawn when card is owned
private bool _isOccupiedPermanently = false;
private AlbumCard _placedCard;
/// <summary>
/// Set the target card this slot should accept
/// </summary>
public void SetTargetCard(CardDefinition definition)
{
targetCardDefinition = definition;
}
/// <summary>
/// Check if this slot can accept a specific card
/// </summary>
public bool CanAcceptCard(CardData cardData)
{
if (cardData == null || targetCardDefinition == null) return false;
if (_isOccupiedPermanently) return false;
// Card must match this slot's target definition
return cardData.DefinitionId == targetCardDefinition.Id;
}
/// <summary>
/// Called when a card is successfully placed in this slot
/// </summary>
public void OnCardPlaced(AlbumCard albumCard = null)
{
_isOccupiedPermanently = true;
if (albumCard != null)
{
_placedCard = albumCard;
albumCard.SetParentSlot(this);
// Register with AlbumViewPage for enlarge/shrink handling
AlbumViewPage albumPage = FindObjectOfType<AlbumViewPage>();
if (albumPage != null)
{
albumPage.RegisterAlbumCard(albumCard);
}
}
}
/// <summary>
/// Check if this slot has a placed card
/// </summary>
public bool HasPlacedCard()
{
return _placedCard != null;
}
/// <summary>
/// Get the placed card (if any)
/// </summary>
public AlbumCard GetPlacedCard()
{
return _placedCard;
}
private void OnEnable()
{
// Check if we should spawn a card for this slot
CheckAndSpawnOwnedCard();
}
/// <summary>
/// Check if player owns the card for this slot and spawn it if so
/// </summary>
private void CheckAndSpawnOwnedCard()
{
// Guard: need CardSystemManager and target definition
if (CardSystemManager.Instance == null || targetCardDefinition == null)
return;
// Guard: don't spawn if already occupied
if (_isOccupiedPermanently || _placedCard != null)
return;
// Guard: need prefab to spawn
if (albumCardPrefab == null)
{
Debug.LogWarning($"[AlbumCardSlot] No albumCardPrefab assigned for slot targeting {targetCardDefinition.name}");
return;
}
// Check if player owns this card at ANY rarity (prioritize highest rarity)
CardData ownedCard = null;
// Check in order: Legendary > Rare > Normal
foreach (CardRarity rarity in new[] { CardRarity.Legendary, CardRarity.Rare, CardRarity.Normal })
{
CardData card = CardSystemManager.Instance.GetCardInventory().GetCard(targetCardDefinition.Id, rarity);
if (card != null)
{
ownedCard = card;
break; // Found highest rarity owned
}
}
// Spawn card if owned
if (ownedCard != null)
{
SpawnAlbumCard(ownedCard);
}
}
/// <summary>
/// Spawn an AlbumCard in this slot
/// </summary>
private void SpawnAlbumCard(CardData cardData)
{
GameObject cardObj = Instantiate(albumCardPrefab, transform);
AlbumCard albumCard = cardObj.GetComponent<AlbumCard>();
if (albumCard != null)
{
albumCard.SetupCard(cardData);
albumCard.SetParentSlot(this);
_placedCard = albumCard;
_isOccupiedPermanently = true;
// Register with AlbumViewPage for enlarge/shrink handling
AlbumViewPage albumPage = FindObjectOfType<AlbumViewPage>();
if (albumPage != null)
{
albumPage.RegisterAlbumCard(albumCard);
}
Debug.Log($"[AlbumCardSlot] Spawned owned card '{cardData.Name}' ({cardData.Rarity}) in slot");
}
else
{
Debug.LogWarning($"[AlbumCardSlot] Spawned prefab has no AlbumCard component!");
Destroy(cardObj);
}
}
/// <summary>
/// Get the target card definition for this slot
/// </summary>
public CardDefinition TargetCardDefinition => targetCardDefinition;
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using AppleHills.Data.CardSystem;
using Pixelplacement;
using Pixelplacement.TweenSystem;
@@ -17,6 +17,7 @@ namespace UI.CardSystem
[SerializeField] private GameObject cardBackObject; // The card back visual
[SerializeField] private GameObject cardFrontObject; // Your CardDisplay prefab instance
[SerializeField] private CardDisplay cardDisplay; // Reference to CardDisplay component
[SerializeField] private AlbumCard albumCard; // Reference to nested AlbumCard (for album placement flow)
[Header("Idle Hover Animation")]
[SerializeField] private bool enableIdleHover = true;
@@ -66,6 +67,12 @@ namespace UI.CardSystem
cardDisplay = cardFrontObject.GetComponent<CardDisplay>();
}
// Auto-find AlbumCard if not assigned
if (albumCard == null)
{
albumCard = GetComponentInChildren<AlbumCard>();
}
// Card back: starts at 0° rotation (normal, facing camera, clickable)
// Card front: starts at 180° rotation (flipped away, will rotate to 0° when revealed)
if (cardBackObject != null)
@@ -621,6 +628,36 @@ namespace UI.CardSystem
});
}
/// <summary>
/// Extract the nested AlbumCard and reparent it to a new parent
/// Used when placing card in album slot - extracts the AlbumCard from this wrapper
/// The caller is responsible for tweening it to the final position
/// </summary>
/// <param name="newParent">The transform to reparent the AlbumCard to (typically the AlbumCardSlot)</param>
/// <returns>The extracted AlbumCard component, or null if not found</returns>
public AlbumCard ExtractAlbumCard(Transform newParent)
{
if (albumCard == null)
{
Debug.LogWarning("[FlippableCard] Cannot extract AlbumCard - none found!");
return null;
}
// Reparent AlbumCard to new parent (maintain world position temporarily)
// The caller will tween it to the final position
albumCard.transform.SetParent(newParent, true);
// Setup the card data on the AlbumCard
if (_cardData != null)
{
albumCard.SetupCard(_cardData);
}
Debug.Log($"[FlippableCard] Extracted AlbumCard '{_cardData?.Name}' to {newParent.name} - ready for tween");
return albumCard;
}
#endregion
private void OnDestroy()