Files
AppleHillsProduction/Assets/Scripts/UI/CardSystem/DragDrop/AlbumCardSlot.cs

166 lines
6.3 KiB
C#
Raw Normal View History

using AppleHills.Data.CardSystem;
2025-11-10 13:03:36 +01:00
using Core;
using Data.CardSystem;
using UI.DragAndDrop.Core;
using UnityEngine;
namespace UI.CardSystem
{
/// <summary>
/// Specialized slot for album pages that only accepts a specific card.
2025-11-17 14:30:07 +01:00
/// Empty by default, auto-spawns owned cards on enable.
/// </summary>
2025-11-16 20:35:54 +01:00
public class AlbumCardSlot : DraggableSlot
{
[Header("Album Slot Configuration")]
[SerializeField] private CardDefinition targetCardDefinition; // Which card this slot accepts
2025-11-16 20:35:54 +01:00
[SerializeField] private GameObject cardPrefab; // Card prefab to spawn when card is owned
2025-11-17 14:30:07 +01:00
private StateMachine.Card _assignedCard; // The card currently in this slot (if any)
/// <summary>
/// Get the target card definition for this slot
/// </summary>
public CardDefinition TargetCardDefinition => targetCardDefinition;
/// <summary>
/// Check if this slot has a card assigned to it
/// </summary>
public bool HasCardAssigned => _assignedCard != null;
/// <summary>
/// Get the card currently assigned to this slot
/// </summary>
public StateMachine.Card AssignedCard => _assignedCard;
private void OnEnable()
{
// Check if we should spawn a card for this slot
CheckAndSpawnOwnedCard();
}
2025-11-17 14:30:07 +01:00
/// <summary>
/// Assign a card to this slot (called by AlbumViewPage after placement animation)
/// </summary>
public void AssignCard(StateMachine.Card card)
{
if (card == null)
{
Logging.Warning("[AlbumCardSlot] Attempted to assign null card to slot");
return;
}
if (_assignedCard != null && _assignedCard != card)
{
Logging.Warning($"[AlbumCardSlot] Slot already has a card assigned, replacing with new card");
// Clean up old card
if (_assignedCard.gameObject != null)
{
Destroy(_assignedCard.gameObject);
}
}
_assignedCard = card;
Logging.Debug($"[AlbumCardSlot] Card '{card.CardData?.Name}' assigned to slot for {targetCardDefinition?.name}");
}
/// <summary>
/// Check if player owns the card for this slot and spawn it if so
2025-11-17 14:30:07 +01:00
/// (Called on OnEnable to handle game reload scenarios)
/// </summary>
private void CheckAndSpawnOwnedCard()
{
// Guard: need CardSystemManager and target definition
if (CardSystemManager.Instance == null || targetCardDefinition == null)
return;
2025-11-17 14:30:07 +01:00
// Guard: don't spawn if already has a card assigned
if (_assignedCard != null)
{
Logging.Debug($"[AlbumCardSlot] Slot for {targetCardDefinition.name} already has card assigned, skipping spawn");
return;
2025-11-17 14:30:07 +01:00
}
// Guard: need prefab to spawn
2025-11-16 20:35:54 +01:00
if (cardPrefab == null)
{
2025-11-16 20:35:54 +01:00
Logging.Warning($"[AlbumCardSlot] No cardPrefab assigned for slot targeting {targetCardDefinition.name}");
return;
}
2025-11-17 14:30:07 +01:00
// Check if player owns this card in COLLECTION (not pending)
CardData ownedCard = FindOwnedCardForSlot();
2025-11-17 14:30:07 +01:00
// Only spawn if owned (not pending)
if (ownedCard != null)
{
Logging.Debug($"[AlbumCardSlot] Found owned card for {targetCardDefinition.name}, spawning");
SpawnOwnedCard(ownedCard);
}
}
/// <summary>
/// Find owned card for this slot (checks collection only, not pending)
/// </summary>
private CardData FindOwnedCardForSlot()
{
var inventory = CardSystemManager.Instance.GetCardInventory();
// Check in order: Legendary > Rare > Normal (prioritize highest rarity)
foreach (CardRarity rarity in new[] { CardRarity.Legendary, CardRarity.Rare, CardRarity.Normal })
{
2025-11-17 14:30:07 +01:00
CardData card = inventory.GetCard(targetCardDefinition.Id, rarity);
if (card != null)
{
2025-11-17 14:30:07 +01:00
return card; // Found highest rarity owned
}
}
2025-11-17 14:30:07 +01:00
return null; // Not owned
}
/// <summary>
2025-11-17 14:30:07 +01:00
/// Spawn a card that the player already owns (for reload scenarios)
/// </summary>
2025-11-17 14:30:07 +01:00
private void SpawnOwnedCard(CardData cardData)
{
2025-11-16 20:35:54 +01:00
GameObject cardObj = Instantiate(cardPrefab, transform);
var card = cardObj.GetComponent<StateMachine.Card>();
2025-11-16 20:35:54 +01:00
if (card != null)
{
2025-11-16 20:35:54 +01:00
// Setup card for album slot (starts in PlacedInSlotState)
card.SetupForAlbumSlot(cardData, this);
2025-11-16 20:35:54 +01:00
// Resize the card to match the slot size
RectTransform cardRect = card.transform as RectTransform;
Refactor interactions, introduce template-method lifecycle management, work on save-load system (#51) # Lifecycle Management & Save System Revamp ## Overview Complete overhaul of game lifecycle management, interactable system, and save/load architecture. Introduces centralized `ManagedBehaviour` base class for consistent initialization ordering and lifecycle hooks across all systems. ## Core Architecture ### New Lifecycle System - **`LifecycleManager`**: Centralized coordinator for all managed objects - **`ManagedBehaviour`**: Base class replacing ad-hoc initialization patterns - `OnManagedAwake()`: Priority-based initialization (0-100, lower = earlier) - `OnSceneReady()`: Scene-specific setup after managers ready - Replaces `BootCompletionService` (deleted) - **Priority groups**: Infrastructure (0-20) → Game Systems (30-50) → Data (60-80) → UI/Gameplay (90-100) - **Editor support**: `EditorLifecycleBootstrap` ensures lifecycle works in editor mode ### Unified SaveID System - Consistent format: `{ParentName}_{ComponentType}` - Auto-registration via `AutoRegisterForSave = true` - New `DebugSaveIds` editor tool for inspection ## Save/Load Improvements ### Enhanced State Management - **Extended SaveLoadData**: Unlocked minigames, card collection states, combination items, slot occupancy - **Async loading**: `ApplyCardCollectionState()` waits for card definitions before restoring - **New `SaveablePlayableDirector`**: Timeline sequences save/restore playback state - **Fixed race conditions**: Proper initialization ordering prevents data corruption ## Interactable & Pickup System - Migrated to `OnManagedAwake()` for consistent initialization - Template method pattern for state restoration (`RestoreInteractionState()`) - Fixed combination item save/load bugs (items in slots vs. follower hand) - Dynamic spawning support for combined items on load - **Breaking**: `Interactable.Awake()` now sealed, use `OnManagedAwake()` instead ## UI System Changes - **AlbumViewPage** and **BoosterNotificationDot**: Migrated to `ManagedBehaviour` - **Fixed menu persistence bug**: Menus no longer reappear after scene transitions - **Pause Menu**: Now reacts to all scene loads (not just first scene) - **Orientation Enforcer**: Enforces per-scene via `SceneManagementService` - **Loading Screen**: Integrated with new lifecycle ## ⚠️ Breaking Changes 1. **`BootCompletionService` removed** → Use `ManagedBehaviour.OnManagedAwake()` with priority 2. **`Interactable.Awake()` sealed** → Override `OnManagedAwake()` instead 3. **SaveID format changed** → Now `{ParentName}_{ComponentType}` consistently 4. **MonoBehaviours needing init ordering** → Must inherit from `ManagedBehaviour` Co-authored-by: Michal Pikulski <michal.a.pikulski@gmail.com> Co-authored-by: Michal Pikulski <michal@foolhardyhorizons.com> Reviewed-on: https://homelab.tailf7f81b.ts.net/tschesky/AppleHillsProduction/pulls/51
2025-11-07 15:38:31 +00:00
RectTransform slotRect = transform as RectTransform;
if (cardRect != null && slotRect != null)
{
float targetHeight = slotRect.rect.height;
cardRect.sizeDelta = new Vector2(cardRect.sizeDelta.x, targetHeight);
cardRect.localPosition = Vector3.zero;
cardRect.localRotation = Quaternion.identity;
}
2025-11-17 14:30:07 +01:00
// Assign card to this slot
_assignedCard = card;
// Register with AlbumViewPage for enlarge/shrink handling
Lifecycle System Refactor & Logging Centralization (#56) ## ManagedBehaviour System Refactor - **Sealed `Awake()`** to prevent override mistakes that break singleton registration - **Added `OnManagedAwake()`** for early initialization (fires during registration) - **Renamed lifecycle hook:** `OnManagedAwake()` → `OnManagedStart()` (fires after boot, mirrors Unity's Awake→Start) - **40 files migrated** to new pattern (2 core, 38 components) - Eliminated all fragile `private new void Awake()` patterns - Zero breaking changes - backward compatible ## Centralized Logging System - **Automatic tagging** via `CallerMemberName` and `CallerFilePath` - logs auto-tagged as `[ClassName][MethodName] message` - **Unified API:** Single `Logging.Debug/Info/Warning/Error()` replaces custom `LogDebugMessage()` implementations - **~90 logging call sites** migrated across 10 files - **10 redundant helper methods** removed - All logs broadcast via `Logging.OnLogEntryAdded` event for real-time monitoring ## Custom Log Console (Editor Window) - **Persistent filter popups** for multi-selection (classes, methods, log levels) - windows stay open during selection - **Search** across class names, methods, and message content - **Time range filter** with MinMaxSlider - **Export** filtered logs to timestamped `.txt` files - **Right-click context menu** for quick filtering and copy actions - **Visual improvements:** White text, alternating row backgrounds, color-coded log levels - **Multiple instances** supported for simultaneous system monitoring - Open via `AppleHills > Custom Log Console` Co-authored-by: Michal Pikulski <michal@foolhardyhorizons.com> Co-authored-by: Michal Pikulski <michal.a.pikulski@gmail.com> Reviewed-on: https://homelab.tailf7f81b.ts.net/tschesky/AppleHillsProduction/pulls/56
2025-11-11 08:48:29 +00:00
AlbumViewPage albumPage = FindFirstObjectByType<AlbumViewPage>();
if (albumPage != null)
{
2025-11-16 20:35:54 +01:00
albumPage.RegisterCardInAlbum(card);
}
2025-11-10 13:03:36 +01:00
Logging.Debug($"[AlbumCardSlot] Spawned owned card '{cardData.Name}' ({cardData.Rarity}) in slot");
}
else
{
2025-11-16 20:35:54 +01:00
Logging.Warning($"[AlbumCardSlot] Spawned prefab has no Card component!");
Destroy(cardObj);
}
}
}
}