- **Refactored Card Placement Flow** - Separated card presentation from orchestration logic - Extracted `CornerCardManager` for pending card lifecycle (spawn, shuffle, rebuild) - Extracted `AlbumNavigationService` for book page navigation and zone mapping - Extracted `CardEnlargeController` for backdrop management and card reparenting - Implemented controller pattern (non-MonoBehaviour) for complex logic - Cards now unparent from slots before rebuild to prevent premature destruction - **Improved Corner Card Display** - Fixed cards spawning on top of each other during rebuild - Implemented shuffle-to-front logic (remaining cards occupy slots 0→1→2) - Added smart card selection (prioritizes cards matching current album page) - Pending cards now removed from queue immediately on drag start - Corner cards rebuild after each placement with proper slot reassignment - **Enhanced Album Card Viewing** - Added dramatic scale increase when viewing cards from album slots - Implemented shrink animation when dismissing enlarged cards - Cards transition: `PlacedInSlotState` → `AlbumEnlargedState` → `PlacedInSlotState` - Backdrop shows/hides with card enlarge/shrink cycle - Cards reparent to enlarged container while viewing, return to slot after - **State Machine Improvements** - Added `CardStateNames` constants for type-safe state transitions - Implemented `ICardClickHandler` and `ICardStateDragHandler` interfaces - State transitions now use cached property indices - `BoosterCardContext` separated from `CardContext` for single responsibility - **Code Cleanup** - Identified unused `SlotContainerHelper.cs` (superseded by `CornerCardManager`) - Identified unused `BoosterPackDraggable.canOpenOnDrop` field - Identified unused `AlbumViewPage._previousInputMode` (hardcoded value) - Identified unused `Card.OnPlacedInAlbumSlot` event (no subscribers) Co-authored-by: Michal Pikulski <michal.a.pikulski@gmail.com> Co-authored-by: Michal Pikulski <michal@foolhardyhorizons.com> Reviewed-on: #59
236 lines
7.6 KiB
C#
236 lines
7.6 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using UnityEngine;
|
|
|
|
namespace AppleHills.Data.CardSystem
|
|
{
|
|
/// <summary>
|
|
/// Manages the player's collection of cards and booster packs
|
|
/// </summary>
|
|
[Serializable]
|
|
public class CardInventory
|
|
{
|
|
// 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;
|
|
|
|
// Additional lookup dictionaries (not serialized)
|
|
[NonSerialized] private Dictionary<CardZone, List<CardData>> cardsByZone = new Dictionary<CardZone, List<CardData>>();
|
|
[NonSerialized] private Dictionary<CardRarity, List<CardData>> cardsByRarity = new Dictionary<CardRarity, List<CardData>>();
|
|
|
|
// Properties with public getters
|
|
public Dictionary<string, CardData> CollectedCards => collectedCards;
|
|
|
|
public int BoosterPackCount
|
|
{
|
|
get => boosterPackCount;
|
|
set => boosterPackCount = value;
|
|
}
|
|
|
|
// Constructor initializes empty dictionaries
|
|
public CardInventory()
|
|
{
|
|
// Initialize dictionaries for all enum values so we never have to check for null
|
|
foreach (CardZone zone in Enum.GetValues(typeof(CardZone)))
|
|
{
|
|
cardsByZone[zone] = new List<CardData>();
|
|
}
|
|
|
|
foreach (CardRarity rarity in Enum.GetValues(typeof(CardRarity)))
|
|
{
|
|
cardsByRarity[rarity] = new List<CardData>();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get all cards in the player's collection as a list
|
|
/// </summary>
|
|
public List<CardData> GetAllCards()
|
|
{
|
|
return collectedCards.Values.ToList();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clears all cards from the inventory
|
|
/// Primarily used for testing
|
|
/// </summary>
|
|
public void ClearAllCards()
|
|
{
|
|
collectedCards.Clear();
|
|
|
|
// Clear lookup dictionaries
|
|
foreach (var zone in cardsByZone.Keys)
|
|
{
|
|
cardsByZone[zone].Clear();
|
|
}
|
|
|
|
foreach (var rarity in cardsByRarity.Keys)
|
|
{
|
|
cardsByRarity[rarity].Clear();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get cards filtered by zone
|
|
/// </summary>
|
|
public List<CardData> GetCardsByZone(CardZone zone)
|
|
{
|
|
return new List<CardData>(cardsByZone[zone]);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get cards filtered by rarity
|
|
/// </summary>
|
|
public List<CardData> GetCardsByRarity(CardRarity rarity)
|
|
{
|
|
return new List<CardData>(cardsByRarity[rarity]);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add a card to the inventory (or increase the copies if already owned)
|
|
/// </summary>
|
|
public void AddCard(CardData card)
|
|
{
|
|
if (card == null) return;
|
|
|
|
string key = GetCardKey(card.DefinitionId, card.Rarity);
|
|
|
|
if (collectedCards.TryGetValue(key, out CardData existingCard))
|
|
{
|
|
// Increase copies of existing card
|
|
existingCard.CopiesOwned++;
|
|
}
|
|
else
|
|
{
|
|
// Add new card to collection
|
|
var newCard = new CardData(card);
|
|
collectedCards[key] = newCard;
|
|
|
|
// Add to lookup dictionaries
|
|
cardsByZone[newCard.Zone].Add(newCard);
|
|
cardsByRarity[newCard.Rarity].Add(newCard);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Update card zone and rarity indexes when a card changes
|
|
/// </summary>
|
|
public void UpdateCardProperties(CardData card, CardZone oldZone, CardRarity oldRarity)
|
|
{
|
|
// Only needed if the card's zone or rarity actually changed
|
|
if (oldZone != card.Zone)
|
|
{
|
|
cardsByZone[oldZone].Remove(card);
|
|
cardsByZone[card.Zone].Add(card);
|
|
}
|
|
|
|
if (oldRarity != card.Rarity)
|
|
{
|
|
cardsByRarity[oldRarity].Remove(card);
|
|
cardsByRarity[card.Rarity].Add(card);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get a specific card from the collection by definition ID and rarity
|
|
/// </summary>
|
|
public CardData GetCard(string definitionId, CardRarity rarity)
|
|
{
|
|
string key = GetCardKey(definitionId, rarity);
|
|
return collectedCards.TryGetValue(key, out CardData card) ? card : null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check if the player has a specific card at a specific rarity
|
|
/// </summary>
|
|
public bool HasCard(string definitionId, CardRarity rarity)
|
|
{
|
|
string key = GetCardKey(definitionId, rarity);
|
|
return collectedCards.ContainsKey(key);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get total number of unique cards in collection
|
|
/// </summary>
|
|
public int GetUniqueCardCount()
|
|
{
|
|
return collectedCards.Count;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get total number of cards including copies
|
|
/// </summary>
|
|
public int GetTotalCardCount()
|
|
{
|
|
return collectedCards.Values.Sum(card => card.CopiesOwned);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get number of cards in a specific zone
|
|
/// </summary>
|
|
public int GetZoneCardCount(CardZone zone)
|
|
{
|
|
return cardsByZone[zone].Count;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get number of cards of a specific rarity
|
|
/// </summary>
|
|
public int GetRarityCardCount(CardRarity rarity)
|
|
{
|
|
return cardsByRarity[rarity].Count;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get cards sorted by collection index (for album view)
|
|
/// </summary>
|
|
public List<CardData> GetCardsSortedByIndex()
|
|
{
|
|
return collectedCards.Values
|
|
.OrderBy(card => card.CollectionIndex)
|
|
.ToList();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check if there's a complete collection for a specific zone
|
|
/// </summary>
|
|
public bool IsZoneCollectionComplete(CardZone zone, List<CardDefinition> allAvailableCards)
|
|
{
|
|
int availableInZone = allAvailableCards.Count(card => card.Zone == zone);
|
|
int collectedInZone = cardsByZone[zone].Count;
|
|
|
|
return availableInZone > 0 && collectedInZone >= availableInZone;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds booster packs to the inventory
|
|
/// </summary>
|
|
public void AddBoosterPacks(int count)
|
|
{
|
|
boosterPackCount += count;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Use a single booster pack (returns true if successful)
|
|
/// </summary>
|
|
public bool UseBoosterPack()
|
|
{
|
|
if (boosterPackCount <= 0) return false;
|
|
|
|
boosterPackCount--;
|
|
return true;
|
|
}
|
|
}
|
|
}
|