Semi-working rarity upgrades
This commit is contained in:
264
Assets/Editor/CardSystem/CardSystemLivePreview.cs
Normal file
264
Assets/Editor/CardSystem/CardSystemLivePreview.cs
Normal file
@@ -0,0 +1,264 @@
|
|||||||
|
#if UNITY_EDITOR
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
using Data.CardSystem;
|
||||||
|
using AppleHills.Data.CardSystem;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace AppleHills.Editor
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Live preview window for the Card System. Shows real-time collection status.
|
||||||
|
/// </summary>
|
||||||
|
public class CardSystemLivePreview : EditorWindow
|
||||||
|
{
|
||||||
|
private Vector2 scrollPosition;
|
||||||
|
private bool isSubscribed = false;
|
||||||
|
private double lastUpdateTime = 0;
|
||||||
|
private const double UPDATE_INTERVAL = 1.0; // Poll every 1 second as backup
|
||||||
|
|
||||||
|
// Cache for display
|
||||||
|
private Dictionary<CardRarity, List<CardData>> cardsByRarity = new Dictionary<CardRarity, List<CardData>>();
|
||||||
|
private int totalCards = 0;
|
||||||
|
private int totalUniqueCards = 0;
|
||||||
|
private int boosterCount = 0;
|
||||||
|
private string lastEventMessage = "";
|
||||||
|
|
||||||
|
[MenuItem("Tools/Card System/Live Collection Preview")]
|
||||||
|
public static void ShowWindow()
|
||||||
|
{
|
||||||
|
CardSystemLivePreview window = GetWindow<CardSystemLivePreview>("Card Collection Live");
|
||||||
|
window.minSize = new Vector2(400, 300);
|
||||||
|
window.Show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnEnable()
|
||||||
|
{
|
||||||
|
EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
|
||||||
|
|
||||||
|
if (Application.isPlaying)
|
||||||
|
{
|
||||||
|
SubscribeToEvents();
|
||||||
|
RefreshData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDisable()
|
||||||
|
{
|
||||||
|
EditorApplication.playModeStateChanged -= OnPlayModeStateChanged;
|
||||||
|
UnsubscribeFromEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPlayModeStateChanged(PlayModeStateChange state)
|
||||||
|
{
|
||||||
|
if (state == PlayModeStateChange.EnteredPlayMode)
|
||||||
|
{
|
||||||
|
SubscribeToEvents();
|
||||||
|
RefreshData();
|
||||||
|
}
|
||||||
|
else if (state == PlayModeStateChange.ExitingPlayMode)
|
||||||
|
{
|
||||||
|
UnsubscribeFromEvents();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SubscribeToEvents()
|
||||||
|
{
|
||||||
|
if (isSubscribed || !Application.isPlaying) return;
|
||||||
|
|
||||||
|
if (CardSystemManager.Instance != null)
|
||||||
|
{
|
||||||
|
CardSystemManager.Instance.OnBoosterOpened += OnBoosterOpened;
|
||||||
|
CardSystemManager.Instance.OnCardCollected += OnCardCollected;
|
||||||
|
CardSystemManager.Instance.OnCardRarityUpgraded += OnCardRarityUpgraded;
|
||||||
|
CardSystemManager.Instance.OnBoosterCountChanged += OnBoosterCountChanged;
|
||||||
|
|
||||||
|
isSubscribed = true;
|
||||||
|
Debug.Log("[CardSystemLivePreview] Subscribed to CardSystemManager events");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UnsubscribeFromEvents()
|
||||||
|
{
|
||||||
|
if (!isSubscribed) return;
|
||||||
|
|
||||||
|
if (CardSystemManager.Instance != null)
|
||||||
|
{
|
||||||
|
CardSystemManager.Instance.OnBoosterOpened -= OnBoosterOpened;
|
||||||
|
CardSystemManager.Instance.OnCardCollected -= OnCardCollected;
|
||||||
|
CardSystemManager.Instance.OnCardRarityUpgraded -= OnCardRarityUpgraded;
|
||||||
|
CardSystemManager.Instance.OnBoosterCountChanged -= OnBoosterCountChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
isSubscribed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event Handlers
|
||||||
|
private void OnBoosterOpened(List<CardData> cards)
|
||||||
|
{
|
||||||
|
lastEventMessage = $"Booster opened! Drew {cards.Count} cards";
|
||||||
|
RefreshData();
|
||||||
|
Repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnCardCollected(CardData card)
|
||||||
|
{
|
||||||
|
lastEventMessage = $"New card collected: {card.Name} ({card.Rarity})";
|
||||||
|
RefreshData();
|
||||||
|
Repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnCardRarityUpgraded(CardData card)
|
||||||
|
{
|
||||||
|
lastEventMessage = $"Card upgraded: {card.Name} → {card.Rarity}!";
|
||||||
|
RefreshData();
|
||||||
|
Repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnBoosterCountChanged(int count)
|
||||||
|
{
|
||||||
|
boosterCount = count;
|
||||||
|
lastEventMessage = $"Booster count changed: {count}";
|
||||||
|
Repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RefreshData()
|
||||||
|
{
|
||||||
|
if (!Application.isPlaying || CardSystemManager.Instance == null) return;
|
||||||
|
|
||||||
|
var allCards = CardSystemManager.Instance.GetAllCollectedCards();
|
||||||
|
|
||||||
|
// Group by rarity
|
||||||
|
cardsByRarity.Clear();
|
||||||
|
cardsByRarity[CardRarity.Normal] = allCards.Where(c => c.Rarity == CardRarity.Normal).ToList();
|
||||||
|
cardsByRarity[CardRarity.Rare] = allCards.Where(c => c.Rarity == CardRarity.Rare).ToList();
|
||||||
|
cardsByRarity[CardRarity.Legendary] = allCards.Where(c => c.Rarity == CardRarity.Legendary).ToList();
|
||||||
|
|
||||||
|
totalCards = allCards.Sum(c => c.CopiesOwned);
|
||||||
|
totalUniqueCards = allCards.Count;
|
||||||
|
boosterCount = CardSystemManager.Instance.GetBoosterPackCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Update()
|
||||||
|
{
|
||||||
|
if (!Application.isPlaying) return;
|
||||||
|
|
||||||
|
// Poll every second as backup (in case events are missed)
|
||||||
|
if (EditorApplication.timeSinceStartup - lastUpdateTime > UPDATE_INTERVAL)
|
||||||
|
{
|
||||||
|
lastUpdateTime = EditorApplication.timeSinceStartup;
|
||||||
|
|
||||||
|
if (!isSubscribed)
|
||||||
|
{
|
||||||
|
SubscribeToEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
RefreshData();
|
||||||
|
Repaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGUI()
|
||||||
|
{
|
||||||
|
if (!Application.isPlaying)
|
||||||
|
{
|
||||||
|
EditorGUILayout.HelpBox("Enter Play Mode to view live collection data.", MessageType.Info);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CardSystemManager.Instance == null)
|
||||||
|
{
|
||||||
|
EditorGUILayout.HelpBox("CardSystemManager instance not found!", MessageType.Warning);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Header
|
||||||
|
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
||||||
|
GUILayout.Label("Live Collection Preview", EditorStyles.boldLabel);
|
||||||
|
EditorGUILayout.Space();
|
||||||
|
|
||||||
|
// Summary Stats
|
||||||
|
EditorGUILayout.LabelField("Total Unique Cards:", totalUniqueCards.ToString());
|
||||||
|
EditorGUILayout.LabelField("Total Cards Owned:", totalCards.ToString());
|
||||||
|
EditorGUILayout.LabelField("Booster Packs:", boosterCount.ToString());
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(lastEventMessage))
|
||||||
|
{
|
||||||
|
EditorGUILayout.Space();
|
||||||
|
EditorGUILayout.HelpBox(lastEventMessage, MessageType.Info);
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.EndVertical();
|
||||||
|
|
||||||
|
EditorGUILayout.Space();
|
||||||
|
|
||||||
|
// Collection by Rarity
|
||||||
|
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
|
||||||
|
|
||||||
|
DrawRaritySection(CardRarity.Legendary, Color.yellow);
|
||||||
|
DrawRaritySection(CardRarity.Rare, new Color(0.5f, 0.5f, 1f)); // Light blue
|
||||||
|
DrawRaritySection(CardRarity.Normal, Color.white);
|
||||||
|
|
||||||
|
EditorGUILayout.EndScrollView();
|
||||||
|
|
||||||
|
EditorGUILayout.Space();
|
||||||
|
|
||||||
|
// Actions
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
if (GUILayout.Button("Refresh Now"))
|
||||||
|
{
|
||||||
|
RefreshData();
|
||||||
|
}
|
||||||
|
if (GUILayout.Button("Clear Event Log"))
|
||||||
|
{
|
||||||
|
lastEventMessage = "";
|
||||||
|
}
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawRaritySection(CardRarity rarity, Color color)
|
||||||
|
{
|
||||||
|
if (!cardsByRarity.ContainsKey(rarity) || cardsByRarity[rarity].Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var cards = cardsByRarity[rarity];
|
||||||
|
int totalCopies = cards.Sum(c => c.CopiesOwned);
|
||||||
|
|
||||||
|
EditorGUILayout.Space();
|
||||||
|
|
||||||
|
// Header
|
||||||
|
var oldColor = GUI.backgroundColor;
|
||||||
|
GUI.backgroundColor = color;
|
||||||
|
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
||||||
|
GUI.backgroundColor = oldColor;
|
||||||
|
|
||||||
|
GUILayout.Label($"{rarity} ({cards.Count} unique, {totalCopies} total)", EditorStyles.boldLabel);
|
||||||
|
|
||||||
|
// Cards
|
||||||
|
foreach (var card in cards.OrderBy(c => c.Name))
|
||||||
|
{
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
|
||||||
|
EditorGUILayout.LabelField(card.Name, GUILayout.Width(200));
|
||||||
|
EditorGUILayout.LabelField($"x{card.CopiesOwned}", GUILayout.Width(50));
|
||||||
|
EditorGUILayout.LabelField(card.Zone.ToString(), GUILayout.Width(100));
|
||||||
|
|
||||||
|
// Progress to next tier (if not Legendary)
|
||||||
|
if (rarity < CardRarity.Legendary)
|
||||||
|
{
|
||||||
|
int copiesNeeded = 5;
|
||||||
|
float progress = Mathf.Clamp01((float)card.CopiesOwned / copiesNeeded);
|
||||||
|
Rect progressRect = GUILayoutUtility.GetRect(100, 18);
|
||||||
|
EditorGUI.ProgressBar(progressRect, progress, $"{card.CopiesOwned}/{copiesNeeded}");
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.EndVertical();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
3
Assets/Editor/CardSystem/CardSystemLivePreview.cs.meta
Normal file
3
Assets/Editor/CardSystem/CardSystemLivePreview.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8c45a4455c1440069cd9c1ee815f32e0
|
||||||
|
timeCreated: 1762464117
|
||||||
@@ -1022,6 +1022,12 @@ MonoBehaviour:
|
|||||||
hoverScaleMultiplier: 1.05
|
hoverScaleMultiplier: 1.05
|
||||||
flipDuration: 0.6
|
flipDuration: 0.6
|
||||||
flipScalePunch: 1.3
|
flipScalePunch: 1.3
|
||||||
|
newCardText: {fileID: 3802662965106921097}
|
||||||
|
newCardIdleText: {fileID: 8335972675955266088}
|
||||||
|
repeatText: {fileID: 7979281912729275558}
|
||||||
|
progressBarContainer: {fileID: 1938654216571238436}
|
||||||
|
cardsToUpgrade: 5
|
||||||
|
enlargedScale: 1.5
|
||||||
--- !u!1001 &8620731150807558660
|
--- !u!1001 &8620731150807558660
|
||||||
PrefabInstance:
|
PrefabInstance:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
|
|||||||
@@ -11,9 +11,17 @@ namespace AppleHills.Data.CardSystem
|
|||||||
[Serializable]
|
[Serializable]
|
||||||
public class CardInventory
|
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>();
|
[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
|
// Number of unopened booster packs the player has
|
||||||
[SerializeField] private int boosterPackCount;
|
[SerializeField] private int boosterPackCount;
|
||||||
|
|
||||||
@@ -96,7 +104,9 @@ namespace AppleHills.Data.CardSystem
|
|||||||
{
|
{
|
||||||
if (card == null) return;
|
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
|
// Increase copies of existing card
|
||||||
existingCard.CopiesOwned++;
|
existingCard.CopiesOwned++;
|
||||||
@@ -105,7 +115,7 @@ namespace AppleHills.Data.CardSystem
|
|||||||
{
|
{
|
||||||
// Add new card to collection
|
// Add new card to collection
|
||||||
var newCard = new CardData(card);
|
var newCard = new CardData(card);
|
||||||
collectedCards[card.DefinitionId] = newCard;
|
collectedCards[key] = newCard;
|
||||||
|
|
||||||
// Add to lookup dictionaries
|
// Add to lookup dictionaries
|
||||||
cardsByZone[newCard.Zone].Add(newCard);
|
cardsByZone[newCard.Zone].Add(newCard);
|
||||||
@@ -133,19 +143,21 @@ namespace AppleHills.Data.CardSystem
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get a specific card from the collection by definition ID
|
/// Get a specific card from the collection by definition ID and rarity
|
||||||
/// </summary>
|
/// </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>
|
/// <summary>
|
||||||
/// Check if the player has a specific card
|
/// Check if the player has a specific card at a specific rarity
|
||||||
/// </summary>
|
/// </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>
|
/// <summary>
|
||||||
|
|||||||
@@ -1,14 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using AppleHills.Data.CardSystem;
|
using AppleHills.Data.CardSystem;
|
||||||
using Bootstrap;
|
using Bootstrap;
|
||||||
using Core;
|
using Core;
|
||||||
using Core.SaveLoad;
|
using Core.SaveLoad;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
#if UNITY_EDITOR
|
|
||||||
using UnityEditor;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace Data.CardSystem
|
namespace Data.CardSystem
|
||||||
{
|
{
|
||||||
@@ -150,6 +146,7 @@ namespace Data.CardSystem
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Opens a booster pack and returns the newly obtained cards
|
/// 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>
|
/// </summary>
|
||||||
public List<CardData> OpenBoosterPack()
|
public List<CardData> OpenBoosterPack()
|
||||||
{
|
{
|
||||||
@@ -165,11 +162,9 @@ namespace Data.CardSystem
|
|||||||
// Draw 3 cards based on rarity distribution
|
// Draw 3 cards based on rarity distribution
|
||||||
List<CardData> drawnCards = DrawRandomCards(3);
|
List<CardData> drawnCards = DrawRandomCards(3);
|
||||||
|
|
||||||
// Add cards to the inventory
|
// NOTE: Cards are NOT added to inventory here anymore
|
||||||
foreach (var card in drawnCards)
|
// They will be added after the player interacts with each revealed card
|
||||||
{
|
// This allows us to show new/repeat status before adding to collection
|
||||||
AddCardToInventory(card);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Notify listeners
|
// Notify listeners
|
||||||
OnBoosterOpened?.Invoke(drawnCards);
|
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}");
|
Logging.Debug($"[CardSystemManager] Opened a booster pack and obtained {drawnCards.Count} cards. Remaining boosters: {playerInventory.BoosterPackCount}");
|
||||||
return drawnCards;
|
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>
|
/// <summary>
|
||||||
/// Adds a card to the player's inventory, handles duplicates
|
/// Adds a card to the player's inventory, handles duplicates
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void AddCardToInventory(CardData card)
|
private void AddCardToInventory(CardData card)
|
||||||
{
|
{
|
||||||
// Check if the player already has this card type (definition)
|
// Check if the player already has this card at this rarity
|
||||||
if (playerInventory.HasCard(card.DefinitionId))
|
if (playerInventory.HasCard(card.DefinitionId, card.Rarity))
|
||||||
{
|
{
|
||||||
CardData existingCard = playerInventory.GetCard(card.DefinitionId);
|
CardData existingCard = playerInventory.GetCard(card.DefinitionId, card.Rarity);
|
||||||
existingCard.CopiesOwned++;
|
existingCard.CopiesOwned++;
|
||||||
|
|
||||||
// Check if the card can be upgraded
|
// Note: Upgrades are now handled separately in BoosterOpeningPage
|
||||||
if (existingCard.TryUpgradeRarity())
|
// We don't auto-upgrade here anymore
|
||||||
{
|
|
||||||
OnCardRarityUpgraded?.Invoke(existingCard);
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
else
|
||||||
{
|
{
|
||||||
// Add new card
|
// Add new card at this rarity
|
||||||
playerInventory.AddCard(card);
|
playerInventory.AddCard(card);
|
||||||
OnCardCollected?.Invoke(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>
|
/// <summary>
|
||||||
/// Returns whether a specific card definition has been collected
|
/// Returns whether a specific card definition has been collected (at any rarity)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsCardCollected(string definitionId)
|
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>
|
/// <summary>
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ using UI.DragAndDrop.Core;
|
|||||||
using Unity.Cinemachine;
|
using Unity.Cinemachine;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.UI;
|
using UnityEngine.UI;
|
||||||
using UnityEngine.UI;
|
|
||||||
|
|
||||||
namespace UI.CardSystem
|
namespace UI.CardSystem
|
||||||
{
|
{
|
||||||
@@ -39,6 +38,7 @@ namespace UI.CardSystem
|
|||||||
[SerializeField] private float boosterDisappearDuration = 0.5f;
|
[SerializeField] private float boosterDisappearDuration = 0.5f;
|
||||||
[SerializeField] private CinemachineImpulseSource impulseSource;
|
[SerializeField] private CinemachineImpulseSource impulseSource;
|
||||||
[SerializeField] private ParticleSystem openingParticleSystem;
|
[SerializeField] private ParticleSystem openingParticleSystem;
|
||||||
|
[SerializeField] private Transform albumIcon; // Target for card fly-away animation
|
||||||
|
|
||||||
private int _availableBoosterCount;
|
private int _availableBoosterCount;
|
||||||
private BoosterPackDraggable _currentBoosterInCenter;
|
private BoosterPackDraggable _currentBoosterInCenter;
|
||||||
@@ -46,9 +46,10 @@ namespace UI.CardSystem
|
|||||||
private List<GameObject> _currentRevealedCards = new List<GameObject>();
|
private List<GameObject> _currentRevealedCards = new List<GameObject>();
|
||||||
private CardData[] _currentCardData;
|
private CardData[] _currentCardData;
|
||||||
private int _revealedCardCount;
|
private int _revealedCardCount;
|
||||||
|
private int _cardsCompletedInteraction; // Track how many cards finished their new/repeat interaction
|
||||||
private bool _isProcessingOpening;
|
private bool _isProcessingOpening;
|
||||||
private const int MAX_VISIBLE_BOOSTERS = 3;
|
private const int MAX_VISIBLE_BOOSTERS = 3;
|
||||||
|
private FlippableCard _currentActiveCard; // The card currently awaiting interaction
|
||||||
private void Awake()
|
private void Awake()
|
||||||
{
|
{
|
||||||
// Make sure we have a CanvasGroup for transitions
|
// Make sure we have a CanvasGroup for transitions
|
||||||
@@ -499,6 +500,7 @@ namespace UI.CardSystem
|
|||||||
|
|
||||||
_currentRevealedCards.Clear();
|
_currentRevealedCards.Clear();
|
||||||
_revealedCardCount = 0;
|
_revealedCardCount = 0;
|
||||||
|
_cardsCompletedInteraction = 0; // Reset interaction count
|
||||||
|
|
||||||
// Calculate positions
|
// Calculate positions
|
||||||
float totalWidth = (count - 1) * cardSpacing;
|
float totalWidth = (count - 1) * cardSpacing;
|
||||||
@@ -521,9 +523,18 @@ namespace UI.CardSystem
|
|||||||
// Setup the card data (stored but not revealed yet)
|
// Setup the card data (stored but not revealed yet)
|
||||||
flippableCard.SetupCard(_currentCardData[i]);
|
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
|
int cardIndex = i; // Capture for closure
|
||||||
|
flippableCard.OnFlipStarted += OnCardFlipStarted;
|
||||||
|
|
||||||
|
// Subscribe to reveal event to track when flipped
|
||||||
flippableCard.OnCardRevealed += (card, data) => OnCardRevealed(cardIndex);
|
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
|
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>
|
/// <summary>
|
||||||
/// Handle card reveal (when flipped)
|
/// Handle card reveal (when flipped)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -545,29 +574,182 @@ namespace UI.CardSystem
|
|||||||
{
|
{
|
||||||
Debug.Log($"[BoosterOpeningPage] Card {cardIndex} revealed!");
|
Debug.Log($"[BoosterOpeningPage] Card {cardIndex} revealed!");
|
||||||
_revealedCardCount++;
|
_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>
|
/// <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>
|
/// </summary>
|
||||||
private IEnumerator WaitForCardReveals()
|
private IEnumerator WaitForCardReveals()
|
||||||
{
|
{
|
||||||
|
// Wait until all cards are flipped
|
||||||
while (_revealedCardCount < _currentCardData.Length)
|
while (_revealedCardCount < _currentCardData.Length)
|
||||||
{
|
{
|
||||||
yield return null;
|
yield return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// All cards revealed, wait a moment
|
Debug.Log($"[BoosterOpeningPage] All cards revealed! Waiting for interactions...");
|
||||||
yield return new WaitForSeconds(1f);
|
|
||||||
|
|
||||||
// Clear cards
|
// Wait until all cards have completed their new/repeat interaction
|
||||||
foreach (GameObject card in _currentRevealedCards)
|
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
|
// Stagger each card with 0.5s delay
|
||||||
Tween.LocalScale(card.transform, Vector3.zero, 0.3f, 0f, Tween.EaseInBack,
|
float delay = cardIndex * 0.5f;
|
||||||
completeCallback: () => Destroy(card));
|
|
||||||
|
// 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 flipDuration = 0.6f;
|
||||||
[SerializeField] private float flipScalePunch = 1.1f;
|
[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
|
// State
|
||||||
private bool _isFlipped = false;
|
private bool _isFlipped = false;
|
||||||
private bool _isFlipping = false;
|
private bool _isFlipping = false;
|
||||||
@@ -36,12 +44,20 @@ namespace UI.CardSystem
|
|||||||
private TweenBase _idleHoverTween;
|
private TweenBase _idleHoverTween;
|
||||||
private CardData _cardData;
|
private CardData _cardData;
|
||||||
private Vector2 _originalPosition; // Track original spawn position
|
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
|
// Events
|
||||||
public event Action<FlippableCard, CardData> OnCardRevealed;
|
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 bool IsFlipped => _isFlipped;
|
||||||
public CardData CardData => _cardData;
|
public CardData CardData => _cardData;
|
||||||
|
public int CardsToUpgrade => cardsToUpgrade; // Expose upgrade threshold
|
||||||
|
|
||||||
private void Awake()
|
private void Awake()
|
||||||
{
|
{
|
||||||
@@ -51,11 +67,29 @@ namespace UI.CardSystem
|
|||||||
cardDisplay = cardFrontObject.GetComponent<CardDisplay>();
|
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)
|
if (cardBackObject != null)
|
||||||
|
{
|
||||||
|
cardBackObject.transform.localRotation = Quaternion.Euler(0, 0, 0);
|
||||||
cardBackObject.SetActive(true);
|
cardBackObject.SetActive(true);
|
||||||
|
}
|
||||||
|
|
||||||
if (cardFrontObject != null)
|
if (cardFrontObject != null)
|
||||||
|
{
|
||||||
|
cardFrontObject.transform.localRotation = Quaternion.Euler(0, 180, 0);
|
||||||
cardFrontObject.SetActive(false);
|
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()
|
private void Start()
|
||||||
@@ -98,40 +132,53 @@ namespace UI.CardSystem
|
|||||||
|
|
||||||
_isFlipping = true;
|
_isFlipping = true;
|
||||||
|
|
||||||
|
// Fire flip started event IMMEDIATELY (before animations)
|
||||||
|
OnFlipStarted?.Invoke(this);
|
||||||
|
|
||||||
// Stop idle hover
|
// Stop idle hover
|
||||||
StopIdleHover();
|
StopIdleHover();
|
||||||
|
|
||||||
// Flip animation: rotate Y 0 -> 90 (hide back) -> 180 (show front)
|
// Flip animation: Rotate the visual children (back from 0→90, front from 180→0)
|
||||||
Transform cardTransform = transform;
|
// ...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)
|
// Phase 1: Rotate both to 90 degrees (edge view)
|
||||||
Tween.LocalRotation(cardTransform, Quaternion.Euler(0, 90, 0), flipDuration * 0.5f, 0f, Tween.EaseInOut,
|
if (cardBackObject != null)
|
||||||
completeCallback: () =>
|
{
|
||||||
{
|
Tween.LocalRotation(cardBackObject.transform, Quaternion.Euler(0, 90, 0), flipDuration * 0.5f, 0f, Tween.EaseInOut);
|
||||||
// Switch visuals at the edge
|
}
|
||||||
if (cardBackObject != null)
|
|
||||||
cardBackObject.SetActive(false);
|
if (cardFrontObject != null)
|
||||||
if (cardFrontObject != null)
|
{
|
||||||
cardFrontObject.SetActive(true);
|
Tween.LocalRotation(cardFrontObject.transform, Quaternion.Euler(0, 90, 0), flipDuration * 0.5f, 0f, Tween.EaseInOut,
|
||||||
|
completeCallback: () =>
|
||||||
// Phase 2: Flip from 90 to 180 (show front)
|
{
|
||||||
Tween.LocalRotation(cardTransform, Quaternion.Euler(0, 180, 0), flipDuration * 0.5f, 0f, Tween.EaseInOut,
|
// At edge (90°), switch visibility
|
||||||
completeCallback: () =>
|
if (cardBackObject != null)
|
||||||
{
|
cardBackObject.SetActive(false);
|
||||||
_isFlipped = true;
|
if (cardFrontObject != null)
|
||||||
_isFlipping = false;
|
cardFrontObject.SetActive(true);
|
||||||
|
|
||||||
// Fire revealed event
|
// Phase 2: Rotate front from 90 to 0 (show at correct orientation)
|
||||||
OnCardRevealed?.Invoke(this, _cardData);
|
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
|
// Scale punch during flip for extra juice
|
||||||
Vector3 originalScale = cardTransform.localScale;
|
Vector3 originalScale = transform.localScale;
|
||||||
Tween.LocalScale(cardTransform, originalScale * flipScalePunch, flipDuration * 0.5f, 0f, Tween.EaseOutBack,
|
Tween.LocalScale(transform, originalScale * flipScalePunch, flipDuration * 0.5f, 0f, Tween.EaseOutBack,
|
||||||
completeCallback: () =>
|
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)
|
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)
|
if (_isFlipped || _isFlipping)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -216,6 +278,352 @@ namespace UI.CardSystem
|
|||||||
|
|
||||||
#endregion
|
#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()
|
private void OnDestroy()
|
||||||
{
|
{
|
||||||
StopIdleHover();
|
StopIdleHover();
|
||||||
|
|||||||
Reference in New Issue
Block a user