Semi-working rarity upgrades
This commit is contained in:
@@ -11,9 +11,17 @@ namespace AppleHills.Data.CardSystem
|
||||
[Serializable]
|
||||
public class CardInventory
|
||||
{
|
||||
// Dictionary of collected cards indexed by definition ID
|
||||
// Dictionary of collected cards indexed by definition ID + rarity (e.g., "Pikachu_Normal", "Pikachu_Rare")
|
||||
[SerializeField] private Dictionary<string, CardData> collectedCards = new Dictionary<string, CardData>();
|
||||
|
||||
/// <summary>
|
||||
/// Generate a unique key for a card based on definition ID and rarity
|
||||
/// </summary>
|
||||
private string GetCardKey(string definitionId, CardRarity rarity)
|
||||
{
|
||||
return $"{definitionId}_{rarity}";
|
||||
}
|
||||
|
||||
// Number of unopened booster packs the player has
|
||||
[SerializeField] private int boosterPackCount;
|
||||
|
||||
@@ -96,7 +104,9 @@ namespace AppleHills.Data.CardSystem
|
||||
{
|
||||
if (card == null) return;
|
||||
|
||||
if (collectedCards.TryGetValue(card.DefinitionId, out CardData existingCard))
|
||||
string key = GetCardKey(card.DefinitionId, card.Rarity);
|
||||
|
||||
if (collectedCards.TryGetValue(key, out CardData existingCard))
|
||||
{
|
||||
// Increase copies of existing card
|
||||
existingCard.CopiesOwned++;
|
||||
@@ -105,7 +115,7 @@ namespace AppleHills.Data.CardSystem
|
||||
{
|
||||
// Add new card to collection
|
||||
var newCard = new CardData(card);
|
||||
collectedCards[card.DefinitionId] = newCard;
|
||||
collectedCards[key] = newCard;
|
||||
|
||||
// Add to lookup dictionaries
|
||||
cardsByZone[newCard.Zone].Add(newCard);
|
||||
@@ -133,19 +143,21 @@ namespace AppleHills.Data.CardSystem
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a specific card from the collection by definition ID
|
||||
/// Get a specific card from the collection by definition ID and rarity
|
||||
/// </summary>
|
||||
public CardData GetCard(string definitionId)
|
||||
public CardData GetCard(string definitionId, CardRarity rarity)
|
||||
{
|
||||
return collectedCards.TryGetValue(definitionId, out CardData card) ? card : null;
|
||||
string key = GetCardKey(definitionId, rarity);
|
||||
return collectedCards.TryGetValue(key, out CardData card) ? card : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the player has a specific card
|
||||
/// Check if the player has a specific card at a specific rarity
|
||||
/// </summary>
|
||||
public bool HasCard(string definitionId)
|
||||
public bool HasCard(string definitionId, CardRarity rarity)
|
||||
{
|
||||
return collectedCards.ContainsKey(definitionId);
|
||||
string key = GetCardKey(definitionId, rarity);
|
||||
return collectedCards.ContainsKey(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AppleHills.Data.CardSystem;
|
||||
using Bootstrap;
|
||||
using Core;
|
||||
using Core.SaveLoad;
|
||||
using UnityEngine;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
namespace Data.CardSystem
|
||||
{
|
||||
@@ -150,6 +146,7 @@ namespace Data.CardSystem
|
||||
|
||||
/// <summary>
|
||||
/// Opens a booster pack and returns the newly obtained cards
|
||||
/// NOTE: Cards are NOT added to inventory immediately - they're added after the reveal interaction
|
||||
/// </summary>
|
||||
public List<CardData> OpenBoosterPack()
|
||||
{
|
||||
@@ -165,11 +162,9 @@ namespace Data.CardSystem
|
||||
// Draw 3 cards based on rarity distribution
|
||||
List<CardData> drawnCards = DrawRandomCards(3);
|
||||
|
||||
// Add cards to the inventory
|
||||
foreach (var card in drawnCards)
|
||||
{
|
||||
AddCardToInventory(card);
|
||||
}
|
||||
// NOTE: Cards are NOT added to inventory here anymore
|
||||
// They will be added after the player interacts with each revealed card
|
||||
// This allows us to show new/repeat status before adding to collection
|
||||
|
||||
// Notify listeners
|
||||
OnBoosterOpened?.Invoke(drawnCards);
|
||||
@@ -177,33 +172,56 @@ namespace Data.CardSystem
|
||||
Logging.Debug($"[CardSystemManager] Opened a booster pack and obtained {drawnCards.Count} cards. Remaining boosters: {playerInventory.BoosterPackCount}");
|
||||
return drawnCards;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if a card is new to the player's collection at the specified rarity
|
||||
/// </summary>
|
||||
/// <param name="cardData">The card to check</param>
|
||||
/// <param name="existingCard">Out parameter - the existing card if found, null otherwise</param>
|
||||
/// <returns>True if this is a new card at this rarity, false if already owned</returns>
|
||||
public bool IsCardNew(CardData cardData, out CardData existingCard)
|
||||
{
|
||||
if (playerInventory.HasCard(cardData.DefinitionId, cardData.Rarity))
|
||||
{
|
||||
existingCard = playerInventory.GetCard(cardData.DefinitionId, cardData.Rarity);
|
||||
return false;
|
||||
}
|
||||
existingCard = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a card to the player's inventory after reveal (delayed add)
|
||||
/// Public wrapper for AddCardToInventory to support delayed inventory updates
|
||||
/// </summary>
|
||||
public void AddCardToInventoryDelayed(CardData card)
|
||||
{
|
||||
AddCardToInventory(card);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a card to the player's inventory, handles duplicates
|
||||
/// </summary>
|
||||
private void AddCardToInventory(CardData card)
|
||||
{
|
||||
// Check if the player already has this card type (definition)
|
||||
if (playerInventory.HasCard(card.DefinitionId))
|
||||
// Check if the player already has this card at this rarity
|
||||
if (playerInventory.HasCard(card.DefinitionId, card.Rarity))
|
||||
{
|
||||
CardData existingCard = playerInventory.GetCard(card.DefinitionId);
|
||||
CardData existingCard = playerInventory.GetCard(card.DefinitionId, card.Rarity);
|
||||
existingCard.CopiesOwned++;
|
||||
|
||||
// Check if the card can be upgraded
|
||||
if (existingCard.TryUpgradeRarity())
|
||||
{
|
||||
OnCardRarityUpgraded?.Invoke(existingCard);
|
||||
}
|
||||
// Note: Upgrades are now handled separately in BoosterOpeningPage
|
||||
// We don't auto-upgrade here anymore
|
||||
|
||||
Logging.Debug($"[CardSystemManager] Added duplicate card '{card.Name}'. Now have {existingCard.CopiesOwned} copies.");
|
||||
Logging.Debug($"[CardSystemManager] Added duplicate card '{card.Name}' ({card.Rarity}). Now have {existingCard.CopiesOwned} copies.");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add new card
|
||||
// Add new card at this rarity
|
||||
playerInventory.AddCard(card);
|
||||
OnCardCollected?.Invoke(card);
|
||||
|
||||
Logging.Debug($"[CardSystemManager] Added new card '{card.Name}' to collection.");
|
||||
Logging.Debug($"[CardSystemManager] Added new card '{card.Name}' ({card.Rarity}) to collection.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -300,11 +318,17 @@ namespace Data.CardSystem
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether a specific card definition has been collected
|
||||
/// Returns whether a specific card definition has been collected (at any rarity)
|
||||
/// </summary>
|
||||
public bool IsCardCollected(string definitionId)
|
||||
{
|
||||
return playerInventory.HasCard(definitionId);
|
||||
// Check if the card exists at any rarity
|
||||
foreach (CardRarity rarity in System.Enum.GetValues(typeof(CardRarity)))
|
||||
{
|
||||
if (playerInventory.HasCard(definitionId, rarity))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -10,7 +10,6 @@ using UI.DragAndDrop.Core;
|
||||
using Unity.Cinemachine;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace UI.CardSystem
|
||||
{
|
||||
@@ -39,6 +38,7 @@ namespace UI.CardSystem
|
||||
[SerializeField] private float boosterDisappearDuration = 0.5f;
|
||||
[SerializeField] private CinemachineImpulseSource impulseSource;
|
||||
[SerializeField] private ParticleSystem openingParticleSystem;
|
||||
[SerializeField] private Transform albumIcon; // Target for card fly-away animation
|
||||
|
||||
private int _availableBoosterCount;
|
||||
private BoosterPackDraggable _currentBoosterInCenter;
|
||||
@@ -46,9 +46,10 @@ namespace UI.CardSystem
|
||||
private List<GameObject> _currentRevealedCards = new List<GameObject>();
|
||||
private CardData[] _currentCardData;
|
||||
private int _revealedCardCount;
|
||||
private int _cardsCompletedInteraction; // Track how many cards finished their new/repeat interaction
|
||||
private bool _isProcessingOpening;
|
||||
private const int MAX_VISIBLE_BOOSTERS = 3;
|
||||
|
||||
private FlippableCard _currentActiveCard; // The card currently awaiting interaction
|
||||
private void Awake()
|
||||
{
|
||||
// Make sure we have a CanvasGroup for transitions
|
||||
@@ -499,6 +500,7 @@ namespace UI.CardSystem
|
||||
|
||||
_currentRevealedCards.Clear();
|
||||
_revealedCardCount = 0;
|
||||
_cardsCompletedInteraction = 0; // Reset interaction count
|
||||
|
||||
// Calculate positions
|
||||
float totalWidth = (count - 1) * cardSpacing;
|
||||
@@ -521,9 +523,18 @@ namespace UI.CardSystem
|
||||
// Setup the card data (stored but not revealed yet)
|
||||
flippableCard.SetupCard(_currentCardData[i]);
|
||||
|
||||
// Subscribe to reveal event to track when flipped
|
||||
// Subscribe to flip started event (to disable other cards IMMEDIATELY)
|
||||
int cardIndex = i; // Capture for closure
|
||||
flippableCard.OnFlipStarted += OnCardFlipStarted;
|
||||
|
||||
// Subscribe to reveal event to track when flipped
|
||||
flippableCard.OnCardRevealed += (card, data) => OnCardRevealed(cardIndex);
|
||||
|
||||
// Subscribe to inactive click event (for jiggle effect)
|
||||
flippableCard.OnClickedWhileInactive += OnCardClickedWhileInactive;
|
||||
|
||||
// Initially, all cards are clickable (for flipping)
|
||||
flippableCard.SetClickable(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -538,6 +549,24 @@ namespace UI.CardSystem
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle when a card flip starts (disable all other cards IMMEDIATELY)
|
||||
/// </summary>
|
||||
private void OnCardFlipStarted(FlippableCard flippingCard)
|
||||
{
|
||||
Debug.Log($"[BoosterOpeningPage] Card flip started, disabling all other cards.");
|
||||
|
||||
// Disable ALL cards immediately to prevent multi-flip
|
||||
foreach (GameObject cardObj in _currentRevealedCards)
|
||||
{
|
||||
FlippableCard card = cardObj.GetComponent<FlippableCard>();
|
||||
if (card != null)
|
||||
{
|
||||
card.SetClickable(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle card reveal (when flipped)
|
||||
/// </summary>
|
||||
@@ -545,29 +574,182 @@ namespace UI.CardSystem
|
||||
{
|
||||
Debug.Log($"[BoosterOpeningPage] Card {cardIndex} revealed!");
|
||||
_revealedCardCount++;
|
||||
|
||||
// Get the flippable card and card data
|
||||
FlippableCard flippableCard = _currentRevealedCards[cardIndex].GetComponent<FlippableCard>();
|
||||
if (flippableCard == null)
|
||||
{
|
||||
Debug.LogWarning($"[BoosterOpeningPage] FlippableCard not found for card {cardIndex}!");
|
||||
return;
|
||||
}
|
||||
|
||||
CardData cardData = flippableCard.CardData;
|
||||
|
||||
// Check if this is a new card using CardSystemManager
|
||||
bool isNew = Data.CardSystem.CardSystemManager.Instance.IsCardNew(cardData, out CardData existingCard);
|
||||
|
||||
if (isNew)
|
||||
{
|
||||
Debug.Log($"[BoosterOpeningPage] Card '{cardData.Name}' is NEW!");
|
||||
flippableCard.ShowAsNew();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Check if card is already Legendary - if so, skip progress bar and auto-progress
|
||||
if (existingCard.Rarity == AppleHills.Data.CardSystem.CardRarity.Legendary)
|
||||
{
|
||||
Debug.Log($"[BoosterOpeningPage] Card '{cardData.Name}' is LEGENDARY - auto-progressing!");
|
||||
// Add to inventory immediately and move to next card
|
||||
Data.CardSystem.CardSystemManager.Instance.AddCardToInventoryDelayed(cardData);
|
||||
_cardsCompletedInteraction++;
|
||||
_revealedCardCount++; // This was already incremented earlier, but we need to track completion
|
||||
EnableUnrevealedCards();
|
||||
return; // Skip showing the card enlarged
|
||||
}
|
||||
|
||||
int ownedCount = existingCard.CopiesOwned;
|
||||
Debug.Log($"[BoosterOpeningPage] Card '{cardData.Name}' is a REPEAT! Owned: {ownedCount}");
|
||||
|
||||
// Check if this card will trigger an upgrade (ownedCount + 1 >= threshold)
|
||||
bool willUpgrade = (ownedCount + 1) >= flippableCard.CardsToUpgrade && existingCard.Rarity < AppleHills.Data.CardSystem.CardRarity.Legendary;
|
||||
|
||||
if (willUpgrade)
|
||||
{
|
||||
Debug.Log($"[BoosterOpeningPage] This card will trigger upgrade! ({ownedCount + 1}/{flippableCard.CardsToUpgrade})");
|
||||
// Show as repeat - progress bar will fill and auto-trigger upgrade
|
||||
flippableCard.ShowAsRepeatWithUpgrade(ownedCount, existingCard);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Normal repeat, no upgrade
|
||||
flippableCard.ShowAsRepeat(ownedCount);
|
||||
}
|
||||
}
|
||||
|
||||
// Set this card as the active one (only this card is clickable now)
|
||||
SetActiveCard(flippableCard);
|
||||
|
||||
// Subscribe to tap event to know when interaction is complete
|
||||
flippableCard.OnCardTappedAfterReveal += (card) => OnCardCompletedInteraction(card, cardIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wait until all cards are revealed
|
||||
/// Handle when a card's interaction is complete (tapped after reveal)
|
||||
/// </summary>
|
||||
private void OnCardCompletedInteraction(FlippableCard card, int cardIndex)
|
||||
{
|
||||
Debug.Log($"[BoosterOpeningPage] Card {cardIndex} interaction complete!");
|
||||
|
||||
// Add card to inventory NOW (after player saw it)
|
||||
Data.CardSystem.CardSystemManager.Instance.AddCardToInventoryDelayed(card.CardData);
|
||||
|
||||
// Return card to normal size
|
||||
card.ReturnToNormalSize();
|
||||
|
||||
// Increment completed interaction count
|
||||
_cardsCompletedInteraction++;
|
||||
|
||||
// Clear active card
|
||||
_currentActiveCard = null;
|
||||
|
||||
// Re-enable all unrevealed cards (they can be flipped now)
|
||||
EnableUnrevealedCards();
|
||||
|
||||
Debug.Log($"[BoosterOpeningPage] Cards completed interaction: {_cardsCompletedInteraction}/{_currentCardData.Length}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set which card is currently active (only this card can be clicked)
|
||||
/// </summary>
|
||||
private void SetActiveCard(FlippableCard activeCard)
|
||||
{
|
||||
_currentActiveCard = activeCard;
|
||||
|
||||
// Disable all other cards
|
||||
foreach (GameObject cardObj in _currentRevealedCards)
|
||||
{
|
||||
FlippableCard card = cardObj.GetComponent<FlippableCard>();
|
||||
if (card != null)
|
||||
{
|
||||
// Only the active card is clickable
|
||||
card.SetClickable(card == activeCard);
|
||||
}
|
||||
}
|
||||
|
||||
Debug.Log($"[BoosterOpeningPage] Set active card. Only one card is now clickable.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Re-enable all unrevealed cards (allow them to be flipped)
|
||||
/// </summary>
|
||||
private void EnableUnrevealedCards()
|
||||
{
|
||||
foreach (GameObject cardObj in _currentRevealedCards)
|
||||
{
|
||||
FlippableCard card = cardObj.GetComponent<FlippableCard>();
|
||||
if (card != null && !card.IsFlipped)
|
||||
{
|
||||
card.SetClickable(true);
|
||||
}
|
||||
}
|
||||
|
||||
Debug.Log($"[BoosterOpeningPage] Re-enabled unrevealed cards for flipping.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle when a card is clicked while not active (jiggle the active card)
|
||||
/// </summary>
|
||||
private void OnCardClickedWhileInactive(FlippableCard inactiveCard)
|
||||
{
|
||||
Debug.Log($"[BoosterOpeningPage] Inactive card clicked, jiggling active card.");
|
||||
|
||||
if (_currentActiveCard != null)
|
||||
{
|
||||
_currentActiveCard.Jiggle();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wait until all cards are revealed AND all interactions are complete
|
||||
/// </summary>
|
||||
private IEnumerator WaitForCardReveals()
|
||||
{
|
||||
// Wait until all cards are flipped
|
||||
while (_revealedCardCount < _currentCardData.Length)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
|
||||
// All cards revealed, wait a moment
|
||||
yield return new WaitForSeconds(1f);
|
||||
Debug.Log($"[BoosterOpeningPage] All cards revealed! Waiting for interactions...");
|
||||
|
||||
// Clear cards
|
||||
foreach (GameObject card in _currentRevealedCards)
|
||||
// Wait until all cards have completed their new/repeat interaction
|
||||
while (_cardsCompletedInteraction < _currentCardData.Length)
|
||||
{
|
||||
if (card != null)
|
||||
yield return null;
|
||||
}
|
||||
|
||||
Debug.Log($"[BoosterOpeningPage] All interactions complete! Animating cards to album...");
|
||||
|
||||
// All cards revealed and interacted with, wait a moment
|
||||
yield return new WaitForSeconds(0.5f);
|
||||
|
||||
// Animate cards to album icon (or center if no icon assigned) with staggered delays
|
||||
Vector3 targetPosition = albumIcon != null ? albumIcon.position : Vector3.zero;
|
||||
|
||||
int cardIndex = 0;
|
||||
foreach (GameObject cardObj in _currentRevealedCards)
|
||||
{
|
||||
if (cardObj != null)
|
||||
{
|
||||
// Animate out
|
||||
Tween.LocalScale(card.transform, Vector3.zero, 0.3f, 0f, Tween.EaseInBack,
|
||||
completeCallback: () => Destroy(card));
|
||||
// Stagger each card with 0.5s delay
|
||||
float delay = cardIndex * 0.5f;
|
||||
|
||||
// Animate to album icon position, then destroy
|
||||
Tween.Position(cardObj.transform, targetPosition, 0.5f, delay, Tween.EaseInBack);
|
||||
Tween.LocalScale(cardObj.transform, Vector3.zero, 0.5f, delay, Tween.EaseInBack,
|
||||
completeCallback: () => Destroy(cardObj));
|
||||
|
||||
cardIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,14 @@ namespace UI.CardSystem
|
||||
[SerializeField] private float flipDuration = 0.6f;
|
||||
[SerializeField] private float flipScalePunch = 1.1f;
|
||||
|
||||
[Header("New/Repeat Card Display")]
|
||||
[SerializeField] private GameObject newCardText;
|
||||
[SerializeField] private GameObject newCardIdleText;
|
||||
[SerializeField] private GameObject repeatText;
|
||||
[SerializeField] private GameObject progressBarContainer;
|
||||
[SerializeField] private int cardsToUpgrade = 5;
|
||||
[SerializeField] private float enlargedScale = 1.5f;
|
||||
|
||||
// State
|
||||
private bool _isFlipped = false;
|
||||
private bool _isFlipping = false;
|
||||
@@ -36,12 +44,20 @@ namespace UI.CardSystem
|
||||
private TweenBase _idleHoverTween;
|
||||
private CardData _cardData;
|
||||
private Vector2 _originalPosition; // Track original spawn position
|
||||
private bool _isWaitingForTap = false; // Waiting for tap after reveal
|
||||
private bool _isNew = false; // Is this a new card
|
||||
private int _ownedCount = 0; // Owned count for repeat cards
|
||||
private bool _isClickable = true; // Can this card be clicked
|
||||
|
||||
// Events
|
||||
public event Action<FlippableCard, CardData> OnCardRevealed;
|
||||
public event Action<FlippableCard> OnCardTappedAfterReveal;
|
||||
public event Action<FlippableCard> OnClickedWhileInactive; // Fired when clicked but not clickable
|
||||
public event Action<FlippableCard> OnFlipStarted; // Fired when flip animation begins
|
||||
|
||||
public bool IsFlipped => _isFlipped;
|
||||
public CardData CardData => _cardData;
|
||||
public int CardsToUpgrade => cardsToUpgrade; // Expose upgrade threshold
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
@@ -51,11 +67,29 @@ namespace UI.CardSystem
|
||||
cardDisplay = cardFrontObject.GetComponent<CardDisplay>();
|
||||
}
|
||||
|
||||
// Start with back showing, front hidden
|
||||
// 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)
|
||||
{
|
||||
cardBackObject.transform.localRotation = Quaternion.Euler(0, 0, 0);
|
||||
cardBackObject.SetActive(true);
|
||||
}
|
||||
|
||||
if (cardFrontObject != null)
|
||||
{
|
||||
cardFrontObject.transform.localRotation = Quaternion.Euler(0, 180, 0);
|
||||
cardFrontObject.SetActive(false);
|
||||
}
|
||||
|
||||
// Hide all new/repeat UI elements initially
|
||||
if (newCardText != null)
|
||||
newCardText.SetActive(false);
|
||||
if (newCardIdleText != null)
|
||||
newCardIdleText.SetActive(false);
|
||||
if (repeatText != null)
|
||||
repeatText.SetActive(false);
|
||||
if (progressBarContainer != null)
|
||||
progressBarContainer.SetActive(false);
|
||||
}
|
||||
|
||||
private void Start()
|
||||
@@ -98,40 +132,53 @@ namespace UI.CardSystem
|
||||
|
||||
_isFlipping = true;
|
||||
|
||||
// Fire flip started event IMMEDIATELY (before animations)
|
||||
OnFlipStarted?.Invoke(this);
|
||||
|
||||
// Stop idle hover
|
||||
StopIdleHover();
|
||||
|
||||
// Flip animation: rotate Y 0 -> 90 (hide back) -> 180 (show front)
|
||||
Transform cardTransform = transform;
|
||||
// Flip animation: Rotate the visual children (back from 0→90, front from 180→0)
|
||||
// ...existing code...
|
||||
// Card back: 0° → 90° (rotates away)
|
||||
// Card front: 180° → 90° → 0° (rotates into view)
|
||||
|
||||
// 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);
|
||||
});
|
||||
});
|
||||
// Phase 1: Rotate both to 90 degrees (edge view)
|
||||
if (cardBackObject != null)
|
||||
{
|
||||
Tween.LocalRotation(cardBackObject.transform, Quaternion.Euler(0, 90, 0), flipDuration * 0.5f, 0f, Tween.EaseInOut);
|
||||
}
|
||||
|
||||
if (cardFrontObject != null)
|
||||
{
|
||||
Tween.LocalRotation(cardFrontObject.transform, Quaternion.Euler(0, 90, 0), flipDuration * 0.5f, 0f, Tween.EaseInOut,
|
||||
completeCallback: () =>
|
||||
{
|
||||
// At edge (90°), switch visibility
|
||||
if (cardBackObject != null)
|
||||
cardBackObject.SetActive(false);
|
||||
if (cardFrontObject != null)
|
||||
cardFrontObject.SetActive(true);
|
||||
|
||||
// Phase 2: Rotate front from 90 to 0 (show at correct orientation)
|
||||
Tween.LocalRotation(cardFrontObject.transform, Quaternion.Euler(0, 0, 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,
|
||||
Vector3 originalScale = transform.localScale;
|
||||
Tween.LocalScale(transform, originalScale * flipScalePunch, flipDuration * 0.5f, 0f, Tween.EaseOutBack,
|
||||
completeCallback: () =>
|
||||
{
|
||||
Tween.LocalScale(cardTransform, originalScale, flipDuration * 0.5f, 0f, Tween.EaseInBack);
|
||||
Tween.LocalScale(transform, originalScale, flipDuration * 0.5f, 0f, Tween.EaseInBack);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -207,6 +254,21 @@ namespace UI.CardSystem
|
||||
|
||||
public void OnPointerClick(PointerEventData eventData)
|
||||
{
|
||||
// If not clickable, notify and return
|
||||
if (!_isClickable)
|
||||
{
|
||||
OnClickedWhileInactive?.Invoke(this);
|
||||
return;
|
||||
}
|
||||
|
||||
// If waiting for tap after reveal, handle that
|
||||
if (_isWaitingForTap)
|
||||
{
|
||||
OnCardTappedAfterReveal?.Invoke(this);
|
||||
_isWaitingForTap = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_isFlipped || _isFlipping)
|
||||
return;
|
||||
|
||||
@@ -216,6 +278,352 @@ namespace UI.CardSystem
|
||||
|
||||
#endregion
|
||||
|
||||
#region New/Repeat Card Display
|
||||
|
||||
/// <summary>
|
||||
/// Show this card as a new card (enlarge, show "NEW CARD" text, wait for tap)
|
||||
/// </summary>
|
||||
public void ShowAsNew()
|
||||
{
|
||||
_isNew = true;
|
||||
_isWaitingForTap = true;
|
||||
|
||||
// Show new card text
|
||||
if (newCardText != null)
|
||||
newCardText.SetActive(true);
|
||||
|
||||
// Enlarge the card
|
||||
EnlargeCard();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show this card as a repeat that will trigger an upgrade (enlarge, show progress, auto-transition to upgrade)
|
||||
/// </summary>
|
||||
/// <param name="ownedCount">Number of copies owned BEFORE this one</param>
|
||||
/// <param name="lowerRarityCard">The existing card data at lower rarity (for upgrade reference)</param>
|
||||
public void ShowAsRepeatWithUpgrade(int ownedCount, AppleHills.Data.CardSystem.CardData lowerRarityCard)
|
||||
{
|
||||
_isNew = false;
|
||||
_ownedCount = ownedCount;
|
||||
_isWaitingForTap = false; // Don't wait yet - upgrade will happen automatically
|
||||
|
||||
// Show repeat text
|
||||
if (repeatText != null)
|
||||
repeatText.SetActive(true);
|
||||
|
||||
// Enlarge the card
|
||||
EnlargeCard();
|
||||
|
||||
// Show progress bar with owned count, then auto-trigger upgrade
|
||||
ShowProgressBar(ownedCount, () =>
|
||||
{
|
||||
// Progress animation complete - trigger upgrade!
|
||||
TriggerUpgradeTransition(lowerRarityCard);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Trigger the upgrade transition (called after progress bar fills)
|
||||
/// </summary>
|
||||
private void TriggerUpgradeTransition(AppleHills.Data.CardSystem.CardData lowerRarityCard)
|
||||
{
|
||||
Debug.Log($"[FlippableCard] Triggering upgrade transition from {lowerRarityCard.Rarity}!");
|
||||
|
||||
AppleHills.Data.CardSystem.CardRarity oldRarity = lowerRarityCard.Rarity;
|
||||
AppleHills.Data.CardSystem.CardRarity newRarity = oldRarity + 1;
|
||||
|
||||
// Reset the lower rarity count to 0
|
||||
lowerRarityCard.CopiesOwned = 0;
|
||||
|
||||
// Create upgraded card data
|
||||
AppleHills.Data.CardSystem.CardData upgradedCardData = new AppleHills.Data.CardSystem.CardData(_cardData);
|
||||
upgradedCardData.Rarity = newRarity;
|
||||
upgradedCardData.CopiesOwned = 1;
|
||||
|
||||
// Check if we already have this card at the higher rarity
|
||||
bool isNewAtHigherRarity = Data.CardSystem.CardSystemManager.Instance.IsCardNew(upgradedCardData, out AppleHills.Data.CardSystem.CardData existingHigherRarity);
|
||||
|
||||
// Add the higher rarity card to inventory
|
||||
Data.CardSystem.CardSystemManager.Instance.GetCardInventory().AddCard(upgradedCardData);
|
||||
|
||||
// Update our displayed card data
|
||||
_cardData.Rarity = newRarity;
|
||||
|
||||
// Transition to appropriate display
|
||||
if (isNewAtHigherRarity || newRarity == AppleHills.Data.CardSystem.CardRarity.Legendary)
|
||||
{
|
||||
// Show as NEW at higher rarity
|
||||
TransitionToNewCardView(newRarity);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Show progress for higher rarity, then transition to NEW
|
||||
int ownedAtHigherRarity = existingHigherRarity.CopiesOwned;
|
||||
ShowProgressBar(ownedAtHigherRarity, () =>
|
||||
{
|
||||
TransitionToNewCardView(newRarity);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show this card as a repeat (enlarge, show progress bar, wait for tap)
|
||||
/// </summary>
|
||||
/// <param name="ownedCount">Number of copies owned BEFORE this one</param>
|
||||
public void ShowAsRepeat(int ownedCount)
|
||||
{
|
||||
_isNew = false;
|
||||
_ownedCount = ownedCount;
|
||||
_isWaitingForTap = true;
|
||||
|
||||
// Show repeat text
|
||||
if (repeatText != null)
|
||||
repeatText.SetActive(true);
|
||||
|
||||
// Enlarge the card
|
||||
EnlargeCard();
|
||||
|
||||
// Show progress bar with owned count, then blink new element
|
||||
ShowProgressBar(ownedCount, () =>
|
||||
{
|
||||
// Progress animation complete
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show this card as upgraded (hide progress bar, show as new with upgraded rarity)
|
||||
/// </summary>
|
||||
public void ShowAsUpgraded(AppleHills.Data.CardSystem.CardRarity oldRarity, AppleHills.Data.CardSystem.CardRarity newRarity)
|
||||
{
|
||||
_isNew = true;
|
||||
_isWaitingForTap = true;
|
||||
|
||||
// Update the CardDisplay to show new rarity
|
||||
if (cardDisplay != null && _cardData != null)
|
||||
{
|
||||
_cardData.Rarity = newRarity;
|
||||
cardDisplay.SetupCard(_cardData);
|
||||
}
|
||||
|
||||
// Hide progress bar and repeat text
|
||||
if (progressBarContainer != null)
|
||||
progressBarContainer.SetActive(false);
|
||||
if (repeatText != null)
|
||||
repeatText.SetActive(false);
|
||||
|
||||
// Show new card text (it's now a "new" card at the higher rarity)
|
||||
if (newCardText != null)
|
||||
newCardText.SetActive(true);
|
||||
|
||||
Debug.Log($"[FlippableCard] Card upgraded from {oldRarity} to {newRarity}! Showing as NEW.");
|
||||
|
||||
// Card is already enlarged from the repeat display, so no need to enlarge again
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show this card as upgraded with progress bar (already have copies at higher rarity)
|
||||
/// </summary>
|
||||
public void ShowAsUpgradedWithProgress(AppleHills.Data.CardSystem.CardRarity oldRarity, AppleHills.Data.CardSystem.CardRarity newRarity, int ownedAtNewRarity)
|
||||
{
|
||||
_isNew = false;
|
||||
_isWaitingForTap = false; // Don't wait for tap yet, progress bar will complete first
|
||||
|
||||
// Hide new card text
|
||||
if (newCardText != null)
|
||||
newCardText.SetActive(false);
|
||||
|
||||
// Show repeat text (it's a repeat at the new rarity)
|
||||
if (repeatText != null)
|
||||
repeatText.SetActive(true);
|
||||
|
||||
// Show progress bar for the new rarity
|
||||
ShowProgressBar(ownedAtNewRarity, () =>
|
||||
{
|
||||
// Progress animation complete - now transition to "NEW CARD" view
|
||||
TransitionToNewCardView(newRarity);
|
||||
});
|
||||
|
||||
Debug.Log($"[FlippableCard] Card upgraded from {oldRarity} to {newRarity}! Showing progress {ownedAtNewRarity}/5");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transition to "NEW CARD" view after upgrade progress completes
|
||||
/// </summary>
|
||||
private void TransitionToNewCardView(AppleHills.Data.CardSystem.CardRarity newRarity)
|
||||
{
|
||||
Debug.Log($"[FlippableCard] Transitioning to NEW CARD view at {newRarity} rarity");
|
||||
|
||||
// Update the CardDisplay to show new rarity
|
||||
if (cardDisplay != null && _cardData != null)
|
||||
{
|
||||
_cardData.Rarity = newRarity;
|
||||
cardDisplay.SetupCard(_cardData);
|
||||
}
|
||||
|
||||
// Hide progress bar and repeat text
|
||||
if (progressBarContainer != null)
|
||||
progressBarContainer.SetActive(false);
|
||||
if (repeatText != null)
|
||||
repeatText.SetActive(false);
|
||||
|
||||
// Show "NEW CARD" text
|
||||
if (newCardText != null)
|
||||
newCardText.SetActive(true);
|
||||
|
||||
// Now wait for tap
|
||||
_isNew = true;
|
||||
_isWaitingForTap = true;
|
||||
|
||||
Debug.Log($"[FlippableCard] Now showing as NEW CARD at {newRarity}, waiting for tap");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enlarge the card
|
||||
/// </summary>
|
||||
private void EnlargeCard()
|
||||
{
|
||||
Tween.LocalScale(transform, Vector3.one * enlargedScale, 0.3f, 0f, Tween.EaseOutBack);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return card to normal size
|
||||
/// </summary>
|
||||
public void ReturnToNormalSize()
|
||||
{
|
||||
Tween.LocalScale(transform, Vector3.one, 0.3f, 0f, Tween.EaseOutBack, completeCallback: () =>
|
||||
{
|
||||
// After returning to normal, hide new card text, show idle text
|
||||
if (_isNew)
|
||||
{
|
||||
if (newCardText != null)
|
||||
newCardText.SetActive(false);
|
||||
if (newCardIdleText != null)
|
||||
newCardIdleText.SetActive(true);
|
||||
}
|
||||
|
||||
// Keep repeat text visible
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show progress bar with owned count, then blink the new element
|
||||
/// </summary>
|
||||
private void ShowProgressBar(int ownedCount, System.Action onComplete)
|
||||
{
|
||||
if (progressBarContainer == null)
|
||||
{
|
||||
onComplete?.Invoke();
|
||||
return;
|
||||
}
|
||||
|
||||
progressBarContainer.SetActive(true);
|
||||
|
||||
// Get all child Image components
|
||||
UnityEngine.UI.Image[] progressElements = progressBarContainer.GetComponentsInChildren<UnityEngine.UI.Image>(true);
|
||||
|
||||
// Check if we have the required number of elements (should match cardsToUpgrade)
|
||||
if (progressElements.Length < cardsToUpgrade)
|
||||
{
|
||||
Debug.LogWarning($"[FlippableCard] Not enough Image components in progress bar! Expected {cardsToUpgrade}, found {progressElements.Length}");
|
||||
onComplete?.Invoke();
|
||||
return;
|
||||
}
|
||||
|
||||
// Disable all elements first
|
||||
foreach (var img in progressElements)
|
||||
{
|
||||
img.enabled = false;
|
||||
}
|
||||
|
||||
// Show owned count (from the END, going backwards)
|
||||
// E.g., if owned 3 cards, enable elements at index [4], [3], [2] (last 3 elements)
|
||||
int startIndex = Mathf.Max(0, cardsToUpgrade - ownedCount);
|
||||
for (int i = startIndex; i < cardsToUpgrade && i < progressElements.Length; i++)
|
||||
{
|
||||
progressElements[i].enabled = true;
|
||||
}
|
||||
|
||||
// Wait a moment, then blink the new element
|
||||
// New element is at index (cardsToUpgrade - ownedCount - 1)
|
||||
int newElementIndex = Mathf.Max(0, cardsToUpgrade - ownedCount - 1);
|
||||
if (newElementIndex >= 0 && newElementIndex < progressElements.Length)
|
||||
{
|
||||
Tween.Value(0f, 1f, (val) => { }, 0.3f, 0f, completeCallback: () =>
|
||||
{
|
||||
BlinkProgressElement(newElementIndex, progressElements, onComplete);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
onComplete?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Blink a progress element (enable/disable rapidly)
|
||||
/// </summary>
|
||||
private void BlinkProgressElement(int index, UnityEngine.UI.Image[] elements, System.Action onComplete)
|
||||
{
|
||||
if (index < 0 || index >= elements.Length)
|
||||
{
|
||||
onComplete?.Invoke();
|
||||
return;
|
||||
}
|
||||
|
||||
UnityEngine.UI.Image element = elements[index];
|
||||
int blinkCount = 0;
|
||||
const int maxBlinks = 3;
|
||||
|
||||
void Blink()
|
||||
{
|
||||
element.enabled = !element.enabled;
|
||||
blinkCount++;
|
||||
|
||||
if (blinkCount >= maxBlinks * 2)
|
||||
{
|
||||
element.enabled = true; // End on enabled
|
||||
onComplete?.Invoke();
|
||||
}
|
||||
else
|
||||
{
|
||||
Tween.Value(0f, 1f, (val) => { }, 0.15f, 0f, completeCallback: Blink);
|
||||
}
|
||||
}
|
||||
|
||||
Blink();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enable or disable clickability of this card
|
||||
/// </summary>
|
||||
public void SetClickable(bool clickable)
|
||||
{
|
||||
_isClickable = clickable;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Jiggle the card (shake animation)
|
||||
/// </summary>
|
||||
public void Jiggle()
|
||||
{
|
||||
// Quick shake animation - rotate left, then right, then center
|
||||
Transform cardTransform = transform;
|
||||
Quaternion originalRotation = cardTransform.localRotation;
|
||||
|
||||
// Shake sequence: 0 -> -5 -> +5 -> 0
|
||||
Tween.LocalRotation(cardTransform, Quaternion.Euler(0, 0, -5), 0.05f, 0f, Tween.EaseInOut,
|
||||
completeCallback: () =>
|
||||
{
|
||||
Tween.LocalRotation(cardTransform, Quaternion.Euler(0, 0, 5), 0.1f, 0f, Tween.EaseInOut,
|
||||
completeCallback: () =>
|
||||
{
|
||||
Tween.LocalRotation(cardTransform, originalRotation, 0.05f, 0f, Tween.EaseInOut);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
StopIdleHover();
|
||||
|
||||
Reference in New Issue
Block a user