using System;
using System.Collections.Generic;
using AppleHills.Data.CardSystem;
using Core;
using UnityEngine;
namespace Data.CardSystem
{
///
/// Manages the player's card collection, booster packs, and related operations.
/// Uses a singleton pattern for global access.
///
public class CardSystemManager : MonoBehaviour
{
private static CardSystemManager _instance;
private static bool _isQuitting = false;
public static CardSystemManager Instance
{
get
{
if (_instance == null && Application.isPlaying && !_isQuitting)
{
_instance = FindAnyObjectByType();
if (_instance == null)
{
var go = new GameObject("CardSystemManager");
_instance = go.AddComponent();
DontDestroyOnLoad(go);
}
}
return _instance;
}
}
[Header("Card Collection")]
[SerializeField] private List availableCards = new List();
// Runtime data - will be serialized for save/load
[SerializeField] private CardInventory playerInventory = new CardInventory();
// Dictionary to quickly look up card definitions by ID
private Dictionary _definitionLookup = new Dictionary();
// Event callbacks using System.Action
public event Action> OnBoosterOpened;
public event Action OnCardCollected;
public event Action OnCardRarityUpgraded;
public event Action OnBoosterCountChanged;
private void Awake()
{
if (_instance != null && _instance != this)
{
Destroy(gameObject);
return;
}
_instance = this;
DontDestroyOnLoad(gameObject);
// Build lookup dictionary
BuildDefinitionLookup();
}
private void OnApplicationQuit()
{
_isQuitting = true;
}
///
/// Builds a lookup dictionary for quick access to card definitions by ID
///
private void BuildDefinitionLookup()
{
_definitionLookup.Clear();
foreach (var cardDef in availableCards)
{
if (cardDef != null && !string.IsNullOrEmpty(cardDef.Id))
{
_definitionLookup[cardDef.Id] = cardDef;
}
}
// Link existing card data to their definitions
foreach (var cardData in playerInventory.CollectedCards.Values)
{
if (!string.IsNullOrEmpty(cardData.DefinitionId) &&
_definitionLookup.TryGetValue(cardData.DefinitionId, out CardDefinition def))
{
cardData.SetDefinition(def);
}
}
}
///
/// Adds a booster pack to the player's inventory
///
public void AddBoosterPack(int count = 1)
{
playerInventory.BoosterPackCount += count;
OnBoosterCountChanged?.Invoke(playerInventory.BoosterPackCount);
Logging.Debug($"[CardSystemManager] Added {count} booster pack(s). Total: {playerInventory.BoosterPackCount}");
}
///
/// Opens a booster pack and returns the newly obtained cards
///
public List OpenBoosterPack()
{
if (playerInventory.BoosterPackCount <= 0)
{
Logging.Warning("[CardSystemManager] Attempted to open a booster pack, but none are available.");
return new List();
}
playerInventory.BoosterPackCount--;
OnBoosterCountChanged?.Invoke(playerInventory.BoosterPackCount);
// Draw 3 cards based on rarity distribution
List drawnCards = DrawRandomCards(3);
// Add cards to the inventory
foreach (var card in drawnCards)
{
AddCardToInventory(card);
}
// Notify listeners
OnBoosterOpened?.Invoke(drawnCards);
Logging.Debug($"[CardSystemManager] Opened a booster pack and obtained {drawnCards.Count} cards. Remaining boosters: {playerInventory.BoosterPackCount}");
return drawnCards;
}
///
/// Adds a card to the player's inventory, handles duplicates
///
private void AddCardToInventory(CardData card)
{
// Check if the player already has this card type (definition)
if (playerInventory.HasCard(card.DefinitionId))
{
CardData existingCard = playerInventory.GetCard(card.DefinitionId);
existingCard.CopiesOwned++;
// Check if the card can be upgraded
if (existingCard.TryUpgradeRarity())
{
OnCardRarityUpgraded?.Invoke(existingCard);
}
Logging.Debug($"[CardSystemManager] Added duplicate card '{card.Name}'. Now have {existingCard.CopiesOwned} copies.");
}
else
{
// Add new card
playerInventory.AddCard(card);
OnCardCollected?.Invoke(card);
Logging.Debug($"[CardSystemManager] Added new card '{card.Name}' to collection.");
}
}
///
/// Draws random cards based on rarity distribution
///
private List DrawRandomCards(int count)
{
List result = new List();
if (availableCards.Count == 0)
{
Debug.LogError("[CardSystemManager] No available cards defined!");
return result;
}
// Simple weighted random selection based on rarity
for (int i = 0; i < count; i++)
{
// Determine card rarity first
CardRarity rarity = DetermineRandomRarity();
// Filter cards by the selected rarity
List cardsOfRarity = availableCards.FindAll(c => c.Rarity == rarity);
if (cardsOfRarity.Count > 0)
{
// Select a random card of this rarity
int randomIndex = UnityEngine.Random.Range(0, cardsOfRarity.Count);
CardDefinition selectedDef = cardsOfRarity[randomIndex];
// Create card data from definition
CardData newCard = selectedDef.CreateCardData();
result.Add(newCard);
}
else
{
// Fallback if no cards of the selected rarity
Logging.Warning($"[CardSystemManager] No cards of rarity {rarity} available, selecting a random card instead.");
int randomIndex = UnityEngine.Random.Range(0, availableCards.Count);
CardDefinition randomDef = availableCards[randomIndex];
CardData newCard = randomDef.CreateCardData();
result.Add(newCard);
}
}
return result;
}
///
/// Determines a random card rarity with appropriate weighting
///
private CardRarity DetermineRandomRarity()
{
// Simple weighted random - can be adjusted for better distribution
float rand = UnityEngine.Random.value;
if (rand < 0.6f) return CardRarity.Common;
if (rand < 0.85f) return CardRarity.Uncommon;
if (rand < 0.95f) return CardRarity.Rare;
if (rand < 0.99f) return CardRarity.Epic;
return CardRarity.Legendary;
}
///
/// Returns all cards from the player's collection
///
public List GetAllCollectedCards()
{
return playerInventory.GetAllCards();
}
///
/// Returns cards from a specific zone
///
public List GetCardsByZone(CardZone zone)
{
return playerInventory.GetCardsByZone(zone);
}
///
/// Returns cards of a specific rarity
///
public List GetCardsByRarity(CardRarity rarity)
{
return playerInventory.GetCardsByRarity(rarity);
}
///
/// Returns the number of booster packs the player has
///
public int GetBoosterPackCount()
{
return playerInventory.BoosterPackCount;
}
///
/// Returns whether a specific card definition has been collected
///
public bool IsCardCollected(string definitionId)
{
return playerInventory.HasCard(definitionId);
}
///
/// Gets total unique card count
///
public int GetUniqueCardCount()
{
return playerInventory.GetUniqueCardCount();
}
///
/// Gets completion percentage for a specific zone (0-100)
///
public float GetZoneCompletionPercentage(CardZone zone)
{
// Count available cards in this zone
int totalInZone = availableCards.FindAll(c => c.Zone == zone).Count;
if (totalInZone == 0) return 0;
// Count collected cards in this zone
int collectedInZone = playerInventory.GetCardsByZone(zone).Count;
return (float)collectedInZone / totalInZone * 100f;
}
}
}