2025-10-10 14:31:51 +02:00
using System ;
using System.Collections.Generic ;
2025-10-20 16:33:18 +02:00
using System.Linq ;
2025-10-14 14:57:50 +02:00
using AppleHills.Data.CardSystem ;
2025-10-16 19:43:19 +02:00
using Bootstrap ;
2025-10-14 15:53:58 +02:00
using Core ;
2025-10-27 14:00:37 +01:00
using Core.SaveLoad ;
2025-10-10 14:31:51 +02:00
using UnityEngine ;
2025-10-20 16:33:18 +02:00
#if UNITY_EDITOR
using UnityEditor ;
#endif
2025-10-10 14:31:51 +02:00
2025-10-14 14:57:50 +02:00
namespace Data.CardSystem
2025-10-10 14:31:51 +02:00
{
/// <summary>
/// Manages the player's card collection, booster packs, and related operations.
/// Uses a singleton pattern for global access.
Refactoring of the interaction system and preliminary integration of save/load functionality across the game. (#44)
### Interactables Architecture Refactor
- Converted composition to inheritance, moved from component-based to class-based interactables. No more requirement for chain of "Interactable -> Item" etc.
- Created `InteractableBase` abstract base class with common functionality that replaces the old component
- Specialized child classes: `Pickup`, `ItemSlot`, `LevelSwitch`, `MinigameSwitch`, `CombinationItem`, `OneClickInteraction` are now children classes
- Light updates to the interactable inspector, moved some things arround, added collapsible inspector sections in the UI for better editor experience
### State Machine Integration
- Custom `AppleMachine` inheritong from Pixelplacement's StateMachine which implements our own interface for saving, easy place for future improvements
- Replaced all previous StateMachines by `AppleMachine`
- Custom `AppleState` extends from default `State`. Added serialization, split state logic into "EnterState", "RestoreState", "ExitState" allowing for separate logic when triggering in-game vs loading game
- Restores directly to target state without triggering transitional logic
- Migration tool converts existing instances
### Prefab Organization
- Saved changes from scenes into prefabs
- Cleaned up duplicated components, confusing prefabs hierarchies
- Created prefab variants where possible
- Consolidated Environment prefabs and moved them out of Placeholders subfolder into main Environment folder
- Organized item prefabs from PrefabsPLACEHOLDER into proper Items folder
- Updated prefab references - All scene references updated to new locations
- Removed placeholder files from Characters, Levels, UI, and Minigames folders
### Scene Updates
- Quarry scene with major updates
- Saved multiple working versions (Quarry, Quarry_Fixed, Quarry_OLD)
- Added proper lighting data
- Updated all interactable components to new architecture
### Minor editor tools
- New tool for testing cards from an editor window (no in-scene object required)
- Updated Interactable Inspector
- New debug option to opt in-and-out of the save/load system
- Tooling for easier migration
Co-authored-by: Michal Pikulski <michal.a.pikulski@gmail.com>
Reviewed-on: https://homelab.tailf7f81b.ts.net/tschesky/AppleHillsProduction/pulls/44
2025-11-03 10:12:51 +00:00
/// Implements ISaveParticipant to integrate with the save/load system.
2025-10-10 14:31:51 +02:00
/// </summary>
Refactoring of the interaction system and preliminary integration of save/load functionality across the game. (#44)
### Interactables Architecture Refactor
- Converted composition to inheritance, moved from component-based to class-based interactables. No more requirement for chain of "Interactable -> Item" etc.
- Created `InteractableBase` abstract base class with common functionality that replaces the old component
- Specialized child classes: `Pickup`, `ItemSlot`, `LevelSwitch`, `MinigameSwitch`, `CombinationItem`, `OneClickInteraction` are now children classes
- Light updates to the interactable inspector, moved some things arround, added collapsible inspector sections in the UI for better editor experience
### State Machine Integration
- Custom `AppleMachine` inheritong from Pixelplacement's StateMachine which implements our own interface for saving, easy place for future improvements
- Replaced all previous StateMachines by `AppleMachine`
- Custom `AppleState` extends from default `State`. Added serialization, split state logic into "EnterState", "RestoreState", "ExitState" allowing for separate logic when triggering in-game vs loading game
- Restores directly to target state without triggering transitional logic
- Migration tool converts existing instances
### Prefab Organization
- Saved changes from scenes into prefabs
- Cleaned up duplicated components, confusing prefabs hierarchies
- Created prefab variants where possible
- Consolidated Environment prefabs and moved them out of Placeholders subfolder into main Environment folder
- Organized item prefabs from PrefabsPLACEHOLDER into proper Items folder
- Updated prefab references - All scene references updated to new locations
- Removed placeholder files from Characters, Levels, UI, and Minigames folders
### Scene Updates
- Quarry scene with major updates
- Saved multiple working versions (Quarry, Quarry_Fixed, Quarry_OLD)
- Added proper lighting data
- Updated all interactable components to new architecture
### Minor editor tools
- New tool for testing cards from an editor window (no in-scene object required)
- Updated Interactable Inspector
- New debug option to opt in-and-out of the save/load system
- Tooling for easier migration
Co-authored-by: Michal Pikulski <michal.a.pikulski@gmail.com>
Reviewed-on: https://homelab.tailf7f81b.ts.net/tschesky/AppleHillsProduction/pulls/44
2025-11-03 10:12:51 +00:00
public class CardSystemManager : MonoBehaviour , ISaveParticipant
2025-10-10 14:31:51 +02:00
{
private static CardSystemManager _instance ;
2025-10-20 12:04:55 +02:00
public static CardSystemManager Instance = > _instance ;
2025-10-10 14:31:51 +02:00
[Header("Card Collection")]
[SerializeField] private List < CardDefinition > availableCards = new List < CardDefinition > ( ) ;
2025-10-21 10:05:49 +02:00
2025-10-10 14:31:51 +02:00
// Runtime data - will be serialized for save/load
[SerializeField] private CardInventory playerInventory = new CardInventory ( ) ;
2025-10-21 10:05:49 +02:00
2025-10-10 14:31:51 +02:00
// Dictionary to quickly look up card definitions by ID
private Dictionary < string , CardDefinition > _definitionLookup = new Dictionary < string , CardDefinition > ( ) ;
2025-10-21 10:05:49 +02:00
2025-10-10 14:31:51 +02:00
// Event callbacks using System.Action
public event Action < List < CardData > > OnBoosterOpened ;
public event Action < CardData > OnCardCollected ;
public event Action < CardData > OnCardRarityUpgraded ;
public event Action < int > OnBoosterCountChanged ;
private void Awake ( )
{
_instance = this ;
2025-10-21 10:05:49 +02:00
2025-10-16 19:43:19 +02:00
// Register for post-boot initialization
BootCompletionService . RegisterInitAction ( InitializePostBoot ) ;
}
2025-10-21 10:05:49 +02:00
2025-10-16 19:43:19 +02:00
private void InitializePostBoot ( )
{
Refactoring of the interaction system and preliminary integration of save/load functionality across the game. (#44)
### Interactables Architecture Refactor
- Converted composition to inheritance, moved from component-based to class-based interactables. No more requirement for chain of "Interactable -> Item" etc.
- Created `InteractableBase` abstract base class with common functionality that replaces the old component
- Specialized child classes: `Pickup`, `ItemSlot`, `LevelSwitch`, `MinigameSwitch`, `CombinationItem`, `OneClickInteraction` are now children classes
- Light updates to the interactable inspector, moved some things arround, added collapsible inspector sections in the UI for better editor experience
### State Machine Integration
- Custom `AppleMachine` inheritong from Pixelplacement's StateMachine which implements our own interface for saving, easy place for future improvements
- Replaced all previous StateMachines by `AppleMachine`
- Custom `AppleState` extends from default `State`. Added serialization, split state logic into "EnterState", "RestoreState", "ExitState" allowing for separate logic when triggering in-game vs loading game
- Restores directly to target state without triggering transitional logic
- Migration tool converts existing instances
### Prefab Organization
- Saved changes from scenes into prefabs
- Cleaned up duplicated components, confusing prefabs hierarchies
- Created prefab variants where possible
- Consolidated Environment prefabs and moved them out of Placeholders subfolder into main Environment folder
- Organized item prefabs from PrefabsPLACEHOLDER into proper Items folder
- Updated prefab references - All scene references updated to new locations
- Removed placeholder files from Characters, Levels, UI, and Minigames folders
### Scene Updates
- Quarry scene with major updates
- Saved multiple working versions (Quarry, Quarry_Fixed, Quarry_OLD)
- Added proper lighting data
- Updated all interactable components to new architecture
### Minor editor tools
- New tool for testing cards from an editor window (no in-scene object required)
- Updated Interactable Inspector
- New debug option to opt in-and-out of the save/load system
- Tooling for easier migration
Co-authored-by: Michal Pikulski <michal.a.pikulski@gmail.com>
Reviewed-on: https://homelab.tailf7f81b.ts.net/tschesky/AppleHillsProduction/pulls/44
2025-11-03 10:12:51 +00:00
// Load card definitions from Addressables, then register with save system
2025-10-21 10:05:49 +02:00
LoadCardDefinitionsFromAddressables ( ) ;
2025-10-16 19:43:19 +02:00
Logging . Debug ( "[CardSystemManager] Post-boot initialization complete" ) ;
2025-10-10 14:31:51 +02:00
}
2025-10-20 16:33:18 +02:00
/// <summary>
2025-10-21 10:05:49 +02:00
/// Loads all card definitions from Addressables using the "BlokkemonCard" label
2025-10-20 16:33:18 +02:00
/// </summary>
2025-10-21 10:05:49 +02:00
private async void LoadCardDefinitionsFromAddressables ( )
2025-10-20 16:33:18 +02:00
{
2025-10-21 10:05:49 +02:00
availableCards = new List < CardDefinition > ( ) ;
// Load by label instead of group name for better flexibility
var handle = UnityEngine . AddressableAssets . Addressables . LoadResourceLocationsAsync ( new string [ ] { "BlokkemonCard" } , UnityEngine . AddressableAssets . Addressables . MergeMode . Union ) ;
await handle . Task ;
if ( handle . Status = = UnityEngine . ResourceManagement . AsyncOperations . AsyncOperationStatus . Succeeded )
2025-10-20 16:33:18 +02:00
{
2025-10-21 10:05:49 +02:00
var locations = handle . Result ;
var loadedIds = new HashSet < string > ( ) ;
foreach ( var loc in locations )
2025-10-20 16:33:18 +02:00
{
2025-10-21 10:05:49 +02:00
var cardHandle = UnityEngine . AddressableAssets . Addressables . LoadAssetAsync < CardDefinition > ( loc ) ;
await cardHandle . Task ;
if ( cardHandle . Status = = UnityEngine . ResourceManagement . AsyncOperations . AsyncOperationStatus . Succeeded )
{
var cardDef = cardHandle . Result ;
if ( cardDef ! = null & & ! string . IsNullOrEmpty ( cardDef . Id ) & & ! loadedIds . Contains ( cardDef . Id ) )
{
availableCards . Add ( cardDef ) ;
loadedIds . Add ( cardDef . Id ) ;
}
}
2025-10-20 16:33:18 +02:00
}
2025-10-27 14:00:37 +01:00
// Build lookup now that cards are loaded
BuildDefinitionLookup ( ) ;
Refactoring of the interaction system and preliminary integration of save/load functionality across the game. (#44)
### Interactables Architecture Refactor
- Converted composition to inheritance, moved from component-based to class-based interactables. No more requirement for chain of "Interactable -> Item" etc.
- Created `InteractableBase` abstract base class with common functionality that replaces the old component
- Specialized child classes: `Pickup`, `ItemSlot`, `LevelSwitch`, `MinigameSwitch`, `CombinationItem`, `OneClickInteraction` are now children classes
- Light updates to the interactable inspector, moved some things arround, added collapsible inspector sections in the UI for better editor experience
### State Machine Integration
- Custom `AppleMachine` inheritong from Pixelplacement's StateMachine which implements our own interface for saving, easy place for future improvements
- Replaced all previous StateMachines by `AppleMachine`
- Custom `AppleState` extends from default `State`. Added serialization, split state logic into "EnterState", "RestoreState", "ExitState" allowing for separate logic when triggering in-game vs loading game
- Restores directly to target state without triggering transitional logic
- Migration tool converts existing instances
### Prefab Organization
- Saved changes from scenes into prefabs
- Cleaned up duplicated components, confusing prefabs hierarchies
- Created prefab variants where possible
- Consolidated Environment prefabs and moved them out of Placeholders subfolder into main Environment folder
- Organized item prefabs from PrefabsPLACEHOLDER into proper Items folder
- Updated prefab references - All scene references updated to new locations
- Removed placeholder files from Characters, Levels, UI, and Minigames folders
### Scene Updates
- Quarry scene with major updates
- Saved multiple working versions (Quarry, Quarry_Fixed, Quarry_OLD)
- Added proper lighting data
- Updated all interactable components to new architecture
### Minor editor tools
- New tool for testing cards from an editor window (no in-scene object required)
- Updated Interactable Inspector
- New debug option to opt in-and-out of the save/load system
- Tooling for easier migration
Co-authored-by: Michal Pikulski <michal.a.pikulski@gmail.com>
Reviewed-on: https://homelab.tailf7f81b.ts.net/tschesky/AppleHillsProduction/pulls/44
2025-11-03 10:12:51 +00:00
Logging . Debug ( $"[CardSystemManager] Loaded {availableCards.Count} card definitions from Addressables" ) ;
// NOW register with save/load system (definitions are ready for state restoration)
2025-10-27 14:00:37 +01:00
if ( SaveLoadManager . Instance ! = null )
{
Refactoring of the interaction system and preliminary integration of save/load functionality across the game. (#44)
### Interactables Architecture Refactor
- Converted composition to inheritance, moved from component-based to class-based interactables. No more requirement for chain of "Interactable -> Item" etc.
- Created `InteractableBase` abstract base class with common functionality that replaces the old component
- Specialized child classes: `Pickup`, `ItemSlot`, `LevelSwitch`, `MinigameSwitch`, `CombinationItem`, `OneClickInteraction` are now children classes
- Light updates to the interactable inspector, moved some things arround, added collapsible inspector sections in the UI for better editor experience
### State Machine Integration
- Custom `AppleMachine` inheritong from Pixelplacement's StateMachine which implements our own interface for saving, easy place for future improvements
- Replaced all previous StateMachines by `AppleMachine`
- Custom `AppleState` extends from default `State`. Added serialization, split state logic into "EnterState", "RestoreState", "ExitState" allowing for separate logic when triggering in-game vs loading game
- Restores directly to target state without triggering transitional logic
- Migration tool converts existing instances
### Prefab Organization
- Saved changes from scenes into prefabs
- Cleaned up duplicated components, confusing prefabs hierarchies
- Created prefab variants where possible
- Consolidated Environment prefabs and moved them out of Placeholders subfolder into main Environment folder
- Organized item prefabs from PrefabsPLACEHOLDER into proper Items folder
- Updated prefab references - All scene references updated to new locations
- Removed placeholder files from Characters, Levels, UI, and Minigames folders
### Scene Updates
- Quarry scene with major updates
- Saved multiple working versions (Quarry, Quarry_Fixed, Quarry_OLD)
- Added proper lighting data
- Updated all interactable components to new architecture
### Minor editor tools
- New tool for testing cards from an editor window (no in-scene object required)
- Updated Interactable Inspector
- New debug option to opt in-and-out of the save/load system
- Tooling for easier migration
Co-authored-by: Michal Pikulski <michal.a.pikulski@gmail.com>
Reviewed-on: https://homelab.tailf7f81b.ts.net/tschesky/AppleHillsProduction/pulls/44
2025-11-03 10:12:51 +00:00
SaveLoadManager . Instance . RegisterParticipant ( this ) ;
Logging . Debug ( "[CardSystemManager] Registered with SaveLoadManager after definitions loaded" ) ;
}
else
{
Logging . Warning ( "[CardSystemManager] SaveLoadManager not available for registration" ) ;
2025-10-27 14:00:37 +01:00
}
2025-10-20 16:33:18 +02:00
}
Refactoring of the interaction system and preliminary integration of save/load functionality across the game. (#44)
### Interactables Architecture Refactor
- Converted composition to inheritance, moved from component-based to class-based interactables. No more requirement for chain of "Interactable -> Item" etc.
- Created `InteractableBase` abstract base class with common functionality that replaces the old component
- Specialized child classes: `Pickup`, `ItemSlot`, `LevelSwitch`, `MinigameSwitch`, `CombinationItem`, `OneClickInteraction` are now children classes
- Light updates to the interactable inspector, moved some things arround, added collapsible inspector sections in the UI for better editor experience
### State Machine Integration
- Custom `AppleMachine` inheritong from Pixelplacement's StateMachine which implements our own interface for saving, easy place for future improvements
- Replaced all previous StateMachines by `AppleMachine`
- Custom `AppleState` extends from default `State`. Added serialization, split state logic into "EnterState", "RestoreState", "ExitState" allowing for separate logic when triggering in-game vs loading game
- Restores directly to target state without triggering transitional logic
- Migration tool converts existing instances
### Prefab Organization
- Saved changes from scenes into prefabs
- Cleaned up duplicated components, confusing prefabs hierarchies
- Created prefab variants where possible
- Consolidated Environment prefabs and moved them out of Placeholders subfolder into main Environment folder
- Organized item prefabs from PrefabsPLACEHOLDER into proper Items folder
- Updated prefab references - All scene references updated to new locations
- Removed placeholder files from Characters, Levels, UI, and Minigames folders
### Scene Updates
- Quarry scene with major updates
- Saved multiple working versions (Quarry, Quarry_Fixed, Quarry_OLD)
- Added proper lighting data
- Updated all interactable components to new architecture
### Minor editor tools
- New tool for testing cards from an editor window (no in-scene object required)
- Updated Interactable Inspector
- New debug option to opt in-and-out of the save/load system
- Tooling for easier migration
Co-authored-by: Michal Pikulski <michal.a.pikulski@gmail.com>
Reviewed-on: https://homelab.tailf7f81b.ts.net/tschesky/AppleHillsProduction/pulls/44
2025-11-03 10:12:51 +00:00
else
2025-10-27 14:00:37 +01:00
{
Refactoring of the interaction system and preliminary integration of save/load functionality across the game. (#44)
### Interactables Architecture Refactor
- Converted composition to inheritance, moved from component-based to class-based interactables. No more requirement for chain of "Interactable -> Item" etc.
- Created `InteractableBase` abstract base class with common functionality that replaces the old component
- Specialized child classes: `Pickup`, `ItemSlot`, `LevelSwitch`, `MinigameSwitch`, `CombinationItem`, `OneClickInteraction` are now children classes
- Light updates to the interactable inspector, moved some things arround, added collapsible inspector sections in the UI for better editor experience
### State Machine Integration
- Custom `AppleMachine` inheritong from Pixelplacement's StateMachine which implements our own interface for saving, easy place for future improvements
- Replaced all previous StateMachines by `AppleMachine`
- Custom `AppleState` extends from default `State`. Added serialization, split state logic into "EnterState", "RestoreState", "ExitState" allowing for separate logic when triggering in-game vs loading game
- Restores directly to target state without triggering transitional logic
- Migration tool converts existing instances
### Prefab Organization
- Saved changes from scenes into prefabs
- Cleaned up duplicated components, confusing prefabs hierarchies
- Created prefab variants where possible
- Consolidated Environment prefabs and moved them out of Placeholders subfolder into main Environment folder
- Organized item prefabs from PrefabsPLACEHOLDER into proper Items folder
- Updated prefab references - All scene references updated to new locations
- Removed placeholder files from Characters, Levels, UI, and Minigames folders
### Scene Updates
- Quarry scene with major updates
- Saved multiple working versions (Quarry, Quarry_Fixed, Quarry_OLD)
- Added proper lighting data
- Updated all interactable components to new architecture
### Minor editor tools
- New tool for testing cards from an editor window (no in-scene object required)
- Updated Interactable Inspector
- New debug option to opt in-and-out of the save/load system
- Tooling for easier migration
Co-authored-by: Michal Pikulski <michal.a.pikulski@gmail.com>
Reviewed-on: https://homelab.tailf7f81b.ts.net/tschesky/AppleHillsProduction/pulls/44
2025-11-03 10:12:51 +00:00
Logging . Warning ( "[CardSystemManager] Failed to load card definitions from Addressables" ) ;
2025-10-27 14:00:37 +01:00
}
}
Refactoring of the interaction system and preliminary integration of save/load functionality across the game. (#44)
### Interactables Architecture Refactor
- Converted composition to inheritance, moved from component-based to class-based interactables. No more requirement for chain of "Interactable -> Item" etc.
- Created `InteractableBase` abstract base class with common functionality that replaces the old component
- Specialized child classes: `Pickup`, `ItemSlot`, `LevelSwitch`, `MinigameSwitch`, `CombinationItem`, `OneClickInteraction` are now children classes
- Light updates to the interactable inspector, moved some things arround, added collapsible inspector sections in the UI for better editor experience
### State Machine Integration
- Custom `AppleMachine` inheritong from Pixelplacement's StateMachine which implements our own interface for saving, easy place for future improvements
- Replaced all previous StateMachines by `AppleMachine`
- Custom `AppleState` extends from default `State`. Added serialization, split state logic into "EnterState", "RestoreState", "ExitState" allowing for separate logic when triggering in-game vs loading game
- Restores directly to target state without triggering transitional logic
- Migration tool converts existing instances
### Prefab Organization
- Saved changes from scenes into prefabs
- Cleaned up duplicated components, confusing prefabs hierarchies
- Created prefab variants where possible
- Consolidated Environment prefabs and moved them out of Placeholders subfolder into main Environment folder
- Organized item prefabs from PrefabsPLACEHOLDER into proper Items folder
- Updated prefab references - All scene references updated to new locations
- Removed placeholder files from Characters, Levels, UI, and Minigames folders
### Scene Updates
- Quarry scene with major updates
- Saved multiple working versions (Quarry, Quarry_Fixed, Quarry_OLD)
- Added proper lighting data
- Updated all interactable components to new architecture
### Minor editor tools
- New tool for testing cards from an editor window (no in-scene object required)
- Updated Interactable Inspector
- New debug option to opt in-and-out of the save/load system
- Tooling for easier migration
Co-authored-by: Michal Pikulski <michal.a.pikulski@gmail.com>
Reviewed-on: https://homelab.tailf7f81b.ts.net/tschesky/AppleHillsProduction/pulls/44
2025-11-03 10:12:51 +00:00
private void OnDestroy ( )
2025-10-27 14:00:37 +01:00
{
Refactoring of the interaction system and preliminary integration of save/load functionality across the game. (#44)
### Interactables Architecture Refactor
- Converted composition to inheritance, moved from component-based to class-based interactables. No more requirement for chain of "Interactable -> Item" etc.
- Created `InteractableBase` abstract base class with common functionality that replaces the old component
- Specialized child classes: `Pickup`, `ItemSlot`, `LevelSwitch`, `MinigameSwitch`, `CombinationItem`, `OneClickInteraction` are now children classes
- Light updates to the interactable inspector, moved some things arround, added collapsible inspector sections in the UI for better editor experience
### State Machine Integration
- Custom `AppleMachine` inheritong from Pixelplacement's StateMachine which implements our own interface for saving, easy place for future improvements
- Replaced all previous StateMachines by `AppleMachine`
- Custom `AppleState` extends from default `State`. Added serialization, split state logic into "EnterState", "RestoreState", "ExitState" allowing for separate logic when triggering in-game vs loading game
- Restores directly to target state without triggering transitional logic
- Migration tool converts existing instances
### Prefab Organization
- Saved changes from scenes into prefabs
- Cleaned up duplicated components, confusing prefabs hierarchies
- Created prefab variants where possible
- Consolidated Environment prefabs and moved them out of Placeholders subfolder into main Environment folder
- Organized item prefabs from PrefabsPLACEHOLDER into proper Items folder
- Updated prefab references - All scene references updated to new locations
- Removed placeholder files from Characters, Levels, UI, and Minigames folders
### Scene Updates
- Quarry scene with major updates
- Saved multiple working versions (Quarry, Quarry_Fixed, Quarry_OLD)
- Added proper lighting data
- Updated all interactable components to new architecture
### Minor editor tools
- New tool for testing cards from an editor window (no in-scene object required)
- Updated Interactable Inspector
- New debug option to opt in-and-out of the save/load system
- Tooling for easier migration
Co-authored-by: Michal Pikulski <michal.a.pikulski@gmail.com>
Reviewed-on: https://homelab.tailf7f81b.ts.net/tschesky/AppleHillsProduction/pulls/44
2025-11-03 10:12:51 +00:00
// Unregister from save/load system
2025-10-27 14:00:37 +01:00
if ( SaveLoadManager . Instance ! = null )
{
Refactoring of the interaction system and preliminary integration of save/load functionality across the game. (#44)
### Interactables Architecture Refactor
- Converted composition to inheritance, moved from component-based to class-based interactables. No more requirement for chain of "Interactable -> Item" etc.
- Created `InteractableBase` abstract base class with common functionality that replaces the old component
- Specialized child classes: `Pickup`, `ItemSlot`, `LevelSwitch`, `MinigameSwitch`, `CombinationItem`, `OneClickInteraction` are now children classes
- Light updates to the interactable inspector, moved some things arround, added collapsible inspector sections in the UI for better editor experience
### State Machine Integration
- Custom `AppleMachine` inheritong from Pixelplacement's StateMachine which implements our own interface for saving, easy place for future improvements
- Replaced all previous StateMachines by `AppleMachine`
- Custom `AppleState` extends from default `State`. Added serialization, split state logic into "EnterState", "RestoreState", "ExitState" allowing for separate logic when triggering in-game vs loading game
- Restores directly to target state without triggering transitional logic
- Migration tool converts existing instances
### Prefab Organization
- Saved changes from scenes into prefabs
- Cleaned up duplicated components, confusing prefabs hierarchies
- Created prefab variants where possible
- Consolidated Environment prefabs and moved them out of Placeholders subfolder into main Environment folder
- Organized item prefabs from PrefabsPLACEHOLDER into proper Items folder
- Updated prefab references - All scene references updated to new locations
- Removed placeholder files from Characters, Levels, UI, and Minigames folders
### Scene Updates
- Quarry scene with major updates
- Saved multiple working versions (Quarry, Quarry_Fixed, Quarry_OLD)
- Added proper lighting data
- Updated all interactable components to new architecture
### Minor editor tools
- New tool for testing cards from an editor window (no in-scene object required)
- Updated Interactable Inspector
- New debug option to opt in-and-out of the save/load system
- Tooling for easier migration
Co-authored-by: Michal Pikulski <michal.a.pikulski@gmail.com>
Reviewed-on: https://homelab.tailf7f81b.ts.net/tschesky/AppleHillsProduction/pulls/44
2025-11-03 10:12:51 +00:00
SaveLoadManager . Instance . UnregisterParticipant ( GetSaveId ( ) ) ;
2025-10-27 14:00:37 +01:00
}
}
2025-10-10 14:31:51 +02:00
/// <summary>
/// Builds a lookup dictionary for quick access to card definitions by ID
/// </summary>
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 ) ;
}
}
}
/// <summary>
/// Adds a booster pack to the player's inventory
/// </summary>
public void AddBoosterPack ( int count = 1 )
{
playerInventory . BoosterPackCount + = count ;
OnBoosterCountChanged ? . Invoke ( playerInventory . BoosterPackCount ) ;
2025-10-14 15:53:58 +02:00
Logging . Debug ( $"[CardSystemManager] Added {count} booster pack(s). Total: {playerInventory.BoosterPackCount}" ) ;
2025-10-10 14:31:51 +02:00
}
/// <summary>
/// Opens a booster pack and returns the newly obtained cards
/// </summary>
public List < CardData > OpenBoosterPack ( )
{
if ( playerInventory . BoosterPackCount < = 0 )
{
2025-10-14 15:53:58 +02:00
Logging . Warning ( "[CardSystemManager] Attempted to open a booster pack, but none are available." ) ;
2025-10-10 14:31:51 +02:00
return new List < CardData > ( ) ;
}
playerInventory . BoosterPackCount - - ;
OnBoosterCountChanged ? . Invoke ( playerInventory . BoosterPackCount ) ;
// Draw 3 cards based on rarity distribution
List < CardData > drawnCards = DrawRandomCards ( 3 ) ;
// Add cards to the inventory
foreach ( var card in drawnCards )
{
AddCardToInventory ( card ) ;
}
// Notify listeners
OnBoosterOpened ? . Invoke ( drawnCards ) ;
2025-10-14 15:53:58 +02:00
Logging . Debug ( $"[CardSystemManager] Opened a booster pack and obtained {drawnCards.Count} cards. Remaining boosters: {playerInventory.BoosterPackCount}" ) ;
2025-10-10 14:31:51 +02:00
return drawnCards ;
}
/// <summary>
/// Adds a card to the player's inventory, handles duplicates
/// </summary>
private void AddCardToInventory ( CardData card )
{
// Check if the player already has this card type (definition)
2025-10-14 14:57:50 +02:00
if ( playerInventory . HasCard ( card . DefinitionId ) )
2025-10-10 14:31:51 +02:00
{
2025-10-14 14:57:50 +02:00
CardData existingCard = playerInventory . GetCard ( card . DefinitionId ) ;
2025-10-10 14:31:51 +02:00
existingCard . CopiesOwned + + ;
// Check if the card can be upgraded
if ( existingCard . TryUpgradeRarity ( ) )
{
OnCardRarityUpgraded ? . Invoke ( existingCard ) ;
}
2025-10-14 15:53:58 +02:00
Logging . Debug ( $"[CardSystemManager] Added duplicate card '{card.Name}'. Now have {existingCard.CopiesOwned} copies." ) ;
2025-10-10 14:31:51 +02:00
}
else
{
// Add new card
2025-10-14 14:57:50 +02:00
playerInventory . AddCard ( card ) ;
2025-10-10 14:31:51 +02:00
OnCardCollected ? . Invoke ( card ) ;
2025-10-14 15:53:58 +02:00
Logging . Debug ( $"[CardSystemManager] Added new card '{card.Name}' to collection." ) ;
2025-10-10 14:31:51 +02:00
}
}
/// <summary>
/// Draws random cards based on rarity distribution
/// </summary>
private List < CardData > DrawRandomCards ( int count )
{
List < CardData > result = new List < CardData > ( ) ;
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 < CardDefinition > 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
2025-10-14 15:53:58 +02:00
Logging . Warning ( $"[CardSystemManager] No cards of rarity {rarity} available, selecting a random card instead." ) ;
2025-10-10 14:31:51 +02:00
int randomIndex = UnityEngine . Random . Range ( 0 , availableCards . Count ) ;
CardDefinition randomDef = availableCards [ randomIndex ] ;
CardData newCard = randomDef . CreateCardData ( ) ;
result . Add ( newCard ) ;
}
}
return result ;
}
/// <summary>
/// Determines a random card rarity with appropriate weighting
/// </summary>
private CardRarity DetermineRandomRarity ( )
{
2025-11-05 23:50:15 +01:00
// Weighted random for 3 rarities
2025-10-10 14:31:51 +02:00
float rand = UnityEngine . Random . value ;
2025-11-05 23:50:15 +01:00
if ( rand < 0.70f ) return CardRarity . Normal ; // 70% chance
if ( rand < 0.95f ) return CardRarity . Rare ; // 25% chance
return CardRarity . Legendary ; // 5% chance
2025-10-10 14:31:51 +02:00
}
/// <summary>
/// Returns all cards from the player's collection
/// </summary>
public List < CardData > GetAllCollectedCards ( )
{
2025-10-14 14:57:50 +02:00
return playerInventory . GetAllCards ( ) ;
}
/// <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 ) ;
2025-10-10 14:31:51 +02:00
}
/// <summary>
/// Returns the number of booster packs the player has
/// </summary>
public int GetBoosterPackCount ( )
{
return playerInventory . BoosterPackCount ;
}
/// <summary>
/// Returns whether a specific card definition has been collected
/// </summary>
public bool IsCardCollected ( string definitionId )
{
2025-10-14 14:57:50 +02:00
return playerInventory . HasCard ( definitionId ) ;
}
/// <summary>
/// Gets total unique card count
/// </summary>
public int GetUniqueCardCount ( )
{
return playerInventory . GetUniqueCardCount ( ) ;
}
/// <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 ;
2025-10-10 14:31:51 +02:00
}
2025-10-20 13:45:56 +02:00
/// <summary>
/// Returns all available card definitions in the system
/// </summary>
public List < CardDefinition > GetAllCardDefinitions ( )
{
return new List < CardDefinition > ( availableCards ) ;
}
/// <summary>
/// Returns direct access to the player's card inventory
/// For advanced operations and testing
/// </summary>
public CardInventory GetCardInventory ( )
{
return playerInventory ;
}
/// <summary>
/// Returns cards filtered by both zone and rarity
/// </summary>
public List < CardData > GetCardsByZoneAndRarity ( CardZone zone , CardRarity rarity )
{
List < CardData > zoneCards = GetCardsByZone ( zone ) ;
return zoneCards . FindAll ( c = > c . Rarity = = rarity ) ;
}
/// <summary>
/// Returns the count of cards by rarity
/// </summary>
public int GetCardCountByRarity ( CardRarity rarity )
{
return playerInventory . GetCardsByRarity ( rarity ) . Count ;
}
/// <summary>
/// Returns the count of cards by zone
/// </summary>
public int GetCardCountByZone ( CardZone zone )
{
return playerInventory . GetCardsByZone ( zone ) . Count ;
}
/// <summary>
/// Gets the total number of card definitions available in the system
/// </summary>
public int GetTotalCardDefinitionsCount ( )
{
return availableCards . Count ;
}
/// <summary>
/// Gets the total collection completion percentage (0-100)
/// </summary>
public float GetTotalCompletionPercentage ( )
{
if ( availableCards . Count = = 0 ) return 0 ;
return ( float ) GetUniqueCardCount ( ) / availableCards . Count * 100f ;
}
/// <summary>
/// Gets total completion percentage for a specific rarity (0-100)
/// </summary>
public float GetRarityCompletionPercentage ( CardRarity rarity )
{
// Count available cards of this rarity
int totalOfRarity = availableCards . FindAll ( c = > c . Rarity = = rarity ) . Count ;
if ( totalOfRarity = = 0 ) return 0 ;
// Count collected cards of this rarity
int collectedOfRarity = playerInventory . GetCardsByRarity ( rarity ) . Count ;
return ( float ) collectedOfRarity / totalOfRarity * 100f ;
}
2025-10-27 14:00:37 +01:00
/// <summary>
/// Export current card collection to a serializable snapshot
/// </summary>
public CardCollectionState ExportCardCollectionState ( )
{
var state = new CardCollectionState
{
boosterPackCount = playerInventory . BoosterPackCount ,
cards = new List < SavedCardEntry > ( )
} ;
foreach ( var card in playerInventory . CollectedCards . Values )
{
if ( string . IsNullOrEmpty ( card . DefinitionId ) ) continue ;
state . cards . Add ( new SavedCardEntry
{
definitionId = card . DefinitionId ,
rarity = card . Rarity ,
copiesOwned = card . CopiesOwned
} ) ;
}
return state ;
}
/// <summary>
/// Apply a previously saved snapshot to the runtime inventory
/// </summary>
public void ApplyCardCollectionState ( CardCollectionState state )
{
if ( state = = null ) return ;
playerInventory . ClearAllCards ( ) ;
playerInventory . BoosterPackCount = state . boosterPackCount ;
OnBoosterCountChanged ? . Invoke ( playerInventory . BoosterPackCount ) ;
foreach ( var entry in state . cards )
{
if ( string . IsNullOrEmpty ( entry . definitionId ) ) continue ;
if ( _definitionLookup . TryGetValue ( entry . definitionId , out var def ) )
{
// Create from definition to ensure links, then overwrite runtime fields
var cd = def . CreateCardData ( ) ;
cd . Rarity = entry . rarity ;
cd . CopiesOwned = entry . copiesOwned ;
playerInventory . AddCard ( cd ) ;
}
else
{
Logging . Warning ( $"[CardSystemManager] Saved card definition not found: {entry.definitionId}" ) ;
}
}
}
Refactoring of the interaction system and preliminary integration of save/load functionality across the game. (#44)
### Interactables Architecture Refactor
- Converted composition to inheritance, moved from component-based to class-based interactables. No more requirement for chain of "Interactable -> Item" etc.
- Created `InteractableBase` abstract base class with common functionality that replaces the old component
- Specialized child classes: `Pickup`, `ItemSlot`, `LevelSwitch`, `MinigameSwitch`, `CombinationItem`, `OneClickInteraction` are now children classes
- Light updates to the interactable inspector, moved some things arround, added collapsible inspector sections in the UI for better editor experience
### State Machine Integration
- Custom `AppleMachine` inheritong from Pixelplacement's StateMachine which implements our own interface for saving, easy place for future improvements
- Replaced all previous StateMachines by `AppleMachine`
- Custom `AppleState` extends from default `State`. Added serialization, split state logic into "EnterState", "RestoreState", "ExitState" allowing for separate logic when triggering in-game vs loading game
- Restores directly to target state without triggering transitional logic
- Migration tool converts existing instances
### Prefab Organization
- Saved changes from scenes into prefabs
- Cleaned up duplicated components, confusing prefabs hierarchies
- Created prefab variants where possible
- Consolidated Environment prefabs and moved them out of Placeholders subfolder into main Environment folder
- Organized item prefabs from PrefabsPLACEHOLDER into proper Items folder
- Updated prefab references - All scene references updated to new locations
- Removed placeholder files from Characters, Levels, UI, and Minigames folders
### Scene Updates
- Quarry scene with major updates
- Saved multiple working versions (Quarry, Quarry_Fixed, Quarry_OLD)
- Added proper lighting data
- Updated all interactable components to new architecture
### Minor editor tools
- New tool for testing cards from an editor window (no in-scene object required)
- Updated Interactable Inspector
- New debug option to opt in-and-out of the save/load system
- Tooling for easier migration
Co-authored-by: Michal Pikulski <michal.a.pikulski@gmail.com>
Reviewed-on: https://homelab.tailf7f81b.ts.net/tschesky/AppleHillsProduction/pulls/44
2025-11-03 10:12:51 +00:00
#region ISaveParticipant Implementation
private bool hasBeenRestored ;
/// <summary>
/// Returns true if this participant has already had its state restored.
/// </summary>
public bool HasBeenRestored = > hasBeenRestored ;
/// <summary>
/// Returns the unique save ID for the CardSystemManager.
/// Since this is a singleton global system, the ID is constant.
/// </summary>
public string GetSaveId ( )
{
return "CardSystemManager" ;
}
/// <summary>
/// Serializes the current card collection state to JSON.
/// </summary>
public string SerializeState ( )
{
var state = ExportCardCollectionState ( ) ;
return JsonUtility . ToJson ( state ) ;
}
/// <summary>
/// Restores the card collection state from serialized JSON data.
/// </summary>
public void RestoreState ( string serializedData )
{
if ( string . IsNullOrEmpty ( serializedData ) )
{
Logging . Debug ( "[CardSystemManager] No saved state to restore, using defaults" ) ;
hasBeenRestored = true ;
return ;
}
try
{
var state = JsonUtility . FromJson < CardCollectionState > ( serializedData ) ;
if ( state ! = null )
{
ApplyCardCollectionState ( state ) ;
hasBeenRestored = true ;
Logging . Debug ( "[CardSystemManager] Successfully restored card collection state" ) ;
}
else
{
Logging . Warning ( "[CardSystemManager] Failed to deserialize card collection state" ) ;
}
}
catch ( Exception ex )
{
Logging . Warning ( $"[CardSystemManager] Exception while restoring card collection state: {ex}" ) ;
}
}
#endregion
2025-10-10 14:31:51 +02:00
}
}