using System; using AppleHills.Data.CardSystem; using Pixelplacement; using Pixelplacement.TweenSystem; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; namespace UI.CardSystem { /// /// Flippable card wrapper that shows a card back, then flips to reveal the CardDisplay front. /// This component nests an existing CardDisplay prefab to reuse card visuals everywhere. /// public class FlippableCard : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IPointerClickHandler { [Header("Card References")] [SerializeField] private GameObject cardBackObject; // The card back visual [SerializeField] private GameObject cardFrontObject; // Your CardDisplay prefab instance [SerializeField] private CardDisplay cardDisplay; // Reference to CardDisplay component [Header("Idle Hover Animation")] [SerializeField] private bool enableIdleHover = true; [SerializeField] private float idleHoverHeight = 10f; [SerializeField] private float idleHoverDuration = 1.5f; [SerializeField] private float hoverScaleMultiplier = 1.05f; [Header("Flip Animation")] [SerializeField] private float flipDuration = 0.6f; [SerializeField] private float flipScalePunch = 1.1f; // State private bool _isFlipped = false; private bool _isFlipping = false; private bool _isHovering = false; private TweenBase _idleHoverTween; private CardData _cardData; private Vector2 _originalPosition; // Track original spawn position // Events public event Action OnCardRevealed; public bool IsFlipped => _isFlipped; public CardData CardData => _cardData; private void Awake() { // Auto-find CardDisplay if not assigned if (cardDisplay == null && cardFrontObject != null) { cardDisplay = cardFrontObject.GetComponent(); } // Start with back showing, front hidden if (cardBackObject != null) cardBackObject.SetActive(true); if (cardFrontObject != null) cardFrontObject.SetActive(false); } private void Start() { // Save the original position so we can return to it after hover RectTransform rectTransform = GetComponent(); if (rectTransform != null) { _originalPosition = rectTransform.anchoredPosition; } // Start idle hover animation if (enableIdleHover && !_isFlipped) { StartIdleHover(); } } /// /// Setup the card data (stores it but doesn't reveal until flipped) /// public void SetupCard(CardData data) { _cardData = data; // Setup the CardDisplay but keep it hidden if (cardDisplay != null) { cardDisplay.SetupCard(data); } } /// /// Flip the card to reveal the front /// public void FlipToReveal() { if (_isFlipped || _isFlipping) return; _isFlipping = true; // Stop idle hover StopIdleHover(); // Flip animation: rotate Y 0 -> 90 (hide back) -> 180 (show front) Transform cardTransform = transform; // Phase 1: Flip to 90 degrees (edge view, hide back) Tween.LocalRotation(cardTransform, Quaternion.Euler(0, 90, 0), flipDuration * 0.5f, 0f, Tween.EaseInOut, completeCallback: () => { // Switch visuals at the edge if (cardBackObject != null) cardBackObject.SetActive(false); if (cardFrontObject != null) cardFrontObject.SetActive(true); // Phase 2: Flip from 90 to 180 (show front) Tween.LocalRotation(cardTransform, Quaternion.Euler(0, 180, 0), flipDuration * 0.5f, 0f, Tween.EaseInOut, completeCallback: () => { _isFlipped = true; _isFlipping = false; // Fire revealed event OnCardRevealed?.Invoke(this, _cardData); }); }); // Scale punch during flip for extra juice Vector3 originalScale = cardTransform.localScale; Tween.LocalScale(cardTransform, originalScale * flipScalePunch, flipDuration * 0.5f, 0f, Tween.EaseOutBack, completeCallback: () => { Tween.LocalScale(cardTransform, originalScale, flipDuration * 0.5f, 0f, Tween.EaseInBack); }); } /// /// Start idle hover animation (gentle bobbing) /// private void StartIdleHover() { if (_idleHoverTween != null) return; RectTransform rectTransform = GetComponent(); if (rectTransform == null) return; Vector2 originalPos = rectTransform.anchoredPosition; Vector2 targetPos = originalPos + Vector2.up * idleHoverHeight; _idleHoverTween = Tween.Value(0f, 1f, (val) => { if (rectTransform != null) { float t = Mathf.Sin(val * Mathf.PI * 2f) * 0.5f + 0.5f; // Smooth sine wave rectTransform.anchoredPosition = Vector2.Lerp(originalPos, targetPos, t); } }, idleHoverDuration, 0f, Tween.EaseInOut, Tween.LoopType.Loop); } /// /// Stop idle hover animation /// private void StopIdleHover() { if (_idleHoverTween != null) { _idleHoverTween.Stop(); _idleHoverTween = null; // Reset to ORIGINAL position (not Vector2.zero!) RectTransform rectTransform = GetComponent(); if (rectTransform != null) { Tween.AnchoredPosition(rectTransform, _originalPosition, 0.3f, 0f, Tween.EaseOutBack); } } } #region Pointer Event Handlers public void OnPointerEnter(PointerEventData eventData) { if (_isFlipped || _isFlipping) return; _isHovering = true; // Scale up slightly on hover Tween.LocalScale(transform, Vector3.one * hoverScaleMultiplier, 0.2f, 0f, Tween.EaseOutBack); } public void OnPointerExit(PointerEventData eventData) { if (_isFlipped || _isFlipping) return; _isHovering = false; // Scale back to normal Tween.LocalScale(transform, Vector3.one, 0.2f, 0f, Tween.EaseOutBack); } public void OnPointerClick(PointerEventData eventData) { if (_isFlipped || _isFlipping) return; // Flip on click FlipToReveal(); } #endregion private void OnDestroy() { StopIdleHover(); } } }