using AppleHills.Data.CardSystem; using Core; using Data.CardSystem; using UI.DragAndDrop.Core; using UnityEngine; using UnityEngine.EventSystems; namespace UI.CardSystem { /// /// Specialized slot for album pages that only accepts a specific card. /// Validates cards based on their CardDefinition. /// Self-populates with owned cards when enabled. /// Shows preview of target card when empty slot is tapped. /// public class AlbumCardSlot : DraggableSlot, IPointerClickHandler { [Header("Album Slot Configuration")] [SerializeField] private CardDefinition targetCardDefinition; // Which card this slot accepts [SerializeField] private GameObject albumCardPrefab; // Prefab to spawn when card is owned [Header("Preview Card (for empty slots)")] [SerializeField] private CardDisplay previewCardDisplay; // Nested CardDisplay showing greyed-out preview [SerializeField] private float previewEnlargedScale = 2.5f; [SerializeField] private float previewScaleDuration = 0.3f; private bool _isOccupiedPermanently = false; private AlbumCard _placedCard; private bool _isPreviewShowing = false; private Vector3 _previewOriginalScale; private void Awake() { // Store original scale of preview card if (previewCardDisplay != null) { _previewOriginalScale = previewCardDisplay.transform.localScale; // Hide preview card by default previewCardDisplay.gameObject.SetActive(false); } } /// /// Set the target card this slot should accept /// public void SetTargetCard(CardDefinition definition) { targetCardDefinition = definition; } /// /// Check if this slot can accept a specific card /// 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; } /// /// Called when a card is successfully placed in this slot /// 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(); if (albumPage != null) { albumPage.RegisterAlbumCard(albumCard); } } } /// /// Check if this slot has a placed card /// public bool HasPlacedCard() { return _placedCard != null; } /// /// Get the placed card (if any) /// public AlbumCard GetPlacedCard() { return _placedCard; } private void OnEnable() { // Check if we should spawn a card for this slot CheckAndSpawnOwnedCard(); // Setup preview card display if slot is empty SetupPreviewCard(); } /// /// Setup the preview card display to show target card with preview visuals /// Preview stays hidden until user taps to show it /// private void SetupPreviewCard() { if (previewCardDisplay == null || targetCardDefinition == null) return; // Only setup preview if slot is empty if (_isOccupiedPermanently || _placedCard != null) { // Hide preview if slot is occupied previewCardDisplay.gameObject.SetActive(false); return; } // Setup preview card data CardData previewData = targetCardDefinition.CreateCardData(); previewData.Rarity = CardRarity.Normal; // Show as normal rarity previewCardDisplay.SetupCard(previewData); // Apply preview visuals (black tint and ?????? name) previewCardDisplay.SetPreviewVisuals(); // Keep preview hidden - it'll show when user taps to enlarge previewCardDisplay.gameObject.SetActive(false); Logging.Debug($"[AlbumCardSlot] Setup preview card for {targetCardDefinition.Name} (hidden until tap)"); } /// /// Check if player owns the card for this slot and spawn it if so /// 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) { Logging.Warning($"[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); } } /// /// Spawn an AlbumCard in this slot /// private void SpawnAlbumCard(CardData cardData) { GameObject cardObj = Instantiate(albumCardPrefab, transform); AlbumCard albumCard = cardObj.GetComponent(); if (albumCard != null) { albumCard.SetupCard(cardData); albumCard.SetParentSlot(this); _placedCard = albumCard; _isOccupiedPermanently = true; // Resize the card to match the slot size (same as placed cards) RectTransform cardRect = albumCard.transform as RectTransform; RectTransform slotRect = transform as RectTransform; if (cardRect != null && slotRect != null) { // Set height to match slot height (AspectRatioFitter will handle width) float targetHeight = slotRect.rect.height; cardRect.sizeDelta = new Vector2(cardRect.sizeDelta.x, targetHeight); // Ensure position and rotation are centered cardRect.localPosition = Vector3.zero; cardRect.localRotation = Quaternion.identity; } // Register with AlbumViewPage for enlarge/shrink handling AlbumViewPage albumPage = FindObjectOfType(); if (albumPage != null) { albumPage.RegisterAlbumCard(albumCard); } Logging.Debug($"[AlbumCardSlot] Spawned owned card '{cardData.Name}' ({cardData.Rarity}) in slot"); } else { Logging.Warning($"[AlbumCardSlot] Spawned prefab has no AlbumCard component!"); Destroy(cardObj); } } /// /// Get the target card definition for this slot /// public CardDefinition GetTargetCardDefinition() { return targetCardDefinition; } /// /// Handle click on slot - show/hide preview if empty /// public void OnPointerClick(PointerEventData eventData) { Logging.Debug($"[CLICK-TRACE-SLOT] OnPointerClick on {name}, _isOccupiedPermanently={_isOccupiedPermanently}, _placedCard={((_placedCard != null) ? _placedCard.name : "NULL")}, _isPreviewShowing={_isPreviewShowing}, position={eventData.position}"); // Only handle clicks if slot is empty if (_isOccupiedPermanently || _placedCard != null) { Logging.Debug($"[CLICK-TRACE-SLOT] {name} - Slot is occupied, ignoring"); return; } // Only handle if we have a preview card setup if (previewCardDisplay == null || targetCardDefinition == null) { Logging.Debug($"[CLICK-TRACE-SLOT] {name} - No preview setup, ignoring"); return; } if (_isPreviewShowing) { Logging.Debug($"[CLICK-TRACE-SLOT] {name} - Preview is showing, hiding it"); HidePreview(); } else { Logging.Debug($"[CLICK-TRACE-SLOT] {name} - Preview is hidden, showing it"); ShowPreview(); } } /// /// Show enlarged preview of target card /// private void ShowPreview() { if (_isPreviewShowing || previewCardDisplay == null) return; _isPreviewShowing = true; // Show the preview card (already has preview visuals applied) previewCardDisplay.gameObject.SetActive(true); // Enable preview mode so clicks on CardDisplay forward to this slot previewCardDisplay.SetPreviewMode(true, this); // Reset to normal scale before enlarging previewCardDisplay.transform.localScale = _previewOriginalScale; // Get AlbumViewPage to show backdrop and reparent AlbumViewPage albumPage = FindObjectOfType(); if (albumPage != null) { albumPage.ShowSlotPreview(this, previewCardDisplay.transform); } // Scale up preview card Pixelplacement.Tween.LocalScale(previewCardDisplay.transform, _previewOriginalScale * previewEnlargedScale, previewScaleDuration, 0f, Pixelplacement.Tween.EaseOutBack); Logging.Debug($"[AlbumCardSlot] Showing preview for {targetCardDefinition.Name}"); } /// /// Hide preview and return to normal /// private void HidePreview() { if (!_isPreviewShowing || previewCardDisplay == null) return; _isPreviewShowing = false; // Disable preview mode on CardDisplay previewCardDisplay.SetPreviewMode(false, null); // Get AlbumViewPage to hide backdrop AlbumViewPage albumPage = FindObjectOfType(); if (albumPage != null) { albumPage.HideSlotPreview(this, previewCardDisplay.transform, () => { // After shrink completes, reparent back to slot previewCardDisplay.transform.SetParent(transform, false); // Reset RectTransform properties RectTransform previewRect = previewCardDisplay.transform as RectTransform; if (previewRect != null) { // Set anchors to stretch in all directions (matching original setup) previewRect.anchorMin = Vector2.zero; // (0, 0) previewRect.anchorMax = Vector2.one; // (1, 1) // Reset offsets to zero (left, right, top, bottom all = 0) previewRect.offsetMin = Vector2.zero; // Sets left and bottom to 0 previewRect.offsetMax = Vector2.zero; // Sets right and top to 0 (note: these are negative values internally) previewRect.pivot = new Vector2(0.5f, 0.5f); } previewCardDisplay.transform.localPosition = Vector3.zero; previewCardDisplay.transform.localRotation = Quaternion.identity; previewCardDisplay.transform.localScale = _previewOriginalScale; // Hide the preview card after returning to slot previewCardDisplay.gameObject.SetActive(false); Logging.Debug($"[AlbumCardSlot] Preview hidden and reset for {targetCardDefinition.Name}"); }); } Logging.Debug($"[AlbumCardSlot] Hiding preview for {targetCardDefinition.Name}"); } /// /// Public method to dismiss preview - can be called by CardDisplay when clicked /// public void DismissPreview() { Logging.Debug($"[CLICK-TRACE-SLOT] DismissPreview called on {name}"); HidePreview(); } /// /// Get the target card definition for this slot /// public CardDefinition TargetCardDefinition => targetCardDefinition; } }