Working card dragging into album slots, persistent slots
This commit is contained in:
172
Assets/Scripts/UI/CardSystem/AlbumCard.cs
Normal file
172
Assets/Scripts/UI/CardSystem/AlbumCard.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
3
Assets/Scripts/UI/CardSystem/AlbumCard.cs.meta
Normal file
3
Assets/Scripts/UI/CardSystem/AlbumCard.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 258a530448814715b5ec19737df2a658
|
||||
timeCreated: 1762505823
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
163
Assets/Scripts/UI/CardSystem/DragDrop/AlbumCardSlot.cs
Normal file
163
Assets/Scripts/UI/CardSystem/DragDrop/AlbumCardSlot.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user