From 18be5974244605a63fcc5e34a73871979cf180a1 Mon Sep 17 00:00:00 2001 From: Michal Pikulski Date: Tue, 14 Oct 2025 14:57:50 +0200 Subject: [PATCH] Add card inventory classes and helpers --- Assets/Scripts/Core/Logging.cs | 23 ++ Assets/Scripts/Core/Logging.cs.meta | 3 + Assets/Scripts/Core/QuickAccess.cs | 1 + .../Scripts/Data/CardSystem/CardInventory.cs | 203 ++++++++++++++++++ .../Data/CardSystem/CardInventory.cs.meta | 3 + .../Data/CardSystem/CardSystemManager.cs | 66 ++++-- Assets/Scripts/UI/CardSystem/AlbumViewPage.cs | 1 + .../UI/CardSystem/BoosterOpeningPage.cs | 1 + Assets/Scripts/UI/CardSystem/CardAlbumUI.cs | 1 + Assets/Scripts/UI/CardSystem/CardMenuPage.cs | 1 + docs/card_system_implementation_plan.md | 132 ++++++++++++ 11 files changed, 415 insertions(+), 20 deletions(-) create mode 100644 Assets/Scripts/Core/Logging.cs create mode 100644 Assets/Scripts/Core/Logging.cs.meta create mode 100644 Assets/Scripts/Data/CardSystem/CardInventory.cs create mode 100644 Assets/Scripts/Data/CardSystem/CardInventory.cs.meta create mode 100644 docs/card_system_implementation_plan.md diff --git a/Assets/Scripts/Core/Logging.cs b/Assets/Scripts/Core/Logging.cs new file mode 100644 index 00000000..9f636cd6 --- /dev/null +++ b/Assets/Scripts/Core/Logging.cs @@ -0,0 +1,23 @@ +namespace Core +{ + public static class Logging + { + [System.Diagnostics.Conditional("ENABLE_LOG")] + public static void Debug(object message) + { + UnityEngine.Debug.Log(message); + } + + [System.Diagnostics.Conditional("ENABLE_LOG")] + public static void Warning(object message) + { + UnityEngine.Debug.LogWarning(message); + } + + [System.Diagnostics.Conditional("ENABLE_LOG")] + public static void Error(object message) + { + UnityEngine.Debug.LogError(message); + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Core/Logging.cs.meta b/Assets/Scripts/Core/Logging.cs.meta new file mode 100644 index 00000000..f11c0dc4 --- /dev/null +++ b/Assets/Scripts/Core/Logging.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 99ee122ee6ee4b57b40c03be4479b124 +timeCreated: 1760446431 \ No newline at end of file diff --git a/Assets/Scripts/Core/QuickAccess.cs b/Assets/Scripts/Core/QuickAccess.cs index 17fd16f1..954860c7 100644 --- a/Assets/Scripts/Core/QuickAccess.cs +++ b/Assets/Scripts/Core/QuickAccess.cs @@ -2,6 +2,7 @@ using AppleHills.Data.CardSystem; using Cinematics; using Core; +using Data.CardSystem; using Input; using PuzzleS; diff --git a/Assets/Scripts/Data/CardSystem/CardInventory.cs b/Assets/Scripts/Data/CardSystem/CardInventory.cs new file mode 100644 index 00000000..ae1479f8 --- /dev/null +++ b/Assets/Scripts/Data/CardSystem/CardInventory.cs @@ -0,0 +1,203 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +namespace AppleHills.Data.CardSystem +{ + /// + /// Manages the player's collection of cards and booster packs + /// + [Serializable] + public class CardInventory + { + // Dictionary of collected cards indexed by definition ID + [SerializeField] private Dictionary collectedCards = new Dictionary(); + + // Number of unopened booster packs the player has + [SerializeField] private int boosterPackCount; + + // Additional lookup dictionaries (not serialized) + [NonSerialized] private Dictionary> cardsByZone = new Dictionary>(); + [NonSerialized] private Dictionary> cardsByRarity = new Dictionary>(); + + // Properties with public getters + public Dictionary 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(); + } + + foreach (CardRarity rarity in Enum.GetValues(typeof(CardRarity))) + { + cardsByRarity[rarity] = new List(); + } + } + + /// + /// Get all cards in the player's collection as a list + /// + public List GetAllCards() + { + return new List(collectedCards.Values); + } + + /// + /// Get cards filtered by zone + /// + public List GetCardsByZone(CardZone zone) + { + return new List(cardsByZone[zone]); + } + + /// + /// Get cards filtered by rarity + /// + public List GetCardsByRarity(CardRarity rarity) + { + return new List(cardsByRarity[rarity]); + } + + /// + /// Add a card to the inventory (or increase the copies if already owned) + /// + public void AddCard(CardData card) + { + if (card == null) return; + + if (collectedCards.TryGetValue(card.DefinitionId, out CardData existingCard)) + { + // Increase copies of existing card + existingCard.CopiesOwned++; + } + else + { + // Add new card to collection + var newCard = new CardData(card); + collectedCards[card.DefinitionId] = newCard; + + // Add to lookup dictionaries + cardsByZone[newCard.Zone].Add(newCard); + cardsByRarity[newCard.Rarity].Add(newCard); + } + } + + /// + /// Update card zone and rarity indexes when a card changes + /// + 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); + } + } + + /// + /// Get a specific card from the collection by definition ID + /// + public CardData GetCard(string definitionId) + { + return collectedCards.TryGetValue(definitionId, out CardData card) ? card : null; + } + + /// + /// Check if the player has a specific card + /// + public bool HasCard(string definitionId) + { + return collectedCards.ContainsKey(definitionId); + } + + /// + /// Get total number of unique cards in collection + /// + public int GetUniqueCardCount() + { + return collectedCards.Count; + } + + /// + /// Get total number of cards including copies + /// + public int GetTotalCardCount() + { + return collectedCards.Values.Sum(card => card.CopiesOwned); + } + + /// + /// Get number of cards in a specific zone + /// + public int GetZoneCardCount(CardZone zone) + { + return cardsByZone[zone].Count; + } + + /// + /// Get number of cards of a specific rarity + /// + public int GetRarityCardCount(CardRarity rarity) + { + return cardsByRarity[rarity].Count; + } + + /// + /// Get cards sorted by collection index (for album view) + /// + public List GetCardsSortedByIndex() + { + return collectedCards.Values + .OrderBy(card => card.CollectionIndex) + .ToList(); + } + + /// + /// Check if there's a complete collection for a specific zone + /// + public bool IsZoneCollectionComplete(CardZone zone, List allAvailableCards) + { + int availableInZone = allAvailableCards.Count(card => card.Zone == zone); + int collectedInZone = cardsByZone[zone].Count; + + return availableInZone > 0 && collectedInZone >= availableInZone; + } + + /// + /// Adds booster packs to the inventory + /// + public void AddBoosterPacks(int count) + { + boosterPackCount += count; + } + + /// + /// Use a single booster pack (returns true if successful) + /// + public bool UseBoosterPack() + { + if (boosterPackCount <= 0) return false; + + boosterPackCount--; + return true; + } + } +} diff --git a/Assets/Scripts/Data/CardSystem/CardInventory.cs.meta b/Assets/Scripts/Data/CardSystem/CardInventory.cs.meta new file mode 100644 index 00000000..fecde4f8 --- /dev/null +++ b/Assets/Scripts/Data/CardSystem/CardInventory.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f5b1aa91590d48a1a4c426f3cd4aa103 +timeCreated: 1760445622 \ No newline at end of file diff --git a/Assets/Scripts/Data/CardSystem/CardSystemManager.cs b/Assets/Scripts/Data/CardSystem/CardSystemManager.cs index c80ed549..183b4e0d 100644 --- a/Assets/Scripts/Data/CardSystem/CardSystemManager.cs +++ b/Assets/Scripts/Data/CardSystem/CardSystemManager.cs @@ -1,8 +1,9 @@ using System; using System.Collections.Generic; +using AppleHills.Data.CardSystem; using UnityEngine; -namespace AppleHills.Data.CardSystem +namespace Data.CardSystem { /// /// Manages the player's card collection, booster packs, and related operations. @@ -138,8 +139,9 @@ namespace AppleHills.Data.CardSystem private void AddCardToInventory(CardData card) { // Check if the player already has this card type (definition) - if (playerInventory.CollectedCards.TryGetValue(card.DefinitionId, out CardData existingCard)) + if (playerInventory.HasCard(card.DefinitionId)) { + CardData existingCard = playerInventory.GetCard(card.DefinitionId); existingCard.CopiesOwned++; // Check if the card can be upgraded @@ -153,7 +155,7 @@ namespace AppleHills.Data.CardSystem else { // Add new card - playerInventory.CollectedCards.Add(card.DefinitionId, new CardData(card)); + playerInventory.AddCard(card); OnCardCollected?.Invoke(card); Debug.Log($"[CardSystemManager] Added new card '{card.Name}' to collection."); @@ -227,12 +229,23 @@ namespace AppleHills.Data.CardSystem /// public List GetAllCollectedCards() { - List result = new List(); - foreach (var card in playerInventory.CollectedCards.Values) - { - result.Add(card); - } - return result; + 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); } /// @@ -248,17 +261,30 @@ namespace AppleHills.Data.CardSystem /// public bool IsCardCollected(string definitionId) { - return playerInventory.CollectedCards.ContainsKey(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; } } - - /// - /// Serializable class to store the player's card inventory - /// - [Serializable] - public class CardInventory - { - public Dictionary CollectedCards = new Dictionary(); - public int BoosterPackCount; - } } diff --git a/Assets/Scripts/UI/CardSystem/AlbumViewPage.cs b/Assets/Scripts/UI/CardSystem/AlbumViewPage.cs index 08bc1341..069cd840 100644 --- a/Assets/Scripts/UI/CardSystem/AlbumViewPage.cs +++ b/Assets/Scripts/UI/CardSystem/AlbumViewPage.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using AppleHills.Data.CardSystem; +using Data.CardSystem; using Pixelplacement; using UnityEngine; using UnityEngine.UI; diff --git a/Assets/Scripts/UI/CardSystem/BoosterOpeningPage.cs b/Assets/Scripts/UI/CardSystem/BoosterOpeningPage.cs index c2a009f5..56bd7668 100644 --- a/Assets/Scripts/UI/CardSystem/BoosterOpeningPage.cs +++ b/Assets/Scripts/UI/CardSystem/BoosterOpeningPage.cs @@ -1,6 +1,7 @@ using System.Collections; using System.Collections.Generic; using AppleHills.Data.CardSystem; +using Data.CardSystem; using Pixelplacement; using UnityEngine; using UnityEngine.UI; diff --git a/Assets/Scripts/UI/CardSystem/CardAlbumUI.cs b/Assets/Scripts/UI/CardSystem/CardAlbumUI.cs index 28ef9a36..79d60628 100644 --- a/Assets/Scripts/UI/CardSystem/CardAlbumUI.cs +++ b/Assets/Scripts/UI/CardSystem/CardAlbumUI.cs @@ -1,5 +1,6 @@ using System; using AppleHills.Data.CardSystem; +using Data.CardSystem; using UnityEngine; using UnityEngine.UI; diff --git a/Assets/Scripts/UI/CardSystem/CardMenuPage.cs b/Assets/Scripts/UI/CardSystem/CardMenuPage.cs index 83ce4a95..85a7683f 100644 --- a/Assets/Scripts/UI/CardSystem/CardMenuPage.cs +++ b/Assets/Scripts/UI/CardSystem/CardMenuPage.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using AppleHills.Data.CardSystem; +using Data.CardSystem; using Pixelplacement; using UnityEngine; using UnityEngine.UI; diff --git a/docs/card_system_implementation_plan.md b/docs/card_system_implementation_plan.md new file mode 100644 index 00000000..b60bbe14 --- /dev/null +++ b/docs/card_system_implementation_plan.md @@ -0,0 +1,132 @@ +# Card System Implementation Plan + +## Current Implementation Analysis + +### Data Layer + +1. **CardData.cs**: + - Represents an instance of a card in the player's collection + - Contains unique ID, rarity, and copies owned + - Has a reference to its CardDefinition for display information + - Includes methods for rarity upgrades when collecting duplicates + - Well-structured with proper serialization support + +2. **CardDefinition.cs**: + - ScriptableObject template for creating card instances + - Contains basic info (name, description), visuals, and collection data + - Provides helper methods like CreateCardData() and GetBackgroundColor() + - Supports different zones (AppleHills, Quarry, Forest, Mountain, Beach) + +3. **CardSystemManager.cs**: + - Singleton manager for the card system + - Manages available card definitions and player inventory + - Includes event callbacks for booster packs and card collection + - Has partial implementation for adding booster packs + - Has a simple CardInventory class stub + +4. **CardVisualConfig.cs**: + - ScriptableObject for configuring the visual appearance of cards + - Maps rarities to colors and zones to colors/shapes + - Uses dictionary lookups for efficient access + +### UI Layer + +1. **CardUIElement.cs**: + - Handles displaying a single card in the UI + - Updates visuals based on card data (rarity, zone) + - References UI elements like card name text, images, frames, etc. + +2. **UIPageController.cs**: + - Manages UI page transitions with a stack-based navigation system + - Provides methods for pushing, popping, and clearing the page stack + - Uses events to notify when pages change + +3. **UI Page Components**: + - Several page-related files (AlbumViewPage.cs, BoosterOpeningPage.cs, CardMenuPage.cs) + - CardAlbumUI.cs file for handling the album display + - Base UIPage.cs class for common page functionality + +## What's Missing + +Based on the requirements, here's what still needs to be implemented: + +1. **Complete CardInventory Implementation**: + - Move from simple stub to full implementation + - Add methods to manage the player's card collection + - Implement filtering, sorting, and organization features + +2. **Booster Pack Generation**: + - Complete the logic to generate random booster packs with appropriate rarity distribution + - Implement methods for awarding booster packs to the player + - Add events for notifying UI when booster packs are obtained + +3. **Card Collection UI**: + - Complete the album browsing UI with filtering and sorting options + - Implement visual indicators for owned/not owned cards + - Add UI for tracking collection completion + +4. **Booster Pack Opening UI**: + - Implement the UI flow for opening booster packs + - Add card reveal animations and effects + - Create UI feedback for rare card discoveries + +5. **Save/Load System**: + - Implement persistence for the player's card collection + - Add autosave functionality for cards and booster packs + +## Implementation Plan + +### Step 1: Complete the Data Layer + +1. **Implement CardInventory.cs**: + - Create a dedicated class file (replacing the stub in CardSystemManager) + - Add methods for adding/removing cards, checking duplicates + - Implement filtering by zone, rarity, etc. + - Add collection statistics methods + +2. **Complete CardSystemManager.cs**: + - Update references to use the new CardInventory class + - Add methods for save/load integration + - Implement additional helper methods for card management + +### Step 2: Implement UI Flow + +1. **Complete UIPage Implementations**: + - Implement CardMenuPage.cs as the main card system entry point + - Complete AlbumViewPage.cs for browsing the collection + - Implement BoosterOpeningPage.cs for the pack opening experience + +2. **Card Collection Browsing**: + - Complete CardAlbumUI.cs to display the player's collection + - Add filtering by zone, rarity, etc. + - Implement pagination for large collections + +3. **Booster Pack UI**: + - Create UI for displaying and opening booster packs + - Implement card reveal animations and transitions + - Add sound effects and visual feedback + +### Step 3: Integration and Polishing + +1. **Connect UI to Data Layer**: + - Hook up UI components to CardSystemManager events + - Ensure data flows correctly between UI and data layers + +2. **Test and Balance**: + - Test rarity distribution in booster packs + - Balance the upgrade system for duplicates + +3. **Performance Optimization**: + - Implement object pooling for card UI elements + - Optimize loading of card data and sprites + +### Step 4: Additional Features (Optional) + +1. **Card Trading System**: + - Allow players to trade cards with NPCs or other players + +2. **Special Collection Bonuses**: + - Rewards for completing sets of cards + +3. **Card Usage Mechanics**: + - Ways to use cards in gameplay beyond collection