Files
AppleHillsProduction/Assets/Scripts/CardSystem/DragDrop/AlbumCardSlot.cs
tschesky 235fa04eba Merge a card refresh (#59)
- **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
2025-11-18 08:40:59 +00:00

166 lines
6.3 KiB
C#

using AppleHills.Data.CardSystem;
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.
/// Empty by default, auto-spawns owned cards on enable.
/// </summary>
public class AlbumCardSlot : DraggableSlot
{
[Header("Album Slot Configuration")]
[SerializeField] private CardDefinition targetCardDefinition; // Which card this slot accepts
[SerializeField] private GameObject cardPrefab; // Card prefab to spawn when card is owned
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();
}
/// <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
/// (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;
// 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;
}
// Guard: need prefab to spawn
if (cardPrefab == null)
{
Logging.Warning($"[AlbumCardSlot] No cardPrefab assigned for slot targeting {targetCardDefinition.name}");
return;
}
// Check if player owns this card in COLLECTION (not pending)
CardData ownedCard = FindOwnedCardForSlot();
// 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 })
{
CardData card = inventory.GetCard(targetCardDefinition.Id, rarity);
if (card != null)
{
return card; // Found highest rarity owned
}
}
return null; // Not owned
}
/// <summary>
/// Spawn a card that the player already owns (for reload scenarios)
/// </summary>
private void SpawnOwnedCard(CardData cardData)
{
GameObject cardObj = Instantiate(cardPrefab, transform);
var card = cardObj.GetComponent<StateMachine.Card>();
if (card != null)
{
// Setup card for album slot (starts in PlacedInSlotState)
card.SetupForAlbumSlot(cardData, this);
// Resize the card to match the slot size
RectTransform cardRect = card.transform as RectTransform;
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;
}
// Assign card to this slot
_assignedCard = card;
// Register with AlbumViewPage for enlarge/shrink handling
AlbumViewPage albumPage = FindFirstObjectByType<AlbumViewPage>();
if (albumPage != null)
{
albumPage.RegisterCardInAlbum(card);
}
Logging.Debug($"[AlbumCardSlot] Spawned owned card '{cardData.Name}' ({cardData.Rarity}) in slot");
}
else
{
Logging.Warning($"[AlbumCardSlot] Spawned prefab has no Card component!");
Destroy(cardObj);
}
}
}
}