Add card inventory classes and helpers

This commit is contained in:
Michal Pikulski
2025-10-14 14:57:50 +02:00
parent e8bf6fbded
commit 18be597424
11 changed files with 415 additions and 20 deletions

View File

@@ -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);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 99ee122ee6ee4b57b40c03be4479b124
timeCreated: 1760446431

View File

@@ -2,6 +2,7 @@
using AppleHills.Data.CardSystem; using AppleHills.Data.CardSystem;
using Cinematics; using Cinematics;
using Core; using Core;
using Data.CardSystem;
using Input; using Input;
using PuzzleS; using PuzzleS;

View File

@@ -0,0 +1,203 @@
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
[SerializeField] private Dictionary<string, CardData> collectedCards = new Dictionary<string, CardData>();
// 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 new List<CardData>(collectedCards.Values);
}
/// <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;
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);
}
}
/// <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
/// </summary>
public CardData GetCard(string definitionId)
{
return collectedCards.TryGetValue(definitionId, out CardData card) ? card : null;
}
/// <summary>
/// Check if the player has a specific card
/// </summary>
public bool HasCard(string definitionId)
{
return collectedCards.ContainsKey(definitionId);
}
/// <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;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f5b1aa91590d48a1a4c426f3cd4aa103
timeCreated: 1760445622

View File

@@ -1,8 +1,9 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using AppleHills.Data.CardSystem;
using UnityEngine; using UnityEngine;
namespace AppleHills.Data.CardSystem namespace Data.CardSystem
{ {
/// <summary> /// <summary>
/// Manages the player's card collection, booster packs, and related operations. /// Manages the player's card collection, booster packs, and related operations.
@@ -138,8 +139,9 @@ namespace AppleHills.Data.CardSystem
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 type (definition)
if (playerInventory.CollectedCards.TryGetValue(card.DefinitionId, out CardData existingCard)) if (playerInventory.HasCard(card.DefinitionId))
{ {
CardData existingCard = playerInventory.GetCard(card.DefinitionId);
existingCard.CopiesOwned++; existingCard.CopiesOwned++;
// Check if the card can be upgraded // Check if the card can be upgraded
@@ -153,7 +155,7 @@ namespace AppleHills.Data.CardSystem
else else
{ {
// Add new card // Add new card
playerInventory.CollectedCards.Add(card.DefinitionId, new CardData(card)); playerInventory.AddCard(card);
OnCardCollected?.Invoke(card); OnCardCollected?.Invoke(card);
Debug.Log($"[CardSystemManager] Added new card '{card.Name}' to collection."); Debug.Log($"[CardSystemManager] Added new card '{card.Name}' to collection.");
@@ -227,12 +229,23 @@ namespace AppleHills.Data.CardSystem
/// </summary> /// </summary>
public List<CardData> GetAllCollectedCards() public List<CardData> GetAllCollectedCards()
{ {
List<CardData> result = new List<CardData>(); return playerInventory.GetAllCards();
foreach (var card in playerInventory.CollectedCards.Values)
{
result.Add(card);
} }
return result;
/// <summary>
/// Returns cards from a specific zone
/// </summary>
public List<CardData> GetCardsByZone(CardZone zone)
{
return playerInventory.GetCardsByZone(zone);
}
/// <summary>
/// Returns cards of a specific rarity
/// </summary>
public List<CardData> GetCardsByRarity(CardRarity rarity)
{
return playerInventory.GetCardsByRarity(rarity);
} }
/// <summary> /// <summary>
@@ -248,17 +261,30 @@ namespace AppleHills.Data.CardSystem
/// </summary> /// </summary>
public bool IsCardCollected(string definitionId) public bool IsCardCollected(string definitionId)
{ {
return playerInventory.CollectedCards.ContainsKey(definitionId); return playerInventory.HasCard(definitionId);
}
} }
/// <summary> /// <summary>
/// Serializable class to store the player's card inventory /// Gets total unique card count
/// </summary> /// </summary>
[Serializable] public int GetUniqueCardCount()
public class CardInventory
{ {
public Dictionary<string, CardData> CollectedCards = new Dictionary<string, CardData>(); return playerInventory.GetUniqueCardCount();
public int BoosterPackCount; }
/// <summary>
/// Gets completion percentage for a specific zone (0-100)
/// </summary>
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;
}
} }
} }

View File

@@ -1,5 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using AppleHills.Data.CardSystem; using AppleHills.Data.CardSystem;
using Data.CardSystem;
using Pixelplacement; using Pixelplacement;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;

View File

@@ -1,6 +1,7 @@
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using AppleHills.Data.CardSystem; using AppleHills.Data.CardSystem;
using Data.CardSystem;
using Pixelplacement; using Pixelplacement;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;

View File

@@ -1,5 +1,6 @@
using System; using System;
using AppleHills.Data.CardSystem; using AppleHills.Data.CardSystem;
using Data.CardSystem;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;

View File

@@ -1,5 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using AppleHills.Data.CardSystem; using AppleHills.Data.CardSystem;
using Data.CardSystem;
using Pixelplacement; using Pixelplacement;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;

View File

@@ -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