Slotting cards in album after revealing
This commit is contained in:
@@ -1,7 +1,11 @@
|
||||
using Bootstrap;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AppleHills.Data.CardSystem;
|
||||
using Bootstrap;
|
||||
using Data.CardSystem;
|
||||
using Pixelplacement;
|
||||
using UI.Core;
|
||||
using UI.DragAndDrop.Core;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
@@ -18,11 +22,20 @@ namespace UI.CardSystem
|
||||
[SerializeField] private Button exitButton;
|
||||
[SerializeField] private BookCurlPro.BookPro book;
|
||||
|
||||
[Header("Zone Navigation")]
|
||||
[SerializeField] private BookTabButton[] zoneTabs; // All zone tab buttons
|
||||
|
||||
[Header("Album Card Reveal")]
|
||||
[SerializeField] private SlotContainer bottomRightSlots;
|
||||
[SerializeField] private GameObject albumCardPrefab;
|
||||
|
||||
[Header("Booster Pack UI")]
|
||||
[SerializeField] private GameObject[] boosterPackButtons;
|
||||
[SerializeField] private BoosterOpeningPage boosterOpeningPage;
|
||||
|
||||
private Input.InputMode _previousInputMode;
|
||||
private List<AlbumCardDraggable> _activeCards = new List<AlbumCardDraggable>();
|
||||
private const int MAX_VISIBLE_CARDS = 3;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
@@ -54,6 +67,8 @@ namespace UI.CardSystem
|
||||
if (CardSystemManager.Instance != null)
|
||||
{
|
||||
CardSystemManager.Instance.OnBoosterCountChanged += OnBoosterCountChanged;
|
||||
// NOTE: OnPendingCardAdded is subscribed in TransitionIn, not here
|
||||
// This prevents spawning cards when page is not active
|
||||
|
||||
// Update initial button visibility
|
||||
int initialCount = CardSystemManager.Instance.GetBoosterPackCount();
|
||||
@@ -83,6 +98,7 @@ namespace UI.CardSystem
|
||||
if (CardSystemManager.Instance != null)
|
||||
{
|
||||
CardSystemManager.Instance.OnBoosterCountChanged -= OnBoosterCountChanged;
|
||||
// NOTE: OnPendingCardAdded is unsubscribed in TransitionOut
|
||||
}
|
||||
|
||||
// Clean up exit button
|
||||
@@ -105,6 +121,9 @@ namespace UI.CardSystem
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up active cards
|
||||
CleanupActiveCards();
|
||||
}
|
||||
|
||||
private void OnExitButtonClicked()
|
||||
@@ -182,11 +201,26 @@ namespace UI.CardSystem
|
||||
Debug.Log("[AlbumViewPage] Switched to UI-only input mode on first entry");
|
||||
}
|
||||
|
||||
// Subscribe to pending card events while page is active
|
||||
if (CardSystemManager.Instance != null)
|
||||
{
|
||||
CardSystemManager.Instance.OnPendingCardAdded += OnPendingCardAdded;
|
||||
}
|
||||
|
||||
// Spawn pending cards when opening album
|
||||
SpawnPendingCards();
|
||||
|
||||
base.TransitionIn();
|
||||
}
|
||||
|
||||
public override void TransitionOut()
|
||||
{
|
||||
// Unsubscribe from pending card events when page closes
|
||||
if (CardSystemManager.Instance != null)
|
||||
{
|
||||
CardSystemManager.Instance.OnPendingCardAdded -= OnPendingCardAdded;
|
||||
}
|
||||
|
||||
// Don't restore input mode here - only restore when actually exiting (in OnExitButtonClicked)
|
||||
base.TransitionOut();
|
||||
}
|
||||
@@ -219,5 +253,312 @@ namespace UI.CardSystem
|
||||
onComplete?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
#region Album Card Reveal System
|
||||
|
||||
/// <summary>
|
||||
/// Spawn pending cards from CardSystemManager
|
||||
/// Only spawns unique cards (one per definition+rarity, not one per copy)
|
||||
/// </summary>
|
||||
private void SpawnPendingCards()
|
||||
{
|
||||
if (CardSystemManager.Instance == null || bottomRightSlots == null || albumCardPrefab == null)
|
||||
return;
|
||||
|
||||
var pending = CardSystemManager.Instance.GetPendingRevealCards();
|
||||
|
||||
// Get unique cards only (by DefinitionId + Rarity)
|
||||
// Filter out cards with CopiesOwned = 0 (shouldn't happen but guard against it)
|
||||
var uniquePending = pending
|
||||
.Where(c => c.CopiesOwned > 0) // Guard: exclude zero-count cards
|
||||
.GroupBy(c => new { c.DefinitionId, c.Rarity })
|
||||
.Select(g => g.First()) // Take first instance of each unique card
|
||||
.ToList();
|
||||
|
||||
int spawnCount = Mathf.Min(uniquePending.Count, MAX_VISIBLE_CARDS);
|
||||
|
||||
Debug.Log($"[AlbumViewPage] Spawning {spawnCount} unique pending cards (total pending: {pending.Count})");
|
||||
|
||||
for (int i = 0; i < spawnCount; i++)
|
||||
{
|
||||
SpawnCardInSlot(i, uniquePending[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spawn a card in a specific slot
|
||||
/// </summary>
|
||||
private void SpawnCardInSlot(int slotIndex, CardData cardData)
|
||||
{
|
||||
// Guard: Don't spawn cards with zero copies
|
||||
if (cardData.CopiesOwned <= 0)
|
||||
{
|
||||
Debug.LogWarning($"[AlbumViewPage] Skipping spawn of card '{cardData.Name}' with {cardData.CopiesOwned} copies");
|
||||
return;
|
||||
}
|
||||
|
||||
DraggableSlot slot = FindSlotByIndex(slotIndex);
|
||||
if (slot == null)
|
||||
{
|
||||
Debug.LogWarning($"[AlbumViewPage] Could not find slot with SlotIndex {slotIndex}");
|
||||
return;
|
||||
}
|
||||
|
||||
// Instantiate card directly as child of the slot container (not the slot itself, not canvas root)
|
||||
// This keeps it in the correct UI hierarchy
|
||||
GameObject cardObj = Instantiate(albumCardPrefab, bottomRightSlots.transform);
|
||||
AlbumCardDraggable card = cardObj.GetComponent<AlbumCardDraggable>();
|
||||
|
||||
if (card != null)
|
||||
{
|
||||
// Setup card data
|
||||
card.SetupCard(cardData);
|
||||
|
||||
// Subscribe to events
|
||||
card.OnCardRevealed += OnCardRevealed;
|
||||
card.OnCardPlacedInAlbum += OnCardPlacedInAlbum;
|
||||
|
||||
// NOW assign to slot - this will:
|
||||
// 1. Reparent to slot
|
||||
// 2. Apply slot's occupantSizeMode scaling
|
||||
// 3. Animate to slot position
|
||||
card.AssignToSlot(slot, true);
|
||||
|
||||
// Track it
|
||||
_activeCards.Add(card);
|
||||
|
||||
Debug.Log($"[AlbumViewPage] Spawned card '{cardData.Name}' (CopiesOwned: {cardData.CopiesOwned}) in slot {slotIndex}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"[AlbumViewPage] Spawned card has no AlbumCardDraggable component!");
|
||||
Destroy(cardObj);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle when a new card is added to pending queue
|
||||
/// Only spawn if this unique card isn't already visualized
|
||||
/// </summary>
|
||||
private void OnPendingCardAdded(CardData card)
|
||||
{
|
||||
// Guard: Don't spawn cards with zero copies
|
||||
if (card.CopiesOwned <= 0)
|
||||
{
|
||||
Debug.LogWarning($"[AlbumViewPage] Ignoring pending card '{card.Name}' with {card.CopiesOwned} copies");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if we already have a card with this definition + rarity spawned
|
||||
bool alreadySpawned = _activeCards.Any(c =>
|
||||
c.CardData.DefinitionId == card.DefinitionId &&
|
||||
c.CardData.Rarity == card.Rarity);
|
||||
|
||||
if (alreadySpawned)
|
||||
{
|
||||
Debug.Log($"[AlbumViewPage] Card '{card.Name}' already spawned, skipping duplicate spawn");
|
||||
return; // Don't spawn duplicates
|
||||
}
|
||||
|
||||
// Try to spawn if we have space
|
||||
if (_activeCards.Count < MAX_VISIBLE_CARDS)
|
||||
{
|
||||
int nextSlotIndex = _activeCards.Count;
|
||||
SpawnCardInSlot(nextSlotIndex, card);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle when a card is revealed (flipped)
|
||||
/// </summary>
|
||||
private void OnCardRevealed(AlbumCardDraggable card, CardData cardData)
|
||||
{
|
||||
Debug.Log($"[AlbumViewPage] Card revealed: {cardData.Name} (Zone: {cardData.Zone}, CopiesOwned: {cardData.CopiesOwned})");
|
||||
|
||||
// IMMEDIATELY move card from pending to inventory upon reveal
|
||||
if (CardSystemManager.Instance != null)
|
||||
{
|
||||
CardSystemManager.Instance.MarkCardAsPlaced(cardData);
|
||||
Debug.Log($"[AlbumViewPage] Moved card '{cardData.Name}' from pending to inventory on reveal");
|
||||
}
|
||||
|
||||
// Remove this card from active cards list
|
||||
_activeCards.Remove(card);
|
||||
|
||||
// Check if we're currently viewing the correct zone for this card
|
||||
CardZone currentZone = GetCurrentZone();
|
||||
|
||||
if (currentZone != cardData.Zone)
|
||||
{
|
||||
// Card is from a different zone - navigate to its zone
|
||||
Debug.Log($"[AlbumViewPage] Card zone ({cardData.Zone}) doesn't match current zone ({currentZone}). Navigating to card's zone...");
|
||||
NavigateToZone(cardData.Zone);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log($"[AlbumViewPage] Card zone ({cardData.Zone}) matches current zone - no navigation needed.");
|
||||
}
|
||||
|
||||
// Shuffle remaining cards to front and spawn next unique card
|
||||
ShuffleCardsToFront();
|
||||
TrySpawnNextCard();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle when a card is placed in the album (from AlbumCardDraggable)
|
||||
/// Card data already moved to inventory in OnCardRevealed
|
||||
/// This just handles cleanup
|
||||
/// </summary>
|
||||
private void OnCardPlacedInAlbum(AlbumCardDraggable card, CardData cardData)
|
||||
{
|
||||
Debug.Log($"[AlbumViewPage] Card placed in album slot: {cardData.Name}");
|
||||
|
||||
// Unsubscribe from events (card is now static in album)
|
||||
card.OnCardRevealed -= OnCardRevealed;
|
||||
card.OnCardPlacedInAlbum -= OnCardPlacedInAlbum;
|
||||
|
||||
// Note: Card already removed from _activeCards in OnCardRevealed
|
||||
// Note: Shuffle and spawn already done in OnCardRevealed
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shuffle active cards to occupy front slots
|
||||
/// </summary>
|
||||
private void ShuffleCardsToFront()
|
||||
{
|
||||
if (bottomRightSlots == null || _activeCards.Count == 0)
|
||||
return;
|
||||
|
||||
// Convert to base DraggableObject list for helper method
|
||||
List<DraggableObject> draggableList = _activeCards.Cast<DraggableObject>().ToList();
|
||||
SlotContainerHelper.ShuffleToFront(bottomRightSlots, draggableList, animate: true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to spawn the next pending card
|
||||
/// Only spawns unique cards (not duplicates)
|
||||
/// </summary>
|
||||
private void TrySpawnNextCard()
|
||||
{
|
||||
if (CardSystemManager.Instance == null)
|
||||
return;
|
||||
|
||||
if (_activeCards.Count >= MAX_VISIBLE_CARDS)
|
||||
return; // Already at max
|
||||
|
||||
var pending = CardSystemManager.Instance.GetPendingRevealCards();
|
||||
|
||||
// Get unique pending cards, excluding zero-count cards
|
||||
var uniquePending = pending
|
||||
.Where(c => c.CopiesOwned > 0) // Guard: exclude zero-count cards
|
||||
.GroupBy(c => new { c.DefinitionId, c.Rarity })
|
||||
.Select(g => g.First())
|
||||
.ToList();
|
||||
|
||||
// Find first unique card that's not already spawned
|
||||
foreach (var cardData in uniquePending)
|
||||
{
|
||||
bool alreadySpawned = _activeCards.Any(c =>
|
||||
c.CardData.DefinitionId == cardData.DefinitionId &&
|
||||
c.CardData.Rarity == cardData.Rarity);
|
||||
|
||||
if (!alreadySpawned)
|
||||
{
|
||||
int nextSlotIndex = _activeCards.Count;
|
||||
SpawnCardInSlot(nextSlotIndex, cardData);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find a slot by its SlotIndex property
|
||||
/// </summary>
|
||||
private DraggableSlot FindSlotByIndex(int slotIndex)
|
||||
{
|
||||
if (bottomRightSlots == null)
|
||||
return null;
|
||||
|
||||
foreach (var slot in bottomRightSlots.Slots)
|
||||
{
|
||||
if (slot.SlotIndex == slotIndex)
|
||||
{
|
||||
return slot;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the current zone based on book page
|
||||
/// </summary>
|
||||
public CardZone GetCurrentZone()
|
||||
{
|
||||
if (book == null || zoneTabs == null || zoneTabs.Length == 0)
|
||||
return CardZone.AppleHills; // Default
|
||||
|
||||
int currentPage = book.CurrentPaper;
|
||||
|
||||
// Find tab with matching target page
|
||||
foreach (var tab in zoneTabs)
|
||||
{
|
||||
if (tab.TargetPage == currentPage)
|
||||
{
|
||||
return tab.Zone;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to first zone
|
||||
return zoneTabs[0].Zone;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get tab for a specific zone
|
||||
/// </summary>
|
||||
public BookTabButton GetTabForZone(CardZone zone)
|
||||
{
|
||||
if (zoneTabs == null)
|
||||
return null;
|
||||
|
||||
foreach (var tab in zoneTabs)
|
||||
{
|
||||
if (tab.Zone == zone)
|
||||
{
|
||||
return tab;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Navigate to a specific zone
|
||||
/// </summary>
|
||||
public void NavigateToZone(CardZone zone)
|
||||
{
|
||||
BookTabButton tab = GetTabForZone(zone);
|
||||
if (tab != null)
|
||||
{
|
||||
tab.ActivateTab();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clean up all active cards
|
||||
/// </summary>
|
||||
private void CleanupActiveCards()
|
||||
{
|
||||
foreach (var card in _activeCards)
|
||||
{
|
||||
if (card != null && card.gameObject != null)
|
||||
{
|
||||
card.OnCardRevealed -= OnCardRevealed;
|
||||
card.OnCardPlacedInAlbum -= OnCardPlacedInAlbum;
|
||||
Destroy(card.gameObject);
|
||||
}
|
||||
}
|
||||
_activeCards.Clear();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user